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