| import { execFile } from "node:child_process"; |
| import { randomUUID } from "node:crypto"; |
| import { constants } from "node:fs"; |
| import fs from "node:fs/promises"; |
| import path from "node:path"; |
| import { promisify } from "node:util"; |
| import { afterEach, describe, expect, it } from "vitest"; |
| import { collectRootPackageExcludedExtensionDirs } from "../scripts/lib/bundled-plugin-build-entries.mjs"; |
| import { isLiveTestEnabled } from "../src/agents/live-test-helpers.js"; |
| import type { AgentExecEnvelope } from "../src/commands/agent-exec-result.js"; |
| import { useAutoCleanupTempDirTracker } from "./helpers/temp-dir.js"; |
|
|
| const execFileAsync = promisify(execFile); |
| const tempDirs = useAutoCleanupTempDirTracker(afterEach); |
| const openAiApiKey = process.env.OPENAI_API_KEY?.trim() ?? ""; |
| const describeLive = isLiveTestEnabled() && openAiApiKey.length > 0 ? describe : describe.skip; |
|
|
| describeLive("agent exec Code Mode with environment authentication", () => { |
| it("starts without configuration and completes dependent filesystem calls", async () => { |
| const root = tempDirs.make("openclaw-agent-exec-code-mode-live-"); |
| const home = path.join(root, "home"); |
| const stateDir = path.join(root, "state"); |
| const workspace = path.join(root, "workspace"); |
| const tmpDir = path.join(root, "tmp"); |
| await Promise.all([home, stateDir, workspace, tmpDir].map((dir) => fs.mkdir(dir))); |
| const repoRoot = path.resolve(import.meta.dirname, ".."); |
| const installedRoot = path.join(root, "node_modules", "openclaw"); |
| const sourceDist = path.join(repoRoot, "dist"); |
| const excludedPlugins = collectRootPackageExcludedExtensionDirs({ cwd: repoRoot }); |
| await fs.mkdir(installedRoot, { recursive: true }); |
| await fs.copyFile( |
| path.join(repoRoot, "package.json"), |
| path.join(installedRoot, "package.json"), |
| ); |
| |
| |
| await fs.cp(sourceDist, path.join(installedRoot, "dist"), { |
| recursive: true, |
| mode: constants.COPYFILE_FICLONE, |
| filter: (source) => { |
| const [directory, pluginId] = path.relative(sourceDist, source).split(path.sep); |
| return ( |
| directory !== "extensions" || pluginId === undefined || !excludedPlugins.has(pluginId) |
| ); |
| }, |
| }); |
| await fs.symlink( |
| await fs.realpath(path.join(repoRoot, "node_modules")), |
| path.join(installedRoot, "node_modules"), |
| process.platform === "win32" ? "junction" : "dir", |
| ); |
| const input = `code-mode-${randomUUID()}\n`; |
| await fs.writeFile(path.join(workspace, "input.txt"), input); |
|
|
| |
| |
| const env: NodeJS.ProcessEnv = { |
| PATH: process.env.PATH, |
| SystemRoot: process.env.SystemRoot, |
| HOME: home, |
| USERPROFILE: home, |
| TMPDIR: tmpDir, |
| TMP: tmpDir, |
| TEMP: tmpDir, |
| OPENCLAW_STATE_DIR: stateDir, |
| OPENAI_API_KEY: openAiApiKey, |
| }; |
| let stdout: string; |
| let stderr: string; |
| try { |
| ({ stdout, stderr } = await execFileAsync( |
| process.execPath, |
| [ |
| path.join(installedRoot, "dist", "entry.js"), |
| "agent", |
| "exec", |
| "Use Code Mode and the filesystem read and write tools to read input.txt, then write " + |
| "output.txt containing exactly the input bytes followed by processed and a newline. " + |
| "Do not use a shell command. Reply with exactly DONE after the write completes.", |
| "--auth-env-only", |
| "--model", |
| "openai/gpt-5.6-luna", |
| "--code-mode", |
| "code", |
| "--local-model-lean", |
| "--thinking", |
| "low", |
| "--cwd", |
| workspace, |
| "--state-dir", |
| stateDir, |
| "--timeout", |
| "180", |
| "--json", |
| ], |
| { |
| cwd: workspace, |
| env, |
| encoding: "utf8", |
| maxBuffer: 2 * 1024 * 1024, |
| timeout: 300_000, |
| }, |
| )); |
| } catch (error) { |
| |
| |
| const message = error instanceof Error ? error.message : String(error); |
| |
| throw new Error(message.replaceAll(openAiApiKey, "[REDACTED]")); |
| } |
|
|
| expect(stdout.includes(openAiApiKey) || stderr.includes(openAiApiKey)).toBe(false); |
| const result = JSON.parse(stdout) as AgentExecEnvelope; |
| expect(result).toMatchObject({ |
| ok: true, |
| status: "ok", |
| provider: "openai", |
| model: "gpt-5.6-luna", |
| codeModeEngaged: true, |
| final: "DONE", |
| }); |
| expect(result.toolSummary?.tools).toContain("exec"); |
| expect(result.bridgeCalls?.call).toBeGreaterThanOrEqual(2); |
| await expect(fs.readFile(path.join(workspace, "output.txt"), "utf8")).resolves.toBe( |
| `${input}processed\n`, |
| ); |
| }, 330_000); |
| }); |
|
|