mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
* feat(ai): add host coding agent sessions with configured providers * Wait for agent initialization and shutdown before resuming sessions * Keep Pi transcripts outside its configuration migration directory * Cover Pi session directory separation * Update core permission catalog expectations for remote agents * Install remote agent runtimes from verified official sources * Wait for the identity panel credentials request in its test * Add scoped SSH forwarding setup for remote agents * Verify forwarding setup through a fresh SSH connection * Bound the SSH forwarding verification timeout * Return the forwarding verification callback result * fix(ai): support agent send and interrupt keyboard shortcuts * feat(ai): add agent queues attachments worktrees and session recovery * fix(ai): handle deleted workspace paths and duplicate status events * fix(ai): accept file references in root workspaces * fix(ai): refresh paused queues after agent errors * fix(ai): store agent sessions in plugin tables and lock down OpenCode Agent sessions lived in a single ctx.kv value holding up to 2000 events, which passed the 256 KB kv limit within minutes of a normal run. Every save after that failed, so history, nativeId, queue and archive changes were lost and every event wrote a kv audit row. Sessions, events and queued prompts now have their own tables. A running agent appends events in batches at most once a second with a lazy save; session and queue changes save immediately. Prompts, drafts and stored event text are capped in UTF-8 bytes to fit MySQL TEXT. kv:own is no longer requested. The OpenCode server now requires a per-session password, so other local accounts on the host cannot drive the agent through its loopback port. * test(upgrade): include agent session tables in the reviewed upgrade SQL
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { mkdtemp, mkdir, writeFile, rm, readFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { execFileSync } from "node:child_process";
|
|
import { installScript } from "../../../src/backend/agents/install.js";
|
|
|
|
it("refuses corrupted Node.js downloads before extracting or installing", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "termix-install-"));
|
|
try {
|
|
const bin = join(dir, "bin");
|
|
await mkdir(bin);
|
|
await writeFile(
|
|
join(bin, "curl"),
|
|
`#!/bin/sh
|
|
for arg do previous="$last"; last="$arg"; done
|
|
case "$last" in
|
|
*SHASUMS256.txt) printf '%064d node-v24.1.0-linux-x64.tar.gz\\n' 0 > "$last";;
|
|
*) echo corrupted > "$last";;
|
|
esac
|
|
`,
|
|
{ mode: 0o700 },
|
|
);
|
|
await writeFile(
|
|
join(bin, "uname"),
|
|
'#!/bin/sh\nif [ "$1" = -s ]; then echo Linux; else echo x86_64; fi\n',
|
|
{ mode: 0o700 },
|
|
);
|
|
let output = "";
|
|
try {
|
|
execFileSync("sh", ["-c", installScript("pi")], {
|
|
env: { ...process.env, HOME: dir, PATH: `${bin}:${process.env.PATH}` },
|
|
stdio: "pipe",
|
|
});
|
|
} catch (error) {
|
|
output = String((error as { stdout: Buffer }).stdout);
|
|
}
|
|
expect(output).toContain("Node.js SHA-256 verification failed");
|
|
await expect(
|
|
readFile(join(dir, ".local/share/termix-agent-runtime/node/bin/node")),
|
|
).rejects.toThrow();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("rejects a mirror dependency before npm ci", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "termix-registry-"));
|
|
try {
|
|
const script = installScript("pi")
|
|
.split("node - <<'VERIFY'\n")[1]
|
|
.split("\nVERIFY")[0];
|
|
await writeFile(
|
|
join(dir, "package-lock.json"),
|
|
JSON.stringify({
|
|
packages: {
|
|
"node_modules/injected": {
|
|
version: "1.0.0",
|
|
resolved: "https://mirror.invalid/pkg.tgz",
|
|
integrity: "sha512-fake",
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
expect(() =>
|
|
execFileSync(process.execPath, ["-e", script], {
|
|
cwd: dir,
|
|
stdio: "pipe",
|
|
}),
|
|
).toThrow();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|