@extenshi/cli 0.0.3 → 0.0.5

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
@@ -26,7 +26,7 @@ const MISSING_KEY_HELP = 'Error: API key not found.\n' +
26
26
  ' • run: extenshi login\n' +
27
27
  ' • set the EXTENSHI_API_KEY environment variable\n' +
28
28
  ' • add EXTENSHI_API_KEY=... to a .env file in this directory\n' +
29
- 'Get an API key at https://dojo.extenshi.io/billing/cli';
29
+ 'Get an API key at https://dojo.extenshi.io/billing';
30
30
  const _require = createRequire(import.meta.url);
31
31
  const pkg = _require('../package.json');
32
32
  program
@@ -37,8 +37,9 @@ program
37
37
  .command('scan <artifact>')
38
38
  .description('Scan a browser extension artifact (.crx, .xpi, or .zip)')
39
39
  .option('--json', 'Output raw JSON (default: pretty-printed findings)')
40
- .option('--extension-id <id>', 'Extension store ID (required for free-credit scans on claimed extensions)')
40
+ .option('--extension-id <id>', "Numeric catalog ID of a verified extension (required to spend free credits; see the Ownership tab in dojo). Paid credits don't need it.")
41
41
  .option('--skip-preflight', 'Skip CI timeout pre-flight probe (use at your own risk)')
42
+ .option('--no-stream', 'Disable live progress streaming; wait for a single final JSON response')
42
43
  .option('--api-url <url>', 'Override the scan API base URL (default: https://scan.extenshi.io)')
43
44
  .action(async (artifact, opts) => {
44
45
  try {
@@ -56,6 +57,8 @@ program
56
57
  extensionId: opts.extensionId,
57
58
  outputJson: opts.json ?? false,
58
59
  skipPreflight: opts.skipPreflight ?? false,
60
+ // commander maps --no-stream to opts.stream === false; default on.
61
+ stream: opts.stream ?? true,
59
62
  });
60
63
  }
61
64
  catch (err) {
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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,eAAO,MAAM,UAAU,QAAuC,CAAA;AAC9D,eAAO,MAAM,WAAW,QAAuC,CAAA;AAE/D,UAAU,UAAU;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AA0CD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,IAAI,CAY5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CASrD;AAED,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAWvG"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,eAAO,MAAM,UAAU,QAAuC,CAAA;AAC9D,eAAO,MAAM,WAAW,QAAuC,CAAA;AAE/D,UAAU,UAAU;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAuCD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,IAAI,CAY5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CASrD;AAED,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAWvG"}
package/dist/config.js CHANGED
@@ -43,8 +43,7 @@ function parseDotEnv(raw) {
43
43
  continue;
44
44
  let value = withoutExport.slice(eq + 1).trim();
45
45
  // Strip matching surrounding quotes
46
- if ((value.startsWith('"') && value.endsWith('"')) ||
47
- (value.startsWith("'") && value.endsWith("'"))) {
46
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
48
47
  value = value.slice(1, -1);
49
48
  }
50
49
  out[key] = value;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/scan.d.ts CHANGED
@@ -22,6 +22,27 @@
22
22
  * "при network error не делайте автоматический retry —
23
23
  * списанный credit вернётся автоматически в течение 1 минуты (auto-refund cron)"
24
24
  */
25
+ /**
26
+ * Build the user-facing message for a 403 from the scan API.
27
+ *
28
+ * The free credit tier is gated: free credits ONLY cover extensions whose
29
+ * ownership you have verified, and ONLY when you name one with
30
+ * `--extension-id <id>` (the numeric catalog ID of a verified extension). A
31
+ * bare `extenshi scan <artifact>` running on free credits therefore 403s —
32
+ * which is confusing when dojo just says "5 free scans left".
33
+ *
34
+ * The BFF signals this case via a stable `errorCode`
35
+ * (`FREE_REQUIRES_CLAIM` / `FREE_REQUIRES_VERIFIED_OWNERSHIP`). The CLI ships
36
+ * via npm independently of the BFF deploy, so during version skew the
37
+ * deployed BFF may not set `errorCode` yet — we fall back to matching the
38
+ * human `error` text. Either way we append actionable next steps.
39
+ *
40
+ * Exported for unit testing.
41
+ */
42
+ export declare function buildAccessDeniedMessage(body: {
43
+ error?: unknown;
44
+ errorCode?: unknown;
45
+ }): string;
25
46
  export interface ScanOptions {
26
47
  artifactPath: string;
27
48
  apiKey: string;
@@ -29,6 +50,8 @@ export interface ScanOptions {
29
50
  extensionId?: string;
30
51
  outputJson: boolean;
31
52
  skipPreflight: boolean;
53
+ /** Request live progress over SSE (default). `false` forces the legacy single-JSON mode. */
54
+ stream: boolean;
32
55
  }
33
56
  export declare function runScan(opts: ScanOptions): Promise<void>;
34
57
  //# sourceMappingURL=scan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAUH,MAAM,WAAW,WAAW;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;CACtB;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAsG9D"}
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CA0B/F;AAqCD,MAAM,WAAW,WAAW;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;IACtB,4FAA4F;IAC5F,MAAM,EAAE,OAAO,CAAA;CACf;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkG9D"}
package/dist/scan.js CHANGED
@@ -24,12 +24,84 @@
24
24
  */
25
25
  import fs from 'node:fs';
26
26
  import path from 'node:path';
27
+ import ora from 'ora';
27
28
  import { FormData, fetch } from 'undici';
28
29
  import { renderReport } from './render.js';
30
+ import { parseSseFrames } from './sse.js';
29
31
  const SCAN_TIMEOUT_MS = 240_000; // 240 seconds
30
32
  const PREFLIGHT_TIMEOUT_MS = 70_000; // 70 seconds
33
+ const CLI_BILLING_URL = 'https://dojo.extenshi.io/billing';
34
+ const DOJO_URL = 'https://dojo.extenshi.io';
35
+ /**
36
+ * Build the user-facing message for a 403 from the scan API.
37
+ *
38
+ * The free credit tier is gated: free credits ONLY cover extensions whose
39
+ * ownership you have verified, and ONLY when you name one with
40
+ * `--extension-id <id>` (the numeric catalog ID of a verified extension). A
41
+ * bare `extenshi scan <artifact>` running on free credits therefore 403s —
42
+ * which is confusing when dojo just says "5 free scans left".
43
+ *
44
+ * The BFF signals this case via a stable `errorCode`
45
+ * (`FREE_REQUIRES_CLAIM` / `FREE_REQUIRES_VERIFIED_OWNERSHIP`). The CLI ships
46
+ * via npm independently of the BFF deploy, so during version skew the
47
+ * deployed BFF may not set `errorCode` yet — we fall back to matching the
48
+ * human `error` text. Either way we append actionable next steps.
49
+ *
50
+ * Exported for unit testing.
51
+ */
52
+ export function buildAccessDeniedMessage(body) {
53
+ const errorMsg = typeof body.error === 'string' ? body.error : 'access denied';
54
+ const code = typeof body.errorCode === 'string' ? body.errorCode : '';
55
+ const isFreeGating = code === 'FREE_REQUIRES_CLAIM' ||
56
+ code === 'FREE_REQUIRES_VERIFIED_OWNERSHIP' ||
57
+ /free credit/i.test(errorMsg);
58
+ if (!isFreeGating) {
59
+ return `Access denied: ${errorMsg}`;
60
+ }
61
+ return (`Access denied: ${errorMsg}\n\n` +
62
+ `Free credits only cover extensions you have verified ownership of, and you\n` +
63
+ `must name one with --extension-id <id>:\n\n` +
64
+ ` extenshi scan <artifact> --extension-id <id>\n\n` +
65
+ `where <id> is the numeric catalog ID of a verified extension (open the\n` +
66
+ `Ownership tab at ${DOJO_URL} to see your verified extensions and their IDs).\n\n` +
67
+ `Other options:\n` +
68
+ ` • Claim ownership of an extension you publish:\n` +
69
+ ` ${DOJO_URL} → Ownership tab → Claim Extension Ownership\n` +
70
+ ` • Buy credits to scan arbitrary / unclaimed artifacts:\n` +
71
+ ` ${CLI_BILLING_URL}`);
72
+ }
73
+ /**
74
+ * A spinner whose label shows live elapsed seconds, rendered to stderr so it
75
+ * never corrupts --json output on stdout. ora auto-disables animation when
76
+ * stderr is not a TTY (CI), degrading to a couple of static log lines.
77
+ *
78
+ * Returns handles to settle it: `succeed` (green ✔), `stop` (clear line).
79
+ * Always call exactly one of them — the elapsed ticker keeps running otherwise.
80
+ */
81
+ function startElapsedSpinner(label) {
82
+ const startedAt = Date.now();
83
+ const seconds = () => Math.round((Date.now() - startedAt) / 1000);
84
+ const spinner = ora({ text: label, stream: process.stderr }).start();
85
+ const ticker = setInterval(() => {
86
+ spinner.text = `${label} (${seconds()}s)`;
87
+ }, 1000);
88
+ // Don't let the 1s ticker keep the event loop alive on its own.
89
+ if (typeof ticker.unref === 'function')
90
+ ticker.unref();
91
+ return {
92
+ succeed: (text) => {
93
+ clearInterval(ticker);
94
+ spinner.succeed(text);
95
+ },
96
+ stop: () => {
97
+ clearInterval(ticker);
98
+ spinner.stop();
99
+ },
100
+ seconds,
101
+ };
102
+ }
31
103
  export async function runScan(opts) {
32
- const { artifactPath, apiKey, apiUrl, extensionId, outputJson, skipPreflight } = opts;
104
+ const { artifactPath, apiKey, apiUrl, extensionId, outputJson, skipPreflight, stream } = opts;
33
105
  // Validate artifact file exists
34
106
  if (!fs.existsSync(artifactPath)) {
35
107
  throw new Error(`Artifact file not found: ${artifactPath}`);
@@ -42,12 +114,14 @@ export async function runScan(opts) {
42
114
  if (artifactStats.size > MAX_SIZE) {
43
115
  throw new Error(`Artifact file exceeds 50 MB limit (${(artifactStats.size / 1024 / 1024).toFixed(1)} MB)`);
44
116
  }
45
- // Step 1: CI timeout pre-flight
46
- if (!skipPreflight) {
117
+ // Step 1: CI timeout pre-flight.
118
+ // Skipped in streaming mode: the SSE keep-alive pings keep the connection
119
+ // alive past any outbound idle timeout, which is the very failure the
120
+ // pre-flight probe exists to detect — so it's redundant there.
121
+ if (!skipPreflight && !stream) {
47
122
  await runPreflight(apiUrl);
48
123
  }
49
124
  // Step 2: Upload and scan
50
- process.stderr.write('Scanning...\n');
51
125
  const artifactData = fs.readFileSync(artifactPath);
52
126
  const artifactName = path.basename(artifactPath);
53
127
  const form = new FormData();
@@ -56,15 +130,24 @@ export async function runScan(opts) {
56
130
  if (extensionId) {
57
131
  form.append('extensionId', extensionId);
58
132
  }
133
+ // Legacy (non-stream) mode is one long-blocking request with no server-side
134
+ // progress, so show an elapsed-seconds spinner. Streaming mode draws live
135
+ // per-scanner progress in renderSseStream instead, so no elapsed spinner.
136
+ const progress = stream ? null : startElapsedSpinner(`Scanning ${artifactName}`);
59
137
  let response;
60
138
  try {
61
139
  response = await fetch(`${apiUrl}/api/v1/scan`, {
62
140
  method: 'POST',
63
141
  body: form,
142
+ // Negotiate streaming progress. Auth/billing errors still come back
143
+ // as JSON regardless, so the response handling branches on the
144
+ // actual Content-Type below, not on this header.
145
+ headers: stream ? { accept: 'text/event-stream' } : undefined,
64
146
  signal: AbortSignal.timeout(SCAN_TIMEOUT_MS),
65
147
  });
66
148
  }
67
149
  catch (err) {
150
+ progress?.stop();
68
151
  if (err instanceof Error && err.name === 'TimeoutError') {
69
152
  throw new Error(`Scan request timed out after ${SCAN_TIMEOUT_MS / 1000}s.\n` +
70
153
  'This may indicate a network issue or a slow scan.\n' +
@@ -73,14 +156,50 @@ export async function runScan(opts) {
73
156
  }
74
157
  throw err;
75
158
  }
76
- // Read as text first the endpoint may return an HTML/plain-text error
77
- // page (404 from a misrouted gateway, 502 from Cloudflare, etc.) instead of
78
- // JSON. Blindly calling response.json() on that throws a cryptic
79
- // "Unexpected token '<'" SyntaxError, hiding the real failure.
80
- const raw = await response.text();
159
+ // Auth/billing/gateway errors are always JSON (they happen before any
160
+ // stream starts), regardless of the Accept header handle and throw.
161
+ if (!response.ok) {
162
+ progress?.stop();
163
+ const body = await parseJsonBody(response, apiUrl);
164
+ throwForStatus(response.status, body);
165
+ }
166
+ // Step 3: Output.
167
+ // Streaming success is text/event-stream; legacy success (or a server that
168
+ // ignored the Accept header) is a single JSON body. Branch on the actual
169
+ // Content-Type rather than assuming the negotiated mode.
170
+ const contentType = response.headers.get('content-type') ?? '';
171
+ if (contentType.includes('text/event-stream')) {
172
+ // progress is null in streaming mode — renderSseStream owns its spinner.
173
+ await renderSseStream(response, outputJson);
174
+ return;
175
+ }
81
176
  let body;
82
177
  try {
83
- body = JSON.parse(raw);
178
+ body = await parseJsonBody(response, apiUrl);
179
+ }
180
+ catch (err) {
181
+ progress?.stop();
182
+ throw err;
183
+ }
184
+ if (progress)
185
+ progress.succeed(`Scan complete in ${progress.seconds()}s`);
186
+ if (outputJson) {
187
+ process.stdout.write(`${JSON.stringify(body, null, 2)}\n`);
188
+ }
189
+ else {
190
+ renderReport(body);
191
+ }
192
+ }
193
+ /**
194
+ * Read the response body as JSON, with a clear error if the endpoint returns
195
+ * an HTML/plain-text page instead (404 from a misrouted gateway, 502 from
196
+ * Cloudflare, etc.) — blindly calling response.json() on that throws a cryptic
197
+ * "Unexpected token '<'" SyntaxError that hides the real failure.
198
+ */
199
+ async function parseJsonBody(response, apiUrl) {
200
+ const raw = await response.text();
201
+ try {
202
+ return JSON.parse(raw);
84
203
  }
85
204
  catch {
86
205
  const snippet = raw.replace(/\s+/g, ' ').trim().slice(0, 200);
@@ -91,30 +210,113 @@ export async function runScan(opts) {
91
210
  (snippet ? ` Response: ${snippet}\n` : '') +
92
211
  `Override the endpoint with --api-url if you use a custom deployment.`);
93
212
  }
94
- if (!response.ok) {
95
- const errorMsg = typeof body.error === 'string' ? body.error : `HTTP ${response.status}`;
96
- // Map known status codes to actionable messages
97
- if (response.status === 401) {
98
- throw new Error(`Authentication failed: ${errorMsg}\nCheck your EXTENSHI_API_KEY.`);
213
+ }
214
+ /** Map a non-2xx status + parsed body to an actionable thrown error. */
215
+ function throwForStatus(status, body) {
216
+ const errorMsg = typeof body.error === 'string' ? body.error : `HTTP ${status}`;
217
+ if (status === 401) {
218
+ throw new Error(`Authentication failed: ${errorMsg}\nCheck your EXTENSHI_API_KEY.`);
219
+ }
220
+ if (status === 402) {
221
+ throw new Error(`Insufficient credits.\nPurchase more at ${CLI_BILLING_URL}`);
222
+ }
223
+ if (status === 403) {
224
+ throw new Error(buildAccessDeniedMessage(body));
225
+ }
226
+ if (status === 429) {
227
+ const retryAfter = body.retryAfterSec ?? 60;
228
+ throw new Error(`Rate limit exceeded. Please wait ${retryAfter}s before trying again.`);
229
+ }
230
+ throw new Error(`Scan failed: ${errorMsg}`);
231
+ }
232
+ function safeJsonParse(data) {
233
+ try {
234
+ return JSON.parse(data);
235
+ }
236
+ catch {
237
+ return null;
238
+ }
239
+ }
240
+ /** Render a unicode progress bar like `[█████░░░░░]`. */
241
+ function progressBar(completed, total, width = 18) {
242
+ const filled = total > 0 ? Math.min(width, Math.round((completed / total) * width)) : 0;
243
+ return `[${'█'.repeat(filled)}${'░'.repeat(Math.max(0, width - filled))}]`;
244
+ }
245
+ /** Turn a progress event into a spinner line. */
246
+ function formatProgress(p) {
247
+ const stage = p.stage ?? '';
248
+ const completed = typeof p.completed === 'number' ? p.completed : 0;
249
+ const total = typeof p.total === 'number' ? p.total : null;
250
+ if (stage === 'accepted')
251
+ return 'Queued…';
252
+ if (stage === 'extracting')
253
+ return 'Extracting artifact…';
254
+ if (stage === 'scanning' && total) {
255
+ const scanner = p.scanner ? ` ${p.scanner}` : '';
256
+ return `Scanning ${progressBar(completed, total)} ${completed}/${total}${scanner}`;
257
+ }
258
+ // Unknown/future stage — show the raw label rather than erroring.
259
+ return `${stage || 'Working'}…`;
260
+ }
261
+ /**
262
+ * Consume the SSE progress stream, drawing a live spinner/bar on stderr, and
263
+ * render the final report (pretty or JSON) once the terminal `result` event
264
+ * arrives. Throws on a terminal `error` event or a truncated stream.
265
+ */
266
+ async function renderSseStream(response, outputJson) {
267
+ if (!response.body) {
268
+ throw new Error('Scan stream had no response body');
269
+ }
270
+ const spinner = ora({ stream: process.stderr, text: 'Connecting…' }).start();
271
+ const decoder = new TextDecoder();
272
+ let buffer = '';
273
+ let report = null;
274
+ let errorMessage = null;
275
+ const handle = (ev) => {
276
+ if (ev.event === 'progress') {
277
+ const p = safeJsonParse(ev.data);
278
+ if (p)
279
+ spinner.text = formatProgress(p);
99
280
  }
100
- if (response.status === 402) {
101
- throw new Error(`Insufficient credits.\nPurchase more at https://dojo.extenshi.io/billing/cli`);
281
+ else if (ev.event === 'result') {
282
+ report = safeJsonParse(ev.data);
102
283
  }
103
- if (response.status === 403) {
104
- throw new Error(`Access denied: ${errorMsg}`);
284
+ else if (ev.event === 'error') {
285
+ const e = safeJsonParse(ev.data);
286
+ errorMessage = e && typeof e.error === 'string' ? e.error : 'scan failed';
105
287
  }
106
- if (response.status === 429) {
107
- const retryAfter = body.retryAfterSec ?? 60;
108
- throw new Error(`Rate limit exceeded. Please wait ${retryAfter}s before trying again.`);
288
+ };
289
+ try {
290
+ for await (const chunk of response.body) {
291
+ buffer += decoder.decode(chunk, { stream: true });
292
+ const { events, rest } = parseSseFrames(buffer);
293
+ buffer = rest;
294
+ for (const ev of events)
295
+ handle(ev);
109
296
  }
110
- throw new Error(`Scan failed: ${errorMsg}`);
297
+ // Flush any trailing frame not terminated by a blank line.
298
+ const { events } = parseSseFrames(`${buffer}\n\n`);
299
+ for (const ev of events)
300
+ handle(ev);
111
301
  }
112
- // Step 3: Output
302
+ catch (err) {
303
+ spinner.fail('Scan stream interrupted');
304
+ throw err;
305
+ }
306
+ if (errorMessage) {
307
+ spinner.fail(errorMessage);
308
+ throw new Error(`Scan failed: ${errorMessage}`);
309
+ }
310
+ if (!report) {
311
+ spinner.fail('Scan ended without a result');
312
+ throw new Error('Scan stream ended before returning a result');
313
+ }
314
+ spinner.succeed('Scan complete');
113
315
  if (outputJson) {
114
- process.stdout.write(`${JSON.stringify(body, null, 2)}\n`);
316
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
115
317
  }
116
318
  else {
117
- renderReport(body);
319
+ renderReport(report);
118
320
  }
119
321
  }
120
322
  /**
@@ -125,13 +327,16 @@ export async function runScan(opts) {
125
327
  * shorter than the 240s the scan needs.
126
328
  */
127
329
  async function runPreflight(apiUrl) {
330
+ const progress = startElapsedSpinner('Checking network/CI timeout');
128
331
  try {
129
332
  await fetch(`${apiUrl}/api/v1/healthz?delay=65`, {
130
333
  signal: AbortSignal.timeout(PREFLIGHT_TIMEOUT_MS),
131
334
  });
132
335
  // Got response → timeout is at least 70s, proceed
336
+ progress.stop();
133
337
  }
134
338
  catch (err) {
339
+ progress.stop();
135
340
  if (err instanceof Error && err.name === 'TimeoutError') {
136
341
  throw new Error(`Your CI/network outbound HTTP timeout appears shorter than the 240s extenshi scan needs.\n\n` +
137
342
  `The pre-flight probe (65s delay) did not complete within 70s.\n\n` +
package/dist/scan.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}
package/dist/sse.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Minimal Server-Sent Events frame parser for the scan progress stream.
3
+ *
4
+ * The worker emits `text/event-stream` with named events (`progress`,
5
+ * `result`, `error`) plus keep-alive comment frames (`:ping`). See
6
+ * services/extenshi-scan-worker/PROGRESS_PROTOCOL.md.
7
+ *
8
+ * This module is intentionally pure (no I/O) so it can be unit-tested:
9
+ * feed it the accumulated buffer, get back the complete events plus the
10
+ * unconsumed tail to carry into the next chunk.
11
+ */
12
+ export interface SseEvent {
13
+ /** The `event:` field value (defaults to "message" per the SSE spec). */
14
+ event: string;
15
+ /** The joined `data:` field value(s). */
16
+ data: string;
17
+ }
18
+ /**
19
+ * Split an accumulated SSE buffer into complete events.
20
+ *
21
+ * Frames are delimited by a blank line (`\n\n`). A partial frame at the end is
22
+ * returned as `rest` so the caller can prepend it to the next chunk. Comment
23
+ * frames (lines starting with `:`, e.g. keep-alive pings) and frames with no
24
+ * `data` are skipped.
25
+ */
26
+ export declare function parseSseFrames(buffer: string): {
27
+ events: SseEvent[];
28
+ rest: string;
29
+ };
30
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,QAAQ;IACxB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAA;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAA;CACZ;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAmCnF"}
package/dist/sse.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Minimal Server-Sent Events frame parser for the scan progress stream.
3
+ *
4
+ * The worker emits `text/event-stream` with named events (`progress`,
5
+ * `result`, `error`) plus keep-alive comment frames (`:ping`). See
6
+ * services/extenshi-scan-worker/PROGRESS_PROTOCOL.md.
7
+ *
8
+ * This module is intentionally pure (no I/O) so it can be unit-tested:
9
+ * feed it the accumulated buffer, get back the complete events plus the
10
+ * unconsumed tail to carry into the next chunk.
11
+ */
12
+ /**
13
+ * Split an accumulated SSE buffer into complete events.
14
+ *
15
+ * Frames are delimited by a blank line (`\n\n`). A partial frame at the end is
16
+ * returned as `rest` so the caller can prepend it to the next chunk. Comment
17
+ * frames (lines starting with `:`, e.g. keep-alive pings) and frames with no
18
+ * `data` are skipped.
19
+ */
20
+ export function parseSseFrames(buffer) {
21
+ // Normalize CRLF so frame splitting on `\n\n` is reliable regardless of
22
+ // the server's line endings. A `\r\n` straddling a chunk boundary stays in
23
+ // `rest` and is normalized on the next pass.
24
+ let buf = buffer.replace(/\r\n/g, '\n');
25
+ const events = [];
26
+ while (true) {
27
+ const boundary = buf.indexOf('\n\n');
28
+ if (boundary === -1)
29
+ break;
30
+ const frame = buf.slice(0, boundary);
31
+ buf = buf.slice(boundary + 2);
32
+ let eventName = 'message';
33
+ const dataLines = [];
34
+ for (const line of frame.split('\n')) {
35
+ if (line === '' || line.startsWith(':'))
36
+ continue; // blank or comment (keep-alive)
37
+ const colon = line.indexOf(':');
38
+ const field = colon === -1 ? line : line.slice(0, colon);
39
+ let value = colon === -1 ? '' : line.slice(colon + 1);
40
+ if (value.startsWith(' '))
41
+ value = value.slice(1); // strip one leading space
42
+ if (field === 'event')
43
+ eventName = value;
44
+ else if (field === 'data')
45
+ dataLines.push(value);
46
+ }
47
+ if (dataLines.length > 0) {
48
+ events.push({ event: eventName, data: dataLines.join('\n') });
49
+ }
50
+ }
51
+ return { events, rest: buf };
52
+ }
53
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC5C,wEAAwE;IACxE,2EAA2E;IAC3E,6CAA6C;IAC7C,IAAI,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IACvC,MAAM,MAAM,GAAe,EAAE,CAAA;IAE7B,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACpC,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,MAAK;QAE1B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACpC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAA;QAE7B,IAAI,SAAS,GAAG,SAAS,CAAA;QACzB,MAAM,SAAS,GAAa,EAAE,CAAA;QAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAQ,CAAC,gCAAgC;YAElF,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC/B,MAAM,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;YACxD,IAAI,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;YACrD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,CAAC,0BAA0B;YAE5E,IAAI,KAAK,KAAK,OAAO;gBAAE,SAAS,GAAG,KAAK,CAAA;iBACnC,IAAI,KAAK,KAAK,MAAM;gBAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACjD,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC9D,CAAC;IACF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;AAC7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@extenshi/cli",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Pre-publish security scanner for browser extensions",
5
5
  "type": "module",
6
6
  "publishConfig": {