Skip to content

Commit 876010e

Browse files
authored
Merge pull request #50 from commandlayer/codex/fix-/execute-request-normalization
Normalize /execute request bodies before verb dispatch
2 parents ccd1a29 + 131a6aa commit 876010e

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

runtime/tests/runtime-signing.test.mjs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,37 @@ test("POST /execute dispatches execution.verb to clean and keeps canonical commo
252252
}
253253
});
254254

255+
test("POST /execute normalizes nested execution into the handler body and preserves execution metadata", async () => {
256+
const keys = makeKeys();
257+
const srv = await startServer({
258+
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64: keys.privatePemB64,
259+
RECEIPT_SIGNING_PUBLIC_KEY_B64: keys.publicRaw32B64,
260+
RECEIPT_SIGNER_ID: "runtime.commandlayer.eth",
261+
});
262+
263+
try {
264+
const resp = await fetch(`${srv.base}/execute`, {
265+
method: "POST",
266+
headers: { "content-type": "application/json" },
267+
body: JSON.stringify({
268+
execution: { verb: "describe", version: "1.1.0", class: "commons" },
269+
input: { subject: "Normalize me" },
270+
}),
271+
});
272+
const json = await resp.json();
273+
const { receipt } = unwrapReceiptResponse(json);
274+
275+
assert.equal(resp.status, 200);
276+
assert.equal(receipt.verb, "describe");
277+
assert.equal(receipt.version, "1.1.0");
278+
assert.equal(receipt.class, "commons");
279+
assert.equal(typeof receipt.result.description, "string");
280+
assert.ok(receipt.result.description.length > 0);
281+
} finally {
282+
await stop(srv.proc);
283+
}
284+
});
285+
255286
test("POST /execute falls back to top-level verb for summarize", async () => {
256287
const keys = makeKeys();
257288
const srv = await startServer({

server.mjs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,7 @@ async function handleVerb(verb, req, res) {
15401540
};
15411541

15421542
try {
1543-
const execution = normalizeExecutionEnvelope(req.body?.execution, verb);
1543+
const execution = normalizeExecutionEnvelope(req.body?.execution ?? req.body, verb);
15441544
warmValidatorForVerb(execution.verb);
15451545

15461546
const callerTimeout = Number(req.body?.limits?.timeout_ms || req.body?.limits?.max_latency_ms || 0);
@@ -1566,7 +1566,7 @@ async function handleVerb(verb, req, res) {
15661566
}
15671567

15681568
} catch (e) {
1569-
const execution = normalizeExecutionEnvelope(req.body?.execution, verb);
1569+
const execution = normalizeExecutionEnvelope(req.body?.execution ?? req.body, verb);
15701570
warmValidatorForVerb(execution.verb);
15711571

15721572
const actor = req.body?.actor
@@ -2025,10 +2025,20 @@ app.post("/verify", async (req, res) => {
20252025
// verb routes
20262026
// -----------------------
20272027
app.post("/execute", (req, res) => {
2028-
const resolvedVerb = String(req.body?.execution?.verb || req.body?.verb || "").trim();
2028+
const body = req.body && typeof req.body === "object" ? req.body : {};
2029+
const execution = body.execution && typeof body.execution === "object" ? body.execution : body;
2030+
const resolvedVerb = String(execution.verb || body.verb || "").trim();
2031+
20292032
if (!resolvedVerb) {
20302033
return res.status(400).json({ ok: false, error: "missing_verb", message: "execution.verb or verb is required", ...instancePayload() });
20312034
}
2035+
2036+
req.body = {
2037+
...body,
2038+
...execution,
2039+
verb: resolvedVerb,
2040+
};
2041+
20322042
return handleVerb(resolvedVerb, req, res);
20332043
});
20342044

0 commit comments

Comments
 (0)