@ai-devkit/agent-manager 0.37.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AgentManager.d.ts +0 -4
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +2 -2
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/capacity/anthropic.test.js +115 -0
- package/dist/__tests__/capacity/anthropic.test.js.map +1 -0
- package/dist/__tests__/capacity/boundaries.test.js +45 -0
- package/dist/__tests__/capacity/boundaries.test.js.map +1 -0
- package/dist/__tests__/capacity/claude.test.js +3 -3
- package/dist/__tests__/capacity/claude.test.js.map +1 -1
- package/dist/__tests__/capacity/codex.test.js +3 -1
- package/dist/__tests__/capacity/codex.test.js.map +1 -1
- package/dist/__tests__/capacity/openai.test.js +21 -12
- package/dist/__tests__/capacity/openai.test.js.map +1 -1
- package/dist/__tests__/capacity/wham.test.js +1 -1
- package/dist/__tests__/capacity/wham.test.js.map +1 -1
- package/dist/__tests__/capacity/zai.test.js +7 -8
- package/dist/__tests__/capacity/zai.test.js.map +1 -1
- package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js +2 -1
- package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js +7 -15
- package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js +3 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js +11 -26
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js +3 -7
- package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js.map +1 -1
- package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js +5 -11
- package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js.map +1 -1
- package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js +9 -19
- package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js.map +1 -1
- package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js +3 -7
- package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js.map +1 -1
- package/dist/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.js +31 -10
- package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -0
- package/dist/__tests__/harnesses/runtimeProfiles.test.js +89 -0
- package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -0
- package/dist/__tests__/print/DurableAgent.test.js +4 -6
- package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgentRepository.test.js +1 -12
- package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
- package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -1
- package/dist/__tests__/terminal/TmuxManager.test.js +1 -1
- package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
- package/dist/capacity/index.d.ts +9 -19
- package/dist/capacity/index.d.ts.map +1 -1
- package/dist/capacity/index.js +11 -4
- package/dist/capacity/index.js.map +1 -1
- package/dist/capacity/providers/anthropic.d.ts +9 -0
- package/dist/capacity/providers/anthropic.d.ts.map +1 -0
- package/dist/capacity/{claude.js → providers/anthropic.js} +10 -128
- package/dist/capacity/providers/anthropic.js.map +1 -0
- package/dist/capacity/providers/openai-platform.d.ts +14 -0
- package/dist/capacity/providers/openai-platform.d.ts.map +1 -0
- package/dist/capacity/providers/openai-platform.js +120 -0
- package/dist/capacity/providers/openai-platform.js.map +1 -0
- package/dist/capacity/providers/openai-subscription.d.ts +20 -0
- package/dist/capacity/providers/openai-subscription.d.ts.map +1 -0
- package/dist/capacity/providers/openai-subscription.js +129 -0
- package/dist/capacity/providers/openai-subscription.js.map +1 -0
- package/dist/capacity/providers/zai.d.ts +8 -0
- package/dist/capacity/providers/zai.d.ts.map +1 -0
- package/dist/capacity/{zai.js → providers/zai.js} +17 -62
- package/dist/capacity/providers/zai.js.map +1 -0
- package/dist/capacity/sources/claude.d.ts +9 -0
- package/dist/capacity/sources/claude.d.ts.map +1 -0
- package/dist/capacity/sources/claude.js +17 -0
- package/dist/capacity/sources/claude.js.map +1 -0
- package/dist/capacity/{codex.d.ts → sources/codex.d.ts} +5 -9
- package/dist/capacity/sources/codex.d.ts.map +1 -0
- package/dist/capacity/{codex.js → sources/codex.js} +31 -84
- package/dist/capacity/sources/codex.js.map +1 -0
- package/dist/capacity/sources/pi.d.ts +30 -0
- package/dist/capacity/sources/pi.d.ts.map +1 -0
- package/dist/capacity/sources/pi.js +81 -0
- package/dist/capacity/sources/pi.js.map +1 -0
- package/dist/capacity/types.d.ts +2 -0
- package/dist/capacity/types.d.ts.map +1 -1
- package/dist/capacity/types.js +1 -1
- package/dist/capacity/types.js.map +1 -1
- package/dist/harnesses/claude/credentials.d.ts +10 -0
- package/dist/harnesses/claude/credentials.d.ts.map +1 -0
- package/dist/harnesses/claude/credentials.js +112 -0
- package/dist/harnesses/claude/credentials.js.map +1 -0
- package/dist/harnesses/claude/readiness.d.ts +3 -0
- package/dist/harnesses/claude/readiness.d.ts.map +1 -0
- package/dist/harnesses/claude/readiness.js +63 -0
- package/dist/harnesses/claude/readiness.js.map +1 -0
- package/dist/harnesses/codex/credentials.d.ts +15 -0
- package/dist/harnesses/codex/credentials.d.ts.map +1 -0
- package/dist/harnesses/codex/credentials.js +58 -0
- package/dist/harnesses/codex/credentials.js.map +1 -0
- package/dist/harnesses/codex/readiness.d.ts +3 -0
- package/dist/harnesses/codex/readiness.d.ts.map +1 -0
- package/dist/harnesses/codex/readiness.js +60 -0
- package/dist/harnesses/codex/readiness.js.map +1 -0
- package/dist/harnesses/copilot/readiness.d.ts +3 -0
- package/dist/harnesses/copilot/readiness.d.ts.map +1 -0
- package/dist/harnesses/copilot/readiness.js +39 -0
- package/dist/harnesses/copilot/readiness.js.map +1 -0
- package/dist/harnesses/opencode/readiness.d.ts +3 -0
- package/dist/harnesses/opencode/readiness.d.ts.map +1 -0
- package/dist/harnesses/opencode/readiness.js +50 -0
- package/dist/harnesses/opencode/readiness.js.map +1 -0
- package/dist/harnesses/pi/credentials.d.ts +22 -0
- package/dist/harnesses/pi/credentials.d.ts.map +1 -0
- package/dist/harnesses/pi/credentials.js +98 -0
- package/dist/harnesses/pi/credentials.js.map +1 -0
- package/dist/harnesses/pi/readiness.d.ts +3 -0
- package/dist/harnesses/pi/readiness.d.ts.map +1 -0
- package/dist/harnesses/pi/readiness.js +96 -0
- package/dist/harnesses/pi/readiness.js.map +1 -0
- package/dist/harnesses/readiness/AgentReadiness.d.ts +6 -0
- package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -0
- package/dist/harnesses/readiness/AgentReadiness.js +68 -0
- package/dist/harnesses/readiness/AgentReadiness.js.map +1 -0
- package/dist/harnesses/readiness/checks.d.ts +31 -0
- package/dist/harnesses/readiness/checks.d.ts.map +1 -0
- package/dist/harnesses/readiness/checks.js +270 -0
- package/dist/harnesses/readiness/checks.js.map +1 -0
- package/dist/{readiness/AgentReadiness.d.ts → harnesses/readiness/types.d.ts} +19 -10
- package/dist/harnesses/readiness/types.d.ts.map +1 -0
- package/dist/harnesses/readiness/types.js +4 -0
- package/dist/harnesses/readiness/types.js.map +1 -0
- package/dist/harnesses/runtimeProfiles.d.ts +11 -0
- package/dist/harnesses/runtimeProfiles.d.ts.map +1 -0
- package/dist/harnesses/runtimeProfiles.js +99 -0
- package/dist/harnesses/runtimeProfiles.js.map +1 -0
- package/dist/index.d.ts +16 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -23
- package/dist/index.js.map +1 -1
- package/dist/runtime/ManagedAgentRuntime.d.ts +3 -3
- package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -1
- package/dist/runtime/ManagedAgentRuntime.js +6 -20
- package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
- package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -1
- package/dist/runtime/herdr/HerdrAgentDiscovery.js +2 -43
- package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
- package/dist/runtime/tmux/PidPolling.d.ts +1 -1
- package/dist/runtime/tmux/PidPolling.d.ts.map +1 -1
- package/dist/runtime/tmux/PidPolling.js.map +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.js +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -1
- package/dist/runtime/tmux/TmuxManager.d.ts +12 -0
- package/dist/runtime/tmux/TmuxManager.d.ts.map +1 -0
- package/dist/{terminal → runtime/tmux}/TmuxManager.js +9 -28
- package/dist/runtime/tmux/TmuxManager.js.map +1 -0
- package/dist/runtime/types.d.ts +1 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/terminal/TerminalFocusManager.d.ts +4 -32
- package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
- package/dist/terminal/TerminalFocusManager.js +17 -267
- package/dist/terminal/TerminalFocusManager.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts +1 -44
- package/dist/terminal/TtyWriter.d.ts.map +1 -1
- package/dist/terminal/TtyWriter.js +11 -347
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/terminal/backends/index.d.ts +5 -0
- package/dist/terminal/backends/index.d.ts.map +1 -0
- package/dist/terminal/backends/index.js +19 -0
- package/dist/terminal/backends/index.js.map +1 -0
- package/dist/terminal/backends/iterm2.d.ts +3 -0
- package/dist/terminal/backends/iterm2.d.ts.map +1 -0
- package/dist/terminal/backends/iterm2.js +135 -0
- package/dist/terminal/backends/iterm2.js.map +1 -0
- package/dist/terminal/backends/shared.d.ts +7 -0
- package/dist/terminal/backends/shared.d.ts.map +1 -0
- package/dist/terminal/backends/shared.js +35 -0
- package/dist/terminal/backends/shared.js.map +1 -0
- package/dist/terminal/backends/terminal-app.d.ts +3 -0
- package/dist/terminal/backends/terminal-app.d.ts.map +1 -0
- package/dist/terminal/backends/terminal-app.js +129 -0
- package/dist/terminal/backends/terminal-app.js.map +1 -0
- package/dist/terminal/backends/tmux.d.ts +3 -0
- package/dist/terminal/backends/tmux.d.ts.map +1 -0
- package/dist/terminal/backends/tmux.js +76 -0
- package/dist/terminal/backends/tmux.js.map +1 -0
- package/dist/terminal/backends/wezterm.d.ts +3 -0
- package/dist/terminal/backends/wezterm.d.ts.map +1 -0
- package/dist/terminal/backends/wezterm.js +71 -0
- package/dist/terminal/backends/wezterm.js.map +1 -0
- package/dist/terminal/types.d.ts +21 -0
- package/dist/terminal/types.d.ts.map +1 -0
- package/dist/terminal/types.js +10 -0
- package/dist/terminal/types.js.map +1 -0
- package/package.json +1 -1
- package/src/AgentManager.ts +1 -1
- package/src/__tests__/capacity/anthropic.test.ts +105 -0
- package/src/__tests__/capacity/boundaries.test.ts +36 -0
- package/src/__tests__/capacity/claude.test.ts +3 -8
- package/src/__tests__/capacity/codex.test.ts +12 -35
- package/src/__tests__/capacity/fixtures/openai-codex-auth.json +1 -1
- package/src/__tests__/capacity/openai.test.ts +15 -15
- package/src/__tests__/capacity/wham.test.ts +1 -1
- package/src/__tests__/capacity/zai.test.ts +27 -62
- package/src/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.ts +2 -2
- package/src/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.ts +1 -1
- package/src/__tests__/harnesses/codex/durable/CodexCliProbe.test.ts +14 -19
- package/src/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.ts +3 -3
- package/src/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.ts +24 -30
- package/src/__tests__/harnesses/codex/durable/CodexPrintRunner.test.ts +4 -8
- package/src/__tests__/harnesses/pi/durable/PiCliProbe.test.ts +7 -11
- package/src/__tests__/harnesses/pi/durable/PiPrintAgentService.test.ts +19 -22
- package/src/__tests__/harnesses/pi/durable/PiPrintRunner.test.ts +7 -8
- package/src/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.ts +36 -9
- package/src/__tests__/harnesses/runtimeProfiles.test.ts +110 -0
- package/src/__tests__/print/DurableAgent.test.ts +4 -13
- package/src/__tests__/print/DurableAgentRepository.test.ts +1 -13
- package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +1 -1
- package/src/__tests__/terminal/TmuxManager.test.ts +1 -1
- package/src/capacity/index.ts +29 -23
- package/src/capacity/{claude.ts → providers/anthropic.ts} +16 -131
- package/src/capacity/providers/openai-platform.ts +150 -0
- package/src/capacity/{wham.ts → providers/openai-subscription.ts} +86 -8
- package/src/capacity/{zai.ts → providers/zai.ts} +29 -111
- package/src/capacity/sources/claude.ts +30 -0
- package/src/capacity/{codex.ts → sources/codex.ts} +54 -124
- package/src/capacity/sources/pi.ts +131 -0
- package/src/capacity/types.ts +3 -0
- package/src/harnesses/claude/credentials.ts +122 -0
- package/src/harnesses/claude/readiness.ts +69 -0
- package/src/harnesses/codex/credentials.ts +81 -0
- package/src/harnesses/codex/readiness.ts +72 -0
- package/src/harnesses/copilot/readiness.ts +33 -0
- package/src/harnesses/opencode/readiness.ts +48 -0
- package/src/harnesses/pi/credentials.ts +129 -0
- package/src/harnesses/pi/readiness.ts +104 -0
- package/src/harnesses/readiness/AgentReadiness.ts +87 -0
- package/src/harnesses/readiness/checks.ts +336 -0
- package/src/harnesses/readiness/types.ts +90 -0
- package/src/{utils/agents.ts → harnesses/runtimeProfiles.ts} +34 -38
- package/src/index.ts +15 -128
- package/src/runtime/ManagedAgentRuntime.ts +7 -22
- package/src/runtime/herdr/HerdrAgentDiscovery.ts +3 -19
- package/src/runtime/tmux/PidPolling.ts +1 -1
- package/src/runtime/tmux/TmuxAgentRuntime.ts +1 -1
- package/src/{terminal → runtime/tmux}/TmuxManager.ts +9 -30
- package/src/runtime/types.ts +1 -1
- package/src/terminal/TerminalFocusManager.ts +19 -292
- package/src/terminal/TtyWriter.ts +16 -336
- package/src/terminal/backends/index.ts +21 -0
- package/src/terminal/backends/iterm2.ts +126 -0
- package/src/terminal/backends/shared.ts +42 -0
- package/src/terminal/backends/terminal-app.ts +122 -0
- package/src/terminal/backends/tmux.ts +62 -0
- package/src/terminal/backends/wezterm.ts +65 -0
- package/src/terminal/types.ts +22 -0
- package/dist/__tests__/readiness/AgentReadiness.test.js.map +0 -1
- package/dist/__tests__/utils/agents.test.js +0 -93
- package/dist/__tests__/utils/agents.test.js.map +0 -1
- package/dist/capacity/claude.d.ts +0 -17
- package/dist/capacity/claude.d.ts.map +0 -1
- package/dist/capacity/claude.js.map +0 -1
- package/dist/capacity/codex.d.ts.map +0 -1
- package/dist/capacity/codex.js.map +0 -1
- package/dist/capacity/openai.d.ts +0 -47
- package/dist/capacity/openai.d.ts.map +0 -1
- package/dist/capacity/openai.js +0 -307
- package/dist/capacity/openai.js.map +0 -1
- package/dist/capacity/wham.d.ts +0 -16
- package/dist/capacity/wham.d.ts.map +0 -1
- package/dist/capacity/wham.js +0 -66
- package/dist/capacity/wham.js.map +0 -1
- package/dist/capacity/zai.d.ts +0 -14
- package/dist/capacity/zai.d.ts.map +0 -1
- package/dist/capacity/zai.js.map +0 -1
- package/dist/readiness/AgentReadiness.d.ts.map +0 -1
- package/dist/readiness/AgentReadiness.js +0 -614
- package/dist/readiness/AgentReadiness.js.map +0 -1
- package/dist/terminal/TmuxManager.d.ts +0 -28
- package/dist/terminal/TmuxManager.d.ts.map +0 -1
- package/dist/terminal/TmuxManager.js.map +0 -1
- package/dist/terminal/index.d.ts +0 -5
- package/dist/terminal/index.d.ts.map +0 -1
- package/dist/terminal/index.js +0 -5
- package/dist/terminal/index.js.map +0 -1
- package/dist/utils/agents.d.ts +0 -16
- package/dist/utils/agents.d.ts.map +0 -1
- package/dist/utils/agents.js +0 -117
- package/dist/utils/agents.js.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -6
- package/dist/utils/index.js.map +0 -1
- package/src/__tests__/utils/agents.test.ts +0 -105
- package/src/capacity/openai.ts +0 -355
- package/src/readiness/AgentReadiness.ts +0 -730
- package/src/terminal/index.ts +0 -4
- package/src/utils/index.ts +0 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/harnesses/runtimeProfiles.test.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport {\n HARNESS_RUNTIME_PROFILES,\n runtimeAgentMatchesHarness,\n} from \"../../harnesses/runtimeProfiles.js\";\n\ndescribe(\"harness runtime profiles\", () => {\n it(\"keeps launch and runtime identity metadata together\", () => {\n expect(HARNESS_RUNTIME_PROFILES.antigravity_cli).toMatchObject({\n command: \"agy\",\n runtimeKind: \"agy\",\n discoveryAliases: [\"agy\", \"antigravity\", \"antigravity_cli\"],\n });\n expect(HARNESS_RUNTIME_PROFILES.antigravity_cli.matches(\"/usr/local/bin/agy\")).toBe(true);\n });\n\n it(\"recognizes agent commands without matching similar argument text\", () => {\n expect(HARNESS_RUNTIME_PROFILES.copilot.matches(\"/opt/copilot-cli/1.0/copilot\")).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.copilot.matches(\"node /repo/copilot-cli/script.js\")).toBe(\n false,\n );\n expect(HARNESS_RUNTIME_PROFILES.grok_cli.matches(\"/Users/dev/.grok/bin/grok --approve\")).toBe(\n true,\n );\n expect(HARNESS_RUNTIME_PROFILES.grok_cli.matches(\"node /repo/grok-cli/script.js\")).toBe(false);\n expect(\n HARNESS_RUNTIME_PROFILES.gemini_cli.matches(\"node --no-warnings /usr/local/bin/gemini\"),\n ).toBe(true);\n });\n\n it(\"matches runtime discovery names through the owning harness profile\", () => {\n expect(runtimeAgentMatchesHarness(\"kiro-cli\", \"kiro\")).toBe(true);\n expect(runtimeAgentMatchesHarness(\"gemini\", \"gemini_cli\")).toBe(true);\n expect(runtimeAgentMatchesHarness(\"codex\", \"claude\")).toBe(false);\n });\n});\n\ndescribe(\"harness matchers with executables under paths containing spaces\", () => {\n let root: string;\n\n function install(relativePath: string): string {\n const fullPath = path.join(root, relativePath);\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n fs.writeFileSync(fullPath, \"\");\n return fullPath;\n }\n\n beforeEach(() => {\n root = fs.mkdtempSync(path.join(os.tmpdir(), \"runtime-profiles-spaces-\"));\n });\n\n afterEach(() => {\n fs.rmSync(root, { recursive: true, force: true });\n });\n\n it(\"recognizes argv[0] under a spaced directory\", () => {\n const codex = install(\"Applications/Some App.app/Contents/Resources/codex\");\n const claude = install(\"Users/dev/My Tools/bin/claude\");\n\n expect(HARNESS_RUNTIME_PROFILES.codex.matches(`${codex} --sandbox workspace-write`)).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.codex.matches(codex)).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.claude.matches(`${claude} --continue`)).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.claude.matches(`${codex} exec --cd /repo`)).toBe(false);\n });\n\n it(\"recognizes argv[0] by distribution name under a spaced directory\", () => {\n const copilot = install(\"opt/Home Brew/Caskroom/copilot-cli/1.0.60/copilot\");\n\n expect(HARNESS_RUNTIME_PROFILES.copilot.matches(`${copilot} --allow-all-tools`)).toBe(true);\n });\n\n it(\"does not treat arguments containing spaces as the executable\", () => {\n const vim = install(\"usr/bin/vim\");\n const codex = install(\"Applications/Some App.app/Contents/Resources/codex\");\n\n expect(HARNESS_RUNTIME_PROFILES.claude.matches(`${vim} My Notes/claude`)).toBe(false);\n expect(\n HARNESS_RUNTIME_PROFILES.claude.matches(`${codex} exec review the Other Dir/claude`),\n ).toBe(false);\n expect(HARNESS_RUNTIME_PROFILES.copilot.matches(`${vim} Some Dir/copilot-cli/notes.md`)).toBe(\n false,\n );\n expect(HARNESS_RUNTIME_PROFILES.claude.matches(\"/usr/bin/vim My Notes/claude\")).toBe(false);\n });\n\n it(\"recognizes spaced argv[0] in script-aware matchers\", () => {\n const pi = install(\"Users/dev/Node Tools/bin/pi\");\n const kiro = install(\"Applications/Kiro CLI.app/Contents/MacOS/kiro-cli\");\n const node = install(\"Applications/Dev Tools/node/bin/node\");\n\n expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${pi} --model x`)).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.kiro.matches(`${kiro} chat`)).toBe(true);\n expect(\n HARNESS_RUNTIME_PROFILES.gemini_cli.matches(`${node} /Users/dev/.npm/bin/gemini --yolo`),\n ).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${node} /opt/pi-tools/script.js`)).toBe(false);\n });\n\n it(\"does not match agent words inside an executable directory\", () => {\n const node = install(\"opt/Dev pi gemini/bin/node\");\n\n expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${node} /repo/script.js`)).toBe(false);\n expect(HARNESS_RUNTIME_PROFILES.gemini_cli.matches(`${node} /repo/script.js`)).toBe(false);\n expect(HARNESS_RUNTIME_PROFILES.gemini_cli.matches(`${node} /repo/bin/gemini`)).toBe(true);\n expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${node} --title pi`)).toBe(true);\n });\n});\n"],"names":["fs","os","path","afterEach","beforeEach","describe","expect","it","HARNESS_RUNTIME_PROFILES","runtimeAgentMatchesHarness","antigravity_cli","toMatchObject","command","runtimeKind","discoveryAliases","matches","toBe","copilot","grok_cli","gemini_cli","root","install","relativePath","fullPath","join","mkdirSync","dirname","recursive","writeFileSync","mkdtempSync","tmpdir","rmSync","force","codex","claude","vim","pi","kiro","node"],"mappings":"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"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const ErrorType = api.DurableAgentBusyError;
|
|
7
|
-
const error = new ErrorType("agent-id", "reviewer");
|
|
2
|
+
import { DurableAgentBusyError } from "../../durable/DurableAgent.js";
|
|
3
|
+
describe("durable-agent errors", ()=>{
|
|
4
|
+
it("classifies a busy error without exposing prompt data", ()=>{
|
|
5
|
+
const error = new DurableAgentBusyError("agent-id", "reviewer");
|
|
8
6
|
expect(error).toMatchObject({
|
|
9
7
|
name: "DurableAgentBusyError",
|
|
10
8
|
code: "DURABLE_AGENT_BUSY",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/__tests__/print/DurableAgent.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\n\ndescribe(\"durable-agent
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/print/DurableAgent.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { DurableAgentBusyError } from \"../../durable/DurableAgent.js\";\n\ndescribe(\"durable-agent errors\", () => {\n it(\"classifies a busy error without exposing prompt data\", () => {\n const error = new DurableAgentBusyError(\"agent-id\", \"reviewer\");\n\n expect(error).toMatchObject({\n name: \"DurableAgentBusyError\",\n code: \"DURABLE_AGENT_BUSY\",\n agentId: \"agent-id\",\n message: 'Durable agent \"reviewer\" is busy.',\n });\n });\n});\n"],"names":["describe","expect","it","DurableAgentBusyError","error","toMatchObject","name","code","agentId","message"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAC9C,SAASC,qBAAqB,QAAQ,gCAAgC;AAEtEH,SAAS,wBAAwB;IAC/BE,GAAG,wDAAwD;QACzD,MAAME,QAAQ,IAAID,sBAAsB,YAAY;QAEpDF,OAAOG,OAAOC,aAAa,CAAC;YAC1BC,MAAM;YACNC,MAAM;YACNC,SAAS;YACTC,SAAS;QACX;IACF;AACF"}
|
|
@@ -2,6 +2,7 @@ import fs from "fs";
|
|
|
2
2
|
import os from "os";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { DurableAgentRepository } from "../../durable/DurableAgentRepository.js";
|
|
5
6
|
const tempDirs = [];
|
|
6
7
|
afterEach(()=>{
|
|
7
8
|
for (const dir of tempDirs.splice(0))fs.rmSync(dir, {
|
|
@@ -9,11 +10,6 @@ afterEach(()=>{
|
|
|
9
10
|
force: true
|
|
10
11
|
});
|
|
11
12
|
});
|
|
12
|
-
async function loadStore() {
|
|
13
|
-
const api = await import("../../index.js");
|
|
14
|
-
expect(api).toHaveProperty("DurableAgentRepository");
|
|
15
|
-
return api.DurableAgentRepository;
|
|
16
|
-
}
|
|
17
13
|
function fixture() {
|
|
18
14
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "durable-agent-store-"));
|
|
19
15
|
tempDirs.push(root);
|
|
@@ -27,7 +23,6 @@ function fixture() {
|
|
|
27
23
|
}
|
|
28
24
|
describe("DurableAgentRepository create/list/resolve", ()=>{
|
|
29
25
|
it("creates distinct durable identities with a canonical cwd and lists them", async ()=>{
|
|
30
|
-
const DurableAgentRepository = await loadStore();
|
|
31
26
|
const { cwd, dbPath } = fixture();
|
|
32
27
|
const repository = new DurableAgentRepository({
|
|
33
28
|
dbPath,
|
|
@@ -55,7 +50,6 @@ describe("DurableAgentRepository create/list/resolve", ()=>{
|
|
|
55
50
|
expect(fs.existsSync(dbPath)).toBe(true);
|
|
56
51
|
});
|
|
57
52
|
it("resolves exact ids and names and rejects duplicate names", async ()=>{
|
|
58
|
-
const DurableAgentRepository = await loadStore();
|
|
59
53
|
const { cwd, dbPath } = fixture();
|
|
60
54
|
const repository = new DurableAgentRepository({
|
|
61
55
|
dbPath
|
|
@@ -79,7 +73,6 @@ describe("DurableAgentRepository create/list/resolve", ()=>{
|
|
|
79
73
|
});
|
|
80
74
|
});
|
|
81
75
|
it("rejects a missing cwd", async ()=>{
|
|
82
|
-
const DurableAgentRepository = await loadStore();
|
|
83
76
|
const { root, dbPath } = fixture();
|
|
84
77
|
const repository = new DurableAgentRepository({
|
|
85
78
|
dbPath
|
|
@@ -94,7 +87,6 @@ describe("DurableAgentRepository create/list/resolve", ()=>{
|
|
|
94
87
|
});
|
|
95
88
|
describe("DurableAgentRepository run ownership", ()=>{
|
|
96
89
|
it("fails fast when another exact owner is live and completes only for its token", async ()=>{
|
|
97
|
-
const DurableAgentRepository = await loadStore();
|
|
98
90
|
const { cwd, dbPath } = fixture();
|
|
99
91
|
const live = new Map([
|
|
100
92
|
[
|
|
@@ -144,7 +136,6 @@ describe("DurableAgentRepository run ownership", ()=>{
|
|
|
144
136
|
});
|
|
145
137
|
});
|
|
146
138
|
it("retains busy for a live provider then recovers a dead run without signaling it", async ()=>{
|
|
147
|
-
const DurableAgentRepository = await loadStore();
|
|
148
139
|
const { cwd, dbPath } = fixture();
|
|
149
140
|
const live = new Map([
|
|
150
141
|
[
|
|
@@ -199,7 +190,6 @@ describe("DurableAgentRepository run ownership", ()=>{
|
|
|
199
190
|
});
|
|
200
191
|
});
|
|
201
192
|
it("reconciles an interrupted run to degraded during list", async ()=>{
|
|
202
|
-
const DurableAgentRepository = await loadStore();
|
|
203
193
|
const { cwd, dbPath } = fixture();
|
|
204
194
|
const live = new Map([
|
|
205
195
|
[
|
|
@@ -237,7 +227,6 @@ describe("DurableAgentRepository run ownership", ()=>{
|
|
|
237
227
|
});
|
|
238
228
|
});
|
|
239
229
|
it("rejects send acquisition when the bound cwd is replaced by a symlink", async ()=>{
|
|
240
|
-
const DurableAgentRepository = await loadStore();
|
|
241
230
|
const { root, cwd, dbPath } = fixture();
|
|
242
231
|
const repository = new DurableAgentRepository({
|
|
243
232
|
dbPath
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/__tests__/print/DurableAgentRepository.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport { afterEach, describe, expect, it } from \"vitest\";\n\nconst tempDirs: string[] = [];\n\nafterEach(() => {\n for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });\n});\n\nasync function loadStore(): Promise<any> {\n const api = (await import(\"../../index.js\")) as Record<string, unknown>;\n expect(api).toHaveProperty(\"DurableAgentRepository\");\n return api.DurableAgentRepository;\n}\n\nfunction fixture(): { root: string; cwd: string; dbPath: string } {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"durable-agent-store-\"));\n tempDirs.push(root);\n const cwd = path.join(root, \"project\");\n fs.mkdirSync(cwd);\n return { root, cwd, dbPath: path.join(root, \"state\", \"agents.db\") };\n}\n\ndescribe(\"DurableAgentRepository create/list/resolve\", () => {\n it(\"creates distinct durable identities with a canonical cwd and lists them\", async () => {\n const DurableAgentRepository = await loadStore();\n const { cwd, dbPath } = fixture();\n const repository = new DurableAgentRepository({\n dbPath,\n now: () => new Date(\"2026-08-07T09:00:00Z\"),\n });\n\n const agent = await repository.create({ name: \"reviewer\", cwd });\n\n expect(agent).toMatchObject({\n name: \"reviewer\",\n provider: \"claude\",\n mode: \"durable\",\n cwd: fs.realpathSync(cwd),\n state: \"ready\",\n sessionHealth: \"uninitialized\",\n activeRun: null,\n });\n expect(agent.id).toMatch(/^[0-9a-f-]{36}$/);\n expect(agent.providerSessionId).toMatch(/^[0-9a-f-]{36}$/);\n expect(agent.id).not.toBe(agent.providerSessionId);\n expect(await repository.list()).toEqual([agent]);\n expect(fs.existsSync(dbPath)).toBe(true);\n });\n\n it(\"resolves exact ids and names and rejects duplicate names\", async () => {\n const DurableAgentRepository = await loadStore();\n const { cwd, dbPath } = fixture();\n const repository = new DurableAgentRepository({ dbPath });\n const agent = await repository.create({ name: \"Reviewer\", cwd });\n\n expect(await repository.resolve(agent.id)).toMatchObject({ id: agent.id });\n expect(await repository.resolve(\"reviewer\")).toMatchObject({ id: agent.id });\n expect(await repository.resolve(\"view\")).toBeNull();\n await expect(repository.create({ name: \"reviewer\", cwd })).rejects.toMatchObject({\n code: \"DURABLE_AGENT_NAME_CONFLICT\",\n });\n });\n\n it(\"rejects a missing cwd\", async () => {\n const DurableAgentRepository = await loadStore();\n const { root, dbPath } = fixture();\n const repository = new DurableAgentRepository({ dbPath });\n\n await expect(\n repository.create({ name: \"missing\", cwd: path.join(root, \"missing\") }),\n ).rejects.toMatchObject({ code: \"DURABLE_AGENT_REPOSITORY\" });\n });\n});\n\ndescribe(\"DurableAgentRepository run ownership\", () => {\n it(\"fails fast when another exact owner is live and completes only for its token\", async () => {\n const DurableAgentRepository = await loadStore();\n const { cwd, dbPath } = fixture();\n const live = new Map<number, string>([[process.pid, \"owner-start\"]]);\n const processInspector = {\n getIdentity: (pid: number) => {\n const startedAt = live.get(pid);\n return startedAt ? { pid, startedAt } : null;\n },\n };\n const repository = new DurableAgentRepository({ dbPath, processInspector });\n const agent = await repository.create({ name: \"runner\", cwd });\n\n const acquired = await repository.acquireRun(agent.id);\n await expect(repository.acquireRun(agent.id)).rejects.toMatchObject({\n code: \"DURABLE_AGENT_BUSY\",\n });\n await expect(\n repository.completeRun(agent.id, \"wrong-token\", {\n status: \"succeeded\",\n exitCode: 0,\n summary: \"done\",\n sessionHealth: \"healthy\",\n }),\n ).rejects.toMatchObject({ code: \"DURABLE_AGENT_REPOSITORY\" });\n\n const completed = await repository.completeRun(agent.id, acquired.token, {\n status: \"succeeded\",\n exitCode: 0,\n summary: \"done\",\n sessionHealth: \"healthy\",\n });\n expect(completed).toMatchObject({ state: \"ready\", sessionHealth: \"healthy\", activeRun: null });\n });\n\n it(\"retains busy for a live provider then recovers a dead run without signaling it\", async () => {\n const DurableAgentRepository = await loadStore();\n const { cwd, dbPath } = fixture();\n const live = new Map<number, string>([\n [process.pid, \"owner-start\"],\n [4242, \"provider-start\"],\n ]);\n const processInspector = {\n getIdentity: (pid: number) => {\n const startedAt = live.get(pid);\n return startedAt ? { pid, startedAt } : null;\n },\n };\n const first = new DurableAgentRepository({ dbPath, processInspector });\n const agent = await first.create({ name: \"recoverable\", cwd });\n const run = await first.acquireRun(agent.id);\n await first.recordProviderProcess(agent.id, run.token, {\n pid: 4242,\n startedAt: \"provider-start\",\n });\n\n live.delete(process.pid);\n await expect(first.acquireRun(agent.id)).rejects.toMatchObject({ code: \"DURABLE_AGENT_BUSY\" });\n\n live.delete(4242);\n live.set(process.pid, \"replacement-owner-start\");\n const recovered = await first.acquireRun(agent.id);\n expect(recovered.agent).toMatchObject({\n state: \"running\",\n lastResult: { status: \"interrupted\" },\n });\n await first.completeRun(agent.id, recovered.token, {\n status: \"failed\",\n exitCode: 1,\n summary: \"failed\",\n sessionHealth: \"unknown\",\n });\n });\n\n it(\"reconciles an interrupted run to degraded during list\", async () => {\n const DurableAgentRepository = await loadStore();\n const { cwd, dbPath } = fixture();\n const live = new Map<number, string>([[process.pid, \"owner-start\"]]);\n const repository = new DurableAgentRepository({\n dbPath,\n incompleteLockGraceMs: 10,\n processInspector: {\n getIdentity: (pid: number) => {\n const startedAt = live.get(pid);\n return startedAt ? { pid, startedAt } : null;\n },\n },\n });\n const agent = await repository.create({ name: \"crashed\", cwd });\n await repository.acquireRun(agent.id);\n live.clear();\n\n const listed = await repository.list();\n\n expect(listed[0]).toMatchObject({\n state: \"degraded\",\n sessionHealth: \"unknown\",\n activeRun: null,\n lastResult: { status: \"interrupted\" },\n });\n });\n\n it(\"rejects send acquisition when the bound cwd is replaced by a symlink\", async () => {\n const DurableAgentRepository = await loadStore();\n const { root, cwd, dbPath } = fixture();\n const repository = new DurableAgentRepository({ dbPath });\n const agent = await repository.create({ name: \"bound\", cwd });\n const moved = path.join(root, \"moved-project\");\n const other = path.join(root, \"other-project\");\n fs.renameSync(cwd, moved);\n fs.mkdirSync(other);\n fs.symlinkSync(other, cwd);\n\n await expect(repository.acquireRun(agent.id)).rejects.toMatchObject({\n code: \"DURABLE_AGENT_REPOSITORY\",\n });\n });\n});\n"],"names":["fs","os","path","afterEach","describe","expect","it","tempDirs","dir","splice","rmSync","recursive","force","loadStore","api","toHaveProperty","DurableAgentRepository","fixture","root","mkdtempSync","join","tmpdir","push","cwd","mkdirSync","dbPath","repository","now","Date","agent","create","name","toMatchObject","provider","mode","realpathSync","state","sessionHealth","activeRun","id","toMatch","providerSessionId","not","toBe","list","toEqual","existsSync","resolve","toBeNull","rejects","code","live","Map","process","pid","processInspector","getIdentity","startedAt","get","acquired","acquireRun","completeRun","status","exitCode","summary","completed","token","first","run","recordProviderProcess","delete","set","recovered","lastResult","incompleteLockGraceMs","clear","listed","moved","other","renameSync","symlinkSync"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/print/DurableAgentRepository.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { DurableAgentRepository } from \"../../durable/DurableAgentRepository.js\";\n\nconst tempDirs: string[] = [];\n\nafterEach(() => {\n for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });\n});\n\nfunction fixture(): { root: string; cwd: string; dbPath: string } {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"durable-agent-store-\"));\n tempDirs.push(root);\n const cwd = path.join(root, \"project\");\n fs.mkdirSync(cwd);\n return { root, cwd, dbPath: path.join(root, \"state\", \"agents.db\") };\n}\n\ndescribe(\"DurableAgentRepository create/list/resolve\", () => {\n it(\"creates distinct durable identities with a canonical cwd and lists them\", async () => {\n const { cwd, dbPath } = fixture();\n const repository = new DurableAgentRepository({\n dbPath,\n now: () => new Date(\"2026-08-07T09:00:00Z\"),\n });\n\n const agent = await repository.create({ name: \"reviewer\", cwd });\n\n expect(agent).toMatchObject({\n name: \"reviewer\",\n provider: \"claude\",\n mode: \"durable\",\n cwd: fs.realpathSync(cwd),\n state: \"ready\",\n sessionHealth: \"uninitialized\",\n activeRun: null,\n });\n expect(agent.id).toMatch(/^[0-9a-f-]{36}$/);\n expect(agent.providerSessionId).toMatch(/^[0-9a-f-]{36}$/);\n expect(agent.id).not.toBe(agent.providerSessionId);\n expect(await repository.list()).toEqual([agent]);\n expect(fs.existsSync(dbPath)).toBe(true);\n });\n\n it(\"resolves exact ids and names and rejects duplicate names\", async () => {\n const { cwd, dbPath } = fixture();\n const repository = new DurableAgentRepository({ dbPath });\n const agent = await repository.create({ name: \"Reviewer\", cwd });\n\n expect(await repository.resolve(agent.id)).toMatchObject({ id: agent.id });\n expect(await repository.resolve(\"reviewer\")).toMatchObject({ id: agent.id });\n expect(await repository.resolve(\"view\")).toBeNull();\n await expect(repository.create({ name: \"reviewer\", cwd })).rejects.toMatchObject({\n code: \"DURABLE_AGENT_NAME_CONFLICT\",\n });\n });\n\n it(\"rejects a missing cwd\", async () => {\n const { root, dbPath } = fixture();\n const repository = new DurableAgentRepository({ dbPath });\n\n await expect(\n repository.create({ name: \"missing\", cwd: path.join(root, \"missing\") }),\n ).rejects.toMatchObject({ code: \"DURABLE_AGENT_REPOSITORY\" });\n });\n});\n\ndescribe(\"DurableAgentRepository run ownership\", () => {\n it(\"fails fast when another exact owner is live and completes only for its token\", async () => {\n const { cwd, dbPath } = fixture();\n const live = new Map<number, string>([[process.pid, \"owner-start\"]]);\n const processInspector = {\n getIdentity: (pid: number) => {\n const startedAt = live.get(pid);\n return startedAt ? { pid, startedAt } : null;\n },\n };\n const repository = new DurableAgentRepository({ dbPath, processInspector });\n const agent = await repository.create({ name: \"runner\", cwd });\n\n const acquired = await repository.acquireRun(agent.id);\n await expect(repository.acquireRun(agent.id)).rejects.toMatchObject({\n code: \"DURABLE_AGENT_BUSY\",\n });\n await expect(\n repository.completeRun(agent.id, \"wrong-token\", {\n status: \"succeeded\",\n exitCode: 0,\n summary: \"done\",\n sessionHealth: \"healthy\",\n }),\n ).rejects.toMatchObject({ code: \"DURABLE_AGENT_REPOSITORY\" });\n\n const completed = await repository.completeRun(agent.id, acquired.token, {\n status: \"succeeded\",\n exitCode: 0,\n summary: \"done\",\n sessionHealth: \"healthy\",\n });\n expect(completed).toMatchObject({ state: \"ready\", sessionHealth: \"healthy\", activeRun: null });\n });\n\n it(\"retains busy for a live provider then recovers a dead run without signaling it\", async () => {\n const { cwd, dbPath } = fixture();\n const live = new Map<number, string>([\n [process.pid, \"owner-start\"],\n [4242, \"provider-start\"],\n ]);\n const processInspector = {\n getIdentity: (pid: number) => {\n const startedAt = live.get(pid);\n return startedAt ? { pid, startedAt } : null;\n },\n };\n const first = new DurableAgentRepository({ dbPath, processInspector });\n const agent = await first.create({ name: \"recoverable\", cwd });\n const run = await first.acquireRun(agent.id);\n await first.recordProviderProcess(agent.id, run.token, {\n pid: 4242,\n startedAt: \"provider-start\",\n });\n\n live.delete(process.pid);\n await expect(first.acquireRun(agent.id)).rejects.toMatchObject({ code: \"DURABLE_AGENT_BUSY\" });\n\n live.delete(4242);\n live.set(process.pid, \"replacement-owner-start\");\n const recovered = await first.acquireRun(agent.id);\n expect(recovered.agent).toMatchObject({\n state: \"running\",\n lastResult: { status: \"interrupted\" },\n });\n await first.completeRun(agent.id, recovered.token, {\n status: \"failed\",\n exitCode: 1,\n summary: \"failed\",\n sessionHealth: \"unknown\",\n });\n });\n\n it(\"reconciles an interrupted run to degraded during list\", async () => {\n const { cwd, dbPath } = fixture();\n const live = new Map<number, string>([[process.pid, \"owner-start\"]]);\n const repository = new DurableAgentRepository({\n dbPath,\n incompleteLockGraceMs: 10,\n processInspector: {\n getIdentity: (pid: number) => {\n const startedAt = live.get(pid);\n return startedAt ? { pid, startedAt } : null;\n },\n },\n });\n const agent = await repository.create({ name: \"crashed\", cwd });\n await repository.acquireRun(agent.id);\n live.clear();\n\n const listed = await repository.list();\n\n expect(listed[0]).toMatchObject({\n state: \"degraded\",\n sessionHealth: \"unknown\",\n activeRun: null,\n lastResult: { status: \"interrupted\" },\n });\n });\n\n it(\"rejects send acquisition when the bound cwd is replaced by a symlink\", async () => {\n const { root, cwd, dbPath } = fixture();\n const repository = new DurableAgentRepository({ dbPath });\n const agent = await repository.create({ name: \"bound\", cwd });\n const moved = path.join(root, \"moved-project\");\n const other = path.join(root, \"other-project\");\n fs.renameSync(cwd, moved);\n fs.mkdirSync(other);\n fs.symlinkSync(other, cwd);\n\n await expect(repository.acquireRun(agent.id)).rejects.toMatchObject({\n code: \"DURABLE_AGENT_REPOSITORY\",\n });\n });\n});\n"],"names":["fs","os","path","afterEach","describe","expect","it","DurableAgentRepository","tempDirs","dir","splice","rmSync","recursive","force","fixture","root","mkdtempSync","join","tmpdir","push","cwd","mkdirSync","dbPath","repository","now","Date","agent","create","name","toMatchObject","provider","mode","realpathSync","state","sessionHealth","activeRun","id","toMatch","providerSessionId","not","toBe","list","toEqual","existsSync","resolve","toBeNull","rejects","code","live","Map","process","pid","processInspector","getIdentity","startedAt","get","acquired","acquireRun","completeRun","status","exitCode","summary","completed","token","first","run","recordProviderProcess","delete","set","recovered","lastResult","incompleteLockGraceMs","clear","listed","moved","other","renameSync","symlinkSync"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/__tests__/runtime/ManagedAgentRuntime.test.ts"],"sourcesContent":["import * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport {\n AgentNameInUseError,\n AgentPidPollTimeoutError,\n AgentTerminalNotFoundError,\n DEFAULT_PID_POLL_TIMEOUT_MS,\n focusAgent,\n sendAgentPrompt,\n startAgent,\n stopAgent,\n TmuxUnavailableError,\n type StartAgentOptions,\n} from \"../../runtime/ManagedAgentRuntime.js\";\nimport type { AgentInfo } from \"../../adapters/AgentAdapter.js\";\nimport type { AgentRegistry, RegistryEntry } from \"../../utils/AgentRegistry.js\";\nimport type { TmuxManager } from \"../../terminal/TmuxManager.js\";\nimport type { InteractiveAgentRuntime } from \"../../runtime/types.js\";\n\nfunction makeAgent(overrides: Partial<AgentInfo> = {}): AgentInfo {\n return {\n name: \"repo-a\",\n type: \"claude\",\n status: \"running\" as AgentInfo[\"status\"],\n summary: \"Working\",\n pid: 10,\n projectPath: \"/repo\",\n sessionId: \"session-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n lastActive: new Date(\"2026-05-14T00:00:00.000Z\"),\n ...overrides,\n };\n}\n\nfunction makeTmux(over: Partial<TmuxManager> = {}): TmuxManager {\n return {\n isAvailable: vi.fn().mockResolvedValue(true),\n sessionExists: vi.fn().mockResolvedValue(false),\n createSession: vi.fn().mockResolvedValue(undefined),\n sendKeys: vi.fn().mockResolvedValue(undefined),\n killSession: vi.fn().mockResolvedValue(undefined),\n findAgentPid: vi.fn().mockResolvedValue(12345),\n ...over,\n } as unknown as TmuxManager;\n}\n\nfunction makeRegistry(over: Partial<AgentRegistry> = {}): AgentRegistry {\n return {\n prune: vi.fn(),\n lookup: vi.fn().mockReturnValue(null),\n list: vi.fn().mockReturnValue([]),\n register: vi.fn(),\n isAlive: vi.fn().mockReturnValue(false),\n ...over,\n } as unknown as AgentRegistry;\n}\n\nfunction makeRuntime(over: Partial<InteractiveAgentRuntime> = {}): InteractiveAgentRuntime {\n return {\n provider: \"herdr\",\n isAvailable: vi.fn().mockResolvedValue({ ok: true, insideRuntime: false }),\n startAgent: vi.fn().mockResolvedValue({\n pid: 12345,\n runtimeRef: { session: \"default\", paneId: \"w1:p2\", agentName: \"agent1\" },\n }),\n send: vi.fn().mockResolvedValue(undefined),\n wait: vi.fn().mockResolvedValue(undefined),\n readOutput: vi.fn().mockResolvedValue(\"done\\n\"),\n focus: vi.fn().mockResolvedValue(true),\n stop: vi.fn().mockResolvedValue(undefined),\n ...over,\n } as unknown as InteractiveAgentRuntime;\n}\n\nconst startOpts: StartAgentOptions = {\n type: \"claude\",\n name: \"agent1\",\n cwd: \"/work\",\n pollIntervalMs: 1,\n pollTimeoutMs: 50,\n};\n\ndescribe(\"managed agent runtime defaults\", () => {\n it(\"allows slower agent startup before PID polling times out\", () => {\n expect(DEFAULT_PID_POLL_TIMEOUT_MS).toBe(15_000);\n });\n});\n\ndescribe(\"stopAgent\", () => {\n it(\"sends SIGTERM to the agent PID\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n const killProcess = vi.fn();\n\n const result = await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n });\n\n expect(killProcess).toHaveBeenCalledWith(123, \"SIGTERM\");\n expect(tmux.killSession).not.toHaveBeenCalled();\n expect(result).toEqual({\n agentName: \"repo-a\",\n pid: 123,\n runtime: \"tmux\",\n runtimeRef: null,\n });\n });\n\n it(\"kills the registry tmux session when present\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 123,\n runtime: \"tmux\",\n runtimeRef: { session: \"repo-a\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n sessionId: \"session-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const killProcess = vi.fn();\n\n const result = await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n });\n\n expect(killProcess).toHaveBeenCalledWith(123, \"SIGTERM\");\n expect(tmux.killSession).toHaveBeenCalledWith(\"repo-a\");\n expect(result.runtimeRef).toEqual({ session: \"repo-a\" });\n });\n\n it(\"delegates Herdr-backed agents to Herdr without sending SIGTERM directly\", async () => {\n const runtime = makeRuntime();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 123,\n runtime: \"herdr\",\n runtimeRef: { session: \"default\", paneId: \"w1:p2\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n sessionId: \"\",\n sessionFilePath: \"\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const killProcess = vi.fn();\n\n const result = await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n runtime,\n registry,\n killProcess,\n });\n\n expect(runtime.stop).toHaveBeenCalledWith(\n expect.objectContaining({\n runtimeRef: { session: \"default\", paneId: \"w1:p2\" },\n }),\n );\n expect(killProcess).not.toHaveBeenCalled();\n expect(result.runtime).toBe(\"herdr\");\n });\n\n it(\"still kills tmux session when the process is already gone\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 123,\n runtime: \"tmux\",\n runtimeRef: { session: \"repo-a\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n sessionId: \"session-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const error = Object.assign(new Error(\"gone\"), { code: \"ESRCH\" });\n const killProcess = vi.fn(() => {\n throw error;\n });\n\n await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n });\n\n expect(tmux.killSession).toHaveBeenCalledWith(\"repo-a\");\n });\n\n it(\"rethrows unexpected process kill errors\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n const error = Object.assign(new Error(\"permission denied\"), { code: \"EPERM\" });\n const killProcess = vi.fn(() => {\n throw error;\n });\n\n await expect(\n stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n }),\n ).rejects.toThrow(\"permission denied\");\n\n expect(tmux.killSession).not.toHaveBeenCalled();\n });\n});\n\ndescribe(\"focusAgent\", () => {\n it(\"delegates Herdr-backed agents to Herdr focus\", async () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"repo-a\" };\n const runtime = makeRuntime();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 10,\n runtime: \"herdr\",\n runtimeRef,\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const focusManager = {\n findTerminal: vi.fn(),\n focusTerminal: vi.fn(),\n };\n\n const result = await focusAgent(makeAgent(), { registry, runtime, focusManager });\n\n expect(runtime.focus).toHaveBeenCalledWith(expect.objectContaining({ runtimeRef }));\n expect(focusManager.findTerminal).not.toHaveBeenCalled();\n expect(result).toEqual({ focused: true });\n });\n\n it(\"reports when a tmux-backed terminal cannot be found\", async () => {\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(null),\n focusTerminal: vi.fn(),\n };\n\n const result = await focusAgent(makeAgent({ pid: 10 }), {\n registry: makeRegistry(),\n focusManager,\n });\n\n expect(focusManager.findTerminal).toHaveBeenCalledWith(10);\n expect(focusManager.focusTerminal).not.toHaveBeenCalled();\n expect(result).toEqual({ focused: false, reason: \"terminal-not-found\" });\n });\n\n it(\"reports when focusing a found terminal fails\", async () => {\n const location = { type: \"tmux\", identifier: \"1:1\", tty: \"/dev/ttys030\" };\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(location),\n focusTerminal: vi.fn().mockResolvedValue(false),\n };\n\n const result = await focusAgent(makeAgent({ pid: 10 }), {\n registry: makeRegistry(),\n focusManager,\n });\n\n expect(focusManager.focusTerminal).toHaveBeenCalledWith(location);\n expect(result).toEqual({ focused: false, reason: \"focus-failed\" });\n });\n});\n\ndescribe(\"sendAgentPrompt\", () => {\n it(\"sends Herdr-backed prompts through Herdr\", async () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"repo-a\" };\n const runtime = makeRuntime();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 10,\n runtime: \"herdr\",\n runtimeRef,\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const focusManager = {\n findTerminal: vi.fn(),\n };\n\n await sendAgentPrompt(makeAgent(), \"hello\", {\n registry,\n runtime,\n focusManager,\n });\n\n expect(runtime.send).toHaveBeenCalledWith(\n expect.objectContaining({ runtimeRef, prompt: \"hello\" }),\n expect.objectContaining({ focusManager }),\n );\n expect(focusManager.findTerminal).not.toHaveBeenCalled();\n });\n\n it(\"writes tmux-backed prompts to the resolved terminal\", async () => {\n const location = { type: \"tmux\", identifier: \"1:1\", tty: \"/dev/ttys030\" };\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(location),\n };\n const writer = vi.fn().mockResolvedValue(undefined);\n\n await sendAgentPrompt(makeAgent({ pid: 10 }), \"hello\", {\n registry: makeRegistry(),\n focusManager,\n writer,\n });\n\n expect(focusManager.findTerminal).toHaveBeenCalledWith(10);\n expect(writer).toHaveBeenCalledWith(location, \"hello\");\n });\n\n it(\"throws when a tmux-backed terminal cannot be found\", async () => {\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(null),\n };\n\n const err = await sendAgentPrompt(makeAgent({ pid: 10 }), \"hello\", {\n registry: makeRegistry(),\n focusManager,\n }).catch((error) => error);\n\n expect(err).toBeInstanceOf(AgentTerminalNotFoundError);\n expect(err.message).toBe('Cannot find terminal for agent \"repo-a\" (PID: 10).');\n });\n});\n\ndescribe(\"startAgent\", () => {\n it(\"happy path: creates session, sends command, polls, registers, returns entry\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n\n const entry = await startAgent({ ...startOpts, pollTimeoutMs: 250 }, { tmux, registry });\n\n expect(tmux.createSession).toHaveBeenCalledWith(\"agent1\", \"/work\");\n expect(tmux.sendKeys).toHaveBeenCalledWith(\"agent1\", \"claude\");\n expect(registry.prune).toHaveBeenCalled();\n expect(registry.register).toHaveBeenCalledOnce();\n expect(entry).toMatchObject({\n name: \"agent1\",\n type: \"claude\",\n pid: 12345,\n runtime: \"tmux\",\n runtimeRef: { session: \"agent1\" },\n cwd: \"/work\",\n pinned: false,\n });\n expect(entry.startedAt).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);\n });\n\n it(\"throws TmuxUnavailableError when tmux is missing\", async () => {\n const tmux = makeTmux({\n isAvailable: vi.fn().mockResolvedValue(false),\n } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n await expect(startAgent(startOpts, { tmux, registry })).rejects.toBeInstanceOf(\n TmuxUnavailableError,\n );\n expect(tmux.createSession).not.toHaveBeenCalled();\n expect(registry.register).not.toHaveBeenCalled();\n });\n\n it(\"starts Herdr-backed agents through the configured runtime and persists runtime metadata\", async () => {\n const runtime = makeRuntime();\n const registry = makeRegistry();\n\n const entry = await startAgent(\n { ...startOpts, runtimeProvider: \"herdr\" },\n { runtime, registry },\n );\n\n expect(runtime.isAvailable).toHaveBeenCalledOnce();\n expect(runtime.startAgent).toHaveBeenCalledWith({\n name: \"agent1\",\n cwd: \"/work\",\n kind: \"claude\",\n args: [],\n timeoutMs: 50,\n });\n expect(entry).toMatchObject({\n name: \"agent1\",\n type: \"claude\",\n pid: 12345,\n runtime: \"herdr\",\n runtimeRef: { session: \"default\", paneId: \"w1:p2\", agentName: \"agent1\" },\n cwd: \"/work\",\n pinned: false,\n });\n expect(registry.register).toHaveBeenCalledWith(\n expect.objectContaining({\n runtime: \"herdr\",\n runtimeRef: { session: \"default\", paneId: \"w1:p2\", agentName: \"agent1\" },\n }),\n );\n });\n\n it(\"fails Herdr starts when the configured runtime is unavailable\", async () => {\n const runtime = makeRuntime({\n isAvailable: vi.fn().mockResolvedValue({\n ok: false,\n reason: \"binary-missing\",\n detail: \"herdr command was not found in PATH.\",\n }),\n });\n const registry = makeRegistry();\n\n await expect(\n startAgent({ ...startOpts, runtimeProvider: \"herdr\" }, { runtime, registry }),\n ).rejects.toMatchObject({\n name: \"AgentRuntimeUnavailableError\",\n provider: \"herdr\",\n reason: \"binary-missing\",\n });\n expect(runtime.startAgent).not.toHaveBeenCalled();\n expect(registry.register).not.toHaveBeenCalled();\n });\n\n it(\"throws AgentNameInUseError when registry already has a live entry\", async () => {\n const tmux = makeTmux();\n const liveEntry: RegistryEntry = {\n name: \"agent1\",\n type: \"claude\",\n pid: 999,\n runtime: \"tmux\",\n runtimeRef: { session: \"agent1\" },\n cwd: \"/old\",\n startedAt: \"2026-01-01T00:00:00.000Z\",\n };\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue(liveEntry),\n } as Partial<AgentRegistry>);\n\n const err = await startAgent(startOpts, { tmux, registry }).catch((e) => e);\n expect(err).toBeInstanceOf(AgentNameInUseError);\n expect(err.pid).toBe(999);\n expect(tmux.createSession).not.toHaveBeenCalled();\n });\n\n it(\"detects the agent PID when the agent is installed under a path containing spaces\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"managed-runtime-spaces-\"));\n try {\n const codexPath = path.join(root, \"Applications/Some App.app/Contents/Resources/codex\");\n fs.mkdirSync(path.dirname(codexPath), { recursive: true });\n fs.writeFileSync(codexPath, \"\");\n const processTree = [\n { pid: 100, command: \"-zsh\" },\n { pid: 200, command: `${codexPath} --sandbox workspace-write` },\n { pid: 300, command: \"/usr/local/bin/node /opt/mcp/server.js\" },\n ];\n const findAgentPid = vi.fn(\n async (_session: string, matches: (psCommand: string) => boolean) =>\n processTree.filter((proc) => matches(proc.command)).at(-1)?.pid ?? null,\n );\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const entry = await startAgent({ ...startOpts, type: \"codex\" }, { tmux, registry });\n\n expect(tmux.sendKeys).toHaveBeenCalledWith(\"agent1\", \"codex\");\n expect(entry).toMatchObject({ type: \"codex\", pid: 200 });\n } finally {\n fs.rmSync(root, { recursive: true, force: true });\n }\n });\n\n it(\"replaces orphan tmux session and calls onWarning\", async () => {\n const tmux = makeTmux({\n sessionExists: vi.fn().mockResolvedValue(true),\n } as Partial<TmuxManager>);\n const registry = makeRegistry();\n const onWarning = vi.fn();\n\n await startAgent(startOpts, { tmux, registry, onWarning });\n\n expect(onWarning).toHaveBeenCalledOnce();\n expect(onWarning.mock.calls[0][0]).toContain(\"agent1\");\n expect(tmux.killSession).toHaveBeenCalledWith(\"agent1\");\n expect(tmux.createSession).toHaveBeenCalledWith(\"agent1\", \"/work\");\n });\n\n it(\"on PID poll timeout: kills session and throws AgentPidPollTimeoutError\", async () => {\n const tmux = makeTmux({\n findAgentPid: vi.fn().mockResolvedValue(null),\n } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const err = await startAgent(startOpts, { tmux, registry }).catch((e) => e);\n\n expect(err).toBeInstanceOf(AgentPidPollTimeoutError);\n expect(err.command).toBe(\"claude\");\n expect(err.timeoutMs).toBe(50);\n expect(tmux.killSession).toHaveBeenLastCalledWith(\"agent1\");\n expect(registry.register).not.toHaveBeenCalled();\n });\n\n it(\"keeps polling until findAgentPid returns a PID\", async () => {\n const findAgentPid = vi\n .fn()\n .mockResolvedValueOnce(null)\n .mockResolvedValueOnce(null)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42);\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const entry = await startAgent({ ...startOpts, pollTimeoutMs: 250 }, { tmux, registry });\n\n expect(findAgentPid).toHaveBeenCalledTimes(7);\n expect(entry.pid).toBe(42);\n });\n\n it(\"waits for the launched process PID to stabilize before registering\", async () => {\n const findAgentPid = vi\n .fn()\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200);\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const entry = await startAgent(startOpts, { tmux, registry });\n\n expect(findAgentPid).toHaveBeenCalledTimes(8);\n expect(entry.pid).toBe(200);\n expect(registry.register).toHaveBeenCalledWith(expect.objectContaining({ pid: 200 }));\n });\n\n it(\"treats an unstabilized PID as a poll timeout\", async () => {\n const findAgentPid = vi\n .fn()\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100);\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const err = await startAgent({ ...startOpts, pollTimeoutMs: 3 }, { tmux, registry }).catch(\n (e) => e,\n );\n\n expect(err).toBeInstanceOf(AgentPidPollTimeoutError);\n expect(registry.register).not.toHaveBeenCalled();\n expect(tmux.killSession).toHaveBeenLastCalledWith(\"agent1\");\n });\n\n it(\"prunes registry before checking for name collision\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n const order: string[] = [];\n (registry.prune as any).mockImplementation(() => order.push(\"prune\"));\n (registry.lookup as any).mockImplementation(() => {\n order.push(\"lookup\");\n return null;\n });\n\n await startAgent(startOpts, { tmux, registry });\n expect(order).toEqual([\"prune\", \"lookup\"]);\n });\n});\n"],"names":["fs","os","path","AgentNameInUseError","AgentPidPollTimeoutError","AgentTerminalNotFoundError","DEFAULT_PID_POLL_TIMEOUT_MS","focusAgent","sendAgentPrompt","startAgent","stopAgent","TmuxUnavailableError","makeAgent","overrides","name","type","status","summary","pid","projectPath","sessionId","sessionFilePath","lastActive","Date","makeTmux","over","isAvailable","vi","fn","mockResolvedValue","sessionExists","createSession","undefined","sendKeys","killSession","findAgentPid","makeRegistry","prune","lookup","mockReturnValue","list","register","isAlive","makeRuntime","provider","ok","insideRuntime","runtimeRef","session","paneId","agentName","send","wait","readOutput","focus","stop","startOpts","cwd","pollIntervalMs","pollTimeoutMs","describe","it","expect","toBe","tmux","registry","killProcess","result","toHaveBeenCalledWith","not","toHaveBeenCalled","toEqual","runtime","startedAt","objectContaining","error","Object","assign","Error","code","rejects","toThrow","focusManager","findTerminal","focusTerminal","focused","reason","location","identifier","tty","prompt","writer","err","catch","toBeInstanceOf","message","entry","toHaveBeenCalledOnce","toMatchObject","pinned","toMatch","runtimeProvider","kind","args","timeoutMs","detail","liveEntry","e","root","mkdtempSync","join","tmpdir","codexPath","mkdirSync","dirname","recursive","writeFileSync","processTree","command","_session","matches","filter","proc","at","rmSync","force","onWarning","mock","calls","toContain","toHaveBeenLastCalledWith","mockResolvedValueOnce","toHaveBeenCalledTimes","order","mockImplementation","push"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/runtime/ManagedAgentRuntime.test.ts"],"sourcesContent":["import * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport {\n AgentNameInUseError,\n AgentPidPollTimeoutError,\n AgentTerminalNotFoundError,\n DEFAULT_PID_POLL_TIMEOUT_MS,\n focusAgent,\n sendAgentPrompt,\n startAgent,\n stopAgent,\n TmuxUnavailableError,\n type StartAgentOptions,\n} from \"../../runtime/ManagedAgentRuntime.js\";\nimport type { AgentInfo } from \"../../adapters/AgentAdapter.js\";\nimport type { AgentRegistry, RegistryEntry } from \"../../utils/AgentRegistry.js\";\nimport type { TmuxManager } from \"../../runtime/tmux/TmuxManager.js\";\nimport type { InteractiveAgentRuntime } from \"../../runtime/types.js\";\n\nfunction makeAgent(overrides: Partial<AgentInfo> = {}): AgentInfo {\n return {\n name: \"repo-a\",\n type: \"claude\",\n status: \"running\" as AgentInfo[\"status\"],\n summary: \"Working\",\n pid: 10,\n projectPath: \"/repo\",\n sessionId: \"session-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n lastActive: new Date(\"2026-05-14T00:00:00.000Z\"),\n ...overrides,\n };\n}\n\nfunction makeTmux(over: Partial<TmuxManager> = {}): TmuxManager {\n return {\n isAvailable: vi.fn().mockResolvedValue(true),\n sessionExists: vi.fn().mockResolvedValue(false),\n createSession: vi.fn().mockResolvedValue(undefined),\n sendKeys: vi.fn().mockResolvedValue(undefined),\n killSession: vi.fn().mockResolvedValue(undefined),\n findAgentPid: vi.fn().mockResolvedValue(12345),\n ...over,\n } as unknown as TmuxManager;\n}\n\nfunction makeRegistry(over: Partial<AgentRegistry> = {}): AgentRegistry {\n return {\n prune: vi.fn(),\n lookup: vi.fn().mockReturnValue(null),\n list: vi.fn().mockReturnValue([]),\n register: vi.fn(),\n isAlive: vi.fn().mockReturnValue(false),\n ...over,\n } as unknown as AgentRegistry;\n}\n\nfunction makeRuntime(over: Partial<InteractiveAgentRuntime> = {}): InteractiveAgentRuntime {\n return {\n provider: \"herdr\",\n isAvailable: vi.fn().mockResolvedValue({ ok: true, insideRuntime: false }),\n startAgent: vi.fn().mockResolvedValue({\n pid: 12345,\n runtimeRef: { session: \"default\", paneId: \"w1:p2\", agentName: \"agent1\" },\n }),\n send: vi.fn().mockResolvedValue(undefined),\n wait: vi.fn().mockResolvedValue(undefined),\n readOutput: vi.fn().mockResolvedValue(\"done\\n\"),\n focus: vi.fn().mockResolvedValue(true),\n stop: vi.fn().mockResolvedValue(undefined),\n ...over,\n } as unknown as InteractiveAgentRuntime;\n}\n\nconst startOpts: StartAgentOptions = {\n type: \"claude\",\n name: \"agent1\",\n cwd: \"/work\",\n pollIntervalMs: 1,\n pollTimeoutMs: 50,\n};\n\ndescribe(\"managed agent runtime defaults\", () => {\n it(\"allows slower agent startup before PID polling times out\", () => {\n expect(DEFAULT_PID_POLL_TIMEOUT_MS).toBe(15_000);\n });\n});\n\ndescribe(\"stopAgent\", () => {\n it(\"sends SIGTERM to the agent PID\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n const killProcess = vi.fn();\n\n const result = await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n });\n\n expect(killProcess).toHaveBeenCalledWith(123, \"SIGTERM\");\n expect(tmux.killSession).not.toHaveBeenCalled();\n expect(result).toEqual({\n agentName: \"repo-a\",\n pid: 123,\n runtime: \"tmux\",\n runtimeRef: null,\n });\n });\n\n it(\"kills the registry tmux session when present\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 123,\n runtime: \"tmux\",\n runtimeRef: { session: \"repo-a\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n sessionId: \"session-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const killProcess = vi.fn();\n\n const result = await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n });\n\n expect(killProcess).toHaveBeenCalledWith(123, \"SIGTERM\");\n expect(tmux.killSession).toHaveBeenCalledWith(\"repo-a\");\n expect(result.runtimeRef).toEqual({ session: \"repo-a\" });\n });\n\n it(\"delegates Herdr-backed agents to Herdr without sending SIGTERM directly\", async () => {\n const runtime = makeRuntime();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 123,\n runtime: \"herdr\",\n runtimeRef: { session: \"default\", paneId: \"w1:p2\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n sessionId: \"\",\n sessionFilePath: \"\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const killProcess = vi.fn();\n\n const result = await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n runtime,\n registry,\n killProcess,\n });\n\n expect(runtime.stop).toHaveBeenCalledWith(\n expect.objectContaining({\n runtimeRef: { session: \"default\", paneId: \"w1:p2\" },\n }),\n );\n expect(killProcess).not.toHaveBeenCalled();\n expect(result.runtime).toBe(\"herdr\");\n });\n\n it(\"still kills tmux session when the process is already gone\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 123,\n runtime: \"tmux\",\n runtimeRef: { session: \"repo-a\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n sessionId: \"session-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const error = Object.assign(new Error(\"gone\"), { code: \"ESRCH\" });\n const killProcess = vi.fn(() => {\n throw error;\n });\n\n await stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n });\n\n expect(tmux.killSession).toHaveBeenCalledWith(\"repo-a\");\n });\n\n it(\"rethrows unexpected process kill errors\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n const error = Object.assign(new Error(\"permission denied\"), { code: \"EPERM\" });\n const killProcess = vi.fn(() => {\n throw error;\n });\n\n await expect(\n stopAgent(makeAgent({ name: \"repo-a\", pid: 123 }), {\n tmux,\n registry,\n killProcess,\n }),\n ).rejects.toThrow(\"permission denied\");\n\n expect(tmux.killSession).not.toHaveBeenCalled();\n });\n});\n\ndescribe(\"focusAgent\", () => {\n it(\"delegates Herdr-backed agents to Herdr focus\", async () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"repo-a\" };\n const runtime = makeRuntime();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 10,\n runtime: \"herdr\",\n runtimeRef,\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const focusManager = {\n findTerminal: vi.fn(),\n focusTerminal: vi.fn(),\n };\n\n const result = await focusAgent(makeAgent(), { registry, runtime, focusManager });\n\n expect(runtime.focus).toHaveBeenCalledWith(expect.objectContaining({ runtimeRef }));\n expect(focusManager.findTerminal).not.toHaveBeenCalled();\n expect(result).toEqual({ focused: true });\n });\n\n it(\"reports when a tmux-backed terminal cannot be found\", async () => {\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(null),\n focusTerminal: vi.fn(),\n };\n\n const result = await focusAgent(makeAgent({ pid: 10 }), {\n registry: makeRegistry(),\n focusManager,\n });\n\n expect(focusManager.findTerminal).toHaveBeenCalledWith(10);\n expect(focusManager.focusTerminal).not.toHaveBeenCalled();\n expect(result).toEqual({ focused: false, reason: \"terminal-not-found\" });\n });\n\n it(\"reports when focusing a found terminal fails\", async () => {\n const location = { type: \"tmux\", identifier: \"1:1\", tty: \"/dev/ttys030\" };\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(location),\n focusTerminal: vi.fn().mockResolvedValue(false),\n };\n\n const result = await focusAgent(makeAgent({ pid: 10 }), {\n registry: makeRegistry(),\n focusManager,\n });\n\n expect(focusManager.focusTerminal).toHaveBeenCalledWith(location);\n expect(result).toEqual({ focused: false, reason: \"focus-failed\" });\n });\n});\n\ndescribe(\"sendAgentPrompt\", () => {\n it(\"sends Herdr-backed prompts through Herdr\", async () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"repo-a\" };\n const runtime = makeRuntime();\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue({\n name: \"repo-a\",\n type: \"claude\",\n pid: 10,\n runtime: \"herdr\",\n runtimeRef,\n cwd: \"/repo\",\n startedAt: \"2026-06-01T00:00:00.000Z\",\n } satisfies RegistryEntry),\n } as Partial<AgentRegistry>);\n const focusManager = {\n findTerminal: vi.fn(),\n };\n\n await sendAgentPrompt(makeAgent(), \"hello\", {\n registry,\n runtime,\n focusManager,\n });\n\n expect(runtime.send).toHaveBeenCalledWith(\n expect.objectContaining({ runtimeRef, prompt: \"hello\" }),\n expect.objectContaining({ focusManager }),\n );\n expect(focusManager.findTerminal).not.toHaveBeenCalled();\n });\n\n it(\"writes tmux-backed prompts to the resolved terminal\", async () => {\n const location = { type: \"tmux\", identifier: \"1:1\", tty: \"/dev/ttys030\" };\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(location),\n };\n const writer = vi.fn().mockResolvedValue(undefined);\n\n await sendAgentPrompt(makeAgent({ pid: 10 }), \"hello\", {\n registry: makeRegistry(),\n focusManager,\n writer,\n });\n\n expect(focusManager.findTerminal).toHaveBeenCalledWith(10);\n expect(writer).toHaveBeenCalledWith(location, \"hello\");\n });\n\n it(\"throws when a tmux-backed terminal cannot be found\", async () => {\n const focusManager = {\n findTerminal: vi.fn().mockResolvedValue(null),\n };\n\n const err = await sendAgentPrompt(makeAgent({ pid: 10 }), \"hello\", {\n registry: makeRegistry(),\n focusManager,\n }).catch((error) => error);\n\n expect(err).toBeInstanceOf(AgentTerminalNotFoundError);\n expect(err.message).toBe('Cannot find terminal for agent \"repo-a\" (PID: 10).');\n });\n});\n\ndescribe(\"startAgent\", () => {\n it(\"happy path: creates session, sends command, polls, registers, returns entry\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n\n const entry = await startAgent({ ...startOpts, pollTimeoutMs: 250 }, { tmux, registry });\n\n expect(tmux.createSession).toHaveBeenCalledWith(\"agent1\", \"/work\");\n expect(tmux.sendKeys).toHaveBeenCalledWith(\"agent1\", \"claude\");\n expect(registry.prune).toHaveBeenCalled();\n expect(registry.register).toHaveBeenCalledOnce();\n expect(entry).toMatchObject({\n name: \"agent1\",\n type: \"claude\",\n pid: 12345,\n runtime: \"tmux\",\n runtimeRef: { session: \"agent1\" },\n cwd: \"/work\",\n pinned: false,\n });\n expect(entry.startedAt).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);\n });\n\n it(\"throws TmuxUnavailableError when tmux is missing\", async () => {\n const tmux = makeTmux({\n isAvailable: vi.fn().mockResolvedValue(false),\n } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n await expect(startAgent(startOpts, { tmux, registry })).rejects.toBeInstanceOf(\n TmuxUnavailableError,\n );\n expect(tmux.createSession).not.toHaveBeenCalled();\n expect(registry.register).not.toHaveBeenCalled();\n });\n\n it(\"starts Herdr-backed agents through the configured runtime and persists runtime metadata\", async () => {\n const runtime = makeRuntime();\n const registry = makeRegistry();\n\n const entry = await startAgent(\n { ...startOpts, runtimeProvider: \"herdr\" },\n { runtime, registry },\n );\n\n expect(runtime.isAvailable).toHaveBeenCalledOnce();\n expect(runtime.startAgent).toHaveBeenCalledWith({\n name: \"agent1\",\n cwd: \"/work\",\n kind: \"claude\",\n args: [],\n timeoutMs: 50,\n });\n expect(entry).toMatchObject({\n name: \"agent1\",\n type: \"claude\",\n pid: 12345,\n runtime: \"herdr\",\n runtimeRef: { session: \"default\", paneId: \"w1:p2\", agentName: \"agent1\" },\n cwd: \"/work\",\n pinned: false,\n });\n expect(registry.register).toHaveBeenCalledWith(\n expect.objectContaining({\n runtime: \"herdr\",\n runtimeRef: { session: \"default\", paneId: \"w1:p2\", agentName: \"agent1\" },\n }),\n );\n });\n\n it(\"fails Herdr starts when the configured runtime is unavailable\", async () => {\n const runtime = makeRuntime({\n isAvailable: vi.fn().mockResolvedValue({\n ok: false,\n reason: \"binary-missing\",\n detail: \"herdr command was not found in PATH.\",\n }),\n });\n const registry = makeRegistry();\n\n await expect(\n startAgent({ ...startOpts, runtimeProvider: \"herdr\" }, { runtime, registry }),\n ).rejects.toMatchObject({\n name: \"AgentRuntimeUnavailableError\",\n provider: \"herdr\",\n reason: \"binary-missing\",\n });\n expect(runtime.startAgent).not.toHaveBeenCalled();\n expect(registry.register).not.toHaveBeenCalled();\n });\n\n it(\"throws AgentNameInUseError when registry already has a live entry\", async () => {\n const tmux = makeTmux();\n const liveEntry: RegistryEntry = {\n name: \"agent1\",\n type: \"claude\",\n pid: 999,\n runtime: \"tmux\",\n runtimeRef: { session: \"agent1\" },\n cwd: \"/old\",\n startedAt: \"2026-01-01T00:00:00.000Z\",\n };\n const registry = makeRegistry({\n lookup: vi.fn().mockReturnValue(liveEntry),\n } as Partial<AgentRegistry>);\n\n const err = await startAgent(startOpts, { tmux, registry }).catch((e) => e);\n expect(err).toBeInstanceOf(AgentNameInUseError);\n expect(err.pid).toBe(999);\n expect(tmux.createSession).not.toHaveBeenCalled();\n });\n\n it(\"detects the agent PID when the agent is installed under a path containing spaces\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"managed-runtime-spaces-\"));\n try {\n const codexPath = path.join(root, \"Applications/Some App.app/Contents/Resources/codex\");\n fs.mkdirSync(path.dirname(codexPath), { recursive: true });\n fs.writeFileSync(codexPath, \"\");\n const processTree = [\n { pid: 100, command: \"-zsh\" },\n { pid: 200, command: `${codexPath} --sandbox workspace-write` },\n { pid: 300, command: \"/usr/local/bin/node /opt/mcp/server.js\" },\n ];\n const findAgentPid = vi.fn(\n async (_session: string, matches: (psCommand: string) => boolean) =>\n processTree.filter((proc) => matches(proc.command)).at(-1)?.pid ?? null,\n );\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const entry = await startAgent({ ...startOpts, type: \"codex\" }, { tmux, registry });\n\n expect(tmux.sendKeys).toHaveBeenCalledWith(\"agent1\", \"codex\");\n expect(entry).toMatchObject({ type: \"codex\", pid: 200 });\n } finally {\n fs.rmSync(root, { recursive: true, force: true });\n }\n });\n\n it(\"replaces orphan tmux session and calls onWarning\", async () => {\n const tmux = makeTmux({\n sessionExists: vi.fn().mockResolvedValue(true),\n } as Partial<TmuxManager>);\n const registry = makeRegistry();\n const onWarning = vi.fn();\n\n await startAgent(startOpts, { tmux, registry, onWarning });\n\n expect(onWarning).toHaveBeenCalledOnce();\n expect(onWarning.mock.calls[0][0]).toContain(\"agent1\");\n expect(tmux.killSession).toHaveBeenCalledWith(\"agent1\");\n expect(tmux.createSession).toHaveBeenCalledWith(\"agent1\", \"/work\");\n });\n\n it(\"on PID poll timeout: kills session and throws AgentPidPollTimeoutError\", async () => {\n const tmux = makeTmux({\n findAgentPid: vi.fn().mockResolvedValue(null),\n } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const err = await startAgent(startOpts, { tmux, registry }).catch((e) => e);\n\n expect(err).toBeInstanceOf(AgentPidPollTimeoutError);\n expect(err.command).toBe(\"claude\");\n expect(err.timeoutMs).toBe(50);\n expect(tmux.killSession).toHaveBeenLastCalledWith(\"agent1\");\n expect(registry.register).not.toHaveBeenCalled();\n });\n\n it(\"keeps polling until findAgentPid returns a PID\", async () => {\n const findAgentPid = vi\n .fn()\n .mockResolvedValueOnce(null)\n .mockResolvedValueOnce(null)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42)\n .mockResolvedValueOnce(42);\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const entry = await startAgent({ ...startOpts, pollTimeoutMs: 250 }, { tmux, registry });\n\n expect(findAgentPid).toHaveBeenCalledTimes(7);\n expect(entry.pid).toBe(42);\n });\n\n it(\"waits for the launched process PID to stabilize before registering\", async () => {\n const findAgentPid = vi\n .fn()\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200)\n .mockResolvedValueOnce(200);\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const entry = await startAgent(startOpts, { tmux, registry });\n\n expect(findAgentPid).toHaveBeenCalledTimes(8);\n expect(entry.pid).toBe(200);\n expect(registry.register).toHaveBeenCalledWith(expect.objectContaining({ pid: 200 }));\n });\n\n it(\"treats an unstabilized PID as a poll timeout\", async () => {\n const findAgentPid = vi\n .fn()\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100)\n .mockResolvedValueOnce(100);\n const tmux = makeTmux({ findAgentPid } as Partial<TmuxManager>);\n const registry = makeRegistry();\n\n const err = await startAgent({ ...startOpts, pollTimeoutMs: 3 }, { tmux, registry }).catch(\n (e) => e,\n );\n\n expect(err).toBeInstanceOf(AgentPidPollTimeoutError);\n expect(registry.register).not.toHaveBeenCalled();\n expect(tmux.killSession).toHaveBeenLastCalledWith(\"agent1\");\n });\n\n it(\"prunes registry before checking for name collision\", async () => {\n const tmux = makeTmux();\n const registry = makeRegistry();\n const order: string[] = [];\n (registry.prune as any).mockImplementation(() => order.push(\"prune\"));\n (registry.lookup as any).mockImplementation(() => {\n order.push(\"lookup\");\n return null;\n });\n\n await startAgent(startOpts, { tmux, registry });\n expect(order).toEqual([\"prune\", \"lookup\"]);\n });\n});\n"],"names":["fs","os","path","AgentNameInUseError","AgentPidPollTimeoutError","AgentTerminalNotFoundError","DEFAULT_PID_POLL_TIMEOUT_MS","focusAgent","sendAgentPrompt","startAgent","stopAgent","TmuxUnavailableError","makeAgent","overrides","name","type","status","summary","pid","projectPath","sessionId","sessionFilePath","lastActive","Date","makeTmux","over","isAvailable","vi","fn","mockResolvedValue","sessionExists","createSession","undefined","sendKeys","killSession","findAgentPid","makeRegistry","prune","lookup","mockReturnValue","list","register","isAlive","makeRuntime","provider","ok","insideRuntime","runtimeRef","session","paneId","agentName","send","wait","readOutput","focus","stop","startOpts","cwd","pollIntervalMs","pollTimeoutMs","describe","it","expect","toBe","tmux","registry","killProcess","result","toHaveBeenCalledWith","not","toHaveBeenCalled","toEqual","runtime","startedAt","objectContaining","error","Object","assign","Error","code","rejects","toThrow","focusManager","findTerminal","focusTerminal","focused","reason","location","identifier","tty","prompt","writer","err","catch","toBeInstanceOf","message","entry","toHaveBeenCalledOnce","toMatchObject","pinned","toMatch","runtimeProvider","kind","args","timeoutMs","detail","liveEntry","e","root","mkdtempSync","join","tmpdir","codexPath","mkdirSync","dirname","recursive","writeFileSync","processTree","command","_session","matches","filter","proc","at","rmSync","force","onWarning","mock","calls","toContain","toHaveBeenLastCalledWith","mockResolvedValueOnce","toHaveBeenCalledTimes","order","mockImplementation","push"],"mappings":"AAAA,YAAYA,QAAQ,KAAK;AACzB,YAAYC,QAAQ,KAAK;AACzB,YAAYC,UAAU,OAAO;AAC7B,SACEC,mBAAmB,EACnBC,wBAAwB,EACxBC,0BAA0B,EAC1BC,2BAA2B,EAC3BC,UAAU,EACVC,eAAe,EACfC,UAAU,EACVC,SAAS,EACTC,oBAAoB,QAEf,uCAAuC;AAM9C,SAASC,UAAUC,YAAgC,CAAC,CAAC;IACnD,OAAO;QACLC,MAAM;QACNC,MAAM;QACNC,QAAQ;QACRC,SAAS;QACTC,KAAK;QACLC,aAAa;QACbC,WAAW;QACXC,iBAAiB;QACjBC,YAAY,IAAIC,KAAK;QACrB,GAAGV,SAAS;IACd;AACF;AAEA,SAASW,SAASC,OAA6B,CAAC,CAAC;IAC/C,OAAO;QACLC,aAAaC,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QACvCC,eAAeH,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QACzCE,eAAeJ,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QACzCC,UAAUN,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QACpCE,aAAaP,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QACvCG,cAAcR,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QACxC,GAAGJ,IAAI;IACT;AACF;AAEA,SAASW,aAAaX,OAA+B,CAAC,CAAC;IACrD,OAAO;QACLY,OAAOV,GAAGC,EAAE;QACZU,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAAC;QAChCC,MAAMb,GAAGC,EAAE,GAAGW,eAAe,CAAC,EAAE;QAChCE,UAAUd,GAAGC,EAAE;QACfc,SAASf,GAAGC,EAAE,GAAGW,eAAe,CAAC;QACjC,GAAGd,IAAI;IACT;AACF;AAEA,SAASkB,YAAYlB,OAAyC,CAAC,CAAC;IAC9D,OAAO;QACLmB,UAAU;QACVlB,aAAaC,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;YAAEgB,IAAI;YAAMC,eAAe;QAAM;QACxErC,YAAYkB,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;YACpCX,KAAK;YACL6B,YAAY;gBAAEC,SAAS;gBAAWC,QAAQ;gBAASC,WAAW;YAAS;QACzE;QACAC,MAAMxB,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QAChCoB,MAAMzB,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QAChCqB,YAAY1B,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QACtCyB,OAAO3B,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QACjC0B,MAAM5B,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QAChC,GAAGP,IAAI;IACT;AACF;AAEA,MAAM+B,YAA+B;IACnCzC,MAAM;IACND,MAAM;IACN2C,KAAK;IACLC,gBAAgB;IAChBC,eAAe;AACjB;AAEAC,SAAS,kCAAkC;IACzCC,GAAG,4DAA4D;QAC7DC,OAAOxD,6BAA6ByD,IAAI,CAAC;IAC3C;AACF;AAEAH,SAAS,aAAa;IACpBC,GAAG,kCAAkC;QACnC,MAAMG,OAAOxC;QACb,MAAMyC,WAAW7B;QACjB,MAAM8B,cAAcvC,GAAGC,EAAE;QAEzB,MAAMuC,SAAS,MAAMzD,UAAUE,UAAU;YAAEE,MAAM;YAAUI,KAAK;QAAI,IAAI;YACtE8C;YACAC;YACAC;QACF;QAEAJ,OAAOI,aAAaE,oBAAoB,CAAC,KAAK;QAC9CN,OAAOE,KAAK9B,WAAW,EAAEmC,GAAG,CAACC,gBAAgB;QAC7CR,OAAOK,QAAQI,OAAO,CAAC;YACrBrB,WAAW;YACXhC,KAAK;YACLsD,SAAS;YACTzB,YAAY;QACd;IACF;IAEAc,GAAG,gDAAgD;QACjD,MAAMG,OAAOxC;QACb,MAAMyC,WAAW7B,aAAa;YAC5BE,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAAC;gBAC9BzB,MAAM;gBACNC,MAAM;gBACNG,KAAK;gBACLsD,SAAS;gBACTzB,YAAY;oBAAEC,SAAS;gBAAS;gBAChCS,KAAK;gBACLgB,WAAW;gBACXrD,WAAW;gBACXC,iBAAiB;YACnB;QACF;QACA,MAAM6C,cAAcvC,GAAGC,EAAE;QAEzB,MAAMuC,SAAS,MAAMzD,UAAUE,UAAU;YAAEE,MAAM;YAAUI,KAAK;QAAI,IAAI;YACtE8C;YACAC;YACAC;QACF;QAEAJ,OAAOI,aAAaE,oBAAoB,CAAC,KAAK;QAC9CN,OAAOE,KAAK9B,WAAW,EAAEkC,oBAAoB,CAAC;QAC9CN,OAAOK,OAAOpB,UAAU,EAAEwB,OAAO,CAAC;YAAEvB,SAAS;QAAS;IACxD;IAEAa,GAAG,2EAA2E;QAC5E,MAAMW,UAAU7B;QAChB,MAAMsB,WAAW7B,aAAa;YAC5BE,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAAC;gBAC9BzB,MAAM;gBACNC,MAAM;gBACNG,KAAK;gBACLsD,SAAS;gBACTzB,YAAY;oBAAEC,SAAS;oBAAWC,QAAQ;gBAAQ;gBAClDQ,KAAK;gBACLgB,WAAW;gBACXrD,WAAW;gBACXC,iBAAiB;YACnB;QACF;QACA,MAAM6C,cAAcvC,GAAGC,EAAE;QAEzB,MAAMuC,SAAS,MAAMzD,UAAUE,UAAU;YAAEE,MAAM;YAAUI,KAAK;QAAI,IAAI;YACtEsD;YACAP;YACAC;QACF;QAEAJ,OAAOU,QAAQjB,IAAI,EAAEa,oBAAoB,CACvCN,OAAOY,gBAAgB,CAAC;YACtB3B,YAAY;gBAAEC,SAAS;gBAAWC,QAAQ;YAAQ;QACpD;QAEFa,OAAOI,aAAaG,GAAG,CAACC,gBAAgB;QACxCR,OAAOK,OAAOK,OAAO,EAAET,IAAI,CAAC;IAC9B;IAEAF,GAAG,6DAA6D;QAC9D,MAAMG,OAAOxC;QACb,MAAMyC,WAAW7B,aAAa;YAC5BE,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAAC;gBAC9BzB,MAAM;gBACNC,MAAM;gBACNG,KAAK;gBACLsD,SAAS;gBACTzB,YAAY;oBAAEC,SAAS;gBAAS;gBAChCS,KAAK;gBACLgB,WAAW;gBACXrD,WAAW;gBACXC,iBAAiB;YACnB;QACF;QACA,MAAMsD,QAAQC,OAAOC,MAAM,CAAC,IAAIC,MAAM,SAAS;YAAEC,MAAM;QAAQ;QAC/D,MAAMb,cAAcvC,GAAGC,EAAE,CAAC;YACxB,MAAM+C;QACR;QAEA,MAAMjE,UAAUE,UAAU;YAAEE,MAAM;YAAUI,KAAK;QAAI,IAAI;YACvD8C;YACAC;YACAC;QACF;QAEAJ,OAAOE,KAAK9B,WAAW,EAAEkC,oBAAoB,CAAC;IAChD;IAEAP,GAAG,2CAA2C;QAC5C,MAAMG,OAAOxC;QACb,MAAMyC,WAAW7B;QACjB,MAAMuC,QAAQC,OAAOC,MAAM,CAAC,IAAIC,MAAM,sBAAsB;YAAEC,MAAM;QAAQ;QAC5E,MAAMb,cAAcvC,GAAGC,EAAE,CAAC;YACxB,MAAM+C;QACR;QAEA,MAAMb,OACJpD,UAAUE,UAAU;YAAEE,MAAM;YAAUI,KAAK;QAAI,IAAI;YACjD8C;YACAC;YACAC;QACF,IACAc,OAAO,CAACC,OAAO,CAAC;QAElBnB,OAAOE,KAAK9B,WAAW,EAAEmC,GAAG,CAACC,gBAAgB;IAC/C;AACF;AAEAV,SAAS,cAAc;IACrBC,GAAG,gDAAgD;QACjD,MAAMd,aAAa;YAAEC,SAAS;YAAWC,QAAQ;YAASC,WAAW;QAAS;QAC9E,MAAMsB,UAAU7B;QAChB,MAAMsB,WAAW7B,aAAa;YAC5BE,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAAC;gBAC9BzB,MAAM;gBACNC,MAAM;gBACNG,KAAK;gBACLsD,SAAS;gBACTzB;gBACAU,KAAK;gBACLgB,WAAW;YACb;QACF;QACA,MAAMS,eAAe;YACnBC,cAAcxD,GAAGC,EAAE;YACnBwD,eAAezD,GAAGC,EAAE;QACtB;QAEA,MAAMuC,SAAS,MAAM5D,WAAWK,aAAa;YAAEqD;YAAUO;YAASU;QAAa;QAE/EpB,OAAOU,QAAQlB,KAAK,EAAEc,oBAAoB,CAACN,OAAOY,gBAAgB,CAAC;YAAE3B;QAAW;QAChFe,OAAOoB,aAAaC,YAAY,EAAEd,GAAG,CAACC,gBAAgB;QACtDR,OAAOK,QAAQI,OAAO,CAAC;YAAEc,SAAS;QAAK;IACzC;IAEAxB,GAAG,uDAAuD;QACxD,MAAMqB,eAAe;YACnBC,cAAcxD,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;YACxCuD,eAAezD,GAAGC,EAAE;QACtB;QAEA,MAAMuC,SAAS,MAAM5D,WAAWK,UAAU;YAAEM,KAAK;QAAG,IAAI;YACtD+C,UAAU7B;YACV8C;QACF;QAEApB,OAAOoB,aAAaC,YAAY,EAAEf,oBAAoB,CAAC;QACvDN,OAAOoB,aAAaE,aAAa,EAAEf,GAAG,CAACC,gBAAgB;QACvDR,OAAOK,QAAQI,OAAO,CAAC;YAAEc,SAAS;YAAOC,QAAQ;QAAqB;IACxE;IAEAzB,GAAG,gDAAgD;QACjD,MAAM0B,WAAW;YAAExE,MAAM;YAAQyE,YAAY;YAAOC,KAAK;QAAe;QACxE,MAAMP,eAAe;YACnBC,cAAcxD,GAAGC,EAAE,GAAGC,iBAAiB,CAAC0D;YACxCH,eAAezD,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QAC3C;QAEA,MAAMsC,SAAS,MAAM5D,WAAWK,UAAU;YAAEM,KAAK;QAAG,IAAI;YACtD+C,UAAU7B;YACV8C;QACF;QAEApB,OAAOoB,aAAaE,aAAa,EAAEhB,oBAAoB,CAACmB;QACxDzB,OAAOK,QAAQI,OAAO,CAAC;YAAEc,SAAS;YAAOC,QAAQ;QAAe;IAClE;AACF;AAEA1B,SAAS,mBAAmB;IAC1BC,GAAG,4CAA4C;QAC7C,MAAMd,aAAa;YAAEC,SAAS;YAAWC,QAAQ;YAASC,WAAW;QAAS;QAC9E,MAAMsB,UAAU7B;QAChB,MAAMsB,WAAW7B,aAAa;YAC5BE,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAAC;gBAC9BzB,MAAM;gBACNC,MAAM;gBACNG,KAAK;gBACLsD,SAAS;gBACTzB;gBACAU,KAAK;gBACLgB,WAAW;YACb;QACF;QACA,MAAMS,eAAe;YACnBC,cAAcxD,GAAGC,EAAE;QACrB;QAEA,MAAMpB,gBAAgBI,aAAa,SAAS;YAC1CqD;YACAO;YACAU;QACF;QAEApB,OAAOU,QAAQrB,IAAI,EAAEiB,oBAAoB,CACvCN,OAAOY,gBAAgB,CAAC;YAAE3B;YAAY2C,QAAQ;QAAQ,IACtD5B,OAAOY,gBAAgB,CAAC;YAAEQ;QAAa;QAEzCpB,OAAOoB,aAAaC,YAAY,EAAEd,GAAG,CAACC,gBAAgB;IACxD;IAEAT,GAAG,uDAAuD;QACxD,MAAM0B,WAAW;YAAExE,MAAM;YAAQyE,YAAY;YAAOC,KAAK;QAAe;QACxE,MAAMP,eAAe;YACnBC,cAAcxD,GAAGC,EAAE,GAAGC,iBAAiB,CAAC0D;QAC1C;QACA,MAAMI,SAAShE,GAAGC,EAAE,GAAGC,iBAAiB,CAACG;QAEzC,MAAMxB,gBAAgBI,UAAU;YAAEM,KAAK;QAAG,IAAI,SAAS;YACrD+C,UAAU7B;YACV8C;YACAS;QACF;QAEA7B,OAAOoB,aAAaC,YAAY,EAAEf,oBAAoB,CAAC;QACvDN,OAAO6B,QAAQvB,oBAAoB,CAACmB,UAAU;IAChD;IAEA1B,GAAG,sDAAsD;QACvD,MAAMqB,eAAe;YACnBC,cAAcxD,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QAC1C;QAEA,MAAM+D,MAAM,MAAMpF,gBAAgBI,UAAU;YAAEM,KAAK;QAAG,IAAI,SAAS;YACjE+C,UAAU7B;YACV8C;QACF,GAAGW,KAAK,CAAC,CAAClB,QAAUA;QAEpBb,OAAO8B,KAAKE,cAAc,CAACzF;QAC3ByD,OAAO8B,IAAIG,OAAO,EAAEhC,IAAI,CAAC;IAC3B;AACF;AAEAH,SAAS,cAAc;IACrBC,GAAG,+EAA+E;QAChF,MAAMG,OAAOxC;QACb,MAAMyC,WAAW7B;QAEjB,MAAM4D,QAAQ,MAAMvF,WAAW;YAAE,GAAG+C,SAAS;YAAEG,eAAe;QAAI,GAAG;YAAEK;YAAMC;QAAS;QAEtFH,OAAOE,KAAKjC,aAAa,EAAEqC,oBAAoB,CAAC,UAAU;QAC1DN,OAAOE,KAAK/B,QAAQ,EAAEmC,oBAAoB,CAAC,UAAU;QACrDN,OAAOG,SAAS5B,KAAK,EAAEiC,gBAAgB;QACvCR,OAAOG,SAASxB,QAAQ,EAAEwD,oBAAoB;QAC9CnC,OAAOkC,OAAOE,aAAa,CAAC;YAC1BpF,MAAM;YACNC,MAAM;YACNG,KAAK;YACLsD,SAAS;YACTzB,YAAY;gBAAEC,SAAS;YAAS;YAChCS,KAAK;YACL0C,QAAQ;QACV;QACArC,OAAOkC,MAAMvB,SAAS,EAAE2B,OAAO,CAAC;IAClC;IAEAvC,GAAG,oDAAoD;QACrD,MAAMG,OAAOxC,SAAS;YACpBE,aAAaC,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QACzC;QACA,MAAMoC,WAAW7B;QAEjB,MAAM0B,OAAOrD,WAAW+C,WAAW;YAAEQ;YAAMC;QAAS,IAAIe,OAAO,CAACc,cAAc,CAC5EnF;QAEFmD,OAAOE,KAAKjC,aAAa,EAAEsC,GAAG,CAACC,gBAAgB;QAC/CR,OAAOG,SAASxB,QAAQ,EAAE4B,GAAG,CAACC,gBAAgB;IAChD;IAEAT,GAAG,2FAA2F;QAC5F,MAAMW,UAAU7B;QAChB,MAAMsB,WAAW7B;QAEjB,MAAM4D,QAAQ,MAAMvF,WAClB;YAAE,GAAG+C,SAAS;YAAE6C,iBAAiB;QAAQ,GACzC;YAAE7B;YAASP;QAAS;QAGtBH,OAAOU,QAAQ9C,WAAW,EAAEuE,oBAAoB;QAChDnC,OAAOU,QAAQ/D,UAAU,EAAE2D,oBAAoB,CAAC;YAC9CtD,MAAM;YACN2C,KAAK;YACL6C,MAAM;YACNC,MAAM,EAAE;YACRC,WAAW;QACb;QACA1C,OAAOkC,OAAOE,aAAa,CAAC;YAC1BpF,MAAM;YACNC,MAAM;YACNG,KAAK;YACLsD,SAAS;YACTzB,YAAY;gBAAEC,SAAS;gBAAWC,QAAQ;gBAASC,WAAW;YAAS;YACvEO,KAAK;YACL0C,QAAQ;QACV;QACArC,OAAOG,SAASxB,QAAQ,EAAE2B,oBAAoB,CAC5CN,OAAOY,gBAAgB,CAAC;YACtBF,SAAS;YACTzB,YAAY;gBAAEC,SAAS;gBAAWC,QAAQ;gBAASC,WAAW;YAAS;QACzE;IAEJ;IAEAW,GAAG,iEAAiE;QAClE,MAAMW,UAAU7B,YAAY;YAC1BjB,aAAaC,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;gBACrCgB,IAAI;gBACJyC,QAAQ;gBACRmB,QAAQ;YACV;QACF;QACA,MAAMxC,WAAW7B;QAEjB,MAAM0B,OACJrD,WAAW;YAAE,GAAG+C,SAAS;YAAE6C,iBAAiB;QAAQ,GAAG;YAAE7B;YAASP;QAAS,IAC3Ee,OAAO,CAACkB,aAAa,CAAC;YACtBpF,MAAM;YACN8B,UAAU;YACV0C,QAAQ;QACV;QACAxB,OAAOU,QAAQ/D,UAAU,EAAE4D,GAAG,CAACC,gBAAgB;QAC/CR,OAAOG,SAASxB,QAAQ,EAAE4B,GAAG,CAACC,gBAAgB;IAChD;IAEAT,GAAG,qEAAqE;QACtE,MAAMG,OAAOxC;QACb,MAAMkF,YAA2B;YAC/B5F,MAAM;YACNC,MAAM;YACNG,KAAK;YACLsD,SAAS;YACTzB,YAAY;gBAAEC,SAAS;YAAS;YAChCS,KAAK;YACLgB,WAAW;QACb;QACA,MAAMR,WAAW7B,aAAa;YAC5BE,QAAQX,GAAGC,EAAE,GAAGW,eAAe,CAACmE;QAClC;QAEA,MAAMd,MAAM,MAAMnF,WAAW+C,WAAW;YAAEQ;YAAMC;QAAS,GAAG4B,KAAK,CAAC,CAACc,IAAMA;QACzE7C,OAAO8B,KAAKE,cAAc,CAAC3F;QAC3B2D,OAAO8B,IAAI1E,GAAG,EAAE6C,IAAI,CAAC;QACrBD,OAAOE,KAAKjC,aAAa,EAAEsC,GAAG,CAACC,gBAAgB;IACjD;IAEAT,GAAG,oFAAoF;QACrF,MAAM+C,OAAO5G,GAAG6G,WAAW,CAAC3G,KAAK4G,IAAI,CAAC7G,GAAG8G,MAAM,IAAI;QACnD,IAAI;YACF,MAAMC,YAAY9G,KAAK4G,IAAI,CAACF,MAAM;YAClC5G,GAAGiH,SAAS,CAAC/G,KAAKgH,OAAO,CAACF,YAAY;gBAAEG,WAAW;YAAK;YACxDnH,GAAGoH,aAAa,CAACJ,WAAW;YAC5B,MAAMK,cAAc;gBAClB;oBAAEnG,KAAK;oBAAKoG,SAAS;gBAAO;gBAC5B;oBAAEpG,KAAK;oBAAKoG,SAAS,GAAGN,UAAU,0BAA0B,CAAC;gBAAC;gBAC9D;oBAAE9F,KAAK;oBAAKoG,SAAS;gBAAyC;aAC/D;YACD,MAAMnF,eAAeR,GAAGC,EAAE,CACxB,OAAO2F,UAAkBC,UACvBH,YAAYI,MAAM,CAAC,CAACC,OAASF,QAAQE,KAAKJ,OAAO,GAAGK,EAAE,CAAC,CAAC,IAAIzG,OAAO;YAEvE,MAAM8C,OAAOxC,SAAS;gBAAEW;YAAa;YACrC,MAAM8B,WAAW7B;YAEjB,MAAM4D,QAAQ,MAAMvF,WAAW;gBAAE,GAAG+C,SAAS;gBAAEzC,MAAM;YAAQ,GAAG;gBAAEiD;gBAAMC;YAAS;YAEjFH,OAAOE,KAAK/B,QAAQ,EAAEmC,oBAAoB,CAAC,UAAU;YACrDN,OAAOkC,OAAOE,aAAa,CAAC;gBAAEnF,MAAM;gBAASG,KAAK;YAAI;QACxD,SAAU;YACRlB,GAAG4H,MAAM,CAAChB,MAAM;gBAAEO,WAAW;gBAAMU,OAAO;YAAK;QACjD;IACF;IAEAhE,GAAG,oDAAoD;QACrD,MAAMG,OAAOxC,SAAS;YACpBM,eAAeH,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QAC3C;QACA,MAAMoC,WAAW7B;QACjB,MAAM0F,YAAYnG,GAAGC,EAAE;QAEvB,MAAMnB,WAAW+C,WAAW;YAAEQ;YAAMC;YAAU6D;QAAU;QAExDhE,OAAOgE,WAAW7B,oBAAoB;QACtCnC,OAAOgE,UAAUC,IAAI,CAACC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAEC,SAAS,CAAC;QAC7CnE,OAAOE,KAAK9B,WAAW,EAAEkC,oBAAoB,CAAC;QAC9CN,OAAOE,KAAKjC,aAAa,EAAEqC,oBAAoB,CAAC,UAAU;IAC5D;IAEAP,GAAG,0EAA0E;QAC3E,MAAMG,OAAOxC,SAAS;YACpBW,cAAcR,GAAGC,EAAE,GAAGC,iBAAiB,CAAC;QAC1C;QACA,MAAMoC,WAAW7B;QAEjB,MAAMwD,MAAM,MAAMnF,WAAW+C,WAAW;YAAEQ;YAAMC;QAAS,GAAG4B,KAAK,CAAC,CAACc,IAAMA;QAEzE7C,OAAO8B,KAAKE,cAAc,CAAC1F;QAC3B0D,OAAO8B,IAAI0B,OAAO,EAAEvD,IAAI,CAAC;QACzBD,OAAO8B,IAAIY,SAAS,EAAEzC,IAAI,CAAC;QAC3BD,OAAOE,KAAK9B,WAAW,EAAEgG,wBAAwB,CAAC;QAClDpE,OAAOG,SAASxB,QAAQ,EAAE4B,GAAG,CAACC,gBAAgB;IAChD;IAEAT,GAAG,kDAAkD;QACnD,MAAM1B,eAAeR,GAClBC,EAAE,GACFuG,qBAAqB,CAAC,MACtBA,qBAAqB,CAAC,MACtBA,qBAAqB,CAAC,IACtBA,qBAAqB,CAAC,IACtBA,qBAAqB,CAAC,IACtBA,qBAAqB,CAAC,IACtBA,qBAAqB,CAAC;QACzB,MAAMnE,OAAOxC,SAAS;YAAEW;QAAa;QACrC,MAAM8B,WAAW7B;QAEjB,MAAM4D,QAAQ,MAAMvF,WAAW;YAAE,GAAG+C,SAAS;YAAEG,eAAe;QAAI,GAAG;YAAEK;YAAMC;QAAS;QAEtFH,OAAO3B,cAAciG,qBAAqB,CAAC;QAC3CtE,OAAOkC,MAAM9E,GAAG,EAAE6C,IAAI,CAAC;IACzB;IAEAF,GAAG,sEAAsE;QACvE,MAAM1B,eAAeR,GAClBC,EAAE,GACFuG,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC;QACzB,MAAMnE,OAAOxC,SAAS;YAAEW;QAAa;QACrC,MAAM8B,WAAW7B;QAEjB,MAAM4D,QAAQ,MAAMvF,WAAW+C,WAAW;YAAEQ;YAAMC;QAAS;QAE3DH,OAAO3B,cAAciG,qBAAqB,CAAC;QAC3CtE,OAAOkC,MAAM9E,GAAG,EAAE6C,IAAI,CAAC;QACvBD,OAAOG,SAASxB,QAAQ,EAAE2B,oBAAoB,CAACN,OAAOY,gBAAgB,CAAC;YAAExD,KAAK;QAAI;IACpF;IAEA2C,GAAG,gDAAgD;QACjD,MAAM1B,eAAeR,GAClBC,EAAE,GACFuG,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC,KACtBA,qBAAqB,CAAC;QACzB,MAAMnE,OAAOxC,SAAS;YAAEW;QAAa;QACrC,MAAM8B,WAAW7B;QAEjB,MAAMwD,MAAM,MAAMnF,WAAW;YAAE,GAAG+C,SAAS;YAAEG,eAAe;QAAE,GAAG;YAAEK;YAAMC;QAAS,GAAG4B,KAAK,CACxF,CAACc,IAAMA;QAGT7C,OAAO8B,KAAKE,cAAc,CAAC1F;QAC3B0D,OAAOG,SAASxB,QAAQ,EAAE4B,GAAG,CAACC,gBAAgB;QAC9CR,OAAOE,KAAK9B,WAAW,EAAEgG,wBAAwB,CAAC;IACpD;IAEArE,GAAG,sDAAsD;QACvD,MAAMG,OAAOxC;QACb,MAAMyC,WAAW7B;QACjB,MAAMiG,QAAkB,EAAE;QACzBpE,SAAS5B,KAAK,CAASiG,kBAAkB,CAAC,IAAMD,MAAME,IAAI,CAAC;QAC3DtE,SAAS3B,MAAM,CAASgG,kBAAkB,CAAC;YAC1CD,MAAME,IAAI,CAAC;YACX,OAAO;QACT;QAEA,MAAM9H,WAAW+C,WAAW;YAAEQ;YAAMC;QAAS;QAC7CH,OAAOuE,OAAO9D,OAAO,CAAC;YAAC;YAAS;SAAS;IAC3C;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/__tests__/terminal/TmuxManager.test.ts"],"sourcesContent":["import { execFile } from \"child_process\";\nimport type { MockedFunction } from \"vitest\";\nimport { TmuxManager } from \"../../terminal/TmuxManager.js\";\n\nvi.mock(\"child_process\", () => ({\n execFile: vi.fn(),\n}));\n\ntype ExecFileCb = (err: Error | null, result?: { stdout: string; stderr: string }) => void;\nconst mockedExecFile = execFile as unknown as MockedFunction<\n (cmd: string, args: string[], cb: ExecFileCb) => void\n>;\n\n/** Drive the promisified execFile mock with a per-call handler. */\nfunction setExecFileHandler(handler: (cmd: string, args: string[]) => string | Error) {\n mockedExecFile.mockImplementation((_cmd, _args, cb) => {\n const result = handler(_cmd, _args);\n if (result instanceof Error) cb(result);\n else cb(null, { stdout: result, stderr: \"\" });\n });\n}\n\ndescribe(\"TmuxManager\", () => {\n let tmux: TmuxManager;\n\n beforeEach(() => {\n tmux = new TmuxManager();\n mockedExecFile.mockReset();\n });\n\n describe(\"isAvailable\", () => {\n it(\"returns true when `tmux -V` succeeds\", async () => {\n setExecFileHandler(() => \"tmux 3.4\");\n expect(await tmux.isAvailable()).toBe(true);\n });\n\n it(\"returns false when tmux is missing\", async () => {\n setExecFileHandler(() => new Error(\"ENOENT\"));\n expect(await tmux.isAvailable()).toBe(false);\n });\n });\n\n describe(\"sessionExists\", () => {\n it(\"returns true when has-session succeeds\", async () => {\n setExecFileHandler(() => \"\");\n expect(await tmux.sessionExists(\"foo\")).toBe(true);\n });\n\n it(\"returns false when has-session fails\", async () => {\n setExecFileHandler(() => new Error(\"no session\"));\n expect(await tmux.sessionExists(\"foo\")).toBe(false);\n });\n });\n\n describe(\"createSession\", () => {\n it(\"issues `tmux new-session -d -s <name> -c <cwd>`\", async () => {\n setExecFileHandler(() => \"\");\n await tmux.createSession(\"foo\", \"/work\");\n expect(mockedExecFile).toHaveBeenCalledWith(\n \"tmux\",\n [\"new-session\", \"-d\", \"-s\", \"foo\", \"-c\", \"/work\"],\n expect.any(Function),\n );\n });\n });\n\n describe(\"sendKeys\", () => {\n it(\"appends Enter so the command runs\", async () => {\n setExecFileHandler(() => \"\");\n await tmux.sendKeys(\"foo\", \"claude\");\n expect(mockedExecFile).toHaveBeenCalledWith(\n \"tmux\",\n [\"send-keys\", \"-t\", \"foo\", \"claude\", \"Enter\"],\n expect.any(Function),\n );\n });\n });\n\n describe(\"killSession\", () => {\n it(\"swallows errors when session is already gone\", async () => {\n setExecFileHandler(() => new Error(\"can't find session\"));\n await expect(tmux.killSession(\"foo\")).resolves.toBeUndefined();\n });\n });\n\n describe(\"findAgentPid\", () => {\n const matchesClaude = (cmd: string) => cmd.split(/\\s+/)[0]?.endsWith(\"claude\") ?? false;\n\n it(\"returns null when the session has no pane\", async () => {\n setExecFileHandler((cmd, args) => {\n if (args[0] === \"list-panes\") return new Error(\"no session\");\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBeNull();\n });\n\n it(\"returns null when no descendant matches\", async () => {\n // pane PID 100; child 200 is \"node /unrelated\" — no match\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\" && args[0] === \"list-panes\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"node /unrelated\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBeNull();\n });\n\n it(\"returns the matching pane PID when the agent replaces the shell\", async () => {\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\" && args[0] === \"list-panes\") return \"100\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"100\") return \"/usr/local/bin/claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(100);\n });\n\n it(\"returns the matching descendant when found\", async () => {\n // pane 100 → child 200 (claude) — no grandchildren\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(200);\n });\n\n it(\"prefers the deepest match (wrapper case)\", async () => {\n // pane 100 → 200 (claude wrapper, matches) → 300 (claude, matches, deeper)\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\" && args[1] === \"200\") return \"300\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"/usr/local/bin/claude\";\n if (cmd === \"ps\" && args[1] === \"300\") return \"/usr/local/lib/claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(300);\n });\n\n it(\"skips non-matching subprocesses (MCP child case)\", async () => {\n // pane 100 → 200 (claude, matches) → 300 (mcp-server, no match)\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\" && args[1] === \"200\") return \"300\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"claude\";\n if (cmd === \"ps\" && args[1] === \"300\") return \"node mcp-server.js\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(200);\n });\n\n it(\"handles the gemini Node-script shape via a token-scan matcher\", async () => {\n const matchesGemini = (cmd: string) =>\n cmd.split(/\\s+/).some((t) => t.endsWith(\"/gemini\") || t === \"gemini\");\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"node /opt/homebrew/bin/gemini --foo\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesGemini)).toBe(200);\n });\n\n it(\"handles multiple children at the same level\", async () => {\n // pane 100 → [200 (nope), 201 (claude)]\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n201\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"bash\";\n if (cmd === \"ps\" && args[1] === \"201\") return \"claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(201);\n });\n });\n});\n"],"names":["execFile","TmuxManager","vi","mock","fn","mockedExecFile","setExecFileHandler","handler","mockImplementation","_cmd","_args","cb","result","Error","stdout","stderr","describe","tmux","beforeEach","mockReset","it","expect","isAvailable","toBe","sessionExists","createSession","toHaveBeenCalledWith","any","Function","sendKeys","killSession","resolves","toBeUndefined","matchesClaude","cmd","split","endsWith","args","findAgentPid","toBeNull","matchesGemini","some","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/terminal/TmuxManager.test.ts"],"sourcesContent":["import { execFile } from \"child_process\";\nimport type { MockedFunction } from \"vitest\";\nimport { TmuxManager } from \"../../runtime/tmux/TmuxManager.js\";\n\nvi.mock(\"child_process\", () => ({\n execFile: vi.fn(),\n}));\n\ntype ExecFileCb = (err: Error | null, result?: { stdout: string; stderr: string }) => void;\nconst mockedExecFile = execFile as unknown as MockedFunction<\n (cmd: string, args: string[], cb: ExecFileCb) => void\n>;\n\n/** Drive the promisified execFile mock with a per-call handler. */\nfunction setExecFileHandler(handler: (cmd: string, args: string[]) => string | Error) {\n mockedExecFile.mockImplementation((_cmd, _args, cb) => {\n const result = handler(_cmd, _args);\n if (result instanceof Error) cb(result);\n else cb(null, { stdout: result, stderr: \"\" });\n });\n}\n\ndescribe(\"TmuxManager\", () => {\n let tmux: TmuxManager;\n\n beforeEach(() => {\n tmux = new TmuxManager();\n mockedExecFile.mockReset();\n });\n\n describe(\"isAvailable\", () => {\n it(\"returns true when `tmux -V` succeeds\", async () => {\n setExecFileHandler(() => \"tmux 3.4\");\n expect(await tmux.isAvailable()).toBe(true);\n });\n\n it(\"returns false when tmux is missing\", async () => {\n setExecFileHandler(() => new Error(\"ENOENT\"));\n expect(await tmux.isAvailable()).toBe(false);\n });\n });\n\n describe(\"sessionExists\", () => {\n it(\"returns true when has-session succeeds\", async () => {\n setExecFileHandler(() => \"\");\n expect(await tmux.sessionExists(\"foo\")).toBe(true);\n });\n\n it(\"returns false when has-session fails\", async () => {\n setExecFileHandler(() => new Error(\"no session\"));\n expect(await tmux.sessionExists(\"foo\")).toBe(false);\n });\n });\n\n describe(\"createSession\", () => {\n it(\"issues `tmux new-session -d -s <name> -c <cwd>`\", async () => {\n setExecFileHandler(() => \"\");\n await tmux.createSession(\"foo\", \"/work\");\n expect(mockedExecFile).toHaveBeenCalledWith(\n \"tmux\",\n [\"new-session\", \"-d\", \"-s\", \"foo\", \"-c\", \"/work\"],\n expect.any(Function),\n );\n });\n });\n\n describe(\"sendKeys\", () => {\n it(\"appends Enter so the command runs\", async () => {\n setExecFileHandler(() => \"\");\n await tmux.sendKeys(\"foo\", \"claude\");\n expect(mockedExecFile).toHaveBeenCalledWith(\n \"tmux\",\n [\"send-keys\", \"-t\", \"foo\", \"claude\", \"Enter\"],\n expect.any(Function),\n );\n });\n });\n\n describe(\"killSession\", () => {\n it(\"swallows errors when session is already gone\", async () => {\n setExecFileHandler(() => new Error(\"can't find session\"));\n await expect(tmux.killSession(\"foo\")).resolves.toBeUndefined();\n });\n });\n\n describe(\"findAgentPid\", () => {\n const matchesClaude = (cmd: string) => cmd.split(/\\s+/)[0]?.endsWith(\"claude\") ?? false;\n\n it(\"returns null when the session has no pane\", async () => {\n setExecFileHandler((cmd, args) => {\n if (args[0] === \"list-panes\") return new Error(\"no session\");\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBeNull();\n });\n\n it(\"returns null when no descendant matches\", async () => {\n // pane PID 100; child 200 is \"node /unrelated\" — no match\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\" && args[0] === \"list-panes\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"node /unrelated\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBeNull();\n });\n\n it(\"returns the matching pane PID when the agent replaces the shell\", async () => {\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\" && args[0] === \"list-panes\") return \"100\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"100\") return \"/usr/local/bin/claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(100);\n });\n\n it(\"returns the matching descendant when found\", async () => {\n // pane 100 → child 200 (claude) — no grandchildren\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(200);\n });\n\n it(\"prefers the deepest match (wrapper case)\", async () => {\n // pane 100 → 200 (claude wrapper, matches) → 300 (claude, matches, deeper)\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\" && args[1] === \"200\") return \"300\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"/usr/local/bin/claude\";\n if (cmd === \"ps\" && args[1] === \"300\") return \"/usr/local/lib/claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(300);\n });\n\n it(\"skips non-matching subprocesses (MCP child case)\", async () => {\n // pane 100 → 200 (claude, matches) → 300 (mcp-server, no match)\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\" && args[1] === \"200\") return \"300\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"claude\";\n if (cmd === \"ps\" && args[1] === \"300\") return \"node mcp-server.js\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(200);\n });\n\n it(\"handles the gemini Node-script shape via a token-scan matcher\", async () => {\n const matchesGemini = (cmd: string) =>\n cmd.split(/\\s+/).some((t) => t.endsWith(\"/gemini\") || t === \"gemini\");\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"node /opt/homebrew/bin/gemini --foo\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesGemini)).toBe(200);\n });\n\n it(\"handles multiple children at the same level\", async () => {\n // pane 100 → [200 (nope), 201 (claude)]\n setExecFileHandler((cmd, args) => {\n if (cmd === \"tmux\") return \"100\\n\";\n if (cmd === \"pgrep\" && args[1] === \"100\") return \"200\\n201\\n\";\n if (cmd === \"pgrep\") return new Error(\"no children\");\n if (cmd === \"ps\" && args[1] === \"200\") return \"bash\";\n if (cmd === \"ps\" && args[1] === \"201\") return \"claude\";\n return \"\";\n });\n expect(await tmux.findAgentPid(\"foo\", matchesClaude)).toBe(201);\n });\n });\n});\n"],"names":["execFile","TmuxManager","vi","mock","fn","mockedExecFile","setExecFileHandler","handler","mockImplementation","_cmd","_args","cb","result","Error","stdout","stderr","describe","tmux","beforeEach","mockReset","it","expect","isAvailable","toBe","sessionExists","createSession","toHaveBeenCalledWith","any","Function","sendKeys","killSession","resolves","toBeUndefined","matchesClaude","cmd","split","endsWith","args","findAgentPid","toBeNull","matchesGemini","some","t"],"mappings":"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"}
|
package/dist/capacity/index.d.ts
CHANGED
|
@@ -1,30 +1,20 @@
|
|
|
1
|
-
import { probeCodexCapacity } from "./codex.js";
|
|
2
|
-
import { type ClaudeCapacityOptions } from "./claude.js";
|
|
1
|
+
import { probeCodexCapacity } from "./sources/codex.js";
|
|
2
|
+
import { type ClaudeCapacityOptions } from "./sources/claude.js";
|
|
3
|
+
import { type PiAnthropicCapacityOptions, type PiOpenAiCapacityOptions, type PiZaiCapacityOptions } from "./sources/pi.js";
|
|
3
4
|
import type { CapacityReport } from "./types.js";
|
|
4
5
|
export type { CapacityReport, CapacityWindow } from "./types.js";
|
|
5
|
-
export type { ClaudeCapacityOptions } from "./claude.js";
|
|
6
|
+
export type { ClaudeCapacityOptions } from "./sources/claude.js";
|
|
7
|
+
export type { PiAnthropicCapacityOptions as AnthropicCapacityOptions } from "./sources/pi.js";
|
|
8
|
+
export type { PiOpenAiCapacityOptions as OpenAiCapacityOptions, PiZaiCapacityOptions as ZaiCapacityOptions, } from "./sources/pi.js";
|
|
6
9
|
export type CapacityProbeOptions = {
|
|
7
10
|
now?: () => Date;
|
|
8
11
|
path?: string;
|
|
9
12
|
access?: (target: string) => Promise<void>;
|
|
10
13
|
probe?: typeof probeCodexCapacity;
|
|
11
14
|
};
|
|
12
|
-
export type ZaiCapacityOptions = {
|
|
13
|
-
now?: () => Date;
|
|
14
|
-
env?: NodeJS.ProcessEnv;
|
|
15
|
-
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
|
|
16
|
-
fetch?: typeof globalThis.fetch;
|
|
17
|
-
timeoutMs?: number;
|
|
18
|
-
};
|
|
19
|
-
export type OpenAiCapacityOptions = {
|
|
20
|
-
now?: () => Date;
|
|
21
|
-
env?: NodeJS.ProcessEnv;
|
|
22
|
-
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
|
|
23
|
-
fetch?: typeof globalThis.fetch;
|
|
24
|
-
timeoutMs?: number;
|
|
25
|
-
};
|
|
26
15
|
export declare function getCodexCapacityReport(options?: CapacityProbeOptions): Promise<CapacityReport>;
|
|
27
|
-
export declare function getZaiCapacityReport(options?:
|
|
28
|
-
export declare function getOpenAiCapacityReport(options?:
|
|
16
|
+
export declare function getZaiCapacityReport(options?: PiZaiCapacityOptions): Promise<CapacityReport>;
|
|
17
|
+
export declare function getOpenAiCapacityReport(options?: PiOpenAiCapacityOptions): Promise<CapacityReport>;
|
|
29
18
|
export declare function getClaudeCapacityReport(options?: ClaudeCapacityOptions): Promise<CapacityReport>;
|
|
19
|
+
export declare function getAnthropicCapacityReport(options?: PiAnthropicCapacityOptions): Promise<CapacityReport>;
|
|
30
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capacity/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capacity/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,0BAA0B,IAAI,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC9F,YAAY,EACV,uBAAuB,IAAI,qBAAqB,EAChD,oBAAoB,IAAI,kBAAkB,GAC3C,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,OAAO,kBAAkB,CAAC;CACnC,CAAC;AA+BF,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,cAAc,CAAC,CAWzB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,cAAc,CAAC,CAMzB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,cAAc,CAAC,CAMzB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,cAAc,CAAC,CAOzB;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,cAAc,CAAC,CAOzB"}
|
package/dist/capacity/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { access as fsAccess } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { codexUnavailableReport, probeCodexCapacity } from "./codex.js";
|
|
5
|
-
import { probeClaudeCapacity } from "./claude.js";
|
|
6
|
-
import { probeOpenAiCapacity } from "./
|
|
7
|
-
import { probeZaiCapacity } from "./zai.js";
|
|
4
|
+
import { codexUnavailableReport, probeCodexCapacity } from "./sources/codex.js";
|
|
5
|
+
import { probeClaudeCapacity } from "./sources/claude.js";
|
|
6
|
+
import { probeOpenAiCapacity, probePiAnthropicCapacity, probeZaiCapacity } from "./sources/pi.js";
|
|
8
7
|
async function canAccess(target, mode) {
|
|
9
8
|
try {
|
|
10
9
|
await fsAccess(target, mode);
|
|
@@ -63,5 +62,13 @@ export async function getClaudeCapacityReport(options = {}) {
|
|
|
63
62
|
now
|
|
64
63
|
});
|
|
65
64
|
}
|
|
65
|
+
export async function getAnthropicCapacityReport(options = {}) {
|
|
66
|
+
const { now, ...probeOptions } = options;
|
|
67
|
+
return probePiAnthropicCapacity({
|
|
68
|
+
...probeOptions,
|
|
69
|
+
checkedAt: (now?.() ?? new Date()).toISOString(),
|
|
70
|
+
now
|
|
71
|
+
});
|
|
72
|
+
}
|
|
66
73
|
|
|
67
74
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/capacity/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { codexUnavailableReport, probeCodexCapacity } from \"./codex.js\";\nimport { probeClaudeCapacity, type ClaudeCapacityOptions } from \"./claude.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/capacity/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { codexUnavailableReport, probeCodexCapacity } from \"./sources/codex.js\";\nimport { probeClaudeCapacity, type ClaudeCapacityOptions } from \"./sources/claude.js\";\nimport {\n probeOpenAiCapacity,\n probePiAnthropicCapacity,\n probeZaiCapacity,\n type PiAnthropicCapacityOptions,\n type PiOpenAiCapacityOptions,\n type PiZaiCapacityOptions,\n} from \"./sources/pi.js\";\nimport type { CapacityReport } from \"./types.js\";\n\nexport type { CapacityReport, CapacityWindow } from \"./types.js\";\nexport type { ClaudeCapacityOptions } from \"./sources/claude.js\";\nexport type { PiAnthropicCapacityOptions as AnthropicCapacityOptions } from \"./sources/pi.js\";\nexport type {\n PiOpenAiCapacityOptions as OpenAiCapacityOptions,\n PiZaiCapacityOptions as ZaiCapacityOptions,\n} from \"./sources/pi.js\";\n\nexport type CapacityProbeOptions = {\n now?: () => Date;\n path?: string;\n access?: (target: string) => Promise<void>;\n probe?: typeof probeCodexCapacity;\n};\n\nasync function canAccess(target: string, mode: number): Promise<boolean> {\n try {\n await fsAccess(target, mode);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isCodexInstalled(\n pathValue: string,\n checkAccess?: (target: string) => Promise<void>,\n): Promise<boolean> {\n const directories = pathValue.split(path.delimiter).filter(Boolean);\n for (const directory of directories) {\n const executable = path.join(directory, \"codex\");\n if (checkAccess) {\n try {\n await checkAccess(executable);\n return true;\n } catch {\n continue;\n }\n }\n if (await canAccess(executable, constants.X_OK)) return true;\n }\n return false;\n}\n\nexport async function getCodexCapacityReport(\n options: CapacityProbeOptions = {},\n): Promise<CapacityReport> {\n const generatedAt = (options.now?.() ?? new Date()).toISOString();\n const installed = await isCodexInstalled(options.path ?? process.env.PATH ?? \"\", options.access);\n try {\n return await (options.probe ?? probeCodexCapacity)({\n installed,\n checkedAt: generatedAt,\n });\n } catch {\n return codexUnavailableReport(generatedAt);\n }\n}\n\nexport async function getZaiCapacityReport(\n options: PiZaiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeZaiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getOpenAiCapacityReport(\n options: PiOpenAiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeOpenAiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getClaudeCapacityReport(\n options: ClaudeCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeClaudeCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n\nexport async function getAnthropicCapacityReport(\n options: PiAnthropicCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probePiAnthropicCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n"],"names":["constants","access","fsAccess","path","codexUnavailableReport","probeCodexCapacity","probeClaudeCapacity","probeOpenAiCapacity","probePiAnthropicCapacity","probeZaiCapacity","canAccess","target","mode","isCodexInstalled","pathValue","checkAccess","directories","split","delimiter","filter","Boolean","directory","executable","join","X_OK","getCodexCapacityReport","options","generatedAt","now","Date","toISOString","installed","process","env","PATH","probe","checkedAt","getZaiCapacityReport","probeOptions","getOpenAiCapacityReport","getClaudeCapacityReport","getAnthropicCapacityReport"],"mappings":"AAAA,SAASA,SAAS,QAAQ,UAAU;AACpC,SAASC,UAAUC,QAAQ,QAAQ,mBAAmB;AACtD,OAAOC,UAAU,YAAY;AAC7B,SAASC,sBAAsB,EAAEC,kBAAkB,QAAQ,qBAAqB;AAChF,SAASC,mBAAmB,QAAoC,sBAAsB;AACtF,SACEC,mBAAmB,EACnBC,wBAAwB,EACxBC,gBAAgB,QAIX,kBAAkB;AAkBzB,eAAeC,UAAUC,MAAc,EAAEC,IAAY;IACnD,IAAI;QACF,MAAMV,SAASS,QAAQC;QACvB,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAeC,iBACbC,SAAiB,EACjBC,WAA+C;IAE/C,MAAMC,cAAcF,UAAUG,KAAK,CAACd,KAAKe,SAAS,EAAEC,MAAM,CAACC;IAC3D,KAAK,MAAMC,aAAaL,YAAa;QACnC,MAAMM,aAAanB,KAAKoB,IAAI,CAACF,WAAW;QACxC,IAAIN,aAAa;YACf,IAAI;gBACF,MAAMA,YAAYO;gBAClB,OAAO;YACT,EAAE,OAAM;gBACN;YACF;QACF;QACA,IAAI,MAAMZ,UAAUY,YAAYtB,UAAUwB,IAAI,GAAG,OAAO;IAC1D;IACA,OAAO;AACT;AAEA,OAAO,eAAeC,uBACpBC,UAAgC,CAAC,CAAC;IAElC,MAAMC,cAAc,AAACD,CAAAA,QAAQE,GAAG,QAAQ,IAAIC,MAAK,EAAGC,WAAW;IAC/D,MAAMC,YAAY,MAAMlB,iBAAiBa,QAAQvB,IAAI,IAAI6B,QAAQC,GAAG,CAACC,IAAI,IAAI,IAAIR,QAAQzB,MAAM;IAC/F,IAAI;QACF,OAAO,MAAM,AAACyB,CAAAA,QAAQS,KAAK,IAAI9B,kBAAiB,EAAG;YACjD0B;YACAK,WAAWT;QACb;IACF,EAAE,OAAM;QACN,OAAOvB,uBAAuBuB;IAChC;AACF;AAEA,OAAO,eAAeU,qBACpBX,UAAgC,CAAC,CAAC;IAElC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOjB,iBAAiB;QACtB,GAAG6B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeS,wBACpBb,UAAmC,CAAC,CAAC;IAErC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOnB,oBAAoB;QACzB,GAAG+B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeU,wBACpBd,UAAiC,CAAC,CAAC;IAEnC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOpB,oBAAoB;QACzB,GAAGgC,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;QAC9CF;IACF;AACF;AAEA,OAAO,eAAea,2BACpBf,UAAsC,CAAC,CAAC;IAExC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOlB,yBAAyB;QAC9B,GAAG8B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;QAC9CF;IACF;AACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProviderCapacitySnapshot } from "../types.js";
|
|
2
|
+
export type AnthropicCapacityRequestOptions = {
|
|
3
|
+
fetch?: typeof globalThis.fetch;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
now?: () => Date;
|
|
6
|
+
};
|
|
7
|
+
export declare function parseAnthropicCapacity(raw: unknown): ProviderCapacitySnapshot;
|
|
8
|
+
export declare function fetchAnthropicCapacity(token: string, options?: AnthropicCapacityRequestOptions): Promise<ProviderCapacitySnapshot>;
|
|
9
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/capacity/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgC,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAM1F,MAAM,MAAM,+BAA+B,GAAG;IAC5C,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AA2HF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,wBAAwB,CAsB7E;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,wBAAwB,CAAC,CAqCnC"}
|