@buda-ai/connector 0.1.29 → 0.1.31

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.
@@ -47,7 +47,7 @@ Capture what matters. Decisions, context, things to remember. Skip the secrets u
47
47
 
48
48
  - **Memory is limited** — if you want to remember something, you MUST write it to a file using your tools (like `bash` or `write`).
49
49
  - "Mental notes" do not survive session restarts or context limits. Only files do.
50
- - When the user gives you a **new rule, a new preference, changes your identity/role, or tells you to "remember this"** → You **MUST IMMEDIATELY** use your tools to update `SOUL.md`, `AGENTS.md`, `MEMORY.md` or `memory/YYYY-MM-DD.md`.
50
+ - When the user gives you a **new rule, a new preference, changes your identity/role, or tells you to "remember this"** → You **MUST IMMEDIATELY** use your tools to update `SOUL.md`, `AGENTS.md`, `USER.md` or `memory/YYYY-MM-DD.md`.
51
51
  - DO NOT just reply "Okay, I will remember this." You must actually execute a file write operation to make it permanent.
52
52
  - When you learn a lesson → update `AGENTS.md`, `TOOLS.md`, or the relevant skill.
53
53
  - When you make a mistake → document it so future-you doesn't repeat it.
package/dist/cli.js CHANGED
@@ -25,8 +25,8 @@ async function acquireExclusiveFileLock(targetPath, options = {}) {
25
25
  } catch (error) {
26
26
  if (!isAlreadyExists(error)) throw error;
27
27
  if (await shouldRecoverLock(lockPath, staleMs)) {
28
- const recovered = await unlink(lockPath).then(() => true).catch(() => false);
29
- if (recovered) continue;
28
+ await unlink(lockPath).catch(() => void 0);
29
+ continue;
30
30
  }
31
31
  if (Date.now() >= deadline) {
32
32
  throw new Error(`Timed out waiting for exclusive access to ${targetPath}`);
@@ -35,23 +35,17 @@ async function acquireExclusiveFileLock(targetPath, options = {}) {
35
35
  continue;
36
36
  }
37
37
  const ownerToken = randomUUID();
38
- try {
39
- await handle.writeFile(
40
- `${JSON.stringify({ pid: process.pid, ownerToken, createdAt: Date.now() })}
38
+ await handle.writeFile(
39
+ `${JSON.stringify({ pid: process.pid, ownerToken, createdAt: Date.now() })}
41
40
  `
42
- );
43
- await handle.sync();
44
- } catch (error) {
45
- await handle.close().catch(() => void 0);
46
- await unlink(lockPath).catch(() => void 0);
47
- throw error;
48
- }
49
- await handle.close();
41
+ );
42
+ await handle.sync();
50
43
  let released = false;
51
44
  return {
52
45
  release: async () => {
53
46
  if (released) return;
54
47
  released = true;
48
+ await handle.close();
55
49
  const currentOwner = await readFile(lockPath, "utf8").then((raw) => JSON.parse(raw).ownerToken).catch(() => void 0);
56
50
  if (currentOwner === ownerToken) await unlink(lockPath).catch(() => void 0);
57
51
  }
@@ -523,7 +517,7 @@ import path2 from "path";
523
517
  // package.json
524
518
  var package_default = {
525
519
  name: "@buda-ai/connector",
526
- version: "0.1.29",
520
+ version: "0.1.31",
527
521
  private: false,
528
522
  type: "module",
529
523
  publishConfig: {
@@ -541,7 +535,6 @@ var package_default = {
541
535
  dev: "tsx src/cli.ts dev",
542
536
  daemon: "tsx src/cli.ts daemon",
543
537
  new: "tsx src/cli.ts new",
544
- prepack: "tsup --silent",
545
538
  start: "node dist/cli.js daemon",
546
539
  build: "tsup",
547
540
  test: "vitest run",
@@ -550,7 +543,7 @@ var package_default = {
550
543
  "lint:err": "biome format . --write && biome check . --write --diagnostic-level=error"
551
544
  },
552
545
  dependencies: {
553
- "@bunny-agent/daemon": "0.9.57",
546
+ "@bunny-agent/daemon": "0.9.51",
554
547
  "@clack/prompts": "^1.0.0",
555
548
  "playwright-core": "^1.52.0",
556
549
  ws: "^8.21.0"
@@ -2215,13 +2208,6 @@ var ConnectorDeletedError = class extends Error {
2215
2208
  this.name = "ConnectorDeletedError";
2216
2209
  }
2217
2210
  };
2218
- var ConnectorUnauthorizedError = class extends Error {
2219
- constructor(message) {
2220
- super(message);
2221
- this.name = "ConnectorUnauthorizedError";
2222
- }
2223
- };
2224
- var UNREGISTER_TIMEOUT_MS = 3e3;
2225
2211
  async function resolveConnectorScope(deviceId, launchConfig) {
2226
2212
  const serverUrl = normalizeServerOrigin(getServerUrl(launchConfig));
2227
2213
  const headers = { "Content-Type": "application/json" };
@@ -2308,15 +2294,14 @@ function buildAuthHeaders(config, launchConfig) {
2308
2294
  if (config.deviceToken) headers["X-Buda-Connector-Device-Token"] = config.deviceToken;
2309
2295
  return headers;
2310
2296
  }
2311
- async function postJson(serverUrl, pathname, body, config, launchConfig, options = {}) {
2297
+ async function postJson(serverUrl, pathname, body, config, launchConfig) {
2312
2298
  const response = await fetch(`${serverUrl}${pathname}`, {
2313
2299
  method: "POST",
2314
2300
  headers: {
2315
2301
  "Content-Type": "application/json",
2316
2302
  ...buildAuthHeaders(config, launchConfig)
2317
2303
  },
2318
- body: JSON.stringify(body),
2319
- ...options.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
2304
+ body: JSON.stringify(body)
2320
2305
  });
2321
2306
  if (!response.ok) {
2322
2307
  const text2 = await response.text().catch(() => "");
@@ -2336,9 +2321,6 @@ async function postJson(serverUrl, pathname, body, config, launchConfig, options
2336
2321
  if (errorCode === "connector_deleted") {
2337
2322
  throw new ConnectorDeletedError(errorMessage3 || "Connector no longer exists");
2338
2323
  }
2339
- if (response.status === 401) {
2340
- throw new ConnectorUnauthorizedError(errorMessage3 || "Connector authentication failed");
2341
- }
2342
2324
  throw new Error(`${pathname} failed: ${response.status} ${errorMessage3}`);
2343
2325
  }
2344
2326
  return await response.json();
@@ -2414,8 +2396,7 @@ async function unregisterConnector(config, launchConfig) {
2414
2396
  connectorId: config.connectorId
2415
2397
  },
2416
2398
  config,
2417
- launchConfig,
2418
- { timeoutMs: UNREGISTER_TIMEOUT_MS }
2399
+ launchConfig
2419
2400
  ).catch((error) => {
2420
2401
  void writeLog("warn", `unregister failed: ${error instanceof Error ? error.message : error}`);
2421
2402
  });
@@ -2424,58 +2405,32 @@ async function unregisterConnector(config, launchConfig) {
2424
2405
  // src/runtime-install.ts
2425
2406
  import { execFile } from "child_process";
2426
2407
  import { randomUUID as randomUUID4 } from "crypto";
2427
- import { access, cp, mkdir as mkdir5, readFile as readFile5, rename as rename4, rm, writeFile as writeFile3 } from "fs/promises";
2408
+ import { access, cp, mkdir as mkdir5, readdir as readdir4, readFile as readFile5, rename as rename4, rm, writeFile as writeFile3 } from "fs/promises";
2428
2409
  import { homedir as homedir3 } from "os";
2429
2410
  import path6 from "path";
2430
2411
  import { promisify } from "util";
2431
2412
  var execFileAsync = promisify(execFile);
2432
2413
  var RUNTIME_INSTALL_TIMEOUT_MS = 5 * 6e4;
2433
- var WINDOWS_RENAME_RETRY_DELAYS_MS = [50, 100, 200, 400, 500, 500, 500, 500, 500];
2434
- var resolveBundledNodePlacement = (sourceRoot, sourceNode, pathApi = path6) => {
2435
- const relativeNode = pathApi.relative(sourceRoot, sourceNode);
2436
- return {
2437
- copyNode: relativeNode.startsWith("..") || pathApi.isAbsolute(relativeNode),
2438
- relativeNode
2439
- };
2440
- };
2441
- var isWindowsRenameContention = (error) => error instanceof Error && "code" in error && ["EACCES", "EBUSY", "EPERM"].includes(String(error.code));
2442
- var renameStagedRuntime = async (source, destination, options = {}) => {
2443
- const platform4 = options.platform ?? process.platform;
2444
- const renamePath = options.renamePath ?? rename4;
2445
- const wait4 = options.wait ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
2446
- const delays = platform4 === "win32" ? WINDOWS_RENAME_RETRY_DELAYS_MS : [];
2447
- for (let attempt = 0; ; attempt += 1) {
2448
- try {
2449
- await renamePath(source, destination);
2450
- return;
2451
- } catch (error) {
2452
- const delay = delays[attempt];
2453
- if (delay === void 0 || !isWindowsRenameContention(error)) throw error;
2454
- await wait4(delay);
2455
- }
2456
- }
2457
- };
2458
- var exists = async (filePath) => access(filePath).then(() => true).catch(() => false);
2459
- var resolveNpmInvocation = async (options = {}) => {
2460
- const platform4 = options.platform ?? process.platform;
2461
- if (platform4 !== "win32") return { argsPrefix: [], executable: "npm" };
2462
- const nodeExecutable = options.nodeExecutable ?? process.execPath;
2463
- const pathApi = options.pathApi ?? path6;
2464
- const pathExists = options.pathExists ?? exists;
2465
- const candidates = [
2466
- options.npmExecPath ?? process.env.npm_execpath,
2467
- pathApi.join(pathApi.dirname(nodeExecutable), "node_modules", "npm", "bin", "npm-cli.js")
2468
- ];
2469
- for (const candidate of candidates) {
2470
- if (!candidate || pathApi.basename(candidate).toLowerCase() !== "npm-cli.js") continue;
2471
- if (await pathExists(candidate)) {
2472
- return { argsPrefix: [candidate], executable: nodeExecutable };
2473
- }
2474
- }
2475
- throw new Error(
2476
- `Unable to locate npm-cli.js for the stable Connector runtime installation (node=${JSON.stringify(nodeExecutable)}, npm_execpath=${JSON.stringify(options.npmExecPath ?? process.env.npm_execpath ?? "")})`
2414
+ var MACOS_NODE_LIBRARY_PATTERN = /^libnode(?:\.\d+)*\.dylib$/;
2415
+ var copyNodeRuntime = async (sourceNode, destinationNode, options = {}) => {
2416
+ const sourceDirectory = path6.dirname(sourceNode);
2417
+ const destinationDirectory = path6.dirname(destinationNode);
2418
+ await mkdir5(destinationDirectory, { recursive: true });
2419
+ await cp(sourceNode, destinationNode, { dereference: true, force: true });
2420
+ if ((options.platform ?? process.platform) !== "darwin") return;
2421
+ const companions = await readdir4(sourceDirectory, { withFileTypes: true });
2422
+ await Promise.all(
2423
+ companions.filter(
2424
+ (entry) => (entry.isFile() || entry.isSymbolicLink()) && MACOS_NODE_LIBRARY_PATTERN.test(entry.name)
2425
+ ).map(
2426
+ (entry) => cp(path6.join(sourceDirectory, entry.name), path6.join(destinationDirectory, entry.name), {
2427
+ dereference: true,
2428
+ force: true
2429
+ })
2430
+ )
2477
2431
  );
2478
2432
  };
2433
+ var exists = async (filePath) => access(filePath).then(() => true).catch(() => false);
2479
2434
  var runtimeRoot = () => process.env.BUDA_CONNECTOR_RUNTIME_ROOT || path6.join(homedir3(), ".buda", "runtime");
2480
2435
  var runtimePlatformKey = `${process.platform}-${process.arch}-node${process.versions.modules}`;
2481
2436
  var versionDirectory = () => path6.join(runtimeRoot(), runtimePlatformKey, CONNECTOR_RUNTIME_VERSION);
@@ -2509,11 +2464,9 @@ var validateRuntime = async (runtime) => {
2509
2464
  };
2510
2465
  var installFromNpm = async (directory) => {
2511
2466
  await mkdir5(directory, { recursive: true, mode: 448 });
2512
- const npm = await resolveNpmInvocation();
2513
2467
  await execFileAsync(
2514
- npm.executable,
2468
+ process.platform === "win32" ? "npm.cmd" : "npm",
2515
2469
  [
2516
- ...npm.argsPrefix,
2517
2470
  "install",
2518
2471
  "--ignore-scripts=false",
2519
2472
  "--no-audit",
@@ -2534,8 +2487,7 @@ var installFromNpm = async (directory) => {
2534
2487
  "cli.js"
2535
2488
  );
2536
2489
  const nodeExecutable = path6.join(directory, "node", path6.basename(process.execPath));
2537
- await mkdir5(path6.dirname(nodeExecutable), { recursive: true });
2538
- await cp(process.execPath, nodeExecutable, { force: true });
2490
+ await copyNodeRuntime(process.execPath, nodeExecutable);
2539
2491
  if (!await exists(entrypoint)) throw new Error("Installed Connector runtime is incomplete");
2540
2492
  return { entrypoint, nodeExecutable, version: CONNECTOR_RUNTIME_VERSION };
2541
2493
  };
@@ -2548,12 +2500,11 @@ var installFromBundledRuntime = async (directory, sourceRoot) => {
2548
2500
  if (relativeEntrypoint.startsWith("..") || path6.isAbsolute(relativeEntrypoint)) {
2549
2501
  throw new Error("Bundled Connector entrypoint must be inside its runtime root");
2550
2502
  }
2551
- const { copyNode, relativeNode } = resolveBundledNodePlacement(sourceRoot, sourceNode);
2503
+ const relativeNode = path6.relative(sourceRoot, sourceNode);
2552
2504
  const entrypoint = path6.join(directory, relativeEntrypoint);
2553
- const nodeExecutable = copyNode ? path6.join(directory, "node", path6.basename(sourceNode)) : path6.join(directory, relativeNode);
2554
- if (copyNode) {
2555
- await mkdir5(path6.dirname(nodeExecutable), { recursive: true });
2556
- await cp(sourceNode, nodeExecutable, { force: true });
2505
+ const nodeExecutable = relativeNode.startsWith("..") ? path6.join(directory, "node", path6.basename(sourceNode)) : path6.join(directory, relativeNode);
2506
+ if (relativeNode.startsWith("..")) {
2507
+ await copyNodeRuntime(sourceNode, nodeExecutable);
2557
2508
  }
2558
2509
  if (!await exists(entrypoint))
2559
2510
  throw new Error("Copied Connector runtime entrypoint is missing");
@@ -2599,7 +2550,7 @@ async function ensureStableConnectorRuntime() {
2599
2550
  };
2600
2551
  await writeManifest(temporary, rebased);
2601
2552
  await rm(finalDirectory, { recursive: true, force: true });
2602
- await renameStagedRuntime(temporary, finalDirectory);
2553
+ await rename4(temporary, finalDirectory);
2603
2554
  return rebased;
2604
2555
  } catch (error) {
2605
2556
  await rm(temporary, { recursive: true, force: true });
@@ -2613,8 +2564,6 @@ async function ensureStableConnectorRuntime() {
2613
2564
  // src/daemon-client.ts
2614
2565
  var START_TIMEOUT_MS = 45e3;
2615
2566
  var BOOTSTRAP_TIMEOUT_MS = 5 * 6e4;
2616
- var GRACEFUL_STOP_TIMEOUT_MS = 3e3;
2617
- var LOCK_ONLY_RECOVERY_MAX_AGE_MS = 5 * 6e4;
2618
2567
  var POLL_INTERVAL_MS = 250;
2619
2568
  var wait2 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
2620
2569
  var getConfigRoot = () => process.env.BUDA_CONNECTOR_CONFIG_ROOT;
@@ -2647,78 +2596,14 @@ var compareVersions = (left, right) => {
2647
2596
  return 0;
2648
2597
  };
2649
2598
  var shouldReplaceDaemonVersion = (runningVersion, candidateVersion) => compareVersions(candidateVersion, runningVersion) > 0;
2650
- var shouldDisplayConnectorLogEntry = (createdAt, startedAt) => {
2651
- const timestamp = Date.parse(createdAt);
2652
- return Number.isNaN(timestamp) || timestamp >= startedAt;
2653
- };
2654
- var getErrorCode = (error) => typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
2655
- var waitForChildSpawn = (child) => new Promise((resolve, reject) => {
2656
- const onError = (error) => {
2657
- child.off("spawn", onSpawn);
2658
- reject(error);
2659
- };
2660
- const onSpawn = () => {
2661
- child.off("error", onError);
2662
- resolve();
2663
- };
2664
- child.once("error", onError);
2665
- child.once("spawn", onSpawn);
2666
- });
2667
- async function spawnDetachedDaemonProcess(command, args, options, dependencies = {}) {
2668
- const platform4 = dependencies.platform ?? process.platform;
2669
- const spawnProcess = dependencies.spawnProcess ?? ((file, fileArgs, spawnOptions) => spawn(file, fileArgs, spawnOptions));
2670
- const strategies = platform4 === "win32" ? [
2671
- { name: "detached-hidden", windowsHide: true },
2672
- { name: "detached-unhidden", windowsHide: false }
2673
- ] : [{ name: "detached-hidden", windowsHide: true }];
2674
- const attempted = [];
2675
- for (const [index, strategy] of strategies.entries()) {
2676
- attempted.push(strategy.name);
2677
- const child = spawnProcess(command, args, {
2678
- detached: true,
2679
- env: options.env,
2680
- stdio: options.stdio,
2681
- windowsHide: strategy.windowsHide
2682
- });
2683
- try {
2684
- await waitForChildSpawn(child);
2685
- if (!child.pid) {
2686
- throw Object.assign(new Error("spawned Connector daemon did not expose a PID"), {
2687
- code: "NO_PID"
2688
- });
2689
- }
2690
- child.unref();
2691
- return { pid: child.pid, strategy: strategy.name };
2692
- } catch (error) {
2693
- const code = getErrorCode(error);
2694
- const shouldRetry = platform4 === "win32" && index === 0 && code === "EINVAL";
2695
- if (shouldRetry) continue;
2696
- const diagnostics = [
2697
- `code=${code}`,
2698
- `strategies=${attempted.join(",")}`,
2699
- `platform=${platform4}`,
2700
- `node=${dependencies.nodeVersion ?? process.version}`,
2701
- `executable=${JSON.stringify(command)}`,
2702
- `entrypoint=${JSON.stringify(args[0] ?? "")}`,
2703
- `cwd=${JSON.stringify(dependencies.cwd ?? process.cwd())}`
2704
- ].join(" ");
2705
- throw new Error(`Connector daemon process creation failed: ${diagnostics}`, { cause: error });
2706
- }
2707
- }
2708
- throw new Error("Connector daemon process creation failed without a spawn attempt");
2709
- }
2710
- var readReadyDaemon = async (launchConfig) => {
2711
- const config = await readDeviceConfig(launchConfig, getConfigRoot());
2712
- if (!config) return null;
2713
- const status = await readLiveStatus(config);
2714
- return status ? { config, status } : null;
2715
- };
2716
2599
  var waitForDaemon = async (launchConfig, deadline) => {
2717
2600
  let lastConfig = null;
2718
2601
  while (Date.now() < deadline) {
2719
- const ready = await readReadyDaemon(launchConfig);
2720
- if (ready) return ready;
2721
2602
  lastConfig = await readDeviceConfig(launchConfig, getConfigRoot());
2603
+ if (lastConfig) {
2604
+ const status = await readLiveStatus(lastConfig);
2605
+ if (status) return { config: lastConfig, status };
2606
+ }
2722
2607
  await wait2(POLL_INTERVAL_MS);
2723
2608
  }
2724
2609
  throw new Error(
@@ -2727,107 +2612,17 @@ var waitForDaemon = async (launchConfig, deadline) => {
2727
2612
  };
2728
2613
  var spawnInternalDaemon = async (launchConfig, mode, preparedRuntime, options = {}) => {
2729
2614
  const runtime = preparedRuntime ?? await ensureStableConnectorRuntime();
2730
- const debugDaemonStdio = process.env.BUDA_CONNECTOR_DEBUG_DAEMON_STDIO === "1";
2731
- return spawnDetachedDaemonProcess(
2732
- runtime.nodeExecutable,
2733
- getRuntimeCommandArgs(runtime, mode, options),
2734
- {
2735
- env: {
2736
- ...process.env,
2737
- BUDA_CONNECTOR_INTERNAL_LAUNCH_CONFIG: JSON.stringify(launchConfig ?? {})
2738
- },
2739
- stdio: debugDaemonStdio ? ["ignore", "inherit", "inherit"] : "ignore"
2740
- }
2741
- );
2742
- };
2743
- var waitForProcessExit = async (pid, timeoutMs) => {
2744
- const deadline = Date.now() + timeoutMs;
2745
- while (Date.now() < deadline) {
2746
- if (!isProcessAlive2(pid)) return true;
2747
- await wait2(50);
2748
- }
2749
- return !isProcessAlive2(pid);
2750
- };
2751
- var signalProcess = (pid, signal) => {
2752
- try {
2753
- process.kill(pid, signal);
2754
- return true;
2755
- } catch (error) {
2756
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH") {
2757
- return false;
2758
- }
2759
- throw error;
2760
- }
2761
- };
2762
- var stopUnresponsiveDaemonProcess = async (pid) => {
2763
- if (!isProcessAlive2(pid)) return;
2764
- if (!signalProcess(pid, "SIGTERM")) return;
2765
- if (await waitForProcessExit(pid, 2e3)) return;
2766
- if (!signalProcess(pid, "SIGKILL")) return;
2767
- if (await waitForProcessExit(pid, 3e3)) return;
2768
- throw new Error(`Timed-out Connector daemon PID ${pid} could not be stopped safely`);
2769
- };
2770
- var waitForDaemonRelease = async (config, status, configRoot = getConfigRoot()) => {
2771
- const deadline = Date.now() + GRACEFUL_STOP_TIMEOUT_MS;
2772
- while (Date.now() < deadline) {
2773
- if (!isProcessAlive2(status.pid)) return true;
2774
- const current = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
2775
- if (!current || current.pid !== status.pid || current.instanceId !== status.instanceId) {
2776
- return true;
2777
- }
2778
- await wait2(50);
2779
- }
2780
- return !isProcessAlive2(status.pid);
2781
- };
2782
- var stopLiveDaemon = async (config, status, configRoot = getConfigRoot(), command = "stop") => {
2783
- const stopStatus = await requestDaemonControl(config, command, configRoot);
2784
- if (command === "stop-if-idle" && !stopStatus.idle) return false;
2785
- if (await waitForDaemonRelease(config, status, configRoot)) return true;
2786
- const current = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
2787
- if (!current || current.pid !== status.pid || current.instanceId !== status.instanceId) {
2788
- return true;
2789
- }
2790
- await stopUnresponsiveDaemonProcess(status.pid);
2791
- return true;
2792
- };
2793
- var shouldRestartUnhealthyDaemonForEnrollment = (status, explicitEnrollment) => explicitEnrollment && status.idle && status.heartbeatHealthy === false;
2794
- var readDaemonLockOwnerPid = async (config) => {
2795
- const lockPath = `${getConnectorDaemonLockTarget(
2796
- config.serverUrl,
2797
- config.connectorId,
2798
- getConfigRoot()
2799
- )}.lock`;
2800
- const raw = await readFile6(lockPath, "utf8").catch(() => "");
2801
- try {
2802
- const metadata = JSON.parse(raw);
2803
- const age = typeof metadata.createdAt === "number" && Number.isFinite(metadata.createdAt) ? Date.now() - metadata.createdAt : Number.POSITIVE_INFINITY;
2804
- return typeof metadata.pid === "number" && Number.isSafeInteger(metadata.pid) && metadata.pid > 0 && age >= 0 && age <= LOCK_ONLY_RECOVERY_MAX_AGE_MS ? metadata.pid : null;
2805
- } catch {
2806
- return null;
2807
- }
2615
+ const child = spawn(runtime.nodeExecutable, getRuntimeCommandArgs(runtime, mode, options), {
2616
+ detached: true,
2617
+ env: {
2618
+ ...process.env,
2619
+ BUDA_CONNECTOR_INTERNAL_LAUNCH_CONFIG: JSON.stringify(launchConfig ?? {})
2620
+ },
2621
+ stdio: "ignore",
2622
+ windowsHide: true
2623
+ });
2624
+ child.unref();
2808
2625
  };
2809
- async function startDaemonWithRecovery(dependencies, maxAttempts = 2) {
2810
- let lastError;
2811
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
2812
- const spawned = await dependencies.spawnDaemon();
2813
- try {
2814
- return await dependencies.waitForReady();
2815
- } catch (error) {
2816
- lastError = error;
2817
- const lateReady = await dependencies.readReady().catch(() => null);
2818
- if (lateReady) return lateReady;
2819
- await dependencies.stopDaemon(spawned.pid);
2820
- if (attempt === maxAttempts) throw error;
2821
- }
2822
- }
2823
- throw lastError;
2824
- }
2825
- var startInternalDaemonWithRecovery = (launchConfig, mode, preparedRuntime, options = {}) => startDaemonWithRecovery({
2826
- readReady: () => readReadyDaemon(launchConfig),
2827
- spawnDaemon: () => spawnInternalDaemon(launchConfig, mode, preparedRuntime, options),
2828
- stopDaemon: stopUnresponsiveDaemonProcess,
2829
- waitForReady: () => waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS)
2830
- });
2831
2626
  var stopOlderIdleDaemon = async (config, status) => {
2832
2627
  if (!shouldReplaceDaemonVersion(status.version, CONNECTOR_RUNTIME_VERSION)) return false;
2833
2628
  if (!status.idle) return false;
@@ -2854,19 +2649,14 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2854
2649
  if (existingConfig) {
2855
2650
  const existingStatus = await readLiveStatus(existingConfig);
2856
2651
  if (existingStatus) {
2857
- if (shouldRestartUnhealthyDaemonForEnrollment(
2858
- existingStatus,
2859
- Boolean(options.explicitEnrollment)
2860
- ) && await stopLiveDaemon(existingConfig, existingStatus, getConfigRoot(), "stop-if-idle")) {
2861
- return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
2862
- }
2863
2652
  const preparedRuntime = !shouldReplaceDaemonVersion(existingStatus.version, CONNECTOR_RUNTIME_VERSION) || !existingStatus.idle ? void 0 : await ensureStableConnectorRuntime();
2864
2653
  const replaced = await stopOlderIdleDaemon(existingConfig, existingStatus);
2865
2654
  if (!replaced) {
2866
2655
  const status = mode === "persistent" ? await requestDaemonControl(existingConfig, "persist", getConfigRoot()) : existingStatus;
2867
2656
  return { config: existingConfig, status };
2868
2657
  }
2869
- return await startInternalDaemonWithRecovery(launchConfig, mode, preparedRuntime, options);
2658
+ await spawnInternalDaemon(launchConfig, mode, preparedRuntime, options);
2659
+ return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2870
2660
  }
2871
2661
  const persistedStatus = await readPersistedDaemonStatus(
2872
2662
  existingConfig,
@@ -2877,12 +2667,9 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2877
2667
  `Connector daemon PID ${persistedStatus.pid} is running but its local control channel is not responding. Stop that process and retry.`
2878
2668
  );
2879
2669
  }
2880
- const lockOwnerPid = await readDaemonLockOwnerPid(existingConfig);
2881
- if (lockOwnerPid && isProcessAlive2(lockOwnerPid)) {
2882
- await stopUnresponsiveDaemonProcess(lockOwnerPid);
2883
- }
2884
2670
  }
2885
- return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
2671
+ await spawnInternalDaemon(launchConfig, mode, void 0, options);
2672
+ return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2886
2673
  } finally {
2887
2674
  await bootstrapLock.release();
2888
2675
  }
@@ -2902,7 +2689,6 @@ var streamDaemonLogs = (options) => {
2902
2689
  for (const line of lines.slice(seenLines)) {
2903
2690
  try {
2904
2691
  const entry = JSON.parse(line);
2905
- if (!shouldDisplayConnectorLogEntry(entry.createdAt, options.startedAt)) continue;
2906
2692
  const connectorId = entry.metadata?.connectorId;
2907
2693
  if (options.dashboardActive && typeof connectorId === "string") {
2908
2694
  const output2 = entry.metadata?.source === "daemon-console" ? entry.message : `[buda-connector] ${entry.message}`;
@@ -2980,7 +2766,7 @@ async function stopLocalConnectorDaemon(connectorId, serverUrl, configRoot = get
2980
2766
  if (!config) throw new Error(`Local Connector ${connectorId} was not found`);
2981
2767
  const status = await readLiveStatus(config, configRoot);
2982
2768
  if (!status) return;
2983
- await stopLiveDaemon(config, status, configRoot);
2769
+ await requestDaemonControl(config, "stop", configRoot);
2984
2770
  }
2985
2771
  async function stopAllLocalConnectorDaemons(configRoot = getConfigRoot()) {
2986
2772
  const configs = await listDeviceConfigs(configRoot);
@@ -2993,7 +2779,7 @@ ${config.connectorId}`, config);
2993
2779
  const results = await Promise.allSettled(
2994
2780
  [...identities.values()].map(async (config) => {
2995
2781
  const status = await readLiveStatus(config, configRoot);
2996
- if (status) await stopLiveDaemon(config, status, configRoot);
2782
+ if (status) await requestDaemonControl(config, "stop", configRoot);
2997
2783
  })
2998
2784
  );
2999
2785
  const failures = results.filter((result) => result.status === "rejected");
@@ -3013,7 +2799,6 @@ var waitForConnectorViewerExit = async (dashboardClosed) => {
3013
2799
  process.off("SIGTERM", finishSignal);
3014
2800
  };
3015
2801
  async function runConnectorDashboard(launchConfig, options = {}) {
3016
- const startedAt = Date.now();
3017
2802
  let finishDashboard = () => void 0;
3018
2803
  const dashboardClosed = new Promise((resolve) => {
3019
2804
  finishDashboard = resolve;
@@ -3034,8 +2819,7 @@ async function runConnectorDashboard(launchConfig, options = {}) {
3034
2819
  connectorId: config.connectorId,
3035
2820
  dashboardActive: false,
3036
2821
  plain: Boolean(options.plain),
3037
- serverUrl: config.serverUrl,
3038
- startedAt
2822
+ serverUrl: config.serverUrl
3039
2823
  });
3040
2824
  if (!process.stdin.isTTY) {
3041
2825
  stopLogs2();
@@ -3062,8 +2846,7 @@ async function runConnectorDashboard(launchConfig, options = {}) {
3062
2846
  const stopLogs = streamDaemonLogs({
3063
2847
  dashboardActive: true,
3064
2848
  plain: false,
3065
- serverUrl: getServerUrl(launchConfig),
3066
- startedAt
2849
+ serverUrl: getServerUrl(launchConfig)
3067
2850
  });
3068
2851
  let refreshing = false;
3069
2852
  const refresh = async () => {
@@ -3608,7 +3391,7 @@ import {
3608
3391
  lstat,
3609
3392
  mkdir as mkdir8,
3610
3393
  open as open2,
3611
- readdir as readdir4,
3394
+ readdir as readdir5,
3612
3395
  readFile as readFile7,
3613
3396
  readlink,
3614
3397
  realpath,
@@ -3793,7 +3576,7 @@ var createJournal = async (storageRoot, migrationDirectory, owner) => {
3793
3576
  };
3794
3577
  };
3795
3578
  var inventoryLegacySources = async (storageRoot) => {
3796
- const rootEntries = await readdir4(storageRoot, { withFileTypes: true });
3579
+ const rootEntries = await readdir5(storageRoot, { withFileTypes: true });
3797
3580
  const templateNames = new Set(LEGACY_TEMPLATE_FILES);
3798
3581
  for (const entry of rootEntries) {
3799
3582
  if (entry.name.endsWith(".md")) templateNames.add(entry.name);
@@ -3823,7 +3606,7 @@ var inventoryLegacySources = async (storageRoot) => {
3823
3606
  `Legacy migration source ${candidate.id} must not be a symbolic link`
3824
3607
  );
3825
3608
  }
3826
- if (info.isDirectory() && (await readdir4(candidate.source)).length === 0) continue;
3609
+ if (info.isDirectory() && (await readdir5(candidate.source)).length === 0) continue;
3827
3610
  found.push(candidate);
3828
3611
  }
3829
3612
  return found;
@@ -3843,7 +3626,7 @@ var copyTree = async (source, destination, onEntryCopied) => {
3843
3626
  const info = await lstat(source);
3844
3627
  if (info.isDirectory()) {
3845
3628
  await mkdir8(destination, { mode: info.mode & 4095 });
3846
- const entries = (await readdir4(source)).sort();
3629
+ const entries = (await readdir5(source)).sort();
3847
3630
  for (const entry of entries) {
3848
3631
  await copyTree(path9.join(source, entry), path9.join(destination, entry), onEntryCopied);
3849
3632
  }
@@ -3880,8 +3663,8 @@ var verifyTree = async (source, destination) => {
3880
3663
  }
3881
3664
  if (sourceInfo.isDirectory()) {
3882
3665
  const [sourceEntries, destinationEntries] = await Promise.all([
3883
- readdir4(source),
3884
- readdir4(destination)
3666
+ readdir5(source),
3667
+ readdir5(destination)
3885
3668
  ]);
3886
3669
  sourceEntries.sort();
3887
3670
  destinationEntries.sort();
@@ -3959,7 +3742,6 @@ var writeJournal = async (journalPath, journal) => {
3959
3742
  }
3960
3743
  };
3961
3744
  var syncDirectory2 = async (directory) => {
3962
- if (process.platform === "win32") return;
3963
3745
  const handle = await open2(directory, constants2.O_RDONLY);
3964
3746
  try {
3965
3747
  await handle.sync();
@@ -4525,7 +4307,7 @@ import {
4525
4307
  copyFile as copyFile2,
4526
4308
  lstat as lstat2,
4527
4309
  mkdir as mkdir9,
4528
- readdir as readdir5,
4310
+ readdir as readdir6,
4529
4311
  readFile as readFile9,
4530
4312
  realpath as realpath3,
4531
4313
  rm as rm3,
@@ -4533,11 +4315,6 @@ import {
4533
4315
  writeFile as writeFile4
4534
4316
  } from "fs/promises";
4535
4317
  import path11 from "path";
4536
-
4537
- // src/directory-link.ts
4538
- var directoryLinkType = (platform4 = process.platform) => platform4 === "win32" ? "junction" : "dir";
4539
-
4540
- // src/workspace-volume-lifecycle.ts
4541
4318
  var MOUNT_DIRECTORY = "mount";
4542
4319
  var ARCHIVE_MARKER = "archived.json";
4543
4320
  var AGENT_CONTEXT_TEMPLATE_FILES = [
@@ -4635,7 +4412,7 @@ var WorkspaceVolumeLifecycle = class {
4635
4412
  const bootstrapped = [];
4636
4413
  const skipped = [];
4637
4414
  const entries = new Set(
4638
- (await readdir5(templateDirectory, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isFile()).map((entry) => entry.name)
4415
+ (await readdir6(templateDirectory, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isFile()).map((entry) => entry.name)
4639
4416
  );
4640
4417
  for (const filename of AGENT_CONTEXT_TEMPLATE_FILES) {
4641
4418
  if (!entries.has(filename)) continue;
@@ -4689,7 +4466,7 @@ var ensureVolumeLink = async (target, linkPath) => {
4689
4466
  } catch (error) {
4690
4467
  if (!isMissingFile(error)) throw error;
4691
4468
  try {
4692
- await symlink2(target, linkPath, directoryLinkType());
4469
+ await symlink2(target, linkPath, "dir");
4693
4470
  } catch (createError) {
4694
4471
  if (!isAlreadyExists3(createError)) throw createError;
4695
4472
  const info = await lstat2(linkPath);
@@ -4704,7 +4481,6 @@ var isAlreadyExists3 = (error) => error instanceof Error && "code" in error && e
4704
4481
  // src/relay.ts
4705
4482
  var RELAY_PING_INTERVAL_MS = 2e4;
4706
4483
  var RELAY_PONG_TIMEOUT_MS = 1e4;
4707
- var RELAY_ACTIVE_REQUEST_PONG_TIMEOUT_LIMIT = 2;
4708
4484
  var RELAY_HANDSHAKE_TIMEOUT_MS = 1e4;
4709
4485
  var RELAY_RECONNECT_MAX_MS = 3e4;
4710
4486
  var RELAY_RECONNECT_JITTER = 0.2;
@@ -4810,7 +4586,6 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4810
4586
  socket = ws;
4811
4587
  const inflight = /* @__PURE__ */ new Map();
4812
4588
  currentInflight = inflight;
4813
- let consecutivePongTimeouts = 0;
4814
4589
  let detached = false;
4815
4590
  const detach = () => {
4816
4591
  if (detached) return;
@@ -4823,7 +4598,6 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4823
4598
  void writeLog("info", `relay connected ${relayUrl}`);
4824
4599
  hadOpenConnection = true;
4825
4600
  reconnectDelayMs = RELAY_RECONNECT_INTERVAL_MS;
4826
- consecutivePongTimeouts = 0;
4827
4601
  pingTimer = setInterval(() => {
4828
4602
  if (ws.readyState !== WebSocket2.OPEN) return;
4829
4603
  try {
@@ -4835,33 +4609,18 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4835
4609
  }
4836
4610
  if (pongTimer) clearTimeout(pongTimer);
4837
4611
  pongTimer = setTimeout(() => {
4838
- pongTimer = null;
4839
- consecutivePongTimeouts += 1;
4840
- if (inflight.size > 0 && consecutivePongTimeouts < RELAY_ACTIVE_REQUEST_PONG_TIMEOUT_LIMIT) {
4841
- void writeLog(
4842
- "warn",
4843
- `relay pong timeout deferred while ${inflight.size} request(s) are active`
4844
- );
4845
- return;
4846
- }
4847
4612
  void writeLog("warn", "relay pong timeout, terminating");
4848
4613
  ws.terminate();
4849
4614
  }, RELAY_PONG_TIMEOUT_MS);
4850
4615
  }, RELAY_PING_INTERVAL_MS);
4851
4616
  });
4852
4617
  ws.on("pong", () => {
4853
- consecutivePongTimeouts = 0;
4854
4618
  if (pongTimer) {
4855
4619
  clearTimeout(pongTimer);
4856
4620
  pongTimer = null;
4857
4621
  }
4858
4622
  });
4859
4623
  ws.on("message", (raw) => {
4860
- consecutivePongTimeouts = 0;
4861
- if (pongTimer) {
4862
- clearTimeout(pongTimer);
4863
- pongTimer = null;
4864
- }
4865
4624
  void handleRelayMessage(
4866
4625
  ws,
4867
4626
  bunnyDaemon,
@@ -5037,7 +4796,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5037
4796
  });
5038
4797
  executionIdentity = message.executionIdentity;
5039
4798
  } catch (error) {
5040
- await sendRelayError(socket, message.requestId, error).catch(() => void 0);
4799
+ sendRelayError(socket, message.requestId, error);
5041
4800
  return;
5042
4801
  }
5043
4802
  }
@@ -5123,7 +4882,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5123
4882
  durationMs: Date.now() - codingRunStartedAt
5124
4883
  });
5125
4884
  }
5126
- await sendRelayMessage(socket, {
4885
+ sendRelayMessage(socket, {
5127
4886
  type: "start",
5128
4887
  requestId: message.requestId,
5129
4888
  status: response.status,
@@ -5143,7 +4902,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5143
4902
  while (true) {
5144
4903
  const { value, done } = await reader.read();
5145
4904
  if (done) break;
5146
- await sendRelayMessage(socket, {
4905
+ sendRelayMessage(socket, {
5147
4906
  type: "chunk",
5148
4907
  requestId: message.requestId,
5149
4908
  data: binaryResponse ? Buffer.from(value).toString("base64") : decoder?.decode(value, { stream: true }) ?? "",
@@ -5152,7 +4911,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5152
4911
  }
5153
4912
  const tail = decoder?.decode() ?? "";
5154
4913
  if (tail) {
5155
- await sendRelayMessage(socket, {
4914
+ sendRelayMessage(socket, {
5156
4915
  type: "chunk",
5157
4916
  requestId: message.requestId,
5158
4917
  data: tail
@@ -5163,7 +4922,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5163
4922
  reader.releaseLock();
5164
4923
  }
5165
4924
  }
5166
- await sendRelayMessage(socket, {
4925
+ sendRelayMessage(socket, {
5167
4926
  type: "end",
5168
4927
  requestId: message.requestId
5169
4928
  });
@@ -5175,16 +4934,14 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5175
4934
  });
5176
4935
  }
5177
4936
  } catch (error) {
5178
- controller.abort();
5179
4937
  if (codingRunTrace && codingRunStartedAt !== void 0) {
5180
4938
  await writeLog("error", "relay request coding run error", {
5181
4939
  ...codingRunTrace,
5182
4940
  status: codingRunStatus,
5183
- durationMs: Date.now() - codingRunStartedAt,
5184
- error: formatConnectorRelayError(error)
4941
+ durationMs: Date.now() - codingRunStartedAt
5185
4942
  });
5186
4943
  }
5187
- await sendRelayError(socket, message.requestId, error).catch(() => void 0);
4944
+ sendRelayError(socket, message.requestId, error);
5188
4945
  } finally {
5189
4946
  inflight.delete(message.requestId);
5190
4947
  }
@@ -5238,20 +4995,17 @@ var normalizeWorkspaceRequest = (request, mounts) => {
5238
4995
  if (!mount || relative === void 0) {
5239
4996
  throw new ConnectorRelayProtocolError("WORKSPACE_MISMATCH");
5240
4997
  }
5241
- const systemEnv = body.systemEnv && typeof body.systemEnv === "object" && !Array.isArray(body.systemEnv) ? body.systemEnv : {};
5242
- const workspaceEnv = {
5243
- BUDA_AGENT_ROOT: mounts.agent,
5244
- BUDA_SPACE_ROOT: mounts.space,
5245
- BUDA_SYSTEM_ROOT: mounts.system
5246
- };
4998
+ const env = body.env && typeof body.env === "object" && !Array.isArray(body.env) ? body.env : {};
5247
4999
  return {
5248
5000
  ...request,
5249
5001
  body: {
5250
5002
  ...body,
5251
5003
  cwd: path12.join(mount, relative),
5252
- systemEnv: {
5253
- ...systemEnv,
5254
- ...workspaceEnv
5004
+ env: {
5005
+ ...env,
5006
+ BUDA_AGENT_ROOT: mounts.agent,
5007
+ BUDA_SPACE_ROOT: mounts.space,
5008
+ BUDA_SYSTEM_ROOT: mounts.system
5255
5009
  }
5256
5010
  }
5257
5011
  };
@@ -5283,24 +5037,17 @@ var normalizeConnectorFilesystemRequest = (request, mounts) => {
5283
5037
  }
5284
5038
  return request;
5285
5039
  };
5286
- async function sendRelayError(socket, requestId, error) {
5287
- await sendRelayMessage(socket, {
5040
+ function sendRelayError(socket, requestId, error) {
5041
+ sendRelayMessage(socket, {
5288
5042
  type: "error",
5289
5043
  requestId,
5290
5044
  error: error instanceof Error ? error.message : String(error),
5291
5045
  ...error instanceof ConnectorRelayProtocolError ? { code: error.code } : {}
5292
5046
  });
5293
5047
  }
5294
- async function sendRelayMessage(socket, message) {
5295
- if (socket.readyState !== WebSocket2.OPEN) {
5296
- throw new Error(`Connector relay socket closed before ${message.type} could be sent.`);
5297
- }
5298
- await new Promise((resolve, reject) => {
5299
- socket.send(JSON.stringify(message), (error) => {
5300
- if (error) reject(error);
5301
- else resolve();
5302
- });
5303
- });
5048
+ function sendRelayMessage(socket, message) {
5049
+ if (socket.readyState !== WebSocket2.OPEN) return;
5050
+ socket.send(JSON.stringify(message));
5304
5051
  }
5305
5052
  function headersToRecord(headers) {
5306
5053
  const result = {};
@@ -5359,7 +5106,7 @@ async function ensureSymlink(target, linkPath) {
5359
5106
  } catch {
5360
5107
  }
5361
5108
  await mkdir10(path13.dirname(linkPath), { recursive: true });
5362
- await symlink3(target, linkPath, directoryLinkType());
5109
+ await symlink3(target, linkPath, "dir");
5363
5110
  }
5364
5111
 
5365
5112
  // src/workspace-registry.ts
@@ -5816,30 +5563,7 @@ import { mkdir as mkdir12 } from "fs/promises";
5816
5563
  import path15 from "path";
5817
5564
  var RECOVERED_DAEMON_STOP_TIMEOUT_MS = 1e4;
5818
5565
  var RECOVERED_DAEMON_STOP_POLL_MS = 100;
5819
- var SHUTDOWN_FORCE_EXIT_MS = 1e4;
5820
- var SHUTDOWN_STEP_TIMEOUT_MS = 2e3;
5821
5566
  var wait3 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
5822
- var runShutdownStep = async (label, operation) => {
5823
- let timeout;
5824
- try {
5825
- await Promise.race([
5826
- Promise.resolve().then(operation),
5827
- new Promise((_, reject) => {
5828
- timeout = setTimeout(
5829
- () => reject(new Error(`${label} timed out after ${SHUTDOWN_STEP_TIMEOUT_MS}ms`)),
5830
- SHUTDOWN_STEP_TIMEOUT_MS
5831
- );
5832
- })
5833
- ]);
5834
- } catch (error) {
5835
- await writeLog(
5836
- "warn",
5837
- `${label} failed during shutdown: ${error instanceof Error ? error.message : String(error)}`
5838
- ).catch(() => void 0);
5839
- } finally {
5840
- if (timeout) clearTimeout(timeout);
5841
- }
5842
- };
5843
5567
  async function stopRecoveredIdleDaemon(config, configRoot) {
5844
5568
  const stopStatus = await requestDaemonControl(config, "stop-if-idle", configRoot).catch(
5845
5569
  () => null
@@ -5959,30 +5683,18 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5959
5683
  const shutdown = async (shutdownOptions = {}) => {
5960
5684
  if (shuttingDown) return;
5961
5685
  shuttingDown = true;
5962
- const forceExit = options.exitOnShutdown === false ? void 0 : setTimeout(() => process.exit(0), SHUTDOWN_FORCE_EXIT_MS);
5963
5686
  if (interval) clearInterval(interval);
5964
5687
  relay.stop();
5965
5688
  try {
5966
- await Promise.all([
5967
- runShutdownStep("browser session cleanup", closeAllBrowserSessions),
5968
- ...shutdownOptions.unregister === false ? [] : [
5969
- runShutdownStep(
5970
- "connector unregister",
5971
- () => unregisterConnector(config, launchConfig)
5972
- )
5973
- ],
5974
- runShutdownStep(
5975
- "embedded Bunny daemon shutdown",
5976
- () => stopEmbeddedBunnyDaemon(activeBunnyDaemon)
5977
- )
5978
- ]);
5689
+ await closeAllBrowserSessions();
5690
+ if (shutdownOptions.unregister !== false) {
5691
+ await unregisterConnector(config, launchConfig);
5692
+ }
5693
+ await stopEmbeddedBunnyDaemon(activeBunnyDaemon);
5979
5694
  } finally {
5980
- await Promise.all([
5981
- runShutdownStep("control channel shutdown", async () => control?.close()),
5982
- runShutdownStep("daemon lock release", () => daemonLock.release()),
5983
- runShutdownStep("console capture restore", async () => restoreConsole?.())
5984
- ]);
5985
- if (forceExit) clearTimeout(forceExit);
5695
+ await control?.close();
5696
+ await daemonLock.release();
5697
+ await restoreConsole?.();
5986
5698
  if (options.exitOnShutdown !== false) process.exit(0);
5987
5699
  }
5988
5700
  };
@@ -5997,11 +5709,8 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5997
5709
  });
5998
5710
  }).catch((error) => {
5999
5711
  heartbeatHealthy = false;
6000
- if (error instanceof ConnectorDeletedError || error instanceof ConnectorUnauthorizedError) {
6001
- void writeLog(
6002
- "warn",
6003
- error instanceof ConnectorDeletedError ? "connector deleted remotely; stopping daemon" : "connector authentication rejected remotely; stopping daemon"
6004
- );
5712
+ if (error instanceof ConnectorDeletedError) {
5713
+ void writeLog("warn", "connector deleted remotely; stopping daemon");
6005
5714
  void shutdown({ unregister: false });
6006
5715
  return;
6007
5716
  }
@@ -6025,10 +5734,6 @@ async function runConnectorDaemon(launchConfig, options = {}) {
6025
5734
  process.once("SIGINT", () => void shutdown());
6026
5735
  process.once("SIGTERM", () => void shutdown());
6027
5736
  } catch (error) {
6028
- await writeLog(
6029
- "error",
6030
- `daemon startup failed: ${error instanceof Error ? error.stack || error.message : String(error)}`
6031
- ).catch(() => void 0);
6032
5737
  if (bunnyDaemon) await stopEmbeddedBunnyDaemon(bunnyDaemon);
6033
5738
  await daemonLock.release();
6034
5739
  await restoreConsole?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buda-ai/connector",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -18,7 +18,6 @@
18
18
  "dev": "tsx src/cli.ts dev",
19
19
  "daemon": "tsx src/cli.ts daemon",
20
20
  "new": "tsx src/cli.ts new",
21
- "prepack": "tsup --silent",
22
21
  "start": "node dist/cli.js daemon",
23
22
  "build": "tsup",
24
23
  "test": "vitest run",
@@ -27,7 +26,7 @@
27
26
  "lint:err": "biome format . --write && biome check . --write --diagnostic-level=error"
28
27
  },
29
28
  "dependencies": {
30
- "@bunny-agent/daemon": "0.9.57",
29
+ "@bunny-agent/daemon": "0.9.51",
31
30
  "@clack/prompts": "^1.0.0",
32
31
  "playwright-core": "^1.52.0",
33
32
  "ws": "^8.21.0"