@ekanos/cli 0.14.0 → 0.16.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 +41 -4
- package/dist/auth/ekanos-api.d.ts +23 -0
- package/dist/auth/ekanos-api.js +33 -6
- package/dist/auth/ekanos-api.js.map +1 -1
- package/dist/commands/agents.d.ts +2 -2
- package/dist/commands/agents.js +14 -20
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/apps.d.ts +11 -3
- package/dist/commands/apps.js +86 -28
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/catalog.d.ts +4 -2
- package/dist/commands/catalog.js +166 -35
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +17 -0
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/runs.d.ts +7 -3
- package/dist/commands/runs.js +24 -6
- package/dist/commands/runs.js.map +1 -1
- package/dist/commands/scope-notice.d.ts +7 -0
- package/dist/commands/scope-notice.js +4 -11
- package/dist/commands/scope-notice.js.map +1 -1
- package/dist/commands/sources-roles.d.ts +5 -0
- package/dist/commands/sources-roles.js +235 -0
- package/dist/commands/sources-roles.js.map +1 -0
- package/dist/commands/sources.d.ts +5 -0
- package/dist/commands/sources.js +6 -1
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/team-agent-readiness.d.ts +23 -17
- package/dist/commands/team-agent-readiness.js +57 -90
- package/dist/commands/team-agent-readiness.js.map +1 -1
- package/dist/commands/team-agents-agents.d.ts +8 -5
- package/dist/commands/team-agents-agents.js +173 -58
- package/dist/commands/team-agents-agents.js.map +1 -1
- package/dist/commands/team-agents-define.d.ts +31 -0
- package/dist/commands/team-agents-define.js +176 -0
- package/dist/commands/team-agents-define.js.map +1 -0
- package/dist/commands/team-agents-mcp.js +112 -10
- package/dist/commands/team-agents-mcp.js.map +1 -1
- package/dist/commands/team-agents-triggers.d.ts +1 -1
- package/dist/commands/team-agents-triggers.js +137 -5
- package/dist/commands/team-agents-triggers.js.map +1 -1
- package/dist/commands/team-agents.d.ts +4 -1
- package/dist/commands/team-agents.js +17 -4
- package/dist/commands/team-agents.js.map +1 -1
- package/dist/commands/team-llm-key.d.ts +14 -0
- package/dist/commands/team-llm-key.js +122 -0
- package/dist/commands/team-llm-key.js.map +1 -0
- package/dist/commands/teams-connect.js +123 -31
- package/dist/commands/teams-connect.js.map +1 -1
- package/dist/commands/teams-lifecycle.d.ts +90 -0
- package/dist/commands/teams-lifecycle.js +457 -0
- package/dist/commands/teams-lifecycle.js.map +1 -0
- package/dist/commands/teams-onboard.d.ts +14 -0
- package/dist/commands/teams-onboard.js +62 -13
- package/dist/commands/teams-onboard.js.map +1 -1
- package/dist/commands/teams-overview.js +7 -3
- package/dist/commands/teams-overview.js.map +1 -1
- package/dist/commands/teams.d.ts +25 -5
- package/dist/commands/teams.js +101 -39
- package/dist/commands/teams.js.map +1 -1
- package/dist/commands/workflows.d.ts +37 -0
- package/dist/commands/workflows.js +589 -0
- package/dist/commands/workflows.js.map +1 -0
- package/dist/connect-fields.d.ts +22 -0
- package/dist/connect-fields.js +115 -0
- package/dist/connect-fields.js.map +1 -0
- package/dist/context.d.ts +12 -0
- package/dist/context.js +32 -9
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.js +367 -133
- package/dist/index.js.map +1 -1
- package/dist/invocation.d.ts +31 -0
- package/dist/invocation.js +58 -0
- package/dist/invocation.js.map +1 -0
- package/dist/runs-api.js +8 -3
- package/dist/runs-api.js.map +1 -1
- package/dist/safe-fs.d.ts +18 -0
- package/dist/safe-fs.js +57 -0
- package/dist/safe-fs.js.map +1 -1
- package/dist/secret-form.d.ts +48 -0
- package/dist/secret-form.js +212 -0
- package/dist/secret-form.js.map +1 -0
- package/dist/secret-input.d.ts +96 -0
- package/dist/secret-input.js +299 -0
- package/dist/secret-input.js.map +1 -0
- package/dist/team-connect-api.d.ts +78 -3
- package/dist/team-connect-api.js +17 -2
- package/dist/team-connect-api.js.map +1 -1
- package/dist/team-lifecycle-api.d.ts +435 -0
- package/dist/team-lifecycle-api.js +301 -0
- package/dist/team-lifecycle-api.js.map +1 -0
- package/dist/teams-api.d.ts +13 -1
- package/dist/teams-api.js +90 -31
- package/dist/teams-api.js.map +1 -1
- package/dist/unknown-command.d.ts +18 -0
- package/dist/unknown-command.js +50 -0
- package/dist/unknown-command.js.map +1 -0
- package/dist/workflows-api.d.ts +69 -0
- package/dist/workflows-api.js +182 -0
- package/dist/workflows-api.js.map +1 -0
- package/package.json +3 -3
- package/templates/AGENTS.md.tmpl +6 -4
- package/templates/claude-skill.md.tmpl +4 -1
- package/templates/package.json.tmpl +4 -4
- package/templates/skills/build-agent/SKILL.md +56 -22
- package/templates/skills/build-agent/references/agent-json.md +4 -1
- package/templates/skills/build-agent/references/app-setup.md +14 -7
- package/templates/skills/build-agent/references/rollout.md +40 -20
- package/templates/skills/build-agent/references/set-up-an-integration.md +46 -29
- package/templates/skills/build-agent/references/triggers.md +4 -3
- package/templates/skills/build-integration/references/widget-design.md +1 -1
- package/templates/skills/ekanos-platform/SKILL.md +77 -23
- package/templates/skills/polish-widgets/references/patterns.md +3 -3
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { requireSession, resolveAuthEnvironment } from '../auth/session.js';
|
|
2
|
+
import { invalidStateError, usageError } from '../errors.js';
|
|
3
|
+
import { cli } from '../invocation.js';
|
|
4
|
+
import { resolveSourceTarget } from '../source-target.js';
|
|
5
|
+
import { createTeamGroup, deleteTeamGroup, leaveTeam, listTeamGroups, listTeamHealth, listTeamWorkspaces, ownTeamHealth, resendTeamInvitation, runTeamHealthCheck, setTeamDefaultWorkspace, setTeamGroupMember, updateTeamGroup, } from '../team-lifecycle-api.js';
|
|
6
|
+
import { promptYesNo } from './publish.js';
|
|
7
|
+
import { TeamsClient, canPrompt } from './teams.js';
|
|
8
|
+
/**
|
|
9
|
+
* A team's lifecycle from the terminal, for the Source admin:
|
|
10
|
+
* `teams members resend`, `teams leave`, `teams groups …`, `teams health`
|
|
11
|
+
* and `teams workspace …`. Each is parsed before a session is touched
|
|
12
|
+
* (`parseLifecycle`), and a change that takes something away is confirmed
|
|
13
|
+
* first — asked at a terminal, `--yes` in a script or agent run — so a usage
|
|
14
|
+
* error or an unconfirmed change never costs a request. The host does the
|
|
15
|
+
* rest through the services the app's own screens use, with an Admin Audit
|
|
16
|
+
* row per write.
|
|
17
|
+
*
|
|
18
|
+
* Ownership transfer and team deletion are not here: in the app they are
|
|
19
|
+
* the primary owner's alone, confirmed by a code emailed to them.
|
|
20
|
+
*/
|
|
21
|
+
export const LIFECYCLE_SUBCOMMANDS = [
|
|
22
|
+
'leave',
|
|
23
|
+
'groups',
|
|
24
|
+
'health',
|
|
25
|
+
'workspace',
|
|
26
|
+
];
|
|
27
|
+
/** `teams members <verb>` verbs handled here rather than in onboarding. */
|
|
28
|
+
export const LIFECYCLE_MEMBER_VERBS = ['resend'];
|
|
29
|
+
export function isLifecycleSubcommand(value) {
|
|
30
|
+
return LIFECYCLE_SUBCOMMANDS.includes(value !== null && value !== void 0 ? value : '');
|
|
31
|
+
}
|
|
32
|
+
export function isLifecycleMemberVerb(value) {
|
|
33
|
+
return LIFECYCLE_MEMBER_VERBS.includes(value !== null && value !== void 0 ? value : '');
|
|
34
|
+
}
|
|
35
|
+
const GROUPS_USAGE = 'Run one of: ekanos teams groups list <team>, ekanos teams groups create <team> <name> [--description <text>] [--products <slug,slug>], ekanos teams groups update <team> <group> [--name <name>] [--description <text>] [--products <slug,slug>|none], ekanos teams groups delete <team> <group> --yes, ekanos teams groups members add|remove <team> <group> <email>.';
|
|
36
|
+
const WORKSPACE_USAGE = 'Run "ekanos teams workspace list <team>" or "ekanos teams workspace default <team> <workspace>".';
|
|
37
|
+
/**
|
|
38
|
+
* `subcommand` is `members` (with `resend` first in `rest`) or one
|
|
39
|
+
* of {@link LIFECYCLE_SUBCOMMANDS}.
|
|
40
|
+
*/
|
|
41
|
+
export function parseLifecycle(subcommand, rest, args) {
|
|
42
|
+
var _a;
|
|
43
|
+
const flags = (_a = args.flags) !== null && _a !== void 0 ? _a : {};
|
|
44
|
+
if (subcommand === 'members') {
|
|
45
|
+
const [, team, email, ...extra] = rest;
|
|
46
|
+
noExtra(extra);
|
|
47
|
+
return {
|
|
48
|
+
kind: 'resend',
|
|
49
|
+
team: slugOf(team, 'team'),
|
|
50
|
+
email: emailOf(email),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (subcommand === 'leave') {
|
|
54
|
+
const [team, ...extra] = rest;
|
|
55
|
+
noExtra(extra);
|
|
56
|
+
const teamSlug = slugOf(team, 'team');
|
|
57
|
+
return Object.assign({ kind: 'leave', team: teamSlug }, confirmUnless(args.yes, {
|
|
58
|
+
what: `Leaving ${teamSlug} ends your own membership of it; only an invitation brings you back.`,
|
|
59
|
+
command: `${cli()} teams leave ${teamSlug} --yes`,
|
|
60
|
+
question: `Leave ${teamSlug}? [y/N]`,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if (subcommand === 'health') {
|
|
64
|
+
const [team, product, ...extra] = rest;
|
|
65
|
+
noExtra(extra);
|
|
66
|
+
const run = flags.run === true;
|
|
67
|
+
const productSlug = product === undefined ? undefined : slugOf(product, 'product');
|
|
68
|
+
if (run && !productSlug) {
|
|
69
|
+
throw usageError('teams health --run checks one integration; name it.', 'Run "ekanos teams health <team> <product> --run"; "ekanos teams health <team>" lists them.');
|
|
70
|
+
}
|
|
71
|
+
const mine = flags.mine === true;
|
|
72
|
+
if (mine && !productSlug) {
|
|
73
|
+
throw usageError('teams health --mine shows your own connection to one integration; name it.', 'Run "ekanos teams health <team> <product> --mine [--run]".');
|
|
74
|
+
}
|
|
75
|
+
return Object.assign(Object.assign(Object.assign({ kind: 'health', team: slugOf(team, 'team') }, (productSlug ? { product: productSlug } : {})), { run }), (mine ? { mine } : {}));
|
|
76
|
+
}
|
|
77
|
+
if (subcommand === 'workspace') {
|
|
78
|
+
const [verb, team, workspace, ...extra] = rest;
|
|
79
|
+
noExtra(extra);
|
|
80
|
+
if (verb === 'list') {
|
|
81
|
+
if (workspace !== undefined)
|
|
82
|
+
noExtra([workspace]);
|
|
83
|
+
return { kind: 'workspace-list', team: slugOf(team, 'team') };
|
|
84
|
+
}
|
|
85
|
+
if (verb === 'default') {
|
|
86
|
+
const teamSlug = slugOf(team, 'team');
|
|
87
|
+
if (!(workspace === null || workspace === void 0 ? void 0 : workspace.trim())) {
|
|
88
|
+
throw usageError('Name the workspace.', `Run "${cli()} teams workspace default ${teamSlug} <workspace>"; "${cli()} teams workspace list ${teamSlug}" lists them.`);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
kind: 'workspace-default',
|
|
92
|
+
team: teamSlug,
|
|
93
|
+
workspace: workspace.trim().toLowerCase(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
throw usageError(verb
|
|
97
|
+
? `Unknown teams workspace subcommand "${verb}".`
|
|
98
|
+
: 'teams workspace needs a subcommand.', WORKSPACE_USAGE);
|
|
99
|
+
}
|
|
100
|
+
const [verb, ...operands] = rest;
|
|
101
|
+
if (verb === 'list') {
|
|
102
|
+
const [team, ...extra] = operands;
|
|
103
|
+
noExtra(extra);
|
|
104
|
+
return { kind: 'groups-list', team: slugOf(team, 'team') };
|
|
105
|
+
}
|
|
106
|
+
if (verb === 'create') {
|
|
107
|
+
const [team, name, ...extra] = operands;
|
|
108
|
+
noExtra(extra);
|
|
109
|
+
const teamSlug = slugOf(team, 'team');
|
|
110
|
+
if (!(name === null || name === void 0 ? void 0 : name.trim())) {
|
|
111
|
+
throw usageError('Name the group.', `Run "${cli()} teams groups create ${teamSlug} \\"Loan officers\\"".`);
|
|
112
|
+
}
|
|
113
|
+
const description = stringFlag(flags.description);
|
|
114
|
+
const products = productsFlag(flags.products);
|
|
115
|
+
return Object.assign(Object.assign({ kind: 'groups-create', team: teamSlug, name: name.trim() }, (description === undefined ? {} : { description })), (products === undefined ? {} : { products }));
|
|
116
|
+
}
|
|
117
|
+
if (verb === 'update') {
|
|
118
|
+
const [team, group, ...extra] = operands;
|
|
119
|
+
noExtra(extra);
|
|
120
|
+
const teamSlug = slugOf(team, 'team');
|
|
121
|
+
const groupRef = groupOf(group, teamSlug);
|
|
122
|
+
const name = stringFlag(flags.name);
|
|
123
|
+
const description = stringFlag(flags.description);
|
|
124
|
+
const products = productsFlag(flags.products);
|
|
125
|
+
if (name === undefined &&
|
|
126
|
+
description === undefined &&
|
|
127
|
+
products === undefined) {
|
|
128
|
+
throw usageError('Nothing to change.', `Pass --name, --description or --products, e.g. "${cli()} teams groups update ${teamSlug} ${quote(groupRef)} --products linear,zendesk".`);
|
|
129
|
+
}
|
|
130
|
+
return Object.assign(Object.assign(Object.assign({ kind: 'groups-update', team: teamSlug, group: groupRef }, (name === undefined ? {} : { name })), (description === undefined
|
|
131
|
+
? {}
|
|
132
|
+
: { description: description === '' ? null : description })), (products === undefined ? {} : { products }));
|
|
133
|
+
}
|
|
134
|
+
if (verb === 'delete') {
|
|
135
|
+
const [team, group, ...extra] = operands;
|
|
136
|
+
noExtra(extra);
|
|
137
|
+
const teamSlug = slugOf(team, 'team');
|
|
138
|
+
const groupRef = groupOf(group, teamSlug);
|
|
139
|
+
return Object.assign({ kind: 'groups-delete', team: teamSlug, group: groupRef }, confirmUnless(args.yes, {
|
|
140
|
+
what: `Deleting the group ${groupRef} of ${teamSlug} takes away what it narrows: its members see what the team's other groups (or the whole team) give them.`,
|
|
141
|
+
command: `${cli()} teams groups delete ${teamSlug} ${quote(groupRef)} --yes`,
|
|
142
|
+
question: `Delete the group ${groupRef} of ${teamSlug}? [y/N]`,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
if (verb === 'members') {
|
|
146
|
+
const [action, team, group, email, ...extra] = operands;
|
|
147
|
+
noExtra(extra);
|
|
148
|
+
if (action !== 'add' && action !== 'remove') {
|
|
149
|
+
throw usageError(action
|
|
150
|
+
? `Unknown teams groups members subcommand "${action}".`
|
|
151
|
+
: 'teams groups members needs add or remove.', GROUPS_USAGE);
|
|
152
|
+
}
|
|
153
|
+
const teamSlug = slugOf(team, 'team');
|
|
154
|
+
const groupRef = groupOf(group, teamSlug);
|
|
155
|
+
const address = emailOf(email);
|
|
156
|
+
return Object.assign({ kind: 'groups-member', team: teamSlug, group: groupRef, email: address, member: action === 'add' }, (action === 'remove'
|
|
157
|
+
? confirmUnless(args.yes, {
|
|
158
|
+
what: `Taking ${address} out of ${groupRef} changes what they see on ${teamSlug}.`,
|
|
159
|
+
command: `${cli()} teams groups members remove ${teamSlug} ${quote(groupRef)} ${address} --yes`,
|
|
160
|
+
question: `Take ${address} out of ${groupRef} on ${teamSlug}? [y/N]`,
|
|
161
|
+
})
|
|
162
|
+
: {}));
|
|
163
|
+
}
|
|
164
|
+
throw usageError(verb
|
|
165
|
+
? `Unknown teams groups subcommand "${verb}".`
|
|
166
|
+
: 'teams groups needs a subcommand.', GROUPS_USAGE);
|
|
167
|
+
}
|
|
168
|
+
export async function runLifecycle(ctx, args, plan) {
|
|
169
|
+
var _a;
|
|
170
|
+
var _b, _c, _d;
|
|
171
|
+
if (plan.confirm)
|
|
172
|
+
await confirmChange(ctx, args, plan.confirm);
|
|
173
|
+
const env = resolveAuthEnvironment(ctx, args.host, args.env, {
|
|
174
|
+
projectDir: args.dir,
|
|
175
|
+
});
|
|
176
|
+
const resolved = await resolveSourceTarget(env, requireSession(env), args);
|
|
177
|
+
const client = new TeamsClient(env, resolved.session);
|
|
178
|
+
const host = env.host;
|
|
179
|
+
const source = resolved.source;
|
|
180
|
+
const team = { source, team: plan.team };
|
|
181
|
+
switch (plan.kind) {
|
|
182
|
+
case 'resend': {
|
|
183
|
+
const done = await client.call((token) => resendTeamInvitation(host, token, Object.assign(Object.assign({}, team), { email: plan.email })));
|
|
184
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
185
|
+
title: done.action === 'resent'
|
|
186
|
+
? `Resent ${done.email}'s invitation to ${done.team.slug}`
|
|
187
|
+
: `Renewed ${done.email}'s expired invitation to ${done.team.slug}`,
|
|
188
|
+
rows: [
|
|
189
|
+
['Role', done.role],
|
|
190
|
+
['Expires', done.expiresAt.slice(0, 10)],
|
|
191
|
+
],
|
|
192
|
+
notes: [
|
|
193
|
+
done.action === 'resent'
|
|
194
|
+
? 'A fresh invitation email is on its way; the earlier link stops working.'
|
|
195
|
+
: 'The link they already have works again; no new email is sent for a renewal.',
|
|
196
|
+
],
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
case 'leave': {
|
|
200
|
+
const done = await client.call((token) => leaveTeam(host, token, team));
|
|
201
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
202
|
+
title: `You left ${done.team.slug}`,
|
|
203
|
+
rows: [['Your role was', done.role]],
|
|
204
|
+
notes: [
|
|
205
|
+
`As the Source admin you still manage it from here ("${cli()} teams show ${done.team.slug}").`,
|
|
206
|
+
],
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
case 'groups-list': {
|
|
210
|
+
const list = await client.call((token) => listTeamGroups(host, token, team));
|
|
211
|
+
return ctx.succeed(Object.assign({ host }, list), ctx.ui.result({
|
|
212
|
+
title: list.groups.length === 0
|
|
213
|
+
? `${list.team.slug} has no groups`
|
|
214
|
+
: `Groups of ${list.team.slug}`,
|
|
215
|
+
tone: list.groups.length === 0 ? 'warn' : 'ok',
|
|
216
|
+
rows: list.groups.map(groupRow),
|
|
217
|
+
notes: [
|
|
218
|
+
`A group narrows what its members see to its products. "${cli()} teams groups create ${list.team.slug} <name> --products <slug,slug>" adds one.`,
|
|
219
|
+
],
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
case 'groups-create': {
|
|
223
|
+
const done = await client.call((token) => createTeamGroup(host, token, team, Object.assign(Object.assign({ name: plan.name }, (plan.description === undefined
|
|
224
|
+
? {}
|
|
225
|
+
: { description: plan.description })), (plan.products === undefined ? {} : { products: plan.products }))));
|
|
226
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
227
|
+
title: `Created group ${done.group.name} on ${done.team.slug}`,
|
|
228
|
+
rows: [groupRow(done.group)],
|
|
229
|
+
notes: [
|
|
230
|
+
`Add members with "${cli()} teams groups members add ${done.team.slug} ${quote(done.group.name)} <email>".`,
|
|
231
|
+
],
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
case 'groups-update': {
|
|
235
|
+
const done = await client.call((token) => updateTeamGroup(host, token, Object.assign(Object.assign({}, team), { group: plan.group }), Object.assign(Object.assign(Object.assign({}, (plan.name === undefined ? {} : { name: plan.name })), (plan.description === undefined
|
|
236
|
+
? {}
|
|
237
|
+
: { description: plan.description })), (plan.products === undefined ? {} : { products: plan.products }))));
|
|
238
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
239
|
+
title: `Updated group ${done.group.name} on ${done.team.slug}`,
|
|
240
|
+
rows: [groupRow(done.group)],
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
case 'groups-delete': {
|
|
244
|
+
const done = await client.call((token) => deleteTeamGroup(host, token, Object.assign(Object.assign({}, team), { group: plan.group })));
|
|
245
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
246
|
+
title: `Deleted group ${done.group.name} from ${done.team.slug}`,
|
|
247
|
+
tone: 'warn',
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
case 'groups-member': {
|
|
251
|
+
const done = await client.call((token) => setTeamGroupMember(host, token, Object.assign(Object.assign({}, team), { group: plan.group, email: plan.email }), plan.member));
|
|
252
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
253
|
+
title: !done.changed
|
|
254
|
+
? `${done.email} was already ${plan.member ? 'in' : 'not in'} ${done.group.name}`
|
|
255
|
+
: plan.member
|
|
256
|
+
? `Added ${done.email} to ${done.group.name}`
|
|
257
|
+
: `Took ${done.email} out of ${done.group.name}`,
|
|
258
|
+
rows: [groupRow(done.group)],
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
case 'health': {
|
|
262
|
+
if (plan.mine && plan.product) {
|
|
263
|
+
const product = plan.product;
|
|
264
|
+
const done = await client.call((token) => ownTeamHealth(host, token, Object.assign(Object.assign({}, team), { product }), plan.run));
|
|
265
|
+
return ctx.succeed(Object.assign(Object.assign({ host }, done), { mine: true }), ctx.ui.result({
|
|
266
|
+
title: `Your ${done.integration.productName} connection: ${done.integration.status}`,
|
|
267
|
+
tone: toneOf(done.integration),
|
|
268
|
+
rows: healthRows(done.integration),
|
|
269
|
+
notes: plan.run
|
|
270
|
+
? []
|
|
271
|
+
: [
|
|
272
|
+
`"${cli()} teams health ${done.team.slug} ${product} --mine --run" checks it now.`,
|
|
273
|
+
],
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
if (plan.run && plan.product) {
|
|
277
|
+
const product = plan.product;
|
|
278
|
+
const done = await client.call((token) => runTeamHealthCheck(host, token, Object.assign(Object.assign({}, team), { product })));
|
|
279
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
280
|
+
title: `${done.integration.productName} on ${done.team.slug}: ${done.integration.status}`,
|
|
281
|
+
tone: toneOf(done.integration),
|
|
282
|
+
rows: healthRows(done.integration),
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
const list = await client.call((token) => listTeamHealth(host, token, team));
|
|
286
|
+
const rows = plan.product
|
|
287
|
+
? list.integrations.filter((row) => row.slug === plan.product)
|
|
288
|
+
: list.integrations;
|
|
289
|
+
if (plan.product && rows.length === 0) {
|
|
290
|
+
throw invalidStateError(`${list.team.slug} has not connected ${plan.product}, so it has no health to show.`, `"${cli()} teams health ${list.team.slug}" lists what it has connected.`);
|
|
291
|
+
}
|
|
292
|
+
const data = Object.assign(Object.assign({ host }, list), { integrations: rows });
|
|
293
|
+
if (plan.product) {
|
|
294
|
+
const row = rows[0];
|
|
295
|
+
return ctx.succeed(data, ctx.ui.result({
|
|
296
|
+
title: `${row.productName} on ${list.team.slug}: ${row.status}`,
|
|
297
|
+
tone: toneOf(row),
|
|
298
|
+
rows: healthRows(row),
|
|
299
|
+
notes: [
|
|
300
|
+
`"${cli()} teams health ${list.team.slug} ${row.slug} --run" checks it now.`,
|
|
301
|
+
],
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
return ctx.succeed(data, ctx.ui.result({
|
|
305
|
+
title: rows.length === 0
|
|
306
|
+
? `${list.team.slug} has no connected integrations`
|
|
307
|
+
: `Integration health on ${list.team.slug}`,
|
|
308
|
+
tone: rows.length === 0
|
|
309
|
+
? 'warn'
|
|
310
|
+
: rows.some((row) => row.status === 'unhealthy')
|
|
311
|
+
? 'err'
|
|
312
|
+
: rows.some((row) => row.status !== 'healthy')
|
|
313
|
+
? 'warn'
|
|
314
|
+
: 'ok',
|
|
315
|
+
rows: rows.map((row) => [
|
|
316
|
+
row.slug,
|
|
317
|
+
`${row.status}${row.statusReason ? ` · ${row.statusReason}` : ''}${row.lastCheckedAt ? ` · checked ${row.lastCheckedAt.slice(0, 10)}` : ' · never checked'}`,
|
|
318
|
+
]),
|
|
319
|
+
notes: rows.length === 0
|
|
320
|
+
? []
|
|
321
|
+
: [
|
|
322
|
+
`"${cli()} teams health ${list.team.slug} <product> --run" checks one now.`,
|
|
323
|
+
],
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
case 'workspace-list': {
|
|
327
|
+
const list = await client.call((token) => listTeamWorkspaces(host, token, team));
|
|
328
|
+
return ctx.succeed(Object.assign({ host }, list), ctx.ui.result({
|
|
329
|
+
title: list.workspaces.length === 0
|
|
330
|
+
? `${list.team.slug} has no workspaces`
|
|
331
|
+
: `Workspaces of ${list.team.slug}`,
|
|
332
|
+
tone: list.workspaces.length === 0 ? 'warn' : 'ok',
|
|
333
|
+
rows: list.workspaces.map((workspace) => { var _a; return [
|
|
334
|
+
workspace.slug,
|
|
335
|
+
[
|
|
336
|
+
(_a = workspace.label) !== null && _a !== void 0 ? _a : workspace.name,
|
|
337
|
+
...(workspace.isDefault ? ['default'] : []),
|
|
338
|
+
...(workspace.visible ? [] : ['hidden by the team']),
|
|
339
|
+
].join(' · '),
|
|
340
|
+
]; }),
|
|
341
|
+
notes: [
|
|
342
|
+
`"${cli()} teams workspace default ${list.team.slug} <workspace>" sets the one the team lands on.`,
|
|
343
|
+
],
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
case 'workspace-default': {
|
|
347
|
+
const done = await client.call((token) => setTeamDefaultWorkspace(host, token, team, plan.workspace));
|
|
348
|
+
return ctx.succeed(Object.assign({ host }, done), ctx.ui.result({
|
|
349
|
+
title: done.changed
|
|
350
|
+
? `${done.team.slug} now lands on ${(_b = done.workspace.label) !== null && _b !== void 0 ? _b : done.workspace.name}`
|
|
351
|
+
: `${(_c = done.workspace.label) !== null && _c !== void 0 ? _c : done.workspace.name} is already ${done.team.slug}'s default`,
|
|
352
|
+
rows: [['Was', (_d = (_a = done.previous) === null || _a === void 0 ? void 0 : _a.slug) !== null && _d !== void 0 ? _d : '—']],
|
|
353
|
+
notes: ['A member who picked their own default keeps theirs.'],
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function confirmUnless(yes, confirm) {
|
|
359
|
+
return yes ? {} : { confirm };
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* A change that takes something away: asked at a terminal; a script, an
|
|
363
|
+
* agent run or `--json` passes `--yes` instead. Asked before any request.
|
|
364
|
+
*/
|
|
365
|
+
export async function confirmChange(ctx, args, confirm) {
|
|
366
|
+
if (!canPrompt(ctx, args)) {
|
|
367
|
+
throw invalidStateError(confirm.what, `Re-run with --yes: ${confirm.command}`);
|
|
368
|
+
}
|
|
369
|
+
const { ask } = args;
|
|
370
|
+
const confirmed = ask
|
|
371
|
+
? /^y(es)?$/i.test((await ask(confirm.question)).trim())
|
|
372
|
+
: await promptYesNo(confirm.question);
|
|
373
|
+
if (!confirmed) {
|
|
374
|
+
throw invalidStateError(`Not confirmed: ${confirm.what}`, 'Nothing was changed.');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function groupRow(group) {
|
|
378
|
+
var _a;
|
|
379
|
+
return [
|
|
380
|
+
group.name,
|
|
381
|
+
[
|
|
382
|
+
(_a = group.description) !== null && _a !== void 0 ? _a : 'no description',
|
|
383
|
+
`members: ${group.members.length === 0 ? 'none' : group.members.map((member) => { var _a; return (_a = member.email) !== null && _a !== void 0 ? _a : member.userId; }).join(', ')}`,
|
|
384
|
+
`products: ${group.products.length === 0 ? 'none (no narrowing)' : group.products.map((product) => product.slug).join(', ')}`,
|
|
385
|
+
],
|
|
386
|
+
];
|
|
387
|
+
}
|
|
388
|
+
function healthRows(row) {
|
|
389
|
+
var _a, _b, _c;
|
|
390
|
+
return [
|
|
391
|
+
['Status', row.status],
|
|
392
|
+
...(row.statusReason ? [['Why', row.statusReason]] : []),
|
|
393
|
+
['Last checked', (_a = row.lastCheckedAt) !== null && _a !== void 0 ? _a : 'never'],
|
|
394
|
+
['Last success', (_b = row.lastSuccessAt) !== null && _b !== void 0 ? _b : '—'],
|
|
395
|
+
['Last failure', (_c = row.lastFailureAt) !== null && _c !== void 0 ? _c : '—'],
|
|
396
|
+
...(row.lastErrorMessage
|
|
397
|
+
? [['Last error', row.lastErrorMessage]]
|
|
398
|
+
: []),
|
|
399
|
+
...(row.tokenExpiresAt
|
|
400
|
+
? [['Token expires', row.tokenExpiresAt]]
|
|
401
|
+
: []),
|
|
402
|
+
];
|
|
403
|
+
}
|
|
404
|
+
function toneOf(row) {
|
|
405
|
+
return row.status === 'healthy'
|
|
406
|
+
? 'ok'
|
|
407
|
+
: row.status === 'unhealthy'
|
|
408
|
+
? 'err'
|
|
409
|
+
: 'warn';
|
|
410
|
+
}
|
|
411
|
+
function stringFlag(value) {
|
|
412
|
+
return typeof value === 'string' ? value.trim() : undefined;
|
|
413
|
+
}
|
|
414
|
+
/** `--products a,b`: the list; `--products none` (or empty): no products. */
|
|
415
|
+
function productsFlag(value) {
|
|
416
|
+
if (typeof value !== 'string')
|
|
417
|
+
return undefined;
|
|
418
|
+
const trimmed = value.trim().toLowerCase();
|
|
419
|
+
if (trimmed === '' || trimmed === 'none')
|
|
420
|
+
return [];
|
|
421
|
+
return [
|
|
422
|
+
...new Set(trimmed
|
|
423
|
+
.split(',')
|
|
424
|
+
.map((slug) => slug.trim())
|
|
425
|
+
.filter(Boolean)
|
|
426
|
+
.map((slug) => slugOf(slug, 'product'))),
|
|
427
|
+
];
|
|
428
|
+
}
|
|
429
|
+
function groupOf(value, team) {
|
|
430
|
+
const group = value === null || value === void 0 ? void 0 : value.trim();
|
|
431
|
+
if (group)
|
|
432
|
+
return group;
|
|
433
|
+
throw usageError('Name the group, by name or id.', `"${cli()} teams groups list ${team}" lists them.`);
|
|
434
|
+
}
|
|
435
|
+
function quote(value) {
|
|
436
|
+
return /^[A-Za-z0-9._-]+$/.test(value) ? value : `"${value}"`;
|
|
437
|
+
}
|
|
438
|
+
function noExtra(extra) {
|
|
439
|
+
if (extra.length > 0) {
|
|
440
|
+
throw usageError(`Unexpected argument "${extra[0]}".`, 'See "ekanos teams --help".');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function slugOf(value, what) {
|
|
444
|
+
const slug = value === null || value === void 0 ? void 0 : value.trim().toLowerCase();
|
|
445
|
+
if (slug && /^[a-z0-9][a-z0-9-]*$/.test(slug))
|
|
446
|
+
return slug;
|
|
447
|
+
throw usageError(value ? `"${value}" is not a ${what} slug.` : `Name the ${what}.`, what === 'team'
|
|
448
|
+
? '"ekanos teams list" lists the teams, e.g. "ekanos teams health bank-a".'
|
|
449
|
+
: '"ekanos catalog" lists the products, e.g. "ekanos teams health bank-a zendesk".');
|
|
450
|
+
}
|
|
451
|
+
function emailOf(value) {
|
|
452
|
+
const email = value === null || value === void 0 ? void 0 : value.trim().toLowerCase();
|
|
453
|
+
if (email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
|
|
454
|
+
return email;
|
|
455
|
+
throw usageError(value ? `"${value}" is not an email address.` : 'Name the person by email.', 'e.g. "ekanos teams members resend bank-a ana@bank-a.com".');
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=teams-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-lifecycle.js","sourceRoot":"","sources":["../../src/commands/teams-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { requireSession, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { cli } from '../invocation';\nimport { resolveSourceTarget } from '../source-target';\nimport {\n type TeamGroup,\n type TeamHealthRow,\n createTeamGroup,\n deleteTeamGroup,\n leaveTeam,\n listTeamGroups,\n listTeamHealth,\n listTeamWorkspaces,\n ownTeamHealth,\n resendTeamInvitation,\n runTeamHealthCheck,\n setTeamDefaultWorkspace,\n setTeamGroupMember,\n updateTeamGroup,\n} from '../team-lifecycle-api';\nimport type { ResultRow } from '../ui';\nimport { promptYesNo } from './publish';\nimport { TeamsClient, type TeamsArgs, canPrompt } from './teams';\n\n/**\n * A team's lifecycle from the terminal, for the Source admin:\n * `teams members resend`, `teams leave`, `teams groups …`, `teams health`\n * and `teams workspace …`. Each is parsed before a session is touched\n * (`parseLifecycle`), and a change that takes something away is confirmed\n * first — asked at a terminal, `--yes` in a script or agent run — so a usage\n * error or an unconfirmed change never costs a request. The host does the\n * rest through the services the app's own screens use, with an Admin Audit\n * row per write.\n *\n * Ownership transfer and team deletion are not here: in the app they are\n * the primary owner's alone, confirmed by a code emailed to them.\n */\n\nexport const LIFECYCLE_SUBCOMMANDS = [\n 'leave',\n 'groups',\n 'health',\n 'workspace',\n] as const;\ntype LifecycleSubcommand = (typeof LIFECYCLE_SUBCOMMANDS)[number];\n\n/** `teams members <verb>` verbs handled here rather than in onboarding. */\nexport const LIFECYCLE_MEMBER_VERBS = ['resend'] as const;\n\n/** What an unconfirmed change says, and the question a terminal is asked. */\nexport interface Confirmation {\n what: string;\n command: string;\n question: string;\n}\n\nexport type LifecyclePlan = (\n | { kind: 'resend'; team: string; email: string }\n | { kind: 'leave'; team: string }\n | { kind: 'groups-list'; team: string }\n | {\n kind: 'groups-create';\n team: string;\n name: string;\n description?: string;\n products?: string[];\n }\n | {\n kind: 'groups-update';\n team: string;\n group: string;\n name?: string;\n description?: string | null;\n products?: string[];\n }\n | { kind: 'groups-delete'; team: string; group: string }\n | {\n kind: 'groups-member';\n team: string;\n group: string;\n email: string;\n member: boolean;\n }\n | {\n kind: 'health';\n team: string;\n product?: string;\n run: boolean;\n /** `--mine`: the caller's own personal connection to <product>. */\n mine?: boolean;\n }\n | { kind: 'workspace-list'; team: string }\n | { kind: 'workspace-default'; team: string; workspace: string }\n) & { confirm?: Confirmation };\n\nexport function isLifecycleSubcommand(\n value: string | undefined,\n): value is LifecycleSubcommand {\n return (LIFECYCLE_SUBCOMMANDS as readonly string[]).includes(value ?? '');\n}\n\nexport function isLifecycleMemberVerb(value: string | undefined): boolean {\n return (LIFECYCLE_MEMBER_VERBS as readonly string[]).includes(value ?? '');\n}\n\nconst GROUPS_USAGE =\n 'Run one of: ekanos teams groups list <team>, ekanos teams groups create <team> <name> [--description <text>] [--products <slug,slug>], ekanos teams groups update <team> <group> [--name <name>] [--description <text>] [--products <slug,slug>|none], ekanos teams groups delete <team> <group> --yes, ekanos teams groups members add|remove <team> <group> <email>.';\nconst WORKSPACE_USAGE =\n 'Run \"ekanos teams workspace list <team>\" or \"ekanos teams workspace default <team> <workspace>\".';\n\n/**\n * `subcommand` is `members` (with `resend` first in `rest`) or one\n * of {@link LIFECYCLE_SUBCOMMANDS}.\n */\nexport function parseLifecycle(\n subcommand: string,\n rest: readonly string[],\n args: Pick<TeamsArgs, 'yes' | 'flags'>,\n): LifecyclePlan {\n const flags = args.flags ?? {};\n\n if (subcommand === 'members') {\n const [, team, email, ...extra] = rest;\n noExtra(extra);\n return {\n kind: 'resend',\n team: slugOf(team, 'team'),\n email: emailOf(email),\n };\n }\n\n if (subcommand === 'leave') {\n const [team, ...extra] = rest;\n noExtra(extra);\n const teamSlug = slugOf(team, 'team');\n return {\n kind: 'leave',\n team: teamSlug,\n ...confirmUnless(args.yes, {\n what: `Leaving ${teamSlug} ends your own membership of it; only an invitation brings you back.`,\n command: `${cli()} teams leave ${teamSlug} --yes`,\n question: `Leave ${teamSlug}? [y/N]`,\n }),\n };\n }\n\n if (subcommand === 'health') {\n const [team, product, ...extra] = rest;\n noExtra(extra);\n const run = flags.run === true;\n const productSlug =\n product === undefined ? undefined : slugOf(product, 'product');\n if (run && !productSlug) {\n throw usageError(\n 'teams health --run checks one integration; name it.',\n 'Run \"ekanos teams health <team> <product> --run\"; \"ekanos teams health <team>\" lists them.',\n );\n }\n const mine = flags.mine === true;\n if (mine && !productSlug) {\n throw usageError(\n 'teams health --mine shows your own connection to one integration; name it.',\n 'Run \"ekanos teams health <team> <product> --mine [--run]\".',\n );\n }\n return {\n kind: 'health',\n team: slugOf(team, 'team'),\n ...(productSlug ? { product: productSlug } : {}),\n run,\n ...(mine ? { mine } : {}),\n };\n }\n\n if (subcommand === 'workspace') {\n const [verb, team, workspace, ...extra] = rest;\n noExtra(extra);\n if (verb === 'list') {\n if (workspace !== undefined) noExtra([workspace]);\n return { kind: 'workspace-list', team: slugOf(team, 'team') };\n }\n if (verb === 'default') {\n const teamSlug = slugOf(team, 'team');\n if (!workspace?.trim()) {\n throw usageError(\n 'Name the workspace.',\n `Run \"${cli()} teams workspace default ${teamSlug} <workspace>\"; \"${cli()} teams workspace list ${teamSlug}\" lists them.`,\n );\n }\n return {\n kind: 'workspace-default',\n team: teamSlug,\n workspace: workspace.trim().toLowerCase(),\n };\n }\n throw usageError(\n verb\n ? `Unknown teams workspace subcommand \"${verb}\".`\n : 'teams workspace needs a subcommand.',\n WORKSPACE_USAGE,\n );\n }\n\n const [verb, ...operands] = rest;\n if (verb === 'list') {\n const [team, ...extra] = operands;\n noExtra(extra);\n return { kind: 'groups-list', team: slugOf(team, 'team') };\n }\n if (verb === 'create') {\n const [team, name, ...extra] = operands;\n noExtra(extra);\n const teamSlug = slugOf(team, 'team');\n if (!name?.trim()) {\n throw usageError(\n 'Name the group.',\n `Run \"${cli()} teams groups create ${teamSlug} \\\\\"Loan officers\\\\\"\".`,\n );\n }\n const description = stringFlag(flags.description);\n const products = productsFlag(flags.products);\n return {\n kind: 'groups-create',\n team: teamSlug,\n name: name.trim(),\n ...(description === undefined ? {} : { description }),\n ...(products === undefined ? {} : { products }),\n };\n }\n if (verb === 'update') {\n const [team, group, ...extra] = operands;\n noExtra(extra);\n const teamSlug = slugOf(team, 'team');\n const groupRef = groupOf(group, teamSlug);\n const name = stringFlag(flags.name);\n const description = stringFlag(flags.description);\n const products = productsFlag(flags.products);\n if (\n name === undefined &&\n description === undefined &&\n products === undefined\n ) {\n throw usageError(\n 'Nothing to change.',\n `Pass --name, --description or --products, e.g. \"${cli()} teams groups update ${teamSlug} ${quote(groupRef)} --products linear,zendesk\".`,\n );\n }\n return {\n kind: 'groups-update',\n team: teamSlug,\n group: groupRef,\n ...(name === undefined ? {} : { name }),\n ...(description === undefined\n ? {}\n : { description: description === '' ? null : description }),\n ...(products === undefined ? {} : { products }),\n };\n }\n if (verb === 'delete') {\n const [team, group, ...extra] = operands;\n noExtra(extra);\n const teamSlug = slugOf(team, 'team');\n const groupRef = groupOf(group, teamSlug);\n return {\n kind: 'groups-delete',\n team: teamSlug,\n group: groupRef,\n ...confirmUnless(args.yes, {\n what: `Deleting the group ${groupRef} of ${teamSlug} takes away what it narrows: its members see what the team's other groups (or the whole team) give them.`,\n command: `${cli()} teams groups delete ${teamSlug} ${quote(groupRef)} --yes`,\n question: `Delete the group ${groupRef} of ${teamSlug}? [y/N]`,\n }),\n };\n }\n if (verb === 'members') {\n const [action, team, group, email, ...extra] = operands;\n noExtra(extra);\n if (action !== 'add' && action !== 'remove') {\n throw usageError(\n action\n ? `Unknown teams groups members subcommand \"${action}\".`\n : 'teams groups members needs add or remove.',\n GROUPS_USAGE,\n );\n }\n const teamSlug = slugOf(team, 'team');\n const groupRef = groupOf(group, teamSlug);\n const address = emailOf(email);\n return {\n kind: 'groups-member',\n team: teamSlug,\n group: groupRef,\n email: address,\n member: action === 'add',\n ...(action === 'remove'\n ? confirmUnless(args.yes, {\n what: `Taking ${address} out of ${groupRef} changes what they see on ${teamSlug}.`,\n command: `${cli()} teams groups members remove ${teamSlug} ${quote(groupRef)} ${address} --yes`,\n question: `Take ${address} out of ${groupRef} on ${teamSlug}? [y/N]`,\n })\n : {}),\n };\n }\n throw usageError(\n verb\n ? `Unknown teams groups subcommand \"${verb}\".`\n : 'teams groups needs a subcommand.',\n GROUPS_USAGE,\n );\n}\n\nexport async function runLifecycle(\n ctx: CliContext,\n args: TeamsArgs,\n plan: LifecyclePlan,\n): Promise<ExitCode> {\n if (plan.confirm) await confirmChange(ctx, args, plan.confirm);\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(env, requireSession(env), args);\n const client = new TeamsClient(env, resolved.session);\n const host = env.host;\n const source = resolved.source;\n const team = { source, team: plan.team };\n\n switch (plan.kind) {\n case 'resend': {\n const done = await client.call((token) =>\n resendTeamInvitation(host, token, { ...team, email: plan.email }),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title:\n done.action === 'resent'\n ? `Resent ${done.email}'s invitation to ${done.team.slug}`\n : `Renewed ${done.email}'s expired invitation to ${done.team.slug}`,\n rows: [\n ['Role', done.role],\n ['Expires', done.expiresAt.slice(0, 10)],\n ],\n notes: [\n done.action === 'resent'\n ? 'A fresh invitation email is on its way; the earlier link stops working.'\n : 'The link they already have works again; no new email is sent for a renewal.',\n ],\n }),\n );\n }\n\n case 'leave': {\n const done = await client.call((token) => leaveTeam(host, token, team));\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: `You left ${done.team.slug}`,\n rows: [['Your role was', done.role]],\n notes: [\n `As the Source admin you still manage it from here (\"${cli()} teams show ${done.team.slug}\").`,\n ],\n }),\n );\n }\n\n case 'groups-list': {\n const list = await client.call((token) =>\n listTeamGroups(host, token, team),\n );\n return ctx.succeed(\n { host, ...list },\n ctx.ui.result({\n title:\n list.groups.length === 0\n ? `${list.team.slug} has no groups`\n : `Groups of ${list.team.slug}`,\n tone: list.groups.length === 0 ? 'warn' : 'ok',\n rows: list.groups.map(groupRow),\n notes: [\n `A group narrows what its members see to its products. \"${cli()} teams groups create ${list.team.slug} <name> --products <slug,slug>\" adds one.`,\n ],\n }),\n );\n }\n\n case 'groups-create': {\n const done = await client.call((token) =>\n createTeamGroup(host, token, team, {\n name: plan.name,\n ...(plan.description === undefined\n ? {}\n : { description: plan.description }),\n ...(plan.products === undefined ? {} : { products: plan.products }),\n }),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: `Created group ${done.group.name} on ${done.team.slug}`,\n rows: [groupRow(done.group)],\n notes: [\n `Add members with \"${cli()} teams groups members add ${done.team.slug} ${quote(done.group.name)} <email>\".`,\n ],\n }),\n );\n }\n\n case 'groups-update': {\n const done = await client.call((token) =>\n updateTeamGroup(\n host,\n token,\n { ...team, group: plan.group },\n {\n ...(plan.name === undefined ? {} : { name: plan.name }),\n ...(plan.description === undefined\n ? {}\n : { description: plan.description }),\n ...(plan.products === undefined ? {} : { products: plan.products }),\n },\n ),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: `Updated group ${done.group.name} on ${done.team.slug}`,\n rows: [groupRow(done.group)],\n }),\n );\n }\n\n case 'groups-delete': {\n const done = await client.call((token) =>\n deleteTeamGroup(host, token, { ...team, group: plan.group }),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: `Deleted group ${done.group.name} from ${done.team.slug}`,\n tone: 'warn',\n }),\n );\n }\n\n case 'groups-member': {\n const done = await client.call((token) =>\n setTeamGroupMember(\n host,\n token,\n { ...team, group: plan.group, email: plan.email },\n plan.member,\n ),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: !done.changed\n ? `${done.email} was already ${plan.member ? 'in' : 'not in'} ${done.group.name}`\n : plan.member\n ? `Added ${done.email} to ${done.group.name}`\n : `Took ${done.email} out of ${done.group.name}`,\n rows: [groupRow(done.group)],\n }),\n );\n }\n\n case 'health': {\n if (plan.mine && plan.product) {\n const product = plan.product;\n const done = await client.call((token) =>\n ownTeamHealth(host, token, { ...team, product }, plan.run),\n );\n return ctx.succeed(\n { host, ...done, mine: true },\n ctx.ui.result({\n title: `Your ${done.integration.productName} connection: ${done.integration.status}`,\n tone: toneOf(done.integration),\n rows: healthRows(done.integration),\n notes: plan.run\n ? []\n : [\n `\"${cli()} teams health ${done.team.slug} ${product} --mine --run\" checks it now.`,\n ],\n }),\n );\n }\n if (plan.run && plan.product) {\n const product = plan.product;\n const done = await client.call((token) =>\n runTeamHealthCheck(host, token, { ...team, product }),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: `${done.integration.productName} on ${done.team.slug}: ${done.integration.status}`,\n tone: toneOf(done.integration),\n rows: healthRows(done.integration),\n }),\n );\n }\n const list = await client.call((token) =>\n listTeamHealth(host, token, team),\n );\n const rows = plan.product\n ? list.integrations.filter((row) => row.slug === plan.product)\n : list.integrations;\n if (plan.product && rows.length === 0) {\n throw invalidStateError(\n `${list.team.slug} has not connected ${plan.product}, so it has no health to show.`,\n `\"${cli()} teams health ${list.team.slug}\" lists what it has connected.`,\n );\n }\n const data = { host, ...list, integrations: rows };\n if (plan.product) {\n const row = rows[0]!;\n return ctx.succeed(\n data,\n ctx.ui.result({\n title: `${row.productName} on ${list.team.slug}: ${row.status}`,\n tone: toneOf(row),\n rows: healthRows(row),\n notes: [\n `\"${cli()} teams health ${list.team.slug} ${row.slug} --run\" checks it now.`,\n ],\n }),\n );\n }\n return ctx.succeed(\n data,\n ctx.ui.result({\n title:\n rows.length === 0\n ? `${list.team.slug} has no connected integrations`\n : `Integration health on ${list.team.slug}`,\n tone:\n rows.length === 0\n ? 'warn'\n : rows.some((row) => row.status === 'unhealthy')\n ? 'err'\n : rows.some((row) => row.status !== 'healthy')\n ? 'warn'\n : 'ok',\n rows: rows.map((row): ResultRow => [\n row.slug,\n `${row.status}${row.statusReason ? ` · ${row.statusReason}` : ''}${row.lastCheckedAt ? ` · checked ${row.lastCheckedAt.slice(0, 10)}` : ' · never checked'}`,\n ]),\n notes:\n rows.length === 0\n ? []\n : [\n `\"${cli()} teams health ${list.team.slug} <product> --run\" checks one now.`,\n ],\n }),\n );\n }\n\n case 'workspace-list': {\n const list = await client.call((token) =>\n listTeamWorkspaces(host, token, team),\n );\n return ctx.succeed(\n { host, ...list },\n ctx.ui.result({\n title:\n list.workspaces.length === 0\n ? `${list.team.slug} has no workspaces`\n : `Workspaces of ${list.team.slug}`,\n tone: list.workspaces.length === 0 ? 'warn' : 'ok',\n rows: list.workspaces.map((workspace): ResultRow => [\n workspace.slug,\n [\n workspace.label ?? workspace.name,\n ...(workspace.isDefault ? ['default'] : []),\n ...(workspace.visible ? [] : ['hidden by the team']),\n ].join(' · '),\n ]),\n notes: [\n `\"${cli()} teams workspace default ${list.team.slug} <workspace>\" sets the one the team lands on.`,\n ],\n }),\n );\n }\n\n case 'workspace-default': {\n const done = await client.call((token) =>\n setTeamDefaultWorkspace(host, token, team, plan.workspace),\n );\n return ctx.succeed(\n { host, ...done },\n ctx.ui.result({\n title: done.changed\n ? `${done.team.slug} now lands on ${done.workspace.label ?? done.workspace.name}`\n : `${done.workspace.label ?? done.workspace.name} is already ${done.team.slug}'s default`,\n rows: [['Was', done.previous?.slug ?? '—']],\n notes: ['A member who picked their own default keeps theirs.'],\n }),\n );\n }\n }\n}\n\nfunction confirmUnless(\n yes: boolean,\n confirm: Confirmation,\n): { confirm?: Confirmation } {\n return yes ? {} : { confirm };\n}\n\n/**\n * A change that takes something away: asked at a terminal; a script, an\n * agent run or `--json` passes `--yes` instead. Asked before any request.\n */\nexport async function confirmChange(\n ctx: CliContext,\n args: TeamsArgs,\n confirm: Confirmation,\n): Promise<void> {\n if (!canPrompt(ctx, args)) {\n throw invalidStateError(\n confirm.what,\n `Re-run with --yes: ${confirm.command}`,\n );\n }\n const { ask } = args;\n const confirmed = ask\n ? /^y(es)?$/i.test((await ask(confirm.question)).trim())\n : await promptYesNo(confirm.question);\n if (!confirmed) {\n throw invalidStateError(\n `Not confirmed: ${confirm.what}`,\n 'Nothing was changed.',\n );\n }\n}\n\nfunction groupRow(group: TeamGroup): ResultRow {\n return [\n group.name,\n [\n group.description ?? 'no description',\n `members: ${group.members.length === 0 ? 'none' : group.members.map((member) => member.email ?? member.userId).join(', ')}`,\n `products: ${group.products.length === 0 ? 'none (no narrowing)' : group.products.map((product) => product.slug).join(', ')}`,\n ],\n ];\n}\n\nfunction healthRows(row: TeamHealthRow): ResultRow[] {\n return [\n ['Status', row.status],\n ...(row.statusReason ? [['Why', row.statusReason] as const] : []),\n ['Last checked', row.lastCheckedAt ?? 'never'],\n ['Last success', row.lastSuccessAt ?? '—'],\n ['Last failure', row.lastFailureAt ?? '—'],\n ...(row.lastErrorMessage\n ? [['Last error', row.lastErrorMessage] as const]\n : []),\n ...(row.tokenExpiresAt\n ? [['Token expires', row.tokenExpiresAt] as const]\n : []),\n ];\n}\n\nfunction toneOf(row: TeamHealthRow) {\n return row.status === 'healthy'\n ? ('ok' as const)\n : row.status === 'unhealthy'\n ? ('err' as const)\n : ('warn' as const);\n}\n\nfunction stringFlag(value: unknown): string | undefined {\n return typeof value === 'string' ? value.trim() : undefined;\n}\n\n/** `--products a,b`: the list; `--products none` (or empty): no products. */\nfunction productsFlag(value: unknown): string[] | undefined {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim().toLowerCase();\n if (trimmed === '' || trimmed === 'none') return [];\n return [\n ...new Set(\n trimmed\n .split(',')\n .map((slug) => slug.trim())\n .filter(Boolean)\n .map((slug) => slugOf(slug, 'product')),\n ),\n ];\n}\n\nfunction groupOf(value: string | undefined, team: string): string {\n const group = value?.trim();\n if (group) return group;\n throw usageError(\n 'Name the group, by name or id.',\n `\"${cli()} teams groups list ${team}\" lists them.`,\n );\n}\n\nfunction quote(value: string): string {\n return /^[A-Za-z0-9._-]+$/.test(value) ? value : `\"${value}\"`;\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 health bank-a\".'\n : '\"ekanos catalog\" lists the products, e.g. \"ekanos teams health 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 resend bank-a ana@bank-a.com\".',\n );\n}\n"]}
|
|
@@ -16,6 +16,10 @@ export interface OnboardingFlags {
|
|
|
16
16
|
role?: string;
|
|
17
17
|
/** `connect --as <member-email>`: the member who signs in. */
|
|
18
18
|
as?: string;
|
|
19
|
+
/** `members add --self`: the caller joins. */
|
|
20
|
+
self?: boolean;
|
|
21
|
+
/** `disconnect --mine`: the caller's own personal connection. */
|
|
22
|
+
mine?: boolean;
|
|
19
23
|
yes: boolean;
|
|
20
24
|
}
|
|
21
25
|
export type OnboardingPlan = {
|
|
@@ -35,6 +39,15 @@ export type OnboardingPlan = {
|
|
|
35
39
|
team: string;
|
|
36
40
|
email: string;
|
|
37
41
|
role: string;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'members-add';
|
|
44
|
+
team: string;
|
|
45
|
+
target: {
|
|
46
|
+
self: true;
|
|
47
|
+
} | {
|
|
48
|
+
email: string;
|
|
49
|
+
};
|
|
50
|
+
role?: string;
|
|
38
51
|
} | {
|
|
39
52
|
kind: 'members-role';
|
|
40
53
|
team: string;
|
|
@@ -56,6 +69,7 @@ export type OnboardingPlan = {
|
|
|
56
69
|
kind: 'disconnect';
|
|
57
70
|
team: string;
|
|
58
71
|
product: string;
|
|
72
|
+
mine: boolean;
|
|
59
73
|
};
|
|
60
74
|
export declare function isOnboardingSubcommand(value: string): value is OnboardingSubcommand;
|
|
61
75
|
export declare function parseOnboarding(subcommand: OnboardingSubcommand, rest: readonly string[], flags: OnboardingFlags): OnboardingPlan;
|