@hivemind-os/collective-shim 0.2.6 → 0.2.8
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/.turbo/turbo-build.log +4 -4
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge.ts +4 -3
- package/src/daemon-launcher.ts +3 -3
- package/src/ipc-client.ts +2 -3
- package/tests/bridge.test.ts +47 -17
- package/tsup.config.ts +6 -0
- package/vitest.config.ts +6 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -6,9 +6,9 @@ $ tsup
|
|
|
6
6
|
[34mCLI[39m Target: es2022
|
|
7
7
|
[34mCLI[39m Cleaning output folder
|
|
8
8
|
[34mESM[39m Build start
|
|
9
|
-
[32mESM[39m [1mdist/index.js [22m[32m13.
|
|
10
|
-
[32mESM[39m [1mdist/index.js.map [22m[
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
9
|
+
[32mESM[39m [1mdist/index.js [22m[32m13.01 KB[39m
|
|
10
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m25.81 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 33ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 2365ms
|
|
14
14
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m20.00 B[39m
|
package/dist/index.js
CHANGED
|
@@ -15,8 +15,8 @@ import { homedir, userInfo } from "os";
|
|
|
15
15
|
import { resolve } from "path";
|
|
16
16
|
import { setTimeout as delay } from "timers/promises";
|
|
17
17
|
import { fileURLToPath } from "url";
|
|
18
|
+
var SHIM_VERSION = "0.2.7";
|
|
18
19
|
var require2 = createRequire(import.meta.url);
|
|
19
|
-
var { version: SHIM_VERSION } = require2("../package.json");
|
|
20
20
|
var LEGACY_WINDOWS_PIPE_PATH = "\\\\.\\pipe\\hivemind-collective";
|
|
21
21
|
function getDefaultIpcPath() {
|
|
22
22
|
return process.env.COLLECTIVE_IPC_PATH ?? (process.platform === "win32" ? `${LEGACY_WINDOWS_PIPE_PATH}-${sanitizePipeSegment(getCurrentUsername())}` : resolve(homedir(), ".hivemind-os/collective", "mesh.sock"));
|
|
@@ -145,11 +145,9 @@ async function stopDaemon(options) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
// src/ipc-client.ts
|
|
148
|
-
import { createRequire as createRequire2 } from "module";
|
|
149
148
|
import { randomUUID } from "crypto";
|
|
150
149
|
import net2 from "net";
|
|
151
|
-
var
|
|
152
|
-
var { version: SHIM_VERSION2 } = require3("../package.json");
|
|
150
|
+
var SHIM_VERSION2 = "0.2.7";
|
|
153
151
|
var IpcClient = class {
|
|
154
152
|
constructor(ipcPath) {
|
|
155
153
|
this.ipcPath = ipcPath;
|
|
@@ -338,10 +336,10 @@ async function createBridge(options = {}) {
|
|
|
338
336
|
});
|
|
339
337
|
try {
|
|
340
338
|
const hello = await next.connect(appName);
|
|
341
|
-
if (!upgraded && hello.daemonVersion
|
|
339
|
+
if (!upgraded && hello.daemonVersion !== SHIM_VERSION) {
|
|
342
340
|
upgraded = true;
|
|
343
341
|
stderr.write(
|
|
344
|
-
`mesh-shim: Upgrading daemon from v${hello.daemonVersion} to v${SHIM_VERSION}
|
|
342
|
+
`mesh-shim: Upgrading daemon from v${hello.daemonVersion ?? "unknown"} to v${SHIM_VERSION}
|
|
345
343
|
`
|
|
346
344
|
);
|
|
347
345
|
next.close();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bridge.ts","../src/daemon-launcher.ts","../src/ipc-client.ts","../src/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { basename } from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { promisify } from 'node:util';\n\nimport {\n ensureDaemonRunning,\n getDefaultIpcPath,\n getDefaultPidFile,\n resolveDaemonBin,\n stopDaemon,\n SHIM_VERSION,\n type LauncherOptions,\n} from './daemon-launcher.js';\nimport { IpcClient } from './ipc-client.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface BridgeOptions {\n ipcPath?: string;\n pidFile?: string;\n daemonBin?: string;\n startupTimeoutMs?: number;\n appName?: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: NodeJS.WritableStream;\n stderr?: NodeJS.WritableStream;\n exit?: (code: number) => void;\n ensureDaemon?: (options: LauncherOptions) => Promise<void>;\n stopDaemon?: (options: { pidFile: string; ipcPath: string }) => Promise<void>;\n}\n\nexport interface BridgeHandle {\n close(): void;\n}\n\nexport async function startShim(): Promise<void> {\n await createBridge();\n}\n\nexport async function createBridge(options: BridgeOptions = {}): Promise<BridgeHandle> {\n const stdin = options.stdin ?? process.stdin;\n const stdout = options.stdout ?? process.stdout;\n const stderr = options.stderr ?? process.stderr;\n const exit = options.exit ?? ((code: number) => process.exit(code));\n const ensureDaemon = options.ensureDaemon ?? ensureDaemonRunning;\n const stopDaemonFn = options.stopDaemon ?? stopDaemon;\n const launcherOptions: LauncherOptions = {\n ipcPath: options.ipcPath ?? getDefaultIpcPath(),\n pidFile: options.pidFile ?? getDefaultPidFile(),\n daemonBin: options.daemonBin ?? resolveDaemonBin(),\n startupTimeoutMs: options.startupTimeoutMs ?? 30_000,\n };\n const appName = options.appName ?? (await guessAppName());\n\n let client: IpcClient | undefined;\n let stdinBuffer = '';\n let closing = false;\n let reconnecting: Promise<void> | undefined;\n const pending: string[] = [];\n\n const writeError = (message: string) => {\n stdout.write(`${JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32000, message } })}\\n`);\n stderr.write(`mesh-shim: ${message}\\n`);\n };\n\n const flushPending = () => {\n if (!client) {\n return;\n }\n\n while (pending.length > 0) {\n try {\n client.sendRaw(pending[0] as string);\n pending.shift();\n } catch {\n return;\n }\n }\n };\n\n let upgraded = false;\n\n const connect = async () => {\n await ensureDaemon(launcherOptions);\n const deadline = Date.now() + launcherOptions.startupTimeoutMs;\n while (true) {\n const next = new IpcClient(launcherOptions.ipcPath);\n next.onMessage((message) => {\n stdout.write(`${JSON.stringify(message)}\\n`);\n });\n next.onClose(() => {\n if (!closing && client === next) {\n client = undefined;\n void reconnect();\n }\n });\n\n try {\n const hello = await next.connect(appName);\n\n // Version mismatch: stop old daemon and restart (once per session)\n if (!upgraded && hello.daemonVersion && hello.daemonVersion !== SHIM_VERSION) {\n upgraded = true;\n stderr.write(\n `mesh-shim: Upgrading daemon from v${hello.daemonVersion} to v${SHIM_VERSION}\\n`,\n );\n next.close();\n await stopDaemonFn({ pidFile: launcherOptions.pidFile, ipcPath: launcherOptions.ipcPath });\n await ensureDaemon(launcherOptions);\n continue;\n }\n\n client = next;\n flushPending();\n return;\n } catch (error) {\n next.close();\n if (Date.now() >= deadline) {\n throw error;\n }\n await delay(200);\n }\n }\n };\n\n const reconnect = async () => {\n if (reconnecting) {\n return reconnecting;\n }\n\n reconnecting = (async () => {\n try {\n await connect();\n } catch (error) {\n if (!closing) {\n closing = true;\n client?.close();\n writeError(`Unable to reach mesh daemon: ${(error as Error).message}`);\n exit(1);\n }\n } finally {\n reconnecting = undefined;\n }\n })();\n\n return reconnecting;\n };\n\n try {\n await connect();\n } catch (error) {\n closing = true;\n writeError(`Unable to start mesh daemon: ${(error as Error).message}`);\n exit(1);\n return { close: () => undefined };\n }\n\n if ('setEncoding' in stdin && typeof stdin.setEncoding === 'function') {\n stdin.setEncoding('utf8');\n }\n\n const handleChunk = (chunk: string | Buffer) => {\n stdinBuffer += chunk.toString();\n let newlineIndex = stdinBuffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const line = stdinBuffer.slice(0, newlineIndex).trim();\n stdinBuffer = stdinBuffer.slice(newlineIndex + 1);\n if (line) {\n if (client) {\n try {\n client.sendRaw(line);\n } catch {\n pending.push(line);\n void reconnect();\n }\n } else {\n pending.push(line);\n void reconnect();\n }\n }\n newlineIndex = stdinBuffer.indexOf('\\n');\n }\n };\n\n const shutdown = () => {\n if (closing) {\n return;\n }\n\n closing = true;\n stdin.off('data', handleChunk);\n stdin.off('end', shutdown);\n stdin.off('close', shutdown);\n client?.close();\n exit(0);\n };\n\n stdin.on('data', handleChunk);\n stdin.on('end', shutdown);\n stdin.on('close', shutdown);\n if ('resume' in stdin && typeof stdin.resume === 'function') {\n stdin.resume();\n }\n\n return { close: shutdown };\n}\n\nasync function guessAppName(): Promise<string> {\n if (process.env.COLLECTIVE_APP_NAME) {\n return process.env.COLLECTIVE_APP_NAME;\n }\n\n const arg = process.argv\n .map((value) => basename(value).toLowerCase())\n .find((value) => value && !['node', 'node.exe', 'mesh-shim', 'index.js'].includes(value));\n if (arg) {\n return arg.replace(/\\.(cmd|exe)$/i, '');\n }\n\n try {\n const result =\n process.platform === 'win32'\n ? await execFileAsync(\n 'powershell',\n ['-NoProfile', '-Command', `(Get-CimInstance Win32_Process -Filter \"ProcessId = ${process.ppid}\").Name`],\n { windowsHide: true },\n )\n : await execFileAsync('ps', ['-o', 'comm=', '-p', String(process.ppid)]);\n const name = basename(result.stdout.trim()).replace(/\\.(cmd|exe)$/i, '').toLowerCase();\n return name || 'unknown';\n } catch {\n return 'unknown';\n }\n}\n","import { spawn, spawnSync } from 'node:child_process';\nimport { readFileSync, unlinkSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport net from 'node:net';\nimport { homedir, userInfo } from 'node:os';\nimport { resolve } from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { fileURLToPath } from 'node:url';\n\nconst require = createRequire(import.meta.url);\nconst { version: SHIM_VERSION } = require('../package.json') as { version: string };\n\nexport { SHIM_VERSION };\n\nexport interface LauncherOptions {\n ipcPath: string;\n pidFile: string;\n daemonBin: string;\n startupTimeoutMs: number;\n}\n\nconst LEGACY_WINDOWS_PIPE_PATH = '\\\\\\\\.\\\\pipe\\\\hivemind-collective';\n\nexport function getDefaultIpcPath(): string {\n return process.env.COLLECTIVE_IPC_PATH ??\n (process.platform === 'win32'\n ? `${LEGACY_WINDOWS_PIPE_PATH}-${sanitizePipeSegment(getCurrentUsername())}`\n : resolve(homedir(), '.hivemind-os/collective', 'mesh.sock'));\n}\n\nexport function getDefaultPidFile(): string {\n return process.env.COLLECTIVE_PID_FILE ?? resolve(homedir(), '.hivemind-os/collective', 'daemon.pid');\n}\n\nexport function resolveDaemonBin(): string {\n if (process.env.COLLECTIVE_DAEMON_BIN) {\n return process.env.COLLECTIVE_DAEMON_BIN;\n }\n\n try {\n return require.resolve('@hivemind-os/collective-daemon');\n } catch {\n if (commandExists('collective-daemon')) {\n return 'collective-daemon';\n }\n\n // Monorepo fallback (only works when running from packages/shim/)\n return fileURLToPath(new URL('../../daemon/dist/index.js', import.meta.url));\n }\n}\n\nexport async function isDaemonRunning(ipcPath: string): Promise<boolean> {\n return new Promise((resolvePromise) => {\n const socket = net.connect(ipcPath);\n const finish = (running: boolean) => {\n socket.removeAllListeners();\n socket.destroy();\n resolvePromise(running);\n };\n\n socket.once('connect', () => {\n finish(true);\n });\n socket.once('error', () => {\n finish(false);\n });\n });\n}\n\nexport async function ensureDaemonRunning(options: LauncherOptions): Promise<void> {\n if (await isDaemonRunning(options.ipcPath)) {\n return;\n }\n\n const command = options.daemonBin.endsWith('.js') ? process.execPath : options.daemonBin;\n const args = options.daemonBin.endsWith('.js') ? [options.daemonBin] : [];\n const child = spawn(command, args, { detached: true, stdio: ['ignore', 'ignore', 'pipe'] });\n let stderrOutput = '';\n child.stderr?.setEncoding('utf8');\n child.stderr?.on('data', (chunk: string) => {\n stderrOutput += chunk;\n });\n child.unref();\n child.stderr?.unref();\n\n let exited = false;\n let exitCode: number | null = null;\n child.once('exit', (code) => {\n exited = true;\n exitCode = code;\n });\n\n const deadline = Date.now() + options.startupTimeoutMs;\n while (Date.now() < deadline) {\n await delay(200);\n if (await isDaemonRunning(options.ipcPath)) {\n return;\n }\n if (exited) {\n const detail = stderrOutput.trim() ? `\\n${stderrOutput.trim()}` : '';\n throw new Error(`Daemon process exited with code ${exitCode} before IPC was ready.${detail}`);\n }\n }\n\n throw new Error(`Timed out waiting for daemon IPC at ${options.ipcPath} (pid file: ${options.pidFile})`);\n}\n\nfunction commandExists(command: string): boolean {\n const probe = process.platform === 'win32' ? 'where' : 'which';\n return spawnSync(probe, [command], { stdio: 'ignore' }).status === 0;\n}\n\nfunction getCurrentUsername(): string {\n try {\n return userInfo().username;\n } catch {\n return process.env.USERNAME ?? process.env.USER ?? 'unknown-user';\n }\n}\n\nfunction sanitizePipeSegment(value: string): string {\n const leaf = value.split(/[\\\\/]+/).filter(Boolean).at(-1) ?? value;\n const sanitized = leaf.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');\n return sanitized || 'unknown-user';\n}\n\n/**\n * Stop the running daemon by reading its PID file and sending SIGTERM.\n * Waits for the IPC socket to go down before returning.\n */\nexport async function stopDaemon(options: { pidFile: string; ipcPath: string; timeoutMs?: number }): Promise<void> {\n const { pidFile, ipcPath, timeoutMs = 10_000 } = options;\n\n let pid: number;\n try {\n pid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10);\n } catch {\n // No PID file — daemon may already be dead\n return;\n }\n\n if (isNaN(pid)) {\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n return;\n }\n\n // Send SIGTERM (on Windows, process.kill sends a termination signal)\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n return;\n }\n\n // Wait for the IPC socket to go down\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!(await isDaemonRunning(ipcPath))) {\n return;\n }\n await delay(200);\n }\n\n // Force kill if still alive\n try {\n process.kill(pid, 'SIGKILL');\n } catch { /* already gone */ }\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n}\n","import { createRequire } from 'node:module';\nimport { randomUUID } from 'node:crypto';\nimport net from 'node:net';\n\nconst require = createRequire(import.meta.url);\nconst { version: SHIM_VERSION } = require('../package.json') as { version: string };\n\ntype MessageHandler = (message: object) => void;\ntype CloseHandler = () => void;\n\nexport interface HelloResult {\n daemonVersion?: string;\n connectionId?: string;\n}\n\nexport class IpcClient {\n private socket?: net.Socket;\n private buffer = '';\n private readonly messageHandlers = new Set<MessageHandler>();\n private readonly closeHandlers = new Set<CloseHandler>();\n private helloId?: string;\n private helloWaiter?: { resolve: (result: HelloResult) => void; reject: (error: Error) => void };\n\n constructor(private readonly ipcPath: string) {}\n\n async connect(appName = 'unknown'): Promise<HelloResult> {\n this.close();\n const socket = await new Promise<net.Socket>((resolve, reject) => {\n const client = net.connect(this.ipcPath, () => {\n client.off('error', reject);\n resolve(client);\n });\n client.once('error', reject);\n });\n\n this.socket = socket;\n socket.setEncoding('utf8');\n socket.setNoDelay(true);\n socket.on('data', (chunk: string | Buffer) => {\n this.buffer += chunk.toString();\n this.drainBuffer();\n });\n socket.on('close', () => {\n this.handleClose();\n });\n socket.on('end', () => {\n this.handleClose();\n });\n socket.on('error', () => undefined);\n\n this.helloId = `shim-hello-${randomUUID()}`;\n return new Promise<HelloResult>((resolve, reject) => {\n this.helloWaiter = { resolve, reject };\n this.send({\n jsonrpc: '2.0',\n id: this.helloId as string,\n method: 'shim_hello',\n params: {\n appName,\n pid: process.pid,\n shimVersion: SHIM_VERSION,\n ...(process.env.COLLECTIVE_PROFILE ? { profile: process.env.COLLECTIVE_PROFILE } : {}),\n },\n });\n });\n }\n\n send(message: object): void {\n this.sendRaw(JSON.stringify(message));\n }\n\n sendRaw(line: string): void {\n const socket = this.socket;\n if (!socket || socket.destroyed) {\n throw new Error('IPC client is not connected');\n }\n\n socket.write(`${line.trimEnd()}\\n`);\n }\n\n onMessage(handler: (message: object) => void): void {\n this.messageHandlers.add(handler);\n }\n\n onClose(handler: () => void): void {\n this.closeHandlers.add(handler);\n }\n\n close(): void {\n const socket = this.socket;\n this.socket = undefined;\n this.buffer = '';\n this.rejectHello(new Error('IPC connection closed'));\n if (socket && !socket.destroyed) {\n socket.destroy();\n }\n }\n\n private drainBuffer(): void {\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const line = this.buffer.slice(0, newlineIndex).trim();\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line) {\n const message = JSON.parse(line) as Record<string, unknown>;\n if (this.helloWaiter && message.id === this.helloId) {\n const waiter = this.helloWaiter;\n this.helloWaiter = undefined;\n this.helloId = undefined;\n if (message.error && typeof message.error === 'object') {\n waiter.reject(new Error(String((message.error as { message?: unknown }).message ?? 'shim_hello failed')));\n } else {\n const result = (message.result ?? {}) as Record<string, unknown>;\n waiter.resolve({\n daemonVersion: typeof result.daemonVersion === 'string' ? result.daemonVersion : undefined,\n connectionId: typeof result.connectionId === 'string' ? result.connectionId : undefined,\n });\n }\n } else {\n for (const handler of this.messageHandlers) {\n handler(message);\n }\n }\n }\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private handleClose(): void {\n if (!this.socket && !this.helloWaiter) {\n return;\n }\n\n this.socket = undefined;\n this.buffer = '';\n this.rejectHello(new Error('IPC connection closed'));\n for (const handler of this.closeHandlers) {\n handler();\n }\n }\n\n private rejectHello(error: Error): void {\n if (!this.helloWaiter) {\n return;\n }\n\n const waiter = this.helloWaiter;\n this.helloWaiter = undefined;\n this.helloId = undefined;\n waiter.reject(error);\n }\n}\n","#!/usr/bin/env node\n\nimport { startShim } from './bridge.js';\n\nstartShim().catch((err) => {\n process.stderr.write(`mesh-shim fatal: ${err.message}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["delay","require","require","createRequire","net","require","SHIM_VERSION","resolve","delay"]}
|
|
1
|
+
{"version":3,"sources":["../src/bridge.ts","../src/daemon-launcher.ts","../src/ipc-client.ts","../src/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { basename } from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { promisify } from 'node:util';\n\nimport {\n ensureDaemonRunning,\n getDefaultIpcPath,\n getDefaultPidFile,\n resolveDaemonBin,\n stopDaemon,\n SHIM_VERSION,\n type LauncherOptions,\n} from './daemon-launcher.js';\nimport { IpcClient } from './ipc-client.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface BridgeOptions {\n ipcPath?: string;\n pidFile?: string;\n daemonBin?: string;\n startupTimeoutMs?: number;\n appName?: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: NodeJS.WritableStream;\n stderr?: NodeJS.WritableStream;\n exit?: (code: number) => void;\n ensureDaemon?: (options: LauncherOptions) => Promise<void>;\n stopDaemon?: (options: { pidFile: string; ipcPath: string }) => Promise<void>;\n}\n\nexport interface BridgeHandle {\n close(): void;\n}\n\nexport async function startShim(): Promise<void> {\n await createBridge();\n}\n\nexport async function createBridge(options: BridgeOptions = {}): Promise<BridgeHandle> {\n const stdin = options.stdin ?? process.stdin;\n const stdout = options.stdout ?? process.stdout;\n const stderr = options.stderr ?? process.stderr;\n const exit = options.exit ?? ((code: number) => process.exit(code));\n const ensureDaemon = options.ensureDaemon ?? ensureDaemonRunning;\n const stopDaemonFn = options.stopDaemon ?? stopDaemon;\n const launcherOptions: LauncherOptions = {\n ipcPath: options.ipcPath ?? getDefaultIpcPath(),\n pidFile: options.pidFile ?? getDefaultPidFile(),\n daemonBin: options.daemonBin ?? resolveDaemonBin(),\n startupTimeoutMs: options.startupTimeoutMs ?? 30_000,\n };\n const appName = options.appName ?? (await guessAppName());\n\n let client: IpcClient | undefined;\n let stdinBuffer = '';\n let closing = false;\n let reconnecting: Promise<void> | undefined;\n const pending: string[] = [];\n\n const writeError = (message: string) => {\n stdout.write(`${JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32000, message } })}\\n`);\n stderr.write(`mesh-shim: ${message}\\n`);\n };\n\n const flushPending = () => {\n if (!client) {\n return;\n }\n\n while (pending.length > 0) {\n try {\n client.sendRaw(pending[0] as string);\n pending.shift();\n } catch {\n return;\n }\n }\n };\n\n let upgraded = false;\n\n const connect = async () => {\n await ensureDaemon(launcherOptions);\n const deadline = Date.now() + launcherOptions.startupTimeoutMs;\n while (true) {\n const next = new IpcClient(launcherOptions.ipcPath);\n next.onMessage((message) => {\n stdout.write(`${JSON.stringify(message)}\\n`);\n });\n next.onClose(() => {\n if (!closing && client === next) {\n client = undefined;\n void reconnect();\n }\n });\n\n try {\n const hello = await next.connect(appName);\n\n // Version mismatch: stop old daemon and restart (once per session).\n // Also upgrade legacy daemons that don't report a version.\n if (!upgraded && hello.daemonVersion !== SHIM_VERSION) {\n upgraded = true;\n stderr.write(\n `mesh-shim: Upgrading daemon from v${hello.daemonVersion ?? 'unknown'} to v${SHIM_VERSION}\\n`,\n );\n next.close();\n await stopDaemonFn({ pidFile: launcherOptions.pidFile, ipcPath: launcherOptions.ipcPath });\n await ensureDaemon(launcherOptions);\n continue;\n }\n\n client = next;\n flushPending();\n return;\n } catch (error) {\n next.close();\n if (Date.now() >= deadline) {\n throw error;\n }\n await delay(200);\n }\n }\n };\n\n const reconnect = async () => {\n if (reconnecting) {\n return reconnecting;\n }\n\n reconnecting = (async () => {\n try {\n await connect();\n } catch (error) {\n if (!closing) {\n closing = true;\n client?.close();\n writeError(`Unable to reach mesh daemon: ${(error as Error).message}`);\n exit(1);\n }\n } finally {\n reconnecting = undefined;\n }\n })();\n\n return reconnecting;\n };\n\n try {\n await connect();\n } catch (error) {\n closing = true;\n writeError(`Unable to start mesh daemon: ${(error as Error).message}`);\n exit(1);\n return { close: () => undefined };\n }\n\n if ('setEncoding' in stdin && typeof stdin.setEncoding === 'function') {\n stdin.setEncoding('utf8');\n }\n\n const handleChunk = (chunk: string | Buffer) => {\n stdinBuffer += chunk.toString();\n let newlineIndex = stdinBuffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const line = stdinBuffer.slice(0, newlineIndex).trim();\n stdinBuffer = stdinBuffer.slice(newlineIndex + 1);\n if (line) {\n if (client) {\n try {\n client.sendRaw(line);\n } catch {\n pending.push(line);\n void reconnect();\n }\n } else {\n pending.push(line);\n void reconnect();\n }\n }\n newlineIndex = stdinBuffer.indexOf('\\n');\n }\n };\n\n const shutdown = () => {\n if (closing) {\n return;\n }\n\n closing = true;\n stdin.off('data', handleChunk);\n stdin.off('end', shutdown);\n stdin.off('close', shutdown);\n client?.close();\n exit(0);\n };\n\n stdin.on('data', handleChunk);\n stdin.on('end', shutdown);\n stdin.on('close', shutdown);\n if ('resume' in stdin && typeof stdin.resume === 'function') {\n stdin.resume();\n }\n\n return { close: shutdown };\n}\n\nasync function guessAppName(): Promise<string> {\n if (process.env.COLLECTIVE_APP_NAME) {\n return process.env.COLLECTIVE_APP_NAME;\n }\n\n const arg = process.argv\n .map((value) => basename(value).toLowerCase())\n .find((value) => value && !['node', 'node.exe', 'mesh-shim', 'index.js'].includes(value));\n if (arg) {\n return arg.replace(/\\.(cmd|exe)$/i, '');\n }\n\n try {\n const result =\n process.platform === 'win32'\n ? await execFileAsync(\n 'powershell',\n ['-NoProfile', '-Command', `(Get-CimInstance Win32_Process -Filter \"ProcessId = ${process.ppid}\").Name`],\n { windowsHide: true },\n )\n : await execFileAsync('ps', ['-o', 'comm=', '-p', String(process.ppid)]);\n const name = basename(result.stdout.trim()).replace(/\\.(cmd|exe)$/i, '').toLowerCase();\n return name || 'unknown';\n } catch {\n return 'unknown';\n }\n}\n","import { spawn, spawnSync } from 'node:child_process';\nimport { readFileSync, unlinkSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport net from 'node:net';\nimport { homedir, userInfo } from 'node:os';\nimport { resolve } from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { fileURLToPath } from 'node:url';\n\ndeclare const PKG_VERSION: string;\nexport const SHIM_VERSION = PKG_VERSION;\n\nconst require = createRequire(import.meta.url);\n\nexport interface LauncherOptions {\n ipcPath: string;\n pidFile: string;\n daemonBin: string;\n startupTimeoutMs: number;\n}\n\nconst LEGACY_WINDOWS_PIPE_PATH = '\\\\\\\\.\\\\pipe\\\\hivemind-collective';\n\nexport function getDefaultIpcPath(): string {\n return process.env.COLLECTIVE_IPC_PATH ??\n (process.platform === 'win32'\n ? `${LEGACY_WINDOWS_PIPE_PATH}-${sanitizePipeSegment(getCurrentUsername())}`\n : resolve(homedir(), '.hivemind-os/collective', 'mesh.sock'));\n}\n\nexport function getDefaultPidFile(): string {\n return process.env.COLLECTIVE_PID_FILE ?? resolve(homedir(), '.hivemind-os/collective', 'daemon.pid');\n}\n\nexport function resolveDaemonBin(): string {\n if (process.env.COLLECTIVE_DAEMON_BIN) {\n return process.env.COLLECTIVE_DAEMON_BIN;\n }\n\n try {\n return require.resolve('@hivemind-os/collective-daemon');\n } catch {\n if (commandExists('collective-daemon')) {\n return 'collective-daemon';\n }\n\n // Monorepo fallback (only works when running from packages/shim/)\n return fileURLToPath(new URL('../../daemon/dist/index.js', import.meta.url));\n }\n}\n\nexport async function isDaemonRunning(ipcPath: string): Promise<boolean> {\n return new Promise((resolvePromise) => {\n const socket = net.connect(ipcPath);\n const finish = (running: boolean) => {\n socket.removeAllListeners();\n socket.destroy();\n resolvePromise(running);\n };\n\n socket.once('connect', () => {\n finish(true);\n });\n socket.once('error', () => {\n finish(false);\n });\n });\n}\n\nexport async function ensureDaemonRunning(options: LauncherOptions): Promise<void> {\n if (await isDaemonRunning(options.ipcPath)) {\n return;\n }\n\n const command = options.daemonBin.endsWith('.js') ? process.execPath : options.daemonBin;\n const args = options.daemonBin.endsWith('.js') ? [options.daemonBin] : [];\n const child = spawn(command, args, { detached: true, stdio: ['ignore', 'ignore', 'pipe'] });\n let stderrOutput = '';\n child.stderr?.setEncoding('utf8');\n child.stderr?.on('data', (chunk: string) => {\n stderrOutput += chunk;\n });\n child.unref();\n child.stderr?.unref();\n\n let exited = false;\n let exitCode: number | null = null;\n child.once('exit', (code) => {\n exited = true;\n exitCode = code;\n });\n\n const deadline = Date.now() + options.startupTimeoutMs;\n while (Date.now() < deadline) {\n await delay(200);\n if (await isDaemonRunning(options.ipcPath)) {\n return;\n }\n if (exited) {\n const detail = stderrOutput.trim() ? `\\n${stderrOutput.trim()}` : '';\n throw new Error(`Daemon process exited with code ${exitCode} before IPC was ready.${detail}`);\n }\n }\n\n throw new Error(`Timed out waiting for daemon IPC at ${options.ipcPath} (pid file: ${options.pidFile})`);\n}\n\nfunction commandExists(command: string): boolean {\n const probe = process.platform === 'win32' ? 'where' : 'which';\n return spawnSync(probe, [command], { stdio: 'ignore' }).status === 0;\n}\n\nfunction getCurrentUsername(): string {\n try {\n return userInfo().username;\n } catch {\n return process.env.USERNAME ?? process.env.USER ?? 'unknown-user';\n }\n}\n\nfunction sanitizePipeSegment(value: string): string {\n const leaf = value.split(/[\\\\/]+/).filter(Boolean).at(-1) ?? value;\n const sanitized = leaf.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');\n return sanitized || 'unknown-user';\n}\n\n/**\n * Stop the running daemon by reading its PID file and sending SIGTERM.\n * Waits for the IPC socket to go down before returning.\n */\nexport async function stopDaemon(options: { pidFile: string; ipcPath: string; timeoutMs?: number }): Promise<void> {\n const { pidFile, ipcPath, timeoutMs = 10_000 } = options;\n\n let pid: number;\n try {\n pid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10);\n } catch {\n // No PID file — daemon may already be dead\n return;\n }\n\n if (isNaN(pid)) {\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n return;\n }\n\n // Send SIGTERM (on Windows, process.kill sends a termination signal)\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n return;\n }\n\n // Wait for the IPC socket to go down\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!(await isDaemonRunning(ipcPath))) {\n return;\n }\n await delay(200);\n }\n\n // Force kill if still alive\n try {\n process.kill(pid, 'SIGKILL');\n } catch { /* already gone */ }\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n}\n","import { randomUUID } from 'node:crypto';\nimport net from 'node:net';\n\ndeclare const PKG_VERSION: string;\nconst SHIM_VERSION = PKG_VERSION;\n\ntype MessageHandler = (message: object) => void;\ntype CloseHandler = () => void;\n\nexport interface HelloResult {\n daemonVersion?: string;\n connectionId?: string;\n}\n\nexport class IpcClient {\n private socket?: net.Socket;\n private buffer = '';\n private readonly messageHandlers = new Set<MessageHandler>();\n private readonly closeHandlers = new Set<CloseHandler>();\n private helloId?: string;\n private helloWaiter?: { resolve: (result: HelloResult) => void; reject: (error: Error) => void };\n\n constructor(private readonly ipcPath: string) {}\n\n async connect(appName = 'unknown'): Promise<HelloResult> {\n this.close();\n const socket = await new Promise<net.Socket>((resolve, reject) => {\n const client = net.connect(this.ipcPath, () => {\n client.off('error', reject);\n resolve(client);\n });\n client.once('error', reject);\n });\n\n this.socket = socket;\n socket.setEncoding('utf8');\n socket.setNoDelay(true);\n socket.on('data', (chunk: string | Buffer) => {\n this.buffer += chunk.toString();\n this.drainBuffer();\n });\n socket.on('close', () => {\n this.handleClose();\n });\n socket.on('end', () => {\n this.handleClose();\n });\n socket.on('error', () => undefined);\n\n this.helloId = `shim-hello-${randomUUID()}`;\n return new Promise<HelloResult>((resolve, reject) => {\n this.helloWaiter = { resolve, reject };\n this.send({\n jsonrpc: '2.0',\n id: this.helloId as string,\n method: 'shim_hello',\n params: {\n appName,\n pid: process.pid,\n shimVersion: SHIM_VERSION,\n ...(process.env.COLLECTIVE_PROFILE ? { profile: process.env.COLLECTIVE_PROFILE } : {}),\n },\n });\n });\n }\n\n send(message: object): void {\n this.sendRaw(JSON.stringify(message));\n }\n\n sendRaw(line: string): void {\n const socket = this.socket;\n if (!socket || socket.destroyed) {\n throw new Error('IPC client is not connected');\n }\n\n socket.write(`${line.trimEnd()}\\n`);\n }\n\n onMessage(handler: (message: object) => void): void {\n this.messageHandlers.add(handler);\n }\n\n onClose(handler: () => void): void {\n this.closeHandlers.add(handler);\n }\n\n close(): void {\n const socket = this.socket;\n this.socket = undefined;\n this.buffer = '';\n this.rejectHello(new Error('IPC connection closed'));\n if (socket && !socket.destroyed) {\n socket.destroy();\n }\n }\n\n private drainBuffer(): void {\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const line = this.buffer.slice(0, newlineIndex).trim();\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line) {\n const message = JSON.parse(line) as Record<string, unknown>;\n if (this.helloWaiter && message.id === this.helloId) {\n const waiter = this.helloWaiter;\n this.helloWaiter = undefined;\n this.helloId = undefined;\n if (message.error && typeof message.error === 'object') {\n waiter.reject(new Error(String((message.error as { message?: unknown }).message ?? 'shim_hello failed')));\n } else {\n const result = (message.result ?? {}) as Record<string, unknown>;\n waiter.resolve({\n daemonVersion: typeof result.daemonVersion === 'string' ? result.daemonVersion : undefined,\n connectionId: typeof result.connectionId === 'string' ? result.connectionId : undefined,\n });\n }\n } else {\n for (const handler of this.messageHandlers) {\n handler(message);\n }\n }\n }\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private handleClose(): void {\n if (!this.socket && !this.helloWaiter) {\n return;\n }\n\n this.socket = undefined;\n this.buffer = '';\n this.rejectHello(new Error('IPC connection closed'));\n for (const handler of this.closeHandlers) {\n handler();\n }\n }\n\n private rejectHello(error: Error): void {\n if (!this.helloWaiter) {\n return;\n }\n\n const waiter = this.helloWaiter;\n this.helloWaiter = undefined;\n this.helloId = undefined;\n waiter.reject(error);\n }\n}\n","#!/usr/bin/env node\n\nimport { startShim } from './bridge.js';\n\nstartShim().catch((err) => {\n process.stderr.write(`mesh-shim fatal: ${err.message}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["delay","require","net","SHIM_VERSION","resolve","delay"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hivemind-os/collective-shim",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"pino": "^9.0.0",
|
|
12
|
-
"@hivemind-os/collective-daemon": "0.2.
|
|
12
|
+
"@hivemind-os/collective-daemon": "0.2.8"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"vitest": "^3.0.0",
|
package/src/bridge.ts
CHANGED
|
@@ -99,11 +99,12 @@ export async function createBridge(options: BridgeOptions = {}): Promise<BridgeH
|
|
|
99
99
|
try {
|
|
100
100
|
const hello = await next.connect(appName);
|
|
101
101
|
|
|
102
|
-
// Version mismatch: stop old daemon and restart (once per session)
|
|
103
|
-
|
|
102
|
+
// Version mismatch: stop old daemon and restart (once per session).
|
|
103
|
+
// Also upgrade legacy daemons that don't report a version.
|
|
104
|
+
if (!upgraded && hello.daemonVersion !== SHIM_VERSION) {
|
|
104
105
|
upgraded = true;
|
|
105
106
|
stderr.write(
|
|
106
|
-
`mesh-shim: Upgrading daemon from v${hello.daemonVersion} to v${SHIM_VERSION}\n`,
|
|
107
|
+
`mesh-shim: Upgrading daemon from v${hello.daemonVersion ?? 'unknown'} to v${SHIM_VERSION}\n`,
|
|
107
108
|
);
|
|
108
109
|
next.close();
|
|
109
110
|
await stopDaemonFn({ pidFile: launcherOptions.pidFile, ipcPath: launcherOptions.ipcPath });
|
package/src/daemon-launcher.ts
CHANGED
|
@@ -7,10 +7,10 @@ import { resolve } from 'node:path';
|
|
|
7
7
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
declare const PKG_VERSION: string;
|
|
11
|
+
export const SHIM_VERSION = PKG_VERSION;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
14
|
|
|
15
15
|
export interface LauncherOptions {
|
|
16
16
|
ipcPath: string;
|
package/src/ipc-client.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
3
2
|
import net from 'node:net';
|
|
4
3
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
4
|
+
declare const PKG_VERSION: string;
|
|
5
|
+
const SHIM_VERSION = PKG_VERSION;
|
|
7
6
|
|
|
8
7
|
type MessageHandler = (message: object) => void;
|
|
9
8
|
type CloseHandler = () => void;
|
package/tests/bridge.test.ts
CHANGED
|
@@ -186,6 +186,8 @@ describe('bridge', () => {
|
|
|
186
186
|
const output = new NdjsonReader(stdout);
|
|
187
187
|
const exit = vi.fn();
|
|
188
188
|
|
|
189
|
+
const { SHIM_VERSION: shimVersion } = await import('../src/daemon-launcher.js');
|
|
190
|
+
|
|
189
191
|
const bridgePromise = createBridge({
|
|
190
192
|
ipcPath,
|
|
191
193
|
pidFile: resolve(dir, 'daemon.pid'),
|
|
@@ -211,7 +213,7 @@ describe('bridge', () => {
|
|
|
211
213
|
server.send({
|
|
212
214
|
jsonrpc: '2.0',
|
|
213
215
|
id: hello.id,
|
|
214
|
-
result: { acknowledged: true, connectionId: 'test-connection' },
|
|
216
|
+
result: { acknowledged: true, connectionId: 'test-connection', daemonVersion: shimVersion },
|
|
215
217
|
});
|
|
216
218
|
const bridge = await bridgePromise;
|
|
217
219
|
|
|
@@ -250,6 +252,8 @@ describe('bridge', () => {
|
|
|
250
252
|
const stderr = new PassThrough();
|
|
251
253
|
const exit = vi.fn();
|
|
252
254
|
|
|
255
|
+
const { SHIM_VERSION: shimVersion } = await import('../src/daemon-launcher.js');
|
|
256
|
+
|
|
253
257
|
const bridgePromise = createBridge({
|
|
254
258
|
ipcPath,
|
|
255
259
|
pidFile: resolve(dir, 'daemon.pid'),
|
|
@@ -267,7 +271,7 @@ describe('bridge', () => {
|
|
|
267
271
|
firstServer.send({
|
|
268
272
|
jsonrpc: '2.0',
|
|
269
273
|
id: firstHello.id,
|
|
270
|
-
result: { acknowledged: true, connectionId: 'first' },
|
|
274
|
+
result: { acknowledged: true, connectionId: 'first', daemonVersion: shimVersion },
|
|
271
275
|
});
|
|
272
276
|
const bridge = await bridgePromise;
|
|
273
277
|
|
|
@@ -287,7 +291,7 @@ describe('bridge', () => {
|
|
|
287
291
|
secondServer.send({
|
|
288
292
|
jsonrpc: '2.0',
|
|
289
293
|
id: secondHello.id,
|
|
290
|
-
result: { acknowledged: true, connectionId: 'second' },
|
|
294
|
+
result: { acknowledged: true, connectionId: 'second', daemonVersion: shimVersion },
|
|
291
295
|
});
|
|
292
296
|
|
|
293
297
|
stdin.write('{"jsonrpc":"2.0","id":"after-reconnect","method":"ping"}\n');
|
|
@@ -440,21 +444,36 @@ describe('bridge', () => {
|
|
|
440
444
|
expect(exit).toHaveBeenCalledWith(0);
|
|
441
445
|
});
|
|
442
446
|
|
|
443
|
-
it('
|
|
447
|
+
it('upgrades legacy daemons that do not report daemonVersion', async () => {
|
|
444
448
|
const dir = await createTestDir();
|
|
445
449
|
const ipcPath = createIpcPath(dir);
|
|
446
450
|
|
|
447
|
-
|
|
448
|
-
await
|
|
451
|
+
let oldServer = new MockIpcServer(ipcPath);
|
|
452
|
+
await oldServer.start();
|
|
449
453
|
|
|
450
|
-
const stopDaemonMock = vi.fn()
|
|
451
|
-
|
|
454
|
+
const stopDaemonMock = vi.fn(async () => {
|
|
455
|
+
await oldServer.stop();
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
let newServer: MockIpcServer | undefined;
|
|
459
|
+
let newServerReady: () => void;
|
|
460
|
+
const newServerPromise = new Promise<void>((r) => { newServerReady = r; });
|
|
461
|
+
const ensureDaemon = vi.fn(async () => {
|
|
462
|
+
if (ensureDaemon.mock.calls.length > 1) {
|
|
463
|
+
newServer = new MockIpcServer(ipcPath);
|
|
464
|
+
await newServer.start();
|
|
465
|
+
newServerReady();
|
|
466
|
+
}
|
|
467
|
+
});
|
|
452
468
|
|
|
453
469
|
const stdin = new PassThrough();
|
|
454
470
|
const stdout = new PassThrough();
|
|
455
471
|
const stderr = new PassThrough();
|
|
472
|
+
const stderrReader = new NdjsonLineReader(stderr);
|
|
456
473
|
const exit = vi.fn();
|
|
457
474
|
|
|
475
|
+
const { SHIM_VERSION: shimVersion } = await import('../src/daemon-launcher.js');
|
|
476
|
+
|
|
458
477
|
const bridgePromise = createBridge({
|
|
459
478
|
ipcPath,
|
|
460
479
|
pidFile: resolve(dir, 'daemon.pid'),
|
|
@@ -466,23 +485,34 @@ describe('bridge', () => {
|
|
|
466
485
|
exit,
|
|
467
486
|
ensureDaemon,
|
|
468
487
|
stopDaemon: stopDaemonMock,
|
|
488
|
+
startupTimeoutMs: 5_000,
|
|
469
489
|
});
|
|
470
490
|
|
|
471
|
-
const
|
|
472
|
-
//
|
|
473
|
-
|
|
491
|
+
const oldHello = await oldServer.nextMessage();
|
|
492
|
+
// Legacy daemon: no daemonVersion field
|
|
493
|
+
oldServer.send({
|
|
474
494
|
jsonrpc: '2.0',
|
|
475
|
-
id:
|
|
495
|
+
id: oldHello.id,
|
|
476
496
|
result: { acknowledged: true, connectionId: 'legacy' },
|
|
477
497
|
});
|
|
478
498
|
|
|
499
|
+
// Should trigger upgrade
|
|
500
|
+
await newServerPromise;
|
|
501
|
+
const newHello = await newServer!.nextMessage();
|
|
502
|
+
newServer!.send({
|
|
503
|
+
jsonrpc: '2.0',
|
|
504
|
+
id: newHello.id,
|
|
505
|
+
result: { acknowledged: true, connectionId: 'new', daemonVersion: shimVersion },
|
|
506
|
+
});
|
|
507
|
+
|
|
479
508
|
const bridge = await bridgePromise;
|
|
480
509
|
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
510
|
+
// Legacy daemons without version SHOULD be upgraded
|
|
511
|
+
expect(stopDaemonMock).toHaveBeenCalledTimes(1);
|
|
512
|
+
expect(ensureDaemon).toHaveBeenCalledTimes(2);
|
|
513
|
+
|
|
514
|
+
const stderrOutput = stderrReader.lines().join('');
|
|
515
|
+
expect(stderrOutput).toContain('Upgrading daemon from vunknown');
|
|
486
516
|
|
|
487
517
|
bridge.close();
|
|
488
518
|
expect(exit).toHaveBeenCalledWith(0);
|
package/tsup.config.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { defineConfig } from 'tsup';
|
|
2
3
|
|
|
4
|
+
const { version } = JSON.parse(readFileSync('./package.json', 'utf8'));
|
|
5
|
+
|
|
3
6
|
export default defineConfig({
|
|
4
7
|
entry: ['src/index.ts'],
|
|
5
8
|
format: ['esm'],
|
|
@@ -7,4 +10,7 @@ export default defineConfig({
|
|
|
7
10
|
clean: true,
|
|
8
11
|
sourcemap: true,
|
|
9
12
|
target: 'es2022',
|
|
13
|
+
define: {
|
|
14
|
+
'PKG_VERSION': JSON.stringify(version),
|
|
15
|
+
},
|
|
10
16
|
});
|
package/vitest.config.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { defineConfig } from 'vitest/config';
|
|
2
3
|
|
|
4
|
+
const { version } = JSON.parse(readFileSync('./package.json', 'utf8'));
|
|
5
|
+
|
|
3
6
|
export default defineConfig({
|
|
7
|
+
define: {
|
|
8
|
+
PKG_VERSION: JSON.stringify(version),
|
|
9
|
+
},
|
|
4
10
|
test: {
|
|
5
11
|
environment: 'node',
|
|
6
12
|
passWithNoTests: false,
|