@ekanos/cli 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/agents-edit.d.ts +49 -0
  3. package/dist/commands/agents-edit.js +385 -0
  4. package/dist/commands/agents-edit.js.map +1 -0
  5. package/dist/commands/agents.d.ts +24 -1
  6. package/dist/commands/agents.js +117 -8
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/apps.d.ts +3 -0
  9. package/dist/commands/apps.js +2 -2
  10. package/dist/commands/apps.js.map +1 -1
  11. package/dist/commands/reviewers.d.ts +21 -0
  12. package/dist/commands/reviewers.js +162 -0
  13. package/dist/commands/reviewers.js.map +1 -0
  14. package/dist/commands/runs.d.ts +42 -0
  15. package/dist/commands/runs.js +362 -0
  16. package/dist/commands/runs.js.map +1 -0
  17. package/dist/commands/skills.d.ts +60 -0
  18. package/dist/commands/skills.js +397 -0
  19. package/dist/commands/skills.js.map +1 -0
  20. package/dist/commands/team-agents-agents.d.ts +9 -0
  21. package/dist/commands/team-agents-agents.js +188 -0
  22. package/dist/commands/team-agents-agents.js.map +1 -0
  23. package/dist/commands/team-agents-mcp.d.ts +4 -0
  24. package/dist/commands/team-agents-mcp.js +218 -0
  25. package/dist/commands/team-agents-mcp.js.map +1 -0
  26. package/dist/commands/team-agents-triggers.d.ts +12 -0
  27. package/dist/commands/team-agents-triggers.js +251 -0
  28. package/dist/commands/team-agents-triggers.js.map +1 -0
  29. package/dist/commands/team-agents.d.ts +31 -0
  30. package/dist/commands/team-agents.js +71 -0
  31. package/dist/commands/team-agents.js.map +1 -0
  32. package/dist/commands/teams-onboard.d.ts +51 -0
  33. package/dist/commands/teams-onboard.js +261 -0
  34. package/dist/commands/teams-onboard.js.map +1 -0
  35. package/dist/commands/teams.d.ts +45 -0
  36. package/dist/commands/teams.js +314 -0
  37. package/dist/commands/teams.js.map +1 -0
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.js +222 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/reviewers-api.d.ts +33 -0
  42. package/dist/reviewers-api.js +81 -0
  43. package/dist/reviewers-api.js.map +1 -0
  44. package/dist/runs-api.d.ts +34 -0
  45. package/dist/runs-api.js +112 -0
  46. package/dist/runs-api.js.map +1 -0
  47. package/dist/skills-api.d.ts +28 -0
  48. package/dist/skills-api.js +104 -0
  49. package/dist/skills-api.js.map +1 -0
  50. package/dist/teams-api.d.ts +85 -0
  51. package/dist/teams-api.js +168 -0
  52. package/dist/teams-api.js.map +1 -0
  53. package/package.json +2 -2
  54. package/templates/package.json.tmpl +4 -4
  55. package/templates/skills/build-agent/SKILL.md +106 -17
  56. package/templates/skills/build-agent/references/agent-json.md +78 -2
  57. package/templates/skills/build-agent/references/rollout.md +9 -1
  58. package/templates/skills/build-agent/references/set-up-an-integration.md +134 -0
  59. package/templates/skills/build-agent/references/skills.md +78 -0
  60. package/templates/skills/build-agent/references/triggers.md +27 -6
  61. package/templates/skills/ekanos-platform/SKILL.md +115 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewers-api.js","sourceRoot":"","sources":["../src/reviewers-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,6BAA6B,EAE7B,wBAAwB,EAExB,gCAAgC,GACjC,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,UAAU,GACX,MAAM,UAAU,CAAC;AAuBlB,MAAM,IAAI,GAAG,wBAAwB,CAAC;AAEtC,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,WAAmB,EACnB,MAAc;IAEd,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,wBAAwB,EAAE;QACvD,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,EAAE,MAAM,EAAE;KAClB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,WAAmB,EACnB,MAAc,EACd,IAA4D;IAE5D,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE;QAC5D,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,EAAE,MAAM,EAAE;QACjB,IAAI;KACL,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,WAAmB,EACnB,MAAc,EACd,MAAwC;IAExC,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE;QAC/D,MAAM,EAAE,QAAQ;QAChB,KAAK,kBACH,MAAM,EACN,KAAK,EAAE,MAAM,CAAC,KAAK,IAChB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC9C;KACF,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,IAAI,CACjB,IAAY,EACZ,WAAmB,EACnB,MAAoB,EACpB,IAIC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAC5B,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EACnD;QACE,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,kBACL,MAAM,EAAE,kBAAkB,EAC1B,aAAa,EAAE,UAAU,WAAW,EAAE,IACnC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS;YACzB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAC5C;QACD,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;KACtE,CACF,CAAC;IAEF,IACE,QAAQ,CAAC,MAAM,KAAK,GAAG;QACvB,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,EACjD,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,OAAO,GACX,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAEnE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QACvD,MAAM,iBAAiB,CACrB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,GAAG,IAAI,wCAAwC,EAC1D,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,cAAc,CAClB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,+BAA+B,EAC1C,IAAI,KAAK,uBAAuB;YAC9B,CAAC,CAAC,6DAA6D;YAC/D,CAAC,CAAC,8DAA8D,CACnE,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,aAAa,CACjB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,YAAY,EACvB,IAAI,KAAK,oBAAoB;YAC3B,CAAC,CAAC,mFAAmF;YACrF,CAAC,CAAC,IAAI,KAAK,gBAAgB;gBACzB,CAAC,CAAC,0DAA0D;gBAC5D,CAAC,CAAC,IAAI,KAAK,oBAAoB;oBAC7B,CAAC,CAAC,4CAA4C;oBAC9C,CAAC,CAAC,8DAA8D,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,UAAU,CACd,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,+BAA+B,EAC1C,gCAAgC,CACjC,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,YAAY,CAChB,GAAG,IAAI,GAAG,IAAI,cAAc,QAAQ,CAAC,MAAM,GAAG,EAC9C,QAAQ,CAAC,MAAM,IAAI,GAAG;YACpB,CAAC,CAAC,+DAA+D;YACjE,CAAC,CAAC,kEAAkE,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,YAAY,CAChB,GAAG,IAAI,GAAG,IAAI,8CAA8C,EAC5D,iFAAiF,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,QAAkB;IAElB,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC9C,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;YAClD,CAAC,CAAE,MAAkC;YACrC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;eAAO,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC","sourcesContent":["import {\n type ReviewerReach,\n type SourceReviewerAddResult,\n SourceReviewerAddResultSchema,\n type SourceReviewerList,\n SourceReviewerListSchema,\n type SourceReviewerRemoveResult,\n SourceReviewerRemoveResultSchema,\n} from '@ekanos/integration-schema';\nimport type { 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 named reviewers — the people who decide\n * agent reply drafts held under an `approve` policy.\n *\n * GET /api/partner/reviewers?source=<slug> → SourceReviewerList\n * POST /api/partner/reviewers?source=<slug> { email, reach, team? }\n * → SourceReviewerAddResult\n * DELETE /api/partner/reviewers?source=<slug>&email=<email>[&team=<slug>]\n * → SourceReviewerRemoveResult\n *\n * → 401 / 3xx refresh once and retry\n * → 403 { code: \"SOURCE_ADMIN_REQUIRED\" | \"MFA_REQUIRED\" | \"FORBIDDEN\" }\n * → 404 { code: \"USER_NOT_IN_SOURCE\" | \"TEAM_NOT_FOUND\" | \"REVIEWER_NOT_FOUND\"\n * | \"SOURCE_NOT_FOUND\" }\n * → 400 { code: \"INVALID_REQUEST\" }\n */\n\nexport type ReviewersApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nconst PATH = '/api/partner/reviewers';\n\nexport function listReviewers(\n host: string,\n accessToken: string,\n source: string,\n): Promise<ReviewersApiResult<SourceReviewerList>> {\n return call(host, accessToken, SourceReviewerListSchema, {\n method: 'GET',\n query: { source },\n });\n}\n\nexport function addReviewer(\n host: string,\n accessToken: string,\n source: string,\n body: { email: string; reach: ReviewerReach; team?: string },\n): Promise<ReviewersApiResult<SourceReviewerAddResult>> {\n return call(host, accessToken, SourceReviewerAddResultSchema, {\n method: 'POST',\n query: { source },\n body,\n });\n}\n\nexport function removeReviewer(\n host: string,\n accessToken: string,\n source: string,\n target: { email: string; team?: string },\n): Promise<ReviewersApiResult<SourceReviewerRemoveResult>> {\n return call(host, accessToken, SourceReviewerRemoveResultSchema, {\n method: 'DELETE',\n query: {\n source,\n email: target.email,\n ...(target.team ? { team: target.team } : {}),\n },\n });\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: {\n method: 'GET' | 'POST' | 'DELETE';\n query: Record<string, string>;\n body?: unknown;\n },\n): Promise<ReviewersApiResult<T>> {\n const response = await request(\n `${host}${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 names its reviewers. 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 === 'USER_NOT_IN_SOURCE'\n ? 'A reviewer must be a member of one of this Source\\'s teams — \"ekanos teams list\".'\n : code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : code === 'REVIEWER_NOT_FOUND'\n ? '\"ekanos reviewers list\" shows who reviews.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos reviewers --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${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 reviewers route.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}${PATH} answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the reviewers 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 {};
@@ -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"]}
@@ -0,0 +1,85 @@
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';
3
+ /**
4
+ * The CLI's client for a Source's teams and their integration setup — the
5
+ * team-level counterpart of `apps-api.ts`.
6
+ *
7
+ * GET /api/partner/teams?source=<slug> → TeamList
8
+ * GET /api/partner/teams/<team>/integrations/<product>?source=<slug> → TeamIntegrationStatus
9
+ * PUT …/integrations/<product>/app?source=<slug> { connectionId?, fields }
10
+ * → TeamAppSetResult
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
18
+ *
19
+ * → 401 / 3xx refresh once and retry
20
+ * → 403 { code: "SOURCE_ADMIN_REQUIRED" | "MFA_REQUIRED" | "FORBIDDEN" }
21
+ * → 404 { code: "TEAM_NOT_FOUND" | "PRODUCT_NOT_ENTITLED" | "WEBHOOK_NOT_FOUND"
22
+ * | "CONNECTION_NOT_FOUND" | "SOURCE_NOT_FOUND" }
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" }
27
+ */
28
+ export type TeamsApiResult<T> = {
29
+ status: 'ok';
30
+ data: T;
31
+ } | {
32
+ status: 'unauthorized';
33
+ };
34
+ export declare function listTeams(host: string, accessToken: string, source: string): Promise<TeamsApiResult<TeamList>>;
35
+ export declare function showTeamIntegration(host: string, accessToken: string, target: {
36
+ source: string;
37
+ team: string;
38
+ product: string;
39
+ }): Promise<TeamsApiResult<TeamIntegrationStatus>>;
40
+ export declare function setTeamApp(host: string, accessToken: string, target: {
41
+ source: string;
42
+ team: string;
43
+ product: string;
44
+ }, body: {
45
+ connectionId?: string;
46
+ fields: Record<string, string>;
47
+ }): Promise<TeamsApiResult<TeamAppSetResult>>;
48
+ export declare function mintTeamWebhook(host: string, accessToken: string, target: {
49
+ source: string;
50
+ team: string;
51
+ product: string;
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>>;
@@ -0,0 +1,168 @@
1
+ import { TeamAppSetResultSchema, TeamCreateResultSchema, TeamMemberChangeResultSchema, TeamMemberInviteResultSchema, TeamMemberListSchema, TeamRolesSchema, TeamIntegrationStatusSchema, TeamListSchema, TeamWebhookMintResultSchema, } 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
+ export function listTeams(host, accessToken, source) {
5
+ return call(host, accessToken, TeamListSchema, {
6
+ method: 'GET',
7
+ path: '/api/partner/teams',
8
+ source,
9
+ });
10
+ }
11
+ export function showTeamIntegration(host, accessToken, target) {
12
+ return call(host, accessToken, TeamIntegrationStatusSchema, {
13
+ method: 'GET',
14
+ path: integrationPath(target),
15
+ source: target.source,
16
+ });
17
+ }
18
+ export function setTeamApp(host, accessToken, target, body) {
19
+ return call(host, accessToken, TeamAppSetResultSchema, {
20
+ method: 'PUT',
21
+ path: `${integrationPath(target)}/app`,
22
+ source: target.source,
23
+ body,
24
+ });
25
+ }
26
+ export function mintTeamWebhook(host, accessToken, target, webhookId) {
27
+ return call(host, accessToken, TeamWebhookMintResultSchema, {
28
+ method: 'POST',
29
+ path: `${integrationPath(target)}/webhooks/${encodeURIComponent(webhookId)}`,
30
+ source: target.source,
31
+ });
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
+ }
81
+ function integrationPath(target) {
82
+ return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}`;
83
+ }
84
+ /** One partner teams call; shared by the team-agent clients (`team-agents-api.ts`). */
85
+ export async function call(host, accessToken, schema, init) {
86
+ const response = await request(`${host}${init.path}?${new URLSearchParams({ source: init.source })}`, {
87
+ method: init.method,
88
+ headers: Object.assign({ accept: 'application/json', authorization: `Bearer ${accessToken}` }, (init.body === undefined
89
+ ? {}
90
+ : { 'content-type': 'application/json' })),
91
+ body: init.body === undefined ? undefined : JSON.stringify(init.body),
92
+ });
93
+ if (response.status === 401 ||
94
+ (response.status >= 300 && response.status < 400)) {
95
+ return { status: 'unauthorized' };
96
+ }
97
+ const body = await readOptionalJson(response);
98
+ const code = typeof body.code === 'string' ? body.code : null;
99
+ const message = typeof body.error === 'string' && body.error ? body.error : null;
100
+ if (response.status === 403 && code === 'MFA_REQUIRED') {
101
+ throw authRequiredError(message !== null && message !== void 0 ? message : `${host} requires multi-factor authentication.`, 'Complete MFA in the browser, then run "ekanos login" again.');
102
+ }
103
+ if (response.status === 403) {
104
+ throw forbiddenError(message !== null && message !== void 0 ? message : 'Not permitted on this Source.', code === 'SOURCE_ADMIN_REQUIRED'
105
+ ? "Only a Source admin manages its teams' integrations. Ask one to run it."
106
+ : 'Run "ekanos sources" to list the sources you hold a seat on.');
107
+ }
108
+ if (response.status === 404) {
109
+ throw notFoundError(message !== null && message !== void 0 ? message : 'Not found.', code === 'TEAM_NOT_FOUND'
110
+ ? 'Run "ekanos teams list" to see the teams on this Source.'
111
+ : code === 'PRODUCT_NOT_ENTITLED'
112
+ ? '"ekanos catalog" lists what this Source has; Ekanos enables a product for a Source.'
113
+ : code === 'WEBHOOK_NOT_FOUND' || code === 'CONNECTION_NOT_FOUND'
114
+ ? 'Run "ekanos teams show <team> <product>" to see its team apps and webhooks.'
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.');
122
+ }
123
+ if (response.status === 409 && code === 'NO_TEAM_APP') {
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>".');
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
+ }
140
+ if (response.status === 400 && code === 'INVALID_FIELDS') {
141
+ throw validationError(message !== null && message !== void 0 ? message : 'The host refused the fields.', 'Run "ekanos teams app set <team> <product>" again.');
142
+ }
143
+ if (response.status === 400) {
144
+ throw usageError(message !== null && message !== void 0 ? message : 'The host refused the request.', 'See "ekanos teams --help".');
145
+ }
146
+ if (!response.ok) {
147
+ throw networkError(`${host}${init.path} responded ${response.status}.`, response.status >= 500
148
+ ? 'The Ekanos deployment returned a server error. Retry shortly.'
149
+ : 'Confirm the host runs a build that includes the teams routes.');
150
+ }
151
+ const parsed = schema.safeParse(body.data);
152
+ if (!parsed.success) {
153
+ throw networkError(`${host}/api/partner/teams answered in a shape this CLI does not read.`, 'Run "ekanos upgrade" — the CLI and the host disagree on the teams contract.');
154
+ }
155
+ return { status: 'ok', data: parsed.data };
156
+ }
157
+ async function readOptionalJson(response) {
158
+ try {
159
+ const parsed = await response.json();
160
+ return typeof parsed === 'object' && parsed !== null
161
+ ? parsed
162
+ : {};
163
+ }
164
+ catch (_a) {
165
+ return {};
166
+ }
167
+ }
168
+ //# sourceMappingURL=teams-api.js.map