proof-of-commitment 1.19.0 → 1.20.1

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 (2) hide show
  1. package/index.js +96 -12
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * proof-of-commitment CLI v1.19.0
3
+ * proof-of-commitment CLI v1.20.1
4
4
  * Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
5
5
  * Usage: npx proof-of-commitment [packages...] [options]
6
6
  */
@@ -22,6 +22,22 @@ const JSON_API_HEADERS = {
22
22
  'Accept': 'application/json',
23
23
  };
24
24
 
25
+ /**
26
+ * Build /api/audit request headers, adding Authorization: Bearer <key>
27
+ * when a key is present in COMMIT_API_KEY or ~/.commit/config.
28
+ *
29
+ * Without this, signed-up users hitting 429 stayed stuck: the inline-signup
30
+ * (v1.20.0) and URL signup flows both save the key locally, but the audit
31
+ * call site never read it — so "Re-run your command" still 429'd. Fixed
32
+ * in v1.20.1 after live dogfood confirmed the dead-end (see commit log).
33
+ */
34
+ async function auditHeaders() {
35
+ const key = await readApiKey();
36
+ return key
37
+ ? { ...JSON_API_HEADERS, Authorization: `Bearer ${key}` }
38
+ : JSON_API_HEADERS;
39
+ }
40
+
25
41
  // ANSI color helpers
26
42
  const c = {
27
43
  reset: '\x1b[0m',
@@ -113,14 +129,80 @@ async function handle429(res) {
113
129
  );
114
130
  }
115
131
  console.error('');
116
- console.error(clr(c.cyan + c.bold, ` → Free API key in 30 seconds (no card): ${instantKeyUrl}`));
117
- if (retryAfter && retryAfter > 0) {
118
- const hours = Math.floor(retryAfter / 3600);
119
- const mins = Math.floor((retryAfter % 3600) / 60);
120
- const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
121
- console.error(clr(c.dim, ` or wait — free-tier resets in ${resetIn} (00:00 UTC).`));
132
+
133
+ // TTY: inline signup collapses the 6-step browser flow (visit URL → enter
134
+ // email copy key → switch back to terminal → export key → re-run) to a
135
+ // single terminal prompt. Non-TTY (CI/piped) falls through to the URL.
136
+ if (process.stdin.isTTY && process.stdout.isTTY) {
137
+ console.error(clr(c.dim, ' ─────────────────────────────────────────────'));
138
+ console.error(clr(c.bold, ' Get a free key and keep scanning (no card, saves to ~/.commit/config):'));
139
+ console.error('');
140
+
141
+ const { createInterface } = await import('readline');
142
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
143
+
144
+ const email = await new Promise(resolve => {
145
+ rl.question(clr(c.dim, ' Your email (Enter to skip): '), answer => {
146
+ rl.close();
147
+ resolve(answer.trim());
148
+ });
149
+ });
150
+
151
+ if (email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
152
+ process.stderr.write(clr(c.dim, ' Creating key...'));
153
+ try {
154
+ const createRes = await fetch('https://poc-backend.amdal-dev.workers.dev/api/keys/create', {
155
+ method: 'POST',
156
+ headers: { 'Content-Type': 'application/json' },
157
+ body: JSON.stringify({ email, source: 'audit-cli-429' }),
158
+ });
159
+ const keyData = await createRes.json();
160
+ if (keyData.key) {
161
+ await writeApiKey(keyData.key);
162
+ console.error(clr(c.green, ' ✓ Key saved to ~/.commit/config'));
163
+ console.error(clr(c.dim, ` Backup sent to ${email}`));
164
+ console.error('');
165
+ console.error(clr(c.bold, ' Re-run your command to continue with your new key.'));
166
+ console.error('');
167
+ } else {
168
+ const errMsg = keyData.error === 'rate_limit_exceeded'
169
+ ? 'Too many keys from this IP today — try again tomorrow.'
170
+ : (keyData.message || 'Could not create key. Try the web: ' + instantKeyUrl);
171
+ console.error(clr(c.red, ` Failed: ${errMsg}`));
172
+ console.error('');
173
+ }
174
+ } catch (err) {
175
+ console.error(clr(c.red, ` Error: ${err.message}`));
176
+ console.error(clr(c.dim, ` Try the web: ${instantKeyUrl}`));
177
+ console.error('');
178
+ }
179
+ } else if (email) {
180
+ console.error(clr(c.red, ' Invalid email. Skipped.'));
181
+ console.error(clr(c.dim, ` Try the web: ${instantKeyUrl}`));
182
+ console.error('');
183
+ } else {
184
+ // User pressed Enter to skip — show URL as fallback
185
+ console.error(clr(c.cyan + c.bold, ` → Get a free key later: ${instantKeyUrl}`));
186
+ if (retryAfter && retryAfter > 0) {
187
+ const hours = Math.floor(retryAfter / 3600);
188
+ const mins = Math.floor((retryAfter % 3600) / 60);
189
+ const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
190
+ console.error(clr(c.dim, ` or wait — free-tier resets in ${resetIn} (00:00 UTC).`));
191
+ }
192
+ console.error('');
193
+ }
194
+ } else {
195
+ // Non-TTY fallback: print URL for CI/piped contexts
196
+ console.error(clr(c.cyan + c.bold, ` → Free API key in 30 seconds (no card): ${instantKeyUrl}`));
197
+ if (retryAfter && retryAfter > 0) {
198
+ const hours = Math.floor(retryAfter / 3600);
199
+ const mins = Math.floor((retryAfter % 3600) / 60);
200
+ const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
201
+ console.error(clr(c.dim, ` or wait — free-tier resets in ${resetIn} (00:00 UTC).`));
202
+ }
203
+ console.error('');
122
204
  }
123
- console.error('');
205
+
124
206
  process.exit(1);
125
207
  }
126
208
 
@@ -396,7 +478,7 @@ async function inlineSignup(results) {
396
478
 
397
479
  function printHelp() {
398
480
  console.log(`
399
- ${clr(c.bold, 'proof-of-commitment')} v1.19.0 — supply chain risk scorer
481
+ ${clr(c.bold, 'proof-of-commitment')} v1.20.1 — supply chain risk scorer
400
482
 
401
483
  ${clr(c.bold, 'Usage:')}
402
484
  npx proof-of-commitment Auto-detect manifest in current dir
@@ -810,11 +892,13 @@ async function auditBatched(packages, ecosystem, { onProgress } = {}) {
810
892
 
811
893
  let completed = 0;
812
894
  let batchedCta = null;
895
+ // Resolve auth once so all parallel batches share the same key lookup.
896
+ const headers = await auditHeaders();
813
897
  const results = await Promise.all(
814
898
  batches.map(async (batch) => {
815
899
  const res = await fetch(API, {
816
900
  method: 'POST',
817
- headers: JSON_API_HEADERS,
901
+ headers,
818
902
  body: JSON.stringify({ packages: batch, ecosystem }),
819
903
  });
820
904
  if (!res.ok) {
@@ -1413,7 +1497,7 @@ async function cmdReport(packages, ecosystem, { filePath, isLockfile, totalScann
1413
1497
  if (packages.length <= 20) {
1414
1498
  const res = await fetch(API, {
1415
1499
  method: 'POST',
1416
- headers: JSON_API_HEADERS,
1500
+ headers: await auditHeaders(),
1417
1501
  body: JSON.stringify({ packages, ecosystem }),
1418
1502
  });
1419
1503
  if (!res.ok) {
@@ -1815,7 +1899,7 @@ async function main() {
1815
1899
  try {
1816
1900
  const res = await fetch(API, {
1817
1901
  method: 'POST',
1818
- headers: JSON_API_HEADERS,
1902
+ headers: await auditHeaders(),
1819
1903
  body: JSON.stringify({ packages, ecosystem }),
1820
1904
  });
1821
1905
  if (!res.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-of-commitment",
3
- "version": "1.19.0",
3
+ "version": "1.20.1",
4
4
  "mcpName": "io.github.piiiico/proof-of-commitment",
5
5
  "description": "Supply chain risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
6
6
  "type": "module",