@extenshi/cli 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -21,28 +21,33 @@
21
21
  * before the process exits. See telemetry.ts — disabled by default until a
22
22
  * PostHog key is configured, and a no-op under DO_NOT_TRACK / EXTENSHI_TELEMETRY=0.
23
23
  */
24
+ import { readFile } from 'node:fs/promises';
24
25
  import { createRequire } from 'node:module';
26
+ import os from 'node:os';
25
27
  import * as readline from 'node:readline/promises';
26
28
  import { program } from 'commander';
27
29
  import { checkAutoUpdate } from './auto-update.js';
28
30
  import { loadConfig, saveConfig } from './config.js';
29
31
  import { runIconPreview } from './icon.js';
32
+ import { makeLoginBff } from './login-bff.js';
33
+ import { runBrowserLogin } from './login-flow.js';
34
+ import { openBrowser, openLoopback, sleep } from './login-runtime.js';
30
35
  import { runPublish } from './publish.js';
31
36
  import { runReviewRisk } from './review-risk.js';
37
+ import { parseRefs, parseStore, runRisk } from './risk.js';
32
38
  import { runScan } from './scan.js';
33
39
  import { captureError, captureEvent, classifyError, flagsFromArgv, flushTelemetry, initTelemetry, } from './telemetry.js';
34
40
  const MISSING_KEY_HELP = "Looks like you don't have an Extenshi API key set up yet — let's fix that.\n" +
35
41
  '\n' +
36
42
  'Getting started is free: no credit card required. Every account includes a\n' +
37
- 'free monthly allowance (25 catalog reads + 5 scans), so you can try things out\n' +
43
+ 'one-time free allowance (10 catalog reads + 3 scans), so you can try things out\n' +
38
44
  'before paying for anything.\n' +
39
45
  '\n' +
40
- '1. Create a free account at https://auth.extenshi.io/signup\n' +
41
- '2. Grab a key at https://dojo.extenshi.io/api-keys\n' +
42
- '3. Then give it to the CLI any of these ways:\n' +
43
- ' • run: extenshi login\n' +
44
- ' • set the EXTENSHI_API_KEY environment variable\n' +
45
- ' • add EXTENSHI_API_KEY=... to a .env file in this directory';
46
+ ' Run: extenshi login\n' +
47
+ '\n' +
48
+ 'That opens your browser, where you can sign in or create an account, and connects\n' +
49
+ 'this terminal automatically. In CI, set EXTENSHI_API_KEY from your secrets instead\n' +
50
+ '(or add EXTENSHI_API_KEY=... to a .env file in this directory).';
46
51
  const _require = createRequire(import.meta.url);
47
52
  const pkg = _require('../package.json');
48
53
  initTelemetry({ surface: 'cli', version: pkg.version });
@@ -234,6 +239,50 @@ program
234
239
  });
235
240
  });
236
241
  });
242
+ program
243
+ .command('risk [store-ids...]')
244
+ .description('Look up the safety score for extensions you already have, by their STORE ids (the id in the ' +
245
+ 'store URL). Built for inventories: up to 40 extensions per request, and the whole request ' +
246
+ 'costs 1 read credit — not 1 per extension. Scores are cluster-resolved server-side, so they ' +
247
+ 'match what the extension page shows.')
248
+ .option('--store <store>', 'Store the ids belong to: chrome | firefox | edge (default: chrome). Per-id "firefox:some-addon" prefixes override it.')
249
+ .option('--file <path>', 'Read ids from a file instead of (or in addition to) the arguments: one per line, bare id or "store:id". Blank lines and # comments are ignored.')
250
+ .option('--catalog-url <url>', 'Override the catalog BFF base URL (default: https://bff.extenshi.io)')
251
+ .option('--json', 'Output raw JSON')
252
+ .action(async (storeIds, opts) => {
253
+ await runCommand('risk', async () => {
254
+ const defaultStore = parseStore(opts.store ?? 'chrome');
255
+ if (!defaultStore) {
256
+ throw new Error('--store must be one of: chrome, firefox, edge');
257
+ }
258
+ const entries = [...(storeIds ?? [])];
259
+ if (opts.file) {
260
+ let contents;
261
+ try {
262
+ contents = await readFile(opts.file, 'utf8');
263
+ }
264
+ catch {
265
+ throw new Error(`could not read --file "${opts.file}"`);
266
+ }
267
+ entries.push(...contents.split(/\r?\n/));
268
+ }
269
+ const { refs, invalid } = parseRefs(entries, defaultStore);
270
+ if (invalid.length > 0) {
271
+ console.error(`Skipped ${invalid.length} unusable entr(y/ies): ${invalid.slice(0, 5).join(', ')}`);
272
+ }
273
+ const config = await loadConfig();
274
+ if (!config.apiKey) {
275
+ throw new Error(MISSING_KEY_HELP);
276
+ }
277
+ await checkAutoUpdate();
278
+ await runRisk({
279
+ refs,
280
+ bffUrl: (opts.catalogUrl ?? config.bffUrl ?? 'https://bff.extenshi.io').replace(/\/$/, ''),
281
+ apiKey: config.apiKey,
282
+ outputJson: opts.json ?? false,
283
+ });
284
+ });
285
+ });
237
286
  const icon = program
238
287
  .command('icon')
239
288
  .description('Icon utilities for browser extensions (free, fully offline)');
@@ -250,27 +299,98 @@ icon
250
299
  await runIconPreview(iconPath, opts, pkg.version);
251
300
  });
252
301
  });
302
+ /**
303
+ * Ask for a key by hand. Kept as the escape hatch for `--paste`, for servers
304
+ * whose BFF predates the browser handshake, and for anyone who would simply
305
+ * rather paste one.
306
+ */
307
+ async function promptForKey(apiUrl) {
308
+ console.error('Get a key at https://dojo.extenshi.io/api-keys (sign up at https://auth.extenshi.io/signup).');
309
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
310
+ let apiKey;
311
+ try {
312
+ apiKey = (await rl.question('Extenshi API key: ')).trim();
313
+ }
314
+ finally {
315
+ rl.close();
316
+ }
317
+ if (!apiKey)
318
+ throw new Error('no API key provided.');
319
+ const savedPath = saveConfig({ apiKey, apiUrl });
320
+ console.error(`✓ API key saved to ${savedPath}`);
321
+ }
322
+ /**
323
+ * Browser login: open extenshi.io, sign in or sign up there, and let this
324
+ * process pick the key up. See `login-flow.ts` for the handshake.
325
+ */
326
+ async function loginViaBrowser(apiUrl) {
327
+ const { bffUrl } = await loadConfig(apiUrl);
328
+ const bff = makeLoginBff(bffUrl);
329
+ const outcome = await runBrowserLogin({
330
+ startAuth: (input) => bff.startAuth(input),
331
+ pollAuth: (deviceCode) => bff.pollAuth(deviceCode),
332
+ openLoopback,
333
+ openBrowser,
334
+ sleep,
335
+ now: () => Date.now(),
336
+ // stderr throughout: `login` keeps stdout free so it composes in a pipeline.
337
+ log: (message) => console.error(message),
338
+ }, {
339
+ tool: 'extenshi-cli',
340
+ version: pkg.version,
341
+ platform: process.platform,
342
+ hostname: os.hostname(),
343
+ });
344
+ switch (outcome.kind) {
345
+ case 'success': {
346
+ const savedPath = saveConfig({ apiKey: outcome.apiKey, apiUrl });
347
+ console.error(`\n✓ Connected${outcome.email ? ` as ${outcome.email}` : ''}. Key saved to ${savedPath}.`);
348
+ return;
349
+ }
350
+ case 'denied':
351
+ throw new Error('login declined in the browser — nothing was connected.');
352
+ case 'expired':
353
+ throw new Error('the login timed out before it was approved. Run `extenshi login` again.');
354
+ case 'key_limit':
355
+ throw new Error('your account already has the maximum number of API keys. ' +
356
+ 'Revoke one at https://dojo.extenshi.io/api-keys, then run `extenshi login` again.');
357
+ case 'unsupported':
358
+ // Older BFF: no handshake to speak. Ask for a key rather than fail.
359
+ console.error('Browser login is not available on this server yet — using a key instead.\n');
360
+ await promptForKey(apiUrl);
361
+ return;
362
+ default:
363
+ throw new Error(`${outcome.message} (you can use \`extenshi login --paste\` instead)`);
364
+ }
365
+ }
253
366
  program
254
367
  .command('login')
255
- .description('Save your Extenshi API key to ~/.extenshi/config.json')
256
- .option('--api-key <key>', 'API key (skips the interactive prompt)')
368
+ .summary('sign in through your browser and connect this terminal')
369
+ .description('Opens extenshi.io in your browser to sign in (or create a free account), then connects this ' +
370
+ 'terminal automatically. The key is stored in ~/.extenshi/config.json, shared with @extenshi/guard.')
371
+ .option('--api-key <key>', 'API key (skips the browser entirely)')
372
+ .option('--paste', 'skip the browser and paste an API key instead')
257
373
  .option('--api-url <url>', 'Override the scan API base URL')
258
374
  .action(async (opts) => {
259
375
  await runCommand('login', async () => {
260
- let apiKey = opts.apiKey?.trim();
261
- if (!apiKey) {
262
- const rl = readline.createInterface({
263
- input: process.stdin,
264
- output: process.stderr,
265
- });
266
- apiKey = (await rl.question('Extenshi API key: ')).trim();
267
- rl.close();
376
+ // An explicitly supplied key is the caller telling us not to open anything.
377
+ const provided = opts.apiKey?.trim();
378
+ if (provided) {
379
+ const savedPath = saveConfig({ apiKey: provided, apiUrl: opts.apiUrl });
380
+ console.error(`✓ API key saved to ${savedPath}`);
381
+ return;
382
+ }
383
+ if (opts.paste) {
384
+ await promptForKey(opts.apiUrl);
385
+ return;
268
386
  }
269
- if (!apiKey) {
270
- throw new Error('no API key provided.');
387
+ // Nobody is watching a non-interactive session, so waiting on a browser
388
+ // approval there would just hang until the login expired.
389
+ if (!process.stdin.isTTY) {
390
+ throw new Error('browser login needs an interactive terminal. In CI, set EXTENSHI_API_KEY from your ' +
391
+ 'secrets instead (create a key at https://dojo.extenshi.io/api-keys).');
271
392
  }
272
- const savedPath = saveConfig({ apiKey, apiUrl: opts.apiUrl });
273
- console.error(`✓ API key saved to ${savedPath}`);
393
+ await loginViaBrowser(opts.apiUrl);
274
394
  });
275
395
  });
276
396
  program.parse();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1112,7 +1112,7 @@ export function renderIconPreviewHtml(input) {
1112
1112
  <span class="next-ico">${SCAN_ICON}</span>
1113
1113
  <div class="next-body">
1114
1114
  <code>npx @extenshi/cli scan dist.zip</code>
1115
- <p>Pre-publish security scan of the built artifact, with a free monthly allowance.</p>
1115
+ <p>Pre-publish security scan of the built artifact, with a free allowance.</p>
1116
1116
  </div>
1117
1117
  </div>
1118
1118
  <div class="next-item">
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The two BFF calls the browser login needs, over plain `fetch`.
3
+ *
4
+ * @extenshi/guard talks to the same endpoints through `@trpc/client`; this
5
+ * package deliberately doesn't carry that dependency (it reaches the BFF with
6
+ * `fetch` everywhere else — see `publish-access.ts`), and adding a client
7
+ * library to a published CLI for two calls would be a poor trade for anyone
8
+ * running it through `npx`.
9
+ *
10
+ * Wire format: tRPC v11, un-batched, no data transformer. A mutation is
11
+ * `POST /api/trpc/<path>` with the input as the raw JSON body, answering
12
+ * `{"result":{"data":…}}` or `{"error":{…}}`.
13
+ *
14
+ * Neither call sends an Authorization header — the caller has no key yet, which
15
+ * is the entire point of the handshake.
16
+ */
17
+ import type { LoginPollResponse, LoginStartResponse } from './login-flow.js';
18
+ export interface LoginBff {
19
+ startAuth(input: {
20
+ tool: string;
21
+ version: string;
22
+ platform: string;
23
+ hostname?: string;
24
+ redirectUri?: string;
25
+ }): Promise<LoginStartResponse>;
26
+ pollAuth(deviceCode: string): Promise<LoginPollResponse>;
27
+ }
28
+ export declare function makeLoginBff(bffUrl: string): LoginBff;
29
+ //# sourceMappingURL=login-bff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-bff.d.ts","sourceRoot":"","sources":["../src/login-bff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAkE5E,MAAM,WAAW,QAAQ;IACxB,SAAS,CAAC,KAAK,EAAE;QAChB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,WAAW,CAAC,EAAE,MAAM,CAAA;KACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACxD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAMrD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The two BFF calls the browser login needs, over plain `fetch`.
3
+ *
4
+ * @extenshi/guard talks to the same endpoints through `@trpc/client`; this
5
+ * package deliberately doesn't carry that dependency (it reaches the BFF with
6
+ * `fetch` everywhere else — see `publish-access.ts`), and adding a client
7
+ * library to a published CLI for two calls would be a poor trade for anyone
8
+ * running it through `npx`.
9
+ *
10
+ * Wire format: tRPC v11, un-batched, no data transformer. A mutation is
11
+ * `POST /api/trpc/<path>` with the input as the raw JSON body, answering
12
+ * `{"result":{"data":…}}` or `{"error":{…}}`.
13
+ *
14
+ * Neither call sends an Authorization header — the caller has no key yet, which
15
+ * is the entire point of the handshake.
16
+ */
17
+ /** Long enough for a cold BFF, short enough not to stall the poll cadence. */
18
+ const REQUEST_TIMEOUT_MS = 15_000;
19
+ /**
20
+ * A tRPC error surfaced with the shape `isUnsupportedByServer` reads.
21
+ *
22
+ * The `data.code` matters: `NOT_FOUND` is how a BFF that predates this router
23
+ * answers, and the CLI turns that into "paste a key instead" rather than an
24
+ * error the user can do nothing about.
25
+ */
26
+ class TrpcHttpError extends Error {
27
+ data;
28
+ constructor(message, data) {
29
+ super(message);
30
+ this.data = data;
31
+ this.name = 'TrpcHttpError';
32
+ }
33
+ }
34
+ async function trpcMutation(bffUrl, path, input) {
35
+ const controller = new AbortController();
36
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
37
+ try {
38
+ const res = await fetch(`${bffUrl}/api/trpc/${path}`, {
39
+ method: 'POST',
40
+ headers: { 'Content-Type': 'application/json' },
41
+ body: JSON.stringify(input),
42
+ signal: controller.signal,
43
+ });
44
+ // Parse before checking `res.ok`: tRPC returns its error envelope WITH a
45
+ // non-2xx status, and that envelope carries the code the caller branches on.
46
+ const body = (await res.json().catch(() => null));
47
+ if (body?.error) {
48
+ throw new TrpcHttpError(body.error.message ?? `Request failed (${res.status})`, {
49
+ code: body.error.data?.code,
50
+ httpStatus: body.error.data?.httpStatus ?? res.status,
51
+ });
52
+ }
53
+ if (!res.ok) {
54
+ // A non-tRPC failure (proxy error page, 502 from the load balancer).
55
+ // A bare 404 here means the same thing as tRPC's NOT_FOUND: this
56
+ // deployment doesn't have the router.
57
+ throw new TrpcHttpError(`Request failed (${res.status})`, {
58
+ code: res.status === 404 ? 'NOT_FOUND' : undefined,
59
+ httpStatus: res.status,
60
+ });
61
+ }
62
+ if (!body?.result || body.result.data === undefined) {
63
+ throw new TrpcHttpError('Malformed response from the server', { httpStatus: res.status });
64
+ }
65
+ return body.result.data;
66
+ }
67
+ finally {
68
+ clearTimeout(timer);
69
+ }
70
+ }
71
+ export function makeLoginBff(bffUrl) {
72
+ const base = bffUrl.replace(/\/$/, '');
73
+ return {
74
+ startAuth: (input) => trpcMutation(base, 'cliAuth.start', input),
75
+ pollAuth: (deviceCode) => trpcMutation(base, 'cliAuth.poll', { deviceCode }),
76
+ };
77
+ }
78
+ //# sourceMappingURL=login-bff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-bff.js","sourceRoot":"","sources":["../src/login-bff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,8EAA8E;AAC9E,MAAM,kBAAkB,GAAG,MAAM,CAAA;AAEjC;;;;;;GAMG;AACH,MAAM,aAAc,SAAQ,KAAK;IAGtB;IAFV,YACC,OAAe,EACN,IAA4C;QAErD,KAAK,CAAC,OAAO,CAAC,CAAA;QAFL,SAAI,GAAJ,IAAI,CAAwC;QAGrD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;IAC5B,CAAC;CACD;AAOD,KAAK,UAAU,YAAY,CAAI,MAAc,EAAE,IAAY,EAAE,KAAc;IAC1E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,kBAAkB,CAAC,CAAA;IACtE,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,aAAa,IAAI,EAAE,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAC3B,MAAM,EAAE,UAAU,CAAC,MAAM;SACzB,CAAC,CAAA;QAEF,yEAAyE;QACzE,6EAA6E;QAC7E,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAA2B,CAAA;QAE3E,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YACjB,MAAM,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,mBAAmB,GAAG,CAAC,MAAM,GAAG,EAAE;gBAC/E,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI;gBAC3B,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,IAAI,GAAG,CAAC,MAAM;aACrD,CAAC,CAAA;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACb,qEAAqE;YACrE,iEAAiE;YACjE,sCAAsC;YACtC,MAAM,IAAI,aAAa,CAAC,mBAAmB,GAAG,CAAC,MAAM,GAAG,EAAE;gBACzD,IAAI,EAAE,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;gBAClD,UAAU,EAAE,GAAG,CAAC,MAAM;aACtB,CAAC,CAAA;QACH,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACrD,MAAM,IAAI,aAAa,CAAC,oCAAoC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;QAC1F,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAA;IACxB,CAAC;YAAS,CAAC;QACV,YAAY,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;AACF,CAAC;AAaD,MAAM,UAAU,YAAY,CAAC,MAAc;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACtC,OAAO;QACN,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAqB,IAAI,EAAE,eAAe,EAAE,KAAK,CAAC;QACpF,QAAQ,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,YAAY,CAAoB,IAAI,EAAE,cAAc,EAAE,EAAE,UAAU,EAAE,CAAC;KAC/F,CAAA;AACF,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Browser login, terminal side.
3
+ *
4
+ * `login` opens a browser at extenshi.io, the user signs in (or SIGNS UP — a
5
+ * terminal cannot host a signup form, which is the whole reason this exists),
6
+ * approves the pairing code, and the terminal picks the key up on its own.
7
+ *
8
+ * Two things carry the result back, and only one of them is load-bearing:
9
+ *
10
+ * poll — the source of truth. The key is minted server-side during the
11
+ * claiming poll and returned over TLS, exactly once.
12
+ * loopback — a local one-shot listener the browser is redirected to. It
13
+ * carries NO key: it only wakes the poll loop early so the tab can
14
+ * close itself and the terminal reacts instantly instead of on the
15
+ * next tick. Everything still works when it can't bind a port
16
+ * (locked-down machine, SSH session, a port already taken) — that
17
+ * is the designed fallback, not an error path.
18
+ *
19
+ * Everything here takes its I/O as arguments so the whole state machine is
20
+ * testable without a browser, a socket or a server. That is also what lets one
21
+ * copy serve two published packages with different HTTP stacks: @extenshi/guard
22
+ * speaks tRPC, @extenshi/cli speaks plain fetch, and both hand this the same
23
+ * two functions.
24
+ *
25
+ * TWIN FILE — byte-identical to `tools/extenshi-cli/src/login-flow.ts`.
26
+ * Neither package can import the other at runtime (both are published to npm and
27
+ * built with plain `tsc`), and the repo already duplicates `config.ts` between
28
+ * them for the same reason. Drift is what makes duplication dangerous, so the
29
+ * copies are pinned equal by `login-twin-sync.test.ts` — edit one, copy it over,
30
+ * or the test fails.
31
+ */
32
+ export interface LoginStartResponse {
33
+ deviceCode: string;
34
+ userCode: string;
35
+ verificationUrl: string;
36
+ verificationUrlComplete: string;
37
+ expiresInSec: number;
38
+ intervalSec: number;
39
+ loopbackAccepted: boolean;
40
+ }
41
+ export type LoginPollStatus = 'pending' | 'approved' | 'denied' | 'expired' | 'claimed' | 'key_limit';
42
+ export interface LoginPollResponse {
43
+ status: LoginPollStatus;
44
+ apiKey?: string;
45
+ email?: string | null;
46
+ }
47
+ /** A bound local listener waiting for the browser to come back. */
48
+ export interface LoopbackListener {
49
+ /** `http://127.0.0.1:<port>/callback` — handed to the server, never composed elsewhere. */
50
+ redirectUri: string;
51
+ /** Resolves when the browser hits it (or never, if it doesn't). */
52
+ hit: Promise<void>;
53
+ close(): void;
54
+ }
55
+ export interface LoginDeps {
56
+ startAuth(input: {
57
+ tool: string;
58
+ version: string;
59
+ platform: string;
60
+ hostname?: string;
61
+ redirectUri?: string;
62
+ }): Promise<LoginStartResponse>;
63
+ pollAuth(deviceCode: string): Promise<LoginPollResponse>;
64
+ /** Bind a loopback listener, or null when this machine won't allow one. */
65
+ openLoopback(): Promise<LoopbackListener | null>;
66
+ /** Launch the user's browser. Returns false when there's nothing to launch. */
67
+ openBrowser(url: string): Promise<boolean>;
68
+ /** Resolve after `ms`, or as soon as `wakeup` settles — whichever is first. */
69
+ sleep(ms: number, wakeup?: Promise<unknown>): Promise<void>;
70
+ now(): number;
71
+ /** Progress lines. Everything user-facing goes to stderr so `--json` stays clean. */
72
+ log(message: string): void;
73
+ }
74
+ export type LoginOutcome = {
75
+ kind: 'success';
76
+ apiKey: string;
77
+ email: string | null;
78
+ } | {
79
+ kind: 'denied';
80
+ } | {
81
+ kind: 'expired';
82
+ } | {
83
+ kind: 'key_limit';
84
+ } | {
85
+ kind: 'unsupported';
86
+ } | {
87
+ kind: 'failed';
88
+ message: string;
89
+ };
90
+ export interface LoginClient {
91
+ tool: string;
92
+ version: string;
93
+ platform: string;
94
+ hostname?: string;
95
+ }
96
+ /**
97
+ * An older BFF has no `cliAuth` router and answers NOT_FOUND. That is not a
98
+ * failure the user can act on — it means "this deployment predates browser
99
+ * login", and the caller should fall back to asking for a pasted key. Matched on
100
+ * the tRPC error code AND the shape of the 404 body, because the two BFF
101
+ * versions can be live at once during a rollout.
102
+ */
103
+ export declare function isUnsupportedByServer(err: unknown): boolean;
104
+ /**
105
+ * Run the whole login. Returns an outcome rather than throwing, so the CLI
106
+ * decides how each case is worded (and every case has a next step).
107
+ */
108
+ export declare function runBrowserLogin(deps: LoginDeps, client: LoginClient): Promise<LoginOutcome>;
109
+ //# sourceMappingURL=login-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-flow.d.ts","sourceRoot":"","sources":["../src/login-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAA;AAErG,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,eAAe,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAChC,2FAA2F;IAC3F,WAAW,EAAE,MAAM,CAAA;IACnB,mEAAmE;IACnE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;CACb;AAED,MAAM,WAAW,SAAS;IACzB,SAAS,CAAC,KAAK,EAAE;QAChB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,WAAW,CAAC,EAAE,MAAM,CAAA;KACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACxD,2EAA2E;IAC3E,YAAY,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IAChD,+EAA+E;IAC/E,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1C,+EAA+E;IAC/E,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,GAAG,IAAI,MAAM,CAAA;IACb,qFAAqF;IACrF,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,MAAM,YAAY,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtC,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAO3D;AAaD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAuFjG"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Browser login, terminal side.
3
+ *
4
+ * `login` opens a browser at extenshi.io, the user signs in (or SIGNS UP — a
5
+ * terminal cannot host a signup form, which is the whole reason this exists),
6
+ * approves the pairing code, and the terminal picks the key up on its own.
7
+ *
8
+ * Two things carry the result back, and only one of them is load-bearing:
9
+ *
10
+ * poll — the source of truth. The key is minted server-side during the
11
+ * claiming poll and returned over TLS, exactly once.
12
+ * loopback — a local one-shot listener the browser is redirected to. It
13
+ * carries NO key: it only wakes the poll loop early so the tab can
14
+ * close itself and the terminal reacts instantly instead of on the
15
+ * next tick. Everything still works when it can't bind a port
16
+ * (locked-down machine, SSH session, a port already taken) — that
17
+ * is the designed fallback, not an error path.
18
+ *
19
+ * Everything here takes its I/O as arguments so the whole state machine is
20
+ * testable without a browser, a socket or a server. That is also what lets one
21
+ * copy serve two published packages with different HTTP stacks: @extenshi/guard
22
+ * speaks tRPC, @extenshi/cli speaks plain fetch, and both hand this the same
23
+ * two functions.
24
+ *
25
+ * TWIN FILE — byte-identical to `tools/extenshi-cli/src/login-flow.ts`.
26
+ * Neither package can import the other at runtime (both are published to npm and
27
+ * built with plain `tsc`), and the repo already duplicates `config.ts` between
28
+ * them for the same reason. Drift is what makes duplication dangerous, so the
29
+ * copies are pinned equal by `login-twin-sync.test.ts` — edit one, copy it over,
30
+ * or the test fails.
31
+ */
32
+ /**
33
+ * An older BFF has no `cliAuth` router and answers NOT_FOUND. That is not a
34
+ * failure the user can act on — it means "this deployment predates browser
35
+ * login", and the caller should fall back to asking for a pasted key. Matched on
36
+ * the tRPC error code AND the shape of the 404 body, because the two BFF
37
+ * versions can be live at once during a rollout.
38
+ */
39
+ export function isUnsupportedByServer(err) {
40
+ const message = err instanceof Error ? err.message : String(err);
41
+ const code = err?.data?.code;
42
+ if (code === 'NOT_FOUND')
43
+ return true;
44
+ return (/No "?mutation"? procedure found|cliAuth|404/i.test(message) && /not found|no procedure/i.test(message));
45
+ }
46
+ /**
47
+ * How many consecutive poll failures to absorb before giving up.
48
+ *
49
+ * A poll loop that dies on the first dropped packet would strand a user who has
50
+ * ALREADY approved in the browser — the worst moment to fail, because the key
51
+ * exists and only the terminal doesn't know. Wifi handoffs and laptop sleep both
52
+ * produce short bursts of errors, so transient failures are retried and only a
53
+ * sustained outage aborts.
54
+ */
55
+ const MAX_CONSECUTIVE_POLL_ERRORS = 5;
56
+ /**
57
+ * Run the whole login. Returns an outcome rather than throwing, so the CLI
58
+ * decides how each case is worded (and every case has a next step).
59
+ */
60
+ export async function runBrowserLogin(deps, client) {
61
+ // Bind the listener BEFORE starting the session: the redirect URI has to be
62
+ // part of the session the server creates, and binding can fail.
63
+ const loopback = await deps.openLoopback();
64
+ try {
65
+ let started;
66
+ try {
67
+ started = await deps.startAuth({
68
+ tool: client.tool,
69
+ version: client.version,
70
+ platform: client.platform,
71
+ hostname: client.hostname,
72
+ redirectUri: loopback?.redirectUri,
73
+ });
74
+ }
75
+ catch (err) {
76
+ if (isUnsupportedByServer(err))
77
+ return { kind: 'unsupported' };
78
+ return { kind: 'failed', message: err instanceof Error ? err.message : String(err) };
79
+ }
80
+ // The server decides whether it accepted the loopback URL. If it didn't,
81
+ // stop waiting on a callback that will never come.
82
+ const wakeup = loopback && started.loopbackAccepted ? loopback.hit : undefined;
83
+ const opened = await deps.openBrowser(started.verificationUrlComplete);
84
+ deps.log(opened
85
+ ? 'Opened your browser to finish signing in.'
86
+ : 'Open this URL in a browser to finish signing in:');
87
+ deps.log(` ${started.verificationUrlComplete}`);
88
+ deps.log('');
89
+ deps.log(` Pairing code: ${started.userCode}`);
90
+ deps.log('');
91
+ deps.log('Check that the code matches, then approve. Waiting… (Ctrl-C to cancel)');
92
+ const deadline = deps.now() + started.expiresInSec * 1000;
93
+ const intervalMs = Math.max(1, started.intervalSec) * 1000;
94
+ let consecutiveErrors = 0;
95
+ while (deps.now() < deadline) {
96
+ let res;
97
+ try {
98
+ res = await deps.pollAuth(started.deviceCode);
99
+ consecutiveErrors = 0;
100
+ }
101
+ catch (err) {
102
+ if (isUnsupportedByServer(err))
103
+ return { kind: 'unsupported' };
104
+ consecutiveErrors += 1;
105
+ if (consecutiveErrors >= MAX_CONSECUTIVE_POLL_ERRORS) {
106
+ return {
107
+ kind: 'failed',
108
+ message: err instanceof Error ? err.message : String(err),
109
+ };
110
+ }
111
+ await deps.sleep(intervalMs);
112
+ continue;
113
+ }
114
+ switch (res.status) {
115
+ case 'approved':
116
+ if (!res.apiKey) {
117
+ return { kind: 'failed', message: 'Server approved the login but returned no key.' };
118
+ }
119
+ return { kind: 'success', apiKey: res.apiKey, email: res.email ?? null };
120
+ case 'denied':
121
+ return { kind: 'denied' };
122
+ case 'expired':
123
+ return { kind: 'expired' };
124
+ case 'key_limit':
125
+ return { kind: 'key_limit' };
126
+ case 'claimed':
127
+ // Someone already exchanged this session's code. Not ours to use.
128
+ return {
129
+ kind: 'failed',
130
+ message: 'This login was already completed elsewhere. Run login again.',
131
+ };
132
+ default:
133
+ // Still pending: wait out the interval, but cut it short the moment
134
+ // the browser calls back.
135
+ await deps.sleep(intervalMs, wakeup);
136
+ }
137
+ }
138
+ return { kind: 'expired' };
139
+ }
140
+ finally {
141
+ loopback?.close();
142
+ }
143
+ }
144
+ //# sourceMappingURL=login-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-flow.js","sourceRoot":"","sources":["../src/login-flow.ts"],"names":[],"mappings":"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"}