Files
AgentCompany/packages/adapter-codex-local/src/server/execute-helpers.ts
T
ZacharyZhang-NY b772e1c758 Build the current workspace snapshot on native CI runners
This branch snapshots the in-progress desktop workspace so GitHub-hosted Windows and Linux runners can package the same source tree that already passes local verification on macOS.

Constraint: Local Apple Silicon Docker/Wine packaging crashes before Windows artifacts are produced
Rejected: Publish from the incomplete base repo state | remote typecheck failed because current source additions were missing
Confidence: medium
Scope-risk: broad
Directive: Treat this branch as a build snapshot only; do not merge it without reviewing the unrelated workspace changes it contains
Tested: local typecheck, lint, workspace typecheck, unit tests, integration tests, e2e smoke, macOS signed release upload
Not-tested: Native Windows/Linux artifacts before this CI run
2026-04-04 03:28:14 -04:00

96 lines
3.3 KiB
TypeScript

import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import type { AdapterExecutionContext, ManagedSkillRef } from "@agentcompany/adapter-utils";
import {
createManagedSkillSymlinks,
} from "@agentcompany/adapter-utils/server-utils";
const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
export const SKILLS_CANDIDATES = [
path.resolve(__moduleDir, "../../skills"), // published: <pkg>/dist/server/ -> <pkg>/skills/
path.resolve(__moduleDir, "../../../../../skills"), // dev: src/server/ -> repo root/skills/
];
export const CODEX_ROLLOUT_NOISE_RE =
/^\d{4}-\d{2}-\d{2}T[^\s]+\s+ERROR\s+codex_core::rollout::list:\s+state db missing rollout path for thread\s+[a-z0-9-]+$/i;
export function stripCodexRolloutNoise(text: string): string {
const parts = text.split(/\r?\n/);
const kept: string[] = [];
for (const part of parts) {
const trimmed = part.trim();
if (!trimmed) {
kept.push(part);
continue;
}
if (CODEX_ROLLOUT_NOISE_RE.test(trimmed)) continue;
kept.push(part);
}
return kept.join("\n");
}
export function hasNonEmptyEnvValue(env: Record<string, string>, key: string): boolean {
const raw = env[key];
return typeof raw === "string" && raw.trim().length > 0;
}
export function resolveCodexBillingType(env: Record<string, string>): "api" | "subscription" {
// Codex uses API-key auth when OPENAI_API_KEY is present; otherwise rely on local login/session auth.
return hasNonEmptyEnvValue(env, "OPENAI_API_KEY") ? "api" : "subscription";
}
export function codexHomeDir(): string {
const fromEnv = process.env.CODEX_HOME;
if (typeof fromEnv === "string" && fromEnv.trim().length > 0) return fromEnv.trim();
return path.join(os.homedir(), ".codex");
}
export async function resolveAgentCompanySkillsDir(): Promise<string | null> {
for (const candidate of SKILLS_CANDIDATES) {
const isDir = await fs.stat(candidate).then((s) => s.isDirectory()).catch(() => false);
if (isDir) return candidate;
}
return null;
}
export async function ensureCodexSkillsInjected(
managedSkills: ManagedSkillRef[],
onLog: AdapterExecutionContext["onLog"],
) {
const skillsHome = path.join(codexHomeDir(), "skills");
// Use managed skills when available; fall back to static repo scan
if (managedSkills.length > 0) {
await createManagedSkillSymlinks(managedSkills, skillsHome, onLog);
return;
}
const skillsDir = await resolveAgentCompanySkillsDir();
if (!skillsDir) return;
await fs.mkdir(skillsHome, { recursive: true });
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const source = path.join(skillsDir, entry.name);
const target = path.join(skillsHome, entry.name);
const existing = await fs.lstat(target).catch(() => null);
if (existing) continue;
try {
await fs.symlink(source, target);
await onLog(
"stderr",
`[agent-company] Injected Codex skill "${entry.name}" into ${skillsHome}\n`,
);
} catch (err) {
await onLog(
"stderr",
`[agent-company] Failed to inject Codex skill "${entry.name}" into ${skillsHome}: ${err instanceof Error ? err.message : String(err)}\n`,
);
}
}
}