@openai-lite/codex-feishu 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openai-lite/codex-feishu",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Feishu bridge for Codex with dual-end synchronized conversations.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5944,6 +5944,13 @@ export async function runDaemon() {
5944
5944
  signal: info.signal ?? null,
5945
5945
  });
5946
5946
  });
5947
+ app.on("error", async (err) => {
5948
+ await appendEvent(store, {
5949
+ source: "app_server",
5950
+ type: "error",
5951
+ error: err?.message ?? String(err),
5952
+ });
5953
+ });
5947
5954
 
5948
5955
  try {
5949
5956
  await app.ensureStarted();
@@ -2,6 +2,17 @@ import { EventEmitter, once } from "node:events";
2
2
  import { spawn } from "node:child_process";
3
3
  import readline from "node:readline";
4
4
 
5
+ function getCodexSpawnOptions(base = {}) {
6
+ if (process.platform === "win32") {
7
+ return {
8
+ ...base,
9
+ // Windows commonly resolves codex via codex.cmd; shell mode keeps it executable.
10
+ shell: true,
11
+ };
12
+ }
13
+ return base;
14
+ }
15
+
5
16
  function normalizeJsonRpcPayload(payload) {
6
17
  if (!payload || typeof payload !== "object") {
7
18
  return null;
@@ -77,10 +88,14 @@ async function probeSubcommand(codexBin, args, timeoutMs = 2_500) {
77
88
  let stdout = "";
78
89
  let stderr = "";
79
90
  let timer = null;
80
- const child = spawn(codexBin, args, {
81
- stdio: ["ignore", "pipe", "pipe"],
82
- env: process.env,
83
- });
91
+ const child = spawn(
92
+ codexBin,
93
+ args,
94
+ getCodexSpawnOptions({
95
+ stdio: ["ignore", "pipe", "pipe"],
96
+ env: process.env,
97
+ }),
98
+ );
84
99
  child.stdout.on("data", (chunk) => {
85
100
  stdout += chunk.toString("utf8");
86
101
  });
@@ -135,7 +150,10 @@ async function probeSubcommand(codexBin, args, timeoutMs = 2_500) {
135
150
  export class AppServerClient extends EventEmitter {
136
151
  constructor(options = {}) {
137
152
  super();
138
- this.codexBin = options.codexBin || process.env.CODEX_BIN || "codex";
153
+ this.codexBin =
154
+ options.codexBin ||
155
+ process.env.CODEX_BIN ||
156
+ (process.platform === "win32" ? "codex.cmd" : "codex");
139
157
  this.transport = options.transport || process.env.CODEX_FEISHU_TRANSPORT || "auto";
140
158
  this.protoCwd = options.protoCwd || process.env.CODEX_FEISHU_CWD || process.cwd();
141
159
  this.protoModel = options.protoModel || process.env.CODEX_FEISHU_MODEL || "gpt-5.3-codex";
@@ -236,11 +254,15 @@ export class AppServerClient extends EventEmitter {
236
254
  }
237
255
 
238
256
  async startLegacyAppServer() {
239
- const child = spawn(this.codexBin, ["app-server", "--listen", "stdio://"], {
240
- stdio: ["pipe", "pipe", "pipe"],
241
- env: process.env,
242
- cwd: this.protoCwd,
243
- });
257
+ const child = spawn(
258
+ this.codexBin,
259
+ ["app-server", "--listen", "stdio://"],
260
+ getCodexSpawnOptions({
261
+ stdio: ["pipe", "pipe", "pipe"],
262
+ env: process.env,
263
+ cwd: this.protoCwd,
264
+ }),
265
+ );
244
266
  this.process = child;
245
267
 
246
268
  child.on("error", (err) => {
@@ -309,11 +331,15 @@ export class AppServerClient extends EventEmitter {
309
331
  }
310
332
 
311
333
  async startProto() {
312
- const child = spawn(this.codexBin, ["proto"], {
313
- stdio: ["pipe", "pipe", "pipe"],
314
- env: process.env,
315
- cwd: this.protoCwd,
316
- });
334
+ const child = spawn(
335
+ this.codexBin,
336
+ ["proto"],
337
+ getCodexSpawnOptions({
338
+ stdio: ["pipe", "pipe", "pipe"],
339
+ env: process.env,
340
+ cwd: this.protoCwd,
341
+ }),
342
+ );
317
343
  this.process = child;
318
344
  this.protoReady = {};
319
345
 
@@ -56,6 +56,22 @@ async function isDaemonRpcResponsive(timeoutMs = 1000) {
56
56
  }
57
57
  }
58
58
 
59
+ async function readLogTail(logPath, maxLines = 60) {
60
+ try {
61
+ const text = await readTextIfExists(logPath);
62
+ if (!text) {
63
+ return "";
64
+ }
65
+ const lines = text
66
+ .split(/\r?\n/)
67
+ .filter((line) => line.trim().length > 0)
68
+ .slice(-maxLines);
69
+ return lines.join("\n");
70
+ } catch {
71
+ return "";
72
+ }
73
+ }
74
+
59
75
  async function stopByPid(pid, timeoutMs = 3000) {
60
76
  if (!isPidAlive(pid)) {
61
77
  return { action: "already_stopped", pid };
@@ -201,7 +217,7 @@ export async function restartDaemonDetached() {
201
217
  for (const cliEntry of candidateEntries) {
202
218
  attempts.push([process.execPath, [cliEntry, "daemon"]]);
203
219
  }
204
- attempts.push(["codex-feishu", ["daemon"]]);
220
+ attempts.push(["cmd.exe", ["/d", "/s", "/c", "codex-feishu daemon"]]);
205
221
  } else {
206
222
  attempts.push(["codex-feishu", ["daemon"]]);
207
223
  for (const cliEntry of candidateEntries) {
@@ -234,8 +250,10 @@ export async function restartDaemonDetached() {
234
250
  failedAttempts.push(`${cmd} ${args.join(" ")} => ${startResult.error}`);
235
251
  }
236
252
  if (!startResult.ok) {
253
+ const logTail = await readLogTail(logPath, 80);
237
254
  const details = failedAttempts.length > 0 ? `; attempts: ${failedAttempts.join(" | ")}` : "";
238
- throw new Error(`failed to start daemon in background: ${startResult.error}${details}`);
255
+ const logHint = logTail ? `; daemon.log tail:\n${logTail}` : "";
256
+ throw new Error(`failed to start daemon in background: ${startResult.error}${details}${logHint}`);
239
257
  }
240
258
 
241
259
  if (startResult.pid) {