@nanocollective/sentinel 0.1.0-alpha.0
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/LICENSE +22 -0
- package/README.md +66 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +254 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/parse.d.ts +13 -0
- package/dist/config/parse.d.ts.map +1 -0
- package/dist/config/parse.js +271 -0
- package/dist/config/parse.js.map +1 -0
- package/dist/config/repo-override.d.ts +13 -0
- package/dist/config/repo-override.d.ts.map +1 -0
- package/dist/config/repo-override.js +128 -0
- package/dist/config/repo-override.js.map +1 -0
- package/dist/config/types.d.ts +86 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +8 -0
- package/dist/config/types.js.map +1 -0
- package/dist/dedup/hash.d.ts +10 -0
- package/dist/dedup/hash.d.ts.map +1 -0
- package/dist/dedup/hash.js +18 -0
- package/dist/dedup/hash.js.map +1 -0
- package/dist/dedup/markers.d.ts +13 -0
- package/dist/dedup/markers.d.ts.map +1 -0
- package/dist/dedup/markers.js +42 -0
- package/dist/dedup/markers.js.map +1 -0
- package/dist/dedup/plan.d.ts +45 -0
- package/dist/dedup/plan.d.ts.map +1 -0
- package/dist/dedup/plan.js +83 -0
- package/dist/dedup/plan.js.map +1 -0
- package/dist/dedup/reconcile.d.ts +29 -0
- package/dist/dedup/reconcile.d.ts.map +1 -0
- package/dist/dedup/reconcile.js +73 -0
- package/dist/dedup/reconcile.js.map +1 -0
- package/dist/findings/types.d.ts +53 -0
- package/dist/findings/types.d.ts.map +1 -0
- package/dist/findings/types.js +28 -0
- package/dist/findings/types.js.map +1 -0
- package/dist/findings/validate.d.ts +36 -0
- package/dist/findings/validate.d.ts.map +1 -0
- package/dist/findings/validate.js +178 -0
- package/dist/findings/validate.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/init/args.d.ts +20 -0
- package/dist/init/args.d.ts.map +1 -0
- package/dist/init/args.js +80 -0
- package/dist/init/args.js.map +1 -0
- package/dist/init/plan.d.ts +9 -0
- package/dist/init/plan.d.ts.map +1 -0
- package/dist/init/plan.js +17 -0
- package/dist/init/plan.js.map +1 -0
- package/dist/init/scaffold.d.ts +17 -0
- package/dist/init/scaffold.d.ts.map +1 -0
- package/dist/init/scaffold.js +27 -0
- package/dist/init/scaffold.js.map +1 -0
- package/dist/init/templates.d.ts +28 -0
- package/dist/init/templates.d.ts.map +1 -0
- package/dist/init/templates.js +187 -0
- package/dist/init/templates.js.map +1 -0
- package/dist/init/types.d.ts +29 -0
- package/dist/init/types.d.ts.map +1 -0
- package/dist/init/types.js +15 -0
- package/dist/init/types.js.map +1 -0
- package/dist/issues/body.d.ts +12 -0
- package/dist/issues/body.d.ts.map +1 -0
- package/dist/issues/body.js +53 -0
- package/dist/issues/body.js.map +1 -0
- package/dist/issues/file.d.ts +24 -0
- package/dist/issues/file.d.ts.map +1 -0
- package/dist/issues/file.js +61 -0
- package/dist/issues/file.js.map +1 -0
- package/dist/issues/gh-client.d.ts +21 -0
- package/dist/issues/gh-client.d.ts.map +1 -0
- package/dist/issues/gh-client.js +133 -0
- package/dist/issues/gh-client.js.map +1 -0
- package/dist/issues/types.d.ts +85 -0
- package/dist/issues/types.d.ts.map +1 -0
- package/dist/issues/types.js +8 -0
- package/dist/issues/types.js.map +1 -0
- package/dist/orchestrator/audit.d.ts +14 -0
- package/dist/orchestrator/audit.d.ts.map +1 -0
- package/dist/orchestrator/audit.js +47 -0
- package/dist/orchestrator/audit.js.map +1 -0
- package/dist/orchestrator/auto-fix.d.ts +31 -0
- package/dist/orchestrator/auto-fix.d.ts.map +1 -0
- package/dist/orchestrator/auto-fix.js +59 -0
- package/dist/orchestrator/auto-fix.js.map +1 -0
- package/dist/orchestrator/extract.d.ts +14 -0
- package/dist/orchestrator/extract.d.ts.map +1 -0
- package/dist/orchestrator/extract.js +70 -0
- package/dist/orchestrator/extract.js.map +1 -0
- package/dist/orchestrator/nanocoder-runner.d.ts +33 -0
- package/dist/orchestrator/nanocoder-runner.d.ts.map +1 -0
- package/dist/orchestrator/nanocoder-runner.js +125 -0
- package/dist/orchestrator/nanocoder-runner.js.map +1 -0
- package/dist/orchestrator/types.d.ts +51 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +8 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/prompt/build.d.ts +14 -0
- package/dist/prompt/build.d.ts.map +1 -0
- package/dist/prompt/build.js +123 -0
- package/dist/prompt/build.js.map +1 -0
- package/dist/prompt/types.d.ts +34 -0
- package/dist/prompt/types.d.ts.map +1 -0
- package/dist/prompt/types.js +8 -0
- package/dist/prompt/types.js.map +1 -0
- package/dist/rule-packs/dependencies.d.ts +20 -0
- package/dist/rule-packs/dependencies.d.ts.map +1 -0
- package/dist/rule-packs/dependencies.js +58 -0
- package/dist/rule-packs/dependencies.js.map +1 -0
- package/dist/rule-packs/glob.d.ts +23 -0
- package/dist/rule-packs/glob.d.ts.map +1 -0
- package/dist/rule-packs/glob.js +66 -0
- package/dist/rule-packs/glob.js.map +1 -0
- package/dist/rule-packs/parse.d.ts +21 -0
- package/dist/rule-packs/parse.d.ts.map +1 -0
- package/dist/rule-packs/parse.js +230 -0
- package/dist/rule-packs/parse.js.map +1 -0
- package/dist/rule-packs/types.d.ts +55 -0
- package/dist/rule-packs/types.d.ts.map +1 -0
- package/dist/rule-packs/types.js +10 -0
- package/dist/rule-packs/types.js.map +1 -0
- package/dist/run/audit.d.ts +20 -0
- package/dist/run/audit.d.ts.map +1 -0
- package/dist/run/audit.js +28 -0
- package/dist/run/audit.js.map +1 -0
- package/dist/run/clone.d.ts +23 -0
- package/dist/run/clone.d.ts.map +1 -0
- package/dist/run/clone.js +51 -0
- package/dist/run/clone.js.map +1 -0
- package/dist/run/preview.d.ts +34 -0
- package/dist/run/preview.d.ts.map +1 -0
- package/dist/run/preview.js +68 -0
- package/dist/run/preview.js.map +1 -0
- package/dist/run/report.d.ts +10 -0
- package/dist/run/report.d.ts.map +1 -0
- package/dist/run/report.js +73 -0
- package/dist/run/report.js.map +1 -0
- package/dist/run/run.d.ts +74 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +131 -0
- package/dist/run/run.js.map +1 -0
- package/dist/run/select.d.ts +14 -0
- package/dist/run/select.d.ts.map +1 -0
- package/dist/run/select.js +31 -0
- package/dist/run/select.js.map +1 -0
- package/dist/run/sources.d.ts +11 -0
- package/dist/run/sources.d.ts.map +1 -0
- package/dist/run/sources.js +106 -0
- package/dist/run/sources.js.map +1 -0
- package/dist/run/types.d.ts +55 -0
- package/dist/run/types.d.ts.map +1 -0
- package/dist/run/types.js +7 -0
- package/dist/run/types.js.map +1 -0
- package/dist/suppression/apply.d.ts +33 -0
- package/dist/suppression/apply.d.ts.map +1 -0
- package/dist/suppression/apply.js +55 -0
- package/dist/suppression/apply.js.map +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue filing types. Like the model runner, the GitHub side is an injectable
|
|
3
|
+
* interface so the filing logic and the issue-body builder stay unit-testable
|
|
4
|
+
* without hitting the API. The real client (gh-client.ts) is the only part that
|
|
5
|
+
* shells out.
|
|
6
|
+
*/
|
|
7
|
+
import type { Finding } from '../findings/types.js';
|
|
8
|
+
/** The rendered content of one issue, before it is filed. */
|
|
9
|
+
export interface IssueContent {
|
|
10
|
+
title: string;
|
|
11
|
+
body: string;
|
|
12
|
+
labels: string[];
|
|
13
|
+
assignees: string[];
|
|
14
|
+
}
|
|
15
|
+
/** Parameters for creating one issue on a specific repository. */
|
|
16
|
+
export interface CreateIssueParams extends IssueContent {
|
|
17
|
+
/** The `owner/name` repository to file on. */
|
|
18
|
+
repo: string;
|
|
19
|
+
}
|
|
20
|
+
/** A created issue, as returned by the client. */
|
|
21
|
+
export interface CreatedIssue {
|
|
22
|
+
number: number;
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
/** The GitHub operations issue filing needs. */
|
|
26
|
+
export interface GitHubClient {
|
|
27
|
+
createIssue(params: CreateIssueParams): Promise<CreatedIssue>;
|
|
28
|
+
}
|
|
29
|
+
/** An issue that already exists on the target repo, as read back for dedup. */
|
|
30
|
+
export interface ExistingIssue {
|
|
31
|
+
number: number;
|
|
32
|
+
url: string;
|
|
33
|
+
state: 'open' | 'closed';
|
|
34
|
+
labels: string[];
|
|
35
|
+
body: string;
|
|
36
|
+
}
|
|
37
|
+
/** Reading and mutating existing issues, needed by the dedup reconcile step. */
|
|
38
|
+
export interface IssueQueryClient {
|
|
39
|
+
listIssues(params: {
|
|
40
|
+
repo: string;
|
|
41
|
+
label: string;
|
|
42
|
+
}): Promise<ExistingIssue[]>;
|
|
43
|
+
updateIssue(params: {
|
|
44
|
+
repo: string;
|
|
45
|
+
number: number;
|
|
46
|
+
body: string;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
closeIssue(params: {
|
|
49
|
+
repo: string;
|
|
50
|
+
number: number;
|
|
51
|
+
reason?: string;
|
|
52
|
+
comment?: string;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
/** A client that can both file and reconcile issues. */
|
|
56
|
+
export type ReconcileClient = GitHubClient & IssueQueryClient;
|
|
57
|
+
/** Context threaded into body building and routing. */
|
|
58
|
+
export interface FilingContext {
|
|
59
|
+
/** The `owner/name` repository being audited. */
|
|
60
|
+
auditedRepo: string;
|
|
61
|
+
/** The config repo `owner/name`, for the footer link and aggregate routing. */
|
|
62
|
+
configRepo?: string;
|
|
63
|
+
/** The version of the rule pack that produced the finding, for the body. */
|
|
64
|
+
packVersion?: string;
|
|
65
|
+
}
|
|
66
|
+
/** A finding paired with the issue it was filed as. */
|
|
67
|
+
export interface FiledIssue {
|
|
68
|
+
finding: Finding;
|
|
69
|
+
issue: CreatedIssue;
|
|
70
|
+
}
|
|
71
|
+
/** A finding whose filing threw. */
|
|
72
|
+
export interface FilingError {
|
|
73
|
+
finding: Finding;
|
|
74
|
+
error: string;
|
|
75
|
+
}
|
|
76
|
+
/** The outcome of filing a batch of findings. */
|
|
77
|
+
export interface FilingResult {
|
|
78
|
+
/** The repository issues were filed on (audited or config repo). */
|
|
79
|
+
targetRepo: string;
|
|
80
|
+
filed: FiledIssue[];
|
|
81
|
+
/** Findings below the severity threshold; not filed. */
|
|
82
|
+
skipped: Finding[];
|
|
83
|
+
errors: FilingError[];
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/issues/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,sBAAsB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACtD,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;CACb;AAED,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC5B,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC9D;AAED,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAChC,UAAU,CAAC,MAAM,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,MAAM,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAED,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAE9D,uDAAuD;AACvD,MAAM,WAAW,aAAa;IAC7B,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,uDAAuD;AACvD,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,YAAY,CAAC;CACpB;AAED,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC5B,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,EAAE,WAAW,EAAE,CAAC;CACtB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue filing types. Like the model runner, the GitHub side is an injectable
|
|
3
|
+
* interface so the filing logic and the issue-body builder stay unit-testable
|
|
4
|
+
* without hitting the API. The real client (gh-client.ts) is the only part that
|
|
5
|
+
* shells out.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../source/issues/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run one audit pass — one rule pack against one repository — by invoking a
|
|
3
|
+
* {@link ModelRunner}, extracting the findings array from its output, and
|
|
4
|
+
* validating it. The result carries either validated findings or the structured
|
|
5
|
+
* errors the auto-fix loop (task #5) will feed back to the model.
|
|
6
|
+
*/
|
|
7
|
+
import type { ModelConfig } from '../config/types.js';
|
|
8
|
+
import type { AuditResult, ModelRunner, RunnerOptions } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Invoke the model on the prompt and return validated findings, or the reason
|
|
11
|
+
* the pass did not produce them.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runAudit(prompt: string, model: ModelConfig, runner: ModelRunner, options?: RunnerOptions): Promise<AuditResult>;
|
|
14
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../source/orchestrator/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAGpD,OAAO,KAAK,EAAC,WAAW,EAAE,WAAW,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AAExE;;;GAGG;AACH,wBAAsB,QAAQ,CAC7B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,WAAW,CAAC,CAoCtB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run one audit pass — one rule pack against one repository — by invoking a
|
|
3
|
+
* {@link ModelRunner}, extracting the findings array from its output, and
|
|
4
|
+
* validating it. The result carries either validated findings or the structured
|
|
5
|
+
* errors the auto-fix loop (task #5) will feed back to the model.
|
|
6
|
+
*/
|
|
7
|
+
import { validateFindings } from '../findings/validate.js';
|
|
8
|
+
import { extractJsonArray } from './extract.js';
|
|
9
|
+
/**
|
|
10
|
+
* Invoke the model on the prompt and return validated findings, or the reason
|
|
11
|
+
* the pass did not produce them.
|
|
12
|
+
*/
|
|
13
|
+
export async function runAudit(prompt, model, runner, options) {
|
|
14
|
+
const run = await runner.run(prompt, model, options);
|
|
15
|
+
if (!run.ok) {
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
findings: [],
|
|
19
|
+
errors: [],
|
|
20
|
+
raw: run.output,
|
|
21
|
+
runError: run.error ?? 'model run failed',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const extracted = extractJsonArray(run.output);
|
|
25
|
+
if (extracted === null) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
findings: [],
|
|
29
|
+
errors: [
|
|
30
|
+
{
|
|
31
|
+
index: -1,
|
|
32
|
+
field: 'document',
|
|
33
|
+
message: 'no JSON array of findings was found in the model output',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
raw: run.output,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const result = validateFindings(extracted);
|
|
40
|
+
return {
|
|
41
|
+
ok: result.valid,
|
|
42
|
+
findings: result.findings,
|
|
43
|
+
errors: result.errors,
|
|
44
|
+
raw: run.output,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../source/orchestrator/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAG9C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC7B,MAAc,EACd,KAAkB,EAClB,MAAmB,EACnB,OAAuB;IAEvB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAErD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACb,OAAO;YACN,EAAE,EAAE,KAAK;YACT,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE,EAAE;YACV,GAAG,EAAE,GAAG,CAAC,MAAM;YACf,QAAQ,EAAE,GAAG,CAAC,KAAK,IAAI,kBAAkB;SACzC,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE;gBACP;oBACC,KAAK,EAAE,CAAC,CAAC;oBACT,KAAK,EAAE,UAAU;oBACjB,OAAO,EAAE,yDAAyD;iBAClE;aACD;YACD,GAAG,EAAE,GAAG,CAAC,MAAM;SACf,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC3C,OAAO;QACN,EAAE,EAAE,MAAM,CAAC,KAAK;QAChB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,GAAG,CAAC,MAAM;KACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auto-fix loop. When an audit pass produces output that fails validation,
|
|
3
|
+
* re-run the model with the structured error report appended, up to a bounded
|
|
4
|
+
* number of attempts (see docs/workflow/index.md#validation). Only validation
|
|
5
|
+
* failures are retried — a process-level failure (nanocoder missing, timeout)
|
|
6
|
+
* will not be fixed by feeding back an error report, so the loop stops there.
|
|
7
|
+
*/
|
|
8
|
+
import type { ModelConfig } from '../config/types.js';
|
|
9
|
+
import type { AuditResult, ModelRunner, RunnerOptions } from './types.js';
|
|
10
|
+
/** Options for the auto-fix loop. */
|
|
11
|
+
export interface AutoFixOptions extends RunnerOptions {
|
|
12
|
+
/** Total attempts, including the first. Clamped to at least 1. Default 2. */
|
|
13
|
+
maxAttempts?: number;
|
|
14
|
+
}
|
|
15
|
+
/** An audit result annotated with how many attempts it took. */
|
|
16
|
+
export interface AutoFixResult extends AuditResult {
|
|
17
|
+
/** Number of model runs performed (1 = succeeded or failed on first try). */
|
|
18
|
+
attempts: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the follow-up prompt: the original audit prompt plus a correction
|
|
22
|
+
* section listing every validation problem and, when available, the malformed
|
|
23
|
+
* array the model returned.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildAutoFixPrompt(originalPrompt: string, previous: AuditResult): string;
|
|
26
|
+
/**
|
|
27
|
+
* Run an audit pass with the auto-fix retry loop. Returns the first valid
|
|
28
|
+
* result, or the last failed result once attempts are exhausted.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runAuditWithAutoFix(prompt: string, model: ModelConfig, runner: ModelRunner, options?: AutoFixOptions): Promise<AutoFixResult>;
|
|
31
|
+
//# sourceMappingURL=auto-fix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-fix.d.ts","sourceRoot":"","sources":["../../source/orchestrator/auto-fix.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAIpD,OAAO,KAAK,EAAC,WAAW,EAAE,WAAW,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AAExE,qCAAqC;AACrC,MAAM,WAAW,cAAe,SAAQ,aAAa;IACpD,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;CACjB;AAcD;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,WAAW,GACnB,MAAM,CAuBR;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,aAAa,CAAC,CAaxB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auto-fix loop. When an audit pass produces output that fails validation,
|
|
3
|
+
* re-run the model with the structured error report appended, up to a bounded
|
|
4
|
+
* number of attempts (see docs/workflow/index.md#validation). Only validation
|
|
5
|
+
* failures are retried — a process-level failure (nanocoder missing, timeout)
|
|
6
|
+
* will not be fixed by feeding back an error report, so the loop stops there.
|
|
7
|
+
*/
|
|
8
|
+
import { runAudit } from './audit.js';
|
|
9
|
+
import { extractJsonArray } from './extract.js';
|
|
10
|
+
function formatErrors(errors) {
|
|
11
|
+
return errors
|
|
12
|
+
.map(error => {
|
|
13
|
+
const where = error.index >= 0
|
|
14
|
+
? `finding[${error.index}].${error.field}`
|
|
15
|
+
: error.field;
|
|
16
|
+
return `- ${where}: ${error.message}`;
|
|
17
|
+
})
|
|
18
|
+
.join('\n');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the follow-up prompt: the original audit prompt plus a correction
|
|
22
|
+
* section listing every validation problem and, when available, the malformed
|
|
23
|
+
* array the model returned.
|
|
24
|
+
*/
|
|
25
|
+
export function buildAutoFixPrompt(originalPrompt, previous) {
|
|
26
|
+
const parts = [
|
|
27
|
+
originalPrompt,
|
|
28
|
+
'',
|
|
29
|
+
'---',
|
|
30
|
+
'',
|
|
31
|
+
'## Correction required',
|
|
32
|
+
'',
|
|
33
|
+
'Your previous response did not produce a valid findings array. Fix these problems:',
|
|
34
|
+
'',
|
|
35
|
+
formatErrors(previous.errors),
|
|
36
|
+
];
|
|
37
|
+
const previousArray = extractJsonArray(previous.raw);
|
|
38
|
+
if (previousArray !== null) {
|
|
39
|
+
parts.push('', 'Your previous JSON array was:', previousArray);
|
|
40
|
+
}
|
|
41
|
+
parts.push('', 'Return ONLY a corrected JSON array that resolves every problem above.');
|
|
42
|
+
return parts.join('\n');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run an audit pass with the auto-fix retry loop. Returns the first valid
|
|
46
|
+
* result, or the last failed result once attempts are exhausted.
|
|
47
|
+
*/
|
|
48
|
+
export async function runAuditWithAutoFix(prompt, model, runner, options = {}) {
|
|
49
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 2);
|
|
50
|
+
let result = await runAudit(prompt, model, runner, options);
|
|
51
|
+
let attempts = 1;
|
|
52
|
+
while (!result.ok && !result.runError && attempts < maxAttempts) {
|
|
53
|
+
const fixPrompt = buildAutoFixPrompt(prompt, result);
|
|
54
|
+
result = await runAudit(fixPrompt, model, runner, options);
|
|
55
|
+
attempts++;
|
|
56
|
+
}
|
|
57
|
+
return { ...result, attempts };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=auto-fix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-fix.js","sourceRoot":"","sources":["../../source/orchestrator/auto-fix.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,OAAO,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAe9C,SAAS,YAAY,CAAC,MAAyB;IAC9C,OAAO,MAAM;SACX,GAAG,CAAC,KAAK,CAAC,EAAE;QACZ,MAAM,KAAK,GACV,KAAK,CAAC,KAAK,IAAI,CAAC;YACf,CAAC,CAAC,WAAW,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE;YAC1C,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;QAChB,OAAO,KAAK,KAAK,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IACvC,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CACjC,cAAsB,EACtB,QAAqB;IAErB,MAAM,KAAK,GAAG;QACb,cAAc;QACd,EAAE;QACF,KAAK;QACL,EAAE;QACF,wBAAwB;QACxB,EAAE;QACF,oFAAoF;QACpF,EAAE;QACF,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;KAC7B,CAAC;IAEF,MAAM,aAAa,GAAG,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,+BAA+B,EAAE,aAAa,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,IAAI,CACT,EAAE,EACF,uEAAuE,CACvE,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,MAAc,EACd,KAAkB,EAClB,MAAmB,EACnB,UAA0B,EAAE;IAE5B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAE1D,IAAI,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5D,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,QAAQ,GAAG,WAAW,EAAE,CAAC;QACjE,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3D,QAAQ,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,EAAC,GAAG,MAAM,EAAE,QAAQ,EAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull the findings JSON array out of a model's raw output. Even when the
|
|
3
|
+
* prompt asks for "only a JSON array", a Nanocoder run emits a full agent
|
|
4
|
+
* transcript, so the array is usually surrounded by reasoning and may be
|
|
5
|
+
* wrapped in a code fence. This scans for balanced, top-level `[...]` spans
|
|
6
|
+
* (ignoring brackets inside strings) and returns the last one that parses as
|
|
7
|
+
* an array — the model's final answer.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Return the last top-level JSON array in the output that parses to an array,
|
|
11
|
+
* as a string, or null if there is none.
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractJsonArray(output: string): string | null;
|
|
14
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../source/orchestrator/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6CH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa9D"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull the findings JSON array out of a model's raw output. Even when the
|
|
3
|
+
* prompt asks for "only a JSON array", a Nanocoder run emits a full agent
|
|
4
|
+
* transcript, so the array is usually surrounded by reasoning and may be
|
|
5
|
+
* wrapped in a code fence. This scans for balanced, top-level `[...]` spans
|
|
6
|
+
* (ignoring brackets inside strings) and returns the last one that parses as
|
|
7
|
+
* an array — the model's final answer.
|
|
8
|
+
*/
|
|
9
|
+
/** Collect every balanced, top-level `[...]` substring, in order. */
|
|
10
|
+
function topLevelArraySpans(text) {
|
|
11
|
+
const spans = [];
|
|
12
|
+
let depth = 0;
|
|
13
|
+
let start = -1;
|
|
14
|
+
let inString = false;
|
|
15
|
+
let escaped = false;
|
|
16
|
+
for (let i = 0; i < text.length; i++) {
|
|
17
|
+
const char = text[i];
|
|
18
|
+
if (inString) {
|
|
19
|
+
if (escaped) {
|
|
20
|
+
escaped = false;
|
|
21
|
+
}
|
|
22
|
+
else if (char === '\\') {
|
|
23
|
+
escaped = true;
|
|
24
|
+
}
|
|
25
|
+
else if (char === '"') {
|
|
26
|
+
inString = false;
|
|
27
|
+
}
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (char === '"') {
|
|
31
|
+
inString = true;
|
|
32
|
+
}
|
|
33
|
+
else if (char === '[') {
|
|
34
|
+
if (depth === 0) {
|
|
35
|
+
start = i;
|
|
36
|
+
}
|
|
37
|
+
depth++;
|
|
38
|
+
}
|
|
39
|
+
else if (char === ']') {
|
|
40
|
+
if (depth > 0) {
|
|
41
|
+
depth--;
|
|
42
|
+
if (depth === 0 && start !== -1) {
|
|
43
|
+
spans.push(text.slice(start, i + 1));
|
|
44
|
+
start = -1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return spans;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Return the last top-level JSON array in the output that parses to an array,
|
|
53
|
+
* as a string, or null if there is none.
|
|
54
|
+
*/
|
|
55
|
+
export function extractJsonArray(output) {
|
|
56
|
+
const spans = topLevelArraySpans(output);
|
|
57
|
+
for (let i = spans.length - 1; i >= 0; i--) {
|
|
58
|
+
const span = spans[i];
|
|
59
|
+
try {
|
|
60
|
+
if (Array.isArray(JSON.parse(span))) {
|
|
61
|
+
return span;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Not valid JSON on its own; try the next candidate.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../source/orchestrator/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qEAAqE;AACrE,SAAS,kBAAkB,CAAC,IAAY;IACvC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;IACf,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAErB,IAAI,QAAQ,EAAE,CAAC;YACd,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,KAAK,CAAC;YACjB,CAAC;iBAAM,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAC1B,OAAO,GAAG,IAAI,CAAC;YAChB,CAAC;iBAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACzB,QAAQ,GAAG,KAAK,CAAC;YAClB,CAAC;YACD,SAAS;QACV,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,QAAQ,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACzB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBACjB,KAAK,GAAG,CAAC,CAAC;YACX,CAAC;YACD,KAAK,EAAE,CAAC;QACT,CAAC;aAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACzB,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACf,KAAK,EAAE,CAAC;gBACR,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBACrC,KAAK,GAAG,CAAC,CAAC,CAAC;gBACZ,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC9C,MAAM,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;QAChC,IAAI,CAAC;YACJ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACrC,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,qDAAqD;QACtD,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The real {@link ModelRunner}: spawns Nanocoder non-interactively and captures
|
|
3
|
+
* its output. Matches the collective's invocation
|
|
4
|
+
* (`nanocoder run "<prompt>" --mode yolo --model <model> --trust-directory`);
|
|
5
|
+
* Nanocoder auto-enables its Ink-free `--plain` runtime off a TTY, so a single
|
|
6
|
+
* direct spawn works locally and on GitHub Actions runners.
|
|
7
|
+
*
|
|
8
|
+
* Unlike ContentForest, Sentinel captures stdout (the agent transcript) so the
|
|
9
|
+
* findings array can be extracted, rather than inheriting stdio.
|
|
10
|
+
*/
|
|
11
|
+
import type { ModelConfig } from '../config/types.js';
|
|
12
|
+
import type { ModelRunner, ModelRunResult, RunnerOptions } from './types.js';
|
|
13
|
+
/** Resolve the model id to pass to Nanocoder, honouring the fallback flag. */
|
|
14
|
+
export declare function resolveModelId(model: ModelConfig, useFallback: boolean): string;
|
|
15
|
+
/**
|
|
16
|
+
* Build the Nanocoder argv for a run. Pure and tested; the spawn itself is not.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildNanocoderArgs(prompt: string, model: ModelConfig, options?: RunnerOptions): string[];
|
|
19
|
+
/**
|
|
20
|
+
* Parse Nanocoder's `--json` report from stdout into a ModelRunResult. The
|
|
21
|
+
* report is `{kind, exitCode, finalText, ...}`; on a non-success kind the
|
|
22
|
+
* message is surfaced as the error. Non-JSON stdout is handed through as-is so
|
|
23
|
+
* the extractor can still try.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseNanocoderReport(stdout: string): ModelRunResult;
|
|
26
|
+
/**
|
|
27
|
+
* The environment for the Nanocoder spawn. When a config dir is given, point
|
|
28
|
+
* Nanocoder at it via NANOCODER_CONFIG_DIR so it loads the config repo's
|
|
29
|
+
* `agents.config.json` regardless of the audited-repo working directory.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildNanocoderEnv(base: NodeJS.ProcessEnv, configDir?: string): NodeJS.ProcessEnv;
|
|
32
|
+
export declare const nanocoderRunner: ModelRunner;
|
|
33
|
+
//# sourceMappingURL=nanocoder-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nanocoder-runner.d.ts","sourceRoot":"","sources":["../../source/orchestrator/nanocoder-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAC,WAAW,EAAE,cAAc,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AAI3E,8EAA8E;AAC9E,wBAAgB,cAAc,CAC7B,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,OAAO,GAClB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,aAAkB,GACzB,MAAM,EAAE,CAaV;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CA4BnE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,CAAC,UAAU,EACvB,SAAS,CAAC,EAAE,MAAM,GAChB,MAAM,CAAC,UAAU,CAKnB;AAGD,eAAO,MAAM,eAAe,EAAE,WAyD7B,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The real {@link ModelRunner}: spawns Nanocoder non-interactively and captures
|
|
3
|
+
* its output. Matches the collective's invocation
|
|
4
|
+
* (`nanocoder run "<prompt>" --mode yolo --model <model> --trust-directory`);
|
|
5
|
+
* Nanocoder auto-enables its Ink-free `--plain` runtime off a TTY, so a single
|
|
6
|
+
* direct spawn works locally and on GitHub Actions runners.
|
|
7
|
+
*
|
|
8
|
+
* Unlike ContentForest, Sentinel captures stdout (the agent transcript) so the
|
|
9
|
+
* findings array can be extracted, rather than inheriting stdio.
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 600_000;
|
|
13
|
+
/** Resolve the model id to pass to Nanocoder, honouring the fallback flag. */
|
|
14
|
+
export function resolveModelId(model, useFallback) {
|
|
15
|
+
if (useFallback && model.fallback) {
|
|
16
|
+
return model.fallback.model;
|
|
17
|
+
}
|
|
18
|
+
return model.model;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the Nanocoder argv for a run. Pure and tested; the spawn itself is not.
|
|
22
|
+
*/
|
|
23
|
+
export function buildNanocoderArgs(prompt, model, options = {}) {
|
|
24
|
+
return [
|
|
25
|
+
'run',
|
|
26
|
+
prompt,
|
|
27
|
+
'--mode',
|
|
28
|
+
'yolo',
|
|
29
|
+
'--model',
|
|
30
|
+
resolveModelId(model, options.useFallback ?? false),
|
|
31
|
+
'--trust-directory',
|
|
32
|
+
// Emit one complete JSON report to stdout instead of a streamed,
|
|
33
|
+
// last-token-lossy human transcript.
|
|
34
|
+
'--json',
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parse Nanocoder's `--json` report from stdout into a ModelRunResult. The
|
|
39
|
+
* report is `{kind, exitCode, finalText, ...}`; on a non-success kind the
|
|
40
|
+
* message is surfaced as the error. Non-JSON stdout is handed through as-is so
|
|
41
|
+
* the extractor can still try.
|
|
42
|
+
*/
|
|
43
|
+
export function parseNanocoderReport(stdout) {
|
|
44
|
+
let report;
|
|
45
|
+
try {
|
|
46
|
+
report = JSON.parse(stdout);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { ok: true, output: stdout };
|
|
50
|
+
}
|
|
51
|
+
if (typeof report !== 'object' || report === null) {
|
|
52
|
+
return { ok: true, output: stdout };
|
|
53
|
+
}
|
|
54
|
+
const { kind, finalText, message } = report;
|
|
55
|
+
const output = typeof finalText === 'string' ? finalText : '';
|
|
56
|
+
if (kind === 'success') {
|
|
57
|
+
return { ok: true, output };
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
output,
|
|
62
|
+
error: typeof message === 'string'
|
|
63
|
+
? message
|
|
64
|
+
: `nanocoder: ${kind ?? 'unknown result'}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The environment for the Nanocoder spawn. When a config dir is given, point
|
|
69
|
+
* Nanocoder at it via NANOCODER_CONFIG_DIR so it loads the config repo's
|
|
70
|
+
* `agents.config.json` regardless of the audited-repo working directory.
|
|
71
|
+
*/
|
|
72
|
+
export function buildNanocoderEnv(base, configDir) {
|
|
73
|
+
if (!configDir) {
|
|
74
|
+
return base;
|
|
75
|
+
}
|
|
76
|
+
return { ...base, NANOCODER_CONFIG_DIR: configDir };
|
|
77
|
+
}
|
|
78
|
+
/* c8 ignore start -- spawns a real process; not exercised in unit tests. */
|
|
79
|
+
export const nanocoderRunner = {
|
|
80
|
+
async run(prompt, model, options = {}) {
|
|
81
|
+
const result = spawnSync('nanocoder', buildNanocoderArgs(prompt, model, options), {
|
|
82
|
+
cwd: options.cwd,
|
|
83
|
+
encoding: 'utf8',
|
|
84
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
85
|
+
timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
86
|
+
env: buildNanocoderEnv(process.env, options.configDir),
|
|
87
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
88
|
+
});
|
|
89
|
+
if (result.error) {
|
|
90
|
+
const code = result.error.code;
|
|
91
|
+
if (code === 'ENOENT') {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
output: '',
|
|
95
|
+
error: '`nanocoder` is not on PATH. Install it (npm i -g @nanocollective/nanocoder).',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (code === 'ETIMEDOUT') {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
output: result.stdout ?? '',
|
|
102
|
+
error: 'nanocoder timed out',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
output: result.stdout ?? '',
|
|
108
|
+
error: String(result.error),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const stdout = result.stdout ?? '';
|
|
112
|
+
if (stdout.trim().length === 0) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
output: '',
|
|
116
|
+
error: `nanocoder produced no output (status ${result.status}): ${result.stderr ?? ''}`.trim(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// nanocoder may exit non-zero for an error kind but still emit the JSON
|
|
120
|
+
// report, so parse regardless of status.
|
|
121
|
+
return parseNanocoderReport(stdout);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
/* c8 ignore stop */
|
|
125
|
+
//# sourceMappingURL=nanocoder-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nanocoder-runner.js","sourceRoot":"","sources":["../../source/orchestrator/nanocoder-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestration types. The audit logic depends on a {@link ModelRunner}
|
|
3
|
+
* interface rather than spawning a process directly, so the parse/validate
|
|
4
|
+
* pipeline stays unit-testable with a fake runner. The real runner
|
|
5
|
+
* (nanocoder-runner.ts) is the only part that touches a child process.
|
|
6
|
+
*/
|
|
7
|
+
import type { ModelConfig } from '../config/types.js';
|
|
8
|
+
import type { Finding } from '../findings/types.js';
|
|
9
|
+
import type { ValidationError } from '../findings/validate.js';
|
|
10
|
+
/** Per-invocation runner options. */
|
|
11
|
+
export interface RunnerOptions {
|
|
12
|
+
/** Working directory for the run (the cloned repo). */
|
|
13
|
+
cwd?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Directory holding nanocoder's `agents.config.json` (the Sentinel config
|
|
16
|
+
* repo). Passed to nanocoder as NANOCODER_CONFIG_DIR so provider/model
|
|
17
|
+
* wiring lives in the config repo, the way ContentForest keeps it in its own.
|
|
18
|
+
*/
|
|
19
|
+
configDir?: string;
|
|
20
|
+
/** Hard timeout for the model process, in milliseconds. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** Use the configured cloud fallback model instead of the primary. */
|
|
23
|
+
useFallback?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** The raw outcome of invoking a model. */
|
|
26
|
+
export interface ModelRunResult {
|
|
27
|
+
/** True when the process ran and exited cleanly. */
|
|
28
|
+
ok: boolean;
|
|
29
|
+
/** The model's raw text output (a full agent transcript, typically). */
|
|
30
|
+
output: string;
|
|
31
|
+
/** A process-level error (spawn failure, non-zero exit, timeout). */
|
|
32
|
+
error?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Runs a prompt against a model and returns its raw output. */
|
|
35
|
+
export interface ModelRunner {
|
|
36
|
+
run(prompt: string, model: ModelConfig, options?: RunnerOptions): Promise<ModelRunResult>;
|
|
37
|
+
}
|
|
38
|
+
/** The outcome of one audit pass: one rule pack against one repository. */
|
|
39
|
+
export interface AuditResult {
|
|
40
|
+
/** True when the runner succeeded and the findings validated. */
|
|
41
|
+
ok: boolean;
|
|
42
|
+
/** The validated findings, in the camelCase model. Empty on failure. */
|
|
43
|
+
findings: Finding[];
|
|
44
|
+
/** Validation errors, for the auto-fix loop. Empty when ok. */
|
|
45
|
+
errors: ValidationError[];
|
|
46
|
+
/** The model's raw output, retained for logging and dry-run previews. */
|
|
47
|
+
raw: string;
|
|
48
|
+
/** Set when the model process itself failed (no output to validate). */
|
|
49
|
+
runError?: string;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/orchestrator/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,yBAAyB,CAAC;AAE7D,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC7B,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC9B,oDAAoD;IACpD,EAAE,EAAE,OAAO,CAAC;IACZ,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC3B,GAAG,CACF,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC,CAAC;CAC3B;AAED,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC3B,iEAAiE;IACjE,EAAE,EAAE,OAAO,CAAC;IACZ,wEAAwE;IACxE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,+DAA+D;IAC/D,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestration types. The audit logic depends on a {@link ModelRunner}
|
|
3
|
+
* interface rather than spawning a process directly, so the parse/validate
|
|
4
|
+
* pipeline stays unit-testable with a fake runner. The real runner
|
|
5
|
+
* (nanocoder-runner.ts) is the only part that touches a child process.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../source/orchestrator/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assemble the templated audit prompt for one rule pack against one repository.
|
|
3
|
+
* The pack's Markdown body is the audit instructions; the source files (scoped
|
|
4
|
+
* by the pack's applies_to globs) are the material; and a fixed reporting
|
|
5
|
+
* contract tells the model to emit findings in the snake_case shape the
|
|
6
|
+
* {@link ../findings/validate.js validator} accepts.
|
|
7
|
+
*/
|
|
8
|
+
import type { PromptInput, PromptResult } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Build an audit prompt. Files not matching the pack's applies_to scope are
|
|
11
|
+
* excluded and reported back in {@link PromptResult.skippedFiles}.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildAuditPrompt(input: PromptInput): PromptResult;
|
|
14
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../source/prompt/build.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAC,WAAW,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAqDtE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA2EjE"}
|