@leviyuan/lodestar 0.16.6 → 0.17.0

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.
Files changed (22) hide show
  1. package/README.md +17 -4
  2. package/dist/dsh-bridge.js +394 -0
  3. package/dist/lodestar-agent.js +10 -10
  4. package/dist/lodestar-setup.js +1 -1
  5. package/dist/lodestar-stop.js +2 -2
  6. package/dist/lodestar-update.js +3 -3
  7. package/dist/lodestar.js +166 -158
  8. package/package.json +16 -5
  9. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/LICENSE +0 -0
  10. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/Readme.md +0 -0
  11. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/esm.mjs +0 -0
  12. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/index.js +0 -0
  13. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/lib/argument.js +0 -0
  14. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/lib/command.js +0 -0
  15. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/lib/error.js +0 -0
  16. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/lib/help.js +0 -0
  17. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/lib/option.js +0 -0
  18. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/lib/suggestSimilar.js +0 -0
  19. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/package-support.json +0 -0
  20. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/package.json +0 -0
  21. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/typings/esm.d.mts +0 -0
  22. /package/node_modules/{commander → speech-rule-engine/node_modules/commander}/typings/index.d.ts +0 -0
package/README.md CHANGED
@@ -10,14 +10,16 @@ AI 不是帮手,是倍率。它放大的不是体力,是你 —— 你的直觉
10
10
 
11
11
  ---
12
12
 
13
- 在飞书群里使用 Codex 和 Claude Code。每个群对应一个项目目录和会话,回复、工具调用、提问和后台任务通过飞书卡片展示。
13
+ 在飞书群里使用 Codex、Claude Code DeepSeek Harness。每个群对应一个项目目录和会话,回复、工具调用、提问和后台任务通过飞书卡片展示。
14
14
 
15
- 支持 Codex、GLM Coding Plan、DeepSeek 和 Claude native。账号、模型与 effort 可在群里切换,选择按群保存。
15
+ 支持 Codex、GLM Coding Plan、DeepSeek、Claude native DeepSeek Harness 原生后端。账号、模型与 effort 可在群里切换,选择按群保存。
16
16
 
17
17
  ## 安装
18
18
 
19
19
  支持 Windows、macOS 和 Linux,需要 Node.js ≥ 18.15。Bun 用于源码开发和构建。
20
20
 
21
+ DeepSeek Harness 子进程另需 Node 22.19+(22.x)或 Node 24+;可通过其账号配置的 `bin` 指定 Node 可执行文件。
22
+
21
23
  ```bash
22
24
  npm i -g @leviyuan/lodestar
23
25
  lodestar-setup
@@ -88,7 +90,7 @@ GLM 的套餐与用量显示在 `hi` 控制台,回复底部也会显示当前
88
90
 
89
91
  主 Agent 可以通过 `lodestar-agent` 查询实时身份,再把任务交给一个或多个模型。同一个任务选择多个身份时并发执行;后续追问可继续使用各模型的原生会话。
90
92
 
91
- 被调用的 Agent 可以编辑文件、执行命令、使用 MCP 和 Skill,也可以继续委派。它们与主 Agent 共享工作区,修改会立即可见。运行状态和结果通过卡片展示;需要用户输入时暂停,由主 Agent 回填答案后继续。委派产生的会话不会混入主群的 `rs` 历史列表。
93
+ 被调用的 Agent 可以编辑文件、执行命令、使用 MCP 和 Skill;委派只允许一层,被调用的 Agent 不能继续派工。它们与主 Agent 共享工作区,修改会立即可见。运行状态和结果通过卡片展示;需要用户输入时暂停,由主 Agent 回填答案后继续。委派产生的会话不会混入主群的 `rs` 历史列表。
92
94
 
93
95
  `lodestar-agent` 只能在 Lodestar 管理的 Agent 进程里调用。命令用法见 [Agent Skill](src/agent-skill.ts)。
94
96
 
@@ -130,7 +132,18 @@ bin = "/abs/path/to/claude-wrapper" # 可选的 Claude 可执行文件
130
132
 
131
133
  账号配置使用 `[token_source.glm]`、`[token_source.deepseek]` 等节。GLM 和 DeepSeek 也可以从本机 Claude settings 中识别;识别后由对应 Token Source 注入凭据,避免不同账号的环境变量串用。项目的工具限制只作用于主会话,委派 Agent 使用完整工具集。
132
134
 
133
- 手动修改配置后需重启 daemon;群内账号启用和模型补录会自行重载相关配置。模型路由、配置优先级和双后端差异见 [后端说明](docs/claude-agent-backend.md)。
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)。
134
147
 
135
148
  ## 开发
136
149
 
@@ -0,0 +1,394 @@
1
+ // src/dsh-bridge.ts
2
+ import { createInterface } from "node:readline";
3
+ import { randomUUID } from "node:crypto";
4
+ import { realpath } from "node:fs/promises";
5
+ import { createUserMessage, ReasoningEffortId } from "@deepseek-ai/dsh-llm";
6
+ import { SessionLogOffset } from "@deepseek-ai/dsh-session";
7
+ var name = "lodestar-bridge";
8
+ var inject = ["agents", "llm", "agentDefaultModel", "sessionPersistence", "tools", "systemPrompt", "userQuestions", "compaction", "shellEnv", "attachments", "sdkAppStartup"];
9
+ var PROVIDER = "deepseek-official";
10
+ var DELEGATION_TOOLS = ["subagent", "subagent_fork", "workflow", "ralph", "send_message"];
11
+ function apply(ctx) {
12
+ let root;
13
+ let rootId;
14
+ let initialized = false;
15
+ let closing = false;
16
+ const lifecycle = new AbortController;
17
+ let opening;
18
+ let selected;
19
+ let turnRoute;
20
+ let lastEnd;
21
+ let activeTurn = 0;
22
+ let turnFailure;
23
+ let settledSeq = -1;
24
+ let settling;
25
+ const questions = new Map;
26
+ const notify = (method, params) => {
27
+ process.stdout.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + `
28
+ `);
29
+ };
30
+ const fail = (error) => {
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ process.stderr.write(`lodestar-dsh: ${message}
33
+ `);
34
+ notify("failure", { message });
35
+ };
36
+ const live = () => {
37
+ if (!root || closing)
38
+ throw new Error("DSH session is not open");
39
+ return root.agent;
40
+ };
41
+ const delegationContext = process.env.DSH_LODESTAR_AGENT_CONTEXT;
42
+ delete process.env.DSH_LODESTAR_AGENT_CONTEXT;
43
+ ctx.shellEnv.register({
44
+ name: "lodestar-agent",
45
+ variables: { DSH_LODESTAR_AGENT_CONTEXT: { description: "Lodestar delegation context for the current root agent only." } },
46
+ resolve: (execution) => delegationContext && execution.agent === root?.agent ? { DSH_LODESTAR_AGENT_CONTEXT: delegationContext } : {}
47
+ });
48
+ const settle = () => {
49
+ if (root?.agent.status === "idle" && turnFailure && lastEnd?.data.turn !== activeTurn) {
50
+ const message = turnFailure;
51
+ turnFailure = undefined;
52
+ fail(new Error(`DSH turn could not be committed: ${message}`));
53
+ return;
54
+ }
55
+ if (settling || !lastEnd || !root || root.agent.status !== "idle" || lastEnd.seq <= settledSeq)
56
+ return;
57
+ const end = lastEnd;
58
+ settling = ctx.sessionPersistence.flush().then(() => {
59
+ settledSeq = end.seq;
60
+ notify("settled", { sessionId: rootId, event: end });
61
+ }).catch((error) => {
62
+ settledSeq = end.seq;
63
+ fail(error);
64
+ }).finally(() => {
65
+ settling = undefined;
66
+ settle();
67
+ });
68
+ };
69
+ ctx.on("session/event", (session, event) => {
70
+ notify("session.event", { sessionId: session.id, event });
71
+ if (session.id === rootId && event.type === "turn/start") {
72
+ turnRoute = selected;
73
+ if (root)
74
+ Object.assign(root.agent.options, selected);
75
+ activeTurn = event.data.turn;
76
+ turnFailure = undefined;
77
+ }
78
+ if (session.id === rootId && event.type === "turn/end") {
79
+ lastEnd = event;
80
+ queueMicrotask(settle);
81
+ }
82
+ });
83
+ ctx.on("agent/assistant-stream", ({ agent, frame }) => {
84
+ notify("assistant.frame", { sessionId: agent.session.id, frame });
85
+ });
86
+ ctx.on("agent/status", ({ agent, status }) => {
87
+ notify("session.status", { sessionId: agent.session.id, status });
88
+ if (agent.session.id === rootId && status === "idle")
89
+ settle();
90
+ });
91
+ ctx.on("agent/error", ({ agent, error }) => {
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ if (agent.session.id === rootId)
94
+ turnFailure = message;
95
+ notify("agent.error", { sessionId: agent.session.id, message });
96
+ });
97
+ ctx.on("agent/created", ({ agent }) => {
98
+ if (agent.session.id === rootId)
99
+ return;
100
+ agent.ctx.tools.restrict({ deny: DELEGATION_TOOLS });
101
+ notify("subagent.started", { sessionId: agent.session.id, parentSessionId: agent.session.header.parentSession });
102
+ });
103
+ ctx.on("agent/disposed", ({ agent }) => {
104
+ if (agent.session.id !== rootId)
105
+ notify("subagent.disposed", { sessionId: agent.session.id });
106
+ });
107
+ ctx.on("approval/request", async () => "allowed-once");
108
+ ctx.on("user-questions/request", (request) => {
109
+ if (request.agent !== root?.agent || closing)
110
+ throw new Error("DSH question is not owned by this session");
111
+ const id = randomUUID();
112
+ return new Promise((resolve, reject) => {
113
+ const abort = () => {
114
+ questions.delete(id);
115
+ notify("question.cancelled", { id });
116
+ reject(new Error("DSH question cancelled"));
117
+ };
118
+ if (request.signal?.aborted)
119
+ return abort();
120
+ questions.set(id, {
121
+ resolve: (answer) => {
122
+ request.signal?.removeEventListener("abort", abort);
123
+ resolve(answer);
124
+ },
125
+ reject: (error) => {
126
+ request.signal?.removeEventListener("abort", abort);
127
+ reject(error);
128
+ }
129
+ });
130
+ request.signal?.addEventListener("abort", abort, { once: true });
131
+ notify("question", { id, questions: request.questions });
132
+ });
133
+ });
134
+ async function catalog() {
135
+ const models = await ctx.llm.listModels(PROVIDER);
136
+ if (!models.length)
137
+ throw new Error("DSH model catalog is empty");
138
+ return Promise.all(models.map(async (model) => {
139
+ const info = await ctx.llm.resolveModelInfo(PROVIDER, model.id);
140
+ if (!info.reasoning?.efforts.length || !info.reasoning.defaultEffort) {
141
+ throw new Error(`DSH model ${model.id} has no reasoning catalog/default`);
142
+ }
143
+ return {
144
+ model: model.id,
145
+ display: model.name,
146
+ efforts: info.reasoning.efforts.map((e) => e.id),
147
+ defaultEffort: info.reasoning.defaultEffort,
148
+ contextWindow: info.context?.contextWindow ?? null,
149
+ isDefault: ctx.agentDefaultModel.currentSelection().provider === PROVIDER && ctx.agentDefaultModel.currentSelection().model === model.id
150
+ };
151
+ }));
152
+ }
153
+ 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
+ return ctx.llm.resolveCallConfig({ provider: PROVIDER, model, reasoningEffort: ReasoningEffortId(effort) });
158
+ }
159
+ async function open(input) {
160
+ if (root || rootId)
161
+ throw new Error("DSH process already owns a session");
162
+ if (await realpath(input.cwd) !== await realpath(process.cwd()))
163
+ throw new Error("DSH workspace differs from process cwd");
164
+ selected = await route(input.model, input.effort);
165
+ turnRoute = selected;
166
+ const setup = (agentCtx, agent) => {
167
+ agentCtx.on("system-prompt/assemble", async (_assembly, context, next) => {
168
+ const assembly = await next();
169
+ if (context.scope !== agent)
170
+ return assembly;
171
+ return { ...assembly, variables: { ...assembly.variables, provider: turnRoute.provider, model: turnRoute.model } };
172
+ });
173
+ agentCtx.on("agent/request", async (payload, next) => {
174
+ const resolved = await next();
175
+ return payload.agent === agent ? { ...resolved, ...turnRoute } : resolved;
176
+ });
177
+ if (!input.allowDelegation)
178
+ agentCtx.tools.restrict({ deny: DELEGATION_TOOLS });
179
+ if (input.allowedTools)
180
+ agentCtx.tools.restrict({ allow: input.allowedTools });
181
+ if (input.developerInstructions) {
182
+ agentCtx.systemPrompt.section({ name: "lodestar:instructions", order: 999, text: input.developerInstructions });
183
+ }
184
+ Object.assign(agent.options, selected);
185
+ };
186
+ const launch = input.launch;
187
+ if (!["fresh", "resume", "fork"].includes(launch.kind))
188
+ throw new Error("unknown DSH launch kind");
189
+ if (launch.kind !== "fresh") {
190
+ if (launch.source.provider !== "dsh" || launch.source.cwd !== input.cwd)
191
+ throw new Error("DSH source provider/cwd mismatch");
192
+ const snapshot = await ctx.sessionPersistence.stat(launch.source.sessionId);
193
+ if (!snapshot || !snapshot.header.cwd || await realpath(snapshot.header.cwd) !== await realpath(input.cwd)) {
194
+ throw new Error("DSH stored session missing or belongs to another workspace");
195
+ }
196
+ }
197
+ rootId = launch.kind === "resume" ? launch.source.sessionId : `session-${randomUUID()}`;
198
+ lastEnd = undefined;
199
+ try {
200
+ lifecycle.signal.throwIfAborted();
201
+ if (launch.kind === "resume") {
202
+ root = await ctx.agents.resume({ resumeSessionId: rootId, agentOptions: selected, setup, signal: lifecycle.signal });
203
+ } else if (launch.kind === "fork") {
204
+ const stored = await ctx.sessionPersistence.open(launch.source.sessionId, "read");
205
+ let events;
206
+ try {
207
+ events = (await stored.read()).events;
208
+ } finally {
209
+ await stored.close();
210
+ }
211
+ const at = launch.through;
212
+ if (at && (at.provider !== "dsh" || at.kind !== "event" || at.source.sessionId !== launch.source.sessionId || !/^\d+$/.test(at.id) || !Number.isSafeInteger(Number(at.id))))
213
+ throw new Error("invalid DSH fork checkpoint");
214
+ const boundary = at ? events.find((e) => e.seq === Number(at.id) && e.type === "turn/end") : [...events].reverse().find((e) => e.type === "turn/end");
215
+ if (!boundary)
216
+ throw new Error("DSH fork source has no completed turn at requested checkpoint");
217
+ const seed = events.slice(0, boundary.seq + 1);
218
+ root = await ctx.agents.create({
219
+ sessionId: rootId,
220
+ seed,
221
+ inheritedEventCount: SessionLogOffset(seed.length),
222
+ meta: { cwd: input.cwd, parentSession: launch.source.sessionId, isSeeded: true },
223
+ agentOptions: selected,
224
+ setup,
225
+ signal: lifecycle.signal
226
+ });
227
+ } else {
228
+ root = await ctx.agents.create({ sessionId: rootId, meta: { cwd: input.cwd }, agentOptions: selected, setup, signal: lifecycle.signal });
229
+ }
230
+ await ctx.sessionPersistence.flush();
231
+ lifecycle.signal.throwIfAborted();
232
+ return { sessionId: rootId, models: await catalog() };
233
+ } catch (error) {
234
+ if (!root)
235
+ rootId = undefined;
236
+ throw error;
237
+ }
238
+ }
239
+ async function dispatch(method, params) {
240
+ if (closing && method !== "shutdown")
241
+ throw new Error("DSH runtime is closing");
242
+ if (method === "initialize") {
243
+ if (initialized)
244
+ throw new Error("DSH already initialized");
245
+ await ctx.get("loader")?.await();
246
+ initialized = true;
247
+ return { protocolVersion: 1, runtimeVersion: "0.1.5-alpha.2" };
248
+ }
249
+ if (!initialized)
250
+ throw new Error("DSH not initialized");
251
+ switch (method) {
252
+ case "model/list":
253
+ return catalog();
254
+ case "session/open": {
255
+ if (opening)
256
+ throw new Error("DSH session initialization is already in progress");
257
+ opening = open(params);
258
+ try {
259
+ return await opening;
260
+ } finally {
261
+ opening = undefined;
262
+ }
263
+ }
264
+ case "session/list": {
265
+ if (await realpath(params.cwd) !== await realpath(process.cwd()))
266
+ throw new Error("DSH history cwd differs from process workspace");
267
+ const entries = await ctx.sessionPersistence.list();
268
+ const rows = [];
269
+ for (const { header } of [...entries].sort((a, b) => b.header.createdAt - a.header.createdAt)) {
270
+ if (header.origin === "subagent" || header.cwd !== params.cwd)
271
+ continue;
272
+ const stored = await ctx.sessionPersistence.open(header.id, "read");
273
+ let preview = header.id;
274
+ try {
275
+ const { events } = await stored.read(0, 256);
276
+ const input = events.find((e) => e.type === "user/message" && e.data.source.kind === "user");
277
+ if (input?.type === "user/message") {
278
+ const text = input.data.content.filter((part) => part.type === "text").map((part) => part.text).join(" ");
279
+ if (text.trim())
280
+ preview = text.replace(/\s+/g, " ").slice(0, 120);
281
+ }
282
+ } finally {
283
+ await stored.close();
284
+ }
285
+ rows.push({ provider: "dsh", sessionId: header.id, cwd: header.cwd, ts: header.createdAt, preview });
286
+ }
287
+ return rows.sort((a, b) => b.ts - a.ts);
288
+ }
289
+ case "session/model": {
290
+ live();
291
+ selected = await route(params.model, params.effort);
292
+ return {};
293
+ }
294
+ case "session/prompt": {
295
+ const agent = live();
296
+ if (!Array.isArray(params.content) || !params.content.length)
297
+ throw new Error("empty DSH prompt");
298
+ if (params.content.some((part) => part.type === "image")) {
299
+ const routeInfo = await ctx.llm.resolveModelInfo(PROVIDER, selected.model);
300
+ if (!routeInfo.inputModalities?.includes("image"))
301
+ throw new Error(`DSH model ${selected.model} does not accept images`);
302
+ }
303
+ const content = await ctx.attachments.admitPromptContent(params.content);
304
+ const message = createUserMessage({ content, source: { kind: "user" } });
305
+ const delivery = params.mode === "auto" ? agent.status === "running" ? "steer" : "queue" : params.mode;
306
+ if (delivery === "steer")
307
+ agent.steer(message);
308
+ else if (delivery === "queue")
309
+ agent.followup(message);
310
+ else
311
+ throw new Error("invalid DSH input mode");
312
+ return { messageId: message.id };
313
+ }
314
+ case "session/cancel":
315
+ live().cancel(Object.freeze({ kind: "user" }), { keepInbox: true });
316
+ return {};
317
+ case "question/answer": {
318
+ const pending = questions.get(params.id);
319
+ if (!pending)
320
+ throw new Error("DSH question no longer pending");
321
+ if (params.error)
322
+ pending.reject(new Error(String(params.error)));
323
+ else
324
+ pending.resolve({ answers: params.answers });
325
+ questions.delete(params.id);
326
+ return {};
327
+ }
328
+ case "session/compact": {
329
+ const agent = live();
330
+ if (agent.status !== "idle")
331
+ throw new Error("DSH compaction requires an idle session");
332
+ const result = await ctx.compaction.compactNow(agent, new AbortController().signal);
333
+ await ctx.sessionPersistence.flush();
334
+ return result;
335
+ }
336
+ case "shutdown": {
337
+ closing = true;
338
+ lifecycle.abort(Object.freeze({ kind: "disposed" }));
339
+ if (opening) {
340
+ try {
341
+ await opening;
342
+ } catch (error) {
343
+ process.stderr.write(`lodestar-dsh: initialization cancelled during shutdown: ${String(error)}
344
+ `);
345
+ }
346
+ }
347
+ for (const pending of questions.values())
348
+ pending.reject(new Error("DSH shutdown"));
349
+ questions.clear();
350
+ if (root) {
351
+ for (const agent of ctx.agents.list()) {
352
+ if (agent === root.agent || ctx.agents.isOwnedBy(agent.id, root.agent)) {
353
+ agent.cancel(Object.freeze({ kind: "disposed" }));
354
+ }
355
+ }
356
+ await root.dispose();
357
+ }
358
+ await settling;
359
+ await ctx.sessionPersistence.flush();
360
+ return {};
361
+ }
362
+ default:
363
+ throw new Error(`unknown Lodestar DSH method: ${method}`);
364
+ }
365
+ }
366
+ const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
367
+ lines.on("line", (line) => {
368
+ let request;
369
+ try {
370
+ request = JSON.parse(line);
371
+ if (request.jsonrpc !== "2.0" || typeof request.id !== "number" || typeof request.method !== "string") {
372
+ throw new Error("invalid Lodestar DSH request envelope");
373
+ }
374
+ } catch (error) {
375
+ fail(error);
376
+ return;
377
+ }
378
+ dispatch(request.method, request.params).then((result) => process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: request.id, result }) + `
379
+ `), (error) => process.stdout.write(JSON.stringify({
380
+ jsonrpc: "2.0",
381
+ id: request.id,
382
+ error: { code: -32603, message: error instanceof Error ? error.message : String(error) }
383
+ }) + `
384
+ `));
385
+ });
386
+ ctx.effect(() => () => {
387
+ lines.close();
388
+ });
389
+ }
390
+ export {
391
+ name,
392
+ inject,
393
+ apply
394
+ };
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import{resolve as E}from"node:path";import{fileURLToPath as b}from"node:url";async function C(e=process.argv.slice(2)){let n=e.shift()??"";if(!n||n==="help"||n==="--help"||n==="-h"){process.stdout.write(`${w()}
3
- `);return}let t=q();switch(n){case"identities":{let r=await d(t,"GET","/agents/identities");process.stdout.write(e.includes("--json")?`${JSON.stringify(r,null,2)}
4
- `:A(r));return}case"run":await P(t,e);return;case"follow-up":case"followup":await T(t,e);return;case"answer":await k(t,e);return;case"status":{let r=c(e[0],"status requires run_id"),s=await d(t,"GET",`/agents/runs/${encodeURIComponent(r)}`);if(process.stdout.write($(s)),s.status==="failed"||s.status==="cancelled")process.exitCode=1;return}case"cancel":{let r=c(e[0],"cancel requires run_id"),s=await d(t,"DELETE",`/agents/runs/${encodeURIComponent(r)}`);process.stdout.write(`${JSON.stringify(s)}
5
- `);return}default:throw Error(w())}}async function P(e,n){let t=g(n,!0),r=await m(t),s={identity_ids:t.identityIds,prompt:r,...t.effort?{effort:t.effort}:{}},i=await d(e,"POST","/agents/runs",s);await y(e,i,t.noWait)}async function T(e,n){let t=c(n.shift(),"follow-up requires run_id"),r=g(n,!1),i={prompt:await m(r),...r.identityId?{identity_id:r.identityId}:{},...r.effort?{effort:r.effort}:{}},o=await d(e,"POST",`/agents/runs/${encodeURIComponent(t)}/follow-up`,i);await y(e,o,r.noWait)}async function k(e,n){let t=c(n.shift(),"answer requires run_id"),r="",s="",i=!1,o={};for(let u=0;u<n.length;u++){let a=n[u],f=()=>c(n[++u],`${a} requires a value`);switch(a){case"--identity":case"-i":r=f();break;case"--request":s=f();break;case"--answer":{let p=f(),l=p.indexOf("=");if(l<=0)throw Error("--answer must be question-or-id=value");o[p.slice(0,l)]=p.slice(l+1);break}case"--stdin":i=!0;break;default:throw Error(`unknown answer option: ${a}`)}}if(i){let u=(await S()).trim(),a;try{a=JSON.parse(u)}catch{throw Error("answer stdin must be a JSON object")}if(!a||typeof a!=="object"||Array.isArray(a))throw Error("answer stdin must be a JSON object");for(let[f,p]of Object.entries(a))o[f]=String(p)}if(!s)throw Error("answer requires --request");if(Object.keys(o).length===0)throw Error("answer requires --answer or --stdin");let I=await d(e,"POST",`/agents/runs/${encodeURIComponent(t)}/answer`,{request_id:s,answers:o,...r?{identity_id:r}:{}});await h(e,String(I.run_id??t))}function g(e,n){let t={identityIds:[],identityId:"",effort:"",prompt:"",noWait:!1,readStdin:!1},r=[];for(let s=0;s<e.length;s++){let i=e[s],o=()=>c(e[++s],`${i} requires a value`);switch(i){case"--identity":case"-i":if(n)t.identityIds.push(o());else t.identityId=o();break;case"--effort":t.effort=o();break;case"--prompt":t.prompt=o();break;case"--stdin":t.readStdin=!0;break;case"--no-wait":t.noWait=!0;break;default:if(i.startsWith("-"))throw Error(`unknown option: ${i}`);r.push(i)}}if(t.identityIds=[...new Set(t.identityIds)],n&&t.identityIds.length===0)throw Error("run requires at least one --identity");if(!t.prompt&&r.length)t.prompt=r.join(" ");if(!t.prompt)t.readStdin=!0;return t}async function m(e){let n=e.readStdin?await S():"",t=e.prompt||n;if(!t.trim())throw Error("agent prompt is empty");return t}async function y(e,n,t){let r=String(n.run_id??"");if(!r)throw Error("agent API returned no run_id");if(t){process.stdout.write(`${JSON.stringify(n,null,2)}
6
- `);return}await h(e,r)}async function h(e,n){let t=!1,r=()=>{if(t)return;t=!0,d(e,"DELETE",`/agents/runs/${encodeURIComponent(n)}`).then(()=>process.exit(130),(s)=>{process.stderr.write(`lodestar-agent: cancellation failed: ${_(s)}
7
- `),process.exit(1)})};process.once("SIGINT",r),process.once("SIGTERM",r);try{while(!0){let s=await d(e,"GET",`/agents/runs/${encodeURIComponent(n)}`);if(s.status==="needs_input"||s.status==="completed"||s.status==="failed"||s.status==="cancelled"){if(process.stdout.write($(s)),s.status==="failed"||s.status==="cancelled")process.exitCode=1;return}await new Promise((i)=>setTimeout(i,1000))}}finally{process.off("SIGINT",r),process.off("SIGTERM",r)}}function q(){let e=String(process.env.LODESTAR_AGENT_URL??"").replace(/\/+$/,""),n=String(process.env.LODESTAR_AGENT_CAPABILITY??"");if(!e||!n)throw Error("lodestar-agent must run inside a Lodestar-managed Agent session (missing capability)");return{baseUrl:e,capability:n}}async function d(e,n,t,r){let s=await fetch(`${e.baseUrl}${t}`,{method:n,headers:{authorization:`Bearer ${e.capability}`,...r?{"content-type":"application/json"}:{}},...r?{body:JSON.stringify(r)}:{}}),i=await s.text(),o;try{o=JSON.parse(i)}catch{throw Error(`agent API ${n} ${t} returned HTTP ${s.status}: ${i||"(empty)"}`)}if(!s.ok)throw Error(o?.error??`agent API HTTP ${s.status}`);return o}function A(e){let n=[`catalog ${e.catalog_generation??"MISS"}`];for(let t of e.identities??[])n.push([t.status==="ready"?"✅":"MISS",t.id,t.display_name,t.model,`default-effort=${t.default_effort}`,t.source_default?"source-default":"",t.reason??""].filter(Boolean).join(" · "));for(let t of e.source_failures??[])n.push(`MISS · ${t.display} · ${t.reason}`);return`${n.join(`
2
+ import{resolve as E}from"node:path";import{fileURLToPath as b}from"node:url";async function T(e=process.argv.slice(2)){let n=e.shift()??"";if(!n||n==="help"||n==="--help"||n==="-h"){process.stdout.write(`${w()}
3
+ `);return}let t=k();switch(n){case"identities":{let r=await d(t,"GET","/agents/identities");process.stdout.write(e.includes("--json")?`${JSON.stringify(r,null,2)}
4
+ `:q(r));return}case"run":await C(t,e);return;case"follow-up":case"followup":await P(t,e);return;case"answer":await A(t,e);return;case"status":{let r=c(e[0],"status requires run_id"),s=await d(t,"GET",`/agents/runs/${encodeURIComponent(r)}`);if(process.stdout.write(S(s)),s.status==="failed"||s.status==="cancelled")process.exitCode=1;return}case"cancel":{let r=c(e[0],"cancel requires run_id"),s=await d(t,"DELETE",`/agents/runs/${encodeURIComponent(r)}`);process.stdout.write(`${JSON.stringify(s)}
5
+ `);return}default:throw Error(w())}}async function C(e,n){let t=g(n,!0),r=await y(t),s={identity_ids:t.identityIds,prompt:r,...t.effort?{effort:t.effort}:{}},i=await d(e,"POST","/agents/runs",s);await m(e,i,t.noWait)}async function P(e,n){let t=c(n.shift(),"follow-up requires run_id"),r=g(n,!1),i={prompt:await y(r),...r.identityId?{identity_id:r.identityId}:{},...r.effort?{effort:r.effort}:{}},o=await d(e,"POST",`/agents/runs/${encodeURIComponent(t)}/follow-up`,i);await m(e,o,r.noWait)}async function A(e,n){let t=c(n.shift(),"answer requires run_id"),r="",s="",i=!1,o={};for(let u=0;u<n.length;u++){let a=n[u],f=()=>c(n[++u],`${a} requires a value`);switch(a){case"--identity":case"-i":r=f();break;case"--request":s=f();break;case"--answer":{let p=f(),l=p.indexOf("=");if(l<=0)throw Error("--answer must be question-or-id=value");o[p.slice(0,l)]=p.slice(l+1);break}case"--stdin":i=!0;break;default:throw Error(`unknown answer option: ${a}`)}}if(i){let u=(await _()).trim(),a;try{a=JSON.parse(u)}catch{throw Error("answer stdin must be a JSON object")}if(!a||typeof a!=="object"||Array.isArray(a))throw Error("answer stdin must be a JSON object");for(let[f,p]of Object.entries(a))o[f]=String(p)}if(!s)throw Error("answer requires --request");if(Object.keys(o).length===0)throw Error("answer requires --answer or --stdin");let I=await d(e,"POST",`/agents/runs/${encodeURIComponent(t)}/answer`,{request_id:s,answers:o,...r?{identity_id:r}:{}});await h(e,String(I.run_id??t))}function g(e,n){let t={identityIds:[],identityId:"",effort:"",prompt:"",noWait:!1,readStdin:!1},r=[];for(let s=0;s<e.length;s++){let i=e[s],o=()=>c(e[++s],`${i} requires a value`);switch(i){case"--identity":case"-i":if(n)t.identityIds.push(o());else t.identityId=o();break;case"--effort":t.effort=o();break;case"--prompt":t.prompt=o();break;case"--stdin":t.readStdin=!0;break;case"--no-wait":t.noWait=!0;break;default:if(i.startsWith("-"))throw Error(`unknown option: ${i}`);r.push(i)}}if(t.identityIds=[...new Set(t.identityIds)],n&&t.identityIds.length===0)throw Error("run requires at least one --identity");if(!t.prompt&&r.length)t.prompt=r.join(" ");if(!t.prompt)t.readStdin=!0;return t}async function y(e){let n=e.readStdin?await _():"",t=e.prompt||n;if(!t.trim())throw Error("agent prompt is empty");return t}async function m(e,n,t){let r=String(n.run_id??"");if(!r)throw Error("agent API returned no run_id");if(t){process.stdout.write(`${JSON.stringify(n,null,2)}
6
+ `);return}await h(e,r)}async function h(e,n){let t=!1,r=()=>{if(t)return;t=!0,d(e,"DELETE",`/agents/runs/${encodeURIComponent(n)}`).then(()=>process.exit(130),(s)=>{process.stderr.write(`lodestar-agent: cancellation failed: ${$(s)}
7
+ `),process.exit(1)})};process.once("SIGINT",r),process.once("SIGTERM",r);try{while(!0){let s=await d(e,"GET",`/agents/runs/${encodeURIComponent(n)}`);if(s.status==="needs_input"||s.status==="completed"||s.status==="failed"||s.status==="cancelled"){if(process.stdout.write(S(s)),s.status==="failed"||s.status==="cancelled")process.exitCode=1;return}await new Promise((i)=>setTimeout(i,1000))}}finally{process.off("SIGINT",r),process.off("SIGTERM",r)}}function k(){if(process.env.DSH_LODESTAR_AGENT_CONTEXT!==void 0){let t=JSON.parse(process.env.DSH_LODESTAR_AGENT_CONTEXT);if(typeof t.baseUrl!=="string"||!t.baseUrl||typeof t.capability!=="string"||!t.capability)throw Error("invalid DSH Lodestar delegation context");return{baseUrl:t.baseUrl.replace(/\/+$/,""),capability:t.capability}}let e=String(process.env.LODESTAR_AGENT_URL??"").replace(/\/+$/,""),n=String(process.env.LODESTAR_AGENT_CAPABILITY??"");if(!e||!n)throw Error("lodestar-agent must run inside a Lodestar-managed Agent session (missing capability)");return{baseUrl:e,capability:n}}async function d(e,n,t,r){let s=await fetch(`${e.baseUrl}${t}`,{method:n,headers:{authorization:`Bearer ${e.capability}`,...r?{"content-type":"application/json"}:{}},...r?{body:JSON.stringify(r)}:{}}),i=await s.text(),o;try{o=JSON.parse(i)}catch{throw Error(`agent API ${n} ${t} returned HTTP ${s.status}: ${i||"(empty)"}`)}if(!s.ok)throw Error(o?.error??`agent API HTTP ${s.status}`);return o}function q(e){let n=[`catalog ${e.catalog_generation??"MISS"}`];for(let t of e.identities??[])n.push([t.status==="ready"?"✅":"MISS",t.id,t.display_name,t.model,`default-effort=${t.default_effort}`,t.source_default?"source-default":"",t.reason??""].filter(Boolean).join(" · "));for(let t of e.source_failures??[])n.push(`MISS · ${t.display} · ${t.reason}`);return`${n.join(`
8
8
  `)}
9
- `}function $(e){let n=[`# Lodestar agent ${e.run_id??"MISS"}`,"",`- Status: ${e.status??"MISS"}`,...e.parent_run_id?[`- Parent: ${e.parent_run_id} (${e.parent_kind??"delegate"})`]:[]];if(e.error)n.push(`- Error: ${e.error}`);for(let t of e.workers??[]){if(n.push("",`## ${t.identity_name??t.identity_id}`,"",`Status: ${t.status}`),t.session_id)n.push(`Session: ${t.session_id}`);if(t.error)n.push("",`Error: ${t.error}`);if(t.pending_input){n.push("",`Input request: ${t.pending_input.request_id}`);for(let r of t.pending_input.questions??[])if(n.push(`- [${r.id}] ${r.question}`),r.options?.length)n.push(` Options: ${r.options.map((s)=>s.label).join(" / ")}`);n.push("","Answer with:",`lodestar-agent answer ${e.run_id} --identity '${t.identity_id}' --request '${t.pending_input.request_id}' --stdin`)}if(t.output)n.push("",t.output)}if(e.presentation_errors?.length)n.push("","Presentation errors:",...e.presentation_errors.map((t)=>`- ${t}`));return`${n.join(`
9
+ `}function S(e){let n=[`# Lodestar agent ${e.run_id??"MISS"}`,"",`- Status: ${e.status??"MISS"}`,...e.parent_run_id?[`- Parent: ${e.parent_run_id} (${e.parent_kind??"delegate"})`]:[]];if(e.error)n.push(`- Error: ${e.error}`);for(let t of e.workers??[]){if(n.push("",`## ${t.identity_name??t.identity_id}`,"",`Status: ${t.status}`),t.session_id)n.push(`Session: ${t.session_id}`);if(t.error)n.push("",`Error: ${t.error}`);if(t.pending_input){n.push("",`Input request: ${t.pending_input.request_id}`);for(let r of t.pending_input.questions??[])if(n.push(`- [${r.id}] ${r.question}`),r.options?.length)n.push(` Options: ${r.options.map((s)=>s.label).join(" / ")}`);n.push("","Answer with:",`lodestar-agent answer ${e.run_id} --identity '${t.identity_id}' --request '${t.pending_input.request_id}' --stdin`)}if(t.output)n.push("",t.output)}if(e.presentation_errors?.length)n.push("","Presentation errors:",...e.presentation_errors.map((t)=>`- ${t}`));return`${n.join(`
10
10
  `)}
11
- `}async function S(){if(process.stdin.isTTY)return"";let e=[];for await(let n of process.stdin)e.push(Buffer.isBuffer(n)?n:Buffer.from(n));return Buffer.concat(e).toString("utf8")}function c(e,n){if(!e?.trim())throw Error(n);return e.trim()}function w(){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 _(e){return e instanceof Error?e.message:String(e)}if(process.argv[1]&&E(process.argv[1])===b(import.meta.url))C().catch((e)=>{process.stderr.write(`lodestar-agent: ${_(e)}
13
- `),process.exitCode=1});export{g as parsePromptArgs,C as main};
11
+ `}async function _(){if(process.stdin.isTTY)return"";let e=[];for await(let n of process.stdin)e.push(Buffer.isBuffer(n)?n:Buffer.from(n));return Buffer.concat(e).toString("utf8")}function c(e,n){if(!e?.trim())throw Error(n);return e.trim()}function w(){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 $(e){return e instanceof Error?e.message:String(e)}if(process.argv[1]&&E(process.argv[1])===b(import.meta.url))T().catch((e)=>{process.stderr.write(`lodestar-agent: ${$(e)}
13
+ `),process.exitCode=1});export{g as parsePromptArgs,T as main};
@@ -1,5 +1,5 @@
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,"context-window-cache.json"),Rz=Y(H,"notify-callbacks.json"),Tz=Y(H,"alive-on-shutdown.json"),uz=Y(H,"inbox"),gz=Y(H,"debug.sock"),Sz=Y(H,"debug-context.json");import{chmodSync as c,closeSync as y,fsyncSync as j,mkdirSync as l,openSync as _,renameSync as r,unlinkSync as s,writeFileSync as d}from"node:fs";import{randomUUID as a}from"node:crypto";import{dirname as P}from"node:path";var o=384;var i=448;function n($){let q=P($);l(q,{recursive:!0,mode:i})}function t($,q,J){n($);let Q=`${$}.${process.pid}.${a()}.tmp`,X=null;try{X=_(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}
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
3
  ${z.green}>${z.reset} `)).trim();if(!Q&&q.default!==void 0)return q.default;if(!Q&&q.required){console.log(`${z.red}必填,请重新输入${z.reset}`);continue}return Q}}function Yz($){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
5
  `)}function E($,q,J){console.log(`
@@ -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(J,K,Y){if(J)return J;if(K)return q(K,"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()),Q=b(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,V()),x=process.env.LODESTAR_CONFIG??q(w,"config.toml"),Z=q(Q,"daemon.pid"),S=q(Q,"daemon.log"),E=q(Q,"session-chat-map.json"),v=q(Q,"session-resume-map.json"),h=q(Q,"session-turns-map.json"),k=q(Q,"session-model-map.json"),R=q(Q,"temp-session-leases.json"),F=q(Q,"tasklist-map.json"),T=q(Q,"agent-runs"),m=q(Q,"agent-session-ids.json"),C=q(Q,"managed-claude-plugin"),j=q(Q,"context-window-cache.json"),l=q(Q,"notify-callbacks.json"),c=q(Q,"alive-on-shutdown.json"),_=q(Q,"inbox"),t=q(Q,"debug.sock"),A=q(Q,"debug-context.json");import{execSync as G}from"node:child_process";import{readFileSync as L,writeFileSync as D}from"node:fs";function M(J){try{if(process.platform==="linux")return L(`/proc/${J}/cmdline`,"utf8").replace(/\0/g," ").trim();if(process.platform==="darwin")return G(`ps -p ${J} -o args=`,{encoding:"utf8",timeout:2000}).trim();if(process.platform==="win32")return G(`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${J}').CommandLine"`,{encoding:"utf8",timeout:5000}).trim()||null;return null}catch{return null}}function N(J,K){if(!Number.isFinite(J)||J<=0||!K)return!1;let Y=M(J);if(!Y)return!1;if(process.platform==="win32")return Y.toLowerCase().includes(K.toLowerCase());return Y.includes(K)}var z={reset:"\x1B[0m",bold:"\x1B[1m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",dim:"\x1B[2m"};function P(J){return new Promise((K)=>setTimeout(K,J))}async function u(){if(!U(Z)){console.log(`${z.yellow}Lodestar daemon 未运行${z.reset} ${z.dim}(${Z} 不存在)${z.reset}`);return}let J=y(Z,"utf8").split(`
3
- `),K=parseInt((J[0]??"").trim(),10),Y=(J[1]??"").trim();if(!Number.isFinite(K)||K<=0)console.error(`${z.red}PID 文件格式坏:${z.reset} ${Z}`),process.exit(1);if(Y&&!N(K,Y)){console.log(`${z.yellow}PID ${K} 上没有 daemon (stale 文件)${z.reset}`),console.log(`${z.dim}手删 ${Z} 后再试${z.reset}`);return}console.log(`${z.bold}停止 daemon${z.reset} (pid ${K})...`);try{if(process.platform==="win32")f(`taskkill /PID ${K} /T /F`,{stdio:"ignore"});else process.kill(K,"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((J)=>{console.error(`${z.red}lodestar-stop:${z.reset} ${J?.message??J}`),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 q}from"node:path";var X=W(),B=process.platform==="win32";function b(J,Q,Y){if(J)return J;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()),K=b(process.env.LODESTAR_DATA_DIR,process.env.XDG_DATA_HOME,V()),x=process.env.LODESTAR_CONFIG??q(w,"config.toml"),Z=q(K,"daemon.pid"),S=q(K,"daemon.log"),E=q(K,"session-chat-map.json"),v=q(K,"session-resume-map.json"),h=q(K,"session-turns-map.json"),k=q(K,"session-model-map.json"),F=q(K,"temp-session-leases.json"),R=q(K,"tasklist-map.json"),T=q(K,"agent-runs"),m=q(K,"agent-session-ids.json"),C=q(K,"managed-claude-plugin"),l=q(K,"dsh"),j=q(K,"context-window-cache.json"),c=q(K,"notify-callbacks.json"),t=q(K,"alive-on-shutdown.json"),_=q(K,"inbox"),A=q(K,"debug.sock"),I=q(K,"debug-context.json");import{execSync as G}from"node:child_process";import{readFileSync as L,writeFileSync as D}from"node:fs";function M(J){try{if(process.platform==="linux")return L(`/proc/${J}/cmdline`,"utf8").replace(/\0/g," ").trim();if(process.platform==="darwin")return G(`ps -p ${J} -o args=`,{encoding:"utf8",timeout:2000}).trim();if(process.platform==="win32")return G(`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${J}').CommandLine"`,{encoding:"utf8",timeout:5000}).trim()||null;return null}catch{return null}}function N(J,Q){if(!Number.isFinite(J)||J<=0||!Q)return!1;let Y=M(J);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(J){return new Promise((Q)=>setTimeout(Q,J))}async function u(){if(!U(Z)){console.log(`${z.yellow}Lodestar daemon 未运行${z.reset} ${z.dim}(${Z} 不存在)${z.reset}`);return}let J=y(Z,"utf8").split(`
3
+ `),Q=parseInt((J[0]??"").trim(),10),Y=(J[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((J)=>{console.error(`${z.red}lodestar-stop:${z.reset} ${J?.message??J}`),process.exit(1)});
@@ -1,5 +1,5 @@
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 o}from"node:path";var m=p(),a=process.platform==="win32";function $(t,l,r){if(t)return t;if(l)return o(l,"lodestar");return r}function u(){if(a)return o(process.env.APPDATA??o(m,"AppData","Roaming"),"Lodestar");return o(m,".config","lodestar")}function y(){if(a)return o(process.env.LOCALAPPDATA??o(m,"AppData","Local"),"Lodestar");return o(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??o(C,"config.toml"),n=o(e,"daemon.pid"),f=o(e,"daemon.log"),S=o(e,"session-chat-map.json"),q=o(e,"session-resume-map.json"),z=o(e,"session-turns-map.json"),J=o(e,"session-model-map.json"),Q=o(e,"temp-session-leases.json"),Y=o(e,"tasklist-map.json"),Z=o(e,"agent-runs"),W=o(e,"agent-session-ids.json"),X=o(e,"managed-claude-plugin"),i=o(e,"context-window-cache.json"),B=o(e,"notify-callbacks.json"),G=o(e,"alive-on-shutdown.json"),N=o(e,"inbox"),U=o(e,"debug.sock"),F=o(e,"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"},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(`${s.red}spawn npm 失败:${s.reset} ${c.message}`),l(1)})})}async function w(){console.log(`${s.bold}更新 Lodestar + Codex CLI + Claude Code / SDK${s.reset}`),console.log(`${s.dim}npm i -g ${d.join(" ")}${s.reset}
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
3
  `);let t=await b();if(t!==0)console.error(`
4
- ${s.red}更新失败 (npm exit ${t})${s.reset}`),process.exit(t);if(console.log(`
5
- ${s.green}✓ 更新完成${s.reset}`),x(n))console.log(),console.log(`${s.yellow}检测到 daemon 仍在跑老版本进程, 用新版本需要重启:${s.reset}`),console.log(` ${s.dim}# Linux systemd --user 托管的:${s.reset}`),console.log(` ${s.cyan}systemctl --user restart lodestar${s.reset} ${s.dim}# Linux${s.reset}`),console.log(` ${s.dim}# macOS launchd / Windows 任务计划:用对应服务管理器重启。${s.reset}`),console.log(` ${s.dim}# 或手动重启:${s.reset}`),console.log(` ${s.cyan}lodestar-stop && lodestar-daemon${s.reset}`)}w().catch((t)=>{console.error(`${s.red}lodestar-update:${s.reset} ${t?.message??t}`),process.exit(1)});
4
+ ${o.red}更新失败 (npm exit ${t})${o.reset}`),process.exit(t);if(console.log(`
5
+ ${o.green}✓ 更新完成${o.reset}`),x(n))console.log(),console.log(`${o.yellow}检测到 daemon 仍在跑老版本进程, 用新版本需要重启:${o.reset}`),console.log(` ${o.dim}# Linux systemd --user 托管的:${o.reset}`),console.log(` ${o.cyan}systemctl --user restart lodestar${o.reset} ${o.dim}# Linux${o.reset}`),console.log(` ${o.dim}# macOS launchd / Windows 任务计划:用对应服务管理器重启。${o.reset}`),console.log(` ${o.dim}# 或手动重启:${o.reset}`),console.log(` ${o.cyan}lodestar-stop && lodestar-daemon${o.reset}`)}w().catch((t)=>{console.error(`${o.red}lodestar-update:${o.reset} ${t?.message??t}`),process.exit(1)});