c0de-agent 1.13.0 → 1.14.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.
- package/dist/cli/commands/serve.js +5 -1
- package/dist/cli/deps.d.ts +2 -4
- package/dist/cli/deps.js +22 -36
- package/dist/cli/modes/print.js +27 -17
- package/dist/core/agent.js +59 -36
- package/dist/core/agents/parallel.js +4 -2
- package/dist/core/config-path.d.ts +3 -1
- package/dist/core/config-path.js +33 -3
- package/dist/core/config.js +81 -0
- package/dist/core/context.js +6 -3
- package/dist/core/loop/persist.js +4 -1
- package/dist/core/loop/subagent.d.ts +19 -0
- package/dist/core/loop/subagent.js +73 -7
- package/dist/core/loop.d.ts +4 -1
- package/dist/core/loop.js +22 -6
- package/dist/{server/registry-config.d.ts → core/provider-registry.d.ts} +10 -2
- package/dist/core/provider-registry.js +66 -0
- package/dist/core/redact.d.ts +2 -1
- package/dist/core/redact.js +4 -2
- package/dist/core/secret.d.ts +5 -0
- package/dist/core/secret.js +14 -0
- package/dist/core/skills.d.ts +2 -0
- package/dist/core/skills.js +61 -0
- package/dist/core/slash.js +31 -18
- package/dist/core/title.js +4 -2
- package/dist/core/todo-tags.d.ts +4 -3
- package/dist/core/todo-tags.js +20 -6
- package/dist/core/workflow.d.ts +4 -13
- package/dist/core/workflow.js +6 -6
- package/dist/core/workflows/context.js +31 -59
- package/dist/core/workflows/runtime.js +10 -8
- package/dist/dap/index.d.ts +1 -0
- package/dist/dap/index.js +1 -0
- package/dist/dap/protocol.d.ts +8 -3
- package/dist/dap/protocol.js +54 -18
- package/dist/dap/session.js +71 -12
- package/dist/dap/spawn.d.ts +11 -0
- package/dist/dap/spawn.js +48 -0
- package/dist/dap/transport.js +18 -0
- package/dist/kanban/index.d.ts +1 -1
- package/dist/kanban/index.js +1 -1
- package/dist/kanban/store.d.ts +8 -1
- package/dist/kanban/store.js +61 -13
- package/dist/llm/protocols/openai-compat.d.ts +7 -0
- package/dist/llm/protocols/openai-compat.js +19 -2
- package/dist/llm/protocols/utils/tool-stream.d.ts +12 -3
- package/dist/llm/protocols/utils/tool-stream.js +47 -23
- package/dist/llm/provider.js +18 -3
- package/dist/llm/retry.d.ts +1 -0
- package/dist/llm/retry.js +20 -7
- package/dist/llm/routing.js +14 -2
- package/dist/llm/schema/messages.d.ts +9 -2
- package/dist/llm/token.d.ts +9 -0
- package/dist/llm/token.js +10 -6
- package/dist/llm/transport.js +7 -2
- package/dist/mcp/client.d.ts +11 -0
- package/dist/mcp/client.js +70 -0
- package/dist/mcp/index.d.ts +46 -0
- package/dist/mcp/index.js +106 -0
- package/dist/mcp/tool-adapter.d.ts +14 -0
- package/dist/mcp/tool-adapter.js +65 -0
- package/dist/mcp/transport.d.ts +35 -0
- package/dist/mcp/transport.js +243 -0
- package/dist/mcp/types.d.ts +55 -0
- package/dist/mcp/types.js +3 -0
- package/dist/plugins/init.d.ts +2 -0
- package/dist/plugins/init.js +10 -1
- package/dist/project/project.js +50 -3
- package/dist/project/resolve.d.ts +7 -0
- package/dist/project/resolve.js +52 -5
- package/dist/server/permission/interactive.js +9 -4
- package/dist/server/permission/store.js +8 -2
- package/dist/server/routes/auth.js +28 -13
- package/dist/server/routes/chat.js +92 -26
- package/dist/server/routes/config.js +38 -10
- package/dist/server/routes/files.js +75 -16
- package/dist/server/routes/filesystem.js +9 -2
- package/dist/server/routes/kanban.js +146 -9
- package/dist/server/routes/permissions.js +32 -0
- package/dist/server/routes/project.js +15 -6
- package/dist/server/routes/provider.js +10 -5
- package/dist/server/routes/session.js +72 -26
- package/dist/server/routes/terminal.js +10 -2
- package/dist/server/routes/todo.js +18 -0
- package/dist/server/routes/tool.js +13 -2
- package/dist/server/routes/update.js +5 -1
- package/dist/server/routes/workflows.js +17 -6
- package/dist/server/server.d.ts +1 -1
- package/dist/server/server.js +31 -17
- package/dist/server/terminal/pty-manager.d.ts +3 -0
- package/dist/server/terminal/pty-manager.js +8 -3
- package/dist/server/types.d.ts +2 -0
- package/dist/server/util/json-body.d.ts +31 -0
- package/dist/server/util/json-body.js +38 -0
- package/dist/session/archive.js +4 -4
- package/dist/session/branch.d.ts +10 -1
- package/dist/session/branch.js +54 -9
- package/dist/session/compaction.d.ts +5 -0
- package/dist/session/compaction.js +15 -10
- package/dist/session/context.d.ts +7 -3
- package/dist/session/context.js +35 -9
- package/dist/session/import.js +53 -6
- package/dist/session/index.d.ts +2 -2
- package/dist/session/index.js +2 -2
- package/dist/session/message.d.ts +12 -3
- package/dist/session/message.js +44 -4
- package/dist/session/session.d.ts +11 -5
- package/dist/session/session.js +99 -88
- package/dist/session/shake.js +35 -25
- package/dist/session/squash.js +3 -4
- package/dist/session/token.d.ts +7 -8
- package/dist/session/token.js +19 -16
- package/dist/session/usage.js +4 -2
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/round.d.ts +12 -0
- package/dist/shared/round.js +18 -0
- package/dist/shared/timer.d.ts +23 -0
- package/dist/shared/timer.js +27 -0
- package/dist/shared/types/tool.d.ts +4 -0
- package/dist/shared/utils/markdown-code.d.ts +15 -0
- package/dist/shared/utils/markdown-code.js +44 -0
- package/dist/shared/utils/path.d.ts +4 -1
- package/dist/shared/utils/path.js +5 -2
- package/dist/shared/utils/string.d.ts +5 -0
- package/dist/shared/utils/string.js +52 -0
- package/dist/tools/builtin/bash.js +79 -10
- package/dist/tools/builtin/edit.js +67 -8
- package/dist/tools/builtin/glob.js +150 -14
- package/dist/tools/builtin/grep.js +53 -11
- package/dist/tools/builtin/kanban.d.ts +14 -0
- package/dist/tools/builtin/kanban.js +4 -1
- package/dist/tools/builtin/read.js +15 -2
- package/dist/tools/builtin/resolvers.d.ts +9 -2
- package/dist/tools/builtin/resolvers.js +20 -6
- package/dist/tools/builtin/task.js +107 -25
- package/dist/tools/builtin/todo.js +84 -9
- package/dist/tools/builtin/write.js +7 -1
- package/dist/tools/builtin/yield.js +9 -1
- package/dist/tools/executor.js +53 -1
- package/dist/tools/hashline/patch.js +114 -38
- package/dist/tools/truncate.js +45 -16
- package/dist/tools/validate.js +66 -20
- package/dist/tools/websearch/index.js +8 -4
- package/dist/update/index.d.ts +1 -1
- package/dist/update/index.js +1 -1
- package/dist/update/scheduler.js +9 -2
- package/dist/update/snapshot.d.ts +14 -4
- package/dist/update/snapshot.js +22 -22
- package/dist/update/updater.d.ts +16 -2
- package/dist/update/updater.js +39 -8
- package/dist/update/version.d.ts +3 -1
- package/dist/update/version.js +63 -9
- package/package.json +1 -1
- package/dist/server/registry-config.js +0 -44
package/dist/update/snapshot.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm';
|
|
1
2
|
import { sessionEntries, sessions } from '../db/schema.js';
|
|
2
3
|
const CURRENT_SNAPSHOT_VERSION = '0.1.0';
|
|
3
4
|
function toDateMs(v) {
|
|
@@ -36,24 +37,6 @@ function toSerializedEntry(row) {
|
|
|
36
37
|
createdAt: toDateMs(row.createdAt),
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
|
-
/** 按 parentId 拓扑序排列 sessions,使父会话先于子会话插入(满足自引用 FK)。 */
|
|
40
|
-
function orderSessionsByParent(list) {
|
|
41
|
-
const byId = new Map(list.map((s) => [s.id, s]));
|
|
42
|
-
const ordered = [];
|
|
43
|
-
const seen = new Set();
|
|
44
|
-
const visit = (s) => {
|
|
45
|
-
if (seen.has(s.id))
|
|
46
|
-
return;
|
|
47
|
-
const parent = s.parentId ? byId.get(s.parentId) : undefined;
|
|
48
|
-
if (parent && !seen.has(parent.id))
|
|
49
|
-
visit(parent);
|
|
50
|
-
seen.add(s.id);
|
|
51
|
-
ordered.push(s);
|
|
52
|
-
};
|
|
53
|
-
for (const s of list)
|
|
54
|
-
visit(s);
|
|
55
|
-
return ordered;
|
|
56
|
-
}
|
|
57
40
|
/** 从 DB 导出所有会话与条目为可序列化快照。terminals 为活跃终端元信息(可选)。 */
|
|
58
41
|
async function serializeSessions(handle, config, terminals) {
|
|
59
42
|
const [sRows, eRows] = await Promise.all([
|
|
@@ -69,15 +52,27 @@ async function serializeSessions(handle, config, terminals) {
|
|
|
69
52
|
...(terminals && terminals.length > 0 ? { terminals } : {}),
|
|
70
53
|
};
|
|
71
54
|
}
|
|
72
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* 把快照导入 DB(保留原始 id 与时间戳),两阶段插入:
|
|
57
|
+
* 1. 全部会话行以 parentId=NULL 插入——任何插入顺序都满足自引用 FK,
|
|
58
|
+
* 无需拓扑排序;
|
|
59
|
+
* 2. 回填各行的 parentId——此时全部行已存在,FK 恒可满足。
|
|
60
|
+
*
|
|
61
|
+
* 此前按拓扑序逐个插入(orderSessionsByParent 先父后子):环防护只保证
|
|
62
|
+
* 「终止」——parentId 成环(a↔b 互指/自引用)时环上第一个被访问的节点先于
|
|
63
|
+
* 其父入序,插入它即撞自引用 FK 23503(其父行尚不存在),整个恢复抛错、
|
|
64
|
+
* serve 启动失败。两阶段插入对任意形状(环/自引用/正常森林)都成立,
|
|
65
|
+
* 环上节点父指针完整保留;悬空 parentId(快照外引用)在第二阶段撞 FK,
|
|
66
|
+
* 仍是「数据损坏显式失败」而非静默改写。
|
|
67
|
+
*/
|
|
73
68
|
async function restoreSessions(handle, snapshot) {
|
|
74
|
-
for (const s of
|
|
69
|
+
for (const s of snapshot.sessions) {
|
|
75
70
|
await handle.db
|
|
76
71
|
.insert(sessions)
|
|
77
72
|
.values({
|
|
78
73
|
id: s.id,
|
|
79
74
|
title: s.title,
|
|
80
|
-
parentId:
|
|
75
|
+
parentId: null,
|
|
81
76
|
projectId: s.projectId,
|
|
82
77
|
branchPoint: s.branchPoint,
|
|
83
78
|
metadata: s.metadata,
|
|
@@ -91,6 +86,11 @@ async function restoreSessions(handle, snapshot) {
|
|
|
91
86
|
})
|
|
92
87
|
.onConflictDoNothing();
|
|
93
88
|
}
|
|
89
|
+
for (const s of snapshot.sessions) {
|
|
90
|
+
if (s.parentId === null)
|
|
91
|
+
continue;
|
|
92
|
+
await handle.db.update(sessions).set({ parentId: s.parentId }).where(eq(sessions.id, s.id));
|
|
93
|
+
}
|
|
94
94
|
for (const e of snapshot.entries) {
|
|
95
95
|
await handle.db
|
|
96
96
|
.insert(sessionEntries)
|
|
@@ -107,4 +107,4 @@ async function restoreSessions(handle, snapshot) {
|
|
|
107
107
|
.onConflictDoNothing();
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
export {
|
|
110
|
+
export { restoreSessions, serializeSessions };
|
package/dist/update/updater.d.ts
CHANGED
|
@@ -77,6 +77,19 @@ type InstallMethod = {
|
|
|
77
77
|
declare function detectInstallMethod(): InstallMethod;
|
|
78
78
|
/** 手动安装提示命令(unknown 安装方式时返回给前端展示)。 */
|
|
79
79
|
declare function manualInstallCommand(pkg: string): string;
|
|
80
|
+
/** 当前执行程序文件的 stat 指纹(size+mtimeMs)。 */
|
|
81
|
+
type FileStatFingerprint = {
|
|
82
|
+
size: number;
|
|
83
|
+
mtimeMs: number;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* watch 当前执行程序文件(argv[1] 及其 realpath 所在目录)变更。
|
|
87
|
+
* 返回 Promise:文件内容变更(size/mtime 变化)后 resolve。
|
|
88
|
+
* 采用目录 fs.watch + 1s 轮询 stat 双保险(npm 原子替换文件时 watch 事件可能丢失)。
|
|
89
|
+
* baseline 由调用方在「变更发生前」采集(安装前),否则安装已改写文件导致永远等不到变化。
|
|
90
|
+
* 超时(timeoutMs)或可注入的 cancel 信号触发时 reject。
|
|
91
|
+
*/
|
|
92
|
+
declare function waitForProgramChange(timeoutMs: number, baseline: Map<string, FileStatFingerprint | null>, signal?: AbortSignal, pollMs?: number): Promise<void>;
|
|
80
93
|
/**
|
|
81
94
|
* 分阶段热更新的第二阶段(P0 修复:install 与 pause 解耦)。
|
|
82
95
|
*
|
|
@@ -109,7 +122,8 @@ declare function performHotUpdate(snapshot: SessionSnapshot, opts?: HotUpdateOpt
|
|
|
109
122
|
* 运行中的 serve 由 `/api/update/apply` 走 performHotUpdate 热更新路径。
|
|
110
123
|
*/
|
|
111
124
|
declare function performInstall(opts?: HotUpdateOptions): Promise<InstallOnlyResult>;
|
|
112
|
-
/**
|
|
125
|
+
/** 清理快照:仅当路径位于 writeSnapshot 创建的自有临时目录时整目录删除;
|
|
126
|
+
* 否则(调用方指定的路径)只删除快照文件本身,绝不递归删除父目录。 */
|
|
113
127
|
declare function cleanupSnapshot(snapshotPath: string): Promise<void>;
|
|
114
128
|
export type { HotUpdateOptions, HotUpdateResult, InstallMethod, InstallOnlyResult, SpawnFn };
|
|
115
|
-
export { cleanupSnapshot, detectInstallMethod, manualInstallCommand, performHandoff, performHotUpdate, performInstall, };
|
|
129
|
+
export { cleanupSnapshot, detectInstallMethod, manualInstallCommand, performHandoff, performHotUpdate, performInstall, waitForProgramChange, };
|
package/dist/update/updater.js
CHANGED
|
@@ -3,8 +3,14 @@ import { realpathSync, statSync, watch } from 'node:fs';
|
|
|
3
3
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { basename, dirname, join } from 'node:path';
|
|
6
|
+
import { clampTimerDelay } from '../shared/timer.js';
|
|
6
7
|
const DEFAULT_PACKAGE = 'c0de-agent';
|
|
7
8
|
const DEFAULT_MANUAL_WAIT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
9
|
+
/** writeSnapshot 经 mkdtemp 创建、归本模块所有的临时目录。
|
|
10
|
+
* 清理快照时只有这些目录允许整目录递归删除——调用方指定的 snapshotPath
|
|
11
|
+
* 只删文件本身,绝不删除其父目录(此前 rm(join(path,'..'), recursive) 对
|
|
12
|
+
* 任意调用方路径都会递归删除整个父目录,同目录下的无关文件一并蒸发)。 */
|
|
13
|
+
const ownedSnapshotDirs = new Set();
|
|
8
14
|
/**
|
|
9
15
|
* 识别当前进程的安装方式:
|
|
10
16
|
* - 入口脚本 realpath 含 node_modules/.pnpm → pnpm
|
|
@@ -40,11 +46,13 @@ function defaultInstall(pkg, method) {
|
|
|
40
46
|
const args = method.kind === 'pnpm' ? ['add', '-g', pkg] : ['install', '-g', pkg];
|
|
41
47
|
const child = spawn(cmd, args, { stdio: 'ignore' });
|
|
42
48
|
child.on('error', reject);
|
|
43
|
-
child.on('exit', (code) => {
|
|
49
|
+
child.on('exit', (code, signal) => {
|
|
44
50
|
if (code === 0)
|
|
45
51
|
resolve();
|
|
52
|
+
// 信号终止时 code 为 null(真实信号在 signal)——直接插值 code 会报
|
|
53
|
+
// 「exited with null」,用户无法区分「包管理器自杀」与「内部错误」。
|
|
46
54
|
else
|
|
47
|
-
reject(new Error(`${cmd} ${args.join(' ')} exited with ${code}`));
|
|
55
|
+
reject(new Error(`${cmd} ${args.join(' ')} ${signal !== null ? `killed by signal ${signal}` : `exited with ${code}`}`));
|
|
48
56
|
});
|
|
49
57
|
});
|
|
50
58
|
}
|
|
@@ -103,10 +111,17 @@ function waitForProgramChange(timeoutMs, baseline, signal, pollMs = 1000) {
|
|
|
103
111
|
let timer = null;
|
|
104
112
|
let interval = null;
|
|
105
113
|
const watchers = [];
|
|
114
|
+
// 监听器必须在终结处显式摘除:{ once: true } 只在「真的触发 abort」时
|
|
115
|
+
// 自动摘除——变更检测/超时终结的调用(绝大多数)从不触发 abort,残留
|
|
116
|
+
// 监听器在长生命周期信号复用场景下累积(第 11 个即触发 Node 的
|
|
117
|
+
// MaxListenersExceededWarning),闭包也随信号长期滞留(同型:executor/
|
|
118
|
+
// bash/permission checker 均已改为终结清理口径)。
|
|
119
|
+
const onAbort = () => finish(() => reject(new Error('waiting aborted')));
|
|
106
120
|
const finish = (fn) => {
|
|
107
121
|
if (settled)
|
|
108
122
|
return;
|
|
109
123
|
settled = true;
|
|
124
|
+
signal?.removeEventListener('abort', onAbort);
|
|
110
125
|
if (timer)
|
|
111
126
|
clearTimeout(timer);
|
|
112
127
|
if (interval)
|
|
@@ -129,7 +144,6 @@ function waitForProgramChange(timeoutMs, baseline, signal, pollMs = 1000) {
|
|
|
129
144
|
}
|
|
130
145
|
return false;
|
|
131
146
|
};
|
|
132
|
-
const onAbort = () => finish(() => reject(new Error('waiting aborted')));
|
|
133
147
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
134
148
|
timer = setTimeout(() => finish(() => reject(new Error('等待程序文件变更超时'))), timeoutMs);
|
|
135
149
|
interval = setInterval(() => {
|
|
@@ -190,7 +204,15 @@ function buildSpawnArgv(snapshotPath, opts) {
|
|
|
190
204
|
* 独立成函数供 performHotUpdate(一体流程)与 performHandoff(分阶段流程)共用。
|
|
191
205
|
*/
|
|
192
206
|
async function writeSnapshot(snapshot, opts) {
|
|
193
|
-
|
|
207
|
+
if (opts.snapshotPath) {
|
|
208
|
+
await writeFile(opts.snapshotPath, JSON.stringify(snapshot), 'utf8');
|
|
209
|
+
return opts.snapshotPath;
|
|
210
|
+
}
|
|
211
|
+
// 仅 mkdtemp 创建的目录归本模块所有(可整目录清理);调用方指定的路径
|
|
212
|
+
// 所有权在调用方,清理时只能删文件本身,绝不触碰其所在目录。
|
|
213
|
+
const dir = await mkdtemp(join(tmpdir(), 'c0de-update-'));
|
|
214
|
+
ownedSnapshotDirs.add(dir);
|
|
215
|
+
const snapshotPath = join(dir, 'snapshot.json');
|
|
194
216
|
await writeFile(snapshotPath, JSON.stringify(snapshot), 'utf8');
|
|
195
217
|
return snapshotPath;
|
|
196
218
|
}
|
|
@@ -275,7 +297,9 @@ async function performHotUpdate(snapshot, opts = {}) {
|
|
|
275
297
|
};
|
|
276
298
|
}
|
|
277
299
|
try {
|
|
278
|
-
|
|
300
|
+
// manualWaitTimeoutMs 是调用方(CLI/宿主)可传的公开选项:非法/超 32 位
|
|
301
|
+
// 上限的时长会被 Node 钳到 1ms——「等 10 分钟」变「立刻放弃等待」。
|
|
302
|
+
await waitForProgramChange(clampTimerDelay(opts.manualWaitTimeoutMs, DEFAULT_MANUAL_WAIT_TIMEOUT_MS, 1), baseline);
|
|
279
303
|
}
|
|
280
304
|
catch (error) {
|
|
281
305
|
return {
|
|
@@ -316,8 +340,15 @@ async function performInstall(opts = {}) {
|
|
|
316
340
|
}
|
|
317
341
|
return { _tag: 'success', installMethod: method.kind };
|
|
318
342
|
}
|
|
319
|
-
/**
|
|
343
|
+
/** 清理快照:仅当路径位于 writeSnapshot 创建的自有临时目录时整目录删除;
|
|
344
|
+
* 否则(调用方指定的路径)只删除快照文件本身,绝不递归删除父目录。 */
|
|
320
345
|
async function cleanupSnapshot(snapshotPath) {
|
|
321
|
-
|
|
346
|
+
const dir = dirname(snapshotPath);
|
|
347
|
+
if (ownedSnapshotDirs.has(dir)) {
|
|
348
|
+
ownedSnapshotDirs.delete(dir);
|
|
349
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
await rm(snapshotPath, { force: true }).catch(() => { });
|
|
322
353
|
}
|
|
323
|
-
export { cleanupSnapshot, detectInstallMethod, manualInstallCommand, performHandoff, performHotUpdate, performInstall, };
|
|
354
|
+
export { cleanupSnapshot, detectInstallMethod, manualInstallCommand, performHandoff, performHotUpdate, performInstall, waitForProgramChange, };
|
package/dist/update/version.d.ts
CHANGED
|
@@ -18,7 +18,9 @@ type CheckOptions = {
|
|
|
18
18
|
* src/dev 与 dist 两种布局下 `../../package.json` 都指向包根;读取失败回退常量。
|
|
19
19
|
*/
|
|
20
20
|
declare function getCurrentVersion(): string;
|
|
21
|
-
/** 语义化版本比较:a < b → -1,a == b → 0,a > b → 1
|
|
21
|
+
/** 语义化版本比较:a < b → -1,a == b → 0,a > b → 1。
|
|
22
|
+
* 前导 v 与 build metadata 忽略;prerelease 低于同 core 的正式版。
|
|
23
|
+
* 无法解析的版本保守返回 0(不误报更新)。 */
|
|
22
24
|
declare function compareSemver(a: string, b: string): number;
|
|
23
25
|
/** 查询 npm registry 判断是否有新版本。网络/解析失败时返回 hasUpdate:false,绝不抛错。 */
|
|
24
26
|
declare function checkForUpdate(opts?: CheckOptions): Promise<UpdateCheckResult>;
|
package/dist/update/version.js
CHANGED
|
@@ -17,23 +17,77 @@ function getCurrentVersion() {
|
|
|
17
17
|
}
|
|
18
18
|
return DEFAULT_VERSION;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/** 解析 semver:core(X.Y.Z 数值数组)+ prerelease 标识符数组。
|
|
21
|
+
* 前导 v 与 build metadata(+ 后缀)剥离;core 非「三个十进制非负整数」返回 null。
|
|
22
|
+
* 此前只取 split('-')[0]:prerelease 被静默丢弃(beta 与正式版判相等、beta 间
|
|
23
|
+
* 不分先后),且 '1.2.3+build' 的 patch 位解析为 NaN——NaN 参与比较恒返回 0,
|
|
24
|
+
* 带 build metadata 的版本与任何版本判相等、永不提示更新。 */
|
|
25
|
+
function parseSemver(v) {
|
|
26
|
+
const cleaned = v.trim().replace(/^v/, '');
|
|
27
|
+
const noBuild = cleaned.replace(/\+.*$/, '');
|
|
28
|
+
const dash = noBuild.indexOf('-');
|
|
29
|
+
const coreStr = dash === -1 ? noBuild : noBuild.slice(0, dash);
|
|
30
|
+
const coreParts = coreStr.split('.');
|
|
31
|
+
if (coreParts.length !== 3 || coreParts.some((p) => !/^\d+$/.test(p)))
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
core: coreParts.map(Number),
|
|
35
|
+
prerelease: dash === -1 ? [] : noBuild.slice(dash + 1).split('.'),
|
|
36
|
+
};
|
|
23
37
|
}
|
|
24
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* 比较 prerelease 标识符(semver §11 优先级规则):
|
|
40
|
+
* 数字标识符按数值、非数字按字典序,数字恒小于非数字;更短前缀为更小。
|
|
41
|
+
*/
|
|
42
|
+
function comparePrerelease(a, b) {
|
|
43
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
44
|
+
const x = a[i];
|
|
45
|
+
const y = b[i];
|
|
46
|
+
if (x === undefined)
|
|
47
|
+
return -1;
|
|
48
|
+
if (y === undefined)
|
|
49
|
+
return 1;
|
|
50
|
+
const xNum = /^\d+$/.test(x) ? Number(x) : null;
|
|
51
|
+
const yNum = /^\d+$/.test(y) ? Number(y) : null;
|
|
52
|
+
if (xNum !== null && yNum !== null) {
|
|
53
|
+
if (xNum !== yNum)
|
|
54
|
+
return xNum < yNum ? -1 : 1;
|
|
55
|
+
}
|
|
56
|
+
else if (xNum !== null) {
|
|
57
|
+
return -1;
|
|
58
|
+
}
|
|
59
|
+
else if (yNum !== null) {
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
else if (x !== y) {
|
|
63
|
+
return x < y ? -1 : 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
/** 语义化版本比较:a < b → -1,a == b → 0,a > b → 1。
|
|
69
|
+
* 前导 v 与 build metadata 忽略;prerelease 低于同 core 的正式版。
|
|
70
|
+
* 无法解析的版本保守返回 0(不误报更新)。 */
|
|
25
71
|
function compareSemver(a, b) {
|
|
26
|
-
const pa =
|
|
27
|
-
const pb =
|
|
72
|
+
const pa = parseSemver(a);
|
|
73
|
+
const pb = parseSemver(b);
|
|
74
|
+
if (!pa || !pb)
|
|
75
|
+
return 0;
|
|
28
76
|
for (let i = 0; i < 3; i++) {
|
|
29
|
-
const x = pa[i] ?? 0;
|
|
30
|
-
const y = pb[i] ?? 0;
|
|
77
|
+
const x = pa.core[i] ?? 0;
|
|
78
|
+
const y = pb.core[i] ?? 0;
|
|
31
79
|
if (x < y)
|
|
32
80
|
return -1;
|
|
33
81
|
if (x > y)
|
|
34
82
|
return 1;
|
|
35
83
|
}
|
|
36
|
-
|
|
84
|
+
if (pa.prerelease.length === 0 && pb.prerelease.length === 0)
|
|
85
|
+
return 0;
|
|
86
|
+
if (pa.prerelease.length === 0)
|
|
87
|
+
return 1;
|
|
88
|
+
if (pb.prerelease.length === 0)
|
|
89
|
+
return -1;
|
|
90
|
+
return comparePrerelease(pa.prerelease, pb.prerelease);
|
|
37
91
|
}
|
|
38
92
|
/** 查询 npm registry 判断是否有新版本。网络/解析失败时返回 hasUpdate:false,绝不抛错。 */
|
|
39
93
|
async function checkForUpdate(opts = {}) {
|
package/package.json
CHANGED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// src/server/registry-config.ts
|
|
2
|
-
// config.providers ↔ LLM registry 的构建/同步逻辑(P1-1 从 server.ts 拆出,
|
|
3
|
-
// 供 chat 路由按项目配置构建注册表,避免 routes → server.ts 循环依赖)。
|
|
4
|
-
import { decryptSecret } from '../core/secret.js';
|
|
5
|
-
import { createRegistry, overrideToCapabilities, rebuildRegistry, registerProvider, } from '../llm/registry.js';
|
|
6
|
-
/** 把 config.providers 注册到新建的 LLM registry(修复此前空 registry 的遗漏)。 */
|
|
7
|
-
function buildRegistryFromConfig(config) {
|
|
8
|
-
const registry = createRegistry();
|
|
9
|
-
for (const p of config.providers) {
|
|
10
|
-
registerProviderFromConfig(registry, p);
|
|
11
|
-
}
|
|
12
|
-
return registry;
|
|
13
|
-
}
|
|
14
|
-
function registerProviderFromConfig(registry, p) {
|
|
15
|
-
// 兼容 config.json 中以 _tag 标识 provider 的格式(name 缺失时回退到 _tag)
|
|
16
|
-
const name = p.name || p._tag;
|
|
17
|
-
if (!name || !p.baseURL)
|
|
18
|
-
return;
|
|
19
|
-
// baseURL 已含 /v1 时用 /chat/completions,避免 /v1/v1 双重前缀
|
|
20
|
-
const path = p.baseURL.replace(/\/+$/, '').endsWith('/v1') ? '/chat/completions' : undefined;
|
|
21
|
-
registerProvider(registry, {
|
|
22
|
-
name,
|
|
23
|
-
baseURL: p.baseURL,
|
|
24
|
-
apiKey: p.apiKey ? decryptSecret(p.apiKey) : p.apiKey,
|
|
25
|
-
...(path ? { path } : {}),
|
|
26
|
-
// 传递用户配置的 per-model capabilities(contextWindow 等),
|
|
27
|
-
// 否则 resolveRoute 回退到 DEFAULT_MODEL_CAPABILITIES,可能导致预算过小。
|
|
28
|
-
...(p.models ? { models: overrideToCapabilities(p.models) } : {}),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* config 变更后原子地同步 registry:在隔离的 next registry 上重建全部路由,
|
|
33
|
-
* 完成后一次性替换 registry 内部 table 引用。运行中的 resolveRoute 任何时刻
|
|
34
|
-
* 看到的都是完整的旧表或完整的新表,不会读到「已清空但未注册完」的半状态,
|
|
35
|
-
* 因此不会把本可用的 provider 误判为 NoRoute。ServerContext 立即生效,无需重启。
|
|
36
|
-
*/
|
|
37
|
-
function syncRegistryFromConfig(registry, config) {
|
|
38
|
-
rebuildRegistry(registry, (next) => {
|
|
39
|
-
for (const p of config.providers) {
|
|
40
|
-
registerProviderFromConfig(next, p);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
export { buildRegistryFromConfig, registerProviderFromConfig, syncRegistryFromConfig };
|