saros-proxy 0.9.0 → 0.10.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/LICENSE +0 -0
- package/README.md +50 -28
- package/dist/cli/autostart.d.ts +0 -0
- package/dist/cli/autostart.js +0 -0
- package/dist/cli/autostart.js.map +0 -0
- package/dist/cli/daemon.d.ts +0 -0
- package/dist/cli/daemon.js +0 -0
- package/dist/cli/daemon.js.map +0 -0
- package/dist/cli/dsh-config.d.ts +0 -0
- package/dist/cli/dsh-config.js +0 -0
- package/dist/cli/dsh-config.js.map +0 -0
- package/dist/cli/harness-models.d.ts +0 -0
- package/dist/cli/harness-models.js +0 -0
- package/dist/cli/harness-models.js.map +0 -0
- package/dist/cli/harness-sync.d.ts +0 -0
- package/dist/cli/harness-sync.js +0 -0
- package/dist/cli/harness-sync.js.map +0 -0
- package/dist/cli/help.d.ts +0 -0
- package/dist/cli/help.js +0 -0
- package/dist/cli/help.js.map +0 -0
- package/dist/cli/omp-config.d.ts +0 -0
- package/dist/cli/omp-config.js +0 -0
- package/dist/cli/omp-config.js.map +0 -0
- package/dist/cli/opencode-config.d.ts +0 -0
- package/dist/cli/opencode-config.js +0 -0
- package/dist/cli/opencode-config.js.map +0 -0
- package/dist/cli/overview.d.ts +0 -0
- package/dist/cli/overview.js +0 -0
- package/dist/cli/overview.js.map +0 -0
- package/dist/cli/pi-config.d.ts +0 -0
- package/dist/cli/pi-config.js +0 -0
- package/dist/cli/pi-config.js.map +0 -0
- package/dist/cli/setup.d.ts +0 -0
- package/dist/cli/setup.js +2 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +0 -0
- package/dist/cli/ui.js.map +1 -1
- package/dist/cli/update-check.d.ts +0 -0
- package/dist/cli/update-check.js +0 -0
- package/dist/cli/update-check.js.map +0 -0
- package/dist/cli/usage.d.ts +0 -0
- package/dist/cli/usage.js +71 -5
- package/dist/cli/usage.js.map +1 -1
- package/dist/config.d.ts +0 -0
- package/dist/config.js +0 -0
- package/dist/config.js.map +0 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +0 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/key-encryption.d.ts +0 -0
- package/dist/key-encryption.js +0 -0
- package/dist/key-encryption.js.map +0 -0
- package/dist/logger.d.ts +0 -0
- package/dist/logger.js +0 -0
- package/dist/logger.js.map +0 -0
- package/dist/model-probe.d.ts +0 -0
- package/dist/model-probe.js +0 -0
- package/dist/model-probe.js.map +0 -0
- package/dist/model-stats.d.ts +0 -0
- package/dist/model-stats.js +0 -0
- package/dist/model-stats.js.map +0 -0
- package/dist/models-fetcher.d.ts +0 -0
- package/dist/models-fetcher.js +0 -0
- package/dist/models-fetcher.js.map +0 -0
- package/dist/models-sync.d.ts +0 -0
- package/dist/models-sync.js +0 -0
- package/dist/models-sync.js.map +0 -0
- package/dist/probe-cache.d.ts +0 -0
- package/dist/probe-cache.js +0 -0
- package/dist/probe-cache.js.map +0 -0
- package/dist/providers/commandcode.d.ts +0 -0
- package/dist/providers/commandcode.js +5 -0
- package/dist/providers/commandcode.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +2 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/infer.d.ts +4 -4
- package/dist/providers/infer.js +0 -0
- package/dist/providers/infer.js.map +0 -0
- package/dist/providers/opencode-go.d.ts +0 -0
- package/dist/providers/opencode-go.js +15 -4
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/types.d.ts +17 -0
- package/dist/providers/types.js +0 -0
- package/dist/providers/types.js.map +0 -0
- package/dist/proxy-logic.d.ts +15 -0
- package/dist/proxy-logic.js +17 -0
- package/dist/proxy-logic.js.map +1 -1
- package/dist/proxy.d.ts +23 -0
- package/dist/proxy.js +137 -46
- package/dist/proxy.js.map +1 -1
- package/dist/token-usage.d.ts +53 -0
- package/dist/token-usage.js +198 -0
- package/dist/token-usage.js.map +1 -0
- package/dist/usage-client.d.ts +25 -4
- package/dist/usage-client.js +43 -12
- package/dist/usage-client.js.map +1 -1
- package/dist/validation.d.ts +4 -4
- package/dist/validation.js +4 -4
- package/dist/validation.js.map +0 -0
- package/package.json +6 -6
- package/dist/dashboard-parser.d.ts +0 -47
- package/dist/dashboard-parser.js +0 -88
- package/dist/dashboard-parser.js.map +0 -1
- package/dist/firefox-cookies.d.ts +0 -42
- package/dist/firefox-cookies.js +0 -170
- package/dist/firefox-cookies.js.map +0 -1
- package/dist/scraper.d.ts +0 -70
- package/dist/scraper.js +0 -239
- package/dist/scraper.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-usage.js","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"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"}
|
package/dist/usage-client.d.ts
CHANGED
|
@@ -23,14 +23,35 @@ export interface KeyUsage {
|
|
|
23
23
|
weeklyResetsAt?: string;
|
|
24
24
|
monthlyResetsAt?: string;
|
|
25
25
|
}
|
|
26
|
+
/** Outcome of one usage fetch. */
|
|
27
|
+
export interface UsageFetchResult {
|
|
28
|
+
/** Parsed usage, or null when the fetch failed / violated the contract. */
|
|
29
|
+
usage: KeyUsage | null;
|
|
30
|
+
/**
|
|
31
|
+
* The upstream rejected the key for lack of plan (403 EntitlementError /
|
|
32
|
+
* upgrade_required). Definitive, not transient: the account cannot use this
|
|
33
|
+
* provider's models at all, so rotation drops the key until a later
|
|
34
|
+
* refresh shows it usable again.
|
|
35
|
+
*/
|
|
36
|
+
noEntitlement: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 403 bodies that mean "this account has no plan for this provider":
|
|
40
|
+
* opencode-go → `{"error":{"type":"EntitlementError","message":"OpenCode Go
|
|
41
|
+
* subscription required."}}`, commandcode → `403 upgrade_required`.
|
|
42
|
+
* Plan-gated *models* (opencode-go's 403 `MODEL_NOT_IN_PLAN`) are a request
|
|
43
|
+
* problem, not an account one, and must not match.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isEntitlementError(status: number, body: string): boolean;
|
|
26
46
|
/** Parse a usage API response body into KeyUsage (throws on contract violation). */
|
|
27
47
|
export declare function parseUsageResponse(body: string): KeyUsage;
|
|
28
48
|
/**
|
|
29
|
-
* Fetch current usage for one key.
|
|
30
|
-
*
|
|
31
|
-
* "
|
|
49
|
+
* Fetch current usage for one key. Failures (network, timeout, non-200,
|
|
50
|
+
* contract violation) yield `usage: null` — callers treat that as "no fresh
|
|
51
|
+
* data". A 403 entitlement rejection is reported separately because it is
|
|
52
|
+
* definitive rather than transient.
|
|
32
53
|
*/
|
|
33
|
-
export declare function fetchKeyUsage(upstreamBaseUrl: string, apiKey: string, usagePath?: string): Promise<
|
|
54
|
+
export declare function fetchKeyUsage(upstreamBaseUrl: string, apiKey: string, usagePath?: string): Promise<UsageFetchResult>;
|
|
34
55
|
/** Test seam: reset coordinator state. */
|
|
35
56
|
export declare function resetUsageRefreshState(): void;
|
|
36
57
|
/**
|
package/dist/usage-client.js
CHANGED
|
@@ -13,8 +13,18 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { logger } from './logger.js';
|
|
15
15
|
import { USAGE_FETCH_TIMEOUT_MS, USAGE_PATH, USAGE_REFRESH_TTL_MS } from './constants.js';
|
|
16
|
-
import { updateKeyUsage } from './proxy-logic.js';
|
|
16
|
+
import { updateKeyUsage, setKeyEntitlement } from './proxy-logic.js';
|
|
17
17
|
import { getProvider, inferProvider } from './providers/index.js';
|
|
18
|
+
/**
|
|
19
|
+
* 403 bodies that mean "this account has no plan for this provider":
|
|
20
|
+
* opencode-go → `{"error":{"type":"EntitlementError","message":"OpenCode Go
|
|
21
|
+
* subscription required."}}`, commandcode → `403 upgrade_required`.
|
|
22
|
+
* Plan-gated *models* (opencode-go's 403 `MODEL_NOT_IN_PLAN`) are a request
|
|
23
|
+
* problem, not an account one, and must not match.
|
|
24
|
+
*/
|
|
25
|
+
export function isEntitlementError(status, body) {
|
|
26
|
+
return status === 403 && /subscription|entitlement|upgrade_required/i.test(body);
|
|
27
|
+
}
|
|
18
28
|
function parsePercent(window) {
|
|
19
29
|
const percent = window?.percent;
|
|
20
30
|
if (typeof percent !== 'number' || !Number.isFinite(percent))
|
|
@@ -48,9 +58,10 @@ export function parseUsageResponse(body) {
|
|
|
48
58
|
};
|
|
49
59
|
}
|
|
50
60
|
/**
|
|
51
|
-
* Fetch current usage for one key.
|
|
52
|
-
*
|
|
53
|
-
* "
|
|
61
|
+
* Fetch current usage for one key. Failures (network, timeout, non-200,
|
|
62
|
+
* contract violation) yield `usage: null` — callers treat that as "no fresh
|
|
63
|
+
* data". A 403 entitlement rejection is reported separately because it is
|
|
64
|
+
* definitive rather than transient.
|
|
54
65
|
*/
|
|
55
66
|
export async function fetchKeyUsage(upstreamBaseUrl, apiKey, usagePath = USAGE_PATH) {
|
|
56
67
|
const controller = new AbortController();
|
|
@@ -63,12 +74,19 @@ export async function fetchKeyUsage(upstreamBaseUrl, apiKey, usagePath = USAGE_P
|
|
|
63
74
|
// Keep the abort armed through the body read: a stalled body after a 200
|
|
64
75
|
// must still time out, or refreshInFlight stays true forever and usage
|
|
65
76
|
// rotation freezes on stale percentages.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
const body = await response.text();
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
return { usage: null, noEntitlement: isEntitlementError(response.status, body) };
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
return { usage: parseUsageResponse(body), noEntitlement: false };
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return { usage: null, noEntitlement: false };
|
|
86
|
+
}
|
|
69
87
|
}
|
|
70
88
|
catch {
|
|
71
|
-
return null;
|
|
89
|
+
return { usage: null, noEntitlement: false };
|
|
72
90
|
}
|
|
73
91
|
finally {
|
|
74
92
|
clearTimeout(timer);
|
|
@@ -107,10 +125,23 @@ export function maybeRefreshUsage(state, config) {
|
|
|
107
125
|
const usage = await fetchKeyUsage(base, k.key, provider.usagePath());
|
|
108
126
|
return [k.label, usage];
|
|
109
127
|
}));
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
128
|
+
const percents = new Map();
|
|
129
|
+
for (const [label, result] of entries) {
|
|
130
|
+
if (!result)
|
|
131
|
+
continue;
|
|
132
|
+
if (result.usage) {
|
|
133
|
+
percents.set(label, result.usage.usedPercent);
|
|
134
|
+
// A key that answers the usage API is entitled again.
|
|
135
|
+
setKeyEntitlement(state, label, true);
|
|
136
|
+
}
|
|
137
|
+
else if (result.noEntitlement) {
|
|
138
|
+
setKeyEntitlement(state, label, false);
|
|
139
|
+
}
|
|
140
|
+
// Transient failures leave the previous entitlement untouched.
|
|
141
|
+
}
|
|
142
|
+
if (percents.size > 0) {
|
|
143
|
+
updateKeyUsage(state, percents);
|
|
144
|
+
logger.debug({ keys: [...percents.keys()] }, 'key usage refreshed');
|
|
114
145
|
}
|
|
115
146
|
}
|
|
116
147
|
catch (err) {
|
package/dist/usage-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-client.js","sourceRoot":"","sources":["../src/usage-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAmB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"usage-client.js","sourceRoot":"","sources":["../src/usage-client.ts"],"names":[],"mappings":"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"}
|
package/dist/validation.d.ts
CHANGED
|
@@ -11,10 +11,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
13
|
*
|
|
14
|
-
* Multi-provider aware: accepts `sk-` (opencode-go /
|
|
15
|
-
* keys) and `user_` (commandcode tokens) of at least
|
|
16
|
-
* characters. Structural check only — provider
|
|
17
|
-
* smoke test in `identifyKey()`.
|
|
14
|
+
* Multi-provider aware: accepts `oc_sk_` (OpenCode-Go), `sk-` (opencode-go /
|
|
15
|
+
* commandcode console keys) and `user_` (commandcode tokens) of at least
|
|
16
|
+
* {@link MIN_KEY_LENGTH} characters. Structural check only — provider
|
|
17
|
+
* identity is confirmed by the smoke test in `identifyKey()`.
|
|
18
18
|
*/
|
|
19
19
|
export declare function isValidApiKey(key: unknown): key is string;
|
|
20
20
|
/**
|
package/dist/validation.js
CHANGED
|
@@ -20,10 +20,10 @@ export function isValidPort(value) {
|
|
|
20
20
|
/**
|
|
21
21
|
* Check if the given string looks like a valid API key.
|
|
22
22
|
*
|
|
23
|
-
* Multi-provider aware: accepts `sk-` (opencode-go /
|
|
24
|
-
* keys) and `user_` (commandcode tokens) of at least
|
|
25
|
-
* characters. Structural check only — provider
|
|
26
|
-
* smoke test in `identifyKey()`.
|
|
23
|
+
* Multi-provider aware: accepts `oc_sk_` (OpenCode-Go), `sk-` (opencode-go /
|
|
24
|
+
* commandcode console keys) and `user_` (commandcode tokens) of at least
|
|
25
|
+
* {@link MIN_KEY_LENGTH} characters. Structural check only — provider
|
|
26
|
+
* identity is confirmed by the smoke test in `identifyKey()`.
|
|
27
27
|
*/
|
|
28
28
|
export function isValidApiKey(key) {
|
|
29
29
|
return typeof key === 'string' && isPlausibleKey(key);
|
package/dist/validation.js.map
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saros-proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Saros — predictive multi-account proxy for the OpenCode-Go API. Cycles between accounts before they exhaust.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -56,16 +56,16 @@
|
|
|
56
56
|
},
|
|
57
57
|
"homepage": "https://github.com/vitorvilaca3011/saros-proxy#readme",
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@types/node": "^26.
|
|
60
|
-
"@vitest/coverage-v8": "^
|
|
59
|
+
"@types/node": "^26.6.4",
|
|
60
|
+
"@vitest/coverage-v8": "^5.0.3",
|
|
61
61
|
"pino-pretty": "^13.1.3",
|
|
62
62
|
"tsx": "^4.23.12",
|
|
63
63
|
"typescript": "^7.0.2",
|
|
64
|
-
"vitest": "^
|
|
64
|
+
"vitest": "^5.0.3"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@clack/prompts": "^1.
|
|
68
|
-
"@hono/node-server": "^2.
|
|
67
|
+
"@clack/prompts": "^1.8.1",
|
|
68
|
+
"@hono/node-server": "^2.1.3",
|
|
69
69
|
"chalk": "^6.0.0",
|
|
70
70
|
"hono": "^4.12.23",
|
|
71
71
|
"hono-rate-limiter": "^0.5.3",
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dashboard-parser.ts — Parse usage percentages from the OpenCode-Go dashboard HTML.
|
|
3
|
-
*
|
|
4
|
-
* The dashboard returns SolidJS SSR hydration data with usage percentages
|
|
5
|
-
* for three time windows: rolling (~5h), weekly, and monthly.
|
|
6
|
-
*
|
|
7
|
-
* Example hydration data:
|
|
8
|
-
* ```
|
|
9
|
-
* rollingUsage:$R[3]={usagePercent:45.2,resetInSec:18000}
|
|
10
|
-
* weeklyUsage:$R[4]={usagePercent:60.5,resetInSec:604800}
|
|
11
|
-
* monthlyUsage:$R[5]={usagePercent:30.0,resetInSec:2592000}
|
|
12
|
-
* ```
|
|
13
|
-
*
|
|
14
|
-
* Field order (usagePercent vs resetInSec) can vary between responses.
|
|
15
|
-
* These functions handle both orderings by trying two regex patterns per window.
|
|
16
|
-
*/
|
|
17
|
-
export interface UsageData {
|
|
18
|
-
rolling: number | null;
|
|
19
|
-
weekly: number | null;
|
|
20
|
-
monthly: number | null;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Parse all three usage windows (rolling, weekly, monthly) from dashboard HTML.
|
|
24
|
-
* Returns null for any window that couldn't be parsed.
|
|
25
|
-
*
|
|
26
|
-
* @param html — Raw HTML from the OpenCode-Go dashboard page.
|
|
27
|
-
* @returns An object with `rolling`, `weekly`, and `monthly` usage percentages,
|
|
28
|
-
* or null for windows that could not be parsed.
|
|
29
|
-
*/
|
|
30
|
-
export declare function parseAllUsage(html: string): UsageData;
|
|
31
|
-
/**
|
|
32
|
-
* Parse a single usage window from dashboard HTML.
|
|
33
|
-
*
|
|
34
|
-
* Tries two regex patterns to handle field order variance
|
|
35
|
-
* (usagePercent before or after resetInSec).
|
|
36
|
-
*
|
|
37
|
-
* Returns null if:
|
|
38
|
-
* - The window data is not found in the HTML
|
|
39
|
-
* - The extracted value is not a finite number
|
|
40
|
-
* - The HTML is malformed or from a non-dashboard page
|
|
41
|
-
*
|
|
42
|
-
* @param html — Raw HTML from the OpenCode-Go dashboard page.
|
|
43
|
-
* @param window — The time window to parse: `'rolling'`, `'weekly'`, or `'monthly'`.
|
|
44
|
-
* @returns The usage percentage as a number (may be negative or exceed 100),
|
|
45
|
-
* or null if the value could not be extracted.
|
|
46
|
-
*/
|
|
47
|
-
export declare function parseUsagePercent(html: string, window: 'rolling' | 'weekly' | 'monthly'): number | null;
|
package/dist/dashboard-parser.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dashboard-parser.ts — Parse usage percentages from the OpenCode-Go dashboard HTML.
|
|
3
|
-
*
|
|
4
|
-
* The dashboard returns SolidJS SSR hydration data with usage percentages
|
|
5
|
-
* for three time windows: rolling (~5h), weekly, and monthly.
|
|
6
|
-
*
|
|
7
|
-
* Example hydration data:
|
|
8
|
-
* ```
|
|
9
|
-
* rollingUsage:$R[3]={usagePercent:45.2,resetInSec:18000}
|
|
10
|
-
* weeklyUsage:$R[4]={usagePercent:60.5,resetInSec:604800}
|
|
11
|
-
* monthlyUsage:$R[5]={usagePercent:30.0,resetInSec:2592000}
|
|
12
|
-
* ```
|
|
13
|
-
*
|
|
14
|
-
* Field order (usagePercent vs resetInSec) can vary between responses.
|
|
15
|
-
* These functions handle both orderings by trying two regex patterns per window.
|
|
16
|
-
*/
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// Regex patterns
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
const SCRAPED_NUMBER_PATTERN = String.raw `(-?\d+(?:\.\d+)?)`;
|
|
21
|
-
const RE_ROLLING_PCT_FIRST = new RegExp(String.raw `rollingUsage:\$R\[\d+\]=\{[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*\}`);
|
|
22
|
-
const RE_ROLLING_RESET_FIRST = new RegExp(String.raw `rollingUsage:\$R\[\d+\]=\{[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*\}`);
|
|
23
|
-
const RE_WEEKLY_PCT_FIRST = new RegExp(String.raw `weeklyUsage:\$R\[\d+\]=\{[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*\}`);
|
|
24
|
-
const RE_WEEKLY_RESET_FIRST = new RegExp(String.raw `weeklyUsage:\$R\[\d+\]=\{[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*\}`);
|
|
25
|
-
const RE_MONTHLY_PCT_FIRST = new RegExp(String.raw `monthlyUsage:\$R\[\d+\]=\{[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*\}`);
|
|
26
|
-
const RE_MONTHLY_RESET_FIRST = new RegExp(String.raw `monthlyUsage:\$R\[\d+\]=\{[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*\}`);
|
|
27
|
-
// Map from window name to its two regex alternatives
|
|
28
|
-
const PATTERNS = {
|
|
29
|
-
rolling: [RE_ROLLING_PCT_FIRST, RE_ROLLING_RESET_FIRST],
|
|
30
|
-
weekly: [RE_WEEKLY_PCT_FIRST, RE_WEEKLY_RESET_FIRST],
|
|
31
|
-
monthly: [RE_MONTHLY_PCT_FIRST, RE_MONTHLY_RESET_FIRST],
|
|
32
|
-
};
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Public API
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
/**
|
|
37
|
-
* Parse all three usage windows (rolling, weekly, monthly) from dashboard HTML.
|
|
38
|
-
* Returns null for any window that couldn't be parsed.
|
|
39
|
-
*
|
|
40
|
-
* @param html — Raw HTML from the OpenCode-Go dashboard page.
|
|
41
|
-
* @returns An object with `rolling`, `weekly`, and `monthly` usage percentages,
|
|
42
|
-
* or null for windows that could not be parsed.
|
|
43
|
-
*/
|
|
44
|
-
export function parseAllUsage(html) {
|
|
45
|
-
return {
|
|
46
|
-
rolling: parseUsagePercent(html, 'rolling'),
|
|
47
|
-
weekly: parseUsagePercent(html, 'weekly'),
|
|
48
|
-
monthly: parseUsagePercent(html, 'monthly'),
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Parse a single usage window from dashboard HTML.
|
|
53
|
-
*
|
|
54
|
-
* Tries two regex patterns to handle field order variance
|
|
55
|
-
* (usagePercent before or after resetInSec).
|
|
56
|
-
*
|
|
57
|
-
* Returns null if:
|
|
58
|
-
* - The window data is not found in the HTML
|
|
59
|
-
* - The extracted value is not a finite number
|
|
60
|
-
* - The HTML is malformed or from a non-dashboard page
|
|
61
|
-
*
|
|
62
|
-
* @param html — Raw HTML from the OpenCode-Go dashboard page.
|
|
63
|
-
* @param window — The time window to parse: `'rolling'`, `'weekly'`, or `'monthly'`.
|
|
64
|
-
* @returns The usage percentage as a number (may be negative or exceed 100),
|
|
65
|
-
* or null if the value could not be extracted.
|
|
66
|
-
*/
|
|
67
|
-
export function parseUsagePercent(html, window) {
|
|
68
|
-
const patterns = PATTERNS[window];
|
|
69
|
-
if (!patterns)
|
|
70
|
-
return null;
|
|
71
|
-
const [rePctFirst, reResetFirst] = patterns;
|
|
72
|
-
// Try usagePercent-first pattern
|
|
73
|
-
const pctMatch = rePctFirst.exec(html);
|
|
74
|
-
if (pctMatch) {
|
|
75
|
-
// pct-first: usagePercent:$1,resetInSec:$2
|
|
76
|
-
const val = Number(pctMatch[1]);
|
|
77
|
-
return Number.isFinite(val) ? val : null;
|
|
78
|
-
}
|
|
79
|
-
// Try resetInSec-first pattern
|
|
80
|
-
const resetMatch = reResetFirst.exec(html);
|
|
81
|
-
if (resetMatch) {
|
|
82
|
-
// reset-first: resetInSec:$1,usagePercent:$2
|
|
83
|
-
const val = Number(resetMatch[2]);
|
|
84
|
-
return Number.isFinite(val) ? val : null;
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
//# sourceMappingURL=dashboard-parser.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-parser.js","sourceRoot":"","sources":["../src/dashboard-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CAAA,mBAAmB,CAAC;AAE7D,MAAM,oBAAoB,GAAG,IAAI,MAAM,CACrC,MAAM,CAAC,GAAG,CAAA,+CAA+C,sBAAsB,mBAAmB,sBAAsB,SAAS,CAClI,CAAC;AACF,MAAM,sBAAsB,GAAG,IAAI,MAAM,CACvC,MAAM,CAAC,GAAG,CAAA,6CAA6C,sBAAsB,qBAAqB,sBAAsB,SAAS,CAClI,CAAC;AACF,MAAM,mBAAmB,GAAG,IAAI,MAAM,CACpC,MAAM,CAAC,GAAG,CAAA,8CAA8C,sBAAsB,mBAAmB,sBAAsB,SAAS,CACjI,CAAC;AACF,MAAM,qBAAqB,GAAG,IAAI,MAAM,CACtC,MAAM,CAAC,GAAG,CAAA,4CAA4C,sBAAsB,qBAAqB,sBAAsB,SAAS,CACjI,CAAC;AACF,MAAM,oBAAoB,GAAG,IAAI,MAAM,CACrC,MAAM,CAAC,GAAG,CAAA,+CAA+C,sBAAsB,mBAAmB,sBAAsB,SAAS,CAClI,CAAC;AACF,MAAM,sBAAsB,GAAG,IAAI,MAAM,CACvC,MAAM,CAAC,GAAG,CAAA,6CAA6C,sBAAsB,qBAAqB,sBAAsB,SAAS,CAClI,CAAC;AAEF,qDAAqD;AACrD,MAAM,QAAQ,GAAqC;IACjD,OAAO,EAAE,CAAC,oBAAoB,EAAE,sBAAsB,CAAC;IACvD,MAAM,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;IACpD,OAAO,EAAE,CAAC,oBAAoB,EAAE,sBAAsB,CAAC;CACxD,CAAC;AAEF,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO;QACL,OAAO,EAAE,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC;QAC3C,MAAM,EAAE,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC;QACzC,OAAO,EAAE,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,MAAwC;IACtF,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC;IAE5C,iCAAiC;IACjC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,QAAQ,EAAE,CAAC;QACb,2CAA2C;QAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,CAAC;IAED,+BAA+B;IAC/B,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,UAAU,EAAE,CAAC;QACf,6CAA6C;QAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* firefox-cookies.ts — Firefox cookie and history extraction.
|
|
3
|
-
*
|
|
4
|
-
* Reads cookies.sqlite and places.sqlite from the default Firefox profile.
|
|
5
|
-
* Uses Node.js built-in node:sqlite (requires Node >= 22).
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Find the default Firefox profile directory on this system.
|
|
9
|
-
*
|
|
10
|
-
* Scans the platform-specific Firefox profiles directory and prefers the
|
|
11
|
-
* `default-release` profile if it exists, otherwise returns the first
|
|
12
|
-
* profile directory found.
|
|
13
|
-
*
|
|
14
|
-
* @returns The path to the Firefox profile directory, or `null` if not found.
|
|
15
|
-
*/
|
|
16
|
-
export declare function findFirefoxProfileDir(): string | null;
|
|
17
|
-
/**
|
|
18
|
-
* Extract the `auth` cookie value for `opencode.ai` from Firefox's cookies.sqlite.
|
|
19
|
-
*
|
|
20
|
-
* Makes a temporary copy of the database (including WAL/SHM sidecars) to avoid
|
|
21
|
-
* locking conflicts with Firefox. The temp copy is cleaned up after extraction.
|
|
22
|
-
*
|
|
23
|
-
* @returns An object with `cookie` (the auth cookie value, or `null` if not found)
|
|
24
|
-
* and an optional `error` message describing what went wrong.
|
|
25
|
-
*/
|
|
26
|
-
export declare function extractFirefoxAuthCookie(): {
|
|
27
|
-
cookie: string | null;
|
|
28
|
-
error?: string;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Extract OpenCode workspace IDs from Firefox's places.sqlite (browser history).
|
|
32
|
-
*
|
|
33
|
-
* Searches `moz_places` for URLs containing `opencode.ai/workspace/` and
|
|
34
|
-
* returns unique workspace IDs (wrk_...) from the most recently visited pages.
|
|
35
|
-
*
|
|
36
|
-
* A temporary copy of the database (including WAL/SHM sidecars) is used to
|
|
37
|
-
* avoid locking conflicts with Firefox, then cleaned up after extraction.
|
|
38
|
-
*
|
|
39
|
-
* @returns An array of unique workspace ID strings (e.g. `["wrk_abc123"]`).
|
|
40
|
-
* Returns an empty array if none are found or if an error occurs.
|
|
41
|
-
*/
|
|
42
|
-
export declare function extractFirefoxWorkspaceIds(): string[];
|
package/dist/firefox-cookies.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* firefox-cookies.ts — Firefox cookie and history extraction.
|
|
3
|
-
*
|
|
4
|
-
* Reads cookies.sqlite and places.sqlite from the default Firefox profile.
|
|
5
|
-
* Uses Node.js built-in node:sqlite (requires Node >= 22).
|
|
6
|
-
*/
|
|
7
|
-
import { existsSync, copyFileSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
|
|
8
|
-
import { join } from 'node:path';
|
|
9
|
-
import { homedir, tmpdir } from 'node:os';
|
|
10
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
11
|
-
import { logger } from './logger.js';
|
|
12
|
-
/**
|
|
13
|
-
* Find the default Firefox profile directory on this system.
|
|
14
|
-
*
|
|
15
|
-
* Scans the platform-specific Firefox profiles directory and prefers the
|
|
16
|
-
* `default-release` profile if it exists, otherwise returns the first
|
|
17
|
-
* profile directory found.
|
|
18
|
-
*
|
|
19
|
-
* @returns The path to the Firefox profile directory, or `null` if not found.
|
|
20
|
-
*/
|
|
21
|
-
export function findFirefoxProfileDir() {
|
|
22
|
-
const platform = process.platform;
|
|
23
|
-
let root;
|
|
24
|
-
if (platform === 'win32') {
|
|
25
|
-
const appData = process.env.APPDATA;
|
|
26
|
-
if (!appData)
|
|
27
|
-
return null;
|
|
28
|
-
root = join(appData, 'Mozilla', 'Firefox', 'Profiles');
|
|
29
|
-
}
|
|
30
|
-
else if (platform === 'darwin') {
|
|
31
|
-
root = join(homedir(), 'Library', 'Application Support', 'Firefox', 'Profiles');
|
|
32
|
-
}
|
|
33
|
-
else if (platform === 'linux') {
|
|
34
|
-
root = join(homedir(), '.mozilla', 'firefox');
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
if (!existsSync(root))
|
|
40
|
-
return null;
|
|
41
|
-
try {
|
|
42
|
-
const profiles = readdirSync(root, { withFileTypes: true })
|
|
43
|
-
.filter(e => e.isDirectory())
|
|
44
|
-
.map(e => e.name);
|
|
45
|
-
if (profiles.length === 0)
|
|
46
|
-
return null;
|
|
47
|
-
const preferred = profiles.find(p => p.includes('default-release')) ?? profiles[0];
|
|
48
|
-
return join(root, preferred);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Extract the `auth` cookie value for `opencode.ai` from Firefox's cookies.sqlite.
|
|
56
|
-
*
|
|
57
|
-
* Makes a temporary copy of the database (including WAL/SHM sidecars) to avoid
|
|
58
|
-
* locking conflicts with Firefox. The temp copy is cleaned up after extraction.
|
|
59
|
-
*
|
|
60
|
-
* @returns An object with `cookie` (the auth cookie value, or `null` if not found)
|
|
61
|
-
* and an optional `error` message describing what went wrong.
|
|
62
|
-
*/
|
|
63
|
-
export function extractFirefoxAuthCookie() {
|
|
64
|
-
const profileDir = findFirefoxProfileDir();
|
|
65
|
-
if (!profileDir)
|
|
66
|
-
return { cookie: null, error: 'Firefox profile not found' };
|
|
67
|
-
const cookiesDb = join(profileDir, 'cookies.sqlite');
|
|
68
|
-
if (!existsSync(cookiesDb))
|
|
69
|
-
return { cookie: null, error: 'cookies.sqlite not found' };
|
|
70
|
-
const tempDir = mkdirSync(join(tmpdir(), 'proto-setup-' + Date.now()), { recursive: true });
|
|
71
|
-
const tempDb = join(tempDir, 'cookies.sqlite');
|
|
72
|
-
try {
|
|
73
|
-
copyFileSync(cookiesDb, tempDb);
|
|
74
|
-
for (const ext of ['-wal', '-shm']) {
|
|
75
|
-
const sidecar = cookiesDb + ext;
|
|
76
|
-
if (existsSync(sidecar)) {
|
|
77
|
-
copyFileSync(sidecar, tempDb + ext);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
const db = new DatabaseSync(tempDb, { readOnly: true });
|
|
81
|
-
try {
|
|
82
|
-
const stmt = db.prepare(`
|
|
83
|
-
SELECT name, value, host
|
|
84
|
-
FROM moz_cookies
|
|
85
|
-
WHERE (host = 'opencode.ai' OR host = '.opencode.ai')
|
|
86
|
-
AND name = 'auth'
|
|
87
|
-
ORDER BY expiry DESC
|
|
88
|
-
LIMIT 1
|
|
89
|
-
`);
|
|
90
|
-
const rows = stmt.all();
|
|
91
|
-
return rows.length > 0
|
|
92
|
-
? { cookie: rows[0].value }
|
|
93
|
-
: { cookie: null, error: 'No auth cookie found for opencode.ai' };
|
|
94
|
-
}
|
|
95
|
-
finally {
|
|
96
|
-
db.close();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
101
|
-
if (msg.includes('EBUSY') || msg.includes('EPERM') || msg.includes('locked')) {
|
|
102
|
-
return { cookie: null, error: 'Firefox has the database locked. Close Firefox and try again.' };
|
|
103
|
-
}
|
|
104
|
-
return { cookie: null, error: msg };
|
|
105
|
-
}
|
|
106
|
-
finally {
|
|
107
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Extract OpenCode workspace IDs from Firefox's places.sqlite (browser history).
|
|
112
|
-
*
|
|
113
|
-
* Searches `moz_places` for URLs containing `opencode.ai/workspace/` and
|
|
114
|
-
* returns unique workspace IDs (wrk_...) from the most recently visited pages.
|
|
115
|
-
*
|
|
116
|
-
* A temporary copy of the database (including WAL/SHM sidecars) is used to
|
|
117
|
-
* avoid locking conflicts with Firefox, then cleaned up after extraction.
|
|
118
|
-
*
|
|
119
|
-
* @returns An array of unique workspace ID strings (e.g. `["wrk_abc123"]`).
|
|
120
|
-
* Returns an empty array if none are found or if an error occurs.
|
|
121
|
-
*/
|
|
122
|
-
export function extractFirefoxWorkspaceIds() {
|
|
123
|
-
const profileDir = findFirefoxProfileDir();
|
|
124
|
-
if (!profileDir)
|
|
125
|
-
return [];
|
|
126
|
-
const placesDb = join(profileDir, 'places.sqlite');
|
|
127
|
-
if (!existsSync(placesDb))
|
|
128
|
-
return [];
|
|
129
|
-
const tempDir = mkdirSync(join(tmpdir(), 'proto-setup-history-' + Date.now()), { recursive: true });
|
|
130
|
-
const tempDb = join(tempDir, 'places.sqlite');
|
|
131
|
-
try {
|
|
132
|
-
copyFileSync(placesDb, tempDb);
|
|
133
|
-
for (const ext of ['-wal', '-shm']) {
|
|
134
|
-
const sidecar = placesDb + ext;
|
|
135
|
-
if (existsSync(sidecar)) {
|
|
136
|
-
copyFileSync(sidecar, tempDb + ext);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const db = new DatabaseSync(tempDb, { readOnly: true });
|
|
140
|
-
try {
|
|
141
|
-
const stmt = db.prepare(`
|
|
142
|
-
SELECT url
|
|
143
|
-
FROM moz_places
|
|
144
|
-
WHERE url LIKE '%opencode.ai/workspace/%'
|
|
145
|
-
ORDER BY last_visit_date DESC
|
|
146
|
-
LIMIT 50
|
|
147
|
-
`);
|
|
148
|
-
const rows = stmt.all();
|
|
149
|
-
const workspaceIds = new Set();
|
|
150
|
-
for (const row of rows) {
|
|
151
|
-
const match = row.url.match(/\/workspace\/(wrk_[A-Za-z0-9]+)/);
|
|
152
|
-
if (match) {
|
|
153
|
-
workspaceIds.add(match[1]);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return Array.from(workspaceIds);
|
|
157
|
-
}
|
|
158
|
-
finally {
|
|
159
|
-
db.close();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
catch (err) {
|
|
163
|
-
logger.warn({ err }, 'Failed to extract Firefox workspace IDs');
|
|
164
|
-
return [];
|
|
165
|
-
}
|
|
166
|
-
finally {
|
|
167
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
//# sourceMappingURL=firefox-cookies.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"firefox-cookies.js","sourceRoot":"","sources":["../src/firefox-cookies.ts"],"names":[],"mappings":"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"}
|