bitfab-cli 0.2.87 → 0.2.89

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 +163 -82
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9398,77 +9398,8 @@ function updateActiveStudioSession(updates) {
9398
9398
  return updated;
9399
9399
  }
9400
9400
 
9401
- // ../bitfab-plugin-lib/dist/parseArgs.js
9402
- function formatPositionalLabel(spec) {
9403
- const tag = spec.format && spec.format !== "string" ? `:${spec.format}` : "";
9404
- if (spec.variadic) {
9405
- return spec.required !== false ? `<${spec.name}${tag}> [<${spec.name}${tag}>...]` : `[<${spec.name}${tag}>...]`;
9406
- }
9407
- return spec.required !== false ? `<${spec.name}${tag}>` : `[${spec.name}${tag}]`;
9408
- }
9409
- function formatFlagLabel(spec) {
9410
- if (spec.boolean) {
9411
- return `[--${spec.name}]`;
9412
- }
9413
- const tag = spec.format && spec.format !== "string" ? `:${spec.format}` : "";
9414
- const valueHint = spec.format === "number" ? " N" : ` <${spec.name}${tag}>`;
9415
- const defaultHint = spec.default !== void 0 ? ` (default: ${spec.default})` : "";
9416
- return `[--${spec.name}${valueHint}${defaultHint}]`;
9417
- }
9418
- function buildUsage(command, positional, flags) {
9419
- const parts = [
9420
- `Usage: ${command}`,
9421
- ...positional.map(formatPositionalLabel),
9422
- ...(flags ?? []).map(formatFlagLabel)
9423
- ];
9424
- return parts.join(" ");
9425
- }
9426
- function buildHelp(command, positional, flags, description) {
9427
- const lines = [buildUsage(command, positional, flags)];
9428
- if (description) {
9429
- lines.push("", description);
9430
- }
9431
- const describedArgs = positional.filter((s) => s.description);
9432
- const flagSpecs = flags ?? [];
9433
- const labelWidth = Math.max(0, ...describedArgs.map((s) => s.name.length), ...flagSpecs.map((s) => s.name.length + 2));
9434
- if (describedArgs.length > 0) {
9435
- lines.push("", "Arguments:");
9436
- for (const spec of describedArgs) {
9437
- lines.push(` ${spec.name.padEnd(labelWidth)} ${spec.description}`);
9438
- }
9439
- }
9440
- if (flagSpecs.length > 0) {
9441
- lines.push("", "Flags:");
9442
- for (const spec of flagSpecs) {
9443
- const detail = [
9444
- spec.description,
9445
- spec.default !== void 0 ? `(default: ${spec.default})` : void 0
9446
- ].filter(Boolean).join(" ");
9447
- lines.push(` ${`--${spec.name}`.padEnd(labelWidth)} ${detail}`.trimEnd());
9448
- }
9449
- }
9450
- return lines.join("\n");
9451
- }
9452
- function helpRequested(raw) {
9453
- return raw.includes("-h") || raw.includes("--help");
9454
- }
9455
- function printHelpIfRequested(opts) {
9456
- const argv = opts.argv ?? process.argv;
9457
- if (!helpRequested(argv.slice(2))) {
9458
- return;
9459
- }
9460
- console.log(buildHelp(extractCommandName(argv), opts.positional ?? [], opts.flags, opts.description));
9461
- process.exit(0);
9462
- }
9463
- function extractCommandName(argv) {
9464
- return argv[1]?.replace(/.*\//, "").replace(/\.[jt]s$/, "") ?? "command";
9465
- }
9466
-
9467
- // ../bitfab-plugin-lib/dist/studioChannel.js
9468
- import crypto6 from "crypto";
9469
-
9470
9401
  // ../bitfab-plugin-lib/dist/browser.js
9471
- import { execSync, spawn } from "child_process";
9402
+ import { execFileSync as execFileSync3, execSync, spawn } from "child_process";
9472
9403
  import fs8 from "fs";
9473
9404
  import os8 from "os";
9474
9405
  function getChromiumBrowsers() {
@@ -9715,6 +9646,47 @@ function shouldDisableChromelessWindows() {
9715
9646
  var WINDOW_OPEN_RATE_WINDOW_MS = 3e4;
9716
9647
  var WINDOW_OPEN_RATE_LIMIT = 3;
9717
9648
  var recentOpens = [];
9649
+ function macAppNameFromBinaryPath(binaryPath) {
9650
+ const match = binaryPath.match(/\/([^/]+)\.app\//);
9651
+ return match ? match[1] : null;
9652
+ }
9653
+ function macChromiumAppNames() {
9654
+ return [
9655
+ ...new Set(getChromiumBrowsers().flatMap((b) => b.binaryPaths.map(macAppNameFromBinaryPath)).filter((name) => name != null))
9656
+ ];
9657
+ }
9658
+ function buildCloseStudioWindowsAppleScript(appName, sessionId) {
9659
+ return [
9660
+ `if application "${appName}" is running then`,
9661
+ ` tell application "${appName}"`,
9662
+ ` repeat with w in (every window)`,
9663
+ ` try`,
9664
+ ` repeat with t in (every tab of w)`,
9665
+ ` if (URL of t) contains "${sessionId}" then`,
9666
+ ` close w`,
9667
+ ` exit repeat`,
9668
+ ` end if`,
9669
+ ` end repeat`,
9670
+ ` end try`,
9671
+ ` end repeat`,
9672
+ ` end tell`,
9673
+ `end if`
9674
+ ].join("\n");
9675
+ }
9676
+ function closeStudioWindowsBySession(sessionId) {
9677
+ if (os8.platform() !== "darwin" || !sessionId) {
9678
+ return;
9679
+ }
9680
+ if (/["\\]/.test(sessionId)) {
9681
+ return;
9682
+ }
9683
+ for (const appName of macChromiumAppNames()) {
9684
+ try {
9685
+ execFileSync3("osascript", ["-e", buildCloseStudioWindowsAppleScript(appName, sessionId)], { stdio: "ignore", timeout: 4e3 });
9686
+ } catch {
9687
+ }
9688
+ }
9689
+ }
9718
9690
  function openChromelessWindow(url2) {
9719
9691
  if (process.env.VITEST || process.env.NODE_ENV === "test") {
9720
9692
  throw new Error("openChromelessWindow called in a test environment without being mocked");
@@ -9756,6 +9728,75 @@ function openChromelessWindow(url2) {
9756
9728
  return null;
9757
9729
  }
9758
9730
 
9731
+ // ../bitfab-plugin-lib/dist/parseArgs.js
9732
+ function formatPositionalLabel(spec) {
9733
+ const tag = spec.format && spec.format !== "string" ? `:${spec.format}` : "";
9734
+ if (spec.variadic) {
9735
+ return spec.required !== false ? `<${spec.name}${tag}> [<${spec.name}${tag}>...]` : `[<${spec.name}${tag}>...]`;
9736
+ }
9737
+ return spec.required !== false ? `<${spec.name}${tag}>` : `[${spec.name}${tag}]`;
9738
+ }
9739
+ function formatFlagLabel(spec) {
9740
+ if (spec.boolean) {
9741
+ return `[--${spec.name}]`;
9742
+ }
9743
+ const tag = spec.format && spec.format !== "string" ? `:${spec.format}` : "";
9744
+ const valueHint = spec.format === "number" ? " N" : ` <${spec.name}${tag}>`;
9745
+ const defaultHint = spec.default !== void 0 ? ` (default: ${spec.default})` : "";
9746
+ return `[--${spec.name}${valueHint}${defaultHint}]`;
9747
+ }
9748
+ function buildUsage(command, positional, flags) {
9749
+ const parts = [
9750
+ `Usage: ${command}`,
9751
+ ...positional.map(formatPositionalLabel),
9752
+ ...(flags ?? []).map(formatFlagLabel)
9753
+ ];
9754
+ return parts.join(" ");
9755
+ }
9756
+ function buildHelp(command, positional, flags, description) {
9757
+ const lines = [buildUsage(command, positional, flags)];
9758
+ if (description) {
9759
+ lines.push("", description);
9760
+ }
9761
+ const describedArgs = positional.filter((s) => s.description);
9762
+ const flagSpecs = flags ?? [];
9763
+ const labelWidth = Math.max(0, ...describedArgs.map((s) => s.name.length), ...flagSpecs.map((s) => s.name.length + 2));
9764
+ if (describedArgs.length > 0) {
9765
+ lines.push("", "Arguments:");
9766
+ for (const spec of describedArgs) {
9767
+ lines.push(` ${spec.name.padEnd(labelWidth)} ${spec.description}`);
9768
+ }
9769
+ }
9770
+ if (flagSpecs.length > 0) {
9771
+ lines.push("", "Flags:");
9772
+ for (const spec of flagSpecs) {
9773
+ const detail = [
9774
+ spec.description,
9775
+ spec.default !== void 0 ? `(default: ${spec.default})` : void 0
9776
+ ].filter(Boolean).join(" ");
9777
+ lines.push(` ${`--${spec.name}`.padEnd(labelWidth)} ${detail}`.trimEnd());
9778
+ }
9779
+ }
9780
+ return lines.join("\n");
9781
+ }
9782
+ function helpRequested(raw) {
9783
+ return raw.includes("-h") || raw.includes("--help");
9784
+ }
9785
+ function printHelpIfRequested(opts) {
9786
+ const argv = opts.argv ?? process.argv;
9787
+ if (!helpRequested(argv.slice(2))) {
9788
+ return;
9789
+ }
9790
+ console.log(buildHelp(extractCommandName(argv), opts.positional ?? [], opts.flags, opts.description));
9791
+ process.exit(0);
9792
+ }
9793
+ function extractCommandName(argv) {
9794
+ return argv[1]?.replace(/.*\//, "").replace(/\.[jt]s$/, "") ?? "command";
9795
+ }
9796
+
9797
+ // ../bitfab-plugin-lib/dist/studioChannel.js
9798
+ import crypto6 from "crypto";
9799
+
9759
9800
  // ../bitfab-plugin-lib/dist/commands/createStudioSession.js
9760
9801
  function ensureStudioPath(p5) {
9761
9802
  if (!p5.startsWith("/studio")) {
@@ -9999,6 +10040,7 @@ var DaemonClient = class {
9999
10040
  buffer = "";
10000
10041
  responseQueue = [];
10001
10042
  eventHandler = null;
10043
+ disconnectHandler = null;
10002
10044
  socketPath;
10003
10045
  constructor(socketPath) {
10004
10046
  this.socketPath = socketPath ?? SOCKET_PATH;
@@ -10020,9 +10062,14 @@ var DaemonClient = class {
10020
10062
  return;
10021
10063
  }
10022
10064
  this.socket = null;
10065
+ this.disconnectHandler?.(err);
10023
10066
  });
10024
10067
  socket.on("close", () => {
10068
+ const wasConnected = this.socket !== null;
10025
10069
  this.socket = null;
10070
+ if (wasConnected) {
10071
+ this.disconnectHandler?.(new Error("daemon connection closed"));
10072
+ }
10026
10073
  });
10027
10074
  });
10028
10075
  }
@@ -10103,8 +10150,13 @@ var DaemonClient = class {
10103
10150
  onEvent(handler) {
10104
10151
  this.eventHandler = handler;
10105
10152
  }
10153
+ /** Fires once when the daemon connection drops unexpectedly (not on destroy). */
10154
+ onDisconnect(handler) {
10155
+ this.disconnectHandler = handler;
10156
+ }
10106
10157
  destroy() {
10107
10158
  this.eventHandler = null;
10159
+ this.disconnectHandler = null;
10108
10160
  this.responseQueue = [];
10109
10161
  if (this.socket) {
10110
10162
  this.socket.destroy();
@@ -10113,6 +10165,23 @@ var DaemonClient = class {
10113
10165
  }
10114
10166
  };
10115
10167
 
10168
+ // ../bitfab-plugin-lib/dist/studioUrl.js
10169
+ var DEFAULT_STUDIO_PATH = "/studio";
10170
+ function resolveStudioInitialPath(input) {
10171
+ if (input && isValidStudioRoute(input)) {
10172
+ return input;
10173
+ }
10174
+ return DEFAULT_STUDIO_PATH;
10175
+ }
10176
+ function buildStudioSessionUrl(args) {
10177
+ const { serviceUrl, path: path18, sessionId } = args;
10178
+ if (path18.includes("session=")) {
10179
+ return `${serviceUrl}${path18}`;
10180
+ }
10181
+ const separator = path18.includes("?") ? "&" : "?";
10182
+ return `${serviceUrl}${path18}${separator}session=${encodeURIComponent(sessionId)}`;
10183
+ }
10184
+
10116
10185
  // ../bitfab-plugin-lib/dist/studioChannel.js
10117
10186
  var DirectChannel = class {
10118
10187
  apiKey;
@@ -10127,11 +10196,12 @@ var DirectChannel = class {
10127
10196
  async openOrNavigate(path18, opts) {
10128
10197
  if (!this.apiKey) {
10129
10198
  const sessionId2 = opts?.sessionId ?? crypto6.randomUUID();
10130
- const initialPath = path18.startsWith("/studio") ? path18 : "/studio";
10131
- if (!isValidStudioRoute(initialPath)) {
10132
- throw new Error(`Studio route not allowed: ${initialPath}`);
10133
- }
10134
- const url2 = initialPath.includes("session=") ? `${this.serviceUrl}${initialPath}` : `${this.serviceUrl}${initialPath}${initialPath.includes("?") ? "&" : "?"}session=${encodeURIComponent(sessionId2)}`;
10199
+ const initialPath = resolveStudioInitialPath(path18);
10200
+ const url2 = buildStudioSessionUrl({
10201
+ serviceUrl: this.serviceUrl,
10202
+ path: initialPath,
10203
+ sessionId: sessionId2
10204
+ });
10135
10205
  const windowPid = openChromelessWindow(url2);
10136
10206
  writeActiveStudioSession({
10137
10207
  sessionId: sessionId2,
@@ -10166,6 +10236,9 @@ var DirectChannel = class {
10166
10236
  async close(sessionId, message) {
10167
10237
  await closeStudio({ serviceUrl: this.serviceUrl, apiKey: this.apiKey, sessionId }, message);
10168
10238
  }
10239
+ forceCloseWindow(sessionId) {
10240
+ closeStudioWindowsBySession(sessionId);
10241
+ }
10169
10242
  subscribe(sessionId, onEvent, onError) {
10170
10243
  const abortController = new AbortController();
10171
10244
  const done = (async () => {
@@ -10255,18 +10328,23 @@ var DaemonChannel = class _DaemonChannel {
10255
10328
  })
10256
10329
  ]);
10257
10330
  }
10258
- subscribe(_sessionId, onEvent, _onError) {
10331
+ forceCloseWindow(_sessionId) {
10332
+ }
10333
+ subscribe(_sessionId, onEvent, onError) {
10259
10334
  const done = new Promise((resolve) => {
10260
10335
  this.doneResolve = resolve;
10261
10336
  });
10262
10337
  this.client.onEvent((push) => {
10263
10338
  const event = {
10264
- id: "",
10339
+ id: push.id,
10265
10340
  type: push.event,
10266
10341
  data: push.data
10267
10342
  };
10268
10343
  onEvent(event);
10269
10344
  });
10345
+ this.client.onDisconnect((err) => {
10346
+ onError(err);
10347
+ });
10270
10348
  return {
10271
10349
  abort: () => this.destroy(),
10272
10350
  done
@@ -10304,6 +10382,9 @@ function withDirectFallback(primary, apiKey, serviceUrl) {
10304
10382
  async close(sessionId, message) {
10305
10383
  await active.close(sessionId, message);
10306
10384
  },
10385
+ forceCloseWindow(sessionId) {
10386
+ active.forceCloseWindow(sessionId);
10387
+ },
10307
10388
  subscribe(sessionId, onEvent, onError) {
10308
10389
  return active.subscribe(sessionId, onEvent, onError);
10309
10390
  },
@@ -10372,13 +10453,13 @@ async function reportHandoff(apiKey, pluginVersion, platform2, body) {
10372
10453
  import crypto7 from "crypto";
10373
10454
 
10374
10455
  // ../bitfab-plugin-lib/dist/frontmostApp.js
10375
- import { execFileSync as execFileSync3, spawn as spawn2 } from "child_process";
10456
+ import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
10376
10457
  import os10 from "os";
10377
10458
  function findAncestorApp() {
10378
10459
  try {
10379
10460
  let pid = process.ppid;
10380
10461
  while (pid > 1) {
10381
- const output = execFileSync3("ps", ["-o", "ppid=,comm=", "-p", String(pid)], { stdio: "pipe", encoding: "utf-8" }).trim();
10462
+ const output = execFileSync4("ps", ["-o", "ppid=,comm=", "-p", String(pid)], { stdio: "pipe", encoding: "utf-8" }).trim();
10382
10463
  const match = output.match(/^\s*(\d+)\s+(.+)$/);
10383
10464
  if (!match) {
10384
10465
  break;
@@ -10437,19 +10518,19 @@ function getFrontmostApp() {
10437
10518
  const platform2 = os10.platform();
10438
10519
  try {
10439
10520
  if (platform2 === "darwin") {
10440
- return execFileSync3("osascript", [
10521
+ return execFileSync4("osascript", [
10441
10522
  "-e",
10442
10523
  'tell application "System Events" to get name of first process whose frontmost is true'
10443
10524
  ], { stdio: "pipe", encoding: "utf-8" }).trim();
10444
10525
  }
10445
10526
  if (platform2 === "linux") {
10446
- return execFileSync3("xdotool", ["getactivewindow"], {
10527
+ return execFileSync4("xdotool", ["getactivewindow"], {
10447
10528
  stdio: "pipe",
10448
10529
  encoding: "utf-8"
10449
10530
  }).trim();
10450
10531
  }
10451
10532
  if (platform2 === "win32") {
10452
- return execFileSync3("powershell.exe", [
10533
+ return execFileSync4("powershell.exe", [
10453
10534
  "-NoProfile",
10454
10535
  "-Command",
10455
10536
  `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.87",
3
+ "version": "0.2.89",
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",