@buda-ai/connector 0.1.32 → 0.1.33

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`, `USER.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`, `MEMORY.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
- await unlink(lockPath).catch(() => void 0);
29
- continue;
28
+ const recovered = await unlink(lockPath).then(() => true).catch(() => false);
29
+ if (recovered) continue;
30
30
  }
31
31
  if (Date.now() >= deadline) {
32
32
  throw new Error(`Timed out waiting for exclusive access to ${targetPath}`);
@@ -35,17 +35,23 @@ async function acquireExclusiveFileLock(targetPath, options = {}) {
35
35
  continue;
36
36
  }
37
37
  const ownerToken = randomUUID();
38
- await handle.writeFile(
39
- `${JSON.stringify({ pid: process.pid, ownerToken, createdAt: Date.now() })}
38
+ try {
39
+ await handle.writeFile(
40
+ `${JSON.stringify({ pid: process.pid, ownerToken, createdAt: Date.now() })}
40
41
  `
41
- );
42
- await handle.sync();
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();
43
50
  let released = false;
44
51
  return {
45
52
  release: async () => {
46
53
  if (released) return;
47
54
  released = true;
48
- await handle.close();
49
55
  const currentOwner = await readFile(lockPath, "utf8").then((raw) => JSON.parse(raw).ownerToken).catch(() => void 0);
50
56
  if (currentOwner === ownerToken) await unlink(lockPath).catch(() => void 0);
51
57
  }
@@ -517,7 +523,7 @@ import path2 from "path";
517
523
  // package.json
518
524
  var package_default = {
519
525
  name: "@buda-ai/connector",
520
- version: "0.1.32",
526
+ version: "0.1.33",
521
527
  private: false,
522
528
  type: "module",
523
529
  publishConfig: {
@@ -535,6 +541,7 @@ var package_default = {
535
541
  dev: "tsx src/cli.ts dev",
536
542
  daemon: "tsx src/cli.ts daemon",
537
543
  new: "tsx src/cli.ts new",
544
+ prepack: "tsup --silent",
538
545
  start: "node dist/cli.js daemon",
539
546
  build: "tsup",
540
547
  test: "vitest run",
@@ -543,7 +550,7 @@ var package_default = {
543
550
  "lint:err": "biome format . --write && biome check . --write --diagnostic-level=error"
544
551
  },
545
552
  dependencies: {
546
- "@bunny-agent/daemon": "0.9.51",
553
+ "@bunny-agent/daemon": "0.9.58",
547
554
  "@clack/prompts": "^1.0.0",
548
555
  "playwright-core": "^1.52.0",
549
556
  ws: "^8.21.0"
@@ -2208,6 +2215,13 @@ var ConnectorDeletedError = class extends Error {
2208
2215
  this.name = "ConnectorDeletedError";
2209
2216
  }
2210
2217
  };
2218
+ var ConnectorUnauthorizedError = class extends Error {
2219
+ constructor(message) {
2220
+ super(message);
2221
+ this.name = "ConnectorUnauthorizedError";
2222
+ }
2223
+ };
2224
+ var UNREGISTER_TIMEOUT_MS = 3e3;
2211
2225
  async function resolveConnectorScope(deviceId, launchConfig) {
2212
2226
  const serverUrl = normalizeServerOrigin(getServerUrl(launchConfig));
2213
2227
  const headers = { "Content-Type": "application/json" };
@@ -2294,14 +2308,15 @@ function buildAuthHeaders(config, launchConfig) {
2294
2308
  if (config.deviceToken) headers["X-Buda-Connector-Device-Token"] = config.deviceToken;
2295
2309
  return headers;
2296
2310
  }
2297
- async function postJson(serverUrl, pathname, body, config, launchConfig) {
2311
+ async function postJson(serverUrl, pathname, body, config, launchConfig, options = {}) {
2298
2312
  const response = await fetch(`${serverUrl}${pathname}`, {
2299
2313
  method: "POST",
2300
2314
  headers: {
2301
2315
  "Content-Type": "application/json",
2302
2316
  ...buildAuthHeaders(config, launchConfig)
2303
2317
  },
2304
- body: JSON.stringify(body)
2318
+ body: JSON.stringify(body),
2319
+ ...options.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
2305
2320
  });
2306
2321
  if (!response.ok) {
2307
2322
  const text2 = await response.text().catch(() => "");
@@ -2321,6 +2336,9 @@ async function postJson(serverUrl, pathname, body, config, launchConfig) {
2321
2336
  if (errorCode === "connector_deleted") {
2322
2337
  throw new ConnectorDeletedError(errorMessage3 || "Connector no longer exists");
2323
2338
  }
2339
+ if (response.status === 401) {
2340
+ throw new ConnectorUnauthorizedError(errorMessage3 || "Connector authentication failed");
2341
+ }
2324
2342
  throw new Error(`${pathname} failed: ${response.status} ${errorMessage3}`);
2325
2343
  }
2326
2344
  return await response.json();
@@ -2396,7 +2414,8 @@ async function unregisterConnector(config, launchConfig) {
2396
2414
  connectorId: config.connectorId
2397
2415
  },
2398
2416
  config,
2399
- launchConfig
2417
+ launchConfig,
2418
+ { timeoutMs: UNREGISTER_TIMEOUT_MS }
2400
2419
  ).catch((error) => {
2401
2420
  void writeLog("warn", `unregister failed: ${error instanceof Error ? error.message : error}`);
2402
2421
  });
@@ -2421,11 +2440,19 @@ import path6 from "path";
2421
2440
  import { promisify } from "util";
2422
2441
  var execFileAsync = promisify(execFile);
2423
2442
  var RUNTIME_INSTALL_TIMEOUT_MS = 5 * 6e4;
2443
+ var WINDOWS_RENAME_RETRY_DELAYS_MS = [50, 100, 200, 400, 500, 500, 500, 500, 500];
2424
2444
  var MACOS_NODE_LIBRARY_PATTERN = /^libnode(?:\.\d+)*\.dylib$/;
2425
2445
  var getLoadedSharedObjects = () => {
2426
2446
  const report = process.report?.getReport();
2427
2447
  return Array.isArray(report?.sharedObjects) ? report.sharedObjects.filter((value) => typeof value === "string") : [];
2428
2448
  };
2449
+ var resolveBundledNodePlacement = (sourceRoot, sourceNode, pathApi = path6) => {
2450
+ const relativeNode = pathApi.relative(sourceRoot, sourceNode);
2451
+ return {
2452
+ copyNode: relativeNode.startsWith("..") || pathApi.isAbsolute(relativeNode),
2453
+ relativeNode
2454
+ };
2455
+ };
2429
2456
  var copyNodeRuntime = async (sourceNode, destinationNode, options = {}) => {
2430
2457
  const resolvedSourceNode = await realpath(sourceNode);
2431
2458
  const sourceDirectory = path6.dirname(resolvedSourceNode);
@@ -2452,8 +2479,7 @@ var copyNodeRuntime = async (sourceNode, destinationNode, options = {}) => {
2452
2479
  }))
2453
2480
  )).flatMap(({ directory, entries }) => entries.map((entry) => ({ directory, entry })));
2454
2481
  const companionFiles = /* @__PURE__ */ new Map();
2455
- const loadedLibraries = options.loadedLibraries ?? getLoadedSharedObjects();
2456
- for (const library of loadedLibraries) {
2482
+ for (const library of options.loadedLibraries ?? getLoadedSharedObjects()) {
2457
2483
  const name = path6.basename(library);
2458
2484
  if (MACOS_NODE_LIBRARY_PATTERN.test(name)) companionFiles.set(name, library);
2459
2485
  }
@@ -2471,7 +2497,44 @@ var copyNodeRuntime = async (sourceNode, destinationNode, options = {}) => {
2471
2497
  )
2472
2498
  );
2473
2499
  };
2500
+ var isWindowsRenameContention = (error) => error instanceof Error && "code" in error && ["EACCES", "EBUSY", "EPERM"].includes(String(error.code));
2501
+ var renameStagedRuntime = async (source, destination, options = {}) => {
2502
+ const platform4 = options.platform ?? process.platform;
2503
+ const renamePath = options.renamePath ?? rename4;
2504
+ const wait4 = options.wait ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
2505
+ const delays = platform4 === "win32" ? WINDOWS_RENAME_RETRY_DELAYS_MS : [];
2506
+ for (let attempt = 0; ; attempt += 1) {
2507
+ try {
2508
+ await renamePath(source, destination);
2509
+ return;
2510
+ } catch (error) {
2511
+ const delay = delays[attempt];
2512
+ if (delay === void 0 || !isWindowsRenameContention(error)) throw error;
2513
+ await wait4(delay);
2514
+ }
2515
+ }
2516
+ };
2474
2517
  var exists = async (filePath) => access(filePath).then(() => true).catch(() => false);
2518
+ var resolveNpmInvocation = async (options = {}) => {
2519
+ const platform4 = options.platform ?? process.platform;
2520
+ if (platform4 !== "win32") return { argsPrefix: [], executable: "npm" };
2521
+ const nodeExecutable = options.nodeExecutable ?? process.execPath;
2522
+ const pathApi = options.pathApi ?? path6;
2523
+ const pathExists = options.pathExists ?? exists;
2524
+ const candidates = [
2525
+ options.npmExecPath ?? process.env.npm_execpath,
2526
+ pathApi.join(pathApi.dirname(nodeExecutable), "node_modules", "npm", "bin", "npm-cli.js")
2527
+ ];
2528
+ for (const candidate of candidates) {
2529
+ if (!candidate || pathApi.basename(candidate).toLowerCase() !== "npm-cli.js") continue;
2530
+ if (await pathExists(candidate)) {
2531
+ return { argsPrefix: [candidate], executable: nodeExecutable };
2532
+ }
2533
+ }
2534
+ throw new Error(
2535
+ `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 ?? "")})`
2536
+ );
2537
+ };
2475
2538
  var runtimeRoot = () => process.env.BUDA_CONNECTOR_RUNTIME_ROOT || path6.join(homedir3(), ".buda", "runtime");
2476
2539
  var runtimePlatformKey = `${process.platform}-${process.arch}-node${process.versions.modules}`;
2477
2540
  var versionDirectory = () => path6.join(runtimeRoot(), runtimePlatformKey, CONNECTOR_RUNTIME_VERSION);
@@ -2505,9 +2568,11 @@ var validateRuntime = async (runtime) => {
2505
2568
  };
2506
2569
  var installFromNpm = async (directory) => {
2507
2570
  await mkdir5(directory, { recursive: true, mode: 448 });
2571
+ const npm = await resolveNpmInvocation();
2508
2572
  await execFileAsync(
2509
- process.platform === "win32" ? "npm.cmd" : "npm",
2573
+ npm.executable,
2510
2574
  [
2575
+ ...npm.argsPrefix,
2511
2576
  "install",
2512
2577
  "--ignore-scripts=false",
2513
2578
  "--no-audit",
@@ -2541,10 +2606,10 @@ var installFromBundledRuntime = async (directory, sourceRoot) => {
2541
2606
  if (relativeEntrypoint.startsWith("..") || path6.isAbsolute(relativeEntrypoint)) {
2542
2607
  throw new Error("Bundled Connector entrypoint must be inside its runtime root");
2543
2608
  }
2544
- const relativeNode = path6.relative(sourceRoot, sourceNode);
2609
+ const { copyNode, relativeNode } = resolveBundledNodePlacement(sourceRoot, sourceNode);
2545
2610
  const entrypoint = path6.join(directory, relativeEntrypoint);
2546
- const nodeExecutable = relativeNode.startsWith("..") ? path6.join(directory, "node", path6.basename(sourceNode)) : path6.join(directory, relativeNode);
2547
- if (relativeNode.startsWith("..")) {
2611
+ const nodeExecutable = copyNode ? path6.join(directory, "node", path6.basename(sourceNode)) : path6.join(directory, relativeNode);
2612
+ if (copyNode) {
2548
2613
  await copyNodeRuntime(sourceNode, nodeExecutable);
2549
2614
  }
2550
2615
  if (!await exists(entrypoint))
@@ -2591,7 +2656,7 @@ async function ensureStableConnectorRuntime() {
2591
2656
  };
2592
2657
  await writeManifest(temporary, rebased);
2593
2658
  await rm(finalDirectory, { recursive: true, force: true });
2594
- await rename4(temporary, finalDirectory);
2659
+ await renameStagedRuntime(temporary, finalDirectory);
2595
2660
  return rebased;
2596
2661
  } catch (error) {
2597
2662
  await rm(temporary, { recursive: true, force: true });
@@ -2605,6 +2670,8 @@ async function ensureStableConnectorRuntime() {
2605
2670
  // src/daemon-client.ts
2606
2671
  var START_TIMEOUT_MS = 45e3;
2607
2672
  var BOOTSTRAP_TIMEOUT_MS = 5 * 6e4;
2673
+ var GRACEFUL_STOP_TIMEOUT_MS = 3e3;
2674
+ var LOCK_ONLY_RECOVERY_MAX_AGE_MS = 5 * 6e4;
2608
2675
  var POLL_INTERVAL_MS = 250;
2609
2676
  var wait2 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
2610
2677
  var getConfigRoot = () => process.env.BUDA_CONNECTOR_CONFIG_ROOT;
@@ -2637,14 +2704,78 @@ var compareVersions = (left, right) => {
2637
2704
  return 0;
2638
2705
  };
2639
2706
  var shouldReplaceDaemonVersion = (runningVersion, candidateVersion) => compareVersions(candidateVersion, runningVersion) > 0;
2707
+ var shouldDisplayConnectorLogEntry = (createdAt, startedAt) => {
2708
+ const timestamp = Date.parse(createdAt);
2709
+ return Number.isNaN(timestamp) || timestamp >= startedAt;
2710
+ };
2711
+ var getErrorCode = (error) => typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
2712
+ var waitForChildSpawn = (child) => new Promise((resolve, reject) => {
2713
+ const onError = (error) => {
2714
+ child.off("spawn", onSpawn);
2715
+ reject(error);
2716
+ };
2717
+ const onSpawn = () => {
2718
+ child.off("error", onError);
2719
+ resolve();
2720
+ };
2721
+ child.once("error", onError);
2722
+ child.once("spawn", onSpawn);
2723
+ });
2724
+ async function spawnDetachedDaemonProcess(command, args, options, dependencies = {}) {
2725
+ const platform4 = dependencies.platform ?? process.platform;
2726
+ const spawnProcess = dependencies.spawnProcess ?? ((file, fileArgs, spawnOptions) => spawn(file, fileArgs, spawnOptions));
2727
+ const strategies = platform4 === "win32" ? [
2728
+ { name: "detached-hidden", windowsHide: true },
2729
+ { name: "detached-unhidden", windowsHide: false }
2730
+ ] : [{ name: "detached-hidden", windowsHide: true }];
2731
+ const attempted = [];
2732
+ for (const [index, strategy] of strategies.entries()) {
2733
+ attempted.push(strategy.name);
2734
+ const child = spawnProcess(command, args, {
2735
+ detached: true,
2736
+ env: options.env,
2737
+ stdio: options.stdio,
2738
+ windowsHide: strategy.windowsHide
2739
+ });
2740
+ try {
2741
+ await waitForChildSpawn(child);
2742
+ if (!child.pid) {
2743
+ throw Object.assign(new Error("spawned Connector daemon did not expose a PID"), {
2744
+ code: "NO_PID"
2745
+ });
2746
+ }
2747
+ child.unref();
2748
+ return { pid: child.pid, strategy: strategy.name };
2749
+ } catch (error) {
2750
+ const code = getErrorCode(error);
2751
+ const shouldRetry = platform4 === "win32" && index === 0 && code === "EINVAL";
2752
+ if (shouldRetry) continue;
2753
+ const diagnostics = [
2754
+ `code=${code}`,
2755
+ `strategies=${attempted.join(",")}`,
2756
+ `platform=${platform4}`,
2757
+ `node=${dependencies.nodeVersion ?? process.version}`,
2758
+ `executable=${JSON.stringify(command)}`,
2759
+ `entrypoint=${JSON.stringify(args[0] ?? "")}`,
2760
+ `cwd=${JSON.stringify(dependencies.cwd ?? process.cwd())}`
2761
+ ].join(" ");
2762
+ throw new Error(`Connector daemon process creation failed: ${diagnostics}`, { cause: error });
2763
+ }
2764
+ }
2765
+ throw new Error("Connector daemon process creation failed without a spawn attempt");
2766
+ }
2767
+ var readReadyDaemon = async (launchConfig) => {
2768
+ const config = await readDeviceConfig(launchConfig, getConfigRoot());
2769
+ if (!config) return null;
2770
+ const status = await readLiveStatus(config);
2771
+ return status ? { config, status } : null;
2772
+ };
2640
2773
  var waitForDaemon = async (launchConfig, deadline) => {
2641
2774
  let lastConfig = null;
2642
2775
  while (Date.now() < deadline) {
2776
+ const ready = await readReadyDaemon(launchConfig);
2777
+ if (ready) return ready;
2643
2778
  lastConfig = await readDeviceConfig(launchConfig, getConfigRoot());
2644
- if (lastConfig) {
2645
- const status = await readLiveStatus(lastConfig);
2646
- if (status) return { config: lastConfig, status };
2647
- }
2648
2779
  await wait2(POLL_INTERVAL_MS);
2649
2780
  }
2650
2781
  throw new Error(
@@ -2653,17 +2784,126 @@ var waitForDaemon = async (launchConfig, deadline) => {
2653
2784
  };
2654
2785
  var spawnInternalDaemon = async (launchConfig, mode, preparedRuntime, options = {}) => {
2655
2786
  const runtime = preparedRuntime ?? await ensureStableConnectorRuntime();
2656
- const child = spawn(runtime.nodeExecutable, getRuntimeCommandArgs(runtime, mode, options), {
2657
- detached: true,
2658
- env: {
2659
- ...process.env,
2660
- BUDA_CONNECTOR_INTERNAL_LAUNCH_CONFIG: JSON.stringify(launchConfig ?? {})
2661
- },
2662
- stdio: "ignore",
2663
- windowsHide: true
2664
- });
2665
- child.unref();
2787
+ const debugDaemonStdio = process.env.BUDA_CONNECTOR_DEBUG_DAEMON_STDIO === "1";
2788
+ return spawnDetachedDaemonProcess(
2789
+ runtime.nodeExecutable,
2790
+ getRuntimeCommandArgs(runtime, mode, options),
2791
+ {
2792
+ env: {
2793
+ ...process.env,
2794
+ BUDA_CONNECTOR_INTERNAL_LAUNCH_CONFIG: JSON.stringify(launchConfig ?? {})
2795
+ },
2796
+ stdio: debugDaemonStdio ? ["ignore", "inherit", "inherit"] : "ignore"
2797
+ }
2798
+ );
2799
+ };
2800
+ var waitForProcessExit = async (pid, timeoutMs) => {
2801
+ const deadline = Date.now() + timeoutMs;
2802
+ while (Date.now() < deadline) {
2803
+ if (!isProcessAlive2(pid)) return true;
2804
+ await wait2(50);
2805
+ }
2806
+ return !isProcessAlive2(pid);
2666
2807
  };
2808
+ var signalProcess = (pid, signal) => {
2809
+ try {
2810
+ process.kill(pid, signal);
2811
+ return true;
2812
+ } catch (error) {
2813
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH") {
2814
+ return false;
2815
+ }
2816
+ throw error;
2817
+ }
2818
+ };
2819
+ var stopUnresponsiveDaemonProcess = async (pid) => {
2820
+ if (!isProcessAlive2(pid)) return;
2821
+ if (!signalProcess(pid, "SIGTERM")) return;
2822
+ if (await waitForProcessExit(pid, 2e3)) return;
2823
+ if (!signalProcess(pid, "SIGKILL")) return;
2824
+ if (await waitForProcessExit(pid, 3e3)) return;
2825
+ throw new Error(`Timed-out Connector daemon PID ${pid} could not be stopped safely`);
2826
+ };
2827
+ var waitForDaemonRelease = async (config, status, configRoot = getConfigRoot()) => {
2828
+ const deadline = Date.now() + GRACEFUL_STOP_TIMEOUT_MS;
2829
+ while (Date.now() < deadline) {
2830
+ if (!isProcessAlive2(status.pid)) return true;
2831
+ const current = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
2832
+ if (!current || current.pid !== status.pid || current.instanceId !== status.instanceId) {
2833
+ return true;
2834
+ }
2835
+ await wait2(50);
2836
+ }
2837
+ return !isProcessAlive2(status.pid);
2838
+ };
2839
+ var stopLiveDaemon = async (config, status, configRoot = getConfigRoot(), command = "stop") => {
2840
+ const stopStatus = await requestDaemonControl(config, command, configRoot);
2841
+ if (command === "stop-if-idle" && !stopStatus.idle) return false;
2842
+ if (await waitForDaemonRelease(config, status, configRoot)) return true;
2843
+ const current = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
2844
+ if (!current || current.pid !== status.pid || current.instanceId !== status.instanceId) {
2845
+ return true;
2846
+ }
2847
+ await stopUnresponsiveDaemonProcess(status.pid);
2848
+ return true;
2849
+ };
2850
+ var matchesDaemonIdentity = (config, status) => status.connectorId === config.connectorId && normalizeServerOrigin(status.serverUrl) === normalizeServerOrigin(config.serverUrl) && Number.isSafeInteger(status.pid) && status.pid > 0 && typeof status.instanceId === "string" && status.instanceId.length > 0;
2851
+ var readMatchingPersistedDaemonStatus = async (config, configRoot = getConfigRoot()) => {
2852
+ const status = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
2853
+ return status && matchesDaemonIdentity(config, status) ? status : null;
2854
+ };
2855
+ var stopDaemonForIdentity = async (config, configRoot = getConfigRoot()) => {
2856
+ const liveStatus = await readLiveStatus(config, configRoot);
2857
+ if (liveStatus) {
2858
+ await stopLiveDaemon(config, liveStatus, configRoot);
2859
+ return;
2860
+ }
2861
+ const persistedStatus = await readMatchingPersistedDaemonStatus(config, configRoot);
2862
+ if (!persistedStatus || !isProcessAlive2(persistedStatus.pid)) return;
2863
+ const currentStatus = await readMatchingPersistedDaemonStatus(config, configRoot);
2864
+ if (!currentStatus || currentStatus.pid !== persistedStatus.pid || currentStatus.instanceId !== persistedStatus.instanceId || !isProcessAlive2(currentStatus.pid)) {
2865
+ return;
2866
+ }
2867
+ await stopUnresponsiveDaemonProcess(currentStatus.pid);
2868
+ };
2869
+ var shouldRestartUnhealthyDaemonForEnrollment = (status, explicitEnrollment) => explicitEnrollment && status.idle && status.heartbeatHealthy === false;
2870
+ var readDaemonLockOwnerPid = async (config) => {
2871
+ const lockPath = `${getConnectorDaemonLockTarget(
2872
+ config.serverUrl,
2873
+ config.connectorId,
2874
+ getConfigRoot()
2875
+ )}.lock`;
2876
+ const raw = await readFile6(lockPath, "utf8").catch(() => "");
2877
+ try {
2878
+ const metadata = JSON.parse(raw);
2879
+ const age = typeof metadata.createdAt === "number" && Number.isFinite(metadata.createdAt) ? Date.now() - metadata.createdAt : Number.POSITIVE_INFINITY;
2880
+ return typeof metadata.pid === "number" && Number.isSafeInteger(metadata.pid) && metadata.pid > 0 && age >= 0 && age <= LOCK_ONLY_RECOVERY_MAX_AGE_MS ? metadata.pid : null;
2881
+ } catch {
2882
+ return null;
2883
+ }
2884
+ };
2885
+ async function startDaemonWithRecovery(dependencies, maxAttempts = 2) {
2886
+ let lastError;
2887
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
2888
+ const spawned = await dependencies.spawnDaemon();
2889
+ try {
2890
+ return await dependencies.waitForReady();
2891
+ } catch (error) {
2892
+ lastError = error;
2893
+ const lateReady = await dependencies.readReady().catch(() => null);
2894
+ if (lateReady) return lateReady;
2895
+ await dependencies.stopDaemon(spawned.pid);
2896
+ if (attempt === maxAttempts) throw error;
2897
+ }
2898
+ }
2899
+ throw lastError;
2900
+ }
2901
+ var startInternalDaemonWithRecovery = (launchConfig, mode, preparedRuntime, options = {}) => startDaemonWithRecovery({
2902
+ readReady: () => readReadyDaemon(launchConfig),
2903
+ spawnDaemon: () => spawnInternalDaemon(launchConfig, mode, preparedRuntime, options),
2904
+ stopDaemon: stopUnresponsiveDaemonProcess,
2905
+ waitForReady: () => waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS)
2906
+ });
2667
2907
  var stopOlderIdleDaemon = async (config, status) => {
2668
2908
  if (!shouldReplaceDaemonVersion(status.version, CONNECTOR_RUNTIME_VERSION)) return false;
2669
2909
  if (!status.idle) return false;
@@ -2690,14 +2930,19 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2690
2930
  if (existingConfig) {
2691
2931
  const existingStatus = await readLiveStatus(existingConfig);
2692
2932
  if (existingStatus) {
2933
+ if (shouldRestartUnhealthyDaemonForEnrollment(
2934
+ existingStatus,
2935
+ Boolean(options.explicitEnrollment)
2936
+ ) && await stopLiveDaemon(existingConfig, existingStatus, getConfigRoot(), "stop-if-idle")) {
2937
+ return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
2938
+ }
2693
2939
  const preparedRuntime = !shouldReplaceDaemonVersion(existingStatus.version, CONNECTOR_RUNTIME_VERSION) || !existingStatus.idle ? void 0 : await ensureStableConnectorRuntime();
2694
2940
  const replaced = await stopOlderIdleDaemon(existingConfig, existingStatus);
2695
2941
  if (!replaced) {
2696
2942
  const status = mode === "persistent" ? await requestDaemonControl(existingConfig, "persist", getConfigRoot()) : existingStatus;
2697
2943
  return { config: existingConfig, status };
2698
2944
  }
2699
- await spawnInternalDaemon(launchConfig, mode, preparedRuntime, options);
2700
- return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2945
+ return await startInternalDaemonWithRecovery(launchConfig, mode, preparedRuntime, options);
2701
2946
  }
2702
2947
  const persistedStatus = await readPersistedDaemonStatus(
2703
2948
  existingConfig,
@@ -2708,9 +2953,12 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2708
2953
  `Connector daemon PID ${persistedStatus.pid} is running but its local control channel is not responding. Stop that process and retry.`
2709
2954
  );
2710
2955
  }
2956
+ const lockOwnerPid = await readDaemonLockOwnerPid(existingConfig);
2957
+ if (lockOwnerPid && isProcessAlive2(lockOwnerPid)) {
2958
+ await stopUnresponsiveDaemonProcess(lockOwnerPid);
2959
+ }
2711
2960
  }
2712
- await spawnInternalDaemon(launchConfig, mode, void 0, options);
2713
- return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2961
+ return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
2714
2962
  } finally {
2715
2963
  await bootstrapLock.release();
2716
2964
  }
@@ -2730,6 +2978,7 @@ var streamDaemonLogs = (options) => {
2730
2978
  for (const line of lines.slice(seenLines)) {
2731
2979
  try {
2732
2980
  const entry = JSON.parse(line);
2981
+ if (!shouldDisplayConnectorLogEntry(entry.createdAt, options.startedAt)) continue;
2733
2982
  const connectorId = entry.metadata?.connectorId;
2734
2983
  if (options.dashboardActive && typeof connectorId === "string") {
2735
2984
  const output2 = entry.metadata?.source === "daemon-console" ? entry.message : `[buda-connector] ${entry.message}`;
@@ -2805,9 +3054,7 @@ async function stopLocalConnectorDaemon(connectorId, serverUrl, configRoot = get
2805
3054
  (candidate) => candidate.connectorId === connectorId && normalizeServerOrigin(candidate.serverUrl) === serverOrigin
2806
3055
  );
2807
3056
  if (!config) throw new Error(`Local Connector ${connectorId} was not found`);
2808
- const status = await readLiveStatus(config, configRoot);
2809
- if (!status) return;
2810
- await requestDaemonControl(config, "stop", configRoot);
3057
+ await stopDaemonForIdentity(config, configRoot);
2811
3058
  }
2812
3059
  async function stopAllLocalConnectorDaemons(configRoot = getConfigRoot()) {
2813
3060
  const configs = await listDeviceConfigs(configRoot);
@@ -2818,10 +3065,7 @@ async function stopAllLocalConnectorDaemons(configRoot = getConfigRoot()) {
2818
3065
  ${config.connectorId}`, config);
2819
3066
  }
2820
3067
  const results = await Promise.allSettled(
2821
- [...identities.values()].map(async (config) => {
2822
- const status = await readLiveStatus(config, configRoot);
2823
- if (status) await requestDaemonControl(config, "stop", configRoot);
2824
- })
3068
+ [...identities.values()].map((config) => stopDaemonForIdentity(config, configRoot))
2825
3069
  );
2826
3070
  const failures = results.filter((result) => result.status === "rejected");
2827
3071
  if (failures.length) {
@@ -2840,6 +3084,7 @@ var waitForConnectorViewerExit = async (dashboardClosed) => {
2840
3084
  process.off("SIGTERM", finishSignal);
2841
3085
  };
2842
3086
  async function runConnectorDashboard(launchConfig, options = {}) {
3087
+ const startedAt = Date.now();
2843
3088
  let finishDashboard = () => void 0;
2844
3089
  const dashboardClosed = new Promise((resolve) => {
2845
3090
  finishDashboard = resolve;
@@ -2860,7 +3105,8 @@ async function runConnectorDashboard(launchConfig, options = {}) {
2860
3105
  connectorId: config.connectorId,
2861
3106
  dashboardActive: false,
2862
3107
  plain: Boolean(options.plain),
2863
- serverUrl: config.serverUrl
3108
+ serverUrl: config.serverUrl,
3109
+ startedAt
2864
3110
  });
2865
3111
  if (!process.stdin.isTTY) {
2866
3112
  stopLogs2();
@@ -2887,7 +3133,8 @@ async function runConnectorDashboard(launchConfig, options = {}) {
2887
3133
  const stopLogs = streamDaemonLogs({
2888
3134
  dashboardActive: true,
2889
3135
  plain: false,
2890
- serverUrl: getServerUrl(launchConfig)
3136
+ serverUrl: getServerUrl(launchConfig),
3137
+ startedAt
2891
3138
  });
2892
3139
  let refreshing = false;
2893
3140
  const refresh = async () => {
@@ -3783,6 +4030,7 @@ var writeJournal = async (journalPath, journal) => {
3783
4030
  }
3784
4031
  };
3785
4032
  var syncDirectory2 = async (directory) => {
4033
+ if (process.platform === "win32") return;
3786
4034
  const handle = await open2(directory, constants2.O_RDONLY);
3787
4035
  try {
3788
4036
  await handle.sync();
@@ -4356,6 +4604,11 @@ import {
4356
4604
  writeFile as writeFile4
4357
4605
  } from "fs/promises";
4358
4606
  import path11 from "path";
4607
+
4608
+ // src/directory-link.ts
4609
+ var directoryLinkType = (platform4 = process.platform) => platform4 === "win32" ? "junction" : "dir";
4610
+
4611
+ // src/workspace-volume-lifecycle.ts
4359
4612
  var MOUNT_DIRECTORY = "mount";
4360
4613
  var ARCHIVE_MARKER = "archived.json";
4361
4614
  var AGENT_CONTEXT_TEMPLATE_FILES = [
@@ -4507,7 +4760,7 @@ var ensureVolumeLink = async (target, linkPath) => {
4507
4760
  } catch (error) {
4508
4761
  if (!isMissingFile(error)) throw error;
4509
4762
  try {
4510
- await symlink2(target, linkPath, "dir");
4763
+ await symlink2(target, linkPath, directoryLinkType());
4511
4764
  } catch (createError) {
4512
4765
  if (!isAlreadyExists3(createError)) throw createError;
4513
4766
  const info = await lstat2(linkPath);
@@ -4522,6 +4775,7 @@ var isAlreadyExists3 = (error) => error instanceof Error && "code" in error && e
4522
4775
  // src/relay.ts
4523
4776
  var RELAY_PING_INTERVAL_MS = 2e4;
4524
4777
  var RELAY_PONG_TIMEOUT_MS = 1e4;
4778
+ var RELAY_ACTIVE_REQUEST_PONG_TIMEOUT_LIMIT = 2;
4525
4779
  var RELAY_HANDSHAKE_TIMEOUT_MS = 1e4;
4526
4780
  var RELAY_RECONNECT_MAX_MS = 3e4;
4527
4781
  var RELAY_RECONNECT_JITTER = 0.2;
@@ -4627,6 +4881,7 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4627
4881
  socket = ws;
4628
4882
  const inflight = /* @__PURE__ */ new Map();
4629
4883
  currentInflight = inflight;
4884
+ let consecutivePongTimeouts = 0;
4630
4885
  let detached = false;
4631
4886
  const detach = () => {
4632
4887
  if (detached) return;
@@ -4639,6 +4894,7 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4639
4894
  void writeLog("info", `relay connected ${relayUrl}`);
4640
4895
  hadOpenConnection = true;
4641
4896
  reconnectDelayMs = RELAY_RECONNECT_INTERVAL_MS;
4897
+ consecutivePongTimeouts = 0;
4642
4898
  pingTimer = setInterval(() => {
4643
4899
  if (ws.readyState !== WebSocket2.OPEN) return;
4644
4900
  try {
@@ -4650,18 +4906,33 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4650
4906
  }
4651
4907
  if (pongTimer) clearTimeout(pongTimer);
4652
4908
  pongTimer = setTimeout(() => {
4909
+ pongTimer = null;
4910
+ consecutivePongTimeouts += 1;
4911
+ if (inflight.size > 0 && consecutivePongTimeouts < RELAY_ACTIVE_REQUEST_PONG_TIMEOUT_LIMIT) {
4912
+ void writeLog(
4913
+ "warn",
4914
+ `relay pong timeout deferred while ${inflight.size} request(s) are active`
4915
+ );
4916
+ return;
4917
+ }
4653
4918
  void writeLog("warn", "relay pong timeout, terminating");
4654
4919
  ws.terminate();
4655
4920
  }, RELAY_PONG_TIMEOUT_MS);
4656
4921
  }, RELAY_PING_INTERVAL_MS);
4657
4922
  });
4658
4923
  ws.on("pong", () => {
4924
+ consecutivePongTimeouts = 0;
4659
4925
  if (pongTimer) {
4660
4926
  clearTimeout(pongTimer);
4661
4927
  pongTimer = null;
4662
4928
  }
4663
4929
  });
4664
4930
  ws.on("message", (raw) => {
4931
+ consecutivePongTimeouts = 0;
4932
+ if (pongTimer) {
4933
+ clearTimeout(pongTimer);
4934
+ pongTimer = null;
4935
+ }
4665
4936
  void handleRelayMessage(
4666
4937
  ws,
4667
4938
  bunnyDaemon,
@@ -4837,7 +5108,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
4837
5108
  });
4838
5109
  executionIdentity = message.executionIdentity;
4839
5110
  } catch (error) {
4840
- sendRelayError(socket, message.requestId, error);
5111
+ await sendRelayError(socket, message.requestId, error).catch(() => void 0);
4841
5112
  return;
4842
5113
  }
4843
5114
  }
@@ -4923,7 +5194,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
4923
5194
  durationMs: Date.now() - codingRunStartedAt
4924
5195
  });
4925
5196
  }
4926
- sendRelayMessage(socket, {
5197
+ await sendRelayMessage(socket, {
4927
5198
  type: "start",
4928
5199
  requestId: message.requestId,
4929
5200
  status: response.status,
@@ -4943,7 +5214,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
4943
5214
  while (true) {
4944
5215
  const { value, done } = await reader.read();
4945
5216
  if (done) break;
4946
- sendRelayMessage(socket, {
5217
+ await sendRelayMessage(socket, {
4947
5218
  type: "chunk",
4948
5219
  requestId: message.requestId,
4949
5220
  data: binaryResponse ? Buffer.from(value).toString("base64") : decoder?.decode(value, { stream: true }) ?? "",
@@ -4952,7 +5223,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
4952
5223
  }
4953
5224
  const tail = decoder?.decode() ?? "";
4954
5225
  if (tail) {
4955
- sendRelayMessage(socket, {
5226
+ await sendRelayMessage(socket, {
4956
5227
  type: "chunk",
4957
5228
  requestId: message.requestId,
4958
5229
  data: tail
@@ -4963,7 +5234,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
4963
5234
  reader.releaseLock();
4964
5235
  }
4965
5236
  }
4966
- sendRelayMessage(socket, {
5237
+ await sendRelayMessage(socket, {
4967
5238
  type: "end",
4968
5239
  requestId: message.requestId
4969
5240
  });
@@ -4975,14 +5246,16 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
4975
5246
  });
4976
5247
  }
4977
5248
  } catch (error) {
5249
+ controller.abort();
4978
5250
  if (codingRunTrace && codingRunStartedAt !== void 0) {
4979
5251
  await writeLog("error", "relay request coding run error", {
4980
5252
  ...codingRunTrace,
4981
5253
  status: codingRunStatus,
4982
- durationMs: Date.now() - codingRunStartedAt
5254
+ durationMs: Date.now() - codingRunStartedAt,
5255
+ error: formatConnectorRelayError(error)
4983
5256
  });
4984
5257
  }
4985
- sendRelayError(socket, message.requestId, error);
5258
+ await sendRelayError(socket, message.requestId, error).catch(() => void 0);
4986
5259
  } finally {
4987
5260
  inflight.delete(message.requestId);
4988
5261
  }
@@ -5036,17 +5309,20 @@ var normalizeWorkspaceRequest = (request, mounts) => {
5036
5309
  if (!mount || relative === void 0) {
5037
5310
  throw new ConnectorRelayProtocolError("WORKSPACE_MISMATCH");
5038
5311
  }
5039
- const env = body.env && typeof body.env === "object" && !Array.isArray(body.env) ? body.env : {};
5312
+ const systemEnv = body.systemEnv && typeof body.systemEnv === "object" && !Array.isArray(body.systemEnv) ? body.systemEnv : {};
5313
+ const workspaceEnv = {
5314
+ BUDA_AGENT_ROOT: mounts.agent,
5315
+ BUDA_SPACE_ROOT: mounts.space,
5316
+ BUDA_SYSTEM_ROOT: mounts.system
5317
+ };
5040
5318
  return {
5041
5319
  ...request,
5042
5320
  body: {
5043
5321
  ...body,
5044
5322
  cwd: path12.join(mount, relative),
5045
- env: {
5046
- ...env,
5047
- BUDA_AGENT_ROOT: mounts.agent,
5048
- BUDA_SPACE_ROOT: mounts.space,
5049
- BUDA_SYSTEM_ROOT: mounts.system
5323
+ systemEnv: {
5324
+ ...systemEnv,
5325
+ ...workspaceEnv
5050
5326
  }
5051
5327
  }
5052
5328
  };
@@ -5078,17 +5354,24 @@ var normalizeConnectorFilesystemRequest = (request, mounts) => {
5078
5354
  }
5079
5355
  return request;
5080
5356
  };
5081
- function sendRelayError(socket, requestId, error) {
5082
- sendRelayMessage(socket, {
5357
+ async function sendRelayError(socket, requestId, error) {
5358
+ await sendRelayMessage(socket, {
5083
5359
  type: "error",
5084
5360
  requestId,
5085
5361
  error: error instanceof Error ? error.message : String(error),
5086
5362
  ...error instanceof ConnectorRelayProtocolError ? { code: error.code } : {}
5087
5363
  });
5088
5364
  }
5089
- function sendRelayMessage(socket, message) {
5090
- if (socket.readyState !== WebSocket2.OPEN) return;
5091
- socket.send(JSON.stringify(message));
5365
+ async function sendRelayMessage(socket, message) {
5366
+ if (socket.readyState !== WebSocket2.OPEN) {
5367
+ throw new Error(`Connector relay socket closed before ${message.type} could be sent.`);
5368
+ }
5369
+ await new Promise((resolve, reject) => {
5370
+ socket.send(JSON.stringify(message), (error) => {
5371
+ if (error) reject(error);
5372
+ else resolve();
5373
+ });
5374
+ });
5092
5375
  }
5093
5376
  function headersToRecord(headers) {
5094
5377
  const result = {};
@@ -5147,7 +5430,7 @@ async function ensureSymlink(target, linkPath) {
5147
5430
  } catch {
5148
5431
  }
5149
5432
  await mkdir10(path13.dirname(linkPath), { recursive: true });
5150
- await symlink3(target, linkPath, "dir");
5433
+ await symlink3(target, linkPath, directoryLinkType());
5151
5434
  }
5152
5435
 
5153
5436
  // src/workspace-registry.ts
@@ -5604,7 +5887,30 @@ import { mkdir as mkdir12 } from "fs/promises";
5604
5887
  import path15 from "path";
5605
5888
  var RECOVERED_DAEMON_STOP_TIMEOUT_MS = 1e4;
5606
5889
  var RECOVERED_DAEMON_STOP_POLL_MS = 100;
5890
+ var SHUTDOWN_FORCE_EXIT_MS = 1e4;
5891
+ var SHUTDOWN_STEP_TIMEOUT_MS = 2e3;
5607
5892
  var wait3 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
5893
+ var runShutdownStep = async (label, operation) => {
5894
+ let timeout;
5895
+ try {
5896
+ await Promise.race([
5897
+ Promise.resolve().then(operation),
5898
+ new Promise((_, reject) => {
5899
+ timeout = setTimeout(
5900
+ () => reject(new Error(`${label} timed out after ${SHUTDOWN_STEP_TIMEOUT_MS}ms`)),
5901
+ SHUTDOWN_STEP_TIMEOUT_MS
5902
+ );
5903
+ })
5904
+ ]);
5905
+ } catch (error) {
5906
+ await writeLog(
5907
+ "warn",
5908
+ `${label} failed during shutdown: ${error instanceof Error ? error.message : String(error)}`
5909
+ ).catch(() => void 0);
5910
+ } finally {
5911
+ if (timeout) clearTimeout(timeout);
5912
+ }
5913
+ };
5608
5914
  async function stopRecoveredIdleDaemon(config, configRoot) {
5609
5915
  const stopStatus = await requestDaemonControl(config, "stop-if-idle", configRoot).catch(
5610
5916
  () => null
@@ -5724,18 +6030,30 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5724
6030
  const shutdown = async (shutdownOptions = {}) => {
5725
6031
  if (shuttingDown) return;
5726
6032
  shuttingDown = true;
6033
+ const forceExit = options.exitOnShutdown === false ? void 0 : setTimeout(() => process.exit(0), SHUTDOWN_FORCE_EXIT_MS);
5727
6034
  if (interval) clearInterval(interval);
5728
6035
  relay.stop();
5729
6036
  try {
5730
- await closeAllBrowserSessions();
5731
- if (shutdownOptions.unregister !== false) {
5732
- await unregisterConnector(config, launchConfig);
5733
- }
5734
- await stopEmbeddedBunnyDaemon(activeBunnyDaemon);
6037
+ await Promise.all([
6038
+ runShutdownStep("browser session cleanup", closeAllBrowserSessions),
6039
+ ...shutdownOptions.unregister === false ? [] : [
6040
+ runShutdownStep(
6041
+ "connector unregister",
6042
+ () => unregisterConnector(config, launchConfig)
6043
+ )
6044
+ ],
6045
+ runShutdownStep(
6046
+ "embedded Bunny daemon shutdown",
6047
+ () => stopEmbeddedBunnyDaemon(activeBunnyDaemon)
6048
+ )
6049
+ ]);
5735
6050
  } finally {
5736
- await control?.close();
5737
- await daemonLock.release();
5738
- await restoreConsole?.();
6051
+ await Promise.all([
6052
+ runShutdownStep("control channel shutdown", async () => control?.close()),
6053
+ runShutdownStep("daemon lock release", () => daemonLock.release()),
6054
+ runShutdownStep("console capture restore", async () => restoreConsole?.())
6055
+ ]);
6056
+ if (forceExit) clearTimeout(forceExit);
5739
6057
  if (options.exitOnShutdown !== false) process.exit(0);
5740
6058
  }
5741
6059
  };
@@ -5750,8 +6068,11 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5750
6068
  });
5751
6069
  }).catch((error) => {
5752
6070
  heartbeatHealthy = false;
5753
- if (error instanceof ConnectorDeletedError) {
5754
- void writeLog("warn", "connector deleted remotely; stopping daemon");
6071
+ if (error instanceof ConnectorDeletedError || error instanceof ConnectorUnauthorizedError) {
6072
+ void writeLog(
6073
+ "warn",
6074
+ error instanceof ConnectorDeletedError ? "connector deleted remotely; stopping daemon" : "connector authentication rejected remotely; stopping daemon"
6075
+ );
5755
6076
  void shutdown({ unregister: false });
5756
6077
  return;
5757
6078
  }
@@ -5775,6 +6096,10 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5775
6096
  process.once("SIGINT", () => void shutdown());
5776
6097
  process.once("SIGTERM", () => void shutdown());
5777
6098
  } catch (error) {
6099
+ await writeLog(
6100
+ "error",
6101
+ `daemon startup failed: ${error instanceof Error ? error.stack || error.message : String(error)}`
6102
+ ).catch(() => void 0);
5778
6103
  if (bunnyDaemon) await stopEmbeddedBunnyDaemon(bunnyDaemon);
5779
6104
  await daemonLock.release();
5780
6105
  await restoreConsole?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buda-ai/connector",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -18,6 +18,7 @@
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",
21
22
  "start": "node dist/cli.js daemon",
22
23
  "build": "tsup",
23
24
  "test": "vitest run",
@@ -26,7 +27,7 @@
26
27
  "lint:err": "biome format . --write && biome check . --write --diagnostic-level=error"
27
28
  },
28
29
  "dependencies": {
29
- "@bunny-agent/daemon": "0.9.51",
30
+ "@bunny-agent/daemon": "0.9.58",
30
31
  "@clack/prompts": "^1.0.0",
31
32
  "playwright-core": "^1.52.0",
32
33
  "ws": "^8.21.0"