@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.
Files changed (69) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/agents-edit.d.ts +49 -0
  3. package/dist/commands/agents-edit.js +385 -0
  4. package/dist/commands/agents-edit.js.map +1 -0
  5. package/dist/commands/agents.d.ts +24 -1
  6. package/dist/commands/agents.js +117 -8
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/apps.d.ts +6 -2
  9. package/dist/commands/apps.js +249 -4
  10. package/dist/commands/apps.js.map +1 -1
  11. package/dist/commands/reviewers.d.ts +21 -0
  12. package/dist/commands/reviewers.js +162 -0
  13. package/dist/commands/reviewers.js.map +1 -0
  14. package/dist/commands/routes.d.ts +23 -0
  15. package/dist/commands/routes.js +277 -0
  16. package/dist/commands/routes.js.map +1 -0
  17. package/dist/commands/runs.d.ts +42 -0
  18. package/dist/commands/runs.js +362 -0
  19. package/dist/commands/runs.js.map +1 -0
  20. package/dist/commands/skills.d.ts +60 -0
  21. package/dist/commands/skills.js +397 -0
  22. package/dist/commands/skills.js.map +1 -0
  23. package/dist/commands/team-agents-agents.d.ts +9 -0
  24. package/dist/commands/team-agents-agents.js +188 -0
  25. package/dist/commands/team-agents-agents.js.map +1 -0
  26. package/dist/commands/team-agents-mcp.d.ts +4 -0
  27. package/dist/commands/team-agents-mcp.js +218 -0
  28. package/dist/commands/team-agents-mcp.js.map +1 -0
  29. package/dist/commands/team-agents-triggers.d.ts +12 -0
  30. package/dist/commands/team-agents-triggers.js +251 -0
  31. package/dist/commands/team-agents-triggers.js.map +1 -0
  32. package/dist/commands/team-agents.d.ts +31 -0
  33. package/dist/commands/team-agents.js +71 -0
  34. package/dist/commands/team-agents.js.map +1 -0
  35. package/dist/commands/teams-onboard.d.ts +51 -0
  36. package/dist/commands/teams-onboard.js +261 -0
  37. package/dist/commands/teams-onboard.js.map +1 -0
  38. package/dist/commands/teams.d.ts +16 -1
  39. package/dist/commands/teams.js +22 -3
  40. package/dist/commands/teams.js.map +1 -1
  41. package/dist/connections-api.d.ts +125 -0
  42. package/dist/connections-api.js +156 -0
  43. package/dist/connections-api.js.map +1 -0
  44. package/dist/index.js +226 -6
  45. package/dist/index.js.map +1 -1
  46. package/dist/reviewers-api.d.ts +33 -0
  47. package/dist/reviewers-api.js +81 -0
  48. package/dist/reviewers-api.js.map +1 -0
  49. package/dist/routes-api.d.ts +105 -0
  50. package/dist/routes-api.js +147 -0
  51. package/dist/routes-api.js.map +1 -0
  52. package/dist/runs-api.d.ts +34 -0
  53. package/dist/runs-api.js +112 -0
  54. package/dist/runs-api.js.map +1 -0
  55. package/dist/skills-api.d.ts +28 -0
  56. package/dist/skills-api.js +104 -0
  57. package/dist/skills-api.js.map +1 -0
  58. package/dist/teams-api.d.ts +45 -3
  59. package/dist/teams-api.js +72 -3
  60. package/dist/teams-api.js.map +1 -1
  61. package/package.json +2 -2
  62. package/templates/package.json.tmpl +4 -4
  63. package/templates/skills/build-agent/SKILL.md +94 -10
  64. package/templates/skills/build-agent/references/agent-json.md +85 -4
  65. package/templates/skills/build-agent/references/rollout.md +9 -1
  66. package/templates/skills/build-agent/references/set-up-an-integration.md +59 -3
  67. package/templates/skills/build-agent/references/skills.md +81 -0
  68. package/templates/skills/build-agent/references/triggers.md +25 -5
  69. package/templates/skills/ekanos-platform/SKILL.md +116 -5
@@ -0,0 +1,362 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
4
+ import { authRequiredError, usageError, validationError } from '../errors.js';
5
+ import { decideApproval, listApprovals, listRuns, showRun, startRun, } from '../runs-api.js';
6
+ import { resolveSourceTarget } from '../source-target.js';
7
+ export const RUNS_SUBCOMMANDS = ['start', 'list', 'show'];
8
+ export const APPROVALS_SUBCOMMANDS = [
9
+ 'list',
10
+ 'approve',
11
+ 'reject',
12
+ 'edit',
13
+ ];
14
+ const FOLLOW_INTERVAL_MS = 2000;
15
+ const FOLLOW_TIMEOUT_MS = 15 * 60 * 1000;
16
+ /**
17
+ * `runs` — run, list and inspect a team's agents as its Source admin. A run
18
+ * started here is the app's "Run now": it runs as the agent's creator under
19
+ * the agent's own grant, policy and approvals (writes still wait for one),
20
+ * and is attributed to you.
21
+ */
22
+ export async function runRuns(ctx, args) {
23
+ const [rawSubcommand, operand, ...extra] = args.positionals;
24
+ const subcommand = RUNS_SUBCOMMANDS.find((name) => name === rawSubcommand);
25
+ if (!subcommand) {
26
+ throw usageError(rawSubcommand
27
+ ? `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>.');
29
+ }
30
+ rejectExtra(extra, 'runs');
31
+ const team = requireTeam(args.team);
32
+ if (subcommand === 'start') {
33
+ const agent = requireOperand(operand, 'agent key', 'runs start');
34
+ return startAndReport(ctx, args, { team, agent, draft: false });
35
+ }
36
+ const { env, client, source } = await connect(ctx, args);
37
+ if (subcommand === 'list') {
38
+ if (operand)
39
+ rejectExtra([operand], 'runs list');
40
+ const list = await client.call((token) => listRuns(env.host, token, { source, team, agent: args.agent }));
41
+ return ctx.succeed(Object.assign({ host: env.host }, list), list.runs.length === 0
42
+ ? ctx.ui.result({
43
+ title: `No runs on ${list.team.slug}${args.agent ? ` for ${args.agent}` : ''}`,
44
+ tone: 'warn',
45
+ })
46
+ : ctx.ui.table(['RUN', 'AGENT', 'STATUS', 'TRIGGER', 'VERSION', 'STARTED'], list.runs.map((run) => [
47
+ run.id,
48
+ agentLabel(run.agent),
49
+ run.status,
50
+ run.trigger,
51
+ versionLabel(run),
52
+ run.startedAt.slice(0, 19).replace('T', ' '),
53
+ ])));
54
+ }
55
+ const runId = requireOperand(operand, 'run id', 'runs show');
56
+ const detail = await client.call((token) => showRun(env.host, token, { source, team, runId }));
57
+ return ctx.succeed(Object.assign({ host: env.host }, detail), runDetail(ctx.ui, detail));
58
+ }
59
+ /**
60
+ * `agents test <key> --team <slug>`: run the template's latest version — its
61
+ * draft, published or not — on one team, without publishing it. Marked as a
62
+ * 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.
64
+ */
65
+ export async function runAgentTest(ctx, args) {
66
+ const agent = requireOperand(args.key, 'agent key', 'agents test');
67
+ const team = requireTeam(args.team);
68
+ return startAndReport(ctx, args, { team, agent, draft: true });
69
+ }
70
+ /**
71
+ * `approvals` — what a team's agent runs are waiting on, and deciding it.
72
+ * Decided with the authority a team's agent manager has in the app, and
73
+ * recorded as your decision.
74
+ */
75
+ export async function runApprovals(ctx, args) {
76
+ const [rawSubcommand, operand, ...extra] = args.positionals;
77
+ const subcommand = APPROVALS_SUBCOMMANDS.find((name) => name === rawSubcommand);
78
+ if (!subcommand) {
79
+ throw usageError(rawSubcommand
80
+ ? `Unknown approvals subcommand "${rawSubcommand}".`
81
+ : 'approvals needs a subcommand.', 'Run one of: ekanos approvals list --team <slug>, ekanos approvals approve|reject <id> --team <slug> [--reason "…"], ekanos approvals edit <id> --team <slug> --body <file>.');
82
+ }
83
+ rejectExtra(extra, 'approvals');
84
+ const team = requireTeam(args.team);
85
+ if (subcommand === 'list') {
86
+ if (operand)
87
+ rejectExtra([operand], 'approvals list');
88
+ const { env, client, source } = await connect(ctx, args);
89
+ const list = await client.call((token) => listApprovals(env.host, token, { source, team }));
90
+ return ctx.succeed(Object.assign({ host: env.host }, list), list.approvals.length === 0
91
+ ? ctx.ui.result({ title: `Nothing waiting on ${list.team.slug}` })
92
+ : ctx.ui.result({
93
+ title: `${list.approvals.length} waiting on ${list.team.slug}`,
94
+ tone: 'warn',
95
+ rows: list.approvals.map((approval) => [
96
+ approval.id,
97
+ approvalLines(approval),
98
+ ]),
99
+ notes: [
100
+ '"ekanos approvals approve|reject <id> --team <slug>" decides one; "approvals edit <id> --body <file>" edits a reply draft and sends it.',
101
+ ],
102
+ }));
103
+ }
104
+ const approvalId = requireOperand(operand, 'approval id', `approvals ${subcommand}`);
105
+ const editedBody = subcommand === 'edit' ? readBodyFile(args.body, args.dir) : undefined;
106
+ if (subcommand !== 'edit' && args.body) {
107
+ throw usageError('--body is for "approvals edit".', `Run "ekanos approvals edit ${approvalId} --team ${team} --body <file>".`);
108
+ }
109
+ const { env, client, source } = await connect(ctx, args);
110
+ const decided = await client.call((token) => decideApproval(env.host, token, { source, team, approvalId }, Object.assign(Object.assign({ decision: subcommand === 'reject' ? 'rejected' : 'approved' }, (args.reason ? { reason: args.reason } : {})), (editedBody ? { body: editedBody } : {}))));
111
+ const approval = decided.approval;
112
+ return ctx.succeed(Object.assign({ host: env.host }, decided), ctx.ui.result({
113
+ title: `${approval.status === 'rejected' ? 'Rejected' : editedBody ? 'Edited and approved' : 'Approved'} ${approval.toolName}`,
114
+ tone: approval.status === 'rejected' ? 'warn' : 'ok',
115
+ rows: [
116
+ ['Agent', agentLabel(approval.agent)],
117
+ ['Run', approval.runId],
118
+ ],
119
+ notes: [
120
+ `The run picks it up now: "ekanos runs show ${approval.runId} --team ${team}".`,
121
+ ],
122
+ }));
123
+ }
124
+ async function startAndReport(ctx, args, target) {
125
+ const body = startBody(args, target);
126
+ const { env, client, source } = await connect(ctx, args);
127
+ const started = await client.call((token) => startRun(env.host, token, { source, team: target.team }, body));
128
+ const startedNote = `${target.draft ? 'Testing' : 'Started'} ${agentLabel(started.agent)} on ${started.team.slug}${started.draftTemplateVersion ? ` (draft v${started.draftTemplateVersion}, unpublished)` : ''}: run ${started.runId}`;
129
+ if (!args.follow) {
130
+ return ctx.succeed(Object.assign({ host: env.host }, started), ctx.ui.result({
131
+ title: startedNote,
132
+ notes: [
133
+ `Watch it: "ekanos runs show ${started.runId} --team ${target.team}".`,
134
+ ],
135
+ }));
136
+ }
137
+ ctx.log(startedNote);
138
+ const detail = await follow(ctx, args, async (after) => client.call((token) => showRun(env.host, token, {
139
+ source,
140
+ team: target.team,
141
+ runId: started.runId,
142
+ after,
143
+ })));
144
+ return ctx.succeed(Object.assign({ host: env.host, started }, detail), runDetail(ctx.ui, detail));
145
+ }
146
+ /**
147
+ * Poll the run, printing its status changes, its messages and each tool
148
+ * call as it lands, until it ends or parks on an approval.
149
+ */
150
+ async function follow(ctx, args, read) {
151
+ var _a;
152
+ const sleep = (_a = args.sleep) !== null && _a !== void 0 ? _a : ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
153
+ const seenCalls = new Set();
154
+ const events = [];
155
+ let lastEvent;
156
+ let lastStatus = '';
157
+ const deadline = Date.now() + FOLLOW_TIMEOUT_MS;
158
+ for (;;) {
159
+ const detail = await read(lastEvent);
160
+ for (const event of detail.events) {
161
+ events.push(event);
162
+ lastEvent = event.id;
163
+ for (const line of eventLines(event.message))
164
+ ctx.log(` ${line}`);
165
+ }
166
+ for (const call of detail.toolCalls) {
167
+ if (seenCalls.has(call.toolCallId))
168
+ continue;
169
+ seenCalls.add(call.toolCallId);
170
+ ctx.log(` ${call.ok === false ? '✗' : '✓'} ${call.toolName} (${call.effect}, ${call.authorizedVia})${call.error ? ` — ${call.error}` : ''}`);
171
+ }
172
+ if (detail.run.status !== lastStatus) {
173
+ lastStatus = detail.run.status;
174
+ ctx.log(` status: ${lastStatus}`);
175
+ }
176
+ const waiting = detail.approvals.some((approval) => approval.status === 'pending');
177
+ const ended = ['completed', 'failed', 'cancelled'].includes(detail.run.status);
178
+ if (ended || waiting || Date.now() > deadline) {
179
+ return Object.assign(Object.assign({}, detail), { events });
180
+ }
181
+ await sleep(FOLLOW_INTERVAL_MS);
182
+ }
183
+ }
184
+ /** Assistant text and tool calls from one live transcript message. */
185
+ function eventLines(message) {
186
+ if (!isRecord(message))
187
+ return [];
188
+ const content = message.content;
189
+ if (message.role === 'assistant' && typeof content === 'string') {
190
+ return content.trim() ? [`agent: ${clip(content)}`] : [];
191
+ }
192
+ if (!Array.isArray(content))
193
+ return [];
194
+ return content.flatMap((part) => {
195
+ if (!isRecord(part))
196
+ return [];
197
+ if (part.type === 'text' && message.role === 'assistant') {
198
+ return typeof part.text === 'string' && part.text.trim()
199
+ ? [`agent: ${clip(part.text)}`]
200
+ : [];
201
+ }
202
+ if (part.type === 'tool-call' && typeof part.toolName === 'string') {
203
+ return [`→ ${part.toolName}`];
204
+ }
205
+ return [];
206
+ });
207
+ }
208
+ function startBody(args, target) {
209
+ if (!args.prompt && !args.input) {
210
+ throw usageError('Give the run a prompt or an input.', `Pass --prompt "…" or --input <file.json>.`);
211
+ }
212
+ let input;
213
+ if (args.input) {
214
+ const file = path.resolve(args.dir, args.input);
215
+ let raw;
216
+ try {
217
+ raw = fs.readFileSync(file, 'utf8');
218
+ }
219
+ catch (_a) {
220
+ throw usageError(`Cannot read ${file}.`, 'Pass --input <file.json>.');
221
+ }
222
+ try {
223
+ input = JSON.parse(raw);
224
+ }
225
+ catch (_b) {
226
+ throw validationError(`${file} is not valid JSON.`, 'Fix the file, or pass the text with --prompt instead.');
227
+ }
228
+ }
229
+ return Object.assign(Object.assign(Object.assign({ agent: target.agent }, (args.prompt ? { prompt: args.prompt } : {})), (input !== undefined ? { input } : {})), { draft: target.draft });
230
+ }
231
+ function readBodyFile(file, dir) {
232
+ if (!file) {
233
+ throw usageError('approvals edit needs the reply to send.', 'Pass --body <file> with the edited reply text.');
234
+ }
235
+ const resolved = path.resolve(dir, file);
236
+ let text;
237
+ try {
238
+ text = fs.readFileSync(resolved, 'utf8');
239
+ }
240
+ catch (_a) {
241
+ throw usageError(`Cannot read ${resolved}.`, 'Pass --body <file>.');
242
+ }
243
+ if (!text.trim()) {
244
+ throw validationError(`${resolved} is empty.`, 'Write the reply to send.');
245
+ }
246
+ return text;
247
+ }
248
+ function runDetail(ui, detail) {
249
+ const { run } = detail;
250
+ const pending = detail.approvals.filter((approval) => approval.status === 'pending');
251
+ const rows = [
252
+ ['Agent', agentLabel(run.agent)],
253
+ ['Status', run.status],
254
+ ['Trigger', run.trigger],
255
+ ['Version', versionLabel(run)],
256
+ ['Started', run.startedAt],
257
+ ...(run.finishedAt ? [['Finished', run.finishedAt]] : []),
258
+ ...(run.error ? [['Error', run.error]] : []),
259
+ [
260
+ 'Tool calls',
261
+ detail.toolCalls.length === 0
262
+ ? 'none'
263
+ : detail.toolCalls.map((call) => `${call.ok === false ? '✗' : '✓'} ${call.toolName} (${call.effect}, ${call.authorizedVia})`),
264
+ ],
265
+ ...(pending.length > 0
266
+ ? [
267
+ [
268
+ 'Waiting on',
269
+ pending.flatMap((approval) => [
270
+ `${approval.id} ${approval.toolName}${approval.replyDraft ? ' (reply draft)' : ''}`,
271
+ ]),
272
+ ],
273
+ ]
274
+ : []),
275
+ ...(run.output ? [['Output', run.output.split('\n')]] : []),
276
+ ];
277
+ return ui.result({
278
+ title: `Run ${run.id}`,
279
+ tone: run.status === 'failed'
280
+ ? 'err'
281
+ : run.status === 'completed'
282
+ ? 'ok'
283
+ : 'warn',
284
+ rows,
285
+ notes: pending.length > 0
286
+ ? [
287
+ `Decide: "ekanos approvals approve|reject <id> --team ${detail.team.slug}". Full record: --json.`,
288
+ ]
289
+ : ['The full transcript and every tool call: --json.'],
290
+ });
291
+ }
292
+ function approvalLines(approval) {
293
+ var _a;
294
+ return [
295
+ `${approval.toolName} · ${agentLabel(approval.agent)}${approval.replyDraft ? ' · reply draft (editable)' : ''}`,
296
+ `run ${approval.runId}${approval.expiresAt ? ` · expires ${approval.expiresAt}` : ''}`,
297
+ clip(JSON.stringify((_a = approval.args) !== null && _a !== void 0 ? _a : null)),
298
+ ];
299
+ }
300
+ function agentLabel(agent) {
301
+ var _a;
302
+ return (_a = agent.templateKey) !== null && _a !== void 0 ? _a : agent.slug;
303
+ }
304
+ function versionLabel(run) {
305
+ return run.draftTemplateVersion
306
+ ? `v${run.agentVersion} · draft test of template v${run.draftTemplateVersion}`
307
+ : `v${run.agentVersion}`;
308
+ }
309
+ function clip(text, max = 200) {
310
+ const flat = text.replace(/\s+/g, ' ').trim();
311
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
312
+ }
313
+ function isRecord(value) {
314
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
315
+ }
316
+ function requireTeam(value) {
317
+ const slug = value === null || value === void 0 ? void 0 : value.trim().toLowerCase();
318
+ if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug))
319
+ return slug;
320
+ throw usageError(value
321
+ ? `"${value}" is not a team slug.`
322
+ : 'Name the team with --team <slug>.', '"ekanos teams list" lists the teams on this Source.');
323
+ }
324
+ function requireOperand(value, what, command) {
325
+ const trimmed = value === null || value === void 0 ? void 0 : value.trim();
326
+ if (trimmed)
327
+ return trimmed;
328
+ throw usageError(`Name the ${what}.`, `See "ekanos ${command} --help".`);
329
+ }
330
+ function rejectExtra(extra, command) {
331
+ if (extra.length > 0) {
332
+ throw usageError(`Unexpected argument "${extra[0]}".`, `See "ekanos ${command.split(' ')[0]} --help".`);
333
+ }
334
+ }
335
+ async function connect(ctx, args) {
336
+ const env = resolveAuthEnvironment(ctx, args.host, args.env, {
337
+ projectDir: args.dir,
338
+ });
339
+ const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
340
+ return {
341
+ env,
342
+ client: new RunsClient(env, resolved.session),
343
+ source: resolved.source,
344
+ };
345
+ }
346
+ class RunsClient {
347
+ constructor(env, session) {
348
+ this.env = env;
349
+ this.session = session;
350
+ }
351
+ async call(send) {
352
+ let result = await send(this.session.accessToken);
353
+ if (result.status === 'unauthorized') {
354
+ this.session = await refreshStoredSession(this.env, this.session);
355
+ result = await send(this.session.accessToken);
356
+ }
357
+ if (result.status === 'ok')
358
+ return result.data;
359
+ throw authRequiredError(`The session for ${this.env.host} could not authorize the call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
360
+ }
361
+ }
362
+ //# sourceMappingURL=runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/commands/runs.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AgentRunApproval,\n AgentRunDetail,\n AgentRunStartRequest,\n AgentRunSummary,\n} from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\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, validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type RunsApiResult,\n decideApproval,\n listApprovals,\n listRuns,\n showRun,\n startRun,\n} from '../runs-api';\nimport { resolveSourceTarget } from '../source-target';\nimport type { ResultRow, Ui } from '../ui';\n\nexport const RUNS_SUBCOMMANDS = ['start', 'list', 'show'] as const;\nexport const APPROVALS_SUBCOMMANDS = [\n 'list',\n 'approve',\n 'reject',\n 'edit',\n] as const;\n\nconst FOLLOW_INTERVAL_MS = 2000;\nconst FOLLOW_TIMEOUT_MS = 15 * 60 * 1000;\n\nexport interface RunsArgs {\n host?: string;\n source?: string;\n positionals: readonly string[];\n team?: string;\n agent?: string;\n prompt?: string;\n input?: string;\n follow: boolean;\n reason?: string;\n body?: string;\n dir: string;\n env: Record<string, string | undefined>;\n /** Test seam for the `--follow` poll delay. */\n sleep?: (ms: number) => Promise<void>;\n}\n\n/**\n * `runs` — run, list and inspect a team's agents as its Source admin. A run\n * started here is the app's \"Run now\": it runs as the agent's creator under\n * the agent's own grant, policy and approvals (writes still wait for one),\n * and is attributed to you.\n */\nexport async function runRuns(\n ctx: CliContext,\n args: RunsArgs,\n): Promise<ExitCode> {\n const [rawSubcommand, operand, ...extra] = args.positionals;\n const subcommand = RUNS_SUBCOMMANDS.find((name) => name === rawSubcommand);\n if (!subcommand) {\n throw usageError(\n rawSubcommand\n ? `Unknown runs subcommand \"${rawSubcommand}\".`\n : 'runs needs a subcommand.',\n 'Run one of: ekanos runs start <agent> --team <slug> --prompt \"…\", ekanos runs list --team <slug>, ekanos runs show <run-id> --team <slug>.',\n );\n }\n rejectExtra(extra, 'runs');\n const team = requireTeam(args.team);\n\n if (subcommand === 'start') {\n const agent = requireOperand(operand, 'agent key', 'runs start');\n return startAndReport(ctx, args, { team, agent, draft: false });\n }\n\n const { env, client, source } = await connect(ctx, args);\n\n if (subcommand === 'list') {\n if (operand) rejectExtra([operand], 'runs list');\n const list = await client.call((token) =>\n listRuns(env.host, token, { source, team, agent: args.agent }),\n );\n return ctx.succeed(\n { host: env.host, ...list },\n list.runs.length === 0\n ? ctx.ui.result({\n title: `No runs on ${list.team.slug}${args.agent ? ` for ${args.agent}` : ''}`,\n tone: 'warn',\n })\n : ctx.ui.table(\n ['RUN', 'AGENT', 'STATUS', 'TRIGGER', 'VERSION', 'STARTED'],\n list.runs.map((run) => [\n run.id,\n agentLabel(run.agent),\n run.status,\n run.trigger,\n versionLabel(run),\n run.startedAt.slice(0, 19).replace('T', ' '),\n ]),\n ),\n );\n }\n\n const runId = requireOperand(operand, 'run id', 'runs show');\n const detail = await client.call((token) =>\n showRun(env.host, token, { source, team, runId }),\n );\n return ctx.succeed({ host: env.host, ...detail }, runDetail(ctx.ui, detail));\n}\n\n/**\n * `agents test <key> --team <slug>`: run the template's latest version — its\n * draft, published or not — on one team, without publishing it. Marked as a\n * draft test run in the ledger; the team's stored agent still bounds every\n * tool call, so a draft can narrow what runs but never widen it.\n */\nexport async function runAgentTest(\n ctx: CliContext,\n args: RunsArgs & { key: string | undefined },\n): Promise<ExitCode> {\n const agent = requireOperand(args.key, 'agent key', 'agents test');\n const team = requireTeam(args.team);\n return startAndReport(ctx, args, { team, agent, draft: true });\n}\n\n/**\n * `approvals` — what a team's agent runs are waiting on, and deciding it.\n * Decided with the authority a team's agent manager has in the app, and\n * recorded as your decision.\n */\nexport async function runApprovals(\n ctx: CliContext,\n args: RunsArgs,\n): Promise<ExitCode> {\n const [rawSubcommand, operand, ...extra] = args.positionals;\n const subcommand = APPROVALS_SUBCOMMANDS.find(\n (name) => name === rawSubcommand,\n );\n if (!subcommand) {\n throw usageError(\n rawSubcommand\n ? `Unknown approvals subcommand \"${rawSubcommand}\".`\n : 'approvals needs a subcommand.',\n 'Run one of: ekanos approvals list --team <slug>, ekanos approvals approve|reject <id> --team <slug> [--reason \"…\"], ekanos approvals edit <id> --team <slug> --body <file>.',\n );\n }\n rejectExtra(extra, 'approvals');\n const team = requireTeam(args.team);\n\n if (subcommand === 'list') {\n if (operand) rejectExtra([operand], 'approvals list');\n const { env, client, source } = await connect(ctx, args);\n const list = await client.call((token) =>\n listApprovals(env.host, token, { source, team }),\n );\n return ctx.succeed(\n { host: env.host, ...list },\n list.approvals.length === 0\n ? ctx.ui.result({ title: `Nothing waiting on ${list.team.slug}` })\n : ctx.ui.result({\n title: `${list.approvals.length} waiting on ${list.team.slug}`,\n tone: 'warn',\n rows: list.approvals.map((approval): ResultRow => [\n approval.id,\n approvalLines(approval),\n ]),\n notes: [\n '\"ekanos approvals approve|reject <id> --team <slug>\" decides one; \"approvals edit <id> --body <file>\" edits a reply draft and sends it.',\n ],\n }),\n );\n }\n\n const approvalId = requireOperand(\n operand,\n 'approval id',\n `approvals ${subcommand}`,\n );\n const editedBody =\n subcommand === 'edit' ? readBodyFile(args.body, args.dir) : undefined;\n if (subcommand !== 'edit' && args.body) {\n throw usageError(\n '--body is for \"approvals edit\".',\n `Run \"ekanos approvals edit ${approvalId} --team ${team} --body <file>\".`,\n );\n }\n\n const { env, client, source } = await connect(ctx, args);\n const decided = await client.call((token) =>\n decideApproval(\n env.host,\n token,\n { source, team, approvalId },\n {\n decision: subcommand === 'reject' ? 'rejected' : 'approved',\n ...(args.reason ? { reason: args.reason } : {}),\n ...(editedBody ? { body: editedBody } : {}),\n },\n ),\n );\n const approval = decided.approval;\n return ctx.succeed(\n { host: env.host, ...decided },\n ctx.ui.result({\n title: `${approval.status === 'rejected' ? 'Rejected' : editedBody ? 'Edited and approved' : 'Approved'} ${approval.toolName}`,\n tone: approval.status === 'rejected' ? 'warn' : 'ok',\n rows: [\n ['Agent', agentLabel(approval.agent)],\n ['Run', approval.runId],\n ],\n notes: [\n `The run picks it up now: \"ekanos runs show ${approval.runId} --team ${team}\".`,\n ],\n }),\n );\n}\n\nasync function startAndReport(\n ctx: CliContext,\n args: RunsArgs,\n target: { team: string; agent: string; draft: boolean },\n): Promise<ExitCode> {\n const body = startBody(args, target);\n const { env, client, source } = await connect(ctx, args);\n const started = await client.call((token) =>\n startRun(env.host, token, { source, team: target.team }, body),\n );\n\n const startedNote = `${target.draft ? 'Testing' : 'Started'} ${agentLabel(started.agent)} on ${started.team.slug}${started.draftTemplateVersion ? ` (draft v${started.draftTemplateVersion}, unpublished)` : ''}: run ${started.runId}`;\n\n if (!args.follow) {\n return ctx.succeed(\n { host: env.host, ...started },\n ctx.ui.result({\n title: startedNote,\n notes: [\n `Watch it: \"ekanos runs show ${started.runId} --team ${target.team}\".`,\n ],\n }),\n );\n }\n\n ctx.log(startedNote);\n const detail = await follow(ctx, args, async (after) =>\n client.call((token) =>\n showRun(env.host, token, {\n source,\n team: target.team,\n runId: started.runId,\n after,\n }),\n ),\n );\n return ctx.succeed(\n { host: env.host, started, ...detail },\n runDetail(ctx.ui, detail),\n );\n}\n\n/**\n * Poll the run, printing its status changes, its messages and each tool\n * call as it lands, until it ends or parks on an approval.\n */\nasync function follow(\n ctx: CliContext,\n args: RunsArgs,\n read: (after: number | undefined) => Promise<AgentRunDetail>,\n): Promise<AgentRunDetail> {\n const sleep =\n args.sleep ??\n ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const seenCalls = new Set<string>();\n const events: AgentRunDetail['events'] = [];\n let lastEvent: number | undefined;\n let lastStatus = '';\n const deadline = Date.now() + FOLLOW_TIMEOUT_MS;\n\n for (;;) {\n const detail = await read(lastEvent);\n for (const event of detail.events) {\n events.push(event);\n lastEvent = event.id;\n for (const line of eventLines(event.message)) ctx.log(` ${line}`);\n }\n for (const call of detail.toolCalls) {\n if (seenCalls.has(call.toolCallId)) continue;\n seenCalls.add(call.toolCallId);\n ctx.log(\n ` ${call.ok === false ? '✗' : '✓'} ${call.toolName} (${call.effect}, ${call.authorizedVia})${call.error ? ` — ${call.error}` : ''}`,\n );\n }\n if (detail.run.status !== lastStatus) {\n lastStatus = detail.run.status;\n ctx.log(` status: ${lastStatus}`);\n }\n\n const waiting = detail.approvals.some(\n (approval) => approval.status === 'pending',\n );\n const ended = ['completed', 'failed', 'cancelled'].includes(\n detail.run.status,\n );\n if (ended || waiting || Date.now() > deadline) {\n return { ...detail, events };\n }\n await sleep(FOLLOW_INTERVAL_MS);\n }\n}\n\n/** Assistant text and tool calls from one live transcript message. */\nfunction eventLines(message: unknown): string[] {\n if (!isRecord(message)) return [];\n const content = message.content;\n if (message.role === 'assistant' && typeof content === 'string') {\n return content.trim() ? [`agent: ${clip(content)}`] : [];\n }\n if (!Array.isArray(content)) return [];\n return content.flatMap((part): string[] => {\n if (!isRecord(part)) return [];\n if (part.type === 'text' && message.role === 'assistant') {\n return typeof part.text === 'string' && part.text.trim()\n ? [`agent: ${clip(part.text)}`]\n : [];\n }\n if (part.type === 'tool-call' && typeof part.toolName === 'string') {\n return [`→ ${part.toolName}`];\n }\n return [];\n });\n}\n\nfunction startBody(\n args: RunsArgs,\n target: { agent: string; draft: boolean },\n): AgentRunStartRequest {\n if (!args.prompt && !args.input) {\n throw usageError(\n 'Give the run a prompt or an input.',\n `Pass --prompt \"…\" or --input <file.json>.`,\n );\n }\n let input: unknown;\n if (args.input) {\n const file = path.resolve(args.dir, args.input);\n let raw: string;\n try {\n raw = fs.readFileSync(file, 'utf8');\n } catch {\n throw usageError(`Cannot read ${file}.`, 'Pass --input <file.json>.');\n }\n try {\n input = JSON.parse(raw);\n } catch {\n throw validationError(\n `${file} is not valid JSON.`,\n 'Fix the file, or pass the text with --prompt instead.',\n );\n }\n }\n return {\n agent: target.agent,\n ...(args.prompt ? { prompt: args.prompt } : {}),\n ...(input !== undefined ? { input } : {}),\n draft: target.draft,\n };\n}\n\nfunction readBodyFile(file: string | undefined, dir: string): string {\n if (!file) {\n throw usageError(\n 'approvals edit needs the reply to send.',\n 'Pass --body <file> with the edited reply text.',\n );\n }\n const resolved = path.resolve(dir, file);\n let text: string;\n try {\n text = fs.readFileSync(resolved, 'utf8');\n } catch {\n throw usageError(`Cannot read ${resolved}.`, 'Pass --body <file>.');\n }\n if (!text.trim()) {\n throw validationError(`${resolved} is empty.`, 'Write the reply to send.');\n }\n return text;\n}\n\nfunction runDetail(ui: Ui, detail: AgentRunDetail): string {\n const { run } = detail;\n const pending = detail.approvals.filter(\n (approval) => approval.status === 'pending',\n );\n const rows: ResultRow[] = [\n ['Agent', agentLabel(run.agent)],\n ['Status', run.status],\n ['Trigger', run.trigger],\n ['Version', versionLabel(run)],\n ['Started', run.startedAt],\n ...(run.finishedAt ? [['Finished', run.finishedAt] as ResultRow] : []),\n ...(run.error ? [['Error', run.error] as ResultRow] : []),\n [\n 'Tool calls',\n detail.toolCalls.length === 0\n ? 'none'\n : detail.toolCalls.map(\n (call) =>\n `${call.ok === false ? '✗' : '✓'} ${call.toolName} (${call.effect}, ${call.authorizedVia})`,\n ),\n ],\n ...(pending.length > 0\n ? [\n [\n 'Waiting on',\n pending.flatMap((approval) => [\n `${approval.id} ${approval.toolName}${approval.replyDraft ? ' (reply draft)' : ''}`,\n ]),\n ] as ResultRow,\n ]\n : []),\n ...(run.output ? [['Output', run.output.split('\\n')] as ResultRow] : []),\n ];\n return ui.result({\n title: `Run ${run.id}`,\n tone:\n run.status === 'failed'\n ? 'err'\n : run.status === 'completed'\n ? 'ok'\n : 'warn',\n rows,\n notes:\n pending.length > 0\n ? [\n `Decide: \"ekanos approvals approve|reject <id> --team ${detail.team.slug}\". Full record: --json.`,\n ]\n : ['The full transcript and every tool call: --json.'],\n });\n}\n\nfunction approvalLines(approval: AgentRunApproval): string[] {\n return [\n `${approval.toolName} · ${agentLabel(approval.agent)}${approval.replyDraft ? ' · reply draft (editable)' : ''}`,\n `run ${approval.runId}${approval.expiresAt ? ` · expires ${approval.expiresAt}` : ''}`,\n clip(JSON.stringify(approval.args ?? null)),\n ];\n}\n\nfunction agentLabel(agent: AgentRunSummary['agent']): string {\n return agent.templateKey ?? agent.slug;\n}\n\nfunction versionLabel(run: AgentRunSummary): string {\n return run.draftTemplateVersion\n ? `v${run.agentVersion} · draft test of template v${run.draftTemplateVersion}`\n : `v${run.agentVersion}`;\n}\n\nfunction clip(text: string, max = 200): string {\n const flat = text.replace(/\\s+/g, ' ').trim();\n return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction requireTeam(value: string | undefined): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value\n ? `\"${value}\" is not a team slug.`\n : 'Name the team with --team <slug>.',\n '\"ekanos teams list\" lists the teams on this Source.',\n );\n}\n\nfunction requireOperand(\n value: string | undefined,\n what: string,\n command: string,\n): string {\n const trimmed = value?.trim();\n if (trimmed) return trimmed;\n throw usageError(`Name the ${what}.`, `See \"ekanos ${command} --help\".`);\n}\n\nfunction rejectExtra(extra: readonly string[], command: string) {\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n `See \"ekanos ${command.split(' ')[0]} --help\".`,\n );\n }\n}\n\nasync function connect(ctx: CliContext, args: RunsArgs) {\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 return {\n env,\n client: new RunsClient(env, resolved.session),\n source: resolved.source,\n };\n}\n\nclass RunsClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<RunsApiResult<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 call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import { type SourceSkillFile, type SourceSkillPushResult } from '@ekanos/integration-schema';
2
+ import type { StoredSession } from '../auth/credential-store.js';
3
+ import { type AuthEnvironment } from '../auth/session.js';
4
+ import type { CliContext } from '../context.js';
5
+ import type { ExitCode } from '../exit-codes.js';
6
+ import { type SkillsApiResult } from '../skills-api.js';
7
+ import { type Ui } from '../ui.js';
8
+ export declare const SKILLS_SUBCOMMANDS: readonly ['list', 'show', 'push', 'pull'];
9
+ /** Where an agent directory keeps the skills it uses: `agents/<key>/skills/<slug>/SKILL.md`. */
10
+ export declare const AGENT_SKILLS_DIR = "skills";
11
+ export interface SkillsArgs {
12
+ host?: string;
13
+ source?: string;
14
+ subcommand?: string;
15
+ /** A skill slug, or for `push` a skill folder. */
16
+ target?: string;
17
+ /** A team's slug: that team's own skills instead of the Source's. */
18
+ team?: string;
19
+ /** `pull`: where to write the folder (default `skills/<slug>`). */
20
+ out?: string;
21
+ dir: string;
22
+ env: Record<string, string | undefined>;
23
+ }
24
+ /**
25
+ * `skills` — the Source's skills, held by Ekanos in its skill store. A skill
26
+ * is a folder: `SKILL.md` plus any `references/` and `assets/`. `push`
27
+ * stores it as the skill's next version (created on first push, nothing
28
+ * stored when it is unchanged), live for every team at once — or with
29
+ * `--team`, as that one team's own skill. `pull` writes the folder back.
30
+ */
31
+ export declare function runSkills(ctx: CliContext, args: SkillsArgs): Promise<ExitCode>;
32
+ /** Every call refreshes the session once on a 401, then gives up. */
33
+ export declare class SkillsClient {
34
+ private readonly env;
35
+ private session;
36
+ constructor(env: AuthEnvironment, session: StoredSession);
37
+ call<T>(send: (accessToken: string) => Promise<SkillsApiResult<T>>): Promise<T>;
38
+ }
39
+ export declare function pushSkillFolder(client: SkillsClient, host: string, input: {
40
+ source: string;
41
+ team?: string;
42
+ folder: SkillFolder;
43
+ }): Promise<SourceSkillPushResult>;
44
+ export interface SkillFolder {
45
+ dir: string;
46
+ slug: string;
47
+ name: string;
48
+ description: string;
49
+ files: SourceSkillFile[];
50
+ }
51
+ /**
52
+ * Read a skill folder: `SKILL.md` and every other file under it. The slug is
53
+ * the frontmatter `name` when it is slug-shaped (Claude Code's own rule is
54
+ * that it matches the folder), else the folder's name; the description is
55
+ * the frontmatter's, else the first paragraph.
56
+ */
57
+ export declare function readSkillFolder(target: string, cwd: string): SkillFolder;
58
+ /** The skill folders beside an agent: `<agentDir>/skills/<slug>/SKILL.md`. */
59
+ export declare function findAgentSkillFolders(agentDir: string): string[];
60
+ export declare function pushSummary(ui: Ui, result: SourceSkillPushResult): string;