@ekanos/cli 0.13.0 → 0.15.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 +25 -8
- package/dist/agents-api.d.ts +11 -2
- package/dist/agents-api.js +39 -3
- package/dist/agents-api.js.map +1 -1
- package/dist/apps-api.js +2 -2
- package/dist/apps-api.js.map +1 -1
- package/dist/auth/ekanos-api.d.ts +16 -2
- package/dist/auth/ekanos-api.js +101 -15
- package/dist/auth/ekanos-api.js.map +1 -1
- package/dist/auth/host-trust.d.ts +46 -0
- package/dist/auth/host-trust.js +79 -0
- package/dist/auth/host-trust.js.map +1 -0
- package/dist/auth/session.d.ts +10 -1
- package/dist/auth/session.js +28 -28
- package/dist/auth/session.js.map +1 -1
- package/dist/commands/agents-edit.d.ts +6 -2
- package/dist/commands/agents-edit.js +57 -26
- package/dist/commands/agents-edit.js.map +1 -1
- package/dist/commands/agents.d.ts +13 -2
- package/dist/commands/agents.js +140 -35
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/apps.d.ts +2 -0
- package/dist/commands/apps.js +16 -12
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/catalog.js +7 -6
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/knowledge.d.ts +19 -0
- package/dist/commands/knowledge.js +119 -27
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.js +30 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +9 -1
- package/dist/commands/logout.js +56 -7
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/publish.js +3 -3
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/reviewers.d.ts +2 -0
- package/dist/commands/reviewers.js +7 -3
- package/dist/commands/reviewers.js.map +1 -1
- package/dist/commands/routes.d.ts +2 -0
- package/dist/commands/routes.js +42 -9
- package/dist/commands/routes.js.map +1 -1
- package/dist/commands/runs.d.ts +13 -2
- package/dist/commands/runs.js +99 -16
- package/dist/commands/runs.js.map +1 -1
- package/dist/commands/scope-notice.d.ts +21 -0
- package/dist/commands/scope-notice.js +35 -0
- package/dist/commands/scope-notice.js.map +1 -0
- package/dist/commands/skills.d.ts +7 -1
- package/dist/commands/skills.js +80 -24
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/sources.d.ts +1 -1
- package/dist/commands/sources.js +126 -12
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/status.js +5 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/team-agent-readiness.d.ts +49 -0
- package/dist/commands/team-agent-readiness.js +308 -0
- package/dist/commands/team-agent-readiness.js.map +1 -0
- package/dist/commands/team-agents-agents.d.ts +24 -3
- package/dist/commands/team-agents-agents.js +314 -35
- package/dist/commands/team-agents-agents.js.map +1 -1
- package/dist/commands/team-agents-mcp.js +32 -3
- package/dist/commands/team-agents-mcp.js.map +1 -1
- package/dist/commands/team-agents-triggers.js +230 -55
- package/dist/commands/team-agents-triggers.js.map +1 -1
- package/dist/commands/team-agents.d.ts +3 -1
- package/dist/commands/team-agents.js +27 -7
- package/dist/commands/team-agents.js.map +1 -1
- package/dist/commands/teams-channel-install.d.ts +24 -0
- package/dist/commands/teams-channel-install.js +94 -0
- package/dist/commands/teams-channel-install.js.map +1 -0
- package/dist/commands/teams-connect.d.ts +41 -0
- package/dist/commands/teams-connect.js +212 -57
- package/dist/commands/teams-connect.js.map +1 -1
- package/dist/commands/teams-events.d.ts +12 -0
- package/dist/commands/teams-events.js +138 -0
- package/dist/commands/teams-events.js.map +1 -0
- package/dist/commands/teams-onboard.d.ts +11 -3
- package/dist/commands/teams-onboard.js +60 -7
- package/dist/commands/teams-onboard.js.map +1 -1
- package/dist/commands/teams-overview.d.ts +11 -0
- package/dist/commands/teams-overview.js +160 -0
- package/dist/commands/teams-overview.js.map +1 -0
- package/dist/commands/teams-scope.d.ts +10 -0
- package/dist/commands/teams-scope.js +109 -0
- package/dist/commands/teams-scope.js.map +1 -0
- package/dist/commands/teams.d.ts +4 -2
- package/dist/commands/teams.js +113 -14
- package/dist/commands/teams.js.map +1 -1
- package/dist/commands/upgrade.js +12 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/use.js +2 -2
- package/dist/commands/use.js.map +1 -1
- package/dist/commands/whoami.js +1 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/connections-api.js +1 -1
- package/dist/connections-api.js.map +1 -1
- package/dist/delegate.js +11 -4
- package/dist/delegate.js.map +1 -1
- package/dist/harness-scaffold.js +12 -0
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +682 -167
- package/dist/index.js.map +1 -1
- package/dist/knowledge-api.d.ts +46 -0
- package/dist/knowledge-api.js +32 -1
- package/dist/knowledge-api.js.map +1 -1
- package/dist/open-url.d.ts +16 -0
- package/dist/open-url.js +73 -0
- package/dist/open-url.js.map +1 -0
- package/dist/parse-argv.d.ts +2 -0
- package/dist/parse-argv.js.map +1 -1
- package/dist/publish-api.d.ts +2 -1
- package/dist/publish-api.js +4 -3
- package/dist/publish-api.js.map +1 -1
- package/dist/pull-write.d.ts +27 -0
- package/dist/pull-write.js +60 -0
- package/dist/pull-write.js.map +1 -0
- package/dist/routes-api.d.ts +3 -0
- package/dist/routes-api.js +9 -4
- package/dist/routes-api.js.map +1 -1
- package/dist/runs-api.d.ts +7 -1
- package/dist/runs-api.js +21 -6
- package/dist/runs-api.js.map +1 -1
- package/dist/safe-fs.d.ts +2 -0
- package/dist/safe-fs.js +12 -6
- package/dist/safe-fs.js.map +1 -1
- package/dist/secret-file.d.ts +17 -0
- package/dist/secret-file.js +69 -0
- package/dist/secret-file.js.map +1 -0
- package/dist/skills-api.d.ts +10 -2
- package/dist/skills-api.js +37 -2
- package/dist/skills-api.js.map +1 -1
- package/dist/source-products-api.d.ts +4 -0
- package/dist/source-products-api.js +14 -4
- package/dist/source-products-api.js.map +1 -1
- package/dist/source-target.js +2 -1
- package/dist/source-target.js.map +1 -1
- package/dist/sources-api.d.ts +2 -1
- package/dist/sources-api.js +3 -2
- package/dist/sources-api.js.map +1 -1
- package/dist/team-connect-api.d.ts +44 -2
- package/dist/team-connect-api.js +19 -7
- package/dist/team-connect-api.js.map +1 -1
- package/dist/teams-api.d.ts +41 -1
- package/dist/teams-api.js +95 -19
- package/dist/teams-api.js.map +1 -1
- package/dist/toolchain-api.d.ts +9 -0
- package/dist/toolchain-api.js +17 -2
- package/dist/toolchain-api.js.map +1 -1
- package/dist/toolchain-resolve.d.ts +3 -0
- package/dist/toolchain-resolve.js +15 -1
- package/dist/toolchain-resolve.js.map +1 -1
- package/dist/toolchain.d.ts +6 -0
- package/dist/toolchain.js +19 -0
- package/dist/toolchain.js.map +1 -1
- package/dist/update-notice.js +2 -1
- package/dist/update-notice.js.map +1 -1
- package/package.json +2 -2
- package/templates/AGENTS.md.tmpl +6 -1
- package/templates/claude-skill.md.tmpl +5 -0
- package/templates/package.json.tmpl +4 -4
- package/templates/skills/build-agent/SKILL.md +104 -17
- package/templates/skills/build-agent/references/agent-json.md +4 -2
- package/templates/skills/build-agent/references/rollout.md +23 -4
- package/templates/skills/build-agent/references/set-up-an-integration.md +55 -7
- package/templates/skills/build-agent/references/skills.md +20 -1
- package/templates/skills/build-agent/references/triggers.md +47 -11
- package/templates/skills/build-integration/references/widget-design.md +3 -3
- package/templates/skills/ekanos-platform/SKILL.md +45 -16
- package/templates/skills/polish-widgets/references/patterns.md +2 -2
package/dist/commands/routes.js
CHANGED
|
@@ -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
|
|
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 },
|
|
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',
|
|
160
|
+
['URL', `saved to ${secretFile} (readable only by you)`],
|
|
133
161
|
],
|
|
134
162
|
notes: [
|
|
135
|
-
'
|
|
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.`,
|
|
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":"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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"]}
|
|
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"]}
|
package/dist/commands/runs.d.ts
CHANGED
|
@@ -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;
|
package/dist/commands/runs.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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}${
|
|
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
|
-
|
|
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
|
|
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
|
|
322
|
+
: detail.toolCalls.map((call) => `${call.ok === false ? '✗' : '✓'} ${call.toolName} (${callFacts(call)})`),
|
|
264
323
|
],
|
|
265
324
|
...(pending.length > 0
|
|
266
325
|
? [
|
|
@@ -294,7 +353,9 @@ function approvalLines(approval) {
|
|
|
294
353
|
return [
|
|
295
354
|
`${approval.toolName} · ${agentLabel(approval.agent)}${approval.replyDraft ? ' · reply draft (editable)' : ''}`,
|
|
296
355
|
`run ${approval.runId}${approval.expiresAt ? ` · expires ${approval.expiresAt}` : ''}`,
|
|
297
|
-
|
|
356
|
+
approval.argsWithheld
|
|
357
|
+
? "arguments withheld: the team's own agent, so only a member of the team who could decide it sees what it would do"
|
|
358
|
+
: clip(JSON.stringify((_a = approval.args) !== null && _a !== void 0 ? _a : null)),
|
|
298
359
|
];
|
|
299
360
|
}
|
|
300
361
|
function agentLabel(agent) {
|
|
@@ -327,6 +388,15 @@ function requireOperand(value, what, command) {
|
|
|
327
388
|
return trimmed;
|
|
328
389
|
throw usageError(`Name the ${what}.`, `See "ekanos ${command} --help".`);
|
|
329
390
|
}
|
|
391
|
+
/** `--status`, one of the run statuses. */
|
|
392
|
+
function parseStatus(raw) {
|
|
393
|
+
if (raw === undefined)
|
|
394
|
+
return undefined;
|
|
395
|
+
const parsed = AgentRunStatusSchema.safeParse(raw.trim());
|
|
396
|
+
if (parsed.success)
|
|
397
|
+
return parsed.data;
|
|
398
|
+
throw usageError(`--status "${raw}" is not a run status.`, `Use one of: ${AgentRunStatusSchema.options.join(', ')}.`);
|
|
399
|
+
}
|
|
330
400
|
function rejectExtra(extra, command) {
|
|
331
401
|
if (extra.length > 0) {
|
|
332
402
|
throw usageError(`Unexpected argument "${extra[0]}".`, `See "ekanos ${command.split(' ')[0]} --help".`);
|
|
@@ -336,7 +406,7 @@ async function connect(ctx, args) {
|
|
|
336
406
|
const env = resolveAuthEnvironment(ctx, args.host, args.env, {
|
|
337
407
|
projectDir: args.dir,
|
|
338
408
|
});
|
|
339
|
-
const resolved = await resolveSourceTarget(env, requireSession(env
|
|
409
|
+
const resolved = await resolveSourceTarget(env, requireSession(env), args);
|
|
340
410
|
return {
|
|
341
411
|
env,
|
|
342
412
|
client: new RunsClient(env, resolved.session),
|
|
@@ -356,7 +426,20 @@ class RunsClient {
|
|
|
356
426
|
}
|
|
357
427
|
if (result.status === 'ok')
|
|
358
428
|
return result.data;
|
|
359
|
-
throw authRequiredError(`The session for ${this.env.host} could not authorize the call.`,
|
|
429
|
+
throw authRequiredError(`The session for ${this.env.host} could not authorize the call.`, loginHint(this.env));
|
|
360
430
|
}
|
|
361
431
|
}
|
|
432
|
+
const CONNECTION_WORDS = {
|
|
433
|
+
source: 'via Source connection',
|
|
434
|
+
team: 'via team connection',
|
|
435
|
+
person: 'via personal connection',
|
|
436
|
+
};
|
|
437
|
+
/** Effect, how it was authorized, and whose connection it ran on. */
|
|
438
|
+
function callFacts(call) {
|
|
439
|
+
return [
|
|
440
|
+
call.effect,
|
|
441
|
+
call.authorizedVia,
|
|
442
|
+
...(call.connection ? [CONNECTION_WORDS[call.connection]] : []),
|
|
443
|
+
].join(', ');
|
|
444
|
+
}
|
|
362
445
|
//# sourceMappingURL=runs.js.map
|