@ekanos/cli 0.4.2 → 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 +2 -2
- package/templates/claude-skill.md.tmpl +20 -1
- package/templates/package.json.tmpl +4 -4
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ npx @ekanos/cli dev
|
|
|
27
27
|
| `whoami` | Print the identity the stored session authenticates as, plus a compact `seats` list (slug + roles) from the same endpoint `sources` uses — degrades to `seats: null` rather than failing if that lookup is unavailable. |
|
|
28
28
|
| `status` | The read verb: login state for the resolved host, this project's `ekanos.json`, and its submissions on the host (`GET /api/partner/submissions`). Never fails just because you are logged out — it reports `authenticated: false` instead. |
|
|
29
29
|
| `sources` | List every Ekanos source you hold a developer or source-admin seat on (`GET /api/partner/sources`), with a marker on this project's current publish target. |
|
|
30
|
+
| `catalog` | `ekanos catalog [<product-slug>]` — what a source can already use (`GET /api/partner/catalog`): every product enabled, switched off or available to enable, with its tools, agent triggers and generated setup steps. Check it before building something the source may already have. |
|
|
31
|
+
| `agents` | `ekanos agents list | show | pull | apply | publish | rollback` — the source's agents, held by Ekanos (`/api/partner/agents`). `apply` stores a new version as a draft, `publish` makes a version the one every team runs, `rollback` publishes an earlier one. Either seat applies; only the source admin publishes.
|
|
30
32
|
| `use` | `ekanos use <source-slug>` — set this project's publish target to a source you hold a seat on. Refuses (`forbidden`, with your actual seats in the hint) for a slug you don't hold a seat on, so a typo can't silently repoint a project. |
|
|
31
33
|
| `publish` | Validate, pack and submit the integration to an Ekanos host — refusing (`forbidden`) a target you hold no seat on, and confirming an ambiguous multi-seat target before submitting. See [Publish target verification](#publish-target-verification). |
|
|
32
34
|
| `upgrade` | Move this toolchain to a coherent target version, with verify-and-rollback. `--check` reports current vs. target and changes nothing; a bare `upgrade` installs, verifies (`validate` + the test script), and rolls back automatically on any failure. See [Keeping the toolchain current](#keeping-the-toolchain-current). |
|
|
@@ -419,7 +421,7 @@ ekanos logout
|
|
|
419
421
|
|
|
420
422
|
### Host resolution
|
|
421
423
|
|
|
422
|
-
`logout`, `whoami`, `status`, `sources`, `use` and `publish` resolve the Ekanos
|
|
424
|
+
`logout`, `whoami`, `status`, `sources`, `catalog`, `agents`, `use` and `publish` resolve the Ekanos
|
|
423
425
|
host in this order:
|
|
424
426
|
|
|
425
427
|
1. the `--host` flag
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type AgentTemplateApplyRequest, type AgentTemplateList, type AgentTemplateResult, type AgentTemplateShow } from '@ekanos/integration-schema';
|
|
2
|
+
/**
|
|
3
|
+
* The CLI's client for Source agent templates (plan 239.0 / 239.2).
|
|
4
|
+
*
|
|
5
|
+
* ---------------------------------------------------------------------------
|
|
6
|
+
* WIRE CONTRACT — the `AgentTemplate*` schemas in `@ekanos/integration-schema`,
|
|
7
|
+
* which the host's `/api/partner/agents` routes build and this parses.
|
|
8
|
+
*
|
|
9
|
+
* GET /api/partner/agents?source=<slug|id> → { ok, data: AgentTemplateList }
|
|
10
|
+
* GET /api/partner/agents/<key>?source=<slug|id> → { ok, data: AgentTemplateShow }
|
|
11
|
+
* POST /api/partner/agents AgentTemplateApplyRequest → { ok, data: AgentTemplateResult }
|
|
12
|
+
* POST /api/partner/agents/<key>/publish { source, version? } → { ok, data: AgentTemplateResult }
|
|
13
|
+
* POST /api/partner/agents/<key>/rollback { source, version } → { ok, data: AgentTemplateResult }
|
|
14
|
+
*
|
|
15
|
+
* → 401 / 3xx refresh once and retry
|
|
16
|
+
* → 400 { code: "INVALID_AGENT", issues: AgentTemplateIssue[] }
|
|
17
|
+
* → 403 { code: "MFA_REQUIRED" | "FORBIDDEN" | "SOURCE_ADMIN_REQUIRED" }
|
|
18
|
+
* → 404 { code: "SOURCE_NOT_FOUND" | "TEMPLATE_NOT_FOUND" | "VERSION_NOT_FOUND" }
|
|
19
|
+
* → 409 { code: "VERSION_CONFLICT", latestVersion }
|
|
20
|
+
* ---------------------------------------------------------------------------
|
|
21
|
+
*/
|
|
22
|
+
export type AgentsApiResult<T> = {
|
|
23
|
+
status: 'ok';
|
|
24
|
+
data: T;
|
|
25
|
+
} | {
|
|
26
|
+
status: 'unauthorized';
|
|
27
|
+
};
|
|
28
|
+
export declare function listAgentTemplates(host: string, accessToken: string, source: string): Promise<AgentsApiResult<AgentTemplateList>>;
|
|
29
|
+
export declare function showAgentTemplate(host: string, accessToken: string, source: string, key: string): Promise<AgentsApiResult<AgentTemplateShow>>;
|
|
30
|
+
export declare function applyAgentTemplate(host: string, accessToken: string, body: AgentTemplateApplyRequest): Promise<AgentsApiResult<AgentTemplateResult>>;
|
|
31
|
+
export declare function publishAgentTemplate(host: string, accessToken: string, action: 'publish' | 'rollback', params: {
|
|
32
|
+
source: string;
|
|
33
|
+
key: string;
|
|
34
|
+
version?: number;
|
|
35
|
+
}): Promise<AgentsApiResult<AgentTemplateResult>>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { AgentTemplateIssueSchema, AgentTemplateListSchema, AgentTemplateResultSchema, AgentTemplateShowSchema, } from '@ekanos/integration-schema';
|
|
2
|
+
import { request } from './auth/ekanos-api.js';
|
|
3
|
+
import { authRequiredError, forbiddenError, invalidStateError, networkError, notFoundError, usageError, validationError, } from './errors.js';
|
|
4
|
+
const ROUTE = '/api/partner/agents';
|
|
5
|
+
export function listAgentTemplates(host, accessToken, source) {
|
|
6
|
+
return call(host, accessToken, AgentTemplateListSchema, {
|
|
7
|
+
method: 'GET',
|
|
8
|
+
path: `${ROUTE}?${new URLSearchParams({ source })}`,
|
|
9
|
+
subject: source,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function showAgentTemplate(host, accessToken, source, key) {
|
|
13
|
+
return call(host, accessToken, AgentTemplateShowSchema, {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
path: `${ROUTE}/${encodeURIComponent(key)}?${new URLSearchParams({ source })}`,
|
|
16
|
+
subject: key,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function applyAgentTemplate(host, accessToken, body) {
|
|
20
|
+
return call(host, accessToken, AgentTemplateResultSchema, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
path: ROUTE,
|
|
23
|
+
body,
|
|
24
|
+
subject: body.key,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function publishAgentTemplate(host, accessToken, action, params) {
|
|
28
|
+
return call(host, accessToken, AgentTemplateResultSchema, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
path: `${ROUTE}/${encodeURIComponent(params.key)}/${action}`,
|
|
31
|
+
body: { source: params.source, version: params.version },
|
|
32
|
+
subject: params.key,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function call(host, accessToken, schema, init) {
|
|
36
|
+
const response = await request(`${host}${init.path}`, {
|
|
37
|
+
method: init.method,
|
|
38
|
+
headers: Object.assign({ accept: 'application/json', authorization: `Bearer ${accessToken}` }, (init.body === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { 'content-type': 'application/json' })),
|
|
41
|
+
body: init.body === undefined ? undefined : JSON.stringify(init.body),
|
|
42
|
+
});
|
|
43
|
+
if (response.status === 401 || isRedirect(response.status)) {
|
|
44
|
+
return { status: 'unauthorized' };
|
|
45
|
+
}
|
|
46
|
+
const body = await readOptionalJson(response);
|
|
47
|
+
const code = str(body.code);
|
|
48
|
+
const message = str(body.error);
|
|
49
|
+
if (response.status === 400 && code === 'INVALID_AGENT') {
|
|
50
|
+
const issues = readIssues(body.issues);
|
|
51
|
+
throw validationError([
|
|
52
|
+
message !== null && message !== void 0 ? message : 'The host refused the agent.',
|
|
53
|
+
...issues.map((issue) => ` ${issue.path}: ${issue.message}`),
|
|
54
|
+
].join('\n'), `Fix the agent and apply again. "ekanos catalog" lists the products ` +
|
|
55
|
+
`and tools this Source has.`);
|
|
56
|
+
}
|
|
57
|
+
if (response.status === 400) {
|
|
58
|
+
throw usageError(message !== null && message !== void 0 ? message : 'The host refused the request.', 'See "ekanos agents --help" for each subcommand\'s arguments.');
|
|
59
|
+
}
|
|
60
|
+
if (response.status === 403 && code === 'MFA_REQUIRED') {
|
|
61
|
+
throw authRequiredError(message !== null && message !== void 0 ? message : `${host} requires multi-factor authentication.`, `Complete MFA in the browser, then run "ekanos login" again.`);
|
|
62
|
+
}
|
|
63
|
+
if (response.status === 403) {
|
|
64
|
+
throw forbiddenError(message !== null && message !== void 0 ? message : 'Not permitted on this Source.', code === 'SOURCE_ADMIN_REQUIRED'
|
|
65
|
+
? `Only a Source admin publishes. Ask one to run it, or apply as a draft.`
|
|
66
|
+
: `Run "ekanos sources" to list the sources you hold a seat on.`);
|
|
67
|
+
}
|
|
68
|
+
if (response.status === 404) {
|
|
69
|
+
throw notFoundError(message !== null && message !== void 0 ? message : `"${init.subject}" was not found.`, code === 'SOURCE_NOT_FOUND'
|
|
70
|
+
? `Run "ekanos sources" to list the sources you hold a seat on.`
|
|
71
|
+
: `Run "ekanos agents list" to see this Source's agents and versions.`);
|
|
72
|
+
}
|
|
73
|
+
if (response.status === 409 && code === 'VERSION_CONFLICT') {
|
|
74
|
+
const latest = typeof body.latestVersion === 'number' ? body.latestVersion : null;
|
|
75
|
+
throw invalidStateError(message !== null && message !== void 0 ? message : `"${init.subject}" changed since you read it` +
|
|
76
|
+
(latest ? ` (now version ${latest}).` : '.'), `Run "ekanos agents show ${init.subject}" to see the latest, then apply ` +
|
|
77
|
+
`again — or pass --overwrite to replace it.`);
|
|
78
|
+
}
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw networkError(`${host}${init.path.split('?')[0]} responded ${response.status}.`, response.status >= 500
|
|
81
|
+
? `The Ekanos deployment returned a server error. Retry shortly.`
|
|
82
|
+
: `Confirm the host runs a build that includes the agents routes.`);
|
|
83
|
+
}
|
|
84
|
+
const parsed = schema.safeParse(body.data);
|
|
85
|
+
if (!parsed.success) {
|
|
86
|
+
throw networkError(`${host}${ROUTE} answered in a shape this CLI does not read.`, `Run "ekanos upgrade" — the CLI and the host disagree on the agents contract.`);
|
|
87
|
+
}
|
|
88
|
+
return { status: 'ok', data: parsed.data };
|
|
89
|
+
}
|
|
90
|
+
function readIssues(value) {
|
|
91
|
+
if (!Array.isArray(value))
|
|
92
|
+
return [];
|
|
93
|
+
return value.flatMap((issue) => {
|
|
94
|
+
const parsed = AgentTemplateIssueSchema.safeParse(issue);
|
|
95
|
+
return parsed.success ? [parsed.data] : [];
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function readOptionalJson(response) {
|
|
99
|
+
try {
|
|
100
|
+
const parsed = await response.json();
|
|
101
|
+
return typeof parsed === 'object' && parsed !== null
|
|
102
|
+
? parsed
|
|
103
|
+
: {};
|
|
104
|
+
}
|
|
105
|
+
catch (_a) {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function isRedirect(status) {
|
|
110
|
+
return status >= 300 && status < 400;
|
|
111
|
+
}
|
|
112
|
+
function str(value) {
|
|
113
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=agents-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-api.js","sourceRoot":"","sources":["../src/agents-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AgentTemplateApplyRequest,\n type AgentTemplateIssue,\n AgentTemplateIssueSchema,\n type AgentTemplateList,\n AgentTemplateListSchema,\n type AgentTemplateResult,\n AgentTemplateResultSchema,\n type AgentTemplateShow,\n AgentTemplateShowSchema,\n} from '@ekanos/integration-schema';\nimport type { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n usageError,\n validationError,\n} from './errors';\n\n/**\n * The CLI's client for Source agent templates (plan 239.0 / 239.2).\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the `AgentTemplate*` schemas in `@ekanos/integration-schema`,\n * which the host's `/api/partner/agents` routes build and this parses.\n *\n * GET /api/partner/agents?source=<slug|id> → { ok, data: AgentTemplateList }\n * GET /api/partner/agents/<key>?source=<slug|id> → { ok, data: AgentTemplateShow }\n * POST /api/partner/agents AgentTemplateApplyRequest → { ok, data: AgentTemplateResult }\n * POST /api/partner/agents/<key>/publish { source, version? } → { ok, data: AgentTemplateResult }\n * POST /api/partner/agents/<key>/rollback { source, version } → { ok, data: AgentTemplateResult }\n *\n * → 401 / 3xx refresh once and retry\n * → 400 { code: \"INVALID_AGENT\", issues: AgentTemplateIssue[] }\n * → 403 { code: \"MFA_REQUIRED\" | \"FORBIDDEN\" | \"SOURCE_ADMIN_REQUIRED\" }\n * → 404 { code: \"SOURCE_NOT_FOUND\" | \"TEMPLATE_NOT_FOUND\" | \"VERSION_NOT_FOUND\" }\n * → 409 { code: \"VERSION_CONFLICT\", latestVersion }\n * ---------------------------------------------------------------------------\n */\n\nexport type AgentsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nconst ROUTE = '/api/partner/agents';\n\nexport function listAgentTemplates(\n host: string,\n accessToken: string,\n source: string,\n): Promise<AgentsApiResult<AgentTemplateList>> {\n return call(host, accessToken, AgentTemplateListSchema, {\n method: 'GET',\n path: `${ROUTE}?${new URLSearchParams({ source })}`,\n subject: source,\n });\n}\n\nexport function showAgentTemplate(\n host: string,\n accessToken: string,\n source: string,\n key: string,\n): Promise<AgentsApiResult<AgentTemplateShow>> {\n return call(host, accessToken, AgentTemplateShowSchema, {\n method: 'GET',\n path: `${ROUTE}/${encodeURIComponent(key)}?${new URLSearchParams({ source })}`,\n subject: key,\n });\n}\n\nexport function applyAgentTemplate(\n host: string,\n accessToken: string,\n body: AgentTemplateApplyRequest,\n): Promise<AgentsApiResult<AgentTemplateResult>> {\n return call(host, accessToken, AgentTemplateResultSchema, {\n method: 'POST',\n path: ROUTE,\n body,\n subject: body.key,\n });\n}\n\nexport function publishAgentTemplate(\n host: string,\n accessToken: string,\n action: 'publish' | 'rollback',\n params: { source: string; key: string; version?: number },\n): Promise<AgentsApiResult<AgentTemplateResult>> {\n return call(host, accessToken, AgentTemplateResultSchema, {\n method: 'POST',\n path: `${ROUTE}/${encodeURIComponent(params.key)}/${action}`,\n body: { source: params.source, version: params.version },\n subject: params.key,\n });\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n /** What a 404 is about, for the message. */\n subject: string;\n },\n): Promise<AgentsApiResult<T>> {\n const response = await request(`${host}${init.path}`, {\n method: init.method,\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n ...(init.body === undefined\n ? {}\n : { 'content-type': 'application/json' }),\n },\n body: init.body === undefined ? undefined : JSON.stringify(init.body),\n });\n\n if (response.status === 401 || isRedirect(response.status)) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = str(body.code);\n const message = str(body.error);\n\n if (response.status === 400 && code === 'INVALID_AGENT') {\n const issues = readIssues(body.issues);\n throw validationError(\n [\n message ?? 'The host refused the agent.',\n ...issues.map((issue) => ` ${issue.path}: ${issue.message}`),\n ].join('\\n'),\n `Fix the agent and apply again. \"ekanos catalog\" lists the products ` +\n `and tools this Source has.`,\n );\n }\n\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos agents --help\" for each subcommand\\'s arguments.',\n );\n }\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n `Complete MFA in the browser, then run \"ekanos login\" again.`,\n );\n }\n\n if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n code === 'SOURCE_ADMIN_REQUIRED'\n ? `Only a Source admin publishes. Ask one to run it, or apply as a draft.`\n : `Run \"ekanos sources\" to list the sources you hold a seat on.`,\n );\n }\n\n if (response.status === 404) {\n throw notFoundError(\n message ?? `\"${init.subject}\" was not found.`,\n code === 'SOURCE_NOT_FOUND'\n ? `Run \"ekanos sources\" to list the sources you hold a seat on.`\n : `Run \"ekanos agents list\" to see this Source's agents and versions.`,\n );\n }\n\n if (response.status === 409 && code === 'VERSION_CONFLICT') {\n const latest =\n typeof body.latestVersion === 'number' ? body.latestVersion : null;\n throw invalidStateError(\n message ??\n `\"${init.subject}\" changed since you read it` +\n (latest ? ` (now version ${latest}).` : '.'),\n `Run \"ekanos agents show ${init.subject}\" to see the latest, then apply ` +\n `again — or pass --overwrite to replace it.`,\n );\n }\n\n if (!response.ok) {\n throw networkError(\n `${host}${init.path.split('?')[0]} responded ${response.status}.`,\n response.status >= 500\n ? `The Ekanos deployment returned a server error. Retry shortly.`\n : `Confirm the host runs a build that includes the agents routes.`,\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}${ROUTE} answered in a shape this CLI does not read.`,\n `Run \"ekanos upgrade\" — the CLI and the host disagree on the agents contract.`,\n );\n }\n\n return { status: 'ok', data: parsed.data };\n}\n\nfunction readIssues(value: unknown): AgentTemplateIssue[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap((issue) => {\n const parsed = AgentTemplateIssueSchema.safeParse(issue);\n return parsed.success ? [parsed.data] : [];\n });\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction isRedirect(status: number): boolean {\n return status >= 300 && status < 400;\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type SourceCatalog } from '@ekanos/integration-schema';
|
|
2
|
+
/**
|
|
3
|
+
* The CLI's client for the Source catalog (plan 239.0).
|
|
4
|
+
*
|
|
5
|
+
* ---------------------------------------------------------------------------
|
|
6
|
+
* WIRE CONTRACT — `SourceCatalogSchema` in `@ekanos/integration-schema`,
|
|
7
|
+
* which the host's `/api/partner/catalog` route builds and this parses.
|
|
8
|
+
*
|
|
9
|
+
* GET /api/partner/catalog?source=<slug|id>[&product=<slug>]
|
|
10
|
+
* [Authorization: Bearer <access_token>]
|
|
11
|
+
* → 200 { ok: true, data: SourceCatalog }
|
|
12
|
+
* → 401 / 3xx refresh once and retry
|
|
13
|
+
* → 403 { code: "MFA_REQUIRED" | "FORBIDDEN" }
|
|
14
|
+
* → 404 { code: "SOURCE_NOT_FOUND" | "PRODUCT_NOT_FOUND" }
|
|
15
|
+
* ---------------------------------------------------------------------------
|
|
16
|
+
*/
|
|
17
|
+
export type FetchSourceCatalogResult = {
|
|
18
|
+
status: 'ok';
|
|
19
|
+
catalog: SourceCatalog;
|
|
20
|
+
} | {
|
|
21
|
+
status: 'unauthorized';
|
|
22
|
+
};
|
|
23
|
+
export declare function fetchSourceCatalog(host: string, accessToken: string, query: {
|
|
24
|
+
source: string;
|
|
25
|
+
product?: string;
|
|
26
|
+
}): Promise<FetchSourceCatalogResult>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { SourceCatalogSchema, } from '@ekanos/integration-schema';
|
|
2
|
+
import { request } from './auth/ekanos-api.js';
|
|
3
|
+
import { authRequiredError, forbiddenError, networkError, notFoundError, } from './errors.js';
|
|
4
|
+
export async function fetchSourceCatalog(host, accessToken, query) {
|
|
5
|
+
var _a;
|
|
6
|
+
const params = new URLSearchParams({ source: query.source });
|
|
7
|
+
if (query.product)
|
|
8
|
+
params.set('product', query.product);
|
|
9
|
+
const response = await request(`${host}/api/partner/catalog?${params.toString()}`, {
|
|
10
|
+
method: 'GET',
|
|
11
|
+
headers: {
|
|
12
|
+
accept: 'application/json',
|
|
13
|
+
authorization: `Bearer ${accessToken}`,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
if (response.status === 401 || isRedirect(response.status)) {
|
|
17
|
+
return { status: 'unauthorized' };
|
|
18
|
+
}
|
|
19
|
+
const body = await readOptionalJson(response);
|
|
20
|
+
const code = str(body.code);
|
|
21
|
+
const message = str(body.error);
|
|
22
|
+
if (response.status === 403 && code === 'MFA_REQUIRED') {
|
|
23
|
+
throw authRequiredError(message !== null && message !== void 0 ? message : `${host} requires multi-factor authentication.`, `Complete MFA in the browser, then run "ekanos login" again.`);
|
|
24
|
+
}
|
|
25
|
+
if (response.status === 403) {
|
|
26
|
+
throw forbiddenError(message !== null && message !== void 0 ? message : `No seat on "${query.source}".`, `Run "ekanos sources" to list the sources you hold a seat on.`);
|
|
27
|
+
}
|
|
28
|
+
if (response.status === 404) {
|
|
29
|
+
throw notFoundError(message !== null && message !== void 0 ? message : `"${(_a = query.product) !== null && _a !== void 0 ? _a : query.source}" was not found.`, code === 'PRODUCT_NOT_FOUND'
|
|
30
|
+
? `Run "ekanos catalog" to list the products this Source can use.`
|
|
31
|
+
: `Run "ekanos sources" to list the sources you hold a seat on.`);
|
|
32
|
+
}
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw networkError(`${host}/api/partner/catalog responded ${response.status}.`, response.status >= 500
|
|
35
|
+
? `The Ekanos deployment returned a server error. Retry shortly.`
|
|
36
|
+
: `Confirm the host runs a build that includes the catalog route.`);
|
|
37
|
+
}
|
|
38
|
+
const parsed = SourceCatalogSchema.safeParse(body.data);
|
|
39
|
+
if (!parsed.success) {
|
|
40
|
+
throw networkError(`${host}/api/partner/catalog answered in a shape this CLI does not read.`, `Run "ekanos upgrade" — the CLI and the host disagree on the catalog contract.`);
|
|
41
|
+
}
|
|
42
|
+
return { status: 'ok', catalog: parsed.data };
|
|
43
|
+
}
|
|
44
|
+
async function readOptionalJson(response) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = await response.json();
|
|
47
|
+
return typeof parsed === 'object' && parsed !== null
|
|
48
|
+
? parsed
|
|
49
|
+
: {};
|
|
50
|
+
}
|
|
51
|
+
catch (_a) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isRedirect(status) {
|
|
56
|
+
return status >= 300 && status < 400;
|
|
57
|
+
}
|
|
58
|
+
function str(value) {
|
|
59
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=catalog-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-api.js","sourceRoot":"","sources":["../src/catalog-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type SourceCatalog,\n SourceCatalogSchema,\n} from '@ekanos/integration-schema';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n networkError,\n notFoundError,\n} from './errors';\n\n/**\n * The CLI's client for the Source catalog (plan 239.0).\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — `SourceCatalogSchema` in `@ekanos/integration-schema`,\n * which the host's `/api/partner/catalog` route builds and this parses.\n *\n * GET /api/partner/catalog?source=<slug|id>[&product=<slug>]\n * [Authorization: Bearer <access_token>]\n * → 200 { ok: true, data: SourceCatalog }\n * → 401 / 3xx refresh once and retry\n * → 403 { code: \"MFA_REQUIRED\" | \"FORBIDDEN\" }\n * → 404 { code: \"SOURCE_NOT_FOUND\" | \"PRODUCT_NOT_FOUND\" }\n * ---------------------------------------------------------------------------\n */\n\nexport type FetchSourceCatalogResult =\n | { status: 'ok'; catalog: SourceCatalog }\n | { status: 'unauthorized' };\n\nexport async function fetchSourceCatalog(\n host: string,\n accessToken: string,\n query: { source: string; product?: string },\n): Promise<FetchSourceCatalogResult> {\n const params = new URLSearchParams({ source: query.source });\n if (query.product) params.set('product', query.product);\n\n const response = await request(\n `${host}/api/partner/catalog?${params.toString()}`,\n {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n },\n );\n\n if (response.status === 401 || isRedirect(response.status)) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = str(body.code);\n const message = str(body.error);\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n `Complete MFA in the browser, then run \"ekanos login\" again.`,\n );\n }\n\n if (response.status === 403) {\n throw forbiddenError(\n message ?? `No seat on \"${query.source}\".`,\n `Run \"ekanos sources\" to list the sources you hold a seat on.`,\n );\n }\n\n if (response.status === 404) {\n throw notFoundError(\n message ?? `\"${query.product ?? query.source}\" was not found.`,\n code === 'PRODUCT_NOT_FOUND'\n ? `Run \"ekanos catalog\" to list the products this Source can use.`\n : `Run \"ekanos sources\" to list the sources you hold a seat on.`,\n );\n }\n\n if (!response.ok) {\n throw networkError(\n `${host}/api/partner/catalog responded ${response.status}.`,\n response.status >= 500\n ? `The Ekanos deployment returned a server error. Retry shortly.`\n : `Confirm the host runs a build that includes the catalog route.`,\n );\n }\n\n const parsed = SourceCatalogSchema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/catalog answered in a shape this CLI does not read.`,\n `Run \"ekanos upgrade\" — the CLI and the host disagree on the catalog contract.`,\n );\n }\n\n return { status: 'ok', catalog: parsed.data };\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction isRedirect(status: number): boolean {\n return status >= 300 && status < 400;\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type AgentTemplateBody } from '@ekanos/integration-schema';
|
|
2
|
+
import type { CliContext } from '../context.js';
|
|
3
|
+
import type { ExitCode } from '../exit-codes.js';
|
|
4
|
+
export declare const AGENTS_SUBCOMMANDS: readonly ['list', 'show', 'pull', 'apply', 'publish', 'rollback'];
|
|
5
|
+
/** The file `pull` writes and `apply` reads, inside an agent's directory. */
|
|
6
|
+
export declare const AGENT_FILE = "agent.json";
|
|
7
|
+
/** Instructions as Markdown, beside it. Used when `agent.json` has none. */
|
|
8
|
+
export declare const AGENT_INSTRUCTIONS_FILE = "AGENT.md";
|
|
9
|
+
export interface AgentsArgs {
|
|
10
|
+
host?: string;
|
|
11
|
+
source?: string;
|
|
12
|
+
subcommand?: string;
|
|
13
|
+
/** A template key, or for `apply` a file or directory. */
|
|
14
|
+
target?: string;
|
|
15
|
+
/** `publish` / `rollback`: the version to make the one teams run. */
|
|
16
|
+
to?: string;
|
|
17
|
+
baseVersion?: string;
|
|
18
|
+
overwrite: boolean;
|
|
19
|
+
out?: string;
|
|
20
|
+
yes: boolean;
|
|
21
|
+
dir: string;
|
|
22
|
+
env: Record<string, string | undefined>;
|
|
23
|
+
stdinIsTTY?: boolean;
|
|
24
|
+
confirm?: (question: string) => Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* `agents` — the Source's agent templates (plan 239.0 / 239.2). Ekanos holds
|
|
28
|
+
* them; nothing here is deployed. `apply` stores a new version (a draft
|
|
29
|
+
* until published), `publish` makes a version the one every team runs,
|
|
30
|
+
* `rollback` publishes an older one. `pull` writes a template out as
|
|
31
|
+
* `agent.json` + `AGENT.md` for review or Git, and `apply` reads the same
|
|
32
|
+
* layout back.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runAgents(ctx: CliContext, args: AgentsArgs): Promise<ExitCode>;
|
|
35
|
+
interface AgentInput {
|
|
36
|
+
key: string;
|
|
37
|
+
body: AgentTemplateBody;
|
|
38
|
+
baseVersion: number | null;
|
|
39
|
+
/** The agent.json read, when there was one, so the new version is recorded in it. */
|
|
40
|
+
file: string | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read `apply`'s input: a directory holding `agent.json` (and optionally
|
|
44
|
+
* `AGENT.md`), or a `.json` file. The key is the file's `key`, else the
|
|
45
|
+
* directory's name. `baseVersion` in the file is the version it was pulled
|
|
46
|
+
* or last applied at — what the host checks for a concurrent change.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readAgentInput(target: string | undefined, cwd: string): AgentInput;
|
|
49
|
+
export {};
|