MASSIVE IMPROVCE

This commit is contained in:
David Blass
2025-11-12 19:57:34 -05:00
parent aff634af29
commit 9588ffd4b6
7 changed files with 155 additions and 167 deletions
+8 -19
View File
@@ -8,17 +8,12 @@ import { pipeline } from "node:stream/promises";
import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk"; import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
import packageJson from "../package.json" with { type: "json" }; import packageJson from "../package.json" with { type: "json" };
import { log } from "../utils/cli.ts"; import { log } from "../utils/cli.ts";
import { type Agent, instructions } from "./shared.ts"; import { agent, instructions } from "./shared.ts";
let cachedCliPath: string | undefined;
export const claude: Agent = {
install: async (): Promise<string> => {
if (cachedCliPath) {
log.info(`Using cached Claude Code CLI at ${cachedCliPath}`);
return cachedCliPath;
}
export const claude = agent({
name: "claude",
inputKey: "anthropic_api_key",
install: async () => {
// Get the SDK version from package.json and resolve to actual version // Get the SDK version from package.json and resolve to actual version
const versionRange = packageJson.dependencies["@anthropic-ai/claude-agent-sdk"] || "latest"; const versionRange = packageJson.dependencies["@anthropic-ai/claude-agent-sdk"] || "latest";
let sdkVersion: string; let sdkVersion: string;
@@ -83,8 +78,6 @@ export const claude: Agent = {
if (!existsSync(cliPath)) { if (!existsSync(cliPath)) {
throw new Error(`cli.js not found in extracted package at ${cliPath}`); throw new Error(`cli.js not found in extracted package at ${cliPath}`);
} }
cachedCliPath = cliPath;
log.info(`✓ Claude Code CLI installed at ${cliPath}`); log.info(`✓ Claude Code CLI installed at ${cliPath}`);
return cliPath; return cliPath;
} catch (error) { } catch (error) {
@@ -97,19 +90,15 @@ export const claude: Agent = {
throw error; throw error;
} }
}, },
run: async ({ prompt, mcpServers, apiKey }) => { run: async ({ prompt, mcpServers, apiKey, cliPath }) => {
process.env.ANTHROPIC_API_KEY = apiKey; process.env.ANTHROPIC_API_KEY = apiKey;
if (!cachedCliPath) {
throw new Error("Claude CLI not installed. Call install() before run().");
}
const queryInstance = query({ const queryInstance = query({
prompt: `${instructions}\n\n****** USER PROMPT ******\n${prompt}`, prompt: `${instructions}\n\n****** USER PROMPT ******\n${prompt}`,
options: { options: {
permissionMode: "bypassPermissions", permissionMode: "bypassPermissions",
mcpServers, mcpServers,
pathToClaudeCodeExecutable: cachedCliPath, pathToClaudeCodeExecutable: cliPath,
}, },
}); });
@@ -124,7 +113,7 @@ export const claude: Agent = {
output: "", output: "",
}; };
}, },
}; });
type SDKMessageType = SDKMessage["type"]; type SDKMessageType = SDKMessage["type"];
+6 -4
View File
@@ -1,10 +1,12 @@
import { spawnSync } from "node:child_process"; import { spawnSync } from "node:child_process";
import { findCliPath, log } from "../utils/cli.ts"; import { findCliPath, log } from "../utils/cli.ts";
import { type Agent, instructions } from "./shared.ts"; import { agent, instructions } from "./shared.ts";
export const codex: Agent = { export const codex = agent({
install: async (): Promise<string> => { name: "codex",
inputKey: "openai_api_key",
install: async () => {
const globalCodexPath = findCliPath("codex"); const globalCodexPath = findCliPath("codex");
if (globalCodexPath) { if (globalCodexPath) {
log.info(`Using global Codex CLI at ${globalCodexPath}`); log.info(`Using global Codex CLI at ${globalCodexPath}`);
@@ -133,4 +135,4 @@ export const codex: Agent = {
}; };
} }
}, },
}; });
+3 -3
View File
@@ -1,9 +1,9 @@
import type { AgentName } from "../main.ts";
import { claude } from "./claude.ts"; import { claude } from "./claude.ts";
import { codex } from "./codex.ts"; import { codex } from "./codex.ts";
import type { Agent } from "./shared.ts";
export const agents = { export const agents = {
claude, claude,
codex, codex,
} as const satisfies Record<AgentName, Agent>; } as const;
export type AgentInputKey = (typeof agents)[keyof typeof agents]["inputKey"];
+5 -2
View File
@@ -23,12 +23,15 @@ export interface AgentConfig {
cliPath: string; cliPath: string;
} }
type InputKey = "anthropic_api_key" | "openai_api_key"; export const agent = <const agent extends Agent>(agent: agent): agent => {
return agent;
};
export type Agent = { export type Agent = {
name: string;
inputKey: string;
install: () => Promise<string>; install: () => Promise<string>;
run: (config: AgentConfig) => Promise<AgentResult>; run: (config: AgentConfig) => Promise<AgentResult>;
inputKey: InputKey;
}; };
export const instructions = ` export const instructions = `
+74 -82
View File
@@ -313,28 +313,28 @@ var require_tunnel = __commonJS({
exports.httpOverHttps = httpOverHttps; exports.httpOverHttps = httpOverHttps;
exports.httpsOverHttps = httpsOverHttps; exports.httpsOverHttps = httpsOverHttps;
function httpOverHttp(options) { function httpOverHttp(options) {
var agent = new TunnelingAgent(options); var agent2 = new TunnelingAgent(options);
agent.request = http.request; agent2.request = http.request;
return agent; return agent2;
} }
function httpsOverHttp(options) { function httpsOverHttp(options) {
var agent = new TunnelingAgent(options); var agent2 = new TunnelingAgent(options);
agent.request = http.request; agent2.request = http.request;
agent.createSocket = createSecureSocket; agent2.createSocket = createSecureSocket;
agent.defaultPort = 443; agent2.defaultPort = 443;
return agent; return agent2;
} }
function httpOverHttps(options) { function httpOverHttps(options) {
var agent = new TunnelingAgent(options); var agent2 = new TunnelingAgent(options);
agent.request = https.request; agent2.request = https.request;
return agent; return agent2;
} }
function httpsOverHttps(options) { function httpsOverHttps(options) {
var agent = new TunnelingAgent(options); var agent2 = new TunnelingAgent(options);
agent.request = https.request; agent2.request = https.request;
agent.createSocket = createSecureSocket; agent2.createSocket = createSecureSocket;
agent.defaultPort = 443; agent2.defaultPort = 443;
return agent; return agent2;
} }
function TunnelingAgent(options) { function TunnelingAgent(options) {
var self2 = this; var self2 = this;
@@ -9136,18 +9136,18 @@ var require_agent = __commonJS({
} }
} }
); );
const agent = this; const agent2 = this;
this[kOnDrain] = (origin, targets) => { this[kOnDrain] = (origin, targets) => {
agent.emit("drain", origin, [agent, ...targets]); agent2.emit("drain", origin, [agent2, ...targets]);
}; };
this[kOnConnect] = (origin, targets) => { this[kOnConnect] = (origin, targets) => {
agent.emit("connect", origin, [agent, ...targets]); agent2.emit("connect", origin, [agent2, ...targets]);
}; };
this[kOnDisconnect] = (origin, targets, err2) => { this[kOnDisconnect] = (origin, targets, err2) => {
agent.emit("disconnect", origin, [agent, ...targets], err2); agent2.emit("disconnect", origin, [agent2, ...targets], err2);
}; };
this[kOnConnectionError] = (origin, targets, err2) => { this[kOnConnectionError] = (origin, targets, err2) => {
agent.emit("connectionError", origin, [agent, ...targets], err2); agent2.emit("connectionError", origin, [agent2, ...targets], err2);
}; };
} }
get [kRunning]() { get [kRunning]() {
@@ -10527,16 +10527,16 @@ var require_mock_utils = __commonJS({
return true; return true;
} }
function buildMockDispatch() { function buildMockDispatch() {
const agent = this[kMockAgent]; const agent2 = this[kMockAgent];
const origin = this[kOrigin]; const origin = this[kOrigin];
const originalDispatch = this[kOriginalDispatch]; const originalDispatch = this[kOriginalDispatch];
return function dispatch(opts, handler) { return function dispatch(opts, handler) {
if (agent.isMockActive) { if (agent2.isMockActive) {
try { try {
mockDispatch.call(this, opts, handler); mockDispatch.call(this, opts, handler);
} catch (error2) { } catch (error2) {
if (error2 instanceof MockNotMatchedError) { if (error2 instanceof MockNotMatchedError) {
const netConnect = agent[kGetNetConnect](); const netConnect = agent2[kGetNetConnect]();
if (netConnect === false) { if (netConnect === false) {
throw new MockNotMatchedError(`${error2.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)`); throw new MockNotMatchedError(`${error2.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)`);
} }
@@ -10565,7 +10565,7 @@ var require_mock_utils = __commonJS({
} }
function buildMockOptions(opts) { function buildMockOptions(opts) {
if (opts) { if (opts) {
const { agent, ...mockOptions } = opts; const { agent: agent2, ...mockOptions } = opts;
return mockOptions; return mockOptions;
} }
} }
@@ -10965,9 +10965,9 @@ var require_mock_agent = __commonJS({
if (opts && opts.agent && typeof opts.agent.dispatch !== "function") { if (opts && opts.agent && typeof opts.agent.dispatch !== "function") {
throw new InvalidArgumentError("Argument opts.agent must implement Agent"); throw new InvalidArgumentError("Argument opts.agent must implement Agent");
} }
const agent = opts && opts.agent ? opts.agent : new Agent(opts); const agent2 = opts && opts.agent ? opts.agent : new Agent(opts);
this[kAgent] = agent; this[kAgent] = agent2;
this[kClients] = agent[kClients]; this[kClients] = agent2[kClients];
this[kOptions] = buildMockOptions(opts); this[kOptions] = buildMockOptions(opts);
} }
get(origin) { get(origin) {
@@ -11493,12 +11493,12 @@ var require_global2 = __commonJS({
if (getGlobalDispatcher() === void 0) { if (getGlobalDispatcher() === void 0) {
setGlobalDispatcher(new Agent()); setGlobalDispatcher(new Agent());
} }
function setGlobalDispatcher(agent) { function setGlobalDispatcher(agent2) {
if (!agent || typeof agent.dispatch !== "function") { if (!agent2 || typeof agent2.dispatch !== "function") {
throw new InvalidArgumentError("Argument agent must implement Agent"); throw new InvalidArgumentError("Argument agent must implement Agent");
} }
Object.defineProperty(globalThis, globalDispatcher, { Object.defineProperty(globalThis, globalDispatcher, {
value: agent, value: agent2,
writable: true, writable: true,
enumerable: false, enumerable: false,
configurable: false configurable: false
@@ -13850,8 +13850,8 @@ var require_fetch = __commonJS({
return response; return response;
async function dispatch({ body }) { async function dispatch({ body }) {
const url2 = requestCurrentURL(request); const url2 = requestCurrentURL(request);
const agent = fetchParams.controller.dispatcher; const agent2 = fetchParams.controller.dispatcher;
return new Promise((resolve, reject) => agent.dispatch( return new Promise((resolve, reject) => agent2.dispatch(
{ {
path: url2.pathname + url2.search, path: url2.pathname + url2.search,
origin: url2.origin, origin: url2.origin,
@@ -17223,8 +17223,8 @@ var require_undici = __commonJS({
} }
url2 = util2.parseURL(url2); url2 = util2.parseURL(url2);
} }
const { agent, dispatcher = getGlobalDispatcher() } = opts; const { agent: agent2, dispatcher = getGlobalDispatcher() } = opts;
if (agent) { if (agent2) {
throw new InvalidArgumentError("unsupported opts.agent. Did you mean opts.client?"); throw new InvalidArgumentError("unsupported opts.agent. Did you mean opts.client?");
} }
return fn2.call(dispatcher, { return fn2.call(dispatcher, {
@@ -17776,17 +17776,17 @@ var require_lib = __commonJS({
return additionalHeaders[header] || clientHeader || _default; return additionalHeaders[header] || clientHeader || _default;
} }
_getAgent(parsedUrl) { _getAgent(parsedUrl) {
let agent; let agent2;
const proxyUrl = pm.getProxyUrl(parsedUrl); const proxyUrl = pm.getProxyUrl(parsedUrl);
const useProxy = proxyUrl && proxyUrl.hostname; const useProxy = proxyUrl && proxyUrl.hostname;
if (this._keepAlive && useProxy) { if (this._keepAlive && useProxy) {
agent = this._proxyAgent; agent2 = this._proxyAgent;
} }
if (!useProxy) { if (!useProxy) {
agent = this._agent; agent2 = this._agent;
} }
if (agent) { if (agent2) {
return agent; return agent2;
} }
const usingSsl = parsedUrl.protocol === "https:"; const usingSsl = parsedUrl.protocol === "https:";
let maxSockets = 100; let maxSockets = 100;
@@ -17808,20 +17808,20 @@ var require_lib = __commonJS({
} else { } else {
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
} }
agent = tunnelAgent(agentOptions); agent2 = tunnelAgent(agentOptions);
this._proxyAgent = agent; this._proxyAgent = agent2;
} }
if (!agent) { if (!agent2) {
const options = { keepAlive: this._keepAlive, maxSockets }; const options = { keepAlive: this._keepAlive, maxSockets };
agent = usingSsl ? new https.Agent(options) : new http.Agent(options); agent2 = usingSsl ? new https.Agent(options) : new http.Agent(options);
this._agent = agent; this._agent = agent2;
} }
if (usingSsl && this._ignoreSslError) { if (usingSsl && this._ignoreSslError) {
agent.options = Object.assign(agent.options || {}, { agent2.options = Object.assign(agent2.options || {}, {
rejectUnauthorized: false rejectUnauthorized: false
}); });
} }
return agent; return agent2;
} }
_getProxyAgentDispatcher(parsedUrl, proxyUrl) { _getProxyAgentDispatcher(parsedUrl, proxyUrl) {
let proxyAgent; let proxyAgent;
@@ -41172,6 +41172,9 @@ var workflows = [
]; ];
// agents/shared.ts // agents/shared.ts
var agent = (agent2) => {
return agent2;
};
var instructions = ` var instructions = `
# General instructions # General instructions
@@ -41217,13 +41220,10 @@ ${w.prompt}`).join("\n\n")}
`; `;
// agents/claude.ts // agents/claude.ts
var cachedCliPath; var claude = agent({
var claude = { name: "claude",
inputKey: "anthropic_api_key",
install: async () => { install: async () => {
if (cachedCliPath) {
log.info(`Using cached Claude Code CLI at ${cachedCliPath}`);
return cachedCliPath;
}
const versionRange = package_default.dependencies["@anthropic-ai/claude-agent-sdk"] || "latest"; const versionRange = package_default.dependencies["@anthropic-ai/claude-agent-sdk"] || "latest";
let sdkVersion; let sdkVersion;
if (versionRange.startsWith("^") || versionRange.startsWith("~")) { if (versionRange.startsWith("^") || versionRange.startsWith("~")) {
@@ -41268,7 +41268,6 @@ var claude = {
if (!existsSync3(cliPath)) { if (!existsSync3(cliPath)) {
throw new Error(`cli.js not found in extracted package at ${cliPath}`); throw new Error(`cli.js not found in extracted package at ${cliPath}`);
} }
cachedCliPath = cliPath;
log.info(`\u2713 Claude Code CLI installed at ${cliPath}`); log.info(`\u2713 Claude Code CLI installed at ${cliPath}`);
return cliPath; return cliPath;
} catch (error2) { } catch (error2) {
@@ -41279,11 +41278,8 @@ var claude = {
throw error2; throw error2;
} }
}, },
run: async ({ prompt, mcpServers, apiKey }) => { run: async ({ prompt, mcpServers, apiKey, cliPath }) => {
process.env.ANTHROPIC_API_KEY = apiKey; process.env.ANTHROPIC_API_KEY = apiKey;
if (!cachedCliPath) {
throw new Error("Claude CLI not installed. Call install() before run().");
}
const queryInstance = query({ const queryInstance = query({
prompt: `${instructions} prompt: `${instructions}
@@ -41292,7 +41288,7 @@ ${prompt}`,
options: { options: {
permissionMode: "bypassPermissions", permissionMode: "bypassPermissions",
mcpServers, mcpServers,
pathToClaudeCodeExecutable: cachedCliPath pathToClaudeCodeExecutable: cliPath
} }
}); });
for await (const message of queryInstance) { for await (const message of queryInstance) {
@@ -41304,7 +41300,7 @@ ${prompt}`,
output: "" output: ""
}; };
} }
}; });
var bashToolIds = /* @__PURE__ */ new Set(); var bashToolIds = /* @__PURE__ */ new Set();
var messageHandlers = { var messageHandlers = {
assistant: (data) => { assistant: (data) => {
@@ -41400,7 +41396,9 @@ var messageHandlers = {
// agents/codex.ts // agents/codex.ts
import { spawnSync as spawnSync2 } from "node:child_process"; import { spawnSync as spawnSync2 } from "node:child_process";
var codex = { var codex = agent({
name: "codex",
inputKey: "openai_api_key",
install: async () => { install: async () => {
const globalCodexPath = findCliPath("codex"); const globalCodexPath = findCliPath("codex");
if (globalCodexPath) { if (globalCodexPath) {
@@ -41507,7 +41505,7 @@ ${prompt}`;
}; };
} }
} }
}; });
// agents/index.ts // agents/index.ts
var agents = { var agents = {
@@ -41598,11 +41596,17 @@ function setupGitAuth(githubToken, repoContext) {
} }
// main.ts // main.ts
var AgentName = type.enumerated("codex", "claude"); var AgentName = type.enumerated(...Object.values(agents).map((agent2) => agent2.name));
var AgentInputKey = type.enumerated(
...Object.values(agents).map((agent2) => agent2.inputKey)
);
var keyInputDefs = flatMorph(
agents,
(_, agent2) => [agent2.inputKey, "string | undefined?"]
);
var Inputs = type({ var Inputs = type({
prompt: "string", prompt: "string",
"anthropic_api_key?": "string | undefined", ...keyInputDefs,
"openai_api_key?": "string | undefined",
"agent?": AgentName "agent?": AgentName
}); });
async function main(inputs) { async function main(inputs) {
@@ -41621,28 +41625,16 @@ async function main(inputs) {
repoContext repoContext
}); });
const agentName = inputs.agent || repoSettings.defaultAgent || "claude"; const agentName = inputs.agent || repoSettings.defaultAgent || "claude";
const agent = agents[agentName]; const agent2 = agents[agentName];
setupGitAuth(githubInstallationToken, repoContext); setupGitAuth(githubInstallationToken, repoContext);
const mcpServers = createMcpConfigs(githubInstallationToken); const mcpServers = createMcpConfigs(githubInstallationToken);
log.debug(`\u{1F4CB} MCP Config: ${JSON.stringify(mcpServers, null, 2)}`); log.debug(`\u{1F4CB} MCP Config: ${JSON.stringify(mcpServers, null, 2)}`);
const cliPath = await agent.install(); const cliPath = await agent2.install();
log.info(`Running ${agentName} Agent SDK...`); log.info(`Running ${agentName} Agent SDK...`);
log.box(inputs.prompt, { title: "Prompt" }); log.box(inputs.prompt, { title: "Prompt" });
let apiKey; const apiKey = inputs[agent2.inputKey];
if (agentName === "claude") { if (!apiKey) throw new Error(`${agent2.inputKey} is required for ${agentName} agent`);
if (!inputs.anthropic_api_key) { const result = await agent2.run({
throw new Error("ANTHROPIC_API_KEY is required for Claude agent");
}
apiKey = inputs.anthropic_api_key;
} else if (agentName === "codex") {
if (!inputs.openai_api_key) {
throw new Error("OPENAI_API_KEY is required for Codex agent");
}
apiKey = inputs.openai_api_key;
} else {
throw new Error(`API key configuration not implemented for agent: ${agentName}`);
}
const result = await agent.run({
prompt: inputs.prompt, prompt: inputs.prompt,
mcpServers, mcpServers,
githubInstallationToken, githubInstallationToken,
+17 -17
View File
@@ -1,3 +1,4 @@
import { flatMorph } from "@ark/util";
import { type } from "arktype"; import { type } from "arktype";
import { agents } from "./agents/index.ts"; import { agents } from "./agents/index.ts";
import { createMcpConfigs } from "./mcp/config.ts"; import { createMcpConfigs } from "./mcp/config.ts";
@@ -11,13 +12,22 @@ import {
} from "./utils/github.ts"; } from "./utils/github.ts";
import { setupGitAuth, setupGitConfig } from "./utils/setup.ts"; import { setupGitAuth, setupGitConfig } from "./utils/setup.ts";
export const AgentName = type.enumerated("codex", "claude"); export const AgentName = type.enumerated(...Object.values(agents).map((agent) => agent.name));
export type AgentName = typeof AgentName.infer; export type AgentName = typeof AgentName.infer;
export const AgentInputKey = type.enumerated(
...Object.values(agents).map((agent) => agent.inputKey)
);
export type AgentInputKey = typeof AgentInputKey.infer;
const keyInputDefs = flatMorph(
agents,
(_, agent) => [agent.inputKey, "string | undefined?"] as const
);
export const Inputs = type({ export const Inputs = type({
prompt: "string", prompt: "string",
"anthropic_api_key?": "string | undefined", ...keyInputDefs,
"openai_api_key?": "string | undefined",
"agent?": AgentName, "agent?": AgentName,
}); });
@@ -72,20 +82,10 @@ export async function main(inputs: Inputs): Promise<MainResult> {
// for webhook payloads, check the specified `agent` field // for webhook payloads, check the specified `agent` field
// Get API key based on agent type // Get API key based on agent type
let apiKey: string;
if (agentName === "claude") { const apiKey = inputs[agent.inputKey];
if (!inputs.anthropic_api_key) {
throw new Error("ANTHROPIC_API_KEY is required for Claude agent"); if (!apiKey) throw new Error(`${agent.inputKey} is required for ${agentName} agent`);
}
apiKey = inputs.anthropic_api_key;
} else if (agentName === "codex") {
if (!inputs.openai_api_key) {
throw new Error("OPENAI_API_KEY is required for Codex agent");
}
apiKey = inputs.openai_api_key;
} else {
throw new Error(`API key configuration not implemented for agent: ${agentName}`);
}
const result = await agent.run({ const result = await agent.run({
prompt: inputs.prompt, prompt: inputs.prompt,
+3 -1
View File
@@ -28,6 +28,7 @@ export async function run(
const inputs: Inputs = { const inputs: Inputs = {
prompt, prompt,
openai_api_key: process.env.OPENAI_API_KEY, openai_api_key: process.env.OPENAI_API_KEY,
anthropic_api_key: process.env.ANTHROPIC_API_KEY,
agent: "codex", agent: "codex",
}; };
@@ -87,7 +88,8 @@ Examples:
// Default to testing tool calls if no file specified // Default to testing tool calls if no file specified
const filePath = args._[0] || null; const filePath = args._[0] || null;
if (!filePath) { if (!filePath) {
prompt = "List all available MCP tools from the gh-pullfrog server and show what each tool does."; prompt =
"List all available MCP tools from the gh-pullfrog server and show what each tool does.";
} else { } else {
const ext = extname(filePath).toLowerCase(); const ext = extname(filePath).toLowerCase();
let resolvedPath: string; let resolvedPath: string;