pqcheck 0.16.14 → 0.16.15

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/bin/pqcheck.js +38 -20
  2. package/package.json +1 -1
package/bin/pqcheck.js CHANGED
@@ -24,7 +24,25 @@
24
24
  })();
25
25
 
26
26
  const API_BASE = process.env.PQCHECK_API_BASE || "https://cipherwake.io";
27
- const VERSION = "0.16.14";
27
+ const VERSION = "0.16.15";
28
+
29
+ // v0.16.15 — attribution suffix. When the CLI runs inside GitHub Actions
30
+ // (GH sets GITHUB_ACTIONS=true automatically in every step) we append
31
+ // "(pqcheck-action)" to the User-Agent so the server-side classifier
32
+ // (lib/events.ts) buckets the call as `action` rather than `cli`. Lets
33
+ // the analytics dashboard split humans/CI invocations cleanly.
34
+ //
35
+ // No new data is collected — the User-Agent string was already being
36
+ // sent on every call; this is a labeling change.
37
+ //
38
+ // Opt out via PQCHECK_DISABLE_ACTION_ATTRIBUTION=1 (UA stays plain
39
+ // `pqcheck-cli/X.Y.Z` even under GitHub Actions). The env var is only
40
+ // honored when GITHUB_ACTIONS=true — it does nothing in other contexts.
41
+ const CI_ACTION_SUFFIX =
42
+ process.env.GITHUB_ACTIONS === "true" &&
43
+ process.env.PQCHECK_DISABLE_ACTION_ATTRIBUTION !== "1"
44
+ ? " (pqcheck-action)"
45
+ : "";
28
46
 
29
47
  // API-key support — paid tier (Founder Pro $19.99/mo launch pricing, locked while sub active) gets
30
48
  // per-account monthly quotas instead of the per-IP rate limit. Set via:
@@ -38,7 +56,7 @@ const QP_API_KEY = (process.env.CIPHERWAKE_API_KEY || "").trim();
38
56
  // Builds headers with optional Authorization. Use for every CLI → API call
39
57
  // so a single env-var toggle authenticates every endpoint at once.
40
58
  function apiHeaders(extra = {}) {
41
- const h = { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION}`, ...extra };
59
+ const h = { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}`, ...extra };
42
60
  if (QP_API_KEY) h.authorization = `Bearer ${QP_API_KEY}`;
43
61
  return h;
44
62
  }
@@ -274,7 +292,7 @@ async function runOneScan({ domain, format, quiet, threshold, webhookUrl, multi,
274
292
  const qs = fresh ? `?domain=${encodeURIComponent(domain)}&force=1` : `?domain=${encodeURIComponent(domain)}`;
275
293
  const resp = await fetch(`${API_BASE}/api/scan${qs}`, {
276
294
  method: "GET",
277
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (scan)` }),
295
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (scan)` }),
278
296
  });
279
297
  if (!quiet && format === "text") process.stderr.write("\r\x1b[K");
280
298
  if (!resp.ok) {
@@ -321,7 +339,7 @@ async function runOneScan({ domain, format, quiet, threshold, webhookUrl, multi,
321
339
  if (webhookUrl) {
322
340
  fetch(webhookUrl, {
323
341
  method: "POST",
324
- headers: { "content-type": "application/json", "user-agent": `pqcheck-cli/${VERSION}` },
342
+ headers: { "content-type": "application/json", "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}` },
325
343
  body: JSON.stringify({ domain, report, source: "pqcheck-cli", at: new Date().toISOString() }),
326
344
  }).catch(() => { /* best-effort — never fail the scan on webhook delivery */ });
327
345
  }
@@ -543,7 +561,7 @@ async function runWatch({ domains, format, quiet, threshold, webhookUrl, interva
543
561
  try {
544
562
  const resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}`, {
545
563
  method: "GET",
546
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (watch)` }),
564
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (watch)` }),
547
565
  });
548
566
  if (!resp.ok) continue;
549
567
  const report = await resp.json();
@@ -554,7 +572,7 @@ async function runWatch({ domains, format, quiet, threshold, webhookUrl, interva
554
572
  if (changed && webhookUrl) {
555
573
  fetch(webhookUrl, {
556
574
  method: "POST",
557
- headers: { "content-type": "application/json", "user-agent": `pqcheck-cli/${VERSION}` },
575
+ headers: { "content-type": "application/json", "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}` },
558
576
  body: JSON.stringify({
559
577
  type: "score_changed",
560
578
  domain,
@@ -899,7 +917,7 @@ async function refreshVersionCacheInBackground(cachePath) {
899
917
  const controller = new AbortController();
900
918
  const timeout = setTimeout(() => controller.abort(), 2500);
901
919
  const resp = await fetch(VERSION_REGISTRY_URL, {
902
- headers: { "Accept": "application/json", "User-Agent": `pqcheck-cli/${VERSION}` },
920
+ headers: { "Accept": "application/json", "User-Agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}` },
903
921
  signal: controller.signal,
904
922
  });
905
923
  clearTimeout(timeout);
@@ -1829,7 +1847,7 @@ async function runLockCommand(args) {
1829
1847
  try {
1830
1848
  const resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}`, {
1831
1849
  method: "GET",
1832
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (lock)` }),
1850
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (lock)` }),
1833
1851
  });
1834
1852
  if (!stdout) process.stderr.write("\r\x1b[K");
1835
1853
  if (!resp.ok) {
@@ -1909,7 +1927,7 @@ function buildQxmManifest(report, crypto) {
1909
1927
  return {
1910
1928
  schema: "https://cipherwake.io/schemas/qxm/v1",
1911
1929
  schemaVersion: 1,
1912
- generator: `pqcheck-cli/${VERSION}`,
1930
+ generator: `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}`,
1913
1931
  generatedAt: report.generatedAt || new Date().toISOString(),
1914
1932
  domain: report.domain,
1915
1933
  reachable: !!report.reachable,
@@ -2152,7 +2170,7 @@ async function runDepsCommand(args) {
2152
2170
  batch.map(async (h) => {
2153
2171
  try {
2154
2172
  const r = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(h.host)}&source=cli-deps`, {
2155
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (deps)` }),
2173
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (deps)` }),
2156
2174
  });
2157
2175
  if (!r.ok) return { ...h, types: Array.from(h.types), scan: null, error: `${r.status}` };
2158
2176
  const body = await r.json();
@@ -2391,7 +2409,7 @@ async function fetchPageHTML(domain) {
2391
2409
  method: "GET",
2392
2410
  redirect: "follow",
2393
2411
  signal: ctrl.signal,
2394
- headers: { "User-Agent": `pqcheck-cli/${VERSION} (deps; +https://cipherwake.io)` },
2412
+ headers: { "User-Agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (deps; +https://cipherwake.io)` },
2395
2413
  });
2396
2414
  clearTimeout(t);
2397
2415
  if (!resp.ok) return null;
@@ -2848,7 +2866,7 @@ async function runHistoryCommand(args) {
2848
2866
  let h;
2849
2867
  try {
2850
2868
  const r = await fetch(`${API_BASE}/api/history?domain=${encodeURIComponent(domain)}&days=${days}`, {
2851
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (history)` }),
2869
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (history)` }),
2852
2870
  });
2853
2871
  if (!r.ok) {
2854
2872
  console.error(color("red", `error: ${r.status} ${r.statusText}`));
@@ -2931,7 +2949,7 @@ async function runChangesCommand(args) {
2931
2949
  let summary;
2932
2950
  try {
2933
2951
  const r = await fetch(`${API_BASE}/api/changes-summary?domain=${encodeURIComponent(domain)}`, {
2934
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (changes)` }),
2952
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (changes)` }),
2935
2953
  });
2936
2954
  if (!r.ok) {
2937
2955
  console.error(color("red", `error: ${r.status} ${r.statusText}`));
@@ -3025,7 +3043,7 @@ async function runChangesCommand(args) {
3025
3043
  async function runScanBasedDeployCheck(domain, args) {
3026
3044
  const headers = {
3027
3045
  "Content-Type": "application/json",
3028
- "User-Agent": `pqcheck-cli/${VERSION}`,
3046
+ "User-Agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}`,
3029
3047
  };
3030
3048
  if (QP_API_KEY) headers["Authorization"] = `Bearer ${QP_API_KEY}`;
3031
3049
 
@@ -3198,7 +3216,7 @@ async function runTrustDiffCommand(args) {
3198
3216
  // anonymous per-IP rate limit path (just like /api/scan).
3199
3217
  const headers = {
3200
3218
  "Content-Type": "application/json",
3201
- "User-Agent": `pqcheck-cli/${VERSION}`,
3219
+ "User-Agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}`,
3202
3220
  };
3203
3221
  if (QP_API_KEY) {
3204
3222
  headers["Authorization"] = `Bearer ${QP_API_KEY}`;
@@ -3696,7 +3714,7 @@ async function runPreviewDiffCommand(args) {
3696
3714
 
3697
3715
  const headers = {
3698
3716
  "Content-Type": "application/json",
3699
- "User-Agent": `pqcheck-cli/${VERSION}`,
3717
+ "User-Agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}`,
3700
3718
  };
3701
3719
  if (QP_API_KEY) {
3702
3720
  headers["Authorization"] = `Bearer ${QP_API_KEY}`;
@@ -4766,7 +4784,7 @@ async function fetchVendorOrigins(domain) {
4766
4784
  try {
4767
4785
  resp = await fetch(`${API_BASE}/api/deps?domain=${encodeURIComponent(domain)}`, {
4768
4786
  method: "GET",
4769
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (vendors)` }),
4787
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (vendors)` }),
4770
4788
  signal: ac.signal,
4771
4789
  });
4772
4790
  } catch (err) {
@@ -4814,7 +4832,7 @@ function normalizeObservedOrigin(value) {
4814
4832
  function buildVendorLockfile(domain, origins) {
4815
4833
  return {
4816
4834
  schema_version: 1,
4817
- generator: `pqcheck-cli/${VERSION}`,
4835
+ generator: `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX}`,
4818
4836
  domain,
4819
4837
  generated_at: new Date().toISOString(),
4820
4838
  approved_script_origins: origins,
@@ -4930,7 +4948,7 @@ async function runVendorsSync(domain, outPath) {
4930
4948
  resp = await fetch(`${API_BASE}/api/vendor-allowlist?domain=${encodeURIComponent(domain)}`, {
4931
4949
  method: "GET",
4932
4950
  headers: {
4933
- "user-agent": `pqcheck-cli/${VERSION} (vendors-sync)`,
4951
+ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (vendors-sync)`,
4934
4952
  "authorization": "Bearer " + QP_API_KEY,
4935
4953
  },
4936
4954
  });
@@ -5084,7 +5102,7 @@ async function runOnboardCommand(args) {
5084
5102
  try {
5085
5103
  const resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}&source=onboard`, {
5086
5104
  method: "GET",
5087
- headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION} (onboard)` }),
5105
+ headers: apiHeaders({ "user-agent": `pqcheck-cli/${VERSION}${CI_ACTION_SUFFIX} (onboard)` }),
5088
5106
  });
5089
5107
  if (resp.ok) {
5090
5108
  const report = await resp.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pqcheck",
3
- "version": "0.16.14",
3
+ "version": "0.16.15",
4
4
  "description": "Deploy gate for AI-coded web apps. `pqcheck deploy-check --ai` returns ship_decision=pass|review|block for Claude Code / Cursor / Copilot / Aider to gate deploys before they ship. Anonymous, no signup, free for first use.",
5
5
  "keywords": [
6
6
  "ai-coder",