Snapshot only committed extensions and publish the last-good marker atomically

This commit is contained in:
2026-11-08 07:00:39 +00:00
parent fcfaff6018
commit 8afab591fe
38 changed files with 535 additions and 117 deletions
+16 -4
View File
@@ -32712,7 +32712,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -32728,7 +32728,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -32818,8 +32819,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -70720,7 +70720,7 @@ async function loadExtensions(harness2, registry3, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -70736,7 +70736,8 @@ async function loadExtensions(harness2, registry3, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state2), context);
ops2.op_extension_status(JSON.stringify(state2));
await fs2.write("/src/sausage/.git/sausage-last-good", state2.good ?? "");
await fs2.write("/src/sausage/.git/sausage-last-good.tmp", state2.good ?? "");
await fs2.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -70826,8 +70827,19 @@ async function loadExtensions(harness2, registry3, storage2, models2, context, b
try {
await fs2.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs2.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs2.remove(archive);
}
await fs2.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs2.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -31486,7 +31486,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -31502,7 +31502,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -31592,8 +31593,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -30546,7 +30546,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -30562,7 +30562,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries3) => {
for (const facet of entries3) for (const cleanup of facet.close) {
@@ -30652,8 +30653,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -31486,7 +31486,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -31502,7 +31502,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -31592,8 +31593,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+35 -5
View File
@@ -31486,7 +31486,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -31502,7 +31502,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -31592,8 +31593,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
@@ -31720,6 +31732,9 @@ var harness = await Harness.open(storage, { models, registry, settings: { get ex
} } }, BACKGROUND_CONTEXT);
var root = await harness.root(BACKGROUND_CONTEXT, { agent: { model: { provider: "faux", modelId: "faux-1" } } });
globalThis.shared = { gate: Promise.withResolvers(), started: false, wait: false };
var markerWrite = fs.write;
var markerBlocked = Promise.withResolvers();
var markerRelease = Promise.withResolvers();
var loader = await loadExtensions(harness, registry, storage, models, BACKGROUND_CONTEXT);
harness.resume();
var turn = async () => {
@@ -31783,15 +31798,30 @@ return defineExtension({name:'demo',tools:[defineTool({name:'demo',description:'
globalThis.shared.gate.resolve();
assert((await running).includes("result A"), "Old call must finish on A");
assert((await turn()).includes("result C"), "Next call must use C");
await loader.reload();
fs.write = async (path, text) => {
if (path.includes("sausage-last-good")) {
fs.write = markerWrite;
await markerWrite(path, "");
markerBlocked.resolve();
await markerRelease.promise;
}
return markerWrite(path, text);
};
await git("/src/sausage", "update-ref", "refs/heads/main", b);
await until2(() => loader.state().current === b && !!loader.state().error);
assert((await exec(["extensions-revert"], { cwd: "/" })).code === 0, "Revert command");
await markerBlocked.promise;
const reverted = await exec(["extensions-revert"], { cwd: "/" });
markerRelease.resolve();
assert(reverted.code === 0, `Revert command: ${JSON.stringify(reverted)}`);
await until2(() => loader.state().current === c && !loader.state().error);
await git("/src/sausage", "update-ref", "refs/heads/main", b);
await until2(() => loader.state().current === b && !!loader.state().error);
await fs.write("/src/sausage/good.txt", c);
}
} finally {
markerRelease.resolve();
fs.write = markerWrite;
globalThis.shared.gate.resolve();
await loader.close();
await harness.close(BACKGROUND_CONTEXT);
+15 -2
View File
@@ -12,6 +12,7 @@ const registry=createRegistry(), storage=await openStorage(globalThis.storagePat
const harness=await Harness.open(storage,{models,registry,settings:{get extensions(){return registry.snapshot().installed();}}},context);
const root=await harness.root(context,{agent:{model:{provider:'faux',modelId:'faux-1'}}});
globalThis.shared={gate:Promise.withResolvers(),started:false,wait:false};
const markerWrite=fs.write, markerBlocked=Promise.withResolvers(), markerRelease=Promise.withResolvers();
const loader=await loadExtensions(harness,registry,storage,models,context);
harness.resume();
const turn = async()=> {
@@ -67,12 +68,24 @@ return defineExtension({name:'demo',tools:[defineTool({name:'demo',description:'
globalThis.shared.wait=false;globalThis.shared.gate.resolve();
assert((await running).includes('result A'),'Old call must finish on A');
assert((await turn()).includes('result C'),'Next call must use C');
await loader.reload();
// Suspend a marker write after truncation, as the asynchronous VFS write can do under load.
fs.write=async(path,text)=>{
if(path.includes('sausage-last-good')) {
fs.write=markerWrite;
await markerWrite(path,'');markerBlocked.resolve();await markerRelease.promise;
}
return markerWrite(path,text);
};
await git('/src/sausage','update-ref','refs/heads/main',b);
await until(()=>loader.state().current===b && !!loader.state().error);
assert((await exec(['extensions-revert'],{cwd:'/'})).code===0,'Revert command');
await markerBlocked.promise;
const reverted=await exec(['extensions-revert'],{cwd:'/'});
markerRelease.resolve();
assert(reverted.code===0,`Revert command: ${JSON.stringify(reverted)}`);
await until(()=>loader.state().current===c && !loader.state().error);
await git('/src/sausage','update-ref','refs/heads/main',b);
await until(()=>loader.state().current===b && !!loader.state().error);
await fs.write('/src/sausage/good.txt',c);
}
} finally {globalThis.shared.gate.resolve();await loader.close();await harness.close(context);}
} finally {markerRelease.resolve();fs.write=markerWrite;globalThis.shared.gate.resolve();await loader.close();await harness.close(context);}
+16 -4
View File
@@ -84,11 +84,11 @@ export async function loadExtensions(harness:any, registry:any, storage:any, mod
let settings:any = {};
let active = true;
const commands = new AbortController();
// Snapshot checkout copies the source tree. The tool helper's 30 seconds is too short under load.
// Loader commands have no wall-clock deadline. close() cancels them, even under machine load.
const loaderGit = async (...args:string[]) => {
for (let attempt=0;;attempt++) {
try {
const result = await exec(['git',...args],{cwd:repo,timeout:120,signal:commands.signal});
const result = await exec(['git',...args],{cwd:repo,signal:commands.signal});
if(result.code)throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch(error) {
@@ -104,7 +104,8 @@ export async function loadExtensions(harness:any, registry:any, storage:any, mod
const publish = async () => {
await harness.commit(async (tx:any)=>Object.assign(await tx.doc(ExtensionState),state),context);
ops.op_extension_status(JSON.stringify(state));
await fs.write('/src/sausage/.git/sausage-last-good',state.good ?? '');
await fs.write('/src/sausage/.git/sausage-last-good.tmp',state.good ?? '');
await fs.rename('/src/sausage/.git/sausage-last-good.tmp','/src/sausage/.git/sausage-last-good');
};
const dispose = async (entries:any[]) => { for(const facet of entries) for(const cleanup of facet.close) { try {await cleanup();} catch(error) {ops.op_output(`Extension cleanup: ${String(error)}\n`);} } };
const scope = (name:string, staged:Map<string,any>) => {
@@ -151,7 +152,18 @@ export async function loadExtensions(harness:any, registry:any, storage:any, mod
try {
const snapshot = `/run/sausage/extensions/${sha}`;
for(const path of ['/run/sausage','/run/sausage/extensions']) {try {await fs.mkdir(path);} catch(error) {if((error as any).code!=='already_exists')throw error;} }
try {await fs.stat(snapshot);} catch {await loaderGit('worktree','prune');await loaderGit('worktree','add','--detach',snapshot,sha);}
try {await fs.stat(snapshot);} catch {
// Only publish a complete subtree. A cancelled extraction can leave its staging directory.
const staging=`${snapshot}.tmp`, archive=`${snapshot}.tar`;
try {await fs.mkdir(staging);} catch(error) {if((error as any).code!=='already_exists')throw error;}
if(await loaderGit('ls-tree','--name-only',sha,'extensions')) {
await loaderGit('archive','--format=tar',`--output=${archive}`,sha,'extensions');
const result=await exec(['tar','-xf',archive,'-C',staging],{cwd:repo,signal:commands.signal});
if(result.code)throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging,snapshot);
}
const modules:any[] = [];
for(const entry of await fs.list(`${snapshot}/extensions`).catch(error=>{if(error.code==='not_found')return [];throw error;})) {
if(entry.kind!=='directory')continue;
+16 -4
View File
@@ -32712,7 +32712,7 @@ async function loadExtensions(harness, registry, storage, models, context, built
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -32728,7 +32728,8 @@ async function loadExtensions(harness, registry, storage, models, context, built
const publish2 = async () => {
await harness.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -32818,8 +32819,19 @@ async function loadExtensions(harness, registry, storage, models, context, built
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -31486,7 +31486,7 @@ async function loadExtensions(harness2, registry, storage, models, context, buil
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -31502,7 +31502,8 @@ async function loadExtensions(harness2, registry, storage, models, context, buil
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -31592,8 +31593,19 @@ async function loadExtensions(harness2, registry, storage, models, context, buil
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -30546,7 +30546,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -30562,7 +30562,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries3) => {
for (const facet of entries3) for (const cleanup of facet.close) {
@@ -30652,8 +30653,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot2);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot2, sha);
const staging = `${snapshot2}.tmp`, archive = `${snapshot2}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot2);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot2}/extensions`).catch((error) => {
+16 -4
View File
@@ -30546,7 +30546,7 @@ async function loadExtensions(harness, registry, storage, models, context, built
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -30562,7 +30562,8 @@ async function loadExtensions(harness, registry, storage, models, context, built
const publish2 = async () => {
await harness.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -30652,8 +30653,19 @@ async function loadExtensions(harness, registry, storage, models, context, built
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -31486,7 +31486,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -31502,7 +31502,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -31592,8 +31593,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -30546,7 +30546,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -30562,7 +30562,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -30652,8 +30653,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot2);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot2, sha);
const staging = `${snapshot2}.tmp`, archive = `${snapshot2}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot2);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot2}/extensions`).catch((error) => {
+16 -4
View File
@@ -30546,7 +30546,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -30562,7 +30562,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -30652,8 +30653,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha2);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha2, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha2, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+16 -4
View File
@@ -30546,7 +30546,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const loaderGit = async (...args) => {
for (let attempt = 0; ; attempt++) {
try {
const result = await exec(["git", ...args], { cwd: repo, timeout: 120, signal: commands.signal });
const result = await exec(["git", ...args], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(result.stderr || `git exited ${result.code}`);
return result.stdout.trim();
} catch (error) {
@@ -30562,7 +30562,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
const publish2 = async () => {
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
ops2.op_extension_status(JSON.stringify(state));
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
await fs.write("/src/sausage/.git/sausage-last-good.tmp", state.good ?? "");
await fs.rename("/src/sausage/.git/sausage-last-good.tmp", "/src/sausage/.git/sausage-last-good");
};
const dispose = async (entries2) => {
for (const facet of entries2) for (const cleanup of facet.close) {
@@ -30652,8 +30653,19 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
try {
await fs.stat(snapshot);
} catch {
await loaderGit("worktree", "prune");
await loaderGit("worktree", "add", "--detach", snapshot, sha);
const staging = `${snapshot}.tmp`, archive = `${snapshot}.tar`;
try {
await fs.mkdir(staging);
} catch (error) {
if (error.code !== "already_exists") throw error;
}
if (await loaderGit("ls-tree", "--name-only", sha, "extensions")) {
await loaderGit("archive", "--format=tar", `--output=${archive}`, sha, "extensions");
const result = await exec(["tar", "-xf", archive, "-C", staging], { cwd: repo, signal: commands.signal });
if (result.code) throw new Error(`tar: ${result.stderr || result.code}`);
await fs.remove(archive);
}
await fs.rename(staging, snapshot);
}
const modules = [];
for (const entry2 of await fs.list(`${snapshot}/extensions`).catch((error) => {
+3
View File
@@ -36,6 +36,8 @@ fn setup(directory: &Path) -> Result<()> {
"git",
"-p",
"curl",
"-p",
"sausage-archive-tools",
"--target",
"wasm32-wasip2",
"--target-dir"
@@ -58,6 +60,7 @@ fn setup(directory: &Path) -> Result<()> {
std::fs::create_dir_all(disk.join("bin"))?;
for (source, name) in [
("git.wasm", "git.wasm"),
("tar.wasm", "tar.wasm"),
("curl.wasm", "curl.wasm"),
("brush.wasm", "sh.wasm"),
] {
+9 -1
View File
@@ -141,7 +141,15 @@ pub fn build_git() -> &'static Path {
cwd_init();
assert!(
Command::new("cargo")
.args(["build", "--release", "-p", "git", "--manifest-path"])
.args([
"build",
"--release",
"-p",
"git",
"-p",
"sausage-archive-tools",
"--manifest-path"
])
.arg(userland.join("Cargo.toml"))
.args(["--target", "wasm32-wasip2", "--target-dir"])
.arg(&target)
+6 -4
View File
@@ -52,10 +52,12 @@ async fn core_extensions() -> anyhow::Result<()> {
let world = World::open(dir.path())?;
let disk = dir.path().join("world");
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
let source = disk.join("src/sausage");
for name in ["core", "models", "prompt", "conversations"] {
copy_tree(
+6 -4
View File
@@ -38,10 +38,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let target = crate::common::build_git();
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
copy_tree(&source.join("extensions"), &repo.join("extensions"))?;
std::fs::create_dir_all(repo.join("extensions/probe"))?;
std::fs::write(
+6 -2
View File
@@ -95,8 +95,12 @@ async fn world_extensions() -> anyhow::Result<()> {
let world = World::open(dir.path())?;
let disk = dir.path().join("world");
std::fs::create_dir_all(disk.join("bin"))?;
let git = target.join("wasm32-wasip2/release/git.wasm");
std::fs::copy(git, disk.join("bin/git.wasm"))?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
let repo = disk.join("src/sausage");
std::fs::create_dir_all(&repo)?;
assert!(
+38 -13
View File
@@ -22,7 +22,15 @@ impl Exec for Author {
] {
options.env.push((key.into(), value.into()));
}
self.0.exec(command, options, output).await
let label = format!("{command:?}");
let budget = options.timeout;
let started = std::time::Instant::now();
let result = self.0.exec(command, options, output).await;
eprintln!(
"Extension {label}: deadline {budget:?}, elapsed {:?}, result {result:?}",
started.elapsed()
);
result
}
}
#[tokio::test(flavor = "current_thread")]
@@ -41,6 +49,8 @@ async fn commits_and_restart() -> anyhow::Result<()> {
"git",
"-p",
"sausage-exec-tools",
"-p",
"sausage-archive-tools",
"--manifest-path"
])
.arg(userland.join("Cargo.toml"))
@@ -59,7 +69,7 @@ async fn commits_and_restart() -> anyhow::Result<()> {
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for name in ["extensions-revert", "env"] {
for name in ["extensions-revert", "env", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{name}.wasm")),
disk.join(format!("bin/{name}.wasm")),
@@ -113,7 +123,7 @@ async fn commits_and_restart() -> anyhow::Result<()> {
Ok(())
}
/// Model command time without a wall-clock delay, including work longer than the old 30 second budget.
/// Model loader commands without delays, and require cancellation instead of a wall-clock deadline.
struct LoaderCommand {
root: Arc<dyn sausage_vfs::Layer>,
failures: usize,
@@ -131,18 +141,21 @@ impl Exec for LoaderCommand {
let Command::Argv(args) = command else {
anyhow::bail!("unexpected shell")
};
assert!(
options.timeout.is_none(),
"Loader work must not have a wall-clock deadline"
);
if args[0] == "tar" {
return Ok(0);
}
match args[1].as_str() {
"rev-parse" => output(Stream::Stdout, b"new\n"),
"worktree" if args[2] == "prune" => {}
"worktree" if args[2] == "add" => {
"ls-tree" => output(Stream::Stdout, b"extensions\n"),
"archive" => {
let attempt = self
.attempts
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
if options
.timeout
.is_some_and(|budget| budget < std::time::Duration::from_secs(31))
|| (attempt < self.failures && self.timeout)
{
if attempt < self.failures && self.timeout {
anyhow::bail!("command timed out");
}
if attempt < self.failures {
@@ -159,9 +172,21 @@ impl Exec for LoaderCommand {
"Retry must publish its error: {status}"
);
}
let snapshot = &args[4];
let archive = args[3].strip_prefix("--output=").unwrap();
let file = self
.root
.open(
archive,
sausage_vfs::Open {
write: true,
create: true,
..Default::default()
},
)
.await?;
file.write(0, vec![]).await?;
let snapshot = format!("{}.tmp", archive.strip_suffix(".tar").unwrap());
for path in [
snapshot.clone(),
format!("{snapshot}/extensions"),
format!("{snapshot}/extensions/demo"),
] {
@@ -217,7 +242,7 @@ async fn loader_command(failures: usize, timeout: bool) -> anyhow::Result<()> {
const testState = testLoader.state();
if(testState.good !== '{expected}') throw new Error(JSON.stringify(testState));
if((testState.error !== null) !== ({expected:?} === 'old')) throw new Error(JSON.stringify(testState));
if(testState.error && !testState.error.includes('git worktree add')) throw new Error(testState.error);
if(testState.error && !testState.error.includes('git archive')) throw new Error(testState.error);
await testLoader.close();
await testHarness.close(BACKGROUND_CONTEXT);
"#
+6 -4
View File
@@ -13,10 +13,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let target = crate::common::build_git();
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
crate::common::copy_tree(&source.join("extensions"), &repo.join("extensions"))?;
std::fs::create_dir_all(repo.join("agents"))?;
std::fs::create_dir_all(repo.join("extensions/probe"))?;
+8 -4
View File
@@ -26,6 +26,8 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
"git",
"-p",
"curl",
"-p",
"sausage-archive-tools",
"--manifest-path"
])
.arg(userland.join("Cargo.toml"))
@@ -57,10 +59,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
target.join("wasm32-wasip2/release/harness-api-client.wasm"),
disk.join("bin/harness-api-client.wasm"),
)?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
std::fs::copy(
target.join("wasm32-wasip2/release/curl.wasm"),
disk.join("bin/curl.wasm"),
+6 -4
View File
@@ -17,10 +17,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let target = crate::common::build_git();
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
for directory in [
"extensions/subagents",
"extensions/messages",
+2
View File
@@ -338,6 +338,8 @@ async fn bundled_extensions_load() -> anyhow::Result<()> {
const bundleLoader = await loadExtensions(bundleHarness, bundleRegistry, bundleStorage,
bundleModels, BACKGROUND_CONTEXT, [CodingTools2, CredentialRequests]);
if (bundleLoader.state().error) throw new Error(bundleLoader.state().error);
const snapshotEntries=await fs2.list(`/run/sausage/extensions/${{bundleLoader.state().good}}`);
if(JSON.stringify(snapshotEntries.map(entry=>entry.name)) !== '["extensions"]') throw new Error(JSON.stringify(snapshotEntries));
await bundleLoader.close();
await bundleHarness.close(BACKGROUND_CONTEXT);
"#
+6 -4
View File
@@ -30,10 +30,12 @@ fn prepare(dir: &Path, all_extensions: bool) -> anyhow::Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let target = crate::common::build_git();
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
if !target.join("wasm32-wasip2/release/brush.wasm").exists() {
anyhow::ensure!(
crate::common::build_brush()?.success(),
+6 -1
View File
@@ -24,7 +24,9 @@ fn prepare(dir: &Path) -> anyhow::Result<PathBuf> {
.args(["rev-parse", "HEAD"])
.output()?;
ensure!(revision.status.success(), "fixture revision");
if fs::read(bundle.join("revision")).ok().as_deref() != Some(revision.stdout.as_slice()) {
if fs::read(bundle.join("revision")).ok().as_deref() != Some(revision.stdout.as_slice())
|| !bundle.join("bin/tar.wasm").is_file()
{
if !userland.join("cwd-init/init.o").exists() {
crate::common::cwd_init();
}
@@ -42,6 +44,8 @@ fn prepare(dir: &Path) -> anyhow::Result<PathBuf> {
"undo",
"-p",
"sausage-coreutils",
"-p",
"sausage-archive-tools",
]))?;
ensure!(
crate::common::build_brush()?.success(),
@@ -53,6 +57,7 @@ fn prepare(dir: &Path) -> anyhow::Result<PathBuf> {
("brush", "sh"),
("brush", "bash"),
("git", "git"),
("tar", "tar"),
("cargo", "cargo"),
("box", "box"),
("undo", "undo"),
+6 -4
View File
@@ -10,10 +10,12 @@ async fn plan_todos_and_restart() -> anyhow::Result<()> {
let disk = dir.path().join("world");
let _world = World::open(dir.path())?;
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
let repo = disk.join("src/sausage");
std::fs::create_dir_all(&repo)?;
assert!(
+6 -4
View File
@@ -15,10 +15,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let target = crate::common::build_git();
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
for directory in [
"extensions/subagents",
"extensions/models",
+6 -4
View File
@@ -17,10 +17,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let target = crate::common::build_git();
std::fs::create_dir_all(disk.join("bin"))?;
std::fs::copy(
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for binary in ["git", "tar"] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{binary}.wasm")),
)?;
}
for directory in [
"extensions/subagents",
"extensions/messages",
+1 -1
View File
@@ -34,7 +34,7 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
assert!(crate::common::build_brush()?.success());
}
std::fs::create_dir_all(disk.join("bin"))?;
for (binary, name) in [("git", "git"), ("brush", "sh")] {
for (binary, name) in [("git", "git"), ("tar", "tar"), ("brush", "sh")] {
std::fs::copy(
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
disk.join(format!("bin/{name}.wasm")),