@ekanos/cli 0.4.1 → 0.5.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 +3 -1
- package/dist/agents-api.d.ts +35 -0
- package/dist/agents-api.js +115 -0
- package/dist/agents-api.js.map +1 -0
- package/dist/catalog-api.d.ts +26 -0
- package/dist/catalog-api.js +61 -0
- package/dist/catalog-api.js.map +1 -0
- package/dist/commands/agents.d.ts +49 -0
- package/dist/commands/agents.js +407 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/catalog.d.ts +21 -0
- package/dist/commands/catalog.js +116 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/sources.d.ts +8 -0
- package/dist/commands/sources.js +1 -1
- package/dist/commands/sources.js.map +1 -1
- package/dist/index.js +62 -11
- package/dist/index.js.map +1 -1
- package/dist/source-target.d.ts +15 -0
- package/dist/source-target.js +27 -0
- package/dist/source-target.js.map +1 -0
- package/package.json +3 -2
- package/templates/AGENTS.md.tmpl +5 -3
- package/templates/claude-skill.md.tmpl +20 -1
- package/templates/package.json.tmpl +4 -4
- package/templates/pnpm-workspace.yaml.tmpl +10 -0
|
@@ -0,0 +1,407 @@
|
|
|
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 { AgentTemplateBodySchema, AgentTemplateKeySchema, } from '@ekanos/integration-schema';
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { applyAgentTemplate, listAgentTemplates, publishAgentTemplate, showAgentTemplate, } from '../agents-api.js';
|
|
16
|
+
import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
|
|
17
|
+
import { authRequiredError, notFoundError, usageError, validationError, } from '../errors.js';
|
|
18
|
+
import { classifyPath } from '../safe-fs.js';
|
|
19
|
+
import { resolveSourceTarget } from '../source-target.js';
|
|
20
|
+
import { pluralize } from '../ui.js';
|
|
21
|
+
import { promptYesNo } from './publish.js';
|
|
22
|
+
export const AGENTS_SUBCOMMANDS = [
|
|
23
|
+
'list',
|
|
24
|
+
'show',
|
|
25
|
+
'pull',
|
|
26
|
+
'apply',
|
|
27
|
+
'publish',
|
|
28
|
+
'rollback',
|
|
29
|
+
];
|
|
30
|
+
/** The file `pull` writes and `apply` reads, inside an agent's directory. */
|
|
31
|
+
export const AGENT_FILE = 'agent.json';
|
|
32
|
+
/** Instructions as Markdown, beside it. Used when `agent.json` has none. */
|
|
33
|
+
export const AGENT_INSTRUCTIONS_FILE = 'AGENT.md';
|
|
34
|
+
/**
|
|
35
|
+
* `agents` — the Source's agent templates (plan 239.0 / 239.2). Ekanos holds
|
|
36
|
+
* them; nothing here is deployed. `apply` stores a new version (a draft
|
|
37
|
+
* until published), `publish` makes a version the one every team runs,
|
|
38
|
+
* `rollback` publishes an older one. `pull` writes a template out as
|
|
39
|
+
* `agent.json` + `AGENT.md` for review or Git, and `apply` reads the same
|
|
40
|
+
* layout back.
|
|
41
|
+
*/
|
|
42
|
+
export async function runAgents(ctx, args) {
|
|
43
|
+
var _a, _b, _c;
|
|
44
|
+
const subcommand = parseSubcommand(args.subcommand);
|
|
45
|
+
// Read the local input before any network call, so a bad file fails fast.
|
|
46
|
+
const local = subcommand === 'apply' ? readAgentInput(args.target, args.dir) : null;
|
|
47
|
+
const env = resolveAuthEnvironment(ctx, args.host, args.env, {
|
|
48
|
+
projectDir: args.dir,
|
|
49
|
+
});
|
|
50
|
+
const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
|
|
51
|
+
const client = new AgentsClient(env, resolved.session);
|
|
52
|
+
const source = resolved.source;
|
|
53
|
+
switch (subcommand) {
|
|
54
|
+
case 'list': {
|
|
55
|
+
const list = await client.call((token) => listAgentTemplates(env.host, token, source));
|
|
56
|
+
return ctx.succeed(Object.assign({ host: env.host }, list), listSummary(ctx.ui, list));
|
|
57
|
+
}
|
|
58
|
+
case 'show': {
|
|
59
|
+
const key = requireKey(args.target, 'show');
|
|
60
|
+
const shown = await client.call((token) => showAgentTemplate(env.host, token, source, key));
|
|
61
|
+
return ctx.succeed(Object.assign({ host: env.host }, shown), templateDetail(ctx.ui, shown.template));
|
|
62
|
+
}
|
|
63
|
+
case 'pull': {
|
|
64
|
+
const key = requireKey(args.target, 'pull');
|
|
65
|
+
const shown = await client.call((token) => showAgentTemplate(env.host, token, source, key));
|
|
66
|
+
const outDir = path.resolve(args.dir, (_a = args.out) !== null && _a !== void 0 ? _a : path.join('agents', key));
|
|
67
|
+
const written = writeAgentFiles(outDir, shown.template);
|
|
68
|
+
return ctx.succeed({
|
|
69
|
+
host: env.host,
|
|
70
|
+
key,
|
|
71
|
+
version: shown.template.latestVersion,
|
|
72
|
+
files: written,
|
|
73
|
+
}, ctx.ui.result({
|
|
74
|
+
title: `Pulled ${key} v${shown.template.latestVersion}`,
|
|
75
|
+
rows: written.map((file) => ['Wrote', file]),
|
|
76
|
+
notes: [
|
|
77
|
+
`Edit, then "ekanos agents apply ${path.relative(args.dir, outDir) || '.'}".`,
|
|
78
|
+
],
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
case 'apply': {
|
|
82
|
+
const input = local;
|
|
83
|
+
const baseVersion = (_b = parseVersionFlag(args.baseVersion, '--base-version')) !== null && _b !== void 0 ? _b : input.baseVersion;
|
|
84
|
+
const result = await client.call((token) => applyAgentTemplate(env.host, token, {
|
|
85
|
+
source,
|
|
86
|
+
key: input.key,
|
|
87
|
+
body: input.body,
|
|
88
|
+
baseVersion,
|
|
89
|
+
overwrite: args.overwrite,
|
|
90
|
+
}));
|
|
91
|
+
if (input.file && result.outcome !== 'unchanged') {
|
|
92
|
+
recordBaseVersion(input.file, result.version);
|
|
93
|
+
}
|
|
94
|
+
return ctx.succeed(Object.assign({ host: env.host }, result), resultSummary(ctx.ui, result));
|
|
95
|
+
}
|
|
96
|
+
case 'publish':
|
|
97
|
+
case 'rollback': {
|
|
98
|
+
const key = requireKey(args.target, subcommand);
|
|
99
|
+
let version = (_c = parseVersionFlag(args.to, '--to')) !== null && _c !== void 0 ? _c : undefined;
|
|
100
|
+
if (subcommand === 'rollback' && version === undefined) {
|
|
101
|
+
throw usageError('rollback needs the version to go back to.', `Pass "--to <n>". "ekanos agents show ${key}" lists them.`);
|
|
102
|
+
}
|
|
103
|
+
// Refuse a script with no --yes before reading anything.
|
|
104
|
+
if (!args.yes && !canPrompt(ctx, args)) {
|
|
105
|
+
throw validationError(`${subcommand === 'rollback' ? 'Rolling back' : 'Publishing'} ${key} on ${source} needs confirmation.`, 'It reaches every team running this agent at once. Pass --yes to confirm.');
|
|
106
|
+
}
|
|
107
|
+
// Publish names the version it confirmed, never "whatever is latest
|
|
108
|
+
// when the request lands" — a draft applied in between is not shipped
|
|
109
|
+
// unseen to every team.
|
|
110
|
+
if (version === undefined) {
|
|
111
|
+
const shown = await client.call((token) => showAgentTemplate(env.host, token, source, key));
|
|
112
|
+
version = shown.template.latestVersion;
|
|
113
|
+
}
|
|
114
|
+
await confirmPublish(ctx, args, {
|
|
115
|
+
key,
|
|
116
|
+
source,
|
|
117
|
+
what: subcommand === 'rollback'
|
|
118
|
+
? `Roll ${key} back to version ${version}`
|
|
119
|
+
: `Publish ${key} version ${version}`,
|
|
120
|
+
});
|
|
121
|
+
const result = await client.call((token) => publishAgentTemplate(env.host, token, subcommand, {
|
|
122
|
+
source,
|
|
123
|
+
key,
|
|
124
|
+
version,
|
|
125
|
+
}));
|
|
126
|
+
return ctx.succeed(Object.assign({ host: env.host }, result), resultSummary(ctx.ui, result));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Every call refreshes the session once on a 401, then gives up. */
|
|
131
|
+
class AgentsClient {
|
|
132
|
+
constructor(env, session) {
|
|
133
|
+
this.env = env;
|
|
134
|
+
this.session = session;
|
|
135
|
+
}
|
|
136
|
+
async call(send) {
|
|
137
|
+
let result = await send(this.session.accessToken);
|
|
138
|
+
if (result.status === 'unauthorized') {
|
|
139
|
+
this.session = await refreshStoredSession(this.env, this.session);
|
|
140
|
+
result = await send(this.session.accessToken);
|
|
141
|
+
}
|
|
142
|
+
if (result.status === 'ok')
|
|
143
|
+
return result.data;
|
|
144
|
+
throw authRequiredError(`The session for ${this.env.host} could not authorize the agents call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function parseSubcommand(value) {
|
|
148
|
+
const match = AGENTS_SUBCOMMANDS.find((name) => name === value);
|
|
149
|
+
if (match)
|
|
150
|
+
return match;
|
|
151
|
+
throw usageError(value
|
|
152
|
+
? `Unknown agents subcommand "${value}".`
|
|
153
|
+
: 'agents needs a subcommand.', `Run one of: ${AGENTS_SUBCOMMANDS.map((name) => `ekanos agents ${name}`).join(', ')}.`);
|
|
154
|
+
}
|
|
155
|
+
function requireKey(value, verb) {
|
|
156
|
+
const parsed = AgentTemplateKeySchema.safeParse(value !== null && value !== void 0 ? value : '');
|
|
157
|
+
if (parsed.success)
|
|
158
|
+
return parsed.data;
|
|
159
|
+
throw usageError(value
|
|
160
|
+
? `"${value}" is not an agent key.`
|
|
161
|
+
: `agents ${verb} needs an agent key.`, `Pass the key, e.g. "ekanos agents ${verb} support-triage". ` +
|
|
162
|
+
`"ekanos agents list" shows them.`);
|
|
163
|
+
}
|
|
164
|
+
function parseVersionFlag(value, flag) {
|
|
165
|
+
if (value === undefined)
|
|
166
|
+
return null;
|
|
167
|
+
const version = Number(value);
|
|
168
|
+
if (Number.isInteger(version) && version >= 1)
|
|
169
|
+
return version;
|
|
170
|
+
throw usageError(`${flag} must be a version number (got "${value}").`, `"ekanos agents show <key>" lists the versions.`);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Read `apply`'s input: a directory holding `agent.json` (and optionally
|
|
174
|
+
* `AGENT.md`), or a `.json` file. The key is the file's `key`, else the
|
|
175
|
+
* directory's name. `baseVersion` in the file is the version it was pulled
|
|
176
|
+
* or last applied at — what the host checks for a concurrent change.
|
|
177
|
+
*/
|
|
178
|
+
export function readAgentInput(target, cwd) {
|
|
179
|
+
var _a;
|
|
180
|
+
var _b;
|
|
181
|
+
if (!target) {
|
|
182
|
+
throw usageError('agents apply needs a file or directory.', `Pass an agent directory ("ekanos agents apply agents/support") or a ` +
|
|
183
|
+
`JSON file. "ekanos agents pull <key>" writes the layout.`);
|
|
184
|
+
}
|
|
185
|
+
const resolved = path.resolve(cwd, target);
|
|
186
|
+
const kind = classifyPath(resolved);
|
|
187
|
+
const dir = kind === 'directory' ? resolved : path.dirname(resolved);
|
|
188
|
+
const file = kind === 'directory' ? path.join(resolved, AGENT_FILE) : resolved;
|
|
189
|
+
if (classifyPath(file) !== 'file') {
|
|
190
|
+
throw notFoundError(`No ${kind === 'directory' ? AGENT_FILE : 'file'} at ${file}.`, `"ekanos agents pull <key>" writes ${AGENT_FILE} and ${AGENT_INSTRUCTIONS_FILE}.`);
|
|
191
|
+
}
|
|
192
|
+
let raw;
|
|
193
|
+
try {
|
|
194
|
+
raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
throw validationError(`${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, 'Fix the JSON and apply again.');
|
|
198
|
+
}
|
|
199
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
200
|
+
throw validationError(`${file} must hold a JSON object.`, 'See "ekanos agents --help".');
|
|
201
|
+
}
|
|
202
|
+
const _c = raw, { key: rawKey, baseVersion: rawBase, $schema: _schema } = _c, rest = __rest(_c, ["key", "baseVersion", "$schema"]);
|
|
203
|
+
const fields = Object.assign({}, rest);
|
|
204
|
+
const instructionsFile = path.join(dir, AGENT_INSTRUCTIONS_FILE);
|
|
205
|
+
if (fields.instructions === undefined &&
|
|
206
|
+
classifyPath(instructionsFile) === 'file') {
|
|
207
|
+
fields.instructions = fs.readFileSync(instructionsFile, 'utf8');
|
|
208
|
+
}
|
|
209
|
+
const key = AgentTemplateKeySchema.safeParse(typeof rawKey === 'string' ? rawKey : path.basename(dir));
|
|
210
|
+
if (!key.success) {
|
|
211
|
+
throw validationError(`No usable agent key: set "key" in ${file}, or name the directory after it.`, (_b = (_a = key.error.issues[0]) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : 'Use lowercase letters, digits and hyphens.');
|
|
212
|
+
}
|
|
213
|
+
const body = AgentTemplateBodySchema.safeParse(fields);
|
|
214
|
+
if (!body.success) {
|
|
215
|
+
throw validationError([
|
|
216
|
+
`${file} is not a valid agent:`,
|
|
217
|
+
...body.error.issues.map((issue) => ` ${issue.path.join('.') || '(root)'}: ${issue.message}`),
|
|
218
|
+
].join('\n'), `Fields: name, description, instructions (or ${AGENT_INSTRUCTIONS_FILE}), ` +
|
|
219
|
+
`model, maxTurns, tools, policy, skills.`);
|
|
220
|
+
}
|
|
221
|
+
const baseVersion = typeof rawBase === 'number' && Number.isInteger(rawBase) && rawBase >= 1
|
|
222
|
+
? rawBase
|
|
223
|
+
: null;
|
|
224
|
+
return { key: key.data, body: body.data, baseVersion, file };
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Write `agent.json` + `AGENT.md`. The instructions live in the Markdown so
|
|
228
|
+
* they read and diff as prose; the JSON holds everything else plus the
|
|
229
|
+
* version they came from.
|
|
230
|
+
*/
|
|
231
|
+
function writeAgentFiles(outDir, template) {
|
|
232
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
233
|
+
const _a = template.body, { instructions } = _a, rest = __rest(_a, ["instructions"]);
|
|
234
|
+
const json = Object.assign({ key: template.key, baseVersion: template.latestVersion }, rest);
|
|
235
|
+
const jsonPath = path.join(outDir, AGENT_FILE);
|
|
236
|
+
const mdPath = path.join(outDir, AGENT_INSTRUCTIONS_FILE);
|
|
237
|
+
for (const target of [jsonPath, mdPath]) {
|
|
238
|
+
const kind = classifyPath(target);
|
|
239
|
+
if (kind !== 'missing' && kind !== 'file') {
|
|
240
|
+
throw validationError(`Refusing to write through ${target} (${kind}).`, 'Remove it, or pass --out <dir>.');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
fs.writeFileSync(jsonPath, `${JSON.stringify(json, null, 2)}\n`);
|
|
244
|
+
fs.writeFileSync(mdPath, instructions.endsWith('\n') ? instructions : `${instructions}\n`);
|
|
245
|
+
return [jsonPath, mdPath];
|
|
246
|
+
}
|
|
247
|
+
/** After a stored version, point the file at it, so the next apply is based on it. */
|
|
248
|
+
function recordBaseVersion(file, version) {
|
|
249
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
250
|
+
if (raw.baseVersion === version)
|
|
251
|
+
return;
|
|
252
|
+
const { key, baseVersion: _old } = raw, rest = __rest(raw, ["key", "baseVersion"]);
|
|
253
|
+
const next = key === undefined
|
|
254
|
+
? Object.assign({ baseVersion: version }, rest) : Object.assign({ key, baseVersion: version }, rest);
|
|
255
|
+
fs.writeFileSync(file, `${JSON.stringify(next, null, 2)}\n`);
|
|
256
|
+
}
|
|
257
|
+
function canPrompt(ctx, args) {
|
|
258
|
+
var _a;
|
|
259
|
+
return !ctx.jsonMode && ((_a = args.stdinIsTTY) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Publishing changes what every team running the agent does, at once. A
|
|
263
|
+
* human run asks first; a scripted or JSON run needs `--yes`.
|
|
264
|
+
*/
|
|
265
|
+
async function confirmPublish(ctx, args, target) {
|
|
266
|
+
var _a;
|
|
267
|
+
if (args.yes)
|
|
268
|
+
return;
|
|
269
|
+
if (!canPrompt(ctx, args)) {
|
|
270
|
+
throw validationError(`${target.what} on ${target.source} needs confirmation.`, 'It reaches every team running this agent at once. Pass --yes to confirm.');
|
|
271
|
+
}
|
|
272
|
+
ctx.log(`${target.what} on ${target.source}. Every team running it follows at once.`);
|
|
273
|
+
const confirmed = await ((_a = args.confirm) !== null && _a !== void 0 ? _a : promptYesNo)('Proceed? [y/N]');
|
|
274
|
+
if (!confirmed) {
|
|
275
|
+
throw validationError(`${target.what} was not confirmed.`, 'Nothing was changed.');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function versionLabel(template) {
|
|
279
|
+
if (template.publishedVersion === null)
|
|
280
|
+
return `v${template.latestVersion} draft, never published`;
|
|
281
|
+
if (template.publishedVersion === template.latestVersion)
|
|
282
|
+
return `v${template.latestVersion} published`;
|
|
283
|
+
return `v${template.publishedVersion} published, v${template.latestVersion} latest`;
|
|
284
|
+
}
|
|
285
|
+
function listSummary(ui, list) {
|
|
286
|
+
if (list.templates.length === 0) {
|
|
287
|
+
return ui.result({
|
|
288
|
+
title: `No agents on ${list.source.slug} yet`,
|
|
289
|
+
tone: 'warn',
|
|
290
|
+
notes: ['"ekanos agents apply <dir>" stores the first one.'],
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return [
|
|
294
|
+
ui.result({
|
|
295
|
+
title: `${pluralize(list.templates.length, 'agent')} on ${list.source.slug}`,
|
|
296
|
+
rows: list.templates.map((template) => [
|
|
297
|
+
template.key,
|
|
298
|
+
[
|
|
299
|
+
template.name,
|
|
300
|
+
versionLabel(template),
|
|
301
|
+
template.status === 'archived' ? 'archived' : null,
|
|
302
|
+
template.teams
|
|
303
|
+
? `${pluralize(template.teams.enabled, 'team')}`
|
|
304
|
+
: null,
|
|
305
|
+
]
|
|
306
|
+
.filter(Boolean)
|
|
307
|
+
.join(' · '),
|
|
308
|
+
]),
|
|
309
|
+
}),
|
|
310
|
+
ui.hint('"ekanos agents show <key>" shows one, with its versions.'),
|
|
311
|
+
].join('\n\n');
|
|
312
|
+
}
|
|
313
|
+
function templateDetail(ui, template) {
|
|
314
|
+
var _a;
|
|
315
|
+
const { body } = template;
|
|
316
|
+
const rows = [
|
|
317
|
+
['Versions', versionLabel(template)],
|
|
318
|
+
['Model', (_a = body.model) !== null && _a !== void 0 ? _a : 'house default'],
|
|
319
|
+
['Tools', describeTools(body)],
|
|
320
|
+
[
|
|
321
|
+
'Approval',
|
|
322
|
+
`reads ${body.policy.reads}, writes ${body.policy.writes}` +
|
|
323
|
+
(Object.keys(body.policy.overrides).length > 0
|
|
324
|
+
? `, ${pluralize(Object.keys(body.policy.overrides).length, 'override')}`
|
|
325
|
+
: ''),
|
|
326
|
+
],
|
|
327
|
+
['Skills', body.skills.length > 0 ? body.skills.join(', ') : 'none'],
|
|
328
|
+
['Connections', describeConnections(body)],
|
|
329
|
+
['Teams may add', describeCustomization(body)],
|
|
330
|
+
];
|
|
331
|
+
if (template.teams) {
|
|
332
|
+
rows.push([
|
|
333
|
+
'Teams',
|
|
334
|
+
`${template.teams.enabled} running` +
|
|
335
|
+
(template.teams.pinned > 0
|
|
336
|
+
? `, ${template.teams.pinned} pinned to an older version`
|
|
337
|
+
: ''),
|
|
338
|
+
]);
|
|
339
|
+
}
|
|
340
|
+
rows.push([
|
|
341
|
+
'History',
|
|
342
|
+
template.versions.map((version) => `v${version.version} ${version.createdAt.slice(0, 10)}` +
|
|
343
|
+
(version.createdBy ? ` by ${version.createdBy}` : '') +
|
|
344
|
+
(version.publishedAt
|
|
345
|
+
? ` · published ${version.publishedAt.slice(0, 10)}`
|
|
346
|
+
: '')),
|
|
347
|
+
]);
|
|
348
|
+
return ui.result({ title: `${body.name} (${template.key})`, rows });
|
|
349
|
+
}
|
|
350
|
+
function describeConnections(body) {
|
|
351
|
+
const owner = (connection) => connection.owner === 'source'
|
|
352
|
+
? 'one connection for the source'
|
|
353
|
+
: `each team's own (${connection.team})`;
|
|
354
|
+
const lines = [
|
|
355
|
+
...Object.entries(body.connections).map(([slug, connection]) => `${slug}: ${owner(connection)}`),
|
|
356
|
+
...body.mcpServers.map((server) => `${server.key} (MCP, ${server.url}): ${owner(server)}`),
|
|
357
|
+
];
|
|
358
|
+
return lines.length > 0 ? lines : ["each team's own (optional)"];
|
|
359
|
+
}
|
|
360
|
+
function describeCustomization(body) {
|
|
361
|
+
const allowed = Object.entries(body.teamCustomization)
|
|
362
|
+
.filter(([, on]) => on)
|
|
363
|
+
.map(([knob]) => { var _a; return (_a = CUSTOMIZATION_LABEL[knob]) !== null && _a !== void 0 ? _a : knob; });
|
|
364
|
+
return allowed.length > 0 ? allowed.join(', ') : 'nothing (template only)';
|
|
365
|
+
}
|
|
366
|
+
const CUSTOMIZATION_LABEL = {
|
|
367
|
+
mcpServers: 'own MCP servers',
|
|
368
|
+
skills: 'own skills',
|
|
369
|
+
integrations: 'more integrations',
|
|
370
|
+
narrow: 'narrower tools',
|
|
371
|
+
};
|
|
372
|
+
function describeTools(body) {
|
|
373
|
+
var _a, _b;
|
|
374
|
+
if (body.tools.mode === 'read_only')
|
|
375
|
+
return 'every read the team has connected';
|
|
376
|
+
if (body.tools.mode === 'all')
|
|
377
|
+
return 'every tool the team has connected';
|
|
378
|
+
return ([...((_a = body.tools.products) !== null && _a !== void 0 ? _a : []), ...((_b = body.tools.tools) !== null && _b !== void 0 ? _b : [])].join(', ') ||
|
|
379
|
+
'none');
|
|
380
|
+
}
|
|
381
|
+
const OUTCOME_TITLE = {
|
|
382
|
+
created: 'Created',
|
|
383
|
+
updated: 'Stored a new version',
|
|
384
|
+
unchanged: 'Unchanged',
|
|
385
|
+
published: 'Published',
|
|
386
|
+
'rolled-back': 'Rolled back',
|
|
387
|
+
};
|
|
388
|
+
function resultSummary(ui, result) {
|
|
389
|
+
const { template } = result;
|
|
390
|
+
const rows = [['Versions', versionLabel(template)]];
|
|
391
|
+
if (result.outcome === 'published' || result.outcome === 'rolled-back') {
|
|
392
|
+
rows.push(['Teams updated', String(result.teamsUpdated)]);
|
|
393
|
+
}
|
|
394
|
+
const notes = (result.outcome === 'created' || result.outcome === 'updated') &&
|
|
395
|
+
template.publishedVersion !== template.latestVersion
|
|
396
|
+
? [
|
|
397
|
+
`Teams still run the published version. "ekanos agents publish ${template.key}" moves them.`,
|
|
398
|
+
]
|
|
399
|
+
: [];
|
|
400
|
+
return ui.result({
|
|
401
|
+
title: `${OUTCOME_TITLE[result.outcome]}: ${template.key}`,
|
|
402
|
+
tone: result.outcome === 'unchanged' ? 'warn' : 'ok',
|
|
403
|
+
rows,
|
|
404
|
+
notes,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AgentTemplateBody,\n AgentTemplateBodySchema,\n type AgentTemplateDetail,\n AgentTemplateKeySchema,\n type AgentTemplateList,\n type AgentTemplateResult,\n} from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n type AgentsApiResult,\n applyAgentTemplate,\n listAgentTemplates,\n publishAgentTemplate,\n showAgentTemplate,\n} from '../agents-api';\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 {\n authRequiredError,\n notFoundError,\n usageError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { classifyPath } from '../safe-fs';\nimport { resolveSourceTarget } from '../source-target';\nimport { type ResultRow, type Ui, pluralize } from '../ui';\nimport { promptYesNo } from './publish';\n\nexport const AGENTS_SUBCOMMANDS = [\n 'list',\n 'show',\n 'pull',\n 'apply',\n 'publish',\n 'rollback',\n] as const;\n\ntype AgentsSubcommand = (typeof AGENTS_SUBCOMMANDS)[number];\n\n/** The file `pull` writes and `apply` reads, inside an agent's directory. */\nexport const AGENT_FILE = 'agent.json';\n/** Instructions as Markdown, beside it. Used when `agent.json` has none. */\nexport const AGENT_INSTRUCTIONS_FILE = 'AGENT.md';\n\nexport interface AgentsArgs {\n host?: string;\n source?: string;\n subcommand?: string;\n /** A template key, or for `apply` a file or directory. */\n target?: string;\n /** `publish` / `rollback`: the version to make the one teams run. */\n to?: string;\n baseVersion?: string;\n overwrite: boolean;\n out?: string;\n yes: boolean;\n dir: string;\n env: Record<string, string | undefined>;\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/**\n * `agents` — the Source's agent templates (plan 239.0 / 239.2). Ekanos holds\n * them; nothing here is deployed. `apply` stores a new version (a draft\n * until published), `publish` makes a version the one every team runs,\n * `rollback` publishes an older one. `pull` writes a template out as\n * `agent.json` + `AGENT.md` for review or Git, and `apply` reads the same\n * layout back.\n */\nexport async function runAgents(\n ctx: CliContext,\n args: AgentsArgs,\n): Promise<ExitCode> {\n const subcommand = parseSubcommand(args.subcommand);\n\n // Read the local input before any network call, so a bad file fails fast.\n const local =\n subcommand === 'apply' ? readAgentInput(args.target, args.dir) : null;\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 AgentsClient(env, resolved.session);\n const source = resolved.source;\n\n switch (subcommand) {\n case 'list': {\n const list = await client.call((token) =>\n listAgentTemplates(env.host, token, source),\n );\n return ctx.succeed(\n { host: env.host, ...list },\n listSummary(ctx.ui, list),\n );\n }\n case 'show': {\n const key = requireKey(args.target, 'show');\n const shown = await client.call((token) =>\n showAgentTemplate(env.host, token, source, key),\n );\n return ctx.succeed(\n { host: env.host, ...shown },\n templateDetail(ctx.ui, shown.template),\n );\n }\n case 'pull': {\n const key = requireKey(args.target, 'pull');\n const shown = await client.call((token) =>\n showAgentTemplate(env.host, token, source, key),\n );\n const outDir = path.resolve(\n args.dir,\n args.out ?? path.join('agents', key),\n );\n const written = writeAgentFiles(outDir, shown.template);\n return ctx.succeed(\n {\n host: env.host,\n key,\n version: shown.template.latestVersion,\n files: written,\n },\n ctx.ui.result({\n title: `Pulled ${key} v${shown.template.latestVersion}`,\n rows: written.map((file): ResultRow => ['Wrote', file]),\n notes: [\n `Edit, then \"ekanos agents apply ${path.relative(args.dir, outDir) || '.'}\".`,\n ],\n }),\n );\n }\n case 'apply': {\n const input = local!;\n const baseVersion =\n parseVersionFlag(args.baseVersion, '--base-version') ??\n input.baseVersion;\n const result = await client.call((token) =>\n applyAgentTemplate(env.host, token, {\n source,\n key: input.key,\n body: input.body,\n baseVersion,\n overwrite: args.overwrite,\n }),\n );\n if (input.file && result.outcome !== 'unchanged') {\n recordBaseVersion(input.file, result.version);\n }\n return ctx.succeed(\n { host: env.host, ...result },\n resultSummary(ctx.ui, result),\n );\n }\n case 'publish':\n case 'rollback': {\n const key = requireKey(args.target, subcommand);\n let version = parseVersionFlag(args.to, '--to') ?? undefined;\n if (subcommand === 'rollback' && version === undefined) {\n throw usageError(\n 'rollback needs the version to go back to.',\n `Pass \"--to <n>\". \"ekanos agents show ${key}\" lists them.`,\n );\n }\n // Refuse a script with no --yes before reading anything.\n if (!args.yes && !canPrompt(ctx, args)) {\n throw validationError(\n `${subcommand === 'rollback' ? 'Rolling back' : 'Publishing'} ${key} on ${source} needs confirmation.`,\n 'It reaches every team running this agent at once. Pass --yes to confirm.',\n );\n }\n // Publish names the version it confirmed, never \"whatever is latest\n // when the request lands\" — a draft applied in between is not shipped\n // unseen to every team.\n if (version === undefined) {\n const shown = await client.call((token) =>\n showAgentTemplate(env.host, token, source, key),\n );\n version = shown.template.latestVersion;\n }\n await confirmPublish(ctx, args, {\n key,\n source,\n what:\n subcommand === 'rollback'\n ? `Roll ${key} back to version ${version}`\n : `Publish ${key} version ${version}`,\n });\n const result = await client.call((token) =>\n publishAgentTemplate(env.host, token, subcommand, {\n source,\n key,\n version,\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n resultSummary(ctx.ui, result),\n );\n }\n }\n}\n\n/** Every call refreshes the session once on a 401, then gives up. */\nclass AgentsClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<AgentsApiResult<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 agents call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n}\n\nfunction parseSubcommand(value: string | undefined): AgentsSubcommand {\n const match = AGENTS_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value\n ? `Unknown agents subcommand \"${value}\".`\n : 'agents needs a subcommand.',\n `Run one of: ${AGENTS_SUBCOMMANDS.map((name) => `ekanos agents ${name}`).join(', ')}.`,\n );\n}\n\nfunction requireKey(value: string | undefined, verb: string): string {\n const parsed = AgentTemplateKeySchema.safeParse(value ?? '');\n if (parsed.success) return parsed.data;\n throw usageError(\n value\n ? `\"${value}\" is not an agent key.`\n : `agents ${verb} needs an agent key.`,\n `Pass the key, e.g. \"ekanos agents ${verb} support-triage\". ` +\n `\"ekanos agents list\" shows them.`,\n );\n}\n\nfunction parseVersionFlag(\n value: string | undefined,\n flag: string,\n): number | null {\n if (value === undefined) return null;\n const version = Number(value);\n if (Number.isInteger(version) && version >= 1) return version;\n throw usageError(\n `${flag} must be a version number (got \"${value}\").`,\n `\"ekanos agents show <key>\" lists the versions.`,\n );\n}\n\ninterface AgentInput {\n key: string;\n body: AgentTemplateBody;\n baseVersion: number | null;\n /** The agent.json read, when there was one, so the new version is recorded in it. */\n file: string | null;\n}\n\n/**\n * Read `apply`'s input: a directory holding `agent.json` (and optionally\n * `AGENT.md`), or a `.json` file. The key is the file's `key`, else the\n * directory's name. `baseVersion` in the file is the version it was pulled\n * or last applied at — what the host checks for a concurrent change.\n */\nexport function readAgentInput(\n target: string | undefined,\n cwd: string,\n): AgentInput {\n if (!target) {\n throw usageError(\n 'agents apply needs a file or directory.',\n `Pass an agent directory (\"ekanos agents apply agents/support\") or a ` +\n `JSON file. \"ekanos agents pull <key>\" writes the layout.`,\n );\n }\n const resolved = path.resolve(cwd, target);\n const kind = classifyPath(resolved);\n const dir = kind === 'directory' ? resolved : path.dirname(resolved);\n const file =\n kind === 'directory' ? path.join(resolved, AGENT_FILE) : resolved;\n\n if (classifyPath(file) !== 'file') {\n throw notFoundError(\n `No ${kind === 'directory' ? AGENT_FILE : 'file'} at ${file}.`,\n `\"ekanos agents pull <key>\" writes ${AGENT_FILE} and ${AGENT_INSTRUCTIONS_FILE}.`,\n );\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(file, 'utf8'));\n } catch (error) {\n throw validationError(\n `${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n 'Fix the JSON and apply again.',\n );\n }\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw validationError(\n `${file} must hold a JSON object.`,\n 'See \"ekanos agents --help\".',\n );\n }\n\n const {\n key: rawKey,\n baseVersion: rawBase,\n $schema: _schema,\n ...rest\n } = raw as Record<string, unknown>;\n const fields: Record<string, unknown> = { ...rest };\n\n const instructionsFile = path.join(dir, AGENT_INSTRUCTIONS_FILE);\n if (\n fields.instructions === undefined &&\n classifyPath(instructionsFile) === 'file'\n ) {\n fields.instructions = fs.readFileSync(instructionsFile, 'utf8');\n }\n\n const key = AgentTemplateKeySchema.safeParse(\n typeof rawKey === 'string' ? rawKey : path.basename(dir),\n );\n if (!key.success) {\n throw validationError(\n `No usable agent key: set \"key\" in ${file}, or name the directory after it.`,\n key.error.issues[0]?.message ??\n 'Use lowercase letters, digits and hyphens.',\n );\n }\n\n const body = AgentTemplateBodySchema.safeParse(fields);\n if (!body.success) {\n throw validationError(\n [\n `${file} is not a valid agent:`,\n ...body.error.issues.map(\n (issue) => ` ${issue.path.join('.') || '(root)'}: ${issue.message}`,\n ),\n ].join('\\n'),\n `Fields: name, description, instructions (or ${AGENT_INSTRUCTIONS_FILE}), ` +\n `model, maxTurns, tools, policy, skills.`,\n );\n }\n\n const baseVersion =\n typeof rawBase === 'number' && Number.isInteger(rawBase) && rawBase >= 1\n ? rawBase\n : null;\n\n return { key: key.data, body: body.data, baseVersion, file };\n}\n\n/**\n * Write `agent.json` + `AGENT.md`. The instructions live in the Markdown so\n * they read and diff as prose; the JSON holds everything else plus the\n * version they came from.\n */\nfunction writeAgentFiles(\n outDir: string,\n template: AgentTemplateDetail,\n): string[] {\n fs.mkdirSync(outDir, { recursive: true });\n const { instructions, ...rest } = template.body;\n const json = {\n key: template.key,\n baseVersion: template.latestVersion,\n ...rest,\n };\n const jsonPath = path.join(outDir, AGENT_FILE);\n const mdPath = path.join(outDir, AGENT_INSTRUCTIONS_FILE);\n for (const target of [jsonPath, mdPath]) {\n const kind = classifyPath(target);\n if (kind !== 'missing' && kind !== 'file') {\n throw validationError(\n `Refusing to write through ${target} (${kind}).`,\n 'Remove it, or pass --out <dir>.',\n );\n }\n }\n fs.writeFileSync(jsonPath, `${JSON.stringify(json, null, 2)}\\n`);\n fs.writeFileSync(\n mdPath,\n instructions.endsWith('\\n') ? instructions : `${instructions}\\n`,\n );\n return [jsonPath, mdPath];\n}\n\n/** After a stored version, point the file at it, so the next apply is based on it. */\nfunction recordBaseVersion(file: string, version: number) {\n const raw = JSON.parse(fs.readFileSync(file, 'utf8')) as Record<\n string,\n unknown\n >;\n if (raw.baseVersion === version) return;\n const { key, baseVersion: _old, ...rest } = raw;\n const next =\n key === undefined\n ? { baseVersion: version, ...rest }\n : { key, baseVersion: version, ...rest };\n fs.writeFileSync(file, `${JSON.stringify(next, null, 2)}\\n`);\n}\n\nfunction canPrompt(ctx: CliContext, args: AgentsArgs): boolean {\n return !ctx.jsonMode && (args.stdinIsTTY ?? process.stdin.isTTY === true);\n}\n\n/**\n * Publishing changes what every team running the agent does, at once. A\n * human run asks first; a scripted or JSON run needs `--yes`.\n */\nasync function confirmPublish(\n ctx: CliContext,\n args: AgentsArgs,\n target: { key: string; source: string; what: string },\n) {\n if (args.yes) return;\n if (!canPrompt(ctx, args)) {\n throw validationError(\n `${target.what} on ${target.source} needs confirmation.`,\n 'It reaches every team running this agent at once. Pass --yes to confirm.',\n );\n }\n ctx.log(\n `${target.what} on ${target.source}. Every team running it follows at once.`,\n );\n const confirmed = await (args.confirm ?? promptYesNo)('Proceed? [y/N]');\n if (!confirmed) {\n throw validationError(\n `${target.what} was not confirmed.`,\n 'Nothing was changed.',\n );\n }\n}\n\nfunction versionLabel(template: {\n latestVersion: number;\n publishedVersion: number | null;\n}): string {\n if (template.publishedVersion === null)\n return `v${template.latestVersion} draft, never published`;\n if (template.publishedVersion === template.latestVersion)\n return `v${template.latestVersion} published`;\n return `v${template.publishedVersion} published, v${template.latestVersion} latest`;\n}\n\nfunction listSummary(ui: Ui, list: AgentTemplateList): string {\n if (list.templates.length === 0) {\n return ui.result({\n title: `No agents on ${list.source.slug} yet`,\n tone: 'warn',\n notes: ['\"ekanos agents apply <dir>\" stores the first one.'],\n });\n }\n return [\n ui.result({\n title: `${pluralize(list.templates.length, 'agent')} on ${list.source.slug}`,\n rows: list.templates.map((template): ResultRow => [\n template.key,\n [\n template.name,\n versionLabel(template),\n template.status === 'archived' ? 'archived' : null,\n template.teams\n ? `${pluralize(template.teams.enabled, 'team')}`\n : null,\n ]\n .filter(Boolean)\n .join(' · '),\n ]),\n }),\n ui.hint('\"ekanos agents show <key>\" shows one, with its versions.'),\n ].join('\\n\\n');\n}\n\nfunction templateDetail(ui: Ui, template: AgentTemplateDetail): string {\n const { body } = template;\n const rows: ResultRow[] = [\n ['Versions', versionLabel(template)],\n ['Model', body.model ?? 'house default'],\n ['Tools', describeTools(body)],\n [\n 'Approval',\n `reads ${body.policy.reads}, writes ${body.policy.writes}` +\n (Object.keys(body.policy.overrides).length > 0\n ? `, ${pluralize(Object.keys(body.policy.overrides).length, 'override')}`\n : ''),\n ],\n ['Skills', body.skills.length > 0 ? body.skills.join(', ') : 'none'],\n ['Connections', describeConnections(body)],\n ['Teams may add', describeCustomization(body)],\n ];\n if (template.teams) {\n rows.push([\n 'Teams',\n `${template.teams.enabled} running` +\n (template.teams.pinned > 0\n ? `, ${template.teams.pinned} pinned to an older version`\n : ''),\n ]);\n }\n rows.push([\n 'History',\n template.versions.map(\n (version) =>\n `v${version.version} ${version.createdAt.slice(0, 10)}` +\n (version.createdBy ? ` by ${version.createdBy}` : '') +\n (version.publishedAt\n ? ` · published ${version.publishedAt.slice(0, 10)}`\n : ''),\n ),\n ]);\n return ui.result({ title: `${body.name} (${template.key})`, rows });\n}\n\nfunction describeConnections(body: AgentTemplateBody): string[] {\n const owner = (connection: AgentTemplateBody['connections'][string]) =>\n connection.owner === 'source'\n ? 'one connection for the source'\n : `each team's own (${connection.team})`;\n const lines = [\n ...Object.entries(body.connections).map(\n ([slug, connection]) => `${slug}: ${owner(connection)}`,\n ),\n ...body.mcpServers.map(\n (server) => `${server.key} (MCP, ${server.url}): ${owner(server)}`,\n ),\n ];\n return lines.length > 0 ? lines : [\"each team's own (optional)\"];\n}\n\nfunction describeCustomization(body: AgentTemplateBody): string {\n const allowed = Object.entries(body.teamCustomization)\n .filter(([, on]) => on)\n .map(([knob]) => CUSTOMIZATION_LABEL[knob] ?? knob);\n return allowed.length > 0 ? allowed.join(', ') : 'nothing (template only)';\n}\n\nconst CUSTOMIZATION_LABEL: Record<string, string> = {\n mcpServers: 'own MCP servers',\n skills: 'own skills',\n integrations: 'more integrations',\n narrow: 'narrower tools',\n};\n\nfunction describeTools(body: AgentTemplateBody): string {\n if (body.tools.mode === 'read_only')\n return 'every read the team has connected';\n if (body.tools.mode === 'all') return 'every tool the team has connected';\n return (\n [...(body.tools.products ?? []), ...(body.tools.tools ?? [])].join(', ') ||\n 'none'\n );\n}\n\nconst OUTCOME_TITLE = {\n created: 'Created',\n updated: 'Stored a new version',\n unchanged: 'Unchanged',\n published: 'Published',\n 'rolled-back': 'Rolled back',\n} as const;\n\nfunction resultSummary(ui: Ui, result: AgentTemplateResult): string {\n const { template } = result;\n const rows: ResultRow[] = [['Versions', versionLabel(template)]];\n if (result.outcome === 'published' || result.outcome === 'rolled-back') {\n rows.push(['Teams updated', String(result.teamsUpdated)]);\n }\n const notes =\n (result.outcome === 'created' || result.outcome === 'updated') &&\n template.publishedVersion !== template.latestVersion\n ? [\n `Teams still run the published version. \"ekanos agents publish ${template.key}\" moves them.`,\n ]\n : [];\n return ui.result({\n title: `${OUTCOME_TITLE[result.outcome]}: ${template.key}`,\n tone: result.outcome === 'unchanged' ? 'warn' : 'ok',\n rows,\n notes,\n });\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CliContext } from '../context.js';
|
|
2
|
+
import type { ExitCode } from '../exit-codes.js';
|
|
3
|
+
export interface CatalogArgs {
|
|
4
|
+
host?: string;
|
|
5
|
+
source?: string;
|
|
6
|
+
/** One product's detail, when given. */
|
|
7
|
+
product?: string;
|
|
8
|
+
dir: string;
|
|
9
|
+
env: Record<string, string | undefined>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `catalog` — what the Source can already do (plan 239.0). Lists every
|
|
13
|
+
* product the Source can use, with its tools, agent events and the steps
|
|
14
|
+
* left to set it up; `ekanos catalog <product>` shows one in full.
|
|
15
|
+
*
|
|
16
|
+
* Read before building: an agent migration asks it first, so a capability
|
|
17
|
+
* the Source already has is set up rather than rebuilt. Works outside a
|
|
18
|
+
* project — the Source comes from `--source`, else `ekanos.json`, else the
|
|
19
|
+
* caller's only seat.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runCatalog(ctx: CliContext, args: CatalogArgs): Promise<ExitCode>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
|
|
2
|
+
import { fetchSourceCatalog, } from '../catalog-api.js';
|
|
3
|
+
import { authRequiredError } from '../errors.js';
|
|
4
|
+
import { resolveSourceTarget } from '../source-target.js';
|
|
5
|
+
import { pluralize } from '../ui.js';
|
|
6
|
+
/**
|
|
7
|
+
* `catalog` — what the Source can already do (plan 239.0). Lists every
|
|
8
|
+
* product the Source can use, with its tools, agent events and the steps
|
|
9
|
+
* left to set it up; `ekanos catalog <product>` shows one in full.
|
|
10
|
+
*
|
|
11
|
+
* Read before building: an agent migration asks it first, so a capability
|
|
12
|
+
* the Source already has is set up rather than rebuilt. Works outside a
|
|
13
|
+
* project — the Source comes from `--source`, else `ekanos.json`, else the
|
|
14
|
+
* caller's only seat.
|
|
15
|
+
*/
|
|
16
|
+
export async function runCatalog(ctx, args) {
|
|
17
|
+
const env = resolveAuthEnvironment(ctx, args.host, args.env, {
|
|
18
|
+
projectDir: args.dir,
|
|
19
|
+
});
|
|
20
|
+
let session = requireSession(env.store, env.host);
|
|
21
|
+
const resolved = await resolveSourceTarget(env, session, args);
|
|
22
|
+
session = resolved.session;
|
|
23
|
+
const query = { source: resolved.source, product: args.product };
|
|
24
|
+
let result = await fetchSourceCatalog(env.host, session.accessToken, query);
|
|
25
|
+
if (result.status === 'unauthorized') {
|
|
26
|
+
session = await refreshStoredSession(env, session);
|
|
27
|
+
result = await fetchSourceCatalog(env.host, session.accessToken, query);
|
|
28
|
+
}
|
|
29
|
+
const catalog = requireCatalog(result, env.host);
|
|
30
|
+
return ctx.succeed(Object.assign({ host: env.host }, catalog), args.product
|
|
31
|
+
? productDetail(ctx.ui, catalog.products[0])
|
|
32
|
+
: catalogSummary(ctx.ui, catalog));
|
|
33
|
+
}
|
|
34
|
+
function requireCatalog(result, host) {
|
|
35
|
+
if (result.status === 'ok')
|
|
36
|
+
return result.catalog;
|
|
37
|
+
throw authRequiredError(`The session for ${host} could not authorize the catalog read.`, `Run "ekanos login --host ${host}" and retry.`);
|
|
38
|
+
}
|
|
39
|
+
const AVAILABILITY_LABEL = {
|
|
40
|
+
enabled: 'Enabled on this Source',
|
|
41
|
+
'turned-off': 'Granted but switched off',
|
|
42
|
+
available: 'Available — ask Ekanos to enable',
|
|
43
|
+
};
|
|
44
|
+
function catalogSummary(ui, catalog) {
|
|
45
|
+
const blocks = ['enabled', 'turned-off', 'available']
|
|
46
|
+
.map((availability) => {
|
|
47
|
+
const products = catalog.products.filter((product) => product.availability === availability);
|
|
48
|
+
if (products.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
return ui.result({
|
|
51
|
+
title: `${AVAILABILITY_LABEL[availability]} (${products.length})`,
|
|
52
|
+
tone: availability === 'enabled' ? 'ok' : 'warn',
|
|
53
|
+
rows: products.map((product) => [
|
|
54
|
+
product.slug,
|
|
55
|
+
[product.name, describeShape(product), openSteps(product)]
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
.join(' · '),
|
|
58
|
+
]),
|
|
59
|
+
});
|
|
60
|
+
})
|
|
61
|
+
.filter((block) => block !== null);
|
|
62
|
+
return [
|
|
63
|
+
...blocks,
|
|
64
|
+
ui.hint(`${pluralize(catalog.products.length, 'product')} on ${catalog.source.slug}. ` +
|
|
65
|
+
`"ekanos catalog <product>" shows tools, agent events and setup steps.`),
|
|
66
|
+
].join('\n\n');
|
|
67
|
+
}
|
|
68
|
+
function productDetail(ui, product) {
|
|
69
|
+
var _a;
|
|
70
|
+
const rows = [
|
|
71
|
+
['What', (_a = product.summary) !== null && _a !== void 0 ? _a : '—'],
|
|
72
|
+
[
|
|
73
|
+
'Kind',
|
|
74
|
+
product.kind === 'experience' ? 'Ekanos Experience' : 'Integration',
|
|
75
|
+
],
|
|
76
|
+
['Status', AVAILABILITY_LABEL[product.availability]],
|
|
77
|
+
];
|
|
78
|
+
if (product.connectedTeams !== null) {
|
|
79
|
+
rows.push(['Teams connected', String(product.connectedTeams)]);
|
|
80
|
+
}
|
|
81
|
+
if (product.tools.length > 0) {
|
|
82
|
+
rows.push([
|
|
83
|
+
'Tools',
|
|
84
|
+
product.tools.map((tool) => `${tool.name} (${tool.effect}${tool.sensitivity === 'undeclared' ? '' : `, ${tool.sensitivity}`})`),
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
if (product.agentEvents.length > 0) {
|
|
88
|
+
rows.push([
|
|
89
|
+
'Agent triggers',
|
|
90
|
+
product.agentEvents.map((event) => `${event.id} — ${event.label}`),
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
if (product.agentReplyTool) {
|
|
94
|
+
rows.push(['Replies with', product.agentReplyTool]);
|
|
95
|
+
}
|
|
96
|
+
rows.push([
|
|
97
|
+
'Setup',
|
|
98
|
+
product.setupSteps.map((step, index) => `${index + 1}. [${step.state}] ${step.title} (${step.actor}) — ${step.detail}` +
|
|
99
|
+
(step.guideUrl ? ` ${step.guideUrl}` : '')),
|
|
100
|
+
]);
|
|
101
|
+
return ui.result({ title: `${product.name} (${product.slug})`, rows });
|
|
102
|
+
}
|
|
103
|
+
function describeShape(product) {
|
|
104
|
+
const parts = [
|
|
105
|
+
product.tools.length > 0 ? pluralize(product.tools.length, 'tool') : null,
|
|
106
|
+
product.agentEvents.length > 0
|
|
107
|
+
? pluralize(product.agentEvents.length, 'agent trigger')
|
|
108
|
+
: null,
|
|
109
|
+
].filter(Boolean);
|
|
110
|
+
return parts.join(', ');
|
|
111
|
+
}
|
|
112
|
+
function openSteps(product) {
|
|
113
|
+
const open = product.setupSteps.filter((step) => step.state === 'todo');
|
|
114
|
+
return open.length === 0 ? '' : `${pluralize(open.length, 'step')} left`;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/commands/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CatalogProduct, SourceCatalog } from '@ekanos/integration-schema';\n\nimport {\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport {\n type FetchSourceCatalogResult,\n fetchSourceCatalog,\n} from '../catalog-api';\nimport type { CliContext } from '../context';\nimport { authRequiredError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { resolveSourceTarget } from '../source-target';\nimport { type Ui, pluralize } from '../ui';\n\nexport interface CatalogArgs {\n host?: string;\n source?: string;\n /** One product's detail, when given. */\n product?: string;\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `catalog` — what the Source can already do (plan 239.0). Lists every\n * product the Source can use, with its tools, agent events and the steps\n * left to set it up; `ekanos catalog <product>` shows one in full.\n *\n * Read before building: an agent migration asks it first, so a capability\n * the Source already has is set up rather than rebuilt. Works outside a\n * project — the Source comes from `--source`, else `ekanos.json`, else the\n * caller's only seat.\n */\nexport async function runCatalog(\n ctx: CliContext,\n args: CatalogArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n let session = requireSession(env.store, env.host);\n\n const resolved = await resolveSourceTarget(env, session, args);\n session = resolved.session;\n\n const query = { source: resolved.source, product: args.product };\n let result = await fetchSourceCatalog(env.host, session.accessToken, query);\n if (result.status === 'unauthorized') {\n session = await refreshStoredSession(env, session);\n result = await fetchSourceCatalog(env.host, session.accessToken, query);\n }\n const catalog = requireCatalog(result, env.host);\n\n return ctx.succeed(\n { host: env.host, ...catalog },\n args.product\n ? productDetail(ctx.ui, catalog.products[0]!)\n : catalogSummary(ctx.ui, catalog),\n );\n}\n\nfunction requireCatalog(\n result: FetchSourceCatalogResult,\n host: string,\n): SourceCatalog {\n if (result.status === 'ok') return result.catalog;\n throw authRequiredError(\n `The session for ${host} could not authorize the catalog read.`,\n `Run \"ekanos login --host ${host}\" and retry.`,\n );\n}\n\nconst AVAILABILITY_LABEL = {\n enabled: 'Enabled on this Source',\n 'turned-off': 'Granted but switched off',\n available: 'Available — ask Ekanos to enable',\n} as const;\n\nfunction catalogSummary(ui: Ui, catalog: SourceCatalog): string {\n const blocks = (['enabled', 'turned-off', 'available'] as const)\n .map((availability) => {\n const products = catalog.products.filter(\n (product) => product.availability === availability,\n );\n if (products.length === 0) return null;\n return ui.result({\n title: `${AVAILABILITY_LABEL[availability]} (${products.length})`,\n tone: availability === 'enabled' ? 'ok' : 'warn',\n rows: products.map((product): [string, string] => [\n product.slug,\n [product.name, describeShape(product), openSteps(product)]\n .filter(Boolean)\n .join(' · '),\n ]),\n });\n })\n .filter((block): block is string => block !== null);\n\n return [\n ...blocks,\n ui.hint(\n `${pluralize(catalog.products.length, 'product')} on ${catalog.source.slug}. ` +\n `\"ekanos catalog <product>\" shows tools, agent events and setup steps.`,\n ),\n ].join('\\n\\n');\n}\n\nfunction productDetail(ui: Ui, product: CatalogProduct): string {\n const rows: [string, string | string[]][] = [\n ['What', product.summary ?? '—'],\n [\n 'Kind',\n product.kind === 'experience' ? 'Ekanos Experience' : 'Integration',\n ],\n ['Status', AVAILABILITY_LABEL[product.availability]],\n ];\n if (product.connectedTeams !== null) {\n rows.push(['Teams connected', String(product.connectedTeams)]);\n }\n if (product.tools.length > 0) {\n rows.push([\n 'Tools',\n product.tools.map(\n (tool) =>\n `${tool.name} (${tool.effect}${tool.sensitivity === 'undeclared' ? '' : `, ${tool.sensitivity}`})`,\n ),\n ]);\n }\n if (product.agentEvents.length > 0) {\n rows.push([\n 'Agent triggers',\n product.agentEvents.map((event) => `${event.id} — ${event.label}`),\n ]);\n }\n if (product.agentReplyTool) {\n rows.push(['Replies with', product.agentReplyTool]);\n }\n rows.push([\n 'Setup',\n product.setupSteps.map(\n (step, index) =>\n `${index + 1}. [${step.state}] ${step.title} (${step.actor}) — ${step.detail}` +\n (step.guideUrl ? ` ${step.guideUrl}` : ''),\n ),\n ]);\n\n return ui.result({ title: `${product.name} (${product.slug})`, rows });\n}\n\nfunction describeShape(product: CatalogProduct): string {\n const parts = [\n product.tools.length > 0 ? pluralize(product.tools.length, 'tool') : null,\n product.agentEvents.length > 0\n ? pluralize(product.agentEvents.length, 'agent trigger')\n : null,\n ].filter(Boolean);\n return parts.join(', ');\n}\n\nfunction openSteps(product: CatalogProduct): string {\n const open = product.setupSteps.filter((step) => step.state === 'todo');\n return open.length === 0 ? '' : `${pluralize(open.length, 'step')} left`;\n}\n"]}
|