@ekanos/cli 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +103 -8
  2. package/dist/auth/ekanos-api.d.ts +23 -0
  3. package/dist/auth/ekanos-api.js +33 -6
  4. package/dist/auth/ekanos-api.js.map +1 -1
  5. package/dist/commands/add.d.ts +2 -0
  6. package/dist/commands/add.js +118 -0
  7. package/dist/commands/add.js.map +1 -0
  8. package/dist/commands/agents.js +3 -2
  9. package/dist/commands/agents.js.map +1 -1
  10. package/dist/commands/apps.d.ts +11 -3
  11. package/dist/commands/apps.js +86 -28
  12. package/dist/commands/apps.js.map +1 -1
  13. package/dist/commands/audiences.d.ts +31 -0
  14. package/dist/commands/audiences.js +239 -0
  15. package/dist/commands/audiences.js.map +1 -0
  16. package/dist/commands/build.d.ts +2 -0
  17. package/dist/commands/build.js +41 -0
  18. package/dist/commands/build.js.map +1 -0
  19. package/dist/commands/catalog.d.ts +4 -2
  20. package/dist/commands/catalog.js +166 -35
  21. package/dist/commands/catalog.js.map +1 -1
  22. package/dist/commands/check.d.ts +2 -0
  23. package/dist/commands/check.js +43 -0
  24. package/dist/commands/check.js.map +1 -0
  25. package/dist/commands/codegen.d.ts +2 -0
  26. package/dist/commands/codegen.js +49 -0
  27. package/dist/commands/codegen.js.map +1 -0
  28. package/dist/commands/contract.d.ts +52 -0
  29. package/dist/commands/contract.js +300 -0
  30. package/dist/commands/contract.js.map +1 -0
  31. package/dist/commands/dev.d.ts +7 -0
  32. package/dist/commands/dev.js +186 -12
  33. package/dist/commands/dev.js.map +1 -1
  34. package/dist/commands/index-experience.d.ts +31 -0
  35. package/dist/commands/index-experience.js +21 -0
  36. package/dist/commands/index-experience.js.map +1 -0
  37. package/dist/commands/init.d.ts +7 -0
  38. package/dist/commands/init.js +12 -0
  39. package/dist/commands/init.js.map +1 -1
  40. package/dist/commands/login.d.ts +2 -0
  41. package/dist/commands/login.js +17 -0
  42. package/dist/commands/login.js.map +1 -1
  43. package/dist/commands/new.d.ts +2 -0
  44. package/dist/commands/new.js +39 -0
  45. package/dist/commands/new.js.map +1 -0
  46. package/dist/commands/publish.d.ts +59 -0
  47. package/dist/commands/publish.js +118 -65
  48. package/dist/commands/publish.js.map +1 -1
  49. package/dist/commands/runs.d.ts +7 -3
  50. package/dist/commands/runs.js +21 -5
  51. package/dist/commands/runs.js.map +1 -1
  52. package/dist/commands/scope-notice.d.ts +13 -0
  53. package/dist/commands/scope-notice.js +12 -4
  54. package/dist/commands/scope-notice.js.map +1 -1
  55. package/dist/commands/sources-roles.d.ts +5 -0
  56. package/dist/commands/sources-roles.js +235 -0
  57. package/dist/commands/sources-roles.js.map +1 -0
  58. package/dist/commands/sources.d.ts +5 -0
  59. package/dist/commands/sources.js +8 -1
  60. package/dist/commands/sources.js.map +1 -1
  61. package/dist/commands/sync.d.ts +2 -0
  62. package/dist/commands/sync.js +150 -0
  63. package/dist/commands/sync.js.map +1 -0
  64. package/dist/commands/team-agent-readiness.d.ts +18 -2
  65. package/dist/commands/team-agent-readiness.js +39 -39
  66. package/dist/commands/team-agent-readiness.js.map +1 -1
  67. package/dist/commands/team-agents-agents.d.ts +5 -2
  68. package/dist/commands/team-agents-agents.js +152 -15
  69. package/dist/commands/team-agents-agents.js.map +1 -1
  70. package/dist/commands/team-agents-define.d.ts +31 -0
  71. package/dist/commands/team-agents-define.js +176 -0
  72. package/dist/commands/team-agents-define.js.map +1 -0
  73. package/dist/commands/team-agents-mcp.js +112 -10
  74. package/dist/commands/team-agents-mcp.js.map +1 -1
  75. package/dist/commands/team-agents-triggers.d.ts +1 -1
  76. package/dist/commands/team-agents-triggers.js +137 -5
  77. package/dist/commands/team-agents-triggers.js.map +1 -1
  78. package/dist/commands/team-agents.d.ts +4 -1
  79. package/dist/commands/team-agents.js +17 -4
  80. package/dist/commands/team-agents.js.map +1 -1
  81. package/dist/commands/team-llm-key.d.ts +14 -0
  82. package/dist/commands/team-llm-key.js +122 -0
  83. package/dist/commands/team-llm-key.js.map +1 -0
  84. package/dist/commands/teams-audience.d.ts +12 -0
  85. package/dist/commands/teams-audience.js +144 -0
  86. package/dist/commands/teams-audience.js.map +1 -0
  87. package/dist/commands/teams-connect.js +123 -31
  88. package/dist/commands/teams-connect.js.map +1 -1
  89. package/dist/commands/teams-lifecycle.d.ts +90 -0
  90. package/dist/commands/teams-lifecycle.js +457 -0
  91. package/dist/commands/teams-lifecycle.js.map +1 -0
  92. package/dist/commands/teams-onboard.d.ts +14 -0
  93. package/dist/commands/teams-onboard.js +62 -13
  94. package/dist/commands/teams-onboard.js.map +1 -1
  95. package/dist/commands/teams-overview.js +6 -2
  96. package/dist/commands/teams-overview.js.map +1 -1
  97. package/dist/commands/teams-scope.js +4 -0
  98. package/dist/commands/teams-scope.js.map +1 -1
  99. package/dist/commands/teams.d.ts +25 -5
  100. package/dist/commands/teams.js +106 -39
  101. package/dist/commands/teams.js.map +1 -1
  102. package/dist/commands/workflows.d.ts +37 -0
  103. package/dist/commands/workflows.js +589 -0
  104. package/dist/commands/workflows.js.map +1 -0
  105. package/dist/compatibility.d.ts +10 -1
  106. package/dist/compatibility.js +10 -1
  107. package/dist/compatibility.js.map +1 -1
  108. package/dist/connect-fields.d.ts +22 -0
  109. package/dist/connect-fields.js +115 -0
  110. package/dist/connect-fields.js.map +1 -0
  111. package/dist/context.d.ts +12 -0
  112. package/dist/context.js +32 -9
  113. package/dist/context.js.map +1 -1
  114. package/dist/dev-experience.d.ts +125 -0
  115. package/dist/dev-experience.js +128 -0
  116. package/dist/dev-experience.js.map +1 -0
  117. package/dist/experience/boundary.d.ts +12 -0
  118. package/dist/experience/boundary.js +260 -0
  119. package/dist/experience/boundary.js.map +1 -0
  120. package/dist/experience/build.d.ts +16 -0
  121. package/dist/experience/build.js +90 -0
  122. package/dist/experience/build.js.map +1 -0
  123. package/dist/experience/checks.d.ts +29 -0
  124. package/dist/experience/checks.js +133 -0
  125. package/dist/experience/checks.js.map +1 -0
  126. package/dist/experience/contract-sources.d.ts +36 -0
  127. package/dist/experience/contract-sources.js +111 -0
  128. package/dist/experience/contract-sources.js.map +1 -0
  129. package/dist/experience/definition-checks.d.ts +26 -0
  130. package/dist/experience/definition-checks.js +201 -0
  131. package/dist/experience/definition-checks.js.map +1 -0
  132. package/dist/experience/definition-shape.d.ts +60 -0
  133. package/dist/experience/definition-shape.js +2 -0
  134. package/dist/experience/definition-shape.js.map +1 -0
  135. package/dist/experience/init.d.ts +10 -0
  136. package/dist/experience/init.js +64 -0
  137. package/dist/experience/init.js.map +1 -0
  138. package/dist/experience/integrations-codegen.d.ts +17 -0
  139. package/dist/experience/integrations-codegen.js +51 -0
  140. package/dist/experience/integrations-codegen.js.map +1 -0
  141. package/dist/experience/json-schema-to-ts.d.ts +1 -0
  142. package/dist/experience/json-schema-to-ts.js +90 -0
  143. package/dist/experience/json-schema-to-ts.js.map +1 -0
  144. package/dist/experience/load-experience.d.ts +23 -0
  145. package/dist/experience/load-experience.js +115 -0
  146. package/dist/experience/load-experience.js.map +1 -0
  147. package/dist/experience/overrides.d.ts +8 -0
  148. package/dist/experience/overrides.js +21 -0
  149. package/dist/experience/overrides.js.map +1 -0
  150. package/dist/experience/pin-integration.d.ts +8 -0
  151. package/dist/experience/pin-integration.js +17 -0
  152. package/dist/experience/pin-integration.js.map +1 -0
  153. package/dist/experience/pin-ops.d.ts +12 -0
  154. package/dist/experience/pin-ops.js +36 -0
  155. package/dist/experience/pin-ops.js.map +1 -0
  156. package/dist/experience/pins.d.ts +35 -0
  157. package/dist/experience/pins.js +91 -0
  158. package/dist/experience/pins.js.map +1 -0
  159. package/dist/experience/project.d.ts +52 -0
  160. package/dist/experience/project.js +149 -0
  161. package/dist/experience/project.js.map +1 -0
  162. package/dist/experience/publish.d.ts +51 -0
  163. package/dist/experience/publish.js +144 -0
  164. package/dist/experience/publish.js.map +1 -0
  165. package/dist/experience/regenerate.d.ts +10 -0
  166. package/dist/experience/regenerate.js +33 -0
  167. package/dist/experience/regenerate.js.map +1 -0
  168. package/dist/experience/report.d.ts +9 -0
  169. package/dist/experience/report.js +23 -0
  170. package/dist/experience/report.js.map +1 -0
  171. package/dist/harness-scaffold.d.ts +52 -18
  172. package/dist/harness-scaffold.js +189 -9
  173. package/dist/harness-scaffold.js.map +1 -1
  174. package/dist/index.d.ts +13 -0
  175. package/dist/index.js +494 -127
  176. package/dist/index.js.map +1 -1
  177. package/dist/invocation.d.ts +31 -0
  178. package/dist/invocation.js +58 -0
  179. package/dist/invocation.js.map +1 -0
  180. package/dist/project-checks.d.ts +12 -0
  181. package/dist/project-checks.js +46 -0
  182. package/dist/project-checks.js.map +1 -1
  183. package/dist/project.d.ts +14 -0
  184. package/dist/project.js +10 -0
  185. package/dist/project.js.map +1 -1
  186. package/dist/repo/publish.d.ts +42 -0
  187. package/dist/repo/publish.js +225 -0
  188. package/dist/repo/publish.js.map +1 -0
  189. package/dist/repo/scaffold.d.ts +30 -0
  190. package/dist/repo/scaffold.js +165 -0
  191. package/dist/repo/scaffold.js.map +1 -0
  192. package/dist/runs-api.js +8 -3
  193. package/dist/runs-api.js.map +1 -1
  194. package/dist/safe-fs.d.ts +18 -0
  195. package/dist/safe-fs.js +57 -0
  196. package/dist/safe-fs.js.map +1 -1
  197. package/dist/secret-form.d.ts +48 -0
  198. package/dist/secret-form.js +212 -0
  199. package/dist/secret-form.js.map +1 -0
  200. package/dist/secret-input.d.ts +96 -0
  201. package/dist/secret-input.js +299 -0
  202. package/dist/secret-input.js.map +1 -0
  203. package/dist/team-connect-api.d.ts +78 -3
  204. package/dist/team-connect-api.js +17 -2
  205. package/dist/team-connect-api.js.map +1 -1
  206. package/dist/team-lifecycle-api.d.ts +435 -0
  207. package/dist/team-lifecycle-api.js +301 -0
  208. package/dist/team-lifecycle-api.js.map +1 -0
  209. package/dist/teams-api.d.ts +49 -1
  210. package/dist/teams-api.js +149 -27
  211. package/dist/teams-api.js.map +1 -1
  212. package/dist/unknown-command.d.ts +18 -0
  213. package/dist/unknown-command.js +50 -0
  214. package/dist/unknown-command.js.map +1 -0
  215. package/dist/validate-findings.js +2 -1
  216. package/dist/validate-findings.js.map +1 -1
  217. package/dist/workflows-api.d.ts +69 -0
  218. package/dist/workflows-api.js +182 -0
  219. package/dist/workflows-api.js.map +1 -0
  220. package/package.json +3 -3
  221. package/templates/AGENTS.md.tmpl +6 -4
  222. package/templates/claude-skill.md.tmpl +4 -1
  223. package/templates/ekanos.json.tmpl +1 -0
  224. package/templates/experience/AGENTS.md.tmpl +22 -0
  225. package/templates/experience/ekanos.json.tmpl +7 -0
  226. package/templates/experience/ekanos.lock.json.tmpl +4 -0
  227. package/templates/experience/eslint.config.mjs.tmpl +25 -0
  228. package/templates/experience/gitignore.tmpl +17 -0
  229. package/templates/experience/package.json.tmpl +41 -0
  230. package/templates/experience/src/.ekanos/integrations.d.ts.tmpl +3 -0
  231. package/templates/experience/src/experience.ts.tmpl +30 -0
  232. package/templates/experience/src/widgets/hello-widget.tsx.tmpl +40 -0
  233. package/templates/experience/tsconfig.json.tmpl +20 -0
  234. package/templates/harness/app/%5F%5Fekanos/api/[...path]/route.ts.tmpl +9 -0
  235. package/templates/harness/app/[slug]/activation/page.tsx.tmpl +1 -1
  236. package/templates/harness/app/[slug]/page.tsx.tmpl +1 -1
  237. package/templates/harness/app/[slug]/tile/page.tsx.tmpl +1 -1
  238. package/templates/harness/app/[slug]/triggers/page.tsx.tmpl +1 -1
  239. package/templates/harness/app/[slug]/widgets/[widgetId]/page.tsx.tmpl +1 -1
  240. package/templates/harness/app/[slug]/widgets/page.tsx.tmpl +1 -1
  241. package/templates/harness/app/experience/layout.tsx.tmpl +17 -0
  242. package/templates/harness/app/experience/page.tsx.tmpl +1 -0
  243. package/templates/harness/app/experience/pages/[id]/page.tsx.tmpl +1 -0
  244. package/templates/harness/app/experience/review-queue/page.tsx.tmpl +1 -0
  245. package/templates/harness/app/experience/runs/page.tsx.tmpl +1 -0
  246. package/templates/harness/app/experience/widgets/[id]/page.tsx.tmpl +1 -0
  247. package/templates/harness/app/experience/widgets/page.tsx.tmpl +1 -0
  248. package/templates/harness/app/experience/workflows/[id]/page.tsx.tmpl +1 -0
  249. package/templates/harness/app/experience/workflows/page.tsx.tmpl +1 -0
  250. package/templates/harness/app/integrations/[slug]/connect/page.tsx.tmpl +1 -0
  251. package/templates/harness/app/integrations/[slug]/events/page.tsx.tmpl +1 -0
  252. package/templates/harness/app/integrations/[slug]/health/page.tsx.tmpl +1 -0
  253. package/templates/harness/app/integrations/[slug]/operations/[kind]/[name]/page.tsx.tmpl +1 -0
  254. package/templates/harness/app/integrations/[slug]/operations/page.tsx.tmpl +1 -0
  255. package/templates/harness/app/integrations/[slug]/page.tsx.tmpl +1 -0
  256. package/templates/harness/app/integrations/[slug]/preview/activation/page.tsx.tmpl +1 -0
  257. package/templates/harness/app/integrations/[slug]/preview/layout.tsx.tmpl +1 -0
  258. package/templates/harness/app/integrations/[slug]/preview/page.tsx.tmpl +1 -0
  259. package/templates/harness/app/integrations/[slug]/preview/tile/page.tsx.tmpl +1 -0
  260. package/templates/harness/app/integrations/[slug]/preview/triggers/page.tsx.tmpl +1 -0
  261. package/templates/harness/app/integrations/[slug]/preview/widgets/[widgetId]/page.tsx.tmpl +1 -0
  262. package/templates/harness/app/integrations/[slug]/preview/widgets/page.tsx.tmpl +1 -0
  263. package/templates/harness/app/integrations/[slug]/runs/page.tsx.tmpl +1 -0
  264. package/templates/harness/app/integrations/layout.tsx.tmpl +1 -0
  265. package/templates/harness/app/integrations/page.tsx.tmpl +1 -0
  266. package/templates/harness/app/page.experience.tsx.tmpl +5 -0
  267. package/templates/harness/repo/app/%5F%5Fekanos/api/[...path]/route.ts.tmpl +11 -0
  268. package/templates/harness/repo/app/(repo)/connections/page.tsx.tmpl +1 -0
  269. package/templates/harness/repo/app/(repo)/layout.tsx.tmpl +1 -0
  270. package/templates/harness/repo/app/(repo)/page.tsx.tmpl +1 -0
  271. package/templates/harness/repo/app/experiences/[name]/connect/page.tsx.tmpl +1 -0
  272. package/templates/harness/repo/app/experiences/[name]/layout.tsx.tmpl +9 -0
  273. package/templates/harness/repo/app/experiences/page.tsx.tmpl +5 -0
  274. package/templates/harness/repo/app/harness-shell.tsx.tmpl +9 -0
  275. package/templates/harness/repo/app/integrations/[slug]/layout.tsx.tmpl +1 -0
  276. package/templates/harness/repo/app/integrations/page.tsx.tmpl +5 -0
  277. package/templates/harness/repo/app/repo-experiences.ts.tmpl +5 -0
  278. package/templates/harness/styles/globals.css.tmpl +15 -1
  279. package/templates/package.json.tmpl +4 -4
  280. package/templates/skills/build-agent/SKILL.md +43 -10
  281. package/templates/skills/build-agent/references/agent-json.md +4 -1
  282. package/templates/skills/build-agent/references/app-setup.md +14 -7
  283. package/templates/skills/build-agent/references/rollout.md +26 -2
  284. package/templates/skills/build-agent/references/set-up-an-integration.md +46 -29
  285. package/templates/skills/build-integration/references/widget-design.md +1 -1
  286. package/templates/skills/ekanos-platform/SKILL.md +66 -14
  287. package/templates/skills/polish-widgets/references/patterns.md +3 -3
@@ -10,6 +10,8 @@ import { resolveSourceTarget } from '../source-target.js';
10
10
  import { stillUnauthorizedSourceSeats } from '../sources-api.js';
11
11
  import { listProductScopes, setScopeProduct } from '../teams-api.js';
12
12
  import { pluralize, shortHost } from '../ui.js';
13
+ import { scopeDeprecationNotice } from './scope-notice.js';
14
+ import { runSourcePeople, runSourceRoles } from './sources-roles.js';
13
15
  /**
14
16
  * `sources` — list every source the caller holds a developer or admin seat
15
17
  * on, the read half of the publish-target-verification story.
@@ -24,8 +26,12 @@ export async function runSources(ctx, args) {
24
26
  if (subcommand !== undefined) {
25
27
  if (subcommand === 'scopes')
26
28
  return runScopes(ctx, args, rest);
29
+ if (subcommand === 'roles')
30
+ return runSourceRoles(ctx, args, rest);
31
+ if (subcommand === 'people')
32
+ return runSourcePeople(ctx, args, rest);
27
33
  if (subcommand !== 'products') {
28
- throw usageError(`Unknown sources subcommand "${subcommand}".`, 'Run "ekanos sources" to list your sources, "ekanos sources products [enable|disable <product>]" or "ekanos sources scopes [add|remove <scope> <product>]".');
34
+ throw usageError(`Unknown sources subcommand "${subcommand}".`, 'Run "ekanos sources" to list your sources, "ekanos sources products [enable|disable <product>]", "ekanos sources scopes [add|remove <scope> <product>]", "ekanos sources roles [list|create|update|delete <role>]" or "ekanos sources people".');
29
35
  }
30
36
  return runProducts(ctx, args, rest);
31
37
  }
@@ -216,6 +222,7 @@ async function runScopes(ctx, args, rest) {
216
222
  if (verb === 'remove' && !args.yes) {
217
223
  throw invalidStateError(`Taking ${product} out of ${scope} hides it, in the app, from every team on ${scope} that no other of its scopes shows it to.`, `Re-run with --yes: ekanos sources scopes remove ${quoteArg(scope)} ${product} --yes`);
218
224
  }
225
+ ctx.warn(scopeDeprecationNotice(verb ? `audiences set ${scope} --products …` : 'audiences list'));
219
226
  const env = resolveAuthEnvironment(ctx, args.host, args.env, {
220
227
  projectDir: args.dir,
221
228
  });
@@ -1 +1 @@
1
- {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/commands/sources.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport { loginHint } from '../auth/host-trust';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport { authRequiredError, invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { EKANOS_CONFIG_FILENAME } from '../project';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport {\n type SourceProduct,\n type SourceProductsApiResult,\n listSourceProducts,\n setSourceProduct,\n} from '../source-products-api';\nimport { resolveSourceTarget } from '../source-target';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { listProductScopes, setScopeProduct } from '../teams-api';\nimport { type Ui, pluralize, shortHost } from '../ui';\n\nexport interface SourcesArgs {\n host?: string;\n /** `products [enable|disable <product>]`, `scopes [add|remove <scope> <product>]`; none lists the sources. */\n positionals?: readonly string[];\n source?: string;\n yes?: boolean;\n /** Where a project's ekanos.json may sit — its `source` field, if any, is the marker. */\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `sources` — list every source the caller holds a developer or admin seat\n * on, the read half of the publish-target-verification story.\n *\n * The list itself is the mechanism that makes `publish` safe: a partner (or\n * an agent acting for one) can always answer \"which sources can I possibly\n * publish to?\" before running `ekanos use` or passing `--source`.\n */\nexport async function runSources(\n ctx: CliContext,\n args: SourcesArgs,\n): Promise<ExitCode> {\n const [subcommand, ...rest] = args.positionals ?? [];\n if (subcommand !== undefined) {\n if (subcommand === 'scopes') return runScopes(ctx, args, rest);\n if (subcommand !== 'products') {\n throw usageError(\n `Unknown sources subcommand \"${subcommand}\".`,\n 'Run \"ekanos sources\" to list your sources, \"ekanos sources products [enable|disable <product>]\" or \"ekanos sources scopes [add|remove <scope> <product>]\".',\n );\n }\n return runProducts(ctx, args, rest);\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = requireSession(env);\n\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env);\n }\n\n const activeSource = readActiveSource(args.dir);\n\n return ctx.succeed(\n { host: env.host, sources: result.sources, activeSource },\n humanSummary(ctx.ui, env.host, result.sources, activeSource),\n );\n}\n\n/**\n * Best-effort, lenient read of ekanos.json's `source` field — mirrors\n * `readProjectHost` in `auth/session.ts`. A missing, unparseable, or\n * sourceless ekanos.json (or no project at all) simply means \"no active\n * source to mark\", never an error: `sources` must answer outside a project\n * too.\n */\nexport function readActiveSource(dir: string): string | null {\n const configPath = path.join(dir, EKANOS_CONFIG_FILENAME);\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const source =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { source?: unknown }).source\n : undefined;\n\n return typeof source === 'string' && source.trim().length > 0\n ? source.trim()\n : null;\n}\n\n/** Why a product is not enabled, by the lever that would enable it — the words `catalog` uses. */\nconst NOT_ENABLED_BECAUSE: Record<SourceProduct['lever'], string> = {\n flag: 'its Flag is off',\n plan: 'no active plan',\n entitlement: 'not granted',\n};\n\nfunction humanSummary(\n ui: Ui,\n host: string,\n sources: SourceSeat[],\n activeSource: string | null,\n): string {\n if (sources.length === 0) {\n return ui.result({\n // Read by any signed-in account, a team member's too: no word here\n // may imply the host serves more than one customer.\n title: 'No partner seats',\n tone: 'warn',\n rows: [['Host', shortHost(host)]],\n notes: ['This account has no partner seats on this host.'],\n });\n }\n\n const orphaned =\n activeSource && !sources.some((s) => s.slug === activeSource);\n\n return ui.result({\n title: `${pluralize(sources.length, 'source')} on ${shortHost(host)}`,\n rows: sources.map((source): [string, string] => [\n source.slug,\n [\n source.name ?? '(unnamed)',\n source.roles.join(', '),\n ...(source.slug === activeSource ? [ui.accent('this project')] : []),\n ].join(' · '),\n ]),\n notes: orphaned\n ? [\n `This project's ekanos.json points at \"${activeSource}\", which is not in the list above.`,\n ]\n : [],\n });\n}\n\n/**\n * `sources products` — the catalog as one Source has it: every product, its\n * kind (Experience or Integration), and whether it is enabled for the Source.\n * `enable|disable <product>` switch one through the levers `/admin` uses\n * (`attachProductToSource` / `detachProductFromSource` for an Integration,\n * the Experience lever for an Experience). A Source admin switches the\n * integrations their Source built (`owned`); anything else is a super-admin\n * grant, which an `ekanos login` session never carries, so from the terminal\n * that write answers with where to do it.\n */\nasync function runProducts(\n ctx: CliContext,\n args: SourcesArgs,\n rest: readonly string[],\n): Promise<ExitCode> {\n const [verb, rawProduct, ...extra] = rest;\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'Run \"ekanos sources products [enable|disable <product>] [--source <slug>]\".',\n );\n }\n if (verb !== undefined && verb !== 'enable' && verb !== 'disable') {\n throw usageError(\n `Unknown sources products subcommand \"${verb}\".`,\n 'Run \"ekanos sources products\", \"ekanos sources products enable <product>\" or \"ekanos sources products disable <product> --yes\".',\n );\n }\n const product = verb ? productSlug(rawProduct, verb) : null;\n if (verb === 'disable' && !args.yes) {\n throw invalidStateError(\n `Disabling ${product} removes it from the Source: its plans, widget settings, Source credentials and workspaces go with it.`,\n `Re-run with --yes: ekanos sources products disable ${product} --yes`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(env, requireSession(env), {\n source: args.source,\n dir: args.dir,\n });\n const client = new ProductsClient(env, resolved.session);\n\n if (!verb || !product) {\n const list = await client.call((token) =>\n listSourceProducts(env.host, token, resolved.source),\n );\n const byKind = (kind: SourceProduct['kind']) =>\n list.products\n .filter((entry) => entry.kind === kind)\n .map((entry): [string, string] => [\n entry.slug,\n `${entry.enabled ? '✓ enabled' : `✗ not enabled (${NOT_ENABLED_BECAUSE[entry.lever]})`} · ${entry.name}${entry.owned ? ' · built by this Source' : ''}${entry.registered ? '' : ' · no package on this host'}`,\n ]);\n return ctx.succeed(\n { host: env.host, ...list },\n [\n ctx.ui.result({\n title: `Experiences on ${list.source.slug}`,\n rows: byKind('experience'),\n }),\n ctx.ui.result({\n title: `Integrations on ${list.source.slug}`,\n rows: byKind('integration'),\n notes: [\n list.canEnable\n ? '\"ekanos sources products enable|disable <product>\" switches one.'\n : list.canEnableOwned\n ? '\"ekanos sources products enable|disable <product>\" switches one built by this Source; any other is a super-admin grant, made in the browser at /admin/sources → the Source → Products.'\n : 'Enabling one is a super-admin grant, made in the browser at /admin/sources → the Source → Products.',\n '\"ekanos teams connect <team> <product>\" connects an enabled integration for a team.',\n ],\n }),\n ].join('\\n'),\n );\n }\n\n const enabled = verb === 'enable';\n const result = await client.call((token) =>\n setSourceProduct(env.host, token, {\n source: resolved.source,\n product,\n enabled,\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.changed\n ? `${enabled ? 'Enabled' : 'Disabled'} ${result.product.name} for ${result.source.slug}`\n : `${result.product.name} was already ${enabled ? 'enabled' : 'off'} for ${result.source.slug}`,\n rows: [\n ['Kind', result.product.kind],\n [\n 'Switched by',\n result.product.lever === 'flag'\n ? 'its Flag'\n : result.product.lever === 'plan'\n ? 'its plan'\n : 'the entitlement',\n ],\n ],\n notes:\n enabled && result.product.kind === 'integration'\n ? [\n `Next: \"ekanos teams connect <team> ${result.product.slug}\" connects it for a team.`,\n ]\n : [],\n }),\n );\n}\n\n/**\n * `sources scopes` — the Source's product scopes, for its Source admin: each\n * scope's products and how many teams hold it. Once a Source has a scope,\n * each team sees in the app only its scopes' products. `add|remove <scope>\n * <product>` changes one scope (by name or id) through what\n * `/admin/sources/[id]/product-scopes` uses; a product can only be added if\n * the Source has it. Taking one out hides it from every team on the scope\n * that no other of its scopes shows it to, so that needs `--yes`.\n */\nasync function runScopes(\n ctx: CliContext,\n args: SourcesArgs,\n rest: readonly string[],\n): Promise<ExitCode> {\n const [verb, rawScope, rawProduct, ...extra] = rest;\n const usage =\n 'Run \"ekanos sources scopes\", \"ekanos sources scopes add <scope> <product>\" or \"ekanos sources scopes remove <scope> <product> --yes\".';\n if (verb !== undefined && verb !== 'add' && verb !== 'remove') {\n throw usageError(`Unknown sources scopes subcommand \"${verb}\".`, usage);\n }\n if (extra.length > 0) {\n throw usageError(`Unexpected argument \"${extra[0]}\".`, usage);\n }\n const scope = rawScope?.trim();\n if (verb && !scope) {\n throw usageError(\n `sources scopes ${verb} needs a scope and a product.`,\n usage,\n );\n }\n const product = verb ? scopeProductSlug(rawProduct, verb) : null;\n if (verb === 'remove' && !args.yes) {\n throw invalidStateError(\n `Taking ${product} out of ${scope} hides it, in the app, from every team on ${scope} that no other of its scopes shows it to.`,\n `Re-run with --yes: ekanos sources scopes remove ${quoteArg(scope!)} ${product} --yes`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(env, requireSession(env), {\n source: args.source,\n dir: args.dir,\n });\n const client = new ProductsClient(env, resolved.session);\n\n if (!verb || !product || !scope) {\n const list = await client.call((token) =>\n listProductScopes(env.host, token, resolved.source),\n );\n if (list.scopes.length === 0) {\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `No product scopes on ${list.source.slug}`,\n rows: [['Teams see', 'every product the Source has']],\n notes: [\n 'Scopes are created at /admin/sources → the Source → Product scopes.',\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `Product scopes on ${list.source.slug}`,\n rows: list.scopes.map((entry): [string, string[]] => [\n entry.name,\n [\n `${pluralize(entry.teamCount, 'team')}${entry.isDefault ? ' · given to every new team' : ''}`,\n entry.products.length > 0\n ? entry.products.map((item) => item.slug).join(', ')\n : 'no products',\n ],\n ]),\n notes: [\n 'A team sees in the app only the products of the scopes it holds.',\n '\"ekanos sources scopes add <scope> <product>\" shows a product to every team on a scope; \"ekanos teams scope show|set <team>\" reads or changes which scopes a team holds.',\n ],\n }),\n );\n }\n\n const included = verb === 'add';\n const result = await client.call((token) =>\n setScopeProduct(\n env.host,\n token,\n { source: resolved.source, scope, product },\n included,\n ),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.changed\n ? `${included ? 'Added' : 'Took'} ${result.product.name} ${included ? 'to' : 'out of'} ${result.scope.name}`\n : `${result.product.name} was already ${included ? 'in' : 'not in'} ${result.scope.name}`,\n tone: !included && result.changed ? 'warn' : 'ok',\n rows: [\n ['Teams on it', String(result.scope.teamCount)],\n [\n 'Products',\n result.scope.products.length > 0\n ? result.scope.products.map((item) => item.slug).join(', ')\n : 'none',\n ],\n ],\n notes: [\n included\n ? `Every team on ${result.scope.name} sees ${result.product.slug} in the app now.`\n : `Teams on ${result.scope.name} see ${result.product.slug} only if another of their scopes has it.`,\n ],\n }),\n );\n}\n\nfunction scopeProductSlug(value: string | undefined, verb: string): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value\n ? `\"${value}\" is not a product slug.`\n : `sources scopes ${verb} needs a product.`,\n '\"ekanos sources products\" lists them, e.g. \"ekanos sources scopes add Bronze linear\".',\n );\n}\n\nfunction quoteArg(value: string): string {\n return /^[A-Za-z0-9._-]+$/.test(value) ? value : `\"${value}\"`;\n}\n\nfunction productSlug(value: string | undefined, verb: string): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value\n ? `\"${value}\" is not a product slug.`\n : `sources products ${verb} needs a product.`,\n '\"ekanos sources products\" lists them, e.g. \"ekanos sources products enable agents\".',\n );\n}\n\nclass ProductsClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<SourceProductsApiResult<T>>,\n ): Promise<T> {\n let result = await send(this.session.accessToken);\n if (result.status === 'unauthorized') {\n this.session = await refreshStoredSession(this.env, this.session);\n result = await send(this.session.accessToken);\n }\n if (result.status === 'ok') return result.data;\n throw authRequiredError(\n `The session for ${this.env.host} could not authorize the sources call.`,\n loginHint(this.env),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/commands/sources.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport { loginHint } from '../auth/host-trust';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport { authRequiredError, invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { EKANOS_CONFIG_FILENAME } from '../project';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport {\n type SourceProduct,\n type SourceProductsApiResult,\n listSourceProducts,\n setSourceProduct,\n} from '../source-products-api';\nimport { resolveSourceTarget } from '../source-target';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { listProductScopes, setScopeProduct } from '../teams-api';\nimport { type Ui, pluralize, shortHost } from '../ui';\nimport { scopeDeprecationNotice } from './scope-notice';\nimport { runSourcePeople, runSourceRoles } from './sources-roles';\n\nexport interface SourcesArgs {\n host?: string;\n /** `products [enable|disable <product>]`, `scopes [add|remove <scope> <product>]`; none lists the sources. */\n positionals?: readonly string[];\n source?: string;\n yes?: boolean;\n /** `roles` and `people`: their flags (`--level`, `--permission`, `--search` …). */\n flags?: Record<string, string | boolean | string[]>;\n /** `roles delete` asks at a terminal; the test seams for it. */\n stdinIsTTY?: boolean;\n ask?: (question: string) => Promise<string>;\n /** Where a project's ekanos.json may sit — its `source` field, if any, is the marker. */\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `sources` — list every source the caller holds a developer or admin seat\n * on, the read half of the publish-target-verification story.\n *\n * The list itself is the mechanism that makes `publish` safe: a partner (or\n * an agent acting for one) can always answer \"which sources can I possibly\n * publish to?\" before running `ekanos use` or passing `--source`.\n */\nexport async function runSources(\n ctx: CliContext,\n args: SourcesArgs,\n): Promise<ExitCode> {\n const [subcommand, ...rest] = args.positionals ?? [];\n if (subcommand !== undefined) {\n if (subcommand === 'scopes') return runScopes(ctx, args, rest);\n if (subcommand === 'roles') return runSourceRoles(ctx, args, rest);\n if (subcommand === 'people') return runSourcePeople(ctx, args, rest);\n if (subcommand !== 'products') {\n throw usageError(\n `Unknown sources subcommand \"${subcommand}\".`,\n 'Run \"ekanos sources\" to list your sources, \"ekanos sources products [enable|disable <product>]\", \"ekanos sources scopes [add|remove <scope> <product>]\", \"ekanos sources roles [list|create|update|delete <role>]\" or \"ekanos sources people\".',\n );\n }\n return runProducts(ctx, args, rest);\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = requireSession(env);\n\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env);\n }\n\n const activeSource = readActiveSource(args.dir);\n\n return ctx.succeed(\n { host: env.host, sources: result.sources, activeSource },\n humanSummary(ctx.ui, env.host, result.sources, activeSource),\n );\n}\n\n/**\n * Best-effort, lenient read of ekanos.json's `source` field — mirrors\n * `readProjectHost` in `auth/session.ts`. A missing, unparseable, or\n * sourceless ekanos.json (or no project at all) simply means \"no active\n * source to mark\", never an error: `sources` must answer outside a project\n * too.\n */\nexport function readActiveSource(dir: string): string | null {\n const configPath = path.join(dir, EKANOS_CONFIG_FILENAME);\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const source =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { source?: unknown }).source\n : undefined;\n\n return typeof source === 'string' && source.trim().length > 0\n ? source.trim()\n : null;\n}\n\n/** Why a product is not enabled, by the lever that would enable it — the words `catalog` uses. */\nconst NOT_ENABLED_BECAUSE: Record<SourceProduct['lever'], string> = {\n flag: 'its Flag is off',\n plan: 'no active plan',\n entitlement: 'not granted',\n};\n\nfunction humanSummary(\n ui: Ui,\n host: string,\n sources: SourceSeat[],\n activeSource: string | null,\n): string {\n if (sources.length === 0) {\n return ui.result({\n // Read by any signed-in account, a team member's too: no word here\n // may imply the host serves more than one customer.\n title: 'No partner seats',\n tone: 'warn',\n rows: [['Host', shortHost(host)]],\n notes: ['This account has no partner seats on this host.'],\n });\n }\n\n const orphaned =\n activeSource && !sources.some((s) => s.slug === activeSource);\n\n return ui.result({\n title: `${pluralize(sources.length, 'source')} on ${shortHost(host)}`,\n rows: sources.map((source): [string, string] => [\n source.slug,\n [\n source.name ?? '(unnamed)',\n source.roles.join(', '),\n ...(source.slug === activeSource ? [ui.accent('this project')] : []),\n ].join(' · '),\n ]),\n notes: orphaned\n ? [\n `This project's ekanos.json points at \"${activeSource}\", which is not in the list above.`,\n ]\n : [],\n });\n}\n\n/**\n * `sources products` — the catalog as one Source has it: every product, its\n * kind (Experience or Integration), and whether it is enabled for the Source.\n * `enable|disable <product>` switch one through the levers `/admin` uses\n * (`attachProductToSource` / `detachProductFromSource` for an Integration,\n * the Experience lever for an Experience). A Source admin switches the\n * integrations their Source built (`owned`); anything else is a super-admin\n * grant, which an `ekanos login` session never carries, so from the terminal\n * that write answers with where to do it.\n */\nasync function runProducts(\n ctx: CliContext,\n args: SourcesArgs,\n rest: readonly string[],\n): Promise<ExitCode> {\n const [verb, rawProduct, ...extra] = rest;\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'Run \"ekanos sources products [enable|disable <product>] [--source <slug>]\".',\n );\n }\n if (verb !== undefined && verb !== 'enable' && verb !== 'disable') {\n throw usageError(\n `Unknown sources products subcommand \"${verb}\".`,\n 'Run \"ekanos sources products\", \"ekanos sources products enable <product>\" or \"ekanos sources products disable <product> --yes\".',\n );\n }\n const product = verb ? productSlug(rawProduct, verb) : null;\n if (verb === 'disable' && !args.yes) {\n throw invalidStateError(\n `Disabling ${product} removes it from the Source: its plans, widget settings, Source credentials and workspaces go with it.`,\n `Re-run with --yes: ekanos sources products disable ${product} --yes`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(env, requireSession(env), {\n source: args.source,\n dir: args.dir,\n });\n const client = new ProductsClient(env, resolved.session);\n\n if (!verb || !product) {\n const list = await client.call((token) =>\n listSourceProducts(env.host, token, resolved.source),\n );\n const byKind = (kind: SourceProduct['kind']) =>\n list.products\n .filter((entry) => entry.kind === kind)\n .map((entry): [string, string] => [\n entry.slug,\n `${entry.enabled ? '✓ enabled' : `✗ not enabled (${NOT_ENABLED_BECAUSE[entry.lever]})`} · ${entry.name}${entry.owned ? ' · built by this Source' : ''}${entry.registered ? '' : ' · no package on this host'}`,\n ]);\n return ctx.succeed(\n { host: env.host, ...list },\n [\n ctx.ui.result({\n title: `Experiences on ${list.source.slug}`,\n rows: byKind('experience'),\n }),\n ctx.ui.result({\n title: `Integrations on ${list.source.slug}`,\n rows: byKind('integration'),\n notes: [\n list.canEnable\n ? '\"ekanos sources products enable|disable <product>\" switches one.'\n : list.canEnableOwned\n ? '\"ekanos sources products enable|disable <product>\" switches one built by this Source; any other is a super-admin grant, made in the browser at /admin/sources → the Source → Products.'\n : 'Enabling one is a super-admin grant, made in the browser at /admin/sources → the Source → Products.',\n '\"ekanos teams connect <team> <product>\" connects an enabled integration for a team.',\n ],\n }),\n ].join('\\n'),\n );\n }\n\n const enabled = verb === 'enable';\n const result = await client.call((token) =>\n setSourceProduct(env.host, token, {\n source: resolved.source,\n product,\n enabled,\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.changed\n ? `${enabled ? 'Enabled' : 'Disabled'} ${result.product.name} for ${result.source.slug}`\n : `${result.product.name} was already ${enabled ? 'enabled' : 'off'} for ${result.source.slug}`,\n rows: [\n ['Kind', result.product.kind],\n [\n 'Switched by',\n result.product.lever === 'flag'\n ? 'its Flag'\n : result.product.lever === 'plan'\n ? 'its plan'\n : 'the entitlement',\n ],\n ],\n notes:\n enabled && result.product.kind === 'integration'\n ? [\n `Next: \"ekanos teams connect <team> ${result.product.slug}\" connects it for a team.`,\n ]\n : [],\n }),\n );\n}\n\n/**\n * `sources scopes` — the Source's product scopes, for its Source admin: each\n * scope's products and how many teams hold it. Once a Source has a scope,\n * each team sees in the app only its scopes' products. `add|remove <scope>\n * <product>` changes one scope (by name or id) through what\n * `/admin/sources/[id]/product-scopes` uses; a product can only be added if\n * the Source has it. Taking one out hides it from every team on the scope\n * that no other of its scopes shows it to, so that needs `--yes`.\n */\nasync function runScopes(\n ctx: CliContext,\n args: SourcesArgs,\n rest: readonly string[],\n): Promise<ExitCode> {\n const [verb, rawScope, rawProduct, ...extra] = rest;\n const usage =\n 'Run \"ekanos sources scopes\", \"ekanos sources scopes add <scope> <product>\" or \"ekanos sources scopes remove <scope> <product> --yes\".';\n if (verb !== undefined && verb !== 'add' && verb !== 'remove') {\n throw usageError(`Unknown sources scopes subcommand \"${verb}\".`, usage);\n }\n if (extra.length > 0) {\n throw usageError(`Unexpected argument \"${extra[0]}\".`, usage);\n }\n const scope = rawScope?.trim();\n if (verb && !scope) {\n throw usageError(\n `sources scopes ${verb} needs a scope and a product.`,\n usage,\n );\n }\n const product = verb ? scopeProductSlug(rawProduct, verb) : null;\n if (verb === 'remove' && !args.yes) {\n throw invalidStateError(\n `Taking ${product} out of ${scope} hides it, in the app, from every team on ${scope} that no other of its scopes shows it to.`,\n `Re-run with --yes: ekanos sources scopes remove ${quoteArg(scope!)} ${product} --yes`,\n );\n }\n\n ctx.warn(\n scopeDeprecationNotice(\n verb ? `audiences set ${scope} --products …` : 'audiences list',\n ),\n );\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(env, requireSession(env), {\n source: args.source,\n dir: args.dir,\n });\n const client = new ProductsClient(env, resolved.session);\n\n if (!verb || !product || !scope) {\n const list = await client.call((token) =>\n listProductScopes(env.host, token, resolved.source),\n );\n if (list.scopes.length === 0) {\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `No product scopes on ${list.source.slug}`,\n rows: [['Teams see', 'every product the Source has']],\n notes: [\n 'Scopes are created at /admin/sources → the Source → Product scopes.',\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `Product scopes on ${list.source.slug}`,\n rows: list.scopes.map((entry): [string, string[]] => [\n entry.name,\n [\n `${pluralize(entry.teamCount, 'team')}${entry.isDefault ? ' · given to every new team' : ''}`,\n entry.products.length > 0\n ? entry.products.map((item) => item.slug).join(', ')\n : 'no products',\n ],\n ]),\n notes: [\n 'A team sees in the app only the products of the scopes it holds.',\n '\"ekanos sources scopes add <scope> <product>\" shows a product to every team on a scope; \"ekanos teams scope show|set <team>\" reads or changes which scopes a team holds.',\n ],\n }),\n );\n }\n\n const included = verb === 'add';\n const result = await client.call((token) =>\n setScopeProduct(\n env.host,\n token,\n { source: resolved.source, scope, product },\n included,\n ),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.changed\n ? `${included ? 'Added' : 'Took'} ${result.product.name} ${included ? 'to' : 'out of'} ${result.scope.name}`\n : `${result.product.name} was already ${included ? 'in' : 'not in'} ${result.scope.name}`,\n tone: !included && result.changed ? 'warn' : 'ok',\n rows: [\n ['Teams on it', String(result.scope.teamCount)],\n [\n 'Products',\n result.scope.products.length > 0\n ? result.scope.products.map((item) => item.slug).join(', ')\n : 'none',\n ],\n ],\n notes: [\n included\n ? `Every team on ${result.scope.name} sees ${result.product.slug} in the app now.`\n : `Teams on ${result.scope.name} see ${result.product.slug} only if another of their scopes has it.`,\n ],\n }),\n );\n}\n\nfunction scopeProductSlug(value: string | undefined, verb: string): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value\n ? `\"${value}\" is not a product slug.`\n : `sources scopes ${verb} needs a product.`,\n '\"ekanos sources products\" lists them, e.g. \"ekanos sources scopes add Bronze linear\".',\n );\n}\n\nfunction quoteArg(value: string): string {\n return /^[A-Za-z0-9._-]+$/.test(value) ? value : `\"${value}\"`;\n}\n\nfunction productSlug(value: string | undefined, verb: string): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value\n ? `\"${value}\" is not a product slug.`\n : `sources products ${verb} needs a product.`,\n '\"ekanos sources products\" lists them, e.g. \"ekanos sources products enable agents\".',\n );\n}\n\nclass ProductsClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<SourceProductsApiResult<T>>,\n ): Promise<T> {\n let result = await send(this.session.accessToken);\n if (result.status === 'unauthorized') {\n this.session = await refreshStoredSession(this.env, this.session);\n result = await send(this.session.accessToken);\n }\n if (result.status === 'ok') return result.data;\n throw authRequiredError(\n `The session for ${this.env.host} could not authorize the sources call.`,\n loginHint(this.env),\n );\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { ExperienceCommand } from './index-experience.js';
2
+ export declare const syncCommand: ExperienceCommand;
@@ -0,0 +1,150 @@
1
+ import { shortOperation } from '@ekanos/integration-schema';
2
+ import { harnessContractSources } from '../dev-experience.js';
3
+ import { validationError } from '../errors.js';
4
+ import { defaultSources, resolveContract, } from '../experience/contract-sources.js';
5
+ import { pinIntegration } from '../experience/pin-integration.js';
6
+ import { DEPRECATED_USES, renamesNotice } from '../experience/pins.js';
7
+ import { readExperienceProject, readLockfile, writeIntegrations, } from '../experience/project.js';
8
+ import { loadPins, regenerateIntegrations } from '../experience/regenerate.js';
9
+ import { failOnErrors } from '../experience/report.js';
10
+ export const syncCommand = {
11
+ name: 'sync',
12
+ usage: [
13
+ " sync Re-resolve every integration's contract, accept a new digest within the",
14
+ ' pinned line, and regenerate the lock + types (--check: report only)',
15
+ ],
16
+ specs: {
17
+ dir: {
18
+ type: 'string',
19
+ description: 'Project directory (default: the current one)',
20
+ },
21
+ check: {
22
+ type: 'boolean',
23
+ description: 'Report drift without writing anything; exit 3 when there is some',
24
+ },
25
+ 'from-dir': {
26
+ type: 'string',
27
+ description: 'Directory of contract files to resolve from',
28
+ },
29
+ 'contracts-dir': {
30
+ type: 'string',
31
+ description: 'Contracts directory the harness resolves from (same as "ekanos dev --contracts-dir"; default: EKANOS_CONTRACTS_DIR)',
32
+ },
33
+ 'from-path': {
34
+ type: 'string',
35
+ description: 'An integration project directory to resolve from',
36
+ },
37
+ },
38
+ async run(ctx, args) {
39
+ var _a, _b;
40
+ const project = readExperienceProject(args.dir);
41
+ const checkOnly = args.flags.check === true;
42
+ const fromDir = args.flags['from-dir'];
43
+ const fromPath = args.flags['from-path'];
44
+ const contractsDir = args.flags['contracts-dir'];
45
+ const explicit = Boolean((_b = (_a = fromDir !== null && fromDir !== void 0 ? fromDir : fromPath) !== null && _a !== void 0 ? _a : contractsDir) !== null && _b !== void 0 ? _b : args.env.EKANOS_CONTRACTS_DIR);
46
+ // The harness's own registry chain (session catalog, published-package
47
+ // cache, contracts directory, then the committed snapshot) is what
48
+ // `ekanos dev` resolves with, so it is what decides drift here too.
49
+ const sources = defaultSources(project, { fromDir, fromPath, env: args.env }, await harnessContractSources(project.dir, args.env, Object.assign(Object.assign({}, ((contractsDir !== null && contractsDir !== void 0 ? contractsDir : fromDir)
50
+ ? { contractsDir: (contractsDir !== null && contractsDir !== void 0 ? contractsDir : fromDir) }
51
+ : {})), { onUnavailable: (reason) => ctx.warn(`Could not ask the project's @ekanos/harness for contracts (${reason}); resolving from the committed snapshots only.`) })));
52
+ if (project.deprecatedUses) {
53
+ ctx.warn(DEPRECATED_USES);
54
+ const notice = renamesNotice(project.renames);
55
+ if (notice)
56
+ ctx.warn(notice);
57
+ }
58
+ const lock = readLockfile(project);
59
+ const resolved = new Map();
60
+ const drift = [];
61
+ const consulted = new Set();
62
+ for (const use of project.integrations) {
63
+ const hit = await resolveContract(sources, use.name, use.version);
64
+ resolved.set(use.name, hit);
65
+ consulted.add(isSnapshotOnly(hit) ? 'snapshot' : 'harness');
66
+ const locked = lock.integrations[use.name];
67
+ if (!locked || locked.digest !== hit.contract.digest) {
68
+ drift.push(`${use.name}: ${locked ? `${locked.contractVersion} -> ${hit.contract.contractVersion}` : `new pin ${hit.contract.contractVersion}`}`);
69
+ }
70
+ }
71
+ const snapshotOnly = !explicit && !consulted.has('harness');
72
+ const snapshotNotice = 'Only the committed snapshots were consulted (no contracts directory and nothing newer from the harness registry), so a running "ekanos dev" that resolves elsewhere can still see a drifted digest. Pass --contracts-dir <dir> or set EKANOS_CONTRACTS_DIR, as you do for "ekanos dev".';
73
+ if (checkOnly) {
74
+ if (drift.length > 0) {
75
+ throw validationError(`${drift.length} integration${drift.length === 1 ? '' : 's'} drifted: ${drift.join('; ')}.`, 'Run "ekanos sync" to accept the new contracts.');
76
+ }
77
+ if (snapshotOnly)
78
+ ctx.warn(snapshotNotice);
79
+ return ctx.succeed({ drift: [], changed: false, snapshotOnly }, snapshotOnly
80
+ ? 'All integrations match their committed snapshots (no other contract source was consulted).'
81
+ : 'All integrations match their pinned contracts.');
82
+ }
83
+ // Judge the incoming contracts before writing any of it: a new contract
84
+ // that drops a declared operation must not silently replace the old pin.
85
+ const candidate = new Map([...resolved].map(([name, hit]) => [name, hit.contract]));
86
+ const incoming = project.integrations.flatMap((use) => {
87
+ const contract = candidate.get(use.name);
88
+ if (!contract)
89
+ return [];
90
+ const have = new Set(contract.operations.map((op) => op.id));
91
+ return use.operations
92
+ .filter((id) => !have.has(id))
93
+ .map((id) => ({
94
+ check: 'checks/integration-pins',
95
+ severity: 'error',
96
+ message: `integration "${use.name}": ${use.name}@${use.version} (contract ${contract.contractVersion}) no longer provides ${shortOperation(id)}.`,
97
+ hint: 'Remove the operation from "integrations", or keep the older pinned contract.',
98
+ file: 'ekanos.json',
99
+ }));
100
+ });
101
+ const failed = failOnErrors(ctx, 'sync', incoming, { drift }, project.dir);
102
+ if (failed !== null)
103
+ return failed;
104
+ let changed = false;
105
+ if (project.deprecatedUses) {
106
+ changed = writeIntegrations(project, project.integrations) || changed;
107
+ }
108
+ for (const use of project.integrations) {
109
+ const hit = resolved.get(use.name);
110
+ if (!hit)
111
+ continue;
112
+ const result = pinIntegration(project, use, hit.contract);
113
+ changed = changed || result.snapshotChanged || result.lockChanged;
114
+ }
115
+ const { pinned } = loadPins(project);
116
+ const typesChanged = regenerateIntegrations(project, pinned);
117
+ changed = changed || typesChanged;
118
+ if (snapshotOnly && drift.length === 0)
119
+ ctx.warn(snapshotNotice);
120
+ return ctx.succeed({
121
+ drift,
122
+ changed,
123
+ snapshotOnly,
124
+ explicitSource: explicit,
125
+ migratedUses: project.deprecatedUses,
126
+ renames: project.renames,
127
+ integrations: project.integrations.map((u) => { var _a, _b, _c; return ({
128
+ integration: u.name,
129
+ contractVersion: (_a = resolved.get(u.name)) === null || _a === void 0 ? void 0 : _a.contract.contractVersion,
130
+ digest: (_b = resolved.get(u.name)) === null || _b === void 0 ? void 0 : _b.contract.digest,
131
+ origin: (_c = resolved.get(u.name)) === null || _c === void 0 ? void 0 : _c.origin,
132
+ }); }),
133
+ }, drift.length > 0
134
+ ? `Synced ${project.integrations.length} integration${project.integrations.length === 1 ? '' : 's'}; accepted: ${drift.join('; ')}.`
135
+ : changed
136
+ ? 'Synced; lock or types were regenerated.'
137
+ : snapshotOnly
138
+ ? 'Nothing to sync against the committed snapshots; no other contract source was consulted.'
139
+ : 'Nothing to sync: every integration matches its pinned contract.');
140
+ },
141
+ };
142
+ /**
143
+ * The harness's registry source falls back to the committed snapshot when it
144
+ * finds nothing newer, and says so in its origin ("snapshot <dir>"). A hit that
145
+ * is only that fallback proves nothing about the registry.
146
+ */
147
+ function isSnapshotOnly(hit) {
148
+ return hit.kind === 'snapshot' || hit.origin.startsWith('snapshot ');
149
+ }
150
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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","sourcesContent":["import { shortOperation } from '@ekanos/integration-schema';\n\nimport { harnessContractSources } from '../dev-experience';\nimport { validationError } from '../errors';\nimport {\n type ResolvedContract,\n defaultSources,\n resolveContract,\n} from '../experience/contract-sources';\nimport { pinIntegration } from '../experience/pin-integration';\nimport { DEPRECATED_USES, renamesNotice } from '../experience/pins';\nimport {\n readExperienceProject,\n readLockfile,\n writeIntegrations,\n} from '../experience/project';\nimport { loadPins, regenerateIntegrations } from '../experience/regenerate';\nimport { failOnErrors } from '../experience/report';\nimport type { ExperienceCommand } from './index-experience';\n\nexport const syncCommand: ExperienceCommand = {\n name: 'sync',\n usage: [\n \" sync Re-resolve every integration's contract, accept a new digest within the\",\n ' pinned line, and regenerate the lock + types (--check: report only)',\n ],\n specs: {\n dir: {\n type: 'string',\n description: 'Project directory (default: the current one)',\n },\n check: {\n type: 'boolean',\n description:\n 'Report drift without writing anything; exit 3 when there is some',\n },\n 'from-dir': {\n type: 'string',\n description: 'Directory of contract files to resolve from',\n },\n 'contracts-dir': {\n type: 'string',\n description:\n 'Contracts directory the harness resolves from (same as \"ekanos dev --contracts-dir\"; default: EKANOS_CONTRACTS_DIR)',\n },\n 'from-path': {\n type: 'string',\n description: 'An integration project directory to resolve from',\n },\n },\n async run(ctx, args) {\n const project = readExperienceProject(args.dir);\n const checkOnly = args.flags.check === true;\n const fromDir = args.flags['from-dir'] as string | undefined;\n const fromPath = args.flags['from-path'] as string | undefined;\n const contractsDir = args.flags['contracts-dir'] as string | undefined;\n const explicit = Boolean(\n fromDir ?? fromPath ?? contractsDir ?? args.env.EKANOS_CONTRACTS_DIR,\n );\n // The harness's own registry chain (session catalog, published-package\n // cache, contracts directory, then the committed snapshot) is what\n // `ekanos dev` resolves with, so it is what decides drift here too.\n const sources = defaultSources(\n project,\n { fromDir, fromPath, env: args.env },\n await harnessContractSources(project.dir, args.env, {\n ...((contractsDir ?? fromDir)\n ? { contractsDir: (contractsDir ?? fromDir) as string }\n : {}),\n onUnavailable: (reason) =>\n ctx.warn(\n `Could not ask the project's @ekanos/harness for contracts (${reason}); resolving from the committed snapshots only.`,\n ),\n }),\n );\n\n if (project.deprecatedUses) {\n ctx.warn(DEPRECATED_USES);\n const notice = renamesNotice(project.renames);\n if (notice) ctx.warn(notice);\n }\n\n const lock = readLockfile(project);\n const resolved = new Map<string, ResolvedContract>();\n const drift: string[] = [];\n const consulted = new Set<ResolvedContract['kind'] | 'harness'>();\n for (const use of project.integrations) {\n const hit = await resolveContract(sources, use.name, use.version);\n resolved.set(use.name, hit);\n consulted.add(isSnapshotOnly(hit) ? 'snapshot' : 'harness');\n const locked = lock.integrations[use.name];\n if (!locked || locked.digest !== hit.contract.digest) {\n drift.push(\n `${use.name}: ${locked ? `${locked.contractVersion} -> ${hit.contract.contractVersion}` : `new pin ${hit.contract.contractVersion}`}`,\n );\n }\n }\n\n const snapshotOnly = !explicit && !consulted.has('harness');\n const snapshotNotice =\n 'Only the committed snapshots were consulted (no contracts directory and nothing newer from the harness registry), so a running \"ekanos dev\" that resolves elsewhere can still see a drifted digest. Pass --contracts-dir <dir> or set EKANOS_CONTRACTS_DIR, as you do for \"ekanos dev\".';\n\n if (checkOnly) {\n if (drift.length > 0) {\n throw validationError(\n `${drift.length} integration${drift.length === 1 ? '' : 's'} drifted: ${drift.join('; ')}.`,\n 'Run \"ekanos sync\" to accept the new contracts.',\n );\n }\n\n if (snapshotOnly) ctx.warn(snapshotNotice);\n\n return ctx.succeed(\n { drift: [], changed: false, snapshotOnly },\n snapshotOnly\n ? 'All integrations match their committed snapshots (no other contract source was consulted).'\n : 'All integrations match their pinned contracts.',\n );\n }\n\n // Judge the incoming contracts before writing any of it: a new contract\n // that drops a declared operation must not silently replace the old pin.\n const candidate = new Map(\n [...resolved].map(([name, hit]) => [name, hit.contract]),\n );\n const incoming = project.integrations.flatMap((use) => {\n const contract = candidate.get(use.name);\n if (!contract) return [];\n const have = new Set(contract.operations.map((op) => op.id));\n\n return use.operations\n .filter((id) => !have.has(id))\n .map((id) => ({\n check: 'checks/integration-pins',\n severity: 'error' as const,\n message: `integration \"${use.name}\": ${use.name}@${use.version} (contract ${contract.contractVersion}) no longer provides ${shortOperation(id)}.`,\n hint: 'Remove the operation from \"integrations\", or keep the older pinned contract.',\n file: 'ekanos.json',\n }));\n });\n const failed = failOnErrors(ctx, 'sync', incoming, { drift }, project.dir);\n if (failed !== null) return failed;\n\n let changed = false;\n if (project.deprecatedUses) {\n changed = writeIntegrations(project, project.integrations) || changed;\n }\n for (const use of project.integrations) {\n const hit = resolved.get(use.name);\n if (!hit) continue;\n const result = pinIntegration(project, use, hit.contract);\n changed = changed || result.snapshotChanged || result.lockChanged;\n }\n const { pinned } = loadPins(project);\n const typesChanged = regenerateIntegrations(project, pinned);\n changed = changed || typesChanged;\n\n if (snapshotOnly && drift.length === 0) ctx.warn(snapshotNotice);\n\n return ctx.succeed(\n {\n drift,\n changed,\n snapshotOnly,\n explicitSource: explicit,\n migratedUses: project.deprecatedUses,\n renames: project.renames,\n integrations: project.integrations.map((u) => ({\n integration: u.name,\n contractVersion: resolved.get(u.name)?.contract.contractVersion,\n digest: resolved.get(u.name)?.contract.digest,\n origin: resolved.get(u.name)?.origin,\n })),\n },\n drift.length > 0\n ? `Synced ${project.integrations.length} integration${project.integrations.length === 1 ? '' : 's'}; accepted: ${drift.join('; ')}.`\n : changed\n ? 'Synced; lock or types were regenerated.'\n : snapshotOnly\n ? 'Nothing to sync against the committed snapshots; no other contract source was consulted.'\n : 'Nothing to sync: every integration matches its pinned contract.',\n );\n },\n};\n\n/**\n * The harness's registry source falls back to the committed snapshot when it\n * finds nothing newer, and says so in its origin (\"snapshot <dir>\"). A hit that\n * is only that fallback proves nothing about the registry.\n */\nfunction isSnapshotOnly(hit: ResolvedContract): boolean {\n return hit.kind === 'snapshot' || hit.origin.startsWith('snapshot ');\n}\n"]}
@@ -1,5 +1,17 @@
1
1
  import type { AgentRunSummary, ChannelRequirement, Requirement, TeamAgentEntry, TeamAgentHold, TeamIntegrationEvent } from '@ekanos/integration-schema';
2
2
  import { type ScopeFacts } from './scope-notice.js';
3
+ /**
4
+ * "Is this agent live on this team, and if not, why?" — one answer for
5
+ * `teams agents show` and the summary line of `teams agents list`, built only
6
+ * from what the host already reports (CLI audit 2026-10-19, §3).
7
+ *
8
+ * Live is: on, on a published version, every connection it needs made,
9
+ * access confirmed by someone who sets it up (a team manager or the Source
10
+ * admin, plan 247.2), at least one trigger on, and every trigger's events
11
+ * able to reach it. Each gap is a step with one fix, in the order they have
12
+ * to be done; the first step's fix is "Next". A publish holds no team, so
13
+ * there is no update waiting.
14
+ */
3
15
  export interface ReadinessStep {
4
16
  /** What is missing, in words. */
5
17
  problem: string;
@@ -26,11 +38,15 @@ export declare function describeHold(hold: TeamAgentHold): string;
26
38
  export declare const REASON_WORDS: Record<NonNullable<Requirement['reason']>, string>;
27
39
  /** The one command that makes a requirement, or null when none can. */
28
40
  export declare function requirementFix(item: Requirement, team: string, source: string): string | null;
29
- /** The one command that makes a channel ready, or the app step. */
41
+ /**
42
+ * The one command that makes a channel ready. An older host sends no
43
+ * `arrival`: the channel's key (`<product>:webhook`, `<product>:route`,
44
+ * `<product>:<connection>`) still says which command it is.
45
+ */
30
46
  export declare function channelFix(channel: ChannelRequirement, team: string, source: string): string;
31
47
  /** How the events of a ready channel reach the agent. */
32
48
  export declare function reachedVia(channel: ChannelRequirement): string;
33
- /** A fix as a line: a command is run in the terminal, an app step is not. */
49
+ /** A fix as a line: a command is run in the terminal; anything else is a person's step. */
34
50
  export declare function fixLine(fix: string): string;
35
51
  /** "Live: yes" or "Live: no — N steps left". */
36
52
  export declare function liveLine(readiness: AgentReadiness): string;
@@ -1,17 +1,5 @@
1
+ import { cli } from '../invocation.js';
1
2
  import { scopeFix, scopeHiddenWords } from './scope-notice.js';
2
- /**
3
- * "Is this agent live on this team, and if not, why?" — one answer for
4
- * `teams agents show` and the summary line of `teams agents list`, built only
5
- * from what the host already reports (CLI audit 2026-10-19, §3).
6
- *
7
- * Live is: on, on a published version, every connection it needs made,
8
- * access confirmed by someone who sets it up (a team manager or the Source
9
- * admin, plan 247.2), at least one trigger on, and every trigger's events
10
- * able to reach it. Each gap is a step with one fix, in the order they have
11
- * to be done; the first step's fix is "Next". A publish holds no team, so
12
- * there is no update waiting.
13
- */
14
- const CLI = 'npx @ekanos/cli';
15
3
  export function agentReadiness(entry, target) {
16
4
  var _a;
17
5
  var _b;
@@ -52,7 +40,7 @@ export function describeHold(hold) {
52
40
  }
53
41
  function readinessSteps(entry, { team, source }) {
54
42
  var _a, _b, _c, _d, _e;
55
- const enable = `${CLI} teams agents enable ${team} ${entry.key} --source ${source}`;
43
+ const enable = `${cli()} teams agents enable ${team} ${entry.key} --source ${source}`;
56
44
  const agent = entry.agent;
57
45
  if (!agent)
58
46
  return [{ problem: `off for ${team}`, fix: enable }];
@@ -64,19 +52,19 @@ function readinessSteps(entry, { team, source }) {
64
52
  // A team testing a draft runs it only when started by hand.
65
53
  steps.push({
66
54
  problem: `testing unpublished draft v${(_a = agent.version) !== null && _a !== void 0 ? _a : '?'}: its triggers do not start it`,
67
- fix: `${CLI} agents publish ${entry.key} --source ${source}`,
55
+ fix: `${cli()} agents publish ${entry.key} --source ${source}`,
68
56
  });
69
57
  }
70
58
  else if (agent.version === null) {
71
59
  steps.push({
72
60
  problem: 'no published version',
73
- fix: `${CLI} agents publish ${entry.key} --source ${source}`,
61
+ fix: `${cli()} agents publish ${entry.key} --source ${source}`,
74
62
  });
75
63
  }
76
64
  for (const item of agent.missing) {
77
65
  steps.push({
78
66
  problem: `${item.name}: ${REASON_WORDS[(_b = item.reason) !== null && _b !== void 0 ? _b : 'not_connected']}`,
79
- fix: (_c = requirementFix(item, team, source)) !== null && _c !== void 0 ? _c : `${CLI} teams mcp status ${team} ${item.key} --source ${source}`,
67
+ fix: (_c = requirementFix(item, team, source)) !== null && _c !== void 0 ? _c : `${cli()} teams mcp status ${team} ${item.key} --source ${source}`,
80
68
  });
81
69
  }
82
70
  if (!agent.accessConfirmed) {
@@ -84,13 +72,13 @@ function readinessSteps(entry, { team, source }) {
84
72
  problem: 'nobody has confirmed what it may use',
85
73
  // Whoever sets it up confirms, as themselves: the Source admin here,
86
74
  // or a team manager in the app (plan 247.2).
87
- fix: `${CLI} teams agents confirm ${team} ${entry.key} --source ${source} (or a team manager confirms in the app: Agents › ${entry.name} › Setup)`,
75
+ fix: `${cli()} teams agents confirm ${team} ${entry.key} --source ${source} (or a team manager confirms in the app: Agents › ${entry.name} › Setup)`,
88
76
  });
89
77
  }
90
78
  if (!agent.triggers.some((trigger) => trigger.enabled)) {
91
79
  steps.push({
92
80
  problem: 'no trigger is on, so it only runs when started by hand',
93
- fix: `${CLI} teams triggers add ${team} ${entry.key} event <event-id> --source ${source} (event ids: ${CLI} catalog --source ${source})`,
81
+ fix: `${cli()} teams triggers add ${team} ${entry.key} event <event-id> --source ${source} (event ids: ${cli()} catalog --source ${source})`,
94
82
  });
95
83
  }
96
84
  for (const channel of (_d = agent.channels) !== null && _d !== void 0 ? _d : []) {
@@ -116,38 +104,50 @@ export function requirementFix(item, team, source) {
116
104
  return null;
117
105
  switch (item.reason) {
118
106
  case 'no_route':
119
- return `${CLI} routes set ${item.key} <value> ${team} --source ${source} (values: ${CLI} routes options ${item.key} --source ${source})`;
107
+ return `${cli()} routes set ${item.key} <value> ${team} --source ${source} (values: ${cli()} routes options ${item.key} --source ${source})`;
120
108
  case 'source_not_connected':
121
- return `${CLI} apps connect ${item.key} --source ${source}`;
109
+ return `${cli()} apps connect ${item.key} --source ${source}`;
122
110
  case 'source_not_entitled':
123
- return `a super-admin enables it for the Source (${CLI} sources products --source ${source})`;
111
+ return `a super-admin enables it for the Source (${cli()} sources products --source ${source})`;
124
112
  case 'source_unsupported':
125
- return `set connections.${item.key}.owner to "team" in agent.json, then ${CLI} agents apply`;
113
+ return `set connections.${item.key}.owner to "team" in agent.json, then ${cli()} agents apply`;
126
114
  default:
127
115
  if (item.connected)
128
116
  return null;
129
117
  // A per-person product is the runs-as person's connection, not the
130
118
  // caller's: connecting your own leaves the agent missing it.
131
119
  return item.via === 'person' && item.viaDetail
132
- ? `${CLI} teams connect ${team} ${item.key} --as ${item.viaDetail} --source ${source}`
133
- : `${CLI} teams connect ${team} ${item.key} --source ${source}`;
120
+ ? `${cli()} teams connect ${team} ${item.key} --as ${item.viaDetail} --source ${source}`
121
+ : `${cli()} teams connect ${team} ${item.key} --source ${source}`;
134
122
  }
135
123
  }
136
- /** The one command that makes a channel ready, or the app step. */
124
+ /**
125
+ * The one command that makes a channel ready. An older host sends no
126
+ * `arrival`: the channel's key (`<product>:webhook`, `<product>:route`,
127
+ * `<product>:<connection>`) still says which command it is.
128
+ */
137
129
  export function channelFix(channel, team, source) {
138
- switch (channel.arrival) {
130
+ var _a, _b;
131
+ const suffix = channel.key.includes(':') ? channel.key.split(':')[1] : null;
132
+ const arrival = (_a = channel.arrival) !== null && _a !== void 0 ? _a : (suffix === 'webhook'
133
+ ? 'team-webhook'
134
+ : suffix === 'route'
135
+ ? 'source-route'
136
+ : 'install');
137
+ switch (arrival) {
139
138
  case 'team-webhook':
140
- return `${CLI} teams webhook ${team} ${channel.product} --source ${source}`;
139
+ return `${cli()} teams webhook ${team} ${channel.product} --source ${source}`;
141
140
  case 'source-route':
142
141
  return channel.webhook
143
- ? `${CLI} routes set ${channel.product} <value> ${team} --source ${source} (values: ${CLI} routes options ${channel.product} --source ${source})`
144
- : `${CLI} routes webhook ${channel.product} --source ${source}`;
145
- default:
146
- return channel.connectionId
147
- ? `${CLI} teams connect ${team} ${channel.product} --connection ${channel.connectionId} --source ${source}`
148
- : channel.key === 'slack' || channel.key === 'linear-app'
149
- ? `${CLI} teams connect ${team} ${channel.key} --source ${source}`
150
- : `install it on the agent's Triggers tab in the app`;
142
+ ? `${cli()} routes set ${channel.product} <value> ${team} --source ${source} (values: ${cli()} routes options ${channel.product} --source ${source})`
143
+ : `${cli()} routes webhook ${channel.product} --source ${source}`;
144
+ default: {
145
+ if (channel.key === 'slack' || channel.key === 'linear-app') {
146
+ return `${cli()} teams connect ${team} ${channel.key} --source ${source}`;
147
+ }
148
+ const connection = (_b = channel.connectionId) !== null && _b !== void 0 ? _b : suffix;
149
+ return `${cli()} teams connect ${team} ${channel.product}${connection ? ` --connection ${connection}` : ''} --source ${source}`;
150
+ }
151
151
  }
152
152
  }
153
153
  /** How the events of a ready channel reach the agent. */
@@ -161,9 +161,9 @@ export function reachedVia(channel) {
161
161
  return `${channel.name} installed`;
162
162
  }
163
163
  }
164
- /** A fix as a line: a command is run in the terminal, an app step is not. */
164
+ /** A fix as a line: a command is run in the terminal; anything else is a person's step. */
165
165
  export function fixLine(fix) {
166
- return fix.startsWith(CLI) ? `in your terminal: ${fix}` : fix;
166
+ return fix.startsWith(cli()) ? `in your terminal: ${fix}` : fix;
167
167
  }
168
168
  /** "Live: yes" or "Live: no — N steps left". */
169
169
  export function liveLine(readiness) {
@@ -203,7 +203,7 @@ export function describeAgentReadiness(entry, target, readiness, activity, scope
203
203
  var _a;
204
204
  const fix = item.connected
205
205
  ? null
206
- : ((_a = requirementFix(item, target.team, target.source)) !== null && _a !== void 0 ? _a : `${CLI} teams mcp status ${target.team} ${item.key} --source ${target.source}`);
206
+ : ((_a = requirementFix(item, target.team, target.source)) !== null && _a !== void 0 ? _a : `${cli()} teams mcp status ${target.team} ${item.key} --source ${target.source}`);
207
207
  const hidden = item.hiddenByScope && scope
208
208
  ? [
209
209
  `${indent}! ${scopeHiddenWords(scope)}`,