@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 @@
|
|
|
1
|
+
{"version":3,"file":"signal-stage.js","sourceRoot":"","sources":["../../src/validate/signal-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving what the caller pointed at.
|
|
3
|
+
*
|
|
4
|
+
* Runs before anything is parsed. Its whole job is to answer "what am I validating, and how deep
|
|
5
|
+
* can I go against it" — and, when it cannot, to say so in terms of what is actually on disk rather
|
|
6
|
+
* than what was expected to be.
|
|
7
|
+
*
|
|
8
|
+
* That last part is why these failures are worth care despite being the least interesting ones: they
|
|
9
|
+
* are the first thing an agent hits, and a refusal that cannot name the real alternatives turns into
|
|
10
|
+
* guessing. So the resolver reads the directory before it complains about it.
|
|
11
|
+
*
|
|
12
|
+
* ## Filenames are conventions, not contract
|
|
13
|
+
*
|
|
14
|
+
* Nothing in the runtime requires a recipe to be called `runtime.yaml`, or a scanner directory to be
|
|
15
|
+
* called `scanners/` — install accepts any path, and the scanner location is a field inside the
|
|
16
|
+
* recipe. Those names are used here only to break ties when a caller points at a directory, and the
|
|
17
|
+
* resolver refuses rather than guesses when they do not.
|
|
18
|
+
*/
|
|
19
|
+
import type { Depth, Finding } from "./types.js";
|
|
20
|
+
/** What the caller pointed at. A union so "a path and inline content" is not representable. */
|
|
21
|
+
export type TargetInput = {
|
|
22
|
+
kind: "path";
|
|
23
|
+
path?: string;
|
|
24
|
+
cwd: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "content";
|
|
27
|
+
content: string;
|
|
28
|
+
dir?: string;
|
|
29
|
+
cwd: string;
|
|
30
|
+
};
|
|
31
|
+
export type TargetKind =
|
|
32
|
+
/** A recipe, named directly or found inside a directory. Every depth is reachable. */
|
|
33
|
+
"recipe"
|
|
34
|
+
/** A directory of scanner code with no recipe. Code can be checked; nothing can be executed. */
|
|
35
|
+
| "scanner-dir"
|
|
36
|
+
/** A single entry file. Same limits as a scanner directory. */
|
|
37
|
+
| "entrypoint"
|
|
38
|
+
/** Recipe content supplied inline. Reaches execution only when a directory is supplied too. */
|
|
39
|
+
| "inline";
|
|
40
|
+
export interface ResolvedTarget {
|
|
41
|
+
kind: TargetKind;
|
|
42
|
+
/** Absolute path to the recipe, when one was found. */
|
|
43
|
+
recipePath?: string;
|
|
44
|
+
/** Recipe content, when supplied inline instead of on disk. */
|
|
45
|
+
recipeContent?: string;
|
|
46
|
+
/** Absolute directory that relative paths inside the recipe resolve against. */
|
|
47
|
+
baseDir?: string;
|
|
48
|
+
/** Absolute scanner directory, for the code-only forms. */
|
|
49
|
+
scannerRoot?: string;
|
|
50
|
+
/** Entry filename within {@link scannerRoot}, for the single-file form. */
|
|
51
|
+
entrypoint?: string;
|
|
52
|
+
/** Depths this target can reach. */
|
|
53
|
+
allowedDepths: readonly Depth[];
|
|
54
|
+
}
|
|
55
|
+
export type TargetResolution = {
|
|
56
|
+
ok: true;
|
|
57
|
+
target: ResolvedTarget;
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
finding: Finding;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Resolve what to validate, or say why it could not be resolved.
|
|
64
|
+
*
|
|
65
|
+
* `requestedDepth` is checked against what the target can reach, so "a live run against a bare
|
|
66
|
+
* scanner directory" fails here — before a process is spawned — rather than deep inside a launch
|
|
67
|
+
* that was never going to be buildable.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveTarget(input: TargetInput, requestedDepth: Depth): TargetResolution;
|
|
70
|
+
//# sourceMappingURL=target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../src/validate/target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiBjD,+FAA+F;AAC/F,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,UAAU;AACpB,sFAAsF;AACpF,QAAQ;AACV,gGAAgG;GAC9F,aAAa;AACf,+DAA+D;GAC7D,YAAY;AACd,+FAA+F;GAC7F,QAAQ,CAAC;AAEb,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,aAAa,EAAE,SAAS,KAAK,EAAE,CAAC;CACjC;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AA6GpC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,GAAG,gBAAgB,CAQzF"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving what the caller pointed at.
|
|
3
|
+
*
|
|
4
|
+
* Runs before anything is parsed. Its whole job is to answer "what am I validating, and how deep
|
|
5
|
+
* can I go against it" — and, when it cannot, to say so in terms of what is actually on disk rather
|
|
6
|
+
* than what was expected to be.
|
|
7
|
+
*
|
|
8
|
+
* That last part is why these failures are worth care despite being the least interesting ones: they
|
|
9
|
+
* are the first thing an agent hits, and a refusal that cannot name the real alternatives turns into
|
|
10
|
+
* guessing. So the resolver reads the directory before it complains about it.
|
|
11
|
+
*
|
|
12
|
+
* ## Filenames are conventions, not contract
|
|
13
|
+
*
|
|
14
|
+
* Nothing in the runtime requires a recipe to be called `runtime.yaml`, or a scanner directory to be
|
|
15
|
+
* called `scanners/` — install accepts any path, and the scanner location is a field inside the
|
|
16
|
+
* recipe. Those names are used here only to break ties when a caller points at a directory, and the
|
|
17
|
+
* resolver refuses rather than guesses when they do not.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
20
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
21
|
+
import { VALIDATE_CODE } from "./codes.js";
|
|
22
|
+
/**
|
|
23
|
+
* The conventional recipe filename. Used only to break ties inside a directory; an explicitly
|
|
24
|
+
* named file is honoured whatever it is called.
|
|
25
|
+
*/
|
|
26
|
+
const CONVENTIONAL_RECIPE = "runtime.yaml";
|
|
27
|
+
/**
|
|
28
|
+
* The deploy manifest. It is a `.yaml` sitting beside recipes, so it must be excluded from
|
|
29
|
+
* recipe candidates or a package root would resolve to its own manifest. It belongs to the deploy
|
|
30
|
+
* layer and is outside what this validates.
|
|
31
|
+
*/
|
|
32
|
+
const DEPLOY_MANIFEST = "strategy.yaml";
|
|
33
|
+
const RECIPE_EXTENSIONS = [".yaml", ".yml"];
|
|
34
|
+
const ALL_DEPTHS = ["static", "import", "live"];
|
|
35
|
+
const CODE_ONLY = ["import"];
|
|
36
|
+
const CONFIG_ONLY = ["static"];
|
|
37
|
+
function invocationFinding(code, parts) {
|
|
38
|
+
return {
|
|
39
|
+
code,
|
|
40
|
+
severity: "error",
|
|
41
|
+
stage: "invocation",
|
|
42
|
+
actor: "agent",
|
|
43
|
+
fix_kind: code === VALIDATE_CODE.noRecipe || code === VALIDATE_CODE.ambiguousRecipe ? "choice" : "command",
|
|
44
|
+
...parts,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Directory entries that could be a recipe: a YAML file that is not the deploy manifest. */
|
|
48
|
+
function recipeCandidates(dir) {
|
|
49
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
50
|
+
.filter((e) => e.isFile())
|
|
51
|
+
.map((e) => e.name)
|
|
52
|
+
.filter((name) => name !== DEPLOY_MANIFEST)
|
|
53
|
+
.filter((name) => RECIPE_EXTENSIONS.some((ext) => name.endsWith(ext)))
|
|
54
|
+
.sort();
|
|
55
|
+
}
|
|
56
|
+
/** Subdirectories that themselves hold a conventionally-named recipe — a package's instances. */
|
|
57
|
+
function instanceDirs(dir) {
|
|
58
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
59
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith("."))
|
|
60
|
+
.map((e) => e.name)
|
|
61
|
+
.filter((name) => existsSync(join(dir, name, CONVENTIONAL_RECIPE)))
|
|
62
|
+
.sort();
|
|
63
|
+
}
|
|
64
|
+
function hasPythonFiles(dir) {
|
|
65
|
+
return readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile() && e.name.endsWith(".py"));
|
|
66
|
+
}
|
|
67
|
+
/** How the resolved target would be written on a command line, for use inside a computed fix. */
|
|
68
|
+
function asArg(cwd, absolute) {
|
|
69
|
+
const rel = relative(cwd, absolute);
|
|
70
|
+
if (rel === "")
|
|
71
|
+
return ".";
|
|
72
|
+
return rel.startsWith("..") || isAbsolute(rel) ? absolute : `./${rel}`;
|
|
73
|
+
}
|
|
74
|
+
function resolveDirectory(dir, cwd) {
|
|
75
|
+
const conventional = join(dir, CONVENTIONAL_RECIPE);
|
|
76
|
+
if (existsSync(conventional) && statSync(conventional).isFile()) {
|
|
77
|
+
return { ok: true, target: { kind: "recipe", recipePath: conventional, baseDir: dir, allowedDepths: ALL_DEPTHS } };
|
|
78
|
+
}
|
|
79
|
+
const candidates = recipeCandidates(dir);
|
|
80
|
+
if (candidates.length === 1) {
|
|
81
|
+
const only = join(dir, candidates[0]);
|
|
82
|
+
return { ok: true, target: { kind: "recipe", recipePath: only, baseDir: dir, allowedDepths: ALL_DEPTHS } };
|
|
83
|
+
}
|
|
84
|
+
if (candidates.length > 1) {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
finding: invocationFinding(VALIDATE_CODE.ambiguousRecipe, {
|
|
88
|
+
what: `${asArg(cwd, dir)} holds ${candidates.length} possible recipes and none is named ${CONVENTIONAL_RECIPE}`,
|
|
89
|
+
why: "Picking one would be a guess, and validating the wrong recipe is worse than refusing.",
|
|
90
|
+
fix: `Name the one you mean:\n${candidates.map((c) => ` senpi validate ${asArg(cwd, join(dir, c))}`).join("\n")}`,
|
|
91
|
+
where: { file: asArg(cwd, dir) },
|
|
92
|
+
evidence: { candidates },
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// No recipe here. A package root holding instance directories is the common case — an agent
|
|
97
|
+
// reaches for the package by name because that is how every other tool refers to it.
|
|
98
|
+
const instances = instanceDirs(dir);
|
|
99
|
+
if (instances.length > 0) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
finding: invocationFinding(VALIDATE_CODE.noRecipe, {
|
|
103
|
+
what: `${asArg(cwd, dir)} has no recipe of its own — it holds ${instances.length} instance${instances.length === 1 ? "" : "s"}`,
|
|
104
|
+
why: "Validation runs against one runtime. A package groups several, each with its own recipe and its own wallet.",
|
|
105
|
+
fix: `Pick an instance:\n${instances.map((i) => ` senpi validate ${asArg(cwd, join(dir, i))}`).join("\n")}`,
|
|
106
|
+
where: { file: asArg(cwd, dir) },
|
|
107
|
+
evidence: { instances },
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Scanner code with no recipe beside it. Legitimate mid-authoring: code exists before the
|
|
112
|
+
// recipe is finished, and the import checks need nothing else.
|
|
113
|
+
if (hasPythonFiles(dir)) {
|
|
114
|
+
return { ok: true, target: { kind: "scanner-dir", scannerRoot: dir, allowedDepths: CODE_ONLY } };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
finding: invocationFinding(VALIDATE_CODE.noRecipe, {
|
|
119
|
+
what: `${asArg(cwd, dir)} holds no recipe and no scanner code`,
|
|
120
|
+
why: "There is nothing here to validate.",
|
|
121
|
+
fix: `Point at a directory containing a recipe (${CONVENTIONAL_RECIPE}), or at a scanner directory to check its code alone.`,
|
|
122
|
+
where: { file: asArg(cwd, dir) },
|
|
123
|
+
evidence: { entries: readdirSync(dir).slice(0, 20) },
|
|
124
|
+
}),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Resolve what to validate, or say why it could not be resolved.
|
|
129
|
+
*
|
|
130
|
+
* `requestedDepth` is checked against what the target can reach, so "a live run against a bare
|
|
131
|
+
* scanner directory" fails here — before a process is spawned — rather than deep inside a launch
|
|
132
|
+
* that was never going to be buildable.
|
|
133
|
+
*/
|
|
134
|
+
export function resolveTarget(input, requestedDepth) {
|
|
135
|
+
const resolved = resolveTargetShape(input);
|
|
136
|
+
if (!resolved.ok)
|
|
137
|
+
return resolved;
|
|
138
|
+
const { target } = resolved;
|
|
139
|
+
if (target.allowedDepths.includes(requestedDepth))
|
|
140
|
+
return resolved;
|
|
141
|
+
return { ok: false, finding: depthUnreachable(target, requestedDepth, input) };
|
|
142
|
+
}
|
|
143
|
+
function resolveTargetShape(input) {
|
|
144
|
+
if (input.kind === "content") {
|
|
145
|
+
if (input.dir === undefined) {
|
|
146
|
+
return {
|
|
147
|
+
ok: true,
|
|
148
|
+
target: { kind: "inline", recipeContent: input.content, allowedDepths: CONFIG_ONLY },
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const baseDir = resolve(input.cwd, input.dir);
|
|
152
|
+
if (!existsSync(baseDir)) {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
finding: invocationFinding(VALIDATE_CODE.targetNotFound, {
|
|
156
|
+
what: `The directory ${input.dir} does not exist`,
|
|
157
|
+
why: "Relative scanner paths inside the recipe are resolved against it.",
|
|
158
|
+
fix: `Pass a directory that exists. Resolved from ${input.cwd} to ${baseDir}.`,
|
|
159
|
+
evidence: { requested: input.dir, resolved: baseDir, cwd: input.cwd },
|
|
160
|
+
}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
ok: true,
|
|
165
|
+
target: { kind: "inline", recipeContent: input.content, baseDir, allowedDepths: ALL_DEPTHS },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const path = resolve(input.cwd, input.path ?? ".");
|
|
169
|
+
if (!existsSync(path)) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
finding: invocationFinding(VALIDATE_CODE.targetNotFound, {
|
|
173
|
+
what: `${input.path ?? "."} does not exist`,
|
|
174
|
+
why: "Nothing can be validated until the target resolves to a file or directory.",
|
|
175
|
+
// Relative-path mistakes are the usual cause, so name what it resolved against.
|
|
176
|
+
fix: `Check the path. It was resolved from ${input.cwd} to ${path}.`,
|
|
177
|
+
evidence: { requested: input.path ?? ".", resolved: path, cwd: input.cwd },
|
|
178
|
+
}),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (statSync(path).isDirectory())
|
|
182
|
+
return resolveDirectory(path, input.cwd);
|
|
183
|
+
if (path.endsWith(".py")) {
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
target: {
|
|
187
|
+
kind: "entrypoint",
|
|
188
|
+
scannerRoot: dirname(path),
|
|
189
|
+
entrypoint: basename(path),
|
|
190
|
+
allowedDepths: CODE_ONLY,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
ok: true,
|
|
196
|
+
target: { kind: "recipe", recipePath: path, baseDir: dirname(path), allowedDepths: ALL_DEPTHS },
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The requested depth is not reachable against this target.
|
|
201
|
+
*
|
|
202
|
+
* Split by cause because the remedies are different, and because the reason a live run needs a
|
|
203
|
+
* recipe is worth stating: the inputs a scanner is called with, the account it runs against, and
|
|
204
|
+
* the schema its output is judged by all live in the recipe. Supplying defaults would let a scanner
|
|
205
|
+
* pass against invented inputs and fail on real ones — which is the failure this exists to catch.
|
|
206
|
+
*/
|
|
207
|
+
function depthUnreachable(target, requested, input) {
|
|
208
|
+
if (target.kind === "inline") {
|
|
209
|
+
return invocationFinding(VALIDATE_CODE.needsDir, {
|
|
210
|
+
what: `A ${requested} run needs a directory, and recipe content was supplied without one`,
|
|
211
|
+
why: "Relative scanner paths in the recipe have nothing to resolve against, so the scanner cannot be located.",
|
|
212
|
+
fix: "Re-run with --dir pointing at the directory the recipe belongs to.",
|
|
213
|
+
evidence: { requested_depth: requested, allowed_depths: target.allowedDepths },
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const what = target.kind === "entrypoint"
|
|
217
|
+
? `A ${requested} run needs a recipe, and the target is a single scanner file`
|
|
218
|
+
: `A ${requested} run needs a recipe, and the target is a scanner directory`;
|
|
219
|
+
return invocationFinding(VALIDATE_CODE.needsRecipe, {
|
|
220
|
+
what,
|
|
221
|
+
why: "Executing a scanner needs the inputs it is called with, the account it runs against, and the schema " +
|
|
222
|
+
"its output is judged by — all of which live in the recipe. Substituting defaults would let it pass " +
|
|
223
|
+
"against invented inputs and fail on real ones.",
|
|
224
|
+
fix: `Point at the recipe to run every check, or keep this target and use --stage import to check the code alone:\n` +
|
|
225
|
+
` senpi validate ${asArg(input.cwd, dirname(target.scannerRoot ?? "."))}\n` +
|
|
226
|
+
` senpi validate ${asArg(input.cwd, target.scannerRoot ?? ".")} --stage import`,
|
|
227
|
+
where: { file: asArg(input.cwd, target.scannerRoot ?? ".") },
|
|
228
|
+
evidence: { requested_depth: requested, allowed_depths: target.allowedDepths },
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target.js","sourceRoot":"","sources":["../../src/validate/target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What validate reports about itself — and, harder, what it must not.
|
|
3
|
+
*
|
|
4
|
+
* The goal is the harness-improvement loop: which codes fire in the field, at what depth runs
|
|
5
|
+
* stop, whether passes produce proofs, which verification reasons refuse deploys. That is
|
|
6
|
+
* answerable entirely with **closed-set dimensions** — and it has to be, because validate handles
|
|
7
|
+
* exactly the material telemetry must never carry: recipe config, scanner source, wallet
|
|
8
|
+
* addresses, file paths, exception text. So the rule is structural, not disciplinary: every
|
|
9
|
+
* attribute value is either drawn from a compile-time set or clamped to `"other"`. There is no
|
|
10
|
+
* code path by which free text reaches an attribute.
|
|
11
|
+
*
|
|
12
|
+
* Pure builders, exported for the redaction tests; the OTel instruments live in
|
|
13
|
+
* `telemetry/metrics.ts` with the rest of the fleet's and consume these unchanged.
|
|
14
|
+
*/
|
|
15
|
+
import type { Finding } from "./types.js";
|
|
16
|
+
import type { ValidateResult } from "./run.js";
|
|
17
|
+
import type { VerifyReason } from "./proof.js";
|
|
18
|
+
/** One run: how deep it went, how it ended, whether the pass became durable. */
|
|
19
|
+
export declare function runAttributes(result: ValidateResult): Record<string, string>;
|
|
20
|
+
/** One finding: the class and where it sits — never the message, never the location. */
|
|
21
|
+
export declare function findingAttributes(finding: Finding): Record<string, string>;
|
|
22
|
+
/** One verification: the outcome alone. Which package it was is nobody's business here. */
|
|
23
|
+
export declare function verificationAttributes(outcome: "ok" | VerifyReason): Record<string, string>;
|
|
24
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/validate/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAsB/C,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ5E;AAED,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ1E;AAED,2FAA2F;AAC3F,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI3F"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What validate reports about itself — and, harder, what it must not.
|
|
3
|
+
*
|
|
4
|
+
* The goal is the harness-improvement loop: which codes fire in the field, at what depth runs
|
|
5
|
+
* stop, whether passes produce proofs, which verification reasons refuse deploys. That is
|
|
6
|
+
* answerable entirely with **closed-set dimensions** — and it has to be, because validate handles
|
|
7
|
+
* exactly the material telemetry must never carry: recipe config, scanner source, wallet
|
|
8
|
+
* addresses, file paths, exception text. So the rule is structural, not disciplinary: every
|
|
9
|
+
* attribute value is either drawn from a compile-time set or clamped to `"other"`. There is no
|
|
10
|
+
* code path by which free text reaches an attribute.
|
|
11
|
+
*
|
|
12
|
+
* Pure builders, exported for the redaction tests; the OTel instruments live in
|
|
13
|
+
* `telemetry/metrics.ts` with the rest of the fleet's and consume these unchanged.
|
|
14
|
+
*/
|
|
15
|
+
import { ALL_VALIDATE_CODES } from "./codes.js";
|
|
16
|
+
const VERDICTS = new Set(["PASS", "FAIL", "UNPROVEN"]);
|
|
17
|
+
const DEPTHS = new Set(["static", "import", "live"]);
|
|
18
|
+
/**
|
|
19
|
+
* Mirrors the `TargetKind` union in `target.ts`. A test asserts it covers every member — a value
|
|
20
|
+
* missing here does not fail loudly, it silently clamps to `"other"`, so the dimension goes quietly
|
|
21
|
+
* useless for exactly the kinds nobody thought about.
|
|
22
|
+
*/
|
|
23
|
+
const TARGET_KINDS = new Set(["recipe", "scanner-dir", "entrypoint", "inline"]);
|
|
24
|
+
const SEVERITIES = new Set(["error", "warning"]);
|
|
25
|
+
const STAGES = new Set(["invocation", "static", "import", "live", "proof"]);
|
|
26
|
+
const ACTORS = new Set(["agent", "user", "ops"]);
|
|
27
|
+
const FIX_KINDS = new Set(["edit", "command", "choice", "lookup", "locate-only"]);
|
|
28
|
+
const KNOWN_CODES = new Set(ALL_VALIDATE_CODES);
|
|
29
|
+
const VERIFY_OUTCOMES = new Set(["ok", "no_proof", "content_changed", "runtime_version_changed"]);
|
|
30
|
+
/** The only route into an attribute value: in the set, or `"other"`. */
|
|
31
|
+
function clamp(value, set) {
|
|
32
|
+
return value !== undefined && set.has(value) ? value : "other";
|
|
33
|
+
}
|
|
34
|
+
/** One run: how deep it went, how it ended, whether the pass became durable. */
|
|
35
|
+
export function runAttributes(result) {
|
|
36
|
+
return {
|
|
37
|
+
"senpi.validate.verdict": clamp(result.verdict, VERDICTS),
|
|
38
|
+
"senpi.validate.deepest_stage": clamp(result.stages_run[result.stages_run.length - 1], DEPTHS),
|
|
39
|
+
"senpi.validate.target_kind": clamp(result.target.kind, TARGET_KINDS),
|
|
40
|
+
"senpi.validate.exit_code": String(result.exit_code),
|
|
41
|
+
"senpi.validate.proof_recorded": String(result.proof_recorded === true),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** One finding: the class and where it sits — never the message, never the location. */
|
|
45
|
+
export function findingAttributes(finding) {
|
|
46
|
+
return {
|
|
47
|
+
"senpi.validate.code": clamp(finding.code, KNOWN_CODES),
|
|
48
|
+
"senpi.validate.severity": clamp(finding.severity, SEVERITIES),
|
|
49
|
+
"senpi.validate.stage": clamp(finding.stage, STAGES),
|
|
50
|
+
"senpi.validate.actor": clamp(finding.actor, ACTORS),
|
|
51
|
+
"senpi.validate.fix_kind": clamp(finding.fix_kind, FIX_KINDS),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** One verification: the outcome alone. Which package it was is nobody's business here. */
|
|
55
|
+
export function verificationAttributes(outcome) {
|
|
56
|
+
return {
|
|
57
|
+
"senpi.validate.verify_outcome": clamp(outcome, VERIFY_OUTCOMES),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/validate/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKhD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AACvD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AACrD;;;;GAIG;AACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,CAAC,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;AACxF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AACjD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5E,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AACjD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;AAClF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,kBAAkB,CAAC,CAAC;AACxD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,yBAAyB,CAAC,CAAC,CAAC;AAElG,wEAAwE;AACxE,SAAS,KAAK,CAAC,KAAyB,EAAE,GAAgB;IACxD,OAAO,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;AACjE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,MAAsB;IAClD,OAAO;QACL,wBAAwB,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;QACzD,8BAA8B,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC;QAC9F,4BAA4B,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;QACrE,0BAA0B,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;QACpD,+BAA+B,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC;KACxE,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,OAAO;QACL,qBAAqB,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC;QACvD,yBAAyB,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC;QAC9D,sBAAsB,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;QACpD,sBAAsB,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;QACpD,yBAAyB,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC;KAC9D,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,sBAAsB,CAAC,OAA4B;IACjE,OAAO;QACL,+BAA+B,EAAE,KAAK,CAAC,OAAO,EAAE,eAAe,CAAC;KACjE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `senpi validate` finding model — the shape every check speaks.
|
|
3
|
+
*
|
|
4
|
+
* A finding is the unit of feedback. It is consumed by an agent operating in a self-fixing loop,
|
|
5
|
+
* so it carries four things a bare message does not: WHAT happened, WHY it matters in production,
|
|
6
|
+
* the FIX to apply, and the raw EVIDENCE behind it. It also carries who can act (`actor`) and what
|
|
7
|
+
* shape the action takes (`fix_kind`), so a caller can route or filter without parsing prose.
|
|
8
|
+
*
|
|
9
|
+
* Field naming is snake_case because a finding is a WIRE shape — it is serialized verbatim into
|
|
10
|
+
* `--json` output that agents parse. Matches `runtime-api/routes/signals.schema.ts`, which is
|
|
11
|
+
* snake_case for the same reason. Internal-only types in this module use camelCase as usual.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The check depth a finding came from. Also the grouping used to enable coverage assertions
|
|
15
|
+
* layer-by-layer while the feature is being built: a stage whose checks do not exist yet has no
|
|
16
|
+
* producing tests, so it is excluded from the coverage set until it lands.
|
|
17
|
+
*
|
|
18
|
+
* `invocation` precedes the depths proper — it covers "you pointed at the wrong thing", which is
|
|
19
|
+
* resolved before any recipe is read.
|
|
20
|
+
*/
|
|
21
|
+
export type ValidationStage = "invocation" | "static" | "import" | "live" | "proof";
|
|
22
|
+
/**
|
|
23
|
+
* How deep a run goes. Each depth includes the ones before it.
|
|
24
|
+
*
|
|
25
|
+
* A subset of {@link ValidationStage}: every depth is a stage a finding can come from, but
|
|
26
|
+
* `invocation` and `proof` are not depths — the first happens before a depth is chosen, the second
|
|
27
|
+
* after the run is over.
|
|
28
|
+
*
|
|
29
|
+
* The ordering is a dependency, not a preference: a recipe that will not parse cannot produce a
|
|
30
|
+
* launch configuration, and a module that will not import cannot be executed.
|
|
31
|
+
*/
|
|
32
|
+
export type Depth = "static" | "import" | "live";
|
|
33
|
+
/** Depths in the order they run, so a caller can expand a requested depth to everything it implies. */
|
|
34
|
+
export declare const DEPTH_ORDER: readonly Depth[];
|
|
35
|
+
/** Every depth up to and including `depth`. */
|
|
36
|
+
export declare function depthsUpTo(depth: Depth): Depth[];
|
|
37
|
+
/** Count findings by severity. Defined here so the verdict rule has one place to read from. */
|
|
38
|
+
export declare function severityOfFindings(findings: readonly {
|
|
39
|
+
severity: Severity;
|
|
40
|
+
}[]): {
|
|
41
|
+
errors: number;
|
|
42
|
+
warnings: number;
|
|
43
|
+
};
|
|
44
|
+
/** `error` fails the run; `warning` is reported and only fails under `--strict`. */
|
|
45
|
+
export type Severity = "error" | "warning";
|
|
46
|
+
/**
|
|
47
|
+
* Who can actually resolve this.
|
|
48
|
+
*
|
|
49
|
+
* Load-bearing for loop control, not documentation: an agent that keeps editing `scan.py` to fix
|
|
50
|
+
* absent credentials will loop until something else stops it. A finding whose actor is not `agent`
|
|
51
|
+
* forces the loop to stop rather than retry.
|
|
52
|
+
*/
|
|
53
|
+
export type Actor =
|
|
54
|
+
/** Fixable by changing the strategy package. */
|
|
55
|
+
"agent"
|
|
56
|
+
/** Needs a human decision or a value only the user has. */
|
|
57
|
+
| "user"
|
|
58
|
+
/** Environment, not the package — credentials, dependencies, disk. */
|
|
59
|
+
| "ops";
|
|
60
|
+
/**
|
|
61
|
+
* The shape of the remedy. Lets a caller check that a fix is actually actionable, and lets tests
|
|
62
|
+
* assert the right *kind* of guidance was produced rather than merely that some string exists.
|
|
63
|
+
*/
|
|
64
|
+
export type FixKind =
|
|
65
|
+
/** Change a specific file at a specific place. */
|
|
66
|
+
"edit"
|
|
67
|
+
/** Run a specific command. */
|
|
68
|
+
| "command"
|
|
69
|
+
/** Pick from options enumerated from what is really present. */
|
|
70
|
+
| "choice"
|
|
71
|
+
/** Read the correct value from an authoritative source. */
|
|
72
|
+
| "lookup"
|
|
73
|
+
/**
|
|
74
|
+
* The failure can be located but not authored — a genuine defect in strategy logic. Held to a
|
|
75
|
+
* weaker bar than the others: name the failing line exactly, do not pretend to know the fix.
|
|
76
|
+
*/
|
|
77
|
+
| "locate-only";
|
|
78
|
+
/** Where a finding points. All fields optional — an environment fault has no location. */
|
|
79
|
+
export interface FindingLocation {
|
|
80
|
+
/** Package-relative path, e.g. `scanners/universe.py`. Never absolute. */
|
|
81
|
+
file?: string;
|
|
82
|
+
/** 1-indexed. */
|
|
83
|
+
line?: number;
|
|
84
|
+
/** Dotted path into the recipe, e.g. `strategy.margin_pct` or `scanners.0.entrypoint`. */
|
|
85
|
+
yaml_path?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* One reported problem.
|
|
89
|
+
*
|
|
90
|
+
* `fix` MUST be computed against the actual state of this package, never rendered from a template.
|
|
91
|
+
* This is not stylistic: a templated next-step instruction once fired at a strategy it did not
|
|
92
|
+
* apply to, and following it destroyed a live position. If a fix cannot be computed, the code
|
|
93
|
+
* belongs to `fix_kind: "locate-only"` and should say where the failure is rather than guess.
|
|
94
|
+
*/
|
|
95
|
+
export interface Finding {
|
|
96
|
+
code: string;
|
|
97
|
+
severity: Severity;
|
|
98
|
+
stage: ValidationStage;
|
|
99
|
+
actor: Actor;
|
|
100
|
+
fix_kind: FixKind;
|
|
101
|
+
where?: FindingLocation;
|
|
102
|
+
/** The specific thing that happened, at the specific place. */
|
|
103
|
+
what: string;
|
|
104
|
+
/** The consequence in production — why this is worth acting on. */
|
|
105
|
+
why: string;
|
|
106
|
+
/** The concrete next action, derived from this package's real state. */
|
|
107
|
+
fix: string;
|
|
108
|
+
/**
|
|
109
|
+
* Raw material for whoever is fixing it: the full error chain, captured output, observed data
|
|
110
|
+
* shapes. Never summarized away. Bounded with an explicit truncation marker when large — see the
|
|
111
|
+
* evidence-bounding rule; unbounded capture is a memory fault, silent clipping is a lying one.
|
|
112
|
+
*/
|
|
113
|
+
evidence?: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/validate/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpF;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjD,uGAAuG;AACvG,eAAO,MAAM,WAAW,EAAE,SAAS,KAAK,EAA0C,CAAC;AAEnF,+CAA+C;AAC/C,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAEhD;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,EAAE,GAC1C;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAQtC;AAED,oFAAoF;AACpF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,MAAM,KAAK;AACf,gDAAgD;AAC9C,OAAO;AACT,2DAA2D;GACzD,MAAM;AACR,sEAAsE;GACpE,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,OAAO;AACjB,kDAAkD;AAChD,MAAM;AACR,8BAA8B;GAC5B,SAAS;AACX,gEAAgE;GAC9D,QAAQ;AACV,2DAA2D;GACzD,QAAQ;AACV;;;GAGG;GACD,aAAa,CAAC;AAElB,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `senpi validate` finding model — the shape every check speaks.
|
|
3
|
+
*
|
|
4
|
+
* A finding is the unit of feedback. It is consumed by an agent operating in a self-fixing loop,
|
|
5
|
+
* so it carries four things a bare message does not: WHAT happened, WHY it matters in production,
|
|
6
|
+
* the FIX to apply, and the raw EVIDENCE behind it. It also carries who can act (`actor`) and what
|
|
7
|
+
* shape the action takes (`fix_kind`), so a caller can route or filter without parsing prose.
|
|
8
|
+
*
|
|
9
|
+
* Field naming is snake_case because a finding is a WIRE shape — it is serialized verbatim into
|
|
10
|
+
* `--json` output that agents parse. Matches `runtime-api/routes/signals.schema.ts`, which is
|
|
11
|
+
* snake_case for the same reason. Internal-only types in this module use camelCase as usual.
|
|
12
|
+
*/
|
|
13
|
+
/** Depths in the order they run, so a caller can expand a requested depth to everything it implies. */
|
|
14
|
+
export const DEPTH_ORDER = ["static", "import", "live"];
|
|
15
|
+
/** Every depth up to and including `depth`. */
|
|
16
|
+
export function depthsUpTo(depth) {
|
|
17
|
+
return DEPTH_ORDER.slice(0, DEPTH_ORDER.indexOf(depth) + 1);
|
|
18
|
+
}
|
|
19
|
+
/** Count findings by severity. Defined here so the verdict rule has one place to read from. */
|
|
20
|
+
export function severityOfFindings(findings) {
|
|
21
|
+
let errors = 0;
|
|
22
|
+
let warnings = 0;
|
|
23
|
+
for (const f of findings) {
|
|
24
|
+
if (f.severity === "error")
|
|
25
|
+
errors += 1;
|
|
26
|
+
else
|
|
27
|
+
warnings += 1;
|
|
28
|
+
}
|
|
29
|
+
return { errors, warnings };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/validate/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwBH,uGAAuG;AACvG,MAAM,CAAC,MAAM,WAAW,GAAqB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAEnF,+CAA+C;AAC/C,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,kBAAkB,CAChC,QAA2C;IAE3C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,MAAM,IAAI,CAAC,CAAC;;YACnC,QAAQ,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which account a live tick runs against.
|
|
3
|
+
*
|
|
4
|
+
* Executing a scanner needs an account identity — it is handed to the exchange reads the scanner
|
|
5
|
+
* makes. But during authoring no account exists yet: the wallet is created when the strategy is
|
|
6
|
+
* funded, which is deliberately after validation. So one is resolved rather than required.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The stand-in used when no real account is available.
|
|
10
|
+
*
|
|
11
|
+
* Faithful rather than a workaround: an account with no history returns an empty book, which is
|
|
12
|
+
* exactly what a freshly funded strategy sees on its first tick. A scanner that breaks on an empty
|
|
13
|
+
* position list is broken, and would break identically in production — catching that is the point.
|
|
14
|
+
* The deploy preflight already validates against this same address for the same reason.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PLACEHOLDER_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
17
|
+
export type WalletSource =
|
|
18
|
+
/** Named on the command line. */
|
|
19
|
+
"explicit"
|
|
20
|
+
/** The recipe's own wallet, when it resolved to a real address. */
|
|
21
|
+
| "recipe"
|
|
22
|
+
/** A default configured for this machine. */
|
|
23
|
+
| "environment"
|
|
24
|
+
/** The stand-in. */
|
|
25
|
+
| "placeholder";
|
|
26
|
+
export interface ResolvedWallet {
|
|
27
|
+
address: string;
|
|
28
|
+
source: WalletSource;
|
|
29
|
+
/** What the account's state implies about what a passing run establishes. */
|
|
30
|
+
book: "real" | "empty";
|
|
31
|
+
}
|
|
32
|
+
export interface WalletInputs {
|
|
33
|
+
/** From `--wallet`. */
|
|
34
|
+
explicit?: string;
|
|
35
|
+
/** `strategy.wallet` after environment substitution — often still a placeholder. */
|
|
36
|
+
fromRecipe?: string;
|
|
37
|
+
/** A machine-level default. */
|
|
38
|
+
fromEnvironment?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve in order of specificity.
|
|
42
|
+
*
|
|
43
|
+
* The recipe's own wallet is preferred over any default because validating an edit to a deployed
|
|
44
|
+
* strategy against its real positions is more faithful than forcing a cold start. It is accepted
|
|
45
|
+
* only when it *looks* like an address: an unresolved placeholder is a non-empty string that the
|
|
46
|
+
* schema accepts happily, and passing that to the exchange would produce a confusing failure well
|
|
47
|
+
* away from its cause.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveWallet(inputs: WalletInputs): ResolvedWallet;
|
|
50
|
+
/** Whether a supplied address is well formed. Checked before anything runs. */
|
|
51
|
+
export declare function isWellFormedAddress(value: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* What running against this account does and does not establish.
|
|
54
|
+
*
|
|
55
|
+
* Rides the result so a caller cannot report more confidence than the run earned. Against an empty
|
|
56
|
+
* book, logic that depends on open positions never executes — which is correct for a strategy about
|
|
57
|
+
* to be funded, and worth saying out loud rather than leaving to inference.
|
|
58
|
+
*/
|
|
59
|
+
export declare function walletClaim(wallet: ResolvedWallet): {
|
|
60
|
+
proves: string;
|
|
61
|
+
doesNotProve: string[];
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=wallet.d.ts.map
|