bitfab-cli 0.2.96 → 0.2.98

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.
Files changed (2) hide show
  1. package/dist/index.js +75 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9834,7 +9834,7 @@ async function createStudioSession({ serviceUrl, apiKey, sessionId, initialPath
9834
9834
  import net2 from "net";
9835
9835
 
9836
9836
  // ../bitfab-plugin-lib/dist/daemon/ensure.js
9837
- import { fork } from "child_process";
9837
+ import { execFileSync as execFileSync4, fork } from "child_process";
9838
9838
  import crypto5 from "crypto";
9839
9839
  import fs9 from "fs";
9840
9840
  import net from "net";
@@ -9921,6 +9921,21 @@ async function waitForSocket(socketPath, timeoutMs) {
9921
9921
  }
9922
9922
  throw new Error(`daemon did not become ready within ${timeoutMs}ms`);
9923
9923
  }
9924
+ async function waitForLockHolderDaemon(socketPath, timeoutMs, localBuild = localBuildFingerprint()) {
9925
+ const deadline = Date.now() + timeoutMs;
9926
+ while (Date.now() < deadline) {
9927
+ const ping = await pingSocket(socketPath);
9928
+ if (ping.alive && (localBuild === "unknown" || !ping.build || ping.build === localBuild)) {
9929
+ return;
9930
+ }
9931
+ await new Promise((r) => setTimeout(r, ENSURE_POLL_INTERVAL_MS));
9932
+ }
9933
+ const finalPing = await pingSocket(socketPath);
9934
+ if (finalPing.alive) {
9935
+ return;
9936
+ }
9937
+ throw new Error(`daemon did not become ready within ${timeoutMs}ms`);
9938
+ }
9924
9939
  function localBuildFingerprint() {
9925
9940
  try {
9926
9941
  const thisDir = path8.dirname(fileURLToPath2(import.meta.url));
@@ -9931,17 +9946,63 @@ function localBuildFingerprint() {
9931
9946
  return "unknown";
9932
9947
  }
9933
9948
  }
9934
- async function killDaemon(pidPath, socketPath) {
9935
- const pid = readPid(pidPath);
9936
- if (pid !== null) {
9949
+ function findDaemonPids(socketPath) {
9950
+ let out;
9951
+ try {
9952
+ out = execFileSync4("ps", ["-axww", "-o", "pid=,command="], {
9953
+ encoding: "utf-8"
9954
+ });
9955
+ } catch {
9956
+ return [];
9957
+ }
9958
+ const pids = [];
9959
+ for (const line of out.split("\n")) {
9960
+ const match = line.trim().match(/^(\d+)\s+(.*)$/);
9961
+ if (!match) {
9962
+ continue;
9963
+ }
9964
+ const pid = Number.parseInt(match[1], 10);
9965
+ const command = match[2];
9966
+ if (pid === process.pid || Number.isNaN(pid)) {
9967
+ continue;
9968
+ }
9969
+ if (command.includes("daemon/main.js") && commandTargetsSocket(command, socketPath)) {
9970
+ pids.push(pid);
9971
+ }
9972
+ }
9973
+ return pids;
9974
+ }
9975
+ function commandTargetsSocket(command, socketPath) {
9976
+ let idx = command.indexOf(socketPath);
9977
+ while (idx !== -1) {
9978
+ const after = command[idx + socketPath.length];
9979
+ if (after === void 0 || after === " ") {
9980
+ return true;
9981
+ }
9982
+ idx = command.indexOf(socketPath, idx + 1);
9983
+ }
9984
+ return false;
9985
+ }
9986
+ async function reapAllDaemons(socketPath, pidPath) {
9987
+ const pids = new Set(findDaemonPids(socketPath));
9988
+ const pidfilePid = readPid(pidPath);
9989
+ if (pidfilePid !== null) {
9990
+ pids.add(pidfilePid);
9991
+ }
9992
+ for (const pid of pids) {
9937
9993
  try {
9938
9994
  process.kill(pid, "SIGTERM");
9939
9995
  } catch {
9940
9996
  }
9941
- const deadline = Date.now() + 5e3;
9942
- while (processIsAlive(pid) && Date.now() < deadline) {
9943
- await new Promise((r) => setTimeout(r, 50));
9997
+ }
9998
+ const deadline = Date.now() + 5e3;
9999
+ while (Date.now() < deadline) {
10000
+ if (![...pids].some(processIsAlive)) {
10001
+ break;
9944
10002
  }
10003
+ await new Promise((r) => setTimeout(r, 50));
10004
+ }
10005
+ for (const pid of pids) {
9945
10006
  if (processIsAlive(pid)) {
9946
10007
  try {
9947
10008
  process.kill(pid, "SIGKILL");
@@ -10015,18 +10076,17 @@ async function ensureDaemon(socketPath = SOCKET_PATH, pidPath = PID_FILE_PATH) {
10015
10076
  const localBuild = localBuildFingerprint();
10016
10077
  if (ping.build && ping.build !== localBuild && localBuild !== "unknown") {
10017
10078
  console.error(`[daemon] build mismatch (running: ${ping.build}, local: ${localBuild}), restarting`);
10018
- await killDaemon(pidPath, socketPath);
10019
10079
  } else {
10020
10080
  return;
10021
10081
  }
10022
10082
  }
10023
10083
  }
10024
10084
  if (!acquireLock(socketPath)) {
10025
- await waitForSocket(socketPath, ENSURE_TIMEOUT_MS);
10085
+ await waitForLockHolderDaemon(socketPath, ENSURE_TIMEOUT_MS);
10026
10086
  return;
10027
10087
  }
10028
10088
  try {
10029
- cleanupStaleFiles(socketPath, pidPath);
10089
+ await reapAllDaemons(socketPath, pidPath);
10030
10090
  spawnDaemon(socketPath, pidPath);
10031
10091
  await waitForSocket(socketPath, ENSURE_TIMEOUT_MS);
10032
10092
  } finally {
@@ -10453,13 +10513,13 @@ async function reportHandoff(apiKey, pluginVersion, platform2, body) {
10453
10513
  import crypto7 from "crypto";
10454
10514
 
10455
10515
  // ../bitfab-plugin-lib/dist/frontmostApp.js
10456
- import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
10516
+ import { execFileSync as execFileSync5, spawn as spawn2 } from "child_process";
10457
10517
  import os10 from "os";
10458
10518
  function findAncestorApp() {
10459
10519
  try {
10460
10520
  let pid = process.ppid;
10461
10521
  while (pid > 1) {
10462
- const output = execFileSync4("ps", ["-o", "ppid=,comm=", "-p", String(pid)], { stdio: "pipe", encoding: "utf-8" }).trim();
10522
+ const output = execFileSync5("ps", ["-o", "ppid=,comm=", "-p", String(pid)], { stdio: "pipe", encoding: "utf-8" }).trim();
10463
10523
  const match = output.match(/^\s*(\d+)\s+(.+)$/);
10464
10524
  if (!match) {
10465
10525
  break;
@@ -10518,19 +10578,19 @@ function getFrontmostApp() {
10518
10578
  const platform2 = os10.platform();
10519
10579
  try {
10520
10580
  if (platform2 === "darwin") {
10521
- return execFileSync4("osascript", [
10581
+ return execFileSync5("osascript", [
10522
10582
  "-e",
10523
10583
  'tell application "System Events" to get name of first process whose frontmost is true'
10524
10584
  ], { stdio: "pipe", encoding: "utf-8" }).trim();
10525
10585
  }
10526
10586
  if (platform2 === "linux") {
10527
- return execFileSync4("xdotool", ["getactivewindow"], {
10587
+ return execFileSync5("xdotool", ["getactivewindow"], {
10528
10588
  stdio: "pipe",
10529
10589
  encoding: "utf-8"
10530
10590
  }).trim();
10531
10591
  }
10532
10592
  if (platform2 === "win32") {
10533
- return execFileSync4("powershell.exe", [
10593
+ return execFileSync5("powershell.exe", [
10534
10594
  "-NoProfile",
10535
10595
  "-Command",
10536
10596
  `Add-Type -MemberDefinition '[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();' -Name WinFocus -Namespace Bitfab; [Bitfab.WinFocus]::GetForegroundWindow().ToInt64()`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitfab-cli",
3
- "version": "0.2.96",
3
+ "version": "0.2.98",
4
4
  "description": "Install and configure the Bitfab plugin in Claude Code, Codex, or Cursor.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",