@miraland-labs/conduit-bridge 0.11.11 → 0.11.12
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/service.js +29 -6
- package/package.json +1 -1
package/dist/service.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { homedir, platform } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
6
6
|
export const SERVICE_LABEL = "io.miraland.conduit-runner";
|
|
7
7
|
function configDir() {
|
|
8
8
|
return join(homedir(), ".config", "conduit");
|
|
@@ -118,6 +118,22 @@ function run(command, args) {
|
|
|
118
118
|
throw new Error(`${command} ${args.join(" ")} failed: ${detail}`);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
function shellQuote(value) {
|
|
122
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `launchctl bootout` of a loaded service kills that service's whole process group — including
|
|
126
|
+
* this process when install-service runs from inside the runner it replaces (Connect on-shift
|
|
127
|
+
* auto-apply). Observed in the field (2026-08-05): the runner died at bootout, the bootstrap on
|
|
128
|
+
* the next line never ran, and the machine stayed Binding incomplete with the service unloaded.
|
|
129
|
+
* The swap therefore runs in a detached helper that survives the bootout; failures append to the
|
|
130
|
+
* runner log.
|
|
131
|
+
*/
|
|
132
|
+
export function launchdSwapCommand(domain, plistPath, log) {
|
|
133
|
+
const plist = shellQuote(plistPath);
|
|
134
|
+
const logQ = shellQuote(log);
|
|
135
|
+
return `sleep 1; launchctl bootout ${domain}/${SERVICE_LABEL} 2>>${logQ}; launchctl bootstrap ${domain} ${plist} 2>>${logQ} || launchctl load -w ${plist} 2>>${logQ}`;
|
|
136
|
+
}
|
|
121
137
|
export async function installRunnerService(options = {}) {
|
|
122
138
|
const host = platform();
|
|
123
139
|
if (host !== "darwin" && host !== "linux") {
|
|
@@ -129,12 +145,19 @@ export async function installRunnerService(options = {}) {
|
|
|
129
145
|
await mkdir(dirname(plistPath), { recursive: true });
|
|
130
146
|
await mkdir(dirname(logPath()), { recursive: true });
|
|
131
147
|
await writeFile(plistPath, launchdPlist(programArguments, logPath()), { mode: 0o644 });
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
// Detached helper (bootout → bootstrap → load fallback): see launchdSwapCommand for why the
|
|
149
|
+
// swap must survive this process dying at bootout.
|
|
150
|
+
const domain = `gui/${process.getuid?.() ?? 501}`;
|
|
151
|
+
spawn("/bin/sh", ["-c", launchdSwapCommand(domain, plistPath, logPath())], { detached: true, stdio: "ignore" }).unref();
|
|
152
|
+
// Confirm the reloaded service when this process survives to see it (an interactive install).
|
|
153
|
+
// A self-replacing runner dies at the helper's bootout and the helper still finishes the swap.
|
|
154
|
+
let loaded = false;
|
|
155
|
+
for (let attempt = 0; attempt < 16 && !loaded; attempt++) {
|
|
156
|
+
await new Promise((resolveSleep) => setTimeout(resolveSleep, attempt === 0 ? 2000 : 500));
|
|
157
|
+
loaded = spawnSync("launchctl", ["print", `${domain}/${SERVICE_LABEL}`], { encoding: "utf8" }).status === 0;
|
|
137
158
|
}
|
|
159
|
+
if (!loaded)
|
|
160
|
+
throw new Error(`launchd did not load ${SERVICE_LABEL}; see ${logPath()}`);
|
|
138
161
|
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "darwin", programArguments, options, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
139
162
|
return { path: plistPath, platform: "darwin" };
|
|
140
163
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.12",
|
|
4
4
|
"description": "Conduit Bridge CLI — join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|