proof-of-commitment 1.18.2 → 1.20.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/index.js +104 -26
- 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.
|
|
3
|
+
* proof-of-commitment CLI v1.20.0
|
|
4
4
|
* Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
|
|
5
5
|
* Usage: npx proof-of-commitment [packages...] [options]
|
|
6
6
|
*/
|
|
@@ -113,14 +113,80 @@ async function handle429(res) {
|
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
115
|
console.error('');
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.error(clr(c.dim,
|
|
116
|
+
|
|
117
|
+
// TTY: inline signup collapses the 6-step browser flow (visit URL → enter
|
|
118
|
+
// email → copy key → switch back to terminal → export key → re-run) to a
|
|
119
|
+
// single terminal prompt. Non-TTY (CI/piped) falls through to the URL.
|
|
120
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
121
|
+
console.error(clr(c.dim, ' ─────────────────────────────────────────────'));
|
|
122
|
+
console.error(clr(c.bold, ' Get a free key and keep scanning (no card, saves to ~/.commit/config):'));
|
|
123
|
+
console.error('');
|
|
124
|
+
|
|
125
|
+
const { createInterface } = await import('readline');
|
|
126
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
127
|
+
|
|
128
|
+
const email = await new Promise(resolve => {
|
|
129
|
+
rl.question(clr(c.dim, ' Your email (Enter to skip): '), answer => {
|
|
130
|
+
rl.close();
|
|
131
|
+
resolve(answer.trim());
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
136
|
+
process.stderr.write(clr(c.dim, ' Creating key...'));
|
|
137
|
+
try {
|
|
138
|
+
const createRes = await fetch('https://poc-backend.amdal-dev.workers.dev/api/keys/create', {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify({ email, source: 'audit-cli-429' }),
|
|
142
|
+
});
|
|
143
|
+
const keyData = await createRes.json();
|
|
144
|
+
if (keyData.key) {
|
|
145
|
+
await writeApiKey(keyData.key);
|
|
146
|
+
console.error(clr(c.green, ' ✓ Key saved to ~/.commit/config'));
|
|
147
|
+
console.error(clr(c.dim, ` Backup sent to ${email}`));
|
|
148
|
+
console.error('');
|
|
149
|
+
console.error(clr(c.bold, ' Re-run your command to continue with your new key.'));
|
|
150
|
+
console.error('');
|
|
151
|
+
} else {
|
|
152
|
+
const errMsg = keyData.error === 'rate_limit_exceeded'
|
|
153
|
+
? 'Too many keys from this IP today — try again tomorrow.'
|
|
154
|
+
: (keyData.message || 'Could not create key. Try the web: ' + instantKeyUrl);
|
|
155
|
+
console.error(clr(c.red, ` Failed: ${errMsg}`));
|
|
156
|
+
console.error('');
|
|
157
|
+
}
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.error(clr(c.red, ` Error: ${err.message}`));
|
|
160
|
+
console.error(clr(c.dim, ` Try the web: ${instantKeyUrl}`));
|
|
161
|
+
console.error('');
|
|
162
|
+
}
|
|
163
|
+
} else if (email) {
|
|
164
|
+
console.error(clr(c.red, ' Invalid email. Skipped.'));
|
|
165
|
+
console.error(clr(c.dim, ` Try the web: ${instantKeyUrl}`));
|
|
166
|
+
console.error('');
|
|
167
|
+
} else {
|
|
168
|
+
// User pressed Enter to skip — show URL as fallback
|
|
169
|
+
console.error(clr(c.cyan + c.bold, ` → Get a free key later: ${instantKeyUrl}`));
|
|
170
|
+
if (retryAfter && retryAfter > 0) {
|
|
171
|
+
const hours = Math.floor(retryAfter / 3600);
|
|
172
|
+
const mins = Math.floor((retryAfter % 3600) / 60);
|
|
173
|
+
const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
|
174
|
+
console.error(clr(c.dim, ` or wait — free-tier resets in ${resetIn} (00:00 UTC).`));
|
|
175
|
+
}
|
|
176
|
+
console.error('');
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
// Non-TTY fallback: print URL for CI/piped contexts
|
|
180
|
+
console.error(clr(c.cyan + c.bold, ` → Free API key in 30 seconds (no card): ${instantKeyUrl}`));
|
|
181
|
+
if (retryAfter && retryAfter > 0) {
|
|
182
|
+
const hours = Math.floor(retryAfter / 3600);
|
|
183
|
+
const mins = Math.floor((retryAfter % 3600) / 60);
|
|
184
|
+
const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
|
185
|
+
console.error(clr(c.dim, ` or wait — free-tier resets in ${resetIn} (00:00 UTC).`));
|
|
186
|
+
}
|
|
187
|
+
console.error('');
|
|
122
188
|
}
|
|
123
|
-
|
|
189
|
+
|
|
124
190
|
process.exit(1);
|
|
125
191
|
}
|
|
126
192
|
|
|
@@ -293,15 +359,11 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
293
359
|
console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login'));
|
|
294
360
|
}
|
|
295
361
|
// else: TTY mode — inlineSignup() will prompt interactively after printTable
|
|
296
|
-
} else if (!hasKey) {
|
|
297
|
-
// HEALTHY case + no saved key:
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
// hidden behind the CRITICAL gate of inlineSignup(). Buyer-journey
|
|
302
|
-
// dogfood 2026-05-24 found 1472 weekly downloads → 0 organic signups;
|
|
303
|
-
// the watchlist value prop ("alert me when these degrade") is real
|
|
304
|
-
// for healthy packages too — that's exactly when monitoring matters.
|
|
362
|
+
} else if (!hasKey && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
|
363
|
+
// HEALTHY case + no saved key + non-TTY (CI/piped): static baseline CTA.
|
|
364
|
+
// In TTY mode, inlineSignup() now prompts interactively for healthy results
|
|
365
|
+
// too — the dim text below converted 0/621 weekly downloads. Keep static
|
|
366
|
+
// text only in CI/piped output where interactive prompts can't fire.
|
|
305
367
|
// ref=audit-baseline distinguishes this funnel from audit-cli-429
|
|
306
368
|
// (rate-limit rescue) and from the static utm_source=cli help-line.
|
|
307
369
|
console.log(clr(c.dim, '\n 📊 Save this scan as your baseline. Re-run anytime with a free key:'));
|
|
@@ -311,23 +373,35 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
311
373
|
}
|
|
312
374
|
|
|
313
375
|
/**
|
|
314
|
-
* Inline signup: after
|
|
376
|
+
* Inline signup: after any real audit, offer one-step email→key flow.
|
|
315
377
|
* Collapses 6-step funnel (visit site → email → check inbox → copy key → login → watch)
|
|
316
378
|
* into a single CLI prompt.
|
|
379
|
+
*
|
|
380
|
+
* v1.19: Triggers on healthy results too (≥3 packages). The dim "Save this scan
|
|
381
|
+
* as your baseline" footer line converted 0/621 weekly downloads — replacing it
|
|
382
|
+
* with an interactive prompt at the moment of audit success captures more
|
|
383
|
+
* intent. Copy adapts to context: degradation alerts (CRITICAL) vs baseline
|
|
384
|
+
* lock-in (healthy). Quick lookups (<3 packages) still skip the prompt.
|
|
317
385
|
*/
|
|
318
386
|
async function inlineSignup(results) {
|
|
319
|
-
// Only prompt in interactive TTY when
|
|
387
|
+
// Only prompt in interactive TTY when no key saved
|
|
320
388
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
321
389
|
const hasKey = !!process.env.COMMIT_API_KEY || _cachedHasKey;
|
|
322
390
|
if (hasKey) return;
|
|
323
391
|
const critPkgs = results.filter(r => hasCritical(r.riskFlags));
|
|
324
392
|
const lowScorePkgs = results.filter(r => typeof r.score === 'number' && r.score < 60);
|
|
325
|
-
// Gate: ≥
|
|
326
|
-
|
|
327
|
-
|
|
393
|
+
// Gate: ≥3 packages scanned (real audit, not a one-off `npx poc somepkg` check)
|
|
394
|
+
if (results.length < 3) return;
|
|
395
|
+
|
|
396
|
+
const hasFindings = critPkgs.length >= 1 || lowScorePkgs.length >= 2;
|
|
397
|
+
// Copy adapts to context. Findings → degradation framing.
|
|
398
|
+
// Healthy → baseline-lock framing (still real value: alert me if any score drops).
|
|
399
|
+
const heading = hasFindings
|
|
400
|
+
? ' 🔔 Lock in this audit. Get alerted if these packages get worse.'
|
|
401
|
+
: ' 🔔 Lock in this baseline. Get alerted if any of these packages degrade.';
|
|
328
402
|
|
|
329
403
|
console.log(clr(c.dim, ' ─────────────────────────────────────────────'));
|
|
330
|
-
console.log(clr(c.bold,
|
|
404
|
+
console.log(clr(c.bold, heading));
|
|
331
405
|
console.log(clr(c.dim, ' Free, no card, 10 seconds. Saves to ~/.commit/config.\n'));
|
|
332
406
|
|
|
333
407
|
const { createInterface } = await import('readline');
|
|
@@ -366,8 +440,12 @@ async function inlineSignup(results) {
|
|
|
366
440
|
console.log();
|
|
367
441
|
console.log(clr(c.bold, ' Next steps:'));
|
|
368
442
|
console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc status') + clr(c.dim, ' — check your account'));
|
|
369
|
-
|
|
370
|
-
|
|
443
|
+
// Surface a concrete watch target. CRITICAL first (highest urgency);
|
|
444
|
+
// otherwise pick the lowest-score package as the most-likely-to-degrade.
|
|
445
|
+
const watchTarget = critPkgs[0]?.name
|
|
446
|
+
|| results.slice().sort((a, b) => (a.score || 100) - (b.score || 100))[0]?.name;
|
|
447
|
+
if (watchTarget) {
|
|
448
|
+
console.log(clr(c.dim, ' • ') + clr(c.cyan, `poc watch ${watchTarget}`) + clr(c.dim, ' — start monitoring (Developer $15/mo)'));
|
|
371
449
|
}
|
|
372
450
|
console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc init') + clr(c.dim, ' — add CI gate to this project'));
|
|
373
451
|
} else if (data.message) {
|
|
@@ -384,7 +462,7 @@ async function inlineSignup(results) {
|
|
|
384
462
|
|
|
385
463
|
function printHelp() {
|
|
386
464
|
console.log(`
|
|
387
|
-
${clr(c.bold, 'proof-of-commitment')} v1.
|
|
465
|
+
${clr(c.bold, 'proof-of-commitment')} v1.20.0 — supply chain risk scorer
|
|
388
466
|
|
|
389
467
|
${clr(c.bold, 'Usage:')}
|
|
390
468
|
npx proof-of-commitment Auto-detect manifest in current dir
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proof-of-commitment",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
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",
|