@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,111 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/spawn.ts
4
+ import { existsSync } from "fs";
5
+ import { join } from "path";
6
+ function narrowInstallScope(scope) {
7
+ return scope === "project" ? "project" : "user";
8
+ }
9
+ function quoteArg(arg) {
10
+ return `"${arg.replace(/\\/g, "/")}"`;
11
+ }
12
+ function buildHomeBinHookCommand(homeBinPath, platformId, event, connectorId) {
13
+ return `${quoteArg(homeBinPath)} hook ${platformId} ${event} --connector ${connectorId}`;
14
+ }
15
+ function isHomeBinHookCommand(command, homeBinPath, connectorId) {
16
+ if (!command) return false;
17
+ const hay = command.replace(/\\/g, "/");
18
+ if (!hay.includes(homeBinPath.replace(/\\/g, "/"))) return false;
19
+ const token = `--connector ${connectorId}`;
20
+ let from = 0;
21
+ for (; ; ) {
22
+ const idx = hay.indexOf(token, from);
23
+ if (idx < 0) return false;
24
+ const after = hay.charAt(idx + token.length);
25
+ if (after === "" || after === '"' || /\s/.test(after)) return true;
26
+ from = idx + token.length;
27
+ }
28
+ }
29
+ function buildUsageEventCommand(homeBinPath, platformId, connectorId) {
30
+ return `${quoteArg(homeBinPath)} usage-event ${platformId} --connector ${connectorId}`;
31
+ }
32
+ function isUsageEventCommand(command, homeBinPath, connectorId) {
33
+ if (!command) return false;
34
+ const hay = command.replace(/\\/g, "/");
35
+ if (!hay.includes(homeBinPath.replace(/\\/g, "/"))) return false;
36
+ if (!hay.includes(" usage-event ")) return false;
37
+ return isHomeBinHookCommand(command, homeBinPath, connectorId);
38
+ }
39
+ function isHostNativeUsageEnabled(telemetry, env = process.env) {
40
+ if (telemetry.enabled !== true) return false;
41
+ if (env.AGENT_CONNECTOR_HOST_NATIVE === "1") return true;
42
+ return telemetry.hostNativeUsage === true;
43
+ }
44
+ function shouldWrapForTelemetry(server, telemetry) {
45
+ return server.wrapForTelemetry !== false && telemetry.enabled === true && server.transport === "stdio" && typeof server.command === "string" && server.command !== "";
46
+ }
47
+ function buildServeWrapperCommand(homeBinPath, connectorId, realCommand, realArgs, scope, platformId) {
48
+ const flags = ["serve", "--connector", connectorId];
49
+ if (scope !== void 0) flags.push("--scope", narrowInstallScope(scope));
50
+ if (platformId !== void 0) flags.push("--host", platformId);
51
+ return {
52
+ command: homeBinPath,
53
+ args: [...flags, "--", realCommand, ...realArgs]
54
+ };
55
+ }
56
+ function resolveSpawnCommand(command, deps = {}) {
57
+ const platform = deps.platform ?? process.platform;
58
+ if (platform !== "win32") return { file: command, needsShell: false };
59
+ const isBatch = (s) => /\.(cmd|bat)$/i.test(s);
60
+ if (/[\\/]/.test(command) || /\.[a-z0-9]+$/i.test(command)) {
61
+ return { file: command, needsShell: isBatch(command) };
62
+ }
63
+ const exists = deps.exists ?? existsSync;
64
+ const pathEnv = deps.pathEnv ?? process.env.PATH ?? process.env.Path ?? "";
65
+ const pathExt = deps.pathExt ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
66
+ const dirs = pathEnv.split(";").filter((d) => d !== "");
67
+ const exts = pathExt.split(";").filter((e) => e !== "");
68
+ for (const dir of dirs) {
69
+ for (const ext of exts) {
70
+ const candidate = join(dir, command + ext);
71
+ if (exists(candidate)) return { file: candidate, needsShell: isBatch(ext) };
72
+ }
73
+ }
74
+ return { file: command, needsShell: true };
75
+ }
76
+ var PACKAGE_RUNNERS = {
77
+ npx: "npx",
78
+ bunx: "bunx",
79
+ uvx: "uvx"
80
+ };
81
+ var INTERPRETERS = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
82
+ function commandBasename(realCommand) {
83
+ const noDir = realCommand.replace(/\\/g, "/").split("/").pop() ?? realCommand;
84
+ return noDir.replace(/\.(exe|cmd|bat|com)$/i, "").toLowerCase();
85
+ }
86
+ function detectLaunchMethod(realCommand, realArgs, opts) {
87
+ void realArgs;
88
+ if (opts?.isRemote === true) return "http";
89
+ if (typeof realCommand !== "string" || realCommand.trim() === "") {
90
+ return "unknown";
91
+ }
92
+ const base = commandBasename(realCommand);
93
+ if (base === "") return "unknown";
94
+ const runner = PACKAGE_RUNNERS[base];
95
+ if (runner !== void 0) return runner;
96
+ if (INTERPRETERS.has(base)) return "node";
97
+ return "binary";
98
+ }
99
+
100
+ export {
101
+ buildHomeBinHookCommand,
102
+ isHomeBinHookCommand,
103
+ buildUsageEventCommand,
104
+ isUsageEventCommand,
105
+ isHostNativeUsageEnabled,
106
+ shouldWrapForTelemetry,
107
+ buildServeWrapperCommand,
108
+ resolveSpawnCommand,
109
+ detectLaunchMethod
110
+ };
111
+ //# sourceMappingURL=chunk-MEH7GC7R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/spawn.ts"],"sourcesContent":["/**\n * core/spawn — cross-platform command construction.\n *\n * Borrowed from context-mode (issues #369/#372/#548/#738): bare `node` is\n * unreliable on Windows Git Bash (MSYS) and MSYS rewrites absolute paths on\n * non-C: drives. Always use the resolved runtime path, forward slashes, and\n * double-quoting. No-ops on macOS/Linux.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport type { InstallScope, PlatformId, ServerDef } from \"./types.js\";\nimport type {\n LaunchMethod,\n TelemetryInstallScope,\n} from \"../telemetry/types.js\";\n\n/**\n * Narrow the framework's 5-value {@link InstallScope} down to the two telemetry\n * slicing buckets: only a `project`-local install is `project`; everything else\n * (system/user/profile/managed) is global and reads as `user`.\n */\nexport function narrowInstallScope(scope: InstallScope): TelemetryInstallScope {\n return scope === \"project\" ? \"project\" : \"user\";\n}\n\n/** Build `\"<nodePath>\" \"<scriptPath>\"`. */\nexport function buildNodeCommand(\n scriptPath: string,\n opts?: { nodePath?: string },\n): string {\n const nodePath = (opts?.nodePath ?? process.execPath).replace(/\\\\/g, \"/\");\n const safe = scriptPath.replace(/\\\\/g, \"/\");\n return `\"${nodePath}\" \"${safe}\"`;\n}\n\n/**\n * Strict inverse of buildNodeCommand. Returns the two quoted tokens, or null if\n * `cmd` was not produced by buildNodeCommand (no fragile last-whitespace tail\n * grabbing — that was the #548 doubled-path bug when paths contained spaces).\n */\nexport function parseNodeCommand(\n cmd: string,\n): { nodePath: string; scriptPath: string } | null {\n if (typeof cmd !== \"string\" || cmd.length === 0) return null;\n const m = cmd.match(/^\"([^\"]+)\"\\s+\"([^\"]+)\"\\s*$/);\n if (!m || !m[1] || !m[2]) return null;\n return { nodePath: m[1], scriptPath: m[2] };\n}\n\n/** Quote a single argument for inclusion in a host hook command string. */\nexport function quoteArg(arg: string): string {\n return `\"${arg.replace(/\\\\/g, \"/\")}\"`;\n}\n\n/**\n * Build the universal hook command a host config points at:\n * \"<homeBin>\" hook <platformId> <event> --connector <id>\n * Pointing every host at the one stable home binary is how a single update\n * propagates everywhere (docs/ARCHITECTURE.md §3 R1).\n */\nexport function buildHomeBinHookCommand(\n homeBinPath: string,\n platformId: string,\n event: string,\n connectorId: string,\n): string {\n return `${quoteArg(homeBinPath)} hook ${platformId} ${event} --connector ${connectorId}`;\n}\n\n/**\n * True when `command` is a home-bin hook command for exactly `connectorId`.\n *\n * Critical: the connector id is the LAST token of the hook command\n * (`... --connector <id>`), so a naive `command.includes(\"--connector \" + id)`\n * collides on shared-prefix ids — `--connector acme-db` contains\n * `--connector acme`, which would let uninstalling `acme` strip `acme-db`'s\n * hooks. We anchor the id token: the character after it must be end-of-string,\n * whitespace, or a closing double-quote (the JSON-embedded case). Connector ids\n * are kebab-case (ID_RE) so they never contain whitespace or quotes themselves.\n */\nexport function isHomeBinHookCommand(\n command: string | undefined,\n homeBinPath: string,\n connectorId: string,\n): boolean {\n if (!command) return false;\n // The stored command was forward-slashed by quoteArg(); homeBinPath() yields\n // NATIVE (back)slashes on Windows (node:path join). Normalize BOTH sides so the\n // ownership substring test holds on win32 (without this, detection always\n // returns false there → duplicate hooks on re-install + orphans on uninstall).\n // No-op on POSIX.\n const hay = command.replace(/\\\\/g, \"/\");\n if (!hay.includes(homeBinPath.replace(/\\\\/g, \"/\"))) return false;\n const token = `--connector ${connectorId}`;\n let from = 0;\n for (;;) {\n const idx = hay.indexOf(token, from);\n if (idx < 0) return false;\n const after = hay.charAt(idx + token.length);\n if (after === \"\" || after === '\"' || /\\s/.test(after)) return true;\n from = idx + token.length;\n }\n}\n\n/**\n * Build the OPT-IN host-native usage-event command an AfterModel / PostInvocation\n * hook points at:\n * \"<homeBin>\" usage-event <platformId> --connector <id>\n * Mirrors {@link buildHomeBinHookCommand} (same anchoring rules apply) but routes\n * to the hidden `usage-event` entrypoint, which records a DISTINCT `model_turn`\n * row instead of dispatching a connector handler.\n */\nexport function buildUsageEventCommand(\n homeBinPath: string,\n platformId: string,\n connectorId: string,\n): string {\n return `${quoteArg(homeBinPath)} usage-event ${platformId} --connector ${connectorId}`;\n}\n\n/**\n * True when `command` is OUR host-native usage-event command for exactly\n * `connectorId`. Same end-of-token anchoring as {@link isHomeBinHookCommand} so a\n * shared-prefix id can't collide; additionally requires the ` usage-event ` verb\n * so it is never confused with a plain `hook` command during uninstall.\n */\nexport function isUsageEventCommand(\n command: string | undefined,\n homeBinPath: string,\n connectorId: string,\n): boolean {\n if (!command) return false;\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\n const hay = command.replace(/\\\\/g, \"/\");\n if (!hay.includes(homeBinPath.replace(/\\\\/g, \"/\"))) return false;\n if (!hay.includes(\" usage-event \")) return false;\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\n}\n\n/**\n * Single source of truth for \"is OPT-IN host-native turn-usage capture enabled\n * for this connector?\" — read by the Gemini / Antigravity adapters at install\n * time to decide whether to ALSO write the AfterModel / PostInvocation usage hook.\n * Enabled when the connector opts in via telemetry.hostNativeUsage OR when the\n * install-time env switch AGENT_CONNECTOR_HOST_NATIVE=1 forces it on. Off by\n * default (privacy: host-native capture is never installed silently).\n */\nexport function isHostNativeUsageEnabled(\n telemetry: { enabled: boolean; hostNativeUsage?: boolean },\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n if (telemetry.enabled !== true) return false;\n if (env.AGENT_CONNECTOR_HOST_NATIVE === \"1\") return true;\n return telemetry.hostNativeUsage === true;\n}\n\n/**\n * Single source of truth for \"should this server be wrapped by `serve` for\n * telemetry?\" — so every adapter behaves identically even for a server that\n * skipped normalizeServer (e.g. a per-platform override). On by default for an\n * stdio server with a command, unless explicitly disabled or telemetry is off.\n */\nexport function shouldWrapForTelemetry(\n server: ServerDef,\n telemetry: { enabled: boolean },\n): boolean {\n return (\n server.wrapForTelemetry !== false &&\n telemetry.enabled === true &&\n server.transport === \"stdio\" &&\n typeof server.command === \"string\" &&\n server.command !== \"\"\n );\n}\n\n/**\n * Build the telemetry-wrapping server command:\n * \"<homeBin>\" serve --connector <id> --scope <scope> --host <platformId> -- <realCommand> <realArgs...>\n * Used when a stdio server opts into transparent telemetry capture.\n *\n * `scope` records the install dimension (global user vs project-local) on every\n * telemetry row so usage can be sliced by it later. It is OPTIONAL for backward\n * compatibility: when omitted, the `--scope` flag is not emitted and the runtime\n * treats the scope as \"unknown\".\n *\n * `platformId` bakes the install TARGET platform into the wrapper so the proxy\n * stamps `hostPlatform` correctly even under a headless spawn where runtime env\n * markers are absent (detectRuntimeHost only knows claude-code/cursor/codex and\n * otherwise mis-attributes). Emitted as `--host <platformId>` in the FLAG\n * section (before `--`). It is OPTIONAL + backward-compatible: when omitted the\n * `--host` flag is not emitted and the runtime falls back to env detection.\n */\nexport function buildServeWrapperCommand(\n homeBinPath: string,\n connectorId: string,\n realCommand: string,\n realArgs: string[],\n scope?: InstallScope,\n platformId?: PlatformId,\n): { command: string; args: string[] } {\n const flags = [\"serve\", \"--connector\", connectorId];\n if (scope !== undefined) flags.push(\"--scope\", narrowInstallScope(scope));\n if (platformId !== undefined) flags.push(\"--host\", platformId);\n return {\n command: homeBinPath,\n args: [...flags, \"--\", realCommand, ...realArgs],\n };\n}\n\n/** Injectable deps for {@link resolveSpawnCommand} (so the win32 branch is unit-testable on POSIX). */\nexport interface SpawnResolveDeps {\n platform?: NodeJS.Platform;\n /** PATH value (`;`-separated on Windows). */\n pathEnv?: string;\n /** PATHEXT value (`;`-separated). */\n pathExt?: string;\n /** File-existence probe (defaults to fs.existsSync). */\n exists?: (p: string) => boolean;\n}\n\n/** How a child command should be handed to spawn() on this platform. */\nexport interface SpawnResolution {\n /** The command to pass to spawn() (an absolute path on win32 when resolved). */\n file: string;\n /**\n * Whether spawn() MUST use `shell: true`. On native Windows a `.cmd`/`.bat`\n * shim (npx.cmd, pnpm.cmd, the agent-connector.cmd launcher) cannot be run by\n * raw CreateProcess — Node throws EINVAL since the 2024 security fix unless a\n * shell is used. `.exe` resolves directly with no shell.\n */\n needsShell: boolean;\n}\n\n/**\n * Resolve a child command for spawn() across platforms. NO-OP on macOS/Linux\n * (returns the command unchanged, no shell). On native Windows it fixes the two\n * spawn failure modes for bare package runners:\n * 1. raw CreateProcess ignores PATHEXT, so spawn(\"npx\") → ENOENT even though\n * `npx.cmd` is on PATH. We search PATH × PATHEXT and return the resolved\n * absolute path.\n * 2. a resolved `.cmd`/`.bat` still cannot be launched without a shell, so we\n * flag `needsShell` for those (and `.exe`/`.com` run directly).\n * A bare name that PATH search misses falls back to `{ file: command,\n * needsShell: true }` so cmd.exe's own PATHEXT lookup still finds it.\n */\nexport function resolveSpawnCommand(\n command: string,\n deps: SpawnResolveDeps = {},\n): SpawnResolution {\n const platform = deps.platform ?? process.platform;\n if (platform !== \"win32\") return { file: command, needsShell: false };\n\n const isBatch = (s: string): boolean => /\\.(cmd|bat)$/i.test(s);\n\n // Already a path or already has an extension → don't search; only decide shell.\n if (/[\\\\/]/.test(command) || /\\.[a-z0-9]+$/i.test(command)) {\n return { file: command, needsShell: isBatch(command) };\n }\n\n const exists = deps.exists ?? existsSync;\n const pathEnv = deps.pathEnv ?? process.env.PATH ?? process.env.Path ?? \"\";\n const pathExt = deps.pathExt ?? process.env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\n const dirs = pathEnv.split(\";\").filter((d) => d !== \"\");\n const exts = pathExt.split(\";\").filter((e) => e !== \"\");\n for (const dir of dirs) {\n for (const ext of exts) {\n const candidate = join(dir, command + ext);\n if (exists(candidate)) return { file: candidate, needsShell: isBatch(ext) };\n }\n }\n // Not found on PATH — let cmd.exe resolve it (handles shim dirs we may miss).\n return { file: command, needsShell: true };\n}\n\n/** Basenames (case-insensitive, sans common extensions) of the ephemeral package runners. */\nconst PACKAGE_RUNNERS: Record<string, Extract<LaunchMethod, \"npx\" | \"bunx\" | \"uvx\">> = {\n npx: \"npx\",\n bunx: \"bunx\",\n uvx: \"uvx\",\n};\n\n/** Basenames of language interpreters that launch a local script. */\nconst INTERPRETERS: ReadonlySet<string> = new Set([\"node\", \"bun\", \"deno\"]);\n\n/**\n * Strip a directory prefix and a trailing executable extension to get the bare\n * program name. Tolerates both POSIX and Windows separators (the wrapped command\n * may be an absolute Windows path) and lowercases for case-insensitive matching.\n */\nfunction commandBasename(realCommand: string): string {\n const noDir = realCommand.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? realCommand;\n return noDir.replace(/\\.(exe|cmd|bat|com)$/i, \"\").toLowerCase();\n}\n\n/**\n * Classify how the real MCP server is launched, for the \"launch-method\" slicing\n * dimension. Package runners (npx/bunx/uvx) and interpreters (node/bun/deno) are\n * detected by the command basename. A remote (http) server is not launched\n * locally, so the caller — which alone knows the transport — passes\n * `isRemote: true` to force \"http\". Anything else resolves to \"binary\".\n */\nexport function detectLaunchMethod(\n realCommand: string,\n realArgs: string[],\n opts?: { isRemote?: boolean },\n): LaunchMethod {\n void realArgs; // reserved: args may later refine node-vs-script heuristics\n if (opts?.isRemote === true) return \"http\";\n if (typeof realCommand !== \"string\" || realCommand.trim() === \"\") {\n return \"unknown\";\n }\n const base = commandBasename(realCommand);\n if (base === \"\") return \"unknown\";\n const runner = PACKAGE_RUNNERS[base];\n if (runner !== undefined) return runner;\n if (INTERPRETERS.has(base)) return \"node\";\n return \"binary\";\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/usage/readers/antigravity-shared.ts
4
+ var MODEL_ALIASES = /* @__PURE__ */ new Map([
5
+ ["big-pickle", "glm-4.7"],
6
+ ["big pickle", "glm-4.7"],
7
+ ["bigpickle", "glm-4.7"],
8
+ ["k2p5", "kimi-k2-thinking"],
9
+ ["k2-p5", "kimi-k2-thinking"],
10
+ ["k2p6", "kimi-k2.6"],
11
+ ["k2-p6", "kimi-k2.6"],
12
+ ["kimi-k2p6", "kimi-k2.6"],
13
+ ["kimi-k2.5-thinking", "kimi-k2-thinking"],
14
+ ["kimi-for-coding", "kimi-k2.5"],
15
+ ["model_placeholder_m26", "claude-opus-4-6"],
16
+ ["model_placeholder_m35", "claude-sonnet-4-6"],
17
+ ["model_placeholder_m36", "gemini-3.1-pro"],
18
+ ["model_placeholder_m37", "gemini-3.1-pro"],
19
+ ["model_placeholder_m47", "gemini-3-flash-preview"],
20
+ ["model_openai_gpt_oss_120b_medium", "gpt-oss-120b-medium"],
21
+ ["claude-opus-4-6-thinking", "claude-opus-4-6"],
22
+ ["claude-sonnet-4-6-thinking", "claude-sonnet-4-6"],
23
+ ["claude-opus-4.6-thinking", "claude-opus-4-6"],
24
+ ["claude-sonnet-4.6-thinking", "claude-sonnet-4-6"],
25
+ ["claude-opus-4-6", "claude-opus-4-6"],
26
+ ["claude-sonnet-4-6", "claude-sonnet-4-6"],
27
+ ["claude-haiku-4-6", "claude-haiku-4-6"],
28
+ ["claude-opus-4.6", "claude-opus-4-6"],
29
+ ["claude-sonnet-4.6", "claude-sonnet-4-6"],
30
+ ["claude-haiku-4.6", "claude-haiku-4-6"],
31
+ ["anthropic/claude-4-5-opus", "claude-opus-4-5"],
32
+ ["anthropic/claude-4-5-sonnet", "claude-sonnet-4-5"],
33
+ ["anthropic/claude-4-5-haiku", "claude-haiku-4-5"],
34
+ ["anthropic/claude-4-6-opus", "claude-opus-4-6"],
35
+ ["anthropic/claude-4-6-sonnet", "claude-sonnet-4-6"],
36
+ ["anthropic/claude-4-6-haiku", "claude-haiku-4-6"],
37
+ ["gemini-3.1-pro-high", "gemini-3.1-pro"],
38
+ ["gemini-3.1-pro-low", "gemini-3.1-pro"],
39
+ ["gemini-3-pro-high", "gemini-3-pro"],
40
+ ["gemini-3-pro-low", "gemini-3-pro"],
41
+ ["gemini-3-flash", "gemini-3-flash-preview"],
42
+ ["gemini-3-flash-c", "gemini-3-flash-preview"],
43
+ ["kimi-k2.5-nvfp4", "kimi-k2.5"],
44
+ ["kimi-k2-instruct-0905", "kimi-k2.5"]
45
+ ]);
46
+ function resolveAlias(modelId) {
47
+ return MODEL_ALIASES.get(modelId.toLowerCase());
48
+ }
49
+ function toSafeInt(v) {
50
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v.trim()) : NaN;
51
+ if (!Number.isFinite(n)) return 0;
52
+ return Math.max(0, Math.trunc(n));
53
+ }
54
+ function nonEmptyStr(v) {
55
+ if (typeof v !== "string") return void 0;
56
+ return v.trim() === "" ? void 0 : v;
57
+ }
58
+
59
+ export {
60
+ resolveAlias,
61
+ toSafeInt,
62
+ nonEmptyStr
63
+ };
64
+ //# sourceMappingURL=chunk-MQPNU2CY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/readers/antigravity-shared.ts"],"sourcesContent":["/**\n * usage/readers/antigravity-shared — logic shared by the Antigravity IDE\n * (`antigravity`) and Antigravity CLI (`antigravity-cli`) native usage readers.\n *\n * Both surfaces persist per-conversation transcripts as `transcript*.jsonl`\n * under a `brain/` subtree of their global dir. Each assistant turn embeds a\n * Gemini-style `usage_metadata` block (camelCase, same field names the Gemini\n * API returns):\n * promptTokenCount → input (cache-INCLUSIVE, like the Gemini API)\n * candidatesTokenCount → output\n * cachedContentTokenCount → cacheRead (the cached portion already inside prompt)\n * thoughtsTokenCount → reasoning\n * cacheWrite is not reported by Gemini → always 0.\n *\n * Because promptTokenCount is cache-inclusive, net input = prompt - cached\n * (clamped ≥ 0), mirroring the gemini-cli reader's subtract_cached_overlap.\n *\n * Confidence is MEDIUM on the native JSONL shape (Antigravity is fast-moving and\n * its docs are JS-rendered): the field names above are the documented/observed\n * shape but versions may differ, so extraction is BEST-EFFORT and every caller\n * fails open to [] when the store is absent or a row does not match. We also\n * tolerate the snake_case spellings as a secondary alias set.\n *\n * The model-alias table is shared with — and kept in sync with — the tokscale\n * pricing::aliases::resolve_alias port (sessions/antigravity.rs).\n */\n\nimport { emptyTokens } from \"../aggregate.js\";\nimport { inferProvider } from \"../normalize.js\";\nimport type { PlatformId } from \"../../core/types.js\";\nimport type { UsageRecord } from \"../types.js\";\n\nconst DEFAULT_MODEL = \"unknown\";\nconst DEFAULT_PROVIDER = \"antigravity\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Model alias table (port of tokscale pricing::aliases::resolve_alias).\n// Keys are lowercased; lookup lowercases the input. Resolves placeholder /\n// reseller model ids (e.g. Antigravity's MODEL_PLACEHOLDER_*) to canonical ids.\n// Shared by both Antigravity readers.\n// ─────────────────────────────────────────────────────────────────────────\nexport const MODEL_ALIASES: ReadonlyMap<string, string> = new Map([\n [\"big-pickle\", \"glm-4.7\"],\n [\"big pickle\", \"glm-4.7\"],\n [\"bigpickle\", \"glm-4.7\"],\n [\"k2p5\", \"kimi-k2-thinking\"],\n [\"k2-p5\", \"kimi-k2-thinking\"],\n [\"k2p6\", \"kimi-k2.6\"],\n [\"k2-p6\", \"kimi-k2.6\"],\n [\"kimi-k2p6\", \"kimi-k2.6\"],\n [\"kimi-k2.5-thinking\", \"kimi-k2-thinking\"],\n [\"kimi-for-coding\", \"kimi-k2.5\"],\n [\"model_placeholder_m26\", \"claude-opus-4-6\"],\n [\"model_placeholder_m35\", \"claude-sonnet-4-6\"],\n [\"model_placeholder_m36\", \"gemini-3.1-pro\"],\n [\"model_placeholder_m37\", \"gemini-3.1-pro\"],\n [\"model_placeholder_m47\", \"gemini-3-flash-preview\"],\n [\"model_openai_gpt_oss_120b_medium\", \"gpt-oss-120b-medium\"],\n [\"claude-opus-4-6-thinking\", \"claude-opus-4-6\"],\n [\"claude-sonnet-4-6-thinking\", \"claude-sonnet-4-6\"],\n [\"claude-opus-4.6-thinking\", \"claude-opus-4-6\"],\n [\"claude-sonnet-4.6-thinking\", \"claude-sonnet-4-6\"],\n [\"claude-opus-4-6\", \"claude-opus-4-6\"],\n [\"claude-sonnet-4-6\", \"claude-sonnet-4-6\"],\n [\"claude-haiku-4-6\", \"claude-haiku-4-6\"],\n [\"claude-opus-4.6\", \"claude-opus-4-6\"],\n [\"claude-sonnet-4.6\", \"claude-sonnet-4-6\"],\n [\"claude-haiku-4.6\", \"claude-haiku-4-6\"],\n [\"anthropic/claude-4-5-opus\", \"claude-opus-4-5\"],\n [\"anthropic/claude-4-5-sonnet\", \"claude-sonnet-4-5\"],\n [\"anthropic/claude-4-5-haiku\", \"claude-haiku-4-5\"],\n [\"anthropic/claude-4-6-opus\", \"claude-opus-4-6\"],\n [\"anthropic/claude-4-6-sonnet\", \"claude-sonnet-4-6\"],\n [\"anthropic/claude-4-6-haiku\", \"claude-haiku-4-6\"],\n [\"gemini-3.1-pro-high\", \"gemini-3.1-pro\"],\n [\"gemini-3.1-pro-low\", \"gemini-3.1-pro\"],\n [\"gemini-3-pro-high\", \"gemini-3-pro\"],\n [\"gemini-3-pro-low\", \"gemini-3-pro\"],\n [\"gemini-3-flash\", \"gemini-3-flash-preview\"],\n [\"gemini-3-flash-c\", \"gemini-3-flash-preview\"],\n [\"kimi-k2.5-nvfp4\", \"kimi-k2.5\"],\n [\"kimi-k2-instruct-0905\", \"kimi-k2.5\"],\n]);\n\n/** Resolve a model alias to its canonical id, or undefined when none matches. */\nexport function resolveAlias(modelId: string): string | undefined {\n return MODEL_ALIASES.get(modelId.toLowerCase());\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Scalar coercion (shared with the tokscale to_safe_i64 contract)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Coerce an unknown to a non-negative i64-safe integer (port of to_safe_i64):\n * accepts number or numeric string, floors toward zero, clamps at 0; 0 otherwise.\n */\nexport function toSafeInt(v: unknown): number {\n const n =\n typeof v === \"number\"\n ? v\n : typeof v === \"string\"\n ? Number(v.trim())\n : NaN;\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.trunc(n));\n}\n\n/** A non-empty trimmed string, or undefined (port of the `.filter(!is_empty)` guards). */\nexport function nonEmptyStr(v: unknown): string | undefined {\n if (typeof v !== \"string\") return undefined;\n return v.trim() === \"\" ? undefined : v;\n}\n\n/** First key whose value coerces to a positive-or-zero integer (ignoring absent/zero). */\nfunction firstInt(obj: Record<string, unknown>, keys: readonly string[]): number {\n for (const k of keys) {\n const v = obj[k];\n if (v === undefined || v === null) continue;\n const n = toSafeInt(v);\n if (n > 0) return n;\n }\n return 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Native-transcript usage_metadata extraction (best-effort, MEDIUM confidence)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The camelCase Gemini field names + snake_case secondary aliases. */\nconst PROMPT_KEYS = [\"promptTokenCount\", \"prompt_token_count\"] as const;\nconst CANDIDATES_KEYS = [\"candidatesTokenCount\", \"candidates_token_count\"] as const;\nconst CACHED_KEYS = [\"cachedContentTokenCount\", \"cached_content_token_count\"] as const;\nconst THOUGHTS_KEYS = [\"thoughtsTokenCount\", \"thoughts_token_count\"] as const;\n\n/** A `usage_metadata` object pulled off a transcript row (any spelling). */\nfunction findUsageMetadata(row: Record<string, unknown>): Record<string, unknown> | undefined {\n for (const k of [\"usage_metadata\", \"usageMetadata\"]) {\n const v = row[k];\n if (typeof v === \"object\" && v !== null && !Array.isArray(v)) {\n return v as Record<string, unknown>;\n }\n }\n return undefined;\n}\n\n/** Pull a model id off a transcript row, tolerant of common spellings. */\nfunction findModelId(row: Record<string, unknown>): string | undefined {\n return (\n nonEmptyStr(row.modelId) ??\n nonEmptyStr(row.model) ??\n nonEmptyStr(row.model_id) ??\n nonEmptyStr(row.modelName) ??\n nonEmptyStr(row.model_name)\n );\n}\n\n/** Pull a session/conversation id off a row, tolerant of common spellings. */\nfunction findSessionId(row: Record<string, unknown>): string | undefined {\n return (\n nonEmptyStr(row.sessionId) ??\n nonEmptyStr(row.session_id) ??\n nonEmptyStr(row.conversationId) ??\n nonEmptyStr(row.conversation_id)\n );\n}\n\n/** Options carried per-file so a transcript with no per-row model/session can borrow them. */\nexport interface UsageMetadataContext {\n /** Platform attribution for emitted records. */\n platformId: PlatformId;\n /** Session id to attribute rows to when the row itself carries none. */\n fallbackSessionId: string;\n /** Per-conversation model fallback (e.g. from a session_meta row), if known. */\n fallbackModel?: string;\n /** Timestamp (epoch ms) to use when the row carries none — never drops a row for it. */\n fallbackTs: number;\n}\n\n/**\n * Best-effort parse of one native brain-transcript JSONL row into a usage record,\n * or undefined when the row has no `usage_metadata` / no tokens. Mirrors the\n * gemini-cli cache-inclusive normalization: net input = prompt - cached (≥ 0).\n *\n * Fail-soft: any shape that does not match yields undefined (the caller skips it).\n */\nexport function parseUsageMetadataRow(\n row: unknown,\n ctx: UsageMetadataContext,\n): UsageRecord | undefined {\n if (typeof row !== \"object\" || row === null || Array.isArray(row)) return undefined;\n const obj = row as Record<string, unknown>;\n\n const meta = findUsageMetadata(obj);\n if (meta === undefined) return undefined;\n\n const promptTokens = firstInt(meta, PROMPT_KEYS);\n const output = firstInt(meta, CANDIDATES_KEYS);\n const cacheRead = firstInt(meta, CACHED_KEYS);\n const reasoning = firstInt(meta, THOUGHTS_KEYS);\n\n // promptTokenCount is cache-inclusive → net input = prompt - cached (≥ 0).\n const input = Math.max(0, promptTokens - Math.min(cacheRead, promptTokens));\n\n if (input === 0 && output === 0 && cacheRead === 0 && reasoning === 0) return undefined;\n\n const rawModel = findModelId(obj) ?? ctx.fallbackModel ?? DEFAULT_MODEL;\n const modelId = resolveAlias(rawModel) ?? rawModel;\n const providerId = inferProvider(modelId) ?? DEFAULT_PROVIDER;\n\n const sessionId = findSessionId(obj) ?? ctx.fallbackSessionId;\n\n const ts = parseRowTimestamp(obj) ?? ctx.fallbackTs;\n\n const tokens = emptyTokens();\n tokens.input = input;\n tokens.output = output;\n tokens.cacheRead = cacheRead;\n tokens.cacheWrite = 0;\n tokens.reasoning = reasoning;\n\n const record: UsageRecord = {\n platformId: ctx.platformId,\n modelId,\n providerId,\n sessionId,\n tokens,\n ts,\n messageCount: 1,\n confidence: \"host-reported\",\n };\n\n const dedupKey = nonEmptyStr(obj.responseId) ?? nonEmptyStr(obj.response_id) ?? nonEmptyStr(obj.id);\n if (dedupKey !== undefined) record.dedupKey = `${sessionId}:${dedupKey}`;\n\n return record;\n}\n\n/** Parse a per-row timestamp (epoch ms / s, or RFC3339 string), else undefined. */\nfunction parseRowTimestamp(obj: Record<string, unknown>): number | undefined {\n const v = obj.timestamp ?? obj.ts ?? obj.created_at ?? obj.createdAt;\n if (typeof v === \"number\" && Number.isFinite(v)) {\n const n = Math.trunc(v);\n if (n <= 0) return undefined;\n return n >= 1_000_000_000_000 ? n : n * 1000;\n }\n if (typeof v === \"string\") {\n const ms = Date.parse(v);\n if (!Number.isNaN(ms)) return ms;\n const numeric = Number(v.trim());\n if (Number.isInteger(numeric) && numeric > 0) {\n return numeric >= 1_000_000_000_000 ? numeric : numeric * 1000;\n }\n }\n return undefined;\n}\n\n/** A per-conversation model fallback off a `session_meta` row, else undefined. */\nexport function sessionMetaModel(row: unknown): string | undefined {\n if (typeof row !== \"object\" || row === null || Array.isArray(row)) return undefined;\n const obj = row as Record<string, unknown>;\n const rowType = nonEmptyStr(obj.type);\n if (rowType !== \"session_meta\") return undefined;\n return findModelId(obj);\n}\n"],"mappings":";;;AAyCO,IAAM,gBAA6C,oBAAI,IAAI;AAAA,EAChE,CAAC,cAAc,SAAS;AAAA,EACxB,CAAC,cAAc,SAAS;AAAA,EACxB,CAAC,aAAa,SAAS;AAAA,EACvB,CAAC,QAAQ,kBAAkB;AAAA,EAC3B,CAAC,SAAS,kBAAkB;AAAA,EAC5B,CAAC,QAAQ,WAAW;AAAA,EACpB,CAAC,SAAS,WAAW;AAAA,EACrB,CAAC,aAAa,WAAW;AAAA,EACzB,CAAC,sBAAsB,kBAAkB;AAAA,EACzC,CAAC,mBAAmB,WAAW;AAAA,EAC/B,CAAC,yBAAyB,iBAAiB;AAAA,EAC3C,CAAC,yBAAyB,mBAAmB;AAAA,EAC7C,CAAC,yBAAyB,gBAAgB;AAAA,EAC1C,CAAC,yBAAyB,gBAAgB;AAAA,EAC1C,CAAC,yBAAyB,wBAAwB;AAAA,EAClD,CAAC,oCAAoC,qBAAqB;AAAA,EAC1D,CAAC,4BAA4B,iBAAiB;AAAA,EAC9C,CAAC,8BAA8B,mBAAmB;AAAA,EAClD,CAAC,4BAA4B,iBAAiB;AAAA,EAC9C,CAAC,8BAA8B,mBAAmB;AAAA,EAClD,CAAC,mBAAmB,iBAAiB;AAAA,EACrC,CAAC,qBAAqB,mBAAmB;AAAA,EACzC,CAAC,oBAAoB,kBAAkB;AAAA,EACvC,CAAC,mBAAmB,iBAAiB;AAAA,EACrC,CAAC,qBAAqB,mBAAmB;AAAA,EACzC,CAAC,oBAAoB,kBAAkB;AAAA,EACvC,CAAC,6BAA6B,iBAAiB;AAAA,EAC/C,CAAC,+BAA+B,mBAAmB;AAAA,EACnD,CAAC,8BAA8B,kBAAkB;AAAA,EACjD,CAAC,6BAA6B,iBAAiB;AAAA,EAC/C,CAAC,+BAA+B,mBAAmB;AAAA,EACnD,CAAC,8BAA8B,kBAAkB;AAAA,EACjD,CAAC,uBAAuB,gBAAgB;AAAA,EACxC,CAAC,sBAAsB,gBAAgB;AAAA,EACvC,CAAC,qBAAqB,cAAc;AAAA,EACpC,CAAC,oBAAoB,cAAc;AAAA,EACnC,CAAC,kBAAkB,wBAAwB;AAAA,EAC3C,CAAC,oBAAoB,wBAAwB;AAAA,EAC7C,CAAC,mBAAmB,WAAW;AAAA,EAC/B,CAAC,yBAAyB,WAAW;AACvC,CAAC;AAGM,SAAS,aAAa,SAAqC;AAChE,SAAO,cAAc,IAAI,QAAQ,YAAY,CAAC;AAChD;AAUO,SAAS,UAAU,GAAoB;AAC5C,QAAM,IACJ,OAAO,MAAM,WACT,IACA,OAAO,MAAM,WACX,OAAO,EAAE,KAAK,CAAC,IACf;AACR,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;AAClC;AAGO,SAAS,YAAY,GAAgC;AAC1D,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,SAAO,EAAE,KAAK,MAAM,KAAK,SAAY;AACvC;","names":[]}
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/toml.ts
4
+ import TOML from "@iarna/toml";
5
+ function writeTomlString(obj) {
6
+ return TOML.stringify(obj);
7
+ }
8
+
9
+ export {
10
+ writeTomlString
11
+ };
12
+ //# sourceMappingURL=chunk-OPXAP3AC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/toml.ts"],"sourcesContent":["/**\n * core/toml — minimal TOML string IO.\n *\n * The TOML analog of core/yaml's read/write helpers. Lifted from the codex\n * adapter's private readToml/writeToml so the Group-B command renderers for\n * Gemini/Qwen/Codex (which author `.toml` command files) share one\n * implementation rather than each re-importing @iarna/toml.\n *\n * Deliberately tiny and string-oriented: these are pure (de)serializers — no\n * filesystem touches, no dryRun, no merge logic. Callers own reading/writing\n * and any idempotency/upsert (the shapes differ per platform), exactly as the\n * adapters layer their own logic on top of the JSON/YAML helpers.\n */\n\nimport TOML from \"@iarna/toml\";\n\n/**\n * Serialize a plain object to a TOML string.\n *\n * @iarna/toml's `stringify` is typed to accept its own `JsonMap`; our objects\n * are structurally compatible, so we cast at the boundary (same approach the\n * codex adapter used). Returns the TOML text — the caller writes it.\n */\nexport function writeTomlString(obj: Record<string, unknown>): string {\n return TOML.stringify(obj as never);\n}\n\n/**\n * Parse a TOML string into a typed object. Throws on malformed TOML (callers\n * that want fail-soft behavior should wrap in try/catch, mirroring how the\n * codex adapter degrades a corrupt config.toml to `{}`).\n */\nexport function readTomlString<T = Record<string, unknown>>(text: string): T {\n return TOML.parse(text) as T;\n}\n"],"mappings":";;;AAcA,OAAO,UAAU;AASV,SAAS,gBAAgB,KAAsC;AACpE,SAAO,KAAK,UAAU,GAAY;AACpC;","names":[]}
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/usage/jsonl.ts
4
+ import { readFileSync, statSync } from "fs";
5
+ function readJsonlLines(path) {
6
+ let text;
7
+ try {
8
+ text = readFileSync(path, "utf8");
9
+ } catch {
10
+ return [];
11
+ }
12
+ const out = [];
13
+ for (const line of text.split("\n")) {
14
+ const trimmed = line.trim();
15
+ if (trimmed === "") continue;
16
+ try {
17
+ out.push(JSON.parse(trimmed));
18
+ } catch {
19
+ continue;
20
+ }
21
+ }
22
+ return out;
23
+ }
24
+ function readJsonFile(path) {
25
+ let text;
26
+ try {
27
+ text = readFileSync(path, "utf8");
28
+ } catch {
29
+ return void 0;
30
+ }
31
+ try {
32
+ return JSON.parse(text);
33
+ } catch {
34
+ return void 0;
35
+ }
36
+ }
37
+ function fileMtimeMs(path) {
38
+ try {
39
+ return Math.floor(statSync(path).mtimeMs);
40
+ } catch {
41
+ return Date.now();
42
+ }
43
+ }
44
+
45
+ export {
46
+ readJsonlLines,
47
+ readJsonFile,
48
+ fileMtimeMs
49
+ };
50
+ //# sourceMappingURL=chunk-RK6VBLXP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/jsonl.ts"],"sourcesContent":["/**\n * usage/jsonl — tolerant JSONL / JSON readers + a file-mtime helper.\n *\n * Every reader is fail-open: a malformed line is skipped (never thrown), a\n * missing/unreadable file yields no rows. Mirrors the NdjsonStore.query and host\n * parser behavior — bad input degrades to empty, it does not crash a report.\n *\n * Ports tokscale sessions/utils.rs file_modified_timestamp_ms (mtime fallback)\n * and the per-line skip-malformed loop used by every JSONL parser.\n */\n\nimport { readFileSync, statSync } from \"node:fs\";\n\n/**\n * Parse a JSONL file into objects, skipping blank/malformed lines. Returns [] on\n * any read error (missing file, permissions). Each yielded value is whatever\n * `JSON.parse` produced — callers narrow it. Order matches the file.\n *\n * Returns an array (not a generator) so callers can index/slice; for the file\n * sizes here (session logs) a full read is acceptable, matching the Rust\n * BufReader-per-file approach.\n */\nexport function readJsonlLines(path: string): unknown[] {\n let text: string;\n try {\n text = readFileSync(path, \"utf8\");\n } catch {\n return [];\n }\n\n const out: unknown[] = [];\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed === \"\") continue;\n try {\n out.push(JSON.parse(trimmed));\n } catch {\n continue; // skip malformed line\n }\n }\n return out;\n}\n\n/**\n * Read and parse a whole JSON file. Returns undefined on any read/parse error\n * (the fail-open contract for JSON readers). Caller narrows the unknown result.\n */\nexport function readJsonFile(path: string): unknown {\n let text: string;\n try {\n text = readFileSync(path, \"utf8\");\n } catch {\n return undefined;\n }\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * File modification time in epoch ms, used as a timestamp fallback when a log\n * line carries none. Port of file_modified_timestamp_ms: falls back to \"now\"\n * when the file cannot be stat'd so a row is never dropped for lack of a ts.\n */\nexport function fileMtimeMs(path: string): number {\n try {\n return Math.floor(statSync(path).mtimeMs);\n } catch {\n return Date.now();\n }\n}\n"],"mappings":";;;AAWA,SAAS,cAAc,gBAAgB;AAWhC,SAAS,eAAe,MAAyB;AACtD,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,MAAiB,CAAC;AACxB,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,YAAY,GAAI;AACpB,QAAI;AACF,UAAI,KAAK,KAAK,MAAM,OAAO,CAAC;AAAA,IAC9B,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,aAAa,MAAuB;AAClD,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,YAAY,MAAsB;AAChD,MAAI;AACF,WAAO,KAAK,MAAM,SAAS,IAAI,EAAE,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO,KAAK,IAAI;AAAA,EAClB;AACF;","names":[]}