@arrislink/axon 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -161,8 +161,8 @@ ax plan # Uses configured provider
161
161
 
162
162
  **Provider Priority:**
163
163
  1. **CLI Mode** - Uses OpenCode CLI (inherits full OMO capabilities)
164
- 2. **Direct Mode** - Reads OMO config, calls APIs directly
165
- 3. **Fallback Mode** - Uses `ANTHROPIC_API_KEY` environment variable
164
+ 2. **Direct Mode** - Reads OMO config and resolves **Antigravity** tokens automatically
165
+ 3. **Fallback Mode** - Uses `ANTHROPIC_API_KEY` etc. environment variables
166
166
 
167
167
  ### Environment Variables
168
168
 
@@ -216,8 +216,8 @@ graph TD
216
216
 
217
217
  subgraph "LLM Layer"
218
218
  Orch --> LLMInt[Unified LLM Interface]
219
- LLMInt --> OMO[OhMyOpenCode Registry]
220
- OMO --> Providers[Providers: Anthropic, OpenAI, Antigrav, etc.]
219
+ LLMInt --> OMO[OMO Config & Antigravity Auth]
220
+ OMO --> Providers[Providers: Anthropic, Google Gemini, OpenAI, etc.]
221
221
  end
222
222
  ```
223
223
 
package/README.zh-CN.md CHANGED
@@ -156,12 +156,12 @@ omo config set-provider antigravity
156
156
 
157
157
  # Axon 自动检测并使用 OMO 配置
158
158
  ax plan # 使用配置的提供商
159
- ```
160
159
 
161
- **提供商优先级:**
162
- 1. **CLI 模式** - 使用 OpenCode CLI(继承 OMO 全部能力)
163
- 2. **直接模式** - 读取 OMO 配置,直接调用 API
164
- 3. **回退模式** - 使用 `ANTHROPIC_API_KEY` 环境变量
160
+ **Provider 优先级:**
161
+ 1. **CLI 模式** - 使用 OpenCode CLI (继承完整的 OMO 能力)
162
+ 2. **Direct 模式** - 读取 OMO 配置并自动解析 **Antigravity** 刷新令牌
163
+ 3. **Fallback 模式** - 使用 `ANTHROPIC_API_KEY` 等环境变量
164
+ ```
165
165
 
166
166
  ### 环境变量
167
167
 
@@ -215,8 +215,8 @@ graph TD
215
215
 
216
216
  subgraph "LLM 层"
217
217
  Orch --> LLMInt[统一 LLM 接口]
218
- LLMInt --> OMO[OhMyOpenCode 注册表]
219
- OMO --> Providers[平台: Anthropic, OpenAI, Antigrav 等]
218
+ LLMInt --> OMO[OMO 配置 & Antigravity 认证]
219
+ OMO --> Providers[提供商: Anthropic, Google Gemini, OpenAI, etc.]
220
220
  end
221
221
  ```
222
222
 
package/dist/index.js CHANGED
@@ -2427,9 +2427,15 @@ var init_defaults = __esm(() => {
2427
2427
  MODEL_PRICING = {
2428
2428
  "claude-sonnet-4-20250514": { input: 3, output: 15 },
2429
2429
  "claude-opus-4-5-20251101": { input: 15, output: 75 },
2430
+ "claude-opus-4-6": { input: 15, output: 75 },
2430
2431
  "gemini-2.0-flash-exp": { input: 0.5, output: 0.5 },
2432
+ "gemini-3-pro": { input: 1.25, output: 3.75 },
2433
+ "gemini-3-flash": { input: 0.1, output: 0.4 },
2431
2434
  "gpt-4-turbo": { input: 10, output: 30 },
2432
- "gpt-4o": { input: 5, output: 15 }
2435
+ "gpt-4o": { input: 5, output: 15 },
2436
+ "gpt-5.3-codex": { input: 10, output: 30 },
2437
+ "gpt-5-nano": { input: 0.5, output: 1.5 },
2438
+ "glm-4.7-free": { input: 0, output: 0 }
2433
2439
  };
2434
2440
  });
2435
2441
 
@@ -16398,120 +16404,6 @@ var init_anthropic = __esm(() => {
16398
16404
  init_errors();
16399
16405
  });
16400
16406
 
16401
- // src/core/llm/opencode-client.ts
16402
- class OpenCodeLLMClient {
16403
- agent;
16404
- command;
16405
- constructor(agent = "sisyphus", command = ["opencode"]) {
16406
- this.agent = agent;
16407
- this.command = command;
16408
- }
16409
- async chat(messages, options) {
16410
- const iterator = this.streamChat(messages, options);
16411
- let result = await iterator.next();
16412
- while (!result.done) {
16413
- result = await iterator.next();
16414
- }
16415
- return result.value;
16416
- }
16417
- async* streamChat(messages, options) {
16418
- const prompt = this.formatMessages(messages);
16419
- const args = [...this.command, "run", "--agent", this.agent, "--format", "json"];
16420
- if (options?.model) {
16421
- args.push("--model", options.model);
16422
- }
16423
- const proc = Bun.spawn(args, {
16424
- stdin: new Blob([prompt]),
16425
- stdout: "pipe",
16426
- stderr: "pipe"
16427
- });
16428
- let fullResponse = "";
16429
- let metadata = {
16430
- model: "unknown",
16431
- tokens: { input: 0, output: 0 },
16432
- cost: 0
16433
- };
16434
- const decoder = new TextDecoder;
16435
- const reader = proc.stdout.getReader();
16436
- let buffer = "";
16437
- try {
16438
- while (true) {
16439
- const { done, value } = await reader.read();
16440
- if (done)
16441
- break;
16442
- buffer += decoder.decode(value, { stream: true });
16443
- const lines = buffer.split(`
16444
- `);
16445
- buffer = lines.pop() || "";
16446
- for (const line of lines) {
16447
- if (!line.trim())
16448
- continue;
16449
- try {
16450
- const event = JSON.parse(line);
16451
- if (event.type === "config" && event.part?.model) {
16452
- metadata.model = event.part.model;
16453
- } else if (event.model && metadata.model === "unknown") {
16454
- metadata.model = event.model;
16455
- }
16456
- if (event.type === "text" && event.part?.text) {
16457
- const text = event.part.text;
16458
- fullResponse += text;
16459
- yield text;
16460
- } else if (event.type === "content" && event.part?.content) {
16461
- const text = event.part.content;
16462
- fullResponse += text;
16463
- yield text;
16464
- } else if (event.type === "step_finish") {
16465
- if (event.part?.snapshot) {}
16466
- if (event.part?.tokens) {
16467
- metadata.tokens.input = event.part.tokens.input || 0;
16468
- metadata.tokens.output = event.part.tokens.output || 0;
16469
- }
16470
- if (event.part?.cost) {
16471
- metadata.cost = event.part.cost;
16472
- }
16473
- }
16474
- } catch (e) {}
16475
- }
16476
- }
16477
- } finally {
16478
- reader.releaseLock();
16479
- proc.kill();
16480
- }
16481
- const exitCode = await proc.exited;
16482
- const stderr = await new Response(proc.stderr).text();
16483
- if (exitCode !== 0) {
16484
- throw new Error(`OpenCode CLI Error: ${stderr || "Unknown error"}`);
16485
- }
16486
- if (fullResponse.trim() === "" && stderr.trim().length > 0) {
16487
- if (stderr.includes("Error") || stderr.includes("NotFound") || stderr.includes("|")) {
16488
- throw new Error(`OpenCode CLI Silent Crash: ${stderr.split(`
16489
- `)[0]}`);
16490
- }
16491
- }
16492
- if (fullResponse.trim() === "") {
16493
- throw new Error("OpenCode CLI returned an empty response");
16494
- }
16495
- return {
16496
- content: fullResponse,
16497
- model: metadata.model,
16498
- tokens: metadata.tokens,
16499
- cost: metadata.cost
16500
- };
16501
- }
16502
- async complete(prompt, options) {
16503
- const result = await this.chat([{ role: "user", content: prompt }], options);
16504
- return result.content;
16505
- }
16506
- formatMessages(messages) {
16507
- return messages.map((m) => `<${m.role}>
16508
- ${m.content}
16509
- </${m.role}>`).join(`
16510
-
16511
- `);
16512
- }
16513
- }
16514
-
16515
16407
  // src/core/llm/omo-config-reader.ts
16516
16408
  var exports_omo_config_reader = {};
16517
16409
  __export(exports_omo_config_reader, {
@@ -16521,6 +16413,18 @@ __export(exports_omo_config_reader, {
16521
16413
  });
16522
16414
  import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
16523
16415
  import { homedir as homedir2 } from "os";
16416
+ function resolveProviderType(modelString) {
16417
+ const [prefix] = (modelString || "").split("/");
16418
+ const typeMap = {
16419
+ opencode: "antigravity",
16420
+ anthropic: "anthropic",
16421
+ google: "google",
16422
+ openai: "openai",
16423
+ mistral: "mistral",
16424
+ deepseek: "deepseek"
16425
+ };
16426
+ return typeMap[prefix] || prefix;
16427
+ }
16524
16428
  function getOMOConfigPaths() {
16525
16429
  const home = homedir2();
16526
16430
  return [
@@ -16538,8 +16442,10 @@ class OMOConfigReader {
16538
16442
  configSource = "";
16539
16443
  defaultProvider;
16540
16444
  fallbackChain = [];
16445
+ antigravityToken;
16541
16446
  constructor() {
16542
16447
  this.loadConfig();
16448
+ this.loadAntigravityToken();
16543
16449
  }
16544
16450
  loadConfig() {
16545
16451
  const paths = getOMOConfigPaths();
@@ -16550,7 +16456,7 @@ class OMOConfigReader {
16550
16456
  if (path.endsWith(".yaml") || path.endsWith(".yml")) {
16551
16457
  this.loadYamlConfig(content);
16552
16458
  } else if (path.endsWith(".json")) {
16553
- this.loadJsonConfig(content);
16459
+ this.loadJsonConfig(content, path);
16554
16460
  }
16555
16461
  if (this.providers.length > 0) {
16556
16462
  this.configSource = path;
@@ -16561,6 +16467,45 @@ class OMOConfigReader {
16561
16467
  }
16562
16468
  }
16563
16469
  }
16470
+ if (this.providers.length > 0 && !this.configSource.endsWith("opencode.json")) {
16471
+ this.mergeOpenCodeProviders();
16472
+ }
16473
+ }
16474
+ loadAntigravityToken() {
16475
+ try {
16476
+ const accountsPath = `${homedir2()}/.config/opencode/antigravity-accounts.json`;
16477
+ if (existsSync4(accountsPath)) {
16478
+ const accounts = JSON.parse(readFileSync2(accountsPath, "utf-8"));
16479
+ if (accounts.accounts?.length > 0) {
16480
+ const activeIdx = accounts.activeIndex ?? 0;
16481
+ const account = accounts.accounts.find((a) => a.enabled !== false) || accounts.accounts[activeIdx];
16482
+ if (account) {
16483
+ this.antigravityToken = account.token || account.refreshToken;
16484
+ }
16485
+ }
16486
+ }
16487
+ } catch {}
16488
+ }
16489
+ mergeOpenCodeProviders() {
16490
+ const opencodePath = `${homedir2()}/.config/opencode/opencode.json`;
16491
+ if (!existsSync4(opencodePath))
16492
+ return;
16493
+ try {
16494
+ const content = readFileSync2(opencodePath, "utf-8");
16495
+ const config = JSON.parse(content);
16496
+ if (config.provider) {
16497
+ for (const [name, details] of Object.entries(config.provider)) {
16498
+ if (!this.providers.some((p) => p.name === name)) {
16499
+ this.providers.push({
16500
+ name,
16501
+ type: name,
16502
+ models: Object.keys(details.models || {}),
16503
+ endpoint: details.endpoint
16504
+ });
16505
+ }
16506
+ }
16507
+ }
16508
+ } catch {}
16564
16509
  }
16565
16510
  loadYamlConfig(content) {
16566
16511
  const config = $parse(content);
@@ -16570,15 +16515,15 @@ class OMOConfigReader {
16570
16515
  this.fallbackChain = config.fallback_chain || [];
16571
16516
  }
16572
16517
  }
16573
- loadJsonConfig(content) {
16518
+ loadJsonConfig(content, _filePath) {
16574
16519
  const config = JSON.parse(content);
16575
16520
  if (config.agents) {
16576
16521
  this.providers = Object.entries(config.agents).map(([name, agent]) => {
16577
- const [providerType] = (agent.model || "").split("/");
16522
+ const resolvedType = resolveProviderType(agent.model || "");
16578
16523
  return {
16579
16524
  name,
16580
16525
  models: [agent.model || "unknown"],
16581
- type: providerType || "unknown",
16526
+ type: resolvedType,
16582
16527
  endpoint: undefined,
16583
16528
  api_key: undefined
16584
16529
  };
@@ -16612,11 +16557,14 @@ class OMOConfigReader {
16612
16557
  if (p)
16613
16558
  return p;
16614
16559
  }
16615
- const priority2 = ["antigravity", "anthropic", "openai", "google", "sisyphus"];
16616
- for (const name of priority2) {
16617
- const p = this.getProvider(name);
16618
- if (p)
16619
- return p;
16560
+ const priority2 = ["antigravity", "anthropic", "openai", "google"];
16561
+ for (const target of priority2) {
16562
+ const byName = this.getProvider(target);
16563
+ if (byName)
16564
+ return byName;
16565
+ const byType = this.providers.find((p) => p.type === target);
16566
+ if (byType)
16567
+ return byType;
16620
16568
  }
16621
16569
  return this.providers[0] || null;
16622
16570
  }
@@ -16633,20 +16581,155 @@ class OMOConfigReader {
16633
16581
  getConfigSource() {
16634
16582
  return this.configSource;
16635
16583
  }
16584
+ hasAntigravityAuth() {
16585
+ return !!this.antigravityToken;
16586
+ }
16587
+ getAntigravityToken() {
16588
+ return this.antigravityToken;
16589
+ }
16636
16590
  getProviderApiKey(provider) {
16637
16591
  if (provider.api_key) {
16638
16592
  const match = provider.api_key.match(/^\$\{(\w+)\}$/);
16639
16593
  return match ? process.env[match[1]] : provider.api_key;
16640
16594
  }
16641
16595
  const type = provider.type || provider.name;
16642
- const envVar = `${type.toUpperCase()}_API_KEY`;
16643
- return process.env[envVar];
16596
+ const envMappings = {
16597
+ anthropic: ["ANTHROPIC_API_KEY"],
16598
+ openai: ["OPENAI_API_KEY"],
16599
+ google: ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
16600
+ antigravity: ["ANTIGRAVITY_API_KEY"],
16601
+ deepseek: ["DEEPSEEK_API_KEY"]
16602
+ };
16603
+ const envVars = envMappings[type] || [`${type.toUpperCase()}_API_KEY`];
16604
+ for (const envVar of envVars) {
16605
+ const envKey = process.env[envVar];
16606
+ if (envKey)
16607
+ return envKey;
16608
+ }
16609
+ if (this.antigravityToken) {
16610
+ return this.antigravityToken;
16611
+ }
16612
+ return;
16644
16613
  }
16645
16614
  }
16646
16615
  var init_omo_config_reader = __esm(() => {
16647
16616
  init_dist();
16648
16617
  });
16649
16618
 
16619
+ // src/core/llm/opencode-client.ts
16620
+ class OpenCodeLLMClient {
16621
+ agent;
16622
+ command;
16623
+ constructor(agent = "sisyphus", command = ["opencode"]) {
16624
+ this.agent = agent;
16625
+ this.command = command;
16626
+ }
16627
+ async chat(messages, options) {
16628
+ const iterator = this.streamChat(messages, options);
16629
+ let result = await iterator.next();
16630
+ while (!result.done) {
16631
+ result = await iterator.next();
16632
+ }
16633
+ return result.value;
16634
+ }
16635
+ async* streamChat(messages, options) {
16636
+ const prompt = this.formatMessages(messages);
16637
+ const args = [...this.command, "run", "--agent", this.agent, "--format", "json"];
16638
+ if (options?.model) {
16639
+ args.push("--model", options.model);
16640
+ }
16641
+ const proc = Bun.spawn(args, {
16642
+ stdin: new Blob([prompt]),
16643
+ stdout: "pipe",
16644
+ stderr: "pipe"
16645
+ });
16646
+ let fullResponse = "";
16647
+ let metadata = {
16648
+ model: "unknown",
16649
+ tokens: { input: 0, output: 0 },
16650
+ cost: 0
16651
+ };
16652
+ const decoder = new TextDecoder;
16653
+ const reader = proc.stdout.getReader();
16654
+ let buffer = "";
16655
+ try {
16656
+ while (true) {
16657
+ const { done, value } = await reader.read();
16658
+ if (done)
16659
+ break;
16660
+ buffer += decoder.decode(value, { stream: true });
16661
+ const lines = buffer.split(`
16662
+ `);
16663
+ buffer = lines.pop() || "";
16664
+ for (const line of lines) {
16665
+ if (!line.trim())
16666
+ continue;
16667
+ try {
16668
+ const event = JSON.parse(line);
16669
+ if (event.type === "config" && event.part?.model) {
16670
+ metadata.model = event.part.model;
16671
+ } else if (event.model && metadata.model === "unknown") {
16672
+ metadata.model = event.model;
16673
+ }
16674
+ if (event.type === "text" && event.part?.text) {
16675
+ const text = event.part.text;
16676
+ fullResponse += text;
16677
+ yield text;
16678
+ } else if (event.type === "content" && event.part?.content) {
16679
+ const text = event.part.content;
16680
+ fullResponse += text;
16681
+ yield text;
16682
+ } else if (event.type === "step_finish") {
16683
+ if (event.part?.snapshot) {}
16684
+ if (event.part?.tokens) {
16685
+ metadata.tokens.input = event.part.tokens.input || 0;
16686
+ metadata.tokens.output = event.part.tokens.output || 0;
16687
+ }
16688
+ if (event.part?.cost) {
16689
+ metadata.cost = event.part.cost;
16690
+ }
16691
+ }
16692
+ } catch (e) {}
16693
+ }
16694
+ }
16695
+ } finally {
16696
+ reader.releaseLock();
16697
+ proc.kill();
16698
+ }
16699
+ const exitCode = await proc.exited;
16700
+ const stderr = await new Response(proc.stderr).text();
16701
+ if (exitCode !== 0) {
16702
+ throw new Error(`OpenCode CLI Error: ${stderr || "Unknown error"}`);
16703
+ }
16704
+ if (fullResponse.trim() === "" && stderr.trim().length > 0) {
16705
+ if (stderr.includes("Error") || stderr.includes("NotFound") || stderr.includes("|")) {
16706
+ throw new Error(`OpenCode CLI Silent Crash: ${stderr.split(`
16707
+ `)[0]}`);
16708
+ }
16709
+ }
16710
+ if (fullResponse.trim() === "") {
16711
+ throw new Error("OpenCode CLI returned an empty response");
16712
+ }
16713
+ return {
16714
+ content: fullResponse,
16715
+ model: metadata.model,
16716
+ tokens: metadata.tokens,
16717
+ cost: metadata.cost
16718
+ };
16719
+ }
16720
+ async complete(prompt, options) {
16721
+ const result = await this.chat([{ role: "user", content: prompt }], options);
16722
+ return result.content;
16723
+ }
16724
+ formatMessages(messages) {
16725
+ return messages.map((m) => `<${m.role}>
16726
+ ${m.content}
16727
+ </${m.role}>`).join(`
16728
+
16729
+ `);
16730
+ }
16731
+ }
16732
+
16650
16733
  // src/core/llm/unified-client.ts
16651
16734
  class UnifiedLLMClient {
16652
16735
  omoConfig;
@@ -16658,11 +16741,20 @@ class UnifiedLLMClient {
16658
16741
  if (!provider) {
16659
16742
  throw new Error("\u672A\u627E\u5230\u53EF\u7528\u7684 LLM Provider");
16660
16743
  }
16661
- switch (provider.name) {
16744
+ const providerType = provider.type || provider.name;
16745
+ switch (providerType) {
16662
16746
  case "anthropic":
16663
- case "antigravity":
16664
16747
  return this.chatAnthropic(provider, messages, options);
16748
+ case "antigravity":
16749
+ return this.chatAntigravity(provider, messages, options);
16750
+ case "google":
16751
+ return this.chatGoogle(provider, messages, options);
16752
+ case "openai":
16753
+ return this.chatOpenAI(provider, messages, options);
16754
+ case "deepseek":
16755
+ return this.chatOpenAI(provider, messages, options, "https://api.deepseek.com/v1");
16665
16756
  default:
16757
+ console.warn(`\uD83E\uDDE0 Axon: \u672A\u77E5 provider type '${providerType}'\uFF0C\u4F7F\u7528 Anthropic \u517C\u5BB9\u6A21\u5F0F`);
16666
16758
  return this.chatAnthropic(provider, messages, options);
16667
16759
  }
16668
16760
  }
@@ -16670,17 +16762,40 @@ class UnifiedLLMClient {
16670
16762
  const result = await this.chat([{ role: "user", content: prompt }], options);
16671
16763
  return result.content;
16672
16764
  }
16765
+ cleanModelName(model) {
16766
+ const parts = model.split("/");
16767
+ return parts.length > 1 ? parts.slice(1).join("/") : model;
16768
+ }
16673
16769
  async chatAnthropic(provider, messages, options) {
16674
16770
  const apiKey = this.omoConfig.getProviderApiKey(provider) || process.env["ANTHROPIC_API_KEY"];
16675
16771
  if (!apiKey) {
16676
16772
  throw new Error(`\u672A\u627E\u5230 ${provider.name} \u7684 API \u5BC6\u94A5`);
16677
16773
  }
16774
+ const model = this.cleanModelName(options?.model || provider.models?.[0] || "claude-sonnet-4-20250514");
16775
+ const client = new AnthropicClient(apiKey, {
16776
+ model,
16777
+ provider: "anthropic",
16778
+ temperature: options?.temperature ?? 0.7,
16779
+ max_tokens: options?.maxTokens || 8000
16780
+ }, provider.endpoint || "https://api.anthropic.com/v1");
16781
+ return this.executeAnthropicChat(client, model, messages, options);
16782
+ }
16783
+ async chatAntigravity(provider, messages, options) {
16784
+ const apiKey = this.omoConfig.getProviderApiKey(provider);
16785
+ if (!apiKey) {
16786
+ throw new Error(`\u672A\u627E\u5230 ${provider.name} \u7684 API \u5BC6\u94A5 (Antigravity token \u6216\u73AF\u5883\u53D8\u91CF\u5747\u672A\u8BBE\u7F6E)`);
16787
+ }
16788
+ const rawModel = options?.model || provider.models?.[0] || "claude-sonnet-4-20250514";
16789
+ const displayModel = this.cleanModelName(rawModel);
16678
16790
  const client = new AnthropicClient(apiKey, {
16679
- model: options?.model || provider.models?.[0] || "claude-sonnet-4-20250514",
16791
+ model: rawModel,
16680
16792
  provider: "anthropic",
16681
16793
  temperature: options?.temperature ?? 0.7,
16682
16794
  max_tokens: options?.maxTokens || 8000
16683
- }, provider.endpoint);
16795
+ }, provider.endpoint || "https://api.opencode.ai/v1");
16796
+ return this.executeAnthropicChat(client, displayModel, messages, options);
16797
+ }
16798
+ async executeAnthropicChat(client, model, messages, options) {
16684
16799
  const systemMessage = messages.find((m) => m.role === "system");
16685
16800
  const chatMessages = messages.filter((m) => m.role !== "system").map((m) => ({
16686
16801
  role: m.role,
@@ -16691,12 +16806,120 @@ class UnifiedLLMClient {
16691
16806
  });
16692
16807
  return {
16693
16808
  content: response.content,
16694
- model: options?.model || provider.models?.[0] || "unknown",
16809
+ model,
16695
16810
  tokens: {
16696
16811
  input: response.usage.input_tokens,
16697
16812
  output: response.usage.output_tokens
16698
16813
  },
16699
- cost: this.calculateCost(options?.model || provider.models?.[0] || "unknown", response.usage)
16814
+ cost: this.calculateCost(model, response.usage)
16815
+ };
16816
+ }
16817
+ async chatGoogle(provider, messages, options) {
16818
+ const apiKey = this.omoConfig.getProviderApiKey(provider);
16819
+ if (!apiKey) {
16820
+ throw new Error(`\u672A\u627E\u5230 ${provider.name} \u7684 API \u5BC6\u94A5`);
16821
+ }
16822
+ const model = this.cleanModelName(options?.model || provider.models?.[0] || "gemini-2.0-flash");
16823
+ const isAntigravityAuth = this.omoConfig.hasAntigravityAuth() && apiKey === this.omoConfig.getAntigravityToken();
16824
+ if (isAntigravityAuth) {
16825
+ return this.chatAntigravity({
16826
+ ...provider,
16827
+ type: "antigravity",
16828
+ endpoint: provider.endpoint || "https://api.opencode.ai/v1"
16829
+ }, messages, options);
16830
+ }
16831
+ const systemMessage = messages.find((m) => m.role === "system");
16832
+ const chatMessages = messages.filter((m) => m.role !== "system").map((m) => ({
16833
+ role: m.role === "assistant" ? "model" : "user",
16834
+ parts: [{ text: m.content }]
16835
+ }));
16836
+ const endpoint = provider.endpoint || "https://generativelanguage.googleapis.com/v1beta";
16837
+ const url = `${endpoint}/models/${model}:generateContent?key=${apiKey}`;
16838
+ const body = {
16839
+ contents: chatMessages,
16840
+ generationConfig: {
16841
+ temperature: options?.temperature ?? 0.7,
16842
+ maxOutputTokens: options?.maxTokens || 8000
16843
+ }
16844
+ };
16845
+ if (systemMessage) {
16846
+ body.systemInstruction = { parts: [{ text: systemMessage.content }] };
16847
+ }
16848
+ const response = await fetch(url, {
16849
+ method: "POST",
16850
+ headers: { "Content-Type": "application/json" },
16851
+ body: JSON.stringify(body)
16852
+ });
16853
+ if (!response.ok) {
16854
+ const errorData = await response.json().catch(() => ({}));
16855
+ throw new Error(`Google API \u8C03\u7528\u5931\u8D25 (${response.status}): ${errorData.error?.message || response.statusText}`);
16856
+ }
16857
+ const data = await response.json();
16858
+ const content = data.candidates?.[0]?.content?.parts?.[0]?.text || "";
16859
+ const usageMetadata = data.usageMetadata || {};
16860
+ return {
16861
+ content,
16862
+ model,
16863
+ tokens: {
16864
+ input: usageMetadata.promptTokenCount || 0,
16865
+ output: usageMetadata.candidatesTokenCount || 0
16866
+ },
16867
+ cost: this.calculateCost(model, {
16868
+ input_tokens: usageMetadata.promptTokenCount || 0,
16869
+ output_tokens: usageMetadata.candidatesTokenCount || 0
16870
+ })
16871
+ };
16872
+ }
16873
+ async chatOpenAI(provider, messages, options, defaultEndpoint = "https://api.openai.com/v1") {
16874
+ const apiKey = this.omoConfig.getProviderApiKey(provider);
16875
+ if (!apiKey) {
16876
+ throw new Error(`\u672A\u627E\u5230 ${provider.name} \u7684 API \u5BC6\u94A5`);
16877
+ }
16878
+ const isAntigravityAuth = this.omoConfig.hasAntigravityAuth() && apiKey === this.omoConfig.getAntigravityToken();
16879
+ if (isAntigravityAuth) {
16880
+ return this.chatAntigravity({
16881
+ ...provider,
16882
+ type: "antigravity"
16883
+ }, messages, options);
16884
+ }
16885
+ const model = this.cleanModelName(options?.model || provider.models?.[0] || "gpt-4o");
16886
+ const endpoint = provider.endpoint || defaultEndpoint;
16887
+ const url = `${endpoint}/chat/completions`;
16888
+ const openaiMessages = messages.map((m) => ({
16889
+ role: m.role,
16890
+ content: m.content
16891
+ }));
16892
+ const response = await fetch(url, {
16893
+ method: "POST",
16894
+ headers: {
16895
+ "Content-Type": "application/json",
16896
+ Authorization: `Bearer ${apiKey}`
16897
+ },
16898
+ body: JSON.stringify({
16899
+ model,
16900
+ messages: openaiMessages,
16901
+ temperature: options?.temperature ?? 0.7,
16902
+ max_tokens: options?.maxTokens || 8000
16903
+ })
16904
+ });
16905
+ if (!response.ok) {
16906
+ const errorData = await response.json().catch(() => ({}));
16907
+ throw new Error(`OpenAI API \u8C03\u7528\u5931\u8D25 (${response.status}): ${errorData.error?.message || response.statusText}`);
16908
+ }
16909
+ const data = await response.json();
16910
+ const content = data.choices?.[0]?.message?.content || "";
16911
+ const usage = data.usage || {};
16912
+ return {
16913
+ content,
16914
+ model,
16915
+ tokens: {
16916
+ input: usage.prompt_tokens || 0,
16917
+ output: usage.completion_tokens || 0
16918
+ },
16919
+ cost: this.calculateCost(model, {
16920
+ input_tokens: usage.prompt_tokens || 0,
16921
+ output_tokens: usage.completion_tokens || 0
16922
+ })
16700
16923
  };
16701
16924
  }
16702
16925
  calculateCost(model, usage) {
@@ -16746,7 +16969,10 @@ class AxonLLMClient {
16746
16969
  }
16747
16970
  } catch {}
16748
16971
  if (hasOMOConfig() && this.omoConfig.hasProviders()) {
16749
- return "direct";
16972
+ const primary = this.omoConfig.getPrimaryProvider();
16973
+ if (primary && this.omoConfig.getProviderApiKey(primary)) {
16974
+ return "direct";
16975
+ }
16750
16976
  }
16751
16977
  return "fallback";
16752
16978
  }
@@ -16759,18 +16985,32 @@ class AxonLLMClient {
16759
16985
  this.unifiedClient = new UnifiedLLMClient(this.omoConfig);
16760
16986
  break;
16761
16987
  case "fallback":
16762
- const apiKey = process.env["ANTHROPIC_API_KEY"];
16763
- if (apiKey) {
16764
- this.anthropicClient = new AnthropicClient(apiKey, {
16765
- model: "claude-3-5-sonnet-20240620",
16766
- provider: "anthropic",
16767
- temperature: 0.7,
16768
- max_tokens: 4000
16769
- });
16770
- }
16988
+ this.initFallbackClient();
16771
16989
  break;
16772
16990
  }
16773
16991
  }
16992
+ initFallbackClient() {
16993
+ const envKeys = [
16994
+ { key: "ANTHROPIC_API_KEY", model: "claude-sonnet-4-20250514", provider: "anthropic" },
16995
+ { key: "OPENAI_API_KEY", model: "gpt-4o", provider: "openai" },
16996
+ { key: "GOOGLE_API_KEY", model: "gemini-2.0-flash", provider: "google" }
16997
+ ];
16998
+ for (const { key, model, provider } of envKeys) {
16999
+ const apiKey = process.env[key];
17000
+ if (apiKey) {
17001
+ this.anthropicClient = new AnthropicClient(apiKey, {
17002
+ model,
17003
+ provider,
17004
+ temperature: 0.7,
17005
+ max_tokens: 4000
17006
+ });
17007
+ return;
17008
+ }
17009
+ }
17010
+ if (this.omoConfig.hasProviders() && this.omoConfig.hasAntigravityAuth()) {
17011
+ this.unifiedClient = new UnifiedLLMClient(this.omoConfig);
17012
+ }
17013
+ }
16774
17014
  async chat(messages, options) {
16775
17015
  try {
16776
17016
  if (this.mode === "cli" && this.openCodeClient) {
@@ -16783,20 +17023,54 @@ class AxonLLMClient {
16783
17023
  if (this.anthropicClient) {
16784
17024
  return await this.chatAnthropicFallback(messages, options);
16785
17025
  }
16786
- throw new APIError("\u672A\u627E\u5230\u6709\u6548\u7684 LLM \u914D\u7F6E\u6216 API \u5BC6\u94A5", 401);
17026
+ if (this.unifiedClient) {
17027
+ return await this.unifiedClient.chat(messages, options);
17028
+ }
17029
+ const diagInfo = this.getDiagnosticInfo();
17030
+ throw new APIError(`\u672A\u627E\u5230\u6709\u6548\u7684 LLM \u914D\u7F6E\u6216 API \u5BC6\u94A5 (${diagInfo})`, 401);
16787
17031
  }
16788
17032
  throw new Error(`\u672A\u652F\u6301\u7684 LLM \u6A21\u5F0F: ${this.mode}`);
16789
17033
  } catch (error) {
16790
- if (this.mode !== "fallback") {
16791
- console.warn(`\uD83E\uDDE0 Axon: ${this.mode} \u6A21\u5F0F\u8C03\u7528\u5931\u8D25\u6216\u54CD\u5E94\u4E3A\u7A7A\uFF0C\u5C1D\u8BD5\u56DE\u9000...`);
16792
- if (process.env["DEBUG"])
16793
- console.error(error);
16794
- this.mode = "fallback";
16795
- this.initClient();
16796
- return await this.chat(messages, options);
16797
- }
16798
- throw error;
17034
+ return this.handleChatError(error, messages, options);
17035
+ }
17036
+ }
17037
+ async handleChatError(error, messages, options) {
17038
+ if (this.mode === "cli") {
17039
+ console.warn("\uD83E\uDDE0 Axon: CLI \u6A21\u5F0F\u8C03\u7528\u5931\u8D25\uFF0C\u5C1D\u8BD5 Direct \u6A21\u5F0F...");
17040
+ if (process.env["DEBUG"])
17041
+ console.error(error);
17042
+ if (this.omoConfig.hasProviders()) {
17043
+ const primary = this.omoConfig.getPrimaryProvider();
17044
+ if (primary && this.omoConfig.getProviderApiKey(primary)) {
17045
+ this.mode = "direct";
17046
+ this.initClient();
17047
+ return await this.chat(messages, options);
17048
+ }
17049
+ }
17050
+ console.warn("\uD83E\uDDE0 Axon: Direct \u6A21\u5F0F\u65E0\u53EF\u7528 Provider\uFF0C\u5C1D\u8BD5 Fallback \u6A21\u5F0F...");
17051
+ this.mode = "fallback";
17052
+ this.initClient();
17053
+ return await this.chat(messages, options);
17054
+ }
17055
+ if (this.mode === "direct") {
17056
+ console.warn("\uD83E\uDDE0 Axon: Direct \u6A21\u5F0F\u8C03\u7528\u5931\u8D25\uFF0C\u5C1D\u8BD5 Fallback \u6A21\u5F0F...");
17057
+ if (process.env["DEBUG"])
17058
+ console.error(error);
17059
+ this.mode = "fallback";
17060
+ this.initClient();
17061
+ return await this.chat(messages, options);
16799
17062
  }
17063
+ throw error;
17064
+ }
17065
+ getDiagnosticInfo() {
17066
+ return [
17067
+ `\u914D\u7F6E\u6587\u4EF6: ${this.omoConfig.getConfigSource() || "\u672A\u627E\u5230"}`,
17068
+ `Providers: ${this.omoConfig.getAllProviders().length}`,
17069
+ `Antigravity Token: ${this.omoConfig.hasAntigravityAuth() ? "\u5DF2\u627E\u5230" : "\u672A\u627E\u5230"}`,
17070
+ `ANTHROPIC_API_KEY: ${process.env["ANTHROPIC_API_KEY"] ? "\u5DF2\u8BBE\u7F6E" : "\u672A\u8BBE\u7F6E"}`,
17071
+ `OPENAI_API_KEY: ${process.env["OPENAI_API_KEY"] ? "\u5DF2\u8BBE\u7F6E" : "\u672A\u8BBE\u7F6E"}`,
17072
+ `GOOGLE_API_KEY: ${process.env["GOOGLE_API_KEY"] ? "\u5DF2\u8BBE\u7F6E" : "\u672A\u8BBE\u7F6E"}`
17073
+ ].join(", ");
16800
17074
  }
16801
17075
  async chatAnthropicFallback(messages, options) {
16802
17076
  if (!this.anthropicClient)
@@ -16813,7 +17087,7 @@ class AxonLLMClient {
16813
17087
  });
16814
17088
  return {
16815
17089
  content: response.content,
16816
- model: options?.model || "claude-3-5-sonnet-20240620",
17090
+ model: options?.model || "claude-sonnet-4-20250514",
16817
17091
  tokens: {
16818
17092
  input: response.usage.input_tokens,
16819
17093
  output: response.usage.output_tokens
@@ -16845,10 +17119,10 @@ class AxonLLMClient {
16845
17119
  }
16846
17120
  }
16847
17121
  var init_llm = __esm(() => {
17122
+ init_errors();
16848
17123
  init_anthropic();
16849
- init_unified_client();
16850
17124
  init_omo_config_reader();
16851
- init_errors();
17125
+ init_unified_client();
16852
17126
  });
16853
17127
 
16854
17128
  // node_modules/underscore/underscore-node-f.cjs
@@ -54081,12 +54355,19 @@ var doctorCommand = new Command("doctor").description(t("Diagnose environment is
54081
54355
  let apiKey = process.env[envVar];
54082
54356
  let source = "environment";
54083
54357
  if (!apiKey) {
54084
- const provider = omoReader.getProvider(name);
54358
+ let provider = omoReader.getProvider(name);
54359
+ if (!provider) {
54360
+ provider = omoReader.getAllProviders().find((p) => p.type === name) || null;
54361
+ }
54085
54362
  if (provider) {
54086
54363
  apiKey = omoReader.getProviderApiKey(provider);
54087
- source = "OMO config (~/.omo/providers.yaml)";
54364
+ source = `OMO config (${omoReader.getConfigSource()})`;
54088
54365
  }
54089
54366
  }
54367
+ if (!apiKey && omoReader.hasAntigravityAuth()) {
54368
+ apiKey = omoReader.getAntigravityToken();
54369
+ source = "Antigravity token (~/.config/opencode/antigravity-accounts.json)";
54370
+ }
54090
54371
  if (apiKey) {
54091
54372
  if (options.checkKeys) {
54092
54373
  spinner.start(`\u9A8C\u8BC1 ${name} API \u5BC6\u94A5 (${source})...`);
@@ -54287,7 +54568,7 @@ configCommand.command("show").description(t("Show current Axon running mode", "\
54287
54568
  console.log(source_default.cyan(" bunx oh-my-opencode install"));
54288
54569
  }
54289
54570
  });
54290
- configCommand.command("test").description(t("Test Provider connection", "\u6D4B\u8BD5 Provider \u8FDE\u63A5")).option("-p, --provider <name>", t("Specify Provider to test", "\u6307\u5B9A Provider \u6D4B\u8BD5")).option("-m, --model <model>", t("Specify model for testing", "\u6307\u5B9A\u6D4B\u8BD5\u4F7F\u7528\u7684\u6A21\u578B")).action(async (options) => {
54571
+ configCommand.command("test").description(t("Test Provider connection", "\u6D4B\u8BD5 Provider \u8FDE\u63A5")).option("-p, --provider <name>", t("Specify Provider to test", "\u6307\u5B9A Provider \u6D4B\u8BD5")).option("-m, --model <model>", t("Specify model for testing", "\u6307\u5B9A\u6D4B\u8BD5\u4F7F\u7528\u7684\u6A21\u578B")).option("--mode <mode>", t("Force specific LLM mode (cli, direct, fallback)", "\u5F3A\u5236\u4F7F\u7528\u7279\u5B9A LLM \u6A21\u5F0F (cli, direct, fallback)")).action(async (options) => {
54291
54572
  const spinner2 = ora("\u6B63\u5728\u521D\u59CB\u5316 LLM \u5BA2\u6237\u7AEF...").start();
54292
54573
  try {
54293
54574
  const omo = new OMOConfigReader;
@@ -54307,8 +54588,9 @@ configCommand.command("test").description(t("Test Provider connection", "\u6D4B\
54307
54588
  }
54308
54589
  }
54309
54590
  const model = options.model || primary?.models?.[0];
54310
- spinner2.text = `\u6D4B\u8BD5\u8FDE\u63A5: ${source_default.cyan(providerName)}${model ? ` (\u6A21\u578B: ${source_default.cyan(model)})` : ""}...`;
54311
- const client = new AxonLLMClient;
54591
+ const mode = options.mode;
54592
+ spinner2.text = `\u6D4B\u8BD5\u8FDE\u63A5: ${source_default.cyan(providerName)}${model ? ` (\u6A21\u578B: ${source_default.cyan(model)})` : ""}${mode ? ` [\u6A21\u5F0F: ${source_default.cyan(mode)}]` : ""}...`;
54593
+ const client = new AxonLLMClient(mode);
54312
54594
  const start = Date.now();
54313
54595
  const response = await client.chat([{ role: "user", content: 'Say "OK" if you can hear me.' }], {
54314
54596
  model,
@@ -54508,7 +54790,8 @@ var VERSION = pkg.version;
54508
54790
  var program2 = new Command;
54509
54791
  program2.name("ax").description(`${source_default.green("\uD83E\uDDE0")} ${source_default.bold("Axon")} - AI-Powered Development Operating System (v${VERSION})
54510
54792
 
54511
- ${source_default.dim("\u4ECE\u9700\u6C42\u5230\u4EE3\u7801\uFF0C\u8BA9 AI \u6210\u4E3A\u4F60\u7684\u5F00\u53D1\u4F19\u4F34\uFF0C\u800C\u975E\u5DE5\u5177\u3002")}`).version(VERSION, "-v, --version", "\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F");
54793
+ From requirements to code, let AI be your development partner, not a tool.
54794
+ \u4ECE\u9700\u6C42\u5230\u4EE3\u7801\uFF0C\u8BA9 AI \u6210\u4E3A\u4F60\u7684\u5F00\u53D1\u4F19\u4F34\uFF0C\u800C\u975E\u5DE5\u5177\u3002`).version(VERSION, "-v, --version", "Show version").helpOption("-h, --help", "Show help information");
54512
54795
  program2.addCommand(initCommand);
54513
54796
  program2.addCommand(specCommand);
54514
54797
  program2.addCommand(planCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrislink/axon",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "AI-Powered Development Operating System with unified LLM provider support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",