@leviyuan/lodestar 0.17.0 → 0.17.2
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 +23 -128
- package/dist/dsh-bridge.js +33 -12
- package/dist/lodestar-agent.js +10 -10
- package/dist/lodestar-setup.js +13 -11
- package/dist/lodestar-stop.js +2 -2
- package/dist/lodestar-update.js +7 -4
- package/dist/lodestar-version.js +1 -1
- package/dist/lodestar.js +169 -166
- package/docs/AGENTS.md +14 -0
- package/docs/claude-agent-backend.md +135 -0
- package/docs/configuration.md +55 -0
- package/docs/dsh-testing-report.md +189 -0
- package/docs/models.md +76 -0
- package/docs/usage.md +79 -0
- package/package.json +246 -19
- package/scripts/postinstall.cjs +1 -26
package/README.md
CHANGED
|
@@ -4,146 +4,43 @@
|
|
|
4
4
|
|
|
5
5
|
# 夜航星 (Lodestar)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
在飞书群里使用 Codex、Claude Code 和 DeepSeek Harness。每个群对应一个项目目录和会话,回复、工具调用、图片、提问及后台任务通过卡片展示。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
支持 Codex 订阅、GLM Coding Plan、DeepSeek、OpenRouter 和 Claude native;账号、模型与 effort 可在群里切换并保存。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## 快速开始
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
支持 Codex、GLM Coding Plan、DeepSeek、Claude native 和 DeepSeek Harness 原生后端。账号、模型与 effort 可在群里切换,选择按群保存。
|
|
16
|
-
|
|
17
|
-
## 安装
|
|
18
|
-
|
|
19
|
-
支持 Windows、macOS 和 Linux,需要 Node.js ≥ 18.15。Bun 用于源码开发和构建。
|
|
20
|
-
|
|
21
|
-
DeepSeek Harness 子进程另需 Node 22.19+(22.x)或 Node 24+;可通过其账号配置的 `bin` 指定 Node 可执行文件。
|
|
13
|
+
需要 Node.js ≥ 18.15,支持 Windows、macOS 和 Linux。使用 DSH 时,其子进程需要 Node 22.19+(22.x)或 24+。
|
|
22
14
|
|
|
23
15
|
```bash
|
|
24
16
|
npm i -g @leviyuan/lodestar
|
|
25
17
|
lodestar-setup
|
|
26
18
|
```
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
把机器人拉进群,群名设为 `projects_root` 下的目录名。目录不存在时会自动创建。首次消息默认使用 Claude 侧已配置的账号;发 `model` 可切换到其他账号。
|
|
20
|
+
完成向导后,把机器人拉进群,将群名设为项目目录名。发送消息开始工作,发送 `md` 选择账号和模型。
|
|
31
21
|
|
|
32
|
-
|
|
22
|
+
Agent 默认自动跟随上游 `latest`,在 daemon 启动时及每 6 小时检查更新;也可用 `lodestar-update --agents-only` 立即更新。
|
|
33
23
|
|
|
34
|
-
|
|
|
24
|
+
| 常用指令 | 作用 |
|
|
35
25
|
| --- | --- |
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
|
|
43
|
-
长期运行可交给 Linux `systemd --user`、macOS `launchd` 或 Windows 任务计划程序。daemon 重启后会恢复上次活跃的会话。
|
|
26
|
+
| `hi` | 查看会话、额度和 Codex 重置卡余量 |
|
|
27
|
+
| `md` | 按 Agent 分组选账号、模型和 effort;支持补录及隐藏 |
|
|
28
|
+
| `stop` | 打断当前回复 |
|
|
29
|
+
| `rs` / `cl` | 恢复会话 / 开始新会话 |
|
|
30
|
+
| `cm` | 压缩上下文 |
|
|
31
|
+
| `wt` / `btw` | 创建独立工作区 / 临时会话 |
|
|
44
32
|
|
|
45
|
-
|
|
33
|
+
单个发送文件不超过 **30 MB**。Codex 生图的提示词和图片默认折叠展示。
|
|
46
34
|
|
|
47
|
-
|
|
35
|
+
## 详细文档
|
|
48
36
|
|
|
49
|
-
|
|
|
37
|
+
| 文档 | 内容 |
|
|
50
38
|
| --- | --- |
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
| `compact` / `cm` | 压缩当前会话的上下文 |
|
|
57
|
-
| `model` / `md` | 按账号 → 模型 → effort 选择并保存 |
|
|
58
|
-
| `agents` | 查看可调用的 Agent 身份及可用状态 |
|
|
59
|
-
| `task` | 创建、查看或删除绑定的飞书任务清单 |
|
|
60
|
-
|
|
61
|
-
模型列表来自各账号的模型目录,获取失败显示 `MISS`。同账号切换 Claude 模型从后续回复生效,Codex 的持久模型设置需重启会话生效;跨账号或后端切换只允许在空闲时进行。
|
|
62
|
-
|
|
63
|
-
GLM 的套餐与用量显示在 `hi` 控制台,回复底部也会显示当前窗口用量。
|
|
64
|
-
|
|
65
|
-
## Worktree 与临时会话
|
|
66
|
-
|
|
67
|
-
需要独立修改文件时,在项目主群使用 worktree:
|
|
68
|
-
|
|
69
|
-
| 指令 | 行为 |
|
|
70
|
-
| --- | --- |
|
|
71
|
-
| `wt` / `worktree` | 列出项目的 `work/*` 分支和工作区状态 |
|
|
72
|
-
| `wt feature-x` | 创建或加入 `<project>[feature-x]` 群和同级 worktree 目录,使用 `work/feature-x` 分支 |
|
|
73
|
-
|
|
74
|
-
已合并且未挂载的分支会折叠隐藏,再次启用时更新到主线。卡片上的“删”会检查对应群没有运行中的会话、工作区没有未提交变更,再解散群并删除 worktree;Git 分支保留。
|
|
75
|
-
|
|
76
|
-
临时会话共享当前工作目录,适合在同一项目里另开一段对话:
|
|
77
|
-
|
|
78
|
-
| 指令 | 行为 |
|
|
79
|
-
| --- | --- |
|
|
80
|
-
| `btw` | 创建 `<session>*MMDD-HHMM` 临时群,继承账号、模型和工作目录,启动新会话 |
|
|
81
|
-
| `bye` | 停止并解散当前临时群,仅适用于 Lodestar 创建的临时群 |
|
|
82
|
-
| `fk` / `fork` | 选择一条用户输入,在临时群里从这条输入之前分叉 |
|
|
83
|
-
| `bk` / `back` | 选择一条用户输入,让本群接到这条输入之前的新分支 |
|
|
84
|
-
|
|
85
|
-
`fk`、`bk` 和从历史记录恢复的 `rs` 都使用后端原生 fork,保留源对话。选第 N 条输入表示回到它发出之前,选中的输入不包含在新分支内。Claude 的新分支在首条输入时获得会话 id,准备状态会持久保存。
|
|
86
|
-
|
|
87
|
-
**分叉和回退只改变对话历史,不回滚文件或撤销 Shell、MCP 等外部操作。** `bk` 会附上已观察到的文件变更记录;需要目录隔离时使用 `wt`。
|
|
88
|
-
|
|
89
|
-
## 多模型任务
|
|
90
|
-
|
|
91
|
-
主 Agent 可以通过 `lodestar-agent` 查询实时身份,再把任务交给一个或多个模型。同一个任务选择多个身份时并发执行;后续追问可继续使用各模型的原生会话。
|
|
92
|
-
|
|
93
|
-
被调用的 Agent 可以编辑文件、执行命令、使用 MCP 和 Skill;委派只允许一层,被调用的 Agent 不能继续派工。它们与主 Agent 共享工作区,修改会立即可见。运行状态和结果通过卡片展示;需要用户输入时暂停,由主 Agent 回填答案后继续。委派产生的会话不会混入主群的 `rs` 历史列表。
|
|
94
|
-
|
|
95
|
-
`lodestar-agent` 只能在 Lodestar 管理的 Agent 进程里调用。命令用法见 [Agent Skill](src/agent-skill.ts)。
|
|
96
|
-
|
|
97
|
-
## 飞书任务清单
|
|
98
|
-
|
|
99
|
-
发 `task`,点“启用”可创建并绑定 `<project>[lodestar]` 任务清单。删除需要在卡片上再次确认,会删除整个清单及其中任务。该面板管理清单绑定,任务执行由用户和 Agent 安排。
|
|
100
|
-
|
|
101
|
-
## 本机通知
|
|
102
|
-
|
|
103
|
-
脚本可通过 HTTP 向项目群发送通知,支持 `info`、`warn`、`error`,以及本地图片、按钮和点击回调:
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
curl -sS -X POST http://127.0.0.1:9876/notify \
|
|
107
|
-
-H 'Content-Type: application/json' \
|
|
108
|
-
-d '{"project":"ops","level":"error","text":"构建失败,截图如下","images":["/abs/shot.png"]}'
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
按钮点击结果可以 POST 到指定的本机 `callback`,或通过 `GET /notify/result/<notify_id>` 查询。字段和协议见 [feishu-notify Skill](src/notify-skill.ts)。daemon 启动时会将该 Skill 同步到 Codex 和 Claude 的 Skill 目录。
|
|
112
|
-
|
|
113
|
-
## 配置
|
|
114
|
-
|
|
115
|
-
默认配置文件是 `~/.config/lodestar/config.toml`,可通过 `LODESTAR_CONFIG` 指定文件。日志和会话状态位于 `~/.local/share/lodestar/`;Windows 使用相应的应用数据目录。完整路径定义见 [src/paths.ts](src/paths.ts)。
|
|
116
|
-
|
|
117
|
-
```toml
|
|
118
|
-
[runtime]
|
|
119
|
-
projects_root = "/abs/projects"
|
|
120
|
-
live_elapsed = "bucket" # bucket 按档位刷新耗时;second 按秒刷新
|
|
121
|
-
|
|
122
|
-
[projects.calculator]
|
|
123
|
-
cwd = "/abs/projects/calculator" # 对两个后端均生效
|
|
124
|
-
setting_sources = "project" # Claude 后端;仅未绑定 Token Source 时使用
|
|
125
|
-
strict_mcp = "true" # 以下字段用于 Claude 主会话
|
|
126
|
-
load_project_mcp = "true"
|
|
127
|
-
tools = "Read,Write,Edit,Bash,Glob,Grep"
|
|
128
|
-
|
|
129
|
-
[claude]
|
|
130
|
-
bin = "/abs/path/to/claude-wrapper" # 可选的 Claude 可执行文件
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
账号配置使用 `[token_source.glm]`、`[token_source.deepseek]` 等节。GLM 和 DeepSeek 也可以从本机 Claude settings 中识别;识别后由对应 Token Source 注入凭据,避免不同账号的环境变量串用。项目的工具限制只作用于主会话,委派 Agent 使用完整工具集。
|
|
134
|
-
|
|
135
|
-
DeepSeek Harness 使用独立的 `[token_source.deepseek-harness]` 账号与原生会话。在群内发送 `deepseek-harness-setup <api_key>`,再通过 `model` 面板选择该来源即可启用。自建端点用 `deepseek-harness-setup <base_url> <api_key>`;这里使用原生 API 根地址,不带 `/anthropic`。
|
|
136
|
-
|
|
137
|
-
```toml
|
|
138
|
-
[token_source.deepseek-harness]
|
|
139
|
-
agent = "dsh"
|
|
140
|
-
api_key = "填写自己的 API key"
|
|
141
|
-
# bin = "/abs/path/to/node" # 可选:运行 DSH 的 Node 可执行文件
|
|
142
|
-
# model = "deepseek-v4-pro" # 可选:必须存在于 DSH 返回的模型目录
|
|
143
|
-
# effort = "high" # 可选:必须是该模型支持的推理强度
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
手动修改配置后需重启 daemon;群内账号启用和模型补录会自行重载相关配置。模型路由、配置优先级和后端差异见 [后端说明](docs/claude-agent-backend.md)。
|
|
39
|
+
| [安装与配置](docs/configuration.md) | CLI、配置文件、服务运行、Agent 自动更新 |
|
|
40
|
+
| [群内使用指南](docs/usage.md) | 完整命令、会话恢复、worktree、多模型任务、文件及通知 |
|
|
41
|
+
| [账号与模型](docs/models.md) | Token Source、effort、OpenRouter 默认模型、DSH 与 GLM 接入 |
|
|
42
|
+
| [后端与路由](docs/claude-agent-backend.md) | 运行机制、接口差异和维护说明 |
|
|
43
|
+
| [DSH 测试记录](docs/dsh-testing-report.md) | 原生工具、会话和飞书实测记录 |
|
|
147
44
|
|
|
148
45
|
## 开发
|
|
149
46
|
|
|
@@ -154,8 +51,6 @@ bun test
|
|
|
154
51
|
bun run build
|
|
155
52
|
```
|
|
156
53
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
## 许可
|
|
54
|
+
源码运行:`bun run start`。真实飞书探针使用前请阅读[脚本说明](scripts/AGENTS.md)。
|
|
160
55
|
|
|
161
|
-
[MIT](LICENSE)
|
|
56
|
+
[MIT License](LICENSE)
|
package/dist/dsh-bridge.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
// src/dsh-bridge.ts
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
4
6
|
import { realpath } from "node:fs/promises";
|
|
5
7
|
import { createUserMessage, ReasoningEffortId } from "@deepseek-ai/dsh-llm";
|
|
6
8
|
import { SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
9
|
+
|
|
10
|
+
// src/dsh-protocol.ts
|
|
11
|
+
var DSH_PROTOCOL_VERSION = 1;
|
|
12
|
+
|
|
13
|
+
// src/dsh-bridge.ts
|
|
7
14
|
var name = "lodestar-bridge";
|
|
8
15
|
var inject = ["agents", "llm", "agentDefaultModel", "sessionPersistence", "tools", "systemPrompt", "userQuestions", "compaction", "shellEnv", "attachments", "sdkAppStartup"];
|
|
9
|
-
var PROVIDER = "deepseek-official";
|
|
16
|
+
var PROVIDER = process.env.LODESTAR_DSH_PROVIDER ?? "deepseek-official";
|
|
17
|
+
if (!["deepseek-official", "zai-coding-cn", "zai"].includes(PROVIDER))
|
|
18
|
+
throw new Error(`Unsupported DSH provider: ${PROVIDER}`);
|
|
10
19
|
var DELEGATION_TOOLS = ["subagent", "subagent_fork", "workflow", "ralph", "send_message"];
|
|
20
|
+
function nativeErrorMessage(error, depth = 0) {
|
|
21
|
+
if (!(error instanceof Error))
|
|
22
|
+
return String(error);
|
|
23
|
+
return error.cause && depth < 4 ? `${error.message}: ${nativeErrorMessage(error.cause, depth + 1)}` : error.message;
|
|
24
|
+
}
|
|
11
25
|
function apply(ctx) {
|
|
12
26
|
let root;
|
|
13
27
|
let rootId;
|
|
@@ -28,7 +42,7 @@ function apply(ctx) {
|
|
|
28
42
|
`);
|
|
29
43
|
};
|
|
30
44
|
const fail = (error) => {
|
|
31
|
-
const message =
|
|
45
|
+
const message = nativeErrorMessage(error);
|
|
32
46
|
process.stderr.write(`lodestar-dsh: ${message}
|
|
33
47
|
`);
|
|
34
48
|
notify("failure", { message });
|
|
@@ -89,7 +103,9 @@ function apply(ctx) {
|
|
|
89
103
|
settle();
|
|
90
104
|
});
|
|
91
105
|
ctx.on("agent/error", ({ agent, error }) => {
|
|
92
|
-
const message =
|
|
106
|
+
const message = nativeErrorMessage(error);
|
|
107
|
+
process.stderr.write(`lodestar-dsh agent error: ${message}
|
|
108
|
+
`);
|
|
93
109
|
if (agent.session.id === rootId)
|
|
94
110
|
turnFailure = message;
|
|
95
111
|
notify("agent.error", { sessionId: agent.session.id, message });
|
|
@@ -131,8 +147,13 @@ function apply(ctx) {
|
|
|
131
147
|
notify("question", { id, questions: request.questions });
|
|
132
148
|
});
|
|
133
149
|
});
|
|
134
|
-
async function catalog() {
|
|
135
|
-
const models = await ctx.llm.listModels(PROVIDER);
|
|
150
|
+
async function catalog(extra = []) {
|
|
151
|
+
const models = [...await ctx.llm.listModels(PROVIDER)];
|
|
152
|
+
const listed = new Set(models.map((model) => model.id));
|
|
153
|
+
for (const id of new Set(extra)) {
|
|
154
|
+
if (!listed.has(id))
|
|
155
|
+
models.push(await ctx.llm.resolveModelInfo(PROVIDER, id));
|
|
156
|
+
}
|
|
136
157
|
if (!models.length)
|
|
137
158
|
throw new Error("DSH model catalog is empty");
|
|
138
159
|
return Promise.all(models.map(async (model) => {
|
|
@@ -146,14 +167,12 @@ function apply(ctx) {
|
|
|
146
167
|
efforts: info.reasoning.efforts.map((e) => e.id),
|
|
147
168
|
defaultEffort: info.reasoning.defaultEffort,
|
|
148
169
|
contextWindow: info.context?.contextWindow ?? null,
|
|
149
|
-
isDefault: ctx.agentDefaultModel.currentSelection().provider === PROVIDER && ctx.agentDefaultModel.currentSelection().model === model.id
|
|
170
|
+
isDefault: ctx.agentDefaultModel.currentSelection().provider === PROVIDER && ctx.agentDefaultModel.currentSelection().model === model.id,
|
|
171
|
+
isCustom: !listed.has(model.id)
|
|
150
172
|
};
|
|
151
173
|
}));
|
|
152
174
|
}
|
|
153
175
|
async function route(model, effort) {
|
|
154
|
-
const info = (await catalog()).find((entry) => entry.model === model);
|
|
155
|
-
if (!info || !info.efforts.some((e) => e === effort))
|
|
156
|
-
throw new Error(`DSH model/effort unavailable: ${model}/${effort}`);
|
|
157
176
|
return ctx.llm.resolveCallConfig({ provider: PROVIDER, model, reasoningEffort: ReasoningEffortId(effort) });
|
|
158
177
|
}
|
|
159
178
|
async function open(input) {
|
|
@@ -229,7 +248,7 @@ function apply(ctx) {
|
|
|
229
248
|
}
|
|
230
249
|
await ctx.sessionPersistence.flush();
|
|
231
250
|
lifecycle.signal.throwIfAborted();
|
|
232
|
-
return { sessionId: rootId, models: await catalog() };
|
|
251
|
+
return { sessionId: rootId, models: await catalog([input.model]) };
|
|
233
252
|
} catch (error) {
|
|
234
253
|
if (!root)
|
|
235
254
|
rootId = undefined;
|
|
@@ -244,13 +263,15 @@ function apply(ctx) {
|
|
|
244
263
|
throw new Error("DSH already initialized");
|
|
245
264
|
await ctx.get("loader")?.await();
|
|
246
265
|
initialized = true;
|
|
247
|
-
|
|
266
|
+
const require2 = createRequire(import.meta.url);
|
|
267
|
+
const manifest = JSON.parse(readFileSync(require2.resolve("@deepseek-ai/dsh/package.json"), "utf8"));
|
|
268
|
+
return { protocolVersion: DSH_PROTOCOL_VERSION, runtimeVersion: manifest.version };
|
|
248
269
|
}
|
|
249
270
|
if (!initialized)
|
|
250
271
|
throw new Error("DSH not initialized");
|
|
251
272
|
switch (method) {
|
|
252
273
|
case "model/list":
|
|
253
|
-
return catalog();
|
|
274
|
+
return catalog(params.models ?? []);
|
|
254
275
|
case "session/open": {
|
|
255
276
|
if (opening)
|
|
256
277
|
throw new Error("DSH session initialization is already in progress");
|
package/dist/lodestar-agent.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
3
|
-
`);return}let
|
|
4
|
-
`:
|
|
5
|
-
`);return}default:throw Error(
|
|
6
|
-
`);return}await
|
|
7
|
-
`),process.exit(1)})};process.once("SIGINT",
|
|
2
|
+
import{realpathSync as Z}from"node:fs";import{fileURLToPath as P}from"node:url";async function J(z=process.argv.slice(2)){let B=z.shift()??"";if(!B||B==="help"||B==="--help"||B==="-h"){process.stdout.write(`${_()}
|
|
3
|
+
`);return}let j=R();switch(B){case"identities":{let D=await M(j,"GET","/agents/identities");process.stdout.write(z.includes("--json")?`${JSON.stringify(D,null,2)}
|
|
4
|
+
`:b(D));return}case"run":await S(j,z);return;case"follow-up":case"followup":await A(j,z);return;case"answer":await f(j,z);return;case"status":{let D=Q(z[0],"status requires run_id"),E=await M(j,"GET",`/agents/runs/${encodeURIComponent(D)}`);if(process.stdout.write(O(E)),E.status==="failed"||E.status==="cancelled")process.exitCode=1;return}case"cancel":{let D=Q(z[0],"cancel requires run_id"),E=await M(j,"DELETE",`/agents/runs/${encodeURIComponent(D)}`);process.stdout.write(`${JSON.stringify(E)}
|
|
5
|
+
`);return}default:throw Error(_())}}async function S(z,B){let j=$(B,!0),D=await L(j),E={identity_ids:j.identityIds,prompt:D,...j.effort?{effort:j.effort}:{}},G=await M(z,"POST","/agents/runs",E);await T(z,G,j.noWait)}async function A(z,B){let j=Q(B.shift(),"follow-up requires run_id"),D=$(B,!1),G={prompt:await L(D),...D.identityId?{identity_id:D.identityId}:{},...D.effort?{effort:D.effort}:{}},H=await M(z,"POST",`/agents/runs/${encodeURIComponent(j)}/follow-up`,G);await T(z,H,D.noWait)}async function f(z,B){let j=Q(B.shift(),"answer requires run_id"),D="",E="",G=!1,H={};for(let N=0;N<B.length;N++){let K=B[N],V=()=>Q(B[++N],`${K} requires a value`);switch(K){case"--identity":case"-i":D=V();break;case"--request":E=V();break;case"--answer":{let X=V(),Y=X.indexOf("=");if(Y<=0)throw Error("--answer must be question-or-id=value");H[X.slice(0,Y)]=X.slice(Y+1);break}case"--stdin":G=!0;break;default:throw Error(`unknown answer option: ${K}`)}}if(G){let N=(await W()).trim(),K;try{K=JSON.parse(N)}catch{throw Error("answer stdin must be a JSON object")}if(!K||typeof K!=="object"||Array.isArray(K))throw Error("answer stdin must be a JSON object");for(let[V,X]of Object.entries(K))H[V]=String(X)}if(!E)throw Error("answer requires --request");if(Object.keys(H).length===0)throw Error("answer requires --answer or --stdin");let U=await M(z,"POST",`/agents/runs/${encodeURIComponent(j)}/answer`,{request_id:E,answers:H,...D?{identity_id:D}:{}});await F(z,String(U.run_id??j))}function $(z,B){let j={identityIds:[],identityId:"",effort:"",prompt:"",noWait:!1,readStdin:!1},D=[];for(let E=0;E<z.length;E++){let G=z[E],H=()=>Q(z[++E],`${G} requires a value`);switch(G){case"--identity":case"-i":if(B)j.identityIds.push(H());else j.identityId=H();break;case"--effort":j.effort=H();break;case"--prompt":j.prompt=H();break;case"--stdin":j.readStdin=!0;break;case"--no-wait":j.noWait=!0;break;default:if(G.startsWith("-"))throw Error(`unknown option: ${G}`);D.push(G)}}if(j.identityIds=[...new Set(j.identityIds)],B&&j.identityIds.length===0)throw Error("run requires at least one --identity");if(!j.prompt&&D.length)j.prompt=D.join(" ");if(!j.prompt)j.readStdin=!0;return j}async function L(z){let B=z.readStdin?await W():"",j=z.prompt||B;if(!j.trim())throw Error("agent prompt is empty");return j}async function T(z,B,j){let D=String(B.run_id??"");if(!D)throw Error("agent API returned no run_id");if(j){process.stdout.write(`${JSON.stringify(B,null,2)}
|
|
6
|
+
`);return}await F(z,D)}async function F(z,B){let j=!1,D=()=>{if(j)return;j=!0,M(z,"DELETE",`/agents/runs/${encodeURIComponent(B)}`).then(()=>process.exit(130),(E)=>{process.stderr.write(`lodestar-agent: cancellation failed: ${C(E)}
|
|
7
|
+
`),process.exit(1)})};process.once("SIGINT",D),process.once("SIGTERM",D);try{while(!0){let E=await M(z,"GET",`/agents/runs/${encodeURIComponent(B)}`);if(E.status==="needs_input"||E.status==="completed"||E.status==="failed"||E.status==="cancelled"){if(process.stdout.write(O(E)),E.status==="failed"||E.status==="cancelled")process.exitCode=1;return}await new Promise((G)=>setTimeout(G,1000))}}finally{process.off("SIGINT",D),process.off("SIGTERM",D)}}function R(){if(process.env.DSH_LODESTAR_AGENT_CONTEXT!==void 0){let j=JSON.parse(process.env.DSH_LODESTAR_AGENT_CONTEXT);if(typeof j.baseUrl!=="string"||!j.baseUrl||typeof j.capability!=="string"||!j.capability)throw Error("invalid DSH Lodestar delegation context");return{baseUrl:j.baseUrl.replace(/\/+$/,""),capability:j.capability}}let z=String(process.env.LODESTAR_AGENT_URL??"").replace(/\/+$/,""),B=String(process.env.LODESTAR_AGENT_CAPABILITY??"");if(!z||!B)throw Error("lodestar-agent must run inside a Lodestar-managed Agent session (missing capability)");return{baseUrl:z,capability:B}}async function M(z,B,j,D){let E=await fetch(`${z.baseUrl}${j}`,{method:B,headers:{authorization:`Bearer ${z.capability}`,...D?{"content-type":"application/json"}:{}},...D?{body:JSON.stringify(D)}:{}}),G=await E.text(),H;try{H=JSON.parse(G)}catch{throw Error(`agent API ${B} ${j} returned HTTP ${E.status}: ${G||"(empty)"}`)}if(!E.ok)throw Error(H?.error??`agent API HTTP ${E.status}`);return H}function b(z){let B=[`catalog ${z.catalog_generation??"MISS"}`];for(let j of z.identities??[])B.push([j.status==="ready"?"✅":"MISS",j.id,j.display_name,j.model,`default-effort=${j.default_effort}`,j.source_default?"source-default":"",j.reason??""].filter(Boolean).join(" · "));for(let j of z.source_failures??[])B.push(`MISS · ${j.display} · ${j.reason}`);return`${B.join(`
|
|
8
8
|
`)}
|
|
9
|
-
`}function
|
|
9
|
+
`}function O(z){let B=[`# Lodestar agent ${z.run_id??"MISS"}`,"",`- Status: ${z.status??"MISS"}`,...z.parent_run_id?[`- Parent: ${z.parent_run_id} (${z.parent_kind??"delegate"})`]:[]];if(z.error)B.push(`- Error: ${z.error}`);for(let j of z.workers??[]){if(B.push("",`## ${j.identity_name??j.identity_id}`,"",`Status: ${j.status}`),j.session_id)B.push(`Session: ${j.session_id}`);if(j.error)B.push("",`Error: ${j.error}`);if(j.pending_input){B.push("",`Input request: ${j.pending_input.request_id}`);for(let D of j.pending_input.questions??[])if(B.push(`- [${D.id}] ${D.question}`),D.options?.length)B.push(` Options: ${D.options.map((E)=>E.label).join(" / ")}`);B.push("","Answer with:",`lodestar-agent answer ${z.run_id} --identity '${j.identity_id}' --request '${j.pending_input.request_id}' --stdin`)}if(j.output)B.push("",j.output)}if(z.presentation_errors?.length)B.push("","Presentation errors:",...z.presentation_errors.map((j)=>`- ${j}`));return`${B.join(`
|
|
10
10
|
`)}
|
|
11
|
-
`}async function
|
|
12
|
-
`)}function
|
|
13
|
-
`),process.exitCode=1});export{
|
|
11
|
+
`}async function W(){if(process.stdin.isTTY)return"";let z=[];for await(let B of process.stdin)z.push(Buffer.isBuffer(B)?B:Buffer.from(B));return Buffer.concat(z).toString("utf8")}function Q(z,B){if(!z?.trim())throw Error(B);return z.trim()}function _(){return["Usage:"," lodestar-agent identities [--json]"," lodestar-agent run --identity <id> [--identity <id>...] [--effort <level>] --stdin"," lodestar-agent follow-up <run_id> [--identity <id>] [--effort <level>] --stdin"," lodestar-agent answer <run_id> [--identity <id>] --request <id> (--answer key=value | --stdin)"," lodestar-agent status <run_id>"," lodestar-agent cancel <run_id>"].join(`
|
|
12
|
+
`)}function C(z){return z instanceof Error?z.message:String(z)}if(process.argv[1]&&Z(process.argv[1])===Z(P(import.meta.url)))J().catch((z)=>{process.stderr.write(`lodestar-agent: ${C(z)}
|
|
13
|
+
`),process.exitCode=1});export{$ as parsePromptArgs,J as main};
|
package/dist/lodestar-setup.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync as C,spawn as G}from"node:child_process";import{existsSync as B,mkdirSync as I,readFileSync as e}from"node:fs";import{createInterface as zz}from"node:readline/promises";import{delimiter as $z,dirname as qz,join as F}from"node:path";import{homedir as Jz}from"node:os";import{fileURLToPath as Qz}from"node:url";import{homedir as h}from"node:os";import{join as Y}from"node:path";var w=h(),g=process.platform==="win32";function S($,q,J){if($)return $;if(q)return Y(q,"lodestar");return J}function m(){if(g)return Y(process.env.APPDATA??Y(w,"AppData","Roaming"),"Lodestar");return Y(w,".config","lodestar")}function p(){if(g)return Y(process.env.LOCALAPPDATA??Y(w,"AppData","Local"),"Lodestar");return Y(w,".local","share","lodestar")}var v=S(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,m()),H=S(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,p()),O=process.env.LODESTAR_CONFIG??Y(v,"config.toml"),Lz=Y(H,"daemon.pid"),Mz=Y(H,"daemon.log"),Gz=Y(H,"session-chat-map.json"),kz=Y(H,"session-resume-map.json"),Oz=Y(H,"session-turns-map.json"),wz=Y(H,"session-model-map.json"),Ez=Y(H,"temp-session-leases.json"),xz=Y(H,"tasklist-map.json"),Bz=Y(H,"agent-runs"),Fz=Y(H,"agent-session-ids.json"),vz=Y(H,"managed-claude-plugin"),yz=Y(H,"dsh"),Rz=Y(H,"context-window-cache.json"),Tz=Y(H,"notify-callbacks.json"),uz=Y(H,"alive-on-shutdown.json"),gz=Y(H,"inbox"),Sz=Y(H,"debug.sock"),jz=Y(H,"debug-context.json");import{chmodSync as c,closeSync as y,fsyncSync as j,mkdirSync as l,openSync as P,renameSync as r,unlinkSync as s,writeFileSync as d}from"node:fs";import{randomUUID as a}from"node:crypto";import{dirname as _}from"node:path";var o=384;var i=448;function n($){let q=_($);l(q,{recursive:!0,mode:i})}function t($,q,J){n($);let Q=`${$}.${process.pid}.${a()}.tmp`,X=null;try{X=P(Q,"wx",J),d(X,q),j(X),y(X),X=null,r(Q,$);try{c($,J)}catch{}if(process.platform!=="win32"){let W=null;try{W=P(_($),"r"),j(W)}catch{}finally{if(W!==null)try{y(W)}catch{}}}}catch(W){if(X!==null)try{y(X)}catch{}try{s(Q)}catch{}throw W}}function R($,q){t($,q,o)}var z={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m",red:"\x1B[31m"},M=zz({input:process.stdin,output:process.stdout});async function L($,q={}){while(!0){let J=q.default?`${z.dim} [${q.default}]${z.reset}`:"",Q=(await M.question(`${z.cyan}? ${z.reset}${$}${J}
|
|
3
|
-
${
|
|
4
|
-
|
|
5
|
-
`)}function
|
|
6
|
-
${
|
|
7
|
-
|
|
8
|
-
`)
|
|
9
|
-
${
|
|
2
|
+
import{createRequire as K1}from"node:module";var Y1=Object.create;var{getPrototypeOf:H1,defineProperty:$0,getOwnPropertyNames:V1}=Object;var L1=Object.prototype.hasOwnProperty;function U1(q){return this[q]}var W1,B1,q0=(q,z,J)=>{var Q=q!=null&&typeof q==="object";if(Q){var Z=z?W1??=new WeakMap:B1??=new WeakMap,X=Z.get(q);if(X)return X}J=q!=null?Y1(H1(q)):{};let Y=z||!q||!q.__esModule?$0(J,"default",{value:q,enumerable:!0}):J;for(let H of V1(q))if(!L1.call(Y,H))$0(Y,H,{get:U1.bind(q,H),enumerable:!0});if(Q)Z.set(q,Y);return Y};var G=(q,z)=>()=>(z||q((z={exports:{}}).exports,z),z.exports);var F=K1(import.meta.url);var X0=G((y8,Z0)=>{Z0.exports=Q0;Q0.sync=G1;var z0=F("fs");function M1(q,z){var J=z.pathExt!==void 0?z.pathExt:process.env.PATHEXT;if(!J)return!0;if(J=J.split(";"),J.indexOf("")!==-1)return!0;for(var Q=0;Q<J.length;Q++){var Z=J[Q].toLowerCase();if(Z&&q.substr(-Z.length).toLowerCase()===Z)return!0}return!1}function J0(q,z,J){if(!q.isSymbolicLink()&&!q.isFile())return!1;return M1(z,J)}function Q0(q,z,J){z0.stat(q,function(Q,Z){J(Q,Q?!1:J0(Z,q,z))})}function G1(q,z){return J0(z0.statSync(q),q,z)}});var U0=G((j8,L0)=>{L0.exports=H0;H0.sync=N1;var Y0=F("fs");function H0(q,z,J){Y0.stat(q,function(Q,Z){J(Q,Q?!1:V0(Z,z))})}function N1(q,z){return V0(Y0.statSync(q),z)}function V0(q,z){return q.isFile()&&O1(q,z)}function O1(q,z){var{mode:J,uid:Q,gid:Z}=q,X=z.uid!==void 0?z.uid:process.getuid&&process.getuid(),Y=z.gid!==void 0?z.gid:process.getgid&&process.getgid(),H=parseInt("100",8),V=parseInt("010",8),U=parseInt("001",8),W=H|V,L=J&U||J&V&&Z===Y||J&H&&Q===X||J&W&&X===0;return L}});var B0=G((D8,W0)=>{var S8=F("fs"),R;if(process.platform==="win32"||global.TESTING_WINDOWS)R=X0();else R=U0();W0.exports=D;D.sync=k1;function D(q,z,J){if(typeof z==="function")J=z,z={};if(!J){if(typeof Promise!=="function")throw TypeError("callback not provided");return new Promise(function(Q,Z){D(q,z||{},function(X,Y){if(X)Z(X);else Q(Y)})})}R(q,z||{},function(Q,Z){if(Q){if(Q.code==="EACCES"||z&&z.ignoreErrors)Q=null,Z=!1}J(Q,Z)})}function k1(q,z){try{return R.sync(q,z||{})}catch(J){if(z&&z.ignoreErrors||J.code==="EACCES")return!1;else throw J}}});var F0=G((v8,k0)=>{var _=process.platform==="win32"||process.env.OSTYPE==="cygwin"||process.env.OSTYPE==="msys",K0=F("path"),F1=_?";":":",M0=B0(),G0=(q)=>Object.assign(Error(`not found: ${q}`),{code:"ENOENT"}),N0=(q,z)=>{let J=z.colon||F1,Q=q.match(/\//)||_&&q.match(/\\/)?[""]:[..._?[process.cwd()]:[],...(z.path||process.env.PATH||"").split(J)],Z=_?z.pathExt||process.env.PATHEXT||".EXE;.CMD;.BAT;.COM":"",X=_?Z.split(J):[""];if(_){if(q.indexOf(".")!==-1&&X[0]!=="")X.unshift("")}return{pathEnv:Q,pathExt:X,pathExtExe:Z}},O0=(q,z,J)=>{if(typeof z==="function")J=z,z={};if(!z)z={};let{pathEnv:Q,pathExt:Z,pathExtExe:X}=N0(q,z),Y=[],H=(U)=>new Promise((W,L)=>{if(U===Q.length)return z.all&&Y.length?W(Y):L(G0(q));let K=Q[U],O=/^".*"$/.test(K)?K.slice(1,-1):K,k=K0.join(O,q),S=!O&&/^\.[\\\/]/.test(q)?q.slice(0,2)+k:k;W(V(S,U,0))}),V=(U,W,L)=>new Promise((K,O)=>{if(L===Z.length)return K(H(W+1));let k=Z[L];M0(U+k,{pathExt:X},(S,X1)=>{if(!S&&X1)if(z.all)Y.push(U+k);else return K(U+k);return K(V(U,W,L+1))})});return J?H(0).then((U)=>J(null,U),J):H(0)},T1=(q,z)=>{z=z||{};let{pathEnv:J,pathExt:Q,pathExtExe:Z}=N0(q,z),X=[];for(let Y=0;Y<J.length;Y++){let H=J[Y],V=/^".*"$/.test(H)?H.slice(1,-1):H,U=K0.join(V,q),W=!V&&/^\.[\\\/]/.test(q)?q.slice(0,2)+U:U;for(let L=0;L<Q.length;L++){let K=W+Q[L];try{if(M0.sync(K,{pathExt:Z}))if(z.all)X.push(K);else return K}catch(O){}}}if(z.all&&X.length)return X;if(z.nothrow)return null;throw G0(q)};k0.exports=O0;O0.sync=T1});var _0=G((w8,v)=>{var T0=(q={})=>{let z=q.env||process.env;if((q.platform||process.platform)!=="win32")return"PATH";return Object.keys(z).reverse().find((Q)=>Q.toUpperCase()==="PATH")||"Path"};v.exports=T0;v.exports.default=T0});var I0=G((A8,b0)=>{var x0=F("path"),_1=F0(),x1=_0();function P0(q,z){let J=q.options.env||process.env,Q=process.cwd(),Z=q.options.cwd!=null,X=Z&&process.chdir!==void 0&&!process.chdir.disabled;if(X)try{process.chdir(q.options.cwd)}catch(H){}let Y;try{Y=_1.sync(q.command,{path:J[x1({env:J})],pathExt:z?x0.delimiter:void 0})}catch(H){}finally{if(X)process.chdir(Q)}if(Y)Y=x0.resolve(Z?q.options.cwd:"",Y);return Y}function P1(q){return P0(q)||P0(q,!0)}b0.exports=P1});var R0=G((R1,A)=>{var w=/([()\][%!^"`<>&|;, *?])/g;function b1(q){return q=q.replace(w,"^$1"),q}function I1(q,z){if(q=`${q}`,q=q.replace(/(?=(\\+?)?)\1"/g,"$1$1\\\""),q=q.replace(/(?=(\\+?)?)\1$/,"$1$1"),q=`"${q}"`,q=q.replace(w,"^$1"),z)q=q.replace(w,"^$1");return q}R1.command=b1;R1.argument=I1});var y0=G((E8,f0)=>{f0.exports=/^#!(.*)/});var S0=G((u8,j0)=>{var j1=y0();j0.exports=(q="")=>{let z=q.match(j1);if(!z)return null;let[J,Q]=z[0].replace(/#! ?/,"").split(" "),Z=J.split("/").pop();if(Z==="env")return Q;return Q?`${Z} ${Q}`:Z}});var v0=G((h8,D0)=>{var E=F("fs"),S1=S0();function D1(q){let J=Buffer.alloc(150),Q;try{Q=E.openSync(q,"r"),E.readSync(Q,J,0,150,0),E.closeSync(Q)}catch(Z){}return S1(J.toString())}D0.exports=D1});var u0=G((g8,E0)=>{var v1=F("path"),w0=I0(),A0=R0(),w1=v0(),A1=process.platform==="win32",E1=/\.(?:com|exe)$/i,u1=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function h1(q){q.file=w0(q);let z=q.file&&w1(q.file);if(z)return q.args.unshift(q.file),q.command=z,w0(q);return q.file}function g1(q){if(!A1)return q;let z=h1(q),J=!E1.test(z);if(q.options.forceShell||J){let Q=u1.test(z);q.command=v1.normalize(q.command),q.command=A0.command(q.command),q.args=q.args.map((X)=>A0.argument(X,Q));let Z=[q.command].concat(q.args).join(" ");q.args=["/d","/s","/c",`"${Z}"`],q.command=process.env.comspec||"cmd.exe",q.options.windowsVerbatimArguments=!0}return q}function c1(q,z,J){if(z&&!Array.isArray(z))J=z,z=null;z=z?z.slice(0):[],J=Object.assign({},J);let Q={command:q,args:z,options:J,file:void 0,original:{command:q,args:z}};return J.shell?Q:g1(Q)}E0.exports=c1});var c0=G((c8,g0)=>{var u=process.platform==="win32";function h(q,z){return Object.assign(Error(`${z} ${q.command} ENOENT`),{code:"ENOENT",errno:"ENOENT",syscall:`${z} ${q.command}`,path:q.command,spawnargs:q.args})}function m1(q,z){if(!u)return;let J=q.emit;q.emit=function(Q,Z){if(Q==="exit"){let X=h0(Z,z);if(X)return J.call(q,"error",X)}return J.apply(q,arguments)}}function h0(q,z){if(u&&q===1&&!z.file)return h(z.original,"spawn");return null}function l1(q,z){if(u&&q===1&&!z.file)return h(z.original,"spawnSync");return null}g0.exports={hookChildProcess:m1,verifyENOENT:h0,verifyENOENTSync:l1,notFoundError:h}});var m=G((m8,x)=>{var m0=F("child_process"),g=u0(),c=c0();function l0(q,z,J){let Q=g(q,z,J),Z=m0.spawn(Q.command,Q.args,Q.options);return c.hookChildProcess(Z,Q),Z}function C1(q,z,J){let Q=g(q,z,J),Z=m0.spawnSync(Q.command,Q.args,Q.options);return Z.error=Z.error||c.verifyENOENTSync(Z.status,Q),Z}x.exports=l0;x.exports.spawn=l0;x.exports.sync=C1;x.exports._parse=g;x.exports._enoent=c});var J1=q0(m(),1);import{execFileSync as B8,spawn as j}from"node:child_process";import{existsSync as a,mkdirSync as Q1,readFileSync as K8}from"node:fs";import{createInterface as M8}from"node:readline/promises";import{dirname as G8,join as e}from"node:path";import{homedir as N8}from"node:os";import{fileURLToPath as O8}from"node:url";import{homedir as d1}from"node:os";import{join as B}from"node:path";var f=d1(),C0=process.platform==="win32";function d0(q,z,J){if(q)return q;if(z)return B(z,"lodestar");return J}function p1(){if(C0)return B(process.env.APPDATA??B(f,"AppData","Roaming"),"Lodestar");return B(f,".config","lodestar")}function i1(){if(C0)return B(process.env.LOCALAPPDATA??B(f,"AppData","Local"),"Lodestar");return B(f,".local","share","lodestar")}var l=d0(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,p1()),M=d0(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,i1()),P=process.env.LODESTAR_CONFIG??B(l,"config.toml"),d8=B(M,"daemon.pid"),p8=B(M,"daemon.log"),i8=B(M,"session-chat-map.json"),n8=B(M,"session-resume-map.json"),r8=B(M,"session-turns-map.json"),s8=B(M,"session-model-map.json"),o8=B(M,"temp-session-leases.json"),t8=B(M,"tasklist-map.json"),a8=B(M,"agent-runs"),e8=B(M,"agent-session-ids.json"),$$=B(M,"managed-claude-plugin"),q$=B(M,"dsh"),C=B(M,"agent-runtimes"),z$=B(M,"context-window-cache.json"),J$=B(M,"notify-callbacks.json"),Q$=B(M,"alive-on-shutdown.json"),Z$=B(M,"inbox"),X$=B(M,"debug.sock"),Y$=B(M,"debug-context.json");import{chmodSync as n1,closeSync as d,fsyncSync as p0,mkdirSync as r1,openSync as i0,renameSync as s1,unlinkSync as o1,writeFileSync as t1}from"node:fs";import{randomUUID as a1}from"node:crypto";import{dirname as n0}from"node:path";var e1=384;var $8=448;function q8(q){let z=n0(q);r1(z,{recursive:!0,mode:$8})}function z8(q,z,J){q8(q);let Q=`${q}.${process.pid}.${a1()}.tmp`,Z=null;try{Z=i0(Q,"wx",J),t1(Z,z),p0(Z),d(Z),Z=null,s1(Q,q);try{n1(q,J)}catch{}if(process.platform!=="win32"){let X=null;try{X=i0(n0(q),"r"),p0(X)}catch{}finally{if(X!==null)try{d(X)}catch{}}}}catch(X){if(Z!==null)try{d(Z)}catch{}try{o1(Q)}catch{}throw X}}function p(q,z){z8(q,z,e1)}import{createHash as J8}from"node:crypto";var e0=q0(m(),1);import{existsSync as n,readFileSync as s0}from"node:fs";import{mkdir as o0,mkdtemp as Q8,readFile as t0,rename as a0,rm as i,writeFile as r}from"node:fs/promises";import{join as N,isAbsolute as Z8}from"node:path";import{fileURLToPath as X8,pathToFileURL as O$}from"node:url";var $1="https://registry.npmjs.org",b={codex:["@openai/codex"],claude:["@anthropic-ai/claude-code","@anthropic-ai/claude-agent-sdk","@anthropic-ai/sdk"],dsh:["@deepseek-ai/dsh","@deepseek-ai/dsh-llm-pi-ai","@deepseek-ai/dsh-tool-ask-user"]},Y8=(q)=>q instanceof Error?q.message:String(q);function q1(q,z=C){let J=N(z,q,"current.json");if(!n(J))return null;let Q=JSON.parse(s0(J,"utf8"));if(!Number.isFinite(Q.checkedAt)||Q.directory!==void 0&&!Z8(Q.directory))throw Error(`Invalid ${q} runtime state: ${J}`);return Q}function H8(q){let z=q1(q);if(z?.error)throw Error(`${q} 自动更新失败: ${z.error}`);if(!z?.directory)throw Error(`${q} runtime 未安装,请运行 lodestar-update --agents-only`);return z.directory}function s(q,z){let J=N(H8(q),"node_modules",".bin",`${z}${process.platform==="win32"?".cmd":""}`);if(!n(J))throw Error(`${q} executable missing: ${J}`);return J}async function V8(q,z,J){let Q=new AbortController,Z=()=>Q.abort(J?.reason);if(J?.addEventListener("abort",Z,{once:!0}),J?.aborted)Z();let X=setTimeout(()=>Q.abort(Error("npm registry request timed out")),30000);try{let Y=await fetch(`${$1}/${encodeURIComponent(q)}/${encodeURIComponent(z)}`,{signal:Q.signal});if(!Y.ok)throw Error(`${q}@${z}: npm HTTP ${Y.status}`);let H=await Y.json();if(H.name!==q||typeof H.version!=="string"||!H.version)throw Error(`Invalid npm manifest: ${q}@${z}`);return H}finally{clearTimeout(X),J?.removeEventListener("abort",Z)}}async function L8(q,z){let J={};if(q!=="dsh"){let Y=await Promise.all(b[q].map((H)=>z(H,"latest")));for(let H of Y)J[H.name]=H.version;return J}let Q=await z("@deepseek-ai/dsh","latest"),Z=new Set(b.dsh),X=new Set;while(Z.size){let Y=[...Z].slice(0,12);for(let V of Y)Z.delete(V),X.add(V);let H=await Promise.all(Y.map((V)=>V===Q.name?Q:z(V,Q.version)));for(let V of H){if(V.version!==Q.version)throw Error(`DSH release package mismatch: ${V.name}@${V.version}, expected ${Q.version}`);J[V.name]=V.version;for(let U of Object.keys({...V.dependencies,...V.peerDependencies,...V.optionalDependencies}))if(U.startsWith("@deepseek-ai/dsh")&&!X.has(U))Z.add(U)}}return J}function U8(q,z){return new Promise((J,Q)=>{let Z=e0.spawn("npm",["install","--prefix",q,"--include=optional","--no-fund","--no-audit",`--registry=${$1}`],{cwd:q,stdio:["ignore","pipe","pipe"],shell:!1}),X="",Y,H=(W)=>{X=(X+W.toString()).slice(-16384)};Z.stdout.on("data",H),Z.stderr.on("data",H);let V=()=>{Y=Error("Agent runtime install aborted"),Z.kill("SIGTERM")};if(z?.addEventListener("abort",V,{once:!0}),z?.aborted)V();let U=setTimeout(()=>{Y=Error("Agent runtime install timed out after 5 minutes"),Z.kill("SIGTERM")},300000);Z.on("error",(W)=>{Y=W}),Z.once("close",(W)=>{if(clearTimeout(U),z?.removeEventListener("abort",V),Y||W!==0)Q(Error(`${Y?.message??`npm install exited ${W}`}
|
|
3
|
+
${X}`));else J()})})}async function r0(q,z){let J=N(q,`current-${process.pid}-${Date.now()}.json`);await r(J,JSON.stringify(z)+`
|
|
4
|
+
`,{mode:384}),await a0(J,N(q,"current.json"))}async function W8(q,z){let J=N(q,"update.lock"),Q=Date.now()+360000;for(;;){z?.throwIfAborted();try{return await o0(J),await r(N(J,"pid"),String(process.pid)),()=>i(J,{recursive:!0})}catch(Z){if(Z.code!=="EEXIST")throw Z;try{let X=Number(await t0(N(J,"pid"),"utf8"));if(!Number.isSafeInteger(X)||X<=0)throw Error(`Invalid runtime update lock: ${J}`);try{process.kill(X,0)}catch(Y){if(Y.code!=="ESRCH")throw Y;await i(J,{recursive:!0});continue}}catch(X){if(X.code!=="ENOENT")throw X}if(Date.now()>=Q)throw Error(`Timed out waiting for runtime update lock: ${J}`);await new Promise((X)=>setTimeout(X,200))}}}async function o(q,z={}){let J=z.root??C,Q=N(J,q);await o0(Q,{recursive:!0,mode:448});let Z=await W8(Q,z.signal),X;try{z.report?.(`${q}: 检查 upstream latest`);let Y=q1(q,J),H=await L8(q,z.metadata??((K,O)=>V8(K,O,z.signal)));z.signal?.throwIfAborted();let V=JSON.parse(s0(X8(new URL("../package.json",import.meta.url)),"utf8")).overrides??{},U=J8("sha256").update(JSON.stringify({installFormat:2,versions:Object.entries(H).sort(),security:V,platform:process.platform,arch:process.arch})).digest("hex").slice(0,20),W=N(Q,U);if(!n(W)){X=await Q8(N(Q,".install-")),await r(N(X,"package.json"),JSON.stringify({name:`lodestar-runtime-${q}`,version:H[b[q][0]],private:!0,type:"module",dependencies:H,overrides:{...V,...q==="dsh"?H:{}}})+`
|
|
5
|
+
`,{mode:384}),await(z.install??U8)(X,z.signal);for(let[K,O]of Object.entries(H)){let k=JSON.parse(await t0(N(X,"node_modules",K,"package.json"),"utf8"));if(k.version!==O)throw Error(`npm installed ${K}@${k.version}, expected ${O}`)}z.signal?.throwIfAborted(),await a0(X,W),X=void 0}let L={directory:W,versions:H,checkedAt:Date.now()};return await r0(Q,L),z.report?.(`${q}: ${b[q][0]}@${H[b[q][0]]}${Y?.directory===W?" 已是 latest":" 已更新,新进程使用新版"}`),L}catch(Y){throw await r0(Q,{checkedAt:Date.now(),error:Y8(Y)}),Y}finally{try{if(X)await i(X,{recursive:!0})}finally{await Z()}}}var $={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m",red:"\x1B[31m"},I=M8({input:process.stdin,output:process.stdout});async function T(q,z={}){while(!0){let J=z.default?`${$.dim} [${z.default}]${$.reset}`:"",Q=(await I.question(`${$.cyan}? ${$.reset}${q}${J}
|
|
6
|
+
${$.green}>${$.reset} `)).trim();if(!Q&&z.default!==void 0)return z.default;if(!Q&&z.required){console.log(`${$.red}必填,请重新输入${$.reset}`);continue}return Q}}function k8(q){let z="═".repeat(58);console.log(`
|
|
7
|
+
${$.bold}${$.cyan}╔${z}╗${$.reset}`),console.log(`${$.bold}${$.cyan}║ ${q.padEnd(54)} ║${$.reset}`),console.log(`${$.bold}${$.cyan}╚${z}╝${$.reset}
|
|
8
|
+
`)}function y(q,z,J){console.log(`
|
|
9
|
+
${$.bold}${$.yellow}[${q}/${z}] ${J}${$.reset}
|
|
10
|
+
`)}function t(q){return q.replace(/\\/g,"\\\\").replace(/"/g,"\\\"")}function z1(q){try{let z=B8(q,["login","status"],{timeout:1e4,shell:process.platform==="win32"}).toString();return/Logged in using ChatGPT/i.test(z)}catch{return!1}}async function F8(q){return new Promise((z)=>{let J=J1.spawn(q,["login"],{stdio:"inherit",shell:!1});J.on("exit",(Q)=>z(Q===0)),J.on("error",()=>z(!1))})}function T8(){return process.env.CLAUDE_CONFIG_DIR||e(N8(),".claude")}async function _8(q){try{let z=await fetch("https://open.bigmodel.cn/api/anthropic/v1/models",{headers:{Authorization:`Bearer ${q}`},signal:AbortSignal.timeout(1e4)});if(!z.ok)return null;let J=await z.json(),Q=(Array.isArray(J?.data)?J.data:[]).map((L)=>typeof L?.display_name==="string"&&L.display_name?L.display_name:String(L?.id??"")).filter(Boolean);if(!Q.length)return null;let Z=(L)=>{let K=L.match(/(\d+(?:\.\d+)?)/);return K?Number(K[1]):-1},X=(L)=>L.replace(/-Turbo|-Air$/i,""),H=[...Q.filter((L)=>!/-Turbo|-Air/i.test(L))].sort((L,K)=>Z(K)-Z(L)),V=H[0],U=H[H.length-1],W={};if(V)W.ANTHROPIC_DEFAULT_OPUS_MODEL=`${V}[1m]`,W.ANTHROPIC_DEFAULT_SONNET_MODEL=`${V}[1m]`;if(U&&X(U)!==X(V))W.ANTHROPIC_DEFAULT_HAIKU_MODEL=U;return Object.keys(W).length?W:null}catch{return null}}async function x8(q){try{let z=T8();Q1(z,{recursive:!0});let J=e(z,"settings.json"),Q={};if(a(J)){let H=K8(J,"utf8");try{let V=JSON.parse(H);if(!V||typeof V!=="object"||Array.isArray(V))return{error:`${J} 内容不是 JSON 对象, 拒绝覆盖, 请手动检查`};Q=V}catch{return{error:`${J} 解析失败 (JSON 语法错), 拒绝覆盖, 请手动修复后重跑`}}}let Z=Q.env&&typeof Q.env==="object"&&!Array.isArray(Q.env)?Q.env:{},X={ANTHROPIC_AUTH_TOKEN:q,ANTHROPIC_BASE_URL:"https://open.bigmodel.cn/api/anthropic",API_TIMEOUT_MS:"3000000",CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:"1"},Y=await _8(q);if(Y)Object.assign(X,Y);return Q.env={...Z,...X},p(J,JSON.stringify(Q,null,2)+`
|
|
11
|
+
`),{path:J}}catch(z){return{error:z?.message??String(z)}}}function P8(q){try{if(process.platform==="win32")j(process.env.ComSpec??"cmd.exe",["/c","start",'""',q],{detached:!0,stdio:"ignore",windowsHide:!0}).unref();else if(process.platform==="darwin")j("open",[q],{detached:!0,stdio:"ignore"}).unref();else j("xdg-open",[q],{detached:!0,stdio:"ignore"}).unref()}catch{}}async function b8(q,z){try{let Q=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:q,app_secret:z}),signal:AbortSignal.timeout(15000)})).json();if(Q.tenant_access_token)return{ok:!0};return{ok:!1,error:`飞书拒绝: code=${Q.code} msg=${Q.msg??"(no msg)"}`}}catch(J){return{ok:!1,error:`网络错误: ${J?.message??String(J)}`}}}function I8(){try{let q=G8(O8(import.meta.url)),z=e(q,"lodestar.js");if(!a(z))return{error:`找不到 daemon bundle: ${z}`};let J=j(process.execPath,[z],{detached:!0,stdio:"ignore",windowsHide:!0});return J.unref(),{pid:J.pid}}catch(q){return{error:q?.message??String(q)}}}async function Z1(){if(!process.stdin.isTTY)console.error(`${$.red}lodestar-setup: stdin 不是 TTY,无法交互式输入。${$.reset}`),console.error("请直接在 cmd / PowerShell / Terminal 里跑,不要 pipe 或重定向 stdin。"),process.exit(1);if(a(P)){if(console.log(`${$.yellow}发现已有配置: ${P}${$.reset}`),(await T("覆盖? (y/N)",{default:"n"})).toLowerCase()!=="y"){console.log("已取消"),I.close();return}}k8("Lodestar 安装向导"),console.log("Lodestar 把 Feishu (飞书) 群聊接到 AI agent。"),console.log("默认后端是 Claude Code; Codex 是可选第二后端, 群里发 model 一键切换。"),console.log("每个群对应一个项目目录, agent 在那里跑、能读写文件。"),console.log(),console.log(`${$.yellow}⚠ Claude 官方限制:${$.reset}`),console.log(` ${$.bold}Claude 订阅 (Pro/Max OAuth 登录) 不支持本项目${$.reset},`),console.log(" 必须走 API 方式 —— 推荐 GLM Coding Plan, 或自备 Anthropic API key。"),console.log(),console.log("本向导依次做 4 件事:"),console.log(` ${$.dim}1) 确保 Claude Code CLI 已装好${$.reset}`),console.log(` ${$.dim}2) GLM Coding Plan API key (推荐, 可选) + Codex (可选)${$.reset}`),console.log(` ${$.dim}3) Feishu 自建应用 (含权限 / 事件 / 发版 + 凭据测试)${$.reset}`),console.log(` ${$.dim}4) 工作目录, 自动启动 daemon${$.reset}`),console.log(),await I.question(`${$.dim}按 Enter 开始 (Ctrl+C 退出)...${$.reset}`),y(1,4,"准备 Claude Code CLI"),await o("claude",{report:(L)=>console.log(L)}),console.log(`${$.green}✓ Claude Code / SDK 已更新${$.reset}: ${$.dim}${s("claude","claude")}${$.reset}`),console.log(),console.log(`${$.dim}下一步可选配 GLM Coding Plan 自动写入路由; 不配则用本机 Claude Code 现有配置。${$.reset}`),console.log(`${$.dim}记住: 别用 \`claude\` 走订阅 OAuth 登录 —— 订阅不支持本项目, 要用 API key。${$.reset}`),y(2,4,"GLM Coding Plan (推荐, 可选)"),console.log("GLM Coding Plan 给 Claude Code 接 GLM 系列模型 (支持 1M token 上下文, 中文友好)。"),console.log("订阅后在智谱开放平台拿一个 API key, 粘到下面 —— 向导自动写进 ~/.claude/settings.json。"),console.log(` ${$.dim}拿 key: https://open.bigmodel.cn → 控制台 → API Keys${$.reset}`),console.log(` ${$.dim}不给也行: 以本机 Claude Code 现有配置启动 (确保是 API key 方式, 非订阅)。${$.reset}`),console.log();let q=await T("GLM API key (直接回车跳过)",{});if(q){let L=await x8(q);if("path"in L)console.log(`${$.green}✓ GLM 路由已写入${$.reset}: ${$.dim}${L.path}${$.reset}`),console.log(`${$.dim}模型 slots 按端点当前列表自动选取(最新一代 opus/sonnet, 1M ctx)${$.reset}`);else console.log(`${$.red}✗ 写入失败:${$.reset} ${L.error}`),console.log(`${$.dim}跳过 GLM 配置, 以本机 Claude Code 现有配置启动。${$.reset}`)}else console.log(`${$.dim}已跳过 GLM, 以本机 Claude Code 现有配置启动。${$.reset}`);if(console.log(),console.log(`${$.bold}Codex (可选第二后端)${$.reset}`),console.log(` ${$.dim}想用 Codex 的话, 登录 ChatGPT 订阅即可, 模型/effort 走 ~/.codex/config.toml, 群里发 model 切换。${$.reset}`),(await T("现在顺便配置 Codex 后端吗?",{default:"n"})).toLowerCase()==="y"){await o("codex",{report:(K)=>console.log(K)});let L=s("codex","codex");if(z1(L))console.log(`${$.green}✓ Codex 已登录 ChatGPT${$.reset}`);else if(console.log(`${$.dim}启动 \`codex login\`...${$.reset}`),await F8(L)&&z1(L))console.log(`${$.green}✓ Codex 已登录 ChatGPT${$.reset}`);else console.log(`${$.yellow}Codex 登录未完成 — 不影响默认 Claude 后端; 需要时随时跑 codex login。${$.reset}`)}else console.log(`${$.dim}已跳过 Codex (需要时随时跑 codex login, 群里发 model 切)。${$.reset}`);y(3,4,"Feishu 自建应用");let J="https://open.feishu.cn/app";console.log("打开飞书开放平台 (浏览器):"),console.log(` ${$.cyan}${J}${$.reset}`),P8(J),console.log(`${$.dim}(如果浏览器没自动开, 复制上面 URL 粘到浏览器)${$.reset}`),console.log(),console.log(`${$.bold}详细操作步骤:${$.reset}`),console.log(),console.log(` ${$.bold}① 创建应用${$.reset}`),console.log(` 点 "创建企业自建应用", 填名字 (如 ${$.dim}Lodestar${$.reset}), logo 随意。`),console.log(),console.log(` ${$.bold}② 添加机器人能力${$.reset}`),console.log(` 左侧菜单 "${$.cyan}添加应用能力${$.reset}" → 找到 "机器人" → 点 "${$.bold}添加${$.reset}" 按钮。`),console.log(),console.log(` ${$.bold}③ 申请权限 (左侧 "${$.cyan}权限管理${$.reset}" → "${$.bold}开通权限${$.reset}")${$.reset}`),console.log(` ${$.yellow}缺一个都会让 daemon 启动后默默丢消息, 一定要全开。${$.reset}`),console.log(` ${$.dim}消息类:${$.reset}`),console.log(` • ${$.bold}im:message:send_as_bot${$.reset} ${$.dim}# 以机器人身份发消息${$.reset}`),console.log(` • ${$.bold}im:message${$.reset} ${$.dim}# 接收/操作消息 (核心)${$.reset}`),console.log(` • ${$.bold}im:chat${$.reset} ${$.dim}# 读/写群信息 (匹配群名 ↔ 项目目录)${$.reset}`),console.log(` • ${$.bold}im:chat:create${$.reset} ${$.dim}# wt: 自动创建 worktree 群${$.reset}`),console.log(` • ${$.bold}im:chat:delete${$.reset} ${$.dim}# wt: 解散 worktree 群${$.reset}`),console.log(` • ${$.bold}im:chat.members:read${$.reset} ${$.dim}# wt: 判断发起人是否已在群内${$.reset}`),console.log(` • ${$.bold}im:chat.members:write_only${$.reset} ${$.dim}# wt: 把发起人拉进已有 worktree 群${$.reset}`),console.log(` • ${$.bold}im:resource${$.reset} ${$.dim}# 上传/下载附件 (图文双向)${$.reset}`),console.log(` • ${$.bold}im:message.urgent${$.reset} ${$.dim}# 加急推送 (锁屏通知 / Ask)${$.reset}`),console.log(` • ${$.bold}im:message.group_msg${$.reset} ${$.dim}# 敏感: 接收群里所有消息${$.reset}`),console.log(` ${$.dim}└ 关键: 没它机器人只收 @ 自己的消息, 拿不到群里其他对话, 一定要开${$.reset}`),console.log(` ${$.dim}└ 敏感权限要走审批: 申请时填用途, 个人开发者通常秒过${$.reset}`),console.log(` • ${$.bold}im:message.group_at_msg:readonly${$.reset} ${$.dim}# 读 @ 机器人消息 (兜底)${$.reset}`),console.log(` ${$.dim}卡片类 (Card Kit):${$.reset}`),console.log(` • ${$.bold}cardkit:card:read${$.reset} ${$.dim}# 读卡片状态${$.reset}`),console.log(` • ${$.bold}cardkit:card:write${$.reset} ${$.dim}# 创建/更新卡片 (流式渲染核心)${$.reset}`),console.log(),console.log(` ${$.bold}④ 订阅事件 (左侧 "${$.cyan}事件与回调${$.reset}", 拆两个子页:)${$.reset}`),console.log(` ${$.dim}a)${$.reset} ${$.bold}事件配置${$.reset} 页:`),console.log(` ${$.yellow}• "订阅方式" → 选 "长连接" → 点保存${$.reset}`),console.log(` • 添加事件: ${$.bold}im.message.receive_v1${$.reset} ${$.dim}# 收群消息${$.reset}`),console.log(` ${$.dim}b)${$.reset} ${$.bold}回调配置${$.reset} 页:`),console.log(` ${$.yellow}• "订阅方式" → 选 "长连接" → 点保存${$.reset}`),console.log(` • 添加事件: ${$.bold}card.action.trigger${$.reset} ${$.dim}# 卡片按钮点击回调${$.reset}`),console.log(),console.log(` ${$.bold}⑤ 发布版本${$.reset}`),console.log(` 页面顶部 "${$.bold}创建版本${$.reset}" → 滚到底点 "${$.bold}保存${$.reset}" → 弹框点 "${$.bold}发布${$.reset}"。`),console.log(` ${$.yellow}没发版的应用收不到任何事件 — 这步九成新手会忘!${$.reset}`),console.log(),console.log(` ${$.bold}⑥ 拿凭据${$.reset}`),console.log(` 左侧 "凭证与基础信息" → 顶部 ${$.bold}App ID${$.reset} (${$.dim}cli_...${$.reset}) 和 ${$.bold}App Secret${$.reset}, 待会粘到下面。`),console.log(),console.log(` ${$.bold}⑦ 把机器人拉进群${$.reset}`),console.log(" 想用的飞书群 → 群设置 → 群机器人 → 添加机器人 → 选你的应用。"),console.log(` ${$.yellow}群名要等于 projects_root 下的项目目录名${$.reset} (下一步设, 默认是用户主目录)。`),console.log();let Q="",Z="";while(!0){Q=await T("App ID (以 cli_ 开头)",{required:!0}),Z=await T("App Secret",{required:!0}),console.log(`${$.dim}测试中... (调 tenant_access_token endpoint)${$.reset}`);let L=await b8(Q,Z);if(L.ok){console.log(`${$.green}✓ Feishu 凭据测试通过${$.reset}`);break}if(console.log(`${$.red}✗ 测试失败:${$.reset} ${L.error}`),console.log(`${$.dim}最常见原因: app_id / app_secret 抄错, 或应用还没 "发布上线" (步骤 ⑤)。${$.reset}`),console.log(),(await T("重新填? (Y/n)",{default:"y"})).toLowerCase()==="n")console.log(`${$.yellow}已取消, 配置未写盘。${$.reset}`),I.close(),process.exit(1)}y(4,4,"工作目录 + 启动"),console.log("每个 Feishu 群对应 projects_root 下同名的目录。"),console.log();let X=process.platform==="win32"?process.env.USERPROFILE??"C:\\Users\\Default":process.env.HOME??"/root",Y=await T("projects_root",{default:X});Q1(l,{recursive:!0});let H=["# Lodestar config — generated by `lodestar-setup`","# Edit by hand or re-run setup to overwrite.","","[feishu]",`app_id = "${t(Q)}"`,`app_secret = "${t(Z)}"`,"","[runtime]",`projects_root = "${t(Y)}"`,""];p(P,H.join(`
|
|
10
12
|
`)),console.log(`
|
|
11
|
-
${
|
|
12
|
-
${
|
|
13
|
-
lodestar-setup: ${
|
|
13
|
+
${$.green}${$.bold}✓ 配置已写入${$.reset}`),console.log(` ${$.cyan}${P}${$.reset}`),console.log(`
|
|
14
|
+
${$.bold}启动 daemon...${$.reset}`);let V=I8(),U=process.platform==="win32"?"\\":"/",W=process.platform==="win32"?`${process.env.LOCALAPPDATA??"%LOCALAPPDATA%"}\\Lodestar\\daemon-YYYY-MM-DD.log`:`${process.env.HOME??"~"}/.local/share/lodestar/daemon-YYYY-MM-DD.log`;if(V.pid)console.log(`${$.green}✓ daemon 已在后台启动${$.reset} (pid ${V.pid})`),console.log(),console.log(`${$.bold}最后一步: 在 Feishu 验证${$.reset}`),console.log(" ① 把机器人拉进任意飞书群"),console.log(` ② 群名 = ${$.cyan}${Y}${U}<群名>${$.reset} 下的目录名 (新群第一条消息会自动建)`),console.log(" ③ 在群里发任意一条消息, 默认由 Claude 接管"),console.log(` ${$.dim}(群里发 model 可选择已配置账号、动态模型和 effort)${$.reset}`),console.log(),console.log("日志 (按日滚动, 保留近 7 天):"),console.log(` ${$.cyan}${W}${$.reset}`),console.log(),console.log(`${$.dim}若长期跑后台, Linux 配 systemd --user,macOS 配 launchd,Windows 配任务计划程序。${$.reset}`);else console.log(`${$.yellow}启动失败: ${V.error}${$.reset}`),console.log(`手动运行: ${$.cyan}lodestar-daemon${$.reset}`);console.log(),I.close()}Z1().catch((q)=>{console.error(`
|
|
15
|
+
lodestar-setup: ${q?.message??q}`),process.exit(1)});
|
package/dist/lodestar-stop.js
CHANGED
|
@@ -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 q}from"node:path";var X=W(),B=process.platform==="win32";function b(
|
|
3
|
-
`),Q=parseInt((
|
|
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 q}from"node:path";var X=W(),B=process.platform==="win32";function b(K,Q,Y){if(K)return K;if(Q)return q(Q,"lodestar");return Y}function H(){if(B)return q(process.env.APPDATA??q(X,"AppData","Roaming"),"Lodestar");return q(X,".config","lodestar")}function V(){if(B)return q(process.env.LOCALAPPDATA??q(X,"AppData","Local"),"Lodestar");return q(X,".local","share","lodestar")}var w=b(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,H()),J=b(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,V()),x=process.env.LODESTAR_CONFIG??q(w,"config.toml"),Z=q(J,"daemon.pid"),S=q(J,"daemon.log"),E=q(J,"session-chat-map.json"),v=q(J,"session-resume-map.json"),h=q(J,"session-turns-map.json"),k=q(J,"session-model-map.json"),F=q(J,"temp-session-leases.json"),R=q(J,"tasklist-map.json"),T=q(J,"agent-runs"),m=q(J,"agent-session-ids.json"),C=q(J,"managed-claude-plugin"),l=q(J,"dsh"),c=q(J,"agent-runtimes"),j=q(J,"context-window-cache.json"),t=q(J,"notify-callbacks.json"),_=q(J,"alive-on-shutdown.json"),A=q(J,"inbox"),I=q(J,"debug.sock"),p=q(J,"debug-context.json");import{execSync as G}from"node:child_process";import{readFileSync as L,writeFileSync as o}from"node:fs";function M(K){try{if(process.platform==="linux")return L(`/proc/${K}/cmdline`,"utf8").replace(/\0/g," ").trim();if(process.platform==="darwin")return G(`ps -p ${K} -o args=`,{encoding:"utf8",timeout:2000}).trim();if(process.platform==="win32")return G(`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${K}').CommandLine"`,{encoding:"utf8",timeout:5000}).trim()||null;return null}catch{return null}}function N(K,Q){if(!Number.isFinite(K)||K<=0||!Q)return!1;let Y=M(K);if(!Y)return!1;if(process.platform==="win32")return Y.toLowerCase().includes(Q.toLowerCase());return Y.includes(Q)}var z={reset:"\x1B[0m",bold:"\x1B[1m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"};function P(K){return new Promise((Q)=>setTimeout(Q,K))}async function u(){if(!U(Z)){console.log(`${z.yellow}Lodestar daemon 未运行${z.reset} ${z.dim}(${Z} 不存在)${z.reset}`);return}let K=y(Z,"utf8").split(`
|
|
3
|
+
`),Q=parseInt((K[0]??"").trim(),10),Y=(K[1]??"").trim();if(!Number.isFinite(Q)||Q<=0)console.error(`${z.red}PID 文件格式坏:${z.reset} ${Z}`),process.exit(1);if(Y&&!N(Q,Y)){console.log(`${z.yellow}PID ${Q} 上没有 daemon (stale 文件)${z.reset}`),console.log(`${z.dim}手删 ${Z} 后再试${z.reset}`);return}console.log(`${z.bold}停止 daemon${z.reset} (pid ${Q})...`);try{if(process.platform==="win32")f(`taskkill /PID ${Q} /T /F`,{stdio:"ignore"});else process.kill(Q,"SIGTERM")}catch($){console.error(`${z.red}发信号失败:${z.reset} ${$?.message??$}`),process.exit(1)}for(let $=0;$<50;$++){if(!U(Z)){console.log(`${z.green}✓ daemon 已停${z.reset}`);return}await P(100)}console.log(`${z.yellow}已发 SIGTERM, 但 5s 内 daemon 没清掉 ${Z}${z.reset}`),console.log(`${z.dim}它可能还在收尾 (落盘 alive marker / 关 WS); 下次启动 pid-guard 会自动判别。${z.reset}`)}u().catch((K)=>{console.error(`${z.red}lodestar-stop:${z.reset} ${K?.message??K}`),process.exit(1)});
|
package/dist/lodestar-update.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn as g}from"node:child_process";import{existsSync as x}from"node:fs";import{homedir as p}from"node:os";import{join as s}from"node:path";var m=p(),a=process.platform==="win32";function $(t,l,r){if(t)return t;if(l)return s(l,"lodestar");return r}function u(){if(a)return s(process.env.APPDATA??s(m,"AppData","Roaming"),"Lodestar");return s(m,".config","lodestar")}function y(){if(a)return s(process.env.LOCALAPPDATA??s(m,"AppData","Local"),"Lodestar");return s(m,".local","share","lodestar")}var C=$(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,u()),e=$(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,y()),K=process.env.LODESTAR_CONFIG??s(C,"config.toml"),n=s(e,"daemon.pid"),f=s(e,"daemon.log"),S=s(e,"session-chat-map.json"),q=s(e,"session-resume-map.json"),z=s(e,"session-turns-map.json"),J=s(e,"session-model-map.json"),Q=s(e,"temp-session-leases.json"),Y=s(e,"tasklist-map.json"),Z=s(e,"agent-runs"),W=s(e,"agent-session-ids.json"),X=s(e,"managed-claude-plugin"),B=s(e,"dsh"),G=s(e,"context-window-cache.json"),N=s(e,"notify-callbacks.json"),U=s(e,"alive-on-shutdown.json"),i=s(e,"inbox"),F=s(e,"debug.sock"),h=s(e,"debug-context.json");var o={reset:"\x1B[0m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"},d=["@leviyuan/lodestar@latest","@openai/codex@latest","@anthropic-ai/claude-code@latest","@anthropic-ai/claude-agent-sdk@latest","@anthropic-ai/sdk@latest"];function b(){let t=process.platform==="win32"?"npm.cmd":"npm";return new Promise((l)=>{let r=g(t,["install","-g","--include=optional",...d],{stdio:"inherit",shell:process.platform==="win32"});r.on("exit",(c)=>l(c??1)),r.on("error",(c)=>{console.error(`${o.red}spawn npm 失败:${o.reset} ${c.message}`),l(1)})})}async function w(){console.log(`${o.bold}更新 Lodestar + Codex CLI + Claude Code / SDK${o.reset}`),console.log(`${o.dim}npm i -g ${d.join(" ")}${o.reset}
|
|
3
|
-
`);let
|
|
4
|
-
${
|
|
5
|
-
|
|
2
|
+
import{createRequire as a$}from"node:module";var d$=Object.create;var{getPrototypeOf:p$,defineProperty:h,getOwnPropertyNames:i$}=Object;var n$=Object.prototype.hasOwnProperty;function t$($){return this[$]}var s$,o$,r$=($,L,q)=>{var J=$!=null&&typeof $==="object";if(J){var Q=L?s$??=new WeakMap:o$??=new WeakMap,X=Q.get($);if(X)return X}q=$!=null?d$(p$($)):{};let Z=L||!$||!$.__esModule?h(q,"default",{value:$,enumerable:!0}):q;for(let z of i$($))if(!n$.call(Z,z))h(Z,z,{get:t$.bind($,z),enumerable:!0});if(J)Q.set($,Z);return Z};var M=($,L)=>()=>(L||$((L={exports:{}}).exports,L),L.exports);var F=a$(import.meta.url);var n=M((G0,i)=>{i.exports=p;p.sync=Q1;var g=F("fs");function J1($,L){var q=L.pathExt!==void 0?L.pathExt:process.env.PATHEXT;if(!q)return!0;if(q=q.split(";"),q.indexOf("")!==-1)return!0;for(var J=0;J<q.length;J++){var Q=q[J].toLowerCase();if(Q&&$.substr(-Q.length).toLowerCase()===Q)return!0}return!1}function d($,L,q){if(!$.isSymbolicLink()&&!$.isFile())return!1;return J1(L,q)}function p($,L,q){g.stat($,function(J,Q){q(J,J?!1:d(Q,$,L))})}function Q1($,L){return d(g.statSync($),$,L)}});var a=M((M0,r)=>{r.exports=s;s.sync=X1;var t=F("fs");function s($,L,q){t.stat($,function(J,Q){q(J,J?!1:o(Q,L))})}function X1($,L){return o(t.statSync($),L)}function o($,L){return $.isFile()&&Z1($,L)}function Z1($,L){var{mode:q,uid:J,gid:Q}=$,X=L.uid!==void 0?L.uid:process.getuid&&process.getuid(),Z=L.gid!==void 0?L.gid:process.getgid&&process.getgid(),z=parseInt("100",8),B=parseInt("010",8),V=parseInt("001",8),K=z|B,G=q&V||q&B&&Q===Z||q&z&&J===X||q&K&&X===0;return G}});var $$=M((O0,e)=>{var P0=F("fs"),C;if(process.platform==="win32"||global.TESTING_WINDOWS)C=n();else C=a();e.exports=_;_.sync=z1;function _($,L,q){if(typeof L==="function")q=L,L={};if(!q){if(typeof Promise!=="function")throw TypeError("callback not provided");return new Promise(function(J,Q){_($,L||{},function(X,Z){if(X)Q(X);else J(Z)})})}C($,L||{},function(J,Q){if(J){if(J.code==="EACCES"||L&&L.ignoreErrors)J=null,Q=!1}q(J,Q)})}function z1($,L){try{return C.sync($,L||{})}catch(q){if(L&&L.ignoreErrors||q.code==="EACCES")return!1;else throw q}}});var z$=M((f0,Z$)=>{var k=process.platform==="win32"||process.env.OSTYPE==="cygwin"||process.env.OSTYPE==="msys",L$=F("path"),B1=k?";":":",q$=$$(),J$=($)=>Object.assign(Error(`not found: ${$}`),{code:"ENOENT"}),Q$=($,L)=>{let q=L.colon||B1,J=$.match(/\//)||k&&$.match(/\\/)?[""]:[...k?[process.cwd()]:[],...(L.path||process.env.PATH||"").split(q)],Q=k?L.pathExt||process.env.PATHEXT||".EXE;.CMD;.BAT;.COM":"",X=k?Q.split(q):[""];if(k){if($.indexOf(".")!==-1&&X[0]!=="")X.unshift("")}return{pathEnv:J,pathExt:X,pathExtExe:Q}},X$=($,L,q)=>{if(typeof L==="function")q=L,L={};if(!L)L={};let{pathEnv:J,pathExt:Q,pathExtExe:X}=Q$($,L),Z=[],z=(V)=>new Promise((K,G)=>{if(V===J.length)return L.all&&Z.length?K(Z):G(J$($));let W=J[V],O=/^".*"$/.test(W)?W.slice(1,-1):W,f=L$.join(O,$),I=!O&&/^\.[\\\/]/.test($)?$.slice(0,2)+f:f;K(B(I,V,0))}),B=(V,K,G)=>new Promise((W,O)=>{if(G===Q.length)return W(z(K+1));let f=Q[G];q$(V+f,{pathExt:X},(I,g$)=>{if(!I&&g$)if(L.all)Z.push(V+f);else return W(V+f);return W(B(V,K,G+1))})});return q?z(0).then((V)=>q(null,V),q):z(0)},Y1=($,L)=>{L=L||{};let{pathEnv:q,pathExt:J,pathExtExe:Q}=Q$($,L),X=[];for(let Z=0;Z<q.length;Z++){let z=q[Z],B=/^".*"$/.test(z)?z.slice(1,-1):z,V=L$.join(B,$),K=!B&&/^\.[\\\/]/.test($)?$.slice(0,2)+V:V;for(let G=0;G<J.length;G++){let W=K+J[G];try{if(q$.sync(W,{pathExt:Q}))if(L.all)X.push(W);else return W}catch(O){}}}if(L.all&&X.length)return X;if(L.nothrow)return null;throw J$($)};Z$.exports=X$;X$.sync=Y1});var Y$=M((F0,T)=>{var B$=($={})=>{let L=$.env||process.env;if(($.platform||process.platform)!=="win32")return"PATH";return Object.keys(L).reverse().find((J)=>J.toUpperCase()==="PATH")||"Path"};T.exports=B$;T.exports.default=B$});var U$=M((k0,K$)=>{var H$=F("path"),H1=z$(),V1=Y$();function V$($,L){let q=$.options.env||process.env,J=process.cwd(),Q=$.options.cwd!=null,X=Q&&process.chdir!==void 0&&!process.chdir.disabled;if(X)try{process.chdir($.options.cwd)}catch(z){}let Z;try{Z=H1.sync($.command,{path:q[V1({env:q})],pathExt:L?H$.delimiter:void 0})}catch(z){}finally{if(X)process.chdir(J)}if(Z)Z=H$.resolve(Q?$.options.cwd:"",Z);return Z}function K1($){return V$($)||V$($,!0)}K$.exports=K1});var W$=M((G1,w)=>{var b=/([()\][%!^"`<>&|;, *?])/g;function U1($){return $=$.replace(b,"^$1"),$}function W1($,L){if($=`${$}`,$=$.replace(/(?=(\\+?)?)\1"/g,"$1$1\\\""),$=$.replace(/(?=(\\+?)?)\1$/,"$1$1"),$=`"${$}"`,$=$.replace(b,"^$1"),L)$=$.replace(b,"^$1");return $}G1.command=U1;G1.argument=W1});var M$=M((y0,G$)=>{G$.exports=/^#!(.*)/});var O$=M((x0,P$)=>{var O1=M$();P$.exports=($="")=>{let L=$.match(O1);if(!L)return null;let[q,J]=L[0].replace(/#! ?/,"").split(" "),Q=q.split("/").pop();if(Q==="env")return J;return J?`${Q} ${J}`:Q}});var F$=M((N0,f$)=>{var R=F("fs"),f1=O$();function F1($){let q=Buffer.alloc(150),J;try{J=R.openSync($,"r"),R.readSync(J,q,0,150,0),R.closeSync(J)}catch(Q){}return f1(q.toString())}f$.exports=F1});var N$=M((C0,x$)=>{var k1=F("path"),k$=U$(),y$=W$(),y1=F$(),x1=process.platform==="win32",N1=/\.(?:com|exe)$/i,C1=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function I1($){$.file=k$($);let L=$.file&&y1($.file);if(L)return $.args.unshift($.file),$.command=L,k$($);return $.file}function S1($){if(!x1)return $;let L=I1($),q=!N1.test(L);if($.options.forceShell||q){let J=C1.test(L);$.command=k1.normalize($.command),$.command=y$.command($.command),$.args=$.args.map((X)=>y$.argument(X,J));let Q=[$.command].concat($.args).join(" ");$.args=["/d","/s","/c",`"${Q}"`],$.command=process.env.comspec||"cmd.exe",$.options.windowsVerbatimArguments=!0}return $}function _1($,L,q){if(L&&!Array.isArray(L))q=L,L=null;L=L?L.slice(0):[],q=Object.assign({},q);let J={command:$,args:L,options:q,file:void 0,original:{command:$,args:L}};return q.shell?J:S1(J)}x$.exports=_1});var S$=M((I0,I$)=>{var D=process.platform==="win32";function j($,L){return Object.assign(Error(`${L} ${$.command} ENOENT`),{code:"ENOENT",errno:"ENOENT",syscall:`${L} ${$.command}`,path:$.command,spawnargs:$.args})}function T1($,L){if(!D)return;let q=$.emit;$.emit=function(J,Q){if(J==="exit"){let X=C$(Q,L);if(X)return q.call($,"error",X)}return q.apply($,arguments)}}function C$($,L){if(D&&$===1&&!L.file)return j(L.original,"spawn");return null}function b1($,L){if(D&&$===1&&!L.file)return j(L.original,"spawnSync");return null}I$.exports={hookChildProcess:T1,verifyENOENT:C$,verifyENOENTSync:b1,notFoundError:j}});var b$=M((S0,y)=>{var _$=F("child_process"),u=N$(),v=S$();function T$($,L,q){let J=u($,L,q),Q=_$.spawn(J.command,J.args,J.options);return v.hookChildProcess(Q,J),Q}function w1($,L,q){let J=u($,L,q),Q=_$.spawnSync(J.command,J.args,J.options);return Q.error=Q.error||v.verifyENOENTSync(Q.status,J),Q}y.exports=T$;y.exports.spawn=T$;y.exports.sync=w1;y.exports._parse=u;y.exports._enoent=v});import{spawn as m1}from"node:child_process";import{existsSync as g1}from"node:fs";import{homedir as e$}from"node:os";import{join as Y}from"node:path";var N=e$(),l=process.platform==="win32";function c($,L,q){if($)return $;if(L)return Y(L,"lodestar");return q}function $1(){if(l)return Y(process.env.APPDATA??Y(N,"AppData","Roaming"),"Lodestar");return Y(N,".config","lodestar")}function L1(){if(l)return Y(process.env.LOCALAPPDATA??Y(N,"AppData","Local"),"Lodestar");return Y(N,".local","share","lodestar")}var q1=c(process.env.LODESTAR_CONFIG_DIR,process.env.XDG_CONFIG_HOME,$1()),U=c(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,L1()),o1=process.env.LODESTAR_CONFIG??Y(q1,"config.toml"),m=Y(U,"daemon.pid"),r1=Y(U,"daemon.log"),a1=Y(U,"session-chat-map.json"),e1=Y(U,"session-resume-map.json"),$0=Y(U,"session-turns-map.json"),L0=Y(U,"session-model-map.json"),q0=Y(U,"temp-session-leases.json"),J0=Y(U,"tasklist-map.json"),Q0=Y(U,"agent-runs"),X0=Y(U,"agent-session-ids.json"),Z0=Y(U,"managed-claude-plugin"),z0=Y(U,"dsh"),S=Y(U,"agent-runtimes"),B0=Y(U,"context-window-cache.json"),Y0=Y(U,"notify-callbacks.json"),H0=Y(U,"alive-on-shutdown.json"),V0=Y(U,"inbox"),K0=Y(U,"debug.sock"),U0=Y(U,"debug-context.json");import{createHash as R1}from"node:crypto";var A$=r$(b$(),1);import{existsSync as D$,readFileSync as j$}from"node:fs";import{mkdir as u$,mkdtemp as D1,readFile as v$,rename as E$,rm as E,writeFile as A}from"node:fs/promises";import{join as P,isAbsolute as j1}from"node:path";import{fileURLToPath as u1,pathToFileURL as D0}from"node:url";var w$=["codex","claude","dsh"];var h$="https://registry.npmjs.org",x={codex:["@openai/codex"],claude:["@anthropic-ai/claude-code","@anthropic-ai/claude-agent-sdk","@anthropic-ai/sdk"],dsh:["@deepseek-ai/dsh","@deepseek-ai/dsh-llm-pi-ai","@deepseek-ai/dsh-tool-ask-user"]},l$=($)=>$ instanceof Error?$.message:String($);function v1($,L=S){let q=P(L,$,"current.json");if(!D$(q))return null;let J=JSON.parse(j$(q,"utf8"));if(!Number.isFinite(J.checkedAt)||J.directory!==void 0&&!j1(J.directory))throw Error(`Invalid ${$} runtime state: ${q}`);return J}async function E1($,L,q){let J=new AbortController,Q=()=>J.abort(q?.reason);if(q?.addEventListener("abort",Q,{once:!0}),q?.aborted)Q();let X=setTimeout(()=>J.abort(Error("npm registry request timed out")),30000);try{let Z=await fetch(`${h$}/${encodeURIComponent($)}/${encodeURIComponent(L)}`,{signal:J.signal});if(!Z.ok)throw Error(`${$}@${L}: npm HTTP ${Z.status}`);let z=await Z.json();if(z.name!==$||typeof z.version!=="string"||!z.version)throw Error(`Invalid npm manifest: ${$}@${L}`);return z}finally{clearTimeout(X),q?.removeEventListener("abort",Q)}}async function A1($,L){let q={};if($!=="dsh"){let Z=await Promise.all(x[$].map((z)=>L(z,"latest")));for(let z of Z)q[z.name]=z.version;return q}let J=await L("@deepseek-ai/dsh","latest"),Q=new Set(x.dsh),X=new Set;while(Q.size){let Z=[...Q].slice(0,12);for(let B of Z)Q.delete(B),X.add(B);let z=await Promise.all(Z.map((B)=>B===J.name?J:L(B,J.version)));for(let B of z){if(B.version!==J.version)throw Error(`DSH release package mismatch: ${B.name}@${B.version}, expected ${J.version}`);q[B.name]=B.version;for(let V of Object.keys({...B.dependencies,...B.peerDependencies,...B.optionalDependencies}))if(V.startsWith("@deepseek-ai/dsh")&&!X.has(V))Q.add(V)}}return q}function h1($,L){return new Promise((q,J)=>{let Q=A$.spawn("npm",["install","--prefix",$,"--include=optional","--no-fund","--no-audit",`--registry=${h$}`],{cwd:$,stdio:["ignore","pipe","pipe"],shell:!1}),X="",Z,z=(K)=>{X=(X+K.toString()).slice(-16384)};Q.stdout.on("data",z),Q.stderr.on("data",z);let B=()=>{Z=Error("Agent runtime install aborted"),Q.kill("SIGTERM")};if(L?.addEventListener("abort",B,{once:!0}),L?.aborted)B();let V=setTimeout(()=>{Z=Error("Agent runtime install timed out after 5 minutes"),Q.kill("SIGTERM")},300000);Q.on("error",(K)=>{Z=K}),Q.once("close",(K)=>{if(clearTimeout(V),L?.removeEventListener("abort",B),Z||K!==0)J(Error(`${Z?.message??`npm install exited ${K}`}
|
|
3
|
+
${X}`));else q()})})}async function R$($,L){let q=P($,`current-${process.pid}-${Date.now()}.json`);await A(q,JSON.stringify(L)+`
|
|
4
|
+
`,{mode:384}),await E$(q,P($,"current.json"))}async function l1($,L){let q=P($,"update.lock"),J=Date.now()+360000;for(;;){L?.throwIfAborted();try{return await u$(q),await A(P(q,"pid"),String(process.pid)),()=>E(q,{recursive:!0})}catch(Q){if(Q.code!=="EEXIST")throw Q;try{let X=Number(await v$(P(q,"pid"),"utf8"));if(!Number.isSafeInteger(X)||X<=0)throw Error(`Invalid runtime update lock: ${q}`);try{process.kill(X,0)}catch(Z){if(Z.code!=="ESRCH")throw Z;await E(q,{recursive:!0});continue}}catch(X){if(X.code!=="ENOENT")throw X}if(Date.now()>=J)throw Error(`Timed out waiting for runtime update lock: ${q}`);await new Promise((X)=>setTimeout(X,200))}}}async function c1($,L={}){let q=L.root??S,J=P(q,$);await u$(J,{recursive:!0,mode:448});let Q=await l1(J,L.signal),X;try{L.report?.(`${$}: 检查 upstream latest`);let Z=v1($,q),z=await A1($,L.metadata??((W,O)=>E1(W,O,L.signal)));L.signal?.throwIfAborted();let B=JSON.parse(j$(u1(new URL("../package.json",import.meta.url)),"utf8")).overrides??{},V=R1("sha256").update(JSON.stringify({installFormat:2,versions:Object.entries(z).sort(),security:B,platform:process.platform,arch:process.arch})).digest("hex").slice(0,20),K=P(J,V);if(!D$(K)){X=await D1(P(J,".install-")),await A(P(X,"package.json"),JSON.stringify({name:`lodestar-runtime-${$}`,version:z[x[$][0]],private:!0,type:"module",dependencies:z,overrides:{...B,...$==="dsh"?z:{}}})+`
|
|
5
|
+
`,{mode:384}),await(L.install??h1)(X,L.signal);for(let[W,O]of Object.entries(z)){let f=JSON.parse(await v$(P(X,"node_modules",W,"package.json"),"utf8"));if(f.version!==O)throw Error(`npm installed ${W}@${f.version}, expected ${O}`)}L.signal?.throwIfAborted(),await E$(X,K),X=void 0}let G={directory:K,versions:z,checkedAt:Date.now()};return await R$(J,G),L.report?.(`${$}: ${x[$][0]}@${z[x[$][0]]}${Z?.directory===K?" 已是 latest":" 已更新,新进程使用新版"}`),G}catch(Z){throw await R$(J,{checkedAt:Date.now(),error:l$(Z)}),Z}finally{try{if(X)await E(X,{recursive:!0})}finally{await Q()}}}async function c$($={}){let L=await Promise.allSettled(w$.map((J)=>c1(J,$))),q=[];for(let J=0;J<L.length;J++){let Q=L[J];if(Q.status==="rejected"){let X=Error(`${w$[J]}: ${l$(Q.reason)}`);$.report?.(`Agent 自动更新失败: ${X.message}`),q.push(X)}}if(q.length)throw AggregateError(q,q.map((J)=>J.message).join(`
|
|
6
|
+
`))}var H={reset:"\x1B[0m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"},m$=["@leviyuan/lodestar@latest"];function d1(){let $=process.platform==="win32"?"npm.cmd":"npm";return new Promise((L)=>{let q=m1($,["install","-g","--include=optional",...m$],{stdio:"inherit",shell:process.platform==="win32"});q.on("exit",(J)=>L(J??1)),q.on("error",(J)=>{console.error(`${H.red}spawn npm 失败:${H.reset} ${J.message}`),L(1)})})}async function p1(){let $=process.argv.includes("--agents-only");if(process.argv.slice(2).some((L)=>L!=="--agents-only"))throw Error("用法: lodestar-update [--agents-only]");if(console.log(`${H.bold}更新 ${$?"":"Lodestar + "}Codex、Claude Code/SDK、DSH${H.reset}`),!$){console.log(`${H.dim}npm i -g ${m$.join(" ")}${H.reset}
|
|
7
|
+
`);let L=await d1();if(L!==0)throw Error(`Lodestar 更新失败 (npm exit ${L})`)}if(await c$({report:(L)=>console.log(L)}),console.log(`
|
|
8
|
+
${H.green}✓ Agent 更新完成,新进程使用新版${H.reset}`),!$&&g1(m))console.log(),console.log(`${H.yellow}检测到 daemon 仍在跑老版本进程, 用新版本需要重启:${H.reset}`),console.log(` ${H.dim}# Linux systemd --user 托管的:${H.reset}`),console.log(` ${H.cyan}systemctl --user restart lodestar${H.reset} ${H.dim}# Linux${H.reset}`),console.log(` ${H.dim}# macOS launchd / Windows 任务计划:用对应服务管理器重启。${H.reset}`),console.log(` ${H.dim}# 或手动重启:${H.reset}`),console.log(` ${H.cyan}lodestar-stop && lodestar-daemon${H.reset}`)}p1().catch(($)=>{console.error(`${H.red}lodestar-update:${H.reset} ${$?.message??$}`),process.exit(1)});
|