shippingszn 0.9.3 → 0.9.5
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/README.md +30 -21
- package/dist/index.js +121 -82
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,13 +12,15 @@ pnpm dlx shippingszn@latest
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The CLI **never writes, modifies, or deletes** any files - it only reads.
|
|
15
|
-
Normal runs
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
Normal runs create a locked scan handoff for checkout and send one anonymous
|
|
16
|
+
Wall report-card summary with score, severity counts, files scanned, scanner
|
|
17
|
+
version, and safe stack tags. It never uploads source code, repo URLs, project
|
|
18
|
+
names, secrets, handles, or emails. Finding-level detail stays out of public
|
|
19
|
+
CLI/Wall output and opens inside the paid Launch Fix Kit.
|
|
20
|
+
|
|
21
|
+
Human output starts with a Launch Debt Score, severity counts, a short
|
|
22
|
+
plain-English launch-risk explanation, completed checks, and the locked Fix Kit
|
|
23
|
+
handoff. Run with `--json` to get the same score-and-counts summary in a
|
|
22
24
|
machine-readable shape:
|
|
23
25
|
|
|
24
26
|
```json
|
|
@@ -27,13 +29,20 @@ machine-readable shape:
|
|
|
27
29
|
"band": "no_go",
|
|
28
30
|
"counts": { "critical": 1, "high": 3, "medium": 4, "lower": 2 },
|
|
29
31
|
"filesScanned": 128,
|
|
32
|
+
"coverage": { "checksCompleted": 19, "checklistAreas": 46 },
|
|
30
33
|
"detailsLocked": true,
|
|
31
|
-
"unlockUrl": "https://shippingszn.com/fix-kit"
|
|
34
|
+
"unlockUrl": "https://shippingszn.com/fix-kit?scanResultId=00000000-0000-4000-8000-000000000123",
|
|
35
|
+
"scanHandoff": {
|
|
36
|
+
"status": "uploaded",
|
|
37
|
+
"resultId": "00000000-0000-4000-8000-000000000123",
|
|
38
|
+
"unlockUrl": "https://shippingszn.com/fix-kit?scanResultId=00000000-0000-4000-8000-000000000123"
|
|
39
|
+
}
|
|
32
40
|
}
|
|
33
41
|
```
|
|
34
42
|
|
|
35
43
|
Finding titles, files, evidence, fix prompts, and verification steps are paid.
|
|
36
|
-
|
|
44
|
+
The free CLI explains why the score matters, but the actual fixes unlock inside
|
|
45
|
+
the Launch Fix Kit.
|
|
37
46
|
|
|
38
47
|
## Aggregate summary
|
|
39
48
|
|
|
@@ -41,10 +50,10 @@ Plain `npx shippingszn@latest` sends the Wall summary automatically. The stored
|
|
|
41
50
|
row is anonymous and intentionally small: score, launch label, files scanned,
|
|
42
51
|
finding counts by severity, detected stack tags, scanner version, and timestamp.
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
it
|
|
53
|
+
The same run also creates a scan-specific paid-report handoff. The terminal
|
|
54
|
+
prints a `/fix-kit?scanResultId=...` URL so checkout can carry that scan into
|
|
55
|
+
the Launch Fix Kit after purchase. `--proof` is still accepted for old docs, but
|
|
56
|
+
it is no longer required.
|
|
48
57
|
|
|
49
58
|
## What gets checked
|
|
50
59
|
|
|
@@ -123,8 +132,8 @@ shippingszn [path] [options]
|
|
|
123
132
|
|
|
124
133
|
Options:
|
|
125
134
|
--json Output a machine-readable JSON summary.
|
|
126
|
-
--proof
|
|
127
|
-
Launch Fix Kit URL.
|
|
135
|
+
--proof Backward-compatible alias. Normal runs already return
|
|
136
|
+
a scan-specific Launch Fix Kit URL.
|
|
128
137
|
--base-url <url> Base URL used to build checkout and Fix Kit links.
|
|
129
138
|
--cwd <path> Directory to scan. Default: current working directory.
|
|
130
139
|
--no-color Disable ANSI colors in the human-readable summary.
|
|
@@ -151,12 +160,12 @@ summary as a comment: score, severity counts, and unlock URL.
|
|
|
151
160
|
## Privacy
|
|
152
161
|
|
|
153
162
|
`shippingszn` reads files on your machine. It never uploads source code. By
|
|
154
|
-
default it
|
|
155
|
-
|
|
156
|
-
detected stack tags, scanner version,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
and not user-configurable.
|
|
163
|
+
default it creates a locked scan handoff for checkout and makes one best-effort
|
|
164
|
+
outbound request to post anonymous Wall stats: score, launch label, files
|
|
165
|
+
scanned count, finding counts by severity, detected stack tags, scanner version,
|
|
166
|
+
and timestamp. Wall stats never include source code, file paths, filenames,
|
|
167
|
+
project names, repo URLs, secrets, emails, handles, finding titles, evidence, or
|
|
168
|
+
report contents. The Wall summary is automatic and not user-configurable.
|
|
160
169
|
|
|
161
170
|
## License
|
|
162
171
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
4
|
+
import * as path18 from "node:path";
|
|
5
|
+
import * as process from "node:process";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
|
|
8
8
|
// src/checks/secrets.ts
|
|
@@ -3685,13 +3685,8 @@ async function publishScan(totals, filesScanned, opts) {
|
|
|
3685
3685
|
}
|
|
3686
3686
|
|
|
3687
3687
|
// src/proof.ts
|
|
3688
|
-
import * as path18 from "node:path";
|
|
3689
3688
|
var PROOF_TIMEOUT_MS = 5e3;
|
|
3690
3689
|
var MAX_FINDINGS = 100;
|
|
3691
|
-
function shouldUploadProof() {
|
|
3692
|
-
const v = process.env["SHIPPINGSZN_PROOF"] ?? "";
|
|
3693
|
-
return v === "1" || v.toLowerCase() === "true" || v === "yes";
|
|
3694
|
-
}
|
|
3695
3690
|
function clampText(value, max, fallback = "") {
|
|
3696
3691
|
const text = value ?? fallback;
|
|
3697
3692
|
return text.length > max ? text.slice(0, max - 3) + "..." : text;
|
|
@@ -3705,8 +3700,8 @@ function buildBadgeMarkdown(baseUrl, id, score) {
|
|
|
3705
3700
|
scanResultId: id,
|
|
3706
3701
|
theme: "dark"
|
|
3707
3702
|
});
|
|
3708
|
-
const
|
|
3709
|
-
return `[})](${fixKitUrl})`;
|
|
3710
3705
|
}
|
|
3711
3706
|
function buildProofPayload(report, scannerVersion) {
|
|
3712
3707
|
const counts = {
|
|
@@ -3719,7 +3714,7 @@ function buildProofPayload(report, scannerVersion) {
|
|
|
3719
3714
|
version: 1,
|
|
3720
3715
|
source: report.source ?? "cli",
|
|
3721
3716
|
scanner: "shippingszn",
|
|
3722
|
-
targetName:
|
|
3717
|
+
targetName: "Anonymous CLI scan",
|
|
3723
3718
|
score: report.launchReadiness.score,
|
|
3724
3719
|
label: report.launchReadiness.label,
|
|
3725
3720
|
decision: report.launchReadiness.decision,
|
|
@@ -4376,13 +4371,13 @@ var PKG_VERSION = (() => {
|
|
|
4376
4371
|
})();
|
|
4377
4372
|
function parseArgs(argv2) {
|
|
4378
4373
|
const opts = {
|
|
4379
|
-
cwd:
|
|
4374
|
+
cwd: process.cwd(),
|
|
4380
4375
|
json: false,
|
|
4381
|
-
baseUrl:
|
|
4376
|
+
baseUrl: process.env.SHIPPINGSZN_BASE_URL ?? process.env.VIBE_LAUNCH_CHECK_BASE_URL ?? DEFAULT_BASE_URL2,
|
|
4382
4377
|
help: false,
|
|
4383
4378
|
version: false,
|
|
4384
|
-
noColor: !!
|
|
4385
|
-
proof:
|
|
4379
|
+
noColor: !!process.env.NO_COLOR,
|
|
4380
|
+
proof: true
|
|
4386
4381
|
};
|
|
4387
4382
|
for (let i = 0; i < argv2.length; i++) {
|
|
4388
4383
|
const a = argv2[i];
|
|
@@ -4394,8 +4389,8 @@ function parseArgs(argv2) {
|
|
|
4394
4389
|
else if (a === "--no-publish" || a === "--publish")
|
|
4395
4390
|
opts.unsupportedWallFlag = a;
|
|
4396
4391
|
else if (a === "--base-url") opts.baseUrl = argv2[++i] ?? opts.baseUrl;
|
|
4397
|
-
else if (a === "--cwd") opts.cwd =
|
|
4398
|
-
else if (!a.startsWith("-")) opts.cwd =
|
|
4392
|
+
else if (a === "--cwd") opts.cwd = path18.resolve(argv2[++i] ?? opts.cwd);
|
|
4393
|
+
else if (!a.startsWith("-")) opts.cwd = path18.resolve(a);
|
|
4399
4394
|
}
|
|
4400
4395
|
return opts;
|
|
4401
4396
|
}
|
|
@@ -4414,8 +4409,14 @@ function color(enabled) {
|
|
|
4414
4409
|
};
|
|
4415
4410
|
}
|
|
4416
4411
|
var SEVERITY_ORDER = ["critical", "high", "medium", "lower"];
|
|
4412
|
+
function pluralize(count, singular, plural = `${singular}s`) {
|
|
4413
|
+
return count === 1 ? singular : plural;
|
|
4414
|
+
}
|
|
4415
|
+
function findingVerb(count) {
|
|
4416
|
+
return count === 1 ? "is" : "are";
|
|
4417
|
+
}
|
|
4417
4418
|
function printHelp() {
|
|
4418
|
-
|
|
4419
|
+
process.stdout.write(
|
|
4419
4420
|
`shippingszn v${PKG_VERSION}
|
|
4420
4421
|
|
|
4421
4422
|
Read-only scanner that checks the current project against a small set of
|
|
@@ -4426,8 +4427,8 @@ Usage:
|
|
|
4426
4427
|
|
|
4427
4428
|
Options:
|
|
4428
4429
|
--json Output a machine-readable JSON summary.
|
|
4429
|
-
--proof
|
|
4430
|
-
Launch Fix Kit URL.
|
|
4430
|
+
--proof Backward-compatible alias. Normal runs already return
|
|
4431
|
+
a scan-specific Launch Fix Kit URL.
|
|
4431
4432
|
--base-url <url> Base URL used to build checkout and Fix Kit links.
|
|
4432
4433
|
(default: ${DEFAULT_BASE_URL2})
|
|
4433
4434
|
--cwd <path> Directory to scan. Default: current working directory.
|
|
@@ -4438,35 +4439,36 @@ Options:
|
|
|
4438
4439
|
This is the FREE preflight. It tells you launch debt exists. It does NOT tell
|
|
4439
4440
|
you what's broken or how to fix it \u2014 that's gated behind the $49 Launch Fix Kit
|
|
4440
4441
|
(unlocks full findings + 46-item workbook + paste-ready AI-builder prompts +
|
|
4441
|
-
verification steps + red-flag checks). Read-only on disk.
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
publish opt-in or opt-out flag. Exit
|
|
4442
|
+
verification steps + red-flag checks). Read-only on disk. Normal runs create a
|
|
4443
|
+
locked scan handoff for checkout and send anonymous Wall stats (score, severity
|
|
4444
|
+
counts, file count, scanner version \u2014 no project name, no paths, no
|
|
4445
|
+
finding-level detail); there is no Wall publish opt-in or opt-out flag. Exit
|
|
4446
|
+
code non-zero if any critical findings.
|
|
4445
4447
|
`
|
|
4446
4448
|
);
|
|
4447
4449
|
}
|
|
4448
4450
|
function scanSource() {
|
|
4449
|
-
return
|
|
4451
|
+
return process.env.GITHUB_ACTIONS === "true" ? "github" : "cli";
|
|
4450
4452
|
}
|
|
4451
4453
|
async function run() {
|
|
4452
|
-
const opts = parseArgs(
|
|
4454
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
4453
4455
|
if (opts.help) {
|
|
4454
4456
|
printHelp();
|
|
4455
4457
|
return 0;
|
|
4456
4458
|
}
|
|
4457
4459
|
if (opts.version) {
|
|
4458
|
-
|
|
4460
|
+
process.stdout.write(`${PKG_VERSION}
|
|
4459
4461
|
`);
|
|
4460
4462
|
return 0;
|
|
4461
4463
|
}
|
|
4462
4464
|
if (opts.unsupportedWallFlag) {
|
|
4463
|
-
|
|
4465
|
+
process.stderr.write(
|
|
4464
4466
|
`${opts.unsupportedWallFlag} is not supported. shippingszn sends anonymous Wall stats on normal runs: score, severity counts, file count, scanner version, and safe stack tags. It never uploads code, file paths, finding-level details, project names, repo URLs, secrets, emails, or handles.
|
|
4465
4467
|
`
|
|
4466
4468
|
);
|
|
4467
4469
|
return 2;
|
|
4468
4470
|
}
|
|
4469
|
-
const c = color(!opts.noColor &&
|
|
4471
|
+
const c = color(!opts.noColor && process.stdout.isTTY === true && !opts.json);
|
|
4470
4472
|
const files = await listFiles(opts.cwd);
|
|
4471
4473
|
const tracked = getTrackedFiles(opts.cwd);
|
|
4472
4474
|
const ctx = { rootDir: opts.cwd, files };
|
|
@@ -4489,7 +4491,7 @@ async function run() {
|
|
|
4489
4491
|
const fixKitUrl = `${trimmedBaseUrl}/fix-kit`;
|
|
4490
4492
|
const reportUrl = fixKitUrl;
|
|
4491
4493
|
const proofCreatePath = `${trimmedBaseUrl}/scan`;
|
|
4492
|
-
const proofUploadHint = "
|
|
4494
|
+
const proofUploadHint = "Normal runs automatically generate a scan-specific Launch Fix Kit URL.";
|
|
4493
4495
|
const tracked_aware = applyTrackingAwareSeverity(all, tracked);
|
|
4494
4496
|
const source = scanSource();
|
|
4495
4497
|
const enriched = tracked_aware.map((f) => {
|
|
@@ -4635,9 +4637,6 @@ async function run() {
|
|
|
4635
4637
|
const automatedAreas = CHECKLIST.filter(
|
|
4636
4638
|
(item) => item.cliCoverage === "automated"
|
|
4637
4639
|
).length;
|
|
4638
|
-
const ownerVerifyAreas = CHECKLIST.filter(
|
|
4639
|
-
(item) => item.cliCoverage === "manual_only"
|
|
4640
|
-
).length;
|
|
4641
4640
|
const SCORE_BAND_LABEL = {
|
|
4642
4641
|
blocked: "NO-GO",
|
|
4643
4642
|
fix_first: "FIX FIRST",
|
|
@@ -4668,9 +4667,8 @@ async function run() {
|
|
|
4668
4667
|
counts: { ...totals },
|
|
4669
4668
|
filesScanned: files.length,
|
|
4670
4669
|
coverage: {
|
|
4671
|
-
automatedAreas,
|
|
4672
|
-
|
|
4673
|
-
totalAreas: CHECKLIST.length
|
|
4670
|
+
checksCompleted: automatedAreas,
|
|
4671
|
+
checklistAreas: CHECKLIST.length
|
|
4674
4672
|
},
|
|
4675
4673
|
scannerVersion: PKG_VERSION,
|
|
4676
4674
|
detailsLocked: true,
|
|
@@ -4680,112 +4678,153 @@ async function run() {
|
|
|
4680
4678
|
...wallUrl ? { url: wallUrl } : {},
|
|
4681
4679
|
...wallError ? { error: wallError } : {}
|
|
4682
4680
|
},
|
|
4683
|
-
|
|
4681
|
+
scanHandoff: {
|
|
4684
4682
|
status: proofResult.status,
|
|
4685
4683
|
...proofResult.status === "uploaded" ? {
|
|
4686
|
-
url: proofResult.proofUrl,
|
|
4687
4684
|
resultId: proofResult.id,
|
|
4688
|
-
|
|
4685
|
+
unlockUrl: proofResult.reportUrl
|
|
4689
4686
|
} : {},
|
|
4690
4687
|
...proofResult.status === "failed" && proofResult.error ? { error: proofResult.error } : {}
|
|
4691
4688
|
}
|
|
4692
4689
|
};
|
|
4693
|
-
|
|
4690
|
+
process.stdout.write(JSON.stringify(scoreSummary, null, 2) + "\n");
|
|
4694
4691
|
return totals.critical > 0 ? 1 : 0;
|
|
4695
4692
|
}
|
|
4696
4693
|
const bandColor = band === "no_go" ? c.red : band === "fix_first" ? c.yellow : band === "verify_before_launch" ? c.blue : c.green;
|
|
4697
|
-
|
|
4694
|
+
process.stdout.write(
|
|
4698
4695
|
`
|
|
4699
4696
|
${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
|
|
4700
4697
|
`
|
|
4701
4698
|
);
|
|
4702
|
-
|
|
4699
|
+
process.stdout.write(c.dim(`Scanned ${files.length} files.
|
|
4703
4700
|
|
|
4704
4701
|
`));
|
|
4705
|
-
|
|
4702
|
+
process.stdout.write(
|
|
4706
4703
|
`${c.bold("Launch Debt Score:")} ${launchReadiness.score}/100 \u2014 ${bandColor(c.bold(bandLabel))}
|
|
4707
4704
|
|
|
4708
4705
|
`
|
|
4709
4706
|
);
|
|
4710
|
-
|
|
4707
|
+
process.stdout.write(`${c.bold("Findings detected:")}
|
|
4711
4708
|
`);
|
|
4712
|
-
|
|
4709
|
+
process.stdout.write(` ${c.red(`${totals.critical} critical`)}
|
|
4713
4710
|
`);
|
|
4714
|
-
|
|
4711
|
+
process.stdout.write(` ${c.yellow(`${totals.high} high`)}
|
|
4715
4712
|
`);
|
|
4716
|
-
|
|
4713
|
+
process.stdout.write(` ${c.blue(`${totals.medium} medium`)}
|
|
4717
4714
|
`);
|
|
4718
|
-
|
|
4715
|
+
process.stdout.write(` ${c.gray(`${totals.lower} lower`)}
|
|
4719
4716
|
|
|
4720
4717
|
`);
|
|
4721
|
-
|
|
4718
|
+
const totalFindings = totals.critical + totals.high + totals.medium + totals.lower;
|
|
4719
|
+
if (totalFindings > 0) {
|
|
4720
|
+
if (totals.critical > 0) {
|
|
4721
|
+
process.stdout.write(c.red("Critical launch blocker found.\n"));
|
|
4722
|
+
process.stdout.write(
|
|
4723
|
+
`This scan found ${totalFindings} ${pluralize(totalFindings, "issue")} that should be fixed before launch.
|
|
4724
|
+
|
|
4725
|
+
`
|
|
4726
|
+
);
|
|
4727
|
+
process.stdout.write(`${c.bold("What that means:")}
|
|
4722
4728
|
`);
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4729
|
+
process.stdout.write(
|
|
4730
|
+
c.dim(
|
|
4731
|
+
"Critical launch debt can expose users, break signup or payment paths, or make the launch unsafe until fixed.\n\n"
|
|
4732
|
+
)
|
|
4733
|
+
);
|
|
4734
|
+
} else {
|
|
4735
|
+
process.stdout.write("No critical blockers found.\n");
|
|
4736
|
+
if (totals.high === 0) {
|
|
4737
|
+
process.stdout.write("No high blockers found.\n");
|
|
4738
|
+
}
|
|
4739
|
+
process.stdout.write(
|
|
4740
|
+
`But ${totalFindings} ${pluralize(totalFindings, "issue")} ${findingVerb(totalFindings)} still holding this back from a clean launch.
|
|
4741
|
+
|
|
4726
4742
|
`
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4743
|
+
);
|
|
4744
|
+
process.stdout.write(`${c.bold("What that means:")}
|
|
4745
|
+
`);
|
|
4746
|
+
if (totals.high > 0) {
|
|
4747
|
+
process.stdout.write(
|
|
4748
|
+
c.dim(
|
|
4749
|
+
"High and medium launch debt can expose trust gaps, break conversion moments, or make the project feel unfinished.\n\n"
|
|
4750
|
+
)
|
|
4751
|
+
);
|
|
4752
|
+
} else if (totals.medium > 0) {
|
|
4753
|
+
process.stdout.write(
|
|
4754
|
+
c.dim(
|
|
4755
|
+
"Medium launch debt usually will not crash the app, but it can make users hesitate, miss conversion moments, or make the project feel unfinished.\n\n"
|
|
4756
|
+
)
|
|
4757
|
+
);
|
|
4758
|
+
} else {
|
|
4759
|
+
process.stdout.write(
|
|
4760
|
+
c.dim(
|
|
4761
|
+
"Lower launch debt usually is not launch-blocking, but it can still make the project feel rough or unfinished.\n\n"
|
|
4762
|
+
)
|
|
4763
|
+
);
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
process.stdout.write(`${c.bold("Coverage:")}
|
|
4768
|
+
`);
|
|
4769
|
+
process.stdout.write(
|
|
4770
|
+
c.dim(` ${automatedAreas} launch-readiness checks completed
|
|
4731
4771
|
|
|
4732
4772
|
`)
|
|
4733
4773
|
);
|
|
4734
|
-
if (
|
|
4774
|
+
if (totalFindings > 0) {
|
|
4735
4775
|
if (scanSpecificUnlockUrl) {
|
|
4736
|
-
|
|
4737
|
-
|
|
4776
|
+
process.stdout.write(c.bold("Unlock this exact scan:\n"));
|
|
4777
|
+
process.stdout.write(` ${c.cyan(scanSpecificUnlockUrl)}
|
|
4738
4778
|
`);
|
|
4739
|
-
|
|
4779
|
+
process.stdout.write(
|
|
4740
4780
|
c.dim(
|
|
4741
|
-
|
|
4781
|
+
` Includes the ${totalFindings} specific ${pluralize(totalFindings, "finding")}, file evidence, and AI-builder fix prompts.
|
|
4782
|
+
|
|
4783
|
+
`
|
|
4742
4784
|
)
|
|
4743
4785
|
);
|
|
4744
4786
|
} else {
|
|
4745
|
-
|
|
4746
|
-
|
|
4787
|
+
process.stdout.write(c.bold("Full findings are locked:\n"));
|
|
4788
|
+
process.stdout.write(` ${c.cyan(fixKitUrl)}
|
|
4747
4789
|
`);
|
|
4748
|
-
|
|
4790
|
+
process.stdout.write(
|
|
4749
4791
|
c.dim(
|
|
4750
|
-
"
|
|
4792
|
+
" This run could not attach scan evidence. The full report stays locked until purchase.\n\n"
|
|
4751
4793
|
)
|
|
4752
4794
|
);
|
|
4753
4795
|
}
|
|
4754
4796
|
} else {
|
|
4755
4797
|
if (scanSpecificUnlockUrl) {
|
|
4756
|
-
|
|
4798
|
+
process.stdout.write(
|
|
4757
4799
|
c.green("No automated findings detected for this scan.\n")
|
|
4758
4800
|
);
|
|
4759
|
-
|
|
4760
|
-
|
|
4801
|
+
process.stdout.write(c.bold("Unlock this exact scan:\n"));
|
|
4802
|
+
process.stdout.write(` ${c.cyan(scanSpecificUnlockUrl)}
|
|
4761
4803
|
|
|
4762
4804
|
`);
|
|
4763
4805
|
} else {
|
|
4764
|
-
|
|
4806
|
+
process.stdout.write(
|
|
4765
4807
|
c.green(
|
|
4766
|
-
"No automated findings detected.
|
|
4808
|
+
"No automated findings detected. Unlock the Launch Fix Kit for the full checklist and written launch report:\n"
|
|
4767
4809
|
)
|
|
4768
4810
|
);
|
|
4769
|
-
|
|
4811
|
+
process.stdout.write(` ${c.cyan(fixKitUrl)}
|
|
4770
4812
|
`);
|
|
4771
|
-
|
|
4813
|
+
process.stdout.write(
|
|
4772
4814
|
c.dim(
|
|
4773
|
-
"
|
|
4815
|
+
" This run could not attach scan evidence. The full report stays locked until purchase.\n\n"
|
|
4774
4816
|
)
|
|
4775
4817
|
);
|
|
4776
4818
|
}
|
|
4777
4819
|
}
|
|
4778
|
-
if (proofResult.status === "
|
|
4779
|
-
|
|
4780
|
-
`)
|
|
4781
|
-
} else if (proofResult.status === "failed") {
|
|
4782
|
-
process2.stdout.write(
|
|
4783
|
-
c.dim(`(Proof upload failed: ${proofResult.error ?? "unknown"})
|
|
4820
|
+
if (proofResult.status === "failed") {
|
|
4821
|
+
process.stdout.write(
|
|
4822
|
+
c.dim(`(Scan handoff failed: ${proofResult.error ?? "unknown"})
|
|
4784
4823
|
`)
|
|
4785
4824
|
);
|
|
4786
4825
|
}
|
|
4787
4826
|
if (publishResult === "published") {
|
|
4788
|
-
|
|
4827
|
+
process.stdout.write(
|
|
4789
4828
|
c.dim("(Anonymous Wall stats sent: score + severity counts.)\n")
|
|
4790
4829
|
);
|
|
4791
4830
|
}
|
|
@@ -4795,12 +4834,12 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
|
|
|
4795
4834
|
return 0;
|
|
4796
4835
|
}
|
|
4797
4836
|
run().then(
|
|
4798
|
-
(code) =>
|
|
4837
|
+
(code) => process.exit(code),
|
|
4799
4838
|
(err) => {
|
|
4800
|
-
|
|
4839
|
+
process.stderr.write(
|
|
4801
4840
|
`shippingszn failed: ${err instanceof Error ? err.message : String(err)}
|
|
4802
4841
|
`
|
|
4803
4842
|
);
|
|
4804
|
-
|
|
4843
|
+
process.exit(2);
|
|
4805
4844
|
}
|
|
4806
4845
|
);
|
package/package.json
CHANGED