@ekanos/cli 0.11.0 → 0.13.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 (57) hide show
  1. package/dist/apps-api.d.ts +3 -2
  2. package/dist/apps-api.js +1 -5
  3. package/dist/apps-api.js.map +1 -1
  4. package/dist/commands/agents-edit.js +1 -1
  5. package/dist/commands/agents-edit.js.map +1 -1
  6. package/dist/commands/apps.d.ts +19 -6
  7. package/dist/commands/apps.js +366 -43
  8. package/dist/commands/apps.js.map +1 -1
  9. package/dist/commands/knowledge.d.ts +37 -0
  10. package/dist/commands/knowledge.js +364 -0
  11. package/dist/commands/knowledge.js.map +1 -0
  12. package/dist/commands/routes.d.ts +23 -0
  13. package/dist/commands/routes.js +277 -0
  14. package/dist/commands/routes.js.map +1 -0
  15. package/dist/commands/sources.d.ts +4 -0
  16. package/dist/commands/sources.js +115 -1
  17. package/dist/commands/sources.js.map +1 -1
  18. package/dist/commands/team-agents-triggers.js +2 -1
  19. package/dist/commands/team-agents-triggers.js.map +1 -1
  20. package/dist/commands/teams-connect.d.ts +25 -0
  21. package/dist/commands/teams-connect.js +242 -0
  22. package/dist/commands/teams-connect.js.map +1 -0
  23. package/dist/commands/teams-onboard.d.ts +5 -2
  24. package/dist/commands/teams-onboard.js +12 -40
  25. package/dist/commands/teams-onboard.js.map +1 -1
  26. package/dist/commands/teams.d.ts +60 -5
  27. package/dist/commands/teams.js +141 -46
  28. package/dist/commands/teams.js.map +1 -1
  29. package/dist/connections-api.d.ts +125 -0
  30. package/dist/connections-api.js +156 -0
  31. package/dist/connections-api.js.map +1 -0
  32. package/dist/index.js +90 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/knowledge-api.d.ts +129 -0
  35. package/dist/knowledge-api.js +138 -0
  36. package/dist/knowledge-api.js.map +1 -0
  37. package/dist/routes-api.d.ts +105 -0
  38. package/dist/routes-api.js +147 -0
  39. package/dist/routes-api.js.map +1 -0
  40. package/dist/source-products-api.d.ts +59 -0
  41. package/dist/source-products-api.js +113 -0
  42. package/dist/source-products-api.js.map +1 -0
  43. package/dist/team-connect-api.d.ts +244 -0
  44. package/dist/team-connect-api.js +95 -0
  45. package/dist/team-connect-api.js.map +1 -0
  46. package/dist/teams-api.d.ts +1 -0
  47. package/dist/teams-api.js +1 -1
  48. package/dist/teams-api.js.map +1 -1
  49. package/package.json +2 -2
  50. package/templates/package.json.tmpl +4 -4
  51. package/templates/skills/build-agent/SKILL.md +18 -3
  52. package/templates/skills/build-agent/references/agent-json.md +29 -8
  53. package/templates/skills/build-agent/references/app-setup.md +2 -1
  54. package/templates/skills/build-agent/references/set-up-an-integration.md +76 -10
  55. package/templates/skills/build-agent/references/skills.md +7 -4
  56. package/templates/skills/build-agent/references/triggers.md +44 -15
  57. package/templates/skills/ekanos-platform/SKILL.md +40 -8
@@ -1 +1 @@
1
- {"version":3,"file":"teams-onboard.js","sourceRoot":"","sources":["../../src/commands/teams-onboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CliContext } from '../context';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n createTeam,\n inviteTeamMember,\n listTeamMembers,\n listTeamRoles,\n removeTeamMember,\n setTeamMemberRole,\n showTeamIntegration,\n} from '../teams-api';\nimport type { TeamsClient } from './teams';\n\n/**\n * Onboarding a team from the terminal, for the Source admin: `teams create`,\n * `teams members list|invite|role|remove`, `teams roles list` and\n * `teams connect`. Parsed before any session is touched (`parseOnboarding`)\n * so a usage error never costs a network round-trip, then run against the\n * session `runTeams` resolved.\n */\n\nexport const ONBOARDING_SUBCOMMANDS = [\n 'create',\n 'members',\n 'roles',\n 'connect',\n] as const;\nexport type OnboardingSubcommand = (typeof ONBOARDING_SUBCOMMANDS)[number];\n\nexport interface OnboardingFlags {\n slug?: string;\n owner?: string;\n role?: string;\n yes: boolean;\n}\n\nexport type OnboardingPlan =\n | { kind: 'create'; name: string; slug?: string; ownerEmail?: string }\n | { kind: 'members-list'; team: string }\n | { kind: 'members-invite'; team: string; email: string; role: string }\n | { kind: 'members-role'; team: string; email: string; role: string }\n | { kind: 'members-remove'; team: string; email: string }\n | { kind: 'roles-list'; team: string }\n | { kind: 'connect'; team: string; product: string };\n\nconst MEMBERS_VERBS = ['list', 'invite', 'role', 'remove'] as const;\n\nexport function isOnboardingSubcommand(\n value: string,\n): value is OnboardingSubcommand {\n return (ONBOARDING_SUBCOMMANDS as readonly string[]).includes(value);\n}\n\nexport function parseOnboarding(\n subcommand: OnboardingSubcommand,\n rest: readonly string[],\n flags: OnboardingFlags,\n): OnboardingPlan {\n if (subcommand === 'create') {\n const [name, ...extra] = rest;\n if (!name?.trim()) {\n throw usageError(\n 'Name the team.',\n 'Run \"ekanos teams create \\\\\"Bank A\\\\\" [--slug bank-a] [--owner <email>]\".',\n );\n }\n noExtra(extra);\n return {\n kind: 'create',\n name: name.trim(),\n slug: flags.slug === undefined ? undefined : slugOf(flags.slug, 'team'),\n ownerEmail: flags.owner === undefined ? undefined : emailOf(flags.owner),\n };\n }\n\n if (subcommand === 'connect') {\n const [team, product, ...extra] = rest;\n noExtra(extra);\n return {\n kind: 'connect',\n team: slugOf(team, 'team'),\n product: slugOf(product, 'product'),\n };\n }\n\n if (subcommand === 'roles') {\n const [verb, team, ...extra] = rest;\n if (verb !== 'list') {\n throw usageError(\n verb\n ? `Unknown teams roles subcommand \"${verb}\".`\n : 'teams roles needs a subcommand.',\n 'Run \"ekanos teams roles list <team>\".',\n );\n }\n noExtra(extra);\n return { kind: 'roles-list', team: slugOf(team, 'team') };\n }\n\n const [verb, team, email, role, ...extra] = rest;\n const match = MEMBERS_VERBS.find((name) => name === verb);\n if (!match) {\n throw usageError(\n verb\n ? `Unknown teams members subcommand \"${verb}\".`\n : 'teams members needs a subcommand.',\n 'Run one of: ekanos teams members list <team>, ekanos teams members invite <team> <email> --role <role>, ekanos teams members role <team> <email> <role>, ekanos teams members remove <team> <email> --yes.',\n );\n }\n noExtra(extra);\n\n if (match === 'list') {\n if (email !== undefined) noExtra([email]);\n return { kind: 'members-list', team: slugOf(team, 'team') };\n }\n\n const teamSlug = slugOf(team, 'team');\n const address = emailOf(email);\n\n if (match === 'invite') {\n if (role !== undefined) noExtra([role]);\n return {\n kind: 'members-invite',\n team: teamSlug,\n email: address,\n role: roleOf(flags.role ?? 'member'),\n };\n }\n\n if (match === 'role') {\n const named = role ?? flags.role;\n if (!named) {\n throw usageError(\n 'Name the new role.',\n `Run \"ekanos teams members role ${teamSlug} ${address} <role>\"; \"ekanos teams roles list ${teamSlug}\" lists them.`,\n );\n }\n return {\n kind: 'members-role',\n team: teamSlug,\n email: address,\n role: roleOf(named),\n };\n }\n\n if (role !== undefined) noExtra([role]);\n if (!flags.yes) {\n throw invalidStateError(\n `Removing ${address} from ${teamSlug} ends their access to the team (a pending invitation is withdrawn instead).`,\n `Re-run with --yes: ekanos teams members remove ${teamSlug} ${address} --yes`,\n );\n }\n return { kind: 'members-remove', team: teamSlug, email: address };\n}\n\nexport async function runOnboarding(\n ctx: CliContext,\n client: TeamsClient,\n host: string,\n source: string,\n plan: OnboardingPlan,\n): Promise<ExitCode> {\n switch (plan.kind) {\n case 'create': {\n const created = await client.call((token) =>\n createTeam(host, token, source, {\n name: plan.name,\n slug: plan.slug,\n ownerEmail: plan.ownerEmail,\n }),\n );\n return ctx.succeed(\n { host, ...created },\n ctx.ui.result({\n title: `Created team ${created.team.slug}`,\n rows: [\n ['Name', created.team.name],\n ['Source', created.source.slug],\n [\n 'Owner',\n created.owner.kind === 'caller'\n ? `you${created.owner.email ? ` (${created.owner.email})` : ''}`\n : `${created.owner.email} — invited; ownership moves to them on accept`,\n ],\n ['Team home', created.teamUrl],\n ],\n notes: [\n `Next: \"ekanos teams members invite ${created.team.slug} <email> --role <role>\" (\"ekanos teams roles list ${created.team.slug}\" lists the roles).`,\n `Then \"ekanos teams connect ${created.team.slug} <product>\" for the link a member opens to connect an integration.`,\n ],\n }),\n );\n }\n\n case 'members-list': {\n const list = await client.call((token) =>\n listTeamMembers(host, token, { source, team: plan.team }),\n );\n const rows: [string, string][] = list.members.map((member) => [\n member.email ?? member.userId,\n `${member.role}${member.primaryOwner ? ' · primary owner' : ''}${member.status === 'active' ? '' : ` · ${member.status}`}${member.name ? ` · ${member.name}` : ''}`,\n ]);\n for (const invitation of list.invitations) {\n rows.push([\n invitation.email,\n `${invitation.role} · invited, expires ${invitation.expiresAt.slice(0, 10)}`,\n ]);\n }\n return ctx.succeed(\n { host, ...list },\n ctx.ui.result({\n title: `Members of ${list.team.slug} (${list.team.name})`,\n tone: rows.length === 0 ? 'warn' : 'ok',\n rows,\n notes: [\n `\"ekanos teams members invite ${list.team.slug} <email> --role <role>\" adds one.`,\n ],\n }),\n );\n }\n\n case 'members-invite': {\n const invited = await client.call((token) =>\n inviteTeamMember(\n host,\n token,\n { source, team: plan.team },\n { email: plan.email, role: plan.role },\n ),\n );\n return ctx.succeed(\n { host, ...invited },\n ctx.ui.result({\n title: `Invited ${invited.invitation.email} to ${invited.team.slug}`,\n rows: [\n ['Role', invited.invitation.role],\n ['Expires', invited.invitation.expiresAt.slice(0, 10)],\n ],\n notes: [\n 'The invitation email is on its way; they join when they accept it.',\n ],\n }),\n );\n }\n\n case 'members-role': {\n const changed = await client.call((token) =>\n setTeamMemberRole(\n host,\n token,\n { source, team: plan.team, email: plan.email },\n plan.role,\n ),\n );\n return ctx.succeed(\n { host, ...changed },\n ctx.ui.result({\n title:\n changed.before === changed.after\n ? `${changed.email} is already ${changed.after} on ${changed.team.slug}`\n : `${changed.email} is now ${changed.after} on ${changed.team.slug}`,\n rows: [\n ['Was', changed.before ?? '—'],\n [\n 'Applies to',\n changed.kind === 'member'\n ? 'their membership'\n : 'their pending invitation',\n ],\n ],\n }),\n );\n }\n\n case 'members-remove': {\n const removed = await client.call((token) =>\n removeTeamMember(host, token, {\n source,\n team: plan.team,\n email: plan.email,\n }),\n );\n return ctx.succeed(\n { host, ...removed },\n ctx.ui.result({\n title:\n removed.kind === 'member'\n ? `Removed ${removed.email} from ${removed.team.slug}`\n : `Withdrew ${removed.email}'s invitation to ${removed.team.slug}`,\n rows: [['Role was', removed.before ?? '—']],\n }),\n );\n }\n\n case 'roles-list': {\n const roles = await client.call((token) =>\n listTeamRoles(host, token, { source, team: plan.team }),\n );\n return ctx.succeed(\n { host, ...roles },\n ctx.ui.result({\n title: `Roles on ${roles.team.slug}`,\n rows: roles.roles.map((role) => [\n `${role.name} (${role.displayName})`,\n role.permissions.length > 0 ? role.permissions : ['no permissions'],\n ]),\n notes: [\n 'Lower roles are listed last. Give one with \"ekanos teams members role <team> <email> <role>\".',\n ],\n }),\n );\n }\n\n case 'connect': {\n const status = await client.call((token) =>\n showTeamIntegration(host, token, {\n source,\n team: plan.team,\n product: plan.product,\n }),\n );\n if (!status.enabled) {\n throw invalidStateError(\n `\"${plan.product}\" is not enabled on ${status.source.slug}.`,\n '\"ekanos catalog\" lists what this Source has; Ekanos enables a product for a Source.',\n );\n }\n const connected = status.activation !== null;\n return ctx.succeed(\n { host, ...status },\n ctx.ui.result({\n title: connected\n ? `${status.team.slug} has connected ${status.product.name}`\n : `Connect link for ${status.team.slug} · ${status.product.name}`,\n tone: connected ? 'ok' : 'warn',\n rows: [\n ['Open', status.connectUrl],\n [\n 'Status',\n connected\n ? `connected${status.activation?.activatedAt ? ` ${status.activation.activatedAt.slice(0, 10)}` : ''}`\n : 'not connected',\n ],\n ],\n notes: [\n 'A team member signs in there and clicks Connect: sign-in happens in their browser, never in a terminal.',\n `\"ekanos teams show ${status.team.slug} ${status.product.slug}\" shows the full setup checklist.`,\n ],\n }),\n );\n }\n }\n}\n\nfunction noExtra(extra: readonly string[]) {\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'See \"ekanos teams --help\".',\n );\n }\n}\n\nfunction slugOf(value: string | undefined, what: 'team' | 'product'): 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 ? `\"${value}\" is not a ${what} slug.` : `Name the ${what}.`,\n what === 'team'\n ? '\"ekanos teams list\" lists the teams, e.g. \"ekanos teams members list bank-a\".'\n : '\"ekanos catalog\" lists the products, e.g. \"ekanos teams connect bank-a zendesk\".',\n );\n}\n\nfunction emailOf(value: string | undefined): string {\n const email = value?.trim().toLowerCase();\n if (email && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) return email;\n throw usageError(\n value ? `\"${value}\" is not an email address.` : 'Name the person by email.',\n 'e.g. \"ekanos teams members invite bank-a ana@bank-a.com --role member\".',\n );\n}\n\nfunction roleOf(value: string): string {\n const role = value.trim().toLowerCase();\n if (/^[a-z][a-z0-9_-]{0,49}$/.test(role)) return role;\n throw usageError(\n `\"${value}\" is not a role name.`,\n '\"ekanos teams roles list <team>\" lists the roles this Source uses.',\n );\n}\n"]}
1
+ {"version":3,"file":"teams-onboard.js","sourceRoot":"","sources":["../../src/commands/teams-onboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CliContext } from '../context';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n createTeam,\n inviteTeamMember,\n listTeamMembers,\n listTeamRoles,\n removeTeamMember,\n setTeamMemberRole,\n} from '../teams-api';\nimport type { TeamsArgs, TeamsClient } from './teams';\nimport { runTeamConnect } from './teams-connect';\n\n/**\n * Onboarding a team from the terminal, for the Source admin: `teams create`,\n * `teams members list|invite|role|remove`, `teams roles list` and\n * `teams connect`. Parsed before any session is touched (`parseOnboarding`)\n * so a usage error never costs a network round-trip, then run against the\n * session `runTeams` resolved.\n */\n\nexport const ONBOARDING_SUBCOMMANDS = [\n 'create',\n 'members',\n 'roles',\n 'connect',\n] as const;\nexport type OnboardingSubcommand = (typeof ONBOARDING_SUBCOMMANDS)[number];\n\nexport interface OnboardingFlags {\n slug?: string;\n owner?: string;\n role?: string;\n /** `connect --as <member-email>`: the member who signs in. */\n as?: string;\n yes: boolean;\n}\n\nexport type OnboardingPlan =\n | { kind: 'create'; name: string; slug?: string; ownerEmail?: string }\n | { kind: 'members-list'; team: string }\n | { kind: 'members-invite'; team: string; email: string; role: string }\n | { kind: 'members-role'; team: string; email: string; role: string }\n | { kind: 'members-remove'; team: string; email: string }\n | { kind: 'roles-list'; team: string }\n | { kind: 'connect'; team: string; product: string; as?: string };\n\nconst MEMBERS_VERBS = ['list', 'invite', 'role', 'remove'] as const;\n\nexport function isOnboardingSubcommand(\n value: string,\n): value is OnboardingSubcommand {\n return (ONBOARDING_SUBCOMMANDS as readonly string[]).includes(value);\n}\n\nexport function parseOnboarding(\n subcommand: OnboardingSubcommand,\n rest: readonly string[],\n flags: OnboardingFlags,\n): OnboardingPlan {\n if (subcommand === 'create') {\n const [name, ...extra] = rest;\n if (!name?.trim()) {\n throw usageError(\n 'Name the team.',\n 'Run \"ekanos teams create \\\\\"Bank A\\\\\" [--slug bank-a] [--owner <email>]\".',\n );\n }\n noExtra(extra);\n return {\n kind: 'create',\n name: name.trim(),\n slug: flags.slug === undefined ? undefined : slugOf(flags.slug, 'team'),\n ownerEmail: flags.owner === undefined ? undefined : emailOf(flags.owner),\n };\n }\n\n if (subcommand === 'connect') {\n const [team, product, ...extra] = rest;\n noExtra(extra);\n return {\n kind: 'connect',\n team: slugOf(team, 'team'),\n product: slugOf(product, 'product'),\n ...(flags.as === undefined ? {} : { as: emailOf(flags.as) }),\n };\n }\n\n if (subcommand === 'roles') {\n const [verb, team, ...extra] = rest;\n if (verb !== 'list') {\n throw usageError(\n verb\n ? `Unknown teams roles subcommand \"${verb}\".`\n : 'teams roles needs a subcommand.',\n 'Run \"ekanos teams roles list <team>\".',\n );\n }\n noExtra(extra);\n return { kind: 'roles-list', team: slugOf(team, 'team') };\n }\n\n const [verb, team, email, role, ...extra] = rest;\n const match = MEMBERS_VERBS.find((name) => name === verb);\n if (!match) {\n throw usageError(\n verb\n ? `Unknown teams members subcommand \"${verb}\".`\n : 'teams members needs a subcommand.',\n 'Run one of: ekanos teams members list <team>, ekanos teams members invite <team> <email> --role <role>, ekanos teams members role <team> <email> <role>, ekanos teams members remove <team> <email> --yes.',\n );\n }\n noExtra(extra);\n\n if (match === 'list') {\n if (email !== undefined) noExtra([email]);\n return { kind: 'members-list', team: slugOf(team, 'team') };\n }\n\n const teamSlug = slugOf(team, 'team');\n const address = emailOf(email);\n\n if (match === 'invite') {\n if (role !== undefined) noExtra([role]);\n return {\n kind: 'members-invite',\n team: teamSlug,\n email: address,\n role: roleOf(flags.role ?? 'member'),\n };\n }\n\n if (match === 'role') {\n const named = role ?? flags.role;\n if (!named) {\n throw usageError(\n 'Name the new role.',\n `Run \"ekanos teams members role ${teamSlug} ${address} <role>\"; \"ekanos teams roles list ${teamSlug}\" lists them.`,\n );\n }\n return {\n kind: 'members-role',\n team: teamSlug,\n email: address,\n role: roleOf(named),\n };\n }\n\n if (role !== undefined) noExtra([role]);\n if (!flags.yes) {\n throw invalidStateError(\n `Removing ${address} from ${teamSlug} ends their access to the team (a pending invitation is withdrawn instead).`,\n `Re-run with --yes: ekanos teams members remove ${teamSlug} ${address} --yes`,\n );\n }\n return { kind: 'members-remove', team: teamSlug, email: address };\n}\n\nexport async function runOnboarding(\n ctx: CliContext,\n args: TeamsArgs,\n client: TeamsClient,\n host: string,\n source: string,\n plan: OnboardingPlan,\n): Promise<ExitCode> {\n switch (plan.kind) {\n case 'create': {\n const created = await client.call((token) =>\n createTeam(host, token, source, {\n name: plan.name,\n slug: plan.slug,\n ownerEmail: plan.ownerEmail,\n }),\n );\n return ctx.succeed(\n { host, ...created },\n ctx.ui.result({\n title: `Created team ${created.team.slug}`,\n rows: [\n ['Name', created.team.name],\n ['Source', created.source.slug],\n [\n 'Owner',\n created.owner.kind === 'caller'\n ? `you${created.owner.email ? ` (${created.owner.email})` : ''}`\n : `${created.owner.email} — invited; ownership moves to them on accept`,\n ],\n ['Team home', created.teamUrl],\n ],\n notes: [\n `Next: \"ekanos teams members invite ${created.team.slug} <email> --role <role>\" (\"ekanos teams roles list ${created.team.slug}\" lists the roles).`,\n `Then \"ekanos teams connect ${created.team.slug} <product>\" connects an integration for the team.`,\n ],\n }),\n );\n }\n\n case 'members-list': {\n const list = await client.call((token) =>\n listTeamMembers(host, token, { source, team: plan.team }),\n );\n const rows: [string, string][] = list.members.map((member) => [\n member.email ?? member.userId,\n `${member.role}${member.primaryOwner ? ' · primary owner' : ''}${member.status === 'active' ? '' : ` · ${member.status}`}${member.name ? ` · ${member.name}` : ''}`,\n ]);\n for (const invitation of list.invitations) {\n rows.push([\n invitation.email,\n `${invitation.role} · invited, expires ${invitation.expiresAt.slice(0, 10)}`,\n ]);\n }\n return ctx.succeed(\n { host, ...list },\n ctx.ui.result({\n title: `Members of ${list.team.slug} (${list.team.name})`,\n tone: rows.length === 0 ? 'warn' : 'ok',\n rows,\n notes: [\n `\"ekanos teams members invite ${list.team.slug} <email> --role <role>\" adds one.`,\n ],\n }),\n );\n }\n\n case 'members-invite': {\n const invited = await client.call((token) =>\n inviteTeamMember(\n host,\n token,\n { source, team: plan.team },\n { email: plan.email, role: plan.role },\n ),\n );\n return ctx.succeed(\n { host, ...invited },\n ctx.ui.result({\n title: `Invited ${invited.invitation.email} to ${invited.team.slug}`,\n rows: [\n ['Role', invited.invitation.role],\n ['Expires', invited.invitation.expiresAt.slice(0, 10)],\n ],\n notes: [\n 'The invitation email is on its way; they join when they accept it.',\n ],\n }),\n );\n }\n\n case 'members-role': {\n const changed = await client.call((token) =>\n setTeamMemberRole(\n host,\n token,\n { source, team: plan.team, email: plan.email },\n plan.role,\n ),\n );\n return ctx.succeed(\n { host, ...changed },\n ctx.ui.result({\n title:\n changed.before === changed.after\n ? `${changed.email} is already ${changed.after} on ${changed.team.slug}`\n : `${changed.email} is now ${changed.after} on ${changed.team.slug}`,\n rows: [\n ['Was', changed.before ?? '—'],\n [\n 'Applies to',\n changed.kind === 'member'\n ? 'their membership'\n : 'their pending invitation',\n ],\n ],\n }),\n );\n }\n\n case 'members-remove': {\n const removed = await client.call((token) =>\n removeTeamMember(host, token, {\n source,\n team: plan.team,\n email: plan.email,\n }),\n );\n return ctx.succeed(\n { host, ...removed },\n ctx.ui.result({\n title:\n removed.kind === 'member'\n ? `Removed ${removed.email} from ${removed.team.slug}`\n : `Withdrew ${removed.email}'s invitation to ${removed.team.slug}`,\n rows: [['Role was', removed.before ?? '—']],\n }),\n );\n }\n\n case 'roles-list': {\n const roles = await client.call((token) =>\n listTeamRoles(host, token, { source, team: plan.team }),\n );\n return ctx.succeed(\n { host, ...roles },\n ctx.ui.result({\n title: `Roles on ${roles.team.slug}`,\n rows: roles.roles.map((role) => [\n `${role.name} (${role.displayName})`,\n role.permissions.length > 0 ? role.permissions : ['no permissions'],\n ]),\n notes: [\n 'Lower roles are listed last. Give one with \"ekanos teams members role <team> <email> <role>\".',\n ],\n }),\n );\n }\n\n case 'connect':\n return runTeamConnect(ctx, args, client, host, source, {\n team: plan.team,\n product: plan.product,\n as: plan.as,\n });\n }\n}\n\nfunction noExtra(extra: readonly string[]) {\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'See \"ekanos teams --help\".',\n );\n }\n}\n\nfunction slugOf(value: string | undefined, what: 'team' | 'product'): 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 ? `\"${value}\" is not a ${what} slug.` : `Name the ${what}.`,\n what === 'team'\n ? '\"ekanos teams list\" lists the teams, e.g. \"ekanos teams members list bank-a\".'\n : '\"ekanos catalog\" lists the products, e.g. \"ekanos teams connect bank-a zendesk\".',\n );\n}\n\nfunction emailOf(value: string | undefined): string {\n const email = value?.trim().toLowerCase();\n if (email && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) return email;\n throw usageError(\n value ? `\"${value}\" is not an email address.` : 'Name the person by email.',\n 'e.g. \"ekanos teams members invite bank-a ana@bank-a.com --role member\".',\n );\n}\n\nfunction roleOf(value: string): string {\n const role = value.trim().toLowerCase();\n if (/^[a-z][a-z0-9_-]{0,49}$/.test(role)) return role;\n throw usageError(\n `\"${value}\" is not a role name.`,\n '\"ekanos teams roles list <team>\" lists the roles this Source uses.',\n );\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { TeamIntegrationStatus } from '@ekanos/integration-schema';
1
+ import type { TeamAppState, TeamIntegrationStatus } from '@ekanos/integration-schema';
2
2
  import type { StoredSession } from '../auth/credential-store.js';
3
3
  import { type AuthEnvironment } from '../auth/session.js';
4
4
  import type { CliContext } from '../context.js';
@@ -16,6 +16,15 @@ export interface TeamsArgs {
16
16
  owner?: string;
17
17
  role?: string;
18
18
  yes: boolean;
19
+ /** `connect --no-wait`: print the sign-in link and return without polling. */
20
+ noWait?: boolean;
21
+ /** `connect --no-open`: never open a browser, only print the link. */
22
+ noOpen?: boolean;
23
+ /** `connect --link-only`: print the team's page, as before connect did the work. */
24
+ linkOnly?: boolean;
25
+ /** Test seams for `connect`: the wait between polls, and the browser opener. */
26
+ pollDelayMs?: number;
27
+ openUrl?: (url: string) => void;
19
28
  /** Every parsed flag, for `teams agents|triggers|links|mcp` (team-agents.ts). */
20
29
  flags?: Record<string, string | boolean | string[]>;
21
30
  dir: string;
@@ -25,21 +34,67 @@ export interface TeamsArgs {
25
34
  ask?: (question: string) => Promise<string>;
26
35
  askSecret?: (question: string) => Promise<string>;
27
36
  }
37
+ export interface Target {
38
+ source: string;
39
+ team: string;
40
+ product: string;
41
+ }
28
42
  /**
29
43
  * `teams` — a Source admin's view of its teams' integration setup, the
30
44
  * team-level counterpart of `apps`. `list` names the teams; `show` reads one
31
- * team's setup of one product as a checklist; `app set` enters a team's own
32
- * app (Zendesk's subdomain, client and signing secret) at the admin's
33
- * terminal — never from a flag, a file or a non-interactive run, so an
34
- * agent driving the CLI never handles a secret; `webhook` mints the URL the
45
+ * team's setup of one product as a checklist; `app set` enters or updates
46
+ * any of a team's own app fields (Zendesk's subdomain, client and signing
47
+ * secret), one at a time if need be — a field left alone keeps its saved
48
+ * value. Secrets are only ever typed at the admin's terminal, never from a
49
+ * flag, a file or a non-interactive run, so an agent driving the CLI never
50
+ * handles one; a non-secret field may be passed as `--set name=value`; `webhook` mints the URL the
35
51
  * team's provider sends to, shown once.
36
52
  */
37
53
  export declare function runTeams(ctx: CliContext, args: TeamsArgs): Promise<ExitCode>;
54
+ /** What `teams app set` was asked to change, from its flags. */
55
+ interface AppEdits {
56
+ /** `--field`: enter just these, at the terminal. */
57
+ only: string[];
58
+ /** `--set name=value`: non-secret values, no prompt. */
59
+ values: Record<string, string>;
60
+ /** `--clear name`: remove a saved value. */
61
+ clear: string[];
62
+ /** Whether anything is asked at the terminal. */
63
+ prompts: boolean;
64
+ }
65
+ /**
66
+ * The `teams app set` walkthrough and prompts for one team app, then the
67
+ * save — of only what was entered: a field left empty keeps its saved value.
68
+ * Shared with `teams connect`, which runs it inline when the integration
69
+ * needs the team's own app (Zendesk) before anyone can sign in.
70
+ */
71
+ export declare function promptAndSaveTeamApp(ctx: CliContext, args: TeamsArgs, client: TeamsClient, host: string, target: Target, status: TeamIntegrationStatus, app: TeamAppState, edits?: AppEdits): Promise<{
72
+ team: {
73
+ slug: string;
74
+ name: string;
75
+ };
76
+ product: {
77
+ slug: string;
78
+ name: string;
79
+ };
80
+ connectionId: string;
81
+ fields: {
82
+ secretName: string;
83
+ label: string;
84
+ description: string | null;
85
+ sensitive: boolean;
86
+ set: boolean;
87
+ value?: string | null | undefined;
88
+ }[];
89
+ }>;
38
90
  /** `teams show`, read top to bottom like a setup checklist. */
39
91
  export declare function checklist(status: TeamIntegrationStatus): string[];
92
+ export declare function chooseApp(status: TeamIntegrationStatus, connection: string | undefined): TeamAppState;
40
93
  export declare class TeamsClient {
41
94
  private readonly env;
42
95
  private session;
43
96
  constructor(env: AuthEnvironment, session: StoredSession);
44
97
  call<T>(send: (accessToken: string) => Promise<TeamsApiResult<T>>): Promise<T>;
45
98
  }
99
+ export declare function canPrompt(ctx: CliContext, args: TeamsArgs): boolean;
100
+ export {};
@@ -15,25 +15,28 @@ export const TEAMS_SUBCOMMANDS = [
15
15
  /**
16
16
  * `teams` — a Source admin's view of its teams' integration setup, the
17
17
  * team-level counterpart of `apps`. `list` names the teams; `show` reads one
18
- * team's setup of one product as a checklist; `app set` enters a team's own
19
- * app (Zendesk's subdomain, client and signing secret) at the admin's
20
- * terminal — never from a flag, a file or a non-interactive run, so an
21
- * agent driving the CLI never handles a secret; `webhook` mints the URL the
18
+ * team's setup of one product as a checklist; `app set` enters or updates
19
+ * any of a team's own app fields (Zendesk's subdomain, client and signing
20
+ * secret), one at a time if need be — a field left alone keeps its saved
21
+ * value. Secrets are only ever typed at the admin's terminal, never from a
22
+ * flag, a file or a non-interactive run, so an agent driving the CLI never
23
+ * handles one; a non-secret field may be passed as `--set name=value`; `webhook` mints the URL the
22
24
  * team's provider sends to, shown once.
23
25
  */
24
26
  export async function runTeams(ctx, args) {
27
+ var _a;
25
28
  const [rawSubcommand, ...rest] = args.positionals;
26
29
  if (isTeamAgentArea(rawSubcommand)) {
27
30
  return runTeamAgentCommands(ctx, args);
28
31
  }
29
32
  const subcommand = parseSubcommand(rawSubcommand);
30
33
  if (isOnboardingSubcommand(subcommand)) {
31
- const plan = parseOnboarding(subcommand, rest, args);
34
+ const plan = parseOnboarding(subcommand, rest, Object.assign(Object.assign({}, args), { as: typeof ((_a = args.flags) === null || _a === void 0 ? void 0 : _a.as) === 'string' ? args.flags.as : undefined }));
32
35
  const env = resolveAuthEnvironment(ctx, args.host, args.env, {
33
36
  projectDir: args.dir,
34
37
  });
35
38
  const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
36
- return runOnboarding(ctx, new TeamsClient(env, resolved.session), env.host, resolved.source, plan);
39
+ return runOnboarding(ctx, args, new TeamsClient(env, resolved.session), env.host, resolved.source, plan);
37
40
  }
38
41
  if (subcommand === 'app' && rest[0] !== 'set') {
39
42
  throw usageError(rest[0]
@@ -47,11 +50,12 @@ export async function runTeams(ctx, args) {
47
50
  const team = subcommand === 'list' ? '' : requireSlug(operands[0], 'team');
48
51
  const product = subcommand === 'list' ? '' : requireSlug(operands[1], 'product');
49
52
  const extra = operands.slice(subcommand === 'webhook' ? 3 : 2);
53
+ const appEdits = subcommand === 'app' ? parseAppEdits(args.flags) : null;
50
54
  if (subcommand !== 'list' && extra.length > 0) {
51
55
  throw usageError(`Unexpected argument "${extra[0]}".`, 'See "ekanos teams --help".');
52
56
  }
53
- if (subcommand === 'app' && !canPrompt(ctx, args)) {
54
- throw usageError("teams app set takes the team's secrets at a terminal, never in a script or an agent run.", `Run it yourself in your terminal: npx @ekanos/cli teams app set ${team} ${product}${args.connection ? ` --connection ${args.connection}` : ''}${args.source ? ` --source ${args.source}` : ''}`);
57
+ if ((appEdits === null || appEdits === void 0 ? void 0 : appEdits.prompts) && !canPrompt(ctx, args)) {
58
+ throw usageError("teams app set takes the team's secrets at a terminal, never in a script or an agent run. Non-secret fields can be passed with --set <name>=<value>.", `Run it yourself in your terminal: npx @ekanos/cli teams app set ${team} ${product}${args.connection ? ` --connection ${args.connection}` : ''}${args.source ? ` --source ${args.source}` : ''}`);
55
59
  }
56
60
  const env = resolveAuthEnvironment(ctx, args.host, args.env, {
57
61
  projectDir: args.dir,
@@ -85,51 +89,141 @@ export async function runTeams(ctx, args) {
85
89
  if (!status.enabled) {
86
90
  throw invalidStateError(`"${product}" is not enabled on ${status.source.slug}.`, '"ekanos catalog" lists what this Source has; Ekanos enables a product for a Source.');
87
91
  }
88
- if (subcommand === 'app') {
89
- return setApp(ctx, args, client, env.host, target, status);
92
+ if (subcommand === 'app' && appEdits) {
93
+ return setApp(ctx, args, client, env.host, target, status, appEdits);
90
94
  }
91
95
  return mintWebhook(ctx, args, client, env.host, target, status, operands[2]);
92
96
  }
93
- async function setApp(ctx, args, client, host, target, status) {
94
- var _a, _b;
95
- const app = chooseApp(status, args.connection);
96
- ctx.log(appWalkthrough(status, app).join('\n'));
97
- const fields = {};
98
- for (const field of app.fields) {
99
- const keep = field.set ? ' (set — Enter keeps it)' : '';
100
- const question = field.sensitive
101
- ? `${field.label}${keep} (hidden): `
102
- : `${field.label}${keep}: `;
103
- const value = (await (field.sensitive
104
- ? ((_a = args.askSecret) !== null && _a !== void 0 ? _a : promptHidden)
105
- : ((_b = args.ask) !== null && _b !== void 0 ? _b : promptLine))(question)).trim();
106
- if (value)
107
- fields[field.secretName] = value;
108
- }
109
- const missing = app.fields.filter((field) => !field.set && !fields[field.secretName]);
110
- if (missing.length > 0) {
111
- throw validationError(`Every field is needed: ${missing.map((field) => field.label).join(', ')}.`, `Run "ekanos teams app set ${target.team} ${target.product}" again.`);
112
- }
113
- if (Object.keys(fields).length === 0) {
114
- throw validationError('Nothing was changed.', `Run "ekanos teams show ${target.team} ${target.product}" to see the current state.`);
97
+ function parseAppEdits(flags) {
98
+ const list = (name) => {
99
+ const raw = flags === null || flags === void 0 ? void 0 : flags[name];
100
+ if (raw === undefined || raw === false)
101
+ return [];
102
+ if (Array.isArray(raw))
103
+ return raw;
104
+ return typeof raw === 'string' ? [raw] : [];
105
+ };
106
+ const values = {};
107
+ for (const pair of list('set')) {
108
+ const at = pair.indexOf('=');
109
+ if (at <= 0) {
110
+ throw usageError(`--set "${pair}" is not <field>=<value>.`, 'For example: --set subdomain=acme. Secrets are never passed as arguments: use --field <name> and enter it at the prompt.');
111
+ }
112
+ values[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();
115
113
  }
116
- const result = await client.call((token) => setTeamApp(host, token, target, {
117
- connectionId: app.connectionId,
118
- fields,
119
- }));
120
- const next = nextSteps(status, app, target);
114
+ const only = list('field');
115
+ const clear = list('clear');
116
+ return {
117
+ only,
118
+ values,
119
+ clear,
120
+ // Bare `app set` walks every field; `--field` asks the named ones.
121
+ prompts: only.length > 0 ||
122
+ (Object.keys(values).length === 0 && clear.length === 0),
123
+ };
124
+ }
125
+ async function setApp(ctx, args, client, host, target, status, edits) {
126
+ const app = chooseApp(status, args.connection);
127
+ const result = await promptAndSaveTeamApp(ctx, args, client, host, target, status, app, edits);
128
+ const missing = result.fields.filter((field) => !field.set);
129
+ const next = [
130
+ ...(missing.length > 0
131
+ ? [
132
+ `Still not set: ${missing.map((field) => field.label).join(', ')} — a team member can't connect until it is. Run "ekanos teams app set ${target.team} ${target.product} ${missing.map((field) => `--field ${field.secretName}`).join(' ')}".`,
133
+ ]
134
+ : []),
135
+ ...nextSteps(status, app, target),
136
+ ];
121
137
  return ctx.succeed(Object.assign({ host }, result), ctx.ui.result({
122
138
  title: `Saved ${target.team}'s ${result.product.name} app`,
123
139
  rows: [
124
140
  ['Source', status.source.slug],
125
- [
126
- 'Fields',
127
- result.fields.map((field) => `${mark(field.set)} ${field.label}`),
128
- ],
141
+ ['Fields', result.fields.map(fieldLine)],
129
142
  ],
130
143
  notes: next,
131
144
  }));
132
145
  }
146
+ /** Every field, asked in turn: bare `teams app set`, and `teams connect`. */
147
+ const WALK_EVERY_FIELD = {
148
+ only: [],
149
+ values: {},
150
+ clear: [],
151
+ prompts: true,
152
+ };
153
+ /**
154
+ * The `teams app set` walkthrough and prompts for one team app, then the
155
+ * save — of only what was entered: a field left empty keeps its saved value.
156
+ * Shared with `teams connect`, which runs it inline when the integration
157
+ * needs the team's own app (Zendesk) before anyone can sign in.
158
+ */
159
+ export async function promptAndSaveTeamApp(ctx, args, client, host, target, status, app, edits = WALK_EVERY_FIELD) {
160
+ var _a;
161
+ var _b, _c;
162
+ const known = new Map(app.fields.map((field) => [field.secretName, field]));
163
+ const names = app.fields.map((field) => field.secretName).join(', ');
164
+ for (const name of [
165
+ ...edits.only,
166
+ ...Object.keys(edits.values),
167
+ ...edits.clear,
168
+ ]) {
169
+ if (!known.has(name)) {
170
+ throw usageError(`${status.product.name}'s team app has no field "${name}".`, `Its fields are: ${names}.`);
171
+ }
172
+ }
173
+ for (const name of Object.keys(edits.values)) {
174
+ if ((_a = known.get(name)) === null || _a === void 0 ? void 0 : _a.sensitive) {
175
+ throw usageError(`"${name}" is a secret: it is never passed as an argument.`, `Enter it at the prompt: ekanos teams app set ${target.team} ${target.product} --field ${name}`);
176
+ }
177
+ }
178
+ const fields = Object.assign({}, edits.values);
179
+ if (edits.prompts) {
180
+ const toAsk = edits.only.length > 0
181
+ ? app.fields.filter((field) => edits.only.includes(field.secretName))
182
+ : app.fields;
183
+ if (edits.only.length === 0)
184
+ ctx.log(appWalkthrough(status, app).join('\n'));
185
+ else {
186
+ ctx.log(`${status.product.name} app for team ${target.team} on ${status.source.slug}`);
187
+ }
188
+ for (const field of toAsk) {
189
+ const value = (await (field.sensitive
190
+ ? ((_b = args.askSecret) !== null && _b !== void 0 ? _b : promptHidden)
191
+ : ((_c = args.ask) !== null && _c !== void 0 ? _c : promptLine))(fieldQuestion(field))).trim();
192
+ if (value)
193
+ fields[field.secretName] = value;
194
+ }
195
+ }
196
+ if (Object.keys(fields).length === 0 && edits.clear.length === 0) {
197
+ throw validationError('Nothing was changed.', `Run "ekanos teams show ${target.team} ${target.product}" to see the current state.`);
198
+ }
199
+ return client.call((token) => setTeamApp(host, token, target, Object.assign({ connectionId: app.connectionId, fields }, (edits.clear.length > 0 ? { clear: edits.clear } : {}))));
200
+ }
201
+ /** The prompt for one field: what is saved, and that Enter keeps it. */
202
+ function fieldQuestion(field) {
203
+ const name = `${field.label} [${field.secretName}]`;
204
+ if (field.sensitive) {
205
+ return field.set
206
+ ? `${name} (saved — Enter to keep, hidden): `
207
+ : `${name} (hidden): `;
208
+ }
209
+ if (field.set) {
210
+ return field.value
211
+ ? `${name} (current: ${field.value} — Enter to keep): `
212
+ : `${name} (saved — Enter to keep): `;
213
+ }
214
+ return `${name}: `;
215
+ }
216
+ /** One field's state: a secret only as saved / not set, never its value. */
217
+ function fieldLine(field) {
218
+ const state = !field.set
219
+ ? 'not set'
220
+ : field.sensitive
221
+ ? 'saved'
222
+ : field.value
223
+ ? field.value
224
+ : 'saved';
225
+ return `${mark(field.set)} ${field.label} (${field.secretName}): ${state}`;
226
+ }
133
227
  async function mintWebhook(ctx, args, client, host, target, status, requested) {
134
228
  const webhooks = status.teamApps.flatMap((app) => app.webhooks);
135
229
  if (webhooks.length === 0) {
@@ -190,10 +284,11 @@ export function checklist(status) {
190
284
  if (app.guideUrl)
191
285
  lines.push(` Guide: ${app.guideUrl}`);
192
286
  for (const field of app.fields) {
193
- lines.push(` ${mark(field.set)} ${field.label}`);
287
+ lines.push(` ${fieldLine(field)}`);
194
288
  }
195
- if (app.fields.some((field) => !field.set)) {
196
- lines.push(` → in your terminal: npx @ekanos/cli teams app set ${target.team} ${target.product} --connection ${app.connectionId} --source ${status.source.slug}`);
289
+ const unset = app.fields.filter((field) => !field.set);
290
+ if (unset.length > 0) {
291
+ lines.push(` → in your terminal: npx @ekanos/cli teams app set ${target.team} ${target.product} --connection ${app.connectionId} ${unset.map((field) => `--field ${field.secretName}`).join(' ')} --source ${status.source.slug}`);
197
292
  }
198
293
  for (const webhook of app.webhooks) {
199
294
  lines.push(webhook.endpoint
@@ -245,7 +340,7 @@ function nextSteps(status, app, target) {
245
340
  : [`Then a team member clicks Connect at ${status.connectUrl}`]),
246
341
  ];
247
342
  }
248
- function chooseApp(status, connection) {
343
+ export function chooseApp(status, connection) {
249
344
  const apps = status.teamApps;
250
345
  if (apps.length === 0) {
251
346
  throw invalidStateError(`${status.product.name} takes no team app: teams connect through the Source's app, or need none.`, `Run "ekanos apps show ${status.product.slug}" for the Source's app.`);
@@ -277,7 +372,7 @@ export class TeamsClient {
277
372
  throw authRequiredError(`The session for ${this.env.host} could not authorize the teams call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
278
373
  }
279
374
  }
280
- function canPrompt(ctx, args) {
375
+ export function canPrompt(ctx, args) {
281
376
  var _a;
282
377
  return !ctx.jsonMode && ((_a = args.stdinIsTTY) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true);
283
378
  }