@leviyuan/lodestar 0.3.13 → 0.4.1

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/README.md CHANGED
@@ -8,66 +8,13 @@ AI 不是帮手,是倍率。它放大的不是体力,是你 —— 你的直觉
8
8
 
9
9
  夜航星让这件事真正发生:在你思考的地方接住想法,在你转身之后继续推向终点。
10
10
 
11
- ---
12
-
13
- ## 功能
14
-
15
- <table>
16
- <tr>
17
- <td width="50%" valign="top">
18
-
19
- **🌊 流式打字机**
20
-
21
- 回复在同一张卡片上逐字刷新,不会刷出一长串新消息。
22
-
23
- </td>
24
- <td width="50%" valign="top">
25
-
26
- **🔧 工具调用逐步可见**
27
-
28
- 每次调用单独一个折叠面板,需要授权时直接在卡片上点按钮。
29
-
30
- </td>
31
- </tr>
32
- <tr>
33
- <td valign="top">
34
-
35
- **💬 能反问也能插话**
36
-
37
- Claude 会反问,你也能中途打断,没处理完的消息自动排到下一轮。
38
-
39
- </td>
40
- <td valign="top">
41
-
42
- **📊 这一轮花了多少**
43
-
44
- 耗时、上下文用量、对应花费,都显示在卡片底部。
45
-
46
- </td>
47
- </tr>
48
- <tr>
49
- <td valign="top">
50
-
51
- **📦 一屏看完所有项目**
52
-
53
- 发个 `hi`,跨群、跨项目的状态收进一张卡片。
54
-
55
- </td>
56
- <td valign="top">
57
-
58
- **🛡 重启不丢上下文**
59
-
60
- 多个项目并发跑,daemon 重启后自动接回上次会话,关键节点推送到锁屏。
61
-
62
- </td>
63
- </tr>
64
- </table>
11
+ `您赛博办公的最佳实践`
65
12
 
66
13
  ---
67
14
 
68
15
  ## 🚀 用起来
69
16
 
70
- 跨平台 (Windows / macOS / Linux),Node18
17
+ 跨平台 (Windows / macOS / Linux),运行时需要 Bun 1.0;npm 只用于全局安装包和 Codex CLI
71
18
 
72
19
  **1. 装包**
73
20
 
@@ -75,14 +22,15 @@ Claude 会反问,你也能中途打断,没处理完的消息自动排到下一
75
22
  npm i -g @leviyuan/lodestar
76
23
  ```
77
24
 
78
- 装完得到 4 个命令:
25
+ 装完得到 5 个命令:
79
26
 
80
27
  | 命令 | 作用 |
81
28
  | --- | --- |
82
29
  | `lodestar-setup` | 首次配置向导 |
83
30
  | `lodestar-daemon` | 启动 daemon |
84
31
  | `lodestar-stop` | 停止 daemon |
85
- | `lodestar-update` | 升级到最新版(含 Claude CLI)|
32
+ | `lodestar-update` | 升级到最新版(含 Codex CLI)|
33
+ | `lodestar-version` | 查看 Lodestar 和 Codex CLI 版本 |
86
34
 
87
35
  **2. 跑向导**
88
36
 
@@ -90,11 +38,11 @@ npm i -g @leviyuan/lodestar
90
38
  lodestar-setup
91
39
  ```
92
40
 
93
- 手把手带你装 claude、配 API、建飞书应用、启动 lodestar。
41
+ 手把手带你装 Codex、登录 ChatGPT、建飞书应用、启动 lodestar。
94
42
 
95
43
  **3. 拉机器人进群**
96
44
 
97
- 群名 = `projects_root` 下的目录名(没建会自动建)。发条消息,Claude 接管。
45
+ 群名 = `projects_root` 下的目录名(没建会自动建)。发条消息,Codex 接管。
98
46
 
99
47
  群里发这五个**裸词**(不要斜杠,大小写不敏感)可以控 daemon:
100
48
 
@@ -102,9 +50,9 @@ lodestar-setup
102
50
  | --- | --- |
103
51
  | `hi` | 未运行时启动;运行中弹一张状态卡片 |
104
52
  | `stop` | 软打断当前 turn,子进程保活,排队消息打 ❌ |
105
- | `kill` | 优雅关闭 Claude 进程,`sessionId` 落盘 |
106
- | `restart` | 用上次 `sessionId` 重启(保留上下文)|
107
- | `clear` | 杀进程并开新 session(等价 `/clear`)|
53
+ | `kill` | 优雅关闭 Codex 进程,`threadId` 落盘 |
54
+ | `restart` | 用上次 `threadId` 重启(保留上下文)|
55
+ | `clear` | 杀进程并开新 thread(等价 `/clear`)|
108
56
 
109
57
  ---
110
58
 
@@ -121,7 +69,7 @@ curl -X POST http://127.0.0.1:9876/notify \
121
69
 
122
70
  ### ⏰ 定时任务
123
71
 
124
- 在群里跟 Claude 说"每天早上 9 点总结昨天 PR",它自己排好。每次 fire 起一个干净的 Claude 子进程跑,不累上下文,silent / verbose 二选一,`hi` 面板带删/切按钮。
72
+ 在群里跟 Codex 说"每天早上 9 点总结昨天 PR",它自己排好。每次 fire 起一个干净的 Codex 线程跑,不累上下文,silent / verbose 二选一,`hi` 面板带删/切按钮。
125
73
 
126
74
  ---
127
75
 
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as M}from"node:child_process";import{existsSync as G,mkdirSync as F,readFileSync as h,writeFileSync as k}from"node:fs";import{homedir as c}from"node:os";import{createInterface as l}from"node:readline/promises";import{delimiter as p,dirname as d,join as N}from"node:path";import{fileURLToPath as r}from"node:url";import{homedir as m}from"node:os";import{join as Y}from"node:path";var V=m(),O=process.platform==="win32";function R($,q,Q){if($)return $;if(q)return Y(q,"lodestar");return Q}function D(){if(O)return Y(process.env.APPDATA??Y(V,"AppData","Roaming"),"Lodestar");return Y(V,".config","lodestar")}function I(){if(O)return Y(process.env.LOCALAPPDATA??Y(V,"AppData","Local"),"Lodestar");return Y(V,".local","share","lodestar")}var P=R(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,D()),b=R(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,I()),L=process.env.LODESTAR_CONFIG??Y(P,"config.toml"),zz=Y(b,"daemon.pid"),$z=Y(b,"daemon.log"),qz=Y(b,"session-chat-map.json"),Jz=Y(b,"session-resume-map.json"),Qz=Y(b,"schedules.json"),Yz=Y(b,"alive-on-shutdown.json"),Zz=Y(b,"inbox"),bz=Y(b,"debug.sock"),Xz=Y(b,"debug-context.json");var z={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m",red:"\x1B[31m"},K=l({input:process.stdin,output:process.stdout});async function f($,q={}){while(!0){let Q=q.default?`${z.dim} [${q.default}]${z.reset}`:"",J=(await K.question(`${z.cyan}? ${z.reset}${$}${Q}
3
- ${z.green}>${z.reset} `)).trim();if(!J&&q.default!==void 0)return q.default;if(!J&&q.required){console.log(`${z.red}必填,请重新输入${z.reset}`);continue}return J}}async function o($,q){while(!0){console.log(`${z.cyan}? ${z.reset}${$}`);for(let Z=0;Z<q.length;Z++)console.log(` ${z.bold}[${Z+1}]${z.reset} ${q[Z].label}`);let Q=(await K.question(`${z.green}选择 [1-${q.length}]:${z.reset} `)).trim(),J=parseInt(Q,10)-1;if(J>=0&&J<q.length)return q[J].value;console.log(`${z.red}无效选择${z.reset}`)}}function a($){let q="═".repeat(58);console.log(`
2
+ import{execSync as B,spawn as b}from"node:child_process";import{existsSync as g,mkdirSync as A,writeFileSync as _}from"node:fs";import{createInterface as j}from"node:readline/promises";import{delimiter as m,dirname as x,join as w}from"node:path";import{fileURLToPath as h}from"node:url";import{homedir as S}from"node:os";import{join as Q}from"node:path";var f=S(),L=process.platform==="win32";function G($,q,J){if($)return $;if(q)return Q(q,"lodestar");return J}function v(){if(L)return Q(process.env.APPDATA??Q(f,"AppData","Roaming"),"Lodestar");return Q(f,".config","lodestar")}function T(){if(L)return Q(process.env.LOCALAPPDATA??Q(f,"AppData","Local"),"Lodestar");return Q(f,".local","share","lodestar")}var N=G(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,v()),Y=G(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,T()),W=process.env.LODESTAR_CONFIG??Q(N,"config.toml"),d=Q(Y,"daemon.pid"),o=Q(Y,"daemon.log"),i=Q(Y,"session-chat-map.json"),n=Q(Y,"session-resume-map.json"),e=Q(Y,"session-context-baselines.json"),C=Q(Y,"schedules.json"),zz=Q(Y,"alive-on-shutdown.json"),$z=Q(Y,"inbox"),qz=Q(Y,"debug.sock"),Jz=Q(Y,"debug-context.json");var z={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m",red:"\x1B[31m"},Z=j({input:process.stdin,output:process.stdout});async function V($,q={}){while(!0){let J=q.default?`${z.dim} [${q.default}]${z.reset}`:"",K=(await Z.question(`${z.cyan}? ${z.reset}${$}${J}
3
+ ${z.green}>${z.reset} `)).trim();if(!K&&q.default!==void 0)return q.default;if(!K&&q.required){console.log(`${z.red}必填,请重新输入${z.reset}`);continue}return K}}function D($){let q="═".repeat(58);console.log(`
4
4
  ${z.bold}${z.cyan}╔${q}╗${z.reset}`),console.log(`${z.bold}${z.cyan}║ ${$.padEnd(54)} ║${z.reset}`),console.log(`${z.bold}${z.cyan}╚${q}╝${z.reset}
5
- `)}function y($,q,Q){console.log(`
6
- ${z.bold}${z.yellow}[${$}/${q}] ${Q}${z.reset}
7
- `)}function B($){return $.replace(/\\/g,"\\\\").replace(/"/g,"\\\"")}function s($){let q=N(c(),".claude"),Q=N(q,"settings.json");try{F(q,{recursive:!0});let J={};if(G(Q))try{J=JSON.parse(h(Q,"utf8"))}catch{J={}}if(!J.env||typeof J.env!=="object")J.env={};for(let[Z,W]of Object.entries($))J.env[Z]=W;return k(Q,JSON.stringify(J,null,2)+`
8
- `,{mode:384}),{path:Q,ok:!0}}catch(J){return{path:Q,ok:!1,error:J?.message??String(J)}}}function T($){let q=process.env.PATH??"";if(!q)return null;let Q=process.platform==="win32"?[`${$}.cmd`,`${$}.bat`,`${$}.exe`,$]:[$];for(let J of q.split(p)){if(!J)continue;for(let Z of Q){let W=N(J,Z);if(G(W))return W}}return null}async function i(){let $=process.platform==="win32"?"npm.cmd":"npm";return new Promise((q)=>{let Q=M($,["install","-g","@anthropic-ai/claude-code"],{stdio:"inherit",shell:process.platform==="win32"});Q.on("exit",(J)=>q(J===0)),Q.on("error",()=>q(!1))})}function g($){try{if(process.platform==="win32")M(process.env.ComSpec??"cmd.exe",["/c","start",'""',$],{detached:!0,stdio:"ignore",windowsHide:!0}).unref();else if(process.platform==="darwin")M("open",[$],{detached:!0,stdio:"ignore"}).unref();else M("xdg-open",[$],{detached:!0,stdio:"ignore"}).unref()}catch{}}async function t($,q){try{let J=await(await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({app_id:$,app_secret:q})})).json();if(J.tenant_access_token)return{ok:!0};return{ok:!1,error:`飞书拒绝: code=${J.code} msg=${J.msg??"(no msg)"}`}}catch(Q){return{ok:!1,error:`网络错误: ${Q?.message??String(Q)}`}}}function n(){try{let $=d(r(import.meta.url)),q=N($,"lodestar.js");if(!G(q))return{error:`找不到 daemon bundle: ${q}`};let Q=M(process.execPath,[q],{detached:!0,stdio:"ignore",windowsHide:!0});return Q.unref(),{pid:Q.pid}}catch($){return{error:$?.message??String($)}}}async function S(){if(!process.stdin.isTTY)console.error(`${z.red}lodestar-setup: stdin 不是 TTY,无法交互式输入。${z.reset}`),console.error("请直接在 cmd / PowerShell / Terminal 里跑,不要 pipe 或重定向 stdin。"),process.exit(1);if(G(L)){if(console.log(`${z.yellow}发现已有配置: ${L}${z.reset}`),(await f("覆盖? (y/N)",{default:"n"})).toLowerCase()!=="y"){console.log("已取消"),K.close();return}}a("Lodestar 安装向导"),console.log("Lodestar 把 Feishu (飞书) 群聊接到 Claude Code。"),console.log("每个群对应一个项目目录, Claude 在那里跑、能读写文件。"),console.log(),console.log("本向导依次做 4 件事:"),console.log(` ${z.dim}1) 确保 claude CLI 已装好${z.reset}`),console.log(` ${z.dim}2) 选 LLM 后端 (订阅 / API key / DeepSeek)${z.reset}`),console.log(` ${z.dim}3) Feishu 自建应用 (含权限 / 事件 / 发版 + 凭据测试)${z.reset}`),console.log(` ${z.dim}4) 工作目录, 自动启动 daemon${z.reset}`),console.log(),await K.question(`${z.dim}按 Enter 开始 (Ctrl+C 退出)...${z.reset}`),y(1,4,"准备 Claude Code");let $=T("claude");if($)console.log(`${z.green}✓ claude CLI 已就位${z.reset}: ${z.dim}${$}${z.reset}`);else{if(console.log(`${z.yellow}未在 PATH 找到 claude CLI, 自动安装...${z.reset}`),console.log(`${z.dim}运行: npm install -g @anthropic-ai/claude-code${z.reset}`),console.log(),!await i())console.error(`
9
- ${z.red}安装失败。${z.reset}`),console.error("请手动运行后再开向导:"),console.error(` ${z.cyan}npm install -g @anthropic-ai/claude-code${z.reset}`),console.error(` ${z.cyan}lodestar-setup${z.reset}`),K.close(),process.exit(1);$=T("claude"),console.log(`${z.green}✓ 安装完成${z.reset}: ${z.dim}${$??"(应该装好了, 但 PATH 找不到 — 重开终端再试)"}${z.reset}`)}if(y(2,4,"LLM 后端"),console.log("claude CLI 默认走 Anthropic 官方, 需要 claude.ai 订阅或 API key。"),console.log("国内访问 anthropic.com 不一定通, 也可以让 claude 走 DeepSeek 后端。"),console.log(),await o("你的 claude 怎么走?",[{label:"已经配过 (订阅 / API key / 已设环境变量), 直接用",value:"existing"},{label:`${z.bold}用 DeepSeek${z.reset}${z.dim} (国内可用, 人民币计费, 推荐)${z.reset}`,value:"deepseek"}])==="deepseek"){console.log(),console.log("打开浏览器拿 DeepSeek API key:");let X="https://platform.deepseek.com/api_keys";console.log(` ${z.cyan}${X}${z.reset}`),g(X),console.log(`${z.dim}(如果浏览器没自动开, 复制上面 URL 粘到浏览器)${z.reset}`),console.log();let x={ANTHROPIC_BASE_URL:"https://api.deepseek.com/anthropic",ANTHROPIC_AUTH_TOKEN:await f("DeepSeek API key (以 sk- 开头)",{required:!0}),ANTHROPIC_MODEL:"deepseek-v4-pro",ANTHROPIC_DEFAULT_OPUS_MODEL:"deepseek-v4-pro",ANTHROPIC_DEFAULT_SONNET_MODEL:"deepseek-v4-pro",ANTHROPIC_DEFAULT_HAIKU_MODEL:"deepseek-v4-flash",CLAUDE_CODE_SUBAGENT_MODEL:"deepseek-v4-flash",CLAUDE_CODE_EFFORT_LEVEL:"max"};console.log();let H=s(x);if(H.ok)console.log(`${z.green}✓ DeepSeek 配置已写入 Claude 配置文件${z.reset}`),console.log(` ${z.cyan}${H.path}${z.reset} ${z.dim}("env" 字段, claude 启动自读)${z.reset}`);else{console.log(`${z.red}✗ 写入 ${H.path} 失败: ${H.error}${z.reset}`),console.log(`${z.yellow}请手动把下面这些键加到该文件的 "env" 对象里:${z.reset}`);for(let[E,j]of Object.entries(x))console.log(` ${z.dim}"${E}": "${E==="ANTHROPIC_AUTH_TOKEN"?"<你的 DeepSeek key>":j}"${z.reset}`)}}else console.log(`${z.dim}OK, 跳过 LLM 后端配置 — daemon 启动时会继承当前环境 + claude 自带 auth。${z.reset}`);y(3,4,"Feishu 自建应用");let Q="https://open.feishu.cn/app";console.log("打开飞书开放平台 (浏览器):"),console.log(` ${z.cyan}${Q}${z.reset}`),g(Q),console.log(`${z.dim}(如果浏览器没自动开, 复制上面 URL 粘到浏览器)${z.reset}`),console.log(),console.log(`${z.bold}详细操作步骤:${z.reset}`),console.log(),console.log(` ${z.bold}① 创建应用${z.reset}`),console.log(` 点 "创建企业自建应用", 填名字 (如 ${z.dim}Lodestar${z.reset}), logo 随意。`),console.log(),console.log(` ${z.bold}② 添加机器人能力${z.reset}`),console.log(` 左侧菜单 "${z.cyan}添加应用能力${z.reset}" → 找到 "机器人" → 点 "${z.bold}添加${z.reset}" 按钮。`),console.log(),console.log(` ${z.bold}③ 申请权限 (左侧 "${z.cyan}权限管理${z.reset}" → "${z.bold}开通权限${z.reset}")${z.reset}`),console.log(` ${z.yellow}缺一个都会让 daemon 启动后默默丢消息, 一定要全开。${z.reset}`),console.log(` ${z.dim}消息类:${z.reset}`),console.log(` • ${z.bold}im:message:send_as_bot${z.reset} ${z.dim}# 以机器人身份发消息${z.reset}`),console.log(` • ${z.bold}im:message${z.reset} ${z.dim}# 接收/操作消息 (核心)${z.reset}`),console.log(` • ${z.bold}im:chat${z.reset} ${z.dim}# 读/写群信息 (匹配群名 ↔ 项目目录)${z.reset}`),console.log(` • ${z.bold}im:resource${z.reset} ${z.dim}# 上传/下载附件 (图文双向)${z.reset}`),console.log(` • ${z.bold}im:message.urgent${z.reset} ${z.dim}# 加急推送 (锁屏通知 / Ask)${z.reset}`),console.log(` • ${z.bold}im:message.group_msg${z.reset} ${z.red}# 敏感: 接收群里所有消息${z.reset}`),console.log(` ${z.dim}└ 关键: 没它机器人只收 @ 自己的消息, 拿不到群里其他对话, 一定要开${z.reset}`),console.log(` ${z.dim}└ 敏感权限要走审批: 申请时填用途, 个人开发者通常秒过${z.reset}`),console.log(` • ${z.bold}im:message.group_at_msg:readonly${z.reset} ${z.dim}# 读 @ 机器人消息 (兜底)${z.reset}`),console.log(` ${z.dim}卡片类 (Card Kit):${z.reset}`),console.log(` • ${z.bold}cardkit:card:read${z.reset} ${z.dim}# 读卡片状态${z.reset}`),console.log(` • ${z.bold}cardkit:card:write${z.reset} ${z.dim}# 创建/更新卡片 (流式渲染核心)${z.reset}`),console.log(),console.log(` ${z.bold}④ 订阅事件 (左侧 "${z.cyan}事件与回调${z.reset}", 拆两个子页:)${z.reset}`),console.log(` ${z.dim}a)${z.reset} ${z.bold}事件配置${z.reset} 页:`),console.log(` ${z.yellow}• "订阅方式" → 选 "长连接" → 点保存${z.reset}`),console.log(` • 添加事件: ${z.bold}im.message.receive_v1${z.reset} ${z.dim}# 收群消息${z.reset}`),console.log(` ${z.dim}b)${z.reset} ${z.bold}回调配置${z.reset} 页:`),console.log(` ${z.yellow}• "订阅方式" → 选 "长连接" → 点保存${z.reset}`),console.log(` • 添加事件: ${z.bold}card.action.trigger${z.reset} ${z.dim}# 卡片按钮点击回调${z.reset}`),console.log(),console.log(` ${z.bold}⑤ 发布版本${z.reset}`),console.log(` 页面顶部 "${z.bold}创建版本${z.reset}" → 滚到底点 "${z.bold}保存${z.reset}" → 弹框点 "${z.bold}发布${z.reset}"。`),console.log(` ${z.yellow}没发版的应用收不到任何事件 — 这步九成新手会忘!${z.reset}`),console.log(),console.log(` ${z.bold}⑥ 拿凭据${z.reset}`),console.log(` 左侧 "凭证与基础信息" → 顶部 ${z.bold}App ID${z.reset} (${z.dim}cli_...${z.reset}) 和 ${z.bold}App Secret${z.reset}, 待会粘到下面。`),console.log(),console.log(` ${z.bold}⑦ 把机器人拉进群${z.reset}`),console.log(" 想用的飞书群 → 群设置 → 群机器人 → 添加机器人 → 选你的应用。"),console.log(` ${z.yellow}群名要等于 projects_root 下的项目目录名${z.reset} (下一步设, 默认是用户主目录)。`),console.log();let J="",Z="";while(!0){J=await f("App ID (以 cli_ 开头)",{required:!0}),Z=await f("App Secret",{required:!0}),console.log(`${z.dim}测试中... (调 tenant_access_token endpoint)${z.reset}`);let X=await t(J,Z);if(X.ok){console.log(`${z.green}✓ Feishu 凭据测试通过${z.reset}`);break}if(console.log(`${z.red}✗ 测试失败:${z.reset} ${X.error}`),console.log(`${z.dim}最常见原因: app_id / app_secret 抄错, 或应用还没 "发布上线" (步骤 ⑤)。${z.reset}`),console.log(),(await f("重新填? (Y/n)",{default:"y"})).toLowerCase()==="n")console.log(`${z.yellow}已取消, 配置未写盘。${z.reset}`),K.close(),process.exit(1)}y(4,4,"工作目录 + 启动"),console.log("每个 Feishu 群对应 projects_root 下同名的目录。"),console.log();let W=process.platform==="win32"?process.env.USERPROFILE??"C:\\Users\\Default":process.env.HOME??"/root",w=await f("projects_root",{default:W});F(P,{recursive:!0});let v=["# Lodestar config — generated by `lodestar-setup`","# Edit by hand or re-run setup to overwrite.","","[feishu]",`app_id = "${B(J)}"`,`app_secret = "${B(Z)}"`,"","[runtime]",`projects_root = "${B(w)}"`,""];k(L,v.join(`
5
+ `)}function M($,q,J){console.log(`
6
+ ${z.bold}${z.yellow}[${$}/${q}] ${J}${z.reset}
7
+ `)}function P($){return $.replace(/\\/g,"\\\\").replace(/"/g,"\\\"")}function E($){let q=process.env.PATH??"";if(!q)return null;let J=process.platform==="win32"?[`${$}.cmd`,`${$}.bat`,`${$}.exe`,$]:[$];for(let K of q.split(m)){if(!K)continue;for(let H of J){let X=w(K,H);if(g(X))return X}}return null}async function I(){let $=process.platform==="win32"?"npm.cmd":"npm";return new Promise((q)=>{let J=b($,["install","-g","@openai/codex@latest"],{stdio:"inherit",shell:process.platform==="win32"});J.on("exit",(K)=>q(K===0)),J.on("error",()=>q(!1))})}function k($){try{let q=B(`"${$}" login status 2>&1`,{timeout:1e4}).toString();return/Logged in using ChatGPT/i.test(q)}catch{return!1}}async function c($){return new Promise((q)=>{let J=b($,["login"],{stdio:"inherit",shell:process.platform==="win32"});J.on("exit",(K)=>q(K===0)),J.on("error",()=>q(!1))})}function l($){try{if(process.platform==="win32")b(process.env.ComSpec??"cmd.exe",["/c","start",'""',$],{detached:!0,stdio:"ignore",windowsHide:!0}).unref();else if(process.platform==="darwin")b("open",[$],{detached:!0,stdio:"ignore"}).unref();else b("xdg-open",[$],{detached:!0,stdio:"ignore"}).unref()}catch{}}async function p($,q){try{let K=await(await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({app_id:$,app_secret:q})})).json();if(K.tenant_access_token)return{ok:!0};return{ok:!1,error:`飞书拒绝: code=${K.code} msg=${K.msg??"(no msg)"}`}}catch(J){return{ok:!1,error:`网络错误: ${J?.message??String(J)}`}}}function r(){try{let $=x(h(import.meta.url)),q=w($,"lodestar.js");if(!g(q))return{error:`找不到 daemon bundle: ${q}`};let J=b(process.execPath,[q],{detached:!0,stdio:"ignore",windowsHide:!0});return J.unref(),{pid:J.pid}}catch($){return{error:$?.message??String($)}}}async function O(){if(!process.stdin.isTTY)console.error(`${z.red}lodestar-setup: stdin 不是 TTY,无法交互式输入。${z.reset}`),console.error("请直接在 cmd / PowerShell / Terminal 里跑,不要 pipe 或重定向 stdin。"),process.exit(1);if(g(W)){if(console.log(`${z.yellow}发现已有配置: ${W}${z.reset}`),(await V("覆盖? (y/N)",{default:"n"})).toLowerCase()!=="y"){console.log("已取消"),Z.close();return}}D("Lodestar 安装向导"),console.log("Lodestar 把 Feishu (飞书) 群聊接到 Codex。"),console.log("每个群对应一个项目目录, Codex 在那里跑、能读写文件。"),console.log(),console.log("本向导依次做 4 件事:"),console.log(` ${z.dim}1) 确保 Codex CLI 已装好${z.reset}`),console.log(` ${z.dim}2) 确认 ChatGPT 登录${z.reset}`),console.log(` ${z.dim}3) Feishu 自建应用 (含权限 / 事件 / 发版 + 凭据测试)${z.reset}`),console.log(` ${z.dim}4) 工作目录, 自动启动 daemon${z.reset}`),console.log(),await Z.question(`${z.dim}按 Enter 开始 (Ctrl+C 退出)...${z.reset}`),M(1,4,"准备 Codex CLI");let $=E("codex");if($)console.log(`${z.green}✓ codex CLI 已就位${z.reset}: ${z.dim}${$}${z.reset}`);else{if(console.log(`${z.yellow}未在 PATH 找到 codex CLI, 自动安装...${z.reset}`),console.log(`${z.dim}运行: npm install -g @openai/codex@latest${z.reset}`),console.log(),!await I())console.error(`
8
+ ${z.red}安装失败。${z.reset}`),console.error("请手动运行后再开向导:"),console.error(` ${z.cyan}npm install -g @openai/codex@latest${z.reset}`),console.error(` ${z.cyan}lodestar-setup${z.reset}`),Z.close(),process.exit(1);$=E("codex"),console.log(`${z.green} 安装完成${z.reset}: ${z.dim}${$??"(应该装好了, PATH 找不到 重开终端再试)"}${z.reset}`)}if(M(2,4,"ChatGPT 登录"),console.log("Lodestar 使用 Codex ChatGPT 登录态。请确保 `codex login status` 显示 ChatGPT 登录。"),console.log(),$&&k($))console.log(`${z.green}✓ Codex 已登录 ChatGPT${z.reset}`);else{if(console.log(`${z.yellow}Codex 尚未登录 ChatGPT。现在启动 \`codex login\`。${z.reset}`),!($?await c($):!1)||!$||!k($))console.error(`
9
+ ${z.red}Codex ChatGPT 登录未完成。${z.reset}`),console.error(`请手动运行 ${z.cyan}codex login${z.reset} 后再开向导。`),Z.close(),process.exit(1);console.log(`${z.green}✓ Codex 已登录 ChatGPT${z.reset}`)}M(3,4,"Feishu 自建应用");let q="https://open.feishu.cn/app";console.log("打开飞书开放平台 (浏览器):"),console.log(` ${z.cyan}${q}${z.reset}`),l(q),console.log(`${z.dim}(如果浏览器没自动开, 复制上面 URL 粘到浏览器)${z.reset}`),console.log(),console.log(`${z.bold}详细操作步骤:${z.reset}`),console.log(),console.log(` ${z.bold}① 创建应用${z.reset}`),console.log(` 点 "创建企业自建应用", 填名字 (如 ${z.dim}Lodestar${z.reset}), logo 随意。`),console.log(),console.log(` ${z.bold}② 添加机器人能力${z.reset}`),console.log(` 左侧菜单 "${z.cyan}添加应用能力${z.reset}" → 找到 "机器人" → 点 "${z.bold}添加${z.reset}" 按钮。`),console.log(),console.log(` ${z.bold}③ 申请权限 (左侧 "${z.cyan}权限管理${z.reset}" → "${z.bold}开通权限${z.reset}")${z.reset}`),console.log(` ${z.yellow}缺一个都会让 daemon 启动后默默丢消息, 一定要全开。${z.reset}`),console.log(` ${z.dim}消息类:${z.reset}`),console.log(` • ${z.bold}im:message:send_as_bot${z.reset} ${z.dim}# 以机器人身份发消息${z.reset}`),console.log(` • ${z.bold}im:message${z.reset} ${z.dim}# 接收/操作消息 (核心)${z.reset}`),console.log(` • ${z.bold}im:chat${z.reset} ${z.dim}# 读/写群信息 (匹配群名 ↔ 项目目录)${z.reset}`),console.log(` • ${z.bold}im:resource${z.reset} ${z.dim}# 上传/下载附件 (图文双向)${z.reset}`),console.log(` • ${z.bold}im:message.urgent${z.reset} ${z.dim}# 加急推送 (锁屏通知 / Ask)${z.reset}`),console.log(` • ${z.bold}im:message.group_msg${z.reset} ${z.red}# 敏感: 接收群里所有消息${z.reset}`),console.log(` ${z.dim}└ 关键: 没它机器人只收 @ 自己的消息, 拿不到群里其他对话, 一定要开${z.reset}`),console.log(` ${z.dim}└ 敏感权限要走审批: 申请时填用途, 个人开发者通常秒过${z.reset}`),console.log(` • ${z.bold}im:message.group_at_msg:readonly${z.reset} ${z.dim}# 读 @ 机器人消息 (兜底)${z.reset}`),console.log(` ${z.dim}卡片类 (Card Kit):${z.reset}`),console.log(` • ${z.bold}cardkit:card:read${z.reset} ${z.dim}# 读卡片状态${z.reset}`),console.log(` • ${z.bold}cardkit:card:write${z.reset} ${z.dim}# 创建/更新卡片 (流式渲染核心)${z.reset}`),console.log(),console.log(` ${z.bold}④ 订阅事件 (左侧 "${z.cyan}事件与回调${z.reset}", 拆两个子页:)${z.reset}`),console.log(` ${z.dim}a)${z.reset} ${z.bold}事件配置${z.reset} 页:`),console.log(` ${z.yellow}• "订阅方式" → 选 "长连接" → 点保存${z.reset}`),console.log(` • 添加事件: ${z.bold}im.message.receive_v1${z.reset} ${z.dim}# 收群消息${z.reset}`),console.log(` ${z.dim}b)${z.reset} ${z.bold}回调配置${z.reset} 页:`),console.log(` ${z.yellow}• "订阅方式" → 选 "长连接" → 点保存${z.reset}`),console.log(` • 添加事件: ${z.bold}card.action.trigger${z.reset} ${z.dim}# 卡片按钮点击回调${z.reset}`),console.log(),console.log(` ${z.bold}⑤ 发布版本${z.reset}`),console.log(` 页面顶部 "${z.bold}创建版本${z.reset}" → 滚到底点 "${z.bold}保存${z.reset}" → 弹框点 "${z.bold}发布${z.reset}"。`),console.log(` ${z.yellow}没发版的应用收不到任何事件 — 这步九成新手会忘!${z.reset}`),console.log(),console.log(` ${z.bold}⑥ 拿凭据${z.reset}`),console.log(` 左侧 "凭证与基础信息" → 顶部 ${z.bold}App ID${z.reset} (${z.dim}cli_...${z.reset}) 和 ${z.bold}App Secret${z.reset}, 待会粘到下面。`),console.log(),console.log(` ${z.bold}⑦ 把机器人拉进群${z.reset}`),console.log(" 想用的飞书群 → 群设置 → 群机器人 → 添加机器人 → 选你的应用。"),console.log(` ${z.yellow}群名要等于 projects_root 下的项目目录名${z.reset} (下一步设, 默认是用户主目录)。`),console.log();let J="",K="";while(!0){J=await V("App ID (以 cli_ 开头)",{required:!0}),K=await V("App Secret",{required:!0}),console.log(`${z.dim}测试中... (调 tenant_access_token endpoint)${z.reset}`);let y=await p(J,K);if(y.ok){console.log(`${z.green}✓ Feishu 凭据测试通过${z.reset}`);break}if(console.log(`${z.red}✗ 测试失败:${z.reset} ${y.error}`),console.log(`${z.dim}最常见原因: app_id / app_secret 抄错, 或应用还没 "发布上线" (步骤 ⑤)。${z.reset}`),console.log(),(await V("重新填? (Y/n)",{default:"y"})).toLowerCase()==="n")console.log(`${z.yellow}已取消, 配置未写盘。${z.reset}`),Z.close(),process.exit(1)}M(4,4,"工作目录 + 启动"),console.log("每个 Feishu 群对应 projects_root 下同名的目录。"),console.log();let H=process.platform==="win32"?process.env.USERPROFILE??"C:\\Users\\Default":process.env.HOME??"/root",X=await V("projects_root",{default:H});A(N,{recursive:!0});let F=["# Lodestar config — generated by `lodestar-setup`","# Edit by hand or re-run setup to overwrite.","","[feishu]",`app_id = "${P(J)}"`,`app_secret = "${P(K)}"`,"","[runtime]",`projects_root = "${P(X)}"`,""];_(W,F.join(`
10
10
  `),{mode:384}),console.log(`
11
- ${z.green}${z.bold}✓ 配置已写入${z.reset}`),console.log(` ${z.cyan}${L}${z.reset}`),console.log(`
12
- ${z.bold}启动 daemon...${z.reset}`);let U=n(),u=process.platform==="win32"?"\\":"/",A=process.platform==="win32"?`${process.env.LOCALAPPDATA??"%LOCALAPPDATA%"}\\Lodestar\\daemon.log`:`${process.env.HOME??"~"}/.local/share/lodestar/daemon.log`;if(U.pid)console.log(`${z.green}✓ daemon 已在后台启动${z.reset} (pid ${U.pid})`),console.log(),console.log(`${z.bold}最后一步: 在 Feishu 验证${z.reset}`),console.log(" ① 把机器人拉进任意飞书群"),console.log(` ② 群名 = ${z.cyan}${w}${u}<群名>${z.reset} 下的目录名 (新群第一条消息会自动建)`),console.log(" ③ 在群里发任意一条消息, Claude 上线"),console.log(),console.log("日志:"),console.log(` ${z.cyan}${A}${z.reset}`),console.log(),console.log(`${z.dim}若长期跑后台, 参考 README "7×24 守护" 一节配 systemd / Task Scheduler。${z.reset}`);else console.log(`${z.yellow}启动失败: ${U.error}${z.reset}`),console.log(`手动运行: ${z.cyan}lodestar-daemon${z.reset}`);console.log(),K.close()}S().catch(($)=>{console.error(`
11
+ ${z.green}${z.bold}✓ 配置已写入${z.reset}`),console.log(` ${z.cyan}${W}${z.reset}`),console.log(`
12
+ ${z.bold}启动 daemon...${z.reset}`);let U=r(),u=process.platform==="win32"?"\\":"/",R=process.platform==="win32"?`${process.env.LOCALAPPDATA??"%LOCALAPPDATA%"}\\Lodestar\\daemon.log`:`${process.env.HOME??"~"}/.local/share/lodestar/daemon.log`;if(U.pid)console.log(`${z.green}✓ daemon 已在后台启动${z.reset} (pid ${U.pid})`),console.log(),console.log(`${z.bold}最后一步: 在 Feishu 验证${z.reset}`),console.log(" ① 把机器人拉进任意飞书群"),console.log(` ② 群名 = ${z.cyan}${X}${u}<群名>${z.reset} 下的目录名 (新群第一条消息会自动建)`),console.log(" ③ 在群里发任意一条消息, Codex 上线"),console.log(),console.log("日志:"),console.log(` ${z.cyan}${R}${z.reset}`),console.log(),console.log(`${z.dim}若长期跑后台, 参考 README "7×24 守护" 一节配 systemd / Task Scheduler。${z.reset}`);else console.log(`${z.yellow}启动失败: ${U.error}${z.reset}`),console.log(`手动运行: ${z.cyan}lodestar-daemon${z.reset}`);console.log(),Z.close()}O().catch(($)=>{console.error(`
13
13
  lodestar-setup: ${$?.message??$}`),process.exit(1)});
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{execSync as f}from"node:child_process";import{existsSync as U,readFileSync as y}from"node:fs";import{homedir as W}from"node:os";import{join as K}from"node:path";var X=W(),B=process.platform==="win32";function b(z,J,Y){if(z)return z;if(J)return K(J,"lodestar");return Y}function H(){if(B)return K(process.env.APPDATA??K(X,"AppData","Roaming"),"Lodestar");return K(X,".config","lodestar")}function V(){if(B)return K(process.env.LOCALAPPDATA??K(X,"AppData","Local"),"Lodestar");return K(X,".local","share","lodestar")}var w=b(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,H()),Q=b(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,V()),O=process.env.LODESTAR_CONFIG??K(w,"config.toml"),Z=K(Q,"daemon.pid"),g=K(Q,"daemon.log"),x=K(Q,"session-chat-map.json"),S=K(Q,"session-resume-map.json"),E=K(Q,"schedules.json"),v=K(Q,"alive-on-shutdown.json"),h=K(Q,"inbox"),k=K(Q,"debug.sock"),A=K(Q,"debug-context.json");import{execSync as G}from"node:child_process";import{readFileSync as L,writeFileSync as C}from"node:fs";function M(z){try{if(process.platform==="linux")return L(`/proc/${z}/cmdline`,"utf8").replace(/\0/g," ").trim();if(process.platform==="darwin")return G(`ps -p ${z} -o args=`,{encoding:"utf8",timeout:2000}).trim();if(process.platform==="win32")return G(`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${z}').CommandLine"`,{encoding:"utf8",timeout:5000}).trim()||null;return null}catch{return null}}function N(z,J){if(!Number.isFinite(z)||z<=0||!J)return!1;let Y=M(z);if(!Y)return!1;if(process.platform==="win32")return Y.toLowerCase().includes(J.toLowerCase());return Y.includes(J)}var q={reset:"\x1B[0m",bold:"\x1B[1m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"};function P(z){return new Promise((J)=>setTimeout(J,z))}async function R(){if(!U(Z)){console.log(`${q.yellow}Lodestar daemon 未运行${q.reset} ${q.dim}(${Z} 不存在)${q.reset}`);return}let z=y(Z,"utf8").split(`
3
- `),J=parseInt((z[0]??"").trim(),10),Y=(z[1]??"").trim();if(!Number.isFinite(J)||J<=0)console.error(`${q.red}PID 文件格式坏:${q.reset} ${Z}`),process.exit(1);if(Y&&!N(J,Y)){console.log(`${q.yellow}PID ${J} 上没有 daemon (stale 文件)${q.reset}`),console.log(`${q.dim}手删 ${Z} 后再试${q.reset}`);return}console.log(`${q.bold}停止 daemon${q.reset} (pid ${J})...`);try{if(process.platform==="win32")f(`taskkill /PID ${J} /T /F`,{stdio:"ignore"});else process.kill(J,"SIGTERM")}catch($){console.error(`${q.red}发信号失败:${q.reset} ${$?.message??$}`),process.exit(1)}for(let $=0;$<50;$++){if(!U(Z)){console.log(`${q.green}✓ daemon 已停${q.reset}`);return}await P(100)}console.log(`${q.yellow}已发 SIGTERM, 但 5s 内 daemon 没清掉 ${Z}${q.reset}`),console.log(`${q.dim}它可能还在收尾 (落盘 alive marker / 关 WS); 下次启动 pid-guard 会自动判别。${q.reset}`)}R().catch((z)=>{console.error(`${q.red}lodestar-stop:${q.reset} ${z?.message??z}`),process.exit(1)});
2
+ import{execSync as f}from"node:child_process";import{existsSync as U,readFileSync as y}from"node:fs";import{homedir as W}from"node:os";import{join as J}from"node:path";var X=W(),B=process.platform==="win32";function b(z,K,Y){if(z)return z;if(K)return J(K,"lodestar");return Y}function H(){if(B)return J(process.env.APPDATA??J(X,"AppData","Roaming"),"Lodestar");return J(X,".config","lodestar")}function V(){if(B)return J(process.env.LOCALAPPDATA??J(X,"AppData","Local"),"Lodestar");return J(X,".local","share","lodestar")}var w=b(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,H()),Q=b(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,V()),T=process.env.LODESTAR_CONFIG??J(w,"config.toml"),Z=J(Q,"daemon.pid"),g=J(Q,"daemon.log"),x=J(Q,"session-chat-map.json"),S=J(Q,"session-resume-map.json"),E=J(Q,"session-context-baselines.json"),v=J(Q,"schedules.json"),h=J(Q,"alive-on-shutdown.json"),k=J(Q,"inbox"),F=J(Q,"debug.sock"),j=J(Q,"debug-context.json");import{execSync as G}from"node:child_process";import{readFileSync as L,writeFileSync as _}from"node:fs";function M(z){try{if(process.platform==="linux")return L(`/proc/${z}/cmdline`,"utf8").replace(/\0/g," ").trim();if(process.platform==="darwin")return G(`ps -p ${z} -o args=`,{encoding:"utf8",timeout:2000}).trim();if(process.platform==="win32")return G(`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${z}').CommandLine"`,{encoding:"utf8",timeout:5000}).trim()||null;return null}catch{return null}}function N(z,K){if(!Number.isFinite(z)||z<=0||!K)return!1;let Y=M(z);if(!Y)return!1;if(process.platform==="win32")return Y.toLowerCase().includes(K.toLowerCase());return Y.includes(K)}var q={reset:"\x1B[0m",bold:"\x1B[1m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"};function P(z){return new Promise((K)=>setTimeout(K,z))}async function u(){if(!U(Z)){console.log(`${q.yellow}Lodestar daemon 未运行${q.reset} ${q.dim}(${Z} 不存在)${q.reset}`);return}let z=y(Z,"utf8").split(`
3
+ `),K=parseInt((z[0]??"").trim(),10),Y=(z[1]??"").trim();if(!Number.isFinite(K)||K<=0)console.error(`${q.red}PID 文件格式坏:${q.reset} ${Z}`),process.exit(1);if(Y&&!N(K,Y)){console.log(`${q.yellow}PID ${K} 上没有 daemon (stale 文件)${q.reset}`),console.log(`${q.dim}手删 ${Z} 后再试${q.reset}`);return}console.log(`${q.bold}停止 daemon${q.reset} (pid ${K})...`);try{if(process.platform==="win32")f(`taskkill /PID ${K} /T /F`,{stdio:"ignore"});else process.kill(K,"SIGTERM")}catch($){console.error(`${q.red}发信号失败:${q.reset} ${$?.message??$}`),process.exit(1)}for(let $=0;$<50;$++){if(!U(Z)){console.log(`${q.green}✓ daemon 已停${q.reset}`);return}await P(100)}console.log(`${q.yellow}已发 SIGTERM, 但 5s 内 daemon 没清掉 ${Z}${q.reset}`),console.log(`${q.dim}它可能还在收尾 (落盘 alive marker / 关 WS); 下次启动 pid-guard 会自动判别。${q.reset}`)}u().catch((z)=>{console.error(`${q.red}lodestar-stop:${q.reset} ${z?.message??z}`),process.exit(1)});
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as b}from"node:child_process";import{existsSync as g}from"node:fs";import{homedir as p}from"node:os";import{join as e}from"node:path";var m=p(),a=process.platform==="win32";function $(o,t,r){if(o)return o;if(t)return e(t,"lodestar");return r}function d(){if(a)return e(process.env.APPDATA??e(m,"AppData","Roaming"),"Lodestar");return e(m,".config","lodestar")}function y(){if(a)return e(process.env.LOCALAPPDATA??e(m,"AppData","Local"),"Lodestar");return e(m,".local","share","lodestar")}var u=$(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,d()),l=$(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,y()),P=process.env.LODESTAR_CONFIG??e(u,"config.toml"),n=e(l,"daemon.pid"),C=e(l,"daemon.log"),q=e(l,"session-chat-map.json"),z=e(l,"session-resume-map.json"),J=e(l,"schedules.json"),K=e(l,"alive-on-shutdown.json"),Q=e(l,"inbox"),S=e(l,"debug.sock"),Y=e(l,"debug-context.json");var s={reset:"\x1B[0m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"};function w(){let o=process.platform==="win32"?"npm.cmd":"npm";return new Promise((t)=>{let r=b(o,["install","-g","@leviyuan/lodestar@latest","@anthropic-ai/claude-code@latest"],{stdio:"inherit",shell:process.platform==="win32"});r.on("exit",(c)=>t(c??1)),r.on("error",(c)=>{console.error(`${s.red}spawn npm 失败:${s.reset} ${c.message}`),t(1)})})}async function x(){console.log(`${s.bold}更新 Lodestar + Claude Code${s.reset}`),console.log(`${s.dim}npm i -g @leviyuan/lodestar@latest @anthropic-ai/claude-code@latest${s.reset}
3
- `);let o=await w();if(o!==0)console.error(`
2
+ import{spawn as b}from"node:child_process";import{existsSync as u}from"node:fs";import{homedir as p}from"node:os";import{join as e}from"node:path";var r=p(),$=process.platform==="win32";function a(o,t,m){if(o)return o;if(t)return e(t,"lodestar");return m}function y(){if($)return e(process.env.APPDATA??e(r,"AppData","Roaming"),"Lodestar");return e(r,".config","lodestar")}function d(){if($)return e(process.env.LOCALAPPDATA??e(r,"AppData","Local"),"Lodestar");return e(r,".local","share","lodestar")}var x=a(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,y()),l=a(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,d()),C=process.env.LODESTAR_CONFIG??e(x,"config.toml"),n=e(l,"daemon.pid"),P=e(l,"daemon.log"),q=e(l,"session-chat-map.json"),z=e(l,"session-resume-map.json"),J=e(l,"session-context-baselines.json"),K=e(l,"schedules.json"),Q=e(l,"alive-on-shutdown.json"),S=e(l,"inbox"),Y=e(l,"debug.sock"),Z=e(l,"debug-context.json");var s={reset:"\x1B[0m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"};function g(){let o=process.platform==="win32"?"npm.cmd":"npm";return new Promise((t)=>{let m=b(o,["install","-g","@leviyuan/lodestar@latest","@openai/codex@latest"],{stdio:"inherit",shell:process.platform==="win32"});m.on("exit",(c)=>t(c??1)),m.on("error",(c)=>{console.error(`${s.red}spawn npm 失败:${s.reset} ${c.message}`),t(1)})})}async function w(){console.log(`${s.bold}更新 Lodestar + Codex CLI${s.reset}`),console.log(`${s.dim}npm i -g @leviyuan/lodestar@latest @openai/codex@latest${s.reset}
3
+ `);let o=await g();if(o!==0)console.error(`
4
4
  ${s.red}更新失败 (npm exit ${o})${s.reset}`),process.exit(o);if(console.log(`
5
- ${s.green}✓ 更新完成${s.reset}`),g(n))console.log(),console.log(`${s.yellow}检测到 daemon 仍在跑老版本进程, 用新版本需要重启:${s.reset}`),console.log(` ${s.dim}# systemd / Task Scheduler 托管的:${s.reset}`),console.log(` ${s.cyan}systemctl --user restart lodestar${s.reset} ${s.dim}# Linux/macOS${s.reset}`),console.log(` ${s.dim}# 或手动重启:${s.reset}`),console.log(` ${s.cyan}lodestar-stop && lodestar-daemon${s.reset}`)}x().catch((o)=>{console.error(`${s.red}lodestar-update:${s.reset} ${o?.message??o}`),process.exit(1)});
5
+ ${s.green}✓ 更新完成${s.reset}`),u(n))console.log(),console.log(`${s.yellow}检测到 daemon 仍在跑老版本进程, 用新版本需要重启:${s.reset}`),console.log(` ${s.dim}# systemd / Task Scheduler 托管的:${s.reset}`),console.log(` ${s.cyan}systemctl --user restart lodestar${s.reset} ${s.dim}# Linux/macOS${s.reset}`),console.log(` ${s.dim}# 或手动重启:${s.reset}`),console.log(` ${s.cyan}lodestar-stop && lodestar-daemon${s.reset}`)}w().catch((o)=>{console.error(`${s.red}lodestar-update:${s.reset} ${o?.message??o}`),process.exit(1)});
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{execSync as z}from"node:child_process";import{readFileSync as B}from"node:fs";import{dirname as D,join as G}from"node:path";import{fileURLToPath as H}from"node:url";var b={reset:"\x1B[0m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",dim:"\x1B[2m"};function J(){try{let q=D(H(import.meta.url)),v=JSON.parse(B(G(q,"..","package.json"),"utf8"));return typeof v.version==="string"?v.version:"(unknown)"}catch{return"(unknown)"}}function K(){try{return z("claude --version",{timeout:1e4,stdio:["ignore","pipe","ignore"]}).toString().trim()||"(unknown)"}catch{return"(未找到 / 未安装)"}}console.log(`${b.bold}Lodestar${b.reset} ${b.green}v${J()}${b.reset}`);console.log(`${b.dim}Claude Code:${b.reset} ${K()}`);console.log(`${b.dim}Runtime:${b.reset} ${process.version} (${process.platform}-${process.arch})`);
2
+ import{execSync as z}from"node:child_process";import{readFileSync as A}from"node:fs";import{dirname as B,join as D}from"node:path";import{fileURLToPath as E}from"node:url";var b={reset:"\x1B[0m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",dim:"\x1B[2m"};function G(){try{let q=B(E(import.meta.url)),v=JSON.parse(A(D(q,"..","package.json"),"utf8"));return typeof v.version==="string"?v.version:"(unknown)"}catch{return"(unknown)"}}function H(){try{return z("codex --version",{timeout:1e4,stdio:["ignore","pipe","ignore"]}).toString().trim()||"(unknown)"}catch{return"(未找到 / 未安装)"}}console.log(`${b.bold}Lodestar${b.reset} ${b.green}v${G()}${b.reset}`);console.log(`${b.dim}Codex CLI:${b.reset} ${H()}`);console.log(`${b.dim}Runtime:${b.reset} ${process.version} (${process.platform}-${process.arch})`);