update
This commit is contained in:
1 parent
7a3db27ffa
commit
1ba3147ed2
96 files changed
+9400
-6269
No files matched your search
@@ -236,7 +236,7 @@ async function buildClaudeRuntimeConfig(input: ClaudeExecutionInput): Promise<Cl
|
||||
env.AGENT_COMPANY_API_KEY = authToken;
|
||||
}
|
||||
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const runtimeEnv = ensurePathInEnv(env);
|
||||
await ensureCommandResolvable(command, cwd, runtimeEnv);
|
||||
|
||||
const timeoutSec = asNumber(config.timeoutSec, 0);
|
||||
|
||||
@@ -78,7 +78,7 @@ export async function testEnvironment(
|
||||
for (const [key, value] of Object.entries(envConfig)) {
|
||||
if (typeof value === "string") env[key] = value;
|
||||
}
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const runtimeEnv = ensurePathInEnv(env);
|
||||
try {
|
||||
await ensureCommandResolvable(command, cwd, runtimeEnv);
|
||||
checks.push({
|
||||
@@ -96,15 +96,13 @@ export async function testEnvironment(
|
||||
}
|
||||
|
||||
const configApiKey = env.ANTHROPIC_API_KEY;
|
||||
const hostApiKey = process.env.ANTHROPIC_API_KEY;
|
||||
if (isNonEmpty(configApiKey) || isNonEmpty(hostApiKey)) {
|
||||
const source = isNonEmpty(configApiKey) ? "adapter config env" : "server environment";
|
||||
if (isNonEmpty(configApiKey)) {
|
||||
checks.push({
|
||||
code: "claude_anthropic_api_key_overrides_subscription",
|
||||
level: "warn",
|
||||
message:
|
||||
"ANTHROPIC_API_KEY is set. Claude will use API-key auth instead of subscription credentials.",
|
||||
detail: `Detected in ${source}.`,
|
||||
detail: "Detected in adapter runtime env.",
|
||||
hint: "Unset ANTHROPIC_API_KEY if you want subscription-based Claude login behavior.",
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -243,7 +243,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
env.AGENT_COMPANY_API_KEY = authToken;
|
||||
}
|
||||
const billingType = resolveCodexBillingType(env);
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const runtimeEnv = ensurePathInEnv(env);
|
||||
await ensureCommandResolvable(command, cwd, runtimeEnv);
|
||||
|
||||
const timeoutSec = asNumber(config.timeoutSec, 0);
|
||||
|
||||
@@ -80,7 +80,7 @@ export async function testEnvironment(
|
||||
for (const [key, value] of Object.entries(envConfig)) {
|
||||
if (typeof value === "string") env[key] = value;
|
||||
}
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const runtimeEnv = ensurePathInEnv(env);
|
||||
try {
|
||||
await ensureCommandResolvable(command, cwd, runtimeEnv);
|
||||
checks.push({
|
||||
@@ -98,21 +98,19 @@ export async function testEnvironment(
|
||||
}
|
||||
|
||||
const configOpenAiKey = env.OPENAI_API_KEY;
|
||||
const hostOpenAiKey = process.env.OPENAI_API_KEY;
|
||||
if (isNonEmpty(configOpenAiKey) || isNonEmpty(hostOpenAiKey)) {
|
||||
const source = isNonEmpty(configOpenAiKey) ? "adapter config env" : "server environment";
|
||||
if (isNonEmpty(configOpenAiKey)) {
|
||||
checks.push({
|
||||
code: "codex_openai_api_key_present",
|
||||
level: "info",
|
||||
message: "OPENAI_API_KEY is set for Codex authentication.",
|
||||
detail: `Detected in ${source}.`,
|
||||
detail: "Detected in adapter runtime env.",
|
||||
});
|
||||
} else {
|
||||
checks.push({
|
||||
code: "codex_openai_api_key_missing",
|
||||
level: "warn",
|
||||
message: "OPENAI_API_KEY is not set. Codex runs may fail until authentication is configured.",
|
||||
hint: "Set OPENAI_API_KEY in adapter env, shell environment, or Codex auth configuration.",
|
||||
hint: "Set OPENAI_API_KEY in connector secret bindings or Codex auth configuration.",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,6 +143,7 @@ export async function testEnvironment(
|
||||
})();
|
||||
|
||||
const args = ["exec", "--json"];
|
||||
args.push("--skip-git-repo-check");
|
||||
if (search) args.unshift("--search");
|
||||
if (bypass) args.push("--dangerously-bypass-approvals-and-sandbox");
|
||||
if (model) args.push("--model", model);
|
||||
|
||||
@@ -214,7 +214,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
env.AGENT_COMPANY_API_KEY = authToken;
|
||||
}
|
||||
const billingType = resolveGeminiBillingType(env);
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const runtimeEnv = ensurePathInEnv(env);
|
||||
await ensureCommandResolvable(command, cwd, runtimeEnv);
|
||||
|
||||
const timeoutSec = asNumber(config.timeoutSec, 0);
|
||||
|
||||
@@ -70,7 +70,7 @@ export async function testEnvironment(
|
||||
for (const [key, value] of Object.entries(envConfig)) {
|
||||
if (typeof value === "string") env[key] = value;
|
||||
}
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const runtimeEnv = ensurePathInEnv(env);
|
||||
try {
|
||||
await ensureCommandResolvable(command, cwd, runtimeEnv);
|
||||
checks.push({
|
||||
@@ -88,22 +88,16 @@ export async function testEnvironment(
|
||||
}
|
||||
|
||||
const configGeminiApiKey = env.GEMINI_API_KEY;
|
||||
const hostGeminiApiKey = process.env.GEMINI_API_KEY;
|
||||
const configGoogleApiKey = env.GOOGLE_API_KEY;
|
||||
const hostGoogleApiKey = process.env.GOOGLE_API_KEY;
|
||||
const hasGca = env.GOOGLE_GENAI_USE_GCA === "true" || process.env.GOOGLE_GENAI_USE_GCA === "true";
|
||||
const hasGca = env.GOOGLE_GENAI_USE_GCA === "true";
|
||||
if (
|
||||
isNonEmpty(configGeminiApiKey) ||
|
||||
isNonEmpty(hostGeminiApiKey) ||
|
||||
isNonEmpty(configGoogleApiKey) ||
|
||||
isNonEmpty(hostGoogleApiKey) ||
|
||||
hasGca
|
||||
) {
|
||||
const source = hasGca
|
||||
? "Google account login (GCA)"
|
||||
: isNonEmpty(configGeminiApiKey) || isNonEmpty(configGoogleApiKey)
|
||||
? "adapter config env"
|
||||
: "server environment";
|
||||
: "adapter runtime env";
|
||||
checks.push({
|
||||
code: "gemini_api_key_present",
|
||||
level: "info",
|
||||
@@ -115,7 +109,7 @@ export async function testEnvironment(
|
||||
code: "gemini_api_key_missing",
|
||||
level: "info",
|
||||
message: "No explicit API key detected. Gemini CLI may still authenticate via `gemini auth login` (OAuth).",
|
||||
hint: "If the hello probe fails with an auth error, set GEMINI_API_KEY or GOOGLE_API_KEY in adapter env, or run `gemini auth login`.",
|
||||
hint: "If the hello probe fails with an auth error, set GEMINI_API_KEY or GOOGLE_API_KEY in connector secret bindings, or run `gemini auth login`.",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@agentcompany/adapter-kimi-local",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./server": "./src/server/index.ts",
|
||||
"./ui": "./src/ui/index.ts",
|
||||
"./cli": "./src/cli/index.ts"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
},
|
||||
"./server": {
|
||||
"types": "./dist/server/index.d.ts",
|
||||
"import": "./dist/server/index.js"
|
||||
},
|
||||
"./ui": {
|
||||
"types": "./dist/ui/index.d.ts",
|
||||
"import": "./dist/ui/index.js"
|
||||
},
|
||||
"./cli": {
|
||||
"types": "./dist/cli/index.d.ts",
|
||||
"import": "./dist/cli/index.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentcompany/adapter-utils": "workspace:*",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.6.0",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import pc from "picocolors";
|
||||
|
||||
export function printKimiStreamEvent(line: string, debug = false) {
|
||||
if (debug) {
|
||||
console.log(pc.dim(`[kimi] ${line}`));
|
||||
return;
|
||||
}
|
||||
console.log(line);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { printKimiStreamEvent } from "./format-event.js";
|
||||
@@ -0,0 +1,33 @@
|
||||
export const type = "kimi_local";
|
||||
export const label = "Kimi Code (local)";
|
||||
export const DEFAULT_KIMI_LOCAL_MODEL = "kimi-for-coding";
|
||||
|
||||
export const models = [
|
||||
{ id: DEFAULT_KIMI_LOCAL_MODEL, label: DEFAULT_KIMI_LOCAL_MODEL },
|
||||
];
|
||||
|
||||
export const agentConfigurationDoc = `# kimi_local agent configuration
|
||||
|
||||
Adapter: kimi_local
|
||||
|
||||
Use when:
|
||||
- You want AgentCompany to invoke the official Kimi Code CLI locally
|
||||
- You accept generic CLI execution without session resume or structured transcript guarantees
|
||||
|
||||
Core fields:
|
||||
- cwd (string, optional): absolute working directory fallback for the agent process
|
||||
- promptTemplate (string, optional): prompt template rendered by AgentCompany before execution
|
||||
- model (string, optional): informational only, defaults to kimi-for-coding
|
||||
- command (string, optional): defaults to "kimi"
|
||||
- extraArgs (string[], optional): additional CLI args
|
||||
- env (object, optional): KEY=VALUE environment variables
|
||||
|
||||
Operational fields:
|
||||
- timeoutSec (number, optional): run timeout in seconds
|
||||
- graceSec (number, optional): graceful termination period in seconds
|
||||
|
||||
Notes:
|
||||
- Kimi integration currently runs in generic CLI mode.
|
||||
- Session resume, model discovery, graceful cancel handshakes, structured transcript, and cost attribution are not guaranteed.
|
||||
- AgentCompany must surface these capability downgrades explicitly in the desktop UI.
|
||||
`;
|
||||
@@ -0,0 +1,73 @@
|
||||
import type { AdapterExecutionContext, AdapterExecutionResult } from "@agentcompany/adapter-utils";
|
||||
import {
|
||||
asNumber,
|
||||
asString,
|
||||
asStringArray,
|
||||
ensureAbsoluteDirectory,
|
||||
ensureCommandResolvable,
|
||||
ensurePathInEnv,
|
||||
runChildProcess,
|
||||
} from "@agentcompany/adapter-utils/server-utils";
|
||||
import { DEFAULT_KIMI_LOCAL_MODEL } from "../index.js";
|
||||
|
||||
function normalizeEnv(input: unknown): Record<string, string> {
|
||||
if (typeof input !== "object" || input === null || Array.isArray(input)) return {};
|
||||
const env: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
|
||||
if (typeof value === "string") env[key] = value;
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult> {
|
||||
const { runId, config, context, onLog } = ctx;
|
||||
const command = asString(config.command, "kimi");
|
||||
const cwd = asString(config.cwd, process.cwd());
|
||||
const model = asString(config.model, DEFAULT_KIMI_LOCAL_MODEL);
|
||||
const timeoutSec = asNumber(config.timeoutSec, 0);
|
||||
const graceSec = asNumber(config.graceSec, 20);
|
||||
const extraArgs = asStringArray(config.extraArgs);
|
||||
|
||||
await ensureAbsoluteDirectory(cwd, { createIfMissing: true });
|
||||
|
||||
const prompt = typeof context.taskTitle === "string" && context.taskTitle.length > 0
|
||||
? [
|
||||
context.taskTitle,
|
||||
typeof context.taskDescription === "string" && context.taskDescription.length > 0
|
||||
? context.taskDescription
|
||||
: "",
|
||||
].filter(Boolean).join("\n\n")
|
||||
: [
|
||||
`You are agent ${ctx.agent.name}.`,
|
||||
"Respond with a concise execution summary when complete.",
|
||||
].join("\n\n");
|
||||
|
||||
const env = normalizeEnv(ensurePathInEnv(normalizeEnv(config.env)));
|
||||
await ensureCommandResolvable(command, cwd, env);
|
||||
|
||||
const result = await runChildProcess(runId, command, extraArgs, {
|
||||
cwd,
|
||||
env,
|
||||
timeoutSec,
|
||||
graceSec,
|
||||
stdin: prompt,
|
||||
onLog,
|
||||
});
|
||||
|
||||
const summary =
|
||||
result.stdout
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.slice(-8)
|
||||
.join("\n") || null;
|
||||
|
||||
return {
|
||||
exitCode: result.exitCode,
|
||||
signal: result.signal,
|
||||
timedOut: result.timedOut,
|
||||
summary,
|
||||
model,
|
||||
billingType: "unknown",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { execute } from "./execute.js";
|
||||
export { testEnvironment } from "./test.js";
|
||||
@@ -0,0 +1,62 @@
|
||||
import type {
|
||||
AdapterEnvironmentTestContext,
|
||||
AdapterEnvironmentTestResult,
|
||||
} from "@agentcompany/adapter-utils";
|
||||
import {
|
||||
asString,
|
||||
ensureCommandResolvable,
|
||||
ensurePathInEnv,
|
||||
} from "@agentcompany/adapter-utils/server-utils";
|
||||
|
||||
function normalizeEnv(input: unknown): Record<string, string> {
|
||||
if (typeof input !== "object" || input === null || Array.isArray(input)) return {};
|
||||
const env: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
|
||||
if (typeof value === "string") env[key] = value;
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
export async function testEnvironment(
|
||||
ctx: AdapterEnvironmentTestContext,
|
||||
): Promise<AdapterEnvironmentTestResult> {
|
||||
const command = asString(ctx.config.command, "kimi");
|
||||
const cwd = asString(ctx.config.cwd, process.cwd());
|
||||
const env = normalizeEnv(ensurePathInEnv(normalizeEnv(ctx.config.env)));
|
||||
|
||||
try {
|
||||
await ensureCommandResolvable(command, cwd, env);
|
||||
return {
|
||||
adapterType: "kimi_local",
|
||||
status: "warn",
|
||||
checks: [
|
||||
{
|
||||
code: "kimi_command_detected",
|
||||
level: "info",
|
||||
message: `Detected Kimi Code command "${command}".`,
|
||||
},
|
||||
{
|
||||
code: "kimi_runtime_generic_mode",
|
||||
level: "warn",
|
||||
message:
|
||||
"Kimi integration is running in generic CLI mode. Session resume, model discovery, and structured transcript are not guaranteed.",
|
||||
},
|
||||
],
|
||||
testedAt: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
adapterType: "kimi_local",
|
||||
status: "fail",
|
||||
checks: [
|
||||
{
|
||||
code: "kimi_command_missing",
|
||||
level: "error",
|
||||
message: error instanceof Error ? error.message : "Kimi command is not available.",
|
||||
hint: "Install Kimi Code with `curl -L code.kimi.com/install.sh | bash` and ensure the CLI is available on PATH.",
|
||||
},
|
||||
],
|
||||
testedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import type { CreateConfigValues } from "@agentcompany/adapter-utils";
|
||||
import { DEFAULT_KIMI_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 buildKimiLocalConfig(v: CreateConfigValues): Record<string, unknown> {
|
||||
const config: Record<string, unknown> = {};
|
||||
if (v.cwd) config.cwd = v.cwd;
|
||||
if (v.promptTemplate) config.promptTemplate = v.promptTemplate;
|
||||
config.model = v.model || DEFAULT_KIMI_LOCAL_MODEL;
|
||||
config.timeoutSec = 0;
|
||||
config.graceSec = 20;
|
||||
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) config.env = env;
|
||||
if (v.command) config.command = v.command;
|
||||
if (v.extraArgs) config.extraArgs = parseCommaArgs(v.extraArgs);
|
||||
return config;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { buildKimiLocalConfig } from "./build-config.js";
|
||||
export { parseKimiStdoutLine } from "./parse-stdout.js";
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { TranscriptEntry } from "@agentcompany/adapter-utils";
|
||||
|
||||
export function parseKimiStdoutLine(line: string, ts: string): TranscriptEntry[] {
|
||||
const text = line.trim();
|
||||
if (!text) return [];
|
||||
return [{ kind: "stdout", ts, text: line }];
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseKimiStdoutLine } from "../src/ui/parse-stdout.js";
|
||||
|
||||
describe("parseKimiStdoutLine", () => {
|
||||
it("returns a stdout entry for non-empty lines", () => {
|
||||
const entries = parseKimiStdoutLine("hello", "2026-03-17T00:00:00.000Z");
|
||||
expect(entries).toEqual([
|
||||
{ kind: "stdout", ts: "2026-03-17T00:00:00.000Z", text: "hello" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("drops blank lines", () => {
|
||||
expect(parseKimiStdoutLine(" ", "2026-03-17T00:00:00.000Z")).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -159,7 +159,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
env.AGENT_COMPANY_API_KEY = authToken;
|
||||
}
|
||||
const runtimeEnv = Object.fromEntries(
|
||||
Object.entries(ensurePathInEnv({ ...process.env, ...env })).filter(
|
||||
Object.entries(ensurePathInEnv(env)).filter(
|
||||
(entry): entry is [string, string] => typeof entry[1] === "string",
|
||||
),
|
||||
);
|
||||
|
||||
@@ -106,7 +106,7 @@ export async function discoverOpenCodeModels(input: {
|
||||
const command = resolveOpenCodeCommand(input.command);
|
||||
const cwd = asString(input.cwd, process.cwd());
|
||||
const env = normalizeEnv(input.env);
|
||||
const runtimeEnv = normalizeEnv(ensurePathInEnv({ ...process.env, ...env }));
|
||||
const runtimeEnv = normalizeEnv(ensurePathInEnv(env));
|
||||
|
||||
const result = await runChildProcess(
|
||||
`opencode-models-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function testEnvironment(
|
||||
});
|
||||
}
|
||||
|
||||
const runtimeEnv = normalizeEnv(ensurePathInEnv({ ...process.env, ...env }));
|
||||
const runtimeEnv = normalizeEnv(ensurePathInEnv(env));
|
||||
|
||||
const cwdInvalid = checks.some((check) => check.code === "opencode_cwd_invalid");
|
||||
if (cwdInvalid) {
|
||||
@@ -191,9 +191,13 @@ export async function testEnvironment(
|
||||
}
|
||||
}
|
||||
|
||||
const modelUnavailable = checks.some((check) => check.code === "opencode_hello_probe_model_unavailable");
|
||||
if (!configuredModel && !modelUnavailable) {
|
||||
// No model configured – skip model requirement if no model-related checks exist
|
||||
if (!configuredModel) {
|
||||
checks.push({
|
||||
code: "opencode_model_missing",
|
||||
level: "warn",
|
||||
message: "OpenCode requires a configured provider/model ID before runs can start.",
|
||||
hint: "Run `opencode models`, choose a provider/model, and save it in the connector settings.",
|
||||
});
|
||||
} else if (configuredModel && canRunProbe) {
|
||||
try {
|
||||
await ensureOpenCodeModelConfiguredAndAvailable({
|
||||
|
||||
@@ -272,7 +272,7 @@ export async function runChildProcess(
|
||||
const onLogError = opts.onLogError ?? ((err, id, msg) => console.warn({ err, runId: id }, msg));
|
||||
|
||||
return new Promise<RunProcessResult>((resolve, reject) => {
|
||||
const rawMerged: NodeJS.ProcessEnv = { ...process.env, ...opts.env };
|
||||
const rawMerged: NodeJS.ProcessEnv = { ...opts.env };
|
||||
|
||||
// Strip Claude Code nesting-guard env vars so spawned `claude` processes
|
||||
// don't refuse to start with "cannot be launched inside another session".
|
||||
|
||||
Reference in new issue
Block a user