@engineeros/connector 0.4.6 → 0.5.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/package.json +1 -1
- package/src/runner.mjs +127 -31
package/package.json
CHANGED
package/src/runner.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { spawn } from "node:child_process";
|
|
3
2
|
import {
|
|
4
3
|
lstat,
|
|
@@ -106,21 +105,117 @@ export async function executeAssignment(assignment, config, callbacks) {
|
|
|
106
105
|
const changedFiles = await changedFilePaths(runWorkspace);
|
|
107
106
|
if (!changedFiles.length)
|
|
108
107
|
throw new Error("Codex completed without changing any files.");
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
const committed = await commitRunChange(
|
|
109
|
+
runWorkspace,
|
|
110
|
+
assignment.base_revision,
|
|
111
|
+
assignment.run_id,
|
|
112
|
+
);
|
|
113
|
+
const verifier = launchCodexProcess(
|
|
114
|
+
runWorkspace,
|
|
115
|
+
verificationPrompt(assignment, committed.revision, committed.changedFiles),
|
|
116
|
+
"read-only",
|
|
117
|
+
callbacks,
|
|
118
|
+
);
|
|
119
|
+
callbacks.onProcess?.(verifier.child);
|
|
120
|
+
const verification = await verifier.completed;
|
|
121
|
+
const proofEvidence = parseVerificationReport(
|
|
122
|
+
verification.finalMessage,
|
|
123
|
+
assignment.proof_checks,
|
|
124
|
+
config.connector_id,
|
|
125
|
+
assignment.run_id,
|
|
126
|
+
);
|
|
112
127
|
return {
|
|
113
|
-
head_revision: revision,
|
|
128
|
+
head_revision: committed.revision,
|
|
114
129
|
repository_locator: assignment.repository_locator,
|
|
115
130
|
external_reference: `connector:${config.connector_id}/run:${assignment.run_id}`,
|
|
116
|
-
diff_patch: diffPatch,
|
|
117
|
-
changed_files: changedFiles,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
131
|
+
diff_patch: committed.diffPatch,
|
|
132
|
+
changed_files: committed.changedFiles,
|
|
133
|
+
proof_evidence: proofEvidence,
|
|
134
|
+
verification_report: verification.finalMessage,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function commitRunChange(workspace, baseRevision, runId) {
|
|
139
|
+
await run("git", ["add", "-A"], workspace);
|
|
140
|
+
await run(
|
|
141
|
+
"git",
|
|
142
|
+
[
|
|
143
|
+
"-c",
|
|
144
|
+
"user.name=EngineerOS Codex",
|
|
145
|
+
"-c",
|
|
146
|
+
"user.email=codex@engineeros.local",
|
|
147
|
+
"commit",
|
|
148
|
+
"-m",
|
|
149
|
+
`EngineerOS Goal Run ${runId}`,
|
|
150
|
+
],
|
|
151
|
+
workspace,
|
|
152
|
+
);
|
|
153
|
+
const head = await run("git", ["rev-parse", "HEAD"], workspace);
|
|
154
|
+
const revision = head.stdout.trim();
|
|
155
|
+
const diff = await run(
|
|
156
|
+
"git",
|
|
157
|
+
["diff", "--binary", baseRevision, revision, "--"],
|
|
158
|
+
workspace,
|
|
159
|
+
);
|
|
160
|
+
const paths = await run(
|
|
161
|
+
"git",
|
|
162
|
+
["diff", "--name-only", "-z", baseRevision, revision, "--"],
|
|
163
|
+
workspace,
|
|
164
|
+
);
|
|
165
|
+
return {
|
|
166
|
+
revision,
|
|
167
|
+
diffPatch: diff.stdout,
|
|
168
|
+
changedFiles: gitPathList(paths.stdout).sort(),
|
|
121
169
|
};
|
|
122
170
|
}
|
|
123
171
|
|
|
172
|
+
export function verificationPrompt(assignment, revision, changedFiles) {
|
|
173
|
+
const checks = (assignment.proof_checks ?? [])
|
|
174
|
+
.map(
|
|
175
|
+
(proof, index) =>
|
|
176
|
+
`## Proof ${index + 1}\n- Check: ${proof.check ?? ""}\n- Expected: ${proof.expected ?? ""}`,
|
|
177
|
+
)
|
|
178
|
+
.join("\n\n");
|
|
179
|
+
return `# EngineerOS Connected Verification
|
|
180
|
+
|
|
181
|
+
Independently verify the frozen Goal at Git commit \`${revision}\`. Do not modify files. Run the commands or inspections needed for every Proof item, and check the Goal boundaries and constraints in the supplied packet.
|
|
182
|
+
|
|
183
|
+
Changed files:
|
|
184
|
+
${changedFiles.map((item) => `- \`${item}\``).join("\n")}
|
|
185
|
+
|
|
186
|
+
${checks}
|
|
187
|
+
|
|
188
|
+
Return structured Markdown only, with exactly one section per Proof:
|
|
189
|
+
|
|
190
|
+
## Proof 1
|
|
191
|
+
- Status: passed or failed
|
|
192
|
+
- Exit code: integer or none
|
|
193
|
+
- Evidence: concise observed output and command
|
|
194
|
+
|
|
195
|
+
Do not claim a Proof passed unless you observed it directly.`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function parseVerificationReport(report, proofChecks, connectorId, runId) {
|
|
199
|
+
if (!report?.trim()) throw new Error("Codex returned no connected verification report.");
|
|
200
|
+
return (proofChecks ?? []).map((_, index) => {
|
|
201
|
+
const start = new RegExp(`^## Proof ${index + 1}\\s*$`, "im").exec(report);
|
|
202
|
+
const tail = start ? report.slice(start.index + start[0].length) : "";
|
|
203
|
+
const section = tail.split(/^## Proof \d+\s*$/im)[0] ?? "";
|
|
204
|
+
const status = /^- Status:\s*(passed|failed)\s*$/im.exec(section)?.[1] ?? "failed";
|
|
205
|
+
const exitValue = /^- Exit code:\s*(\d+|none)\s*$/im.exec(section)?.[1] ?? "none";
|
|
206
|
+
const evidence = /^- Evidence:\s*(.+)$/im.exec(section)?.[1]?.trim();
|
|
207
|
+
return {
|
|
208
|
+
proof_index: index,
|
|
209
|
+
status,
|
|
210
|
+
verifier_type: "agent_reported",
|
|
211
|
+
verifier_identity: "Connected Codex CLI verifier",
|
|
212
|
+
locator: `connector:${connectorId}/run:${runId}#proof-${index + 1}`,
|
|
213
|
+
output_excerpt: evidence || "The connected verifier did not provide evidence for this Proof.",
|
|
214
|
+
exit_code: exitValue === "none" ? null : Number(exitValue),
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
124
219
|
export async function executeWorkspaceAssessment(
|
|
125
220
|
assignment,
|
|
126
221
|
config,
|
|
@@ -585,20 +680,17 @@ function runCodexCommand(command, args, cwd) {
|
|
|
585
680
|
async function changedFilePaths(workspace) {
|
|
586
681
|
const tracked = await run(
|
|
587
682
|
"git",
|
|
588
|
-
["diff", "--name-only", "HEAD", "--"],
|
|
683
|
+
["diff", "--name-only", "-z", "HEAD", "--"],
|
|
589
684
|
workspace,
|
|
590
685
|
);
|
|
591
686
|
const untracked = await run(
|
|
592
687
|
"git",
|
|
593
|
-
["ls-files", "--others", "--exclude-standard"],
|
|
688
|
+
["ls-files", "-z", "--others", "--exclude-standard"],
|
|
594
689
|
workspace,
|
|
595
690
|
);
|
|
596
691
|
return [
|
|
597
692
|
...new Set(
|
|
598
|
-
|
|
599
|
-
.split(/\r?\n/)
|
|
600
|
-
.map(normalizePath)
|
|
601
|
-
.filter(Boolean),
|
|
693
|
+
[...gitPathList(tracked.stdout), ...gitPathList(untracked.stdout)],
|
|
602
694
|
),
|
|
603
695
|
].sort();
|
|
604
696
|
}
|
|
@@ -610,16 +702,15 @@ async function boundedDiff(workspace, changedFiles) {
|
|
|
610
702
|
workspace,
|
|
611
703
|
);
|
|
612
704
|
const untracked = new Set(
|
|
613
|
-
(
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
.
|
|
621
|
-
|
|
622
|
-
.filter(Boolean),
|
|
705
|
+
gitPathList(
|
|
706
|
+
(
|
|
707
|
+
await run(
|
|
708
|
+
"git",
|
|
709
|
+
["ls-files", "-z", "--others", "--exclude-standard"],
|
|
710
|
+
workspace,
|
|
711
|
+
)
|
|
712
|
+
).stdout,
|
|
713
|
+
),
|
|
623
714
|
);
|
|
624
715
|
const parts = [tracked.stdout];
|
|
625
716
|
for (const relative of changedFiles.filter((item) => untracked.has(item))) {
|
|
@@ -638,15 +729,13 @@ async function boundedDiff(workspace, changedFiles) {
|
|
|
638
729
|
return diff;
|
|
639
730
|
}
|
|
640
731
|
|
|
641
|
-
async function repositoryArchive(workspace) {
|
|
732
|
+
export async function repositoryArchive(workspace) {
|
|
642
733
|
const listed = await run(
|
|
643
734
|
"git",
|
|
644
|
-
["ls-files", "-co", "--exclude-standard"],
|
|
735
|
+
["ls-files", "-z", "-co", "--exclude-standard"],
|
|
645
736
|
workspace,
|
|
646
737
|
);
|
|
647
|
-
const files = [
|
|
648
|
-
...new Set(listed.stdout.split(/\r?\n/).map(normalizePath).filter(Boolean)),
|
|
649
|
-
].sort();
|
|
738
|
+
const files = [...new Set(gitPathList(listed.stdout))].sort();
|
|
650
739
|
return createZip(
|
|
651
740
|
await Promise.all(
|
|
652
741
|
files.map(async (relative) => ({
|
|
@@ -801,6 +890,13 @@ function normalizePath(value) {
|
|
|
801
890
|
.replace(/^\.\//, "");
|
|
802
891
|
}
|
|
803
892
|
|
|
893
|
+
function gitPathList(value) {
|
|
894
|
+
return String(value ?? "")
|
|
895
|
+
.split("\0")
|
|
896
|
+
.map(normalizePath)
|
|
897
|
+
.filter(Boolean);
|
|
898
|
+
}
|
|
899
|
+
|
|
804
900
|
function run(command, args, cwd, options = {}) {
|
|
805
901
|
return new Promise((resolve, reject) => {
|
|
806
902
|
const child = spawn(command, args, {
|