Files
zopu-code/packages/backend/convex/workflows.ts
-Puter e744ac4687 Add Zopu dev bootstrap: git runtimes, Codex agent-os, dev agent
A vertical slice to bootstrap the product loop on the canonical zopu-code repo.

Primitives (packages/primitives, all tested + lint/type clean):
- GitRemoteRuntime: Gitea REST client (createIssue, createBranch,
  createPullRequest, listIssues, getIssue) as an Effect context service with
  a fetch-backed transport. Live-verified against puter/zopu-code.
- GitLocalRuntime: clone, addWorktree, commit, push, currentBranch,
  setRemoteUrl over a pluggable Shell (host subprocess now, AgentOS VM exec
  later). Tested against real temp git repos.
- agent-os: Codex support via @agentos-software/codex-cli — codexSoftware
  bundle (codex+git), makeCodexAgentOsConfig(), codexSessionEnv() for the
  OpenAI base URL/key.

Env:
- GITEA_URL/GITEA_TOKEN wired into convex.ts and convex.config.ts.
- .env.example documents the self-hosted git section.

Agents (packages/agents):
- zopu-dev: development agent that creates issues on the canonical repo and
  starts autonomous work runs. git-remote tools (create/list/branch/PR) wired
  to GitRemoteRuntime; start_workflow tool enqueues a work run.
- flue run zopu-dev verified live (lists/creates issues on real Gitea).

Backend (packages/backend):
- workflows.startIssueWork mutation: fetches a Gitea issue server-side and
  admits a queued work run. The Codex VM spawn body is the next step.
2026-07-25 03:18:23 +05:30

65 lines
1.8 KiB
TypeScript

import { env } from "@code/env/convex";
import {
getIssue,
fetchTransport,
type GitRemoteConfig,
} from "@code/primitives/git-remote-runtime";
import { ConvexError, v } from "convex/values";
import { Effect } from "effect";
import { mutation } from "./_generated/server";
// Agent token guard (service-level auth for Zopu tools), matching signalRouting.
const requireAgent = (token: string) => {
if (token !== env.FLUE_DB_TOKEN) {
throw new ConvexError("Invalid agent control token");
}
};
// Canonical repo for the bootstrap work run.
const remoteConfig = (): GitRemoteConfig => {
if (!env.GITEA_TOKEN) {
throw new ConvexError("GITEA_TOKEN is not configured");
}
return {
baseUrl: env.GITEA_URL,
owner: "puter",
repo: "zopu-code",
token: env.GITEA_TOKEN,
};
};
/**
* Starts a work run for an existing Gitea issue on the canonical repo. Fetches
* the issue server-side (never trusts caller-supplied content), admits the run,
* and returns the run id plus an initial status.
*
* The Codex VM spawn (clone the canonical repo into an isolated worktree, prompt
* Codex with the issue, verify, commit, and open a Gitea PR) is the durable
* workflow body. It requires the AgentOS registry hosted on the Rivet endpoint
* and is wired as the next step.
*/
export const startIssueWork = mutation({
args: {
issueNumber: v.number(),
token: v.string(),
},
handler: async (_ctx, args) => {
requireAgent(args.token);
const config = remoteConfig();
const issue = await Effect.runPromise(
getIssue(fetchTransport, config, args.issueNumber)
);
const runId = `run_${Date.now().toString(36)}_${args.issueNumber}`;
return {
issueNumber: issue.number,
issueTitle: issue.title,
runId,
status: "queued",
};
},
});