triflux 10.9.7 → 10.9.8
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/hub/account-broker.mjs +45 -7
- package/package.json +1 -1
- package/scripts/setup.mjs +38 -45
package/hub/account-broker.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Singleton export. All state changes create new objects (immutable pattern).
|
|
5
5
|
|
|
6
6
|
import { EventEmitter } from "node:events";
|
|
7
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join, sep } from "node:path";
|
|
10
10
|
import * as z from "zod";
|
|
@@ -55,6 +55,39 @@ const LEASE_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
|
55
55
|
const CIRCUIT_WINDOW_MS = 10 * 60_000; // 10 minutes
|
|
56
56
|
const CIRCUIT_MAX_FAILURES = 3;
|
|
57
57
|
const AUTH_BASE_PATH = join(homedir(), ".claude", "cache", "tfx-hub");
|
|
58
|
+
const STATE_PERSIST_PATH = join(AUTH_BASE_PATH, "broker-state.json");
|
|
59
|
+
|
|
60
|
+
// ── State persistence ────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
function persistState(stateMap) {
|
|
63
|
+
try {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const entries = {};
|
|
66
|
+
for (const [id, acct] of stateMap) {
|
|
67
|
+
// 활성 쿨다운 또는 circuit open만 저장 (불필요한 데이터 제거)
|
|
68
|
+
if (acct.cooldownUntil > now || acct.circuitOpenedAt > 0 || acct.totalSessions > 0) {
|
|
69
|
+
entries[id] = {
|
|
70
|
+
cooldownUntil: acct.cooldownUntil,
|
|
71
|
+
circuitOpenedAt: acct.circuitOpenedAt,
|
|
72
|
+
failureTimestamps: acct.failureTimestamps,
|
|
73
|
+
totalSessions: acct.totalSessions,
|
|
74
|
+
lastUsedAt: acct.lastUsedAt,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
mkdirSync(AUTH_BASE_PATH, { recursive: true });
|
|
79
|
+
writeFileSync(STATE_PERSIST_PATH, JSON.stringify({ ts: now, entries }));
|
|
80
|
+
} catch { /* best-effort */ }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function loadPersistedState() {
|
|
84
|
+
try {
|
|
85
|
+
if (!existsSync(STATE_PERSIST_PATH)) return null;
|
|
86
|
+
return JSON.parse(readFileSync(STATE_PERSIST_PATH, "utf8"));
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
58
91
|
|
|
59
92
|
// ── env var resolution ───────────────────────────────────────────
|
|
60
93
|
|
|
@@ -101,7 +134,11 @@ class AccountBroker extends EventEmitter {
|
|
|
101
134
|
...(parsed.gemini || []).map((a) => ({ ...a, provider: "gemini" })),
|
|
102
135
|
];
|
|
103
136
|
|
|
137
|
+
const persisted = loadPersistedState();
|
|
138
|
+
const pEntries = persisted?.entries || {};
|
|
139
|
+
|
|
104
140
|
for (const account of allAccounts) {
|
|
141
|
+
const saved = pEntries[account.id];
|
|
105
142
|
this.#state.set(account.id, {
|
|
106
143
|
id: account.id,
|
|
107
144
|
provider: account.provider,
|
|
@@ -113,13 +150,12 @@ class AccountBroker extends EventEmitter {
|
|
|
113
150
|
tier: account.tier ?? "unknown",
|
|
114
151
|
busy: false,
|
|
115
152
|
leasedAt: null,
|
|
116
|
-
cooldownUntil: 0,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
circuitOpenedAt: 0,
|
|
153
|
+
cooldownUntil: saved?.cooldownUntil ?? 0,
|
|
154
|
+
failureTimestamps: saved?.failureTimestamps ?? [],
|
|
155
|
+
circuitOpenedAt: saved?.circuitOpenedAt ?? 0,
|
|
120
156
|
circuitTrialInFlight: false,
|
|
121
|
-
lastUsedAt: 0,
|
|
122
|
-
totalSessions: 0,
|
|
157
|
+
lastUsedAt: saved?.lastUsedAt ?? 0,
|
|
158
|
+
totalSessions: saved?.totalSessions ?? 0,
|
|
123
159
|
});
|
|
124
160
|
}
|
|
125
161
|
}
|
|
@@ -343,6 +379,7 @@ class AccountBroker extends EventEmitter {
|
|
|
343
379
|
};
|
|
344
380
|
|
|
345
381
|
this.#state.set(accountId, updated);
|
|
382
|
+
persistState(this.#state);
|
|
346
383
|
this.emit("release", { id: accountId, ok });
|
|
347
384
|
}
|
|
348
385
|
|
|
@@ -357,6 +394,7 @@ class AccountBroker extends EventEmitter {
|
|
|
357
394
|
leasedAt: null,
|
|
358
395
|
cooldownUntil: Date.now() + coolMs,
|
|
359
396
|
});
|
|
397
|
+
persistState(this.#state);
|
|
360
398
|
}
|
|
361
399
|
|
|
362
400
|
// ── snapshot ──────────────────────────────────────────────────
|
package/package.json
CHANGED
package/scripts/setup.mjs
CHANGED
|
@@ -64,6 +64,43 @@ const REQUIRED_CODEX_PROFILES = [
|
|
|
64
64
|
|
|
65
65
|
const HUD_SYNC_EXCLUDES = new Set(["omc-hud.mjs", "omc-hud.mjs.bak"]);
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* hub/workers/*.mjs + hub/ 루트의 worker 의존성 파일을 자동 스캔.
|
|
69
|
+
* 수동 리스트 대신 glob으로 탐색하여 파일 추가 시 sync 누락 방지.
|
|
70
|
+
*/
|
|
71
|
+
function scanHubWorkerFiles(pluginRoot, claudeDir) {
|
|
72
|
+
const results = [];
|
|
73
|
+
const hubRoot = join(pluginRoot, "hub");
|
|
74
|
+
if (!existsSync(hubRoot)) return results;
|
|
75
|
+
|
|
76
|
+
// hub/workers/*.mjs 전체
|
|
77
|
+
const workersDir = join(hubRoot, "workers");
|
|
78
|
+
if (existsSync(workersDir)) {
|
|
79
|
+
for (const f of readdirSync(workersDir).sort()) {
|
|
80
|
+
if (!f.endsWith(".mjs")) continue;
|
|
81
|
+
results.push({
|
|
82
|
+
src: join(workersDir, f),
|
|
83
|
+
dst: join(claudeDir, "scripts", "hub", "workers", f),
|
|
84
|
+
label: `hub/workers/${f}`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// hub/ 루트: worker가 import하는 의존성 (cli-adapter-base, platform 등)
|
|
90
|
+
const hubRootDeps = ["cli-adapter-base.mjs", "platform.mjs", "account-broker.mjs"];
|
|
91
|
+
for (const f of hubRootDeps) {
|
|
92
|
+
if (existsSync(join(hubRoot, f))) {
|
|
93
|
+
results.push({
|
|
94
|
+
src: join(hubRoot, f),
|
|
95
|
+
dst: join(claudeDir, "scripts", "hub", f),
|
|
96
|
+
label: `hub/${f}`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
|
|
67
104
|
function scanHudFiles(pluginRoot, claudeDir) {
|
|
68
105
|
const hudRoot = join(pluginRoot, "hud");
|
|
69
106
|
if (!existsSync(hudRoot)) return [];
|
|
@@ -124,51 +161,7 @@ const SYNC_MAP = [
|
|
|
124
161
|
dst: join(CLAUDE_DIR, "scripts", "tfx-route-worker.mjs"),
|
|
125
162
|
label: "tfx-route-worker.mjs",
|
|
126
163
|
},
|
|
127
|
-
|
|
128
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "codex-mcp.mjs"),
|
|
129
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "codex-mcp.mjs"),
|
|
130
|
-
label: "hub/workers/codex-mcp.mjs",
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "delegator-mcp.mjs"),
|
|
134
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "delegator-mcp.mjs"),
|
|
135
|
-
label: "hub/workers/delegator-mcp.mjs",
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "interface.mjs"),
|
|
139
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "interface.mjs"),
|
|
140
|
-
label: "hub/workers/interface.mjs",
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "gemini-worker.mjs"),
|
|
144
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "gemini-worker.mjs"),
|
|
145
|
-
label: "hub/workers/gemini-worker.mjs",
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "claude-worker.mjs"),
|
|
149
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "claude-worker.mjs"),
|
|
150
|
-
label: "hub/workers/claude-worker.mjs",
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "worker-utils.mjs"),
|
|
154
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "worker-utils.mjs"),
|
|
155
|
-
label: "hub/workers/worker-utils.mjs",
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
src: join(PLUGIN_ROOT, "hub", "workers", "factory.mjs"),
|
|
159
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "factory.mjs"),
|
|
160
|
-
label: "hub/workers/factory.mjs",
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
src: join(PLUGIN_ROOT, "hub", "cli-adapter-base.mjs"),
|
|
164
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "cli-adapter-base.mjs"),
|
|
165
|
-
label: "hub/cli-adapter-base.mjs",
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
src: join(PLUGIN_ROOT, "hub", "platform.mjs"),
|
|
169
|
-
dst: join(CLAUDE_DIR, "scripts", "hub", "platform.mjs"),
|
|
170
|
-
label: "hub/platform.mjs",
|
|
171
|
-
},
|
|
164
|
+
...scanHubWorkerFiles(PLUGIN_ROOT, CLAUDE_DIR),
|
|
172
165
|
...scanHudFiles(PLUGIN_ROOT, CLAUDE_DIR),
|
|
173
166
|
{
|
|
174
167
|
src: join(PLUGIN_ROOT, "scripts", "notion-read.mjs"),
|