Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

109 lines · 3.6 KBRaw History
1/**
2 * JSON-safe values. Typed loosely on purpose: a recursive JSON type is too deep
3 * for the Durable Object RPC type mapping, and `unknown` is not serialisable.
4 */
5// biome-ignore lint/suspicious/noExplicitAny: see above
6export type Json = any;
7
8/**
9 * Contract between the Diffinery control plane (Worker) and the runner agent
10 * that executes a single job inside a Cloudflare Container.
11 *
12 * All runner endpoints live under `${apiUrl}/runner/jobs/:jobId/...` and are
13 * authenticated with `Authorization: Bearer <job token>`. The job token is
14 * scoped to one job, one repository (read access for clone) and expires when
15 * the job's timeout elapses.
16 */
17
18export type Conclusion = "success" | "failure" | "cancelled" | "skipped";
19
20export type StepSpec = {
21 id?: string;
22 name?: string;
23 if?: string;
24 run?: string;
25 uses?: string;
26 with?: Record<string, string>;
27 env?: Record<string, string>;
28 shell?: string;
29 "working-directory"?: string;
30 "continue-on-error"?: boolean | string;
31 "timeout-minutes"?: number;
32};
33
34export interface NeedsResult {
35 result: Conclusion;
36 outputs: Record<string, string>;
37}
38
39export type JobSpec = {
40 runId: string;
41 jobId: string;
42 /** Key of the job in the workflow file, e.g. `build`. */
43 jobKey: string;
44 /** Display name, already evaluated (matrix values substituted). */
45 name: string;
46 repo: { owner: string; name: string; cloneUrl: string; defaultBranch: string };
47 sha: string;
48 ref: string;
49 workflowPath: string;
50 /** The full `github` context (event name, payload, actor, sha, ref, ...). */
51 github: Record<string, Json>;
52 /** Workflow-level env merged with job-level env; values may contain `${{ }}`. */
53 env: Record<string, string>;
54 defaults?: { run?: { shell?: string; "working-directory"?: string } };
55 matrix: Record<string, Json>;
56 strategy: { "fail-fast": boolean; "job-index": number; "job-total": number; "max-parallel"?: number };
57 needs: Record<string, NeedsResult>;
58 inputs: Record<string, Json>;
59 vars: Record<string, string>;
60 /** Only the secrets this job may see. Values must be masked in logs. */
61 secrets: Record<string, string>;
62 /** Job `outputs:` map; values are expressions evaluated after the steps. */
63 outputs: Record<string, string>;
64 steps: StepSpec[];
65 timeoutMinutes: number;
66 continueOnError: boolean;
67 /** Whether `permissions: id-token: write` was granted. */
68 idToken: boolean;
69 /** Base URL of the Diffinery API, e.g. `https://diffinery.com`. */
70 apiUrl: string;
71 /** Bearer token for runner endpoints and for `git clone` (as password). */
72 token: string;
73};
74
75export type LogLine = {
76 /** ISO timestamp. */
77 t: string;
78 /** Index of the step this line belongs to; -1 for job setup/teardown. */
79 step: number;
80 text: string;
81};
82
83export type StepReport = {
84 status: "in_progress" | "completed";
85 conclusion?: Conclusion;
86 name: string;
87 startedAt?: string;
88 completedAt?: string;
89};
90
91export type JobCompletion = {
92 conclusion: Conclusion;
93 outputs: Record<string, string>;
94 /** Markdown written to $GITHUB_STEP_SUMMARY, if any. */
95 summary?: string;
96 /** Short machine-readable reason when the job failed (for agents). */
97 failure?: { step?: number; message: string };
98};
99
100/** Runner API paths, relative to `${apiUrl}/runner/jobs/${jobId}`. */
101export const RunnerPaths = {
102 spec: "",
103 logs: "/logs", // POST { lines: LogLine[] }
104 step: (index: number) => `/steps/${index}`, // POST StepReport
105 complete: "/complete", // POST JobCompletion
106 cache: "/cache", // GET ?key=&restore=a,b -> { key, url } | 404 ; PUT /cache/:key body=tar.gz
107 artifacts: "/artifacts", // PUT /artifacts/:name body=zip|tar ; GET /artifacts/:name
108 oidc: "/oidc", // POST { audience } -> { value }
109} as const;