frizz-server 0.15.20 → 0.15.23
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/dist/claude-agent-broker.js +371 -39
- package/dist/dev-child.js +8255 -6173
- package/package.json +1 -1
- package/runtime/cc-worker/DECISIONS.md +42 -2
- package/runtime/cc-worker/bin/frizz-mcp.mjs +11 -1848
- package/runtime/cc-worker/bin/frizzctl +14 -0
- package/runtime/cc-worker/bin/frizzctl.cmd +9 -0
- package/runtime/cc-worker/bin/frizzctl.mjs +438 -0
- package/runtime/cc-worker/hooks/agent-dispatch.mjs +2 -2
- package/runtime/cc-worker/hooks/deny-ask.mjs +3 -3
- package/runtime/cc-worker/hooks/deny-plan.mjs +2 -2
- package/runtime/cc-worker/hooks/perm-policy.mjs +52 -1
- package/runtime/cc-worker/hooks/scratchpad.mjs +16 -10
- package/runtime/cc-worker/hooks/session-seed.mjs +1 -1
- package/runtime/cc-worker/lib/frizz-tools.mjs +2241 -0
- package/runtime/cc-worker/skills/gh/SKILL.md +6 -6
- package/web-dist/assets/{abnfDiagram-VRR7QNED-B61nQEjt.js → abnfDiagram-VRR7QNED-BW_CIF9J.js} +1 -1
- package/web-dist/assets/{arc-CR8_qWI3.js → arc-B475yv46.js} +1 -1
- package/web-dist/assets/architecture-TIHT7OUA-wAP7tOoN.js +1 -0
- package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-6sc4SDOh.js → architectureDiagram-ZJ3FMSHR-DkPvFjFp.js} +1 -1
- package/web-dist/assets/{blockDiagram-677ZJIJ3-p-DPTDGB.js → blockDiagram-677ZJIJ3-Rls7Nttu.js} +1 -1
- package/web-dist/assets/{c4Diagram-LMCZKHZV-cqvAqZE2.js → c4Diagram-LMCZKHZV-maV0q2bv.js} +1 -1
- package/web-dist/assets/channel-Clg0HJPA.js +1 -0
- package/web-dist/assets/{chunk-32BRIVSS-BNnoKILR.js → chunk-32BRIVSS-Coi7L0de.js} +1 -1
- package/web-dist/assets/{chunk-52WLFC77-BmHIWiVu.js → chunk-52WLFC77-DZaDe-48.js} +1 -1
- package/web-dist/assets/{chunk-C7G6YPKG-D7XYFno8.js → chunk-C7G6YPKG-DNQ-JhD9.js} +1 -1
- package/web-dist/assets/{chunk-EX3LRPZG-BUViIha7.js → chunk-EX3LRPZG-BQCebwoA.js} +1 -1
- package/web-dist/assets/{chunk-FWX5IMBZ-3x5Zky3W.js → chunk-FWX5IMBZ-CqiHtpEB.js} +2 -2
- package/web-dist/assets/{chunk-HOUHSVGY-C6_RBSov.js → chunk-HOUHSVGY-Bop69PFg.js} +1 -1
- package/web-dist/assets/{chunk-ICXQ74PX-NbVxQ4i1.js → chunk-ICXQ74PX-D3PkeZ6B.js} +1 -1
- package/web-dist/assets/{chunk-MOJQB5TN-nRMfcSHe.js → chunk-MOJQB5TN-lidXw8-C.js} +1 -1
- package/web-dist/assets/{chunk-OGEWGWER-BbSXfbEB.js → chunk-OGEWGWER-CzD91FAh.js} +1 -1
- package/web-dist/assets/{chunk-PUDLZKDR-Byubn9VY.js → chunk-PUDLZKDR-Cilkzvck.js} +1 -1
- package/web-dist/assets/{chunk-Q4XR5HBZ-DLq-aZ3t.js → chunk-Q4XR5HBZ-C37UQCfb.js} +1 -1
- package/web-dist/assets/{chunk-RYQCIY6F-Dbt66M6V.js → chunk-RYQCIY6F-C0qnX-Ux.js} +1 -1
- package/web-dist/assets/{chunk-V7JOEXUC-DNFTmXz6.js → chunk-V7JOEXUC-Da5vOGHX.js} +1 -1
- package/web-dist/assets/{chunk-VAUOI2AC-QWrszv3X.js → chunk-VAUOI2AC-FrJadrJY.js} +1 -1
- package/web-dist/assets/{chunk-VR4S4FIN-CJdyjwam.js → chunk-VR4S4FIN-DYxK2Z50.js} +1 -1
- package/web-dist/assets/{chunk-WYO6CB5R-D_786t-3.js → chunk-WYO6CB5R-C164KTfT.js} +1 -1
- package/web-dist/assets/{chunk-XXDRQBXY-Bd_2jBj_.js → chunk-XXDRQBXY-D4wHtxet.js} +1 -1
- package/web-dist/assets/{chunk-ZGVPDNZ5-DklPG-LG.js → chunk-ZGVPDNZ5-BXKkVljT.js} +1 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-23bCXfvg.js +1 -0
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-23bCXfvg.js +1 -0
- package/web-dist/assets/{cose-bilkent-JH36ORCC-C4MtSvEs.js → cose-bilkent-JH36ORCC-BFSgVzNT.js} +1 -1
- package/web-dist/assets/{cynefin-VYW2F7L2-Dr0CmMQz.js → cynefin-VYW2F7L2-BSoEbT-s.js} +1 -1
- package/web-dist/assets/{cynefinDiagram-TSTJHNR4-BpHXKyDO.js → cynefinDiagram-TSTJHNR4-BBZgLbdX.js} +1 -1
- package/web-dist/assets/{dagre-VKFMJZFB-CWAtKn43.js → dagre-VKFMJZFB-Dx6giO7u.js} +1 -1
- package/web-dist/assets/{diagram-FQU43EPY-B_6PhpPG.js → diagram-FQU43EPY-BMI3qRca.js} +1 -1
- package/web-dist/assets/{diagram-G47NLZAW-DPNrX-3a.js → diagram-G47NLZAW-CJY-MA3L.js} +1 -1
- package/web-dist/assets/{diagram-NH7WQ7WH-BL4nqAUm.js → diagram-NH7WQ7WH-CbQVFJ3r.js} +1 -1
- package/web-dist/assets/{diagram-OA4YK3LP-CcFbtuCf.js → diagram-OA4YK3LP-CPnrakdY.js} +1 -1
- package/web-dist/assets/{diagram-WEI45ONY-Bkl0nXre.js → diagram-WEI45ONY-D9DNwzlo.js} +1 -1
- package/web-dist/assets/{dist-krVNNn4H.js → dist-CO93_xVa.js} +1 -1
- package/web-dist/assets/{ebnfDiagram-CCIWWBDH-C_51YQqw.js → ebnfDiagram-CCIWWBDH-DNHudB8r.js} +1 -1
- package/web-dist/assets/{erDiagram-Q63AITRT-D8oBhiQB.js → erDiagram-Q63AITRT-Cwt63Xz8.js} +1 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-Q3Nq1ecc.js +1 -0
- package/web-dist/assets/flowDiagram-23GEKE2U-B2snV3ot.js +1 -0
- package/web-dist/assets/{ganttDiagram-NO4QXBWP-BOJJvkcQ.js → ganttDiagram-NO4QXBWP-DiFpSJc2.js} +1 -1
- package/web-dist/assets/{gitGraph-TEB2WS4Q-DrZGs6X0.js → gitGraph-TEB2WS4Q-2-hTvDOu.js} +1 -1
- package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-C0pqDVhD.js → gitGraphDiagram-IHSO6WYX-mbc4Il71.js} +1 -1
- package/web-dist/assets/index-C6AXnNpg.css +2 -0
- package/web-dist/assets/index-DewBdeY0.js +352 -0
- package/web-dist/assets/{info-DKCQHKI2-0OcIuVxc.js → info-DKCQHKI2-BO0ZUEnn.js} +1 -1
- package/web-dist/assets/{infoDiagram-FWYZ7A6U-CRAc-N7_.js → infoDiagram-FWYZ7A6U-Cn3DmnjS.js} +1 -1
- package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-D_bHCCxh.js → ishikawaDiagram-FXEZZL3T-Ckwh42aC.js} +1 -1
- package/web-dist/assets/{journeyDiagram-5HDEW3XC-Dbvh7D1e.js → journeyDiagram-5HDEW3XC-D26ySLLD.js} +1 -1
- package/web-dist/assets/{kanban-definition-HUTT4EX6-CxOWhFpB.js → kanban-definition-HUTT4EX6-CXKwXGxe.js} +1 -1
- package/web-dist/assets/{line-BGqbhtvK.js → line-BHqhqZTw.js} +1 -1
- package/web-dist/assets/{linear-DRvKJ3WJ.js → linear-Xq8ICqCe.js} +1 -1
- package/web-dist/assets/{mermaid-parser.core-C5lIBam8.js → mermaid-parser.core-ByagDKoG.js} +3 -3
- package/web-dist/assets/{mermaid.core-ChLUqNEE.js → mermaid.core-CEwbkqrb.js} +3 -3
- package/web-dist/assets/{mindmap-definition-LN4V7U3C-BfPv_GOf.js → mindmap-definition-LN4V7U3C-C3S3nEo5.js} +1 -1
- package/web-dist/assets/{packet-7NZHBO7P-CH3B2i7_.js → packet-7NZHBO7P-BjcgqWf7.js} +1 -1
- package/web-dist/assets/{pegDiagram-2B236MQR-DLuCHUaK.js → pegDiagram-2B236MQR-Due7wPzY.js} +1 -1
- package/web-dist/assets/{pie-RZYD4A2V-C-hDlp0h.js → pie-RZYD4A2V-C1X-Fm2K.js} +1 -1
- package/web-dist/assets/{pieDiagram-ENE6RG2P-CKVu3PTC.js → pieDiagram-ENE6RG2P-CbKS5jFv.js} +1 -1
- package/web-dist/assets/{quadrantDiagram-ABIIQ3AL--tZ3JdyA.js → quadrantDiagram-ABIIQ3AL-CVF1CNNO.js} +1 -1
- package/web-dist/assets/{radar-I7S5WNFK-C10EMwWx.js → radar-I7S5WNFK-Co7Otr5p.js} +1 -1
- package/web-dist/assets/{railroad-3IZDKUUU-BYegvtJS.js → railroad-3IZDKUUU-BTwZqHtN.js} +1 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-CEkmgARz.js +1 -0
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-D2dXzrFd.js +1 -0
- package/web-dist/assets/railroad-peg-LSFZ7HO6-yA2XVaxN.js +1 -0
- package/web-dist/assets/{railroadDiagram-RFXS5EU6-CfpRdkA8.js → railroadDiagram-RFXS5EU6-DCymlvVU.js} +1 -1
- package/web-dist/assets/{requirementDiagram-TGXJPOKE-CUajgDbc.js → requirementDiagram-TGXJPOKE-CEFbAu30.js} +1 -1
- package/web-dist/assets/{sankeyDiagram-HTMAVEWB-DRs5oKTx.js → sankeyDiagram-HTMAVEWB-fnX7WrAj.js} +1 -1
- package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-BTeEqLFC.js → sequenceDiagram-DBY2YBRQ-BUvsAYkQ.js} +1 -1
- package/web-dist/assets/{src-Cf1DXZ3M.js → src-V5VjKILh.js} +1 -1
- package/web-dist/assets/{stateDiagram-2N3HPSRC-BPcqMj-B.js → stateDiagram-2N3HPSRC-C9LJKbHM.js} +1 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-Bm1cPGFT.js +1 -0
- package/web-dist/assets/{swimlanes-5IMT3BWC-CJTd58TJ.js → swimlanes-5IMT3BWC-aq9B5aBs.js} +1 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-DR6Yx_Ic.js +8 -0
- package/web-dist/assets/{timeline-definition-FHXFAJF6-CqbaBVpe.js → timeline-definition-FHXFAJF6-DhJ9Jf4f.js} +1 -1
- package/web-dist/assets/{treeView-QDETBFTQ-B2OQ8r59.js → treeView-QDETBFTQ-D42FrEH4.js} +1 -1
- package/web-dist/assets/{treemap-6X3UGDF4-DGpAdWTA.js → treemap-6X3UGDF4-BV_9JyG6.js} +1 -1
- package/web-dist/assets/{vennDiagram-L72KCM5P-BAS_eWHm.js → vennDiagram-L72KCM5P-BkwHzxsN.js} +1 -1
- package/web-dist/assets/{wardley-OPB4EBWU-DpNHIH3W.js → wardley-OPB4EBWU-fwm52ei3.js} +1 -1
- package/web-dist/assets/{wardleyDiagram-EHGQE667-2N7X1vj8.js → wardleyDiagram-EHGQE667-D_UqvgcE.js} +1 -1
- package/web-dist/assets/{xychartDiagram-FW5EYKEG-C8mfLCei.js → xychartDiagram-FW5EYKEG-tKdmcTfS.js} +1 -1
- package/web-dist/index.html +2 -2
- package/web-dist/assets/architecture-TIHT7OUA-CXyjbDft.js +0 -1
- package/web-dist/assets/channel-uFMfT294.js +0 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-D1koMbqS.js +0 -1
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-D1koMbqS.js +0 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-tWFB9ALb.js +0 -1
- package/web-dist/assets/flowDiagram-23GEKE2U-Dz_Opd7B.js +0 -1
- package/web-dist/assets/index-BwC7xvWu.js +0 -336
- package/web-dist/assets/index-CuXKWysP.css +0 -2
- package/web-dist/assets/railroad-abnf-AHOZXSZD-DLmknJDm.js +0 -1
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-BoZF0VpA.js +0 -1
- package/web-dist/assets/railroad-peg-LSFZ7HO6-Dib7YVqg.js +0 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-DiV9dOER.js +0 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-BDeobZmn.js +0 -8
|
@@ -2,8 +2,9 @@ import { createRequire as __frizzCreateRequire } from "node:module"; const requi
|
|
|
2
2
|
|
|
3
3
|
// packages/server/src/backend/claude-agent-broker.ts
|
|
4
4
|
import net from "node:net";
|
|
5
|
-
import { readFileSync as
|
|
6
|
-
import {
|
|
5
|
+
import { readFileSync as readFileSync8, realpathSync as realpathSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
6
|
+
import { homedir as homedir3 } from "node:os";
|
|
7
|
+
import { join as join11 } from "node:path";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
8
9
|
import { fileURLToPath } from "node:url";
|
|
9
10
|
|
|
@@ -26081,11 +26082,6 @@ var LEGACY_DIR_NAME = ".frizz";
|
|
|
26081
26082
|
function legacyFrizzRoot(home = homedir()) {
|
|
26082
26083
|
return join2(home, LEGACY_DIR_NAME);
|
|
26083
26084
|
}
|
|
26084
|
-
function isLegacyStateRoot(root, exists) {
|
|
26085
|
-
if (!exists(root)) return false;
|
|
26086
|
-
if (!exists(join2(root, ".id")) && !exists(join2(root, ".gitignore"))) return true;
|
|
26087
|
-
return exists(join2(root, "projects")) || exists(join2(root, "registry.json"));
|
|
26088
|
-
}
|
|
26089
26085
|
function xdg(env, name) {
|
|
26090
26086
|
const value = env[name];
|
|
26091
26087
|
return value && (value.startsWith("/") || /^[A-Za-z]:[\\/]/u.test(value)) ? value : void 0;
|
|
@@ -26128,7 +26124,7 @@ function frizzPaths(options = {}) {
|
|
|
26128
26124
|
const home = options.home ?? homedir();
|
|
26129
26125
|
const exists = options.exists ?? existsSync2;
|
|
26130
26126
|
const legacyRoot = legacyFrizzRoot(home);
|
|
26131
|
-
if (
|
|
26127
|
+
if (exists(legacyRoot)) {
|
|
26132
26128
|
return { data: legacyRoot, state: legacyRoot, cache: legacyRoot, legacy: true };
|
|
26133
26129
|
}
|
|
26134
26130
|
const explicit = {
|
|
@@ -26505,7 +26501,7 @@ var BoundedAsyncQueue = class {
|
|
|
26505
26501
|
if (value !== void 0) return Promise.resolve({ done: false, value });
|
|
26506
26502
|
if (this.failure) return Promise.reject(this.failure);
|
|
26507
26503
|
if (this.ended) return Promise.resolve({ done: true, value: void 0 });
|
|
26508
|
-
return new Promise((
|
|
26504
|
+
return new Promise((resolve4, reject) => this.waiters.push({ resolve: resolve4, reject }));
|
|
26509
26505
|
}
|
|
26510
26506
|
[Symbol.asyncIterator]() {
|
|
26511
26507
|
return this;
|
|
@@ -26587,6 +26583,7 @@ var RealClaudeQueryHandle = class {
|
|
|
26587
26583
|
sdkQuery;
|
|
26588
26584
|
input;
|
|
26589
26585
|
diagnostic;
|
|
26586
|
+
rateLimit;
|
|
26590
26587
|
lifecycleAbort;
|
|
26591
26588
|
// id → the main-thread turn count at the moment it was sent. A Map rather than a Set plus a
|
|
26592
26589
|
// parallel order array because insertion order IS send order, which is all the oldest-first
|
|
@@ -26596,15 +26593,16 @@ var RealClaudeQueryHandle = class {
|
|
|
26596
26593
|
mainThreadResults = 0;
|
|
26597
26594
|
providerProgressCovered = false;
|
|
26598
26595
|
closePromise;
|
|
26599
|
-
constructor(sdkQuery, input, sessionId, lifecycleAbort, redactor, diagnostic) {
|
|
26596
|
+
constructor(sdkQuery, input, sessionId, lifecycleAbort, redactor, diagnostic, rateLimit) {
|
|
26600
26597
|
this.sdkQuery = sdkQuery;
|
|
26601
26598
|
this.input = input;
|
|
26602
26599
|
this.sessionId = sessionId;
|
|
26603
26600
|
this.lifecycleAbort = lifecycleAbort;
|
|
26604
26601
|
this.redactor = redactor;
|
|
26605
26602
|
this.diagnostic = diagnostic;
|
|
26606
|
-
this.
|
|
26607
|
-
|
|
26603
|
+
this.rateLimit = rateLimit;
|
|
26604
|
+
this.readyPromise = new Promise((resolve4, reject) => {
|
|
26605
|
+
this.resolveReady = resolve4;
|
|
26608
26606
|
this.rejectReady = reject;
|
|
26609
26607
|
});
|
|
26610
26608
|
void this.readyPromise.catch(() => void 0);
|
|
@@ -26836,6 +26834,9 @@ var RealClaudeQueryHandle = class {
|
|
|
26836
26834
|
async pump() {
|
|
26837
26835
|
try {
|
|
26838
26836
|
for await (const raw of this.sdkQuery) {
|
|
26837
|
+
if (this.rateLimit && raw.type === "rate_limit_event") {
|
|
26838
|
+
this.rateLimit(raw.rate_limit_info);
|
|
26839
|
+
}
|
|
26839
26840
|
let event;
|
|
26840
26841
|
try {
|
|
26841
26842
|
event = mapSdkMessage(raw);
|
|
@@ -26972,6 +26973,7 @@ function startClaudeQuery(executablePath, options) {
|
|
|
26972
26973
|
const environment = buildEnvironment(options.env);
|
|
26973
26974
|
const redact = createClaudeDiagnosticRedactor(environment);
|
|
26974
26975
|
const diagnostic = guardDiagnosticCallback(options.onDiagnostic);
|
|
26976
|
+
const rateLimit = guardObserverCallback(options.onRateLimit);
|
|
26975
26977
|
const permissionMode = validatePermissionMode(options.permissionMode ?? "default");
|
|
26976
26978
|
const lifecycleAbort = new AbortController();
|
|
26977
26979
|
const permissionRequests = new BoundedIdempotencyCache(MAX_PERMISSION_REQUESTS, "Claude permission request cache");
|
|
@@ -27085,7 +27087,7 @@ function startClaudeQuery(executablePath, options) {
|
|
|
27085
27087
|
}
|
|
27086
27088
|
}
|
|
27087
27089
|
});
|
|
27088
|
-
const handle = new RealClaudeQueryHandle(raw, input, sessionId, lifecycleAbort, redact, diagnostic);
|
|
27090
|
+
const handle = new RealClaudeQueryHandle(raw, input, sessionId, lifecycleAbort, redact, diagnostic, rateLimit);
|
|
27089
27091
|
if (mcpConfigFile) {
|
|
27090
27092
|
const drop = () => {
|
|
27091
27093
|
try {
|
|
@@ -27778,13 +27780,13 @@ function canonicalFingerprint(value) {
|
|
|
27778
27780
|
}
|
|
27779
27781
|
function abortableCallback(promise, signal, label) {
|
|
27780
27782
|
if (signal.aborted) return Promise.reject(new ClaudeAgentSdkProtocolError(`${label} aborted`));
|
|
27781
|
-
return new Promise((
|
|
27783
|
+
return new Promise((resolve4, reject) => {
|
|
27782
27784
|
const abort = () => reject(new ClaudeAgentSdkProtocolError(`${label} aborted`));
|
|
27783
27785
|
signal.addEventListener("abort", abort, { once: true });
|
|
27784
27786
|
promise.then(
|
|
27785
27787
|
(value) => {
|
|
27786
27788
|
signal.removeEventListener("abort", abort);
|
|
27787
|
-
|
|
27789
|
+
resolve4(value);
|
|
27788
27790
|
},
|
|
27789
27791
|
(error) => {
|
|
27790
27792
|
signal.removeEventListener("abort", abort);
|
|
@@ -27802,6 +27804,15 @@ function guardDiagnosticCallback(callback) {
|
|
|
27802
27804
|
}
|
|
27803
27805
|
};
|
|
27804
27806
|
}
|
|
27807
|
+
function guardObserverCallback(callback) {
|
|
27808
|
+
if (!callback) return void 0;
|
|
27809
|
+
return (value) => {
|
|
27810
|
+
try {
|
|
27811
|
+
callback(value);
|
|
27812
|
+
} catch {
|
|
27813
|
+
}
|
|
27814
|
+
};
|
|
27815
|
+
}
|
|
27805
27816
|
function validateExecutablePath(value) {
|
|
27806
27817
|
const path = validateAbsolutePath(value, "executablePath");
|
|
27807
27818
|
try {
|
|
@@ -28237,14 +28248,14 @@ data: ${JSON.stringify(message)}
|
|
|
28237
28248
|
}
|
|
28238
28249
|
internalRequest(method, params) {
|
|
28239
28250
|
const id2 = `frizz-lazy-${++this.nextInternalId}`;
|
|
28240
|
-
return new Promise((
|
|
28241
|
-
this.internal.set(id2,
|
|
28251
|
+
return new Promise((resolve4) => {
|
|
28252
|
+
this.internal.set(id2, resolve4);
|
|
28242
28253
|
this.send({ jsonrpc: "2.0", id: id2, method, ...params === void 0 ? {} : { params } });
|
|
28243
28254
|
});
|
|
28244
28255
|
}
|
|
28245
28256
|
forward(message) {
|
|
28246
|
-
return new Promise((
|
|
28247
|
-
this.forwarded.set(JSON.stringify(message.id),
|
|
28257
|
+
return new Promise((resolve4) => {
|
|
28258
|
+
this.forwarded.set(JSON.stringify(message.id), resolve4);
|
|
28248
28259
|
this.send(message);
|
|
28249
28260
|
});
|
|
28250
28261
|
}
|
|
@@ -28280,18 +28291,18 @@ data: ${JSON.stringify(message)}
|
|
|
28280
28291
|
const child = this.child;
|
|
28281
28292
|
if (!child) return;
|
|
28282
28293
|
this.child = void 0;
|
|
28283
|
-
await new Promise((
|
|
28294
|
+
await new Promise((resolve4) => {
|
|
28284
28295
|
const timer = setTimeout(() => {
|
|
28285
28296
|
try {
|
|
28286
28297
|
child.kill("SIGKILL");
|
|
28287
28298
|
} catch {
|
|
28288
28299
|
}
|
|
28289
28300
|
;
|
|
28290
|
-
|
|
28301
|
+
resolve4();
|
|
28291
28302
|
}, 2e3);
|
|
28292
28303
|
child.once("exit", () => {
|
|
28293
28304
|
clearTimeout(timer);
|
|
28294
|
-
|
|
28305
|
+
resolve4();
|
|
28295
28306
|
});
|
|
28296
28307
|
try {
|
|
28297
28308
|
child.stdin?.end();
|
|
@@ -28305,7 +28316,7 @@ data: ${JSON.stringify(message)}
|
|
|
28305
28316
|
}
|
|
28306
28317
|
};
|
|
28307
28318
|
function readBody(request) {
|
|
28308
|
-
return new Promise((
|
|
28319
|
+
return new Promise((resolve4, reject) => {
|
|
28309
28320
|
let size = 0;
|
|
28310
28321
|
const chunks = [];
|
|
28311
28322
|
request.on("data", (chunk) => {
|
|
@@ -28317,7 +28328,7 @@ function readBody(request) {
|
|
|
28317
28328
|
}
|
|
28318
28329
|
chunks.push(chunk);
|
|
28319
28330
|
});
|
|
28320
|
-
request.on("end", () =>
|
|
28331
|
+
request.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
|
|
28321
28332
|
request.on("error", reject);
|
|
28322
28333
|
});
|
|
28323
28334
|
}
|
|
@@ -28368,13 +28379,13 @@ function startLazyMcpHost(options) {
|
|
|
28368
28379
|
})();
|
|
28369
28380
|
});
|
|
28370
28381
|
httpServer.requestTimeout = 0;
|
|
28371
|
-
return new Promise((
|
|
28382
|
+
return new Promise((resolve4, reject) => {
|
|
28372
28383
|
httpServer.once("error", reject);
|
|
28373
28384
|
httpServer.listen(0, "127.0.0.1", () => {
|
|
28374
28385
|
httpServer.off("error", reject);
|
|
28375
28386
|
const address = httpServer.address();
|
|
28376
28387
|
const port = typeof address === "object" && address ? address.port : 0;
|
|
28377
|
-
|
|
28388
|
+
resolve4({
|
|
28378
28389
|
port,
|
|
28379
28390
|
mount(stdio, context) {
|
|
28380
28391
|
mkdirSync4(options.cacheDir, { recursive: true, mode: 448 });
|
|
@@ -28401,7 +28412,6 @@ function startLazyMcpHost(options) {
|
|
|
28401
28412
|
// packages/server/src/backend/types.ts
|
|
28402
28413
|
var CLAUDE_WORKER_ENV = {
|
|
28403
28414
|
CLAUDE_CODE_TOTAL_TOKENS_REMINDER: "infinite",
|
|
28404
|
-
BASH_DEFAULT_TIMEOUT_MS: "60000",
|
|
28405
28415
|
BASH_MAX_TIMEOUT_MS: String(24 * 60 * 60 * 1e3)
|
|
28406
28416
|
};
|
|
28407
28417
|
var CLAUDE_AUTO_COMPACT_WINDOW_ENV = "CLAUDE_CODE_AUTO_COMPACT_WINDOW";
|
|
@@ -28466,9 +28476,292 @@ function createClaudeBrokerExitWriter(path, meta, now = () => /* @__PURE__ */ ne
|
|
|
28466
28476
|
};
|
|
28467
28477
|
}
|
|
28468
28478
|
|
|
28479
|
+
// packages/server/src/backend/claude-plugin-sync.ts
|
|
28480
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
28481
|
+
import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
|
|
28482
|
+
import { join as join9 } from "node:path";
|
|
28483
|
+
|
|
28484
|
+
// packages/server/src/stable-plugin-path.ts
|
|
28485
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
28486
|
+
import { cpSync, lstatSync as lstatSync3, mkdirSync as mkdirSync6, readFileSync as readFileSync6, readdirSync as readdirSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync2, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync4, symlinkSync } from "node:fs";
|
|
28487
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join8, resolve as resolve3 } from "node:path";
|
|
28488
|
+
var LINK_TYPE = process.platform === "win32" ? "junction" : void 0;
|
|
28489
|
+
function pluginFiles(root) {
|
|
28490
|
+
const files = [];
|
|
28491
|
+
const walk = (dir, prefix) => {
|
|
28492
|
+
for (const name of readdirSync3(dir).sort()) {
|
|
28493
|
+
const rel = prefix ? `${prefix}/${name}` : name;
|
|
28494
|
+
if (statSync4(join8(dir, name)).isDirectory()) walk(join8(dir, name), rel);
|
|
28495
|
+
else files.push(rel);
|
|
28496
|
+
}
|
|
28497
|
+
};
|
|
28498
|
+
walk(root, "");
|
|
28499
|
+
return files;
|
|
28500
|
+
}
|
|
28501
|
+
function pluginFingerprint(dir) {
|
|
28502
|
+
try {
|
|
28503
|
+
const root = realpathSync2(dir);
|
|
28504
|
+
const hash = createHash3("sha1").update(root);
|
|
28505
|
+
for (const rel of pluginFiles(root)) {
|
|
28506
|
+
const stat = statSync4(join8(root, rel));
|
|
28507
|
+
hash.update(`\0${rel}\0${stat.size}\0${stat.mtimeMs}`);
|
|
28508
|
+
}
|
|
28509
|
+
return hash.digest("hex");
|
|
28510
|
+
} catch {
|
|
28511
|
+
return void 0;
|
|
28512
|
+
}
|
|
28513
|
+
}
|
|
28514
|
+
|
|
28515
|
+
// packages/server/src/backend/claude-plugin-sync.ts
|
|
28516
|
+
function claudePluginSyncEnabled(env = process.env) {
|
|
28517
|
+
return env.FRIZZ_PLUGIN_SYNC_OFF !== "1";
|
|
28518
|
+
}
|
|
28519
|
+
var RELOAD_DEADLINE_MS = 8e3;
|
|
28520
|
+
var RELOAD_ATTEMPTS = 2;
|
|
28521
|
+
var ClosureUnreadable = class extends Error {
|
|
28522
|
+
};
|
|
28523
|
+
function claudeDirEntries(root, entries) {
|
|
28524
|
+
const stamp = (path) => {
|
|
28525
|
+
try {
|
|
28526
|
+
const stat = statSync5(path);
|
|
28527
|
+
if (stat.isFile()) entries.push(`${path}\0${stat.size}\0${stat.mtimeMs}`);
|
|
28528
|
+
} catch {
|
|
28529
|
+
}
|
|
28530
|
+
};
|
|
28531
|
+
const list = (dir) => {
|
|
28532
|
+
try {
|
|
28533
|
+
return readdirSync4(dir).sort();
|
|
28534
|
+
} catch {
|
|
28535
|
+
return [];
|
|
28536
|
+
}
|
|
28537
|
+
};
|
|
28538
|
+
for (const name of list(join9(root, "skills"))) stamp(join9(root, "skills", name, "SKILL.md"));
|
|
28539
|
+
const markdownUnder = (dir, depth) => {
|
|
28540
|
+
for (const name of list(dir)) {
|
|
28541
|
+
const path = join9(dir, name);
|
|
28542
|
+
if (name.endsWith(".md")) stamp(path);
|
|
28543
|
+
else if (depth < 4) markdownUnder(path, depth + 1);
|
|
28544
|
+
}
|
|
28545
|
+
};
|
|
28546
|
+
markdownUnder(join9(root, "agents"), 0);
|
|
28547
|
+
markdownUnder(join9(root, "commands"), 0);
|
|
28548
|
+
for (const file of ["settings.json", "settings.local.json"]) {
|
|
28549
|
+
let raw;
|
|
28550
|
+
try {
|
|
28551
|
+
raw = readFileSync7(join9(root, file), "utf8");
|
|
28552
|
+
} catch {
|
|
28553
|
+
continue;
|
|
28554
|
+
}
|
|
28555
|
+
let enabled;
|
|
28556
|
+
try {
|
|
28557
|
+
enabled = JSON.parse(raw)?.enabledPlugins;
|
|
28558
|
+
} catch {
|
|
28559
|
+
throw new ClosureUnreadable(file);
|
|
28560
|
+
}
|
|
28561
|
+
entries.push(`${join9(root, file)}\0${JSON.stringify(enabled ?? null)}`);
|
|
28562
|
+
}
|
|
28563
|
+
stamp(join9(root, "plugins", "installed_plugins.json"));
|
|
28564
|
+
}
|
|
28565
|
+
function claudeClosureFingerprint(closure) {
|
|
28566
|
+
const plugin = closure.pluginDir ? pluginFingerprint(closure.pluginDir) : "";
|
|
28567
|
+
if (plugin === void 0) return void 0;
|
|
28568
|
+
const entries = [plugin];
|
|
28569
|
+
try {
|
|
28570
|
+
claudeDirEntries(join9(closure.projectDir, ".claude"), entries);
|
|
28571
|
+
claudeDirEntries(closure.configDir, entries);
|
|
28572
|
+
} catch {
|
|
28573
|
+
return void 0;
|
|
28574
|
+
}
|
|
28575
|
+
return createHash4("sha1").update(entries.join("\n")).digest("hex");
|
|
28576
|
+
}
|
|
28577
|
+
function createClaudePluginSync(deps) {
|
|
28578
|
+
let loaded = deps.fingerprint();
|
|
28579
|
+
let busy = false;
|
|
28580
|
+
let inFlight = null;
|
|
28581
|
+
let failed;
|
|
28582
|
+
const say = (message) => {
|
|
28583
|
+
try {
|
|
28584
|
+
deps.log(message);
|
|
28585
|
+
} catch {
|
|
28586
|
+
}
|
|
28587
|
+
};
|
|
28588
|
+
const reloadWithinDeadline = () => new Promise((resolve4, reject) => {
|
|
28589
|
+
const timer = setTimeout(() => reject(new Error("the session did not answer in time")), deps.deadlineMs ?? RELOAD_DEADLINE_MS);
|
|
28590
|
+
timer.unref?.();
|
|
28591
|
+
deps.reload().then(
|
|
28592
|
+
(result) => {
|
|
28593
|
+
clearTimeout(timer);
|
|
28594
|
+
resolve4(result);
|
|
28595
|
+
},
|
|
28596
|
+
(error) => {
|
|
28597
|
+
clearTimeout(timer);
|
|
28598
|
+
reject(error);
|
|
28599
|
+
}
|
|
28600
|
+
);
|
|
28601
|
+
});
|
|
28602
|
+
const check = () => {
|
|
28603
|
+
if (inFlight) return;
|
|
28604
|
+
const current = deps.fingerprint();
|
|
28605
|
+
if (current === void 0 || current === loaded) return;
|
|
28606
|
+
if (failed?.fingerprint === current && failed.attempts >= RELOAD_ATTEMPTS) return;
|
|
28607
|
+
const attempt = reloadWithinDeadline().then(
|
|
28608
|
+
(result) => {
|
|
28609
|
+
loaded = current;
|
|
28610
|
+
failed = void 0;
|
|
28611
|
+
say(`plugin closure changed on disk; reloaded in place${result.errorCount ? ` with ${result.errorCount} load error(s)` : ""}`);
|
|
28612
|
+
},
|
|
28613
|
+
(error) => {
|
|
28614
|
+
failed = { fingerprint: current, attempts: failed?.fingerprint === current ? failed.attempts + 1 : 1 };
|
|
28615
|
+
say(`plugin closure changed on disk but the reload failed (attempt ${failed.attempts}/${RELOAD_ATTEMPTS}): ${error instanceof Error ? error.message : String(error)}`);
|
|
28616
|
+
}
|
|
28617
|
+
).catch(() => void 0).finally(() => {
|
|
28618
|
+
if (inFlight === attempt) inFlight = null;
|
|
28619
|
+
});
|
|
28620
|
+
inFlight = attempt;
|
|
28621
|
+
};
|
|
28622
|
+
return {
|
|
28623
|
+
beforeInput(startsTurn) {
|
|
28624
|
+
if (startsTurn) {
|
|
28625
|
+
if (!busy) check();
|
|
28626
|
+
busy = true;
|
|
28627
|
+
}
|
|
28628
|
+
return inFlight;
|
|
28629
|
+
},
|
|
28630
|
+
beforeRead() {
|
|
28631
|
+
if (!busy) check();
|
|
28632
|
+
return inFlight;
|
|
28633
|
+
},
|
|
28634
|
+
turnActive() {
|
|
28635
|
+
busy = true;
|
|
28636
|
+
},
|
|
28637
|
+
turnEnded() {
|
|
28638
|
+
busy = false;
|
|
28639
|
+
check();
|
|
28640
|
+
}
|
|
28641
|
+
};
|
|
28642
|
+
}
|
|
28643
|
+
|
|
28644
|
+
// packages/server/src/backend/claude-quota-observed.ts
|
|
28645
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
28646
|
+
import { mkdir, readFile, rename, rm as rm2, writeFile } from "node:fs/promises";
|
|
28647
|
+
import { join as join10 } from "node:path";
|
|
28648
|
+
var WINDOW_IDENTITY = {
|
|
28649
|
+
five_hour: ["5h", "5h"],
|
|
28650
|
+
seven_day: ["weekly", "Weekly"],
|
|
28651
|
+
seven_day_opus: ["weekly-opus", "Opus wk"],
|
|
28652
|
+
seven_day_sonnet: ["weekly-sonnet", "Sonnet wk"]
|
|
28653
|
+
};
|
|
28654
|
+
var OBSERVED_MAX_AGE_MS = 24 * 60 * 6e4;
|
|
28655
|
+
var REWRITE_INTERVAL_MS = 3e4;
|
|
28656
|
+
function finite(value) {
|
|
28657
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
28658
|
+
}
|
|
28659
|
+
function toWindow(name, utilization, resetsAt) {
|
|
28660
|
+
const identity = WINDOW_IDENTITY[name];
|
|
28661
|
+
const fraction = finite(utilization);
|
|
28662
|
+
if (!identity || fraction === void 0 || fraction < 0) return void 0;
|
|
28663
|
+
const resets = finite(resetsAt);
|
|
28664
|
+
return {
|
|
28665
|
+
key: identity[0],
|
|
28666
|
+
label: identity[1],
|
|
28667
|
+
// 0..1 → the endpoint's 0..100, without the binary-float tail (0.65 * 100 is 65.00000000000001).
|
|
28668
|
+
usedPercent: Math.round(fraction * 1e4) / 100,
|
|
28669
|
+
...resets !== void 0 && resets > 0 ? { resetsAt: Math.round(resets) } : {}
|
|
28670
|
+
};
|
|
28671
|
+
}
|
|
28672
|
+
function windowsFromRateLimitInfo(info) {
|
|
28673
|
+
if (!info || typeof info !== "object") return [];
|
|
28674
|
+
const raw = info;
|
|
28675
|
+
const windows = /* @__PURE__ */ new Map();
|
|
28676
|
+
if (raw.unifiedWindows && typeof raw.unifiedWindows === "object") {
|
|
28677
|
+
for (const [name, value] of Object.entries(raw.unifiedWindows)) {
|
|
28678
|
+
if (!value || typeof value !== "object") continue;
|
|
28679
|
+
const w = value;
|
|
28680
|
+
const window = toWindow(name, w.utilization, w.resetsAt);
|
|
28681
|
+
if (window) windows.set(window.key, window);
|
|
28682
|
+
}
|
|
28683
|
+
}
|
|
28684
|
+
if (typeof raw.rateLimitType === "string") {
|
|
28685
|
+
const window = toWindow(raw.rateLimitType, raw.utilization, raw.resetsAt);
|
|
28686
|
+
if (window && !windows.has(window.key)) windows.set(window.key, window);
|
|
28687
|
+
const identity = WINDOW_IDENTITY[raw.rateLimitType];
|
|
28688
|
+
if (raw.status === "rejected" && identity) {
|
|
28689
|
+
const standing = windows.get(identity[0]);
|
|
28690
|
+
const resets = finite(raw.resetsAt);
|
|
28691
|
+
windows.set(identity[0], {
|
|
28692
|
+
key: identity[0],
|
|
28693
|
+
label: identity[1],
|
|
28694
|
+
usedPercent: Math.max(100, standing?.usedPercent ?? 0),
|
|
28695
|
+
...standing?.resetsAt !== void 0 ? { resetsAt: standing.resetsAt } : resets !== void 0 && resets > 0 ? { resetsAt: Math.round(resets) } : {}
|
|
28696
|
+
});
|
|
28697
|
+
}
|
|
28698
|
+
}
|
|
28699
|
+
return [...windows.values()];
|
|
28700
|
+
}
|
|
28701
|
+
function observedQuotaPath(cacheDir, configDir) {
|
|
28702
|
+
const profile = createHash5("sha256").update(configDir).digest("hex").slice(0, 12);
|
|
28703
|
+
return join10(cacheDir, `claude-observed-${profile}.json`);
|
|
28704
|
+
}
|
|
28705
|
+
function live(window, now) {
|
|
28706
|
+
if (now - window.at >= OBSERVED_MAX_AGE_MS) return false;
|
|
28707
|
+
return window.resetsAt === void 0 || window.resetsAt * 1e3 > now;
|
|
28708
|
+
}
|
|
28709
|
+
async function readObservedClaudeQuota(cacheDir, configDir, now = Date.now()) {
|
|
28710
|
+
let parsed;
|
|
28711
|
+
try {
|
|
28712
|
+
parsed = JSON.parse(await readFile(observedQuotaPath(cacheDir, configDir), "utf8"));
|
|
28713
|
+
} catch {
|
|
28714
|
+
return void 0;
|
|
28715
|
+
}
|
|
28716
|
+
const list = parsed?.windows;
|
|
28717
|
+
if (!Array.isArray(list)) return void 0;
|
|
28718
|
+
const windows = [];
|
|
28719
|
+
for (const entry of list) {
|
|
28720
|
+
if (!entry || typeof entry !== "object") continue;
|
|
28721
|
+
const w = entry;
|
|
28722
|
+
const at2 = finite(w.at);
|
|
28723
|
+
const usedPercent = finite(w.usedPercent);
|
|
28724
|
+
const resetsAt = finite(w.resetsAt);
|
|
28725
|
+
if (typeof w.key !== "string" || typeof w.label !== "string" || at2 === void 0 || usedPercent === void 0) continue;
|
|
28726
|
+
const window = { key: w.key, label: w.label, usedPercent, at: at2, ...resetsAt === void 0 ? {} : { resetsAt } };
|
|
28727
|
+
if (live(window, now)) windows.push(window);
|
|
28728
|
+
}
|
|
28729
|
+
if (windows.length === 0) return void 0;
|
|
28730
|
+
return { at: Math.max(...windows.map((w) => w.at)), windows };
|
|
28731
|
+
}
|
|
28732
|
+
var lastWritten = /* @__PURE__ */ new Map();
|
|
28733
|
+
async function recordObservedClaudeQuota(info, where) {
|
|
28734
|
+
try {
|
|
28735
|
+
const now = where.now ?? Date.now();
|
|
28736
|
+
const reported = windowsFromRateLimitInfo(info);
|
|
28737
|
+
if (reported.length === 0) return false;
|
|
28738
|
+
const path = observedQuotaPath(where.cacheDir, where.configDir);
|
|
28739
|
+
const signature = JSON.stringify(reported);
|
|
28740
|
+
const last = lastWritten.get(path);
|
|
28741
|
+
if (last && last.signature === signature && now - last.at < REWRITE_INTERVAL_MS) return false;
|
|
28742
|
+
const standing = await readObservedClaudeQuota(where.cacheDir, where.configDir, now);
|
|
28743
|
+
const windows = new Map((standing?.windows ?? []).map((w) => [w.key, w]));
|
|
28744
|
+
for (const window of reported) windows.set(window.key, { ...window, at: now });
|
|
28745
|
+
await mkdir(where.cacheDir, { recursive: true, mode: 448 });
|
|
28746
|
+
const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
28747
|
+
try {
|
|
28748
|
+
await writeFile(tmp, JSON.stringify({ at: now, windows: [...windows.values()] }), { mode: 384 });
|
|
28749
|
+
await rename(tmp, path);
|
|
28750
|
+
} finally {
|
|
28751
|
+
await rm2(tmp, { force: true }).catch(() => {
|
|
28752
|
+
});
|
|
28753
|
+
}
|
|
28754
|
+
lastWritten.set(path, { at: now, signature });
|
|
28755
|
+
return true;
|
|
28756
|
+
} catch {
|
|
28757
|
+
return false;
|
|
28758
|
+
}
|
|
28759
|
+
}
|
|
28760
|
+
|
|
28469
28761
|
// packages/server/src/backend/claude-agent-broker.ts
|
|
28470
28762
|
var BROKER_CAPABILITIES = [CLAUDE_BROKER_CAPABILITY_SUBAGENT_STEER, CLAUDE_BROKER_CAPABILITY_CANCEL_INPUT, CLAUDE_BROKER_CAPABILITY_STOP_TASK, CLAUDE_BROKER_CAPABILITY_RELOAD_PLUGINS, CLAUDE_BROKER_CAPABILITY_RENAME, CLAUDE_BROKER_CAPABILITY_LIST_SKILLS, CLAUDE_BROKER_CAPABILITY_INPUT_ACK];
|
|
28471
28763
|
var IDLE_EXIT_MS = 6 * 60 * 60 * 1e3;
|
|
28764
|
+
var PERMISSION_SHOW_HOLD_MS = 250;
|
|
28472
28765
|
var REACHABILITY_CHECK_MS = 3e4;
|
|
28473
28766
|
var REACHABILITY_STRIKES = 2;
|
|
28474
28767
|
var EVENT_ERROR_TOLERANCE = 5;
|
|
@@ -28511,6 +28804,17 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28511
28804
|
const releaseRuntimeLease = leaseRuntime(config.executablePath, "claude-broker");
|
|
28512
28805
|
const factory = createClaudeQueryFactory({ enabled: true, executablePath: config.executablePath });
|
|
28513
28806
|
const env = { ...inheritWorkerEnvironment(config.env), ...config.workerEnv ?? {} };
|
|
28807
|
+
const closure = {
|
|
28808
|
+
pluginDir: config.pluginDir,
|
|
28809
|
+
projectDir: config.cwd,
|
|
28810
|
+
// Where claude itself will look, from the environment it is about to be started with.
|
|
28811
|
+
configDir: env.CLAUDE_CONFIG_DIR?.trim() || join11(env.HOME || homedir3(), ".claude")
|
|
28812
|
+
};
|
|
28813
|
+
const pluginSync = claudePluginSyncEnabled() ? createClaudePluginSync({
|
|
28814
|
+
fingerprint: () => claudeClosureFingerprint(closure),
|
|
28815
|
+
reload: () => handle.reloadPlugins(),
|
|
28816
|
+
log: (message) => writeDiagnostic?.({ kind: "stderr", message: `[plugin-sync] ${message}`, truncated: false })
|
|
28817
|
+
}) : void 0;
|
|
28514
28818
|
const handle = factory.start({
|
|
28515
28819
|
cwd: config.cwd,
|
|
28516
28820
|
session: config.resume ? { kind: "resume", sessionId: config.sessionId } : { kind: "new", sessionId: config.sessionId },
|
|
@@ -28549,20 +28853,40 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28549
28853
|
// and unwinds the tool call (see retirePendingFor in the bridge), so a parked turn is still steerable.
|
|
28550
28854
|
canUseTool: async (request, context) => {
|
|
28551
28855
|
const requestId = `perm-${++permSeq}`;
|
|
28552
|
-
return await new Promise((
|
|
28856
|
+
return await new Promise((resolve4) => {
|
|
28553
28857
|
let settled = false;
|
|
28858
|
+
let showTimer;
|
|
28859
|
+
const entry = { request, shown: false, resolve: (decision) => settle(decision) };
|
|
28554
28860
|
const settle = (decision) => {
|
|
28555
28861
|
if (settled) return;
|
|
28556
28862
|
settled = true;
|
|
28863
|
+
clearTimeout(showTimer);
|
|
28557
28864
|
pendingPermissions.delete(requestId);
|
|
28558
|
-
|
|
28865
|
+
resolve4(decision);
|
|
28866
|
+
};
|
|
28867
|
+
const withdraw = () => {
|
|
28868
|
+
const wasShown = entry.shown;
|
|
28869
|
+
settle({ behavior: "deny", message: "The turn was interrupted before this was answered." });
|
|
28870
|
+
if (wasShown && client) write(client, { t: "permission-withdrawn", requestId });
|
|
28559
28871
|
};
|
|
28560
|
-
pendingPermissions.set(requestId,
|
|
28561
|
-
if (context.signal.aborted)
|
|
28562
|
-
else context.signal.addEventListener("abort",
|
|
28563
|
-
if (!settled
|
|
28872
|
+
pendingPermissions.set(requestId, entry);
|
|
28873
|
+
if (context.signal.aborted) withdraw();
|
|
28874
|
+
else context.signal.addEventListener("abort", withdraw, { once: true });
|
|
28875
|
+
if (!settled) {
|
|
28876
|
+
showTimer = setTimeout(() => {
|
|
28877
|
+
if (settled || entry.shown || !client) return;
|
|
28878
|
+
entry.shown = true;
|
|
28879
|
+
write(client, { t: "permission-request", requestId, request });
|
|
28880
|
+
}, PERMISSION_SHOW_HOLD_MS);
|
|
28881
|
+
}
|
|
28564
28882
|
});
|
|
28565
28883
|
},
|
|
28884
|
+
// The account's plan windows, as every API response reports them to this session. Written by THIS
|
|
28885
|
+
// process because it is the one that sees the stream and it outlives the Frizz server; the server
|
|
28886
|
+
// only reads the file (claude-quota-observed.ts). Fire-and-forget: the recorder never rejects.
|
|
28887
|
+
onRateLimit: (info) => {
|
|
28888
|
+
void recordObservedClaudeQuota(info, { cacheDir: join11(frizzRoots().cache, "quota-cache"), configDir: closure.configDir });
|
|
28889
|
+
},
|
|
28566
28890
|
onDiagnostic: (diagnostic) => {
|
|
28567
28891
|
writeDiagnostic?.(diagnostic);
|
|
28568
28892
|
if (client) write(client, { t: "diagnostic", diagnostic });
|
|
@@ -28598,6 +28922,8 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28598
28922
|
consecutiveErrors = 0;
|
|
28599
28923
|
if (next.done) return { reason: "session-stream-ended" };
|
|
28600
28924
|
emitEvent(next.value);
|
|
28925
|
+
if (next.value.kind === "result") pluginSync?.turnEnded();
|
|
28926
|
+
else if (next.value.kind === "assistant" || next.value.kind === "user") pluginSync?.turnActive();
|
|
28601
28927
|
}
|
|
28602
28928
|
})().then(
|
|
28603
28929
|
(end) => shutdown(0, end.reason, end.detail),
|
|
@@ -28612,7 +28938,10 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28612
28938
|
client = sock;
|
|
28613
28939
|
clearTimeout(idleTimer);
|
|
28614
28940
|
write(sock, { t: "hello", sessionId: handle.sessionId, generation });
|
|
28615
|
-
for (const [requestId,
|
|
28941
|
+
for (const [requestId, entry] of pendingPermissions) {
|
|
28942
|
+
entry.shown = true;
|
|
28943
|
+
write(sock, { t: "permission-request", requestId, request: entry.request });
|
|
28944
|
+
}
|
|
28616
28945
|
while (eventBacklog.length) sock.write(eventBacklog.shift());
|
|
28617
28946
|
eventBacklogBytes = 0;
|
|
28618
28947
|
let buf = "";
|
|
@@ -28636,7 +28965,7 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28636
28965
|
message: `input received: id=${message?.id ?? "?"} chars=${typeof message?.text === "string" ? message.text.length : 0}${message?.parentToolUseId ? ` addressed=${message.parentToolUseId}` : ""}`,
|
|
28637
28966
|
truncated: false
|
|
28638
28967
|
});
|
|
28639
|
-
void handle.send(message).then(() => {
|
|
28968
|
+
const forward = () => void handle.send(message).then(() => {
|
|
28640
28969
|
if (requestId) write(sock, { t: "input-result", requestId });
|
|
28641
28970
|
}, (error) => {
|
|
28642
28971
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -28646,6 +28975,9 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28646
28975
|
writeDiagnostic?.(diagnostic);
|
|
28647
28976
|
if (client) write(client, { t: "diagnostic", diagnostic });
|
|
28648
28977
|
});
|
|
28978
|
+
const reloading = pluginSync?.beforeInput(!message?.parentToolUseId);
|
|
28979
|
+
if (reloading) void reloading.then(forward);
|
|
28980
|
+
else forward();
|
|
28649
28981
|
seedSessionTitle(message);
|
|
28650
28982
|
} else if (msg.t === "permission") {
|
|
28651
28983
|
const e13 = pendingPermissions.get(msg.requestId);
|
|
@@ -28684,7 +29016,7 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28684
29016
|
);
|
|
28685
29017
|
} else if (msg.t === "list-skills") {
|
|
28686
29018
|
const requestId = msg.requestId;
|
|
28687
|
-
void handle.listSkills().then(
|
|
29019
|
+
void (pluginSync?.beforeRead() ?? Promise.resolve()).then(() => handle.listSkills()).then(
|
|
28688
29020
|
(skills) => write(sock, { t: "skills-result", requestId, skills }),
|
|
28689
29021
|
(error) => write(sock, { t: "skills-result", requestId, error: error instanceof Error ? error.message : String(error) })
|
|
28690
29022
|
);
|
|
@@ -28705,7 +29037,7 @@ function runClaudeBroker(config, options = {}) {
|
|
|
28705
29037
|
const recordOwner = () => {
|
|
28706
29038
|
if (!config.recordPath) return null;
|
|
28707
29039
|
try {
|
|
28708
|
-
return JSON.parse(
|
|
29040
|
+
return JSON.parse(readFileSync8(config.recordPath, "utf8")).daemonPid;
|
|
28709
29041
|
} catch {
|
|
28710
29042
|
return null;
|
|
28711
29043
|
}
|
|
@@ -28800,7 +29132,7 @@ function startedAsProcessEntry() {
|
|
|
28800
29132
|
const entry = process.argv[1];
|
|
28801
29133
|
if (!entry) return false;
|
|
28802
29134
|
try {
|
|
28803
|
-
return
|
|
29135
|
+
return realpathSync3(fileURLToPath(import.meta.url)) === realpathSync3(entry);
|
|
28804
29136
|
} catch {
|
|
28805
29137
|
return false;
|
|
28806
29138
|
}
|
|
@@ -28831,7 +29163,7 @@ ${error.stack ?? ""}`.slice(0, 2e3) : String(error));
|
|
|
28831
29163
|
${reason.stack ?? ""}`.slice(0, 2e3) : String(reason));
|
|
28832
29164
|
process.exit(1);
|
|
28833
29165
|
});
|
|
28834
|
-
const lazyMcpHost = lazyMcpEnabled() ? startLazyMcpHost({ cacheDir:
|
|
29166
|
+
const lazyMcpHost = lazyMcpEnabled() ? startLazyMcpHost({ cacheDir: join11(frizzRoots().data, "mcp-cache") }) : Promise.resolve(void 0);
|
|
28835
29167
|
void lazyMcpHost.catch(() => void 0).then((host) => {
|
|
28836
29168
|
running = runClaudeBroker(config, { lazyMcpHost: host });
|
|
28837
29169
|
});
|