@cartanova/qgrid-cli 1.5.6 → 1.6.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/bundle/dist/application/qgrid/oauth.js +1 -1
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +167 -0
- package/bundle/dist/application/qgrid/qgrid.frame.js +45 -37
- package/bundle/dist/application/qgrid/qgrid.types.js +6 -16
- package/bundle/dist/application/queries.generated.js +2 -1
- package/bundle/dist/application/request-log/request-log.model.js +15 -2
- package/bundle/dist/application/request-log/request-log.types.js +6 -2
- package/bundle/dist/application/sonamu.generated.js +14 -6
- package/bundle/dist/application/sonamu.generated.sso.js +5 -2
- package/bundle/dist/i18n/sd.generated.js +7 -4
- package/bundle/dist/migrations/20260423180818_alter_request_logs_add1_alter1.js +17 -0
- package/bundle/dist/migrations/20260423182017_alter_request_logs_add2_drop1_alter1.js +19 -0
- package/bundle/dist/migrations/20260423183613_alter_request_logs_add1_alter1.js +16 -0
- package/bundle/dist/sonamu.config.js +6 -9
- package/bundle/src/application/qgrid/oauth.ts +1 -1
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +184 -0
- package/bundle/src/application/qgrid/qgrid.frame.ts +38 -51
- package/bundle/src/application/qgrid/qgrid.types.ts +3 -14
- package/bundle/src/application/queries.generated.ts +3 -0
- package/bundle/src/application/request-log/request-log.entity.json +33 -17
- package/bundle/src/application/request-log/request-log.model.ts +26 -2
- package/bundle/src/application/request-log/request-log.types.ts +6 -2
- package/bundle/src/application/sonamu.generated.http +17 -2
- package/bundle/src/application/sonamu.generated.sso.ts +9 -6
- package/bundle/src/application/sonamu.generated.ts +25 -5
- package/bundle/src/i18n/sd.generated.ts +6 -3
- package/bundle/src/migrations/20260423180818_alter_request_logs_add1_alter1.ts +18 -0
- package/bundle/src/migrations/20260423182017_alter_request_logs_add2_drop1_alter1.ts +20 -0
- package/bundle/src/migrations/20260423183613_alter_request_logs_add1_alter1.ts +15 -0
- package/bundle/src/sonamu.config.ts +4 -7
- package/bundle/web-dist/client/assets/index-BfL_bTVI.css +1 -0
- package/bundle/web-dist/client/assets/index-CMOHJBal.js +78 -0
- package/bundle/web-dist/client/assets/logs-B2pDXaji.js +1 -0
- package/bundle/web-dist/client/assets/{routes-CqzIoGYg.js → routes-fpJMuLAs.js} +1 -1
- package/bundle/web-dist/client/assets/{sd.generated-Duipc8HN.js → sd.generated-CIOZewtL.js} +1 -1
- package/bundle/web-dist/client/assets/services.generated-B2N0iPav.js +1 -0
- package/bundle/web-dist/client/assets/{show-BuMxiSr1.js → show-DFVBcmXm.js} +2 -2
- package/bundle/web-dist/client/assets/tokens--LyDCm10.js +1 -0
- package/bundle/web-dist/client/index.html +3 -3
- package/bundle/web-dist/server/assets/{copy-BPyN_0Dp.js → copy-CKCGxwII.js} +1 -1
- package/bundle/web-dist/server/assets/{lazyRouteComponent-Bmjwy0OA.js → lazyRouteComponent-BlvOfJIF.js} +2 -2
- package/bundle/web-dist/server/assets/{logs-BgcF7jVg.js → logs-BYIBD0yv.js} +172 -80
- package/bundle/web-dist/server/assets/{routes-DVA-nsZ-.js → routes-p2n4SfAB.js} +2 -2
- package/bundle/web-dist/server/assets/{sd.generated-CDVQy4CJ.js → sd.generated-CIg_jf8e.js} +6 -3
- package/bundle/web-dist/server/assets/{services.generated-DWyvb1vb.js → services.generated-DIeiDdqy.js} +21 -4
- package/bundle/web-dist/server/assets/{shim-CqVZY710.js → shim-Dq_Izqnc.js} +1 -1
- package/bundle/web-dist/server/assets/{show-BnKnfNFP.js → show-DZ_U1u-N.js} +30 -9
- package/bundle/web-dist/server/assets/{tokens-7z55hPRV.js → tokens-DOybPnNU.js} +19 -21
- package/bundle/web-dist/server/entry-server.generated.js +12 -8
- package/package.json +1 -1
- package/bundle/dist/application/qgrid/pool.js +0 -128
- package/bundle/dist/application/qgrid/worker.js +0 -189
- package/bundle/src/application/qgrid/pool.ts +0 -165
- package/bundle/src/application/qgrid/worker.ts +0 -240
- package/bundle/web-dist/client/assets/index-BRZCFaFz.css +0 -1
- package/bundle/web-dist/client/assets/index-BnqlZB1V.js +0 -78
- package/bundle/web-dist/client/assets/logs-KoJtGoEG.js +0 -1
- package/bundle/web-dist/client/assets/services.generated-Ccs6tru4.js +0 -1
- package/bundle/web-dist/client/assets/tokens--z-eVto5.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-
|
|
3
|
-
import { t as require_shim } from "./assets/shim-
|
|
1
|
+
import { B as hashKey, C as require_react_dom, D as require_react, E as require_jsx_runtime, F as onlineManager, G as noop$1, I as notifyManager, K as partialMatchKey, M as infiniteQueryBehavior, N as Query, O as Mutation, Q as skipToken, U as matchMutation, V as hashQueryKeyByOptions, W as matchQuery, Y as resolveStaleTime, at as __require, b as object, d as ZodObject, f as ZodOptional, l as ZodArray, n as SUPPORTED_LOCALES, nt as Subscribable, ot as __toESM, r as setLocale, rt as __commonJSMin, t as SD, tt as focusManager, u as ZodNullable, w as QueryClientProvider, x as string, y as number, z as functionalUpdate } from "./assets/sd.generated-CIg_jf8e.js";
|
|
2
|
+
import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-BlvOfJIF.js";
|
|
3
|
+
import { t as require_shim } from "./assets/shim-Dq_Izqnc.js";
|
|
4
4
|
//#region ../../node_modules/.pnpm/@tanstack+query-core@5.95.2/node_modules/@tanstack/query-core/build/modern/hydration.js
|
|
5
5
|
function defaultTransformerFn(data) {
|
|
6
6
|
return data;
|
|
@@ -44599,23 +44599,27 @@ function RootComponent() {
|
|
|
44599
44599
|
}
|
|
44600
44600
|
//#endregion
|
|
44601
44601
|
//#region src/routes/tokens.tsx
|
|
44602
|
-
var $$splitComponentImporter$3 = () => import("./assets/tokens-
|
|
44602
|
+
var $$splitComponentImporter$3 = () => import("./assets/tokens-DOybPnNU.js");
|
|
44603
44603
|
var Route$3 = createFileRoute("/tokens")({ component: lazyRouteComponent($$splitComponentImporter$3, "component") });
|
|
44604
44604
|
//#endregion
|
|
44605
44605
|
//#region src/routes/logs.tsx
|
|
44606
|
-
var $$splitComponentImporter$2 = () => import("./assets/logs-
|
|
44607
|
-
var logsSearchSchema = object({
|
|
44606
|
+
var $$splitComponentImporter$2 = () => import("./assets/logs-BYIBD0yv.js");
|
|
44607
|
+
var logsSearchSchema = object({
|
|
44608
|
+
token: string().optional(),
|
|
44609
|
+
project: string().optional(),
|
|
44610
|
+
page: number().optional().default(1)
|
|
44611
|
+
});
|
|
44608
44612
|
var Route$2 = createFileRoute("/logs")({
|
|
44609
44613
|
validateSearch: logsSearchSchema,
|
|
44610
44614
|
component: lazyRouteComponent($$splitComponentImporter$2, "component")
|
|
44611
44615
|
});
|
|
44612
44616
|
//#endregion
|
|
44613
44617
|
//#region src/routes/index.tsx
|
|
44614
|
-
var $$splitComponentImporter$1 = () => import("./assets/routes-
|
|
44618
|
+
var $$splitComponentImporter$1 = () => import("./assets/routes-p2n4SfAB.js");
|
|
44615
44619
|
var Route$1 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter$1, "component") });
|
|
44616
44620
|
//#endregion
|
|
44617
44621
|
//#region src/routes/requests/show.tsx
|
|
44618
|
-
var $$splitComponentImporter = () => import("./assets/show-
|
|
44622
|
+
var $$splitComponentImporter = () => import("./assets/show-DZ_U1u-N.js");
|
|
44619
44623
|
var showSearchSchema = object({ id: number() });
|
|
44620
44624
|
var Route = createFileRoute("/requests/show")({
|
|
44621
44625
|
validateSearch: showSearchSchema,
|
package/package.json
CHANGED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import "../../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { QuotaError } from "./qgrid.types.js";
|
|
3
|
-
import { Worker } from "./worker.js";
|
|
4
|
-
//#region src/application/qgrid/pool.ts
|
|
5
|
-
var ClaudePool = class {
|
|
6
|
-
workers = /* @__PURE__ */ new Map();
|
|
7
|
-
tokenNames = /* @__PURE__ */ new Map();
|
|
8
|
-
quotaExhausted = /* @__PURE__ */ new Set();
|
|
9
|
-
requestCounts = /* @__PURE__ */ new Map();
|
|
10
|
-
lastUsedToken = "";
|
|
11
|
-
lastUsedWorkerIndex = 0;
|
|
12
|
-
size;
|
|
13
|
-
model;
|
|
14
|
-
timeout;
|
|
15
|
-
cwd;
|
|
16
|
-
maxCalls;
|
|
17
|
-
constructor(config) {
|
|
18
|
-
this.size = config.size ?? 3;
|
|
19
|
-
this.model = config.model ?? "sonnet";
|
|
20
|
-
this.timeout = config.timeout ?? 6e5;
|
|
21
|
-
this.cwd = config.cwd ?? "/tmp/qgrid";
|
|
22
|
-
this.maxCalls = config.maxCalls ?? 500;
|
|
23
|
-
config.tokens.forEach(({ token, name }) => {
|
|
24
|
-
this.createWorkers(token, name);
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
getLastUsedWorkerName() {
|
|
28
|
-
return `${this.tokenNames.get(this.lastUsedToken) ?? "Unknown"}-${this.lastUsedWorkerIndex}`;
|
|
29
|
-
}
|
|
30
|
-
getStats() {
|
|
31
|
-
return [...this.workers.keys()].map((token) => ({
|
|
32
|
-
token,
|
|
33
|
-
name: this.tokenNames.get(token) ?? "Unknown",
|
|
34
|
-
requests: this.requestCounts.get(token) ?? 0,
|
|
35
|
-
active: !this.quotaExhausted.has(token)
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
tokenRrIndex = 0;
|
|
39
|
-
workerRrIndexes = /* @__PURE__ */ new Map();
|
|
40
|
-
selectWorker() {
|
|
41
|
-
const availableTokens = [...this.workers.entries()].filter(([token]) => !this.quotaExhausted.has(token));
|
|
42
|
-
if (availableTokens.length === 0) return null;
|
|
43
|
-
const tokenDepths = availableTokens.map(([token, workers]) => ({
|
|
44
|
-
token,
|
|
45
|
-
workers,
|
|
46
|
-
totalDepth: workers.reduce((sum, w) => sum + w.getQueueDepth(), 0)
|
|
47
|
-
}));
|
|
48
|
-
const minTokenDepth = Math.min(...tokenDepths.map((t) => t.totalDepth));
|
|
49
|
-
const idleTokens = tokenDepths.filter((t) => t.totalDepth === minTokenDepth);
|
|
50
|
-
const selected = idleTokens[this.tokenRrIndex % idleTokens.length];
|
|
51
|
-
this.tokenRrIndex++;
|
|
52
|
-
const minDepth = Math.min(...selected.workers.map((w) => w.getQueueDepth()));
|
|
53
|
-
const idleWorkers = selected.workers.filter((w) => w.getQueueDepth() === minDepth);
|
|
54
|
-
const wrIdx = this.workerRrIndexes.get(selected.token) ?? 0;
|
|
55
|
-
const picked = idleWorkers[wrIdx % idleWorkers.length];
|
|
56
|
-
this.workerRrIndexes.set(selected.token, wrIdx + 1);
|
|
57
|
-
return picked;
|
|
58
|
-
}
|
|
59
|
-
async query(input, timeoutMs) {
|
|
60
|
-
const triedTokens = /* @__PURE__ */ new Set();
|
|
61
|
-
while (true) {
|
|
62
|
-
const worker = this.selectWorker();
|
|
63
|
-
if (!worker) throw new QuotaError("All tokens exhausted");
|
|
64
|
-
if (triedTokens.has(worker.tokenId)) throw new QuotaError("All tokens exhausted");
|
|
65
|
-
const workerName = this.tokenNames.get(worker.tokenId) ?? "Unknown";
|
|
66
|
-
const tokenWorkers = this.workers.get(worker.tokenId);
|
|
67
|
-
const workerIdx = tokenWorkers ? tokenWorkers.indexOf(worker) + 1 : 0;
|
|
68
|
-
console.log(`[qgrid] → ${workerName}-${workerIdx} (queue: ${worker.getQueueDepth()})`);
|
|
69
|
-
try {
|
|
70
|
-
const result = await worker.query(input, timeoutMs);
|
|
71
|
-
this.lastUsedToken = worker.tokenId;
|
|
72
|
-
this.lastUsedWorkerIndex = workerIdx;
|
|
73
|
-
this.requestCounts.set(worker.tokenId, (this.requestCounts.get(worker.tokenId) ?? 0) + 1);
|
|
74
|
-
return result;
|
|
75
|
-
} catch (err) {
|
|
76
|
-
if (err instanceof QuotaError) {
|
|
77
|
-
this.quotaExhausted.add(worker.tokenId);
|
|
78
|
-
triedTokens.add(worker.tokenId);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
throw err;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
kill() {
|
|
86
|
-
[...this.workers.values()].flat().forEach((w) => {
|
|
87
|
-
w.kill();
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
createWorkers(token, name) {
|
|
91
|
-
if (this.workers.has(token)) return;
|
|
92
|
-
this.tokenNames.set(token, name);
|
|
93
|
-
const workerConfig = {
|
|
94
|
-
token,
|
|
95
|
-
model: this.model,
|
|
96
|
-
timeout: this.timeout,
|
|
97
|
-
cwd: this.cwd,
|
|
98
|
-
maxCalls: this.maxCalls
|
|
99
|
-
};
|
|
100
|
-
const workers = Array.from({ length: this.size }, () => new Worker(workerConfig));
|
|
101
|
-
this.workers.set(token, workers);
|
|
102
|
-
this.requestCounts.set(token, 0);
|
|
103
|
-
}
|
|
104
|
-
destroyWorkers(token) {
|
|
105
|
-
const workers = this.workers.get(token);
|
|
106
|
-
if (!workers) return;
|
|
107
|
-
workers.forEach((w) => {
|
|
108
|
-
w.kill();
|
|
109
|
-
});
|
|
110
|
-
this.workers.delete(token);
|
|
111
|
-
this.tokenNames.delete(token);
|
|
112
|
-
this.quotaExhausted.delete(token);
|
|
113
|
-
this.requestCounts.delete(token);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
let pool = null;
|
|
117
|
-
function initPool(tokens) {
|
|
118
|
-
pool = new ClaudePool({ tokens });
|
|
119
|
-
return pool;
|
|
120
|
-
}
|
|
121
|
-
function getPool() {
|
|
122
|
-
if (!pool) throw new Error("Pool not initialized. Call initPool() first.");
|
|
123
|
-
return pool;
|
|
124
|
-
}
|
|
125
|
-
//#endregion
|
|
126
|
-
export { ClaudePool, getPool, initPool };
|
|
127
|
-
|
|
128
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9vbC5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBwbGljYXRpb24vcWdyaWQvcG9vbC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENsYXVkZVBvb2wg4oCUIOupgO2LsCDthqDtgbAg7ZSE66Gc7IS47IqkIO2SgC5cbiAqXG4gKiAyLWxheWVyIOudvOyasO2MhTog7Yag7YGwIOyEoO2DnSAobGVhc3QtdG90YWwtZGVwdGggKyByb3VuZC1yb2Jpbikg4oaSIOybjOy7pCDshKDtg51cbiAqIC0gTWFwPHRva2VuLCBXb3JrZXJbXT7roZwg7Yag7YGw67OEIOybjOy7pCDqtIDrpqxcbiAqIC0g7Yag7YGwIOqwhCBxdW90YSDqt6Drk7Eg7IaM7KeEXG4gKi9cbmltcG9ydCB7IHR5cGUgQ2xpUmVzdWx0LCB0eXBlIFBvb2xDb25maWcsIHR5cGUgUXVlcnlJbnB1dCwgdHlwZSBUb2tlblN0YXRzIH0gZnJvbSBcIi4vcWdyaWQudHlwZXNcIjtcbmltcG9ydCB7IFF1b3RhRXJyb3IgfSBmcm9tIFwiLi9xZ3JpZC50eXBlc1wiO1xuaW1wb3J0IHsgV29ya2VyLCB0eXBlIFdvcmtlckNvbmZpZyB9IGZyb20gXCIuL3dvcmtlclwiO1xuXG5leHBvcnQgY2xhc3MgQ2xhdWRlUG9vbCB7XG4gIHdvcmtlcnMgPSBuZXcgTWFwPHN0cmluZywgV29ya2VyW10+KCk7XG4gIHRva2VuTmFtZXMgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICBxdW90YUV4aGF1c3RlZCA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICByZXF1ZXN0Q291bnRzID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgbGFzdFVzZWRUb2tlbiA9IFwiXCI7XG4gIGxhc3RVc2VkV29ya2VySW5kZXggPSAwO1xuICBzaXplOiBudW1iZXI7XG4gIG1vZGVsOiBzdHJpbmc7XG4gIHRpbWVvdXQ6IG51bWJlcjtcbiAgY3dkOiBzdHJpbmc7XG4gIG1heENhbGxzOiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3IoY29uZmlnOiBQb29sQ29uZmlnKSB7XG4gICAgdGhpcy5zaXplID0gY29uZmlnLnNpemUgPz8gMztcbiAgICB0aGlzLm1vZGVsID0gY29uZmlnLm1vZGVsID8/IFwic29ubmV0XCI7XG4gICAgdGhpcy50aW1lb3V0ID0gY29uZmlnLnRpbWVvdXQgPz8gNjAwXzAwMDtcbiAgICB0aGlzLmN3ZCA9IGNvbmZpZy5jd2QgPz8gXCIvdG1wL3FncmlkXCI7XG4gICAgdGhpcy5tYXhDYWxscyA9IGNvbmZpZy5tYXhDYWxscyA/PyA1MDA7XG5cbiAgICBjb25maWcudG9rZW5zLmZvckVhY2goKHsgdG9rZW4sIG5hbWUgfSkgPT4ge1xuICAgICAgdGhpcy5jcmVhdGVXb3JrZXJzKHRva2VuLCBuYW1lKTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldExhc3RVc2VkV29ya2VyTmFtZSgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGJhc2VOYW1lID0gdGhpcy50b2tlbk5hbWVzLmdldCh0aGlzLmxhc3RVc2VkVG9rZW4pID8/IFwiVW5rbm93blwiO1xuICAgIHJldHVybiBgJHtiYXNlTmFtZX0tJHt0aGlzLmxhc3RVc2VkV29ya2VySW5kZXh9YDtcbiAgfVxuXG4gIGdldFN0YXRzKCk6IFRva2VuU3RhdHNbXSB7XG4gICAgcmV0dXJuIFsuLi50aGlzLndvcmtlcnMua2V5cygpXS5tYXAoKHRva2VuKSA9PiAoe1xuICAgICAgdG9rZW4sXG4gICAgICBuYW1lOiB0aGlzLnRva2VuTmFtZXMuZ2V0KHRva2VuKSA/PyBcIlVua25vd25cIixcbiAgICAgIHJlcXVlc3RzOiB0aGlzLnJlcXVlc3RDb3VudHMuZ2V0KHRva2VuKSA/PyAwLFxuICAgICAgYWN0aXZlOiAhdGhpcy5xdW90YUV4aGF1c3RlZC5oYXModG9rZW4pLFxuICAgIH0pKTtcbiAgfVxuXG4gIHByaXZhdGUgdG9rZW5SckluZGV4ID0gMDtcbiAgcHJpdmF0ZSB3b3JrZXJSckluZGV4ZXMgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xuXG4gIHNlbGVjdFdvcmtlcigpOiBXb3JrZXIgfCBudWxsIHtcbiAgICBjb25zdCBhdmFpbGFibGVUb2tlbnMgPSBbLi4udGhpcy53b3JrZXJzLmVudHJpZXMoKV0uZmlsdGVyKFxuICAgICAgKFt0b2tlbl0pID0+ICF0aGlzLnF1b3RhRXhoYXVzdGVkLmhhcyh0b2tlbiksXG4gICAgKTtcblxuICAgIGlmIChhdmFpbGFibGVUb2tlbnMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcblxuICAgIC8vIDHri6jqs4Q6IO2GoO2BsOuzhCDstJ0g7YGQIGRlcHRo6rCAIOqwgOyepSDrgq7snYAg7Yag7YGwIOyEoO2DnVxuICAgIGNvbnN0IHRva2VuRGVwdGhzID0gYXZhaWxhYmxlVG9rZW5zLm1hcCgoW3Rva2VuLCB3b3JrZXJzXSkgPT4gKHtcbiAgICAgIHRva2VuLFxuICAgICAgd29ya2VycyxcbiAgICAgIHRvdGFsRGVwdGg6IHdvcmtlcnMucmVkdWNlKChzdW0sIHcpID0+IHN1bSArIHcuZ2V0UXVldWVEZXB0aCgpLCAwKSxcbiAgICB9KSk7XG4gICAgY29uc3QgbWluVG9rZW5EZXB0aCA9IE1hdGgubWluKC4uLnRva2VuRGVwdGhzLm1hcCgodCkgPT4gdC50b3RhbERlcHRoKSk7XG4gICAgY29uc3QgaWRsZVRva2VucyA9IHRva2VuRGVwdGhzLmZpbHRlcigodCkgPT4gdC50b3RhbERlcHRoID09PSBtaW5Ub2tlbkRlcHRoKTtcbiAgICBjb25zdCBzZWxlY3RlZCA9IGlkbGVUb2tlbnNbdGhpcy50b2tlblJySW5kZXggJSBpZGxlVG9rZW5zLmxlbmd0aF0hO1xuICAgIHRoaXMudG9rZW5SckluZGV4Kys7XG5cbiAgICAvLyAy64uo6rOEOiDthqDtgbAg64K07JeQ7IScIGxlYXN0LWRlcHRoIOybjOy7pCwg64+Z66Wg7J2066m0IHJvdW5kLXJvYmluXG4gICAgY29uc3QgbWluRGVwdGggPSBNYXRoLm1pbiguLi5zZWxlY3RlZC53b3JrZXJzLm1hcCgodykgPT4gdy5nZXRRdWV1ZURlcHRoKCkpKTtcbiAgICBjb25zdCBpZGxlV29ya2VycyA9IHNlbGVjdGVkLndvcmtlcnMuZmlsdGVyKCh3KSA9PiB3LmdldFF1ZXVlRGVwdGgoKSA9PT0gbWluRGVwdGgpO1xuICAgIGNvbnN0IHdySWR4ID0gdGhpcy53b3JrZXJSckluZGV4ZXMuZ2V0KHNlbGVjdGVkLnRva2VuKSA/PyAwO1xuICAgIGNvbnN0IHBpY2tlZCA9IGlkbGVXb3JrZXJzW3dySWR4ICUgaWRsZVdvcmtlcnMubGVuZ3RoXSE7XG4gICAgdGhpcy53b3JrZXJSckluZGV4ZXMuc2V0KHNlbGVjdGVkLnRva2VuLCB3cklkeCArIDEpO1xuXG4gICAgcmV0dXJuIHBpY2tlZDtcbiAgfVxuXG4gIGFzeW5jIHF1ZXJ5KGlucHV0OiBRdWVyeUlucHV0LCB0aW1lb3V0TXM/OiBudW1iZXIpOiBQcm9taXNlPENsaVJlc3VsdD4ge1xuICAgIGNvbnN0IHRyaWVkVG9rZW5zID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgY29uc3Qgd29ya2VyID0gdGhpcy5zZWxlY3RXb3JrZXIoKTtcbiAgICAgIGlmICghd29ya2VyKSB7XG4gICAgICAgIHRocm93IG5ldyBRdW90YUVycm9yKFwiQWxsIHRva2VucyBleGhhdXN0ZWRcIik7XG4gICAgICB9XG5cbiAgICAgIGlmICh0cmllZFRva2Vucy5oYXMod29ya2VyLnRva2VuSWQpKSB7XG4gICAgICAgIHRocm93IG5ldyBRdW90YUVycm9yKFwiQWxsIHRva2VucyBleGhhdXN0ZWRcIik7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHdvcmtlck5hbWUgPSB0aGlzLnRva2VuTmFtZXMuZ2V0KHdvcmtlci50b2tlbklkKSA/PyBcIlVua25vd25cIjtcbiAgICAgIGNvbnN0IHRva2VuV29ya2VycyA9IHRoaXMud29ya2Vycy5nZXQod29ya2VyLnRva2VuSWQpO1xuICAgICAgY29uc3Qgd29ya2VySWR4ID0gdG9rZW5Xb3JrZXJzID8gdG9rZW5Xb3JrZXJzLmluZGV4T2Yod29ya2VyKSArIDEgOiAwO1xuICAgICAgY29uc29sZS5sb2coYFtxZ3JpZF0g4oaSICR7d29ya2VyTmFtZX0tJHt3b3JrZXJJZHh9IChxdWV1ZTogJHt3b3JrZXIuZ2V0UXVldWVEZXB0aCgpfSlgKTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgd29ya2VyLnF1ZXJ5KGlucHV0LCB0aW1lb3V0TXMpO1xuICAgICAgICB0aGlzLmxhc3RVc2VkVG9rZW4gPSB3b3JrZXIudG9rZW5JZDtcbiAgICAgICAgdGhpcy5sYXN0VXNlZFdvcmtlckluZGV4ID0gd29ya2VySWR4O1xuICAgICAgICB0aGlzLnJlcXVlc3RDb3VudHMuc2V0KHdvcmtlci50b2tlbklkLCAodGhpcy5yZXF1ZXN0Q291bnRzLmdldCh3b3JrZXIudG9rZW5JZCkgPz8gMCkgKyAxKTtcblxuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBRdW90YUVycm9yKSB7XG4gICAgICAgICAgdGhpcy5xdW90YUV4aGF1c3RlZC5hZGQod29ya2VyLnRva2VuSWQpO1xuICAgICAgICAgIHRyaWVkVG9rZW5zLmFkZCh3b3JrZXIudG9rZW5JZCk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGtpbGwoKTogdm9pZCB7XG4gICAgWy4uLnRoaXMud29ya2Vycy52YWx1ZXMoKV0uZmxhdCgpLmZvckVhY2goKHcpID0+IHtcbiAgICAgIHcua2lsbCgpO1xuICAgIH0pO1xuICB9XG5cbiAgY3JlYXRlV29ya2Vycyh0b2tlbjogc3RyaW5nLCBuYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAodGhpcy53b3JrZXJzLmhhcyh0b2tlbikpIHJldHVybjtcblxuICAgIHRoaXMudG9rZW5OYW1lcy5zZXQodG9rZW4sIG5hbWUpO1xuICAgIGNvbnN0IHdvcmtlckNvbmZpZzogV29ya2VyQ29uZmlnID0ge1xuICAgICAgdG9rZW4sXG4gICAgICBtb2RlbDogdGhpcy5tb2RlbCxcbiAgICAgIHRpbWVvdXQ6IHRoaXMudGltZW91dCxcbiAgICAgIGN3ZDogdGhpcy5jd2QsXG4gICAgICBtYXhDYWxsczogdGhpcy5tYXhDYWxscyxcbiAgICB9O1xuICAgIGNvbnN0IHdvcmtlcnMgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiB0aGlzLnNpemUgfSwgKCkgPT4gbmV3IFdvcmtlcih3b3JrZXJDb25maWcpKTtcbiAgICB0aGlzLndvcmtlcnMuc2V0KHRva2VuLCB3b3JrZXJzKTtcbiAgICB0aGlzLnJlcXVlc3RDb3VudHMuc2V0KHRva2VuLCAwKTtcbiAgfVxuXG4gIGRlc3Ryb3lXb3JrZXJzKHRva2VuOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCB3b3JrZXJzID0gdGhpcy53b3JrZXJzLmdldCh0b2tlbik7XG4gICAgaWYgKCF3b3JrZXJzKSByZXR1cm47XG5cbiAgICB3b3JrZXJzLmZvckVhY2goKHcpID0+IHtcbiAgICAgIHcua2lsbCgpO1xuICAgIH0pO1xuICAgIHRoaXMud29ya2Vycy5kZWxldGUodG9rZW4pO1xuICAgIHRoaXMudG9rZW5OYW1lcy5kZWxldGUodG9rZW4pO1xuICAgIHRoaXMucXVvdGFFeGhhdXN0ZWQuZGVsZXRlKHRva2VuKTtcbiAgICB0aGlzLnJlcXVlc3RDb3VudHMuZGVsZXRlKHRva2VuKTtcbiAgfVxufVxuXG4vLyBQb29sIOyLseq4gO2EtCDqtIDrpqxcbmxldCBwb29sOiBDbGF1ZGVQb29sIHwgbnVsbCA9IG51bGw7XG5cbmV4cG9ydCBmdW5jdGlvbiBpbml0UG9vbCh0b2tlbnM6IHsgdG9rZW46IHN0cmluZzsgbmFtZTogc3RyaW5nIH1bXSk6IENsYXVkZVBvb2wge1xuICBwb29sID0gbmV3IENsYXVkZVBvb2woeyB0b2tlbnMgfSk7XG4gIHJldHVybiBwb29sO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbCgpOiBDbGF1ZGVQb29sIHtcbiAgaWYgKCFwb29sKSB0aHJvdyBuZXcgRXJyb3IoXCJQb29sIG5vdCBpbml0aWFsaXplZC4gQ2FsbCBpbml0UG9vbCgpIGZpcnN0LlwiKTtcbiAgcmV0dXJuIHBvb2w7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7QUFXQSxJQUFhLGFBQWIsTUFBd0I7Q0FDdEIsMEJBQVUsSUFBSSxLQUF1QjtDQUNyQyw2QkFBYSxJQUFJLEtBQXFCO0NBQ3RDLGlDQUFpQixJQUFJLEtBQWE7Q0FDbEMsZ0NBQWdCLElBQUksS0FBcUI7Q0FDekMsZ0JBQWdCO0NBQ2hCLHNCQUFzQjtDQUN0QjtDQUNBO0NBQ0E7Q0FDQTtDQUNBO0NBRUEsWUFBWSxRQUFvQjtBQUM5QixPQUFLLE9BQU8sT0FBTyxRQUFRO0FBQzNCLE9BQUssUUFBUSxPQUFPLFNBQVM7QUFDN0IsT0FBSyxVQUFVLE9BQU8sV0FBVztBQUNqQyxPQUFLLE1BQU0sT0FBTyxPQUFPO0FBQ3pCLE9BQUssV0FBVyxPQUFPLFlBQVk7QUFFbkMsU0FBTyxPQUFPLFNBQVMsRUFBRSxPQUFPLFdBQVc7QUFDekMsUUFBSyxjQUFjLE9BQU8sS0FBSztJQUMvQjs7Q0FHSix3QkFBZ0M7QUFFOUIsU0FBTyxHQURVLEtBQUssV0FBVyxJQUFJLEtBQUssY0FBYyxJQUFJLFVBQ3pDLEdBQUcsS0FBSzs7Q0FHN0IsV0FBeUI7QUFDdkIsU0FBTyxDQUFDLEdBQUcsS0FBSyxRQUFRLE1BQU0sQ0FBQyxDQUFDLEtBQUssV0FBVztHQUM5QztHQUNBLE1BQU0sS0FBSyxXQUFXLElBQUksTUFBTSxJQUFJO0dBQ3BDLFVBQVUsS0FBSyxjQUFjLElBQUksTUFBTSxJQUFJO0dBQzNDLFFBQVEsQ0FBQyxLQUFLLGVBQWUsSUFBSSxNQUFNO0dBQ3hDLEVBQUU7O0NBR0wsZUFBdUI7Q0FDdkIsa0NBQTBCLElBQUksS0FBcUI7Q0FFbkQsZUFBOEI7RUFDNUIsTUFBTSxrQkFBa0IsQ0FBQyxHQUFHLEtBQUssUUFBUSxTQUFTLENBQUMsQ0FBQyxRQUNqRCxDQUFDLFdBQVcsQ0FBQyxLQUFLLGVBQWUsSUFBSSxNQUFNLENBQzdDO0FBRUQsTUFBSSxnQkFBZ0IsV0FBVyxFQUFHLFFBQU87RUFHekMsTUFBTSxjQUFjLGdCQUFnQixLQUFLLENBQUMsT0FBTyxjQUFjO0dBQzdEO0dBQ0E7R0FDQSxZQUFZLFFBQVEsUUFBUSxLQUFLLE1BQU0sTUFBTSxFQUFFLGVBQWUsRUFBRSxFQUFFO0dBQ25FLEVBQUU7RUFDSCxNQUFNLGdCQUFnQixLQUFLLElBQUksR0FBRyxZQUFZLEtBQUssTUFBTSxFQUFFLFdBQVcsQ0FBQztFQUN2RSxNQUFNLGFBQWEsWUFBWSxRQUFRLE1BQU0sRUFBRSxlQUFlLGNBQWM7RUFDNUUsTUFBTSxXQUFXLFdBQVcsS0FBSyxlQUFlLFdBQVc7QUFDM0QsT0FBSztFQUdMLE1BQU0sV0FBVyxLQUFLLElBQUksR0FBRyxTQUFTLFFBQVEsS0FBSyxNQUFNLEVBQUUsZUFBZSxDQUFDLENBQUM7RUFDNUUsTUFBTSxjQUFjLFNBQVMsUUFBUSxRQUFRLE1BQU0sRUFBRSxlQUFlLEtBQUssU0FBUztFQUNsRixNQUFNLFFBQVEsS0FBSyxnQkFBZ0IsSUFBSSxTQUFTLE1BQU0sSUFBSTtFQUMxRCxNQUFNLFNBQVMsWUFBWSxRQUFRLFlBQVk7QUFDL0MsT0FBSyxnQkFBZ0IsSUFBSSxTQUFTLE9BQU8sUUFBUSxFQUFFO0FBRW5ELFNBQU87O0NBR1QsTUFBTSxNQUFNLE9BQW1CLFdBQXdDO0VBQ3JFLE1BQU0sOEJBQWMsSUFBSSxLQUFhO0FBRXJDLFNBQU8sTUFBTTtHQUNYLE1BQU0sU0FBUyxLQUFLLGNBQWM7QUFDbEMsT0FBSSxDQUFDLE9BQ0gsT0FBTSxJQUFJLFdBQVcsdUJBQXVCO0FBRzlDLE9BQUksWUFBWSxJQUFJLE9BQU8sUUFBUSxDQUNqQyxPQUFNLElBQUksV0FBVyx1QkFBdUI7R0FHOUMsTUFBTSxhQUFhLEtBQUssV0FBVyxJQUFJLE9BQU8sUUFBUSxJQUFJO0dBQzFELE1BQU0sZUFBZSxLQUFLLFFBQVEsSUFBSSxPQUFPLFFBQVE7R0FDckQsTUFBTSxZQUFZLGVBQWUsYUFBYSxRQUFRLE9BQU8sR0FBRyxJQUFJO0FBQ3BFLFdBQVEsSUFBSSxhQUFhLFdBQVcsR0FBRyxVQUFVLFdBQVcsT0FBTyxlQUFlLENBQUMsR0FBRztBQUV0RixPQUFJO0lBQ0YsTUFBTSxTQUFTLE1BQU0sT0FBTyxNQUFNLE9BQU8sVUFBVTtBQUNuRCxTQUFLLGdCQUFnQixPQUFPO0FBQzVCLFNBQUssc0JBQXNCO0FBQzNCLFNBQUssY0FBYyxJQUFJLE9BQU8sVUFBVSxLQUFLLGNBQWMsSUFBSSxPQUFPLFFBQVEsSUFBSSxLQUFLLEVBQUU7QUFFekYsV0FBTztZQUNBLEtBQUs7QUFDWixRQUFJLGVBQWUsWUFBWTtBQUM3QixVQUFLLGVBQWUsSUFBSSxPQUFPLFFBQVE7QUFDdkMsaUJBQVksSUFBSSxPQUFPLFFBQVE7QUFDL0I7O0FBRUYsVUFBTTs7OztDQUtaLE9BQWE7QUFDWCxHQUFDLEdBQUcsS0FBSyxRQUFRLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLE1BQU07QUFDL0MsS0FBRSxNQUFNO0lBQ1I7O0NBR0osY0FBYyxPQUFlLE1BQW9CO0FBQy9DLE1BQUksS0FBSyxRQUFRLElBQUksTUFBTSxDQUFFO0FBRTdCLE9BQUssV0FBVyxJQUFJLE9BQU8sS0FBSztFQUNoQyxNQUFNLGVBQTZCO0dBQ2pDO0dBQ0EsT0FBTyxLQUFLO0dBQ1osU0FBUyxLQUFLO0dBQ2QsS0FBSyxLQUFLO0dBQ1YsVUFBVSxLQUFLO0dBQ2hCO0VBQ0QsTUFBTSxVQUFVLE1BQU0sS0FBSyxFQUFFLFFBQVEsS0FBSyxNQUFNLFFBQVEsSUFBSSxPQUFPLGFBQWEsQ0FBQztBQUNqRixPQUFLLFFBQVEsSUFBSSxPQUFPLFFBQVE7QUFDaEMsT0FBSyxjQUFjLElBQUksT0FBTyxFQUFFOztDQUdsQyxlQUFlLE9BQXFCO0VBQ2xDLE1BQU0sVUFBVSxLQUFLLFFBQVEsSUFBSSxNQUFNO0FBQ3ZDLE1BQUksQ0FBQyxRQUFTO0FBRWQsVUFBUSxTQUFTLE1BQU07QUFDckIsS0FBRSxNQUFNO0lBQ1I7QUFDRixPQUFLLFFBQVEsT0FBTyxNQUFNO0FBQzFCLE9BQUssV0FBVyxPQUFPLE1BQU07QUFDN0IsT0FBSyxlQUFlLE9BQU8sTUFBTTtBQUNqQyxPQUFLLGNBQWMsT0FBTyxNQUFNOzs7QUFLcEMsSUFBSSxPQUEwQjtBQUU5QixTQUFnQixTQUFTLFFBQXVEO0FBQzlFLFFBQU8sSUFBSSxXQUFXLEVBQUUsUUFBUSxDQUFDO0FBQ2pDLFFBQU87O0FBR1QsU0FBZ0IsVUFBc0I7QUFDcEMsS0FBSSxDQUFDLEtBQU0sT0FBTSxJQUFJLE1BQU0sK0NBQStDO0FBQzFFLFFBQU8ifQ==
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import "../../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { ProcessError, QuotaError, TimeoutError, maskToken } from "./qgrid.types.js";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { mkdirSync } from "node:fs";
|
|
5
|
-
//#region src/application/qgrid/worker.ts
|
|
6
|
-
/**
|
|
7
|
-
* Worker — Claude CLI stream-json 프로세스 하나를 관리.
|
|
8
|
-
*
|
|
9
|
-
* 기존 SocratsAI ClaudeCliService에서 추출 + 리팩터:
|
|
10
|
-
* - 토큰/모델을 생성자에서 주입
|
|
11
|
-
* - env allowlist (PATH, TMPDIR, CLAUDE_CODE_OAUTH_TOKEN + CLAUDE_CODE_DISABLE_* )
|
|
12
|
-
* - callCount 추적 + maxCalls 도달 시 프로세스 재활용
|
|
13
|
-
* - QuotaError/TimeoutError/ProcessError 구분
|
|
14
|
-
*/
|
|
15
|
-
var Worker = class {
|
|
16
|
-
tokenId;
|
|
17
|
-
config;
|
|
18
|
-
process = null;
|
|
19
|
-
buffer = "";
|
|
20
|
-
pending = null;
|
|
21
|
-
sessionId = null;
|
|
22
|
-
queue = [];
|
|
23
|
-
cwdCreated = false;
|
|
24
|
-
callCount = 0;
|
|
25
|
-
needsRecycle = false;
|
|
26
|
-
constructor(config) {
|
|
27
|
-
this.config = config;
|
|
28
|
-
this.tokenId = config.token;
|
|
29
|
-
}
|
|
30
|
-
getQueueDepth() {
|
|
31
|
-
return this.queue.length + (this.pending ? 1 : 0);
|
|
32
|
-
}
|
|
33
|
-
rejectPending(err) {
|
|
34
|
-
if (!this.pending) return;
|
|
35
|
-
this.pending.reject(err);
|
|
36
|
-
clearTimeout(this.pending.timer);
|
|
37
|
-
this.pending = null;
|
|
38
|
-
}
|
|
39
|
-
ensureProcess() {
|
|
40
|
-
if (this.process && this.process.exitCode === null && !this.needsRecycle) return this.process;
|
|
41
|
-
if (this.process && this.process.exitCode === null) {
|
|
42
|
-
this.process.kill();
|
|
43
|
-
this.process = null;
|
|
44
|
-
}
|
|
45
|
-
const env = {
|
|
46
|
-
PATH: process.env.PATH,
|
|
47
|
-
TMPDIR: process.env.TMPDIR,
|
|
48
|
-
CLAUDE_CODE_OAUTH_TOKEN: this.config.token,
|
|
49
|
-
CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
|
|
50
|
-
CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING: "1",
|
|
51
|
-
CLAUDE_CODE_DISABLE_1M_CONTEXT: "1"
|
|
52
|
-
};
|
|
53
|
-
const { cwd } = this.config;
|
|
54
|
-
if (!this.cwdCreated) {
|
|
55
|
-
mkdirSync(cwd, { recursive: true });
|
|
56
|
-
this.cwdCreated = true;
|
|
57
|
-
}
|
|
58
|
-
const child = spawn("claude", [
|
|
59
|
-
"-p",
|
|
60
|
-
"--input-format",
|
|
61
|
-
"stream-json",
|
|
62
|
-
"--output-format",
|
|
63
|
-
"stream-json",
|
|
64
|
-
"--verbose",
|
|
65
|
-
"--max-turns",
|
|
66
|
-
"1",
|
|
67
|
-
"--permission-mode",
|
|
68
|
-
"bypassPermissions",
|
|
69
|
-
"--setting-sources",
|
|
70
|
-
"project",
|
|
71
|
-
"--model",
|
|
72
|
-
this.config.model
|
|
73
|
-
], {
|
|
74
|
-
stdio: [
|
|
75
|
-
"pipe",
|
|
76
|
-
"pipe",
|
|
77
|
-
"ignore"
|
|
78
|
-
],
|
|
79
|
-
env,
|
|
80
|
-
cwd
|
|
81
|
-
});
|
|
82
|
-
child.stdout?.on("data", (d) => {
|
|
83
|
-
this.buffer += d.toString();
|
|
84
|
-
this.processBuffer();
|
|
85
|
-
});
|
|
86
|
-
child.on("close", () => {
|
|
87
|
-
this.process = null;
|
|
88
|
-
this.rejectPending(new ProcessError(`CLI process closed (token: ${maskToken(this.tokenId)})`));
|
|
89
|
-
this.drainQueue();
|
|
90
|
-
});
|
|
91
|
-
child.on("error", (err) => {
|
|
92
|
-
this.process = null;
|
|
93
|
-
this.rejectPending(new ProcessError(`CLI process error: ${err.message} (token: ${maskToken(this.tokenId)})`));
|
|
94
|
-
this.drainQueue();
|
|
95
|
-
});
|
|
96
|
-
this.process = child;
|
|
97
|
-
this.buffer = "";
|
|
98
|
-
this.sessionId = null;
|
|
99
|
-
this.callCount = 0;
|
|
100
|
-
this.needsRecycle = false;
|
|
101
|
-
return child;
|
|
102
|
-
}
|
|
103
|
-
processBuffer() {
|
|
104
|
-
const lines = this.buffer.split("\n");
|
|
105
|
-
this.buffer = lines.pop() ?? "";
|
|
106
|
-
lines.filter((line) => line.trim() !== "").forEach((line) => {
|
|
107
|
-
try {
|
|
108
|
-
const j = JSON.parse(line);
|
|
109
|
-
if (j.type === "system" && j.subtype === "init" && j.session_id) this.sessionId = j.session_id;
|
|
110
|
-
if (j.type === "result" && this.pending) {
|
|
111
|
-
let text = j.result ?? "";
|
|
112
|
-
text = text.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "");
|
|
113
|
-
if (text.startsWith("You've hit")) {
|
|
114
|
-
this.pending.reject(new QuotaError(`Quota exhausted (token: ${maskToken(this.tokenId)})`));
|
|
115
|
-
clearTimeout(this.pending.timer);
|
|
116
|
-
this.pending = null;
|
|
117
|
-
this.drainQueue();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const u = j.usage ?? {};
|
|
121
|
-
this.pending.resolve({
|
|
122
|
-
text,
|
|
123
|
-
usage: {
|
|
124
|
-
input_tokens: u.input_tokens ?? 0,
|
|
125
|
-
output_tokens: u.output_tokens ?? 0,
|
|
126
|
-
cache_creation_input_tokens: u.cache_creation_input_tokens ?? 0,
|
|
127
|
-
cache_read_input_tokens: u.cache_read_input_tokens ?? 0
|
|
128
|
-
},
|
|
129
|
-
durationMs: j.duration_ms ?? 0,
|
|
130
|
-
costUsd: j.total_cost_usd ?? 0
|
|
131
|
-
});
|
|
132
|
-
clearTimeout(this.pending.timer);
|
|
133
|
-
this.pending = null;
|
|
134
|
-
this.drainQueue();
|
|
135
|
-
}
|
|
136
|
-
} catch {}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
drainQueue() {
|
|
140
|
-
if (this.pending) return;
|
|
141
|
-
const next = this.queue.shift();
|
|
142
|
-
if (next) next();
|
|
143
|
-
}
|
|
144
|
-
async query(input, timeoutMs) {
|
|
145
|
-
if (this.pending) await new Promise((resolve) => {
|
|
146
|
-
this.queue.push(resolve);
|
|
147
|
-
});
|
|
148
|
-
const child = this.ensureProcess();
|
|
149
|
-
const timeout = timeoutMs ?? this.config.timeout;
|
|
150
|
-
this.callCount++;
|
|
151
|
-
if (this.callCount >= this.config.maxCalls) this.needsRecycle = true;
|
|
152
|
-
const prompt = input.system ? `${input.system}\n\n${input.prompt}` : input.prompt;
|
|
153
|
-
return new Promise((resolve, reject) => {
|
|
154
|
-
this.pending = {
|
|
155
|
-
resolve,
|
|
156
|
-
reject,
|
|
157
|
-
timer: setTimeout(() => {
|
|
158
|
-
this.pending = null;
|
|
159
|
-
reject(new TimeoutError(`Timeout after ${timeout / 1e3}s (token: ${maskToken(this.tokenId)})`));
|
|
160
|
-
this.kill();
|
|
161
|
-
}, timeout)
|
|
162
|
-
};
|
|
163
|
-
const msg = JSON.stringify({
|
|
164
|
-
type: "user",
|
|
165
|
-
message: {
|
|
166
|
-
role: "user",
|
|
167
|
-
content: prompt
|
|
168
|
-
},
|
|
169
|
-
session_id: this.sessionId ?? "default",
|
|
170
|
-
parent_tool_use_id: null
|
|
171
|
-
});
|
|
172
|
-
child.stdin?.write(`${msg}\n`);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
kill() {
|
|
176
|
-
if (this.process && this.process.exitCode === null) this.process.kill();
|
|
177
|
-
this.process = null;
|
|
178
|
-
this.buffer = "";
|
|
179
|
-
this.sessionId = null;
|
|
180
|
-
this.callCount = 0;
|
|
181
|
-
this.needsRecycle = false;
|
|
182
|
-
this.rejectPending(new ProcessError(`Worker killed (token: ${maskToken(this.tokenId)})`));
|
|
183
|
-
this.drainQueue();
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
//#endregion
|
|
187
|
-
export { Worker };
|
|
188
|
-
|
|
189
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2VyLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcHBsaWNhdGlvbi9xZ3JpZC93b3JrZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBXb3JrZXIg4oCUIENsYXVkZSBDTEkgc3RyZWFtLWpzb24g7ZSE66Gc7IS47IqkIO2VmOuCmOulvCDqtIDrpqwuXG4gKlxuICog6riw7KG0IFNvY3JhdHNBSSBDbGF1ZGVDbGlTZXJ2aWNl7JeQ7IScIOy2lOy2nCArIOumrO2Mqe2EsDpcbiAqIC0g7Yag7YGwL+uqqOuNuOydhCDsg53shLHsnpDsl5DshJwg7KO87J6FXG4gKiAtIGVudiBhbGxvd2xpc3QgKFBBVEgsIFRNUERJUiwgQ0xBVURFX0NPREVfT0FVVEhfVE9LRU4gKyBDTEFVREVfQ09ERV9ESVNBQkxFXyogKVxuICogLSBjYWxsQ291bnQg7LaU7KCBICsgbWF4Q2FsbHMg64+E64usIOyLnCDtlITroZzshLjsiqQg7J6s7Zmc7JqpXG4gKiAtIFF1b3RhRXJyb3IvVGltZW91dEVycm9yL1Byb2Nlc3NFcnJvciDqtazrtoRcbiAqL1xuaW1wb3J0IHsgdHlwZSBDaGlsZFByb2Nlc3MsIHNwYXduIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHsgbWtkaXJTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcblxuaW1wb3J0IHsgdHlwZSBDbGlSZXN1bHQsIHR5cGUgUXVlcnlJbnB1dCB9IGZyb20gXCIuL3FncmlkLnR5cGVzXCI7XG5pbXBvcnQgeyBtYXNrVG9rZW4sIFByb2Nlc3NFcnJvciwgUXVvdGFFcnJvciwgVGltZW91dEVycm9yIH0gZnJvbSBcIi4vcWdyaWQudHlwZXNcIjtcblxudHlwZSBQZW5kaW5nUmVxdWVzdCA9IHtcbiAgcmVzb2x2ZTogKHZhbHVlOiBDbGlSZXN1bHQpID0+IHZvaWQ7XG4gIHJlamVjdDogKHJlYXNvbjogRXJyb3IpID0+IHZvaWQ7XG4gIHRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0Pjtcbn07XG5cbmV4cG9ydCB0eXBlIFdvcmtlckNvbmZpZyA9IHtcbiAgdG9rZW46IHN0cmluZztcbiAgbW9kZWw6IHN0cmluZztcbiAgdGltZW91dDogbnVtYmVyO1xuICBjd2Q6IHN0cmluZztcbiAgbWF4Q2FsbHM6IG51bWJlcjtcbn07XG5cbmV4cG9ydCBjbGFzcyBXb3JrZXIge1xuICB0b2tlbklkOiBzdHJpbmc7XG4gIGNvbmZpZzogV29ya2VyQ29uZmlnO1xuICBwcm9jZXNzOiBDaGlsZFByb2Nlc3MgfCBudWxsID0gbnVsbDtcbiAgYnVmZmVyID0gXCJcIjtcbiAgcGVuZGluZzogUGVuZGluZ1JlcXVlc3QgfCBudWxsID0gbnVsbDtcbiAgc2Vzc2lvbklkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgcXVldWU6IEFycmF5PCgpID0+IHZvaWQ+ID0gW107XG4gIGN3ZENyZWF0ZWQgPSBmYWxzZTtcbiAgY2FsbENvdW50ID0gMDtcbiAgbmVlZHNSZWN5Y2xlID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoY29uZmlnOiBXb3JrZXJDb25maWcpIHtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLnRva2VuSWQgPSBjb25maWcudG9rZW47XG4gIH1cblxuICBnZXRRdWV1ZURlcHRoKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMucXVldWUubGVuZ3RoICsgKHRoaXMucGVuZGluZyA/IDEgOiAwKTtcbiAgfVxuXG4gIHJlamVjdFBlbmRpbmcoZXJyOiBFcnJvcik6IHZvaWQge1xuICAgIGlmICghdGhpcy5wZW5kaW5nKSByZXR1cm47XG4gICAgdGhpcy5wZW5kaW5nLnJlamVjdChlcnIpO1xuICAgIGNsZWFyVGltZW91dCh0aGlzLnBlbmRpbmcudGltZXIpO1xuICAgIHRoaXMucGVuZGluZyA9IG51bGw7XG4gIH1cblxuICBlbnN1cmVQcm9jZXNzKCk6IENoaWxkUHJvY2VzcyB7XG4gICAgaWYgKHRoaXMucHJvY2VzcyAmJiB0aGlzLnByb2Nlc3MuZXhpdENvZGUgPT09IG51bGwgJiYgIXRoaXMubmVlZHNSZWN5Y2xlKSB7XG4gICAgICByZXR1cm4gdGhpcy5wcm9jZXNzO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnByb2Nlc3MgJiYgdGhpcy5wcm9jZXNzLmV4aXRDb2RlID09PSBudWxsKSB7XG4gICAgICB0aGlzLnByb2Nlc3Mua2lsbCgpO1xuICAgICAgdGhpcy5wcm9jZXNzID0gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBlbnY6IE5vZGVKUy5Qcm9jZXNzRW52ID0ge1xuICAgICAgUEFUSDogcHJvY2Vzcy5lbnYuUEFUSCxcbiAgICAgIFRNUERJUjogcHJvY2Vzcy5lbnYuVE1QRElSLFxuICAgICAgQ0xBVURFX0NPREVfT0FVVEhfVE9LRU46IHRoaXMuY29uZmlnLnRva2VuLFxuICAgICAgQ0xBVURFX0NPREVfRElTQUJMRV9BVVRPX01FTU9SWTogXCIxXCIsXG4gICAgICBDTEFVREVfQ09ERV9ESVNBQkxFX0FEQVBUSVZFX1RISU5LSU5HOiBcIjFcIixcbiAgICAgIENMQVVERV9DT0RFX0RJU0FCTEVfMU1fQ09OVEVYVDogXCIxXCIsXG4gICAgfTtcblxuICAgIGNvbnN0IHsgY3dkIH0gPSB0aGlzLmNvbmZpZztcbiAgICBpZiAoIXRoaXMuY3dkQ3JlYXRlZCkge1xuICAgICAgbWtkaXJTeW5jKGN3ZCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICB0aGlzLmN3ZENyZWF0ZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGNoaWxkID0gc3Bhd24oXG4gICAgICBcImNsYXVkZVwiLFxuICAgICAgW1xuICAgICAgICBcIi1wXCIsXG4gICAgICAgIFwiLS1pbnB1dC1mb3JtYXRcIixcbiAgICAgICAgXCJzdHJlYW0tanNvblwiLFxuICAgICAgICBcIi0tb3V0cHV0LWZvcm1hdFwiLFxuICAgICAgICBcInN0cmVhbS1qc29uXCIsXG4gICAgICAgIFwiLS12ZXJib3NlXCIsXG4gICAgICAgIFwiLS1tYXgtdHVybnNcIixcbiAgICAgICAgXCIxXCIsXG4gICAgICAgIFwiLS1wZXJtaXNzaW9uLW1vZGVcIixcbiAgICAgICAgXCJieXBhc3NQZXJtaXNzaW9uc1wiLFxuICAgICAgICBcIi0tc2V0dGluZy1zb3VyY2VzXCIsXG4gICAgICAgIFwicHJvamVjdFwiLFxuICAgICAgICBcIi0tbW9kZWxcIixcbiAgICAgICAgdGhpcy5jb25maWcubW9kZWwsXG4gICAgICBdLFxuICAgICAgeyBzdGRpbzogW1wicGlwZVwiLCBcInBpcGVcIiwgXCJpZ25vcmVcIl0sIGVudiwgY3dkIH0sXG4gICAgKTtcblxuICAgIGNoaWxkLnN0ZG91dD8ub24oXCJkYXRhXCIsIChkOiBCdWZmZXIpID0+IHtcbiAgICAgIHRoaXMuYnVmZmVyICs9IGQudG9TdHJpbmcoKTtcbiAgICAgIHRoaXMucHJvY2Vzc0J1ZmZlcigpO1xuICAgIH0pO1xuXG4gICAgY2hpbGQub24oXCJjbG9zZVwiLCAoKSA9PiB7XG4gICAgICB0aGlzLnByb2Nlc3MgPSBudWxsO1xuICAgICAgdGhpcy5yZWplY3RQZW5kaW5nKFxuICAgICAgICBuZXcgUHJvY2Vzc0Vycm9yKGBDTEkgcHJvY2VzcyBjbG9zZWQgKHRva2VuOiAke21hc2tUb2tlbih0aGlzLnRva2VuSWQpfSlgKSxcbiAgICAgICk7XG4gICAgICB0aGlzLmRyYWluUXVldWUoKTtcbiAgICB9KTtcblxuICAgIGNoaWxkLm9uKFwiZXJyb3JcIiwgKGVycikgPT4ge1xuICAgICAgdGhpcy5wcm9jZXNzID0gbnVsbDtcbiAgICAgIHRoaXMucmVqZWN0UGVuZGluZyhcbiAgICAgICAgbmV3IFByb2Nlc3NFcnJvcihgQ0xJIHByb2Nlc3MgZXJyb3I6ICR7ZXJyLm1lc3NhZ2V9ICh0b2tlbjogJHttYXNrVG9rZW4odGhpcy50b2tlbklkKX0pYCksXG4gICAgICApO1xuICAgICAgdGhpcy5kcmFpblF1ZXVlKCk7XG4gICAgfSk7XG5cbiAgICB0aGlzLnByb2Nlc3MgPSBjaGlsZDtcbiAgICB0aGlzLmJ1ZmZlciA9IFwiXCI7XG4gICAgdGhpcy5zZXNzaW9uSWQgPSBudWxsO1xuICAgIHRoaXMuY2FsbENvdW50ID0gMDtcbiAgICB0aGlzLm5lZWRzUmVjeWNsZSA9IGZhbHNlO1xuICAgIHJldHVybiBjaGlsZDtcbiAgfVxuXG4gIHByb2Nlc3NCdWZmZXIoKTogdm9pZCB7XG4gICAgY29uc3QgbGluZXMgPSB0aGlzLmJ1ZmZlci5zcGxpdChcIlxcblwiKTtcbiAgICB0aGlzLmJ1ZmZlciA9IGxpbmVzLnBvcCgpID8/IFwiXCI7XG5cbiAgICBsaW5lc1xuICAgICAgLmZpbHRlcigobGluZSkgPT4gbGluZS50cmltKCkgIT09IFwiXCIpXG4gICAgICAuZm9yRWFjaCgobGluZSkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGogPSBKU09OLnBhcnNlKGxpbmUpO1xuXG4gICAgICAgICAgaWYgKGoudHlwZSA9PT0gXCJzeXN0ZW1cIiAmJiBqLnN1YnR5cGUgPT09IFwiaW5pdFwiICYmIGouc2Vzc2lvbl9pZCkge1xuICAgICAgICAgICAgdGhpcy5zZXNzaW9uSWQgPSBqLnNlc3Npb25faWQ7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGoudHlwZSA9PT0gXCJyZXN1bHRcIiAmJiB0aGlzLnBlbmRpbmcpIHtcbiAgICAgICAgICAgIGxldCB0ZXh0OiBzdHJpbmcgPSBqLnJlc3VsdCA/PyBcIlwiO1xuICAgICAgICAgICAgdGV4dCA9IHRleHQucmVwbGFjZSgvXmBgYCg/Ompzb24pP1xccypcXG4/L2ksIFwiXCIpLnJlcGxhY2UoL1xcbj9gYGBcXHMqJC9pLCBcIlwiKTtcblxuICAgICAgICAgICAgaWYgKHRleHQuc3RhcnRzV2l0aChcIllvdSd2ZSBoaXRcIikpIHtcbiAgICAgICAgICAgICAgdGhpcy5wZW5kaW5nLnJlamVjdChcbiAgICAgICAgICAgICAgICBuZXcgUXVvdGFFcnJvcihgUXVvdGEgZXhoYXVzdGVkICh0b2tlbjogJHttYXNrVG9rZW4odGhpcy50b2tlbklkKX0pYCksXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aGlzLnBlbmRpbmcudGltZXIpO1xuICAgICAgICAgICAgICB0aGlzLnBlbmRpbmcgPSBudWxsO1xuICAgICAgICAgICAgICB0aGlzLmRyYWluUXVldWUoKTtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCB1ID0gai51c2FnZSA/PyB7fTtcbiAgICAgICAgICAgIHRoaXMucGVuZGluZy5yZXNvbHZlKHtcbiAgICAgICAgICAgICAgdGV4dCxcbiAgICAgICAgICAgICAgdXNhZ2U6IHtcbiAgICAgICAgICAgICAgICBpbnB1dF90b2tlbnM6IHUuaW5wdXRfdG9rZW5zID8/IDAsXG4gICAgICAgICAgICAgICAgb3V0cHV0X3Rva2VuczogdS5vdXRwdXRfdG9rZW5zID8/IDAsXG4gICAgICAgICAgICAgICAgY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zOiB1LmNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyA/PyAwLFxuICAgICAgICAgICAgICAgIGNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zOiB1LmNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zID8/IDAsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIGR1cmF0aW9uTXM6IGouZHVyYXRpb25fbXMgPz8gMCxcbiAgICAgICAgICAgICAgY29zdFVzZDogai50b3RhbF9jb3N0X3VzZCA/PyAwLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5wZW5kaW5nLnRpbWVyKTtcbiAgICAgICAgICAgIHRoaXMucGVuZGluZyA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLmRyYWluUXVldWUoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIHNraXAgbm9uLWpzb24gbGluZXNcbiAgICAgICAgfVxuICAgICAgfSk7XG4gIH1cblxuICBkcmFpblF1ZXVlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnBlbmRpbmcpIHJldHVybjtcbiAgICBjb25zdCBuZXh0ID0gdGhpcy5xdWV1ZS5zaGlmdCgpO1xuICAgIGlmIChuZXh0KSBuZXh0KCk7XG4gIH1cblxuICBhc3luYyBxdWVyeShpbnB1dDogUXVlcnlJbnB1dCwgdGltZW91dE1zPzogbnVtYmVyKTogUHJvbWlzZTxDbGlSZXN1bHQ+IHtcbiAgICBpZiAodGhpcy5wZW5kaW5nKSB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgICAgICB0aGlzLnF1ZXVlLnB1c2gocmVzb2x2ZSk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBjb25zdCBjaGlsZCA9IHRoaXMuZW5zdXJlUHJvY2VzcygpO1xuICAgIGNvbnN0IHRpbWVvdXQgPSB0aW1lb3V0TXMgPz8gdGhpcy5jb25maWcudGltZW91dDtcblxuICAgIHRoaXMuY2FsbENvdW50Kys7XG4gICAgaWYgKHRoaXMuY2FsbENvdW50ID49IHRoaXMuY29uZmlnLm1heENhbGxzKSB7XG4gICAgICB0aGlzLm5lZWRzUmVjeWNsZSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcHJvbXB0ID0gaW5wdXQuc3lzdGVtID8gYCR7aW5wdXQuc3lzdGVtfVxcblxcbiR7aW5wdXQucHJvbXB0fWAgOiBpbnB1dC5wcm9tcHQ7XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2U8Q2xpUmVzdWx0PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLnBlbmRpbmcgPSBudWxsO1xuICAgICAgICByZWplY3QoXG4gICAgICAgICAgbmV3IFRpbWVvdXRFcnJvcihgVGltZW91dCBhZnRlciAke3RpbWVvdXQgLyAxMDAwfXMgKHRva2VuOiAke21hc2tUb2tlbih0aGlzLnRva2VuSWQpfSlgKSxcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5raWxsKCk7XG4gICAgICB9LCB0aW1lb3V0KTtcblxuICAgICAgdGhpcy5wZW5kaW5nID0geyByZXNvbHZlLCByZWplY3QsIHRpbWVyIH07XG5cbiAgICAgIGNvbnN0IG1zZyA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgdHlwZTogXCJ1c2VyXCIsXG4gICAgICAgIG1lc3NhZ2U6IHsgcm9sZTogXCJ1c2VyXCIsIGNvbnRlbnQ6IHByb21wdCB9LFxuICAgICAgICBzZXNzaW9uX2lkOiB0aGlzLnNlc3Npb25JZCA/PyBcImRlZmF1bHRcIixcbiAgICAgICAgcGFyZW50X3Rvb2xfdXNlX2lkOiBudWxsLFxuICAgICAgfSk7XG5cbiAgICAgIGNoaWxkLnN0ZGluPy53cml0ZShgJHttc2d9XFxuYCk7XG4gICAgfSk7XG4gIH1cblxuICBraWxsKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnByb2Nlc3MgJiYgdGhpcy5wcm9jZXNzLmV4aXRDb2RlID09PSBudWxsKSB7XG4gICAgICB0aGlzLnByb2Nlc3Mua2lsbCgpO1xuICAgIH1cbiAgICB0aGlzLnByb2Nlc3MgPSBudWxsO1xuICAgIHRoaXMuYnVmZmVyID0gXCJcIjtcbiAgICB0aGlzLnNlc3Npb25JZCA9IG51bGw7XG4gICAgdGhpcy5jYWxsQ291bnQgPSAwO1xuICAgIHRoaXMubmVlZHNSZWN5Y2xlID0gZmFsc2U7XG4gICAgdGhpcy5yZWplY3RQZW5kaW5nKG5ldyBQcm9jZXNzRXJyb3IoYFdvcmtlciBraWxsZWQgKHRva2VuOiAke21hc2tUb2tlbih0aGlzLnRva2VuSWQpfSlgKSk7XG4gICAgdGhpcy5kcmFpblF1ZXVlKCk7XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7QUE2QkEsSUFBYSxTQUFiLE1BQW9CO0NBQ2xCO0NBQ0E7Q0FDQSxVQUErQjtDQUMvQixTQUFTO0NBQ1QsVUFBaUM7Q0FDakMsWUFBMkI7Q0FDM0IsUUFBMkIsRUFBRTtDQUM3QixhQUFhO0NBQ2IsWUFBWTtDQUNaLGVBQWU7Q0FFZixZQUFZLFFBQXNCO0FBQ2hDLE9BQUss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
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ClaudePool — 멀티 토큰 프로세스 풀.
|
|
3
|
-
*
|
|
4
|
-
* 2-layer 라우팅: 토큰 선택 (least-total-depth + round-robin) → 워커 선택
|
|
5
|
-
* - Map<token, Worker[]>로 토큰별 워커 관리
|
|
6
|
-
* - 토큰 간 quota 균등 소진
|
|
7
|
-
*/
|
|
8
|
-
import { type CliResult, type PoolConfig, type QueryInput, type TokenStats } from "./qgrid.types";
|
|
9
|
-
import { QuotaError } from "./qgrid.types";
|
|
10
|
-
import { Worker, type WorkerConfig } from "./worker";
|
|
11
|
-
|
|
12
|
-
export class ClaudePool {
|
|
13
|
-
workers = new Map<string, Worker[]>();
|
|
14
|
-
tokenNames = new Map<string, string>();
|
|
15
|
-
quotaExhausted = new Set<string>();
|
|
16
|
-
requestCounts = new Map<string, number>();
|
|
17
|
-
lastUsedToken = "";
|
|
18
|
-
lastUsedWorkerIndex = 0;
|
|
19
|
-
size: number;
|
|
20
|
-
model: string;
|
|
21
|
-
timeout: number;
|
|
22
|
-
cwd: string;
|
|
23
|
-
maxCalls: number;
|
|
24
|
-
|
|
25
|
-
constructor(config: PoolConfig) {
|
|
26
|
-
this.size = config.size ?? 3;
|
|
27
|
-
this.model = config.model ?? "sonnet";
|
|
28
|
-
this.timeout = config.timeout ?? 600_000;
|
|
29
|
-
this.cwd = config.cwd ?? "/tmp/qgrid";
|
|
30
|
-
this.maxCalls = config.maxCalls ?? 500;
|
|
31
|
-
|
|
32
|
-
config.tokens.forEach(({ token, name }) => {
|
|
33
|
-
this.createWorkers(token, name);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
getLastUsedWorkerName(): string {
|
|
38
|
-
const baseName = this.tokenNames.get(this.lastUsedToken) ?? "Unknown";
|
|
39
|
-
return `${baseName}-${this.lastUsedWorkerIndex}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getStats(): TokenStats[] {
|
|
43
|
-
return [...this.workers.keys()].map((token) => ({
|
|
44
|
-
token,
|
|
45
|
-
name: this.tokenNames.get(token) ?? "Unknown",
|
|
46
|
-
requests: this.requestCounts.get(token) ?? 0,
|
|
47
|
-
active: !this.quotaExhausted.has(token),
|
|
48
|
-
}));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private tokenRrIndex = 0;
|
|
52
|
-
private workerRrIndexes = new Map<string, number>();
|
|
53
|
-
|
|
54
|
-
selectWorker(): Worker | null {
|
|
55
|
-
const availableTokens = [...this.workers.entries()].filter(
|
|
56
|
-
([token]) => !this.quotaExhausted.has(token),
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
if (availableTokens.length === 0) return null;
|
|
60
|
-
|
|
61
|
-
// 1단계: 토큰별 총 큐 depth가 가장 낮은 토큰 선택
|
|
62
|
-
const tokenDepths = availableTokens.map(([token, workers]) => ({
|
|
63
|
-
token,
|
|
64
|
-
workers,
|
|
65
|
-
totalDepth: workers.reduce((sum, w) => sum + w.getQueueDepth(), 0),
|
|
66
|
-
}));
|
|
67
|
-
const minTokenDepth = Math.min(...tokenDepths.map((t) => t.totalDepth));
|
|
68
|
-
const idleTokens = tokenDepths.filter((t) => t.totalDepth === minTokenDepth);
|
|
69
|
-
const selected = idleTokens[this.tokenRrIndex % idleTokens.length]!;
|
|
70
|
-
this.tokenRrIndex++;
|
|
71
|
-
|
|
72
|
-
// 2단계: 토큰 내에서 least-depth 워커, 동률이면 round-robin
|
|
73
|
-
const minDepth = Math.min(...selected.workers.map((w) => w.getQueueDepth()));
|
|
74
|
-
const idleWorkers = selected.workers.filter((w) => w.getQueueDepth() === minDepth);
|
|
75
|
-
const wrIdx = this.workerRrIndexes.get(selected.token) ?? 0;
|
|
76
|
-
const picked = idleWorkers[wrIdx % idleWorkers.length]!;
|
|
77
|
-
this.workerRrIndexes.set(selected.token, wrIdx + 1);
|
|
78
|
-
|
|
79
|
-
return picked;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async query(input: QueryInput, timeoutMs?: number): Promise<CliResult> {
|
|
83
|
-
const triedTokens = new Set<string>();
|
|
84
|
-
|
|
85
|
-
while (true) {
|
|
86
|
-
const worker = this.selectWorker();
|
|
87
|
-
if (!worker) {
|
|
88
|
-
throw new QuotaError("All tokens exhausted");
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (triedTokens.has(worker.tokenId)) {
|
|
92
|
-
throw new QuotaError("All tokens exhausted");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const workerName = this.tokenNames.get(worker.tokenId) ?? "Unknown";
|
|
96
|
-
const tokenWorkers = this.workers.get(worker.tokenId);
|
|
97
|
-
const workerIdx = tokenWorkers ? tokenWorkers.indexOf(worker) + 1 : 0;
|
|
98
|
-
console.log(`[qgrid] → ${workerName}-${workerIdx} (queue: ${worker.getQueueDepth()})`);
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const result = await worker.query(input, timeoutMs);
|
|
102
|
-
this.lastUsedToken = worker.tokenId;
|
|
103
|
-
this.lastUsedWorkerIndex = workerIdx;
|
|
104
|
-
this.requestCounts.set(worker.tokenId, (this.requestCounts.get(worker.tokenId) ?? 0) + 1);
|
|
105
|
-
|
|
106
|
-
return result;
|
|
107
|
-
} catch (err) {
|
|
108
|
-
if (err instanceof QuotaError) {
|
|
109
|
-
this.quotaExhausted.add(worker.tokenId);
|
|
110
|
-
triedTokens.add(worker.tokenId);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
kill(): void {
|
|
119
|
-
[...this.workers.values()].flat().forEach((w) => {
|
|
120
|
-
w.kill();
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
createWorkers(token: string, name: string): void {
|
|
125
|
-
if (this.workers.has(token)) return;
|
|
126
|
-
|
|
127
|
-
this.tokenNames.set(token, name);
|
|
128
|
-
const workerConfig: WorkerConfig = {
|
|
129
|
-
token,
|
|
130
|
-
model: this.model,
|
|
131
|
-
timeout: this.timeout,
|
|
132
|
-
cwd: this.cwd,
|
|
133
|
-
maxCalls: this.maxCalls,
|
|
134
|
-
};
|
|
135
|
-
const workers = Array.from({ length: this.size }, () => new Worker(workerConfig));
|
|
136
|
-
this.workers.set(token, workers);
|
|
137
|
-
this.requestCounts.set(token, 0);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
destroyWorkers(token: string): void {
|
|
141
|
-
const workers = this.workers.get(token);
|
|
142
|
-
if (!workers) return;
|
|
143
|
-
|
|
144
|
-
workers.forEach((w) => {
|
|
145
|
-
w.kill();
|
|
146
|
-
});
|
|
147
|
-
this.workers.delete(token);
|
|
148
|
-
this.tokenNames.delete(token);
|
|
149
|
-
this.quotaExhausted.delete(token);
|
|
150
|
-
this.requestCounts.delete(token);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Pool 싱글턴 관리
|
|
155
|
-
let pool: ClaudePool | null = null;
|
|
156
|
-
|
|
157
|
-
export function initPool(tokens: { token: string; name: string }[]): ClaudePool {
|
|
158
|
-
pool = new ClaudePool({ tokens });
|
|
159
|
-
return pool;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function getPool(): ClaudePool {
|
|
163
|
-
if (!pool) throw new Error("Pool not initialized. Call initPool() first.");
|
|
164
|
-
return pool;
|
|
165
|
-
}
|