@fased/fased 0.1.58 → 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 (167) 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/custom-api-registry.js +21 -0
  5. package/dist/agents/google-transport-stream.js +543 -0
  6. package/dist/agents/models-config.js +2 -1
  7. package/dist/agents/openai-completions-compat.js +49 -0
  8. package/dist/agents/openai-responses-payload-policy.js +100 -0
  9. package/dist/agents/openai-tool-schema.js +130 -0
  10. package/dist/agents/openai-transport-stream.js +1040 -0
  11. package/dist/agents/pi-embedded-runner/compact.js +9 -0
  12. package/dist/agents/pi-embedded-runner/run/attempt.js +9 -0
  13. package/dist/agents/provider-request-config.js +1 -1
  14. package/dist/agents/provider-stream.js +29 -0
  15. package/dist/agents/provider-transport-fetch.js +89 -0
  16. package/dist/agents/provider-transport-stream.js +72 -0
  17. package/dist/agents/system-prompt-cache-boundary.js +1 -1
  18. package/dist/agents/system-prompt.js +1 -1
  19. package/dist/agents/transport-message-transform.js +143 -0
  20. package/dist/agents/transport-stream-shared.js +81 -0
  21. package/dist/brand.js +1 -1
  22. package/dist/build-info.json +3 -3
  23. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  24. package/dist/channels/delivery.js +78 -0
  25. package/dist/channels/plugins/catalog.js +10 -5
  26. package/dist/cli/components-cli.js +16 -13
  27. package/dist/cli/program/register.subclis.js +9 -0
  28. package/dist/cli/providers-cli.js +79 -6
  29. package/dist/cli/services-cli.js +28 -0
  30. package/dist/commands/auth-choice.js +1 -1
  31. package/dist/commands/models/list.auth-overview.js +12 -0
  32. package/dist/commands/onboard-channels.js +5 -0
  33. package/dist/commands/onboard-helpers.js +1 -1
  34. package/dist/commands/vllm-setup.js +12 -3
  35. package/dist/control-ui/assets/{app-CpgQGqe6.js → app-S_jg-ADS.js} +1706 -1675
  36. package/dist/control-ui/assets/{index-DiBBflpv.js → index-C22rDOFm.js} +3 -3
  37. package/dist/control-ui/assets/{providers-Cc89iMdx.js → providers-QMn4w2_f.js} +3 -3
  38. package/dist/control-ui/index.html +1 -1
  39. package/dist/control-ui/version.json +1 -1
  40. package/dist/gateway/control-reply-text.js +10 -1
  41. package/dist/gateway/method-scopes.js +2 -0
  42. package/dist/gateway/protocol/schema/agents-models-skills.js +1 -0
  43. package/dist/gateway/server-chat.js +3 -3
  44. package/dist/gateway/server-methods/channels.js +10 -2
  45. package/dist/gateway/server-methods/chat.js +19 -12
  46. package/dist/gateway/server-methods/models.js +4 -0
  47. package/dist/gateway/server-methods/services.js +47 -2
  48. package/dist/gateway/server-methods-list.js +2 -0
  49. package/dist/gateway/session-event-payloads.js +20 -9
  50. package/dist/infra/google-api-base-url.js +29 -0
  51. package/dist/plugin-sdk/{accounts-BJuN64m-.js → accounts-DxblobYM.js} +1 -1
  52. package/dist/plugin-sdk/{actions-DaEvM9Bl.js → actions-D4UNimWa.js} +2 -2
  53. package/dist/plugin-sdk/{active-listener-B8Kuanet.js → active-listener-CFuy106s.js} +1 -1
  54. package/dist/plugin-sdk/{audio-preflight-D7tCO0EH.js → audio-preflight-CxZixyhW.js} +25 -25
  55. package/dist/plugin-sdk/{audit-CVWH4BVP.js → audit-Cs0K7LBp.js} +1 -1
  56. package/dist/plugin-sdk/{channel-access-CH9FAS-I.js → channel-access-D5aHoTY8.js} +1 -1
  57. package/dist/plugin-sdk/{channel-access-configure-CI0je66H.js → channel-access-configure-Dvch9mMQ.js} +1 -1
  58. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  59. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  60. package/dist/plugin-sdk/{chunk-01oBnQjh.js → chunk-B0FUGc5A.js} +1 -1
  61. package/dist/plugin-sdk/{chunk-items-D2zDJ8q8.js → chunk-items-Cy9a3X7x.js} +1 -1
  62. package/dist/plugin-sdk/command-status.js +25 -25
  63. package/dist/plugin-sdk/{commands-registry-ckg4fhYa.js → commands-registry-DmDRhRAU.js} +2 -2
  64. package/dist/plugin-sdk/{common-zHvdO8ly.js → common-CfUOowOp.js} +1 -1
  65. package/dist/plugin-sdk/{config-DrhYEiCM.js → config-mxULv0fQ.js} +1 -1
  66. package/dist/plugin-sdk/{config-schema-BtjeV5Hm.js → config-schema-D8E0LX8X.js} +4405 -460
  67. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  68. package/dist/plugin-sdk/discord.d.ts +3 -3
  69. package/dist/plugin-sdk/discord.js +19 -19
  70. package/dist/plugin-sdk/{handle-action-5dhYpMbP.js → handle-action-DbVCF9HZ.js} +13 -13
  71. package/dist/plugin-sdk/{helpers-E9aafRzA.js → helpers-BXbdgs5H.js} +1 -1
  72. package/dist/plugin-sdk/{image-ops-D2U9ctmu.js → image-ops-D7jPxaph.js} +1 -1
  73. package/dist/plugin-sdk/index.d.ts +4 -4
  74. package/dist/plugin-sdk/index.js +27 -27
  75. package/dist/plugin-sdk/{login-hql0fNuL.js → login-BRxww6nv.js} +3 -3
  76. package/dist/plugin-sdk/{login-qr-DJJ0svux.js → login-qr-DJXsk9XH.js} +3 -3
  77. package/dist/plugin-sdk/{manager-ciNnAMBn.js → manager-Y2S_5IlI.js} +2 -2
  78. package/dist/plugin-sdk/{markdown-tables-BKxbua_h.js → markdown-tables-DHLFQqaF.js} +1 -1
  79. package/dist/plugin-sdk/{media-DUIZ6x9H.js → media-Czo4carg.js} +2 -2
  80. package/dist/plugin-sdk/{monitor-D-giXewh.js → monitor-CFHFAUOj.js} +30 -30
  81. package/dist/plugin-sdk/{monitor-CNSGw4uQ.js → monitor-xS9A8Upr.js} +29 -29
  82. package/dist/plugin-sdk/ollama-runtime.js +970 -0
  83. package/dist/plugin-sdk/{openresponses-http-dbTd8UMk.js → openresponses-http-BcUbeVV6.js} +25 -25
  84. package/dist/plugin-sdk/{optional-runtime-dependency-CKsJL1PD.js → optional-runtime-dependency-DdMbxS24.js} +1 -1
  85. package/dist/plugin-sdk/{outbound-DhGrjRft.js → outbound-Db8mRN6P.js} +6 -6
  86. package/dist/plugin-sdk/{outbound-attachment-DAF8huFz.js → outbound-attachment-BqFjcypb.js} +2 -2
  87. package/dist/plugin-sdk/{pairing-message-BsJVvXkb.js → pairing-message-B3GC_7CD.js} +1 -1
  88. package/dist/plugin-sdk/{pairing-message-C403mq4K.d.ts → pairing-message-CxonJmwM.d.ts} +1 -1
  89. package/dist/plugin-sdk/{pairing-store-DwM-BNbj.js → pairing-store-CQikgIbw.js} +2 -2
  90. package/dist/plugin-sdk/{pi-model-discovery-DkIdlJvJ.js → pi-model-discovery-CY3m5zJP.js} +1 -1
  91. package/dist/plugin-sdk/{pi-model-discovery-runtime-Cv7okUMt.js → pi-model-discovery-runtime-DSh1T2pn.js} +2 -2
  92. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  93. package/dist/plugin-sdk/{pw-ai-Dxd87W3J.js → pw-ai-AFUqn-tQ.js} +3 -3
  94. package/dist/plugin-sdk/{replies-Bx61SvAC.js → replies-Fzh9PhXl.js} +2 -2
  95. package/dist/plugin-sdk/{resolve-outbound-target-bc-FpPZg.js → resolve-outbound-target-KpF3co6T.js} +1 -1
  96. package/dist/plugin-sdk/{resolve-route-D0eBYPtb.js → resolve-route-CDl74O19.js} +1 -1
  97. package/dist/plugin-sdk/{retry-policy-HTrC4zfC.js → retry-policy-D4uiqUOG.js} +1 -1
  98. package/dist/plugin-sdk/{runtime-BVkIrYLQ.js → runtime-Bb6KRnCC.js} +6 -4
  99. package/dist/plugin-sdk/{runtime-dependency-B2PqMdBu.js → runtime-dependency-DLDmB94f.js} +2 -2
  100. package/dist/plugin-sdk/sat-runtime.d.ts +2 -2
  101. package/dist/plugin-sdk/sat-runtime.js +25 -25
  102. package/dist/plugin-sdk/{send-DgzZUx2i.js → send-5nFktR1n.js} +5 -5
  103. package/dist/plugin-sdk/{send-DT2egZNK.js → send-Bfzf11VY.js} +7 -7
  104. package/dist/plugin-sdk/{send-BGDGzxUu.js → send-C4k4JuJN.js} +6 -6
  105. package/dist/plugin-sdk/{send-KJy3ajA3.js → send-D-uDKGIq.js} +6 -6
  106. package/dist/plugin-sdk/{send-C3QWSYLR.js → send-Du8ykP1k.js} +8 -8
  107. package/dist/plugin-sdk/{session-BCIEFOu1.js → session-CS-Td9EM.js} +2 -2
  108. package/dist/plugin-sdk/{skill-commands-CyIkcR8P.js → skill-commands-CFAlXWHk.js} +2 -2
  109. package/dist/plugin-sdk/{slack-DjKNh2uh.js → slack-DXYGbizH.js} +37 -37
  110. package/dist/plugin-sdk/{slack-actions-CeM9LWDp.js → slack-actions-BcMkNv1Z.js} +11 -11
  111. package/dist/plugin-sdk/slack.d.ts +3 -3
  112. package/dist/plugin-sdk/slack.js +9 -9
  113. package/dist/plugin-sdk/{sticker-cache-DcWzQ2wx.js → sticker-cache-Dr3rYfU1.js} +3 -3
  114. package/dist/plugin-sdk/{tables-B2iehyjG.js → tables-bFXyRpkx.js} +1 -1
  115. package/dist/plugin-sdk/{targets-BPket0SF.js → targets-DH7a9lKB.js} +1 -1
  116. package/dist/plugin-sdk/{telegram-actions-Cx_HWVht.js → telegram-actions-Besbw_Hg.js} +28 -28
  117. package/dist/plugin-sdk/telegram.d.ts +3 -3
  118. package/dist/plugin-sdk/telegram.js +8 -8
  119. package/dist/plugin-sdk/{thread-bindings.lifecycle-CYH69SIw.js → thread-bindings.lifecycle-CgOvvdw8.js} +3 -3
  120. package/dist/plugin-sdk/{thread-bindings.state-BN6461WG.js → thread-bindings.state-CQpk44Hq.js} +2 -2
  121. package/dist/plugin-sdk/{tmp-fased-dir-Ouj58axY.d.ts → tmp-fased-dir-DQ00xQ5D.d.ts} +1 -1
  122. package/dist/plugin-sdk/{types-B4hj6HgR.d.ts → types-B6zx-Dl2.d.ts} +1 -1
  123. package/dist/plugin-sdk/{types-dWZFX1fk.d.ts → types-eUM7hcDz.d.ts} +1 -1
  124. package/dist/plugin-sdk/{web-vR-oNiBv.js → web-Q1-G--JH.js} +30 -30
  125. package/dist/plugin-sdk/{whatsapp-actions-BYNf8ceY.js → whatsapp-actions-BtSc2CBv.js} +13 -13
  126. package/dist/plugin-sdk/{whatsapp-heartbeat-DZwa8MF0.js → whatsapp-heartbeat-Bg9TuYzl.js} +2 -2
  127. package/dist/plugin-sdk/whatsapp.d.ts +3 -3
  128. package/dist/plugin-sdk/whatsapp.js +12 -12
  129. package/dist/plugin-sdk/{wsl-WGtdw_6h.js → wsl-C4DGFkOd.js} +3 -3
  130. package/dist/plugins/provider-runtime.js +79 -0
  131. package/dist/providers/refresh.js +14 -0
  132. package/extensions/acpx/package.json +1 -1
  133. package/extensions/bluebubbles/package.json +1 -1
  134. package/extensions/copilot-proxy/package.json +1 -1
  135. package/extensions/diagnostics-otel/package.json +1 -1
  136. package/extensions/fased-federation/package.json +1 -1
  137. package/extensions/google-gemini-cli-auth/package.json +1 -1
  138. package/extensions/imessage/package.json +1 -1
  139. package/extensions/irc/package.json +1 -1
  140. package/extensions/line/package.json +1 -1
  141. package/extensions/llm-task/package.json +1 -1
  142. package/extensions/lobster/package.json +1 -1
  143. package/extensions/matrix/CHANGELOG.md +6 -0
  144. package/extensions/matrix/package.json +1 -1
  145. package/extensions/mattermost/package.json +1 -1
  146. package/extensions/memory-core/package.json +2 -2
  147. package/extensions/memory-lancedb/package.json +1 -1
  148. package/extensions/minimax-portal-auth/package.json +1 -1
  149. package/extensions/msteams/CHANGELOG.md +6 -0
  150. package/extensions/msteams/package.json +1 -1
  151. package/extensions/nextcloud-talk/package.json +1 -1
  152. package/extensions/nostr/CHANGELOG.md +6 -0
  153. package/extensions/nostr/package.json +1 -1
  154. package/extensions/open-prose/package.json +1 -1
  155. package/extensions/sat-mining/package.json +1 -1
  156. package/extensions/signal/package.json +1 -1
  157. package/extensions/synology-chat/package.json +1 -1
  158. package/extensions/tlon/package.json +1 -1
  159. package/extensions/twitch/CHANGELOG.md +6 -0
  160. package/extensions/twitch/package.json +1 -1
  161. package/extensions/voice-call/CHANGELOG.md +6 -0
  162. package/extensions/voice-call/package.json +1 -1
  163. package/extensions/zalo/CHANGELOG.md +6 -0
  164. package/extensions/zalo/package.json +1 -1
  165. package/extensions/zalouser/CHANGELOG.md +6 -0
  166. package/extensions/zalouser/package.json +1 -1
  167. package/package.json +1 -1
@@ -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
  }