@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 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
- export const SERVICE_LABEL = "io.miraland.conduit-runner";
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 join(homedir(), ".config", "conduit");
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", "conduit-runner.log");
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", `${SERVICE_LABEL}.plist`);
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", "conduit-runner.service");
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", "conduit-runner.service.d");
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", "conduit-runner.service", "--property=DropInPaths", "--value"], { encoding: "utf8" });
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", "conduit-runner.service"], { encoding: "utf8" });
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
- "Run ops install to realign the service, or remove stale systemd drop-ins under conduit-runner.service.d/.");
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", "conduit-runner.service"],
293
- ...(!insideRunner ? [["--user", "restart", "conduit-runner.service"]] : []),
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
- run("schtasks", ["/Create", "/TN", WINDOWS_TASK_NAME, "/TR", tr, "/SC", "ONLOGON", "/RL", "LIMITED", "/F"]);
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", WINDOWS_TASK_NAME]);
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: WINDOWS_TASK_NAME, platform: "win32" };
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(logPath()), { recursive: true });
344
- await writeFile(plistPath, launchdPlist(programArguments, logPath()), { mode: 0o644 });
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, logPath())], { detached: true, stdio: "ignore" }).unref();
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 ${SERVICE_LABEL}; see ${logPath()}`);
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 conduit-runner.service drop-in(s): ${dropIns.join(", ")}. ` +
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
- spawnSync("schtasks", ["/Delete", "/TN", WINDOWS_TASK_NAME, "/F"], { encoding: "utf8" });
455
+ const taskName = windowsTaskNameFor(serviceName);
456
+ spawnSync("schtasks", ["/Delete", "/TN", taskName, "/F"], { encoding: "utf8" });
400
457
  await unlink(serviceStatePath()).catch(() => undefined);
401
- return { path: WINDOWS_TASK_NAME, platform: "win32" };
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}/${SERVICE_LABEL}`], { encoding: "utf8" });
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", "conduit-runner.service"], { encoding: "utf8" });
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.119",
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": {