@seandong/seno 0.1.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 (104) hide show
  1. package/README.md +70 -0
  2. package/dist/agent/conversation.d.ts +39 -0
  3. package/dist/agent/conversation.js +60 -0
  4. package/dist/agent/conversation.js.map +1 -0
  5. package/dist/agent/loop.d.ts +41 -0
  6. package/dist/agent/loop.js +203 -0
  7. package/dist/agent/loop.js.map +1 -0
  8. package/dist/agent/session.d.ts +63 -0
  9. package/dist/agent/session.js +135 -0
  10. package/dist/agent/session.js.map +1 -0
  11. package/dist/cli/commands.d.ts +52 -0
  12. package/dist/cli/commands.js +667 -0
  13. package/dist/cli/commands.js.map +1 -0
  14. package/dist/cli/logger.d.ts +38 -0
  15. package/dist/cli/logger.js +79 -0
  16. package/dist/cli/logger.js.map +1 -0
  17. package/dist/cli/output.d.ts +75 -0
  18. package/dist/cli/output.js +305 -0
  19. package/dist/cli/output.js.map +1 -0
  20. package/dist/cli/prompt.d.ts +30 -0
  21. package/dist/cli/prompt.js +196 -0
  22. package/dist/cli/prompt.js.map +1 -0
  23. package/dist/cli/repl.d.ts +27 -0
  24. package/dist/cli/repl.js +485 -0
  25. package/dist/cli/repl.js.map +1 -0
  26. package/dist/commands/init.d.ts +4 -0
  27. package/dist/commands/init.js +170 -0
  28. package/dist/commands/init.js.map +1 -0
  29. package/dist/commands/model.d.ts +10 -0
  30. package/dist/commands/model.js +270 -0
  31. package/dist/commands/model.js.map +1 -0
  32. package/dist/config/manager.d.ts +67 -0
  33. package/dist/config/manager.js +194 -0
  34. package/dist/config/manager.js.map +1 -0
  35. package/dist/config/types.d.ts +98 -0
  36. package/dist/config/types.js +2 -0
  37. package/dist/config/types.js.map +1 -0
  38. package/dist/errors.d.ts +37 -0
  39. package/dist/errors.js +54 -0
  40. package/dist/errors.js.map +1 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +185 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/llm/anthropic.d.ts +27 -0
  45. package/dist/llm/anthropic.js +189 -0
  46. package/dist/llm/anthropic.js.map +1 -0
  47. package/dist/llm/factory.d.ts +47 -0
  48. package/dist/llm/factory.js +163 -0
  49. package/dist/llm/factory.js.map +1 -0
  50. package/dist/llm/openai-codex.d.ts +45 -0
  51. package/dist/llm/openai-codex.js +398 -0
  52. package/dist/llm/openai-codex.js.map +1 -0
  53. package/dist/llm/openai.d.ts +16 -0
  54. package/dist/llm/openai.js +288 -0
  55. package/dist/llm/openai.js.map +1 -0
  56. package/dist/llm/provider.d.ts +19 -0
  57. package/dist/llm/provider.js +2 -0
  58. package/dist/llm/provider.js.map +1 -0
  59. package/dist/llm/types.d.ts +102 -0
  60. package/dist/llm/types.js +2 -0
  61. package/dist/llm/types.js.map +1 -0
  62. package/dist/mcp/bridge.d.ts +30 -0
  63. package/dist/mcp/bridge.js +73 -0
  64. package/dist/mcp/bridge.js.map +1 -0
  65. package/dist/mcp/config.d.ts +6 -0
  66. package/dist/mcp/config.js +26 -0
  67. package/dist/mcp/config.js.map +1 -0
  68. package/dist/mcp/manager.d.ts +54 -0
  69. package/dist/mcp/manager.js +171 -0
  70. package/dist/mcp/manager.js.map +1 -0
  71. package/dist/prompts/system.d.ts +14 -0
  72. package/dist/prompts/system.js +194 -0
  73. package/dist/prompts/system.js.map +1 -0
  74. package/dist/skills/loader.d.ts +7 -0
  75. package/dist/skills/loader.js +81 -0
  76. package/dist/skills/loader.js.map +1 -0
  77. package/dist/skills/registry.d.ts +48 -0
  78. package/dist/skills/registry.js +104 -0
  79. package/dist/skills/registry.js.map +1 -0
  80. package/dist/skills/sync.d.ts +34 -0
  81. package/dist/skills/sync.js +179 -0
  82. package/dist/skills/sync.js.map +1 -0
  83. package/dist/skills/types.d.ts +29 -0
  84. package/dist/skills/types.js +2 -0
  85. package/dist/skills/types.js.map +1 -0
  86. package/dist/tools/ask.d.ts +16 -0
  87. package/dist/tools/ask.js +57 -0
  88. package/dist/tools/ask.js.map +1 -0
  89. package/dist/tools/registry.d.ts +54 -0
  90. package/dist/tools/registry.js +114 -0
  91. package/dist/tools/registry.js.map +1 -0
  92. package/dist/tools/shell.d.ts +10 -0
  93. package/dist/tools/shell.js +131 -0
  94. package/dist/tools/shell.js.map +1 -0
  95. package/dist/tools/ssh.d.ts +40 -0
  96. package/dist/tools/ssh.js +302 -0
  97. package/dist/tools/ssh.js.map +1 -0
  98. package/dist/tools/types.d.ts +20 -0
  99. package/dist/tools/types.js +2 -0
  100. package/dist/tools/types.js.map +1 -0
  101. package/dist/utils/retry.d.ts +20 -0
  102. package/dist/utils/retry.js +33 -0
  103. package/dist/utils/retry.js.map +1 -0
  104. package/package.json +51 -0
@@ -0,0 +1,189 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { logger } from '../cli/logger.js';
3
+ import { LLMError } from '../errors.js';
4
+ import { withRetry } from '../utils/retry.js';
5
+ const DEFAULT_MODEL = 'claude-sonnet-4-20250514';
6
+ const DEFAULT_MAX_TOKENS = 8192;
7
+ /**
8
+ * Anthropic Claude LLM Provider
9
+ *
10
+ * 使用 @anthropic-ai/sdk 的流式 API,将原始事件转换为统一的 StreamEvent。
11
+ * 支持 API 错误分类和自动重试(429 Rate Limit、5xx Server Error、网络错误)。
12
+ */
13
+ export class AnthropicProvider {
14
+ name = 'anthropic';
15
+ client;
16
+ constructor(apiKey) {
17
+ this.client = new Anthropic({ apiKey });
18
+ }
19
+ /**
20
+ * 流式对话调用
21
+ * 使用 AsyncGenerator 模式,逐个 yield StreamEvent。
22
+ */
23
+ async *chat(messages, tools, options) {
24
+ const model = options?.model || DEFAULT_MODEL;
25
+ const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS;
26
+ // 构造请求参数
27
+ const params = {
28
+ model,
29
+ max_tokens: maxTokens,
30
+ messages: messages,
31
+ stream: true,
32
+ };
33
+ // 系统提示词
34
+ if (options?.system) {
35
+ params.system = options.system;
36
+ }
37
+ // 温度
38
+ if (options?.temperature !== undefined) {
39
+ params.temperature = options.temperature;
40
+ }
41
+ // 工具定义(为空时不传,避免 API 报错)
42
+ if (tools.length > 0) {
43
+ params.tools = tools;
44
+ }
45
+ logger.debug('llm', `→ Request: model=${model}, tools=${tools.length}, messages=${messages.length}`);
46
+ // 1. 创建流(带重试:429/5xx/网络错误自动重试)
47
+ let stream;
48
+ try {
49
+ stream = await withRetry(() => this.client.messages.create(params), {
50
+ maxRetries: 3,
51
+ baseDelayMs: 1000,
52
+ maxDelayMs: 8000,
53
+ shouldRetry: (e) => this.isRetryableError(e),
54
+ });
55
+ }
56
+ catch (error) {
57
+ yield { type: 'error', error: this.classifyError(error) };
58
+ return;
59
+ }
60
+ // 2. 处理流式事件(流式阶段不重试)
61
+ try {
62
+ // 跟踪当前是否在 tool_use 上下文中
63
+ let inToolUse = false;
64
+ // 累积 token 计数(message_start 提供 input,message_delta 提供 output)
65
+ let inputTokens = 0;
66
+ for await (const event of stream) {
67
+ switch (event.type) {
68
+ case 'message_start': {
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ const usage = event.message?.usage;
71
+ inputTokens = usage?.input_tokens || 0;
72
+ break;
73
+ }
74
+ case 'content_block_start': {
75
+ if (event.content_block.type === 'tool_use') {
76
+ inToolUse = true;
77
+ yield {
78
+ type: 'tool_use_start',
79
+ id: event.content_block.id,
80
+ name: event.content_block.name,
81
+ };
82
+ }
83
+ // text block start 不需要额外事件,等待 delta
84
+ break;
85
+ }
86
+ case 'content_block_delta': {
87
+ if (event.delta.type === 'text_delta') {
88
+ yield {
89
+ type: 'text_delta',
90
+ text: event.delta.text,
91
+ };
92
+ }
93
+ else if (event.delta.type === 'input_json_delta') {
94
+ yield {
95
+ type: 'tool_use_delta',
96
+ input_json: event.delta.partial_json,
97
+ };
98
+ }
99
+ break;
100
+ }
101
+ case 'content_block_stop': {
102
+ if (inToolUse) {
103
+ yield { type: 'tool_use_end' };
104
+ inToolUse = false;
105
+ }
106
+ break;
107
+ }
108
+ case 'message_delta': {
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ const outputTokens = event.usage?.output_tokens || 0;
111
+ logger.debug('llm', `← Response: stop=${event.delta.stop_reason}, in=${inputTokens}, out=${outputTokens}`);
112
+ yield {
113
+ type: 'message_end',
114
+ stop_reason: event.delta.stop_reason || 'end_turn',
115
+ usage: { inputTokens, outputTokens },
116
+ };
117
+ break;
118
+ }
119
+ }
120
+ }
121
+ }
122
+ catch (error) {
123
+ yield {
124
+ type: 'error',
125
+ error: error instanceof Error ? error : new Error(String(error)),
126
+ };
127
+ }
128
+ }
129
+ /**
130
+ * 判断 API 错误是否可重试
131
+ * 429 Rate Limit、5xx Server Error、网络错误可重试
132
+ */
133
+ isRetryableError(error) {
134
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
+ const status = error?.status;
136
+ if (status === 429 ||
137
+ status === 500 ||
138
+ status === 502 ||
139
+ status === 503) {
140
+ return true;
141
+ }
142
+ const message = error instanceof Error ? error.message : String(error);
143
+ if (message.includes('fetch') ||
144
+ message.includes('network') ||
145
+ message.includes('ECONNREFUSED') ||
146
+ message.includes('ETIMEDOUT')) {
147
+ return true;
148
+ }
149
+ return false;
150
+ }
151
+ /**
152
+ * 将原始错误转换为 LLMError,提供用户友好的错误信息
153
+ */
154
+ classifyError(error) {
155
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
+ const status = error?.status;
157
+ const message = error instanceof Error ? error.message : String(error);
158
+ switch (status) {
159
+ case 401:
160
+ return new LLMError('API 认证失败,请检查 API Key 配置 (seno init)', 401, false);
161
+ case 400: {
162
+ // context length exceeded 保留原始消息,由 agent loop 处理
163
+ if (message.includes('context') ||
164
+ message.includes('too long') ||
165
+ message.includes('max tokens') ||
166
+ message.includes('too many')) {
167
+ return new LLMError(message, 400, false);
168
+ }
169
+ return new LLMError(`请求参数错误: ${message}`, 400, false);
170
+ }
171
+ case 429:
172
+ return new LLMError('API 请求频率超限,已重试仍失败', 429, true);
173
+ case 500:
174
+ case 502:
175
+ case 503:
176
+ return new LLMError(`Anthropic API 服务暂时不可用 (${status}),已重试仍失败`, status, true);
177
+ default: {
178
+ if (message.includes('fetch') ||
179
+ message.includes('network') ||
180
+ message.includes('ECONNREFUSED') ||
181
+ message.includes('ETIMEDOUT')) {
182
+ return new LLMError('网络连接失败,请检查网络', undefined, true);
183
+ }
184
+ return new LLMError(message, status);
185
+ }
186
+ }
187
+ }
188
+ }
189
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/llm/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAG1C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,aAAa,GAAG,0BAA0B,CAAC;AACjD,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,OAAO,iBAAiB;IACnB,IAAI,GAAG,WAAW,CAAC;IACpB,MAAM,CAAY;IAE1B,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,CAAC,IAAI,CACT,QAAmB,EACnB,KAAuB,EACvB,OAAqB;QAErB,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,aAAa,CAAC;QAC9C,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,kBAAkB,CAAC;QAE3D,SAAS;QACT,MAAM,MAAM,GAAkC;YAC5C,KAAK;YACL,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,QAAoC;YAC9C,MAAM,EAAE,IAAI;SACb,CAAC;QAEF,QAAQ;QACR,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QACjC,CAAC;QAED,KAAK;QACL,IAAI,OAAO,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;YACvC,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QAC3C,CAAC;QAED,wBAAwB;QACxB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,KAAK,GAAG,KAAyB,CAAC;QAC3C,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,oBAAoB,KAAK,WAAW,KAAK,CAAC,MAAM,cAAc,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAErG,+BAA+B;QAC/B,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,SAAS,CACtB,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EACzC;gBACE,UAAU,EAAE,CAAC;gBACb,WAAW,EAAE,IAAI;gBACjB,UAAU,EAAE,IAAI;gBAChB,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;aAC7C,CACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO;QACT,CAAC;QAED,qBAAqB;QACrB,IAAI,CAAC;YACH,wBAAwB;YACxB,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,8DAA8D;YAC9D,IAAI,WAAW,GAAG,CAAC,CAAC;YAEpB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAwD,EAAE,CAAC;gBACnF,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;oBACnB,KAAK,eAAe,CAAC,CAAC,CAAC;wBACrB,8DAA8D;wBAC9D,MAAM,KAAK,GAAI,KAAa,CAAC,OAAO,EAAE,KAAK,CAAC;wBAC5C,WAAW,GAAG,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC;wBACvC,MAAM;oBACR,CAAC;oBAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;wBAC3B,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;4BAC5C,SAAS,GAAG,IAAI,CAAC;4BACjB,MAAM;gCACJ,IAAI,EAAE,gBAAgB;gCACtB,EAAE,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE;gCAC1B,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,IAAI;6BAC/B,CAAC;wBACJ,CAAC;wBACD,oCAAoC;wBACpC,MAAM;oBACR,CAAC;oBAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;wBAC3B,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;4BACtC,MAAM;gCACJ,IAAI,EAAE,YAAY;gCAClB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;6BACvB,CAAC;wBACJ,CAAC;6BAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;4BACnD,MAAM;gCACJ,IAAI,EAAE,gBAAgB;gCACtB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,YAAY;6BACrC,CAAC;wBACJ,CAAC;wBACD,MAAM;oBACR,CAAC;oBAED,KAAK,oBAAoB,CAAC,CAAC,CAAC;wBAC1B,IAAI,SAAS,EAAE,CAAC;4BACd,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;4BAC/B,SAAS,GAAG,KAAK,CAAC;wBACpB,CAAC;wBACD,MAAM;oBACR,CAAC;oBAED,KAAK,eAAe,CAAC,CAAC,CAAC;wBACrB,8DAA8D;wBAC9D,MAAM,YAAY,GAAI,KAAa,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC;wBAC9D,MAAM,CAAC,KAAK,CACV,KAAK,EACL,oBAAoB,KAAK,CAAC,KAAK,CAAC,WAAW,QAAQ,WAAW,SAAS,YAAY,EAAE,CACtF,CAAC;wBACF,MAAM;4BACJ,IAAI,EAAE,aAAa;4BACnB,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,IAAI,UAAU;4BAClD,KAAK,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE;yBACrC,CAAC;wBACF,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM;gBACJ,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aACjE,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,gBAAgB,CAAC,KAAc;QACrC,8DAA8D;QAC9D,MAAM,MAAM,GAAI,KAAa,EAAE,MAAM,CAAC;QACtC,IACE,MAAM,KAAK,GAAG;YACd,MAAM,KAAK,GAAG;YACd,MAAM,KAAK,GAAG;YACd,MAAM,KAAK,GAAG,EACd,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,IACE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;YACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;YAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAC7B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,KAAc;QAClC,8DAA8D;QAC9D,MAAM,MAAM,GAAI,KAAa,EAAE,MAA4B,CAAC;QAC5D,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEvE,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,GAAG;gBACN,OAAO,IAAI,QAAQ,CACjB,qCAAqC,EACrC,GAAG,EACH,KAAK,CACN,CAAC;YACJ,KAAK,GAAG,CAAC,CAAC,CAAC;gBACT,iDAAiD;gBACjD,IACE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;oBAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;oBAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;oBAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAC5B,CAAC;oBACD,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC3C,CAAC;gBACD,OAAO,IAAI,QAAQ,CAAC,WAAW,OAAO,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACxD,CAAC;YACD,KAAK,GAAG;gBACN,OAAO,IAAI,QAAQ,CACjB,mBAAmB,EACnB,GAAG,EACH,IAAI,CACL,CAAC;YACJ,KAAK,GAAG,CAAC;YACT,KAAK,GAAG,CAAC;YACT,KAAK,GAAG;gBACN,OAAO,IAAI,QAAQ,CACjB,0BAA0B,MAAM,UAAU,EAC1C,MAAM,EACN,IAAI,CACL,CAAC;YACJ,OAAO,CAAC,CAAC,CAAC;gBACR,IACE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;oBACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;oBAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;oBAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAC7B,CAAC;oBACD,OAAO,IAAI,QAAQ,CACjB,cAAc,EACd,SAAS,EACT,IAAI,CACL,CAAC;gBACJ,CAAC;gBACD,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ import type { LLMProvider } from './provider.js';
2
+ import type { LLMProviderName, AuthMode, ProviderConfig } from '../config/types.js';
3
+ /**
4
+ * Provider 模型信息
5
+ */
6
+ export interface ProviderModelInfo {
7
+ id: string;
8
+ displayName: string;
9
+ }
10
+ /**
11
+ * Provider 注册信息
12
+ */
13
+ export interface ProviderInfo {
14
+ name: LLMProviderName;
15
+ displayName: string;
16
+ authModes: Array<{
17
+ mode: AuthMode;
18
+ displayName: string;
19
+ }>;
20
+ models: ProviderModelInfo[];
21
+ validateKey?: (key: string) => string | null;
22
+ keyPlaceholder?: string;
23
+ }
24
+ /**
25
+ * Provider 注册表
26
+ *
27
+ * 包含所有支持的 Provider 的元数据:显示名、认证方式、模型列表、校验规则。
28
+ */
29
+ export declare const PROVIDER_REGISTRY: ProviderInfo[];
30
+ /**
31
+ * 根据 provider name 查找注册信息
32
+ */
33
+ export declare function getProviderInfo(name: LLMProviderName): ProviderInfo | undefined;
34
+ /**
35
+ * 根据配置创建 LLM Provider 实例
36
+ *
37
+ * - anthropic → AnthropicProvider(api_key)
38
+ * - openai → OpenAIProvider(api_key)
39
+ * - openai-codex → OpenAIProvider(oauth_access_token)
40
+ */
41
+ export declare function createLLMProvider(providerConfig: ProviderConfig, providerName: LLMProviderName): Promise<LLMProvider>;
42
+ /**
43
+ * 从 provider API 动态获取可用模型列表
44
+ *
45
+ * 有凭证时尝试从 API 获取,失败或无凭证时回退到 PROVIDER_REGISTRY 中的固定列表。
46
+ */
47
+ export declare function fetchAvailableModels(providerName: LLMProviderName, credential?: string): Promise<ProviderModelInfo[]>;
@@ -0,0 +1,163 @@
1
+ import { AnthropicProvider } from './anthropic.js';
2
+ import { OpenAIProvider } from './openai.js';
3
+ import { getCodexAuthInfo, OpenAICodexProvider } from './openai-codex.js';
4
+ /**
5
+ * Provider 注册表
6
+ *
7
+ * 包含所有支持的 Provider 的元数据:显示名、认证方式、模型列表、校验规则。
8
+ */
9
+ export const PROVIDER_REGISTRY = [
10
+ {
11
+ name: 'openai-codex',
12
+ displayName: 'OpenAI Codex (OAuth)',
13
+ authModes: [{ mode: 'oauth', displayName: 'Codex OAuth 登录' }],
14
+ models: [
15
+ { id: 'gpt-5.4', displayName: 'GPT-5.4' },
16
+ { id: 'gpt-5.3-codex', displayName: 'GPT-5.3 Codex' },
17
+ { id: 'gpt-5.2-codex', displayName: 'GPT-5.2 Codex' },
18
+ { id: 'gpt-5.1-codex', displayName: 'GPT-5.1 Codex' },
19
+ ],
20
+ },
21
+ {
22
+ name: 'openai',
23
+ displayName: 'OpenAI (API Key)',
24
+ authModes: [{ mode: 'api_key', displayName: 'API Key' }],
25
+ models: [
26
+ { id: 'gpt-4o', displayName: 'GPT-4o' },
27
+ { id: 'gpt-4o-mini', displayName: 'GPT-4o Mini' },
28
+ { id: 'o3-mini', displayName: 'o3-mini' },
29
+ ],
30
+ validateKey: (key) => !key
31
+ ? 'API Key 不能为空'
32
+ : !key.startsWith('sk-')
33
+ ? 'API Key 格式不正确,应以 sk- 开头'
34
+ : null,
35
+ keyPlaceholder: 'sk-xxx...',
36
+ },
37
+ {
38
+ name: 'anthropic',
39
+ displayName: 'Anthropic (Claude)',
40
+ authModes: [{ mode: 'api_key', displayName: 'API Key' }],
41
+ models: [
42
+ { id: 'claude-opus-4-20250514', displayName: 'Claude Opus 4' },
43
+ { id: 'claude-sonnet-4-20250514', displayName: 'Claude Sonnet 4' },
44
+ { id: 'claude-haiku-3-5-20241022', displayName: 'Claude Haiku 3.5' },
45
+ ],
46
+ validateKey: (key) => !key
47
+ ? 'API Key 不能为空'
48
+ : !key.startsWith('sk-ant-')
49
+ ? 'API Key 格式不正确,应以 sk-ant- 开头'
50
+ : null,
51
+ keyPlaceholder: 'sk-ant-xxx...',
52
+ },
53
+ ];
54
+ /**
55
+ * 根据 provider name 查找注册信息
56
+ */
57
+ export function getProviderInfo(name) {
58
+ return PROVIDER_REGISTRY.find((p) => p.name === name);
59
+ }
60
+ /**
61
+ * 根据配置创建 LLM Provider 实例
62
+ *
63
+ * - anthropic → AnthropicProvider(api_key)
64
+ * - openai → OpenAIProvider(api_key)
65
+ * - openai-codex → OpenAIProvider(oauth_access_token)
66
+ */
67
+ export async function createLLMProvider(providerConfig, providerName) {
68
+ switch (providerName) {
69
+ case 'anthropic': {
70
+ if (!providerConfig.api_key) {
71
+ throw new Error('Anthropic API Key 未配置');
72
+ }
73
+ return new AnthropicProvider(providerConfig.api_key);
74
+ }
75
+ case 'openai': {
76
+ if (!providerConfig.api_key) {
77
+ throw new Error('OpenAI API Key 未配置');
78
+ }
79
+ return new OpenAIProvider(providerConfig.api_key, providerConfig.base_url);
80
+ }
81
+ case 'openai-codex': {
82
+ const { accessToken, accountId } = await getCodexAuthInfo();
83
+ return new OpenAICodexProvider(accessToken, accountId);
84
+ }
85
+ default:
86
+ throw new Error(`不支持的 Provider: ${providerName}`);
87
+ }
88
+ }
89
+ /**
90
+ * 从 provider API 动态获取可用模型列表
91
+ *
92
+ * 有凭证时尝试从 API 获取,失败或无凭证时回退到 PROVIDER_REGISTRY 中的固定列表。
93
+ */
94
+ export async function fetchAvailableModels(providerName, credential) {
95
+ const providerInfo = getProviderInfo(providerName);
96
+ if (!providerInfo) {
97
+ return [];
98
+ }
99
+ // 无凭证时直接返回固定列表
100
+ if (!credential) {
101
+ return providerInfo.models;
102
+ }
103
+ try {
104
+ if (providerName === 'anthropic') {
105
+ return await fetchAnthropicModels(credential);
106
+ }
107
+ else {
108
+ return await fetchOpenAIModels(credential);
109
+ }
110
+ }
111
+ catch {
112
+ // API 获取失败,回退到固定列表
113
+ return providerInfo.models;
114
+ }
115
+ }
116
+ /**
117
+ * 从 Anthropic API 获取模型列表
118
+ */
119
+ async function fetchAnthropicModels(apiKey) {
120
+ const response = await fetch('https://api.anthropic.com/v1/models', {
121
+ headers: {
122
+ 'x-api-key': apiKey,
123
+ 'anthropic-version': '2023-06-01',
124
+ },
125
+ });
126
+ if (!response.ok) {
127
+ throw new Error(`Anthropic API error: ${response.status}`);
128
+ }
129
+ const data = (await response.json());
130
+ return data.data
131
+ .filter((m) => m.id.includes('claude'))
132
+ .map((m) => ({
133
+ id: m.id,
134
+ displayName: m.display_name || m.id,
135
+ }))
136
+ .sort((a, b) => a.id.localeCompare(b.id));
137
+ }
138
+ /**
139
+ * 从 OpenAI API 获取模型列表
140
+ */
141
+ async function fetchOpenAIModels(credential) {
142
+ const response = await fetch('https://api.openai.com/v1/models', {
143
+ headers: {
144
+ Authorization: `Bearer ${credential}`,
145
+ },
146
+ });
147
+ if (!response.ok) {
148
+ throw new Error(`OpenAI API error: ${response.status}`);
149
+ }
150
+ const data = (await response.json());
151
+ // 过滤出 chat 模型(gpt-*, o1-*, o3-*),排除 embedding/tts 等
152
+ return data.data
153
+ .filter((m) => m.id.startsWith('gpt-') ||
154
+ m.id.startsWith('o1-') ||
155
+ m.id.startsWith('o3-') ||
156
+ m.id.startsWith('o4-'))
157
+ .map((m) => ({
158
+ id: m.id,
159
+ displayName: m.id,
160
+ }))
161
+ .sort((a, b) => a.id.localeCompare(b.id));
162
+ }
163
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/llm/factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { LLMProvider } from './provider.js';
2
+ import type { Message, ToolDefinition, ChatOptions, StreamEvent } from './types.js';
3
+ /**
4
+ * 检查 codex CLI 是否已安装
5
+ */
6
+ export declare function isCodexInstalled(): boolean;
7
+ /**
8
+ * 通过 Codex CLI 执行 Device Code Flow OAuth
9
+ *
10
+ * 调用 `codex login --device-auth` 子进程(继承 stdio 以展示 URL 和用户码)。
11
+ * 授权完成后 token 由 Codex 存储到 ~/.codex/auth.json。
12
+ */
13
+ export declare function codexOAuthLogin(): Promise<void>;
14
+ /**
15
+ * Codex 认证信息
16
+ */
17
+ export interface CodexAuthInfo {
18
+ accessToken: string;
19
+ accountId: string;
20
+ }
21
+ /**
22
+ * 从 ~/.codex/auth.json 读取认证信息(access_token + account_id)
23
+ *
24
+ * 若 token 可能过期(last_refresh > 55 分钟前),自动刷新。
25
+ */
26
+ export declare function getCodexAuthInfo(): Promise<CodexAuthInfo>;
27
+ /**
28
+ * 从 ~/.codex/auth.json 读取当前 access_token
29
+ *
30
+ * 若 token 可能过期(last_refresh > 55 分钟前),自动刷新。
31
+ */
32
+ export declare function getCodexAccessToken(): Promise<string>;
33
+ /**
34
+ * OpenAI Codex LLM Provider
35
+ *
36
+ * 使用 ChatGPT 后端的 Responses API (`chatgpt.com/backend-api/codex/responses`)。
37
+ * Codex OAuth Token 绑定 ChatGPT/Codex 订阅,需要通过 ChatGPT 后端代理调用。
38
+ * 标准 OpenAI API (`api.openai.com`) 不接受 Codex OAuth Token。
39
+ */
40
+ export declare class OpenAICodexProvider implements LLMProvider {
41
+ readonly name = "openai-codex";
42
+ private client;
43
+ constructor(accessToken: string, accountId: string);
44
+ chat(messages: Message[], tools: ToolDefinition[], options?: ChatOptions): AsyncGenerator<StreamEvent>;
45
+ }