@ekanos/cli 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/commands/agents-edit.d.ts +49 -0
- package/dist/commands/agents-edit.js +385 -0
- package/dist/commands/agents-edit.js.map +1 -0
- package/dist/commands/agents.d.ts +24 -1
- package/dist/commands/agents.js +117 -8
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/apps.d.ts +6 -2
- package/dist/commands/apps.js +249 -4
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/reviewers.d.ts +21 -0
- package/dist/commands/reviewers.js +162 -0
- package/dist/commands/reviewers.js.map +1 -0
- package/dist/commands/routes.d.ts +23 -0
- package/dist/commands/routes.js +277 -0
- package/dist/commands/routes.js.map +1 -0
- package/dist/commands/runs.d.ts +42 -0
- package/dist/commands/runs.js +362 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/skills.d.ts +60 -0
- package/dist/commands/skills.js +397 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/team-agents-agents.d.ts +9 -0
- package/dist/commands/team-agents-agents.js +188 -0
- package/dist/commands/team-agents-agents.js.map +1 -0
- package/dist/commands/team-agents-mcp.d.ts +4 -0
- package/dist/commands/team-agents-mcp.js +218 -0
- package/dist/commands/team-agents-mcp.js.map +1 -0
- package/dist/commands/team-agents-triggers.d.ts +12 -0
- package/dist/commands/team-agents-triggers.js +251 -0
- package/dist/commands/team-agents-triggers.js.map +1 -0
- package/dist/commands/team-agents.d.ts +31 -0
- package/dist/commands/team-agents.js +71 -0
- package/dist/commands/team-agents.js.map +1 -0
- package/dist/commands/teams-onboard.d.ts +51 -0
- package/dist/commands/teams-onboard.js +261 -0
- package/dist/commands/teams-onboard.js.map +1 -0
- package/dist/commands/teams.d.ts +16 -1
- package/dist/commands/teams.js +22 -3
- package/dist/commands/teams.js.map +1 -1
- package/dist/connections-api.d.ts +125 -0
- package/dist/connections-api.js +156 -0
- package/dist/connections-api.js.map +1 -0
- package/dist/index.js +226 -6
- package/dist/index.js.map +1 -1
- package/dist/reviewers-api.d.ts +33 -0
- package/dist/reviewers-api.js +81 -0
- package/dist/reviewers-api.js.map +1 -0
- package/dist/routes-api.d.ts +105 -0
- package/dist/routes-api.js +147 -0
- package/dist/routes-api.js.map +1 -0
- package/dist/runs-api.d.ts +34 -0
- package/dist/runs-api.js +112 -0
- package/dist/runs-api.js.map +1 -0
- package/dist/skills-api.d.ts +28 -0
- package/dist/skills-api.js +104 -0
- package/dist/skills-api.js.map +1 -0
- package/dist/teams-api.d.ts +45 -3
- package/dist/teams-api.js +72 -3
- package/dist/teams-api.js.map +1 -1
- package/package.json +2 -2
- package/templates/package.json.tmpl +4 -4
- package/templates/skills/build-agent/SKILL.md +94 -10
- package/templates/skills/build-agent/references/agent-json.md +85 -4
- package/templates/skills/build-agent/references/rollout.md +9 -1
- package/templates/skills/build-agent/references/set-up-an-integration.md +59 -3
- package/templates/skills/build-agent/references/skills.md +81 -0
- package/templates/skills/build-agent/references/triggers.md +25 -5
- package/templates/skills/ekanos-platform/SKILL.md +116 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-api.js","sourceRoot":"","sources":["../src/routes-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n networkError,\n notFoundError,\n usageError,\n} from './errors';\n\n/**\n * The CLI's client for a Source's event routing (plan 241.4): which team each\n * value of an integration's route key (Zendesk's organization) belongs to,\n * the Source's webhook endpoint, and the events no team is mapped to yet.\n *\n * GET /api/partner/routes?source=<slug>[&product=<slug>]\n * PUT /api/partner/routes/<product>?source=<slug> { value, team, label? }\n * DELETE /api/partner/routes/<product>?source=<slug>&value=<value>\n * GET /api/partner/routes/<product>/options?source=<slug>\n * POST /api/partner/routes/<product>/webhook?source=<slug>[&webhook=<id>]\n * GET /api/partner/routes/unrouted?source=<slug>[&product=<slug>]\n * POST /api/partner/routes/unrouted/<id>/replay?source=<slug>\n *\n * → 401 / 3xx refresh once and retry\n * → 403 { code: \"SOURCE_ADMIN_REQUIRED\" | \"MFA_REQUIRED\" | \"FORBIDDEN\" }\n * → 404 { code: \"PRODUCT_NOT_ENTITLED\" | \"TEAM_NOT_FOUND\" | \"ROUTE_NOT_FOUND\"\n * | \"EVENT_NOT_FOUND\" | \"SOURCE_NOT_FOUND\" }\n * → 409 { code: \"NO_ROUTING\" | \"NO_WEBHOOK\" }\n *\n * The contract's shapes live here rather than in `@ekanos/integration-schema`,\n * so the routing commands ship with the CLI alone.\n */\n\nconst SourceRefSchema = z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n});\n\nconst RouteSchema = z.object({\n product: z.string(),\n key: z.string(),\n value: z.string(),\n label: z.string().nullable(),\n team: z.object({\n id: z.string(),\n slug: z.string().nullable(),\n name: z.string(),\n }),\n updatedAt: z.string(),\n});\nexport type SourceEventRoute = z.infer<typeof RouteSchema>;\n\nexport const RouteListSchema = z.object({\n source: SourceRefSchema,\n routes: z.array(RouteSchema),\n});\n\nexport const RouteSetSchema = z.object({\n route: RouteSchema,\n replaced: z.boolean(),\n});\n\nexport const RouteRemoveSchema = z.object({ removed: z.number() });\n\nexport const RouteOptionsSchema = z.object({\n key: z.string(),\n options: z\n .array(z.object({ value: z.string(), label: z.string() }))\n .nullable(),\n});\n\nexport const RouteWebhookSchema = z.object({\n webhookId: z.string(),\n url: z.string(),\n signature: z\n .object({ header: z.string(), secretName: z.string() })\n .nullable(),\n});\n\nconst UnroutedEventSchema = z.object({\n id: z.string(),\n product: z.string(),\n eventId: z.string(),\n routeKey: z.string().nullable(),\n routeValue: z.string().nullable(),\n createdAt: z.string(),\n});\nexport type UnroutedEvent = z.infer<typeof UnroutedEventSchema>;\n\nexport const UnroutedListSchema = z.object({\n events: z.array(UnroutedEventSchema),\n});\n\nexport const ReplaySchema = z.object({\n status: z.enum(['routed', 'already-routed', 'unrouted']),\n});\n\nexport type RoutesApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nconst BASE = '/api/partner/routes';\n\nexport async function routesCall<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: {\n method: 'GET' | 'PUT' | 'POST' | 'DELETE';\n path?: string;\n query: Record<string, string | undefined>;\n body?: unknown;\n },\n): Promise<RoutesApiResult<T>> {\n const query = Object.fromEntries(\n Object.entries(init.query).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n const url = `${host}${BASE}${init.path ?? ''}?${new URLSearchParams(query)}`;\n const response = await request(url, {\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 (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\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 if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n code === 'SOURCE_ADMIN_REQUIRED'\n ? \"Only a Source admin manages the Source's routing. Ask one to run it.\"\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : code === 'ROUTE_NOT_FOUND'\n ? '\"ekanos routes list\" shows the mapped values.'\n : code === 'EVENT_NOT_FOUND'\n ? '\"ekanos routes unrouted\" lists the events waiting for a mapping.'\n : code === 'PRODUCT_NOT_ENTITLED'\n ? 'Run \"ekanos catalog\" to see what the Source has.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409) {\n throw usageError(\n message ?? 'The integration does not route events.',\n code === 'NO_WEBHOOK'\n ? 'Name the webhook: \"ekanos routes webhook <product> --webhook <id>\".'\n : 'Only an integration that declares a Source connection and a route key routes events.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos routes --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${BASE} 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 routes route.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}${BASE} answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the routes contract.',\n );\n }\n return { status: 'ok', data: 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"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type AgentApprovalDecideRequest, type AgentApprovalDecided, type AgentApprovalList, type AgentRunDetail, type AgentRunList, type AgentRunStartRequest, type AgentRunStarted } from '@ekanos/integration-schema';
|
|
2
|
+
/**
|
|
3
|
+
* The CLI's client for a team's agent runs and approvals — `ekanos runs`,
|
|
4
|
+
* `ekanos agents test` and `ekanos approvals`.
|
|
5
|
+
*
|
|
6
|
+
* GET /api/partner/teams/<team>/runs?source=<slug>[&agent=<key>] → AgentRunList
|
|
7
|
+
* POST /api/partner/teams/<team>/runs?source=<slug> → AgentRunStarted
|
|
8
|
+
* GET /api/partner/teams/<team>/runs/<id>?source=<slug>[&after=] → AgentRunDetail
|
|
9
|
+
* GET /api/partner/teams/<team>/approvals?source=<slug> → AgentApprovalList
|
|
10
|
+
* POST /api/partner/teams/<team>/approvals/<id>?source=<slug> → AgentApprovalDecided
|
|
11
|
+
*/
|
|
12
|
+
export type RunsApiResult<T> = {
|
|
13
|
+
status: 'ok';
|
|
14
|
+
data: T;
|
|
15
|
+
} | {
|
|
16
|
+
status: 'unauthorized';
|
|
17
|
+
};
|
|
18
|
+
interface TeamTarget {
|
|
19
|
+
source: string;
|
|
20
|
+
team: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function listRuns(host: string, accessToken: string, target: TeamTarget & {
|
|
23
|
+
agent?: string;
|
|
24
|
+
}): Promise<RunsApiResult<AgentRunList>>;
|
|
25
|
+
export declare function startRun(host: string, accessToken: string, target: TeamTarget, body: AgentRunStartRequest): Promise<RunsApiResult<AgentRunStarted>>;
|
|
26
|
+
export declare function showRun(host: string, accessToken: string, target: TeamTarget & {
|
|
27
|
+
runId: string;
|
|
28
|
+
after?: number;
|
|
29
|
+
}): Promise<RunsApiResult<AgentRunDetail>>;
|
|
30
|
+
export declare function listApprovals(host: string, accessToken: string, target: TeamTarget): Promise<RunsApiResult<AgentApprovalList>>;
|
|
31
|
+
export declare function decideApproval(host: string, accessToken: string, target: TeamTarget & {
|
|
32
|
+
approvalId: string;
|
|
33
|
+
}, body: AgentApprovalDecideRequest): Promise<RunsApiResult<AgentApprovalDecided>>;
|
|
34
|
+
export {};
|
package/dist/runs-api.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { AgentApprovalDecidedSchema, AgentApprovalListSchema, AgentRunDetailSchema, AgentRunListSchema, AgentRunStartedSchema, } from '@ekanos/integration-schema';
|
|
2
|
+
import { request } from './auth/ekanos-api.js';
|
|
3
|
+
import { authRequiredError, forbiddenError, invalidStateError, networkError, notFoundError, usageError, } from './errors.js';
|
|
4
|
+
export function listRuns(host, accessToken, target) {
|
|
5
|
+
return call(host, accessToken, AgentRunListSchema, {
|
|
6
|
+
method: 'GET',
|
|
7
|
+
path: `${teamPath(target)}/runs`,
|
|
8
|
+
query: Object.assign({ source: target.source }, (target.agent ? { agent: target.agent } : {})),
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function startRun(host, accessToken, target, body) {
|
|
12
|
+
return call(host, accessToken, AgentRunStartedSchema, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
path: `${teamPath(target)}/runs`,
|
|
15
|
+
query: { source: target.source },
|
|
16
|
+
body,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function showRun(host, accessToken, target) {
|
|
20
|
+
return call(host, accessToken, AgentRunDetailSchema, {
|
|
21
|
+
method: 'GET',
|
|
22
|
+
path: `${teamPath(target)}/runs/${encodeURIComponent(target.runId)}`,
|
|
23
|
+
query: Object.assign({ source: target.source }, (target.after ? { after: String(target.after) } : {})),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function listApprovals(host, accessToken, target) {
|
|
27
|
+
return call(host, accessToken, AgentApprovalListSchema, {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
path: `${teamPath(target)}/approvals`,
|
|
30
|
+
query: { source: target.source },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function decideApproval(host, accessToken, target, body) {
|
|
34
|
+
return call(host, accessToken, AgentApprovalDecidedSchema, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
path: `${teamPath(target)}/approvals/${encodeURIComponent(target.approvalId)}`,
|
|
37
|
+
query: { source: target.source },
|
|
38
|
+
body,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function teamPath(target) {
|
|
42
|
+
return `/api/partner/teams/${encodeURIComponent(target.team)}`;
|
|
43
|
+
}
|
|
44
|
+
async function call(host, accessToken, schema, init) {
|
|
45
|
+
const response = await request(`${host}${init.path}?${new URLSearchParams(init.query)}`, {
|
|
46
|
+
method: init.method,
|
|
47
|
+
headers: Object.assign({ accept: 'application/json', authorization: `Bearer ${accessToken}` }, (init.body === undefined
|
|
48
|
+
? {}
|
|
49
|
+
: { 'content-type': 'application/json' })),
|
|
50
|
+
body: init.body === undefined ? undefined : JSON.stringify(init.body),
|
|
51
|
+
});
|
|
52
|
+
if (response.status === 401 ||
|
|
53
|
+
(response.status >= 300 && response.status < 400)) {
|
|
54
|
+
return { status: 'unauthorized' };
|
|
55
|
+
}
|
|
56
|
+
const body = await readOptionalJson(response);
|
|
57
|
+
const code = typeof body.code === 'string' ? body.code : null;
|
|
58
|
+
const message = typeof body.error === 'string' && body.error ? body.error : null;
|
|
59
|
+
if (response.status === 403 && code === 'MFA_REQUIRED') {
|
|
60
|
+
throw authRequiredError(message !== null && message !== void 0 ? message : `${host} requires multi-factor authentication.`, 'Complete MFA in the browser, then run "ekanos login" again.');
|
|
61
|
+
}
|
|
62
|
+
if (response.status === 403) {
|
|
63
|
+
throw forbiddenError(message !== null && message !== void 0 ? message : 'Not permitted on this Source.', code === 'SOURCE_ADMIN_REQUIRED'
|
|
64
|
+
? "Only a Source admin runs its teams' agents and decides their approvals."
|
|
65
|
+
: 'Run "ekanos sources" to list the sources you hold a seat on.');
|
|
66
|
+
}
|
|
67
|
+
if (response.status === 404) {
|
|
68
|
+
throw notFoundError(message !== null && message !== void 0 ? message : 'Not found.', code === 'TEAM_NOT_FOUND'
|
|
69
|
+
? 'Run "ekanos teams list" to see the teams on this Source.'
|
|
70
|
+
: code === 'AGENT_NOT_FOUND' || code === 'TEMPLATE_NOT_FOUND'
|
|
71
|
+
? 'Run "ekanos agents list" for the Source\'s agents; a team runs one once it is turned on there.'
|
|
72
|
+
: code === 'RUN_NOT_FOUND'
|
|
73
|
+
? 'Run "ekanos runs list --team <slug>" for the team\'s runs.'
|
|
74
|
+
: code === 'NOT_FOUND'
|
|
75
|
+
? 'Run "ekanos approvals list --team <slug>" for what is waiting.'
|
|
76
|
+
: 'Run "ekanos sources" to list the sources you hold a seat on.');
|
|
77
|
+
}
|
|
78
|
+
if (response.status === 409 || response.status === 502) {
|
|
79
|
+
throw invalidStateError(message !== null && message !== void 0 ? message : 'The host refused the request in its current state.', code === 'ALREADY_DECIDED'
|
|
80
|
+
? 'Run "ekanos approvals list --team <slug>" for what is still waiting.'
|
|
81
|
+
: code === 'RESUME_FAILED'
|
|
82
|
+
? 'The decision is saved; the run picks it up shortly. Check with "ekanos runs show <run-id>".'
|
|
83
|
+
: 'Check the agent in the app: it may be paused, need a connection, or Agents may be off for this Source.');
|
|
84
|
+
}
|
|
85
|
+
if (response.status === 400) {
|
|
86
|
+
throw usageError(message !== null && message !== void 0 ? message : 'The host refused the request.', code === 'NOT_EDITABLE'
|
|
87
|
+
? 'Only a reply draft can be edited; approve or reject it instead.'
|
|
88
|
+
: 'See "ekanos runs --help" / "ekanos approvals --help".');
|
|
89
|
+
}
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw networkError(`${host}${init.path} responded ${response.status}.`, response.status >= 500
|
|
92
|
+
? 'The Ekanos deployment returned a server error. Retry shortly.'
|
|
93
|
+
: 'Confirm the host runs a build that includes the runs routes.');
|
|
94
|
+
}
|
|
95
|
+
const parsed = schema.safeParse(body.data);
|
|
96
|
+
if (!parsed.success) {
|
|
97
|
+
throw networkError(`${host}${init.path} answered in a shape this CLI does not read.`, 'Run "ekanos upgrade" — the CLI and the host disagree on the runs contract.');
|
|
98
|
+
}
|
|
99
|
+
return { status: 'ok', data: parsed.data };
|
|
100
|
+
}
|
|
101
|
+
async function readOptionalJson(response) {
|
|
102
|
+
try {
|
|
103
|
+
const parsed = await response.json();
|
|
104
|
+
return typeof parsed === 'object' && parsed !== null
|
|
105
|
+
? parsed
|
|
106
|
+
: {};
|
|
107
|
+
}
|
|
108
|
+
catch (_a) {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=runs-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runs-api.js","sourceRoot":"","sources":["../src/runs-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AgentApprovalDecideRequest,\n type AgentApprovalDecided,\n AgentApprovalDecidedSchema,\n type AgentApprovalList,\n AgentApprovalListSchema,\n type AgentRunDetail,\n AgentRunDetailSchema,\n type AgentRunList,\n AgentRunListSchema,\n type AgentRunStartRequest,\n type AgentRunStarted,\n AgentRunStartedSchema,\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} from './errors';\n\n/**\n * The CLI's client for a team's agent runs and approvals — `ekanos runs`,\n * `ekanos agents test` and `ekanos approvals`.\n *\n * GET /api/partner/teams/<team>/runs?source=<slug>[&agent=<key>] → AgentRunList\n * POST /api/partner/teams/<team>/runs?source=<slug> → AgentRunStarted\n * GET /api/partner/teams/<team>/runs/<id>?source=<slug>[&after=] → AgentRunDetail\n * GET /api/partner/teams/<team>/approvals?source=<slug> → AgentApprovalList\n * POST /api/partner/teams/<team>/approvals/<id>?source=<slug> → AgentApprovalDecided\n */\n\nexport type RunsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\ninterface TeamTarget {\n source: string;\n team: string;\n}\n\nexport function listRuns(\n host: string,\n accessToken: string,\n target: TeamTarget & { agent?: string },\n): Promise<RunsApiResult<AgentRunList>> {\n return call(host, accessToken, AgentRunListSchema, {\n method: 'GET',\n path: `${teamPath(target)}/runs`,\n query: {\n source: target.source,\n ...(target.agent ? { agent: target.agent } : {}),\n },\n });\n}\n\nexport function startRun(\n host: string,\n accessToken: string,\n target: TeamTarget,\n body: AgentRunStartRequest,\n): Promise<RunsApiResult<AgentRunStarted>> {\n return call(host, accessToken, AgentRunStartedSchema, {\n method: 'POST',\n path: `${teamPath(target)}/runs`,\n query: { source: target.source },\n body,\n });\n}\n\nexport function showRun(\n host: string,\n accessToken: string,\n target: TeamTarget & { runId: string; after?: number },\n): Promise<RunsApiResult<AgentRunDetail>> {\n return call(host, accessToken, AgentRunDetailSchema, {\n method: 'GET',\n path: `${teamPath(target)}/runs/${encodeURIComponent(target.runId)}`,\n query: {\n source: target.source,\n ...(target.after ? { after: String(target.after) } : {}),\n },\n });\n}\n\nexport function listApprovals(\n host: string,\n accessToken: string,\n target: TeamTarget,\n): Promise<RunsApiResult<AgentApprovalList>> {\n return call(host, accessToken, AgentApprovalListSchema, {\n method: 'GET',\n path: `${teamPath(target)}/approvals`,\n query: { source: target.source },\n });\n}\n\nexport function decideApproval(\n host: string,\n accessToken: string,\n target: TeamTarget & { approvalId: string },\n body: AgentApprovalDecideRequest,\n): Promise<RunsApiResult<AgentApprovalDecided>> {\n return call(host, accessToken, AgentApprovalDecidedSchema, {\n method: 'POST',\n path: `${teamPath(target)}/approvals/${encodeURIComponent(target.approvalId)}`,\n query: { source: target.source },\n body,\n });\n}\n\nfunction teamPath(target: TeamTarget): string {\n return `/api/partner/teams/${encodeURIComponent(target.team)}`;\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 query: Record<string, string>;\n body?: unknown;\n },\n): Promise<RunsApiResult<T>> {\n const response = await request(\n `${host}${init.path}?${new URLSearchParams(init.query)}`,\n {\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\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\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 if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n code === 'SOURCE_ADMIN_REQUIRED'\n ? \"Only a Source admin runs its teams' agents and decides their approvals.\"\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : code === 'AGENT_NOT_FOUND' || code === 'TEMPLATE_NOT_FOUND'\n ? 'Run \"ekanos agents list\" for the Source\\'s agents; a team runs one once it is turned on there.'\n : code === 'RUN_NOT_FOUND'\n ? 'Run \"ekanos runs list --team <slug>\" for the team\\'s runs.'\n : code === 'NOT_FOUND'\n ? 'Run \"ekanos approvals list --team <slug>\" for what is waiting.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409 || response.status === 502) {\n throw invalidStateError(\n message ?? 'The host refused the request in its current state.',\n code === 'ALREADY_DECIDED'\n ? 'Run \"ekanos approvals list --team <slug>\" for what is still waiting.'\n : code === 'RESUME_FAILED'\n ? 'The decision is saved; the run picks it up shortly. Check with \"ekanos runs show <run-id>\".'\n : 'Check the agent in the app: it may be paused, need a connection, or Agents may be off for this Source.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n code === 'NOT_EDITABLE'\n ? 'Only a reply draft can be edited; approve or reject it instead.'\n : 'See \"ekanos runs --help\" / \"ekanos approvals --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path} 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 runs routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}${init.path} answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the runs contract.',\n );\n }\n return { status: 'ok', data: 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"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type SourceSkillList, type SourceSkillPushRequest, type SourceSkillPushResult, type SourceSkillShow } from '@ekanos/integration-schema';
|
|
2
|
+
/**
|
|
3
|
+
* The CLI's client for a Source's skills.
|
|
4
|
+
*
|
|
5
|
+
* ---------------------------------------------------------------------------
|
|
6
|
+
* WIRE CONTRACT — the `SourceSkill*` schemas in `@ekanos/integration-schema`,
|
|
7
|
+
* which the host's `/api/partner/skills` routes build and this parses.
|
|
8
|
+
*
|
|
9
|
+
* GET /api/partner/skills?source=<s>[&team=<t>] → { ok, data: SourceSkillList }
|
|
10
|
+
* GET /api/partner/skills/<slug>?source=<s>[&team=<t>] → { ok, data: SourceSkillShow }
|
|
11
|
+
* POST /api/partner/skills SourceSkillPushRequest → { ok, data: SourceSkillPushResult }
|
|
12
|
+
*
|
|
13
|
+
* → 401 / 3xx refresh once and retry
|
|
14
|
+
* → 400 { code: "INVALID_SKILL", issues: { path, message }[] }
|
|
15
|
+
* → 403 { code: "MFA_REQUIRED" | "FORBIDDEN" | "SOURCE_ADMIN_REQUIRED" }
|
|
16
|
+
* → 404 { code: "SOURCE_NOT_FOUND" | "TEAM_NOT_FOUND" | "SKILL_NOT_FOUND" }
|
|
17
|
+
* → 409 { code: "SKILL_NOT_PROMPT" }
|
|
18
|
+
* ---------------------------------------------------------------------------
|
|
19
|
+
*/
|
|
20
|
+
export type SkillsApiResult<T> = {
|
|
21
|
+
status: 'ok';
|
|
22
|
+
data: T;
|
|
23
|
+
} | {
|
|
24
|
+
status: 'unauthorized';
|
|
25
|
+
};
|
|
26
|
+
export declare function listSkills(host: string, accessToken: string, source: string, team?: string): Promise<SkillsApiResult<SourceSkillList>>;
|
|
27
|
+
export declare function showSkill(host: string, accessToken: string, source: string, slug: string, team?: string): Promise<SkillsApiResult<SourceSkillShow>>;
|
|
28
|
+
export declare function pushSkill(host: string, accessToken: string, body: SourceSkillPushRequest): Promise<SkillsApiResult<SourceSkillPushResult>>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { SourceSkillListSchema, SourceSkillPushResultSchema, SourceSkillShowSchema, } 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/skills';
|
|
5
|
+
function query(source, team) {
|
|
6
|
+
return new URLSearchParams(team ? { source, team } : { source });
|
|
7
|
+
}
|
|
8
|
+
export function listSkills(host, accessToken, source, team) {
|
|
9
|
+
return call(host, accessToken, SourceSkillListSchema, {
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: `${ROUTE}?${query(source, team)}`,
|
|
12
|
+
subject: team !== null && team !== void 0 ? team : source,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function showSkill(host, accessToken, source, slug, team) {
|
|
16
|
+
return call(host, accessToken, SourceSkillShowSchema, {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
path: `${ROUTE}/${encodeURIComponent(slug)}?${query(source, team)}`,
|
|
19
|
+
subject: slug,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function pushSkill(host, accessToken, body) {
|
|
23
|
+
return call(host, accessToken, SourceSkillPushResultSchema, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
path: ROUTE,
|
|
26
|
+
body,
|
|
27
|
+
subject: body.slug,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function call(host, accessToken, schema, init) {
|
|
31
|
+
const response = await request(`${host}${init.path}`, {
|
|
32
|
+
method: init.method,
|
|
33
|
+
headers: Object.assign({ accept: 'application/json', authorization: `Bearer ${accessToken}` }, (init.body === undefined
|
|
34
|
+
? {}
|
|
35
|
+
: { 'content-type': 'application/json' })),
|
|
36
|
+
body: init.body === undefined ? undefined : JSON.stringify(init.body),
|
|
37
|
+
});
|
|
38
|
+
if (response.status === 401 ||
|
|
39
|
+
(response.status >= 300 && response.status < 400)) {
|
|
40
|
+
return { status: 'unauthorized' };
|
|
41
|
+
}
|
|
42
|
+
const body = await readOptionalJson(response);
|
|
43
|
+
const code = str(body.code);
|
|
44
|
+
const message = str(body.error);
|
|
45
|
+
if (response.status === 400 && code === 'INVALID_SKILL') {
|
|
46
|
+
const issues = Array.isArray(body.issues)
|
|
47
|
+
? body.issues.flatMap((issue) => {
|
|
48
|
+
if (typeof issue !== 'object' || issue === null)
|
|
49
|
+
return [];
|
|
50
|
+
const { path, message: text } = issue;
|
|
51
|
+
return [` ${String(path)}: ${String(text)}`];
|
|
52
|
+
})
|
|
53
|
+
: [];
|
|
54
|
+
throw validationError([message !== null && message !== void 0 ? message : 'The host refused the skill.', ...issues].join('\n'), 'Fix the skill folder and push again.');
|
|
55
|
+
}
|
|
56
|
+
if (response.status === 400 || response.status === 413) {
|
|
57
|
+
throw usageError(message !== null && message !== void 0 ? message : (response.status === 413
|
|
58
|
+
? 'The skill folder is too large to send.'
|
|
59
|
+
: 'The host refused the request.'), 'See "ekanos skills --help" for each subcommand\'s arguments.');
|
|
60
|
+
}
|
|
61
|
+
if (response.status === 403 && code === 'MFA_REQUIRED') {
|
|
62
|
+
throw authRequiredError(message !== null && message !== void 0 ? message : `${host} requires multi-factor authentication.`, 'Complete MFA in the browser, then run "ekanos login" again.');
|
|
63
|
+
}
|
|
64
|
+
if (response.status === 403) {
|
|
65
|
+
throw forbiddenError(message !== null && message !== void 0 ? message : 'Not permitted on this Source.', code === 'SOURCE_ADMIN_REQUIRED'
|
|
66
|
+
? 'Only a Source admin pushes skills or reads team skills. Ask one to run it.'
|
|
67
|
+
: 'Run "ekanos sources" to list the sources you hold a seat on.');
|
|
68
|
+
}
|
|
69
|
+
if (response.status === 404) {
|
|
70
|
+
throw notFoundError(message !== null && message !== void 0 ? message : `"${init.subject}" was not found.`, code === 'SOURCE_NOT_FOUND'
|
|
71
|
+
? 'Run "ekanos sources" to list the sources you hold a seat on.'
|
|
72
|
+
: code === 'TEAM_NOT_FOUND'
|
|
73
|
+
? 'Run "ekanos teams list" to see the Source\'s teams.'
|
|
74
|
+
: 'Run "ekanos skills list" to see the skills.');
|
|
75
|
+
}
|
|
76
|
+
if (response.status === 409) {
|
|
77
|
+
throw invalidStateError(message !== null && message !== void 0 ? message : `"${init.subject}" cannot be pushed.`, 'Rename the skill folder (its slug) and push again.');
|
|
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 skills 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 skills contract.');
|
|
87
|
+
}
|
|
88
|
+
return { status: 'ok', data: parsed.data };
|
|
89
|
+
}
|
|
90
|
+
async function readOptionalJson(response) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = await response.json();
|
|
93
|
+
return typeof parsed === 'object' && parsed !== null
|
|
94
|
+
? parsed
|
|
95
|
+
: {};
|
|
96
|
+
}
|
|
97
|
+
catch (_a) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function str(value) {
|
|
102
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=skills-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-api.js","sourceRoot":"","sources":["../src/skills-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type SourceSkillList,\n SourceSkillListSchema,\n type SourceSkillPushRequest,\n type SourceSkillPushResult,\n SourceSkillPushResultSchema,\n type SourceSkillShow,\n SourceSkillShowSchema,\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 a Source's skills.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the `SourceSkill*` schemas in `@ekanos/integration-schema`,\n * which the host's `/api/partner/skills` routes build and this parses.\n *\n * GET /api/partner/skills?source=<s>[&team=<t>] → { ok, data: SourceSkillList }\n * GET /api/partner/skills/<slug>?source=<s>[&team=<t>] → { ok, data: SourceSkillShow }\n * POST /api/partner/skills SourceSkillPushRequest → { ok, data: SourceSkillPushResult }\n *\n * → 401 / 3xx refresh once and retry\n * → 400 { code: \"INVALID_SKILL\", issues: { path, message }[] }\n * → 403 { code: \"MFA_REQUIRED\" | \"FORBIDDEN\" | \"SOURCE_ADMIN_REQUIRED\" }\n * → 404 { code: \"SOURCE_NOT_FOUND\" | \"TEAM_NOT_FOUND\" | \"SKILL_NOT_FOUND\" }\n * → 409 { code: \"SKILL_NOT_PROMPT\" }\n * ---------------------------------------------------------------------------\n */\n\nexport type SkillsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nconst ROUTE = '/api/partner/skills';\n\nfunction query(source: string, team: string | undefined) {\n return new URLSearchParams(team ? { source, team } : { source });\n}\n\nexport function listSkills(\n host: string,\n accessToken: string,\n source: string,\n team?: string,\n): Promise<SkillsApiResult<SourceSkillList>> {\n return call(host, accessToken, SourceSkillListSchema, {\n method: 'GET',\n path: `${ROUTE}?${query(source, team)}`,\n subject: team ?? source,\n });\n}\n\nexport function showSkill(\n host: string,\n accessToken: string,\n source: string,\n slug: string,\n team?: string,\n): Promise<SkillsApiResult<SourceSkillShow>> {\n return call(host, accessToken, SourceSkillShowSchema, {\n method: 'GET',\n path: `${ROUTE}/${encodeURIComponent(slug)}?${query(source, team)}`,\n subject: slug,\n });\n}\n\nexport function pushSkill(\n host: string,\n accessToken: string,\n body: SourceSkillPushRequest,\n): Promise<SkillsApiResult<SourceSkillPushResult>> {\n return call(host, accessToken, SourceSkillPushResultSchema, {\n method: 'POST',\n path: ROUTE,\n body,\n subject: body.slug,\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 subject: string;\n },\n): Promise<SkillsApiResult<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 (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\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_SKILL') {\n const issues = Array.isArray(body.issues)\n ? body.issues.flatMap((issue: unknown) => {\n if (typeof issue !== 'object' || issue === null) return [];\n const { path, message: text } = issue as Record<string, unknown>;\n return [` ${String(path)}: ${String(text)}`];\n })\n : [];\n throw validationError(\n [message ?? 'The host refused the skill.', ...issues].join('\\n'),\n 'Fix the skill folder and push again.',\n );\n }\n\n if (response.status === 400 || response.status === 413) {\n throw usageError(\n message ??\n (response.status === 413\n ? 'The skill folder is too large to send.'\n : 'The host refused the request.'),\n 'See \"ekanos skills --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 pushes skills or reads team skills. Ask one to run it.'\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 : code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the Source\\'s teams.'\n : 'Run \"ekanos skills list\" to see the skills.',\n );\n }\n\n if (response.status === 409) {\n throw invalidStateError(\n message ?? `\"${init.subject}\" cannot be pushed.`,\n 'Rename the skill folder (its slug) and push again.',\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 skills 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 skills contract.',\n );\n }\n\n return { status: 'ok', data: 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 str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n"]}
|
package/dist/teams-api.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type TeamAppSetResult, type TeamIntegrationStatus, type TeamList, type TeamWebhookMintResult } from '@ekanos/integration-schema';
|
|
1
|
+
import { type TeamAppSetResult, type TeamCreateRequest, type TeamCreateResult, type TeamMemberChangeResult, type TeamMemberInviteResult, type TeamMemberList, type TeamRoles, type TeamIntegrationStatus, type TeamList, type TeamWebhookMintResult } from '@ekanos/integration-schema';
|
|
2
|
+
import type { z } from 'zod';
|
|
2
3
|
/**
|
|
3
4
|
* The CLI's client for a Source's teams and their integration setup — the
|
|
4
5
|
* team-level counterpart of `apps-api.ts`.
|
|
@@ -8,13 +9,21 @@ import { type TeamAppSetResult, type TeamIntegrationStatus, type TeamList, type
|
|
|
8
9
|
* PUT …/integrations/<product>/app?source=<slug> { connectionId?, fields }
|
|
9
10
|
* → TeamAppSetResult
|
|
10
11
|
* POST …/integrations/<product>/webhooks/<id>?source=<slug> → TeamWebhookMintResult
|
|
12
|
+
* POST /api/partner/teams?source=<slug> { name, slug?, ownerEmail? } → TeamCreateResult
|
|
13
|
+
* GET /api/partner/teams/<team>/members?source=<slug> → TeamMemberList
|
|
14
|
+
* POST …/<team>/members?source=<slug> { email, role } → TeamMemberInviteResult
|
|
15
|
+
* PATCH …/<team>/members/<email>?source=<slug> { role } → TeamMemberChangeResult
|
|
16
|
+
* DELETE …/<team>/members/<email>?source=<slug> → TeamMemberChangeResult
|
|
17
|
+
* GET /api/partner/teams/<team>/roles?source=<slug> → TeamRoles
|
|
11
18
|
*
|
|
12
19
|
* → 401 / 3xx refresh once and retry
|
|
13
20
|
* → 403 { code: "SOURCE_ADMIN_REQUIRED" | "MFA_REQUIRED" | "FORBIDDEN" }
|
|
14
21
|
* → 404 { code: "TEAM_NOT_FOUND" | "PRODUCT_NOT_ENTITLED" | "WEBHOOK_NOT_FOUND"
|
|
15
22
|
* | "CONNECTION_NOT_FOUND" | "SOURCE_NOT_FOUND" }
|
|
16
|
-
*
|
|
17
|
-
* →
|
|
23
|
+
* | "MEMBER_NOT_FOUND" }
|
|
24
|
+
* → 409 { code: "NO_TEAM_APP" | "SLUG_TAKEN" | "TEAM_ACCOUNTS_DISABLED"
|
|
25
|
+
* | "ALREADY_MEMBER" | "ALREADY_INVITED" | "PRIMARY_OWNER" }
|
|
26
|
+
* → 400 { code: "INVALID_FIELDS" | "INVALID_REQUEST" | "UNKNOWN_ROLE" }
|
|
18
27
|
*/
|
|
19
28
|
export type TeamsApiResult<T> = {
|
|
20
29
|
status: 'ok';
|
|
@@ -41,3 +50,36 @@ export declare function mintTeamWebhook(host: string, accessToken: string, targe
|
|
|
41
50
|
team: string;
|
|
42
51
|
product: string;
|
|
43
52
|
}, webhookId: string): Promise<TeamsApiResult<TeamWebhookMintResult>>;
|
|
53
|
+
export declare function createTeam(host: string, accessToken: string, source: string, body: TeamCreateRequest): Promise<TeamsApiResult<TeamCreateResult>>;
|
|
54
|
+
export declare function listTeamMembers(host: string, accessToken: string, target: {
|
|
55
|
+
source: string;
|
|
56
|
+
team: string;
|
|
57
|
+
}): Promise<TeamsApiResult<TeamMemberList>>;
|
|
58
|
+
export declare function inviteTeamMember(host: string, accessToken: string, target: {
|
|
59
|
+
source: string;
|
|
60
|
+
team: string;
|
|
61
|
+
}, body: {
|
|
62
|
+
email: string;
|
|
63
|
+
role: string;
|
|
64
|
+
}): Promise<TeamsApiResult<TeamMemberInviteResult>>;
|
|
65
|
+
export declare function setTeamMemberRole(host: string, accessToken: string, target: {
|
|
66
|
+
source: string;
|
|
67
|
+
team: string;
|
|
68
|
+
email: string;
|
|
69
|
+
}, role: string): Promise<TeamsApiResult<TeamMemberChangeResult>>;
|
|
70
|
+
export declare function removeTeamMember(host: string, accessToken: string, target: {
|
|
71
|
+
source: string;
|
|
72
|
+
team: string;
|
|
73
|
+
email: string;
|
|
74
|
+
}): Promise<TeamsApiResult<TeamMemberChangeResult>>;
|
|
75
|
+
export declare function listTeamRoles(host: string, accessToken: string, target: {
|
|
76
|
+
source: string;
|
|
77
|
+
team: string;
|
|
78
|
+
}): Promise<TeamsApiResult<TeamRoles>>;
|
|
79
|
+
/** One partner teams call; shared by the team-agent clients (`team-agents-api.ts`). */
|
|
80
|
+
export declare function call<T>(host: string, accessToken: string, schema: z.ZodType<T>, init: {
|
|
81
|
+
method: 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE';
|
|
82
|
+
path: string;
|
|
83
|
+
source: string;
|
|
84
|
+
body?: unknown;
|
|
85
|
+
}): Promise<TeamsApiResult<T>>;
|
package/dist/teams-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TeamAppSetResultSchema, TeamIntegrationStatusSchema, TeamListSchema, TeamWebhookMintResultSchema, } from '@ekanos/integration-schema';
|
|
1
|
+
import { TeamAppSetResultSchema, TeamCreateResultSchema, TeamMemberChangeResultSchema, TeamMemberInviteResultSchema, TeamMemberListSchema, TeamRolesSchema, TeamIntegrationStatusSchema, TeamListSchema, TeamWebhookMintResultSchema, } from '@ekanos/integration-schema';
|
|
2
2
|
import { request } from './auth/ekanos-api.js';
|
|
3
3
|
import { authRequiredError, forbiddenError, invalidStateError, networkError, notFoundError, usageError, validationError, } from './errors.js';
|
|
4
4
|
export function listTeams(host, accessToken, source) {
|
|
@@ -30,10 +30,59 @@ export function mintTeamWebhook(host, accessToken, target, webhookId) {
|
|
|
30
30
|
source: target.source,
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
+
export function createTeam(host, accessToken, source, body) {
|
|
34
|
+
return call(host, accessToken, TeamCreateResultSchema, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
path: '/api/partner/teams',
|
|
37
|
+
source,
|
|
38
|
+
body,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function listTeamMembers(host, accessToken, target) {
|
|
42
|
+
return call(host, accessToken, TeamMemberListSchema, {
|
|
43
|
+
method: 'GET',
|
|
44
|
+
path: membersPath(target.team),
|
|
45
|
+
source: target.source,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function inviteTeamMember(host, accessToken, target, body) {
|
|
49
|
+
return call(host, accessToken, TeamMemberInviteResultSchema, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
path: membersPath(target.team),
|
|
52
|
+
source: target.source,
|
|
53
|
+
body,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function setTeamMemberRole(host, accessToken, target, role) {
|
|
57
|
+
return call(host, accessToken, TeamMemberChangeResultSchema, {
|
|
58
|
+
method: 'PATCH',
|
|
59
|
+
path: `${membersPath(target.team)}/${encodeURIComponent(target.email)}`,
|
|
60
|
+
source: target.source,
|
|
61
|
+
body: { role },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function removeTeamMember(host, accessToken, target) {
|
|
65
|
+
return call(host, accessToken, TeamMemberChangeResultSchema, {
|
|
66
|
+
method: 'DELETE',
|
|
67
|
+
path: `${membersPath(target.team)}/${encodeURIComponent(target.email)}`,
|
|
68
|
+
source: target.source,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export function listTeamRoles(host, accessToken, target) {
|
|
72
|
+
return call(host, accessToken, TeamRolesSchema, {
|
|
73
|
+
method: 'GET',
|
|
74
|
+
path: `/api/partner/teams/${encodeURIComponent(target.team)}/roles`,
|
|
75
|
+
source: target.source,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function membersPath(team) {
|
|
79
|
+
return `/api/partner/teams/${encodeURIComponent(team)}/members`;
|
|
80
|
+
}
|
|
33
81
|
function integrationPath(target) {
|
|
34
82
|
return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}`;
|
|
35
83
|
}
|
|
36
|
-
|
|
84
|
+
/** One partner teams call; shared by the team-agent clients (`team-agents-api.ts`). */
|
|
85
|
+
export async function call(host, accessToken, schema, init) {
|
|
37
86
|
const response = await request(`${host}${init.path}?${new URLSearchParams({ source: init.source })}`, {
|
|
38
87
|
method: init.method,
|
|
39
88
|
headers: Object.assign({ accept: 'application/json', authorization: `Bearer ${accessToken}` }, (init.body === undefined
|
|
@@ -63,11 +112,31 @@ async function call(host, accessToken, schema, init) {
|
|
|
63
112
|
? '"ekanos catalog" lists what this Source has; Ekanos enables a product for a Source.'
|
|
64
113
|
: code === 'WEBHOOK_NOT_FOUND' || code === 'CONNECTION_NOT_FOUND'
|
|
65
114
|
? 'Run "ekanos teams show <team> <product>" to see its team apps and webhooks.'
|
|
66
|
-
:
|
|
115
|
+
: code === 'MEMBER_NOT_FOUND'
|
|
116
|
+
? 'Run "ekanos teams members list <team>" to see its members and invitations.'
|
|
117
|
+
: (code === null || code === void 0 ? void 0 : code.startsWith('AGENT_')) || code === 'TEMPLATE_NOT_FOUND'
|
|
118
|
+
? 'Run "ekanos teams agents list <team>" to see the team\'s agents.'
|
|
119
|
+
: /^(TRIGGER|LINK|MCP)_/.test(code !== null && code !== void 0 ? code : '')
|
|
120
|
+
? 'List them first: "ekanos teams triggers|links|mcp list <team> ..."'
|
|
121
|
+
: 'Run "ekanos sources" to list the sources you hold a seat on.');
|
|
67
122
|
}
|
|
68
123
|
if (response.status === 409 && code === 'NO_TEAM_APP') {
|
|
69
124
|
throw invalidStateError(message !== null && message !== void 0 ? message : 'This integration takes no team app.', 'Teams connect through the Source\'s app: "ekanos apps show <product>".');
|
|
70
125
|
}
|
|
126
|
+
if (response.status === 409 || response.status === 422) {
|
|
127
|
+
throw invalidStateError(message !== null && message !== void 0 ? message : 'The host refused the change.', code === 'SLUG_TAKEN'
|
|
128
|
+
? 'Pick another with --slug, or run "ekanos teams list" to see the teams.'
|
|
129
|
+
: code === 'ALREADY_MEMBER' || code === 'ALREADY_INVITED'
|
|
130
|
+
? 'Run "ekanos teams members list <team>"; change a role with "ekanos teams members role <team> <email> <role>".'
|
|
131
|
+
: code === 'PRIMARY_OWNER'
|
|
132
|
+
? "The primary owner's ownership is transferred in the app (team Settings → Members)."
|
|
133
|
+
: code === 'TEAM_ACCOUNTS_DISABLED'
|
|
134
|
+
? 'Ekanos switches team accounts on for a Source; ask a super-admin.'
|
|
135
|
+
: 'See "ekanos teams --help".');
|
|
136
|
+
}
|
|
137
|
+
if (response.status === 400 && code === 'UNKNOWN_ROLE') {
|
|
138
|
+
throw validationError(message !== null && message !== void 0 ? message : 'No such role.', 'Run "ekanos teams roles list <team>" for the roles this Source uses.');
|
|
139
|
+
}
|
|
71
140
|
if (response.status === 400 && code === 'INVALID_FIELDS') {
|
|
72
141
|
throw validationError(message !== null && message !== void 0 ? message : 'The host refused the fields.', 'Run "ekanos teams app set <team> <product>" again.');
|
|
73
142
|
}
|
package/dist/teams-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams-api.js","sourceRoot":"","sources":["../src/teams-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type TeamAppSetResult,\n TeamAppSetResultSchema,\n type TeamIntegrationStatus,\n TeamIntegrationStatusSchema,\n type TeamList,\n TeamListSchema,\n type TeamWebhookMintResult,\n TeamWebhookMintResultSchema,\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 a Source's teams and their integration setup — the\n * team-level counterpart of `apps-api.ts`.\n *\n * GET /api/partner/teams?source=<slug> → TeamList\n * GET /api/partner/teams/<team>/integrations/<product>?source=<slug> → TeamIntegrationStatus\n * PUT …/integrations/<product>/app?source=<slug> { connectionId?, fields }\n * → TeamAppSetResult\n * POST …/integrations/<product>/webhooks/<id>?source=<slug> → TeamWebhookMintResult\n *\n * → 401 / 3xx refresh once and retry\n * → 403 { code: \"SOURCE_ADMIN_REQUIRED\" | \"MFA_REQUIRED\" | \"FORBIDDEN\" }\n * → 404 { code: \"TEAM_NOT_FOUND\" | \"PRODUCT_NOT_ENTITLED\" | \"WEBHOOK_NOT_FOUND\"\n * | \"CONNECTION_NOT_FOUND\" | \"SOURCE_NOT_FOUND\" }\n * → 409 { code: \"NO_TEAM_APP\" }\n * → 400 { code: \"INVALID_FIELDS\" | \"INVALID_REQUEST\" }\n */\n\nexport type TeamsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listTeams(\n host: string,\n accessToken: string,\n source: string,\n): Promise<TeamsApiResult<TeamList>> {\n return call(host, accessToken, TeamListSchema, {\n method: 'GET',\n path: '/api/partner/teams',\n source,\n });\n}\n\nexport function showTeamIntegration(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n): Promise<TeamsApiResult<TeamIntegrationStatus>> {\n return call(host, accessToken, TeamIntegrationStatusSchema, {\n method: 'GET',\n path: integrationPath(target),\n source: target.source,\n });\n}\n\nexport function setTeamApp(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n body: { connectionId?: string; fields: Record<string, string> },\n): Promise<TeamsApiResult<TeamAppSetResult>> {\n return call(host, accessToken, TeamAppSetResultSchema, {\n method: 'PUT',\n path: `${integrationPath(target)}/app`,\n source: target.source,\n body,\n });\n}\n\nexport function mintTeamWebhook(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n webhookId: string,\n): Promise<TeamsApiResult<TeamWebhookMintResult>> {\n return call(host, accessToken, TeamWebhookMintResultSchema, {\n method: 'POST',\n path: `${integrationPath(target)}/webhooks/${encodeURIComponent(webhookId)}`,\n source: target.source,\n });\n}\n\nfunction integrationPath(target: { team: string; product: string }): string {\n return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}`;\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: {\n method: 'GET' | 'PUT' | 'POST';\n path: string;\n source: string;\n body?: unknown;\n },\n): Promise<TeamsApiResult<T>> {\n const response = await request(\n `${host}${init.path}?${new URLSearchParams({ source: init.source })}`,\n {\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\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\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 if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n code === 'SOURCE_ADMIN_REQUIRED'\n ? \"Only a Source admin manages its teams' integrations. Ask one to run it.\"\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : code === 'PRODUCT_NOT_ENTITLED'\n ? '\"ekanos catalog\" lists what this Source has; Ekanos enables a product for a Source.'\n : code === 'WEBHOOK_NOT_FOUND' || code === 'CONNECTION_NOT_FOUND'\n ? 'Run \"ekanos teams show <team> <product>\" to see its team apps and webhooks.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409 && code === 'NO_TEAM_APP') {\n throw invalidStateError(\n message ?? 'This integration takes no team app.',\n 'Teams connect through the Source\\'s app: \"ekanos apps show <product>\".',\n );\n }\n if (response.status === 400 && code === 'INVALID_FIELDS') {\n throw validationError(\n message ?? 'The host refused the fields.',\n 'Run \"ekanos teams app set <team> <product>\" again.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos teams --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path} 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 teams routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/teams answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the teams contract.',\n );\n }\n return { status: 'ok', data: 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"]}
|
|
1
|
+
{"version":3,"file":"teams-api.js","sourceRoot":"","sources":["../src/teams-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type TeamAppSetResult,\n TeamAppSetResultSchema,\n type TeamCreateRequest,\n type TeamCreateResult,\n TeamCreateResultSchema,\n type TeamMemberChangeResult,\n TeamMemberChangeResultSchema,\n type TeamMemberInviteResult,\n TeamMemberInviteResultSchema,\n type TeamMemberList,\n TeamMemberListSchema,\n type TeamRoles,\n TeamRolesSchema,\n type TeamIntegrationStatus,\n TeamIntegrationStatusSchema,\n type TeamList,\n TeamListSchema,\n type TeamWebhookMintResult,\n TeamWebhookMintResultSchema,\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 a Source's teams and their integration setup — the\n * team-level counterpart of `apps-api.ts`.\n *\n * GET /api/partner/teams?source=<slug> → TeamList\n * GET /api/partner/teams/<team>/integrations/<product>?source=<slug> → TeamIntegrationStatus\n * PUT …/integrations/<product>/app?source=<slug> { connectionId?, fields }\n * → TeamAppSetResult\n * POST …/integrations/<product>/webhooks/<id>?source=<slug> → TeamWebhookMintResult\n * POST /api/partner/teams?source=<slug> { name, slug?, ownerEmail? } → TeamCreateResult\n * GET /api/partner/teams/<team>/members?source=<slug> → TeamMemberList\n * POST …/<team>/members?source=<slug> { email, role } → TeamMemberInviteResult\n * PATCH …/<team>/members/<email>?source=<slug> { role } → TeamMemberChangeResult\n * DELETE …/<team>/members/<email>?source=<slug> → TeamMemberChangeResult\n * GET /api/partner/teams/<team>/roles?source=<slug> → TeamRoles\n *\n * → 401 / 3xx refresh once and retry\n * → 403 { code: \"SOURCE_ADMIN_REQUIRED\" | \"MFA_REQUIRED\" | \"FORBIDDEN\" }\n * → 404 { code: \"TEAM_NOT_FOUND\" | \"PRODUCT_NOT_ENTITLED\" | \"WEBHOOK_NOT_FOUND\"\n * | \"CONNECTION_NOT_FOUND\" | \"SOURCE_NOT_FOUND\" }\n * | \"MEMBER_NOT_FOUND\" }\n * → 409 { code: \"NO_TEAM_APP\" | \"SLUG_TAKEN\" | \"TEAM_ACCOUNTS_DISABLED\"\n * | \"ALREADY_MEMBER\" | \"ALREADY_INVITED\" | \"PRIMARY_OWNER\" }\n * → 400 { code: \"INVALID_FIELDS\" | \"INVALID_REQUEST\" | \"UNKNOWN_ROLE\" }\n */\n\nexport type TeamsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listTeams(\n host: string,\n accessToken: string,\n source: string,\n): Promise<TeamsApiResult<TeamList>> {\n return call(host, accessToken, TeamListSchema, {\n method: 'GET',\n path: '/api/partner/teams',\n source,\n });\n}\n\nexport function showTeamIntegration(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n): Promise<TeamsApiResult<TeamIntegrationStatus>> {\n return call(host, accessToken, TeamIntegrationStatusSchema, {\n method: 'GET',\n path: integrationPath(target),\n source: target.source,\n });\n}\n\nexport function setTeamApp(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n body: { connectionId?: string; fields: Record<string, string> },\n): Promise<TeamsApiResult<TeamAppSetResult>> {\n return call(host, accessToken, TeamAppSetResultSchema, {\n method: 'PUT',\n path: `${integrationPath(target)}/app`,\n source: target.source,\n body,\n });\n}\n\nexport function mintTeamWebhook(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n webhookId: string,\n): Promise<TeamsApiResult<TeamWebhookMintResult>> {\n return call(host, accessToken, TeamWebhookMintResultSchema, {\n method: 'POST',\n path: `${integrationPath(target)}/webhooks/${encodeURIComponent(webhookId)}`,\n source: target.source,\n });\n}\n\nexport function createTeam(\n host: string,\n accessToken: string,\n source: string,\n body: TeamCreateRequest,\n): Promise<TeamsApiResult<TeamCreateResult>> {\n return call(host, accessToken, TeamCreateResultSchema, {\n method: 'POST',\n path: '/api/partner/teams',\n source,\n body,\n });\n}\n\nexport function listTeamMembers(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n): Promise<TeamsApiResult<TeamMemberList>> {\n return call(host, accessToken, TeamMemberListSchema, {\n method: 'GET',\n path: membersPath(target.team),\n source: target.source,\n });\n}\n\nexport function inviteTeamMember(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n body: { email: string; role: string },\n): Promise<TeamsApiResult<TeamMemberInviteResult>> {\n return call(host, accessToken, TeamMemberInviteResultSchema, {\n method: 'POST',\n path: membersPath(target.team),\n source: target.source,\n body,\n });\n}\n\nexport function setTeamMemberRole(\n host: string,\n accessToken: string,\n target: { source: string; team: string; email: string },\n role: string,\n): Promise<TeamsApiResult<TeamMemberChangeResult>> {\n return call(host, accessToken, TeamMemberChangeResultSchema, {\n method: 'PATCH',\n path: `${membersPath(target.team)}/${encodeURIComponent(target.email)}`,\n source: target.source,\n body: { role },\n });\n}\n\nexport function removeTeamMember(\n host: string,\n accessToken: string,\n target: { source: string; team: string; email: string },\n): Promise<TeamsApiResult<TeamMemberChangeResult>> {\n return call(host, accessToken, TeamMemberChangeResultSchema, {\n method: 'DELETE',\n path: `${membersPath(target.team)}/${encodeURIComponent(target.email)}`,\n source: target.source,\n });\n}\n\nexport function listTeamRoles(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n): Promise<TeamsApiResult<TeamRoles>> {\n return call(host, accessToken, TeamRolesSchema, {\n method: 'GET',\n path: `/api/partner/teams/${encodeURIComponent(target.team)}/roles`,\n source: target.source,\n });\n}\n\nfunction membersPath(team: string): string {\n return `/api/partner/teams/${encodeURIComponent(team)}/members`;\n}\n\nfunction integrationPath(target: { team: string; product: string }): string {\n return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}`;\n}\n\n/** One partner teams call; shared by the team-agent clients (`team-agents-api.ts`). */\nexport async function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: {\n method: 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE';\n path: string;\n source: string;\n body?: unknown;\n },\n): Promise<TeamsApiResult<T>> {\n const response = await request(\n `${host}${init.path}?${new URLSearchParams({ source: init.source })}`,\n {\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\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\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 if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n code === 'SOURCE_ADMIN_REQUIRED'\n ? \"Only a Source admin manages its teams' integrations. Ask one to run it.\"\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : code === 'PRODUCT_NOT_ENTITLED'\n ? '\"ekanos catalog\" lists what this Source has; Ekanos enables a product for a Source.'\n : code === 'WEBHOOK_NOT_FOUND' || code === 'CONNECTION_NOT_FOUND'\n ? 'Run \"ekanos teams show <team> <product>\" to see its team apps and webhooks.'\n : code === 'MEMBER_NOT_FOUND'\n ? 'Run \"ekanos teams members list <team>\" to see its members and invitations.'\n : code?.startsWith('AGENT_') || code === 'TEMPLATE_NOT_FOUND'\n ? 'Run \"ekanos teams agents list <team>\" to see the team\\'s agents.'\n : /^(TRIGGER|LINK|MCP)_/.test(code ?? '')\n ? 'List them first: \"ekanos teams triggers|links|mcp list <team> ...\"'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409 && code === 'NO_TEAM_APP') {\n throw invalidStateError(\n message ?? 'This integration takes no team app.',\n 'Teams connect through the Source\\'s app: \"ekanos apps show <product>\".',\n );\n }\n if (response.status === 409 || response.status === 422) {\n throw invalidStateError(\n message ?? 'The host refused the change.',\n code === 'SLUG_TAKEN'\n ? 'Pick another with --slug, or run \"ekanos teams list\" to see the teams.'\n : code === 'ALREADY_MEMBER' || code === 'ALREADY_INVITED'\n ? 'Run \"ekanos teams members list <team>\"; change a role with \"ekanos teams members role <team> <email> <role>\".'\n : code === 'PRIMARY_OWNER'\n ? \"The primary owner's ownership is transferred in the app (team Settings → Members).\"\n : code === 'TEAM_ACCOUNTS_DISABLED'\n ? 'Ekanos switches team accounts on for a Source; ask a super-admin.'\n : 'See \"ekanos teams --help\".',\n );\n }\n if (response.status === 400 && code === 'UNKNOWN_ROLE') {\n throw validationError(\n message ?? 'No such role.',\n 'Run \"ekanos teams roles list <team>\" for the roles this Source uses.',\n );\n }\n if (response.status === 400 && code === 'INVALID_FIELDS') {\n throw validationError(\n message ?? 'The host refused the fields.',\n 'Run \"ekanos teams app set <team> <product>\" again.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos teams --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path} 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 teams routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/teams answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the teams contract.',\n );\n }\n return { status: 'ok', data: 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"]}
|