@elisym/sdk 0.25.3 → 0.25.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +29 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +29 -5
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +47 -28
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +6 -0
- package/dist/node.d.ts +6 -0
- package/dist/node.js +47 -28
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/primitives/encryption.ts","../src/constants.ts","../src/agent-store/writer.ts","../src/config/global-schema.ts","../src/config/global.ts","../src/transport/iroh.ts"],"names":["randomBytes","readFile","YAML"],"mappings":";;;;;;;;AAcA,IAAM,MAAA,GAAS,eAAA;AACf,IAAM,WAAA,GAAc,EAAA;AACpB,IAAM,SAAA,GAAY,EAAA;AAClB,IAAM,UAAA,GAAa,EAAA;AACnB,IAAM,UAAA,GAAa,EAAA;AAEnB,IAAM,WAAW,CAAA,IAAK,EAAA;AACtB,IAAM,QAAA,GAAW,CAAA;AACjB,IAAM,QAAA,GAAW,CAAA;AACjB,IAAM,aAAA,GAAgB,GAAA,GAAM,QAAA,GAAW,QAAA,GAAW,CAAA;AAG3C,SAAS,YAAY,KAAA,EAAwB;AAClD,EAAA,OAAO,KAAA,CAAM,WAAW,MAAM,CAAA;AAChC;AAGO,SAAS,aAAA,CAAc,WAAmB,UAAA,EAA4B;AAC3E,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,MAAM,IAAI,MAAM,+BAA+B,CAAA;AAAA,EACjD;AAEA,EAAA,MAAM,IAAA,GAAO,YAAY,WAAW,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,UAAA,CAAW,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY;AAAA,IACnD,CAAA,EAAG,QAAA;AAAA,IACH,CAAA,EAAG,QAAA;AAAA,IACH,CAAA,EAAG,QAAA;AAAA,IACH,MAAA,EAAQ;AAAA,GACT,CAAA;AACD,EAAA,MAAM,EAAA,GAAK,YAAY,SAAS,CAAA;AAEhC,EAAA,MAAM,MAAA,GAAS,cAAA,CAAe,aAAA,EAAe,GAAA,EAAK,EAAE,CAAA;AACpD,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,CAAC,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,MAAM,CAAA,EAAG,MAAA,CAAO,KAAA,EAAO,CAAC,CAAA;AAClF,EAAA,MAAM,GAAA,GAAM,OAAO,UAAA,EAAW;AAE9B,EAAA,MAAM,OAAA,GAAU,OAAO,MAAA,CAAO,CAAC,MAAM,EAAA,EAAI,SAAA,EAAW,GAAG,CAAC,CAAA;AACxD,EAAA,OAAO,MAAA,GAAS,OAAA,CAAQ,QAAA,CAAS,QAAQ,CAAA;AAC3C;AAGO,SAAS,aAAA,CAAc,WAAmB,UAAA,EAA4B;AAC3E,EAAA,IAAI,CAAC,WAAA,CAAY,SAAS,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAAA,EAC1E;AACA,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,MAAM,IAAI,MAAM,+BAA+B,CAAA;AAAA,EACjD;AAEA,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,CAAK,SAAA,CAAU,MAAM,MAAA,CAAO,MAAM,GAAG,QAAQ,CAAA;AACpE,EAAA,IAAI,OAAA,CAAQ,MAAA,GAAS,WAAA,GAAc,SAAA,GAAY,UAAA,EAAY;AACzD,IAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,EACnD;AAEA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,QAAA,CAAS,CAAA,EAAG,WAAW,CAAA;AAC5C,EAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,QAAA,CAAS,WAAA,EAAa,cAAc,SAAS,CAAA;AAChE,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,QAAA,CAAS,OAAA,CAAQ,SAAS,UAAU,CAAA;AACxD,EAAA,MAAM,aAAa,OAAA,CAAQ,QAAA,CAAS,cAAc,SAAA,EAAW,OAAA,CAAQ,SAAS,UAAU,CAAA;AAExF,EAAA,MAAM,GAAA,GAAM,UAAA,CAAW,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY;AAAA,IACnD,CAAA,EAAG,QAAA;AAAA,IACH,CAAA,EAAG,QAAA;AAAA,IACH,CAAA,EAAG,QAAA;AAAA,IACH,MAAA,EAAQ;AAAA,GACT,CAAA;AAED,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,aAAA,EAAe,GAAA,EAAK,EAAE,CAAA;AACxD,EAAA,QAAA,CAAS,WAAW,GAAG,CAAA;AAEvB,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,CAAC,QAAA,CAAS,MAAA,CAAO,UAAU,CAAA,EAAG,QAAA,CAAS,KAAA,EAAO,CAAC,CAAA;AAC/E,IAAA,OAAO,SAAA,CAAU,SAAS,MAAM,CAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAAA,EAC1E;AACF;ACCO,IAAM,QAAA,GAAW;AAAA,EAgBE;AAAA;AAAA;AAAA,EAIxB,qBAAA,EAAuB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKvB,oBAAA,EAAsB,IAMxB,CAAA;AAGO,IAAM,MAAA,GAAS;AAAA,EAgBK;AAAA;AAAA;AAAA;AAAA,EAIzB,aAAA,EAAe,UAuBjB,CAAA;AAEqB,IAAI,WAAA;AC8JzB,eAAsB,eAAA,CACpB,IAAA,EACA,IAAA,EACA,IAAA,EACe;AACf,EAAA,MAAM,MAAM,OAAA,CAAQ,IAAI,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAC9C,EAAA,MAAM,OAAA,GAAU,GAAG,IAAI,CAAA,KAAA,EAAQA,YAAY,CAAC,CAAA,CAAE,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA;AAC7D,EAAA,MAAM,SAAA,CAAU,OAAA,EAAS,IAAA,EAAM,EAAE,MAAM,CAAA;AACvC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,SAAS,IAAI,CAAA;AAAA,EAC5B,SAAS,CAAA,EAAG;AAEV,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,MAAA,EAAO,GAAI,MAAM,OAAO,kBAAkB,CAAA;AAClD,MAAA,MAAM,OAAO,OAAO,CAAA;AAAA,IACtB,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,MAAM,CAAA;AAAA,EACR;AACF;AChVO,IAAM,4BAAA,GAA+B,EACzC,MAAA,CAAO;AAAA,EACN,KAAA,EAAO,CAAA,CAAE,IAAA,CAAK,CAAC,QAAQ,CAAC,CAAA;AAAA,EACxB,KAAA,EAAO,CAAA,CACJ,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,GAAA,CAAI,EAAE,CAAA,CACN,KAAA,CAAM,aAAA,EAAe,sCAAsC,CAAA;AAAA,EAC9D,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,QAAA,EAAS;AAAA;AAAA;AAAA;AAAA,EAIzC,MAAA,EAAQ,CAAA,CACL,KAAA,CAAM,CAAC,EAAE,MAAA,EAAO,EAAG,CAAA,CAAE,MAAA,EAAQ,CAAC,CAAA,CAC9B,SAAA,CAAU,CAAC,UAAW,OAAO,KAAA,KAAU,QAAA,GAAW,MAAA,CAAO,KAAK,CAAA,GAAI,KAAA,CAAM,IAAA,EAAO,EAC/E,MAAA,CAAO,CAAC,KAAA,KAAU,iBAAA,CAAkB,KAAK,KAAK,CAAA,IAAK,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA,EAAG;AAAA,IACvE,OAAA,EAAS;AAAA,GACV;AACL,CAAC,EACA,MAAA,EAAO;AAEH,IAAM,kBAAA,GAAqB,EAC/B,MAAA,CAAO;AAAA,EACN,oBAAA,EAAsB,EAAE,KAAA,CAAM,4BAA4B,EAAE,GAAA,CAAI,EAAE,EAAE,QAAA;AACtE,CAAC,EACA,MAAA,EAAO;;;ACfV,SAAS,SAAS,CAAA,EAAqB;AACrC,EAAA,OACE,OAAO,MAAM,QAAA,IAAY,CAAA,KAAM,QAAQ,MAAA,IAAU,CAAA,IAAM,EAAuB,IAAA,KAAS,QAAA;AAE3F;AAOA,eAAsB,iBAAiB,IAAA,EAAqC;AAC1E,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,MAAMC,QAAAA,CAAS,IAAA,EAAM,OAAO,CAAA;AAAA,EACpC,SAAS,CAAA,EAAG;AACV,IAAA,IAAI,QAAA,CAAS,CAAC,CAAA,EAAG;AACf,MAAA,OAAO,EAAC;AAAA,IACV;AACA,IAAA,MAAM,CAAA;AAAA,EACR;AACA,EAAA,IAAI,GAAA,CAAI,IAAA,EAAK,KAAM,EAAA,EAAI;AACrB,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,MAAM,MAAA,GAAkBC,KAAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,OAAO,kBAAA,CAAmB,KAAA,CAAM,MAAA,IAAU,EAAE,CAAA;AAC9C;AAGA,eAAsB,iBAAA,CAAkB,MAAc,MAAA,EAAqC;AACzF,EAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,KAAA,CAAM,MAAM,CAAA;AACjD,EAAA,MAAM,IAAA,GAAOA,KAAAA,CAAK,SAAA,CAAU,SAAS,CAAA;AACrC,EAAA,MAAM,eAAA,CAAgB,IAAA,EAAM,IAAA,EAAM,GAAK,CAAA;AACzC;;;AC2DA,IAAM,6BAAA,GAAgC,mBAAA;AACtC,IAAM,kBAAA,GAAqB,MAAA;AAC3B,IAAM,gBAAA,GAAmB,MAAA;AAOzB,IAAM,eAAA,GAAkB,CAAC,eAAA,EAAiB,wBAAwB,CAAA;AAElE,eAAe,cAAA,GAAsC;AACnD,EAAA,IAAI,SAAA;AACJ,EAAA,KAAA,MAAW,YAAY,eAAA,EAAiB;AACtC,IAAA,IAAI;AACF,MAAA,OAAQ,MAAM,OAAO,QAAA,CAAA;AAAA,IACvB,SAAS,KAAA,EAAO;AACd,MAAA,SAAA,GAAY,KAAA;AAAA,IACd;AAAA,EACF;AACA,EAAA,MAAM,IAAI,KAAA;AAAA,IACR,8FAAA;AAAA,IACA,EAAE,OAAO,SAAA;AAAU,GACrB;AACF;AAGO,IAAM,gBAAA,GAAN,cAA+B,KAAA,CAAM;AAAC,CAAA;AAE7C,SAAS,WAAA,CACP,WACA,KAAA,EACiD;AACjD,EAAA,IAAI,KAAA;AACJ,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAe,CAAC,UAAU,MAAA,KAAW;AACvD,IAAA,KAAA,GAAQ,UAAA;AAAA,MACN,MAAM,OAAO,IAAI,gBAAA,CAAiB,GAAG,KAAK,CAAA,iBAAA,EAAoB,SAAS,CAAA,EAAA,CAAI,CAAC,CAAA;AAAA,MAC5E;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,MAAM,YAAA,CAAa,KAAK,CAAA,EAAE;AACtD;AAOO,SAAS,oBAAoB,OAAA,EAAwD;AAC1F,EAAA,IAAI,WAAA,GAAsE,IAAA;AAG1E,EAAA,IAAI,YAAA,GAAqC,IAAA;AAOzC,EAAA,MAAM,YAAY,MAAqB;AACrC,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,OAAO,YAAA;AAAA,IACT;AACA,IAAA,MAAM,OAAA,GAAU,WAAA;AAChB,IAAA,WAAA,GAAc,IAAA;AACd,IAAA,MAAM,WAAW,YAAY;AAC3B,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA;AAAA,MACF;AACA,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,IAAI,CAAA;AAC7C,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,OAAA,GAAU,WAAA,CAAY,QAAA,CAAS,oBAAA,EAAsB,oBAAoB,CAAA;AAC/E,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,MAAA,CAAO,IAAA,CAAK,KAAK,QAAA,EAAS,EAAG,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,MACnE,CAAA,CAAA,MAAQ;AAAA,MAER,CAAA,SAAE;AACA,QAAA,OAAA,CAAQ,MAAA,EAAO;AAAA,MACjB;AAAA,IACF,CAAA,GAAG;AACH,IAAA,YAAA,GAAe,OAAA,CAAQ,QAAQ,MAAM;AACnC,MAAA,YAAA,GAAe,IAAA;AAAA,IACjB,CAAC,CAAA;AACD,IAAA,OAAO,YAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,UAAU,YAA6D;AAE3E,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,MAAM,YAAA;AAAA,IACR;AACA,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,MAAM,WAAW,YAAY;AAC3B,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,OAAA,CAAQ,UAAA,IAAc,cAAA,GAAgB;AAC5D,QAAA,MAAM,OAAO,MAAM,MAAA,CAAO,IAAA,CAAK,UAAA,CAAW,QAAQ,SAAS,CAAA;AAC3D,QAAA,OAAO,EAAE,QAAQ,IAAA,EAAK;AAAA,MACxB,CAAA,GAAG;AACH,MAAA,WAAA,GAAc,OAAA;AAKd,MAAA,OAAA,CAAQ,MAAM,MAAM;AAClB,QAAA,IAAI,gBAAgB,OAAA,EAAS;AAC3B,UAAA,WAAA,GAAc,IAAA;AAAA,QAChB;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AACA,IAAA,OAAO,WAAA;AAAA,EACT,CAAA;AAKA,EAAA,MAAM,eAAA,GAAkB,OAAU,KAAA,EAAe,EAAA,KAAqC;AACpF,IAAA,MAAM,OAAA,GAAU,WAAA,CAAY,QAAA,CAAS,oBAAA,EAAsB,KAAK,CAAA;AAChE,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,QAAQ,IAAA,CAAK,CAAC,IAAG,EAAG,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,IACnD,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,gBAAA,EAAkB;AACrC,QAAA,KAAK,SAAA,EAAU;AAAA,MACjB;AACA,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,OAAA,CAAQ,MAAA,EAAO;AAAA,IACjB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,IAAA,KAChB,eAAA,CAAgB,oBAAoB,YAAY;AAC9C,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI,MAAM,OAAA,EAAQ;AACvC,IAAA,IAAI,IAAA,GAAO,CAAA;AACX,IAAA,MAAM,UAAU,MAAM,IAAI,OAAA,CAA0C,CAAC,SAAS,MAAA,KAAW;AACvF,MAAA,IAAA,CAAK,KAAA,CACF,WAAA;AAAA,QACC,IAAA;AAAA,QACA,KAAA;AAAA,QACA,MAAA,CAAO,aAAa,IAAA,EAAK;AAAA,QACzB,EAAE,MAAM,KAAA,EAAM;AAAA,QACd,CAAC,KAAK,QAAA,KAAa;AACjB,UAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,YAAA,MAAA,CAAO,GAAG,CAAA;AACV,YAAA;AAAA,UACF;AACA,UAAA,IAAI,QAAA,CAAS,UAAU,MAAA,EAAW;AAChC,YAAA,IAAA,GAAO,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA;AAAA,UACnC;AACA,UAAA,IAAI,QAAA,CAAS,YAAY,MAAA,EAAW;AAClC,YAAA,OAAA,CAAQ,SAAS,OAAO,CAAA;AAAA,UAC1B;AAAA,QACF;AAAA,OACF,CACC,MAAM,MAAM,CAAA;AAAA,IACjB,CAAC,CAAA;AACD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA,CAAM,KAAA;AAAA,MAC9B,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR;AAAA,KACF;AACA,IAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,CAAO,QAAA,IAAY,IAAA,EAAK;AAAA,EAC3C,CAAC,CAAA;AAEH,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KACjB,eAAA,CAAgB,qBAAqB,YAAY;AAC/C,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,OAAA,EAAQ;AAC/B,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,KAAA,CAAM,SAAS,KAAA,CAAM,IAAA,CAAK,KAAK,CAAC,CAAA;AAC3D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA,CAAM,KAAA;AAAA,MAC9B,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR;AAAA,KACF;AACA,IAAA,OAAO,EAAE,QAAQ,MAAA,CAAO,QAAA,IAAY,IAAA,EAAM,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAE;AAAA,EACjE,CAAC,CAAA;AAKH,EAAA,MAAM,eAAA,GAAkB,OACtB,SAAA,EACA,YAAA,KAC8C;AAC9C,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI,MAAM,OAAA,EAAQ;AACvC,IAAA,MAAM,QAAA,GAAW,YAAA,EAAc,QAAA,IAAY,MAAA,CAAO,aAAA;AAClD,IAAA,MAAM,SAAA,GAAY,YAAA,EAAc,SAAA,IAAa,QAAA,CAAS,qBAAA;AACtD,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,UAAA,CAAW,SAAS,CAAA;AACrD,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AAEpB,IAAA,MAAM,OAAA,GAAU,WAAA,CAAY,SAAA,EAAW,YAAY,CAAA;AACnD,IAAA,IAAI;AACF,MAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,QACjB,OAAA,CAAQ,OAAA;AAAA,QACR,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AACrC,UAAA,IAAA,CAAK,KAAA,CACF,SAAS,IAAA,EAAM,MAAA,CAAO,mBAAkB,EAAG,CAAC,KAAK,QAAA,KAAa;AAC7D,YAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,cAAA,MAAA,CAAO,GAAG,CAAA;AACV,cAAA;AAAA,YACF;AAEA,YAAA,IAAI,QAAA,CAAS,UAAU,KAAA,CAAA,IAAa,MAAA,CAAO,SAAS,KAAA,CAAM,IAAI,IAAI,QAAA,EAAU;AAC1E,cAAA,MAAA;AAAA,gBACE,IAAI,KAAA;AAAA,kBACF,CAAA,4BAAA,EAA+B,QAAA,CAAS,KAAA,CAAM,IAAI,MAAM,QAAQ,CAAA,MAAA;AAAA;AAClE,eACF;AACA,cAAA;AAAA,YACF;AACA,YAAA,IAAI,QAAA,CAAS,aAAa,KAAA,CAAA,IAAa,MAAA,CAAO,SAAS,QAAA,CAAS,MAAM,IAAI,QAAA,EAAU;AAClF,cAAA,MAAA;AAAA,gBACE,IAAI,KAAA,CAAM,CAAA,8CAAA,EAAiD,QAAQ,CAAA,OAAA,CAAS;AAAA,eAC9E;AACA,cAAA;AAAA,YACF;AACA,YAAA,IAAI,QAAA,CAAS,YAAY,KAAA,CAAA,EAAW;AAClC,cAAA,OAAA,EAAQ;AAAA,YACV;AAAA,UACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM,CAAA;AAAA,QACjB,CAAC;AAAA,OACF,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AAKd,MAAA,MAAM,KAAK,KAAA,CAAM,UAAA,CAAW,IAAI,CAAA,CAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAChD,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,OAAA,CAAQ,MAAA,EAAO;AAAA,IACjB;AAMA,IAAA,MAAM,eAAe,MAAA,CAAO,MAAM,KAAK,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA;AACvD,IAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,MAAA,MAAM,KAAK,KAAA,CAAM,UAAA,CAAW,IAAI,CAAA,CAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAChD,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,YAAY,CAAA,GAAA,EAAM,QAAQ,CAAA,MAAA,CAAQ,CAAA;AAAA,IACnF;AACA,IAAA,OAAO,EAAE,MAAM,IAAA,EAAK;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,OAClB,SAAA,EACA,IAAA,EACA,YAAA,KACkB;AAClB,IAAA,MAAM,EAAE,IAAA,EAAM,IAAA,KAAS,MAAM,eAAA,CAAgB,WAAW,YAAY,CAAA;AACpE,IAAA,MAAM,KAAK,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM,IAAA,EAAM,oBAAoB,gBAAgB,CAAA;AAAA,EAC1E,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,OACnB,SAAA,EACA,YAAA,KACwB;AACxB,IAAA,MAAM,EAAE,IAAA,EAAM,IAAA,KAAS,MAAM,eAAA,CAAgB,WAAW,YAAY,CAAA;AAGpE,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,KAAA,CAAM,YAAY,IAAI,CAAA;AAC/C,IAAA,OAAO,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,EAC9B,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,SAAA,KACf,eAAA,CAAgB,iBAAiB,YAAY;AAC3C,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI,MAAM,OAAA,EAAQ;AACvC,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,UAAA,CAAW,SAAS,CAAA;AACrD,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,KAAA,CAAM,KAAA;AAAA,MAC7B,MAAA,CAAO,IAAA;AAAA,MACP,MAAA,CAAO,MAAA;AAAA,MACP;AAAA,KACF;AACA,IAAA,OAAO,MAAM,QAAA,EAAS;AAAA,EACxB,CAAC,CAAA;AAEH,EAAA,MAAM,WAAW,YAA2B;AAC1C,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GAAU,WAAA;AAChB,IAAA,WAAA,GAAc,IAAA;AACd,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,IAAI,CAAA;AAC7C,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAA,EAAS;AAAA,IAClC;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,QAAA,EAAU,SAAA,EAAW,WAAA,EAAa,YAAA,EAAc,SAAS,QAAA,EAAS;AAC7E","file":"node.js","sourcesContent":["/**\n * Secret encryption/decryption for agent config files.\n * Uses scrypt (KDF) + AES-256-GCM (cipher).\n * Format: \"encrypted:v1:\" + base64(salt[16] + iv[12] + ciphertext + tag[16])\n *\n * scrypt params: N=2^17, r=8, p=1 (~128 MB RAM per derivation).\n *\n * Node.js/Bun only - not available in browsers. Reachable only via the\n * '@elisym/sdk/node' subpath, which browser bundlers will not resolve.\n */\n\nimport { Buffer } from 'node:buffer';\nimport { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';\n\nconst PREFIX = 'encrypted:v1:';\nconst SALT_LENGTH = 16;\nconst IV_LENGTH = 12;\nconst TAG_LENGTH = 16;\nconst KEY_LENGTH = 32; // AES-256\n// v1: N=2^17 (OWASP minimum). v2 will use N=2^20 with format migration.\nconst SCRYPT_N = 2 ** 17;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\nconst SCRYPT_MAXMEM = 128 * SCRYPT_N * SCRYPT_R * 2; // 2x the minimum required memory\n\n/** Check if a value is encrypted (has the encrypted:v1: prefix). */\nexport function isEncrypted(value: string): boolean {\n return value.startsWith(PREFIX);\n}\n\n/** Encrypt a plaintext secret with a passphrase. Returns \"encrypted:v1:base64...\". Node.js/Bun only. */\nexport function encryptSecret(plaintext: string, passphrase: string): string {\n if (!passphrase) {\n throw new Error('Passphrase must not be empty.');\n }\n\n const salt = randomBytes(SALT_LENGTH);\n const key = scryptSync(passphrase, salt, KEY_LENGTH, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n const iv = randomBytes(IV_LENGTH);\n\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n const payload = Buffer.concat([salt, iv, encrypted, tag]);\n return PREFIX + payload.toString('base64');\n}\n\n/** Decrypt an encrypted secret with a passphrase. Throws on wrong passphrase or corrupted data. Node.js/Bun only. */\nexport function decryptSecret(encrypted: string, passphrase: string): string {\n if (!isEncrypted(encrypted)) {\n throw new Error('Value is not encrypted (missing encrypted:v1: prefix).');\n }\n if (!passphrase) {\n throw new Error('Passphrase must not be empty.');\n }\n\n const payload = Buffer.from(encrypted.slice(PREFIX.length), 'base64');\n if (payload.length < SALT_LENGTH + IV_LENGTH + TAG_LENGTH) {\n throw new Error('Encrypted payload is too short.');\n }\n\n const salt = payload.subarray(0, SALT_LENGTH);\n const iv = payload.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const tag = payload.subarray(payload.length - TAG_LENGTH);\n const ciphertext = payload.subarray(SALT_LENGTH + IV_LENGTH, payload.length - TAG_LENGTH);\n\n const key = scryptSync(passphrase, salt, KEY_LENGTH, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n\n try {\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n return decrypted.toString('utf8');\n } catch {\n throw new Error('Decryption failed. Wrong passphrase or corrupted data.');\n }\n}\n","import type { Address } from '@solana/kit';\n\nexport const RELAYS = [\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n 'wss://relay.primal.net',\n 'wss://relay.snort.social',\n];\n\nexport const KIND_APP_HANDLER = 31990;\nexport const KIND_LONG_FORM_ARTICLE = 30023;\nexport const KIND_JOB_REQUEST_BASE = 5000;\nexport const KIND_JOB_RESULT_BASE = 6000;\nexport const KIND_JOB_FEEDBACK = 7000;\nexport const DEFAULT_KIND_OFFSET = 100;\n\n/** Discovery tag attached to elisym agent policy events (kind 30023). */\nexport const POLICY_T_TAG = 'elisym-policy';\n/** d-tag prefix for policy events: full d-tag = `<prefix><type>` (e.g. `elisym-policy-tos`). */\nexport const POLICY_D_TAG_PREFIX = 'elisym-policy-';\n/** Validation regex for policy `type` slug. Lowercase ASCII + hyphen, 1-32 chars, no leading/trailing hyphen. */\nexport const POLICY_TYPE_REGEX = /^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$/;\n\n/** Default job request kind (5000 + 100). */\nexport const KIND_JOB_REQUEST = KIND_JOB_REQUEST_BASE + DEFAULT_KIND_OFFSET;\n/** Default job result kind (6000 + 100). */\nexport const KIND_JOB_RESULT = KIND_JOB_RESULT_BASE + DEFAULT_KIND_OFFSET;\n\n/** Compute a job request kind from an offset (5000 + offset). */\nexport function jobRequestKind(offset: number): number {\n if (!Number.isInteger(offset) || offset < 0 || offset >= 1000) {\n throw new Error(`Invalid kind offset: ${offset}. Must be integer 0-999.`);\n }\n return KIND_JOB_REQUEST_BASE + offset;\n}\n\n/** Compute a job result kind from an offset (6000 + offset). */\nexport function jobResultKind(offset: number): number {\n if (!Number.isInteger(offset) || offset < 0 || offset >= 1000) {\n throw new Error(`Invalid kind offset: ${offset}. Must be integer 0-999.`);\n }\n return KIND_JOB_RESULT_BASE + offset;\n}\n\n/** Ephemeral ping/pong kinds (not stored by relays, forwarded in real-time). */\nexport const KIND_PING = 20200;\nexport const KIND_PONG = 20201;\n\nexport const LAMPORTS_PER_SOL = 1_000_000_000;\n\n/**\n * Solana program ID for the elisym protocol config (devnet deployment).\n *\n * The Anchor program at this address is the source of truth for fee bps,\n * treasury address, and admin rotation state. Read via `getProtocolConfig`.\n */\nexport const PROTOCOL_PROGRAM_ID_DEVNET = 'BrX1CRkSgvcjxBvc2bgc3QqgWjinusofDmeP7ZVxvwrE' as Address;\n\n/**\n * Read-only marker pubkey attached as a non-signer account to every elisym\n * payment transaction. Lets indexers enumerate every elisym tx network-wide\n * via a single `getSignaturesForAddress(ELISYM_PROTOCOL_TAG)` call,\n * independent of fee size or recipient.\n *\n * The account does not need to exist on-chain; including its pubkey as an\n * extra read-only account in the provider transfer instruction is enough for\n * Solana's tx-by-account index to pick it up. The corresponding secret key\n * was generated and discarded - the tag never signs and never holds funds.\n */\nexport const ELISYM_PROTOCOL_TAG = 'ELiZksgwDt41LaeuPDLkUfWgFXhGgVayTMP7L5nTSEL8' as Address;\n\nexport type ProtocolCluster = 'devnet' | 'mainnet' | 'localnet';\n\n/**\n * Resolve the elisym-config program ID for a given Solana cluster.\n * Mainnet is intentionally unsupported until the program ships there.\n */\nexport function getProtocolProgramId(cluster: ProtocolCluster): Address {\n switch (cluster) {\n case 'devnet':\n case 'localnet':\n return PROTOCOL_PROGRAM_ID_DEVNET;\n case 'mainnet':\n throw new Error('Protocol program is not deployed on mainnet yet');\n }\n}\n\n/** Default values for timeouts, retries, and batch sizes. */\nexport const DEFAULTS = {\n SUBSCRIPTION_TIMEOUT_MS: 120_000,\n PING_TIMEOUT_MS: 3_000,\n PING_RETRIES: 2,\n PING_CACHE_TTL_MS: 30_000,\n PAYMENT_EXPIRY_SECS: 600,\n BATCH_SIZE: 250,\n QUERY_TIMEOUT_MS: 15_000,\n EOSE_TIMEOUT_MS: 3_000,\n VERIFY_RETRIES: 10,\n VERIFY_INTERVAL_MS: 3_000,\n VERIFY_BY_REF_RETRIES: 15,\n VERIFY_BY_REF_INTERVAL_MS: 2_000,\n RESULT_RETRY_COUNT: 3,\n RESULT_RETRY_BASE_MS: 1_000,\n QUERY_MAX_CONCURRENCY: 6,\n VERIFY_SIGNATURE_LIMIT: 25,\n // Default ceiling for a single iroh file transfer (seed/fetch). A tunable\n // default, not a protocol constant - the transfer is resumable and its own\n // budget, decoupled from the result-wait window.\n IROH_FETCH_TIMEOUT_MS: 300_000,\n // Ceiling for a single iroh SEED (addFromPath/addBytes/share). Seeding is local\n // (hash + store-copy + ticket mint), so this is a generous backstop: it bounds\n // the JS await so a wedged native call surfaces as a thrown error (and triggers a\n // node reset) instead of an indefinite hang that stalls file delivery.\n IROH_SEED_TIMEOUT_MS: 120_000,\n // Ceiling for a single Blossom blob upload (PUT /upload). Large blobs (up to\n // LIMITS.MAX_FILE_SIZE) need far more than the 30s used for small media images.\n BLOSSOM_UPLOAD_TIMEOUT_MS: 300_000,\n // Ceiling for a single encrypted Blossom blob download (GET). Same budget as upload.\n BLOSSOM_FETCH_TIMEOUT_MS: 300_000,\n} as const;\n\n/** Protocol limits for input validation. */\nexport const LIMITS = {\n MAX_INPUT_LENGTH: 100_000,\n // NIP-44 v2 hard cap on encrypted plaintext: the pad() length prefix is a u16,\n // so the plaintext can be at most 65_535 BYTES (not chars). Encrypting anything\n // larger throws `invalid plaintext size` inside nip44Encrypt. This is the binding\n // limit for TARGETED (encrypted) jobs - lower than every relay's NIP-11 cap.\n NIP44_MAX_PLAINTEXT_BYTES: 65_535,\n // Spill threshold for encrypted content: above this many UTF-8 bytes, callers\n // route the text through iroh out-of-band instead of inlining it. A non-spilled\n // job is encrypted RAW (no envelope - see marketplace.submitJobRequest), so a\n // 60_000-byte input is a 60_000-byte plaintext; the ~5.5KB gap under the hard cap\n // is plain slack, and NIP44_MAX_PLAINTEXT_BYTES is the SDK backstop.\n MAX_ENCRYPTED_INLINE_BYTES: 60_000,\n // Ceiling above which a text/* attachment is NOT materialized back into a string\n // (re-inlined into SkillInput.data) - the consumer gets a filePath / explicit\n // fetch instead. Also bounds the in-memory git-diff buffer (memory-DoS guard).\n MAX_REINLINE_TEXT_BYTES: 4_194_304, // 4 MiB\n // Hard safety cap on a single file transferred via iroh, enforced on the\n // actual streamed bytes (never the sender-declared `size`). A tunable default;\n // providers may lower it per deployment.\n MAX_FILE_SIZE: 1_073_741_824, // 1 GiB\n // Cap for the ENCRYPTED Blossom path (web/SDK). The encrypt-then-upload flow is\n // whole-buffer in WebCrypto + BlossomService (~3x file-size peak RAM), so this is\n // deliberately far below MAX_FILE_SIZE to stay safe in a browser tab; larger files\n // use iroh. The relay enforces a ~128 MiB server-side backstop.\n MAX_BLOSSOM_ENCRYPTED_BYTES: 104_857_600, // 100 MiB\n\n MAX_TIMEOUT_SECS: 600,\n // Upper bound for execution budgets (`max_execution_secs` / `execution_timeout_secs`).\n // Distinct from MAX_TIMEOUT_SECS (the result-wait cap): execution budgets may be\n // hours, so this exists only to keep `secs * 1000` within Node's setTimeout limit\n // (2_147_483_647 ms) - a larger value overflows and fires the timer immediately.\n MAX_EXECUTION_SECS: 2_147_483,\n MAX_CAPABILITIES: 20,\n MAX_DESCRIPTION_LENGTH: 500,\n MAX_AGENT_NAME_LENGTH: 64,\n MAX_CAPABILITY_LENGTH: 64,\n MAX_POLICY_CONTENT_LENGTH: 50_000,\n MAX_POLICIES_PER_AGENT: 12,\n MAX_POLICY_TYPE_LENGTH: 32,\n MAX_POLICY_TITLE_LENGTH: 120,\n MAX_POLICY_SUMMARY_LENGTH: 280,\n MAX_POLICY_VERSION_LENGTH: 32,\n} as const;\n\nconst UTF8_ENCODER = new TextEncoder();\n\n/**\n * UTF-8 byte length of a string. All size guards on encrypted content measure\n * BYTES, not `String.length` (UTF-16 code units): NIP-44's plaintext cap is a\n * byte cap, so a multibyte string under the char cap can still exceed it.\n */\nexport function utf8ByteLength(value: string): number {\n return UTF8_ENCODER.encode(value).length;\n}\n","/**\n * Write agent files: elisym.yaml, .secrets.json, .gitignore, and create agent dirs.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport YAML from 'yaml';\nimport { encryptSecret, isEncrypted } from '../primitives/encryption';\nimport { agentPaths, type AgentPaths } from './paths';\nimport { elisymRootFor, type AgentSource } from './resolver';\nimport { ElisymYamlSchema, SecretsSchema, type ElisymYaml, type Secrets } from './schema';\nimport { renderInitialYaml } from './template';\n\n/** Bare per-agent `.iroh/` store dir; ignored at any depth under `.elisym/`. */\nconst IROH_GITIGNORE_ENTRY = '.iroh/';\n\nconst GITIGNORE_CONTENT = [\n '# elisym private state - do not commit.',\n '.secrets.json',\n '.media-cache.json',\n '.jobs.json',\n '.jobs.json.corrupt.*',\n '.customer-history.json',\n '.contacts.json',\n IROH_GITIGNORE_ENTRY,\n '',\n].join('\\n');\n\n/**\n * Ensure the project-local `.elisym/.gitignore` ignores the iroh blob store.\n * Idempotent migration for agents created before `.iroh/` was added to the\n * default ignore list; a no-op when no `.gitignore` exists (e.g. home-global,\n * which relies on directory permissions instead). The iroh store holds job\n * payloads in cleartext, so it must never be committed.\n */\nexport async function ensureGitignoreHasIrohEntry(elisymRoot: string): Promise<void> {\n const gitignorePath = join(elisymRoot, '.gitignore');\n let current: string;\n try {\n current = await readFile(gitignorePath, 'utf-8');\n } catch {\n return;\n }\n const hasEntry = current.split('\\n').some((line) => line.trim() === IROH_GITIGNORE_ENTRY);\n if (hasEntry) {\n return;\n }\n const separator = current.length === 0 || current.endsWith('\\n') ? '' : '\\n';\n await writeFile(gitignorePath, `${current}${separator}${IROH_GITIGNORE_ENTRY}\\n`, {\n mode: 0o644,\n });\n}\n\nexport interface CreateAgentDirOptions {\n target: AgentSource;\n name: string;\n cwd: string;\n /**\n * For `target: 'project'`: if no .elisym/ dir exists above cwd,\n * where should we create one? Defaults to cwd.\n */\n projectRoot?: string;\n}\n\nexport interface CreatedAgentDir {\n dir: string;\n paths: AgentPaths;\n source: AgentSource;\n createdNewElisymRoot: boolean;\n}\n\n/**\n * Create (or reuse) the directory layout for a new agent. Idempotent: if the\n * agent directory already exists, returns its paths without overwriting.\n * Writes `.gitignore` in project-local .elisym/ on first creation.\n */\nexport async function createAgentDir(options: CreateAgentDirOptions): Promise<CreatedAgentDir> {\n const { target, name, cwd, projectRoot } = options;\n\n const existingRoot = elisymRootFor(target, cwd);\n let elisymRoot: string;\n let createdNewElisymRoot = false;\n\n if (existingRoot) {\n elisymRoot = existingRoot;\n } else if (target === 'project') {\n elisymRoot = join(projectRoot ?? cwd, '.elisym');\n createdNewElisymRoot = true;\n } else {\n throw new Error('homeElisymDir should always exist conceptually - this is unreachable');\n }\n\n const agentDir = join(elisymRoot, name);\n const mode = target === 'home' ? 0o700 : 0o755;\n await mkdir(agentDir, { recursive: true, mode });\n await mkdir(join(agentDir, 'skills'), { recursive: true, mode });\n\n if (target === 'project') {\n const gitignorePath = join(elisymRoot, '.gitignore');\n await writeFileIfMissing(gitignorePath, GITIGNORE_CONTENT, 0o644);\n }\n\n return {\n dir: agentDir,\n paths: agentPaths(agentDir),\n source: target,\n createdNewElisymRoot,\n };\n}\n\n/** Write elisym.yaml atomically. Validates via Zod before writing. */\nexport async function writeYaml(agentDir: string, yaml: ElisymYaml): Promise<void> {\n const validated = ElisymYamlSchema.parse(yaml);\n const body = YAML.stringify(validated);\n const target = agentPaths(agentDir).yaml;\n await writeFileAtomic(target, body, 0o644);\n}\n\n/**\n * Write a brand-new elisym.yaml with descriptive header comments and\n * commented-out examples for unset optional fields. Use only at agent\n * creation time (CLI `init`, MCP `create_agent`). Subsequent edits go\n * through `writeYaml`, which discards comments.\n */\nexport async function writeYamlInitial(agentDir: string, yaml: ElisymYaml): Promise<void> {\n const validated = ElisymYamlSchema.parse(yaml);\n const body = renderInitialYaml(validated);\n const target = agentPaths(agentDir).yaml;\n await writeFileAtomic(target, body, 0o644);\n}\n\n/**\n * Create a skills directory placeholder file with a commented-out SKILL.md\n * template covering every supported field, so operators have a reference\n * for what they can declare without having to read source. The file is\n * named `EXAMPLE.md` (not `SKILL.md`) and lives directly in `skills/`\n * (not in a subdirectory), so the skill loader skips it - it's reference\n * material, not an active skill. To turn it into a real skill: copy it\n * into `skills/<your-skill-name>/SKILL.md` and uncomment the lines you\n * need.\n *\n * Idempotent: written with `wx` flag so we never overwrite an operator's\n * edits on re-run of `init`.\n */\nexport async function writeExampleSkillTemplate(agentDir: string): Promise<void> {\n const target = join(agentDir, 'skills', 'EXAMPLE.md');\n await writeFileIfMissing(target, EXAMPLE_SKILL_TEMPLATE, 0o644);\n}\n\nconst EXAMPLE_SKILL_TEMPLATE = `# elisym skill template\n#\n# This is reference material, not an active skill. The agent runtime\n# only loads skills from \\`skills/<name>/SKILL.md\\` (one folder per\n# skill). To turn this template into a real skill:\n#\n# 1. mkdir skills/my-skill\n# 2. cp skills/EXAMPLE.md skills/my-skill/SKILL.md\n# 3. uncomment the fields you need and fill in real values\n# 4. delete this comment block from the new file\n#\n# Full reference: see packages/cli/SKILLS.md in the elisym monorepo.\n\n# ---\n# # Required fields ---------------------------------------------------\n#\n# # Skill name. Must be unique within the agent. Used as the d-tag for\n# # routing incoming jobs (NIP-89/NIP-90).\n# name: My Skill\n#\n# # One-line description shown to customers in discovery UIs. Keep it\n# # short and concrete - this is the \"elevator pitch\" for the skill.\n# description: Send a prompt, get back a poem about it.\n#\n# # Capability tags. Customers filter and discover skills by these.\n# # At least one entry required. Use lowercase kebab-case.\n# capabilities:\n# - poetry\n# - text-generation\n#\n# # Price the customer pays per job. Number or numeric string. Free\n# # skills (price: 0) are allowed only when the agent runtime is\n# # configured with \\`allowFreeSkills\\`.\n# price: 0.05\n#\n# # Asset the price is denominated in. Defaults to \"sol\" for back-compat\n# # but USDC is the canonical paid-skill currency for examples.\n# token: usdc\n# # Optional explicit mint (base58). Resolved automatically for known\n# # tokens, so usually omit this.\n# # mint: 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU\n#\n# # Execution mode. Defaults to \"llm\" if omitted.\n# # - llm: feed input to an LLM with the system prompt below.\n# # - static-file: return the contents of a fixed file (no input read).\n# # - static-script: spawn a script with no stdin (no input read).\n# # - dynamic-script: spawn a script and pipe the customer's input to stdin.\n# mode: llm\n#\n# # ---\n# # LLM configuration / dependency -----------------------------------\n# #\n# # For mode: 'llm', \\`provider\\` + \\`model\\` override the agent default\n# # for runtime LLM execution. \\`max_tokens\\` overrides the default cap.\n# #\n# # For script modes (static-script / dynamic-script / static-file):\n# # \\`provider\\` + \\`model\\` declare which LLM API key the script\n# # depends on. The agent registers the (provider, model) pair with\n# # the health monitor so it can:\n# # - probe the key at startup (refuse to start on invalid/billing-out)\n# # - reactively flip the pair to unhealthy if the script exits with\n# # SCRIPT_EXIT_BILLING_EXHAUSTED (= 42), refusing future jobs\n# # before payment until the key recovers\n# # - run a 5-min lazy recovery probe loop that flips the pair back\n# # to healthy as soon as the key works again\n# # \\`max_tokens\\` is rejected for script modes (the script controls\n# # its own token limits).\n# # provider: anthropic\n# # model: claude-haiku-4-5-20251001\n# # max_tokens: 4096\n#\n# # ---\n# # mode-specific fields -------------------------------------------\n#\n# # Required when mode === 'static-file'.\n# # output_file: ./output.txt\n#\n# # Required when mode === 'static-script' | 'dynamic-script'.\n# # script: ./scripts/run.sh\n# # script_args:\n# # - --flag\n# # - value\n# # script_timeout_ms: 60000\n#\n# # ---\n# # mode === 'llm' extras ------------------------------------------\n# #\n# # External tools the LLM can invoke during a job. Each tool is a\n# # named subprocess; the LLM decides whether/when to call it.\n# # tools:\n# # - name: lookup\n# # description: Look up a record by id.\n# # command:\n# # - ./tools/lookup.sh\n# # parameters:\n# # - name: id\n# # description: Record identifier (UUID).\n# # required: true\n#\n# # Cap on tool-use rounds (LLM <-> tools loop). Default 10.\n# # max_tool_rounds: 10\n#\n# # ---\n# # Per-skill rate limit (any mode) --------------------------------\n# # Snake-case in YAML, applied by the runtime regardless of mode.\n# # rate_limit:\n# # per_window_secs: 60\n# # max_per_window: 30\n#\n# # ---\n# # Per-skill execution budget (any mode) --------------------------\n# # Caps skill.execute (seconds). 0 = unlimited; omit to inherit the\n# # agent-level execution_timeout_secs (unlimited if that is unset too).\n# # max_execution_secs: 1800\n#\n# # ---\n# # Imagery ---------------------------------------------------------\n# # Either a local file path (uploaded on first start) or an absolute\n# # URL. Local paths must stay inside the skill directory.\n# # image_file: ./skill-icon.png\n# # image: https://example.com/icon.png\n# ---\n#\n# Markdown body below the frontmatter is the system prompt for\n# mode === 'llm'. For other modes it's ignored.\n#\n# You are a helpful assistant. Reply concisely.\n`;\n\n/**\n * Write .secrets.json atomically. If `passphrase` is given, encrypts all\n * plaintext secret fields (already-encrypted values are left as-is).\n * Generic over `llm_api_keys` so any registered provider's key is\n * encrypted without per-provider plumbing here.\n */\nexport async function writeSecrets(\n agentDir: string,\n secrets: Secrets,\n passphrase?: string,\n): Promise<void> {\n const validated = SecretsSchema.parse(secrets);\n let encryptedLlmKeys: Record<string, string> | undefined;\n if (validated.llm_api_keys) {\n encryptedLlmKeys = {};\n for (const [providerId, value] of Object.entries(validated.llm_api_keys)) {\n if (value) {\n encryptedLlmKeys[providerId] = maybeEncrypt(value, passphrase);\n }\n }\n if (Object.keys(encryptedLlmKeys).length === 0) {\n encryptedLlmKeys = undefined;\n }\n }\n const finalSecrets: Secrets = {\n nostr_secret_key: maybeEncrypt(validated.nostr_secret_key, passphrase),\n solana_secret_key: validated.solana_secret_key\n ? maybeEncrypt(validated.solana_secret_key, passphrase)\n : undefined,\n llm_api_keys: encryptedLlmKeys,\n };\n const body = JSON.stringify(finalSecrets, null, 2) + '\\n';\n const target = agentPaths(agentDir).secrets;\n await writeFileAtomic(target, body, 0o600);\n}\n\nfunction maybeEncrypt(value: string, passphrase: string | undefined): string {\n if (!passphrase) {\n return value;\n }\n if (isEncrypted(value)) {\n return value;\n }\n return encryptSecret(value, passphrase);\n}\n\n/** Atomic write: temp file + rename. Preserves mode. */\nexport async function writeFileAtomic(\n path: string,\n data: string | Buffer,\n mode: number,\n): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const tmpPath = `${path}.tmp.${randomBytes(6).toString('hex')}`;\n await writeFile(tmpPath, data, { mode });\n try {\n await rename(tmpPath, path);\n } catch (e) {\n // Best-effort cleanup of temp file on rename failure.\n try {\n const { unlink } = await import('node:fs/promises');\n await unlink(tmpPath);\n } catch {\n /* ignore */\n }\n throw e;\n }\n}\n\nasync function writeFileIfMissing(path: string, data: string, mode: number): Promise<void> {\n try {\n await writeFile(path, data, { mode, flag: 'wx' });\n } catch (e: unknown) {\n // wx fails with EEXIST if file exists - that's fine.\n if (!isEexist(e)) {\n throw e;\n }\n }\n}\n\nfunction isEexist(e: unknown): boolean {\n return (\n typeof e === 'object' && e !== null && 'code' in e && (e as { code: string }).code === 'EEXIST'\n );\n}\n","/**\n * Zod schemas and types for `~/.elisym/config.yaml`.\n *\n * Split from `./global` so the schemas can be re-exported from the\n * browser-safe `@elisym/sdk` entry point without dragging in `node:fs/promises`\n * (which the loader/writer in `./global` needs).\n */\n\nimport { z } from 'zod';\n\nexport const SessionSpendLimitEntrySchema = z\n .object({\n chain: z.enum(['solana']),\n token: z\n .string()\n .min(1)\n .max(16)\n .regex(/^[a-z0-9]+$/, 'token must be lowercase alphanumeric'),\n mint: z.string().min(1).max(64).optional(),\n // Stored as a string to preserve the operator's exact decimal text (avoids\n // Number round-tripping to scientific notation). Legacy configs persisted a\n // number; accept both and normalize to a positive-decimal string.\n amount: z\n .union([z.string(), z.number()])\n .transform((value) => (typeof value === 'number' ? String(value) : value.trim()))\n .refine((value) => /^\\d+(?:\\.\\d+)?$/.test(value) && /[1-9]/.test(value), {\n message: 'amount must be a positive decimal (e.g. \"0.5\", \"1\")',\n }),\n })\n .strict();\n\nexport const GlobalConfigSchema = z\n .object({\n session_spend_limits: z.array(SessionSpendLimitEntrySchema).max(16).optional(),\n })\n .strict();\n\nexport type SessionSpendLimitEntry = z.infer<typeof SessionSpendLimitEntrySchema>;\nexport type GlobalConfig = z.infer<typeof GlobalConfigSchema>;\n","/**\n * Global (not per-agent) config stored at `~/.elisym/config.yaml`.\n *\n * Node.js/Bun only - reads and writes the filesystem. Browser code must not\n * import this module; import the schemas from `./global-schema` instead, or\n * the loader/writer from `@elisym/sdk/node`.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport YAML from 'yaml';\nimport { writeFileAtomic } from '../agent-store/writer';\nimport { GlobalConfigSchema, type GlobalConfig } from './global-schema';\n\nexport {\n GlobalConfigSchema,\n SessionSpendLimitEntrySchema,\n type GlobalConfig,\n type SessionSpendLimitEntry,\n} from './global-schema';\n\nfunction isEnoent(e: unknown): boolean {\n return (\n typeof e === 'object' && e !== null && 'code' in e && (e as { code: string }).code === 'ENOENT'\n );\n}\n\n/**\n * Read and validate `~/.elisym/config.yaml`. Returns `{}` if missing. Throws\n * on malformed YAML or schema violations — the MCP server treats these as fatal\n * at startup rather than silently ignoring bad overrides.\n */\nexport async function loadGlobalConfig(path: string): Promise<GlobalConfig> {\n let raw: string;\n try {\n raw = await readFile(path, 'utf-8');\n } catch (e) {\n if (isEnoent(e)) {\n return {};\n }\n throw e;\n }\n if (raw.trim() === '') {\n return {};\n }\n const parsed: unknown = YAML.parse(raw);\n return GlobalConfigSchema.parse(parsed ?? {});\n}\n\n/** Write the config YAML atomically. Validates via Zod before writing. */\nexport async function writeGlobalConfig(path: string, config: GlobalConfig): Promise<void> {\n const validated = GlobalConfigSchema.parse(config);\n const body = YAML.stringify(validated);\n await writeFileAtomic(path, body, 0o644);\n}\n","/**\n * Node-only iroh blob transport: seed a file as a content-addressed blob and\n * fetch one by ticket, streaming to/from disk. Import from `@elisym/sdk/node`.\n *\n * `@number0/iroh` is an OPTIONAL native (napi) dependency, loaded via dynamic\n * `import()` only on first use, so the SDK installs and runs without it (file\n * transfer is simply unavailable). To stay decoupled from the optional addon's\n * type surface (and its `const enum`s), the binding is accessed through the\n * minimal local interface below; enum parameters are passed as their runtime\n * string values, which is what the napi layer expects.\n */\nimport { LIMITS, DEFAULTS } from '../constants';\n\n/** Streamed add-progress (subset). `found` carries the byte size; `allDone` the hash. */\ninterface IrohAddProgress {\n found?: { size: bigint };\n allDone?: { hash: string; format: string };\n}\n\n/** Streamed download-progress (subset). `found.size` is the BLAKE3-verified total. */\ninterface IrohDownloadProgress {\n found?: { size: bigint };\n progress?: { offset: bigint };\n allDone?: { bytesWritten: bigint };\n}\n\ninterface IrohTicket {\n hash: string;\n format: string;\n asDownloadOptions(): unknown;\n toString(): string;\n}\n\ninterface IrohBlobs {\n addFromPath(\n path: string,\n inPlace: boolean,\n tag: unknown,\n wrap: { wrap: boolean },\n cb: (err: Error | null, progress: IrohAddProgress) => void,\n ): Promise<void>;\n addBytes(bytes: number[]): Promise<{ hash: string; format: string; size: bigint }>;\n share(hash: string, blobFormat: string, addrInfoOptions: string): Promise<IrohTicket>;\n download(\n hash: string,\n opts: unknown,\n cb: (err: Error | null, progress: IrohDownloadProgress) => void,\n ): Promise<void>;\n export(hash: string, destination: string, format: string, mode: string): Promise<void>;\n readToBytes(hash: string): Promise<number[]>;\n size(hash: string): Promise<bigint>;\n deleteBlob(hash: string): Promise<void>;\n}\n\ninterface IrohNode {\n blobs: IrohBlobs;\n node: { shutdown(): Promise<void> };\n}\n\nexport interface IrohModule {\n Iroh: { persistent(path: string): Promise<IrohNode> };\n SetTagOption: { auto(): unknown };\n BlobTicket: { fromString(ticket: string): IrohTicket };\n}\n\n/** Outcome of seeding a file: a shareable ticket plus the blob's byte size. */\nexport interface SeedResult {\n ticket: string;\n size: number;\n}\n\nexport interface FetchOptions {\n /** Hard cap on the BLAKE3-verified blob size; defaults to `LIMITS.MAX_FILE_SIZE`. */\n maxBytes?: number;\n /** Per-fetch timeout; defaults to `DEFAULTS.IROH_FETCH_TIMEOUT_MS`. */\n timeoutMs?: number;\n}\n\n/**\n * Node-to-node blob transport. Files move path-based (streamed to/from disk),\n * never buffered whole in memory.\n */\nexport interface IrohBlobTransport {\n /** Add a file to the local store and return a shareable ticket + its byte size. */\n seedPath(path: string): Promise<SeedResult>;\n /** Add an in-memory buffer to the local store (e.g. large inline text). */\n seedBytes(bytes: Uint8Array): Promise<SeedResult>;\n /** Download a blob by ticket and export it to `dest`, bounded by size + timeout. */\n fetchToPath(ticket: string, dest: string, options?: FetchOptions): Promise<void>;\n /**\n * Download a blob by ticket and return it as bytes, bounded by size + timeout.\n * The whole blob is held in memory, so callers MUST pass a tight `maxBytes`\n * (e.g. `LIMITS.MAX_REINLINE_TEXT_BYTES`) - the cap is enforced on the\n * BLAKE3-verified size BEFORE the blob is read into memory.\n */\n fetchToBytes(ticket: string, options?: FetchOptions): Promise<Uint8Array>;\n /** Re-share an already-stored blob to mint a fresh ticket (e.g. after a restart). */\n reShare(ticket: string): Promise<string>;\n /** Shut the node down, releasing the fs-store lock. Safe if the node was never created. */\n shutdown(): Promise<void>;\n}\n\nexport interface CreateIrohTransportOptions {\n /** Directory for the persistent fs-store (e.g. `<agent-dir>/.iroh/`). */\n storePath: string;\n /**\n * Test seam: override how the native addon is loaded. Defaults to the real\n * dynamic `import('@number0/iroh')`. Production code never sets this.\n */\n loadModule?: () => Promise<IrohModule>;\n}\n\nconst ADDR_INFO_RELAY_AND_ADDRESSES = 'RelayAndAddresses';\nconst EXPORT_FORMAT_BLOB = 'Blob';\nconst EXPORT_MODE_COPY = 'Copy';\n\n// Specifiers are passed through variables so bundlers (Vite/esbuild) leave the\n// dynamic import for the Node loader at runtime - we never want the native addon\n// bundled. The published `package.json` has a non-standard `main`: Node's CJS\n// loader falls back to `index.js`, but stricter loaders (e.g. vite-node under the\n// test runner) do not, so we fall back to the explicit `/index.js` subpath.\nconst IROH_MODULE_IDS = ['@number0/iroh', '@number0/iroh/index.js'];\n\nasync function loadIrohModule(): Promise<IrohModule> {\n let lastError: unknown;\n for (const moduleId of IROH_MODULE_IDS) {\n try {\n return (await import(moduleId)) as unknown as IrohModule;\n } catch (cause) {\n lastError = cause;\n }\n }\n throw new Error(\n 'iroh file transfer is unavailable: the optional @number0/iroh native addon is not installed.',\n { cause: lastError },\n );\n}\n\n/** Thrown when a bounded iroh op exceeds its deadline (distinct so callers can react). */\nexport class IrohTimeoutError extends Error {}\n\nfunction rejectAfter(\n timeoutMs: number,\n label: string,\n): { promise: Promise<never>; cancel: () => void } {\n let timer: ReturnType<typeof setTimeout>;\n const promise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(\n () => reject(new IrohTimeoutError(`${label} timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n });\n return { promise, cancel: () => clearTimeout(timer) };\n}\n\n/**\n * Create a lazily-initialized iroh transport bound to a persistent fs-store.\n * The node (and the native addon import) is created on first use and shared\n * single-flight across concurrent callers.\n */\nexport function createIrohTransport(options: CreateIrohTransportOptions): IrohBlobTransport {\n let nodePromise: Promise<{ module: IrohModule; node: IrohNode }> | null = null;\n // Set while a node teardown-and-recreate is in flight. `getNode` awaits it so no\n // caller spins up a SECOND `Iroh.persistent` on the still-locked fs-store mid-reset.\n let resetPromise: Promise<void> | null = null;\n\n // Tear the shared node down so the next `getNode` recreates a fresh one - the\n // automatic equivalent of a manual agent restart when a native call wedges the\n // node. Single-flight; `nodePromise` is nulled SYNCHRONOUSLY (no await before\n // `resetPromise` is assigned) so a concurrent `getNode` either awaits the reset or\n // sees a fresh node, never a null+no-reset gap that would create a duplicate node.\n const resetNode = (): Promise<void> => {\n if (resetPromise) {\n return resetPromise;\n }\n const pending = nodePromise;\n nodePromise = null;\n const running = (async () => {\n if (!pending) {\n return;\n }\n const loaded = await pending.catch(() => null);\n if (!loaded) {\n return;\n }\n // A wedged node's shutdown may itself hang, so bound it; recreate regardless.\n const timeout = rejectAfter(DEFAULTS.IROH_SEED_TIMEOUT_MS, 'iroh node shutdown');\n try {\n await Promise.race([loaded.node.node.shutdown(), timeout.promise]);\n } catch {\n // ignore - we proceed to recreate on the next getNode either way\n } finally {\n timeout.cancel();\n }\n })();\n resetPromise = running.finally(() => {\n resetPromise = null;\n });\n return resetPromise;\n };\n\n const getNode = async (): Promise<{ module: IrohModule; node: IrohNode }> => {\n // Wait out any in-flight reset before checking/creating the node.\n if (resetPromise) {\n await resetPromise;\n }\n if (!nodePromise) {\n const pending = (async () => {\n const module = await (options.loadModule ?? loadIrohModule)();\n const node = await module.Iroh.persistent(options.storePath);\n return { module, node };\n })();\n nodePromise = pending;\n // Never cache a rejection: a failed node creation (e.g. another process is\n // briefly holding the fs-store lock) must not permanently disable the\n // transport. Clear it so the next call retries; concurrent in-flight callers\n // still share this single attempt.\n pending.catch(() => {\n if (nodePromise === pending) {\n nodePromise = null;\n }\n });\n }\n return nodePromise;\n };\n\n // Bound a seed/share op so a wedged native call surfaces as a thrown error\n // instead of an indefinite hang. On TIMEOUT (only - not ordinary errors, which\n // would thrash the node) reset the node so the next op gets a fresh one.\n const withSeedTimeout = async <T>(label: string, op: () => Promise<T>): Promise<T> => {\n const timeout = rejectAfter(DEFAULTS.IROH_SEED_TIMEOUT_MS, label);\n try {\n return await Promise.race([op(), timeout.promise]);\n } catch (error) {\n if (error instanceof IrohTimeoutError) {\n void resetNode();\n }\n throw error;\n } finally {\n timeout.cancel();\n }\n };\n\n const seedPath = (path: string): Promise<SeedResult> =>\n withSeedTimeout('iroh seed (path)', async () => {\n const { module, node } = await getNode();\n let size = 0;\n const outcome = await new Promise<{ hash: string; format: string }>((resolve, reject) => {\n node.blobs\n .addFromPath(\n path,\n false,\n module.SetTagOption.auto(),\n { wrap: false },\n (err, progress) => {\n if (err !== null) {\n reject(err);\n return;\n }\n if (progress.found !== undefined) {\n size = Number(progress.found.size);\n }\n if (progress.allDone !== undefined) {\n resolve(progress.allDone);\n }\n },\n )\n .catch(reject);\n });\n const ticket = await node.blobs.share(\n outcome.hash,\n outcome.format,\n ADDR_INFO_RELAY_AND_ADDRESSES,\n );\n return { ticket: ticket.toString(), size };\n });\n\n const seedBytes = (bytes: Uint8Array): Promise<SeedResult> =>\n withSeedTimeout('iroh seed (bytes)', async () => {\n const { node } = await getNode();\n const outcome = await node.blobs.addBytes(Array.from(bytes));\n const ticket = await node.blobs.share(\n outcome.hash,\n outcome.format,\n ADDR_INFO_RELAY_AND_ADDRESSES,\n );\n return { ticket: ticket.toString(), size: Number(outcome.size) };\n });\n\n // Download a blob by ticket, enforcing the size cap on the BLAKE3-verified\n // total and a per-fetch timeout, deleting any (partial) blob on failure.\n // Returns the node + hash so the caller can export to disk or read to memory.\n const downloadBounded = async (\n ticketStr: string,\n fetchOptions: FetchOptions | undefined,\n ): Promise<{ node: IrohNode; hash: string }> => {\n const { module, node } = await getNode();\n const maxBytes = fetchOptions?.maxBytes ?? LIMITS.MAX_FILE_SIZE;\n const timeoutMs = fetchOptions?.timeoutMs ?? DEFAULTS.IROH_FETCH_TIMEOUT_MS;\n const ticket = module.BlobTicket.fromString(ticketStr);\n const hash = ticket.hash;\n\n const timeout = rejectAfter(timeoutMs, 'iroh fetch');\n try {\n await Promise.race([\n timeout.promise,\n new Promise<void>((resolve, reject) => {\n node.blobs\n .download(hash, ticket.asDownloadOptions(), (err, progress) => {\n if (err !== null) {\n reject(err);\n return;\n }\n // Enforce on the BLAKE3-verified size, never the descriptor's claim.\n if (progress.found !== undefined && Number(progress.found.size) > maxBytes) {\n reject(\n new Error(\n `file exceeds MAX_FILE_SIZE: ${progress.found.size} > ${maxBytes} bytes`,\n ),\n );\n return;\n }\n if (progress.progress !== undefined && Number(progress.progress.offset) > maxBytes) {\n reject(\n new Error(`file exceeds MAX_FILE_SIZE during transfer (> ${maxBytes} bytes)`),\n );\n return;\n }\n if (progress.allDone !== undefined) {\n resolve();\n }\n })\n .catch(reject);\n }),\n ]);\n } catch (error) {\n // Best-effort reclaim of any (partial) data for the rejected blob. The napi\n // binding exposes no mid-transfer cancel, so an oversized transfer may finish\n // in the background before this runs; the cap still prevents the caller from\n // exporting/using it, and this keeps the store from retaining it.\n await node.blobs.deleteBlob(hash).catch(() => {});\n throw error;\n } finally {\n timeout.cancel();\n }\n // Re-check the verified size against the cap. The progress-callback guard\n // above only fires while bytes transfer; a blob ALREADY resident in the local\n // store completes via `allDone` alone (no `found`/`progress`), so without this\n // an oversized resident blob would slip past the cap before `readToBytes`\n // pulls it into memory. `blobs.size` reports the BLAKE3-verified total.\n const verifiedSize = Number(await node.blobs.size(hash));\n if (verifiedSize > maxBytes) {\n await node.blobs.deleteBlob(hash).catch(() => {});\n throw new Error(`file exceeds MAX_FILE_SIZE: ${verifiedSize} > ${maxBytes} bytes`);\n }\n return { node, hash };\n };\n\n const fetchToPath = async (\n ticketStr: string,\n dest: string,\n fetchOptions?: FetchOptions,\n ): Promise<void> => {\n const { node, hash } = await downloadBounded(ticketStr, fetchOptions);\n await node.blobs.export(hash, dest, EXPORT_FORMAT_BLOB, EXPORT_MODE_COPY);\n };\n\n const fetchToBytes = async (\n ticketStr: string,\n fetchOptions?: FetchOptions,\n ): Promise<Uint8Array> => {\n const { node, hash } = await downloadBounded(ticketStr, fetchOptions);\n // The cap was enforced on the verified size during download, so reading the\n // whole blob into memory here is bounded by the caller's maxBytes.\n const bytes = await node.blobs.readToBytes(hash);\n return Uint8Array.from(bytes);\n };\n\n const reShare = (ticketStr: string): Promise<string> =>\n withSeedTimeout('iroh re-share', async () => {\n const { module, node } = await getNode();\n const ticket = module.BlobTicket.fromString(ticketStr);\n const fresh = await node.blobs.share(\n ticket.hash,\n ticket.format,\n ADDR_INFO_RELAY_AND_ADDRESSES,\n );\n return fresh.toString();\n });\n\n const shutdown = async (): Promise<void> => {\n if (!nodePromise) {\n return;\n }\n const pending = nodePromise;\n nodePromise = null;\n const loaded = await pending.catch(() => null);\n if (loaded) {\n await loaded.node.node.shutdown();\n }\n };\n\n return { seedPath, seedBytes, fetchToPath, fetchToBytes, reShare, shutdown };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/primitives/encryption.ts","../src/constants.ts","../src/agent-store/writer.ts","../src/config/global-schema.ts","../src/config/global.ts","../src/transport/iroh.ts"],"names":["randomBytes","readFile","YAML"],"mappings":"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","file":"node.js","sourcesContent":["/**\n * Secret encryption/decryption for agent config files.\n * Uses scrypt (KDF) + AES-256-GCM (cipher).\n * Format: \"encrypted:v1:\" + base64(salt[16] + iv[12] + ciphertext + tag[16])\n *\n * scrypt params: N=2^17, r=8, p=1 (~128 MB RAM per derivation).\n *\n * Node.js/Bun only - not available in browsers. Reachable only via the\n * '@elisym/sdk/node' subpath, which browser bundlers will not resolve.\n */\n\nimport { Buffer } from 'node:buffer';\nimport { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';\n\nconst PREFIX = 'encrypted:v1:';\nconst SALT_LENGTH = 16;\nconst IV_LENGTH = 12;\nconst TAG_LENGTH = 16;\nconst KEY_LENGTH = 32; // AES-256\n// v1: N=2^17 (OWASP minimum). v2 will use N=2^20 with format migration.\nconst SCRYPT_N = 2 ** 17;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\nconst SCRYPT_MAXMEM = 128 * SCRYPT_N * SCRYPT_R * 2; // 2x the minimum required memory\n\n/** Check if a value is encrypted (has the encrypted:v1: prefix). */\nexport function isEncrypted(value: string): boolean {\n return value.startsWith(PREFIX);\n}\n\n/** Encrypt a plaintext secret with a passphrase. Returns \"encrypted:v1:base64...\". Node.js/Bun only. */\nexport function encryptSecret(plaintext: string, passphrase: string): string {\n if (!passphrase) {\n throw new Error('Passphrase must not be empty.');\n }\n\n const salt = randomBytes(SALT_LENGTH);\n const key = scryptSync(passphrase, salt, KEY_LENGTH, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n const iv = randomBytes(IV_LENGTH);\n\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n const payload = Buffer.concat([salt, iv, encrypted, tag]);\n return PREFIX + payload.toString('base64');\n}\n\n/** Decrypt an encrypted secret with a passphrase. Throws on wrong passphrase or corrupted data. Node.js/Bun only. */\nexport function decryptSecret(encrypted: string, passphrase: string): string {\n if (!isEncrypted(encrypted)) {\n throw new Error('Value is not encrypted (missing encrypted:v1: prefix).');\n }\n if (!passphrase) {\n throw new Error('Passphrase must not be empty.');\n }\n\n const payload = Buffer.from(encrypted.slice(PREFIX.length), 'base64');\n if (payload.length < SALT_LENGTH + IV_LENGTH + TAG_LENGTH) {\n throw new Error('Encrypted payload is too short.');\n }\n\n const salt = payload.subarray(0, SALT_LENGTH);\n const iv = payload.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const tag = payload.subarray(payload.length - TAG_LENGTH);\n const ciphertext = payload.subarray(SALT_LENGTH + IV_LENGTH, payload.length - TAG_LENGTH);\n\n const key = scryptSync(passphrase, salt, KEY_LENGTH, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n\n try {\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n return decrypted.toString('utf8');\n } catch {\n throw new Error('Decryption failed. Wrong passphrase or corrupted data.');\n }\n}\n","import type { Address } from '@solana/kit';\n\nexport const RELAYS = [\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n 'wss://relay.primal.net',\n 'wss://relay.snort.social',\n];\n\nexport const KIND_APP_HANDLER = 31990;\nexport const KIND_LONG_FORM_ARTICLE = 30023;\nexport const KIND_JOB_REQUEST_BASE = 5000;\nexport const KIND_JOB_RESULT_BASE = 6000;\nexport const KIND_JOB_FEEDBACK = 7000;\nexport const DEFAULT_KIND_OFFSET = 100;\n\n/** Discovery tag attached to elisym agent policy events (kind 30023). */\nexport const POLICY_T_TAG = 'elisym-policy';\n/** d-tag prefix for policy events: full d-tag = `<prefix><type>` (e.g. `elisym-policy-tos`). */\nexport const POLICY_D_TAG_PREFIX = 'elisym-policy-';\n/** Validation regex for policy `type` slug. Lowercase ASCII + hyphen, 1-32 chars, no leading/trailing hyphen. */\nexport const POLICY_TYPE_REGEX = /^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$/;\n\n/** Default job request kind (5000 + 100). */\nexport const KIND_JOB_REQUEST = KIND_JOB_REQUEST_BASE + DEFAULT_KIND_OFFSET;\n/** Default job result kind (6000 + 100). */\nexport const KIND_JOB_RESULT = KIND_JOB_RESULT_BASE + DEFAULT_KIND_OFFSET;\n\n/** Compute a job request kind from an offset (5000 + offset). */\nexport function jobRequestKind(offset: number): number {\n if (!Number.isInteger(offset) || offset < 0 || offset >= 1000) {\n throw new Error(`Invalid kind offset: ${offset}. Must be integer 0-999.`);\n }\n return KIND_JOB_REQUEST_BASE + offset;\n}\n\n/** Compute a job result kind from an offset (6000 + offset). */\nexport function jobResultKind(offset: number): number {\n if (!Number.isInteger(offset) || offset < 0 || offset >= 1000) {\n throw new Error(`Invalid kind offset: ${offset}. Must be integer 0-999.`);\n }\n return KIND_JOB_RESULT_BASE + offset;\n}\n\n/** Ephemeral ping/pong kinds (not stored by relays, forwarded in real-time). */\nexport const KIND_PING = 20200;\nexport const KIND_PONG = 20201;\n\nexport const LAMPORTS_PER_SOL = 1_000_000_000;\n\n/**\n * Solana program ID for the elisym protocol config (devnet deployment).\n *\n * The Anchor program at this address is the source of truth for fee bps,\n * treasury address, and admin rotation state. Read via `getProtocolConfig`.\n */\nexport const PROTOCOL_PROGRAM_ID_DEVNET = 'BrX1CRkSgvcjxBvc2bgc3QqgWjinusofDmeP7ZVxvwrE' as Address;\n\n/**\n * Read-only marker pubkey attached as a non-signer account to every elisym\n * payment transaction. Lets indexers enumerate every elisym tx network-wide\n * via a single `getSignaturesForAddress(ELISYM_PROTOCOL_TAG)` call,\n * independent of fee size or recipient.\n *\n * The account does not need to exist on-chain; including its pubkey as an\n * extra read-only account in the provider transfer instruction is enough for\n * Solana's tx-by-account index to pick it up. The corresponding secret key\n * was generated and discarded - the tag never signs and never holds funds.\n */\nexport const ELISYM_PROTOCOL_TAG = 'ELiZksgwDt41LaeuPDLkUfWgFXhGgVayTMP7L5nTSEL8' as Address;\n\nexport type ProtocolCluster = 'devnet' | 'mainnet' | 'localnet';\n\n/**\n * Resolve the elisym-config program ID for a given Solana cluster.\n * Mainnet is intentionally unsupported until the program ships there.\n */\nexport function getProtocolProgramId(cluster: ProtocolCluster): Address {\n switch (cluster) {\n case 'devnet':\n case 'localnet':\n return PROTOCOL_PROGRAM_ID_DEVNET;\n case 'mainnet':\n throw new Error('Protocol program is not deployed on mainnet yet');\n }\n}\n\n/** Default values for timeouts, retries, and batch sizes. */\nexport const DEFAULTS = {\n SUBSCRIPTION_TIMEOUT_MS: 120_000,\n PING_TIMEOUT_MS: 3_000,\n PING_RETRIES: 2,\n PING_CACHE_TTL_MS: 30_000,\n PAYMENT_EXPIRY_SECS: 600,\n BATCH_SIZE: 250,\n QUERY_TIMEOUT_MS: 15_000,\n EOSE_TIMEOUT_MS: 3_000,\n VERIFY_RETRIES: 10,\n VERIFY_INTERVAL_MS: 3_000,\n VERIFY_BY_REF_RETRIES: 15,\n VERIFY_BY_REF_INTERVAL_MS: 2_000,\n RESULT_RETRY_COUNT: 3,\n RESULT_RETRY_BASE_MS: 1_000,\n QUERY_MAX_CONCURRENCY: 6,\n VERIFY_SIGNATURE_LIMIT: 25,\n // Default ceiling for a single iroh file transfer (seed/fetch). A tunable\n // default, not a protocol constant - the transfer is resumable and its own\n // budget, decoupled from the result-wait window.\n IROH_FETCH_TIMEOUT_MS: 300_000,\n // Ceiling for a single iroh SEED (addFromPath/addBytes/share). Seeding is local\n // (hash + store-copy + ticket mint), so this is a generous backstop: it bounds\n // the JS await so a wedged native call surfaces as a thrown error (and triggers a\n // node reset) instead of an indefinite hang that stalls file delivery.\n IROH_SEED_TIMEOUT_MS: 120_000,\n // Ceiling for a single Blossom blob upload (PUT /upload). Large blobs (up to\n // LIMITS.MAX_FILE_SIZE) need far more than the 30s used for small media images.\n BLOSSOM_UPLOAD_TIMEOUT_MS: 300_000,\n // Ceiling for a single encrypted Blossom blob download (GET). Same budget as upload.\n BLOSSOM_FETCH_TIMEOUT_MS: 300_000,\n} as const;\n\n/** Protocol limits for input validation. */\nexport const LIMITS = {\n MAX_INPUT_LENGTH: 100_000,\n // NIP-44 v2 hard cap on encrypted plaintext: the pad() length prefix is a u16,\n // so the plaintext can be at most 65_535 BYTES (not chars). Encrypting anything\n // larger throws `invalid plaintext size` inside nip44Encrypt. This is the binding\n // limit for TARGETED (encrypted) jobs - lower than every relay's NIP-11 cap.\n NIP44_MAX_PLAINTEXT_BYTES: 65_535,\n // Spill threshold for encrypted content: above this many UTF-8 bytes, callers\n // route the text through iroh out-of-band instead of inlining it. A non-spilled\n // job is encrypted RAW (no envelope - see marketplace.submitJobRequest), so a\n // 60_000-byte input is a 60_000-byte plaintext; the ~5.5KB gap under the hard cap\n // is plain slack, and NIP44_MAX_PLAINTEXT_BYTES is the SDK backstop.\n MAX_ENCRYPTED_INLINE_BYTES: 60_000,\n // Ceiling above which a text/* attachment is NOT materialized back into a string\n // (re-inlined into SkillInput.data) - the consumer gets a filePath / explicit\n // fetch instead. Also bounds the in-memory git-diff buffer (memory-DoS guard).\n MAX_REINLINE_TEXT_BYTES: 4_194_304, // 4 MiB\n // Hard safety cap on a single file transferred via iroh, enforced on the\n // actual streamed bytes (never the sender-declared `size`). A tunable default;\n // providers may lower it per deployment.\n MAX_FILE_SIZE: 1_073_741_824, // 1 GiB\n // Cap for the ENCRYPTED Blossom path (web/SDK). The encrypt-then-upload flow is\n // whole-buffer in WebCrypto + BlossomService (~3x file-size peak RAM), so this is\n // deliberately far below MAX_FILE_SIZE to stay safe in a browser tab; larger files\n // use iroh. The relay enforces a ~128 MiB server-side backstop.\n MAX_BLOSSOM_ENCRYPTED_BYTES: 104_857_600, // 100 MiB\n\n MAX_TIMEOUT_SECS: 600,\n // Upper bound for execution budgets (`max_execution_secs` / `execution_timeout_secs`).\n // Distinct from MAX_TIMEOUT_SECS (the result-wait cap): execution budgets may be\n // hours, so this exists only to keep `secs * 1000` within Node's setTimeout limit\n // (2_147_483_647 ms) - a larger value overflows and fires the timer immediately.\n MAX_EXECUTION_SECS: 2_147_483,\n MAX_CAPABILITIES: 20,\n MAX_DESCRIPTION_LENGTH: 500,\n MAX_AGENT_NAME_LENGTH: 64,\n MAX_CAPABILITY_LENGTH: 64,\n MAX_POLICY_CONTENT_LENGTH: 50_000,\n MAX_POLICIES_PER_AGENT: 12,\n MAX_POLICY_TYPE_LENGTH: 32,\n MAX_POLICY_TITLE_LENGTH: 120,\n MAX_POLICY_SUMMARY_LENGTH: 280,\n MAX_POLICY_VERSION_LENGTH: 32,\n} as const;\n\nconst UTF8_ENCODER = new TextEncoder();\n\n/**\n * UTF-8 byte length of a string. All size guards on encrypted content measure\n * BYTES, not `String.length` (UTF-16 code units): NIP-44's plaintext cap is a\n * byte cap, so a multibyte string under the char cap can still exceed it.\n */\nexport function utf8ByteLength(value: string): number {\n return UTF8_ENCODER.encode(value).length;\n}\n","/**\n * Write agent files: elisym.yaml, .secrets.json, .gitignore, and create agent dirs.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport YAML from 'yaml';\nimport { encryptSecret, isEncrypted } from '../primitives/encryption';\nimport { agentPaths, type AgentPaths } from './paths';\nimport { elisymRootFor, type AgentSource } from './resolver';\nimport { ElisymYamlSchema, SecretsSchema, type ElisymYaml, type Secrets } from './schema';\nimport { renderInitialYaml } from './template';\n\n/** Bare per-agent `.iroh/` store dir; ignored at any depth under `.elisym/`. */\nconst IROH_GITIGNORE_ENTRY = '.iroh/';\n\nconst GITIGNORE_CONTENT = [\n '# elisym private state - do not commit.',\n '.secrets.json',\n '.media-cache.json',\n '.jobs.json',\n '.jobs.json.corrupt.*',\n '.customer-history.json',\n '.contacts.json',\n IROH_GITIGNORE_ENTRY,\n '',\n].join('\\n');\n\n/**\n * Ensure the project-local `.elisym/.gitignore` ignores the iroh blob store.\n * Idempotent migration for agents created before `.iroh/` was added to the\n * default ignore list; a no-op when no `.gitignore` exists (e.g. home-global,\n * which relies on directory permissions instead). The iroh store holds job\n * payloads in cleartext, so it must never be committed.\n */\nexport async function ensureGitignoreHasIrohEntry(elisymRoot: string): Promise<void> {\n const gitignorePath = join(elisymRoot, '.gitignore');\n let current: string;\n try {\n current = await readFile(gitignorePath, 'utf-8');\n } catch {\n return;\n }\n const hasEntry = current.split('\\n').some((line) => line.trim() === IROH_GITIGNORE_ENTRY);\n if (hasEntry) {\n return;\n }\n const separator = current.length === 0 || current.endsWith('\\n') ? '' : '\\n';\n await writeFile(gitignorePath, `${current}${separator}${IROH_GITIGNORE_ENTRY}\\n`, {\n mode: 0o644,\n });\n}\n\nexport interface CreateAgentDirOptions {\n target: AgentSource;\n name: string;\n cwd: string;\n /**\n * For `target: 'project'`: if no .elisym/ dir exists above cwd,\n * where should we create one? Defaults to cwd.\n */\n projectRoot?: string;\n}\n\nexport interface CreatedAgentDir {\n dir: string;\n paths: AgentPaths;\n source: AgentSource;\n createdNewElisymRoot: boolean;\n}\n\n/**\n * Create (or reuse) the directory layout for a new agent. Idempotent: if the\n * agent directory already exists, returns its paths without overwriting.\n * Writes `.gitignore` in project-local .elisym/ on first creation.\n */\nexport async function createAgentDir(options: CreateAgentDirOptions): Promise<CreatedAgentDir> {\n const { target, name, cwd, projectRoot } = options;\n\n const existingRoot = elisymRootFor(target, cwd);\n let elisymRoot: string;\n let createdNewElisymRoot = false;\n\n if (existingRoot) {\n elisymRoot = existingRoot;\n } else if (target === 'project') {\n elisymRoot = join(projectRoot ?? cwd, '.elisym');\n createdNewElisymRoot = true;\n } else {\n throw new Error('homeElisymDir should always exist conceptually - this is unreachable');\n }\n\n const agentDir = join(elisymRoot, name);\n const mode = target === 'home' ? 0o700 : 0o755;\n await mkdir(agentDir, { recursive: true, mode });\n await mkdir(join(agentDir, 'skills'), { recursive: true, mode });\n\n if (target === 'project') {\n const gitignorePath = join(elisymRoot, '.gitignore');\n await writeFileIfMissing(gitignorePath, GITIGNORE_CONTENT, 0o644);\n }\n\n return {\n dir: agentDir,\n paths: agentPaths(agentDir),\n source: target,\n createdNewElisymRoot,\n };\n}\n\n/** Write elisym.yaml atomically. Validates via Zod before writing. */\nexport async function writeYaml(agentDir: string, yaml: ElisymYaml): Promise<void> {\n const validated = ElisymYamlSchema.parse(yaml);\n const body = YAML.stringify(validated);\n const target = agentPaths(agentDir).yaml;\n await writeFileAtomic(target, body, 0o644);\n}\n\n/**\n * Write a brand-new elisym.yaml with descriptive header comments and\n * commented-out examples for unset optional fields. Use only at agent\n * creation time (CLI `init`, MCP `create_agent`). Subsequent edits go\n * through `writeYaml`, which discards comments.\n */\nexport async function writeYamlInitial(agentDir: string, yaml: ElisymYaml): Promise<void> {\n const validated = ElisymYamlSchema.parse(yaml);\n const body = renderInitialYaml(validated);\n const target = agentPaths(agentDir).yaml;\n await writeFileAtomic(target, body, 0o644);\n}\n\n/**\n * Create a skills directory placeholder file with a commented-out SKILL.md\n * template covering every supported field, so operators have a reference\n * for what they can declare without having to read source. The file is\n * named `EXAMPLE.md` (not `SKILL.md`) and lives directly in `skills/`\n * (not in a subdirectory), so the skill loader skips it - it's reference\n * material, not an active skill. To turn it into a real skill: copy it\n * into `skills/<your-skill-name>/SKILL.md` and uncomment the lines you\n * need.\n *\n * Idempotent: written with `wx` flag so we never overwrite an operator's\n * edits on re-run of `init`.\n */\nexport async function writeExampleSkillTemplate(agentDir: string): Promise<void> {\n const target = join(agentDir, 'skills', 'EXAMPLE.md');\n await writeFileIfMissing(target, EXAMPLE_SKILL_TEMPLATE, 0o644);\n}\n\nconst EXAMPLE_SKILL_TEMPLATE = `# elisym skill template\n#\n# This is reference material, not an active skill. The agent runtime\n# only loads skills from \\`skills/<name>/SKILL.md\\` (one folder per\n# skill). To turn this template into a real skill:\n#\n# 1. mkdir skills/my-skill\n# 2. cp skills/EXAMPLE.md skills/my-skill/SKILL.md\n# 3. uncomment the fields you need and fill in real values\n# 4. delete this comment block from the new file\n#\n# Full reference: see packages/cli/SKILLS.md in the elisym monorepo.\n\n# ---\n# # Required fields ---------------------------------------------------\n#\n# # Skill name. Must be unique within the agent. Used as the d-tag for\n# # routing incoming jobs (NIP-89/NIP-90).\n# name: My Skill\n#\n# # One-line description shown to customers in discovery UIs. Keep it\n# # short and concrete - this is the \"elevator pitch\" for the skill.\n# description: Send a prompt, get back a poem about it.\n#\n# # Capability tags. Customers filter and discover skills by these.\n# # At least one entry required. Use lowercase kebab-case.\n# capabilities:\n# - poetry\n# - text-generation\n#\n# # Price the customer pays per job. Number or numeric string. Free\n# # skills (price: 0) are allowed only when the agent runtime is\n# # configured with \\`allowFreeSkills\\`.\n# price: 0.05\n#\n# # Asset the price is denominated in. Defaults to \"sol\" for back-compat\n# # but USDC is the canonical paid-skill currency for examples.\n# token: usdc\n# # Optional explicit mint (base58). Resolved automatically for known\n# # tokens, so usually omit this.\n# # mint: 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU\n#\n# # Execution mode. Defaults to \"llm\" if omitted.\n# # - llm: feed input to an LLM with the system prompt below.\n# # - static-file: return the contents of a fixed file (no input read).\n# # - static-script: spawn a script with no stdin (no input read).\n# # - dynamic-script: spawn a script and pipe the customer's input to stdin.\n# mode: llm\n#\n# # ---\n# # LLM configuration / dependency -----------------------------------\n# #\n# # For mode: 'llm', \\`provider\\` + \\`model\\` override the agent default\n# # for runtime LLM execution. \\`max_tokens\\` overrides the default cap.\n# #\n# # For script modes (static-script / dynamic-script / static-file):\n# # \\`provider\\` + \\`model\\` declare which LLM API key the script\n# # depends on. The agent registers the (provider, model) pair with\n# # the health monitor so it can:\n# # - probe the key at startup (refuse to start on invalid/billing-out)\n# # - reactively flip the pair to unhealthy if the script exits with\n# # SCRIPT_EXIT_BILLING_EXHAUSTED (= 42), refusing future jobs\n# # before payment until the key recovers\n# # - run a 5-min lazy recovery probe loop that flips the pair back\n# # to healthy as soon as the key works again\n# # \\`max_tokens\\` is rejected for script modes (the script controls\n# # its own token limits).\n# # provider: anthropic\n# # model: claude-haiku-4-5-20251001\n# # max_tokens: 4096\n#\n# # ---\n# # mode-specific fields -------------------------------------------\n#\n# # Required when mode === 'static-file'.\n# # output_file: ./output.txt\n#\n# # Required when mode === 'static-script' | 'dynamic-script'.\n# # script: ./scripts/run.sh\n# # script_args:\n# # - --flag\n# # - value\n# # script_timeout_ms: 60000\n#\n# # ---\n# # mode === 'llm' extras ------------------------------------------\n# #\n# # External tools the LLM can invoke during a job. Each tool is a\n# # named subprocess; the LLM decides whether/when to call it.\n# # tools:\n# # - name: lookup\n# # description: Look up a record by id.\n# # command:\n# # - ./tools/lookup.sh\n# # parameters:\n# # - name: id\n# # description: Record identifier (UUID).\n# # required: true\n#\n# # Cap on tool-use rounds (LLM <-> tools loop). Default 10.\n# # max_tool_rounds: 10\n#\n# # ---\n# # Per-skill rate limit (any mode) --------------------------------\n# # Snake-case in YAML, applied by the runtime regardless of mode.\n# # rate_limit:\n# # per_window_secs: 60\n# # max_per_window: 30\n#\n# # ---\n# # Per-skill execution budget (any mode) --------------------------\n# # Caps skill.execute (seconds). 0 = unlimited; omit to inherit the\n# # agent-level execution_timeout_secs (unlimited if that is unset too).\n# # max_execution_secs: 1800\n#\n# # ---\n# # Imagery ---------------------------------------------------------\n# # Either a local file path (uploaded on first start) or an absolute\n# # URL. Local paths must stay inside the skill directory.\n# # image_file: ./skill-icon.png\n# # image: https://example.com/icon.png\n# ---\n#\n# Markdown body below the frontmatter is the system prompt for\n# mode === 'llm'. For other modes it's ignored.\n#\n# You are a helpful assistant. Reply concisely.\n`;\n\n/**\n * Write .secrets.json atomically. If `passphrase` is given, encrypts all\n * plaintext secret fields (already-encrypted values are left as-is).\n * Generic over `llm_api_keys` so any registered provider's key is\n * encrypted without per-provider plumbing here.\n */\nexport async function writeSecrets(\n agentDir: string,\n secrets: Secrets,\n passphrase?: string,\n): Promise<void> {\n const validated = SecretsSchema.parse(secrets);\n let encryptedLlmKeys: Record<string, string> | undefined;\n if (validated.llm_api_keys) {\n encryptedLlmKeys = {};\n for (const [providerId, value] of Object.entries(validated.llm_api_keys)) {\n if (value) {\n encryptedLlmKeys[providerId] = maybeEncrypt(value, passphrase);\n }\n }\n if (Object.keys(encryptedLlmKeys).length === 0) {\n encryptedLlmKeys = undefined;\n }\n }\n const finalSecrets: Secrets = {\n nostr_secret_key: maybeEncrypt(validated.nostr_secret_key, passphrase),\n solana_secret_key: validated.solana_secret_key\n ? maybeEncrypt(validated.solana_secret_key, passphrase)\n : undefined,\n llm_api_keys: encryptedLlmKeys,\n };\n const body = JSON.stringify(finalSecrets, null, 2) + '\\n';\n const target = agentPaths(agentDir).secrets;\n await writeFileAtomic(target, body, 0o600);\n}\n\nfunction maybeEncrypt(value: string, passphrase: string | undefined): string {\n if (!passphrase) {\n return value;\n }\n if (isEncrypted(value)) {\n return value;\n }\n return encryptSecret(value, passphrase);\n}\n\n/** Atomic write: temp file + rename. Preserves mode. */\nexport async function writeFileAtomic(\n path: string,\n data: string | Buffer,\n mode: number,\n): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const tmpPath = `${path}.tmp.${randomBytes(6).toString('hex')}`;\n await writeFile(tmpPath, data, { mode });\n try {\n await rename(tmpPath, path);\n } catch (e) {\n // Best-effort cleanup of temp file on rename failure.\n try {\n const { unlink } = await import('node:fs/promises');\n await unlink(tmpPath);\n } catch {\n /* ignore */\n }\n throw e;\n }\n}\n\nasync function writeFileIfMissing(path: string, data: string, mode: number): Promise<void> {\n try {\n await writeFile(path, data, { mode, flag: 'wx' });\n } catch (e: unknown) {\n // wx fails with EEXIST if file exists - that's fine.\n if (!isEexist(e)) {\n throw e;\n }\n }\n}\n\nfunction isEexist(e: unknown): boolean {\n return (\n typeof e === 'object' && e !== null && 'code' in e && (e as { code: string }).code === 'EEXIST'\n );\n}\n","/**\n * Zod schemas and types for `~/.elisym/config.yaml`.\n *\n * Split from `./global` so the schemas can be re-exported from the\n * browser-safe `@elisym/sdk` entry point without dragging in `node:fs/promises`\n * (which the loader/writer in `./global` needs).\n */\n\nimport { z } from 'zod';\n\nexport const SessionSpendLimitEntrySchema = z\n .object({\n chain: z.enum(['solana']),\n token: z\n .string()\n .min(1)\n .max(16)\n .regex(/^[a-z0-9]+$/, 'token must be lowercase alphanumeric'),\n mint: z.string().min(1).max(64).optional(),\n // Stored as a string to preserve the operator's exact decimal text (avoids\n // Number round-tripping to scientific notation). Legacy configs persisted a\n // number; accept both and normalize to a positive-decimal string.\n amount: z\n .union([z.string(), z.number()])\n .transform((value) => (typeof value === 'number' ? String(value) : value.trim()))\n .refine((value) => /^\\d+(?:\\.\\d+)?$/.test(value) && /[1-9]/.test(value), {\n message: 'amount must be a positive decimal (e.g. \"0.5\", \"1\")',\n }),\n })\n .strict();\n\nexport const GlobalConfigSchema = z\n .object({\n session_spend_limits: z.array(SessionSpendLimitEntrySchema).max(16).optional(),\n })\n .strict();\n\nexport type SessionSpendLimitEntry = z.infer<typeof SessionSpendLimitEntrySchema>;\nexport type GlobalConfig = z.infer<typeof GlobalConfigSchema>;\n","/**\n * Global (not per-agent) config stored at `~/.elisym/config.yaml`.\n *\n * Node.js/Bun only - reads and writes the filesystem. Browser code must not\n * import this module; import the schemas from `./global-schema` instead, or\n * the loader/writer from `@elisym/sdk/node`.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport YAML from 'yaml';\nimport { writeFileAtomic } from '../agent-store/writer';\nimport { GlobalConfigSchema, type GlobalConfig } from './global-schema';\n\nexport {\n GlobalConfigSchema,\n SessionSpendLimitEntrySchema,\n type GlobalConfig,\n type SessionSpendLimitEntry,\n} from './global-schema';\n\nfunction isEnoent(e: unknown): boolean {\n return (\n typeof e === 'object' && e !== null && 'code' in e && (e as { code: string }).code === 'ENOENT'\n );\n}\n\n/**\n * Read and validate `~/.elisym/config.yaml`. Returns `{}` if missing. Throws\n * on malformed YAML or schema violations — the MCP server treats these as fatal\n * at startup rather than silently ignoring bad overrides.\n */\nexport async function loadGlobalConfig(path: string): Promise<GlobalConfig> {\n let raw: string;\n try {\n raw = await readFile(path, 'utf-8');\n } catch (e) {\n if (isEnoent(e)) {\n return {};\n }\n throw e;\n }\n if (raw.trim() === '') {\n return {};\n }\n const parsed: unknown = YAML.parse(raw);\n return GlobalConfigSchema.parse(parsed ?? {});\n}\n\n/** Write the config YAML atomically. Validates via Zod before writing. */\nexport async function writeGlobalConfig(path: string, config: GlobalConfig): Promise<void> {\n const validated = GlobalConfigSchema.parse(config);\n const body = YAML.stringify(validated);\n await writeFileAtomic(path, body, 0o644);\n}\n","/**\n * Node-only iroh blob transport: seed a file as a content-addressed blob and\n * fetch one by ticket, streaming to/from disk. Import from `@elisym/sdk/node`.\n *\n * `@number0/iroh` is an OPTIONAL native (napi) dependency, loaded via dynamic\n * `import()` only on first use, so the SDK installs and runs without it (file\n * transfer is simply unavailable). To stay decoupled from the optional addon's\n * type surface (and its `const enum`s), the binding is accessed through the\n * minimal local interface below; enum parameters are passed as their runtime\n * string values, which is what the napi layer expects.\n */\nimport { LIMITS, DEFAULTS } from '../constants';\n\n/** Streamed add-progress (subset). `found` carries the byte size; `allDone` the hash. */\ninterface IrohAddProgress {\n found?: { size: bigint };\n allDone?: { hash: string; format: string };\n}\n\n/** Streamed download-progress (subset). `found.size` is the BLAKE3-verified total. */\ninterface IrohDownloadProgress {\n found?: { size: bigint };\n progress?: { offset: bigint };\n allDone?: { bytesWritten: bigint };\n}\n\ninterface IrohTicket {\n hash: string;\n format: string;\n asDownloadOptions(): unknown;\n toString(): string;\n}\n\ninterface IrohBlobs {\n addFromPath(\n path: string,\n inPlace: boolean,\n tag: unknown,\n wrap: { wrap: boolean },\n cb: (err: Error | null, progress: IrohAddProgress) => void,\n ): Promise<void>;\n addBytes(bytes: number[]): Promise<{ hash: string; format: string; size: bigint }>;\n share(hash: string, blobFormat: string, addrInfoOptions: string): Promise<IrohTicket>;\n download(\n hash: string,\n opts: unknown,\n cb: (err: Error | null, progress: IrohDownloadProgress) => void,\n ): Promise<void>;\n export(hash: string, destination: string, format: string, mode: string): Promise<void>;\n readToBytes(hash: string): Promise<number[]>;\n size(hash: string): Promise<bigint>;\n deleteBlob(hash: string): Promise<void>;\n}\n\ninterface IrohNode {\n blobs: IrohBlobs;\n node: { shutdown(): Promise<void> };\n}\n\nexport interface IrohModule {\n Iroh: { persistent(path: string): Promise<IrohNode> };\n SetTagOption: { auto(): unknown };\n BlobTicket: { fromString(ticket: string): IrohTicket };\n}\n\n/** Outcome of seeding a file: a shareable ticket plus the blob's byte size. */\nexport interface SeedResult {\n ticket: string;\n size: number;\n}\n\nexport interface FetchOptions {\n /** Hard cap on the BLAKE3-verified blob size; defaults to `LIMITS.MAX_FILE_SIZE`. */\n maxBytes?: number;\n /** Per-fetch timeout; defaults to `DEFAULTS.IROH_FETCH_TIMEOUT_MS`. */\n timeoutMs?: number;\n /**\n * Abort the JS wait early (e.g. on shutdown). NOTE: the napi binding has no mid-transfer\n * cancel, so this abandons the await - freeing the caller - while the native transfer\n * self-terminates in the background, exactly like the `timeoutMs` path.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Node-to-node blob transport. Files move path-based (streamed to/from disk),\n * never buffered whole in memory.\n */\nexport interface IrohBlobTransport {\n /** Add a file to the local store and return a shareable ticket + its byte size. */\n seedPath(path: string): Promise<SeedResult>;\n /** Add an in-memory buffer to the local store (e.g. large inline text). */\n seedBytes(bytes: Uint8Array): Promise<SeedResult>;\n /** Download a blob by ticket and export it to `dest`, bounded by size + timeout. */\n fetchToPath(ticket: string, dest: string, options?: FetchOptions): Promise<void>;\n /**\n * Download a blob by ticket and return it as bytes, bounded by size + timeout.\n * The whole blob is held in memory, so callers MUST pass a tight `maxBytes`\n * (e.g. `LIMITS.MAX_REINLINE_TEXT_BYTES`) - the cap is enforced on the\n * BLAKE3-verified size BEFORE the blob is read into memory.\n */\n fetchToBytes(ticket: string, options?: FetchOptions): Promise<Uint8Array>;\n /** Re-share an already-stored blob to mint a fresh ticket (e.g. after a restart). */\n reShare(ticket: string): Promise<string>;\n /** Shut the node down, releasing the fs-store lock. Safe if the node was never created. */\n shutdown(): Promise<void>;\n}\n\nexport interface CreateIrohTransportOptions {\n /** Directory for the persistent fs-store (e.g. `<agent-dir>/.iroh/`). */\n storePath: string;\n /**\n * Test seam: override how the native addon is loaded. Defaults to the real\n * dynamic `import('@number0/iroh')`. Production code never sets this.\n */\n loadModule?: () => Promise<IrohModule>;\n}\n\nconst ADDR_INFO_RELAY_AND_ADDRESSES = 'RelayAndAddresses';\nconst EXPORT_FORMAT_BLOB = 'Blob';\nconst EXPORT_MODE_COPY = 'Copy';\n\n// Specifiers are passed through variables so bundlers (Vite/esbuild) leave the\n// dynamic import for the Node loader at runtime - we never want the native addon\n// bundled. The published `package.json` has a non-standard `main`: Node's CJS\n// loader falls back to `index.js`, but stricter loaders (e.g. vite-node under the\n// test runner) do not, so we fall back to the explicit `/index.js` subpath.\nconst IROH_MODULE_IDS = ['@number0/iroh', '@number0/iroh/index.js'];\n\nasync function loadIrohModule(): Promise<IrohModule> {\n let lastError: unknown;\n for (const moduleId of IROH_MODULE_IDS) {\n try {\n return (await import(moduleId)) as unknown as IrohModule;\n } catch (cause) {\n lastError = cause;\n }\n }\n throw new Error(\n 'iroh file transfer is unavailable: the optional @number0/iroh native addon is not installed.',\n { cause: lastError },\n );\n}\n\n/** Thrown when a bounded iroh op exceeds its deadline (distinct so callers can react). */\nexport class IrohTimeoutError extends Error {}\n\nfunction rejectAfter(\n timeoutMs: number,\n label: string,\n): { promise: Promise<never>; cancel: () => void } {\n let timer: ReturnType<typeof setTimeout>;\n const promise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(\n () => reject(new IrohTimeoutError(`${label} timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n });\n return { promise, cancel: () => clearTimeout(timer) };\n}\n\n/** Thrown when a bounded iroh op is abandoned via an AbortSignal (distinct from timeout). */\nclass IrohAbortError extends Error {}\n\n/**\n * Race partner that rejects when `signal` aborts. Returns `promise: undefined` when no\n * signal is given (caller omits it from the race - no dangling promise). `cancel()`\n * detaches the listener and is safe to call when none was attached.\n */\nfunction rejectOnAbort(signal: AbortSignal | undefined): {\n promise: Promise<never> | undefined;\n cancel: () => void;\n} {\n if (signal === undefined) {\n return { promise: undefined, cancel: () => {} };\n }\n let onAbort: () => void = () => {};\n const promise = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(new IrohAbortError('iroh fetch aborted'));\n if (signal.aborted) {\n onAbort();\n return;\n }\n signal.addEventListener('abort', onAbort, { once: true });\n });\n return { promise, cancel: () => signal.removeEventListener('abort', onAbort) };\n}\n\n/**\n * Create a lazily-initialized iroh transport bound to a persistent fs-store.\n * The node (and the native addon import) is created on first use and shared\n * single-flight across concurrent callers.\n */\nexport function createIrohTransport(options: CreateIrohTransportOptions): IrohBlobTransport {\n let nodePromise: Promise<{ module: IrohModule; node: IrohNode }> | null = null;\n // Set while a node teardown-and-recreate is in flight. `getNode` awaits it so no\n // caller spins up a SECOND `Iroh.persistent` on the still-locked fs-store mid-reset.\n let resetPromise: Promise<void> | null = null;\n\n // Tear the shared node down so the next `getNode` recreates a fresh one - the\n // automatic equivalent of a manual agent restart when a native call wedges the\n // node. Single-flight; `nodePromise` is nulled SYNCHRONOUSLY (no await before\n // `resetPromise` is assigned) so a concurrent `getNode` either awaits the reset or\n // sees a fresh node, never a null+no-reset gap that would create a duplicate node.\n const resetNode = (): Promise<void> => {\n if (resetPromise) {\n return resetPromise;\n }\n const pending = nodePromise;\n nodePromise = null;\n const running = (async () => {\n if (!pending) {\n return;\n }\n const loaded = await pending.catch(() => null);\n if (!loaded) {\n return;\n }\n // A wedged node's shutdown may itself hang, so bound it; recreate regardless.\n const timeout = rejectAfter(DEFAULTS.IROH_SEED_TIMEOUT_MS, 'iroh node shutdown');\n try {\n await Promise.race([loaded.node.node.shutdown(), timeout.promise]);\n } catch {\n // ignore - we proceed to recreate on the next getNode either way\n } finally {\n timeout.cancel();\n }\n })();\n resetPromise = running.finally(() => {\n resetPromise = null;\n });\n return resetPromise;\n };\n\n const getNode = async (): Promise<{ module: IrohModule; node: IrohNode }> => {\n // Wait out any in-flight reset before checking/creating the node.\n if (resetPromise) {\n await resetPromise;\n }\n if (!nodePromise) {\n const pending = (async () => {\n const module = await (options.loadModule ?? loadIrohModule)();\n const node = await module.Iroh.persistent(options.storePath);\n return { module, node };\n })();\n nodePromise = pending;\n // Never cache a rejection: a failed node creation (e.g. another process is\n // briefly holding the fs-store lock) must not permanently disable the\n // transport. Clear it so the next call retries; concurrent in-flight callers\n // still share this single attempt.\n pending.catch(() => {\n if (nodePromise === pending) {\n nodePromise = null;\n }\n });\n }\n return nodePromise;\n };\n\n // Bound a seed/share op so a wedged native call surfaces as a thrown error\n // instead of an indefinite hang. On TIMEOUT (only - not ordinary errors, which\n // would thrash the node) reset the node so the next op gets a fresh one.\n const withSeedTimeout = async <T>(label: string, op: () => Promise<T>): Promise<T> => {\n const timeout = rejectAfter(DEFAULTS.IROH_SEED_TIMEOUT_MS, label);\n try {\n return await Promise.race([op(), timeout.promise]);\n } catch (error) {\n if (error instanceof IrohTimeoutError) {\n void resetNode();\n }\n throw error;\n } finally {\n timeout.cancel();\n }\n };\n\n const seedPath = (path: string): Promise<SeedResult> =>\n withSeedTimeout('iroh seed (path)', async () => {\n const { module, node } = await getNode();\n let size = 0;\n const outcome = await new Promise<{ hash: string; format: string }>((resolve, reject) => {\n node.blobs\n .addFromPath(\n path,\n false,\n module.SetTagOption.auto(),\n { wrap: false },\n (err, progress) => {\n if (err !== null) {\n reject(err);\n return;\n }\n if (progress.found !== undefined) {\n size = Number(progress.found.size);\n }\n if (progress.allDone !== undefined) {\n resolve(progress.allDone);\n }\n },\n )\n .catch(reject);\n });\n const ticket = await node.blobs.share(\n outcome.hash,\n outcome.format,\n ADDR_INFO_RELAY_AND_ADDRESSES,\n );\n return { ticket: ticket.toString(), size };\n });\n\n const seedBytes = (bytes: Uint8Array): Promise<SeedResult> =>\n withSeedTimeout('iroh seed (bytes)', async () => {\n const { node } = await getNode();\n const outcome = await node.blobs.addBytes(Array.from(bytes));\n const ticket = await node.blobs.share(\n outcome.hash,\n outcome.format,\n ADDR_INFO_RELAY_AND_ADDRESSES,\n );\n return { ticket: ticket.toString(), size: Number(outcome.size) };\n });\n\n // Download a blob by ticket, enforcing the size cap on the BLAKE3-verified\n // total and a per-fetch timeout, deleting any (partial) blob on failure.\n // Returns the node + hash so the caller can export to disk or read to memory.\n const downloadBounded = async (\n ticketStr: string,\n fetchOptions: FetchOptions | undefined,\n ): Promise<{ node: IrohNode; hash: string }> => {\n const { module, node } = await getNode();\n const maxBytes = fetchOptions?.maxBytes ?? LIMITS.MAX_FILE_SIZE;\n const timeoutMs = fetchOptions?.timeoutMs ?? DEFAULTS.IROH_FETCH_TIMEOUT_MS;\n const ticket = module.BlobTicket.fromString(ticketStr);\n const hash = ticket.hash;\n\n const timeout = rejectAfter(timeoutMs, 'iroh fetch');\n const abort = rejectOnAbort(fetchOptions?.signal);\n try {\n const downloadPromise = new Promise<void>((resolve, reject) => {\n node.blobs\n .download(hash, ticket.asDownloadOptions(), (err, progress) => {\n if (err !== null) {\n reject(err);\n return;\n }\n // Enforce on the BLAKE3-verified size, never the descriptor's claim.\n if (progress.found !== undefined && Number(progress.found.size) > maxBytes) {\n reject(\n new Error(`file exceeds MAX_FILE_SIZE: ${progress.found.size} > ${maxBytes} bytes`),\n );\n return;\n }\n if (progress.progress !== undefined && Number(progress.progress.offset) > maxBytes) {\n reject(new Error(`file exceeds MAX_FILE_SIZE during transfer (> ${maxBytes} bytes)`));\n return;\n }\n if (progress.allDone !== undefined) {\n resolve();\n }\n })\n .catch(reject);\n });\n // The abort racer is only added when a signal was supplied, so the common\n // (no-signal) path never allocates a dangling promise.\n const racers: Promise<unknown>[] = [timeout.promise, downloadPromise];\n if (abort.promise !== undefined) {\n racers.push(abort.promise);\n }\n await Promise.race(racers);\n } catch (error) {\n // Best-effort reclaim of any (partial) data for the rejected blob. The napi\n // binding exposes no mid-transfer cancel, so an oversized (or aborted) transfer may\n // finish in the background before this runs; the cap still prevents the caller from\n // exporting/using it, and this keeps the store from retaining it.\n await node.blobs.deleteBlob(hash).catch(() => {});\n throw error;\n } finally {\n timeout.cancel();\n abort.cancel();\n }\n // Re-check the verified size against the cap. The progress-callback guard\n // above only fires while bytes transfer; a blob ALREADY resident in the local\n // store completes via `allDone` alone (no `found`/`progress`), so without this\n // an oversized resident blob would slip past the cap before `readToBytes`\n // pulls it into memory. `blobs.size` reports the BLAKE3-verified total.\n const verifiedSize = Number(await node.blobs.size(hash));\n if (verifiedSize > maxBytes) {\n await node.blobs.deleteBlob(hash).catch(() => {});\n throw new Error(`file exceeds MAX_FILE_SIZE: ${verifiedSize} > ${maxBytes} bytes`);\n }\n return { node, hash };\n };\n\n const fetchToPath = async (\n ticketStr: string,\n dest: string,\n fetchOptions?: FetchOptions,\n ): Promise<void> => {\n const { node, hash } = await downloadBounded(ticketStr, fetchOptions);\n await node.blobs.export(hash, dest, EXPORT_FORMAT_BLOB, EXPORT_MODE_COPY);\n };\n\n const fetchToBytes = async (\n ticketStr: string,\n fetchOptions?: FetchOptions,\n ): Promise<Uint8Array> => {\n const { node, hash } = await downloadBounded(ticketStr, fetchOptions);\n // The cap was enforced on the verified size during download, so reading the\n // whole blob into memory here is bounded by the caller's maxBytes.\n const bytes = await node.blobs.readToBytes(hash);\n return Uint8Array.from(bytes);\n };\n\n const reShare = (ticketStr: string): Promise<string> =>\n withSeedTimeout('iroh re-share', async () => {\n const { module, node } = await getNode();\n const ticket = module.BlobTicket.fromString(ticketStr);\n const fresh = await node.blobs.share(\n ticket.hash,\n ticket.format,\n ADDR_INFO_RELAY_AND_ADDRESSES,\n );\n return fresh.toString();\n });\n\n const shutdown = async (): Promise<void> => {\n if (!nodePromise) {\n return;\n }\n const pending = nodePromise;\n nodePromise = null;\n const loaded = await pending.catch(() => null);\n if (loaded) {\n await loaded.node.node.shutdown();\n }\n };\n\n return { seedPath, seedBytes, fetchToPath, fetchToBytes, reShare, shutdown };\n}\n"]}
|