opencode-anthropic-fix 0.1.32 → 0.2.1
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 +9 -6
- package/cli.mjs +43 -35
- package/index.mjs +377 -3646
- package/lib/backoff.mjs +1 -1
- package/lib/cc-credentials.mjs +1 -1
- package/lib/config.mjs +2 -2
- package/lib/config.test.mjs +305 -1
- package/lib/diagnose.mjs +184 -0
- package/lib/diagnose.test.mjs +122 -0
- package/lib/env.mjs +19 -0
- package/lib/env.test.mjs +25 -0
- package/lib/mimicry/cache.mjs +62 -0
- package/lib/mimicry/cache.test.mjs +112 -0
- package/lib/mimicry/headers.mjs +528 -0
- package/lib/mimicry/headers.test.mjs +50 -0
- package/lib/mimicry/models.mjs +146 -0
- package/lib/mimicry/models.test.mjs +132 -0
- package/lib/mimicry/request-body.mjs +682 -0
- package/lib/mimicry/request-body.test.mjs +34 -0
- package/lib/mimicry/request-helpers.mjs +243 -0
- package/lib/mimicry/request-helpers.test.mjs +155 -0
- package/lib/mimicry/response-stream.mjs +429 -0
- package/lib/mimicry/response-stream.test.mjs +127 -0
- package/lib/mimicry/system-prompt.mjs +638 -0
- package/lib/mimicry/system-prompt.test.mjs +73 -0
- package/lib/redact.mjs +147 -0
- package/lib/redact.test.mjs +154 -0
- package/lib/refresh-lock.test.mjs +52 -0
- package/lib/retry/overload-loop.mjs +83 -0
- package/lib/retry/overload-loop.test.mjs +107 -0
- package/lib/rotation.mjs +16 -0
- package/lib/rotation.test.mjs +46 -0
- package/lib/session-metrics.mjs +75 -0
- package/lib/session-metrics.test.mjs +67 -0
- package/lib/storage.test.mjs +82 -0
- package/lib/token-economy/microcompact.mjs +35 -0
- package/lib/token-economy/microcompact.test.mjs +28 -0
- package/lib/token-economy/transforms.mjs +637 -0
- package/lib/token-economy/transforms.test.mjs +192 -0
- package/lib/tuning.mjs +70 -0
- package/lib/tuning.test.mjs +89 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -365,11 +365,12 @@ Pending slash OAuth flows expire after 10 minutes. If completion fails with an e
|
|
|
365
365
|
|
|
366
366
|
Control how the plugin picks which account to use for each request.
|
|
367
367
|
|
|
368
|
-
| Strategy | Behavior
|
|
369
|
-
| ---------------------- |
|
|
370
|
-
| **`sticky`** (default) | Stay on one account until it fails or is rate-limited
|
|
371
|
-
| **`round-robin`** | Rotate through accounts on every request
|
|
372
|
-
| **`hybrid`** | Score-based selection with stickiness bias. Considers health, token budget, and freshness
|
|
368
|
+
| Strategy | Behavior | Best For |
|
|
369
|
+
| ---------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
370
|
+
| **`sticky`** (default) | Stay on one account until it fails or is rate-limited | Single account, predictable behavior, full feature compatibility |
|
|
371
|
+
| **`round-robin`** | Rotate through accounts on every request | Spreading load evenly across accounts (see [limitations](#round-robin-limitations)) |
|
|
372
|
+
| **`hybrid`** | Score-based selection with stickiness bias. Considers health, token budget, and freshness | Multiple accounts with varying rate limits |
|
|
373
|
+
| **`single`** | Use one account only; stop and alert when it fails or is rate-limited (never auto-switches) | Simple multi-account setups (e.g. work vs personal) where requests must never mix accounts |
|
|
373
374
|
|
|
374
375
|
### Change Strategy
|
|
375
376
|
|
|
@@ -413,7 +414,7 @@ Configuration is stored at `~/.config/opencode/anthropic-auth.json`. All setting
|
|
|
413
414
|
|
|
414
415
|
```jsonc
|
|
415
416
|
{
|
|
416
|
-
// Account selection strategy: "sticky" | "round-robin" | "hybrid"
|
|
417
|
+
// Account selection strategy: "sticky" | "round-robin" | "hybrid" | "single"
|
|
417
418
|
"account_selection_strategy": "sticky",
|
|
418
419
|
|
|
419
420
|
// Seconds before consecutive failure count resets (60-7200)
|
|
@@ -597,6 +598,8 @@ Account credentials are stored with restrictive file permissions (owner read/wri
|
|
|
597
598
|
|
|
598
599
|
## Troubleshooting
|
|
599
600
|
|
|
601
|
+
Run `opencode-anthropic-auth diagnose` to write a redacted, shareable diagnostic bundle, or add `--stdout` to print it.
|
|
602
|
+
|
|
600
603
|
### "Provider not showing up"
|
|
601
604
|
|
|
602
605
|
Make sure the plugin is installed in `~/.config/opencode/plugin/`. Restart OpenCode after installing.
|
package/cli.mjs
CHANGED
|
@@ -29,13 +29,17 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
import { loadAccounts, saveAccounts, getStoragePath, createDefaultStats } from "./lib/storage.mjs";
|
|
32
|
-
import { loadConfig, saveConfig, getConfigPath, VALID_STRATEGIES
|
|
33
|
-
import { authorize, exchange, revoke } from "./lib/oauth.mjs";
|
|
32
|
+
import { loadConfig, saveConfig, getConfigPath, VALID_STRATEGIES } from "./lib/config.mjs";
|
|
33
|
+
import { authorize, exchange, refreshToken, revoke } from "./lib/oauth.mjs";
|
|
34
|
+
import { adjustActiveIndexAfterRemoval } from "./lib/account-state.mjs";
|
|
34
35
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
36
|
+
import { writeFile } from "node:fs/promises";
|
|
37
|
+
import path from "node:path";
|
|
35
38
|
import { pathToFileURL } from "node:url";
|
|
36
39
|
import { exec } from "node:child_process";
|
|
37
40
|
import { createInterface } from "node:readline/promises";
|
|
38
41
|
import { stdin, stdout } from "node:process";
|
|
42
|
+
import { buildDiagnosticBundle } from "./lib/diagnose.mjs";
|
|
39
43
|
|
|
40
44
|
// ---------------------------------------------------------------------------
|
|
41
45
|
// Color helpers — zero dependencies, respects NO_COLOR / TTY
|
|
@@ -146,18 +150,7 @@ function rpad(str, width) {
|
|
|
146
150
|
*/
|
|
147
151
|
export async function refreshAccessToken(account) {
|
|
148
152
|
try {
|
|
149
|
-
const
|
|
150
|
-
method: "POST",
|
|
151
|
-
headers: { "Content-Type": "application/json" },
|
|
152
|
-
body: JSON.stringify({
|
|
153
|
-
grant_type: "refresh_token",
|
|
154
|
-
refresh_token: account.refreshToken,
|
|
155
|
-
client_id: CLIENT_ID,
|
|
156
|
-
}),
|
|
157
|
-
signal: AbortSignal.timeout(5000),
|
|
158
|
-
});
|
|
159
|
-
if (!resp.ok) return null;
|
|
160
|
-
const json = await resp.json();
|
|
153
|
+
const json = await refreshToken(account.refreshToken, { signal: AbortSignal.timeout(5000) });
|
|
161
154
|
account.access = json.access_token;
|
|
162
155
|
account.expires = Date.now() + json.expires_in * 1000;
|
|
163
156
|
if (json.refresh_token) account.refreshToken = json.refresh_token;
|
|
@@ -483,13 +476,7 @@ export async function cmdLogout(arg, opts = {}) {
|
|
|
483
476
|
stored.accounts.splice(idx, 1);
|
|
484
477
|
|
|
485
478
|
// Adjust active index
|
|
486
|
-
|
|
487
|
-
stored.activeIndex = 0;
|
|
488
|
-
} else if (stored.activeIndex >= stored.accounts.length) {
|
|
489
|
-
stored.activeIndex = stored.accounts.length - 1;
|
|
490
|
-
} else if (stored.activeIndex > idx) {
|
|
491
|
-
stored.activeIndex--;
|
|
492
|
-
}
|
|
479
|
+
adjustActiveIndexAfterRemoval(stored, idx);
|
|
493
480
|
|
|
494
481
|
await saveAccounts(stored);
|
|
495
482
|
console.log(c.green(`Logged out account #${n} (${label}).`));
|
|
@@ -1019,13 +1006,7 @@ export async function cmdRemove(arg, opts = {}) {
|
|
|
1019
1006
|
stored.accounts.splice(idx, 1);
|
|
1020
1007
|
|
|
1021
1008
|
// Adjust active index
|
|
1022
|
-
|
|
1023
|
-
stored.activeIndex = 0;
|
|
1024
|
-
} else if (stored.activeIndex >= stored.accounts.length) {
|
|
1025
|
-
stored.activeIndex = stored.accounts.length - 1;
|
|
1026
|
-
} else if (stored.activeIndex > idx) {
|
|
1027
|
-
stored.activeIndex--;
|
|
1028
|
-
}
|
|
1009
|
+
adjustActiveIndexAfterRemoval(stored, idx);
|
|
1029
1010
|
|
|
1030
1011
|
await saveAccounts(stored);
|
|
1031
1012
|
console.log(c.green(`Removed account #${n} (${label}).`));
|
|
@@ -1172,6 +1153,7 @@ export async function cmdStrategy(arg) {
|
|
|
1172
1153
|
sticky: "Stay on one account until it fails or is rate-limited",
|
|
1173
1154
|
"round-robin": "Rotate through accounts on every request",
|
|
1174
1155
|
hybrid: "Prefer healthy accounts, rotate when degraded",
|
|
1156
|
+
single: "Use one account only; stop and alert when it fails or is rate-limited",
|
|
1175
1157
|
};
|
|
1176
1158
|
|
|
1177
1159
|
for (const s of VALID_STRATEGIES) {
|
|
@@ -1514,13 +1496,7 @@ export async function cmdManage() {
|
|
|
1514
1496
|
if (confirm.trim().toLowerCase() === "y") {
|
|
1515
1497
|
stored.accounts.splice(idx, 1);
|
|
1516
1498
|
// Adjust active index
|
|
1517
|
-
|
|
1518
|
-
stored.activeIndex = 0;
|
|
1519
|
-
} else if (stored.activeIndex >= stored.accounts.length) {
|
|
1520
|
-
stored.activeIndex = stored.accounts.length - 1;
|
|
1521
|
-
} else if (stored.activeIndex > idx) {
|
|
1522
|
-
stored.activeIndex--;
|
|
1523
|
-
}
|
|
1499
|
+
adjustActiveIndexAfterRemoval(stored, idx);
|
|
1524
1500
|
await saveAccounts(stored);
|
|
1525
1501
|
console.log(c.green(`Removed account #${num}.`));
|
|
1526
1502
|
} else {
|
|
@@ -1557,6 +1533,34 @@ export async function cmdManage() {
|
|
|
1557
1533
|
return 0;
|
|
1558
1534
|
}
|
|
1559
1535
|
|
|
1536
|
+
/**
|
|
1537
|
+
* Write or print a redacted diagnostic bundle.
|
|
1538
|
+
*
|
|
1539
|
+
* @param {string | undefined} _arg
|
|
1540
|
+
* @param {{ stdout?: boolean }} [opts]
|
|
1541
|
+
* @returns {Promise<number>} exit code
|
|
1542
|
+
*/
|
|
1543
|
+
export async function cmdDiagnose(_arg, opts = {}) {
|
|
1544
|
+
try {
|
|
1545
|
+
const bundle = await buildDiagnosticBundle();
|
|
1546
|
+
const json = JSON.stringify(bundle, null, 2);
|
|
1547
|
+
if (opts.stdout) {
|
|
1548
|
+
console.log(json);
|
|
1549
|
+
return 0;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1553
|
+
const outputPath = path.resolve(process.cwd(), `opencode-anthropic-diagnose-${timestamp}.json`);
|
|
1554
|
+
await writeFile(outputPath, `${json}\n`, "utf8");
|
|
1555
|
+
console.log(outputPath);
|
|
1556
|
+
return 0;
|
|
1557
|
+
} catch (error) {
|
|
1558
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1559
|
+
console.error(c.red(`Failed to create diagnostic bundle: ${message}`));
|
|
1560
|
+
return 1;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1560
1564
|
/**
|
|
1561
1565
|
* Show help text.
|
|
1562
1566
|
*/
|
|
@@ -1588,6 +1592,7 @@ ${c.dim("Account Commands:")}
|
|
|
1588
1592
|
${pad(c.cyan("strategy") + " [name]", 22)}Show or change selection strategy
|
|
1589
1593
|
${pad(c.cyan("config"), 22)}Show configuration and file paths
|
|
1590
1594
|
${pad(c.cyan("manage"), 22)}Interactive account management menu
|
|
1595
|
+
${pad(c.cyan("diagnose"), 22)}Write a redacted diagnostic bundle ${c.dim("(dg; --stdout to print)")}
|
|
1591
1596
|
${pad(c.cyan("help"), 22)}Show this help message
|
|
1592
1597
|
|
|
1593
1598
|
${c.dim("Options:")}
|
|
@@ -1732,6 +1737,9 @@ async function dispatch(argv) {
|
|
|
1732
1737
|
case "manage":
|
|
1733
1738
|
case "mg":
|
|
1734
1739
|
return cmdManage();
|
|
1740
|
+
case "diagnose":
|
|
1741
|
+
case "dg":
|
|
1742
|
+
return cmdDiagnose(arg, { stdout: flags.includes("--stdout") });
|
|
1735
1743
|
case "help":
|
|
1736
1744
|
case "-h":
|
|
1737
1745
|
case "--help":
|