@fased/fased 0.1.57 → 0.1.59

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 (172) hide show
  1. package/dist/agents/anthropic-payload-policy.js +188 -0
  2. package/dist/agents/anthropic-transport-stream.js +736 -0
  3. package/dist/agents/copilot-dynamic-headers.js +1 -1
  4. package/dist/agents/current-model-catalog.js +5 -2
  5. package/dist/agents/custom-api-registry.js +21 -0
  6. package/dist/agents/google-transport-stream.js +543 -0
  7. package/dist/agents/model-catalog-status.js +6 -1
  8. package/dist/agents/models-config.js +2 -1
  9. package/dist/agents/openai-completions-compat.js +49 -0
  10. package/dist/agents/openai-responses-payload-policy.js +100 -0
  11. package/dist/agents/openai-tool-schema.js +130 -0
  12. package/dist/agents/openai-transport-stream.js +1040 -0
  13. package/dist/agents/pi-embedded-runner/compact.js +9 -0
  14. package/dist/agents/pi-embedded-runner/run/attempt.js +9 -0
  15. package/dist/agents/pi-embedded-runner/session-manager-init.js +5 -20
  16. package/dist/agents/provider-extension-catalog-manifest.js +2 -2
  17. package/dist/agents/provider-request-config.js +1 -1
  18. package/dist/agents/provider-stream.js +29 -0
  19. package/dist/agents/provider-transport-fetch.js +89 -0
  20. package/dist/agents/provider-transport-stream.js +72 -0
  21. package/dist/agents/system-prompt-cache-boundary.js +1 -1
  22. package/dist/agents/system-prompt.js +1 -1
  23. package/dist/agents/transport-message-transform.js +143 -0
  24. package/dist/agents/transport-stream-shared.js +81 -0
  25. package/dist/brand.js +1 -1
  26. package/dist/build-info.json +3 -3
  27. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  28. package/dist/channels/delivery.js +78 -0
  29. package/dist/channels/plugins/catalog.js +10 -5
  30. package/dist/cli/components-cli.js +16 -13
  31. package/dist/cli/program/register.subclis.js +9 -0
  32. package/dist/cli/providers-cli.js +79 -6
  33. package/dist/cli/services-cli.js +28 -0
  34. package/dist/commands/auth-choice.js +1 -1
  35. package/dist/commands/models/list.auth-overview.js +12 -0
  36. package/dist/commands/onboard-channels.js +5 -0
  37. package/dist/commands/onboard-helpers.js +1 -1
  38. package/dist/commands/vllm-setup.js +12 -3
  39. package/dist/control-ui/assets/{app-DltSyY4Y.js → app-S_jg-ADS.js} +1706 -1675
  40. package/dist/control-ui/assets/{index-D5JlBp7f.js → index-C22rDOFm.js} +3 -3
  41. package/dist/control-ui/assets/{providers-Cc89iMdx.js → providers-QMn4w2_f.js} +3 -3
  42. package/dist/control-ui/index.html +1 -1
  43. package/dist/control-ui/version.json +1 -1
  44. package/dist/gateway/control-reply-text.js +10 -1
  45. package/dist/gateway/method-scopes.js +2 -0
  46. package/dist/gateway/protocol/schema/agents-models-skills.js +1 -0
  47. package/dist/gateway/server-chat.js +3 -3
  48. package/dist/gateway/server-methods/channels.js +10 -2
  49. package/dist/gateway/server-methods/chat.js +19 -12
  50. package/dist/gateway/server-methods/models.js +4 -0
  51. package/dist/gateway/server-methods/services.js +47 -2
  52. package/dist/gateway/server-methods-list.js +2 -0
  53. package/dist/gateway/session-event-payloads.js +20 -9
  54. package/dist/infra/google-api-base-url.js +29 -0
  55. package/dist/plugin-sdk/{accounts-CmMwe9Dt.js → accounts-DxblobYM.js} +1 -1
  56. package/dist/plugin-sdk/{actions-DYViFQiX.js → actions-D4UNimWa.js} +2 -2
  57. package/dist/plugin-sdk/{active-listener-DVbpcisf.js → active-listener-CFuy106s.js} +1 -1
  58. package/dist/plugin-sdk/{audio-preflight-D-Mfay8i.js → audio-preflight-CxZixyhW.js} +25 -25
  59. package/dist/plugin-sdk/{audit-JImYFOzJ.js → audit-Cs0K7LBp.js} +1 -1
  60. package/dist/plugin-sdk/{channel-access-cxCK2mci.js → channel-access-D5aHoTY8.js} +1 -1
  61. package/dist/plugin-sdk/{channel-access-configure-B_qYjml8.js → channel-access-configure-Dvch9mMQ.js} +1 -1
  62. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  63. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  64. package/dist/plugin-sdk/{chunk-CF8nvZNj.js → chunk-B0FUGc5A.js} +1 -1
  65. package/dist/plugin-sdk/{chunk-items-yLFoO571.js → chunk-items-Cy9a3X7x.js} +1 -1
  66. package/dist/plugin-sdk/command-status.js +25 -25
  67. package/dist/plugin-sdk/{commands-registry-CK4KmMG0.js → commands-registry-DmDRhRAU.js} +2 -2
  68. package/dist/plugin-sdk/{common-Cky4hpqr.js → common-CfUOowOp.js} +1 -1
  69. package/dist/plugin-sdk/{config-Dbxj_jUP.js → config-mxULv0fQ.js} +4 -1
  70. package/dist/plugin-sdk/{config-schema-aKPMn6ML.js → config-schema-D8E0LX8X.js} +4422 -486
  71. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  72. package/dist/plugin-sdk/discord.d.ts +4 -4
  73. package/dist/plugin-sdk/discord.js +19 -19
  74. package/dist/plugin-sdk/{handle-action-BSNDD2Vc.js → handle-action-DbVCF9HZ.js} +13 -13
  75. package/dist/plugin-sdk/{helpers-CDo2XBSr.js → helpers-BXbdgs5H.js} +1 -1
  76. package/dist/plugin-sdk/{image-ops-DbeBxbn4.js → image-ops-D7jPxaph.js} +1 -1
  77. package/dist/plugin-sdk/index.d.ts +15 -15
  78. package/dist/plugin-sdk/index.js +27 -27
  79. package/dist/plugin-sdk/{login-CWXvFTd8.js → login-BRxww6nv.js} +3 -3
  80. package/dist/plugin-sdk/{login-qr-CJB2zjmM.js → login-qr-DJXsk9XH.js} +3 -3
  81. package/dist/plugin-sdk/{manager-CbiFFLKS.js → manager-Y2S_5IlI.js} +2 -2
  82. package/dist/plugin-sdk/{markdown-tables-Bskt9r8i.js → markdown-tables-DHLFQqaF.js} +1 -1
  83. package/dist/plugin-sdk/{media-B3aAg5rh.js → media-Czo4carg.js} +2 -2
  84. package/dist/plugin-sdk/{monitor-BYGbTI5V.js → monitor-CFHFAUOj.js} +32 -32
  85. package/dist/plugin-sdk/{monitor-BJs6z7AT.js → monitor-xS9A8Upr.js} +29 -29
  86. package/dist/plugin-sdk/ollama-runtime.js +970 -0
  87. package/dist/plugin-sdk/{openresponses-http-BGCl2dS2.js → openresponses-http-BcUbeVV6.js} +25 -25
  88. package/dist/plugin-sdk/{optional-runtime-dependency-BlTVkcAg.js → optional-runtime-dependency-DdMbxS24.js} +1 -1
  89. package/dist/plugin-sdk/{outbound-CIemga5_.js → outbound-Db8mRN6P.js} +6 -6
  90. package/dist/plugin-sdk/{outbound-attachment-2RckWqrk.js → outbound-attachment-BqFjcypb.js} +2 -2
  91. package/dist/plugin-sdk/{pairing-message-r-VQfhFB.js → pairing-message-B3GC_7CD.js} +1 -1
  92. package/dist/plugin-sdk/{pairing-message-C403mq4K.d.ts → pairing-message-CxonJmwM.d.ts} +1 -1
  93. package/dist/plugin-sdk/{pairing-store-uPnLfGgB.js → pairing-store-CQikgIbw.js} +2 -2
  94. package/dist/plugin-sdk/{pi-model-discovery-BpEilAym.js → pi-model-discovery-CY3m5zJP.js} +1 -1
  95. package/dist/plugin-sdk/{pi-model-discovery-runtime-DwNdFaz-.js → pi-model-discovery-runtime-DSh1T2pn.js} +2 -2
  96. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  97. package/dist/plugin-sdk/{pw-ai-H7UgunQQ.js → pw-ai-AFUqn-tQ.js} +3 -3
  98. package/dist/plugin-sdk/{replies-CKMwkH4G.js → replies-Fzh9PhXl.js} +2 -2
  99. package/dist/plugin-sdk/{resolve-outbound-target-B68fSoGi.js → resolve-outbound-target-KpF3co6T.js} +1 -1
  100. package/dist/plugin-sdk/{resolve-route-B8fOfihG.js → resolve-route-CDl74O19.js} +1 -1
  101. package/dist/plugin-sdk/{retry-policy-B6GQldxB.js → retry-policy-D4uiqUOG.js} +1 -1
  102. package/dist/plugin-sdk/{runtime-nVJl3PlS.js → runtime-Bb6KRnCC.js} +6 -4
  103. package/dist/plugin-sdk/{runtime-dependency-DlqVSsSs.js → runtime-dependency-DLDmB94f.js} +11 -11
  104. package/dist/plugin-sdk/sat-runtime.d.ts +2 -2
  105. package/dist/plugin-sdk/sat-runtime.js +25 -25
  106. package/dist/plugin-sdk/{send-CPK_HlYW.js → send-5nFktR1n.js} +5 -5
  107. package/dist/plugin-sdk/{send-hSu1HWms.js → send-Bfzf11VY.js} +7 -7
  108. package/dist/plugin-sdk/{send-D3DQOg2a.js → send-C4k4JuJN.js} +6 -6
  109. package/dist/plugin-sdk/{send-BHokB4TR.js → send-D-uDKGIq.js} +6 -6
  110. package/dist/plugin-sdk/{send-BCJ4hi3t.js → send-Du8ykP1k.js} +8 -8
  111. package/dist/plugin-sdk/{session-DQ4Dr-mf.js → session-CS-Td9EM.js} +2 -2
  112. package/dist/plugin-sdk/{skill-commands-CmdoKLh_.js → skill-commands-CFAlXWHk.js} +2 -2
  113. package/dist/plugin-sdk/{slack-B70NHpMF.js → slack-DXYGbizH.js} +37 -37
  114. package/dist/plugin-sdk/{slack-actions-fSmP981b.js → slack-actions-BcMkNv1Z.js} +11 -11
  115. package/dist/plugin-sdk/slack.d.ts +4 -4
  116. package/dist/plugin-sdk/slack.js +9 -9
  117. package/dist/plugin-sdk/{sticker-cache-B4dG2sKU.js → sticker-cache-Dr3rYfU1.js} +3 -3
  118. package/dist/plugin-sdk/{tables-EX4Hy-a-.js → tables-bFXyRpkx.js} +1 -1
  119. package/dist/plugin-sdk/{targets-BeGUCGWK.js → targets-DH7a9lKB.js} +1 -1
  120. package/dist/plugin-sdk/{telegram-actions-CM8SJgc9.js → telegram-actions-Besbw_Hg.js} +28 -28
  121. package/dist/plugin-sdk/telegram.d.ts +4 -4
  122. package/dist/plugin-sdk/telegram.js +8 -8
  123. package/dist/plugin-sdk/{thread-bindings.lifecycle-tHK_1mvp.js → thread-bindings.lifecycle-CgOvvdw8.js} +3 -3
  124. package/dist/plugin-sdk/{thread-bindings.state-D7xG1GHu.js → thread-bindings.state-CQpk44Hq.js} +2 -2
  125. package/dist/plugin-sdk/{tmp-fased-dir-Ouj58axY.d.ts → tmp-fased-dir-DQ00xQ5D.d.ts} +1 -1
  126. package/dist/plugin-sdk/{types-B4hj6HgR.d.ts → types-B6zx-Dl2.d.ts} +1 -1
  127. package/dist/plugin-sdk/{types-dWZFX1fk.d.ts → types-eUM7hcDz.d.ts} +1 -1
  128. package/dist/plugin-sdk/{web-BFolfZ3i.js → web-Q1-G--JH.js} +30 -30
  129. package/dist/plugin-sdk/{whatsapp-actions-CYmleHCz.js → whatsapp-actions-BtSc2CBv.js} +13 -13
  130. package/dist/plugin-sdk/{whatsapp-heartbeat-BEwm4GK3.js → whatsapp-heartbeat-Bg9TuYzl.js} +2 -2
  131. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  132. package/dist/plugin-sdk/whatsapp.js +12 -12
  133. package/dist/plugin-sdk/{wsl-BtMDzMv5.js → wsl-C4DGFkOd.js} +3 -3
  134. package/dist/plugin-sdk/{zod-schema.providers-core-FaYR6KdD.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  135. package/dist/plugins/provider-runtime.js +79 -0
  136. package/dist/providers/refresh.js +14 -0
  137. package/extensions/acpx/package.json +1 -1
  138. package/extensions/bluebubbles/package.json +1 -1
  139. package/extensions/copilot-proxy/package.json +1 -1
  140. package/extensions/diagnostics-otel/package.json +1 -1
  141. package/extensions/fased-federation/package.json +1 -1
  142. package/extensions/google-gemini-cli-auth/package.json +1 -1
  143. package/extensions/imessage/package.json +1 -1
  144. package/extensions/irc/package.json +1 -1
  145. package/extensions/line/package.json +1 -1
  146. package/extensions/llm-task/package.json +1 -1
  147. package/extensions/lobster/package.json +1 -1
  148. package/extensions/matrix/CHANGELOG.md +12 -0
  149. package/extensions/matrix/package.json +1 -1
  150. package/extensions/mattermost/package.json +1 -1
  151. package/extensions/memory-core/package.json +2 -2
  152. package/extensions/memory-lancedb/package.json +1 -1
  153. package/extensions/minimax-portal-auth/package.json +1 -1
  154. package/extensions/msteams/CHANGELOG.md +12 -0
  155. package/extensions/msteams/package.json +1 -1
  156. package/extensions/nextcloud-talk/package.json +1 -1
  157. package/extensions/nostr/CHANGELOG.md +12 -0
  158. package/extensions/nostr/package.json +1 -1
  159. package/extensions/open-prose/package.json +1 -1
  160. package/extensions/sat-mining/package.json +1 -1
  161. package/extensions/signal/package.json +1 -1
  162. package/extensions/synology-chat/package.json +1 -1
  163. package/extensions/tlon/package.json +1 -1
  164. package/extensions/twitch/CHANGELOG.md +12 -0
  165. package/extensions/twitch/package.json +1 -1
  166. package/extensions/voice-call/CHANGELOG.md +12 -0
  167. package/extensions/voice-call/package.json +1 -1
  168. package/extensions/zalo/CHANGELOG.md +12 -0
  169. package/extensions/zalo/package.json +1 -1
  170. package/extensions/zalouser/CHANGELOG.md +12 -0
  171. package/extensions/zalouser/package.json +1 -1
  172. package/package.json +1 -1
@@ -2,6 +2,7 @@ import { CONFIG_DIR, isRecord, resolveUserPath } from "../../utils.js";
2
2
  import { resolveFasedAgentPackageRootSync } from "../../infra/fased-root.js";
3
3
  import { getPackageManifestMetadata } from "../../plugins/manifest.js";
4
4
  import { discoverFasedAgentPlugins } from "../../plugins/discovery.js";
5
+ import { channelDeliveryAllowsInstall, getChannelDelivery } from "../delivery.js";
5
6
  import fs from "node:fs";
6
7
  import path from "node:path";
7
8
 
@@ -152,20 +153,22 @@ function buildCatalogEntry(candidate) {
152
153
  if (!meta) {
153
154
  return null;
154
155
  }
156
+ const delivery = candidate.catalogSource === "official-catalog" ? "official-addon" : getChannelDelivery(id);
155
157
  const install = resolveInstallInfo({
156
158
  manifest,
157
159
  packageName: candidate.packageName,
158
160
  packageDir: candidate.packageDir,
159
161
  workspaceDir: candidate.workspaceDir
160
162
  });
161
- if (!install) {
163
+ if (!install && channelDeliveryAllowsInstall(delivery)) {
162
164
  return null;
163
165
  }
164
166
  return {
165
167
  id,
166
168
  meta,
169
+ delivery,
167
170
  catalogSource: candidate.catalogSource,
168
- install
171
+ install: channelDeliveryAllowsInstall(delivery) ? install ?? {} : {}
169
172
  };
170
173
  }
171
174
  function buildExternalCatalogEntry(entry, catalogSource) {
@@ -201,7 +204,7 @@ function formatChannelPluginCatalogSource(source) {
201
204
  }
202
205
  }
203
206
  function formatChannelPluginCatalogInstallBits(entry) {
204
- const bits = [formatChannelPluginCatalogSource(entry.catalogSource)];
207
+ const bits = [entry.delivery, formatChannelPluginCatalogSource(entry.catalogSource)];
205
208
  if (entry.install.expectedIntegrity) {
206
209
  bits.push("integrity pinned");
207
210
  }
@@ -211,10 +214,12 @@ function formatChannelPluginCatalogInstallBits(entry) {
211
214
  return bits;
212
215
  }
213
216
  function formatChannelPluginCatalogSelectionHint(entry) {
214
- return [...formatChannelPluginCatalogInstallBits(entry), "install"].join(" · ");
217
+ const action = entry.delivery === "official-addon" ? "install" : entry.delivery === "bundled" ? "enable bundled" : "docs required";
218
+ return [...formatChannelPluginCatalogInstallBits(entry), action].join(" · ");
215
219
  }
216
220
  function formatChannelPluginCatalogStatusLine(entry) {
217
- return `${entry.meta.label}: ${formatChannelPluginCatalogInstallBits(entry).join(", ")}, install plugin to enable`;
221
+ const next = entry.delivery === "official-addon" ? "install add-on to enable" : entry.delivery === "bundled" ? "enable bundled integration" : `follow ${entry.meta.docsPath}`;
222
+ return `${entry.meta.label}: ${formatChannelPluginCatalogInstallBits(entry).join(", ")}, ${next}`;
218
223
  }
219
224
  function buildChannelUiCatalog(plugins) {
220
225
  const entries = plugins.map((plugin) => {
@@ -16,20 +16,23 @@ function registerComponentsCli(program) {
16
16
  renderComponentReport(options);
17
17
  });
18
18
  components.command("install").description("Install a cataloged optional component").argument("<id>", "Component id from `fased components`").action(async (id) => {
19
- const result = await installCapabilityComponent({
20
- id,
21
- config: loadConfig()
22
- });
23
- await writeConfigFile(result.config);
24
- for (const warning of result.slotWarnings) {
25
- defaultRuntime.log(theme.warn(warning));
26
- }
27
- defaultRuntime.log(`Installed component: ${result.entry.label} (${result.entry.packageName})`);
28
- if (result.entry.restartRequired !== false) {
29
- defaultRuntime.log("Restart the gateway to apply the component.");
30
- }
19
+ await installComponentCommand(id);
31
20
  });
32
21
  }
22
+ async function installComponentCommand(id) {
23
+ const result = await installCapabilityComponent({
24
+ id,
25
+ config: loadConfig()
26
+ });
27
+ await writeConfigFile(result.config);
28
+ for (const warning of result.slotWarnings) {
29
+ defaultRuntime.log(theme.warn(warning));
30
+ }
31
+ defaultRuntime.log(`Installed component: ${result.entry.label} (${result.entry.packageName})`);
32
+ if (result.entry.restartRequired !== false) {
33
+ defaultRuntime.log("Restart the gateway to apply the component.");
34
+ }
35
+ }
33
36
  function renderComponentReport(options) {
34
37
  const report = buildCapabilityReadinessReport();
35
38
  if (options.json) {
@@ -80,4 +83,4 @@ function renderComponentReport(options) {
80
83
  }
81
84
 
82
85
  //#endregion
83
- export { registerComponentsCli };
86
+ export { installComponentCommand, registerComponentsCli, renderComponentReport };
@@ -327,6 +327,15 @@ const entries = [
327
327
  mod.registerComponentsCli(program);
328
328
  }
329
329
  },
330
+ {
331
+ name: "services",
332
+ description: "Inspect and connect optional services and runtimes",
333
+ hasSubcommands: true,
334
+ register: async (program) => {
335
+ const mod = await import("../services-cli.js");
336
+ mod.registerServicesCli(program);
337
+ }
338
+ },
330
339
  {
331
340
  name: "update",
332
341
  description: "Update FasedAgent and inspect update channel status",
@@ -1,11 +1,14 @@
1
1
  import { theme } from "../terminal/theme.js";
2
- import { listAgentIds, resolveAgentDir } from "../agents/agent-scope.js";
2
+ import { defaultRuntime } from "../runtime.js";
3
+ import { listAgentIds, resolveAgentDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
3
4
  import { getProviderBrandManifestForRoute } from "../providers/registry.js";
4
- import { loadAuthProfileStoreForRuntime } from "../agents/auth-profiles/store.js";
5
+ import { ensureAuthProfileStore, loadAuthProfileStoreForRuntime } from "../agents/auth-profiles/store.js";
5
6
  import { loadConfig, writeConfigFile } from "../config/io.js";
6
7
  import "../config/config.js";
7
8
  import "../agents/auth-profiles.js";
9
+ import { ensureFasedModelsJson } from "../agents/models-config.js";
8
10
  import { applyProviderRefreshToRegistrySource, buildProviderCapabilityOverridesSource, buildProviderRefreshEnvFromCredentials, buildProviderRefreshReport, buildProviderRegistryReviewPatch, fetchOfficialProviderRefreshSnapshot, loadProviderRefreshSnapshotFromFile } from "../providers/refresh.js";
11
+ import { buildAuthChoiceGroups } from "../commands/auth-choice-options.js";
9
12
  import { createClackPrompter } from "../wizard/clack-prompter.js";
10
13
  import path from "node:path";
11
14
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -106,11 +109,13 @@ async function providersModelsAddCommand(options) {
106
109
  ...options.api?.trim() ? { api: options.api.trim() } : {},
107
110
  models: nextModels
108
111
  };
109
- await writeConfigFile(upsertProviderModelConfig({
112
+ const nextConfig = upsertProviderModelConfig({
110
113
  cfg,
111
114
  providerId,
112
115
  providerConfig: nextProvider
113
- }));
116
+ });
117
+ await writeConfigFile(nextConfig);
118
+ await ensureFasedModelsJson(nextConfig);
114
119
  console.log(`${theme.success("Model saved:")} ${providerId}/${model.id}`);
115
120
  }
116
121
  async function providersModelsRemoveCommand(options) {
@@ -125,14 +130,16 @@ async function providersModelsRemoveCommand(options) {
125
130
  if (nextModels.length === (existing.models ?? []).length) {
126
131
  throw new Error(`Model "${providerId}/${modelId}" is not configured.`);
127
132
  }
128
- await writeConfigFile(upsertProviderModelConfig({
133
+ const nextConfig = upsertProviderModelConfig({
129
134
  cfg,
130
135
  providerId,
131
136
  providerConfig: {
132
137
  ...existing,
133
138
  models: nextModels
134
139
  }
135
- }));
140
+ });
141
+ await writeConfigFile(nextConfig);
142
+ await ensureFasedModelsJson(nextConfig);
136
143
  console.log(`${theme.success("Model removed:")} ${providerId}/${modelId}`);
137
144
  }
138
145
  async function providersModelsListCommand(options) {
@@ -353,6 +360,13 @@ async function providersRefreshCommand(options) {
353
360
  }
354
361
  function registerProvidersCli(program) {
355
362
  const providers = program.command("providers").description("Review provider manifests and model catalogs");
363
+ providers.command("connect").description("Connect a model provider using the same auth flow as onboarding and the UI").argument("[provider]", "Public provider id, for example openai or anthropic").option("--method <id>", "Credential method id").option("--set-default", "Apply the provider's recommended default model", false).action(async (provider, options) => {
364
+ await providersConnectCommand({
365
+ provider,
366
+ method: options.method,
367
+ setDefault: options.setDefault === true
368
+ });
369
+ });
356
370
  providers.command("refresh").description("Compare provider registry against official/source catalogs").option("--from-file <path>", "Use a provider refresh snapshot JSON file").option("--json", "Output JSON", false).option("--write-review [path]", "Write a review patch instead of applying changes").option("--apply", "Apply model-list updates to src/providers/registry.ts", false).option("--wizard", "Choose report/review/apply from an interactive wizard", false).option("--no-network", "Do not fetch official provider docs; report local registry only").action(async (opts) => {
357
371
  await providersRefreshCommand({
358
372
  fromFile: opts.fromFile,
@@ -392,6 +406,65 @@ function registerProvidersCli(program) {
392
406
  });
393
407
  });
394
408
  }
409
+ async function providersConnectCommand(options) {
410
+ if (!process.stdin.isTTY) {
411
+ throw new Error("providers connect requires an interactive TTY.");
412
+ }
413
+ const config = loadConfig();
414
+ const [{ applyAuthChoice }, { openUrl }] = await Promise.all([import("../commands/auth-choice.js"), import("../commands/onboard-helpers.js")]);
415
+ const agentId = resolveDefaultAgentId(config);
416
+ const agentDir = resolveAgentDir(config, agentId);
417
+ const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
418
+ const { groups } = buildAuthChoiceGroups({
419
+ store,
420
+ includeSkip: false
421
+ });
422
+ const requestedProvider = options.provider?.trim().toLowerCase();
423
+ const prompter = createClackPrompter();
424
+ let selectedGroup = requestedProvider ? groups.find((candidate) => String(candidate.value).toLowerCase() === requestedProvider || candidate.label.toLowerCase() === requestedProvider) : undefined;
425
+ if (!requestedProvider) {
426
+ const selectedProvider = await prompter.select({
427
+ message: "Model provider",
428
+ options: groups.map((candidate) => ({
429
+ value: candidate.value,
430
+ label: candidate.label,
431
+ hint: candidate.hint
432
+ }))
433
+ });
434
+ selectedGroup = groups.find((candidate) => String(candidate.value) === String(selectedProvider));
435
+ }
436
+ if (!selectedGroup) {
437
+ throw new Error(`Unknown provider: ${options.provider}. Run \`fased providers connect\` to choose.`);
438
+ }
439
+ const requestedMethod = options.method?.trim();
440
+ let method = requestedMethod ? selectedGroup.options.find((candidate) => String(candidate.value) === requestedMethod) : selectedGroup.options.length === 1 ? selectedGroup.options[0] : undefined;
441
+ if (!requestedMethod && selectedGroup.options.length > 1) {
442
+ const selectedMethod = await prompter.select({
443
+ message: `${selectedGroup.label} auth method`,
444
+ options: selectedGroup.options
445
+ });
446
+ method = selectedGroup.options.find((candidate) => String(candidate.value) === String(selectedMethod));
447
+ }
448
+ if (!method) {
449
+ throw new Error(`Unknown auth method for ${selectedGroup.label}: ${options.method ?? "(none)"}`);
450
+ }
451
+ const result = await applyAuthChoice({
452
+ authChoice: method.value,
453
+ config,
454
+ prompter,
455
+ runtime: defaultRuntime,
456
+ openUrl: async (url) => {
457
+ await openUrl(url);
458
+ },
459
+ agentDir,
460
+ agentId,
461
+ setDefaultModel: options.setDefault === true,
462
+ opts: {}
463
+ });
464
+ await writeConfigFile(result.config);
465
+ await ensureFasedModelsJson(result.config, agentDir);
466
+ defaultRuntime.log(`${theme.success("Provider connected:")} ${selectedGroup.label}`);
467
+ }
395
468
 
396
469
  //#endregion
397
470
  export { registerProvidersCli };
@@ -0,0 +1,28 @@
1
+ import { theme } from "../terminal/theme.js";
2
+ import { defaultRuntime } from "../runtime.js";
3
+ import { formatDocsLink } from "../terminal/links.js";
4
+ import { buildCapabilityReadinessReport } from "../capabilities/catalog.js";
5
+ import { installComponentCommand, renderComponentReport } from "./components-cli.js";
6
+
7
+ //#region src/cli/services-cli.ts
8
+ function registerServicesCli(program) {
9
+ const services = program.command("services").description("Inspect and connect optional services and runtimes");
10
+ services.command("status").description("Show service and component readiness").option("--json", "Output JSON", false).action((options) => renderComponentReport(options));
11
+ services.command("install").description("Install an official optional component").argument("<id>", "Component id from `fased services status`").action(async (id) => installComponentCommand(id));
12
+ services.command("connect").description("Install when supported, or show the canonical connection surface").argument("<id>", "Component id from `fased services status`").action(async (id) => {
13
+ const entry = buildCapabilityReadinessReport().entries.find((candidate) => candidate.id === id.trim());
14
+ if (!entry) {
15
+ throw new Error(`Unknown service component: ${id}. Run \`fased services status\`.`);
16
+ }
17
+ if (entry.delivery === "npm-addon" && entry.state === "not-installed") {
18
+ await installComponentCommand(entry.id);
19
+ return;
20
+ }
21
+ defaultRuntime.log(`${theme.heading(entry.label)}: ${entry.detail}`);
22
+ defaultRuntime.log(`Connect from: ${entry.surface}`);
23
+ defaultRuntime.log(`Docs: ${formatDocsLink(entry.docsPath, `docs.fased.ai${entry.docsPath}`)}`);
24
+ });
25
+ }
26
+
27
+ //#endregion
28
+ export { registerServicesCli };
@@ -2,4 +2,4 @@ import { applyAuthChoice } from "./auth-choice.apply.js";
2
2
  import { warnIfModelConfigLooksOff } from "./auth-choice.model-check.js";
3
3
  import { resolvePreferredProviderForAuthChoice } from "./auth-choice.preferred-provider.js";
4
4
 
5
- export { };
5
+ export { applyAuthChoice, resolvePreferredProviderForAuthChoice, warnIfModelConfigLooksOff };
@@ -10,6 +10,11 @@ import { formatRemainingShort } from "../../agents/auth-health.js";
10
10
  import "./list.format.js";
11
11
 
12
12
  //#region src/commands/models/list.auth-overview.ts
13
+ const LOCAL_PROVIDER_IDS = new Set([
14
+ "ollama",
15
+ "lmstudio",
16
+ "vllm"
17
+ ]);
13
18
  function formatProfileSecretLabel(params) {
14
19
  const value = typeof params.value === "string" ? params.value.trim() : "";
15
20
  if (value) {
@@ -69,6 +74,13 @@ function resolveProviderAuthOverview(params) {
69
74
  const envKey = resolveEnvApiKey(provider);
70
75
  const customKey = getCustomProviderApiKey(cfg, provider);
71
76
  const effective = (() => {
77
+ const providerConfig = cfg.models?.providers?.[provider];
78
+ if (LOCAL_PROVIDER_IDS.has(provider) && providerConfig?.baseUrl?.trim()) {
79
+ return {
80
+ kind: "local",
81
+ detail: providerConfig.baseUrl.trim()
82
+ };
83
+ }
72
84
  if (profiles.length > 0) {
73
85
  return {
74
86
  kind: "profiles",
@@ -6,6 +6,7 @@ import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.
6
6
  import { formatChannelPrimerLine, formatChannelSelectionLine, listChatChannels } from "../channels/registry.js";
7
7
  import { enablePluginInConfig } from "../plugins/enable.js";
8
8
  import { formatDocsLink } from "../terminal/links.js";
9
+ import { channelDeliveryAllowsInstall, formatChannelDelivery } from "../channels/delivery.js";
9
10
  import { formatChannelPluginCatalogSelectionHint, formatChannelPluginCatalogStatusLine, listChannelPluginCatalogEntries } from "../channels/plugins/catalog.js";
10
11
  import { isChannelConfigured } from "../config/plugin-auto-enable.js";
11
12
  import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
@@ -492,6 +493,10 @@ async function setupChannels(cfg, runtime, prompter, options) {
492
493
  const { catalogById } = getChannelEntries();
493
494
  const catalogEntry = catalogById.get(channel);
494
495
  if (catalogEntry) {
496
+ if (!channelDeliveryAllowsInstall(catalogEntry.delivery)) {
497
+ await prompter.note(`${catalogEntry.meta.label} is ${formatChannelDelivery(catalogEntry.delivery).toLowerCase()}. Follow ${catalogEntry.meta.docsPath}; onboarding cannot install it from npm.`, "Channel delivery");
498
+ return;
499
+ }
495
500
  const workspaceDir = resolveAgentWorkspaceDir(next, resolveDefaultAgentId(next));
496
501
  const result = await ensureOnboardingPluginInstalled({
497
502
  cfg: next,
@@ -460,4 +460,4 @@ function resolveControlUiLinks(params) {
460
460
  }
461
461
 
462
462
  //#endregion
463
- export { DEFAULT_WORKSPACE, applyWizardMetadata, detectBinary, detectBrowserOpenSupport, ensureWorkspaceAndSessions, formatControlUiSshHint, guardCancel, handleOnboardingRepair, handleReset, moveToTrash, normalizeGatewayTokenInput, openUrl, printWizardHeader, probeGatewayReachable, randomToken, resolveControlUiLinks, resolveNodeManagerOptions, summarizeExistingConfig, validateGatewayPasswordInput, waitForGatewayReachable };
463
+ export { DEFAULT_WORKSPACE, applyWizardMetadata, detectBinary, detectBrowserOpenSupport, ensureWorkspaceAndSessions, formatControlUiSshHint, guardCancel, handleOnboardingRepair, handleReset, moveToTrash, normalizeGatewayTokenInput, openUrl, openUrlInBackground, printWizardHeader, probeGatewayReachable, randomToken, resolveBrowserOpenCommand, resolveControlUiLinks, resolveNodeManagerOptions, summarizeExistingConfig, validateGatewayPasswordInput, waitForGatewayReachable };
@@ -20,6 +20,15 @@ function resolveLmStudioBaseUrl(value) {
20
20
  const trimmed = value.trim().replace(/\/+$/, "");
21
21
  return /\/v1$/i.test(trimmed) ? trimmed : `${trimmed}/v1`;
22
22
  }
23
+ function normalizeLocalProviderModelId(provider, value) {
24
+ const normalizedProvider = provider.trim().toLowerCase();
25
+ let modelId = value.trim();
26
+ const prefix = `${normalizedProvider}/`;
27
+ while (normalizedProvider && modelId.toLowerCase().startsWith(prefix)) {
28
+ modelId = modelId.slice(prefix.length).trim();
29
+ }
30
+ return modelId;
31
+ }
23
32
  function localModelDefinition(params) {
24
33
  const modelId = params.id.trim();
25
34
  const lower = modelId.toLowerCase();
@@ -37,7 +46,7 @@ function localModelDefinition(params) {
37
46
  async function configureVllmProvider(params) {
38
47
  const baseUrl = params.baseUrl.trim().replace(/\/+$/, "");
39
48
  const apiKey = params.apiKey.trim();
40
- const modelId = params.modelId.trim();
49
+ const modelId = normalizeLocalProviderModelId("vllm", params.modelId);
41
50
  if (!baseUrl || !apiKey || !modelId) {
42
51
  throw new Error("vLLM setup requires base URL, API key or placeholder, and model ID.");
43
52
  }
@@ -77,7 +86,7 @@ async function configureVllmProvider(params) {
77
86
  async function configureOllamaProvider(params) {
78
87
  const baseUrl = resolveOllamaBaseUrl(params.baseUrl || OLLAMA_DEFAULT_BASE_URL);
79
88
  const apiKey = params.apiKey?.trim() || "ollama-local";
80
- const modelId = params.modelId.trim();
89
+ const modelId = normalizeLocalProviderModelId("ollama", params.modelId);
81
90
  if (!baseUrl || !modelId) {
82
91
  throw new Error("Ollama setup requires base URL and model ID.");
83
92
  }
@@ -117,7 +126,7 @@ async function configureOllamaProvider(params) {
117
126
  async function configureLmStudioProvider(params) {
118
127
  const baseUrl = resolveLmStudioBaseUrl(params.baseUrl || LMSTUDIO_DEFAULT_BASE_URL);
119
128
  const apiKey = params.apiKey?.trim() || "lmstudio-local";
120
- const modelId = params.modelId.trim();
129
+ const modelId = normalizeLocalProviderModelId("lmstudio", params.modelId);
121
130
  if (!baseUrl || !modelId) {
122
131
  throw new Error("LM Studio setup requires base URL and model ID.");
123
132
  }