@enterstellar-ai/cloud 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +196 -0
- package/NOTICE +17 -0
- package/README.md +297 -0
- package/dist/index.cjs +1433 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1359 -0
- package/dist/index.d.ts +1359 -0
- package/dist/index.js +1428 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/version.ts","../src/transport/idempotency.ts","../src/transport/cloud-http.ts","../src/transport/cloud-sse.ts","../src/metering/ipu-tracker.ts","../src/metering/ipu-costs.ts","../src/inference/cloud-forge-proxy.ts","../src/inference/cloud-index-proxy.ts","../src/routing/cloud-router-proxy.ts","../src/analytics/cloud-analytics-proxy.ts","../src/traces/trace-submitter.ts","../src/signals/signal-submitter.ts","../src/operations/traces-query-proxy.ts","../src/operations/certify-proxy.ts","../src/operations/ledger-query-proxy.ts","../src/operations/data-deletion-proxy.ts","../src/create-cloud-client.ts"],"names":["errorBody","parseNumericHeader","parseErrorBody","requestId","body","buildIPU","buildQueryString"],"mappings":";;;;AAyFO,IAAM,UAAA,GAAN,MAAM,WAAA,SAAmB,iBAAA,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ9B,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,WAAA,CACI,IAAA,EACA,SAAA,EACA,OAAA,EACA,aACA,OAAA,EAMF;AACE,IAAA,KAAA,CAAM,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,WAAA,EAAa,SAAS,KAAK,CAAA;AACzD,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,aAAa,OAAA,EAAS,UAAA;AAC3B,IAAA,IAAA,CAAK,eAAe,OAAA,EAAS,YAAA;AAC7B,IAAA,IAAA,CAAK,YAAY,OAAA,EAAS,SAAA;AAI1B,IAAA,IAAI,uBAAuB,KAAA,EAAO;AAC9B,MAAC,KAAA,CAKE,iBAAA;AAAA,QACC,IAAA;AAAA,QACA;AAAA,OACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASgB,MAAA,GAYd;AACE,IAAA,OAAO;AAAA,MACH,GAAG,MAAM,MAAA,EAAO;AAAA,MAChB,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,MAAA,EAAQ,OAAA;AAAA,MACR,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,WAAW,IAAA,CAAK;AAAA,KACpB;AAAA,EACJ;AACJ;AAqBO,SAAS,kBAAkB,KAAA,EAA2B;AACzD,EAAA,OAAO,IAAI,UAAA;AAAA,IACP,UAAA;AAAA,IACA,UAAA;AAAA,IACA,kDAA6C,KAAK,CAAA,6CAAA,CAAA;AAAA,IAClD;AAAA,GACJ;AACJ;AAkBO,SAAS,mBAAA,GAAkC;AAC9C,EAAA,OAAO,IAAI,UAAA;AAAA,IACP,UAAA;AAAA,IACA,UAAA;AAAA,IACA,6GAAA;AAAA,IACA;AAAA,GACJ;AACJ;AAmDO,SAAS,yBAAyB,MAAA,EAA4B;AACjE,EAAA,OAAO,IAAI,UAAA;AAAA,IACP,UAAA;AAAA,IACA,UAAA;AAAA,IACA,2BAA2B,MAAM,CAAA,gJAAA,CAAA;AAAA,IAGjC;AAAA,GACJ;AACJ;AAiBO,SAAS,2BAAA,CACZ,QAAA,EACA,cAAA,EACA,SAAA,EACU;AACV,EAAA,MAAM,eAAe,cAAA,KAAmB,MAAA,GAClC,iBAAiB,MAAA,CAAO,cAAc,CAAC,CAAA,CAAA,CAAA,GACvC,+BAAA;AAEN,EAAA,OAAO,IAAI,UAAA;AAAA,IACP,UAAA;AAAA,IACA,UAAA;AAAA,IACA,CAAA,4BAAA,EAA+B,MAAA,CAAO,QAAQ,CAAC,0BAA0B,YAAY,CAAA,CAAA;AAAA,IACrF,IAAA;AAAA,IACA,EAAE,SAAA;AAAU,GAChB;AACJ;AAiCO,SAAS,wBAAA,CACZ,MACA,SAAA,EACU;AACV,EAAA,OAAO,IAAI,UAAA;AAAA,IACP,UAAA;AAAA;AAAA,IACA,IAAA,CAAK,IAAA;AAAA;AAAA,IACL,CAAA,wBAAA,EAA2B,KAAK,OAAO,CAAA,CAAA;AAAA,IACvC,IAAA;AAAA;AAAA,IACA;AAAA,MACI,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB;AAAA;AACJ,GACJ;AACJ;;;ACjWO,IAAM,iBAAA,GAAoB;;;ACEjC,IAAM,gBAAA,GAAmB,kCAAA;AAmBzB,SAAS,gBAAgB,MAAA,EAAwB;AAC7C,EAAA,IAAI,SAAA,GAAY,MAAA;AAChB,EAAA,MAAM,KAAA,GAAkB,IAAI,KAAA,CAAc,EAAE,CAAA;AAI5C,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,EAAG,CAAA,EAAA,EAAK;AAEzB,IAAA,KAAA,CAAM,CAAC,CAAA,GAAI,gBAAA,CAAiB,SAAA,GAAY,EAAI,CAAA;AAC5C,IAAA,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,EAAE,CAAA;AAAA,EACzC;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,EAAE,CAAA;AACxB;AAiBA,SAAS,gBAAA,GAA2B;AAChC,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,EAAE,CAAA;AAC/B,EAAA,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAE5B,EAAA,MAAM,KAAA,GAAkB,IAAI,KAAA,CAAc,EAAE,CAAA;AAK5C,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,KAAA,IAAS,SAAA,GAAY,CAAA,EAAG,SAAA,GAAY,EAAA,EAAI,SAAA,EAAA,EAAa;AAEjD,IAAA,SAAA,GAAa,SAAA,IAAa,CAAA,GAAK,KAAA,CAAM,SAAS,CAAA;AAC9C,IAAA,YAAA,IAAgB,CAAA;AAEhB,IAAA,OAAO,gBAAgB,CAAA,EAAG;AACtB,MAAA,YAAA,IAAgB,CAAA;AAChB,MAAA,MAAM,YAAA,GAAgB,cAAc,YAAA,GAAgB,EAAA;AAEpD,MAAA,KAAA,CAAM,SAAS,CAAA,GAAI,gBAAA,CAAiB,YAAY,CAAA;AAChD,MAAA,SAAA,EAAA;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,EAAE,CAAA;AACxB;AAgCO,SAAS,sBAAA,GAAiC;AAC7C,EAAA,OAAO,eAAA,CAAgB,IAAA,CAAK,GAAA,EAAK,IAAI,gBAAA,EAAiB;AAC1D;;;ACzFA,IAAM,kBAAA,GAAqB,GAAA;AAQ3B,IAAM,YAAA,GAAe,CAAA;AAQrB,IAAM,gBAAA,GAAsC,CAAC,GAAA,EAAO,GAAA,EAAO,GAAK,CAAA;AAYzD,IAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO;AAAA;AAAA,EAE5C,KAAA,EAAO,GAAA;AAAA;AAAA,EAGP,OAAA,EAAS,GAAA;AAAA;AAAA,EAGT,SAAA,EAAW,GAAA;AAAA;AAAA,EAGX,iBAAA,EAAmB,GAAA;AAAA;AAAA,EAGnB,OAAA,EAAS;AACb,CAAU,CAAA;AAqFV,SAAS,kBAAA,CAAmB,SAAkB,IAAA,EAAkC;AAC5E,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAC5B,EAAA,IAAI,QAAQ,IAAA,IAAQ,GAAA,CAAI,IAAA,EAAK,CAAE,WAAW,CAAA,EAAG;AACzC,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACtC,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA;AACX;AAYA,eAAe,cAAiB,QAAA,EAAuC;AACnE,EAAA,IAAI;AACA,IAAA,MAAM,IAAA,GAAgB,MAAM,QAAA,CAAS,IAAA,EAAK;AAC1C,IAAA,OAAO,IAAA;AAAA,EACX,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAYA,eAAe,eAAe,QAAA,EAAoD;AAC9E,EAAA,IAAI;AACA,IAAA,MAAM,GAAA,GAAe,MAAM,QAAA,CAAS,IAAA,EAAK;AAGzC,IAAA,IACI,OAAO,GAAA,KAAQ,QAAA,IACf,GAAA,KAAQ,IAAA,IACR,WAAW,GAAA,EACb;AACE,MAAA,MAAM,QAAA,GAAW,GAAA;AACjB,MAAA,MAAM,WAAW,QAAA,CAAS,KAAA;AAE1B,MAAA,IACI,OAAO,QAAA,KAAa,QAAA,IACpB,QAAA,KAAa,IAAA,IACb,UAAU,QAAA,IACV,SAAA,IAAa,QAAA,IACb,OAAQ,SAA+B,IAAA,KAAS,QAAA,IAChD,OAAQ,QAAA,CAAkC,YAAY,QAAA,EACxD;AACE,QAAA,MAAM,KAAA,GAAQ,QAAA;AAOd,QAAA,OAAO;AAAA,UACH,MAAM,KAAA,CAAM,IAAA;AAAA,UACZ,SAAS,KAAA,CAAM,OAAA;AAAA,UACf,cAAc,OAAO,KAAA,CAAM,YAAA,KAAiB,QAAA,GACtC,MAAM,YAAA,GACN,KAAA,CAAA;AAAA,UACN,YAAY,OAAO,KAAA,CAAM,UAAA,KAAe,QAAA,GAClC,MAAM,UAAA,GACN,KAAA;AAAA,SACV;AAAA,MACJ;AAAA,IACJ;AAEA,IAAA,OAAO,IAAA;AAAA,EACX,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAUA,SAAS,MAAM,EAAA,EAA2B;AACtC,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AAClC,IAAA,UAAA,CAAW,SAAS,EAAE,CAAA;AAAA,EAC1B,CAAC,CAAA;AACL;AAWA,SAAS,cAAA,CACL,eACA,gBAAA,EACM;AACN,EAAA,OAAO,iBAAiB,gBAAA,IAAoB,kBAAA;AAChD;AAoDO,SAAS,yBAAyB,MAAA,EAA6C;AAClF,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,SAAA,EAAW,iBAAgB,GAAI,MAAA;AAEzD,EAAA,OAAO;AAAA,IACH,MAAM,QAAW,SAAA,EAA0D;AACvE,MAAA,MAAM,gBAAA,GAAmB,cAAA;AAAA,QACrB,eAAA;AAAA,QACA,SAAA,CAAU;AAAA,OACd;AACA,MAAA,MAAM,GAAA,GAAM,CAAA,EAAG,QAAQ,CAAA,EAAG,UAAU,IAAI,CAAA,CAAA;AAOxC,MAAA,MAAM,cAAA,GAAiB,SAAA,CAAU,OAAA,GAAU,CAAA,GACrC,wBAAuB,GACvB,MAAA;AAMN,MAAA,MAAM,OAAA,GAAkC;AAAA,QACpC,eAAA,EAAiB,UAAU,MAAM,CAAA,CAAA;AAAA,QACjC,YAAA,EAAc,0BAA0B,iBAAiB,CAAA,CAAA;AAAA,QACzD,QAAA,EAAU;AAAA,OACd;AAGA,MAAA,IAAI,SAAA,CAAU,SAAS,MAAA,EAAW;AAC9B,QAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,kBAAA;AAAA,MAC9B;AAGA,MAAA,IAAI,mBAAmB,MAAA,EAAW;AAC9B,QAAA,OAAA,CAAQ,mBAAmB,CAAA,GAAI,cAAA;AAAA,MACnC;AAKA,MAAA,IAAI,cAAA;AACJ,MAAA,IAAI,aAAA;AAEJ,MAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,YAAA,EAAc,OAAA,EAAA,EAAW;AACrD,QAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,QAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AAC/B,UAAA,UAAA,CAAW,KAAA,EAAM;AAAA,QACrB,GAAG,gBAAgB,CAAA;AAEnB,QAAA,IAAI;AAIA,UAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,YAC9B,QAAQ,SAAA,CAAU,MAAA;AAAA,YAClB,OAAA;AAAA,YACA,GAAI,SAAA,CAAU,IAAA,KAAS,KAAA,CAAA,GACjB,EAAE,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA,EAAE,GACvC,EAAC;AAAA,YACP,QAAQ,UAAA,CAAW;AAAA,WACtB,CAAA;AAED,UAAA,YAAA,CAAa,SAAS,CAAA;AAKtB,UAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,QAAA,CAAS,OAAA,EAAS,YAAY,CAAA;AACjE,UAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,QAAA,CAAS,OAAA,EAAS,iBAAiB,CAAA;AAC3E,UAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,QAAA,CAAS,OAAA,EAAS,YAAY,CAAA;AACjE,UAAA,MAAM,SAAA,GAAY,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,KAAA,CAAA;AAE1D,UAAA,cAAA,GAAiB,QAAA,CAAS,MAAA;AAC1B,UAAA,aAAA,GAAgB,SAAA;AAKhB,UAAA,IAAI,SAAS,EAAA,EAAI;AACb,YAAA,MAAM,IAAA,GAAO,MAAM,aAAA,CAAiB,QAAQ,CAAA;AAE5C,YAAA,OAAO;AAAA,cACH,EAAA,EAAI,IAAA;AAAA,cACJ,YAAY,QAAA,CAAS,MAAA;AAAA,cACrB,IAAA;AAAA,cACA,OAAA;AAAA,cACA,YAAA;AAAA,cACA,OAAA;AAAA,cACA,SAAA;AAAA,cACA,KAAA,EAAO;AAAA,aACX;AAAA,UACJ;AAOA,UAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AACzB,YAAA,MAAMA,UAAAA,GAAY,MAAM,cAAA,CAAe,QAAQ,CAAA;AAG/C,YAAA,MAAM,OAAuBA,UAAAA,IAAa;AAAA,cACtC,IAAA,EAAM,WAAA;AAAA,cACN,OAAA,EAAS;AAAA,aACb;AAEA,YAAA,MAAM,wBAAA,CAAyB,MAAM,SAAS,CAAA;AAAA,UAClD;AAMA,UAAA,IAAI,QAAA,CAAS,UAAU,GAAA,EAAK;AAExB,YAAA,MAAM,QAAA,CAAS,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,KAAA,CAAS,CAAA;AAG3C,YAAA,IAAI,OAAA,GAAU,eAAe,CAAA,EAAG;AAE5B,cAAA,MAAM,SAAA,GAAY,iBAAiB,OAAO,CAAA;AAC1C,cAAA,MAAM,MAAM,SAAS,CAAA;AAAA,YACzB;AAEA,YAAA;AAAA,UACJ;AAOA,UAAA,MAAM,SAAA,GAAY,MAAM,cAAA,CAAe,QAAQ,CAAA;AAE/C,UAAA,MAAM,IAAI,UAAA;AAAA,YACN,UAAA;AAAA,YACA,WAAW,IAAA,IAAQ,CAAA,KAAA,EAAQ,MAAA,CAAO,QAAA,CAAS,MAAM,CAAC,CAAA,CAAA;AAAA,YAClD,CAAA,8CAAA,EAA4C,WAAW,OAAA,IAAW,CAAA,KAAA,EAAQ,OAAO,QAAA,CAAS,MAAM,CAAC,CAAA,CAAE,CAAA,CAAA,CAAA;AAAA,YACnG,KAAA;AAAA,YACA,EAAE,SAAA;AAAU,WAChB;AAAA,QACJ,SAAS,KAAA,EAAgB;AACrB,UAAA,YAAA,CAAa,SAAS,CAAA;AAMtB,UAAA,IAAI,iBAAiB,UAAA,EAAY;AAC7B,YAAA,MAAM,KAAA;AAAA,UACV;AAMA,UAAA,cAAA,GAAiB,MAAA;AACjB,UAAA,aAAA,GAAgB,MAAA;AAGhB,UAAA,IAAI,OAAA,GAAU,eAAe,CAAA,EAAG;AAE5B,YAAA,MAAM,SAAA,GAAY,iBAAiB,OAAO,CAAA;AAC1C,YAAA,MAAM,MAAM,SAAS,CAAA;AAAA,UACzB;AAAA,QAGJ;AAAA,MACJ;AAKA,MAAA,MAAM,2BAAA,CAA4B,YAAA,EAAc,cAAA,EAAgB,aAAa,CAAA;AAAA,IACjF;AAAA,GACJ;AACJ;ACzZA,SAASC,mBAAAA,CAAmB,SAAkB,IAAA,EAAkC;AAC5E,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAC5B,EAAA,IAAI,QAAQ,IAAA,IAAQ,GAAA,CAAI,IAAA,EAAK,CAAE,WAAW,CAAA,EAAG;AACzC,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACtC,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA;AACX;AAYA,SAAS,eAAA,CAAgB,SAAkB,WAAA,EAAuC;AAC9E,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,IAAA,GAAOA,mBAAAA,CAAmB,OAAA,EAAS,YAAY,CAAA;AACrD,EAAA,MAAM,SAAA,GAAYA,mBAAAA,CAAmB,OAAA,EAAS,iBAAiB,CAAA;AAC/D,EAAA,MAAM,IAAA,GAAOA,mBAAAA,CAAmB,OAAA,EAAS,YAAY,CAAA;AAIrD,EAAA,IAAI,IAAA,KAAS,MAAA,IAAa,SAAA,KAAc,MAAA,IAAa,SAAS,MAAA,EAAW;AACrE,IAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,IAAA,EAAK;AAAA,EACnC;AAEA,EAAA,OAAO,IAAA;AACX;AAQA,eAAeC,gBAAe,QAAA,EAAoD;AAC9E,EAAA,IAAI;AACA,IAAA,MAAM,GAAA,GAAe,MAAM,QAAA,CAAS,IAAA,EAAK;AAEzC,IAAA,IACI,OAAO,GAAA,KAAQ,QAAA,IACf,GAAA,KAAQ,IAAA,IACR,WAAW,GAAA,EACb;AACE,MAAA,MAAM,QAAA,GAAW,GAAA;AACjB,MAAA,MAAM,WAAW,QAAA,CAAS,KAAA;AAE1B,MAAA,IACI,OAAO,QAAA,KAAa,QAAA,IACpB,QAAA,KAAa,IAAA,IACb,UAAU,QAAA,IACV,SAAA,IAAa,QAAA,IACb,OAAQ,SAA+B,IAAA,KAAS,QAAA,IAChD,OAAQ,QAAA,CAAkC,YAAY,QAAA,EACxD;AACE,QAAA,MAAM,KAAA,GAAQ,QAAA;AAOd,QAAA,OAAO;AAAA,UACH,MAAM,KAAA,CAAM,IAAA;AAAA,UACZ,SAAS,KAAA,CAAM,OAAA;AAAA,UACf,cAAc,OAAO,KAAA,CAAM,YAAA,KAAiB,QAAA,GACtC,MAAM,YAAA,GACN,KAAA,CAAA;AAAA,UACN,YAAY,OAAO,KAAA,CAAM,UAAA,KAAe,QAAA,GAClC,MAAM,UAAA,GACN,KAAA;AAAA,SACV;AAAA,MACJ;AAAA,IACJ;AAEA,IAAA,OAAO,IAAA;AAAA,EACX,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAWA,SAAS,oBAAoB,IAAA,EAAuB;AAChD,EAAA,IAAI;AACA,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAaA,SAAS,kBAAA,CACL,OACA,GAAA,EACoB;AACpB,EAAA,MAAM,SAAA,GAAY,MAAM,KAAA,IAAS,SAAA;AAEjC,EAAA,QAAQ,SAAA;AAAW,IACf,KAAK,MAAA,EAAQ;AACT,MAAA,MAAM,IAAA,GAAO,mBAAA,CAAoB,KAAA,CAAM,IAAI,CAAA;AAC3C,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,OAAO,IAAA;AAAA,MACX;AAEA,MAAA,MAAM,QAAA,GAA8B;AAAA,QAChC,IAAA,EAAM,MAAA;AAAA,QACN,MAAM,EAAE,QAAA,EAAU,KAAK,QAAA,EAAU,KAAA,EAAO,KAAK,KAAA,EAAM;AAAA,QACnD;AAAA,OACJ;AACA,MAAA,OAAO,QAAA;AAAA,IACX;AAAA,IAEA,KAAK,MAAA,EAAQ;AACT,MAAA,MAAM,IAAA,GAAO,mBAAA,CAAoB,KAAA,CAAM,IAAI,CAAA;AAC3C,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,OAAO,IAAA;AAAA,MACX;AAEA,MAAA,MAAM,QAAA,GAA8B;AAAA,QAChC,IAAA,EAAM,MAAA;AAAA,QACN;AAAA,OACJ;AACA,MAAA,OAAO,QAAA;AAAA,IACX;AAAA,IAEA,KAAK,UAAA,EAAY;AACb,MAAA,MAAM,IAAA,GAAO,mBAAA,CAAoB,KAAA,CAAM,IAAI,CAAA;AAC3C,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,OAAO,IAAA;AAAA,MACX;AAEA,MAAA,MAAM,QAAA,GAAkC;AAAA,QACpC,IAAA,EAAM,UAAA;AAAA,QACN,MAAM,EAAE,IAAA,EAAM,KAAK,IAAA,EAAM,KAAA,EAAO,KAAK,KAAA;AAAM,OAC/C;AACA,MAAA,OAAO,QAAA;AAAA,IACX;AAAA,IAEA,KAAK,UAAA,EAAY;AACb,MAAA,MAAM,IAAA,GAAO,mBAAA,CAAoB,KAAA,CAAM,IAAI,CAAA;AAC3C,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,OAAO,IAAA;AAAA,MACX;AAEA,MAAA,MAAM,QAAA,GAAkC;AAAA,QACpC,IAAA,EAAM,UAAA;AAAA,QACN,IAAA;AAAA,QACA;AAAA,OACJ;AACA,MAAA,OAAO,QAAA;AAAA,IACX;AAAA,IAEA,KAAK,OAAA,EAAS;AACV,MAAA,MAAM,IAAA,GAAO,mBAAA,CAAoB,KAAA,CAAM,IAAI,CAAA;AAC3C,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,OAAO,IAAA;AAAA,MACX;AAEA,MAAA,MAAM,QAAA,GAA+B;AAAA,QACjC,IAAA,EAAM,OAAA;AAAA,QACN,MAAM,EAAE,IAAA,EAAM,KAAK,IAAA,EAAM,OAAA,EAAS,KAAK,OAAA;AAAQ,OACnD;AACA,MAAA,OAAO,QAAA;AAAA,IACX;AAAA,IAEA;AAGI,MAAA,OAAO,IAAA;AAAA;AAEnB;AA6BO,SAAS,wBAAwB,MAAA,EAA4C;AAChF,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,SAAA,EAAW,iBAAgB,GAAI,MAAA;AAEzD,EAAA,OAAO;AAAA,IACH,OAAO,OAAO,SAAA,EAA2E;AACrF,MAAA,MAAM,gBAAA,GAAmB,mBAAmB,kBAAA,CAAmB,KAAA;AAC/D,MAAA,MAAM,GAAA,GAAM,GAAG,QAAQ,CAAA,SAAA,CAAA;AAKvB,MAAA,MAAM,iBAAiB,sBAAA,EAAuB;AAE9C,MAAA,MAAM,OAAA,GAAkC;AAAA,QACpC,eAAA,EAAiB,UAAU,MAAM,CAAA,CAAA;AAAA,QACjC,YAAA,EAAc,0BAA0B,iBAAiB,CAAA,CAAA;AAAA,QACzD,QAAA,EAAU,mBAAA;AAAA,QACV,cAAA,EAAgB,kBAAA;AAAA,QAChB,mBAAA,EAAqB;AAAA,OACzB;AAEA,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AAC/B,QAAA,UAAA,CAAW,KAAA,EAAM;AAAA,MACrB,GAAG,gBAAgB,CAAA;AAKnB,MAAA,IAAI,QAAA;AAEJ,MAAA,IAAI;AACA,QAAA,QAAA,GAAW,MAAM,MAAM,GAAA,EAAK;AAAA,UACxB,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA;AAAA,UACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA;AAAA,UACnC,QAAQ,UAAA,CAAW;AAAA,SACtB,CAAA;AAAA,MACL,CAAA,CAAA,MAAQ;AACJ,QAAA,YAAA,CAAa,SAAS,CAAA;AAItB,QAAA,MAAM,2BAAA;AAAA,UACF,CAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACJ;AAAA,MACJ;AAKA,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,QAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,QAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AACzB,UAAA,MAAMF,UAAAA,GAAY,MAAME,eAAAA,CAAe,QAAQ,CAAA;AAC/C,UAAA,MAAMC,UAAAA,GAAY,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,MAAA;AAC1D,UAAA,MAAMC,QAAuBJ,UAAAA,IAAa;AAAA,YACtC,IAAA,EAAM,WAAA;AAAA,YACN,OAAA,EAAS;AAAA,WACb;AAEA,UAAA,MAAM,wBAAA,CAAyBI,OAAMD,UAAS,CAAA;AAAA,QAClD;AAEA,QAAA,MAAM,SAAA,GAAY,MAAMD,eAAAA,CAAe,QAAQ,CAAA;AAC/C,QAAA,MAAM,SAAA,GAAY,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,MAAA;AAE1D,QAAA,MAAM,IAAI,UAAA;AAAA,UACN,UAAA;AAAA,UACA,WAAW,IAAA,IAAQ,CAAA,KAAA,EAAQ,MAAA,CAAO,QAAA,CAAS,MAAM,CAAC,CAAA,CAAA;AAAA,UAClD,CAAA,mDAAA,EAAiD,WAAW,OAAA,IAAW,CAAA,KAAA,EAAQ,OAAO,QAAA,CAAS,MAAM,CAAC,CAAA,CAAE,CAAA,CAAA,CAAA;AAAA,UACxG,KAAA;AAAA,UACA,EAAE,SAAA;AAAU,SAChB;AAAA,MACJ;AAKA,MAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,QAAA,CAAS,OAAA,EAAS,UAAU,WAAW,CAAA;AAKnE,MAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AAEtB,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,YAAA,CAAa,SAAS,CAAA;AACtB,QAAA,MAAM,2BAAA,CAA4B,CAAA,EAAG,QAAA,CAAS,MAAM,CAAA;AAAA,MACxD;AAKA,MAAA,MAAM,iBAAkC,EAAC;AACzC,MAAA,MAAM,WAAA,GAAc,EAAE,IAAA,EAAM,KAAA,EAAM;AAElC,MAAA,MAAM,SAAS,YAAA,CAAa;AAAA,QACxB,QAAQ,KAAA,EAAiC;AACrC,UAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,KAAA,EAAO,GAAG,CAAA;AAC9C,UAAA,IAAI,aAAa,IAAA,EAAM;AACnB,YAAA,cAAA,CAAe,KAAK,QAAQ,CAAA;AAG5B,YAAA,IAAI,QAAA,CAAS,IAAA,KAAS,UAAA,IAAc,QAAA,CAAS,SAAS,OAAA,EAAS;AAC3D,cAAA,WAAA,CAAY,IAAA,GAAO,IAAA;AAAA,YACvB;AAAA,UACJ;AAAA,QACJ;AAAA,OACH,CAAA;AAMD,MAAA,SAAS,WAAA,GAA+B;AACpC,QAAA,OAAO,cAAA,CAAe,OAAO,CAAC,CAAA;AAAA,MAClC;AAKA,MAAA,MAAM,MAAA,GAAS,KAAK,SAAA,EAAU;AAC9B,MAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEhC,MAAA,IAAI;AAEA,QAAA,IAAI,UAAA,GAAa,KAAA;AAEjB,QAAA,OAAO,CAAC,UAAA,EAAY;AAChB,UAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,IAAA,EAAK;AACrC,UAAA,UAAA,GAAa,UAAA,CAAW,IAAA;AAExB,UAAA,IAAI,UAAA,CAAW,UAAU,KAAA,CAAA,EAAW;AAEhC,YAAA,MAAM,IAAA,GAAO,QAAQ,MAAA,CAAO,UAAA,CAAW,OAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AAC9D,YAAA,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,UACpB;AAGA,UAAA,KAAA,MAAW,QAAA,IAAY,aAAY,EAAG;AAClC,YAAA,MAAM,QAAA;AAAA,UACV;AAIA,UAAA,IAAI,YAAY,IAAA,EAAM;AAClB,YAAA;AAAA,UACJ;AAAA,QACJ;AAGA,QAAA,KAAA,MAAW,QAAA,IAAY,aAAY,EAAG;AAClC,UAAA,MAAM,QAAA;AAAA,QACV;AAAA,MACJ,SAAS,KAAA,EAAgB;AAErB,QAAA,IAAI,iBAAiB,UAAA,EAAY;AAC7B,UAAA,MAAM,KAAA;AAAA,QACV;AAGA,QAAA,MAAM,2BAAA,CAA4B,CAAA,EAAG,MAAA,EAAW,MAAS,CAAA;AAAA,MAC7D,CAAA,SAAE;AACE,QAAA,YAAA,CAAa,SAAS,CAAA;AAGtB,QAAA,IAAI;AACA,UAAA,MAAA,CAAO,WAAA,EAAY;AAAA,QACvB,CAAA,CAAA,MAAQ;AAAA,QAGR;AAAA,MACJ;AAAA,IACJ;AAAA,GACJ;AACJ;;;AC5XA,IAAM,eAAA,GAAkB,GAAA;AAgCjB,SAAS,gBAAA,GAA+B;AAM3C,EAAA,IAAI,SAAA,GAAY,CAAA;AAMhB,EAAA,IAAI,WAAA,GAA6B,IAAA;AAGjC,EAAA,IAAI,aAAA,GAAgB,KAAA;AAOpB,EAAA,IAAI,WAAA;AAMJ,EAAA,OAAO;AAAA,IACH,OAAO,IAAA,EAAoB;AACvB,MAAA,SAAA,IAAa,IAAA;AAAA,IACjB,CAAA;AAAA,IAEA,SAAA,CAAU,UAAA,EAAoB,eAAA,EAAyB,OAAA,EAAwB;AAE3E,MAAA,WAAA,GAAc,OAAA;AAGd,MAAA,WAAA,GAAc,UAAA,GAAa,eAAA;AAW3B,MAAA,IAAI,eAAe,CAAA,EAAG;AAElB,QAAA,IAAI,YAAY,CAAA,EAAG;AACf,UAAA,SAAA,GAAY,UAAA;AACZ,UAAA,aAAA,GAAgB,IAAA;AAAA,QACpB,CAAA,MAAO;AACH,UAAA,aAAA,GAAgB,KAAA;AAAA,QACpB;AACA,QAAA;AAAA,MACJ;AAEA,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,SAAA,GAAY,UAAU,CAAA,GAAI,UAAA;AAEjD,MAAA,IAAI,QAAQ,eAAA,EAAiB;AAEzB,QAAA,SAAA,GAAY,UAAA;AACZ,QAAA,aAAA,GAAgB,IAAA;AAAA,MACpB,CAAA,MAAO;AACH,QAAA,aAAA,GAAgB,KAAA;AAAA,MACpB;AAAA,IACJ,CAAA;AAAA,IAEA,WAAA,GAA2B;AAEvB,MAAA,MAAM,SAAA,GAAY,gBAAgB,IAAA,GAC5B,IAAA,CAAK,IAAI,CAAA,EAAG,WAAA,GAAc,SAAS,CAAA,GACnC,IAAA;AAEN,MAAA,OAAO;AAAA,QACH,IAAA,EAAM,SAAA;AAAA,QACN,SAAA;AAAA,QACA,KAAA,EAAO,WAAA;AAAA,QACP,2BAAA,EAA6B;AAAA,OACjC;AAAA,IACJ,CAAA;AAAA,IAEA,WAAA,GAAuB;AAGnB,MAAA,IAAI,gBAAgB,IAAA,EAAM;AACtB,QAAA,OAAO,KAAA;AAAA,MACX;AAEA,MAAA,OAAO,SAAA,IAAa,WAAA;AAAA,IACxB,CAAA;AAAA,IAEA,cAAA,GAAqC;AACjC,MAAA,OAAO,WAAA;AAAA,IACX,CAAA;AAAA,IAEA,KAAA,GAAc;AACV,MAAA,SAAA,GAAY,CAAA;AACZ,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,aAAA,GAAgB,KAAA;AAChB,MAAA,WAAA,GAAc,MAAA;AAAA,IAClB;AAAA,GACJ;AACJ;;;ACjPO,IAAM,SAAA,GAAY,OAAO,MAAA,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQnC,KAAA,EAAO,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASP,eAAA,EAAiB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjB,KAAA,EAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASP,sBAAA,EAAwB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUxB,aAAA,EAAe,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWf,YAAA,EAAc,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASd,eAAA,EAAiB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjB,kBAAA,EAAoB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUpB,OAAA,EAAS,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,WAAA,EAAa,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASb,YAAA,EAAc,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQd,UAAA,EAAY,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUZ,mBAAA,EAAqB;AACzB,CAAU;;;AC7EV,SAAS,QAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AAmCO,SAAS,qBAAA,CACZ,SAAA,EACA,YAAA,EACA,OAAA,EACA,aACA,WAAA,EACe;AACf,EAAA,OAAO;AAAA,IACH,MAAM,MAAM,OAAA,EAAgE;AAKxE,MAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAMA,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAA2B;AAAA,QACxD,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,WAAA;AAAA,QACN,IAAA,EAAM;AAAA,UACF,QAAQ,OAAA,CAAQ,MAAA;AAAA,UAChB,aAAa,OAAA,CAAQ,WAAA;AAAA,UACrB;AAAA,SACJ;AAAA,QACA,SAAS,SAAA,CAAU,KAAA;AAAA,QACnB,kBAAkB,kBAAA,CAAmB;AAAA,OACxC,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAGA,MAAA,OAAA,CAAQ,MAAA,CAAO,UAAU,KAAK,CAAA;AAK9B,MAAA,MAAM,GAAA,GAAM,QAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAIA,MAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAEA,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB,CAAA;AAAA,IAEA,OAAO,OAAO,OAAA,EAAuE;AAIjF,MAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAOA,MAAA,MAAM,SAAA,GAA4B;AAAA,QAC9B,IAAA,EAAM;AAAA,UACF,QAAQ,OAAA,CAAQ,MAAA;AAAA,UAChB,aAAa,OAAA,CAAQ,WAAA;AAAA,UACrB;AAAA,SACJ;AAAA,QACA;AAAA,OACJ;AAKA,MAAA,OAAA,CAAQ,MAAA,CAAO,UAAU,KAAK,CAAA;AAE9B,MAAA,OAAO,YAAA,CAAa,OAAO,SAAS,CAAA;AAAA,IACxC;AAAA,GACJ;AACJ;;;AChNA,IAAM,aAAA,GAAgB,CAAA;AAsDtB,SAASG,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA6BO,SAAS,qBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,OAAO;AAAA,IACH,MAAM,MAAA,CACF,KAAA,EACA,IAAA,GAAe,aAAA,EACsC;AAKrD,MAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAMA,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAgC;AAAA,QAC7D,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,qBAAA;AAAA,QACN,IAAA,EAAM,EAAE,KAAA,EAAO,IAAA,EAAK;AAAA,QACpB,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAGA,MAAA,OAAA,CAAQ,MAAA,CAAO,UAAU,eAAe,CAAA;AAKxC,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAIA,MAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,MAAA,MAAM,OAAA,GAA2C,IAAA,EAAM,OAAA,IAAW,EAAC;AAEnE,MAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,GAAA,EAAI;AAAA,IAChC;AAAA,GACJ;AACJ;;;ACxHA,SAASA,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA8BO,SAAS,sBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACgB;AAChB,EAAA,OAAO;AAAA,IACH,MAAM,MAAM,UAAA,EAA4D;AAIpE,MAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAKA,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAA0B;AAAA,QACvD,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,WAAA;AAAA,QACN,IAAA,EAAM,EAAE,UAAA,EAAW;AAAA,QACnB,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAGA,MAAA,OAAA,CAAQ,MAAA,CAAO,UAAU,KAAK,CAAA;AAK9B,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAGA,MAAA,MAAM,IAAA,GAAyB,SAAS,IAAA,IAAQ;AAAA,QAC5C,aAAa,EAAC;AAAA,QACd,QAAA,EAAU,EAAE,YAAA,EAAc,SAAA,EAAW,aAAa,CAAA;AAAE,OACxD;AAEA,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB,CAAA;AAAA,IAEA,MAAM,WACF,YAAA,EACiD;AAIjD,MAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAKA,MAAA,MAAM,SAAA,GAAY,YAAA,CAAa,MAAA,GAAS,SAAA,CAAU,sBAAA;AAKlD,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAqC;AAAA,QAClE,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,iBAAA;AAAA,QACN,IAAA,EAAM,EAAE,YAAA,EAAa;AAAA,QACrB,OAAA,EAAS;AAAA,OACZ,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAGA,MAAA,OAAA,CAAQ,OAAO,SAAS,CAAA;AAKxB,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAGA,MAAA,MAAM,IAAA,GAAoC,QAAA,CAAS,IAAA,IAAQ,EAAC;AAE5D,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB;AAAA,GACJ;AACJ;;;ACtIA,SAASA,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AAqCO,SAAS,yBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACmB;AAYnB,EAAA,eAAe,uBAAA,CACX,IAAA,EACA,KAAA,EACA,YAAA,EACqC;AAIrC,IAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,MAAA,MAAM,wBAAA,CAAyB;AAAA,QAC3B,IAAA,EAAM,WAAA;AAAA,QACN,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,IACL;AAOA,IAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAyB;AAAA,MACtD,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA;AAAA,MACA,IAAA,EAAM,KAAA;AAAA,MACN,OAAA,EAAS,YAAA;AAAA,MACT,kBAAkB,kBAAA,CAAmB;AAAA,KACxC,CAAA;AAKD,IAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,MAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,IAC/E;AAGA,IAAA,OAAA,CAAQ,OAAO,YAAY,CAAA;AAK3B,IAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,MACR,QAAA,CAAS,OAAA;AAAA,MACT,QAAA,CAAS,YAAA;AAAA,MACT,QAAA,CAAS,OAAA;AAAA,MACT;AAAA,KACJ;AAIA,IAAA,MAAM,IAAA,GAAwB,SAAS,IAAA,IAAQ;AAAA,MAC3C,MAAM,EAAC;AAAA,MACP,WAAW,KAAA,CAAM;AAAA,KACrB;AAEA,IAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,EACvB;AAEA,EAAA,OAAO;AAAA,IACH,MAAM,UAAU,KAAA,EAA8D;AAC1E,MAAA,OAAO,uBAAA;AAAA,QACH,sBAAA;AAAA,QACA,KAAA;AAAA,QACA,SAAA,CAAU;AAAA,OACd;AAAA,IACJ,CAAA;AAAA,IAEA,MAAM,kBAAkB,KAAA,EAA8D;AAClF,MAAA,OAAO,uBAAA;AAAA,QACH,qBAAA;AAAA,QACA,KAAA;AAAA,QACA,SAAA,CAAU;AAAA,OACd;AAAA,IACJ;AAAA,GACJ;AACJ;;;AC7LA,IAAM,qBAAA,GAAqE,OAAO,MAAA,CAAO;AAAA,EACrF,MAAM,MAAA,CAAO,MAAA,CAAO,EAAE,QAAA,EAAU,OAAO,CAAA;AAAA,EACvC,GAAA,EAAK;AACT,CAAC,CAAA;AA2DD,SAASA,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA+BO,SAAS,oBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACA,cACA,WAAA,EACc;AACd,EAAA,OAAO;AAAA,IACH,MAAM,YACF,KAAA,EACoD;AAOpD,MAAA,IAAI,CAAC,YAAA,EAAc;AACf,QAAA,OAAO,qBAAA;AAAA,MACX;AAQA,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,qBAAA,EAAuB;AACtC,QAAA,OAAO,qBAAA;AAAA,MACX;AAWA,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAA6B;AAAA,QAC1D,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,YAAA;AAAA,QACN,IAAA,EAAM,EAAE,KAAA,EAAO,WAAA,EAAY;AAAA,QAC3B,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAMD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAOA,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAEA,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,EAAM,QAAA,IAAY,IAAA;AAE5C,MAAA,OAAO,EAAE,IAAA,EAAM,EAAE,QAAA,IAAY,GAAA,EAAI;AAAA,IACrC;AAAA,GACJ;AACJ;;;AChJA,SAASA,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA8BO,SAAS,qBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACA,WAAA,EACe;AACf,EAAA,OAAO;AAAA,IACH,MAAM,aACF,MAAA,EACoD;AASpD,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAA8B;AAAA,QAC3D,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,aAAA;AAAA,QACN,IAAA,EAAM,EAAE,GAAG,MAAA,EAAQ,WAAA,EAAY;AAAA,QAC/B,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAOD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAOA,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAEA,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,EAAM,QAAA,IAAY,IAAA;AAE5C,MAAA,OAAO,EAAE,IAAA,EAAM,EAAE,QAAA,IAAY,GAAA,EAAI;AAAA,IACrC;AAAA,GACJ;AACJ;;;AC9HA,SAAS,iBACL,MAAA,EACM;AACN,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC/C,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACvC,MAAA,OAAA,CAAQ,IAAA;AAAA,QACJ,CAAA,EAAG,mBAAmB,GAAG,CAAC,IAAI,kBAAA,CAAmB,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AAAA,OACnE;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,OAAO,OAAA,CAAQ,SAAS,CAAA,GAAI,CAAA,CAAA,EAAI,QAAQ,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,GAAK,EAAA;AAC1D;AAcA,SAASA,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA8BO,SAAS,sBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACgB;AAChB,EAAA,OAAO;AAAA,IACH,MAAM,UACF,OAAA,EAC+B;AAS/B,MAAA,MAAM,cAAc,gBAAA,CAAiB;AAAA,QACjC,QAAQ,OAAA,EAAS,MAAA;AAAA,QACjB,OAAO,OAAA,EAAS,KAAA;AAAA,QAChB,gBAAgB,OAAA,EAAS,aAAA;AAAA,QACzB,WAAW,OAAA,EAAS;AAAA,OACvB,CAAA;AAOD,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAmB;AAAA,QAChD,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,aAAa,WAAW,CAAA,CAAA;AAAA,QAC9B,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAKA,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAEA,MAAA,MAAM,IAAA,GAAkB,SAAS,IAAA,IAAQ;AAAA,QACrC,OAAO,EAAC;AAAA,QACR,MAAA,EAAQ,IAAA;AAAA,QACR,OAAA,EAAS;AAAA,OACb;AAEA,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB;AAAA,GACJ;AACJ;;;AC5GA,SAASA,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA4BO,SAAS,kBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACY;AACZ,EAAA,OAAO;AAAA,IACH,MAAM,QAAQ,UAAA,EAAyD;AAMnE,MAAA,IAAI,OAAA,CAAQ,aAAY,EAAG;AACvB,QAAA,MAAM,wBAAA,CAAyB;AAAA,UAC3B,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACZ,CAAA;AAAA,MACL;AAOA,MAAA,MAAM,IAAA,GAAO,CAAA,cAAA,EAAiB,kBAAA,CAAmB,UAAU,CAAC,CAAA,QAAA,CAAA;AAO5D,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAyB;AAAA,QACtD,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA;AAAA,QACA,SAAS,SAAA,CAAU,OAAA;AAAA,QACnB,kBAAkB,kBAAA,CAAmB;AAAA,OACxC,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAGA,MAAA,OAAA,CAAQ,MAAA,CAAO,UAAU,OAAO,CAAA;AAKhC,MAAA,MAAM,GAAA,GAAMA,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAGA,MAAA,MAAM,IAAA,GAAsB,SAAS,IAAA,IAAQ;AAAA,QACzC,MAAA,EAAQ,SAAA;AAAA,QACR,OAAA,EAAS,CAAA,cAAA,EAAiB,kBAAA,CAAmB,UAAU,CAAC,CAAA;AAAA,OAC5D;AAEA,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB;AAAA,GACJ;AACJ;;;ACtIA,SAASC,kBACL,MAAA,EACM;AACN,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC/C,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACvC,MAAA,OAAA,CAAQ,IAAA;AAAA,QACJ,CAAA,EAAG,mBAAmB,GAAG,CAAC,IAAI,kBAAA,CAAmB,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AAAA,OACnE;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,OAAO,OAAA,CAAQ,SAAS,CAAA,GAAI,CAAA,CAAA,EAAI,QAAQ,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,GAAK,EAAA;AAC1D;AAcA,SAASD,SAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AAoCO,SAAS,sBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACgB;AAChB,EAAA,OAAO;AAAA,IACH,MAAM,UACF,OAAA,EACgC;AAQhC,MAAA,MAAM,cAAcC,iBAAAA,CAAiB;AAAA,QACjC,QAAQ,OAAA,EAAS,MAAA;AAAA,QACjB,OAAO,OAAA,EAAS;AAAA,OACnB,CAAA;AAOD,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAoB;AAAA,QACjD,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,mBAAmB,WAAW,CAAA,CAAA;AAAA,QACpC,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAKA,MAAA,MAAM,GAAA,GAAMD,SAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAEA,MAAA,MAAM,IAAA,GAAmB,SAAS,IAAA,IAAQ;AAAA,QACtC,OAAO,EAAC;AAAA,QACR,MAAA,EAAQ,IAAA;AAAA,QACR,OAAA,EAAS;AAAA,OACb;AAEA,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB;AAAA,GACJ;AACJ;;;ACtHA,SAASA,UAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AA4BO,SAAS,uBAAA,CACZ,SAAA,EACA,OAAA,EACA,WAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACH,MAAM,kBACF,SAAA,EACoD;AAUpD,MAAA,MAAM,IAAA,GAAO,CAAA,YAAA,EAAe,kBAAA,CAAmB,SAAS,CAAC,CAAA,KAAA,CAAA;AAQzD,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAA+B;AAAA,QAC5D,MAAA,EAAQ,QAAA;AAAA,QACR,IAAA;AAAA,QACA,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAMA,MAAA,MAAM,GAAA,GAAMA,UAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAEA,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,EAAM,QAAA,IAAY,IAAA;AAE5C,MAAA,OAAO,EAAE,IAAA,EAAM,EAAE,QAAA,IAAY,GAAA,EAAI;AAAA,IACrC;AAAA,GACJ;AACJ;;;AC3FA,IAAM,gBAAA,GAAmB,8BAAA;AAOzB,IAAM,oBAAA,GAAuB,UAAA;AAO7B,IAAM,oBAAA,GAAoC,KAAA;AAiC1C,SAASA,UAAAA,CACL,OAAA,EACA,YAAA,EACA,OAAA,EACA,WAAA,EACe;AACf,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,YAAA,KAAiB,MAAA,IAAa,YAAY,MAAA,EAAW;AAC9E,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,YAAA,EAAc,MAAM,OAAA,EAAQ;AAAA,EACnE;AAEA,EAAA,OAAO,IAAA;AACX;AAwDO,SAAS,8BAA8B,MAAA,EAA8C;AAKxF,EAAA,IAAI,CAAC,OAAO,MAAA,IAAU,MAAA,CAAO,OAAO,IAAA,EAAK,CAAE,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,kBAAkB,QAAQ,CAAA;AAAA,EACpC;AAMA,EAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,gBAAA;AAClC,EAAA,MAAM,WAAA,GAAc,OAAO,WAAA,IAAe,oBAAA;AAC1C,EAAA,MAAM,YAAA,GAAe,OAAO,YAAA,IAAgB,KAAA;AAC5C,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,MAAA,CAAO,UAAA,CAAW,oBAAoB,CAAA;AAMjE,EAAA,MAAM,YAAY,wBAAA,CAAyB;AAAA,IACvC,QAAA,EAAU,OAAA;AAAA,IACV,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,WAAW,MAAA,CAAO;AAAA,GACrB,CAAA;AAED,EAAA,MAAM,eAAe,uBAAA,CAAwB;AAAA,IACzC,QAAA,EAAU,OAAA;AAAA,IACV,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,WAAW,MAAA,CAAO;AAAA,GACrB,CAAA;AAMD,EAAA,MAAM,UAAU,gBAAA,EAAiB;AAMjC,EAAA,MAAM,UAAA,GAAa,qBAAA;AAAA,IACf,SAAA;AAAA,IAAW,YAAA;AAAA,IAAc,OAAA;AAAA,IAAS,WAAA;AAAA,IAAa;AAAA,GACnD;AAEA,EAAA,MAAM,UAAA,GAAa,qBAAA,CAAsB,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AACxE,EAAA,MAAM,WAAA,GAAc,sBAAA,CAAuB,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAC1E,EAAA,MAAM,cAAA,GAAiB,yBAAA,CAA0B,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAEhF,EAAA,MAAM,cAAA,GAAiB,oBAAA;AAAA,IACnB,SAAA;AAAA,IAAW,OAAA;AAAA,IAAS,WAAA;AAAA,IAAa,YAAA;AAAA,IAAc;AAAA,GACnD;AAEA,EAAA,MAAM,eAAA,GAAkB,qBAAA;AAAA,IACpB,SAAA;AAAA,IAAW,OAAA;AAAA,IAAS,WAAA;AAAA,IAAa;AAAA,GACrC;AAEA,EAAA,MAAM,gBAAA,GAAmB,sBAAA,CAAuB,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAC/E,EAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AACvE,EAAA,MAAM,gBAAA,GAAmB,sBAAA,CAAuB,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAC/E,EAAA,MAAM,iBAAA,GAAoB,uBAAA,CAAwB,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAOjF,EAAA,IAAI,QAAA,GAAW,KAAA;AAOf,EAAA,SAAS,iBAAA,GAA0B;AAC/B,IAAA,IAAI,QAAA,EAAU;AACV,MAAA,MAAM,mBAAA,EAAoB;AAAA,IAC9B;AAAA,EACJ;AAQA,EAAA,SAAS,mBAAmB,MAAA,EAAsB;AAC9C,IAAA,IAAI,WAAA,EAAa;AACb,MAAA,MAAM,yBAAyB,MAAM,CAAA;AAAA,IACzC;AAAA,EACJ;AASA,EAAA,MAAM,QAAQ,MAAA,CAAO,MAAA;AAAA,IACjB,OAAO,OAAA,KAAmE;AACtE,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,MAAA,OAAO,UAAA,CAAW,MAAM,OAAO,CAAA;AAAA,IACnC,CAAA;AAAA,IACA;AAAA,MACI,OAAO,OAAA,EAAuE;AAC1E,QAAA,iBAAA,EAAkB;AAClB,QAAA,kBAAA,CAAmB,cAAc,CAAA;AACjC,QAAA,OAAO,UAAA,CAAW,OAAO,OAAO,CAAA;AAAA,MACpC;AAAA;AACJ,GACJ;AAEA,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA,IAIH,KAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,MAAA,CAAO,KAAA,EAAO,IAAA,EAAM;AACtB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,QAAQ,CAAA;AAC3B,MAAA,OAAO,UAAA,CAAW,MAAA,CAAO,KAAA,EAAO,IAAI,CAAA;AAAA,IACxC,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,MAAM,UAAA,EAAY;AACpB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,MAAA,OAAO,WAAA,CAAY,MAAM,UAAU,CAAA;AAAA,IACvC,CAAA;AAAA,IAEA,MAAM,WAAW,YAAA,EAAc;AAC3B,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,YAAY,CAAA;AAC/B,MAAA,OAAO,WAAA,CAAY,WAAW,YAAY,CAAA;AAAA,IAC9C,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,aAAa,MAAA,EAAQ;AACvB,MAAA,iBAAA,EAAkB;AAElB,MAAA,OAAO,eAAA,CAAgB,aAAa,MAAM,CAAA;AAAA,IAC9C,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,YAAY,KAAA,EAAO;AACrB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,aAAa,CAAA;AAChC,MAAA,OAAO,cAAA,CAAe,YAAY,KAAK,CAAA;AAAA,IAC3C,CAAA;AAAA,IAEA,MAAM,UAAU,OAAA,EAAS;AACrB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,WAAW,CAAA;AAC9B,MAAA,OAAO,gBAAA,CAAiB,UAAU,OAAO,CAAA;AAAA,IAC7C,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,UAAU,KAAA,EAAO;AACnB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,WAAW,CAAA;AAC9B,MAAA,OAAO,cAAA,CAAe,UAAU,KAAK,CAAA;AAAA,IACzC,CAAA;AAAA,IAEA,MAAM,kBAAkB,KAAA,EAAO;AAC3B,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,mBAAmB,CAAA;AACtC,MAAA,OAAO,cAAA,CAAe,kBAAkB,KAAK,CAAA;AAAA,IACjD,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,QAAA,GAAW;AACb,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,UAAU,CAAA;AAO7B,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,CAAuB;AAAA,QACpD,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,WAAA;AAAA,QACN,SAAS,SAAA,CAAU;AAAA,OACtB,CAAA;AAKD,MAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,iBAAiB,MAAA,EAAW;AACvE,QAAA,OAAA,CAAQ,UAAU,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,YAAA,EAAc,SAAS,OAAO,CAAA;AAAA,MAC/E;AAKA,MAAA,MAAM,GAAA,GAAMA,UAAAA;AAAA,QACR,QAAA,CAAS,OAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT;AAAA,OACJ;AAIA,MAAA,MAAM,IAAA,GAAmB,QAAA,CAAS,IAAA,KAAS,IAAA,GACrC;AAAA,QACE,IAAA,EAAM,SAAS,IAAA,CAAK,IAAA;AAAA,QACpB,KAAA,EAAO,SAAS,IAAA,CAAK,KAAA;AAAA,QACrB,IAAA,EAAM,SAAS,IAAA,CAAK;AAAA,UAEtB,EAAE,IAAA,EAAM,GAAG,KAAA,EAAO,CAAA,EAAG,MAAM,SAAA,EAAU;AAE3C,MAAA,OAAO,EAAE,MAAM,GAAA,EAAI;AAAA,IACvB,CAAA;AAAA,IAEA,MAAM,UAAU,OAAA,EAAS;AACrB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,WAAW,CAAA;AAC9B,MAAA,OAAO,gBAAA,CAAiB,UAAU,OAAO,CAAA;AAAA,IAC7C,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,QAAQ,UAAA,EAAY;AACtB,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,SAAS,CAAA;AAC5B,MAAA,OAAO,YAAA,CAAa,QAAQ,UAAU,CAAA;AAAA,IAC1C,CAAA;AAAA,IAEA,MAAM,kBAAkB,SAAA,EAAW;AAC/B,MAAA,iBAAA,EAAkB;AAClB,MAAA,kBAAA,CAAmB,mBAAmB,CAAA;AACtC,MAAA,OAAO,iBAAA,CAAkB,kBAAkB,SAAS,CAAA;AAAA,IACxD,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,OAAA,GAAgB;AAGZ,MAAA,QAAA,GAAW,IAAA;AAAA,IACf;AAAA,GACJ;AACJ","file":"index.js","sourcesContent":["/**\n * @module @enterstellar-ai/cloud/errors\n * @description Cloud SDK error class and deterministic factory functions.\n *\n * `CloudError` extends `EnterstellarError` (from `@enterstellar-ai/types`) with Cloud-specific\n * metadata: the server's error code (`cloudCode`), an optional upgrade URL\n * for quota-exceeded scenarios, a retry-after duration, and the server's\n * request ID for correlation.\n *\n * **Error code taxonomy:**\n *\n * | Namespace | Origin | Examples |\n * |:----------------|:-----------------|:-----------------------------------|\n * | `ENS-5xxx` | SDK-originated | `ENS-5001` (config), `ENS-5004` |\n * | `ENS-C{NNNN}` | Server-originated| `ENS-C4290` (quota exceeded) |\n *\n * SDK-originated errors use `EnterstellarErrorCode` values directly (`ENS-5001`–\n * `ENS-5005`). Server-originated errors store the cloud code in the\n * `cloudCode` field and use `ENS-5003` as the base `EnterstellarError.code`\n * (since `ENS-C{NNNN}` is not in the `EnterstellarErrorCode` union).\n *\n * Six factory functions provide deterministic, documented construction\n * for every known error scenario — consumers never call `new CloudError()`\n * directly.\n *\n * @see Design Choice SD3 — throw `CloudError` on 429 with `upgradeUrl`.\n * @see Design Choice AG10 — `ENS-C{NNNN}` server error code format.\n * @see Bible §9.4 — Cloud error response shape.\n */\n\nimport { EnterstellarError } from '@enterstellar-ai/types';\n\nimport type { EnterstellarErrorCode } from '@enterstellar-ai/types';\n\n// ---------------------------------------------------------------------------\n// CloudErrorBody — parsed from server error responses (§9.4)\n// ---------------------------------------------------------------------------\n\n/**\n * The structured error body returned by Enterstellar Cloud API endpoints.\n *\n * Parsed from the JSON response on non-2xx status codes. The `code` field\n * uses the `ENS-C{NNNN}` format (AG10). Optional fields `retryAfterMs`\n * and `upgradeUrl` are present only on `ENS-C4290` (quota exceeded).\n *\n * @example\n * ```json\n * {\n * \"code\": \"ENS-C4290\",\n * \"message\": \"IPU quota exceeded\",\n * \"retryAfterMs\": 3600000,\n * \"upgradeUrl\": \"https://cloud.enterstellar.dev/billing/upgrade\"\n * }\n * ```\n *\n * @see Bible §9.4 — Error response shape.\n * @see Design Choice AG10 — `ENS-C{NNNN}` code format.\n */\nexport type CloudErrorBody = {\n readonly code: string;\n readonly message: string;\n readonly retryAfterMs?: number | undefined;\n readonly upgradeUrl?: string | undefined;\n};\n\n// ---------------------------------------------------------------------------\n// CloudError Class\n// ---------------------------------------------------------------------------\n\n/**\n * Cloud SDK error — extends `EnterstellarError` with Cloud-specific metadata.\n *\n * All `@enterstellar-ai/cloud` SDK errors are instances of both `CloudError` and\n * `EnterstellarError`. Consumer catch blocks can narrow on either:\n *\n * ```ts\n * try {\n * await client.forge({ intent: 'patient vitals' });\n * } catch (error) {\n * if (error instanceof CloudError && error.upgradeUrl) {\n * showUpgradePrompt(error.upgradeUrl);\n * }\n * }\n * ```\n *\n * **Do not construct directly** — use the factory functions below.\n *\n * @see Design Choice SD3 — throw on 429 with `upgradeUrl` + `retryAfterMs`.\n */\nexport class CloudError extends EnterstellarError {\n /**\n * The Cloud-specific error code.\n *\n * For SDK-originated errors, this mirrors `EnterstellarError.code` (e.g., `'ENS-5001'`).\n * For server-originated errors, this is the `ENS-C{NNNN}` code from the\n * response body (e.g., `'ENS-C4290'`).\n */\n public readonly cloudCode: string;\n\n /**\n * URL for the billing upgrade page.\n * Present only on `ENS-C4290` (IPU quota exceeded) errors.\n *\n * @see Design Choice SD3 — app decides how to surface the upgrade prompt.\n */\n public readonly upgradeUrl: string | undefined;\n\n /**\n * Milliseconds until the quota resets or rate limit expires.\n * Present only on `ENS-C4290` errors.\n *\n * Use this to schedule a retry or display a countdown to the user.\n */\n public readonly retryAfterMs: number | undefined;\n\n /**\n * The `X-Request-Id` header value from the server response.\n * A bare ULID (AG16) for support ticket correlation.\n * `undefined` if the error occurred before a server response was received\n * (e.g., network failure, config validation).\n */\n public readonly requestId: string | undefined;\n\n /**\n * @internal Use factory functions instead of constructing directly.\n *\n * @param code - The `EnterstellarErrorCode` for the base `EnterstellarError` class.\n * @param cloudCode - The Cloud-specific error code (`ENS-5xxx` or `ENS-C{NNNN}`).\n * @param message - Human-readable error description.\n * @param recoverable - Whether the caller can meaningfully retry.\n * @param options - Optional Cloud-specific metadata.\n */\n constructor(\n code: EnterstellarErrorCode,\n cloudCode: string,\n message: string,\n recoverable: boolean,\n options?: {\n readonly upgradeUrl?: string | undefined;\n readonly retryAfterMs?: number | undefined;\n readonly requestId?: string | undefined;\n readonly cause?: unknown;\n },\n ) {\n super(code, 'cloud', message, recoverable, options?.cause);\n this.name = 'CloudError';\n this.cloudCode = cloudCode;\n this.upgradeUrl = options?.upgradeUrl;\n this.retryAfterMs = options?.retryAfterMs;\n this.requestId = options?.requestId;\n\n // Preserve proper stack trace in V8 environments.\n // Points the stack to the factory function call site, not the constructor.\n if ('captureStackTrace' in Error) {\n (Error as unknown as {\n captureStackTrace: (\n target: object,\n ctor: (...args: unknown[]) => unknown,\n ) => void;\n }).captureStackTrace(\n this,\n CloudError as unknown as (...args: unknown[]) => unknown,\n );\n }\n }\n\n /**\n * Serializes the error to a plain object for logging, telemetry, or DevTools.\n *\n * Extends `EnterstellarError.toJSON()` with Cloud-specific fields.\n *\n * @returns A plain object representation including all Cloud metadata.\n */\n public override toJSON(): {\n name: string;\n code: EnterstellarErrorCode;\n cloudCode: string;\n module: 'cloud';\n message: string;\n recoverable: boolean;\n timestamp: string;\n upgradeUrl: string | undefined;\n retryAfterMs: number | undefined;\n requestId: string | undefined;\n stack: string | undefined;\n } {\n return {\n ...super.toJSON(),\n name: this.name,\n cloudCode: this.cloudCode,\n module: 'cloud',\n upgradeUrl: this.upgradeUrl,\n retryAfterMs: this.retryAfterMs,\n requestId: this.requestId,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory Functions — SDK-Originated Errors (ENS-5xxx)\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a `CloudError` for invalid or missing configuration.\n *\n * Thrown during `createEnterstellarCloudClient()` when a required config field\n * is empty, missing, or malformed. This is a developer error — non-recoverable.\n *\n * @param field - The config field that failed validation (e.g., `'apiKey'`).\n * @returns A non-recoverable `CloudError` with code `ENS-5001`.\n *\n * @example\n * ```ts\n * throw createConfigError('apiKey');\n * // → CloudError { code: 'ENS-5001', message: '@enterstellar-ai/cloud: Invalid config — \"apiKey\" ...' }\n * ```\n */\nexport function createConfigError(field: string): CloudError {\n return new CloudError(\n 'ENS-5001',\n 'ENS-5001',\n `@enterstellar-ai/cloud: Invalid config — \"${field}\" is required and must be a non-empty string.`,\n false,\n );\n}\n\n/**\n * Creates a `CloudError` for method calls after `dispose()`.\n *\n * Thrown when any method on `EnterstellarCloudClient` is called after the client\n * has been disposed. This is a developer error — non-recoverable.\n * `dispose()` itself is idempotent and never throws.\n *\n * @returns A non-recoverable `CloudError` with code `ENS-5002`.\n *\n * @example\n * ```ts\n * client.dispose();\n * await client.forge({ intent: 'card' });\n * // → throws CloudError { code: 'ENS-5002' }\n * ```\n */\nexport function createDisposedError(): CloudError {\n return new CloudError(\n 'ENS-5002',\n 'ENS-5002',\n '@enterstellar-ai/cloud: Client has been disposed. Create a new client with createEnterstellarCloudClient().',\n false,\n );\n}\n\n/**\n * Creates a `CloudError` for failed usage/operational queries.\n *\n * Thrown when a server request fails with a non-429, non-5xx error that\n * does not trigger the retry loop. Recoverable — the caller can retry\n * or fall back to cached data.\n *\n * @param statusCode - The HTTP status code returned by the server, or\n * `undefined` if the request never reached the server (e.g., DNS failure).\n * @param requestId - The `X-Request-Id` from the response, if available.\n * @returns A recoverable `CloudError` with code `ENS-5003`.\n */\nexport function createUsageFetchError(\n statusCode: number | undefined,\n requestId?: string,\n): CloudError {\n const statusSuffix = statusCode !== undefined\n ? ` (HTTP ${String(statusCode)})`\n : ' (no response)';\n\n return new CloudError(\n 'ENS-5003',\n 'ENS-5003',\n `@enterstellar-ai/cloud: Usage query failed${statusSuffix}.`,\n true,\n { requestId },\n );\n}\n\n/**\n * Creates a `CloudError` for non-signal method calls in anonymous mode.\n *\n * Thrown when a method other than `submitSignal()` or `dispose()` is called\n * on a client initialized with a `pk_anon_*` API key (SD1). Anonymous mode\n * only supports signal submission — all other operations require a project\n * API key (`ak_*`).\n *\n * @param method - The method name that was called (e.g., `'forge'`, `'search'`).\n * @returns A non-recoverable `CloudError` with code `ENS-5004`.\n *\n * @see Design Choice SD1 — auto-detect `pk_anon` prefix → anonymous mode.\n *\n * @example\n * ```ts\n * const client = createEnterstellarCloudClient({ apiKey: 'pk_anon_abc123' });\n * await client.forge({ intent: 'card' });\n * // → throws CloudError { code: 'ENS-5004', message: '...forge()...' }\n * ```\n */\nexport function createAnonymousModeError(method: string): CloudError {\n return new CloudError(\n 'ENS-5004',\n 'ENS-5004',\n `@enterstellar-ai/cloud: ${method}() is not available in anonymous mode. `\n + 'Anonymous clients (pk_anon_*) can only call submitSignal(). '\n + 'Use a project API key (ak_*) for full access.',\n false,\n );\n}\n\n/**\n * Creates a `CloudError` for exhausted retries.\n *\n * Thrown after all 3 retry attempts fail for 5xx or network errors (SD5).\n * Recoverable — the caller may want to retry later, queue the request,\n * or fall back to a local alternative.\n *\n * @param attempts - The total number of attempts made (always 3 per SD5).\n * @param lastStatusCode - The HTTP status code from the last attempt, or\n * `undefined` if the last failure was a network error.\n * @param requestId - The `X-Request-Id` from the last response, if available.\n * @returns A recoverable `CloudError` with code `ENS-5005`.\n *\n * @see Design Choice SD5 — blanket exponential backoff, 3 retries.\n */\nexport function createRetriesExhaustedError(\n attempts: number,\n lastStatusCode?: number,\n requestId?: string,\n): CloudError {\n const statusSuffix = lastStatusCode !== undefined\n ? ` Last status: ${String(lastStatusCode)}.`\n : ' Last error: network failure.';\n\n return new CloudError(\n 'ENS-5005',\n 'ENS-5005',\n `@enterstellar-ai/cloud: All ${String(attempts)} retry attempts failed.${statusSuffix}`,\n true,\n { requestId },\n );\n}\n\n// ---------------------------------------------------------------------------\n// Factory Function — Server-Originated Error (ENS-C{NNNN})\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a `CloudError` from a server `ENS-C4290` (quota exceeded) response.\n *\n * This is the primary billing error (SD3). The server returns a structured\n * body with `upgradeUrl` (link to the billing page) and `retryAfterMs`\n * (milliseconds until quota resets). The SDK throws this instead of silently\n * degrading — the application's error boundary decides what to do.\n *\n * The `retryAfterMs` field differentiates quota exhaustion (hours) from\n * rate limiting (seconds). The `upgradeUrl` is present only for quota\n * exhaustion.\n *\n * @param body - The parsed error body from the server response (§9.4).\n * @param requestId - The `X-Request-Id` from the response headers.\n * @returns A recoverable `CloudError` with the server's `ENS-C4290` code.\n *\n * @see Design Choice SD3 — throw with `upgradeUrl` and `retryAfterMs`.\n * @see Bible §9.4 — error response shape.\n *\n * @example\n * ```ts\n * // Server response:\n * // { \"error\": { \"code\": \"ENS-C4290\", \"message\": \"IPU quota exceeded\",\n * // \"retryAfterMs\": 3600000, \"upgradeUrl\": \"https://...\" } }\n * throw createQuotaExceededError(parsedBody, '01HYX...');\n * ```\n */\nexport function createQuotaExceededError(\n body: CloudErrorBody,\n requestId?: string,\n): CloudError {\n return new CloudError(\n 'ENS-5003', // Base EnterstellarErrorCode — server errors map to ENS-5003\n body.code, // Cloud code — 'ENS-C4290' (or 'ENS-C4291' if server sends it)\n `@enterstellar-ai/cloud: ${body.message}`,\n true, // Recoverable — caller can upgrade tier or wait for reset\n {\n upgradeUrl: body.upgradeUrl,\n retryAfterMs: body.retryAfterMs,\n requestId,\n },\n );\n}\n","/**\n * @module @enterstellar-ai/cloud/version\n * @description SDK version constant for `@enterstellar-ai/cloud`.\n *\n * Used by the transport layer to set the `User-Agent` header on all\n * outgoing requests (`User-Agent: enterstellar-cloud-sdk/{CLOUD_SDK_VERSION}`),\n * and available to consumers for diagnostic logging.\n *\n * This value MUST match the `version` field in `packages/cloud/package.json`.\n * Updates are managed exclusively via Changesets (X4) — never manually.\n *\n * @see Enterstellar OS version constant convention\n * @see Design Choice SD9 — zero framework deps, `User-Agent` is the only\n * SDK-identifying header.\n */\n\n// ---------------------------------------------------------------------------\n// SDK Version\n// ---------------------------------------------------------------------------\n\n/**\n * The current version of the `@enterstellar-ai/cloud` SDK.\n *\n * @remarks\n * - Follows semver (major.minor.patch).\n * - Must stay in sync with `packages/cloud/package.json#version`.\n * - Typed as a string literal via `as const` for compile-time narrowing.\n * - Consumed by {@link CloudHttpTransport} for the `User-Agent` header.\n *\n * @example\n * ```ts\n * import { CLOUD_SDK_VERSION } from '@enterstellar-ai/cloud';\n *\n * console.log(`Running enterstellar-cloud-sdk v${CLOUD_SDK_VERSION}`);\n * // → \"Running enterstellar-cloud-sdk v0.1.0\"\n * ```\n */\nexport const CLOUD_SDK_VERSION = '0.1.0' as const;\n","/**\n * @module @enterstellar-ai/cloud/transport/idempotency\n * @description Hand-rolled ULID generator for `X-Idempotency-Key` headers.\n *\n * Enterstellar Cloud requires an `X-Idempotency-Key` header on all IPU-consuming\n * requests (AM10). The server uses this key with `INSERT OR IGNORE` on\n * `(idempotency_key, project_id)` to prevent double-deduction when the\n * SDK retries failed requests (SD5).\n *\n * The key format is a standard ULID: 10 chars (48-bit ms timestamp) +\n * 16 chars (80-bit randomness) = 26 chars, Crockford Base32 encoded.\n *\n * **Why hand-rolled:** SD9 mandates zero external dependencies beyond\n * `eventsource-parser`. A ULID generator is < 30 LOC with the Web Crypto\n * API, which is available in all target runtimes (Node 19+, browsers,\n * Cloudflare Workers, Deno).\n *\n * **Monotonicity note:** Unlike a full ULID library, this implementation\n * does NOT guarantee monotonic ordering within the same millisecond.\n * Idempotency keys only need uniqueness, not strict ordering — the\n * timestamp prefix provides sufficient sortability for server-side\n * debugging and ledger queries.\n *\n * @see Design Choice AM10 — `X-Idempotency-Key` on all IPU-consuming requests.\n * @see Design Choice SD5 — blanket retry makes idempotency mandatory.\n * @see Design Choice AG16 — bare ULIDs for request IDs (same format).\n * @see Design Choice SD9 — zero external deps beyond `eventsource-parser`.\n */\n\n// ---------------------------------------------------------------------------\n// Crockford Base32 Encoding Table\n// ---------------------------------------------------------------------------\n\n/**\n * Crockford's Base32 alphabet (excludes I, L, O, U to avoid ambiguity).\n * Used by the ULID specification for human-friendly, case-insensitive encoding.\n *\n * @see https://www.crockford.com/base32.html\n */\nconst CROCKFORD_BASE32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ' as const;\n\n// ---------------------------------------------------------------------------\n// Timestamp Encoding\n// ---------------------------------------------------------------------------\n\n/**\n * Encodes a millisecond Unix timestamp into a 10-character Crockford Base32\n * string (ULID timestamp component).\n *\n * The ULID spec allocates 48 bits for the timestamp, which supports dates\n * up to the year 10889. The encoding produces exactly 10 characters by\n * extracting 5-bit chunks from the 48-bit value (most-significant first).\n *\n * @param timeMs - Unix timestamp in milliseconds (e.g., `Date.now()`).\n * @returns A 10-character Crockford Base32 string.\n *\n * @internal\n */\nfunction encodeTimestamp(timeMs: number): string {\n let remaining = timeMs;\n const chars: string[] = new Array<string>(10);\n\n // Extract 10 characters (5 bits each = 50 bits, but only 48 are used).\n // Work from the least significant end to preserve leading zeros.\n for (let i = 9; i >= 0; i--) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- CROCKFORD_BASE32 has 32 entries, index is always 0-31\n chars[i] = CROCKFORD_BASE32[remaining & 0x1f]!;\n remaining = Math.floor(remaining / 32);\n }\n\n return chars.join('');\n}\n\n// ---------------------------------------------------------------------------\n// Randomness Encoding\n// ---------------------------------------------------------------------------\n\n/**\n * Generates a 16-character Crockford Base32 string from 80 bits of\n * cryptographic randomness (ULID randomness component).\n *\n * Uses `crypto.getRandomValues()` for cryptographically secure random\n * bytes. 10 bytes = 80 bits → 16 Base32 characters (5 bits per char).\n *\n * @returns A 16-character Crockford Base32 string.\n *\n * @internal\n */\nfunction encodeRandomness(): string {\n const bytes = new Uint8Array(10);\n crypto.getRandomValues(bytes);\n\n const chars: string[] = new Array<string>(16);\n\n // Convert 10 bytes (80 bits) into 16 Base32 characters (5 bits each).\n // Process bytes as a big-endian bitstream, extracting 5-bit chunks.\n // We use a sliding window approach over the byte array.\n let bitBuffer = 0;\n let bitsInBuffer = 0;\n let charIndex = 0;\n\n for (let byteIndex = 0; byteIndex < 10; byteIndex++) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- byteIndex is bounded by loop\n bitBuffer = (bitBuffer << 8) | bytes[byteIndex]!;\n bitsInBuffer += 8;\n\n while (bitsInBuffer >= 5) {\n bitsInBuffer -= 5;\n const fiveBitValue = (bitBuffer >>> bitsInBuffer) & 0x1f;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- fiveBitValue is always 0-31\n chars[charIndex] = CROCKFORD_BASE32[fiveBitValue]!;\n charIndex++;\n }\n }\n\n return chars.join('');\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Generates a ULID-format string for use as an `X-Idempotency-Key` header value.\n *\n * Produces a 26-character string: 10 chars (timestamp) + 16 chars (randomness),\n * encoded in Crockford Base32. The timestamp prefix enables chronological\n * sorting for server-side debugging. The randomness suffix guarantees\n * uniqueness across concurrent requests.\n *\n * **Collision probability:** With 80 bits of randomness per key, the\n * probability of collision is ~1 in 2^80 (effectively zero for any\n * practical request volume).\n *\n * @returns A 26-character Crockford Base32 ULID string.\n *\n * @example\n * ```ts\n * import { generateIdempotencyKey } from './transport/idempotency.js';\n *\n * const key = generateIdempotencyKey();\n * // → '01HYX4K8R3ABCDEFGHJKMNPQRS' (26 chars)\n *\n * headers['X-Idempotency-Key'] = key;\n * ```\n *\n * @see Design Choice AM10 — universal idempotency on IPU-consuming requests.\n */\nexport function generateIdempotencyKey(): string {\n return encodeTimestamp(Date.now()) + encodeRandomness();\n}\n","/**\n * @module @enterstellar-ai/cloud/transport/cloud-http\n * @description Shared HTTP transport for all Enterstellar Cloud API calls.\n *\n * Provides a factory function that creates a typed HTTP client for\n * communicating with `api.enterstellar.dev`. All cloud proxy modules\n * (`cloud-forge-proxy`, `cloud-index-proxy`, `trace-submitter`,\n * `cloud-router-proxy`, `cloud-analytics-proxy`, etc.) route through\n * this transport.\n *\n * **Key responsibilities (v0.1.0):**\n * - `Authorization: Bearer {apiKey}` on every request (CL4).\n * - `User-Agent: enterstellar-cloud-sdk/{version}` on every request (F22).\n * - `X-Idempotency-Key: {ulid}` on IPU-consuming requests (AM10/F8).\n * - `AbortController` timeout per request, with per-operation defaults (F21).\n * - 3-attempt retry loop with exponential backoff (1s, 2s, 4s) for\n * 5xx/network errors (SD5). Same idempotency key on all retries.\n * - Parses `X-IPU-Used`, `X-IPU-Remaining`, `X-IPU-Cost`, and\n * `X-Request-Id` from response headers (§9.3).\n * - **Throws** `CloudError` on 429 (SD3), 4xx, and retries exhausted.\n *\n * **Changes from v0.0.x:**\n * - `degraded` pattern removed — operational errors now throw (SD3).\n * - Retry loop added (SD5) — 3 attempts for 5xx/network.\n * - `X-Idempotency-Key` added (AM10) — only when `ipuCost > 0`.\n * - `User-Agent` header added (F22).\n * - `X-IPU-Cost` and `X-Request-Id` parsing added (§9.3).\n * - Error body parsing added (§9.4).\n * - Per-operation timeout defaults added (F21).\n * - Instance-level `retryAttempt` state removed — retry is per-request.\n *\n * @see Design Choice SD3 — throw on 429 with `upgradeUrl` + `retryAfterMs`.\n * @see Design Choice SD5 — blanket 3× exponential backoff for 5xx/network.\n * @see Design Choice AM10 — `X-Idempotency-Key` on IPU-consuming requests.\n * @see Design Choice CL1 — hybrid IPU metering with `X-IPU-*` headers.\n * @see Design Choice CL4 — bearer token auth.\n * @see Bible §9.3 — response header format.\n * @see Bible §9.4 — error response shape.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { CloudErrorBody } from '../errors.js';\nimport type { CloudRequestConfig, CloudResponse } from '../types.js';\n\nimport {\n CloudError,\n createQuotaExceededError,\n createRetriesExhaustedError,\n} from '../errors.js';\nimport { CLOUD_SDK_VERSION } from '../version.js';\nimport { generateIdempotencyKey } from './idempotency.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Default HTTP request timeout in milliseconds.\n * Used when neither `CloudConfig.timeoutMs` nor `CloudRequestConfig.operationTimeout`\n * is specified. 10s is sufficient for most endpoints (GET /v1/usage, etc.).\n */\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\n/**\n * Maximum number of request attempts before throwing `ENS-5005`.\n * Attempt 1 = initial request. Attempts 2 and 3 = retries.\n *\n * @see Design Choice SD5 — 3 attempts total, exponential backoff.\n */\nconst MAX_ATTEMPTS = 3;\n\n/**\n * Exponential backoff delays between retry attempts in milliseconds.\n * Index 0 = delay after attempt 1 fails. Index 1 = delay after attempt 2 fails.\n *\n * @see Design Choice SD5 — 1s, 2s, 4s backoff schedule.\n */\nconst RETRY_BACKOFF_MS: readonly number[] = [1_000, 2_000, 4_000];\n\n/**\n * Per-operation timeout defaults in milliseconds.\n *\n * These are used when `CloudConfig.timeoutMs` is not set (global override).\n * Each proxy module passes its operation-specific timeout via\n * `CloudRequestConfig.operationTimeout`. If neither is set,\n * `DEFAULT_TIMEOUT_MS` (10s) is used.\n *\n * @see Audit Finding F21 — per-operation timeout defaults.\n */\nexport const OPERATION_TIMEOUTS = Object.freeze({\n /** Forge P99 = 10s (§8.9), 3× safety margin. */\n forge: 30_000,\n\n /** CR5: max 60s runtime + network/queue overhead. */\n certify: 90_000,\n\n /** OLAP queries can be slow depending on data volume. */\n analytics: 30_000,\n\n /** Business analytics — same profile as trace analytics. */\n businessAnalytics: 30_000,\n\n /** Default for all other operations. */\n default: 10_000,\n} as const);\n\n// ---------------------------------------------------------------------------\n// CloudHttpConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for the cloud HTTP transport.\n *\n * Constructed by `createEnterstellarCloudClient()` from the public `CloudConfig`.\n * Not exported from the barrel — internal to `@enterstellar-ai/cloud`.\n *\n * @internal\n */\nexport type CloudHttpConfig = {\n /**\n * Base URL of the Enterstellar Cloud API.\n * Path segments (`/v1/forge`, etc.) are appended by proxy modules.\n *\n * @example 'https://api.enterstellar.dev'\n */\n readonly endpoint: string;\n\n /** Bearer token for `Authorization` header (CL4). */\n readonly apiKey: string;\n\n /**\n * Global HTTP request timeout in milliseconds.\n * When set, overrides ALL per-operation timeout defaults.\n * When `undefined`, each request uses its own `operationTimeout`.\n *\n * @see Audit Finding F21 — per-operation timeout defaults.\n */\n readonly timeoutMs?: number | undefined;\n};\n\n// ---------------------------------------------------------------------------\n// CloudHttpTransport Interface\n// ---------------------------------------------------------------------------\n\n/**\n * The HTTP transport interface returned by {@link createCloudHttpTransport}.\n *\n * Provides a single `request()` method for all cloud API calls.\n * Each call returns a structured {@link CloudResponse} on success,\n * or throws {@link CloudError} on failure (SD3, SD5).\n *\n * **Error policy (v0.1.0):**\n * - 2xx → return `CloudResponse<T>`.\n * - 429 → throw `CloudError` with `upgradeUrl`/`retryAfterMs` (SD3).\n * - 4xx (non-429) → throw `CloudError` immediately, no retry.\n * - 5xx → retry up to 3× (SD5). If all fail, throw `ENS-5005`.\n * - Network error → retry up to 3× (SD5). If all fail, throw `ENS-5005`.\n *\n * @internal — consumed by proxy modules, not exported publicly.\n */\nexport interface CloudHttpTransport {\n /**\n * Execute an HTTP request against the Enterstellar Cloud API.\n *\n * @typeParam T - Expected JSON body type on success.\n * @param config - Request configuration (method, path, body, ipuCost, operationTimeout).\n * @returns Structured response with parsed headers on 2xx.\n *\n * @throws {CloudError} `ENS-C4290` on 429 (quota exceeded).\n * @throws {CloudError} Parsed server error on 4xx (non-429).\n * @throws {CloudError} `ENS-5005` after 3 failed retry attempts.\n */\n request<T>(config: CloudRequestConfig): Promise<CloudResponse<T>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parses a numeric header value from the response.\n *\n * Returns `undefined` if the header is absent, empty, or non-numeric.\n * Used for `X-IPU-Used`, `X-IPU-Remaining`, and `X-IPU-Cost` (§9.3).\n *\n * @param headers - The response `Headers` object.\n * @param name - Header name to parse.\n * @returns Parsed non-negative number, or `undefined` if absent/invalid.\n */\nfunction parseNumericHeader(headers: Headers, name: string): number | undefined {\n const raw = headers.get(name);\n if (raw === null || raw.trim().length === 0) {\n return undefined;\n }\n\n const value = Number(raw);\n if (Number.isFinite(value) && value >= 0) {\n return value;\n }\n\n return undefined;\n}\n\n/**\n * Safely parses a JSON response body.\n *\n * Returns `null` if the body cannot be parsed (empty, malformed,\n * or non-JSON content type). Never throws.\n *\n * @typeParam T - Expected parsed type.\n * @param response - The `Response` object from `fetch()`.\n * @returns Parsed body as `T`, or `null` on failure.\n */\nasync function safeParseJson<T>(response: Response): Promise<T | null> {\n try {\n const data: unknown = await response.json();\n return data as T;\n } catch {\n return null;\n }\n}\n\n/**\n * Parses the error body from non-2xx responses per §9.4.\n *\n * Expected shape: `{ error: { code, message, retryAfterMs?, upgradeUrl? } }`.\n * Returns `null` if the body is absent, unparseable, or doesn't match\n * the expected shape.\n *\n * @param response - The non-2xx `Response` object.\n * @returns Parsed `CloudErrorBody`, or `null` if parsing fails.\n */\nasync function parseErrorBody(response: Response): Promise<CloudErrorBody | null> {\n try {\n const raw: unknown = await response.json();\n\n // Validate the expected §9.4 shape: { error: { code, message, ... } }\n if (\n typeof raw === 'object' &&\n raw !== null &&\n 'error' in raw\n ) {\n const envelope = raw;\n const errorObj = envelope.error;\n\n if (\n typeof errorObj === 'object' &&\n errorObj !== null &&\n 'code' in errorObj &&\n 'message' in errorObj &&\n typeof (errorObj as { code: unknown }).code === 'string' &&\n typeof (errorObj as { message: unknown }).message === 'string'\n ) {\n const typed = errorObj as {\n code: string;\n message: string;\n retryAfterMs?: unknown;\n upgradeUrl?: unknown;\n };\n\n return {\n code: typed.code,\n message: typed.message,\n retryAfterMs: typeof typed.retryAfterMs === 'number'\n ? typed.retryAfterMs\n : undefined,\n upgradeUrl: typeof typed.upgradeUrl === 'string'\n ? typed.upgradeUrl\n : undefined,\n };\n }\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Sleeps for the specified number of milliseconds.\n *\n * Used for exponential backoff delays between retry attempts. Non-blocking.\n *\n * @param ms - Duration in milliseconds.\n * @returns A promise that resolves after the delay.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/**\n * Resolves the effective request timeout in milliseconds.\n *\n * Priority: global config override → per-operation default → 10s fallback.\n *\n * @param globalTimeout - `CloudConfig.timeoutMs` (global override), or `undefined`.\n * @param operationTimeout - `CloudRequestConfig.operationTimeout`, or `undefined`.\n * @returns Effective timeout in milliseconds.\n */\nfunction resolveTimeout(\n globalTimeout: number | undefined,\n operationTimeout: number | undefined,\n): number {\n return globalTimeout ?? operationTimeout ?? DEFAULT_TIMEOUT_MS;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CloudHttpTransport} for communicating with the\n * Enterstellar Cloud API.\n *\n * **Request lifecycle per call (v0.1.0):**\n * 1. Generate `X-Idempotency-Key` (ULID) if `ipuCost > 0` (AM10/F8).\n * 2. Build headers: `Authorization`, `User-Agent`, `Accept`, `Content-Type`,\n * `X-Idempotency-Key` (conditional).\n * 3. Attempt loop (max 3):\n * a. Execute `fetch()` with `AbortController` timeout.\n * b. 2xx → parse response, return `CloudResponse<T>`.\n * c. 429 → parse error body, **throw** `CloudError` (SD3). No retry.\n * d. 4xx → parse error body, **throw** `CloudError`. No retry.\n * e. 5xx → sleep (backoff), retry next attempt.\n * f. Network/timeout error → sleep (backoff), retry next attempt.\n * 4. All 3 attempts failed → **throw** `ENS-5005`.\n *\n * **No mutable instance state.** All retry state is local to each\n * `request()` invocation. Concurrent requests are fully independent.\n *\n * @param config - Transport configuration (endpoint, API key, timeout).\n * @returns A `CloudHttpTransport` instance.\n *\n * @example\n * ```ts\n * const transport = createCloudHttpTransport({\n * endpoint: 'https://api.enterstellar.dev',\n * apiKey: 'ak_my_key',\n * });\n *\n * const response = await transport.request<UsageData>({\n * method: 'GET',\n * path: '/v1/usage',\n * ipuCost: 0,\n * });\n *\n * console.log(response.data); // UsageData\n * ```\n *\n * @see Design Choice SD3 — throw on 429.\n * @see Design Choice SD5 — 3× exponential backoff for 5xx/network.\n * @see Design Choice AM10 — `X-Idempotency-Key` on IPU-consuming requests.\n * @see Design Choice CL4 — bearer token auth.\n * @see Design Choice CL1 — `X-IPU-*` header reconciliation.\n * @internal — not part of the public API barrel.\n */\nexport function createCloudHttpTransport(config: CloudHttpConfig): CloudHttpTransport {\n const { endpoint, apiKey, timeoutMs: globalTimeoutMs } = config;\n\n return {\n async request<T>(reqConfig: CloudRequestConfig): Promise<CloudResponse<T>> {\n const effectiveTimeout = resolveTimeout(\n globalTimeoutMs,\n reqConfig.operationTimeout,\n );\n const url = `${endpoint}${reqConfig.path}`;\n\n // ---------------------------------------------------------------\n // Step 1: Generate idempotency key (AM10, F8)\n // Generated ONCE per request. All retry attempts reuse the same key.\n // Only generated for IPU-consuming requests (ipuCost > 0).\n // ---------------------------------------------------------------\n const idempotencyKey = reqConfig.ipuCost > 0\n ? generateIdempotencyKey()\n : undefined;\n\n // ---------------------------------------------------------------\n // Step 2: Build request headers\n // These are the same for all retry attempts.\n // ---------------------------------------------------------------\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${apiKey}`,\n 'User-Agent': `enterstellar-cloud-sdk/${CLOUD_SDK_VERSION}`,\n 'Accept': 'application/json',\n };\n\n // Content-Type only for requests with a body (POST).\n if (reqConfig.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n // Idempotency key only when IPU > 0 (AM10/F8).\n if (idempotencyKey !== undefined) {\n headers['X-Idempotency-Key'] = idempotencyKey;\n }\n\n // ---------------------------------------------------------------\n // Step 3: Attempt loop (max 3 attempts — SD5)\n // ---------------------------------------------------------------\n let lastStatusCode: number | undefined;\n let lastRequestId: string | undefined;\n\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, effectiveTimeout);\n\n try {\n // -----------------------------------------------------------\n // Execute fetch\n // -----------------------------------------------------------\n const response = await fetch(url, {\n method: reqConfig.method,\n headers,\n ...(reqConfig.body !== undefined\n ? { body: JSON.stringify(reqConfig.body) }\n : {}),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // -----------------------------------------------------------\n // Parse response headers (§9.3)\n // -----------------------------------------------------------\n const ipuUsed = parseNumericHeader(response.headers, 'X-IPU-Used');\n const ipuRemaining = parseNumericHeader(response.headers, 'X-IPU-Remaining');\n const ipuCost = parseNumericHeader(response.headers, 'X-IPU-Cost');\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n\n lastStatusCode = response.status;\n lastRequestId = requestId;\n\n // -----------------------------------------------------------\n // 2xx — Success. Return structured response.\n // -----------------------------------------------------------\n if (response.ok) {\n const data = await safeParseJson<T>(response);\n\n return {\n ok: true,\n statusCode: response.status,\n data,\n ipuUsed,\n ipuRemaining,\n ipuCost,\n requestId,\n error: null,\n };\n }\n\n // -----------------------------------------------------------\n // 429 — Quota exceeded / rate limited (SD3).\n // THROW immediately. Never retry 429.\n // Parse error body for upgradeUrl and retryAfterMs.\n // -----------------------------------------------------------\n if (response.status === 429) {\n const errorBody = await parseErrorBody(response);\n\n // Use parsed body if available, otherwise build a fallback.\n const body: CloudErrorBody = errorBody ?? {\n code: 'ENS-C4290',\n message: 'IPU quota exceeded',\n };\n\n throw createQuotaExceededError(body, requestId);\n }\n\n // -----------------------------------------------------------\n // 5xx — Server error. Retry with backoff (SD5).\n // Only retry — do NOT throw yet. Let the loop continue.\n // -----------------------------------------------------------\n if (response.status >= 500) {\n // Consume the body to release the connection (prevents memory leak).\n await response.text().catch(() => undefined);\n\n // If this is NOT the last attempt, sleep before next retry.\n if (attempt < MAX_ATTEMPTS - 1) {\n // RETRY_BACKOFF_MS has 3 entries — index is always in bounds.\n const backoffMs = RETRY_BACKOFF_MS[attempt] as number;\n await sleep(backoffMs);\n }\n\n continue;\n }\n\n // -----------------------------------------------------------\n // 4xx (non-429) — Client error. THROW immediately, no retry.\n // These indicate a permanent error (bad request, not found,\n // unauthorized, etc.) — retrying won't help.\n // -----------------------------------------------------------\n const errorBody = await parseErrorBody(response);\n\n throw new CloudError(\n 'ENS-5003',\n errorBody?.code ?? `HTTP-${String(response.status)}`,\n `@enterstellar-ai/cloud: Request failed — ${errorBody?.message ?? `HTTP ${String(response.status)}`}.`,\n false,\n { requestId },\n );\n } catch (error: unknown) {\n clearTimeout(timeoutId);\n\n // -----------------------------------------------------------\n // Re-throw CloudError (from 429 or 4xx handling above).\n // These are intentional throws — not network errors.\n // -----------------------------------------------------------\n if (error instanceof CloudError) {\n throw error;\n }\n\n // -----------------------------------------------------------\n // Network / timeout error — retry with backoff (SD5).\n // AbortError (timeout), TypeError (DNS, TLS), etc.\n // -----------------------------------------------------------\n lastStatusCode = undefined;\n lastRequestId = undefined;\n\n // If this is NOT the last attempt, sleep before next retry.\n if (attempt < MAX_ATTEMPTS - 1) {\n // RETRY_BACKOFF_MS has 3 entries — index is always in bounds.\n const backoffMs = RETRY_BACKOFF_MS[attempt] as number;\n await sleep(backoffMs);\n }\n\n // Let the loop continue to the next attempt.\n }\n }\n\n // ---------------------------------------------------------------\n // All attempts exhausted — throw ENS-5005.\n // ---------------------------------------------------------------\n throw createRetriesExhaustedError(MAX_ATTEMPTS, lastStatusCode, lastRequestId);\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/transport/cloud-sse\n * @description SSE transport for CloudForge streaming (`forge.stream()`).\n *\n * Opens a `POST /v1/forge` request with `Accept: text/event-stream` and\n * parses the Server-Sent Events stream into typed {@link ForgeFragment}\n * objects, yielded via `AsyncGenerator`.\n *\n * **SSE event type mapping (CF6):**\n *\n * | SSE `event:` field | `ForgeFragment.type` | Description |\n * |:-------------------|:---------------------|:--------------------------------|\n * | `meta` | `meta` | Provider info, IPU from headers |\n * | `node` | `node` | Partial contract structure |\n * | `property` | `property` | Single property update |\n * | `complete` | `complete` | Full contract, IPU from headers |\n * | `error` | `error` | Generation failure |\n *\n * **IPU delivery (F18):** The `X-IPU-Used`, `X-IPU-Remaining`, and `X-IPU-Cost`\n * response headers are available at stream start (before any SSE events).\n * The transport parses them once and injects the `CloudIPU` into the `meta`\n * (first) and `complete` (last) fragments.\n *\n * **Error handling:**\n * - 429 → throw `CloudError` (SD3) before yielding any fragments.\n * - 4xx → throw `CloudError` immediately.\n * - Network error mid-stream → throw `CloudError` (`ENS-5005`). No mid-stream\n * retry — partial data has already been consumed by the caller.\n * - SSE `error` event → yield `ForgeErrorFragment`, then return.\n *\n * **No retry for SSE streams.** Unlike the HTTP transport (SD5), SSE streams\n * are not retried. The caller (forge proxy) may choose to retry from scratch\n * if the stream fails before yielding a `complete` fragment.\n *\n * @see Design Choice SD6 — `forge.stream()` returns `AsyncGenerator<ForgeFragment>`.\n * @see Design Choice SD9 — `eventsource-parser` (minimal SSE dep).\n * @see Design Choice CF6 — SSE event types.\n * @see Design Choice CF9 — provider identity via `meta` event.\n * @see Design Choice CF14 — SSE streaming format.\n * @see Audit Finding F18 — IPU on `meta` and `complete` fragments.\n * @see Principle L15 — zero framework imports.\n */\n\nimport { createParser } from 'eventsource-parser';\n\nimport type { EventSourceMessage } from 'eventsource-parser';\n\nimport type { ComponentContract } from '@enterstellar-ai/types';\n\nimport type {\n CloudIPU,\n ForgeCompleteFragment,\n ForgeErrorFragment,\n ForgeFragment,\n ForgeMetaFragment,\n ForgeNodeFragment,\n ForgePropertyFragment,\n} from '../types.js';\nimport type { CloudErrorBody } from '../errors.js';\nimport type { CloudHttpConfig } from './cloud-http.js';\n\nimport {\n CloudError,\n createQuotaExceededError,\n createRetriesExhaustedError,\n} from '../errors.js';\nimport { CLOUD_SDK_VERSION } from '../version.js';\nimport { generateIdempotencyKey } from './idempotency.js';\nimport { OPERATION_TIMEOUTS } from './cloud-http.js';\n\n// ---------------------------------------------------------------------------\n// CloudSSEConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for a single SSE streaming request.\n *\n * Passed by the forge proxy to initiate a streaming forge operation.\n *\n * @internal\n */\nexport type CloudSSEConfig = {\n /** The forge request body to send as JSON. */\n readonly body: unknown;\n\n /**\n * Whether the client is in anonymous mode (`pk_anon_*`).\n * When `true`, `ipu` on fragments is `null` (AG8).\n */\n readonly isAnonymous: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// CloudSSETransport Interface\n// ---------------------------------------------------------------------------\n\n/**\n * SSE transport interface for streaming forge operations.\n *\n * Returns an `AsyncGenerator` that yields `ForgeFragment` objects\n * as SSE events arrive from the server.\n *\n * @internal — consumed by `cloud-forge-proxy`, not exported publicly.\n */\nexport interface CloudSSETransport {\n /**\n * Opens a streaming forge connection and yields fragments.\n *\n * @param config - SSE request configuration.\n * @yields {ForgeFragment} Typed SSE fragments in lifecycle order.\n *\n * @throws {CloudError} `ENS-C4290` on 429 (before any fragments).\n * @throws {CloudError} On 4xx (before any fragments).\n * @throws {CloudError} `ENS-5005` on network error mid-stream.\n */\n stream(config: CloudSSEConfig): AsyncGenerator<ForgeFragment, void, undefined>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parses a numeric header value from the response.\n * Duplicated from `cloud-http.ts` to avoid circular imports.\n *\n * @param headers - The response `Headers` object.\n * @param name - Header name to parse.\n * @returns Parsed non-negative number, or `undefined` if absent/invalid.\n */\nfunction parseNumericHeader(headers: Headers, name: string): number | undefined {\n const raw = headers.get(name);\n if (raw === null || raw.trim().length === 0) {\n return undefined;\n }\n\n const value = Number(raw);\n if (Number.isFinite(value) && value >= 0) {\n return value;\n }\n\n return undefined;\n}\n\n/**\n * Parses IPU headers from the HTTP response into a {@link CloudIPU} object.\n *\n * Returns `null` if the request is anonymous (AG8: all `X-IPU-*` headers\n * omitted) or if headers are absent/unparseable.\n *\n * @param headers - Response headers containing `X-IPU-*` values.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns Parsed `CloudIPU` or `null`.\n */\nfunction parseIPUHeaders(headers: Headers, isAnonymous: boolean): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n const used = parseNumericHeader(headers, 'X-IPU-Used');\n const remaining = parseNumericHeader(headers, 'X-IPU-Remaining');\n const cost = parseNumericHeader(headers, 'X-IPU-Cost');\n\n // If all three headers are present, construct a CloudIPU object.\n // If any are missing, return null — partial IPU data is unreliable.\n if (used !== undefined && remaining !== undefined && cost !== undefined) {\n return { used, remaining, cost };\n }\n\n return null;\n}\n\n/**\n * Parses the error body from a non-2xx response (§9.4).\n *\n * @param response - The non-2xx `Response` object.\n * @returns Parsed `CloudErrorBody`, or `null`.\n */\nasync function parseErrorBody(response: Response): Promise<CloudErrorBody | null> {\n try {\n const raw: unknown = await response.json();\n\n if (\n typeof raw === 'object' &&\n raw !== null &&\n 'error' in raw\n ) {\n const envelope = raw;\n const errorObj = envelope.error;\n\n if (\n typeof errorObj === 'object' &&\n errorObj !== null &&\n 'code' in errorObj &&\n 'message' in errorObj &&\n typeof (errorObj as { code: unknown }).code === 'string' &&\n typeof (errorObj as { message: unknown }).message === 'string'\n ) {\n const typed = errorObj as {\n code: string;\n message: string;\n retryAfterMs?: unknown;\n upgradeUrl?: unknown;\n };\n\n return {\n code: typed.code,\n message: typed.message,\n retryAfterMs: typeof typed.retryAfterMs === 'number'\n ? typed.retryAfterMs\n : undefined,\n upgradeUrl: typeof typed.upgradeUrl === 'string'\n ? typed.upgradeUrl\n : undefined,\n };\n }\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Safely parses a JSON string into a typed value.\n *\n * Returns `null` if the string is empty or unparseable. Never throws.\n *\n * @typeParam T - Expected parsed type.\n * @param data - The raw JSON string from an SSE `data:` field.\n * @returns Parsed value as `T`, or `null` on failure.\n */\nfunction safeParseJsonString(data: string): unknown {\n try {\n return JSON.parse(data) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Creates a typed {@link ForgeFragment} from a parsed SSE event.\n *\n * Maps the SSE `event:` field to the `ForgeFragment.type` discriminant\n * and parses the `data:` field as JSON. Returns `null` for unrecognized\n * event types or unparseable data.\n *\n * @param event - The parsed SSE event from `eventsource-parser`.\n * @param ipu - Pre-parsed IPU data from HTTP response headers (F18).\n * @returns A typed `ForgeFragment`, or `null` if the event is unrecognized.\n */\nfunction mapEventToFragment(\n event: EventSourceMessage,\n ipu: CloudIPU | null,\n): ForgeFragment | null {\n const eventType = event.event ?? 'message';\n\n switch (eventType) {\n case 'meta': {\n const data = safeParseJsonString(event.data) as { provider: string; model: string } | null;\n if (data === null) {\n return null;\n }\n\n const fragment: ForgeMetaFragment = {\n type: 'meta',\n data: { provider: data.provider, model: data.model },\n ipu,\n };\n return fragment;\n }\n\n case 'node': {\n const data = safeParseJsonString(event.data) as Partial<ComponentContract> | null;\n if (data === null) {\n return null;\n }\n\n const fragment: ForgeNodeFragment = {\n type: 'node',\n data,\n };\n return fragment;\n }\n\n case 'property': {\n const data = safeParseJsonString(event.data) as { path: string; value: unknown } | null;\n if (data === null) {\n return null;\n }\n\n const fragment: ForgePropertyFragment = {\n type: 'property',\n data: { path: data.path, value: data.value },\n };\n return fragment;\n }\n\n case 'complete': {\n const data = safeParseJsonString(event.data) as ComponentContract | null;\n if (data === null) {\n return null;\n }\n\n const fragment: ForgeCompleteFragment = {\n type: 'complete',\n data,\n ipu,\n };\n return fragment;\n }\n\n case 'error': {\n const data = safeParseJsonString(event.data) as { code: string; message: string } | null;\n if (data === null) {\n return null;\n }\n\n const fragment: ForgeErrorFragment = {\n type: 'error',\n data: { code: data.code, message: data.message },\n };\n return fragment;\n }\n\n default:\n // Unrecognized event type — skip silently.\n // The server may add new event types in the future (forward compatibility).\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CloudSSETransport} for streaming forge operations.\n *\n * **Stream lifecycle:**\n * 1. Build request with `Accept: text/event-stream`, `Authorization`,\n * `User-Agent`, `X-Idempotency-Key`, and JSON body.\n * 2. Execute `fetch()` with `AbortController` timeout (30s default).\n * 3. On non-2xx: parse error body, throw `CloudError`.\n * 4. Parse `X-IPU-*` headers from response (F18).\n * 5. Read `ReadableStream` body as text chunks.\n * 6. Feed chunks into `eventsource-parser`.\n * 7. Map parsed events to `ForgeFragment` objects, yield via generator.\n * 8. On `complete` or `error` event: generator returns.\n * 9. On network error mid-stream: throw `ENS-5005`.\n *\n * @param config - HTTP transport configuration (endpoint, API key, timeout).\n * @returns A `CloudSSETransport` instance.\n *\n * @see Design Choice SD6 — streaming forge via SSE.\n * @see Design Choice SD9 — `eventsource-parser` as the only runtime dep.\n * @see Design Choice CF6 — SSE event types.\n * @internal — not part of the public API barrel.\n */\nexport function createCloudSSETransport(config: CloudHttpConfig): CloudSSETransport {\n const { endpoint, apiKey, timeoutMs: globalTimeoutMs } = config;\n\n return {\n async *stream(sseConfig: CloudSSEConfig): AsyncGenerator<ForgeFragment, void, undefined> {\n const effectiveTimeout = globalTimeoutMs ?? OPERATION_TIMEOUTS.forge;\n const url = `${endpoint}/v1/forge`;\n\n // ---------------------------------------------------------------\n // Step 1: Build request\n // ---------------------------------------------------------------\n const idempotencyKey = generateIdempotencyKey();\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${apiKey}`,\n 'User-Agent': `enterstellar-cloud-sdk/${CLOUD_SDK_VERSION}`,\n 'Accept': 'text/event-stream',\n 'Content-Type': 'application/json',\n 'X-Idempotency-Key': idempotencyKey,\n };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, effectiveTimeout);\n\n // ---------------------------------------------------------------\n // Step 2: Execute fetch\n // ---------------------------------------------------------------\n let response: Response;\n\n try {\n response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(sseConfig.body),\n signal: controller.signal,\n });\n } catch {\n clearTimeout(timeoutId);\n\n // Network error or timeout before response —\n // no retry for SSE (partial data cannot be retried).\n throw createRetriesExhaustedError(\n 1,\n undefined,\n undefined,\n );\n }\n\n // ---------------------------------------------------------------\n // Step 3: Handle non-2xx before streaming starts\n // ---------------------------------------------------------------\n if (!response.ok) {\n clearTimeout(timeoutId);\n\n if (response.status === 429) {\n const errorBody = await parseErrorBody(response);\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n const body: CloudErrorBody = errorBody ?? {\n code: 'ENS-C4290',\n message: 'IPU quota exceeded',\n };\n\n throw createQuotaExceededError(body, requestId);\n }\n\n const errorBody = await parseErrorBody(response);\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n\n throw new CloudError(\n 'ENS-5003',\n errorBody?.code ?? `HTTP-${String(response.status)}`,\n `@enterstellar-ai/cloud: Forge stream failed — ${errorBody?.message ?? `HTTP ${String(response.status)}`}.`,\n false,\n { requestId },\n );\n }\n\n // ---------------------------------------------------------------\n // Step 4: Parse IPU headers (F18)\n // ---------------------------------------------------------------\n const ipu = parseIPUHeaders(response.headers, sseConfig.isAnonymous);\n\n // ---------------------------------------------------------------\n // Step 5: Set up SSE parsing pipeline\n // ---------------------------------------------------------------\n const body = response.body;\n\n if (body === null) {\n clearTimeout(timeoutId);\n throw createRetriesExhaustedError(1, response.status);\n }\n\n // Buffer for fragments produced by the parser.\n // The parser's `onEvent` callback pushes fragments here,\n // and the generator loop below yields them.\n const fragmentBuffer: ForgeFragment[] = [];\n const streamState = { done: false };\n\n const parser = createParser({\n onEvent(event: EventSourceMessage): void {\n const fragment = mapEventToFragment(event, ipu);\n if (fragment !== null) {\n fragmentBuffer.push(fragment);\n\n // `complete` and `error` events signal end of stream.\n if (fragment.type === 'complete' || fragment.type === 'error') {\n streamState.done = true;\n }\n }\n },\n });\n\n /**\n * Drains all buffered fragments and returns them as a new array.\n * Empties the buffer in-place via `splice(0)`.\n */\n function drainBuffer(): ForgeFragment[] {\n return fragmentBuffer.splice(0);\n }\n\n // ---------------------------------------------------------------\n // Step 6: Read stream and yield fragments\n // ---------------------------------------------------------------\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n try {\n // Read chunks until the stream ends or a terminal event fires.\n let readerDone = false;\n\n while (!readerDone) {\n const readResult = await reader.read();\n readerDone = readResult.done;\n\n if (readResult.value !== undefined) {\n // Decode the chunk and feed it to the SSE parser.\n const text = decoder.decode(readResult.value, { stream: true });\n parser.feed(text);\n }\n\n // Yield all fragments produced by the parser for this chunk.\n for (const fragment of drainBuffer()) {\n yield fragment;\n }\n\n // `streamDone` is mutated synchronously inside the `onEvent`\n // callback during `parser.feed()` above.\n if (streamState.done) {\n break;\n }\n }\n\n // Yield any remaining buffered fragments after stream ends.\n for (const fragment of drainBuffer()) {\n yield fragment;\n }\n } catch (error: unknown) {\n // Re-throw CloudError (from internal handling).\n if (error instanceof CloudError) {\n throw error;\n }\n\n // Network error mid-stream — AbortError (timeout), etc.\n throw createRetriesExhaustedError(1, undefined, undefined);\n } finally {\n clearTimeout(timeoutId);\n\n // Always release the reader to prevent memory leaks.\n try {\n reader.releaseLock();\n } catch {\n // `releaseLock()` can throw if the reader is already released\n // or the stream is in an error state. Safe to ignore.\n }\n }\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/metering/ipu-tracker\n * @description Local IPU (Intent Processing Unit) estimate tracker.\n *\n * Implements the **client side** of CL1's hybrid metering model:\n * - The server is authoritative (IPU values in `X-IPU-Used`/`X-IPU-Remaining`).\n * - The client tracks local estimates for real-time dashboards.\n * - On each API response, `reconcile()` compares local vs server values.\n * - If drift exceeds 10%, the local estimate auto-corrects to the server value.\n *\n * **Lifecycle:**\n * 1. Created by `createEnterstellarCloudClient()` (one tracker per client instance).\n * 2. `record(cost)` called by each proxy module after a cloud API call.\n * 3. `reconcile(serverUsed, serverRemaining, ipuCost?)` called after parsing\n * `X-IPU-Used`, `X-IPU-Remaining`, and `X-IPU-Cost` response headers (§9.3).\n * 4. `isOverQuota()` checked by proxies before making API calls.\n * 5. `getLastIPUCost()` used by proxies to populate `CloudIPU.cost` in\n * the `CloudResult<T>` wrapper (SD7).\n *\n * **No persistence** — the tracker is ephemeral per session. IPU usage\n * resets on page reload. Server values are the ground truth.\n *\n * @see Design Choice CL1 — hybrid metering, auto-correct on >10% drift.\n * @see Design Choice CL2 — weighted IPU costs.\n * @see Design Choice CL3 — `isOverQuota()` enables pre-flight degradation.\n * @see Design Choice SD7 — universal return wrapper requires `CloudIPU.cost`.\n */\n\n// ---------------------------------------------------------------------------\n// IPUTracker Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Local IPU estimate tracker.\n *\n * Created via {@link createIPUTracker}. Tracks client-side IPU estimates\n * and reconciles with server-reported values.\n *\n * @see Design Choice CL1\n */\nexport interface IPUTracker {\n /**\n * Record a local IPU cost estimate.\n *\n * Called by proxy modules after each cloud API call to increment\n * the local usage counter.\n *\n * @param cost - The IPU cost of the operation (from {@link IPU_COSTS}).\n */\n record(cost: number): void;\n\n /**\n * Reconcile local estimates with server-reported values.\n *\n * Called after every cloud API response that includes `X-IPU-Used`,\n * `X-IPU-Remaining`, and `X-IPU-Cost` headers (§9.3). If the drift\n * between local and server values exceeds 10%, the local estimate\n * auto-corrects to the server value (CL1).\n *\n * The optional `ipuCost` parameter stores the per-request cost\n * (from `X-IPU-Cost` header). Retrieved via {@link getLastIPUCost}\n * for populating `CloudIPU.cost` in `CloudResult<T>` (SD7).\n *\n * @param serverUsed - IPU used as reported by the server (`X-IPU-Used`).\n * @param serverRemaining - IPU remaining as reported by the server (`X-IPU-Remaining`).\n * @param ipuCost - IPU charged for this specific request (`X-IPU-Cost`). Optional.\n */\n reconcile(serverUsed: number, serverRemaining: number, ipuCost?: number): void;\n\n /**\n * Get the current local IPU estimate.\n *\n * @returns Object with `used`, `remaining`, and `limit` fields.\n * `remaining` and `limit` are `null` until the first\n * `reconcile()` call provides server data.\n */\n getEstimate(): IPUEstimate;\n\n /**\n * Check whether the client is over its IPU quota.\n *\n * Returns `true` if the local estimate of remaining IPUs is ≤ 0.\n * Returns `false` if no limit is known yet (before first reconciliation).\n *\n * Used by proxy modules for pre-flight checks before making\n * API calls — enables immediate graceful degradation (CL3)\n * without waiting for a 429 response.\n *\n * @returns `true` if over quota, `false` otherwise.\n */\n isOverQuota(): boolean;\n\n /**\n * Get the per-request IPU cost from the last `reconcile()` call.\n *\n * Returns the `ipuCost` value passed to the most recent `reconcile()`\n * invocation. Used by proxy modules to populate `CloudIPU.cost`\n * in the `CloudResult<T>` wrapper (SD7).\n *\n * Returns `undefined` if `reconcile()` has never been called or\n * if the last call did not include an `ipuCost` parameter.\n *\n * @returns The last per-request IPU cost, or `undefined`.\n *\n * @see Design Choice SD7 — `CloudResult<T>` includes `CloudIPU.cost`.\n */\n getLastIPUCost(): number | undefined;\n\n /**\n * Reset the tracker state.\n *\n * Clears the local estimate, server-reported values, and last\n * IPU cost. Used for billing period rollovers or testing.\n */\n reset(): void;\n}\n\n// ---------------------------------------------------------------------------\n// IPUEstimate Type\n// ---------------------------------------------------------------------------\n\n/**\n * Current local IPU estimate snapshot.\n *\n * @see {@link IPUTracker.getEstimate}\n */\nexport type IPUEstimate = {\n /** Estimated IPUs consumed (locally tracked). */\n readonly used: number;\n\n /**\n * Estimated IPUs remaining.\n * `null` until the first server reconciliation provides a limit.\n */\n readonly remaining: number | null;\n\n /**\n * Total IPU limit for the billing period.\n * `null` until the first server reconciliation.\n */\n readonly limit: number | null;\n\n /**\n * Whether the last reconciliation detected drift > 10%\n * and auto-corrected the local estimate.\n */\n readonly lastReconciliationCorrected: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Maximum acceptable drift between local and server IPU estimates.\n *\n * If `|localUsed - serverUsed| / serverUsed > DRIFT_THRESHOLD`,\n * the local estimate auto-corrects to the server value (CL1).\n */\nconst DRIFT_THRESHOLD = 0.10;\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an {@link IPUTracker} for local IPU estimate tracking.\n *\n * @returns A new `IPUTracker` instance with zero initial usage.\n *\n * @example\n * ```ts\n * import { createIPUTracker } from './metering/ipu-tracker.js';\n * import { IPU_COSTS } from './metering/ipu-costs.js';\n *\n * const tracker = createIPUTracker();\n *\n * // After a forge call:\n * tracker.record(IPU_COSTS.FORGE); // +10 IPU\n *\n * // After receiving server headers:\n * tracker.reconcile(42, 958); // serverUsed=42, serverRemaining=958\n *\n * // Pre-flight check:\n * if (tracker.isOverQuota()) {\n * // Fall back to local processing (CL3)\n * }\n * ```\n *\n * @see Design Choice CL1 — hybrid metering.\n */\nexport function createIPUTracker(): IPUTracker {\n // -----------------------------------------------------------------------\n // Internal State\n // -----------------------------------------------------------------------\n\n /** Local estimate of IPUs consumed this session. */\n let localUsed = 0;\n\n /**\n * Server-reported IPU limit for the billing period.\n * `null` until the first `reconcile()` call.\n */\n let serverLimit: number | null = null;\n\n /** Whether the last reconciliation triggered a drift correction. */\n let lastCorrected = false;\n\n /**\n * Per-request IPU cost from the last `reconcile()` call.\n * Populated from the `X-IPU-Cost` response header.\n * `undefined` until the first reconciliation with a cost value.\n */\n let lastIPUCost: number | undefined;\n\n // -----------------------------------------------------------------------\n // IPUTracker Implementation\n // -----------------------------------------------------------------------\n\n return {\n record(cost: number): void {\n localUsed += cost;\n },\n\n reconcile(serverUsed: number, serverRemaining: number, ipuCost?: number): void {\n // Store the per-request cost for CloudResult<T> construction (SD7).\n lastIPUCost = ipuCost;\n\n // Compute the total limit from server data.\n serverLimit = serverUsed + serverRemaining;\n\n // ---------------------------------------------------------------\n // Drift detection (CL1)\n //\n // Formula: |localUsed - serverUsed| / serverUsed > 10%\n //\n // Edge case: if serverUsed is 0, any non-zero local estimate\n // is considered drift (since the server has no record of usage).\n // If both are 0, there is no drift.\n // ---------------------------------------------------------------\n if (serverUsed === 0) {\n // If local is also 0, no drift. If local > 0, correct.\n if (localUsed > 0) {\n localUsed = serverUsed;\n lastCorrected = true;\n } else {\n lastCorrected = false;\n }\n return;\n }\n\n const drift = Math.abs(localUsed - serverUsed) / serverUsed;\n\n if (drift > DRIFT_THRESHOLD) {\n // Auto-correct: snap local estimate to server value.\n localUsed = serverUsed;\n lastCorrected = true;\n } else {\n lastCorrected = false;\n }\n },\n\n getEstimate(): IPUEstimate {\n // Compute remaining from local estimate if we have a limit.\n const remaining = serverLimit !== null\n ? Math.max(0, serverLimit - localUsed)\n : null;\n\n return {\n used: localUsed,\n remaining,\n limit: serverLimit,\n lastReconciliationCorrected: lastCorrected,\n };\n },\n\n isOverQuota(): boolean {\n // If no limit is known yet, assume not over quota.\n // The first API call will either succeed or return 429.\n if (serverLimit === null) {\n return false;\n }\n\n return localUsed >= serverLimit;\n },\n\n getLastIPUCost(): number | undefined {\n return lastIPUCost;\n },\n\n reset(): void {\n localUsed = 0;\n serverLimit = null;\n lastCorrected = false;\n lastIPUCost = undefined;\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/metering/ipu-costs\n * @description Weighted IPU (Intent Processing Unit) cost constants.\n *\n * Each Enterstellar Cloud API operation has a fixed IPU cost. These constants\n * are the single source of truth for cost calculations across all\n * proxy modules. They are also exported from the barrel for consumer\n * use (e.g., pre-flight cost estimation in application code).\n *\n * **13 operations** defined per Bible §9.1 (corrected):\n *\n * | Operation | Constant | IPU |\n * |:---------------------|:--------------------------|:---:|\n * | Cloud Forge | `FORGE` | 10 |\n * | Semantic Search | `SEMANTIC_SEARCH` | 1 |\n * | Intent Route | `ROUTE` | 1 |\n * | Batch Route (per) | `ROUTE_BATCH_PER_INTENT` | 1 |\n * | Submit Signal | `SIGNAL_SUBMIT` | 0 |\n * | Submit Trace | `TRACE_SUBMIT` | 0 |\n * | Trace Analytics | `TRACE_ANALYTICS` | 5 |\n * | Business Analytics | `BUSINESS_ANALYTICS` | 5 |\n * | Certify | `CERTIFY` | 20 |\n * | Usage Query | `USAGE_QUERY` | 0 |\n * | Ledger Query | `LEDGER_QUERY` | 0 |\n * | Get Traces | `GET_TRACES` | 0 |\n * | Delete Project Data | `DELETE_PROJECT_DATA` | 0 |\n *\n * **Scope (F9):** This table covers SDK-triggered operations only.\n * Server-internal operations (`cold_path`, `hitl_review`, `global_index_publish`,\n * etc.) are defined in the Cloud monorepo's `shared/ipu.ts`.\n *\n * @see Design Choice CL2 — weighted IPU costs.\n * @see Bible §9.1 — API endpoint table with IPU costs.\n * @see Audit Finding F9 — SDK-triggered operations only.\n */\n\n// ---------------------------------------------------------------------------\n// IPU Cost Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Weighted IPU costs per cloud API operation.\n *\n * These values are locked per Design Choice CL2 and Bible §9.1.\n * Do not modify without an explicit amendment to the design choices\n * document and the Implementation Bible.\n *\n * @example\n * ```ts\n * import { IPU_COSTS } from '@enterstellar-ai/cloud';\n *\n * // Pre-flight cost estimation:\n * const batchCost = intentHashes.length * IPU_COSTS.ROUTE_BATCH_PER_INTENT;\n * console.log(`Batch route will cost ${batchCost} IPU`);\n * ```\n */\nexport const IPU_COSTS = Object.freeze({\n /**\n * CloudForge generation — LLM-based component contract generation.\n * Premium feature, highest per-operation cost.\n *\n * @see Design Choice CL2 — \"CloudForge generation = 10 IPU\"\n * @see Bible §9.1 — `POST /v1/forge`\n */\n FORGE: 10,\n\n /**\n * Cloud semantic search — vector similarity lookup via Vectorize.\n * Lightweight operation, lowest per-operation cost.\n *\n * @see Design Choice CL2 — \"cloud semantic search = 1 IPU\"\n * @see Bible §9.1 — `POST /v1/semantic-search`\n */\n SEMANTIC_SEARCH: 1,\n\n /**\n * Intent routing — frequency-based (Phase 2) or ML-based (Phase 3)\n * component prediction for a single intent hash.\n *\n * @see Design Choice IR2 — router prediction response shape.\n * @see Bible §9.1 — `POST /v1/route`\n */\n ROUTE: 1,\n\n /**\n * Batch intent routing — per-intent cost within a batch request.\n * A batch of N intents costs `N × 1 IPU`.\n *\n * @see Design Choice IR5 — batch routing for pre-rendering.\n * @see Bible §9.1 — `POST /v1/route/batch`\n */\n ROUTE_BATCH_PER_INTENT: 1,\n\n /**\n * ForgeSignal submission — telemetry data ingestion.\n * Free — signal data is Enterstellar's #1 strategic asset (§9.1:\n * \"never charge for data collection\").\n *\n * @see Design Choice SD4 — transparent `pk_anon` auth for signals.\n * @see Bible §9.1 — `POST /v1/signals`\n */\n SIGNAL_SUBMIT: 0,\n\n /**\n * AgentTrace submission — trace data ingestion for aggregation.\n * Free — trace data is the feedstock for analytics features.\n *\n * **CORRECTED:** Was 5 IPU in the OSS Bible (§4.13). Changed to 0\n * in the Cloud Bible §9.1: \"never charge for data collection.\"\n *\n * @see Bible §9.1 — `POST /v1/traces` (0 IPU)\n */\n TRACE_SUBMIT: 0,\n\n /**\n * Trace analytics query — server-side OLAP aggregation via ClickHouse.\n * Moderate cost reflecting the compute intensity.\n *\n * @see Design Choice TA5 — fixed analytics query types.\n * @see Bible §9.1 — `POST /v1/traces/analytics`\n */\n TRACE_ANALYTICS: 5,\n\n /**\n * Business analytics query — product intelligence via ClickHouse.\n * Same cost profile as trace analytics.\n *\n * @see Design Choice TA10 — Enterstellar Analytics (business intelligence).\n * @see Bible §9.1 — `POST /v1/analytics/*`\n */\n BUSINESS_ANALYTICS: 5,\n\n /**\n * Contract certification — \"Enterstellar Certified\" audit initiation.\n * Highest cost — involves Fly.io microVM test execution (CR5).\n *\n * @see Design Choice GI5 — certification lifecycle.\n * @see Design Choice CR6 — certification costs 20 IPU.\n * @see Bible §9.1 — `POST /v1/contracts/:id/certify`\n */\n CERTIFY: 20,\n\n /**\n * Usage query — returns current IPU consumption and tier.\n * Free — necessary for clients to monitor their own usage.\n *\n * @see Bible §9.1 — `GET /v1/usage`\n */\n USAGE_QUERY: 0,\n\n /**\n * IPU ledger query — per-operation charge audit trail.\n * Free — billing transparency.\n *\n * @see Design Choice AM13 — IPU ledger exposure.\n * @see Bible §9.1 — `GET /v1/usage/ledger`\n */\n LEDGER_QUERY: 0,\n\n /**\n * Trace listing query — paginated trace retrieval.\n * Free — reading your own data is never charged.\n *\n * @see Bible §9.1 — `GET /v1/traces`\n */\n GET_TRACES: 0,\n\n /**\n * GDPR data deletion — initiate project data purge.\n * Free — compliance operations are never charged.\n *\n * @see Design Choice AG9 — two-phase delete.\n * @see Design Choice D110 — GDPR soft-delete.\n * @see Bible §9.1 — `DELETE /v1/project/:id/data`\n */\n DELETE_PROJECT_DATA: 0,\n} as const);\n\n// ---------------------------------------------------------------------------\n// IPU Cost Type\n// ---------------------------------------------------------------------------\n\n/**\n * Union type of all valid IPU cost values.\n *\n * Derived from the `IPU_COSTS` constant object. Useful for\n * type-constraining function parameters that accept IPU costs.\n *\n * Currently: `0 | 1 | 5 | 10 | 20`.\n */\nexport type IPUCostValue = (typeof IPU_COSTS)[keyof typeof IPU_COSTS];\n","/**\n * @module @enterstellar-ai/cloud/inference/cloud-forge-proxy\n * @description Proxies forge generation requests to Enterstellar Cloud.\n *\n * Provides the dual forge API mandated by SD6:\n * - `forge(options)` → `Promise<CloudResult<ComponentContract>>` — buffers\n * the full SSE stream and returns the complete contract.\n * - `stream(options)` → `AsyncGenerator<ForgeFragment>` — yields typed SSE\n * fragments as they arrive (progressive rendering).\n *\n * **IPU cost:** 10 per invocation (§9.1, CL2).\n * **Timeout:** 30s default (P99 = 10s, §8.9, F21).\n * **Idempotency:** `X-Idempotency-Key` sent on all requests (AM10).\n *\n * **Error policy (SD3):**\n * - Pre-flight quota exceeded → throw `CloudError` (`ENS-C4290`).\n * - 429 from server → throw `CloudError` (via transport).\n * - 5xx / network → retry 3× then throw `ENS-5005` (via transport).\n * - No more \"degraded\" return values — errors always throw.\n *\n * @see Design Choice SD6 — dual API: `forge()` + `forge.stream()`.\n * @see Design Choice CL2 — CloudForge = 10 IPU.\n * @see Design Choice SD3 — throw on 429, never silent degrade.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Bible §9.1 — `POST /v1/forge`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { ComponentContract } from '@enterstellar-ai/types';\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CloudSSETransport, CloudSSEConfig } from '../transport/cloud-sse.js';\nimport type {\n CloudIPU,\n CloudResult,\n ForgeFragment,\n ForgeOptions,\n} from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\nimport { OPERATION_TIMEOUTS } from '../transport/cloud-http.js';\nimport { createQuotaExceededError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// CloudForgeProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for Cloud Forge generation — dual API (SD6).\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface CloudForgeProxy {\n /**\n * Generate a `ComponentContract` via Cloud Forge (Promise API).\n *\n * Buffers the full server response and returns the complete contract\n * wrapped in `CloudResult<T>`.\n *\n * @param options - Forge generation options (intent + optional constraints).\n * @returns The complete contract with IPU metadata.\n *\n * @throws {CloudError} `ENS-C4290` if IPU quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n forge(options: ForgeOptions): Promise<CloudResult<ComponentContract>>;\n\n /**\n * Stream forge generation via Server-Sent Events (AsyncGenerator API).\n *\n * Delegates to `CloudSSETransport.stream()` and yields typed\n * `ForgeFragment` objects as the LLM generates the contract.\n *\n * @param options - Forge generation options (intent + optional constraints).\n * @yields {ForgeFragment} Typed SSE fragments in lifecycle order.\n *\n * @throws {CloudError} `ENS-C4290` if quota exceeded (before or during).\n * @throws {CloudError} `ENS-5005` on network error mid-stream.\n */\n stream(options: ForgeOptions): AsyncGenerator<ForgeFragment, void, undefined>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * Returns `null` if the client is in anonymous mode (AG8: all\n * `X-IPU-*` headers omitted) or if required headers are missing.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CloudForgeProxy} that routes forge requests to the\n * Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport for the Promise API.\n * @param sseTransport - The SSE transport for the streaming API.\n * @param tracker - The IPU tracker for cost recording and quota checks.\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @param sessionType - The session type from `CloudConfig` (D111).\n * @returns A `CloudForgeProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createCloudForgeProxy(transport, sseTransport, tracker, false, 'app');\n *\n * // Promise API:\n * const { data: contract, ipu } = await proxy.forge({ intent: 'card' });\n *\n * // Streaming API:\n * for await (const fragment of proxy.stream({ intent: 'card' })) {\n * console.log(fragment.type, fragment.data);\n * }\n * ```\n *\n * @see Design Choice SD6 — dual API.\n * @see Design Choice CL2 — CloudForge = 10 IPU.\n * @see Design Choice SD3 — throw on quota exceeded.\n * @internal\n */\nexport function createCloudForgeProxy(\n transport: CloudHttpTransport,\n sseTransport: CloudSSETransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n sessionType: string,\n): CloudForgeProxy {\n return {\n async forge(options: ForgeOptions): Promise<CloudResult<ComponentContract>> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // Throws CloudError instead of returning degraded.\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // Transport handles retry (SD5), 429 throw (SD3), timeout (F21).\n // ---------------------------------------------------------------\n const response = await transport.request<ComponentContract>({\n method: 'POST',\n path: '/v1/forge',\n body: {\n intent: options.intent,\n constraints: options.constraints,\n sessionType,\n },\n ipuCost: IPU_COSTS.FORGE,\n operationTimeout: OPERATION_TIMEOUTS.forge,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker with server headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // Record local cost estimate.\n tracker.record(IPU_COSTS.FORGE);\n\n // ---------------------------------------------------------------\n // Build CloudResult<ComponentContract> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // The transport guarantees `response.ok === true` at this point\n // (non-2xx throws). We still guard against null data defensively.\n const data = response.data;\n if (data === null) {\n throw createQuotaExceededError({\n code: 'ENS-C5000',\n message: 'Forge response contained no data',\n });\n }\n\n return { data, ipu };\n },\n\n async *stream(options: ForgeOptions): AsyncGenerator<ForgeFragment, void, undefined> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Delegate to SSE transport.\n // The SSE transport handles timeout, idempotency key,\n // header parsing, and fragment mapping.\n // ---------------------------------------------------------------\n const sseConfig: CloudSSEConfig = {\n body: {\n intent: options.intent,\n constraints: options.constraints,\n sessionType,\n },\n isAnonymous,\n };\n\n // Record local cost estimate upfront.\n // The SSE transport will throw on failure, and the cost\n // is already committed server-side when the 2xx is received.\n tracker.record(IPU_COSTS.FORGE);\n\n yield* sseTransport.stream(sseConfig);\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/inference/cloud-index-proxy\n * @description Proxies semantic search requests to Enterstellar Cloud.\n *\n * Sends a natural language query to `POST /v1/semantic-search` and\n * returns a `CloudResult<readonly SemanticSearchResult[]>` wrapping\n * the server's search results with IPU metadata (SD7).\n *\n * **IPU cost:** 1 per invocation (§9.1, CL2).\n * **Timeout:** 10s default.\n * **Idempotency:** `X-Idempotency-Key` sent (AM10, ipuCost > 0).\n *\n * **Error policy (SD3):**\n * - Pre-flight quota exceeded → throw `CloudError` (`ENS-C4290`).\n * - 429 from server → throw `CloudError` (via transport).\n * - 5xx / network → retry 3× then throw `ENS-5005` (via transport).\n *\n * @see Design Choice CL2 — cloud semantic search = 1 IPU.\n * @see Design Choice SD3 — throw on 429.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Design Choice SI5 — default `topK: 5`.\n * @see Bible §9.1 — `POST /v1/semantic-search`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { SemanticSearchResult } from '@enterstellar-ai/types';\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CloudIPU, CloudResult } from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\nimport { createQuotaExceededError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Default number of results to return from a semantic search.\n *\n * @see Design Choice SI5 — default `topK: 5`.\n */\nconst DEFAULT_TOP_K = 5;\n\n// ---------------------------------------------------------------------------\n// Server Response Shape\n// ---------------------------------------------------------------------------\n\n/**\n * Expected JSON response shape from `POST /v1/semantic-search`.\n *\n * @internal — used only for typing the transport response.\n */\ntype SemanticSearchResponse = {\n readonly results: readonly SemanticSearchResult[];\n};\n\n// ---------------------------------------------------------------------------\n// CloudIndexProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for Cloud Semantic Index search.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface CloudIndexProxy {\n /**\n * Search for components via Cloud Semantic Index.\n *\n * @param query - Natural language search query (intent string).\n * @param topK - Maximum number of results. @default 5 (SI5).\n * @returns Search results wrapped in `CloudResult<T>` with IPU metadata.\n *\n * @throws {CloudError} `ENS-C4290` if IPU quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n search(\n query: string,\n topK?: number,\n ): Promise<CloudResult<readonly SemanticSearchResult[]>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CloudIndexProxy} that routes semantic search requests\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker for cost recording and quota checks.\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `CloudIndexProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createCloudIndexProxy(transport, tracker, false);\n * const { data: results, ipu } = await proxy.search('patient vitals', 10);\n *\n * for (const result of results) {\n * console.log(result.componentName, result.score);\n * }\n * ```\n *\n * @see Design Choice CL2 — cloud semantic search = 1 IPU.\n * @see Design Choice SD3 — throw on quota exceeded.\n * @internal\n */\nexport function createCloudIndexProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): CloudIndexProxy {\n return {\n async search(\n query: string,\n topK: number = DEFAULT_TOP_K,\n ): Promise<CloudResult<readonly SemanticSearchResult[]>> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // Throws CloudError instead of returning degraded.\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // Transport handles retry (SD5), 429 throw (SD3), timeout (F21).\n // ---------------------------------------------------------------\n const response = await transport.request<SemanticSearchResponse>({\n method: 'POST',\n path: '/v1/semantic-search',\n body: { query, topK },\n ipuCost: IPU_COSTS.SEMANTIC_SEARCH,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker with server headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // Record local cost estimate.\n tracker.record(IPU_COSTS.SEMANTIC_SEARCH);\n\n // ---------------------------------------------------------------\n // Build CloudResult<readonly SemanticSearchResult[]> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // The transport guarantees `response.ok === true` at this point.\n // Guard against null data defensively.\n const data = response.data;\n const results: readonly SemanticSearchResult[] = data?.results ?? [];\n\n return { data: results, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/routing/cloud-router-proxy\n * @description Proxies intent routing requests to Enterstellar Cloud.\n *\n * Provides two methods:\n * - `route(intentHash)` → single prediction via `POST /v1/route`.\n * - `routeBatch(intentHashes)` → batch predictions via `POST /v1/route/batch`.\n *\n * **IPU cost:** 1 per intent (§9.1). Batch cost = N × 1 IPU.\n * **Timeout:** 10s default.\n * **Idempotency:** `X-Idempotency-Key` sent (AM10, ipuCost > 0).\n *\n * **Batch ordering guarantee (F19):** `result.data[i]` corresponds to\n * `intentHashes[i]`. The proxy does NOT reorder — the server preserves\n * input order.\n *\n * @see Design Choice IR2 — router prediction response shape.\n * @see Design Choice IR3 — empty predictions for unknown intents.\n * @see Design Choice IR5 — batch routing for pre-rendering.\n * @see Design Choice SD3 — throw on 429.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Bible §9.1 — `POST /v1/route`, `POST /v1/route/batch`.\n * @see Audit Finding F19 — batch ordering invariant.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CloudIPU, CloudResult, RouterPrediction } from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\nimport { createQuotaExceededError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// CloudRouterProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for Cloud Intent Router — single and batch prediction.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface CloudRouterProxy {\n /**\n * Predict the component for a single intent hash.\n *\n * @param intentHash - SHA-256 hash of the intent string.\n * @returns Ranked predictions with model metadata.\n *\n * @throws {CloudError} `ENS-C4290` if quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n route(intentHash: string): Promise<CloudResult<RouterPrediction>>;\n\n /**\n * Predict components for a batch of intent hashes (pre-rendering).\n *\n * @param intentHashes - Array of SHA-256 intent hashes to resolve.\n * @returns Array of predictions in the same order as input (F19).\n *\n * @throws {CloudError} `ENS-C4290` if quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n routeBatch(\n intentHashes: readonly string[],\n ): Promise<CloudResult<readonly RouterPrediction[]>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CloudRouterProxy} that routes intent prediction requests\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker for cost recording and quota checks.\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `CloudRouterProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createCloudRouterProxy(transport, tracker, false);\n *\n * // Single intent:\n * const { data } = await proxy.route('a1b2c3...');\n *\n * // Batch intents:\n * const { data: predictions } = await proxy.routeBatch(['a1b2c3...', 'd4e5f6...']);\n * ```\n *\n * @see Design Choice IR2 — prediction response shape.\n * @see Design Choice IR5 — batch routing.\n * @internal\n */\nexport function createCloudRouterProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): CloudRouterProxy {\n return {\n async route(intentHash: string): Promise<CloudResult<RouterPrediction>> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // ---------------------------------------------------------------\n const response = await transport.request<RouterPrediction>({\n method: 'POST',\n path: '/v1/route',\n body: { intentHash },\n ipuCost: IPU_COSTS.ROUTE,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker with server headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // Record local cost estimate.\n tracker.record(IPU_COSTS.ROUTE);\n\n // ---------------------------------------------------------------\n // Build CloudResult<RouterPrediction> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // Guard against null data defensively.\n const data: RouterPrediction = response.data ?? {\n predictions: [],\n metadata: { modelVersion: 'unknown', signalCount: 0 },\n };\n\n return { data, ipu };\n },\n\n async routeBatch(\n intentHashes: readonly string[],\n ): Promise<CloudResult<readonly RouterPrediction[]>> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Compute dynamic IPU cost: N × 1 per intent.\n // ---------------------------------------------------------------\n const batchCost = intentHashes.length * IPU_COSTS.ROUTE_BATCH_PER_INTENT;\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // ---------------------------------------------------------------\n const response = await transport.request<readonly RouterPrediction[]>({\n method: 'POST',\n path: '/v1/route/batch',\n body: { intentHashes },\n ipuCost: batchCost,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker with server headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // Record local cost estimate (dynamic batch cost).\n tracker.record(batchCost);\n\n // ---------------------------------------------------------------\n // Build CloudResult<readonly RouterPrediction[]> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // Guard against null data — return empty array.\n const data: readonly RouterPrediction[] = response.data ?? [];\n\n return { data, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/analytics/cloud-analytics-proxy\n * @description Proxies analytics queries to Enterstellar Cloud.\n *\n * Provides two methods mapping to two distinct Cloud endpoints:\n * - `analytics(query)` → `POST /v1/traces/analytics` — trace analytics\n * powered by the dedicated Analytics Worker (TA3) and ClickHouse (TA4).\n * - `businessAnalytics(query)` → `POST /v1/analytics/query` — product\n * intelligence analytics for the BI dashboard (TA10).\n *\n * Both accept an {@link AnalyticsQuery} (fixed `queryType` + optional\n * `filters`) and return `CloudResult<AnalyticsResult>` (SD7).\n *\n * **IPU cost:** 5 per invocation for both endpoints (§9.1, CL2).\n * **Timeout:** 30s default — OLAP queries can be slow (F21).\n * **Idempotency:** `X-Idempotency-Key` sent (AM10, ipuCost > 0).\n *\n * **HTTP method note (F17):** Bible §9.1 specifies `GET` for analytics\n * endpoints, but `AnalyticsQuery` requires a JSON body. The SDK uses\n * `POST` — a Bible §9.1 amendment has been flagged per audit finding F17.\n *\n * @see Design Choice TA3 — dedicated Analytics Worker.\n * @see Design Choice TA5 — fixed query types with filters.\n * @see Design Choice TA10 — Enterstellar Analytics (business intelligence).\n * @see Design Choice SD3 — throw on 429.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Audit Finding F17 — POST instead of GET for JSON body.\n * @see Audit Finding F21 — 30s timeout for OLAP queries.\n * @see Bible §9.1 — `POST /v1/traces/analytics`, `POST /v1/analytics/query`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type {\n AnalyticsQuery,\n AnalyticsResult,\n CloudIPU,\n CloudResult,\n} from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\nimport { OPERATION_TIMEOUTS } from '../transport/cloud-http.js';\nimport { createQuotaExceededError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// CloudAnalyticsProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for Cloud Analytics — trace analytics and business analytics.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface CloudAnalyticsProxy {\n /**\n * Query trace analytics from ClickHouse via the Analytics Worker.\n *\n * Proxies to `POST /v1/traces/analytics`. Fixed query types with\n * optional filters (TA5). Results are returned as generic rows —\n * the schema varies by `queryType`.\n *\n * **IPU cost:** 5 per invocation (§9.1).\n *\n * @param query - Analytics query with `queryType` and optional `filters`.\n * @returns Analytics result rows wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-C4290` if quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n analytics(query: AnalyticsQuery): Promise<CloudResult<AnalyticsResult>>;\n\n /**\n * Query business/product analytics from ClickHouse.\n *\n * Proxies to `POST /v1/analytics/query`. Separate from trace\n * analytics — powers the Business Intelligence dashboard (TA10).\n *\n * **IPU cost:** 5 per invocation (§9.1).\n *\n * @param query - Analytics query with `queryType` and optional `filters`.\n * @returns Analytics result rows wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-C4290` if quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n businessAnalytics(query: AnalyticsQuery): Promise<CloudResult<AnalyticsResult>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CloudAnalyticsProxy} that routes analytics queries\n * to the Enterstellar Cloud API.\n *\n * Both `analytics()` and `businessAnalytics()` share the same request\n * lifecycle — only the endpoint URL and IPU cost constant differ.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker for cost recording and quota checks.\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `CloudAnalyticsProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createCloudAnalyticsProxy(transport, tracker, false);\n *\n * const { data } = await proxy.analytics({\n * queryType: 'intent_patterns',\n * filters: { timeRange: '7d', limit: 100 },\n * });\n *\n * for (const row of data.rows) {\n * console.log(row);\n * }\n * ```\n *\n * @see Design Choice TA3 — dedicated Analytics Worker.\n * @see Design Choice TA5 — fixed query types.\n * @see Design Choice TA10 — Enterstellar Analytics (BI).\n * @internal\n */\nexport function createCloudAnalyticsProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): CloudAnalyticsProxy {\n /**\n * Shared implementation for both analytics endpoints.\n *\n * Executes the full request lifecycle: pre-flight check → transport\n * call → reconcile → record → build `CloudResult<AnalyticsResult>`.\n *\n * @param path - The API endpoint path (e.g., `'/v1/traces/analytics'`).\n * @param query - The analytics query payload.\n * @param costConstant - The IPU cost for this operation.\n * @returns Analytics result wrapped in `CloudResult<T>`.\n */\n async function executeAnalyticsRequest(\n path: string,\n query: AnalyticsQuery,\n costConstant: number,\n ): Promise<CloudResult<AnalyticsResult>> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // Uses 30s timeout for OLAP queries (F21).\n // POST method per F17 (JSON body cannot be sent via GET).\n // ---------------------------------------------------------------\n const response = await transport.request<AnalyticsResult>({\n method: 'POST',\n path,\n body: query,\n ipuCost: costConstant,\n operationTimeout: OPERATION_TIMEOUTS.analytics,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker with server headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // Record local cost estimate.\n tracker.record(costConstant);\n\n // ---------------------------------------------------------------\n // Build CloudResult<AnalyticsResult> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // Defensive fallback: if data is null, construct an empty result\n // with the original queryType for client-side discrimination.\n const data: AnalyticsResult = response.data ?? {\n rows: [],\n queryType: query.queryType,\n };\n\n return { data, ipu };\n }\n\n return {\n async analytics(query: AnalyticsQuery): Promise<CloudResult<AnalyticsResult>> {\n return executeAnalyticsRequest(\n '/v1/traces/analytics',\n query,\n IPU_COSTS.TRACE_ANALYTICS,\n );\n },\n\n async businessAnalytics(query: AnalyticsQuery): Promise<CloudResult<AnalyticsResult>> {\n return executeAnalyticsRequest(\n '/v1/analytics/query',\n query,\n IPU_COSTS.BUSINESS_ANALYTICS,\n );\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/traces/trace-submitter\n * @description Submits `AgentTrace` objects to Enterstellar Cloud for aggregation.\n *\n * Sends full `AgentTrace` payloads to `POST /v1/traces` for cloud-side\n * analytics, dashboard reporting, and Intent Router training data.\n *\n * **Triple consent gate (TA2, F13):**\n * Three independent checks must ALL pass before any network call:\n * 1. `CloudConfig.traceConsent` — client SDK flag (default `false`).\n * 2. `trace.consent.anonymizedAggregation` — per-trace consent field.\n * 3. Server-side `projects.trace_consent` — checked on the server.\n *\n * If either client-side check fails, the submission is silently skipped —\n * no network call, no IPU charge, no data leaves the device.\n *\n * **IPU cost:** 0 — trace submission is free (§9.1 corrected).\n * **No idempotency key:** 0-cost operations skip `X-Idempotency-Key` (F8).\n * **No pre-flight quota check:** Traces are free — quota is irrelevant.\n *\n * **Changes from v0.0.x:**\n * - IPU cost corrected: 5 → 0 (Bible §9.1: \"never charge for data collection\").\n * - Returns `CloudResult<{ accepted }>` instead of `CloudTraceResult` (SD7).\n * - Transport errors propagate as `CloudError` throws (SD3) — no longer\n * silently swallowed.\n * - `traceConsent` flag added as a factory parameter (TA2 dual-consent).\n *\n * @see Design Choice TA2 — dual-consent: client flag + server flag.\n * @see Design Choice SD3 — throw on error, never silent degrade.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Audit Finding F13 — mandatory client consent flag.\n * @see Principle L12 — ForgeSignal is mandatory; AgentTrace is opt-in.\n * @see Principle L15 — zero framework imports.\n * @see Bible §9.1 — `POST /v1/traces` (0 IPU).\n */\n\nimport type { AgentTrace } from '@enterstellar-ai/types';\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CloudIPU, CloudResult } from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-built result for consent-denied submissions.\n *\n * Returned immediately when either client-side consent check fails.\n * No network call, no IPU headers — `ipu` is `null`.\n *\n * Frozen to prevent accidental mutation.\n */\nconst CONSENT_DENIED_RESULT: CloudResult<{ readonly accepted: boolean }> = Object.freeze({\n data: Object.freeze({ accepted: false }),\n ipu: null,\n});\n\n// ---------------------------------------------------------------------------\n// Server Response Shape\n// ---------------------------------------------------------------------------\n\n/**\n * Expected JSON response shape from `POST /v1/traces`.\n *\n * @internal — used only for typing the transport response.\n */\ntype TraceSubmitResponse = {\n readonly accepted: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// TraceSubmitter Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Submitter for cloud-side `AgentTrace` aggregation.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface TraceSubmitter {\n /**\n * Submit an `AgentTrace` for cloud aggregation.\n *\n * **Triple consent gate (TA2, F13):**\n * 1. `CloudConfig.traceConsent` must be `true`.\n * 2. `trace.consent.anonymizedAggregation` must be `true`.\n * 3. Server checks `projects.trace_consent` (not our concern).\n *\n * If either client-side check fails, returns immediately with\n * `{ data: { accepted: false }, ipu: null }`.\n *\n * @param trace - The full `AgentTrace` to submit. Must have consent fields.\n * @returns Submission result wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n submitTrace(\n trace: AgentTrace,\n ): Promise<CloudResult<{ readonly accepted: boolean }>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link TraceSubmitter} that submits `AgentTrace` objects\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker (for reconciliation only — traces cost 0 IPU).\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @param traceConsent - The `CloudConfig.traceConsent` flag (TA2, default `false`).\n * @param sessionType - The session type from `CloudConfig` (D111).\n * @returns A `TraceSubmitter` instance.\n *\n * @example\n * ```ts\n * const submitter = createTraceSubmitter(transport, tracker, false, true, 'app');\n * const { data } = await submitter.submitTrace(trace);\n *\n * if (data.accepted) {\n * console.log('Trace submitted for cloud aggregation');\n * }\n * ```\n *\n * @see Design Choice TA2 — dual-consent gate.\n * @see Design Choice SD7 — universal return wrapper.\n * @internal\n */\nexport function createTraceSubmitter(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n traceConsent: boolean,\n sessionType: string,\n): TraceSubmitter {\n return {\n async submitTrace(\n trace: AgentTrace,\n ): Promise<CloudResult<{ readonly accepted: boolean }>> {\n // ---------------------------------------------------------------\n // Consent gate 1: CloudConfig.traceConsent (TA2, F13).\n //\n // If the client SDK flag is false, skip immediately.\n // This is the first line of defense — no data leaves the device.\n // ---------------------------------------------------------------\n if (!traceConsent) {\n return CONSENT_DENIED_RESULT;\n }\n\n // ---------------------------------------------------------------\n // Consent gate 2: per-trace consent field (L12/TL10).\n //\n // Each AgentTrace carries its own consent. The application\n // sets this based on user preference. If false, skip.\n // ---------------------------------------------------------------\n if (!trace.consent.anonymizedAggregation) {\n return CONSENT_DENIED_RESULT;\n }\n\n // ---------------------------------------------------------------\n // No pre-flight quota check — traces are free (0 IPU).\n // ---------------------------------------------------------------\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // ipuCost: 0 → no X-Idempotency-Key sent (AM10/F8).\n // Transport errors propagate as CloudError (SD3).\n // ---------------------------------------------------------------\n const response = await transport.request<TraceSubmitResponse>({\n method: 'POST',\n path: '/v1/traces',\n body: { trace, sessionType },\n ipuCost: IPU_COSTS.TRACE_SUBMIT,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker if server provides headers.\n // For 0-IPU endpoints the server may omit these (AG8).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // No local cost recording — traces are free.\n\n // ---------------------------------------------------------------\n // Build CloudResult<{ accepted: boolean }> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n const accepted = response.data?.accepted ?? true;\n\n return { data: { accepted }, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/signals/signal-submitter\n * @description Submits `ForgeSignal` objects to Enterstellar Cloud.\n *\n * Proxies signal submission to `POST /v1/signals`. This is the **only\n * method that works in anonymous mode** (`pk_anon_*` keys, SD1/SD4).\n *\n * **IPU cost:** 0 — signal ingestion is free (§9.1). Data collection\n * is Enterstellar's #1 strategic asset — never charge for it.\n *\n * **No idempotency key:** 0-cost operations do not require\n * `X-Idempotency-Key` (AM10/F8).\n *\n * **No pre-flight quota check:** Signals are free — quota status\n * is irrelevant. The only failure mode is network/server error.\n *\n * **Consent model:** ForgeSignals are **mandatory** (L12). Unlike\n * `AgentTrace` (opt-in, consent-gated), signals are the core telemetry\n * data that feeds the Intent Router. The consent model is enforced at\n * the `@enterstellar-ai/telemetry` layer, not here.\n *\n * @see Design Choice SD1 — anonymous mode: only `submitSignal()` available.\n * @see Design Choice SD4 — `@enterstellar-ai/telemetry` uses SDK with `pk_anon`.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Principle L12 — ForgeSignal is mandatory; AgentTrace is opt-in.\n * @see Principle L15 — zero framework imports.\n * @see Bible §9.1 — `POST /v1/signals` (0 IPU).\n */\n\nimport type { ForgeSignal } from '@enterstellar-ai/types';\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CloudIPU, CloudResult } from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\n\n// ---------------------------------------------------------------------------\n// Server Response Shape\n// ---------------------------------------------------------------------------\n\n/**\n * Expected JSON response shape from `POST /v1/signals`.\n *\n * @internal — used only for typing the transport response.\n */\ntype SignalSubmitResponse = {\n readonly accepted: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// SignalSubmitter Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Submitter for Cloud ForgeSignal ingestion.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface SignalSubmitter {\n /**\n * Submit a `ForgeSignal` to the Cloud corpus.\n *\n * Works in both full mode and anonymous mode. This is the only\n * SDK operation available with `pk_anon_*` keys (SD1).\n *\n * @param signal - The `ForgeSignal` from `@enterstellar-ai/telemetry`.\n * @returns Acceptance confirmation wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n submitSignal(\n signal: ForgeSignal,\n ): Promise<CloudResult<{ readonly accepted: boolean }>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * For 0-IPU endpoints, the server may omit `X-IPU-*` headers (AG8).\n * In that case, or in anonymous mode, returns `null`.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link SignalSubmitter} that submits `ForgeSignal` objects\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker (for reconciliation only — signals cost 0 IPU).\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @param sessionType - The session type from `CloudConfig` (D111).\n * @returns A `SignalSubmitter` instance.\n *\n * @example\n * ```ts\n * const submitter = createSignalSubmitter(transport, tracker, true, 'app');\n * const { data } = await submitter.submitSignal(signal);\n *\n * if (data.accepted) {\n * console.log('Signal ingested for Intent Router training');\n * }\n * ```\n *\n * @see Design Choice SD1 — anonymous mode: only signals.\n * @see Design Choice SD4 — `@enterstellar-ai/telemetry` uses `pk_anon`.\n * @internal\n */\nexport function createSignalSubmitter(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n sessionType: string,\n): SignalSubmitter {\n return {\n async submitSignal(\n signal: ForgeSignal,\n ): Promise<CloudResult<{ readonly accepted: boolean }>> {\n // ---------------------------------------------------------------\n // No pre-flight quota check — signals are free (0 IPU).\n // ---------------------------------------------------------------\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // ipuCost: 0 → no X-Idempotency-Key sent (AM10/F8).\n // ---------------------------------------------------------------\n const response = await transport.request<SignalSubmitResponse>({\n method: 'POST',\n path: '/v1/signals',\n body: { ...signal, sessionType },\n ipuCost: IPU_COSTS.SIGNAL_SUBMIT,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker if server provides headers.\n // For 0-IPU endpoints the server may omit these (AG8),\n // but if present, we accept them for future-proofing.\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // No local cost recording — signals are free.\n\n // ---------------------------------------------------------------\n // Build CloudResult<{ accepted: boolean }> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n const accepted = response.data?.accepted ?? true;\n\n return { data: { accepted }, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/operations/traces-query-proxy\n * @description Proxies paginated trace listing requests to Enterstellar Cloud.\n *\n * Provides `getTraces(options?)` → `GET /v1/traces` — paginated trace\n * listing with cursor-based pagination, filterable by `correlationId`\n * and/or `threadId`.\n *\n * **IPU cost:** 0 (§9.1). Reading your own data is always free.\n * **No idempotency key:** 0-cost operations skip `X-Idempotency-Key` (F8).\n * **No pre-flight quota check:** Free operations — quota is irrelevant.\n *\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Bible §9.1 — `GET /v1/traces`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type {\n CloudIPU,\n CloudResult,\n TraceListOptions,\n TracePage,\n} from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\n\n// ---------------------------------------------------------------------------\n// TracesQueryProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for paginated trace listing.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface TracesQueryProxy {\n /**\n * Query traces for the authenticated project.\n *\n * @param options - Pagination and filter options. All optional.\n * @returns Paginated trace listing wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n getTraces(options?: TraceListOptions): Promise<CloudResult<TracePage>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a URL query string from an object of optional parameters.\n *\n * Only includes keys whose values are not `undefined` and not `null`.\n * Returns an empty string if no parameters are provided.\n *\n * @param params - Key-value pairs to serialize as query parameters.\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction buildQueryString(\n params: Readonly<Record<string, string | number | undefined | null>>,\n): string {\n const entries: string[] = [];\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n entries.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,\n );\n }\n }\n\n return entries.length > 0 ? `?${entries.join('&')}` : '';\n}\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * For 0-IPU endpoints, the server may omit `X-IPU-*` headers (AG8).\n * In that case, or in anonymous mode, returns `null`.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link TracesQueryProxy} that routes paginated trace queries\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker (for reconciliation only — queries cost 0 IPU).\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `TracesQueryProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createTracesQueryProxy(transport, tracker, false);\n *\n * // First page:\n * const { data: page } = await proxy.getTraces({ limit: 20 });\n *\n * // Next page:\n * if (page.hasMore && page.cursor !== null) {\n * const { data: nextPage } = await proxy.getTraces({ cursor: page.cursor });\n * }\n * ```\n *\n * @internal\n */\nexport function createTracesQueryProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): TracesQueryProxy {\n return {\n async getTraces(\n options?: TraceListOptions,\n ): Promise<CloudResult<TracePage>> {\n // ---------------------------------------------------------------\n // No pre-flight quota check — queries are free (0 IPU).\n // ---------------------------------------------------------------\n\n // ---------------------------------------------------------------\n // Build query string from optional parameters.\n // Only non-undefined values are included.\n // ---------------------------------------------------------------\n const queryString = buildQueryString({\n cursor: options?.cursor,\n limit: options?.limit,\n correlation_id: options?.correlationId,\n thread_id: options?.threadId,\n });\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // GET request — no body, params in URL.\n // ipuCost: 0 → no X-Idempotency-Key (F8).\n // ---------------------------------------------------------------\n const response = await transport.request<TracePage>({\n method: 'GET',\n path: `/v1/traces${queryString}`,\n ipuCost: IPU_COSTS.GET_TRACES,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker if server provides headers.\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // ---------------------------------------------------------------\n // Build CloudResult<TracePage> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n const data: TracePage = response.data ?? {\n items: [],\n cursor: null,\n hasMore: false,\n };\n\n return { data, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/operations/certify-proxy\n * @description Proxies contract certification initiation to Enterstellar Cloud.\n *\n * Sends a certification request to `POST /v1/contracts/:id/certify`.\n * This starts the asynchronous \"Enterstellar Certified\" lifecycle (GI5):\n * `none → pending → running → certified | failed`.\n *\n * The SDK returns the initial `pending` state with a polling URL.\n * The caller is responsible for polling `GET /v1/contracts/:id`\n * to check `certification_status` for completion (CR10).\n *\n * **IPU cost:** 20 per invocation (§9.1, CR6) — highest per-operation cost.\n * **Timeout:** 90s default (CR5: max 60s microVM runtime + overhead, F21).\n * **Idempotency:** `X-Idempotency-Key` sent (AM10). Critical at 20 IPU —\n * prevents double-charge if the response is lost in transit.\n *\n * @see Design Choice GI5 — certification lifecycle state machine.\n * @see Design Choice CR5 — Fly.io microVM, max 60s runtime.\n * @see Design Choice CR6 — certification costs 20 IPU.\n * @see Design Choice CR10 — polling-based notification.\n * @see Design Choice SD3 — throw on 429.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Audit Finding F14 — `CertifyResult` type defined per GI5 shape.\n * @see Audit Finding F21 — 90s timeout for certification.\n * @see Bible §9.1 — `POST /v1/contracts/:id/certify`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CertifyResult, CloudIPU, CloudResult } from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\nimport { OPERATION_TIMEOUTS } from '../transport/cloud-http.js';\nimport { createQuotaExceededError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// Server Response Shape\n// ---------------------------------------------------------------------------\n\n/**\n * Expected JSON response shape from `POST /v1/contracts/:id/certify`.\n *\n * The server returns a `202 Accepted` with the certification job status.\n *\n * @internal — used only for typing the transport response.\n */\ntype CertifyResponse = {\n readonly status: 'pending';\n readonly pollUrl: string;\n};\n\n// ---------------------------------------------------------------------------\n// CertifyProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for \"Enterstellar Certified\" contract certification initiation.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface CertifyProxy {\n /**\n * Initiate certification for a published contract.\n *\n * @param contractId - The contract ID to certify (e.g., `'comp_01HYX...'`).\n * @returns Pending status with polling URL, wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-C4290` if quota exceeded (SD3).\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n certify(contractId: string): Promise<CloudResult<CertifyResult>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link CertifyProxy} that routes certification requests\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker for cost recording and quota checks.\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `CertifyProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createCertifyProxy(transport, tracker, false);\n * const { data } = await proxy.certify('comp_01HYX...');\n *\n * // data.status === 'pending'\n * // Poll data.pollUrl for completion via @enterstellar-ai/global-index.\n * ```\n *\n * @see Design Choice GI5 — certification lifecycle.\n * @see Design Choice CR6 — 20 IPU cost.\n * @internal\n */\nexport function createCertifyProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): CertifyProxy {\n return {\n async certify(contractId: string): Promise<CloudResult<CertifyResult>> {\n // ---------------------------------------------------------------\n // Pre-flight quota check (SD3).\n // Critical at 20 IPU — avoid initiating a costly operation\n // that will be rejected by the server.\n // ---------------------------------------------------------------\n if (tracker.isOverQuota()) {\n throw createQuotaExceededError({\n code: 'ENS-C4290',\n message: 'IPU quota exceeded (pre-flight check)',\n });\n }\n\n // ---------------------------------------------------------------\n // Build dynamic path with contractId.\n // Defensive encodeURIComponent — IDs are ULID-prefixed\n // (alphanumeric + underscore), but we encode just in case.\n // ---------------------------------------------------------------\n const path = `/v1/contracts/${encodeURIComponent(contractId)}/certify`;\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // 90s timeout (CR5: max 60s microVM + overhead).\n // X-Idempotency-Key sent (AM10, ipuCost = 20 > 0).\n // ---------------------------------------------------------------\n const response = await transport.request<CertifyResponse>({\n method: 'POST',\n path,\n ipuCost: IPU_COSTS.CERTIFY,\n operationTimeout: OPERATION_TIMEOUTS.certify,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker with server headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // Record local cost estimate.\n tracker.record(IPU_COSTS.CERTIFY);\n\n // ---------------------------------------------------------------\n // Build CloudResult<CertifyResult> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // Defensive fallback — should always be present on 2xx.\n const data: CertifyResult = response.data ?? {\n status: 'pending',\n pollUrl: `/v1/contracts/${encodeURIComponent(contractId)}`,\n };\n\n return { data, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/operations/ledger-query-proxy\n * @description Proxies paginated IPU ledger queries to Enterstellar Cloud.\n *\n * Provides `getLedger(options?)` → `GET /v1/usage/ledger` — paginated\n * IPU ledger listing with cursor-based pagination. Returns per-operation\n * IPU charges for billing audit and verification.\n *\n * **IPU cost:** 0 (§9.1). Billing transparency — never charge to view\n * your own charges.\n * **No idempotency key:** 0-cost operations skip `X-Idempotency-Key` (F8).\n * **No pre-flight quota check:** Free operations — quota is irrelevant.\n *\n * @see Design Choice AM13 — IPU ledger exposure to customers.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Bible §9.1 — `GET /v1/usage/ledger`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type {\n CloudIPU,\n CloudResult,\n LedgerListOptions,\n LedgerPage,\n} from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\n\n// ---------------------------------------------------------------------------\n// LedgerQueryProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for paginated IPU ledger listing.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface LedgerQueryProxy {\n /**\n * Query the per-operation IPU ledger.\n *\n * @param options - Pagination options. All optional.\n * @returns Paginated ledger entries wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n getLedger(options?: LedgerListOptions): Promise<CloudResult<LedgerPage>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a URL query string from an object of optional parameters.\n *\n * Only includes keys whose values are not `undefined` and not `null`.\n * Returns an empty string if no parameters are provided.\n *\n * @param params - Key-value pairs to serialize as query parameters.\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction buildQueryString(\n params: Readonly<Record<string, string | number | undefined | null>>,\n): string {\n const entries: string[] = [];\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n entries.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,\n );\n }\n }\n\n return entries.length > 0 ? `?${entries.join('&')}` : '';\n}\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * For 0-IPU endpoints, the server may omit `X-IPU-*` headers (AG8).\n * In that case, or in anonymous mode, returns `null`.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link LedgerQueryProxy} that routes paginated ledger queries\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker (for reconciliation only — queries cost 0 IPU).\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `LedgerQueryProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createLedgerQueryProxy(transport, tracker, false);\n *\n * // First page:\n * const { data: page } = await proxy.getLedger({ limit: 50 });\n *\n * // Iterate through entries:\n * for (const entry of page.items) {\n * console.log(entry); // { operation, ipu_cost, timestamp, request_id }\n * }\n *\n * // Next page:\n * if (page.hasMore && page.cursor !== null) {\n * const { data: nextPage } = await proxy.getLedger({ cursor: page.cursor });\n * }\n * ```\n *\n * @see Design Choice AM13 — IPU ledger exposure.\n * @internal\n */\nexport function createLedgerQueryProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): LedgerQueryProxy {\n return {\n async getLedger(\n options?: LedgerListOptions,\n ): Promise<CloudResult<LedgerPage>> {\n // ---------------------------------------------------------------\n // No pre-flight quota check — queries are free (0 IPU).\n // ---------------------------------------------------------------\n\n // ---------------------------------------------------------------\n // Build query string from optional parameters.\n // ---------------------------------------------------------------\n const queryString = buildQueryString({\n cursor: options?.cursor,\n limit: options?.limit,\n });\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // GET request — no body, params in URL.\n // ipuCost: 0 → no X-Idempotency-Key (F8).\n // ---------------------------------------------------------------\n const response = await transport.request<LedgerPage>({\n method: 'GET',\n path: `/v1/usage/ledger${queryString}`,\n ipuCost: IPU_COSTS.LEDGER_QUERY,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker if server provides headers.\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // ---------------------------------------------------------------\n // Build CloudResult<LedgerPage> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n const data: LedgerPage = response.data ?? {\n items: [],\n cursor: null,\n hasMore: false,\n };\n\n return { data, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/operations/data-deletion-proxy\n * @description Proxies GDPR right-to-delete requests to Enterstellar Cloud.\n *\n * Sends a data deletion request to `DELETE /v1/project/:id/data`.\n * This initiates the two-phase delete process (AG9, D110):\n *\n * 1. **Immediate soft-delete:** D1 rows get `deleted_at = NOW()`.\n * Soft-deleted data is excluded from all queries immediately.\n * 2. **Background hard-purge:** A queue-triggered Worker permanently\n * removes data from D1, R2 (object storage), Vectorize (embeddings),\n * and ClickHouse (analytics) within 72 hours.\n *\n * **⚠ IRREVERSIBLE:** Once initiated, the soft-delete is immediate\n * and the hard-purge is queued. There is no undo mechanism. This\n * operation is designed for GDPR Article 17 (\"right to erasure\")\n * compliance.\n *\n * **IPU cost:** 0 (§9.1). Compliance operations are never charged.\n * **No idempotency key:** 0-cost operations skip `X-Idempotency-Key` (F8).\n * **No pre-flight quota check:** Free operations — quota is irrelevant.\n * **Fire-and-forget (F16):** Server returns `202 Accepted` immediately.\n * No `jobId` or polling mechanism — deletion completes asynchronously.\n *\n * @see Design Choice AG9 — two-phase delete: soft-delete + background purge.\n * @see Design Choice D110 — GDPR soft-delete with queue-based hard-purge.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Audit Finding F16 — fire-and-forget, no `jobId`.\n * @see Bible §9.1 — `DELETE /v1/project/:id/data`.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type { IPUTracker } from '../metering/ipu-tracker.js';\nimport type { CloudHttpTransport } from '../transport/cloud-http.js';\nimport type { CloudIPU, CloudResult } from '../types.js';\n\nimport { IPU_COSTS } from '../metering/ipu-costs.js';\n\n// ---------------------------------------------------------------------------\n// DataDeletionProxy Interface\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy for GDPR data deletion — project data purge.\n *\n * @internal — consumed by `createEnterstellarCloudClient()`, not exported publicly.\n */\nexport interface DataDeletionProxy {\n /**\n * Initiate GDPR right-to-delete for a project's data.\n *\n * **⚠ IRREVERSIBLE.** Immediately soft-deletes all project data\n * in D1 and queues a background hard-purge across all storage\n * systems (D1, R2, Vectorize, ClickHouse).\n *\n * Returns `202 Accepted` — fire-and-forget from the SDK's perspective.\n *\n * @param projectId - The project ID to delete data for.\n * @returns Acceptance confirmation wrapped in `CloudResult<T>`.\n *\n * @throws {CloudError} `ENS-5005` if all retries fail (SD5).\n */\n deleteProjectData(\n projectId: string,\n ): Promise<CloudResult<{ readonly accepted: boolean }>>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * For 0-IPU endpoints, the server may omit `X-IPU-*` headers (AG8).\n * In that case, or in anonymous mode, returns `null`.\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link DataDeletionProxy} that routes data deletion requests\n * to the Enterstellar Cloud API.\n *\n * @param transport - The shared HTTP transport (provides auth, timeout, retry).\n * @param tracker - The IPU tracker (for reconciliation only — deletion costs 0 IPU).\n * @param isAnonymous - Whether the client is in anonymous mode (`pk_anon`).\n * @returns A `DataDeletionProxy` instance.\n *\n * @example\n * ```ts\n * const proxy = createDataDeletionProxy(transport, tracker, false);\n *\n * // ⚠ IRREVERSIBLE — all project data will be purged.\n * const { data } = await proxy.deleteProjectData('proj_01HYX...');\n * console.log(data.accepted); // true — deletion initiated\n * ```\n *\n * @see Design Choice AG9 — two-phase delete.\n * @see Design Choice D110 — GDPR compliance.\n * @internal\n */\nexport function createDataDeletionProxy(\n transport: CloudHttpTransport,\n tracker: IPUTracker,\n isAnonymous: boolean,\n): DataDeletionProxy {\n return {\n async deleteProjectData(\n projectId: string,\n ): Promise<CloudResult<{ readonly accepted: boolean }>> {\n // ---------------------------------------------------------------\n // No pre-flight quota check — deletion is free (0 IPU).\n // ---------------------------------------------------------------\n\n // ---------------------------------------------------------------\n // Build dynamic path with projectId.\n // Defensive encodeURIComponent — IDs are ULID-prefixed\n // (alphanumeric + underscore), but we encode just in case.\n // ---------------------------------------------------------------\n const path = `/v1/project/${encodeURIComponent(projectId)}/data`;\n\n // ---------------------------------------------------------------\n // Execute the cloud API call.\n // DELETE method — no body.\n // ipuCost: 0 → no X-Idempotency-Key (F8).\n // Server returns 202 Accepted (fire-and-forget, F16).\n // ---------------------------------------------------------------\n const response = await transport.request<{ accepted: boolean }>({\n method: 'DELETE',\n path,\n ipuCost: IPU_COSTS.DELETE_PROJECT_DATA,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker if server provides headers.\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // ---------------------------------------------------------------\n // Build CloudResult<{ accepted: boolean }> (SD7).\n // Default to accepted: true on 2xx — server returned 202.\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n const accepted = response.data?.accepted ?? true;\n\n return { data: { accepted }, ipu };\n },\n };\n}\n","/**\n * @module @enterstellar-ai/cloud/create-cloud-client\n * @description Factory function for creating the Enterstellar Cloud SDK client.\n *\n * This is the top-level entry point of `@enterstellar-ai/cloud`. It validates the\n * configuration, initializes all internal modules — HTTP transport, SSE\n * transport, IPU tracker, and 8 proxy modules — and returns a fully\n * wired {@link EnterstellarCloudClient} instance.\n *\n * **13 methods + `forge.stream()` + `dispose()`** wired from:\n *\n * | Method | Proxy Module |\n * |:----------------------|:-------------------------------|\n * | `forge` / `.stream()` | `CloudForgeProxy` |\n * | `search` | `CloudIndexProxy` |\n * | `route` | `CloudRouterProxy` |\n * | `routeBatch` | `CloudRouterProxy` |\n * | `submitSignal` | `SignalSubmitter` |\n * | `submitTrace` | `TraceSubmitter` |\n * | `getTraces` | `TracesQueryProxy` |\n * | `analytics` | `CloudAnalyticsProxy` |\n * | `businessAnalytics` | `CloudAnalyticsProxy` |\n * | `getUsage` | (inline — direct transport) |\n * | `getLedger` | `LedgerQueryProxy` |\n * | `certify` | `CertifyProxy` |\n * | `deleteProjectData` | `DataDeletionProxy` |\n *\n * **Anonymous mode (SD1):**\n * When the API key starts with `pk_anon_`, only `submitSignal()` and\n * `dispose()` are available. All other methods throw `ENS-5004`.\n *\n * **Error policy:**\n * - Config validation errors → throw `CloudError` (`ENS-5001`) at creation.\n * - Post-dispose calls → throw `CloudError` (`ENS-5002`).\n * - Anonymous mode violations → throw `CloudError` (`ENS-5004`).\n * - Operational errors → delegated to proxy modules / transport (SD3, SD5).\n *\n * @see Bible §9.2 — SDK ↔ Cloud endpoint mapping.\n * @see Design Choice SD1 — auto-detect `pk_anon_` → anonymous mode.\n * @see Design Choice SD6 — dual forge API: `forge()` + `forge.stream()`.\n * @see Design Choice SD7 — every method returns `CloudResult<T>`.\n * @see Design Choice SD8 — default `baseUrl` = `https://api.enterstellar.dev`.\n * @see Design Choice TA2 — `traceConsent` defaults to `false`.\n * @see Design Choice D111 — `sessionType` defaults to `'app'`.\n * @see Design Choices CL1–CL5 — metering, billing, degradation.\n * @see Principle L15 — zero framework imports.\n */\n\nimport type {\n EnterstellarCloudClient,\n CloudConfig,\n CloudIPU,\n CloudResult,\n CloudUsage,\n ForgeFragment,\n ForgeOptions,\n SessionType,\n} from './types.js';\n\nimport { createCloudHttpTransport } from './transport/cloud-http.js';\nimport { createCloudSSETransport } from './transport/cloud-sse.js';\nimport { createIPUTracker } from './metering/ipu-tracker.js';\nimport { IPU_COSTS } from './metering/ipu-costs.js';\nimport { createCloudForgeProxy } from './inference/cloud-forge-proxy.js';\nimport { createCloudIndexProxy } from './inference/cloud-index-proxy.js';\nimport { createCloudRouterProxy } from './routing/cloud-router-proxy.js';\nimport { createCloudAnalyticsProxy } from './analytics/cloud-analytics-proxy.js';\nimport { createTraceSubmitter } from './traces/trace-submitter.js';\nimport { createSignalSubmitter } from './signals/signal-submitter.js';\nimport { createTracesQueryProxy } from './operations/traces-query-proxy.js';\nimport { createCertifyProxy } from './operations/certify-proxy.js';\nimport { createLedgerQueryProxy } from './operations/ledger-query-proxy.js';\nimport { createDataDeletionProxy } from './operations/data-deletion-proxy.js';\nimport {\n createAnonymousModeError,\n createConfigError,\n createDisposedError,\n} from './errors.js';\n\nimport type { ComponentContract } from '@enterstellar-ai/types';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Default Enterstellar Cloud API base URL.\n *\n * @see Design Choice SD8 — default with override via `CloudConfig.baseUrl`.\n */\nconst DEFAULT_BASE_URL = 'https://api.enterstellar.dev';\n\n/**\n * API key prefix for anonymous mode detection.\n *\n * @see Design Choice SD1 — auto-detect `pk_anon_` prefix.\n */\nconst ANONYMOUS_KEY_PREFIX = 'pk_anon_';\n\n/**\n * Default session type when not specified in config.\n *\n * @see Design Choice D111 — `session_type TEXT DEFAULT 'app'`.\n */\nconst DEFAULT_SESSION_TYPE: SessionType = 'app';\n\n// ---------------------------------------------------------------------------\n// Server Response Shape for GET /v1/usage\n// ---------------------------------------------------------------------------\n\n/**\n * Expected JSON response shape from `GET /v1/usage`.\n *\n * @internal — used only for typing the transport response.\n */\ntype UsageResponse = {\n readonly used: number;\n readonly limit: number;\n readonly tier: string;\n};\n\n// ---------------------------------------------------------------------------\n// Internal Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `CloudIPU` object from transport response headers.\n *\n * For 0-IPU endpoints (like usage query), the server may still\n * include `X-IPU-*` headers. Returns `null` in anonymous mode (AG8).\n *\n * @param ipuUsed - `X-IPU-Used` header value.\n * @param ipuRemaining - `X-IPU-Remaining` header value.\n * @param ipuCost - `X-IPU-Cost` header value.\n * @param isAnonymous - Whether the client is in anonymous mode.\n * @returns A `CloudIPU` object, or `null`.\n */\nfunction buildIPU(\n ipuUsed: number | undefined,\n ipuRemaining: number | undefined,\n ipuCost: number | undefined,\n isAnonymous: boolean,\n): CloudIPU | null {\n if (isAnonymous) {\n return null;\n }\n\n if (ipuUsed !== undefined && ipuRemaining !== undefined && ipuCost !== undefined) {\n return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an {@link EnterstellarCloudClient} — the primary public API of `@enterstellar-ai/cloud`.\n *\n * Validates the configuration, initializes the HTTP and SSE transports\n * with bearer token auth (CL4), creates an IPU tracker for hybrid metering\n * (CL1), wires all 8 proxy modules, and returns the client interface.\n *\n * **Anonymous mode (SD1):** If `config.apiKey` starts with `pk_anon_`,\n * only `submitSignal()` and `dispose()` are available. All other method\n * calls throw `CloudError` (`ENS-5004`).\n *\n * @param config - Client configuration. See {@link CloudConfig} for details.\n * @returns A fully wired `EnterstellarCloudClient` instance.\n *\n * @throws {CloudError} `ENS-5001` if `apiKey` is empty or missing.\n *\n * @example\n * ```ts\n * import { createEnterstellarCloudClient, CloudError } from '@enterstellar-ai/cloud';\n *\n * // Full mode:\n * const client = createEnterstellarCloudClient({\n * apiKey: process.env['ENTERSTELLAR_API_KEY']!,\n * traceConsent: true,\n * sessionType: 'app',\n * });\n *\n * try {\n * const { data: contract, ipu } = await client.forge({ intent: 'patient vitals' });\n * console.log(`Generated: ${contract.name}, IPU remaining: ${ipu?.remaining}`);\n * } catch (error) {\n * if (error instanceof CloudError && error.upgradeUrl) {\n * showUpgradePrompt(error.upgradeUrl);\n * }\n * } finally {\n * client.dispose();\n * }\n *\n * // Anonymous mode:\n * const anonClient = createEnterstellarCloudClient({ apiKey: 'pk_anon_abc123' });\n * await anonClient.submitSignal(signal); // ✓ Works\n * await anonClient.forge({ intent: 'x' }); // ✗ Throws ENS-5004\n * ```\n *\n * @see Bible §9.2 — SDK ↔ Cloud endpoint mapping.\n * @see Design Choice SD1 — anonymous mode auto-detection.\n * @see Design Choice SD6 — dual forge API.\n * @see Design Choice SD7 — universal `CloudResult<T>` return wrapper.\n * @see Design Choices CL1–CL5 — metering and billing.\n */\nexport function createEnterstellarCloudClient(config: CloudConfig): EnterstellarCloudClient {\n // -----------------------------------------------------------------------\n // Configuration Validation\n // -----------------------------------------------------------------------\n\n if (!config.apiKey || config.apiKey.trim().length === 0) {\n throw createConfigError('apiKey');\n }\n\n // -----------------------------------------------------------------------\n // Resolve Defaults\n // -----------------------------------------------------------------------\n\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const sessionType = config.sessionType ?? DEFAULT_SESSION_TYPE;\n const traceConsent = config.traceConsent ?? false;\n const isAnonymous = config.apiKey.startsWith(ANONYMOUS_KEY_PREFIX);\n\n // -----------------------------------------------------------------------\n // Wire Transport Layer\n // -----------------------------------------------------------------------\n\n const transport = createCloudHttpTransport({\n endpoint: baseUrl,\n apiKey: config.apiKey,\n timeoutMs: config.timeoutMs,\n });\n\n const sseTransport = createCloudSSETransport({\n endpoint: baseUrl,\n apiKey: config.apiKey,\n timeoutMs: config.timeoutMs,\n });\n\n // -----------------------------------------------------------------------\n // Wire IPU Tracker (CL1)\n // -----------------------------------------------------------------------\n\n const tracker = createIPUTracker();\n\n // -----------------------------------------------------------------------\n // Wire Proxy Modules (one per domain)\n // -----------------------------------------------------------------------\n\n const forgeProxy = createCloudForgeProxy(\n transport, sseTransport, tracker, isAnonymous, sessionType,\n );\n\n const indexProxy = createCloudIndexProxy(transport, tracker, isAnonymous);\n const routerProxy = createCloudRouterProxy(transport, tracker, isAnonymous);\n const analyticsProxy = createCloudAnalyticsProxy(transport, tracker, isAnonymous);\n\n const traceSubmitter = createTraceSubmitter(\n transport, tracker, isAnonymous, traceConsent, sessionType,\n );\n\n const signalSubmitter = createSignalSubmitter(\n transport, tracker, isAnonymous, sessionType,\n );\n\n const tracesQueryProxy = createTracesQueryProxy(transport, tracker, isAnonymous);\n const certifyProxy = createCertifyProxy(transport, tracker, isAnonymous);\n const ledgerQueryProxy = createLedgerQueryProxy(transport, tracker, isAnonymous);\n const dataDeletionProxy = createDataDeletionProxy(transport, tracker, isAnonymous);\n\n // -----------------------------------------------------------------------\n // Disposal State\n // -----------------------------------------------------------------------\n\n /** Whether `dispose()` has been called. */\n let disposed = false;\n\n /**\n * Guards against method calls after `dispose()`.\n *\n * @throws {CloudError} `ENS-5002` if the client has been disposed.\n */\n function assertNotDisposed(): void {\n if (disposed) {\n throw createDisposedError();\n }\n }\n\n /**\n * Guards against non-signal method calls in anonymous mode (SD1).\n *\n * @param method - The method name that was called (for error message).\n * @throws {CloudError} `ENS-5004` if in anonymous mode.\n */\n function assertNotAnonymous(method: string): void {\n if (isAnonymous) {\n throw createAnonymousModeError(method);\n }\n }\n\n // -----------------------------------------------------------------------\n // EnterstellarCloudClient Implementation\n // -----------------------------------------------------------------------\n\n // --- forge: callable + .stream() (SD6) ---\n // `Object.assign` creates a function that is also an object with a\n // `.stream` property. TypeScript sees this as `ForgeFunction & { stream }`.\n const forge = Object.assign(\n async (options: ForgeOptions): Promise<CloudResult<ComponentContract>> => {\n assertNotDisposed();\n assertNotAnonymous('forge');\n return forgeProxy.forge(options);\n },\n {\n stream(options: ForgeOptions): AsyncGenerator<ForgeFragment, void, undefined> {\n assertNotDisposed();\n assertNotAnonymous('forge.stream');\n return forgeProxy.stream(options);\n },\n },\n );\n\n return {\n // -------------------------------------------------------------------\n // Generation (SD6)\n // -------------------------------------------------------------------\n forge,\n\n // -------------------------------------------------------------------\n // Search\n // -------------------------------------------------------------------\n async search(query, topK) {\n assertNotDisposed();\n assertNotAnonymous('search');\n return indexProxy.search(query, topK);\n },\n\n // -------------------------------------------------------------------\n // Routing (IR2, IR5)\n // -------------------------------------------------------------------\n async route(intentHash) {\n assertNotDisposed();\n assertNotAnonymous('route');\n return routerProxy.route(intentHash);\n },\n\n async routeBatch(intentHashes) {\n assertNotDisposed();\n assertNotAnonymous('routeBatch');\n return routerProxy.routeBatch(intentHashes);\n },\n\n // -------------------------------------------------------------------\n // Signals (SD1, SD4) — works in anonymous mode\n // -------------------------------------------------------------------\n async submitSignal(signal) {\n assertNotDisposed();\n // No assertNotAnonymous — signals work in anonymous mode.\n return signalSubmitter.submitSignal(signal);\n },\n\n // -------------------------------------------------------------------\n // Traces (TA2)\n // -------------------------------------------------------------------\n async submitTrace(trace) {\n assertNotDisposed();\n assertNotAnonymous('submitTrace');\n return traceSubmitter.submitTrace(trace);\n },\n\n async getTraces(options) {\n assertNotDisposed();\n assertNotAnonymous('getTraces');\n return tracesQueryProxy.getTraces(options);\n },\n\n // -------------------------------------------------------------------\n // Analytics (TA3, TA5, TA10)\n // -------------------------------------------------------------------\n async analytics(query) {\n assertNotDisposed();\n assertNotAnonymous('analytics');\n return analyticsProxy.analytics(query);\n },\n\n async businessAnalytics(query) {\n assertNotDisposed();\n assertNotAnonymous('businessAnalytics');\n return analyticsProxy.businessAnalytics(query);\n },\n\n // -------------------------------------------------------------------\n // Billing (CL1) — inline, no separate proxy\n // -------------------------------------------------------------------\n async getUsage() {\n assertNotDisposed();\n assertNotAnonymous('getUsage');\n\n // ---------------------------------------------------------------\n // Execute GET /v1/usage.\n // ipuCost: 0 → no X-Idempotency-Key (F8).\n // Transport handles retry (SD5), throws on failure (SD3).\n // ---------------------------------------------------------------\n const response = await transport.request<UsageResponse>({\n method: 'GET',\n path: '/v1/usage',\n ipuCost: IPU_COSTS.USAGE_QUERY,\n });\n\n // ---------------------------------------------------------------\n // Reconcile IPU tracker from response headers (CL1).\n // ---------------------------------------------------------------\n if (response.ipuUsed !== undefined && response.ipuRemaining !== undefined) {\n tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);\n }\n\n // ---------------------------------------------------------------\n // Build CloudResult<CloudUsage> (SD7).\n // ---------------------------------------------------------------\n const ipu = buildIPU(\n response.ipuUsed,\n response.ipuRemaining,\n response.ipuCost,\n isAnonymous,\n );\n\n // Defensive fallback — transport guarantees 2xx at this point,\n // but guard against null data for safety.\n const data: CloudUsage = response.data !== null\n ? {\n used: response.data.used,\n limit: response.data.limit,\n tier: response.data.tier,\n }\n : { used: 0, limit: 0, tier: 'unknown' };\n\n return { data, ipu };\n },\n\n async getLedger(options) {\n assertNotDisposed();\n assertNotAnonymous('getLedger');\n return ledgerQueryProxy.getLedger(options);\n },\n\n // -------------------------------------------------------------------\n // Operations\n // -------------------------------------------------------------------\n async certify(contractId) {\n assertNotDisposed();\n assertNotAnonymous('certify');\n return certifyProxy.certify(contractId);\n },\n\n async deleteProjectData(projectId) {\n assertNotDisposed();\n assertNotAnonymous('deleteProjectData');\n return dataDeletionProxy.deleteProjectData(projectId);\n },\n\n // -------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------\n dispose(): void {\n // Idempotent — safe to call multiple times.\n // Never throws — always allowed, even in anonymous mode.\n disposed = true;\n },\n };\n}\n"]}
|