Incorporate improvements from PR #13 and #105 into the gemini-local adapter: - Add detectGeminiAuthRequired() for runtime auth failure detection with errorCode: "gemini_auth_required" on execution results - Add isGeminiTurnLimitResult() to detect exit code 53 / turn_limit status and clear session to prevent stuck sessions on next heartbeat - Add describeGeminiFailure() for structured error messages from parsed result events including errors array extraction - Return parsed resultEvent in resultJson instead of raw stdout/stderr - Add isRetry guard to prevent stale session ID fallback after retry - Replace boolean yolo with approvalMode string (default/auto_edit/yolo) with backwards-compatible config.yolo fallback - Add sandbox config option (--sandbox / --sandbox=none) - Add GOOGLE_GENAI_USE_GCA auth detection in environment test - Consolidate auth detection regex into shared detectGeminiAuthRequired() - Add gemini-2.0-flash and gemini-2.0-flash-lite model IDs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import type { CreateConfigValues } from "@paperclipai/adapter-utils";
|
|
import { DEFAULT_GEMINI_LOCAL_MODEL } from "../index.js";
|
|
|
|
function parseCommaArgs(value: string): string[] {
|
|
return value
|
|
.split(",")
|
|
.map((item) => item.trim())
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function parseEnvVars(text: string): Record<string, string> {
|
|
const env: Record<string, string> = {};
|
|
for (const line of text.split(/\r?\n/)) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
const eq = trimmed.indexOf("=");
|
|
if (eq <= 0) continue;
|
|
const key = trimmed.slice(0, eq).trim();
|
|
const value = trimmed.slice(eq + 1);
|
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
env[key] = value;
|
|
}
|
|
return env;
|
|
}
|
|
|
|
function parseEnvBindings(bindings: unknown): Record<string, unknown> {
|
|
if (typeof bindings !== "object" || bindings === null || Array.isArray(bindings)) return {};
|
|
const env: Record<string, unknown> = {};
|
|
for (const [key, raw] of Object.entries(bindings)) {
|
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
if (typeof raw === "string") {
|
|
env[key] = { type: "plain", value: raw };
|
|
continue;
|
|
}
|
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) continue;
|
|
const rec = raw as Record<string, unknown>;
|
|
if (rec.type === "plain" && typeof rec.value === "string") {
|
|
env[key] = { type: "plain", value: rec.value };
|
|
continue;
|
|
}
|
|
if (rec.type === "secret_ref" && typeof rec.secretId === "string") {
|
|
env[key] = {
|
|
type: "secret_ref",
|
|
secretId: rec.secretId,
|
|
...(typeof rec.version === "number" || rec.version === "latest"
|
|
? { version: rec.version }
|
|
: {}),
|
|
};
|
|
}
|
|
}
|
|
return env;
|
|
}
|
|
|
|
export function buildGeminiLocalConfig(v: CreateConfigValues): Record<string, unknown> {
|
|
const ac: Record<string, unknown> = {};
|
|
if (v.cwd) ac.cwd = v.cwd;
|
|
if (v.instructionsFilePath) ac.instructionsFilePath = v.instructionsFilePath;
|
|
if (v.promptTemplate) ac.promptTemplate = v.promptTemplate;
|
|
ac.model = v.model || DEFAULT_GEMINI_LOCAL_MODEL;
|
|
ac.timeoutSec = 0;
|
|
ac.graceSec = 15;
|
|
const env = parseEnvBindings(v.envBindings);
|
|
const legacy = parseEnvVars(v.envVars);
|
|
for (const [key, value] of Object.entries(legacy)) {
|
|
if (!Object.prototype.hasOwnProperty.call(env, key)) {
|
|
env[key] = { type: "plain", value };
|
|
}
|
|
}
|
|
if (Object.keys(env).length > 0) ac.env = env;
|
|
if (v.dangerouslyBypassSandbox) ac.approvalMode = "yolo";
|
|
ac.sandbox = !v.dangerouslyBypassSandbox;
|
|
if (v.command) ac.command = v.command;
|
|
if (v.extraArgs) ac.extraArgs = parseCommaArgs(v.extraArgs);
|
|
return ac;
|
|
}
|