saros-proxy 0.7.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +98 -47
  2. package/dist/cli/autostart.js.map +1 -1
  3. package/dist/cli/daemon.d.ts +31 -4
  4. package/dist/cli/daemon.js +124 -22
  5. package/dist/cli/daemon.js.map +1 -1
  6. package/dist/cli/dsh-config.d.ts +32 -0
  7. package/dist/cli/dsh-config.js +122 -0
  8. package/dist/cli/dsh-config.js.map +1 -0
  9. package/dist/cli/harness-models.d.ts +26 -2
  10. package/dist/cli/harness-models.js +192 -2
  11. package/dist/cli/harness-models.js.map +1 -1
  12. package/dist/cli/harness-sync.d.ts +27 -10
  13. package/dist/cli/harness-sync.js +70 -16
  14. package/dist/cli/harness-sync.js.map +1 -1
  15. package/dist/cli/help.js +4 -2
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli/omp-config.js +22 -5
  18. package/dist/cli/omp-config.js.map +1 -1
  19. package/dist/cli/opencode-config.js +22 -15
  20. package/dist/cli/opencode-config.js.map +1 -1
  21. package/dist/cli/overview.d.ts +10 -0
  22. package/dist/cli/overview.js +61 -0
  23. package/dist/cli/overview.js.map +1 -0
  24. package/dist/cli/pi-config.js +21 -5
  25. package/dist/cli/pi-config.js.map +1 -1
  26. package/dist/cli/setup.d.ts +6 -10
  27. package/dist/cli/setup.js +107 -233
  28. package/dist/cli/setup.js.map +1 -1
  29. package/dist/cli/ui.d.ts +0 -1
  30. package/dist/cli/ui.js +0 -16
  31. package/dist/cli/ui.js.map +1 -1
  32. package/dist/cli/update-check.js.map +1 -1
  33. package/dist/cli/usage.d.ts +12 -0
  34. package/dist/cli/usage.js +116 -0
  35. package/dist/cli/usage.js.map +1 -0
  36. package/dist/config.d.ts +13 -17
  37. package/dist/config.js +29 -78
  38. package/dist/config.js.map +1 -1
  39. package/dist/constants.d.ts +7 -6
  40. package/dist/constants.js +10 -5
  41. package/dist/constants.js.map +1 -1
  42. package/dist/index.d.ts +4 -4
  43. package/dist/index.js +34 -34
  44. package/dist/index.js.map +1 -1
  45. package/dist/model-stats.d.ts +20 -0
  46. package/dist/model-stats.js +135 -0
  47. package/dist/model-stats.js.map +1 -0
  48. package/dist/models-sync.js +10 -4
  49. package/dist/models-sync.js.map +1 -1
  50. package/dist/probe-cache.js.map +1 -1
  51. package/dist/providers/commandcode.d.ts +17 -0
  52. package/dist/providers/commandcode.js +232 -0
  53. package/dist/providers/commandcode.js.map +1 -0
  54. package/dist/providers/index.d.ts +43 -0
  55. package/dist/providers/index.js +75 -0
  56. package/dist/providers/index.js.map +1 -0
  57. package/dist/providers/infer.d.ts +23 -0
  58. package/dist/providers/infer.js +13 -0
  59. package/dist/providers/infer.js.map +1 -0
  60. package/dist/providers/opencode-go.d.ts +4 -0
  61. package/dist/providers/opencode-go.js +179 -0
  62. package/dist/providers/opencode-go.js.map +1 -0
  63. package/dist/providers/types.d.ts +118 -0
  64. package/dist/providers/types.js +10 -0
  65. package/dist/providers/types.js.map +1 -0
  66. package/dist/proxy-logic.d.ts +46 -22
  67. package/dist/proxy-logic.js +146 -106
  68. package/dist/proxy-logic.js.map +1 -1
  69. package/dist/proxy.d.ts +20 -2
  70. package/dist/proxy.js +231 -57
  71. package/dist/proxy.js.map +1 -1
  72. package/dist/usage-client.d.ts +41 -0
  73. package/dist/usage-client.js +124 -0
  74. package/dist/usage-client.js.map +1 -0
  75. package/dist/validation.d.ts +5 -5
  76. package/dist/validation.js +7 -9
  77. package/dist/validation.js.map +1 -1
  78. package/package.json +75 -77
@@ -0,0 +1,41 @@
1
+ /**
2
+ * usage-client.ts — Per-key quota usage from the upstream usage API.
3
+ *
4
+ * GET <upstream>/zen/go/v1/usage (Bearer = the proxy's own API key) returns:
5
+ * {"usage":{"rolling":{"status":"ok","percent":12,"resetsAt":"…"},
6
+ * "weekly":{…},"monthly":{…}}}
7
+ *
8
+ * Refresh strategy: stale-while-revalidate on demand. `maybeRefreshUsage`
9
+ * is called on the request path; when data is older than USAGE_REFRESH_TTL_MS
10
+ * and no refresh is in flight, it fires a background refresh for every key.
11
+ * Failures keep the last known values (weighted rotation just keeps working
12
+ * with slightly stale percentages).
13
+ */
14
+ import { type ProxyState } from './proxy-logic.js';
15
+ import type { ProxyConfig } from './config.js';
16
+ export interface KeyUsage {
17
+ /** Worst-window used percent, 0-100. */
18
+ usedPercent: number;
19
+ rollingPercent: number;
20
+ weeklyPercent: number;
21
+ monthlyPercent: number;
22
+ rollingResetsAt?: string;
23
+ weeklyResetsAt?: string;
24
+ monthlyResetsAt?: string;
25
+ }
26
+ /** Parse a usage API response body into KeyUsage (throws on contract violation). */
27
+ export declare function parseUsageResponse(body: string): KeyUsage;
28
+ /**
29
+ * Fetch current usage for one key. Returns null on any failure
30
+ * (network, timeout, non-200, contract violation) — callers treat null as
31
+ * "no fresh data".
32
+ */
33
+ export declare function fetchKeyUsage(upstreamBaseUrl: string, apiKey: string, usagePath?: string): Promise<KeyUsage | null>;
34
+ /** Test seam: reset coordinator state. */
35
+ export declare function resetUsageRefreshState(): void;
36
+ /**
37
+ * Kick off a background refresh of every key's usage when the cached data is
38
+ * older than USAGE_REFRESH_TTL_MS. Fire-and-forget: never throws, never blocks
39
+ * the request path, deduplicates concurrent calls.
40
+ */
41
+ export declare function maybeRefreshUsage(state: ProxyState, config: ProxyConfig): void;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * usage-client.ts — Per-key quota usage from the upstream usage API.
3
+ *
4
+ * GET <upstream>/zen/go/v1/usage (Bearer = the proxy's own API key) returns:
5
+ * {"usage":{"rolling":{"status":"ok","percent":12,"resetsAt":"…"},
6
+ * "weekly":{…},"monthly":{…}}}
7
+ *
8
+ * Refresh strategy: stale-while-revalidate on demand. `maybeRefreshUsage`
9
+ * is called on the request path; when data is older than USAGE_REFRESH_TTL_MS
10
+ * and no refresh is in flight, it fires a background refresh for every key.
11
+ * Failures keep the last known values (weighted rotation just keeps working
12
+ * with slightly stale percentages).
13
+ */
14
+ import { logger } from './logger.js';
15
+ import { USAGE_FETCH_TIMEOUT_MS, USAGE_PATH, USAGE_REFRESH_TTL_MS } from './constants.js';
16
+ import { updateKeyUsage } from './proxy-logic.js';
17
+ import { getProvider, inferProvider } from './providers/index.js';
18
+ function parsePercent(window) {
19
+ const percent = window?.percent;
20
+ if (typeof percent !== 'number' || !Number.isFinite(percent))
21
+ return 0;
22
+ return Math.min(100, Math.max(0, percent));
23
+ }
24
+ /** Parse a usage API response body into KeyUsage (throws on contract violation). */
25
+ export function parseUsageResponse(body) {
26
+ let parsed;
27
+ try {
28
+ parsed = JSON.parse(body);
29
+ }
30
+ catch {
31
+ throw new Error('usage response is not valid JSON');
32
+ }
33
+ const usage = parsed?.usage;
34
+ if (!usage || typeof usage !== 'object') {
35
+ throw new Error('usage response missing "usage" object');
36
+ }
37
+ const rolling = parsePercent(usage.rolling);
38
+ const weekly = parsePercent(usage.weekly);
39
+ const monthly = parsePercent(usage.monthly);
40
+ return {
41
+ rollingPercent: rolling,
42
+ weeklyPercent: weekly,
43
+ monthlyPercent: monthly,
44
+ usedPercent: Math.max(rolling, weekly, monthly),
45
+ rollingResetsAt: typeof usage.rolling?.resetsAt === 'string' ? usage.rolling.resetsAt : undefined,
46
+ weeklyResetsAt: typeof usage.weekly?.resetsAt === 'string' ? usage.weekly.resetsAt : undefined,
47
+ monthlyResetsAt: typeof usage.monthly?.resetsAt === 'string' ? usage.monthly.resetsAt : undefined,
48
+ };
49
+ }
50
+ /**
51
+ * Fetch current usage for one key. Returns null on any failure
52
+ * (network, timeout, non-200, contract violation) — callers treat null as
53
+ * "no fresh data".
54
+ */
55
+ export async function fetchKeyUsage(upstreamBaseUrl, apiKey, usagePath = USAGE_PATH) {
56
+ const controller = new AbortController();
57
+ const timer = setTimeout(() => controller.abort(), USAGE_FETCH_TIMEOUT_MS);
58
+ try {
59
+ const response = await fetch(new URL(usagePath, upstreamBaseUrl), {
60
+ headers: { Authorization: `Bearer ${apiKey}`, Accept: 'application/json' },
61
+ signal: controller.signal,
62
+ });
63
+ // Keep the abort armed through the body read: a stalled body after a 200
64
+ // must still time out, or refreshInFlight stays true forever and usage
65
+ // rotation freezes on stale percentages.
66
+ if (!response.ok)
67
+ return null;
68
+ return parseUsageResponse(await response.text());
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ finally {
74
+ clearTimeout(timer);
75
+ }
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Stale-while-revalidate coordinator (one per proxy process)
79
+ // ---------------------------------------------------------------------------
80
+ let lastRefreshAt = 0;
81
+ let refreshInFlight = false;
82
+ /** Test seam: reset coordinator state. */
83
+ export function resetUsageRefreshState() {
84
+ lastRefreshAt = 0;
85
+ refreshInFlight = false;
86
+ }
87
+ /**
88
+ * Kick off a background refresh of every key's usage when the cached data is
89
+ * older than USAGE_REFRESH_TTL_MS. Fire-and-forget: never throws, never blocks
90
+ * the request path, deduplicates concurrent calls.
91
+ */
92
+ export function maybeRefreshUsage(state, config) {
93
+ const now = Date.now();
94
+ if (refreshInFlight || now - lastRefreshAt < USAGE_REFRESH_TTL_MS)
95
+ return;
96
+ refreshInFlight = true;
97
+ lastRefreshAt = now;
98
+ void (async () => {
99
+ try {
100
+ const entries = await Promise.all(state.keys.map(async (k) => {
101
+ const provider = getProvider(inferProvider({ label: k.label, key: k.key, provider: k.provider }));
102
+ // Providers without a queryable usage API (commandcode) are skipped —
103
+ // weighted rotation simply treats them as having no fresh data.
104
+ if (!provider || !provider.usagePath())
105
+ return [k.label, null];
106
+ const base = config.upstreams?.[provider.id] ?? (provider.id === 'opencode-go' ? config.upstreamBaseUrl : provider.baseUrl);
107
+ const usage = await fetchKeyUsage(base, k.key, provider.usagePath());
108
+ return [k.label, usage];
109
+ }));
110
+ const usable = entries.filter(([, u]) => u !== null);
111
+ if (usable.length > 0) {
112
+ updateKeyUsage(state, new Map(usable.map(([label, u]) => [label, u.usedPercent])));
113
+ logger.debug({ keys: usable.map(([l]) => l) }, 'key usage refreshed');
114
+ }
115
+ }
116
+ catch (err) {
117
+ logger.debug({ err }, 'usage refresh failed');
118
+ }
119
+ finally {
120
+ refreshInFlight = false;
121
+ }
122
+ })();
123
+ }
124
+ //# sourceMappingURL=usage-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-client.js","sourceRoot":"","sources":["../src/usage-client.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,11 @@
10
10
  export declare function isValidPort(value: unknown): value is number;
11
11
  /**
12
12
  * Check if the given string looks like a valid API key.
13
- * Must start with `sk-` and be at least {@link MIN_KEY_LENGTH} characters long.
13
+ *
14
+ * Multi-provider aware: accepts `sk-` (opencode-go / commandcode console
15
+ * keys) and `user_` (commandcode tokens) of at least {@link MIN_KEY_LENGTH}
16
+ * characters. Structural check only — provider identity is confirmed by the
17
+ * smoke test in `identifyKey()`.
14
18
  */
15
19
  export declare function isValidApiKey(key: unknown): key is string;
16
20
  /**
@@ -28,7 +32,3 @@ export declare function isValidHttpsUrl(url: unknown): url is string;
28
32
  * @returns `true` if the value is a positive integer in the given range.
29
33
  */
30
34
  export declare function isValidPositiveInt(val: string, min?: number, max?: number): boolean;
31
- /**
32
- * Check if the given string is a valid workspace ID (matches `wrk_[A-Za-z0-9]+`).
33
- */
34
- export declare function isValidWorkspaceId(id: unknown): id is string;
@@ -4,7 +4,7 @@
4
4
  * Centralises input validation patterns used across config.ts and setup.ts
5
5
  * to ensure consistent rules and reduce duplication.
6
6
  */
7
- import { API_KEY_PREFIX, MIN_KEY_LENGTH, WORKSPACE_ID_REGEX } from './constants.js';
7
+ import { isPlausibleKey } from './providers/index.js';
8
8
  /**
9
9
  * Check if the given value is a valid TCP port number (1–65535).
10
10
  */
@@ -19,10 +19,14 @@ export function isValidPort(value) {
19
19
  }
20
20
  /**
21
21
  * Check if the given string looks like a valid API key.
22
- * Must start with `sk-` and be at least {@link MIN_KEY_LENGTH} characters long.
22
+ *
23
+ * Multi-provider aware: accepts `sk-` (opencode-go / commandcode console
24
+ * keys) and `user_` (commandcode tokens) of at least {@link MIN_KEY_LENGTH}
25
+ * characters. Structural check only — provider identity is confirmed by the
26
+ * smoke test in `identifyKey()`.
23
27
  */
24
28
  export function isValidApiKey(key) {
25
- return typeof key === 'string' && key.startsWith(API_KEY_PREFIX) && key.length >= MIN_KEY_LENGTH;
29
+ return typeof key === 'string' && isPlausibleKey(key);
26
30
  }
27
31
  /**
28
32
  * Check if the given value is a valid HTTPS URL.
@@ -55,10 +59,4 @@ export function isValidPositiveInt(val, min = 1, max = 100) {
55
59
  const n = Number.parseInt(trimmed, 10);
56
60
  return n >= min && n <= max;
57
61
  }
58
- /**
59
- * Check if the given string is a valid workspace ID (matches `wrk_[A-Za-z0-9]+`).
60
- */
61
- export function isValidWorkspaceId(id) {
62
- return typeof id === 'string' && WORKSPACE_ID_REGEX.test(id);
63
- }
64
62
  //# sourceMappingURL=validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpF;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC;AAC9F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,MAAM,IAAI,cAAc,CAAC;AACnG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,GAAG;IAChE,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAW;IAC5C,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC/D,CAAC"}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC;AAC9F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,GAAG;IAChE,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC;AAC9B,CAAC"}
package/package.json CHANGED
@@ -1,77 +1,75 @@
1
- {
2
- "name": "saros-proxy",
3
- "version": "0.7.3",
4
- "description": "Saros — predictive multi-account proxy for the OpenCode-Go API. Cycles between accounts before they exhaust.",
5
- "type": "module",
6
- "bin": {
7
- "saros-proxy": "dist/index.js",
8
- "saros-proxy-setup": "dist/cli/setup.js"
9
- },
10
- "files": [
11
- "dist",
12
- "README.md",
13
- "LICENSE"
14
- ],
15
- "scripts": {
16
- "setup": "tsx src/cli/setup.ts",
17
- "prototype": "tsx prototype/prototype.ts",
18
- "proto:usage": "tsx prototype/usage-switching.ts",
19
- "proto:probe": "tsx prototype/api-probe.ts",
20
- "proto:scrape": "tsx prototype/dashboard-scraper.ts",
21
- "proto:setup": "tsx prototype/setup-wizard.ts",
22
- "proto:autostart": "tsx prototype/auto-start.ts",
23
- "dev": "tsx src/index.ts",
24
- "build": "tsc",
25
- "lint": "tsc --noEmit",
26
- "prepublishOnly": "npm run build",
27
- "test": "vitest",
28
- "coverage": "vitest run --coverage",
29
- "test:npm": "vitest run test/npm-package.test.ts",
30
- "test:source": "vitest run test/source-install.test.ts",
31
- "test:docker": "vitest run test/docker-install.test.ts",
32
- "docker:build": "docker compose build proxy",
33
- "docker:run": "docker compose up proxy",
34
- "docker:test": "docker compose run --rm test"
35
- },
36
- "keywords": [
37
- "opencode",
38
- "proxy",
39
- "multi-account",
40
- "failover",
41
- "load-balancer",
42
- "api-gateway",
43
- "circuit-breaker",
44
- "saros",
45
- "saros-proxy"
46
- ],
47
- "author": "Saros Contributors",
48
- "license": "MIT",
49
- "engines": {
50
- "node": ">=22.0.0"
51
- },
52
- "repository": {
53
- "type": "git",
54
- "url": "git+https://github.com/vitorvilaca3011/saros-proxy.git"
55
- },
56
- "bugs": {
57
- "url": "https://github.com/vitorvilaca3011/saros-proxy/issues"
58
- },
59
- "homepage": "https://github.com/vitorvilaca3011/saros-proxy#readme",
60
- "devDependencies": {
61
- "@types/node": "^25.9.1",
62
- "@vitest/coverage-v8": "^4.1.8",
63
- "pino-pretty": "^13.1.3",
64
- "tsx": "^4.22.4",
65
- "typescript": "^6.0.3",
66
- "vitest": "^4.1.8"
67
- },
68
- "dependencies": {
69
- "@clack/prompts": "^1.5.1",
70
- "@hono/node-server": "^2.0.4",
71
- "chalk": "^5.6.2",
72
- "hono": "^4.12.23",
73
- "hono-rate-limiter": "^0.5.3",
74
- "pino": "^10.3.1",
75
- "yaml": "^2.9.0"
76
- }
77
- }
1
+ {
2
+ "name": "saros-proxy",
3
+ "version": "0.9.0",
4
+ "description": "Saros — predictive multi-account proxy for the OpenCode-Go API. Cycles between accounts before they exhaust.",
5
+ "type": "module",
6
+ "bin": {
7
+ "saros-proxy": "dist/index.js",
8
+ "saros-proxy-setup": "dist/cli/setup.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "setup": "tsx src/cli/setup.ts",
17
+ "dev": "tsx src/index.ts serve",
18
+ "build": "tsc",
19
+ "lint": "tsc --noEmit",
20
+ "prepublishOnly": "npm run build",
21
+ "test": "vitest",
22
+ "coverage": "vitest run --coverage",
23
+ "test:npm": "vitest run test/npm-package.test.ts",
24
+ "test:live-model": "npm run build && vitest run --config vitest.e2e.config.ts test/live-model.e2e.test.ts",
25
+ "test:source": "vitest run test/source-install.test.ts",
26
+ "test:install": "npm run build && vitest run --config vitest.e2e.config.ts test/install.e2e.test.ts",
27
+ "test:model-e2e": "npm run build && vitest run --config vitest.e2e.config.ts test/model-e2e.test.ts",
28
+ "test:docker": "vitest run test/docker-install.test.ts",
29
+ "docker:build": "docker compose build proxy",
30
+ "docker:run": "docker compose up proxy",
31
+ "docker:test": "docker compose run --rm test",
32
+ "test:e2e": "npm run build && vitest run --config vitest.e2e.config.ts"
33
+ },
34
+ "keywords": [
35
+ "opencode",
36
+ "proxy",
37
+ "multi-account",
38
+ "failover",
39
+ "load-balancer",
40
+ "api-gateway",
41
+ "circuit-breaker",
42
+ "saros",
43
+ "saros-proxy"
44
+ ],
45
+ "author": "Saros Contributors",
46
+ "license": "MIT",
47
+ "engines": {
48
+ "node": ">=22.0.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/vitorvilaca3011/saros-proxy.git"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/vitorvilaca3011/saros-proxy/issues"
56
+ },
57
+ "homepage": "https://github.com/vitorvilaca3011/saros-proxy#readme",
58
+ "devDependencies": {
59
+ "@types/node": "^26.2.0",
60
+ "@vitest/coverage-v8": "^4.1.10",
61
+ "pino-pretty": "^13.1.3",
62
+ "tsx": "^4.23.12",
63
+ "typescript": "^7.0.2",
64
+ "vitest": "^4.1.10"
65
+ },
66
+ "dependencies": {
67
+ "@clack/prompts": "^1.7.0",
68
+ "@hono/node-server": "^2.0.4",
69
+ "chalk": "^6.0.0",
70
+ "hono": "^4.12.23",
71
+ "hono-rate-limiter": "^0.5.3",
72
+ "pino": "^10.3.1",
73
+ "yaml": "^2.9.0"
74
+ }
75
+ }