seaworthycode 1.2.7 → 1.2.9
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/dist/index.js +86 -55
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -782,6 +782,7 @@ var copy = {
|
|
|
782
782
|
"report.bySeverity": "By severity",
|
|
783
783
|
"report.tierWatermark": (licensedTo) => `\u2693 Pro \xB7 ${licensedTo}`,
|
|
784
784
|
"report.scanProgress.starting": (targetDir) => `\u2693 Scanning ${targetDir}...`,
|
|
785
|
+
"report.scanProgress.checking": (completed, total) => `\u2693 Checking... [${completed}/${total}]`,
|
|
785
786
|
"report.scanProgress.category": (label, count) => count > 0 ? ` \u2693 ${label} \u2014 ${count} issue(s)` : ` \u2693 ${label} \u2713`,
|
|
786
787
|
"report.licenseNotice": (licensedTo) => `Licensed to ${licensedTo}`,
|
|
787
788
|
"report.cta.clean": `\u2713 All clear \u2014 your project is shipshape!`,
|
|
@@ -1330,7 +1331,7 @@ var copy = {
|
|
|
1330
1331
|
"debug.cache_write_failed": (key) => `Cache write failed for key ${key}, skipping.`,
|
|
1331
1332
|
// CLI
|
|
1332
1333
|
"cli.help.description": "Seaworthy \u2014 static analysis for vibe coders",
|
|
1333
|
-
"cli.help.usage": "Usage:
|
|
1334
|
+
"cli.help.usage": "Usage: seaworthycode [path] [options]",
|
|
1334
1335
|
"cli.help.options": "Options",
|
|
1335
1336
|
"cli.scanning": "Scanning...",
|
|
1336
1337
|
"cli.reportWritten": (path) => `Report written to ${path}`,
|
|
@@ -1621,6 +1622,12 @@ var ScanRunner = class {
|
|
|
1621
1622
|
};
|
|
1622
1623
|
const results = [];
|
|
1623
1624
|
const checkResults = /* @__PURE__ */ new Map();
|
|
1625
|
+
const categoryTotals = /* @__PURE__ */ new Map();
|
|
1626
|
+
for (const check2 of checks) {
|
|
1627
|
+
categoryTotals.set(check2.category, (categoryTotals.get(check2.category) ?? 0) + 1);
|
|
1628
|
+
}
|
|
1629
|
+
const categoryCompleted = /* @__PURE__ */ new Map();
|
|
1630
|
+
let completedCount = 0;
|
|
1624
1631
|
await runWithConcurrency(
|
|
1625
1632
|
checks,
|
|
1626
1633
|
async (check2) => {
|
|
@@ -1630,26 +1637,26 @@ var ScanRunner = class {
|
|
|
1630
1637
|
} catch (err) {
|
|
1631
1638
|
console.error(`Check "${check2.id}" failed: ${String(err)}`);
|
|
1632
1639
|
}
|
|
1640
|
+
options.onCheckComplete?.(++completedCount, checks.length);
|
|
1641
|
+
if (options.onCategoryComplete) {
|
|
1642
|
+
const catDone = (categoryCompleted.get(check2.category) ?? 0) + 1;
|
|
1643
|
+
categoryCompleted.set(check2.category, catDone);
|
|
1644
|
+
if (catDone === categoryTotals.get(check2.category)) {
|
|
1645
|
+
let catFindingsCount = 0;
|
|
1646
|
+
for (const c of checks) {
|
|
1647
|
+
if (c.category === check2.category) {
|
|
1648
|
+
catFindingsCount += (checkResults.get(c.id) ?? []).length;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
options.onCategoryComplete(check2.category, catFindingsCount);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1633
1654
|
},
|
|
1634
1655
|
concurrency
|
|
1635
1656
|
);
|
|
1636
1657
|
for (const check2 of checks) {
|
|
1637
1658
|
results.push(...checkResults.get(check2.id) ?? []);
|
|
1638
1659
|
}
|
|
1639
|
-
if (options.onCategoryComplete) {
|
|
1640
|
-
const findingsPerCategory = /* @__PURE__ */ new Map();
|
|
1641
|
-
for (const check2 of checks) {
|
|
1642
|
-
const count = (checkResults.get(check2.id) ?? []).length;
|
|
1643
|
-
findingsPerCategory.set(check2.category, (findingsPerCategory.get(check2.category) ?? 0) + count);
|
|
1644
|
-
}
|
|
1645
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1646
|
-
for (const check2 of checks) {
|
|
1647
|
-
if (!seen.has(check2.category)) {
|
|
1648
|
-
seen.add(check2.category);
|
|
1649
|
-
options.onCategoryComplete(check2.category, findingsPerCategory.get(check2.category) ?? 0);
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
1660
|
ctx.astCache?.clear();
|
|
1654
1661
|
for (const [, promise] of ctx.bashAstCache ?? []) {
|
|
1655
1662
|
try {
|
|
@@ -15914,7 +15921,7 @@ import { dirname as dirname5, resolve as resolve2, relative as relative7, isAbso
|
|
|
15914
15921
|
// src/copy/index.ts
|
|
15915
15922
|
var copy3 = {
|
|
15916
15923
|
"cli.help.description": "Seaworthy \u2014 static analysis for vibe coders",
|
|
15917
|
-
"cli.help.usage": "Usage:
|
|
15924
|
+
"cli.help.usage": "Usage: seaworthycode [path] [options]",
|
|
15918
15925
|
"cli.help.options": "Options",
|
|
15919
15926
|
"cli.help.learnMore": `Documentation: ${SITE_URL}/docs`,
|
|
15920
15927
|
"cli.setup.intro": "Welcome to Seaworthy! Let's get you set up.",
|
|
@@ -15943,14 +15950,14 @@ var copy3 = {
|
|
|
15943
15950
|
"cli.help.flag.sarifStdout": "Write SARIF report to stdout",
|
|
15944
15951
|
"cli.sarif.mutually_exclusive": "Flags --sarif and --sarif-stdout cannot be used together.",
|
|
15945
15952
|
"cli.sarif.stdout_conflict": "Flags --sarif-stdout and --json cannot be used together. Use --output to send JSON to a file.",
|
|
15946
|
-
"cli.error.llmRequired": "LLM API key required for Pro checks. Set it with
|
|
15947
|
-
"cli.warn.llmSkipped": "Pro checks skipped: no LLM API key configured. Set it with
|
|
15953
|
+
"cli.error.llmRequired": "LLM API key required for Pro checks. Set it with seaworthycode config or the SEAWORTHY_LLM_API_KEY environment variable.",
|
|
15954
|
+
"cli.warn.llmSkipped": "Pro checks skipped: no LLM API key configured. Set it with seaworthycode config or the SEAWORTHY_LLM_API_KEY environment variable to enable semantic analysis.",
|
|
15948
15955
|
"cli.warn.licenseInvalid": "Your license key is invalid or has been revoked. Running in free tier.\nTo re-enter your key, run: npx seaworthycode setup",
|
|
15949
|
-
"cli.error.config.missingKey": "Missing config key. Usage:
|
|
15950
|
-
"cli.error.config.missingValue": "Missing config value. Usage:
|
|
15956
|
+
"cli.error.config.missingKey": "Missing config key. Usage: seaworthycode config <set|get> <key>",
|
|
15957
|
+
"cli.error.config.missingValue": "Missing config value. Usage: seaworthycode config set <key> <value>",
|
|
15951
15958
|
"cli.error.config.unknownKey": "Unknown config key. Valid keys: licenseKey, llmProvider, llmApiKey, llmModel, llmApiUrl",
|
|
15952
15959
|
"cli.error.config.invalidProvider": "Invalid provider. Valid providers: anthropic, openai, kimi, moonshot, deepseek, gemini, ollama, openrouter",
|
|
15953
|
-
"cli.error.config.invalidAction": "Invalid config action. Usage:
|
|
15960
|
+
"cli.error.config.invalidAction": "Invalid config action. Usage: seaworthycode config <set|get|list>",
|
|
15954
15961
|
"cli.config.setSuccess": (key) => `${key} updated.`,
|
|
15955
15962
|
"cli.licenseNotice": (licensedTo) => `Licensed to ${licensedTo}`,
|
|
15956
15963
|
"cli.error.debugHint": (logPath) => `Re-run with --debug to write a diagnostic log: ${logPath}
|
|
@@ -15996,9 +16003,9 @@ function createCommand(definition, options) {
|
|
|
15996
16003
|
}
|
|
15997
16004
|
|
|
15998
16005
|
// src/commands/scan.ts
|
|
15999
|
-
var
|
|
16000
|
-
var
|
|
16001
|
-
function
|
|
16006
|
+
var FREE_CHECK_IDLE_MS = 6e4;
|
|
16007
|
+
var PRO_CHECK_IDLE_MS = 3e5;
|
|
16008
|
+
function resolveCheckIdleMs(tier) {
|
|
16002
16009
|
const env2 = process.env.SEAWORTHY_SCAN_TIMEOUT_MS;
|
|
16003
16010
|
if (env2) {
|
|
16004
16011
|
const parsed = Number(env2);
|
|
@@ -16006,7 +16013,7 @@ function resolveScanTimeoutMs(tier) {
|
|
|
16006
16013
|
return parsed;
|
|
16007
16014
|
}
|
|
16008
16015
|
}
|
|
16009
|
-
return tier === "free" ?
|
|
16016
|
+
return tier === "free" ? FREE_CHECK_IDLE_MS : PRO_CHECK_IDLE_MS;
|
|
16010
16017
|
}
|
|
16011
16018
|
var CATEGORY_LABELS = {
|
|
16012
16019
|
security: getCopy2("cli.category.security"),
|
|
@@ -16016,17 +16023,29 @@ var CATEGORY_LABELS = {
|
|
|
16016
16023
|
configuration: getCopy2("cli.category.configuration"),
|
|
16017
16024
|
"data-exposure": getCopy2("cli.category.data-exposure")
|
|
16018
16025
|
};
|
|
16019
|
-
function
|
|
16020
|
-
|
|
16021
|
-
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16026
|
+
function withIdleTimeout(promise, idleMs, message) {
|
|
16027
|
+
let timerId;
|
|
16028
|
+
let rejectFn = () => {
|
|
16029
|
+
};
|
|
16030
|
+
const heartbeat = () => {
|
|
16031
|
+
clearTimeout(timerId);
|
|
16032
|
+
timerId = setTimeout(() => rejectFn(new Error(message)), idleMs);
|
|
16033
|
+
};
|
|
16034
|
+
const wrappedPromise = new Promise((resolve5, reject) => {
|
|
16035
|
+
rejectFn = reject;
|
|
16036
|
+
heartbeat();
|
|
16037
|
+
promise.then(
|
|
16038
|
+
(value) => {
|
|
16039
|
+
clearTimeout(timerId);
|
|
16040
|
+
resolve5(value);
|
|
16041
|
+
},
|
|
16042
|
+
(err) => {
|
|
16043
|
+
clearTimeout(timerId);
|
|
16044
|
+
reject(err);
|
|
16045
|
+
}
|
|
16046
|
+
);
|
|
16029
16047
|
});
|
|
16048
|
+
return { promise: wrappedPromise, heartbeat };
|
|
16030
16049
|
}
|
|
16031
16050
|
function isInsideDirectory(outputPath, targetDir) {
|
|
16032
16051
|
const resolvedOutput = resolve2(outputPath);
|
|
@@ -16058,24 +16077,36 @@ async function executeScan(args, ctx) {
|
|
|
16058
16077
|
process.stderr.write(getCopy("report.scanProgress.starting", args.targetDir) + "\n");
|
|
16059
16078
|
}
|
|
16060
16079
|
const runner = new ScanRunner();
|
|
16061
|
-
const
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16080
|
+
const checkIdleMs = resolveCheckIdleMs(ctx.tier);
|
|
16081
|
+
let heartbeat = () => {
|
|
16082
|
+
};
|
|
16083
|
+
const scanPromise = runner.run({
|
|
16084
|
+
targetDir: args.targetDir,
|
|
16085
|
+
tier: ctx.tier,
|
|
16086
|
+
llm,
|
|
16087
|
+
maxFileSize: args.maxFileSize,
|
|
16088
|
+
debug: args.debug,
|
|
16089
|
+
baseline: args.baseline,
|
|
16090
|
+
minConfidence: args.minConfidence,
|
|
16091
|
+
onCheckComplete: (completed, total) => {
|
|
16092
|
+
heartbeat();
|
|
16093
|
+
if (showProgress) {
|
|
16094
|
+
process.stderr.write(`\r${getCopy("report.scanProgress.checking", completed, total)}\x1B[K`);
|
|
16095
|
+
}
|
|
16096
|
+
},
|
|
16097
|
+
onCategoryComplete: showProgress ? (category, count) => {
|
|
16098
|
+
const label = CATEGORY_LABELS[category] ?? category;
|
|
16099
|
+
process.stderr.write(`\r\x1B[K${getCopy("report.scanProgress.category", label, count)}
|
|
16100
|
+
`);
|
|
16101
|
+
} : void 0
|
|
16102
|
+
});
|
|
16103
|
+
const { promise: timedScanPromise, heartbeat: hb } = withIdleTimeout(
|
|
16104
|
+
scanPromise,
|
|
16105
|
+
checkIdleMs,
|
|
16106
|
+
getCopy("errors.scan.timed_out", Math.round(checkIdleMs / 1e3))
|
|
16078
16107
|
);
|
|
16108
|
+
heartbeat = hb;
|
|
16109
|
+
const result = await timedScanPromise;
|
|
16079
16110
|
const reportInput = {
|
|
16080
16111
|
findings: result.findings,
|
|
16081
16112
|
targetDir: args.targetDir,
|
|
@@ -16267,7 +16298,7 @@ var FsCredentialProvider = class {
|
|
|
16267
16298
|
import { readFile } from "fs/promises";
|
|
16268
16299
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16269
16300
|
import { dirname as dirname6, join as join14 } from "path";
|
|
16270
|
-
var NPM_REGISTRY_URL = "https://registry.npmjs.org/
|
|
16301
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/seaworthycode";
|
|
16271
16302
|
var UPDATE_TIMEOUT_MS = 3e3;
|
|
16272
16303
|
function isLowerVersion(current, latest) {
|
|
16273
16304
|
const parse2 = (v) => v.split(".").map((n) => Number(n));
|
|
@@ -16300,7 +16331,7 @@ async function checkUpdate() {
|
|
|
16300
16331
|
}
|
|
16301
16332
|
const installed = await getInstalledVersion();
|
|
16302
16333
|
if (isLowerVersion(installed, latest)) {
|
|
16303
|
-
return `Update available: ${installed} \u2192 ${latest}. Run npm i -g
|
|
16334
|
+
return `Update available: ${installed} \u2192 ${latest}. Run npm i -g seaworthycode to update.`;
|
|
16304
16335
|
}
|
|
16305
16336
|
return void 0;
|
|
16306
16337
|
} catch {
|
|
@@ -16605,7 +16636,7 @@ if (existsSync6(repoRootEnv)) {
|
|
|
16605
16636
|
}
|
|
16606
16637
|
var require5 = createRequire4(import.meta.url);
|
|
16607
16638
|
var { version } = require5("../package.json");
|
|
16608
|
-
var cli = cac("
|
|
16639
|
+
var cli = cac("seaworthycode");
|
|
16609
16640
|
cli.version(version);
|
|
16610
16641
|
cli.command("[path]", "Scan a directory for issues").option("--json", "Output JSON to stdout").option("--output <path>", "Write HTML report to file").option("--sarif <path>", "Write SARIF report to file").option("--sarif-stdout", "Write SARIF report to stdout").option("--fail-on-findings", "Exit with code 5 when findings are found").option("--tier <tier>", "Select tier (free, pro)").option("--provider <provider>", "LLM provider (anthropic, openai, kimi, moonshot, deepseek, gemini, minimax, ollama, openrouter)").option("--max-file-size <bytes>", "Skip files larger than this (default: 1 MB)").option("--show-ids", "Show check IDs alongside names in terminal output").option("--debug", "Enable debug logging").option("--baseline [ref]", "Compare against a baseline file or save current findings as baseline (--baseline save)").option("--min-confidence <level>", "Hide findings below this confidence (high, medium, low)").option("--all", "List every finding; do not collapse low-severity findings").action(async (targetDir, options) => {
|
|
16611
16642
|
const maxFileSize = options.maxFileSize ? Number(options.maxFileSize) : void 0;
|