Files
shockbot/test/crossagent/codexAuth.ts
T
David Blass d3b5340583 fix: audit batch — MCP timeouts, entryPost, vip_audit 404s, and 6 more (#824)
* fix: 9 unaddressed log-audit / run-audit findings

Co-authored-by: Cursor <cursoragent@cursor.com>

#815 entryPost stdlib-only imports; #823 MCP timeoutMs on checkout_pr/shell;
#816 FREE_FALLBACK → opencode/big-pickle; #822 chunk GraphQL nodes ≤100;
#817/#821 vip_audit 404 skip paths; #813 longer serializable retries;
#818 run-context handler-entered log; #805 audit severity template.

* fix: update footer test for big-pickle fallback slug

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: anneal round 1 — ghaCore getState casing, post-hook timeout

Match @actions/core STATE_ key semantics (no uppercasing), cap
postApiFetch at 30s, trim serializable retries to stay under GitHub's
10s webhook window, log Clerk failures in getUserTokenByGithubLogin.

Co-authored-by: Cursor <cursoragent@cursor.com>

* revert: drop run-context handler log (#818 deferred)

The #692 client-side fix is already on main; residual SyntaxError hits
are old action pins. Per-request log added noise without fixing anything.

Co-authored-by: Cursor <cursoragent@cursor.com>

* document per-issue Closes syntax for audit PRs

GitHub only auto-closes the first issue when numbers are comma-separated;
/audits and AGENTS.md now require Closes before each issue number.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: anneal round 2 — outreach privacy, alert resilience, vertex cleanup

Filter private repos from VIP authority output, harden console alert
lines against DB failures, drop spoofable changesets body check, and
unset GOOGLE_APPLICATION_CREDENTIALS after vertex credential cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: drop codexHome re-export of detectCodexRefresh

Import detectCodexRefresh directly from codexRefreshDetect.ts everywhere;
rename the unit test file to match. codexHome.ts stays install-only.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: drop deprecated minimax-m2.5-free; add paid minimax-m2.5

Remove the deprecated free MiniMax promo from the catalog, docs, and
tests. BYOK fallback and picker copy stay on opencode/big-pickle. Add
opencode/minimax-m2.5 and openrouter/minimax-m2.5 for Zen BYOK and
Router. Pin #816 regressions with freeFallbackCatalog and runErrorRenderer
unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: hidden minimax-m2.5-free fallback for stored slugs

Re-add opencode/minimax-m2.5-free as a hidden fallback alias to big-pickle
so repos with the legacy slug still resolve as free. Drop live Zen API
experiment tests in freeFallbackCatalog.test.ts.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 16:48:25 +00:00

112 lines
4.0 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { readFileSync } from "node:fs";
import { detectCodexRefresh } from "../../utils/codexRefreshDetect.ts";
import type { AgentResult, TestRunnerOptions, ValidationCheck } from "../utils.ts";
import { defineFixture } from "../utils.ts";
/**
* codex-auth test — end-to-end Codex ChatGPT-subscription auth smoke.
*
* Pins openai/gpt-5.5 (in upstream opencode's Codex `ALLOWED_MODELS` allow
* list) and runs the full opencode harness against the developer's / CI's
* `CODEX_AUTH_JSON`. Exercises:
*
* - installCodexAuth() materializes auth.json at $HOME/.local/share/opencode/
* with `expires: 0` (forces refresh on first request).
* - opencode's CodexAuthPlugin routes openai requests through the ChatGPT
* subscription instead of needing OPENAI_API_KEY.
* - the refresh chain advances during the run (proving the refresh path
* works end-to-end against live Codex auth servers).
* - detectCodexRefresh() would surface the rotation to entryPost.ts for
* write-back to Pullfrog's secret store.
*
* the post-hook itself runs in a separate GHA `post:` step and is not
* invoked by `pnpm runtest`. instead, this test asserts the on-disk auth.json
* state that the post-hook would consume, which is the genuine integration
* boundary (everything past `detectCodexRefresh` is a single fetch + unit-
* tested in codexRefreshDetect.test.ts).
*
* requires `CODEX_AUTH_JSON` in the environment. dev-local: put it in
* `.env`. CI: provisioned as `secrets.CODEX_AUTH_JSON` and forwarded by the
* `action-agents` job env block in `.github/workflows/test.yml`.
*/
const token = randomUUID();
const fixture = defineFixture(
{
prompt: `Call set_output with exactly this token and nothing else: ${token}`,
shell: "restricted",
push: "disabled",
timeout: "4m",
},
{ localOnly: true }
);
function parseOriginalRefresh(): string | null {
const raw = process.env.CODEX_AUTH_JSON;
if (!raw) return null;
try {
const parsed = JSON.parse(raw) as { tokens?: { refresh_token?: unknown } };
const rt = parsed?.tokens?.refresh_token;
return typeof rt === "string" && rt.length > 0 ? rt : null;
} catch {
return null;
}
}
function validator(result: AgentResult): ValidationCheck[] {
const setOutputCalled = result.structuredOutput !== null;
const tokenMatches = result.structuredOutput === token;
// installCodexAuth() emits this log line with the absolute path; we use it
// to find the per-test HOME (randomized inside runAgentStreaming).
const pathMatch = result.output.match(/installed Codex auth at (\S+)/);
const authPath = pathMatch?.[1];
let authMaterialized = false;
let refreshRotated = false;
if (authPath) {
try {
const content = readFileSync(authPath, "utf8");
authMaterialized = true;
const originalRefresh = parseOriginalRefresh();
if (originalRefresh) {
refreshRotated = detectCodexRefresh({ authFileContent: content, originalRefresh }) !== null;
}
} catch {
// authMaterialized stays false
}
}
return [
{ name: "set_output", passed: setOutputCalled },
{ name: "token_matches", passed: tokenMatches },
{ name: "auth_materialized", passed: authMaterialized },
{ name: "refresh_rotated", passed: refreshRotated },
];
}
export const test: TestRunnerOptions = {
name: "codex-auth",
fixture,
validator,
agents: ["opencode"],
env: {
PULLFROG_MODEL: "openai/gpt",
PULLFROG_DISABLE_SECURITY_INSTRUCTIONS: "1",
},
coverage: [
"action/utils/codexHome.ts",
"action/utils/codexRefreshDetect.ts",
"action/entryPost.ts",
"action/agents/{opencode,opencode_v2}.ts",
],
// forks + contributors without the Codex secret skip cleanly rather than
// failing on `auth_materialized=✗` and (with fail-fast: true) cascading
// cancellation across the rest of the matrix. CI on `pullfrog/app` and
// dev-local with `.env` both have the secret and run the test as normal.
skipIf: () => (process.env.CODEX_AUTH_JSON ? null : "CODEX_AUTH_JSON unset"),
};