@ken-jo/agent-connector 0.4.100 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -9
- package/dist/{action-5OEM3QC4.js → action-EKK6FMPY.js} +8 -8
- package/dist/{audit-BO7JQYIH.js → audit-H7PQWIVH.js} +5 -5
- package/dist/{chunk-BXPSDNIB.js → chunk-34RRWEWU.js} +2 -2
- package/dist/{chunk-SFY6YLVK.js → chunk-4YOYR3T5.js} +5 -6
- package/dist/{chunk-SFY6YLVK.js.map → chunk-4YOYR3T5.js.map} +1 -1
- package/dist/{chunk-6WN37NVN.js → chunk-6FRAVOVF.js} +2 -2
- package/dist/{chunk-2Y2MZRLP.js → chunk-CWUPIT72.js} +2 -2
- package/dist/{chunk-6XMV2OOA.js → chunk-CZOXXS77.js} +3 -3
- package/dist/{chunk-IRUYJD23.js → chunk-IVJUPNLD.js} +2 -8
- package/dist/chunk-IVJUPNLD.js.map +1 -0
- package/dist/{chunk-FKW5A6HE.js → chunk-IY3SSCNK.js} +4 -4
- package/dist/{chunk-N4STCQGP.js → chunk-JUBEM3OR.js} +449 -71
- package/dist/chunk-JUBEM3OR.js.map +1 -0
- package/dist/{chunk-UM5S7DKK.js → chunk-KOFCNA5T.js} +11 -2
- package/dist/{chunk-UM5S7DKK.js.map → chunk-KOFCNA5T.js.map} +1 -1
- package/dist/{chunk-4KIXXJ7B.js → chunk-KY2VQGD7.js} +2 -2
- package/dist/{chunk-M6OS4OLU.js → chunk-R4FABNAL.js} +2 -2
- package/dist/{chunk-BV5WK6VQ.js → chunk-SA6CSOH5.js} +20 -20
- package/dist/chunk-SA6CSOH5.js.map +1 -0
- package/dist/{chunk-G5HAIYRM.js → chunk-XJSVJD5T.js} +37 -29
- package/dist/chunk-XJSVJD5T.js.map +1 -0
- package/dist/{chunk-KLZBRNFP.js → chunk-XSR3GF55.js} +63 -38
- package/dist/chunk-XSR3GF55.js.map +1 -0
- package/dist/{claude-code-LJ6DVXWQ.js → claude-code-VEERATN5.js} +4 -4
- package/dist/cli/sdk.js +4 -4
- package/dist/cli.js +1 -1
- package/dist/{cline-JAPJNOZQ.js → cline-UW6WCQRK.js} +2 -2
- package/dist/cline-UW6WCQRK.js.map +1 -0
- package/dist/{codebuff-3VODFPD4.js → codebuff-R7QBLCSJ.js} +11 -11
- package/dist/codebuff-R7QBLCSJ.js.map +1 -0
- package/dist/{codex-RXIS5BHM.js → codex-S2RADR5K.js} +2 -2
- package/dist/{crush-YB3SK6NK.js → crush-AJNQRFVE.js} +2 -2
- package/dist/{define-connector-odRHEC4H.d.ts → define-connector-iosc34CA.d.ts} +1 -1
- package/dist/{detect-ALP4G7WG.js → detect-ZC344YOJ.js} +4 -4
- package/dist/{doctor-YEQWCWZJ.js → doctor-ABYQF2YX.js} +13 -13
- package/dist/{goose-3X7ID7YW.js → goose-SXFRT7UF.js} +2 -2
- package/dist/grok-build-EP2ZUFXZ.js +682 -0
- package/dist/grok-build-EP2ZUFXZ.js.map +1 -0
- package/dist/{grok-cli-4VFVF2CQ.js → grok-cli-UUNPOUYY.js} +4 -3
- package/dist/grok-cli-UUNPOUYY.js.map +1 -0
- package/dist/{hook-F63I3SD5.js → hook-M3KQSEO3.js} +8 -8
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/{install-23WRRFBY.js → install-4X4JDKGY.js} +10 -10
- package/dist/{introspect-gTx6GzpW.d.ts → introspect-z3eKVWHN.d.ts} +1 -1
- package/dist/{kilo-SW7BH6EG.js → kilo-FVBFSS4Y.js} +1 -1
- package/dist/kilo-FVBFSS4Y.js.map +1 -0
- package/dist/{kilo-T4OXNUYY.js → kilo-N5T6KXAG.js} +2 -2
- package/dist/{kilo-cli-I2S3W245.js → kilo-cli-JOAEWYI4.js} +2 -2
- package/dist/{leaderboard-YBD3IKC2.js → leaderboard-Q4YZ3LI7.js} +3 -3
- package/dist/{mux-BO2L5CTW.js → mux-WSZWWZOX.js} +27 -21
- package/dist/mux-WSZWWZOX.js.map +1 -0
- package/dist/{package-TAUTPP7Z.js → package-FY2QZWRM.js} +23 -15
- package/dist/package-FY2QZWRM.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +6 -6
- package/dist/sdk/index.d.ts +4 -4
- package/dist/sdk/index.js +3 -3
- package/dist/sdk/test.d.ts +2 -2
- package/dist/sdk/test.js +6 -6
- package/dist/{serve-6MDLRQS2.js → serve-XT76DWUN.js} +8 -8
- package/dist/{status-IUP75XHP.js → status-DHVWBRLI.js} +8 -8
- package/dist/{statusline-PRODZV2J.js → statusline-SY5UBQOJ.js} +7 -7
- package/dist/{telemetry-X4PLZEDB.js → telemetry-TJIFAKWX.js} +5 -5
- package/dist/{types-DWXnnx-N.d.ts → types-CAKApZpX.d.ts} +4 -4
- package/dist/{uninstall-BSIJ4QXN.js → uninstall-VQO36IQ4.js} +10 -10
- package/dist/{upgrade-ZSICU3NE.js → upgrade-QEYZOA7S.js} +10 -10
- package/dist/{usage-EQUNVSCT.js → usage-LV7UGO64.js} +3 -3
- package/dist/{usage-event-J5CQ6LNT.js → usage-event-MQMKMK2R.js} +7 -7
- package/dist/{windsurf-R6D2JUTN.js → windsurf-ZWVFNJ73.js} +1 -1
- package/dist/windsurf-ZWVFNJ73.js.map +1 -0
- package/dist/{zed-DQV3ZUCU.js → zed-JCOKXOLA.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-BV5WK6VQ.js.map +0 -1
- package/dist/chunk-G5HAIYRM.js.map +0 -1
- package/dist/chunk-IRUYJD23.js.map +0 -1
- package/dist/chunk-KLZBRNFP.js.map +0 -1
- package/dist/chunk-N4STCQGP.js.map +0 -1
- package/dist/cline-JAPJNOZQ.js.map +0 -1
- package/dist/codebuff-3VODFPD4.js.map +0 -1
- package/dist/grok-cli-4VFVF2CQ.js.map +0 -1
- package/dist/kilo-SW7BH6EG.js.map +0 -1
- package/dist/mux-BO2L5CTW.js.map +0 -1
- package/dist/package-TAUTPP7Z.js.map +0 -1
- package/dist/roo-code-SUWI4BBN.js +0 -229
- package/dist/roo-code-SUWI4BBN.js.map +0 -1
- package/dist/roo-code-VI2ZKTZC.js +0 -361
- package/dist/roo-code-VI2ZKTZC.js.map +0 -1
- package/dist/windsurf-R6D2JUTN.js.map +0 -1
- /package/dist/{action-5OEM3QC4.js.map → action-EKK6FMPY.js.map} +0 -0
- /package/dist/{audit-BO7JQYIH.js.map → audit-H7PQWIVH.js.map} +0 -0
- /package/dist/{chunk-BXPSDNIB.js.map → chunk-34RRWEWU.js.map} +0 -0
- /package/dist/{chunk-6WN37NVN.js.map → chunk-6FRAVOVF.js.map} +0 -0
- /package/dist/{chunk-2Y2MZRLP.js.map → chunk-CWUPIT72.js.map} +0 -0
- /package/dist/{chunk-6XMV2OOA.js.map → chunk-CZOXXS77.js.map} +0 -0
- /package/dist/{chunk-FKW5A6HE.js.map → chunk-IY3SSCNK.js.map} +0 -0
- /package/dist/{chunk-4KIXXJ7B.js.map → chunk-KY2VQGD7.js.map} +0 -0
- /package/dist/{chunk-M6OS4OLU.js.map → chunk-R4FABNAL.js.map} +0 -0
- /package/dist/{claude-code-LJ6DVXWQ.js.map → claude-code-VEERATN5.js.map} +0 -0
- /package/dist/{codex-RXIS5BHM.js.map → codex-S2RADR5K.js.map} +0 -0
- /package/dist/{crush-YB3SK6NK.js.map → crush-AJNQRFVE.js.map} +0 -0
- /package/dist/{detect-ALP4G7WG.js.map → detect-ZC344YOJ.js.map} +0 -0
- /package/dist/{doctor-YEQWCWZJ.js.map → doctor-ABYQF2YX.js.map} +0 -0
- /package/dist/{goose-3X7ID7YW.js.map → goose-SXFRT7UF.js.map} +0 -0
- /package/dist/{hook-F63I3SD5.js.map → hook-M3KQSEO3.js.map} +0 -0
- /package/dist/{install-23WRRFBY.js.map → install-4X4JDKGY.js.map} +0 -0
- /package/dist/{kilo-T4OXNUYY.js.map → kilo-N5T6KXAG.js.map} +0 -0
- /package/dist/{kilo-cli-I2S3W245.js.map → kilo-cli-JOAEWYI4.js.map} +0 -0
- /package/dist/{leaderboard-YBD3IKC2.js.map → leaderboard-Q4YZ3LI7.js.map} +0 -0
- /package/dist/{serve-6MDLRQS2.js.map → serve-XT76DWUN.js.map} +0 -0
- /package/dist/{status-IUP75XHP.js.map → status-DHVWBRLI.js.map} +0 -0
- /package/dist/{statusline-PRODZV2J.js.map → statusline-SY5UBQOJ.js.map} +0 -0
- /package/dist/{telemetry-X4PLZEDB.js.map → telemetry-TJIFAKWX.js.map} +0 -0
- /package/dist/{uninstall-BSIJ4QXN.js.map → uninstall-VQO36IQ4.js.map} +0 -0
- /package/dist/{upgrade-ZSICU3NE.js.map → upgrade-QEYZOA7S.js.map} +0 -0
- /package/dist/{usage-EQUNVSCT.js.map → usage-LV7UGO64.js.map} +0 -0
- /package/dist/{usage-event-J5CQ6LNT.js.map → usage-event-MQMKMK2R.js.map} +0 -0
- /package/dist/{zed-DQV3ZUCU.js.map → zed-JCOKXOLA.js.map} +0 -0
package/dist/mux-BO2L5CTW.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/mux/index.ts"],"sourcesContent":["/**\n * adapters/mux — Mux (Coder) platform adapter for agent-connector.\n *\n * Mux is an **mcp-only** host: it exposes no lifecycle hook system, and MCP is\n * its extensibility mechanism. This adapter therefore installs only the MCP\n * server and reports hooks as unavailable — the same shape as the Warp\n * reference adapter.\n *\n * MCP config:\n * - user scope → ~/.mux/mcp.jsonc\n * - project scope → <projectDir>/.mux/mcp.jsonc\n * Both are JSONC (comments allowed) but we write strict JSON. The root key is\n * \"servers\", and this file doubles as the (non-existent) hook config — there\n * is no separate hook file here.\n *\n * QUIRK: Mux models each server entry as a single shell-command STRING, not an\n * object. So `servers[id]` is `\"<exe> <arg1> <arg2> ...\"` — space-joined, with\n * any token containing whitespace double-quoted. We therefore build that string\n * ourselves and upsert it idempotently (the generic object upsert helper would\n * write the wrong shape). When telemetry-wrapping, the string is the home-bin\n * `serve` wrapper command followed by its args.\n *\n * Mux documents no native `${env:VAR}` interpolation token, so env-refs in the\n * command/args are resolved to literals at install time. The string form has no\n * place for an env map, so server.env is dropped with no native equivalent.\n */\n\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type { Adapter, InstallContext } from \"../spi.js\";\nimport type {\n ChangeRecord,\n DetectedPlatform,\n HealthCheck,\n HookParadigm,\n PlatformCapabilities,\n PlatformId,\n ServerDef,\n} from \"../../core/types.js\";\nimport { resolveEnvRefs, resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport {\n buildWrappedStdio,\n} from \"../../core/spawn.js\";\nimport { renderSkillMd } from \"../claude-code/render.js\";\n\nconst HOST: PlatformId = \"mux\";\nconst MCP_ROOT_KEY = \"servers\";\n\n/**\n * Mux enforces this exact pattern (1–64 chars) on a SKILL directory name, and\n * the SKILL.md `name` field MUST equal the directory name\n * (mux.coder.com/agents/agent-skills). A skill name that does not match is\n * skip-warned rather than written to a dir Mux would reject.\n */\nconst MUX_SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst MUX_SKILL_NAME_MAX = 64;\n\n/** Quote a token only when it contains whitespace (Mux command-string form). */\nfunction quoteToken(token: string): string {\n return /\\s/.test(token) ? `\"${token}\"` : token;\n}\n\n/** Join an executable + args into Mux's single shell-command string. */\nfunction buildCommandString(command: string, args: readonly string[]): string {\n return [command, ...args].map(quoteToken).join(\" \");\n}\n\nexport class MuxAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"Mux\";\n readonly paradigm: HookParadigm = \"mcp-only\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\n supportsMemory: true,\n // Mux has no lifecycle hook system — every hook capability is false.\n preToolUse: false,\n postToolUse: false,\n preCompact: false,\n sessionStart: false,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n canModifyArgs: false,\n canModifyOutput: false,\n canInjectSessionContext: false,\n // Mux's command-string server entry is stdio-only.\n transports: [\"stdio\"],\n // Content surface: Mux auto-discovers dir-per-skill SKILL.md from its\n // workspace-local and global roots (mux.coder.com/agents/agent-skills,\n // fetched 2026-06-16):\n // project → <projectDir>/.mux/skills/<name>/SKILL.md (workspace-local)\n // user → ~/.mux/skills/<name>/SKILL.md (global, Mux-specific)\n // The skill directory name MUST match ^[a-z0-9]+(?:-[a-z0-9]+)*$ (1–64\n // chars) and the SKILL.md `name` field must equal it — a name that cannot\n // be represented is skip-warned (see installSkills).\n supportsSkills: true,\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userDir = join(homedir(), \".mux\");\n const userMcp = join(userDir, \"mcp.jsonc\");\n const projDir = join(projectDir, \".mux\");\n const projMcp = join(projDir, \"mcp.jsonc\");\n\n const userInstalled = existsSync(userDir) || existsSync(userMcp);\n const projInstalled = existsSync(projDir) || existsSync(projMcp);\n const installed = userInstalled || projInstalled;\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projMcp : userMcp;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? `found Mux config (${scope})`\n : `no Mux config at ${userDir} or ${projDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".mux\")\n : join(homedir(), \".mux\");\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"mcp.jsonc\");\n }\n\n /**\n * Mux has no hook file — hooks are not a thing here. The hook \"config path\"\n * is the same mcp.jsonc so the generic doctor/backup behave sensibly.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return this.getServerConfigPath(ctx);\n }\n\n // ── MCP server install / uninstall ───────────────────────────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const { connector, dryRun } = ctx;\n const override = connector.platforms[HOST]?.server;\n if (!connector.server || override === false) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: connector.server\n ? \"server registration disabled for mux\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n // Shallow-merge any per-platform server override into the base ServerDef.\n const server: ServerDef =\n override && typeof override === \"object\"\n ? { ...connector.server, ...override }\n : connector.server;\n\n const path = this.getServerConfigPath(ctx);\n\n if (server.transport !== \"stdio\" || !server.command) {\n // Mux's command-string entry is stdio-only; remote transports skip.\n return [\n {\n platform: this.id,\n action: \"skip\",\n path,\n detail: `transport \"${server.transport}\" not registrable (mux expects a stdio command string)`,\n },\n ];\n }\n\n const entry = this.renderCommandString(ctx, server);\n // Mux's server VALUE is a command string, but the object-map upsert is\n // value-agnostic — route through the shared helper (byte-identical output).\n return [this.upsertServerInJson(path, MCP_ROOT_KEY, connector.id, entry, dryRun)];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const path = this.getServerConfigPath(ctx);\n return [\n this.removeServerFromJson(path, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\n ];\n }\n\n /**\n * Render a stdio ServerDef into Mux's single shell-command string. Honors the\n * telemetry serve-wrapper and resolves every `${env:VAR}` to a literal (Mux\n * documents no native interpolation token).\n */\n private renderCommandString(ctx: InstallContext, server: ServerDef): string {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\n\n // Resolve env-refs to literals (Mux has no native interpolation token).\n command = resolveEnvRefs(command);\n args = resolveEnvRefsDeep(args);\n\n return buildCommandString(command, args);\n }\n\n // ── Hooks (unavailable — Mux is mcp-only) ─────────────────────────────────\n\n installHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Mux is mcp-only)\",\n },\n ];\n }\n\n uninstallHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Mux is mcp-only)\",\n },\n ];\n }\n\n // ── Content surface: skills (dir-per-skill SKILL.md) ─────────────────────\n // CONTENT-ONLY: pure native-file writers, mirroring goose/openclaw. No\n // runtime dispatch, no home-bin pointer, no telemetry wrap. Idempotent\n // (byte-identical → skip) via writeContentFile and reversible via\n // removeContentFile + removeDirIfEmpty. Honors platforms[\"mux\"].skills ===\n // false. No mcp.jsonc entry — Mux auto-discovers the skills dirs.\n //\n // Native locations (mux.coder.com/agents/agent-skills):\n // project → <projectDir>/.mux/skills/<name>/SKILL.md (workspace-local)\n // user → ~/.mux/skills/<name>/SKILL.md (global, Mux-specific)\n // The dir name MUST match MUX_SKILL_NAME_RE (1–64 chars) and the SKILL.md\n // `name` field must equal it — a name that can't be represented is skip-warned.\n\n /** The skills ROOT dir for ctx.scope (parent of every per-skill dir). */\n private skillsDir(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"skills\");\n }\n\n /** Per-skill directory: <skillsDir>/<name>. */\n private skillDir(ctx: InstallContext, name: string): string {\n return join(this.skillsDir(ctx), name);\n }\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for mux\" }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n // Mux REQUIRES the dir name (and SKILL.md `name`) to match its pattern;\n // a name we cannot represent is skip-warned rather than written to a dir\n // Mux would reject (the honesty bar — never write an unreadable path).\n if (!this.isValidSkillName(skill.name)) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail:\n `skill \"${skill.name}\" cannot be represented on mux ` +\n `(name must match ${MUX_SKILL_NAME_RE.source}, 1–${MUX_SKILL_NAME_MAX} chars); skipped`,\n });\n continue;\n }\n const dir = this.skillDir(ctx, skill.name);\n // Guard a skills path occupied by a regular FILE (mkdir would throw\n // ENOTDIR): skip-warn rather than crash (cline `.clinerules`-is-a-file\n // precedent).\n const blockingFile = this.firstFileOnSkillPath(ctx, dir);\n if (blockingFile !== null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n path: blockingFile,\n detail: `${blockingFile} is a file, not a directory; skill \"${skill.name}\" skipped`,\n });\n continue;\n }\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir.\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n // Skip names install never wrote (invalid → never on disk under our dir).\n if (!this.isValidSkillName(skill.name)) continue;\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n if (changes.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"no installable skills to remove\" }];\n }\n return changes;\n }\n\n /** True when a skill name is representable as a Mux skill directory name. */\n private isValidSkillName(name: string): boolean {\n return name.length >= 1 && name.length <= MUX_SKILL_NAME_MAX && MUX_SKILL_NAME_RE.test(name);\n }\n\n /**\n * Walk from the skills root down to `leaf` and return the FIRST segment that\n * exists as a regular file (a mkdir there throws ENOTDIR), else null — so\n * installSkills can skip-warn instead of crashing.\n */\n private firstFileOnSkillPath(ctx: InstallContext, leaf: string): string | null {\n const root = this.skillsDir(ctx);\n const segments: string[] = [];\n let cur = leaf;\n while (cur.length >= root.length) {\n segments.unshift(cur);\n if (cur === root) break;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n for (const p of segments) {\n if (!existsSync(p)) return null;\n try {\n if (statSync(p).isFile()) return p;\n } catch {\n return null;\n }\n }\n return null;\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const mcpPath = this.getServerConfigPath(ctx);\n const connectorId = ctx.connector.id;\n return [\n {\n name: `${this.name}: mcp.jsonc present`,\n check: () =>\n existsSync(mcpPath)\n ? { status: \"OK\", detail: mcpPath }\n : { status: \"FAIL\", detail: `not found: ${mcpPath}` },\n },\n {\n name: `${this.name}: server entry registered`,\n check: () => {\n // Only assert what the connector declares: a server-less connector\n // never writes an mcpServers entry, so its absence is healthy.\n if (!ctx.connector.server) {\n return { status: \"OK\", detail: \"no MCP server declared\" };\n }\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(\n mcpPath,\n );\n const bucket = cfg?.[MCP_ROOT_KEY];\n if (!cfg || !bucket) {\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${mcpPath}` };\n }\n return connectorId in bucket\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\n : {\n status: \"FAIL\",\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${mcpPath}`,\n };\n },\n },\n ];\n }\n}\n\nexport const adapter = new MuxAdapter();\nexport default adapter;\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/package.ts"],"sourcesContent":["/**\n * cli/commands/package — emit a marketplace/extension-installable connector bundle.\n *\n * Resolves the connector config (--connector <path>, else findConnectorConfig\n * walking up from the project dir, exactly like install.ts), packages it for the\n * requested --format into --out (default <cwd>/dist-plugin), then prints the\n * emitted file tree plus per-format install instructions.\n *\n * `--format <x>` accepts the full union; `--format all` emits EVERY feasible\n * format, each into <out>/<format>/..., printing install instructions per format.\n * Default --format claude-plugin.\n */\n\nimport { parseArgs } from \"node:util\";\nimport { join, relative } from \"node:path\";\n\nimport { findConnectorConfig, loadConnectorFromPath } from \"../../core/load-connector.js\";\nimport {\n ALL_FORMATS,\n FEASIBLE_FORMATS,\n installInstructions,\n isPackageFormat,\n packageConnector,\n packageConnectorAll,\n type PackageFormat,\n type PackageResult,\n} from \"../../core/package.js\";\nimport { fail, print } from \"../app.js\";\n\nconst HELP = `agent-connector package — emit a plugin/extension/marketplace bundle.\n\nusage: agent-connector package [flags]\n\nflags:\n --connector <path> Connector config to package (else auto-discovered).\n --format <fmt> Output format (default: claude-plugin). One of:\n ${ALL_FORMATS.join(\", \")}\n or \"all\" to emit every feasible format into <out>/<fmt>/.\n --out <dir> Output directory. Default: <cwd>/dist-plugin.\n --project <dir> Project root for connector discovery (default: cwd).\n --dry-run Compute the file tree without writing anything.\n --help Show this help.`;\n\n/** Print the file tree + the per-format install instructions for one bundle. */\nfunction printFormatResult(\n format: PackageFormat,\n connectorId: string,\n outDir: string,\n result: PackageResult,\n): void {\n print(` pluginDir: ${result.pluginDir}`);\n if (result.marketplacePath) {\n print(` marketplace: ${relative(outDir, result.marketplacePath)}`);\n }\n print(\" emitted files:\");\n for (const f of result.files) {\n print(` + ${relative(outDir, f)}`);\n }\n if (result.notes && result.notes.length > 0) {\n print(\" notes:\");\n for (const n of result.notes) print(` ! ${n}`);\n }\n print(\" install:\");\n for (const line of installInstructions(format, connectorId, outDir)) {\n print(` ${line}`);\n }\n // `package` stays a pure emitter (no host CLIs spawned) — but for hosts whose\n // marketplace flow agent-connector can DRIVE, point at the lifecycle verb.\n if (format === \"claude-plugin\") {\n print(\n \" (or let agent-connector drive it: agent-connector install --method marketplace --targets claude-code)\",\n );\n }\n print(\"\");\n}\n\nexport async function run(argv: string[]): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n connector: { type: \"string\" },\n format: { type: \"string\", default: \"claude-plugin\" },\n out: { type: \"string\" },\n project: { type: \"string\" },\n \"dry-run\": { type: \"boolean\", default: false },\n help: { type: \"boolean\", default: false },\n },\n allowPositionals: false,\n });\n\n if (values.help) {\n print(HELP);\n return 0;\n }\n\n const formatArg = values.format ?? \"claude-plugin\";\n const isAll = formatArg === \"all\";\n if (!isAll && !isPackageFormat(formatArg)) {\n return fail(\n `invalid --format \"${formatArg}\" (expected one of: ${ALL_FORMATS.join(\", \")}, or \"all\")`,\n );\n }\n\n const projectDir = values.project ?? process.cwd();\n const configPath = values.connector ?? findConnectorConfig(projectDir);\n if (!configPath) {\n return fail(\n \"no connector config found. Pass --connector <path> or add an \" +\n \"agent-connector.config.{mjs,js,json} to your project.\",\n );\n }\n\n const outDir = values.out ?? `${process.cwd()}/dist-plugin`;\n const dryRun = values[\"dry-run\"];\n const mode = dryRun ? \" (dry-run — nothing written)\" : \"\";\n\n const { connector } = await loadConnectorFromPath(configPath);\n\n if (isAll) {\n print(`package \"${connector.id}\" → all formats${mode}`);\n print(` outDir: ${outDir}`);\n print(\"\");\n const results = packageConnectorAll(connector, { outDir, dryRun });\n for (const { format, result } of results) {\n const formatOut = join(outDir, format);\n print(`── ${format} ──`);\n printFormatResult(format, connector.id, formatOut, result);\n }\n print(`emitted ${results.length} formats: ${FEASIBLE_FORMATS.join(\", \")}`);\n return 0;\n }\n\n const format: PackageFormat = formatArg;\n let result: PackageResult;\n try {\n result = packageConnector(connector, { outDir, format, dryRun });\n } catch (err) {\n // Emitter validation errors (e.g. mcp-server-json without\n // publish.registryNamespace) are actionable one-liners — print them as a\n // normal CLI error, never a stack trace.\n const message = err instanceof Error ? err.message : String(err);\n return fail(message);\n }\n\n print(`package \"${connector.id}\" → ${format}${mode}`);\n print(` outDir: ${outDir}`);\n print(\"\");\n printFormatResult(format, connector.id, outDir, result);\n return 0;\n}\n"],"mappings":"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","names":["format","result"]}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readJsonFile
|
|
4
|
-
} from "./chunk-RK6VBLXP.js";
|
|
5
|
-
import {
|
|
6
|
-
expandHome,
|
|
7
|
-
walkFiles
|
|
8
|
-
} from "./chunk-O7SDRBVB.js";
|
|
9
|
-
import {
|
|
10
|
-
emptyTokens
|
|
11
|
-
} from "./chunk-XD7MY6HB.js";
|
|
12
|
-
|
|
13
|
-
// src/usage/readers/roo-code.ts
|
|
14
|
-
import { existsSync, readFileSync } from "fs";
|
|
15
|
-
import { homedir, platform as osPlatform } from "os";
|
|
16
|
-
import { basename, dirname, isAbsolute, join, resolve } from "path";
|
|
17
|
-
var PLATFORM_ID = "roo-code";
|
|
18
|
-
var DEFAULT_MODEL = "unknown";
|
|
19
|
-
var DEFAULT_PROVIDER = "unknown";
|
|
20
|
-
var DEFAULT_SESSION = "unknown";
|
|
21
|
-
var ROO_EXTENSION_ID = "rooveterinaryinc.roo-cline";
|
|
22
|
-
function vscodeUserDir() {
|
|
23
|
-
const home = homedir();
|
|
24
|
-
switch (osPlatform()) {
|
|
25
|
-
case "darwin":
|
|
26
|
-
return join(home, "Library", "Application Support", "Code", "User");
|
|
27
|
-
case "win32": {
|
|
28
|
-
const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
|
|
29
|
-
return join(appData, "Code", "User");
|
|
30
|
-
}
|
|
31
|
-
default:
|
|
32
|
-
return join(home, ".config", "Code", "User");
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function tasksRoot() {
|
|
36
|
-
const raw = process.env.AGENT_CONNECTOR_ROO_CODE_DIR;
|
|
37
|
-
if (raw != null && raw.trim() !== "") {
|
|
38
|
-
const expanded = expandHome(raw.trim());
|
|
39
|
-
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
40
|
-
}
|
|
41
|
-
return join(vscodeUserDir(), "globalStorage", ROO_EXTENSION_ID, "tasks");
|
|
42
|
-
}
|
|
43
|
-
function extractI64(v) {
|
|
44
|
-
if (typeof v === "number") {
|
|
45
|
-
if (!Number.isFinite(v)) return void 0;
|
|
46
|
-
return Math.trunc(v);
|
|
47
|
-
}
|
|
48
|
-
if (typeof v === "string") {
|
|
49
|
-
const trimmed = v.trim();
|
|
50
|
-
if (!/^[+-]?\d+$/.test(trimmed)) return void 0;
|
|
51
|
-
const n = Number(trimmed);
|
|
52
|
-
return Number.isFinite(n) ? Math.trunc(n) : void 0;
|
|
53
|
-
}
|
|
54
|
-
return void 0;
|
|
55
|
-
}
|
|
56
|
-
function tokenField(v) {
|
|
57
|
-
const n = extractI64(v);
|
|
58
|
-
return n === void 0 ? 0 : Math.max(0, n);
|
|
59
|
-
}
|
|
60
|
-
function extractF64(v) {
|
|
61
|
-
if (typeof v === "number") return Number.isFinite(v) ? v : void 0;
|
|
62
|
-
if (typeof v === "string") {
|
|
63
|
-
const n = Number(v.trim());
|
|
64
|
-
return Number.isFinite(n) ? n : void 0;
|
|
65
|
-
}
|
|
66
|
-
return void 0;
|
|
67
|
-
}
|
|
68
|
-
function parseEntryTimestamp(v) {
|
|
69
|
-
let s;
|
|
70
|
-
if (typeof v === "string") s = v;
|
|
71
|
-
else if (typeof v === "number" && Number.isFinite(v)) s = String(Math.trunc(v));
|
|
72
|
-
else return null;
|
|
73
|
-
return parseTimestampStr(s);
|
|
74
|
-
}
|
|
75
|
-
function parseTimestampStr(value) {
|
|
76
|
-
const ms = Date.parse(value);
|
|
77
|
-
if (!Number.isNaN(ms)) return ms;
|
|
78
|
-
if (/^[+-]?\d+$/.test(value.trim())) {
|
|
79
|
-
const numeric = Number(value.trim());
|
|
80
|
-
if (!Number.isFinite(numeric) || numeric <= 0) return null;
|
|
81
|
-
return numeric >= 1e12 ? numeric : numeric * 1e3;
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
function extractSessionId(path) {
|
|
86
|
-
const parent = basename(dirname(path));
|
|
87
|
-
return parent !== "" ? parent : DEFAULT_SESSION;
|
|
88
|
-
}
|
|
89
|
-
function readTaskMetadata(uiMessagesPath) {
|
|
90
|
-
const historyPath = join(dirname(uiMessagesPath), "api_conversation_history.json");
|
|
91
|
-
let content;
|
|
92
|
-
try {
|
|
93
|
-
content = readFileSync(historyPath, "utf8");
|
|
94
|
-
} catch {
|
|
95
|
-
return { model: DEFAULT_MODEL };
|
|
96
|
-
}
|
|
97
|
-
return extractModelAndAgent(content);
|
|
98
|
-
}
|
|
99
|
-
function extractModelAndAgent(content) {
|
|
100
|
-
const ENV_START = "<environment_details>";
|
|
101
|
-
const ENV_END = "</environment_details>";
|
|
102
|
-
let offset = 0;
|
|
103
|
-
let lastModel;
|
|
104
|
-
let lastSlug;
|
|
105
|
-
let lastName;
|
|
106
|
-
for (; ; ) {
|
|
107
|
-
const startRel = content.indexOf(ENV_START, offset);
|
|
108
|
-
if (startRel < 0) break;
|
|
109
|
-
const startIdx = startRel + ENV_START.length;
|
|
110
|
-
const endRel = content.indexOf(ENV_END, startIdx);
|
|
111
|
-
if (endRel < 0) break;
|
|
112
|
-
const block = content.slice(startIdx, endRel);
|
|
113
|
-
const model = extractTagValue(block, "model");
|
|
114
|
-
if (model !== void 0) lastModel = model;
|
|
115
|
-
const slug = extractTagValue(block, "slug");
|
|
116
|
-
if (slug !== void 0) lastSlug = slug;
|
|
117
|
-
const name = extractTagValue(block, "name");
|
|
118
|
-
if (name !== void 0) lastName = name;
|
|
119
|
-
offset = endRel + ENV_END.length;
|
|
120
|
-
}
|
|
121
|
-
const agent = lastSlug ?? lastName;
|
|
122
|
-
const result = { model: lastModel ?? DEFAULT_MODEL };
|
|
123
|
-
if (agent !== void 0) result.agent = agent;
|
|
124
|
-
return result;
|
|
125
|
-
}
|
|
126
|
-
function extractTagValue(block, tag) {
|
|
127
|
-
const open = `<${tag}>`;
|
|
128
|
-
const close = `</${tag}>`;
|
|
129
|
-
const openIdx = block.indexOf(open);
|
|
130
|
-
if (openIdx < 0) return void 0;
|
|
131
|
-
const start = openIdx + open.length;
|
|
132
|
-
const endRel = block.indexOf(close, start);
|
|
133
|
-
if (endRel < 0) return void 0;
|
|
134
|
-
const value = block.slice(start, endRel).trim();
|
|
135
|
-
return value === "" ? void 0 : value;
|
|
136
|
-
}
|
|
137
|
-
function parseApiReqStartedPayload(text) {
|
|
138
|
-
let value;
|
|
139
|
-
try {
|
|
140
|
-
value = JSON.parse(text);
|
|
141
|
-
} catch {
|
|
142
|
-
return void 0;
|
|
143
|
-
}
|
|
144
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
145
|
-
const obj = value;
|
|
146
|
-
const cost = Math.max(0, extractF64(obj["cost"]) ?? 0);
|
|
147
|
-
const apiProtocolRaw = typeof obj["apiProtocol"] === "string" ? obj["apiProtocol"] : void 0;
|
|
148
|
-
const payload = {
|
|
149
|
-
cost,
|
|
150
|
-
tokensIn: tokenField(obj["tokensIn"]),
|
|
151
|
-
tokensOut: tokenField(obj["tokensOut"]),
|
|
152
|
-
cacheReads: tokenField(obj["cacheReads"]),
|
|
153
|
-
cacheWrites: tokenField(obj["cacheWrites"])
|
|
154
|
-
};
|
|
155
|
-
if (apiProtocolRaw !== void 0) payload.apiProtocol = apiProtocolRaw;
|
|
156
|
-
return payload;
|
|
157
|
-
}
|
|
158
|
-
function providerFromApiProtocol(apiProtocol) {
|
|
159
|
-
if (apiProtocol === void 0) return DEFAULT_PROVIDER;
|
|
160
|
-
const trimmed = apiProtocol.trim();
|
|
161
|
-
return trimmed === "" ? DEFAULT_PROVIDER : trimmed;
|
|
162
|
-
}
|
|
163
|
-
function parseRooCodeFile(path) {
|
|
164
|
-
const data = readJsonFile(path);
|
|
165
|
-
if (!Array.isArray(data)) return [];
|
|
166
|
-
const sessionId = extractSessionId(path);
|
|
167
|
-
const { model, agent } = readTaskMetadata(path);
|
|
168
|
-
const out = [];
|
|
169
|
-
for (const raw of data) {
|
|
170
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
171
|
-
const entry = raw;
|
|
172
|
-
if (entry.type !== "say" || entry.say !== "api_req_started") continue;
|
|
173
|
-
if (typeof entry.text !== "string") continue;
|
|
174
|
-
const ts = parseEntryTimestamp(entry.ts);
|
|
175
|
-
if (ts === null) continue;
|
|
176
|
-
const payload = parseApiReqStartedPayload(entry.text);
|
|
177
|
-
if (payload === void 0) continue;
|
|
178
|
-
const providerId = providerFromApiProtocol(payload.apiProtocol);
|
|
179
|
-
const tokens = emptyTokens();
|
|
180
|
-
tokens.input = payload.tokensIn;
|
|
181
|
-
tokens.output = payload.tokensOut;
|
|
182
|
-
tokens.cacheRead = payload.cacheReads;
|
|
183
|
-
tokens.cacheWrite = payload.cacheWrites;
|
|
184
|
-
const record = {
|
|
185
|
-
platformId: PLATFORM_ID,
|
|
186
|
-
modelId: model,
|
|
187
|
-
providerId,
|
|
188
|
-
sessionId,
|
|
189
|
-
tokens,
|
|
190
|
-
ts,
|
|
191
|
-
messageCount: 1,
|
|
192
|
-
confidence: "host-reported"
|
|
193
|
-
};
|
|
194
|
-
if (payload.cost > 0) record.cost = payload.cost;
|
|
195
|
-
if (agent !== void 0) record.agent = agent;
|
|
196
|
-
out.push(record);
|
|
197
|
-
}
|
|
198
|
-
return out;
|
|
199
|
-
}
|
|
200
|
-
var rooCodeReader = {
|
|
201
|
-
platformId: PLATFORM_ID,
|
|
202
|
-
kind: "local",
|
|
203
|
-
async read({ sinceMs }) {
|
|
204
|
-
const root = tasksRoot();
|
|
205
|
-
if (!existsSync(root)) return [];
|
|
206
|
-
const files = walkFiles(root, (name, abs) => {
|
|
207
|
-
return name === "ui_messages.json" && /[\\/]ui_messages\.json$/.test(abs);
|
|
208
|
-
});
|
|
209
|
-
const records = [];
|
|
210
|
-
for (const file of files) {
|
|
211
|
-
let rows;
|
|
212
|
-
try {
|
|
213
|
-
rows = parseRooCodeFile(file);
|
|
214
|
-
} catch {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
for (const row of rows) {
|
|
218
|
-
if (sinceMs !== void 0 && row.ts < sinceMs) continue;
|
|
219
|
-
records.push(row);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return records;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
var roo_code_default = rooCodeReader;
|
|
226
|
-
export {
|
|
227
|
-
roo_code_default as default
|
|
228
|
-
};
|
|
229
|
-
//# sourceMappingURL=roo-code-SUWI4BBN.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usage/readers/roo-code.ts"],"sourcesContent":["/**\n * usage/readers/roo-code — Roo Code (VS Code extension) task-log reader.\n *\n * Faithful port of tokscale sessions/roocode.rs (parse_roo_kilo_file, source\n * \"roocode\"). Roo Code (rooveterinaryinc.roo-cline) is a Cline-fork VS Code\n * extension that persists per-task logs under its VS Code globalStorage:\n * <vscodeUserDir>/globalStorage/rooveterinaryinc.roo-cline/tasks/<taskId>/\n * ui_messages.json (the UI message stream — token source)\n * api_conversation_history.json (sibling — model/agent source)\n *\n * roo-code is NOT in paths.ts hostRoots (the framework's per-platform table\n * targets CLIs with ~/.<tool> roots, not VS Code extension globalStorage), so\n * this reader resolves the cross-OS VS Code \"User\" dir itself, mirroring the\n * roo-code platform adapter (adapters/roo-code/index.ts vscodeUserDir):\n * - macOS → ~/Library/Application Support/Code/User\n * - Windows → %APPDATA%/Code/User (else ~/AppData/Roaming/Code/User)\n * - Linux → ~/.config/Code/User\n * An AGENT_CONNECTOR_ROO_CODE_DIR override (verbatim, non-empty, ~-expanded)\n * points directly at a `tasks` parent when set, matching the env convention in\n * paths.ts hostRoots.\n *\n * Tokens (ui_messages.json): the file is a JSON ARRAY of entries; only entries\n * with type==\"say\" AND say==\"api_req_started\" carry usage. Each such entry's\n * `text` field is a JSON-in-string payload:\n * tokensIn → input\n * tokensOut → output\n * cacheReads → cacheRead\n * cacheWrites → cacheWrite\n * cost (f64) → cost\n * apiProtocol → providerId (preserved verbatim, e.g. \"bedrock/anthropic\")\n * reasoning is ALWAYS 0 (Roo Code does not report it). A malformed text payload,\n * a missing text, or an unparseable `ts` skips just that entry.\n *\n * model / agent (api_conversation_history.json): scan every\n * <environment_details>…</environment_details> block for <model>, <slug>, <name>\n * tags; the LAST <model> wins (default \"unknown\"); agent prefers <slug> then\n * <name> (optional). When the sibling file is absent/unreadable: model \"unknown\",\n * no agent — exactly the Rust read_task_metadata fallback.\n *\n * session id is the taskId — the parent directory name of ui_messages.json\n * (port of extract_session_id), defaulting to \"unknown\". provider from\n * apiProtocol (trimmed, default \"unknown\"). No project attribution (Roo Code's\n * logs carry no cwd). No dedup — one row per api_req_started event. Confidence is\n * \"host-reported\".\n *\n * Fail-open: no root → []; unreadable/malformed file, entry, or payload → skipped\n * (never throws). Read-only.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir, platform as osPlatform } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\n\nimport type { UsageReader, UsageRecord } from \"../types.js\";\nimport { emptyTokens } from \"../aggregate.js\";\nimport { readJsonFile } from \"../jsonl.js\";\nimport { expandHome, walkFiles } from \"../paths.js\";\n\nconst PLATFORM_ID = \"roo-code\" as const;\nconst DEFAULT_MODEL = \"unknown\";\nconst DEFAULT_PROVIDER = \"unknown\";\nconst DEFAULT_SESSION = \"unknown\";\n\n/** Roo Code extension id → its VS Code globalStorage folder. */\nconst ROO_EXTENSION_ID = \"rooveterinaryinc.roo-cline\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Storage-root resolution (mirrors adapters/roo-code vscodeUserDir + the\n// AGENT_CONNECTOR_<PLATFORM>_DIR env convention in paths.ts hostRoots)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Resolve the cross-OS VS Code per-user \"User\" dir (the globalStorage parent). */\nfunction vscodeUserDir(): string {\n const home = homedir();\n switch (osPlatform()) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Code\", \"User\");\n case \"win32\": {\n const appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n return join(appData, \"Code\", \"User\");\n }\n default:\n return join(home, \".config\", \"Code\", \"User\");\n }\n}\n\n/**\n * The `tasks` root holding per-task subdirectories. Honors an\n * AGENT_CONNECTOR_ROO_CODE_DIR override (verbatim, non-empty, ~-expanded,\n * resolved against CWD when relative), else the extension's globalStorage tasks\n * dir under the resolved VS Code user dir.\n */\nfunction tasksRoot(): string {\n const raw = process.env.AGENT_CONNECTOR_ROO_CODE_DIR;\n if (raw != null && raw.trim() !== \"\") {\n const expanded = expandHome(raw.trim());\n return isAbsolute(expanded) ? expanded : resolve(expanded);\n }\n return join(vscodeUserDir(), \"globalStorage\", ROO_EXTENSION_ID, \"tasks\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Value coercion (ports of utils.rs extract_i64 / extract_f64 / parse_timestamp_str)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Port of extract_i64: number / numeric-string → integer, else undefined. */\nfunction extractI64(v: unknown): number | undefined {\n if (typeof v === \"number\") {\n if (!Number.isFinite(v)) return undefined;\n return Math.trunc(v);\n }\n if (typeof v === \"string\") {\n const trimmed = v.trim();\n if (!/^[+-]?\\d+$/.test(trimmed)) return undefined; // Rust parses as i64 (integer-only)\n const n = Number(trimmed);\n return Number.isFinite(n) ? Math.trunc(n) : undefined;\n }\n return undefined;\n}\n\n/** A non-negative integer from a payload field (0 on absence/garbage, clamped ≥ 0). */\nfunction tokenField(v: unknown): number {\n const n = extractI64(v);\n return n === undefined ? 0 : Math.max(0, n);\n}\n\n/** Port of extract_f64: number / numeric-string → f64, else undefined. */\nfunction extractF64(v: unknown): number | undefined {\n if (typeof v === \"number\") return Number.isFinite(v) ? v : undefined;\n if (typeof v === \"string\") {\n const n = Number(v.trim());\n return Number.isFinite(n) ? n : undefined;\n }\n return undefined;\n}\n\n/**\n * Port of utils.rs parse_timestamp_str / parse_entry_timestamp: an RFC3339 string\n * → epoch ms; an integer (string or number) → ms (×1000 when below the 1e12 ms\n * threshold); rejects ≤ 0. Returns null when unusable so the entry is skipped.\n */\nfunction parseEntryTimestamp(v: unknown): number | null {\n let s: string;\n if (typeof v === \"string\") s = v;\n else if (typeof v === \"number\" && Number.isFinite(v)) s = String(Math.trunc(v));\n else return null;\n\n return parseTimestampStr(s);\n}\n\nfunction parseTimestampStr(value: string): number | null {\n const ms = Date.parse(value);\n if (!Number.isNaN(ms)) return ms; // RFC3339 / any Date-parseable form\n\n if (/^[+-]?\\d+$/.test(value.trim())) {\n const numeric = Number(value.trim());\n if (!Number.isFinite(numeric) || numeric <= 0) return null;\n return numeric >= 1_000_000_000_000 ? numeric : numeric * 1000;\n }\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// session id (port of extract_session_id)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Session id = the parent directory (taskId) of ui_messages.json, else \"unknown\". */\nfunction extractSessionId(path: string): string {\n const parent = basename(dirname(path));\n return parent !== \"\" ? parent : DEFAULT_SESSION;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// model / agent (ports of read_task_metadata / extract_model_and_agent / extract_tag_value)\n// ─────────────────────────────────────────────────────────────────────────\n\ninterface TaskMetadata {\n model: string;\n agent?: string;\n}\n\n/** Read the sibling api_conversation_history.json for model/agent (fail-open). */\nfunction readTaskMetadata(uiMessagesPath: string): TaskMetadata {\n const historyPath = join(dirname(uiMessagesPath), \"api_conversation_history.json\");\n let content: string;\n try {\n content = readFileSync(historyPath, \"utf8\");\n } catch {\n return { model: DEFAULT_MODEL }; // missing/unreadable → (\"unknown\", None)\n }\n return extractModelAndAgent(content);\n}\n\n/**\n * Scan every <environment_details>…</environment_details> block, extracting\n * <model>, <slug>, <name>. The LAST non-empty value of each tag wins (port of the\n * Rust last_* accumulation). model defaults to \"unknown\"; agent prefers slug then\n * name (and is omitted when neither is present).\n */\nfunction extractModelAndAgent(content: string): TaskMetadata {\n const ENV_START = \"<environment_details>\";\n const ENV_END = \"</environment_details>\";\n\n let offset = 0;\n let lastModel: string | undefined;\n let lastSlug: string | undefined;\n let lastName: string | undefined;\n\n for (;;) {\n const startRel = content.indexOf(ENV_START, offset);\n if (startRel < 0) break;\n const startIdx = startRel + ENV_START.length;\n\n const endRel = content.indexOf(ENV_END, startIdx);\n if (endRel < 0) break;\n\n const block = content.slice(startIdx, endRel);\n\n const model = extractTagValue(block, \"model\");\n if (model !== undefined) lastModel = model;\n const slug = extractTagValue(block, \"slug\");\n if (slug !== undefined) lastSlug = slug;\n const name = extractTagValue(block, \"name\");\n if (name !== undefined) lastName = name;\n\n offset = endRel + ENV_END.length;\n }\n\n const agent = lastSlug ?? lastName;\n const result: TaskMetadata = { model: lastModel ?? DEFAULT_MODEL };\n if (agent !== undefined) result.agent = agent;\n return result;\n}\n\n/** First <tag>…</tag> value in `block`, trimmed; undefined when absent or empty. */\nfunction extractTagValue(block: string, tag: string): string | undefined {\n const open = `<${tag}>`;\n const close = `</${tag}>`;\n\n const openIdx = block.indexOf(open);\n if (openIdx < 0) return undefined;\n const start = openIdx + open.length;\n const endRel = block.indexOf(close, start);\n if (endRel < 0) return undefined;\n\n const value = block.slice(start, endRel).trim();\n return value === \"\" ? undefined : value;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// api_req_started payload (port of parse_api_req_started_payload + provider_from_api_protocol)\n// ─────────────────────────────────────────────────────────────────────────\n\ninterface ApiReqStartedPayload {\n cost: number;\n tokensIn: number;\n tokensOut: number;\n cacheReads: number;\n cacheWrites: number;\n apiProtocol?: string;\n}\n\n/** Parse the JSON-in-string `text` payload of an api_req_started entry. */\nfunction parseApiReqStartedPayload(text: string): ApiReqStartedPayload | undefined {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return undefined;\n }\n if (typeof value !== \"object\" || value === null) return undefined;\n const obj = value as Record<string, unknown>;\n\n // cost: extract_f64(.cost).unwrap_or(0.0).max(0.0)\n const cost = Math.max(0, extractF64(obj[\"cost\"]) ?? 0);\n const apiProtocolRaw = typeof obj[\"apiProtocol\"] === \"string\" ? (obj[\"apiProtocol\"] as string) : undefined;\n\n const payload: ApiReqStartedPayload = {\n cost,\n tokensIn: tokenField(obj[\"tokensIn\"]),\n tokensOut: tokenField(obj[\"tokensOut\"]),\n cacheReads: tokenField(obj[\"cacheReads\"]),\n cacheWrites: tokenField(obj[\"cacheWrites\"]),\n };\n if (apiProtocolRaw !== undefined) payload.apiProtocol = apiProtocolRaw;\n return payload;\n}\n\n/** Provider from apiProtocol: trimmed, non-empty, else \"unknown\" (verbatim — no inference). */\nfunction providerFromApiProtocol(apiProtocol: string | undefined): string {\n if (apiProtocol === undefined) return DEFAULT_PROVIDER;\n const trimmed = apiProtocol.trim();\n return trimmed === \"\" ? DEFAULT_PROVIDER : trimmed;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// File parse (port of parse_roo_kilo_file with source \"roocode\")\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The fields we read off a ui_messages.json entry (all optional / unknown). */\ninterface UiMessageEntry {\n type?: unknown;\n say?: unknown;\n text?: unknown;\n ts?: unknown;\n}\n\n/** Parse one task's ui_messages.json into usage records. */\nfunction parseRooCodeFile(path: string): UsageRecord[] {\n const data = readJsonFile(path);\n if (!Array.isArray(data)) return []; // malformed / non-array → no rows\n\n const sessionId = extractSessionId(path);\n const { model, agent } = readTaskMetadata(path);\n\n const out: UsageRecord[] = [];\n for (const raw of data) {\n if (typeof raw !== \"object\" || raw === null) continue;\n const entry = raw as UiMessageEntry;\n\n if (entry.type !== \"say\" || entry.say !== \"api_req_started\") continue;\n\n if (typeof entry.text !== \"string\") continue; // None => skip\n const ts = parseEntryTimestamp(entry.ts);\n if (ts === null) continue; // unparseable ts => skip\n\n const payload = parseApiReqStartedPayload(entry.text);\n if (payload === undefined) continue; // malformed payload => skip\n\n const providerId = providerFromApiProtocol(payload.apiProtocol);\n\n const tokens = emptyTokens();\n tokens.input = payload.tokensIn;\n tokens.output = payload.tokensOut;\n tokens.cacheRead = payload.cacheReads;\n tokens.cacheWrite = payload.cacheWrites;\n // reasoning stays 0 (Roo Code does not report it).\n\n const record: UsageRecord = {\n platformId: PLATFORM_ID,\n modelId: model,\n providerId,\n sessionId,\n tokens,\n ts,\n messageCount: 1,\n confidence: \"host-reported\",\n };\n if (payload.cost > 0) record.cost = payload.cost;\n if (agent !== undefined) record.agent = agent;\n out.push(record);\n }\n\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Reader singleton\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The Roo Code (VS Code extension) usage reader singleton. */\nconst rooCodeReader: UsageReader = {\n platformId: PLATFORM_ID,\n kind: \"local\",\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\n const root = tasksRoot();\n if (!existsSync(root)) return []; // no tasks dir → fail-open\n\n // <root>/<taskId>/ui_messages.json\n const files = walkFiles(root, (name, abs) => {\n return name === \"ui_messages.json\" && /[\\\\/]ui_messages\\.json$/.test(abs);\n });\n\n const records: UsageRecord[] = [];\n for (const file of files) {\n let rows: UsageRecord[];\n try {\n rows = parseRooCodeFile(file);\n } catch {\n continue; // fail-open per file\n }\n for (const row of rows) {\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\n records.push(row);\n }\n }\n return records;\n },\n};\n\nexport default rooCodeReader;\n"],"mappings":"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","names":[]}
|