@ekanos/cli 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -8
- package/dist/agents-api.d.ts +11 -2
- package/dist/agents-api.js +39 -3
- package/dist/agents-api.js.map +1 -1
- package/dist/apps-api.d.ts +3 -2
- package/dist/apps-api.js +3 -7
- package/dist/apps-api.js.map +1 -1
- package/dist/auth/ekanos-api.d.ts +16 -2
- package/dist/auth/ekanos-api.js +101 -15
- package/dist/auth/ekanos-api.js.map +1 -1
- package/dist/auth/host-trust.d.ts +46 -0
- package/dist/auth/host-trust.js +79 -0
- package/dist/auth/host-trust.js.map +1 -0
- package/dist/auth/session.d.ts +10 -1
- package/dist/auth/session.js +28 -28
- package/dist/auth/session.js.map +1 -1
- package/dist/commands/agents-edit.d.ts +6 -2
- package/dist/commands/agents-edit.js +57 -26
- package/dist/commands/agents-edit.js.map +1 -1
- package/dist/commands/agents.d.ts +13 -2
- package/dist/commands/agents.js +148 -36
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/apps.d.ts +15 -4
- package/dist/commands/apps.js +133 -51
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/catalog.js +7 -6
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/knowledge.d.ts +56 -0
- package/dist/commands/knowledge.js +456 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.js +30 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +9 -1
- package/dist/commands/logout.js +56 -7
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/publish.js +3 -3
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/reviewers.d.ts +2 -0
- package/dist/commands/reviewers.js +7 -3
- package/dist/commands/reviewers.js.map +1 -1
- package/dist/commands/routes.d.ts +2 -0
- package/dist/commands/routes.js +42 -9
- package/dist/commands/routes.js.map +1 -1
- package/dist/commands/runs.d.ts +13 -2
- package/dist/commands/runs.js +96 -15
- package/dist/commands/runs.js.map +1 -1
- package/dist/commands/scope-notice.d.ts +21 -0
- package/dist/commands/scope-notice.js +35 -0
- package/dist/commands/scope-notice.js.map +1 -0
- package/dist/commands/skills.d.ts +7 -1
- package/dist/commands/skills.js +80 -24
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/sources.d.ts +4 -0
- package/dist/commands/sources.js +233 -5
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/status.js +5 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/team-agent-readiness.d.ts +59 -0
- package/dist/commands/team-agent-readiness.js +341 -0
- package/dist/commands/team-agent-readiness.js.map +1 -0
- package/dist/commands/team-agents-agents.d.ts +24 -3
- package/dist/commands/team-agents-agents.js +336 -35
- package/dist/commands/team-agents-agents.js.map +1 -1
- package/dist/commands/team-agents-mcp.js +32 -3
- package/dist/commands/team-agents-mcp.js.map +1 -1
- package/dist/commands/team-agents-triggers.js +230 -54
- package/dist/commands/team-agents-triggers.js.map +1 -1
- package/dist/commands/team-agents.d.ts +3 -1
- package/dist/commands/team-agents.js +27 -7
- package/dist/commands/team-agents.js.map +1 -1
- package/dist/commands/teams-channel-install.d.ts +24 -0
- package/dist/commands/teams-channel-install.js +94 -0
- package/dist/commands/teams-channel-install.js.map +1 -0
- package/dist/commands/teams-connect.d.ts +66 -0
- package/dist/commands/teams-connect.js +397 -0
- package/dist/commands/teams-connect.js.map +1 -0
- package/dist/commands/teams-events.d.ts +12 -0
- package/dist/commands/teams-events.js +138 -0
- package/dist/commands/teams-events.js.map +1 -0
- package/dist/commands/teams-onboard.d.ts +16 -5
- package/dist/commands/teams-onboard.js +61 -36
- package/dist/commands/teams-onboard.js.map +1 -1
- package/dist/commands/teams-overview.d.ts +11 -0
- package/dist/commands/teams-overview.js +160 -0
- package/dist/commands/teams-overview.js.map +1 -0
- package/dist/commands/teams-scope.d.ts +10 -0
- package/dist/commands/teams-scope.js +109 -0
- package/dist/commands/teams-scope.js.map +1 -0
- package/dist/commands/teams.d.ts +64 -7
- package/dist/commands/teams.js +254 -60
- package/dist/commands/teams.js.map +1 -1
- package/dist/commands/upgrade.js +12 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/use.js +2 -2
- package/dist/commands/use.js.map +1 -1
- package/dist/commands/whoami.js +1 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/connections-api.js +1 -1
- package/dist/connections-api.js.map +1 -1
- package/dist/delegate.js +11 -4
- package/dist/delegate.js.map +1 -1
- package/dist/harness-scaffold.js +12 -0
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +724 -152
- package/dist/index.js.map +1 -1
- package/dist/knowledge-api.d.ts +175 -0
- package/dist/knowledge-api.js +169 -0
- package/dist/knowledge-api.js.map +1 -0
- package/dist/open-url.d.ts +16 -0
- package/dist/open-url.js +73 -0
- package/dist/open-url.js.map +1 -0
- package/dist/parse-argv.d.ts +2 -0
- package/dist/parse-argv.js.map +1 -1
- package/dist/publish-api.d.ts +2 -1
- package/dist/publish-api.js +4 -3
- package/dist/publish-api.js.map +1 -1
- package/dist/pull-write.d.ts +27 -0
- package/dist/pull-write.js +60 -0
- package/dist/pull-write.js.map +1 -0
- package/dist/routes-api.d.ts +3 -0
- package/dist/routes-api.js +9 -4
- package/dist/routes-api.js.map +1 -1
- package/dist/runs-api.d.ts +7 -1
- package/dist/runs-api.js +21 -6
- package/dist/runs-api.js.map +1 -1
- package/dist/safe-fs.d.ts +2 -0
- package/dist/safe-fs.js +12 -6
- package/dist/safe-fs.js.map +1 -1
- package/dist/secret-file.d.ts +17 -0
- package/dist/secret-file.js +69 -0
- package/dist/secret-file.js.map +1 -0
- package/dist/skills-api.d.ts +10 -2
- package/dist/skills-api.js +37 -2
- package/dist/skills-api.js.map +1 -1
- package/dist/source-products-api.d.ts +63 -0
- package/dist/source-products-api.js +123 -0
- package/dist/source-products-api.js.map +1 -0
- package/dist/source-target.js +2 -1
- package/dist/source-target.js.map +1 -1
- package/dist/sources-api.d.ts +2 -1
- package/dist/sources-api.js +3 -2
- package/dist/sources-api.js.map +1 -1
- package/dist/team-connect-api.d.ts +286 -0
- package/dist/team-connect-api.js +107 -0
- package/dist/team-connect-api.js.map +1 -0
- package/dist/teams-api.d.ts +42 -1
- package/dist/teams-api.js +98 -20
- package/dist/teams-api.js.map +1 -1
- package/dist/toolchain-api.d.ts +9 -0
- package/dist/toolchain-api.js +17 -2
- package/dist/toolchain-api.js.map +1 -1
- package/dist/toolchain-resolve.d.ts +3 -0
- package/dist/toolchain-resolve.js +15 -1
- package/dist/toolchain-resolve.js.map +1 -1
- package/dist/toolchain.d.ts +6 -0
- package/dist/toolchain.js +19 -0
- package/dist/toolchain.js.map +1 -1
- package/dist/update-notice.js +2 -1
- package/dist/update-notice.js.map +1 -1
- package/package.json +2 -2
- package/templates/AGENTS.md.tmpl +6 -1
- package/templates/claude-skill.md.tmpl +5 -0
- package/templates/package.json.tmpl +4 -4
- package/templates/skills/build-agent/SKILL.md +115 -16
- package/templates/skills/build-agent/references/agent-json.md +26 -8
- package/templates/skills/build-agent/references/app-setup.md +2 -1
- package/templates/skills/build-agent/references/rollout.md +27 -4
- package/templates/skills/build-agent/references/set-up-an-integration.md +74 -15
- package/templates/skills/build-agent/references/skills.md +20 -1
- package/templates/skills/build-agent/references/triggers.md +84 -20
- package/templates/skills/build-integration/references/widget-design.md +3 -3
- package/templates/skills/ekanos-platform/SKILL.md +70 -17
- package/templates/skills/polish-widgets/references/patterns.md +2 -2
package/dist/teams-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TeamAppSetResultSchema, TeamCreateResultSchema, TeamMemberChangeResultSchema, TeamMemberInviteResultSchema, TeamMemberListSchema, TeamRolesSchema, TeamIntegrationStatusSchema, TeamListSchema, TeamWebhookMintResultSchema, } from '@ekanos/integration-schema';
|
|
1
|
+
import { ProductScopeListSchema, ProductScopeProductChangeSchema, TeamScopesSchema, TeamScopesSetResultSchema, TeamAppSetResultSchema, TeamCreateResultSchema, TeamDisconnectResultSchema, TeamMemberChangeResultSchema, TeamMemberInviteResultSchema, TeamMemberListSchema, TeamRenameResultSchema, TeamRolesSchema, TeamIntegrationStatusSchema, TeamIntegrationEventListSchema, TeamListSchema, TeamWebhookMintResultSchema, } from '@ekanos/integration-schema';
|
|
2
2
|
import { request } from './auth/ekanos-api.js';
|
|
3
3
|
import { authRequiredError, forbiddenError, invalidStateError, networkError, notFoundError, usageError, validationError, } from './errors.js';
|
|
4
4
|
export function listTeams(host, accessToken, source) {
|
|
@@ -30,6 +30,13 @@ export function mintTeamWebhook(host, accessToken, target, webhookId) {
|
|
|
30
30
|
source: target.source,
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
+
export function disconnectTeamIntegration(host, accessToken, target) {
|
|
34
|
+
return call(host, accessToken, TeamDisconnectResultSchema, {
|
|
35
|
+
method: 'DELETE',
|
|
36
|
+
path: integrationPath(target),
|
|
37
|
+
source: target.source,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
33
40
|
export function createTeam(host, accessToken, source, body) {
|
|
34
41
|
return call(host, accessToken, TeamCreateResultSchema, {
|
|
35
42
|
method: 'POST',
|
|
@@ -38,6 +45,14 @@ export function createTeam(host, accessToken, source, body) {
|
|
|
38
45
|
body,
|
|
39
46
|
});
|
|
40
47
|
}
|
|
48
|
+
export function renameTeam(host, accessToken, target, name) {
|
|
49
|
+
return call(host, accessToken, TeamRenameResultSchema, {
|
|
50
|
+
method: 'PATCH',
|
|
51
|
+
path: `/api/partner/teams/${encodeURIComponent(target.team)}`,
|
|
52
|
+
source: target.source,
|
|
53
|
+
body: { name },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
41
56
|
export function listTeamMembers(host, accessToken, target) {
|
|
42
57
|
return call(host, accessToken, TeamMemberListSchema, {
|
|
43
58
|
method: 'GET',
|
|
@@ -68,6 +83,35 @@ export function removeTeamMember(host, accessToken, target) {
|
|
|
68
83
|
source: target.source,
|
|
69
84
|
});
|
|
70
85
|
}
|
|
86
|
+
export function listProductScopes(host, accessToken, source) {
|
|
87
|
+
return call(host, accessToken, ProductScopeListSchema, {
|
|
88
|
+
method: 'GET',
|
|
89
|
+
path: '/api/partner/scopes',
|
|
90
|
+
source,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export function setScopeProduct(host, accessToken, target, included) {
|
|
94
|
+
return call(host, accessToken, ProductScopeProductChangeSchema, {
|
|
95
|
+
method: included ? 'PUT' : 'DELETE',
|
|
96
|
+
path: `/api/partner/scopes/${encodeURIComponent(target.scope)}/products/${encodeURIComponent(target.product)}`,
|
|
97
|
+
source: target.source,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
export function showTeamScopes(host, accessToken, target) {
|
|
101
|
+
return call(host, accessToken, TeamScopesSchema, {
|
|
102
|
+
method: 'GET',
|
|
103
|
+
path: `/api/partner/teams/${encodeURIComponent(target.team)}/scopes`,
|
|
104
|
+
source: target.source,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
export function setTeamScopes(host, accessToken, target, scopes) {
|
|
108
|
+
return call(host, accessToken, TeamScopesSetResultSchema, {
|
|
109
|
+
method: 'PUT',
|
|
110
|
+
path: `/api/partner/teams/${encodeURIComponent(target.team)}/scopes`,
|
|
111
|
+
source: target.source,
|
|
112
|
+
body: { scopes },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
71
115
|
export function listTeamRoles(host, accessToken, target) {
|
|
72
116
|
return call(host, accessToken, TeamRolesSchema, {
|
|
73
117
|
method: 'GET',
|
|
@@ -75,6 +119,22 @@ export function listTeamRoles(host, accessToken, target) {
|
|
|
75
119
|
source: target.source,
|
|
76
120
|
});
|
|
77
121
|
}
|
|
122
|
+
export function listTeamEvents(host, accessToken, target, filters) {
|
|
123
|
+
const query = new URLSearchParams();
|
|
124
|
+
if (filters.product)
|
|
125
|
+
query.set('product', filters.product);
|
|
126
|
+
if (filters.since)
|
|
127
|
+
query.set('since', filters.since);
|
|
128
|
+
if (filters.limit !== undefined)
|
|
129
|
+
query.set('limit', String(filters.limit));
|
|
130
|
+
const encoded = query.toString();
|
|
131
|
+
const suffix = encoded ? `?${encoded}` : '';
|
|
132
|
+
return call(host, accessToken, TeamIntegrationEventListSchema, {
|
|
133
|
+
method: 'GET',
|
|
134
|
+
path: `/api/partner/teams/${encodeURIComponent(target.team)}/events${suffix}`,
|
|
135
|
+
source: target.source,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
78
138
|
function membersPath(team) {
|
|
79
139
|
return `/api/partner/teams/${encodeURIComponent(team)}/members`;
|
|
80
140
|
}
|
|
@@ -83,7 +143,7 @@ function integrationPath(target) {
|
|
|
83
143
|
}
|
|
84
144
|
/** One partner teams call; shared by the team-agent clients (`team-agents-api.ts`). */
|
|
85
145
|
export async function call(host, accessToken, schema, init) {
|
|
86
|
-
const response = await request(`${host}${init.path}
|
|
146
|
+
const response = await request(`${host}${init.path}${init.path.includes('?') ? '&' : '?'}${new URLSearchParams({ source: init.source })}`, {
|
|
87
147
|
method: init.method,
|
|
88
148
|
headers: Object.assign({ accept: 'application/json', authorization: `Bearer ${accessToken}` }, (init.body === undefined
|
|
89
149
|
? {}
|
|
@@ -103,22 +163,30 @@ export async function call(host, accessToken, schema, init) {
|
|
|
103
163
|
if (response.status === 403) {
|
|
104
164
|
throw forbiddenError(message !== null && message !== void 0 ? message : 'Not permitted on this Source.', code === 'SOURCE_ADMIN_REQUIRED'
|
|
105
165
|
? "Only a Source admin manages its teams' integrations. Ask one to run it."
|
|
106
|
-
:
|
|
166
|
+
: code === 'NOT_RUNS_AS'
|
|
167
|
+
? 'Consent is personal: the person it runs as gives it, signed in as themselves. "ekanos teams agents list <team>" shows who that is.'
|
|
168
|
+
: code === 'AGENTS_MANAGE_REQUIRED'
|
|
169
|
+
? 'Ask a team owner for a role that manages agents, then confirm again.'
|
|
170
|
+
: 'Run "ekanos sources" to list the sources you hold a seat on.');
|
|
107
171
|
}
|
|
108
172
|
if (response.status === 404) {
|
|
109
173
|
throw notFoundError(message !== null && message !== void 0 ? message : 'Not found.', code === 'TEAM_NOT_FOUND'
|
|
110
174
|
? 'Run "ekanos teams list" to see the teams on this Source.'
|
|
111
175
|
: code === 'PRODUCT_NOT_ENTITLED'
|
|
112
|
-
? '"ekanos
|
|
113
|
-
: code === '
|
|
114
|
-
? 'Run "ekanos
|
|
115
|
-
: code === '
|
|
116
|
-
? 'Run "ekanos teams
|
|
117
|
-
:
|
|
118
|
-
? 'Run "ekanos teams
|
|
119
|
-
:
|
|
120
|
-
? '
|
|
121
|
-
:
|
|
176
|
+
? '"ekanos sources products" shows what this Source has; only a super-admin enables a product for a Source (in /admin).'
|
|
177
|
+
: code === 'SCOPE_NOT_FOUND' || code === 'PRODUCT_NOT_IN_SCOPE'
|
|
178
|
+
? 'Run "ekanos sources scopes" to see the Source\'s product scopes and their products.'
|
|
179
|
+
: code === 'WEBHOOK_NOT_FOUND' || code === 'CONNECTION_NOT_FOUND'
|
|
180
|
+
? 'Run "ekanos teams show <team> <product>" to see its team apps and webhooks.'
|
|
181
|
+
: code === 'MEMBER_NOT_FOUND'
|
|
182
|
+
? 'Run "ekanos teams members list <team>" to see its members and invitations.'
|
|
183
|
+
: code === 'SKILL_NOT_FOUND'
|
|
184
|
+
? 'Run "ekanos skills list --team <team>" (and "ekanos skills list") to see the skills this team can use.'
|
|
185
|
+
: (code === null || code === void 0 ? void 0 : code.startsWith('AGENT_')) || code === 'TEMPLATE_NOT_FOUND'
|
|
186
|
+
? 'Run "ekanos teams agents list <team>" to see the team\'s agents.'
|
|
187
|
+
: /^(TRIGGER|LINK|MCP)_/.test(code !== null && code !== void 0 ? code : '')
|
|
188
|
+
? 'List them first: "ekanos teams triggers|links|mcp list <team> ..."'
|
|
189
|
+
: 'Run "ekanos sources" to list the sources you hold a seat on.');
|
|
122
190
|
}
|
|
123
191
|
if (response.status === 409 && code === 'NO_TEAM_APP') {
|
|
124
192
|
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>".');
|
|
@@ -126,13 +194,23 @@ export async function call(host, accessToken, schema, init) {
|
|
|
126
194
|
if (response.status === 409 || response.status === 422) {
|
|
127
195
|
throw invalidStateError(message !== null && message !== void 0 ? message : 'The host refused the change.', code === 'SLUG_TAKEN'
|
|
128
196
|
? 'Pick another with --slug, or run "ekanos teams list" to see the teams.'
|
|
129
|
-
: code === '
|
|
130
|
-
? '
|
|
131
|
-
: code === '
|
|
132
|
-
? "
|
|
133
|
-
: code === '
|
|
134
|
-
? '
|
|
135
|
-
:
|
|
197
|
+
: code === 'AGENT_VERSION_CHANGED'
|
|
198
|
+
? 'A newer version was published since it was shown to you. Review it with "ekanos teams agents show <team> <key>", then run "ekanos teams agents confirm" again.'
|
|
199
|
+
: code === 'ALREADY_MEMBER' || code === 'ALREADY_INVITED'
|
|
200
|
+
? 'Run "ekanos teams members list <team>"; change a role with "ekanos teams members role <team> <email> <role>".'
|
|
201
|
+
: code === 'PRIMARY_OWNER'
|
|
202
|
+
? "The primary owner's ownership is transferred in the app (team Settings → Members)."
|
|
203
|
+
: code === 'TEAM_ACCOUNTS_DISABLED'
|
|
204
|
+
? 'Ekanos switches team accounts on for a Source; ask a super-admin.'
|
|
205
|
+
: code === 'PERSONAL_CONNECTION'
|
|
206
|
+
? "The member who made it disconnects it on the team's Integrations page in the app."
|
|
207
|
+
: code === 'MCP_SERVER_FROM_TEMPLATE'
|
|
208
|
+
? 'It stays while the agent template declares it: change the template, apply and publish.'
|
|
209
|
+
: code === 'PRODUCT_NOT_ENTITLED'
|
|
210
|
+
? '"ekanos sources products" shows what this Source has; only those can be in its scopes.'
|
|
211
|
+
: code === 'TEMPLATE_ARCHIVED'
|
|
212
|
+
? 'Unarchive it first: "ekanos agents unarchive <key>".'
|
|
213
|
+
: 'See "ekanos teams --help".');
|
|
136
214
|
}
|
|
137
215
|
if (response.status === 400 && code === 'UNKNOWN_ROLE') {
|
|
138
216
|
throw validationError(message !== null && message !== void 0 ? message : 'No such role.', 'Run "ekanos teams roles list <team>" for the roles this Source uses.');
|
package/dist/teams-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams-api.js","sourceRoot":"","sources":["../src/teams-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type TeamAppSetResult,\n TeamAppSetResultSchema,\n type 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":"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","sourcesContent":["import {\n type ProductScopeList,\n ProductScopeListSchema,\n type ProductScopeProductChange,\n ProductScopeProductChangeSchema,\n type TeamScopes,\n TeamScopesSchema,\n type TeamScopesSetResult,\n TeamScopesSetResultSchema,\n type TeamAppSetResult,\n TeamAppSetResultSchema,\n type TeamCreateRequest,\n type TeamCreateResult,\n TeamCreateResultSchema,\n type TeamDisconnectResult,\n TeamDisconnectResultSchema,\n type TeamMemberChangeResult,\n TeamMemberChangeResultSchema,\n type TeamMemberInviteResult,\n TeamMemberInviteResultSchema,\n type TeamMemberList,\n TeamMemberListSchema,\n type TeamRenameResult,\n TeamRenameResultSchema,\n type TeamRoles,\n TeamRolesSchema,\n type TeamIntegrationStatus,\n TeamIntegrationStatusSchema,\n type TeamIntegrationEventList,\n TeamIntegrationEventListSchema,\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 * DELETE /api/partner/teams/<team>/integrations/<product>?source=<slug> → TeamDisconnectResult\n * POST /api/partner/teams?source=<slug> { name, slug?, ownerEmail? } → TeamCreateResult\n * PATCH /api/partner/teams/<team>?source=<slug> { name } → TeamRenameResult\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 * GET /api/partner/teams/<team>/events?source=<slug>[&product=<slug>]\n * [&since=<iso>][&limit=<n>] → TeamIntegrationEventList\n * GET /api/partner/scopes?source=<slug> → ProductScopeList\n * PUT | DELETE /api/partner/scopes/<scope>/products/<product>?source=<slug>\n * → ProductScopeProductChange\n * GET /api/partner/teams/<team>/scopes?source=<slug> → TeamScopes\n * PUT /api/partner/teams/<team>/scopes?source=<slug> { scopes } → TeamScopesSetResult\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 disconnectTeamIntegration(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n): Promise<TeamsApiResult<TeamDisconnectResult>> {\n return call(host, accessToken, TeamDisconnectResultSchema, {\n method: 'DELETE',\n path: integrationPath(target),\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 renameTeam(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n name: string,\n): Promise<TeamsApiResult<TeamRenameResult>> {\n return call(host, accessToken, TeamRenameResultSchema, {\n method: 'PATCH',\n path: `/api/partner/teams/${encodeURIComponent(target.team)}`,\n source: target.source,\n body: { name },\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 listProductScopes(\n host: string,\n accessToken: string,\n source: string,\n): Promise<TeamsApiResult<ProductScopeList>> {\n return call(host, accessToken, ProductScopeListSchema, {\n method: 'GET',\n path: '/api/partner/scopes',\n source,\n });\n}\n\nexport function setScopeProduct(\n host: string,\n accessToken: string,\n target: { source: string; scope: string; product: string },\n included: boolean,\n): Promise<TeamsApiResult<ProductScopeProductChange>> {\n return call(host, accessToken, ProductScopeProductChangeSchema, {\n method: included ? 'PUT' : 'DELETE',\n path: `/api/partner/scopes/${encodeURIComponent(target.scope)}/products/${encodeURIComponent(target.product)}`,\n source: target.source,\n });\n}\n\nexport function showTeamScopes(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n): Promise<TeamsApiResult<TeamScopes>> {\n return call(host, accessToken, TeamScopesSchema, {\n method: 'GET',\n path: `/api/partner/teams/${encodeURIComponent(target.team)}/scopes`,\n source: target.source,\n });\n}\n\nexport function setTeamScopes(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n scopes: string[],\n): Promise<TeamsApiResult<TeamScopesSetResult>> {\n return call(host, accessToken, TeamScopesSetResultSchema, {\n method: 'PUT',\n path: `/api/partner/teams/${encodeURIComponent(target.team)}/scopes`,\n source: target.source,\n body: { scopes },\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\nexport function listTeamEvents(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n filters: { product?: string; since?: string; limit?: number },\n): Promise<TeamsApiResult<TeamIntegrationEventList>> {\n const query = new URLSearchParams();\n if (filters.product) query.set('product', filters.product);\n if (filters.since) query.set('since', filters.since);\n if (filters.limit !== undefined) query.set('limit', String(filters.limit));\n const encoded = query.toString();\n const suffix = encoded ? `?${encoded}` : '';\n return call(host, accessToken, TeamIntegrationEventListSchema, {\n method: 'GET',\n path: `/api/partner/teams/${encodeURIComponent(target.team)}/events${suffix}`,\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 : code === 'NOT_RUNS_AS'\n ? 'Consent is personal: the person it runs as gives it, signed in as themselves. \"ekanos teams agents list <team>\" shows who that is.'\n : code === 'AGENTS_MANAGE_REQUIRED'\n ? 'Ask a team owner for a role that manages agents, then confirm again.'\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 sources products\" shows what this Source has; only a super-admin enables a product for a Source (in /admin).'\n : code === 'SCOPE_NOT_FOUND' || code === 'PRODUCT_NOT_IN_SCOPE'\n ? 'Run \"ekanos sources scopes\" to see the Source\\'s product scopes and their products.'\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 === 'SKILL_NOT_FOUND'\n ? 'Run \"ekanos skills list --team <team>\" (and \"ekanos skills list\") to see the skills this team can use.'\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 === 'AGENT_VERSION_CHANGED'\n ? 'A newer version was published since it was shown to you. Review it with \"ekanos teams agents show <team> <key>\", then run \"ekanos teams agents confirm\" again.'\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 : code === 'PERSONAL_CONNECTION'\n ? \"The member who made it disconnects it on the team's Integrations page in the app.\"\n : code === 'MCP_SERVER_FROM_TEMPLATE'\n ? 'It stays while the agent template declares it: change the template, apply and publish.'\n : code === 'PRODUCT_NOT_ENTITLED'\n ? '\"ekanos sources products\" shows what this Source has; only those can be in its scopes.'\n : code === 'TEMPLATE_ARCHIVED'\n ? 'Unarchive it first: \"ekanos agents unarchive <key>\".'\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/dist/toolchain-api.d.ts
CHANGED
|
@@ -38,6 +38,12 @@ import { type ToolchainNote, type ToolchainVersions } from './toolchain.js';
|
|
|
38
38
|
*
|
|
39
39
|
* Unknown extra fields in the response are tolerated (only the fields above
|
|
40
40
|
* are read); a missing `notes` defaults to an empty array.
|
|
41
|
+
*
|
|
42
|
+
* The one exception to "never throws, always falls back": a version that is
|
|
43
|
+
* present but is not an exact semver (`1.2.3`, `1.2.3-rc.1`) is `invalid`, not
|
|
44
|
+
* `unavailable`. Each version becomes a `pnpm add -E` spec, so a value like
|
|
45
|
+
* `github:owner/repo` is a host trying to install code; that is refused
|
|
46
|
+
* outright rather than quietly papered over by the npm fallback.
|
|
41
47
|
* ---------------------------------------------------------------------------
|
|
42
48
|
*/
|
|
43
49
|
export interface ToolchainReport {
|
|
@@ -48,6 +54,9 @@ export interface ToolchainReport {
|
|
|
48
54
|
export type FetchToolchainResult = {
|
|
49
55
|
status: 'ok';
|
|
50
56
|
report: ToolchainReport;
|
|
57
|
+
} | {
|
|
58
|
+
status: 'invalid';
|
|
59
|
+
detail: string;
|
|
51
60
|
} | {
|
|
52
61
|
status: 'unavailable';
|
|
53
62
|
};
|
package/dist/toolchain-api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { request } from './auth/ekanos-api.js';
|
|
2
|
-
import { TOOLCHAIN_PACKAGES, } from './toolchain.js';
|
|
2
|
+
import { TOOLCHAIN_PACKAGES, firstInexactVersion, } from './toolchain.js';
|
|
3
3
|
export async function fetchToolchainReport(host, accessToken) {
|
|
4
4
|
try {
|
|
5
5
|
const response = await request(`${host}/api/partner/toolchain`, {
|
|
@@ -13,7 +13,11 @@ export async function fetchToolchainReport(host, accessToken) {
|
|
|
13
13
|
return { status: 'unavailable' };
|
|
14
14
|
const body = await response.json();
|
|
15
15
|
const report = parseReport(body);
|
|
16
|
-
|
|
16
|
+
if (report === null)
|
|
17
|
+
return { status: 'unavailable' };
|
|
18
|
+
if ('invalid' in report)
|
|
19
|
+
return { status: 'invalid', detail: report.invalid };
|
|
20
|
+
return { status: 'ok', report };
|
|
17
21
|
}
|
|
18
22
|
catch (_a) {
|
|
19
23
|
// Network failure, timeout, or a body that is not even JSON — all read
|
|
@@ -31,6 +35,17 @@ function parseReport(body) {
|
|
|
31
35
|
const minimum = parseVersions(data.minimum);
|
|
32
36
|
if (!current || !minimum)
|
|
33
37
|
return null;
|
|
38
|
+
for (const [label, versions] of [
|
|
39
|
+
['current', current],
|
|
40
|
+
['minimum', minimum],
|
|
41
|
+
]) {
|
|
42
|
+
const bad = firstInexactVersion(versions);
|
|
43
|
+
if (bad) {
|
|
44
|
+
return {
|
|
45
|
+
invalid: `the host's ${label} ${TOOLCHAIN_PACKAGES[bad.key]} is ${JSON.stringify(bad.value)}, which is not an exact version`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
34
49
|
const rawNotes = data.notes;
|
|
35
50
|
const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];
|
|
36
51
|
return { current, minimum, notes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolchain-api.js","sourceRoot":"","sources":["../src/toolchain-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EACL,kBAAkB,
|
|
1
|
+
{"version":3,"file":"toolchain-api.js","sourceRoot":"","sources":["../src/toolchain-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { request } from './auth/ekanos-api';\nimport {\n TOOLCHAIN_PACKAGES,\n type ToolchainNote,\n type ToolchainVersions,\n firstInexactVersion,\n} from './toolchain';\n\n/**\n * The CLI's client for the Ekanos toolchain-advisory route.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the seam between this package and the Ekanos web app.\n *\n * The server side is the host's `/api/partner/toolchain` route and ships on\n * a DIFFERENT BRANCH from this file, same as every other route in\n * `auth/ekanos-api.ts` and `publish-api.ts`. Nothing here imports host code;\n * the two halves are coupled only by HTTP, and the tests stub `fetch`,\n * never the route.\n *\n * GET /api/partner/toolchain [Authorization: Bearer <access_token>]\n * → 200 { ok: true, data: {\n * current: { cli, sdk, ui, integrationSchema, harness }, // the\n * // coherent\n * // set the\n * // host\n * // recommends\n * minimum: { cli, sdk, ui, integrationSchema, harness }, // below\n * // this the\n * // host\n * // refuses\n * notes?: [{ level: \"warn\" | \"info\", message: string }],\n * } }\n * → any other status, or a body that fails to parse or match this shape\n *\n * This endpoint is treated as ADVISORY, not authoritative in the way the auth\n * and submission routes are: a 404 (an older Ekanos deployment that predates\n * this route) or a 501 (deliberately unimplemented) are both entirely\n * ordinary, and a 4xx/5xx/network failure/malformed body all mean exactly the\n * same thing to every caller — \"the endpoint is unavailable, fall back to the\n * npm registry\" — so this module never throws. There is no not-configured\n * distinction to make here the way `cli-config` has: a caller with no way to\n * reach this route at all (no host, no session) never calls it in the first\n * place; see `toolchain-resolve.ts`.\n *\n * Unknown extra fields in the response are tolerated (only the fields above\n * are read); a missing `notes` defaults to an empty array.\n *\n * The one exception to \"never throws, always falls back\": a version that is\n * present but is not an exact semver (`1.2.3`, `1.2.3-rc.1`) is `invalid`, not\n * `unavailable`. Each version becomes a `pnpm add -E` spec, so a value like\n * `github:owner/repo` is a host trying to install code; that is refused\n * outright rather than quietly papered over by the npm fallback.\n * ---------------------------------------------------------------------------\n */\n\nexport interface ToolchainReport {\n current: ToolchainVersions;\n minimum: ToolchainVersions;\n notes: ToolchainNote[];\n}\n\nexport type FetchToolchainResult =\n | { status: 'ok'; report: ToolchainReport }\n | { status: 'invalid'; detail: string }\n | { status: 'unavailable' };\n\nexport async function fetchToolchainReport(\n host: string,\n accessToken: string,\n): Promise<FetchToolchainResult> {\n try {\n const response = await request(`${host}/api/partner/toolchain`, {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) return { status: 'unavailable' };\n\n const body: unknown = await response.json();\n const report = parseReport(body);\n\n if (report === null) return { status: 'unavailable' };\n if ('invalid' in report)\n return { status: 'invalid', detail: report.invalid };\n return { status: 'ok', report };\n } catch {\n // Network failure, timeout, or a body that is not even JSON — all read\n // the same: the endpoint is unavailable this time, fall back to npm.\n return { status: 'unavailable' };\n }\n}\n\nfunction parseReport(\n body: unknown,\n): ToolchainReport | { invalid: string } | null {\n if (typeof body !== 'object' || body === null) return null;\n const data = (body as { data?: unknown }).data;\n if (typeof data !== 'object' || data === null) return null;\n\n const current = parseVersions((data as { current?: unknown }).current);\n const minimum = parseVersions((data as { minimum?: unknown }).minimum);\n if (!current || !minimum) return null;\n\n for (const [label, versions] of [\n ['current', current],\n ['minimum', minimum],\n ] as const) {\n const bad = firstInexactVersion(versions);\n if (bad) {\n return {\n invalid: `the host's ${label} ${TOOLCHAIN_PACKAGES[bad.key]} is ${JSON.stringify(bad.value)}, which is not an exact version`,\n };\n }\n }\n\n const rawNotes = (data as { notes?: unknown }).notes;\n const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];\n\n return { current, minimum, notes };\n}\n\nfunction parseVersions(value: unknown): ToolchainVersions | null {\n if (typeof value !== 'object' || value === null) return null;\n const record = value as Record<string, unknown>;\n const result: Partial<ToolchainVersions> = {};\n\n for (const key of Object.keys(\n TOOLCHAIN_PACKAGES,\n ) as (keyof ToolchainVersions)[]) {\n const raw = record[key];\n if (typeof raw !== 'string' || raw.length === 0) return null;\n result[key] = raw;\n }\n\n return result as ToolchainVersions;\n}\n\nfunction isNote(value: unknown): value is ToolchainNote {\n if (typeof value !== 'object' || value === null) return false;\n const note = value as { level?: unknown; message?: unknown };\n return (\n (note.level === 'warn' || note.level === 'info') &&\n typeof note.message === 'string'\n );\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TOOLCHAIN_PACKAGES, } from './toolchain.js';
|
|
1
|
+
import { TOOLCHAIN_PACKAGES, firstInexactVersion, } from './toolchain.js';
|
|
2
2
|
import { fetchToolchainReport } from './toolchain-api.js';
|
|
3
3
|
/**
|
|
4
4
|
* The single place that answers "what should this toolchain be at?" — shared
|
|
@@ -19,6 +19,11 @@ import { fetchToolchainReport } from './toolchain-api.js';
|
|
|
19
19
|
* can produce a set that literally cannot install together. Refusing is
|
|
20
20
|
* the only safe response to a set caught in that state; the failure is
|
|
21
21
|
* obvious and self-resolves the moment the publish finishes.
|
|
22
|
+
*
|
|
23
|
+
* Either way every version must be EXACT (`1.2.3`, `1.2.3-rc.1`): each one
|
|
24
|
+
* becomes a `pnpm add -E` spec, so a host or dist-tag answering
|
|
25
|
+
* `github:owner/repo` or a tarball URL resolves to `invalid` and installs
|
|
26
|
+
* nothing.
|
|
22
27
|
*/
|
|
23
28
|
const REGISTRY_BASE = 'https://registry.npmjs.org';
|
|
24
29
|
/** npm registry package URLs encode the scope's `/` but not its `@`. */
|
|
@@ -40,6 +45,8 @@ export async function resolveToolchainTarget(params) {
|
|
|
40
45
|
notes: hostResult.report.notes,
|
|
41
46
|
};
|
|
42
47
|
}
|
|
48
|
+
if (hostResult.status === 'invalid')
|
|
49
|
+
return hostResult;
|
|
43
50
|
// 404 / 501 / network failure / malformed body: fall through to npm.
|
|
44
51
|
}
|
|
45
52
|
return resolveFromNpmRegistry(params.timeoutMs);
|
|
@@ -57,6 +64,13 @@ async function resolveFromNpmRegistry(timeoutMs) {
|
|
|
57
64
|
if (Object.values(versions).some((version) => version === null)) {
|
|
58
65
|
return { status: 'unavailable' };
|
|
59
66
|
}
|
|
67
|
+
const inexact = firstInexactVersion(versions);
|
|
68
|
+
if (inexact) {
|
|
69
|
+
return {
|
|
70
|
+
status: 'invalid',
|
|
71
|
+
detail: `npm's latest ${TOOLCHAIN_PACKAGES[inexact.key]} is ${JSON.stringify(inexact.value)}, which is not an exact version`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
60
74
|
const target = versions;
|
|
61
75
|
const trio = [target.sdk, target.ui, target.integrationSchema];
|
|
62
76
|
if (new Set(trio).size !== 1) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolchain-resolve.js","sourceRoot":"","sources":["../src/toolchain-resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,
|
|
1
|
+
{"version":3,"file":"toolchain-resolve.js","sourceRoot":"","sources":["../src/toolchain-resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n TOOLCHAIN_PACKAGES,\n type ToolchainKey,\n type ToolchainNote,\n type ToolchainVersions,\n firstInexactVersion,\n} from './toolchain';\nimport { fetchToolchainReport } from './toolchain-api';\n\n/**\n * The single place that answers \"what should this toolchain be at?\" — shared\n * by `ekanos upgrade` (which installs the answer) and the proactive update\n * notice (which only reports it). Both need the exact same coherent target,\n * so there is exactly one resolution path, tried in this order:\n *\n * 1. `GET /api/partner/toolchain` on the resolved host, when both a host and\n * a session are available. Whatever the host returns is trusted as\n * already-coherent — it is the operator's own recommendation.\n * 2. The npm registry's `dist-tags.latest` for all five packages, with an\n * explicit COHERENCE check before ever calling it a \"target\": the\n * `@ekanos/sdk` / `@ekanos/ui` / `@ekanos/integration-schema` trio must\n * resolve to one identical version, and the `@ekanos/sdk` release at that\n * version must declare the exact same `@ekanos/integration-schema`\n * dependency. A multi-package publish is not atomic — this repo hit that\n * exact window mid-publish — so assembling \"latest of each, independently\"\n * can produce a set that literally cannot install together. Refusing is\n * the only safe response to a set caught in that state; the failure is\n * obvious and self-resolves the moment the publish finishes.\n *\n * Either way every version must be EXACT (`1.2.3`, `1.2.3-rc.1`): each one\n * becomes a `pnpm add -E` spec, so a host or dist-tag answering\n * `github:owner/repo` or a tarball URL resolves to `invalid` and installs\n * nothing.\n */\n\nconst REGISTRY_BASE = 'https://registry.npmjs.org';\n\n/** npm registry package URLs encode the scope's `/` but not its `@`. */\nfunction registryPackageUrl(pkg: string, version?: string): string {\n const encoded = encodeURIComponent(pkg).replace('%40', '@');\n return version\n ? `${REGISTRY_BASE}/${encoded}/${encodeURIComponent(version)}`\n : `${REGISTRY_BASE}/${encoded}`;\n}\n\nexport type ToolchainResolution =\n | {\n status: 'ok';\n source: 'host';\n target: ToolchainVersions;\n minimum: ToolchainVersions;\n notes: ToolchainNote[];\n }\n | { status: 'ok'; source: 'npm'; target: ToolchainVersions }\n | { status: 'incoherent'; detail: string }\n | { status: 'invalid'; detail: string }\n | { status: 'unavailable' };\n\nexport interface ResolveToolchainParams {\n host?: string;\n session?: { accessToken: string } | null;\n /** Per-request timeout for the underlying fetches. */\n timeoutMs?: number;\n}\n\nexport async function resolveToolchainTarget(\n params: ResolveToolchainParams,\n): Promise<ToolchainResolution> {\n if (params.host && params.session) {\n const hostResult = await fetchToolchainReport(\n params.host,\n params.session.accessToken,\n );\n\n if (hostResult.status === 'ok') {\n return {\n status: 'ok',\n source: 'host',\n target: hostResult.report.current,\n minimum: hostResult.report.minimum,\n notes: hostResult.report.notes,\n };\n }\n if (hostResult.status === 'invalid') return hostResult;\n\n // 404 / 501 / network failure / malformed body: fall through to npm.\n }\n\n return resolveFromNpmRegistry(params.timeoutMs);\n}\n\nasync function resolveFromNpmRegistry(\n timeoutMs?: number,\n): Promise<ToolchainResolution> {\n const entries = Object.entries(TOOLCHAIN_PACKAGES) as [\n ToolchainKey,\n string,\n ][];\n\n const latests = await Promise.all(\n entries.map(async ([key, pkg]) => {\n const body = await fetchRegistryJson(registryPackageUrl(pkg), timeoutMs);\n const latest = (body as { 'dist-tags'?: { latest?: unknown } } | null)?.[\n 'dist-tags'\n ]?.latest;\n return [key, typeof latest === 'string' ? latest : null] as const;\n }),\n );\n\n const versions = Object.fromEntries(latests) as Record<\n ToolchainKey,\n string | null\n >;\n\n if (Object.values(versions).some((version) => version === null)) {\n return { status: 'unavailable' };\n }\n\n const inexact = firstInexactVersion(versions);\n if (inexact) {\n return {\n status: 'invalid',\n detail: `npm's latest ${TOOLCHAIN_PACKAGES[inexact.key]} is ${JSON.stringify(inexact.value)}, which is not an exact version`,\n };\n }\n\n const target = versions as ToolchainVersions;\n\n const trio = [target.sdk, target.ui, target.integrationSchema];\n if (new Set(trio).size !== 1) {\n return {\n status: 'incoherent',\n detail:\n `the npm registry reports mismatched versions for the sdk/ui/schema ` +\n `trio: @ekanos/sdk@${target.sdk}, @ekanos/ui@${target.ui}, ` +\n `@ekanos/integration-schema@${target.integrationSchema}. This is ` +\n `usually a multi-package publish still in flight — retry shortly.`,\n };\n }\n\n const sdkManifest = await fetchRegistryJson(\n registryPackageUrl(TOOLCHAIN_PACKAGES.sdk, target.sdk),\n timeoutMs,\n );\n const sdkPinnedSchema = (\n sdkManifest as { dependencies?: Record<string, unknown> } | null\n )?.dependencies?.['@ekanos/integration-schema'];\n\n if (\n typeof sdkPinnedSchema !== 'string' ||\n sdkPinnedSchema !== target.integrationSchema\n ) {\n return {\n status: 'incoherent',\n detail:\n `@ekanos/sdk@${target.sdk} on npm depends on ` +\n `@ekanos/integration-schema@${String(sdkPinnedSchema)}, which does ` +\n `not match the published @ekanos/integration-schema@` +\n `${target.integrationSchema}. This is usually a multi-package ` +\n `publish still in flight — retry shortly.`,\n };\n }\n\n return { status: 'ok', source: 'npm', target };\n}\n\nasync function fetchRegistryJson(\n url: string,\n timeoutMs?: number,\n): Promise<unknown | null> {\n try {\n const response = await fetch(url, {\n headers: { accept: 'application/json' },\n signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined,\n });\n if (!response.ok) return null;\n return await response.json();\n } catch {\n return null;\n }\n}\n"]}
|
package/dist/toolchain.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ export declare const TOOLCHAIN_PACKAGES: {
|
|
|
15
15
|
};
|
|
16
16
|
export type ToolchainKey = keyof typeof TOOLCHAIN_PACKAGES;
|
|
17
17
|
export type ToolchainVersions = Record<ToolchainKey, string>;
|
|
18
|
+
export declare function isExactVersion(value: unknown): value is string;
|
|
19
|
+
/** The first toolchain entry that is not an exact version, if any. */
|
|
20
|
+
export declare function firstInexactVersion(versions: Record<ToolchainKey, unknown>): {
|
|
21
|
+
key: ToolchainKey;
|
|
22
|
+
value: unknown;
|
|
23
|
+
} | null;
|
|
18
24
|
export interface ToolchainNote {
|
|
19
25
|
level: 'warn' | 'info';
|
|
20
26
|
message: string;
|
package/dist/toolchain.js
CHANGED
|
@@ -13,4 +13,23 @@ export const TOOLCHAIN_PACKAGES = {
|
|
|
13
13
|
integrationSchema: '@ekanos/integration-schema',
|
|
14
14
|
harness: '@ekanos/harness',
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* An exact published version — `1.2.3` or `1.2.3-rc.1`, nothing else. Every
|
|
18
|
+
* toolchain version comes from a server (the host's advisory route or npm's
|
|
19
|
+
* `dist-tags`) and ends up as `pnpm add -E <pkg>@<version>`, where a spec like
|
|
20
|
+
* `github:owner/repo`, a tarball URL or `file:../x` would install arbitrary
|
|
21
|
+
* code. So a version is accepted only in this shape.
|
|
22
|
+
*/
|
|
23
|
+
const EXACT_VERSION = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
24
|
+
export function isExactVersion(value) {
|
|
25
|
+
return typeof value === 'string' && EXACT_VERSION.test(value);
|
|
26
|
+
}
|
|
27
|
+
/** The first toolchain entry that is not an exact version, if any. */
|
|
28
|
+
export function firstInexactVersion(versions) {
|
|
29
|
+
for (const key of Object.keys(TOOLCHAIN_PACKAGES)) {
|
|
30
|
+
if (!isExactVersion(versions[key]))
|
|
31
|
+
return { key, value: versions[key] };
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
16
35
|
//# sourceMappingURL=toolchain.js.map
|
package/dist/toolchain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolchain.js","sourceRoot":"","sources":["../src/toolchain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,GAAG,EAAE,aAAa;IAClB,GAAG,EAAE,aAAa;IAClB,EAAE,EAAE,YAAY;IAChB,iBAAiB,EAAE,4BAA4B;IAC/C,OAAO,EAAE,iBAAiB;CAClB,CAAC","sourcesContent":["/**\n * The five packages that make up \"the toolchain\" — the CLI plus the four\n * packages a partner's project itself installs. `ekanos upgrade` and the\n * proactive update notice both reason about this exact set, from two\n * different angles: upgrade moves a PROJECT to a coherent target, the notice\n * tells a partner the CLI itself is behind. One shared vocabulary so the two\n * features cannot drift apart on what \"the toolchain\" even means.\n */\nexport const TOOLCHAIN_PACKAGES = {\n cli: '@ekanos/cli',\n sdk: '@ekanos/sdk',\n ui: '@ekanos/ui',\n integrationSchema: '@ekanos/integration-schema',\n harness: '@ekanos/harness',\n} as const;\n\nexport type ToolchainKey = keyof typeof TOOLCHAIN_PACKAGES;\n\nexport type ToolchainVersions = Record<ToolchainKey, string>;\n\nexport interface ToolchainNote {\n level: 'warn' | 'info';\n message: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"toolchain.js","sourceRoot":"","sources":["../src/toolchain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,GAAG,EAAE,aAAa;IAClB,GAAG,EAAE,aAAa;IAClB,EAAE,EAAE,YAAY;IAChB,iBAAiB,EAAE,4BAA4B;IAC/C,OAAO,EAAE,iBAAiB;CAClB,CAAC;AAMX;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,mCAAmC,CAAC;AAE1D,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,mBAAmB,CACjC,QAAuC;IAEvC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAmB,EAAE,CAAC;QACpE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["/**\n * The five packages that make up \"the toolchain\" — the CLI plus the four\n * packages a partner's project itself installs. `ekanos upgrade` and the\n * proactive update notice both reason about this exact set, from two\n * different angles: upgrade moves a PROJECT to a coherent target, the notice\n * tells a partner the CLI itself is behind. One shared vocabulary so the two\n * features cannot drift apart on what \"the toolchain\" even means.\n */\nexport const TOOLCHAIN_PACKAGES = {\n cli: '@ekanos/cli',\n sdk: '@ekanos/sdk',\n ui: '@ekanos/ui',\n integrationSchema: '@ekanos/integration-schema',\n harness: '@ekanos/harness',\n} as const;\n\nexport type ToolchainKey = keyof typeof TOOLCHAIN_PACKAGES;\n\nexport type ToolchainVersions = Record<ToolchainKey, string>;\n\n/**\n * An exact published version — `1.2.3` or `1.2.3-rc.1`, nothing else. Every\n * toolchain version comes from a server (the host's advisory route or npm's\n * `dist-tags`) and ends up as `pnpm add -E <pkg>@<version>`, where a spec like\n * `github:owner/repo`, a tarball URL or `file:../x` would install arbitrary\n * code. So a version is accepted only in this shape.\n */\nconst EXACT_VERSION = /^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$/;\n\nexport function isExactVersion(value: unknown): value is string {\n return typeof value === 'string' && EXACT_VERSION.test(value);\n}\n\n/** The first toolchain entry that is not an exact version, if any. */\nexport function firstInexactVersion(\n versions: Record<ToolchainKey, unknown>,\n): { key: ToolchainKey; value: unknown } | null {\n for (const key of Object.keys(TOOLCHAIN_PACKAGES) as ToolchainKey[]) {\n if (!isExactVersion(versions[key])) return { key, value: versions[key] };\n }\n return null;\n}\n\nexport interface ToolchainNote {\n level: 'warn' | 'info';\n message: string;\n}\n"]}
|
package/dist/update-notice.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { CredentialStore, resolveEkanosHome } from './auth/credential-store.js';
|
|
4
|
+
import { isPlaintextRemoteHost } from './auth/host-trust.js';
|
|
4
5
|
import { cliVersion, compareVersions } from './compatibility.js';
|
|
5
6
|
import { resolveToolchainTarget } from './toolchain-resolve.js';
|
|
6
7
|
/**
|
|
@@ -74,7 +75,7 @@ function bestEffortHostSession(env) {
|
|
|
74
75
|
try {
|
|
75
76
|
const store = new CredentialStore({ home: resolveEkanosHome(env) });
|
|
76
77
|
const hosts = store.hosts();
|
|
77
|
-
if (hosts.length !== 1)
|
|
78
|
+
if (hosts.length !== 1 || isPlaintextRemoteHost(hosts[0]))
|
|
78
79
|
return null;
|
|
79
80
|
const session = store.read(hosts[0]);
|
|
80
81
|
return session ? { host: hosts[0], session } : null;
|