@ekanos/cli 0.12.0 → 0.14.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 (175) hide show
  1. package/README.md +25 -8
  2. package/dist/agents-api.d.ts +11 -2
  3. package/dist/agents-api.js +39 -3
  4. package/dist/agents-api.js.map +1 -1
  5. package/dist/apps-api.d.ts +3 -2
  6. package/dist/apps-api.js +3 -7
  7. package/dist/apps-api.js.map +1 -1
  8. package/dist/auth/ekanos-api.d.ts +16 -2
  9. package/dist/auth/ekanos-api.js +101 -15
  10. package/dist/auth/ekanos-api.js.map +1 -1
  11. package/dist/auth/host-trust.d.ts +46 -0
  12. package/dist/auth/host-trust.js +79 -0
  13. package/dist/auth/host-trust.js.map +1 -0
  14. package/dist/auth/session.d.ts +10 -1
  15. package/dist/auth/session.js +28 -28
  16. package/dist/auth/session.js.map +1 -1
  17. package/dist/commands/agents-edit.d.ts +6 -2
  18. package/dist/commands/agents-edit.js +57 -26
  19. package/dist/commands/agents-edit.js.map +1 -1
  20. package/dist/commands/agents.d.ts +13 -2
  21. package/dist/commands/agents.js +148 -36
  22. package/dist/commands/agents.js.map +1 -1
  23. package/dist/commands/apps.d.ts +15 -4
  24. package/dist/commands/apps.js +133 -51
  25. package/dist/commands/apps.js.map +1 -1
  26. package/dist/commands/catalog.js +7 -6
  27. package/dist/commands/catalog.js.map +1 -1
  28. package/dist/commands/knowledge.d.ts +56 -0
  29. package/dist/commands/knowledge.js +456 -0
  30. package/dist/commands/knowledge.js.map +1 -0
  31. package/dist/commands/login.d.ts +4 -0
  32. package/dist/commands/login.js +30 -2
  33. package/dist/commands/login.js.map +1 -1
  34. package/dist/commands/logout.d.ts +9 -1
  35. package/dist/commands/logout.js +56 -7
  36. package/dist/commands/logout.js.map +1 -1
  37. package/dist/commands/publish.js +3 -3
  38. package/dist/commands/publish.js.map +1 -1
  39. package/dist/commands/reviewers.d.ts +2 -0
  40. package/dist/commands/reviewers.js +7 -3
  41. package/dist/commands/reviewers.js.map +1 -1
  42. package/dist/commands/routes.d.ts +2 -0
  43. package/dist/commands/routes.js +42 -9
  44. package/dist/commands/routes.js.map +1 -1
  45. package/dist/commands/runs.d.ts +13 -2
  46. package/dist/commands/runs.js +96 -15
  47. package/dist/commands/runs.js.map +1 -1
  48. package/dist/commands/scope-notice.d.ts +21 -0
  49. package/dist/commands/scope-notice.js +35 -0
  50. package/dist/commands/scope-notice.js.map +1 -0
  51. package/dist/commands/skills.d.ts +7 -1
  52. package/dist/commands/skills.js +80 -24
  53. package/dist/commands/skills.js.map +1 -1
  54. package/dist/commands/sources.d.ts +4 -0
  55. package/dist/commands/sources.js +233 -5
  56. package/dist/commands/sources.js.map +1 -1
  57. package/dist/commands/status.js +5 -4
  58. package/dist/commands/status.js.map +1 -1
  59. package/dist/commands/team-agent-readiness.d.ts +59 -0
  60. package/dist/commands/team-agent-readiness.js +341 -0
  61. package/dist/commands/team-agent-readiness.js.map +1 -0
  62. package/dist/commands/team-agents-agents.d.ts +24 -3
  63. package/dist/commands/team-agents-agents.js +336 -35
  64. package/dist/commands/team-agents-agents.js.map +1 -1
  65. package/dist/commands/team-agents-mcp.js +32 -3
  66. package/dist/commands/team-agents-mcp.js.map +1 -1
  67. package/dist/commands/team-agents-triggers.js +230 -54
  68. package/dist/commands/team-agents-triggers.js.map +1 -1
  69. package/dist/commands/team-agents.d.ts +3 -1
  70. package/dist/commands/team-agents.js +27 -7
  71. package/dist/commands/team-agents.js.map +1 -1
  72. package/dist/commands/teams-channel-install.d.ts +24 -0
  73. package/dist/commands/teams-channel-install.js +94 -0
  74. package/dist/commands/teams-channel-install.js.map +1 -0
  75. package/dist/commands/teams-connect.d.ts +66 -0
  76. package/dist/commands/teams-connect.js +397 -0
  77. package/dist/commands/teams-connect.js.map +1 -0
  78. package/dist/commands/teams-events.d.ts +12 -0
  79. package/dist/commands/teams-events.js +138 -0
  80. package/dist/commands/teams-events.js.map +1 -0
  81. package/dist/commands/teams-onboard.d.ts +16 -5
  82. package/dist/commands/teams-onboard.js +61 -36
  83. package/dist/commands/teams-onboard.js.map +1 -1
  84. package/dist/commands/teams-overview.d.ts +11 -0
  85. package/dist/commands/teams-overview.js +160 -0
  86. package/dist/commands/teams-overview.js.map +1 -0
  87. package/dist/commands/teams-scope.d.ts +10 -0
  88. package/dist/commands/teams-scope.js +109 -0
  89. package/dist/commands/teams-scope.js.map +1 -0
  90. package/dist/commands/teams.d.ts +64 -7
  91. package/dist/commands/teams.js +254 -60
  92. package/dist/commands/teams.js.map +1 -1
  93. package/dist/commands/upgrade.js +12 -2
  94. package/dist/commands/upgrade.js.map +1 -1
  95. package/dist/commands/use.js +2 -2
  96. package/dist/commands/use.js.map +1 -1
  97. package/dist/commands/whoami.js +1 -1
  98. package/dist/commands/whoami.js.map +1 -1
  99. package/dist/connections-api.js +1 -1
  100. package/dist/connections-api.js.map +1 -1
  101. package/dist/delegate.js +11 -4
  102. package/dist/delegate.js.map +1 -1
  103. package/dist/harness-scaffold.js +12 -0
  104. package/dist/harness-scaffold.js.map +1 -1
  105. package/dist/index.d.ts +6 -0
  106. package/dist/index.js +724 -152
  107. package/dist/index.js.map +1 -1
  108. package/dist/knowledge-api.d.ts +175 -0
  109. package/dist/knowledge-api.js +169 -0
  110. package/dist/knowledge-api.js.map +1 -0
  111. package/dist/open-url.d.ts +16 -0
  112. package/dist/open-url.js +73 -0
  113. package/dist/open-url.js.map +1 -0
  114. package/dist/parse-argv.d.ts +2 -0
  115. package/dist/parse-argv.js.map +1 -1
  116. package/dist/publish-api.d.ts +2 -1
  117. package/dist/publish-api.js +4 -3
  118. package/dist/publish-api.js.map +1 -1
  119. package/dist/pull-write.d.ts +27 -0
  120. package/dist/pull-write.js +60 -0
  121. package/dist/pull-write.js.map +1 -0
  122. package/dist/routes-api.d.ts +3 -0
  123. package/dist/routes-api.js +9 -4
  124. package/dist/routes-api.js.map +1 -1
  125. package/dist/runs-api.d.ts +7 -1
  126. package/dist/runs-api.js +21 -6
  127. package/dist/runs-api.js.map +1 -1
  128. package/dist/safe-fs.d.ts +2 -0
  129. package/dist/safe-fs.js +12 -6
  130. package/dist/safe-fs.js.map +1 -1
  131. package/dist/secret-file.d.ts +17 -0
  132. package/dist/secret-file.js +69 -0
  133. package/dist/secret-file.js.map +1 -0
  134. package/dist/skills-api.d.ts +10 -2
  135. package/dist/skills-api.js +37 -2
  136. package/dist/skills-api.js.map +1 -1
  137. package/dist/source-products-api.d.ts +63 -0
  138. package/dist/source-products-api.js +123 -0
  139. package/dist/source-products-api.js.map +1 -0
  140. package/dist/source-target.js +2 -1
  141. package/dist/source-target.js.map +1 -1
  142. package/dist/sources-api.d.ts +2 -1
  143. package/dist/sources-api.js +3 -2
  144. package/dist/sources-api.js.map +1 -1
  145. package/dist/team-connect-api.d.ts +286 -0
  146. package/dist/team-connect-api.js +107 -0
  147. package/dist/team-connect-api.js.map +1 -0
  148. package/dist/teams-api.d.ts +42 -1
  149. package/dist/teams-api.js +98 -20
  150. package/dist/teams-api.js.map +1 -1
  151. package/dist/toolchain-api.d.ts +9 -0
  152. package/dist/toolchain-api.js +17 -2
  153. package/dist/toolchain-api.js.map +1 -1
  154. package/dist/toolchain-resolve.d.ts +3 -0
  155. package/dist/toolchain-resolve.js +15 -1
  156. package/dist/toolchain-resolve.js.map +1 -1
  157. package/dist/toolchain.d.ts +6 -0
  158. package/dist/toolchain.js +19 -0
  159. package/dist/toolchain.js.map +1 -1
  160. package/dist/update-notice.js +2 -1
  161. package/dist/update-notice.js.map +1 -1
  162. package/package.json +2 -2
  163. package/templates/AGENTS.md.tmpl +6 -1
  164. package/templates/claude-skill.md.tmpl +5 -0
  165. package/templates/package.json.tmpl +4 -4
  166. package/templates/skills/build-agent/SKILL.md +115 -16
  167. package/templates/skills/build-agent/references/agent-json.md +26 -8
  168. package/templates/skills/build-agent/references/app-setup.md +2 -1
  169. package/templates/skills/build-agent/references/rollout.md +27 -4
  170. package/templates/skills/build-agent/references/set-up-an-integration.md +74 -15
  171. package/templates/skills/build-agent/references/skills.md +20 -1
  172. package/templates/skills/build-agent/references/triggers.md +84 -20
  173. package/templates/skills/build-integration/references/widget-design.md +3 -3
  174. package/templates/skills/ekanos-platform/SKILL.md +70 -17
  175. package/templates/skills/polish-widgets/references/patterns.md +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"reviewers.js","sourceRoot":"","sources":["../../src/commands/reviewers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReviewerReach, SourceReviewer } from '@ekanos/integration-schema';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport { authRequiredError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type ReviewersApiResult,\n addReviewer,\n listReviewers,\n removeReviewer,\n} from '../reviewers-api';\nimport { resolveSourceTarget } from '../source-target';\n\nexport const REVIEWERS_SUBCOMMANDS = ['list', 'add', 'remove'] as const;\ntype ReviewersSubcommand = (typeof REVIEWERS_SUBCOMMANDS)[number];\n\nconst REACHES: readonly ReviewerReach[] = ['source', 'team'];\n\nexport interface ReviewersArgs {\n host?: string;\n source?: string;\n /** `list`, `add <email>`, `remove <email>`. */\n positionals: readonly string[];\n reach?: string;\n team?: string;\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `reviewers` — a Source admin names who decides its agents' reply drafts\n * held under an `approve` policy. A reviewer is one of the Source's own\n * users, at a reach: `source` (drafts from every team's provided agents) or\n * `team` (one named team's). `list` shows them; `add` names one; `remove`\n * takes one off — every reach, or only the named team's with `--team`.\n */\nexport async function runReviewers(\n ctx: CliContext,\n args: ReviewersArgs,\n): Promise<ExitCode> {\n const [rawSubcommand, rawEmail, ...extra] = args.positionals;\n const subcommand = parseSubcommand(rawSubcommand);\n\n if (subcommand === 'list') {\n if (rawEmail !== undefined || args.reach || args.team) {\n throw usageError(\n 'reviewers list takes no arguments.',\n 'Run \"ekanos reviewers list --source <slug>\".',\n );\n }\n } else if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'See \"ekanos reviewers --help\".',\n );\n }\n\n const email = subcommand === 'list' ? '' : requireEmail(rawEmail, subcommand);\n const team = args.team === undefined ? undefined : requireTeam(args.team);\n const reach =\n subcommand === 'add' ? requireReach(args.reach, email, team) : undefined;\n if (subcommand === 'remove' && args.reach) {\n throw usageError(\n 'reviewers remove takes no --reach.',\n `Run \"ekanos reviewers remove ${email}\" (every reach) or add \"--team <slug>\" for one team's.`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(\n env,\n requireSession(env.store, env.host),\n args,\n );\n const client = new ReviewersClient(env, resolved.session);\n const source = resolved.source;\n\n if (subcommand === 'list') {\n const list = await client.call((token) =>\n listReviewers(env.host, token, source),\n );\n if (list.reviewers.length === 0) {\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `No reviewers on ${list.source.slug}`,\n tone: 'warn',\n notes: [\n 'Reply drafts under an \"approve\" policy wait for a named reviewer.',\n 'Name one: \"ekanos reviewers add <email> --reach source\".',\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...list },\n [\n ctx.ui.bold(`Reviewers on ${list.source.slug}`),\n ctx.ui.table(\n ['Email', 'Name', 'Reach', 'Team'],\n list.reviewers.map(reviewerRow),\n ),\n ].join('\\n'),\n );\n }\n\n if (subcommand === 'add' && reach) {\n const result = await client.call((token) =>\n addReviewer(env.host, token, source, {\n email,\n reach,\n ...(team ? { team } : {}),\n }),\n );\n const reviewer = result.reviewer;\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.created\n ? `Added ${who(reviewer)} as a reviewer`\n : `${who(reviewer)} already reviews at that reach`,\n tone: result.created ? 'ok' : 'warn',\n rows: [\n ['Source', source],\n ['Reach', reachLabel(reviewer)],\n ],\n notes: [\n reviewer.reach === 'source'\n ? \"Decides drafts from every team's provided agents, in the Source's review queue.\"\n : `Decides ${reviewer.team?.slug ?? 'the team'}'s drafts, from its Approvals inbox or the Source's review queue.`,\n ],\n }),\n );\n }\n\n const result = await client.call((token) =>\n removeReviewer(env.host, token, source, {\n email,\n ...(team ? { team } : {}),\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: `Removed ${email}${team ? ` from ${team}` : ''}`,\n rows: [\n ['Source', source],\n [\n 'Removed',\n `${result.removed} reach${result.removed === 1 ? '' : 'es'}`,\n ],\n ],\n }),\n );\n}\n\nfunction reviewerRow(reviewer: SourceReviewer): string[] {\n return [\n reviewer.email ?? reviewer.userId,\n reviewer.name ?? '—',\n reviewer.reach,\n reviewer.team?.slug ?? '—',\n ];\n}\n\nfunction reachLabel(reviewer: SourceReviewer): string {\n return reviewer.reach === 'team' && reviewer.team\n ? `team ${reviewer.team.slug} (${reviewer.team.name})`\n : 'source';\n}\n\nfunction who(reviewer: SourceReviewer): string {\n return reviewer.email ?? reviewer.name ?? reviewer.userId;\n}\n\nclass ReviewersClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<ReviewersApiResult<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 reviewers call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n}\n\nfunction parseSubcommand(value: string | undefined): ReviewersSubcommand {\n const match = REVIEWERS_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value\n ? `Unknown reviewers subcommand \"${value}\".`\n : 'reviewers needs a subcommand.',\n 'Run one of: ekanos reviewers list, ekanos reviewers add <email> --reach source|team [--team <slug>], ekanos reviewers remove <email> [--team <slug>].',\n );\n}\n\nfunction requireEmail(value: string | undefined, subcommand: 'add' | 'remove') {\n const email = value?.trim().toLowerCase();\n if (email && /^[^\\s@]+@[^\\s@]+$/.test(email)) return email;\n throw usageError(\n value\n ? `\"${value}\" is not an email address.`\n : 'Name the reviewer by email.',\n subcommand === 'add'\n ? 'e.g. \"ekanos reviewers add ana@example.com --reach source\".'\n : '\"ekanos reviewers list\" shows who reviews.',\n );\n}\n\nfunction requireTeam(value: string): string {\n const slug = value.trim().toLowerCase();\n if (/^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n `\"${value}\" is not a team slug.`,\n 'Run \"ekanos teams list\" to see the teams on this Source.',\n );\n}\n\nfunction requireReach(\n value: string | undefined,\n email: string,\n team: string | undefined,\n): ReviewerReach {\n const reach = REACHES.find((entry) => entry === value?.trim().toLowerCase());\n if (!reach) {\n throw usageError(\n value\n ? `\"${value}\" is not a reach: use source or team.`\n : 'Name the reach: --reach source or --reach team.',\n `e.g. \"ekanos reviewers add ${email} --reach source\", or \"--reach team --team <slug>\" for one team's drafts.`,\n );\n }\n if (reach === 'team' && !team) {\n throw usageError(\n 'A team-reach reviewer needs --team <slug>.',\n 'Run \"ekanos teams list\" to see the teams on this Source.',\n );\n }\n if (reach === 'source' && team) {\n throw usageError(\n 'A source-reach reviewer decides for every team; drop --team.',\n `Run \"ekanos reviewers add ${email} --reach source\", or use \"--reach team --team ${team}\".`,\n );\n }\n return reach;\n}\n"]}
1
+ {"version":3,"file":"reviewers.js","sourceRoot":"","sources":["../../src/commands/reviewers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReviewerReach, SourceReviewer } from '@ekanos/integration-schema';\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 {\n type ReviewersApiResult,\n addReviewer,\n listReviewers,\n removeReviewer,\n} from '../reviewers-api';\nimport { resolveSourceTarget } from '../source-target';\n\nexport const REVIEWERS_SUBCOMMANDS = ['list', 'add', 'remove'] as const;\ntype ReviewersSubcommand = (typeof REVIEWERS_SUBCOMMANDS)[number];\n\nconst REACHES: readonly ReviewerReach[] = ['source', 'team'];\n\nexport interface ReviewersArgs {\n host?: string;\n source?: string;\n /** `list`, `add <email>`, `remove <email>`. */\n positionals: readonly string[];\n reach?: string;\n team?: string;\n /** `remove --yes`: confirm taking the reviewer off. */\n yes?: boolean;\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `reviewers` — a Source admin names who decides its agents' reply drafts\n * held under an `approve` policy. A reviewer is one of the Source's own\n * users, at a reach: `source` (drafts from every team's provided agents) or\n * `team` (one named team's). `list` shows them; `add` names one; `remove`\n * takes one off — every reach, or only the named team's with `--team`.\n */\nexport async function runReviewers(\n ctx: CliContext,\n args: ReviewersArgs,\n): Promise<ExitCode> {\n const [rawSubcommand, rawEmail, ...extra] = args.positionals;\n const subcommand = parseSubcommand(rawSubcommand);\n\n if (subcommand === 'list') {\n if (rawEmail !== undefined || args.reach || args.team) {\n throw usageError(\n 'reviewers list takes no arguments.',\n 'Run \"ekanos reviewers list --source <slug>\".',\n );\n }\n } else if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'See \"ekanos reviewers --help\".',\n );\n }\n\n const email = subcommand === 'list' ? '' : requireEmail(rawEmail, subcommand);\n const team = args.team === undefined ? undefined : requireTeam(args.team);\n const reach =\n subcommand === 'add' ? requireReach(args.reach, email, team) : undefined;\n if (subcommand === 'remove' && args.reach) {\n throw usageError(\n 'reviewers remove takes no --reach.',\n `Run \"ekanos reviewers remove ${email}\" (every reach) or add \"--team <slug>\" for one team's.`,\n );\n }\n if (subcommand === 'remove' && !args.yes) {\n throw invalidStateError(\n `Removing ${email} as a reviewer stops them deciding reply drafts${team ? ` from ${team}'s agents` : ' at every reach'}.`,\n `Re-run with --yes: ekanos reviewers remove ${email}${team ? ` --team ${team}` : ''} --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), args);\n const client = new ReviewersClient(env, resolved.session);\n const source = resolved.source;\n\n if (subcommand === 'list') {\n const list = await client.call((token) =>\n listReviewers(env.host, token, source),\n );\n if (list.reviewers.length === 0) {\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `No reviewers on ${list.source.slug}`,\n tone: 'warn',\n notes: [\n 'Reply drafts under an \"approve\" policy wait for a named reviewer.',\n 'Name one: \"ekanos reviewers add <email> --reach source\".',\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...list },\n [\n ctx.ui.bold(`Reviewers on ${list.source.slug}`),\n ctx.ui.table(\n ['Email', 'Name', 'Reach', 'Team'],\n list.reviewers.map(reviewerRow),\n ),\n ].join('\\n'),\n );\n }\n\n if (subcommand === 'add' && reach) {\n const result = await client.call((token) =>\n addReviewer(env.host, token, source, {\n email,\n reach,\n ...(team ? { team } : {}),\n }),\n );\n const reviewer = result.reviewer;\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.created\n ? `Added ${who(reviewer)} as a reviewer`\n : `${who(reviewer)} already reviews at that reach`,\n tone: result.created ? 'ok' : 'warn',\n rows: [\n ['Source', source],\n ['Reach', reachLabel(reviewer)],\n ],\n notes: [\n reviewer.reach === 'source'\n ? \"Decides drafts from every team's provided agents, in the Source's review queue.\"\n : `Decides ${reviewer.team?.slug ?? 'the team'}'s drafts, from its Approvals inbox or the Source's review queue.`,\n ],\n }),\n );\n }\n\n const result = await client.call((token) =>\n removeReviewer(env.host, token, source, {\n email,\n ...(team ? { team } : {}),\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: `Removed ${email}${team ? ` from ${team}` : ''}`,\n rows: [\n ['Source', source],\n [\n 'Removed',\n `${result.removed} reach${result.removed === 1 ? '' : 'es'}`,\n ],\n ],\n }),\n );\n}\n\nfunction reviewerRow(reviewer: SourceReviewer): string[] {\n return [\n reviewer.email ?? reviewer.userId,\n reviewer.name ?? '—',\n reviewer.reach,\n reviewer.team?.slug ?? '—',\n ];\n}\n\nfunction reachLabel(reviewer: SourceReviewer): string {\n return reviewer.reach === 'team' && reviewer.team\n ? `team ${reviewer.team.slug} (${reviewer.team.name})`\n : 'source';\n}\n\nfunction who(reviewer: SourceReviewer): string {\n return reviewer.email ?? reviewer.name ?? reviewer.userId;\n}\n\nclass ReviewersClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<ReviewersApiResult<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 reviewers call.`,\n loginHint(this.env),\n );\n }\n}\n\nfunction parseSubcommand(value: string | undefined): ReviewersSubcommand {\n const match = REVIEWERS_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value\n ? `Unknown reviewers subcommand \"${value}\".`\n : 'reviewers needs a subcommand.',\n 'Run one of: ekanos reviewers list, ekanos reviewers add <email> --reach source|team [--team <slug>], ekanos reviewers remove <email> [--team <slug>].',\n );\n}\n\nfunction requireEmail(value: string | undefined, subcommand: 'add' | 'remove') {\n const email = value?.trim().toLowerCase();\n if (email && /^[^\\s@]+@[^\\s@]+$/.test(email)) return email;\n throw usageError(\n value\n ? `\"${value}\" is not an email address.`\n : 'Name the reviewer by email.',\n subcommand === 'add'\n ? 'e.g. \"ekanos reviewers add ana@example.com --reach source\".'\n : '\"ekanos reviewers list\" shows who reviews.',\n );\n}\n\nfunction requireTeam(value: string): string {\n const slug = value.trim().toLowerCase();\n if (/^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n `\"${value}\" is not a team slug.`,\n 'Run \"ekanos teams list\" to see the teams on this Source.',\n );\n}\n\nfunction requireReach(\n value: string | undefined,\n email: string,\n team: string | undefined,\n): ReviewerReach {\n const reach = REACHES.find((entry) => entry === value?.trim().toLowerCase());\n if (!reach) {\n throw usageError(\n value\n ? `\"${value}\" is not a reach: use source or team.`\n : 'Name the reach: --reach source or --reach team.',\n `e.g. \"ekanos reviewers add ${email} --reach source\", or \"--reach team --team <slug>\" for one team's drafts.`,\n );\n }\n if (reach === 'team' && !team) {\n throw usageError(\n 'A team-reach reviewer needs --team <slug>.',\n 'Run \"ekanos teams list\" to see the teams on this Source.',\n );\n }\n if (reach === 'source' && team) {\n throw usageError(\n 'A source-reach reviewer decides for every team; drop --team.',\n `Run \"ekanos reviewers add ${email} --reach source\", or use \"--reach team --team ${team}\".`,\n );\n }\n return reach;\n}\n"]}
@@ -8,6 +8,8 @@ export interface RoutesArgs {
8
8
  positionals: readonly string[];
9
9
  label?: string;
10
10
  webhook?: string;
11
+ /** `webhook --yes`: replace the live URL (revoking it). */
12
+ yes?: boolean;
11
13
  dir: string;
12
14
  env: Record<string, string | undefined>;
13
15
  }
@@ -1,6 +1,19 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { loginHint } from '../auth/host-trust.js';
1
13
  import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
2
- import { authRequiredError, usageError } from '../errors.js';
14
+ import { authRequiredError, invalidStateError, usageError } from '../errors.js';
3
15
  import { ReplaySchema, RouteListSchema, RouteOptionsSchema, RouteRemoveSchema, RouteSetSchema, RouteWebhookSchema, UnroutedListSchema, routesCall, } from '../routes-api.js';
16
+ import { ensureSecretsDir, writeSecretFile } from '../secret-file.js';
4
17
  import { resolveSourceTarget } from '../source-target.js';
5
18
  export const ROUTES_SUBCOMMANDS = [
6
19
  'list',
@@ -11,7 +24,7 @@ export const ROUTES_SUBCOMMANDS = [
11
24
  'unrouted',
12
25
  'replay',
13
26
  ];
14
- const USAGE = 'Run one of: ekanos routes list [<product>] | options <product> | set <product> <value> <team> [--label <name>] | remove <product> <value> | webhook <product> [--webhook <id>] | unrouted [<product>] | replay <event-id>.';
27
+ const USAGE = 'Run one of: ekanos routes list [<product>] | options <product> | set <product> <value> <team> [--label <name>] | remove <product> <value> --yes | webhook <product> [--webhook <id>] [--yes] | unrouted [<product>] | replay <event-id>.';
15
28
  /**
16
29
  * `routes` — a Source admin's routing of a Source connection's events to
17
30
  * teams (plan 241.4). One Zendesk connected for the whole Source sends every
@@ -32,7 +45,7 @@ export async function runRoutes(ctx, args) {
32
45
  const env = resolveAuthEnvironment(ctx, args.host, args.env, {
33
46
  projectDir: args.dir,
34
47
  });
35
- const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
48
+ const resolved = await resolveSourceTarget(env, requireSession(env), args);
36
49
  const client = new RoutesClient(env, resolved.session);
37
50
  const source = resolved.source;
38
51
  const send = (schema, init) => client.call((token) => routesCall(env.host, token, schema, Object.assign(Object.assign({}, init), { query: Object.assign({ source }, init.query) })));
@@ -107,6 +120,9 @@ export async function runRoutes(ctx, args) {
107
120
  case 'remove': {
108
121
  const product = requireSlug(operands[0]);
109
122
  const value = requireValue(operands[1]);
123
+ if (!args.yes) {
124
+ throw invalidStateError(`Unmapping ${product} ${value} stops its events reaching that team's agents: they stay unrouted and start nothing until it is mapped again.`, `Re-run with --yes: ekanos routes remove ${product} ${value} --yes`);
125
+ }
110
126
  const result = await send(RouteRemoveSchema, {
111
127
  method: 'DELETE',
112
128
  path: `/${encodeURIComponent(product)}`,
@@ -120,19 +136,36 @@ export async function runRoutes(ctx, args) {
120
136
  }
121
137
  case 'webhook': {
122
138
  const product = requireSlug(operands[0]);
139
+ const command = `ekanos routes webhook ${product}${args.webhook ? ` --webhook ${args.webhook}` : ''}`;
140
+ // The URL carries its token, so it goes to a 0600 file, never the output.
141
+ const secrets = ensureSecretsDir(args.env);
123
142
  const result = await send(RouteWebhookSchema, {
124
143
  method: 'POST',
125
144
  path: `/${encodeURIComponent(product)}/webhook`,
126
- query: { webhook: args.webhook },
145
+ query: Object.assign({ webhook: args.webhook }, (args.yes ? { replace: 'true' } : {})),
146
+ confirmHint: `Re-run with --yes when you are ready to update the vendor: ${command} --yes`,
147
+ });
148
+ const { url } = result, rest = __rest(result, ["url"]);
149
+ const secretFile = writeSecretFile(secrets, {
150
+ owner: source,
151
+ what: `${product}-${result.webhookId}-webhook-url`,
152
+ purpose: `Webhook URL for ${product} (${result.webhookId}) on source ${source}, once for the whole Source: point the vendor at it, and routed teams get their events. The token is part of the URL.`,
153
+ values: [['WEBHOOK_URL', url]],
154
+ again: `${command} --source ${source} --yes`,
127
155
  });
128
- return ctx.succeed(Object.assign({ host: env.host, product }, result), ctx.ui.result({
156
+ return ctx.succeed(Object.assign(Object.assign({ host: env.host, product }, rest), { secretFile }), ctx.ui.result({
129
157
  title: `Webhook URL for ${product} on ${source} (shown once)`,
130
158
  rows: [
131
159
  ['Webhook', result.webhookId],
132
- ['URL', result.url],
160
+ ['URL', `saved to ${secretFile} (readable only by you)`],
133
161
  ],
134
162
  notes: [
135
- 'Point the vendor at this URL once, for the whole Source. Minting again revokes the previous URL.',
163
+ 'The URL carries its token, so it is shown nowhere else and cannot be read again. Copy it from that file into the vendor yourself — not into a chat or an agent session — then delete the file. Once for the whole Source.',
164
+ ...(result.replaced
165
+ ? [
166
+ 'The previous URL is revoked: the vendor must use this one, or no routed team gets events.',
167
+ ]
168
+ : []),
136
169
  ...(result.signature
137
170
  ? [
138
171
  `Deliveries must be signed (${result.signature.header}); the Source connection needs the "${result.signature.secretName}" secret.`,
@@ -201,7 +234,7 @@ const OPERANDS = {
201
234
  min: 2,
202
235
  max: 2,
203
236
  usage: '<product> <value>',
204
- example: 'zendesk 360001234567',
237
+ example: 'zendesk 360001234567 --yes',
205
238
  },
206
239
  webhook: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },
207
240
  unrouted: {
@@ -240,7 +273,7 @@ class RoutesClient {
240
273
  }
241
274
  if (result.status === 'ok')
242
275
  return result.data;
243
- throw authRequiredError(`The session for ${this.env.host} could not authorize the routes call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
276
+ throw authRequiredError(`The session for ${this.env.host} could not authorize the routes call.`, loginHint(this.env));
244
277
  }
245
278
  }
246
279
  function parseSubcommand(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/commands/routes.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,oBAAoB,EACpB,cAAc,EACd,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE1D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAGlB,kBAAkB,EAClB,UAAU,GACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,MAAM;IACN,SAAS;IACT,KAAK;IACL,QAAQ;IACR,SAAS;IACT,UAAU;IACV,QAAQ;CACA,CAAC;AAcX,MAAM,KAAK,GACT,4NAA4N,CAAC;AAE/N;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAe,EACf,IAAgB;;IAEhB,MAAM,CAAC,aAAa,EAAE,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;IACtD,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtC,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC;QACrE,MAAM,UAAU,CACd,UAAU,UAAU,UAAU,QAAQ,CAAC,KAAK,GAAG,EAC/C,uBAAuB,UAAU,IAAI,QAAQ,CAAC,OAAO,IAAI,CAC1D,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;QAC3D,UAAU,EAAE,IAAI,CAAC,GAAG;KACrB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CACxC,GAAG,EACH,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EACnC,IAAI,CACL,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC/B,MAAM,IAAI,GAAG,CACX,MAAoB,EACpB,IAAsC,EACtC,EAAE,CACF,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACpB,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,kCAC7B,IAAI,KACP,KAAK,kBAAI,MAAM,IAAK,IAAI,CAAC,KAAK,KAC9B,CACH,CAAC;IAEJ,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE;gBACvC,MAAM,EAAE,KAAK;gBACb,KAAK,EAAE,EAAE,OAAO,EAAE;aACnB,CAAC,CAAC;YACH,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7B,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,IAAK,IAAI,GACzB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;oBACZ,KAAK,EAAE,gBAAgB,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;oBAC5E,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE;wBACL,6GAA6G;wBAC7G,wDAAwD;qBACzD;iBACF,CAAC,CACH,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,IAAK,IAAI,GACzB;gBACE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,GAAG,CAAC,EAAE,CAAC,KAAK,CACV,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAC5C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAC1B;aACF,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACJ,CAAC;QAED,KAAK,SAAS,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE;gBAC5C,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI,kBAAkB,CAAC,OAAO,CAAC,UAAU;gBAC/C,KAAK,EAAE,EAAE;aACV,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,IAAK,MAAM,GACpC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;oBACZ,KAAK,EAAE,GAAG,OAAO,sBAAsB,MAAM,CAAC,GAAG,SAAS;oBAC1D,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE;wBACL,YAAY,MAAM,CAAC,GAAG,2BAA2B,OAAO,mBAAmB;qBAC5E;iBACF,CAAC,CACH,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,IAAK,MAAM,GACpC;gBACE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,OAAO,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC;gBAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,CACV,CAAC,OAAO,EAAE,OAAO,CAAC,EAClB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAC7D;aACF,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACJ,CAAC;QAED,KAAK,KAAK,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE;gBACxC,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE;gBACvC,KAAK,EAAE,EAAE;gBACT,IAAI,kBAAI,KAAK,EAAE,IAAI,IAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAE;aACpE,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,IAAK,MAAM,GAC3B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;gBACZ,KAAK,EAAE,UAAU,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,MAAA,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,mCAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;gBAC9G,IAAI,EAAE;oBACJ,CAAC,QAAQ,EAAE,MAAM,CAAC;oBAClB,CAAC,OAAO,QAAE,MAAM,CAAC,KAAK,CAAC,KAAK,mCAAI,GAAG,CAAC;oBACpC;wBACE,UAAU;wBACV,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,IAAI;qBACzD;iBACF;gBACD,KAAK,EAAE;oBACL,+IAA+I;iBAChJ;aACF,CAAC,CACH,CAAC;QACJ,CAAC;QAED,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE;gBAC3C,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE;gBACvC,KAAK,EAAE,EAAE,KAAK,EAAE;aACjB,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,IAAK,MAAM,GAC3B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;gBACZ,KAAK,EAAE,YAAY,OAAO,IAAI,KAAK,EAAE;gBACrC,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC1B,KAAK,EAAE,CAAC,iDAAiD,CAAC;aAC3D,CAAC,CACH,CAAC;QACJ,CAAC;QAED,KAAK,SAAS,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE;gBAC5C,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,IAAI,kBAAkB,CAAC,OAAO,CAAC,UAAU;gBAC/C,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;aACjC,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,IAAK,MAAM,GACpC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;gBACZ,KAAK,EAAE,mBAAmB,OAAO,OAAO,MAAM,eAAe;gBAC7D,IAAI,EAAE;oBACJ,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;oBAC7B,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;iBACpB;gBACD,KAAK,EAAE;oBACL,kGAAkG;oBAClG,GAAG,CAAC,MAAM,CAAC,SAAS;wBAClB,CAAC,CAAC;4BACE,8BAA8B,MAAM,CAAC,SAAS,CAAC,MAAM,uCAAuC,MAAM,CAAC,SAAS,CAAC,UAAU,WAAW;yBACnI;wBACH,CAAC,CAAC,EAAE,CAAC;iBACR;aACF,CAAC,CACH,CAAC;QACJ,CAAC;QAED,KAAK,UAAU,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE;gBAC5C,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,WAAW;gBACjB,KAAK,EAAE,EAAE,OAAO,EAAE;aACnB,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,IAAK,MAAM,GAC3B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAC3D,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,IAAK,MAAM,GAC3B;gBACE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,mDAAmD,CAAC;gBAChE,GAAG,CAAC,EAAE,CAAC,KAAK,CACV,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAC/C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,kBAAC;oBAC3B,KAAK,CAAC,EAAE;oBACR,KAAK,CAAC,OAAO;oBACb,KAAK,CAAC,OAAO;oBACb,KAAK,CAAC,UAAU;wBACd,CAAC,CAAC,GAAG,MAAA,KAAK,CAAC,QAAQ,mCAAI,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE;wBACtD,CAAC,CAAC,QAAQ;oBACZ,KAAK,CAAC,SAAS;iBAChB,GAAA,CAAC,CACH;gBACD,2EAA2E;aAC5E,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACJ,CAAC;QAED,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;gBACtC,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,aAAa,kBAAkB,CAAC,EAAE,CAAC,SAAS;gBAClD,KAAK,EAAE,EAAE;aACV,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,OAAO,iBACd,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,IAAK,MAAM,GAC/B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;gBACZ,KAAK,EACH,MAAM,CAAC,MAAM,KAAK,QAAQ;oBACxB,CAAC,CAAC,UAAU,EAAE,EAAE;oBAChB,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,gBAAgB;wBAClC,CAAC,CAAC,GAAG,EAAE,qBAAqB;wBAC5B,CAAC,CAAC,GAAG,EAAE,oBAAoB;gBACjC,IAAI,EAAE,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;gBAClD,KAAK,EACH,MAAM,CAAC,MAAM,KAAK,UAAU;oBAC1B,CAAC,CAAC;wBACE,gEAAgE;qBACjE;oBACH,CAAC,CAAC,EAAE;aACT,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,QAAQ,GAGV;IACF,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE;IAC1E,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE;IACnE,GAAG,EAAE;QACH,GAAG,EAAE,CAAC;QACN,GAAG,EAAE,CAAC;QACN,KAAK,EAAE,0BAA0B;QACjC,OAAO,EAAE,iCAAiC;KAC3C;IACD,MAAM,EAAE;QACN,GAAG,EAAE,CAAC;QACN,GAAG,EAAE,CAAC;QACN,KAAK,EAAE,mBAAmB;QAC1B,OAAO,EAAE,sBAAsB;KAChC;IACD,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE;IACnE,QAAQ,EAAE;QACR,GAAG,EAAE,CAAC;QACN,GAAG,EAAE,CAAC;QACN,KAAK,EAAE,qBAAqB;QAC5B,OAAO,EAAE,SAAS;KACnB;IACD,MAAM,EAAE;QACN,GAAG,EAAE,CAAC;QACN,GAAG,EAAE,CAAC;QACN,KAAK,EAAE,2CAA2C;QAClD,OAAO,EAAE,YAAY;KACtB;CACF,CAAC;AAEF,SAAS,QAAQ,CAAC,KAAuB;;IACvC,OAAO;QACL,KAAK,CAAC,OAAO;QACb,KAAK,CAAC,GAAG;QACT,KAAK,CAAC,KAAK;cACX,KAAK,CAAC,KAAK,mCAAI,GAAG;cAClB,KAAK,CAAC,IAAI,CAAC,IAAI,mCAAI,KAAK,CAAC,IAAI,CAAC,IAAI;KACnC,CAAC;AACJ,CAAC;AAED,MAAM,YAAY;IAChB,YACmB,GAAoB,EAC7B,OAAsB;mBADb,GAAG;uBACZ,OAAO;IACd,CAAC;IAEJ,KAAK,CAAC,IAAI,CACR,IAA0D;QAE1D,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAClD,IAAI,MAAM,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QAC/C,MAAM,iBAAiB,CACrB,mBAAmB,IAAI,CAAC,GAAG,CAAC,IAAI,uCAAuC,EACvE,4BAA4B,IAAI,CAAC,GAAG,CAAC,IAAI,cAAc,CACxD,CAAC;IACJ,CAAC;CACF;AAED,SAAS,eAAe,CAAC,KAAyB;IAChD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;IAChE,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,UAAU,CACd,KAAK;QACH,CAAC,CAAC,8BAA8B,KAAK,IAAI;QACzC,CAAC,CAAC,4BAA4B,EAChC,KAAK,CACN,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAyB,EAAE,IAAI,GAAG,SAAS;;IAC9D,MAAM,IAAI,SAAG,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,GAAG,WAAW,EAAE,mCAAI,EAAE,CAAC;IAC/C,IAAI,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxD,MAAM,UAAU,CACd,IAAI,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,cAAc,IAAI,QAAQ,EACzC,IAAI,KAAK,MAAM;QACb,CAAC,CAAC,0DAA0D;QAC5D,CAAC,CAAC,kDAAkD,CACvD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB;;IAC7C,MAAM,OAAO,SAAG,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,EAAE,mCAAI,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,OAAO,CAAC;IAChE,MAAM,UAAU,CACd,uCAAuC,EACvC,2EAA2E,CAC5E,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;;IAC/C,MAAM,EAAE,SAAG,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,EAAE,mCAAI,EAAE,CAAC;IAC/B,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3C,MAAM,UAAU,CACd,IAAI,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,uBAAuB,EACtC,kEAAkE,CACnE,CAAC;AACJ,CAAC","sourcesContent":["import type { z } from 'zod';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport { authRequiredError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n ReplaySchema,\n RouteListSchema,\n RouteOptionsSchema,\n RouteRemoveSchema,\n RouteSetSchema,\n RouteWebhookSchema,\n type RoutesApiResult,\n type SourceEventRoute,\n UnroutedListSchema,\n routesCall,\n} from '../routes-api';\nimport { resolveSourceTarget } from '../source-target';\n\nexport const ROUTES_SUBCOMMANDS = [\n 'list',\n 'options',\n 'set',\n 'remove',\n 'webhook',\n 'unrouted',\n 'replay',\n] as const;\ntype RoutesSubcommand = (typeof ROUTES_SUBCOMMANDS)[number];\n\nexport interface RoutesArgs {\n host?: string;\n source?: string;\n /** `list [product]`, `set <product> <value> <team>`, `replay <event-id>`, … */\n positionals: readonly string[];\n label?: string;\n webhook?: string;\n dir: string;\n env: Record<string, string | undefined>;\n}\n\nconst USAGE =\n 'Run one of: ekanos routes list [<product>] | options <product> | set <product> <value> <team> [--label <name>] | remove <product> <value> | webhook <product> [--webhook <id>] | unrouted [<product>] | replay <event-id>.';\n\n/**\n * `routes` — a Source admin's routing of a Source connection's events to\n * teams (plan 241.4). One Zendesk connected for the whole Source sends every\n * ticket to one webhook; each ticket's organization decides which team's\n * agents see it. `set` maps a value to a team, `remove` unmaps it, `options`\n * lists the values the integration knows (Zendesk's organizations),\n * `webhook` mints the Source's endpoint URL (shown once), and `unrouted` /\n * `replay` deal with events no team was mapped to — they started nothing.\n */\nexport async function runRoutes(\n ctx: CliContext,\n args: RoutesArgs,\n): Promise<ExitCode> {\n const [rawSubcommand, ...operands] = args.positionals;\n const subcommand = parseSubcommand(rawSubcommand);\n const expected = OPERANDS[subcommand];\n if (operands.length < expected.min || operands.length > expected.max) {\n throw usageError(\n `routes ${subcommand} takes ${expected.usage}.`,\n `e.g. \"ekanos routes ${subcommand} ${expected.example}\".`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(\n env,\n requireSession(env.store, env.host),\n args,\n );\n const client = new RoutesClient(env, resolved.session);\n const source = resolved.source;\n const send = <T>(\n schema: z.ZodType<T>,\n init: Parameters<typeof routesCall>[3],\n ) =>\n client.call((token) =>\n routesCall(env.host, token, schema, {\n ...init,\n query: { source, ...init.query },\n }),\n );\n\n switch (subcommand) {\n case 'list': {\n const product = operands[0] ? requireSlug(operands[0]) : undefined;\n const list = await send(RouteListSchema, {\n method: 'GET',\n query: { product },\n });\n if (list.routes.length === 0) {\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `No routes on ${list.source.slug}${product ? ` for ${product}` : ''}`,\n tone: 'warn',\n notes: [\n \"Events on the Source's endpoint stay unrouted — they start nothing — until their value is mapped to a team.\",\n 'Map one: \"ekanos routes set <product> <value> <team>\".',\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...list },\n [\n ctx.ui.bold(`Routes on ${list.source.slug}`),\n ctx.ui.table(\n ['Product', 'Key', 'Value', 'Label', 'Team'],\n list.routes.map(routeRow),\n ),\n ].join('\\n'),\n );\n }\n\n case 'options': {\n const product = requireSlug(operands[0]);\n const result = await send(RouteOptionsSchema, {\n method: 'GET',\n path: `/${encodeURIComponent(product)}/options`,\n query: {},\n });\n if (!result.options) {\n return ctx.succeed(\n { host: env.host, product, ...result },\n ctx.ui.result({\n title: `${product} does not list its ${result.key} values`,\n tone: 'warn',\n notes: [\n `Type the ${result.key} id: \"ekanos routes set ${product} <value> <team>\".`,\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, product, ...result },\n [\n ctx.ui.bold(`${product} ${result.key} values`),\n ctx.ui.table(\n ['Value', 'Label'],\n result.options.map((option) => [option.value, option.label]),\n ),\n ].join('\\n'),\n );\n }\n\n case 'set': {\n const product = requireSlug(operands[0]);\n const value = requireValue(operands[1]);\n const team = requireSlug(operands[2], 'team');\n const result = await send(RouteSetSchema, {\n method: 'PUT',\n path: `/${encodeURIComponent(product)}`,\n query: {},\n body: { value, team, ...(args.label ? { label: args.label } : {}) },\n });\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: `Routed ${product} ${result.route.key} ${value} to ${result.route.team.slug ?? result.route.team.name}`,\n rows: [\n ['Source', source],\n ['Label', result.route.label ?? '—'],\n [\n 'Replaced',\n result.replaced ? 'yes (moved from another team)' : 'no',\n ],\n ],\n notes: [\n 'New events with this value start that team\\'s agents. Earlier unrouted ones: \"ekanos routes unrouted\" then \"ekanos routes replay <event-id>\".',\n ],\n }),\n );\n }\n\n case 'remove': {\n const product = requireSlug(operands[0]);\n const value = requireValue(operands[1]);\n const result = await send(RouteRemoveSchema, {\n method: 'DELETE',\n path: `/${encodeURIComponent(product)}`,\n query: { value },\n });\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: `Unmapped ${product} ${value}`,\n rows: [['Source', source]],\n notes: ['Its events now stay unrouted and start nothing.'],\n }),\n );\n }\n\n case 'webhook': {\n const product = requireSlug(operands[0]);\n const result = await send(RouteWebhookSchema, {\n method: 'POST',\n path: `/${encodeURIComponent(product)}/webhook`,\n query: { webhook: args.webhook },\n });\n return ctx.succeed(\n { host: env.host, product, ...result },\n ctx.ui.result({\n title: `Webhook URL for ${product} on ${source} (shown once)`,\n rows: [\n ['Webhook', result.webhookId],\n ['URL', result.url],\n ],\n notes: [\n 'Point the vendor at this URL once, for the whole Source. Minting again revokes the previous URL.',\n ...(result.signature\n ? [\n `Deliveries must be signed (${result.signature.header}); the Source connection needs the \"${result.signature.secretName}\" secret.`,\n ]\n : []),\n ],\n }),\n );\n }\n\n case 'unrouted': {\n const product = operands[0] ? requireSlug(operands[0]) : undefined;\n const result = await send(UnroutedListSchema, {\n method: 'GET',\n path: '/unrouted',\n query: { product },\n });\n if (result.events.length === 0) {\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({ title: 'No unrouted events', tone: 'ok' }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...result },\n [\n ctx.ui.bold('Unrouted events (no team mapped; nothing started)'),\n ctx.ui.table(\n ['Id', 'Product', 'Event', 'Value', 'Received'],\n result.events.map((event) => [\n event.id,\n event.product,\n event.eventId,\n event.routeValue\n ? `${event.routeKey ?? ''} ${event.routeValue}`.trim()\n : '(none)',\n event.createdAt,\n ]),\n ),\n 'Map the value with \"ekanos routes set\", then \"ekanos routes replay <id>\".',\n ].join('\\n'),\n );\n }\n\n case 'replay': {\n const id = requireEventId(operands[0]);\n const result = await send(ReplaySchema, {\n method: 'POST',\n path: `/unrouted/${encodeURIComponent(id)}/replay`,\n query: {},\n });\n return ctx.succeed(\n { host: env.host, id, ...result },\n ctx.ui.result({\n title:\n result.status === 'routed'\n ? `Routed ${id}`\n : result.status === 'already-routed'\n ? `${id} was already routed`\n : `${id} is still unrouted`,\n tone: result.status === 'unrouted' ? 'warn' : 'ok',\n notes:\n result.status === 'unrouted'\n ? [\n 'No team is mapped to its value yet: \"ekanos routes set\" first.',\n ]\n : [],\n }),\n );\n }\n }\n}\n\nconst OPERANDS: Record<\n RoutesSubcommand,\n { min: number; max: number; usage: string; example: string }\n> = {\n list: { min: 0, max: 1, usage: 'an optional product', example: 'zendesk' },\n options: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },\n set: {\n min: 3,\n max: 3,\n usage: '<product> <value> <team>',\n example: 'zendesk 360001234567 first-bank',\n },\n remove: {\n min: 2,\n max: 2,\n usage: '<product> <value>',\n example: 'zendesk 360001234567',\n },\n webhook: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },\n unrouted: {\n min: 0,\n max: 1,\n usage: 'an optional product',\n example: 'zendesk',\n },\n replay: {\n min: 1,\n max: 1,\n usage: 'an event id from \"ekanos routes unrouted\"',\n example: '<event-id>',\n },\n};\n\nfunction routeRow(route: SourceEventRoute): string[] {\n return [\n route.product,\n route.key,\n route.value,\n route.label ?? '—',\n route.team.slug ?? route.team.name,\n ];\n}\n\nclass RoutesClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<RoutesApiResult<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 routes call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n}\n\nfunction parseSubcommand(value: string | undefined): RoutesSubcommand {\n const match = ROUTES_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value\n ? `Unknown routes subcommand \"${value}\".`\n : 'routes needs a subcommand.',\n USAGE,\n );\n}\n\nfunction requireSlug(value: string | undefined, what = 'product'): string {\n const slug = value?.trim().toLowerCase() ?? '';\n if (/^[a-z0-9][a-z0-9-]{0,63}$/.test(slug)) return slug;\n throw usageError(\n `\"${value ?? ''}\" is not a ${what} slug.`,\n what === 'team'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : 'Run \"ekanos catalog\" to see what the Source has.',\n );\n}\n\nfunction requireValue(value: string | undefined): string {\n const trimmed = value?.trim() ?? '';\n if (trimmed.length > 0 && trimmed.length <= 200) return trimmed;\n throw usageError(\n 'A route value is 1 to 200 characters.',\n '\"ekanos routes options <product>\" lists the values the integration knows.',\n );\n}\n\nfunction requireEventId(value: string | undefined): string {\n const id = value?.trim() ?? '';\n if (/^[0-9a-f-]{36}$/i.test(id)) return id;\n throw usageError(\n `\"${value ?? ''}\" is not an event id.`,\n '\"ekanos routes unrouted\" lists the events waiting for a mapping.',\n );\n}\n"]}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/commands/routes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { z } from 'zod';\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 {\n ReplaySchema,\n RouteListSchema,\n RouteOptionsSchema,\n RouteRemoveSchema,\n RouteSetSchema,\n RouteWebhookSchema,\n type RoutesApiResult,\n type SourceEventRoute,\n UnroutedListSchema,\n routesCall,\n} from '../routes-api';\nimport { ensureSecretsDir, writeSecretFile } from '../secret-file';\nimport { resolveSourceTarget } from '../source-target';\n\nexport const ROUTES_SUBCOMMANDS = [\n 'list',\n 'options',\n 'set',\n 'remove',\n 'webhook',\n 'unrouted',\n 'replay',\n] as const;\ntype RoutesSubcommand = (typeof ROUTES_SUBCOMMANDS)[number];\n\nexport interface RoutesArgs {\n host?: string;\n source?: string;\n /** `list [product]`, `set <product> <value> <team>`, `replay <event-id>`, … */\n positionals: readonly string[];\n label?: string;\n webhook?: string;\n /** `webhook --yes`: replace the live URL (revoking it). */\n yes?: boolean;\n dir: string;\n env: Record<string, string | undefined>;\n}\n\nconst USAGE =\n 'Run one of: ekanos routes list [<product>] | options <product> | set <product> <value> <team> [--label <name>] | remove <product> <value> --yes | webhook <product> [--webhook <id>] [--yes] | unrouted [<product>] | replay <event-id>.';\n\n/**\n * `routes` — a Source admin's routing of a Source connection's events to\n * teams (plan 241.4). One Zendesk connected for the whole Source sends every\n * ticket to one webhook; each ticket's organization decides which team's\n * agents see it. `set` maps a value to a team, `remove` unmaps it, `options`\n * lists the values the integration knows (Zendesk's organizations),\n * `webhook` mints the Source's endpoint URL (shown once), and `unrouted` /\n * `replay` deal with events no team was mapped to — they started nothing.\n */\nexport async function runRoutes(\n ctx: CliContext,\n args: RoutesArgs,\n): Promise<ExitCode> {\n const [rawSubcommand, ...operands] = args.positionals;\n const subcommand = parseSubcommand(rawSubcommand);\n const expected = OPERANDS[subcommand];\n if (operands.length < expected.min || operands.length > expected.max) {\n throw usageError(\n `routes ${subcommand} takes ${expected.usage}.`,\n `e.g. \"ekanos routes ${subcommand} ${expected.example}\".`,\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), args);\n const client = new RoutesClient(env, resolved.session);\n const source = resolved.source;\n const send = <T>(\n schema: z.ZodType<T>,\n init: Parameters<typeof routesCall>[3],\n ) =>\n client.call((token) =>\n routesCall(env.host, token, schema, {\n ...init,\n query: { source, ...init.query },\n }),\n );\n\n switch (subcommand) {\n case 'list': {\n const product = operands[0] ? requireSlug(operands[0]) : undefined;\n const list = await send(RouteListSchema, {\n method: 'GET',\n query: { product },\n });\n if (list.routes.length === 0) {\n return ctx.succeed(\n { host: env.host, ...list },\n ctx.ui.result({\n title: `No routes on ${list.source.slug}${product ? ` for ${product}` : ''}`,\n tone: 'warn',\n notes: [\n \"Events on the Source's endpoint stay unrouted — they start nothing — until their value is mapped to a team.\",\n 'Map one: \"ekanos routes set <product> <value> <team>\".',\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...list },\n [\n ctx.ui.bold(`Routes on ${list.source.slug}`),\n ctx.ui.table(\n ['Product', 'Key', 'Value', 'Label', 'Team'],\n list.routes.map(routeRow),\n ),\n ].join('\\n'),\n );\n }\n\n case 'options': {\n const product = requireSlug(operands[0]);\n const result = await send(RouteOptionsSchema, {\n method: 'GET',\n path: `/${encodeURIComponent(product)}/options`,\n query: {},\n });\n if (!result.options) {\n return ctx.succeed(\n { host: env.host, product, ...result },\n ctx.ui.result({\n title: `${product} does not list its ${result.key} values`,\n tone: 'warn',\n notes: [\n `Type the ${result.key} id: \"ekanos routes set ${product} <value> <team>\".`,\n ],\n }),\n );\n }\n return ctx.succeed(\n { host: env.host, product, ...result },\n [\n ctx.ui.bold(`${product} ${result.key} values`),\n ctx.ui.table(\n ['Value', 'Label'],\n result.options.map((option) => [option.value, option.label]),\n ),\n ].join('\\n'),\n );\n }\n\n case 'set': {\n const product = requireSlug(operands[0]);\n const value = requireValue(operands[1]);\n const team = requireSlug(operands[2], 'team');\n const result = await send(RouteSetSchema, {\n method: 'PUT',\n path: `/${encodeURIComponent(product)}`,\n query: {},\n body: { value, team, ...(args.label ? { label: args.label } : {}) },\n });\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: `Routed ${product} ${result.route.key} ${value} to ${result.route.team.slug ?? result.route.team.name}`,\n rows: [\n ['Source', source],\n ['Label', result.route.label ?? '—'],\n [\n 'Replaced',\n result.replaced ? 'yes (moved from another team)' : 'no',\n ],\n ],\n notes: [\n 'New events with this value start that team\\'s agents. Earlier unrouted ones: \"ekanos routes unrouted\" then \"ekanos routes replay <event-id>\".',\n ],\n }),\n );\n }\n\n case 'remove': {\n const product = requireSlug(operands[0]);\n const value = requireValue(operands[1]);\n if (!args.yes) {\n throw invalidStateError(\n `Unmapping ${product} ${value} stops its events reaching that team's agents: they stay unrouted and start nothing until it is mapped again.`,\n `Re-run with --yes: ekanos routes remove ${product} ${value} --yes`,\n );\n }\n const result = await send(RouteRemoveSchema, {\n method: 'DELETE',\n path: `/${encodeURIComponent(product)}`,\n query: { value },\n });\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: `Unmapped ${product} ${value}`,\n rows: [['Source', source]],\n notes: ['Its events now stay unrouted and start nothing.'],\n }),\n );\n }\n\n case 'webhook': {\n const product = requireSlug(operands[0]);\n const command = `ekanos routes webhook ${product}${args.webhook ? ` --webhook ${args.webhook}` : ''}`;\n // The URL carries its token, so it goes to a 0600 file, never the output.\n const secrets = ensureSecretsDir(args.env);\n const result = await send(RouteWebhookSchema, {\n method: 'POST',\n path: `/${encodeURIComponent(product)}/webhook`,\n query: {\n webhook: args.webhook,\n ...(args.yes ? { replace: 'true' } : {}),\n },\n confirmHint: `Re-run with --yes when you are ready to update the vendor: ${command} --yes`,\n });\n const { url, ...rest } = result;\n const secretFile = writeSecretFile(secrets, {\n owner: source,\n what: `${product}-${result.webhookId}-webhook-url`,\n purpose: `Webhook URL for ${product} (${result.webhookId}) on source ${source}, once for the whole Source: point the vendor at it, and routed teams get their events. The token is part of the URL.`,\n values: [['WEBHOOK_URL', url]],\n again: `${command} --source ${source} --yes`,\n });\n return ctx.succeed(\n { host: env.host, product, ...rest, secretFile },\n ctx.ui.result({\n title: `Webhook URL for ${product} on ${source} (shown once)`,\n rows: [\n ['Webhook', result.webhookId],\n ['URL', `saved to ${secretFile} (readable only by you)`],\n ],\n notes: [\n 'The URL carries its token, so it is shown nowhere else and cannot be read again. Copy it from that file into the vendor yourself — not into a chat or an agent session — then delete the file. Once for the whole Source.',\n ...(result.replaced\n ? [\n 'The previous URL is revoked: the vendor must use this one, or no routed team gets events.',\n ]\n : []),\n ...(result.signature\n ? [\n `Deliveries must be signed (${result.signature.header}); the Source connection needs the \"${result.signature.secretName}\" secret.`,\n ]\n : []),\n ],\n }),\n );\n }\n\n case 'unrouted': {\n const product = operands[0] ? requireSlug(operands[0]) : undefined;\n const result = await send(UnroutedListSchema, {\n method: 'GET',\n path: '/unrouted',\n query: { product },\n });\n if (result.events.length === 0) {\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({ title: 'No unrouted events', tone: 'ok' }),\n );\n }\n return ctx.succeed(\n { host: env.host, ...result },\n [\n ctx.ui.bold('Unrouted events (no team mapped; nothing started)'),\n ctx.ui.table(\n ['Id', 'Product', 'Event', 'Value', 'Received'],\n result.events.map((event) => [\n event.id,\n event.product,\n event.eventId,\n event.routeValue\n ? `${event.routeKey ?? ''} ${event.routeValue}`.trim()\n : '(none)',\n event.createdAt,\n ]),\n ),\n 'Map the value with \"ekanos routes set\", then \"ekanos routes replay <id>\".',\n ].join('\\n'),\n );\n }\n\n case 'replay': {\n const id = requireEventId(operands[0]);\n const result = await send(ReplaySchema, {\n method: 'POST',\n path: `/unrouted/${encodeURIComponent(id)}/replay`,\n query: {},\n });\n return ctx.succeed(\n { host: env.host, id, ...result },\n ctx.ui.result({\n title:\n result.status === 'routed'\n ? `Routed ${id}`\n : result.status === 'already-routed'\n ? `${id} was already routed`\n : `${id} is still unrouted`,\n tone: result.status === 'unrouted' ? 'warn' : 'ok',\n notes:\n result.status === 'unrouted'\n ? [\n 'No team is mapped to its value yet: \"ekanos routes set\" first.',\n ]\n : [],\n }),\n );\n }\n }\n}\n\nconst OPERANDS: Record<\n RoutesSubcommand,\n { min: number; max: number; usage: string; example: string }\n> = {\n list: { min: 0, max: 1, usage: 'an optional product', example: 'zendesk' },\n options: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },\n set: {\n min: 3,\n max: 3,\n usage: '<product> <value> <team>',\n example: 'zendesk 360001234567 first-bank',\n },\n remove: {\n min: 2,\n max: 2,\n usage: '<product> <value>',\n example: 'zendesk 360001234567 --yes',\n },\n webhook: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },\n unrouted: {\n min: 0,\n max: 1,\n usage: 'an optional product',\n example: 'zendesk',\n },\n replay: {\n min: 1,\n max: 1,\n usage: 'an event id from \"ekanos routes unrouted\"',\n example: '<event-id>',\n },\n};\n\nfunction routeRow(route: SourceEventRoute): string[] {\n return [\n route.product,\n route.key,\n route.value,\n route.label ?? '—',\n route.team.slug ?? route.team.name,\n ];\n}\n\nclass RoutesClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<RoutesApiResult<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 routes call.`,\n loginHint(this.env),\n );\n }\n}\n\nfunction parseSubcommand(value: string | undefined): RoutesSubcommand {\n const match = ROUTES_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value\n ? `Unknown routes subcommand \"${value}\".`\n : 'routes needs a subcommand.',\n USAGE,\n );\n}\n\nfunction requireSlug(value: string | undefined, what = 'product'): string {\n const slug = value?.trim().toLowerCase() ?? '';\n if (/^[a-z0-9][a-z0-9-]{0,63}$/.test(slug)) return slug;\n throw usageError(\n `\"${value ?? ''}\" is not a ${what} slug.`,\n what === 'team'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : 'Run \"ekanos catalog\" to see what the Source has.',\n );\n}\n\nfunction requireValue(value: string | undefined): string {\n const trimmed = value?.trim() ?? '';\n if (trimmed.length > 0 && trimmed.length <= 200) return trimmed;\n throw usageError(\n 'A route value is 1 to 200 characters.',\n '\"ekanos routes options <product>\" lists the values the integration knows.',\n );\n}\n\nfunction requireEventId(value: string | undefined): string {\n const id = value?.trim() ?? '';\n if (/^[0-9a-f-]{36}$/i.test(id)) return id;\n throw usageError(\n `\"${value ?? ''}\" is not an event id.`,\n '\"ekanos routes unrouted\" lists the events waiting for a mapping.',\n );\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { CliContext } from '../context.js';
2
2
  import type { ExitCode } from '../exit-codes.js';
3
- export declare const RUNS_SUBCOMMANDS: readonly ['start', 'list', 'show'];
3
+ export declare const RUNS_SUBCOMMANDS: readonly ['start', 'list', 'show', 'cancel'];
4
4
  export declare const APPROVALS_SUBCOMMANDS: readonly ['list', 'approve', 'reject', 'edit'];
5
5
  export interface RunsArgs {
6
6
  host?: string;
@@ -8,15 +8,23 @@ export interface RunsArgs {
8
8
  positionals: readonly string[];
9
9
  team?: string;
10
10
  agent?: string;
11
+ /** `list --status <status>` and `--since 24h|7d|<date>`. */
12
+ status?: string;
13
+ since?: string;
11
14
  prompt?: string;
12
15
  input?: string;
13
16
  follow: boolean;
14
17
  reason?: string;
15
18
  body?: string;
19
+ /** `runs cancel`: confirmed up front, for a script or an agent run. */
20
+ yes?: boolean;
16
21
  dir: string;
17
22
  env: Record<string, string | undefined>;
18
23
  /** Test seam for the `--follow` poll delay. */
19
24
  sleep?: (ms: number) => Promise<void>;
25
+ /** Test seams for `runs cancel`'s question. */
26
+ stdinIsTTY?: boolean;
27
+ confirm?: (question: string) => Promise<boolean>;
20
28
  }
21
29
  /**
22
30
  * `runs` — run, list and inspect a team's agents as its Source admin. A run
@@ -29,7 +37,10 @@ export declare function runRuns(ctx: CliContext, args: RunsArgs): Promise<ExitCo
29
37
  * `agents test <key> --team <slug>`: run the template's latest version — its
30
38
  * draft, published or not — on one team, without publishing it. Marked as a
31
39
  * draft test run in the ledger; the team's stored agent still bounds every
32
- * tool call, so a draft can narrow what runs but never widen it.
40
+ * tool call, so a draft can narrow what runs but never widen it. To test what
41
+ * a draft adds — or a template never published — pin the team to the draft
42
+ * first (`teams agents enable|pin <team> <key> --draft`); the host warns when
43
+ * the team holds another version.
33
44
  */
34
45
  export declare function runAgentTest(ctx: CliContext, args: RunsArgs & {
35
46
  key: string | undefined;
@@ -1,10 +1,14 @@
1
+ import { AgentRunStatusSchema, } from '@ekanos/integration-schema';
1
2
  import * as fs from 'node:fs';
2
3
  import * as path from 'node:path';
4
+ import { loginHint } from '../auth/host-trust.js';
3
5
  import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
4
6
  import { authRequiredError, usageError, validationError } from '../errors.js';
5
- import { decideApproval, listApprovals, listRuns, showRun, startRun, } from '../runs-api.js';
7
+ import { cancelRun, decideApproval, listApprovals, listRuns, showRun, startRun, } from '../runs-api.js';
6
8
  import { resolveSourceTarget } from '../source-target.js';
7
- export const RUNS_SUBCOMMANDS = ['start', 'list', 'show'];
9
+ import { promptYesNo } from './publish.js';
10
+ import { parseSince } from './teams-events.js';
11
+ export const RUNS_SUBCOMMANDS = ['start', 'list', 'show', 'cancel'];
8
12
  export const APPROVALS_SUBCOMMANDS = [
9
13
  'list',
10
14
  'approve',
@@ -25,7 +29,7 @@ export async function runRuns(ctx, args) {
25
29
  if (!subcommand) {
26
30
  throw usageError(rawSubcommand
27
31
  ? `Unknown runs subcommand "${rawSubcommand}".`
28
- : 'runs needs a subcommand.', 'Run one of: ekanos runs start <agent> --team <slug> --prompt "…", ekanos runs list --team <slug>, ekanos runs show <run-id> --team <slug>.');
32
+ : 'runs needs a subcommand.', 'Run one of: ekanos runs start <agent> --team <slug> --prompt "…", ekanos runs list --team <slug>, ekanos runs show <run-id> --team <slug>, ekanos runs cancel <run-id> --team <slug> [--yes].');
29
33
  }
30
34
  rejectExtra(extra, 'runs');
31
35
  const team = requireTeam(args.team);
@@ -37,10 +41,25 @@ export async function runRuns(ctx, args) {
37
41
  if (subcommand === 'list') {
38
42
  if (operand)
39
43
  rejectExtra([operand], 'runs list');
40
- const list = await client.call((token) => listRuns(env.host, token, { source, team, agent: args.agent }));
44
+ const status = parseStatus(args.status);
45
+ const since = parseSince(args.since, Date.now());
46
+ const list = await client.call((token) => listRuns(env.host, token, {
47
+ source,
48
+ team,
49
+ agent: args.agent,
50
+ status,
51
+ since,
52
+ }));
53
+ const scope = [
54
+ args.agent ? `for ${args.agent}` : '',
55
+ status ? `${status}` : '',
56
+ since ? `since ${since.slice(0, 16).replace('T', ' ')}` : '',
57
+ ]
58
+ .filter(Boolean)
59
+ .join(', ');
41
60
  return ctx.succeed(Object.assign({ host: env.host }, list), list.runs.length === 0
42
61
  ? ctx.ui.result({
43
- title: `No runs on ${list.team.slug}${args.agent ? ` for ${args.agent}` : ''}`,
62
+ title: `No runs on ${list.team.slug}${scope ? ` (${scope})` : ''}`,
44
63
  tone: 'warn',
45
64
  })
46
65
  : ctx.ui.table(['RUN', 'AGENT', 'STATUS', 'TRIGGER', 'VERSION', 'STARTED'], list.runs.map((run) => [
@@ -52,6 +71,22 @@ export async function runRuns(ctx, args) {
52
71
  run.startedAt.slice(0, 19).replace('T', ' '),
53
72
  ])));
54
73
  }
74
+ if (subcommand === 'cancel') {
75
+ const runId = requireOperand(operand, 'run id', 'runs cancel');
76
+ await confirmCancel(ctx, args, { runId, team });
77
+ const cancelled = await client.call((token) => cancelRun(env.host, token, { source, team, runId }));
78
+ return ctx.succeed(Object.assign({ host: env.host }, cancelled), ctx.ui.result({
79
+ title: `Cancelled run ${cancelled.run.id} on ${cancelled.team.slug}`,
80
+ tone: 'warn',
81
+ rows: [
82
+ ['Agent', agentLabel(cancelled.run.agent)],
83
+ ['Status', cancelled.run.status],
84
+ ],
85
+ notes: [
86
+ 'Its pending approvals expired, and runs it was waiting on were stopped too.',
87
+ ],
88
+ }));
89
+ }
55
90
  const runId = requireOperand(operand, 'run id', 'runs show');
56
91
  const detail = await client.call((token) => showRun(env.host, token, { source, team, runId }));
57
92
  return ctx.succeed(Object.assign({ host: env.host }, detail), runDetail(ctx.ui, detail));
@@ -60,7 +95,10 @@ export async function runRuns(ctx, args) {
60
95
  * `agents test <key> --team <slug>`: run the template's latest version — its
61
96
  * draft, published or not — on one team, without publishing it. Marked as a
62
97
  * draft test run in the ledger; the team's stored agent still bounds every
63
- * tool call, so a draft can narrow what runs but never widen it.
98
+ * tool call, so a draft can narrow what runs but never widen it. To test what
99
+ * a draft adds — or a template never published — pin the team to the draft
100
+ * first (`teams agents enable|pin <team> <key> --draft`); the host warns when
101
+ * the team holds another version.
64
102
  */
65
103
  export async function runAgentTest(ctx, args) {
66
104
  const agent = requireOperand(args.key, 'agent key', 'agents test');
@@ -121,20 +159,41 @@ export async function runApprovals(ctx, args) {
121
159
  ],
122
160
  }));
123
161
  }
162
+ /**
163
+ * Stopping a run can't be undone: a human run asks first; a scripted or JSON
164
+ * run needs `--yes`. Asked before any request.
165
+ */
166
+ async function confirmCancel(ctx, args, target) {
167
+ var _a, _b;
168
+ if (args.yes)
169
+ return;
170
+ if (ctx.jsonMode || !((_a = args.stdinIsTTY) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true)) {
171
+ throw validationError(`Cancelling run ${target.runId} on ${target.team} needs confirmation.`, `It stops the run for good and expires its pending approvals. Pass --yes to confirm: ekanos runs cancel ${target.runId} --team ${target.team} --yes`);
172
+ }
173
+ const confirmed = await ((_b = args.confirm) !== null && _b !== void 0 ? _b : promptYesNo)(`Stop run ${target.runId} on ${target.team} for good? [y/N]`);
174
+ if (!confirmed) {
175
+ throw validationError(`Cancelling run ${target.runId} was not confirmed.`, 'Nothing was changed.');
176
+ }
177
+ }
124
178
  async function startAndReport(ctx, args, target) {
179
+ var _a;
125
180
  const body = startBody(args, target);
126
181
  const { env, client, source } = await connect(ctx, args);
127
182
  const started = await client.call((token) => startRun(env.host, token, { source, team: target.team }, body));
128
183
  const startedNote = `${target.draft ? 'Testing' : 'Started'} ${agentLabel(started.agent)} on ${started.team.slug}${started.draftTemplateVersion ? ` (draft v${started.draftTemplateVersion}, unpublished)` : ''}: run ${started.runId}`;
184
+ // A run started here needs no channel, so it ran; the agent's real trigger
185
+ // may still never fire. Said up front, so a passing test is not read as
186
+ // "the agent answers in Linear".
187
+ const warnings = ((_a = started.warnings) !== null && _a !== void 0 ? _a : []).map((warning) => `Warning: ${warning}`);
129
188
  if (!args.follow) {
130
- return ctx.succeed(Object.assign({ host: env.host }, started), ctx.ui.result({
131
- title: startedNote,
132
- notes: [
189
+ return ctx.succeed(Object.assign({ host: env.host }, started), ctx.ui.result(Object.assign(Object.assign({ title: startedNote }, (warnings.length > 0 ? { tone: 'warn' } : {})), { notes: [
190
+ ...warnings,
133
191
  `Watch it: "ekanos runs show ${started.runId} --team ${target.team}".`,
134
- ],
135
- }));
192
+ ] })));
136
193
  }
137
194
  ctx.log(startedNote);
195
+ for (const warning of warnings)
196
+ ctx.log(warning);
138
197
  const detail = await follow(ctx, args, async (after) => client.call((token) => showRun(env.host, token, {
139
198
  source,
140
199
  team: target.team,
@@ -167,7 +226,7 @@ async function follow(ctx, args, read) {
167
226
  if (seenCalls.has(call.toolCallId))
168
227
  continue;
169
228
  seenCalls.add(call.toolCallId);
170
- ctx.log(` ${call.ok === false ? '✗' : '✓'} ${call.toolName} (${call.effect}, ${call.authorizedVia})${call.error ? ` — ${call.error}` : ''}`);
229
+ ctx.log(` ${call.ok === false ? '✗' : '✓'} ${call.toolName} (${callFacts(call)})${call.error ? ` — ${call.error}` : ''}`);
171
230
  }
172
231
  if (detail.run.status !== lastStatus) {
173
232
  lastStatus = detail.run.status;
@@ -260,7 +319,7 @@ function runDetail(ui, detail) {
260
319
  'Tool calls',
261
320
  detail.toolCalls.length === 0
262
321
  ? 'none'
263
- : detail.toolCalls.map((call) => `${call.ok === false ? '✗' : '✓'} ${call.toolName} (${call.effect}, ${call.authorizedVia})`),
322
+ : detail.toolCalls.map((call) => `${call.ok === false ? '✗' : '✓'} ${call.toolName} (${callFacts(call)})`),
264
323
  ],
265
324
  ...(pending.length > 0
266
325
  ? [
@@ -327,6 +386,15 @@ function requireOperand(value, what, command) {
327
386
  return trimmed;
328
387
  throw usageError(`Name the ${what}.`, `See "ekanos ${command} --help".`);
329
388
  }
389
+ /** `--status`, one of the run statuses. */
390
+ function parseStatus(raw) {
391
+ if (raw === undefined)
392
+ return undefined;
393
+ const parsed = AgentRunStatusSchema.safeParse(raw.trim());
394
+ if (parsed.success)
395
+ return parsed.data;
396
+ throw usageError(`--status "${raw}" is not a run status.`, `Use one of: ${AgentRunStatusSchema.options.join(', ')}.`);
397
+ }
330
398
  function rejectExtra(extra, command) {
331
399
  if (extra.length > 0) {
332
400
  throw usageError(`Unexpected argument "${extra[0]}".`, `See "ekanos ${command.split(' ')[0]} --help".`);
@@ -336,7 +404,7 @@ async function connect(ctx, args) {
336
404
  const env = resolveAuthEnvironment(ctx, args.host, args.env, {
337
405
  projectDir: args.dir,
338
406
  });
339
- const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
407
+ const resolved = await resolveSourceTarget(env, requireSession(env), args);
340
408
  return {
341
409
  env,
342
410
  client: new RunsClient(env, resolved.session),
@@ -356,7 +424,20 @@ class RunsClient {
356
424
  }
357
425
  if (result.status === 'ok')
358
426
  return result.data;
359
- throw authRequiredError(`The session for ${this.env.host} could not authorize the call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
427
+ throw authRequiredError(`The session for ${this.env.host} could not authorize the call.`, loginHint(this.env));
360
428
  }
361
429
  }
430
+ const CONNECTION_WORDS = {
431
+ source: 'via Source connection',
432
+ team: 'via team connection',
433
+ person: 'via personal connection',
434
+ };
435
+ /** Effect, how it was authorized, and whose connection it ran on. */
436
+ function callFacts(call) {
437
+ return [
438
+ call.effect,
439
+ call.authorizedVia,
440
+ ...(call.connection ? [CONNECTION_WORDS[call.connection]] : []),
441
+ ].join(', ');
442
+ }
362
443
  //# sourceMappingURL=runs.js.map