aboutsummaryrefslogtreecommitdiff
path: root/src/integration/assets
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-18 23:46:34 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-18 23:56:20 +0300
commita51a15914a5b29155022a1fd589d2bcc6409cadf (patch)
tree8602b6d94c811666d49e4c2aa7636bb6a9b0e8a3 /src/integration/assets
parent60c040057e3949ae93ee88d495df09b4b848d22d (diff)
fix: support javascript integrations on windows
refs #1531
Diffstat (limited to 'src/integration/assets')
-rw-r--r--src/integration/assets/herdr-agent-state.test.ts78
-rw-r--r--src/integration/assets/kilo/herdr-agent-state.js7
-rw-r--r--src/integration/assets/omp/herdr-agent-state.ts8
-rw-r--r--src/integration/assets/opencode/herdr-agent-state.js5
-rw-r--r--src/integration/assets/pi/herdr-agent-state.ts8
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));