@agentproto/catalog-sync 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +77 -23
- package/dist/bin.mjs.map +1 -1
- package/dist/generators/llm-opencode.mjs.map +1 -1
- package/dist/generators/llm-openrouter.mjs +64 -10
- package/dist/generators/llm-openrouter.mjs.map +1 -1
- package/dist/index.mjs +79 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/snapshots/image-replicate.json +202 -349
- package/snapshots/llm-anthropic.json +308 -15
- package/snapshots/llm-huggingface.json +2279 -2189
- package/snapshots/llm-moonshot.json +25 -25
- package/snapshots/llm-opencode-go.json +32 -0
- package/snapshots/llm-opencode-zen.json +85 -0
- package/snapshots/llm-openrouter.json +5249 -3776
- package/snapshots/voice-elevenlabs.json +6361 -6061
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types.ts","../../src/added-at.ts","../../src/generators/llm-opencode.ts"],"names":["__dirname"],"mappings":";;;;;;;;;;;;;AAsDO,SAAS,gBAAgB,CAAA,EAAuC;AACrE,EAAA,OAAO,CAAA;AACT;AC5BA,IAAMA,WAAA,GAAY,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAGxD,SAAS,cAAA,GAAyB;AAChC,EAAA,OAAO,OAAA,CAAQA,aAAW,IAAI,CAAA;AAChC;AAGO,SAAS,cAAc,QAAA,EAA0B;AACtD,EAAA,OAAO,KAAK,cAAA,EAAe,EAAG,QAAA,EAAU,CAAA,EAAG,QAAQ,CAAA,KAAA,CAAO,CAAA;AAC5D;AAGO,SAAS,cAAc,QAAA,EAA0B;AACtD,EAAA,OAAO,gCAAgC,QAAQ,CAAA,KAAA,CAAA;AACjD;AAKO,SAAS,WAAW,QAAA,EAAiC;AAC1D,EAAA,MAAM,IAAA,GAAO,cAAc,QAAQ,CAAA;AACnC,EAAA,IAAI,CAAC,UAAA,CAAW,IAAI,CAAA,SAAU,EAAC;AAC/B,EAAA,IAAI;AACF,IAAA,MAAM,SAAkB,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,MAAM,CAAC,CAAA;AAC7D,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,MAAM,OAAA,CAAQ,MAAM,CAAA,EAAG,OAAO,EAAC;AAC5E,IAAA,OAAO,MAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAGO,SAAS,gBAAgB,MAAA,EAA+B;AAC7D,EAAA,MAAM,SAAwB,EAAC;AAC/B,EAAA,KAAA,MAAW,EAAA,IAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,IAAA,EAAK,EAAG,MAAA,CAAO,EAAE,CAAA,GAAI,MAAA,CAAO,EAAE,CAAA;AACnE,EAAA,OAAO,GAAG,IAAA,CAAK,SAAA,CAAU,MAAA,EAAQ,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAC3C;AAaO,SAAS,oBAAA,CACd,UAAA,EACA,QAAA,EACA,SAAA,EACA,KAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAsB,EAAE,GAAG,QAAA,EAAS;AAC1C,EAAA,KAAA,MAAW,MAAM,UAAA,EAAY;AAC3B,IAAA,IAAI,IAAA,CAAK,EAAE,CAAA,KAAM,MAAA,EAAW;AAC5B,IAAA,IAAA,CAAK,EAAE,CAAA,GAAI,SAAA,CAAU,EAAE,CAAA,IAAK,KAAA;AAAA,EAC9B;AACA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,QAAA,GAAmB;AACjC,EAAA,OAAA,qBAAW,IAAA,EAAK,EAAE,aAAY,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAC7C;;;ACvDA,IAAM,cAAA,GAAiB,6BAAA;AAcvB,IAAM,WAAA,GAAc,EACjB,MAAA,CAAO;AAAA,EACN,EAAA,EAAI,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACxB,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC1B,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAE5B,YAAA,EAAc,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAClC,QAAA,EAAU,EACP,MAAA,CAAO;AAAA;AAAA;AAAA;AAAA,IAIN,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAC1B,CAAA,CACA,WAAA,EAAY,CACZ,QAAA,EAAS;AAAA,EACZ,IAAA,EAAM,EACH,MAAA,CAAO;AAAA,IACN,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAC3B,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAC5B,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAChC,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAClC,CAAA,CACA,WAAA,EAAY,CACZ,QAAA;AACL,CAAC,EACA,WAAA,EAAY;AAIf,IAAM,cAAA,GAAiB,EACpB,MAAA,CAAO;AAAA,EACN,EAAA,EAAI,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACxB,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAE1B,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzB,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzB,KAAK,CAAA,CAAE,KAAA,CAAM,EAAE,MAAA,EAAQ,EAAE,QAAA,EAAS;AAAA,EAClC,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzB,MAAA,EAAQ,EAAE,MAAA,CAAO,CAAA,CAAE,QAAO,EAAG,WAAW,EAAE,QAAA;AAC5C,CAAC,EACA,WAAA,EAAY;AAMf,IAAM,iBAAiB,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,MAAA,IAAU,cAAc,CAAA;AAkB1D,SAAS,OAAO,CAAA,EAAmB;AACjC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,GAAS,CAAA,GAAI,GAAA;AACrC;AAGA,SAAS,IAAI,CAAA,EAAmB;AAC9B,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,GAAG,OAAO,GAAA;AAChC,EAAA,OAAO,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA,CAAA;AACrB;AAQA,SAAS,eAAA,CACP,YACA,UAAA,EACoB;AACpB,EAAA,IAAI,eAAe,MAAA,IAAa,CAAC,OAAO,QAAA,CAAS,UAAU,GAAG,OAAO,MAAA;AACrE,EAAA,IAAI,UAAA,IAAc,GAAG,OAAO,MAAA;AAC5B,EAAA,OAAO,MAAA,CAAO,aAAa,UAAU,CAAA;AACvC;AA0BA,IAAM,mBAAA,GAAgE;AAAA,EACpE,CAAC,YAAY,WAAW,CAAA;AAAA,EACxB,CAAC,YAAY,QAAQ,CAAA;AAAA,EACrB,CAAC,SAAS,QAAQ,CAAA;AAAA,EAClB,CAAC,UAAU,MAAM,CAAA;AAAA,EACjB,CAAC,SAAS,MAAM,CAAA;AAAA,EAChB,CAAC,UAAU,YAAY,CAAA;AAAA,EACvB,CAAC,cAAc,UAAU,CAAA;AAAA,EACzB,CAAC,aAAa,SAAS,CAAA;AAAA,EACvB,CAAC,SAAS,MAAM,CAAA;AAAA,EAChB,CAAC,UAAU,QAAQ,CAAA;AAAA,EACnB,CAAC,SAAS,SAAS,CAAA;AAAA,EACnB,CAAC,aAAa,SAAS,CAAA;AAAA,EACvB,CAAC,iBAAiB,aAAa,CAAA;AAAA,EAC/B,CAAC,cAAc,QAAQ,CAAA;AAAA,EACvB,CAAC,aAAa,UAAU,CAAA;AAAA,EACxB,CAAC,YAAY,UAAU,CAAA;AAAA,EACvB,CAAC,WAAW,QAAQ,CAAA;AAAA,EACpB,CAAC,gBAAgB,MAAM;AACzB,CAAA;AAGO,SAAS,yBAAyB,MAAA,EAAwB;AAC/D,EAAA,KAAA,MAAW,CAAC,OAAA,EAAS,MAAM,CAAA,IAAK,mBAAA,EAAqB;AACnD,IAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,EAAG,OAAO,MAAA;AAAA,EACnC;AACA,EAAA,OAAO,UAAA;AACT;AAmBA,SAAS,QAAA,CAAS,UAAkB,MAAA,EAAwB;AAC1D,EAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAC9B;AAEA,SAAS,eAAe,CAAA,EAA4B;AAClD,EAAA,MAAM,MAAA,GAAmB;AAAA,IACvB,CAAA,YAAA,EAAe,GAAA,CAAI,CAAA,CAAE,UAAU,CAAC,CAAA,CAAA;AAAA,IAChC,CAAA,aAAA,EAAgB,GAAA,CAAI,CAAA,CAAE,WAAW,CAAC,CAAA;AAAA,GACpC;AACA,EAAA,IAAI,CAAA,CAAE,wBAAwB,MAAA,EAAW;AACvC,IAAA,MAAA,CAAO,KAAK,CAAA,qBAAA,EAAwB,GAAA,CAAI,CAAA,CAAE,mBAAmB,CAAC,CAAA,CAAE,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,CAAA,CAAE,yBAAyB,MAAA,EAAW;AACxC,IAAA,MAAA,CAAO,KAAK,CAAA,sBAAA,EAAyB,GAAA,CAAI,CAAA,CAAE,oBAAoB,CAAC,CAAA,CAAE,CAAA;AAAA,EACpE;AACA,EAAA,IAAI,CAAA,CAAE,YAAY,MAAA,EAAW;AAC3B,IAAA,MAAA,CAAO,KAAK,CAAA,SAAA,EAAY,IAAA,CAAK,UAAU,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,CAAA;AAAA,EACrD;AACA,EAAA,MAAA,CAAO,KAAK,CAAA,QAAA,EAAW,IAAA,CAAK,UAAU,CAAA,CAAE,MAAM,CAAC,CAAA,CAAE,CAAA;AACjD,EAAA,MAAA,CAAO,KAAK,CAAA,UAAA,EAAa,IAAA,CAAK,UAAU,CAAA,CAAE,QAAQ,CAAC,CAAA,CAAE,CAAA;AACrD,EAAA,OAAO,CAAA;AAAA,IAAA,EAAU,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,GAAA,CAAA;AACzC;AAoBA,IAAM,EAAA,GAAuB;AAAA,EAC3B,WAAA,EAAa,aAAA;AAAA,EACb,QAAA,EAAU,iBAAA;AAAA,EACV,aAAA,EAAe,iBAAA;AAAA,EACf,UAAA,EAAY,gEAAA;AAAA,EACZ,WAAA,EAAa,oBAAA;AAAA,EACb,cAAA,EAAgB,8BAAA;AAAA,EAChB,KAAA,EAAO;AACT,CAAA;AAEA,IAAM,GAAA,GAAwB;AAAA,EAC5B,WAAA,EAAa,UAAA;AAAA,EACb,QAAA,EAAU,kBAAA;AAAA,EACV,aAAA,EAAe,kBAAA;AAAA,EACf,UAAA,EAAY,iEAAA;AAAA,EACZ,WAAA,EAAa,qBAAA;AAAA,EACb,cAAA,EAAgB,+BAAA;AAAA,EAChB,KAAA,EAAO;AACT,CAAA;AAEA,SAAS,aAAA,CACP,QAAA,EACA,OAAA,EACA,YAAA,EACQ;AACR,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,OAAO,EAAE,IAAA,EAAK;AAEvC,EAAA,MAAM,KAAA,GAAkB;AAAA,IACtB,CAAA,+CAAA,EAAkD,SAAS,aAAa,CAAA,EAAA,CAAA;AAAA,IACxE,yFAAA;AAAA,IACA,cAAc,cAAc,CAAA,YAAA,EAAe,SAAS,WAAW,CAAA,SAAA,EAAO,SAAS,KAAK,CAAA,CAAA,CAAA;AAAA,IACpF,2EAAA;AAAA,IACA,oEAAA;AAAA,IACA,kEAAA;AAAA,IACA,0EAAA;AAAA,IACA,2EAAA;AAAA,IACA,wEAAA;AAAA,IACA,iFAAA;AAAA,IACA,qBAAA;AAAA,IACA,6EAAA;AAAA,IACA,0EAAA;AAAA,IACA,uEAAA;AAAA,IACA,mFAAA;AAAA,IACA,kEAAA;AAAA,IACA,kEAAA;AAAA,IACA,EAAA;AAAA,IACA,gDAAA;AAAA,IACA,EAAA;AAAA,IACA,CAAA,aAAA,EAAgB,SAAS,WAAW,CAAA,gCAAA;AAAA,GACtC;AACA,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,EAAA,EAAK,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAE,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EAC1E;AACA,EAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,eAAA,EAAkB,QAAA,CAAS,KAAK,CAAA,6CAAA,CAA+C,CAAA;AAC1F,EAAA,KAAA,CAAM,KAAK,mFAA+E,CAAA;AAC1F,EAAA,KAAA,CAAM,KAAK,8EAA8E,CAAA;AACzF,EAAA,KAAA,CAAM,KAAK,4EAA4E,CAAA;AACvF,EAAA,KAAA,CAAM,KAAK,6EAA6E,CAAA;AACxF,EAAA,KAAA,CAAM,KAAK,+EAA+E,CAAA;AAC1F,EAAA,KAAA,CAAM,KAAK,8EAA8E,CAAA;AACzF,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,QAAA,CAAS,cAAc,CAAA,uBAAA,CAAyB,CAAA;AAC3E,EAAA,KAAA,MAAW,MAAM,CAAC,GAAG,YAAY,CAAA,CAAE,MAAK,EAAG;AACzC,IAAA,KAAA,CAAM,KAAK,CAAA,EAAA,EAAK,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EACvC;AACA,EAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAsCA,SAAS,eAAA,CAAgB,aAAqB,QAAA,EAAkC;AAC9E,EAAA,MAAM,SAAyC,EAAC;AAChD,EAAA,KAAA,MAAW,MAAA,IAAU,OAAO,IAAA,CAAK,QAAA,CAAS,UAAU,EAAE,CAAA,CAAE,IAAA,EAAK,EAAG;AAC9D,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,MAAA,GAAS,MAAM,CAAA;AACtC,IAAA,IAAI,CAAC,KAAA,EAAO;AAIZ,IAAA,MAAA,CAAO,MAAM,CAAA,GAAI;AAAA,MACf,GAAI,MAAM,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,KAAA,CAAM,EAAA,EAAG,GAAI,EAAC;AAAA,MACjD,GAAI,MAAM,IAAA,KAAS,MAAA,GAAY,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK,GAAI,EAAC;AAAA,MACvD,GAAI,MAAM,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO,GAAI,EAAC;AAAA,MAC7D,GAAI,MAAM,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,KAAA,CAAM,YAAA,EAAa,GAAI,EAAC;AAAA,MAC/E,GAAI,KAAA,CAAM,QAAA,EAAU,GAAA,KAAQ,SAAY,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,KAAA,CAAM,QAAA,CAAS,GAAA,EAAI,KAAM,EAAC;AAAA,MACrF,GAAI,KAAA,CAAM,IAAA,KAAS,MAAA,GACf;AAAA,QACE,IAAA,EAAM;AAAA,UACJ,GAAI,KAAA,CAAM,IAAA,CAAK,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAM,GAAI,EAAC;AAAA,UACpE,GAAI,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,MAAA,EAAO,GAAI,EAAC;AAAA,UACvE,GAAI,KAAA,CAAM,IAAA,CAAK,UAAA,KAAe,MAAA,GAC1B,EAAE,UAAA,EAAY,KAAA,CAAM,IAAA,CAAK,UAAA,EAAW,GACpC,EAAC;AAAA,UACL,GAAI,KAAA,CAAM,IAAA,CAAK,WAAA,KAAgB,MAAA,GAC3B,EAAE,WAAA,EAAa,KAAA,CAAM,IAAA,CAAK,WAAA,EAAY,GACtC;AAAC;AACP,UAEF;AAAC,KACP;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAA+B;AAAA,IACnC,GAAI,SAAS,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,QAAA,CAAS,EAAA,EAAG,GAAI,EAAC;AAAA,IACvD,GAAI,SAAS,IAAA,KAAS,MAAA,GAAY,EAAE,IAAA,EAAM,QAAA,CAAS,IAAA,EAAK,GAAI,EAAC;AAAA,IAC7D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D;AAAA,GACF;AAEA,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,WAAW,GAAG,SAAA,EAAU,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AACjE;AAKA,SAAS,gBAAgB,QAAA,EAA0B;AACjD,EAAA,OAAO,mCAAmC,QAAQ,CAAA,KAAA,CAAA;AACpD;AAYA,SAAS,oBAAoB,MAAA,EAAyD;AACpF,EAAA,OAAO,MAAA,CAAO,QAAQ,MAAM,CAAA,CACzB,OAAO,CAAC,GAAG,KAAK,CAAA,KAAM,KAAA,CAAM,UAAU,GAAA,KAAQ,mBAAmB,EACjE,GAAA,CAAI,CAAC,CAAC,MAAM,CAAA,KAAM,MAAM,CAAA,CACxB,IAAA,EAAK;AACV;AAEA,eAAe,WAAA,CACb,QAAA,EACA,MAAA,EACA,GAAA,EACyB;AACzB,EAAA,MAAM,SAAS,cAAA,CAAe,KAAA,CAAM,MAAM,GAAA,CAAI,WAAA,CAAY,MAAM,CAAC,CAAA;AACjE,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA;AAC5C,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,EAAG,SAAS,aAAa,CAAA,iCAAA,EAAoC,SAAS,WAAW,CAAA,mBAAA,EACpE,OAAO,GAAG,CAAA,yCAAA;AAAA,KACzB;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,MAAA,IAAU,EAAC;AAEnC,EAAA,MAAM,UAA2C,EAAC;AAClD,EAAA,MAAM,aAAqC,EAAC;AAC5C,EAAA,KAAA,MAAW,CAAC,MAAA,EAAQ,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACpD,IAAA,MAAM,UAAA,GAAa,MAAM,IAAA,EAAM,KAAA;AAC/B,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,EAAM,MAAA;AAIhC,IAAA,IAAI,UAAA,KAAe,MAAA,IAAa,WAAA,KAAgB,MAAA,EAAW;AAC3D,IAAA,IAAI,CAAC,OAAO,QAAA,CAAS,UAAU,KAAK,CAAC,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,EAAG;AAEnE,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,QAAA,CAAS,WAAA,EAAa,MAAM,CAAA;AACjD,IAAA,MAAM,KAAA,GAAyB;AAAA,MAC7B,UAAA,EAAY,OAAO,UAAU,CAAA;AAAA,MAC7B,WAAA,EAAa,OAAO,WAAW,CAAA;AAAA,MAC/B,MAAA,EAAQ,yBAAyB,MAAM,CAAA;AAAA,MACvC,UAAU,QAAA,CAAS;AAAA,KACrB;AACA,IAAA,MAAM,IAAA,GAAO,eAAA,CAAgB,KAAA,CAAM,IAAA,EAAM,YAAY,UAAU,CAAA;AAC/D,IAAA,IAAI,IAAA,KAAS,MAAA,EAAW,KAAA,CAAM,mBAAA,GAAsB,IAAA;AACpD,IAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,CAAM,IAAA,EAAM,aAAa,UAAU,CAAA;AACjE,IAAA,IAAI,KAAA,KAAU,MAAA,EAAW,KAAA,CAAM,oBAAA,GAAuB,KAAA;AAEtD,IAAA,OAAA,CAAQ,GAAG,CAAA,GAAI,KAAA;AACf,IAAA,IAAI,MAAM,YAAA,KAAiB,MAAA,EAAW,UAAA,CAAW,GAAG,IAAI,KAAA,CAAM,YAAA;AAAA,EAChE;AAEA,EAAA,MAAM,MAAA,GAAS,oBAAA;AAAA,IACb,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA,IACnB,UAAA,CAAW,SAAS,QAAQ,CAAA;AAAA,IAC5B,UAAA;AAAA,IACA,QAAA;AAAS,GACX;AACA,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,GAAG,CAAA;AAE9E,EAAA,OAAO;AAAA,IACL,CAAC,SAAS,UAAU,GAAG,cAAc,QAAA,EAAU,OAAA,EAAS,mBAAA,CAAoB,MAAM,CAAC,CAAA;AAAA,IACnF,CAAC,aAAA,CAAc,QAAA,CAAS,QAAQ,CAAC,GAAG,gBAAgB,MAAM,CAAA;AAAA,IAC1D,CAAC,gBAAgB,QAAA,CAAS,QAAQ,CAAC,GAAG,eAAA,CAAgB,QAAA,CAAS,WAAA,EAAa,QAAQ;AAAA,GACtF;AACF;AAKA,IAAM,SAAA,GAAY,CAAC,EAAE,EAAA,EAAI,GAAG,QAAA,EAAU,GAAA,EAAK,gBAAgB,CAAA;AAC3D,IAAM,UAAA,GAAa,CAAC,EAAE,EAAA,EAAI,IAAI,QAAA,EAAU,GAAA,EAAK,gBAAgB,CAAA;AAEtD,IAAM,yBAAyB,eAAA,CAAgB;AAAA,EACpD,MAAM,EAAA,CAAG,aAAA;AAAA,EACT,QAAA,EAAU,KAAA;AAAA,EACV,OAAA,EAAS,SAAA;AAAA,EACT,UAAU,CAAA,GAAA,KAAO,WAAA,CAAY,IAAI,SAAA,CAAU,CAAC,GAAI,GAAG;AACrD,CAAC;AAEM,IAAM,0BAA0B,eAAA,CAAgB;AAAA,EACrD,MAAM,GAAA,CAAI,aAAA;AAAA,EACV,QAAA,EAAU,KAAA;AAAA,EACV,OAAA,EAAS,UAAA;AAAA,EACT,UAAU,CAAA,GAAA,KAAO,WAAA,CAAY,KAAK,UAAA,CAAW,CAAC,GAAI,GAAG;AACvD,CAAC","file":"llm-opencode.mjs","sourcesContent":["/**\n * catalog-sync generator contract.\n *\n * FROZEN — a sibling agent builds provider generators against this surface.\n * Do not change field names, shapes, or the `defineGenerator` signature\n * without coordinating. Adding new generators is the expected extension\n * path; editing this file is not.\n */\n\nexport interface CatalogSource {\n id: string\n url: string\n /** HTTP method for the live (`--refresh`) fetch. Default `\"GET\"`. */\n method?: \"GET\" | \"POST\"\n /**\n * Request headers for the live fetch. A value may embed `env:VAR_NAME`\n * tokens, resolved from `process.env` at fetch time (e.g.\n * `{ \"xi-api-key\": \"env:ELEVENLABS_API_KEY\" }` or\n * `{ Authorization: \"Bearer env:MINIMAX_API_KEY\" }`). If any referenced env\n * var is unset, the source is treated as un-refreshable — the committed\n * snapshot is reused rather than fetched without auth. Offline reads\n * (default, no `--refresh`) ignore headers entirely.\n */\n headers?: Record<string, string>\n /** JSON request body for POST sources (e.g. MiniMax `get_voice`). */\n body?: unknown\n}\n\nexport interface GeneratorContext {\n /**\n * Reads the pinned snapshot at `snapshots/<src.id>.json`; if missing and\n * `refresh=true`, fetches `src.url` and writes the snapshot. Returns\n * parsed JSON.\n *\n * Offline by default: when no snapshot exists and `refresh` is false this\n * throws — generators MUST be runnable without network access so CI and\n * tests are deterministic. The only network path is behind `--refresh`.\n */\n fetchSource(src: CatalogSource): Promise<unknown>\n refresh: boolean\n}\n\n/** Repo-relative path → TS source text. */\nexport type GeneratedFiles = Record<string, string>\n\nexport interface CatalogGenerator {\n /** e.g. \"llm:openrouter\" */\n name: string\n modality: \"llm\" | \"image\" | \"video\" | \"audio\" | \"voice\"\n sources: CatalogSource[]\n generate(ctx: GeneratorContext): Promise<GeneratedFiles>\n}\n\n/** Identity helper — lets a generator module declare itself with type inference. */\nexport function defineGenerator(g: CatalogGenerator): CatalogGenerator {\n return g\n}\n","/**\n * `addedAt` ledger — tracks, per generator, the ISO date each model id was\n * FIRST seen by a sync run. See `README.md` (\"addedAt convention\") for the\n * full contract; this module is the mechanism.\n *\n * Invariant: once an id has a ledger entry, that date is NEVER overwritten —\n * not even if the source's own `created` timestamp for that id later\n * resolves to something different (a provider backdating/correcting its own\n * metadata must not rewrite catalog history). New ids are backfilled from\n * the source's own creation timestamp when it has one, else stamped with\n * the current run's date.\n *\n * Ledger entries are never dropped when an id disappears from a run — the\n * ledger records \"first ever seen\", not \"currently present\". A model that\n * vanishes from a provider's list and later reappears keeps its original\n * `addedAt` rather than looking newly added.\n *\n * Each generator owns one ledger file, committed at\n * `packages/catalog-sync/ledger/<generator-id>.json` (id → ISO date, sorted\n * keys, deterministic serialization) — included as a normal entry in the\n * generator's `GeneratedFiles` return value, so `runner.ts`'s existing\n * diff/write logic covers it for free (including `--check` drift detection).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/** The catalog-sync package directory — see `runner.ts`'s `catalogSyncDir`. */\nfunction catalogSyncDir(): string {\n return resolve(__dirname, \"..\")\n}\n\n/** Absolute path to a generator's committed addedAt ledger. */\nexport function ledgerAbsPath(ledgerId: string): string {\n return join(catalogSyncDir(), \"ledger\", `${ledgerId}.json`)\n}\n\n/** Repo-relative path for a ledger — for inclusion in a generator's `GeneratedFiles`. */\nexport function ledgerRelPath(ledgerId: string): string {\n return `packages/catalog-sync/ledger/${ledgerId}.json`\n}\n\nexport type AddedAtLedger = Record<string, string>\n\n/** Reads a committed ledger from disk. Missing/unparseable → empty ledger. */\nexport function readLedger(ledgerId: string): AddedAtLedger {\n const path = ledgerAbsPath(ledgerId)\n if (!existsSync(path)) return {}\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"))\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return {}\n return parsed as AddedAtLedger\n } catch {\n return {}\n }\n}\n\n/** Deterministic (sorted-key) JSON serialization, matching the `*.generated.ts` style of a trailing newline. */\nexport function serializeLedger(ledger: AddedAtLedger): string {\n const sorted: AddedAtLedger = {}\n for (const id of Object.keys(ledger).sort()) sorted[id] = ledger[id]!\n return `${JSON.stringify(sorted, null, 2)}\\n`\n}\n\n/**\n * Merge the current run's ids into a ledger.\n *\n * - id already in `previous` → keep the existing stamp verbatim (never\n * mutated), regardless of what `createdAt` says this run.\n * - id new to the ledger, source has a native creation timestamp for it →\n * backfill with `createdAt[id]`.\n * - id new to the ledger, no native timestamp → stamp with `today`.\n * - ids in `previous` but absent from `currentIds` this run are kept as-is\n * (see module doc comment — the ledger is \"first ever seen\").\n */\nexport function computeAddedAtLedger(\n currentIds: readonly string[],\n previous: AddedAtLedger,\n createdAt: Readonly<Record<string, string>>,\n today: string\n): AddedAtLedger {\n const next: AddedAtLedger = { ...previous }\n for (const id of currentIds) {\n if (next[id] !== undefined) continue\n next[id] = createdAt[id] ?? today\n }\n return next\n}\n\n/** Today's date as `YYYY-MM-DD`, in UTC. */\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\n/** Converts a provider's Unix-seconds creation timestamp to `YYYY-MM-DD`. */\nexport function isoDateFromUnixSeconds(seconds: number): string {\n return new Date(seconds * 1000).toISOString().slice(0, 10)\n}\n","import { z } from \"zod\"\n\nimport { defineGenerator, type GeneratedFiles, type GeneratorContext } from \"../types.js\"\nimport {\n computeAddedAtLedger,\n ledgerRelPath,\n readLedger,\n serializeLedger,\n todayIso,\n} from \"../added-at.js\"\n\n/**\n * OpenCode's two hosted billing endpoints, both sourced from models.dev.\n *\n * - `opencode-go` — OpenCode Go, the flat $10/mo API-key subscription.\n * - `opencode` — OpenCode Zen, the pay-as-you-go sibling.\n *\n * They are DIFFERENT endpoints with different per-model prices and different\n * model lineups (Go serves no Claude/Gemini; Zen serves both), so each gets\n * its own generator, ledger, snapshot and route table. They only share a\n * source URL and an env var name (`OPENCODE_API_KEY` — see the note in\n * `@agentproto/model-catalog`'s `PROVIDER_KEY_ENV`: same name, two distinct\n * secrets).\n *\n * PINNED source. models.dev's `api.json` is the whole ecosystem's catalog\n * (~4.6 MB, 213 providers) and is fetched UNAUTHENTICATED — unlike Requesty,\n * there is no key to gate the refresh on, so `sources` carries no headers and\n * `--refresh` always hits the live URL.\n *\n * Because the payload is 4.6 MB and we model two providers out of 213, the\n * committed snapshot is a PROJECTION, not the raw response: each generator\n * emits `snapshots/<ledger-id>.json` as one of its {@link GeneratedFiles},\n * narrowed to its own provider and to the fields below, with deterministic key\n * order. The runner's `--refresh` writes the raw response to that path first\n * and this projection immediately overwrites it in the same run, so a refresh\n * never leaves 4.6 MB of unrelated providers in the tree. The projection keeps\n * the SOURCE's own shape (nested `provider.npm`, nested `cost`), so\n * {@link SnapshotSchema} parses the raw payload and the committed projection\n * identically and re-projecting a projection is a no-op.\n */\nconst MODELS_DEV_URL = \"https://models.dev/api.json\"\n\n// ── Source schema ────────────────────────────────────────────────────────\n// Everything is optional + passthrough: this schema is applied to the FULL\n// models.dev payload (213 providers), so one unrelated provider drifting a\n// field must not break our two generators. Prices are USD **per 1M tokens\n// ALREADY** (`cost.input: 1.4` = $1.40/1M) — unlike Requesty's per-token\n// numbers, so there is no ×1e6 conversion here. `cost.cache_read` /\n// `cost.cache_write` are absolute per-1M prices too, from which the\n// multipliers are derived. `cost.tiers` / `cost.context_over_200k` exist on\n// some entries and are deliberately NOT modelled: LLMPricing has no tiered\n// shape, and inventing one from an un-consumed field would be fabricated\n// pricing.\n\nconst ModelSchema = z\n .object({\n id: z.string().optional(),\n name: z.string().optional(),\n family: z.string().optional(),\n /** ISO `YYYY-MM-DD` already — no unix-seconds conversion (cf. Requesty). */\n release_date: z.string().optional(),\n provider: z\n .object({\n /** Which AI-SDK package serves this model — the ONLY discriminator\n * for the endpoint's three wire surfaces (see\n * {@link anthropicSurfaceIds}). */\n npm: z.string().optional(),\n })\n .passthrough()\n .optional(),\n cost: z\n .object({\n input: z.number().optional(),\n output: z.number().optional(),\n cache_read: z.number().optional(),\n cache_write: z.number().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough()\n\ntype SourceModel = z.infer<typeof ModelSchema>\n\nconst ProviderSchema = z\n .object({\n id: z.string().optional(),\n name: z.string().optional(),\n /** The endpoint's base URL, e.g. `https://opencode.ai/zen/go/v1`. */\n api: z.string().optional(),\n doc: z.string().optional(),\n env: z.array(z.string()).optional(),\n npm: z.string().optional(),\n models: z.record(z.string(), ModelSchema).optional(),\n })\n .passthrough()\n\ntype SourceProvider = z.infer<typeof ProviderSchema>\n\n/** models.dev's whole payload is `{ [providerId]: provider }`; a committed\n * projection is the same map narrowed to one key. */\nconst SnapshotSchema = z.record(z.string(), ProviderSchema)\n\n// ── Output shape (mirrors model-catalog's LLMPricing) ────────────────────\n\ninterface LLMPricingEntry {\n inputPer1M: number\n outputPer1M: number\n cacheReadMultiplier?: number\n cacheWriteMultiplier?: number\n /** ISO date this id was first seen by a sync run. See `../added-at.ts`. */\n addedAt?: string\n vendor: string\n provider: string\n}\n\n// ── Number helpers ──────────────────────────────────────────────────────\n\n/** Round to 6 decimal places, killing binary-float noise from the division. */\nfunction round6(n: number): number {\n return Math.round(n * 1_000_000) / 1_000_000\n}\n\n/** Shortest faithful decimal string (matches the other generated files). */\nfunction fmt(n: number): string {\n if (!Number.isFinite(n)) return \"0\"\n return `${round6(n)}`\n}\n\n/**\n * A cache price relative to the base input price. Only meaningful when the\n * base price is positive: every `-free` variant on Zen carries\n * `cost.input: 0` alongside `cost.cache_read: 0`, and 0/0 is NaN — a free\n * model has no cache DISCOUNT to express, so no multiplier is emitted.\n */\nfunction cacheMultiplier(\n cachePer1M: number | undefined,\n inputPer1M: number\n): number | undefined {\n if (cachePer1M === undefined || !Number.isFinite(cachePer1M)) return undefined\n if (inputPer1M <= 0) return undefined\n return round6(cachePer1M / inputPer1M)\n}\n\n// ── Vendor attribution ──────────────────────────────────────────────────\n\n/**\n * Model id prefix → vendor (who BUILT the model), matched in order.\n *\n * HEURISTIC by necessity: models.dev's opencode/opencode-go entries carry\n * BARE product ids (`glm-5.3`, not `z-ai/glm-5.3`) and no vendor field at\n * all — `family` is a product line (`glm`, `deepseek-flash`), not a builder.\n * Each slug below is the one the repo's own catalog already uses for that\n * builder (verified against `OPENROUTER_ROUTES`' `vendor` values, which is\n * why it's `x-ai` and not `xai`, `moonshotai` and not `moonshot`), and each\n * attribution was cross-checked against another models.dev provider that\n * namespaces the SAME id under its builder (`inclusionai/ling-3.0-flash`,\n * `nvidia/nemotron-3-super-…`, `arcee-ai/trinity-large-…`,\n * `poolside/laguna-s-2.1`, `cohere/north-mini-code`, `meta/muse-spark-1.2`).\n * A new id that matches nothing here falls back to `opencode` — the handful\n * of unattributed ids on these endpoints (`omen-alpha`, `ox-alpha-free`,\n * `big-pickle`, `x-preview-f-free`) are opencode's own stealth/preview\n * models with no disclosed builder.\n *\n * `vendor` is METADATA on the pricing entry. It is NOT the route key: the\n * route tables are keyed `<provider>/<bare-id>` (see {@link routeKey}), so a\n * mis-attributed vendor can never mis-route or mis-bill a spawn.\n */\nconst VENDOR_BY_ID_PREFIX: ReadonlyArray<readonly [RegExp, string]> = [\n [/^claude-/, \"anthropic\"],\n [/^gemini-/, \"google\"],\n [/^gpt-/, \"openai\"],\n [/^grok-/, \"x-ai\"],\n [/^glm-/, \"z-ai\"],\n [/^kimi-/, \"moonshotai\"],\n [/^deepseek-/, \"deepseek\"],\n [/^minimax-/, \"minimax\"],\n [/^qwen/, \"qwen\"],\n [/^mimo-/, \"xiaomi\"],\n [/^hy\\d/, \"tencent\"],\n [/^longcat-/, \"meituan\"],\n [/^(ling|ring)-/, \"inclusionai\"],\n [/^nemotron-/, \"nvidia\"],\n [/^trinity-/, \"arcee-ai\"],\n [/^laguna-/, \"poolside\"],\n [/^north-/, \"cohere\"],\n [/^muse-spark-/, \"meta\"],\n]\n\n/** Vendor for a bare model id — see {@link VENDOR_BY_ID_PREFIX}. */\nexport function vendorForOpencodeModelId(bareId: string): string {\n for (const [pattern, vendor] of VENDOR_BY_ID_PREFIX) {\n if (pattern.test(bareId)) return vendor\n }\n return \"opencode\"\n}\n\n// ── Serialization ────────────────────────────────────────────────────────\n\n/**\n * Route-table key: `<provider>/<bare-id>` — `opencode-go/glm-5.3`,\n * `opencode/claude-sonnet-4-6`.\n *\n * Deliberately NOT `<vendor>/<product>` like the OpenRouter / Requesty\n * tables. Those routers namespace their own ids by vendor, so their keys\n * round-trip through route-identity's `vendor/product@route` grammar.\n * models.dev gives these two endpoints BARE ids, and opencode's own config\n * spells them `opencode-go/glm-5.3` — which is also the form the runtime\n * derives the billing endpoint from (`modelIdPrefixProvider`) and the form\n * the opencode adapter puts on the wire. Keying by the provider makes all\n * three agree on one string, and `route-identity`'s opencode branches key\n * off exactly this (see the comment there for why they must run ahead of the\n * direct-vendor branch).\n */\nfunction routeKey(provider: string, bareId: string): string {\n return `${provider}/${bareId}`\n}\n\nfunction serializeEntry(e: LLMPricingEntry): string {\n const fields: string[] = [\n `inputPer1M: ${fmt(e.inputPer1M)}`,\n `outputPer1M: ${fmt(e.outputPer1M)}`,\n ]\n if (e.cacheReadMultiplier !== undefined) {\n fields.push(`cacheReadMultiplier: ${fmt(e.cacheReadMultiplier)}`)\n }\n if (e.cacheWriteMultiplier !== undefined) {\n fields.push(`cacheWriteMultiplier: ${fmt(e.cacheWriteMultiplier)}`)\n }\n if (e.addedAt !== undefined) {\n fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`)\n }\n fields.push(`vendor: ${JSON.stringify(e.vendor)}`)\n fields.push(`provider: ${JSON.stringify(e.provider)}`)\n return `{\\n ${fields.join(\",\\n \")},\\n }`\n}\n\n/** One endpoint's identity — everything the emitted files need to describe it. */\ninterface OpencodeEndpoint {\n /** models.dev provider key, and this repo's `CatalogProvider` value. */\n providerKey: string\n /** Generator + snapshot + ledger id. */\n ledgerId: string\n /** `llm:<name>` generator name. */\n generatorName: string\n /** Repo-relative emitted route table. */\n outputPath: string\n /** Exported route-table const. */\n routesConst: string\n /** Exported Anthropic-surface id list const. */\n anthropicConst: string\n /** Human label, used only in generated comments. */\n label: string\n}\n\nconst GO: OpencodeEndpoint = {\n providerKey: \"opencode-go\",\n ledgerId: \"llm-opencode-go\",\n generatorName: \"llm:opencode-go\",\n outputPath: \"packages/model-catalog/src/llm/opencode-go-routes.generated.ts\",\n routesConst: \"OPENCODE_GO_ROUTES\",\n anthropicConst: \"OPENCODE_GO_ANTHROPIC_MODELS\",\n label: \"OpenCode Go\",\n}\n\nconst ZEN: OpencodeEndpoint = {\n providerKey: \"opencode\",\n ledgerId: \"llm-opencode-zen\",\n generatorName: \"llm:opencode-zen\",\n outputPath: \"packages/model-catalog/src/llm/opencode-zen-routes.generated.ts\",\n routesConst: \"OPENCODE_ZEN_ROUTES\",\n anthropicConst: \"OPENCODE_ZEN_ANTHROPIC_MODELS\",\n label: \"OpenCode Zen\",\n}\n\nfunction serializeFile(\n endpoint: OpencodeEndpoint,\n entries: Record<string, LLMPricingEntry>,\n anthropicIds: readonly string[]\n): string {\n const keys = Object.keys(entries).sort()\n\n const lines: string[] = [\n `// AUTO-GENERATED by @agentproto/catalog-sync (${endpoint.generatorName}).`,\n \"// Do not edit by hand — re-run `pnpm --filter @agentproto/catalog-sync generate`.\",\n `// Source: ${MODELS_DEV_URL} (provider \"${endpoint.providerKey}\" — ${endpoint.label})`,\n \"// Prices come VERBATIM from the source's cost.input / cost.output, which\",\n \"// models.dev already publishes as USD per 1M tokens (no per-token\",\n \"// conversion, unlike the Requesty table). cacheReadMultiplier /\",\n \"// cacheWriteMultiplier are cost.cache_read / cost.cache_write expressed\",\n \"// relative to cost.input, and are omitted when the base input price is 0\",\n \"// (every `-free` variant) because a free model has no cache discount.\",\n \"// Zero-priced models are KEPT — on these endpoints zero is the truth, not\",\n \"// a missing price.\",\n \"// Keys are `<provider>/<bare-id>`, the form opencode's own config uses and\",\n \"// the form the runtime derives the billing endpoint from; `vendor` is a\",\n \"// heuristic attribution of the model's BUILDER and is metadata only.\",\n \"// addedAt is the ISO date this id was first seen by a sync run — backfilled\",\n \"// from the source's own `release_date`, then NEVER mutated; see\",\n \"// packages/catalog-sync/src/added-at.ts and the package README.\",\n \"\",\n 'import type { LLMPricing } from \"./catalog.js\"',\n \"\",\n `export const ${endpoint.routesConst}: Record<string, LLMPricing> = {`,\n ]\n for (const key of keys) {\n lines.push(` ${JSON.stringify(key)}: ${serializeEntry(entries[key]!)},`)\n }\n lines.push(\"}\")\n lines.push(\"\")\n lines.push(\"/**\")\n lines.push(` * BARE ids on ${endpoint.label} whose wire surface is the Anthropic Messages`)\n lines.push(\" * API — the source's `provider.npm === \\\"@ai-sdk/anthropic\\\"`. These are the\")\n lines.push(\" * ONLY ids reachable through the Anthropic-compatible gateway preset of the\")\n lines.push(\" * same name (claude-code / claude-sdk); the rest of the endpoint's lineup\")\n lines.push(\" * speaks OpenAI chat/completions or the OpenAI Responses API and has to go\")\n lines.push(\" * through the opencode adapter instead. Bare (not `<provider>/<id>`) because\")\n lines.push(\" * that is what an Anthropic client puts in `ANTHROPIC_MODEL` / on the wire.\")\n lines.push(\" */\")\n lines.push(`export const ${endpoint.anthropicConst}: readonly string[] = [`)\n for (const id of [...anthropicIds].sort()) {\n lines.push(` ${JSON.stringify(id)},`)\n }\n lines.push(\"]\")\n lines.push(\"\")\n return lines.join(\"\\n\")\n}\n\n// ── Snapshot projection ─────────────────────────────────────────────────\n\n/** The committed snapshot's per-model shape — a strict subset of the source's\n * own, so one schema parses both. */\ninterface ProjectedModel {\n id?: string\n name?: string\n family?: string\n release_date?: string\n provider?: { npm: string }\n cost?: {\n input?: number\n output?: number\n cache_read?: number\n cache_write?: number\n }\n}\n\n/** The committed snapshot's per-provider shape (see {@link ProjectedModel}). */\ninterface ProjectedProvider {\n id?: string\n name?: string\n api?: string\n doc?: string\n env?: readonly string[]\n npm?: string\n models: Record<string, ProjectedModel>\n}\n\n/**\n * Narrow the raw models.dev payload to one provider and to the fields this\n * generator models, with deterministic (sorted) model order and a fixed field\n * order. Shape-identical to the source, so {@link SnapshotSchema} parses this\n * projection and re-projecting it is a no-op — which is what makes `--check`\n * drift detection meaningful on the snapshot itself.\n */\nfunction projectSnapshot(providerKey: string, provider: SourceProvider): string {\n const models: Record<string, ProjectedModel> = {}\n for (const bareId of Object.keys(provider.models ?? {}).sort()) {\n const model = provider.models?.[bareId]\n if (!model) continue\n // Object-literal spreads, not assignments: JSON.stringify emits keys in\n // insertion order, so a FIXED literal order is what makes the committed\n // snapshot byte-stable across runs.\n models[bareId] = {\n ...(model.id !== undefined ? { id: model.id } : {}),\n ...(model.name !== undefined ? { name: model.name } : {}),\n ...(model.family !== undefined ? { family: model.family } : {}),\n ...(model.release_date !== undefined ? { release_date: model.release_date } : {}),\n ...(model.provider?.npm !== undefined ? { provider: { npm: model.provider.npm } } : {}),\n ...(model.cost !== undefined\n ? {\n cost: {\n ...(model.cost.input !== undefined ? { input: model.cost.input } : {}),\n ...(model.cost.output !== undefined ? { output: model.cost.output } : {}),\n ...(model.cost.cache_read !== undefined\n ? { cache_read: model.cost.cache_read }\n : {}),\n ...(model.cost.cache_write !== undefined\n ? { cache_write: model.cost.cache_write }\n : {}),\n },\n }\n : {}),\n }\n }\n\n const projected: ProjectedProvider = {\n ...(provider.id !== undefined ? { id: provider.id } : {}),\n ...(provider.name !== undefined ? { name: provider.name } : {}),\n ...(provider.api !== undefined ? { api: provider.api } : {}),\n ...(provider.doc !== undefined ? { doc: provider.doc } : {}),\n ...(provider.env !== undefined ? { env: provider.env } : {}),\n ...(provider.npm !== undefined ? { npm: provider.npm } : {}),\n models,\n }\n\n return `${JSON.stringify({ [providerKey]: projected }, null, 2)}\\n`\n}\n\n/** Repo-relative snapshot path for a source id — mirrors `runner.ts`'s\n * `snapshotPath`, expressed repo-relative so the projection can ride along\n * in {@link GeneratedFiles}. */\nfunction snapshotRelPath(ledgerId: string): string {\n return `packages/catalog-sync/snapshots/${ledgerId}.json`\n}\n\n// ── Generator ───────────────────────────────────────────────────────────\n\n/**\n * The bare ids served on the Anthropic Messages surface. models.dev\n * discriminates an endpoint's wire surfaces per model via `provider.npm`:\n * absent ⇒ OpenAI chat/completions, `@ai-sdk/anthropic` ⇒ Anthropic\n * Messages, `@ai-sdk/openai` ⇒ OpenAI Responses, `@ai-sdk/google` ⇒ Gemini.\n * Only the Anthropic set is emitted, because that is the one a caller has to\n * know: it is exactly what the gateway preset can serve.\n */\nfunction anthropicSurfaceIds(models: Readonly<Record<string, SourceModel>>): string[] {\n return Object.entries(models)\n .filter(([, model]) => model.provider?.npm === \"@ai-sdk/anthropic\")\n .map(([bareId]) => bareId)\n .sort()\n}\n\nasync function generateFor(\n endpoint: OpencodeEndpoint,\n source: { id: string; url: string },\n ctx: GeneratorContext\n): Promise<GeneratedFiles> {\n const parsed = SnapshotSchema.parse(await ctx.fetchSource(source))\n const provider = parsed[endpoint.providerKey]\n if (!provider) {\n throw new Error(\n `${endpoint.generatorName}: models.dev payload carries no \"${endpoint.providerKey}\" provider ` +\n `(source ${source.url}). Refusing to emit an empty route table.`\n )\n }\n const models = provider.models ?? {}\n\n const entries: Record<string, LLMPricingEntry> = {}\n const releasedAt: Record<string, string> = {}\n for (const [bareId, model] of Object.entries(models)) {\n const inputPer1M = model.cost?.input\n const outputPer1M = model.cost?.output\n // An entry with no published price at all is skipped: a fabricated 0 would\n // read as \"free\" to the billing surface. An explicit 0 IS kept (the `-free`\n // variants) — see the header comment in the emitted file.\n if (inputPer1M === undefined || outputPer1M === undefined) continue\n if (!Number.isFinite(inputPer1M) || !Number.isFinite(outputPer1M)) continue\n\n const key = routeKey(endpoint.providerKey, bareId)\n const entry: LLMPricingEntry = {\n inputPer1M: round6(inputPer1M),\n outputPer1M: round6(outputPer1M),\n vendor: vendorForOpencodeModelId(bareId),\n provider: endpoint.providerKey,\n }\n const read = cacheMultiplier(model.cost?.cache_read, inputPer1M)\n if (read !== undefined) entry.cacheReadMultiplier = read\n const write = cacheMultiplier(model.cost?.cache_write, inputPer1M)\n if (write !== undefined) entry.cacheWriteMultiplier = write\n\n entries[key] = entry\n if (model.release_date !== undefined) releasedAt[key] = model.release_date\n }\n\n const ledger = computeAddedAtLedger(\n Object.keys(entries),\n readLedger(endpoint.ledgerId),\n releasedAt,\n todayIso()\n )\n for (const [key, entry] of Object.entries(entries)) entry.addedAt = ledger[key]\n\n return {\n [endpoint.outputPath]: serializeFile(endpoint, entries, anthropicSurfaceIds(models)),\n [ledgerRelPath(endpoint.ledgerId)]: serializeLedger(ledger),\n [snapshotRelPath(endpoint.ledgerId)]: projectSnapshot(endpoint.providerKey, provider),\n }\n}\n\n/** models.dev needs no credential, so `headers` is deliberately absent — the\n * runner only reuses a committed snapshot instead of refreshing when a\n * referenced env var is missing, and there is none to miss here. */\nconst goSources = [{ id: GO.ledgerId, url: MODELS_DEV_URL }]\nconst zenSources = [{ id: ZEN.ledgerId, url: MODELS_DEV_URL }]\n\nexport const llmOpencodeGoGenerator = defineGenerator({\n name: GO.generatorName,\n modality: \"llm\",\n sources: goSources,\n generate: ctx => generateFor(GO, goSources[0]!, ctx),\n})\n\nexport const llmOpencodeZenGenerator = defineGenerator({\n name: ZEN.generatorName,\n modality: \"llm\",\n sources: zenSources,\n generate: ctx => generateFor(ZEN, zenSources[0]!, ctx),\n})\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts","../../src/added-at.ts","../../src/generators/llm-opencode.ts"],"names":["__dirname"],"mappings":";;;;;;;;;;;;;AAsDO,SAAS,gBAAgB,CAAA,EAAuC;AACrE,EAAA,OAAO,CAAA;AACT;AC5BA,IAAMA,WAAA,GAAY,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAGxD,SAAS,cAAA,GAAyB;AAChC,EAAA,OAAO,OAAA,CAAQA,aAAW,IAAI,CAAA;AAChC;AAGO,SAAS,cAAc,QAAA,EAA0B;AACtD,EAAA,OAAO,KAAK,cAAA,EAAe,EAAG,QAAA,EAAU,CAAA,EAAG,QAAQ,CAAA,KAAA,CAAO,CAAA;AAC5D;AAGO,SAAS,cAAc,QAAA,EAA0B;AACtD,EAAA,OAAO,gCAAgC,QAAQ,CAAA,KAAA,CAAA;AACjD;AAKO,SAAS,WAAW,QAAA,EAAiC;AAC1D,EAAA,MAAM,IAAA,GAAO,cAAc,QAAQ,CAAA;AACnC,EAAA,IAAI,CAAC,UAAA,CAAW,IAAI,CAAA,SAAU,EAAC;AAC/B,EAAA,IAAI;AACF,IAAA,MAAM,SAAkB,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,MAAM,CAAC,CAAA;AAC7D,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,MAAM,OAAA,CAAQ,MAAM,CAAA,EAAG,OAAO,EAAC;AAC5E,IAAA,OAAO,MAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAGO,SAAS,gBAAgB,MAAA,EAA+B;AAC7D,EAAA,MAAM,SAAwB,EAAC;AAC/B,EAAA,KAAA,MAAW,EAAA,IAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,IAAA,EAAK,EAAG,MAAA,CAAO,EAAE,CAAA,GAAI,MAAA,CAAO,EAAE,CAAA;AACnE,EAAA,OAAO,GAAG,IAAA,CAAK,SAAA,CAAU,MAAA,EAAQ,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAC3C;AAaO,SAAS,oBAAA,CACd,UAAA,EACA,QAAA,EACA,SAAA,EACA,KAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAsB,EAAE,GAAG,QAAA,EAAS;AAC1C,EAAA,KAAA,MAAW,MAAM,UAAA,EAAY;AAC3B,IAAA,IAAI,IAAA,CAAK,EAAE,CAAA,KAAM,MAAA,EAAW;AAC5B,IAAA,IAAA,CAAK,EAAE,CAAA,GAAI,SAAA,CAAU,EAAE,CAAA,IAAK,KAAA;AAAA,EAC9B;AACA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,QAAA,GAAmB;AACjC,EAAA,OAAA,qBAAW,IAAA,EAAK,EAAE,aAAY,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAC7C;;;ACvDA,IAAM,cAAA,GAAiB,6BAAA;AAcvB,IAAM,WAAA,GAAc,EACjB,MAAA,CAAO;AAAA,EACN,EAAA,EAAI,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACxB,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC1B,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAE5B,YAAA,EAAc,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAClC,QAAA,EAAU,EACP,MAAA,CAAO;AAAA;AAAA;AAAA;AAAA,IAIN,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAC1B,CAAA,CACA,WAAA,EAAY,CACZ,QAAA,EAAS;AAAA,EACZ,IAAA,EAAM,EACH,MAAA,CAAO;AAAA,IACN,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAC3B,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAC5B,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAChC,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAClC,CAAA,CACA,WAAA,EAAY,CACZ,QAAA;AACL,CAAC,EACA,WAAA,EAAY;AAIf,IAAM,cAAA,GAAiB,EACpB,MAAA,CAAO;AAAA,EACN,EAAA,EAAI,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACxB,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAE1B,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzB,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzB,KAAK,CAAA,CAAE,KAAA,CAAM,EAAE,MAAA,EAAQ,EAAE,QAAA,EAAS;AAAA,EAClC,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzB,MAAA,EAAQ,EAAE,MAAA,CAAO,CAAA,CAAE,QAAO,EAAG,WAAW,EAAE,QAAA;AAC5C,CAAC,EACA,WAAA,EAAY;AAMf,IAAM,iBAAiB,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,MAAA,IAAU,cAAc,CAAA;AAkB1D,SAAS,OAAO,CAAA,EAAmB;AACjC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,GAAS,CAAA,GAAI,GAAA;AACrC;AAGA,SAAS,IAAI,CAAA,EAAmB;AAC9B,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,GAAG,OAAO,GAAA;AAChC,EAAA,OAAO,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA,CAAA;AACrB;AAQA,SAAS,eAAA,CACP,YACA,UAAA,EACoB;AACpB,EAAA,IAAI,eAAe,MAAA,IAAa,CAAC,OAAO,QAAA,CAAS,UAAU,GAAG,OAAO,MAAA;AACrE,EAAA,IAAI,UAAA,IAAc,GAAG,OAAO,MAAA;AAC5B,EAAA,OAAO,MAAA,CAAO,aAAa,UAAU,CAAA;AACvC;AA0BA,IAAM,mBAAA,GAAgE;AAAA,EACpE,CAAC,YAAY,WAAW,CAAA;AAAA,EACxB,CAAC,YAAY,QAAQ,CAAA;AAAA,EACrB,CAAC,SAAS,QAAQ,CAAA;AAAA,EAClB,CAAC,UAAU,MAAM,CAAA;AAAA,EACjB,CAAC,SAAS,MAAM,CAAA;AAAA,EAChB,CAAC,UAAU,YAAY,CAAA;AAAA,EACvB,CAAC,cAAc,UAAU,CAAA;AAAA,EACzB,CAAC,aAAa,SAAS,CAAA;AAAA,EACvB,CAAC,SAAS,MAAM,CAAA;AAAA,EAChB,CAAC,UAAU,QAAQ,CAAA;AAAA,EACnB,CAAC,SAAS,SAAS,CAAA;AAAA,EACnB,CAAC,aAAa,SAAS,CAAA;AAAA,EACvB,CAAC,iBAAiB,aAAa,CAAA;AAAA,EAC/B,CAAC,cAAc,QAAQ,CAAA;AAAA,EACvB,CAAC,aAAa,UAAU,CAAA;AAAA,EACxB,CAAC,YAAY,UAAU,CAAA;AAAA,EACvB,CAAC,WAAW,QAAQ,CAAA;AAAA,EACpB,CAAC,gBAAgB,MAAM;AACzB,CAAA;AAGO,SAAS,yBAAyB,MAAA,EAAwB;AAC/D,EAAA,KAAA,MAAW,CAAC,OAAA,EAAS,MAAM,CAAA,IAAK,mBAAA,EAAqB;AACnD,IAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,EAAG,OAAO,MAAA;AAAA,EACnC;AACA,EAAA,OAAO,UAAA;AACT;AAmBA,SAAS,QAAA,CAAS,UAAkB,MAAA,EAAwB;AAC1D,EAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAC9B;AAEA,SAAS,eAAe,CAAA,EAA4B;AAClD,EAAA,MAAM,MAAA,GAAmB;AAAA,IACvB,CAAA,YAAA,EAAe,GAAA,CAAI,CAAA,CAAE,UAAU,CAAC,CAAA,CAAA;AAAA,IAChC,CAAA,aAAA,EAAgB,GAAA,CAAI,CAAA,CAAE,WAAW,CAAC,CAAA;AAAA,GACpC;AACA,EAAA,IAAI,CAAA,CAAE,wBAAwB,MAAA,EAAW;AACvC,IAAA,MAAA,CAAO,KAAK,CAAA,qBAAA,EAAwB,GAAA,CAAI,CAAA,CAAE,mBAAmB,CAAC,CAAA,CAAE,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,CAAA,CAAE,yBAAyB,MAAA,EAAW;AACxC,IAAA,MAAA,CAAO,KAAK,CAAA,sBAAA,EAAyB,GAAA,CAAI,CAAA,CAAE,oBAAoB,CAAC,CAAA,CAAE,CAAA;AAAA,EACpE;AACA,EAAA,IAAI,CAAA,CAAE,YAAY,MAAA,EAAW;AAC3B,IAAA,MAAA,CAAO,KAAK,CAAA,SAAA,EAAY,IAAA,CAAK,UAAU,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,CAAA;AAAA,EACrD;AACA,EAAA,MAAA,CAAO,KAAK,CAAA,QAAA,EAAW,IAAA,CAAK,UAAU,CAAA,CAAE,MAAM,CAAC,CAAA,CAAE,CAAA;AACjD,EAAA,MAAA,CAAO,KAAK,CAAA,UAAA,EAAa,IAAA,CAAK,UAAU,CAAA,CAAE,QAAQ,CAAC,CAAA,CAAE,CAAA;AACrD,EAAA,OAAO,CAAA;AAAA,IAAA,EAAU,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,GAAA,CAAA;AACzC;AAoBA,IAAM,EAAA,GAAuB;AAAA,EAC3B,WAAA,EAAa,aAAA;AAAA,EACb,QAAA,EAAU,iBAAA;AAAA,EACV,aAAA,EAAe,iBAAA;AAAA,EACf,UAAA,EAAY,gEAAA;AAAA,EACZ,WAAA,EAAa,oBAAA;AAAA,EACb,cAAA,EAAgB,8BAAA;AAAA,EAChB,KAAA,EAAO;AACT,CAAA;AAEA,IAAM,GAAA,GAAwB;AAAA,EAC5B,WAAA,EAAa,UAAA;AAAA,EACb,QAAA,EAAU,kBAAA;AAAA,EACV,aAAA,EAAe,kBAAA;AAAA,EACf,UAAA,EAAY,iEAAA;AAAA,EACZ,WAAA,EAAa,qBAAA;AAAA,EACb,cAAA,EAAgB,+BAAA;AAAA,EAChB,KAAA,EAAO;AACT,CAAA;AAEA,SAAS,aAAA,CACP,QAAA,EACA,OAAA,EACA,YAAA,EACQ;AACR,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,OAAO,EAAE,IAAA,EAAK;AAEvC,EAAA,MAAM,KAAA,GAAkB;AAAA,IACtB,CAAA,+CAAA,EAAkD,SAAS,aAAa,CAAA,EAAA,CAAA;AAAA,IACxE,yFAAA;AAAA,IACA,cAAc,cAAc,CAAA,YAAA,EAAe,SAAS,WAAW,CAAA,SAAA,EAAO,SAAS,KAAK,CAAA,CAAA,CAAA;AAAA,IACpF,2EAAA;AAAA,IACA,oEAAA;AAAA,IACA,kEAAA;AAAA,IACA,0EAAA;AAAA,IACA,2EAAA;AAAA,IACA,wEAAA;AAAA,IACA,iFAAA;AAAA,IACA,qBAAA;AAAA,IACA,6EAAA;AAAA,IACA,0EAAA;AAAA,IACA,uEAAA;AAAA,IACA,mFAAA;AAAA,IACA,kEAAA;AAAA,IACA,kEAAA;AAAA,IACA,EAAA;AAAA,IACA,gDAAA;AAAA,IACA,EAAA;AAAA,IACA,CAAA,aAAA,EAAgB,SAAS,WAAW,CAAA,gCAAA;AAAA,GACtC;AACA,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,EAAA,EAAK,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAE,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EAC1E;AACA,EAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,eAAA,EAAkB,QAAA,CAAS,KAAK,CAAA,6CAAA,CAA+C,CAAA;AAC1F,EAAA,KAAA,CAAM,KAAK,mFAA+E,CAAA;AAC1F,EAAA,KAAA,CAAM,KAAK,8EAA8E,CAAA;AACzF,EAAA,KAAA,CAAM,KAAK,4EAA4E,CAAA;AACvF,EAAA,KAAA,CAAM,KAAK,6EAA6E,CAAA;AACxF,EAAA,KAAA,CAAM,KAAK,+EAA+E,CAAA;AAC1F,EAAA,KAAA,CAAM,KAAK,8EAA8E,CAAA;AACzF,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,QAAA,CAAS,cAAc,CAAA,uBAAA,CAAyB,CAAA;AAC3E,EAAA,KAAA,MAAW,MAAM,CAAC,GAAG,YAAY,CAAA,CAAE,MAAK,EAAG;AACzC,IAAA,KAAA,CAAM,KAAK,CAAA,EAAA,EAAK,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EACvC;AACA,EAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAsCA,SAAS,eAAA,CAAgB,aAAqB,QAAA,EAAkC;AAC9E,EAAA,MAAM,SAAyC,EAAC;AAChD,EAAA,KAAA,MAAW,MAAA,IAAU,OAAO,IAAA,CAAK,QAAA,CAAS,UAAU,EAAE,CAAA,CAAE,IAAA,EAAK,EAAG;AAC9D,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,MAAA,GAAS,MAAM,CAAA;AACtC,IAAA,IAAI,CAAC,KAAA,EAAO;AAIZ,IAAA,MAAA,CAAO,MAAM,CAAA,GAAI;AAAA,MACf,GAAI,MAAM,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,KAAA,CAAM,EAAA,EAAG,GAAI,EAAC;AAAA,MACjD,GAAI,MAAM,IAAA,KAAS,MAAA,GAAY,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK,GAAI,EAAC;AAAA,MACvD,GAAI,MAAM,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO,GAAI,EAAC;AAAA,MAC7D,GAAI,MAAM,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,KAAA,CAAM,YAAA,EAAa,GAAI,EAAC;AAAA,MAC/E,GAAI,KAAA,CAAM,QAAA,EAAU,GAAA,KAAQ,SAAY,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,KAAA,CAAM,QAAA,CAAS,GAAA,EAAI,KAAM,EAAC;AAAA,MACrF,GAAI,KAAA,CAAM,IAAA,KAAS,MAAA,GACf;AAAA,QACE,IAAA,EAAM;AAAA,UACJ,GAAI,KAAA,CAAM,IAAA,CAAK,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAM,GAAI,EAAC;AAAA,UACpE,GAAI,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,MAAA,EAAO,GAAI,EAAC;AAAA,UACvE,GAAI,KAAA,CAAM,IAAA,CAAK,UAAA,KAAe,MAAA,GAC1B,EAAE,UAAA,EAAY,KAAA,CAAM,IAAA,CAAK,UAAA,EAAW,GACpC,EAAC;AAAA,UACL,GAAI,KAAA,CAAM,IAAA,CAAK,WAAA,KAAgB,MAAA,GAC3B,EAAE,WAAA,EAAa,KAAA,CAAM,IAAA,CAAK,WAAA,EAAY,GACtC;AAAC;AACP,UAEF;AAAC,KACP;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAA+B;AAAA,IACnC,GAAI,SAAS,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,QAAA,CAAS,EAAA,EAAG,GAAI,EAAC;AAAA,IACvD,GAAI,SAAS,IAAA,KAAS,MAAA,GAAY,EAAE,IAAA,EAAM,QAAA,CAAS,IAAA,EAAK,GAAI,EAAC;AAAA,IAC7D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D,GAAI,SAAS,GAAA,KAAQ,MAAA,GAAY,EAAE,GAAA,EAAK,QAAA,CAAS,GAAA,EAAI,GAAI,EAAC;AAAA,IAC1D;AAAA,GACF;AAEA,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,WAAW,GAAG,SAAA,EAAU,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AACjE;AAKA,SAAS,gBAAgB,QAAA,EAA0B;AACjD,EAAA,OAAO,mCAAmC,QAAQ,CAAA,KAAA,CAAA;AACpD;AAYA,SAAS,oBAAoB,MAAA,EAAyD;AACpF,EAAA,OAAO,MAAA,CAAO,QAAQ,MAAM,CAAA,CACzB,OAAO,CAAC,GAAG,KAAK,CAAA,KAAM,KAAA,CAAM,UAAU,GAAA,KAAQ,mBAAmB,EACjE,GAAA,CAAI,CAAC,CAAC,MAAM,CAAA,KAAM,MAAM,CAAA,CACxB,IAAA,EAAK;AACV;AAEA,eAAe,WAAA,CACb,QAAA,EACA,MAAA,EACA,GAAA,EACyB;AACzB,EAAA,MAAM,SAAS,cAAA,CAAe,KAAA,CAAM,MAAM,GAAA,CAAI,WAAA,CAAY,MAAM,CAAC,CAAA;AACjE,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA;AAC5C,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,EAAG,SAAS,aAAa,CAAA,iCAAA,EAAoC,SAAS,WAAW,CAAA,mBAAA,EACpE,OAAO,GAAG,CAAA,yCAAA;AAAA,KACzB;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,MAAA,IAAU,EAAC;AAEnC,EAAA,MAAM,UAA2C,EAAC;AAClD,EAAA,MAAM,aAAqC,EAAC;AAC5C,EAAA,KAAA,MAAW,CAAC,MAAA,EAAQ,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACpD,IAAA,MAAM,UAAA,GAAa,MAAM,IAAA,EAAM,KAAA;AAC/B,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,EAAM,MAAA;AAIhC,IAAA,IAAI,UAAA,KAAe,MAAA,IAAa,WAAA,KAAgB,MAAA,EAAW;AAC3D,IAAA,IAAI,CAAC,OAAO,QAAA,CAAS,UAAU,KAAK,CAAC,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,EAAG;AAEnE,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,QAAA,CAAS,WAAA,EAAa,MAAM,CAAA;AACjD,IAAA,MAAM,KAAA,GAAyB;AAAA,MAC7B,UAAA,EAAY,OAAO,UAAU,CAAA;AAAA,MAC7B,WAAA,EAAa,OAAO,WAAW,CAAA;AAAA,MAC/B,MAAA,EAAQ,yBAAyB,MAAM,CAAA;AAAA,MACvC,UAAU,QAAA,CAAS;AAAA,KACrB;AACA,IAAA,MAAM,IAAA,GAAO,eAAA,CAAgB,KAAA,CAAM,IAAA,EAAM,YAAY,UAAU,CAAA;AAC/D,IAAA,IAAI,IAAA,KAAS,MAAA,EAAW,KAAA,CAAM,mBAAA,GAAsB,IAAA;AACpD,IAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,CAAM,IAAA,EAAM,aAAa,UAAU,CAAA;AACjE,IAAA,IAAI,KAAA,KAAU,MAAA,EAAW,KAAA,CAAM,oBAAA,GAAuB,KAAA;AAEtD,IAAA,OAAA,CAAQ,GAAG,CAAA,GAAI,KAAA;AACf,IAAA,IAAI,MAAM,YAAA,KAAiB,MAAA,EAAW,UAAA,CAAW,GAAG,IAAI,KAAA,CAAM,YAAA;AAAA,EAChE;AAEA,EAAA,MAAM,MAAA,GAAS,oBAAA;AAAA,IACb,MAAA,CAAO,KAAK,OAAO,CAAA;AAAA,IACnB,UAAA,CAAW,SAAS,QAAQ,CAAA;AAAA,IAC5B,UAAA;AAAA,IACA,QAAA;AAAS,GACX;AACA,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,GAAG,CAAA;AAE9E,EAAA,OAAO;AAAA,IACL,CAAC,SAAS,UAAU,GAAG,cAAc,QAAA,EAAU,OAAA,EAAS,mBAAA,CAAoB,MAAM,CAAC,CAAA;AAAA,IACnF,CAAC,aAAA,CAAc,QAAA,CAAS,QAAQ,CAAC,GAAG,gBAAgB,MAAM,CAAA;AAAA,IAC1D,CAAC,gBAAgB,QAAA,CAAS,QAAQ,CAAC,GAAG,eAAA,CAAgB,QAAA,CAAS,WAAA,EAAa,QAAQ;AAAA,GACtF;AACF;AAKA,IAAM,SAAA,GAAY,CAAC,EAAE,EAAA,EAAI,GAAG,QAAA,EAAU,GAAA,EAAK,gBAAgB,CAAA;AAC3D,IAAM,UAAA,GAAa,CAAC,EAAE,EAAA,EAAI,IAAI,QAAA,EAAU,GAAA,EAAK,gBAAgB,CAAA;AAEtD,IAAM,yBAAyB,eAAA,CAAgB;AAAA,EACpD,MAAM,EAAA,CAAG,aAAA;AAAA,EACT,QAAA,EAAU,KAAA;AAAA,EACV,OAAA,EAAS,SAAA;AAAA,EACT,UAAU,CAAA,GAAA,KAAO,WAAA,CAAY,IAAI,SAAA,CAAU,CAAC,GAAI,GAAG;AACrD,CAAC;AAEM,IAAM,0BAA0B,eAAA,CAAgB;AAAA,EACrD,MAAM,GAAA,CAAI,aAAA;AAAA,EACV,QAAA,EAAU,KAAA;AAAA,EACV,OAAA,EAAS,UAAA;AAAA,EACT,UAAU,CAAA,GAAA,KAAO,WAAA,CAAY,KAAK,UAAA,CAAW,CAAC,GAAI,GAAG;AACvD,CAAC","file":"llm-opencode.mjs","sourcesContent":["/**\n * catalog-sync generator contract.\n *\n * FROZEN — a sibling agent builds provider generators against this surface.\n * Do not change field names, shapes, or the `defineGenerator` signature\n * without coordinating. Adding new generators is the expected extension\n * path; editing this file is not.\n */\n\nexport interface CatalogSource {\n id: string\n url: string\n /** HTTP method for the live (`--refresh`) fetch. Default `\"GET\"`. */\n method?: \"GET\" | \"POST\"\n /**\n * Request headers for the live fetch. A value may embed `env:VAR_NAME`\n * tokens, resolved from `process.env` at fetch time (e.g.\n * `{ \"xi-api-key\": \"env:ELEVENLABS_API_KEY\" }` or\n * `{ Authorization: \"Bearer env:MINIMAX_API_KEY\" }`). If any referenced env\n * var is unset, the source is treated as un-refreshable — the committed\n * snapshot is reused rather than fetched without auth. Offline reads\n * (default, no `--refresh`) ignore headers entirely.\n */\n headers?: Record<string, string>\n /** JSON request body for POST sources (e.g. MiniMax `get_voice`). */\n body?: unknown\n}\n\nexport interface GeneratorContext {\n /**\n * Reads the pinned snapshot at `snapshots/<src.id>.json`; if missing and\n * `refresh=true`, fetches `src.url` and writes the snapshot. Returns\n * parsed JSON.\n *\n * Offline by default: when no snapshot exists and `refresh` is false this\n * throws — generators MUST be runnable without network access so CI and\n * tests are deterministic. The only network path is behind `--refresh`.\n */\n fetchSource(src: CatalogSource): Promise<unknown>\n refresh: boolean\n}\n\n/** Repo-relative path → TS source text. */\nexport type GeneratedFiles = Record<string, string>\n\nexport interface CatalogGenerator {\n /** e.g. \"llm:openrouter\" */\n name: string\n modality: \"llm\" | \"image\" | \"video\" | \"audio\" | \"voice\"\n sources: CatalogSource[]\n generate(ctx: GeneratorContext): Promise<GeneratedFiles>\n}\n\n/** Identity helper — lets a generator module declare itself with type inference. */\nexport function defineGenerator(g: CatalogGenerator): CatalogGenerator {\n return g\n}\n","/**\n * `addedAt` ledger — tracks, per generator, the ISO date each model id was\n * FIRST seen by a sync run. See `README.md` (\"addedAt convention\") for the\n * full contract; this module is the mechanism.\n *\n * Invariant: once an id has a ledger entry, that date is NEVER overwritten —\n * not even if the source's own `created` timestamp for that id later\n * resolves to something different (a provider backdating/correcting its own\n * metadata must not rewrite catalog history). New ids are backfilled from\n * the source's own creation timestamp when it has one, else stamped with\n * the current run's date.\n *\n * Ledger entries are never dropped when an id disappears from a run — the\n * ledger records \"first ever seen\", not \"currently present\". A model that\n * vanishes from a provider's list and later reappears keeps its original\n * `addedAt` rather than looking newly added.\n *\n * Each generator owns one ledger file, committed at\n * `packages/catalog-sync/ledger/<generator-id>.json` (id → ISO date, sorted\n * keys, deterministic serialization) — included as a normal entry in the\n * generator's `GeneratedFiles` return value, so `runner.ts`'s existing\n * diff/write logic covers it for free (including `--check` drift detection).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/** The catalog-sync package directory — see `runner.ts`'s `catalogSyncDir`. */\nfunction catalogSyncDir(): string {\n return resolve(__dirname, \"..\")\n}\n\n/** Absolute path to a generator's committed addedAt ledger. */\nexport function ledgerAbsPath(ledgerId: string): string {\n return join(catalogSyncDir(), \"ledger\", `${ledgerId}.json`)\n}\n\n/** Repo-relative path for a ledger — for inclusion in a generator's `GeneratedFiles`. */\nexport function ledgerRelPath(ledgerId: string): string {\n return `packages/catalog-sync/ledger/${ledgerId}.json`\n}\n\nexport type AddedAtLedger = Record<string, string>\n\n/** Reads a committed ledger from disk. Missing/unparseable → empty ledger. */\nexport function readLedger(ledgerId: string): AddedAtLedger {\n const path = ledgerAbsPath(ledgerId)\n if (!existsSync(path)) return {}\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"))\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return {}\n return parsed as AddedAtLedger\n } catch {\n return {}\n }\n}\n\n/** Deterministic (sorted-key) JSON serialization, matching the `*.generated.ts` style of a trailing newline. */\nexport function serializeLedger(ledger: AddedAtLedger): string {\n const sorted: AddedAtLedger = {}\n for (const id of Object.keys(ledger).sort()) sorted[id] = ledger[id]!\n return `${JSON.stringify(sorted, null, 2)}\\n`\n}\n\n/**\n * Merge the current run's ids into a ledger.\n *\n * - id already in `previous` → keep the existing stamp verbatim (never\n * mutated), regardless of what `createdAt` says this run.\n * - id new to the ledger, source has a native creation timestamp for it →\n * backfill with `createdAt[id]`.\n * - id new to the ledger, no native timestamp → stamp with `today`.\n * - ids in `previous` but absent from `currentIds` this run are kept as-is\n * (see module doc comment — the ledger is \"first ever seen\").\n */\nexport function computeAddedAtLedger(\n currentIds: readonly string[],\n previous: AddedAtLedger,\n createdAt: Readonly<Record<string, string>>,\n today: string\n): AddedAtLedger {\n const next: AddedAtLedger = { ...previous }\n for (const id of currentIds) {\n if (next[id] !== undefined) continue\n next[id] = createdAt[id] ?? today\n }\n return next\n}\n\n/** Today's date as `YYYY-MM-DD`, in UTC. */\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\n/** Converts a provider's Unix-seconds creation timestamp to `YYYY-MM-DD`. */\nexport function isoDateFromUnixSeconds(seconds: number): string {\n return new Date(seconds * 1000).toISOString().slice(0, 10)\n}\n","import { z } from \"zod\"\n\nimport { defineGenerator, type GeneratedFiles, type GeneratorContext } from \"../types.js\"\nimport {\n computeAddedAtLedger,\n ledgerRelPath,\n readLedger,\n serializeLedger,\n todayIso,\n} from \"../added-at.js\"\n\n/**\n * OpenCode's two hosted billing endpoints, both sourced from models.dev.\n *\n * - `opencode-go` — OpenCode Go, the flat $10/mo API-key subscription.\n * - `opencode` — OpenCode Zen, the pay-as-you-go sibling.\n *\n * They are DIFFERENT endpoints with different per-model prices and different\n * model lineups (Go serves no Claude/Gemini; Zen serves both), so each gets\n * its own generator, ledger, snapshot and route table. They only share a\n * source URL and an env var name (`OPENCODE_API_KEY` — see the note in\n * `@agentproto/model-catalog`'s `PROVIDER_KEY_ENV`: same name, two distinct\n * secrets).\n *\n * PINNED source. models.dev's `api.json` is the whole ecosystem's catalog\n * (~4.6 MB, 213 providers) and is fetched UNAUTHENTICATED — unlike Requesty,\n * there is no key to gate the refresh on, so `sources` carries no headers and\n * `--refresh` always hits the live URL.\n *\n * Because the payload is 4.6 MB and we model two providers out of 213, the\n * committed snapshot is a PROJECTION, not the raw response: each generator\n * emits `snapshots/<ledger-id>.json` as one of its {@link GeneratedFiles},\n * narrowed to its own provider and to the fields below, with deterministic key\n * order. The runner's `--refresh` writes the raw response to that path first\n * and this projection immediately overwrites it in the same run, so a refresh\n * never leaves 4.6 MB of unrelated providers in the tree. The projection keeps\n * the SOURCE's own shape (nested `provider.npm`, nested `cost`), so\n * {@link SnapshotSchema} parses the raw payload and the committed projection\n * identically and re-projecting a projection is a no-op.\n */\nconst MODELS_DEV_URL = \"https://models.dev/api.json\"\n\n// ── Source schema ────────────────────────────────────────────────────────\n// Everything is optional + passthrough: this schema is applied to the FULL\n// models.dev payload (213 providers), so one unrelated provider drifting a\n// field must not break our two generators. Prices are USD **per 1M tokens\n// ALREADY** (`cost.input: 1.4` = $1.40/1M) — unlike Requesty's per-token\n// numbers, so there is no ×1e6 conversion here. `cost.cache_read` /\n// `cost.cache_write` are absolute per-1M prices too, from which the\n// multipliers are derived. `cost.tiers` / `cost.context_over_200k` exist on\n// some models.dev entries but on none of the opencode go/zen routes in the\n// pinned snapshots, so they are not read here. If they ever appear, map them\n// to `LLMPricing.tiers` (see ../sources/openrouter-prompt-tiers.mjs).\n\nconst ModelSchema = z\n .object({\n id: z.string().optional(),\n name: z.string().optional(),\n family: z.string().optional(),\n /** ISO `YYYY-MM-DD` already — no unix-seconds conversion (cf. Requesty). */\n release_date: z.string().optional(),\n provider: z\n .object({\n /** Which AI-SDK package serves this model — the ONLY discriminator\n * for the endpoint's three wire surfaces (see\n * {@link anthropicSurfaceIds}). */\n npm: z.string().optional(),\n })\n .passthrough()\n .optional(),\n cost: z\n .object({\n input: z.number().optional(),\n output: z.number().optional(),\n cache_read: z.number().optional(),\n cache_write: z.number().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough()\n\ntype SourceModel = z.infer<typeof ModelSchema>\n\nconst ProviderSchema = z\n .object({\n id: z.string().optional(),\n name: z.string().optional(),\n /** The endpoint's base URL, e.g. `https://opencode.ai/zen/go/v1`. */\n api: z.string().optional(),\n doc: z.string().optional(),\n env: z.array(z.string()).optional(),\n npm: z.string().optional(),\n models: z.record(z.string(), ModelSchema).optional(),\n })\n .passthrough()\n\ntype SourceProvider = z.infer<typeof ProviderSchema>\n\n/** models.dev's whole payload is `{ [providerId]: provider }`; a committed\n * projection is the same map narrowed to one key. */\nconst SnapshotSchema = z.record(z.string(), ProviderSchema)\n\n// ── Output shape (mirrors model-catalog's LLMPricing) ────────────────────\n\ninterface LLMPricingEntry {\n inputPer1M: number\n outputPer1M: number\n cacheReadMultiplier?: number\n cacheWriteMultiplier?: number\n /** ISO date this id was first seen by a sync run. See `../added-at.ts`. */\n addedAt?: string\n vendor: string\n provider: string\n}\n\n// ── Number helpers ──────────────────────────────────────────────────────\n\n/** Round to 6 decimal places, killing binary-float noise from the division. */\nfunction round6(n: number): number {\n return Math.round(n * 1_000_000) / 1_000_000\n}\n\n/** Shortest faithful decimal string (matches the other generated files). */\nfunction fmt(n: number): string {\n if (!Number.isFinite(n)) return \"0\"\n return `${round6(n)}`\n}\n\n/**\n * A cache price relative to the base input price. Only meaningful when the\n * base price is positive: every `-free` variant on Zen carries\n * `cost.input: 0` alongside `cost.cache_read: 0`, and 0/0 is NaN — a free\n * model has no cache DISCOUNT to express, so no multiplier is emitted.\n */\nfunction cacheMultiplier(\n cachePer1M: number | undefined,\n inputPer1M: number\n): number | undefined {\n if (cachePer1M === undefined || !Number.isFinite(cachePer1M)) return undefined\n if (inputPer1M <= 0) return undefined\n return round6(cachePer1M / inputPer1M)\n}\n\n// ── Vendor attribution ──────────────────────────────────────────────────\n\n/**\n * Model id prefix → vendor (who BUILT the model), matched in order.\n *\n * HEURISTIC by necessity: models.dev's opencode/opencode-go entries carry\n * BARE product ids (`glm-5.3`, not `z-ai/glm-5.3`) and no vendor field at\n * all — `family` is a product line (`glm`, `deepseek-flash`), not a builder.\n * Each slug below is the one the repo's own catalog already uses for that\n * builder (verified against `OPENROUTER_ROUTES`' `vendor` values, which is\n * why it's `x-ai` and not `xai`, `moonshotai` and not `moonshot`), and each\n * attribution was cross-checked against another models.dev provider that\n * namespaces the SAME id under its builder (`inclusionai/ling-3.0-flash`,\n * `nvidia/nemotron-3-super-…`, `arcee-ai/trinity-large-…`,\n * `poolside/laguna-s-2.1`, `cohere/north-mini-code`, `meta/muse-spark-1.2`).\n * A new id that matches nothing here falls back to `opencode` — the handful\n * of unattributed ids on these endpoints (`omen-alpha`, `ox-alpha-free`,\n * `big-pickle`, `x-preview-f-free`) are opencode's own stealth/preview\n * models with no disclosed builder.\n *\n * `vendor` is METADATA on the pricing entry. It is NOT the route key: the\n * route tables are keyed `<provider>/<bare-id>` (see {@link routeKey}), so a\n * mis-attributed vendor can never mis-route or mis-bill a spawn.\n */\nconst VENDOR_BY_ID_PREFIX: ReadonlyArray<readonly [RegExp, string]> = [\n [/^claude-/, \"anthropic\"],\n [/^gemini-/, \"google\"],\n [/^gpt-/, \"openai\"],\n [/^grok-/, \"x-ai\"],\n [/^glm-/, \"z-ai\"],\n [/^kimi-/, \"moonshotai\"],\n [/^deepseek-/, \"deepseek\"],\n [/^minimax-/, \"minimax\"],\n [/^qwen/, \"qwen\"],\n [/^mimo-/, \"xiaomi\"],\n [/^hy\\d/, \"tencent\"],\n [/^longcat-/, \"meituan\"],\n [/^(ling|ring)-/, \"inclusionai\"],\n [/^nemotron-/, \"nvidia\"],\n [/^trinity-/, \"arcee-ai\"],\n [/^laguna-/, \"poolside\"],\n [/^north-/, \"cohere\"],\n [/^muse-spark-/, \"meta\"],\n]\n\n/** Vendor for a bare model id — see {@link VENDOR_BY_ID_PREFIX}. */\nexport function vendorForOpencodeModelId(bareId: string): string {\n for (const [pattern, vendor] of VENDOR_BY_ID_PREFIX) {\n if (pattern.test(bareId)) return vendor\n }\n return \"opencode\"\n}\n\n// ── Serialization ────────────────────────────────────────────────────────\n\n/**\n * Route-table key: `<provider>/<bare-id>` — `opencode-go/glm-5.3`,\n * `opencode/claude-sonnet-4-6`.\n *\n * Deliberately NOT `<vendor>/<product>` like the OpenRouter / Requesty\n * tables. Those routers namespace their own ids by vendor, so their keys\n * round-trip through route-identity's `vendor/product@route` grammar.\n * models.dev gives these two endpoints BARE ids, and opencode's own config\n * spells them `opencode-go/glm-5.3` — which is also the form the runtime\n * derives the billing endpoint from (`modelIdPrefixProvider`) and the form\n * the opencode adapter puts on the wire. Keying by the provider makes all\n * three agree on one string, and `route-identity`'s opencode branches key\n * off exactly this (see the comment there for why they must run ahead of the\n * direct-vendor branch).\n */\nfunction routeKey(provider: string, bareId: string): string {\n return `${provider}/${bareId}`\n}\n\nfunction serializeEntry(e: LLMPricingEntry): string {\n const fields: string[] = [\n `inputPer1M: ${fmt(e.inputPer1M)}`,\n `outputPer1M: ${fmt(e.outputPer1M)}`,\n ]\n if (e.cacheReadMultiplier !== undefined) {\n fields.push(`cacheReadMultiplier: ${fmt(e.cacheReadMultiplier)}`)\n }\n if (e.cacheWriteMultiplier !== undefined) {\n fields.push(`cacheWriteMultiplier: ${fmt(e.cacheWriteMultiplier)}`)\n }\n if (e.addedAt !== undefined) {\n fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`)\n }\n fields.push(`vendor: ${JSON.stringify(e.vendor)}`)\n fields.push(`provider: ${JSON.stringify(e.provider)}`)\n return `{\\n ${fields.join(\",\\n \")},\\n }`\n}\n\n/** One endpoint's identity — everything the emitted files need to describe it. */\ninterface OpencodeEndpoint {\n /** models.dev provider key, and this repo's `CatalogProvider` value. */\n providerKey: string\n /** Generator + snapshot + ledger id. */\n ledgerId: string\n /** `llm:<name>` generator name. */\n generatorName: string\n /** Repo-relative emitted route table. */\n outputPath: string\n /** Exported route-table const. */\n routesConst: string\n /** Exported Anthropic-surface id list const. */\n anthropicConst: string\n /** Human label, used only in generated comments. */\n label: string\n}\n\nconst GO: OpencodeEndpoint = {\n providerKey: \"opencode-go\",\n ledgerId: \"llm-opencode-go\",\n generatorName: \"llm:opencode-go\",\n outputPath: \"packages/model-catalog/src/llm/opencode-go-routes.generated.ts\",\n routesConst: \"OPENCODE_GO_ROUTES\",\n anthropicConst: \"OPENCODE_GO_ANTHROPIC_MODELS\",\n label: \"OpenCode Go\",\n}\n\nconst ZEN: OpencodeEndpoint = {\n providerKey: \"opencode\",\n ledgerId: \"llm-opencode-zen\",\n generatorName: \"llm:opencode-zen\",\n outputPath: \"packages/model-catalog/src/llm/opencode-zen-routes.generated.ts\",\n routesConst: \"OPENCODE_ZEN_ROUTES\",\n anthropicConst: \"OPENCODE_ZEN_ANTHROPIC_MODELS\",\n label: \"OpenCode Zen\",\n}\n\nfunction serializeFile(\n endpoint: OpencodeEndpoint,\n entries: Record<string, LLMPricingEntry>,\n anthropicIds: readonly string[]\n): string {\n const keys = Object.keys(entries).sort()\n\n const lines: string[] = [\n `// AUTO-GENERATED by @agentproto/catalog-sync (${endpoint.generatorName}).`,\n \"// Do not edit by hand — re-run `pnpm --filter @agentproto/catalog-sync generate`.\",\n `// Source: ${MODELS_DEV_URL} (provider \"${endpoint.providerKey}\" — ${endpoint.label})`,\n \"// Prices come VERBATIM from the source's cost.input / cost.output, which\",\n \"// models.dev already publishes as USD per 1M tokens (no per-token\",\n \"// conversion, unlike the Requesty table). cacheReadMultiplier /\",\n \"// cacheWriteMultiplier are cost.cache_read / cost.cache_write expressed\",\n \"// relative to cost.input, and are omitted when the base input price is 0\",\n \"// (every `-free` variant) because a free model has no cache discount.\",\n \"// Zero-priced models are KEPT — on these endpoints zero is the truth, not\",\n \"// a missing price.\",\n \"// Keys are `<provider>/<bare-id>`, the form opencode's own config uses and\",\n \"// the form the runtime derives the billing endpoint from; `vendor` is a\",\n \"// heuristic attribution of the model's BUILDER and is metadata only.\",\n \"// addedAt is the ISO date this id was first seen by a sync run — backfilled\",\n \"// from the source's own `release_date`, then NEVER mutated; see\",\n \"// packages/catalog-sync/src/added-at.ts and the package README.\",\n \"\",\n 'import type { LLMPricing } from \"./catalog.js\"',\n \"\",\n `export const ${endpoint.routesConst}: Record<string, LLMPricing> = {`,\n ]\n for (const key of keys) {\n lines.push(` ${JSON.stringify(key)}: ${serializeEntry(entries[key]!)},`)\n }\n lines.push(\"}\")\n lines.push(\"\")\n lines.push(\"/**\")\n lines.push(` * BARE ids on ${endpoint.label} whose wire surface is the Anthropic Messages`)\n lines.push(\" * API — the source's `provider.npm === \\\"@ai-sdk/anthropic\\\"`. These are the\")\n lines.push(\" * ONLY ids reachable through the Anthropic-compatible gateway preset of the\")\n lines.push(\" * same name (claude-code / claude-sdk); the rest of the endpoint's lineup\")\n lines.push(\" * speaks OpenAI chat/completions or the OpenAI Responses API and has to go\")\n lines.push(\" * through the opencode adapter instead. Bare (not `<provider>/<id>`) because\")\n lines.push(\" * that is what an Anthropic client puts in `ANTHROPIC_MODEL` / on the wire.\")\n lines.push(\" */\")\n lines.push(`export const ${endpoint.anthropicConst}: readonly string[] = [`)\n for (const id of [...anthropicIds].sort()) {\n lines.push(` ${JSON.stringify(id)},`)\n }\n lines.push(\"]\")\n lines.push(\"\")\n return lines.join(\"\\n\")\n}\n\n// ── Snapshot projection ─────────────────────────────────────────────────\n\n/** The committed snapshot's per-model shape — a strict subset of the source's\n * own, so one schema parses both. */\ninterface ProjectedModel {\n id?: string\n name?: string\n family?: string\n release_date?: string\n provider?: { npm: string }\n cost?: {\n input?: number\n output?: number\n cache_read?: number\n cache_write?: number\n }\n}\n\n/** The committed snapshot's per-provider shape (see {@link ProjectedModel}). */\ninterface ProjectedProvider {\n id?: string\n name?: string\n api?: string\n doc?: string\n env?: readonly string[]\n npm?: string\n models: Record<string, ProjectedModel>\n}\n\n/**\n * Narrow the raw models.dev payload to one provider and to the fields this\n * generator models, with deterministic (sorted) model order and a fixed field\n * order. Shape-identical to the source, so {@link SnapshotSchema} parses this\n * projection and re-projecting it is a no-op — which is what makes `--check`\n * drift detection meaningful on the snapshot itself.\n */\nfunction projectSnapshot(providerKey: string, provider: SourceProvider): string {\n const models: Record<string, ProjectedModel> = {}\n for (const bareId of Object.keys(provider.models ?? {}).sort()) {\n const model = provider.models?.[bareId]\n if (!model) continue\n // Object-literal spreads, not assignments: JSON.stringify emits keys in\n // insertion order, so a FIXED literal order is what makes the committed\n // snapshot byte-stable across runs.\n models[bareId] = {\n ...(model.id !== undefined ? { id: model.id } : {}),\n ...(model.name !== undefined ? { name: model.name } : {}),\n ...(model.family !== undefined ? { family: model.family } : {}),\n ...(model.release_date !== undefined ? { release_date: model.release_date } : {}),\n ...(model.provider?.npm !== undefined ? { provider: { npm: model.provider.npm } } : {}),\n ...(model.cost !== undefined\n ? {\n cost: {\n ...(model.cost.input !== undefined ? { input: model.cost.input } : {}),\n ...(model.cost.output !== undefined ? { output: model.cost.output } : {}),\n ...(model.cost.cache_read !== undefined\n ? { cache_read: model.cost.cache_read }\n : {}),\n ...(model.cost.cache_write !== undefined\n ? { cache_write: model.cost.cache_write }\n : {}),\n },\n }\n : {}),\n }\n }\n\n const projected: ProjectedProvider = {\n ...(provider.id !== undefined ? { id: provider.id } : {}),\n ...(provider.name !== undefined ? { name: provider.name } : {}),\n ...(provider.api !== undefined ? { api: provider.api } : {}),\n ...(provider.doc !== undefined ? { doc: provider.doc } : {}),\n ...(provider.env !== undefined ? { env: provider.env } : {}),\n ...(provider.npm !== undefined ? { npm: provider.npm } : {}),\n models,\n }\n\n return `${JSON.stringify({ [providerKey]: projected }, null, 2)}\\n`\n}\n\n/** Repo-relative snapshot path for a source id — mirrors `runner.ts`'s\n * `snapshotPath`, expressed repo-relative so the projection can ride along\n * in {@link GeneratedFiles}. */\nfunction snapshotRelPath(ledgerId: string): string {\n return `packages/catalog-sync/snapshots/${ledgerId}.json`\n}\n\n// ── Generator ───────────────────────────────────────────────────────────\n\n/**\n * The bare ids served on the Anthropic Messages surface. models.dev\n * discriminates an endpoint's wire surfaces per model via `provider.npm`:\n * absent ⇒ OpenAI chat/completions, `@ai-sdk/anthropic` ⇒ Anthropic\n * Messages, `@ai-sdk/openai` ⇒ OpenAI Responses, `@ai-sdk/google` ⇒ Gemini.\n * Only the Anthropic set is emitted, because that is the one a caller has to\n * know: it is exactly what the gateway preset can serve.\n */\nfunction anthropicSurfaceIds(models: Readonly<Record<string, SourceModel>>): string[] {\n return Object.entries(models)\n .filter(([, model]) => model.provider?.npm === \"@ai-sdk/anthropic\")\n .map(([bareId]) => bareId)\n .sort()\n}\n\nasync function generateFor(\n endpoint: OpencodeEndpoint,\n source: { id: string; url: string },\n ctx: GeneratorContext\n): Promise<GeneratedFiles> {\n const parsed = SnapshotSchema.parse(await ctx.fetchSource(source))\n const provider = parsed[endpoint.providerKey]\n if (!provider) {\n throw new Error(\n `${endpoint.generatorName}: models.dev payload carries no \"${endpoint.providerKey}\" provider ` +\n `(source ${source.url}). Refusing to emit an empty route table.`\n )\n }\n const models = provider.models ?? {}\n\n const entries: Record<string, LLMPricingEntry> = {}\n const releasedAt: Record<string, string> = {}\n for (const [bareId, model] of Object.entries(models)) {\n const inputPer1M = model.cost?.input\n const outputPer1M = model.cost?.output\n // An entry with no published price at all is skipped: a fabricated 0 would\n // read as \"free\" to the billing surface. An explicit 0 IS kept (the `-free`\n // variants) — see the header comment in the emitted file.\n if (inputPer1M === undefined || outputPer1M === undefined) continue\n if (!Number.isFinite(inputPer1M) || !Number.isFinite(outputPer1M)) continue\n\n const key = routeKey(endpoint.providerKey, bareId)\n const entry: LLMPricingEntry = {\n inputPer1M: round6(inputPer1M),\n outputPer1M: round6(outputPer1M),\n vendor: vendorForOpencodeModelId(bareId),\n provider: endpoint.providerKey,\n }\n const read = cacheMultiplier(model.cost?.cache_read, inputPer1M)\n if (read !== undefined) entry.cacheReadMultiplier = read\n const write = cacheMultiplier(model.cost?.cache_write, inputPer1M)\n if (write !== undefined) entry.cacheWriteMultiplier = write\n\n entries[key] = entry\n if (model.release_date !== undefined) releasedAt[key] = model.release_date\n }\n\n const ledger = computeAddedAtLedger(\n Object.keys(entries),\n readLedger(endpoint.ledgerId),\n releasedAt,\n todayIso()\n )\n for (const [key, entry] of Object.entries(entries)) entry.addedAt = ledger[key]\n\n return {\n [endpoint.outputPath]: serializeFile(endpoint, entries, anthropicSurfaceIds(models)),\n [ledgerRelPath(endpoint.ledgerId)]: serializeLedger(ledger),\n [snapshotRelPath(endpoint.ledgerId)]: projectSnapshot(endpoint.providerKey, provider),\n }\n}\n\n/** models.dev needs no credential, so `headers` is deliberately absent — the\n * runner only reuses a committed snapshot instead of refreshing when a\n * referenced env var is missing, and there is none to miss here. */\nconst goSources = [{ id: GO.ledgerId, url: MODELS_DEV_URL }]\nconst zenSources = [{ id: ZEN.ledgerId, url: MODELS_DEV_URL }]\n\nexport const llmOpencodeGoGenerator = defineGenerator({\n name: GO.generatorName,\n modality: \"llm\",\n sources: goSources,\n generate: ctx => generateFor(GO, goSources[0]!, ctx),\n})\n\nexport const llmOpencodeZenGenerator = defineGenerator({\n name: ZEN.generatorName,\n modality: \"llm\",\n sources: zenSources,\n generate: ctx => generateFor(ZEN, zenSources[0]!, ctx),\n})\n"]}
|
|
@@ -56,6 +56,54 @@ function isoDateFromUnixSeconds(seconds) {
|
|
|
56
56
|
return new Date(seconds * 1e3).toISOString().slice(0, 10);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// src/sources/openrouter-prompt-tiers.mjs
|
|
60
|
+
function round6(n) {
|
|
61
|
+
return Math.round(n * 1e6) / 1e6;
|
|
62
|
+
}
|
|
63
|
+
function per1m(tokenPrice) {
|
|
64
|
+
if (tokenPrice === void 0 || tokenPrice === null) return void 0;
|
|
65
|
+
const n = Number(tokenPrice);
|
|
66
|
+
return Number.isFinite(n) ? round6(n * 1e6) : void 0;
|
|
67
|
+
}
|
|
68
|
+
function promptLengthTiers(pricing) {
|
|
69
|
+
const overrides = pricing?.overrides;
|
|
70
|
+
if (!Array.isArray(overrides)) return void 0;
|
|
71
|
+
const byThreshold = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const o of overrides) {
|
|
73
|
+
if (!o || typeof o !== "object") continue;
|
|
74
|
+
if (typeof o.min_prompt_tokens !== "number" || !(o.min_prompt_tokens > 0)) continue;
|
|
75
|
+
if (Object.keys(o).some((k) => k.startsWith("utc_"))) continue;
|
|
76
|
+
const inputPer1M = per1m(o.prompt);
|
|
77
|
+
const outputPer1M = per1m(o.completion);
|
|
78
|
+
if (inputPer1M === void 0 || outputPer1M === void 0) continue;
|
|
79
|
+
const tier = { aboveInputTokens: o.min_prompt_tokens, inputPer1M, outputPer1M };
|
|
80
|
+
const cacheRead = per1m(o.input_cache_read);
|
|
81
|
+
const cacheWrite = per1m(o.input_cache_write);
|
|
82
|
+
if (cacheRead !== void 0 && cacheRead > 0 && inputPer1M > 0) {
|
|
83
|
+
tier.cacheReadMultiplier = round6(cacheRead / inputPer1M);
|
|
84
|
+
}
|
|
85
|
+
if (cacheWrite !== void 0 && cacheWrite > 0 && inputPer1M > 0) {
|
|
86
|
+
tier.cacheWriteMultiplier = round6(cacheWrite / inputPer1M);
|
|
87
|
+
}
|
|
88
|
+
byThreshold.set(tier.aboveInputTokens, tier);
|
|
89
|
+
}
|
|
90
|
+
if (byThreshold.size === 0) return void 0;
|
|
91
|
+
return [...byThreshold.values()].sort((a, b) => a.aboveInputTokens - b.aboveInputTokens);
|
|
92
|
+
}
|
|
93
|
+
function serializeTiers(tiers) {
|
|
94
|
+
const items = tiers.map((t) => {
|
|
95
|
+
const fields = [
|
|
96
|
+
`aboveInputTokens: ${t.aboveInputTokens}`,
|
|
97
|
+
`inputPer1M: ${t.inputPer1M}`,
|
|
98
|
+
`outputPer1M: ${t.outputPer1M}`
|
|
99
|
+
];
|
|
100
|
+
if (t.cacheReadMultiplier !== void 0) fields.push(`cacheReadMultiplier: ${t.cacheReadMultiplier}`);
|
|
101
|
+
if (t.cacheWriteMultiplier !== void 0) fields.push(`cacheWriteMultiplier: ${t.cacheWriteMultiplier}`);
|
|
102
|
+
return `{ ${fields.join(", ")} }`;
|
|
103
|
+
});
|
|
104
|
+
return `tiers: [${items.join(", ")}]`;
|
|
105
|
+
}
|
|
106
|
+
|
|
59
107
|
// src/generators/llm-openrouter.ts
|
|
60
108
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
61
109
|
var OUTPUT_PATH = "packages/model-catalog/src/llm/openrouter-routes.generated.ts";
|
|
@@ -73,18 +121,18 @@ var ModelSchema = z.object({
|
|
|
73
121
|
var SnapshotSchema = z.object({
|
|
74
122
|
data: z.array(ModelSchema)
|
|
75
123
|
});
|
|
76
|
-
function
|
|
124
|
+
function round62(n) {
|
|
77
125
|
return Math.round(n * 1e6) / 1e6;
|
|
78
126
|
}
|
|
79
|
-
function
|
|
127
|
+
function per1m2(tokenPrice) {
|
|
80
128
|
if (tokenPrice === void 0) return void 0;
|
|
81
129
|
const n = Number(tokenPrice);
|
|
82
130
|
if (!Number.isFinite(n)) return void 0;
|
|
83
|
-
return
|
|
131
|
+
return round62(n * 1e6);
|
|
84
132
|
}
|
|
85
133
|
function fmt(n) {
|
|
86
134
|
if (!Number.isFinite(n)) return "0";
|
|
87
|
-
return `${
|
|
135
|
+
return `${round62(n)}`;
|
|
88
136
|
}
|
|
89
137
|
function vendorOf(id) {
|
|
90
138
|
if (!id.includes("/")) return id;
|
|
@@ -101,6 +149,9 @@ function serializeEntry(e) {
|
|
|
101
149
|
if (e.cacheWriteMultiplier !== void 0) {
|
|
102
150
|
fields.push(`cacheWriteMultiplier: ${fmt(e.cacheWriteMultiplier)}`);
|
|
103
151
|
}
|
|
152
|
+
if (e.tiers !== void 0) {
|
|
153
|
+
fields.push(serializeTiers(e.tiers));
|
|
154
|
+
}
|
|
104
155
|
if (e.addedAt !== void 0) {
|
|
105
156
|
fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`);
|
|
106
157
|
}
|
|
@@ -120,6 +171,7 @@ function serializeFile(entries) {
|
|
|
120
171
|
"// Pricing carries provider USD (inputPer1M / outputPer1M) plus cache",
|
|
121
172
|
"// multipliers (cacheReadMultiplier / cacheWriteMultiplier) derived from the",
|
|
122
173
|
"// source's input_cache_read / input_cache_write per-token fields when present.",
|
|
174
|
+
"// tiers are the source's prompt-length price overrides (min_prompt_tokens).",
|
|
123
175
|
"// addedAt is the ISO date this id was first seen by a sync run \u2014 backfilled",
|
|
124
176
|
"// from the source's own `created` timestamp, then NEVER mutated; see",
|
|
125
177
|
"// packages/catalog-sync/src/added-at.ts and the package README.",
|
|
@@ -152,8 +204,8 @@ async function generate(ctx) {
|
|
|
152
204
|
for (const model of parsed.data) {
|
|
153
205
|
const pricing = model.pricing;
|
|
154
206
|
if (!pricing) continue;
|
|
155
|
-
const inputPer1M =
|
|
156
|
-
const outputPer1M =
|
|
207
|
+
const inputPer1M = per1m2(pricing.prompt);
|
|
208
|
+
const outputPer1M = per1m2(pricing.completion);
|
|
157
209
|
if (inputPer1M === void 0 || outputPer1M === void 0) continue;
|
|
158
210
|
if (inputPer1M === 0 && outputPer1M === 0) continue;
|
|
159
211
|
const entry = {
|
|
@@ -162,14 +214,16 @@ async function generate(ctx) {
|
|
|
162
214
|
vendor: vendorOf(model.id),
|
|
163
215
|
provider: "openrouter"
|
|
164
216
|
};
|
|
165
|
-
const cacheReadPer1M =
|
|
166
|
-
const cacheWritePer1M =
|
|
217
|
+
const cacheReadPer1M = per1m2(pricing.input_cache_read);
|
|
218
|
+
const cacheWritePer1M = per1m2(pricing.input_cache_write);
|
|
167
219
|
if (cacheReadPer1M !== void 0 && cacheReadPer1M > 0 && inputPer1M > 0) {
|
|
168
|
-
entry.cacheReadMultiplier =
|
|
220
|
+
entry.cacheReadMultiplier = round62(cacheReadPer1M / inputPer1M);
|
|
169
221
|
}
|
|
170
222
|
if (cacheWritePer1M !== void 0 && cacheWritePer1M > 0 && inputPer1M > 0) {
|
|
171
|
-
entry.cacheWriteMultiplier =
|
|
223
|
+
entry.cacheWriteMultiplier = round62(cacheWritePer1M / inputPer1M);
|
|
172
224
|
}
|
|
225
|
+
const tiers = promptLengthTiers(pricing);
|
|
226
|
+
if (tiers) entry.tiers = tiers;
|
|
173
227
|
entries[model.id] = entry;
|
|
174
228
|
if (model.created !== void 0) createdAt[model.id] = isoDateFromUnixSeconds(model.created);
|
|
175
229
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types.ts","../../src/added-at.ts","../../src/generators/llm-openrouter.ts"],"names":["__dirname"],"mappings":"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","file":"llm-openrouter.mjs","sourcesContent":["/**\n * catalog-sync generator contract.\n *\n * FROZEN — a sibling agent builds provider generators against this surface.\n * Do not change field names, shapes, or the `defineGenerator` signature\n * without coordinating. Adding new generators is the expected extension\n * path; editing this file is not.\n */\n\nexport interface CatalogSource {\n id: string\n url: string\n /** HTTP method for the live (`--refresh`) fetch. Default `\"GET\"`. */\n method?: \"GET\" | \"POST\"\n /**\n * Request headers for the live fetch. A value may embed `env:VAR_NAME`\n * tokens, resolved from `process.env` at fetch time (e.g.\n * `{ \"xi-api-key\": \"env:ELEVENLABS_API_KEY\" }` or\n * `{ Authorization: \"Bearer env:MINIMAX_API_KEY\" }`). If any referenced env\n * var is unset, the source is treated as un-refreshable — the committed\n * snapshot is reused rather than fetched without auth. Offline reads\n * (default, no `--refresh`) ignore headers entirely.\n */\n headers?: Record<string, string>\n /** JSON request body for POST sources (e.g. MiniMax `get_voice`). */\n body?: unknown\n}\n\nexport interface GeneratorContext {\n /**\n * Reads the pinned snapshot at `snapshots/<src.id>.json`; if missing and\n * `refresh=true`, fetches `src.url` and writes the snapshot. Returns\n * parsed JSON.\n *\n * Offline by default: when no snapshot exists and `refresh` is false this\n * throws — generators MUST be runnable without network access so CI and\n * tests are deterministic. The only network path is behind `--refresh`.\n */\n fetchSource(src: CatalogSource): Promise<unknown>\n refresh: boolean\n}\n\n/** Repo-relative path → TS source text. */\nexport type GeneratedFiles = Record<string, string>\n\nexport interface CatalogGenerator {\n /** e.g. \"llm:openrouter\" */\n name: string\n modality: \"llm\" | \"image\" | \"video\" | \"audio\" | \"voice\"\n sources: CatalogSource[]\n generate(ctx: GeneratorContext): Promise<GeneratedFiles>\n}\n\n/** Identity helper — lets a generator module declare itself with type inference. */\nexport function defineGenerator(g: CatalogGenerator): CatalogGenerator {\n return g\n}\n","/**\n * `addedAt` ledger — tracks, per generator, the ISO date each model id was\n * FIRST seen by a sync run. See `README.md` (\"addedAt convention\") for the\n * full contract; this module is the mechanism.\n *\n * Invariant: once an id has a ledger entry, that date is NEVER overwritten —\n * not even if the source's own `created` timestamp for that id later\n * resolves to something different (a provider backdating/correcting its own\n * metadata must not rewrite catalog history). New ids are backfilled from\n * the source's own creation timestamp when it has one, else stamped with\n * the current run's date.\n *\n * Ledger entries are never dropped when an id disappears from a run — the\n * ledger records \"first ever seen\", not \"currently present\". A model that\n * vanishes from a provider's list and later reappears keeps its original\n * `addedAt` rather than looking newly added.\n *\n * Each generator owns one ledger file, committed at\n * `packages/catalog-sync/ledger/<generator-id>.json` (id → ISO date, sorted\n * keys, deterministic serialization) — included as a normal entry in the\n * generator's `GeneratedFiles` return value, so `runner.ts`'s existing\n * diff/write logic covers it for free (including `--check` drift detection).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/** The catalog-sync package directory — see `runner.ts`'s `catalogSyncDir`. */\nfunction catalogSyncDir(): string {\n return resolve(__dirname, \"..\")\n}\n\n/** Absolute path to a generator's committed addedAt ledger. */\nexport function ledgerAbsPath(ledgerId: string): string {\n return join(catalogSyncDir(), \"ledger\", `${ledgerId}.json`)\n}\n\n/** Repo-relative path for a ledger — for inclusion in a generator's `GeneratedFiles`. */\nexport function ledgerRelPath(ledgerId: string): string {\n return `packages/catalog-sync/ledger/${ledgerId}.json`\n}\n\nexport type AddedAtLedger = Record<string, string>\n\n/** Reads a committed ledger from disk. Missing/unparseable → empty ledger. */\nexport function readLedger(ledgerId: string): AddedAtLedger {\n const path = ledgerAbsPath(ledgerId)\n if (!existsSync(path)) return {}\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"))\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return {}\n return parsed as AddedAtLedger\n } catch {\n return {}\n }\n}\n\n/** Deterministic (sorted-key) JSON serialization, matching the `*.generated.ts` style of a trailing newline. */\nexport function serializeLedger(ledger: AddedAtLedger): string {\n const sorted: AddedAtLedger = {}\n for (const id of Object.keys(ledger).sort()) sorted[id] = ledger[id]!\n return `${JSON.stringify(sorted, null, 2)}\\n`\n}\n\n/**\n * Merge the current run's ids into a ledger.\n *\n * - id already in `previous` → keep the existing stamp verbatim (never\n * mutated), regardless of what `createdAt` says this run.\n * - id new to the ledger, source has a native creation timestamp for it →\n * backfill with `createdAt[id]`.\n * - id new to the ledger, no native timestamp → stamp with `today`.\n * - ids in `previous` but absent from `currentIds` this run are kept as-is\n * (see module doc comment — the ledger is \"first ever seen\").\n */\nexport function computeAddedAtLedger(\n currentIds: readonly string[],\n previous: AddedAtLedger,\n createdAt: Readonly<Record<string, string>>,\n today: string\n): AddedAtLedger {\n const next: AddedAtLedger = { ...previous }\n for (const id of currentIds) {\n if (next[id] !== undefined) continue\n next[id] = createdAt[id] ?? today\n }\n return next\n}\n\n/** Today's date as `YYYY-MM-DD`, in UTC. */\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\n/** Converts a provider's Unix-seconds creation timestamp to `YYYY-MM-DD`. */\nexport function isoDateFromUnixSeconds(seconds: number): string {\n return new Date(seconds * 1000).toISOString().slice(0, 10)\n}\n","import { z } from \"zod\"\n\nimport { defineGenerator, type GeneratedFiles, type GeneratorContext } from \"../types.js\"\nimport {\n computeAddedAtLedger,\n isoDateFromUnixSeconds,\n ledgerRelPath,\n readLedger,\n serializeLedger,\n todayIso,\n} from \"../added-at.js\"\n\n/**\n * PINNED source. The OpenRouter `/api/v1/models` payload lists every route\n * the router exposes with per-token USD pricing. We never fetch it at build\n * or test time — only behind `--refresh`, which writes\n * `snapshots/llm-openrouter.json` so the next offline run is deterministic.\n */\nconst OPENROUTER_MODELS_URL = \"https://openrouter.ai/api/v1/models\"\n\n/** Repo-relative drop-in target for {@link @agentproto/model-catalog}. */\nconst OUTPUT_PATH = \"packages/model-catalog/src/llm/openrouter-routes.generated.ts\"\n\n// ── Source schema ────────────────────────────────────────────────────────\n// OpenRouter prices are STRINGS of USD-per-token (e.g. \"0.000003\" = $3/1M).\n// `input_cache_read` / `input_cache_write` are optional and only present for\n// models that support prompt caching (Anthropic, etc.). These are the REAL\n// OpenRouter field names (verified against the live /v1/models payload) — an\n// earlier draft used `cache_read`/`cache_write`, which never matched, so cache\n// multipliers were silently always absent. passthrough() keeps the other live\n// keys (web_search, image, audio, internal_reasoning, input_cache_write_1h)\n// forward-compatible without a regenerate — we only read what we model.\n\nconst PricingSchema = z\n .object({\n prompt: z.string().optional(),\n completion: z.string().optional(),\n input_cache_read: z.string().optional(),\n input_cache_write: z.string().optional(),\n })\n .passthrough()\n\n// `created` is Unix seconds — OpenRouter's own route-creation timestamp,\n// used to backfill `addedAt` for ids not already in the ledger (see\n// `../added-at.ts`). Verified present on the live payload (2026-08-31).\nconst ModelSchema = z\n .object({\n id: z.string(),\n pricing: PricingSchema.optional(),\n created: z.number().optional(),\n })\n .passthrough()\n\nconst SnapshotSchema = z.object({\n data: z.array(ModelSchema),\n})\n\n// ── Output shape (mirrors model-catalog's LLMPricing) ────────────────────\n// Drop-in for `packages/model-catalog/src/llm/openrouter-routes.generated.ts`.\n// Structurally identical to LLMPricing — inputPer1M / outputPer1M in USD per\n// 1M tokens, plus cacheReadMultiplier / cacheWriteMultiplier WHEN the source\n// carries cache_read / cache_write. The existing committed file omits cache\n// fields; this generator emits them when present (per the P3 spec: \"plus\n// cache fields if the source has them\"). Fields are optional on LLMPricing,\n// so the output remains drop-in compatible.\n\ninterface LLMPricingEntry {\n inputPer1M: number\n outputPer1M: number\n cacheReadMultiplier?: number\n cacheWriteMultiplier?: number\n /** ISO date this id was first seen by a sync run. See `../added-at.ts`. */\n addedAt?: string\n vendor: string\n provider: \"openrouter\"\n}\n\n// ── Number helpers ──────────────────────────────────────────────────────\n\n/** Round to 6 decimal places, killing binary-float noise from the per-token × 1e6 product. */\nfunction round6(n: number): number {\n return Math.round(n * 1_000_000) / 1_000_000\n}\n\n/** Convert a per-token USD string to USD-per-1M-tokens, or undefined if unpriced. */\nfunction per1m(tokenPrice: string | undefined): number | undefined {\n if (tokenPrice === undefined) return undefined\n const n = Number(tokenPrice)\n if (!Number.isFinite(n)) return undefined\n return round6(n * 1_000_000)\n}\n\n/** Shortest faithful decimal string (matches existing file: 1, 0.5, 0.7448, 1.25). */\nfunction fmt(n: number): string {\n if (!Number.isFinite(n)) return \"0\"\n return `${round6(n)}`\n}\n\n/** Vendor slug = first segment of the OpenRouter route id (`vendor/model`). */\nfunction vendorOf(id: string): string {\n if (!id.includes(\"/\")) return id\n return id.split(\"/\")[0] ?? id\n}\n\n// ── Serialization ────────────────────────────────────────────────────────\n\nfunction serializeEntry(e: LLMPricingEntry): string {\n const fields: string[] = [\n `inputPer1M: ${fmt(e.inputPer1M)}`,\n `outputPer1M: ${fmt(e.outputPer1M)}`,\n ]\n if (e.cacheReadMultiplier !== undefined) {\n fields.push(`cacheReadMultiplier: ${fmt(e.cacheReadMultiplier)}`)\n }\n if (e.cacheWriteMultiplier !== undefined) {\n fields.push(`cacheWriteMultiplier: ${fmt(e.cacheWriteMultiplier)}`)\n }\n if (e.addedAt !== undefined) {\n fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`)\n }\n fields.push(`vendor: ${JSON.stringify(e.vendor)}`)\n fields.push(`provider: ${JSON.stringify(e.provider)}`)\n return `{\\n ${fields.join(\",\\n \")},\\n }`\n}\n\nfunction serializeFile(entries: Record<string, LLMPricingEntry>): string {\n const ids = Object.keys(entries).sort()\n const providers = Array.from(new Set(ids.map(vendorOf))).sort()\n\n const lines: string[] = [\n \"// AUTO-GENERATED by @agentproto/catalog-sync (llm:openrouter).\",\n \"// Do not edit by hand — re-run `pnpm --filter @agentproto/catalog-sync generate`.\",\n `// Source: ${OPENROUTER_MODELS_URL}`,\n \"// Pricing carries provider USD (inputPer1M / outputPer1M) plus cache\",\n \"// multipliers (cacheReadMultiplier / cacheWriteMultiplier) derived from the\",\n \"// source's input_cache_read / input_cache_write per-token fields when present.\",\n \"// addedAt is the ISO date this id was first seen by a sync run — backfilled\",\n \"// from the source's own `created` timestamp, then NEVER mutated; see\",\n \"// packages/catalog-sync/src/added-at.ts and the package README.\",\n \"\",\n 'import type { LLMPricing } from \"./catalog.js\"',\n \"\",\n \"export const OPENROUTER_ROUTES: Record<string, LLMPricing> = {\",\n ]\n for (const id of ids) {\n lines.push(` ${JSON.stringify(id)}: ${serializeEntry(entries[id]!)},`)\n }\n lines.push(\"}\")\n lines.push(\"\")\n lines.push(\"/** Provider slugs treated as OpenRouter routes by the picker. */\")\n lines.push(\"export const OPENROUTER_PROVIDERS: readonly string[] = [\")\n for (const p of providers) {\n lines.push(` ${JSON.stringify(p)},`)\n }\n lines.push(\"]\")\n lines.push(\"\")\n return lines.join(\"\\n\")\n}\n\n// ── Generator ───────────────────────────────────────────────────────────\n\nconst LEDGER_ID = \"llm-openrouter\"\n\nasync function generate(ctx: GeneratorContext): Promise<GeneratedFiles> {\n const src = sources[0]\n if (!src) throw new Error(\"llm:openrouter: no source configured\")\n const parsed = SnapshotSchema.parse(await ctx.fetchSource(src))\n\n const entries: Record<string, LLMPricingEntry> = {}\n const createdAt: Record<string, string> = {}\n for (const model of parsed.data) {\n const pricing = model.pricing\n if (!pricing) continue\n const inputPer1M = per1m(pricing.prompt)\n const outputPer1M = per1m(pricing.completion)\n if (inputPer1M === undefined || outputPer1M === undefined) continue\n // Skip unpriced routes (e.g. openrouter/auto, which carries \"0\" pricing).\n if (inputPer1M === 0 && outputPer1M === 0) continue\n\n const entry: LLMPricingEntry = {\n inputPer1M,\n outputPer1M,\n vendor: vendorOf(model.id),\n provider: \"openrouter\",\n }\n\n const cacheReadPer1M = per1m(pricing.input_cache_read)\n const cacheWritePer1M = per1m(pricing.input_cache_write)\n if (cacheReadPer1M !== undefined && cacheReadPer1M > 0 && inputPer1M > 0) {\n entry.cacheReadMultiplier = round6(cacheReadPer1M / inputPer1M)\n }\n if (cacheWritePer1M !== undefined && cacheWritePer1M > 0 && inputPer1M > 0) {\n entry.cacheWriteMultiplier = round6(cacheWritePer1M / inputPer1M)\n }\n\n entries[model.id] = entry\n if (model.created !== undefined) createdAt[model.id] = isoDateFromUnixSeconds(model.created)\n }\n\n const ledger = computeAddedAtLedger(\n Object.keys(entries),\n readLedger(LEDGER_ID),\n createdAt,\n todayIso()\n )\n for (const [id, entry] of Object.entries(entries)) entry.addedAt = ledger[id]\n\n return {\n [OUTPUT_PATH]: serializeFile(entries),\n [ledgerRelPath(LEDGER_ID)]: serializeLedger(ledger),\n }\n}\n\nconst sources = [{ id: \"llm-openrouter\", url: OPENROUTER_MODELS_URL }]\n\nexport const llmOpenRouterGenerator = defineGenerator({\n name: \"llm:openrouter\",\n modality: \"llm\",\n sources,\n generate,\n})\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts","../../src/added-at.ts","../../src/sources/openrouter-prompt-tiers.mjs","../../src/generators/llm-openrouter.ts"],"names":["__dirname","round6","per1m"],"mappings":"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","file":"llm-openrouter.mjs","sourcesContent":["/**\n * catalog-sync generator contract.\n *\n * FROZEN — a sibling agent builds provider generators against this surface.\n * Do not change field names, shapes, or the `defineGenerator` signature\n * without coordinating. Adding new generators is the expected extension\n * path; editing this file is not.\n */\n\nexport interface CatalogSource {\n id: string\n url: string\n /** HTTP method for the live (`--refresh`) fetch. Default `\"GET\"`. */\n method?: \"GET\" | \"POST\"\n /**\n * Request headers for the live fetch. A value may embed `env:VAR_NAME`\n * tokens, resolved from `process.env` at fetch time (e.g.\n * `{ \"xi-api-key\": \"env:ELEVENLABS_API_KEY\" }` or\n * `{ Authorization: \"Bearer env:MINIMAX_API_KEY\" }`). If any referenced env\n * var is unset, the source is treated as un-refreshable — the committed\n * snapshot is reused rather than fetched without auth. Offline reads\n * (default, no `--refresh`) ignore headers entirely.\n */\n headers?: Record<string, string>\n /** JSON request body for POST sources (e.g. MiniMax `get_voice`). */\n body?: unknown\n}\n\nexport interface GeneratorContext {\n /**\n * Reads the pinned snapshot at `snapshots/<src.id>.json`; if missing and\n * `refresh=true`, fetches `src.url` and writes the snapshot. Returns\n * parsed JSON.\n *\n * Offline by default: when no snapshot exists and `refresh` is false this\n * throws — generators MUST be runnable without network access so CI and\n * tests are deterministic. The only network path is behind `--refresh`.\n */\n fetchSource(src: CatalogSource): Promise<unknown>\n refresh: boolean\n}\n\n/** Repo-relative path → TS source text. */\nexport type GeneratedFiles = Record<string, string>\n\nexport interface CatalogGenerator {\n /** e.g. \"llm:openrouter\" */\n name: string\n modality: \"llm\" | \"image\" | \"video\" | \"audio\" | \"voice\"\n sources: CatalogSource[]\n generate(ctx: GeneratorContext): Promise<GeneratedFiles>\n}\n\n/** Identity helper — lets a generator module declare itself with type inference. */\nexport function defineGenerator(g: CatalogGenerator): CatalogGenerator {\n return g\n}\n","/**\n * `addedAt` ledger — tracks, per generator, the ISO date each model id was\n * FIRST seen by a sync run. See `README.md` (\"addedAt convention\") for the\n * full contract; this module is the mechanism.\n *\n * Invariant: once an id has a ledger entry, that date is NEVER overwritten —\n * not even if the source's own `created` timestamp for that id later\n * resolves to something different (a provider backdating/correcting its own\n * metadata must not rewrite catalog history). New ids are backfilled from\n * the source's own creation timestamp when it has one, else stamped with\n * the current run's date.\n *\n * Ledger entries are never dropped when an id disappears from a run — the\n * ledger records \"first ever seen\", not \"currently present\". A model that\n * vanishes from a provider's list and later reappears keeps its original\n * `addedAt` rather than looking newly added.\n *\n * Each generator owns one ledger file, committed at\n * `packages/catalog-sync/ledger/<generator-id>.json` (id → ISO date, sorted\n * keys, deterministic serialization) — included as a normal entry in the\n * generator's `GeneratedFiles` return value, so `runner.ts`'s existing\n * diff/write logic covers it for free (including `--check` drift detection).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/** The catalog-sync package directory — see `runner.ts`'s `catalogSyncDir`. */\nfunction catalogSyncDir(): string {\n return resolve(__dirname, \"..\")\n}\n\n/** Absolute path to a generator's committed addedAt ledger. */\nexport function ledgerAbsPath(ledgerId: string): string {\n return join(catalogSyncDir(), \"ledger\", `${ledgerId}.json`)\n}\n\n/** Repo-relative path for a ledger — for inclusion in a generator's `GeneratedFiles`. */\nexport function ledgerRelPath(ledgerId: string): string {\n return `packages/catalog-sync/ledger/${ledgerId}.json`\n}\n\nexport type AddedAtLedger = Record<string, string>\n\n/** Reads a committed ledger from disk. Missing/unparseable → empty ledger. */\nexport function readLedger(ledgerId: string): AddedAtLedger {\n const path = ledgerAbsPath(ledgerId)\n if (!existsSync(path)) return {}\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"))\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return {}\n return parsed as AddedAtLedger\n } catch {\n return {}\n }\n}\n\n/** Deterministic (sorted-key) JSON serialization, matching the `*.generated.ts` style of a trailing newline. */\nexport function serializeLedger(ledger: AddedAtLedger): string {\n const sorted: AddedAtLedger = {}\n for (const id of Object.keys(ledger).sort()) sorted[id] = ledger[id]!\n return `${JSON.stringify(sorted, null, 2)}\\n`\n}\n\n/**\n * Merge the current run's ids into a ledger.\n *\n * - id already in `previous` → keep the existing stamp verbatim (never\n * mutated), regardless of what `createdAt` says this run.\n * - id new to the ledger, source has a native creation timestamp for it →\n * backfill with `createdAt[id]`.\n * - id new to the ledger, no native timestamp → stamp with `today`.\n * - ids in `previous` but absent from `currentIds` this run are kept as-is\n * (see module doc comment — the ledger is \"first ever seen\").\n */\nexport function computeAddedAtLedger(\n currentIds: readonly string[],\n previous: AddedAtLedger,\n createdAt: Readonly<Record<string, string>>,\n today: string\n): AddedAtLedger {\n const next: AddedAtLedger = { ...previous }\n for (const id of currentIds) {\n if (next[id] !== undefined) continue\n next[id] = createdAt[id] ?? today\n }\n return next\n}\n\n/** Today's date as `YYYY-MM-DD`, in UTC. */\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\n/** Converts a provider's Unix-seconds creation timestamp to `YYYY-MM-DD`. */\nexport function isoDateFromUnixSeconds(seconds: number): string {\n return new Date(seconds * 1000).toISOString().slice(0, 10)\n}\n","/**\n * Prompt-length price tiers from an OpenRouter `pricing` block.\n *\n * Plain JS (types in `openrouter-prompt-tiers.d.mts`) so both the TS\n * `llm:openrouter` generator and the bare-`node` native sync scripts\n * (`scripts/catalog-sync/sync-anthropic.mjs`) derive tiers the same way.\n *\n * OpenRouter publishes a model's non-base prices as `pricing.overrides[]`.\n * Two unrelated kinds share that array:\n * - prompt-length tiers: `{ min_prompt_tokens: 100000, prompt, completion,\n * input_cache_read?, input_cache_write? }` — e.g. Claude Haiku 5.5, 5x\n * over 100k tokens (matches platform.claude.com/docs/en/about-claude/pricing,\n * checked 2026-10-08).\n * - time-of-day discounts: `{ utc_start, utc_end, utc_days?, … }` (DeepSeek,\n * Tencent off-peak). NOT a prompt-length tier — ignored here.\n * Only overrides keyed SOLELY on `min_prompt_tokens` become tiers.\n *\n * `min_prompt_tokens` maps to `aboveInputTokens` as-is; a prompt of exactly\n * the threshold lands in the base tier (Anthropic: \"prompts over 100,000\n * tokens\"). Cache ratios are relative to the TIER's own prompt price and\n * omitted when the override doesn't carry them (the catalog then inherits\n * the base multiplier).\n */\n\nfunction round6(n) {\n return Math.round(n * 1_000_000) / 1_000_000\n}\n\nfunction per1m(tokenPrice) {\n if (tokenPrice === undefined || tokenPrice === null) return undefined\n const n = Number(tokenPrice)\n return Number.isFinite(n) ? round6(n * 1_000_000) : undefined\n}\n\n/**\n * @param {Record<string, unknown> | undefined} pricing OpenRouter `pricing`\n * @returns {import(\"./openrouter-prompt-tiers.d.mts\").PromptLengthTier[] | undefined}\n * ascending by `aboveInputTokens`, or undefined when the model has none.\n */\nexport function promptLengthTiers(pricing) {\n const overrides = pricing?.overrides\n if (!Array.isArray(overrides)) return undefined\n const byThreshold = new Map()\n for (const o of overrides) {\n if (!o || typeof o !== \"object\") continue\n if (typeof o.min_prompt_tokens !== \"number\" || !(o.min_prompt_tokens > 0)) continue\n if (Object.keys(o).some((k) => k.startsWith(\"utc_\"))) continue\n const inputPer1M = per1m(o.prompt)\n const outputPer1M = per1m(o.completion)\n if (inputPer1M === undefined || outputPer1M === undefined) continue\n const tier = { aboveInputTokens: o.min_prompt_tokens, inputPer1M, outputPer1M }\n const cacheRead = per1m(o.input_cache_read)\n const cacheWrite = per1m(o.input_cache_write)\n if (cacheRead !== undefined && cacheRead > 0 && inputPer1M > 0) {\n tier.cacheReadMultiplier = round6(cacheRead / inputPer1M)\n }\n if (cacheWrite !== undefined && cacheWrite > 0 && inputPer1M > 0) {\n tier.cacheWriteMultiplier = round6(cacheWrite / inputPer1M)\n }\n byThreshold.set(tier.aboveInputTokens, tier)\n }\n if (byThreshold.size === 0) return undefined\n return [...byThreshold.values()].sort((a, b) => a.aboveInputTokens - b.aboveInputTokens)\n}\n\n/**\n * Render tiers as a TS object-literal field for a generated pricing row:\n * `tiers: [{ aboveInputTokens: 100000, inputPer1M: 0.5, outputPer1M: 2.5 }]`.\n *\n * @param {import(\"./openrouter-prompt-tiers.d.mts\").PromptLengthTier[]} tiers\n */\nexport function serializeTiers(tiers) {\n const items = tiers.map((t) => {\n const fields = [\n `aboveInputTokens: ${t.aboveInputTokens}`,\n `inputPer1M: ${t.inputPer1M}`,\n `outputPer1M: ${t.outputPer1M}`,\n ]\n if (t.cacheReadMultiplier !== undefined) fields.push(`cacheReadMultiplier: ${t.cacheReadMultiplier}`)\n if (t.cacheWriteMultiplier !== undefined) fields.push(`cacheWriteMultiplier: ${t.cacheWriteMultiplier}`)\n return `{ ${fields.join(\", \")} }`\n })\n return `tiers: [${items.join(\", \")}]`\n}\n","import { z } from \"zod\"\n\nimport { defineGenerator, type GeneratedFiles, type GeneratorContext } from \"../types.js\"\nimport {\n computeAddedAtLedger,\n isoDateFromUnixSeconds,\n ledgerRelPath,\n readLedger,\n serializeLedger,\n todayIso,\n} from \"../added-at.js\"\nimport {\n promptLengthTiers,\n serializeTiers,\n type PromptLengthTier,\n} from \"../sources/openrouter-prompt-tiers.mjs\"\n\n/**\n * PINNED source. The OpenRouter `/api/v1/models` payload lists every route\n * the router exposes with per-token USD pricing. We never fetch it at build\n * or test time — only behind `--refresh`, which writes\n * `snapshots/llm-openrouter.json` so the next offline run is deterministic.\n */\nconst OPENROUTER_MODELS_URL = \"https://openrouter.ai/api/v1/models\"\n\n/** Repo-relative drop-in target for {@link @agentproto/model-catalog}. */\nconst OUTPUT_PATH = \"packages/model-catalog/src/llm/openrouter-routes.generated.ts\"\n\n// ── Source schema ────────────────────────────────────────────────────────\n// OpenRouter prices are STRINGS of USD-per-token (e.g. \"0.000003\" = $3/1M).\n// `input_cache_read` / `input_cache_write` are optional and only present for\n// models that support prompt caching (Anthropic, etc.). These are the REAL\n// OpenRouter field names (verified against the live /v1/models payload) — an\n// earlier draft used `cache_read`/`cache_write`, which never matched, so cache\n// multipliers were silently always absent. passthrough() keeps the other live\n// keys (web_search, image, audio, internal_reasoning, input_cache_write_1h)\n// forward-compatible without a regenerate — we only read what we model.\n// `overrides` (prompt-length tiers, time-of-day discounts) rides through\n// passthrough() and is read by `promptLengthTiers`.\n\nconst PricingSchema = z\n .object({\n prompt: z.string().optional(),\n completion: z.string().optional(),\n input_cache_read: z.string().optional(),\n input_cache_write: z.string().optional(),\n })\n .passthrough()\n\n// `created` is Unix seconds — OpenRouter's own route-creation timestamp,\n// used to backfill `addedAt` for ids not already in the ledger (see\n// `../added-at.ts`). Verified present on the live payload (2026-08-31).\nconst ModelSchema = z\n .object({\n id: z.string(),\n pricing: PricingSchema.optional(),\n created: z.number().optional(),\n })\n .passthrough()\n\nconst SnapshotSchema = z.object({\n data: z.array(ModelSchema),\n})\n\n// ── Output shape (mirrors model-catalog's LLMPricing) ────────────────────\n// Drop-in for `packages/model-catalog/src/llm/openrouter-routes.generated.ts`.\n// Structurally identical to LLMPricing — inputPer1M / outputPer1M in USD per\n// 1M tokens, plus cacheReadMultiplier / cacheWriteMultiplier WHEN the source\n// carries cache_read / cache_write. The existing committed file omits cache\n// fields; this generator emits them when present (per the P3 spec: \"plus\n// cache fields if the source has them\"). Fields are optional on LLMPricing,\n// so the output remains drop-in compatible.\n\ninterface LLMPricingEntry {\n inputPer1M: number\n outputPer1M: number\n cacheReadMultiplier?: number\n cacheWriteMultiplier?: number\n /** Prompt-length tiers from `pricing.overrides`. See `../sources/openrouter-prompt-tiers.mjs`. */\n tiers?: PromptLengthTier[]\n /** ISO date this id was first seen by a sync run. See `../added-at.ts`. */\n addedAt?: string\n vendor: string\n provider: \"openrouter\"\n}\n\n// ── Number helpers ──────────────────────────────────────────────────────\n\n/** Round to 6 decimal places, killing binary-float noise from the per-token × 1e6 product. */\nfunction round6(n: number): number {\n return Math.round(n * 1_000_000) / 1_000_000\n}\n\n/** Convert a per-token USD string to USD-per-1M-tokens, or undefined if unpriced. */\nfunction per1m(tokenPrice: string | undefined): number | undefined {\n if (tokenPrice === undefined) return undefined\n const n = Number(tokenPrice)\n if (!Number.isFinite(n)) return undefined\n return round6(n * 1_000_000)\n}\n\n/** Shortest faithful decimal string (matches existing file: 1, 0.5, 0.7448, 1.25). */\nfunction fmt(n: number): string {\n if (!Number.isFinite(n)) return \"0\"\n return `${round6(n)}`\n}\n\n/** Vendor slug = first segment of the OpenRouter route id (`vendor/model`). */\nfunction vendorOf(id: string): string {\n if (!id.includes(\"/\")) return id\n return id.split(\"/\")[0] ?? id\n}\n\n// ── Serialization ────────────────────────────────────────────────────────\n\nfunction serializeEntry(e: LLMPricingEntry): string {\n const fields: string[] = [\n `inputPer1M: ${fmt(e.inputPer1M)}`,\n `outputPer1M: ${fmt(e.outputPer1M)}`,\n ]\n if (e.cacheReadMultiplier !== undefined) {\n fields.push(`cacheReadMultiplier: ${fmt(e.cacheReadMultiplier)}`)\n }\n if (e.cacheWriteMultiplier !== undefined) {\n fields.push(`cacheWriteMultiplier: ${fmt(e.cacheWriteMultiplier)}`)\n }\n if (e.tiers !== undefined) {\n fields.push(serializeTiers(e.tiers))\n }\n if (e.addedAt !== undefined) {\n fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`)\n }\n fields.push(`vendor: ${JSON.stringify(e.vendor)}`)\n fields.push(`provider: ${JSON.stringify(e.provider)}`)\n return `{\\n ${fields.join(\",\\n \")},\\n }`\n}\n\nfunction serializeFile(entries: Record<string, LLMPricingEntry>): string {\n const ids = Object.keys(entries).sort()\n const providers = Array.from(new Set(ids.map(vendorOf))).sort()\n\n const lines: string[] = [\n \"// AUTO-GENERATED by @agentproto/catalog-sync (llm:openrouter).\",\n \"// Do not edit by hand — re-run `pnpm --filter @agentproto/catalog-sync generate`.\",\n `// Source: ${OPENROUTER_MODELS_URL}`,\n \"// Pricing carries provider USD (inputPer1M / outputPer1M) plus cache\",\n \"// multipliers (cacheReadMultiplier / cacheWriteMultiplier) derived from the\",\n \"// source's input_cache_read / input_cache_write per-token fields when present.\",\n \"// tiers are the source's prompt-length price overrides (min_prompt_tokens).\",\n \"// addedAt is the ISO date this id was first seen by a sync run — backfilled\",\n \"// from the source's own `created` timestamp, then NEVER mutated; see\",\n \"// packages/catalog-sync/src/added-at.ts and the package README.\",\n \"\",\n 'import type { LLMPricing } from \"./catalog.js\"',\n \"\",\n \"export const OPENROUTER_ROUTES: Record<string, LLMPricing> = {\",\n ]\n for (const id of ids) {\n lines.push(` ${JSON.stringify(id)}: ${serializeEntry(entries[id]!)},`)\n }\n lines.push(\"}\")\n lines.push(\"\")\n lines.push(\"/** Provider slugs treated as OpenRouter routes by the picker. */\")\n lines.push(\"export const OPENROUTER_PROVIDERS: readonly string[] = [\")\n for (const p of providers) {\n lines.push(` ${JSON.stringify(p)},`)\n }\n lines.push(\"]\")\n lines.push(\"\")\n return lines.join(\"\\n\")\n}\n\n// ── Generator ───────────────────────────────────────────────────────────\n\nconst LEDGER_ID = \"llm-openrouter\"\n\nasync function generate(ctx: GeneratorContext): Promise<GeneratedFiles> {\n const src = sources[0]\n if (!src) throw new Error(\"llm:openrouter: no source configured\")\n const parsed = SnapshotSchema.parse(await ctx.fetchSource(src))\n\n const entries: Record<string, LLMPricingEntry> = {}\n const createdAt: Record<string, string> = {}\n for (const model of parsed.data) {\n const pricing = model.pricing\n if (!pricing) continue\n const inputPer1M = per1m(pricing.prompt)\n const outputPer1M = per1m(pricing.completion)\n if (inputPer1M === undefined || outputPer1M === undefined) continue\n // Skip unpriced routes (e.g. openrouter/auto, which carries \"0\" pricing).\n if (inputPer1M === 0 && outputPer1M === 0) continue\n\n const entry: LLMPricingEntry = {\n inputPer1M,\n outputPer1M,\n vendor: vendorOf(model.id),\n provider: \"openrouter\",\n }\n\n const cacheReadPer1M = per1m(pricing.input_cache_read)\n const cacheWritePer1M = per1m(pricing.input_cache_write)\n if (cacheReadPer1M !== undefined && cacheReadPer1M > 0 && inputPer1M > 0) {\n entry.cacheReadMultiplier = round6(cacheReadPer1M / inputPer1M)\n }\n if (cacheWritePer1M !== undefined && cacheWritePer1M > 0 && inputPer1M > 0) {\n entry.cacheWriteMultiplier = round6(cacheWritePer1M / inputPer1M)\n }\n const tiers = promptLengthTiers(pricing)\n if (tiers) entry.tiers = tiers\n\n entries[model.id] = entry\n if (model.created !== undefined) createdAt[model.id] = isoDateFromUnixSeconds(model.created)\n }\n\n const ledger = computeAddedAtLedger(\n Object.keys(entries),\n readLedger(LEDGER_ID),\n createdAt,\n todayIso()\n )\n for (const [id, entry] of Object.entries(entries)) entry.addedAt = ledger[id]\n\n return {\n [OUTPUT_PATH]: serializeFile(entries),\n [ledgerRelPath(LEDGER_ID)]: serializeLedger(ledger),\n }\n}\n\nconst sources = [{ id: \"llm-openrouter\", url: OPENROUTER_MODELS_URL }]\n\nexport const llmOpenRouterGenerator = defineGenerator({\n name: \"llm:openrouter\",\n modality: \"llm\",\n sources,\n generate,\n})\n"]}
|