@ken-jo/agent-connector 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +348 -0
  4. package/dist/amp-QSFH2V7Y.js +217 -0
  5. package/dist/amp-QSFH2V7Y.js.map +1 -0
  6. package/dist/amp-TULUPXXL.js +237 -0
  7. package/dist/amp-TULUPXXL.js.map +1 -0
  8. package/dist/antigravity-CX2YLSY7.js +17 -0
  9. package/dist/antigravity-CX2YLSY7.js.map +1 -0
  10. package/dist/antigravity-STHZWMP3.js +171 -0
  11. package/dist/antigravity-STHZWMP3.js.map +1 -0
  12. package/dist/antigravity-cli-P6HADOAO.js +121 -0
  13. package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
  14. package/dist/antigravity-cli-YQDFVV4U.js +86 -0
  15. package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
  16. package/dist/chunk-54EMKTTX.js +191 -0
  17. package/dist/chunk-54EMKTTX.js.map +1 -0
  18. package/dist/chunk-7L6ITC4T.js +54 -0
  19. package/dist/chunk-7L6ITC4T.js.map +1 -0
  20. package/dist/chunk-7MSDGHJW.js +316 -0
  21. package/dist/chunk-7MSDGHJW.js.map +1 -0
  22. package/dist/chunk-BCHWFTV4.js +196 -0
  23. package/dist/chunk-BCHWFTV4.js.map +1 -0
  24. package/dist/chunk-DBQMX75U.js +36 -0
  25. package/dist/chunk-DBQMX75U.js.map +1 -0
  26. package/dist/chunk-EXLS7BSI.js +29 -0
  27. package/dist/chunk-EXLS7BSI.js.map +1 -0
  28. package/dist/chunk-JNM3TPLX.js +593 -0
  29. package/dist/chunk-JNM3TPLX.js.map +1 -0
  30. package/dist/chunk-JWNT5KW7.js +197 -0
  31. package/dist/chunk-JWNT5KW7.js.map +1 -0
  32. package/dist/chunk-KHO5WNTP.js +30 -0
  33. package/dist/chunk-KHO5WNTP.js.map +1 -0
  34. package/dist/chunk-LTT56GI3.js +155 -0
  35. package/dist/chunk-LTT56GI3.js.map +1 -0
  36. package/dist/chunk-MEH7GC7R.js +111 -0
  37. package/dist/chunk-MEH7GC7R.js.map +1 -0
  38. package/dist/chunk-MQPNU2CY.js +64 -0
  39. package/dist/chunk-MQPNU2CY.js.map +1 -0
  40. package/dist/chunk-OPXAP3AC.js +12 -0
  41. package/dist/chunk-OPXAP3AC.js.map +1 -0
  42. package/dist/chunk-RK6VBLXP.js +50 -0
  43. package/dist/chunk-RK6VBLXP.js.map +1 -0
  44. package/dist/chunk-TIIP3RG2.js +420 -0
  45. package/dist/chunk-TIIP3RG2.js.map +1 -0
  46. package/dist/chunk-TRKWUNO4.js +702 -0
  47. package/dist/chunk-TRKWUNO4.js.map +1 -0
  48. package/dist/chunk-TRQKHP7D.js +93 -0
  49. package/dist/chunk-TRQKHP7D.js.map +1 -0
  50. package/dist/chunk-WCETQWWK.js +59 -0
  51. package/dist/chunk-WCETQWWK.js.map +1 -0
  52. package/dist/chunk-WV63X7IK.js +357 -0
  53. package/dist/chunk-WV63X7IK.js.map +1 -0
  54. package/dist/chunk-XD7MY6HB.js +343 -0
  55. package/dist/chunk-XD7MY6HB.js.map +1 -0
  56. package/dist/chunk-XNAM7F35.js +276 -0
  57. package/dist/chunk-XNAM7F35.js.map +1 -0
  58. package/dist/chunk-XUCWLJY5.js +238 -0
  59. package/dist/chunk-XUCWLJY5.js.map +1 -0
  60. package/dist/chunk-YNZU6PAV.js +800 -0
  61. package/dist/chunk-YNZU6PAV.js.map +1 -0
  62. package/dist/chunk-YT43MUSJ.js +146 -0
  63. package/dist/chunk-YT43MUSJ.js.map +1 -0
  64. package/dist/claude-code-HKDV7T32.js +637 -0
  65. package/dist/claude-code-HKDV7T32.js.map +1 -0
  66. package/dist/claude-code-W74MNTAC.js +652 -0
  67. package/dist/claude-code-W74MNTAC.js.map +1 -0
  68. package/dist/cli/sdk.d.ts +54 -0
  69. package/dist/cli/sdk.js +82 -0
  70. package/dist/cli/sdk.js.map +1 -0
  71. package/dist/cli.d.ts +2 -0
  72. package/dist/cli.js +15 -0
  73. package/dist/cli.js.map +1 -0
  74. package/dist/codebuff-3PMZJJHP.js +307 -0
  75. package/dist/codebuff-3PMZJJHP.js.map +1 -0
  76. package/dist/codebuff-QX57FJDN.js +220 -0
  77. package/dist/codebuff-QX57FJDN.js.map +1 -0
  78. package/dist/codex-JFQTS6LU.js +593 -0
  79. package/dist/codex-JFQTS6LU.js.map +1 -0
  80. package/dist/codex-Q3ATRRX3.js +364 -0
  81. package/dist/codex-Q3ATRRX3.js.map +1 -0
  82. package/dist/copilot-cli-MTCGHZ2T.js +622 -0
  83. package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
  84. package/dist/copilot-cli-SJGIXVSD.js +369 -0
  85. package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
  86. package/dist/crush-I7PK3FGI.js +360 -0
  87. package/dist/crush-I7PK3FGI.js.map +1 -0
  88. package/dist/crush-LN5QFFR2.js +183 -0
  89. package/dist/crush-LN5QFFR2.js.map +1 -0
  90. package/dist/cursor-4TMXKJOZ.js +603 -0
  91. package/dist/cursor-4TMXKJOZ.js.map +1 -0
  92. package/dist/cursor-QZ6MTO74.js +194 -0
  93. package/dist/cursor-QZ6MTO74.js.map +1 -0
  94. package/dist/detect-WIAGVYS4.js +68 -0
  95. package/dist/detect-WIAGVYS4.js.map +1 -0
  96. package/dist/doctor-EZW2K4NI.js +372 -0
  97. package/dist/doctor-EZW2K4NI.js.map +1 -0
  98. package/dist/droid-KN6VRMMT.js +424 -0
  99. package/dist/droid-KN6VRMMT.js.map +1 -0
  100. package/dist/droid-W5WF5XEX.js +224 -0
  101. package/dist/droid-W5WF5XEX.js.map +1 -0
  102. package/dist/gemini-cli-CIRW37TU.js +369 -0
  103. package/dist/gemini-cli-CIRW37TU.js.map +1 -0
  104. package/dist/gemini-cli-Y35FFK4M.js +705 -0
  105. package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
  106. package/dist/goose-A3M5GODH.js +212 -0
  107. package/dist/goose-A3M5GODH.js.map +1 -0
  108. package/dist/goose-ZTGTSUFJ.js +480 -0
  109. package/dist/goose-ZTGTSUFJ.js.map +1 -0
  110. package/dist/hermes-H2KV44GK.js +470 -0
  111. package/dist/hermes-H2KV44GK.js.map +1 -0
  112. package/dist/hermes-ZYLMOFLZ.js +196 -0
  113. package/dist/hermes-ZYLMOFLZ.js.map +1 -0
  114. package/dist/hook-YVDILAMZ.js +75 -0
  115. package/dist/hook-YVDILAMZ.js.map +1 -0
  116. package/dist/index.d.ts +22 -0
  117. package/dist/index.js +10 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/install-HQRYZOQ2.js +60 -0
  120. package/dist/install-HQRYZOQ2.js.map +1 -0
  121. package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
  122. package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
  123. package/dist/kilo-NSWAVP6P.js +235 -0
  124. package/dist/kilo-NSWAVP6P.js.map +1 -0
  125. package/dist/kilo-QXJRCSLT.js +384 -0
  126. package/dist/kilo-QXJRCSLT.js.map +1 -0
  127. package/dist/kilo-cli-EAQTHXUW.js +587 -0
  128. package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
  129. package/dist/kilo-cli-EVBKUODA.js +160 -0
  130. package/dist/kilo-cli-EVBKUODA.js.map +1 -0
  131. package/dist/kimi-EI3UM3ZV.js +155 -0
  132. package/dist/kimi-EI3UM3ZV.js.map +1 -0
  133. package/dist/kimi-GQ7VXNYT.js +423 -0
  134. package/dist/kimi-GQ7VXNYT.js.map +1 -0
  135. package/dist/kiro-UCLSLBW2.js +217 -0
  136. package/dist/kiro-UCLSLBW2.js.map +1 -0
  137. package/dist/kiro-VQTT6LO2.js +451 -0
  138. package/dist/kiro-VQTT6LO2.js.map +1 -0
  139. package/dist/leaderboard-K6ON3SWW.js +137 -0
  140. package/dist/leaderboard-K6ON3SWW.js.map +1 -0
  141. package/dist/mux-T2TV4CIQ.js +211 -0
  142. package/dist/mux-T2TV4CIQ.js.map +1 -0
  143. package/dist/mux-ZMLVAXOO.js +96 -0
  144. package/dist/mux-ZMLVAXOO.js.map +1 -0
  145. package/dist/omp-PB4VDKS6.js +586 -0
  146. package/dist/omp-PB4VDKS6.js.map +1 -0
  147. package/dist/openclaw-ETN2M63O.js +751 -0
  148. package/dist/openclaw-ETN2M63O.js.map +1 -0
  149. package/dist/openclaw-IYA7ASL6.js +150 -0
  150. package/dist/openclaw-IYA7ASL6.js.map +1 -0
  151. package/dist/opencode-FZ33JKUZ.js +734 -0
  152. package/dist/opencode-FZ33JKUZ.js.map +1 -0
  153. package/dist/opencode-HWT3RSMN.js +216 -0
  154. package/dist/opencode-HWT3RSMN.js.map +1 -0
  155. package/dist/package-DLOVE2E4.js +1132 -0
  156. package/dist/package-DLOVE2E4.js.map +1 -0
  157. package/dist/pi-CIU2QHT4.js +208 -0
  158. package/dist/pi-CIU2QHT4.js.map +1 -0
  159. package/dist/pi-UFX2OYFV.js +139 -0
  160. package/dist/pi-UFX2OYFV.js.map +1 -0
  161. package/dist/qwen-code-5POCNU6P.js +573 -0
  162. package/dist/qwen-code-5POCNU6P.js.map +1 -0
  163. package/dist/qwen-code-W7NUCDEN.js +134 -0
  164. package/dist/qwen-code-W7NUCDEN.js.map +1 -0
  165. package/dist/roo-code-IOIUXRDE.js +242 -0
  166. package/dist/roo-code-IOIUXRDE.js.map +1 -0
  167. package/dist/roo-code-ZLKH4A7N.js +229 -0
  168. package/dist/roo-code-ZLKH4A7N.js.map +1 -0
  169. package/dist/runtime/index.d.ts +157 -0
  170. package/dist/runtime/index.js +20 -0
  171. package/dist/runtime/index.js.map +1 -0
  172. package/dist/serve-HVWWY4B6.js +65 -0
  173. package/dist/serve-HVWWY4B6.js.map +1 -0
  174. package/dist/status-FE7TECZD.js +128 -0
  175. package/dist/status-FE7TECZD.js.map +1 -0
  176. package/dist/synthetic-UKR6SMGS.js +178 -0
  177. package/dist/synthetic-UKR6SMGS.js.map +1 -0
  178. package/dist/telemetry-36GESKQQ.js +349 -0
  179. package/dist/telemetry-36GESKQQ.js.map +1 -0
  180. package/dist/trae-JSNDZFKL.js +204 -0
  181. package/dist/trae-JSNDZFKL.js.map +1 -0
  182. package/dist/trae-PA26DPBW.js +157 -0
  183. package/dist/trae-PA26DPBW.js.map +1 -0
  184. package/dist/types-CYUB1OTr.d.ts +427 -0
  185. package/dist/uninstall-IGCKNOSF.js +72 -0
  186. package/dist/uninstall-IGCKNOSF.js.map +1 -0
  187. package/dist/upgrade-JV2VK242.js +104 -0
  188. package/dist/upgrade-JV2VK242.js.map +1 -0
  189. package/dist/usage-6ER6EMEY.js +324 -0
  190. package/dist/usage-6ER6EMEY.js.map +1 -0
  191. package/dist/usage-event-JKBI6XCF.js +48 -0
  192. package/dist/usage-event-JKBI6XCF.js.map +1 -0
  193. package/dist/vscode-copilot-CK3NO54M.js +715 -0
  194. package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
  195. package/dist/warp-JNQA7PBU.js +126 -0
  196. package/dist/warp-JNQA7PBU.js.map +1 -0
  197. package/dist/warp-QNVDXOG4.js +199 -0
  198. package/dist/warp-QNVDXOG4.js.map +1 -0
  199. package/dist/zed-NNNX5MNT.js +272 -0
  200. package/dist/zed-NNNX5MNT.js.map +1 -0
  201. package/dist/zed-VRJKWYUR.js +209 -0
  202. package/dist/zed-VRJKWYUR.js.map +1 -0
  203. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/status.ts"],"sourcesContent":["/**\n * cli/commands/status — a light, glanceable install-state summary.\n *\n * There is NO MCP standard for \"which connectors are installed into which host\n * configs on this machine\" — distribution/wire standards (server.json, MCPB,\n * the lifecycle) cover publishing + the protocol, not local install state. So\n * `status` is purely agent-connector infrastructure, and we design it for the\n * everyday question it answers in one line per platform:\n *\n * \"which of my connectors are present on which hosts?\"\n *\n * It REUSES detect (host enumeration) + the adapters' own config-path resolvers\n * (a read-only presence check) — NO new adapter SPI methods. Unlike `doctor`\n * (deep health checks, non-zero exit on failure) and `doctor --probe` (a live\n * protocol probe that FAILs on a dead server), `status` ALWAYS exits 0: it\n * describes, it never gates. A connector being absent is informational.\n */\n\nimport { parseArgs } from \"node:util\";\nimport { readFileSync } from \"node:fs\";\n\nimport type { InstallScope, PlatformId, ResolvedConnector } from \"../../core/types.js\";\nimport type { InstallContext } from \"../../adapters/spi.js\";\nimport { detectInstalledPlatforms } from \"../../adapters/detect.js\";\nimport { loadAdapter } from \"../../adapters/registry.js\";\nimport {\n findConnectorConfig,\n listRegisteredConnectors,\n loadConnectorFromPath,\n} from \"../../core/load-connector.js\";\nimport { dataRoot, homeBinPath } from \"../../core/paths.js\";\nimport { fail, parseScope, print } from \"../app.js\";\n\n/**\n * Resolve which connector(s) status reports on: an explicit --connector path or\n * a local config first, else every connector registered under the data-root\n * (what is actually installed). Empty array when none — status still lists the\n * detected platforms.\n */\nasync function resolveConnectors(\n connectorPath: string | undefined,\n projectDir: string,\n): Promise<ResolvedConnector[]> {\n const configPath = connectorPath ?? findConnectorConfig(projectDir);\n if (configPath) {\n try {\n const { connector } = await loadConnectorFromPath(configPath);\n return [connector];\n } catch {\n /* fall through to the registry */\n }\n }\n return listRegisteredConnectors();\n}\n\nfunction buildContext(\n connector: ResolvedConnector,\n id: PlatformId,\n scope: InstallScope,\n projectDir: string,\n): InstallContext {\n return {\n connector,\n scope: connector.platforms[id]?.scope ?? scope,\n projectDir,\n homeBinPath: homeBinPath(),\n dataRoot: dataRoot(),\n dryRun: true,\n };\n}\n\n/** Read-only: does the file at `path` mention `needle`? Missing/unreadable → false. */\nfunction fileMentions(path: string, needle: string): boolean {\n try {\n return readFileSync(path, \"utf8\").includes(needle);\n } catch {\n return false;\n }\n}\n\ninterface ConnStatus {\n id: string;\n serverPresent: boolean;\n hooksPresent: boolean;\n}\ninterface StatusRow {\n platform: PlatformId;\n scope: InstallScope;\n configPath: string;\n connectors: ConnStatus[];\n}\n\nexport async function run(argv: string[]): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n scope: { type: \"string\", default: \"user\" },\n connector: { type: \"string\" },\n project: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n },\n allowPositionals: false,\n });\n\n const projectDir = values.project ?? process.cwd();\n const scope = parseScope(values.scope);\n if (scope == null) return fail(`invalid --scope \"${values.scope}\" (use user|project)`);\n\n const connectors = await resolveConnectors(values.connector, projectDir);\n const detected = await detectInstalledPlatforms(projectDir);\n\n const rows: StatusRow[] = [];\n for (const p of detected) {\n const adapter = await loadAdapter(p.id);\n if (!adapter) continue;\n const connStatuses: ConnStatus[] = connectors.map((connector) => {\n const ctx = buildContext(connector, p.id, scope, projectDir);\n let serverPath = \"\";\n let hookPath = \"\";\n try {\n serverPath = adapter.getServerConfigPath(ctx);\n } catch {\n /* adapter has no server config path */\n }\n try {\n hookPath = adapter.getHookConfigPath(ctx);\n } catch {\n /* adapter has no hook config path */\n }\n return {\n id: connector.id,\n serverPresent: serverPath ? fileMentions(serverPath, connector.id) : false,\n hooksPresent: hookPath ? fileMentions(hookPath, connector.id) : false,\n };\n });\n rows.push({\n platform: p.id,\n scope: p.scope,\n configPath: p.configPath,\n connectors: connStatuses,\n });\n }\n\n if (values.json) {\n print(JSON.stringify(rows, null, 2));\n return 0;\n }\n\n if (rows.length === 0) {\n print(\"status: no agent platforms detected.\");\n return 0;\n }\n if (connectors.length === 0) {\n print(\"status: no connectors found (pass --connector <path> or install one).\\n\");\n }\n\n for (const r of rows) {\n print(`${r.platform} [${r.scope}] ${r.configPath}`);\n if (r.connectors.length === 0) {\n print(\" (no connectors to check)\");\n } else {\n for (const c of r.connectors) {\n const mark = c.serverPresent || c.hooksPresent ? \"[installed]\" : \"[absent ]\";\n const server = c.serverPresent ? \"server ✓\" : \"server ·\";\n const hooks = c.hooksPresent ? \"hooks ✓\" : \"hooks ·\";\n print(` ${mark} ${c.id} ${server} ${hooks}`);\n }\n }\n print(\"\");\n }\n\n // Always 0: status is descriptive (what is installed where), never a gate —\n // that contrast with doctor (non-zero on FAIL) is the reason it exists.\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAkBA,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;AAoB7B,eAAe,kBACb,eACA,YAC8B;AAC9B,QAAM,aAAa,iBAAiB,oBAAoB,UAAU;AAClE,MAAI,YAAY;AACd,QAAI;AACF,YAAM,EAAE,UAAU,IAAI,MAAM,sBAAsB,UAAU;AAC5D,aAAO,CAAC,SAAS;AAAA,IACnB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,yBAAyB;AAClC;AAEA,SAAS,aACP,WACA,IACA,OACA,YACgB;AAChB,SAAO;AAAA,IACL;AAAA,IACA,OAAO,UAAU,UAAU,EAAE,GAAG,SAAS;AAAA,IACzC;AAAA,IACA,aAAa,YAAY;AAAA,IACzB,UAAU,SAAS;AAAA,IACnB,QAAQ;AAAA,EACV;AACF;AAGA,SAAS,aAAa,MAAc,QAAyB;AAC3D,MAAI;AACF,WAAO,aAAa,MAAM,MAAM,EAAE,SAAS,MAAM;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,IAAI,MAAiC;AACzD,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS;AAAA,MACP,OAAO,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,MACzC,WAAW,EAAE,MAAM,SAAS;AAAA,MAC5B,SAAS,EAAE,MAAM,SAAS;AAAA,MAC1B,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,IAC1C;AAAA,IACA,kBAAkB;AAAA,EACpB,CAAC;AAED,QAAM,aAAa,OAAO,WAAW,QAAQ,IAAI;AACjD,QAAM,QAAQ,WAAW,OAAO,KAAK;AACrC,MAAI,SAAS,KAAM,QAAO,KAAK,oBAAoB,OAAO,KAAK,sBAAsB;AAErF,QAAM,aAAa,MAAM,kBAAkB,OAAO,WAAW,UAAU;AACvE,QAAM,WAAW,MAAM,yBAAyB,UAAU;AAE1D,QAAM,OAAoB,CAAC;AAC3B,aAAW,KAAK,UAAU;AACxB,UAAM,UAAU,MAAM,YAAY,EAAE,EAAE;AACtC,QAAI,CAAC,QAAS;AACd,UAAM,eAA6B,WAAW,IAAI,CAAC,cAAc;AAC/D,YAAM,MAAM,aAAa,WAAW,EAAE,IAAI,OAAO,UAAU;AAC3D,UAAI,aAAa;AACjB,UAAI,WAAW;AACf,UAAI;AACF,qBAAa,QAAQ,oBAAoB,GAAG;AAAA,MAC9C,QAAQ;AAAA,MAER;AACA,UAAI;AACF,mBAAW,QAAQ,kBAAkB,GAAG;AAAA,MAC1C,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,QACL,IAAI,UAAU;AAAA,QACd,eAAe,aAAa,aAAa,YAAY,UAAU,EAAE,IAAI;AAAA,QACrE,cAAc,WAAW,aAAa,UAAU,UAAU,EAAE,IAAI;AAAA,MAClE;AAAA,IACF,CAAC;AACD,SAAK,KAAK;AAAA,MACR,UAAU,EAAE;AAAA,MACZ,OAAO,EAAE;AAAA,MACT,YAAY,EAAE;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,MAAM;AACf,UAAM,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,sCAAsC;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,yEAAyE;AAAA,EACjF;AAEA,aAAW,KAAK,MAAM;AACpB,UAAM,GAAG,EAAE,QAAQ,MAAM,EAAE,KAAK,MAAM,EAAE,UAAU,EAAE;AACpD,QAAI,EAAE,WAAW,WAAW,GAAG;AAC7B,YAAM,4BAA4B;AAAA,IACpC,OAAO;AACL,iBAAW,KAAK,EAAE,YAAY;AAC5B,cAAM,OAAO,EAAE,iBAAiB,EAAE,eAAe,gBAAgB;AACjE,cAAM,SAAS,EAAE,gBAAgB,kBAAa;AAC9C,cAAM,QAAQ,EAAE,eAAe,iBAAY;AAC3C,cAAM,KAAK,IAAI,IAAI,EAAE,EAAE,KAAK,MAAM,KAAK,KAAK,EAAE;AAAA,MAChD;AAAA,IACF;AACA,UAAM,EAAE;AAAA,EACV;AAIA,SAAO;AACT;","names":[]}
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ openSqlite
4
+ } from "./chunk-YT43MUSJ.js";
5
+ import {
6
+ expandHome
7
+ } from "./chunk-JWNT5KW7.js";
8
+ import {
9
+ emptyTokens,
10
+ inferProvider
11
+ } from "./chunk-XD7MY6HB.js";
12
+
13
+ // src/usage/readers/synthetic.ts
14
+ import { homedir } from "os";
15
+ import { isAbsolute, join, resolve } from "path";
16
+ var PLATFORM_ID = "synthetic";
17
+ var DEFAULT_PROVIDER = "synthetic";
18
+ function envOverride(name) {
19
+ const raw = process.env[name];
20
+ if (raw == null || raw.trim() === "") return void 0;
21
+ const expanded = expandHome(raw.trim());
22
+ return isAbsolute(expanded) ? expanded : resolve(expanded);
23
+ }
24
+ function xdgDataHome() {
25
+ const xdg = process.env.XDG_DATA_HOME;
26
+ if (xdg && xdg.trim() !== "") return resolve(expandHome(xdg.trim()));
27
+ return join(homedir(), ".local", "share");
28
+ }
29
+ function octofriendDbPath() {
30
+ const override = envOverride("AGENT_CONNECTOR_SYNTHETIC_DIR");
31
+ if (override !== void 0) return join(override, "sqlite.db");
32
+ return join(xdgDataHome(), "octofriend", "sqlite.db");
33
+ }
34
+ function normalizeSyntheticModel(modelId) {
35
+ const lower = modelId.toLowerCase();
36
+ if (lower.startsWith("hf:")) {
37
+ const rest = lower.slice("hf:".length);
38
+ const slash = rest.indexOf("/");
39
+ if (slash >= 0) return rest.slice(slash + 1);
40
+ return rest;
41
+ }
42
+ if (lower.startsWith("accounts/")) {
43
+ const rest = lower.slice("accounts/".length);
44
+ const marker = "/models/";
45
+ const idx = rest.indexOf(marker);
46
+ if (idx >= 0) return rest.slice(idx + marker.length);
47
+ }
48
+ return lower;
49
+ }
50
+ function toInt(v) {
51
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
52
+ if (!Number.isFinite(n)) return 0;
53
+ return Math.trunc(n);
54
+ }
55
+ function toFloat(v) {
56
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
57
+ return Number.isFinite(n) ? n : 0;
58
+ }
59
+ function toStr(v) {
60
+ if (typeof v !== "string") return void 0;
61
+ return v;
62
+ }
63
+ function timestampToMs(timestamp) {
64
+ return timestamp > 1e12 ? Math.trunc(timestamp) : Math.trunc(timestamp * 1e3);
65
+ }
66
+ var TABLE_CHECK_SQL = `
67
+ SELECT count(*) AS n
68
+ FROM sqlite_master
69
+ WHERE type='table' AND name IN ('messages', 'sessions', 'token_usage')
70
+ `;
71
+ var MESSAGES_SQL = `
72
+ SELECT id, model, input_tokens, output_tokens, cache_read_tokens,
73
+ cache_write_tokens, reasoning_tokens, cost, timestamp, session_id, provider
74
+ FROM messages
75
+ WHERE input_tokens IS NOT NULL OR output_tokens IS NOT NULL
76
+ `;
77
+ var TOKEN_USAGE_SQL = `
78
+ SELECT id, model, input_tokens, output_tokens, timestamp, session_id
79
+ FROM token_usage
80
+ WHERE input_tokens > 0 OR output_tokens > 0
81
+ `;
82
+ var syntheticReader = {
83
+ platformId: PLATFORM_ID,
84
+ kind: "local",
85
+ async read({ sinceMs }) {
86
+ const dbPath = octofriendDbPath();
87
+ const db = await openSqlite(dbPath);
88
+ if (db === null) return [];
89
+ try {
90
+ const tableCheck = db.all(TABLE_CHECK_SQL);
91
+ const tableCount = tableCheck.length > 0 ? toInt(tableCheck[0]?.n) : 0;
92
+ if (tableCount <= 0) return [];
93
+ const records = [];
94
+ const messageRows = db.all(MESSAGES_SQL);
95
+ for (const raw of messageRows) {
96
+ const row = raw;
97
+ const id = toStr(row.id);
98
+ if (id === void 0) continue;
99
+ const rawModel = toStr(row.model) ?? "";
100
+ const input = Math.max(0, toInt(row.input_tokens));
101
+ const output = Math.max(0, toInt(row.output_tokens));
102
+ const cacheRead = Math.max(0, toInt(row.cache_read_tokens));
103
+ const cacheWrite = Math.max(0, toInt(row.cache_write_tokens));
104
+ const reasoning = Math.max(0, toInt(row.reasoning_tokens));
105
+ const total = toInt(row.input_tokens) + toInt(row.output_tokens) + toInt(row.cache_read_tokens) + toInt(row.cache_write_tokens) + toInt(row.reasoning_tokens);
106
+ if (total === 0) continue;
107
+ const cost = Math.max(0, toFloat(row.cost));
108
+ const ts = timestampToMs(toFloat(row.timestamp));
109
+ if (sinceMs !== void 0 && ts < sinceMs) continue;
110
+ const sessionId = toStr(row.session_id) ?? "unknown";
111
+ const rowProvider = toStr(row.provider);
112
+ const columnProvider = rowProvider !== void 0 && rowProvider !== "" ? rowProvider : DEFAULT_PROVIDER;
113
+ const modelId = normalizeSyntheticModel(rawModel);
114
+ const providerId = inferProvider(modelId) ?? columnProvider;
115
+ const tokens = emptyTokens();
116
+ tokens.input = input;
117
+ tokens.output = output;
118
+ tokens.cacheRead = cacheRead;
119
+ tokens.cacheWrite = cacheWrite;
120
+ tokens.reasoning = reasoning;
121
+ const record = {
122
+ platformId: PLATFORM_ID,
123
+ modelId,
124
+ providerId,
125
+ sessionId,
126
+ tokens,
127
+ ts,
128
+ messageCount: 1,
129
+ dedupKey: id,
130
+ // messages.id → one record per row
131
+ confidence: "host-estimated"
132
+ // spec rates this source medium
133
+ };
134
+ if (cost > 0) record.cost = cost;
135
+ records.push(record);
136
+ }
137
+ if (records.length === 0) {
138
+ const tokenRows = db.all(TOKEN_USAGE_SQL);
139
+ for (const raw of tokenRows) {
140
+ const row = raw;
141
+ const id = toStr(row.id);
142
+ if (id === void 0) continue;
143
+ const rawModel = toStr(row.model) ?? "";
144
+ const input = Math.max(0, toInt(row.input_tokens));
145
+ const output = Math.max(0, toInt(row.output_tokens));
146
+ const ts = timestampToMs(toFloat(row.timestamp));
147
+ if (sinceMs !== void 0 && ts < sinceMs) continue;
148
+ const sessionId = toStr(row.session_id) ?? "unknown";
149
+ const modelId = normalizeSyntheticModel(rawModel);
150
+ const providerId = inferProvider(modelId) ?? DEFAULT_PROVIDER;
151
+ const tokens = emptyTokens();
152
+ tokens.input = input;
153
+ tokens.output = output;
154
+ const record = {
155
+ platformId: PLATFORM_ID,
156
+ modelId,
157
+ providerId,
158
+ sessionId,
159
+ tokens,
160
+ ts,
161
+ messageCount: 1,
162
+ dedupKey: id,
163
+ confidence: "host-estimated"
164
+ };
165
+ records.push(record);
166
+ }
167
+ }
168
+ return records;
169
+ } finally {
170
+ db.close();
171
+ }
172
+ }
173
+ };
174
+ var synthetic_default = syntheticReader;
175
+ export {
176
+ synthetic_default as default
177
+ };
178
+ //# sourceMappingURL=synthetic-UKR6SMGS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/readers/synthetic.ts"],"sourcesContent":["/**\n * usage/readers/synthetic — Synthetic.new (Octofriend / HF gateway) usage reader.\n *\n * Faithful port of tokscale sessions/synthetic.rs (parse_octofriend_sqlite). The\n * Octofriend client persists usage in a single SQLite database. Per the tokscale\n * scanner (scanner.rs), the db lives at:\n * ${XDG_DATA_HOME:-~/.local/share}/octofriend/sqlite.db\n * (an AGENT_CONNECTOR_SYNTHETIC_DIR override is honored first — verbatim,\n * non-empty — mirroring the paths.ts env-override contract; `synthetic` is not\n * yet wired into paths.ts hostRoots, so the path is resolved here.)\n *\n * Octofriend historically stored only input_history (no token data), so the\n * parser is future-proofed: it first checks sqlite_master for any of the\n * token-tracking tables ('messages', 'sessions', 'token_usage'); when none\n * exist it yields []. When present it parses the `messages` table (the full\n * schema with cache + reasoning + cost), falling back to the simpler\n * `token_usage` table only when `messages` produced no rows.\n *\n * messages table (port of the primary SELECT):\n * SELECT id, model, input_tokens, output_tokens, cache_read_tokens,\n * cache_write_tokens, reasoning_tokens, cost, timestamp, session_id,\n * provider\n * FROM messages\n * WHERE input_tokens IS NOT NULL OR output_tokens IS NOT NULL\n * → input/output/cacheRead/cacheWrite/reasoning each clamped ≥ 0; a row whose\n * 5-dimension sum is 0 is skipped; cost ≥ 0; provider column (fallback\n * \"synthetic\" when missing/empty); dedup_key = id.\n *\n * token_usage fallback (only when messages yielded nothing):\n * SELECT id, model, input_tokens, output_tokens, timestamp, session_id\n * FROM token_usage\n * WHERE input_tokens > 0 OR output_tokens > 0\n * → only input/output (cache + reasoning = 0); provider hardcoded \"synthetic\";\n * cost = 0; dedup_key = id.\n *\n * Model normalization (port of normalize_synthetic_model): strips synthetic.new\n * gateway prefixes for a clean grouping id:\n * \"hf:deepseek-ai/DeepSeek-V3-0324\" → \"deepseek-v3-0324\"\n * \"accounts/fireworks/models/deepseek-v3-0324\" → \"deepseek-v3-0324\"\n * Provider is inferProvider() of the cleaned model, falling back to the row's\n * provider column (or \"synthetic\"). Timestamp is the f64 `timestamp` column:\n * values > 1e12 are already ms, smaller values are seconds (×1000). No project\n * attribution exists in the Octofriend schema. Confidence is host-reported, but\n * the spec rates this source medium (the schema is still emergent/future-proofed),\n * so rows are marked \"host-estimated\" to label that honestly.\n *\n * Fail-open: db missing/locked/unreadable / non-sqlite → openSqlite returns null\n * → []; a bad row is skipped, never thrown.\n */\n\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, resolve } from \"node:path\";\n\nimport type { UsageReader, UsageRecord } from \"../types.js\";\nimport { emptyTokens } from \"../aggregate.js\";\nimport { inferProvider } from \"../normalize.js\";\nimport { expandHome } from \"../paths.js\";\nimport { openSqlite } from \"../sqlite.js\";\n\nconst PLATFORM_ID = \"synthetic\" as const;\nconst DEFAULT_PROVIDER = \"synthetic\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Octofriend db-path resolution (port of scanner.rs synthetic_db discovery).\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Read an env override, treating empty/blank as unset (mirrors the paths.ts\n * envOverride contract: an empty override must NOT resolve to \"\"). Relative\n * paths resolve against the process CWD; a leading \"~\" is expanded.\n */\nfunction envOverride(name: string): string | undefined {\n const raw = process.env[name];\n if (raw == null || raw.trim() === \"\") return undefined;\n const expanded = expandHome(raw.trim());\n return isAbsolute(expanded) ? expanded : resolve(expanded);\n}\n\n/** $XDG_DATA_HOME (when set & non-empty) else ~/.local/share — as in paths.ts. */\nfunction xdgDataHome(): string {\n const xdg = process.env.XDG_DATA_HOME;\n if (xdg && xdg.trim() !== \"\") return resolve(expandHome(xdg.trim()));\n return join(homedir(), \".local\", \"share\");\n}\n\n/**\n * Resolve the Octofriend sqlite.db path. Honors AGENT_CONNECTOR_SYNTHETIC_DIR\n * first (treated as the octofriend directory: `<dir>/sqlite.db`), else the\n * tokscale default `${XDG_DATA_HOME:-~/.local/share}/octofriend/sqlite.db`.\n */\nfunction octofriendDbPath(): string {\n const override = envOverride(\"AGENT_CONNECTOR_SYNTHETIC_DIR\");\n if (override !== undefined) return join(override, \"sqlite.db\");\n return join(xdgDataHome(), \"octofriend\", \"sqlite.db\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Model normalization (port of normalize_synthetic_model).\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Normalize a synthetic.new model id to a standard form, lowercased:\n * \"hf:<org>/<model>\" → \"<model>\"\n * \"hf:<model>\" → \"<model>\"\n * \"accounts/<provider>/models/<model>\" → \"<model>\"\n * Any other id passes through lowercased. Mirrors the Rust strip_prefix /\n * split_once chain exactly.\n */\nfunction normalizeSyntheticModel(modelId: string): string {\n const lower = modelId.toLowerCase();\n\n // Strip \"hf:\" prefix and (optional) org name.\n if (lower.startsWith(\"hf:\")) {\n const rest = lower.slice(\"hf:\".length);\n const slash = rest.indexOf(\"/\");\n if (slash >= 0) return rest.slice(slash + 1);\n return rest;\n }\n\n // Strip \"accounts/<provider>/models/\" prefix.\n if (lower.startsWith(\"accounts/\")) {\n const rest = lower.slice(\"accounts/\".length);\n const marker = \"/models/\";\n const idx = rest.indexOf(marker);\n if (idx >= 0) return rest.slice(idx + marker.length);\n }\n\n return lower;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Cell coercion & timestamp.\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Coerce a SQLite cell to an integer (Rust get::<i64>().unwrap_or(0)), no clamp. */\nfunction toInt(v: unknown): number {\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\n if (!Number.isFinite(n)) return 0;\n return Math.trunc(n);\n}\n\n/** Coerce a SQLite cell to a float (Rust get::<f64>().unwrap_or(0.0)). */\nfunction toFloat(v: unknown): number {\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\n return Number.isFinite(n) ? n : 0;\n}\n\n/** Coerce to a non-empty trimmed string, or undefined. */\nfunction toStr(v: unknown): string | undefined {\n if (typeof v !== \"string\") return undefined;\n return v;\n}\n\n/**\n * Convert the f64 `timestamp` column to epoch ms (port of the Rust branch):\n * values > 1e12 are already milliseconds (cast); smaller values are seconds\n * (×1000). Truncating, matching the Rust `as i64`.\n */\nfunction timestampToMs(timestamp: number): number {\n return timestamp > 1e12 ? Math.trunc(timestamp) : Math.trunc(timestamp * 1000);\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Table presence check (port of the sqlite_master count).\n// ─────────────────────────────────────────────────────────────────────────\n\nconst TABLE_CHECK_SQL = `\n SELECT count(*) AS n\n FROM sqlite_master\n WHERE type='table' AND name IN ('messages', 'sessions', 'token_usage')\n`;\n\n/** Row shape for the primary `messages` SELECT (all cells optional / unknown). */\ninterface MessagesRow {\n id?: unknown;\n model?: unknown;\n input_tokens?: unknown;\n output_tokens?: unknown;\n cache_read_tokens?: unknown;\n cache_write_tokens?: unknown;\n reasoning_tokens?: unknown;\n cost?: unknown;\n timestamp?: unknown;\n session_id?: unknown;\n provider?: unknown;\n}\n\nconst MESSAGES_SQL = `\n SELECT id, model, input_tokens, output_tokens, cache_read_tokens,\n cache_write_tokens, reasoning_tokens, cost, timestamp, session_id, provider\n FROM messages\n WHERE input_tokens IS NOT NULL OR output_tokens IS NOT NULL\n`;\n\n/** Row shape for the `token_usage` fallback SELECT. */\ninterface TokenUsageRow {\n id?: unknown;\n model?: unknown;\n input_tokens?: unknown;\n output_tokens?: unknown;\n timestamp?: unknown;\n session_id?: unknown;\n}\n\nconst TOKEN_USAGE_SQL = `\n SELECT id, model, input_tokens, output_tokens, timestamp, session_id\n FROM token_usage\n WHERE input_tokens > 0 OR output_tokens > 0\n`;\n\n// ─────────────────────────────────────────────────────────────────────────\n// Reader\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The Synthetic.new (Octofriend) usage reader singleton. */\nconst syntheticReader: UsageReader = {\n platformId: PLATFORM_ID,\n kind: \"local\",\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\n const dbPath = octofriendDbPath();\n\n const db = await openSqlite(dbPath);\n if (db === null) return []; // missing / locked / unreadable / non-sqlite → fail-open\n\n try {\n // Future-proofing: only parse when a token-tracking table exists.\n const tableCheck = db.all(TABLE_CHECK_SQL);\n const tableCount = tableCheck.length > 0 ? toInt(tableCheck[0]?.n) : 0;\n if (tableCount <= 0) return [];\n\n const records: UsageRecord[] = [];\n\n // Primary schema: the `messages` table (full token breakdown + cost).\n const messageRows = db.all(MESSAGES_SQL); // bad SQL / schema mismatch → []\n for (const raw of messageRows) {\n const row = raw as MessagesRow;\n\n const id = toStr(row.id);\n if (id === undefined) continue; // Rust requires id (row.get(0)? bails otherwise)\n\n const rawModel = toStr(row.model) ?? \"\";\n const input = Math.max(0, toInt(row.input_tokens));\n const output = Math.max(0, toInt(row.output_tokens));\n const cacheRead = Math.max(0, toInt(row.cache_read_tokens));\n const cacheWrite = Math.max(0, toInt(row.cache_write_tokens));\n const reasoning = Math.max(0, toInt(row.reasoning_tokens));\n\n // Skip zero-token rows (Rust: total == 0 → continue), using raw (signed) sums.\n const total =\n toInt(row.input_tokens) +\n toInt(row.output_tokens) +\n toInt(row.cache_read_tokens) +\n toInt(row.cache_write_tokens) +\n toInt(row.reasoning_tokens);\n if (total === 0) continue;\n\n const cost = Math.max(0, toFloat(row.cost));\n const ts = timestampToMs(toFloat(row.timestamp));\n if (sinceMs !== undefined && ts < sinceMs) continue;\n\n const sessionId = toStr(row.session_id) ?? \"unknown\";\n // Provider: row column when present, else \"synthetic\" (Rust unwrap_or).\n const rowProvider = toStr(row.provider);\n const columnProvider =\n rowProvider !== undefined && rowProvider !== \"\" ? rowProvider : DEFAULT_PROVIDER;\n\n const modelId = normalizeSyntheticModel(rawModel);\n const providerId = inferProvider(modelId) ?? columnProvider;\n\n const tokens = emptyTokens();\n tokens.input = input;\n tokens.output = output;\n tokens.cacheRead = cacheRead;\n tokens.cacheWrite = cacheWrite;\n tokens.reasoning = reasoning;\n\n const record: UsageRecord = {\n platformId: PLATFORM_ID,\n modelId,\n providerId,\n sessionId,\n tokens,\n ts,\n messageCount: 1,\n dedupKey: id, // messages.id → one record per row\n confidence: \"host-estimated\", // spec rates this source medium\n };\n if (cost > 0) record.cost = cost;\n records.push(record);\n }\n\n // Fallback schema: only when `messages` produced nothing.\n if (records.length === 0) {\n const tokenRows = db.all(TOKEN_USAGE_SQL);\n for (const raw of tokenRows) {\n const row = raw as TokenUsageRow;\n\n const id = toStr(row.id);\n if (id === undefined) continue;\n\n const rawModel = toStr(row.model) ?? \"\";\n const input = Math.max(0, toInt(row.input_tokens));\n const output = Math.max(0, toInt(row.output_tokens));\n\n const ts = timestampToMs(toFloat(row.timestamp));\n if (sinceMs !== undefined && ts < sinceMs) continue;\n\n const sessionId = toStr(row.session_id) ?? \"unknown\";\n const modelId = normalizeSyntheticModel(rawModel);\n const providerId = inferProvider(modelId) ?? DEFAULT_PROVIDER;\n\n const tokens = emptyTokens();\n tokens.input = input;\n tokens.output = output;\n // cacheRead / cacheWrite / reasoning stay 0 (token_usage has no such columns).\n\n const record: UsageRecord = {\n platformId: PLATFORM_ID,\n modelId,\n providerId,\n sessionId,\n tokens,\n ts,\n messageCount: 1,\n dedupKey: id,\n confidence: \"host-estimated\",\n };\n records.push(record);\n }\n }\n\n return records;\n } finally {\n db.close();\n }\n },\n};\n\nexport default syntheticReader;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ SCOPE_FILTER_VALUES,
4
+ formatMcpLeaderboard,
5
+ formatSurfaceLeaderboard,
6
+ formatToolLeaderboard,
7
+ isScopeFilter,
8
+ mcpLeaderboard,
9
+ surfaceLeaderboard,
10
+ toolLeaderboard
11
+ } from "./chunk-JNM3TPLX.js";
12
+ import {
13
+ fail,
14
+ print
15
+ } from "./chunk-54EMKTTX.js";
16
+ import {
17
+ openStore
18
+ } from "./chunk-BCHWFTV4.js";
19
+ import {
20
+ connectorFromMeta,
21
+ listRegisteredConnectors,
22
+ loadRegisteredConnector,
23
+ readRegisteredMeta
24
+ } from "./chunk-LTT56GI3.js";
25
+ import "./chunk-XNAM7F35.js";
26
+ import "./chunk-TRQKHP7D.js";
27
+
28
+ // src/cli/commands/telemetry.ts
29
+ import { writeFileSync } from "fs";
30
+ import { parseArgs } from "util";
31
+
32
+ // src/telemetry/report.ts
33
+ var KEY_HEADER = {
34
+ tool: "TOOL",
35
+ session: "SESSION",
36
+ project: "PROJECT"
37
+ };
38
+ var ESTIMATE_SOURCES = /* @__PURE__ */ new Set([
39
+ "heuristic",
40
+ "tokenizer-approx",
41
+ "tokenizer-calibrated"
42
+ ]);
43
+ function fmtInt(n) {
44
+ return n.toLocaleString("en-US");
45
+ }
46
+ function padRight(s, width) {
47
+ return s.length >= width ? s : s + " ".repeat(width - s.length);
48
+ }
49
+ function padLeft(s, width) {
50
+ return s.length >= width ? s : " ".repeat(width - s.length) + s;
51
+ }
52
+ function formatReport(rows, by) {
53
+ const keyHeader = KEY_HEADER[by];
54
+ const headers = [keyHeader, "CALLS", "IN", "OUT", "TOTAL", "CONFIDENCE"];
55
+ const sorted = [...rows].sort((a, b) => b.totalTokens - a.totalTokens);
56
+ const dataRows = sorted.map((r) => [
57
+ r.key,
58
+ fmtInt(r.calls),
59
+ fmtInt(r.inputTokens),
60
+ fmtInt(r.outputTokens),
61
+ fmtInt(r.totalTokens),
62
+ r.confidence
63
+ ]);
64
+ const totals = sorted.reduce(
65
+ (acc, r) => {
66
+ acc.calls += r.calls;
67
+ acc.inputTokens += r.inputTokens;
68
+ acc.outputTokens += r.outputTokens;
69
+ acc.totalTokens += r.totalTokens;
70
+ return acc;
71
+ },
72
+ { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 }
73
+ );
74
+ const totalRow = [
75
+ "TOTAL",
76
+ fmtInt(totals.calls),
77
+ fmtInt(totals.inputTokens),
78
+ fmtInt(totals.outputTokens),
79
+ fmtInt(totals.totalTokens),
80
+ ""
81
+ ];
82
+ const widths = headers.map((h, col) => {
83
+ let w = h.length;
84
+ for (const row of dataRows) w = Math.max(w, (row[col] ?? "").length);
85
+ w = Math.max(w, (totalRow[col] ?? "").length);
86
+ return w;
87
+ });
88
+ const renderRow = (cells) => cells.map(
89
+ (c, col) => col === 0 ? padRight(c, widths[col] ?? 0) : padLeft(c, widths[col] ?? 0)
90
+ ).join(" ").trimEnd();
91
+ const ruleWidth = widths.reduce((a, b) => a + b, 0) + 2 * (widths.length - 1);
92
+ const rule = "-".repeat(ruleWidth);
93
+ const lines = [];
94
+ lines.push(renderRow(headers));
95
+ lines.push(rule);
96
+ if (dataRows.length === 0) {
97
+ lines.push("(no telemetry recorded)");
98
+ } else {
99
+ for (const row of dataRows) lines.push(renderRow(row));
100
+ }
101
+ lines.push(rule);
102
+ lines.push(renderRow(totalRow));
103
+ const hasEstimate = sorted.some((r) => ESTIMATE_SOURCES.has(r.confidence));
104
+ if (hasEstimate) {
105
+ lines.push("");
106
+ lines.push(
107
+ "note: heuristic and tokenizer-approx token counts are estimates, not exact host-reported usage."
108
+ );
109
+ if (sorted.some((r) => r.confidence === "tokenizer-calibrated")) {
110
+ lines.push(
111
+ "note: tokenizer-calibrated = local approx adjusted by a sampled Anthropic count_tokens factor (opt-in; content sampled off-box only when AGENT_CONNECTOR_CALIBRATE=anthropic)."
112
+ );
113
+ }
114
+ }
115
+ return lines.join("\n");
116
+ }
117
+ var CSV_COLUMNS = [
118
+ "id",
119
+ "ts",
120
+ "connectorId",
121
+ "toolName",
122
+ "scope",
123
+ "hostPlatform",
124
+ "sessionId",
125
+ "projectKey",
126
+ "projectDir",
127
+ "inputTokens",
128
+ "outputTokens",
129
+ "confidenceSource",
130
+ "isError",
131
+ // installScope / launchMethod mirror the JSON export so the scope dimension is
132
+ // not silently dropped from CSV; absent on a record → an empty cell.
133
+ "installScope",
134
+ "launchMethod"
135
+ ];
136
+ function csvCell(value) {
137
+ if (value === void 0 || value === null) return "";
138
+ const s = String(value);
139
+ if (/[",\n\r]/.test(s)) {
140
+ return `"${s.replace(/"/g, '""')}"`;
141
+ }
142
+ return s;
143
+ }
144
+ function toCSV(records) {
145
+ const lines = [];
146
+ lines.push(CSV_COLUMNS.join(","));
147
+ for (const record of records) {
148
+ lines.push(CSV_COLUMNS.map((col) => csvCell(record[col])).join(","));
149
+ }
150
+ return lines.join("\r\n");
151
+ }
152
+ function toJSONExport(records) {
153
+ return JSON.stringify(records, null, 2);
154
+ }
155
+ function summarize(store, opts) {
156
+ const by = opts?.by ?? "tool";
157
+ const filter = opts?.filter ?? {};
158
+ const rows = store.rollup(by, filter);
159
+ return { rows, text: formatReport(rows, by) };
160
+ }
161
+
162
+ // src/cli/commands/telemetry.ts
163
+ function parseSince(since) {
164
+ if (since == null || since.trim() === "") return void 0;
165
+ const m = since.trim().match(/^(\d+)\s*([smhd])$/i);
166
+ if (!m || !m[1] || !m[2]) return null;
167
+ const n = Number(m[1]);
168
+ const unitMs = {
169
+ s: 1e3,
170
+ m: 6e4,
171
+ h: 36e5,
172
+ d: 864e5
173
+ };
174
+ const factor = unitMs[m[2].toLowerCase()];
175
+ if (factor == null) return null;
176
+ return Date.now() - n * factor;
177
+ }
178
+ function buildFilter(since, connectorId) {
179
+ const sinceMs = parseSince(since);
180
+ if (sinceMs === null) return null;
181
+ const filter = {};
182
+ if (sinceMs !== void 0) filter.sinceMs = sinceMs;
183
+ if (connectorId && connectorId.trim() !== "") filter.connectorId = connectorId;
184
+ return filter;
185
+ }
186
+ function runReport(argv) {
187
+ const { values } = parseArgs({
188
+ args: argv,
189
+ options: {
190
+ by: { type: "string", default: "tool" },
191
+ since: { type: "string" },
192
+ connector: { type: "string" },
193
+ json: { type: "boolean", default: false }
194
+ },
195
+ allowPositionals: false
196
+ });
197
+ const by = values.by;
198
+ if (by !== "tool" && by !== "session" && by !== "project") {
199
+ return fail(`invalid --by "${by}" (use tool|session|project)`);
200
+ }
201
+ const dimension = by;
202
+ const filter = buildFilter(values.since, values.connector);
203
+ if (filter === null) {
204
+ return fail(`invalid --since "${values.since}" (use forms like 30s, 15m, 24h, 7d)`);
205
+ }
206
+ const store = openStore({});
207
+ try {
208
+ const { rows, text } = summarize(store, { by: dimension, filter });
209
+ if (values.json) {
210
+ print(JSON.stringify(rows, null, 2));
211
+ } else {
212
+ print(text);
213
+ }
214
+ } finally {
215
+ store.close();
216
+ }
217
+ return 0;
218
+ }
219
+ function runExport(argv) {
220
+ const { values } = parseArgs({
221
+ args: argv,
222
+ options: {
223
+ format: { type: "string", default: "json" },
224
+ out: { type: "string" },
225
+ since: { type: "string" },
226
+ connector: { type: "string" }
227
+ },
228
+ allowPositionals: false
229
+ });
230
+ const format = values.format;
231
+ if (format !== "csv" && format !== "json") {
232
+ return fail(`invalid --format "${format}" (use csv|json)`);
233
+ }
234
+ const filter = buildFilter(values.since, values.connector);
235
+ if (filter === null) {
236
+ return fail(`invalid --since "${values.since}" (use forms like 30s, 15m, 24h, 7d)`);
237
+ }
238
+ const store = openStore({});
239
+ let serialized;
240
+ try {
241
+ const records = store.query(filter);
242
+ serialized = format === "csv" ? toCSV(records) : toJSONExport(records);
243
+ } finally {
244
+ store.close();
245
+ }
246
+ if (values.out && values.out.trim() !== "") {
247
+ writeFileSync(values.out, serialized, "utf8");
248
+ print(`wrote ${format.toUpperCase()} export \u2192 ${values.out}`);
249
+ } else {
250
+ print(serialized);
251
+ }
252
+ return 0;
253
+ }
254
+ async function runLeaderboard(argv) {
255
+ const { values } = parseArgs({
256
+ args: argv,
257
+ options: {
258
+ by: { type: "string", default: "mcp" },
259
+ since: { type: "string" },
260
+ connector: { type: "string" },
261
+ scope: { type: "string" },
262
+ json: { type: "boolean", default: false }
263
+ },
264
+ allowPositionals: false
265
+ });
266
+ const by = values.by;
267
+ if (by !== "mcp" && by !== "tool" && by !== "surface") {
268
+ return fail(`invalid --by "${by}" (use mcp|tool|surface)`);
269
+ }
270
+ const sinceMs = parseSince(values.since);
271
+ if (sinceMs === null) {
272
+ return fail(`invalid --since "${values.since}" (use forms like 30s, 15m, 24h, 7d)`);
273
+ }
274
+ let scope;
275
+ if (values.scope !== void 0 && values.scope.trim() !== "") {
276
+ const s = values.scope.trim();
277
+ if (!isScopeFilter(s)) {
278
+ return fail(`invalid --scope "${s}" (use ${SCOPE_FILTER_VALUES.join("|")})`);
279
+ }
280
+ scope = s;
281
+ }
282
+ const opts = {};
283
+ if (sinceMs !== void 0) opts.sinceMs = sinceMs;
284
+ if (values.connector && values.connector.trim() !== "") {
285
+ opts.connectorId = values.connector.trim();
286
+ }
287
+ if (scope !== void 0) opts.scope = scope;
288
+ if (by === "tool") {
289
+ const rows = toolLeaderboard(opts);
290
+ print(values.json ? JSON.stringify(rows, null, 2) : formatToolLeaderboard(rows));
291
+ } else if (by === "surface") {
292
+ const connectorId = values.connector?.trim();
293
+ const surfaceOpts = {
294
+ ...opts,
295
+ connectors: await gatherConnectors(connectorId)
296
+ };
297
+ const rows = surfaceLeaderboard(surfaceOpts);
298
+ print(values.json ? JSON.stringify(rows, null, 2) : formatSurfaceLeaderboard(rows));
299
+ } else {
300
+ const rows = mcpLeaderboard(opts);
301
+ print(values.json ? JSON.stringify(rows, null, 2) : formatMcpLeaderboard(rows));
302
+ }
303
+ return 0;
304
+ }
305
+ async function gatherConnectors(connectorId) {
306
+ if (connectorId !== void 0 && connectorId !== "") {
307
+ try {
308
+ return [await loadRegisteredConnector(connectorId)];
309
+ } catch {
310
+ const meta = readRegisteredMetaSafe(connectorId);
311
+ return meta ? [connectorFromMeta(meta)] : [];
312
+ }
313
+ }
314
+ return listRegisteredConnectors();
315
+ }
316
+ function readRegisteredMetaSafe(id) {
317
+ try {
318
+ return readRegisteredMeta(id);
319
+ } catch {
320
+ return null;
321
+ }
322
+ }
323
+ async function run(argv) {
324
+ const sub = argv[0];
325
+ const rest = argv.slice(1);
326
+ switch (sub) {
327
+ case "report":
328
+ return runReport(rest);
329
+ case "export":
330
+ return runExport(rest);
331
+ case "leaderboard":
332
+ return await runLeaderboard(rest);
333
+ case void 0:
334
+ case "--help":
335
+ case "-h":
336
+ print("usage: agent-connector telemetry <report|export|leaderboard> [flags]");
337
+ print(" report --by tool|session|project --since 7d --connector <id> --json");
338
+ print(" export --format csv|json --out <file> --since 7d --connector <id>");
339
+ print(" leaderboard --by mcp|tool|surface --since 7d --connector <id> --scope <slice> --json");
340
+ return sub === void 0 ? 1 : 0;
341
+ default:
342
+ return fail(`unknown telemetry subcommand "${sub}" (use report|export|leaderboard)`);
343
+ }
344
+ }
345
+ export {
346
+ parseSince,
347
+ run
348
+ };
349
+ //# sourceMappingURL=telemetry-36GESKQQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/telemetry.ts","../src/telemetry/report.ts"],"sourcesContent":["/**\n * cli/commands/telemetry — inspect the local per-tool token telemetry store.\n *\n * Subcommands:\n * report --by tool|session|project (default tool) --since 7d|24h|… --connector <id> [--json]\n * → summarize(openStore({}), …) + formatReport (the aligned table).\n * export --format csv|json (default json) --out <file> [--since/--connector …]\n * → toCSV / toJSONExport of the raw records.\n *\n * Telemetry rows are aggregate counts only (never raw arguments/results).\n */\n\nimport { writeFileSync } from \"node:fs\";\nimport { parseArgs } from \"node:util\";\n\nimport type { QueryFilter } from \"../../telemetry/types.js\";\nimport type { RollupDimension } from \"../../telemetry/report.js\";\nimport { openStore } from \"../../telemetry/store.js\";\nimport { summarize, toCSV, toJSONExport } from \"../../telemetry/report.js\";\nimport {\n formatMcpLeaderboard,\n formatSurfaceLeaderboard,\n formatToolLeaderboard,\n isScopeFilter,\n mcpLeaderboard,\n SCOPE_FILTER_VALUES,\n surfaceLeaderboard,\n toolLeaderboard,\n type LeaderboardOptions,\n type ScopeFilter,\n type SurfaceLeaderboardOptions,\n} from \"../../telemetry/leaderboard.js\";\nimport {\n connectorFromMeta,\n listRegisteredConnectors,\n loadRegisteredConnector,\n readRegisteredMeta,\n} from \"../../core/load-connector.js\";\nimport type { ResolvedConnector } from \"../../core/types.js\";\nimport { fail, print } from \"../app.js\";\n\n/**\n * Parse a relative duration (`Ns`/`Nm`/`Nh`/`Nd`) to a lower-bound epoch ms.\n * Returns undefined for an empty input and null for a malformed one.\n */\nexport function parseSince(since: string | undefined): number | null | undefined {\n if (since == null || since.trim() === \"\") return undefined;\n const m = since.trim().match(/^(\\d+)\\s*([smhd])$/i);\n if (!m || !m[1] || !m[2]) return null;\n const n = Number(m[1]);\n const unitMs: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n };\n const factor = unitMs[m[2].toLowerCase()];\n if (factor == null) return null;\n return Date.now() - n * factor;\n}\n\n/** Build a QueryFilter from the shared --since / --connector flags. */\nfunction buildFilter(\n since: string | undefined,\n connectorId: string | undefined,\n): QueryFilter | null {\n const sinceMs = parseSince(since);\n if (sinceMs === null) return null;\n const filter: QueryFilter = {};\n if (sinceMs !== undefined) filter.sinceMs = sinceMs;\n if (connectorId && connectorId.trim() !== \"\") filter.connectorId = connectorId;\n return filter;\n}\n\nfunction runReport(argv: string[]): number {\n const { values } = parseArgs({\n args: argv,\n options: {\n by: { type: \"string\", default: \"tool\" },\n since: { type: \"string\" },\n connector: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n },\n allowPositionals: false,\n });\n\n const by = values.by;\n if (by !== \"tool\" && by !== \"session\" && by !== \"project\") {\n return fail(`invalid --by \"${by}\" (use tool|session|project)`);\n }\n const dimension: RollupDimension = by;\n\n const filter = buildFilter(values.since, values.connector);\n if (filter === null) {\n return fail(`invalid --since \"${values.since}\" (use forms like 30s, 15m, 24h, 7d)`);\n }\n\n const store = openStore({});\n try {\n const { rows, text } = summarize(store, { by: dimension, filter });\n if (values.json) {\n print(JSON.stringify(rows, null, 2));\n } else {\n print(text);\n }\n } finally {\n store.close();\n }\n return 0;\n}\n\nfunction runExport(argv: string[]): number {\n const { values } = parseArgs({\n args: argv,\n options: {\n format: { type: \"string\", default: \"json\" },\n out: { type: \"string\" },\n since: { type: \"string\" },\n connector: { type: \"string\" },\n },\n allowPositionals: false,\n });\n\n const format = values.format;\n if (format !== \"csv\" && format !== \"json\") {\n return fail(`invalid --format \"${format}\" (use csv|json)`);\n }\n\n const filter = buildFilter(values.since, values.connector);\n if (filter === null) {\n return fail(`invalid --since \"${values.since}\" (use forms like 30s, 15m, 24h, 7d)`);\n }\n\n const store = openStore({});\n let serialized: string;\n try {\n const records = store.query(filter);\n serialized = format === \"csv\" ? toCSV(records) : toJSONExport(records);\n } finally {\n store.close();\n }\n\n if (values.out && values.out.trim() !== \"\") {\n writeFileSync(values.out, serialized, \"utf8\");\n print(`wrote ${format.toUpperCase()} export → ${values.out}`);\n } else {\n print(serialized);\n }\n return 0;\n}\n\n/**\n * `telemetry leaderboard` — rank the per-MCP telemetry by connector (--by mcp,\n * the default and signature \"which MCP server costs the most\" metric), by tool\n * (--by tool), or by developer-axis surface (--by surface — the FIVE surfaces:\n * server + hook runtime rows plus static command/skill/subagent footprints).\n * Honors --since / --connector and the scope slice (--scope).\n */\nasync function runLeaderboard(argv: string[]): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n by: { type: \"string\", default: \"mcp\" },\n since: { type: \"string\" },\n connector: { type: \"string\" },\n scope: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n },\n allowPositionals: false,\n });\n\n const by = values.by;\n if (by !== \"mcp\" && by !== \"tool\" && by !== \"surface\") {\n return fail(`invalid --by \"${by}\" (use mcp|tool|surface)`);\n }\n\n const sinceMs = parseSince(values.since);\n if (sinceMs === null) {\n return fail(`invalid --since \"${values.since}\" (use forms like 30s, 15m, 24h, 7d)`);\n }\n\n let scope: ScopeFilter | undefined;\n if (values.scope !== undefined && values.scope.trim() !== \"\") {\n const s = values.scope.trim();\n if (!isScopeFilter(s)) {\n return fail(`invalid --scope \"${s}\" (use ${SCOPE_FILTER_VALUES.join(\"|\")})`);\n }\n scope = s;\n }\n\n const opts: LeaderboardOptions = {};\n if (sinceMs !== undefined) opts.sinceMs = sinceMs;\n if (values.connector && values.connector.trim() !== \"\") {\n opts.connectorId = values.connector.trim();\n }\n if (scope !== undefined) opts.scope = scope;\n\n if (by === \"tool\") {\n const rows = toolLeaderboard(opts);\n print(values.json ? JSON.stringify(rows, null, 2) : formatToolLeaderboard(rows));\n } else if (by === \"surface\") {\n // The per-surface view folds the STATIC command/skill/subagent footprints of\n // the registered connector(s) into the runtime server/hook rows.\n const connectorId = values.connector?.trim();\n const surfaceOpts: SurfaceLeaderboardOptions = {\n ...opts,\n connectors: await gatherConnectors(connectorId),\n };\n const rows = surfaceLeaderboard(surfaceOpts);\n print(values.json ? JSON.stringify(rows, null, 2) : formatSurfaceLeaderboard(rows));\n } else {\n const rows = mcpLeaderboard(opts);\n print(values.json ? JSON.stringify(rows, null, 2) : formatMcpLeaderboard(rows));\n }\n return 0;\n}\n\n/**\n * Gather the registered connector(s) whose STATIC command/skill/subagent\n * footprints feed the per-surface view. When `connectorId` is given, prefer the\n * LIVE module (freshest content) and fall back to the persisted meta record;\n * otherwise enumerate every registered connector from its meta. Best-effort: a\n * connector that cannot be loaded is skipped (the surface view still renders the\n * runtime rows it can read from the store).\n */\nasync function gatherConnectors(\n connectorId: string | undefined,\n): Promise<ResolvedConnector[]> {\n if (connectorId !== undefined && connectorId !== \"\") {\n try {\n return [await loadRegisteredConnector(connectorId)];\n } catch {\n const meta = readRegisteredMetaSafe(connectorId);\n return meta ? [connectorFromMeta(meta)] : [];\n }\n }\n return listRegisteredConnectors();\n}\n\n/** readRegisteredMeta wrapped so a missing/corrupt record never throws here. */\nfunction readRegisteredMetaSafe(id: string): ReturnType<typeof readRegisteredMeta> {\n try {\n return readRegisteredMeta(id);\n } catch {\n return null;\n }\n}\n\nexport async function run(argv: string[]): Promise<number> {\n const sub = argv[0];\n const rest = argv.slice(1);\n\n switch (sub) {\n case \"report\":\n return runReport(rest);\n case \"export\":\n return runExport(rest);\n case \"leaderboard\":\n return await runLeaderboard(rest);\n case undefined:\n case \"--help\":\n case \"-h\":\n print(\"usage: agent-connector telemetry <report|export|leaderboard> [flags]\");\n print(\" report --by tool|session|project --since 7d --connector <id> --json\");\n print(\" export --format csv|json --out <file> --since 7d --connector <id>\");\n print(\" leaderboard --by mcp|tool|surface --since 7d --connector <id> --scope <slice> --json\");\n return sub === undefined ? 1 : 0;\n default:\n return fail(`unknown telemetry subcommand \"${sub}\" (use report|export|leaderboard)`);\n }\n}\n","/**\n * telemetry/report — CLI-facing formatting for telemetry rollups and exports.\n *\n * Pure functions: take store output (rollup rows / raw records) and render text,\n * CSV, or JSON. No IO except {@link summarize}, which reads through a passed-in\n * {@link TelemetryStore}. Confidence is surfaced verbatim so heuristic/approx\n * numbers are never mistaken for exact host-reported usage.\n */\n\nimport type { TelemetryStore } from \"./types.js\";\nimport type {\n ConfidenceSource,\n QueryFilter,\n RollupRow,\n ToolEventRecord,\n} from \"./types.js\";\n\n/** Grouping dimension, mirrored from {@link TelemetryStore.rollup}. */\nexport type RollupDimension = \"tool\" | \"session\" | \"project\";\n\n/** Human label for the leading KEY column, per grouping dimension. */\nconst KEY_HEADER: Record<RollupDimension, string> = {\n tool: \"TOOL\",\n session: \"SESSION\",\n project: \"PROJECT\",\n};\n\n/**\n * Sources whose counts are estimates (vs. real / host-reported numbers).\n * `tokenizer-calibrated` is still an estimate — an approximation nudged toward\n * truth by a sampled Anthropic count_tokens factor — so it belongs here too.\n */\nconst ESTIMATE_SOURCES: ReadonlySet<ConfidenceSource> = new Set<ConfidenceSource>([\n \"heuristic\",\n \"tokenizer-approx\",\n \"tokenizer-calibrated\",\n]);\n\n/** Compact, fixed-width integer formatting with thousands separators. */\nfunction fmtInt(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\n/** Pad a cell to a column width (right-pad text, used for left-aligned cols). */\nfunction padRight(s: string, width: number): string {\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\n}\n\n/** Pad a cell to a column width (left-pad, used for right-aligned numbers). */\nfunction padLeft(s: string, width: number): string {\n return s.length >= width ? s : \" \".repeat(width - s.length) + s;\n}\n\n/**\n * Render an aligned text table of rollup rows, sorted by total tokens desc, with\n * a TOTAL footer and a short legend noting which confidence labels are estimates.\n */\nexport function formatReport(rows: RollupRow[], by: RollupDimension): string {\n const keyHeader = KEY_HEADER[by];\n const headers = [keyHeader, \"CALLS\", \"IN\", \"OUT\", \"TOTAL\", \"CONFIDENCE\"];\n\n const sorted = [...rows].sort((a, b) => b.totalTokens - a.totalTokens);\n\n // Build the data matrix (string cells) first so we can measure widths.\n const dataRows = sorted.map((r) => [\n r.key,\n fmtInt(r.calls),\n fmtInt(r.inputTokens),\n fmtInt(r.outputTokens),\n fmtInt(r.totalTokens),\n r.confidence,\n ]);\n\n // Totals footer. Confidence is the worst (least-confident) across all rows.\n const totals = sorted.reduce(\n (acc, r) => {\n acc.calls += r.calls;\n acc.inputTokens += r.inputTokens;\n acc.outputTokens += r.outputTokens;\n acc.totalTokens += r.totalTokens;\n return acc;\n },\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n );\n const totalRow = [\n \"TOTAL\",\n fmtInt(totals.calls),\n fmtInt(totals.inputTokens),\n fmtInt(totals.outputTokens),\n fmtInt(totals.totalTokens),\n \"\",\n ];\n\n // Column widths: max over header, every data cell, and the totals row.\n const widths = headers.map((h, col) => {\n let w = h.length;\n for (const row of dataRows) w = Math.max(w, (row[col] ?? \"\").length);\n w = Math.max(w, (totalRow[col] ?? \"\").length);\n return w;\n });\n\n // Column 0 (KEY) is left-aligned; numeric/label columns are right-aligned.\n const renderRow = (cells: string[]): string =>\n cells\n .map((c, col) =>\n col === 0\n ? padRight(c, widths[col] ?? 0)\n : padLeft(c, widths[col] ?? 0),\n )\n .join(\" \")\n .trimEnd();\n\n const ruleWidth = widths.reduce((a, b) => a + b, 0) + 2 * (widths.length - 1);\n const rule = \"-\".repeat(ruleWidth);\n\n const lines: string[] = [];\n lines.push(renderRow(headers));\n lines.push(rule);\n if (dataRows.length === 0) {\n lines.push(\"(no telemetry recorded)\");\n } else {\n for (const row of dataRows) lines.push(renderRow(row));\n }\n lines.push(rule);\n lines.push(renderRow(totalRow));\n\n // Legend — honest labeling of which confidence values are estimates.\n const hasEstimate = sorted.some((r) => ESTIMATE_SOURCES.has(r.confidence));\n if (hasEstimate) {\n lines.push(\"\");\n lines.push(\n \"note: heuristic and tokenizer-approx token counts are estimates, \" +\n \"not exact host-reported usage.\",\n );\n if (sorted.some((r) => r.confidence === \"tokenizer-calibrated\")) {\n lines.push(\n \"note: tokenizer-calibrated = local approx adjusted by a sampled \" +\n \"Anthropic count_tokens factor (opt-in; content sampled off-box only \" +\n \"when AGENT_CONNECTOR_CALIBRATE=anthropic).\",\n );\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Ordered columns for {@link toCSV}, matching the {@link ToolEventRecord} shape. */\nconst CSV_COLUMNS = [\n \"id\",\n \"ts\",\n \"connectorId\",\n \"toolName\",\n \"scope\",\n \"hostPlatform\",\n \"sessionId\",\n \"projectKey\",\n \"projectDir\",\n \"inputTokens\",\n \"outputTokens\",\n \"confidenceSource\",\n \"isError\",\n // installScope / launchMethod mirror the JSON export so the scope dimension is\n // not silently dropped from CSV; absent on a record → an empty cell.\n \"installScope\",\n \"launchMethod\",\n] as const satisfies ReadonlyArray<keyof ToolEventRecord>;\n\n/** RFC-4180-ish CSV escaping: quote when the cell holds a comma, quote, or newline. */\nfunction csvCell(value: unknown): string {\n // An absent field (optional column like installScope/launchMethod) renders as\n // an empty cell rather than the literal \"undefined\" — the JSON export omits\n // the key entirely, so the CSV should likewise carry no value.\n if (value === undefined || value === null) return \"\";\n const s = String(value);\n if (/[\",\\n\\r]/.test(s)) {\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n }\n return s;\n}\n\n/** Serialize raw records to CSV (header + one line per record, CRLF-terminated). */\nexport function toCSV(records: ToolEventRecord[]): string {\n const lines: string[] = [];\n lines.push(CSV_COLUMNS.join(\",\"));\n for (const record of records) {\n lines.push(CSV_COLUMNS.map((col) => csvCell(record[col])).join(\",\"));\n }\n return lines.join(\"\\r\\n\");\n}\n\n/** Serialize raw records to a pretty-printed JSON array string. */\nexport function toJSONExport(records: ToolEventRecord[]): string {\n return JSON.stringify(records, null, 2);\n}\n\n/** Options for {@link summarize}. */\nexport interface SummarizeOptions {\n /** Grouping dimension. Default \"tool\". */\n by?: RollupDimension;\n /** Query filter forwarded to the store. Default: no filter (all records). */\n filter?: QueryFilter;\n}\n\n/**\n * Roll up a store and render it in one call. Returns the structured rows (for\n * machine use) alongside the formatted text table (for the CLI).\n */\nexport function summarize(\n store: TelemetryStore,\n opts?: SummarizeOptions,\n): { rows: RollupRow[]; text: string } {\n const by = opts?.by ?? \"tool\";\n const filter = opts?.filter ?? {};\n const rows = store.rollup(by, filter);\n return { rows, text: formatReport(rows, by) };\n}\n"],"mappings":"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","names":[]}