@acosmi/sdk-ts 1.0.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 (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/dist/browser/index.js +3950 -0
  4. package/dist/browser/index.js.map +1 -0
  5. package/dist/index.js +3950 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/node/adapters/anthropic.cjs +257 -0
  8. package/dist/node/adapters/anthropic.cjs.map +1 -0
  9. package/dist/node/adapters/anthropic.d.cts +39 -0
  10. package/dist/node/adapters/anthropic.d.ts +39 -0
  11. package/dist/node/adapters/anthropic.js +254 -0
  12. package/dist/node/adapters/anthropic.js.map +1 -0
  13. package/dist/node/adapters/openai.cjs +472 -0
  14. package/dist/node/adapters/openai.cjs.map +1 -0
  15. package/dist/node/adapters/openai.d.cts +64 -0
  16. package/dist/node/adapters/openai.d.ts +64 -0
  17. package/dist/node/adapters/openai.js +465 -0
  18. package/dist/node/adapters/openai.js.map +1 -0
  19. package/dist/node/index-C3Z_84Bv.d.cts +992 -0
  20. package/dist/node/index-C3Z_84Bv.d.ts +992 -0
  21. package/dist/node/index-nvLKgCm9.d.cts +147 -0
  22. package/dist/node/index-nvLKgCm9.d.ts +147 -0
  23. package/dist/node/index.cjs +4024 -0
  24. package/dist/node/index.cjs.map +1 -0
  25. package/dist/node/index.d.cts +791 -0
  26. package/dist/node/index.d.ts +791 -0
  27. package/dist/node/index.js +3950 -0
  28. package/dist/node/index.js.map +1 -0
  29. package/dist/node/sanitize/index.cjs +248 -0
  30. package/dist/node/sanitize/index.cjs.map +1 -0
  31. package/dist/node/sanitize/index.d.cts +1 -0
  32. package/dist/node/sanitize/index.d.ts +1 -0
  33. package/dist/node/sanitize/index.js +217 -0
  34. package/dist/node/sanitize/index.js.map +1 -0
  35. package/package.json +84 -0
@@ -0,0 +1,4024 @@
1
+ 'use strict';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+
13
+ // src/types.ts
14
+ function tokenSetIsExpired(t) {
15
+ const expiresAt = new Date(t.expires_at).getTime();
16
+ return Date.now() > expiresAt - 3e4;
17
+ }
18
+ function bucketInfoIsCommercial(b) {
19
+ if (!b) return false;
20
+ return b.bucketClass.toLowerCase() === exports.BucketClassCommercial.toLowerCase();
21
+ }
22
+ function bucketRowIsCommercial(r) {
23
+ if (!r) return false;
24
+ return r.bucketClass.toLowerCase() === exports.BucketClassCommercial.toLowerCase();
25
+ }
26
+ function newThinkingConfig(level) {
27
+ if (level === "" || level === exports.ThinkingOff) {
28
+ return { type: "disabled" };
29
+ }
30
+ return { type: "adaptive", level };
31
+ }
32
+ function newWebSearchTool(cfg) {
33
+ const st = {
34
+ type: exports.ServerToolTypeWebSearch,
35
+ name: "web_search"
36
+ };
37
+ if (cfg) {
38
+ if ((cfg.allowed_domains?.length ?? 0) > 0 && (cfg.blocked_domains?.length ?? 0) > 0) {
39
+ throw new Error("web search: allowed_domains and blocked_domains are mutually exclusive");
40
+ }
41
+ const m = {};
42
+ if (cfg.max_uses && cfg.max_uses > 0) m["max_uses"] = cfg.max_uses;
43
+ if (cfg.allowed_domains?.length) m["allowed_domains"] = cfg.allowed_domains;
44
+ if (cfg.blocked_domains?.length) m["blocked_domains"] = cfg.blocked_domains;
45
+ if (cfg.user_location) m["user_location"] = cfg.user_location;
46
+ if (Object.keys(m).length > 0) st.config = m;
47
+ }
48
+ return st;
49
+ }
50
+ function parseSourcesEvent(ev) {
51
+ let wrapper;
52
+ try {
53
+ wrapper = JSON.parse(ev.data);
54
+ } catch {
55
+ return null;
56
+ }
57
+ if (wrapper.type !== "sources" && ev.event !== "sources") {
58
+ return null;
59
+ }
60
+ if (!wrapper.sources || wrapper.sources.length === 0) {
61
+ return null;
62
+ }
63
+ return { sources: wrapper.sources, session_id: wrapper.session_id };
64
+ }
65
+ function anthropicResponseTextContent(r) {
66
+ const parts = [];
67
+ for (const b of r.content) {
68
+ if (b.type === "text" && b.text) parts.push(b.text);
69
+ }
70
+ return parts.join("");
71
+ }
72
+ function anthropicResponseThinkingContent(r) {
73
+ const parts = [];
74
+ for (const b of r.content) {
75
+ if (b.type === "thinking" && b.thinking) parts.push(b.thinking);
76
+ }
77
+ return parts.join("");
78
+ }
79
+ function anthropicResponseToolUseBlocks(r) {
80
+ return r.content.filter((b) => b.type === "tool_use");
81
+ }
82
+ function parseSettlement(ev) {
83
+ if (ev.event !== "settled" && ev.event !== "pending_settle") {
84
+ return null;
85
+ }
86
+ let s;
87
+ try {
88
+ s = JSON.parse(ev.data);
89
+ } catch {
90
+ return null;
91
+ }
92
+ return {
93
+ requestId: s.requestId ?? "",
94
+ consumeStatus: s.consumeStatus ?? "",
95
+ inputTokens: s.inputTokens ?? 0,
96
+ outputTokens: s.outputTokens ?? 0,
97
+ totalTokens: s.totalTokens ?? 0,
98
+ tokenRemaining: s.tokenRemaining ?? -1,
99
+ callRemaining: s.callRemaining ?? -1
100
+ };
101
+ }
102
+ function apiResponseGetMessage(r) {
103
+ return r.message ?? r.msg ?? "";
104
+ }
105
+ function apiResponseBusinessError(r) {
106
+ if (r.code !== 0) {
107
+ return new exports.BusinessError(r.code, apiResponseGetMessage(r));
108
+ }
109
+ return null;
110
+ }
111
+ function parseNotificationEvent(ev) {
112
+ if (ev.type !== "event" || ev.topic !== "system") {
113
+ return null;
114
+ }
115
+ if (ev.data == null) return null;
116
+ let n;
117
+ try {
118
+ if (typeof ev.data === "string") {
119
+ n = JSON.parse(ev.data);
120
+ } else {
121
+ n = ev.data;
122
+ }
123
+ } catch {
124
+ return null;
125
+ }
126
+ if (!n.id) return null;
127
+ return n;
128
+ }
129
+ exports.BucketClassCommercial = void 0; exports.BucketClassGeneric = void 0; exports.ThinkingOff = void 0; exports.ThinkingHigh = void 0; exports.ThinkingMax = void 0; exports.ThinkingHighMinMaxTokens = void 0; exports.ThinkingMaxFallbackMaxTokens = void 0; exports.ServerToolTypeWebSearch = void 0; exports.RateLimitError = void 0; exports.BusinessError = void 0; exports.OrderTerminalError = void 0; exports.ModelNotFoundError = void 0; exports.HTTPError = void 0; exports.NetworkError = void 0; exports.StreamError = void 0;
130
+ var init_types = __esm({
131
+ "src/types.ts"() {
132
+ exports.BucketClassCommercial = "COMMERCIAL";
133
+ exports.BucketClassGeneric = "GENERIC";
134
+ exports.ThinkingOff = "off";
135
+ exports.ThinkingHigh = "high";
136
+ exports.ThinkingMax = "max";
137
+ exports.ThinkingHighMinMaxTokens = 32e3;
138
+ exports.ThinkingMaxFallbackMaxTokens = 128e3;
139
+ exports.ServerToolTypeWebSearch = "web_search_20250305";
140
+ exports.RateLimitError = class extends Error {
141
+ retryAfter;
142
+ raw;
143
+ constructor(message, retryAfter, raw) {
144
+ super(message);
145
+ this.name = "RateLimitError";
146
+ this.retryAfter = retryAfter;
147
+ this.raw = raw;
148
+ }
149
+ };
150
+ exports.BusinessError = class extends Error {
151
+ code;
152
+ constructor(code, message) {
153
+ super(`API error (code=${code}): ${message}`);
154
+ this.name = "BusinessError";
155
+ this.code = code;
156
+ }
157
+ };
158
+ exports.OrderTerminalError = class extends Error {
159
+ orderId;
160
+ status;
161
+ constructor(orderId, status) {
162
+ super(`order ${orderId} terminated: ${status}`);
163
+ this.name = "OrderTerminalError";
164
+ this.orderId = orderId;
165
+ this.status = status;
166
+ }
167
+ };
168
+ exports.ModelNotFoundError = class extends Error {
169
+ modelId;
170
+ constructor(modelId) {
171
+ super(`managed model "${modelId}" not found (list models to refresh cache, or verify model id)`);
172
+ this.name = "ModelNotFoundError";
173
+ this.modelId = modelId;
174
+ }
175
+ };
176
+ exports.HTTPError = class extends Error {
177
+ statusCode;
178
+ /** anthropic.error.type / openai.error.type, 缺失为空 */
179
+ type;
180
+ /** Retry-After 头解析的秒数, 0 表示未提供或解析失败 */
181
+ retryAfter;
182
+ /** 原始响应体 (截断到 maxErrorBodySize) */
183
+ body;
184
+ constructor(statusCode, opts = {}) {
185
+ let msg;
186
+ if (opts.type) {
187
+ msg = `HTTP ${statusCode}: [${opts.type}] ${opts.message ?? ""}`;
188
+ } else if (opts.message) {
189
+ msg = `HTTP ${statusCode}: ${opts.message}`;
190
+ } else if (opts.body) {
191
+ msg = `HTTP ${statusCode}: ${opts.body}`;
192
+ } else {
193
+ msg = `HTTP ${statusCode}`;
194
+ }
195
+ super(msg);
196
+ this.name = "HTTPError";
197
+ this.statusCode = statusCode;
198
+ this.type = opts.type ?? "";
199
+ this.retryAfter = opts.retryAfter ?? 0;
200
+ this.body = opts.body ?? "";
201
+ }
202
+ };
203
+ exports.NetworkError = class extends Error {
204
+ /** 操作描述, e.g. "POST /v1/messages" */
205
+ op;
206
+ /** 请求 URL (脱敏后) */
207
+ url;
208
+ cause;
209
+ timeout;
210
+ eof;
211
+ constructor(op, url, cause, opts = {}) {
212
+ const causeMsg = cause instanceof Error ? cause.message : cause != null ? String(cause) : "network error";
213
+ super(`${op} ${url}: ${causeMsg}`);
214
+ this.name = "NetworkError";
215
+ this.op = op;
216
+ this.url = url;
217
+ this.cause = cause;
218
+ this.timeout = opts.timeout ?? false;
219
+ this.eof = opts.eof ?? false;
220
+ }
221
+ isTimeout() {
222
+ return this.timeout;
223
+ }
224
+ isEOF() {
225
+ return this.eof;
226
+ }
227
+ };
228
+ exports.StreamError = class extends Error {
229
+ /** 例: "empty_response" / "rate_limit" / "overloaded" / "" */
230
+ code;
231
+ /** 例: "provider" / "settlement" */
232
+ stage;
233
+ /** 用户友好提示 (中文); 历史字段, 与 rawError 区分 */
234
+ userMessage;
235
+ /** gateway 原始 error 字符串 */
236
+ rawError;
237
+ /** 客户端是否值得重试 */
238
+ retryable;
239
+ constructor(opts = {}) {
240
+ const code = opts.code ?? "";
241
+ const stage = opts.stage ?? "";
242
+ const userMessage = opts.message ?? "";
243
+ const rawError = opts.rawError ?? "";
244
+ const retryable = opts.retryable ?? false;
245
+ const body = rawError !== "" ? rawError : userMessage;
246
+ const msg = stage !== "" ? `stream failed: ${stage}: ${body}` : `stream failed: ${body}`;
247
+ super(msg);
248
+ this.name = "StreamError";
249
+ this.code = code;
250
+ this.stage = stage;
251
+ this.userMessage = userMessage;
252
+ this.rawError = rawError;
253
+ this.retryable = retryable;
254
+ }
255
+ };
256
+ }
257
+ });
258
+
259
+ // src/betas.ts
260
+ function buildBetas(caps, req) {
261
+ const betas = [];
262
+ if (caps.supports_isp) {
263
+ betas.push(betaInterleavedThinking);
264
+ betas.push(betaContextManagement);
265
+ }
266
+ if (caps.supports_redact_thinking && req.thinking && req.thinking.display === "summary") {
267
+ betas.push(betaRedactThinking);
268
+ }
269
+ if (caps.supports_1m_context) {
270
+ betas.push(betaContext1M);
271
+ }
272
+ const hasStructuredOutput = caps.supports_structured_output && req.outputConfig != null;
273
+ if (hasStructuredOutput) {
274
+ betas.push(betaStructuredOutputs);
275
+ } else if (caps.supports_token_efficient) {
276
+ betas.push(betaTokenEfficientTools);
277
+ }
278
+ if (caps.supports_tool_search) {
279
+ betas.push(betaAdvancedToolUse);
280
+ }
281
+ const needsEffort = req.effort != null || req.thinking != null && req.thinking.level && req.thinking.level !== exports.ThinkingOff;
282
+ if (caps.supports_effort && needsEffort) {
283
+ betas.push(betaEffort);
284
+ }
285
+ if (caps.supports_fast_mode && req.speed === "fast") {
286
+ betas.push(betaFastMode);
287
+ }
288
+ if (caps.supports_prompt_cache) {
289
+ betas.push(betaPromptCachingScope);
290
+ }
291
+ return uniqueMerge(betas, req.betas ?? []);
292
+ }
293
+ function uniqueMerge(base, extra) {
294
+ if (extra.length === 0) return base;
295
+ const seen = new Set(base);
296
+ for (const s of extra) {
297
+ if (!seen.has(s)) {
298
+ base.push(s);
299
+ seen.add(s);
300
+ }
301
+ }
302
+ return base;
303
+ }
304
+ var betaInterleavedThinking, betaContext1M, betaContextManagement, betaStructuredOutputs, betaAdvancedToolUse, betaEffort, betaPromptCachingScope, betaFastMode, betaRedactThinking, betaTokenEfficientTools;
305
+ var init_betas = __esm({
306
+ "src/betas.ts"() {
307
+ init_types();
308
+ betaInterleavedThinking = "interleaved-thinking-2025-05-14";
309
+ betaContext1M = "context-1m-2025-08-07";
310
+ betaContextManagement = "context-management-2025-06-27";
311
+ betaStructuredOutputs = "structured-outputs-2025-11-13";
312
+ betaAdvancedToolUse = "advanced-tool-use-2025-11-20";
313
+ betaEffort = "effort-2025-11-24";
314
+ betaPromptCachingScope = "prompt-caching-scope-2026-01-05";
315
+ betaFastMode = "fast-mode-2026-02-01";
316
+ betaRedactThinking = "redact-thinking-2026-02-12";
317
+ betaTokenEfficientTools = "token-efficient-tools-2025-02-19";
318
+ }
319
+ });
320
+
321
+ // src/adapters/anthropic.ts
322
+ function resolveThinkingLevel(body, req, caps) {
323
+ const level = req.thinking?.level ?? "";
324
+ if (level === exports.ThinkingOff) {
325
+ body["thinking"] = { type: "disabled" };
326
+ return;
327
+ }
328
+ if (!caps.supports_adaptive_thinking && !caps.supports_thinking) {
329
+ return;
330
+ }
331
+ let maxTokens = req.max_tokens ?? 0;
332
+ if (maxTokens <= 0) {
333
+ maxTokens = exports.ThinkingHighMinMaxTokens;
334
+ }
335
+ if (level === exports.ThinkingMax) {
336
+ let modelMax = caps.max_output_tokens;
337
+ if (modelMax <= 0) modelMax = exports.ThinkingMaxFallbackMaxTokens;
338
+ if (maxTokens < modelMax) maxTokens = modelMax;
339
+ } else {
340
+ if (maxTokens < exports.ThinkingHighMinMaxTokens) maxTokens = exports.ThinkingHighMinMaxTokens;
341
+ }
342
+ body["max_tokens"] = maxTokens;
343
+ if (caps.supports_adaptive_thinking) {
344
+ const thinking = { type: "adaptive" };
345
+ if (req.thinking?.display) thinking["display"] = req.thinking.display;
346
+ body["thinking"] = thinking;
347
+ } else if (caps.supports_thinking) {
348
+ let budget = maxTokens - 1;
349
+ if (budget < 1024) budget = 1024;
350
+ const thinking = {
351
+ type: "enabled",
352
+ budget_tokens: budget
353
+ };
354
+ if (req.thinking?.display) thinking["display"] = req.thinking.display;
355
+ body["thinking"] = thinking;
356
+ }
357
+ if (caps.supports_effort) {
358
+ let effortLevel = "high";
359
+ if (level === exports.ThinkingMax && caps.supports_max_effort) {
360
+ effortLevel = "max";
361
+ }
362
+ body["effort"] = { level: effortLevel };
363
+ }
364
+ delete body["temperature"];
365
+ }
366
+ exports.AnthropicAdapter = void 0;
367
+ var init_anthropic = __esm({
368
+ "src/adapters/anthropic.ts"() {
369
+ init_types();
370
+ init_betas();
371
+ init_adapters();
372
+ exports.AnthropicAdapter = class {
373
+ format() {
374
+ return 0 /* Anthropic */;
375
+ }
376
+ endpointSuffix() {
377
+ return "/anthropic";
378
+ }
379
+ /**
380
+ * 构建 Anthropic 格式请求体
381
+ * 逻辑等同于原 buildChatRequest, 包含完整的 betas/tools/serverTools/extraBody 处理
382
+ */
383
+ buildRequestBody(caps, req) {
384
+ const body = {};
385
+ if (req.rawMessages != null) {
386
+ body["messages"] = req.rawMessages;
387
+ } else if ((req.messages?.length ?? 0) > 0) {
388
+ body["messages"] = req.messages;
389
+ }
390
+ body["stream"] = req.stream === true;
391
+ if (req.max_tokens && req.max_tokens > 0) {
392
+ body["max_tokens"] = req.max_tokens;
393
+ }
394
+ if (req.system != null) {
395
+ body["system"] = req.system;
396
+ }
397
+ if (req.temperature != null) {
398
+ body["temperature"] = req.temperature;
399
+ }
400
+ if (req.thinking && req.thinking.level && req.thinking.level !== "") {
401
+ resolveThinkingLevel(body, req, caps);
402
+ } else {
403
+ if (req.thinking) {
404
+ body["thinking"] = req.thinking;
405
+ }
406
+ if (req.effort) {
407
+ body["effort"] = req.effort;
408
+ }
409
+ }
410
+ if (req.metadata) {
411
+ body["metadata"] = req.metadata;
412
+ }
413
+ const allTools = [];
414
+ if (req.tools != null) {
415
+ try {
416
+ const parsed = JSON.parse(JSON.stringify(req.tools));
417
+ if (Array.isArray(parsed)) {
418
+ for (const t of parsed) allTools.push(t);
419
+ }
420
+ } catch {
421
+ }
422
+ }
423
+ for (const st of req.serverTools ?? []) {
424
+ const schema = {
425
+ type: st.type,
426
+ name: st.name
427
+ };
428
+ if (st.config) {
429
+ for (const [k, v] of Object.entries(st.config)) {
430
+ schema[k] = v;
431
+ }
432
+ }
433
+ allTools.push(schema);
434
+ }
435
+ if (allTools.length > 0) {
436
+ body["tools"] = allTools;
437
+ }
438
+ if (req.speed && req.speed !== "") {
439
+ body["speed"] = req.speed;
440
+ }
441
+ if (req.outputConfig) {
442
+ body["output_config"] = req.outputConfig;
443
+ }
444
+ const betas = buildBetas(caps, req);
445
+ if (betas.length > 0) {
446
+ body["betas"] = betas;
447
+ }
448
+ if (req.extraBody) {
449
+ for (const [k, v] of Object.entries(req.extraBody)) {
450
+ body[k] = v;
451
+ }
452
+ }
453
+ return body;
454
+ }
455
+ /**
456
+ * 解析 Anthropic 格式同步响应
457
+ * 兼容 APIResponse 包装 {"code":0,"data":{...}} 和裸 Anthropic JSON 两种格式
458
+ */
459
+ parseResponse(bodyInput) {
460
+ const bodyStr = typeof bodyInput === "string" ? bodyInput : new TextDecoder().decode(bodyInput);
461
+ let raw = bodyStr;
462
+ try {
463
+ const wrapper = JSON.parse(bodyStr);
464
+ if (wrapper.data != null && wrapper.data !== null) {
465
+ if ((wrapper.code ?? 0) !== 0) {
466
+ throw new exports.BusinessError(wrapper.code ?? 0, wrapper.message ?? "");
467
+ }
468
+ raw = JSON.stringify(wrapper.data);
469
+ }
470
+ } catch (e) {
471
+ if (e instanceof exports.BusinessError) throw e;
472
+ }
473
+ let resp;
474
+ try {
475
+ resp = JSON.parse(raw);
476
+ } catch (e) {
477
+ throw new Error(
478
+ `decode anthropic response: ${e instanceof Error ? e.message : String(e)}`
479
+ );
480
+ }
481
+ resp.tokenRemaining = -1;
482
+ resp.callRemaining = -1;
483
+ resp.modelTokenRemaining = -1;
484
+ resp.modelTokenRemainingETU = -1;
485
+ return resp;
486
+ }
487
+ /**
488
+ * 解析 Anthropic SSE 行
489
+ *
490
+ * Anthropic 原生协议无 [DONE] (message_stop 后上游关闭连接),
491
+ * 但 Nexus Gateway 的 ChatStream 路径会追加 [DONE] 哨兵, 此处一并处理。
492
+ */
493
+ parseStreamLine(eventType, data) {
494
+ if (data === "[DONE]") {
495
+ return { event: { event: "", data: "" }, done: true };
496
+ }
497
+ return { event: { event: eventType, data }, done: false };
498
+ }
499
+ };
500
+ }
501
+ });
502
+
503
+ // src/adapters/openai.ts
504
+ var openai_exports = {};
505
+ __export(openai_exports, {
506
+ OpenAIAdapter: () => exports.OpenAIAdapter,
507
+ OpenAIStreamConverter: () => OpenAIStreamConverter,
508
+ newOpenAIStreamConverter: () => newOpenAIStreamConverter,
509
+ parseOpenAIResponseToAnthropic: () => parseOpenAIResponseToAnthropic,
510
+ resolveOpenAIReasoningEffort: () => resolveOpenAIReasoningEffort,
511
+ resolveOpenAIResponseFormat: () => resolveOpenAIResponseFormat
512
+ });
513
+ function resolveOpenAIReasoningEffort(req) {
514
+ if (req.effort && req.effort.level !== "") {
515
+ switch (req.effort.level) {
516
+ case "low":
517
+ case "medium":
518
+ case "high":
519
+ return req.effort.level;
520
+ case "max":
521
+ return "high";
522
+ }
523
+ }
524
+ if (req.thinking) {
525
+ switch (req.thinking.level) {
526
+ case exports.ThinkingHigh:
527
+ return "high";
528
+ case exports.ThinkingMax:
529
+ return "high";
530
+ case exports.ThinkingOff:
531
+ return "";
532
+ }
533
+ }
534
+ return "";
535
+ }
536
+ function resolveOpenAIResponseFormat(req) {
537
+ if (!req.outputConfig) return null;
538
+ switch (req.outputConfig.format) {
539
+ case "json_schema": {
540
+ const js = {};
541
+ if (req.outputConfig.schema != null) {
542
+ js["schema"] = req.outputConfig.schema;
543
+ }
544
+ js["strict"] = true;
545
+ return {
546
+ type: "json_schema",
547
+ json_schema: js
548
+ };
549
+ }
550
+ case "json_object":
551
+ return { type: "json_object" };
552
+ case "":
553
+ case void 0:
554
+ return null;
555
+ default:
556
+ return { type: req.outputConfig.format };
557
+ }
558
+ }
559
+ function convertOpenAIToChatResponse(oai) {
560
+ const resp = {
561
+ id: oai.id,
562
+ type: "message",
563
+ model: oai.model,
564
+ role: "assistant",
565
+ content: [],
566
+ stop_reason: "",
567
+ usage: {
568
+ input_tokens: oai.usage.prompt_tokens,
569
+ output_tokens: oai.usage.completion_tokens
570
+ },
571
+ tokenRemaining: -1,
572
+ callRemaining: -1,
573
+ modelTokenRemaining: -1,
574
+ modelTokenRemainingETU: -1
575
+ };
576
+ if (oai.choices.length > 0) {
577
+ const choice = oai.choices[0];
578
+ switch (choice.finish_reason) {
579
+ case "stop":
580
+ resp.stop_reason = "end_turn";
581
+ break;
582
+ case "tool_calls":
583
+ resp.stop_reason = "tool_use";
584
+ break;
585
+ case "length":
586
+ resp.stop_reason = "max_tokens";
587
+ break;
588
+ default:
589
+ resp.stop_reason = choice.finish_reason;
590
+ }
591
+ if (choice.message.reasoning_content && choice.message.reasoning_content !== "") {
592
+ resp.content.push({
593
+ type: "thinking",
594
+ thinking: choice.message.reasoning_content
595
+ });
596
+ }
597
+ if (choice.message.content && choice.message.content !== "") {
598
+ resp.content.push({
599
+ type: "text",
600
+ text: choice.message.content
601
+ });
602
+ }
603
+ for (const tc of choice.message.tool_calls ?? []) {
604
+ resp.content.push({
605
+ type: "tool_use",
606
+ id: tc.id,
607
+ name: tc.function.name,
608
+ // Anthropic 协议 input 是 raw JSON value; OpenAI 给的 arguments 是 string,
609
+ // Go 侧用 json.RawMessage(arguments) 直透(原始字节). TS 我们尝试解析:
610
+ input: tryParseJSON(tc.function.arguments)
611
+ });
612
+ }
613
+ }
614
+ return resp;
615
+ }
616
+ function tryParseJSON(s) {
617
+ try {
618
+ return JSON.parse(s);
619
+ } catch {
620
+ return s;
621
+ }
622
+ }
623
+ function parseOpenAIResponseToAnthropic(raw) {
624
+ const rawStr = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
625
+ let data = rawStr;
626
+ try {
627
+ const wrapper = JSON.parse(rawStr);
628
+ if (wrapper.data != null && wrapper.data !== null) {
629
+ if ((wrapper.code ?? 0) !== 0) {
630
+ throw new exports.BusinessError(wrapper.code ?? 0, wrapper.message ?? "");
631
+ }
632
+ data = JSON.stringify(wrapper.data);
633
+ }
634
+ } catch (e) {
635
+ if (e instanceof exports.BusinessError) throw e;
636
+ }
637
+ let oaiResp;
638
+ try {
639
+ oaiResp = JSON.parse(data);
640
+ } catch (e) {
641
+ throw new Error(`decode openai response: ${e instanceof Error ? e.message : String(e)}`);
642
+ }
643
+ const resp = {
644
+ id: oaiResp.id,
645
+ type: "message",
646
+ role: "assistant",
647
+ content: [],
648
+ model: oaiResp.model,
649
+ stop_reason: "",
650
+ usage: {
651
+ input_tokens: oaiResp.usage.prompt_tokens,
652
+ output_tokens: oaiResp.usage.completion_tokens
653
+ }
654
+ };
655
+ if (oaiResp.choices.length > 0) {
656
+ const choice = oaiResp.choices[0];
657
+ switch (choice.finish_reason) {
658
+ case "stop":
659
+ resp.stop_reason = "end_turn";
660
+ break;
661
+ case "tool_calls":
662
+ resp.stop_reason = "tool_use";
663
+ break;
664
+ case "length":
665
+ resp.stop_reason = "max_tokens";
666
+ break;
667
+ default:
668
+ resp.stop_reason = choice.finish_reason;
669
+ }
670
+ if (choice.message.reasoning_content && choice.message.reasoning_content !== "") {
671
+ resp.content.push({
672
+ type: "thinking",
673
+ thinking: choice.message.reasoning_content
674
+ });
675
+ }
676
+ if (choice.message.content && choice.message.content !== "") {
677
+ resp.content.push({
678
+ type: "text",
679
+ text: choice.message.content
680
+ });
681
+ }
682
+ for (const tc of choice.message.tool_calls ?? []) {
683
+ resp.content.push({
684
+ type: "tool_use",
685
+ id: tc.id,
686
+ name: tc.function.name,
687
+ input: tryParseJSON(tc.function.arguments)
688
+ });
689
+ }
690
+ }
691
+ return resp;
692
+ }
693
+ function newOpenAIStreamConverter() {
694
+ return new OpenAIStreamConverter();
695
+ }
696
+ exports.OpenAIAdapter = void 0; var OpenAIStreamConverter;
697
+ var init_openai = __esm({
698
+ "src/adapters/openai.ts"() {
699
+ init_types();
700
+ init_adapters();
701
+ exports.OpenAIAdapter = class {
702
+ format() {
703
+ return 1 /* OpenAI */;
704
+ }
705
+ endpointSuffix() {
706
+ return "/chat";
707
+ }
708
+ /**
709
+ * 构建 OpenAI 兼容格式请求体
710
+ * 不注入 Anthropic betas, 扩展字段 (thinking/effort/speed) 以通用 JSON 传递
711
+ */
712
+ buildRequestBody(_caps, req) {
713
+ const body = {};
714
+ if (req.rawMessages != null) {
715
+ body["messages"] = req.rawMessages;
716
+ } else if ((req.messages?.length ?? 0) > 0) {
717
+ body["messages"] = req.messages;
718
+ }
719
+ body["stream"] = req.stream === true;
720
+ if (req.max_tokens && req.max_tokens > 0) {
721
+ body["max_tokens"] = req.max_tokens;
722
+ }
723
+ if (req.system != null) {
724
+ body["system"] = req.system;
725
+ }
726
+ if (req.temperature != null) {
727
+ body["temperature"] = req.temperature;
728
+ }
729
+ if (req.tools != null) {
730
+ body["tools"] = req.tools;
731
+ }
732
+ const eff = resolveOpenAIReasoningEffort(req);
733
+ if (eff !== "") {
734
+ body["reasoning_effort"] = eff;
735
+ }
736
+ if (req.speed && req.speed !== "") {
737
+ body["speed"] = req.speed;
738
+ }
739
+ const rf = resolveOpenAIResponseFormat(req);
740
+ if (rf) {
741
+ body["response_format"] = rf;
742
+ }
743
+ if (req.metadata) {
744
+ body["metadata"] = req.metadata;
745
+ }
746
+ if (req.parallelToolCalls != null) {
747
+ body["parallel_tool_calls"] = req.parallelToolCalls;
748
+ }
749
+ if (req.extraBody) {
750
+ for (const [k, v] of Object.entries(req.extraBody)) {
751
+ body[k] = v;
752
+ }
753
+ }
754
+ if (req.stream === true) {
755
+ body["stream_options"] = { include_usage: true };
756
+ }
757
+ return body;
758
+ }
759
+ /**
760
+ * 解析 OpenAI 格式同步响应为 ChatResponse
761
+ * 兼容 APIResponse 包装 {"code":0,"data":{...}} 和裸 OpenAI JSON 两种格式
762
+ */
763
+ parseResponse(bodyInput) {
764
+ const bodyStr = typeof bodyInput === "string" ? bodyInput : new TextDecoder().decode(bodyInput);
765
+ let raw = bodyStr;
766
+ try {
767
+ const wrapper = JSON.parse(bodyStr);
768
+ if (wrapper.data != null && wrapper.data !== null) {
769
+ if ((wrapper.code ?? 0) !== 0) {
770
+ throw new exports.BusinessError(wrapper.code ?? 0, wrapper.message ?? "");
771
+ }
772
+ raw = JSON.stringify(wrapper.data);
773
+ }
774
+ } catch (e) {
775
+ if (e instanceof exports.BusinessError) throw e;
776
+ }
777
+ let oaiResp;
778
+ try {
779
+ oaiResp = JSON.parse(raw);
780
+ } catch (e) {
781
+ throw new Error(`decode openai response: ${e instanceof Error ? e.message : String(e)}`);
782
+ }
783
+ return convertOpenAIToChatResponse(oaiResp);
784
+ }
785
+ /**
786
+ * 解析 OpenAI SSE 行
787
+ * [DONE] 标记流结束
788
+ */
789
+ parseStreamLine(eventType, data) {
790
+ if (data === "[DONE]") {
791
+ return { event: { event: "", data: "" }, done: true };
792
+ }
793
+ try {
794
+ JSON.parse(data);
795
+ } catch (e) {
796
+ throw new Error(`parse openai stream chunk: ${e instanceof Error ? e.message : String(e)}`);
797
+ }
798
+ return { event: { event: eventType, data }, done: false };
799
+ }
800
+ };
801
+ OpenAIStreamConverter = class {
802
+ messageStarted = false;
803
+ thinkingStarted = false;
804
+ thinkingStopped = false;
805
+ textStarted = false;
806
+ /** OpenAI tool_call index → Anthropic block index */
807
+ toolBlockIndex = /* @__PURE__ */ new Map();
808
+ blockIndex = 0;
809
+ /**
810
+ * 将一行 OpenAI SSE data 转换为零或多个 Anthropic 格式 StreamEvent
811
+ * 返回 { events, done }
812
+ */
813
+ convert(data) {
814
+ if (data === "[DONE]") {
815
+ return { events: [], done: true };
816
+ }
817
+ let chunk;
818
+ try {
819
+ chunk = JSON.parse(data);
820
+ } catch (e) {
821
+ throw new Error(`parse openai stream chunk: ${e instanceof Error ? e.message : String(e)}`);
822
+ }
823
+ const events = [];
824
+ if (chunk.choices.length === 0) {
825
+ return { events, done: false };
826
+ }
827
+ const choice = chunk.choices[0];
828
+ if (!this.messageStarted) {
829
+ this.messageStarted = true;
830
+ const msgJSON = JSON.stringify({
831
+ type: "message_start",
832
+ message: {
833
+ id: chunk.id,
834
+ type: "message",
835
+ role: "assistant",
836
+ content: [],
837
+ model: ""
838
+ }
839
+ });
840
+ events.push({ event: "message_start", data: msgJSON });
841
+ }
842
+ if (choice.delta.reasoning_content && choice.delta.reasoning_content !== "") {
843
+ if (!this.thinkingStarted) {
844
+ this.thinkingStarted = true;
845
+ const blockJSON = JSON.stringify({
846
+ type: "content_block_start",
847
+ index: this.blockIndex,
848
+ content_block: { type: "thinking", thinking: "" }
849
+ });
850
+ events.push({ event: "content_block_start", data: blockJSON });
851
+ }
852
+ const deltaJSON = JSON.stringify({
853
+ type: "content_block_delta",
854
+ index: this.blockIndex,
855
+ delta: { type: "thinking_delta", thinking: choice.delta.reasoning_content }
856
+ });
857
+ events.push({ event: "content_block_delta", data: deltaJSON });
858
+ }
859
+ if (choice.delta.content && choice.delta.content !== "") {
860
+ if (this.thinkingStarted && !this.thinkingStopped) {
861
+ this.thinkingStopped = true;
862
+ const stopJSON = JSON.stringify({
863
+ type: "content_block_stop",
864
+ index: this.blockIndex
865
+ });
866
+ events.push({ event: "content_block_stop", data: stopJSON });
867
+ this.blockIndex++;
868
+ }
869
+ if (!this.textStarted) {
870
+ this.textStarted = true;
871
+ const blockJSON = JSON.stringify({
872
+ type: "content_block_start",
873
+ index: this.blockIndex,
874
+ content_block: { type: "text", text: "" }
875
+ });
876
+ events.push({ event: "content_block_start", data: blockJSON });
877
+ }
878
+ const deltaJSON = JSON.stringify({
879
+ type: "content_block_delta",
880
+ index: this.blockIndex,
881
+ delta: { type: "text_delta", text: choice.delta.content }
882
+ });
883
+ events.push({ event: "content_block_delta", data: deltaJSON });
884
+ }
885
+ for (const tc of choice.delta.tool_calls ?? []) {
886
+ if (!this.toolBlockIndex.has(tc.index)) {
887
+ if (this.textStarted) {
888
+ const stopJSON = JSON.stringify({
889
+ type: "content_block_stop",
890
+ index: this.blockIndex
891
+ });
892
+ events.push({ event: "content_block_stop", data: stopJSON });
893
+ this.blockIndex++;
894
+ this.textStarted = false;
895
+ }
896
+ this.toolBlockIndex.set(tc.index, this.blockIndex);
897
+ const blockJSON = JSON.stringify({
898
+ type: "content_block_start",
899
+ index: this.blockIndex,
900
+ content_block: {
901
+ type: "tool_use",
902
+ id: tc.id,
903
+ name: tc.function.name,
904
+ input: {}
905
+ }
906
+ });
907
+ events.push({ event: "content_block_start", data: blockJSON });
908
+ this.blockIndex++;
909
+ }
910
+ if (tc.function.arguments && tc.function.arguments !== "") {
911
+ const idx = this.toolBlockIndex.get(tc.index);
912
+ const deltaJSON = JSON.stringify({
913
+ type: "content_block_delta",
914
+ index: idx,
915
+ delta: {
916
+ type: "input_json_delta",
917
+ partial_json: tc.function.arguments
918
+ }
919
+ });
920
+ events.push({ event: "content_block_delta", data: deltaJSON });
921
+ }
922
+ }
923
+ if (choice.finish_reason != null && choice.finish_reason !== "") {
924
+ if (this.textStarted) {
925
+ const stopJSON2 = JSON.stringify({
926
+ type: "content_block_stop",
927
+ index: this.blockIndex
928
+ });
929
+ events.push({ event: "content_block_stop", data: stopJSON2 });
930
+ } else if (this.thinkingStarted && !this.thinkingStopped) {
931
+ const stopJSON2 = JSON.stringify({
932
+ type: "content_block_stop",
933
+ index: this.blockIndex
934
+ });
935
+ events.push({ event: "content_block_stop", data: stopJSON2 });
936
+ }
937
+ for (const idx of this.toolBlockIndex.values()) {
938
+ const stopJSON2 = JSON.stringify({
939
+ type: "content_block_stop",
940
+ index: idx
941
+ });
942
+ events.push({ event: "content_block_stop", data: stopJSON2 });
943
+ }
944
+ let stopReason = "end_turn";
945
+ switch (choice.finish_reason) {
946
+ case "tool_calls":
947
+ stopReason = "tool_use";
948
+ break;
949
+ case "length":
950
+ stopReason = "max_tokens";
951
+ break;
952
+ }
953
+ const deltaJSON = JSON.stringify({
954
+ type: "message_delta",
955
+ delta: { stop_reason: stopReason }
956
+ });
957
+ events.push({ event: "message_delta", data: deltaJSON });
958
+ const stopJSON = JSON.stringify({ type: "message_stop" });
959
+ events.push({ event: "message_stop", data: stopJSON });
960
+ }
961
+ return { events, done: false };
962
+ }
963
+ };
964
+ }
965
+ });
966
+
967
+ // src/adapters/index.ts
968
+ function getAdapter(provider) {
969
+ const a = adapterRegistry[provider.toLowerCase()];
970
+ if (a) return a;
971
+ return defaultOpenAIAdapter;
972
+ }
973
+ function getAdapterForModel(m) {
974
+ const pref = (m.preferred_format ?? "").trim().toLowerCase();
975
+ switch (pref) {
976
+ case "anthropic":
977
+ return new exports.AnthropicAdapter();
978
+ case "openai":
979
+ return new exports.OpenAIAdapter();
980
+ }
981
+ let hasAnthropic = false;
982
+ let hasOpenAI = false;
983
+ for (const f of m.supported_formats ?? []) {
984
+ switch (f.trim().toLowerCase()) {
985
+ case "anthropic":
986
+ hasAnthropic = true;
987
+ break;
988
+ case "openai":
989
+ hasOpenAI = true;
990
+ break;
991
+ }
992
+ }
993
+ if (hasAnthropic) return new exports.AnthropicAdapter();
994
+ if (hasOpenAI) return new exports.OpenAIAdapter();
995
+ return getAdapter((m.provider ?? "").toLowerCase());
996
+ }
997
+ exports.ProviderFormat = void 0; var adapterRegistry, defaultOpenAIAdapter;
998
+ var init_adapters = __esm({
999
+ "src/adapters/index.ts"() {
1000
+ init_anthropic();
1001
+ init_openai();
1002
+ exports.ProviderFormat = /* @__PURE__ */ ((ProviderFormat2) => {
1003
+ ProviderFormat2[ProviderFormat2["Anthropic"] = 0] = "Anthropic";
1004
+ ProviderFormat2[ProviderFormat2["OpenAI"] = 1] = "OpenAI";
1005
+ return ProviderFormat2;
1006
+ })(exports.ProviderFormat || {});
1007
+ adapterRegistry = {
1008
+ anthropic: new exports.AnthropicAdapter(),
1009
+ /** Acosmi 自有模型走 Anthropic 格式 */
1010
+ acosmi: new exports.AnthropicAdapter()
1011
+ };
1012
+ defaultOpenAIAdapter = new exports.OpenAIAdapter();
1013
+ }
1014
+ });
1015
+
1016
+ // src/index.ts
1017
+ init_types();
1018
+ init_adapters();
1019
+
1020
+ // src/auth.ts
1021
+ var authTimeoutMs = 3e4;
1022
+ async function discover(serverURL, signal) {
1023
+ let parsed;
1024
+ try {
1025
+ parsed = new URL(serverURL.replace(/\/+$/, ""));
1026
+ } catch (e) {
1027
+ throw new Error(`discover: invalid server URL: ${e instanceof Error ? e.message : String(e)}`);
1028
+ }
1029
+ const origin = `${parsed.protocol}//${parsed.host}`;
1030
+ const endpoint = `${origin}/.well-known/oauth-authorization-server/desktop`;
1031
+ const ctl = withTimeout(authTimeoutMs, signal);
1032
+ let resp;
1033
+ try {
1034
+ resp = await fetch(endpoint, { method: "GET", signal: ctl.signal });
1035
+ } catch (e) {
1036
+ throw new Error(`discover: ${e instanceof Error ? e.message : String(e)}`);
1037
+ } finally {
1038
+ ctl.dispose();
1039
+ }
1040
+ if (!resp.ok) {
1041
+ throw new Error(`discover: HTTP ${resp.status}`);
1042
+ }
1043
+ let meta;
1044
+ try {
1045
+ meta = await resp.json();
1046
+ } catch (e) {
1047
+ throw new Error(`discover: decode: ${e instanceof Error ? e.message : String(e)}`);
1048
+ }
1049
+ if (!meta.token_endpoint || !meta.authorization_endpoint) {
1050
+ throw new Error(
1051
+ `discover: metadata missing required endpoints (token=${meta.token_endpoint ?? ""}, auth=${meta.authorization_endpoint ?? ""})`
1052
+ );
1053
+ }
1054
+ return meta;
1055
+ }
1056
+ async function register(meta, appName, signal) {
1057
+ const regReq = {
1058
+ client_name: appName,
1059
+ token_endpoint_auth_method: "none",
1060
+ grant_types: ["authorization_code", "refresh_token"],
1061
+ redirect_uris: ["http://127.0.0.1/callback"],
1062
+ response_types: ["code"]
1063
+ };
1064
+ const ctl = withTimeout(authTimeoutMs, signal);
1065
+ let resp;
1066
+ try {
1067
+ resp = await fetch(meta.registration_endpoint, {
1068
+ method: "POST",
1069
+ headers: { "Content-Type": "application/json" },
1070
+ body: JSON.stringify(regReq),
1071
+ signal: ctl.signal
1072
+ });
1073
+ } catch (e) {
1074
+ throw new Error(`register: ${e instanceof Error ? e.message : String(e)}`);
1075
+ } finally {
1076
+ ctl.dispose();
1077
+ }
1078
+ if (resp.status !== 200 && resp.status !== 201) {
1079
+ throw new Error(`register: HTTP ${resp.status}`);
1080
+ }
1081
+ try {
1082
+ return await resp.json();
1083
+ } catch (e) {
1084
+ throw new Error(`register: decode: ${e instanceof Error ? e.message : String(e)}`);
1085
+ }
1086
+ }
1087
+ async function generateCodeVerifier() {
1088
+ const c = await getCrypto();
1089
+ const b = new Uint8Array(32);
1090
+ c.getRandomValues(b);
1091
+ return base64urlNoPad(b);
1092
+ }
1093
+ async function codeChallenge(verifier) {
1094
+ const c = await getCrypto();
1095
+ const buf = await c.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
1096
+ return base64urlNoPad(new Uint8Array(buf));
1097
+ }
1098
+ async function getCrypto() {
1099
+ if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
1100
+ return globalThis.crypto;
1101
+ }
1102
+ const nodeCrypto = await import('crypto');
1103
+ return nodeCrypto.webcrypto;
1104
+ }
1105
+ function base64urlNoPad(b) {
1106
+ let bin = "";
1107
+ for (let i = 0; i < b.length; i++) bin += String.fromCharCode(b[i]);
1108
+ let s;
1109
+ if (typeof btoa === "function") {
1110
+ s = btoa(bin);
1111
+ } else {
1112
+ s = Buffer.from(b).toString("base64");
1113
+ }
1114
+ return s.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1115
+ }
1116
+ var EventAuthURL = "auth_url";
1117
+ var EventComplete = "complete";
1118
+ var EventError = "error";
1119
+ var ErrDiscovery = "discovery_failed";
1120
+ var ErrRegistration = "registration_failed";
1121
+ var ErrBrowserOpen = "browser_open_failed";
1122
+ var ErrAuthDenied = "auth_denied";
1123
+ var ErrTimeout = "auth_timeout";
1124
+ var ErrTokenExchange = "token_exchange_failed";
1125
+ var ErrSSLProxy = "ssl_proxy_detected";
1126
+ function isSSLError(err) {
1127
+ const msg = err instanceof Error ? err.message : String(err);
1128
+ return msg.includes("tls:") || msg.includes("x509:") || msg.includes("certificate");
1129
+ }
1130
+ async function authorize(meta, clientID, scopes, opts = {}) {
1131
+ if (typeof process === "undefined" || !process.versions || !process.versions.node) {
1132
+ throw new Error("authorize requires Node.js environment (HTTP callback server)");
1133
+ }
1134
+ const handler = opts.handler;
1135
+ const signal = opts.signal;
1136
+ const emit = (e) => {
1137
+ if (handler) handler(e);
1138
+ };
1139
+ const verifier = await generateCodeVerifier();
1140
+ const challenge = await codeChallenge(verifier);
1141
+ const http = await import('http');
1142
+ const server = http.createServer();
1143
+ await new Promise((resolve, reject) => {
1144
+ server.once("error", reject);
1145
+ server.listen(0, "127.0.0.1", () => resolve());
1146
+ });
1147
+ const addr = server.address();
1148
+ const port = addr.port;
1149
+ const redirectURI = `http://127.0.0.1:${port}/callback`;
1150
+ let codeResolver;
1151
+ let codeRejecter;
1152
+ const codePromise = new Promise((resolve, reject) => {
1153
+ codeResolver = resolve;
1154
+ codeRejecter = reject;
1155
+ });
1156
+ server.on("request", (req, res) => {
1157
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
1158
+ if (url.pathname !== "/callback") {
1159
+ res.statusCode = 404;
1160
+ res.end();
1161
+ return;
1162
+ }
1163
+ const code = url.searchParams.get("code");
1164
+ if (!code) {
1165
+ const errMsg = url.searchParams.get("error_description") || url.searchParams.get("error") || "";
1166
+ const escaped = htmlEscape(errMsg);
1167
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1168
+ res.end(
1169
+ `<!DOCTYPE html><html><head><meta charset="utf-8"><title>\u6388\u6743\u5931\u8D25</title></head><body style="font-family:system-ui,sans-serif;text-align:center;padding:60px 20px"><h2>\u6388\u6743\u5931\u8D25</h2><p>${escaped}</p><p style="color:#888;font-size:14px">\u53EF\u4EE5\u5173\u95ED\u6B64\u7A97\u53E3\u3002</p></body></html>`
1170
+ );
1171
+ codeRejecter(new Error(`authorization denied: ${errMsg}`));
1172
+ return;
1173
+ }
1174
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1175
+ res.end(
1176
+ `<!DOCTYPE html><html><head><meta charset="utf-8"><title>\u6388\u6743\u6210\u529F</title></head><body style="font-family:system-ui,sans-serif;text-align:center;padding:60px 20px"><h2>\u6388\u6743\u6210\u529F</h2><p>\u5DF2\u5B8C\u6210\u8EAB\u4EFD\u8BA4\u8BC1, \u8BF7\u8FD4\u56DE\u5E94\u7528\u7EE7\u7EED\u4F7F\u7528\u3002</p><p style="color:#888;font-size:14px">\u6B64\u7A97\u53E3\u5C06\u5728 3 \u79D2\u540E\u81EA\u52A8\u5173\u95ED\u2026</p><script>setTimeout(function(){window.close()},3000)</script></body></html>`
1177
+ );
1178
+ codeResolver(code);
1179
+ });
1180
+ const authURL = new URL(meta.authorization_endpoint);
1181
+ authURL.searchParams.set("client_id", clientID);
1182
+ authURL.searchParams.set("redirect_uri", redirectURI);
1183
+ authURL.searchParams.set("response_type", "code");
1184
+ authURL.searchParams.set("code_challenge", challenge);
1185
+ authURL.searchParams.set("code_challenge_method", "S256");
1186
+ if (scopes.length > 0) {
1187
+ authURL.searchParams.set("scope", scopes.join(" "));
1188
+ }
1189
+ if (opts.loginHint) authURL.searchParams.set("login_hint", opts.loginHint);
1190
+ if (opts.loginMethod) authURL.searchParams.set("login_method", opts.loginMethod);
1191
+ if (opts.orgUUID) authURL.searchParams.set("orgUUID", opts.orgUUID);
1192
+ emit({ type: EventAuthURL, url: authURL.toString() });
1193
+ if (!opts.skipBrowser) {
1194
+ try {
1195
+ await openBrowser(authURL.toString());
1196
+ } catch (e) {
1197
+ emit({
1198
+ type: EventError,
1199
+ err_code: ErrBrowserOpen,
1200
+ url: authURL.toString(),
1201
+ error: e instanceof Error ? e.message : String(e)
1202
+ });
1203
+ }
1204
+ }
1205
+ let abortHandler;
1206
+ try {
1207
+ const code = await Promise.race([
1208
+ codePromise,
1209
+ new Promise((_, reject) => {
1210
+ if (signal) {
1211
+ if (signal.aborted) {
1212
+ reject(new Error("authorization timed out"));
1213
+ return;
1214
+ }
1215
+ abortHandler = () => reject(new Error("authorization timed out"));
1216
+ signal.addEventListener("abort", abortHandler);
1217
+ }
1218
+ })
1219
+ ]);
1220
+ return { result: { code, redirectURI }, verifier };
1221
+ } catch (e) {
1222
+ const msg = e instanceof Error ? e.message : String(e);
1223
+ if (msg.includes("denied")) {
1224
+ emit({ type: EventError, err_code: ErrAuthDenied, error: msg });
1225
+ } else if (msg.includes("timed out")) {
1226
+ emit({ type: EventError, err_code: ErrTimeout, error: msg });
1227
+ } else {
1228
+ emit({ type: EventError, err_code: ErrTokenExchange, error: msg });
1229
+ }
1230
+ throw e;
1231
+ } finally {
1232
+ if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
1233
+ server.close();
1234
+ }
1235
+ }
1236
+ function htmlEscape(s) {
1237
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1238
+ }
1239
+ async function exchangeCode(meta, clientID, code, redirectURI, codeVerifier, signal) {
1240
+ const data = new URLSearchParams({
1241
+ grant_type: "authorization_code",
1242
+ client_id: clientID,
1243
+ code,
1244
+ redirect_uri: redirectURI,
1245
+ code_verifier: codeVerifier
1246
+ });
1247
+ return postToken(meta.token_endpoint, data, signal);
1248
+ }
1249
+ async function exchangeCodeWithExpiry(meta, clientID, code, redirectURI, codeVerifier, expiresIn, signal) {
1250
+ const data = new URLSearchParams({
1251
+ grant_type: "authorization_code",
1252
+ client_id: clientID,
1253
+ code,
1254
+ redirect_uri: redirectURI,
1255
+ code_verifier: codeVerifier,
1256
+ expires_in: String(expiresIn)
1257
+ });
1258
+ return postToken(meta.token_endpoint, data, signal);
1259
+ }
1260
+ async function refreshToken(meta, clientID, refreshTokenValue, signal) {
1261
+ const data = new URLSearchParams({
1262
+ grant_type: "refresh_token",
1263
+ client_id: clientID,
1264
+ refresh_token: refreshTokenValue
1265
+ });
1266
+ return postToken(meta.token_endpoint, data, signal);
1267
+ }
1268
+ async function revokeToken(meta, token, signal) {
1269
+ if (!meta.revocation_endpoint || meta.revocation_endpoint === "") {
1270
+ return;
1271
+ }
1272
+ const data = new URLSearchParams({ token });
1273
+ const ctl = withTimeout(authTimeoutMs, signal);
1274
+ try {
1275
+ await fetch(meta.revocation_endpoint, {
1276
+ method: "POST",
1277
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1278
+ body: data,
1279
+ signal: ctl.signal
1280
+ });
1281
+ } catch (e) {
1282
+ throw new Error(`revoke: ${e instanceof Error ? e.message : String(e)}`);
1283
+ } finally {
1284
+ ctl.dispose();
1285
+ }
1286
+ }
1287
+ async function postToken(endpoint, data, signal) {
1288
+ const ctl = withTimeout(authTimeoutMs, signal);
1289
+ let resp;
1290
+ try {
1291
+ resp = await fetch(endpoint, {
1292
+ method: "POST",
1293
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1294
+ body: data,
1295
+ signal: ctl.signal
1296
+ });
1297
+ } catch (e) {
1298
+ throw new Error(`token request: ${e instanceof Error ? e.message : String(e)}`);
1299
+ } finally {
1300
+ ctl.dispose();
1301
+ }
1302
+ if (!resp.ok) {
1303
+ let errBody = {};
1304
+ try {
1305
+ errBody = await resp.json();
1306
+ } catch {
1307
+ }
1308
+ throw new Error(`token: HTTP ${resp.status}: ${errBody.error_description ?? ""}`);
1309
+ }
1310
+ try {
1311
+ return await resp.json();
1312
+ } catch (e) {
1313
+ throw new Error(`token: decode: ${e instanceof Error ? e.message : String(e)}`);
1314
+ }
1315
+ }
1316
+ function newTokenSet(resp, clientID, serverURL) {
1317
+ let expiresIn = resp.expires_in;
1318
+ if (expiresIn < 60) expiresIn = 60;
1319
+ return {
1320
+ access_token: resp.access_token,
1321
+ refresh_token: resp.refresh_token ?? "",
1322
+ expires_at: new Date(Date.now() + expiresIn * 1e3).toISOString(),
1323
+ scope: resp.scope ?? "",
1324
+ client_id: clientID,
1325
+ server_url: serverURL
1326
+ };
1327
+ }
1328
+ async function openBrowser(url) {
1329
+ const { spawn } = await import('child_process');
1330
+ let cmd;
1331
+ let args;
1332
+ switch (process.platform) {
1333
+ case "darwin":
1334
+ cmd = "open";
1335
+ args = [url];
1336
+ break;
1337
+ case "linux":
1338
+ cmd = "xdg-open";
1339
+ args = [url];
1340
+ break;
1341
+ case "win32":
1342
+ cmd = "rundll32";
1343
+ args = ["url.dll,FileProtocolHandler", url];
1344
+ break;
1345
+ default:
1346
+ throw new Error(`unsupported platform: ${process.platform}`);
1347
+ }
1348
+ return new Promise((resolve, reject) => {
1349
+ try {
1350
+ const child = spawn(cmd, args, { detached: true, stdio: "ignore" });
1351
+ child.on("error", reject);
1352
+ child.unref();
1353
+ resolve();
1354
+ } catch (e) {
1355
+ reject(e);
1356
+ }
1357
+ });
1358
+ }
1359
+ function withTimeout(ms, parent) {
1360
+ const ctl = new AbortController();
1361
+ const timer = setTimeout(() => ctl.abort(), ms);
1362
+ let parentHandler;
1363
+ if (parent) {
1364
+ if (parent.aborted) {
1365
+ ctl.abort();
1366
+ } else {
1367
+ parentHandler = () => ctl.abort();
1368
+ parent.addEventListener("abort", parentHandler);
1369
+ }
1370
+ }
1371
+ return {
1372
+ signal: ctl.signal,
1373
+ dispose() {
1374
+ clearTimeout(timer);
1375
+ if (parentHandler && parent) parent.removeEventListener("abort", parentHandler);
1376
+ }
1377
+ };
1378
+ }
1379
+
1380
+ // src/scopes.ts
1381
+ var ScopeAI = "ai";
1382
+ var ScopeSkills = "skills";
1383
+ var ScopeAccount = "account";
1384
+ var ScopeModels = "models";
1385
+ var ScopeModelsChat = "models:chat";
1386
+ var ScopeEntitlements = "entitlements";
1387
+ var ScopeTokenPackages = "token-packages";
1388
+ var ScopeSkillStore = "skill_store";
1389
+ var ScopeTools = "tools";
1390
+ var ScopeToolsExecute = "tools:execute";
1391
+ var ScopeWallet = "wallet";
1392
+ var ScopeWalletReadonly = "wallet:readonly";
1393
+ var ScopeProfile = "profile";
1394
+ function allScopes() {
1395
+ return [ScopeAI, ScopeSkills, ScopeAccount];
1396
+ }
1397
+ function modelScopes() {
1398
+ return [ScopeAI];
1399
+ }
1400
+ function commerceScopes() {
1401
+ return [ScopeAI, ScopeAccount];
1402
+ }
1403
+ function skillScopes() {
1404
+ return [ScopeSkills];
1405
+ }
1406
+
1407
+ // src/store.ts
1408
+ var FileTokenStore = class {
1409
+ path;
1410
+ /** 简单串行化锁 (替代 Go sync.Mutex) */
1411
+ chain = Promise.resolve();
1412
+ constructor(path) {
1413
+ if (typeof process === "undefined" || !process.versions || !process.versions.node) {
1414
+ throw new Error("FileTokenStore requires Node.js environment; use LocalStorageTokenStore or InMemoryTokenStore in browser");
1415
+ }
1416
+ if (path && path !== "") {
1417
+ this.path = path;
1418
+ } else {
1419
+ this.path = "";
1420
+ }
1421
+ }
1422
+ async resolvePath() {
1423
+ if (this.path && this.path !== "") return this.path;
1424
+ const os = await import('os');
1425
+ const path = await import('path');
1426
+ this.path = path.join(os.homedir(), ".acosmi", "tokens.json");
1427
+ return this.path;
1428
+ }
1429
+ withLock(fn) {
1430
+ const next = this.chain.then(fn, fn);
1431
+ this.chain = next.then(
1432
+ () => void 0,
1433
+ () => void 0
1434
+ );
1435
+ return next;
1436
+ }
1437
+ save(tokens) {
1438
+ return this.withLock(async () => {
1439
+ const fs = await import('fs/promises');
1440
+ const path = await import('path');
1441
+ const p = await this.resolvePath();
1442
+ const dir = path.dirname(p);
1443
+ await fs.mkdir(dir, { recursive: true, mode: 448 });
1444
+ const data = JSON.stringify(tokens, null, 2);
1445
+ await fs.writeFile(p, data, { encoding: "utf8", mode: 384 });
1446
+ });
1447
+ }
1448
+ load() {
1449
+ return this.withLock(async () => {
1450
+ const fs = await import('fs/promises');
1451
+ const p = await this.resolvePath();
1452
+ try {
1453
+ const data = await fs.readFile(p, "utf8");
1454
+ return JSON.parse(data);
1455
+ } catch (e) {
1456
+ if (isNotExistError(e)) return null;
1457
+ throw new Error(
1458
+ `read token file: ${e instanceof Error ? e.message : String(e)}`
1459
+ );
1460
+ }
1461
+ });
1462
+ }
1463
+ clear() {
1464
+ return this.withLock(async () => {
1465
+ const fs = await import('fs/promises');
1466
+ const p = await this.resolvePath();
1467
+ try {
1468
+ await fs.unlink(p);
1469
+ } catch (e) {
1470
+ if (isNotExistError(e)) return;
1471
+ throw e;
1472
+ }
1473
+ });
1474
+ }
1475
+ };
1476
+ function isNotExistError(e) {
1477
+ if (typeof e === "object" && e !== null && "code" in e) {
1478
+ return e.code === "ENOENT";
1479
+ }
1480
+ return false;
1481
+ }
1482
+ function newFileTokenStore(path) {
1483
+ return new FileTokenStore(path);
1484
+ }
1485
+ var LocalStorageTokenStore = class {
1486
+ key;
1487
+ constructor(key = "acosmi.tokens") {
1488
+ if (typeof globalThis.localStorage === "undefined") {
1489
+ throw new Error("LocalStorageTokenStore requires browser environment");
1490
+ }
1491
+ this.key = key;
1492
+ }
1493
+ async save(tokens) {
1494
+ globalThis.localStorage.setItem(this.key, JSON.stringify(tokens));
1495
+ }
1496
+ async load() {
1497
+ const data = globalThis.localStorage.getItem(this.key);
1498
+ if (data == null || data === "") return null;
1499
+ try {
1500
+ return JSON.parse(data);
1501
+ } catch {
1502
+ return null;
1503
+ }
1504
+ }
1505
+ async clear() {
1506
+ globalThis.localStorage.removeItem(this.key);
1507
+ }
1508
+ };
1509
+ var InMemoryTokenStore = class {
1510
+ tokens = null;
1511
+ async save(tokens) {
1512
+ this.tokens = tokens;
1513
+ }
1514
+ async load() {
1515
+ return this.tokens;
1516
+ }
1517
+ async clear() {
1518
+ this.tokens = null;
1519
+ }
1520
+ };
1521
+
1522
+ // src/retry.ts
1523
+ init_types();
1524
+ var DefaultRetryPolicy = {
1525
+ maxAttempts: 2,
1526
+ backoffMs: 200,
1527
+ backoffMaxMs: 2e3,
1528
+ backoffMul: 2,
1529
+ onRetryable: defaultRetryable,
1530
+ safeToRetry: defaultSafeToRetry
1531
+ };
1532
+ function defaultSafeToRetry(req) {
1533
+ switch (req.method.toUpperCase()) {
1534
+ case "GET":
1535
+ case "HEAD":
1536
+ case "OPTIONS":
1537
+ return true;
1538
+ }
1539
+ return false;
1540
+ }
1541
+ function defaultRetryable(err) {
1542
+ if (err == null) return false;
1543
+ if (err instanceof exports.StreamError) return false;
1544
+ if (err instanceof exports.HTTPError) {
1545
+ return err.statusCode >= 500 || err.statusCode === 429;
1546
+ }
1547
+ if (err instanceof exports.NetworkError) {
1548
+ return err.isTimeout() || err.isEOF();
1549
+ }
1550
+ return false;
1551
+ }
1552
+ function effectivePolicy(p) {
1553
+ if (p == null) return null;
1554
+ const out = {
1555
+ maxAttempts: p.maxAttempts && p.maxAttempts > 0 ? p.maxAttempts : DefaultRetryPolicy.maxAttempts,
1556
+ backoffMs: p.backoffMs && p.backoffMs > 0 ? p.backoffMs : DefaultRetryPolicy.backoffMs,
1557
+ backoffMaxMs: p.backoffMaxMs && p.backoffMaxMs > 0 ? p.backoffMaxMs : DefaultRetryPolicy.backoffMaxMs,
1558
+ backoffMul: p.backoffMul && p.backoffMul > 0 ? p.backoffMul : DefaultRetryPolicy.backoffMul,
1559
+ onRetryable: p.onRetryable ?? defaultRetryable,
1560
+ safeToRetry: p.safeToRetry ?? defaultSafeToRetry
1561
+ };
1562
+ return out;
1563
+ }
1564
+ var retryAfterUpperBoundMs = 6e4;
1565
+ function computeBackoff(p, attempt, err) {
1566
+ if (err instanceof exports.HTTPError && err.retryAfter > 0) {
1567
+ const ms = err.retryAfter * 1e3;
1568
+ return Math.min(ms, retryAfterUpperBoundMs);
1569
+ }
1570
+ let d = p.backoffMs;
1571
+ for (let i = 0; i < attempt; i++) {
1572
+ d = d * p.backoffMul;
1573
+ if (d > p.backoffMaxMs) return p.backoffMaxMs;
1574
+ }
1575
+ return d;
1576
+ }
1577
+
1578
+ // src/sanitize/index.ts
1579
+ var sanitize_exports = {};
1580
+ __export(sanitize_exports, {
1581
+ BlockCodeExecutionToolResult: () => BlockCodeExecutionToolResult,
1582
+ BlockContainerUpload: () => BlockContainerUpload,
1583
+ BlockDeniedError: () => BlockDeniedError,
1584
+ BlockDocument: () => BlockDocument,
1585
+ BlockImage: () => BlockImage,
1586
+ BlockMCPToolResult: () => BlockMCPToolResult,
1587
+ BlockMCPToolUse: () => BlockMCPToolUse,
1588
+ BlockRedactedThinking: () => BlockRedactedThinking,
1589
+ BlockSearchResult: () => BlockSearchResult,
1590
+ BlockServerToolUse: () => BlockServerToolUse,
1591
+ BlockText: () => BlockText,
1592
+ BlockThinking: () => BlockThinking,
1593
+ BlockToolReference: () => BlockToolReference,
1594
+ BlockToolResult: () => BlockToolResult,
1595
+ BlockToolUse: () => BlockToolUse,
1596
+ BlockVideo: () => BlockVideo,
1597
+ BlockWebSearchToolResult: () => BlockWebSearchToolResult,
1598
+ DeltaCitations: () => DeltaCitations,
1599
+ DeltaInputJSON: () => DeltaInputJSON,
1600
+ DeltaSignature: () => DeltaSignature,
1601
+ DeltaText: () => DeltaText,
1602
+ DeltaThinking: () => DeltaThinking,
1603
+ EphemeralMarkerField: () => EphemeralMarkerField,
1604
+ ErrBlockDenied: () => ErrBlockDenied,
1605
+ ErrHistoryTooDeep: () => ErrHistoryTooDeep,
1606
+ HistoryTooDeepError: () => HistoryTooDeepError,
1607
+ SizeError: () => SizeError,
1608
+ dropBlocks: () => dropBlocks,
1609
+ sanitize: () => sanitize,
1610
+ stripEphemeral: () => stripEphemeral
1611
+ });
1612
+
1613
+ // src/sanitize/types.ts
1614
+ var BlockText = "text";
1615
+ var BlockImage = "image";
1616
+ var BlockVideo = "video";
1617
+ var BlockDocument = "document";
1618
+ var BlockSearchResult = "search_result";
1619
+ var BlockThinking = "thinking";
1620
+ var BlockRedactedThinking = "redacted_thinking";
1621
+ var BlockToolUse = "tool_use";
1622
+ var BlockToolResult = "tool_result";
1623
+ var BlockToolReference = "tool_reference";
1624
+ var BlockServerToolUse = "server_tool_use";
1625
+ var BlockWebSearchToolResult = "web_search_tool_result";
1626
+ var BlockCodeExecutionToolResult = "code_execution_tool_result";
1627
+ var BlockMCPToolUse = "mcp_tool_use";
1628
+ var BlockMCPToolResult = "mcp_tool_result";
1629
+ var BlockContainerUpload = "container_upload";
1630
+ var DeltaText = "text_delta";
1631
+ var DeltaInputJSON = "input_json_delta";
1632
+ var DeltaThinking = "thinking_delta";
1633
+ var DeltaSignature = "signature_delta";
1634
+ var DeltaCitations = "citations_delta";
1635
+ var EphemeralMarkerField = "acosmi_ephemeral";
1636
+
1637
+ // src/sanitize/config.ts
1638
+ var HistoryTooDeepError = class extends Error {
1639
+ constructor() {
1640
+ super("sanitize: messages history exceeds configured depth");
1641
+ this.name = "HistoryTooDeepError";
1642
+ }
1643
+ };
1644
+ var BlockDeniedError = class extends Error {
1645
+ constructor() {
1646
+ super("sanitize: block type permanently denied");
1647
+ this.name = "BlockDeniedError";
1648
+ }
1649
+ };
1650
+ var SizeError = class extends Error {
1651
+ blockType;
1652
+ actual;
1653
+ limit;
1654
+ constructor(blockType, actual, limit) {
1655
+ super(`sanitize: ${blockType} base64 size ${actual} exceeds limit ${limit}`);
1656
+ this.name = "SizeError";
1657
+ this.blockType = blockType;
1658
+ this.actual = actual;
1659
+ this.limit = limit;
1660
+ }
1661
+ };
1662
+ var ErrHistoryTooDeep = new HistoryTooDeepError();
1663
+ var ErrBlockDenied = new BlockDeniedError();
1664
+
1665
+ // src/sanitize/history.ts
1666
+ function dropBlocks(messages, pred) {
1667
+ const droppedToolUseIDs = collectDroppedToolUseIDs(messages, pred);
1668
+ const out = [];
1669
+ for (const msg of messages) {
1670
+ if (!isPlainObject(msg)) {
1671
+ out.push(msg);
1672
+ continue;
1673
+ }
1674
+ const content = msg["content"];
1675
+ if (!Array.isArray(content)) {
1676
+ out.push(msg);
1677
+ continue;
1678
+ }
1679
+ const { kept, changed } = filterBlocks(content, pred, droppedToolUseIDs);
1680
+ if (!changed) {
1681
+ out.push(msg);
1682
+ continue;
1683
+ }
1684
+ if (kept.length === 0) {
1685
+ continue;
1686
+ }
1687
+ const newMsg = { ...msg };
1688
+ newMsg["content"] = kept;
1689
+ out.push(newMsg);
1690
+ }
1691
+ return out;
1692
+ }
1693
+ function collectDroppedToolUseIDs(messages, pred) {
1694
+ const ids = /* @__PURE__ */ new Set();
1695
+ for (const msg of messages) {
1696
+ if (!isPlainObject(msg)) continue;
1697
+ const content = msg["content"];
1698
+ if (!Array.isArray(content)) continue;
1699
+ for (const raw of content) {
1700
+ if (!isPlainObject(raw)) continue;
1701
+ if (!pred(raw)) continue;
1702
+ const t = raw["type"];
1703
+ if (typeof t !== "string") continue;
1704
+ if (t === "tool_use" || t === "server_tool_use" || t === "mcp_tool_use") {
1705
+ const id = raw["id"];
1706
+ if (typeof id === "string" && id !== "") ids.add(id);
1707
+ }
1708
+ }
1709
+ }
1710
+ return ids;
1711
+ }
1712
+ function filterBlocks(content, pred, droppedToolUseIDs) {
1713
+ const kept = [];
1714
+ let changed = false;
1715
+ for (const raw of content) {
1716
+ if (!isPlainObject(raw)) {
1717
+ kept.push(raw);
1718
+ continue;
1719
+ }
1720
+ if (pred(raw)) {
1721
+ changed = true;
1722
+ continue;
1723
+ }
1724
+ if (droppedToolUseIDs.size > 0) {
1725
+ const t = raw["type"];
1726
+ if (t === "tool_result" || t === "mcp_tool_result") {
1727
+ const id = raw["tool_use_id"];
1728
+ if (typeof id === "string" && id !== "" && droppedToolUseIDs.has(id)) {
1729
+ changed = true;
1730
+ continue;
1731
+ }
1732
+ }
1733
+ }
1734
+ kept.push(raw);
1735
+ }
1736
+ return { kept, changed };
1737
+ }
1738
+ function stripEphemeral(messages) {
1739
+ return dropBlocks(messages, (b) => {
1740
+ const t = b["type"];
1741
+ if (t === "thinking" || t === "redacted_thinking") {
1742
+ return false;
1743
+ }
1744
+ const v = b[EphemeralMarkerField];
1745
+ return v === true;
1746
+ });
1747
+ }
1748
+ function isPlainObject(v) {
1749
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1750
+ }
1751
+
1752
+ // src/sanitize/defensive.ts
1753
+ function sanitize(messages, cfg) {
1754
+ if ((cfg.maxMessagesTurns ?? 0) > 0 && messages.length > cfg.maxMessagesTurns) {
1755
+ throw ErrHistoryTooDeep;
1756
+ }
1757
+ if ((cfg.maxImageBytes ?? 0) > 0 || (cfg.maxVideoBytes ?? 0) > 0 || (cfg.maxPDFBytes ?? 0) > 0) {
1758
+ checkMediaSizes(messages, cfg);
1759
+ }
1760
+ if (cfg.permanentDenyBlocks && cfg.permanentDenyBlocks.length > 0) {
1761
+ const denySet = /* @__PURE__ */ new Set();
1762
+ for (const bt of cfg.permanentDenyBlocks) denySet.add(bt);
1763
+ messages = dropBlocks(messages, (b) => {
1764
+ const t = b["type"];
1765
+ return typeof t === "string" && denySet.has(t);
1766
+ });
1767
+ }
1768
+ return messages;
1769
+ }
1770
+ function checkMediaSizes(messages, cfg) {
1771
+ for (const msg of messages) {
1772
+ if (!isPlainObject2(msg)) continue;
1773
+ const content = msg["content"];
1774
+ if (!Array.isArray(content)) continue;
1775
+ for (const raw of content) {
1776
+ if (!isPlainObject2(raw)) continue;
1777
+ const bt = raw["type"];
1778
+ if (typeof bt !== "string") continue;
1779
+ let limit = 0;
1780
+ switch (bt) {
1781
+ case "image":
1782
+ limit = cfg.maxImageBytes ?? 0;
1783
+ break;
1784
+ case "video":
1785
+ limit = cfg.maxVideoBytes ?? 0;
1786
+ break;
1787
+ case "document":
1788
+ limit = cfg.maxPDFBytes ?? 0;
1789
+ break;
1790
+ default:
1791
+ continue;
1792
+ }
1793
+ if (limit <= 0) continue;
1794
+ const data = extractBase64Data(raw);
1795
+ if (data === "") continue;
1796
+ const actual = base64DecodedLen(data);
1797
+ if (actual > limit) {
1798
+ throw new SizeError(bt, actual, limit);
1799
+ }
1800
+ }
1801
+ }
1802
+ }
1803
+ function extractBase64Data(block) {
1804
+ const src = block["source"];
1805
+ if (!isPlainObject2(src)) return "";
1806
+ if (src["type"] !== "base64") return "";
1807
+ const dataRaw = src["data"];
1808
+ if (typeof dataRaw !== "string") return "";
1809
+ let data = dataRaw;
1810
+ const i = data.indexOf("base64,");
1811
+ if (i >= 0) {
1812
+ data = data.slice(i + "base64,".length);
1813
+ }
1814
+ return data;
1815
+ }
1816
+ function base64DecodedLen(b64) {
1817
+ const n = b64.length;
1818
+ let pad = 0;
1819
+ if (n >= 1 && b64[n - 1] === "=") pad++;
1820
+ if (n >= 2 && b64[n - 2] === "=") pad++;
1821
+ return Math.floor(n * 3 / 4) - pad;
1822
+ }
1823
+ function isPlainObject2(v) {
1824
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1825
+ }
1826
+
1827
+ // src/stream-meta.ts
1828
+ function extractAnthropicBlockMeta(eventType, data, blockTypeMap) {
1829
+ switch (eventType) {
1830
+ case "content_block_start": {
1831
+ let payload;
1832
+ try {
1833
+ payload = JSON.parse(data);
1834
+ } catch {
1835
+ return [0, "", false];
1836
+ }
1837
+ const index = payload.index ?? 0;
1838
+ const meta = {
1839
+ type: payload.content_block?.type ?? "",
1840
+ ephemeral: payload.content_block?.acosmi_ephemeral ?? false
1841
+ };
1842
+ blockTypeMap.set(index, meta);
1843
+ return [index, meta.type, meta.ephemeral];
1844
+ }
1845
+ case "content_block_delta": {
1846
+ let payload;
1847
+ try {
1848
+ payload = JSON.parse(data);
1849
+ } catch {
1850
+ return [0, "", false];
1851
+ }
1852
+ const index = payload.index ?? 0;
1853
+ const meta = blockTypeMap.get(index);
1854
+ return [index, meta?.type ?? "", meta?.ephemeral ?? false];
1855
+ }
1856
+ case "content_block_stop": {
1857
+ let payload;
1858
+ try {
1859
+ payload = JSON.parse(data);
1860
+ } catch {
1861
+ return [0, "", false];
1862
+ }
1863
+ const index = payload.index ?? 0;
1864
+ const meta = blockTypeMap.get(index);
1865
+ blockTypeMap.delete(index);
1866
+ return [index, meta?.type ?? "", meta?.ephemeral ?? false];
1867
+ }
1868
+ default:
1869
+ return [0, "", false];
1870
+ }
1871
+ }
1872
+
1873
+ // src/index.ts
1874
+ init_betas();
1875
+
1876
+ // src/client.ts
1877
+ init_types();
1878
+ init_adapters();
1879
+ init_openai();
1880
+
1881
+ // src/client-helpers.ts
1882
+ init_types();
1883
+ var maxDownloadSize = 50 * 1024 * 1024;
1884
+ var maxErrorBodySize = 1 * 1024 * 1024;
1885
+ var maxSSELineSize = 1 * 1024 * 1024;
1886
+ var modelCacheTTLMs = 5 * 60 * 1e3;
1887
+ var coefCacheTTLMs = 8 * 1e3;
1888
+ function parseHTTPErrorWithHeader(statusCode, body, header) {
1889
+ const bodyStr = typeof body === "string" ? body : new TextDecoder().decode(body);
1890
+ let retryAfter = 0;
1891
+ if (header) {
1892
+ const ra = header.get("Retry-After");
1893
+ if (ra) {
1894
+ const sec = parseInt(ra, 10);
1895
+ if (!isNaN(sec) && sec > 0) retryAfter = sec;
1896
+ }
1897
+ }
1898
+ if (bodyStr.length === 0) {
1899
+ return new exports.HTTPError(statusCode, { retryAfter });
1900
+ }
1901
+ let type = "";
1902
+ let message = "";
1903
+ try {
1904
+ const obj = JSON.parse(bodyStr);
1905
+ if (obj && typeof obj === "object") {
1906
+ const errObj = obj.error;
1907
+ if (errObj && typeof errObj === "object") {
1908
+ const e = errObj;
1909
+ if (typeof e.message === "string") message = e.message;
1910
+ if (typeof e.type === "string") type = e.type;
1911
+ }
1912
+ }
1913
+ } catch {
1914
+ }
1915
+ return new exports.HTTPError(statusCode, { type, message, retryAfter, body: bodyStr });
1916
+ }
1917
+ function classifyTransport(op, urlStr, err) {
1918
+ const ne = new exports.NetworkError(op, urlStr, err);
1919
+ if (err instanceof Error) {
1920
+ if (err.name === "AbortError") {
1921
+ ne.timeout = true;
1922
+ return ne;
1923
+ }
1924
+ const cause = err.cause;
1925
+ if (cause) {
1926
+ if (cause.code === "UND_ERR_CONNECT_TIMEOUT" || cause.code === "UND_ERR_HEADERS_TIMEOUT" || cause.code === "ETIMEDOUT") {
1927
+ ne.timeout = true;
1928
+ return ne;
1929
+ }
1930
+ if (cause.code === "ECONNRESET" || cause.code === "EPIPE" || cause.code === "ECONNREFUSED" || cause.code === "EAI_AGAIN") {
1931
+ ne.eof = true;
1932
+ return ne;
1933
+ }
1934
+ }
1935
+ const msg = err.message;
1936
+ if (msg.includes("EOF") || msg.includes("connection reset") || msg.includes("broken pipe")) {
1937
+ ne.eof = true;
1938
+ }
1939
+ }
1940
+ return ne;
1941
+ }
1942
+ function parseStreamError(data) {
1943
+ let payload;
1944
+ try {
1945
+ payload = JSON.parse(data);
1946
+ } catch {
1947
+ return new exports.StreamError({ rawError: data });
1948
+ }
1949
+ let code = payload.errorCode ?? "";
1950
+ const stage = payload.stage ?? "";
1951
+ let message = payload.message ?? "";
1952
+ const retryable = payload.retryable ?? false;
1953
+ let rawError = "";
1954
+ if (payload.error != null) {
1955
+ if (typeof payload.error === "string") {
1956
+ rawError = payload.error;
1957
+ } else if (typeof payload.error === "object") {
1958
+ const errObj = payload.error;
1959
+ rawError = JSON.stringify(payload.error);
1960
+ if (message === "" && errObj.message) message = errObj.message;
1961
+ if (code === "" && errObj.type) code = errObj.type;
1962
+ }
1963
+ }
1964
+ return new exports.StreamError({ code, stage, message, rawError, retryable });
1965
+ }
1966
+ function isOrderSuccess(status) {
1967
+ switch (status) {
1968
+ case "PAID":
1969
+ case "SUCCESS":
1970
+ case "COMPLETED":
1971
+ return true;
1972
+ }
1973
+ return false;
1974
+ }
1975
+ function isOrderTerminal(status) {
1976
+ switch (status) {
1977
+ case "PAID":
1978
+ case "SUCCESS":
1979
+ case "COMPLETED":
1980
+ case "FAILED":
1981
+ case "CANCELLED":
1982
+ case "CLOSED":
1983
+ case "EXPIRED":
1984
+ case "REFUNDED":
1985
+ return true;
1986
+ }
1987
+ return false;
1988
+ }
1989
+ async function* iterSSELines(body, maxLineBytes = maxSSELineSize) {
1990
+ const reader = body.getReader();
1991
+ const decoder = new TextDecoder("utf-8");
1992
+ let buf = "";
1993
+ try {
1994
+ while (true) {
1995
+ const { done, value } = await reader.read();
1996
+ if (done) break;
1997
+ buf += decoder.decode(value, { stream: true });
1998
+ let nlIdx;
1999
+ while ((nlIdx = buf.indexOf("\n")) >= 0) {
2000
+ let line = buf.slice(0, nlIdx);
2001
+ buf = buf.slice(nlIdx + 1);
2002
+ if (line.endsWith("\r")) line = line.slice(0, -1);
2003
+ yield line;
2004
+ }
2005
+ if (buf.length > maxLineBytes) {
2006
+ throw new Error(`SSE line exceeds ${maxLineBytes} bytes`);
2007
+ }
2008
+ }
2009
+ buf += decoder.decode();
2010
+ if (buf.length > 0) {
2011
+ if (buf.endsWith("\r")) buf = buf.slice(0, -1);
2012
+ if (buf !== "") yield buf;
2013
+ }
2014
+ } finally {
2015
+ try {
2016
+ reader.releaseLock();
2017
+ } catch {
2018
+ }
2019
+ }
2020
+ }
2021
+ async function readLimited(body, maxBytes) {
2022
+ const reader = body.getReader();
2023
+ const chunks = [];
2024
+ let total = 0;
2025
+ try {
2026
+ while (true) {
2027
+ const { done, value } = await reader.read();
2028
+ if (done) break;
2029
+ if (total + value.byteLength > maxBytes) {
2030
+ const remain = maxBytes - total;
2031
+ if (remain > 0) chunks.push(value.subarray(0, remain));
2032
+ break;
2033
+ }
2034
+ chunks.push(value);
2035
+ total += value.byteLength;
2036
+ }
2037
+ } finally {
2038
+ try {
2039
+ reader.releaseLock();
2040
+ } catch {
2041
+ }
2042
+ }
2043
+ let len = 0;
2044
+ for (const c of chunks) len += c.byteLength;
2045
+ const out = new Uint8Array(len);
2046
+ let off = 0;
2047
+ for (const c of chunks) {
2048
+ out.set(c, off);
2049
+ off += c.byteLength;
2050
+ }
2051
+ return out;
2052
+ }
2053
+ async function readLimitedText(body, maxBytes) {
2054
+ const buf = await readLimited(body, maxBytes);
2055
+ return new TextDecoder("utf-8").decode(buf);
2056
+ }
2057
+
2058
+ // src/client.ts
2059
+ var FilterStatusOK = "ok";
2060
+ var FilterStatusAdminBypass = "admin-bypass";
2061
+ var FilterStatusInternalBypass = "internal-bypass";
2062
+ var FilterStatusDisabledByFlag = "disabled-by-flag";
2063
+ var FilterStatusFallbackTkdistError = "fallback-tkdist-error";
2064
+ var FilterStatusFallbackTkdistSkew = "fallback-tkdist-deployment-skew";
2065
+ var FilterStatusFallbackNoBuckets = "fallback-no-buckets";
2066
+ var FilterStatusFallbackMissingUser = "fallback-missing-userid";
2067
+ var FilterStatusUnknown = "";
2068
+ function newDeferred() {
2069
+ let resolve;
2070
+ let reject;
2071
+ const promise = new Promise((r, j) => {
2072
+ resolve = r;
2073
+ reject = j;
2074
+ });
2075
+ return { promise, resolve, reject };
2076
+ }
2077
+ var Client = class _Client {
2078
+ /** SDK 内部使用 — 业务方法 (mixin) 通过 this.* 访问以下字段 */
2079
+ /** 服务器根地址 (已 trim 尾随 /) */
2080
+ serverURL;
2081
+ /** OAuth metadata (lazy loaded) */
2082
+ meta = null;
2083
+ /** 当前 token (内存) */
2084
+ tokens = null;
2085
+ /** token 持久化 */
2086
+ store;
2087
+ /** fetch 实现 (默认 globalThis.fetch) */
2088
+ fetchImpl;
2089
+ /** 互斥锁 (TS 用 Promise chain 替代 sync.Mutex) */
2090
+ mu = Promise.resolve();
2091
+ /** WebSocket 状态 (实际方法由 ws.ts mixin 维护) */
2092
+ ws = null;
2093
+ /** v0.15.1: token 就绪等待机制 — login 成功后 resolve, 等待方解除阻塞 */
2094
+ tokenReady = newDeferred();
2095
+ /** Login 进行中 — 等待方需等而非 fail-fast */
2096
+ loginInFlight = false;
2097
+ /** 防止 tokenReady 被多次 resolve (替代 Go sync.Once) */
2098
+ tokenReadyResolved = false;
2099
+ /** 模型能力缓存 (CrabCode 扩展) */
2100
+ modelCache = [];
2101
+ modelCacheTimeMs = 0;
2102
+ /** sanitize-bridge 配置 (默认禁用) */
2103
+ defensiveCfg = null;
2104
+ autoStripEphemeral = false;
2105
+ /** L6 (v0.15): 重试策略. null = 禁用 (v0.14.1 行为) */
2106
+ retryPolicy;
2107
+ /** V29 系数缓存 (TTL 8s, listCoefficients 内部用) */
2108
+ coefCacheData = null;
2109
+ coefCacheTimeMs = 0;
2110
+ /** 串行化锁 (替代 Go sync.Mutex) */
2111
+ coefMu = Promise.resolve();
2112
+ constructor(cfg = {}) {
2113
+ this.serverURL = (cfg.serverURL ?? "https://acosmi.com").replace(/\/+$/, "");
2114
+ this.store = cfg.store ?? defaultTokenStore();
2115
+ this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
2116
+ this.retryPolicy = effectivePolicy(cfg.retryPolicy ?? null);
2117
+ }
2118
+ /**
2119
+ * 异步初始化 (从 store 加载已有 token)
2120
+ * 替代 Go NewClient (Go 同步 IO, TS 必须 async)
2121
+ */
2122
+ static async create(cfg = {}) {
2123
+ const c = new _Client(cfg);
2124
+ try {
2125
+ const tokens = await c.store.load();
2126
+ if (tokens) {
2127
+ c.tokens = tokens;
2128
+ if (!c.tokenReadyResolved) {
2129
+ c.tokenReadyResolved = true;
2130
+ c.tokenReady.resolve();
2131
+ }
2132
+ }
2133
+ } catch {
2134
+ }
2135
+ return c;
2136
+ }
2137
+ // ===========================================================================
2138
+ // 授权生命周期
2139
+ // ===========================================================================
2140
+ /** 是否已授权 (有可用 token) */
2141
+ isAuthorized() {
2142
+ return this.tokens != null;
2143
+ }
2144
+ /** 当前 token 信息 (用于 CLI whoami 显示) */
2145
+ getTokenSet() {
2146
+ return this.tokens;
2147
+ }
2148
+ getCachedClientID() {
2149
+ return this.tokens?.client_id ?? "";
2150
+ }
2151
+ /**
2152
+ * 完整授权流程: 发现 → 注册 → 授权 → 换 token → 持久化
2153
+ * @param appName 桌面智能体名称 (如 "CrabClaw Desktop")
2154
+ * @param scopes 请求的权限范围 (参考 allScopes / modelScopes / commerceScopes 等预设)
2155
+ */
2156
+ async login(appName, scopes, signal) {
2157
+ return this.loginInternal(appName, scopes, void 0, signal);
2158
+ }
2159
+ /**
2160
+ * 带事件回调的登录流程 — CrabCode 使用
2161
+ *
2162
+ * handler 在以下时刻被调用:
2163
+ * - EventAuthURL: 授权 URL 已就绪, 调用方可展示/打开浏览器
2164
+ * - EventComplete: 登录成功, tokens 已持久化
2165
+ * - EventError: 某步骤失败, 附 ErrCode 分类码
2166
+ *
2167
+ * 当 handler 为 null 时, 行为与 login() 完全一致。
2168
+ */
2169
+ async loginWithHandler(appName, scopes, handler, opts = {}, signal) {
2170
+ return this.loginInternal(appName, scopes, { handler, ...opts }, signal);
2171
+ }
2172
+ async loginInternal(appName, scopes, opts, signal) {
2173
+ const handler = opts?.handler ?? void 0;
2174
+ const emit = (e) => {
2175
+ if (handler) handler(e);
2176
+ };
2177
+ const emitError = (code, err) => {
2178
+ emit({
2179
+ type: EventError,
2180
+ err_code: code,
2181
+ error: err instanceof Error ? err.message : String(err)
2182
+ });
2183
+ };
2184
+ this.loginInFlight = true;
2185
+ try {
2186
+ let meta;
2187
+ try {
2188
+ meta = await discover(this.serverURL, signal);
2189
+ } catch (err) {
2190
+ emitError(ErrDiscovery, err);
2191
+ throw new Error(`discovery failed: ${err instanceof Error ? err.message : String(err)}`);
2192
+ }
2193
+ this.meta = meta;
2194
+ let clientID = this.getCachedClientID();
2195
+ if (clientID === "") {
2196
+ try {
2197
+ const reg = await register(meta, appName, signal);
2198
+ clientID = reg.client_id;
2199
+ } catch (err) {
2200
+ emitError(ErrRegistration, err);
2201
+ throw new Error(
2202
+ `registration failed: ${err instanceof Error ? err.message : String(err)}`
2203
+ );
2204
+ }
2205
+ }
2206
+ let result;
2207
+ let verifier;
2208
+ try {
2209
+ const r = await authorize(meta, clientID, scopes, { ...opts, handler, signal });
2210
+ result = r.result;
2211
+ verifier = r.verifier;
2212
+ } catch (err) {
2213
+ try {
2214
+ const reg = await register(meta, appName, signal);
2215
+ clientID = reg.client_id;
2216
+ } catch (regErr) {
2217
+ emitError(ErrRegistration, regErr);
2218
+ throw new Error(
2219
+ `authorization failed (retry registration also failed): ${err instanceof Error ? err.message : String(err)}`
2220
+ );
2221
+ }
2222
+ try {
2223
+ const r = await authorize(meta, clientID, scopes, { ...opts, handler, signal });
2224
+ result = r.result;
2225
+ verifier = r.verifier;
2226
+ } catch (err2) {
2227
+ throw new Error(
2228
+ `authorization failed: ${err2 instanceof Error ? err2.message : String(err2)}`
2229
+ );
2230
+ }
2231
+ }
2232
+ let tokenResp;
2233
+ try {
2234
+ if (opts?.expiresIn && opts.expiresIn > 0) {
2235
+ tokenResp = await exchangeCodeWithExpiry(
2236
+ meta,
2237
+ clientID,
2238
+ result.code,
2239
+ result.redirectURI,
2240
+ verifier,
2241
+ opts.expiresIn,
2242
+ signal
2243
+ );
2244
+ } else {
2245
+ tokenResp = await exchangeCode(
2246
+ meta,
2247
+ clientID,
2248
+ result.code,
2249
+ result.redirectURI,
2250
+ verifier,
2251
+ signal
2252
+ );
2253
+ }
2254
+ } catch (err) {
2255
+ const code = isSSLError(err) ? ErrSSLProxy : ErrTokenExchange;
2256
+ emitError(code, err);
2257
+ throw new Error(
2258
+ `token exchange failed: ${err instanceof Error ? err.message : String(err)}`
2259
+ );
2260
+ }
2261
+ const tokens = newTokenSet(tokenResp, clientID, this.serverURL);
2262
+ this.tokens = tokens;
2263
+ if (!this.tokenReadyResolved) {
2264
+ this.tokenReadyResolved = true;
2265
+ this.tokenReady.resolve();
2266
+ }
2267
+ try {
2268
+ await this.store.save(tokens);
2269
+ } catch (err) {
2270
+ throw new Error(`save tokens: ${err instanceof Error ? err.message : String(err)}`);
2271
+ }
2272
+ emit({ type: EventComplete });
2273
+ } finally {
2274
+ this.loginInFlight = false;
2275
+ }
2276
+ }
2277
+ /** 吊销 token 并清除本地存储 */
2278
+ async logout(signal) {
2279
+ const tokens = this.tokens;
2280
+ let meta = this.meta;
2281
+ this.tokens = null;
2282
+ this.meta = null;
2283
+ this.tokenReady = newDeferred();
2284
+ this.tokenReadyResolved = false;
2285
+ if (tokens) {
2286
+ if (!meta) {
2287
+ try {
2288
+ meta = await discover(this.serverURL, signal);
2289
+ } catch (e) {
2290
+ console.warn(`[acosmi-sdk] warning: discover for revocation failed: ${e instanceof Error ? e.message : String(e)}`);
2291
+ }
2292
+ }
2293
+ if (meta) {
2294
+ try {
2295
+ await revokeToken(meta, tokens.access_token, signal);
2296
+ } catch {
2297
+ }
2298
+ try {
2299
+ await revokeToken(meta, tokens.refresh_token, signal);
2300
+ } catch {
2301
+ }
2302
+ }
2303
+ }
2304
+ await this.store.clear();
2305
+ }
2306
+ // ===========================================================================
2307
+ // Token 管理
2308
+ // ===========================================================================
2309
+ /**
2310
+ * 确保有有效的 access_token, 过期则自动刷新
2311
+ *
2312
+ * v0.15.1: 当 tokens==null 且 login 正在并发进行中时, 阻塞等待 token 就绪,
2313
+ * 避免应用启动期 "login + 多个 API 调用" 并发场景下 4+ 条 "not authorized" 误报.
2314
+ */
2315
+ async ensureToken(signal) {
2316
+ let tokens = this.tokens;
2317
+ const ready = this.tokenReady.promise;
2318
+ const inFlight = this.loginInFlight;
2319
+ if (tokens == null) {
2320
+ if (!inFlight) {
2321
+ throw new Error("not authorized, call login() first");
2322
+ }
2323
+ let abortHandler;
2324
+ try {
2325
+ await Promise.race([
2326
+ ready,
2327
+ new Promise((_, reject) => {
2328
+ if (signal) {
2329
+ if (signal.aborted) reject(new Error(`waiting for token: aborted`));
2330
+ else {
2331
+ abortHandler = () => reject(new Error(`waiting for token: aborted`));
2332
+ signal.addEventListener("abort", abortHandler);
2333
+ }
2334
+ }
2335
+ })
2336
+ ]);
2337
+ } finally {
2338
+ if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
2339
+ }
2340
+ tokens = this.tokens;
2341
+ if (tokens == null) {
2342
+ throw new Error("not authorized, call login() first");
2343
+ }
2344
+ }
2345
+ if (!tokenSetIsExpired(tokens)) {
2346
+ return tokens.access_token;
2347
+ }
2348
+ return this.withMu(async () => {
2349
+ if (this.tokens == null) {
2350
+ throw new Error("not authorized, call login() first");
2351
+ }
2352
+ if (!tokenSetIsExpired(this.tokens)) {
2353
+ return this.tokens.access_token;
2354
+ }
2355
+ if (this.meta == null) {
2356
+ try {
2357
+ this.meta = await discover(this.serverURL, signal);
2358
+ } catch (e) {
2359
+ throw new Error(
2360
+ `discover for refresh: ${e instanceof Error ? e.message : String(e)}`
2361
+ );
2362
+ }
2363
+ }
2364
+ let tokenResp;
2365
+ try {
2366
+ tokenResp = await refreshToken(this.meta, this.tokens.client_id, this.tokens.refresh_token, signal);
2367
+ } catch (e) {
2368
+ throw new Error(`refresh token: ${e instanceof Error ? e.message : String(e)}`);
2369
+ }
2370
+ this.tokens = newTokenSet(tokenResp, this.tokens.client_id, this.serverURL);
2371
+ try {
2372
+ await this.store.save(this.tokens);
2373
+ } catch (e) {
2374
+ console.warn(`[acosmi-sdk] warning: save refreshed token failed: ${e instanceof Error ? e.message : String(e)}`);
2375
+ }
2376
+ return this.tokens.access_token;
2377
+ });
2378
+ }
2379
+ /** 强制刷新 token (用于 401 重试) */
2380
+ async forceRefresh(signal) {
2381
+ return this.withMu(async () => {
2382
+ if (this.tokens == null) {
2383
+ throw new Error("no tokens to refresh");
2384
+ }
2385
+ if (this.meta == null) {
2386
+ this.meta = await discover(this.serverURL, signal);
2387
+ }
2388
+ const tokenResp = await refreshToken(
2389
+ this.meta,
2390
+ this.tokens.client_id,
2391
+ this.tokens.refresh_token,
2392
+ signal
2393
+ );
2394
+ this.tokens = newTokenSet(tokenResp, this.tokens.client_id, this.serverURL);
2395
+ try {
2396
+ await this.store.save(this.tokens);
2397
+ } catch (e) {
2398
+ console.warn(
2399
+ `[acosmi-sdk] warning: save refreshed token failed: ${e instanceof Error ? e.message : String(e)}`
2400
+ );
2401
+ }
2402
+ });
2403
+ }
2404
+ /** 互斥锁 helper (替代 Go sync.Mutex) */
2405
+ withMu(fn) {
2406
+ const next = this.mu.then(fn, fn);
2407
+ this.mu = next.then(
2408
+ () => void 0,
2409
+ () => void 0
2410
+ );
2411
+ return next;
2412
+ }
2413
+ // ===========================================================================
2414
+ // Managed Models
2415
+ // ===========================================================================
2416
+ /**
2417
+ * 获取可用的托管模型列表.
2418
+ *
2419
+ * V30 二轮审计 D-P1-2: 此方法不返回 entitlement-filter-status header.
2420
+ * UI 想根据 fallback 状态显示降级提示, 请改调 listModelsWithStatus.
2421
+ */
2422
+ async listModels(signal) {
2423
+ const r = await this.listModelsWithStatus(signal);
2424
+ return r.models;
2425
+ }
2426
+ /**
2427
+ * 获取可用模型列表, 同时返回 X-Entitlement-Filter-Status header.
2428
+ *
2429
+ * V30 二轮审计 D-P1-2: 老 listModels 丢弃 header 让 SDK 用户无法识别 fail-OPEN 降级状态,
2430
+ * 此方法暴露 status 让 UI 可:
2431
+ * - status === 'ok' → 正常显示 BucketInfo 余量
2432
+ * - status === 'fallback-tkdist-error' → 灰显余量 + toast "tk-dist 离线, 模型列表降级"
2433
+ * - status === 'disabled-by-flag' → 不显示余量 (运维灰度中)
2434
+ * - status === '' (Unknown) → 老 nexus, 按老 v0.17 行为 (不显示 BucketInfo)
2435
+ */
2436
+ async listModelsWithStatus(signal) {
2437
+ const { result, headers } = await this.doJSONFull(
2438
+ "GET",
2439
+ "/managed-models",
2440
+ null,
2441
+ signal
2442
+ );
2443
+ this.modelCache = result.data;
2444
+ this.modelCacheTimeMs = Date.now();
2445
+ let status = "";
2446
+ if (headers) {
2447
+ const h = headers.get("X-Entitlement-Filter-Status");
2448
+ if (h) status = h;
2449
+ }
2450
+ return { models: result.data, status };
2451
+ }
2452
+ /** 查询当前用户账户级权益总览 (v0.19+) */
2453
+ async getQuotaSummary(signal) {
2454
+ const resp = await this.doJSON(
2455
+ "GET",
2456
+ "/entitlements/quota-summary",
2457
+ null,
2458
+ signal
2459
+ );
2460
+ return resp.data;
2461
+ }
2462
+ /**
2463
+ * 查询单个模型的能力矩阵
2464
+ * 优先从 listModels 缓存读取, miss 时调用 listModels 刷新
2465
+ */
2466
+ async getModelCapabilities(modelID, signal) {
2467
+ let caps = this.getCachedCapabilities(modelID);
2468
+ if (caps) return caps;
2469
+ await this.listModels(signal);
2470
+ caps = this.getCachedCapabilities(modelID);
2471
+ if (caps) return caps;
2472
+ return zeroModelCapabilities();
2473
+ }
2474
+ getCachedCapabilities(modelID) {
2475
+ if (this.modelCache.length === 0 || Date.now() - this.modelCacheTimeMs > modelCacheTTLMs) {
2476
+ return null;
2477
+ }
2478
+ for (const m of this.modelCache) {
2479
+ if (m.id === modelID || m.modelId === modelID) {
2480
+ return m.capabilities;
2481
+ }
2482
+ }
2483
+ return null;
2484
+ }
2485
+ /** 从缓存中查找完整 ManagedModel (未命中返 null) */
2486
+ getCachedModel(modelID) {
2487
+ for (const m of this.modelCache) {
2488
+ if (m.id === modelID || m.modelId === modelID) return m;
2489
+ }
2490
+ return null;
2491
+ }
2492
+ /** 测试辅助: 把占位 ManagedModel 塞入缓存. 仅测试用. */
2493
+ primeModelCacheForTest(...ids) {
2494
+ for (const id of ids) {
2495
+ this.modelCache.push({
2496
+ id,
2497
+ name: "",
2498
+ provider: "anthropic",
2499
+ modelId: id,
2500
+ maxTokens: 0,
2501
+ isEnabled: true,
2502
+ capabilities: zeroModelCapabilities()
2503
+ });
2504
+ }
2505
+ this.modelCacheTimeMs = Date.now();
2506
+ }
2507
+ /**
2508
+ * 确保指定 modelID 的 ManagedModel 已在缓存中。
2509
+ *
2510
+ * 语义:
2511
+ * 1. 若缓存命中 → 直接返回
2512
+ * 2. 若未命中 → 调 listModels 刷新一次
2513
+ * 3. 刷新后仍未命中 → 抛 ModelNotFoundError
2514
+ *
2515
+ * 根因修复: 消除未预热场景下 provider="anthropic" 硬编码回退,
2516
+ * 该回退会让 DashScope/Zhipu/DeepSeek 等 non-anthropic 模型被按 Anthropic
2517
+ * 格式编码并打到错误的 /anthropic 端点。
2518
+ */
2519
+ async ensureModelCached(modelID, signal) {
2520
+ let m = this.getCachedModel(modelID);
2521
+ if (m) return m;
2522
+ await this.listModels(signal);
2523
+ m = this.getCachedModel(modelID);
2524
+ if (m) return m;
2525
+ throw new exports.ModelNotFoundError(modelID);
2526
+ }
2527
+ // ===========================================================================
2528
+ // Chat
2529
+ // ===========================================================================
2530
+ /**
2531
+ * 构建完整的聊天请求体 (v0.5.0 adapter 模式)
2532
+ *
2533
+ * 根据 provider 选择 adapter, 委托 buildRequestBody 构建格式化的请求体。
2534
+ *
2535
+ * v0.13.x: 前置 ensureModelCached, 消除冷缓存硬编码回退。未知 modelID 抛 ModelNotFoundError.
2536
+ */
2537
+ async buildChatRequest(modelID, req, signal) {
2538
+ if (typeof this.applyRequestSanitizers === "function") {
2539
+ this.applyRequestSanitizers(req);
2540
+ }
2541
+ const m = await this.ensureModelCached(modelID, signal);
2542
+ const adapter = getAdapterForModel(m);
2543
+ const caps = this.getCachedCapabilities(modelID) ?? zeroModelCapabilities();
2544
+ const body = adapter.buildRequestBody(caps, req);
2545
+ return { body: JSON.stringify(body), adapter };
2546
+ }
2547
+ /**
2548
+ * 同步聊天 (适合短回复)
2549
+ * 响应的 tokenRemaining / callRemaining 字段来自服务端 Header, 反映结算后余额
2550
+ * v0.5.0: 根据 provider 自动路由到 /anthropic 或 /chat 端点
2551
+ */
2552
+ async chat(modelID, req, signal) {
2553
+ req.stream = false;
2554
+ const ctl = withRequestTimeout(5 * 60 * 1e3, signal);
2555
+ try {
2556
+ const { body, adapter } = await this.buildChatRequest(modelID, req, ctl.signal);
2557
+ const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2558
+ const { result, headers } = await this.doJSONFullRaw("POST", endpoint, body, ctl.signal);
2559
+ const resp = adapter.parseResponse(result);
2560
+ const v1 = headers.get("X-Token-Remaining");
2561
+ if (v1) {
2562
+ const n = parseInt(v1, 10);
2563
+ if (!isNaN(n)) resp.tokenRemaining = n;
2564
+ }
2565
+ const v2 = headers.get("X-Call-Remaining");
2566
+ if (v2) {
2567
+ const n = parseInt(v2, 10);
2568
+ if (!isNaN(n)) resp.callRemaining = n;
2569
+ }
2570
+ const v3 = headers.get("X-Token-Remaining-Model");
2571
+ if (v3) {
2572
+ const n = parseInt(v3, 10);
2573
+ if (!isNaN(n)) resp.modelTokenRemaining = n;
2574
+ }
2575
+ const v4 = headers.get("X-Token-Remaining-Model-ETU");
2576
+ if (v4) {
2577
+ const n = parseInt(v4, 10);
2578
+ if (!isNaN(n)) resp.modelTokenRemainingETU = n;
2579
+ }
2580
+ return resp;
2581
+ } finally {
2582
+ ctl.dispose();
2583
+ }
2584
+ }
2585
+ /**
2586
+ * Anthropic 原生格式同步聊天
2587
+ * v0.5.0: 根据 provider 自动路由
2588
+ * Anthropic → chatMessagesAnthropic (现有路径, POST /anthropic)
2589
+ * 其他厂商 → chatMessagesOpenAI (POST /chat, 响应转换为 AnthropicResponse)
2590
+ */
2591
+ async chatMessages(modelID, req, signal) {
2592
+ const m = await this.ensureModelCached(modelID, signal);
2593
+ const adapter = getAdapterForModel(m);
2594
+ if (adapter.format() === 0 /* Anthropic */) {
2595
+ return this.chatMessagesAnthropic(modelID, req, adapter, signal);
2596
+ }
2597
+ return this.chatMessagesOpenAI(modelID, req, adapter, signal);
2598
+ }
2599
+ async chatMessagesAnthropic(modelID, req, adapter, signal) {
2600
+ req.stream = false;
2601
+ const ctl = withRequestTimeout(5 * 60 * 1e3, signal);
2602
+ try {
2603
+ const caps = this.getCachedCapabilities(modelID) ?? zeroModelCapabilities();
2604
+ const body = adapter.buildRequestBody(caps, req);
2605
+ const data = JSON.stringify(body);
2606
+ const { result } = await this.doJSONFullRaw(
2607
+ "POST",
2608
+ `/managed-models/${encodeURIComponent(modelID)}/anthropic`,
2609
+ data,
2610
+ ctl.signal
2611
+ );
2612
+ const rawStr = new TextDecoder().decode(result);
2613
+ try {
2614
+ const wrapper = JSON.parse(rawStr);
2615
+ if (wrapper.data != null && wrapper.data !== null) {
2616
+ const bizErr = apiResponseBusinessError({
2617
+ code: wrapper.code ?? 0,
2618
+ message: wrapper.message,
2619
+ data: wrapper.data
2620
+ });
2621
+ if (bizErr) throw bizErr;
2622
+ return wrapper.data;
2623
+ }
2624
+ } catch (e) {
2625
+ if (e && typeof e === "object" && "name" in e && e.name === "BusinessError") throw e;
2626
+ }
2627
+ try {
2628
+ return JSON.parse(rawStr);
2629
+ } catch (e) {
2630
+ throw new Error(`decode anthropic response: ${e instanceof Error ? e.message : String(e)}`);
2631
+ }
2632
+ } finally {
2633
+ ctl.dispose();
2634
+ }
2635
+ }
2636
+ async chatMessagesOpenAI(modelID, req, adapter, signal) {
2637
+ req.stream = false;
2638
+ const ctl = withRequestTimeout(5 * 60 * 1e3, signal);
2639
+ try {
2640
+ const caps = this.getCachedCapabilities(modelID) ?? zeroModelCapabilities();
2641
+ const body = adapter.buildRequestBody(caps, req);
2642
+ const data = JSON.stringify(body);
2643
+ const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2644
+ const { result } = await this.doJSONFullRaw("POST", endpoint, data, ctl.signal);
2645
+ const { parseOpenAIResponseToAnthropic: parseOpenAIResponseToAnthropic2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
2646
+ return parseOpenAIResponseToAnthropic2(result);
2647
+ } finally {
2648
+ ctl.dispose();
2649
+ }
2650
+ }
2651
+ /**
2652
+ * 流式聊天 (SSE), 通过 async generator 返回事件
2653
+ * v0.5.0: 根据 adapter 路由端点
2654
+ */
2655
+ chatStream(modelID, req, signal) {
2656
+ return {
2657
+ [Symbol.asyncIterator]: () => this.chatStreamGen(modelID, req, signal, false)
2658
+ };
2659
+ }
2660
+ /**
2661
+ * Anthropic 原生格式流式聊天 (SSE)
2662
+ * 调用 POST /managed-models/:id/anthropic, SSE 事件为 Anthropic 协议格式
2663
+ * 无 started/settled/failed 自定义事件, 无 data: [DONE], message_stop 为自然结束
2664
+ */
2665
+ chatMessagesStream(modelID, req, signal) {
2666
+ return {
2667
+ [Symbol.asyncIterator]: () => this.chatMessagesStreamGen(modelID, req, signal, false)
2668
+ };
2669
+ }
2670
+ async *chatStreamGen(modelID, req, signal, retried) {
2671
+ req.stream = true;
2672
+ const { body, adapter } = await this.buildChatRequest(modelID, req, signal);
2673
+ const token = await this.ensureToken(signal);
2674
+ const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2675
+ const url = this.apiURL(endpoint);
2676
+ let resp;
2677
+ try {
2678
+ resp = await this.fetchImpl(url, {
2679
+ method: "POST",
2680
+ headers: {
2681
+ Authorization: `Bearer ${token}`,
2682
+ "Content-Type": "application/json",
2683
+ Accept: "text/event-stream"
2684
+ },
2685
+ body,
2686
+ signal
2687
+ });
2688
+ } catch (e) {
2689
+ throw classifyTransport("POST " + endpoint, url, e);
2690
+ }
2691
+ if (resp.status === 401 && !retried) {
2692
+ try {
2693
+ await resp.body?.cancel();
2694
+ } catch {
2695
+ }
2696
+ try {
2697
+ await this.forceRefresh(signal);
2698
+ } catch (refreshErr) {
2699
+ throw new Error(
2700
+ `stream: unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
2701
+ );
2702
+ }
2703
+ yield* this.chatStreamGen(modelID, req, signal, true);
2704
+ return;
2705
+ }
2706
+ if (!resp.ok) {
2707
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
2708
+ throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
2709
+ }
2710
+ if (!resp.body) {
2711
+ throw new Error("stream: empty response body");
2712
+ }
2713
+ let blockTypeMap = null;
2714
+ if (adapter.format() === 0 /* Anthropic */) {
2715
+ blockTypeMap = /* @__PURE__ */ new Map();
2716
+ }
2717
+ let currentEvent = "";
2718
+ for await (const line of iterSSELines(resp.body)) {
2719
+ if (line.startsWith("event:")) {
2720
+ currentEvent = line.slice("event:".length).trim();
2721
+ } else if (line.startsWith("data:")) {
2722
+ const data = line.slice("data:".length).trim();
2723
+ const parsed = adapter.parseStreamLine(currentEvent, data);
2724
+ if (parsed.done) return;
2725
+ const ev = parsed.event;
2726
+ if (blockTypeMap) {
2727
+ const [idx, bt, eph] = extractAnthropicBlockMeta(currentEvent, data, blockTypeMap);
2728
+ if (bt !== "") {
2729
+ ev.blockIndex = idx;
2730
+ ev.blockType = bt;
2731
+ ev.ephemeral = eph;
2732
+ }
2733
+ }
2734
+ yield ev;
2735
+ }
2736
+ }
2737
+ }
2738
+ async *chatMessagesStreamGen(modelID, req, signal, retried) {
2739
+ req.stream = true;
2740
+ const { body, adapter } = await this.buildChatRequest(modelID, req, signal);
2741
+ const token = await this.ensureToken(signal);
2742
+ const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2743
+ const url = this.apiURL(endpoint);
2744
+ let resp;
2745
+ try {
2746
+ resp = await this.fetchImpl(url, {
2747
+ method: "POST",
2748
+ headers: {
2749
+ Authorization: `Bearer ${token}`,
2750
+ "Content-Type": "application/json",
2751
+ Accept: "text/event-stream"
2752
+ },
2753
+ body,
2754
+ signal
2755
+ });
2756
+ } catch (e) {
2757
+ throw classifyTransport("POST " + endpoint, url, e);
2758
+ }
2759
+ if (resp.status === 401 && !retried) {
2760
+ try {
2761
+ await resp.body?.cancel();
2762
+ } catch {
2763
+ }
2764
+ try {
2765
+ await this.forceRefresh(signal);
2766
+ } catch (refreshErr) {
2767
+ throw new Error(
2768
+ `messages stream: unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
2769
+ );
2770
+ }
2771
+ yield* this.chatMessagesStreamGen(modelID, req, signal, true);
2772
+ return;
2773
+ }
2774
+ if (!resp.ok) {
2775
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
2776
+ throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
2777
+ }
2778
+ if (!resp.body) {
2779
+ throw new Error("messages stream: empty response body");
2780
+ }
2781
+ if (adapter.format() === 1 /* OpenAI */) {
2782
+ const converter = newOpenAIStreamConverter();
2783
+ for await (const line of iterSSELines(resp.body)) {
2784
+ if (line.startsWith("event:")) {
2785
+ line.slice("event:".length).trim();
2786
+ } else if (line.startsWith("data:")) {
2787
+ const data = line.slice("data:".length).trim();
2788
+ const { events, done } = converter.convert(data);
2789
+ for (const ev of events) yield ev;
2790
+ if (done) return;
2791
+ }
2792
+ }
2793
+ } else {
2794
+ const blockTypeMap = /* @__PURE__ */ new Map();
2795
+ let currentEvent = "";
2796
+ for await (const line of iterSSELines(resp.body)) {
2797
+ if (line.startsWith("event:")) {
2798
+ currentEvent = line.slice("event:".length).trim();
2799
+ } else if (line.startsWith("data:")) {
2800
+ const data = line.slice("data:".length).trim();
2801
+ const ev = { event: currentEvent, data };
2802
+ const [idx, bt, eph] = extractAnthropicBlockMeta(currentEvent, data, blockTypeMap);
2803
+ if (bt !== "") {
2804
+ ev.blockIndex = idx;
2805
+ ev.blockType = bt;
2806
+ ev.ephemeral = eph;
2807
+ }
2808
+ yield ev;
2809
+ }
2810
+ }
2811
+ }
2812
+ }
2813
+ /**
2814
+ * 流式聊天, 自动解析结算事件和搜索来源
2815
+ *
2816
+ * 返回单一 tagged AsyncIterable, kind 区分 4 种事件:
2817
+ * - kind='content': 内容增量事件
2818
+ * - kind='sources': 搜索来源
2819
+ * - kind='settle': 结算 (token 消耗 + 剩余余额)
2820
+ *
2821
+ * Go 版本 4 channels (eventCh/sourcesCh/settleCh/errCh), 错误抛出取代 errCh。
2822
+ */
2823
+ async *chatStreamWithUsage(modelID, req, signal) {
2824
+ for await (const ev of this.chatStream(modelID, req, signal)) {
2825
+ const s = parseSettlement(ev);
2826
+ if (s) {
2827
+ yield { kind: "settle", event: s };
2828
+ continue;
2829
+ }
2830
+ const src = parseSourcesEvent(ev);
2831
+ if (src) {
2832
+ yield { kind: "sources", event: src };
2833
+ continue;
2834
+ }
2835
+ if (ev.event === "started") continue;
2836
+ if (ev.event === "failed" || ev.event === "error") {
2837
+ throw parseStreamError(ev.data);
2838
+ }
2839
+ yield { kind: "content", event: ev };
2840
+ }
2841
+ }
2842
+ // ===========================================================================
2843
+ // Internal HTTP
2844
+ // ===========================================================================
2845
+ apiURL(path) {
2846
+ let base = this.serverURL;
2847
+ if (!base.endsWith("/api/v4")) {
2848
+ base += "/api/v4";
2849
+ }
2850
+ return base + path;
2851
+ }
2852
+ /** GET/POST/... 通用 JSON 调用 (返回 result 已 typed) */
2853
+ async doJSON(method, path, body, signal) {
2854
+ const r = await this.doJSONFull(method, path, body, signal);
2855
+ return r.result;
2856
+ }
2857
+ /** 与 doJSON 相同, 但返回响应 Headers (用于提取 X-Token-Remaining 等) */
2858
+ async doJSONFull(method, path, body, signal) {
2859
+ return this.doJSONFullInternal(method, path, body, signal, false);
2860
+ }
2861
+ async doJSONFullInternal(method, path, body, signal, retried) {
2862
+ const ctl = withRequestTimeout(3e4, signal);
2863
+ try {
2864
+ const token = await this.ensureToken(ctl.signal);
2865
+ let bodyStr = null;
2866
+ if (body != null) {
2867
+ bodyStr = typeof body === "string" ? body : JSON.stringify(body);
2868
+ }
2869
+ const url = this.apiURL(path);
2870
+ const headers = {
2871
+ Authorization: `Bearer ${token}`
2872
+ };
2873
+ if (bodyStr != null) headers["Content-Type"] = "application/json";
2874
+ const resp = await this.doRequestWithRetry(
2875
+ { method, url, headers, body: bodyStr ?? void 0 },
2876
+ ctl.signal
2877
+ );
2878
+ if (resp.status === 401 && !retried) {
2879
+ try {
2880
+ await resp.body?.cancel();
2881
+ } catch {
2882
+ }
2883
+ try {
2884
+ await this.forceRefresh(ctl.signal);
2885
+ } catch (refreshErr) {
2886
+ throw new Error(
2887
+ `unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
2888
+ );
2889
+ }
2890
+ return this.doJSONFullInternal(method, path, body, signal, true);
2891
+ }
2892
+ if (resp.status < 200 || resp.status >= 300) {
2893
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
2894
+ throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
2895
+ }
2896
+ const text = await resp.text();
2897
+ const result = JSON.parse(text);
2898
+ if (result && typeof result === "object" && "code" in result) {
2899
+ const bizErr = apiResponseBusinessError(result);
2900
+ if (bizErr) throw bizErr;
2901
+ }
2902
+ return { result, headers: resp.headers };
2903
+ } catch (e) {
2904
+ if (e instanceof Error && e.name === "AbortError") {
2905
+ throw classifyTransport(`${method} ${path}`, this.apiURL(path), e);
2906
+ }
2907
+ throw e;
2908
+ } finally {
2909
+ ctl.dispose();
2910
+ }
2911
+ }
2912
+ /** doJSONFull 的 raw bytes 变体 (chat 用, 不立即 JSON.parse) */
2913
+ async doJSONFullRaw(method, path, body, signal) {
2914
+ return this.doJSONFullRawInternal(method, path, body, signal, false);
2915
+ }
2916
+ async doJSONFullRawInternal(method, path, body, signal, retried) {
2917
+ const ctl = withRequestTimeout(3e4, signal);
2918
+ try {
2919
+ const token = await this.ensureToken(ctl.signal);
2920
+ let bodyStr = null;
2921
+ if (body != null) {
2922
+ bodyStr = typeof body === "string" ? body : JSON.stringify(body);
2923
+ }
2924
+ const url = this.apiURL(path);
2925
+ const headers = {
2926
+ Authorization: `Bearer ${token}`
2927
+ };
2928
+ if (bodyStr != null) headers["Content-Type"] = "application/json";
2929
+ const resp = await this.doRequestWithRetry(
2930
+ { method, url, headers, body: bodyStr ?? void 0 },
2931
+ ctl.signal
2932
+ );
2933
+ if (resp.status === 401 && !retried) {
2934
+ try {
2935
+ await resp.body?.cancel();
2936
+ } catch {
2937
+ }
2938
+ try {
2939
+ await this.forceRefresh(ctl.signal);
2940
+ } catch (refreshErr) {
2941
+ throw new Error(
2942
+ `unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
2943
+ );
2944
+ }
2945
+ return this.doJSONFullRawInternal(method, path, body, signal, true);
2946
+ }
2947
+ if (resp.status < 200 || resp.status >= 300) {
2948
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
2949
+ throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
2950
+ }
2951
+ const buf = new Uint8Array(await resp.arrayBuffer());
2952
+ return { result: buf, headers: resp.headers };
2953
+ } finally {
2954
+ ctl.dispose();
2955
+ }
2956
+ }
2957
+ /**
2958
+ * 公共端点请求
2959
+ * 有 token 时自动附带 (享受认证用户待遇), 无 token 时匿名请求
2960
+ * 不做 401 重试 (公共端点不应要求认证)
2961
+ */
2962
+ async doPublicJSON(method, path, body, signal) {
2963
+ const ctl = withRequestTimeout(3e4, signal);
2964
+ try {
2965
+ let token = "";
2966
+ try {
2967
+ token = await this.ensureToken(ctl.signal);
2968
+ } catch {
2969
+ }
2970
+ let bodyStr = null;
2971
+ if (body != null) {
2972
+ bodyStr = typeof body === "string" ? body : JSON.stringify(body);
2973
+ }
2974
+ const url = this.apiURL(path);
2975
+ const headers = {};
2976
+ if (token) headers["Authorization"] = `Bearer ${token}`;
2977
+ if (bodyStr != null) headers["Content-Type"] = "application/json";
2978
+ const resp = await this.doRequestWithRetry(
2979
+ { method, url, headers, body: bodyStr ?? void 0 },
2980
+ ctl.signal
2981
+ );
2982
+ if (resp.status < 200 || resp.status >= 300) {
2983
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
2984
+ throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
2985
+ }
2986
+ const text = await resp.text();
2987
+ const result = JSON.parse(text);
2988
+ if (result && typeof result === "object" && "code" in result) {
2989
+ const bizErr = apiResponseBusinessError(result);
2990
+ if (bizErr) throw bizErr;
2991
+ }
2992
+ return result;
2993
+ } finally {
2994
+ ctl.dispose();
2995
+ }
2996
+ }
2997
+ /**
2998
+ * fetch 包装 — 错误经 classifyTransport 转 NetworkError
2999
+ * 6 处原始 fetch() 全部走此 helper
3000
+ */
3001
+ async doRequest(req, signal) {
3002
+ try {
3003
+ return await this.fetchImpl(req.url, {
3004
+ method: req.method,
3005
+ headers: req.headers,
3006
+ body: req.body,
3007
+ signal
3008
+ });
3009
+ } catch (e) {
3010
+ throw classifyTransport(req.method + " " + new URL(req.url).pathname, req.url, e);
3011
+ }
3012
+ }
3013
+ /**
3014
+ * 带 RetryPolicy 的 doRequest 包装 — 仅用于同步 (非流式) 路径.
3015
+ *
3016
+ * 重试触发:
3017
+ * - transport 层 err (NetworkError isTimeout/isEOF) → 重试
3018
+ * - HTTP 5xx / 429 → 主动构造 HTTPError 喂给 onRetryable, 默认重试
3019
+ * - 其他 (HTTP 2xx/3xx/4xx 非 429 / DNS / StreamError) → 不重试
3020
+ *
3021
+ * 流式路径 (chatMessagesStreamGen / chatStreamGen) **不得**调用此函数, 必须直接用 doRequest.
3022
+ */
3023
+ async doRequestWithRetry(req, signal) {
3024
+ const policy = this.retryPolicy;
3025
+ if (!policy || !policy.safeToRetry({ method: req.method, url: req.url })) {
3026
+ return this.doRequest(req, signal);
3027
+ }
3028
+ let lastErr = null;
3029
+ for (let attempt = 0; attempt < policy.maxAttempts; attempt++) {
3030
+ try {
3031
+ const resp = await this.doRequest(req, signal);
3032
+ if (resp.status < 500 && resp.status !== 429) {
3033
+ return resp;
3034
+ }
3035
+ const bodyPeek = await readLimited(resp.body, maxErrorBodySize);
3036
+ lastErr = parseHTTPErrorWithHeader(resp.status, bodyPeek, resp.headers);
3037
+ } catch (e) {
3038
+ lastErr = e;
3039
+ }
3040
+ if (attempt + 1 === policy.maxAttempts) break;
3041
+ if (!policy.onRetryable(lastErr)) break;
3042
+ const backoff = computeBackoff(policy, attempt, lastErr);
3043
+ await sleep(backoff, signal);
3044
+ }
3045
+ throw lastErr;
3046
+ }
3047
+ };
3048
+ function defaultTokenStore() {
3049
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
3050
+ try {
3051
+ return new FileTokenStore();
3052
+ } catch {
3053
+ return new InMemoryTokenStore();
3054
+ }
3055
+ }
3056
+ if (typeof globalThis.localStorage !== "undefined") {
3057
+ try {
3058
+ return new LocalStorageTokenStore();
3059
+ } catch {
3060
+ return new InMemoryTokenStore();
3061
+ }
3062
+ }
3063
+ return new InMemoryTokenStore();
3064
+ }
3065
+ function zeroModelCapabilities() {
3066
+ return {
3067
+ supports_thinking: false,
3068
+ supports_adaptive_thinking: false,
3069
+ supports_isp: false,
3070
+ supports_web_search: false,
3071
+ supports_tool_search: false,
3072
+ supports_structured_output: false,
3073
+ supports_effort: false,
3074
+ supports_max_effort: false,
3075
+ supports_fast_mode: false,
3076
+ supports_auto_mode: false,
3077
+ supports_1m_context: false,
3078
+ supports_prompt_cache: false,
3079
+ supports_cache_editing: false,
3080
+ supports_token_efficient: false,
3081
+ supports_redact_thinking: false,
3082
+ max_input_tokens: 0,
3083
+ max_output_tokens: 0
3084
+ };
3085
+ }
3086
+ function withRequestTimeout(ms, parent) {
3087
+ const ctl = new AbortController();
3088
+ const timer = setTimeout(() => ctl.abort(), ms);
3089
+ let parentHandler;
3090
+ if (parent) {
3091
+ if (parent.aborted) {
3092
+ ctl.abort();
3093
+ } else {
3094
+ parentHandler = () => ctl.abort();
3095
+ parent.addEventListener("abort", parentHandler);
3096
+ }
3097
+ }
3098
+ return {
3099
+ signal: ctl.signal,
3100
+ dispose() {
3101
+ clearTimeout(timer);
3102
+ if (parentHandler && parent) parent.removeEventListener("abort", parentHandler);
3103
+ }
3104
+ };
3105
+ }
3106
+ async function sleep(ms, signal) {
3107
+ if (ms <= 0) return;
3108
+ if (signal && signal.aborted) throw new Error("aborted");
3109
+ return new Promise((resolve, reject) => {
3110
+ const t = setTimeout(() => {
3111
+ if (signal && abortHandler) signal.removeEventListener("abort", abortHandler);
3112
+ resolve();
3113
+ }, ms);
3114
+ let abortHandler;
3115
+ if (signal) {
3116
+ abortHandler = () => {
3117
+ clearTimeout(t);
3118
+ signal.removeEventListener("abort", abortHandler);
3119
+ reject(new Error("aborted"));
3120
+ };
3121
+ signal.addEventListener("abort", abortHandler);
3122
+ }
3123
+ });
3124
+ }
3125
+
3126
+ // src/client/entitlements.ts
3127
+ Client.prototype.getBalance = async function(signal) {
3128
+ const resp = await this.doJSON(
3129
+ "GET",
3130
+ "/entitlements/balance",
3131
+ null,
3132
+ signal
3133
+ );
3134
+ return resp.data;
3135
+ };
3136
+ Client.prototype.getBalanceDetail = async function(signal) {
3137
+ const resp = await this.doJSON(
3138
+ "GET",
3139
+ "/entitlements/balance-detail",
3140
+ null,
3141
+ signal
3142
+ );
3143
+ return resp.data;
3144
+ };
3145
+ Client.prototype.listEntitlements = async function(status, signal) {
3146
+ let path = "/entitlements";
3147
+ if (status !== "") {
3148
+ path += `?status=${encodeURIComponent(status)}`;
3149
+ }
3150
+ const resp = await this.doJSON("GET", path, null, signal);
3151
+ return resp.data;
3152
+ };
3153
+ Client.prototype.listConsumeRecords = async function(page, pageSize, signal) {
3154
+ const path = `/entitlements/consume-records?page=${page}&pageSize=${pageSize}`;
3155
+ const resp = await this.doJSON("GET", path, null, signal);
3156
+ return resp.data;
3157
+ };
3158
+ Client.prototype.claimMonthlyFree = async function(signal) {
3159
+ const resp = await this.doJSON(
3160
+ "POST",
3161
+ "/entitlements/claim-monthly",
3162
+ null,
3163
+ signal
3164
+ );
3165
+ return resp.data;
3166
+ };
3167
+ Client.prototype.getByModel = async function(modelID, signal) {
3168
+ if (modelID === "") throw new Error("modelID required");
3169
+ const path = `/entitlements/by-model?modelId=${encodeURIComponent(modelID)}`;
3170
+ const resp = await this.doJSON("GET", path, null, signal);
3171
+ return resp.data;
3172
+ };
3173
+ Client.prototype.listBuckets = async function(signal) {
3174
+ const resp = await this.doJSON(
3175
+ "GET",
3176
+ "/entitlements/buckets",
3177
+ null,
3178
+ signal
3179
+ );
3180
+ return resp.data;
3181
+ };
3182
+ Client.prototype.listCoefficients = async function(signal) {
3183
+ if (this.coefCacheData && Date.now() - this.coefCacheTimeMs < coefCacheTTLMs) {
3184
+ return [...this.coefCacheData];
3185
+ }
3186
+ const resp = await this.doJSON(
3187
+ "GET",
3188
+ "/entitlements/coefficients",
3189
+ null,
3190
+ signal
3191
+ );
3192
+ this.coefCacheData = [...resp.data];
3193
+ this.coefCacheTimeMs = Date.now();
3194
+ return resp.data;
3195
+ };
3196
+ Client.prototype.invalidateCoefficientCache = function() {
3197
+ this.coefCacheData = null;
3198
+ this.coefCacheTimeMs = 0;
3199
+ };
3200
+
3201
+ // src/client/packages.ts
3202
+ init_types();
3203
+ Client.prototype.listTokenPackages = async function(signal) {
3204
+ const raw = await this.doJSON("GET", "/token-packages", null, signal);
3205
+ if (raw.data && typeof raw.data === "object" && "list" in raw.data) {
3206
+ const page = raw.data;
3207
+ if (Array.isArray(page.list)) return page.list;
3208
+ }
3209
+ if (Array.isArray(raw.data)) return raw.data;
3210
+ throw new Error("decode token packages: unexpected shape");
3211
+ };
3212
+ Client.prototype.getTokenPackageDetail = async function(packageID, signal) {
3213
+ const resp = await this.doJSON(
3214
+ "GET",
3215
+ `/token-packages/${encodeURIComponent(packageID)}`,
3216
+ null,
3217
+ signal
3218
+ );
3219
+ return resp.data;
3220
+ };
3221
+ Client.prototype.buyTokenPackage = async function(packageID, payload, signal) {
3222
+ const body = payload ?? null;
3223
+ const resp = await this.doJSON(
3224
+ "POST",
3225
+ `/token-packages/${encodeURIComponent(packageID)}/buy`,
3226
+ body,
3227
+ signal
3228
+ );
3229
+ return resp.data;
3230
+ };
3231
+ Client.prototype.getOrderStatus = async function(orderID, signal) {
3232
+ const resp = await this.doJSON(
3233
+ "GET",
3234
+ `/token-packages/orders/${encodeURIComponent(orderID)}/status`,
3235
+ null,
3236
+ signal
3237
+ );
3238
+ return resp.data;
3239
+ };
3240
+ Client.prototype.listMyOrders = async function(signal) {
3241
+ const raw = await this.doJSON("GET", "/token-packages/my", null, signal);
3242
+ if (raw.data && typeof raw.data === "object" && "list" in raw.data) {
3243
+ const page = raw.data;
3244
+ if (Array.isArray(page.list)) return page.list;
3245
+ }
3246
+ if (Array.isArray(raw.data)) return raw.data;
3247
+ throw new Error("decode orders: unexpected shape");
3248
+ };
3249
+ Client.prototype.waitForPayment = async function(orderID, pollIntervalMs, signal) {
3250
+ if (pollIntervalMs <= 0) pollIntervalMs = 2e3;
3251
+ while (true) {
3252
+ const status = await this.getOrderStatus(orderID, signal);
3253
+ if (isOrderTerminal(status.status)) {
3254
+ if (isOrderSuccess(status.status)) return status;
3255
+ throw new exports.OrderTerminalError(orderID, status.status);
3256
+ }
3257
+ await sleepWithSignal(pollIntervalMs, signal);
3258
+ }
3259
+ };
3260
+ async function sleepWithSignal(ms, signal) {
3261
+ if (ms <= 0) return;
3262
+ if (signal && signal.aborted) throw new Error("aborted");
3263
+ return new Promise((resolve, reject) => {
3264
+ const t = setTimeout(() => {
3265
+ if (signal && abortHandler) signal.removeEventListener("abort", abortHandler);
3266
+ resolve();
3267
+ }, ms);
3268
+ let abortHandler;
3269
+ if (signal) {
3270
+ abortHandler = () => {
3271
+ clearTimeout(t);
3272
+ signal.removeEventListener("abort", abortHandler);
3273
+ reject(new Error("aborted"));
3274
+ };
3275
+ signal.addEventListener("abort", abortHandler);
3276
+ }
3277
+ });
3278
+ }
3279
+
3280
+ // src/client/wallet.ts
3281
+ Client.prototype.getWalletStats = async function(signal) {
3282
+ const resp = await this.doJSON("GET", "/wallet/stats", null, signal);
3283
+ return resp.data;
3284
+ };
3285
+ Client.prototype.getWalletTransactions = async function(signal) {
3286
+ const resp = await this.doJSON(
3287
+ "GET",
3288
+ "/wallet/transactions",
3289
+ null,
3290
+ signal
3291
+ );
3292
+ return resp.data;
3293
+ };
3294
+
3295
+ // src/client/skills.ts
3296
+ init_types();
3297
+ Client.prototype.browseSkillStore = async function(query, signal) {
3298
+ const resp = await this.browseSkills(
3299
+ 1,
3300
+ 50,
3301
+ query.category ?? "",
3302
+ query.keyword ?? "",
3303
+ query.tag ?? "",
3304
+ "",
3305
+ signal
3306
+ );
3307
+ return resp.items;
3308
+ };
3309
+ Client.prototype.browseSkills = async function(page, pageSize, category, keyword, tag, source, signal) {
3310
+ const qv = new URLSearchParams();
3311
+ qv.set("page", String(page));
3312
+ qv.set("pageSize", String(pageSize));
3313
+ if (category) qv.set("category", category);
3314
+ if (keyword) qv.set("keyword", keyword);
3315
+ if (tag) qv.set("tag", tag);
3316
+ if (source) qv.set("source", source);
3317
+ const resp = await this.doPublicJSON(
3318
+ "GET",
3319
+ `/skill-store?${qv.toString()}`,
3320
+ null,
3321
+ signal
3322
+ );
3323
+ return resp.data;
3324
+ };
3325
+ Client.prototype.browseSkillsList = async function(page, pageSize, category, keyword, tag, source, signal) {
3326
+ const qv = new URLSearchParams();
3327
+ qv.set("page", String(page));
3328
+ qv.set("pageSize", String(pageSize));
3329
+ qv.set("fields", "minimal");
3330
+ if (category) qv.set("category", category);
3331
+ if (keyword) qv.set("keyword", keyword);
3332
+ if (tag) qv.set("tag", tag);
3333
+ if (source) qv.set("source", source);
3334
+ const resp = await this.doPublicJSON(
3335
+ "GET",
3336
+ `/skill-store?${qv.toString()}`,
3337
+ null,
3338
+ signal
3339
+ );
3340
+ return resp.data;
3341
+ };
3342
+ Client.prototype.getSkillDetail = async function(skillID, signal) {
3343
+ const resp = await this.doPublicJSON(
3344
+ "GET",
3345
+ `/skill-store/${encodeURIComponent(skillID)}`,
3346
+ null,
3347
+ signal
3348
+ );
3349
+ return resp.data;
3350
+ };
3351
+ Client.prototype.resolveSkill = async function(key, signal) {
3352
+ const resp = await this.doPublicJSON(
3353
+ "GET",
3354
+ `/skill-store/resolve/${encodeURIComponent(key)}`,
3355
+ null,
3356
+ signal
3357
+ );
3358
+ return resp.data;
3359
+ };
3360
+ Client.prototype.installSkill = async function(skillID, signal) {
3361
+ const resp = await this.doJSON(
3362
+ "POST",
3363
+ `/skill-store/${encodeURIComponent(skillID)}/install`,
3364
+ null,
3365
+ signal
3366
+ );
3367
+ return resp.data;
3368
+ };
3369
+ Client.prototype.downloadSkill = async function(skillID, signal) {
3370
+ const ctl = new AbortController();
3371
+ const timer = setTimeout(() => ctl.abort(), 5 * 60 * 1e3);
3372
+ let parentHandler;
3373
+ if (signal) {
3374
+ if (signal.aborted) ctl.abort();
3375
+ else {
3376
+ parentHandler = () => ctl.abort();
3377
+ signal.addEventListener("abort", parentHandler);
3378
+ }
3379
+ }
3380
+ try {
3381
+ const url = this.apiURL(`/skill-store/${encodeURIComponent(skillID)}/download`);
3382
+ const headers = {};
3383
+ let token = "";
3384
+ try {
3385
+ token = await this.ensureToken(ctl.signal);
3386
+ } catch {
3387
+ }
3388
+ if (token) headers["Authorization"] = `Bearer ${token}`;
3389
+ let resp;
3390
+ try {
3391
+ resp = await this.fetchImpl(url, { method: "GET", headers, signal: ctl.signal });
3392
+ } catch (e) {
3393
+ throw classifyTransport(`GET /skill-store/${skillID}/download`, url, e);
3394
+ }
3395
+ if (resp.status === 429) {
3396
+ const bodyText = await readLimitedText(resp.body, maxErrorBodySize);
3397
+ throw new exports.RateLimitError("\u533F\u540D\u4E0B\u8F7D\u5DF2\u8FBE\u9650\u5236", resp.headers.get("Retry-After") ?? "", bodyText);
3398
+ }
3399
+ if (!resp.ok) {
3400
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
3401
+ throw new Error(
3402
+ `download skill: ${parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers).message}`
3403
+ );
3404
+ }
3405
+ const data = await readLimited(resp.body, maxDownloadSize + 1);
3406
+ if (data.byteLength > maxDownloadSize) {
3407
+ throw new Error(`download skill: response exceeds ${maxDownloadSize >> 20}MB limit`);
3408
+ }
3409
+ let filename = "skill.zip";
3410
+ const cd = resp.headers.get("Content-Disposition");
3411
+ if (cd) {
3412
+ const idx = cd.indexOf("filename");
3413
+ if (idx !== -1) {
3414
+ const parts = cd.slice(idx).split("=", 2);
3415
+ if (parts.length === 2) {
3416
+ filename = parts[1].trim().replace(/^["' ]+|["' ]+$/g, "");
3417
+ }
3418
+ }
3419
+ }
3420
+ return { data, filename };
3421
+ } finally {
3422
+ clearTimeout(timer);
3423
+ if (parentHandler && signal) signal.removeEventListener("abort", parentHandler);
3424
+ }
3425
+ };
3426
+ Client.prototype.uploadSkill = async function(zipData, scope, intent, signal) {
3427
+ return uploadSkillInternal(this, zipData, scope, intent, false, signal);
3428
+ };
3429
+ async function uploadSkillInternal(c, zipData, scope, intent, retried, signal) {
3430
+ const ctl = new AbortController();
3431
+ const timer = setTimeout(() => ctl.abort(), 5 * 60 * 1e3);
3432
+ let parentHandler;
3433
+ if (signal) {
3434
+ if (signal.aborted) ctl.abort();
3435
+ else {
3436
+ parentHandler = () => ctl.abort();
3437
+ signal.addEventListener("abort", parentHandler);
3438
+ }
3439
+ }
3440
+ try {
3441
+ const token = await c.ensureToken(ctl.signal);
3442
+ const form = new FormData();
3443
+ form.append("scope", scope);
3444
+ form.append("intent", intent);
3445
+ const blob = new Blob([zipData], { type: "application/zip" });
3446
+ form.append("file", blob, "skill.zip");
3447
+ const url = c.apiURL("/skill-store/upload");
3448
+ let resp;
3449
+ try {
3450
+ resp = await c.fetchImpl(url, {
3451
+ method: "POST",
3452
+ headers: { Authorization: `Bearer ${token}` },
3453
+ body: form,
3454
+ signal: ctl.signal
3455
+ });
3456
+ } catch (e) {
3457
+ throw classifyTransport("POST /skill-store/upload", url, e);
3458
+ }
3459
+ if (resp.status === 401 && !retried) {
3460
+ try {
3461
+ await resp.body?.cancel();
3462
+ } catch {
3463
+ }
3464
+ try {
3465
+ await c.forceRefresh(ctl.signal);
3466
+ } catch (refreshErr) {
3467
+ throw new Error(
3468
+ `upload: unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
3469
+ );
3470
+ }
3471
+ return uploadSkillInternal(c, zipData, scope, intent, true, signal);
3472
+ }
3473
+ if (resp.status < 200 || resp.status >= 300) {
3474
+ const bodyBytes = await readLimited(resp.body, maxErrorBodySize);
3475
+ throw new Error(
3476
+ `upload: ${parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers).message}`
3477
+ );
3478
+ }
3479
+ const text = await resp.text();
3480
+ const result = JSON.parse(text);
3481
+ return result.data.skill;
3482
+ } finally {
3483
+ clearTimeout(timer);
3484
+ if (parentHandler && signal) signal.removeEventListener("abort", parentHandler);
3485
+ }
3486
+ }
3487
+ Client.prototype.getSkillSummary = async function(signal) {
3488
+ const resp = await this.doJSON("GET", "/skills/summary", null, signal);
3489
+ return resp.data;
3490
+ };
3491
+ Client.prototype.certifySkill = async function(skillID, signal) {
3492
+ await this.doJSON(
3493
+ "POST",
3494
+ `/skill-store/${encodeURIComponent(skillID)}/certify`,
3495
+ null,
3496
+ signal
3497
+ );
3498
+ };
3499
+ Client.prototype.getCertificationStatus = async function(skillID, signal) {
3500
+ const resp = await this.doJSON(
3501
+ "GET",
3502
+ `/skill-store/${encodeURIComponent(skillID)}/certification`,
3503
+ null,
3504
+ signal
3505
+ );
3506
+ return resp.data;
3507
+ };
3508
+ Client.prototype.generateSkill = async function(req, signal) {
3509
+ const resp = await this.doJSON(
3510
+ "POST",
3511
+ "/skill-generator/generate",
3512
+ req,
3513
+ signal
3514
+ );
3515
+ return resp.data;
3516
+ };
3517
+ Client.prototype.optimizeSkill = async function(req, signal) {
3518
+ const resp = await this.doJSON(
3519
+ "POST",
3520
+ "/skill-generator/optimize",
3521
+ req,
3522
+ signal
3523
+ );
3524
+ return resp.data;
3525
+ };
3526
+ Client.prototype.validateSkill = async function(skillName, signal) {
3527
+ await this.doJSON(
3528
+ "POST",
3529
+ "/skill-generator/validate",
3530
+ { skillName },
3531
+ signal
3532
+ );
3533
+ };
3534
+
3535
+ // src/client/tools.ts
3536
+ Client.prototype.listTools = async function(signal) {
3537
+ const resp = await this.doJSON("GET", "/tools", null, signal);
3538
+ return resp.data.skills;
3539
+ };
3540
+ Client.prototype.getTool = async function(toolID, signal) {
3541
+ const resp = await this.doJSON(
3542
+ "GET",
3543
+ `/tools/${encodeURIComponent(toolID)}`,
3544
+ null,
3545
+ signal
3546
+ );
3547
+ return resp.data;
3548
+ };
3549
+
3550
+ // src/client/notifications.ts
3551
+ Client.prototype.listNotifications = async function(page, pageSize, typeFilter, signal) {
3552
+ let path = `/notifications?page=${page}&pageSize=${pageSize}`;
3553
+ if (typeFilter) path += `&type=${encodeURIComponent(typeFilter)}`;
3554
+ const resp = await this.doJSON("GET", path, null, signal);
3555
+ return resp.data;
3556
+ };
3557
+ Client.prototype.getUnreadCount = async function(signal) {
3558
+ const resp = await this.doJSON(
3559
+ "GET",
3560
+ "/notifications/unread-count",
3561
+ null,
3562
+ signal
3563
+ );
3564
+ return resp.data.unreadCount;
3565
+ };
3566
+ Client.prototype.markNotificationRead = async function(id, signal) {
3567
+ await this.doJSON(
3568
+ "PUT",
3569
+ `/notifications/${encodeURIComponent(id)}/read`,
3570
+ null,
3571
+ signal
3572
+ );
3573
+ };
3574
+ Client.prototype.markAllNotificationsRead = async function(signal) {
3575
+ await this.doJSON("PUT", "/notifications/read-all", null, signal);
3576
+ };
3577
+ Client.prototype.deleteNotification = async function(id, signal) {
3578
+ await this.doJSON(
3579
+ "DELETE",
3580
+ `/notifications/${encodeURIComponent(id)}`,
3581
+ null,
3582
+ signal
3583
+ );
3584
+ };
3585
+ Client.prototype.registerDevice = async function(reg, signal) {
3586
+ await this.doJSON("POST", "/devices/register", reg, signal);
3587
+ };
3588
+ Client.prototype.unregisterDevice = async function(token, signal) {
3589
+ await this.doJSON(
3590
+ "DELETE",
3591
+ `/devices/${encodeURIComponent(token)}`,
3592
+ null,
3593
+ signal
3594
+ );
3595
+ };
3596
+ Client.prototype.listNotificationPreferences = async function(signal) {
3597
+ const resp = await this.doJSON(
3598
+ "GET",
3599
+ "/notification-preferences",
3600
+ null,
3601
+ signal
3602
+ );
3603
+ return resp.data;
3604
+ };
3605
+ Client.prototype.updateNotificationPreference = async function(typeCode, pref, signal) {
3606
+ await this.doJSON(
3607
+ "PUT",
3608
+ `/notification-preferences/${encodeURIComponent(typeCode)}`,
3609
+ pref,
3610
+ signal
3611
+ );
3612
+ };
3613
+
3614
+ // src/sanitize-bridge.ts
3615
+ Client.prototype.setDefensiveSanitize = function(cfg) {
3616
+ this.defensiveCfg = cfg;
3617
+ };
3618
+ Client.prototype.setAutoStripEphemeralHistory = function(on) {
3619
+ this.autoStripEphemeral = on;
3620
+ };
3621
+ Client.prototype.applyRequestSanitizers = function(req) {
3622
+ const cfg = this.defensiveCfg;
3623
+ const strip = this.autoStripEphemeral;
3624
+ if (cfg == null && !strip) return;
3625
+ if (req.rawMessages != null) {
3626
+ let msgs;
3627
+ try {
3628
+ msgs = normalizeRawMessages(req.rawMessages);
3629
+ } catch (e) {
3630
+ throw new Error(
3631
+ `sanitize: normalize raw messages: ${e instanceof Error ? e.message : String(e)}`
3632
+ );
3633
+ }
3634
+ if (cfg) {
3635
+ msgs = sanitize(msgs, cfg);
3636
+ }
3637
+ if (strip) {
3638
+ msgs = stripEphemeral(msgs);
3639
+ }
3640
+ req.rawMessages = msgs;
3641
+ return;
3642
+ }
3643
+ if (cfg && (cfg.maxMessagesTurns ?? 0) > 0 && (req.messages?.length ?? 0) > cfg.maxMessagesTurns) {
3644
+ throw ErrHistoryTooDeep;
3645
+ }
3646
+ };
3647
+ function normalizeRawMessages(rm) {
3648
+ if (Array.isArray(rm)) return rm;
3649
+ let s;
3650
+ try {
3651
+ s = JSON.parse(JSON.stringify(rm));
3652
+ } catch (e) {
3653
+ throw new Error(`raw messages: ${e instanceof Error ? e.message : String(e)}`);
3654
+ }
3655
+ if (!Array.isArray(s)) {
3656
+ throw new Error("raw messages must be a JSON array");
3657
+ }
3658
+ return s;
3659
+ }
3660
+
3661
+ // src/ws.ts
3662
+ Client.prototype.connect = async function(cfg, signal) {
3663
+ const noop = () => {
3664
+ };
3665
+ const filledCfg = {
3666
+ onEvent: cfg.onEvent ?? noop,
3667
+ onConnect: cfg.onConnect ?? noop,
3668
+ onDisconnect: cfg.onDisconnect ?? noop,
3669
+ topics: cfg.topics ?? [],
3670
+ reconnectMinMs: cfg.reconnectMinMs ?? 2e3,
3671
+ reconnectMaxMs: cfg.reconnectMaxMs ?? 6e4,
3672
+ autoReconnect: cfg.autoReconnect ?? true
3673
+ };
3674
+ let resolveDone;
3675
+ const done = new Promise((r) => {
3676
+ resolveDone = r;
3677
+ });
3678
+ const abort = new AbortController();
3679
+ if (signal) {
3680
+ if (signal.aborted) abort.abort();
3681
+ else signal.addEventListener("abort", () => abort.abort());
3682
+ }
3683
+ const ws = {
3684
+ conn: null,
3685
+ cfg: filledCfg,
3686
+ abort,
3687
+ done,
3688
+ doneResolve: resolveDone,
3689
+ connected: false
3690
+ };
3691
+ await wsConnectOnce(this, ws);
3692
+ this.ws = ws;
3693
+ void wsLoop(this, ws);
3694
+ };
3695
+ Client.prototype.disconnect = async function() {
3696
+ const ws = this.ws;
3697
+ this.ws = null;
3698
+ if (!ws) return;
3699
+ ws.abort.abort();
3700
+ ws.connected = false;
3701
+ if (ws.conn) {
3702
+ try {
3703
+ ws.conn.close(1e3, "");
3704
+ } catch {
3705
+ }
3706
+ }
3707
+ await Promise.race([
3708
+ ws.done,
3709
+ new Promise((resolve) => setTimeout(resolve, 5e3))
3710
+ ]);
3711
+ };
3712
+ Client.prototype.isConnected = function() {
3713
+ const ws = this.ws;
3714
+ if (!ws) return false;
3715
+ return ws.connected;
3716
+ };
3717
+ function wsURL(c) {
3718
+ const base = c.apiURL("/ws");
3719
+ return base.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://");
3720
+ }
3721
+ function getWebSocketCtor() {
3722
+ const WSCtor = globalThis.WebSocket;
3723
+ if (!WSCtor) {
3724
+ throw new Error(
3725
+ 'WebSocket not available \u2014 on Node \u226421 set globalThis.WebSocket = require("ws") before connect'
3726
+ );
3727
+ }
3728
+ return WSCtor;
3729
+ }
3730
+ async function wsConnectOnce(c, ws) {
3731
+ const token = await c.ensureToken(ws.abort.signal);
3732
+ const url = wsURL(c);
3733
+ const WSCtor = getWebSocketCtor();
3734
+ const u = new URL(url);
3735
+ u.searchParams.set("token", token);
3736
+ let conn;
3737
+ try {
3738
+ conn = new WSCtor(u.toString());
3739
+ } catch (e) {
3740
+ throw new Error(`dial: ${e instanceof Error ? e.message : String(e)}`);
3741
+ }
3742
+ await new Promise((resolve, reject) => {
3743
+ let opened = false;
3744
+ const handshakeTimer = setTimeout(() => {
3745
+ if (!opened) {
3746
+ try {
3747
+ conn.close();
3748
+ } catch {
3749
+ }
3750
+ reject(new Error("dial: handshake timeout"));
3751
+ }
3752
+ }, 3e4);
3753
+ conn.addEventListener("open", () => {
3754
+ opened = true;
3755
+ });
3756
+ conn.addEventListener("error", (e) => {
3757
+ clearTimeout(handshakeTimer);
3758
+ reject(new Error(`dial: ${e.message ?? "connection error"}`));
3759
+ });
3760
+ conn.addEventListener("message", (e) => {
3761
+ try {
3762
+ const msg = e.data;
3763
+ const welcome = JSON.parse(msg);
3764
+ if (welcome.type !== "welcome") {
3765
+ clearTimeout(handshakeTimer);
3766
+ try {
3767
+ conn.close();
3768
+ } catch {
3769
+ }
3770
+ reject(new Error(`unexpected first message: ${welcome.type}`));
3771
+ return;
3772
+ }
3773
+ clearTimeout(handshakeTimer);
3774
+ ws.conn = conn;
3775
+ ws.connected = true;
3776
+ if (ws.cfg.topics.length > 0) {
3777
+ try {
3778
+ conn.send(
3779
+ JSON.stringify({
3780
+ type: "subscribe",
3781
+ topics: ws.cfg.topics
3782
+ })
3783
+ );
3784
+ } catch (sendErr) {
3785
+ ws.conn = null;
3786
+ ws.connected = false;
3787
+ try {
3788
+ conn.close();
3789
+ } catch {
3790
+ }
3791
+ reject(new Error(`send subscribe: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}`));
3792
+ return;
3793
+ }
3794
+ }
3795
+ ws.cfg.onConnect();
3796
+ console.log(`[acosmi-sdk] websocket connected, connId=${welcome.connId ?? ""}`);
3797
+ resolve();
3798
+ } catch (parseErr) {
3799
+ clearTimeout(handshakeTimer);
3800
+ try {
3801
+ conn.close();
3802
+ } catch {
3803
+ }
3804
+ reject(new Error(`parse welcome: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`));
3805
+ }
3806
+ }, { once: true });
3807
+ });
3808
+ }
3809
+ async function wsLoop(c, ws) {
3810
+ try {
3811
+ while (true) {
3812
+ await wsReadLoop(ws);
3813
+ if (ws.abort.signal.aborted) return;
3814
+ if (ws.conn) {
3815
+ try {
3816
+ ws.conn.close();
3817
+ } catch {
3818
+ }
3819
+ ws.conn = null;
3820
+ }
3821
+ ws.connected = false;
3822
+ if (!ws.cfg.autoReconnect) return;
3823
+ let delay = ws.cfg.reconnectMinMs;
3824
+ while (true) {
3825
+ if (ws.abort.signal.aborted) return;
3826
+ await sleepWithSignal2(delay, ws.abort.signal).catch(() => {
3827
+ });
3828
+ if (ws.abort.signal.aborted) return;
3829
+ console.log(`[acosmi-sdk] websocket reconnecting (delay=${delay}ms)...`);
3830
+ try {
3831
+ await wsConnectOnce(c, ws);
3832
+ break;
3833
+ } catch (err) {
3834
+ console.log(`[acosmi-sdk] websocket reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
3835
+ delay = Math.min(delay * 2, ws.cfg.reconnectMaxMs);
3836
+ }
3837
+ }
3838
+ }
3839
+ } finally {
3840
+ ws.doneResolve();
3841
+ }
3842
+ }
3843
+ async function wsReadLoop(ws) {
3844
+ const conn = ws.conn;
3845
+ if (!conn) return;
3846
+ return new Promise((resolve) => {
3847
+ const handleMessage = (e) => {
3848
+ try {
3849
+ const data = e.data;
3850
+ const event = JSON.parse(data);
3851
+ try {
3852
+ ws.cfg.onEvent(event);
3853
+ } catch {
3854
+ }
3855
+ } catch {
3856
+ }
3857
+ };
3858
+ const handleClose = (e) => {
3859
+ conn.removeEventListener("message", handleMessage);
3860
+ conn.removeEventListener("close", handleClose);
3861
+ conn.removeEventListener("error", handleError);
3862
+ try {
3863
+ ws.cfg.onDisconnect(new Error(`closed: code=${e.code} reason=${e.reason}`));
3864
+ } catch {
3865
+ }
3866
+ resolve();
3867
+ };
3868
+ const handleError = (e) => {
3869
+ conn.removeEventListener("message", handleMessage);
3870
+ conn.removeEventListener("close", handleClose);
3871
+ conn.removeEventListener("error", handleError);
3872
+ try {
3873
+ ws.cfg.onDisconnect(e);
3874
+ } catch {
3875
+ }
3876
+ resolve();
3877
+ };
3878
+ conn.addEventListener("message", handleMessage);
3879
+ conn.addEventListener("close", handleClose);
3880
+ conn.addEventListener("error", handleError);
3881
+ if (ws.abort.signal.aborted) {
3882
+ try {
3883
+ conn.close();
3884
+ } catch {
3885
+ }
3886
+ } else {
3887
+ ws.abort.signal.addEventListener(
3888
+ "abort",
3889
+ () => {
3890
+ try {
3891
+ conn.close();
3892
+ } catch {
3893
+ }
3894
+ },
3895
+ { once: true }
3896
+ );
3897
+ }
3898
+ });
3899
+ }
3900
+ async function sleepWithSignal2(ms, signal) {
3901
+ if (ms <= 0) return;
3902
+ if (signal.aborted) throw new Error("aborted");
3903
+ return new Promise((resolve, reject) => {
3904
+ const t = setTimeout(() => {
3905
+ signal.removeEventListener("abort", abortHandler);
3906
+ resolve();
3907
+ }, ms);
3908
+ const abortHandler = () => {
3909
+ clearTimeout(t);
3910
+ signal.removeEventListener("abort", abortHandler);
3911
+ reject(new Error("aborted"));
3912
+ };
3913
+ signal.addEventListener("abort", abortHandler);
3914
+ });
3915
+ }
3916
+
3917
+ // src/bug-report.ts
3918
+ Client.prototype.submitBugReport = async function(reportData, signal) {
3919
+ if (reportData == null) {
3920
+ throw new Error("acosmi: reportData required");
3921
+ }
3922
+ let contentStr;
3923
+ try {
3924
+ contentStr = JSON.stringify(reportData);
3925
+ } catch (e) {
3926
+ throw new Error(`acosmi: marshal reportData: ${e instanceof Error ? e.message : String(e)}`);
3927
+ }
3928
+ const result = await this.doJSON(
3929
+ "POST",
3930
+ "/crabcode_cli_feedback",
3931
+ { content: contentStr },
3932
+ signal
3933
+ );
3934
+ return result.data;
3935
+ };
3936
+ Client.prototype.getBugReport = async function(bugID, signal) {
3937
+ const trimmed = bugID.trim();
3938
+ if (trimmed === "") {
3939
+ throw new Error("acosmi: bugID required");
3940
+ }
3941
+ const resp = await this.doPublicJSON(
3942
+ "GET",
3943
+ `/crabcode/bug/${trimmed}`,
3944
+ null,
3945
+ signal
3946
+ );
3947
+ return resp.data;
3948
+ };
3949
+
3950
+ exports.Client = Client;
3951
+ exports.DefaultRetryPolicy = DefaultRetryPolicy;
3952
+ exports.ErrAuthDenied = ErrAuthDenied;
3953
+ exports.ErrBrowserOpen = ErrBrowserOpen;
3954
+ exports.ErrDiscovery = ErrDiscovery;
3955
+ exports.ErrRegistration = ErrRegistration;
3956
+ exports.ErrSSLProxy = ErrSSLProxy;
3957
+ exports.ErrTimeout = ErrTimeout;
3958
+ exports.ErrTokenExchange = ErrTokenExchange;
3959
+ exports.EventAuthURL = EventAuthURL;
3960
+ exports.EventComplete = EventComplete;
3961
+ exports.EventError = EventError;
3962
+ exports.FileTokenStore = FileTokenStore;
3963
+ exports.FilterStatusAdminBypass = FilterStatusAdminBypass;
3964
+ exports.FilterStatusDisabledByFlag = FilterStatusDisabledByFlag;
3965
+ exports.FilterStatusFallbackMissingUser = FilterStatusFallbackMissingUser;
3966
+ exports.FilterStatusFallbackNoBuckets = FilterStatusFallbackNoBuckets;
3967
+ exports.FilterStatusFallbackTkdistError = FilterStatusFallbackTkdistError;
3968
+ exports.FilterStatusFallbackTkdistSkew = FilterStatusFallbackTkdistSkew;
3969
+ exports.FilterStatusInternalBypass = FilterStatusInternalBypass;
3970
+ exports.FilterStatusOK = FilterStatusOK;
3971
+ exports.FilterStatusUnknown = FilterStatusUnknown;
3972
+ exports.InMemoryTokenStore = InMemoryTokenStore;
3973
+ exports.LocalStorageTokenStore = LocalStorageTokenStore;
3974
+ exports.ScopeAI = ScopeAI;
3975
+ exports.ScopeAccount = ScopeAccount;
3976
+ exports.ScopeEntitlements = ScopeEntitlements;
3977
+ exports.ScopeModels = ScopeModels;
3978
+ exports.ScopeModelsChat = ScopeModelsChat;
3979
+ exports.ScopeProfile = ScopeProfile;
3980
+ exports.ScopeSkillStore = ScopeSkillStore;
3981
+ exports.ScopeSkills = ScopeSkills;
3982
+ exports.ScopeTokenPackages = ScopeTokenPackages;
3983
+ exports.ScopeTools = ScopeTools;
3984
+ exports.ScopeToolsExecute = ScopeToolsExecute;
3985
+ exports.ScopeWallet = ScopeWallet;
3986
+ exports.ScopeWalletReadonly = ScopeWalletReadonly;
3987
+ exports.allScopes = allScopes;
3988
+ exports.anthropicResponseTextContent = anthropicResponseTextContent;
3989
+ exports.anthropicResponseThinkingContent = anthropicResponseThinkingContent;
3990
+ exports.anthropicResponseToolUseBlocks = anthropicResponseToolUseBlocks;
3991
+ exports.apiResponseBusinessError = apiResponseBusinessError;
3992
+ exports.apiResponseGetMessage = apiResponseGetMessage;
3993
+ exports.authorize = authorize;
3994
+ exports.bucketInfoIsCommercial = bucketInfoIsCommercial;
3995
+ exports.bucketRowIsCommercial = bucketRowIsCommercial;
3996
+ exports.buildBetas = buildBetas;
3997
+ exports.commerceScopes = commerceScopes;
3998
+ exports.computeBackoff = computeBackoff;
3999
+ exports.defaultRetryable = defaultRetryable;
4000
+ exports.defaultSafeToRetry = defaultSafeToRetry;
4001
+ exports.discover = discover;
4002
+ exports.effectivePolicy = effectivePolicy;
4003
+ exports.exchangeCode = exchangeCode;
4004
+ exports.extractAnthropicBlockMeta = extractAnthropicBlockMeta;
4005
+ exports.getAdapter = getAdapter;
4006
+ exports.getAdapterForModel = getAdapterForModel;
4007
+ exports.isSSLError = isSSLError;
4008
+ exports.modelScopes = modelScopes;
4009
+ exports.newFileTokenStore = newFileTokenStore;
4010
+ exports.newThinkingConfig = newThinkingConfig;
4011
+ exports.newTokenSet = newTokenSet;
4012
+ exports.newWebSearchTool = newWebSearchTool;
4013
+ exports.parseNotificationEvent = parseNotificationEvent;
4014
+ exports.parseSettlement = parseSettlement;
4015
+ exports.parseSourcesEvent = parseSourcesEvent;
4016
+ exports.refreshToken = refreshToken;
4017
+ exports.register = register;
4018
+ exports.revokeToken = revokeToken;
4019
+ exports.sanitize = sanitize_exports;
4020
+ exports.skillScopes = skillScopes;
4021
+ exports.tokenSetIsExpired = tokenSetIsExpired;
4022
+ exports.uniqueMerge = uniqueMerge;
4023
+ //# sourceMappingURL=index.cjs.map
4024
+ //# sourceMappingURL=index.cjs.map