426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
194 lines
5.9 KiB
TypeScript
194 lines
5.9 KiB
TypeScript
// Live end-to-end test that drives the real `codex` CLI to generate an artifact
|
|
// (game.js/index.html/styles.css) and asserts the files land on disk. Gated on
|
|
// the `codex` binary being in PATH and `~/.codex/auth.json` existing, so it
|
|
// skips cleanly otherwise — run via `test:e2e:manual`.
|
|
import { spawn, spawnSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
|
|
const EXPECTED_FILES = ["game.js", "index.html", "styles.css"];
|
|
const REAL_HOME_DIR = os.userInfo().homedir;
|
|
const CODEX_AUTH_PATH = path.join(REAL_HOME_DIR, ".codex", "auth.json");
|
|
|
|
function isCodexCliAvailable(): boolean {
|
|
const probe = spawnSync("codex", ["--version"], {
|
|
encoding: "utf8",
|
|
stdio: "pipe",
|
|
timeout: 5_000,
|
|
});
|
|
return probe.status === 0;
|
|
}
|
|
|
|
const CODEX_AVAILABLE = isCodexCliAvailable();
|
|
const CODEX_AUTH_AVAILABLE = fs.existsSync(CODEX_AUTH_PATH);
|
|
const describeIf =
|
|
CODEX_AVAILABLE && CODEX_AUTH_AVAILABLE ? describe : describe.skip;
|
|
function createIsolatedCodexHome(): string {
|
|
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "eliza-codex-home-"));
|
|
const codexDir = path.join(homeDir, ".codex");
|
|
fs.mkdirSync(codexDir, { recursive: true });
|
|
fs.copyFileSync(CODEX_AUTH_PATH, path.join(codexDir, "auth.json"));
|
|
fs.writeFileSync(
|
|
path.join(codexDir, "config.toml"),
|
|
'model = "gpt-5.4"\n',
|
|
"utf8",
|
|
);
|
|
return homeDir;
|
|
}
|
|
|
|
function runCodexExec(
|
|
workingDirectory: string,
|
|
homeDir: string,
|
|
prompt: string,
|
|
timeoutMs: number,
|
|
): Promise<{ exitCode: number | null; stdout: string; stderr: string }> {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(
|
|
"codex",
|
|
[
|
|
"exec",
|
|
"--model",
|
|
"gpt-5.4",
|
|
"--full-auto",
|
|
"--skip-git-repo-check",
|
|
"--color",
|
|
"never",
|
|
prompt,
|
|
],
|
|
{
|
|
cwd: workingDirectory,
|
|
env: {
|
|
...process.env,
|
|
HOME: homeDir,
|
|
NO_COLOR: "1",
|
|
TERM: "dumb",
|
|
},
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
},
|
|
);
|
|
|
|
let stdout = "";
|
|
let stderr = "";
|
|
const timer = setTimeout(() => {
|
|
child.kill("SIGKILL");
|
|
reject(
|
|
new Error(
|
|
[
|
|
`codex exec timed out after ${timeoutMs}ms`,
|
|
stdout && `stdout:\n${stdout}`,
|
|
stderr && `stderr:\n${stderr}`,
|
|
]
|
|
.filter(Boolean)
|
|
.join("\n\n"),
|
|
),
|
|
);
|
|
}, timeoutMs);
|
|
|
|
child.stdout.setEncoding("utf8");
|
|
child.stderr.setEncoding("utf8");
|
|
child.stdout.on("data", (chunk: string) => {
|
|
stdout += chunk;
|
|
});
|
|
child.stderr.on("data", (chunk: string) => {
|
|
stderr += chunk;
|
|
});
|
|
child.on("error", (error) => {
|
|
clearTimeout(timer);
|
|
reject(error);
|
|
});
|
|
child.on("close", (exitCode) => {
|
|
clearTimeout(timer);
|
|
resolve({ exitCode, stdout, stderr });
|
|
});
|
|
});
|
|
}
|
|
|
|
describeIf("Coding agent Codex artifact generation", () => {
|
|
const cleanupDirs: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const dir of cleanupDirs.splice(0)) {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("creates a browser Tetris game in the expected files", async () => {
|
|
const homeDir = createIsolatedCodexHome();
|
|
const workingDirectory = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), "eliza-codex-tetris-"),
|
|
);
|
|
cleanupDirs.push(homeDir, workingDirectory);
|
|
|
|
const prompt = [
|
|
"Create a browser Tetris game in the current directory.",
|
|
"Write exactly three files at the workspace root: index.html, styles.css, and game.js.",
|
|
"Do not create any other files or folders.",
|
|
"index.html must include elements with ids board, score, and next-piece, and load styles.css and game.js via relative paths.",
|
|
"game.js must implement keyboard controls for ArrowLeft, ArrowRight, ArrowDown, ArrowUp, and Space.",
|
|
"Render the score into #score and the next-piece preview into #next-piece.",
|
|
"Use plain browser JavaScript only.",
|
|
].join(" ");
|
|
|
|
const result = await runCodexExec(
|
|
workingDirectory,
|
|
homeDir,
|
|
prompt,
|
|
300_000,
|
|
);
|
|
const resultOutput = [result.stdout, result.stderr]
|
|
.filter(Boolean)
|
|
.join("\n\n");
|
|
|
|
expect(result.exitCode, resultOutput).toBe(0);
|
|
|
|
const entries = fs
|
|
.readdirSync(workingDirectory)
|
|
.filter((entry) => !entry.startsWith("."))
|
|
.sort();
|
|
expect(entries).toEqual(EXPECTED_FILES);
|
|
|
|
const indexHtml = fs.readFileSync(
|
|
path.join(workingDirectory, "index.html"),
|
|
"utf8",
|
|
);
|
|
const stylesCss = fs.readFileSync(
|
|
path.join(workingDirectory, "styles.css"),
|
|
"utf8",
|
|
);
|
|
const gameJs = fs.readFileSync(
|
|
path.join(workingDirectory, "game.js"),
|
|
"utf8",
|
|
);
|
|
|
|
expect(indexHtml).toMatch(/id=["']board["']/i);
|
|
expect(indexHtml).toMatch(/id=["']score["']/i);
|
|
expect(indexHtml).toMatch(/id=["']next-piece["']/i);
|
|
expect(indexHtml).toMatch(/href=["']\.?\/?styles\.css["']/i);
|
|
expect(indexHtml).toMatch(/src=["']\.?\/?game\.js["']/i);
|
|
|
|
expect(stylesCss).toMatch(/[#.]board\b/i);
|
|
// Codex sometimes styles the preview container class instead of the exact id.
|
|
expect(stylesCss).toMatch(/(?:[#.]next-piece\b|[.]preview\b)/i);
|
|
|
|
expect(gameJs).toMatch(/ArrowLeft/);
|
|
expect(gameJs).toMatch(/ArrowRight/);
|
|
expect(gameJs).toMatch(/ArrowDown/);
|
|
expect(gameJs).toMatch(/ArrowUp/);
|
|
expect(gameJs).toMatch(/Space|["'] ["']/);
|
|
expect(gameJs).toMatch(/getElementById\(["']score["']\)/);
|
|
expect(gameJs).toMatch(/getElementById\(["']next-piece["']\)/);
|
|
|
|
const syntaxCheck = spawnSync(
|
|
process.execPath,
|
|
["--check", path.join(workingDirectory, "game.js")],
|
|
{ encoding: "utf8" },
|
|
);
|
|
expect(
|
|
syntaxCheck.status,
|
|
[syntaxCheck.stdout, syntaxCheck.stderr].filter(Boolean).join("\n\n"),
|
|
).toBe(0);
|
|
}, 360_000);
|
|
});
|