@opensoftware/foreman 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +270 -2
- package/dist/adapters/factory.js +33 -0
- package/dist/adapters/factory.js.map +1 -0
- package/dist/adapters/gatekeeper.js +18 -0
- package/dist/adapters/gatekeeper.js.map +1 -0
- package/dist/adapters/harness.js +53 -0
- package/dist/adapters/harness.js.map +1 -0
- package/dist/adapters/nats.js +31 -0
- package/dist/adapters/nats.js.map +1 -0
- package/dist/adapters/sandbox.js +56 -0
- package/dist/adapters/sandbox.js.map +1 -0
- package/dist/cli.js +20 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/runs.js +68 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/serve.js +110 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/domain/definition.js +45 -0
- package/dist/domain/definition.js.map +1 -0
- package/dist/domain/run.js +147 -0
- package/dist/domain/run.js.map +1 -0
- package/dist/domain/time.js +3 -0
- package/dist/domain/time.js.map +1 -0
- package/dist/workflows/activities.js +105 -0
- package/dist/workflows/activities.js.map +1 -0
- package/dist/workflows/run.js +53 -0
- package/dist/workflows/run.js.map +1 -0
- package/dist/workflows.bundle.js +12231 -0
- package/package.json +41 -3
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// A stage's sandbox through the sandbox port (ADR 0054, ADR 0068). The adapter is a program; the foreman creates a
|
|
2
|
+
// sandbox from the spec, copies the run's folder in, runs the harness with exec, copies the folder out and destroys it.
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { cpSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join, posix } from "node:path";
|
|
7
|
+
/** Runs one command of the adapter; a failure names the command and what the adapter wrote to standard error. */
|
|
8
|
+
function adapterRun(adapter, env) {
|
|
9
|
+
const [file, ...args] = adapter;
|
|
10
|
+
return (...command) => new Promise((resolve, reject) => {
|
|
11
|
+
execFile(file, [...args, ...command], { env }, (error, stdout, stderr) => {
|
|
12
|
+
if (error)
|
|
13
|
+
reject(new Error(`the sandbox adapter's ${command[0]} failed: ${stderr.trim() || error.message}`));
|
|
14
|
+
else
|
|
15
|
+
resolve(stdout.trim());
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A new sandbox from the spec for the files of `local`, the run's folder, which it holds at `<working folder>/<name>`
|
|
21
|
+
* after `copyIn`. The caller destroys it.
|
|
22
|
+
*/
|
|
23
|
+
export async function createSandbox(adapter, spec, local, name, env) {
|
|
24
|
+
const run = adapterRun(adapter, env);
|
|
25
|
+
const temporary = mkdtempSync(join(tmpdir(), "foreman-sandbox-"));
|
|
26
|
+
let id;
|
|
27
|
+
try {
|
|
28
|
+
writeFileSync(join(temporary, "spec.json"), JSON.stringify(spec));
|
|
29
|
+
id = (await run("create", join(temporary, "spec.json"))).split("\n").pop();
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
async copyIn() {
|
|
36
|
+
const working = await run("exec", id, "--", "pwd");
|
|
37
|
+
// The path does not exist in the sandbox yet, so every adapter copies the folder itself, not into it.
|
|
38
|
+
await run("copy", local, `${id}:${name}`);
|
|
39
|
+
return posix.join(working, name);
|
|
40
|
+
},
|
|
41
|
+
exec: (command) => [...adapter, "exec", id, "--", ...command],
|
|
42
|
+
async copyOut() {
|
|
43
|
+
const out = mkdtempSync(join(tmpdir(), "foreman-sandbox-out-"));
|
|
44
|
+
try {
|
|
45
|
+
await run("copy", `${id}:${name}`, join(out, name));
|
|
46
|
+
rmSync(local, { recursive: true, force: true });
|
|
47
|
+
cpSync(join(out, name), local, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
rmSync(out, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
destroy: async () => void (await run("destroy", id)),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/adapters/sandbox.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// foreman <command>: the entry layer only parses arguments and calls the commands.
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
import { runProgram } from "@opensoftware/cli";
|
|
5
|
+
import { serve } from "./commands/serve.js";
|
|
6
|
+
const HELP = `Usage: foreman <command>
|
|
7
|
+
|
|
8
|
+
Commands:
|
|
9
|
+
serve Serve the run API on PORT and run the factory's workflow in FOREMAN_FACTORY on Temporal at
|
|
10
|
+
FOREMAN_TEMPORAL_ADDRESS.
|
|
11
|
+
`;
|
|
12
|
+
const { positionals, values } = parseArgs({ allowPositionals: true, options: { help: { type: "boolean" } } });
|
|
13
|
+
const serving = () => serve(process.env);
|
|
14
|
+
process.exitCode = await runProgram({
|
|
15
|
+
name: "foreman",
|
|
16
|
+
usage: HELP,
|
|
17
|
+
command: positionals[0] === "serve" ? serving : undefined,
|
|
18
|
+
help: values.help,
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,mFAAmF;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAgB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,IAAI,GAAG;;;;;CAKZ,CAAC;AAEF,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;AAC9G,MAAM,OAAO,GAAY,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAClD,OAAO,CAAC,QAAQ,GAAG,MAAM,UAAU,CAAC;IAClC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;IACzD,IAAI,EAAE,MAAM,CAAC,IAAI;CAClB,CAAC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// The runs of the foreman as Temporal workflows (ADR 0067): start one per work item, read its view, signal it.
|
|
2
|
+
import { UsageError } from "@opensoftware/cli";
|
|
3
|
+
import { validate } from "@opensoftware/contracts";
|
|
4
|
+
import { WorkflowExecutionAlreadyStartedError, WorkflowIdReusePolicy, WorkflowNotFoundError, } from "@temporalio/client";
|
|
5
|
+
import { firstView, hasEnded, runIdOf, signalOf } from "../domain/run.js";
|
|
6
|
+
import { factoryRun, gateSignal, steeringQuery, steerSignal, viewQuery } from "../workflows/run.js";
|
|
7
|
+
export class Conflict extends Error {
|
|
8
|
+
}
|
|
9
|
+
function checked(format, document, what) {
|
|
10
|
+
const problems = validate(format, document);
|
|
11
|
+
if (problems.length)
|
|
12
|
+
throw new UsageError(`${what}: ${problems.map((problem) => `${problem.path || "/"}: ${problem.message}`).join("; ")}`);
|
|
13
|
+
return document;
|
|
14
|
+
}
|
|
15
|
+
export function runs(options) {
|
|
16
|
+
const { client, taskQueue } = options;
|
|
17
|
+
const handle = (runId) => client.workflow.getHandle(runId);
|
|
18
|
+
const view = (runId) => handle(runId)
|
|
19
|
+
.query(viewQuery)
|
|
20
|
+
.catch((error) => (error instanceof WorkflowNotFoundError ? undefined : Promise.reject(error)));
|
|
21
|
+
return {
|
|
22
|
+
async start(document) {
|
|
23
|
+
const workItem = checked("work-item", document, "the body is not a work item");
|
|
24
|
+
const { definition, version } = await options.factory();
|
|
25
|
+
const first = firstView(workItem, definition, version, options.now());
|
|
26
|
+
try {
|
|
27
|
+
await client.workflow.start(factoryRun, {
|
|
28
|
+
workflowId: first.runId,
|
|
29
|
+
taskQueue,
|
|
30
|
+
// One run per work item, also after it ended.
|
|
31
|
+
workflowIdReusePolicy: WorkflowIdReusePolicy.REJECT_DUPLICATE,
|
|
32
|
+
args: [{ view: first, definition }],
|
|
33
|
+
});
|
|
34
|
+
return { created: true, view: first };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (!(error instanceof WorkflowExecutionAlreadyStartedError))
|
|
38
|
+
throw error;
|
|
39
|
+
return { created: false, view: (await view(first.runId)) };
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
view,
|
|
43
|
+
async signal(runId, document) {
|
|
44
|
+
const signal = signalOf(document);
|
|
45
|
+
if (typeof signal === "string")
|
|
46
|
+
throw new UsageError(`the body is not a signal: ${signal}`);
|
|
47
|
+
const current = await view(runId);
|
|
48
|
+
if (!current)
|
|
49
|
+
return undefined;
|
|
50
|
+
if (hasEnded(current))
|
|
51
|
+
throw new Conflict(`${runId} is ${current.state}`);
|
|
52
|
+
if (signal.type === "steer") {
|
|
53
|
+
await handle(runId).signal(steerSignal, signal.message);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
await handle(runId).cancel();
|
|
57
|
+
}
|
|
58
|
+
return current;
|
|
59
|
+
},
|
|
60
|
+
async steering(runId) {
|
|
61
|
+
return handle(runId).query(steeringQuery);
|
|
62
|
+
},
|
|
63
|
+
async decided(workItemId, decision) {
|
|
64
|
+
await handle(runIdOf(workItemId)).signal(gateSignal, decision);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=runs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/commands/runs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// foreman serve: the run API on PORT and a Temporal worker that runs the factory's workflow (ADR 0067). With
|
|
2
|
+
// FOREMAN_NATS_URL it also starts runs for work.received@v1 and passes gate.decided@v1 to the waiting runs. It runs
|
|
3
|
+
// until SIGTERM or SIGINT.
|
|
4
|
+
import { timingSafeEqual } from "node:crypto";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { UsageError } from "@opensoftware/cli";
|
|
8
|
+
import { Client, Connection } from "@temporalio/client";
|
|
9
|
+
import { NativeConnection, Worker } from "@temporalio/worker";
|
|
10
|
+
import Fastify, {} from "fastify";
|
|
11
|
+
import { readFactory } from "../adapters/factory.js";
|
|
12
|
+
import { gatekeeper } from "../adapters/gatekeeper.js";
|
|
13
|
+
import { openBus, SUBJECTS } from "../adapters/nats.js";
|
|
14
|
+
import { isoNow } from "../domain/time.js";
|
|
15
|
+
import { activities } from "../workflows/activities.js";
|
|
16
|
+
import { Conflict, runs } from "./runs.js";
|
|
17
|
+
const BUNDLE = fileURLToPath(new URL("../workflows.bundle.js", import.meta.url));
|
|
18
|
+
const problem = (reply, status, title, detail) => reply.code(status).type("application/problem+json").send({ title, status, detail });
|
|
19
|
+
/** Whether the request carries the token; compared in constant time. */
|
|
20
|
+
function authorized(header, token) {
|
|
21
|
+
const [sent, wanted] = [Buffer.from(header ?? ""), Buffer.from(`Bearer ${token}`)];
|
|
22
|
+
return sent.length === wanted.length && timingSafeEqual(sent, wanted);
|
|
23
|
+
}
|
|
24
|
+
function api(work, token) {
|
|
25
|
+
const app = Fastify();
|
|
26
|
+
app.setErrorHandler((error, _request, reply) => {
|
|
27
|
+
if (error instanceof UsageError)
|
|
28
|
+
return problem(reply, 400, "Bad request", error.message);
|
|
29
|
+
if (error instanceof Conflict)
|
|
30
|
+
return problem(reply, 409, "The run ended", error.message);
|
|
31
|
+
return problem(reply, error.statusCode ?? 500, "The request failed", error.message);
|
|
32
|
+
});
|
|
33
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
34
|
+
if (!authorized(request.headers.authorization, token))
|
|
35
|
+
return problem(reply, 401, "Unauthorized", "send Authorization: Bearer <FOREMAN_API_TOKEN>");
|
|
36
|
+
});
|
|
37
|
+
const unknown = (reply, runId) => problem(reply, 404, "No such run", `the server has no run ${runId}`);
|
|
38
|
+
app.post("/v1/runs", async (request, reply) => {
|
|
39
|
+
const { created, view } = await work.start(request.body);
|
|
40
|
+
return reply.code(created ? 201 : 200).send(view);
|
|
41
|
+
});
|
|
42
|
+
app.get("/v1/runs/:runId", async (request, reply) => {
|
|
43
|
+
const view = await work.view(request.params.runId);
|
|
44
|
+
return view ?? unknown(reply, request.params.runId);
|
|
45
|
+
});
|
|
46
|
+
app.post("/v1/runs/:runId/signals", async (request, reply) => {
|
|
47
|
+
const view = await work.signal(request.params.runId, request.body);
|
|
48
|
+
return view ? reply.code(202).send(view) : unknown(reply, request.params.runId);
|
|
49
|
+
});
|
|
50
|
+
return app;
|
|
51
|
+
}
|
|
52
|
+
/** Starts runs for work items from intake and hands gate decisions to the runs that wait for them. */
|
|
53
|
+
function listen(bus, work) {
|
|
54
|
+
bus.subscribe(SUBJECTS.workReceived, async (event) => {
|
|
55
|
+
await work.start(event.data);
|
|
56
|
+
});
|
|
57
|
+
bus.subscribe(SUBJECTS.gateDecided, async (event) => {
|
|
58
|
+
const { data } = event;
|
|
59
|
+
await work.decided(data.workItemId, { gateId: data.gateId, state: data.decision });
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export async function serve(env) {
|
|
63
|
+
const token = env.FOREMAN_API_TOKEN;
|
|
64
|
+
if (!token)
|
|
65
|
+
throw new UsageError("set FOREMAN_API_TOKEN: the token that callers of the API send");
|
|
66
|
+
const factoryPath = resolve(env.FOREMAN_FACTORY || "factory.yaml");
|
|
67
|
+
await readFactory(factoryPath);
|
|
68
|
+
const now = () => isoNow(env.OSF_NOW);
|
|
69
|
+
const warn = (text) => process.stderr.write(`foreman: ${text}\n`);
|
|
70
|
+
const address = env.FOREMAN_TEMPORAL_ADDRESS || "localhost:7233";
|
|
71
|
+
// Unset, the client and the worker use the namespace "default".
|
|
72
|
+
const namespace = env.FOREMAN_TEMPORAL_NAMESPACE;
|
|
73
|
+
const taskQueue = env.FOREMAN_TASK_QUEUE || "foreman";
|
|
74
|
+
const pollMs = Number(env.FOREMAN_POLL_MS || 1000);
|
|
75
|
+
const bus = env.FOREMAN_NATS_URL ? await openBus(env.FOREMAN_NATS_URL, warn) : undefined;
|
|
76
|
+
const client = new Client({ connection: await Connection.connect({ address }), namespace });
|
|
77
|
+
const work = runs({ client, taskQueue, factory: () => readFactory(factoryPath), now });
|
|
78
|
+
const worker = await Worker.create({
|
|
79
|
+
connection: await NativeConnection.connect({ address }),
|
|
80
|
+
namespace,
|
|
81
|
+
taskQueue,
|
|
82
|
+
workflowBundle: { codePath: BUNDLE },
|
|
83
|
+
activities: activities({
|
|
84
|
+
now,
|
|
85
|
+
bus,
|
|
86
|
+
gatekeeper: gatekeeper(env.FOREMAN_GATEKEEPER_URL, env.FOREMAN_GATEKEEPER_TOKEN),
|
|
87
|
+
harnesses: JSON.parse(env.FOREMAN_HARNESSES || "{}"),
|
|
88
|
+
sandboxes: JSON.parse(env.FOREMAN_SANDBOXES || "{}"),
|
|
89
|
+
data: resolve(env.OSF_DATA || ".foreman"),
|
|
90
|
+
pollMs,
|
|
91
|
+
steering: (runId) => work.steering(runId),
|
|
92
|
+
env,
|
|
93
|
+
}),
|
|
94
|
+
maxHeartbeatThrottleInterval: pollMs,
|
|
95
|
+
defaultHeartbeatThrottleInterval: pollMs,
|
|
96
|
+
});
|
|
97
|
+
const running = worker.run();
|
|
98
|
+
if (bus)
|
|
99
|
+
listen(bus, work);
|
|
100
|
+
const app = api(work, token);
|
|
101
|
+
const port = Number(env.PORT || 8080);
|
|
102
|
+
await app.listen({ port, host: "0.0.0.0" });
|
|
103
|
+
process.stdout.write(`foreman serves the run API on port ${port}, task queue ${taskQueue} at ${address}\n`);
|
|
104
|
+
// Temporal's runtime stops the worker on SIGTERM and SIGINT; the server stops with it.
|
|
105
|
+
await running;
|
|
106
|
+
await app.close();
|
|
107
|
+
await bus?.close();
|
|
108
|
+
return { lines: [] };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** The problems that only the foreman sees: two sandbox adapters, or a stage whose agent the factory does not declare. */
|
|
2
|
+
export function stageProblems(factory) {
|
|
3
|
+
const adapters = Object.keys(factory.providers?.sandbox ?? {}).length;
|
|
4
|
+
const sandbox = adapters > 1 ? [{ path: "/providers/sandbox", message: `name one sandbox adapter, not ${adapters}` }] : [];
|
|
5
|
+
return sandbox.concat(stageAgentProblems(factory));
|
|
6
|
+
}
|
|
7
|
+
function stageAgentProblems(factory) {
|
|
8
|
+
return (factory.workflow ?? []).flatMap((stage, index) => {
|
|
9
|
+
const agent = agentOf(stage);
|
|
10
|
+
return factory.agents[agent]
|
|
11
|
+
? []
|
|
12
|
+
: [{ path: `/workflow/${index}/${stage.agent ? "agent" : "stage"}`, message: `no agent "${agent}"` }];
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** A stage's agent defaults to the agent with the stage's name. */
|
|
16
|
+
export const agentOf = (stage) => stage.agent ?? stage.stage;
|
|
17
|
+
/** The definition of a checked factory, with the text of each prompt from `read`. */
|
|
18
|
+
export function definitionOf(factory, read) {
|
|
19
|
+
return {
|
|
20
|
+
factory: factory.factory,
|
|
21
|
+
sandboxAdapter: Object.keys(factory.providers?.sandbox ?? {})[0] ?? "docker",
|
|
22
|
+
stages: (factory.workflow ?? []).map((stage) => {
|
|
23
|
+
const agent = agentOf(stage);
|
|
24
|
+
const declared = factory.agents[agent];
|
|
25
|
+
return {
|
|
26
|
+
stage: stage.stage,
|
|
27
|
+
agent,
|
|
28
|
+
harness: declared.harness,
|
|
29
|
+
prompt: read(declared.prompt),
|
|
30
|
+
...(stage.gate ? { gate: stage.gate } : {}),
|
|
31
|
+
...(declared.sandbox ? { sandbox: { ...declared.sandbox, secrets: secretsOf(declared.secrets ?? []) } } : {}),
|
|
32
|
+
};
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** The variable of a secret reference: its name in upper case, every character outside A-Z and 0-9 as "_". */
|
|
37
|
+
export const secretVariable = (reference) => reference
|
|
38
|
+
.slice(reference.indexOf("/") + 1)
|
|
39
|
+
.toUpperCase()
|
|
40
|
+
.replace(/[^A-Z0-9]/g, "_");
|
|
41
|
+
/** The agent's secret references by their variables, as the sandbox spec holds them (ADR 0068). */
|
|
42
|
+
function secretsOf(references) {
|
|
43
|
+
return Object.fromEntries(references.map((reference) => [secretVariable(reference), reference]));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=definition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/domain/definition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// A run of the factory's workflow for one work item, without I/O (ADR 0067): its ID, its first view, the next step,
|
|
2
|
+
// how each fact changes the view, and the events it publishes. The workflow asks for each step and applies each fact
|
|
3
|
+
// through local activities, so this code runs in the worker's own thread, not in Temporal's sandbox.
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
const ENDED = new Set(["complete", "failed", "cancelled"]);
|
|
6
|
+
/** "run_" and 20 hex digits of the SHA-256 of the work item's ID: one run per work item. */
|
|
7
|
+
export const runIdOf = (workItemId) => `run_${createHash("sha256").update(workItemId).digest("hex").slice(0, 20)}`;
|
|
8
|
+
export function firstView(workItem, definition, version, startedAt) {
|
|
9
|
+
return {
|
|
10
|
+
runId: runIdOf(workItem.id),
|
|
11
|
+
workItem,
|
|
12
|
+
factory: definition.factory,
|
|
13
|
+
factoryVersion: version,
|
|
14
|
+
state: "running",
|
|
15
|
+
startedAt,
|
|
16
|
+
stages: definition.stages.map(({ stage, agent }) => ({ stage, agent, state: "pending" })),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export const hasEnded = (view) => ENDED.has(view.state);
|
|
20
|
+
export function nextStep(view, definition) {
|
|
21
|
+
if (hasEnded(view))
|
|
22
|
+
return { kind: "end" };
|
|
23
|
+
// A run whose last stage succeeded is complete, so a run that goes on has a stage that did not succeed.
|
|
24
|
+
const index = view.stages.findIndex((stage) => stage.state !== "succeeded");
|
|
25
|
+
const stage = view.stages[index];
|
|
26
|
+
if (stage.state === "blocked" && stage.gateId)
|
|
27
|
+
return { kind: "await-gate", index, gateId: stage.gateId };
|
|
28
|
+
if (definition.stages[index]?.gate && !stage.gateId)
|
|
29
|
+
return { kind: "request-gate", index };
|
|
30
|
+
return { kind: "run-stage", index };
|
|
31
|
+
}
|
|
32
|
+
/** The view after the fact, at the time `at`. */
|
|
33
|
+
export function apply(view, fact, at) {
|
|
34
|
+
const next = { ...view, stages: view.stages.map((stage) => ({ ...stage })) };
|
|
35
|
+
const end = (state) => ({ ...next, state, endedAt: at });
|
|
36
|
+
switch (fact.kind) {
|
|
37
|
+
case "gate":
|
|
38
|
+
return gate(next, fact, end);
|
|
39
|
+
case "stage-started": {
|
|
40
|
+
Object.assign(next.stages[fact.index], { state: "running", startedAt: at });
|
|
41
|
+
return next;
|
|
42
|
+
}
|
|
43
|
+
case "stage-ended": {
|
|
44
|
+
const state = stageStateOf(fact.stopReason);
|
|
45
|
+
const stage = next.stages[fact.index];
|
|
46
|
+
Object.assign(stage, { state, endedAt: at }, fact.sessionId ? { sessionId: fact.sessionId } : {});
|
|
47
|
+
if (state !== "succeeded")
|
|
48
|
+
return end(state === "cancelled" ? "cancelled" : "failed");
|
|
49
|
+
return fact.index === next.stages.length - 1 ? end("complete") : next;
|
|
50
|
+
}
|
|
51
|
+
case "cancelled":
|
|
52
|
+
case "error": {
|
|
53
|
+
const stopped = fact.kind === "cancelled" ? "cancelled" : "failed";
|
|
54
|
+
for (const stage of next.stages)
|
|
55
|
+
if (stage.state === "running" || stage.state === "blocked")
|
|
56
|
+
Object.assign(stage, { state: stopped, endedAt: at });
|
|
57
|
+
return hasEnded(view) ? view : end(fact.kind === "cancelled" ? "cancelled" : "failed");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function gate(next, fact, end) {
|
|
62
|
+
const stage = next.stages[fact.index];
|
|
63
|
+
stage.gateId = fact.gateId;
|
|
64
|
+
if (fact.state === "pending") {
|
|
65
|
+
stage.state = "blocked";
|
|
66
|
+
return { ...next, state: "blocked" };
|
|
67
|
+
}
|
|
68
|
+
if (fact.state === "approved") {
|
|
69
|
+
stage.state = "pending";
|
|
70
|
+
return { ...next, state: "running" };
|
|
71
|
+
}
|
|
72
|
+
stage.state = "rejected";
|
|
73
|
+
return end("failed");
|
|
74
|
+
}
|
|
75
|
+
/** How a turn's stop reason ends its stage (harness port): only `end_turn` succeeds. */
|
|
76
|
+
export function stageStateOf(stopReason) {
|
|
77
|
+
if (stopReason === "end_turn")
|
|
78
|
+
return "succeeded";
|
|
79
|
+
if (stopReason === "cancelled")
|
|
80
|
+
return "cancelled";
|
|
81
|
+
return "failed";
|
|
82
|
+
}
|
|
83
|
+
/** The prompt of a stage: the agent's prompt, then the work item. */
|
|
84
|
+
export function promptOf(agentPrompt, workItem) {
|
|
85
|
+
const description = workItem.description ? `\n\n${workItem.description}` : "";
|
|
86
|
+
return `${agentPrompt.trimEnd()}\n\n# Work item ${workItem.id}\n\n${workItem.title}${description}\n`;
|
|
87
|
+
}
|
|
88
|
+
/** The signal in a request's body, or the problem with it as "<JSON Pointer>: <what is wrong>". */
|
|
89
|
+
export function signalOf(body) {
|
|
90
|
+
const { type, message } = (typeof body === "object" && body !== null ? body : {});
|
|
91
|
+
if (type === "cancel")
|
|
92
|
+
return { type };
|
|
93
|
+
if (type !== "steer")
|
|
94
|
+
return `/type: must be "steer" or "cancel"`;
|
|
95
|
+
if (typeof message !== "string" || message === "")
|
|
96
|
+
return "/message: a steering message needs the text";
|
|
97
|
+
return { type, message };
|
|
98
|
+
}
|
|
99
|
+
/** The summary gatekeeper shows the approvers. */
|
|
100
|
+
export const gateSummaryOf = (view, index) => `${view.workItem.title}: start the stage ${view.stages[index]?.stage}`;
|
|
101
|
+
const SOURCE = "/osf/foreman";
|
|
102
|
+
export function runStarted(view) {
|
|
103
|
+
return {
|
|
104
|
+
specversion: "1.0",
|
|
105
|
+
id: `${view.runId}.started`,
|
|
106
|
+
source: SOURCE,
|
|
107
|
+
type: "run.started@v1",
|
|
108
|
+
time: view.startedAt,
|
|
109
|
+
subject: view.runId,
|
|
110
|
+
datacontenttype: "application/json",
|
|
111
|
+
data: {
|
|
112
|
+
runId: view.runId,
|
|
113
|
+
workItemId: view.workItem.id,
|
|
114
|
+
factory: view.factory,
|
|
115
|
+
factoryVersion: view.factoryVersion,
|
|
116
|
+
stages: view.stages.map((stage) => stage.stage),
|
|
117
|
+
startedAt: view.startedAt,
|
|
118
|
+
requester: view.workItem.requester,
|
|
119
|
+
source: view.workItem.origin.source,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export function runCompleted(view) {
|
|
124
|
+
const endedAt = view.endedAt ?? view.startedAt;
|
|
125
|
+
const last = [...view.stages].reverse().find((stage) => stage.state !== "pending");
|
|
126
|
+
return {
|
|
127
|
+
specversion: "1.0",
|
|
128
|
+
id: `${view.runId}.completed`,
|
|
129
|
+
source: SOURCE,
|
|
130
|
+
type: "run.completed@v1",
|
|
131
|
+
time: endedAt,
|
|
132
|
+
subject: view.runId,
|
|
133
|
+
datacontenttype: "application/json",
|
|
134
|
+
data: {
|
|
135
|
+
runId: view.runId,
|
|
136
|
+
workItemId: view.workItem.id,
|
|
137
|
+
...(last ? { stage: last.stage } : {}),
|
|
138
|
+
factoryVersion: view.factoryVersion,
|
|
139
|
+
outcome: view.state === "complete" ? "succeeded" : view.state === "cancelled" ? "cancelled" : "failed",
|
|
140
|
+
startedAt: view.startedAt,
|
|
141
|
+
endedAt,
|
|
142
|
+
requester: view.workItem.requester,
|
|
143
|
+
source: view.workItem.origin.source,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/domain/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/domain/time.ts"],"names":[],"mappings":"AAAA,qHAAqH;AAErH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAyB,EAAU,EAAE,CAC1D,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// The activities of a run (ADR 0067). `next` and `apply` are local activities around the domain, so a run's logic runs
|
|
2
|
+
// in the worker's thread; the others reach outside: the sandbox, the harness, gatekeeper and NATS.
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { Context } from "@temporalio/activity";
|
|
6
|
+
import { startTurn } from "../adapters/harness.js";
|
|
7
|
+
import { SUBJECTS } from "../adapters/nats.js";
|
|
8
|
+
import { createSandbox } from "../adapters/sandbox.js";
|
|
9
|
+
import { apply, gateSummaryOf, nextStep, promptOf, runCompleted, runStarted, } from "../domain/run.js";
|
|
10
|
+
/** The command line of an adapter by its name, as program and arguments. */
|
|
11
|
+
const commandLine = (lines, name, otherwise) => (lines[name] ?? otherwise).split(" ");
|
|
12
|
+
export function activities(deps) {
|
|
13
|
+
/** The turn's stop reason; meanwhile it sends the run's new steering messages and reports that the stage lives. */
|
|
14
|
+
async function untilDone(turn, runId, steered) {
|
|
15
|
+
const context = Context.current();
|
|
16
|
+
context.cancellationSignal.addEventListener("abort", () => turn.cancel());
|
|
17
|
+
let sent = steered;
|
|
18
|
+
const watch = setInterval(() => {
|
|
19
|
+
context.heartbeat();
|
|
20
|
+
deps.steering(runId).then((messages) => {
|
|
21
|
+
for (const message of messages.slice(sent))
|
|
22
|
+
turn.steer(message);
|
|
23
|
+
sent = Math.max(sent, messages.length);
|
|
24
|
+
}, () => undefined);
|
|
25
|
+
}, deps.pollMs);
|
|
26
|
+
try {
|
|
27
|
+
return await turn.done;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
clearInterval(watch);
|
|
31
|
+
await turn.close();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
async next(view, definition) {
|
|
36
|
+
return nextStep(view, definition);
|
|
37
|
+
},
|
|
38
|
+
async apply(view, fact) {
|
|
39
|
+
return apply(view, fact, deps.now());
|
|
40
|
+
},
|
|
41
|
+
async publishStarted(view) {
|
|
42
|
+
await deps.bus?.publish(SUBJECTS.runStarted, runStarted(view));
|
|
43
|
+
},
|
|
44
|
+
async publishCompleted(view) {
|
|
45
|
+
await deps.bus?.publish(SUBJECTS.runCompleted, runCompleted(view));
|
|
46
|
+
},
|
|
47
|
+
async requestGate(view, definition, index) {
|
|
48
|
+
const plan = definition.stages[index];
|
|
49
|
+
const origin = view.workItem.origin.url;
|
|
50
|
+
const gate = await deps.gatekeeper.request({
|
|
51
|
+
workItem: view.workItem.id,
|
|
52
|
+
stage: plan?.stage ?? "",
|
|
53
|
+
summary: gateSummaryOf(view, index),
|
|
54
|
+
...(origin ? { links: [{ url: origin, title: "Origin" }] } : {}),
|
|
55
|
+
change: { factory: definition.factory, policy: plan?.gate?.policy, approvers: plan?.gate?.approvers ?? [] },
|
|
56
|
+
});
|
|
57
|
+
return { kind: "gate", index, gateId: gate.id, state: gate.state };
|
|
58
|
+
},
|
|
59
|
+
/** Runs the stage's agent, in a sandbox when it declares one; steering messages from the `steered`-th on are for it. */
|
|
60
|
+
async runStage(view, definition, index, steered) {
|
|
61
|
+
const plan = definition.stages[index];
|
|
62
|
+
if (!plan)
|
|
63
|
+
throw new Error(`no stage ${index}`);
|
|
64
|
+
const cwd = join(deps.data, "runs", view.runId);
|
|
65
|
+
mkdirSync(cwd, { recursive: true });
|
|
66
|
+
const attributes = {
|
|
67
|
+
"osf.run.id": view.runId,
|
|
68
|
+
"osf.work_item.id": view.workItem.id,
|
|
69
|
+
"osf.stage": plan.stage,
|
|
70
|
+
"osf.factory.version": view.factoryVersion,
|
|
71
|
+
};
|
|
72
|
+
const resource = Object.entries(attributes)
|
|
73
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
74
|
+
.join(",");
|
|
75
|
+
const harness = commandLine(deps.harnesses, plan.harness, `npx -y @opensoftware/harness-${plan.harness} run`);
|
|
76
|
+
const adapter = definition.sandboxAdapter;
|
|
77
|
+
const sandbox = plan.sandbox
|
|
78
|
+
? await createSandbox(commandLine(deps.sandboxes, adapter, `npx -y @opensoftware/sandbox-${adapter}`), { ...plan.sandbox, env: { OTEL_RESOURCE_ATTRIBUTES: resource } }, cwd, view.runId, deps.env)
|
|
79
|
+
: undefined;
|
|
80
|
+
try {
|
|
81
|
+
const prompt = promptOf(plan.prompt, view.workItem);
|
|
82
|
+
const turn = await startTurn(sandbox
|
|
83
|
+
? { command: sandbox.exec(harness), cwd, folder: await sandbox.copyIn(), env: deps.env, prompt }
|
|
84
|
+
: {
|
|
85
|
+
command: harness,
|
|
86
|
+
cwd,
|
|
87
|
+
folder: cwd,
|
|
88
|
+
env: {
|
|
89
|
+
...deps.env,
|
|
90
|
+
OSF_DATA: join(deps.data, "harness", plan.harness),
|
|
91
|
+
OTEL_RESOURCE_ATTRIBUTES: resource,
|
|
92
|
+
},
|
|
93
|
+
prompt,
|
|
94
|
+
});
|
|
95
|
+
const stopReason = await untilDone(turn, view.runId, steered);
|
|
96
|
+
await sandbox?.copyOut();
|
|
97
|
+
return { kind: "stage-ended", index, stopReason, sessionId: turn.sessionId };
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
await sandbox?.destroy();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=activities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activities.js","sourceRoot":"","sources":["../../src/workflows/activities.ts"],"names":[],"mappings":"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"}
|