@albinocrabs/o-switcher 0.2.0 → 0.3.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/CHANGELOG.md +9 -0
- package/dist/{chunk-TJ7ZGZHD.cjs → chunk-H72U2MNG.cjs} +116 -13
- package/dist/{chunk-7ITX5623.js → chunk-XXH633FY.js} +115 -14
- package/dist/index.cjs +79 -71
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +2 -2
- package/dist/plugin.cjs +47 -22
- package/dist/plugin.js +30 -5
- package/package.json +1 -1
- package/src/tui.tsx +16 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Multi-profile auth rotation for OpenAI accounts
|
|
8
|
+
- Watch all `auth-work*.json` files, not just `auth.json`
|
|
9
|
+
- Add `switchProfile` / `switchToNextProfile` for credential rotation
|
|
10
|
+
- Auto-switch to next healthy profile when health drops below 30%
|
|
11
|
+
|
|
3
12
|
## 0.2.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
|
@@ -1502,6 +1502,8 @@ var nextProfileId = (store, provider) => {
|
|
|
1502
1502
|
return `${provider}-${maxN + 1}`;
|
|
1503
1503
|
};
|
|
1504
1504
|
var AUTH_JSON_PATH = path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
|
|
1505
|
+
var AUTH_DIR = path.join(os.homedir(), ".local", "share", "opencode");
|
|
1506
|
+
var isAuthFile = (filename) => filename === "auth.json" || /^auth-work\d+\.json$/.test(filename);
|
|
1505
1507
|
var DEBOUNCE_MS = 100;
|
|
1506
1508
|
var readAuthJson = async (path) => {
|
|
1507
1509
|
try {
|
|
@@ -1511,6 +1513,22 @@ var readAuthJson = async (path) => {
|
|
|
1511
1513
|
return {};
|
|
1512
1514
|
}
|
|
1513
1515
|
};
|
|
1516
|
+
var readAllAuthFiles = async (dir) => {
|
|
1517
|
+
const { readdir } = await import('fs/promises');
|
|
1518
|
+
const merged = {};
|
|
1519
|
+
try {
|
|
1520
|
+
const files = await readdir(dir);
|
|
1521
|
+
for (const file of files.filter(isAuthFile)) {
|
|
1522
|
+
const data = await readAuthJson(path.join(dir, file));
|
|
1523
|
+
for (const [provider, entry] of Object.entries(data)) {
|
|
1524
|
+
const key = `${provider}:${file}`;
|
|
1525
|
+
merged[key] = entry;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
} catch {
|
|
1529
|
+
}
|
|
1530
|
+
return merged;
|
|
1531
|
+
};
|
|
1514
1532
|
var toCredential = (entry) => {
|
|
1515
1533
|
if (entry.type === "oauth" || entry.refresh !== void 0 && entry.access !== void 0) {
|
|
1516
1534
|
return {
|
|
@@ -1532,22 +1550,23 @@ var entriesEqual = (a, b) => {
|
|
|
1532
1550
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
1533
1551
|
};
|
|
1534
1552
|
var createAuthWatcher = (options) => {
|
|
1535
|
-
const
|
|
1553
|
+
const authDir = options?.authJsonPath ? path.dirname(options.authJsonPath) : AUTH_DIR;
|
|
1536
1554
|
const profPath = options?.profilesPath ?? PROFILES_PATH;
|
|
1537
1555
|
const log = options?.logger?.child({ component: "profile-watcher" });
|
|
1538
1556
|
let lastKnownAuth = {};
|
|
1539
1557
|
let abortController = null;
|
|
1540
1558
|
let debounceTimer = null;
|
|
1541
1559
|
const processChange = async () => {
|
|
1542
|
-
const newAuth = await
|
|
1560
|
+
const newAuth = await readAllAuthFiles(authDir);
|
|
1543
1561
|
let store = await loadProfiles(profPath);
|
|
1544
1562
|
let changed = false;
|
|
1545
|
-
for (const [
|
|
1563
|
+
for (const [compositeKey, entry] of Object.entries(newAuth)) {
|
|
1546
1564
|
if (!entry) continue;
|
|
1547
|
-
const
|
|
1565
|
+
const provider = compositeKey.split(":")[0] ?? compositeKey;
|
|
1566
|
+
const previousEntry = lastKnownAuth[compositeKey];
|
|
1548
1567
|
if (previousEntry !== void 0 && !entriesEqual(previousEntry, entry)) {
|
|
1549
1568
|
log?.info(
|
|
1550
|
-
{ provider, action: "credential_changed" },
|
|
1569
|
+
{ provider, source: compositeKey, action: "credential_changed" },
|
|
1551
1570
|
"Credential change detected \u2014 saving previous credential"
|
|
1552
1571
|
);
|
|
1553
1572
|
const prevCredential = toCredential(previousEntry);
|
|
@@ -1557,7 +1576,7 @@ var createAuthWatcher = (options) => {
|
|
|
1557
1576
|
changed = true;
|
|
1558
1577
|
}
|
|
1559
1578
|
} else if (previousEntry === void 0) {
|
|
1560
|
-
log?.info({ provider, action: "new_provider" }, "New
|
|
1579
|
+
log?.info({ provider, source: compositeKey, action: "new_provider" }, "New auth file detected");
|
|
1561
1580
|
const credential = toCredential(entry);
|
|
1562
1581
|
const newStore = addProfile(store, provider, credential);
|
|
1563
1582
|
if (newStore !== store) {
|
|
@@ -1584,32 +1603,32 @@ var createAuthWatcher = (options) => {
|
|
|
1584
1603
|
}, DEBOUNCE_MS);
|
|
1585
1604
|
};
|
|
1586
1605
|
const start = async () => {
|
|
1587
|
-
const currentAuth = await
|
|
1606
|
+
const currentAuth = await readAllAuthFiles(authDir);
|
|
1588
1607
|
const currentStore = await loadProfiles(profPath);
|
|
1589
|
-
log?.info({
|
|
1608
|
+
log?.info({ auth_keys: Object.keys(currentAuth).length }, "Auth watcher started");
|
|
1590
1609
|
if (Object.keys(currentStore).length === 0 && Object.keys(currentAuth).length > 0) {
|
|
1591
1610
|
let store = {};
|
|
1592
|
-
for (const [
|
|
1611
|
+
for (const [compositeKey, entry] of Object.entries(currentAuth)) {
|
|
1593
1612
|
if (!entry) continue;
|
|
1613
|
+
const provider = compositeKey.split(":")[0] ?? compositeKey;
|
|
1594
1614
|
const credential = toCredential(entry);
|
|
1595
1615
|
store = addProfile(store, provider, credential);
|
|
1596
1616
|
}
|
|
1597
1617
|
await saveProfiles(store, profPath);
|
|
1598
1618
|
log?.info(
|
|
1599
1619
|
{ profiles_initialized: Object.keys(store).length },
|
|
1600
|
-
"Initialized profiles from auth
|
|
1620
|
+
"Initialized profiles from auth files"
|
|
1601
1621
|
);
|
|
1602
1622
|
}
|
|
1603
1623
|
lastKnownAuth = currentAuth;
|
|
1604
1624
|
abortController = new AbortController();
|
|
1605
|
-
const parentDir = path.dirname(authPath);
|
|
1606
1625
|
(async () => {
|
|
1607
1626
|
try {
|
|
1608
|
-
const watcher = promises.watch(
|
|
1627
|
+
const watcher = promises.watch(authDir, {
|
|
1609
1628
|
signal: abortController.signal
|
|
1610
1629
|
});
|
|
1611
1630
|
for await (const event of watcher) {
|
|
1612
|
-
if (event.filename
|
|
1631
|
+
if (event.filename !== null && isAuthFile(event.filename)) {
|
|
1613
1632
|
onFileChange();
|
|
1614
1633
|
}
|
|
1615
1634
|
}
|
|
@@ -1631,6 +1650,88 @@ var createAuthWatcher = (options) => {
|
|
|
1631
1650
|
};
|
|
1632
1651
|
return { start, stop };
|
|
1633
1652
|
};
|
|
1653
|
+
var credentialToAuthEntry = (cred) => {
|
|
1654
|
+
if (cred.type === "oauth") {
|
|
1655
|
+
return {
|
|
1656
|
+
type: "oauth",
|
|
1657
|
+
refresh: cred.refresh,
|
|
1658
|
+
access: cred.access,
|
|
1659
|
+
expires: cred.expires,
|
|
1660
|
+
accountId: cred.accountId
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
return {
|
|
1664
|
+
type: "api-key",
|
|
1665
|
+
key: cred.key
|
|
1666
|
+
};
|
|
1667
|
+
};
|
|
1668
|
+
var readCurrentAuth = async (authPath) => {
|
|
1669
|
+
try {
|
|
1670
|
+
const content = await promises.readFile(authPath, "utf-8");
|
|
1671
|
+
return JSON.parse(content);
|
|
1672
|
+
} catch {
|
|
1673
|
+
return void 0;
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
var switchProfile = async (options) => {
|
|
1677
|
+
const profPath = options.profilesPath ?? PROFILES_PATH;
|
|
1678
|
+
const authPath = options.authJsonPath ?? AUTH_JSON_PATH;
|
|
1679
|
+
const log = options.logger?.child({ component: "profile-switcher" });
|
|
1680
|
+
const store = await loadProfiles(profPath);
|
|
1681
|
+
const target = store[options.targetProfileId];
|
|
1682
|
+
if (target === void 0) {
|
|
1683
|
+
log?.warn({ profileId: options.targetProfileId }, "Profile not found");
|
|
1684
|
+
return { success: false, to: options.targetProfileId, reason: "profile_not_found" };
|
|
1685
|
+
}
|
|
1686
|
+
const currentAuth = await readCurrentAuth(authPath);
|
|
1687
|
+
const currentAccountId = currentAuth?.[target.provider] ? currentAuth[target.provider].accountId : void 0;
|
|
1688
|
+
const newAuth = {};
|
|
1689
|
+
newAuth[target.provider] = credentialToAuthEntry(target.credentials);
|
|
1690
|
+
const dir = path.dirname(authPath);
|
|
1691
|
+
await promises.mkdir(dir, { recursive: true });
|
|
1692
|
+
const tmpPath = `${authPath}.tmp`;
|
|
1693
|
+
await promises.writeFile(tmpPath, JSON.stringify(newAuth, null, 2), "utf-8");
|
|
1694
|
+
await promises.rename(tmpPath, authPath);
|
|
1695
|
+
log?.info(
|
|
1696
|
+
{
|
|
1697
|
+
from: currentAccountId,
|
|
1698
|
+
to: target.credentials.type === "oauth" ? target.credentials.accountId : "(api-key)",
|
|
1699
|
+
profileId: target.id,
|
|
1700
|
+
provider: target.provider
|
|
1701
|
+
},
|
|
1702
|
+
"Switched active profile"
|
|
1703
|
+
);
|
|
1704
|
+
return {
|
|
1705
|
+
success: true,
|
|
1706
|
+
from: currentAccountId ?? void 0,
|
|
1707
|
+
to: target.id
|
|
1708
|
+
};
|
|
1709
|
+
};
|
|
1710
|
+
var switchToNextProfile = async (options) => {
|
|
1711
|
+
const profPath = options.profilesPath ?? PROFILES_PATH;
|
|
1712
|
+
const log = options.logger?.child({ component: "profile-switcher" });
|
|
1713
|
+
const store = await loadProfiles(profPath);
|
|
1714
|
+
const excludeSet = new Set(options.excludeProfileIds ?? []);
|
|
1715
|
+
if (options.currentProfileId) {
|
|
1716
|
+
excludeSet.add(options.currentProfileId);
|
|
1717
|
+
}
|
|
1718
|
+
const candidates = Object.values(store).filter((p) => p.provider === options.provider && !excludeSet.has(p.id)).sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
|
|
1719
|
+
if (candidates.length === 0) {
|
|
1720
|
+
log?.warn({ provider: options.provider, excluded: [...excludeSet] }, "No available profiles to switch to");
|
|
1721
|
+
return {
|
|
1722
|
+
success: false,
|
|
1723
|
+
to: options.currentProfileId ?? "none",
|
|
1724
|
+
reason: "no_candidates"
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
const next = candidates[0];
|
|
1728
|
+
return switchProfile({
|
|
1729
|
+
targetProfileId: next.id,
|
|
1730
|
+
profilesPath: profPath,
|
|
1731
|
+
authJsonPath: options.authJsonPath,
|
|
1732
|
+
logger: options.logger
|
|
1733
|
+
});
|
|
1734
|
+
};
|
|
1634
1735
|
var { schema: z4 } = tool.tool;
|
|
1635
1736
|
var createProfileTools = (options) => ({
|
|
1636
1737
|
profilesList: tool.tool({
|
|
@@ -1731,6 +1832,8 @@ exports.removeProfile = removeProfile;
|
|
|
1731
1832
|
exports.resumeTarget = resumeTarget;
|
|
1732
1833
|
exports.saveProfiles = saveProfiles;
|
|
1733
1834
|
exports.selectTarget = selectTarget;
|
|
1835
|
+
exports.switchProfile = switchProfile;
|
|
1836
|
+
exports.switchToNextProfile = switchToNextProfile;
|
|
1734
1837
|
exports.updateHealthScore = updateHealthScore;
|
|
1735
1838
|
exports.updateLatencyEma = updateLatencyEma;
|
|
1736
1839
|
exports.validateConfig = validateConfig;
|
|
@@ -1494,6 +1494,8 @@ var nextProfileId = (store, provider) => {
|
|
|
1494
1494
|
return `${provider}-${maxN + 1}`;
|
|
1495
1495
|
};
|
|
1496
1496
|
var AUTH_JSON_PATH = join(homedir(), ".local", "share", "opencode", "auth.json");
|
|
1497
|
+
var AUTH_DIR = join(homedir(), ".local", "share", "opencode");
|
|
1498
|
+
var isAuthFile = (filename) => filename === "auth.json" || /^auth-work\d+\.json$/.test(filename);
|
|
1497
1499
|
var DEBOUNCE_MS = 100;
|
|
1498
1500
|
var readAuthJson = async (path) => {
|
|
1499
1501
|
try {
|
|
@@ -1503,6 +1505,22 @@ var readAuthJson = async (path) => {
|
|
|
1503
1505
|
return {};
|
|
1504
1506
|
}
|
|
1505
1507
|
};
|
|
1508
|
+
var readAllAuthFiles = async (dir) => {
|
|
1509
|
+
const { readdir } = await import('fs/promises');
|
|
1510
|
+
const merged = {};
|
|
1511
|
+
try {
|
|
1512
|
+
const files = await readdir(dir);
|
|
1513
|
+
for (const file of files.filter(isAuthFile)) {
|
|
1514
|
+
const data = await readAuthJson(join(dir, file));
|
|
1515
|
+
for (const [provider, entry] of Object.entries(data)) {
|
|
1516
|
+
const key = `${provider}:${file}`;
|
|
1517
|
+
merged[key] = entry;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
} catch {
|
|
1521
|
+
}
|
|
1522
|
+
return merged;
|
|
1523
|
+
};
|
|
1506
1524
|
var toCredential = (entry) => {
|
|
1507
1525
|
if (entry.type === "oauth" || entry.refresh !== void 0 && entry.access !== void 0) {
|
|
1508
1526
|
return {
|
|
@@ -1524,22 +1542,23 @@ var entriesEqual = (a, b) => {
|
|
|
1524
1542
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
1525
1543
|
};
|
|
1526
1544
|
var createAuthWatcher = (options) => {
|
|
1527
|
-
const
|
|
1545
|
+
const authDir = options?.authJsonPath ? dirname(options.authJsonPath) : AUTH_DIR;
|
|
1528
1546
|
const profPath = options?.profilesPath ?? PROFILES_PATH;
|
|
1529
1547
|
const log = options?.logger?.child({ component: "profile-watcher" });
|
|
1530
1548
|
let lastKnownAuth = {};
|
|
1531
1549
|
let abortController = null;
|
|
1532
1550
|
let debounceTimer = null;
|
|
1533
1551
|
const processChange = async () => {
|
|
1534
|
-
const newAuth = await
|
|
1552
|
+
const newAuth = await readAllAuthFiles(authDir);
|
|
1535
1553
|
let store = await loadProfiles(profPath);
|
|
1536
1554
|
let changed = false;
|
|
1537
|
-
for (const [
|
|
1555
|
+
for (const [compositeKey, entry] of Object.entries(newAuth)) {
|
|
1538
1556
|
if (!entry) continue;
|
|
1539
|
-
const
|
|
1557
|
+
const provider = compositeKey.split(":")[0] ?? compositeKey;
|
|
1558
|
+
const previousEntry = lastKnownAuth[compositeKey];
|
|
1540
1559
|
if (previousEntry !== void 0 && !entriesEqual(previousEntry, entry)) {
|
|
1541
1560
|
log?.info(
|
|
1542
|
-
{ provider, action: "credential_changed" },
|
|
1561
|
+
{ provider, source: compositeKey, action: "credential_changed" },
|
|
1543
1562
|
"Credential change detected \u2014 saving previous credential"
|
|
1544
1563
|
);
|
|
1545
1564
|
const prevCredential = toCredential(previousEntry);
|
|
@@ -1549,7 +1568,7 @@ var createAuthWatcher = (options) => {
|
|
|
1549
1568
|
changed = true;
|
|
1550
1569
|
}
|
|
1551
1570
|
} else if (previousEntry === void 0) {
|
|
1552
|
-
log?.info({ provider, action: "new_provider" }, "New
|
|
1571
|
+
log?.info({ provider, source: compositeKey, action: "new_provider" }, "New auth file detected");
|
|
1553
1572
|
const credential = toCredential(entry);
|
|
1554
1573
|
const newStore = addProfile(store, provider, credential);
|
|
1555
1574
|
if (newStore !== store) {
|
|
@@ -1576,32 +1595,32 @@ var createAuthWatcher = (options) => {
|
|
|
1576
1595
|
}, DEBOUNCE_MS);
|
|
1577
1596
|
};
|
|
1578
1597
|
const start = async () => {
|
|
1579
|
-
const currentAuth = await
|
|
1598
|
+
const currentAuth = await readAllAuthFiles(authDir);
|
|
1580
1599
|
const currentStore = await loadProfiles(profPath);
|
|
1581
|
-
log?.info({
|
|
1600
|
+
log?.info({ auth_keys: Object.keys(currentAuth).length }, "Auth watcher started");
|
|
1582
1601
|
if (Object.keys(currentStore).length === 0 && Object.keys(currentAuth).length > 0) {
|
|
1583
1602
|
let store = {};
|
|
1584
|
-
for (const [
|
|
1603
|
+
for (const [compositeKey, entry] of Object.entries(currentAuth)) {
|
|
1585
1604
|
if (!entry) continue;
|
|
1605
|
+
const provider = compositeKey.split(":")[0] ?? compositeKey;
|
|
1586
1606
|
const credential = toCredential(entry);
|
|
1587
1607
|
store = addProfile(store, provider, credential);
|
|
1588
1608
|
}
|
|
1589
1609
|
await saveProfiles(store, profPath);
|
|
1590
1610
|
log?.info(
|
|
1591
1611
|
{ profiles_initialized: Object.keys(store).length },
|
|
1592
|
-
"Initialized profiles from auth
|
|
1612
|
+
"Initialized profiles from auth files"
|
|
1593
1613
|
);
|
|
1594
1614
|
}
|
|
1595
1615
|
lastKnownAuth = currentAuth;
|
|
1596
1616
|
abortController = new AbortController();
|
|
1597
|
-
const parentDir = dirname(authPath);
|
|
1598
1617
|
(async () => {
|
|
1599
1618
|
try {
|
|
1600
|
-
const watcher = watch(
|
|
1619
|
+
const watcher = watch(authDir, {
|
|
1601
1620
|
signal: abortController.signal
|
|
1602
1621
|
});
|
|
1603
1622
|
for await (const event of watcher) {
|
|
1604
|
-
if (event.filename
|
|
1623
|
+
if (event.filename !== null && isAuthFile(event.filename)) {
|
|
1605
1624
|
onFileChange();
|
|
1606
1625
|
}
|
|
1607
1626
|
}
|
|
@@ -1623,6 +1642,88 @@ var createAuthWatcher = (options) => {
|
|
|
1623
1642
|
};
|
|
1624
1643
|
return { start, stop };
|
|
1625
1644
|
};
|
|
1645
|
+
var credentialToAuthEntry = (cred) => {
|
|
1646
|
+
if (cred.type === "oauth") {
|
|
1647
|
+
return {
|
|
1648
|
+
type: "oauth",
|
|
1649
|
+
refresh: cred.refresh,
|
|
1650
|
+
access: cred.access,
|
|
1651
|
+
expires: cred.expires,
|
|
1652
|
+
accountId: cred.accountId
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
return {
|
|
1656
|
+
type: "api-key",
|
|
1657
|
+
key: cred.key
|
|
1658
|
+
};
|
|
1659
|
+
};
|
|
1660
|
+
var readCurrentAuth = async (authPath) => {
|
|
1661
|
+
try {
|
|
1662
|
+
const content = await readFile(authPath, "utf-8");
|
|
1663
|
+
return JSON.parse(content);
|
|
1664
|
+
} catch {
|
|
1665
|
+
return void 0;
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
var switchProfile = async (options) => {
|
|
1669
|
+
const profPath = options.profilesPath ?? PROFILES_PATH;
|
|
1670
|
+
const authPath = options.authJsonPath ?? AUTH_JSON_PATH;
|
|
1671
|
+
const log = options.logger?.child({ component: "profile-switcher" });
|
|
1672
|
+
const store = await loadProfiles(profPath);
|
|
1673
|
+
const target = store[options.targetProfileId];
|
|
1674
|
+
if (target === void 0) {
|
|
1675
|
+
log?.warn({ profileId: options.targetProfileId }, "Profile not found");
|
|
1676
|
+
return { success: false, to: options.targetProfileId, reason: "profile_not_found" };
|
|
1677
|
+
}
|
|
1678
|
+
const currentAuth = await readCurrentAuth(authPath);
|
|
1679
|
+
const currentAccountId = currentAuth?.[target.provider] ? currentAuth[target.provider].accountId : void 0;
|
|
1680
|
+
const newAuth = {};
|
|
1681
|
+
newAuth[target.provider] = credentialToAuthEntry(target.credentials);
|
|
1682
|
+
const dir = dirname(authPath);
|
|
1683
|
+
await mkdir(dir, { recursive: true });
|
|
1684
|
+
const tmpPath = `${authPath}.tmp`;
|
|
1685
|
+
await writeFile(tmpPath, JSON.stringify(newAuth, null, 2), "utf-8");
|
|
1686
|
+
await rename(tmpPath, authPath);
|
|
1687
|
+
log?.info(
|
|
1688
|
+
{
|
|
1689
|
+
from: currentAccountId,
|
|
1690
|
+
to: target.credentials.type === "oauth" ? target.credentials.accountId : "(api-key)",
|
|
1691
|
+
profileId: target.id,
|
|
1692
|
+
provider: target.provider
|
|
1693
|
+
},
|
|
1694
|
+
"Switched active profile"
|
|
1695
|
+
);
|
|
1696
|
+
return {
|
|
1697
|
+
success: true,
|
|
1698
|
+
from: currentAccountId ?? void 0,
|
|
1699
|
+
to: target.id
|
|
1700
|
+
};
|
|
1701
|
+
};
|
|
1702
|
+
var switchToNextProfile = async (options) => {
|
|
1703
|
+
const profPath = options.profilesPath ?? PROFILES_PATH;
|
|
1704
|
+
const log = options.logger?.child({ component: "profile-switcher" });
|
|
1705
|
+
const store = await loadProfiles(profPath);
|
|
1706
|
+
const excludeSet = new Set(options.excludeProfileIds ?? []);
|
|
1707
|
+
if (options.currentProfileId) {
|
|
1708
|
+
excludeSet.add(options.currentProfileId);
|
|
1709
|
+
}
|
|
1710
|
+
const candidates = Object.values(store).filter((p) => p.provider === options.provider && !excludeSet.has(p.id)).sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
|
|
1711
|
+
if (candidates.length === 0) {
|
|
1712
|
+
log?.warn({ provider: options.provider, excluded: [...excludeSet] }, "No available profiles to switch to");
|
|
1713
|
+
return {
|
|
1714
|
+
success: false,
|
|
1715
|
+
to: options.currentProfileId ?? "none",
|
|
1716
|
+
reason: "no_candidates"
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
const next = candidates[0];
|
|
1720
|
+
return switchProfile({
|
|
1721
|
+
targetProfileId: next.id,
|
|
1722
|
+
profilesPath: profPath,
|
|
1723
|
+
authJsonPath: options.authJsonPath,
|
|
1724
|
+
logger: options.logger
|
|
1725
|
+
});
|
|
1726
|
+
};
|
|
1626
1727
|
var { schema: z4 } = tool;
|
|
1627
1728
|
var createProfileTools = (options) => ({
|
|
1628
1729
|
profilesList: tool({
|
|
@@ -1659,4 +1760,4 @@ var createProfileTools = (options) => ({
|
|
|
1659
1760
|
})
|
|
1660
1761
|
});
|
|
1661
1762
|
|
|
1662
|
-
export { ADMISSION_RESULTS, BackoffConfigSchema, ConfigValidationError, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, DualBreaker, EXCLUSION_REASONS, ErrorClassSchema, INITIAL_HEALTH_SCORE, REDACT_PATHS, SwitcherConfigSchema, TARGET_STATES, TargetConfigSchema, TargetRegistry, addProfile, applyConfigDiff, checkHardRejects, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createFailoverOrchestrator, createLogSubscriber, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, generateCorrelationId, getExclusionReason, getTargetStateTransition, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, updateHealthScore, updateLatencyEma, validateConfig };
|
|
1763
|
+
export { ADMISSION_RESULTS, BackoffConfigSchema, ConfigValidationError, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, DualBreaker, EXCLUSION_REASONS, ErrorClassSchema, INITIAL_HEALTH_SCORE, REDACT_PATHS, SwitcherConfigSchema, TARGET_STATES, TargetConfigSchema, TargetRegistry, addProfile, applyConfigDiff, checkHardRejects, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createFailoverOrchestrator, createLogSubscriber, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, generateCorrelationId, getExclusionReason, getTargetStateTransition, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, switchProfile, switchToNextProfile, updateHealthScore, updateLatencyEma, validateConfig };
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkH72U2MNG_cjs = require('./chunk-H72U2MNG.cjs');
|
|
4
4
|
var crypto = require('crypto');
|
|
5
5
|
|
|
6
6
|
// src/mode/detection.ts
|
|
@@ -631,7 +631,7 @@ var createStreamStitcher = (_requestId) => {
|
|
|
631
631
|
|
|
632
632
|
// src/execution/audit-collector.ts
|
|
633
633
|
var createAuditCollector = (logger, requestId) => {
|
|
634
|
-
const requestLogger =
|
|
634
|
+
const requestLogger = chunkH72U2MNG_cjs.createRequestLogger(logger, requestId);
|
|
635
635
|
const attempts = [];
|
|
636
636
|
const segments = [];
|
|
637
637
|
return {
|
|
@@ -661,7 +661,7 @@ var createAuditCollector = (logger, requestId) => {
|
|
|
661
661
|
// src/execution/orchestrator.ts
|
|
662
662
|
var createExecutionOrchestrator = (deps) => ({
|
|
663
663
|
async execute(request) {
|
|
664
|
-
const requestId = request.request_id ||
|
|
664
|
+
const requestId = request.request_id || chunkH72U2MNG_cjs.generateCorrelationId();
|
|
665
665
|
const auditCollector = createAuditCollector(deps.logger, requestId);
|
|
666
666
|
const stitcher = createStreamStitcher();
|
|
667
667
|
const startMs = Date.now();
|
|
@@ -760,7 +760,7 @@ var createExecutionOrchestrator = (deps) => ({
|
|
|
760
760
|
};
|
|
761
761
|
} finally {
|
|
762
762
|
auditCollector.flush(outcome, finalTarget);
|
|
763
|
-
const requestLogger =
|
|
763
|
+
const requestLogger = chunkH72U2MNG_cjs.createRequestLogger(deps.logger, requestId);
|
|
764
764
|
const endMs = Date.now();
|
|
765
765
|
requestLogger.info(
|
|
766
766
|
{
|
|
@@ -891,271 +891,279 @@ var validateBearerToken = (authHeader, expectedToken) => {
|
|
|
891
891
|
|
|
892
892
|
Object.defineProperty(exports, "ADMISSION_RESULTS", {
|
|
893
893
|
enumerable: true,
|
|
894
|
-
get: function () { return
|
|
894
|
+
get: function () { return chunkH72U2MNG_cjs.ADMISSION_RESULTS; }
|
|
895
895
|
});
|
|
896
896
|
Object.defineProperty(exports, "BackoffConfigSchema", {
|
|
897
897
|
enumerable: true,
|
|
898
|
-
get: function () { return
|
|
898
|
+
get: function () { return chunkH72U2MNG_cjs.BackoffConfigSchema; }
|
|
899
899
|
});
|
|
900
900
|
Object.defineProperty(exports, "ConfigValidationError", {
|
|
901
901
|
enumerable: true,
|
|
902
|
-
get: function () { return
|
|
902
|
+
get: function () { return chunkH72U2MNG_cjs.ConfigValidationError; }
|
|
903
903
|
});
|
|
904
904
|
Object.defineProperty(exports, "DEFAULT_ALPHA", {
|
|
905
905
|
enumerable: true,
|
|
906
|
-
get: function () { return
|
|
906
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_ALPHA; }
|
|
907
907
|
});
|
|
908
908
|
Object.defineProperty(exports, "DEFAULT_BACKOFF_BASE_MS", {
|
|
909
909
|
enumerable: true,
|
|
910
|
-
get: function () { return
|
|
910
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_BACKOFF_BASE_MS; }
|
|
911
911
|
});
|
|
912
912
|
Object.defineProperty(exports, "DEFAULT_BACKOFF_JITTER", {
|
|
913
913
|
enumerable: true,
|
|
914
|
-
get: function () { return
|
|
914
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_BACKOFF_JITTER; }
|
|
915
915
|
});
|
|
916
916
|
Object.defineProperty(exports, "DEFAULT_BACKOFF_MAX_MS", {
|
|
917
917
|
enumerable: true,
|
|
918
|
-
get: function () { return
|
|
918
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_BACKOFF_MAX_MS; }
|
|
919
919
|
});
|
|
920
920
|
Object.defineProperty(exports, "DEFAULT_BACKOFF_MULTIPLIER", {
|
|
921
921
|
enumerable: true,
|
|
922
|
-
get: function () { return
|
|
922
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_BACKOFF_MULTIPLIER; }
|
|
923
923
|
});
|
|
924
924
|
Object.defineProperty(exports, "DEFAULT_BACKOFF_PARAMS", {
|
|
925
925
|
enumerable: true,
|
|
926
|
-
get: function () { return
|
|
926
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_BACKOFF_PARAMS; }
|
|
927
927
|
});
|
|
928
928
|
Object.defineProperty(exports, "DEFAULT_FAILOVER_BUDGET", {
|
|
929
929
|
enumerable: true,
|
|
930
|
-
get: function () { return
|
|
930
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_FAILOVER_BUDGET; }
|
|
931
931
|
});
|
|
932
932
|
Object.defineProperty(exports, "DEFAULT_RETRY", {
|
|
933
933
|
enumerable: true,
|
|
934
|
-
get: function () { return
|
|
934
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_RETRY; }
|
|
935
935
|
});
|
|
936
936
|
Object.defineProperty(exports, "DEFAULT_RETRY_BUDGET", {
|
|
937
937
|
enumerable: true,
|
|
938
|
-
get: function () { return
|
|
938
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_RETRY_BUDGET; }
|
|
939
939
|
});
|
|
940
940
|
Object.defineProperty(exports, "DEFAULT_TIMEOUT_MS", {
|
|
941
941
|
enumerable: true,
|
|
942
|
-
get: function () { return
|
|
942
|
+
get: function () { return chunkH72U2MNG_cjs.DEFAULT_TIMEOUT_MS; }
|
|
943
943
|
});
|
|
944
944
|
Object.defineProperty(exports, "DualBreaker", {
|
|
945
945
|
enumerable: true,
|
|
946
|
-
get: function () { return
|
|
946
|
+
get: function () { return chunkH72U2MNG_cjs.DualBreaker; }
|
|
947
947
|
});
|
|
948
948
|
Object.defineProperty(exports, "EXCLUSION_REASONS", {
|
|
949
949
|
enumerable: true,
|
|
950
|
-
get: function () { return
|
|
950
|
+
get: function () { return chunkH72U2MNG_cjs.EXCLUSION_REASONS; }
|
|
951
951
|
});
|
|
952
952
|
Object.defineProperty(exports, "ErrorClassSchema", {
|
|
953
953
|
enumerable: true,
|
|
954
|
-
get: function () { return
|
|
954
|
+
get: function () { return chunkH72U2MNG_cjs.ErrorClassSchema; }
|
|
955
955
|
});
|
|
956
956
|
Object.defineProperty(exports, "INITIAL_HEALTH_SCORE", {
|
|
957
957
|
enumerable: true,
|
|
958
|
-
get: function () { return
|
|
958
|
+
get: function () { return chunkH72U2MNG_cjs.INITIAL_HEALTH_SCORE; }
|
|
959
959
|
});
|
|
960
960
|
Object.defineProperty(exports, "REDACT_PATHS", {
|
|
961
961
|
enumerable: true,
|
|
962
|
-
get: function () { return
|
|
962
|
+
get: function () { return chunkH72U2MNG_cjs.REDACT_PATHS; }
|
|
963
963
|
});
|
|
964
964
|
Object.defineProperty(exports, "SwitcherConfigSchema", {
|
|
965
965
|
enumerable: true,
|
|
966
|
-
get: function () { return
|
|
966
|
+
get: function () { return chunkH72U2MNG_cjs.SwitcherConfigSchema; }
|
|
967
967
|
});
|
|
968
968
|
Object.defineProperty(exports, "TARGET_STATES", {
|
|
969
969
|
enumerable: true,
|
|
970
|
-
get: function () { return
|
|
970
|
+
get: function () { return chunkH72U2MNG_cjs.TARGET_STATES; }
|
|
971
971
|
});
|
|
972
972
|
Object.defineProperty(exports, "TargetConfigSchema", {
|
|
973
973
|
enumerable: true,
|
|
974
|
-
get: function () { return
|
|
974
|
+
get: function () { return chunkH72U2MNG_cjs.TargetConfigSchema; }
|
|
975
975
|
});
|
|
976
976
|
Object.defineProperty(exports, "TargetRegistry", {
|
|
977
977
|
enumerable: true,
|
|
978
|
-
get: function () { return
|
|
978
|
+
get: function () { return chunkH72U2MNG_cjs.TargetRegistry; }
|
|
979
979
|
});
|
|
980
980
|
Object.defineProperty(exports, "addProfile", {
|
|
981
981
|
enumerable: true,
|
|
982
|
-
get: function () { return
|
|
982
|
+
get: function () { return chunkH72U2MNG_cjs.addProfile; }
|
|
983
983
|
});
|
|
984
984
|
Object.defineProperty(exports, "applyConfigDiff", {
|
|
985
985
|
enumerable: true,
|
|
986
|
-
get: function () { return
|
|
986
|
+
get: function () { return chunkH72U2MNG_cjs.applyConfigDiff; }
|
|
987
987
|
});
|
|
988
988
|
Object.defineProperty(exports, "checkHardRejects", {
|
|
989
989
|
enumerable: true,
|
|
990
|
-
get: function () { return
|
|
990
|
+
get: function () { return chunkH72U2MNG_cjs.checkHardRejects; }
|
|
991
991
|
});
|
|
992
992
|
Object.defineProperty(exports, "computeBackoffMs", {
|
|
993
993
|
enumerable: true,
|
|
994
|
-
get: function () { return
|
|
994
|
+
get: function () { return chunkH72U2MNG_cjs.computeBackoffMs; }
|
|
995
995
|
});
|
|
996
996
|
Object.defineProperty(exports, "computeConfigDiff", {
|
|
997
997
|
enumerable: true,
|
|
998
|
-
get: function () { return
|
|
998
|
+
get: function () { return chunkH72U2MNG_cjs.computeConfigDiff; }
|
|
999
999
|
});
|
|
1000
1000
|
Object.defineProperty(exports, "computeCooldownMs", {
|
|
1001
1001
|
enumerable: true,
|
|
1002
|
-
get: function () { return
|
|
1002
|
+
get: function () { return chunkH72U2MNG_cjs.computeCooldownMs; }
|
|
1003
1003
|
});
|
|
1004
1004
|
Object.defineProperty(exports, "computeScore", {
|
|
1005
1005
|
enumerable: true,
|
|
1006
|
-
get: function () { return
|
|
1006
|
+
get: function () { return chunkH72U2MNG_cjs.computeScore; }
|
|
1007
1007
|
});
|
|
1008
1008
|
Object.defineProperty(exports, "createAdmissionController", {
|
|
1009
1009
|
enumerable: true,
|
|
1010
|
-
get: function () { return
|
|
1010
|
+
get: function () { return chunkH72U2MNG_cjs.createAdmissionController; }
|
|
1011
1011
|
});
|
|
1012
1012
|
Object.defineProperty(exports, "createAuditLogger", {
|
|
1013
1013
|
enumerable: true,
|
|
1014
|
-
get: function () { return
|
|
1014
|
+
get: function () { return chunkH72U2MNG_cjs.createAuditLogger; }
|
|
1015
1015
|
});
|
|
1016
1016
|
Object.defineProperty(exports, "createAuthWatcher", {
|
|
1017
1017
|
enumerable: true,
|
|
1018
|
-
get: function () { return
|
|
1018
|
+
get: function () { return chunkH72U2MNG_cjs.createAuthWatcher; }
|
|
1019
1019
|
});
|
|
1020
1020
|
Object.defineProperty(exports, "createCircuitBreaker", {
|
|
1021
1021
|
enumerable: true,
|
|
1022
|
-
get: function () { return
|
|
1022
|
+
get: function () { return chunkH72U2MNG_cjs.createCircuitBreaker; }
|
|
1023
1023
|
});
|
|
1024
1024
|
Object.defineProperty(exports, "createConcurrencyTracker", {
|
|
1025
1025
|
enumerable: true,
|
|
1026
|
-
get: function () { return
|
|
1026
|
+
get: function () { return chunkH72U2MNG_cjs.createConcurrencyTracker; }
|
|
1027
1027
|
});
|
|
1028
1028
|
Object.defineProperty(exports, "createCooldownManager", {
|
|
1029
1029
|
enumerable: true,
|
|
1030
|
-
get: function () { return
|
|
1030
|
+
get: function () { return chunkH72U2MNG_cjs.createCooldownManager; }
|
|
1031
1031
|
});
|
|
1032
1032
|
Object.defineProperty(exports, "createFailoverOrchestrator", {
|
|
1033
1033
|
enumerable: true,
|
|
1034
|
-
get: function () { return
|
|
1034
|
+
get: function () { return chunkH72U2MNG_cjs.createFailoverOrchestrator; }
|
|
1035
1035
|
});
|
|
1036
1036
|
Object.defineProperty(exports, "createLogSubscriber", {
|
|
1037
1037
|
enumerable: true,
|
|
1038
|
-
get: function () { return
|
|
1038
|
+
get: function () { return chunkH72U2MNG_cjs.createLogSubscriber; }
|
|
1039
1039
|
});
|
|
1040
1040
|
Object.defineProperty(exports, "createOperatorTools", {
|
|
1041
1041
|
enumerable: true,
|
|
1042
|
-
get: function () { return
|
|
1042
|
+
get: function () { return chunkH72U2MNG_cjs.createOperatorTools; }
|
|
1043
1043
|
});
|
|
1044
1044
|
Object.defineProperty(exports, "createProfileTools", {
|
|
1045
1045
|
enumerable: true,
|
|
1046
|
-
get: function () { return
|
|
1046
|
+
get: function () { return chunkH72U2MNG_cjs.createProfileTools; }
|
|
1047
1047
|
});
|
|
1048
1048
|
Object.defineProperty(exports, "createRegistry", {
|
|
1049
1049
|
enumerable: true,
|
|
1050
|
-
get: function () { return
|
|
1050
|
+
get: function () { return chunkH72U2MNG_cjs.createRegistry; }
|
|
1051
1051
|
});
|
|
1052
1052
|
Object.defineProperty(exports, "createRequestLogger", {
|
|
1053
1053
|
enumerable: true,
|
|
1054
|
-
get: function () { return
|
|
1054
|
+
get: function () { return chunkH72U2MNG_cjs.createRequestLogger; }
|
|
1055
1055
|
});
|
|
1056
1056
|
Object.defineProperty(exports, "createRequestTraceBuffer", {
|
|
1057
1057
|
enumerable: true,
|
|
1058
|
-
get: function () { return
|
|
1058
|
+
get: function () { return chunkH72U2MNG_cjs.createRequestTraceBuffer; }
|
|
1059
1059
|
});
|
|
1060
1060
|
Object.defineProperty(exports, "createRetryPolicy", {
|
|
1061
1061
|
enumerable: true,
|
|
1062
|
-
get: function () { return
|
|
1062
|
+
get: function () { return chunkH72U2MNG_cjs.createRetryPolicy; }
|
|
1063
1063
|
});
|
|
1064
1064
|
Object.defineProperty(exports, "createRoutingEventBus", {
|
|
1065
1065
|
enumerable: true,
|
|
1066
|
-
get: function () { return
|
|
1066
|
+
get: function () { return chunkH72U2MNG_cjs.createRoutingEventBus; }
|
|
1067
1067
|
});
|
|
1068
1068
|
Object.defineProperty(exports, "disableTarget", {
|
|
1069
1069
|
enumerable: true,
|
|
1070
|
-
get: function () { return
|
|
1070
|
+
get: function () { return chunkH72U2MNG_cjs.disableTarget; }
|
|
1071
1071
|
});
|
|
1072
1072
|
Object.defineProperty(exports, "discoverTargets", {
|
|
1073
1073
|
enumerable: true,
|
|
1074
|
-
get: function () { return
|
|
1074
|
+
get: function () { return chunkH72U2MNG_cjs.discoverTargets; }
|
|
1075
1075
|
});
|
|
1076
1076
|
Object.defineProperty(exports, "discoverTargetsFromProfiles", {
|
|
1077
1077
|
enumerable: true,
|
|
1078
|
-
get: function () { return
|
|
1078
|
+
get: function () { return chunkH72U2MNG_cjs.discoverTargetsFromProfiles; }
|
|
1079
1079
|
});
|
|
1080
1080
|
Object.defineProperty(exports, "drainTarget", {
|
|
1081
1081
|
enumerable: true,
|
|
1082
|
-
get: function () { return
|
|
1082
|
+
get: function () { return chunkH72U2MNG_cjs.drainTarget; }
|
|
1083
1083
|
});
|
|
1084
1084
|
Object.defineProperty(exports, "generateCorrelationId", {
|
|
1085
1085
|
enumerable: true,
|
|
1086
|
-
get: function () { return
|
|
1086
|
+
get: function () { return chunkH72U2MNG_cjs.generateCorrelationId; }
|
|
1087
1087
|
});
|
|
1088
1088
|
Object.defineProperty(exports, "getExclusionReason", {
|
|
1089
1089
|
enumerable: true,
|
|
1090
|
-
get: function () { return
|
|
1090
|
+
get: function () { return chunkH72U2MNG_cjs.getExclusionReason; }
|
|
1091
1091
|
});
|
|
1092
1092
|
Object.defineProperty(exports, "getTargetStateTransition", {
|
|
1093
1093
|
enumerable: true,
|
|
1094
|
-
get: function () { return
|
|
1094
|
+
get: function () { return chunkH72U2MNG_cjs.getTargetStateTransition; }
|
|
1095
1095
|
});
|
|
1096
1096
|
Object.defineProperty(exports, "inspectRequest", {
|
|
1097
1097
|
enumerable: true,
|
|
1098
|
-
get: function () { return
|
|
1098
|
+
get: function () { return chunkH72U2MNG_cjs.inspectRequest; }
|
|
1099
1099
|
});
|
|
1100
1100
|
Object.defineProperty(exports, "isRetryable", {
|
|
1101
1101
|
enumerable: true,
|
|
1102
|
-
get: function () { return
|
|
1102
|
+
get: function () { return chunkH72U2MNG_cjs.isRetryable; }
|
|
1103
1103
|
});
|
|
1104
1104
|
Object.defineProperty(exports, "listProfiles", {
|
|
1105
1105
|
enumerable: true,
|
|
1106
|
-
get: function () { return
|
|
1106
|
+
get: function () { return chunkH72U2MNG_cjs.listProfiles; }
|
|
1107
1107
|
});
|
|
1108
1108
|
Object.defineProperty(exports, "listTargets", {
|
|
1109
1109
|
enumerable: true,
|
|
1110
|
-
get: function () { return
|
|
1110
|
+
get: function () { return chunkH72U2MNG_cjs.listTargets; }
|
|
1111
1111
|
});
|
|
1112
1112
|
Object.defineProperty(exports, "loadProfiles", {
|
|
1113
1113
|
enumerable: true,
|
|
1114
|
-
get: function () { return
|
|
1114
|
+
get: function () { return chunkH72U2MNG_cjs.loadProfiles; }
|
|
1115
1115
|
});
|
|
1116
1116
|
Object.defineProperty(exports, "nextProfileId", {
|
|
1117
1117
|
enumerable: true,
|
|
1118
|
-
get: function () { return
|
|
1118
|
+
get: function () { return chunkH72U2MNG_cjs.nextProfileId; }
|
|
1119
1119
|
});
|
|
1120
1120
|
Object.defineProperty(exports, "normalizeLatency", {
|
|
1121
1121
|
enumerable: true,
|
|
1122
|
-
get: function () { return
|
|
1122
|
+
get: function () { return chunkH72U2MNG_cjs.normalizeLatency; }
|
|
1123
1123
|
});
|
|
1124
1124
|
Object.defineProperty(exports, "pauseTarget", {
|
|
1125
1125
|
enumerable: true,
|
|
1126
|
-
get: function () { return
|
|
1126
|
+
get: function () { return chunkH72U2MNG_cjs.pauseTarget; }
|
|
1127
1127
|
});
|
|
1128
1128
|
Object.defineProperty(exports, "reloadConfig", {
|
|
1129
1129
|
enumerable: true,
|
|
1130
|
-
get: function () { return
|
|
1130
|
+
get: function () { return chunkH72U2MNG_cjs.reloadConfig; }
|
|
1131
1131
|
});
|
|
1132
1132
|
Object.defineProperty(exports, "removeProfile", {
|
|
1133
1133
|
enumerable: true,
|
|
1134
|
-
get: function () { return
|
|
1134
|
+
get: function () { return chunkH72U2MNG_cjs.removeProfile; }
|
|
1135
1135
|
});
|
|
1136
1136
|
Object.defineProperty(exports, "resumeTarget", {
|
|
1137
1137
|
enumerable: true,
|
|
1138
|
-
get: function () { return
|
|
1138
|
+
get: function () { return chunkH72U2MNG_cjs.resumeTarget; }
|
|
1139
1139
|
});
|
|
1140
1140
|
Object.defineProperty(exports, "saveProfiles", {
|
|
1141
1141
|
enumerable: true,
|
|
1142
|
-
get: function () { return
|
|
1142
|
+
get: function () { return chunkH72U2MNG_cjs.saveProfiles; }
|
|
1143
1143
|
});
|
|
1144
1144
|
Object.defineProperty(exports, "selectTarget", {
|
|
1145
1145
|
enumerable: true,
|
|
1146
|
-
get: function () { return
|
|
1146
|
+
get: function () { return chunkH72U2MNG_cjs.selectTarget; }
|
|
1147
|
+
});
|
|
1148
|
+
Object.defineProperty(exports, "switchProfile", {
|
|
1149
|
+
enumerable: true,
|
|
1150
|
+
get: function () { return chunkH72U2MNG_cjs.switchProfile; }
|
|
1151
|
+
});
|
|
1152
|
+
Object.defineProperty(exports, "switchToNextProfile", {
|
|
1153
|
+
enumerable: true,
|
|
1154
|
+
get: function () { return chunkH72U2MNG_cjs.switchToNextProfile; }
|
|
1147
1155
|
});
|
|
1148
1156
|
Object.defineProperty(exports, "updateHealthScore", {
|
|
1149
1157
|
enumerable: true,
|
|
1150
|
-
get: function () { return
|
|
1158
|
+
get: function () { return chunkH72U2MNG_cjs.updateHealthScore; }
|
|
1151
1159
|
});
|
|
1152
1160
|
Object.defineProperty(exports, "updateLatencyEma", {
|
|
1153
1161
|
enumerable: true,
|
|
1154
|
-
get: function () { return
|
|
1162
|
+
get: function () { return chunkH72U2MNG_cjs.updateLatencyEma; }
|
|
1155
1163
|
});
|
|
1156
1164
|
Object.defineProperty(exports, "validateConfig", {
|
|
1157
1165
|
enumerable: true,
|
|
1158
|
-
get: function () { return
|
|
1166
|
+
get: function () { return chunkH72U2MNG_cjs.validateConfig; }
|
|
1159
1167
|
});
|
|
1160
1168
|
exports.HEURISTIC_PATTERNS = HEURISTIC_PATTERNS;
|
|
1161
1169
|
exports.PROVIDER_PATTERNS = PROVIDER_PATTERNS;
|
package/dist/index.d.cts
CHANGED
|
@@ -1989,6 +1989,48 @@ interface AuthWatcherOptions {
|
|
|
1989
1989
|
*/
|
|
1990
1990
|
declare const createAuthWatcher: (options?: AuthWatcherOptions) => AuthWatcher;
|
|
1991
1991
|
|
|
1992
|
+
/**
|
|
1993
|
+
* Profile switcher — writes the selected profile's credentials into
|
|
1994
|
+
* OpenCode's auth.json so the next request (or new window) picks them up.
|
|
1995
|
+
*
|
|
1996
|
+
* Also updates .active-openai-auth when switching between auth-work* files.
|
|
1997
|
+
*/
|
|
1998
|
+
|
|
1999
|
+
/** Result of a profile switch attempt. */
|
|
2000
|
+
interface SwitchResult {
|
|
2001
|
+
readonly success: boolean;
|
|
2002
|
+
readonly from?: string;
|
|
2003
|
+
readonly to: string;
|
|
2004
|
+
readonly reason?: string;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Switches the active profile by writing its credentials to auth.json.
|
|
2008
|
+
*
|
|
2009
|
+
* 1. Reads the target profile from profiles.json
|
|
2010
|
+
* 2. Writes its credentials to auth.json (atomic: tmp + rename)
|
|
2011
|
+
* 3. Updates .active-openai-auth if applicable
|
|
2012
|
+
*/
|
|
2013
|
+
declare const switchProfile: (options: {
|
|
2014
|
+
readonly targetProfileId: string;
|
|
2015
|
+
readonly profilesPath?: string;
|
|
2016
|
+
readonly authJsonPath?: string;
|
|
2017
|
+
readonly logger?: pino.Logger;
|
|
2018
|
+
}) => Promise<SwitchResult>;
|
|
2019
|
+
/**
|
|
2020
|
+
* Picks the next healthy profile for a provider and switches to it.
|
|
2021
|
+
*
|
|
2022
|
+
* Selection strategy: round-robin among profiles that are NOT the current one.
|
|
2023
|
+
* The caller (plugin.ts) provides health data to skip unhealthy profiles.
|
|
2024
|
+
*/
|
|
2025
|
+
declare const switchToNextProfile: (options: {
|
|
2026
|
+
readonly provider: string;
|
|
2027
|
+
readonly currentProfileId?: string;
|
|
2028
|
+
readonly excludeProfileIds?: ReadonlyArray<string>;
|
|
2029
|
+
readonly profilesPath?: string;
|
|
2030
|
+
readonly authJsonPath?: string;
|
|
2031
|
+
readonly logger?: pino.Logger;
|
|
2032
|
+
}) => Promise<SwitchResult>;
|
|
2033
|
+
|
|
1992
2034
|
/**
|
|
1993
2035
|
* OpenCode plugin tool definitions for profile management.
|
|
1994
2036
|
*
|
|
@@ -2018,4 +2060,4 @@ interface ProfileToolsOptions {
|
|
|
2018
2060
|
*/
|
|
2019
2061
|
declare const createProfileTools: (options?: ProfileToolsOptions) => ProfileTools;
|
|
2020
2062
|
|
|
2021
|
-
export { ADMISSION_RESULTS, type AdapterRequest, type AdapterResult, type AdmissionConfig, type AdmissionContext, type AdmissionController, type AdmissionDecision, type AdmissionResult, type ApiKeyCredential, type AttemptFn, type AuditCollector, type AuditEvent, type AuditLoggerOptions, type AuthCredential, type AuthResult, type AuthWatcher, type AuthWatcherOptions, type BackoffConfig, BackoffConfigSchema, type BackoffParams, type CircuitBreaker, type CircuitBreakerConfig, type ClassificationResult, type ConcurrencyTrackerApi, type ConfigDiagnostic, type ConfigDiff, type ConfigRef, ConfigValidationError, type ContinuationMode, type CooldownManager, type CorrelationId, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, type DeploymentMode, DualBreaker, EXCLUSION_REASONS, type ErrorClass, ErrorClassSchema, type ExclusionReason, type ExecutionDeps, type ExecutionOrchestrator, type ExecutionProvenance, type ExecutionResult, type FailoverAttempt, type FailoverDeps, type FailoverOrchestrator, type FailoverRegistry, type FailoverResult, HEURISTIC_PATTERNS, INITIAL_HEALTH_SCORE, type InspectResult, type ListTargetsResult, type ModeAdapter, type ModeCapabilities, type NormalizedSignal, type OAuthCredential, type OperatorDeps, type OperatorTools, PROVIDER_PATTERNS, type ProfileEntry, type ProfileStore, type ProfileTools, type ProfileToolsOptions, type ProviderConfigLike, REDACT_PATHS, type RegistrySnapshot, type ReloadResult, type RequestTraceBuffer, type RequestTraceEntry, type RetryDecision, type RetryPolicy, type RoutingEventMap, type RoutingWeights, type SegmentProvenance, type SelectionRecord, type SignalFidelity, type StitchedOutput, type StreamBuffer, type StreamChunk, type StreamStitcher, type SwitcherConfig, SwitcherConfigSchema, TARGET_STATES, TEMPORAL_QUOTA_PATTERN, type TargetActionResult, type TargetConfig, TargetConfigSchema, type TargetEntry, TargetRegistry, type TargetState, type TargetView, addProfile, applyConfigDiff, checkHardRejects, classify, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditCollector, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createExecutionOrchestrator, createFailoverOrchestrator, createLogSubscriber, createModeAdapter, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, createStreamBuffer, createStreamStitcher, detectDeploymentMode, determineContinuationMode, directSignalFromResponse, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, extractRetryAfterMs, generateCorrelationId, getExclusionReason, getModeCapabilities, getSignalFidelity, getTargetStateTransition, heuristicSignalFromEvent, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, updateHealthScore, updateLatencyEma, validateBearerToken, validateConfig };
|
|
2063
|
+
export { ADMISSION_RESULTS, type AdapterRequest, type AdapterResult, type AdmissionConfig, type AdmissionContext, type AdmissionController, type AdmissionDecision, type AdmissionResult, type ApiKeyCredential, type AttemptFn, type AuditCollector, type AuditEvent, type AuditLoggerOptions, type AuthCredential, type AuthResult, type AuthWatcher, type AuthWatcherOptions, type BackoffConfig, BackoffConfigSchema, type BackoffParams, type CircuitBreaker, type CircuitBreakerConfig, type ClassificationResult, type ConcurrencyTrackerApi, type ConfigDiagnostic, type ConfigDiff, type ConfigRef, ConfigValidationError, type ContinuationMode, type CooldownManager, type CorrelationId, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, type DeploymentMode, DualBreaker, EXCLUSION_REASONS, type ErrorClass, ErrorClassSchema, type ExclusionReason, type ExecutionDeps, type ExecutionOrchestrator, type ExecutionProvenance, type ExecutionResult, type FailoverAttempt, type FailoverDeps, type FailoverOrchestrator, type FailoverRegistry, type FailoverResult, HEURISTIC_PATTERNS, INITIAL_HEALTH_SCORE, type InspectResult, type ListTargetsResult, type ModeAdapter, type ModeCapabilities, type NormalizedSignal, type OAuthCredential, type OperatorDeps, type OperatorTools, PROVIDER_PATTERNS, type ProfileEntry, type ProfileStore, type ProfileTools, type ProfileToolsOptions, type ProviderConfigLike, REDACT_PATHS, type RegistrySnapshot, type ReloadResult, type RequestTraceBuffer, type RequestTraceEntry, type RetryDecision, type RetryPolicy, type RoutingEventMap, type RoutingWeights, type SegmentProvenance, type SelectionRecord, type SignalFidelity, type StitchedOutput, type StreamBuffer, type StreamChunk, type StreamStitcher, type SwitchResult, type SwitcherConfig, SwitcherConfigSchema, TARGET_STATES, TEMPORAL_QUOTA_PATTERN, type TargetActionResult, type TargetConfig, TargetConfigSchema, type TargetEntry, TargetRegistry, type TargetState, type TargetView, addProfile, applyConfigDiff, checkHardRejects, classify, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditCollector, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createExecutionOrchestrator, createFailoverOrchestrator, createLogSubscriber, createModeAdapter, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, createStreamBuffer, createStreamStitcher, detectDeploymentMode, determineContinuationMode, directSignalFromResponse, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, extractRetryAfterMs, generateCorrelationId, getExclusionReason, getModeCapabilities, getSignalFidelity, getTargetStateTransition, heuristicSignalFromEvent, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, switchProfile, switchToNextProfile, updateHealthScore, updateLatencyEma, validateBearerToken, validateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -1989,6 +1989,48 @@ interface AuthWatcherOptions {
|
|
|
1989
1989
|
*/
|
|
1990
1990
|
declare const createAuthWatcher: (options?: AuthWatcherOptions) => AuthWatcher;
|
|
1991
1991
|
|
|
1992
|
+
/**
|
|
1993
|
+
* Profile switcher — writes the selected profile's credentials into
|
|
1994
|
+
* OpenCode's auth.json so the next request (or new window) picks them up.
|
|
1995
|
+
*
|
|
1996
|
+
* Also updates .active-openai-auth when switching between auth-work* files.
|
|
1997
|
+
*/
|
|
1998
|
+
|
|
1999
|
+
/** Result of a profile switch attempt. */
|
|
2000
|
+
interface SwitchResult {
|
|
2001
|
+
readonly success: boolean;
|
|
2002
|
+
readonly from?: string;
|
|
2003
|
+
readonly to: string;
|
|
2004
|
+
readonly reason?: string;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Switches the active profile by writing its credentials to auth.json.
|
|
2008
|
+
*
|
|
2009
|
+
* 1. Reads the target profile from profiles.json
|
|
2010
|
+
* 2. Writes its credentials to auth.json (atomic: tmp + rename)
|
|
2011
|
+
* 3. Updates .active-openai-auth if applicable
|
|
2012
|
+
*/
|
|
2013
|
+
declare const switchProfile: (options: {
|
|
2014
|
+
readonly targetProfileId: string;
|
|
2015
|
+
readonly profilesPath?: string;
|
|
2016
|
+
readonly authJsonPath?: string;
|
|
2017
|
+
readonly logger?: pino.Logger;
|
|
2018
|
+
}) => Promise<SwitchResult>;
|
|
2019
|
+
/**
|
|
2020
|
+
* Picks the next healthy profile for a provider and switches to it.
|
|
2021
|
+
*
|
|
2022
|
+
* Selection strategy: round-robin among profiles that are NOT the current one.
|
|
2023
|
+
* The caller (plugin.ts) provides health data to skip unhealthy profiles.
|
|
2024
|
+
*/
|
|
2025
|
+
declare const switchToNextProfile: (options: {
|
|
2026
|
+
readonly provider: string;
|
|
2027
|
+
readonly currentProfileId?: string;
|
|
2028
|
+
readonly excludeProfileIds?: ReadonlyArray<string>;
|
|
2029
|
+
readonly profilesPath?: string;
|
|
2030
|
+
readonly authJsonPath?: string;
|
|
2031
|
+
readonly logger?: pino.Logger;
|
|
2032
|
+
}) => Promise<SwitchResult>;
|
|
2033
|
+
|
|
1992
2034
|
/**
|
|
1993
2035
|
* OpenCode plugin tool definitions for profile management.
|
|
1994
2036
|
*
|
|
@@ -2018,4 +2060,4 @@ interface ProfileToolsOptions {
|
|
|
2018
2060
|
*/
|
|
2019
2061
|
declare const createProfileTools: (options?: ProfileToolsOptions) => ProfileTools;
|
|
2020
2062
|
|
|
2021
|
-
export { ADMISSION_RESULTS, type AdapterRequest, type AdapterResult, type AdmissionConfig, type AdmissionContext, type AdmissionController, type AdmissionDecision, type AdmissionResult, type ApiKeyCredential, type AttemptFn, type AuditCollector, type AuditEvent, type AuditLoggerOptions, type AuthCredential, type AuthResult, type AuthWatcher, type AuthWatcherOptions, type BackoffConfig, BackoffConfigSchema, type BackoffParams, type CircuitBreaker, type CircuitBreakerConfig, type ClassificationResult, type ConcurrencyTrackerApi, type ConfigDiagnostic, type ConfigDiff, type ConfigRef, ConfigValidationError, type ContinuationMode, type CooldownManager, type CorrelationId, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, type DeploymentMode, DualBreaker, EXCLUSION_REASONS, type ErrorClass, ErrorClassSchema, type ExclusionReason, type ExecutionDeps, type ExecutionOrchestrator, type ExecutionProvenance, type ExecutionResult, type FailoverAttempt, type FailoverDeps, type FailoverOrchestrator, type FailoverRegistry, type FailoverResult, HEURISTIC_PATTERNS, INITIAL_HEALTH_SCORE, type InspectResult, type ListTargetsResult, type ModeAdapter, type ModeCapabilities, type NormalizedSignal, type OAuthCredential, type OperatorDeps, type OperatorTools, PROVIDER_PATTERNS, type ProfileEntry, type ProfileStore, type ProfileTools, type ProfileToolsOptions, type ProviderConfigLike, REDACT_PATHS, type RegistrySnapshot, type ReloadResult, type RequestTraceBuffer, type RequestTraceEntry, type RetryDecision, type RetryPolicy, type RoutingEventMap, type RoutingWeights, type SegmentProvenance, type SelectionRecord, type SignalFidelity, type StitchedOutput, type StreamBuffer, type StreamChunk, type StreamStitcher, type SwitcherConfig, SwitcherConfigSchema, TARGET_STATES, TEMPORAL_QUOTA_PATTERN, type TargetActionResult, type TargetConfig, TargetConfigSchema, type TargetEntry, TargetRegistry, type TargetState, type TargetView, addProfile, applyConfigDiff, checkHardRejects, classify, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditCollector, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createExecutionOrchestrator, createFailoverOrchestrator, createLogSubscriber, createModeAdapter, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, createStreamBuffer, createStreamStitcher, detectDeploymentMode, determineContinuationMode, directSignalFromResponse, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, extractRetryAfterMs, generateCorrelationId, getExclusionReason, getModeCapabilities, getSignalFidelity, getTargetStateTransition, heuristicSignalFromEvent, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, updateHealthScore, updateLatencyEma, validateBearerToken, validateConfig };
|
|
2063
|
+
export { ADMISSION_RESULTS, type AdapterRequest, type AdapterResult, type AdmissionConfig, type AdmissionContext, type AdmissionController, type AdmissionDecision, type AdmissionResult, type ApiKeyCredential, type AttemptFn, type AuditCollector, type AuditEvent, type AuditLoggerOptions, type AuthCredential, type AuthResult, type AuthWatcher, type AuthWatcherOptions, type BackoffConfig, BackoffConfigSchema, type BackoffParams, type CircuitBreaker, type CircuitBreakerConfig, type ClassificationResult, type ConcurrencyTrackerApi, type ConfigDiagnostic, type ConfigDiff, type ConfigRef, ConfigValidationError, type ContinuationMode, type CooldownManager, type CorrelationId, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, type DeploymentMode, DualBreaker, EXCLUSION_REASONS, type ErrorClass, ErrorClassSchema, type ExclusionReason, type ExecutionDeps, type ExecutionOrchestrator, type ExecutionProvenance, type ExecutionResult, type FailoverAttempt, type FailoverDeps, type FailoverOrchestrator, type FailoverRegistry, type FailoverResult, HEURISTIC_PATTERNS, INITIAL_HEALTH_SCORE, type InspectResult, type ListTargetsResult, type ModeAdapter, type ModeCapabilities, type NormalizedSignal, type OAuthCredential, type OperatorDeps, type OperatorTools, PROVIDER_PATTERNS, type ProfileEntry, type ProfileStore, type ProfileTools, type ProfileToolsOptions, type ProviderConfigLike, REDACT_PATHS, type RegistrySnapshot, type ReloadResult, type RequestTraceBuffer, type RequestTraceEntry, type RetryDecision, type RetryPolicy, type RoutingEventMap, type RoutingWeights, type SegmentProvenance, type SelectionRecord, type SignalFidelity, type StitchedOutput, type StreamBuffer, type StreamChunk, type StreamStitcher, type SwitchResult, type SwitcherConfig, SwitcherConfigSchema, TARGET_STATES, TEMPORAL_QUOTA_PATTERN, type TargetActionResult, type TargetConfig, TargetConfigSchema, type TargetEntry, TargetRegistry, type TargetState, type TargetView, addProfile, applyConfigDiff, checkHardRejects, classify, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditCollector, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createExecutionOrchestrator, createFailoverOrchestrator, createLogSubscriber, createModeAdapter, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, createStreamBuffer, createStreamStitcher, detectDeploymentMode, determineContinuationMode, directSignalFromResponse, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, extractRetryAfterMs, generateCorrelationId, getExclusionReason, getModeCapabilities, getSignalFidelity, getTargetStateTransition, heuristicSignalFromEvent, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, switchProfile, switchToNextProfile, updateHealthScore, updateLatencyEma, validateBearerToken, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createRequestLogger, generateCorrelationId } from './chunk-
|
|
2
|
-
export { ADMISSION_RESULTS, BackoffConfigSchema, ConfigValidationError, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, DualBreaker, EXCLUSION_REASONS, ErrorClassSchema, INITIAL_HEALTH_SCORE, REDACT_PATHS, SwitcherConfigSchema, TARGET_STATES, TargetConfigSchema, TargetRegistry, addProfile, applyConfigDiff, checkHardRejects, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createFailoverOrchestrator, createLogSubscriber, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, generateCorrelationId, getExclusionReason, getTargetStateTransition, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, updateHealthScore, updateLatencyEma, validateConfig } from './chunk-
|
|
1
|
+
import { createRequestLogger, generateCorrelationId } from './chunk-XXH633FY.js';
|
|
2
|
+
export { ADMISSION_RESULTS, BackoffConfigSchema, ConfigValidationError, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, DualBreaker, EXCLUSION_REASONS, ErrorClassSchema, INITIAL_HEALTH_SCORE, REDACT_PATHS, SwitcherConfigSchema, TARGET_STATES, TargetConfigSchema, TargetRegistry, addProfile, applyConfigDiff, checkHardRejects, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createFailoverOrchestrator, createLogSubscriber, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, generateCorrelationId, getExclusionReason, getTargetStateTransition, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, switchProfile, switchToNextProfile, updateHealthScore, updateLatencyEma, validateConfig } from './chunk-XXH633FY.js';
|
|
3
3
|
import { timingSafeEqual } from 'crypto';
|
|
4
4
|
|
|
5
5
|
// src/mode/detection.ts
|
package/dist/plugin.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var chunkH72U2MNG_cjs = require('./chunk-H72U2MNG.cjs');
|
|
6
6
|
var promises = require('fs/promises');
|
|
7
7
|
var path = require('path');
|
|
8
8
|
var os = require('os');
|
|
@@ -44,21 +44,21 @@ var createStateWriter = (debounceMs = 500) => {
|
|
|
44
44
|
|
|
45
45
|
// src/plugin.ts
|
|
46
46
|
var initializeSwitcher = (rawConfig) => {
|
|
47
|
-
const config =
|
|
48
|
-
const registry =
|
|
49
|
-
const logger =
|
|
50
|
-
const eventBus =
|
|
47
|
+
const config = chunkH72U2MNG_cjs.validateConfig(rawConfig);
|
|
48
|
+
const registry = chunkH72U2MNG_cjs.createRegistry(config);
|
|
49
|
+
const logger = chunkH72U2MNG_cjs.createAuditLogger();
|
|
50
|
+
const eventBus = chunkH72U2MNG_cjs.createRoutingEventBus();
|
|
51
51
|
const circuitBreakers = /* @__PURE__ */ new Map();
|
|
52
52
|
for (const target of registry.getAllTargets()) {
|
|
53
53
|
circuitBreakers.set(
|
|
54
54
|
target.target_id,
|
|
55
|
-
|
|
55
|
+
chunkH72U2MNG_cjs.createCircuitBreaker(target.target_id, config.circuit_breaker, eventBus)
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
|
-
const concurrency =
|
|
59
|
-
const cooldownManager =
|
|
60
|
-
const traceBuffer =
|
|
61
|
-
|
|
58
|
+
const concurrency = chunkH72U2MNG_cjs.createConcurrencyTracker(config.concurrency_limit);
|
|
59
|
+
const cooldownManager = chunkH72U2MNG_cjs.createCooldownManager(registry, eventBus);
|
|
60
|
+
const traceBuffer = chunkH72U2MNG_cjs.createRequestTraceBuffer(100);
|
|
61
|
+
chunkH72U2MNG_cjs.createLogSubscriber(eventBus, logger);
|
|
62
62
|
const configRef = {
|
|
63
63
|
current: () => config,
|
|
64
64
|
swap: () => {
|
|
@@ -94,7 +94,7 @@ var snapshotState = (state) => {
|
|
|
94
94
|
};
|
|
95
95
|
};
|
|
96
96
|
var server = async (_input) => {
|
|
97
|
-
const logger =
|
|
97
|
+
const logger = chunkH72U2MNG_cjs.createAuditLogger({ level: "info" });
|
|
98
98
|
logger.info("O-Switcher plugin initializing");
|
|
99
99
|
const state = {};
|
|
100
100
|
const stateWriter = createStateWriter();
|
|
@@ -137,17 +137,17 @@ var server = async (_input) => {
|
|
|
137
137
|
rawConfig["targets"] && Array.isArray(rawConfig["targets"]) && rawConfig["targets"].length > 0
|
|
138
138
|
);
|
|
139
139
|
if (!hasExplicitTargets) {
|
|
140
|
-
const profileStore = await
|
|
140
|
+
const profileStore = await chunkH72U2MNG_cjs.loadProfiles().catch(() => ({}));
|
|
141
141
|
const profileKeys = Object.keys(profileStore);
|
|
142
142
|
if (profileKeys.length > 0) {
|
|
143
|
-
const profileTargets =
|
|
143
|
+
const profileTargets = chunkH72U2MNG_cjs.discoverTargetsFromProfiles(profileStore);
|
|
144
144
|
rawConfig["targets"] = profileTargets;
|
|
145
145
|
logger.info(
|
|
146
146
|
{ discovered: profileTargets.length, profiles: profileKeys },
|
|
147
147
|
"Auto-discovered targets from O-Switcher profiles"
|
|
148
148
|
);
|
|
149
149
|
} else if (providerConfig && typeof providerConfig === "object") {
|
|
150
|
-
const discoveredTargets =
|
|
150
|
+
const discoveredTargets = chunkH72U2MNG_cjs.discoverTargets(providerConfig);
|
|
151
151
|
if (discoveredTargets.length === 0) {
|
|
152
152
|
logger.warn("No providers found \u2014 running in passthrough mode");
|
|
153
153
|
return;
|
|
@@ -167,7 +167,7 @@ var server = async (_input) => {
|
|
|
167
167
|
Object.assign(state, initialized);
|
|
168
168
|
logger.info({ targets: state.registry?.getAllTargets().length }, "O-Switcher initialized");
|
|
169
169
|
publishTuiState();
|
|
170
|
-
state.authWatcher =
|
|
170
|
+
state.authWatcher = chunkH72U2MNG_cjs.createAuthWatcher({ logger });
|
|
171
171
|
await state.authWatcher.start();
|
|
172
172
|
logger.info("Auth watcher started");
|
|
173
173
|
} catch (err) {
|
|
@@ -182,7 +182,7 @@ var server = async (_input) => {
|
|
|
182
182
|
*/
|
|
183
183
|
async "chat.params"(input, output) {
|
|
184
184
|
if (!state.registry) return;
|
|
185
|
-
const requestId =
|
|
185
|
+
const requestId = chunkH72U2MNG_cjs.generateCorrelationId();
|
|
186
186
|
const targets = state.registry.getAllTargets();
|
|
187
187
|
const providerId = input.provider?.info?.id ?? input.provider?.info?.name ?? input.model?.providerID ?? void 0;
|
|
188
188
|
if (!providerId) return;
|
|
@@ -239,17 +239,42 @@ var server = async (_input) => {
|
|
|
239
239
|
state.registry.recordObservation(target.target_id, 0);
|
|
240
240
|
state.circuitBreakers?.get(target.target_id)?.recordFailure();
|
|
241
241
|
publishTuiState();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
);
|
|
242
|
+
const errorObj = error;
|
|
243
|
+
const statusCode = errorObj.statusCode ?? errorObj.error?.statusCode;
|
|
244
|
+
const errorName = String(errorObj.name ?? errorObj.error ?? "");
|
|
245
|
+
const isRateLimit = statusCode === 429 || statusCode === "429" || errorName.includes("RateLimitError") || errorName.includes("QuotaExceeded");
|
|
246
|
+
if (isRateLimit) {
|
|
247
|
+
state.logger?.info(
|
|
248
|
+
{ target_id: target.target_id, statusCode },
|
|
249
|
+
"Rate limit hit \u2014 switching profile"
|
|
250
|
+
);
|
|
251
|
+
chunkH72U2MNG_cjs.switchToNextProfile({
|
|
252
|
+
provider: providerId,
|
|
253
|
+
currentProfileId: target.target_id,
|
|
254
|
+
logger: state.logger
|
|
255
|
+
}).then((result) => {
|
|
256
|
+
if (result.success) {
|
|
257
|
+
state.logger?.info(
|
|
258
|
+
{ from: result.from, to: result.to, provider: providerId },
|
|
259
|
+
"Switched to next profile after rate limit"
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}).catch((err) => {
|
|
263
|
+
state.logger?.warn({ err }, "Failed to switch profile");
|
|
264
|
+
});
|
|
265
|
+
} else {
|
|
266
|
+
state.logger?.info(
|
|
267
|
+
{ target_id: target.target_id, event_type: event.type },
|
|
268
|
+
"Recorded failure from session event"
|
|
269
|
+
);
|
|
270
|
+
}
|
|
246
271
|
}
|
|
247
272
|
}
|
|
248
273
|
}
|
|
249
274
|
},
|
|
250
275
|
tool: {
|
|
251
|
-
...
|
|
252
|
-
...
|
|
276
|
+
...chunkH72U2MNG_cjs.createProfileTools(),
|
|
277
|
+
...chunkH72U2MNG_cjs.createOperatorTools(lazyOperatorDeps)
|
|
253
278
|
}
|
|
254
279
|
};
|
|
255
280
|
return hooks;
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createAuditLogger, createOperatorTools, createProfileTools, generateCorrelationId, loadProfiles, discoverTargetsFromProfiles, discoverTargets, createAuthWatcher, validateConfig, createRegistry, createRoutingEventBus, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createRequestTraceBuffer, createLogSubscriber } from './chunk-
|
|
1
|
+
import { createAuditLogger, createOperatorTools, createProfileTools, switchToNextProfile, generateCorrelationId, loadProfiles, discoverTargetsFromProfiles, discoverTargets, createAuthWatcher, validateConfig, createRegistry, createRoutingEventBus, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createRequestTraceBuffer, createLogSubscriber } from './chunk-XXH633FY.js';
|
|
2
2
|
import { mkdir, writeFile, rename } from 'fs/promises';
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
@@ -235,10 +235,35 @@ var server = async (_input) => {
|
|
|
235
235
|
state.registry.recordObservation(target.target_id, 0);
|
|
236
236
|
state.circuitBreakers?.get(target.target_id)?.recordFailure();
|
|
237
237
|
publishTuiState();
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
);
|
|
238
|
+
const errorObj = error;
|
|
239
|
+
const statusCode = errorObj.statusCode ?? errorObj.error?.statusCode;
|
|
240
|
+
const errorName = String(errorObj.name ?? errorObj.error ?? "");
|
|
241
|
+
const isRateLimit = statusCode === 429 || statusCode === "429" || errorName.includes("RateLimitError") || errorName.includes("QuotaExceeded");
|
|
242
|
+
if (isRateLimit) {
|
|
243
|
+
state.logger?.info(
|
|
244
|
+
{ target_id: target.target_id, statusCode },
|
|
245
|
+
"Rate limit hit \u2014 switching profile"
|
|
246
|
+
);
|
|
247
|
+
switchToNextProfile({
|
|
248
|
+
provider: providerId,
|
|
249
|
+
currentProfileId: target.target_id,
|
|
250
|
+
logger: state.logger
|
|
251
|
+
}).then((result) => {
|
|
252
|
+
if (result.success) {
|
|
253
|
+
state.logger?.info(
|
|
254
|
+
{ from: result.from, to: result.to, provider: providerId },
|
|
255
|
+
"Switched to next profile after rate limit"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}).catch((err) => {
|
|
259
|
+
state.logger?.warn({ err }, "Failed to switch profile");
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
state.logger?.info(
|
|
263
|
+
{ target_id: target.target_id, event_type: event.type },
|
|
264
|
+
"Recorded failure from session event"
|
|
265
|
+
);
|
|
266
|
+
}
|
|
242
267
|
}
|
|
243
268
|
}
|
|
244
269
|
}
|
package/package.json
CHANGED
package/src/tui.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
1
2
|
/**
|
|
2
3
|
* O-Switcher TUI plugin for OpenCode.
|
|
3
4
|
*
|
|
@@ -102,7 +103,7 @@ const SidebarFooter = () => {
|
|
|
102
103
|
};
|
|
103
104
|
|
|
104
105
|
/** Full status dashboard route. */
|
|
105
|
-
const StatusDashboard = () => {
|
|
106
|
+
const StatusDashboard = (props: { api: TuiPluginApi }) => {
|
|
106
107
|
const state = useTuiState();
|
|
107
108
|
|
|
108
109
|
const content = (): string => {
|
|
@@ -166,11 +167,19 @@ const StatusDashboard = () => {
|
|
|
166
167
|
const age = Date.now() - s.updated_at;
|
|
167
168
|
const ageSec = Math.round(age / 1000);
|
|
168
169
|
lines.push(` Updated ${ageSec}s ago`);
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push(' Press Esc or q to go back');
|
|
169
172
|
|
|
170
173
|
return lines.join('\n');
|
|
171
174
|
};
|
|
172
175
|
|
|
173
|
-
|
|
176
|
+
const onKeyDown = (e: { name: string }) => {
|
|
177
|
+
if (e.name === 'escape' || e.name === 'q') {
|
|
178
|
+
props.api.route.navigate('home');
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return <box focused onKeyDown={onKeyDown}><text wrapMode="none">{content()}</text></box>;
|
|
174
183
|
};
|
|
175
184
|
|
|
176
185
|
// ── Plugin entry point ────────────────────────────────────────────
|
|
@@ -189,7 +198,7 @@ const tui = async (api: TuiPluginApi) => {
|
|
|
189
198
|
api.route.register([
|
|
190
199
|
{
|
|
191
200
|
name: 'o-switcher-status',
|
|
192
|
-
render: () => <StatusDashboard />,
|
|
201
|
+
render: () => <StatusDashboard api={api} />,
|
|
193
202
|
},
|
|
194
203
|
]);
|
|
195
204
|
|
|
@@ -210,5 +219,8 @@ const tui = async (api: TuiPluginApi) => {
|
|
|
210
219
|
]);
|
|
211
220
|
};
|
|
212
221
|
|
|
213
|
-
const tuiModule: TuiPluginModule
|
|
222
|
+
const tuiModule: TuiPluginModule & { id: string } = {
|
|
223
|
+
id: 'o-switcher',
|
|
224
|
+
tui,
|
|
225
|
+
};
|
|
214
226
|
export default tuiModule;
|