@router-agent/infra 0.0.0-stage → 0.3.31

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/chat.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 通用对话补全客户端。
3
+ *
4
+ * 只依赖两种常见协议的 HTTP 接口,不引入新 SDK:
5
+ * - `openai-completions`:POST `{baseUrl}/chat/completions`,密钥走 `Authorization: Bearer`;
6
+ * - `anthropic-messages`:POST `{baseUrl}/v1/messages`,密钥走 `x-api-key`
7
+ * (配置了 `authHeader` 时按 `ANTHROPIC_AUTH_TOKEN` 的语义改成 `Authorization: Bearer`)。
8
+ *
9
+ * 支持工具调用(tool use):模型可以要求执行只读工具(读配置、读本地代码),
10
+ * 再基于工具结果给结论——这正是「排查助手要能看本机代码与配置」需要的形态。
11
+ */
12
+ export type ChatApi = 'openai-completions' | 'anthropic-messages';
13
+ export interface ChatModelConfig {
14
+ baseUrl: string;
15
+ apiKey: string;
16
+ model: string;
17
+ api?: ChatApi;
18
+ /** anthropic-messages 下把密钥作为 Authorization: Bearer 发送。 */
19
+ authHeader?: boolean;
20
+ temperature?: number;
21
+ }
22
+ /** 工具声明(JSON Schema 形式的参数说明)。 */
23
+ export interface ChatToolSpec {
24
+ name: string;
25
+ description: string;
26
+ parameters: Record<string, unknown>;
27
+ }
28
+ /** 模型发起的一次工具调用。 */
29
+ export interface ChatToolCall {
30
+ id: string;
31
+ name: string;
32
+ /** 已解析的参数对象。 */
33
+ arguments: Record<string, unknown>;
34
+ }
35
+ export interface ChatMessage {
36
+ role: 'system' | 'user' | 'assistant' | 'tool';
37
+ content: string;
38
+ /** assistant 发起工具调用时带上。 */
39
+ toolCalls?: ChatToolCall[];
40
+ /** tool 消息指回哪一次调用。 */
41
+ toolCallId?: string;
42
+ }
43
+ /** 一轮对话的结果。 */
44
+ export interface ChatTurn {
45
+ /** 模型的文字输出(发起工具调用时可以为空)。 */
46
+ content: string;
47
+ /** 模型请求执行的工具。 */
48
+ toolCalls: ChatToolCall[];
49
+ }
50
+ export interface ChatClientOptions {
51
+ /** 单次请求硬超时,默认 60s。 */
52
+ timeoutMs?: number;
53
+ /** 输出 token 上限,默认 2000。 */
54
+ maxTokens?: number;
55
+ /** 测试注入点。 */
56
+ fetchImpl?: typeof fetch;
57
+ }
58
+ export interface ChatClient {
59
+ complete(messages: ChatMessage[], opts?: {
60
+ tools?: ChatToolSpec[];
61
+ }): Promise<ChatTurn>;
62
+ }
63
+ /** 创建对话补全客户端;模型地址、密钥、模型名缺一不可。 */
64
+ export declare function createChatClient(model: ChatModelConfig, opts?: ChatClientOptions): ChatClient;
65
+ /** OpenAI 兼容地址:baseUrl 填到 /v1 即可,这里补上 /chat/completions。 */
66
+ export declare function chatCompletionsUrl(baseUrl: string): string;
67
+ /** Anthropic 兼容地址:baseUrl 填网关根地址(不带 /v1/messages)。 */
68
+ export declare function anthropicMessagesUrl(baseUrl: string): string;
69
+ /** 解析一轮响应:文字 + 工具调用。 */
70
+ export declare function parseChatTurn(payload: unknown, api?: ChatApi): ChatTurn;
71
+ /** 从返回里取文本;兼容 OpenAI 的字符串 / 分段数组与 Anthropic 的 content 数组。 */
72
+ export declare function parseChatText(payload: unknown, api?: ChatApi): string;
73
+ /** 工具参数可能是 JSON 字符串,也可能是已经解析好的对象。 */
74
+ export declare function parseToolArguments(raw: unknown): Record<string, unknown>;
package/dist/chat.js ADDED
@@ -0,0 +1,236 @@
1
+ /**
2
+ * 通用对话补全客户端。
3
+ *
4
+ * 只依赖两种常见协议的 HTTP 接口,不引入新 SDK:
5
+ * - `openai-completions`:POST `{baseUrl}/chat/completions`,密钥走 `Authorization: Bearer`;
6
+ * - `anthropic-messages`:POST `{baseUrl}/v1/messages`,密钥走 `x-api-key`
7
+ * (配置了 `authHeader` 时按 `ANTHROPIC_AUTH_TOKEN` 的语义改成 `Authorization: Bearer`)。
8
+ *
9
+ * 支持工具调用(tool use):模型可以要求执行只读工具(读配置、读本地代码),
10
+ * 再基于工具结果给结论——这正是「排查助手要能看本机代码与配置」需要的形态。
11
+ */
12
+ const DEFAULT_TIMEOUT_MS = 60_000;
13
+ const DEFAULT_MAX_TOKENS = 2_000;
14
+ /** 创建对话补全客户端;模型地址、密钥、模型名缺一不可。 */
15
+ export function createChatClient(model, opts = {}) {
16
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
17
+ const timeoutMs = opts.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
18
+ const maxTokens = opts.maxTokens && opts.maxTokens > 0 ? opts.maxTokens : DEFAULT_MAX_TOKENS;
19
+ const api = model.api === 'anthropic-messages' ? 'anthropic-messages' : 'openai-completions';
20
+ const url = api === 'anthropic-messages' ? anthropicMessagesUrl(model.baseUrl) : chatCompletionsUrl(model.baseUrl);
21
+ return {
22
+ async complete(messages, completeOpts = {}) {
23
+ const controller = new AbortController();
24
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
25
+ let response;
26
+ try {
27
+ response = await fetchImpl(url, {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ ...authHeaders(model, api),
32
+ },
33
+ body: JSON.stringify(requestBody(model, api, messages, maxTokens, completeOpts.tools)),
34
+ signal: controller.signal,
35
+ });
36
+ }
37
+ catch (error) {
38
+ if (isAbortError(error))
39
+ throw new Error(`chat:timeout(${timeoutMs}ms)`);
40
+ throw new Error(`chat:request-failed: ${errorText(error)}`);
41
+ }
42
+ finally {
43
+ clearTimeout(timer);
44
+ }
45
+ if (!response.ok) {
46
+ const detail = (await response.text().catch(() => '')).slice(0, 200);
47
+ throw new Error(`chat:http-${response.status}${detail ? `: ${detail}` : ''}`);
48
+ }
49
+ let payload;
50
+ try {
51
+ payload = await response.json();
52
+ }
53
+ catch (error) {
54
+ throw new Error(`chat:invalid-json: ${errorText(error)}`);
55
+ }
56
+ const turn = parseChatTurn(payload, api);
57
+ // 既没有文字也没有工具调用,说明模型没给出可用的东西,直接报错让上层降级。
58
+ if (!turn.content && turn.toolCalls.length === 0)
59
+ throw new Error('chat:empty-response');
60
+ return turn;
61
+ },
62
+ };
63
+ }
64
+ /** OpenAI 兼容地址:baseUrl 填到 /v1 即可,这里补上 /chat/completions。 */
65
+ export function chatCompletionsUrl(baseUrl) {
66
+ const clean = baseUrl.trim().replace(/\/+$/, '');
67
+ if (!clean)
68
+ throw new Error('chat:base-url-missing');
69
+ if (clean.endsWith('/chat/completions'))
70
+ return clean;
71
+ return `${clean}/chat/completions`;
72
+ }
73
+ /** Anthropic 兼容地址:baseUrl 填网关根地址(不带 /v1/messages)。 */
74
+ export function anthropicMessagesUrl(baseUrl) {
75
+ const clean = baseUrl.trim().replace(/\/+$/, '');
76
+ if (!clean)
77
+ throw new Error('chat:base-url-missing');
78
+ if (clean.endsWith('/v1/messages'))
79
+ return clean;
80
+ if (clean.endsWith('/messages'))
81
+ return clean;
82
+ return `${clean}/v1/messages`;
83
+ }
84
+ function authHeaders(model, api) {
85
+ if (!model.apiKey)
86
+ return {};
87
+ if (api === 'anthropic-messages') {
88
+ return model.authHeader
89
+ ? { Authorization: `Bearer ${model.apiKey}` }
90
+ : { 'x-api-key': model.apiKey, 'anthropic-version': '2023-06-01' };
91
+ }
92
+ return { Authorization: `Bearer ${model.apiKey}` };
93
+ }
94
+ function requestBody(model, api, messages, maxTokens, tools) {
95
+ const temperature = model.temperature ?? 0.2;
96
+ if (api === 'anthropic-messages') {
97
+ const system = messages.filter(item => item.role === 'system').map(item => item.content).join('\n\n');
98
+ const rest = messages.filter(item => item.role !== 'system');
99
+ return {
100
+ model: model.model,
101
+ max_tokens: maxTokens,
102
+ temperature,
103
+ ...(system ? { system } : {}),
104
+ ...(tools && tools.length > 0 ? { tools: tools.map(anthropicTool) } : {}),
105
+ messages: rest.map(anthropicMessage),
106
+ };
107
+ }
108
+ return {
109
+ model: model.model,
110
+ max_tokens: maxTokens,
111
+ temperature,
112
+ ...(tools && tools.length > 0 ? { tools: tools.map(openaiTool) } : {}),
113
+ messages: messages.map(openaiMessage),
114
+ };
115
+ }
116
+ function openaiTool(tool) {
117
+ return {
118
+ type: 'function',
119
+ function: { name: tool.name, description: tool.description, parameters: tool.parameters },
120
+ };
121
+ }
122
+ function anthropicTool(tool) {
123
+ return { name: tool.name, description: tool.description, input_schema: tool.parameters };
124
+ }
125
+ function openaiMessage(message) {
126
+ if (message.role === 'tool') {
127
+ return { role: 'tool', tool_call_id: message.toolCallId ?? '', content: message.content };
128
+ }
129
+ if (message.role === 'assistant' && message.toolCalls && message.toolCalls.length > 0) {
130
+ return {
131
+ role: 'assistant',
132
+ content: message.content || null,
133
+ tool_calls: message.toolCalls.map(call => ({
134
+ id: call.id,
135
+ type: 'function',
136
+ function: { name: call.name, arguments: JSON.stringify(call.arguments ?? {}) },
137
+ })),
138
+ };
139
+ }
140
+ return { role: message.role, content: message.content };
141
+ }
142
+ function anthropicMessage(message) {
143
+ if (message.role === 'tool') {
144
+ return {
145
+ role: 'user',
146
+ content: [{ type: 'tool_result', tool_use_id: message.toolCallId ?? '', content: message.content }],
147
+ };
148
+ }
149
+ if (message.role === 'assistant' && message.toolCalls && message.toolCalls.length > 0) {
150
+ const blocks = [];
151
+ if (message.content)
152
+ blocks.push({ type: 'text', text: message.content });
153
+ for (const call of message.toolCalls) {
154
+ blocks.push({ type: 'tool_use', id: call.id, name: call.name, input: call.arguments ?? {} });
155
+ }
156
+ return { role: 'assistant', content: blocks };
157
+ }
158
+ return { role: message.role, content: message.content };
159
+ }
160
+ /** 解析一轮响应:文字 + 工具调用。 */
161
+ export function parseChatTurn(payload, api = 'openai-completions') {
162
+ const root = payload;
163
+ if (!root)
164
+ return { content: '', toolCalls: [] };
165
+ if (api === 'anthropic-messages') {
166
+ const blocks = Array.isArray(root.content) ? root.content : [];
167
+ const content = blocks
168
+ .filter((block) => block?.type === 'text')
169
+ .map((block) => String(block.text ?? ''))
170
+ .join('')
171
+ .trim();
172
+ const toolCalls = blocks
173
+ .filter((block) => block?.type === 'tool_use')
174
+ .map((block) => ({
175
+ id: String(block.id ?? ''),
176
+ name: String(block.name ?? ''),
177
+ arguments: block.input ?? {},
178
+ }));
179
+ return { content, toolCalls };
180
+ }
181
+ const message = root.choices?.[0]?.message;
182
+ const content = parseChatContent(message?.content);
183
+ const rawCalls = Array.isArray(message?.tool_calls) ? message.tool_calls : [];
184
+ const toolCalls = rawCalls.map((call) => ({
185
+ id: String(call?.id ?? ''),
186
+ name: String(call?.function?.name ?? ''),
187
+ arguments: parseToolArguments(call?.function?.arguments),
188
+ }));
189
+ return { content, toolCalls };
190
+ }
191
+ /** 从返回里取文本;兼容 OpenAI 的字符串 / 分段数组与 Anthropic 的 content 数组。 */
192
+ export function parseChatText(payload, api = 'openai-completions') {
193
+ const root = payload;
194
+ if (!root)
195
+ return '';
196
+ if (api === 'anthropic-messages') {
197
+ const blocks = Array.isArray(root.content) ? root.content : [];
198
+ return blocks
199
+ .map((block) => (block && typeof block.text === 'string' ? block.text : ''))
200
+ .join('')
201
+ .trim();
202
+ }
203
+ return parseChatContent(root.choices?.[0]?.message?.content);
204
+ }
205
+ function parseChatContent(content) {
206
+ if (typeof content === 'string')
207
+ return content.trim();
208
+ if (Array.isArray(content)) {
209
+ return content
210
+ .map((part) => (part && part.type === 'text' ? String(part.text ?? '') : ''))
211
+ .join('')
212
+ .trim();
213
+ }
214
+ return '';
215
+ }
216
+ /** 工具参数可能是 JSON 字符串,也可能是已经解析好的对象。 */
217
+ export function parseToolArguments(raw) {
218
+ if (raw && typeof raw === 'object')
219
+ return raw;
220
+ if (typeof raw !== 'string' || !raw.trim())
221
+ return {};
222
+ try {
223
+ const parsed = JSON.parse(raw);
224
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
225
+ }
226
+ catch {
227
+ return {};
228
+ }
229
+ }
230
+ function isAbortError(error) {
231
+ return error instanceof Error && (error.name === 'AbortError' || /aborted/i.test(error.message));
232
+ }
233
+ function errorText(error) {
234
+ return error instanceof Error ? error.message : String(error);
235
+ }
236
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../../packages/infra/src/chat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA2DH,MAAM,kBAAkB,GAAG,MAAM,CAAA;AACjC,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEhC,iCAAiC;AACjC,MAAM,UAAU,gBAAgB,CAAC,KAAsB,EAAE,OAA0B,EAAE;IACnF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAA;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAA;IAC5F,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAA;IAC5F,MAAM,GAAG,GAAY,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,CAAA;IACrG,MAAM,GAAG,GAAG,GAAG,KAAK,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAElH,OAAO;QACL,KAAK,CAAC,QAAQ,CAAC,QAAuB,EAAE,eAA2C,EAAE;YACnF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;YACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAA;YAC7D,IAAI,QAAkB,CAAA;YACtB,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;oBAC9B,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,cAAc,EAAE,kBAAkB;wBAClC,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC;qBAC3B;oBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;oBACtF,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAA;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,YAAY,CAAC,KAAK,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,SAAS,KAAK,CAAC,CAAA;gBACxE,MAAM,IAAI,KAAK,CAAC,wBAAwB,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC7D,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,KAAK,CAAC,CAAA;YACrB,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;gBACpE,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;YAC/E,CAAC;YAED,IAAI,OAAgB,CAAA;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YACjC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC3D,CAAC;YAED,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;YACxC,uCAAuC;YACvC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;YACxF,OAAO,IAAI,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAChD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;IACpD,IAAI,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAAE,OAAO,KAAK,CAAA;IACrD,OAAO,GAAG,KAAK,mBAAmB,CAAA;AACpC,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAChD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;IACpD,IAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,KAAK,CAAA;IAChD,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7C,OAAO,GAAG,KAAK,cAAc,CAAA;AAC/B,CAAC;AAED,SAAS,WAAW,CAAC,KAAsB,EAAE,GAAY;IACvD,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAC5B,IAAI,GAAG,KAAK,oBAAoB,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC,UAAU;YACrB,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE,EAAE;YAC7C,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAA;IACtE,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE,EAAE,CAAA;AACpD,CAAC;AAED,SAAS,WAAW,CAClB,KAAsB,EACtB,GAAY,EACZ,QAAuB,EACvB,SAAiB,EACjB,KAAsB;IAEtB,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,GAAG,CAAA;IAC5C,IAAI,GAAG,KAAK,oBAAoB,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACrG,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;QAC5D,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,UAAU,EAAE,SAAS;YACrB,WAAW;YACX,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC;SACrC,CAAA;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,UAAU,EAAE,SAAS;QACrB,WAAW;QACX,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC;KACtC,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,IAAkB;IACpC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;KAC1F,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAkB;IACvC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,CAAA;AAC1F,CAAC;AAED,SAAS,aAAa,CAAC,OAAoB;IACzC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;IAC3F,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtF,OAAO;YACL,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;YAChC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACzC,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE;aAC/E,CAAC,CAAC;SACJ,CAAA;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;AACzD,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAoB;IAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;SACpG,CAAA;IACH,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtF,MAAM,MAAM,GAA8B,EAAE,CAAA;QAC5C,IAAI,OAAO,CAAC,OAAO;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;QACzE,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC,CAAA;QAC9F,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;IAC/C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;AACzD,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,MAAe,oBAAoB;IACjF,MAAM,IAAI,GAAG,OAAqC,CAAA;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAChD,IAAI,GAAG,KAAK,oBAAoB,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QAC9D,MAAM,OAAO,GAAG,MAAM;aACnB,MAAM,CAAC,CAAC,KAA8B,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAC;aAClE,GAAG,CAAC,CAAC,KAA8B,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;aACjE,IAAI,CAAC,EAAE,CAAC;aACR,IAAI,EAAE,CAAA;QACT,MAAM,SAAS,GAAG,MAAM;aACrB,MAAM,CAAC,CAAC,KAA8B,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,UAAU,CAAC;aACtE,GAAG,CAAC,CAAC,KAA8B,EAAE,EAAE,CAAC,CAAC;YACxC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;YAC1B,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YAC9B,SAAS,EAAG,KAAK,CAAC,KAAiC,IAAI,EAAE;SAC1D,CAAC,CAAC,CAAA;QACL,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;IAC/B,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAA;IAC1C,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IAC7E,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAyB,EAAE,EAAE,CAAC,CAAC;QAC7D,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC;QACxC,SAAS,EAAE,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC;KACzD,CAAC,CAAC,CAAA;IACH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;AAC/B,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,MAAe,oBAAoB;IACjF,MAAM,IAAI,GAAG,OAAqC,CAAA;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IACpB,IAAI,GAAG,KAAK,oBAAoB,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QAC9D,OAAO,MAAM;aACV,GAAG,CAAC,CAAC,KAA8B,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACpG,IAAI,CAAC,EAAE,CAAC;aACR,IAAI,EAAE,CAAA;IACX,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;AAC9D,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAgB;IACxC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,CAAA;IACtD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,GAAG,CAAC,CAAC,IAA6B,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACrG,IAAI,CAAC,EAAE,CAAC;aACR,IAAI,EAAE,CAAA;IACX,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAA8B,CAAA;IACzE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAA;IACrD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAA;QACzC,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAiC,CAAC,CAAC,CAAC,EAAE,CAAA;IAChH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;AAClG,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 基础设施包入口。
3
+ *
4
+ * `chat.ts` 与 `vision.ts` 各自保留了同名的 OpenAI 地址规范化函数(错误码不同),
5
+ * 这里显式只导出 chat 版本,避免 barrel 出现二义性;vision 版本仍可从模块深路径引用。
6
+ */
7
+ export * from './logger.js';
8
+ export * from './process.js';
9
+ export * from './chat.js';
10
+ export * from './plugin-loader.js';
11
+ export { createVisionTranscriber, type ImageInput, type ImageTranscriber, type VisionModelConfig, } from './vision.js';
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 基础设施包入口。
3
+ *
4
+ * `chat.ts` 与 `vision.ts` 各自保留了同名的 OpenAI 地址规范化函数(错误码不同),
5
+ * 这里显式只导出 chat 版本,避免 barrel 出现二义性;vision 版本仍可从模块深路径引用。
6
+ */
7
+ export * from './logger.js';
8
+ export * from './process.js';
9
+ export * from './chat.js';
10
+ export * from './plugin-loader.js';
11
+ export { createVisionTranscriber, } from './vision.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/infra/src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,OAAO,EACL,uBAAuB,GAIxB,MAAM,aAAa,CAAA"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 结构化日志的统一入口。
3
+ *
4
+ * 服务日志一行一个 JSON(`at` / `level` / `message` + 业务字段),
5
+ * 面板日志页按 info / warn / error 三档过滤,所以这里只保留这三档。
6
+ * 各模块只依赖这个接口,不直接碰文件,方便测试注入与将来换实现。
7
+ */
8
+ export interface Logger {
9
+ info(message: string, extra?: Record<string, unknown>): void;
10
+ warn(message: string, extra?: Record<string, unknown>): void;
11
+ error(message: string, extra?: Record<string, unknown>): void;
12
+ }
13
+ /** 未注入日志实现时的空实现;关键链路不会因为「没传 logger」而崩。 */
14
+ export declare const noopLogger: Logger;
15
+ /** 把任意异常折成一行可读文本;日志里不写堆栈,避免刷屏。 */
16
+ export declare function errorMessage(error: unknown): string;
package/dist/logger.js ADDED
@@ -0,0 +1,11 @@
1
+ /** 未注入日志实现时的空实现;关键链路不会因为「没传 logger」而崩。 */
2
+ export const noopLogger = {
3
+ info: () => { },
4
+ warn: () => { },
5
+ error: () => { },
6
+ };
7
+ /** 把任意异常折成一行可读文本;日志里不写堆栈,避免刷屏。 */
8
+ export function errorMessage(error) {
9
+ return error instanceof Error ? error.message : String(error);
10
+ }
11
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../../packages/infra/src/logger.ts"],"names":[],"mappings":"AAaA,0CAA0C;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAW;IAChC,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAA;AAED,kCAAkC;AAClC,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { type Logger } from './logger.js';
2
+ import type { CommandRunner } from './process.js';
3
+ /** 插件在配置里的条目:包名、本地路径或目录。 */
4
+ export type PluginEntry = string | {
5
+ /** npm 包名或本地路径。 */
6
+ package?: string;
7
+ path?: string;
8
+ /** 配置别名:与 path 等价。 */
9
+ plugin?: string;
10
+ /** 包内入口,例如 `dist/index.js`。 */
11
+ entry?: string;
12
+ enabled?: boolean;
13
+ };
14
+ export interface PluginMeta {
15
+ id: string;
16
+ name: string;
17
+ label?: string;
18
+ hint?: string;
19
+ installHint?: string;
20
+ installUrl?: string;
21
+ installable?: boolean;
22
+ packageName?: string;
23
+ deprecated?: boolean;
24
+ repoSelector?: boolean;
25
+ default?: boolean;
26
+ }
27
+ export type PluginKind = 'runtime' | 'channel';
28
+ /** node:module 的 resolve/import 抽象;测试可注入。 */
29
+ export interface PluginModuleLoader {
30
+ resolve(specifier: string, parent?: string): string;
31
+ import(url: string): Promise<unknown>;
32
+ }
33
+ /** 一次插件加载失败;面板与日志都要能看到「哪个包、为什么」。 */
34
+ export interface PluginFailure {
35
+ kind: PluginKind;
36
+ id: string;
37
+ specifier: string;
38
+ message: string;
39
+ }
40
+ export interface PluginLoadResult<T> {
41
+ plugins: T[];
42
+ /** 插件与其在配置里的 id 一一对应;插件声明的 meta.id 可能不同。 */
43
+ entries: Array<{
44
+ id: string;
45
+ plugin: T;
46
+ }>;
47
+ failures: PluginFailure[];
48
+ }
49
+ export interface PluginLoadOptions {
50
+ kind: PluginKind;
51
+ home: string;
52
+ specifierOf(id: string): string | undefined;
53
+ /** 包内入口,例如 dist/index.js;目录型插件用它选具体文件。 */
54
+ entryOf?(id: string): string | undefined;
55
+ ids: readonly string[];
56
+ loader?: PluginModuleLoader;
57
+ logger: Logger;
58
+ }
59
+ export declare function pluginSpecifier(entry: PluginEntry | undefined): string | undefined;
60
+ export declare function pluginEntryTarget(entry: PluginEntry | undefined): string | undefined;
61
+ /**
62
+ * 解析插件入口。
63
+ *
64
+ * 支持裸包名、绝对路径、相对 home 的路径、目录和 `entry`。目录优先读 package.json
65
+ * 的 `exports` / `module` / `main`,再退回 index.js / index.mjs。
66
+ */
67
+ export declare function resolvePluginSpecifier(specifier: string, home: string, loader: PluginModuleLoader, entry?: string): string;
68
+ export declare function resolvePluginPath(path: string, entry?: string): string;
69
+ export declare function defaultPluginLoader(): PluginModuleLoader;
70
+ export declare function loadPluginModules<T extends {
71
+ kind: PluginKind;
72
+ }>(opts: PluginLoadOptions): Promise<PluginLoadResult<T>>;
73
+ export declare function importPluginModule<T>(specifier: string, home: string, loader?: PluginModuleLoader, entry?: string): Promise<T>;
74
+ /** 插件自行创建 runner 时用;宿主不假设具体 CLI。 */
75
+ export declare function throwRunner(kind: PluginKind, bin: string): CommandRunner;
@@ -0,0 +1,181 @@
1
+ import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+ import { errorMessage } from './logger.js';
6
+ export function pluginSpecifier(entry) {
7
+ if (!entry)
8
+ return undefined;
9
+ if (typeof entry === 'string')
10
+ return entry.trim() || undefined;
11
+ if (entry.enabled === false)
12
+ return undefined;
13
+ return (entry.plugin ?? entry.path ?? entry.package)?.trim() || undefined;
14
+ }
15
+ export function pluginEntryTarget(entry) {
16
+ if (typeof entry === 'string')
17
+ return entry.trim() || undefined;
18
+ return entry?.entry?.trim() || undefined;
19
+ }
20
+ /**
21
+ * 解析插件入口。
22
+ *
23
+ * 支持裸包名、绝对路径、相对 home 的路径、目录和 `entry`。目录优先读 package.json
24
+ * 的 `exports` / `module` / `main`,再退回 index.js / index.mjs。
25
+ */
26
+ export function resolvePluginSpecifier(specifier, home, loader, entry) {
27
+ if (specifier.startsWith('file://'))
28
+ return specifier;
29
+ if (isAbsolute(specifier) || specifier.startsWith('.')) {
30
+ const base = isAbsolute(specifier) ? specifier : resolve(home, specifier);
31
+ return pathToFileURL(resolvePluginPath(base, entry)).href;
32
+ }
33
+ try {
34
+ const resolved = loader.resolve(specifier, resolve(home, 'config.yaml'));
35
+ return pathToFileURL(entry ? resolvePluginPath(resolved, entry) : resolved).href;
36
+ }
37
+ catch {
38
+ // 裸包名解析失败时保留原名,让 import 报出真实的「找不到包」;
39
+ // 这里不能静默吞掉,否则面板只会看到一条没有包名的错误。
40
+ return specifier;
41
+ }
42
+ }
43
+ export function resolvePluginPath(path, entry) {
44
+ if (!existsSync(path))
45
+ return path;
46
+ if (!statSync(path).isDirectory())
47
+ return path;
48
+ if (entry) {
49
+ const candidate = join(path, entry);
50
+ if (existsSync(candidate))
51
+ return candidate;
52
+ }
53
+ const manifest = join(path, 'package.json');
54
+ if (existsSync(manifest)) {
55
+ try {
56
+ const pkg = JSON.parse(readFileSync(manifest, 'utf8'));
57
+ const exported = exportTarget(pkg.exports);
58
+ for (const value of [exported, pkg.module, pkg.main]) {
59
+ if (typeof value !== 'string' || !value.trim())
60
+ continue;
61
+ const candidate = join(path, value.trim());
62
+ if (existsSync(candidate))
63
+ return candidate;
64
+ }
65
+ }
66
+ catch {
67
+ // package.json 坏了就继续按目录默认入口找,最后的加载失败会带出真实原因。
68
+ }
69
+ }
70
+ for (const name of ['index.js', 'index.mjs', 'index.cjs']) {
71
+ const candidate = join(path, name);
72
+ if (existsSync(candidate))
73
+ return candidate;
74
+ }
75
+ return path;
76
+ }
77
+ function exportTarget(exports) {
78
+ if (typeof exports === 'string')
79
+ return exports;
80
+ if (!exports || typeof exports !== 'object' || Array.isArray(exports))
81
+ return undefined;
82
+ const root = exports['.'];
83
+ if (typeof root === 'string')
84
+ return root;
85
+ if (root && typeof root === 'object' && !Array.isArray(root)) {
86
+ const record = root;
87
+ for (const key of ['import', 'node', 'default']) {
88
+ if (typeof record[key] === 'string')
89
+ return record[key];
90
+ }
91
+ }
92
+ return undefined;
93
+ }
94
+ export function defaultPluginLoader() {
95
+ return {
96
+ resolve(specifier, parent) {
97
+ const require = createRequire(parent ? pathToFileURL(parent) : import.meta.url);
98
+ try {
99
+ return require.resolve(specifier);
100
+ }
101
+ catch (error) {
102
+ // ESM-only 包(exports 只有 import 条件)无法被 require.resolve 解析,
103
+ // 手动按包名定位目录后再交给默认目录入口解析。
104
+ const fallback = resolveEsmOnlyPackage(specifier, parent, error);
105
+ if (fallback)
106
+ return resolvePluginPath(fallback);
107
+ throw error;
108
+ }
109
+ },
110
+ async import(url) {
111
+ return import(url);
112
+ },
113
+ };
114
+ }
115
+ /**
116
+ * 沿着 parent 所在目录向上找工作区 / 全局的 `node_modules/<specifier>`。
117
+ *
118
+ * RA_HOME 下的插件就是这种布局;只有 require.resolve 因 ESM-only exports
119
+ * 失败时才走这里,找不到就返回 null,让调用方保留原始错误。
120
+ */
121
+ function resolveEsmOnlyPackage(specifier, parent, error) {
122
+ const code = error?.code;
123
+ if (code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED' && code !== 'ERR_MODULE_NOT_FOUND')
124
+ return null;
125
+ const parts = specifier.split('/');
126
+ const name = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
127
+ const start = parent ? dirname(parent) : dirname(fileURLToPath(import.meta.url));
128
+ for (let dir = start;; dir = dirname(dir)) {
129
+ const candidate = join(dir, 'node_modules', name);
130
+ if (existsSync(candidate))
131
+ return candidate;
132
+ if (dirname(dir) === dir)
133
+ return null;
134
+ }
135
+ }
136
+ export async function loadPluginModules(opts) {
137
+ const loader = opts.loader ?? defaultPluginLoader();
138
+ const ids = [...new Set(opts.ids)];
139
+ const plugins = [];
140
+ const entries = [];
141
+ const failures = [];
142
+ for (const id of ids) {
143
+ const specifier = opts.specifierOf(id);
144
+ if (!specifier)
145
+ continue;
146
+ try {
147
+ const target = resolvePluginSpecifier(specifier, opts.home, loader, opts.entryOf?.(id));
148
+ const mod = await loader.import(target);
149
+ const plugin = mod.default ?? mod;
150
+ if (!plugin || typeof plugin !== 'object' || plugin.kind !== opts.kind) {
151
+ throw new Error(`插件 ${specifier} 没有导出 kind=${opts.kind} 的默认对象`);
152
+ }
153
+ plugins.push(plugin);
154
+ entries.push({ id, plugin });
155
+ }
156
+ catch (error) {
157
+ const message = errorMessage(error);
158
+ failures.push({ kind: opts.kind, id, specifier, message });
159
+ opts.logger.error('plugin:load-failed', {
160
+ kind: opts.kind,
161
+ id,
162
+ package: specifier,
163
+ detail: `${opts.kind} 插件 ${id} 加载失败:${message}`,
164
+ });
165
+ }
166
+ }
167
+ return { plugins, entries, failures };
168
+ }
169
+ export async function importPluginModule(specifier, home, loader = defaultPluginLoader(), entry) {
170
+ const target = resolvePluginSpecifier(specifier, home, loader, entry);
171
+ return await loader.import(target);
172
+ }
173
+ /** 插件自行创建 runner 时用;宿主不假设具体 CLI。 */
174
+ export function throwRunner(kind, bin) {
175
+ return {
176
+ async run() {
177
+ throw new Error(`${kind} 插件未注入 ${bin} 运行器`);
178
+ },
179
+ };
180
+ }
181
+ //# sourceMappingURL=plugin-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-loader.js","sourceRoot":"","sources":["../../../../packages/infra/src/plugin-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface CommandResult {
2
+ code: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ /** 被信号终止时的信号名(SIGKILL 等);正常退出为 null。 */
6
+ signal?: string | null;
7
+ /** 是否因为超时被 kill —— 与「命令自己报错退出」要区分开。 */
8
+ timedOut?: boolean;
9
+ }
10
+ /** argv 不含二进制名:runner 已绑定到某个可执行文件。 */
11
+ export interface CommandRunner {
12
+ run(argv: string[], opts?: {
13
+ cwd?: string;
14
+ env?: NodeJS.ProcessEnv;
15
+ timeoutMs?: number;
16
+ input?: string;
17
+ }): Promise<CommandResult>;
18
+ }
19
+ /** 用 child_process 执行绑定到 `bin` 的命令。 */
20
+ export declare function realRunner(bin: string): CommandRunner;
@@ -0,0 +1,46 @@
1
+ import { spawn } from 'node:child_process';
2
+ /** 用 child_process 执行绑定到 `bin` 的命令。 */
3
+ export function realRunner(bin) {
4
+ return {
5
+ run(argv, opts = {}) {
6
+ return new Promise(resolve => {
7
+ const child = spawn(bin, argv, {
8
+ cwd: opts.cwd,
9
+ env: opts.env ?? process.env,
10
+ stdio: [opts.input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'],
11
+ });
12
+ let stdout = '';
13
+ let stderr = '';
14
+ let timedOut = false;
15
+ const timer = opts.timeoutMs
16
+ ? setTimeout(() => {
17
+ timedOut = true;
18
+ child.kill('SIGKILL');
19
+ }, opts.timeoutMs)
20
+ : null;
21
+ if (opts.input !== undefined) {
22
+ child.stdin?.end(opts.input);
23
+ }
24
+ child.stdout.on('data', chunk => { stdout += String(chunk); });
25
+ child.stderr.on('data', chunk => { stderr += String(chunk); });
26
+ child.on('error', error => {
27
+ if (timer)
28
+ clearTimeout(timer);
29
+ resolve({ code: 127, stdout, stderr: `${stderr}${error.message}`, signal: null, timedOut });
30
+ });
31
+ child.on('close', code => {
32
+ if (timer)
33
+ clearTimeout(timer);
34
+ resolve({
35
+ code: code ?? 1,
36
+ stdout,
37
+ stderr,
38
+ signal: child.signalCode ?? null,
39
+ timedOut,
40
+ });
41
+ });
42
+ });
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../../../packages/infra/src/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAiB1C,uCAAuC;AACvC,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO;QACL,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE;YACjB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;gBAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;oBAC7B,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG;oBAC5B,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;iBACtE,CAAC,CAAA;gBACF,IAAI,MAAM,GAAG,EAAE,CAAA;gBACf,IAAI,MAAM,GAAG,EAAE,CAAA;gBACf,IAAI,QAAQ,GAAG,KAAK,CAAA;gBACpB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS;oBAC1B,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE;wBACd,QAAQ,GAAG,IAAI,CAAA;wBACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;oBACvB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;oBACpB,CAAC,CAAC,IAAI,CAAA;gBACR,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBAC7B,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAC9B,CAAC;gBACD,KAAK,CAAC,MAAO,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;gBAC9D,KAAK,CAAC,MAAO,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;gBAC9D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;oBACxB,IAAI,KAAK;wBAAE,YAAY,CAAC,KAAK,CAAC,CAAA;oBAC9B,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;gBAC7F,CAAC,CAAC,CAAA;gBACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE;oBACvB,IAAI,KAAK;wBAAE,YAAY,CAAC,KAAK,CAAC,CAAA;oBAC9B,OAAO,CAAC;wBACN,IAAI,EAAE,IAAI,IAAI,CAAC;wBACf,MAAM;wBACN,MAAM;wBACN,MAAM,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;wBAChC,QAAQ;qBACT,CAAC,CAAA;gBACJ,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 图片转文字(多模态模型)的基础设施。
3
+ *
4
+ * 只依赖 OpenAI 兼容的 `/chat/completions` 接口,不引入新 SDK:
5
+ * 飞书图片先由消息层下载成 base64,再交给这里转成可进入研判的文本。
6
+ */
7
+ export interface ImageInput {
8
+ /** base64 编码(不含 `data:` 前缀)。 */
9
+ data: string;
10
+ /** 如 image/png、image/jpeg。 */
11
+ mimeType: string;
12
+ }
13
+ export interface ImageTranscriber {
14
+ transcribe(image: ImageInput): Promise<string>;
15
+ }
16
+ export interface VisionModelConfig {
17
+ baseUrl: string;
18
+ apiKey: string;
19
+ model: string;
20
+ temperature?: number;
21
+ }
22
+ export interface VisionOptions {
23
+ /** 识别提示词;不传用内置的「转写 + 描述」。 */
24
+ prompt?: string;
25
+ /** 单张图片的硬超时,默认 60s。 */
26
+ timeoutMs?: number;
27
+ /** 转写结果的最大字符数,超出截断。 */
28
+ maxChars?: number;
29
+ /** 输出 token 上限,默认 1500。 */
30
+ maxTokens?: number;
31
+ /** 测试注入点。 */
32
+ fetchImpl?: typeof fetch;
33
+ }
34
+ export declare const DEFAULT_VISION_PROMPT: string;
35
+ /** 把 OpenAI 兼容的 baseUrl 规范成 chat/completions 地址。 */
36
+ export declare function chatCompletionsUrl(baseUrl: string): string;
37
+ /** 从 `chat/completions` 的返回里取 assistant 文本;兼容字符串与分段数组。 */
38
+ export declare function parseChatCompletionText(payload: unknown): string;
39
+ /**
40
+ * 创建视觉转写器。
41
+ *
42
+ * 结构性失败(模型不支持图片、接口不存在)会熔断:同一进程里后续图片直接快速失败,
43
+ * 避免每张图都等一次超时、刷一屏同样的错误。
44
+ */
45
+ export declare function createVisionTranscriber(model: VisionModelConfig, opts?: VisionOptions): ImageTranscriber;
package/dist/vision.js ADDED
@@ -0,0 +1,164 @@
1
+ /**
2
+ * 图片转文字(多模态模型)的基础设施。
3
+ *
4
+ * 只依赖 OpenAI 兼容的 `/chat/completions` 接口,不引入新 SDK:
5
+ * 飞书图片先由消息层下载成 base64,再交给这里转成可进入研判的文本。
6
+ */
7
+ export const DEFAULT_VISION_PROMPT = [
8
+ '把这张聊天图片转换成可以直接进入研发研判的纯文本:',
9
+ '1. 逐字转写图片里所有可见文字,保留报错信息、日志、代码、命令行和表格结构;',
10
+ '2. 如果图片是界面截图,说明这是哪个页面/功能区域,以及图里表现出的异常或操作意图;',
11
+ '3. 如果图片是设计稿或流程图,按层级概述它的结构与关键信息。',
12
+ '看不清的内容写「[看不清]」,不要猜测或编造。',
13
+ '直接输出纯文本,不要加 Markdown 代码块,也不要解释你在做什么。',
14
+ ].join('\n');
15
+ const DEFAULT_TIMEOUT_MS = 60_000;
16
+ const DEFAULT_MAX_CHARS = 2_000;
17
+ const DEFAULT_MAX_TOKENS = 1_500;
18
+ /** 把 OpenAI 兼容的 baseUrl 规范成 chat/completions 地址。 */
19
+ export function chatCompletionsUrl(baseUrl) {
20
+ const clean = baseUrl.trim().replace(/\/+$/, '');
21
+ if (!clean)
22
+ throw new Error('image:vision-base-url-missing');
23
+ if (clean.endsWith('/chat/completions'))
24
+ return clean;
25
+ return `${clean}/chat/completions`;
26
+ }
27
+ /** 从 `chat/completions` 的返回里取 assistant 文本;兼容字符串与分段数组。 */
28
+ export function parseChatCompletionText(payload) {
29
+ const root = payload;
30
+ const content = root?.choices?.[0]?.message?.content;
31
+ if (typeof content === 'string')
32
+ return content.trim();
33
+ if (Array.isArray(content)) {
34
+ return content
35
+ .map((part) => (part && part.type === 'text' ? String(part.text ?? '') : ''))
36
+ .join('')
37
+ .trim();
38
+ }
39
+ return '';
40
+ }
41
+ /**
42
+ * 创建视觉转写器。
43
+ *
44
+ * 结构性失败(模型不支持图片、接口不存在)会熔断:同一进程里后续图片直接快速失败,
45
+ * 避免每张图都等一次超时、刷一屏同样的错误。
46
+ */
47
+ export function createVisionTranscriber(model, opts = {}) {
48
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
49
+ const timeoutMs = opts.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
50
+ const maxChars = opts.maxChars && opts.maxChars > 0 ? opts.maxChars : DEFAULT_MAX_CHARS;
51
+ const maxTokens = opts.maxTokens && opts.maxTokens > 0 ? opts.maxTokens : DEFAULT_MAX_TOKENS;
52
+ const prompt = opts.prompt?.trim() || DEFAULT_VISION_PROMPT;
53
+ const url = chatCompletionsUrl(model.baseUrl);
54
+ let unsupported = null;
55
+ // 一旦确认这个模型会把预算耗在 reasoning 上,后续图片直接关闭推理,省掉一次白跑的请求。
56
+ let reasoningOff = false;
57
+ /** 发一次请求;reasoningOff=true 时显式关闭推理模型的思考预算。 */
58
+ async function request(reasoningOff, image) {
59
+ const controller = new AbortController();
60
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
61
+ let response;
62
+ try {
63
+ response = await fetchImpl(url, {
64
+ method: 'POST',
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ ...(model.apiKey ? { Authorization: `Bearer ${model.apiKey}` } : {}),
68
+ },
69
+ body: JSON.stringify({
70
+ model: model.model,
71
+ temperature: model.temperature ?? 0.1,
72
+ max_tokens: maxTokens,
73
+ // 推理模型的思考 token 也算在 max_tokens 里;图片转写不需要推理,重试时直接关掉。
74
+ ...(reasoningOff ? { reasoning_effort: 'none' } : {}),
75
+ messages: [
76
+ {
77
+ role: 'user',
78
+ content: [
79
+ { type: 'text', text: prompt },
80
+ { type: 'image_url', image_url: { url: `data:${image.mimeType};base64,${image.data}` } },
81
+ ],
82
+ },
83
+ ],
84
+ }),
85
+ signal: controller.signal,
86
+ });
87
+ }
88
+ catch (error) {
89
+ if (isAbortError(error))
90
+ throw new Error(`image:vision-timeout(${timeoutMs}ms)`);
91
+ throw new Error(`image:vision-request-failed: ${errorText(error)}`);
92
+ }
93
+ finally {
94
+ clearTimeout(timer);
95
+ }
96
+ if (!response.ok) {
97
+ const detail = (await response.text().catch(() => '')).slice(0, 200);
98
+ // 404/415/422,或 400 且明确提到 image/vision,基本可判定为「这个模型/接口不接受图片」。
99
+ // 「图片太大」之类的 400 不熔断,换一张图仍可能成功。
100
+ const looksUnsupported = response.status === 404 || response.status === 415 || response.status === 422 ||
101
+ (response.status === 400 && /(not|doesn'?t|unsupported).{0,40}(support|image|vision)|modalit|multimodal/i.test(detail));
102
+ if (looksUnsupported)
103
+ unsupported = `http-${response.status}${detail ? `: ${detail}` : ''}`;
104
+ throw new Error(`image:vision-http-${response.status}${detail ? `: ${detail}` : ''}`);
105
+ }
106
+ try {
107
+ return await response.json();
108
+ }
109
+ catch (error) {
110
+ throw new Error(`image:vision-invalid-json: ${errorText(error)}`);
111
+ }
112
+ }
113
+ return {
114
+ async transcribe(image) {
115
+ if (unsupported)
116
+ throw new Error(`image:vision-unsupported: ${unsupported}`);
117
+ let payload = await request(reasoningOff, image);
118
+ let text = parseChatCompletionText(payload);
119
+ // 推理模型的 reasoning_content 会把 max_tokens 吃光,content 为空或半截就截断。
120
+ // 转写不需要思考,发现这种情况时明确关闭推理重试一次。
121
+ if (!reasoningOff && reasoningUsedUp(payload, text)) {
122
+ try {
123
+ const retried = await request(true, image);
124
+ const retriedText = parseChatCompletionText(retried);
125
+ if (retriedText.length > text.length) {
126
+ payload = retried;
127
+ text = retriedText;
128
+ reasoningOff = true;
129
+ }
130
+ }
131
+ catch {
132
+ // 部分 OpenAI 兼容服务不认识 reasoning_effort;重试失败保留首次结果。
133
+ }
134
+ }
135
+ if (!text)
136
+ throw new Error(`image:vision-empty(${completionDetail(payload)})`);
137
+ return text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
138
+ },
139
+ };
140
+ }
141
+ /** 推理 token 是否占满了预算:有 reasoning 消耗,且 content 为空或因为长度被截断。 */
142
+ function reasoningUsedUp(payload, text) {
143
+ const root = payload;
144
+ const choice = root?.choices?.[0];
145
+ const finish = String(choice?.finish_reason ?? '');
146
+ const reasoning = Number(root?.usage?.completion_tokens_details?.reasoning_tokens ?? root?.usage?.reasoning_tokens ?? 0);
147
+ if (!Number.isFinite(reasoning) || reasoning <= 0)
148
+ return false;
149
+ return !text || finish === 'length';
150
+ }
151
+ /** 给空结果留一个可排查的细节:finish_reason 与 reasoning token 数。 */
152
+ function completionDetail(payload) {
153
+ const root = payload;
154
+ const finish = String(root?.choices?.[0]?.finish_reason ?? 'unknown');
155
+ const reasoning = Number(root?.usage?.completion_tokens_details?.reasoning_tokens ?? root?.usage?.reasoning_tokens ?? 0);
156
+ return `finish=${finish}, reasoning=${Number.isFinite(reasoning) ? reasoning : 0}`;
157
+ }
158
+ function isAbortError(error) {
159
+ return error instanceof Error && (error.name === 'AbortError' || /aborted/i.test(error.message));
160
+ }
161
+ function errorText(error) {
162
+ return error instanceof Error ? error.message : String(error);
163
+ }
164
+ //# sourceMappingURL=vision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision.js","sourceRoot":"","sources":["../../../../packages/infra/src/vision.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "name": "@router-agent/infra",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.31",
4
+ "type": "module",
5
+ "description": "基础设施:日志、子进程、对话模型与图片识别",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/blankPen/router-agent.git",
10
+ "directory": "packages/infra"
11
+ },
12
+ "homepage": "https://github.com/blankPen/router-agent#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/blankPen/router-agent/issues"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./*.js": "./dist/*.js",
23
+ "./*": "./dist/*"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "types": "./dist/index.d.ts",
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "registry": "https://registry.npmjs.org/"
32
+ }
33
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.