Commit f7047bcf authored by edy's avatar edy

fix(desktop): inject lobster key into workspace entries

parent b178b48c
Pipeline #18456 failed
...@@ -60,6 +60,7 @@ import { ...@@ -60,6 +60,7 @@ import {
buildProjectModelRuntime, buildProjectModelRuntime,
materializeProjectModelRuntime materializeProjectModelRuntime
} from "./services/project-model-runtime.js"; } from "./services/project-model-runtime.js";
import { buildWorkspaceEntryLobsterEnv } from "./services/workspace-entry-lobster-env.js";
import { import {
refreshProjectContextAfterExecution, refreshProjectContextAfterExecution,
shouldRefreshProjectContextAfterExecution shouldRefreshProjectContextAfterExecution
...@@ -732,6 +733,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -732,6 +733,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
return runtime.env; return runtime.env;
}; };
const prepareWorkspaceEntryLobsterEnv = async (): Promise<Record<string, string>> => {
return buildWorkspaceEntryLobsterEnv(await secretManager.getApiKey());
};
const resolveConfiguredChatModel = async (config?: AppConfig) => { const resolveConfiguredChatModel = async (config?: AppConfig) => {
const nextConfig = config ?? await getEffectiveConfig(); const nextConfig = config ?? await getEffectiveConfig();
const baseUrl = nextConfig.expertModelConfig.copywriting.baseUrl.trim(); const baseUrl = nextConfig.expertModelConfig.copywriting.baseUrl.trim();
...@@ -1769,6 +1774,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -1769,6 +1774,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
runtimeCloudSupervisor.noteMessageReceived(executionSessionId, prompt, executionSkillId); runtimeCloudSupervisor.noteMessageReceived(executionSessionId, prompt, executionSkillId);
try { try {
if (preparedExecution.decision.kind === "workspace-entry") { if (preparedExecution.decision.kind === "workspace-entry") {
const lobsterEnv = await prepareWorkspaceEntryLobsterEnv();
const projectModelEnv = await prepareProjectModelRuntime( const projectModelEnv = await prepareProjectModelRuntime(
preparedExecution.sessionState.projectId, preparedExecution.sessionState.projectId,
preparedExecution.sessionState.projectRoot preparedExecution.sessionState.projectRoot
...@@ -1779,7 +1785,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -1779,7 +1785,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
prompt: preparedExecution.decision.preparedPrompt, prompt: preparedExecution.decision.preparedPrompt,
userPrompt: prompt, userPrompt: prompt,
attachments: preparedExecution.attachments, attachments: preparedExecution.attachments,
extraEnv: projectModelEnv extraEnv: {
...projectModelEnv,
...lobsterEnv
}
}); });
if ("handoff" in result) { if ("handoff" in result) {
const fallbackExecutionPolicy = await resolveExecutionPolicy( const fallbackExecutionPolicy = await resolveExecutionPolicy(
...@@ -2067,6 +2076,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -2067,6 +2076,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
flushQueuedEvents(); flushQueuedEvents();
void (async () => { void (async () => {
try { try {
const lobsterEnv = await prepareWorkspaceEntryLobsterEnv();
const projectModelEnv = await prepareProjectModelRuntime( const projectModelEnv = await prepareProjectModelRuntime(
preparedExecution.sessionState.projectId, preparedExecution.sessionState.projectId,
preparedExecution.sessionState.projectRoot preparedExecution.sessionState.projectRoot
...@@ -2077,7 +2087,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -2077,7 +2087,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
prompt: preparedExecution.decision.preparedPrompt, prompt: preparedExecution.decision.preparedPrompt,
userPrompt: prompt, userPrompt: prompt,
attachments: preparedExecution.attachments, attachments: preparedExecution.attachments,
extraEnv: projectModelEnv extraEnv: {
...projectModelEnv,
...lobsterEnv
}
}, { }, {
onStarted: (runId) => { onStarted: (runId) => {
queueOrSend({ queueOrSend({
......
const LOBSTER_KEY_REQUIRED_MESSAGE = "请先绑定龙虾密钥";
export function buildWorkspaceEntryLobsterEnv(apiKey: string | undefined): Record<string, string> {
const lobsterKey = apiKey?.trim();
if (!lobsterKey) {
throw new Error(LOBSTER_KEY_REQUIRED_MESSAGE);
}
return {
QJCLAW_LOBSTER_KEY: lobsterKey,
OPENCLAW_EMPLOYEE_API_KEY: lobsterKey
};
}
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const repoRoot = path.resolve(__dirname, "..", "..");
const tempRoot = path.join(repoRoot, ".tmp", "workspace-entry-lobster-env-smoke");
const compileRoot = path.join(tempRoot, "compiled");
const sourcePath = path.join(repoRoot, "build", "scripts", "workspace-entry-lobster-env-smoke.ts");
const entryPath = path.join(compileRoot, "build", "scripts", "workspace-entry-lobster-env-smoke.js");
const resultPath = process.argv[2] ? path.resolve(process.argv[2]) : path.join(tempRoot, "result.json");
if (!existsSync(sourcePath)) {
throw new Error(`Workspace-entry lobster env smoke source was not found: ${sourcePath}`);
}
rmSync(compileRoot, { recursive: true, force: true });
mkdirSync(compileRoot, { recursive: true });
execFileSync("corepack", [
"pnpm",
"--dir",
path.join(repoRoot, "apps", "desktop"),
"exec",
"tsc",
"--module",
"ES2022",
"--moduleResolution",
"node",
"--target",
"ES2022",
"--lib",
"ES2022",
"--types",
"node",
"--esModuleInterop",
"--allowSyntheticDefaultImports",
"--skipLibCheck",
"--outDir",
compileRoot,
sourcePath
], { stdio: "inherit" });
if (!existsSync(entryPath)) {
throw new Error(`Workspace-entry lobster env smoke entry was not emitted: ${entryPath}`);
}
writeFileSync(path.join(compileRoot, "package.json"), "{\"type\":\"module\"}", "utf8");
execFileSync("node", [entryPath, resultPath], { stdio: "inherit" });
if (!existsSync(resultPath)) {
throw new Error(`Workspace-entry lobster env smoke did not produce a result file: ${resultPath}`);
}
import { mkdir, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { buildWorkspaceEntryLobsterEnv } from "../../apps/desktop/src/main/services/workspace-entry-lobster-env.js";
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
function assertThrowsMissingKey(input: string | undefined): void {
try {
buildWorkspaceEntryLobsterEnv(input);
} catch (error) {
assert(error instanceof Error, "Missing-key path should throw an Error.");
assert(error.message === "请先绑定龙虾密钥", "Missing-key error message changed.");
return;
}
throw new Error("Missing-key path did not throw.");
}
async function main(): Promise<void> {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const repoRoot = path.resolve(__dirname, "..", "..");
const resultPath = path.resolve(process.argv[2] ?? path.join(repoRoot, ".tmp", "workspace-entry-lobster-env-smoke", "result.json"));
const tempRoot = path.dirname(resultPath);
await rm(tempRoot, { recursive: true, force: true });
await mkdir(tempRoot, { recursive: true });
const env = buildWorkspaceEntryLobsterEnv(" lobster-secret ");
assert(env.QJCLAW_LOBSTER_KEY === "lobster-secret", "Primary lobster env var should use the trimmed key.");
assert(env.OPENCLAW_EMPLOYEE_API_KEY === "lobster-secret", "Compatibility env var should use the trimmed key.");
assert(Object.keys(env).length === 2, "Workspace-entry lobster env should expose only the two expected variables.");
assertThrowsMissingKey(undefined);
assertThrowsMissingKey("");
assertThrowsMissingKey(" ");
await writeFile(resultPath, JSON.stringify({
ok: true,
envKeys: Object.keys(env)
}, null, 2), "utf8");
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
"smoke:mac:workspace-service": "node build/scripts/mac-workspace-service-smoke.mjs", "smoke:mac:workspace-service": "node build/scripts/mac-workspace-service-smoke.mjs",
"smoke:mac:workspace-startup": "node build/scripts/mac-workspace-startup-smoke.mjs", "smoke:mac:workspace-startup": "node build/scripts/mac-workspace-startup-smoke.mjs",
"smoke:workspace-agent-runner": "node build/scripts/workspace-agent-runner-smoke.mjs", "smoke:workspace-agent-runner": "node build/scripts/workspace-agent-runner-smoke.mjs",
"smoke:workspace-entry-lobster-env": "node build/scripts/workspace-entry-lobster-env-smoke.mjs",
"smoke:bundled-runtime": "powershell -ExecutionPolicy Bypass -File build/scripts/bundled-runtime-smoke.ps1", "smoke:bundled-runtime": "powershell -ExecutionPolicy Bypass -File build/scripts/bundled-runtime-smoke.ps1",
"smoke:workspace-entry": "powershell -ExecutionPolicy Bypass -File build/scripts/workspace-entry-smoke.ps1", "smoke:workspace-entry": "powershell -ExecutionPolicy Bypass -File build/scripts/workspace-entry-smoke.ps1",
"smoke:cloud-bundle": "powershell -ExecutionPolicy Bypass -File build/scripts/cloud-bundle-smoke.ps1", "smoke:cloud-bundle": "powershell -ExecutionPolicy Bypass -File build/scripts/cloud-bundle-smoke.ps1",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment