@ekanos/cli 0.10.0 → 0.12.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.
- package/README.md +1 -0
- package/dist/commands/agents-edit.d.ts +49 -0
- package/dist/commands/agents-edit.js +385 -0
- package/dist/commands/agents-edit.js.map +1 -0
- package/dist/commands/agents.d.ts +24 -1
- package/dist/commands/agents.js +117 -8
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/apps.d.ts +6 -2
- package/dist/commands/apps.js +249 -4
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/reviewers.d.ts +21 -0
- package/dist/commands/reviewers.js +162 -0
- package/dist/commands/reviewers.js.map +1 -0
- package/dist/commands/routes.d.ts +23 -0
- package/dist/commands/routes.js +277 -0
- package/dist/commands/routes.js.map +1 -0
- package/dist/commands/runs.d.ts +42 -0
- package/dist/commands/runs.js +362 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/skills.d.ts +60 -0
- package/dist/commands/skills.js +397 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/team-agents-agents.d.ts +9 -0
- package/dist/commands/team-agents-agents.js +188 -0
- package/dist/commands/team-agents-agents.js.map +1 -0
- package/dist/commands/team-agents-mcp.d.ts +4 -0
- package/dist/commands/team-agents-mcp.js +218 -0
- package/dist/commands/team-agents-mcp.js.map +1 -0
- package/dist/commands/team-agents-triggers.d.ts +12 -0
- package/dist/commands/team-agents-triggers.js +251 -0
- package/dist/commands/team-agents-triggers.js.map +1 -0
- package/dist/commands/team-agents.d.ts +31 -0
- package/dist/commands/team-agents.js +71 -0
- package/dist/commands/team-agents.js.map +1 -0
- package/dist/commands/teams-onboard.d.ts +51 -0
- package/dist/commands/teams-onboard.js +261 -0
- package/dist/commands/teams-onboard.js.map +1 -0
- package/dist/commands/teams.d.ts +16 -1
- package/dist/commands/teams.js +22 -3
- package/dist/commands/teams.js.map +1 -1
- package/dist/connections-api.d.ts +125 -0
- package/dist/connections-api.js +156 -0
- package/dist/connections-api.js.map +1 -0
- package/dist/index.js +226 -6
- package/dist/index.js.map +1 -1
- package/dist/reviewers-api.d.ts +33 -0
- package/dist/reviewers-api.js +81 -0
- package/dist/reviewers-api.js.map +1 -0
- package/dist/routes-api.d.ts +105 -0
- package/dist/routes-api.js +147 -0
- package/dist/routes-api.js.map +1 -0
- package/dist/runs-api.d.ts +34 -0
- package/dist/runs-api.js +112 -0
- package/dist/runs-api.js.map +1 -0
- package/dist/skills-api.d.ts +28 -0
- package/dist/skills-api.js +104 -0
- package/dist/skills-api.js.map +1 -0
- package/dist/teams-api.d.ts +45 -3
- package/dist/teams-api.js +72 -3
- package/dist/teams-api.js.map +1 -1
- package/package.json +2 -2
- package/templates/package.json.tmpl +4 -4
- package/templates/skills/build-agent/SKILL.md +94 -10
- package/templates/skills/build-agent/references/agent-json.md +85 -4
- package/templates/skills/build-agent/references/rollout.md +9 -1
- package/templates/skills/build-agent/references/set-up-an-integration.md +59 -3
- package/templates/skills/build-agent/references/skills.md +81 -0
- package/templates/skills/build-agent/references/triggers.md +25 -5
- package/templates/skills/ekanos-platform/SKILL.md +116 -5
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CliContext } from '../context.js';
|
|
2
|
+
import type { ExitCode } from '../exit-codes.js';
|
|
3
|
+
export declare const ROUTES_SUBCOMMANDS: readonly ['list', 'options', 'set', 'remove', 'webhook', 'unrouted', 'replay'];
|
|
4
|
+
export interface RoutesArgs {
|
|
5
|
+
host?: string;
|
|
6
|
+
source?: string;
|
|
7
|
+
/** `list [product]`, `set <product> <value> <team>`, `replay <event-id>`, … */
|
|
8
|
+
positionals: readonly string[];
|
|
9
|
+
label?: string;
|
|
10
|
+
webhook?: string;
|
|
11
|
+
dir: string;
|
|
12
|
+
env: Record<string, string | undefined>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `routes` — a Source admin's routing of a Source connection's events to
|
|
16
|
+
* teams (plan 241.4). One Zendesk connected for the whole Source sends every
|
|
17
|
+
* ticket to one webhook; each ticket's organization decides which team's
|
|
18
|
+
* agents see it. `set` maps a value to a team, `remove` unmaps it, `options`
|
|
19
|
+
* lists the values the integration knows (Zendesk's organizations),
|
|
20
|
+
* `webhook` mints the Source's endpoint URL (shown once), and `unrouted` /
|
|
21
|
+
* `replay` deal with events no team was mapped to — they started nothing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runRoutes(ctx: CliContext, args: RoutesArgs): Promise<ExitCode>;
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
|
|
2
|
+
import { authRequiredError, usageError } from '../errors.js';
|
|
3
|
+
import { ReplaySchema, RouteListSchema, RouteOptionsSchema, RouteRemoveSchema, RouteSetSchema, RouteWebhookSchema, UnroutedListSchema, routesCall, } from '../routes-api.js';
|
|
4
|
+
import { resolveSourceTarget } from '../source-target.js';
|
|
5
|
+
export const ROUTES_SUBCOMMANDS = [
|
|
6
|
+
'list',
|
|
7
|
+
'options',
|
|
8
|
+
'set',
|
|
9
|
+
'remove',
|
|
10
|
+
'webhook',
|
|
11
|
+
'unrouted',
|
|
12
|
+
'replay',
|
|
13
|
+
];
|
|
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>.';
|
|
15
|
+
/**
|
|
16
|
+
* `routes` — a Source admin's routing of a Source connection's events to
|
|
17
|
+
* teams (plan 241.4). One Zendesk connected for the whole Source sends every
|
|
18
|
+
* ticket to one webhook; each ticket's organization decides which team's
|
|
19
|
+
* agents see it. `set` maps a value to a team, `remove` unmaps it, `options`
|
|
20
|
+
* lists the values the integration knows (Zendesk's organizations),
|
|
21
|
+
* `webhook` mints the Source's endpoint URL (shown once), and `unrouted` /
|
|
22
|
+
* `replay` deal with events no team was mapped to — they started nothing.
|
|
23
|
+
*/
|
|
24
|
+
export async function runRoutes(ctx, args) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
const [rawSubcommand, ...operands] = args.positionals;
|
|
27
|
+
const subcommand = parseSubcommand(rawSubcommand);
|
|
28
|
+
const expected = OPERANDS[subcommand];
|
|
29
|
+
if (operands.length < expected.min || operands.length > expected.max) {
|
|
30
|
+
throw usageError(`routes ${subcommand} takes ${expected.usage}.`, `e.g. "ekanos routes ${subcommand} ${expected.example}".`);
|
|
31
|
+
}
|
|
32
|
+
const env = resolveAuthEnvironment(ctx, args.host, args.env, {
|
|
33
|
+
projectDir: args.dir,
|
|
34
|
+
});
|
|
35
|
+
const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
|
|
36
|
+
const client = new RoutesClient(env, resolved.session);
|
|
37
|
+
const source = resolved.source;
|
|
38
|
+
const send = (schema, init) => client.call((token) => routesCall(env.host, token, schema, Object.assign(Object.assign({}, init), { query: Object.assign({ source }, init.query) })));
|
|
39
|
+
switch (subcommand) {
|
|
40
|
+
case 'list': {
|
|
41
|
+
const product = operands[0] ? requireSlug(operands[0]) : undefined;
|
|
42
|
+
const list = await send(RouteListSchema, {
|
|
43
|
+
method: 'GET',
|
|
44
|
+
query: { product },
|
|
45
|
+
});
|
|
46
|
+
if (list.routes.length === 0) {
|
|
47
|
+
return ctx.succeed(Object.assign({ host: env.host }, list), ctx.ui.result({
|
|
48
|
+
title: `No routes on ${list.source.slug}${product ? ` for ${product}` : ''}`,
|
|
49
|
+
tone: 'warn',
|
|
50
|
+
notes: [
|
|
51
|
+
"Events on the Source's endpoint stay unrouted — they start nothing — until their value is mapped to a team.",
|
|
52
|
+
'Map one: "ekanos routes set <product> <value> <team>".',
|
|
53
|
+
],
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
return ctx.succeed(Object.assign({ host: env.host }, list), [
|
|
57
|
+
ctx.ui.bold(`Routes on ${list.source.slug}`),
|
|
58
|
+
ctx.ui.table(['Product', 'Key', 'Value', 'Label', 'Team'], list.routes.map(routeRow)),
|
|
59
|
+
].join('\n'));
|
|
60
|
+
}
|
|
61
|
+
case 'options': {
|
|
62
|
+
const product = requireSlug(operands[0]);
|
|
63
|
+
const result = await send(RouteOptionsSchema, {
|
|
64
|
+
method: 'GET',
|
|
65
|
+
path: `/${encodeURIComponent(product)}/options`,
|
|
66
|
+
query: {},
|
|
67
|
+
});
|
|
68
|
+
if (!result.options) {
|
|
69
|
+
return ctx.succeed(Object.assign({ host: env.host, product }, result), ctx.ui.result({
|
|
70
|
+
title: `${product} does not list its ${result.key} values`,
|
|
71
|
+
tone: 'warn',
|
|
72
|
+
notes: [
|
|
73
|
+
`Type the ${result.key} id: "ekanos routes set ${product} <value> <team>".`,
|
|
74
|
+
],
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
return ctx.succeed(Object.assign({ host: env.host, product }, result), [
|
|
78
|
+
ctx.ui.bold(`${product} ${result.key} values`),
|
|
79
|
+
ctx.ui.table(['Value', 'Label'], result.options.map((option) => [option.value, option.label])),
|
|
80
|
+
].join('\n'));
|
|
81
|
+
}
|
|
82
|
+
case 'set': {
|
|
83
|
+
const product = requireSlug(operands[0]);
|
|
84
|
+
const value = requireValue(operands[1]);
|
|
85
|
+
const team = requireSlug(operands[2], 'team');
|
|
86
|
+
const result = await send(RouteSetSchema, {
|
|
87
|
+
method: 'PUT',
|
|
88
|
+
path: `/${encodeURIComponent(product)}`,
|
|
89
|
+
query: {},
|
|
90
|
+
body: Object.assign({ value, team }, (args.label ? { label: args.label } : {})),
|
|
91
|
+
});
|
|
92
|
+
return ctx.succeed(Object.assign({ host: env.host }, result), ctx.ui.result({
|
|
93
|
+
title: `Routed ${product} ${result.route.key} ${value} to ${(_a = result.route.team.slug) !== null && _a !== void 0 ? _a : result.route.team.name}`,
|
|
94
|
+
rows: [
|
|
95
|
+
['Source', source],
|
|
96
|
+
['Label', (_b = result.route.label) !== null && _b !== void 0 ? _b : '—'],
|
|
97
|
+
[
|
|
98
|
+
'Replaced',
|
|
99
|
+
result.replaced ? 'yes (moved from another team)' : 'no',
|
|
100
|
+
],
|
|
101
|
+
],
|
|
102
|
+
notes: [
|
|
103
|
+
'New events with this value start that team\'s agents. Earlier unrouted ones: "ekanos routes unrouted" then "ekanos routes replay <event-id>".',
|
|
104
|
+
],
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
case 'remove': {
|
|
108
|
+
const product = requireSlug(operands[0]);
|
|
109
|
+
const value = requireValue(operands[1]);
|
|
110
|
+
const result = await send(RouteRemoveSchema, {
|
|
111
|
+
method: 'DELETE',
|
|
112
|
+
path: `/${encodeURIComponent(product)}`,
|
|
113
|
+
query: { value },
|
|
114
|
+
});
|
|
115
|
+
return ctx.succeed(Object.assign({ host: env.host }, result), ctx.ui.result({
|
|
116
|
+
title: `Unmapped ${product} ${value}`,
|
|
117
|
+
rows: [['Source', source]],
|
|
118
|
+
notes: ['Its events now stay unrouted and start nothing.'],
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
case 'webhook': {
|
|
122
|
+
const product = requireSlug(operands[0]);
|
|
123
|
+
const result = await send(RouteWebhookSchema, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
path: `/${encodeURIComponent(product)}/webhook`,
|
|
126
|
+
query: { webhook: args.webhook },
|
|
127
|
+
});
|
|
128
|
+
return ctx.succeed(Object.assign({ host: env.host, product }, result), ctx.ui.result({
|
|
129
|
+
title: `Webhook URL for ${product} on ${source} (shown once)`,
|
|
130
|
+
rows: [
|
|
131
|
+
['Webhook', result.webhookId],
|
|
132
|
+
['URL', result.url],
|
|
133
|
+
],
|
|
134
|
+
notes: [
|
|
135
|
+
'Point the vendor at this URL once, for the whole Source. Minting again revokes the previous URL.',
|
|
136
|
+
...(result.signature
|
|
137
|
+
? [
|
|
138
|
+
`Deliveries must be signed (${result.signature.header}); the Source connection needs the "${result.signature.secretName}" secret.`,
|
|
139
|
+
]
|
|
140
|
+
: []),
|
|
141
|
+
],
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
case 'unrouted': {
|
|
145
|
+
const product = operands[0] ? requireSlug(operands[0]) : undefined;
|
|
146
|
+
const result = await send(UnroutedListSchema, {
|
|
147
|
+
method: 'GET',
|
|
148
|
+
path: '/unrouted',
|
|
149
|
+
query: { product },
|
|
150
|
+
});
|
|
151
|
+
if (result.events.length === 0) {
|
|
152
|
+
return ctx.succeed(Object.assign({ host: env.host }, result), ctx.ui.result({ title: 'No unrouted events', tone: 'ok' }));
|
|
153
|
+
}
|
|
154
|
+
return ctx.succeed(Object.assign({ host: env.host }, result), [
|
|
155
|
+
ctx.ui.bold('Unrouted events (no team mapped; nothing started)'),
|
|
156
|
+
ctx.ui.table(['Id', 'Product', 'Event', 'Value', 'Received'], result.events.map((event) => { var _a; return [
|
|
157
|
+
event.id,
|
|
158
|
+
event.product,
|
|
159
|
+
event.eventId,
|
|
160
|
+
event.routeValue
|
|
161
|
+
? `${(_a = event.routeKey) !== null && _a !== void 0 ? _a : ''} ${event.routeValue}`.trim()
|
|
162
|
+
: '(none)',
|
|
163
|
+
event.createdAt,
|
|
164
|
+
]; })),
|
|
165
|
+
'Map the value with "ekanos routes set", then "ekanos routes replay <id>".',
|
|
166
|
+
].join('\n'));
|
|
167
|
+
}
|
|
168
|
+
case 'replay': {
|
|
169
|
+
const id = requireEventId(operands[0]);
|
|
170
|
+
const result = await send(ReplaySchema, {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
path: `/unrouted/${encodeURIComponent(id)}/replay`,
|
|
173
|
+
query: {},
|
|
174
|
+
});
|
|
175
|
+
return ctx.succeed(Object.assign({ host: env.host, id }, result), ctx.ui.result({
|
|
176
|
+
title: result.status === 'routed'
|
|
177
|
+
? `Routed ${id}`
|
|
178
|
+
: result.status === 'already-routed'
|
|
179
|
+
? `${id} was already routed`
|
|
180
|
+
: `${id} is still unrouted`,
|
|
181
|
+
tone: result.status === 'unrouted' ? 'warn' : 'ok',
|
|
182
|
+
notes: result.status === 'unrouted'
|
|
183
|
+
? [
|
|
184
|
+
'No team is mapped to its value yet: "ekanos routes set" first.',
|
|
185
|
+
]
|
|
186
|
+
: [],
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const OPERANDS = {
|
|
192
|
+
list: { min: 0, max: 1, usage: 'an optional product', example: 'zendesk' },
|
|
193
|
+
options: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },
|
|
194
|
+
set: {
|
|
195
|
+
min: 3,
|
|
196
|
+
max: 3,
|
|
197
|
+
usage: '<product> <value> <team>',
|
|
198
|
+
example: 'zendesk 360001234567 first-bank',
|
|
199
|
+
},
|
|
200
|
+
remove: {
|
|
201
|
+
min: 2,
|
|
202
|
+
max: 2,
|
|
203
|
+
usage: '<product> <value>',
|
|
204
|
+
example: 'zendesk 360001234567',
|
|
205
|
+
},
|
|
206
|
+
webhook: { min: 1, max: 1, usage: 'a product', example: 'zendesk' },
|
|
207
|
+
unrouted: {
|
|
208
|
+
min: 0,
|
|
209
|
+
max: 1,
|
|
210
|
+
usage: 'an optional product',
|
|
211
|
+
example: 'zendesk',
|
|
212
|
+
},
|
|
213
|
+
replay: {
|
|
214
|
+
min: 1,
|
|
215
|
+
max: 1,
|
|
216
|
+
usage: 'an event id from "ekanos routes unrouted"',
|
|
217
|
+
example: '<event-id>',
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
function routeRow(route) {
|
|
221
|
+
var _a, _b;
|
|
222
|
+
return [
|
|
223
|
+
route.product,
|
|
224
|
+
route.key,
|
|
225
|
+
route.value,
|
|
226
|
+
(_a = route.label) !== null && _a !== void 0 ? _a : '—',
|
|
227
|
+
(_b = route.team.slug) !== null && _b !== void 0 ? _b : route.team.name,
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
class RoutesClient {
|
|
231
|
+
constructor(env, session) {
|
|
232
|
+
this.env = env;
|
|
233
|
+
this.session = session;
|
|
234
|
+
}
|
|
235
|
+
async call(send) {
|
|
236
|
+
let result = await send(this.session.accessToken);
|
|
237
|
+
if (result.status === 'unauthorized') {
|
|
238
|
+
this.session = await refreshStoredSession(this.env, this.session);
|
|
239
|
+
result = await send(this.session.accessToken);
|
|
240
|
+
}
|
|
241
|
+
if (result.status === 'ok')
|
|
242
|
+
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.`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function parseSubcommand(value) {
|
|
247
|
+
const match = ROUTES_SUBCOMMANDS.find((name) => name === value);
|
|
248
|
+
if (match)
|
|
249
|
+
return match;
|
|
250
|
+
throw usageError(value
|
|
251
|
+
? `Unknown routes subcommand "${value}".`
|
|
252
|
+
: 'routes needs a subcommand.', USAGE);
|
|
253
|
+
}
|
|
254
|
+
function requireSlug(value, what = 'product') {
|
|
255
|
+
var _a;
|
|
256
|
+
const slug = (_a = value === null || value === void 0 ? void 0 : value.trim().toLowerCase()) !== null && _a !== void 0 ? _a : '';
|
|
257
|
+
if (/^[a-z0-9][a-z0-9-]{0,63}$/.test(slug))
|
|
258
|
+
return slug;
|
|
259
|
+
throw usageError(`"${value !== null && value !== void 0 ? value : ''}" is not a ${what} slug.`, what === 'team'
|
|
260
|
+
? 'Run "ekanos teams list" to see the teams on this Source.'
|
|
261
|
+
: 'Run "ekanos catalog" to see what the Source has.');
|
|
262
|
+
}
|
|
263
|
+
function requireValue(value) {
|
|
264
|
+
var _a;
|
|
265
|
+
const trimmed = (_a = value === null || value === void 0 ? void 0 : value.trim()) !== null && _a !== void 0 ? _a : '';
|
|
266
|
+
if (trimmed.length > 0 && trimmed.length <= 200)
|
|
267
|
+
return trimmed;
|
|
268
|
+
throw usageError('A route value is 1 to 200 characters.', '"ekanos routes options <product>" lists the values the integration knows.');
|
|
269
|
+
}
|
|
270
|
+
function requireEventId(value) {
|
|
271
|
+
var _a;
|
|
272
|
+
const id = (_a = value === null || value === void 0 ? void 0 : value.trim()) !== null && _a !== void 0 ? _a : '';
|
|
273
|
+
if (/^[0-9a-f-]{36}$/i.test(id))
|
|
274
|
+
return id;
|
|
275
|
+
throw usageError(`"${value !== null && value !== void 0 ? value : ''}" is not an event id.`, '"ekanos routes unrouted" lists the events waiting for a mapping.');
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 {\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"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CliContext } from '../context.js';
|
|
2
|
+
import type { ExitCode } from '../exit-codes.js';
|
|
3
|
+
export declare const RUNS_SUBCOMMANDS: readonly ['start', 'list', 'show'];
|
|
4
|
+
export declare const APPROVALS_SUBCOMMANDS: readonly ['list', 'approve', 'reject', 'edit'];
|
|
5
|
+
export interface RunsArgs {
|
|
6
|
+
host?: string;
|
|
7
|
+
source?: string;
|
|
8
|
+
positionals: readonly string[];
|
|
9
|
+
team?: string;
|
|
10
|
+
agent?: string;
|
|
11
|
+
prompt?: string;
|
|
12
|
+
input?: string;
|
|
13
|
+
follow: boolean;
|
|
14
|
+
reason?: string;
|
|
15
|
+
body?: string;
|
|
16
|
+
dir: string;
|
|
17
|
+
env: Record<string, string | undefined>;
|
|
18
|
+
/** Test seam for the `--follow` poll delay. */
|
|
19
|
+
sleep?: (ms: number) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `runs` — run, list and inspect a team's agents as its Source admin. A run
|
|
23
|
+
* started here is the app's "Run now": it runs as the agent's creator under
|
|
24
|
+
* the agent's own grant, policy and approvals (writes still wait for one),
|
|
25
|
+
* and is attributed to you.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runRuns(ctx: CliContext, args: RunsArgs): Promise<ExitCode>;
|
|
28
|
+
/**
|
|
29
|
+
* `agents test <key> --team <slug>`: run the template's latest version — its
|
|
30
|
+
* draft, published or not — on one team, without publishing it. Marked as a
|
|
31
|
+
* 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.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runAgentTest(ctx: CliContext, args: RunsArgs & {
|
|
35
|
+
key: string | undefined;
|
|
36
|
+
}): Promise<ExitCode>;
|
|
37
|
+
/**
|
|
38
|
+
* `approvals` — what a team's agent runs are waiting on, and deciding it.
|
|
39
|
+
* Decided with the authority a team's agent manager has in the app, and
|
|
40
|
+
* recorded as your decision.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runApprovals(ctx: CliContext, args: RunsArgs): Promise<ExitCode>;
|