@vibgrate/cli 2026.819.1 → 2026.819.3
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/DOCS.md +3 -3
- package/dist/agent-AFMDB4GM.js +30 -0
- package/dist/{agent-3CKNTCRX.js.map → agent-AFMDB4GM.js.map} +1 -1
- package/dist/{approvals-DHTG3HI4.js → approvals-F2DJGPU7.js} +7 -7
- package/dist/{approvals-DHTG3HI4.js.map → approvals-F2DJGPU7.js.map} +1 -1
- package/dist/attach-XTX2Q3AD.js +10 -0
- package/dist/{attach-OCQ3I76L.js.map → attach-XTX2Q3AD.js.map} +1 -1
- package/dist/baseline-KTE2C3LW.js +9 -0
- package/dist/{baseline-QDC6OQ2X.js.map → baseline-KTE2C3LW.js.map} +1 -1
- package/dist/{chunk-D33M2ARL.js → chunk-3SOJZ3VV.js} +3 -3
- package/dist/{chunk-D33M2ARL.js.map → chunk-3SOJZ3VV.js.map} +1 -1
- package/dist/{chunk-JGBOYZAF.js → chunk-3XMTRK24.js} +99 -19
- package/dist/chunk-3XMTRK24.js.map +1 -0
- package/dist/{chunk-OV4KGXPD.js → chunk-57ZRSCHY.js} +3 -3
- package/dist/{chunk-OV4KGXPD.js.map → chunk-57ZRSCHY.js.map} +1 -1
- package/dist/{chunk-KFEM6FWP.js → chunk-5R4HYDTC.js} +4 -4
- package/dist/{chunk-KFEM6FWP.js.map → chunk-5R4HYDTC.js.map} +1 -1
- package/dist/{chunk-EI5AKDSC.js → chunk-5RMME7PT.js} +3 -3
- package/dist/{chunk-EI5AKDSC.js.map → chunk-5RMME7PT.js.map} +1 -1
- package/dist/{chunk-OQT3JBWW.js → chunk-5UG4UHKU.js} +205 -36
- package/dist/chunk-5UG4UHKU.js.map +1 -0
- package/dist/{chunk-NDNNOAS6.js → chunk-6H2QV5DR.js} +3 -3
- package/dist/{chunk-NDNNOAS6.js.map → chunk-6H2QV5DR.js.map} +1 -1
- package/dist/{chunk-LJSJX6U4.js → chunk-A3WWRW7U.js} +3 -3
- package/dist/{chunk-LJSJX6U4.js.map → chunk-A3WWRW7U.js.map} +1 -1
- package/dist/{chunk-O5ASXWKF.js → chunk-ARGYMFST.js} +3 -3
- package/dist/{chunk-O5ASXWKF.js.map → chunk-ARGYMFST.js.map} +1 -1
- package/dist/{chunk-FFY6NW7P.js → chunk-C5AOLTQS.js} +5 -5
- package/dist/{chunk-FFY6NW7P.js.map → chunk-C5AOLTQS.js.map} +1 -1
- package/dist/{chunk-F46Q3AEI.js → chunk-CEJIX3O7.js} +44 -15
- package/dist/chunk-CEJIX3O7.js.map +1 -0
- package/dist/{chunk-QMRSCJUN.js → chunk-DMI4BMUL.js} +3 -3
- package/dist/{chunk-QMRSCJUN.js.map → chunk-DMI4BMUL.js.map} +1 -1
- package/dist/{chunk-TS25WFAT.js → chunk-EXFA7QFC.js} +9 -9
- package/dist/{chunk-TS25WFAT.js.map → chunk-EXFA7QFC.js.map} +1 -1
- package/dist/{chunk-VYSDR2QE.js → chunk-HB3SONYP.js} +4 -4
- package/dist/{chunk-VYSDR2QE.js.map → chunk-HB3SONYP.js.map} +1 -1
- package/dist/{chunk-KKQX3F5F.js → chunk-HRYF4PWE.js} +7 -5
- package/dist/chunk-HRYF4PWE.js.map +1 -0
- package/dist/{chunk-EIESBKLW.js → chunk-HVJHFTIV.js} +7 -7
- package/dist/{chunk-EIESBKLW.js.map → chunk-HVJHFTIV.js.map} +1 -1
- package/dist/{chunk-JFE7XCAE.js → chunk-IASO2HCI.js} +3 -3
- package/dist/{chunk-JFE7XCAE.js.map → chunk-IASO2HCI.js.map} +1 -1
- package/dist/chunk-MQJY4EQV.js +6 -0
- package/dist/{chunk-DHU3OOEI.js.map → chunk-MQJY4EQV.js.map} +1 -1
- package/dist/{chunk-7AVYKZEM.js → chunk-NV7XNBX4.js} +4 -4
- package/dist/{chunk-7AVYKZEM.js.map → chunk-NV7XNBX4.js.map} +1 -1
- package/dist/{chunk-NZELMYT4.js → chunk-OLIMW4PQ.js} +8 -56
- package/dist/chunk-OLIMW4PQ.js.map +1 -0
- package/dist/{chunk-NWWLUFN5.js → chunk-PQISIPYP.js} +5 -5
- package/dist/{chunk-NWWLUFN5.js.map → chunk-PQISIPYP.js.map} +1 -1
- package/dist/{chunk-5XY4EFR7.js → chunk-QLNJ3VWO.js} +3 -3
- package/dist/{chunk-5XY4EFR7.js.map → chunk-QLNJ3VWO.js.map} +1 -1
- package/dist/{chunk-D44PGMH7.js → chunk-QOU2Q5L4.js} +12 -12
- package/dist/{chunk-D44PGMH7.js.map → chunk-QOU2Q5L4.js.map} +1 -1
- package/dist/{chunk-TS7STRMI.js → chunk-RTURQXQL.js} +4 -4
- package/dist/{chunk-TS7STRMI.js.map → chunk-RTURQXQL.js.map} +1 -1
- package/dist/{chunk-74UBGF4P.js → chunk-SHD5TCZ5.js} +3 -3
- package/dist/{chunk-74UBGF4P.js.map → chunk-SHD5TCZ5.js.map} +1 -1
- package/dist/{chunk-WJYJCDNY.js → chunk-SOAEPTSW.js} +13 -13
- package/dist/{chunk-WJYJCDNY.js.map → chunk-SOAEPTSW.js.map} +1 -1
- package/dist/{chunk-EMEMHNE3.js → chunk-TD4JAEMH.js} +6 -6
- package/dist/{chunk-EMEMHNE3.js.map → chunk-TD4JAEMH.js.map} +1 -1
- package/dist/{chunk-5O6O7DJH.js → chunk-UAPA5AHX.js} +3 -3
- package/dist/{chunk-5O6O7DJH.js.map → chunk-UAPA5AHX.js.map} +1 -1
- package/dist/{chunk-4VAA2XSM.js → chunk-UEXDSWKN.js} +6 -6
- package/dist/{chunk-4VAA2XSM.js.map → chunk-UEXDSWKN.js.map} +1 -1
- package/dist/{chunk-YIEP5GK5.js → chunk-UP2BR6TV.js} +15 -11
- package/dist/chunk-UP2BR6TV.js.map +1 -0
- package/dist/{chunk-6F52QR6F.js → chunk-VEQA2FSM.js} +4 -4
- package/dist/{chunk-6F52QR6F.js.map → chunk-VEQA2FSM.js.map} +1 -1
- package/dist/{chunk-E53WBVUQ.js → chunk-VHRSG6CF.js} +3 -3
- package/dist/{chunk-E53WBVUQ.js.map → chunk-VHRSG6CF.js.map} +1 -1
- package/dist/{chunk-I5LP55B6.js → chunk-W44THEXF.js} +3 -3
- package/dist/{chunk-I5LP55B6.js.map → chunk-W44THEXF.js.map} +1 -1
- package/dist/{chunk-FQVAQVJV.js → chunk-WI66PODG.js} +3 -3
- package/dist/{chunk-FQVAQVJV.js.map → chunk-WI66PODG.js.map} +1 -1
- package/dist/{chunk-CQDUBAQY.js → chunk-WNKGRXUA.js} +3 -3
- package/dist/{chunk-CQDUBAQY.js.map → chunk-WNKGRXUA.js.map} +1 -1
- package/dist/{chunk-FJG5AXKZ.js → chunk-WUFIXAEJ.js} +22 -6
- package/dist/chunk-WUFIXAEJ.js.map +1 -0
- package/dist/{chunk-QSLVOVNZ.js → chunk-XAS45W3G.js} +10 -10
- package/dist/{chunk-QSLVOVNZ.js.map → chunk-XAS45W3G.js.map} +1 -1
- package/dist/{chunk-BWPBB45M.js → chunk-XP6UUCZZ.js} +3 -3
- package/dist/{chunk-BWPBB45M.js.map → chunk-XP6UUCZZ.js.map} +1 -1
- package/dist/{chunk-BPF3YX2U.js → chunk-XQNKCA2J.js} +6 -3
- package/dist/chunk-XQNKCA2J.js.map +1 -0
- package/dist/{chunk-U7A7D2HP.js → chunk-XZDI2N7M.js} +7 -7
- package/dist/{chunk-U7A7D2HP.js.map → chunk-XZDI2N7M.js.map} +1 -1
- package/dist/{chunk-7N4Y3E47.js → chunk-YJYEPYB2.js} +13 -18
- package/dist/chunk-YJYEPYB2.js.map +1 -0
- package/dist/cli.js +104 -94
- package/dist/cli.js.map +1 -1
- package/dist/client-P5XSRFV4.js +8 -0
- package/dist/{client-UQIELEEZ.js.map → client-P5XSRFV4.js.map} +1 -1
- package/dist/daemon-VMQKIIYP.js +30 -0
- package/dist/{daemon-UNL65PTP.js.map → daemon-VMQKIIYP.js.map} +1 -1
- package/dist/embed-worker-main.js +5 -5
- package/dist/ensure-map-YIUJWMBK.js +40 -0
- package/dist/{ensure-map-4D2HJ6MA.js.map → ensure-map-YIUJWMBK.js.map} +1 -1
- package/dist/{federation-SZYVHYB2.js → federation-M4ZNXOPX.js} +5 -5
- package/dist/{federation-SZYVHYB2.js.map → federation-M4ZNXOPX.js.map} +1 -1
- package/dist/git-ref-LMT3EXYY.js +4 -0
- package/dist/{git-ref-RSRAN4OH.js.map → git-ref-LMT3EXYY.js.map} +1 -1
- package/dist/graph-backend-FDZUY4TR.js +16 -0
- package/dist/{graph-backend-36EIAAPQ.js.map → graph-backend-FDZUY4TR.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +23 -23
- package/dist/{interactive-ZZMHSZ3W.js → interactive-CS4PGZPK.js} +40 -40
- package/dist/{interactive-ZZMHSZ3W.js.map → interactive-CS4PGZPK.js.map} +1 -1
- package/dist/{llm-host-XFYFOAAX.js → llm-host-QVTJJVER.js} +5 -5
- package/dist/{llm-host-XFYFOAAX.js.map → llm-host-QVTJJVER.js.map} +1 -1
- package/dist/load-4EY64JJO.js +11 -0
- package/dist/{load-6CYLMUT5.js.map → load-4EY64JJO.js.map} +1 -1
- package/dist/local-runtime-GA43KYS5.js +6 -0
- package/dist/{local-runtime-6SNWKSA7.js.map → local-runtime-GA43KYS5.js.map} +1 -1
- package/dist/model-manager-6H3TMZKK.js +10 -0
- package/dist/{model-manager-SXHZ5VD4.js.map → model-manager-6H3TMZKK.js.map} +1 -1
- package/dist/{model-orchestrator-LHYPJBFH.js → model-orchestrator-X3NE3Y2G.js} +6 -6
- package/dist/{model-orchestrator-LHYPJBFH.js.map → model-orchestrator-X3NE3Y2G.js.map} +1 -1
- package/dist/parse-worker.js +2 -2
- package/dist/{paths-62AKTFSR.js → paths-RVK6FKRN.js} +4 -4
- package/dist/{paths-62AKTFSR.js.map → paths-RVK6FKRN.js.map} +1 -1
- package/dist/publish-27S66NOW.js +9 -0
- package/dist/{publish-7YHRTBRA.js.map → publish-27S66NOW.js.map} +1 -1
- package/dist/resolve-gguf-WVKVDN45.js +9 -0
- package/dist/{resolve-gguf-SDIYGGI6.js.map → resolve-gguf-WVKVDN45.js.map} +1 -1
- package/dist/{run-outcome-ADEAKXHH.js → run-outcome-T7VDBNWB.js} +7 -7
- package/dist/{run-outcome-ADEAKXHH.js.map → run-outcome-T7VDBNWB.js.map} +1 -1
- package/dist/runtime-session-WZLVADPQ.js +26 -0
- package/dist/{runtime-session-VNSXQAEO.js.map → runtime-session-WZLVADPQ.js.map} +1 -1
- package/dist/session-VPP2VGUN.js +17 -0
- package/dist/{session-3A6LKJHD.js.map → session-VPP2VGUN.js.map} +1 -1
- package/dist/{slot-subscription-25GOARW5.js → slot-subscription-C2ZUJED6.js} +5 -5
- package/dist/{slot-subscription-25GOARW5.js.map → slot-subscription-C2ZUJED6.js.map} +1 -1
- package/dist/{stream-json-S2TP2VPU.js → stream-json-XXCFN4MF.js} +17 -17
- package/dist/{stream-json-S2TP2VPU.js.map → stream-json-XXCFN4MF.js.map} +1 -1
- package/dist/version-2UBJ7N2I.js +4 -0
- package/dist/{version-SZI6I3A2.js.map → version-2UBJ7N2I.js.map} +1 -1
- package/dist/{vg-mcp-bridge-IIBEAOEB.js → vg-mcp-bridge-H6GXYNKV.js} +14 -14
- package/dist/{vg-mcp-bridge-IIBEAOEB.js.map → vg-mcp-bridge-H6GXYNKV.js.map} +1 -1
- package/dist/{vgd-KGXIKGOS.js → vgd-SBZO4ONQ.js} +13 -13
- package/dist/vgd-SBZO4ONQ.js.map +1 -0
- package/package.json +1 -1
- package/dist/agent-3CKNTCRX.js +0 -30
- package/dist/attach-OCQ3I76L.js +0 -10
- package/dist/baseline-QDC6OQ2X.js +0 -9
- package/dist/chunk-7N4Y3E47.js.map +0 -1
- package/dist/chunk-BPF3YX2U.js.map +0 -1
- package/dist/chunk-DHU3OOEI.js +0 -6
- package/dist/chunk-F46Q3AEI.js.map +0 -1
- package/dist/chunk-FJG5AXKZ.js.map +0 -1
- package/dist/chunk-JGBOYZAF.js.map +0 -1
- package/dist/chunk-KKQX3F5F.js.map +0 -1
- package/dist/chunk-NZELMYT4.js.map +0 -1
- package/dist/chunk-OQT3JBWW.js.map +0 -1
- package/dist/chunk-YIEP5GK5.js.map +0 -1
- package/dist/client-UQIELEEZ.js +0 -8
- package/dist/daemon-UNL65PTP.js +0 -30
- package/dist/ensure-map-4D2HJ6MA.js +0 -40
- package/dist/git-ref-RSRAN4OH.js +0 -4
- package/dist/graph-backend-36EIAAPQ.js +0 -16
- package/dist/load-6CYLMUT5.js +0 -11
- package/dist/local-runtime-6SNWKSA7.js +0 -6
- package/dist/model-manager-SXHZ5VD4.js +0 -10
- package/dist/publish-7YHRTBRA.js +0 -9
- package/dist/resolve-gguf-SDIYGGI6.js +0 -9
- package/dist/runtime-session-VNSXQAEO.js +0 -26
- package/dist/session-3A6LKJHD.js +0 -17
- package/dist/version-SZI6I3A2.js +0 -4
- package/dist/vgd-KGXIKGOS.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { vibgrateCacheDir } from './chunk-
|
|
1
|
+
import { vibgrateCacheDir } from './chunk-XP6UUCZZ.js';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import * as https from 'https';
|
|
@@ -237,5 +237,5 @@ function listCachedWeights(env = process.env) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
export { catalogIntegrityReport, ensureWeightCached, freeBytesOnVolume, isWeightCached, listCachedWeights, lookupCatalog, weightPathForRef, weightStoreDir };
|
|
240
|
-
//# sourceMappingURL=chunk-
|
|
241
|
-
//# sourceMappingURL=chunk-
|
|
240
|
+
//# sourceMappingURL=chunk-57ZRSCHY.js.map
|
|
241
|
+
//# sourceMappingURL=chunk-57ZRSCHY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/weight-store.ts"],"names":[],"mappings":";;;;;;;AAoBO,IAAM,8BAAA,uBAAqC,GAAA,CAAI;AAAA,EACpD,gBAAA;AAAA,EACA,OAAA;AAAA,EACA,wBAAA;AAAA,EACA,6BAAA;AAAA,EACA,kBAAA;AAAA,EACA,yBAAA;AAAA,EACA,+BAAA;AAAA,EACA;AACF,CAAC,CAAA;AAGM,SAAS,oBAAoB,QAAA,EAA2B;AAC7D,EAAA,MAAM,CAAA,GAAI,SAAS,WAAA,EAAY;AAC/B,EAAA,IAAI,8BAAA,CAA+B,GAAA,CAAI,CAAC,CAAA,EAAG,OAAO,IAAA;AAClD,EAAA,IAAI,CAAA,CAAE,QAAA,CAAS,iBAAiB,CAAA,EAAG,OAAO,IAAA;AAC1C,EAAA,IAAI,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,EAAG,OAAO,IAAA;AACjC,EAAA,IAAI,CAAA,CAAE,QAAA,CAAS,YAAY,CAAA,EAAG,OAAO,IAAA;AACrC,EAAA,OAAO,KAAA;AACT;AA2BO,IAAM,0BAAA,GAAmD;AAAA,EAC9D;AAAA,IACE,GAAA,EAAK,8BAAA;AAAA,IACL,GAAA,EAAK,+GAAA;AAAA,IACL,OAAA,EAAS,YAAA;AAAA,IACT,WAAA,EAAa,WAAA;AAAA;AAAA,IAEb,MAAA,EAAQ,kEAAA;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,8BAAA;AAAA,IACL,GAAA,EAAK,+GAAA;AAAA,IACL,OAAA,EAAS,YAAA;AAAA,IACT,WAAA,EAAa,WAAA;AAAA,IACb,MAAA,EAAQ,kEAAA;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,+BAAA;AAAA,IACL,GAAA,EAAK,iHAAA;AAAA,IACL,OAAA,EAAS,YAAA;AAAA,IACT,WAAA,EAAa,WAAA;AAAA;AAAA,IAEb,MAAA,EAAQ,kEAAA;AAAA,IACR,KAAA,EAAO;AAAA;AAEX,CAAA;AAUO,SAAS,sBAAA,CACd,UAAgC,0BAAA,EACR;AACxB,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,MAAA,CAAO,CAAC,MAAM,CAAC,CAAA,CAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,GAAG,CAAA;AAC1E,EAAA,OAAO;AAAA,IACL,OAAO,OAAA,CAAQ,MAAA;AAAA,IACf,MAAA,EAAQ,OAAA,CAAQ,MAAA,GAAS,QAAA,CAAS,MAAA;AAAA,IAClC,QAAA;AAAA,IACA,QAAA,EAAU,SAAS,MAAA,KAAW;AAAA,GAChC;AACF;AAYO,SAAS,cAAA,CAAe,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAa;AAC3E,EAAA,OAAY,IAAA,CAAA,IAAA,CAAK,gBAAA,CAAiB,GAAG,CAAA,EAAG,SAAS,CAAA;AACnD;AAEO,SAAS,gBAAA,CAAiB,GAAA,EAAa,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAa;AAC1F,EAAA,MAAM,OAAY,IAAA,CAAA,QAAA,CAAS,GAAG,CAAA,CAAE,OAAA,CAAQ,qBAAqB,GAAG,CAAA;AAChE,EAAA,OAAY,IAAA,CAAA,IAAA,CAAK,cAAA,CAAe,GAAG,CAAA,EAAG,IAAI,CAAA;AAC5C;AAEO,SAAS,cAAA,CAAe,GAAA,EAAa,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAc;AACzF,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,gBAAA,CAAiB,GAAA,EAAK,GAAG,CAAA;AACnC,IAAA,OAAU,cAAW,CAAC,CAAA,IAAQ,EAAA,CAAA,QAAA,CAAS,CAAC,EAAE,IAAA,GAAO,CAAA;AAAA,EACnD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEO,SAAS,aAAA,CAAc,GAAA,EAAa,OAAA,GAAgC,0BAAA,EAAuD;AAChI,EAAA,MAAM,IAAA,GAAO,IAAI,WAAA,EAAY;AAC7B,EAAA,OACE,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,GAAA,CAAI,WAAA,EAAY,KAAM,IAAI,CAAA,IAChD,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,IAAA,CAAK,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,WAAA,EAAa,CAAA,IAAK,CAAA,CAAE,GAAA,CAAI,WAAA,EAAY,CAAE,QAAA,CAAS,IAAI,CAAC,CAAA,IAC5F,IAAA;AAEJ;AAEO,SAAS,mBAAmB,GAAA,EAAsB;AACvD,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,GAAG,CAAA;AACrB,IAAA,IAAI,CAAA,CAAE,QAAA,KAAa,QAAA,EAAU,OAAO,KAAA;AACpC,IAAA,OAAO,mBAAA,CAAoB,EAAE,QAAQ,CAAA;AAAA,EACvC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAoBA,IAAM,QAAA,uBAAe,GAAA,EAA2C;AAMhE,eAAsB,kBAAA,CAAmB,GAAA,EAAa,OAAA,GAAiC,EAAC,EAAkC;AACxH,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,OAAA,CAAQ,GAAA;AACnC,EAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,GAAA,EAAK,GAAG,CAAA;AACtC,EAAA,IAAI,cAAA,CAAe,GAAA,EAAK,GAAG,CAAA,EAAG;AAC5B,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,WAAW,IAAA,EAAK;AAAA,EACjD;AAEA,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA;AAClC,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,MAAM,QAAQ,YAA2C;AAEvD,IAAA,IAAI,cAAA,CAAe,GAAA,EAAK,GAAG,CAAA,EAAG;AAC5B,MAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,WAAW,IAAA,EAAK;AAAA,IACjD;AAEA,IAAA,MAAM,KAAA,GAAQ,cAAc,GAAG,CAAA;AAC/B,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,KAAA,EAAO,GAAA;AAClC,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO,EAAE,IAAI,KAAA,EAAO,KAAA,EAAO,gDAAgD,GAAG,CAAA,mDAAA,EAAiD,IAAI,CAAA,CAAA,EAAG;AAAA,IACxI;AACA,IAAA,IAAI,CAAC,kBAAA,CAAmB,GAAG,CAAA,EAAG;AAC5B,MAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,CAAA,iCAAA,EAAoC,GAAG,CAAA,CAAA,EAAG;AAAA,IACvE;AAEA,IAAA,IAAI;AACF,MAAG,aAAe,IAAA,CAAA,OAAA,CAAQ,IAAI,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACpD,MAAA,MAAM,GAAA,GAAM,GAAG,IAAI,CAAA,SAAA,EAAY,QAAQ,GAAG,CAAA,CAAA,EAAI,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AACxD,MAAA,MAAM,OAAA,GAAU,QAAQ,WAAA,IAAe,kBAAA;AACvC,MAAA,OAAA,CAAQ,UAAA,GAAa,CAAA,YAAA,EAAe,GAAG,CAAA,CAAE,CAAA;AACzC,MAAA,MAAM,OAAA,CAAQ,GAAA,EAAK,GAAA,EAAK,OAAA,CAAQ,UAAU,CAAA;AAC1C,MAAA,MAAM,GAAA,GAAA,CAAO,OAAA,CAAQ,cAAA,IAAkB,KAAA,EAAO,SAAS,IAAA,EAAK;AAC5D,MAAA,IAAI,GAAA,EAAK;AACP,QAAA,MAAM,MAAA,GAAS,WAAW,GAAG,CAAA;AAC7B,QAAA,IAAI,MAAA,KAAW,GAAA,CAAI,WAAA,EAAY,EAAG;AAChC,UAAA,IAAI;AACF,YAAG,cAAW,GAAG,CAAA;AAAA,UACnB,CAAA,CAAA,MAAQ;AAAA,UAER;AACA,UAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,CAAA,oBAAA,EAAuB,GAAG,CAAA,WAAA,EAAc,GAAG,CAAA,MAAA,EAAS,MAAM,CAAA,CAAA,CAAA,EAAI;AAAA,QAC3F;AAAA,MACF,CAAA,MAAA,IAAW,KAAA,IAAS,CAAC,KAAA,CAAM,MAAA,EAAQ;AACjC,QAAA,OAAA,CAAQ,UAAA,GAAa,CAAA,SAAA,EAAY,GAAG,CAAA,2DAAA,CAAwD,CAAA;AAAA,MAC9F;AAEA,MAAA,IAAI,cAAA,CAAe,GAAA,EAAK,GAAG,CAAA,EAAG;AAC5B,QAAA,IAAI;AACF,UAAG,cAAW,GAAG,CAAA;AAAA,QACnB,CAAA,CAAA,MAAQ;AAAA,QAER;AACA,QAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,WAAW,IAAA,EAAK;AAAA,MACjD;AACA,MAAG,EAAA,CAAA,UAAA,CAAW,KAAK,IAAI,CAAA;AACvB,MAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,WAAW,KAAA,EAAM;AAAA,IAClD,SAAS,CAAA,EAAG;AACV,MAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,CAAA,YAAa,QAAQ,CAAA,CAAE,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,EAAE;AAAA,IACxE,CAAA,SAAE;AACA,MAAA,QAAA,CAAS,OAAO,IAAI,CAAA;AAAA,IACtB;AAAA,EACF,CAAA,GAAG;AAEH,EAAA,QAAA,CAAS,GAAA,CAAI,MAAM,IAAI,CAAA;AACvB,EAAA,OAAO,IAAA;AACT;AAOA,SAAS,WAAW,IAAA,EAAsB;AACxC,EAAA,MAAM,CAAA,GAAI,WAAW,QAAQ,CAAA;AAC7B,EAAA,MAAM,EAAA,GAAQ,EAAA,CAAA,QAAA,CAAS,IAAA,EAAM,GAAG,CAAA;AAChC,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,KAAA,CAAM,CAAA,GAAI,OAAO,IAAI,CAAA;AACxC,IAAA,IAAI,CAAA;AACJ,IAAA,OAAA,CAAQ,CAAA,GAAO,YAAS,EAAA,EAAI,GAAA,EAAK,GAAG,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA,IAAK,CAAA,EAAG;AAC1D,MAAA,CAAA,CAAE,MAAA,CAAO,MAAM,GAAA,CAAI,MAAA,GAAS,MAAM,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAC,CAAC,CAAA;AAAA,IACtD;AAAA,EACF,CAAA,SAAE;AACA,IAAG,aAAU,EAAE,CAAA;AAAA,EACjB;AACA,EAAA,OAAO,CAAA,CAAE,OAAO,KAAK,CAAA;AACvB;AAGO,SAAS,kBAAA,CAAmB,GAAA,EAAa,IAAA,EAAc,UAAA,EAAoD;AAChH,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,GAAA,GAAM,CAAC,CAAA,EAAW,SAAA,KAA4B;AAClD,MAAA,IAAI,YAAY,CAAA,EAAG;AACjB,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,oBAAoB,CAAC,CAAA;AACtC,QAAA;AAAA,MACF;AACA,MAAA,MAAM,GAAA,GAAM,CAAA,CAAE,UAAA,CAAW,QAAQ,IAAI,KAAA,GAAQ,IAAA;AAC7C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,CAAI,CAAA,EAAG,CAAC,GAAA,KAAQ;AAC9B,QAAA,MAAM,IAAA,GAAO,IAAI,UAAA,IAAc,CAAA;AAC/B,QAAA,IAAI,QAAQ,GAAA,IAAO,IAAA,GAAO,GAAA,IAAO,GAAA,CAAI,QAAQ,QAAA,EAAU;AACrD,UAAA,MAAM,OAAO,IAAI,GAAA,CAAI,IAAI,OAAA,CAAQ,QAAA,EAAU,CAAC,CAAA,CAAE,IAAA;AAE9C,UAAA,IAAI;AACF,YAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,IAAI,CAAA;AAC5B,YAAA,IAAI,QAAQ,QAAA,KAAa,QAAA,IAAY,CAAC,mBAAA,CAAoB,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC3E,cAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,+BAAA,EAAkC,QAAQ,QAAA,IAAY,IAAI,EAAE,CAAC,CAAA;AAC9E,cAAA;AAAA,YACF;AAAA,UACF,CAAA,CAAA,MAAQ;AACN,YAAA,MAAA,CAAO,IAAI,KAAA,CAAM,sBAAsB,CAAC,CAAA;AACxC,YAAA;AAAA,UACF;AACA,UAAA,GAAA,CAAI,MAAA,EAAO;AACX,UAAA,GAAA,CAAI,IAAA,EAAM,YAAY,CAAC,CAAA;AACvB,UAAA;AAAA,QACF;AACA,QAAA,IAAI,SAAS,GAAA,EAAK;AAChB,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQ,IAAI,mBAAmB,CAAC,CAAA;AACjD,UAAA,GAAA,CAAI,MAAA,EAAO;AACX,UAAA;AAAA,QACF;AACA,QAAA,MAAM,QAAQ,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,gBAAgB,KAAK,CAAC,CAAA;AACvD,QAAA,IAAI,GAAA,GAAM,CAAA;AACV,QAAA,MAAM,GAAA,GAAS,qBAAkB,IAAI,CAAA;AACrC,QAAA,GAAA,CAAI,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB;AAChC,UAAA,GAAA,IAAO,KAAA,CAAM,MAAA;AACb,UAAA,IAAI,QAAQ,CAAA,IAAK,GAAA,IAAO,IAAI,IAAA,GAAO,IAAA,CAAA,GAAQ,MAAM,MAAA,EAAQ;AACvD,YAAA,UAAA,GAAa,GAAG,IAAA,CAAK,KAAA,CAAO,MAAM,GAAA,GAAO,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,UACpD;AAAA,QACF,CAAC,CAAA;AACD,QAAA,GAAA,CAAI,KAAK,GAAG,CAAA;AACZ,QAAA,GAAA,CAAI,EAAA,CAAG,QAAA,EAAU,MAAM,OAAA,EAAS,CAAA;AAChC,QAAA,GAAA,CAAI,EAAA,CAAG,SAAS,MAAM,CAAA;AACtB,QAAA,GAAA,CAAI,EAAA,CAAG,SAAS,MAAM,CAAA;AAAA,MACxB,CAAC,CAAA;AACD,MAAA,GAAA,CAAI,EAAA,CAAG,SAAS,MAAM,CAAA;AAAA,IACxB,CAAA;AACA,IAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,EACZ,CAAC,CAAA;AACH;AAMO,SAAS,kBAAkB,UAAA,EAAwC;AACxE,EAAA,IAAI,GAAA,GAAM,UAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK;AAC3B,IAAA,IAAI;AACF,MAAA,MAAM,EAAA,GAIJ,gBAAa,GAAG,CAAA;AAClB,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,MAAM,OAAO,MAAA,CAAO,EAAA,CAAG,MAAM,CAAA,GAAI,MAAA,CAAO,GAAG,KAAK,CAAA;AAChD,QAAA,IAAI,OAAO,QAAA,CAAS,IAAI,CAAA,IAAK,IAAA,IAAQ,GAAG,OAAO,IAAA;AAAA,MACjD;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,MAAM,MAAA,GAAc,aAAQ,GAAG,CAAA;AAC/B,IAAA,IAAI,WAAW,GAAA,EAAK;AACpB,IAAA,GAAA,GAAM,MAAA;AAAA,EACR;AACA,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,iBAAA,CAAkB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAA4C;AAC7G,EAAA,MAAM,GAAA,GAAM,eAAe,GAAG,CAAA;AAC9B,EAAA,IAAI;AACF,IAAA,OACG,EAAA,CAAA,WAAA,CAAY,GAAG,CAAA,CACf,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC,CAAA,CACjC,IAAA,GACA,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,IAAA,EAAM,CAAA,EAAG,MAAW,IAAA,CAAA,IAAA,CAAK,GAAA,EAAK,CAAC,CAAA,EAAE,CAAE,CAAA;AAAA,EACtD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF","file":"chunk-OV4KGXPD.js","sourcesContent":["/**\n * First-party weight store — content-addressed GGUF cache under the Vibgrate\n * cache root (never in-repo). Complements Ollama as a download channel so\n * embedded llama.cpp packs need not depend on a third-party app for weights.\n *\n * Only HTTPS URLs from an allowlist (or pack-declared URLs that pass the same\n * host allowlist) are fetched. No auto-download except via explicit\n * `vg models install` / executor consent.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as https from 'node:https';\nimport * as http from 'node:http';\nimport { createHash } from 'node:crypto';\nimport { vibgrateCacheDir } from './paths.js';\n\nexport const WEIGHT_STORE_SCHEMA = 'weight-store/0' as const;\n\n/** Hosts we will download weights from (no arbitrary open proxy). */\nexport const WEIGHT_DOWNLOAD_HOST_ALLOWLIST = new Set([\n 'huggingface.co',\n 'hf.co',\n 'cdn-lfs.huggingface.co',\n 'cdn-lfs-us-1.huggingface.co',\n 'us.aws.cdn.hf.co',\n 'cas-server.xethub.hf.co',\n 'objects.githubusercontent.com',\n 'github.com',\n]);\n\n/** Host suffixes allowed for HF LFS / Xet CDN redirects. */\nexport function isAllowedWeightHost(hostname: string): boolean {\n const h = hostname.toLowerCase();\n if (WEIGHT_DOWNLOAD_HOST_ALLOWLIST.has(h)) return true;\n if (h.endsWith('.huggingface.co')) return true;\n if (h.endsWith('.hf.co')) return true;\n if (h.endsWith('.cdn.hf.co')) return true;\n return false;\n}\n\nexport interface WeightCatalogEntry {\n /** Stable ref (matches pack weightsRef or basename). */\n ref: string;\n /** HTTPS URL to the GGUF (or redirected LFS object). */\n url: string;\n /** Optional expected size for progress UX. */\n bytes?: number;\n license?: string;\n /**\n * When set, download **must** match this sha256 or install fails (B6).\n * Leave unset only while pinning is pending — status reports unpinned.\n */\n sha256?: string;\n /** Pack channel this entry was verified against. */\n packChannel?: string;\n}\n\n/**\n * Curated first-party catalog — small, versioned with packs. Not a general hub.\n * URLs point at public Hugging Face GGUF artifacts for Qwen2.5-Coder.\n *\n * Integrity: sha256 pins are required for production installs once verified.\n * Entries without sha256 are marked unpinned in {@link catalogIntegrityReport}\n * and still download, but CI should fail if any pack primary lacks a pin.\n */\nexport const FIRST_PARTY_WEIGHT_CATALOG: WeightCatalogEntry[] = [\n {\n ref: 'qwen2.5-coder-3b-q4_k_m.gguf',\n url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct-GGUF/resolve/main/qwen2.5-coder-3b-instruct-q4_k_m.gguf',\n license: 'Apache-2.0',\n packChannel: '2026.07.3',\n // Git LFS oid = content sha256 (from HF API tree, 2026-07-27).\n sha256: '724fb256bec1ff062b2f65e4569e871ad2e95ab2a3989723d1769c54294730b7',\n bytes: 2_104_932_800,\n },\n {\n ref: 'qwen2.5-coder-7b-q4_k_m.gguf',\n url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_k_m.gguf',\n license: 'Apache-2.0',\n packChannel: '2026.07.3',\n sha256: '509287f78cb4d4cf6b3843734733b914b2c158e43e22a7f4bf5e963800894d3c',\n bytes: 4_683_073_536,\n },\n {\n ref: 'qwen2.5-coder-14b-q4_k_m.gguf',\n url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-14B-Instruct-GGUF/resolve/main/qwen2.5-coder-14b-instruct-q4_k_m.gguf',\n license: 'Apache-2.0',\n packChannel: '2026.07.4',\n // Git LFS oid = content sha256 (from HF API tree, 2026-07-27).\n sha256: 'c1e659736d89ac1065fb495330fb824d94001974a4bfa78e7270e43476a8d940',\n bytes: 8_988_110_272,\n },\n];\n\nexport interface CatalogIntegrityReport {\n total: number;\n pinned: number;\n unpinned: string[];\n /** True when every entry has sha256. */\n complete: boolean;\n}\n\nexport function catalogIntegrityReport(\n catalog: WeightCatalogEntry[] = FIRST_PARTY_WEIGHT_CATALOG,\n): CatalogIntegrityReport {\n const unpinned = catalog.filter((e) => !e.sha256?.trim()).map((e) => e.ref);\n return {\n total: catalog.length,\n pinned: catalog.length - unpinned.length,\n unpinned,\n complete: unpinned.length === 0,\n };\n}\n\n/** Require sha256 on every catalog entry (CI gate for pack releases). */\nexport function assertCatalogPinned(catalog: WeightCatalogEntry[] = FIRST_PARTY_WEIGHT_CATALOG): void {\n const report = catalogIntegrityReport(catalog);\n if (!report.complete) {\n throw new Error(\n `weight catalog incomplete: ${report.unpinned.length}/${report.total} entries missing sha256: ${report.unpinned.join(', ')}`,\n );\n }\n}\n\nexport function weightStoreDir(env: NodeJS.ProcessEnv = process.env): string {\n return path.join(vibgrateCacheDir(env), 'weights');\n}\n\nexport function weightPathForRef(ref: string, env: NodeJS.ProcessEnv = process.env): string {\n const base = path.basename(ref).replace(/[^a-zA-Z0-9._+-]/g, '_');\n return path.join(weightStoreDir(env), base);\n}\n\nexport function isWeightCached(ref: string, env: NodeJS.ProcessEnv = process.env): boolean {\n try {\n const p = weightPathForRef(ref, env);\n return fs.existsSync(p) && fs.statSync(p).size > 0;\n } catch {\n return false;\n }\n}\n\nexport function lookupCatalog(ref: string, catalog: WeightCatalogEntry[] = FIRST_PARTY_WEIGHT_CATALOG): WeightCatalogEntry | null {\n const want = ref.toLowerCase();\n return (\n catalog.find((e) => e.ref.toLowerCase() === want) ??\n catalog.find((e) => want.includes(e.ref.toLowerCase()) || e.ref.toLowerCase().includes(want)) ??\n null\n );\n}\n\nexport function isAllowedWeightUrl(url: string): boolean {\n try {\n const u = new URL(url);\n if (u.protocol !== 'https:') return false;\n return isAllowedWeightHost(u.hostname);\n } catch {\n return false;\n }\n}\n\nexport interface DownloadWeightOptions {\n env?: NodeJS.ProcessEnv;\n /** Override catalog URL. */\n url?: string;\n expectedSha256?: string;\n onProgress?: (line: string) => void;\n /** Injectable fetch for tests. */\n fetchToFile?: (url: string, dest: string, onProgress?: (line: string) => void) => Promise<void>;\n}\n\nexport interface DownloadWeightResult {\n ok: boolean;\n path?: string;\n error?: string;\n fromCache?: boolean;\n}\n\n/** In-flight downloads: one network fetch per dest path (concurrent install-safe). */\nconst inflight = new Map<string, Promise<DownloadWeightResult>>();\n\n/**\n * Ensure weights for `ref` exist in the first-party store. Downloads when missing.\n * Concurrent callers for the same ref share a single download promise.\n */\nexport async function ensureWeightCached(ref: string, options: DownloadWeightOptions = {}): Promise<DownloadWeightResult> {\n const env = options.env ?? process.env;\n const dest = weightPathForRef(ref, env);\n if (isWeightCached(ref, env)) {\n return { ok: true, path: dest, fromCache: true };\n }\n\n const existing = inflight.get(dest);\n if (existing) return existing;\n\n const work = (async (): Promise<DownloadWeightResult> => {\n // Re-check after joining the queue.\n if (isWeightCached(ref, env)) {\n return { ok: true, path: dest, fromCache: true };\n }\n\n const entry = lookupCatalog(ref);\n const url = options.url ?? entry?.url;\n if (!url) {\n return { ok: false, error: `no first-party download URL for weights ref \"${ref}\" — set a pack downloadUrl or place a gguf at ${dest}` };\n }\n if (!isAllowedWeightUrl(url)) {\n return { ok: false, error: `weight URL host not allowlisted: ${url}` };\n }\n\n try {\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n const tmp = `${dest}.partial.${process.pid}.${Date.now()}`;\n const fetchFn = options.fetchToFile ?? defaultFetchToFile;\n options.onProgress?.(`downloading ${ref}`);\n await fetchFn(url, tmp, options.onProgress);\n const sha = (options.expectedSha256 ?? entry?.sha256)?.trim();\n if (sha) {\n const actual = sha256File(tmp);\n if (actual !== sha.toLowerCase()) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n /* ignore */\n }\n return { ok: false, error: `sha256 mismatch for ${ref} (expected ${sha}, got ${actual})` };\n }\n } else if (entry && !entry.sha256) {\n options.onProgress?.(`warning: ${ref} has no sha256 pin in catalog — integrity not verified`);\n }\n // Atomic publish: if another worker won the race, drop our partial.\n if (isWeightCached(ref, env)) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n /* ignore */\n }\n return { ok: true, path: dest, fromCache: true };\n }\n fs.renameSync(tmp, dest);\n return { ok: true, path: dest, fromCache: false };\n } catch (e) {\n return { ok: false, error: e instanceof Error ? e.message : String(e) };\n } finally {\n inflight.delete(dest);\n }\n })();\n\n inflight.set(dest, work);\n return work;\n}\n\n/**\n * Stream a file through sha256. Never `readFileSync` whole weights — Node rejects\n * buffers larger than ~2 GiB (`File size (…) is greater than 2 GiB`), which broke\n * Flow/Forge installs even when the download completed.\n */\nfunction sha256File(file: string): string {\n const h = createHash('sha256');\n const fd = fs.openSync(file, 'r');\n try {\n const buf = Buffer.alloc(8 * 1024 * 1024);\n let n: number;\n while ((n = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {\n h.update(n === buf.length ? buf : buf.subarray(0, n));\n }\n } finally {\n fs.closeSync(fd);\n }\n return h.digest('hex');\n}\n\n/** Minimal HTTPS GET with redirect follow (tests inject fetchToFile). */\nexport function defaultFetchToFile(url: string, dest: string, onProgress?: (line: string) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const get = (u: string, redirects: number): void => {\n if (redirects > 8) {\n reject(new Error('too many redirects'));\n return;\n }\n const lib = u.startsWith('https:') ? https : http;\n const req = lib.get(u, (res) => {\n const code = res.statusCode ?? 0;\n if (code >= 300 && code < 400 && res.headers.location) {\n const next = new URL(res.headers.location, u).href;\n // Redirects must stay on HTTPS + allowlisted hosts (incl. HF LFS/Xet CDNs).\n try {\n const nextUrl = new URL(next);\n if (nextUrl.protocol !== 'https:' || !isAllowedWeightHost(nextUrl.hostname)) {\n reject(new Error(`redirect host not allowlisted: ${nextUrl.hostname || next}`));\n return;\n }\n } catch {\n reject(new Error('invalid redirect URL'));\n return;\n }\n res.resume();\n get(next, redirects + 1);\n return;\n }\n if (code !== 200) {\n reject(new Error(`HTTP ${code} fetching weights`));\n res.resume();\n return;\n }\n const total = Number(res.headers['content-length'] || 0);\n let got = 0;\n const out = fs.createWriteStream(dest);\n res.on('data', (chunk: Buffer) => {\n got += chunk.length;\n if (total > 0 && got % (8 * 1024 * 1024) < chunk.length) {\n onProgress?.(`${Math.floor((100 * got) / total)}%`);\n }\n });\n res.pipe(out);\n out.on('finish', () => resolve());\n out.on('error', reject);\n res.on('error', reject);\n });\n req.on('error', reject);\n };\n get(url, 0);\n });\n}\n\n/**\n * Free bytes on the volume that holds `targetPath` (weight store / cache).\n * Best-effort via `statfsSync` (Node 18.15+); walks parents if path is missing.\n */\nexport function freeBytesOnVolume(targetPath: string): number | undefined {\n let dir = targetPath;\n for (let i = 0; i < 10; i++) {\n try {\n const st = (\n fs as typeof fs & {\n statfsSync?: (p: string) => { bavail: number | bigint; bsize: number | bigint };\n }\n ).statfsSync?.(dir);\n if (st) {\n const free = Number(st.bavail) * Number(st.bsize);\n if (Number.isFinite(free) && free >= 0) return free;\n }\n } catch {\n /* try parent */\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\n/** List GGUF files already in the first-party store (for discovery). */\nexport function listCachedWeights(env: NodeJS.ProcessEnv = process.env): Array<{ name: string; path: string }> {\n const dir = weightStoreDir(env);\n try {\n return fs\n .readdirSync(dir)\n .filter((n) => n.endsWith('.gguf'))\n .sort()\n .map((n) => ({ name: n, path: path.join(dir, n) }));\n } catch {\n return [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/weight-store.ts"],"names":[],"mappings":"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","file":"chunk-57ZRSCHY.js","sourcesContent":["/**\n * First-party weight store — content-addressed GGUF cache under the Vibgrate\n * cache root (never in-repo). Complements Ollama as a download channel so\n * embedded llama.cpp packs need not depend on a third-party app for weights.\n *\n * Only HTTPS URLs from an allowlist (or pack-declared URLs that pass the same\n * host allowlist) are fetched. No auto-download except via explicit\n * `vg models install` / executor consent.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as https from 'node:https';\nimport * as http from 'node:http';\nimport { createHash } from 'node:crypto';\nimport { vibgrateCacheDir } from './paths.js';\n\nexport const WEIGHT_STORE_SCHEMA = 'weight-store/0' as const;\n\n/** Hosts we will download weights from (no arbitrary open proxy). */\nexport const WEIGHT_DOWNLOAD_HOST_ALLOWLIST = new Set([\n 'huggingface.co',\n 'hf.co',\n 'cdn-lfs.huggingface.co',\n 'cdn-lfs-us-1.huggingface.co',\n 'us.aws.cdn.hf.co',\n 'cas-server.xethub.hf.co',\n 'objects.githubusercontent.com',\n 'github.com',\n]);\n\n/** Host suffixes allowed for HF LFS / Xet CDN redirects. */\nexport function isAllowedWeightHost(hostname: string): boolean {\n const h = hostname.toLowerCase();\n if (WEIGHT_DOWNLOAD_HOST_ALLOWLIST.has(h)) return true;\n if (h.endsWith('.huggingface.co')) return true;\n if (h.endsWith('.hf.co')) return true;\n if (h.endsWith('.cdn.hf.co')) return true;\n return false;\n}\n\nexport interface WeightCatalogEntry {\n /** Stable ref (matches pack weightsRef or basename). */\n ref: string;\n /** HTTPS URL to the GGUF (or redirected LFS object). */\n url: string;\n /** Optional expected size for progress UX. */\n bytes?: number;\n license?: string;\n /**\n * When set, download **must** match this sha256 or install fails (B6).\n * Leave unset only while pinning is pending — status reports unpinned.\n */\n sha256?: string;\n /** Pack channel this entry was verified against. */\n packChannel?: string;\n}\n\n/**\n * Curated first-party catalog — small, versioned with packs. Not a general hub.\n * URLs point at public Hugging Face GGUF artifacts for Qwen2.5-Coder.\n *\n * Integrity: sha256 pins are required for production installs once verified.\n * Entries without sha256 are marked unpinned in {@link catalogIntegrityReport}\n * and still download, but CI should fail if any pack primary lacks a pin.\n */\nexport const FIRST_PARTY_WEIGHT_CATALOG: WeightCatalogEntry[] = [\n {\n ref: 'qwen2.5-coder-3b-q4_k_m.gguf',\n url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct-GGUF/resolve/main/qwen2.5-coder-3b-instruct-q4_k_m.gguf',\n license: 'Apache-2.0',\n packChannel: '2026.07.3',\n // Git LFS oid = content sha256 (from HF API tree, 2026-07-27).\n sha256: '724fb256bec1ff062b2f65e4569e871ad2e95ab2a3989723d1769c54294730b7',\n bytes: 2_104_932_800,\n },\n {\n ref: 'qwen2.5-coder-7b-q4_k_m.gguf',\n url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_k_m.gguf',\n license: 'Apache-2.0',\n packChannel: '2026.07.3',\n sha256: '509287f78cb4d4cf6b3843734733b914b2c158e43e22a7f4bf5e963800894d3c',\n bytes: 4_683_073_536,\n },\n {\n ref: 'qwen2.5-coder-14b-q4_k_m.gguf',\n url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-14B-Instruct-GGUF/resolve/main/qwen2.5-coder-14b-instruct-q4_k_m.gguf',\n license: 'Apache-2.0',\n packChannel: '2026.07.4',\n // Git LFS oid = content sha256 (from HF API tree, 2026-07-27).\n sha256: 'c1e659736d89ac1065fb495330fb824d94001974a4bfa78e7270e43476a8d940',\n bytes: 8_988_110_272,\n },\n];\n\nexport interface CatalogIntegrityReport {\n total: number;\n pinned: number;\n unpinned: string[];\n /** True when every entry has sha256. */\n complete: boolean;\n}\n\nexport function catalogIntegrityReport(\n catalog: WeightCatalogEntry[] = FIRST_PARTY_WEIGHT_CATALOG,\n): CatalogIntegrityReport {\n const unpinned = catalog.filter((e) => !e.sha256?.trim()).map((e) => e.ref);\n return {\n total: catalog.length,\n pinned: catalog.length - unpinned.length,\n unpinned,\n complete: unpinned.length === 0,\n };\n}\n\n/** Require sha256 on every catalog entry (CI gate for pack releases). */\nexport function assertCatalogPinned(catalog: WeightCatalogEntry[] = FIRST_PARTY_WEIGHT_CATALOG): void {\n const report = catalogIntegrityReport(catalog);\n if (!report.complete) {\n throw new Error(\n `weight catalog incomplete: ${report.unpinned.length}/${report.total} entries missing sha256: ${report.unpinned.join(', ')}`,\n );\n }\n}\n\nexport function weightStoreDir(env: NodeJS.ProcessEnv = process.env): string {\n return path.join(vibgrateCacheDir(env), 'weights');\n}\n\nexport function weightPathForRef(ref: string, env: NodeJS.ProcessEnv = process.env): string {\n const base = path.basename(ref).replace(/[^a-zA-Z0-9._+-]/g, '_');\n return path.join(weightStoreDir(env), base);\n}\n\nexport function isWeightCached(ref: string, env: NodeJS.ProcessEnv = process.env): boolean {\n try {\n const p = weightPathForRef(ref, env);\n return fs.existsSync(p) && fs.statSync(p).size > 0;\n } catch {\n return false;\n }\n}\n\nexport function lookupCatalog(ref: string, catalog: WeightCatalogEntry[] = FIRST_PARTY_WEIGHT_CATALOG): WeightCatalogEntry | null {\n const want = ref.toLowerCase();\n return (\n catalog.find((e) => e.ref.toLowerCase() === want) ??\n catalog.find((e) => want.includes(e.ref.toLowerCase()) || e.ref.toLowerCase().includes(want)) ??\n null\n );\n}\n\nexport function isAllowedWeightUrl(url: string): boolean {\n try {\n const u = new URL(url);\n if (u.protocol !== 'https:') return false;\n return isAllowedWeightHost(u.hostname);\n } catch {\n return false;\n }\n}\n\nexport interface DownloadWeightOptions {\n env?: NodeJS.ProcessEnv;\n /** Override catalog URL. */\n url?: string;\n expectedSha256?: string;\n onProgress?: (line: string) => void;\n /** Injectable fetch for tests. */\n fetchToFile?: (url: string, dest: string, onProgress?: (line: string) => void) => Promise<void>;\n}\n\nexport interface DownloadWeightResult {\n ok: boolean;\n path?: string;\n error?: string;\n fromCache?: boolean;\n}\n\n/** In-flight downloads: one network fetch per dest path (concurrent install-safe). */\nconst inflight = new Map<string, Promise<DownloadWeightResult>>();\n\n/**\n * Ensure weights for `ref` exist in the first-party store. Downloads when missing.\n * Concurrent callers for the same ref share a single download promise.\n */\nexport async function ensureWeightCached(ref: string, options: DownloadWeightOptions = {}): Promise<DownloadWeightResult> {\n const env = options.env ?? process.env;\n const dest = weightPathForRef(ref, env);\n if (isWeightCached(ref, env)) {\n return { ok: true, path: dest, fromCache: true };\n }\n\n const existing = inflight.get(dest);\n if (existing) return existing;\n\n const work = (async (): Promise<DownloadWeightResult> => {\n // Re-check after joining the queue.\n if (isWeightCached(ref, env)) {\n return { ok: true, path: dest, fromCache: true };\n }\n\n const entry = lookupCatalog(ref);\n const url = options.url ?? entry?.url;\n if (!url) {\n return { ok: false, error: `no first-party download URL for weights ref \"${ref}\" — set a pack downloadUrl or place a gguf at ${dest}` };\n }\n if (!isAllowedWeightUrl(url)) {\n return { ok: false, error: `weight URL host not allowlisted: ${url}` };\n }\n\n try {\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n const tmp = `${dest}.partial.${process.pid}.${Date.now()}`;\n const fetchFn = options.fetchToFile ?? defaultFetchToFile;\n options.onProgress?.(`downloading ${ref}`);\n await fetchFn(url, tmp, options.onProgress);\n const sha = (options.expectedSha256 ?? entry?.sha256)?.trim();\n if (sha) {\n const actual = sha256File(tmp);\n if (actual !== sha.toLowerCase()) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n /* ignore */\n }\n return { ok: false, error: `sha256 mismatch for ${ref} (expected ${sha}, got ${actual})` };\n }\n } else if (entry && !entry.sha256) {\n options.onProgress?.(`warning: ${ref} has no sha256 pin in catalog — integrity not verified`);\n }\n // Atomic publish: if another worker won the race, drop our partial.\n if (isWeightCached(ref, env)) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n /* ignore */\n }\n return { ok: true, path: dest, fromCache: true };\n }\n fs.renameSync(tmp, dest);\n return { ok: true, path: dest, fromCache: false };\n } catch (e) {\n return { ok: false, error: e instanceof Error ? e.message : String(e) };\n } finally {\n inflight.delete(dest);\n }\n })();\n\n inflight.set(dest, work);\n return work;\n}\n\n/**\n * Stream a file through sha256. Never `readFileSync` whole weights — Node rejects\n * buffers larger than ~2 GiB (`File size (…) is greater than 2 GiB`), which broke\n * Flow/Forge installs even when the download completed.\n */\nfunction sha256File(file: string): string {\n const h = createHash('sha256');\n const fd = fs.openSync(file, 'r');\n try {\n const buf = Buffer.alloc(8 * 1024 * 1024);\n let n: number;\n while ((n = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {\n h.update(n === buf.length ? buf : buf.subarray(0, n));\n }\n } finally {\n fs.closeSync(fd);\n }\n return h.digest('hex');\n}\n\n/** Minimal HTTPS GET with redirect follow (tests inject fetchToFile). */\nexport function defaultFetchToFile(url: string, dest: string, onProgress?: (line: string) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const get = (u: string, redirects: number): void => {\n if (redirects > 8) {\n reject(new Error('too many redirects'));\n return;\n }\n const lib = u.startsWith('https:') ? https : http;\n const req = lib.get(u, (res) => {\n const code = res.statusCode ?? 0;\n if (code >= 300 && code < 400 && res.headers.location) {\n const next = new URL(res.headers.location, u).href;\n // Redirects must stay on HTTPS + allowlisted hosts (incl. HF LFS/Xet CDNs).\n try {\n const nextUrl = new URL(next);\n if (nextUrl.protocol !== 'https:' || !isAllowedWeightHost(nextUrl.hostname)) {\n reject(new Error(`redirect host not allowlisted: ${nextUrl.hostname || next}`));\n return;\n }\n } catch {\n reject(new Error('invalid redirect URL'));\n return;\n }\n res.resume();\n get(next, redirects + 1);\n return;\n }\n if (code !== 200) {\n reject(new Error(`HTTP ${code} fetching weights`));\n res.resume();\n return;\n }\n const total = Number(res.headers['content-length'] || 0);\n let got = 0;\n const out = fs.createWriteStream(dest);\n res.on('data', (chunk: Buffer) => {\n got += chunk.length;\n if (total > 0 && got % (8 * 1024 * 1024) < chunk.length) {\n onProgress?.(`${Math.floor((100 * got) / total)}%`);\n }\n });\n res.pipe(out);\n out.on('finish', () => resolve());\n out.on('error', reject);\n res.on('error', reject);\n });\n req.on('error', reject);\n };\n get(url, 0);\n });\n}\n\n/**\n * Free bytes on the volume that holds `targetPath` (weight store / cache).\n * Best-effort via `statfsSync` (Node 18.15+); walks parents if path is missing.\n */\nexport function freeBytesOnVolume(targetPath: string): number | undefined {\n let dir = targetPath;\n for (let i = 0; i < 10; i++) {\n try {\n const st = (\n fs as typeof fs & {\n statfsSync?: (p: string) => { bavail: number | bigint; bsize: number | bigint };\n }\n ).statfsSync?.(dir);\n if (st) {\n const free = Number(st.bavail) * Number(st.bsize);\n if (Number.isFinite(free) && free >= 0) return free;\n }\n } catch {\n /* try parent */\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\n/** List GGUF files already in the first-party store (for discovery). */\nexport function listCachedWeights(env: NodeJS.ProcessEnv = process.env): Array<{ name: string; path: string }> {\n const dir = weightStoreDir(env);\n try {\n return fs\n .readdirSync(dir)\n .filter((n) => n.endsWith('.gguf'))\n .sort()\n .map((n) => ({ name: n, path: path.join(dir, n) }));\n } catch {\n return [];\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { runBuild } from './chunk-
|
|
2
|
-
import { loadGraph } from './chunk-
|
|
1
|
+
import { runBuild } from './chunk-UP2BR6TV.js';
|
|
2
|
+
import { loadGraph } from './chunk-ARGYMFST.js';
|
|
3
3
|
|
|
4
4
|
// src/code/ensure-map.ts
|
|
5
5
|
async function ensureCodeMap(root, global, hooks) {
|
|
@@ -38,5 +38,5 @@ async function ensureCodeMap(root, global, hooks) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export { ensureCodeMap };
|
|
41
|
-
//# sourceMappingURL=chunk-
|
|
42
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-5R4HYDTC.js.map
|
|
42
|
+
//# sourceMappingURL=chunk-5R4HYDTC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/code/ensure-map.ts"],"names":[],"mappings":";;;;AA4BA,eAAsB,aAAA,CACpB,IAAA,EACA,MAAA,EACA,KAAA,EAC4B;AAC5B,EAAA,IAAI,SAAA,CAAU,IAAA,EAAM,MAAA,CAAO,KAAK,GAAG,OAAO,OAAA;AAE1C,EAAA,KAAA,EAAO,UAAA,GAAa;AAAA,IAClB,KAAA,EAAO,OAAA;AAAA,IACP,OAAA,EAAS,+DAAA;AAAA,IACT,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,IAAI;AACF,IAAA,MAAM,QAAA;AAAA,MACJ,CAAC,IAAI,CAAA;AAAA,MACL,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAM;AAAA,MAC7B,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAK;AAAA,MACzB;AAAA,QACE,eAAA,EAAiB,CAAC,IAAA,EAAM,KAAA,KAAU;AAChC,UAAA,MAAM,GAAA,GAAM,KAAA,GAAQ,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,KAAA,CAAO,IAAA,GAAO,KAAA,GAAS,GAAG,CAAC,CAAA,GAAI,KAAA,CAAA;AACzE,UAAA,KAAA,EAAO,UAAA,GAAa;AAAA,YAClB,KAAA,EAAO,UAAA;AAAA,YACP,OAAA,EACE,KAAA,GAAQ,CAAA,GACJ,CAAA,oBAAA,EAAkB,IAAA,CAAK,cAAA,EAAgB,CAAA,CAAA,EAAI,KAAA,CAAM,cAAA,EAAgB,CAAA,CAAA,GACjE,qBAAA;AAAA,YACN;AAAA,WACD,CAAA;AAAA,QACH;AAAA;AACF,KACF;AACA,IAAA,KAAA,EAAO,UAAA,GAAa,EAAE,KAAA,EAAO,MAAA,EAAQ,SAAS,gDAAA,EAAwC,GAAA,EAAK,KAAK,CAAA;AAChG,IAAA,OAAO,OAAA;AAAA,EACT,SAAS,CAAA,EAAG;AACV,IAAA,MAAM,UAAU,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,OAAO,CAAC,CAAA;AACzD,IAAA,KAAA,EAAO,UAAA,GAAa;AAAA,MAClB,KAAA,EAAO,OAAA;AAAA,MACP,OAAA,EAAS,iCAAiC,OAAO,CAAA;AAAA,KAClD,CAAA;AACD,IAAA,MAAM,CAAA;AAAA,EACR;AACF","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/code/ensure-map.ts"],"names":[],"mappings":";;;;AA4BA,eAAsB,aAAA,CACpB,IAAA,EACA,MAAA,EACA,KAAA,EAC4B;AAC5B,EAAA,IAAI,SAAA,CAAU,IAAA,EAAM,MAAA,CAAO,KAAK,GAAG,OAAO,OAAA;AAE1C,EAAA,KAAA,EAAO,UAAA,GAAa;AAAA,IAClB,KAAA,EAAO,OAAA;AAAA,IACP,OAAA,EAAS,+DAAA;AAAA,IACT,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,IAAI;AACF,IAAA,MAAM,QAAA;AAAA,MACJ,CAAC,IAAI,CAAA;AAAA,MACL,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAM;AAAA,MAC7B,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAK;AAAA,MACzB;AAAA,QACE,eAAA,EAAiB,CAAC,IAAA,EAAM,KAAA,KAAU;AAChC,UAAA,MAAM,GAAA,GAAM,KAAA,GAAQ,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,KAAA,CAAO,IAAA,GAAO,KAAA,GAAS,GAAG,CAAC,CAAA,GAAI,KAAA,CAAA;AACzE,UAAA,KAAA,EAAO,UAAA,GAAa;AAAA,YAClB,KAAA,EAAO,UAAA;AAAA,YACP,OAAA,EACE,KAAA,GAAQ,CAAA,GACJ,CAAA,oBAAA,EAAkB,IAAA,CAAK,cAAA,EAAgB,CAAA,CAAA,EAAI,KAAA,CAAM,cAAA,EAAgB,CAAA,CAAA,GACjE,qBAAA;AAAA,YACN;AAAA,WACD,CAAA;AAAA,QACH;AAAA;AACF,KACF;AACA,IAAA,KAAA,EAAO,UAAA,GAAa,EAAE,KAAA,EAAO,MAAA,EAAQ,SAAS,gDAAA,EAAwC,GAAA,EAAK,KAAK,CAAA;AAChG,IAAA,OAAO,OAAA;AAAA,EACT,SAAS,CAAA,EAAG;AACV,IAAA,MAAM,UAAU,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,OAAO,CAAC,CAAA;AACzD,IAAA,KAAA,EAAO,UAAA,GAAa;AAAA,MAClB,KAAA,EAAO,OAAA;AAAA,MACP,OAAA,EAAS,iCAAiC,OAAO,CAAA;AAAA,KAClD,CAAA;AACD,IAAA,MAAM,CAAA;AAAA,EACR;AACF","file":"chunk-5R4HYDTC.js","sourcesContent":["/**\n * Ensure a code map exists before VG Code (or any host) starts work.\n *\n * Interactive CLI, `vg code --stream-json`, and VS Code all share this path so\n * the first turn never fails with \"no map found\" — we build once (with progress\n * callbacks for host UIs) and then proceed.\n */\n\nimport { loadGraph } from '../engine/load.js';\nimport { runBuild } from '../commands/build.js';\nimport type { GlobalOpts } from '../cli-options.js';\n\nexport type EnsureMapPhase = 'start' | 'progress' | 'done' | 'error';\n\nexport interface EnsureMapProgress {\n phase: EnsureMapPhase;\n message: string;\n /** 0–100 when known (parse phase); omitted when indeterminate. */\n pct?: number;\n}\n\nexport interface EnsureMapHooks {\n onProgress?: (p: EnsureMapProgress) => void;\n}\n\n/**\n * @returns `ready` when a map already existed; `built` when this call built one.\n */\nexport async function ensureCodeMap(\n root: string,\n global: GlobalOpts,\n hooks?: EnsureMapHooks,\n): Promise<'ready' | 'built'> {\n if (loadGraph(root, global.graph)) return 'ready';\n\n hooks?.onProgress?.({\n phase: 'start',\n message: 'Building the code map for this project… (first run only)',\n pct: 0,\n });\n\n try {\n await runBuild(\n [root],\n { html: false, report: false },\n { ...global, quiet: true },\n {\n onParseProgress: (done, total) => {\n const pct = total > 0 ? Math.min(99, Math.round((done / total) * 100)) : undefined;\n hooks?.onProgress?.({\n phase: 'progress',\n message:\n total > 0\n ? `Parsing files… ${done.toLocaleString()}/${total.toLocaleString()}`\n : 'Parsing files…',\n pct,\n });\n },\n },\n );\n hooks?.onProgress?.({ phase: 'done', message: 'Code map ready — starting your task…', pct: 100 });\n return 'built';\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n hooks?.onProgress?.({\n phase: 'error',\n message: `Could not build the code map: ${message}`,\n });\n throw e;\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveIngestHost } from './chunk-HTLDXEHW.js';
|
|
2
2
|
import { cacheDir } from './chunk-MVPRPSXU.js';
|
|
3
|
-
import { VERSION } from './chunk-
|
|
3
|
+
import { VERSION } from './chunk-MQJY4EQV.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import * as path from 'path';
|
|
@@ -212,5 +212,5 @@ var StatsSharer = class {
|
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
export { StatsSharer, installId, isCI, statsEndpoint, telemetryOptOut };
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-5RMME7PT.js.map
|
|
216
|
+
//# sourceMappingURL=chunk-5RMME7PT.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine/stats-share.ts"],"names":[],"mappings":"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","file":"chunk-EI5AKDSC.js","sourcesContent":["import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { cacheDir } from './cache.js';\nimport { VERSION } from '../version.js';\nimport { resolveIngestHost } from '../reporting/regions.js';\nimport type { SavingEntry, Source, Outcome } from './savings.js';\n\n/**\n * Opt-in usage-stats sharing for `vg serve --share-stats`.\n *\n * This is the ONLY path in `vg serve` that sends anything off the machine, and\n * it is off unless the operator explicitly asks for it. What we send is the\n * same counts-only ledger `vg savings` reports locally — per (source, client,\n * tool, outcome): how many calls and the vg-vs-grep token figures — plus the vg\n * version, OS/arch, and a random per-install id. We never send code, file paths,\n * question text, repo identity, or any credential. See GUARDRAILS §3.4.\n *\n * Even when the flag is passed, the universal `DO_NOT_TRACK` opt-out (or\n * `VIBGRATE_TELEMETRY=0`) wins: nothing is uploaded and no install id is ever\n * minted or persisted (see `telemetryOptOut` / `installId`).\n *\n * Design:\n * - The local ledger (savings.jsonl) is the single source of truth; the flusher\n * reads only the tail past a persisted byte offset, so a batch is sent once.\n * - Fire-and-forget with a short timeout. A network problem never blocks a tool\n * call and never throws — the offset only advances on a confirmed 2xx, so a\n * failed batch is retried on the next flush.\n * - Fully offline-safe: under `--local` the sharer is never constructed.\n */\n\n/** Payload schema version — must match the server's `mcpUsageStatsSchema`. */\nconst SCHEMA_VERSION = '1';\n/** Where the offset (and cached install id) live, per repo, beside the ledger. */\nconst OFFSET_FILE = 'stats-share.json';\n/** The machine-global anonymous install id lives here (stable across repos). */\nconst INSTALL_ID_DIR = path.join(os.homedir(), '.vibgrate');\nconst INSTALL_ID_FILE = path.join(INSTALL_ID_DIR, 'install-id');\n/** Give a flush POST a hard ceiling so a hung endpoint never stalls shutdown. */\nconst SEND_TIMEOUT_MS = 4000;\n\n/** One aggregated row of the shared batch — counts only. */\nexport interface StatsRow {\n source: Source;\n client: string;\n tool: string;\n outcome: Outcome;\n calls: number;\n vgTokens: number;\n baselineTokens: number;\n}\n\n/** The counts-only batch POSTed to the ingest endpoint. */\nexport interface StatsBatch {\n schemaVersion: string;\n installId: string;\n vgVersion: string;\n os: string;\n arch: string;\n windowStart: string;\n windowEnd: string;\n totalCalls: number;\n rows: StatsRow[];\n}\n\n/**\n * The universal opt-out (https://consoledonottrack.com) plus a tool-specific\n * alias, honoured even when `--share-stats` is passed explicitly. Pure function\n * of the environment for testability. Returns the name of the variable that\n * opted out (for the disclosure message), or null when sharing may proceed.\n */\nexport function telemetryOptOut(env: NodeJS.ProcessEnv = process.env): string | null {\n const dnt = (env.DO_NOT_TRACK ?? '').trim().toLowerCase();\n if (dnt !== '' && dnt !== '0' && dnt !== 'false') return 'DO_NOT_TRACK';\n const vt = (env.VIBGRATE_TELEMETRY ?? '').trim().toLowerCase();\n if (vt === '0' || vt === 'false' || vt === 'off') return 'VIBGRATE_TELEMETRY';\n return null;\n}\n\n/** Common CI environment markers — a CI runner is never an interactive session. */\nconst CI_ENV_VARS = [\n 'CI',\n 'GITHUB_ACTIONS',\n 'GITLAB_CI',\n 'CIRCLECI',\n 'TRAVIS',\n 'BUILDKITE',\n 'JENKINS_URL',\n 'TF_BUILD',\n] as const;\n\n/** True when running under a recognised CI system. Pure function of the env. */\nexport function isCI(env: NodeJS.ProcessEnv = process.env): boolean {\n return CI_ENV_VARS.some((v) => {\n const val = (env[v] ?? '').trim().toLowerCase();\n return val !== '' && val !== '0' && val !== 'false';\n });\n}\n\n/**\n * Read (or lazily create) the anonymous per-install id — a random UUID stored in\n * `~/.vibgrate/install-id`. It is *not* tied to the user, the repo, or any\n * account; it only lets us de-duplicate batches from the same install when\n * aggregating. If the home dir isn't writable we fall back to an ephemeral id\n * so sharing still works (it just can't be de-duplicated across restarts).\n *\n * Under an env opt-out no id is ever read or persisted (the sharing path isn't\n * reached then either — this is defence in depth), and on CI runners the id is\n * ephemeral: a persisted id on a shared/throwaway runner would be meaningless\n * for de-duplication and would leave state behind on machines we don't own.\n */\nexport function installId(env: NodeJS.ProcessEnv = process.env): string {\n if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();\n try {\n const existing = fs.readFileSync(INSTALL_ID_FILE, 'utf8').trim();\n if (isUuid(existing)) return existing;\n } catch {\n /* not created yet */\n }\n const id = randomUuid();\n try {\n fs.mkdirSync(INSTALL_ID_DIR, { recursive: true });\n fs.writeFileSync(INSTALL_ID_FILE, id + '\\n', { mode: 0o600 });\n } catch {\n /* home dir read-only — use the ephemeral id for this process */\n }\n return id;\n}\n\nfunction isUuid(s: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);\n}\n\nfunction randomUuid(): string {\n return randomUUID();\n}\n\n/** Resolve the ingest endpoint: env override wins, else the default region host. */\nexport function statsEndpoint(): string {\n const override = process.env.VIBGRATE_STATS_ENDPOINT;\n if (override && /^https?:\\/\\//.test(override)) return override;\n return `https://${resolveIngestHost()}/v1/ingest/cli-mcp-usage`;\n}\n\nfunction offsetPath(root: string): string {\n return path.join(cacheDir(root), OFFSET_FILE);\n}\n\nfunction ledgerFilePath(root: string): string {\n return path.join(cacheDir(root), 'savings.jsonl');\n}\n\nfunction readOffset(root: string): number {\n try {\n const raw = JSON.parse(fs.readFileSync(offsetPath(root), 'utf8')) as { offset?: number };\n return typeof raw.offset === 'number' && raw.offset >= 0 ? raw.offset : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeOffset(root: string, offset: number): void {\n try {\n fs.mkdirSync(cacheDir(root), { recursive: true });\n fs.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + '\\n');\n } catch {\n /* best-effort; a lost offset just means one batch may re-send */\n }\n}\n\n/**\n * Aggregate the ledger entries after `fromOffset` into a counts-only batch.\n * Returns the batch (or null if there's nothing new) and the new byte offset to\n * persist once the batch is confirmed sent.\n */\nexport function buildBatch(\n root: string,\n fromOffset: number,\n id = installId(),\n): { batch: StatsBatch | null; newOffset: number } {\n const file = ledgerFilePath(root);\n let size = 0;\n try {\n size = fs.statSync(file).size;\n } catch {\n return { batch: null, newOffset: 0 };\n }\n // The ledger was truncated/recreated under us — re-read from the top.\n const start = fromOffset > size ? 0 : fromOffset;\n if (start >= size) return { batch: null, newOffset: size };\n\n let text = '';\n try {\n const fd = fs.openSync(file, 'r');\n try {\n const buf = Buffer.alloc(size - start);\n fs.readSync(fd, buf, 0, buf.length, start);\n text = buf.toString('utf8');\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return { batch: null, newOffset: fromOffset };\n }\n\n // Only whole lines are complete; a trailing partial line (mid-append) is left\n // for the next flush by rewinding the offset to the last newline.\n const lastNl = text.lastIndexOf('\\n');\n if (lastNl < 0) return { batch: null, newOffset: start };\n const consumed = start + lastNl + 1;\n const rows = new Map<string, StatsRow>();\n let total = 0;\n let minTs = Infinity;\n let maxTs = -Infinity;\n for (const line of text.slice(0, lastNl).split('\\n')) {\n if (!line.trim()) continue;\n let e: SavingEntry;\n try {\n e = JSON.parse(line) as SavingEntry;\n } catch {\n continue;\n }\n const source: Source = e.source ?? 'mcp';\n const client = e.client ?? 'unknown';\n const outcome: Outcome = e.outcome ?? 'complete';\n const key = `${source}|${client}|${e.tool}|${outcome}`;\n const row = rows.get(key) ?? {\n source,\n client,\n tool: e.tool,\n outcome,\n calls: 0,\n vgTokens: 0,\n baselineTokens: 0,\n };\n row.calls++;\n row.vgTokens += e.vgTokens ?? 0;\n row.baselineTokens += e.baselineTokens ?? 0;\n rows.set(key, row);\n total++;\n if (typeof e.ts === 'number') {\n if (e.ts < minTs) minTs = e.ts;\n if (e.ts > maxTs) maxTs = e.ts;\n }\n }\n if (total === 0 || !Number.isFinite(minTs)) return { batch: null, newOffset: consumed };\n const batch: StatsBatch = {\n schemaVersion: SCHEMA_VERSION,\n installId: id,\n vgVersion: VERSION,\n os: process.platform,\n arch: process.arch,\n windowStart: new Date(minTs).toISOString(),\n windowEnd: new Date(maxTs).toISOString(),\n totalCalls: total,\n rows: [...rows.values()],\n };\n return { batch, newOffset: consumed };\n}\n\n/**\n * POST a batch to the endpoint. Never throws; returns whether it was accepted.\n * A short timeout keeps a hung endpoint from stalling the caller (e.g. shutdown).\n */\nasync function postBatch(batch: StatsBatch): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);\n timer.unref?.();\n try {\n const res = await fetch(statsEndpoint(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Connection: 'close' },\n body: JSON.stringify(batch),\n signal: controller.signal,\n });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Periodic + on-shutdown flusher for one serve session. Holds the install id and\n * the persisted offset so each flush reads only new ledger lines and advances the\n * offset only when the batch is confirmed sent.\n */\nexport class StatsSharer {\n private readonly id = installId();\n private offset: number;\n private flushing = false;\n\n constructor(private readonly root: string) {\n this.offset = readOffset(root);\n }\n\n /**\n * Flush any new ledger entries. Single-flight (a slow POST won't overlap the\n * next tick). Best-effort: any failure is swallowed and retried next time.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n this.flushing = true;\n try {\n const { batch, newOffset } = buildBatch(this.root, this.offset, this.id);\n if (!batch) {\n // Nothing to send, but still advance past consumed blank/partial space.\n if (newOffset > this.offset) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n return;\n }\n const ok = await postBatch(batch);\n if (ok) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n } catch {\n /* never let sharing break serving */\n } finally {\n this.flushing = false;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine/stats-share.ts"],"names":[],"mappings":"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","file":"chunk-5RMME7PT.js","sourcesContent":["import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { cacheDir } from './cache.js';\nimport { VERSION } from '../version.js';\nimport { resolveIngestHost } from '../reporting/regions.js';\nimport type { SavingEntry, Source, Outcome } from './savings.js';\n\n/**\n * Opt-in usage-stats sharing for `vg serve --share-stats`.\n *\n * This is the ONLY path in `vg serve` that sends anything off the machine, and\n * it is off unless the operator explicitly asks for it. What we send is the\n * same counts-only ledger `vg savings` reports locally — per (source, client,\n * tool, outcome): how many calls and the vg-vs-grep token figures — plus the vg\n * version, OS/arch, and a random per-install id. We never send code, file paths,\n * question text, repo identity, or any credential. See GUARDRAILS §3.4.\n *\n * Even when the flag is passed, the universal `DO_NOT_TRACK` opt-out (or\n * `VIBGRATE_TELEMETRY=0`) wins: nothing is uploaded and no install id is ever\n * minted or persisted (see `telemetryOptOut` / `installId`).\n *\n * Design:\n * - The local ledger (savings.jsonl) is the single source of truth; the flusher\n * reads only the tail past a persisted byte offset, so a batch is sent once.\n * - Fire-and-forget with a short timeout. A network problem never blocks a tool\n * call and never throws — the offset only advances on a confirmed 2xx, so a\n * failed batch is retried on the next flush.\n * - Fully offline-safe: under `--local` the sharer is never constructed.\n */\n\n/** Payload schema version — must match the server's `mcpUsageStatsSchema`. */\nconst SCHEMA_VERSION = '1';\n/** Where the offset (and cached install id) live, per repo, beside the ledger. */\nconst OFFSET_FILE = 'stats-share.json';\n/** The machine-global anonymous install id lives here (stable across repos). */\nconst INSTALL_ID_DIR = path.join(os.homedir(), '.vibgrate');\nconst INSTALL_ID_FILE = path.join(INSTALL_ID_DIR, 'install-id');\n/** Give a flush POST a hard ceiling so a hung endpoint never stalls shutdown. */\nconst SEND_TIMEOUT_MS = 4000;\n\n/** One aggregated row of the shared batch — counts only. */\nexport interface StatsRow {\n source: Source;\n client: string;\n tool: string;\n outcome: Outcome;\n calls: number;\n vgTokens: number;\n baselineTokens: number;\n}\n\n/** The counts-only batch POSTed to the ingest endpoint. */\nexport interface StatsBatch {\n schemaVersion: string;\n installId: string;\n vgVersion: string;\n os: string;\n arch: string;\n windowStart: string;\n windowEnd: string;\n totalCalls: number;\n rows: StatsRow[];\n}\n\n/**\n * The universal opt-out (https://consoledonottrack.com) plus a tool-specific\n * alias, honoured even when `--share-stats` is passed explicitly. Pure function\n * of the environment for testability. Returns the name of the variable that\n * opted out (for the disclosure message), or null when sharing may proceed.\n */\nexport function telemetryOptOut(env: NodeJS.ProcessEnv = process.env): string | null {\n const dnt = (env.DO_NOT_TRACK ?? '').trim().toLowerCase();\n if (dnt !== '' && dnt !== '0' && dnt !== 'false') return 'DO_NOT_TRACK';\n const vt = (env.VIBGRATE_TELEMETRY ?? '').trim().toLowerCase();\n if (vt === '0' || vt === 'false' || vt === 'off') return 'VIBGRATE_TELEMETRY';\n return null;\n}\n\n/** Common CI environment markers — a CI runner is never an interactive session. */\nconst CI_ENV_VARS = [\n 'CI',\n 'GITHUB_ACTIONS',\n 'GITLAB_CI',\n 'CIRCLECI',\n 'TRAVIS',\n 'BUILDKITE',\n 'JENKINS_URL',\n 'TF_BUILD',\n] as const;\n\n/** True when running under a recognised CI system. Pure function of the env. */\nexport function isCI(env: NodeJS.ProcessEnv = process.env): boolean {\n return CI_ENV_VARS.some((v) => {\n const val = (env[v] ?? '').trim().toLowerCase();\n return val !== '' && val !== '0' && val !== 'false';\n });\n}\n\n/**\n * Read (or lazily create) the anonymous per-install id — a random UUID stored in\n * `~/.vibgrate/install-id`. It is *not* tied to the user, the repo, or any\n * account; it only lets us de-duplicate batches from the same install when\n * aggregating. If the home dir isn't writable we fall back to an ephemeral id\n * so sharing still works (it just can't be de-duplicated across restarts).\n *\n * Under an env opt-out no id is ever read or persisted (the sharing path isn't\n * reached then either — this is defence in depth), and on CI runners the id is\n * ephemeral: a persisted id on a shared/throwaway runner would be meaningless\n * for de-duplication and would leave state behind on machines we don't own.\n */\nexport function installId(env: NodeJS.ProcessEnv = process.env): string {\n if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();\n try {\n const existing = fs.readFileSync(INSTALL_ID_FILE, 'utf8').trim();\n if (isUuid(existing)) return existing;\n } catch {\n /* not created yet */\n }\n const id = randomUuid();\n try {\n fs.mkdirSync(INSTALL_ID_DIR, { recursive: true });\n fs.writeFileSync(INSTALL_ID_FILE, id + '\\n', { mode: 0o600 });\n } catch {\n /* home dir read-only — use the ephemeral id for this process */\n }\n return id;\n}\n\nfunction isUuid(s: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);\n}\n\nfunction randomUuid(): string {\n return randomUUID();\n}\n\n/** Resolve the ingest endpoint: env override wins, else the default region host. */\nexport function statsEndpoint(): string {\n const override = process.env.VIBGRATE_STATS_ENDPOINT;\n if (override && /^https?:\\/\\//.test(override)) return override;\n return `https://${resolveIngestHost()}/v1/ingest/cli-mcp-usage`;\n}\n\nfunction offsetPath(root: string): string {\n return path.join(cacheDir(root), OFFSET_FILE);\n}\n\nfunction ledgerFilePath(root: string): string {\n return path.join(cacheDir(root), 'savings.jsonl');\n}\n\nfunction readOffset(root: string): number {\n try {\n const raw = JSON.parse(fs.readFileSync(offsetPath(root), 'utf8')) as { offset?: number };\n return typeof raw.offset === 'number' && raw.offset >= 0 ? raw.offset : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeOffset(root: string, offset: number): void {\n try {\n fs.mkdirSync(cacheDir(root), { recursive: true });\n fs.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + '\\n');\n } catch {\n /* best-effort; a lost offset just means one batch may re-send */\n }\n}\n\n/**\n * Aggregate the ledger entries after `fromOffset` into a counts-only batch.\n * Returns the batch (or null if there's nothing new) and the new byte offset to\n * persist once the batch is confirmed sent.\n */\nexport function buildBatch(\n root: string,\n fromOffset: number,\n id = installId(),\n): { batch: StatsBatch | null; newOffset: number } {\n const file = ledgerFilePath(root);\n let size = 0;\n try {\n size = fs.statSync(file).size;\n } catch {\n return { batch: null, newOffset: 0 };\n }\n // The ledger was truncated/recreated under us — re-read from the top.\n const start = fromOffset > size ? 0 : fromOffset;\n if (start >= size) return { batch: null, newOffset: size };\n\n let text = '';\n try {\n const fd = fs.openSync(file, 'r');\n try {\n const buf = Buffer.alloc(size - start);\n fs.readSync(fd, buf, 0, buf.length, start);\n text = buf.toString('utf8');\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return { batch: null, newOffset: fromOffset };\n }\n\n // Only whole lines are complete; a trailing partial line (mid-append) is left\n // for the next flush by rewinding the offset to the last newline.\n const lastNl = text.lastIndexOf('\\n');\n if (lastNl < 0) return { batch: null, newOffset: start };\n const consumed = start + lastNl + 1;\n const rows = new Map<string, StatsRow>();\n let total = 0;\n let minTs = Infinity;\n let maxTs = -Infinity;\n for (const line of text.slice(0, lastNl).split('\\n')) {\n if (!line.trim()) continue;\n let e: SavingEntry;\n try {\n e = JSON.parse(line) as SavingEntry;\n } catch {\n continue;\n }\n const source: Source = e.source ?? 'mcp';\n const client = e.client ?? 'unknown';\n const outcome: Outcome = e.outcome ?? 'complete';\n const key = `${source}|${client}|${e.tool}|${outcome}`;\n const row = rows.get(key) ?? {\n source,\n client,\n tool: e.tool,\n outcome,\n calls: 0,\n vgTokens: 0,\n baselineTokens: 0,\n };\n row.calls++;\n row.vgTokens += e.vgTokens ?? 0;\n row.baselineTokens += e.baselineTokens ?? 0;\n rows.set(key, row);\n total++;\n if (typeof e.ts === 'number') {\n if (e.ts < minTs) minTs = e.ts;\n if (e.ts > maxTs) maxTs = e.ts;\n }\n }\n if (total === 0 || !Number.isFinite(minTs)) return { batch: null, newOffset: consumed };\n const batch: StatsBatch = {\n schemaVersion: SCHEMA_VERSION,\n installId: id,\n vgVersion: VERSION,\n os: process.platform,\n arch: process.arch,\n windowStart: new Date(minTs).toISOString(),\n windowEnd: new Date(maxTs).toISOString(),\n totalCalls: total,\n rows: [...rows.values()],\n };\n return { batch, newOffset: consumed };\n}\n\n/**\n * POST a batch to the endpoint. Never throws; returns whether it was accepted.\n * A short timeout keeps a hung endpoint from stalling the caller (e.g. shutdown).\n */\nasync function postBatch(batch: StatsBatch): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);\n timer.unref?.();\n try {\n const res = await fetch(statsEndpoint(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Connection: 'close' },\n body: JSON.stringify(batch),\n signal: controller.signal,\n });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Periodic + on-shutdown flusher for one serve session. Holds the install id and\n * the persisted offset so each flush reads only new ledger lines and advances the\n * offset only when the batch is confirmed sent.\n */\nexport class StatsSharer {\n private readonly id = installId();\n private offset: number;\n private flushing = false;\n\n constructor(private readonly root: string) {\n this.offset = readOffset(root);\n }\n\n /**\n * Flush any new ledger entries. Single-flight (a slow POST won't overlap the\n * next tick). Best-effort: any failure is swallowed and retried next time.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n this.flushing = true;\n try {\n const { batch, newOffset } = buildBatch(this.root, this.offset, this.id);\n if (!batch) {\n // Nothing to send, but still advance past consumed blank/partial space.\n if (newOffset > this.offset) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n return;\n }\n const ok = await postBatch(batch);\n if (ok) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n } catch {\n /* never let sharing break serving */\n } finally {\n this.flushing = false;\n }\n }\n}\n"]}
|