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.
- package/index.js +96 -12
- 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.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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.error(clr(c.dim,
|
|
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
|
-
|
|
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.
|
|
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
|
|
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:
|
|
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:
|
|
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.
|
|
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",
|