























@@ -31,8 +31,14 @@ import { startGatewayServer } from "./server.js";
3131const LIVE = isLiveTestEnabled();
3232const CODEX_BIND_LIVE = isTruthyEnvValue(process.env.OPENCLAW_LIVE_CODEX_BIND);
3333const describeLive = LIVE && CODEX_BIND_LIVE ? describe : describe.skip;
34-const CODEX_BIND_TIMEOUT_MS = 10 * 60_000;
35-const CODEX_BIND_REQUEST_TIMEOUT_MS = 180_000;
34+const CODEX_BIND_TIMEOUT_MS = resolveLiveTimeoutMs(
35+process.env.OPENCLAW_LIVE_CODEX_BIND_TIMEOUT_MS,
36+900_000,
37+);
38+const CODEX_BIND_REQUEST_TIMEOUT_MS = resolveLiveTimeoutMs(
39+process.env.OPENCLAW_LIVE_CODEX_BIND_REQUEST_TIMEOUT_MS,
40+300_000,
41+);
3642const DEFAULT_CODEX_BIND_MODEL = "gpt-5.5";
37433844type CapturedOutboundReply = {
@@ -42,6 +48,15 @@ type CapturedOutboundReply = {
4248to: string;
4349};
445051+function resolveLiveTimeoutMs(raw: string | undefined, fallback: number): number {
52+const parsed = raw ? Number(raw) : Number.NaN;
53+return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
54+}
55+56+function logCodexBindStep(message: string): void {
57+console.info(`[live-codex-bind] ${message}`);
58+}
59+4560function createSlackCurrentConversationBindingRegistry(outboundReplies: CapturedOutboundReply[]) {
4661return createTestRegistry([
4762{
@@ -164,21 +179,32 @@ function restoreEnvVar(name: string, value: string | undefined): void {
164179process.env[name] = value;
165180}
166181167-async function waitForAgentRunOk(client: GatewayClient, runId: string): Promise<void> {
168-const result: { status?: string } = await client.request(
169-"agent.wait",
170-{ runId, timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS },
171-{ timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS + 5_000 },
172-);
182+async function waitForAgentRunOk(
183+client: GatewayClient,
184+runId: string,
185+context: string,
186+): Promise<void> {
187+let result: { status?: string };
188+try {
189+result = await client.request(
190+"agent.wait",
191+{ runId, timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS },
192+{ timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS + 5_000 },
193+);
194+} catch (error) {
195+const message = error instanceof Error ? error.message : String(error);
196+throw new Error(`${context}: agent.wait error for ${runId}: ${message}`, { cause: error });
197+}
173198if (result?.status !== "ok") {
174-throw new Error(`agent.wait failed for ${runId}: status=${String(result?.status)}`);
199+throw new Error(`${context}: agent.wait failed for ${runId}: status=${String(result?.status)}`);
175200}
176201}
177202178203async function sendChatAndWait(params: {
179204client: GatewayClient;
180205sessionKey: string;
181206idempotencyKey: string;
207+context: string;
182208message: string;
183209originatingChannel: string;
184210originatingTo: string;
@@ -201,9 +227,13 @@ async function sendChatAndWait(params: {
201227attachments: params.attachments,
202228});
203229if (started?.status !== "started" || typeof started.runId !== "string") {
204-throw new Error(`chat.send did not start correctly: ${JSON.stringify(started)}`);
230+throw new Error(
231+`${params.context}: chat.send did not start correctly: ${JSON.stringify(started)}`,
232+);
205233}
206-await waitForAgentRunOk(params.client, started.runId);
234+logCodexBindStep(`${params.context} started (${started.runId})`);
235+await waitForAgentRunOk(params.client, started.runId, params.context);
236+logCodexBindStep(`${params.context} completed`);
207237}
208238209239async function waitForAssistantText(params: {
@@ -344,8 +374,10 @@ async function writeGatewayConfig(params: {
344374agents: {
345375defaults: {
346376workspace: params.workspace,
347-agentRuntime: { id: "codex" },
348377model: { primary: `${modelProvider}/${params.model}` },
378+models: {
379+[`${modelProvider}/${params.model}`]: { agentRuntime: { id: "codex" } },
380+},
349381skipBootstrap: true,
350382heartbeat: { every: "0m" },
351383sandbox: { mode: "off" },
@@ -462,6 +494,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
462494 client,
463495 sessionKey,
464496idempotencyKey: `idem-codex-bind-${randomUUID()}`,
497+context: "bind command",
465498message: `/codex bind --cwd ${workspace} --model ${bindModel}${
466499 bindProvider ? ` --provider ${bindProvider}` : ""
467500 }`,
@@ -481,13 +514,15 @@ describeLive("gateway live (native Codex conversation binding)", () => {
481514 accountId,
482515 conversationId,
483516});
517+logCodexBindStep(`binding resolved to ${boundSessionKey}`);
484518let commandReplyCount = bindReply.outboundTexts.length;
485519486520const sendCodexCommand = async (message: string, contains: string, timeoutMs = 60_000) => {
487521await sendChatAndWait({
488522 client,
489523 sessionKey,
490524idempotencyKey: `idem-codex-command-${randomUUID()}`,
525+context: message,
491526 message,
492527originatingChannel: "slack",
493528originatingTo: conversationId,
@@ -530,6 +565,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
530565 client,
531566 sessionKey,
532567idempotencyKey: `idem-codex-bound-text-${randomUUID()}`,
568+context: "bound text turn",
533569message: `Reply with exactly this token and nothing else: ${textToken}`,
534570originatingChannel: "slack",
535571originatingTo: conversationId,
@@ -547,6 +583,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
547583 client,
548584 sessionKey,
549585idempotencyKey: `idem-codex-bound-image-${randomUUID()}`,
586+context: "bound image turn",
550587message:
551588"What animal is drawn in the attached image? Reply with only the lowercase animal name.",
552589originatingChannel: "slack",
@@ -578,7 +615,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
578615clearRuntimeConfigSnapshot();
579616await client.stopAndWait({ timeoutMs: 2_000 }).catch(() => {});
580617await server.close();
581-await fs.rm(tempRoot, { recursive: true, force: true });
618+await fs.rm(tempRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
582619restoreEnvVar("CODEX_HOME", previous.codexHome);
583620restoreEnvVar("OPENCLAW_CONFIG_PATH", previous.configPath);
584621restoreEnvVar("OPENCLAW_GATEWAY_TOKEN", previous.gatewayToken);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。