@ekanos/cli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/apps-api.d.ts +3 -2
  2. package/dist/apps-api.js +1 -5
  3. package/dist/apps-api.js.map +1 -1
  4. package/dist/commands/agents-edit.js +1 -1
  5. package/dist/commands/agents-edit.js.map +1 -1
  6. package/dist/commands/apps.d.ts +19 -6
  7. package/dist/commands/apps.js +366 -43
  8. package/dist/commands/apps.js.map +1 -1
  9. package/dist/commands/knowledge.d.ts +37 -0
  10. package/dist/commands/knowledge.js +364 -0
  11. package/dist/commands/knowledge.js.map +1 -0
  12. package/dist/commands/routes.d.ts +23 -0
  13. package/dist/commands/routes.js +277 -0
  14. package/dist/commands/routes.js.map +1 -0
  15. package/dist/commands/sources.d.ts +4 -0
  16. package/dist/commands/sources.js +115 -1
  17. package/dist/commands/sources.js.map +1 -1
  18. package/dist/commands/team-agents-triggers.js +2 -1
  19. package/dist/commands/team-agents-triggers.js.map +1 -1
  20. package/dist/commands/teams-connect.d.ts +25 -0
  21. package/dist/commands/teams-connect.js +242 -0
  22. package/dist/commands/teams-connect.js.map +1 -0
  23. package/dist/commands/teams-onboard.d.ts +5 -2
  24. package/dist/commands/teams-onboard.js +12 -40
  25. package/dist/commands/teams-onboard.js.map +1 -1
  26. package/dist/commands/teams.d.ts +60 -5
  27. package/dist/commands/teams.js +141 -46
  28. package/dist/commands/teams.js.map +1 -1
  29. package/dist/connections-api.d.ts +125 -0
  30. package/dist/connections-api.js +156 -0
  31. package/dist/connections-api.js.map +1 -0
  32. package/dist/index.js +90 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/knowledge-api.d.ts +129 -0
  35. package/dist/knowledge-api.js +138 -0
  36. package/dist/knowledge-api.js.map +1 -0
  37. package/dist/routes-api.d.ts +105 -0
  38. package/dist/routes-api.js +147 -0
  39. package/dist/routes-api.js.map +1 -0
  40. package/dist/source-products-api.d.ts +59 -0
  41. package/dist/source-products-api.js +113 -0
  42. package/dist/source-products-api.js.map +1 -0
  43. package/dist/team-connect-api.d.ts +244 -0
  44. package/dist/team-connect-api.js +95 -0
  45. package/dist/team-connect-api.js.map +1 -0
  46. package/dist/teams-api.d.ts +1 -0
  47. package/dist/teams-api.js +1 -1
  48. package/dist/teams-api.js.map +1 -1
  49. package/package.json +2 -2
  50. package/templates/package.json.tmpl +4 -4
  51. package/templates/skills/build-agent/SKILL.md +18 -3
  52. package/templates/skills/build-agent/references/agent-json.md +29 -8
  53. package/templates/skills/build-agent/references/app-setup.md +2 -1
  54. package/templates/skills/build-agent/references/set-up-an-integration.md +76 -10
  55. package/templates/skills/build-agent/references/skills.md +7 -4
  56. package/templates/skills/build-agent/references/triggers.md +44 -15
  57. package/templates/skills/ekanos-platform/SKILL.md +40 -8
@@ -1,21 +1,33 @@
1
1
  import { setProviderApp, showProviderApp, } from '../apps-api.js';
2
2
  import { refreshStoredSession, requireSession, resolveAuthEnvironment, } from '../auth/session.js';
3
- import { authRequiredError, usageError, validationError } from '../errors.js';
3
+ import { deleteConnection, listConnections, showConnection, startConnection, } from '../connections-api.js';
4
+ import { authRequiredError, invalidStateError, usageError, validationError, } from '../errors.js';
4
5
  import { resolveSourceTarget } from '../source-target.js';
5
6
  import { promptYesNo } from './publish.js';
6
- export const APPS_SUBCOMMANDS = ['show', 'set'];
7
+ export const APPS_SUBCOMMANDS = [
8
+ 'show',
9
+ 'set',
10
+ 'list',
11
+ 'connect',
12
+ 'disconnect',
13
+ ];
7
14
  /**
8
15
  * `apps` — a Source's provider apps: the OAuth client an integration signs
9
16
  * every team in through (Linear's, say). `show` says whether the Source has
10
17
  * one and which callback URL to register; `set` takes the client id and
11
- * secret from the Source admin at the terminal — never from a flag, a file
12
- * or a non-interactive run, so an agent driving the CLI never handles the
13
- * secret.
18
+ * secret from the Source admin at the terminal — either on its own, Enter
19
+ * keeping the saved one — never from a flag, a file or a non-interactive
20
+ * run, so an agent driving the CLI never handles the secret.
14
21
  */
15
22
  export async function runApps(ctx, args) {
16
23
  var _a, _b, _c;
17
24
  const subcommand = parseSubcommand(args.subcommand);
25
+ if (subcommand === 'list')
26
+ return runList(ctx, args);
18
27
  const product = requireProduct(args.product, subcommand);
28
+ if (subcommand === 'connect' || subcommand === 'disconnect') {
29
+ return runConnection(ctx, args, subcommand, product);
30
+ }
19
31
  if (subcommand === 'set' && !canPrompt(ctx, args)) {
20
32
  throw usageError('apps set takes the client secret at a terminal, never in a script or an agent run.', `Run it yourself in your terminal: npx @ekanos/cli apps set ${product}${args.source ? ` --source ${args.source}` : ''}`);
21
33
  }
@@ -25,34 +37,37 @@ export async function runApps(ctx, args) {
25
37
  const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
26
38
  const client = new AppsClient(env, resolved.session);
27
39
  const source = resolved.source;
28
- const status = await client.call((token) => showProviderApp(env.host, token, source, product));
29
40
  if (subcommand === 'show') {
30
- return ctx.succeed(Object.assign({ host: env.host }, status), summary(ctx, status));
41
+ return showApp(ctx, env.host, client, source, product);
31
42
  }
43
+ const status = await client.call((token) => showProviderApp(env.host, token, source, product));
32
44
  ctx.log(walkthrough(status).join('\n'));
33
- if (status.configured) {
34
- const replace = await ((_a = args.confirm) !== null && _a !== void 0 ? _a : promptYesNo)(`${status.source.slug} already has ${status.product.name} credentials. Replace them? [y/N]`);
35
- if (!replace) {
36
- throw validationError('Nothing was changed.', `Run "ekanos apps show ${product}" to see the current state.`);
37
- }
38
- }
39
- const clientId = (await ((_b = args.ask) !== null && _b !== void 0 ? _b : promptLine)('Client ID: ')).trim();
40
- const clientSecret = (await ((_c = args.askSecret) !== null && _c !== void 0 ? _c : promptHidden)('Client secret (hidden): ')).trim();
41
- if (!clientId || !clientSecret) {
42
- throw validationError('Both the client ID and the client secret are needed.', `Run "ekanos apps set ${product}" again.`);
45
+ // Either credential on its own: Enter keeps what is saved. An older host
46
+ // sends no `fields`; then `configured` says whether both are saved.
47
+ const saved = (name) => { var _a; var _b; return (_b = (_a = status.fields) === null || _a === void 0 ? void 0 : _a.find((field) => field.name === name)) !== null && _b !== void 0 ? _b : {
48
+ set: status.configured,
49
+ value: null,
50
+ }; };
51
+ const savedId = saved('clientId');
52
+ const savedSecret = saved('clientSecret');
53
+ const clientId = (await ((_a = args.ask) !== null && _a !== void 0 ? _a : promptLine)(savedId.set
54
+ ? `Client ID (current: ${(_b = savedId.value) !== null && _b !== void 0 ? _b : 'saved'} — Enter to keep): `
55
+ : 'Client ID: ')).trim();
56
+ const clientSecret = (await ((_c = args.askSecret) !== null && _c !== void 0 ? _c : promptHidden)(savedSecret.set
57
+ ? 'Client secret (saved — Enter to keep, hidden): '
58
+ : 'Client secret (hidden): ')).trim();
59
+ if (!clientId && !clientSecret) {
60
+ throw validationError('Nothing was changed.', `Run "ekanos apps show ${product}" to see the current state.`);
43
61
  }
44
- const result = await client.call((token) => setProviderApp(env.host, token, {
45
- source,
46
- product,
47
- clientId,
48
- clientSecret,
49
- }));
62
+ const result = await client.call((token) => setProviderApp(env.host, token, Object.assign(Object.assign({ source,
63
+ product }, (clientId ? { clientId } : {})), (clientSecret ? { clientSecret } : {}))));
50
64
  return ctx.succeed(Object.assign({ host: env.host }, result), ctx.ui.result({
51
65
  title: result.replaced
52
- ? `Replaced the ${result.product.name} app`
66
+ ? `Updated the ${result.product.name} app`
53
67
  : `Set up the ${result.product.name} app`,
54
68
  rows: [
55
69
  ['Source', result.source.slug],
70
+ ...providerFieldRows(result),
56
71
  ['Teams can connect', result.configured ? 'yes' : 'not yet'],
57
72
  ['Callback URLs', callbackUrls(result)],
58
73
  ],
@@ -100,6 +115,7 @@ function summary(ctx, status) {
100
115
  ],
101
116
  ]
102
117
  : []),
118
+ ...providerFieldRows(status),
103
119
  [
104
120
  'Set up',
105
121
  status.configured
@@ -117,6 +133,228 @@ function summary(ctx, status) {
117
133
  ]),
118
134
  ].join('\n');
119
135
  }
136
+ /**
137
+ * `apps show <product>`: the Source provider app, and — for an integration
138
+ * that supports one — the Source connection (plan 241). A `teamApp`
139
+ * integration (Zendesk) has no provider app but can have a Source
140
+ * connection, so either half may be absent.
141
+ */
142
+ async function showApp(ctx, host, client, source, product) {
143
+ const [app, connection] = await Promise.all([
144
+ client
145
+ .call((token) => showProviderApp(host, token, source, product))
146
+ .then((status) => ({ ok: true, status }), (error) => ({ ok: false, error })),
147
+ // A host without the connections routes (before plan 241), or one that
148
+ // refuses, leaves `show` exactly as it was: the provider app alone.
149
+ client
150
+ .connection((token) => showConnection(host, token, source, product))
151
+ .catch(() => ({ status: 'unavailable' })),
152
+ ]);
153
+ if (!app.ok && connection.status !== 'ok')
154
+ throw app.error;
155
+ const parts = [];
156
+ if (app.ok)
157
+ parts.push(summary(ctx, app.status));
158
+ if (connection.status === 'ok') {
159
+ parts.push(connectionSummary(ctx, connection.data.connection, {
160
+ sourceSlug: connection.data.source.slug,
161
+ callbackUrls: connection.data.callbackUrls,
162
+ }));
163
+ }
164
+ return ctx.succeed(Object.assign(Object.assign({ host }, (app.ok ? app.status : {})), (connection.status === 'ok'
165
+ ? {
166
+ sourceConnection: connection.data.connection,
167
+ sourceConnectionCallbackUrls: connection.data.callbackUrls,
168
+ }
169
+ : {})), parts.join('\n'));
170
+ }
171
+ /** `apps list`: every integration the Source can connect once, with status. */
172
+ async function runList(ctx, args) {
173
+ const { env, client, source } = await openClient(ctx, args);
174
+ const result = await client.connection((token) => listConnections(env.host, token, source));
175
+ if (result.status !== 'ok') {
176
+ throw invalidStateError('The host could not list the connections.', 'Retry shortly.');
177
+ }
178
+ const { connections } = result.data;
179
+ const rows = connections.map((connection) => [
180
+ `${connection.name} (${connection.slug})`,
181
+ connectionState(connection),
182
+ ]);
183
+ return ctx.succeed(Object.assign({ host: env.host }, result.data), connections.length === 0
184
+ ? ctx.ui.hint(`No integration on ${result.data.source.slug} supports one connection for the whole Source yet.`)
185
+ : ctx.ui.result({
186
+ title: `Source connections on ${result.data.source.slug}`,
187
+ rows,
188
+ }));
189
+ }
190
+ function connectionState(connection) {
191
+ if (connection.connected) {
192
+ return [
193
+ 'connected',
194
+ connection.connectedAt ? ` ${connection.connectedAt}` : '',
195
+ connection.connectedBy ? ` by ${connection.connectedBy}` : '',
196
+ ].join('');
197
+ }
198
+ return connection.missing.length > 0
199
+ ? `not connected — app fields missing: ${connection.missing.join(', ')}`
200
+ : 'not connected';
201
+ }
202
+ /**
203
+ * `apps connect <product>`: connect the integration ONCE for the whole Source,
204
+ * for the Source's provided agents whose template says `owner: "source"`.
205
+ * Asks for any missing app fields at the terminal (secrets hidden, never from
206
+ * a flag), prints the provider's consent URL, then waits until the browser
207
+ * finishes. `apps disconnect <product>` removes it.
208
+ */
209
+ async function runConnection(ctx, args, subcommand, product) {
210
+ const { env, client, source } = await openClient(ctx, args);
211
+ if (subcommand === 'disconnect') {
212
+ return disconnect(ctx, args, client, env.host, source, product);
213
+ }
214
+ const shown = await client.connection((token) => showConnection(env.host, token, source, product));
215
+ if (shown.status === 'unsupported') {
216
+ throw invalidStateError(shown.message, `Each team connects ${product} itself, from the team's Integrations page.`);
217
+ }
218
+ if (shown.status !== 'ok') {
219
+ throw invalidStateError('The host could not read the connection.', 'Retry shortly.');
220
+ }
221
+ const current = shown.data.connection;
222
+ if (current.app === 'fields') {
223
+ ctx.log([
224
+ `${current.name} app for ${shown.data.source.slug} (one for the whole Source)`,
225
+ ` Register this redirect URL in the app: ${shown.data.callbackUrls.join(', ')}`,
226
+ ].join('\n'));
227
+ }
228
+ const fields = await askFields(ctx, args, current, product);
229
+ const started = await client.connection((token) => startConnection(env.host, token, Object.assign({ source,
230
+ product }, (Object.keys(fields).length > 0 ? { fields } : {}))));
231
+ if (started.status === 'not-configured') {
232
+ throw validationError(`${current.name} is not set up for the Source yet — missing: ${started.missing.join(', ')}.`, current.app === 'source-app'
233
+ ? `Run "ekanos apps set ${product}" to enter the Source's app first.`
234
+ : `Run "ekanos apps connect ${product}" again and fill every field.`);
235
+ }
236
+ if (started.status !== 'ok') {
237
+ throw invalidStateError('The host could not start the connection.', 'Retry shortly.');
238
+ }
239
+ ctx.log([
240
+ `Open this link and approve ${current.name} as the account the Source's agents should act as:`,
241
+ ` ${started.data.authorizeUrl}`,
242
+ ].join('\n'));
243
+ if (args.noWait || ctx.jsonMode) {
244
+ return ctx.succeed({
245
+ host: env.host,
246
+ product,
247
+ authorizeUrl: started.data.authorizeUrl,
248
+ callbackUrl: started.data.callbackUrl,
249
+ connected: false,
250
+ }, ctx.ui.hint(`Then check with "ekanos apps show ${product}".`));
251
+ }
252
+ const deadline = Date.now() + 10 * 60 * 1000;
253
+ const before = current.connectedAt;
254
+ while (Date.now() < deadline) {
255
+ await new Promise((resolve) => { var _a; return setTimeout(resolve, (_a = args.pollDelayMs) !== null && _a !== void 0 ? _a : 3000); });
256
+ const polled = await client.connection((token) => showConnection(env.host, token, source, product));
257
+ if (polled.status === 'ok' &&
258
+ polled.data.connection.connected &&
259
+ polled.data.connection.connectedAt !== before) {
260
+ return ctx.succeed({ host: env.host, product, connection: polled.data.connection }, connectionSummary(ctx, polled.data.connection, {
261
+ sourceSlug: polled.data.source.slug,
262
+ callbackUrls: polled.data.callbackUrls,
263
+ }));
264
+ }
265
+ }
266
+ throw validationError('The connection did not finish within ten minutes.', `Run "ekanos apps connect ${product}" again for a fresh link.`);
267
+ }
268
+ async function askFields(ctx, args, current, product) {
269
+ var _a, _b;
270
+ const toAsk = current.fields.filter((field) => !field.set || current.missing.includes(field.name));
271
+ if (toAsk.length === 0)
272
+ return {};
273
+ if (!canPrompt(ctx, args)) {
274
+ throw usageError(`${current.name} needs its app fields entered for the Source: ${toAsk.map((field) => field.label).join(', ')}.`, `Run it yourself in your terminal: npx @ekanos/cli apps connect ${product}${args.source ? ` --source ${args.source}` : ''}`);
275
+ }
276
+ const fields = {};
277
+ for (const field of toAsk) {
278
+ const question = field.sensitive
279
+ ? `${field.label} (hidden): `
280
+ : `${field.label}: `;
281
+ const value = (await (field.sensitive
282
+ ? ((_a = args.askSecret) !== null && _a !== void 0 ? _a : promptHidden)
283
+ : ((_b = args.ask) !== null && _b !== void 0 ? _b : promptLine))(question)).trim();
284
+ if (value)
285
+ fields[field.name] = value;
286
+ }
287
+ return fields;
288
+ }
289
+ async function disconnect(ctx, args, client, host, source, product) {
290
+ var _a;
291
+ if (canPrompt(ctx, args)) {
292
+ const sure = await ((_a = args.confirm) !== null && _a !== void 0 ? _a : promptYesNo)(`Disconnect ${product} for every team's provided agents on ${source}? [y/N]`);
293
+ if (!sure) {
294
+ throw validationError('Nothing was changed.', `Run "ekanos apps show ${product}" to see the current state.`);
295
+ }
296
+ }
297
+ const result = await client.connection((token) => deleteConnection(host, token, source, product));
298
+ if (result.status === 'unsupported') {
299
+ throw invalidStateError(result.message, `Each team connects ${product} itself, from the team's Integrations page.`);
300
+ }
301
+ if (result.status !== 'ok') {
302
+ throw invalidStateError('The host could not disconnect.', 'Retry shortly.');
303
+ }
304
+ return ctx.succeed(Object.assign({ host, product }, result.data), ctx.ui.result({
305
+ title: result.data.removed
306
+ ? `Disconnected ${product} for ${source}`
307
+ : `${product} was not connected for ${source}`,
308
+ rows: [
309
+ [
310
+ 'Provided agents',
311
+ 'their tools for this product stop until you connect again',
312
+ ],
313
+ ],
314
+ }));
315
+ }
316
+ function connectionSummary(ctx, connection, meta) {
317
+ const rows = [
318
+ ['Source', meta.sourceSlug],
319
+ ['Connected', connectionState(connection)],
320
+ ...(connection.scope ? [['Scope', connection.scope]] : []),
321
+ ...(connection.fields.length > 0
322
+ ? [
323
+ [
324
+ 'App fields',
325
+ connection.fields.map((field) => { var _a; return `${field.label}: ${field.set ? ((_a = field.value) !== null && _a !== void 0 ? _a : 'set') : 'missing'}`; }),
326
+ ],
327
+ ]
328
+ : []),
329
+ ['Redirect URL', meta.callbackUrls],
330
+ [
331
+ 'Used by',
332
+ 'provided agents whose template says connections.<product>.owner: "source", in every team',
333
+ ],
334
+ ];
335
+ return [
336
+ ctx.ui.result({
337
+ title: `${connection.name} — one connection for the whole Source`,
338
+ rows,
339
+ }),
340
+ ...(connection.connected
341
+ ? []
342
+ : [
343
+ ctx.ui.hint(`Run "ekanos apps connect ${connection.slug}" in your terminal to connect it.`),
344
+ ]),
345
+ ].join('\n');
346
+ }
347
+ async function openClient(ctx, args) {
348
+ const env = resolveAuthEnvironment(ctx, args.host, args.env, {
349
+ projectDir: args.dir,
350
+ });
351
+ const resolved = await resolveSourceTarget(env, requireSession(env.store, env.host), args);
352
+ return {
353
+ env,
354
+ client: new AppsClient(env, resolved.session),
355
+ source: resolved.source,
356
+ };
357
+ }
120
358
  class AppsClient {
121
359
  constructor(env, session) {
122
360
  this.env = env;
@@ -132,6 +370,17 @@ class AppsClient {
132
370
  return result.data;
133
371
  throw authRequiredError(`The session for ${this.env.host} could not authorize the apps call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
134
372
  }
373
+ /** As `call`, for the connections client, whose 409s are answers. */
374
+ async connection(send) {
375
+ let result = await send(this.session.accessToken);
376
+ if (result.status === 'unauthorized') {
377
+ this.session = await refreshStoredSession(this.env, this.session);
378
+ result = await send(this.session.accessToken);
379
+ }
380
+ if (result.status !== 'unauthorized')
381
+ return result;
382
+ throw authRequiredError(`The session for ${this.env.host} could not authorize the connections call.`, `Run "ekanos login --host ${this.env.host}" and retry.`);
383
+ }
135
384
  }
136
385
  function canPrompt(ctx, args) {
137
386
  var _a;
@@ -164,27 +413,101 @@ export async function promptLine(question) {
164
413
  rl.close();
165
414
  }
166
415
  }
167
- /** Read a line without echoing it: the secret never appears on screen or in scrollback. */
416
+ /**
417
+ * Read a line without echoing it: the secret never appears on screen or in
418
+ * scrollback. The question is always printed; only the typed characters are
419
+ * hidden (each shows as `*`, so a paste visibly landed).
420
+ *
421
+ * Raw mode by hand rather than readline: a terminal readline redraws its
422
+ * line on `question()` — cursor to column 0, clear to the end of the screen —
423
+ * which erased the question written before it, so the terminal sat on an
424
+ * empty line and a hidden field looked skipped.
425
+ */
168
426
  export async function promptHidden(question) {
169
- const readline = await import('node:readline');
170
- process.stderr.write(question);
171
- const rl = readline.createInterface({
172
- input: process.stdin,
173
- output: process.stderr,
174
- terminal: true,
175
- });
176
- // Swallow readline's echo of each keystroke; the question is already written.
177
- rl._writeToOutput =
178
- () => { };
179
- try {
180
- return await new Promise((resolve) => {
181
- rl.question('', resolve);
182
- });
183
- }
184
- finally {
185
- rl.close();
186
- process.stderr.write('\n');
427
+ const stdin = process.stdin;
428
+ const stderr = process.stderr;
429
+ stderr.write(question);
430
+ if (!stdin.isTTY) {
431
+ // Piped stdin echoes nothing: read one line as is.
432
+ const readline = await import('node:readline/promises');
433
+ const rl = readline.createInterface({ input: stdin, terminal: false });
434
+ try {
435
+ return await rl.question('');
436
+ }
437
+ finally {
438
+ rl.close();
439
+ stderr.write('\n');
440
+ }
187
441
  }
442
+ return new Promise((resolve) => {
443
+ let value = '';
444
+ let inEscape = false;
445
+ const wasRaw = stdin.isRaw;
446
+ const finish = () => {
447
+ stdin.off('data', onData);
448
+ stdin.setRawMode(wasRaw);
449
+ stdin.pause();
450
+ stderr.write('\n');
451
+ };
452
+ const onData = (chunk) => {
453
+ for (const char of typeof chunk === 'string'
454
+ ? chunk
455
+ : chunk.toString('utf8')) {
456
+ if (inEscape) {
457
+ // An escape sequence (an arrow key) ends at its final letter.
458
+ if (/[A-Za-z~]/.test(char))
459
+ inEscape = false;
460
+ continue;
461
+ }
462
+ if (char === '\u001b') {
463
+ inEscape = true;
464
+ continue;
465
+ }
466
+ if (char === '\r' || char === '\n' || char === '\u0004') {
467
+ finish();
468
+ resolve(value);
469
+ return;
470
+ }
471
+ if (char === '\u0003') {
472
+ // Ctrl-C: raw mode swallowed the signal, so leave as SIGINT would.
473
+ finish();
474
+ process.exit(130);
475
+ }
476
+ if (char === '\u007f' || char === '\b') {
477
+ if (value.length > 0) {
478
+ value = value.slice(0, -1);
479
+ stderr.write('\b \b');
480
+ }
481
+ continue;
482
+ }
483
+ if (char === '\u0015') {
484
+ // Ctrl-U: clear what was typed.
485
+ stderr.write('\b \b'.repeat(value.length));
486
+ value = '';
487
+ continue;
488
+ }
489
+ if (char < ' ')
490
+ continue;
491
+ value += char;
492
+ stderr.write('*');
493
+ }
494
+ };
495
+ stdin.setRawMode(true);
496
+ stdin.resume();
497
+ stdin.on('data', onData);
498
+ });
499
+ }
500
+ /** Each credential's state: the client id's value, the secret as saved or not. */
501
+ function providerFieldRows(status) {
502
+ var _a;
503
+ return ((_a = status.fields) !== null && _a !== void 0 ? _a : []).map((field) => { var _a; return [
504
+ field.label,
505
+ !field.set
506
+ ? 'not set'
507
+ : field.sensitive
508
+ ? 'saved'
509
+ : ((_a = field.value) !== null && _a !== void 0 ? _a : 'saved'),
510
+ ]; });
188
511
  }
189
512
  /** Every redirect URI to register; an older host sends only the one. */
190
513
  function callbackUrls(status) {
@@ -1 +1 @@
1
- {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ProviderAppStatus } from '@ekanos/integration-schema';\n\nimport {\n type AppsApiResult,\n setProviderApp,\n showProviderApp,\n} from '../apps-api';\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport { authRequiredError, usageError, validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { resolveSourceTarget } from '../source-target';\nimport type { ResultRow } from '../ui';\nimport { promptYesNo } from './publish';\n\nexport const APPS_SUBCOMMANDS = ['show', 'set'] as const;\ntype AppsSubcommand = (typeof APPS_SUBCOMMANDS)[number];\n\nexport interface AppsArgs {\n host?: string;\n source?: string;\n subcommand?: string;\n product?: string;\n dir: string;\n env: Record<string, string | undefined>;\n stdinIsTTY?: boolean;\n /** Test seams for the questions `set` asks. */\n ask?: (question: string) => Promise<string>;\n askSecret?: (question: string) => Promise<string>;\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/**\n * `apps` — a Source's provider apps: the OAuth client an integration signs\n * every team in through (Linear's, say). `show` says whether the Source has\n * one and which callback URL to register; `set` takes the client id and\n * secret from the Source admin at the terminal — never from a flag, a file\n * or a non-interactive run, so an agent driving the CLI never handles the\n * secret.\n */\nexport async function runApps(\n ctx: CliContext,\n args: AppsArgs,\n): Promise<ExitCode> {\n const subcommand = parseSubcommand(args.subcommand);\n const product = requireProduct(args.product, subcommand);\n\n if (subcommand === 'set' && !canPrompt(ctx, args)) {\n throw usageError(\n 'apps set takes the client secret at a terminal, never in a script or an agent run.',\n `Run it yourself in your terminal: npx @ekanos/cli apps set ${product}${args.source ? ` --source ${args.source}` : ''}`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(\n env,\n requireSession(env.store, env.host),\n args,\n );\n const client = new AppsClient(env, resolved.session);\n const source = resolved.source;\n\n const status = await client.call((token) =>\n showProviderApp(env.host, token, source, product),\n );\n\n if (subcommand === 'show') {\n return ctx.succeed({ host: env.host, ...status }, summary(ctx, status));\n }\n\n ctx.log(walkthrough(status).join('\\n'));\n if (status.configured) {\n const replace = await (args.confirm ?? promptYesNo)(\n `${status.source.slug} already has ${status.product.name} credentials. Replace them? [y/N]`,\n );\n if (!replace) {\n throw validationError(\n 'Nothing was changed.',\n `Run \"ekanos apps show ${product}\" to see the current state.`,\n );\n }\n }\n\n const clientId = (await (args.ask ?? promptLine)('Client ID: ')).trim();\n const clientSecret = (\n await (args.askSecret ?? promptHidden)('Client secret (hidden): ')\n ).trim();\n if (!clientId || !clientSecret) {\n throw validationError(\n 'Both the client ID and the client secret are needed.',\n `Run \"ekanos apps set ${product}\" again.`,\n );\n }\n\n const result = await client.call((token) =>\n setProviderApp(env.host, token, {\n source,\n product,\n clientId,\n clientSecret,\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.replaced\n ? `Replaced the ${result.product.name} app`\n : `Set up the ${result.product.name} app`,\n rows: [\n ['Source', result.source.slug],\n ['Teams can connect', result.configured ? 'yes' : 'not yet'],\n ['Callback URLs', callbackUrls(result)],\n ],\n }),\n );\n}\n\n/**\n * How to create the app: the integration's own steps (`oauth.setup`, with the\n * callback URL filled in) when it declares them, else the generic three.\n */\nexport function walkthrough(status: ProviderAppStatus): string[] {\n const name = status.product.name;\n const lines = [`${name} app for ${status.source.slug}`];\n if (status.setup?.consoleUrl) {\n lines.push(` Open: ${status.setup.consoleUrl}`);\n }\n const steps = status.setup?.steps.length\n ? [\n ...status.setup.steps,\n `Check the app lists exactly: ${callbackUrls(status).join(', ')}`,\n ]\n : [\n `In ${name}, create an OAuth app (or open the one you use).`,\n `Register exactly these callback URLs: ${callbackUrls(status).join(', ')}`,\n ];\n steps.push(\n \"Enter its client ID and secret below. The secret is stored in Ekanos's vault and never shown again.\",\n );\n lines.push(...steps.map((step, index) => ` ${index + 1}. ${step}`));\n if (status.setup?.guideUrl) lines.push(` Guide: ${status.setup.guideUrl}`);\n return lines;\n}\n\nfunction summary(ctx: CliContext, status: ProviderAppStatus): string {\n const rows: ResultRow[] = [\n ['Source', status.source.slug],\n ...(status.setup\n ? ([\n [\n 'How to create it',\n [\n ...(status.setup.consoleUrl\n ? [`Open ${status.setup.consoleUrl}`]\n : []),\n ...status.setup.steps.map(\n (step, index) => `${index + 1}. ${step}`,\n ),\n ],\n ],\n ] as const)\n : []),\n [\n 'Set up',\n status.configured\n ? 'yes — teams can connect'\n : 'no — no team can connect yet',\n ],\n ['Callback URLs', callbackUrls(status)],\n ];\n return [\n ctx.ui.result({ title: `${status.product.name} app`, rows }),\n ...(status.configured\n ? []\n : [\n ctx.ui.hint(\n `Run \"ekanos apps set ${status.product.slug}\" in your terminal to enter its credentials.`,\n ),\n ]),\n ].join('\\n');\n}\n\nclass AppsClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<AppsApiResult<T>>,\n ): Promise<T> {\n let result = await send(this.session.accessToken);\n if (result.status === 'unauthorized') {\n this.session = await refreshStoredSession(this.env, this.session);\n result = await send(this.session.accessToken);\n }\n if (result.status === 'ok') return result.data;\n throw authRequiredError(\n `The session for ${this.env.host} could not authorize the apps call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n}\n\nfunction canPrompt(ctx: CliContext, args: AppsArgs): boolean {\n return !ctx.jsonMode && (args.stdinIsTTY ?? process.stdin.isTTY === true);\n}\n\nfunction parseSubcommand(value: string | undefined): AppsSubcommand {\n const match = APPS_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value ? `Unknown apps subcommand \"${value}\".` : 'apps needs a subcommand.',\n `Run one of: ${APPS_SUBCOMMANDS.map((name) => `ekanos apps ${name} <product>`).join(', ')}.`,\n );\n}\n\nfunction requireProduct(value: string | undefined, verb: string): 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\n ? `\"${value}\" is not a product slug.`\n : `apps ${verb} needs a product.`,\n `Pass the product's slug, e.g. \"ekanos apps ${verb} linear\". \"ekanos catalog\" lists them.`,\n );\n}\n\nexport async function promptLine(question: string): Promise<string> {\n const readline = await import('node:readline/promises');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n try {\n return await rl.question(question);\n } finally {\n rl.close();\n }\n}\n\n/** Read a line without echoing it: the secret never appears on screen or in scrollback. */\nexport async function promptHidden(question: string): Promise<string> {\n const readline = await import('node:readline');\n process.stderr.write(question);\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n terminal: true,\n });\n // Swallow readline's echo of each keystroke; the question is already written.\n (rl as unknown as { _writeToOutput: (text: string) => void })._writeToOutput =\n () => {};\n try {\n return await new Promise<string>((resolve) => {\n rl.question('', resolve);\n });\n } finally {\n rl.close();\n process.stderr.write('\\n');\n }\n}\n\n/** Every redirect URI to register; an older host sends only the one. */\nfunction callbackUrls(status: ProviderAppStatus): string[] {\n return status.callbackUrls?.length\n ? status.callbackUrls\n : [status.callbackUrl];\n}\n"]}
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ProviderAppStatus } from '@ekanos/integration-schema';\n\nimport {\n type AppsApiResult,\n setProviderApp,\n showProviderApp,\n} from '../apps-api';\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport {\n type ConnectionsApiResult,\n type SourceConnection,\n deleteConnection,\n listConnections,\n showConnection,\n startConnection,\n} from '../connections-api';\nimport type { CliContext } from '../context';\nimport {\n authRequiredError,\n invalidStateError,\n usageError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { resolveSourceTarget } from '../source-target';\nimport type { ResultRow } from '../ui';\nimport { promptYesNo } from './publish';\n\nexport const APPS_SUBCOMMANDS = [\n 'show',\n 'set',\n 'list',\n 'connect',\n 'disconnect',\n] as const;\ntype AppsSubcommand = (typeof APPS_SUBCOMMANDS)[number];\n\nexport interface AppsArgs {\n host?: string;\n source?: string;\n subcommand?: string;\n product?: string;\n dir: string;\n env: Record<string, string | undefined>;\n stdinIsTTY?: boolean;\n /** Test seams for the questions `set` and `connect` ask. */\n ask?: (question: string) => Promise<string>;\n askSecret?: (question: string) => Promise<string>;\n confirm?: (question: string) => Promise<boolean>;\n /** Test seam: `connect`'s wait between status polls. */\n pollDelayMs?: number;\n /** `connect --no-wait`: print the URL and return without polling. */\n noWait?: boolean;\n}\n\n/**\n * `apps` — a Source's provider apps: the OAuth client an integration signs\n * every team in through (Linear's, say). `show` says whether the Source has\n * one and which callback URL to register; `set` takes the client id and\n * secret from the Source admin at the terminal — either on its own, Enter\n * keeping the saved one — never from a flag, a file or a non-interactive\n * run, so an agent driving the CLI never handles the secret.\n */\nexport async function runApps(\n ctx: CliContext,\n args: AppsArgs,\n): Promise<ExitCode> {\n const subcommand = parseSubcommand(args.subcommand);\n if (subcommand === 'list') return runList(ctx, args);\n const product = requireProduct(args.product, subcommand);\n if (subcommand === 'connect' || subcommand === 'disconnect') {\n return runConnection(ctx, args, subcommand, product);\n }\n\n if (subcommand === 'set' && !canPrompt(ctx, args)) {\n throw usageError(\n 'apps set takes the client secret at a terminal, never in a script or an agent run.',\n `Run it yourself in your terminal: npx @ekanos/cli apps set ${product}${args.source ? ` --source ${args.source}` : ''}`,\n );\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(\n env,\n requireSession(env.store, env.host),\n args,\n );\n const client = new AppsClient(env, resolved.session);\n const source = resolved.source;\n\n if (subcommand === 'show') {\n return showApp(ctx, env.host, client, source, product);\n }\n\n const status = await client.call((token) =>\n showProviderApp(env.host, token, source, product),\n );\n\n ctx.log(walkthrough(status).join('\\n'));\n\n // Either credential on its own: Enter keeps what is saved. An older host\n // sends no `fields`; then `configured` says whether both are saved.\n const saved = (name: 'clientId' | 'clientSecret') =>\n status.fields?.find((field) => field.name === name) ?? {\n set: status.configured,\n value: null,\n };\n const savedId = saved('clientId');\n const savedSecret = saved('clientSecret');\n const clientId = (\n await (args.ask ?? promptLine)(\n savedId.set\n ? `Client ID (current: ${savedId.value ?? 'saved'} — Enter to keep): `\n : 'Client ID: ',\n )\n ).trim();\n const clientSecret = (\n await (args.askSecret ?? promptHidden)(\n savedSecret.set\n ? 'Client secret (saved — Enter to keep, hidden): '\n : 'Client secret (hidden): ',\n )\n ).trim();\n if (!clientId && !clientSecret) {\n throw validationError(\n 'Nothing was changed.',\n `Run \"ekanos apps show ${product}\" to see the current state.`,\n );\n }\n\n const result = await client.call((token) =>\n setProviderApp(env.host, token, {\n source,\n product,\n ...(clientId ? { clientId } : {}),\n ...(clientSecret ? { clientSecret } : {}),\n }),\n );\n return ctx.succeed(\n { host: env.host, ...result },\n ctx.ui.result({\n title: result.replaced\n ? `Updated the ${result.product.name} app`\n : `Set up the ${result.product.name} app`,\n rows: [\n ['Source', result.source.slug],\n ...providerFieldRows(result),\n ['Teams can connect', result.configured ? 'yes' : 'not yet'],\n ['Callback URLs', callbackUrls(result)],\n ],\n }),\n );\n}\n\n/**\n * How to create the app: the integration's own steps (`oauth.setup`, with the\n * callback URL filled in) when it declares them, else the generic three.\n */\nexport function walkthrough(status: ProviderAppStatus): string[] {\n const name = status.product.name;\n const lines = [`${name} app for ${status.source.slug}`];\n if (status.setup?.consoleUrl) {\n lines.push(` Open: ${status.setup.consoleUrl}`);\n }\n const steps = status.setup?.steps.length\n ? [\n ...status.setup.steps,\n `Check the app lists exactly: ${callbackUrls(status).join(', ')}`,\n ]\n : [\n `In ${name}, create an OAuth app (or open the one you use).`,\n `Register exactly these callback URLs: ${callbackUrls(status).join(', ')}`,\n ];\n steps.push(\n \"Enter its client ID and secret below. The secret is stored in Ekanos's vault and never shown again.\",\n );\n lines.push(...steps.map((step, index) => ` ${index + 1}. ${step}`));\n if (status.setup?.guideUrl) lines.push(` Guide: ${status.setup.guideUrl}`);\n return lines;\n}\n\nfunction summary(ctx: CliContext, status: ProviderAppStatus): string {\n const rows: ResultRow[] = [\n ['Source', status.source.slug],\n ...(status.setup\n ? ([\n [\n 'How to create it',\n [\n ...(status.setup.consoleUrl\n ? [`Open ${status.setup.consoleUrl}`]\n : []),\n ...status.setup.steps.map(\n (step, index) => `${index + 1}. ${step}`,\n ),\n ],\n ],\n ] as const)\n : []),\n ...providerFieldRows(status),\n [\n 'Set up',\n status.configured\n ? 'yes — teams can connect'\n : 'no — no team can connect yet',\n ],\n ['Callback URLs', callbackUrls(status)],\n ];\n return [\n ctx.ui.result({ title: `${status.product.name} app`, rows }),\n ...(status.configured\n ? []\n : [\n ctx.ui.hint(\n `Run \"ekanos apps set ${status.product.slug}\" in your terminal to enter its credentials.`,\n ),\n ]),\n ].join('\\n');\n}\n\n/**\n * `apps show <product>`: the Source provider app, and — for an integration\n * that supports one — the Source connection (plan 241). A `teamApp`\n * integration (Zendesk) has no provider app but can have a Source\n * connection, so either half may be absent.\n */\nasync function showApp(\n ctx: CliContext,\n host: string,\n client: AppsClient,\n source: string,\n product: string,\n): Promise<ExitCode> {\n const [app, connection] = await Promise.all([\n client\n .call((token) => showProviderApp(host, token, source, product))\n .then(\n (status) => ({ ok: true as const, status }),\n (error: unknown) => ({ ok: false as const, error }),\n ),\n // A host without the connections routes (before plan 241), or one that\n // refuses, leaves `show` exactly as it was: the provider app alone.\n client\n .connection((token) => showConnection(host, token, source, product))\n .catch(() => ({ status: 'unavailable' as const })),\n ]);\n\n if (!app.ok && connection.status !== 'ok') throw app.error;\n\n const parts: string[] = [];\n if (app.ok) parts.push(summary(ctx, app.status));\n if (connection.status === 'ok') {\n parts.push(\n connectionSummary(ctx, connection.data.connection, {\n sourceSlug: connection.data.source.slug,\n callbackUrls: connection.data.callbackUrls,\n }),\n );\n }\n\n return ctx.succeed(\n {\n host,\n ...(app.ok ? app.status : {}),\n ...(connection.status === 'ok'\n ? {\n sourceConnection: connection.data.connection,\n sourceConnectionCallbackUrls: connection.data.callbackUrls,\n }\n : {}),\n },\n parts.join('\\n'),\n );\n}\n\n/** `apps list`: every integration the Source can connect once, with status. */\nasync function runList(ctx: CliContext, args: AppsArgs): Promise<ExitCode> {\n const { env, client, source } = await openClient(ctx, args);\n const result = await client.connection((token) =>\n listConnections(env.host, token, source),\n );\n if (result.status !== 'ok') {\n throw invalidStateError(\n 'The host could not list the connections.',\n 'Retry shortly.',\n );\n }\n const { connections } = result.data;\n const rows: ResultRow[] = connections.map((connection) => [\n `${connection.name} (${connection.slug})`,\n connectionState(connection),\n ]);\n return ctx.succeed(\n { host: env.host, ...result.data },\n connections.length === 0\n ? ctx.ui.hint(\n `No integration on ${result.data.source.slug} supports one connection for the whole Source yet.`,\n )\n : ctx.ui.result({\n title: `Source connections on ${result.data.source.slug}`,\n rows,\n }),\n );\n}\n\nfunction connectionState(connection: SourceConnection): string {\n if (connection.connected) {\n return [\n 'connected',\n connection.connectedAt ? ` ${connection.connectedAt}` : '',\n connection.connectedBy ? ` by ${connection.connectedBy}` : '',\n ].join('');\n }\n return connection.missing.length > 0\n ? `not connected — app fields missing: ${connection.missing.join(', ')}`\n : 'not connected';\n}\n\n/**\n * `apps connect <product>`: connect the integration ONCE for the whole Source,\n * for the Source's provided agents whose template says `owner: \"source\"`.\n * Asks for any missing app fields at the terminal (secrets hidden, never from\n * a flag), prints the provider's consent URL, then waits until the browser\n * finishes. `apps disconnect <product>` removes it.\n */\nasync function runConnection(\n ctx: CliContext,\n args: AppsArgs,\n subcommand: 'connect' | 'disconnect',\n product: string,\n): Promise<ExitCode> {\n const { env, client, source } = await openClient(ctx, args);\n\n if (subcommand === 'disconnect') {\n return disconnect(ctx, args, client, env.host, source, product);\n }\n\n const shown = await client.connection((token) =>\n showConnection(env.host, token, source, product),\n );\n if (shown.status === 'unsupported') {\n throw invalidStateError(\n shown.message,\n `Each team connects ${product} itself, from the team's Integrations page.`,\n );\n }\n if (shown.status !== 'ok') {\n throw invalidStateError(\n 'The host could not read the connection.',\n 'Retry shortly.',\n );\n }\n const current = shown.data.connection;\n\n if (current.app === 'fields') {\n ctx.log(\n [\n `${current.name} app for ${shown.data.source.slug} (one for the whole Source)`,\n ` Register this redirect URL in the app: ${shown.data.callbackUrls.join(', ')}`,\n ].join('\\n'),\n );\n }\n\n const fields = await askFields(ctx, args, current, product);\n\n const started = await client.connection((token) =>\n startConnection(env.host, token, {\n source,\n product,\n ...(Object.keys(fields).length > 0 ? { fields } : {}),\n }),\n );\n if (started.status === 'not-configured') {\n throw validationError(\n `${current.name} is not set up for the Source yet — missing: ${started.missing.join(', ')}.`,\n current.app === 'source-app'\n ? `Run \"ekanos apps set ${product}\" to enter the Source's app first.`\n : `Run \"ekanos apps connect ${product}\" again and fill every field.`,\n );\n }\n if (started.status !== 'ok') {\n throw invalidStateError(\n 'The host could not start the connection.',\n 'Retry shortly.',\n );\n }\n\n ctx.log(\n [\n `Open this link and approve ${current.name} as the account the Source's agents should act as:`,\n ` ${started.data.authorizeUrl}`,\n ].join('\\n'),\n );\n\n if (args.noWait || ctx.jsonMode) {\n return ctx.succeed(\n {\n host: env.host,\n product,\n authorizeUrl: started.data.authorizeUrl,\n callbackUrl: started.data.callbackUrl,\n connected: false,\n },\n ctx.ui.hint(`Then check with \"ekanos apps show ${product}\".`),\n );\n }\n\n const deadline = Date.now() + 10 * 60 * 1000;\n const before = current.connectedAt;\n while (Date.now() < deadline) {\n await new Promise((resolve) =>\n setTimeout(resolve, args.pollDelayMs ?? 3000),\n );\n const polled = await client.connection((token) =>\n showConnection(env.host, token, source, product),\n );\n if (\n polled.status === 'ok' &&\n polled.data.connection.connected &&\n polled.data.connection.connectedAt !== before\n ) {\n return ctx.succeed(\n { host: env.host, product, connection: polled.data.connection },\n connectionSummary(ctx, polled.data.connection, {\n sourceSlug: polled.data.source.slug,\n callbackUrls: polled.data.callbackUrls,\n }),\n );\n }\n }\n throw validationError(\n 'The connection did not finish within ten minutes.',\n `Run \"ekanos apps connect ${product}\" again for a fresh link.`,\n );\n}\n\nasync function askFields(\n ctx: CliContext,\n args: AppsArgs,\n current: SourceConnection,\n product: string,\n): Promise<Record<string, string>> {\n const toAsk = current.fields.filter(\n (field) => !field.set || current.missing.includes(field.name),\n );\n if (toAsk.length === 0) return {};\n if (!canPrompt(ctx, args)) {\n throw usageError(\n `${current.name} needs its app fields entered for the Source: ${toAsk.map((field) => field.label).join(', ')}.`,\n `Run it yourself in your terminal: npx @ekanos/cli apps connect ${product}${args.source ? ` --source ${args.source}` : ''}`,\n );\n }\n const fields: Record<string, string> = {};\n for (const field of toAsk) {\n const question = field.sensitive\n ? `${field.label} (hidden): `\n : `${field.label}: `;\n const value = (\n await (\n field.sensitive\n ? (args.askSecret ?? promptHidden)\n : (args.ask ?? promptLine)\n )(question)\n ).trim();\n if (value) fields[field.name] = value;\n }\n return fields;\n}\n\nasync function disconnect(\n ctx: CliContext,\n args: AppsArgs,\n client: AppsClient,\n host: string,\n source: string,\n product: string,\n): Promise<ExitCode> {\n if (canPrompt(ctx, args)) {\n const sure = await (args.confirm ?? promptYesNo)(\n `Disconnect ${product} for every team's provided agents on ${source}? [y/N]`,\n );\n if (!sure) {\n throw validationError(\n 'Nothing was changed.',\n `Run \"ekanos apps show ${product}\" to see the current state.`,\n );\n }\n }\n const result = await client.connection((token) =>\n deleteConnection(host, token, source, product),\n );\n if (result.status === 'unsupported') {\n throw invalidStateError(\n result.message,\n `Each team connects ${product} itself, from the team's Integrations page.`,\n );\n }\n if (result.status !== 'ok') {\n throw invalidStateError('The host could not disconnect.', 'Retry shortly.');\n }\n return ctx.succeed(\n { host, product, ...result.data },\n ctx.ui.result({\n title: result.data.removed\n ? `Disconnected ${product} for ${source}`\n : `${product} was not connected for ${source}`,\n rows: [\n [\n 'Provided agents',\n 'their tools for this product stop until you connect again',\n ],\n ],\n }),\n );\n}\n\nfunction connectionSummary(\n ctx: CliContext,\n connection: SourceConnection,\n meta: { sourceSlug: string; callbackUrls: string[] },\n): string {\n const rows: ResultRow[] = [\n ['Source', meta.sourceSlug],\n ['Connected', connectionState(connection)],\n ...(connection.scope ? ([['Scope', connection.scope]] as ResultRow[]) : []),\n ...(connection.fields.length > 0\n ? ([\n [\n 'App fields',\n connection.fields.map(\n (field) =>\n `${field.label}: ${field.set ? (field.value ?? 'set') : 'missing'}`,\n ),\n ],\n ] as ResultRow[])\n : []),\n ['Redirect URL', meta.callbackUrls],\n [\n 'Used by',\n 'provided agents whose template says connections.<product>.owner: \"source\", in every team',\n ],\n ];\n return [\n ctx.ui.result({\n title: `${connection.name} — one connection for the whole Source`,\n rows,\n }),\n ...(connection.connected\n ? []\n : [\n ctx.ui.hint(\n `Run \"ekanos apps connect ${connection.slug}\" in your terminal to connect it.`,\n ),\n ]),\n ].join('\\n');\n}\n\nasync function openClient(ctx: CliContext, args: AppsArgs) {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const resolved = await resolveSourceTarget(\n env,\n requireSession(env.store, env.host),\n args,\n );\n return {\n env,\n client: new AppsClient(env, resolved.session),\n source: resolved.source,\n };\n}\n\nclass AppsClient {\n constructor(\n private readonly env: AuthEnvironment,\n private session: StoredSession,\n ) {}\n\n async call<T>(\n send: (accessToken: string) => Promise<AppsApiResult<T>>,\n ): Promise<T> {\n let result = await send(this.session.accessToken);\n if (result.status === 'unauthorized') {\n this.session = await refreshStoredSession(this.env, this.session);\n result = await send(this.session.accessToken);\n }\n if (result.status === 'ok') return result.data;\n throw authRequiredError(\n `The session for ${this.env.host} could not authorize the apps call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n\n /** As `call`, for the connections client, whose 409s are answers. */\n async connection<T>(\n send: (accessToken: string) => Promise<ConnectionsApiResult<T>>,\n ): Promise<Exclude<ConnectionsApiResult<T>, { status: 'unauthorized' }>> {\n let result = await send(this.session.accessToken);\n if (result.status === 'unauthorized') {\n this.session = await refreshStoredSession(this.env, this.session);\n result = await send(this.session.accessToken);\n }\n if (result.status !== 'unauthorized') return result;\n throw authRequiredError(\n `The session for ${this.env.host} could not authorize the connections call.`,\n `Run \"ekanos login --host ${this.env.host}\" and retry.`,\n );\n }\n}\n\nfunction canPrompt(ctx: CliContext, args: AppsArgs): boolean {\n return !ctx.jsonMode && (args.stdinIsTTY ?? process.stdin.isTTY === true);\n}\n\nfunction parseSubcommand(value: string | undefined): AppsSubcommand {\n const match = APPS_SUBCOMMANDS.find((name) => name === value);\n if (match) return match;\n throw usageError(\n value ? `Unknown apps subcommand \"${value}\".` : 'apps needs a subcommand.',\n `Run one of: ${APPS_SUBCOMMANDS.map((name) => `ekanos apps ${name} <product>`).join(', ')}.`,\n );\n}\n\nfunction requireProduct(value: string | undefined, verb: string): 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\n ? `\"${value}\" is not a product slug.`\n : `apps ${verb} needs a product.`,\n `Pass the product's slug, e.g. \"ekanos apps ${verb} linear\". \"ekanos catalog\" lists them.`,\n );\n}\n\nexport async function promptLine(question: string): Promise<string> {\n const readline = await import('node:readline/promises');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n try {\n return await rl.question(question);\n } finally {\n rl.close();\n }\n}\n\n/**\n * Read a line without echoing it: the secret never appears on screen or in\n * scrollback. The question is always printed; only the typed characters are\n * hidden (each shows as `*`, so a paste visibly landed).\n *\n * Raw mode by hand rather than readline: a terminal readline redraws its\n * line on `question()` — cursor to column 0, clear to the end of the screen —\n * which erased the question written before it, so the terminal sat on an\n * empty line and a hidden field looked skipped.\n */\nexport async function promptHidden(question: string): Promise<string> {\n const stdin = process.stdin;\n const stderr = process.stderr;\n stderr.write(question);\n\n if (!stdin.isTTY) {\n // Piped stdin echoes nothing: read one line as is.\n const readline = await import('node:readline/promises');\n const rl = readline.createInterface({ input: stdin, terminal: false });\n try {\n return await rl.question('');\n } finally {\n rl.close();\n stderr.write('\\n');\n }\n }\n\n return new Promise<string>((resolve) => {\n let value = '';\n let inEscape = false;\n const wasRaw = stdin.isRaw;\n const finish = () => {\n stdin.off('data', onData);\n stdin.setRawMode(wasRaw);\n stdin.pause();\n stderr.write('\\n');\n };\n const onData = (chunk: Buffer | string) => {\n for (const char of typeof chunk === 'string'\n ? chunk\n : chunk.toString('utf8')) {\n if (inEscape) {\n // An escape sequence (an arrow key) ends at its final letter.\n if (/[A-Za-z~]/.test(char)) inEscape = false;\n continue;\n }\n if (char === '\\u001b') {\n inEscape = true;\n continue;\n }\n if (char === '\\r' || char === '\\n' || char === '\\u0004') {\n finish();\n resolve(value);\n return;\n }\n if (char === '\\u0003') {\n // Ctrl-C: raw mode swallowed the signal, so leave as SIGINT would.\n finish();\n process.exit(130);\n }\n if (char === '\\u007f' || char === '\\b') {\n if (value.length > 0) {\n value = value.slice(0, -1);\n stderr.write('\\b \\b');\n }\n continue;\n }\n if (char === '\\u0015') {\n // Ctrl-U: clear what was typed.\n stderr.write('\\b \\b'.repeat(value.length));\n value = '';\n continue;\n }\n if (char < ' ') continue;\n value += char;\n stderr.write('*');\n }\n };\n stdin.setRawMode(true);\n stdin.resume();\n stdin.on('data', onData);\n });\n}\n\n/** Each credential's state: the client id's value, the secret as saved or not. */\nfunction providerFieldRows(status: ProviderAppStatus): ResultRow[] {\n return (status.fields ?? []).map((field) => [\n field.label,\n !field.set\n ? 'not set'\n : field.sensitive\n ? 'saved'\n : (field.value ?? 'saved'),\n ]);\n}\n\n/** Every redirect URI to register; an older host sends only the one. */\nfunction callbackUrls(status: ProviderAppStatus): string[] {\n return status.callbackUrls?.length\n ? status.callbackUrls\n : [status.callbackUrl];\n}\n"]}