pqcheck 0.16.3 → 0.16.4
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/bin/cipherwake-statusline.js +69 -1
- package/bin/pqcheck.js +18 -3
- package/package.json +1 -1
|
@@ -73,7 +73,11 @@ try {
|
|
|
73
73
|
process.exit(0);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const {
|
|
76
|
+
const {
|
|
77
|
+
domain, score, grade, ship_decision, written_at, max_severity, unreachable,
|
|
78
|
+
// v0.16.4 — preview-diff-specific fields for the 4-line render
|
|
79
|
+
kind, delta_count, diff_no_change, sector_ranking, verified_signal_categories, last_changed,
|
|
80
|
+
} = state;
|
|
77
81
|
const age = ageHours(written_at);
|
|
78
82
|
|
|
79
83
|
if (age > STALE_THRESHOLD_HOURS) {
|
|
@@ -87,6 +91,70 @@ if (age > STALE_THRESHOLD_HOURS) {
|
|
|
87
91
|
process.exit(0);
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
// v0.16.4 — preview-diff 4-line state.
|
|
95
|
+
// User direction: when the last scan was `preview-diff` (two URLs compared),
|
|
96
|
+
// render the high-yield block in the statusline. Other scan kinds keep the
|
|
97
|
+
// 1-line compact format below — the bigger block would be too noisy when
|
|
98
|
+
// it's just a routine domain scan with no diff context.
|
|
99
|
+
if (kind === "preview-diff" && !unreachable) {
|
|
100
|
+
// Line 1: brand header (hostname only).
|
|
101
|
+
const head = c(C.bold, "◆ Cipherwake") + c(C.dim, " — ") + c(C.bold, domain || "—");
|
|
102
|
+
// Line 3: decision pulse, diff-flavored copy.
|
|
103
|
+
let pulse;
|
|
104
|
+
if (ship_decision === "pass") {
|
|
105
|
+
pulse = c(C.green, "✓ PASS") + c(C.dim, " · no risky deploy-surface drift detected");
|
|
106
|
+
} else if (ship_decision === "review") {
|
|
107
|
+
pulse = c(C.yellow, "⚠ REVIEW") + c(C.dim, ` · ${delta_count || 0} public-surface change${delta_count === 1 ? "" : "s"}`);
|
|
108
|
+
} else if (ship_decision === "block") {
|
|
109
|
+
pulse = c(C.red, "⛔ BLOCK") + c(C.dim, ` · ${delta_count || 0} critical change${delta_count === 1 ? "" : "s"}`);
|
|
110
|
+
} else {
|
|
111
|
+
pulse = c(C.dim, "· no decision");
|
|
112
|
+
}
|
|
113
|
+
// Line 4: trust posture (DBR + percentile copy + stability).
|
|
114
|
+
function formatPercentile(percentile, sector) {
|
|
115
|
+
if (typeof percentile !== "number") return null;
|
|
116
|
+
const s = sector || "industry";
|
|
117
|
+
if (percentile <= 10) return `top 10% in ${s}`;
|
|
118
|
+
if (percentile <= 25) return `top 25% in ${s}`;
|
|
119
|
+
if (percentile <= 50) return `above median in ${s}`;
|
|
120
|
+
if (percentile <= 75) return `below median in ${s}`;
|
|
121
|
+
if (percentile <= 90) return `bottom 25% in ${s}`;
|
|
122
|
+
return `bottom 10% in ${s}`;
|
|
123
|
+
}
|
|
124
|
+
function formatStability(iso) {
|
|
125
|
+
if (!iso) return null;
|
|
126
|
+
const days = Math.floor((Date.now() - new Date(iso).getTime()) / 86400000);
|
|
127
|
+
if (days <= 0) return "drifted today";
|
|
128
|
+
if (days < 7) return `drifted ${days}d ago`;
|
|
129
|
+
return `stable ${days}d`;
|
|
130
|
+
}
|
|
131
|
+
const trustParts = [];
|
|
132
|
+
if (typeof score === "number") {
|
|
133
|
+
trustParts.push(`DBR ${score.toFixed(1)}${grade ? " " + grade : ""}`);
|
|
134
|
+
}
|
|
135
|
+
const pct = formatPercentile(sector_ranking?.percentile, sector_ranking?.sectorLabel || sector_ranking?.sectorName || sector_ranking?.sector);
|
|
136
|
+
if (pct) trustParts.push(pct);
|
|
137
|
+
const stab = formatStability(last_changed);
|
|
138
|
+
if (stab) trustParts.push(stab);
|
|
139
|
+
const trustLine = trustParts.length > 0
|
|
140
|
+
? c(C.green, "✓ ") + c(C.bold, "Trust posture: ") + trustParts.join(c(C.dim, " · "))
|
|
141
|
+
: null;
|
|
142
|
+
// Line 5: verified signals (or finding line when there IS drift).
|
|
143
|
+
let verifiedLine = null;
|
|
144
|
+
if (Array.isArray(verified_signal_categories) && verified_signal_categories.length > 0) {
|
|
145
|
+
const head = diff_no_change === true
|
|
146
|
+
? "Security-relevant surface unchanged"
|
|
147
|
+
: `Verified ${verified_signal_categories.length} signal${verified_signal_categories.length === 1 ? "" : "s"}`;
|
|
148
|
+
verifiedLine = c(C.green, "✓ ") + c(C.bold, head) + c(C.dim, " · " + verified_signal_categories.join(", "));
|
|
149
|
+
}
|
|
150
|
+
// Compose
|
|
151
|
+
const lines = [head, "", pulse];
|
|
152
|
+
if (trustLine) lines.push(trustLine);
|
|
153
|
+
if (verifiedLine) lines.push(verifiedLine);
|
|
154
|
+
process.stdout.write(lines.join("\n"));
|
|
155
|
+
process.exit(0);
|
|
156
|
+
}
|
|
157
|
+
|
|
90
158
|
// Brand-anchored layout — "Cipherwake" is always the first word after the
|
|
91
159
|
// diamond, so the customer (and their AI agent) can identify the status
|
|
92
160
|
// line's source at a glance. Trailing segments depend on what we have:
|
package/bin/pqcheck.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
})();
|
|
25
25
|
|
|
26
26
|
const API_BASE = process.env.PQCHECK_API_BASE || "https://cipherwake.io";
|
|
27
|
-
const VERSION = "0.16.
|
|
27
|
+
const VERSION = "0.16.4";
|
|
28
28
|
|
|
29
29
|
// API-key support — paid tiers (Starter $29 / Growth $79 / Scale $199) get
|
|
30
30
|
// per-account monthly quotas instead of the per-IP rate limit. Set via:
|
|
@@ -3390,15 +3390,30 @@ async function runPreviewDiffCommand(args) {
|
|
|
3390
3390
|
}));
|
|
3391
3391
|
console.log("");
|
|
3392
3392
|
|
|
3393
|
+
// v0.16.4 — write the data the statusline needs to render its
|
|
3394
|
+
// 4-line preview-diff state (brand header + decision pulse + trust
|
|
3395
|
+
// posture + verified signals). For non-preview-diff kinds we keep
|
|
3396
|
+
// the 1-line statusline; only preview-diff gets the bigger format.
|
|
3397
|
+
const verifiedSignalCats = (() => {
|
|
3398
|
+
try { return countVerifiedSignals(reportLike).categories; } catch { return null; }
|
|
3399
|
+
})();
|
|
3393
3400
|
await writeLastScanFile({
|
|
3394
|
-
domain: result?.production?.domain || productionUrl,
|
|
3401
|
+
domain: result?.production?.hostname || result?.production?.domain || productionUrl,
|
|
3395
3402
|
kind: "preview-diff",
|
|
3396
3403
|
preview_url: previewUrl,
|
|
3397
3404
|
production_url: productionUrl,
|
|
3398
|
-
score
|
|
3405
|
+
// v0.16.4 — score/grade come from the PRODUCTION side so the trust
|
|
3406
|
+
// posture line reflects the live customer site, not the in-flight
|
|
3407
|
+
// preview. (Was prevScore before — wrong reference.)
|
|
3408
|
+
score: typeof prodSig?.score === "number" ? prodSig.score : (typeof prevScore === "number" ? prevScore : null),
|
|
3409
|
+
grade: prodSig?.grade || null,
|
|
3399
3410
|
max_severity: maxSev,
|
|
3400
3411
|
ship_decision: shipDecision,
|
|
3401
3412
|
delta_count: summaryLines.filter((l) => !/no meaningful/i.test(l)).length,
|
|
3413
|
+
diff_no_change: diffNoChange,
|
|
3414
|
+
sector_ranking: prodSig?.sectorRanking || null,
|
|
3415
|
+
verified_signal_categories: verifiedSignalCats,
|
|
3416
|
+
last_changed: prodSig?.lastChanged || null,
|
|
3402
3417
|
});
|
|
3403
3418
|
|
|
3404
3419
|
process.exit(shipDecisionExitCode(shipDecision));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pqcheck",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
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",
|