app/worker/src/index.ts
zzmq.dev@gmail.com/zz / app / worker / src / index.ts
@ 15050aba
Download
Ladder Summary
- file The module implements a Cloudflare‑Worker test harness that (1) seeds a fresh Git repository in an in‑memory filesystem and pushes it to an Artifacts repo (`seedRepo`), and (2) runs the complete “zz” protocol end‑to‑end—proposing a lane, posting and claiming work and review asks, granting the review, and submitting a changeset—by invoking methods on a real `MaintainerDO` durable object (`testFullProtocol`). It also re‑exports the `MaintainerDO` class so that other modules can instantiate or reference the durable object. **Exported identifiers** - `MaintainerDO` (class) (the only symbol exported from this file)
// Proof-of-mechanism Worker for the Cloudflare backend
// (site/content/remote-backend/index.md). /full-protocol-test is the
// real target: the actual agent/tools.ts tool surface, through a real
// MaintainerDO, doing the real protocol end to end (propose -> work ->
// changeset_review -> submit_changeset -> commit -> Ladder write ->
// read back) against a real Artifacts repo. The other routes are the
// smaller, lower-level proofs built incrementally to get there, kept
// as regression checks for the mechanisms underneath (native reads,
// isomorphic-git writes, the Ladder's scoped notes checkout).
import * as git from "isomorphic-git";
import http from "isomorphic-git/http/web";
import { createMemFs } from "./memFs.js";
import { MaintainerDO } from "./MaintainerDO.js";
import { commitChangesetOnArtifacts, readFileAtCommit } from "./cloudflareGitBackend.js";
import { writeRung, readRung, appendRepoChildren } from "./cloudflareLadderBackend.js";
import { ensureMeta, readRepoMeta, readRepoRoles, writeRepoRoles } from "./repoMeta.js";
import { handleWebRequest } from "./webApp.js";
import { decryptSecret, type OAuthEnv } from "./auth.js";
export { MaintainerDO };
// Artifacts/ArtifactsRepo/ArtifactsCreateRepoResult are ambient globals
// from @cloudflare/workers-types (tsconfig's "types" pulls them in) --
// no hand-rolled copy needed, and no import either; a hand-rolled copy
// used to live here (and drifted from the real shape -- see
// cloudflareGitBackend.ts's header for the same fix made there).
interface Env extends OAuthEnv {
ARTIFACTS: Artifacts;
MAINTAINER_DO: DurableObjectNamespace<MaintainerDO>;
DB: D1Database;
}
function tokenString(token: { plaintext: string } | string): string {
return typeof token === "string" ? token.split("?")[0] : token.plaintext;
}
async function seedRepo(env: Env, repoName: string, files: { path: string; content: string }[]): Promise<{ remote: string; commitOid: string }> {
const created = await env.ARTIFACTS.create(repoName, { setDefaultBranch: "main" });
const fs = createMemFs();
const dir = "/seed";
await git.init({ fs, dir, defaultBranch: "main" });
const entries = [];
for (const f of files) {
const oid = await git.writeBlob({ fs, dir, blob: new TextEncoder().encode(f.content) });
entries.push({ mode: "100644", path: f.path, oid, type: "blob" as const });
}
const tree = await git.writeTree({ fs, dir, tree: entries });
const person = { name: "seed", email: "seed@local", timestamp: Math.floor(Date.now() / 1000), timezoneOffset: 0 };
const commitOid = await git.writeCommit({ fs, dir, commit: { message: "seed", tree, parent: [], author: person, committer: person } });
await git.writeRef({ fs, dir, ref: "refs/heads/main", value: commitOid, force: true });
await git.push({ fs, http, dir, url: created.remote, onAuth: () => ({ username: "x-token-auth", password: tokenString(created.token) }), ref: "main", remoteRef: "main" });
return { remote: created.remote, commitOid };
}
// The real tool surface, through a real DO, doing the real protocol
// end to end -- the thing all the other routes were building toward.
// Skips run_in_sandbox (can't run on a Worker, by design) and composes
// file content directly instead, same as the rest of this test's
// seeding does; everything else is the actual zz protocol, unmodified.
async function testFullProtocol(env: Env): Promise<Response> {
const repoName = "full-protocol-" + Date.now();
const steps: Record<string, unknown> = {};
const actor = "zz-worker-test";
try {
const { commitOid: seedCommitOid } = await seedRepo(env, repoName, [{ path: "clamp.js", content: "export function clamp(v,min,max){return Math.min(Math.max(v,min),max);}\n" }]);
steps.seeded = seedCommitOid;
const id = env.MAINTAINER_DO.idFromName(repoName);
const stub = env.MAINTAINER_DO.get(id);
await stub.configure(repoName);
const call = (name: string, args: Record<string, unknown>) => stub.callTool(name, args);
const lane = (await call("lane_propose", { repo: repoName, name: "full-protocol-test", objective: "prove the real tool surface end to end", scope: ["**"], actor })) as { id: string };
steps.lane = lane;
const workAsk = (await call("ask_post", { repo: repoName, lane_id: lane.id, kind: "work", from: "human", payload: { description: "add isOdd to index.js" } })) as { id: string };
await call("ask_claim", { repo: repoName, lane_id: lane.id, ask_id: workAsk.id, actor });
const proposedFiles = [
{ path: "clamp.js", content: "export function clamp(v,min,max){return Math.min(Math.max(v,min),max);}\n" },
{ path: "index.js", content: "export { clamp } from \"./clamp.js\";\n\nexport function isOdd(n) { return n % 2 !== 0; }\n" },
];
// The Maintainer-role gate from earlier this session: a review has
// to exist and be granted before submit_changeset is even
// claimable (enforced in engine/lane.ts's answer(), not just
// hoped for) -- so post and grant it first, same order
// agent/scriptedWork.ts's real flow uses.
const review = (await call("ask_post", { repo: repoName, lane_id: lane.id, kind: "changeset_review", from: actor, payload: { taskDescription: "add isOdd", files: proposedFiles, message: "add isOdd", summary: "Add isOdd(n)" } })) as { id: string };
await call("ask_claim", { repo: repoName, lane_id: lane.id, ask_id: review.id, actor });
await call("ask_answer", { repo: repoName, lane_id: lane.id, ask_id: review.id, actor, action: "grant" });
steps.reviewGranted = true;
const submit = (await call("ask_post", {
repo: repoName,
lane_id: lane.id,
kind: "submit_changeset",
from: actor,
payload: { files: proposedFiles, message: "add isOdd", summary: "Add isOdd(n)" },
depends_on: [workAsk.id, review.id],
})) as { id: string };
await call("ask_answer", { repo: repoName, lane_id: lane.id, ask_id: workAsk.id, actor, action: "complete", payload: { summary: "proposed" } });
await call("ask_claim", { repo: repoName, lane_id: lane.id, ask_id: submit.id, actor });
const commitResult = (await call("commit_changeset", { repo: repoName, files: proposedFiles, message: "add isOdd", author_name: actor, author_email: `${actor}@local` })) as { commitOid: string };
steps.commitOid = commitResult.commitOid;
const now = new Date().toISOString();
const source = [{ kind: "commit", ref: commitResult.commitOid, title: "add isOdd", author: actor, at: now }];
// appendRepoChildren (called by mergeLaneLadder after every
// non-root rung write) requires the "_repo" root note to already
// exist -- demo/greenfield.ts's own bootstrap writes this once,
// first, before anything else; this test has to do the same.
await call("ladder_write_rung", { repo: repoName, rung: { path: "_repo", kind: "repo", summary: "A test repo for the full protocol.", sources: source, children: [] } });
steps.rootRungWritten = true;
await call("ladder_write_rung", { repo: repoName, rung: { path: "index.js", kind: "file", summary: "Re-exports clamp and adds isOdd.", sources: source, children: [] } });
steps.fileRungWritten = true;
await call("ladder_write_rung", {
repo: repoName,
rung: { path: `changesets/${commitResult.commitOid}`, kind: "changeset", summary: "Add isOdd.", sources: source, children: [{ path: `changesets/${commitResult.commitOid}/index.js`, kind: "filechange", summary: "Added isOdd.", sources: source, children: [] }] },
});
steps.changesetRungWritten = true;
await call("ask_answer", { repo: repoName, lane_id: lane.id, ask_id: submit.id, actor, action: "complete", payload: { commitOid: commitResult.commitOid } });
steps.answered = true;
steps.readIndexRung = await call("ladder_read", { repo: repoName, path: "index.js" });
steps.readLane = await call("lane_get", { repo: repoName, lane_id: lane.id });
steps.readFileAtCommit = await call("git_show_file", { repo: repoName, sha: commitResult.commitOid, path: "index.js" });
await env.ARTIFACTS.delete(repoName);
steps.cleanedUp = true;
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
async function testChangesetCommit(env: Env): Promise<Response> {
const repoName = "changeset-proof-" + Date.now();
const steps: Record<string, unknown> = {};
try {
const { remote, commitOid: seedCommitOid } = await seedRepo(env, repoName, [
{ path: "index.js", content: "export { clamp } from \"./clamp.js\";\n" },
{ path: "clamp.js", content: "export function clamp(v,min,max){return Math.min(Math.max(v,min),max);}\n" },
]);
steps.seeded = seedCommitOid;
using repo = await env.ARTIFACTS.get(repoName);
const newToken = await repo.createToken("write", 3600);
const commitOid = await commitChangesetOnArtifacts(
repoName,
remote,
newToken,
repo,
{ files: [{ path: "index.js", content: "export { clamp } from \"./clamp.js\";\n\nexport function isOdd(n) { return n % 2 !== 0; }\n" }], message: "add isOdd", author: { name: "zz-worker", email: "zz-worker@local" } },
);
steps.changesetCommit = commitOid;
steps.indexJsAfter = await readFileAtCommit(repo, commitOid, "index.js");
steps.clampJsStillThere = await readFileAtCommit(repo, commitOid, "clamp.js");
await env.ARTIFACTS.delete(repoName);
steps.cleanedUp = true;
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
async function testLadder(env: Env): Promise<Response> {
const repoName = "ladder-proof-" + Date.now();
const steps: Record<string, unknown> = {};
try {
const { remote, commitOid: seedCommitOid } = await seedRepo(env, repoName, [{ path: "clamp.js", content: "export function clamp(v,min,max){return Math.min(Math.max(v,min),max);}\n" }]);
steps.seeded = seedCommitOid;
using repo = await env.ARTIFACTS.get(repoName);
const writeToken = await repo.createToken("write", 3600);
await writeRung(remote, writeToken, repo, {
path: "clamp.js",
kind: "file",
summary: "Exports clamp(value, min, max).",
sources: [{ kind: "commit", ref: seedCommitOid, title: "seed", author: "seed", at: new Date().toISOString() }],
children: [],
});
steps.wroteFileRung = true;
using repo2 = await env.ARTIFACTS.get(repoName);
const readToken = await repo2.createToken("read", 3600);
steps.readFileRung = await readRung(remote, readToken, repo2, "file", "clamp.js");
await env.ARTIFACTS.delete(repoName);
steps.cleanedUp = true;
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
// Diagnostic only: what does git.fetch() actually return for a ref
// that genuinely doesn't exist on the remote yet? scopedNotesCheckout
// (cloudflareLadderBackend.ts) assumes it throws or returns a falsy
// fetchHead -- this checks that assumption directly against a real
// Artifacts repo instead of guessing from a confusing downstream error.
// Diagnostic only: the "repo"-kind rung (anchored to rootCommitOid
// rather than a file blob) and appendRepoChildren were both added
// speculatively in this final phase and never exercised live --
// testLadder only covers a "file" rung. Isolates each step so a
// failure here points at exactly one of: writing the root note,
// reading it back, or appending a child to it.
async function testRepoRungAndAppend(env: Env): Promise<Response> {
const repoName = "repo-rung-proof-" + Date.now();
const steps: Record<string, unknown> = {};
try {
const { remote, commitOid: seedCommitOid } = await seedRepo(env, repoName, [{ path: "a.txt", content: "a\n" }]);
steps.seeded = seedCommitOid;
const source = [{ kind: "commit" as const, ref: seedCommitOid, title: "seed", author: "seed", at: new Date().toISOString() }];
using repo1 = await env.ARTIFACTS.get(repoName);
const writeToken1 = await repo1.createToken("write", 3600);
await writeRung(remote, writeToken1, repo1, { path: "_repo", kind: "repo", summary: "root", sources: source, children: [] });
steps.wroteRootRung = true;
using repo2 = await env.ARTIFACTS.get(repoName);
const readToken1 = await repo2.createToken("read", 3600);
steps.readRootAfterWrite = await readRung(remote, readToken1, repo2, "repo", "_repo");
using repo3 = await env.ARTIFACTS.get(repoName);
const writeToken2 = await repo3.createToken("write", 3600);
await appendRepoChildren(remote, writeToken2, repo3, [{ path: "a.txt", kind: "file" }]);
steps.appended = true;
using repo4 = await env.ARTIFACTS.get(repoName);
const readToken2 = await repo4.createToken("read", 3600);
steps.readRootAfterAppend = await readRung(remote, readToken2, repo4, "repo", "_repo");
await env.ARTIFACTS.delete(repoName);
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
// Diagnostic only: the zz-meta orphan branch is brand new this phase
// -- verify ensureMeta's bootstrap, a role-grant write, and reads of
// both files against a real Artifacts repo before anything else is
// built on top of it (D1, the web app).
async function testRepoMeta(env: Env): Promise<Response> {
const repoName = "repo-meta-proof-" + Date.now();
const steps: Record<string, unknown> = {};
try {
const { remote } = await seedRepo(env, repoName, [{ path: "a.txt", content: "a\n" }]);
using repo = await env.ARTIFACTS.get(repoName);
const token = await repo.createToken("write", 3600);
await ensureMeta(remote, token, { description: "a test repo", owner: "acct-test" });
steps.ensuredOnce = await readRepoMeta(remote, token);
// Idempotency: calling ensureMeta again must not clobber the existing meta.json.
await ensureMeta(remote, token, { description: "SHOULD NOT APPEAR", owner: "acct-other" });
steps.ensuredTwice = await readRepoMeta(remote, token);
const rolesBefore = await readRepoRoles(remote, token);
steps.rolesBefore = rolesBefore;
await writeRepoRoles(remote, token, { grants: [...(rolesBefore?.grants ?? []), { principal: "agent:agent-test", role: "contributor", granted_by: "acct-test", at: new Date().toISOString() }] });
steps.rolesAfter = await readRepoRoles(remote, token);
// meta.json must survive the roles.json-only write untouched.
steps.metaAfterRolesWrite = await readRepoMeta(remote, token);
await env.ARTIFACTS.delete(repoName);
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
async function testFetchMissingRef(env: Env): Promise<Response> {
const repoName = "fetch-missing-ref-" + Date.now();
const steps: Record<string, unknown> = {};
try {
const { remote } = await seedRepo(env, repoName, [{ path: "a.txt", content: "a\n" }]);
using repo = await env.ARTIFACTS.get(repoName);
const token = await repo.createToken("write", 3600);
const fs = createMemFs();
const dir = "/notes";
await git.init({ fs, dir, defaultBranch: "main" });
await git.addRemote({ fs, dir, remote: "origin", url: remote });
const onAuth = () => ({ username: "x-token-auth", password: tokenString(token) });
const ref = "refs/notes/zz/ladder/code";
try {
const result = await git.fetch({ fs, http, dir, remote: "origin", onAuth, ref, remoteRef: ref, singleBranch: true, tags: false });
steps.fetchResolved = true;
steps.fetchResult = { fetchHead: result.fetchHead, headers: result.headers ? Object.fromEntries(Object.entries(result.headers)) : null, pruned: result.pruned ?? null };
} catch (fetchErr) {
steps.fetchThrew = true;
steps.fetchError = fetchErr instanceof Error ? (fetchErr.stack ?? fetchErr.message) : String(fetchErr);
}
await env.ARTIFACTS.delete(repoName);
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
// The original proof-of-mechanism test (see this file's own header):
// create a real Artifacts repo, write/push a commit via isomorphic-git,
// read it back through the native binding, delete it. Only ever runs
// on its own explicit path now -- see the fetch handler's own comment
// for why it used to run on every unmatched request instead.
async function testProofOfMechanism(env: Env): Promise<Response> {
const repoName = "proof-" + Date.now();
const steps: Record<string, unknown> = {};
try {
const created = await env.ARTIFACTS.create(repoName, { setDefaultBranch: "main" });
steps.created = { name: created.name, remote: created.remote };
const token = tokenString(created.token);
const fs = createMemFs();
const dir = "/repo";
await git.init({ fs, dir, defaultBranch: "main" });
const blobOid = await git.writeBlob({ fs, dir, blob: new TextEncoder().encode("hello from a real Worker\n") });
const treeOid = await git.writeTree({ fs, dir, tree: [{ mode: "100644", path: "README.md", oid: blobOid, type: "blob" }] });
const person = { name: "zz-worker", email: "zz-worker@local", timestamp: Math.floor(Date.now() / 1000), timezoneOffset: 0 };
const commitOid = await git.writeCommit({ fs, dir, commit: { message: "seed from Worker", tree: treeOid, parent: [], author: person, committer: person } });
await git.writeRef({ fs, dir, ref: "refs/heads/main", value: commitOid, force: true });
const onAuth = () => ({ username: "x-token-auth", password: token });
const pushResult = await git.push({ fs, http, dir, url: created.remote, onAuth, ref: "main", remoteRef: "main" });
steps.push = pushResult;
const repo = await env.ARTIFACTS.get(repoName);
const log = await repo.log({ ref: "main", limit: 5 });
steps.log = log;
const nativeCommit = await repo.readCommit(commitOid);
const nativeTree = nativeCommit ? await repo.readTree(nativeCommit.treeHash) : null;
const nativeBlob = nativeTree ? await repo.readBlob(nativeTree[0].hash) : null;
steps.nativeRead = { blobText: nativeBlob ? await nativeBlob.text() : null };
await env.ARTIFACTS.delete(repoName);
steps.cleanedUp = true;
return Response.json({ ok: true, steps });
} catch (err) {
await env.ARTIFACTS.delete(repoName).catch(() => {});
return Response.json({ ok: false, error: err instanceof Error ? (err.stack ?? err.message) : String(err), steps }, { status: 500 });
}
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const path = new URL(req.url).pathname;
if (path === "/full-protocol-test") return testFullProtocol(env);
if (path === "/ladder-test") return testLadder(env);
if (path === "/changeset-test") return testChangesetCommit(env);
if (path === "/fetch-missing-ref-test") return testFetchMissingRef(env);
if (path === "/repo-rung-test") return testRepoRungAndAppend(env);
if (path === "/repo-meta-test") return testRepoMeta(env);
if (path === "/dev-seed-content") {
const artifactsName = new URL(req.url).searchParams.get("repo");
if (!artifactsName) return new Response("missing ?repo=", { status: 400 });
const stub = env.MAINTAINER_DO.get(env.MAINTAINER_DO.idFromName(artifactsName));
await stub.configure(artifactsName);
const commitResult = await stub.callTool("commit_changeset", {
repo: artifactsName,
files: [
{ path: "README.md", content: "# Demo widget\n\nA tiny demo repo for testing the web app.\n" },
{ path: "index.js", content: "export function widget() { return 42; }\n" },
],
message: "seed content",
author_name: "dev-seed",
author_email: "dev-seed@local",
});
return Response.json(commitResult);
}
if (path === "/dev-cf-userinfo-raw") {
// One-off diagnostic, extended: does GET /client/v4/user with the
// OAuth access token as a bearer actually return email/name,
// the way a REST API token's Bearer auth would? (user-details.read
// is an API-token-permission-shaped scope, not an OIDC one, so
// it's plausible the access token doubles as a regular API
// token for that one permission -- or it may not, API tokens and
// OAuth access tokens aren't always interchangeable. Checking
// directly rather than assuming either way.)
const row = await env.DB.prepare("SELECT access_token_enc FROM agent_identities WHERE provider = 'cloudflare' ORDER BY created_at DESC LIMIT 1").first<{ access_token_enc: string }>();
if (!row) return new Response("no cloudflare identity stored yet", { status: 404 });
const accessToken = await decryptSecret(env, row.access_token_enc);
const [userinfoResp, apiUserResp] = await Promise.all([
fetch("https://dash.cloudflare.com/oauth2/userinfo", { headers: { authorization: `Bearer ${accessToken}` } }),
fetch("https://api.cloudflare.com/client/v4/user", { headers: { authorization: `Bearer ${accessToken}` } }),
]);
const [userinfoText, apiUserText] = await Promise.all([userinfoResp.text(), apiUserResp.text()]);
return new Response(
`GET /oauth2/userinfo -> ${userinfoResp.status}\n${userinfoText}\n\nGET /client/v4/user -> ${apiUserResp.status}\n${apiUserText}`,
{ headers: { "content-type": "text/plain" } },
);
}
const webResponse = await handleWebRequest(req, env);
if (webResponse) return webResponse;
// Was, for a long time, an unguarded live-Artifacts proof-of-
// mechanism test (create a real repo, commit, push, read back,
// delete) run on EVERY request handleWebRequest didn't itself
// claim -- including, once the web app grew enough real routes
// that most of the URL space belongs to it, totally ordinary
// unmatched requests in production (confirmed live: a Workers
// Routes precedence issue sent "/" here instead of the docs
// worker, and this ran a real create/push/delete cycle on every
// hit instead of just 404ing). The actual proof-of-mechanism test
// this used to BE still exists, just moved behind its own explicit
// path so it only ever runs on purpose.
if (path === "/proof-of-mechanism-test") return testProofOfMechanism(env);
return new Response("not found", { status: 404 });
},
};