@co0ontty/wand 4.80.0 → 4.80.1-beta.g1fbbefb

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 (51) hide show
  1. package/dist/ai-team-availability.js +0 -1
  2. package/dist/ai-team-live.d.ts +3 -1
  3. package/dist/ai-team-live.js +31 -2
  4. package/dist/ai-team-runner.d.ts +2 -1
  5. package/dist/ai-team-runner.js +17 -7
  6. package/dist/ai-team-types.d.ts +8 -0
  7. package/dist/ai-team-types.js +4 -0
  8. package/dist/build-info.json +4 -4
  9. package/dist/claude-pty-bridge.js +16 -1
  10. package/dist/cli.js +0 -9
  11. package/dist/config.d.ts +2 -10
  12. package/dist/config.js +1 -73
  13. package/dist/git-quick-commit.d.ts +3 -6
  14. package/dist/git-quick-commit.js +38 -75
  15. package/dist/language-prompt.d.ts +1 -1
  16. package/dist/language-prompt.js +1 -1
  17. package/dist/native-session-title.d.ts +17 -0
  18. package/dist/native-session-title.js +257 -0
  19. package/dist/process-manager.d.ts +3 -0
  20. package/dist/process-manager.js +37 -6
  21. package/dist/server-session-routes.js +4 -29
  22. package/dist/server-settings-routes.js +2 -177
  23. package/dist/server-task-routes.js +1 -1
  24. package/dist/server-workspace-routes.js +1 -1
  25. package/dist/server.js +1 -13
  26. package/dist/session-ai-context.d.ts +3 -7
  27. package/dist/session-ai-context.js +0 -25
  28. package/dist/session-topic.d.ts +19 -0
  29. package/dist/session-topic.js +53 -3
  30. package/dist/session-transport.js +0 -1
  31. package/dist/silicon-employee-draft.d.ts +1 -1
  32. package/dist/storage.js +12 -18
  33. package/dist/structured-claude-adapter.d.ts +0 -6
  34. package/dist/structured-claude-adapter.js +1 -5
  35. package/dist/structured-provider-common.d.ts +8 -4
  36. package/dist/structured-provider-common.js +14 -15
  37. package/dist/structured-session-manager.d.ts +4 -40
  38. package/dist/structured-session-manager.js +53 -773
  39. package/dist/types.d.ts +12 -27
  40. package/dist/web-ui/content/ai-teams.js +9 -9
  41. package/dist/web-ui/content/scripts.js +70 -70
  42. package/dist/web-ui/content/styles.css +1 -1
  43. package/dist/web-ui/embedded-assets.js +3 -3
  44. package/dist/ws-broadcast.js +19 -7
  45. package/package.json +1 -2
  46. package/dist/claude-sdk-runner.d.ts +0 -59
  47. package/dist/claude-sdk-runner.js +0 -188
  48. package/dist/claude-skills.d.ts +0 -6
  49. package/dist/claude-skills.js +0 -53
  50. package/dist/system-ai.d.ts +0 -27
  51. package/dist/system-ai.js +0 -424
package/dist/system-ai.js DELETED
@@ -1,424 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { randomUUID } from "node:crypto";
3
- import os from "node:os";
4
- import path from "node:path";
5
- const SYSTEM_AI_TIMEOUT_MS = 60_000;
6
- const SYSTEM_AI_ROUTE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
7
- class SystemAiError extends Error {
8
- code;
9
- constructor(message, code) {
10
- super(message);
11
- this.code = code;
12
- this.name = "SystemAiError";
13
- }
14
- }
15
- export function normalizeSystemAiConfig(value, fallback) {
16
- if (!value || typeof value !== "object" || Array.isArray(value)) {
17
- throw new Error("systemAi 必须是对象。");
18
- }
19
- const raw = value;
20
- const protocol = raw.protocol === "anthropic" ? "anthropic" : "openai";
21
- const baseUrl = typeof raw.baseUrl === "string" ? raw.baseUrl.trim().replace(/\/+$/, "") : fallback?.baseUrl ?? "";
22
- if (baseUrl) {
23
- let parsed;
24
- try {
25
- parsed = new URL(baseUrl);
26
- }
27
- catch {
28
- throw new Error("系统 AI API 地址无效。");
29
- }
30
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
31
- throw new Error("系统 AI API 地址必须使用 http 或 https。");
32
- }
33
- const normalized = {
34
- id: typeof raw.id === "string" && SYSTEM_AI_ROUTE_ID_PATTERN.test(raw.id.trim())
35
- ? raw.id.trim()
36
- : fallback?.id ?? randomUUID(),
37
- enabled: raw.enabled === true,
38
- protocol,
39
- baseUrl,
40
- apiKey: typeof raw.apiKey === "string" ? raw.apiKey.trim() : fallback?.apiKey ?? "",
41
- model: typeof raw.model === "string" ? raw.model.trim() : fallback?.model ?? "",
42
- authHeader: raw.authHeader === "x-api-key" ? "x-api-key" : "bearer",
43
- source: raw.source === "claude" || raw.source === "codex" || raw.source === "opencode" || raw.source === "grok"
44
- ? raw.source
45
- : "custom",
46
- };
47
- if (Array.isArray(raw.fallbacks)) {
48
- normalized.fallbacks = raw.fallbacks
49
- .map((item) => {
50
- try {
51
- const itemId = item && typeof item === "object" && !Array.isArray(item)
52
- && typeof item.id === "string"
53
- ? item.id.trim()
54
- : "";
55
- const itemFallback = itemId
56
- ? fallback?.fallbacks?.find((profile) => profile.id === itemId)
57
- : undefined;
58
- const profile = normalizeSystemAiConfig(item, itemFallback);
59
- delete profile.fallbacks;
60
- return profile;
61
- }
62
- catch {
63
- return null;
64
- }
65
- })
66
- .filter((item) => item !== null);
67
- }
68
- else if (fallback?.fallbacks?.length) {
69
- normalized.fallbacks = fallback.fallbacks.map((item) => ({ ...item, fallbacks: undefined }));
70
- }
71
- return normalized;
72
- }
73
- function tryNormalizeSystemAiConfig(value) {
74
- try {
75
- return normalizeSystemAiConfig(value);
76
- }
77
- catch {
78
- // A stale or partially edited tool profile must not prevent the remaining
79
- // APIs—or the final current-session CLI fallback—from being tried.
80
- return null;
81
- }
82
- }
83
- function systemAiProfileKey(profile) {
84
- // Credential is intentionally part of runtime dedupe: if a stored key is
85
- // stale but a tool config has already rotated it, the discovered route must
86
- // remain available later in the same fallback chain. Import handles that
87
- // case separately by refreshing the stored route in place.
88
- return [
89
- profile.protocol,
90
- profile.baseUrl,
91
- profile.apiKey,
92
- profile.model,
93
- profile.authHeader ?? "bearer",
94
- ].join("\0");
95
- }
96
- function readJson(filePath) {
97
- if (!existsSync(filePath))
98
- return null;
99
- try {
100
- return JSON.parse(readFileSync(filePath, "utf8"));
101
- }
102
- catch {
103
- return null;
104
- }
105
- }
106
- function parseTomlString(raw) {
107
- const value = raw.trim();
108
- if (value.startsWith('"')) {
109
- const quoted = /^"(?:\\.|[^"\\])*"/.exec(value)?.[0];
110
- if (!quoted)
111
- return null;
112
- try {
113
- const parsed = JSON.parse(quoted);
114
- return typeof parsed === "string" ? parsed : null;
115
- }
116
- catch {
117
- return null;
118
- }
119
- }
120
- if (value.startsWith("'")) {
121
- const end = value.indexOf("'", 1);
122
- return end < 0 ? null : value.slice(1, end);
123
- }
124
- return null;
125
- }
126
- function readTomlStringSections(filePath) {
127
- if (!existsSync(filePath))
128
- return null;
129
- try {
130
- const sections = new Map();
131
- let section = "";
132
- sections.set(section, new Map());
133
- for (const line of readFileSync(filePath, "utf8").split(/\r?\n/)) {
134
- const sectionMatch = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/.exec(line);
135
- if (sectionMatch) {
136
- section = sectionMatch[1].trim();
137
- if (!sections.has(section))
138
- sections.set(section, new Map());
139
- continue;
140
- }
141
- const assignment = /^\s*([A-Za-z0-9_-]+)\s*=\s*(.+)$/.exec(line);
142
- if (!assignment)
143
- continue;
144
- const value = parseTomlString(assignment[2]);
145
- if (value !== null)
146
- sections.get(section).set(assignment[1], value);
147
- }
148
- return sections;
149
- }
150
- catch {
151
- return null;
152
- }
153
- }
154
- function discoverClaude(home) {
155
- const settings = readJson(path.join(home, ".claude", "settings.json"));
156
- const env = settings?.env && typeof settings.env === "object" ? settings.env : {};
157
- const apiKey = typeof env.ANTHROPIC_AUTH_TOKEN === "string" ? env.ANTHROPIC_AUTH_TOKEN
158
- : typeof env.ANTHROPIC_API_KEY === "string" ? env.ANTHROPIC_API_KEY : "";
159
- const baseUrl = typeof env.ANTHROPIC_BASE_URL === "string" ? env.ANTHROPIC_BASE_URL : "https://api.anthropic.com";
160
- const model = typeof settings?.model === "string" ? settings.model : "";
161
- if (!apiKey || !model)
162
- return null;
163
- return tryNormalizeSystemAiConfig({
164
- enabled: true, protocol: "anthropic", baseUrl, apiKey, model,
165
- authHeader: typeof env.ANTHROPIC_AUTH_TOKEN === "string" ? "bearer" : "x-api-key",
166
- source: "claude",
167
- });
168
- }
169
- function discoverOpenCode(home) {
170
- const config = readJson(path.join(home, ".config", "opencode", "opencode.json"));
171
- const selectedModel = typeof config?.model === "string" ? config.model : "";
172
- const providerId = selectedModel.split("/", 1)[0] ?? "";
173
- const providers = config?.provider && typeof config.provider === "object" ? config.provider : {};
174
- const orderedProviders = [providerId, ...Object.keys(providers).filter((id) => id !== providerId)];
175
- const found = [];
176
- for (const id of orderedProviders) {
177
- const provider = providers[id] && typeof providers[id] === "object" ? providers[id] : null;
178
- const options = provider?.options && typeof provider.options === "object" ? provider.options : {};
179
- const apiKey = typeof options.apiKey === "string" ? options.apiKey : "";
180
- const baseUrl = typeof options.baseURL === "string" ? options.baseURL : typeof options.baseUrl === "string" ? options.baseUrl : "";
181
- const models = provider?.models && typeof provider.models === "object" ? provider.models : {};
182
- const configuredModel = id === providerId && selectedModel
183
- ? (selectedModel.includes("/") ? selectedModel.slice(selectedModel.indexOf("/") + 1) : selectedModel)
184
- : typeof provider?.model === "string" ? provider.model : Object.keys(models)[0] ?? "";
185
- if (!apiKey || !baseUrl || !configuredModel)
186
- continue;
187
- const profile = tryNormalizeSystemAiConfig({
188
- enabled: true,
189
- protocol: "openai",
190
- baseUrl,
191
- apiKey,
192
- model: configuredModel,
193
- authHeader: "bearer",
194
- source: "opencode",
195
- });
196
- if (profile)
197
- found.push(profile);
198
- }
199
- return found;
200
- }
201
- function discoverCodex(home) {
202
- const auth = readJson(path.join(home, ".codex", "auth.json"));
203
- const apiKey = typeof auth?.OPENAI_API_KEY === "string" ? auth.OPENAI_API_KEY : "";
204
- if (!apiKey)
205
- return null;
206
- let model = "";
207
- let baseUrl = "https://api.openai.com";
208
- try {
209
- const toml = readFileSync(path.join(home, ".codex", "config.toml"), "utf8");
210
- model = /^model\s*=\s*["']([^"']+)["']/m.exec(toml)?.[1] ?? "";
211
- baseUrl = /^(?:base_url|baseURL)\s*=\s*["']([^"']+)["']/m.exec(toml)?.[1] ?? baseUrl;
212
- }
213
- catch { /* optional config */ }
214
- if (!model)
215
- return null;
216
- return tryNormalizeSystemAiConfig({ enabled: true, protocol: "openai", baseUrl, apiKey, model, authHeader: "bearer", source: "codex" });
217
- }
218
- function discoverGrok(home) {
219
- const sections = readTomlStringSections(path.join(home, ".grok", "config.toml"));
220
- if (!sections)
221
- return [];
222
- const defaultProfile = sections.get("models")?.get("default") ?? "";
223
- const modelSections = [...sections.entries()].filter(([name]) => name.startsWith("model."));
224
- const orderedSections = [
225
- ...modelSections.filter(([name]) => name === `model.${defaultProfile}`),
226
- ...modelSections.filter(([name]) => name !== `model.${defaultProfile}`),
227
- ];
228
- const found = [];
229
- for (const [section, values] of orderedSections) {
230
- if (values.get("api_backend")?.trim().toLowerCase() !== "chat_completions")
231
- continue;
232
- const apiKey = values.get("api_key") ?? "";
233
- const baseUrl = values.get("base_url") ?? "";
234
- const model = values.get("model") ?? section.slice("model.".length);
235
- if (!apiKey || !baseUrl || !model)
236
- continue;
237
- const profile = tryNormalizeSystemAiConfig({
238
- enabled: true,
239
- protocol: "openai",
240
- baseUrl,
241
- apiKey,
242
- model,
243
- authHeader: "bearer",
244
- source: "grok",
245
- });
246
- if (profile)
247
- found.push(profile);
248
- }
249
- return found;
250
- }
251
- /** Copy every usable direct-API profile from the user's configured CLIs. */
252
- export function discoverCliSystemAiConfigs(preferred, home = os.homedir()) {
253
- const discoverers = {
254
- claude: (dir) => [discoverClaude(dir)].filter((item) => item !== null),
255
- codex: (dir) => [discoverCodex(dir)].filter((item) => item !== null),
256
- opencode: discoverOpenCode,
257
- grok: discoverGrok,
258
- };
259
- const order = [preferred ?? "claude", "claude", "opencode", "grok", "codex"];
260
- const found = [];
261
- const seen = new Set();
262
- for (const provider of [...new Set(order)]) {
263
- if (provider === "qoder" || provider === "pi" || provider === "gemini")
264
- continue;
265
- let discovered;
266
- try {
267
- discovered = discoverers[provider](home);
268
- }
269
- catch {
270
- continue;
271
- }
272
- for (const profile of discovered) {
273
- const key = systemAiProfileKey(profile);
274
- if (seen.has(key))
275
- continue;
276
- seen.add(key);
277
- found.push(profile);
278
- }
279
- }
280
- return found;
281
- }
282
- /** Backward-compatible first-profile discovery. */
283
- export function discoverCliSystemAiConfig(preferred, home = os.homedir()) {
284
- return discoverCliSystemAiConfigs(preferred, home)[0] ?? null;
285
- }
286
- /** Return the configured API chain in call order, excluding incomplete entries. */
287
- export function systemAiProfiles(config, forceEnabled = false) {
288
- if (!config || (!forceEnabled && !config.enabled))
289
- return [];
290
- const candidates = [config, ...(config.fallbacks ?? [])];
291
- const seen = new Set();
292
- return candidates.flatMap((candidate) => {
293
- const normalized = tryNormalizeSystemAiConfig({ ...candidate, enabled: true, fallbacks: undefined });
294
- if (!normalized)
295
- return [];
296
- if (!normalized.baseUrl || !normalized.apiKey || !normalized.model)
297
- return [];
298
- const key = systemAiProfileKey(normalized);
299
- if (seen.has(key))
300
- return [];
301
- seen.add(key);
302
- return [normalized];
303
- });
304
- }
305
- /**
306
- * Flatten and combine direct-API groups in caller-defined priority order.
307
- * Later groups are appended after earlier groups and duplicate routes are skipped.
308
- */
309
- export function mergeSystemAiConfigs(...groups) {
310
- const profiles = [];
311
- const seen = new Set();
312
- for (const group of groups) {
313
- for (const config of Array.isArray(group) ? group : group ? [group] : []) {
314
- for (const profile of systemAiProfiles(config, true)) {
315
- const key = systemAiProfileKey(profile);
316
- if (seen.has(key))
317
- continue;
318
- seen.add(key);
319
- profiles.push({ ...profile, enabled: true, fallbacks: undefined });
320
- }
321
- }
322
- }
323
- const [primary, ...fallbacks] = profiles;
324
- if (!primary)
325
- return undefined;
326
- const merged = { ...primary, enabled: true };
327
- if (fallbacks.length)
328
- merged.fallbacks = fallbacks;
329
- return merged;
330
- }
331
- function endpoint(baseUrl, protocol) {
332
- const url = new URL(baseUrl);
333
- const pathName = url.pathname.replace(/\/+$/, "");
334
- const fullSuffix = protocol === "anthropic" ? "/v1/messages" : "/v1/chat/completions";
335
- const shortSuffix = protocol === "anthropic" ? "/messages" : "/chat/completions";
336
- if (pathName.toLowerCase().endsWith(shortSuffix)) {
337
- url.pathname = pathName;
338
- }
339
- else if (/\/v\d+(?:\.\d+)?$/i.test(pathName)) {
340
- url.pathname = `${pathName}${shortSuffix}`;
341
- }
342
- else {
343
- url.pathname = `${pathName}${fullSuffix}`;
344
- }
345
- return url.toString();
346
- }
347
- function requestParts(request) {
348
- return { system: request.system?.trim() ?? "", prompt: request.prompt ?? "" };
349
- }
350
- export async function callSystemAiText(request, config, timeoutMs = SYSTEM_AI_TIMEOUT_MS) {
351
- const normalized = normalizeSystemAiConfig(config);
352
- if (!normalized.enabled || !normalized.baseUrl || !normalized.apiKey || !normalized.model) {
353
- throw new SystemAiError("系统 AI API 配置不完整。", "SYSTEM_AI_CONFIG_INVALID");
354
- }
355
- const { system, prompt } = requestParts(request);
356
- const headers = { "content-type": "application/json" };
357
- if (normalized.protocol === "anthropic") {
358
- headers["anthropic-version"] = "2023-06-01";
359
- if (normalized.authHeader === "x-api-key")
360
- headers["x-api-key"] = normalized.apiKey;
361
- else
362
- headers.authorization = `Bearer ${normalized.apiKey}`;
363
- }
364
- else {
365
- if (normalized.authHeader === "x-api-key")
366
- headers["x-api-key"] = normalized.apiKey;
367
- else
368
- headers.authorization = `Bearer ${normalized.apiKey}`;
369
- }
370
- const body = normalized.protocol === "anthropic"
371
- ? { model: normalized.model, max_tokens: 2048, ...(system ? { system } : {}), messages: [{ role: "user", content: prompt }] }
372
- : {
373
- model: normalized.model,
374
- // Quick system tasks should not silently inherit a model's expensive
375
- // default reasoning level. Settings probes a route with this same
376
- // payload, so an incompatible endpoint fails visibly before it is used.
377
- reasoning_effort: "low",
378
- messages: [...(system ? [{ role: "system", content: system }] : []), { role: "user", content: prompt }],
379
- stream: false,
380
- };
381
- let response;
382
- try {
383
- response = await fetch(endpoint(normalized.baseUrl, normalized.protocol), {
384
- method: "POST", headers, body: JSON.stringify(body), signal: AbortSignal.timeout(timeoutMs),
385
- });
386
- }
387
- catch (error) {
388
- const timedOut = error instanceof DOMException && error.name === "TimeoutError";
389
- throw new SystemAiError(timedOut ? "系统 AI API 调用超时。" : `系统 AI API 无法连接:${error instanceof Error ? error.message : String(error)}`, timedOut ? "SYSTEM_AI_TIMEOUT" : "SYSTEM_AI_REQUEST_FAILED");
390
- }
391
- const raw = await response.text();
392
- if (!response.ok)
393
- throw new SystemAiError(`系统 AI API 返回 ${response.status}:${raw.slice(0, 500)}`, "SYSTEM_AI_REQUEST_FAILED");
394
- try {
395
- const data = JSON.parse(raw);
396
- if (normalized.protocol === "anthropic")
397
- return (data.content ?? []).map((item) => item.text ?? "").join("\n").trim();
398
- const content = data.choices?.[0]?.message?.content;
399
- return typeof content === "string" ? content.trim() : Array.isArray(content) ? content.map((item) => item.text ?? "").join("\n").trim() : "";
400
- }
401
- catch {
402
- throw new SystemAiError("系统 AI API 返回了无法解析的响应。", "SYSTEM_AI_INVALID_RESPONSE");
403
- }
404
- }
405
- /** Try every configured API in order. Empty responses are treated as unavailable. */
406
- export async function callSystemAiTextWithFallback(request, config, timeoutMs = SYSTEM_AI_TIMEOUT_MS) {
407
- const profiles = systemAiProfiles(config, true);
408
- if (!profiles.length) {
409
- throw new SystemAiError("系统 AI API 配置不完整。", "SYSTEM_AI_CONFIG_INVALID");
410
- }
411
- const errors = [];
412
- for (const profile of profiles) {
413
- try {
414
- const text = await callSystemAiText(request, profile, timeoutMs);
415
- if (text.trim())
416
- return text;
417
- errors.push(`${profile.source ?? "custom"}: 返回空结果`);
418
- }
419
- catch (error) {
420
- errors.push(`${profile.source ?? "custom"}: ${error instanceof Error ? error.message : String(error)}`);
421
- }
422
- }
423
- throw new SystemAiError(`所有系统 AI API 均不可用:${errors.join(";")}`, "SYSTEM_AI_ALL_FAILED");
424
- }