@miraland-labs/conduit-bridge 0.16.119 → 0.16.120
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/cli.js +2 -0
- package/dist/config.js +5 -0
- package/dist/ops.js +9 -3
- package/dist/service.js +100 -43
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -339,6 +339,7 @@ async function installService() {
|
|
|
339
339
|
const { values } = parseArgs({ args: process.argv.slice(3), options: {
|
|
340
340
|
workspace: { type: "string" }, interval: { type: "string" }, "agent-timeout-minutes": { type: "string" },
|
|
341
341
|
"ensure-checkout": { type: "string" }, "bootstrap-result": { type: "string" },
|
|
342
|
+
service: { type: "string" },
|
|
342
343
|
} });
|
|
343
344
|
if (!values.workspace) {
|
|
344
345
|
throw new Error(`Usage: ${bridgeUsage("install-service", "--workspace", "<repository-path>", "[--ensure-checkout <repository-url>]")} (workspace required; uses online driver lanes)`);
|
|
@@ -363,6 +364,7 @@ async function installService() {
|
|
|
363
364
|
agentTimeoutMinutes: values["agent-timeout-minutes"],
|
|
364
365
|
ensureCheckout: values["ensure-checkout"],
|
|
365
366
|
bootstrapResult,
|
|
367
|
+
...(values.service ? { serviceName: values.service } : {}),
|
|
366
368
|
});
|
|
367
369
|
console.log(`Installed Conduit Bridge v${bridgeVersion()} (protocol ${BRIDGE_PROTOCOL_VERSION}) runner service (${result.platform}): ${result.path}`);
|
|
368
370
|
console.log(`Online lanes: ${onlineDriverIds(config).join(", ")}. Toggle with \`${bridgeUsage("drivers", "online|offline", "…")}\`.`);
|
package/dist/config.js
CHANGED
|
@@ -18,6 +18,11 @@ export function clampLeaseCapacity(value) {
|
|
|
18
18
|
const directory = process.env.CONDUIT_BRIDGE_CONFIG_DIR?.trim()
|
|
19
19
|
? resolve(process.env.CONDUIT_BRIDGE_CONFIG_DIR)
|
|
20
20
|
: join(homedir(), ".config", "conduit");
|
|
21
|
+
/** The installation this process belongs to. Service state follows it, or a second installation on
|
|
22
|
+
* one computer writes its service record over the first one's. */
|
|
23
|
+
export function bridgeConfigDirectory() {
|
|
24
|
+
return directory;
|
|
25
|
+
}
|
|
21
26
|
const path = join(directory, "config.json");
|
|
22
27
|
/** Runtime attempt/session state — separate so drivers CLI cannot clobber live claims. */
|
|
23
28
|
const runtimePath = join(directory, "runtime.json");
|
package/dist/ops.js
CHANGED
|
@@ -15,7 +15,7 @@ import { ensureCheckout } from "./checkout.js";
|
|
|
15
15
|
import { detectInstalledClients, probeAgentHealth } from "./detect.js";
|
|
16
16
|
import { driverIdsFromDetectedLabels, formatLaneTiers, laneDispatchBlock, laneStatuses } from "./drivers.js";
|
|
17
17
|
import { BRIDGE_PROTOCOL_VERSION, describePreflightIssue, runBridgePreflight } from "./preflight.js";
|
|
18
|
-
import { applyRunnerToolPath, readActiveRunnerServiceWorkspace, readStoredRunnerServiceOptions, runnerServiceWorkspaceWarnings } from "./service.js";
|
|
18
|
+
import { applyRunnerToolPath, readActiveRunnerServiceWorkspace, readStoredRunnerServiceOptions, readStoredServiceName, runnerServiceWorkspaceWarnings } from "./service.js";
|
|
19
19
|
import { bridgeVersion } from "./version.js";
|
|
20
20
|
import { bootstrapManagedWorkspace } from "./workspace-bootstrap.js";
|
|
21
21
|
import { managedWorkspacePath } from "./managed-workspace-path.js";
|
|
@@ -351,7 +351,7 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
351
351
|
const config = await (deps.loadBridgeConfig ?? loadConfig)();
|
|
352
352
|
const workspace = resolve(expandOpsValue(workspaceRaw));
|
|
353
353
|
if (!values["skip-service-alignment"]) {
|
|
354
|
-
for (const warning of runnerServiceWorkspaceWarnings(workspace)) {
|
|
354
|
+
for (const warning of runnerServiceWorkspaceWarnings(workspace, homedir(), await readStoredServiceName())) {
|
|
355
355
|
console.warn(`WARNING: ${warning}`);
|
|
356
356
|
}
|
|
357
357
|
}
|
|
@@ -379,7 +379,7 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
379
379
|
console.log("");
|
|
380
380
|
}
|
|
381
381
|
if (report.ready) {
|
|
382
|
-
const serviceWarnings = values["skip-service-alignment"] ? [] : runnerServiceWorkspaceWarnings(workspace);
|
|
382
|
+
const serviceWarnings = values["skip-service-alignment"] ? [] : runnerServiceWorkspaceWarnings(workspace, homedir(), await readStoredServiceName());
|
|
383
383
|
if (serviceWarnings.length > 0) {
|
|
384
384
|
for (const warning of serviceWarnings)
|
|
385
385
|
console.warn(`WARNING: ${warning}`);
|
|
@@ -408,16 +408,20 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
408
408
|
// seed ops.env once; later project changes must not mutate it.
|
|
409
409
|
let installEnv = env;
|
|
410
410
|
let installArgv = argv;
|
|
411
|
+
let installServiceName = null;
|
|
411
412
|
if (verb === "install") {
|
|
412
413
|
const { values, positionals } = parseArgs({
|
|
413
414
|
args: argv,
|
|
414
415
|
options: {
|
|
415
416
|
workspace: { type: "string" },
|
|
416
417
|
repo: { type: "string" },
|
|
418
|
+
// The supervisor name this installation's service carries; one computer may hold two.
|
|
419
|
+
service: { type: "string" },
|
|
417
420
|
},
|
|
418
421
|
allowPositionals: true,
|
|
419
422
|
});
|
|
420
423
|
installArgv = positionals;
|
|
424
|
+
installServiceName = values.service?.trim() || null;
|
|
421
425
|
const declaredWorkspace = values.workspace?.trim();
|
|
422
426
|
const declaredRepo = values.repo?.trim();
|
|
423
427
|
if (declaredWorkspace || declaredRepo) {
|
|
@@ -670,6 +674,8 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
670
674
|
]);
|
|
671
675
|
}
|
|
672
676
|
const installArgs = ["install-service", "--workspace", workspace];
|
|
677
|
+
if (installServiceName)
|
|
678
|
+
installArgs.push("--service", installServiceName);
|
|
673
679
|
if (installEnv.CONDUIT_REPO)
|
|
674
680
|
installArgs.push("--ensure-checkout", installEnv.CONDUIT_REPO);
|
|
675
681
|
if (bootstrapResult)
|
package/dist/service.js
CHANGED
|
@@ -3,15 +3,38 @@ import { homedir, platform } from "node:os";
|
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { existsSync, realpathSync } from "node:fs";
|
|
5
5
|
import { spawn, spawnSync } from "node:child_process";
|
|
6
|
-
|
|
6
|
+
import { bridgeConfigDirectory } from "./config.js";
|
|
7
|
+
/** The name an installation's service carries when its operator named none. */
|
|
8
|
+
export const DEFAULT_SERVICE_NAME = "conduit-runner";
|
|
9
|
+
/** Backwards-compatible label for the default installation (macOS). */
|
|
10
|
+
export const SERVICE_LABEL = `io.miraland.${DEFAULT_SERVICE_NAME}`;
|
|
11
|
+
/**
|
|
12
|
+
* One computer may hold more than one installation — this host runs the Conduit lane and the forge
|
|
13
|
+
* lane — and a service named after the product cannot say which work it serves. The name is a
|
|
14
|
+
* property of the installation: given once at install time, stored beside its program arguments, and
|
|
15
|
+
* read back by every path that looks the service up.
|
|
16
|
+
*/
|
|
17
|
+
const SERVICE_NAME_PATTERN = /^[a-z][a-z0-9-]{1,30}$/;
|
|
18
|
+
export function assertServiceName(name) {
|
|
19
|
+
if (!SERVICE_NAME_PATTERN.test(name)) {
|
|
20
|
+
throw new Error(`Service name must match ${SERVICE_NAME_PATTERN} (got "${name}") — no .service suffix, no spaces.`);
|
|
21
|
+
}
|
|
22
|
+
return name;
|
|
23
|
+
}
|
|
24
|
+
export function serviceLabelFor(name) {
|
|
25
|
+
return `io.miraland.${name}`;
|
|
26
|
+
}
|
|
27
|
+
export function windowsTaskNameFor(name) {
|
|
28
|
+
return name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
29
|
+
}
|
|
7
30
|
function configDir() {
|
|
8
|
-
return
|
|
31
|
+
return bridgeConfigDirectory();
|
|
9
32
|
}
|
|
10
33
|
function serviceStatePath() {
|
|
11
34
|
return join(configDir(), "service.json");
|
|
12
35
|
}
|
|
13
|
-
function logPath() {
|
|
14
|
-
return join(homedir(), "Library", "Logs",
|
|
36
|
+
function logPath(name = DEFAULT_SERVICE_NAME) {
|
|
37
|
+
return join(homedir(), "Library", "Logs", `${name}.log`);
|
|
15
38
|
}
|
|
16
39
|
function linuxLogDir() {
|
|
17
40
|
return join(homedir(), ".local", "state", "conduit");
|
|
@@ -130,14 +153,14 @@ function escapeSystemdExecArgument(value) {
|
|
|
130
153
|
// Exec*= additionally expands $ environment references; $$ passes one literal dollar.
|
|
131
154
|
return escapeSystemdUnitValue(value.replaceAll("$", () => "$$"));
|
|
132
155
|
}
|
|
133
|
-
function launchAgentsPath() {
|
|
134
|
-
return join(homedir(), "Library", "LaunchAgents", `${
|
|
156
|
+
function launchAgentsPath(name = DEFAULT_SERVICE_NAME) {
|
|
157
|
+
return join(homedir(), "Library", "LaunchAgents", `${serviceLabelFor(name)}.plist`);
|
|
135
158
|
}
|
|
136
|
-
export function systemdUnitPath(home = homedir()) {
|
|
137
|
-
return join(home, ".config", "systemd", "user",
|
|
159
|
+
export function systemdUnitPath(home = homedir(), name = DEFAULT_SERVICE_NAME) {
|
|
160
|
+
return join(home, ".config", "systemd", "user", `${name}.service`);
|
|
138
161
|
}
|
|
139
|
-
export function systemdDropInDir(home = homedir()) {
|
|
140
|
-
return join(home, ".config", "systemd", "user",
|
|
162
|
+
export function systemdDropInDir(home = homedir(), name = DEFAULT_SERVICE_NAME) {
|
|
163
|
+
return join(home, ".config", "systemd", "user", `${name}.service.d`);
|
|
141
164
|
}
|
|
142
165
|
/** Parse `--workspace` from a runner argv list or systemd ExecStart line. */
|
|
143
166
|
export function parseRunnerWorkspaceFromProgramArguments(args) {
|
|
@@ -154,9 +177,9 @@ export function parseSystemdDropInPaths(value) {
|
|
|
154
177
|
return value.trim().split(/\s+/).filter((path) => path.endsWith(".conf"));
|
|
155
178
|
}
|
|
156
179
|
/** Any effective drop-in can change workspace, identity, fuel, PATH, or restart behaviour. */
|
|
157
|
-
export async function systemdDropInConflicts(home = homedir()) {
|
|
180
|
+
export async function systemdDropInConflicts(home = homedir(), name = DEFAULT_SERVICE_NAME) {
|
|
158
181
|
const conflicts = new Set();
|
|
159
|
-
const dir = systemdDropInDir(home);
|
|
182
|
+
const dir = systemdDropInDir(home, name);
|
|
160
183
|
if (existsSync(dir)) {
|
|
161
184
|
for (const name of await readdir(dir)) {
|
|
162
185
|
if (name.endsWith(".conf"))
|
|
@@ -164,7 +187,7 @@ export async function systemdDropInConflicts(home = homedir()) {
|
|
|
164
187
|
}
|
|
165
188
|
}
|
|
166
189
|
if (platform() === "linux") {
|
|
167
|
-
const effective = spawnSync("systemctl", ["--user", "show",
|
|
190
|
+
const effective = spawnSync("systemctl", ["--user", "show", `${name}.service`, "--property=DropInPaths", "--value"], { encoding: "utf8" });
|
|
168
191
|
if (effective.status === 0) {
|
|
169
192
|
for (const path of parseSystemdDropInPaths(effective.stdout))
|
|
170
193
|
conflicts.add(basename(path));
|
|
@@ -172,6 +195,16 @@ export async function systemdDropInConflicts(home = homedir()) {
|
|
|
172
195
|
}
|
|
173
196
|
return [...conflicts].sort();
|
|
174
197
|
}
|
|
198
|
+
/** The name this installation's service carries, from its own record. */
|
|
199
|
+
export async function readStoredServiceName() {
|
|
200
|
+
try {
|
|
201
|
+
const stored = JSON.parse(await readFile(serviceStatePath(), "utf8"));
|
|
202
|
+
return stored.serviceName && SERVICE_NAME_PATTERN.test(stored.serviceName) ? stored.serviceName : DEFAULT_SERVICE_NAME;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return DEFAULT_SERVICE_NAME;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
175
208
|
export async function readStoredRunnerServiceOptions() {
|
|
176
209
|
try {
|
|
177
210
|
const stored = JSON.parse(await readFile(serviceStatePath(), "utf8"));
|
|
@@ -191,13 +224,13 @@ export async function readStoredRunnerServiceWorkspace() {
|
|
|
191
224
|
}
|
|
192
225
|
/** Installed supervisor command is the active workspace authority; service.json is fallback. */
|
|
193
226
|
export async function readActiveRunnerServiceWorkspace(home = homedir()) {
|
|
194
|
-
return readEffectiveLinuxRunnerWorkspace(home) ?? await readStoredRunnerServiceWorkspace();
|
|
227
|
+
return readEffectiveLinuxRunnerWorkspace(home, await readStoredServiceName()) ?? await readStoredRunnerServiceWorkspace();
|
|
195
228
|
}
|
|
196
229
|
/** Effective workspace from the installed supervisor (systemd user unit on Linux). */
|
|
197
|
-
export function readEffectiveLinuxRunnerWorkspace(home = homedir()) {
|
|
230
|
+
export function readEffectiveLinuxRunnerWorkspace(home = homedir(), name = DEFAULT_SERVICE_NAME) {
|
|
198
231
|
if (platform() !== "linux")
|
|
199
232
|
return null;
|
|
200
|
-
const result = spawnSync("systemctl", ["--user", "cat",
|
|
233
|
+
const result = spawnSync("systemctl", ["--user", "cat", `${name}.service`], { encoding: "utf8" });
|
|
201
234
|
if (result.status !== 0)
|
|
202
235
|
return null;
|
|
203
236
|
let execStart = null;
|
|
@@ -207,13 +240,13 @@ export function readEffectiveLinuxRunnerWorkspace(home = homedir()) {
|
|
|
207
240
|
}
|
|
208
241
|
return execStart ? parseRunnerWorkspaceFromExecStart(execStart) : null;
|
|
209
242
|
}
|
|
210
|
-
export function runnerServiceWorkspaceWarnings(declaredWorkspace, home = homedir()) {
|
|
243
|
+
export function runnerServiceWorkspaceWarnings(declaredWorkspace, home = homedir(), name = DEFAULT_SERVICE_NAME) {
|
|
211
244
|
const declared = resolve(declaredWorkspace);
|
|
212
245
|
const warnings = [];
|
|
213
|
-
const stored = platform() === "linux" ? readEffectiveLinuxRunnerWorkspace(home) : null;
|
|
246
|
+
const stored = platform() === "linux" ? readEffectiveLinuxRunnerWorkspace(home, name) : null;
|
|
214
247
|
if (stored && resolve(stored) !== declared) {
|
|
215
248
|
warnings.push(`Background runner service uses workspace ${stored}, but ops.env declares ${declaredWorkspace}. ` +
|
|
216
|
-
|
|
249
|
+
`Run ops install to realign the service, or remove stale systemd drop-ins under ${name}.service.d/.`);
|
|
217
250
|
}
|
|
218
251
|
return warnings;
|
|
219
252
|
}
|
|
@@ -287,10 +320,10 @@ export function runningInsideRunnerContainer(env = process.env) {
|
|
|
287
320
|
* explicitly; the resident runner must return to its caller and exit normally, after which
|
|
288
321
|
* Restart=always starts the rewritten unit.
|
|
289
322
|
*/
|
|
290
|
-
export function systemdActivationCommands(insideRunner) {
|
|
323
|
+
export function systemdActivationCommands(insideRunner, name = DEFAULT_SERVICE_NAME) {
|
|
291
324
|
return [
|
|
292
|
-
["--user", "enable",
|
|
293
|
-
...(!insideRunner ? [["--user", "restart",
|
|
325
|
+
["--user", "enable", `${name}.service`],
|
|
326
|
+
...(!insideRunner ? [["--user", "restart", `${name}.service`]] : []),
|
|
294
327
|
];
|
|
295
328
|
}
|
|
296
329
|
/**
|
|
@@ -315,6 +348,18 @@ export function reloadLaunchdRunnerSync(domain, plistPath) {
|
|
|
315
348
|
spawnSync("launchctl", ["kickstart", "-k", label], { encoding: "utf8" });
|
|
316
349
|
return launchdServiceLoaded(domain);
|
|
317
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* A unit already written by another installation must not be taken over: that is how one lane's
|
|
353
|
+
* service silently started serving another lane's workspace. The unit records the installation it
|
|
354
|
+
* belongs to in its WorkingDirectory.
|
|
355
|
+
*/
|
|
356
|
+
export function unitBelongsToAnotherInstallation(unitText, installation) {
|
|
357
|
+
const line = unitText.split(/\r?\n/).find((entry) => entry.startsWith("WorkingDirectory="));
|
|
358
|
+
if (!line)
|
|
359
|
+
return null;
|
|
360
|
+
const owner = line.slice("WorkingDirectory=".length).trim().replace(/^"|"$/g, "");
|
|
361
|
+
return owner && resolve(owner) !== resolve(installation) ? owner : null;
|
|
362
|
+
}
|
|
318
363
|
export async function installRunnerService(options = {}) {
|
|
319
364
|
if (runningInsideRunnerContainer()) {
|
|
320
365
|
return { path: "container entrypoint", platform: "container" };
|
|
@@ -323,30 +368,34 @@ export async function installRunnerService(options = {}) {
|
|
|
323
368
|
if (host !== "darwin" && host !== "linux" && host !== "win32") {
|
|
324
369
|
throw new Error(`install-service is supported on macOS, Linux, and Windows only (got ${host})`);
|
|
325
370
|
}
|
|
371
|
+
const serviceName = assertServiceName(options.serviceName ?? await readStoredServiceName());
|
|
372
|
+
const stored = { ...options, serviceName };
|
|
326
373
|
const programArguments = runnerProgramArguments(options);
|
|
327
374
|
if (host === "win32") {
|
|
328
375
|
const tr = windowsTaskCommandLine(programArguments);
|
|
329
|
-
|
|
376
|
+
const taskName = windowsTaskNameFor(serviceName);
|
|
377
|
+
run("schtasks", ["/Create", "/TN", taskName, "/TR", tr, "/SC", "ONLOGON", "/RL", "LIMITED", "/F"]);
|
|
330
378
|
try {
|
|
331
|
-
run("schtasks", ["/Run", "/TN",
|
|
379
|
+
run("schtasks", ["/Run", "/TN", taskName]);
|
|
332
380
|
}
|
|
333
381
|
catch {
|
|
334
382
|
// Task is registered for next logon. /Run needs an interactive session; enroll already succeeded.
|
|
335
383
|
}
|
|
336
384
|
await mkdir(configDir(), { recursive: true });
|
|
337
|
-
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "win32", programArguments, options, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
338
|
-
return { path:
|
|
385
|
+
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "win32", serviceName, programArguments, options: stored, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
386
|
+
return { path: taskName, platform: "win32" };
|
|
339
387
|
}
|
|
340
388
|
if (host === "darwin") {
|
|
341
|
-
const plistPath = launchAgentsPath();
|
|
389
|
+
const plistPath = launchAgentsPath(serviceName);
|
|
390
|
+
const log = logPath(serviceName);
|
|
342
391
|
await mkdir(dirname(plistPath), { recursive: true });
|
|
343
|
-
await mkdir(dirname(
|
|
344
|
-
await writeFile(plistPath, launchdPlist(programArguments,
|
|
392
|
+
await mkdir(dirname(log), { recursive: true });
|
|
393
|
+
await writeFile(plistPath, launchdPlist(programArguments, log), { mode: 0o644 });
|
|
345
394
|
const domain = `gui/${process.getuid?.() ?? 501}`;
|
|
346
395
|
let loaded = false;
|
|
347
396
|
if (runningInsideRunnerLaunchAgent()) {
|
|
348
397
|
// Detached helper must survive bootout of this process.
|
|
349
|
-
spawn("/bin/sh", ["-c", launchdSwapCommand(domain, plistPath,
|
|
398
|
+
spawn("/bin/sh", ["-c", launchdSwapCommand(domain, plistPath, log)], { detached: true, stdio: "ignore" }).unref();
|
|
350
399
|
for (let attempt = 0; attempt < 20 && !loaded; attempt++) {
|
|
351
400
|
await sleepMs(attempt === 0 ? 2000 : 500);
|
|
352
401
|
loaded = launchdServiceLoaded(domain);
|
|
@@ -363,24 +412,30 @@ export async function installRunnerService(options = {}) {
|
|
|
363
412
|
}
|
|
364
413
|
}
|
|
365
414
|
if (!loaded)
|
|
366
|
-
throw new Error(`launchd did not load ${
|
|
367
|
-
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "darwin", programArguments, options, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
415
|
+
throw new Error(`launchd did not load ${serviceLabelFor(serviceName)}; see ${log}`);
|
|
416
|
+
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "darwin", serviceName, programArguments, options: stored, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
368
417
|
return { path: plistPath, platform: "darwin" };
|
|
369
418
|
}
|
|
370
|
-
const unitPath = systemdUnitPath();
|
|
371
|
-
const dropIns = await systemdDropInConflicts();
|
|
419
|
+
const unitPath = systemdUnitPath(homedir(), serviceName);
|
|
420
|
+
const dropIns = await systemdDropInConflicts(homedir(), serviceName);
|
|
372
421
|
if (dropIns.length) {
|
|
373
|
-
throw new Error(`Refusing to install with
|
|
422
|
+
throw new Error(`Refusing to install with ${serviceName}.service drop-in(s): ${dropIns.join(", ")}. ` +
|
|
374
423
|
"Review and migrate or remove them, then run ops install again.");
|
|
375
424
|
}
|
|
425
|
+
const existing = existsSync(unitPath) ? await readFile(unitPath, "utf8") : "";
|
|
426
|
+
const otherOwner = existing ? unitBelongsToAnotherInstallation(existing, configDir()) : null;
|
|
427
|
+
if (otherOwner) {
|
|
428
|
+
throw new Error(`${serviceName}.service already belongs to the installation at ${otherOwner}. ` +
|
|
429
|
+
`Install this one under another name: ops install --service <name>.`);
|
|
430
|
+
}
|
|
376
431
|
await mkdir(dirname(unitPath), { recursive: true });
|
|
377
432
|
await mkdir(linuxLogDir(), { recursive: true });
|
|
378
433
|
await writeFile(unitPath, systemdUserUnit(programArguments, configDir()), { mode: 0o644 });
|
|
379
434
|
run("systemctl", ["--user", "daemon-reload"]);
|
|
380
|
-
for (const args of systemdActivationCommands(runningInsideRunnerSystemd())) {
|
|
435
|
+
for (const args of systemdActivationCommands(runningInsideRunnerSystemd(), serviceName)) {
|
|
381
436
|
run("systemctl", args);
|
|
382
437
|
}
|
|
383
|
-
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "linux", programArguments, options, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
438
|
+
await writeFile(serviceStatePath(), `${JSON.stringify({ platform: "linux", serviceName, programArguments, options: stored, installed_at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
384
439
|
return { path: unitPath, platform: "linux" };
|
|
385
440
|
}
|
|
386
441
|
export async function uninstallRunnerService() {
|
|
@@ -395,21 +450,23 @@ export async function uninstallRunnerService() {
|
|
|
395
450
|
catch {
|
|
396
451
|
stored = null;
|
|
397
452
|
}
|
|
453
|
+
const serviceName = await readStoredServiceName();
|
|
398
454
|
if (host === "win32") {
|
|
399
|
-
|
|
455
|
+
const taskName = windowsTaskNameFor(serviceName);
|
|
456
|
+
spawnSync("schtasks", ["/Delete", "/TN", taskName, "/F"], { encoding: "utf8" });
|
|
400
457
|
await unlink(serviceStatePath()).catch(() => undefined);
|
|
401
|
-
return { path:
|
|
458
|
+
return { path: taskName, platform: "win32" };
|
|
402
459
|
}
|
|
403
460
|
if (host === "darwin") {
|
|
404
|
-
const plistPath = launchAgentsPath();
|
|
405
|
-
spawnSync("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${
|
|
461
|
+
const plistPath = launchAgentsPath(serviceName);
|
|
462
|
+
spawnSync("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${serviceLabelFor(serviceName)}`], { encoding: "utf8" });
|
|
406
463
|
spawnSync("launchctl", ["unload", "-w", plistPath], { encoding: "utf8" });
|
|
407
464
|
await unlink(plistPath).catch(() => undefined);
|
|
408
465
|
await unlink(serviceStatePath()).catch(() => undefined);
|
|
409
466
|
return { path: plistPath, platform: stored?.platform ?? "darwin" };
|
|
410
467
|
}
|
|
411
|
-
const unitPath = systemdUnitPath();
|
|
412
|
-
spawnSync("systemctl", ["--user", "disable", "--now",
|
|
468
|
+
const unitPath = systemdUnitPath(homedir(), serviceName);
|
|
469
|
+
spawnSync("systemctl", ["--user", "disable", "--now", `${serviceName}.service`], { encoding: "utf8" });
|
|
413
470
|
await unlink(unitPath).catch(() => undefined);
|
|
414
471
|
spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
|
|
415
472
|
await unlink(serviceStatePath()).catch(() => undefined);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.120",
|
|
4
4
|
"description": "Conduit Bridge CLI \u2014 join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity / Grok Build agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|