@buda-ai/connector 0.1.30 → 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.30",
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,126 +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 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;
2794
- var readMatchingPersistedDaemonStatus = async (config, configRoot = getConfigRoot()) => {
2795
- const status = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
2796
- return status && matchesDaemonIdentity(config, status) ? status : null;
2797
- };
2798
- var stopDaemonForIdentity = async (config, configRoot = getConfigRoot()) => {
2799
- const liveStatus = await readLiveStatus(config, configRoot);
2800
- if (liveStatus) {
2801
- await stopLiveDaemon(config, liveStatus, configRoot);
2802
- return;
2803
- }
2804
- const persistedStatus = await readMatchingPersistedDaemonStatus(config, configRoot);
2805
- if (!persistedStatus || !isProcessAlive2(persistedStatus.pid)) return;
2806
- const currentStatus = await readMatchingPersistedDaemonStatus(config, configRoot);
2807
- if (!currentStatus || currentStatus.pid !== persistedStatus.pid || currentStatus.instanceId !== persistedStatus.instanceId || !isProcessAlive2(currentStatus.pid)) {
2808
- return;
2809
- }
2810
- await stopUnresponsiveDaemonProcess(currentStatus.pid);
2811
- };
2812
- var shouldRestartUnhealthyDaemonForEnrollment = (status, explicitEnrollment) => explicitEnrollment && status.idle && status.heartbeatHealthy === false;
2813
- var readDaemonLockOwnerPid = async (config) => {
2814
- const lockPath = `${getConnectorDaemonLockTarget(
2815
- config.serverUrl,
2816
- config.connectorId,
2817
- getConfigRoot()
2818
- )}.lock`;
2819
- const raw = await readFile6(lockPath, "utf8").catch(() => "");
2820
- try {
2821
- const metadata = JSON.parse(raw);
2822
- const age = typeof metadata.createdAt === "number" && Number.isFinite(metadata.createdAt) ? Date.now() - metadata.createdAt : Number.POSITIVE_INFINITY;
2823
- return typeof metadata.pid === "number" && Number.isSafeInteger(metadata.pid) && metadata.pid > 0 && age >= 0 && age <= LOCK_ONLY_RECOVERY_MAX_AGE_MS ? metadata.pid : null;
2824
- } catch {
2825
- return null;
2826
- }
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();
2827
2625
  };
2828
- async function startDaemonWithRecovery(dependencies, maxAttempts = 2) {
2829
- let lastError;
2830
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
2831
- const spawned = await dependencies.spawnDaemon();
2832
- try {
2833
- return await dependencies.waitForReady();
2834
- } catch (error) {
2835
- lastError = error;
2836
- const lateReady = await dependencies.readReady().catch(() => null);
2837
- if (lateReady) return lateReady;
2838
- await dependencies.stopDaemon(spawned.pid);
2839
- if (attempt === maxAttempts) throw error;
2840
- }
2841
- }
2842
- throw lastError;
2843
- }
2844
- var startInternalDaemonWithRecovery = (launchConfig, mode, preparedRuntime, options = {}) => startDaemonWithRecovery({
2845
- readReady: () => readReadyDaemon(launchConfig),
2846
- spawnDaemon: () => spawnInternalDaemon(launchConfig, mode, preparedRuntime, options),
2847
- stopDaemon: stopUnresponsiveDaemonProcess,
2848
- waitForReady: () => waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS)
2849
- });
2850
2626
  var stopOlderIdleDaemon = async (config, status) => {
2851
2627
  if (!shouldReplaceDaemonVersion(status.version, CONNECTOR_RUNTIME_VERSION)) return false;
2852
2628
  if (!status.idle) return false;
@@ -2873,19 +2649,14 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2873
2649
  if (existingConfig) {
2874
2650
  const existingStatus = await readLiveStatus(existingConfig);
2875
2651
  if (existingStatus) {
2876
- if (shouldRestartUnhealthyDaemonForEnrollment(
2877
- existingStatus,
2878
- Boolean(options.explicitEnrollment)
2879
- ) && await stopLiveDaemon(existingConfig, existingStatus, getConfigRoot(), "stop-if-idle")) {
2880
- return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
2881
- }
2882
2652
  const preparedRuntime = !shouldReplaceDaemonVersion(existingStatus.version, CONNECTOR_RUNTIME_VERSION) || !existingStatus.idle ? void 0 : await ensureStableConnectorRuntime();
2883
2653
  const replaced = await stopOlderIdleDaemon(existingConfig, existingStatus);
2884
2654
  if (!replaced) {
2885
2655
  const status = mode === "persistent" ? await requestDaemonControl(existingConfig, "persist", getConfigRoot()) : existingStatus;
2886
2656
  return { config: existingConfig, status };
2887
2657
  }
2888
- return await startInternalDaemonWithRecovery(launchConfig, mode, preparedRuntime, options);
2658
+ await spawnInternalDaemon(launchConfig, mode, preparedRuntime, options);
2659
+ return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2889
2660
  }
2890
2661
  const persistedStatus = await readPersistedDaemonStatus(
2891
2662
  existingConfig,
@@ -2896,12 +2667,9 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2896
2667
  `Connector daemon PID ${persistedStatus.pid} is running but its local control channel is not responding. Stop that process and retry.`
2897
2668
  );
2898
2669
  }
2899
- const lockOwnerPid = await readDaemonLockOwnerPid(existingConfig);
2900
- if (lockOwnerPid && isProcessAlive2(lockOwnerPid)) {
2901
- await stopUnresponsiveDaemonProcess(lockOwnerPid);
2902
- }
2903
2670
  }
2904
- 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);
2905
2673
  } finally {
2906
2674
  await bootstrapLock.release();
2907
2675
  }
@@ -2921,7 +2689,6 @@ var streamDaemonLogs = (options) => {
2921
2689
  for (const line of lines.slice(seenLines)) {
2922
2690
  try {
2923
2691
  const entry = JSON.parse(line);
2924
- if (!shouldDisplayConnectorLogEntry(entry.createdAt, options.startedAt)) continue;
2925
2692
  const connectorId = entry.metadata?.connectorId;
2926
2693
  if (options.dashboardActive && typeof connectorId === "string") {
2927
2694
  const output2 = entry.metadata?.source === "daemon-console" ? entry.message : `[buda-connector] ${entry.message}`;
@@ -2997,7 +2764,9 @@ async function stopLocalConnectorDaemon(connectorId, serverUrl, configRoot = get
2997
2764
  (candidate) => candidate.connectorId === connectorId && normalizeServerOrigin(candidate.serverUrl) === serverOrigin
2998
2765
  );
2999
2766
  if (!config) throw new Error(`Local Connector ${connectorId} was not found`);
3000
- await stopDaemonForIdentity(config, configRoot);
2767
+ const status = await readLiveStatus(config, configRoot);
2768
+ if (!status) return;
2769
+ await requestDaemonControl(config, "stop", configRoot);
3001
2770
  }
3002
2771
  async function stopAllLocalConnectorDaemons(configRoot = getConfigRoot()) {
3003
2772
  const configs = await listDeviceConfigs(configRoot);
@@ -3008,7 +2777,10 @@ async function stopAllLocalConnectorDaemons(configRoot = getConfigRoot()) {
3008
2777
  ${config.connectorId}`, config);
3009
2778
  }
3010
2779
  const results = await Promise.allSettled(
3011
- [...identities.values()].map((config) => stopDaemonForIdentity(config, configRoot))
2780
+ [...identities.values()].map(async (config) => {
2781
+ const status = await readLiveStatus(config, configRoot);
2782
+ if (status) await requestDaemonControl(config, "stop", configRoot);
2783
+ })
3012
2784
  );
3013
2785
  const failures = results.filter((result) => result.status === "rejected");
3014
2786
  if (failures.length) {
@@ -3027,7 +2799,6 @@ var waitForConnectorViewerExit = async (dashboardClosed) => {
3027
2799
  process.off("SIGTERM", finishSignal);
3028
2800
  };
3029
2801
  async function runConnectorDashboard(launchConfig, options = {}) {
3030
- const startedAt = Date.now();
3031
2802
  let finishDashboard = () => void 0;
3032
2803
  const dashboardClosed = new Promise((resolve) => {
3033
2804
  finishDashboard = resolve;
@@ -3048,8 +2819,7 @@ async function runConnectorDashboard(launchConfig, options = {}) {
3048
2819
  connectorId: config.connectorId,
3049
2820
  dashboardActive: false,
3050
2821
  plain: Boolean(options.plain),
3051
- serverUrl: config.serverUrl,
3052
- startedAt
2822
+ serverUrl: config.serverUrl
3053
2823
  });
3054
2824
  if (!process.stdin.isTTY) {
3055
2825
  stopLogs2();
@@ -3076,8 +2846,7 @@ async function runConnectorDashboard(launchConfig, options = {}) {
3076
2846
  const stopLogs = streamDaemonLogs({
3077
2847
  dashboardActive: true,
3078
2848
  plain: false,
3079
- serverUrl: getServerUrl(launchConfig),
3080
- startedAt
2849
+ serverUrl: getServerUrl(launchConfig)
3081
2850
  });
3082
2851
  let refreshing = false;
3083
2852
  const refresh = async () => {
@@ -3622,7 +3391,7 @@ import {
3622
3391
  lstat,
3623
3392
  mkdir as mkdir8,
3624
3393
  open as open2,
3625
- readdir as readdir4,
3394
+ readdir as readdir5,
3626
3395
  readFile as readFile7,
3627
3396
  readlink,
3628
3397
  realpath,
@@ -3807,7 +3576,7 @@ var createJournal = async (storageRoot, migrationDirectory, owner) => {
3807
3576
  };
3808
3577
  };
3809
3578
  var inventoryLegacySources = async (storageRoot) => {
3810
- const rootEntries = await readdir4(storageRoot, { withFileTypes: true });
3579
+ const rootEntries = await readdir5(storageRoot, { withFileTypes: true });
3811
3580
  const templateNames = new Set(LEGACY_TEMPLATE_FILES);
3812
3581
  for (const entry of rootEntries) {
3813
3582
  if (entry.name.endsWith(".md")) templateNames.add(entry.name);
@@ -3837,7 +3606,7 @@ var inventoryLegacySources = async (storageRoot) => {
3837
3606
  `Legacy migration source ${candidate.id} must not be a symbolic link`
3838
3607
  );
3839
3608
  }
3840
- if (info.isDirectory() && (await readdir4(candidate.source)).length === 0) continue;
3609
+ if (info.isDirectory() && (await readdir5(candidate.source)).length === 0) continue;
3841
3610
  found.push(candidate);
3842
3611
  }
3843
3612
  return found;
@@ -3857,7 +3626,7 @@ var copyTree = async (source, destination, onEntryCopied) => {
3857
3626
  const info = await lstat(source);
3858
3627
  if (info.isDirectory()) {
3859
3628
  await mkdir8(destination, { mode: info.mode & 4095 });
3860
- const entries = (await readdir4(source)).sort();
3629
+ const entries = (await readdir5(source)).sort();
3861
3630
  for (const entry of entries) {
3862
3631
  await copyTree(path9.join(source, entry), path9.join(destination, entry), onEntryCopied);
3863
3632
  }
@@ -3894,8 +3663,8 @@ var verifyTree = async (source, destination) => {
3894
3663
  }
3895
3664
  if (sourceInfo.isDirectory()) {
3896
3665
  const [sourceEntries, destinationEntries] = await Promise.all([
3897
- readdir4(source),
3898
- readdir4(destination)
3666
+ readdir5(source),
3667
+ readdir5(destination)
3899
3668
  ]);
3900
3669
  sourceEntries.sort();
3901
3670
  destinationEntries.sort();
@@ -3973,7 +3742,6 @@ var writeJournal = async (journalPath, journal) => {
3973
3742
  }
3974
3743
  };
3975
3744
  var syncDirectory2 = async (directory) => {
3976
- if (process.platform === "win32") return;
3977
3745
  const handle = await open2(directory, constants2.O_RDONLY);
3978
3746
  try {
3979
3747
  await handle.sync();
@@ -4539,7 +4307,7 @@ import {
4539
4307
  copyFile as copyFile2,
4540
4308
  lstat as lstat2,
4541
4309
  mkdir as mkdir9,
4542
- readdir as readdir5,
4310
+ readdir as readdir6,
4543
4311
  readFile as readFile9,
4544
4312
  realpath as realpath3,
4545
4313
  rm as rm3,
@@ -4547,11 +4315,6 @@ import {
4547
4315
  writeFile as writeFile4
4548
4316
  } from "fs/promises";
4549
4317
  import path11 from "path";
4550
-
4551
- // src/directory-link.ts
4552
- var directoryLinkType = (platform4 = process.platform) => platform4 === "win32" ? "junction" : "dir";
4553
-
4554
- // src/workspace-volume-lifecycle.ts
4555
4318
  var MOUNT_DIRECTORY = "mount";
4556
4319
  var ARCHIVE_MARKER = "archived.json";
4557
4320
  var AGENT_CONTEXT_TEMPLATE_FILES = [
@@ -4649,7 +4412,7 @@ var WorkspaceVolumeLifecycle = class {
4649
4412
  const bootstrapped = [];
4650
4413
  const skipped = [];
4651
4414
  const entries = new Set(
4652
- (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)
4653
4416
  );
4654
4417
  for (const filename of AGENT_CONTEXT_TEMPLATE_FILES) {
4655
4418
  if (!entries.has(filename)) continue;
@@ -4703,7 +4466,7 @@ var ensureVolumeLink = async (target, linkPath) => {
4703
4466
  } catch (error) {
4704
4467
  if (!isMissingFile(error)) throw error;
4705
4468
  try {
4706
- await symlink2(target, linkPath, directoryLinkType());
4469
+ await symlink2(target, linkPath, "dir");
4707
4470
  } catch (createError) {
4708
4471
  if (!isAlreadyExists3(createError)) throw createError;
4709
4472
  const info = await lstat2(linkPath);
@@ -4718,7 +4481,6 @@ var isAlreadyExists3 = (error) => error instanceof Error && "code" in error && e
4718
4481
  // src/relay.ts
4719
4482
  var RELAY_PING_INTERVAL_MS = 2e4;
4720
4483
  var RELAY_PONG_TIMEOUT_MS = 1e4;
4721
- var RELAY_ACTIVE_REQUEST_PONG_TIMEOUT_LIMIT = 2;
4722
4484
  var RELAY_HANDSHAKE_TIMEOUT_MS = 1e4;
4723
4485
  var RELAY_RECONNECT_MAX_MS = 3e4;
4724
4486
  var RELAY_RECONNECT_JITTER = 0.2;
@@ -4824,7 +4586,6 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4824
4586
  socket = ws;
4825
4587
  const inflight = /* @__PURE__ */ new Map();
4826
4588
  currentInflight = inflight;
4827
- let consecutivePongTimeouts = 0;
4828
4589
  let detached = false;
4829
4590
  const detach = () => {
4830
4591
  if (detached) return;
@@ -4837,7 +4598,6 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4837
4598
  void writeLog("info", `relay connected ${relayUrl}`);
4838
4599
  hadOpenConnection = true;
4839
4600
  reconnectDelayMs = RELAY_RECONNECT_INTERVAL_MS;
4840
- consecutivePongTimeouts = 0;
4841
4601
  pingTimer = setInterval(() => {
4842
4602
  if (ws.readyState !== WebSocket2.OPEN) return;
4843
4603
  try {
@@ -4849,33 +4609,18 @@ function startConnectorRelay(config, bunnyDaemon, relayProtocol, workspaceRegist
4849
4609
  }
4850
4610
  if (pongTimer) clearTimeout(pongTimer);
4851
4611
  pongTimer = setTimeout(() => {
4852
- pongTimer = null;
4853
- consecutivePongTimeouts += 1;
4854
- if (inflight.size > 0 && consecutivePongTimeouts < RELAY_ACTIVE_REQUEST_PONG_TIMEOUT_LIMIT) {
4855
- void writeLog(
4856
- "warn",
4857
- `relay pong timeout deferred while ${inflight.size} request(s) are active`
4858
- );
4859
- return;
4860
- }
4861
4612
  void writeLog("warn", "relay pong timeout, terminating");
4862
4613
  ws.terminate();
4863
4614
  }, RELAY_PONG_TIMEOUT_MS);
4864
4615
  }, RELAY_PING_INTERVAL_MS);
4865
4616
  });
4866
4617
  ws.on("pong", () => {
4867
- consecutivePongTimeouts = 0;
4868
4618
  if (pongTimer) {
4869
4619
  clearTimeout(pongTimer);
4870
4620
  pongTimer = null;
4871
4621
  }
4872
4622
  });
4873
4623
  ws.on("message", (raw) => {
4874
- consecutivePongTimeouts = 0;
4875
- if (pongTimer) {
4876
- clearTimeout(pongTimer);
4877
- pongTimer = null;
4878
- }
4879
4624
  void handleRelayMessage(
4880
4625
  ws,
4881
4626
  bunnyDaemon,
@@ -5051,7 +4796,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5051
4796
  });
5052
4797
  executionIdentity = message.executionIdentity;
5053
4798
  } catch (error) {
5054
- await sendRelayError(socket, message.requestId, error).catch(() => void 0);
4799
+ sendRelayError(socket, message.requestId, error);
5055
4800
  return;
5056
4801
  }
5057
4802
  }
@@ -5137,7 +4882,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5137
4882
  durationMs: Date.now() - codingRunStartedAt
5138
4883
  });
5139
4884
  }
5140
- await sendRelayMessage(socket, {
4885
+ sendRelayMessage(socket, {
5141
4886
  type: "start",
5142
4887
  requestId: message.requestId,
5143
4888
  status: response.status,
@@ -5157,7 +4902,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5157
4902
  while (true) {
5158
4903
  const { value, done } = await reader.read();
5159
4904
  if (done) break;
5160
- await sendRelayMessage(socket, {
4905
+ sendRelayMessage(socket, {
5161
4906
  type: "chunk",
5162
4907
  requestId: message.requestId,
5163
4908
  data: binaryResponse ? Buffer.from(value).toString("base64") : decoder?.decode(value, { stream: true }) ?? "",
@@ -5166,7 +4911,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5166
4911
  }
5167
4912
  const tail = decoder?.decode() ?? "";
5168
4913
  if (tail) {
5169
- await sendRelayMessage(socket, {
4914
+ sendRelayMessage(socket, {
5170
4915
  type: "chunk",
5171
4916
  requestId: message.requestId,
5172
4917
  data: tail
@@ -5177,7 +4922,7 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5177
4922
  reader.releaseLock();
5178
4923
  }
5179
4924
  }
5180
- await sendRelayMessage(socket, {
4925
+ sendRelayMessage(socket, {
5181
4926
  type: "end",
5182
4927
  requestId: message.requestId
5183
4928
  });
@@ -5189,16 +4934,14 @@ async function handleRelayMessage(socket, bunnyDaemon, raw, inflight, expectedCo
5189
4934
  });
5190
4935
  }
5191
4936
  } catch (error) {
5192
- controller.abort();
5193
4937
  if (codingRunTrace && codingRunStartedAt !== void 0) {
5194
4938
  await writeLog("error", "relay request coding run error", {
5195
4939
  ...codingRunTrace,
5196
4940
  status: codingRunStatus,
5197
- durationMs: Date.now() - codingRunStartedAt,
5198
- error: formatConnectorRelayError(error)
4941
+ durationMs: Date.now() - codingRunStartedAt
5199
4942
  });
5200
4943
  }
5201
- await sendRelayError(socket, message.requestId, error).catch(() => void 0);
4944
+ sendRelayError(socket, message.requestId, error);
5202
4945
  } finally {
5203
4946
  inflight.delete(message.requestId);
5204
4947
  }
@@ -5252,20 +4995,17 @@ var normalizeWorkspaceRequest = (request, mounts) => {
5252
4995
  if (!mount || relative === void 0) {
5253
4996
  throw new ConnectorRelayProtocolError("WORKSPACE_MISMATCH");
5254
4997
  }
5255
- const systemEnv = body.systemEnv && typeof body.systemEnv === "object" && !Array.isArray(body.systemEnv) ? body.systemEnv : {};
5256
- const workspaceEnv = {
5257
- BUDA_AGENT_ROOT: mounts.agent,
5258
- BUDA_SPACE_ROOT: mounts.space,
5259
- BUDA_SYSTEM_ROOT: mounts.system
5260
- };
4998
+ const env = body.env && typeof body.env === "object" && !Array.isArray(body.env) ? body.env : {};
5261
4999
  return {
5262
5000
  ...request,
5263
5001
  body: {
5264
5002
  ...body,
5265
5003
  cwd: path12.join(mount, relative),
5266
- systemEnv: {
5267
- ...systemEnv,
5268
- ...workspaceEnv
5004
+ env: {
5005
+ ...env,
5006
+ BUDA_AGENT_ROOT: mounts.agent,
5007
+ BUDA_SPACE_ROOT: mounts.space,
5008
+ BUDA_SYSTEM_ROOT: mounts.system
5269
5009
  }
5270
5010
  }
5271
5011
  };
@@ -5297,24 +5037,17 @@ var normalizeConnectorFilesystemRequest = (request, mounts) => {
5297
5037
  }
5298
5038
  return request;
5299
5039
  };
5300
- async function sendRelayError(socket, requestId, error) {
5301
- await sendRelayMessage(socket, {
5040
+ function sendRelayError(socket, requestId, error) {
5041
+ sendRelayMessage(socket, {
5302
5042
  type: "error",
5303
5043
  requestId,
5304
5044
  error: error instanceof Error ? error.message : String(error),
5305
5045
  ...error instanceof ConnectorRelayProtocolError ? { code: error.code } : {}
5306
5046
  });
5307
5047
  }
5308
- async function sendRelayMessage(socket, message) {
5309
- if (socket.readyState !== WebSocket2.OPEN) {
5310
- throw new Error(`Connector relay socket closed before ${message.type} could be sent.`);
5311
- }
5312
- await new Promise((resolve, reject) => {
5313
- socket.send(JSON.stringify(message), (error) => {
5314
- if (error) reject(error);
5315
- else resolve();
5316
- });
5317
- });
5048
+ function sendRelayMessage(socket, message) {
5049
+ if (socket.readyState !== WebSocket2.OPEN) return;
5050
+ socket.send(JSON.stringify(message));
5318
5051
  }
5319
5052
  function headersToRecord(headers) {
5320
5053
  const result = {};
@@ -5373,7 +5106,7 @@ async function ensureSymlink(target, linkPath) {
5373
5106
  } catch {
5374
5107
  }
5375
5108
  await mkdir10(path13.dirname(linkPath), { recursive: true });
5376
- await symlink3(target, linkPath, directoryLinkType());
5109
+ await symlink3(target, linkPath, "dir");
5377
5110
  }
5378
5111
 
5379
5112
  // src/workspace-registry.ts
@@ -5830,30 +5563,7 @@ import { mkdir as mkdir12 } from "fs/promises";
5830
5563
  import path15 from "path";
5831
5564
  var RECOVERED_DAEMON_STOP_TIMEOUT_MS = 1e4;
5832
5565
  var RECOVERED_DAEMON_STOP_POLL_MS = 100;
5833
- var SHUTDOWN_FORCE_EXIT_MS = 1e4;
5834
- var SHUTDOWN_STEP_TIMEOUT_MS = 2e3;
5835
5566
  var wait3 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
5836
- var runShutdownStep = async (label, operation) => {
5837
- let timeout;
5838
- try {
5839
- await Promise.race([
5840
- Promise.resolve().then(operation),
5841
- new Promise((_, reject) => {
5842
- timeout = setTimeout(
5843
- () => reject(new Error(`${label} timed out after ${SHUTDOWN_STEP_TIMEOUT_MS}ms`)),
5844
- SHUTDOWN_STEP_TIMEOUT_MS
5845
- );
5846
- })
5847
- ]);
5848
- } catch (error) {
5849
- await writeLog(
5850
- "warn",
5851
- `${label} failed during shutdown: ${error instanceof Error ? error.message : String(error)}`
5852
- ).catch(() => void 0);
5853
- } finally {
5854
- if (timeout) clearTimeout(timeout);
5855
- }
5856
- };
5857
5567
  async function stopRecoveredIdleDaemon(config, configRoot) {
5858
5568
  const stopStatus = await requestDaemonControl(config, "stop-if-idle", configRoot).catch(
5859
5569
  () => null
@@ -5973,30 +5683,18 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5973
5683
  const shutdown = async (shutdownOptions = {}) => {
5974
5684
  if (shuttingDown) return;
5975
5685
  shuttingDown = true;
5976
- const forceExit = options.exitOnShutdown === false ? void 0 : setTimeout(() => process.exit(0), SHUTDOWN_FORCE_EXIT_MS);
5977
5686
  if (interval) clearInterval(interval);
5978
5687
  relay.stop();
5979
5688
  try {
5980
- await Promise.all([
5981
- runShutdownStep("browser session cleanup", closeAllBrowserSessions),
5982
- ...shutdownOptions.unregister === false ? [] : [
5983
- runShutdownStep(
5984
- "connector unregister",
5985
- () => unregisterConnector(config, launchConfig)
5986
- )
5987
- ],
5988
- runShutdownStep(
5989
- "embedded Bunny daemon shutdown",
5990
- () => stopEmbeddedBunnyDaemon(activeBunnyDaemon)
5991
- )
5992
- ]);
5689
+ await closeAllBrowserSessions();
5690
+ if (shutdownOptions.unregister !== false) {
5691
+ await unregisterConnector(config, launchConfig);
5692
+ }
5693
+ await stopEmbeddedBunnyDaemon(activeBunnyDaemon);
5993
5694
  } finally {
5994
- await Promise.all([
5995
- runShutdownStep("control channel shutdown", async () => control?.close()),
5996
- runShutdownStep("daemon lock release", () => daemonLock.release()),
5997
- runShutdownStep("console capture restore", async () => restoreConsole?.())
5998
- ]);
5999
- if (forceExit) clearTimeout(forceExit);
5695
+ await control?.close();
5696
+ await daemonLock.release();
5697
+ await restoreConsole?.();
6000
5698
  if (options.exitOnShutdown !== false) process.exit(0);
6001
5699
  }
6002
5700
  };
@@ -6011,11 +5709,8 @@ async function runConnectorDaemon(launchConfig, options = {}) {
6011
5709
  });
6012
5710
  }).catch((error) => {
6013
5711
  heartbeatHealthy = false;
6014
- if (error instanceof ConnectorDeletedError || error instanceof ConnectorUnauthorizedError) {
6015
- void writeLog(
6016
- "warn",
6017
- error instanceof ConnectorDeletedError ? "connector deleted remotely; stopping daemon" : "connector authentication rejected remotely; stopping daemon"
6018
- );
5712
+ if (error instanceof ConnectorDeletedError) {
5713
+ void writeLog("warn", "connector deleted remotely; stopping daemon");
6019
5714
  void shutdown({ unregister: false });
6020
5715
  return;
6021
5716
  }
@@ -6039,10 +5734,6 @@ async function runConnectorDaemon(launchConfig, options = {}) {
6039
5734
  process.once("SIGINT", () => void shutdown());
6040
5735
  process.once("SIGTERM", () => void shutdown());
6041
5736
  } catch (error) {
6042
- await writeLog(
6043
- "error",
6044
- `daemon startup failed: ${error instanceof Error ? error.stack || error.message : String(error)}`
6045
- ).catch(() => void 0);
6046
5737
  if (bunnyDaemon) await stopEmbeddedBunnyDaemon(bunnyDaemon);
6047
5738
  await daemonLock.release();
6048
5739
  await restoreConsole?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buda-ai/connector",
3
- "version": "0.1.30",
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"