@senpi/runtime 3.0.56 → 3.0.58
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/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +28 -2
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +2 -0
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/cli/validate-command.d.ts +29 -0
- package/dist/cli/validate-command.d.ts.map +1 -0
- package/dist/cli/validate-command.js +89 -0
- package/dist/cli/validate-command.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/load-runtime-config.d.ts +38 -0
- package/dist/runtime/load-runtime-config.d.ts.map +1 -1
- package/dist/runtime/load-runtime-config.js +33 -25
- package/dist/runtime/load-runtime-config.js.map +1 -1
- package/dist/runtime/runtime-schema.d.ts +100 -100
- package/dist/scanners/external/scaffold/__main__.py +8 -0
- package/dist/scanners/external/scaffold/ctx.py +15 -1
- package/dist/scanners/external/scaffold/http_post.py +30 -0
- package/dist/scanners/external/scaffold/import_check.py +310 -0
- package/dist/scanners/external/scaffold/preflight.py +545 -0
- package/dist/scanners/external/scaffold/scaffold.py +19 -6
- package/dist/scanners/implementations/market-regime.d.ts +1 -1
- package/dist/telemetry/metrics.d.ts +6 -0
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +45 -0
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/utils/event-catalog.d.ts +40 -0
- package/dist/utils/event-catalog.d.ts.map +1 -1
- package/dist/utils/event-catalog.js +12 -0
- package/dist/utils/event-catalog.js.map +1 -1
- package/dist/validate/codes.d.ts +165 -0
- package/dist/validate/codes.d.ts.map +1 -0
- package/dist/validate/codes.js +381 -0
- package/dist/validate/codes.js.map +1 -0
- package/dist/validate/coverage.d.ts +34 -0
- package/dist/validate/coverage.d.ts.map +1 -0
- package/dist/validate/coverage.js +34 -0
- package/dist/validate/coverage.js.map +1 -0
- package/dist/validate/import-stage.d.ts +56 -0
- package/dist/validate/import-stage.d.ts.map +1 -0
- package/dist/validate/import-stage.js +244 -0
- package/dist/validate/import-stage.js.map +1 -0
- package/dist/validate/index.d.ts +28 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +27 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/install-gate.d.ts +44 -0
- package/dist/validate/install-gate.d.ts.map +1 -0
- package/dist/validate/install-gate.js +66 -0
- package/dist/validate/install-gate.js.map +1 -0
- package/dist/validate/live-stage.d.ts +134 -0
- package/dist/validate/live-stage.d.ts.map +1 -0
- package/dist/validate/live-stage.js +472 -0
- package/dist/validate/live-stage.js.map +1 -0
- package/dist/validate/proof.d.ts +104 -0
- package/dist/validate/proof.d.ts.map +1 -0
- package/dist/validate/proof.js +269 -0
- package/dist/validate/proof.js.map +1 -0
- package/dist/validate/recipe-checks.d.ts +79 -0
- package/dist/validate/recipe-checks.d.ts.map +1 -0
- package/dist/validate/recipe-checks.js +205 -0
- package/dist/validate/recipe-checks.js.map +1 -0
- package/dist/validate/recipe.d.ts +41 -0
- package/dist/validate/recipe.d.ts.map +1 -0
- package/dist/validate/recipe.js +215 -0
- package/dist/validate/recipe.js.map +1 -0
- package/dist/validate/report.d.ts +35 -0
- package/dist/validate/report.d.ts.map +1 -0
- package/dist/validate/report.js +80 -0
- package/dist/validate/report.js.map +1 -0
- package/dist/validate/run.d.ts +101 -0
- package/dist/validate/run.d.ts.map +1 -0
- package/dist/validate/run.js +471 -0
- package/dist/validate/run.js.map +1 -0
- package/dist/validate/signal-stage.d.ts +40 -0
- package/dist/validate/signal-stage.d.ts.map +1 -0
- package/dist/validate/signal-stage.js +116 -0
- package/dist/validate/signal-stage.js.map +1 -0
- package/dist/validate/target.d.ts +70 -0
- package/dist/validate/target.d.ts.map +1 -0
- package/dist/validate/target.js +231 -0
- package/dist/validate/target.js.map +1 -0
- package/dist/validate/telemetry.d.ts +24 -0
- package/dist/validate/telemetry.d.ts.map +1 -0
- package/dist/validate/telemetry.js +60 -0
- package/dist/validate/telemetry.js.map +1 -0
- package/dist/validate/types.d.ts +115 -0
- package/dist/validate/types.d.ts.map +1 -0
- package/dist/validate/types.js +31 -0
- package/dist/validate/types.js.map +1 -0
- package/dist/validate/wallet.d.ts +63 -0
- package/dist/validate/wallet.d.ts.map +1 -0
- package/dist/validate/wallet.js +66 -0
- package/dist/validate/wallet.js.map +1 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The import depth: run the loadability checks and turn what they find into findings.
|
|
3
|
+
*
|
|
4
|
+
* The checks themselves live in Python, next to the loader the scaffold uses before its own first
|
|
5
|
+
* tick — so a package that passes has satisfied the contract production applies. This side spawns
|
|
6
|
+
* that checker, one process per scanner, and translates its output.
|
|
7
|
+
*
|
|
8
|
+
* ## Why a process per scanner
|
|
9
|
+
*
|
|
10
|
+
* Importing puts the scanner's directory at the front of the module search path, and two scanners
|
|
11
|
+
* routinely contain files of the same name. Checking several in one interpreter lets one scanner's
|
|
12
|
+
* `scoring` satisfy another's import of a file that does not exist — a broken package reporting
|
|
13
|
+
* clean. Production spawns a process per scanner for the same reason, so this matches it.
|
|
14
|
+
*/
|
|
15
|
+
// Via the repo's wrapper, never the stdlib module directly: OpenClaw's install gate
|
|
16
|
+
// blocks any plugin file matching BOTH a spawn-like call AND the literal module name,
|
|
17
|
+
// and `safe-spawn` exists to keep those bytes out of a file. Importing it directly here
|
|
18
|
+
// shipped in 3.0.57, whose published dist the gate refuses outright — a hard failure,
|
|
19
|
+
// not a warning, because senpi-agent's bootstrap deliberately stopped passing
|
|
20
|
+
// --dangerously-force-unsafe-install so that exactly this regression would be loud.
|
|
21
|
+
import { spawn } from "../utils/safe-spawn.js";
|
|
22
|
+
import { isAbsolute, resolve } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { VALIDATE_CODE } from "./codes.js";
|
|
25
|
+
/**
|
|
26
|
+
* Loading must terminate. Importing a module runs its body, so a `while True:` or a blocking
|
|
27
|
+
* connection at module level — exactly the class of defect this depth exists to find — would
|
|
28
|
+
* otherwise hang the command looking for it. Generous, because a large package legitimately takes
|
|
29
|
+
* a moment; the point is that it ends.
|
|
30
|
+
*/
|
|
31
|
+
const IMPORT_BUDGET_MS = 60_000;
|
|
32
|
+
/** The directory the `scaffold` package is importable from, in dev and in the built package alike. */
|
|
33
|
+
export function scaffoldPackageDir() {
|
|
34
|
+
return fileURLToPath(new URL("../scanners/external", import.meta.url));
|
|
35
|
+
}
|
|
36
|
+
function importFinding(code, fixKind, parts) {
|
|
37
|
+
const severity = code.startsWith("W_") ? "warning" : "error";
|
|
38
|
+
return { code, severity, stage: "import", actor: "agent", fix_kind: fixKind, ...parts };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Turn one raw result into findings.
|
|
42
|
+
*
|
|
43
|
+
* The Python side reports *what it observed*; the reasons and remedies are composed here, so all
|
|
44
|
+
* message policy lives in one language rather than being split across the boundary.
|
|
45
|
+
*/
|
|
46
|
+
export function toFindings(scanner, raw) {
|
|
47
|
+
const at = (file, line) => file === undefined ? undefined : { file, ...(line != null ? { line } : {}) };
|
|
48
|
+
switch (raw.code) {
|
|
49
|
+
case "SYNTAX":
|
|
50
|
+
return [
|
|
51
|
+
importFinding(VALIDATE_CODE.syntax, "edit", {
|
|
52
|
+
what: `${raw.file} does not compile: ${raw.message}`,
|
|
53
|
+
why: "Every file under the scanner directory is compiled, not only the ones the entrypoint " +
|
|
54
|
+
"imports — a broken helper behind a condition would otherwise stay invisible until that " +
|
|
55
|
+
"condition is true, in production.",
|
|
56
|
+
fix: `Correct the syntax at ${raw.file}${raw.line != null ? `:${raw.line}` : ""}.`,
|
|
57
|
+
where: at(raw.file, raw.line),
|
|
58
|
+
}),
|
|
59
|
+
];
|
|
60
|
+
case "IMPORT_FAILED": {
|
|
61
|
+
const chain = raw.import_chain ?? [];
|
|
62
|
+
const trail = chain.length > 1 ? `${chain.join(" → ")}: ` : "";
|
|
63
|
+
return [
|
|
64
|
+
importFinding(VALIDATE_CODE.importFailed, "edit", {
|
|
65
|
+
what: `${trail}${raw.error_type}: ${raw.message}`,
|
|
66
|
+
why: "This runs while the module is being imported, which the runtime does before the first " +
|
|
67
|
+
"tick. The scanner would die on launch and never produce a signal.",
|
|
68
|
+
fix: raw.where !== null && raw.where !== undefined
|
|
69
|
+
? `Fix ${raw.where.file}:${raw.where.line} — that is where the import chain actually broke, not ${scanner.entrypoint ?? "the entrypoint"}.`
|
|
70
|
+
: `Resolve the failed import in ${scanner.entrypoint ?? "the entrypoint"}.`,
|
|
71
|
+
where: raw.where ?? undefined,
|
|
72
|
+
evidence: { traceback: raw.traceback, import_chain: chain },
|
|
73
|
+
}),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
case "SCAN_MISSING":
|
|
77
|
+
return [
|
|
78
|
+
importFinding(VALIDATE_CODE.scanMissing, "edit", {
|
|
79
|
+
what: `${raw.file} imports, but defines no \`scan\` function`,
|
|
80
|
+
why: "The runtime looks up exactly this name. A module that imports cleanly with the entry " +
|
|
81
|
+
"function named anything else passes every check that does not run it, and then does " +
|
|
82
|
+
"nothing forever.",
|
|
83
|
+
fix: "Rename the entry function to `scan`, taking exactly two parameters: `def scan(inputs, ctx)`.",
|
|
84
|
+
where: at(raw.file),
|
|
85
|
+
}),
|
|
86
|
+
];
|
|
87
|
+
case "SCAN_NOT_CALLABLE":
|
|
88
|
+
return [
|
|
89
|
+
importFinding(VALIDATE_CODE.scanNotCallable, "edit", {
|
|
90
|
+
what: `\`scan\` exists in ${raw.file} but is not callable: ${raw.message}`,
|
|
91
|
+
why: "The runtime calls it every interval; a value cannot be called.",
|
|
92
|
+
fix: "Define `scan` as a function: `def scan(inputs, ctx)`.",
|
|
93
|
+
where: at(raw.file),
|
|
94
|
+
}),
|
|
95
|
+
];
|
|
96
|
+
case "SCAN_BAD_ARITY":
|
|
97
|
+
return [
|
|
98
|
+
importFinding(VALIDATE_CODE.scanBadArity, "edit", {
|
|
99
|
+
what: raw.message ?? "scan has the wrong number of parameters",
|
|
100
|
+
why: "The runtime calls `scan(inputs, ctx)` with exactly two arguments. Any other required shape raises on the first tick.",
|
|
101
|
+
fix: "Change the signature to `def scan(inputs, ctx)`. Extra parameters are allowed only with defaults.",
|
|
102
|
+
where: at(raw.file),
|
|
103
|
+
}),
|
|
104
|
+
];
|
|
105
|
+
case "SCANNERS_IS_PACKAGE":
|
|
106
|
+
return [
|
|
107
|
+
importFinding(VALIDATE_CODE.scannersIsPackage, "edit", {
|
|
108
|
+
what: `${scanner.root} contains __init__.py`,
|
|
109
|
+
why: "The runtime imports these files as loose top-level modules, never as a package. The " +
|
|
110
|
+
"marker suggests relative imports are available; they resolve in an editor and fail at " +
|
|
111
|
+
"run time.",
|
|
112
|
+
fix: "Delete __init__.py and import siblings by plain name — `import scoring`, not `from .scoring import …`.",
|
|
113
|
+
where: at(raw.file),
|
|
114
|
+
}),
|
|
115
|
+
];
|
|
116
|
+
case "SHADOWS_STDLIB":
|
|
117
|
+
return [
|
|
118
|
+
importFinding(VALIDATE_CODE.shadowsStdlib, "edit", {
|
|
119
|
+
what: `${raw.file} shadows the standard-library module \`${raw.module}\``,
|
|
120
|
+
why: "The scanner directory goes to the front of the module search path, so everything " +
|
|
121
|
+
"imported afterwards — including the runtime's own dependencies — resolves to this file " +
|
|
122
|
+
"instead of the real module.",
|
|
123
|
+
fix: `Rename ${raw.file} to something outside the standard library, and update its importers.`,
|
|
124
|
+
where: at(raw.file),
|
|
125
|
+
}),
|
|
126
|
+
];
|
|
127
|
+
case "NON_MCP_SOURCE":
|
|
128
|
+
return [
|
|
129
|
+
importFinding(VALIDATE_CODE.nonMcpSource, "edit", {
|
|
130
|
+
what: `${raw.file} imports \`${raw.module}\``,
|
|
131
|
+
why: "Data fetched outside the sanctioned client is invisible to the runtime, and to the " +
|
|
132
|
+
"check that decides whether a tick read anything at all — a scanner sourcing this way " +
|
|
133
|
+
"cannot be proven to work.",
|
|
134
|
+
fix: "Fetch through the context's data client so reads are observable.",
|
|
135
|
+
where: at(raw.file, raw.line),
|
|
136
|
+
}),
|
|
137
|
+
];
|
|
138
|
+
default:
|
|
139
|
+
// A code the Python side emits that this side does not know is a wiring mistake between the
|
|
140
|
+
// two, and silently dropping it would hide a check that is running but never reported.
|
|
141
|
+
return [
|
|
142
|
+
importFinding(VALIDATE_CODE.importFailed, "locate-only", {
|
|
143
|
+
what: `The loadability checker reported an unrecognised result: ${raw.code}`,
|
|
144
|
+
why: "This is a defect in the validator itself rather than in the strategy.",
|
|
145
|
+
fix: "Report this — the checker and its reader are out of step.",
|
|
146
|
+
evidence: { raw: raw },
|
|
147
|
+
}),
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** Run the Python checker for one scanner. */
|
|
152
|
+
export async function checkScanner(scanner, options = {}) {
|
|
153
|
+
const python = options.pythonBin ?? process.env.PYTHON ?? "python3";
|
|
154
|
+
const budgetMs = options.budgetMs ?? IMPORT_BUDGET_MS;
|
|
155
|
+
const args = ["-m", "scaffold.import_check", scanner.root];
|
|
156
|
+
if (scanner.entrypoint !== undefined)
|
|
157
|
+
args.push(scanner.entrypoint);
|
|
158
|
+
const existing = process.env.PYTHONPATH;
|
|
159
|
+
const pythonPath = existing ? `${scaffoldPackageDir()}:${existing}` : scaffoldPackageDir();
|
|
160
|
+
const raw = await new Promise((done) => {
|
|
161
|
+
const child = spawn(python, args, {
|
|
162
|
+
cwd: options.cwd,
|
|
163
|
+
env: { ...process.env, PYTHONPATH: pythonPath },
|
|
164
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
165
|
+
});
|
|
166
|
+
let stdout = "";
|
|
167
|
+
let stderr = "";
|
|
168
|
+
let settled = false;
|
|
169
|
+
const finish = (r) => {
|
|
170
|
+
if (settled)
|
|
171
|
+
return;
|
|
172
|
+
settled = true;
|
|
173
|
+
clearTimeout(timer);
|
|
174
|
+
done(r);
|
|
175
|
+
};
|
|
176
|
+
// Importing a module executes its body, and a `while True:` or a blocking connection at module
|
|
177
|
+
// level is exactly the broken code this depth exists to find — so it must not be able to hang
|
|
178
|
+
// the command that is looking for it. Generous, because a large package legitimately takes a
|
|
179
|
+
// moment; the point is that it terminates.
|
|
180
|
+
const timer = setTimeout(() => {
|
|
181
|
+
child.kill("SIGKILL");
|
|
182
|
+
finish({ code: null, stdout, stderr, timedOut: true });
|
|
183
|
+
}, budgetMs);
|
|
184
|
+
child.stdout.on("data", (chunk) => (stdout += String(chunk)));
|
|
185
|
+
child.stderr.on("data", (chunk) => (stderr += String(chunk)));
|
|
186
|
+
child.on("error", (err) => finish({ code: null, stdout, stderr: String(err) }));
|
|
187
|
+
child.on("close", (code) => finish({ code, stdout, stderr }));
|
|
188
|
+
});
|
|
189
|
+
if (raw.timedOut) {
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
code: VALIDATE_CODE.importTimeout,
|
|
193
|
+
severity: "error",
|
|
194
|
+
stage: "import",
|
|
195
|
+
actor: "agent",
|
|
196
|
+
fix_kind: "locate-only",
|
|
197
|
+
what: `Loading scanner '${scanner.name}' did not finish within ${budgetMs / 1000}s`,
|
|
198
|
+
why: "Importing a module runs its body. Work at module level — a loop, a blocking connection, a long computation — runs on every launch in production too, before the scanner has ticked once.",
|
|
199
|
+
fix: "Move work out of the module body and into scan(), where the tick budget governs it.",
|
|
200
|
+
evidence: { stdout: raw.stdout.slice(-2000), stderr: raw.stderr.slice(-2000) },
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
let parsed;
|
|
205
|
+
try {
|
|
206
|
+
parsed = JSON.parse(raw.stdout);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// Reaching here means the interpreter could not be started, or died before writing. That is an
|
|
210
|
+
// environment fault: an agent responding by editing the strategy would be editing working code.
|
|
211
|
+
return [
|
|
212
|
+
{
|
|
213
|
+
code: VALIDATE_CODE.importHarnessFailed,
|
|
214
|
+
severity: "error",
|
|
215
|
+
stage: "import",
|
|
216
|
+
actor: "ops",
|
|
217
|
+
fix_kind: "command",
|
|
218
|
+
what: `The loadability checker could not be run for scanner '${scanner.name}'`,
|
|
219
|
+
why: "Checking whether the code loads needs a Python interpreter that can import the runtime's scaffold package.",
|
|
220
|
+
fix: `Ensure \`${python}\` is on PATH. This is an environment problem, not a problem with the strategy.`,
|
|
221
|
+
evidence: { exit_code: raw.code, stderr: raw.stderr.slice(0, 2000) },
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
if (!parsed.ok) {
|
|
226
|
+
return [
|
|
227
|
+
importFinding(VALIDATE_CODE.importFailed, "locate-only", {
|
|
228
|
+
what: parsed.error ?? "the loadability checker could not run",
|
|
229
|
+
why: "Nothing about this scanner's code could be established.",
|
|
230
|
+
fix: `Check that ${scanner.root} exists and is readable.`,
|
|
231
|
+
}),
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
return (parsed.findings ?? []).flatMap((finding) => toFindings(scanner, finding));
|
|
235
|
+
}
|
|
236
|
+
/** Absolute scanner root for a declared path, resolved against the recipe's directory. */
|
|
237
|
+
export function resolveRoot(declared, baseDir) {
|
|
238
|
+
if (isAbsolute(declared))
|
|
239
|
+
return declared;
|
|
240
|
+
if (baseDir === undefined)
|
|
241
|
+
return undefined;
|
|
242
|
+
return resolve(baseDir, declared);
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=import-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-stage.js","sourceRoot":"","sources":["../../src/validate/import-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `senpi validate` — proving a strategy can actually run, before it is funded.
|
|
3
|
+
*
|
|
4
|
+
* This layer is the vocabulary: the finding shape every check speaks, the closed set of codes, and
|
|
5
|
+
* the accounting that keeps the two honest. Checks themselves live in sibling modules.
|
|
6
|
+
*/
|
|
7
|
+
export type { Actor, Depth, Finding, FindingLocation, FixKind, Severity, ValidationStage, } from "./types.js";
|
|
8
|
+
export { DEPTH_ORDER, depthsUpTo } from "./types.js";
|
|
9
|
+
export { resolveTarget, type ResolvedTarget, type TargetInput, type TargetKind, type TargetResolution, } from "./target.js";
|
|
10
|
+
export { ALL_VALIDATE_CODES, VALIDATE_CODE, VALIDATE_CODE_META, codesForStage, severityOf, type ValidateCode, type ValidateCodeMeta, } from "./codes.js";
|
|
11
|
+
export { findUncoveredCodes, findUnknownCodes, type CoverageOptions } from "./coverage.js";
|
|
12
|
+
export { findLlmActionsWithoutModel, findMarginPctFraction, findMissingEntrypoints, findUngatedStrategy, findUnsizedStrategy, } from "./recipe-checks.js";
|
|
13
|
+
export { collectRecipeFindings, type RecipeSource, type RecipeValidation, } from "./recipe.js";
|
|
14
|
+
export { isImplemented, runValidation, type ExitCode, type ValidateOptions, type ValidateResult, type Verdict, type VerdictBasis, } from "./run.js";
|
|
15
|
+
export { renderFinding, renderHuman, renderJson } from "./report.js";
|
|
16
|
+
/**
|
|
17
|
+
* What the runtime refuses to install. Wired into `senpi.installRuntime`; the boot loop
|
|
18
|
+
* deliberately does NOT use it — see the module header for why.
|
|
19
|
+
*/
|
|
20
|
+
export { INSTALL_BLOCKING_CODES, checkInstallGate, type InstallGateResult } from "./install-gate.js";
|
|
21
|
+
/**
|
|
22
|
+
* The deploy verb's half of the gate. `verifyProof(packageDir)` answers "is this package's
|
|
23
|
+
* content the content that passed?" — pure observation, never throws, and every refusal carries
|
|
24
|
+
* a finding whose fix is the same one command. Enforcement belongs to the caller.
|
|
25
|
+
*/
|
|
26
|
+
export { PROOF_FILENAME, fingerprintPackage, verifyProof, writeProof, type ProofRecord, type ScannerObservation, type VerifyOptions, type VerifyReason, type VerifyResult, } from "./proof.js";
|
|
27
|
+
export { checkScanner, resolveRoot, scaffoldPackageDir, toFindings, type ScannerTarget, } from "./import-stage.js";
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,KAAK,EACL,KAAK,EACL,OAAO,EACP,eAAe,EACf,OAAO,EACP,QAAQ,EACR,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,aAAa,EACb,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErE;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAErG;;;;GAIG;AACH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `senpi validate` — proving a strategy can actually run, before it is funded.
|
|
3
|
+
*
|
|
4
|
+
* This layer is the vocabulary: the finding shape every check speaks, the closed set of codes, and
|
|
5
|
+
* the accounting that keeps the two honest. Checks themselves live in sibling modules.
|
|
6
|
+
*/
|
|
7
|
+
export { DEPTH_ORDER, depthsUpTo } from "./types.js";
|
|
8
|
+
export { resolveTarget, } from "./target.js";
|
|
9
|
+
export { ALL_VALIDATE_CODES, VALIDATE_CODE, VALIDATE_CODE_META, codesForStage, severityOf, } from "./codes.js";
|
|
10
|
+
export { findUncoveredCodes, findUnknownCodes } from "./coverage.js";
|
|
11
|
+
export { findLlmActionsWithoutModel, findMarginPctFraction, findMissingEntrypoints, findUngatedStrategy, findUnsizedStrategy, } from "./recipe-checks.js";
|
|
12
|
+
export { collectRecipeFindings, } from "./recipe.js";
|
|
13
|
+
export { isImplemented, runValidation, } from "./run.js";
|
|
14
|
+
export { renderFinding, renderHuman, renderJson } from "./report.js";
|
|
15
|
+
/**
|
|
16
|
+
* What the runtime refuses to install. Wired into `senpi.installRuntime`; the boot loop
|
|
17
|
+
* deliberately does NOT use it — see the module header for why.
|
|
18
|
+
*/
|
|
19
|
+
export { INSTALL_BLOCKING_CODES, checkInstallGate } from "./install-gate.js";
|
|
20
|
+
/**
|
|
21
|
+
* The deploy verb's half of the gate. `verifyProof(packageDir)` answers "is this package's
|
|
22
|
+
* content the content that passed?" — pure observation, never throws, and every refusal carries
|
|
23
|
+
* a finding whose fix is the same one command. Enforcement belongs to the caller.
|
|
24
|
+
*/
|
|
25
|
+
export { PROOF_FILENAME, fingerprintPackage, verifyProof, writeProof, } from "./proof.js";
|
|
26
|
+
export { checkScanner, resolveRoot, scaffoldPackageDir, toFindings, } from "./import-stage.js";
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EACL,aAAa,GAKd,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,GAGX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAwB,MAAM,eAAe,CAAC;AAE3F,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,GAGtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,aAAa,EACb,aAAa,GAMd,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErE;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAA0B,MAAM,mBAAmB,CAAC;AAErG;;;;GAIG;AACH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,GAMX,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,UAAU,GAEX,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the runtime refuses to install.
|
|
3
|
+
*
|
|
4
|
+
* `senpi validate` reports these same problems, but reporting is advisory: nothing stops an
|
|
5
|
+
* operator installing a recipe validate would refuse. This closes that from the other side — at
|
|
6
|
+
* the one boundary where refusing is free of consequence.
|
|
7
|
+
*
|
|
8
|
+
* ## Why install and not boot
|
|
9
|
+
*
|
|
10
|
+
* Refusing an install affects only the thing being installed: the gateway answers an error and
|
|
11
|
+
* everything already running is untouched. The boot loop is not like that — it starts runtimes in
|
|
12
|
+
* a single `try` whose `catch` stops every runtime already started, so a throw there takes the box
|
|
13
|
+
* down. A boot-time refusal would convert "one bad recipe trades" into "one bad recipe stops every
|
|
14
|
+
* other strategy", which is strictly worse than the problem. Boot-side enforcement waits for the
|
|
15
|
+
* per-entry degrade the supervised-scanner mount already uses.
|
|
16
|
+
*
|
|
17
|
+
* ## What it refuses, and what it deliberately does not
|
|
18
|
+
*
|
|
19
|
+
* Only conditions that make the recipe **inert or unsafe on every tick**, never a matter of taste:
|
|
20
|
+
* an entry action with no reachable margin source declines every signal it ever sees; one with no
|
|
21
|
+
* guardrails trades with no daily-loss or drawdown ceiling; a declared scanner file that does not
|
|
22
|
+
* exist can never start. Warnings stay warnings — this gate is not `--strict`, because an install
|
|
23
|
+
* refusal an operator disagrees with is how a safety check gets routed around.
|
|
24
|
+
*
|
|
25
|
+
* Measured before it was enforced: none of the 124 deployed instances trips any of these.
|
|
26
|
+
*/
|
|
27
|
+
import type { Finding } from "./types.js";
|
|
28
|
+
export interface InstallGateResult {
|
|
29
|
+
ok: boolean;
|
|
30
|
+
/** The findings that blocked, in report order. Empty when `ok`. */
|
|
31
|
+
blocking: Finding[];
|
|
32
|
+
/** Ready to hand to the gateway's error responder. Present exactly when blocked. */
|
|
33
|
+
message?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Decide whether a parsed recipe may be installed.
|
|
37
|
+
*
|
|
38
|
+
* Takes findings rather than a config so the caller owns how they were produced, and so this stays
|
|
39
|
+
* a pure decision over data — the same findings the CLI would print.
|
|
40
|
+
*/
|
|
41
|
+
export declare function checkInstallGate(findings: Finding[]): InstallGateResult;
|
|
42
|
+
/** The codes this gate blocks on. Exported so a test can assert the list, not infer it. */
|
|
43
|
+
export declare const INSTALL_BLOCKING_CODES: readonly string[];
|
|
44
|
+
//# sourceMappingURL=install-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-gate.d.ts","sourceRoot":"","sources":["../../src/validate/install-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAgB1C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,mEAAmE;IACnE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAKvE;AAED,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAkB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the runtime refuses to install.
|
|
3
|
+
*
|
|
4
|
+
* `senpi validate` reports these same problems, but reporting is advisory: nothing stops an
|
|
5
|
+
* operator installing a recipe validate would refuse. This closes that from the other side — at
|
|
6
|
+
* the one boundary where refusing is free of consequence.
|
|
7
|
+
*
|
|
8
|
+
* ## Why install and not boot
|
|
9
|
+
*
|
|
10
|
+
* Refusing an install affects only the thing being installed: the gateway answers an error and
|
|
11
|
+
* everything already running is untouched. The boot loop is not like that — it starts runtimes in
|
|
12
|
+
* a single `try` whose `catch` stops every runtime already started, so a throw there takes the box
|
|
13
|
+
* down. A boot-time refusal would convert "one bad recipe trades" into "one bad recipe stops every
|
|
14
|
+
* other strategy", which is strictly worse than the problem. Boot-side enforcement waits for the
|
|
15
|
+
* per-entry degrade the supervised-scanner mount already uses.
|
|
16
|
+
*
|
|
17
|
+
* ## What it refuses, and what it deliberately does not
|
|
18
|
+
*
|
|
19
|
+
* Only conditions that make the recipe **inert or unsafe on every tick**, never a matter of taste:
|
|
20
|
+
* an entry action with no reachable margin source declines every signal it ever sees; one with no
|
|
21
|
+
* guardrails trades with no daily-loss or drawdown ceiling; a declared scanner file that does not
|
|
22
|
+
* exist can never start. Warnings stay warnings — this gate is not `--strict`, because an install
|
|
23
|
+
* refusal an operator disagrees with is how a safety check gets routed around.
|
|
24
|
+
*
|
|
25
|
+
* Measured before it was enforced: none of the 124 deployed instances trips any of these.
|
|
26
|
+
*/
|
|
27
|
+
import { VALIDATE_CODE } from "./codes.js";
|
|
28
|
+
/**
|
|
29
|
+
* The conditions that block an install.
|
|
30
|
+
*
|
|
31
|
+
* A closed list rather than "any error-severity finding": the static depth also reports things
|
|
32
|
+
* that are wrong but not dangerous, and an install gate that grows silently as checks are added
|
|
33
|
+
* is one that will one day refuse something nobody intended it to.
|
|
34
|
+
*/
|
|
35
|
+
const BLOCKING = new Set([
|
|
36
|
+
VALIDATE_CODE.unsized,
|
|
37
|
+
VALIDATE_CODE.ungated,
|
|
38
|
+
VALIDATE_CODE.entrypointMissing,
|
|
39
|
+
VALIDATE_CODE.unresolvableScannerPath,
|
|
40
|
+
]);
|
|
41
|
+
/**
|
|
42
|
+
* Render a refusal the recipient can act on without seeing this code.
|
|
43
|
+
*
|
|
44
|
+
* The gateway's error string is often the only thing an operator or agent ever reads, so it
|
|
45
|
+
* carries the same what/why/fix the findings do rather than a count and a pointer to logs.
|
|
46
|
+
*/
|
|
47
|
+
function render(blocking) {
|
|
48
|
+
const lines = blocking.map((f) => ` • ${f.what}\n why: ${f.why}\n fix: ${f.fix}`);
|
|
49
|
+
return (`This recipe cannot be installed — it would not trade.\n\n${lines.join("\n\n")}\n\n` +
|
|
50
|
+
`Run \`senpi validate\` on the package for the full report.`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Decide whether a parsed recipe may be installed.
|
|
54
|
+
*
|
|
55
|
+
* Takes findings rather than a config so the caller owns how they were produced, and so this stays
|
|
56
|
+
* a pure decision over data — the same findings the CLI would print.
|
|
57
|
+
*/
|
|
58
|
+
export function checkInstallGate(findings) {
|
|
59
|
+
const blocking = findings.filter((f) => BLOCKING.has(f.code) && f.severity === "error");
|
|
60
|
+
return blocking.length === 0
|
|
61
|
+
? { ok: true, blocking: [] }
|
|
62
|
+
: { ok: false, blocking, message: render(blocking) };
|
|
63
|
+
}
|
|
64
|
+
/** The codes this gate blocks on. Exported so a test can assert the list, not infer it. */
|
|
65
|
+
export const INSTALL_BLOCKING_CODES = [...BLOCKING];
|
|
66
|
+
//# sourceMappingURL=install-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-gate.js","sourceRoot":"","sources":["../../src/validate/install-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS;IAC/B,aAAa,CAAC,OAAO;IACrB,aAAa,CAAC,OAAO;IACrB,aAAa,CAAC,iBAAiB;IAC/B,aAAa,CAAC,uBAAuB;CACtC,CAAC,CAAC;AAUH;;;;;GAKG;AACH,SAAS,MAAM,CAAC,QAAmB;IACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACzF,OAAO,CACL,4DAA4D,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM;QACpF,4DAA4D,CAC7D,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAmB;IAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IACxF,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC1B,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC5B,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AACzD,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,GAAG,QAAQ,CAAC,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live depth: run one tick against real data, and say what it established.
|
|
3
|
+
*
|
|
4
|
+
* The harness is in Python, beside the loop production uses. This side builds its launch
|
|
5
|
+
* configuration, runs it, and judges the outcome.
|
|
6
|
+
*
|
|
7
|
+
* ## The judgement that matters
|
|
8
|
+
*
|
|
9
|
+
* A tick returning nothing is ambiguous. It could be a healthy scanner with no setups, or one that
|
|
10
|
+
* stopped before touching anything, or one whose every read failed. Those look identical from the
|
|
11
|
+
* outside and mean completely different things, so the verdict keys on **successful reads** rather
|
|
12
|
+
* than on the tick completing.
|
|
13
|
+
*
|
|
14
|
+
* A read that failed is not a read. Observed in practice: an authentication failure arrives as an
|
|
15
|
+
* ordinary response carrying a false flag, raising nothing, so a scanner's own error handling never
|
|
16
|
+
* fires and it computes a confidently wrong answer from missing input. Counting that as a read
|
|
17
|
+
* would report the run clean.
|
|
18
|
+
*/
|
|
19
|
+
import { type ScannerTarget } from "./import-stage.js";
|
|
20
|
+
import { type SignalRecord } from "./signal-stage.js";
|
|
21
|
+
import type { Finding, FindingLocation } from "./types.js";
|
|
22
|
+
import type { ResolvedWallet } from "./wallet.js";
|
|
23
|
+
/** One recorded call, as the harness reports it. */
|
|
24
|
+
export interface ReadRecord {
|
|
25
|
+
tool: string;
|
|
26
|
+
ms: number;
|
|
27
|
+
ok: boolean;
|
|
28
|
+
error?: string;
|
|
29
|
+
error_type?: string;
|
|
30
|
+
response_keys?: string[];
|
|
31
|
+
cast_dropped?: boolean;
|
|
32
|
+
/** The call would have changed state and was refused rather than made. */
|
|
33
|
+
blocked?: boolean;
|
|
34
|
+
/** The response came back and the scanner never looked inside it. */
|
|
35
|
+
unread?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface TickOutcome {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
reason?: string;
|
|
40
|
+
detail?: string;
|
|
41
|
+
status?: "ok" | "error" | "timeout";
|
|
42
|
+
failure?: {
|
|
43
|
+
type: string;
|
|
44
|
+
message: string;
|
|
45
|
+
where?: {
|
|
46
|
+
file: string;
|
|
47
|
+
line: number;
|
|
48
|
+
} | null;
|
|
49
|
+
} | null;
|
|
50
|
+
reads?: ReadRecord[];
|
|
51
|
+
reads_ok?: number;
|
|
52
|
+
reads_failed?: number;
|
|
53
|
+
signals?: SignalRecord[];
|
|
54
|
+
suppressed?: {
|
|
55
|
+
file: string;
|
|
56
|
+
line: number;
|
|
57
|
+
type: string;
|
|
58
|
+
message: string;
|
|
59
|
+
}[];
|
|
60
|
+
last_return?: {
|
|
61
|
+
file: string;
|
|
62
|
+
line: number;
|
|
63
|
+
function: string;
|
|
64
|
+
} | null;
|
|
65
|
+
stdout?: string;
|
|
66
|
+
stderr?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface LiveOptions {
|
|
69
|
+
scanner: ScannerTarget;
|
|
70
|
+
wallet: ResolvedWallet;
|
|
71
|
+
/** Per-scanner budget. Defaults to what the recipe declares — never to the cadence. */
|
|
72
|
+
timeoutSeconds: number;
|
|
73
|
+
intervalSeconds: number;
|
|
74
|
+
defaultSignalValiditySeconds: number;
|
|
75
|
+
inputs?: Record<string, unknown>;
|
|
76
|
+
signalDataSchema?: Record<string, unknown>;
|
|
77
|
+
stateHistoryMaxCount?: number;
|
|
78
|
+
pythonBin?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Group failed reads by the service they belong to.
|
|
82
|
+
*
|
|
83
|
+
* One failed call among many is noise; every call to one service failing means a whole input is
|
|
84
|
+
* missing and the scanner scored without it. The tool-name prefix is the service, which is enough
|
|
85
|
+
* to tell those apart without knowing anything about the catalogue.
|
|
86
|
+
*/
|
|
87
|
+
export declare function groupFailuresByService(reads: ReadRecord[]): Map<string, ReadRecord[]>;
|
|
88
|
+
/** What an earlier depth found that changes how this one reads its own result. */
|
|
89
|
+
export interface TickContext {
|
|
90
|
+
/**
|
|
91
|
+
* Places the import depth saw a data route the runtime cannot observe.
|
|
92
|
+
*
|
|
93
|
+
* Carried here because a tick that read nothing through the client, in a scanner that imports
|
|
94
|
+
* `requests`, is a different situation from one that stopped early — and pointing the second
|
|
95
|
+
* diagnosis at the first sends someone hunting a gate that does not exist.
|
|
96
|
+
*/
|
|
97
|
+
nonMcpSources?: FindingLocation[];
|
|
98
|
+
/**
|
|
99
|
+
* What this run allowed a tick, and what production would allow it.
|
|
100
|
+
*
|
|
101
|
+
* They are not the same number. Validate caps at its own default so a six-hour-cadence scanner
|
|
102
|
+
* does not make the command hang for six hours; production, when the recipe declares no
|
|
103
|
+
* `timeout_seconds`, falls back to the interval (`scaffold.py`: `timeout_seconds = interval`).
|
|
104
|
+
* Carried here because a timeout finding that says "this would time out in production too" is
|
|
105
|
+
* simply false in that case, and a validator that states something untrue about production has
|
|
106
|
+
* failed at the one job it has.
|
|
107
|
+
*/
|
|
108
|
+
budget?: TickBudget;
|
|
109
|
+
}
|
|
110
|
+
export interface TickBudget {
|
|
111
|
+
/** The budget this run enforced. */
|
|
112
|
+
validateSeconds: number;
|
|
113
|
+
/** The budget production would enforce for the same scanner. */
|
|
114
|
+
productionSeconds: number;
|
|
115
|
+
/** Whether the recipe declared `timeout_seconds`. When it did, the two agree. */
|
|
116
|
+
declared: boolean;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Production's tick budget for a scanner, mirroring the scaffold's own fallback.
|
|
120
|
+
*
|
|
121
|
+
* Kept beside the judgement that reports it, with the source named, because the whole point is
|
|
122
|
+
* that this number is production's and not ours.
|
|
123
|
+
*/
|
|
124
|
+
export declare function productionBudgetSeconds(declaredTimeout: number | undefined, intervalSeconds: number): number;
|
|
125
|
+
/**
|
|
126
|
+
* Turn one tick into findings.
|
|
127
|
+
*
|
|
128
|
+
* Exported separately from running it so the judgement can be tested against recorded outcomes
|
|
129
|
+
* without spawning anything or needing credentials.
|
|
130
|
+
*/
|
|
131
|
+
export declare function judgeTick(scanner: ScannerTarget, outcome: TickOutcome, context?: TickContext): Finding[];
|
|
132
|
+
/** Run one tick for one scanner. */
|
|
133
|
+
export declare function runTick(options: LiveOptions): Promise<TickOutcome>;
|
|
134
|
+
//# sourceMappingURL=live-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-stage.d.ts","sourceRoot":"","sources":["../../src/validate/live-stage.ts"],"names":[],"mappings":"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"}
|