diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-18 23:46:34 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-18 23:56:20 +0300 |
| commit | a51a15914a5b29155022a1fd589d2bcc6409cadf (patch) | |
| tree | 8602b6d94c811666d49e4c2aa7636bb6a9b0e8a3 /src/integration/assets | |
| parent | 60c040057e3949ae93ee88d495df09b4b848d22d (diff) | |
fix: support javascript integrations on windows
refs #1531
Diffstat (limited to 'src/integration/assets')
5 files changed, 96 insertions, 10 deletions
diff --git a/src/integration/assets/herdr-agent-state.test.ts b/src/integration/assets/herdr-agent-state.test.ts index c662e132..50f1fc52 100644 --- a/src/integration/assets/herdr-agent-state.test.ts +++ b/src/integration/assets/herdr-agent-state.test.ts @@ -1,9 +1,11 @@ import { afterEach, expect, test } from "bun:test"; import { rm } from "node:fs/promises"; -import { createServer, type Server } from "node:net"; +import net, { createServer, type Server } from "node:net"; import { tmpdir } from "node:os"; import { join } from "node:path"; +const originalPlatform = process.platform; +const originalCreateConnection = net.createConnection; const originalEnvironment = { HERDR_ENV: process.env.HERDR_ENV, HERDR_OMP_IDLE_DEBOUNCE_MS: process.env.HERDR_OMP_IDLE_DEBOUNCE_MS, @@ -30,6 +32,8 @@ afterEach(async () => { socketPath = undefined; } + Object.defineProperty(process, "platform", { value: originalPlatform }); + net.createConnection = originalCreateConnection; for (const [name, value] of Object.entries(originalEnvironment)) { if (value === undefined) { delete process.env[name]; @@ -44,6 +48,15 @@ const integrations = [ { name: "Oh My Pi", modulePath: "./omp/herdr-agent-state.ts" }, ] as const; +const socketPlugins = [ + { + name: "OpenCode", + modulePath: "./opencode/herdr-agent-state.js", + sessionID: "opencode-session", + }, + { name: "Kilo", modulePath: "./kilo/herdr-agent-state.js", sessionID: "kilo-session" }, +] as const; + function importFresh(modulePath: string) { importCounter += 1; return import(`${modulePath}?test=${importCounter}`); @@ -74,6 +87,15 @@ function configureIntegrationEnvironment(recordingSocketPath: string) { process.env.HERDR_PANE_ID = "test:p1"; } +function captureConnectionEndpoint() { + let connectedEndpoint: unknown; + net.createConnection = ((...args: unknown[]) => { + connectedEndpoint = args[0]; + return Reflect.apply(originalCreateConnection, net, args); + }) as typeof net.createConnection; + return () => connectedEndpoint; +} + async function startRecordingServer(name: string): Promise<unknown[]> { const recordingSocketPath = join(tmpdir(), `herdr-${name}-${process.pid}.sock`); socketPath = recordingSocketPath; @@ -102,7 +124,61 @@ async function startRecordingServer(name: string): Promise<unknown[]> { return requests; } +for (const socketPlugin of socketPlugins) { + test(`${socketPlugin.name} maps the Windows socket marker path to a named pipe endpoint`, async () => { + const markerPath = `herdr-${socketPlugin.name.toLowerCase()}-${process.pid}.sock`; + configureIntegrationEnvironment(markerPath); + Object.defineProperty(process, "platform", { value: "win32" }); + const connectedEndpoint = captureConnectionEndpoint(); + + const { HerdrAgentStatePlugin } = await importFresh(socketPlugin.modulePath); + const plugin = await HerdrAgentStatePlugin(); + await plugin.event({ + event: { + type: "session.updated", + properties: { sessionID: socketPlugin.sessionID }, + }, + }); + + expect(connectedEndpoint()).toBe(`\\\\.\\pipe\\${markerPath}`); + }); +} + +test("OpenCode stays disabled without the Herdr socket environment", async () => { + process.env.HERDR_ENV = "1"; + process.env.HERDR_PANE_ID = "test:p1"; + delete process.env.HERDR_SOCKET_PATH; + + const { HerdrAgentStatePlugin } = await importFresh("./opencode/herdr-agent-state.js"); + + expect(await HerdrAgentStatePlugin()).toEqual({}); +}); + for (const integration of integrations) { + test(`${integration.name} maps the Windows socket marker path to a named pipe endpoint`, async () => { + const markerPath = `herdr-${integration.name.toLowerCase().replaceAll(" ", "-")}-${process.pid}.sock`; + configureIntegrationEnvironment(markerPath); + Object.defineProperty(process, "platform", { value: "win32" }); + const connectedEndpoint = captureConnectionEndpoint(); + const { handlers, pi } = createExtensionHarness(); + + const { default: install } = await importFresh(integration.modulePath); + install(pi); + await handlers.get("session_start")?.( + { reason: "startup" }, + { + hasUI: true, + isIdle: () => true, + sessionManager: { + getSessionFile: () => undefined, + getSessionId: () => "test-session", + }, + }, + ); + + expect(connectedEndpoint()).toBe(`\\\\.\\pipe\\${markerPath}`); + }); + test(`${integration.name} reload preserves working state when the agent is active`, async () => { const requests = await startRecordingServer( integration.name.toLowerCase().replaceAll(" ", "-"), diff --git a/src/integration/assets/kilo/herdr-agent-state.js b/src/integration/assets/kilo/herdr-agent-state.js index fc12919c..e8600ab1 100644 --- a/src/integration/assets/kilo/herdr-agent-state.js +++ b/src/integration/assets/kilo/herdr-agent-state.js @@ -2,7 +2,7 @@ // managed by herdr; reinstalling or updating the integration overwrites this file. // add custom hooks/plugins beside this file instead of editing it. // HERDR_INTEGRATION_ID=kilo -// HERDR_INTEGRATION_VERSION=2 +// HERDR_INTEGRATION_VERSION=3 import net from "node:net"; @@ -48,6 +48,9 @@ function request(method, params) { return Promise.resolve(); } + const socketEndpoint = + process.platform === "win32" ? `\\\\.\\pipe\\${socketPath}` : socketPath; + const requestId = `${SOURCE}:${Date.now()}:${Math.floor(Math.random() * 1_000_000) .toString() .padStart(6, "0")}`; @@ -64,7 +67,7 @@ function request(method, params) { }; return new Promise((resolve) => { - const client = net.createConnection(socketPath, () => { + const client = net.createConnection(socketEndpoint, () => { client.write(`${JSON.stringify(request)}\n`); }); diff --git a/src/integration/assets/omp/herdr-agent-state.ts b/src/integration/assets/omp/herdr-agent-state.ts index 9bb6671a..350cdea7 100644 --- a/src/integration/assets/omp/herdr-agent-state.ts +++ b/src/integration/assets/omp/herdr-agent-state.ts @@ -2,13 +2,15 @@ // managed by herdr; reinstalling or updating the integration overwrites this file. // add custom hooks/plugins beside this file instead of editing it. // HERDR_INTEGRATION_ID=omp -// HERDR_INTEGRATION_VERSION=5 +// HERDR_INTEGRATION_VERSION=6 // @ts-nocheck -import { createConnection } from "node:net"; +import net from "node:net"; const HERDR_ENV = process.env.HERDR_ENV; const socketPath = process.env.HERDR_SOCKET_PATH; +const socketEndpoint = + process.platform === "win32" && socketPath ? `\\\\.\\pipe\\${socketPath}` : socketPath; const paneId = process.env.HERDR_PANE_ID; const source = "herdr:omp"; @@ -36,7 +38,7 @@ function sendRequestAttempt(request: unknown, timeoutMs: number): Promise<boolea resolve(delivered); }; - const socket = createConnection(socketPath!); + const socket = net.createConnection(socketEndpoint!); socket.on("error", () => finish(false)); socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`)); socket.on("data", () => finish(true)); diff --git a/src/integration/assets/opencode/herdr-agent-state.js b/src/integration/assets/opencode/herdr-agent-state.js index 4d59f9ec..27ea7ee5 100644 --- a/src/integration/assets/opencode/herdr-agent-state.js +++ b/src/integration/assets/opencode/herdr-agent-state.js @@ -62,6 +62,9 @@ function requestOnce(method, params) { return Promise.resolve(); } + const socketEndpoint = + process.platform === "win32" ? `\\\\.\\pipe\\${socketPath}` : socketPath; + const requestId = `${SOURCE}:${Date.now()}:${Math.floor(Math.random() * 1_000_000) .toString() .padStart(6, "0")}`; @@ -78,7 +81,7 @@ function requestOnce(method, params) { }; return new Promise((resolve) => { - const client = net.createConnection(socketPath, () => { + const client = net.createConnection(socketEndpoint, () => { client.write(`${JSON.stringify(request)}\n`); }); diff --git a/src/integration/assets/pi/herdr-agent-state.ts b/src/integration/assets/pi/herdr-agent-state.ts index 52340ebb..218cede9 100644 --- a/src/integration/assets/pi/herdr-agent-state.ts +++ b/src/integration/assets/pi/herdr-agent-state.ts @@ -2,13 +2,15 @@ // managed by herdr; reinstalling or updating the integration overwrites this file. // add custom hooks/plugins beside this file instead of editing it. // HERDR_INTEGRATION_ID=pi -// HERDR_INTEGRATION_VERSION=5 +// HERDR_INTEGRATION_VERSION=6 // @ts-nocheck -import { createConnection } from "node:net"; +import net from "node:net"; const HERDR_ENV = process.env.HERDR_ENV; const socketPath = process.env.HERDR_SOCKET_PATH; +const socketEndpoint = + process.platform === "win32" && socketPath ? `\\\\.\\pipe\\${socketPath}` : socketPath; const paneId = process.env.HERDR_PANE_ID; const source = "herdr:pi"; @@ -34,7 +36,7 @@ function sendRequestAttempt(request: unknown, timeoutMs: number): Promise<boolea resolve(delivered); }; - const socket = createConnection(socketPath!); + const socket = net.createConnection(socketEndpoint!); socket.on("error", () => finish(false)); socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`)); socket.on("data", () => finish(true)); |
