@ekanos/cli 0.12.0 → 0.13.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 (47) hide show
  1. package/dist/apps-api.d.ts +3 -2
  2. package/dist/apps-api.js +1 -5
  3. package/dist/apps-api.js.map +1 -1
  4. package/dist/commands/agents-edit.js +1 -1
  5. package/dist/commands/agents-edit.js.map +1 -1
  6. package/dist/commands/apps.d.ts +13 -4
  7. package/dist/commands/apps.js +117 -39
  8. package/dist/commands/apps.js.map +1 -1
  9. package/dist/commands/knowledge.d.ts +37 -0
  10. package/dist/commands/knowledge.js +364 -0
  11. package/dist/commands/knowledge.js.map +1 -0
  12. package/dist/commands/sources.d.ts +4 -0
  13. package/dist/commands/sources.js +115 -1
  14. package/dist/commands/sources.js.map +1 -1
  15. package/dist/commands/team-agents-triggers.js +2 -1
  16. package/dist/commands/team-agents-triggers.js.map +1 -1
  17. package/dist/commands/teams-connect.d.ts +25 -0
  18. package/dist/commands/teams-connect.js +242 -0
  19. package/dist/commands/teams-connect.js.map +1 -0
  20. package/dist/commands/teams-onboard.d.ts +5 -2
  21. package/dist/commands/teams-onboard.js +12 -40
  22. package/dist/commands/teams-onboard.js.map +1 -1
  23. package/dist/commands/teams.d.ts +60 -5
  24. package/dist/commands/teams.js +141 -46
  25. package/dist/commands/teams.js.map +1 -1
  26. package/dist/index.js +62 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/knowledge-api.d.ts +129 -0
  29. package/dist/knowledge-api.js +138 -0
  30. package/dist/knowledge-api.js.map +1 -0
  31. package/dist/source-products-api.d.ts +59 -0
  32. package/dist/source-products-api.js +113 -0
  33. package/dist/source-products-api.js.map +1 -0
  34. package/dist/team-connect-api.d.ts +244 -0
  35. package/dist/team-connect-api.js +95 -0
  36. package/dist/team-connect-api.js.map +1 -0
  37. package/dist/teams-api.d.ts +1 -0
  38. package/dist/teams-api.js +1 -1
  39. package/dist/teams-api.js.map +1 -1
  40. package/package.json +2 -2
  41. package/templates/package.json.tmpl +4 -4
  42. package/templates/skills/build-agent/SKILL.md +14 -1
  43. package/templates/skills/build-agent/references/agent-json.md +22 -6
  44. package/templates/skills/build-agent/references/app-setup.md +2 -1
  45. package/templates/skills/build-agent/references/set-up-an-integration.md +19 -8
  46. package/templates/skills/build-agent/references/triggers.md +44 -15
  47. package/templates/skills/ekanos-platform/SKILL.md +32 -6
@@ -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 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"]}
1
+ {"version":3,"file":"teams-api.js","sourceRoot":"","sources":["../src/teams-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAGtB,sBAAsB,EAEtB,4BAA4B,EAE5B,4BAA4B,EAE5B,oBAAoB,EAEpB,eAAe,EAEf,2BAA2B,EAE3B,cAAc,EAEd,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,UAAU,EACV,eAAe,GAChB,MAAM,UAAU,CAAC;AAgClB,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,WAAmB,EACnB,MAAc;IAEd,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE;QAC7C,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,oBAAoB;QAC1B,MAAM;KACP,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,WAAmB,EACnB,MAAyD;IAEzD,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,2BAA2B,EAAE;QAC1D,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC;QAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,WAAmB,EACnB,MAAyD,EACzD,IAIC;IAED,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,sBAAsB,EAAE;QACrD,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM;QACtC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI;KACL,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,WAAmB,EACnB,MAAyD,EACzD,SAAiB;IAEjB,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,2BAA2B,EAAE;QAC1D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,aAAa,kBAAkB,CAAC,SAAS,CAAC,EAAE;QAC5E,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,WAAmB,EACnB,MAAc,EACd,IAAuB;IAEvB,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,sBAAsB,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,oBAAoB;QAC1B,MAAM;QACN,IAAI;KACL,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,WAAmB,EACnB,MAAwC;IAExC,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,oBAAoB,EAAE;QACnD,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,WAAmB,EACnB,MAAwC,EACxC,IAAqC;IAErC,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE;QAC3D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI;KACL,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,WAAmB,EACnB,MAAuD,EACvD,IAAY;IAEZ,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE;QAC3D,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;QACvE,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,EAAE,IAAI,EAAE;KACf,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,WAAmB,EACnB,MAAuD;IAEvD,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE;QAC3D,MAAM,EAAE,QAAQ;QAChB,IAAI,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;QACvE,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,WAAmB,EACnB,MAAwC;IAExC,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE;QAC9C,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,sBAAsB,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ;QACnE,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,sBAAsB,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAClE,CAAC;AAED,SAAS,eAAe,CAAC,MAAyC;IAChE,OAAO,sBAAsB,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;AACpH,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,IAAY,EACZ,WAAmB,EACnB,MAAoB,EACpB,IAKC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAC5B,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAC1G;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,yEAAyE;YAC3E,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,gBAAgB;YACvB,CAAC,CAAC,0DAA0D;YAC5D,CAAC,CAAC,IAAI,KAAK,sBAAsB;gBAC/B,CAAC,CAAC,qFAAqF;gBACvF,CAAC,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,KAAK,sBAAsB;oBAC/D,CAAC,CAAC,6EAA6E;oBAC/E,CAAC,CAAC,IAAI,KAAK,kBAAkB;wBAC3B,CAAC,CAAC,4EAA4E;wBAC9E,CAAC,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,CAAC,QAAQ,CAAC,KAAI,IAAI,KAAK,oBAAoB;4BAC3D,CAAC,CAAC,kEAAkE;4BACpE,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,CAAC;gCACvC,CAAC,CAAC,oEAAoE;gCACtE,CAAC,CAAC,8DAA8D,CAC7E,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QACtD,MAAM,iBAAiB,CACrB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,qCAAqC,EAChD,wEAAwE,CACzE,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvD,MAAM,iBAAiB,CACrB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,8BAA8B,EACzC,IAAI,KAAK,YAAY;YACnB,CAAC,CAAC,wEAAwE;YAC1E,CAAC,CAAC,IAAI,KAAK,gBAAgB,IAAI,IAAI,KAAK,iBAAiB;gBACvD,CAAC,CAAC,+GAA+G;gBACjH,CAAC,CAAC,IAAI,KAAK,eAAe;oBACxB,CAAC,CAAC,oFAAoF;oBACtF,CAAC,CAAC,IAAI,KAAK,wBAAwB;wBACjC,CAAC,CAAC,mEAAmE;wBACrE,CAAC,CAAC,4BAA4B,CACvC,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QACvD,MAAM,eAAe,CACnB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,eAAe,EAC1B,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACzD,MAAM,eAAe,CACnB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,8BAA8B,EACzC,oDAAoD,CACrD,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,4BAA4B,CAC7B,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,YAAY,CAChB,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI,cAAc,QAAQ,CAAC,MAAM,GAAG,EACnD,QAAQ,CAAC,MAAM,IAAI,GAAG;YACpB,CAAC,CAAC,+DAA+D;YACjE,CAAC,CAAC,+DAA+D,CACpE,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,gEAAgE,EACvE,6EAA6E,CAC9E,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 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: {\n connectionId?: string;\n fields: Record<string, string>;\n clear?: string[];\n },\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}${init.path.includes('?') ? '&' : '?'}${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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekanos/cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "private": false,
5
5
  "description": "The Ekanos partner toolchain CLI: scaffold, validate, and test an Ekanos integration against the published @ekanos packages. Agent-native — every verb speaks JSON with a stable exit-code taxonomy.",
6
6
  "keywords": [
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@clack/prompts": "1.7.0",
38
- "@ekanos/integration-schema": "0.15.0",
38
+ "@ekanos/integration-schema": "0.15.1",
39
39
  "esbuild": "0.28.1",
40
40
  "picocolors": "1.1.1"
41
41
  },
@@ -12,12 +12,12 @@
12
12
  "prepare": "node scripts/setup-hooks.mjs"
13
13
  },
14
14
  "dependencies": {
15
- "@ekanos/sdk": "^0.15.0",
16
- "@ekanos/ui": "^0.15.0"
15
+ "@ekanos/sdk": "^0.15.1",
16
+ "@ekanos/ui": "^0.15.1"
17
17
  },
18
18
  "devDependencies": {
19
- "@ekanos/cli": "^0.12.0",
20
- "@ekanos/harness": "^0.15.0",
19
+ "@ekanos/cli": "^0.13.0",
20
+ "@ekanos/harness": "^0.15.1",
21
21
  "@hookform/resolvers": "^5.2.2",
22
22
  "@tailwindcss/postcss": "^4.3.3",
23
23
  "@tanstack/react-query": "^5.101.4",
@@ -49,6 +49,15 @@ every `hint`. Look things up in the `ekanos-sdk-docs` MCP server
49
49
  `provider-app`) is not set up, so no team can connect. Offer to walk them
50
50
  through it now — `references/app-setup.md` — or after the interview. The
51
51
  agent can be drafted while it is pending.
52
+ - **Docs and know-how with no home yet** — when the user asks where the
53
+ agent's docs, policies or FAQs should live, offer **Knowledge** first:
54
+ Ekanos's own knowledge base, nothing to sign up for. One collection per
55
+ team plus the Source's General one; load a folder of markdown with
56
+ `ekanos knowledge push <dir> [--team <team>]`, check with
57
+ `ekanos knowledge search "<words>" --team <team>`, and grant the agent
58
+ `search_knowledge` + `read_knowledge_article` (`references/agent-json.md`
59
+ → `tools`). Keep skills for how-to-work instructions; Knowledge for the
60
+ facts the agent looks up.
52
61
  - **Missing** — no product does it. Say so plainly. Options: build the
53
62
  integration (the `build-integration` skill), use a remote MCP server
54
63
  instead if the service offers one (common for docs and knowledge bases:
@@ -109,7 +118,11 @@ in parentheses are the recommendation when the user has no preference.
109
118
  7. **What starts it** — see `references/triggers.md`. Declare the triggers in
110
119
  the template (an event such as `zendesk.ticket_created` with its filters,
111
120
  a schedule, a webhook) so every team gets them, or leave them to each
112
- team. Say what each team needs connected first.
121
+ team. Take event ids from the catalog's `agentEvents`, which lists every
122
+ event a product declares (Linear also has `linear.issue_labeled`,
123
+ `linear.issue_created` and `linear.assigned`; Zendesk also has
124
+ `zendesk.comment_added`) — map an old trigger onto the matching event
125
+ rather than onto an @mention. Say what each team needs connected first.
113
126
  8. **Who it works with** — other agents of this Source it may ask or hand
114
127
  work to (`links`). A link only exists in a team that runs both agents.
115
128
  9. **What teams may change** — `teamOverrides` per area (approvals,
@@ -39,7 +39,7 @@ conflicting change unless `--overwrite`. Unknown fields are refused.
39
39
  "key": "new-tickets",
40
40
  "type": "event",
41
41
  "event": "zendesk.ticket_created",
42
- "filter": { "tags": ["vip", "urgent"], "brand": "123", "reply_as": "internal_note" }
42
+ "filter": { "tags": ["vip", "urgent"], "brands": ["Acme Support"] }
43
43
  },
44
44
  {
45
45
  "key": "weekday-digest",
@@ -70,6 +70,13 @@ The tool names above are illustrations. Take real ones from
70
70
  - `all`: every tool, reads and changes.
71
71
  - `custom`: whole `products` (slugs) plus single `tools` (names).
72
72
  A tool it must never call is simply not granted — there is no deny list.
73
+ **Knowledge** (Ekanos's own knowledge base, `ekanos knowledge`): add
74
+ `search_knowledge` and `read_knowledge_article` to `tools.tools` for an
75
+ agent that should look things up — they read the team's collection plus
76
+ the Source's General collection, and the agent never picks which. A
77
+ feedback agent that turns reviewer corrections into articles also gets
78
+ `propose_knowledge_article` (a write: it waits for approval by default).
79
+ These are named one by one; no mode or product grants them.
73
80
  Extra team integrations (`teamCustomization.integrations`) need `custom`.
74
81
  - **`policy`** — for each granted tool, `auto` (runs on its own) or
75
82
  `approve` (waits for a person in the app). `reads`/`writes` set the
@@ -108,9 +115,14 @@ The tool names above are illustrations. Take real ones from
108
115
  an optional `prompt` (what to do each time), and `enabled` (default
109
116
  `true`; `false` sets it up turned off). Types:
110
117
  - `event` — `event` is an integration event (`<slug>.<event>`, as
111
- `ekanos catalog <product>` lists under `agentEvents`); `filter` holds the
112
- event's declared filter fields (Zendesk: `organization`, `brand`,
113
- `group`, `tags`, `reply_as` …).
118
+ `ekanos catalog <product> --json` lists under `agentEvents` — every
119
+ event the product declares, e.g. Zendesk `zendesk.ticket_created` and
120
+ `zendesk.comment_added`, Linear `linear.mentioned`, `linear.assigned`,
121
+ `linear.issue_labeled` and `linear.issue_created`); `filter` holds only
122
+ the keys in that event's `filterFields` (Zendesk: `brands`, `groups`,
123
+ `tags`, `organizations`, each a list; Linear: `teamKeys`, plus `labels`,
124
+ which `linear.issue_labeled` requires). Zendesk's `reply_as` is not a
125
+ filter: it is set in the Zendesk trigger's body (`triggers.md`).
114
126
  - `schedule` — `cron` (5 fields, on a 5-minute boundary) and `timezone`
115
127
  (IANA, default `UTC`); give it a `prompt`.
116
128
  - `webhook` — each team gets its **own** URL and secret, created or
@@ -143,7 +155,9 @@ ekanos agents policy <key> unset zendesk_add_comment
143
155
  ekanos agents policy <key> --reads auto --writes approve
144
156
  ekanos agents triggers <key> # list
145
157
  ekanos agents triggers <key> add event new-tickets --event zendesk.ticket_created \
146
- --filter tags=vip,urgent --filter brand=123 --filter reply_as=internal_note
158
+ --filter tags=vip,urgent --filter brands=Acme,
159
+ ekanos agents triggers <key> add event feedback-label --event linear.issue_labeled \
160
+ --filter labels=feedback,
147
161
  ekanos agents triggers <key> add schedule weekday-digest --cron "0 9 * * 1-5" \
148
162
  --timezone America/Los_Angeles --prompt "Summarize yesterday's escalations."
149
163
  ekanos agents triggers <key> add webhook inbound --prompt "Triage the posted ticket."
@@ -156,7 +170,9 @@ ekanos agents overrides <key> disable policy
156
170
  ```
157
171
 
158
172
  `--filter field=value` repeats; a value with commas is a list, `true` or
159
- `false` a switch, anything else text. `--disabled` adds a trigger turned off.
173
+ `false` a switch, anything else text. A list field (`tags`, `brands`,
174
+ `labels`, `teamKeys` …) needs a list even for one item, so end a single
175
+ item with a comma: `--filter labels=feedback,`. `--disabled` adds a trigger turned off.
160
176
 
161
177
  ## What the host checks on `apply`
162
178
 
@@ -47,7 +47,8 @@ working from those rather than from Ekanos.
47
47
  npx @ekanos/cli apps set <product> --source <slug>
48
48
  ```
49
49
 
50
- It asks for the client ID, then the secret with hidden input. For a team's
50
+ It asks for the client ID, then the secret with hidden input; Enter keeps
51
+ a saved value, so either can be changed on its own. For a team's
51
52
  app, they enter the fields in the team's setup panel in the Ekanos app.
52
53
  - Then check: `apps show <product> --source <slug> --json` should say
53
54
  `configured: true`, and the catalog's step should be `done`. Tell them
@@ -62,11 +62,16 @@ connect nothing for it.
62
62
  `ekanos teams app set`, with hidden input) or in the team's page in the app.
63
63
  If they paste one into the chat anyway, tell them to rotate it in the
64
64
  provider's console and enter the new one at the terminal.
65
- - **Signing in is a person's click.** Connecting (the provider's consent
66
- screen) happens in the browser, by someone on the team, from the link in
67
- `teams show` (or just the link and whether it is connected yet:
68
- `teams connect <team> <product>`). Tell them who should click it: the account the integration
69
- acts as. A dedicated seat such as "Support agent" makes its replies easy
65
+ - **Connect with `teams connect <team> <product>`, run by the user in their
66
+ own terminal.** It does whatever the integration needs: asks a form's fields
67
+ (secrets hidden), runs the team-app prompts first when the team's own app is
68
+ missing, or opens a one-time sign-in link and waits until the team is
69
+ connected. The provider's consent is still a person's click, by a member of
70
+ the team (owner/admin for a team-wide connection): the user when they are
71
+ one, else `--as <email>` prints a link to send that member. `--link-only`
72
+ prints just the team's page. Tell them who should sign in: the account the
73
+ integration acts as. If the product is not enabled for the Source, a
74
+ super-admin enables it (`sources products` shows what the Source has). A dedicated seat such as "Support agent" makes its replies easy
70
75
  to recognise.
71
76
  - **Close each product out**: one line on what now works ("New tickets in
72
77
  Acme's Zendesk will start the support agent"), then move on to the next
@@ -82,8 +87,13 @@ pilot), the team-side steps can be done from the CLI:
82
87
  2. They create the app in the provider's console, step by step as above.
83
88
  3. **In their own terminal:**
84
89
  `npx @ekanos/cli teams app set <team> <product> --source <slug>`. It asks
85
- for each field and hides the secret ones. It refuses to run inside Claude;
86
- that is deliberate.
90
+ for each field and hides the secret ones; Enter keeps a saved value. It
91
+ refuses to run inside Claude; that is deliberate. To change one value
92
+ later, they need not re-enter the rest:
93
+ `teams app set <team> <product> --field <name>` asks for just that field
94
+ (names are in brackets in `teams show`), `--set <name>=<value>` sets a
95
+ non-secret field (a subdomain, a client ID) without a prompt — you may run
96
+ that one yourself — and `--clear <name>` removes a value.
87
97
  4. If the integration takes webhooks:
88
98
  `npx @ekanos/cli teams webhook <team> <product> --source <slug>` prints the
89
99
  team's webhook URL **once**. Have them paste it into the provider at that
@@ -159,7 +169,8 @@ from `teams show zendesk`.
159
169
  secret.
160
170
  3. **Credentials into Ekanos.** `ekanos teams app set <team> zendesk` in
161
171
  their terminal: subdomain, client ID, client secret, webhook signing
162
- secret.
172
+ secret. Any of them can be entered or changed later on its own
173
+ (`--field <name>`); `teams show` says which are saved.
163
174
  4. **Connect.** Someone on the team opens the link and connects, as the
164
175
  Zendesk agent the integration should reply as.
165
176
  5. **Zendesk triggers.** Objects and rules › Triggers: one for *Ticket is
@@ -20,7 +20,14 @@ A Source admin can add them for the team from the terminal instead:
20
20
  `ekanos teams triggers add acme-support support-agent event zendesk.ticket_created --filter '{"tags":["billing"]}'`,
21
21
  `… schedule --cron "0 9 * * 1-5" --timezone America/Los_Angeles --prompt "…"`,
22
22
  `… webhook --prompt "…"` (prints the URL and token once) or `… manual`.
23
- Slack and Linear triggers are added in the app. When a provided agent's
23
+ Any event in a connected integration's `agentEvents` is added this way,
24
+ including every Linear integration event (`event linear.issue_labeled
25
+ --filter '{"labels":["feedback"]}'`). Only two trigger *types* are app-only:
26
+ a **Slack** trigger, and a **Linear (Agents app)** trigger — Ekanos's own
27
+ Linear app, which answers @mentions and assignments only. That app is not
28
+ the Linear integration, and its limits are not the integration's: an agent
29
+ that should start when a label is applied uses `linear.issue_labeled`, not an
30
+ @mention. When a provided agent's
24
31
  template declares triggers and has not turned on team overrides for them, the
25
32
  team's triggers follow the template and these commands refuse to change them.
26
33
 
@@ -39,23 +46,45 @@ team's triggers follow the template and these commands refuse to change them.
39
46
 
40
47
  ## Integrations with agent events
41
48
 
42
- Check `ekanos catalog <product> --source <slug> --json` for what is actually
43
- there; these are the known shapes.
49
+ `ekanos catalog <product> --source <slug> --json` is the authority: its
50
+ `agentEvents` lists **every** event the product declares, with each one's
51
+ filter keys (`filterFields`). The lists below are the known shapes; never
52
+ conclude a product lacks an event because an example here or elsewhere
53
+ names only one. A refused event id is answered with the ids the product
54
+ does declare, and a refused filter with the keys the event takes.
44
55
 
45
- **Linear** (`linear.mentioned`, `linear.assigned`, `linear.issue_labeled`) —
46
- the agent becomes a teammate in Linear: @mention it, assign it an issue, or
47
- apply a label. Each team needs two things in the app: each person's own
48
- Linear connection (for the tools), and the team's own Linear app for the
49
- agent (client id, secret and webhook signing secret, entered in Ekanos).
56
+ An event can be declared and still never fire for a team until the team
57
+ finishes that event's own setup on the provider's side. That is a setup
58
+ step, not a missing event — say which step, as below.
59
+
60
+ **Linear** — the agent becomes a teammate in Linear. Each team needs two
61
+ things in the app: each person's own Linear connection (for the tools), and
62
+ the team's own Linear app for the agent (client id, secret and webhook
63
+ signing secret, entered in Ekanos).
50
64
  Guide: https://sdk.ekanos.dev/guides/linear-agent
51
65
 
52
- **Zendesk** (`zendesk.ticket_created`, `zendesk.comment_added`; filters such
53
- as brand, group, tags, organization) — when present in the catalog, each team
54
- connects its own Zendesk (subdomain plus an OAuth client it creates in
55
- Zendesk) and adds a Zendesk webhook pointing at its Ekanos endpoint
56
- (walk-through: `set-up-an-integration.md`, worked example). The agent
57
- answers on the ticket as a public reply or an internal note — recommend
58
- internal notes, or approval on public replies, until the team trusts it.
66
+ | Event | Starts when | Filter keys | Needs on the team's Linear app |
67
+ |---|---|---|---|
68
+ | `linear.mentioned` | someone @mentions the agent, or writes to it again in that conversation | `teamKeys` | webhook with **Agent session events** ticked |
69
+ | `linear.assigned` | someone assigns (delegates) an issue to the agent | `teamKeys` | webhook with **Agent session events** ticked |
70
+ | `linear.issue_labeled` | someone applies one of the given labels to an issue | `labels` (**required**), `teamKeys` | webhook with **Issues** ticked |
71
+ | `linear.issue_created` | someone creates an issue (optionally only with given labels) | `labels`, `teamKeys` | webhook with **Issues** ticked |
72
+
73
+ **Zendesk** — each team connects its own Zendesk (subdomain plus an OAuth
74
+ client it creates in Zendesk) and adds a Zendesk webhook pointing at its
75
+ Ekanos endpoint (walk-through: `set-up-an-integration.md`, worked example).
76
+ The agent answers on the ticket as a public reply or an internal note —
77
+ recommend internal notes, or approval on public replies, until the team
78
+ trusts it.
79
+
80
+ | Event | Starts when | Filter keys | Needs in Zendesk |
81
+ |---|---|---|---|
82
+ | `zendesk.ticket_created` | a new ticket arrives | `brands`, `groups`, `tags`, `organizations` | a Zendesk trigger on *Ticket is Created* notifying the webhook |
83
+ | `zendesk.comment_added` | an end user (requester or CC) comments on a ticket; agent and admin comments never start it | `brands`, `groups`, `tags`, `organizations` | a **second** Zendesk trigger, on a customer's public reply, notifying the webhook |
84
+
85
+ Filter values are lists (`{"tags":["billing"],"brands":["Acme Support"]}`);
86
+ brands, groups and organizations match by name or id. Where the answer goes
87
+ (`reply_as`) is set in the Zendesk trigger's body, not in the filter.
59
88
 
60
89
  ## Checklist to hand each team
61
90
 
@@ -53,7 +53,9 @@ npx @ekanos/cli teams members invite bank-a raj@bank-a.com --role admin # defa
53
53
  npx @ekanos/cli teams members list bank-a # members and pending invitations
54
54
  npx @ekanos/cli teams members role bank-a raj@bank-a.com member
55
55
  npx @ekanos/cli teams members remove bank-a raj@bank-a.com --yes
56
- npx @ekanos/cli teams connect bank-a zendesk # the link a member opens to connect
56
+ npx @ekanos/cli teams connect bank-a zendesk # connect it for the team (asks, signs in, waits)
57
+ npx @ekanos/cli teams connect bank-a linear --as raj@bank-a.com # a sign-in link for raj
58
+ npx @ekanos/cli sources products # what the Source has enabled (super-admin enables)
57
59
  ```
58
60
 
59
61
  - `create` without `--owner` seats the person running it as the team's
@@ -66,9 +68,18 @@ npx @ekanos/cli teams connect bank-a zendesk # the link a membe
66
68
  customized ones). Pick a reviewer for agents by the role holding
67
69
  `agents.review` in `teams roles list`. The primary owner's role cannot be
68
70
  changed here; ownership moves in the app.
69
- - Connecting an integration is always a team member signing in in their
70
- browser. `teams connect` prints the link to send them and whether the team
71
- is connected yet; `teams show <team> <product>` has the full checklist.
71
+ - `teams connect <team> <product>` does the connect: it asks a form's fields
72
+ at the user's terminal (hidden for secrets — so the user runs it, never you
73
+ with values), runs the team-app prompts first when needed (Zendesk), or
74
+ opens a one-time sign-in link and waits. The person who signs in is a team
75
+ member (owner/admin for a team-wide connection): the user when they are
76
+ one, else `--as <email>` prints a link to send that member. `--no-wait`,
77
+ `--no-open`, `--link-only` (just the team's page). `teams show <team>
78
+ <product>` has the full checklist.
79
+ - A product the Source does not have is refused; `sources products` lists
80
+ what it has. Enabling one is a super-admin grant (`sources products enable`)
81
+ that an `ekanos login` session cannot make — the super-admin does it in
82
+ /admin.
72
83
 
73
84
  ## Products: integrations and experiences
74
85
 
@@ -77,7 +88,11 @@ Everything in the catalog (`npx @ekanos/cli catalog --source <slug> --json`) is
77
88
 
78
89
  - **Integration** — a connection to a third party (Linear, Zendesk, Slack…).
79
90
  Brings tools an agent can call, and may declare **agent events** that start
80
- agents (`linear.mentioned`, `zendesk.ticket_created`…).
91
+ agents — Linear declares `linear.mentioned`, `linear.assigned`,
92
+ `linear.issue_labeled` and `linear.issue_created`; Zendesk declares
93
+ `zendesk.ticket_created` and `zendesk.comment_added`. The catalog's
94
+ `agentEvents` is the authority: read it before concluding a product lacks
95
+ an event, never infer the set from an example.
81
96
  - **Experience** — an Ekanos-native capability (Agents, Workflows, Home…).
82
97
  Never "connected"; it is simply on or off for the Source.
83
98
 
@@ -144,6 +159,14 @@ Skills are folders (`SKILL.md` + references) an agent loads when needed.
144
159
  A Source admin pushes them with `ekanos skills push <dir>`: at Source level
145
160
  every team can use them, with `--team <slug>` only that team can.
146
161
 
162
+ **Knowledge** is Ekanos's own knowledge base (no outside account): markdown
163
+ articles, one collection per team plus the Source's **General** collection
164
+ every team reads. A Source admin loads folders of `.md` with
165
+ `ekanos knowledge push <dir> [--team <slug>]`; team members edit theirs in
166
+ the app (Agents → Knowledge). Agents granted `search_knowledge` /
167
+ `read_knowledge_article` search their own team's collection and General.
168
+ Skills say *how* to work; Knowledge holds the facts an agent looks up.
169
+
147
170
  The `build-agent` skill walks through choosing between these and building one.
148
171
 
149
172
  A Source admin can also do a team's agent setup from the terminal, acting on
@@ -165,7 +188,9 @@ always `ekanos teams <area> <verb> <team> …`:
165
188
  follows its template unless the template turns on team overrides for it.
166
189
  - `ekanos teams triggers list|add|remove <team> <key> …` — manual, schedule
167
190
  (`--cron`, `--timezone`), webhook (URL and token shown once) and
168
- integration events (`event zendesk.ticket_created --filter '{…}'`).
191
+ integration events (`event zendesk.ticket_created --filter '{…}'`, or any
192
+ other id in the product's `agentEvents`, e.g. `event linear.issue_labeled
193
+ --filter '{"labels":["feedback"]}'`).
169
194
  - `ekanos teams links list|add|remove <team> <from-key> [<to-key>] --kind
170
195
  talk|delegate [--mode await|handoff]`.
171
196
  - `ekanos teams mcp list|add|status|token|effect <team> [<server-key> …]` — a
@@ -215,6 +240,7 @@ is refused (`USER_NOT_IN_SOURCE`): `ekanos teams list` shows the teams.
215
240
  | Running a team's agent, reading its runs (transcript, tool calls) | A team's agent manager in the app, or a Source admin with `ekanos runs` |
216
241
  | Deciding what an agent run waits on (approve, reject, edit & send a reply) | A team's agent manager in the app, or a Source admin with `ekanos approvals` |
217
242
  | Skills (SKILL.md folders) for every team, or one team's own | A Source admin, with `ekanos skills push` (`--team <slug>` for one team) |
243
+ | Knowledge articles for every team (General), or one team's own | A Source admin, with `ekanos knowledge push` (`--team <slug>` for one team); a team's members in the app |
218
244
  | Who decides agent drafts that wait for a person | A Source admin, with `ekanos reviewers` |
219
245
  | An integration's app for the whole Source (client ID and secret) | A Source admin, with `ekanos apps set` at their own terminal |
220
246
  | One connection for the whole Source, used by provided agents in every team | A Source admin, with `ekanos apps connect` (status: `ekanos apps show`), or **Shared integrations › Connections** in the Source admin area |