@@ -8,14 +8,15 @@ import { OpenCode } from "@opencode/client";
|
||||
import { command } from "../src/multi-work/herdr.js";
|
||||
import { z } from "zod";
|
||||
|
||||
const binary = process.env.OPENCODE_2022;
|
||||
if (!binary) throw new Error("Set OPENCODE_2022 to an isolated OpenCode 2.0.22 binary");
|
||||
const binary = process.env["OPENCODE_2022"];
|
||||
if (binary === undefined || binary === "")
|
||||
throw new Error("Set OPENCODE_2022 to an isolated OpenCode 2.0.22 binary");
|
||||
const temp = await mkdtemp(join(tmpdir(), "native-multi-work-"));
|
||||
try {
|
||||
const root = join(temp, "root"),
|
||||
destination = join(temp, "child"),
|
||||
bin = join(temp, "bin"),
|
||||
config = join(temp, "config");
|
||||
const root = join(temp, "root");
|
||||
const destination = join(temp, "child");
|
||||
const bin = join(temp, "bin");
|
||||
const config = join(temp, "config");
|
||||
const herdrCreation = join(temp, "herdr-creation.txt");
|
||||
for (const path of [root, bin, config, join(temp, "plugin")]) await mkdir(path);
|
||||
const signal = AbortSignal.timeout(120_000);
|
||||
@@ -82,7 +83,7 @@ try {
|
||||
target: "bun",
|
||||
format: "esm",
|
||||
});
|
||||
if (!built.success) throw new Error(String(built.logs));
|
||||
if (!built.success) throw new Error(built.logs.map((log) => log.message).join(","));
|
||||
}
|
||||
await writeFile(
|
||||
join(config, "opencode.json"),
|
||||
@@ -134,12 +135,18 @@ try {
|
||||
},
|
||||
});
|
||||
const socket = createServer();
|
||||
await new Promise<void>((done) => socket.listen(0, "127.0.0.1", done));
|
||||
await new Promise<void>((done) => {
|
||||
socket.listen(0, "127.0.0.1", done);
|
||||
});
|
||||
const { port } = z.object({ port: z.number() }).parse(socket.address());
|
||||
await new Promise<void>((done) => socket.close(() => done()));
|
||||
await new Promise<void>((done) => {
|
||||
socket.close(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
// Do not inherit user credentials, discovery URLs, Herdr identity, or config overrides.
|
||||
const env = {
|
||||
PATH: `${bin}:${process.env.PATH}`,
|
||||
PATH: `${bin}:${process.env["PATH"]}`,
|
||||
HOME: join(temp, "home"),
|
||||
XDG_CONFIG_HOME: config,
|
||||
XDG_DATA_HOME: join(temp, "data"),
|
||||
@@ -160,13 +167,13 @@ try {
|
||||
let server = start();
|
||||
const headers = { authorization: "" };
|
||||
const client = OpenCode.make({ baseUrl: `http://127.0.0.1:${port}`, headers });
|
||||
async function ready() {
|
||||
const ready = async () => {
|
||||
for (let attempt = 0; attempt < 200; attempt++) {
|
||||
try {
|
||||
const password = (await readFile(join(temp, "server.log"), "utf8")).match(
|
||||
/server password (\S+)/,
|
||||
/server password (\S+)/u,
|
||||
)?.[1];
|
||||
if (password)
|
||||
if (password !== undefined)
|
||||
headers.authorization = `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`;
|
||||
await client.server.info();
|
||||
return;
|
||||
@@ -177,7 +184,7 @@ try {
|
||||
throw new Error(
|
||||
`Host did not start: ${await readFile(join(temp, "server-errors.log"), "utf8")}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
try {
|
||||
await ready();
|
||||
const location = { directory: root };
|
||||
@@ -228,7 +235,7 @@ try {
|
||||
(await readFile(env.FIXTURE_TRACE, "utf8"))
|
||||
.trim()
|
||||
.split("\n")
|
||||
.map((line) => JSON.parse(line)),
|
||||
.map((line) => z.json().parse(JSON.parse(line))),
|
||||
);
|
||||
if (
|
||||
!admission.some((entry) => entry.id === child.id && entry.directory === destination) ||
|
||||
@@ -305,21 +312,21 @@ try {
|
||||
pending.then(() => {
|
||||
throw new Error("Foreground unexpectedly finished before cancellation");
|
||||
}),
|
||||
new Promise<never>((_resolve, reject) =>
|
||||
new Promise<never>((_resolve, reject) => {
|
||||
deadline.addEventListener(
|
||||
"abort",
|
||||
() => reject(new Error("Model was not reached")),
|
||||
{
|
||||
once: true,
|
||||
() => {
|
||||
reject(new Error("Model was not reached"));
|
||||
},
|
||||
),
|
||||
),
|
||||
{ once: true },
|
||||
);
|
||||
}),
|
||||
]);
|
||||
cancel.abort();
|
||||
let rejected = false;
|
||||
await pending.catch(() => {
|
||||
rejected = true;
|
||||
});
|
||||
const rejected = await pending.then(
|
||||
() => false,
|
||||
() => true,
|
||||
);
|
||||
if (!rejected) throw new Error("Cancelled RPC did not reject");
|
||||
for (let attempt = 0; attempt < 100; attempt++) {
|
||||
if ((await client.session.get({ sessionID: child.id })).outcome === "interrupted")
|
||||
@@ -329,6 +336,18 @@ try {
|
||||
}
|
||||
releaseModel.resolve();
|
||||
holdModel = false;
|
||||
const multiWorkFailed = async () => {
|
||||
for (let attempt = 0; attempt < 100; attempt++) {
|
||||
const { data } = await client.plugin.list({ location });
|
||||
const multi = data.find((plugin) => plugin.id === "stanp-opencode.multi-work");
|
||||
if (multi?.state.status === "failed") return;
|
||||
if (attempt === 99 || multi?.state.status === "active")
|
||||
throw new Error(
|
||||
`Missing or late Agents did not fail Multi-work activation: ${JSON.stringify(data)}`,
|
||||
);
|
||||
await Bun.sleep(100);
|
||||
}
|
||||
};
|
||||
for (const plugins of [
|
||||
[{ package: join(temp, "plugin", "multi-work") }],
|
||||
[
|
||||
@@ -344,16 +363,7 @@ try {
|
||||
);
|
||||
server = start();
|
||||
await ready();
|
||||
for (let attempt = 0; attempt < 100; attempt++) {
|
||||
const { data } = await client.plugin.list({ location });
|
||||
const multi = data.find((plugin) => plugin.id === "stanp-opencode.multi-work");
|
||||
if (multi?.state.status === "failed") break;
|
||||
if (attempt === 99 || multi?.state.status === "active")
|
||||
throw new Error(
|
||||
`Missing or late Agents did not fail Multi-work activation: ${JSON.stringify(data)}`,
|
||||
);
|
||||
await Bun.sleep(100);
|
||||
}
|
||||
await multiWorkFailed();
|
||||
}
|
||||
server.kill();
|
||||
await server.exited;
|
||||
@@ -364,7 +374,8 @@ try {
|
||||
target: "bun",
|
||||
format: "esm",
|
||||
});
|
||||
if (!quotaBuild.success) throw new Error(String(quotaBuild.logs));
|
||||
if (!quotaBuild.success)
|
||||
throw new Error(quotaBuild.logs.map((log) => log.message).join(","));
|
||||
await writeFile(
|
||||
join(config, "opencode.json"),
|
||||
JSON.stringify({
|
||||
@@ -409,7 +420,7 @@ try {
|
||||
releaseModel.resolve();
|
||||
server.kill();
|
||||
await server.exited;
|
||||
model.stop(true);
|
||||
void model.stop(true);
|
||||
}
|
||||
} finally {
|
||||
await rm(temp, { recursive: true, force: true });
|
||||
|
||||
Reference in New Issue
Block a user