@debbl/relay 0.0.2 → 0.0.3

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/dist/index.mjs CHANGED
@@ -1125,11 +1125,11 @@ async function listOpenProjects() {
1125
1125
 
1126
1126
  //#endregion
1127
1127
  //#region src/locales/en/messages.po
1128
- const messages$1 = JSON.parse("{\"1xKjU/\":[\"Failed to read open projects. Please try again later.\"],\"36QmnO\":[\"thread: \",[\"0\"]],\"4IDydv\":[\"Invalid relay config at \",[\"configPath\"],\": root must be a JSON object.\"],\"4Vrm3r\":[\"cwd: \",[\"0\"]],\"6tcMXX\":[\"Invalid relay config: \",[\"field\"],\" is required and must be a non-empty string.\"],\"AWIU5i\":[\"Relay config is missing. Template created at \",[\"configPath\"],\". Please edit this file and restart.\"],\"Bb/dZi\":[\"Available commands:\"],\"CfFOzJ\":[\"Invalid relay config at \",[\"configPath\"],\": env must be a JSON object.\"],\"Ck6rmi\":[\"Current session has been cleared.\"],\"FZcpfm\":[\"Failed to process message. Please try again later.\"],\"G1WYAl\":[\"Unknown command \\\"\",[\"0\"],\"\\\".\\n\\n\",[\"helpText\"]],\"H7VlDR\":[\"Currently busy. Please try again later.\"],\"HEx9te\":[\"New Session\"],\"Hrkm8q\":[\"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"],\"JvAWQ5\":[\"No working directories are currently open.\"],\"K8IUjY\":[\"mode: \",[\"0\"]],\"Ks6r4a\":[\"Invalid relay config: \",[\"field\"],\" must be a string.\"],\"MZ28Ys\":[\"Failed to read open projects: \",[\"0\"]],\"NX40Ku\":[\"/new [default|plan] - Create a new session\"],\"OZZfXh\":[\"Current session status:\"],\"Oeb7jH\":[\"model: \",[\"0\"]],\"Qq8U1p\":[\"/status does not accept arguments.\\n\\n\",[\"helpText\"]],\"RLcmLa\":[\"/status - Show current session status\"],\"Rn/EAE\":[\"You are a session title generator.\\nGenerate a short English title based on the user message.\\nStrict requirements:\\n1. Output title text only, with no explanation.\\n2. Output a single line with no line breaks.\\n3. Do not use quotes.\\n4. Keep the title within 24 characters.\"],\"T/qXZl\":[\"/new accepts at most one optional argument: default or plan.\\n\\n\",[\"helpText\"]],\"Xg0HiS\":[\"/mode <default|plan> - Switch current session mode\"],\"YwjZm9\":[\"No active session. Send a normal message or use /new to create one.\"],\"aQiwam\":[\"Failed to start relay: \",[\"message\"]],\"dV7HiV\":[\"Invalid relay config: LOCALE \\\"\",[\"0\"],\"\\\" is not supported. Falling back to en.\"],\"dXeZCW\":[\"Switched to \",[\"0\"],\" mode.\"],\"dZhhUX\":[\"Created a new session.\"],\"doQwEN\":[\"Codex execution failed: \",[\"0\"]],\"eie2Mj\":[\"Command cannot be empty.\\n\\n\",[\"helpText\"]],\"gHcqpZ\":[\"Invalid mode \\\"\",[\"modeToken\"],\"\\\", only default or plan are supported.\\n\\n\",[\"helpText\"]],\"gf810l\":[\"/mode requires one argument: default or plan.\\n\\n\",[\"helpText\"]],\"j3bsc1\":[[\"0\"],\". \",[\"root\"]],\"ksj3fj\":[\"Invalid relay config: LOCALE \\\"\",[\"normalized\"],\"\\\" is not supported. Falling back to en.\"],\"lksJXf\":[\"/projects - Show current working directories\"],\"mehbut\":[\"No active session. Send a normal message or use /new to create one first.\"],\"miupLy\":[\"Current working directories:\"],\"o4F8ck\":[\"/reset does not accept arguments.\\n\\n\",[\"helpText\"]],\"oEMyJf\":[\"Please send a text message.\"],\"oKQkYZ\":[\"/projects does not accept arguments.\\n\\n\",[\"helpText\"]],\"orhkAj\":[\"Codex execution failed. Please try again later.\"],\"qN3BkN\":[\"/reset - Clear current session\"],\"rHDhWM\":[\"Failed to parse message. Please send a text message.\"],\"rdodSw\":[\"You are a session title generator.\\nGenerate a short Chinese title based on the user message.\\nStrict requirements:\\n1. Output title text only, with no explanation.\\n2. Output a single line with no line breaks.\\n3. Do not use quotes or title marks.\\n4. Keep the title within 24 characters.\"],\"smfLBQ\":[\"Cannot identify sender. Please try again later.\"],\"tHEFWw\":[\"title: \",[\"title\"]],\"tZQUtS\":[\"Failed to read relay config at \",[\"configPath\"],\": \",[\"0\"]],\"uAkDSp\":[\"User message: \",[\"0\"]],\"wBrugH\":[\"/help does not accept arguments.\\n\\n\",[\"helpText\"]],\"wSNjFy\":[\"/help - Show help\"],\"z+7ZYe\":[\"Invalid JSON in relay config at \",[\"configPath\"],\": \",[\"0\"]]}");
1128
+ const messages$1 = JSON.parse("{\"1xKjU/\":[\"Failed to read open projects. Please try again later.\"],\"36QmnO\":[\"thread: \",[\"0\"]],\"4IDydv\":[\"Invalid relay config at \",[\"configPath\"],\": root must be a JSON object.\"],\"4Vrm3r\":[\"cwd: \",[\"0\"]],\"6tcMXX\":[\"Invalid relay config: \",[\"field\"],\" is required and must be a non-empty string.\"],\"AWIU5i\":[\"Relay config is missing. Template created at \",[\"configPath\"],\". Please edit this file and restart.\"],\"Bb/dZi\":[\"Available commands:\"],\"CfFOzJ\":[\"Invalid relay config at \",[\"configPath\"],\": env must be a JSON object.\"],\"CgiJb7\":[\"Invalid relay config: locale \\\"\",[\"0\"],\"\\\" is not supported. Falling back to \",[\"systemLocale\"],\".\"],\"Ck6rmi\":[\"Current session has been cleared.\"],\"FZcpfm\":[\"Failed to process message. Please try again later.\"],\"G1WYAl\":[\"Unknown command \\\"\",[\"0\"],\"\\\".\\n\\n\",[\"helpText\"]],\"H7VlDR\":[\"Currently busy. Please try again later.\"],\"HEx9te\":[\"New Session\"],\"Hrkm8q\":[\"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"],\"JvAWQ5\":[\"No working directories are currently open.\"],\"K8IUjY\":[\"mode: \",[\"0\"]],\"Ks6r4a\":[\"Invalid relay config: \",[\"field\"],\" must be a string.\"],\"MZ28Ys\":[\"Failed to read open projects: \",[\"0\"]],\"NX40Ku\":[\"/new [default|plan] - Create a new session\"],\"OZZfXh\":[\"Current session status:\"],\"Oeb7jH\":[\"model: \",[\"0\"]],\"Qq8U1p\":[\"/status does not accept arguments.\\n\\n\",[\"helpText\"]],\"RLcmLa\":[\"/status - Show current session status\"],\"Rn/EAE\":[\"You are a session title generator.\\nGenerate a short English title based on the user message.\\nStrict requirements:\\n1. Output title text only, with no explanation.\\n2. Output a single line with no line breaks.\\n3. Do not use quotes.\\n4. Keep the title within 24 characters.\"],\"T/qXZl\":[\"/new accepts at most one optional argument: default or plan.\\n\\n\",[\"helpText\"]],\"Xg0HiS\":[\"/mode <default|plan> - Switch current session mode\"],\"YwjZm9\":[\"No active session. Send a normal message or use /new to create one.\"],\"aQiwam\":[\"Failed to start relay: \",[\"message\"]],\"dV7HiV\":[\"Invalid relay config: LOCALE \\\"\",[\"0\"],\"\\\" is not supported. Falling back to en.\"],\"dXeZCW\":[\"Switched to \",[\"0\"],\" mode.\"],\"dZhhUX\":[\"Created a new session.\"],\"doQwEN\":[\"Codex execution failed: \",[\"0\"]],\"eie2Mj\":[\"Command cannot be empty.\\n\\n\",[\"helpText\"]],\"gHcqpZ\":[\"Invalid mode \\\"\",[\"modeToken\"],\"\\\", only default or plan are supported.\\n\\n\",[\"helpText\"]],\"gf810l\":[\"/mode requires one argument: default or plan.\\n\\n\",[\"helpText\"]],\"j3bsc1\":[[\"0\"],\". \",[\"root\"]],\"ksj3fj\":[\"Invalid relay config: LOCALE \\\"\",[\"normalized\"],\"\\\" is not supported. Falling back to en.\"],\"lksJXf\":[\"/projects - Show current working directories\"],\"mehbut\":[\"No active session. Send a normal message or use /new to create one first.\"],\"miupLy\":[\"Current working directories:\"],\"o4F8ck\":[\"/reset does not accept arguments.\\n\\n\",[\"helpText\"]],\"oEMyJf\":[\"Please send a text message.\"],\"oKQkYZ\":[\"/projects does not accept arguments.\\n\\n\",[\"helpText\"]],\"orhkAj\":[\"Codex execution failed. Please try again later.\"],\"qN3BkN\":[\"/reset - Clear current session\"],\"rHDhWM\":[\"Failed to parse message. Please send a text message.\"],\"rdodSw\":[\"You are a session title generator.\\nGenerate a short Chinese title based on the user message.\\nStrict requirements:\\n1. Output title text only, with no explanation.\\n2. Output a single line with no line breaks.\\n3. Do not use quotes or title marks.\\n4. Keep the title within 24 characters.\"],\"smfLBQ\":[\"Cannot identify sender. Please try again later.\"],\"tHEFWw\":[\"title: \",[\"title\"]],\"tZQUtS\":[\"Failed to read relay config at \",[\"configPath\"],\": \",[\"0\"]],\"uAkDSp\":[\"User message: \",[\"0\"]],\"wBrugH\":[\"/help does not accept arguments.\\n\\n\",[\"helpText\"]],\"wSNjFy\":[\"/help - Show help\"],\"xBlpjC\":[\"Invalid relay config: locale \\\"\",[\"normalized\"],\"\\\" is not supported. Falling back to \",[\"systemLocale\"],\".\"],\"z+7ZYe\":[\"Invalid JSON in relay config at \",[\"configPath\"],\": \",[\"0\"]]}");
1129
1129
 
1130
1130
  //#endregion
1131
1131
  //#region src/locales/zh/messages.po
1132
- const messages = JSON.parse("{\"1xKjU/\":[\"读取已打开项目失败,请稍后重试。\"],\"36QmnO\":[\"thread: \",[\"0\"]],\"4IDydv\":[\"中继配置 \",[\"configPath\"],\" 无效:root 必须是 JSON 对象。\"],\"4Vrm3r\":[\"cwd: \",[\"0\"]],\"6tcMXX\":[\"中继配置无效:\",[\"field\"],\" 为必填项且必须为非空字符串。\"],\"AWIU5i\":[\"中继配置缺失,已在 \",[\"configPath\"],\" 创建模板。请编辑该文件后重启。\"],\"Bb/dZi\":[\"可用命令:\"],\"CfFOzJ\":[\"中继配置 \",[\"configPath\"],\" 无效:env 必须是 JSON 对象。\"],\"Ck6rmi\":[\"当前会话已清空。\"],\"FZcpfm\":[\"处理消息失败,请稍后重试。\"],\"G1WYAl\":[\"未知命令 \\\"\",[\"0\"],\"\\\"。\\n\\n\",[\"helpText\"]],\"H7VlDR\":[\"当前忙碌,请稍后重试。\"],\"HEx9te\":[\"新会话\"],\"Hrkm8q\":[\"中继配置无效:CODEX_TIMEOUT_MS 必须是正整数。\"],\"JvAWQ5\":[\"当前没有打开的工作目录。\"],\"K8IUjY\":[\"mode: \",[\"0\"]],\"Ks6r4a\":[\"中继配置无效:\",[\"field\"],\" 必须是字符串。\"],\"MZ28Ys\":[\"读取已打开项目失败:\",[\"0\"]],\"NX40Ku\":[\"/new [default|plan] - 创建新会话\"],\"OZZfXh\":[\"当前会话状态:\"],\"Oeb7jH\":[\"model: \",[\"0\"]],\"Qq8U1p\":[\"/status 不接受参数。\\n\\n\",[\"helpText\"]],\"RLcmLa\":[\"/status - 显示当前会话状态\"],\"Rn/EAE\":[\"你是会话标题生成器。\\n根据用户消息生成简短英文标题。\\n严格要求:\\n1. 仅输出标题文字,不要解释。\\n2. 单行输出,不要换行。\\n3. 不要使用引号。\\n4. 标题不超过 24 个字符。\"],\"T/qXZl\":[\"/new 最多接受一个可选参数:default 或 plan。\\n\\n\",[\"helpText\"]],\"Xg0HiS\":[\"/mode <default|plan> - 切换当前会话模式\"],\"YwjZm9\":[\"没有活跃会话。请发送普通消息或使用 /new 创建会话。\"],\"aQiwam\":[\"启动中继失败:\",[\"message\"]],\"dV7HiV\":[\"中继配置无效:不支持的 LOCALE \\\"\",[\"0\"],\"\\\",已回退为 en。\"],\"dXeZCW\":[\"已切换到 \",[\"0\"],\" 模式。\"],\"dZhhUX\":[\"已创建新会话。\"],\"doQwEN\":[\"Codex 执行失败:\",[\"0\"]],\"eie2Mj\":[\"命令不能为空。\\n\\n\",[\"helpText\"]],\"gHcqpZ\":[\"无效的模式 \\\"\",[\"modeToken\"],\"\\\",仅支持 default 或 plan。\\n\\n\",[\"helpText\"]],\"gf810l\":[\"/mode 需要一个参数:default 或 plan。\\n\\n\",[\"helpText\"]],\"j3bsc1\":[[\"0\"],\". \",[\"root\"]],\"ksj3fj\":[\"中继配置无效:不支持的 LOCALE \\\"\",[\"normalized\"],\"\\\",已回退为 en。\"],\"lksJXf\":[\"/projects - 显示当前工作目录\"],\"mehbut\":[\"没有活跃会话。请先发送普通消息或使用 /new 创建会话。\"],\"miupLy\":[\"当前工作目录:\"],\"o4F8ck\":[\"/reset 不接受参数。\\n\\n\",[\"helpText\"]],\"oEMyJf\":[\"请发送文本消息。\"],\"oKQkYZ\":[\"/projects 不接受参数。\\n\\n\",[\"helpText\"]],\"orhkAj\":[\"Codex 执行失败,请稍后重试。\"],\"qN3BkN\":[\"/reset - 清空当前会话\"],\"rHDhWM\":[\"解析消息失败,请发送文本消息。\"],\"rdodSw\":[\"你是会话标题生成器。\\n根据用户消息生成简短中文标题。\\n严格要求:\\n1. 仅输出标题文字,不要解释。\\n2. 单行输出,不要换行。\\n3. 不要使用引号或书名号。\\n4. 标题不超过 24 个字符。\"],\"smfLBQ\":[\"无法识别发送者,请稍后重试。\"],\"tHEFWw\":[\"title: \",[\"title\"]],\"tZQUtS\":[\"读取中继配置失败 \",[\"configPath\"],\":\",[\"0\"]],\"uAkDSp\":[\"用户消息:\",[\"0\"]],\"wBrugH\":[\"/help 不接受参数。\\n\\n\",[\"helpText\"]],\"wSNjFy\":[\"/help - 显示帮助\"],\"z+7ZYe\":[\"中继配置 \",[\"configPath\"],\" 中的 JSON 无效:\",[\"0\"]]}");
1132
+ const messages = JSON.parse("{\"1xKjU/\":[\"读取已打开项目失败,请稍后重试。\"],\"36QmnO\":[\"thread: \",[\"0\"]],\"4IDydv\":[\"relay 配置 \",[\"configPath\"],\" 无效:root 必须是 JSON 对象。\"],\"4Vrm3r\":[\"cwd: \",[\"0\"]],\"6tcMXX\":[\"relay 配置无效:\",[\"field\"],\" 为必填项且必须为非空字符串。\"],\"AWIU5i\":[\"relay 配置缺失,已在 \",[\"configPath\"],\" 创建模板。请编辑该文件后重启。\"],\"Bb/dZi\":[\"可用命令:\"],\"CfFOzJ\":[\"relay 配置 \",[\"configPath\"],\" 无效:env 必须是 JSON 对象。\"],\"CgiJb7\":[\"relay 配置无效:不支持的 locale \\\"\",[\"0\"],\"\\\",已回退为 \",[\"systemLocale\"],\"。\"],\"Ck6rmi\":[\"当前会话已清空。\"],\"FZcpfm\":[\"处理消息失败,请稍后重试。\"],\"G1WYAl\":[\"未知命令 \\\"\",[\"0\"],\"\\\"。\\n\\n\",[\"helpText\"]],\"H7VlDR\":[\"当前忙碌,请稍后重试。\"],\"HEx9te\":[\"新会话\"],\"Hrkm8q\":[\"relay 配置无效:CODEX_TIMEOUT_MS 必须是正整数。\"],\"JvAWQ5\":[\"当前没有打开的工作目录。\"],\"K8IUjY\":[\"mode: \",[\"0\"]],\"Ks6r4a\":[\"relay 配置无效:\",[\"field\"],\" 必须是字符串。\"],\"MZ28Ys\":[\"读取已打开项目失败:\",[\"0\"]],\"NX40Ku\":[\"/new [default|plan] - 创建新会话\"],\"OZZfXh\":[\"当前会话状态:\"],\"Oeb7jH\":[\"model: \",[\"0\"]],\"Qq8U1p\":[\"/status 不接受参数。\\n\\n\",[\"helpText\"]],\"RLcmLa\":[\"/status - 显示当前会话状态\"],\"Rn/EAE\":[\"你是会话标题生成器。\\n根据用户消息生成简短英文标题。\\n严格要求:\\n1. 仅输出标题文字,不要解释。\\n2. 单行输出,不要换行。\\n3. 不要使用引号。\\n4. 标题不超过 24 个字符。\"],\"T/qXZl\":[\"/new 最多接受一个可选参数:default 或 plan。\\n\\n\",[\"helpText\"]],\"Xg0HiS\":[\"/mode <default|plan> - 切换当前会话模式\"],\"YwjZm9\":[\"没有活跃会话。请发送普通消息或使用 /new 创建会话。\"],\"aQiwam\":[\"启动 relay 失败:\",[\"message\"]],\"dV7HiV\":[\"relay 配置无效:不支持的 LOCALE \\\"\",[\"0\"],\"\\\",已回退为 en。\"],\"dXeZCW\":[\"已切换到 \",[\"0\"],\" 模式。\"],\"dZhhUX\":[\"已创建新会话。\"],\"doQwEN\":[\"Codex 执行失败:\",[\"0\"]],\"eie2Mj\":[\"命令不能为空。\\n\\n\",[\"helpText\"]],\"gHcqpZ\":[\"无效的模式 \\\"\",[\"modeToken\"],\"\\\",仅支持 default 或 plan。\\n\\n\",[\"helpText\"]],\"gf810l\":[\"/mode 需要一个参数:default 或 plan。\\n\\n\",[\"helpText\"]],\"j3bsc1\":[[\"0\"],\". \",[\"root\"]],\"ksj3fj\":[\"relay 配置无效:不支持的 LOCALE \\\"\",[\"normalized\"],\"\\\",已回退为 en。\"],\"lksJXf\":[\"/projects - 显示当前工作目录\"],\"mehbut\":[\"没有活跃会话。请先发送普通消息或使用 /new 创建会话。\"],\"miupLy\":[\"当前工作目录:\"],\"o4F8ck\":[\"/reset 不接受参数。\\n\\n\",[\"helpText\"]],\"oEMyJf\":[\"请发送文本消息。\"],\"oKQkYZ\":[\"/projects 不接受参数。\\n\\n\",[\"helpText\"]],\"orhkAj\":[\"Codex 执行失败,请稍后重试。\"],\"qN3BkN\":[\"/reset - 清空当前会话\"],\"rHDhWM\":[\"解析消息失败,请发送文本消息。\"],\"rdodSw\":[\"你是会话标题生成器。\\n根据用户消息生成简短中文标题。\\n严格要求:\\n1. 仅输出标题文字,不要解释。\\n2. 单行输出,不要换行。\\n3. 不要使用引号或书名号。\\n4. 标题不超过 24 个字符。\"],\"smfLBQ\":[\"无法识别发送者,请稍后重试。\"],\"tHEFWw\":[\"title: \",[\"title\"]],\"tZQUtS\":[\"读取relay 配置失败 \",[\"configPath\"],\":\",[\"0\"]],\"uAkDSp\":[\"用户消息:\",[\"0\"]],\"wBrugH\":[\"/help 不接受参数。\\n\\n\",[\"helpText\"]],\"wSNjFy\":[\"/help - 显示帮助\"],\"xBlpjC\":[\"relay 配置无效:不支持的 locale \\\"\",[\"normalized\"],\"\\\",已回退为 \",[\"systemLocale\"],\"。\"],\"z+7ZYe\":[\"relay 配置 \",[\"configPath\"],\" 中的 JSON 无效:\",[\"0\"]]}");
1133
1133
 
1134
1134
  //#endregion
1135
1135
  //#region src/i18n/runtime.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["COMMAND_HELP","COMMAND_NEW","COMMAND_MODE","COMMAND_STATUS","COMMAND_PROJECTS","COMMAND_RESET","getHelpText","t","join","parseCommand","input","normalized","trim","helpText","length","type","message","startsWith","prompt","parts","split","command","toLowerCase","modeToken","mode","parseMode","fs","path","sessionStore","Map","sessionQueue","SESSION_FILE_NAME","SESSION_FILE_VERSION","persistenceState","initializeSessionStore","input","relayDir","join","homeDir","filePath","mkdirSync","recursive","ensureSessionFileExists","persisted","readPersistedSessionsFile","workspaceSessions","workspaces","workspaceCwd","clear","activeBySessionKey","sessionRef","set","sessionKey","hydrateSession","data","getSessionKey","chatType","chatId","userId","getSession","get","setSession","session","persistSetSession","clearSession","delete","persistClearSession","withSessionLock","run","previous","Promise","resolve","running","then","queueItem","undefined","resetSessionStore","state","savedAt","Date","toISOString","activeSession","toPersistedActiveSession","historySession","toPersistedSessionSnapshot","getOrCreateWorkspaceSessions","history","historyBySessionKey","threadId","push","updatedAt","writePersistedSessionsFile","existsSync","initialContent","JSON","stringify","createEmptyPersistedSessionsFile","writeFileSync","encoding","flag","version","raw","readFileSync","error","Error","formatError","parsed","parse","parsePersistedSessionsFile","value","isObject","TypeError","workspaceValue","Object","entries","parseWorkspaceSessions","parseWorkspaceActiveSession","parseWorkspaceHistorySessions","parsePersistedActiveSession","entryKey","historyValue","trim","length","Array","isArray","map","item","index","parsePersistedSessionSnapshot","location","parseNonEmptyString","snapshot","model","mode","title","normalizeOptionalTitle","cwd","normalized","existing","created","tempPath","now","Math","random","toString","slice","content","renameSync","rmSync","force","message","String","getSessionKey","getHelpText","parseCommand","MAX_SESSION_TITLE_LENGTH","handleIncomingText","input","deps","sessionKey","chatType","chatId","userId","senderId","withSessionLock","parsed","text","currentSession","getSession","type","message","t","title","normalizeSessionTitle","threadId","mode","model","join","result","listOpenProjects","roots","length","lines","map","root","index","error","formatProjectsError","clearSession","setSession","created","createThread","cwd","formatCodexError","runTurn","prompt","session","resolveSessionTitle","Error","trim","currentTitle","undefined","buildFallbackSessionTitle","normalizedPrompt","normalizePrompt","truncateTitle","normalized","chars","Array","from","slice","replace","shouldProcessMessage","event","botOpenId","isMessageFromBot","message","chat_type","shouldHandleGroupMessage","mentions","length","some","mention","id","open_id","resolveSenderId","senderId","user_id","union_id","senderType","sender","sender_type","toLowerCase","sender_id","isPlainObject","isMessageFromBot","resolveSenderId","shouldHandleGroupMessage","buildReplyForMessageEvent","event","deps","botOpenId","message","message_type","t","text","parseTextContent","content","chat_type","mentions","senderId","sender","sender_id","normalizedText","stripMentionTags","trim","length","handleIncomingText","chatType","chatId","chat_id","replace","parsed","JSON","parse","isPlainObject","parseRpcLine","line","parsed","JSON","parse","method","isRpcRequestId","id","params","isRpcErrorObject","error","result","formatRpcError","code","message","value","isRpcErrorResponse","isRpcSuccessResponse","isRpcServerRequest","getServerRequestResult","decision","acceptSettings","forSession","endsWith","answers","success","contentItems","type","text","spawn","createInterface","formatRpcError","getServerRequestResult","isRpcErrorResponse","isRpcServerRequest","isRpcSuccessResponse","parseRpcLine","CodexAppServerClient","setNotificationHandler","handler","notificationHandler","request","method","params","exited","Error","buildExitMessage","requestId","nextId","responsePromise","Promise","resolve","reject","pending","set","value","payload","JSON","stringify","jsonrpc","id","child","stdin","write","error","delete","dispose","lineReader","close","killed","kill","handleStdoutLine","line","parsed","respondToServerRequest","catch","stderrBuffer","push","String","get","result","sendRpcResult","sendRpcError","writeRpcPayload","code","message","serialized","signal","suffix","length","at","options","Map","commandArgs","codexBin","cwd","stdio","input","stdout","crlfDelay","Infinity","on","stderr","chunk","text","trim","values","clear","isPlainObject","initializeClient","client","request","clientInfo","name","title","version","capabilities","experimentalApi","getCollaborationModes","raw","isCollaborationModeListResponse","Error","data","openThread","session","cwd","startThread","resumed","resumeThread","threadId","error","isThreadMissingError","approvalPolicy","sandbox","experimentalRawEvents","parseThreadResult","selectCollaborationModePayload","masks","mode","model","selected","find","mask","toLowerCase","settings","reasoning_effort","developer_instructions","isThreadResult","thread","id","message","includes","isCollaborationModeMask","value","modeIsValid","Array","isArray","every","isPlainObject","createTurnAccumulator","turnCompleted","turnError","lastAgentMessageByItem","lastAgentMessageByTask","applyTurnNotification","accumulator","notification","method","params","message","item","type","text","msg","last_agent_message","turn","error","status","resolveTurnMessage","CodexAppServerClient","getCollaborationModes","initializeClient","openThread","selectCollaborationModePayload","startThread","applyTurnNotification","createTurnAccumulator","resolveTurnMessage","formatRpcError","parseRpcLine","DEFAULT_CODEX_BIN","createCodexThread","input","client","cwd","codexBin","runWithOptionalTimeout","opened","threadId","model","mode","timeoutMs","dispose","runCodexTurn","accumulator","turnDone","createDeferred","turnDoneResolved","setNotificationHandler","notification","turnCompleted","resolve","modeMasks","session","collaborationMode","request","type","text","prompt","text_elements","promise","turnError","Error","message","trim","length","reject","run","onTimeout","withTimeout","Promise","innerResolve","innerReject","timeoutHandle","timeoutPromise","_","setTimeout","race","clearTimeout","process","listOpenProjects","roots","cwd","i18n","messages","enMessages","zhMessages","DEFAULT_LOCALE","detectDefaultLocale","CATALOGS","en","zh","activeLocale","initializeI18n","locale","resolved","resolveLocale","loadAndActivate","getCurrentLocale","ensureI18nInitialized","isSupportedLocale","getDefaultLocale","mappedLocale","mapToAppLocale","systemLocale","readSystemLocale","Intl","DateTimeFormat","resolvedOptions","undefined","normalized","trim","length","toLowerCase","replaceAll","startsWith","fs","os","path","process","getDefaultLocale","initializeI18n","isSupportedLocale","DEFAULT_CODEX_BIN","TEMPLATE_ENV_CONFIG","BASE_DOMAIN","APP_ID","APP_SECRET","BOT_OPEN_ID","CODEX_BIN","CODEX_TIMEOUT_MS","TEMPLATE_CONFIG","env","loadRelayConfig","options","homeDir","homedir","workspaceCwd","cwd","configDir","join","configPath","existsSync","ensureConfigTemplate","Error","t","parsed","parseConfigFile","locale","readLocale","localeValue","domain","readRequiredString","appId","appSecret","baseConfig","botOpenId","readOptionalString","codexBin","codexTimeoutMs","readTimeoutMs","mkdirSync","recursive","writeFileSync","JSON","stringify","encoding","flag","raw","readFileSync","error","formatError","parse","isObject","configObject","undefined","value","field","normalized","TypeError","trim","length","Number","isInteger","trimmed","test","parseInt","systemLocale","console","warn","formatInvalidLocale","mapped","mapLocaleToAppLocale","String","Array","isArray","toLowerCase","replaceAll","startsWith","message","process","loadRelayConfig","loadConfigOrExit","error","console","formatStartupError","exit","message","Error","String","t","resolveSenderId","getSession","getSessionKey","FALLBACK_REPLY_TAG","sendReply","larkClient","data","text","options","content","JSON","stringify","formatReplyTextWithThreadId","message","chat_type","im","v1","create","params","receive_id_type","receive_id","chat_id","msg_type","reply","path","message_id","includeThreadTag","trim","replyTag","resolveReplyTag","normalizedText","length","senderId","sender","sender_id","sessionKey","chatType","chatId","userId","session","threadId","process","Lark","isPlainObject","parseCommand","handleIncomingText","shouldProcessMessage","buildReplyForMessageEvent","stripMentionTags","createCodexThread","runCodexTurn","listOpenProjects","loadConfigOrExit","sendReply","initializeI18n","clearSession","getSession","initializeSessionStore","setSession","withSessionLock","relayConfig","locale","homeDir","workspaceCwd","error","message","Error","String","console","t","exit","BUSY_MESSAGE","client","Client","baseConfig","wsClient","WSClient","isTaskRunning","processIncomingEvent","data","reply","botOpenId","input","createThread","mode","cwd","codexBin","timeoutMs","codexTimeoutMs","runTurn","params","includeThreadTag","shouldAttachThreadTag","replyError","rawText","parseEventText","content","normalizedText","trim","length","type","parsed","JSON","parse","text","eventDispatcher","EventDispatcher","register","info","stringify","finally","start"],"sources":["../src/bot/commands.ts","../src/session/store.ts","../src/bot/handler.ts","../src/bot/message-filter.ts","../src/bot/relay.ts","../src/codex/rpc.ts","../src/codex/app-server-client.ts","../src/codex/thread.ts","../src/codex/turn-state.ts","../src/codex/app-server.ts","../src/codex/state.ts","../src/locales/en/messages.po","../src/locales/zh/messages.po","../src/i18n/runtime.ts","../src/core/config.ts","../src/core/startup.ts","../src/feishu/reply.ts","../src/index.ts"],"sourcesContent":["import { t } from '@lingui/core/macro'\nimport type { ChatMode, ParsedCommand } from '../core/types'\n\nconst COMMAND_HELP = '/help'\nconst COMMAND_NEW = '/new'\nconst COMMAND_MODE = '/mode'\nconst COMMAND_STATUS = '/status'\nconst COMMAND_PROJECTS = '/projects'\nconst COMMAND_RESET = '/reset'\n\nexport function getHelpText(): string {\n return [\n t`Available commands:`,\n t`/help - Show help`,\n t`/new [default|plan] - Create a new session`,\n t`/mode <default|plan> - Switch current session mode`,\n t`/status - Show current session status`,\n t`/projects - Show current working directories`,\n t`/reset - Clear current session`,\n ].join('\\n')\n}\n\nexport function parseCommand(input: string): ParsedCommand {\n const normalized = input.trim()\n const helpText = getHelpText()\n\n if (normalized.length === 0) {\n return {\n type: 'invalid',\n message: t`Command cannot be empty.\\n\\n${helpText}`,\n }\n }\n\n if (!normalized.startsWith('/')) {\n return { type: 'prompt', prompt: normalized }\n }\n\n const parts = normalized.split(/\\s+/)\n const command = parts[0]?.toLowerCase()\n\n if (command === COMMAND_HELP) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/help does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'help' }\n }\n\n if (command === COMMAND_NEW) {\n if (parts.length > 2) {\n return {\n type: 'invalid',\n message: t`/new accepts at most one optional argument: default or plan.\\n\\n${helpText}`,\n }\n }\n\n const modeToken = parts[1]\n if (!modeToken) {\n return { type: 'new', mode: 'default' }\n }\n\n const mode = parseMode(modeToken)\n if (!mode) {\n return {\n type: 'invalid',\n message: t`Invalid mode \"${modeToken}\", only default or plan are supported.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'new', mode }\n }\n\n if (command === COMMAND_MODE) {\n const modeToken = parts[1]\n if (!modeToken || parts.length > 2) {\n return {\n type: 'invalid',\n message: t`/mode requires one argument: default or plan.\\n\\n${helpText}`,\n }\n }\n\n const mode = parseMode(modeToken)\n if (!mode) {\n return {\n type: 'invalid',\n message: t`Invalid mode \"${modeToken}\", only default or plan are supported.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'mode', mode }\n }\n\n if (command === COMMAND_STATUS) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/status does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'status' }\n }\n\n if (command === COMMAND_PROJECTS) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/projects does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'projects' }\n }\n\n if (command === COMMAND_RESET) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/reset does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'reset' }\n }\n\n return {\n type: 'invalid',\n message: t`Unknown command \"${command ?? normalized}\".\\n\\n${helpText}`,\n }\n}\n\nfunction parseMode(input: string): ChatMode | null {\n const normalized = input.toLowerCase()\n if (normalized === 'default' || normalized === 'plan') {\n return normalized\n }\n\n return null\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { BotSession, ChatMode, SessionKeyInput } from '../core/types'\n\nconst sessionStore = new Map<string, BotSession>()\nconst sessionQueue = new Map<string, Promise<void>>()\nconst SESSION_FILE_NAME = 'sessions.json'\nconst SESSION_FILE_VERSION = 1 as const\n\ninterface PersistedSessionSnapshot {\n mode: ChatMode\n model: string\n title?: string\n savedAt: string\n}\n\ninterface PersistedActiveSession extends PersistedSessionSnapshot {\n sessionKey: string\n threadId: string\n}\n\ninterface PersistedWorkspaceSessions {\n activeBySessionKey: PersistedActiveSession | null\n historyBySessionKey: Record<string, PersistedSessionSnapshot[]>\n}\n\ninterface PersistedSessionsFile {\n version: typeof SESSION_FILE_VERSION\n updatedAt: string\n workspaces: Record<string, PersistedWorkspaceSessions>\n}\n\ninterface SessionStorePersistenceState {\n filePath: string\n workspaceCwd: string\n data: PersistedSessionsFile\n}\n\nlet persistenceState: SessionStorePersistenceState | null = null\n\nexport function initializeSessionStore(input: {\n homeDir: string\n workspaceCwd: string\n}): void {\n const relayDir = path.join(input.homeDir, '.relay')\n const filePath = path.join(relayDir, SESSION_FILE_NAME)\n\n fs.mkdirSync(relayDir, { recursive: true })\n ensureSessionFileExists(filePath)\n\n const persisted = readPersistedSessionsFile(filePath)\n const workspaceSessions = persisted.workspaces[input.workspaceCwd]\n\n sessionStore.clear()\n sessionQueue.clear()\n\n if (workspaceSessions) {\n if (workspaceSessions.activeBySessionKey) {\n const sessionRef = workspaceSessions.activeBySessionKey\n sessionStore.set(\n sessionRef.sessionKey,\n hydrateSession(sessionRef, input.workspaceCwd),\n )\n }\n }\n\n persistenceState = {\n filePath,\n workspaceCwd: input.workspaceCwd,\n data: persisted,\n }\n}\n\nexport function getSessionKey(input: SessionKeyInput): string {\n if (input.chatType === 'p2p') {\n return `p2p:${input.chatId}`\n }\n\n return `group:${input.chatId}:${input.userId}`\n}\n\nexport function getSession(sessionKey: string): BotSession | undefined {\n return sessionStore.get(sessionKey)\n}\n\nexport function setSession(sessionKey: string, session: BotSession): void {\n sessionStore.set(sessionKey, session)\n persistSetSession(sessionKey, session)\n}\n\nexport function clearSession(sessionKey: string): void {\n sessionStore.delete(sessionKey)\n persistClearSession(sessionKey)\n}\n\nexport async function withSessionLock<T>(\n sessionKey: string,\n run: () => Promise<T>,\n): Promise<T> {\n const previous = sessionQueue.get(sessionKey) ?? Promise.resolve()\n const running = previous.then(\n () => run(),\n () => run(),\n )\n const queueItem = running.then(\n () => undefined,\n () => undefined,\n )\n\n sessionQueue.set(sessionKey, queueItem)\n\n try {\n return await running\n } finally {\n if (sessionQueue.get(sessionKey) === queueItem) {\n sessionQueue.delete(sessionKey)\n }\n }\n}\n\nexport function resetSessionStore(): void {\n sessionStore.clear()\n sessionQueue.clear()\n persistenceState = null\n}\n\nfunction persistSetSession(sessionKey: string, session: BotSession): void {\n const state = persistenceState\n if (!state) {\n return\n }\n\n const savedAt = new Date().toISOString()\n const activeSession = toPersistedActiveSession(sessionKey, session, savedAt)\n const historySession = toPersistedSessionSnapshot(session, savedAt)\n const workspaceSessions = getOrCreateWorkspaceSessions(\n state.data,\n state.workspaceCwd,\n )\n\n workspaceSessions.activeBySessionKey = activeSession\n const history = workspaceSessions.historyBySessionKey[session.threadId] ?? []\n history.push(historySession)\n workspaceSessions.historyBySessionKey[session.threadId] = history\n\n state.data.updatedAt = savedAt\n writePersistedSessionsFile(state.filePath, state.data)\n}\n\nfunction persistClearSession(sessionKey: string): void {\n const state = persistenceState\n if (!state) {\n return\n }\n\n const workspaceSessions = state.data.workspaces[state.workspaceCwd]\n if (!workspaceSessions) {\n return\n }\n\n if (\n !workspaceSessions.activeBySessionKey ||\n workspaceSessions.activeBySessionKey.sessionKey !== sessionKey\n ) {\n return\n }\n\n workspaceSessions.activeBySessionKey = null\n\n state.data.updatedAt = new Date().toISOString()\n writePersistedSessionsFile(state.filePath, state.data)\n}\n\nfunction ensureSessionFileExists(filePath: string): void {\n if (fs.existsSync(filePath)) {\n return\n }\n\n const initialContent = `${JSON.stringify(createEmptyPersistedSessionsFile(), null, 2)}\\n`\n fs.writeFileSync(filePath, initialContent, { encoding: 'utf-8', flag: 'wx' })\n}\n\nfunction createEmptyPersistedSessionsFile(): PersistedSessionsFile {\n return {\n version: SESSION_FILE_VERSION,\n updatedAt: new Date().toISOString(),\n workspaces: {},\n }\n}\n\nfunction readPersistedSessionsFile(filePath: string): PersistedSessionsFile {\n let raw: string\n try {\n raw = fs.readFileSync(filePath, 'utf-8')\n } catch (error) {\n throw new Error(\n `Failed to read relay session index at ${filePath}: ${formatError(error)}`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n throw new Error(\n `Invalid JSON in relay session index at ${filePath}: ${formatError(error)}`,\n )\n }\n\n return parsePersistedSessionsFile(parsed, filePath)\n}\n\nfunction parsePersistedSessionsFile(\n value: unknown,\n filePath: string,\n): PersistedSessionsFile {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: root must be a JSON object.`,\n )\n }\n\n if (value.version !== SESSION_FILE_VERSION) {\n throw new Error(\n `Invalid relay session index at ${filePath}: version must be ${SESSION_FILE_VERSION}.`,\n )\n }\n\n if (typeof value.updatedAt !== 'string') {\n throw new TypeError(\n `Invalid relay session index at ${filePath}: updatedAt must be a string.`,\n )\n }\n\n if (!isObject(value.workspaces)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: workspaces must be a JSON object.`,\n )\n }\n\n const workspaces: Record<string, PersistedWorkspaceSessions> = {}\n for (const [workspaceCwd, workspaceValue] of Object.entries(\n value.workspaces,\n )) {\n workspaces[workspaceCwd] = parseWorkspaceSessions(\n workspaceValue,\n filePath,\n workspaceCwd,\n )\n }\n\n return {\n version: SESSION_FILE_VERSION,\n updatedAt: value.updatedAt,\n workspaces,\n }\n}\n\nfunction parseWorkspaceSessions(\n value: unknown,\n filePath: string,\n workspaceCwd: string,\n): PersistedWorkspaceSessions {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: workspace \"${workspaceCwd}\" must be a JSON object.`,\n )\n }\n\n const activeBySessionKey = parseWorkspaceActiveSession(\n value.activeBySessionKey,\n filePath,\n workspaceCwd,\n )\n const historyBySessionKey = parseWorkspaceHistorySessions(\n value.historyBySessionKey,\n filePath,\n workspaceCwd,\n )\n\n return {\n activeBySessionKey,\n historyBySessionKey,\n }\n}\n\nfunction parseWorkspaceActiveSession(\n value: unknown,\n filePath: string,\n workspaceCwd: string,\n): PersistedActiveSession | null {\n if (value === null || value === undefined) {\n return null\n }\n\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: activeBySessionKey for workspace \"${workspaceCwd}\" must be a JSON object or null.`,\n )\n }\n\n return parsePersistedActiveSession(\n value,\n filePath,\n `activeBySessionKey for workspace \"${workspaceCwd}\"`,\n )\n}\n\nfunction parseWorkspaceHistorySessions(\n value: unknown,\n filePath: string,\n workspaceCwd: string,\n): Record<string, PersistedSessionSnapshot[]> {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: historyBySessionKey for workspace \"${workspaceCwd}\" must be a JSON object.`,\n )\n }\n\n const historyBySessionKey: Record<string, PersistedSessionSnapshot[]> = {}\n for (const [entryKey, historyValue] of Object.entries(value)) {\n if (entryKey.trim().length === 0) {\n throw new Error(\n `Invalid relay session index at ${filePath}: historyBySessionKey key in workspace \"${workspaceCwd}\" must be a non-empty threadId.`,\n )\n }\n\n if (!Array.isArray(historyValue)) {\n throw new TypeError(\n `Invalid relay session index at ${filePath}: historyBySessionKey.${entryKey} must be an array.`,\n )\n }\n\n historyBySessionKey[entryKey] = historyValue.map((item, index) =>\n parsePersistedSessionSnapshot(\n item,\n filePath,\n `historyBySessionKey.${entryKey}[${index}]`,\n ),\n )\n }\n\n return historyBySessionKey\n}\n\nfunction parsePersistedActiveSession(\n value: unknown,\n filePath: string,\n location: string,\n): PersistedActiveSession {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location} must be a JSON object.`,\n )\n }\n\n const sessionKey = parseNonEmptyString(\n value.sessionKey,\n filePath,\n `${location}.sessionKey`,\n )\n const threadId = parseNonEmptyString(\n value.threadId,\n filePath,\n `${location}.threadId`,\n )\n const snapshot = parsePersistedSessionSnapshot(value, filePath, location)\n return {\n sessionKey,\n threadId,\n ...snapshot,\n }\n}\n\nfunction parsePersistedSessionSnapshot(\n value: unknown,\n filePath: string,\n location: string,\n): PersistedSessionSnapshot {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location} must be a JSON object.`,\n )\n }\n\n const model = parseNonEmptyString(value.model, filePath, `${location}.model`)\n if (value.mode !== 'default' && value.mode !== 'plan') {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location}.mode must be \"default\" or \"plan\".`,\n )\n }\n\n if (typeof value.savedAt !== 'string') {\n throw new TypeError(\n `Invalid relay session index at ${filePath}: ${location}.savedAt must be a string.`,\n )\n }\n\n const title = normalizeOptionalTitle(value.title)\n return {\n mode: value.mode,\n model,\n title,\n savedAt: value.savedAt,\n }\n}\n\nfunction parseNonEmptyString(\n value: unknown,\n filePath: string,\n location: string,\n): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location} must be a non-empty string.`,\n )\n }\n\n return value\n}\n\nfunction hydrateSession(\n sessionRef: PersistedActiveSession,\n cwd: string,\n): BotSession {\n return {\n threadId: sessionRef.threadId,\n mode: sessionRef.mode,\n model: sessionRef.model,\n cwd,\n title: normalizeOptionalTitle(sessionRef.title),\n }\n}\n\nfunction toPersistedActiveSession(\n sessionKey: string,\n session: BotSession,\n savedAt: string,\n): PersistedActiveSession {\n return {\n sessionKey,\n threadId: session.threadId,\n ...toPersistedSessionSnapshot(session, savedAt),\n }\n}\n\nfunction toPersistedSessionSnapshot(\n session: BotSession,\n savedAt: string,\n): PersistedSessionSnapshot {\n const title = normalizeOptionalTitle(session.title)\n\n return {\n mode: session.mode,\n model: session.model,\n title,\n savedAt,\n }\n}\n\nfunction normalizeOptionalTitle(title: unknown): string | undefined {\n if (typeof title !== 'string') {\n return undefined\n }\n\n const normalized = title.trim()\n if (normalized.length === 0) {\n return undefined\n }\n\n return normalized\n}\n\nfunction getOrCreateWorkspaceSessions(\n data: PersistedSessionsFile,\n workspaceCwd: string,\n): PersistedWorkspaceSessions {\n const existing = data.workspaces[workspaceCwd]\n if (existing) {\n return existing\n }\n\n const created: PersistedWorkspaceSessions = {\n activeBySessionKey: null,\n historyBySessionKey: {},\n }\n data.workspaces[workspaceCwd] = created\n return created\n}\n\nfunction writePersistedSessionsFile(\n filePath: string,\n data: PersistedSessionsFile,\n): void {\n const tempPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`\n const content = `${JSON.stringify(data, null, 2)}\\n`\n\n try {\n fs.writeFileSync(tempPath, content, 'utf-8')\n fs.renameSync(tempPath, filePath)\n } catch (error) {\n try {\n if (fs.existsSync(tempPath)) {\n fs.rmSync(tempPath, { force: true })\n }\n } catch {\n // Best-effort cleanup for temporary file.\n }\n\n throw new Error(\n `Failed to write relay session index at ${filePath}: ${formatError(error)}`,\n )\n }\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return error.message\n }\n\n return String(error)\n}\n","import { t } from '@lingui/core/macro'\nimport { getSessionKey } from '../session/store'\nimport { getHelpText, parseCommand } from './commands'\nimport type {\n BotSession,\n ChatMode,\n CodexTurnResult,\n HandleIncomingTextInput,\n OpenProjectsResult,\n} from '../core/types'\n\nconst MAX_SESSION_TITLE_LENGTH = 24\n\nexport interface HandleIncomingTextDeps {\n createThread: (mode: ChatMode) => Promise<BotSession>\n runTurn: (params: {\n prompt: string\n mode: ChatMode\n session: BotSession | null\n }) => Promise<CodexTurnResult>\n getSession: (sessionKey: string) => BotSession | undefined\n setSession: (sessionKey: string, session: BotSession) => void\n clearSession: (sessionKey: string) => void\n withSessionLock: <T>(sessionKey: string, run: () => Promise<T>) => Promise<T>\n listOpenProjects: () => Promise<OpenProjectsResult>\n}\n\nexport async function handleIncomingText(\n input: HandleIncomingTextInput,\n deps: HandleIncomingTextDeps,\n): Promise<string> {\n const sessionKey = getSessionKey({\n chatType: input.chatType,\n chatId: input.chatId,\n userId: input.senderId,\n })\n\n return deps.withSessionLock(sessionKey, async () => {\n const parsed = parseCommand(input.text)\n const currentSession = deps.getSession(sessionKey)\n\n if (parsed.type === 'invalid') {\n return parsed.message\n }\n\n if (parsed.type === 'help') {\n return getHelpText()\n }\n\n if (parsed.type === 'status') {\n if (!currentSession) {\n return t`No active session. Send a normal message or use /new to create one.`\n }\n\n const title =\n normalizeSessionTitle(currentSession.title) ?? t`New Session`\n\n return [\n t`Current session status:`,\n t`thread: ${currentSession.threadId}`,\n t`title: ${title}`,\n t`mode: ${currentSession.mode}`,\n t`model: ${currentSession.model}`,\n ].join('\\n')\n }\n\n if (parsed.type === 'projects') {\n try {\n const result = await deps.listOpenProjects()\n if (result.roots.length === 0) {\n return t`No working directories are currently open.`\n }\n\n const lines = result.roots.map(\n (root, index) => t`${index + 1}. ${root}`,\n )\n return [t`Current working directories:`, ...lines].join('\\n')\n } catch (error) {\n return formatProjectsError(error)\n }\n }\n\n if (parsed.type === 'reset') {\n deps.clearSession(sessionKey)\n return t`Current session has been cleared.`\n }\n\n if (parsed.type === 'mode') {\n if (!currentSession) {\n return t`No active session. Send a normal message or use /new to create one first.`\n }\n\n deps.setSession(sessionKey, {\n ...currentSession,\n mode: parsed.mode,\n })\n\n return t`Switched to ${parsed.mode} mode.`\n }\n\n if (parsed.type === 'new') {\n try {\n const created = await deps.createThread(parsed.mode)\n deps.setSession(sessionKey, created)\n return [\n t`Created a new session.`,\n t`thread: ${created.threadId}`,\n t`cwd: ${created.cwd}`,\n t`mode: ${created.mode}`,\n t`model: ${created.model}`,\n ].join('\\n')\n } catch (error) {\n return formatCodexError(error)\n }\n }\n\n try {\n const mode = currentSession?.mode ?? 'default'\n const result = await deps.runTurn({\n prompt: parsed.prompt,\n mode,\n session: currentSession ?? null,\n })\n\n const title = await resolveSessionTitle({\n currentSession,\n prompt: parsed.prompt,\n })\n\n deps.setSession(sessionKey, {\n threadId: result.threadId,\n model: result.model,\n mode: result.mode,\n cwd: result.cwd,\n title,\n })\n return result.message\n } catch (error) {\n return formatCodexError(error)\n }\n })\n}\n\nfunction formatCodexError(error: unknown): string {\n if (error instanceof Error && error.message.trim().length > 0) {\n return t`Codex execution failed: ${error.message}`\n }\n\n return t`Codex execution failed. Please try again later.`\n}\n\nfunction formatProjectsError(error: unknown): string {\n if (error instanceof Error && error.message.trim().length > 0) {\n return t`Failed to read open projects: ${error.message}`\n }\n\n return t`Failed to read open projects. Please try again later.`\n}\n\nasync function resolveSessionTitle(input: {\n currentSession: BotSession | undefined\n prompt: string\n}): Promise<string | undefined> {\n const currentTitle = normalizeSessionTitle(input.currentSession?.title)\n if (currentTitle) {\n return currentTitle\n }\n\n if (!input.currentSession) {\n return undefined\n }\n\n return buildFallbackSessionTitle(input.prompt)\n}\n\nfunction buildFallbackSessionTitle(prompt: string): string {\n const normalizedPrompt = normalizePrompt(prompt)\n if (normalizedPrompt.length === 0) {\n return t`New Session`\n }\n\n return truncateTitle(normalizedPrompt)\n}\n\nfunction normalizeSessionTitle(title: string | undefined): string | null {\n if (!title) {\n return null\n }\n\n const normalized = title.trim()\n if (normalized.length === 0) {\n return null\n }\n\n return normalized\n}\n\nfunction truncateTitle(input: string): string {\n const chars = Array.from(input)\n if (chars.length <= MAX_SESSION_TITLE_LENGTH) {\n return input\n }\n\n if (MAX_SESSION_TITLE_LENGTH <= 3) {\n return chars.slice(0, MAX_SESSION_TITLE_LENGTH).join('')\n }\n\n return `${chars.slice(0, MAX_SESSION_TITLE_LENGTH - 3).join('')}...`\n}\n\nfunction normalizePrompt(input: string): string {\n return input.replace(/\\s+/g, ' ').trim()\n}\n","import type { FeishuMention } from '../core/types'\n\ninterface EventSenderId {\n open_id?: string\n user_id?: string\n union_id?: string\n}\n\ninterface MessageFilterEvent {\n sender: {\n sender_type?: string\n sender_id?: EventSenderId\n }\n message: {\n chat_type: string\n mentions?: FeishuMention[]\n }\n}\n\nexport function shouldProcessMessage(\n event: MessageFilterEvent,\n botOpenId?: string,\n): boolean {\n if (isMessageFromBot(event, botOpenId)) {\n return false\n }\n\n if (event.message.chat_type === 'p2p') {\n return true\n }\n\n return shouldHandleGroupMessage(event.message.mentions, botOpenId)\n}\n\nexport function shouldHandleGroupMessage(\n mentions: FeishuMention[] | undefined,\n botOpenId?: string,\n): boolean {\n if (!mentions || mentions.length === 0) {\n return false\n }\n\n if (!botOpenId) {\n return true\n }\n\n return mentions.some((mention) => mention.id?.open_id === botOpenId)\n}\n\nexport function resolveSenderId(\n senderId: EventSenderId | undefined,\n): string | null {\n if (!senderId) {\n return null\n }\n\n return senderId.open_id ?? senderId.user_id ?? senderId.union_id ?? null\n}\n\nexport function isMessageFromBot(\n event: MessageFilterEvent,\n botOpenId?: string,\n): boolean {\n const senderType = event.sender.sender_type?.toLowerCase()\n if (senderType === 'app') {\n return true\n }\n\n if (!botOpenId) {\n return false\n }\n\n const senderId = resolveSenderId(event.sender.sender_id)\n return senderId === botOpenId\n}\n","import { t } from '@lingui/core/macro'\nimport { isPlainObject } from 'es-toolkit/predicate'\nimport {\n isMessageFromBot,\n resolveSenderId,\n shouldHandleGroupMessage,\n} from './message-filter'\nimport type { FeishuMention, HandleIncomingTextInput } from '../core/types'\n\nexport interface ReceiveMessageEvent {\n sender: {\n sender_type?: string\n sender_id?: {\n open_id?: string\n user_id?: string\n union_id?: string\n }\n }\n message: {\n chat_id: string\n chat_type: string\n message_type: string\n content: string\n mentions?: FeishuMention[]\n }\n}\n\nexport interface RelayBotDeps {\n botOpenId?: string\n handleIncomingText: (input: HandleIncomingTextInput) => Promise<string>\n}\n\nexport { shouldHandleGroupMessage } from './message-filter'\n\nexport async function buildReplyForMessageEvent(\n event: ReceiveMessageEvent,\n deps: RelayBotDeps,\n): Promise<string | null> {\n if (isMessageFromBot(event, deps.botOpenId)) {\n return null\n }\n\n if (event.message.message_type !== 'text') {\n return t`Failed to parse message. Please send a text message.`\n }\n\n const text = parseTextContent(event.message.content)\n if (!text) {\n return t`Failed to parse message. Please send a text message.`\n }\n\n if (\n event.message.chat_type !== 'p2p' &&\n !shouldHandleGroupMessage(event.message.mentions, deps.botOpenId)\n ) {\n return null\n }\n\n const senderId = resolveSenderId(event.sender.sender_id)\n if (!senderId) {\n return t`Cannot identify sender. Please try again later.`\n }\n\n const normalizedText = stripMentionTags(text).trim()\n if (normalizedText.length === 0) {\n return t`Please send a text message.`\n }\n\n return deps.handleIncomingText({\n chatType: event.message.chat_type,\n chatId: event.message.chat_id,\n senderId,\n text: normalizedText,\n })\n}\n\nexport function stripMentionTags(text: string): string {\n return text.replace(/<at\\b[^>]*>.*?<\\/at>/g, '').trim()\n}\n\nfunction parseTextContent(content: string): string | null {\n try {\n const parsed: unknown = JSON.parse(content)\n if (!isPlainObject(parsed)) {\n return null\n }\n\n return typeof parsed.text === 'string' ? parsed.text : null\n } catch {\n return null\n }\n}\n","import { isPlainObject } from 'es-toolkit/predicate'\nimport type {\n RpcErrorObject,\n RpcErrorResponse,\n RpcIncomingLine,\n RpcRequestId,\n RpcServerRequest,\n RpcSuccessResponse,\n} from '../core/types'\n\nexport function parseRpcLine(line: string): RpcIncomingLine | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(line)\n } catch {\n return null\n }\n\n if (!isPlainObject(parsed)) {\n return null\n }\n\n if (typeof parsed.method === 'string') {\n if (isRpcRequestId(parsed.id)) {\n return {\n id: parsed.id,\n method: parsed.method,\n params: parsed.params,\n }\n }\n\n return {\n method: parsed.method,\n params: parsed.params,\n }\n }\n\n if (!isRpcRequestId(parsed.id)) {\n return null\n }\n\n if ('error' in parsed && isRpcErrorObject(parsed.error)) {\n return {\n id: parsed.id,\n error: parsed.error,\n }\n }\n\n if ('result' in parsed) {\n return {\n id: parsed.id,\n result: parsed.result,\n }\n }\n\n return null\n}\n\nexport function formatRpcError(error: RpcErrorObject): string {\n return `Codex RPC error (${error.code}): ${error.message}`\n}\n\nexport function isRpcRequestId(value: unknown): value is RpcRequestId {\n return typeof value === 'number' || typeof value === 'string'\n}\n\nexport function isRpcErrorObject(value: unknown): value is RpcErrorObject {\n if (!isPlainObject(value)) {\n return false\n }\n\n return typeof value.code === 'number' && typeof value.message === 'string'\n}\n\nexport function isRpcErrorResponse(\n value: RpcIncomingLine,\n): value is RpcErrorResponse {\n return 'error' in value\n}\n\nexport function isRpcSuccessResponse(\n value: RpcIncomingLine,\n): value is RpcSuccessResponse<unknown> {\n return 'result' in value\n}\n\nexport function isRpcServerRequest(\n value: RpcIncomingLine,\n): value is RpcServerRequest<unknown> {\n return 'method' in value && 'id' in value\n}\n\nexport function getServerRequestResult(method: string): unknown | null {\n if (method === 'item/commandExecution/requestApproval') {\n return {\n decision: 'accept',\n acceptSettings: {\n forSession: true,\n },\n }\n }\n\n if (method === 'item/fileChange/requestApproval') {\n return { decision: 'accept' }\n }\n\n if (method.endsWith('/requestApproval')) {\n return { decision: 'accept' }\n }\n\n if (method === 'execCommandApproval') {\n return { decision: 'allow' }\n }\n\n if (method === 'applyPatchApproval') {\n return { decision: 'allow' }\n }\n\n if (method.endsWith('Approval')) {\n return { decision: 'allow' }\n }\n\n if (method === 'item/tool/requestUserInput') {\n return { answers: {} }\n }\n\n if (method === 'item/tool/call') {\n return {\n success: false,\n contentItems: [\n {\n type: 'inputText',\n text: 'Dynamic tool calls are unavailable in relay-bot.',\n },\n ],\n }\n }\n\n return null\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport {\n formatRpcError,\n getServerRequestResult,\n isRpcErrorResponse,\n isRpcServerRequest,\n isRpcSuccessResponse,\n parseRpcLine,\n} from './rpc'\nimport type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface } from 'node:readline'\nimport type {\n RpcNotification,\n RpcRequestId,\n RpcServerRequest,\n} from '../core/types'\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (reason: Error) => void\n}\n\nexport class CodexAppServerClient {\n private readonly options: {\n cwd: string\n codexBin: string\n }\n\n private readonly child: ChildProcessWithoutNullStreams\n\n private readonly pending = new Map<RpcRequestId, PendingRequest>()\n\n private readonly stderrBuffer: string[] = []\n\n private readonly lineReader: Interface\n\n private nextId = 1\n\n private notificationHandler:\n | ((notification: RpcNotification<unknown>) => void)\n | null = null\n\n private exited = false\n\n constructor(options: { cwd: string; codexBin: string }) {\n this.options = options\n const commandArgs = ['app-server']\n\n this.child = spawn(this.options.codexBin, commandArgs, {\n cwd: this.options.cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n this.lineReader = createInterface({\n input: this.child.stdout,\n crlfDelay: Infinity,\n })\n\n this.lineReader.on('line', (line) => {\n this.handleStdoutLine(line)\n })\n\n this.child.stderr.on('data', (chunk) => {\n const text = String(chunk).trim()\n if (text.length > 0) {\n this.stderrBuffer.push(text)\n }\n })\n\n this.child.on('exit', (code, signal) => {\n this.exited = true\n const error = new Error(this.buildExitMessage(code, signal))\n for (const pending of this.pending.values()) {\n pending.reject(error)\n }\n this.pending.clear()\n })\n }\n\n setNotificationHandler(\n handler: (notification: RpcNotification<unknown>) => void,\n ): void {\n this.notificationHandler = handler\n }\n\n async request<T>(method: string, params: unknown): Promise<T> {\n if (this.exited) {\n throw new Error(this.buildExitMessage(null, null))\n }\n\n const requestId = this.nextId\n this.nextId += 1\n\n const responsePromise = new Promise<T>((resolve, reject) => {\n this.pending.set(requestId, {\n resolve: (value: unknown) => resolve(value as T),\n reject,\n })\n })\n\n const payload = JSON.stringify({\n jsonrpc: '2.0',\n id: requestId,\n method,\n params,\n })\n\n await new Promise<void>((resolve, reject) => {\n this.child.stdin.write(`${payload}\\n`, (error) => {\n if (error) {\n this.pending.delete(requestId)\n reject(error)\n return\n }\n resolve()\n })\n })\n\n return responsePromise\n }\n\n dispose(): void {\n this.lineReader.close()\n if (!this.child.killed) {\n this.child.kill('SIGTERM')\n }\n }\n\n private handleStdoutLine(line: string): void {\n const parsed = parseRpcLine(line)\n if (!parsed) {\n return\n }\n\n if ('method' in parsed) {\n if (isRpcServerRequest(parsed)) {\n void this.respondToServerRequest(parsed).catch((error) => {\n this.stderrBuffer.push(\n `failed to respond to server request \"${parsed.method}\": ${String(\n error,\n )}`,\n )\n })\n return\n }\n\n this.notificationHandler?.(parsed)\n return\n }\n\n const pending = this.pending.get(parsed.id)\n if (!pending) {\n return\n }\n\n this.pending.delete(parsed.id)\n if (isRpcErrorResponse(parsed)) {\n pending.reject(new Error(formatRpcError(parsed.error)))\n return\n }\n\n if (isRpcSuccessResponse(parsed)) {\n pending.resolve(parsed.result)\n }\n }\n\n private async respondToServerRequest(\n request: RpcServerRequest<unknown>,\n ): Promise<void> {\n const result = getServerRequestResult(request.method)\n if (result !== null) {\n await this.sendRpcResult(request.id, result)\n return\n }\n\n await this.sendRpcError(\n request.id,\n -32601,\n `Unsupported server request method: ${request.method}`,\n )\n }\n\n private async sendRpcResult(\n id: RpcRequestId,\n result: unknown,\n ): Promise<void> {\n await this.writeRpcPayload({\n jsonrpc: '2.0',\n id,\n result,\n })\n }\n\n private async sendRpcError(\n id: RpcRequestId,\n code: number,\n message: string,\n ): Promise<void> {\n await this.writeRpcPayload({\n jsonrpc: '2.0',\n id,\n error: {\n code,\n message,\n },\n })\n }\n\n private async writeRpcPayload(payload: {\n jsonrpc: '2.0'\n id: RpcRequestId\n result?: unknown\n error?: {\n code: number\n message: string\n }\n }): Promise<void> {\n if (this.exited) {\n return\n }\n\n const serialized = JSON.stringify(payload)\n await new Promise<void>((resolve, reject) => {\n this.child.stdin.write(`${serialized}\\n`, (error) => {\n if (error) {\n reject(error)\n return\n }\n resolve()\n })\n })\n }\n\n private buildExitMessage(\n code: number | null,\n signal: NodeJS.Signals | null,\n ): string {\n const suffix =\n this.stderrBuffer.length > 0\n ? `; stderr: ${this.stderrBuffer.at(-1)}`\n : ''\n return `Codex app-server exited (code=${code ?? 'null'}, signal=${\n signal ?? 'null'\n })${suffix}`\n }\n}\n","import { isPlainObject } from 'es-toolkit/predicate'\nimport type {\n BotSession,\n ChatMode,\n CollaborationModeListResponse,\n CollaborationModeMask,\n ThreadResult,\n} from '../core/types'\nimport type { CodexAppServerClient } from './app-server-client'\n\nexport interface OpenThreadResult {\n threadId: string\n cwd: string\n model: string\n}\n\nexport interface CollaborationModePayload {\n mode: ChatMode\n settings: {\n model: string\n reasoning_effort: string | null\n developer_instructions: string | null\n }\n}\n\nexport async function initializeClient(\n client: CodexAppServerClient,\n): Promise<void> {\n await client.request('initialize', {\n clientInfo: {\n name: 'relay-bot',\n title: 'Relay Bot',\n version: '0.0.0',\n },\n capabilities: {\n experimentalApi: true,\n },\n })\n}\n\nexport async function getCollaborationModes(\n client: CodexAppServerClient,\n): Promise<CollaborationModeMask[]> {\n const raw = await client.request('collaborationMode/list', {})\n if (!isCollaborationModeListResponse(raw)) {\n throw new Error('Invalid collaboration mode response from Codex')\n }\n\n return raw.data\n}\n\nexport async function openThread(\n client: CodexAppServerClient,\n session: BotSession | null,\n cwd: string,\n): Promise<OpenThreadResult> {\n if (!session) {\n return startThread(client, cwd)\n }\n\n if (session.cwd !== cwd) {\n return startThread(client, cwd)\n }\n\n try {\n const resumed = await resumeThread(client, session.threadId)\n if (resumed.cwd !== cwd) {\n return startThread(client, cwd)\n }\n\n return resumed\n } catch (error) {\n if (isThreadMissingError(error)) {\n return startThread(client, cwd)\n }\n throw error\n }\n}\n\nexport async function startThread(\n client: CodexAppServerClient,\n cwd: string,\n): Promise<OpenThreadResult> {\n const raw = await client.request('thread/start', {\n cwd,\n approvalPolicy: 'on-request',\n sandbox: 'workspace-write',\n experimentalRawEvents: false,\n })\n\n return parseThreadResult(raw)\n}\n\nexport function selectCollaborationModePayload(\n masks: CollaborationModeMask[],\n mode: ChatMode,\n model: string,\n): CollaborationModePayload {\n const selected = masks.find((mask) => {\n if (mask.mode === mode) {\n return true\n }\n\n return mask.name.toLowerCase() === mode\n })\n\n if (!selected) {\n throw new Error(`Collaboration mode \"${mode}\" is unavailable`)\n }\n\n return {\n mode,\n settings: {\n model,\n reasoning_effort: selected.reasoning_effort,\n developer_instructions: selected.developer_instructions,\n },\n }\n}\n\nasync function resumeThread(\n client: CodexAppServerClient,\n threadId: string,\n): Promise<OpenThreadResult> {\n const raw = await client.request('thread/resume', {\n threadId,\n })\n\n return parseThreadResult(raw)\n}\n\nfunction parseThreadResult(raw: unknown): OpenThreadResult {\n if (!isThreadResult(raw)) {\n throw new Error('Invalid thread response from Codex')\n }\n\n return {\n threadId: raw.thread.id,\n model: raw.model,\n cwd: raw.cwd,\n }\n}\n\nfunction isThreadMissingError(error: unknown): boolean {\n if (!(error instanceof Error)) {\n return false\n }\n\n return error.message.includes('thread not found')\n}\n\nfunction isCollaborationModeMask(\n value: unknown,\n): value is CollaborationModeMask {\n if (!isPlainObject(value)) {\n return false\n }\n\n const modeIsValid =\n value.mode === null || value.mode === 'default' || value.mode === 'plan'\n\n return (\n typeof value.name === 'string' &&\n modeIsValid &&\n (typeof value.model === 'string' || value.model === null) &&\n (typeof value.reasoning_effort === 'string' ||\n value.reasoning_effort === null) &&\n (typeof value.developer_instructions === 'string' ||\n value.developer_instructions === null)\n )\n}\n\nfunction isCollaborationModeListResponse(\n value: unknown,\n): value is CollaborationModeListResponse {\n if (!isPlainObject(value) || !Array.isArray(value.data)) {\n return false\n }\n\n return value.data.every(isCollaborationModeMask)\n}\n\nfunction isThreadResult(value: unknown): value is ThreadResult {\n if (!isPlainObject(value) || !isPlainObject(value.thread)) {\n return false\n }\n\n return typeof value.thread.id === 'string' && typeof value.model === 'string'\n}\n","import { isPlainObject } from 'es-toolkit/predicate'\nimport type {\n RpcItemCompletedParams,\n RpcNotification,\n RpcTaskCompleteParams,\n RpcTurnCompletedParams,\n TurnAccumulator,\n} from '../core/types'\n\nexport function createTurnAccumulator(): TurnAccumulator {\n return {\n turnCompleted: false,\n turnError: null,\n lastAgentMessageByItem: null,\n lastAgentMessageByTask: null,\n }\n}\n\nexport function applyTurnNotification(\n accumulator: TurnAccumulator,\n notification: RpcNotification<unknown>,\n): void {\n if (notification.method === 'error') {\n if (\n isPlainObject(notification.params) &&\n typeof notification.params.message === 'string'\n ) {\n accumulator.turnError = notification.params.message\n } else {\n accumulator.turnError = 'Codex returned an unknown error event'\n }\n accumulator.turnCompleted = true\n return\n }\n\n if (notification.method === 'item/completed') {\n const params = notification.params as RpcItemCompletedParams\n const item = params.item\n if (item?.type === 'agentMessage' && typeof item.text === 'string') {\n accumulator.lastAgentMessageByItem = item.text\n }\n return\n }\n\n if (notification.method === 'codex/event/task_complete') {\n const params = notification.params as RpcTaskCompleteParams\n const message = params.msg?.last_agent_message\n if (typeof message === 'string') {\n accumulator.lastAgentMessageByTask = message\n }\n return\n }\n\n if (notification.method === 'turn/completed') {\n const params = notification.params as RpcTurnCompletedParams\n accumulator.turnCompleted = true\n if (params.turn?.error?.message) {\n accumulator.turnError = params.turn.error.message\n return\n }\n\n if (params.turn?.status === 'failed') {\n accumulator.turnError = 'Codex turn failed'\n }\n }\n}\n\nexport function resolveTurnMessage(\n accumulator: TurnAccumulator,\n): string | null {\n return (\n accumulator.lastAgentMessageByTask ?? accumulator.lastAgentMessageByItem\n )\n}\n","import { CodexAppServerClient } from './app-server-client'\nimport {\n getCollaborationModes,\n initializeClient,\n openThread,\n selectCollaborationModePayload,\n startThread,\n} from './thread'\nimport {\n applyTurnNotification,\n createTurnAccumulator,\n resolveTurnMessage,\n} from './turn-state'\nimport type { BotSession, ChatMode, CodexTurnResult } from '../core/types'\n\nexport { formatRpcError, parseRpcLine } from './rpc'\nexport {\n applyTurnNotification,\n createTurnAccumulator,\n resolveTurnMessage,\n} from './turn-state'\n\nconst DEFAULT_CODEX_BIN = 'codex'\n\ninterface Deferred<T> {\n promise: Promise<T>\n resolve: (value: T | PromiseLike<T>) => void\n reject: (reason?: unknown) => void\n}\n\nexport interface RunCodexTurnInput {\n prompt: string\n mode: ChatMode\n session: BotSession | null\n cwd: string\n codexBin?: string\n timeoutMs?: number\n}\n\nexport interface CreateCodexThreadInput {\n mode: ChatMode\n cwd: string\n codexBin?: string\n timeoutMs?: number\n}\n\nexport async function createCodexThread(\n input: CreateCodexThreadInput,\n): Promise<BotSession> {\n const client = new CodexAppServerClient({\n cwd: input.cwd,\n codexBin: input.codexBin ?? DEFAULT_CODEX_BIN,\n })\n\n try {\n return await runWithOptionalTimeout(\n async () => {\n await initializeClient(client)\n const opened = await startThread(client, input.cwd)\n return {\n threadId: opened.threadId,\n model: opened.model,\n mode: input.mode,\n cwd: opened.cwd,\n }\n },\n input.timeoutMs,\n () => client.dispose(),\n )\n } finally {\n client.dispose()\n }\n}\n\nexport async function runCodexTurn(\n input: RunCodexTurnInput,\n): Promise<CodexTurnResult> {\n const client = new CodexAppServerClient({\n cwd: input.cwd,\n codexBin: input.codexBin ?? DEFAULT_CODEX_BIN,\n })\n\n const accumulator = createTurnAccumulator()\n const turnDone = createDeferred<void>()\n let turnDoneResolved = false\n\n client.setNotificationHandler((notification) => {\n applyTurnNotification(accumulator, notification)\n if (accumulator.turnCompleted && !turnDoneResolved) {\n turnDoneResolved = true\n turnDone.resolve()\n }\n })\n\n try {\n return await runWithOptionalTimeout(\n async () => {\n await initializeClient(client)\n const modeMasks = await getCollaborationModes(client)\n const opened = await openThread(client, input.session, input.cwd)\n const collaborationMode = selectCollaborationModePayload(\n modeMasks,\n input.mode,\n opened.model,\n )\n\n await client.request('turn/start', {\n threadId: opened.threadId,\n input: [\n {\n type: 'text',\n text: input.prompt,\n text_elements: [],\n },\n ],\n collaborationMode,\n })\n\n await turnDone.promise\n\n if (accumulator.turnError) {\n throw new Error(accumulator.turnError)\n }\n\n const message = resolveTurnMessage(accumulator)\n if (!message || message.trim().length === 0) {\n throw new Error('Codex did not return a message')\n }\n\n return {\n threadId: opened.threadId,\n model: opened.model,\n mode: input.mode,\n message,\n cwd: opened.cwd,\n }\n },\n input.timeoutMs,\n () => {\n if (!turnDoneResolved) {\n turnDoneResolved = true\n turnDone.reject(new Error('Codex execution timed out'))\n }\n client.dispose()\n },\n )\n } finally {\n client.dispose()\n }\n}\n\nasync function runWithOptionalTimeout<T>(\n run: () => Promise<T>,\n timeoutMs: number | undefined,\n onTimeout: () => void,\n): Promise<T> {\n if (typeof timeoutMs !== 'number' || timeoutMs <= 0) {\n return run()\n }\n\n return withTimeout(run, timeoutMs, onTimeout)\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T | PromiseLike<T>) => void\n let reject!: (reason?: unknown) => void\n const promise = new Promise<T>((innerResolve, innerReject) => {\n resolve = innerResolve\n reject = innerReject\n })\n\n return { promise, resolve, reject }\n}\n\nasync function withTimeout<T>(\n run: () => Promise<T>,\n timeoutMs: number,\n onTimeout: () => void,\n): Promise<T> {\n let timeoutHandle: NodeJS.Timeout | undefined\n const timeoutPromise = new Promise<T>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n onTimeout()\n reject(new Error(`Codex request timed out after ${timeoutMs}ms`))\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([run(), timeoutPromise])\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle)\n }\n }\n}\n","import process from 'node:process'\nimport type { OpenProjectsResult } from '../core/types'\n\nexport async function listOpenProjects(): Promise<OpenProjectsResult> {\n return {\n roots: [process.cwd()],\n }\n}\n","msgid \"\"\nmsgstr \"\"\n\"POT-Creation-Date: 2026-02-20 17:04+0800\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: @lingui/cli\\n\"\n\"Language: en\\n\"\n\n#. placeholder {0}: index + 1\n#: src/bot/handler.ts:83\nmsgid \"{0}. {root}\"\nmsgstr \"{0}. {root}\"\n\n#: src/bot/commands.ts:14\nmsgid \"/help - Show help\"\nmsgstr \"/help - Show help\"\n\n#: src/bot/commands.ts:45\nmsgid \"\"\n\"/help does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/help does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:16\nmsgid \"/mode <default|plan> - Switch current session mode\"\nmsgstr \"/mode <default|plan> - Switch current session mode\"\n\n#: src/bot/commands.ts:83\nmsgid \"\"\n\"/mode requires one argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/mode requires one argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:15\nmsgid \"/new [default|plan] - Create a new session\"\nmsgstr \"/new [default|plan] - Create a new session\"\n\n#: src/bot/commands.ts:57\nmsgid \"\"\n\"/new accepts at most one optional argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/new accepts at most one optional argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:18\nmsgid \"/projects - Show current working directories\"\nmsgstr \"/projects - Show current working directories\"\n\n#: src/bot/commands.ts:114\nmsgid \"\"\n\"/projects does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/projects does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:19\nmsgid \"/reset - Clear current session\"\nmsgstr \"/reset - Clear current session\"\n\n#: src/bot/commands.ts:125\nmsgid \"\"\n\"/reset does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/reset does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:17\nmsgid \"/status - Show current session status\"\nmsgstr \"/status - Show current session status\"\n\n#: src/bot/commands.ts:103\nmsgid \"\"\n\"/status does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/status does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:13\nmsgid \"Available commands:\"\nmsgstr \"Available commands:\"\n\n#: src/bot/relay.ts:60\nmsgid \"Cannot identify sender. Please try again later.\"\nmsgstr \"Cannot identify sender. Please try again later.\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:155\nmsgid \"Codex execution failed: {0}\"\nmsgstr \"Codex execution failed: {0}\"\n\n#: src/bot/handler.ts:158\nmsgid \"Codex execution failed. Please try again later.\"\nmsgstr \"Codex execution failed. Please try again later.\"\n\n#: src/bot/commands.ts:30\nmsgid \"\"\n\"Command cannot be empty.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"Command cannot be empty.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/handler.ts:113\nmsgid \"Created a new session.\"\nmsgstr \"Created a new session.\"\n\n#: src/bot/handler.ts:92\nmsgid \"Current session has been cleared.\"\nmsgstr \"Current session has been cleared.\"\n\n#: src/bot/handler.ts:68\nmsgid \"Current session status:\"\nmsgstr \"Current session status:\"\n\n#: src/bot/handler.ts:84\nmsgid \"Current working directories:\"\nmsgstr \"Current working directories:\"\n\n#: src/index.ts:21\nmsgid \"Currently busy. Please try again later.\"\nmsgstr \"Currently busy. Please try again later.\"\n\n#. placeholder {0}: created.cwd\n#: src/bot/handler.ts:115\nmsgid \"cwd: {0}\"\nmsgstr \"cwd: {0}\"\n\n#: src/bot/relay.ts:43\n#: src/bot/relay.ts:48\nmsgid \"Failed to parse message. Please send a text message.\"\nmsgstr \"Failed to parse message. Please send a text message.\"\n\n#: src/index.ts:68\nmsgid \"Failed to process message. Please try again later.\"\nmsgstr \"Failed to process message. Please try again later.\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:163\nmsgid \"Failed to read open projects: {0}\"\nmsgstr \"Failed to read open projects: {0}\"\n\n#: src/bot/handler.ts:166\nmsgid \"Failed to read open projects. Please try again later.\"\nmsgstr \"Failed to read open projects. Please try again later.\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:121\nmsgid \"Failed to read relay config at {configPath}: {0}\"\nmsgstr \"Failed to read relay config at {configPath}: {0}\"\n\n#: src/core/startup.ts:17\nmsgid \"Failed to start relay: {message}\"\nmsgstr \"Failed to start relay: {message}\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:130\nmsgid \"Invalid JSON in relay config at {configPath}: {0}\"\nmsgstr \"Invalid JSON in relay config at {configPath}: {0}\"\n\n#: src/bot/commands.ts:71\n#: src/bot/commands.ts:92\nmsgid \"\"\n\"Invalid mode \\\"{modeToken}\\\", only default or plan are supported.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"Invalid mode \\\"{modeToken}\\\", only default or plan are supported.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/core/config.ts:147\nmsgid \"Invalid relay config at {configPath}: env must be a JSON object.\"\nmsgstr \"Invalid relay config at {configPath}: env must be a JSON object.\"\n\n#: src/core/config.ts:136\nmsgid \"Invalid relay config at {configPath}: root must be a JSON object.\"\nmsgstr \"Invalid relay config at {configPath}: root must be a JSON object.\"\n\n#: src/core/config.ts:158\nmsgid \"Invalid relay config: {field} is required and must be a non-empty string.\"\nmsgstr \"Invalid relay config: {field} is required and must be a non-empty string.\"\n\n#: src/core/config.ts:172\nmsgid \"Invalid relay config: {field} must be a string.\"\nmsgstr \"Invalid relay config: {field} must be a string.\"\n\n#: src/core/config.ts:194\n#: src/core/config.ts:205\n#: src/core/config.ts:213\nmsgid \"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"\nmsgstr \"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"\n\n#. placeholder {0}: formatInvalidLocale(value)\n#: src/core/config.ts:226\nmsgid \"Invalid relay config: LOCALE \\\"{0}\\\" is not supported. Falling back to en.\"\nmsgstr \"Invalid relay config: LOCALE \\\"{0}\\\" is not supported. Falling back to en.\"\n\n#: src/core/config.ts:241\nmsgid \"Invalid relay config: LOCALE \\\"{normalized}\\\" is not supported. Falling back to en.\"\nmsgstr \"Invalid relay config: LOCALE \\\"{normalized}\\\" is not supported. Falling back to en.\"\n\n#. placeholder {0}: created.mode\n#. placeholder {0}: currentSession.mode\n#: src/bot/handler.ts:71\n#: src/bot/handler.ts:116\nmsgid \"mode: {0}\"\nmsgstr \"mode: {0}\"\n\n#. placeholder {0}: created.model\n#. placeholder {0}: currentSession.model\n#: src/bot/handler.ts:72\n#: src/bot/handler.ts:117\nmsgid \"model: {0}\"\nmsgstr \"model: {0}\"\n\n#: src/bot/handler.ts:65\n#: src/bot/handler.ts:225\nmsgid \"New Session\"\nmsgstr \"New Session\"\n\n#: src/bot/handler.ts:97\nmsgid \"No active session. Send a normal message or use /new to create one first.\"\nmsgstr \"No active session. Send a normal message or use /new to create one first.\"\n\n#: src/bot/handler.ts:62\nmsgid \"No active session. Send a normal message or use /new to create one.\"\nmsgstr \"No active session. Send a normal message or use /new to create one.\"\n\n#: src/bot/handler.ts:80\nmsgid \"No working directories are currently open.\"\nmsgstr \"No working directories are currently open.\"\n\n#: src/bot/relay.ts:65\nmsgid \"Please send a text message.\"\nmsgstr \"Please send a text message.\"\n\n#: src/core/config.ts:72\nmsgid \"Relay config is missing. Template created at {configPath}. Please edit this file and restart.\"\nmsgstr \"Relay config is missing. Template created at {configPath}. Please edit this file and restart.\"\n\n#. placeholder {0}: parsed.mode\n#: src/bot/handler.ts:105\nmsgid \"Switched to {0} mode.\"\nmsgstr \"Switched to {0} mode.\"\n\n#. placeholder {0}: created.threadId\n#. placeholder {0}: currentSession.threadId\n#: src/bot/handler.ts:69\n#: src/bot/handler.ts:114\nmsgid \"thread: {0}\"\nmsgstr \"thread: {0}\"\n\n#: src/bot/handler.ts:70\nmsgid \"title: {title}\"\nmsgstr \"title: {title}\"\n\n#. placeholder {0}: command ?? normalized\n#: src/bot/commands.ts:134\nmsgid \"\"\n\"Unknown command \\\"{0}\\\".\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"Unknown command \\\"{0}\\\".\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#. placeholder {0}: normalizePrompt(prompt)\n#: src/bot/handler.ts:263\nmsgid \"User message: {0}\"\nmsgstr \"User message: {0}\"\n\n#: src/bot/handler.ts:248\nmsgid \"\"\n\"You are a session title generator.\\n\"\n\"Generate a short Chinese title based on the user message.\\n\"\n\"Strict requirements:\\n\"\n\"1. Output title text only, with no explanation.\\n\"\n\"2. Output a single line with no line breaks.\\n\"\n\"3. Do not use quotes or title marks.\\n\"\n\"4. Keep the title within 24 characters.\"\nmsgstr \"\"\n\"You are a session title generator.\\n\"\n\"Generate a short Chinese title based on the user message.\\n\"\n\"Strict requirements:\\n\"\n\"1. Output title text only, with no explanation.\\n\"\n\"2. Output a single line with no line breaks.\\n\"\n\"3. Do not use quotes or title marks.\\n\"\n\"4. Keep the title within 24 characters.\"\n\n#: src/bot/handler.ts:255\nmsgid \"\"\n\"You are a session title generator.\\n\"\n\"Generate a short English title based on the user message.\\n\"\n\"Strict requirements:\\n\"\n\"1. Output title text only, with no explanation.\\n\"\n\"2. Output a single line with no line breaks.\\n\"\n\"3. Do not use quotes.\\n\"\n\"4. Keep the title within 24 characters.\"\nmsgstr \"\"\n\"You are a session title generator.\\n\"\n\"Generate a short English title based on the user message.\\n\"\n\"Strict requirements:\\n\"\n\"1. Output title text only, with no explanation.\\n\"\n\"2. Output a single line with no line breaks.\\n\"\n\"3. Do not use quotes.\\n\"\n\"4. Keep the title within 24 characters.\"\n","msgid \"\"\nmsgstr \"\"\n\"POT-Creation-Date: 2026-02-20 17:04+0800\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: @lingui/cli\\n\"\n\"Language: zh\\n\"\n\n#. placeholder {0}: index + 1\n#: src/bot/handler.ts:83\nmsgid \"{0}. {root}\"\nmsgstr \"{0}. {root}\"\n\n#: src/bot/commands.ts:14\nmsgid \"/help - Show help\"\nmsgstr \"/help - 显示帮助\"\n\n#: src/bot/commands.ts:45\nmsgid \"\"\n\"/help does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/help 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:16\nmsgid \"/mode <default|plan> - Switch current session mode\"\nmsgstr \"/mode <default|plan> - 切换当前会话模式\"\n\n#: src/bot/commands.ts:83\nmsgid \"\"\n\"/mode requires one argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/mode 需要一个参数:default 或 plan。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:15\nmsgid \"/new [default|plan] - Create a new session\"\nmsgstr \"/new [default|plan] - 创建新会话\"\n\n#: src/bot/commands.ts:57\nmsgid \"\"\n\"/new accepts at most one optional argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/new 最多接受一个可选参数:default 或 plan。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:18\nmsgid \"/projects - Show current working directories\"\nmsgstr \"/projects - 显示当前工作目录\"\n\n#: src/bot/commands.ts:114\nmsgid \"\"\n\"/projects does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/projects 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:19\nmsgid \"/reset - Clear current session\"\nmsgstr \"/reset - 清空当前会话\"\n\n#: src/bot/commands.ts:125\nmsgid \"\"\n\"/reset does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/reset 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:17\nmsgid \"/status - Show current session status\"\nmsgstr \"/status - 显示当前会话状态\"\n\n#: src/bot/commands.ts:103\nmsgid \"\"\n\"/status does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/status 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:13\nmsgid \"Available commands:\"\nmsgstr \"可用命令:\"\n\n#: src/bot/relay.ts:60\nmsgid \"Cannot identify sender. Please try again later.\"\nmsgstr \"无法识别发送者,请稍后重试。\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:155\nmsgid \"Codex execution failed: {0}\"\nmsgstr \"Codex 执行失败:{0}\"\n\n#: src/bot/handler.ts:158\nmsgid \"Codex execution failed. Please try again later.\"\nmsgstr \"Codex 执行失败,请稍后重试。\"\n\n#: src/bot/commands.ts:30\nmsgid \"\"\n\"Command cannot be empty.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"命令不能为空。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/handler.ts:113\nmsgid \"Created a new session.\"\nmsgstr \"已创建新会话。\"\n\n#: src/bot/handler.ts:92\nmsgid \"Current session has been cleared.\"\nmsgstr \"当前会话已清空。\"\n\n#: src/bot/handler.ts:68\nmsgid \"Current session status:\"\nmsgstr \"当前会话状态:\"\n\n#: src/bot/handler.ts:84\nmsgid \"Current working directories:\"\nmsgstr \"当前工作目录:\"\n\n#: src/index.ts:21\nmsgid \"Currently busy. Please try again later.\"\nmsgstr \"当前忙碌,请稍后重试。\"\n\n#. placeholder {0}: created.cwd\n#: src/bot/handler.ts:115\nmsgid \"cwd: {0}\"\nmsgstr \"cwd: {0}\"\n\n#: src/bot/relay.ts:43\n#: src/bot/relay.ts:48\nmsgid \"Failed to parse message. Please send a text message.\"\nmsgstr \"解析消息失败,请发送文本消息。\"\n\n#: src/index.ts:68\nmsgid \"Failed to process message. Please try again later.\"\nmsgstr \"处理消息失败,请稍后重试。\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:163\nmsgid \"Failed to read open projects: {0}\"\nmsgstr \"读取已打开项目失败:{0}\"\n\n#: src/bot/handler.ts:166\nmsgid \"Failed to read open projects. Please try again later.\"\nmsgstr \"读取已打开项目失败,请稍后重试。\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:121\nmsgid \"Failed to read relay config at {configPath}: {0}\"\nmsgstr \"读取中继配置失败 {configPath}:{0}\"\n\n#: src/core/startup.ts:17\nmsgid \"Failed to start relay: {message}\"\nmsgstr \"启动中继失败:{message}\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:130\nmsgid \"Invalid JSON in relay config at {configPath}: {0}\"\nmsgstr \"中继配置 {configPath} 中的 JSON 无效:{0}\"\n\n#: src/bot/commands.ts:71\n#: src/bot/commands.ts:92\nmsgid \"\"\n\"Invalid mode \\\"{modeToken}\\\", only default or plan are supported.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"无效的模式 \\\"{modeToken}\\\",仅支持 default 或 plan。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/core/config.ts:147\nmsgid \"Invalid relay config at {configPath}: env must be a JSON object.\"\nmsgstr \"中继配置 {configPath} 无效:env 必须是 JSON 对象。\"\n\n#: src/core/config.ts:136\nmsgid \"Invalid relay config at {configPath}: root must be a JSON object.\"\nmsgstr \"中继配置 {configPath} 无效:root 必须是 JSON 对象。\"\n\n#: src/core/config.ts:158\nmsgid \"Invalid relay config: {field} is required and must be a non-empty string.\"\nmsgstr \"中继配置无效:{field} 为必填项且必须为非空字符串。\"\n\n#: src/core/config.ts:172\nmsgid \"Invalid relay config: {field} must be a string.\"\nmsgstr \"中继配置无效:{field} 必须是字符串。\"\n\n#: src/core/config.ts:194\n#: src/core/config.ts:205\n#: src/core/config.ts:213\nmsgid \"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"\nmsgstr \"中继配置无效:CODEX_TIMEOUT_MS 必须是正整数。\"\n\n#. placeholder {0}: formatInvalidLocale(value)\n#: src/core/config.ts:226\nmsgid \"Invalid relay config: LOCALE \\\"{0}\\\" is not supported. Falling back to en.\"\nmsgstr \"中继配置无效:不支持的 LOCALE \\\"{0}\\\",已回退为 en。\"\n\n#: src/core/config.ts:241\nmsgid \"Invalid relay config: LOCALE \\\"{normalized}\\\" is not supported. Falling back to en.\"\nmsgstr \"中继配置无效:不支持的 LOCALE \\\"{normalized}\\\",已回退为 en。\"\n\n#. placeholder {0}: created.mode\n#. placeholder {0}: currentSession.mode\n#: src/bot/handler.ts:71\n#: src/bot/handler.ts:116\nmsgid \"mode: {0}\"\nmsgstr \"mode: {0}\"\n\n#. placeholder {0}: created.model\n#. placeholder {0}: currentSession.model\n#: src/bot/handler.ts:72\n#: src/bot/handler.ts:117\nmsgid \"model: {0}\"\nmsgstr \"model: {0}\"\n\n#: src/bot/handler.ts:65\n#: src/bot/handler.ts:225\nmsgid \"New Session\"\nmsgstr \"新会话\"\n\n#: src/bot/handler.ts:97\nmsgid \"No active session. Send a normal message or use /new to create one first.\"\nmsgstr \"没有活跃会话。请先发送普通消息或使用 /new 创建会话。\"\n\n#: src/bot/handler.ts:62\nmsgid \"No active session. Send a normal message or use /new to create one.\"\nmsgstr \"没有活跃会话。请发送普通消息或使用 /new 创建会话。\"\n\n#: src/bot/handler.ts:80\nmsgid \"No working directories are currently open.\"\nmsgstr \"当前没有打开的工作目录。\"\n\n#: src/bot/relay.ts:65\nmsgid \"Please send a text message.\"\nmsgstr \"请发送文本消息。\"\n\n#: src/core/config.ts:72\nmsgid \"Relay config is missing. Template created at {configPath}. Please edit this file and restart.\"\nmsgstr \"中继配置缺失,已在 {configPath} 创建模板。请编辑该文件后重启。\"\n\n#. placeholder {0}: parsed.mode\n#: src/bot/handler.ts:105\nmsgid \"Switched to {0} mode.\"\nmsgstr \"已切换到 {0} 模式。\"\n\n#. placeholder {0}: created.threadId\n#. placeholder {0}: currentSession.threadId\n#: src/bot/handler.ts:69\n#: src/bot/handler.ts:114\nmsgid \"thread: {0}\"\nmsgstr \"thread: {0}\"\n\n#: src/bot/handler.ts:70\nmsgid \"title: {title}\"\nmsgstr \"title: {title}\"\n\n#. placeholder {0}: command ?? normalized\n#: src/bot/commands.ts:134\nmsgid \"\"\n\"Unknown command \\\"{0}\\\".\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"未知命令 \\\"{0}\\\"。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#. placeholder {0}: normalizePrompt(prompt)\n#: src/bot/handler.ts:263\nmsgid \"User message: {0}\"\nmsgstr \"用户消息:{0}\"\n\n#: src/bot/handler.ts:248\nmsgid \"\"\n\"You are a session title generator.\\n\"\n\"Generate a short Chinese title based on the user message.\\n\"\n\"Strict requirements:\\n\"\n\"1. Output title text only, with no explanation.\\n\"\n\"2. Output a single line with no line breaks.\\n\"\n\"3. Do not use quotes or title marks.\\n\"\n\"4. Keep the title within 24 characters.\"\nmsgstr \"\"\n\"你是会话标题生成器。\\n\"\n\"根据用户消息生成简短中文标题。\\n\"\n\"严格要求:\\n\"\n\"1. 仅输出标题文字,不要解释。\\n\"\n\"2. 单行输出,不要换行。\\n\"\n\"3. 不要使用引号或书名号。\\n\"\n\"4. 标题不超过 24 个字符。\"\n\n#: src/bot/handler.ts:255\nmsgid \"\"\n\"You are a session title generator.\\n\"\n\"Generate a short English title based on the user message.\\n\"\n\"Strict requirements:\\n\"\n\"1. Output title text only, with no explanation.\\n\"\n\"2. Output a single line with no line breaks.\\n\"\n\"3. Do not use quotes.\\n\"\n\"4. Keep the title within 24 characters.\"\nmsgstr \"\"\n\"你是会话标题生成器。\\n\"\n\"根据用户消息生成简短英文标题。\\n\"\n\"严格要求:\\n\"\n\"1. 仅输出标题文字,不要解释。\\n\"\n\"2. 单行输出,不要换行。\\n\"\n\"3. 不要使用引号。\\n\"\n\"4. 标题不超过 24 个字符。\"\n","import { i18n } from '@lingui/core'\nimport { messages as enMessages } from '../locales/en/messages.po'\nimport { messages as zhMessages } from '../locales/zh/messages.po'\nimport type { Messages } from '@lingui/core'\n\nexport type AppLocale = 'en' | 'zh'\n\nconst DEFAULT_LOCALE: AppLocale = detectDefaultLocale()\n\nconst CATALOGS: Record<AppLocale, Messages> = {\n en: enMessages as Messages,\n zh: zhMessages as Messages,\n}\n\nlet activeLocale: AppLocale | null = null\n\n// Activate a default locale eagerly so top-level `t` calls in imported modules\n// never run before Lingui has an active locale.\ninitializeI18n(DEFAULT_LOCALE)\n\nexport function initializeI18n(locale?: string): AppLocale {\n const resolved = resolveLocale(locale)\n i18n.loadAndActivate({\n locale: resolved,\n messages: CATALOGS[resolved],\n })\n activeLocale = resolved\n return resolved\n}\n\nexport function getCurrentLocale(): AppLocale {\n ensureI18nInitialized()\n return activeLocale ?? DEFAULT_LOCALE\n}\n\nexport function isSupportedLocale(locale: string): locale is AppLocale {\n return locale === 'en' || locale === 'zh'\n}\n\nexport function getDefaultLocale(): AppLocale {\n return DEFAULT_LOCALE\n}\n\nfunction resolveLocale(locale?: string): AppLocale {\n if (!locale) {\n return DEFAULT_LOCALE\n }\n\n const mappedLocale = mapToAppLocale(locale)\n if (mappedLocale) {\n return mappedLocale\n }\n\n return DEFAULT_LOCALE\n}\n\nfunction ensureI18nInitialized(): void {\n if (!activeLocale) {\n initializeI18n(DEFAULT_LOCALE)\n }\n}\n\nfunction detectDefaultLocale(): AppLocale {\n const systemLocale = readSystemLocale()\n if (!systemLocale) {\n return 'en'\n }\n\n return mapToAppLocale(systemLocale) ?? 'en'\n}\n\nfunction readSystemLocale(): string | undefined {\n const locale = Intl.DateTimeFormat().resolvedOptions().locale\n if (typeof locale !== 'string') {\n return undefined\n }\n\n const normalized = locale.trim()\n if (normalized.length === 0) {\n return undefined\n }\n\n return normalized\n}\n\nfunction mapToAppLocale(locale: string): AppLocale | null {\n const normalized = locale.trim().toLowerCase().replaceAll('_', '-')\n\n if (normalized === 'zh' || normalized.startsWith('zh-')) {\n return 'zh'\n }\n\n if (normalized === 'en' || normalized.startsWith('en-')) {\n return 'en'\n }\n\n return null\n}\n","import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { t } from '@lingui/core/macro'\nimport {\n getDefaultLocale,\n initializeI18n,\n isSupportedLocale,\n} from '../i18n/runtime'\nimport type { AppLocale } from '../i18n/runtime'\n\nconst DEFAULT_CODEX_BIN = 'codex'\n\nconst TEMPLATE_ENV_CONFIG: Required<RelayConfigEnv> = {\n BASE_DOMAIN: 'https://open.feishu.cn',\n APP_ID: 'your_app_id',\n APP_SECRET: 'your_app_secret',\n BOT_OPEN_ID: 'ou_xxx',\n CODEX_BIN: DEFAULT_CODEX_BIN,\n CODEX_TIMEOUT_MS: null,\n}\n\nconst TEMPLATE_CONFIG: {\n locale?: AppLocale\n env: Required<RelayConfigEnv>\n} = {\n env: TEMPLATE_ENV_CONFIG,\n}\n\nexport interface RelayConfigEnv {\n BASE_DOMAIN?: string\n APP_ID?: string\n APP_SECRET?: string\n BOT_OPEN_ID?: string\n CODEX_BIN?: string\n CODEX_TIMEOUT_MS?: number | string | null\n}\n\ninterface RelayConfigFile extends RelayConfigEnv {\n locale?: string\n env?: RelayConfigEnv\n}\n\ninterface ParsedRelayConfig {\n env: RelayConfigEnv\n localeValue: unknown\n}\n\nexport interface RelayConfig {\n baseConfig: {\n appId: string\n appSecret: string\n domain: string\n }\n homeDir: string\n botOpenId?: string\n codexBin: string\n codexTimeoutMs?: number\n workspaceCwd: string\n locale: AppLocale\n}\n\nexport interface LoadRelayConfigOptions {\n homeDir?: string\n workspaceCwd?: string\n}\n\nexport function loadRelayConfig(\n options: LoadRelayConfigOptions = {},\n): RelayConfig {\n const homeDir = options.homeDir ?? os.homedir()\n const workspaceCwd = options.workspaceCwd ?? process.cwd()\n const configDir = path.join(homeDir, '.relay')\n const configPath = path.join(configDir, 'config.json')\n\n if (!fs.existsSync(configPath)) {\n ensureConfigTemplate(configDir, configPath)\n throw new Error(\n t`Relay config is missing. Template created at ${configPath}. Please edit this file and restart.`,\n )\n }\n\n const parsed = parseConfigFile(configPath)\n const locale = readLocale(parsed.localeValue)\n initializeI18n(locale)\n\n const domain = readRequiredString(parsed.env.BASE_DOMAIN, 'BASE_DOMAIN')\n const appId = readRequiredString(parsed.env.APP_ID, 'APP_ID')\n const appSecret = readRequiredString(parsed.env.APP_SECRET, 'APP_SECRET')\n\n return {\n baseConfig: {\n appId,\n appSecret,\n domain,\n },\n homeDir,\n botOpenId: readOptionalString(parsed.env.BOT_OPEN_ID, 'BOT_OPEN_ID'),\n codexBin:\n readOptionalString(parsed.env.CODEX_BIN, 'CODEX_BIN') ??\n DEFAULT_CODEX_BIN,\n codexTimeoutMs: readTimeoutMs(parsed.env.CODEX_TIMEOUT_MS),\n workspaceCwd,\n locale,\n }\n}\n\nfunction ensureConfigTemplate(configDir: string, configPath: string): void {\n fs.mkdirSync(configDir, { recursive: true })\n if (fs.existsSync(configPath)) {\n return\n }\n\n fs.writeFileSync(\n configPath,\n `${JSON.stringify(TEMPLATE_CONFIG, null, 2)}\\n`,\n {\n encoding: 'utf-8',\n flag: 'wx',\n },\n )\n}\n\nfunction parseConfigFile(configPath: string): ParsedRelayConfig {\n let raw: string\n try {\n raw = fs.readFileSync(configPath, 'utf-8')\n } catch (error) {\n throw new Error(\n t`Failed to read relay config at ${configPath}: ${formatError(error)}`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n throw new Error(\n t`Invalid JSON in relay config at ${configPath}: ${formatError(error)}`,\n )\n }\n\n if (!isObject(parsed)) {\n throw new Error(\n t`Invalid relay config at ${configPath}: root must be a JSON object.`,\n )\n }\n\n const configObject = parsed as RelayConfigFile\n if (configObject.env === undefined) {\n return {\n env: configObject,\n localeValue: configObject.locale,\n }\n }\n\n if (!isObject(configObject.env)) {\n throw new Error(\n t`Invalid relay config at ${configPath}: env must be a JSON object.`,\n )\n }\n\n return {\n env: configObject.env,\n localeValue: configObject.locale,\n }\n}\n\nfunction readRequiredString(value: unknown, field: string): string {\n const normalized = readOptionalString(value, field)\n if (!normalized) {\n throw new Error(\n t`Invalid relay config: ${field} is required and must be a non-empty string.`,\n )\n }\n\n return normalized\n}\n\nfunction readOptionalString(value: unknown, field: string): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n if (typeof value !== 'string') {\n throw new TypeError(t`Invalid relay config: ${field} must be a string.`)\n }\n\n const normalized = value.trim()\n if (normalized.length === 0) {\n return undefined\n }\n\n return normalized\n}\n\nfunction readTimeoutMs(value: unknown): number | undefined {\n if (value === undefined || value === null) {\n return undefined\n }\n\n if (typeof value === 'number') {\n if (Number.isInteger(value) && value > 0) {\n return value\n }\n throw new Error(\n t`Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.`,\n )\n }\n\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length === 0) {\n return undefined\n }\n if (!/^[1-9]\\d*$/.test(trimmed)) {\n throw new Error(\n t`Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.`,\n )\n }\n\n return Number.parseInt(trimmed, 10)\n }\n\n throw new Error(\n t`Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.`,\n )\n}\n\nfunction readLocale(value: unknown): AppLocale {\n // When locale is missing in config, use the system-detected default locale.\n const systemLocale = getDefaultLocale()\n\n if (value === undefined || value === null) {\n return systemLocale\n }\n\n if (typeof value !== 'string') {\n console.warn(\n t`Invalid relay config: locale \"${formatInvalidLocale(value)}\" is not supported. Falling back to ${systemLocale}.`,\n )\n return systemLocale\n }\n\n const normalized = value.trim()\n if (normalized.length === 0) {\n return systemLocale\n }\n\n const mapped = mapLocaleToAppLocale(normalized)\n if (mapped) {\n return mapped\n }\n\n console.warn(\n t`Invalid relay config: locale \"${normalized}\" is not supported. Falling back to ${systemLocale}.`,\n )\n\n return systemLocale\n}\n\nfunction formatInvalidLocale(value: unknown): string {\n if (typeof value === 'string') {\n return value\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction mapLocaleToAppLocale(value: string): AppLocale | null {\n if (isSupportedLocale(value)) {\n return value\n }\n\n const normalized = value.toLowerCase().replaceAll('_', '-')\n if (normalized === 'zh' || normalized.startsWith('zh-')) {\n return 'zh'\n }\n\n if (normalized === 'en' || normalized.startsWith('en-')) {\n return 'en'\n }\n\n return null\n}\n\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return error.message\n }\n\n return String(error)\n}\n","import process from 'node:process'\nimport { t } from '@lingui/core/macro'\nimport { loadRelayConfig } from './config'\nimport type { RelayConfig } from './config'\n\nexport function loadConfigOrExit(): RelayConfig {\n try {\n return loadRelayConfig()\n } catch (error) {\n console.error(formatStartupError(error))\n process.exit(1)\n }\n}\n\nfunction formatStartupError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error)\n return t`Failed to start relay: ${message}`\n}\n","import { resolveSenderId } from '../bot/message-filter'\nimport { getSession, getSessionKey } from '../session/store'\nimport type * as Lark from '@larksuiteoapi/node-sdk'\nimport type { ReceiveMessageEvent } from '../bot/relay'\n\nconst FALLBACK_REPLY_TAG = 'no-thread'\n\nexport interface SendReplyOptions {\n includeThreadTag?: boolean\n}\n\nexport interface FeishuReceiveMessageEvent extends ReceiveMessageEvent {\n event_id?: string\n message: ReceiveMessageEvent['message'] & {\n message_id: string\n }\n}\n\nexport async function sendReply(\n larkClient: Lark.Client,\n data: FeishuReceiveMessageEvent,\n text: string,\n options?: SendReplyOptions,\n): Promise<void> {\n const content = JSON.stringify({\n text: formatReplyTextWithThreadId(data, text, options),\n })\n\n if (data.message.chat_type === 'p2p') {\n await larkClient.im.v1.message.create({\n params: {\n receive_id_type: 'chat_id',\n },\n data: {\n receive_id: data.message.chat_id,\n msg_type: 'text',\n content,\n },\n })\n return\n }\n\n await larkClient.im.v1.message.reply({\n path: {\n message_id: data.message.message_id,\n },\n data: {\n msg_type: 'text',\n content,\n },\n })\n}\n\nfunction formatReplyTextWithThreadId(\n data: FeishuReceiveMessageEvent,\n text: string,\n options?: SendReplyOptions,\n): string {\n if (!options?.includeThreadTag) {\n return text.trim()\n }\n\n const replyTag = resolveReplyTag(data)\n const normalizedText = text.trim()\n if (normalizedText.length === 0) {\n return `${replyTag}\\n`\n }\n\n return `${replyTag}\\n\\n${normalizedText}`\n}\n\nfunction resolveReplyTag(data: FeishuReceiveMessageEvent): string {\n const senderId = resolveSenderId(data.sender.sender_id)\n if (!senderId) {\n return FALLBACK_REPLY_TAG\n }\n\n const sessionKey = getSessionKey({\n chatType: data.message.chat_type,\n chatId: data.message.chat_id,\n userId: senderId,\n })\n const session = getSession(sessionKey)\n if (!session || session.threadId.trim().length === 0) {\n return FALLBACK_REPLY_TAG\n }\n\n return session.threadId\n}\n","import process from 'node:process'\nimport * as Lark from '@larksuiteoapi/node-sdk'\nimport { t } from '@lingui/core/macro'\nimport { isPlainObject } from 'es-toolkit/predicate'\nimport { parseCommand } from './bot/commands'\nimport { handleIncomingText } from './bot/handler'\nimport { shouldProcessMessage } from './bot/message-filter'\nimport { buildReplyForMessageEvent, stripMentionTags } from './bot/relay'\nimport { createCodexThread, runCodexTurn } from './codex/app-server'\nimport { listOpenProjects } from './codex/state'\nimport { loadConfigOrExit } from './core/startup'\nimport { sendReply } from './feishu/reply'\nimport { initializeI18n } from './i18n/runtime'\nimport {\n clearSession,\n getSession,\n initializeSessionStore,\n setSession,\n withSessionLock,\n} from './session/store'\nimport type { FeishuReceiveMessageEvent } from './feishu/reply'\n\nconst relayConfig = loadConfigOrExit()\ninitializeI18n(relayConfig.locale)\n\ntry {\n initializeSessionStore({\n homeDir: relayConfig.homeDir,\n workspaceCwd: relayConfig.workspaceCwd,\n })\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.error(t`Failed to start relay: ${message}`)\n process.exit(1)\n}\n\nconst BUSY_MESSAGE = t`Currently busy. Please try again later.`\n\nconst client = new Lark.Client(relayConfig.baseConfig)\nconst wsClient = new Lark.WSClient(relayConfig.baseConfig)\nlet isTaskRunning = false\n\nasync function processIncomingEvent(\n data: FeishuReceiveMessageEvent,\n): Promise<void> {\n try {\n const reply = await buildReplyForMessageEvent(data, {\n botOpenId: relayConfig.botOpenId,\n handleIncomingText: (input) =>\n handleIncomingText(input, {\n createThread: (mode) =>\n createCodexThread({\n mode,\n cwd: relayConfig.workspaceCwd,\n codexBin: relayConfig.codexBin,\n timeoutMs: relayConfig.codexTimeoutMs,\n }),\n runTurn: (params) =>\n runCodexTurn({\n ...params,\n cwd: relayConfig.workspaceCwd,\n codexBin: relayConfig.codexBin,\n timeoutMs: relayConfig.codexTimeoutMs,\n }),\n getSession,\n setSession,\n clearSession,\n withSessionLock,\n listOpenProjects,\n }),\n })\n\n if (reply === null) {\n return\n }\n\n await sendReply(client, data, reply, {\n includeThreadTag: shouldAttachThreadTag(data),\n })\n } catch (error) {\n console.error('failed to handle Feishu message', error)\n try {\n await sendReply(\n client,\n data,\n t`Failed to process message. Please try again later.`,\n )\n } catch (replyError) {\n console.error('failed to send failure message', replyError)\n }\n }\n}\n\nfunction shouldAttachThreadTag(data: FeishuReceiveMessageEvent): boolean {\n const rawText = parseEventText(data.message.content)\n if (rawText === null) {\n return false\n }\n\n const normalizedText = stripMentionTags(rawText).trim()\n if (normalizedText.length === 0) {\n return false\n }\n\n return parseCommand(normalizedText).type === 'prompt'\n}\n\nfunction parseEventText(content: string): string | null {\n try {\n const parsed: unknown = JSON.parse(content)\n if (!isPlainObject(parsed)) {\n return null\n }\n\n return typeof parsed.text === 'string' ? parsed.text : null\n } catch {\n return null\n }\n}\n\nconst eventDispatcher = new Lark.EventDispatcher({}).register({\n 'im.message.receive_v1': async (data: FeishuReceiveMessageEvent) => {\n // eslint-disable-next-line no-console\n console.info(\n 'feishu message received\\n',\n JSON.stringify(data, null, 2),\n '\\n',\n )\n\n if (!shouldProcessMessage(data, relayConfig.botOpenId)) {\n return\n }\n\n if (isTaskRunning) {\n void sendReply(client, data, BUSY_MESSAGE)\n return\n }\n\n isTaskRunning = true\n void processIncomingEvent(data).finally(() => {\n isTaskRunning = false\n })\n },\n})\n\nwsClient.start({ eventDispatcher })\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["COMMAND_HELP","COMMAND_NEW","COMMAND_MODE","COMMAND_STATUS","COMMAND_PROJECTS","COMMAND_RESET","getHelpText","t","join","parseCommand","input","normalized","trim","helpText","length","type","message","startsWith","prompt","parts","split","command","toLowerCase","modeToken","mode","parseMode","fs","path","sessionStore","Map","sessionQueue","SESSION_FILE_NAME","SESSION_FILE_VERSION","persistenceState","initializeSessionStore","input","relayDir","join","homeDir","filePath","mkdirSync","recursive","ensureSessionFileExists","persisted","readPersistedSessionsFile","workspaceSessions","workspaces","workspaceCwd","clear","activeBySessionKey","sessionRef","set","sessionKey","hydrateSession","data","getSessionKey","chatType","chatId","userId","getSession","get","setSession","session","persistSetSession","clearSession","delete","persistClearSession","withSessionLock","run","previous","Promise","resolve","running","then","queueItem","undefined","resetSessionStore","state","savedAt","Date","toISOString","activeSession","toPersistedActiveSession","historySession","toPersistedSessionSnapshot","getOrCreateWorkspaceSessions","history","historyBySessionKey","threadId","push","updatedAt","writePersistedSessionsFile","existsSync","initialContent","JSON","stringify","createEmptyPersistedSessionsFile","writeFileSync","encoding","flag","version","raw","readFileSync","error","Error","formatError","parsed","parse","parsePersistedSessionsFile","value","isObject","TypeError","workspaceValue","Object","entries","parseWorkspaceSessions","parseWorkspaceActiveSession","parseWorkspaceHistorySessions","parsePersistedActiveSession","entryKey","historyValue","trim","length","Array","isArray","map","item","index","parsePersistedSessionSnapshot","location","parseNonEmptyString","snapshot","model","mode","title","normalizeOptionalTitle","cwd","normalized","existing","created","tempPath","now","Math","random","toString","slice","content","renameSync","rmSync","force","message","String","getSessionKey","getHelpText","parseCommand","MAX_SESSION_TITLE_LENGTH","handleIncomingText","input","deps","sessionKey","chatType","chatId","userId","senderId","withSessionLock","parsed","text","currentSession","getSession","type","message","t","title","normalizeSessionTitle","threadId","mode","model","join","result","listOpenProjects","roots","length","lines","map","root","index","error","formatProjectsError","clearSession","setSession","created","createThread","cwd","formatCodexError","runTurn","prompt","session","resolveSessionTitle","Error","trim","currentTitle","undefined","buildFallbackSessionTitle","normalizedPrompt","normalizePrompt","truncateTitle","normalized","chars","Array","from","slice","replace","shouldProcessMessage","event","botOpenId","isMessageFromBot","message","chat_type","shouldHandleGroupMessage","mentions","length","some","mention","id","open_id","resolveSenderId","senderId","user_id","union_id","senderType","sender","sender_type","toLowerCase","sender_id","isPlainObject","isMessageFromBot","resolveSenderId","shouldHandleGroupMessage","buildReplyForMessageEvent","event","deps","botOpenId","message","message_type","t","text","parseTextContent","content","chat_type","mentions","senderId","sender","sender_id","normalizedText","stripMentionTags","trim","length","handleIncomingText","chatType","chatId","chat_id","replace","parsed","JSON","parse","isPlainObject","parseRpcLine","line","parsed","JSON","parse","method","isRpcRequestId","id","params","isRpcErrorObject","error","result","formatRpcError","code","message","value","isRpcErrorResponse","isRpcSuccessResponse","isRpcServerRequest","getServerRequestResult","decision","acceptSettings","forSession","endsWith","answers","success","contentItems","type","text","spawn","createInterface","formatRpcError","getServerRequestResult","isRpcErrorResponse","isRpcServerRequest","isRpcSuccessResponse","parseRpcLine","CodexAppServerClient","setNotificationHandler","handler","notificationHandler","request","method","params","exited","Error","buildExitMessage","requestId","nextId","responsePromise","Promise","resolve","reject","pending","set","value","payload","JSON","stringify","jsonrpc","id","child","stdin","write","error","delete","dispose","lineReader","close","killed","kill","handleStdoutLine","line","parsed","respondToServerRequest","catch","stderrBuffer","push","String","get","result","sendRpcResult","sendRpcError","writeRpcPayload","code","message","serialized","signal","suffix","length","at","options","Map","commandArgs","codexBin","cwd","stdio","input","stdout","crlfDelay","Infinity","on","stderr","chunk","text","trim","values","clear","isPlainObject","initializeClient","client","request","clientInfo","name","title","version","capabilities","experimentalApi","getCollaborationModes","raw","isCollaborationModeListResponse","Error","data","openThread","session","cwd","startThread","resumed","resumeThread","threadId","error","isThreadMissingError","approvalPolicy","sandbox","experimentalRawEvents","parseThreadResult","selectCollaborationModePayload","masks","mode","model","selected","find","mask","toLowerCase","settings","reasoning_effort","developer_instructions","isThreadResult","thread","id","message","includes","isCollaborationModeMask","value","modeIsValid","Array","isArray","every","isPlainObject","createTurnAccumulator","turnCompleted","turnError","lastAgentMessageByItem","lastAgentMessageByTask","applyTurnNotification","accumulator","notification","method","params","message","item","type","text","msg","last_agent_message","turn","error","status","resolveTurnMessage","CodexAppServerClient","getCollaborationModes","initializeClient","openThread","selectCollaborationModePayload","startThread","applyTurnNotification","createTurnAccumulator","resolveTurnMessage","formatRpcError","parseRpcLine","DEFAULT_CODEX_BIN","createCodexThread","input","client","cwd","codexBin","runWithOptionalTimeout","opened","threadId","model","mode","timeoutMs","dispose","runCodexTurn","accumulator","turnDone","createDeferred","turnDoneResolved","setNotificationHandler","notification","turnCompleted","resolve","modeMasks","session","collaborationMode","request","type","text","prompt","text_elements","promise","turnError","Error","message","trim","length","reject","run","onTimeout","withTimeout","Promise","innerResolve","innerReject","timeoutHandle","timeoutPromise","_","setTimeout","race","clearTimeout","process","listOpenProjects","roots","cwd","i18n","messages","enMessages","zhMessages","DEFAULT_LOCALE","detectDefaultLocale","CATALOGS","en","zh","activeLocale","initializeI18n","locale","resolved","resolveLocale","loadAndActivate","getCurrentLocale","ensureI18nInitialized","isSupportedLocale","getDefaultLocale","mappedLocale","mapToAppLocale","systemLocale","readSystemLocale","Intl","DateTimeFormat","resolvedOptions","undefined","normalized","trim","length","toLowerCase","replaceAll","startsWith","fs","os","path","process","getDefaultLocale","initializeI18n","isSupportedLocale","DEFAULT_CODEX_BIN","TEMPLATE_ENV_CONFIG","BASE_DOMAIN","APP_ID","APP_SECRET","BOT_OPEN_ID","CODEX_BIN","CODEX_TIMEOUT_MS","TEMPLATE_CONFIG","env","loadRelayConfig","options","homeDir","homedir","workspaceCwd","cwd","configDir","join","configPath","existsSync","ensureConfigTemplate","Error","t","parsed","parseConfigFile","locale","readLocale","localeValue","domain","readRequiredString","appId","appSecret","baseConfig","botOpenId","readOptionalString","codexBin","codexTimeoutMs","readTimeoutMs","mkdirSync","recursive","writeFileSync","JSON","stringify","encoding","flag","raw","readFileSync","error","formatError","parse","isObject","configObject","undefined","value","field","normalized","TypeError","trim","length","Number","isInteger","trimmed","test","parseInt","systemLocale","console","warn","formatInvalidLocale","mapped","mapLocaleToAppLocale","String","Array","isArray","toLowerCase","replaceAll","startsWith","message","process","loadRelayConfig","loadConfigOrExit","error","console","formatStartupError","exit","message","Error","String","t","resolveSenderId","getSession","getSessionKey","FALLBACK_REPLY_TAG","sendReply","larkClient","data","text","options","content","JSON","stringify","formatReplyTextWithThreadId","message","chat_type","im","v1","create","params","receive_id_type","receive_id","chat_id","msg_type","reply","path","message_id","includeThreadTag","trim","replyTag","resolveReplyTag","normalizedText","length","senderId","sender","sender_id","sessionKey","chatType","chatId","userId","session","threadId","process","Lark","isPlainObject","parseCommand","handleIncomingText","shouldProcessMessage","buildReplyForMessageEvent","stripMentionTags","createCodexThread","runCodexTurn","listOpenProjects","loadConfigOrExit","sendReply","initializeI18n","clearSession","getSession","initializeSessionStore","setSession","withSessionLock","relayConfig","locale","homeDir","workspaceCwd","error","message","Error","String","console","t","exit","BUSY_MESSAGE","client","Client","baseConfig","wsClient","WSClient","isTaskRunning","processIncomingEvent","data","reply","botOpenId","input","createThread","mode","cwd","codexBin","timeoutMs","codexTimeoutMs","runTurn","params","includeThreadTag","shouldAttachThreadTag","replyError","rawText","parseEventText","content","normalizedText","trim","length","type","parsed","JSON","parse","text","eventDispatcher","EventDispatcher","register","info","stringify","finally","start"],"sources":["../src/bot/commands.ts","../src/session/store.ts","../src/bot/handler.ts","../src/bot/message-filter.ts","../src/bot/relay.ts","../src/codex/rpc.ts","../src/codex/app-server-client.ts","../src/codex/thread.ts","../src/codex/turn-state.ts","../src/codex/app-server.ts","../src/codex/state.ts","../src/locales/en/messages.po","../src/locales/zh/messages.po","../src/i18n/runtime.ts","../src/core/config.ts","../src/core/startup.ts","../src/feishu/reply.ts","../src/index.ts"],"sourcesContent":["import { t } from '@lingui/core/macro'\nimport type { ChatMode, ParsedCommand } from '../core/types'\n\nconst COMMAND_HELP = '/help'\nconst COMMAND_NEW = '/new'\nconst COMMAND_MODE = '/mode'\nconst COMMAND_STATUS = '/status'\nconst COMMAND_PROJECTS = '/projects'\nconst COMMAND_RESET = '/reset'\n\nexport function getHelpText(): string {\n return [\n t`Available commands:`,\n t`/help - Show help`,\n t`/new [default|plan] - Create a new session`,\n t`/mode <default|plan> - Switch current session mode`,\n t`/status - Show current session status`,\n t`/projects - Show current working directories`,\n t`/reset - Clear current session`,\n ].join('\\n')\n}\n\nexport function parseCommand(input: string): ParsedCommand {\n const normalized = input.trim()\n const helpText = getHelpText()\n\n if (normalized.length === 0) {\n return {\n type: 'invalid',\n message: t`Command cannot be empty.\\n\\n${helpText}`,\n }\n }\n\n if (!normalized.startsWith('/')) {\n return { type: 'prompt', prompt: normalized }\n }\n\n const parts = normalized.split(/\\s+/)\n const command = parts[0]?.toLowerCase()\n\n if (command === COMMAND_HELP) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/help does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'help' }\n }\n\n if (command === COMMAND_NEW) {\n if (parts.length > 2) {\n return {\n type: 'invalid',\n message: t`/new accepts at most one optional argument: default or plan.\\n\\n${helpText}`,\n }\n }\n\n const modeToken = parts[1]\n if (!modeToken) {\n return { type: 'new', mode: 'default' }\n }\n\n const mode = parseMode(modeToken)\n if (!mode) {\n return {\n type: 'invalid',\n message: t`Invalid mode \"${modeToken}\", only default or plan are supported.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'new', mode }\n }\n\n if (command === COMMAND_MODE) {\n const modeToken = parts[1]\n if (!modeToken || parts.length > 2) {\n return {\n type: 'invalid',\n message: t`/mode requires one argument: default or plan.\\n\\n${helpText}`,\n }\n }\n\n const mode = parseMode(modeToken)\n if (!mode) {\n return {\n type: 'invalid',\n message: t`Invalid mode \"${modeToken}\", only default or plan are supported.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'mode', mode }\n }\n\n if (command === COMMAND_STATUS) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/status does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'status' }\n }\n\n if (command === COMMAND_PROJECTS) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/projects does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'projects' }\n }\n\n if (command === COMMAND_RESET) {\n if (parts.length > 1) {\n return {\n type: 'invalid',\n message: t`/reset does not accept arguments.\\n\\n${helpText}`,\n }\n }\n\n return { type: 'reset' }\n }\n\n return {\n type: 'invalid',\n message: t`Unknown command \"${command ?? normalized}\".\\n\\n${helpText}`,\n }\n}\n\nfunction parseMode(input: string): ChatMode | null {\n const normalized = input.toLowerCase()\n if (normalized === 'default' || normalized === 'plan') {\n return normalized\n }\n\n return null\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { BotSession, ChatMode, SessionKeyInput } from '../core/types'\n\nconst sessionStore = new Map<string, BotSession>()\nconst sessionQueue = new Map<string, Promise<void>>()\nconst SESSION_FILE_NAME = 'sessions.json'\nconst SESSION_FILE_VERSION = 1 as const\n\ninterface PersistedSessionSnapshot {\n mode: ChatMode\n model: string\n title?: string\n savedAt: string\n}\n\ninterface PersistedActiveSession extends PersistedSessionSnapshot {\n sessionKey: string\n threadId: string\n}\n\ninterface PersistedWorkspaceSessions {\n activeBySessionKey: PersistedActiveSession | null\n historyBySessionKey: Record<string, PersistedSessionSnapshot[]>\n}\n\ninterface PersistedSessionsFile {\n version: typeof SESSION_FILE_VERSION\n updatedAt: string\n workspaces: Record<string, PersistedWorkspaceSessions>\n}\n\ninterface SessionStorePersistenceState {\n filePath: string\n workspaceCwd: string\n data: PersistedSessionsFile\n}\n\nlet persistenceState: SessionStorePersistenceState | null = null\n\nexport function initializeSessionStore(input: {\n homeDir: string\n workspaceCwd: string\n}): void {\n const relayDir = path.join(input.homeDir, '.relay')\n const filePath = path.join(relayDir, SESSION_FILE_NAME)\n\n fs.mkdirSync(relayDir, { recursive: true })\n ensureSessionFileExists(filePath)\n\n const persisted = readPersistedSessionsFile(filePath)\n const workspaceSessions = persisted.workspaces[input.workspaceCwd]\n\n sessionStore.clear()\n sessionQueue.clear()\n\n if (workspaceSessions) {\n if (workspaceSessions.activeBySessionKey) {\n const sessionRef = workspaceSessions.activeBySessionKey\n sessionStore.set(\n sessionRef.sessionKey,\n hydrateSession(sessionRef, input.workspaceCwd),\n )\n }\n }\n\n persistenceState = {\n filePath,\n workspaceCwd: input.workspaceCwd,\n data: persisted,\n }\n}\n\nexport function getSessionKey(input: SessionKeyInput): string {\n if (input.chatType === 'p2p') {\n return `p2p:${input.chatId}`\n }\n\n return `group:${input.chatId}:${input.userId}`\n}\n\nexport function getSession(sessionKey: string): BotSession | undefined {\n return sessionStore.get(sessionKey)\n}\n\nexport function setSession(sessionKey: string, session: BotSession): void {\n sessionStore.set(sessionKey, session)\n persistSetSession(sessionKey, session)\n}\n\nexport function clearSession(sessionKey: string): void {\n sessionStore.delete(sessionKey)\n persistClearSession(sessionKey)\n}\n\nexport async function withSessionLock<T>(\n sessionKey: string,\n run: () => Promise<T>,\n): Promise<T> {\n const previous = sessionQueue.get(sessionKey) ?? Promise.resolve()\n const running = previous.then(\n () => run(),\n () => run(),\n )\n const queueItem = running.then(\n () => undefined,\n () => undefined,\n )\n\n sessionQueue.set(sessionKey, queueItem)\n\n try {\n return await running\n } finally {\n if (sessionQueue.get(sessionKey) === queueItem) {\n sessionQueue.delete(sessionKey)\n }\n }\n}\n\nexport function resetSessionStore(): void {\n sessionStore.clear()\n sessionQueue.clear()\n persistenceState = null\n}\n\nfunction persistSetSession(sessionKey: string, session: BotSession): void {\n const state = persistenceState\n if (!state) {\n return\n }\n\n const savedAt = new Date().toISOString()\n const activeSession = toPersistedActiveSession(sessionKey, session, savedAt)\n const historySession = toPersistedSessionSnapshot(session, savedAt)\n const workspaceSessions = getOrCreateWorkspaceSessions(\n state.data,\n state.workspaceCwd,\n )\n\n workspaceSessions.activeBySessionKey = activeSession\n const history = workspaceSessions.historyBySessionKey[session.threadId] ?? []\n history.push(historySession)\n workspaceSessions.historyBySessionKey[session.threadId] = history\n\n state.data.updatedAt = savedAt\n writePersistedSessionsFile(state.filePath, state.data)\n}\n\nfunction persistClearSession(sessionKey: string): void {\n const state = persistenceState\n if (!state) {\n return\n }\n\n const workspaceSessions = state.data.workspaces[state.workspaceCwd]\n if (!workspaceSessions) {\n return\n }\n\n if (\n !workspaceSessions.activeBySessionKey ||\n workspaceSessions.activeBySessionKey.sessionKey !== sessionKey\n ) {\n return\n }\n\n workspaceSessions.activeBySessionKey = null\n\n state.data.updatedAt = new Date().toISOString()\n writePersistedSessionsFile(state.filePath, state.data)\n}\n\nfunction ensureSessionFileExists(filePath: string): void {\n if (fs.existsSync(filePath)) {\n return\n }\n\n const initialContent = `${JSON.stringify(createEmptyPersistedSessionsFile(), null, 2)}\\n`\n fs.writeFileSync(filePath, initialContent, { encoding: 'utf-8', flag: 'wx' })\n}\n\nfunction createEmptyPersistedSessionsFile(): PersistedSessionsFile {\n return {\n version: SESSION_FILE_VERSION,\n updatedAt: new Date().toISOString(),\n workspaces: {},\n }\n}\n\nfunction readPersistedSessionsFile(filePath: string): PersistedSessionsFile {\n let raw: string\n try {\n raw = fs.readFileSync(filePath, 'utf-8')\n } catch (error) {\n throw new Error(\n `Failed to read relay session index at ${filePath}: ${formatError(error)}`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n throw new Error(\n `Invalid JSON in relay session index at ${filePath}: ${formatError(error)}`,\n )\n }\n\n return parsePersistedSessionsFile(parsed, filePath)\n}\n\nfunction parsePersistedSessionsFile(\n value: unknown,\n filePath: string,\n): PersistedSessionsFile {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: root must be a JSON object.`,\n )\n }\n\n if (value.version !== SESSION_FILE_VERSION) {\n throw new Error(\n `Invalid relay session index at ${filePath}: version must be ${SESSION_FILE_VERSION}.`,\n )\n }\n\n if (typeof value.updatedAt !== 'string') {\n throw new TypeError(\n `Invalid relay session index at ${filePath}: updatedAt must be a string.`,\n )\n }\n\n if (!isObject(value.workspaces)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: workspaces must be a JSON object.`,\n )\n }\n\n const workspaces: Record<string, PersistedWorkspaceSessions> = {}\n for (const [workspaceCwd, workspaceValue] of Object.entries(\n value.workspaces,\n )) {\n workspaces[workspaceCwd] = parseWorkspaceSessions(\n workspaceValue,\n filePath,\n workspaceCwd,\n )\n }\n\n return {\n version: SESSION_FILE_VERSION,\n updatedAt: value.updatedAt,\n workspaces,\n }\n}\n\nfunction parseWorkspaceSessions(\n value: unknown,\n filePath: string,\n workspaceCwd: string,\n): PersistedWorkspaceSessions {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: workspace \"${workspaceCwd}\" must be a JSON object.`,\n )\n }\n\n const activeBySessionKey = parseWorkspaceActiveSession(\n value.activeBySessionKey,\n filePath,\n workspaceCwd,\n )\n const historyBySessionKey = parseWorkspaceHistorySessions(\n value.historyBySessionKey,\n filePath,\n workspaceCwd,\n )\n\n return {\n activeBySessionKey,\n historyBySessionKey,\n }\n}\n\nfunction parseWorkspaceActiveSession(\n value: unknown,\n filePath: string,\n workspaceCwd: string,\n): PersistedActiveSession | null {\n if (value === null || value === undefined) {\n return null\n }\n\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: activeBySessionKey for workspace \"${workspaceCwd}\" must be a JSON object or null.`,\n )\n }\n\n return parsePersistedActiveSession(\n value,\n filePath,\n `activeBySessionKey for workspace \"${workspaceCwd}\"`,\n )\n}\n\nfunction parseWorkspaceHistorySessions(\n value: unknown,\n filePath: string,\n workspaceCwd: string,\n): Record<string, PersistedSessionSnapshot[]> {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: historyBySessionKey for workspace \"${workspaceCwd}\" must be a JSON object.`,\n )\n }\n\n const historyBySessionKey: Record<string, PersistedSessionSnapshot[]> = {}\n for (const [entryKey, historyValue] of Object.entries(value)) {\n if (entryKey.trim().length === 0) {\n throw new Error(\n `Invalid relay session index at ${filePath}: historyBySessionKey key in workspace \"${workspaceCwd}\" must be a non-empty threadId.`,\n )\n }\n\n if (!Array.isArray(historyValue)) {\n throw new TypeError(\n `Invalid relay session index at ${filePath}: historyBySessionKey.${entryKey} must be an array.`,\n )\n }\n\n historyBySessionKey[entryKey] = historyValue.map((item, index) =>\n parsePersistedSessionSnapshot(\n item,\n filePath,\n `historyBySessionKey.${entryKey}[${index}]`,\n ),\n )\n }\n\n return historyBySessionKey\n}\n\nfunction parsePersistedActiveSession(\n value: unknown,\n filePath: string,\n location: string,\n): PersistedActiveSession {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location} must be a JSON object.`,\n )\n }\n\n const sessionKey = parseNonEmptyString(\n value.sessionKey,\n filePath,\n `${location}.sessionKey`,\n )\n const threadId = parseNonEmptyString(\n value.threadId,\n filePath,\n `${location}.threadId`,\n )\n const snapshot = parsePersistedSessionSnapshot(value, filePath, location)\n return {\n sessionKey,\n threadId,\n ...snapshot,\n }\n}\n\nfunction parsePersistedSessionSnapshot(\n value: unknown,\n filePath: string,\n location: string,\n): PersistedSessionSnapshot {\n if (!isObject(value)) {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location} must be a JSON object.`,\n )\n }\n\n const model = parseNonEmptyString(value.model, filePath, `${location}.model`)\n if (value.mode !== 'default' && value.mode !== 'plan') {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location}.mode must be \"default\" or \"plan\".`,\n )\n }\n\n if (typeof value.savedAt !== 'string') {\n throw new TypeError(\n `Invalid relay session index at ${filePath}: ${location}.savedAt must be a string.`,\n )\n }\n\n const title = normalizeOptionalTitle(value.title)\n return {\n mode: value.mode,\n model,\n title,\n savedAt: value.savedAt,\n }\n}\n\nfunction parseNonEmptyString(\n value: unknown,\n filePath: string,\n location: string,\n): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new Error(\n `Invalid relay session index at ${filePath}: ${location} must be a non-empty string.`,\n )\n }\n\n return value\n}\n\nfunction hydrateSession(\n sessionRef: PersistedActiveSession,\n cwd: string,\n): BotSession {\n return {\n threadId: sessionRef.threadId,\n mode: sessionRef.mode,\n model: sessionRef.model,\n cwd,\n title: normalizeOptionalTitle(sessionRef.title),\n }\n}\n\nfunction toPersistedActiveSession(\n sessionKey: string,\n session: BotSession,\n savedAt: string,\n): PersistedActiveSession {\n return {\n sessionKey,\n threadId: session.threadId,\n ...toPersistedSessionSnapshot(session, savedAt),\n }\n}\n\nfunction toPersistedSessionSnapshot(\n session: BotSession,\n savedAt: string,\n): PersistedSessionSnapshot {\n const title = normalizeOptionalTitle(session.title)\n\n return {\n mode: session.mode,\n model: session.model,\n title,\n savedAt,\n }\n}\n\nfunction normalizeOptionalTitle(title: unknown): string | undefined {\n if (typeof title !== 'string') {\n return undefined\n }\n\n const normalized = title.trim()\n if (normalized.length === 0) {\n return undefined\n }\n\n return normalized\n}\n\nfunction getOrCreateWorkspaceSessions(\n data: PersistedSessionsFile,\n workspaceCwd: string,\n): PersistedWorkspaceSessions {\n const existing = data.workspaces[workspaceCwd]\n if (existing) {\n return existing\n }\n\n const created: PersistedWorkspaceSessions = {\n activeBySessionKey: null,\n historyBySessionKey: {},\n }\n data.workspaces[workspaceCwd] = created\n return created\n}\n\nfunction writePersistedSessionsFile(\n filePath: string,\n data: PersistedSessionsFile,\n): void {\n const tempPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`\n const content = `${JSON.stringify(data, null, 2)}\\n`\n\n try {\n fs.writeFileSync(tempPath, content, 'utf-8')\n fs.renameSync(tempPath, filePath)\n } catch (error) {\n try {\n if (fs.existsSync(tempPath)) {\n fs.rmSync(tempPath, { force: true })\n }\n } catch {\n // Best-effort cleanup for temporary file.\n }\n\n throw new Error(\n `Failed to write relay session index at ${filePath}: ${formatError(error)}`,\n )\n }\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return error.message\n }\n\n return String(error)\n}\n","import { t } from '@lingui/core/macro'\nimport { getSessionKey } from '../session/store'\nimport { getHelpText, parseCommand } from './commands'\nimport type {\n BotSession,\n ChatMode,\n CodexTurnResult,\n HandleIncomingTextInput,\n OpenProjectsResult,\n} from '../core/types'\n\nconst MAX_SESSION_TITLE_LENGTH = 24\n\nexport interface HandleIncomingTextDeps {\n createThread: (mode: ChatMode) => Promise<BotSession>\n runTurn: (params: {\n prompt: string\n mode: ChatMode\n session: BotSession | null\n }) => Promise<CodexTurnResult>\n getSession: (sessionKey: string) => BotSession | undefined\n setSession: (sessionKey: string, session: BotSession) => void\n clearSession: (sessionKey: string) => void\n withSessionLock: <T>(sessionKey: string, run: () => Promise<T>) => Promise<T>\n listOpenProjects: () => Promise<OpenProjectsResult>\n}\n\nexport async function handleIncomingText(\n input: HandleIncomingTextInput,\n deps: HandleIncomingTextDeps,\n): Promise<string> {\n const sessionKey = getSessionKey({\n chatType: input.chatType,\n chatId: input.chatId,\n userId: input.senderId,\n })\n\n return deps.withSessionLock(sessionKey, async () => {\n const parsed = parseCommand(input.text)\n const currentSession = deps.getSession(sessionKey)\n\n if (parsed.type === 'invalid') {\n return parsed.message\n }\n\n if (parsed.type === 'help') {\n return getHelpText()\n }\n\n if (parsed.type === 'status') {\n if (!currentSession) {\n return t`No active session. Send a normal message or use /new to create one.`\n }\n\n const title =\n normalizeSessionTitle(currentSession.title) ?? t`New Session`\n\n return [\n t`Current session status:`,\n t`thread: ${currentSession.threadId}`,\n t`title: ${title}`,\n t`mode: ${currentSession.mode}`,\n t`model: ${currentSession.model}`,\n ].join('\\n')\n }\n\n if (parsed.type === 'projects') {\n try {\n const result = await deps.listOpenProjects()\n if (result.roots.length === 0) {\n return t`No working directories are currently open.`\n }\n\n const lines = result.roots.map(\n (root, index) => t`${index + 1}. ${root}`,\n )\n return [t`Current working directories:`, ...lines].join('\\n')\n } catch (error) {\n return formatProjectsError(error)\n }\n }\n\n if (parsed.type === 'reset') {\n deps.clearSession(sessionKey)\n return t`Current session has been cleared.`\n }\n\n if (parsed.type === 'mode') {\n if (!currentSession) {\n return t`No active session. Send a normal message or use /new to create one first.`\n }\n\n deps.setSession(sessionKey, {\n ...currentSession,\n mode: parsed.mode,\n })\n\n return t`Switched to ${parsed.mode} mode.`\n }\n\n if (parsed.type === 'new') {\n try {\n const created = await deps.createThread(parsed.mode)\n deps.setSession(sessionKey, created)\n return [\n t`Created a new session.`,\n t`thread: ${created.threadId}`,\n t`cwd: ${created.cwd}`,\n t`mode: ${created.mode}`,\n t`model: ${created.model}`,\n ].join('\\n')\n } catch (error) {\n return formatCodexError(error)\n }\n }\n\n try {\n const mode = currentSession?.mode ?? 'default'\n const result = await deps.runTurn({\n prompt: parsed.prompt,\n mode,\n session: currentSession ?? null,\n })\n\n const title = await resolveSessionTitle({\n currentSession,\n prompt: parsed.prompt,\n })\n\n deps.setSession(sessionKey, {\n threadId: result.threadId,\n model: result.model,\n mode: result.mode,\n cwd: result.cwd,\n title,\n })\n return result.message\n } catch (error) {\n return formatCodexError(error)\n }\n })\n}\n\nfunction formatCodexError(error: unknown): string {\n if (error instanceof Error && error.message.trim().length > 0) {\n return t`Codex execution failed: ${error.message}`\n }\n\n return t`Codex execution failed. Please try again later.`\n}\n\nfunction formatProjectsError(error: unknown): string {\n if (error instanceof Error && error.message.trim().length > 0) {\n return t`Failed to read open projects: ${error.message}`\n }\n\n return t`Failed to read open projects. Please try again later.`\n}\n\nasync function resolveSessionTitle(input: {\n currentSession: BotSession | undefined\n prompt: string\n}): Promise<string | undefined> {\n const currentTitle = normalizeSessionTitle(input.currentSession?.title)\n if (currentTitle) {\n return currentTitle\n }\n\n if (!input.currentSession) {\n return undefined\n }\n\n return buildFallbackSessionTitle(input.prompt)\n}\n\nfunction buildFallbackSessionTitle(prompt: string): string {\n const normalizedPrompt = normalizePrompt(prompt)\n if (normalizedPrompt.length === 0) {\n return t`New Session`\n }\n\n return truncateTitle(normalizedPrompt)\n}\n\nfunction normalizeSessionTitle(title: string | undefined): string | null {\n if (!title) {\n return null\n }\n\n const normalized = title.trim()\n if (normalized.length === 0) {\n return null\n }\n\n return normalized\n}\n\nfunction truncateTitle(input: string): string {\n const chars = Array.from(input)\n if (chars.length <= MAX_SESSION_TITLE_LENGTH) {\n return input\n }\n\n if (MAX_SESSION_TITLE_LENGTH <= 3) {\n return chars.slice(0, MAX_SESSION_TITLE_LENGTH).join('')\n }\n\n return `${chars.slice(0, MAX_SESSION_TITLE_LENGTH - 3).join('')}...`\n}\n\nfunction normalizePrompt(input: string): string {\n return input.replace(/\\s+/g, ' ').trim()\n}\n","import type { FeishuMention } from '../core/types'\n\ninterface EventSenderId {\n open_id?: string\n user_id?: string\n union_id?: string\n}\n\ninterface MessageFilterEvent {\n sender: {\n sender_type?: string\n sender_id?: EventSenderId\n }\n message: {\n chat_type: string\n mentions?: FeishuMention[]\n }\n}\n\nexport function shouldProcessMessage(\n event: MessageFilterEvent,\n botOpenId?: string,\n): boolean {\n if (isMessageFromBot(event, botOpenId)) {\n return false\n }\n\n if (event.message.chat_type === 'p2p') {\n return true\n }\n\n return shouldHandleGroupMessage(event.message.mentions, botOpenId)\n}\n\nexport function shouldHandleGroupMessage(\n mentions: FeishuMention[] | undefined,\n botOpenId?: string,\n): boolean {\n if (!mentions || mentions.length === 0) {\n return false\n }\n\n if (!botOpenId) {\n return true\n }\n\n return mentions.some((mention) => mention.id?.open_id === botOpenId)\n}\n\nexport function resolveSenderId(\n senderId: EventSenderId | undefined,\n): string | null {\n if (!senderId) {\n return null\n }\n\n return senderId.open_id ?? senderId.user_id ?? senderId.union_id ?? null\n}\n\nexport function isMessageFromBot(\n event: MessageFilterEvent,\n botOpenId?: string,\n): boolean {\n const senderType = event.sender.sender_type?.toLowerCase()\n if (senderType === 'app') {\n return true\n }\n\n if (!botOpenId) {\n return false\n }\n\n const senderId = resolveSenderId(event.sender.sender_id)\n return senderId === botOpenId\n}\n","import { t } from '@lingui/core/macro'\nimport { isPlainObject } from 'es-toolkit/predicate'\nimport {\n isMessageFromBot,\n resolveSenderId,\n shouldHandleGroupMessage,\n} from './message-filter'\nimport type { FeishuMention, HandleIncomingTextInput } from '../core/types'\n\nexport interface ReceiveMessageEvent {\n sender: {\n sender_type?: string\n sender_id?: {\n open_id?: string\n user_id?: string\n union_id?: string\n }\n }\n message: {\n chat_id: string\n chat_type: string\n message_type: string\n content: string\n mentions?: FeishuMention[]\n }\n}\n\nexport interface RelayBotDeps {\n botOpenId?: string\n handleIncomingText: (input: HandleIncomingTextInput) => Promise<string>\n}\n\nexport { shouldHandleGroupMessage } from './message-filter'\n\nexport async function buildReplyForMessageEvent(\n event: ReceiveMessageEvent,\n deps: RelayBotDeps,\n): Promise<string | null> {\n if (isMessageFromBot(event, deps.botOpenId)) {\n return null\n }\n\n if (event.message.message_type !== 'text') {\n return t`Failed to parse message. Please send a text message.`\n }\n\n const text = parseTextContent(event.message.content)\n if (!text) {\n return t`Failed to parse message. Please send a text message.`\n }\n\n if (\n event.message.chat_type !== 'p2p' &&\n !shouldHandleGroupMessage(event.message.mentions, deps.botOpenId)\n ) {\n return null\n }\n\n const senderId = resolveSenderId(event.sender.sender_id)\n if (!senderId) {\n return t`Cannot identify sender. Please try again later.`\n }\n\n const normalizedText = stripMentionTags(text).trim()\n if (normalizedText.length === 0) {\n return t`Please send a text message.`\n }\n\n return deps.handleIncomingText({\n chatType: event.message.chat_type,\n chatId: event.message.chat_id,\n senderId,\n text: normalizedText,\n })\n}\n\nexport function stripMentionTags(text: string): string {\n return text.replace(/<at\\b[^>]*>.*?<\\/at>/g, '').trim()\n}\n\nfunction parseTextContent(content: string): string | null {\n try {\n const parsed: unknown = JSON.parse(content)\n if (!isPlainObject(parsed)) {\n return null\n }\n\n return typeof parsed.text === 'string' ? parsed.text : null\n } catch {\n return null\n }\n}\n","import { isPlainObject } from 'es-toolkit/predicate'\nimport type {\n RpcErrorObject,\n RpcErrorResponse,\n RpcIncomingLine,\n RpcRequestId,\n RpcServerRequest,\n RpcSuccessResponse,\n} from '../core/types'\n\nexport function parseRpcLine(line: string): RpcIncomingLine | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(line)\n } catch {\n return null\n }\n\n if (!isPlainObject(parsed)) {\n return null\n }\n\n if (typeof parsed.method === 'string') {\n if (isRpcRequestId(parsed.id)) {\n return {\n id: parsed.id,\n method: parsed.method,\n params: parsed.params,\n }\n }\n\n return {\n method: parsed.method,\n params: parsed.params,\n }\n }\n\n if (!isRpcRequestId(parsed.id)) {\n return null\n }\n\n if ('error' in parsed && isRpcErrorObject(parsed.error)) {\n return {\n id: parsed.id,\n error: parsed.error,\n }\n }\n\n if ('result' in parsed) {\n return {\n id: parsed.id,\n result: parsed.result,\n }\n }\n\n return null\n}\n\nexport function formatRpcError(error: RpcErrorObject): string {\n return `Codex RPC error (${error.code}): ${error.message}`\n}\n\nexport function isRpcRequestId(value: unknown): value is RpcRequestId {\n return typeof value === 'number' || typeof value === 'string'\n}\n\nexport function isRpcErrorObject(value: unknown): value is RpcErrorObject {\n if (!isPlainObject(value)) {\n return false\n }\n\n return typeof value.code === 'number' && typeof value.message === 'string'\n}\n\nexport function isRpcErrorResponse(\n value: RpcIncomingLine,\n): value is RpcErrorResponse {\n return 'error' in value\n}\n\nexport function isRpcSuccessResponse(\n value: RpcIncomingLine,\n): value is RpcSuccessResponse<unknown> {\n return 'result' in value\n}\n\nexport function isRpcServerRequest(\n value: RpcIncomingLine,\n): value is RpcServerRequest<unknown> {\n return 'method' in value && 'id' in value\n}\n\nexport function getServerRequestResult(method: string): unknown | null {\n if (method === 'item/commandExecution/requestApproval') {\n return {\n decision: 'accept',\n acceptSettings: {\n forSession: true,\n },\n }\n }\n\n if (method === 'item/fileChange/requestApproval') {\n return { decision: 'accept' }\n }\n\n if (method.endsWith('/requestApproval')) {\n return { decision: 'accept' }\n }\n\n if (method === 'execCommandApproval') {\n return { decision: 'allow' }\n }\n\n if (method === 'applyPatchApproval') {\n return { decision: 'allow' }\n }\n\n if (method.endsWith('Approval')) {\n return { decision: 'allow' }\n }\n\n if (method === 'item/tool/requestUserInput') {\n return { answers: {} }\n }\n\n if (method === 'item/tool/call') {\n return {\n success: false,\n contentItems: [\n {\n type: 'inputText',\n text: 'Dynamic tool calls are unavailable in relay-bot.',\n },\n ],\n }\n }\n\n return null\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport {\n formatRpcError,\n getServerRequestResult,\n isRpcErrorResponse,\n isRpcServerRequest,\n isRpcSuccessResponse,\n parseRpcLine,\n} from './rpc'\nimport type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface } from 'node:readline'\nimport type {\n RpcNotification,\n RpcRequestId,\n RpcServerRequest,\n} from '../core/types'\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (reason: Error) => void\n}\n\nexport class CodexAppServerClient {\n private readonly options: {\n cwd: string\n codexBin: string\n }\n\n private readonly child: ChildProcessWithoutNullStreams\n\n private readonly pending = new Map<RpcRequestId, PendingRequest>()\n\n private readonly stderrBuffer: string[] = []\n\n private readonly lineReader: Interface\n\n private nextId = 1\n\n private notificationHandler:\n | ((notification: RpcNotification<unknown>) => void)\n | null = null\n\n private exited = false\n\n constructor(options: { cwd: string; codexBin: string }) {\n this.options = options\n const commandArgs = ['app-server']\n\n this.child = spawn(this.options.codexBin, commandArgs, {\n cwd: this.options.cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n this.lineReader = createInterface({\n input: this.child.stdout,\n crlfDelay: Infinity,\n })\n\n this.lineReader.on('line', (line) => {\n this.handleStdoutLine(line)\n })\n\n this.child.stderr.on('data', (chunk) => {\n const text = String(chunk).trim()\n if (text.length > 0) {\n this.stderrBuffer.push(text)\n }\n })\n\n this.child.on('exit', (code, signal) => {\n this.exited = true\n const error = new Error(this.buildExitMessage(code, signal))\n for (const pending of this.pending.values()) {\n pending.reject(error)\n }\n this.pending.clear()\n })\n }\n\n setNotificationHandler(\n handler: (notification: RpcNotification<unknown>) => void,\n ): void {\n this.notificationHandler = handler\n }\n\n async request<T>(method: string, params: unknown): Promise<T> {\n if (this.exited) {\n throw new Error(this.buildExitMessage(null, null))\n }\n\n const requestId = this.nextId\n this.nextId += 1\n\n const responsePromise = new Promise<T>((resolve, reject) => {\n this.pending.set(requestId, {\n resolve: (value: unknown) => resolve(value as T),\n reject,\n })\n })\n\n const payload = JSON.stringify({\n jsonrpc: '2.0',\n id: requestId,\n method,\n params,\n })\n\n await new Promise<void>((resolve, reject) => {\n this.child.stdin.write(`${payload}\\n`, (error) => {\n if (error) {\n this.pending.delete(requestId)\n reject(error)\n return\n }\n resolve()\n })\n })\n\n return responsePromise\n }\n\n dispose(): void {\n this.lineReader.close()\n if (!this.child.killed) {\n this.child.kill('SIGTERM')\n }\n }\n\n private handleStdoutLine(line: string): void {\n const parsed = parseRpcLine(line)\n if (!parsed) {\n return\n }\n\n if ('method' in parsed) {\n if (isRpcServerRequest(parsed)) {\n void this.respondToServerRequest(parsed).catch((error) => {\n this.stderrBuffer.push(\n `failed to respond to server request \"${parsed.method}\": ${String(\n error,\n )}`,\n )\n })\n return\n }\n\n this.notificationHandler?.(parsed)\n return\n }\n\n const pending = this.pending.get(parsed.id)\n if (!pending) {\n return\n }\n\n this.pending.delete(parsed.id)\n if (isRpcErrorResponse(parsed)) {\n pending.reject(new Error(formatRpcError(parsed.error)))\n return\n }\n\n if (isRpcSuccessResponse(parsed)) {\n pending.resolve(parsed.result)\n }\n }\n\n private async respondToServerRequest(\n request: RpcServerRequest<unknown>,\n ): Promise<void> {\n const result = getServerRequestResult(request.method)\n if (result !== null) {\n await this.sendRpcResult(request.id, result)\n return\n }\n\n await this.sendRpcError(\n request.id,\n -32601,\n `Unsupported server request method: ${request.method}`,\n )\n }\n\n private async sendRpcResult(\n id: RpcRequestId,\n result: unknown,\n ): Promise<void> {\n await this.writeRpcPayload({\n jsonrpc: '2.0',\n id,\n result,\n })\n }\n\n private async sendRpcError(\n id: RpcRequestId,\n code: number,\n message: string,\n ): Promise<void> {\n await this.writeRpcPayload({\n jsonrpc: '2.0',\n id,\n error: {\n code,\n message,\n },\n })\n }\n\n private async writeRpcPayload(payload: {\n jsonrpc: '2.0'\n id: RpcRequestId\n result?: unknown\n error?: {\n code: number\n message: string\n }\n }): Promise<void> {\n if (this.exited) {\n return\n }\n\n const serialized = JSON.stringify(payload)\n await new Promise<void>((resolve, reject) => {\n this.child.stdin.write(`${serialized}\\n`, (error) => {\n if (error) {\n reject(error)\n return\n }\n resolve()\n })\n })\n }\n\n private buildExitMessage(\n code: number | null,\n signal: NodeJS.Signals | null,\n ): string {\n const suffix =\n this.stderrBuffer.length > 0\n ? `; stderr: ${this.stderrBuffer.at(-1)}`\n : ''\n return `Codex app-server exited (code=${code ?? 'null'}, signal=${\n signal ?? 'null'\n })${suffix}`\n }\n}\n","import { isPlainObject } from 'es-toolkit/predicate'\nimport type {\n BotSession,\n ChatMode,\n CollaborationModeListResponse,\n CollaborationModeMask,\n ThreadResult,\n} from '../core/types'\nimport type { CodexAppServerClient } from './app-server-client'\n\nexport interface OpenThreadResult {\n threadId: string\n cwd: string\n model: string\n}\n\nexport interface CollaborationModePayload {\n mode: ChatMode\n settings: {\n model: string\n reasoning_effort: string | null\n developer_instructions: string | null\n }\n}\n\nexport async function initializeClient(\n client: CodexAppServerClient,\n): Promise<void> {\n await client.request('initialize', {\n clientInfo: {\n name: 'relay-bot',\n title: 'Relay Bot',\n version: '0.0.0',\n },\n capabilities: {\n experimentalApi: true,\n },\n })\n}\n\nexport async function getCollaborationModes(\n client: CodexAppServerClient,\n): Promise<CollaborationModeMask[]> {\n const raw = await client.request('collaborationMode/list', {})\n if (!isCollaborationModeListResponse(raw)) {\n throw new Error('Invalid collaboration mode response from Codex')\n }\n\n return raw.data\n}\n\nexport async function openThread(\n client: CodexAppServerClient,\n session: BotSession | null,\n cwd: string,\n): Promise<OpenThreadResult> {\n if (!session) {\n return startThread(client, cwd)\n }\n\n if (session.cwd !== cwd) {\n return startThread(client, cwd)\n }\n\n try {\n const resumed = await resumeThread(client, session.threadId)\n if (resumed.cwd !== cwd) {\n return startThread(client, cwd)\n }\n\n return resumed\n } catch (error) {\n if (isThreadMissingError(error)) {\n return startThread(client, cwd)\n }\n throw error\n }\n}\n\nexport async function startThread(\n client: CodexAppServerClient,\n cwd: string,\n): Promise<OpenThreadResult> {\n const raw = await client.request('thread/start', {\n cwd,\n approvalPolicy: 'on-request',\n sandbox: 'workspace-write',\n experimentalRawEvents: false,\n })\n\n return parseThreadResult(raw)\n}\n\nexport function selectCollaborationModePayload(\n masks: CollaborationModeMask[],\n mode: ChatMode,\n model: string,\n): CollaborationModePayload {\n const selected = masks.find((mask) => {\n if (mask.mode === mode) {\n return true\n }\n\n return mask.name.toLowerCase() === mode\n })\n\n if (!selected) {\n throw new Error(`Collaboration mode \"${mode}\" is unavailable`)\n }\n\n return {\n mode,\n settings: {\n model,\n reasoning_effort: selected.reasoning_effort,\n developer_instructions: selected.developer_instructions,\n },\n }\n}\n\nasync function resumeThread(\n client: CodexAppServerClient,\n threadId: string,\n): Promise<OpenThreadResult> {\n const raw = await client.request('thread/resume', {\n threadId,\n })\n\n return parseThreadResult(raw)\n}\n\nfunction parseThreadResult(raw: unknown): OpenThreadResult {\n if (!isThreadResult(raw)) {\n throw new Error('Invalid thread response from Codex')\n }\n\n return {\n threadId: raw.thread.id,\n model: raw.model,\n cwd: raw.cwd,\n }\n}\n\nfunction isThreadMissingError(error: unknown): boolean {\n if (!(error instanceof Error)) {\n return false\n }\n\n return error.message.includes('thread not found')\n}\n\nfunction isCollaborationModeMask(\n value: unknown,\n): value is CollaborationModeMask {\n if (!isPlainObject(value)) {\n return false\n }\n\n const modeIsValid =\n value.mode === null || value.mode === 'default' || value.mode === 'plan'\n\n return (\n typeof value.name === 'string' &&\n modeIsValid &&\n (typeof value.model === 'string' || value.model === null) &&\n (typeof value.reasoning_effort === 'string' ||\n value.reasoning_effort === null) &&\n (typeof value.developer_instructions === 'string' ||\n value.developer_instructions === null)\n )\n}\n\nfunction isCollaborationModeListResponse(\n value: unknown,\n): value is CollaborationModeListResponse {\n if (!isPlainObject(value) || !Array.isArray(value.data)) {\n return false\n }\n\n return value.data.every(isCollaborationModeMask)\n}\n\nfunction isThreadResult(value: unknown): value is ThreadResult {\n if (!isPlainObject(value) || !isPlainObject(value.thread)) {\n return false\n }\n\n return typeof value.thread.id === 'string' && typeof value.model === 'string'\n}\n","import { isPlainObject } from 'es-toolkit/predicate'\nimport type {\n RpcItemCompletedParams,\n RpcNotification,\n RpcTaskCompleteParams,\n RpcTurnCompletedParams,\n TurnAccumulator,\n} from '../core/types'\n\nexport function createTurnAccumulator(): TurnAccumulator {\n return {\n turnCompleted: false,\n turnError: null,\n lastAgentMessageByItem: null,\n lastAgentMessageByTask: null,\n }\n}\n\nexport function applyTurnNotification(\n accumulator: TurnAccumulator,\n notification: RpcNotification<unknown>,\n): void {\n if (notification.method === 'error') {\n if (\n isPlainObject(notification.params) &&\n typeof notification.params.message === 'string'\n ) {\n accumulator.turnError = notification.params.message\n } else {\n accumulator.turnError = 'Codex returned an unknown error event'\n }\n accumulator.turnCompleted = true\n return\n }\n\n if (notification.method === 'item/completed') {\n const params = notification.params as RpcItemCompletedParams\n const item = params.item\n if (item?.type === 'agentMessage' && typeof item.text === 'string') {\n accumulator.lastAgentMessageByItem = item.text\n }\n return\n }\n\n if (notification.method === 'codex/event/task_complete') {\n const params = notification.params as RpcTaskCompleteParams\n const message = params.msg?.last_agent_message\n if (typeof message === 'string') {\n accumulator.lastAgentMessageByTask = message\n }\n return\n }\n\n if (notification.method === 'turn/completed') {\n const params = notification.params as RpcTurnCompletedParams\n accumulator.turnCompleted = true\n if (params.turn?.error?.message) {\n accumulator.turnError = params.turn.error.message\n return\n }\n\n if (params.turn?.status === 'failed') {\n accumulator.turnError = 'Codex turn failed'\n }\n }\n}\n\nexport function resolveTurnMessage(\n accumulator: TurnAccumulator,\n): string | null {\n return (\n accumulator.lastAgentMessageByTask ?? accumulator.lastAgentMessageByItem\n )\n}\n","import { CodexAppServerClient } from './app-server-client'\nimport {\n getCollaborationModes,\n initializeClient,\n openThread,\n selectCollaborationModePayload,\n startThread,\n} from './thread'\nimport {\n applyTurnNotification,\n createTurnAccumulator,\n resolveTurnMessage,\n} from './turn-state'\nimport type { BotSession, ChatMode, CodexTurnResult } from '../core/types'\n\nexport { formatRpcError, parseRpcLine } from './rpc'\nexport {\n applyTurnNotification,\n createTurnAccumulator,\n resolveTurnMessage,\n} from './turn-state'\n\nconst DEFAULT_CODEX_BIN = 'codex'\n\ninterface Deferred<T> {\n promise: Promise<T>\n resolve: (value: T | PromiseLike<T>) => void\n reject: (reason?: unknown) => void\n}\n\nexport interface RunCodexTurnInput {\n prompt: string\n mode: ChatMode\n session: BotSession | null\n cwd: string\n codexBin?: string\n timeoutMs?: number\n}\n\nexport interface CreateCodexThreadInput {\n mode: ChatMode\n cwd: string\n codexBin?: string\n timeoutMs?: number\n}\n\nexport async function createCodexThread(\n input: CreateCodexThreadInput,\n): Promise<BotSession> {\n const client = new CodexAppServerClient({\n cwd: input.cwd,\n codexBin: input.codexBin ?? DEFAULT_CODEX_BIN,\n })\n\n try {\n return await runWithOptionalTimeout(\n async () => {\n await initializeClient(client)\n const opened = await startThread(client, input.cwd)\n return {\n threadId: opened.threadId,\n model: opened.model,\n mode: input.mode,\n cwd: opened.cwd,\n }\n },\n input.timeoutMs,\n () => client.dispose(),\n )\n } finally {\n client.dispose()\n }\n}\n\nexport async function runCodexTurn(\n input: RunCodexTurnInput,\n): Promise<CodexTurnResult> {\n const client = new CodexAppServerClient({\n cwd: input.cwd,\n codexBin: input.codexBin ?? DEFAULT_CODEX_BIN,\n })\n\n const accumulator = createTurnAccumulator()\n const turnDone = createDeferred<void>()\n let turnDoneResolved = false\n\n client.setNotificationHandler((notification) => {\n applyTurnNotification(accumulator, notification)\n if (accumulator.turnCompleted && !turnDoneResolved) {\n turnDoneResolved = true\n turnDone.resolve()\n }\n })\n\n try {\n return await runWithOptionalTimeout(\n async () => {\n await initializeClient(client)\n const modeMasks = await getCollaborationModes(client)\n const opened = await openThread(client, input.session, input.cwd)\n const collaborationMode = selectCollaborationModePayload(\n modeMasks,\n input.mode,\n opened.model,\n )\n\n await client.request('turn/start', {\n threadId: opened.threadId,\n input: [\n {\n type: 'text',\n text: input.prompt,\n text_elements: [],\n },\n ],\n collaborationMode,\n })\n\n await turnDone.promise\n\n if (accumulator.turnError) {\n throw new Error(accumulator.turnError)\n }\n\n const message = resolveTurnMessage(accumulator)\n if (!message || message.trim().length === 0) {\n throw new Error('Codex did not return a message')\n }\n\n return {\n threadId: opened.threadId,\n model: opened.model,\n mode: input.mode,\n message,\n cwd: opened.cwd,\n }\n },\n input.timeoutMs,\n () => {\n if (!turnDoneResolved) {\n turnDoneResolved = true\n turnDone.reject(new Error('Codex execution timed out'))\n }\n client.dispose()\n },\n )\n } finally {\n client.dispose()\n }\n}\n\nasync function runWithOptionalTimeout<T>(\n run: () => Promise<T>,\n timeoutMs: number | undefined,\n onTimeout: () => void,\n): Promise<T> {\n if (typeof timeoutMs !== 'number' || timeoutMs <= 0) {\n return run()\n }\n\n return withTimeout(run, timeoutMs, onTimeout)\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T | PromiseLike<T>) => void\n let reject!: (reason?: unknown) => void\n const promise = new Promise<T>((innerResolve, innerReject) => {\n resolve = innerResolve\n reject = innerReject\n })\n\n return { promise, resolve, reject }\n}\n\nasync function withTimeout<T>(\n run: () => Promise<T>,\n timeoutMs: number,\n onTimeout: () => void,\n): Promise<T> {\n let timeoutHandle: NodeJS.Timeout | undefined\n const timeoutPromise = new Promise<T>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n onTimeout()\n reject(new Error(`Codex request timed out after ${timeoutMs}ms`))\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([run(), timeoutPromise])\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle)\n }\n }\n}\n","import process from 'node:process'\nimport type { OpenProjectsResult } from '../core/types'\n\nexport async function listOpenProjects(): Promise<OpenProjectsResult> {\n return {\n roots: [process.cwd()],\n }\n}\n","msgid \"\"\nmsgstr \"\"\n\"POT-Creation-Date: 2026-02-20 17:04+0800\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: @lingui/cli\\n\"\n\"Language: en\\n\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"PO-Revision-Date: \\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: \\n\"\n\"Plural-Forms: \\n\"\n\n#. placeholder {0}: index + 1\n#: src/bot/handler.ts:75\nmsgid \"{0}. {root}\"\nmsgstr \"{0}. {root}\"\n\n#: src/bot/commands.ts:14\nmsgid \"/help - Show help\"\nmsgstr \"/help - Show help\"\n\n#: src/bot/commands.ts:45\nmsgid \"\"\n\"/help does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/help does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:16\nmsgid \"/mode <default|plan> - Switch current session mode\"\nmsgstr \"/mode <default|plan> - Switch current session mode\"\n\n#: src/bot/commands.ts:81\nmsgid \"\"\n\"/mode requires one argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/mode requires one argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:15\nmsgid \"/new [default|plan] - Create a new session\"\nmsgstr \"/new [default|plan] - Create a new session\"\n\n#: src/bot/commands.ts:56\nmsgid \"\"\n\"/new accepts at most one optional argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/new accepts at most one optional argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:18\nmsgid \"/projects - Show current working directories\"\nmsgstr \"/projects - Show current working directories\"\n\n#: src/bot/commands.ts:111\nmsgid \"\"\n\"/projects does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/projects does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:19\nmsgid \"/reset - Clear current session\"\nmsgstr \"/reset - Clear current session\"\n\n#: src/bot/commands.ts:122\nmsgid \"\"\n\"/reset does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/reset does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:17\nmsgid \"/status - Show current session status\"\nmsgstr \"/status - Show current session status\"\n\n#: src/bot/commands.ts:100\nmsgid \"\"\n\"/status does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/status does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:13\nmsgid \"Available commands:\"\nmsgstr \"Available commands:\"\n\n#: src/bot/relay.ts:61\nmsgid \"Cannot identify sender. Please try again later.\"\nmsgstr \"Cannot identify sender. Please try again later.\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:146\nmsgid \"Codex execution failed: {0}\"\nmsgstr \"Codex execution failed: {0}\"\n\n#: src/bot/handler.ts:149\nmsgid \"Codex execution failed. Please try again later.\"\nmsgstr \"Codex execution failed. Please try again later.\"\n\n#: src/bot/commands.ts:30\nmsgid \"\"\n\"Command cannot be empty.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"Command cannot be empty.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/handler.ts:106\nmsgid \"Created a new session.\"\nmsgstr \"Created a new session.\"\n\n#: src/bot/handler.ts:85\nmsgid \"Current session has been cleared.\"\nmsgstr \"Current session has been cleared.\"\n\n#: src/bot/handler.ts:59\nmsgid \"Current session status:\"\nmsgstr \"Current session status:\"\n\n#: src/bot/handler.ts:77\nmsgid \"Current working directories:\"\nmsgstr \"Current working directories:\"\n\n#: src/index.ts:37\nmsgid \"Currently busy. Please try again later.\"\nmsgstr \"Currently busy. Please try again later.\"\n\n#. placeholder {0}: created.cwd\n#: src/bot/handler.ts:108\nmsgid \"cwd: {0}\"\nmsgstr \"cwd: {0}\"\n\n#: src/bot/relay.ts:44\n#: src/bot/relay.ts:49\nmsgid \"Failed to parse message. Please send a text message.\"\nmsgstr \"Failed to parse message. Please send a text message.\"\n\n#: src/index.ts:86\nmsgid \"Failed to process message. Please try again later.\"\nmsgstr \"Failed to process message. Please try again later.\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:154\nmsgid \"Failed to read open projects: {0}\"\nmsgstr \"Failed to read open projects: {0}\"\n\n#: src/bot/handler.ts:157\nmsgid \"Failed to read open projects. Please try again later.\"\nmsgstr \"Failed to read open projects. Please try again later.\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:131\nmsgid \"Failed to read relay config at {configPath}: {0}\"\nmsgstr \"Failed to read relay config at {configPath}: {0}\"\n\n#: src/core/startup.ts:17\n#: src/index.ts:33\nmsgid \"Failed to start relay: {message}\"\nmsgstr \"Failed to start relay: {message}\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:140\nmsgid \"Invalid JSON in relay config at {configPath}: {0}\"\nmsgstr \"Invalid JSON in relay config at {configPath}: {0}\"\n\n#: src/bot/commands.ts:69\n#: src/bot/commands.ts:89\nmsgid \"\"\n\"Invalid mode \\\"{modeToken}\\\", only default or plan are supported.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"Invalid mode \\\"{modeToken}\\\", only default or plan are supported.\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/core/config.ts:160\nmsgid \"Invalid relay config at {configPath}: env must be a JSON object.\"\nmsgstr \"Invalid relay config at {configPath}: env must be a JSON object.\"\n\n#: src/core/config.ts:146\nmsgid \"Invalid relay config at {configPath}: root must be a JSON object.\"\nmsgstr \"Invalid relay config at {configPath}: root must be a JSON object.\"\n\n#: src/core/config.ts:174\nmsgid \"Invalid relay config: {field} is required and must be a non-empty string.\"\nmsgstr \"Invalid relay config: {field} is required and must be a non-empty string.\"\n\n#: src/core/config.ts:187\nmsgid \"Invalid relay config: {field} must be a string.\"\nmsgstr \"Invalid relay config: {field} must be a string.\"\n\n#: src/core/config.ts:208\n#: src/core/config.ts:219\n#: src/core/config.ts:227\nmsgid \"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"\nmsgstr \"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"\n\n#. placeholder {0}: formatInvalidLocale(value)\n#: src/core/config.ts:241\nmsgid \"Invalid relay config: locale \\\"{0}\\\" is not supported. Falling back to {systemLocale}.\"\nmsgstr \"Invalid relay config: locale \\\"{0}\\\" is not supported. Falling back to {systemLocale}.\"\n\n#: src/core/config.ts:226\n#~ msgid \"Invalid relay config: LOCALE \\\"{0}\\\" is not supported. Falling back to en.\"\n#~ msgstr \"Invalid relay config: LOCALE \\\"{0}\\\" is not supported. Falling back to en.\"\n\n#: src/core/config.ts:257\nmsgid \"Invalid relay config: locale \\\"{normalized}\\\" is not supported. Falling back to {systemLocale}.\"\nmsgstr \"Invalid relay config: locale \\\"{normalized}\\\" is not supported. Falling back to {systemLocale}.\"\n\n#: src/core/config.ts:241\n#~ msgid \"Invalid relay config: LOCALE \\\"{normalized}\\\" is not supported. Falling back to en.\"\n#~ msgstr \"Invalid relay config: LOCALE \\\"{normalized}\\\" is not supported. Falling back to en.\"\n\n#. placeholder {0}: created.mode\n#. placeholder {0}: currentSession.mode\n#: src/bot/handler.ts:62\n#: src/bot/handler.ts:109\nmsgid \"mode: {0}\"\nmsgstr \"mode: {0}\"\n\n#. placeholder {0}: created.model\n#. placeholder {0}: currentSession.model\n#: src/bot/handler.ts:63\n#: src/bot/handler.ts:110\nmsgid \"model: {0}\"\nmsgstr \"model: {0}\"\n\n#: src/bot/handler.ts:56\n#: src/bot/handler.ts:179\nmsgid \"New Session\"\nmsgstr \"New Session\"\n\n#: src/bot/handler.ts:90\nmsgid \"No active session. Send a normal message or use /new to create one first.\"\nmsgstr \"No active session. Send a normal message or use /new to create one first.\"\n\n#: src/bot/handler.ts:52\nmsgid \"No active session. Send a normal message or use /new to create one.\"\nmsgstr \"No active session. Send a normal message or use /new to create one.\"\n\n#: src/bot/handler.ts:71\nmsgid \"No working directories are currently open.\"\nmsgstr \"No working directories are currently open.\"\n\n#: src/bot/relay.ts:66\nmsgid \"Please send a text message.\"\nmsgstr \"Please send a text message.\"\n\n#: src/core/config.ts:80\nmsgid \"Relay config is missing. Template created at {configPath}. Please edit this file and restart.\"\nmsgstr \"Relay config is missing. Template created at {configPath}. Please edit this file and restart.\"\n\n#. placeholder {0}: parsed.mode\n#: src/bot/handler.ts:98\nmsgid \"Switched to {0} mode.\"\nmsgstr \"Switched to {0} mode.\"\n\n#. placeholder {0}: created.threadId\n#. placeholder {0}: currentSession.threadId\n#: src/bot/handler.ts:60\n#: src/bot/handler.ts:107\nmsgid \"thread: {0}\"\nmsgstr \"thread: {0}\"\n\n#: src/bot/handler.ts:61\nmsgid \"title: {title}\"\nmsgstr \"title: {title}\"\n\n#. placeholder {0}: command ?? normalized\n#: src/bot/commands.ts:131\nmsgid \"\"\n\"Unknown command \\\"{0}\\\".\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"Unknown command \\\"{0}\\\".\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/handler.ts:263\n#~ msgid \"User message: {0}\"\n#~ msgstr \"User message: {0}\"\n\n#: src/bot/handler.ts:248\n#~ msgid \"\"\n#~ \"You are a session title generator.\\n\"\n#~ \"Generate a short Chinese title based on the user message.\\n\"\n#~ \"Strict requirements:\\n\"\n#~ \"1. Output title text only, with no explanation.\\n\"\n#~ \"2. Output a single line with no line breaks.\\n\"\n#~ \"3. Do not use quotes or title marks.\\n\"\n#~ \"4. Keep the title within 24 characters.\"\n#~ msgstr \"\"\n#~ \"You are a session title generator.\\n\"\n#~ \"Generate a short Chinese title based on the user message.\\n\"\n#~ \"Strict requirements:\\n\"\n#~ \"1. Output title text only, with no explanation.\\n\"\n#~ \"2. Output a single line with no line breaks.\\n\"\n#~ \"3. Do not use quotes or title marks.\\n\"\n#~ \"4. Keep the title within 24 characters.\"\n\n#: src/bot/handler.ts:255\n#~ msgid \"\"\n#~ \"You are a session title generator.\\n\"\n#~ \"Generate a short English title based on the user message.\\n\"\n#~ \"Strict requirements:\\n\"\n#~ \"1. Output title text only, with no explanation.\\n\"\n#~ \"2. Output a single line with no line breaks.\\n\"\n#~ \"3. Do not use quotes.\\n\"\n#~ \"4. Keep the title within 24 characters.\"\n#~ msgstr \"\"\n#~ \"You are a session title generator.\\n\"\n#~ \"Generate a short English title based on the user message.\\n\"\n#~ \"Strict requirements:\\n\"\n#~ \"1. Output title text only, with no explanation.\\n\"\n#~ \"2. Output a single line with no line breaks.\\n\"\n#~ \"3. Do not use quotes.\\n\"\n#~ \"4. Keep the title within 24 characters.\"\n","msgid \"\"\nmsgstr \"\"\n\"POT-Creation-Date: 2026-02-20 17:04+0800\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: @lingui/cli\\n\"\n\"Language: zh\\n\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"PO-Revision-Date: \\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: \\n\"\n\"Plural-Forms: \\n\"\n\n#. placeholder {0}: index + 1\n#: src/bot/handler.ts:75\nmsgid \"{0}. {root}\"\nmsgstr \"{0}. {root}\"\n\n#: src/bot/commands.ts:14\nmsgid \"/help - Show help\"\nmsgstr \"/help - 显示帮助\"\n\n#: src/bot/commands.ts:45\nmsgid \"\"\n\"/help does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/help 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:16\nmsgid \"/mode <default|plan> - Switch current session mode\"\nmsgstr \"/mode <default|plan> - 切换当前会话模式\"\n\n#: src/bot/commands.ts:81\nmsgid \"\"\n\"/mode requires one argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/mode 需要一个参数:default 或 plan。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:15\nmsgid \"/new [default|plan] - Create a new session\"\nmsgstr \"/new [default|plan] - 创建新会话\"\n\n#: src/bot/commands.ts:56\nmsgid \"\"\n\"/new accepts at most one optional argument: default or plan.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/new 最多接受一个可选参数:default 或 plan。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:18\nmsgid \"/projects - Show current working directories\"\nmsgstr \"/projects - 显示当前工作目录\"\n\n#: src/bot/commands.ts:111\nmsgid \"\"\n\"/projects does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/projects 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:19\nmsgid \"/reset - Clear current session\"\nmsgstr \"/reset - 清空当前会话\"\n\n#: src/bot/commands.ts:122\nmsgid \"\"\n\"/reset does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/reset 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:17\nmsgid \"/status - Show current session status\"\nmsgstr \"/status - 显示当前会话状态\"\n\n#: src/bot/commands.ts:100\nmsgid \"\"\n\"/status does not accept arguments.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"/status 不接受参数。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/commands.ts:13\nmsgid \"Available commands:\"\nmsgstr \"可用命令:\"\n\n#: src/bot/relay.ts:61\nmsgid \"Cannot identify sender. Please try again later.\"\nmsgstr \"无法识别发送者,请稍后重试。\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:146\nmsgid \"Codex execution failed: {0}\"\nmsgstr \"Codex 执行失败:{0}\"\n\n#: src/bot/handler.ts:149\nmsgid \"Codex execution failed. Please try again later.\"\nmsgstr \"Codex 执行失败,请稍后重试。\"\n\n#: src/bot/commands.ts:30\nmsgid \"\"\n\"Command cannot be empty.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"命令不能为空。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/handler.ts:106\nmsgid \"Created a new session.\"\nmsgstr \"已创建新会话。\"\n\n#: src/bot/handler.ts:85\nmsgid \"Current session has been cleared.\"\nmsgstr \"当前会话已清空。\"\n\n#: src/bot/handler.ts:59\nmsgid \"Current session status:\"\nmsgstr \"当前会话状态:\"\n\n#: src/bot/handler.ts:77\nmsgid \"Current working directories:\"\nmsgstr \"当前工作目录:\"\n\n#: src/index.ts:37\nmsgid \"Currently busy. Please try again later.\"\nmsgstr \"当前忙碌,请稍后重试。\"\n\n#. placeholder {0}: created.cwd\n#: src/bot/handler.ts:108\nmsgid \"cwd: {0}\"\nmsgstr \"cwd: {0}\"\n\n#: src/bot/relay.ts:44\n#: src/bot/relay.ts:49\nmsgid \"Failed to parse message. Please send a text message.\"\nmsgstr \"解析消息失败,请发送文本消息。\"\n\n#: src/index.ts:86\nmsgid \"Failed to process message. Please try again later.\"\nmsgstr \"处理消息失败,请稍后重试。\"\n\n#. placeholder {0}: error.message\n#: src/bot/handler.ts:154\nmsgid \"Failed to read open projects: {0}\"\nmsgstr \"读取已打开项目失败:{0}\"\n\n#: src/bot/handler.ts:157\nmsgid \"Failed to read open projects. Please try again later.\"\nmsgstr \"读取已打开项目失败,请稍后重试。\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:131\nmsgid \"Failed to read relay config at {configPath}: {0}\"\nmsgstr \"读取relay 配置失败 {configPath}:{0}\"\n\n#: src/core/startup.ts:17\n#: src/index.ts:33\nmsgid \"Failed to start relay: {message}\"\nmsgstr \"启动 relay 失败:{message}\"\n\n#. placeholder {0}: formatError(error)\n#: src/core/config.ts:140\nmsgid \"Invalid JSON in relay config at {configPath}: {0}\"\nmsgstr \"relay 配置 {configPath} 中的 JSON 无效:{0}\"\n\n#: src/bot/commands.ts:69\n#: src/bot/commands.ts:89\nmsgid \"\"\n\"Invalid mode \\\"{modeToken}\\\", only default or plan are supported.\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"无效的模式 \\\"{modeToken}\\\",仅支持 default 或 plan。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/core/config.ts:160\nmsgid \"Invalid relay config at {configPath}: env must be a JSON object.\"\nmsgstr \"relay 配置 {configPath} 无效:env 必须是 JSON 对象。\"\n\n#: src/core/config.ts:146\nmsgid \"Invalid relay config at {configPath}: root must be a JSON object.\"\nmsgstr \"relay 配置 {configPath} 无效:root 必须是 JSON 对象。\"\n\n#: src/core/config.ts:174\nmsgid \"Invalid relay config: {field} is required and must be a non-empty string.\"\nmsgstr \"relay 配置无效:{field} 为必填项且必须为非空字符串。\"\n\n#: src/core/config.ts:187\nmsgid \"Invalid relay config: {field} must be a string.\"\nmsgstr \"relay 配置无效:{field} 必须是字符串。\"\n\n#: src/core/config.ts:208\n#: src/core/config.ts:219\n#: src/core/config.ts:227\nmsgid \"Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.\"\nmsgstr \"relay 配置无效:CODEX_TIMEOUT_MS 必须是正整数。\"\n\n#. placeholder {0}: formatInvalidLocale(value)\n#: src/core/config.ts:241\nmsgid \"Invalid relay config: locale \\\"{0}\\\" is not supported. Falling back to {systemLocale}.\"\nmsgstr \"relay 配置无效:不支持的 locale \\\"{0}\\\",已回退为 {systemLocale}。\"\n\n#: src/core/config.ts:226\n#~ msgid \"Invalid relay config: LOCALE \\\"{0}\\\" is not supported. Falling back to en.\"\n#~ msgstr \"relay 配置无效:不支持的 LOCALE \\\"{0}\\\",已回退为 en。\"\n\n#: src/core/config.ts:257\nmsgid \"Invalid relay config: locale \\\"{normalized}\\\" is not supported. Falling back to {systemLocale}.\"\nmsgstr \"relay 配置无效:不支持的 locale \\\"{normalized}\\\",已回退为 {systemLocale}。\"\n\n#: src/core/config.ts:241\n#~ msgid \"Invalid relay config: LOCALE \\\"{normalized}\\\" is not supported. Falling back to en.\"\n#~ msgstr \"relay 配置无效:不支持的 LOCALE \\\"{normalized}\\\",已回退为 en。\"\n\n#. placeholder {0}: created.mode\n#. placeholder {0}: currentSession.mode\n#: src/bot/handler.ts:62\n#: src/bot/handler.ts:109\nmsgid \"mode: {0}\"\nmsgstr \"mode: {0}\"\n\n#. placeholder {0}: created.model\n#. placeholder {0}: currentSession.model\n#: src/bot/handler.ts:63\n#: src/bot/handler.ts:110\nmsgid \"model: {0}\"\nmsgstr \"model: {0}\"\n\n#: src/bot/handler.ts:56\n#: src/bot/handler.ts:179\nmsgid \"New Session\"\nmsgstr \"新会话\"\n\n#: src/bot/handler.ts:90\nmsgid \"No active session. Send a normal message or use /new to create one first.\"\nmsgstr \"没有活跃会话。请先发送普通消息或使用 /new 创建会话。\"\n\n#: src/bot/handler.ts:52\nmsgid \"No active session. Send a normal message or use /new to create one.\"\nmsgstr \"没有活跃会话。请发送普通消息或使用 /new 创建会话。\"\n\n#: src/bot/handler.ts:71\nmsgid \"No working directories are currently open.\"\nmsgstr \"当前没有打开的工作目录。\"\n\n#: src/bot/relay.ts:66\nmsgid \"Please send a text message.\"\nmsgstr \"请发送文本消息。\"\n\n#: src/core/config.ts:80\nmsgid \"Relay config is missing. Template created at {configPath}. Please edit this file and restart.\"\nmsgstr \"relay 配置缺失,已在 {configPath} 创建模板。请编辑该文件后重启。\"\n\n#. placeholder {0}: parsed.mode\n#: src/bot/handler.ts:98\nmsgid \"Switched to {0} mode.\"\nmsgstr \"已切换到 {0} 模式。\"\n\n#. placeholder {0}: created.threadId\n#. placeholder {0}: currentSession.threadId\n#: src/bot/handler.ts:60\n#: src/bot/handler.ts:107\nmsgid \"thread: {0}\"\nmsgstr \"thread: {0}\"\n\n#: src/bot/handler.ts:61\nmsgid \"title: {title}\"\nmsgstr \"title: {title}\"\n\n#. placeholder {0}: command ?? normalized\n#: src/bot/commands.ts:131\nmsgid \"\"\n\"Unknown command \\\"{0}\\\".\\n\"\n\"\\n\"\n\"{helpText}\"\nmsgstr \"\"\n\"未知命令 \\\"{0}\\\"。\\n\"\n\"\\n\"\n\"{helpText}\"\n\n#: src/bot/handler.ts:263\n#~ msgid \"User message: {0}\"\n#~ msgstr \"用户消息:{0}\"\n\n#: src/bot/handler.ts:248\n#~ msgid \"\"\n#~ \"You are a session title generator.\\n\"\n#~ \"Generate a short Chinese title based on the user message.\\n\"\n#~ \"Strict requirements:\\n\"\n#~ \"1. Output title text only, with no explanation.\\n\"\n#~ \"2. Output a single line with no line breaks.\\n\"\n#~ \"3. Do not use quotes or title marks.\\n\"\n#~ \"4. Keep the title within 24 characters.\"\n#~ msgstr \"\"\n#~ \"你是会话标题生成器。\\n\"\n#~ \"根据用户消息生成简短中文标题。\\n\"\n#~ \"严格要求:\\n\"\n#~ \"1. 仅输出标题文字,不要解释。\\n\"\n#~ \"2. 单行输出,不要换行。\\n\"\n#~ \"3. 不要使用引号或书名号。\\n\"\n#~ \"4. 标题不超过 24 个字符。\"\n\n#: src/bot/handler.ts:255\n#~ msgid \"\"\n#~ \"You are a session title generator.\\n\"\n#~ \"Generate a short English title based on the user message.\\n\"\n#~ \"Strict requirements:\\n\"\n#~ \"1. Output title text only, with no explanation.\\n\"\n#~ \"2. Output a single line with no line breaks.\\n\"\n#~ \"3. Do not use quotes.\\n\"\n#~ \"4. Keep the title within 24 characters.\"\n#~ msgstr \"\"\n#~ \"你是会话标题生成器。\\n\"\n#~ \"根据用户消息生成简短英文标题。\\n\"\n#~ \"严格要求:\\n\"\n#~ \"1. 仅输出标题文字,不要解释。\\n\"\n#~ \"2. 单行输出,不要换行。\\n\"\n#~ \"3. 不要使用引号。\\n\"\n#~ \"4. 标题不超过 24 个字符。\"\n","import { i18n } from '@lingui/core'\nimport { messages as enMessages } from '../locales/en/messages.po'\nimport { messages as zhMessages } from '../locales/zh/messages.po'\nimport type { Messages } from '@lingui/core'\n\nexport type AppLocale = 'en' | 'zh'\n\nconst DEFAULT_LOCALE: AppLocale = detectDefaultLocale()\n\nconst CATALOGS: Record<AppLocale, Messages> = {\n en: enMessages as Messages,\n zh: zhMessages as Messages,\n}\n\nlet activeLocale: AppLocale | null = null\n\n// Activate a default locale eagerly so top-level `t` calls in imported modules\n// never run before Lingui has an active locale.\ninitializeI18n(DEFAULT_LOCALE)\n\nexport function initializeI18n(locale?: string): AppLocale {\n const resolved = resolveLocale(locale)\n i18n.loadAndActivate({\n locale: resolved,\n messages: CATALOGS[resolved],\n })\n activeLocale = resolved\n return resolved\n}\n\nexport function getCurrentLocale(): AppLocale {\n ensureI18nInitialized()\n return activeLocale ?? DEFAULT_LOCALE\n}\n\nexport function isSupportedLocale(locale: string): locale is AppLocale {\n return locale === 'en' || locale === 'zh'\n}\n\nexport function getDefaultLocale(): AppLocale {\n return DEFAULT_LOCALE\n}\n\nfunction resolveLocale(locale?: string): AppLocale {\n if (!locale) {\n return DEFAULT_LOCALE\n }\n\n const mappedLocale = mapToAppLocale(locale)\n if (mappedLocale) {\n return mappedLocale\n }\n\n return DEFAULT_LOCALE\n}\n\nfunction ensureI18nInitialized(): void {\n if (!activeLocale) {\n initializeI18n(DEFAULT_LOCALE)\n }\n}\n\nfunction detectDefaultLocale(): AppLocale {\n const systemLocale = readSystemLocale()\n if (!systemLocale) {\n return 'en'\n }\n\n return mapToAppLocale(systemLocale) ?? 'en'\n}\n\nfunction readSystemLocale(): string | undefined {\n const locale = Intl.DateTimeFormat().resolvedOptions().locale\n if (typeof locale !== 'string') {\n return undefined\n }\n\n const normalized = locale.trim()\n if (normalized.length === 0) {\n return undefined\n }\n\n return normalized\n}\n\nfunction mapToAppLocale(locale: string): AppLocale | null {\n const normalized = locale.trim().toLowerCase().replaceAll('_', '-')\n\n if (normalized === 'zh' || normalized.startsWith('zh-')) {\n return 'zh'\n }\n\n if (normalized === 'en' || normalized.startsWith('en-')) {\n return 'en'\n }\n\n return null\n}\n","import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { t } from '@lingui/core/macro'\nimport {\n getDefaultLocale,\n initializeI18n,\n isSupportedLocale,\n} from '../i18n/runtime'\nimport type { AppLocale } from '../i18n/runtime'\n\nconst DEFAULT_CODEX_BIN = 'codex'\n\nconst TEMPLATE_ENV_CONFIG: Required<RelayConfigEnv> = {\n BASE_DOMAIN: 'https://open.feishu.cn',\n APP_ID: 'your_app_id',\n APP_SECRET: 'your_app_secret',\n BOT_OPEN_ID: 'ou_xxx',\n CODEX_BIN: DEFAULT_CODEX_BIN,\n CODEX_TIMEOUT_MS: null,\n}\n\nconst TEMPLATE_CONFIG: {\n locale?: AppLocale\n env: Required<RelayConfigEnv>\n} = {\n env: TEMPLATE_ENV_CONFIG,\n}\n\nexport interface RelayConfigEnv {\n BASE_DOMAIN?: string\n APP_ID?: string\n APP_SECRET?: string\n BOT_OPEN_ID?: string\n CODEX_BIN?: string\n CODEX_TIMEOUT_MS?: number | string | null\n}\n\ninterface RelayConfigFile extends RelayConfigEnv {\n locale?: string\n env?: RelayConfigEnv\n}\n\ninterface ParsedRelayConfig {\n env: RelayConfigEnv\n localeValue: unknown\n}\n\nexport interface RelayConfig {\n baseConfig: {\n appId: string\n appSecret: string\n domain: string\n }\n homeDir: string\n botOpenId?: string\n codexBin: string\n codexTimeoutMs?: number\n workspaceCwd: string\n locale: AppLocale\n}\n\nexport interface LoadRelayConfigOptions {\n homeDir?: string\n workspaceCwd?: string\n}\n\nexport function loadRelayConfig(\n options: LoadRelayConfigOptions = {},\n): RelayConfig {\n const homeDir = options.homeDir ?? os.homedir()\n const workspaceCwd = options.workspaceCwd ?? process.cwd()\n const configDir = path.join(homeDir, '.relay')\n const configPath = path.join(configDir, 'config.json')\n\n if (!fs.existsSync(configPath)) {\n ensureConfigTemplate(configDir, configPath)\n throw new Error(\n t`Relay config is missing. Template created at ${configPath}. Please edit this file and restart.`,\n )\n }\n\n const parsed = parseConfigFile(configPath)\n const locale = readLocale(parsed.localeValue)\n initializeI18n(locale)\n\n const domain = readRequiredString(parsed.env.BASE_DOMAIN, 'BASE_DOMAIN')\n const appId = readRequiredString(parsed.env.APP_ID, 'APP_ID')\n const appSecret = readRequiredString(parsed.env.APP_SECRET, 'APP_SECRET')\n\n return {\n baseConfig: {\n appId,\n appSecret,\n domain,\n },\n homeDir,\n botOpenId: readOptionalString(parsed.env.BOT_OPEN_ID, 'BOT_OPEN_ID'),\n codexBin:\n readOptionalString(parsed.env.CODEX_BIN, 'CODEX_BIN') ??\n DEFAULT_CODEX_BIN,\n codexTimeoutMs: readTimeoutMs(parsed.env.CODEX_TIMEOUT_MS),\n workspaceCwd,\n locale,\n }\n}\n\nfunction ensureConfigTemplate(configDir: string, configPath: string): void {\n fs.mkdirSync(configDir, { recursive: true })\n if (fs.existsSync(configPath)) {\n return\n }\n\n fs.writeFileSync(\n configPath,\n `${JSON.stringify(TEMPLATE_CONFIG, null, 2)}\\n`,\n {\n encoding: 'utf-8',\n flag: 'wx',\n },\n )\n}\n\nfunction parseConfigFile(configPath: string): ParsedRelayConfig {\n let raw: string\n try {\n raw = fs.readFileSync(configPath, 'utf-8')\n } catch (error) {\n throw new Error(\n t`Failed to read relay config at ${configPath}: ${formatError(error)}`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n throw new Error(\n t`Invalid JSON in relay config at ${configPath}: ${formatError(error)}`,\n )\n }\n\n if (!isObject(parsed)) {\n throw new Error(\n t`Invalid relay config at ${configPath}: root must be a JSON object.`,\n )\n }\n\n const configObject = parsed as RelayConfigFile\n if (configObject.env === undefined) {\n return {\n env: configObject,\n localeValue: configObject.locale,\n }\n }\n\n if (!isObject(configObject.env)) {\n throw new Error(\n t`Invalid relay config at ${configPath}: env must be a JSON object.`,\n )\n }\n\n return {\n env: configObject.env,\n localeValue: configObject.locale,\n }\n}\n\nfunction readRequiredString(value: unknown, field: string): string {\n const normalized = readOptionalString(value, field)\n if (!normalized) {\n throw new Error(\n t`Invalid relay config: ${field} is required and must be a non-empty string.`,\n )\n }\n\n return normalized\n}\n\nfunction readOptionalString(value: unknown, field: string): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n if (typeof value !== 'string') {\n throw new TypeError(t`Invalid relay config: ${field} must be a string.`)\n }\n\n const normalized = value.trim()\n if (normalized.length === 0) {\n return undefined\n }\n\n return normalized\n}\n\nfunction readTimeoutMs(value: unknown): number | undefined {\n if (value === undefined || value === null) {\n return undefined\n }\n\n if (typeof value === 'number') {\n if (Number.isInteger(value) && value > 0) {\n return value\n }\n throw new Error(\n t`Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.`,\n )\n }\n\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length === 0) {\n return undefined\n }\n if (!/^[1-9]\\d*$/.test(trimmed)) {\n throw new Error(\n t`Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.`,\n )\n }\n\n return Number.parseInt(trimmed, 10)\n }\n\n throw new Error(\n t`Invalid relay config: CODEX_TIMEOUT_MS must be a positive integer.`,\n )\n}\n\nfunction readLocale(value: unknown): AppLocale {\n // When locale is missing in config, use the system-detected default locale.\n const systemLocale = getDefaultLocale()\n\n if (value === undefined || value === null) {\n return systemLocale\n }\n\n if (typeof value !== 'string') {\n console.warn(\n t`Invalid relay config: locale \"${formatInvalidLocale(value)}\" is not supported. Falling back to ${systemLocale}.`,\n )\n return systemLocale\n }\n\n const normalized = value.trim()\n if (normalized.length === 0) {\n return systemLocale\n }\n\n const mapped = mapLocaleToAppLocale(normalized)\n if (mapped) {\n return mapped\n }\n\n console.warn(\n t`Invalid relay config: locale \"${normalized}\" is not supported. Falling back to ${systemLocale}.`,\n )\n\n return systemLocale\n}\n\nfunction formatInvalidLocale(value: unknown): string {\n if (typeof value === 'string') {\n return value\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction mapLocaleToAppLocale(value: string): AppLocale | null {\n if (isSupportedLocale(value)) {\n return value\n }\n\n const normalized = value.toLowerCase().replaceAll('_', '-')\n if (normalized === 'zh' || normalized.startsWith('zh-')) {\n return 'zh'\n }\n\n if (normalized === 'en' || normalized.startsWith('en-')) {\n return 'en'\n }\n\n return null\n}\n\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return error.message\n }\n\n return String(error)\n}\n","import process from 'node:process'\nimport { t } from '@lingui/core/macro'\nimport { loadRelayConfig } from './config'\nimport type { RelayConfig } from './config'\n\nexport function loadConfigOrExit(): RelayConfig {\n try {\n return loadRelayConfig()\n } catch (error) {\n console.error(formatStartupError(error))\n process.exit(1)\n }\n}\n\nfunction formatStartupError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error)\n return t`Failed to start relay: ${message}`\n}\n","import { resolveSenderId } from '../bot/message-filter'\nimport { getSession, getSessionKey } from '../session/store'\nimport type * as Lark from '@larksuiteoapi/node-sdk'\nimport type { ReceiveMessageEvent } from '../bot/relay'\n\nconst FALLBACK_REPLY_TAG = 'no-thread'\n\nexport interface SendReplyOptions {\n includeThreadTag?: boolean\n}\n\nexport interface FeishuReceiveMessageEvent extends ReceiveMessageEvent {\n event_id?: string\n message: ReceiveMessageEvent['message'] & {\n message_id: string\n }\n}\n\nexport async function sendReply(\n larkClient: Lark.Client,\n data: FeishuReceiveMessageEvent,\n text: string,\n options?: SendReplyOptions,\n): Promise<void> {\n const content = JSON.stringify({\n text: formatReplyTextWithThreadId(data, text, options),\n })\n\n if (data.message.chat_type === 'p2p') {\n await larkClient.im.v1.message.create({\n params: {\n receive_id_type: 'chat_id',\n },\n data: {\n receive_id: data.message.chat_id,\n msg_type: 'text',\n content,\n },\n })\n return\n }\n\n await larkClient.im.v1.message.reply({\n path: {\n message_id: data.message.message_id,\n },\n data: {\n msg_type: 'text',\n content,\n },\n })\n}\n\nfunction formatReplyTextWithThreadId(\n data: FeishuReceiveMessageEvent,\n text: string,\n options?: SendReplyOptions,\n): string {\n if (!options?.includeThreadTag) {\n return text.trim()\n }\n\n const replyTag = resolveReplyTag(data)\n const normalizedText = text.trim()\n if (normalizedText.length === 0) {\n return `${replyTag}\\n`\n }\n\n return `${replyTag}\\n\\n${normalizedText}`\n}\n\nfunction resolveReplyTag(data: FeishuReceiveMessageEvent): string {\n const senderId = resolveSenderId(data.sender.sender_id)\n if (!senderId) {\n return FALLBACK_REPLY_TAG\n }\n\n const sessionKey = getSessionKey({\n chatType: data.message.chat_type,\n chatId: data.message.chat_id,\n userId: senderId,\n })\n const session = getSession(sessionKey)\n if (!session || session.threadId.trim().length === 0) {\n return FALLBACK_REPLY_TAG\n }\n\n return session.threadId\n}\n","import process from 'node:process'\nimport * as Lark from '@larksuiteoapi/node-sdk'\nimport { t } from '@lingui/core/macro'\nimport { isPlainObject } from 'es-toolkit/predicate'\nimport { parseCommand } from './bot/commands'\nimport { handleIncomingText } from './bot/handler'\nimport { shouldProcessMessage } from './bot/message-filter'\nimport { buildReplyForMessageEvent, stripMentionTags } from './bot/relay'\nimport { createCodexThread, runCodexTurn } from './codex/app-server'\nimport { listOpenProjects } from './codex/state'\nimport { loadConfigOrExit } from './core/startup'\nimport { sendReply } from './feishu/reply'\nimport { initializeI18n } from './i18n/runtime'\nimport {\n clearSession,\n getSession,\n initializeSessionStore,\n setSession,\n withSessionLock,\n} from './session/store'\nimport type { FeishuReceiveMessageEvent } from './feishu/reply'\n\nconst relayConfig = loadConfigOrExit()\ninitializeI18n(relayConfig.locale)\n\ntry {\n initializeSessionStore({\n homeDir: relayConfig.homeDir,\n workspaceCwd: relayConfig.workspaceCwd,\n })\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.error(t`Failed to start relay: ${message}`)\n process.exit(1)\n}\n\nconst BUSY_MESSAGE = t`Currently busy. Please try again later.`\n\nconst client = new Lark.Client(relayConfig.baseConfig)\nconst wsClient = new Lark.WSClient(relayConfig.baseConfig)\nlet isTaskRunning = false\n\nasync function processIncomingEvent(\n data: FeishuReceiveMessageEvent,\n): Promise<void> {\n try {\n const reply = await buildReplyForMessageEvent(data, {\n botOpenId: relayConfig.botOpenId,\n handleIncomingText: (input) =>\n handleIncomingText(input, {\n createThread: (mode) =>\n createCodexThread({\n mode,\n cwd: relayConfig.workspaceCwd,\n codexBin: relayConfig.codexBin,\n timeoutMs: relayConfig.codexTimeoutMs,\n }),\n runTurn: (params) =>\n runCodexTurn({\n ...params,\n cwd: relayConfig.workspaceCwd,\n codexBin: relayConfig.codexBin,\n timeoutMs: relayConfig.codexTimeoutMs,\n }),\n getSession,\n setSession,\n clearSession,\n withSessionLock,\n listOpenProjects,\n }),\n })\n\n if (reply === null) {\n return\n }\n\n await sendReply(client, data, reply, {\n includeThreadTag: shouldAttachThreadTag(data),\n })\n } catch (error) {\n console.error('failed to handle Feishu message', error)\n try {\n await sendReply(\n client,\n data,\n t`Failed to process message. Please try again later.`,\n )\n } catch (replyError) {\n console.error('failed to send failure message', replyError)\n }\n }\n}\n\nfunction shouldAttachThreadTag(data: FeishuReceiveMessageEvent): boolean {\n const rawText = parseEventText(data.message.content)\n if (rawText === null) {\n return false\n }\n\n const normalizedText = stripMentionTags(rawText).trim()\n if (normalizedText.length === 0) {\n return false\n }\n\n return parseCommand(normalizedText).type === 'prompt'\n}\n\nfunction parseEventText(content: string): string | null {\n try {\n const parsed: unknown = JSON.parse(content)\n if (!isPlainObject(parsed)) {\n return null\n }\n\n return typeof parsed.text === 'string' ? parsed.text : null\n } catch {\n return null\n }\n}\n\nconst eventDispatcher = new Lark.EventDispatcher({}).register({\n 'im.message.receive_v1': async (data: FeishuReceiveMessageEvent) => {\n // eslint-disable-next-line no-console\n console.info(\n 'feishu message received\\n',\n JSON.stringify(data, null, 2),\n '\\n',\n )\n\n if (!shouldProcessMessage(data, relayConfig.botOpenId)) {\n return\n }\n\n if (isTaskRunning) {\n void sendReply(client, data, BUSY_MESSAGE)\n return\n }\n\n isTaskRunning = true\n void processIncomingEvent(data).finally(() => {\n isTaskRunning = false\n })\n },\n})\n\nwsClient.start({ eventDispatcher })\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@debbl/relay",
3
3
  "type": "module",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "description": "relay",
6
6
  "author": "Brendan Dash <me@aiwan.run> (https://aiwan.run)",
7
7
  "license": "MIT",