@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.
- package/dist/apps-api.d.ts +3 -2
- package/dist/apps-api.js +1 -5
- package/dist/apps-api.js.map +1 -1
- package/dist/commands/agents-edit.js +1 -1
- package/dist/commands/agents-edit.js.map +1 -1
- package/dist/commands/apps.d.ts +13 -4
- package/dist/commands/apps.js +117 -39
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/knowledge.d.ts +37 -0
- package/dist/commands/knowledge.js +364 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/sources.d.ts +4 -0
- package/dist/commands/sources.js +115 -1
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/team-agents-triggers.js +2 -1
- package/dist/commands/team-agents-triggers.js.map +1 -1
- package/dist/commands/teams-connect.d.ts +25 -0
- package/dist/commands/teams-connect.js +242 -0
- package/dist/commands/teams-connect.js.map +1 -0
- package/dist/commands/teams-onboard.d.ts +5 -2
- package/dist/commands/teams-onboard.js +12 -40
- package/dist/commands/teams-onboard.js.map +1 -1
- package/dist/commands/teams.d.ts +60 -5
- package/dist/commands/teams.js +141 -46
- package/dist/commands/teams.js.map +1 -1
- package/dist/index.js +62 -6
- package/dist/index.js.map +1 -1
- package/dist/knowledge-api.d.ts +129 -0
- package/dist/knowledge-api.js +138 -0
- package/dist/knowledge-api.js.map +1 -0
- package/dist/source-products-api.d.ts +59 -0
- package/dist/source-products-api.js +113 -0
- package/dist/source-products-api.js.map +1 -0
- package/dist/team-connect-api.d.ts +244 -0
- package/dist/team-connect-api.js +95 -0
- package/dist/team-connect-api.js.map +1 -0
- package/dist/teams-api.d.ts +1 -0
- package/dist/teams-api.js +1 -1
- package/dist/teams-api.js.map +1 -1
- package/package.json +2 -2
- package/templates/package.json.tmpl +4 -4
- package/templates/skills/build-agent/SKILL.md +14 -1
- package/templates/skills/build-agent/references/agent-json.md +22 -6
- package/templates/skills/build-agent/references/app-setup.md +2 -1
- package/templates/skills/build-agent/references/set-up-an-integration.md +19 -8
- package/templates/skills/build-agent/references/triggers.md +44 -15
- package/templates/skills/ekanos-platform/SKILL.md +32 -6
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { invalidStateError, usageError, validationError } from '../errors.js';
|
|
3
|
+
import { showTeamConnect, startTeamConnect, } from '../team-connect-api.js';
|
|
4
|
+
import { showTeamIntegration } from '../teams-api.js';
|
|
5
|
+
import { promptHidden, promptLine } from './apps.js';
|
|
6
|
+
import { canPrompt, checklist, promptAndSaveTeamApp, } from './teams.js';
|
|
7
|
+
/**
|
|
8
|
+
* `teams connect <team> <product>` — connect a product for one team, doing
|
|
9
|
+
* whichever the integration needs (the host's plan decides, never the CLI):
|
|
10
|
+
*
|
|
11
|
+
* - works as shipped → says so; not self-serve or not enabled → refuses
|
|
12
|
+
* with the reason (and points at `sources products` for the latter);
|
|
13
|
+
* - form fields → asks them here (secrets hidden, never as arguments) and
|
|
14
|
+
* activates exactly as the app's form does;
|
|
15
|
+
* - OAuth → runs `teams app set` inline when the team's own app is needed
|
|
16
|
+
* first (Zendesk), then a one-time sign-in link for the member who
|
|
17
|
+
* consents: the caller when a member, else `--as <email>`. Opens it for
|
|
18
|
+
* the caller (unless `--no-open`), always prints it, and waits until the
|
|
19
|
+
* team is connected (unless `--no-wait`);
|
|
20
|
+
* - a form only the browser can fill → prints the team's page.
|
|
21
|
+
*
|
|
22
|
+
* `--link-only` keeps the old behaviour: the team's page and its status.
|
|
23
|
+
*/
|
|
24
|
+
export async function runTeamConnect(ctx, args, client, host, source, input) {
|
|
25
|
+
var _a, _b, _c, _d;
|
|
26
|
+
const target = { source, team: input.team, product: input.product };
|
|
27
|
+
if (args.linkOnly)
|
|
28
|
+
return printLink(ctx, client, host, target);
|
|
29
|
+
const { plan } = await client.call((token) => showTeamConnect(host, token, target, input.as));
|
|
30
|
+
const name = plan.product.name;
|
|
31
|
+
switch (plan.mode) {
|
|
32
|
+
case 'not-entitled':
|
|
33
|
+
throw invalidStateError((_a = plan.reason) !== null && _a !== void 0 ? _a : `"${input.product}" is not enabled on ${plan.source.slug}.`, `A super-admin enables it for the Source first ("ekanos sources products" shows what ${plan.source.slug} has; "ekanos sources products enable ${input.product}" from a super-admin session, or /admin in the browser).`);
|
|
34
|
+
case 'not-self-serve':
|
|
35
|
+
throw invalidStateError((_b = plan.reason) !== null && _b !== void 0 ? _b : `${name} is not connected by a team.`, plan.product.kind === 'experience'
|
|
36
|
+
? `"ekanos sources products --source ${plan.source.slug}" shows whether it is on for the Source.`
|
|
37
|
+
: 'It is turned on for the Source by Ekanos; there is nothing for the team to do.');
|
|
38
|
+
case 'unsupported':
|
|
39
|
+
throw invalidStateError((_c = plan.reason) !== null && _c !== void 0 ? _c : `${name} cannot be connected from here.`, `Open the team's page instead: ${plan.connectUrl}`);
|
|
40
|
+
case 'works-as-shipped':
|
|
41
|
+
return ctx.succeed({ host, plan, connected: true }, ctx.ui.result({
|
|
42
|
+
title: `${name} works as shipped for ${plan.team.slug}`,
|
|
43
|
+
rows: [['Status', 'nothing to connect']],
|
|
44
|
+
}));
|
|
45
|
+
case 'browser':
|
|
46
|
+
return ctx.succeed({ host, plan, connected: plan.connected }, ctx.ui.result({
|
|
47
|
+
title: plan.connected
|
|
48
|
+
? `${plan.team.slug} has connected ${name}`
|
|
49
|
+
: `${name} connects in the app for ${plan.team.slug}`,
|
|
50
|
+
tone: plan.connected ? 'ok' : 'warn',
|
|
51
|
+
rows: [
|
|
52
|
+
['Open', plan.connectUrl],
|
|
53
|
+
['Status', statusOf(plan)],
|
|
54
|
+
],
|
|
55
|
+
notes: plan.connected
|
|
56
|
+
? []
|
|
57
|
+
: [
|
|
58
|
+
(_d = plan.reason) !== null && _d !== void 0 ? _d : `${name} connects through its own form in the app.`,
|
|
59
|
+
'A team admin signs in there and clicks Connect.',
|
|
60
|
+
],
|
|
61
|
+
}));
|
|
62
|
+
default:
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (plan.connected) {
|
|
66
|
+
return ctx.succeed({ host, plan, connected: true }, ctx.ui.result({
|
|
67
|
+
title: `${plan.team.slug} has already connected ${name}`,
|
|
68
|
+
rows: [
|
|
69
|
+
['Status', statusOf(plan)],
|
|
70
|
+
['Team page', plan.connectUrl],
|
|
71
|
+
],
|
|
72
|
+
notes: [
|
|
73
|
+
`"ekanos teams show ${plan.team.slug} ${plan.product.slug}" shows the full setup.`,
|
|
74
|
+
],
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
if (plan.mode === 'fields' || plan.mode === 'no-fields') {
|
|
78
|
+
return activate(ctx, args, client, host, target, plan);
|
|
79
|
+
}
|
|
80
|
+
return connectOAuth(ctx, args, client, host, target, plan, input.as);
|
|
81
|
+
}
|
|
82
|
+
async function activate(ctx, args, client, host, target, plan) {
|
|
83
|
+
var _a;
|
|
84
|
+
var _b, _c, _d, _e;
|
|
85
|
+
if (plan.scope === 'user' && !plan.member) {
|
|
86
|
+
throw invalidStateError((_b = plan.memberProblem) !== null && _b !== void 0 ? _b : `${plan.product.name} is a personal connection: each member connects their own.`, `A member connects it for themselves on the team's page: ${plan.connectUrl}`);
|
|
87
|
+
}
|
|
88
|
+
const fields = {};
|
|
89
|
+
if (plan.mode === 'fields') {
|
|
90
|
+
if (!canPrompt(ctx, args)) {
|
|
91
|
+
throw usageError(`${plan.product.name} needs its fields typed at a terminal (${plan.fields.map((field) => field.label).join(', ')}), never in a script or an agent run.`, `Run it yourself in your terminal: npx @ekanos/cli teams connect ${target.team} ${target.product} --source ${plan.source.slug}`);
|
|
92
|
+
}
|
|
93
|
+
ctx.log(`${plan.product.name} for ${plan.scope === 'user' ? 'you, in' : 'team'} ${plan.team.slug}: enter what the app's form asks. Secrets are hidden and never shown again.`);
|
|
94
|
+
for (const field of plan.fields) {
|
|
95
|
+
if (field.description)
|
|
96
|
+
ctx.log(` ${field.description}`);
|
|
97
|
+
const suffix = `${field.required ? '' : ' (optional — Enter skips)'}${field.sensitive ? ' (hidden)' : ''}: `;
|
|
98
|
+
const value = (await (field.sensitive
|
|
99
|
+
? ((_c = args.askSecret) !== null && _c !== void 0 ? _c : promptHidden)
|
|
100
|
+
: ((_d = args.ask) !== null && _d !== void 0 ? _d : promptLine))(`${field.label}${suffix}`)).trim();
|
|
101
|
+
if (value)
|
|
102
|
+
fields[field.name] = value;
|
|
103
|
+
else if (field.required) {
|
|
104
|
+
throw validationError(`${field.label} is needed.`, `Run "ekanos teams connect ${target.team} ${target.product}" again.`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const started = await client.call((token) => startTeamConnect(host, token, target, { fields }));
|
|
109
|
+
return ctx.succeed(Object.assign(Object.assign({ host }, started), { connected: started.plan.connected }), ctx.ui.result({
|
|
110
|
+
title: `Connected ${started.plan.product.name} for ${started.plan.team.slug}`,
|
|
111
|
+
rows: [
|
|
112
|
+
['Status', statusOf(started.plan)],
|
|
113
|
+
[
|
|
114
|
+
'For',
|
|
115
|
+
started.plan.scope === 'user'
|
|
116
|
+
? `you (${(_e = (_a = started.plan.member) === null || _a === void 0 ? void 0 : _a.email) !== null && _e !== void 0 ? _e : 'your account'}) — a personal connection`
|
|
117
|
+
: 'the whole team',
|
|
118
|
+
],
|
|
119
|
+
['Team page', started.plan.connectUrl],
|
|
120
|
+
],
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
async function connectOAuth(ctx, args, client, host, target, plan, asEmail) {
|
|
124
|
+
var _a, _b, _c, _d, _e, _f;
|
|
125
|
+
if (!plan.member) {
|
|
126
|
+
throw invalidStateError((_a = plan.memberProblem) !== null && _a !== void 0 ? _a : 'No member of the team can sign in yet.', `Name a member of ${plan.team.slug} who signs in: ekanos teams connect ${target.team} ${target.product} --as <email>`);
|
|
127
|
+
}
|
|
128
|
+
if (plan.teamApp) {
|
|
129
|
+
const status = await client.call((token) => showTeamIntegration(host, token, target));
|
|
130
|
+
const app = (_b = status.teamApps.find((entry) => entry.connectionId === 'default')) !== null && _b !== void 0 ? _b : status.teamApps[0];
|
|
131
|
+
if (app && app.fields.some((field) => !field.set)) {
|
|
132
|
+
if (!canPrompt(ctx, args)) {
|
|
133
|
+
throw usageError(`${plan.product.name} needs ${plan.team.slug}'s own app first, entered at a terminal.`, `Run it yourself in your terminal: npx @ekanos/cli teams connect ${target.team} ${target.product} --source ${plan.source.slug}`);
|
|
134
|
+
}
|
|
135
|
+
ctx.log(`${plan.product.name} signs ${plan.team.slug} in through the team's own app. Set it up first:`);
|
|
136
|
+
await promptAndSaveTeamApp(ctx, args, client, host, target, status, app);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const started = await client.call((token) => startTeamConnect(host, token, target, asEmail ? { as: asEmail } : {}));
|
|
140
|
+
if (started.result !== 'consent') {
|
|
141
|
+
throw invalidStateError(`The host answered "${started.result}" where a sign-in link was expected.`, 'Run "ekanos upgrade" — the CLI and the host disagree on the connect contract.');
|
|
142
|
+
}
|
|
143
|
+
const member = (_c = started.plan.member) !== null && _c !== void 0 ? _c : plan.member;
|
|
144
|
+
const forCaller = member.isCaller;
|
|
145
|
+
const expires = started.expiresAt.replace('T', ' ').slice(0, 16);
|
|
146
|
+
ctx.log(forCaller
|
|
147
|
+
? [
|
|
148
|
+
`Sign in to ${plan.product.name} as the account ${plan.team.slug} should use${plan.scope === 'user' ? ' (a personal connection: yours)' : ''}:`,
|
|
149
|
+
` ${started.authorizeUrl}`,
|
|
150
|
+
` (one-time link, valid until ${expires} UTC)`,
|
|
151
|
+
].join('\n')
|
|
152
|
+
: [
|
|
153
|
+
`Send this link to ${(_d = member.email) !== null && _d !== void 0 ? _d : 'the member'} — they sign in to ${plan.product.name} for ${plan.team.slug}${plan.scope === 'user' ? ' (a personal connection: theirs)' : ''}:`,
|
|
154
|
+
` ${started.authorizeUrl}`,
|
|
155
|
+
` (one-time, for them only, valid until ${expires} UTC)`,
|
|
156
|
+
].join('\n'));
|
|
157
|
+
// Only ever the caller's own browser: opening a link meant for another
|
|
158
|
+
// member would sign the caller's provider account in for them.
|
|
159
|
+
if (forCaller && !args.noOpen && !ctx.jsonMode) {
|
|
160
|
+
((_e = args.openUrl) !== null && _e !== void 0 ? _e : openInBrowser)(started.authorizeUrl);
|
|
161
|
+
}
|
|
162
|
+
if (args.noWait || ctx.jsonMode) {
|
|
163
|
+
return ctx.succeed({
|
|
164
|
+
host,
|
|
165
|
+
plan: started.plan,
|
|
166
|
+
authorizeUrl: started.authorizeUrl,
|
|
167
|
+
expiresAt: started.expiresAt,
|
|
168
|
+
connected: false,
|
|
169
|
+
}, ctx.ui.hint(`Then check with "ekanos teams show ${target.team} ${target.product}".`));
|
|
170
|
+
}
|
|
171
|
+
ctx.log('Waiting for the sign-in to finish (Ctrl-C stops waiting; the link stays valid)…');
|
|
172
|
+
const deadline = Date.parse(started.expiresAt) || Date.now() + 10 * 60 * 1000;
|
|
173
|
+
while (Date.now() < deadline) {
|
|
174
|
+
await new Promise((resolve) => { var _a; return setTimeout(resolve, (_a = args.pollDelayMs) !== null && _a !== void 0 ? _a : 3000); });
|
|
175
|
+
const polled = await client.call((token) => showTeamConnect(host, token, target, asEmail));
|
|
176
|
+
if (polled.plan.connected) {
|
|
177
|
+
const status = await client.call((token) => showTeamIntegration(host, token, target));
|
|
178
|
+
return ctx.succeed({ host, plan: polled.plan, status, connected: true }, [
|
|
179
|
+
ctx.ui.result({
|
|
180
|
+
title: `Connected ${plan.product.name} for ${plan.team.slug}`,
|
|
181
|
+
rows: [
|
|
182
|
+
['Signed in by', (_f = member.email) !== null && _f !== void 0 ? _f : member.userId],
|
|
183
|
+
['Status', statusOf(polled.plan)],
|
|
184
|
+
],
|
|
185
|
+
}),
|
|
186
|
+
checklist(status).join('\n'),
|
|
187
|
+
].join('\n'));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw validationError('The sign-in did not finish before the link expired.', `Run "ekanos teams connect ${target.team} ${target.product}${asEmail ? ` --as ${asEmail}` : ''}" again for a fresh link.`);
|
|
191
|
+
}
|
|
192
|
+
/** `--link-only`: the team's page and whether it is connected. */
|
|
193
|
+
async function printLink(ctx, client, host, target) {
|
|
194
|
+
var _a;
|
|
195
|
+
const status = await client.call((token) => showTeamIntegration(host, token, target));
|
|
196
|
+
if (!status.enabled) {
|
|
197
|
+
throw invalidStateError(`"${target.product}" is not enabled on ${status.source.slug}.`, `"ekanos sources products --source ${status.source.slug}" shows what this Source has; a super-admin enables a product for a Source.`);
|
|
198
|
+
}
|
|
199
|
+
const connected = status.activation !== null;
|
|
200
|
+
const rows = [
|
|
201
|
+
['Open', status.connectUrl],
|
|
202
|
+
[
|
|
203
|
+
'Status',
|
|
204
|
+
connected
|
|
205
|
+
? `connected${((_a = status.activation) === null || _a === void 0 ? void 0 : _a.activatedAt) ? ` ${status.activation.activatedAt.slice(0, 10)}` : ''}`
|
|
206
|
+
: 'not connected',
|
|
207
|
+
],
|
|
208
|
+
];
|
|
209
|
+
return ctx.succeed(Object.assign({ host }, status), ctx.ui.result({
|
|
210
|
+
title: connected
|
|
211
|
+
? `${status.team.slug} has connected ${status.product.name}`
|
|
212
|
+
: `Connect link for ${status.team.slug} · ${status.product.name}`,
|
|
213
|
+
tone: connected ? 'ok' : 'warn',
|
|
214
|
+
rows,
|
|
215
|
+
notes: [
|
|
216
|
+
'A team member signs in there and clicks Connect.',
|
|
217
|
+
`"ekanos teams connect ${status.team.slug} ${status.product.slug}" (without --link-only) connects it from here.`,
|
|
218
|
+
],
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
function statusOf(plan) {
|
|
222
|
+
if (!plan.connected)
|
|
223
|
+
return 'not connected';
|
|
224
|
+
return `connected${plan.connectedAt ? ` ${plan.connectedAt.slice(0, 10)}` : ''}`;
|
|
225
|
+
}
|
|
226
|
+
/** Best effort: the link is always printed, so a failed open costs nothing. */
|
|
227
|
+
function openInBrowser(url) {
|
|
228
|
+
const [command, argv] = process.platform === 'darwin'
|
|
229
|
+
? ['open', [url]]
|
|
230
|
+
: process.platform === 'win32'
|
|
231
|
+
? ['cmd', ['/c', 'start', '""', url]]
|
|
232
|
+
: ['xdg-open', [url]];
|
|
233
|
+
try {
|
|
234
|
+
const child = spawn(command, argv, { detached: true, stdio: 'ignore' });
|
|
235
|
+
child.on('error', () => { });
|
|
236
|
+
child.unref();
|
|
237
|
+
}
|
|
238
|
+
catch (_a) {
|
|
239
|
+
// Printed above; nothing to do.
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=teams-connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-connect.js","sourceRoot":"","sources":["../../src/commands/teams-connect.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\n\nimport type { CliContext } from '../context';\nimport { invalidStateError, usageError, validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type TeamConnectPlan,\n showTeamConnect,\n startTeamConnect,\n} from '../team-connect-api';\nimport { showTeamIntegration } from '../teams-api';\nimport type { ResultRow } from '../ui';\nimport { promptHidden, promptLine } from './apps';\nimport {\n type TeamsArgs,\n type TeamsClient,\n canPrompt,\n checklist,\n promptAndSaveTeamApp,\n} from './teams';\n\n/**\n * `teams connect <team> <product>` — connect a product for one team, doing\n * whichever the integration needs (the host's plan decides, never the CLI):\n *\n * - works as shipped → says so; not self-serve or not enabled → refuses\n * with the reason (and points at `sources products` for the latter);\n * - form fields → asks them here (secrets hidden, never as arguments) and\n * activates exactly as the app's form does;\n * - OAuth → runs `teams app set` inline when the team's own app is needed\n * first (Zendesk), then a one-time sign-in link for the member who\n * consents: the caller when a member, else `--as <email>`. Opens it for\n * the caller (unless `--no-open`), always prints it, and waits until the\n * team is connected (unless `--no-wait`);\n * - a form only the browser can fill → prints the team's page.\n *\n * `--link-only` keeps the old behaviour: the team's page and its status.\n */\nexport async function runTeamConnect(\n ctx: CliContext,\n args: TeamsArgs,\n client: TeamsClient,\n host: string,\n source: string,\n input: { team: string; product: string; as?: string },\n): Promise<ExitCode> {\n const target = { source, team: input.team, product: input.product };\n\n if (args.linkOnly) return printLink(ctx, client, host, target);\n\n const { plan } = await client.call((token) =>\n showTeamConnect(host, token, target, input.as),\n );\n const name = plan.product.name;\n\n switch (plan.mode) {\n case 'not-entitled':\n throw invalidStateError(\n plan.reason ??\n `\"${input.product}\" is not enabled on ${plan.source.slug}.`,\n `A super-admin enables it for the Source first (\"ekanos sources products\" shows what ${plan.source.slug} has; \"ekanos sources products enable ${input.product}\" from a super-admin session, or /admin in the browser).`,\n );\n case 'not-self-serve':\n throw invalidStateError(\n plan.reason ?? `${name} is not connected by a team.`,\n plan.product.kind === 'experience'\n ? `\"ekanos sources products --source ${plan.source.slug}\" shows whether it is on for the Source.`\n : 'It is turned on for the Source by Ekanos; there is nothing for the team to do.',\n );\n case 'unsupported':\n throw invalidStateError(\n plan.reason ?? `${name} cannot be connected from here.`,\n `Open the team's page instead: ${plan.connectUrl}`,\n );\n case 'works-as-shipped':\n return ctx.succeed(\n { host, plan, connected: true },\n ctx.ui.result({\n title: `${name} works as shipped for ${plan.team.slug}`,\n rows: [['Status', 'nothing to connect']],\n }),\n );\n case 'browser':\n return ctx.succeed(\n { host, plan, connected: plan.connected },\n ctx.ui.result({\n title: plan.connected\n ? `${plan.team.slug} has connected ${name}`\n : `${name} connects in the app for ${plan.team.slug}`,\n tone: plan.connected ? 'ok' : 'warn',\n rows: [\n ['Open', plan.connectUrl],\n ['Status', statusOf(plan)],\n ],\n notes: plan.connected\n ? []\n : [\n plan.reason ??\n `${name} connects through its own form in the app.`,\n 'A team admin signs in there and clicks Connect.',\n ],\n }),\n );\n default:\n break;\n }\n\n if (plan.connected) {\n return ctx.succeed(\n { host, plan, connected: true },\n ctx.ui.result({\n title: `${plan.team.slug} has already connected ${name}`,\n rows: [\n ['Status', statusOf(plan)],\n ['Team page', plan.connectUrl],\n ],\n notes: [\n `\"ekanos teams show ${plan.team.slug} ${plan.product.slug}\" shows the full setup.`,\n ],\n }),\n );\n }\n\n if (plan.mode === 'fields' || plan.mode === 'no-fields') {\n return activate(ctx, args, client, host, target, plan);\n }\n return connectOAuth(ctx, args, client, host, target, plan, input.as);\n}\n\nasync function activate(\n ctx: CliContext,\n args: TeamsArgs,\n client: TeamsClient,\n host: string,\n target: { source: string; team: string; product: string },\n plan: TeamConnectPlan,\n): Promise<ExitCode> {\n if (plan.scope === 'user' && !plan.member) {\n throw invalidStateError(\n plan.memberProblem ??\n `${plan.product.name} is a personal connection: each member connects their own.`,\n `A member connects it for themselves on the team's page: ${plan.connectUrl}`,\n );\n }\n\n const fields: Record<string, string> = {};\n if (plan.mode === 'fields') {\n if (!canPrompt(ctx, args)) {\n throw usageError(\n `${plan.product.name} needs its fields typed at a terminal (${plan.fields.map((field) => field.label).join(', ')}), never in a script or an agent run.`,\n `Run it yourself in your terminal: npx @ekanos/cli teams connect ${target.team} ${target.product} --source ${plan.source.slug}`,\n );\n }\n ctx.log(\n `${plan.product.name} for ${plan.scope === 'user' ? 'you, in' : 'team'} ${plan.team.slug}: enter what the app's form asks. Secrets are hidden and never shown again.`,\n );\n for (const field of plan.fields) {\n if (field.description) ctx.log(` ${field.description}`);\n const suffix = `${field.required ? '' : ' (optional — Enter skips)'}${field.sensitive ? ' (hidden)' : ''}: `;\n const value = (\n await (\n field.sensitive\n ? (args.askSecret ?? promptHidden)\n : (args.ask ?? promptLine)\n )(`${field.label}${suffix}`)\n ).trim();\n if (value) fields[field.name] = value;\n else if (field.required) {\n throw validationError(\n `${field.label} is needed.`,\n `Run \"ekanos teams connect ${target.team} ${target.product}\" again.`,\n );\n }\n }\n }\n\n const started = await client.call((token) =>\n startTeamConnect(host, token, target, { fields }),\n );\n return ctx.succeed(\n { host, ...started, connected: started.plan.connected },\n ctx.ui.result({\n title: `Connected ${started.plan.product.name} for ${started.plan.team.slug}`,\n rows: [\n ['Status', statusOf(started.plan)],\n [\n 'For',\n started.plan.scope === 'user'\n ? `you (${started.plan.member?.email ?? 'your account'}) — a personal connection`\n : 'the whole team',\n ],\n ['Team page', started.plan.connectUrl],\n ],\n }),\n );\n}\n\nasync function connectOAuth(\n ctx: CliContext,\n args: TeamsArgs,\n client: TeamsClient,\n host: string,\n target: { source: string; team: string; product: string },\n plan: TeamConnectPlan,\n asEmail: string | undefined,\n): Promise<ExitCode> {\n if (!plan.member) {\n throw invalidStateError(\n plan.memberProblem ?? 'No member of the team can sign in yet.',\n `Name a member of ${plan.team.slug} who signs in: ekanos teams connect ${target.team} ${target.product} --as <email>`,\n );\n }\n\n if (plan.teamApp) {\n const status = await client.call((token) =>\n showTeamIntegration(host, token, target),\n );\n const app =\n status.teamApps.find((entry) => entry.connectionId === 'default') ??\n status.teamApps[0];\n if (app && app.fields.some((field) => !field.set)) {\n if (!canPrompt(ctx, args)) {\n throw usageError(\n `${plan.product.name} needs ${plan.team.slug}'s own app first, entered at a terminal.`,\n `Run it yourself in your terminal: npx @ekanos/cli teams connect ${target.team} ${target.product} --source ${plan.source.slug}`,\n );\n }\n ctx.log(\n `${plan.product.name} signs ${plan.team.slug} in through the team's own app. Set it up first:`,\n );\n await promptAndSaveTeamApp(ctx, args, client, host, target, status, app);\n }\n }\n\n const started = await client.call((token) =>\n startTeamConnect(host, token, target, asEmail ? { as: asEmail } : {}),\n );\n if (started.result !== 'consent') {\n throw invalidStateError(\n `The host answered \"${started.result}\" where a sign-in link was expected.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the connect contract.',\n );\n }\n\n const member = started.plan.member ?? plan.member;\n const forCaller = member.isCaller;\n const expires = started.expiresAt.replace('T', ' ').slice(0, 16);\n ctx.log(\n forCaller\n ? [\n `Sign in to ${plan.product.name} as the account ${plan.team.slug} should use${plan.scope === 'user' ? ' (a personal connection: yours)' : ''}:`,\n ` ${started.authorizeUrl}`,\n ` (one-time link, valid until ${expires} UTC)`,\n ].join('\\n')\n : [\n `Send this link to ${member.email ?? 'the member'} — they sign in to ${plan.product.name} for ${plan.team.slug}${plan.scope === 'user' ? ' (a personal connection: theirs)' : ''}:`,\n ` ${started.authorizeUrl}`,\n ` (one-time, for them only, valid until ${expires} UTC)`,\n ].join('\\n'),\n );\n\n // Only ever the caller's own browser: opening a link meant for another\n // member would sign the caller's provider account in for them.\n if (forCaller && !args.noOpen && !ctx.jsonMode) {\n (args.openUrl ?? openInBrowser)(started.authorizeUrl);\n }\n\n if (args.noWait || ctx.jsonMode) {\n return ctx.succeed(\n {\n host,\n plan: started.plan,\n authorizeUrl: started.authorizeUrl,\n expiresAt: started.expiresAt,\n connected: false,\n },\n ctx.ui.hint(\n `Then check with \"ekanos teams show ${target.team} ${target.product}\".`,\n ),\n );\n }\n\n ctx.log(\n 'Waiting for the sign-in to finish (Ctrl-C stops waiting; the link stays valid)…',\n );\n const deadline = Date.parse(started.expiresAt) || Date.now() + 10 * 60 * 1000;\n while (Date.now() < deadline) {\n await new Promise((resolve) =>\n setTimeout(resolve, args.pollDelayMs ?? 3000),\n );\n const polled = await client.call((token) =>\n showTeamConnect(host, token, target, asEmail),\n );\n if (polled.plan.connected) {\n const status = await client.call((token) =>\n showTeamIntegration(host, token, target),\n );\n return ctx.succeed(\n { host, plan: polled.plan, status, connected: true },\n [\n ctx.ui.result({\n title: `Connected ${plan.product.name} for ${plan.team.slug}`,\n rows: [\n ['Signed in by', member.email ?? member.userId],\n ['Status', statusOf(polled.plan)],\n ],\n }),\n checklist(status).join('\\n'),\n ].join('\\n'),\n );\n }\n }\n throw validationError(\n 'The sign-in did not finish before the link expired.',\n `Run \"ekanos teams connect ${target.team} ${target.product}${asEmail ? ` --as ${asEmail}` : ''}\" again for a fresh link.`,\n );\n}\n\n/** `--link-only`: the team's page and whether it is connected. */\nasync function printLink(\n ctx: CliContext,\n client: TeamsClient,\n host: string,\n target: { source: string; team: string; product: string },\n): Promise<ExitCode> {\n const status = await client.call((token) =>\n showTeamIntegration(host, token, target),\n );\n if (!status.enabled) {\n throw invalidStateError(\n `\"${target.product}\" is not enabled on ${status.source.slug}.`,\n `\"ekanos sources products --source ${status.source.slug}\" shows what this Source has; a super-admin enables a product for a Source.`,\n );\n }\n const connected = status.activation !== null;\n const rows: ResultRow[] = [\n ['Open', status.connectUrl],\n [\n 'Status',\n connected\n ? `connected${status.activation?.activatedAt ? ` ${status.activation.activatedAt.slice(0, 10)}` : ''}`\n : 'not connected',\n ],\n ];\n return ctx.succeed(\n { host, ...status },\n ctx.ui.result({\n title: connected\n ? `${status.team.slug} has connected ${status.product.name}`\n : `Connect link for ${status.team.slug} · ${status.product.name}`,\n tone: connected ? 'ok' : 'warn',\n rows,\n notes: [\n 'A team member signs in there and clicks Connect.',\n `\"ekanos teams connect ${status.team.slug} ${status.product.slug}\" (without --link-only) connects it from here.`,\n ],\n }),\n );\n}\n\nfunction statusOf(plan: TeamConnectPlan): string {\n if (!plan.connected) return 'not connected';\n return `connected${plan.connectedAt ? ` ${plan.connectedAt.slice(0, 10)}` : ''}`;\n}\n\n/** Best effort: the link is always printed, so a failed open costs nothing. */\nfunction openInBrowser(url: string): void {\n const [command, argv] =\n process.platform === 'darwin'\n ? ['open', [url]]\n : process.platform === 'win32'\n ? ['cmd', ['/c', 'start', '\"\"', url]]\n : ['xdg-open', [url]];\n try {\n const child = spawn(command, argv, { detached: true, stdio: 'ignore' });\n child.on('error', () => {});\n child.unref();\n } catch {\n // Printed above; nothing to do.\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CliContext } from '../context.js';
|
|
2
2
|
import type { ExitCode } from '../exit-codes.js';
|
|
3
|
-
import type { TeamsClient } from './teams.js';
|
|
3
|
+
import type { TeamsArgs, TeamsClient } from './teams.js';
|
|
4
4
|
/**
|
|
5
5
|
* Onboarding a team from the terminal, for the Source admin: `teams create`,
|
|
6
6
|
* `teams members list|invite|role|remove`, `teams roles list` and
|
|
@@ -14,6 +14,8 @@ export interface OnboardingFlags {
|
|
|
14
14
|
slug?: string;
|
|
15
15
|
owner?: string;
|
|
16
16
|
role?: string;
|
|
17
|
+
/** `connect --as <member-email>`: the member who signs in. */
|
|
18
|
+
as?: string;
|
|
17
19
|
yes: boolean;
|
|
18
20
|
}
|
|
19
21
|
export type OnboardingPlan = {
|
|
@@ -45,7 +47,8 @@ export type OnboardingPlan = {
|
|
|
45
47
|
kind: 'connect';
|
|
46
48
|
team: string;
|
|
47
49
|
product: string;
|
|
50
|
+
as?: string;
|
|
48
51
|
};
|
|
49
52
|
export declare function isOnboardingSubcommand(value: string): value is OnboardingSubcommand;
|
|
50
53
|
export declare function parseOnboarding(subcommand: OnboardingSubcommand, rest: readonly string[], flags: OnboardingFlags): OnboardingPlan;
|
|
51
|
-
export declare function runOnboarding(ctx: CliContext, client: TeamsClient, host: string, source: string, plan: OnboardingPlan): Promise<ExitCode>;
|
|
54
|
+
export declare function runOnboarding(ctx: CliContext, args: TeamsArgs, client: TeamsClient, host: string, source: string, plan: OnboardingPlan): Promise<ExitCode>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { invalidStateError, usageError } from '../errors.js';
|
|
2
|
-
import { createTeam, inviteTeamMember, listTeamMembers, listTeamRoles, removeTeamMember, setTeamMemberRole,
|
|
2
|
+
import { createTeam, inviteTeamMember, listTeamMembers, listTeamRoles, removeTeamMember, setTeamMemberRole, } from '../teams-api.js';
|
|
3
|
+
import { runTeamConnect } from './teams-connect.js';
|
|
3
4
|
/**
|
|
4
5
|
* Onboarding a team from the terminal, for the Source admin: `teams create`,
|
|
5
6
|
* `teams members list|invite|role|remove`, `teams roles list` and
|
|
@@ -35,11 +36,7 @@ export function parseOnboarding(subcommand, rest, flags) {
|
|
|
35
36
|
if (subcommand === 'connect') {
|
|
36
37
|
const [team, product, ...extra] = rest;
|
|
37
38
|
noExtra(extra);
|
|
38
|
-
return {
|
|
39
|
-
kind: 'connect',
|
|
40
|
-
team: slugOf(team, 'team'),
|
|
41
|
-
product: slugOf(product, 'product'),
|
|
42
|
-
};
|
|
39
|
+
return Object.assign({ kind: 'connect', team: slugOf(team, 'team'), product: slugOf(product, 'product') }, (flags.as === undefined ? {} : { as: emailOf(flags.as) }));
|
|
43
40
|
}
|
|
44
41
|
if (subcommand === 'roles') {
|
|
45
42
|
const [verb, team, ...extra] = rest;
|
|
@@ -95,9 +92,8 @@ export function parseOnboarding(subcommand, rest, flags) {
|
|
|
95
92
|
}
|
|
96
93
|
return { kind: 'members-remove', team: teamSlug, email: address };
|
|
97
94
|
}
|
|
98
|
-
export async function runOnboarding(ctx, client, host, source, plan) {
|
|
99
|
-
var _a;
|
|
100
|
-
var _b, _c;
|
|
95
|
+
export async function runOnboarding(ctx, args, client, host, source, plan) {
|
|
96
|
+
var _a, _b;
|
|
101
97
|
switch (plan.kind) {
|
|
102
98
|
case 'create': {
|
|
103
99
|
const created = await client.call((token) => createTeam(host, token, source, {
|
|
@@ -120,7 +116,7 @@ export async function runOnboarding(ctx, client, host, source, plan) {
|
|
|
120
116
|
],
|
|
121
117
|
notes: [
|
|
122
118
|
`Next: "ekanos teams members invite ${created.team.slug} <email> --role <role>" ("ekanos teams roles list ${created.team.slug}" lists the roles).`,
|
|
123
|
-
`Then "ekanos teams connect ${created.team.slug} <product>"
|
|
119
|
+
`Then "ekanos teams connect ${created.team.slug} <product>" connects an integration for the team.`,
|
|
124
120
|
],
|
|
125
121
|
}));
|
|
126
122
|
}
|
|
@@ -165,7 +161,7 @@ export async function runOnboarding(ctx, client, host, source, plan) {
|
|
|
165
161
|
? `${changed.email} is already ${changed.after} on ${changed.team.slug}`
|
|
166
162
|
: `${changed.email} is now ${changed.after} on ${changed.team.slug}`,
|
|
167
163
|
rows: [
|
|
168
|
-
['Was', (
|
|
164
|
+
['Was', (_a = changed.before) !== null && _a !== void 0 ? _a : '—'],
|
|
169
165
|
[
|
|
170
166
|
'Applies to',
|
|
171
167
|
changed.kind === 'member'
|
|
@@ -185,7 +181,7 @@ export async function runOnboarding(ctx, client, host, source, plan) {
|
|
|
185
181
|
title: removed.kind === 'member'
|
|
186
182
|
? `Removed ${removed.email} from ${removed.team.slug}`
|
|
187
183
|
: `Withdrew ${removed.email}'s invitation to ${removed.team.slug}`,
|
|
188
|
-
rows: [['Role was', (
|
|
184
|
+
rows: [['Role was', (_b = removed.before) !== null && _b !== void 0 ? _b : '—']],
|
|
189
185
|
}));
|
|
190
186
|
}
|
|
191
187
|
case 'roles-list': {
|
|
@@ -201,36 +197,12 @@ export async function runOnboarding(ctx, client, host, source, plan) {
|
|
|
201
197
|
],
|
|
202
198
|
}));
|
|
203
199
|
}
|
|
204
|
-
case 'connect':
|
|
205
|
-
|
|
206
|
-
source,
|
|
200
|
+
case 'connect':
|
|
201
|
+
return runTeamConnect(ctx, args, client, host, source, {
|
|
207
202
|
team: plan.team,
|
|
208
203
|
product: plan.product,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
throw invalidStateError(`"${plan.product}" is not enabled on ${status.source.slug}.`, '"ekanos catalog" lists what this Source has; Ekanos enables a product for a Source.');
|
|
212
|
-
}
|
|
213
|
-
const connected = status.activation !== null;
|
|
214
|
-
return ctx.succeed(Object.assign({ host }, status), ctx.ui.result({
|
|
215
|
-
title: connected
|
|
216
|
-
? `${status.team.slug} has connected ${status.product.name}`
|
|
217
|
-
: `Connect link for ${status.team.slug} · ${status.product.name}`,
|
|
218
|
-
tone: connected ? 'ok' : 'warn',
|
|
219
|
-
rows: [
|
|
220
|
-
['Open', status.connectUrl],
|
|
221
|
-
[
|
|
222
|
-
'Status',
|
|
223
|
-
connected
|
|
224
|
-
? `connected${((_a = status.activation) === null || _a === void 0 ? void 0 : _a.activatedAt) ? ` ${status.activation.activatedAt.slice(0, 10)}` : ''}`
|
|
225
|
-
: 'not connected',
|
|
226
|
-
],
|
|
227
|
-
],
|
|
228
|
-
notes: [
|
|
229
|
-
'A team member signs in there and clicks Connect: sign-in happens in their browser, never in a terminal.',
|
|
230
|
-
`"ekanos teams show ${status.team.slug} ${status.product.slug}" shows the full setup checklist.`,
|
|
231
|
-
],
|
|
232
|
-
}));
|
|
233
|
-
}
|
|
204
|
+
as: plan.as,
|
|
205
|
+
});
|
|
234
206
|
}
|
|
235
207
|
}
|
|
236
208
|
function noExtra(extra) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams-onboard.js","sourceRoot":"","sources":["../../src/commands/teams-onboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CliContext } from '../context';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n createTeam,\n inviteTeamMember,\n listTeamMembers,\n listTeamRoles,\n removeTeamMember,\n setTeamMemberRole,\n showTeamIntegration,\n} from '../teams-api';\nimport type { TeamsClient } from './teams';\n\n/**\n * Onboarding a team from the terminal, for the Source admin: `teams create`,\n * `teams members list|invite|role|remove`, `teams roles list` and\n * `teams connect`. Parsed before any session is touched (`parseOnboarding`)\n * so a usage error never costs a network round-trip, then run against the\n * session `runTeams` resolved.\n */\n\nexport const ONBOARDING_SUBCOMMANDS = [\n 'create',\n 'members',\n 'roles',\n 'connect',\n] as const;\nexport type OnboardingSubcommand = (typeof ONBOARDING_SUBCOMMANDS)[number];\n\nexport interface OnboardingFlags {\n slug?: string;\n owner?: string;\n role?: string;\n yes: boolean;\n}\n\nexport type OnboardingPlan =\n | { kind: 'create'; name: string; slug?: string; ownerEmail?: string }\n | { kind: 'members-list'; team: string }\n | { kind: 'members-invite'; team: string; email: string; role: string }\n | { kind: 'members-role'; team: string; email: string; role: string }\n | { kind: 'members-remove'; team: string; email: string }\n | { kind: 'roles-list'; team: string }\n | { kind: 'connect'; team: string; product: string };\n\nconst MEMBERS_VERBS = ['list', 'invite', 'role', 'remove'] as const;\n\nexport function isOnboardingSubcommand(\n value: string,\n): value is OnboardingSubcommand {\n return (ONBOARDING_SUBCOMMANDS as readonly string[]).includes(value);\n}\n\nexport function parseOnboarding(\n subcommand: OnboardingSubcommand,\n rest: readonly string[],\n flags: OnboardingFlags,\n): OnboardingPlan {\n if (subcommand === 'create') {\n const [name, ...extra] = rest;\n if (!name?.trim()) {\n throw usageError(\n 'Name the team.',\n 'Run \"ekanos teams create \\\\\"Bank A\\\\\" [--slug bank-a] [--owner <email>]\".',\n );\n }\n noExtra(extra);\n return {\n kind: 'create',\n name: name.trim(),\n slug: flags.slug === undefined ? undefined : slugOf(flags.slug, 'team'),\n ownerEmail: flags.owner === undefined ? undefined : emailOf(flags.owner),\n };\n }\n\n if (subcommand === 'connect') {\n const [team, product, ...extra] = rest;\n noExtra(extra);\n return {\n kind: 'connect',\n team: slugOf(team, 'team'),\n product: slugOf(product, 'product'),\n };\n }\n\n if (subcommand === 'roles') {\n const [verb, team, ...extra] = rest;\n if (verb !== 'list') {\n throw usageError(\n verb\n ? `Unknown teams roles subcommand \"${verb}\".`\n : 'teams roles needs a subcommand.',\n 'Run \"ekanos teams roles list <team>\".',\n );\n }\n noExtra(extra);\n return { kind: 'roles-list', team: slugOf(team, 'team') };\n }\n\n const [verb, team, email, role, ...extra] = rest;\n const match = MEMBERS_VERBS.find((name) => name === verb);\n if (!match) {\n throw usageError(\n verb\n ? `Unknown teams members subcommand \"${verb}\".`\n : 'teams members needs a subcommand.',\n 'Run one of: ekanos teams members list <team>, ekanos teams members invite <team> <email> --role <role>, ekanos teams members role <team> <email> <role>, ekanos teams members remove <team> <email> --yes.',\n );\n }\n noExtra(extra);\n\n if (match === 'list') {\n if (email !== undefined) noExtra([email]);\n return { kind: 'members-list', team: slugOf(team, 'team') };\n }\n\n const teamSlug = slugOf(team, 'team');\n const address = emailOf(email);\n\n if (match === 'invite') {\n if (role !== undefined) noExtra([role]);\n return {\n kind: 'members-invite',\n team: teamSlug,\n email: address,\n role: roleOf(flags.role ?? 'member'),\n };\n }\n\n if (match === 'role') {\n const named = role ?? flags.role;\n if (!named) {\n throw usageError(\n 'Name the new role.',\n `Run \"ekanos teams members role ${teamSlug} ${address} <role>\"; \"ekanos teams roles list ${teamSlug}\" lists them.`,\n );\n }\n return {\n kind: 'members-role',\n team: teamSlug,\n email: address,\n role: roleOf(named),\n };\n }\n\n if (role !== undefined) noExtra([role]);\n if (!flags.yes) {\n throw invalidStateError(\n `Removing ${address} from ${teamSlug} ends their access to the team (a pending invitation is withdrawn instead).`,\n `Re-run with --yes: ekanos teams members remove ${teamSlug} ${address} --yes`,\n );\n }\n return { kind: 'members-remove', team: teamSlug, email: address };\n}\n\nexport async function runOnboarding(\n ctx: CliContext,\n client: TeamsClient,\n host: string,\n source: string,\n plan: OnboardingPlan,\n): Promise<ExitCode> {\n switch (plan.kind) {\n case 'create': {\n const created = await client.call((token) =>\n createTeam(host, token, source, {\n name: plan.name,\n slug: plan.slug,\n ownerEmail: plan.ownerEmail,\n }),\n );\n return ctx.succeed(\n { host, ...created },\n ctx.ui.result({\n title: `Created team ${created.team.slug}`,\n rows: [\n ['Name', created.team.name],\n ['Source', created.source.slug],\n [\n 'Owner',\n created.owner.kind === 'caller'\n ? `you${created.owner.email ? ` (${created.owner.email})` : ''}`\n : `${created.owner.email} — invited; ownership moves to them on accept`,\n ],\n ['Team home', created.teamUrl],\n ],\n notes: [\n `Next: \"ekanos teams members invite ${created.team.slug} <email> --role <role>\" (\"ekanos teams roles list ${created.team.slug}\" lists the roles).`,\n `Then \"ekanos teams connect ${created.team.slug} <product>\" for the link a member opens to connect an integration.`,\n ],\n }),\n );\n }\n\n case 'members-list': {\n const list = await client.call((token) =>\n listTeamMembers(host, token, { source, team: plan.team }),\n );\n const rows: [string, string][] = list.members.map((member) => [\n member.email ?? member.userId,\n `${member.role}${member.primaryOwner ? ' · primary owner' : ''}${member.status === 'active' ? '' : ` · ${member.status}`}${member.name ? ` · ${member.name}` : ''}`,\n ]);\n for (const invitation of list.invitations) {\n rows.push([\n invitation.email,\n `${invitation.role} · invited, expires ${invitation.expiresAt.slice(0, 10)}`,\n ]);\n }\n return ctx.succeed(\n { host, ...list },\n ctx.ui.result({\n title: `Members of ${list.team.slug} (${list.team.name})`,\n tone: rows.length === 0 ? 'warn' : 'ok',\n rows,\n notes: [\n `\"ekanos teams members invite ${list.team.slug} <email> --role <role>\" adds one.`,\n ],\n }),\n );\n }\n\n case 'members-invite': {\n const invited = await client.call((token) =>\n inviteTeamMember(\n host,\n token,\n { source, team: plan.team },\n { email: plan.email, role: plan.role },\n ),\n );\n return ctx.succeed(\n { host, ...invited },\n ctx.ui.result({\n title: `Invited ${invited.invitation.email} to ${invited.team.slug}`,\n rows: [\n ['Role', invited.invitation.role],\n ['Expires', invited.invitation.expiresAt.slice(0, 10)],\n ],\n notes: [\n 'The invitation email is on its way; they join when they accept it.',\n ],\n }),\n );\n }\n\n case 'members-role': {\n const changed = await client.call((token) =>\n setTeamMemberRole(\n host,\n token,\n { source, team: plan.team, email: plan.email },\n plan.role,\n ),\n );\n return ctx.succeed(\n { host, ...changed },\n ctx.ui.result({\n title:\n changed.before === changed.after\n ? `${changed.email} is already ${changed.after} on ${changed.team.slug}`\n : `${changed.email} is now ${changed.after} on ${changed.team.slug}`,\n rows: [\n ['Was', changed.before ?? '—'],\n [\n 'Applies to',\n changed.kind === 'member'\n ? 'their membership'\n : 'their pending invitation',\n ],\n ],\n }),\n );\n }\n\n case 'members-remove': {\n const removed = await client.call((token) =>\n removeTeamMember(host, token, {\n source,\n team: plan.team,\n email: plan.email,\n }),\n );\n return ctx.succeed(\n { host, ...removed },\n ctx.ui.result({\n title:\n removed.kind === 'member'\n ? `Removed ${removed.email} from ${removed.team.slug}`\n : `Withdrew ${removed.email}'s invitation to ${removed.team.slug}`,\n rows: [['Role was', removed.before ?? '—']],\n }),\n );\n }\n\n case 'roles-list': {\n const roles = await client.call((token) =>\n listTeamRoles(host, token, { source, team: plan.team }),\n );\n return ctx.succeed(\n { host, ...roles },\n ctx.ui.result({\n title: `Roles on ${roles.team.slug}`,\n rows: roles.roles.map((role) => [\n `${role.name} (${role.displayName})`,\n role.permissions.length > 0 ? role.permissions : ['no permissions'],\n ]),\n notes: [\n 'Lower roles are listed last. Give one with \"ekanos teams members role <team> <email> <role>\".',\n ],\n }),\n );\n }\n\n case 'connect': {\n const status = await client.call((token) =>\n showTeamIntegration(host, token, {\n source,\n team: plan.team,\n product: plan.product,\n }),\n );\n if (!status.enabled) {\n throw invalidStateError(\n `\"${plan.product}\" is not enabled on ${status.source.slug}.`,\n '\"ekanos catalog\" lists what this Source has; Ekanos enables a product for a Source.',\n );\n }\n const connected = status.activation !== null;\n return ctx.succeed(\n { host, ...status },\n ctx.ui.result({\n title: connected\n ? `${status.team.slug} has connected ${status.product.name}`\n : `Connect link for ${status.team.slug} · ${status.product.name}`,\n tone: connected ? 'ok' : 'warn',\n rows: [\n ['Open', status.connectUrl],\n [\n 'Status',\n connected\n ? `connected${status.activation?.activatedAt ? ` ${status.activation.activatedAt.slice(0, 10)}` : ''}`\n : 'not connected',\n ],\n ],\n notes: [\n 'A team member signs in there and clicks Connect: sign-in happens in their browser, never in a terminal.',\n `\"ekanos teams show ${status.team.slug} ${status.product.slug}\" shows the full setup checklist.`,\n ],\n }),\n );\n }\n }\n}\n\nfunction noExtra(extra: readonly string[]) {\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'See \"ekanos teams --help\".',\n );\n }\n}\n\nfunction slugOf(value: string | undefined, what: 'team' | 'product'): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value ? `\"${value}\" is not a ${what} slug.` : `Name the ${what}.`,\n what === 'team'\n ? '\"ekanos teams list\" lists the teams, e.g. \"ekanos teams members list bank-a\".'\n : '\"ekanos catalog\" lists the products, e.g. \"ekanos teams connect bank-a zendesk\".',\n );\n}\n\nfunction emailOf(value: string | undefined): string {\n const email = value?.trim().toLowerCase();\n if (email && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) return email;\n throw usageError(\n value ? `\"${value}\" is not an email address.` : 'Name the person by email.',\n 'e.g. \"ekanos teams members invite bank-a ana@bank-a.com --role member\".',\n );\n}\n\nfunction roleOf(value: string): string {\n const role = value.trim().toLowerCase();\n if (/^[a-z][a-z0-9_-]{0,49}$/.test(role)) return role;\n throw usageError(\n `\"${value}\" is not a role name.`,\n '\"ekanos teams roles list <team>\" lists the roles this Source uses.',\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"teams-onboard.js","sourceRoot":"","sources":["../../src/commands/teams-onboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CliContext } from '../context';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n createTeam,\n inviteTeamMember,\n listTeamMembers,\n listTeamRoles,\n removeTeamMember,\n setTeamMemberRole,\n} from '../teams-api';\nimport type { TeamsArgs, TeamsClient } from './teams';\nimport { runTeamConnect } from './teams-connect';\n\n/**\n * Onboarding a team from the terminal, for the Source admin: `teams create`,\n * `teams members list|invite|role|remove`, `teams roles list` and\n * `teams connect`. Parsed before any session is touched (`parseOnboarding`)\n * so a usage error never costs a network round-trip, then run against the\n * session `runTeams` resolved.\n */\n\nexport const ONBOARDING_SUBCOMMANDS = [\n 'create',\n 'members',\n 'roles',\n 'connect',\n] as const;\nexport type OnboardingSubcommand = (typeof ONBOARDING_SUBCOMMANDS)[number];\n\nexport interface OnboardingFlags {\n slug?: string;\n owner?: string;\n role?: string;\n /** `connect --as <member-email>`: the member who signs in. */\n as?: string;\n yes: boolean;\n}\n\nexport type OnboardingPlan =\n | { kind: 'create'; name: string; slug?: string; ownerEmail?: string }\n | { kind: 'members-list'; team: string }\n | { kind: 'members-invite'; team: string; email: string; role: string }\n | { kind: 'members-role'; team: string; email: string; role: string }\n | { kind: 'members-remove'; team: string; email: string }\n | { kind: 'roles-list'; team: string }\n | { kind: 'connect'; team: string; product: string; as?: string };\n\nconst MEMBERS_VERBS = ['list', 'invite', 'role', 'remove'] as const;\n\nexport function isOnboardingSubcommand(\n value: string,\n): value is OnboardingSubcommand {\n return (ONBOARDING_SUBCOMMANDS as readonly string[]).includes(value);\n}\n\nexport function parseOnboarding(\n subcommand: OnboardingSubcommand,\n rest: readonly string[],\n flags: OnboardingFlags,\n): OnboardingPlan {\n if (subcommand === 'create') {\n const [name, ...extra] = rest;\n if (!name?.trim()) {\n throw usageError(\n 'Name the team.',\n 'Run \"ekanos teams create \\\\\"Bank A\\\\\" [--slug bank-a] [--owner <email>]\".',\n );\n }\n noExtra(extra);\n return {\n kind: 'create',\n name: name.trim(),\n slug: flags.slug === undefined ? undefined : slugOf(flags.slug, 'team'),\n ownerEmail: flags.owner === undefined ? undefined : emailOf(flags.owner),\n };\n }\n\n if (subcommand === 'connect') {\n const [team, product, ...extra] = rest;\n noExtra(extra);\n return {\n kind: 'connect',\n team: slugOf(team, 'team'),\n product: slugOf(product, 'product'),\n ...(flags.as === undefined ? {} : { as: emailOf(flags.as) }),\n };\n }\n\n if (subcommand === 'roles') {\n const [verb, team, ...extra] = rest;\n if (verb !== 'list') {\n throw usageError(\n verb\n ? `Unknown teams roles subcommand \"${verb}\".`\n : 'teams roles needs a subcommand.',\n 'Run \"ekanos teams roles list <team>\".',\n );\n }\n noExtra(extra);\n return { kind: 'roles-list', team: slugOf(team, 'team') };\n }\n\n const [verb, team, email, role, ...extra] = rest;\n const match = MEMBERS_VERBS.find((name) => name === verb);\n if (!match) {\n throw usageError(\n verb\n ? `Unknown teams members subcommand \"${verb}\".`\n : 'teams members needs a subcommand.',\n 'Run one of: ekanos teams members list <team>, ekanos teams members invite <team> <email> --role <role>, ekanos teams members role <team> <email> <role>, ekanos teams members remove <team> <email> --yes.',\n );\n }\n noExtra(extra);\n\n if (match === 'list') {\n if (email !== undefined) noExtra([email]);\n return { kind: 'members-list', team: slugOf(team, 'team') };\n }\n\n const teamSlug = slugOf(team, 'team');\n const address = emailOf(email);\n\n if (match === 'invite') {\n if (role !== undefined) noExtra([role]);\n return {\n kind: 'members-invite',\n team: teamSlug,\n email: address,\n role: roleOf(flags.role ?? 'member'),\n };\n }\n\n if (match === 'role') {\n const named = role ?? flags.role;\n if (!named) {\n throw usageError(\n 'Name the new role.',\n `Run \"ekanos teams members role ${teamSlug} ${address} <role>\"; \"ekanos teams roles list ${teamSlug}\" lists them.`,\n );\n }\n return {\n kind: 'members-role',\n team: teamSlug,\n email: address,\n role: roleOf(named),\n };\n }\n\n if (role !== undefined) noExtra([role]);\n if (!flags.yes) {\n throw invalidStateError(\n `Removing ${address} from ${teamSlug} ends their access to the team (a pending invitation is withdrawn instead).`,\n `Re-run with --yes: ekanos teams members remove ${teamSlug} ${address} --yes`,\n );\n }\n return { kind: 'members-remove', team: teamSlug, email: address };\n}\n\nexport async function runOnboarding(\n ctx: CliContext,\n args: TeamsArgs,\n client: TeamsClient,\n host: string,\n source: string,\n plan: OnboardingPlan,\n): Promise<ExitCode> {\n switch (plan.kind) {\n case 'create': {\n const created = await client.call((token) =>\n createTeam(host, token, source, {\n name: plan.name,\n slug: plan.slug,\n ownerEmail: plan.ownerEmail,\n }),\n );\n return ctx.succeed(\n { host, ...created },\n ctx.ui.result({\n title: `Created team ${created.team.slug}`,\n rows: [\n ['Name', created.team.name],\n ['Source', created.source.slug],\n [\n 'Owner',\n created.owner.kind === 'caller'\n ? `you${created.owner.email ? ` (${created.owner.email})` : ''}`\n : `${created.owner.email} — invited; ownership moves to them on accept`,\n ],\n ['Team home', created.teamUrl],\n ],\n notes: [\n `Next: \"ekanos teams members invite ${created.team.slug} <email> --role <role>\" (\"ekanos teams roles list ${created.team.slug}\" lists the roles).`,\n `Then \"ekanos teams connect ${created.team.slug} <product>\" connects an integration for the team.`,\n ],\n }),\n );\n }\n\n case 'members-list': {\n const list = await client.call((token) =>\n listTeamMembers(host, token, { source, team: plan.team }),\n );\n const rows: [string, string][] = list.members.map((member) => [\n member.email ?? member.userId,\n `${member.role}${member.primaryOwner ? ' · primary owner' : ''}${member.status === 'active' ? '' : ` · ${member.status}`}${member.name ? ` · ${member.name}` : ''}`,\n ]);\n for (const invitation of list.invitations) {\n rows.push([\n invitation.email,\n `${invitation.role} · invited, expires ${invitation.expiresAt.slice(0, 10)}`,\n ]);\n }\n return ctx.succeed(\n { host, ...list },\n ctx.ui.result({\n title: `Members of ${list.team.slug} (${list.team.name})`,\n tone: rows.length === 0 ? 'warn' : 'ok',\n rows,\n notes: [\n `\"ekanos teams members invite ${list.team.slug} <email> --role <role>\" adds one.`,\n ],\n }),\n );\n }\n\n case 'members-invite': {\n const invited = await client.call((token) =>\n inviteTeamMember(\n host,\n token,\n { source, team: plan.team },\n { email: plan.email, role: plan.role },\n ),\n );\n return ctx.succeed(\n { host, ...invited },\n ctx.ui.result({\n title: `Invited ${invited.invitation.email} to ${invited.team.slug}`,\n rows: [\n ['Role', invited.invitation.role],\n ['Expires', invited.invitation.expiresAt.slice(0, 10)],\n ],\n notes: [\n 'The invitation email is on its way; they join when they accept it.',\n ],\n }),\n );\n }\n\n case 'members-role': {\n const changed = await client.call((token) =>\n setTeamMemberRole(\n host,\n token,\n { source, team: plan.team, email: plan.email },\n plan.role,\n ),\n );\n return ctx.succeed(\n { host, ...changed },\n ctx.ui.result({\n title:\n changed.before === changed.after\n ? `${changed.email} is already ${changed.after} on ${changed.team.slug}`\n : `${changed.email} is now ${changed.after} on ${changed.team.slug}`,\n rows: [\n ['Was', changed.before ?? '—'],\n [\n 'Applies to',\n changed.kind === 'member'\n ? 'their membership'\n : 'their pending invitation',\n ],\n ],\n }),\n );\n }\n\n case 'members-remove': {\n const removed = await client.call((token) =>\n removeTeamMember(host, token, {\n source,\n team: plan.team,\n email: plan.email,\n }),\n );\n return ctx.succeed(\n { host, ...removed },\n ctx.ui.result({\n title:\n removed.kind === 'member'\n ? `Removed ${removed.email} from ${removed.team.slug}`\n : `Withdrew ${removed.email}'s invitation to ${removed.team.slug}`,\n rows: [['Role was', removed.before ?? '—']],\n }),\n );\n }\n\n case 'roles-list': {\n const roles = await client.call((token) =>\n listTeamRoles(host, token, { source, team: plan.team }),\n );\n return ctx.succeed(\n { host, ...roles },\n ctx.ui.result({\n title: `Roles on ${roles.team.slug}`,\n rows: roles.roles.map((role) => [\n `${role.name} (${role.displayName})`,\n role.permissions.length > 0 ? role.permissions : ['no permissions'],\n ]),\n notes: [\n 'Lower roles are listed last. Give one with \"ekanos teams members role <team> <email> <role>\".',\n ],\n }),\n );\n }\n\n case 'connect':\n return runTeamConnect(ctx, args, client, host, source, {\n team: plan.team,\n product: plan.product,\n as: plan.as,\n });\n }\n}\n\nfunction noExtra(extra: readonly string[]) {\n if (extra.length > 0) {\n throw usageError(\n `Unexpected argument \"${extra[0]}\".`,\n 'See \"ekanos teams --help\".',\n );\n }\n}\n\nfunction slugOf(value: string | undefined, what: 'team' | 'product'): string {\n const slug = value?.trim().toLowerCase();\n if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug)) return slug;\n throw usageError(\n value ? `\"${value}\" is not a ${what} slug.` : `Name the ${what}.`,\n what === 'team'\n ? '\"ekanos teams list\" lists the teams, e.g. \"ekanos teams members list bank-a\".'\n : '\"ekanos catalog\" lists the products, e.g. \"ekanos teams connect bank-a zendesk\".',\n );\n}\n\nfunction emailOf(value: string | undefined): string {\n const email = value?.trim().toLowerCase();\n if (email && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) return email;\n throw usageError(\n value ? `\"${value}\" is not an email address.` : 'Name the person by email.',\n 'e.g. \"ekanos teams members invite bank-a ana@bank-a.com --role member\".',\n );\n}\n\nfunction roleOf(value: string): string {\n const role = value.trim().toLowerCase();\n if (/^[a-z][a-z0-9_-]{0,49}$/.test(role)) return role;\n throw usageError(\n `\"${value}\" is not a role name.`,\n '\"ekanos teams roles list <team>\" lists the roles this Source uses.',\n );\n}\n"]}
|
package/dist/commands/teams.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TeamIntegrationStatus } from '@ekanos/integration-schema';
|
|
1
|
+
import type { TeamAppState, TeamIntegrationStatus } from '@ekanos/integration-schema';
|
|
2
2
|
import type { StoredSession } from '../auth/credential-store.js';
|
|
3
3
|
import { type AuthEnvironment } from '../auth/session.js';
|
|
4
4
|
import type { CliContext } from '../context.js';
|
|
@@ -16,6 +16,15 @@ export interface TeamsArgs {
|
|
|
16
16
|
owner?: string;
|
|
17
17
|
role?: string;
|
|
18
18
|
yes: boolean;
|
|
19
|
+
/** `connect --no-wait`: print the sign-in link and return without polling. */
|
|
20
|
+
noWait?: boolean;
|
|
21
|
+
/** `connect --no-open`: never open a browser, only print the link. */
|
|
22
|
+
noOpen?: boolean;
|
|
23
|
+
/** `connect --link-only`: print the team's page, as before connect did the work. */
|
|
24
|
+
linkOnly?: boolean;
|
|
25
|
+
/** Test seams for `connect`: the wait between polls, and the browser opener. */
|
|
26
|
+
pollDelayMs?: number;
|
|
27
|
+
openUrl?: (url: string) => void;
|
|
19
28
|
/** Every parsed flag, for `teams agents|triggers|links|mcp` (team-agents.ts). */
|
|
20
29
|
flags?: Record<string, string | boolean | string[]>;
|
|
21
30
|
dir: string;
|
|
@@ -25,21 +34,67 @@ export interface TeamsArgs {
|
|
|
25
34
|
ask?: (question: string) => Promise<string>;
|
|
26
35
|
askSecret?: (question: string) => Promise<string>;
|
|
27
36
|
}
|
|
37
|
+
export interface Target {
|
|
38
|
+
source: string;
|
|
39
|
+
team: string;
|
|
40
|
+
product: string;
|
|
41
|
+
}
|
|
28
42
|
/**
|
|
29
43
|
* `teams` — a Source admin's view of its teams' integration setup, the
|
|
30
44
|
* team-level counterpart of `apps`. `list` names the teams; `show` reads one
|
|
31
|
-
* team's setup of one product as a checklist; `app set` enters
|
|
32
|
-
* app (Zendesk's subdomain, client and signing
|
|
33
|
-
*
|
|
34
|
-
*
|
|
45
|
+
* team's setup of one product as a checklist; `app set` enters or updates
|
|
46
|
+
* any of a team's own app fields (Zendesk's subdomain, client and signing
|
|
47
|
+
* secret), one at a time if need be — a field left alone keeps its saved
|
|
48
|
+
* value. Secrets are only ever typed at the admin's terminal, never from a
|
|
49
|
+
* flag, a file or a non-interactive run, so an agent driving the CLI never
|
|
50
|
+
* handles one; a non-secret field may be passed as `--set name=value`; `webhook` mints the URL the
|
|
35
51
|
* team's provider sends to, shown once.
|
|
36
52
|
*/
|
|
37
53
|
export declare function runTeams(ctx: CliContext, args: TeamsArgs): Promise<ExitCode>;
|
|
54
|
+
/** What `teams app set` was asked to change, from its flags. */
|
|
55
|
+
interface AppEdits {
|
|
56
|
+
/** `--field`: enter just these, at the terminal. */
|
|
57
|
+
only: string[];
|
|
58
|
+
/** `--set name=value`: non-secret values, no prompt. */
|
|
59
|
+
values: Record<string, string>;
|
|
60
|
+
/** `--clear name`: remove a saved value. */
|
|
61
|
+
clear: string[];
|
|
62
|
+
/** Whether anything is asked at the terminal. */
|
|
63
|
+
prompts: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The `teams app set` walkthrough and prompts for one team app, then the
|
|
67
|
+
* save — of only what was entered: a field left empty keeps its saved value.
|
|
68
|
+
* Shared with `teams connect`, which runs it inline when the integration
|
|
69
|
+
* needs the team's own app (Zendesk) before anyone can sign in.
|
|
70
|
+
*/
|
|
71
|
+
export declare function promptAndSaveTeamApp(ctx: CliContext, args: TeamsArgs, client: TeamsClient, host: string, target: Target, status: TeamIntegrationStatus, app: TeamAppState, edits?: AppEdits): Promise<{
|
|
72
|
+
team: {
|
|
73
|
+
slug: string;
|
|
74
|
+
name: string;
|
|
75
|
+
};
|
|
76
|
+
product: {
|
|
77
|
+
slug: string;
|
|
78
|
+
name: string;
|
|
79
|
+
};
|
|
80
|
+
connectionId: string;
|
|
81
|
+
fields: {
|
|
82
|
+
secretName: string;
|
|
83
|
+
label: string;
|
|
84
|
+
description: string | null;
|
|
85
|
+
sensitive: boolean;
|
|
86
|
+
set: boolean;
|
|
87
|
+
value?: string | null | undefined;
|
|
88
|
+
}[];
|
|
89
|
+
}>;
|
|
38
90
|
/** `teams show`, read top to bottom like a setup checklist. */
|
|
39
91
|
export declare function checklist(status: TeamIntegrationStatus): string[];
|
|
92
|
+
export declare function chooseApp(status: TeamIntegrationStatus, connection: string | undefined): TeamAppState;
|
|
40
93
|
export declare class TeamsClient {
|
|
41
94
|
private readonly env;
|
|
42
95
|
private session;
|
|
43
96
|
constructor(env: AuthEnvironment, session: StoredSession);
|
|
44
97
|
call<T>(send: (accessToken: string) => Promise<TeamsApiResult<T>>): Promise<T>;
|
|
45
98
|
}
|
|
99
|
+
export declare function canPrompt(ctx: CliContext, args: TeamsArgs): boolean;
|
|
100
|
+
export {};
|