fix: harden CLI executors and remove fallback API keys
This commit is contained in:
parent
ec49645e25
commit
32338932de
|
|
@ -0,0 +1,22 @@
|
|||
import { spawn } from "node:child_process";
|
||||
|
||||
export function runCli(bin: string, args: string[], timeoutMs = 120_000): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(bin, args, {
|
||||
shell: false,
|
||||
timeout: timeoutMs,
|
||||
windowsHide: true,
|
||||
});
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout.on("data", (d) => { stdout += d.toString(); });
|
||||
child.stderr.on("data", (d) => { stderr += d.toString(); });
|
||||
child.on("error", (err) => resolve(`[CLI error: ${err.message.slice(0, 200)}]`));
|
||||
child.on("close", (code) => {
|
||||
const out = stdout.trim();
|
||||
if (out) resolve(out);
|
||||
else resolve(`[CLI exited ${code ?? "unknown"}: ${stderr.slice(0, 200) || "no output"}]`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import { EnhancedMetaAgent } from "../upgrades/enhanced-meta-agent.js";
|
|||
import { ExaSearch } from "../upgrades/exa-search.js";
|
||||
import { SkillSync } from "../upgrades/skill-sync.js";
|
||||
|
||||
const API_KEY = process.env.EXA_API_KEY || "622b4eb6-7d6a-4647-ad3a-872bac6586a7";
|
||||
const API_KEY = process.env.EXA_API_KEY;
|
||||
|
||||
function log(msg: string): void { process.stdout.write(msg + "\n"); }
|
||||
function divider(label: string): void {
|
||||
|
|
@ -21,8 +21,12 @@ function divider(label: string): void {
|
|||
log(" " + label);
|
||||
log("=".repeat(56));
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
if (!API_KEY) {
|
||||
log("Set EXA_API_KEY to run this demo.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Clean state for demo
|
||||
try {
|
||||
const fs = await import("node:fs");
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* const result = await feedbackLoop.run(task, ag);
|
||||
*/
|
||||
|
||||
import { exec } from "node:child_process";
|
||||
import { runCli } from "../../core/cli-runner.js";
|
||||
import * as path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
import type { LoopIteration } from "../../core/types.js";
|
||||
|
|
@ -79,15 +79,10 @@ export class AntigravExecutor implements PhaseExecutor {
|
|||
}
|
||||
|
||||
private async call(prompt: string): Promise<string> {
|
||||
const modelFlag = `--model "${this.config.model}"`;
|
||||
const sandboxFlag = this.config.sandbox ? "--sandbox" : "";
|
||||
const cmd = `"${this.config.bin}" --print ${modelFlag} ${sandboxFlag} "${prompt.replace(/"/g, '\\"')}" 2>/dev/null`;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
|
||||
resolve(stdout.trim() || `[Antigrav error: ${err?.message?.slice(0, 200) ?? "unknown"}]`);
|
||||
});
|
||||
});
|
||||
const args = ["--print", "--model", this.config.model ?? "gemini-2.5-pro"];
|
||||
if (this.config.sandbox) args.push("--sandbox");
|
||||
args.push(prompt);
|
||||
return runCli(this.config.bin ?? "agy", args, this.config.timeoutMs);
|
||||
}
|
||||
|
||||
static describeConfig(): string {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* const result = await feedbackLoop.run(task, cc);
|
||||
*/
|
||||
|
||||
import { exec } from "node:child_process";
|
||||
import { runCli } from "../../core/cli-runner.js";
|
||||
import type { LoopIteration } from "../../core/types.js";
|
||||
import type { PhaseExecutor } from "../../tier2-primitives/loops/feedback-loop.js";
|
||||
|
||||
|
|
@ -64,14 +64,9 @@ export class ClaudeCodeExecutor implements PhaseExecutor {
|
|||
}
|
||||
|
||||
private async callClaude(prompt: string): Promise<string> {
|
||||
const modelFlag = this.config.model ? `-m "${this.config.model}"` : "";
|
||||
const cmd = `"${this.config.bin}" -p "${prompt.replace(/"/g, '\\"')}" ${modelFlag} 2>/dev/null`;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
|
||||
resolve(stdout.trim() || `[Claude Code error: ${err?.message?.slice(0, 200) ?? "unknown"}]`);
|
||||
});
|
||||
});
|
||||
const args = ["-p", prompt];
|
||||
if (this.config.model) args.push("-m", this.config.model);
|
||||
return runCli(this.config.bin ?? "claude", args, this.config.timeoutMs);
|
||||
}
|
||||
|
||||
static describeConfig(): string {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* const viaPi = new CodexExecutor({ usePi: true });
|
||||
*/
|
||||
|
||||
import { exec } from "node:child_process";
|
||||
import { runCli } from "../../core/cli-runner.js";
|
||||
import type { LoopIteration } from "../../core/types.js";
|
||||
import type { PhaseExecutor } from "../../tier2-primitives/loops/feedback-loop.js";
|
||||
|
||||
|
|
@ -62,15 +62,9 @@ export class CodexExecutor implements PhaseExecutor {
|
|||
}
|
||||
|
||||
private async call(prompt: string): Promise<string> {
|
||||
const cmd = this.config.usePi
|
||||
? `pi --provider codex --model codex --print --no-session "${prompt.replace(/"/g, '\\"')}" 2>/dev/null`
|
||||
: `"${this.config.bin}" "${prompt.replace(/"/g, '\\"')}" 2>/dev/null`;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
|
||||
resolve(stdout.trim() || `[Codex error: ${err?.message?.slice(0, 200) ?? "unknown"}]`);
|
||||
});
|
||||
});
|
||||
return this.config.usePi
|
||||
? runCli("pi", ["--provider", "codex", "--model", "codex", "--print", "--no-session", prompt], this.config.timeoutMs)
|
||||
: runCli(this.config.bin ?? "codex", [prompt], this.config.timeoutMs);
|
||||
}
|
||||
|
||||
static describeConfig(): string {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* const result = await feedbackLoop.run(task, grok);
|
||||
*/
|
||||
|
||||
import { exec } from "node:child_process";
|
||||
import { runCli } from "../../core/cli-runner.js";
|
||||
import * as path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
import type { LoopIteration } from "../../core/types.js";
|
||||
|
|
@ -76,14 +76,7 @@ export class GrokExecutor implements PhaseExecutor {
|
|||
}
|
||||
|
||||
private async callGrok(prompt: string): Promise<string> {
|
||||
const modelFlag = `-m "${this.config.model}"`;
|
||||
const cmd = `"${this.config.bin}" ${modelFlag} -p "${prompt.replace(/"/g, '\\"')}" --output-format text 2>/dev/null`;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
|
||||
resolve(stdout.trim() || `[Grok error: ${err?.message?.slice(0, 200) ?? "unknown"}]`);
|
||||
});
|
||||
});
|
||||
return runCli(this.config.bin ?? "grok", ["-m", this.config.model ?? "grok-3", "-p", prompt, "--output-format", "text"], this.config.timeoutMs);
|
||||
}
|
||||
|
||||
static describeConfig(): string {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* const result = await feedbackLoop.run(task, oc);
|
||||
*/
|
||||
|
||||
import { exec } from "node:child_process";
|
||||
import { runCli } from "../../core/cli-runner.js";
|
||||
import type { LoopIteration } from "../../core/types.js";
|
||||
import type { PhaseExecutor } from "../../tier2-primitives/loops/feedback-loop.js";
|
||||
|
||||
|
|
@ -61,15 +61,9 @@ export class OpenCodeExecutor implements PhaseExecutor {
|
|||
}
|
||||
|
||||
private async callOpenCode(prompt: string): Promise<string> {
|
||||
const modelFlag = this.config.model ? `-m "${this.config.model}"` : "";
|
||||
const proxyFlag = `--proxy "${this.config.proxyUrl}"`;
|
||||
const cmd = `"${this.config.bin}" -p "${prompt.replace(/"/g, '\\"')}" ${modelFlag} ${proxyFlag} 2>/dev/null`;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exec(cmd, { timeout: this.config.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
|
||||
resolve(stdout.trim() || `[OpenCode error: ${err?.message?.slice(0, 200) ?? "unknown"}]`);
|
||||
});
|
||||
});
|
||||
const args = ["-p", prompt, "--proxy", this.config.proxyUrl ?? "http://localhost:18901/v1"];
|
||||
if (this.config.model) args.push("-m", this.config.model);
|
||||
return runCli(this.config.bin ?? "opencode", args, this.config.timeoutMs);
|
||||
}
|
||||
|
||||
static describeConfig(): string {
|
||||
|
|
|
|||
15
src/index.ts
15
src/index.ts
|
|
@ -861,6 +861,15 @@ const exa = program
|
|||
.command("exa")
|
||||
.description("Exa AI web search and company research");
|
||||
|
||||
function requireExaApiKey(): string {
|
||||
const apiKey = process.env.EXA_API_KEY;
|
||||
if (!apiKey) {
|
||||
console.error(" ✗ EXA_API_KEY is required. Set it in your environment or .env file.");
|
||||
process.exit(1);
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
exa
|
||||
.command("search <query>")
|
||||
.description("Search the web via Exa AI")
|
||||
|
|
@ -871,7 +880,7 @@ exa
|
|||
const { StateStore } = await import("./core/state-store.js");
|
||||
const { ExaSearch } = await import("./upgrades/exa-search.js");
|
||||
|
||||
const apiKey = process.env.EXA_API_KEY || "622b4eb6-7d6a-4647-ad3a-872bac6586a7";
|
||||
const apiKey = requireExaApiKey();
|
||||
const exa = new ExaSearch(apiKey, new StateStore());
|
||||
|
||||
console.log(`\n Searching: ${query}\n`);
|
||||
|
|
@ -891,7 +900,7 @@ exa
|
|||
.action(async (name: string) => {
|
||||
const { ExaSearch } = await import("./upgrades/exa-search.js");
|
||||
|
||||
const apiKey = process.env.EXA_API_KEY || "622b4eb6-7d6a-4647-ad3a-872bac6586a7";
|
||||
const apiKey = requireExaApiKey();
|
||||
const exa = new ExaSearch(apiKey);
|
||||
|
||||
console.log(`\n Researching: ${name}\n`);
|
||||
|
|
@ -936,7 +945,7 @@ program
|
|||
const { EnhancedMetaAgent } = await import("./upgrades/enhanced-meta-agent.js");
|
||||
const { SkillSync } = await import("./upgrades/skill-sync.js");
|
||||
|
||||
const apiKey = process.env.EXA_API_KEY || "622b4eb6-7d6a-4647-ad3a-872bac6586a7";
|
||||
const apiKey = requireExaApiKey();
|
||||
const exa = new ExaSearch(apiKey);
|
||||
const agent = new EnhancedMetaAgent();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue