bitfab-cli 0.2.20 → 0.2.22

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 +59 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7324,26 +7324,63 @@ function sessionScope() {
7324
7324
  function filePath() {
7325
7325
  return path5.join(os6.homedir(), ".config", "bitfab", `active-studio-session.${sessionScope()}.json`);
7326
7326
  }
7327
- function writeActiveStudioSession(session) {
7327
+ function writeRecord(record2) {
7328
7328
  const target = filePath();
7329
7329
  fs6.mkdirSync(path5.dirname(target), { recursive: true });
7330
- const payload = {
7331
- ...session,
7332
- pid: process.pid,
7333
- startedAt: (/* @__PURE__ */ new Date()).toISOString()
7334
- };
7335
7330
  const tmp = `${target}.${process.pid}.tmp`;
7336
- fs6.writeFileSync(tmp, `${JSON.stringify(payload, null, 2)}
7331
+ fs6.writeFileSync(tmp, `${JSON.stringify(record2, null, 2)}
7337
7332
  `);
7338
7333
  fs6.renameSync(tmp, target);
7339
7334
  }
7340
- function clearActiveStudioSession() {
7335
+ function writeActiveStudioSession(session) {
7336
+ writeRecord({
7337
+ ...session,
7338
+ pid: process.pid,
7339
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
7340
+ });
7341
+ }
7342
+ function isProcessAlive(pid) {
7343
+ try {
7344
+ process.kill(pid, 0);
7345
+ return true;
7346
+ } catch (err) {
7347
+ return err.code === "EPERM";
7348
+ }
7349
+ }
7350
+ function stampActiveStudioSession(sessionId, field) {
7351
+ try {
7352
+ const current = readActiveStudioSessionRaw();
7353
+ if (!current || current.sessionId !== sessionId || current[field]) {
7354
+ return;
7355
+ }
7356
+ if (current.pid !== process.pid && isProcessAlive(current.pid)) {
7357
+ return;
7358
+ }
7359
+ writeRecord({ ...current, [field]: (/* @__PURE__ */ new Date()).toISOString() });
7360
+ } catch {
7361
+ }
7362
+ }
7363
+ function markActiveStudioSessionClosed(sessionId) {
7364
+ stampActiveStudioSession(sessionId, "closedAt");
7365
+ }
7366
+ function markActiveStudioSessionEnded(sessionId) {
7367
+ stampActiveStudioSession(sessionId, "endedAt");
7368
+ }
7369
+ function reactivateActiveStudioSession(sessionId) {
7341
7370
  try {
7342
7371
  const current = readActiveStudioSessionRaw();
7343
- if (current && current.pid !== process.pid) {
7372
+ if (!current || current.sessionId !== sessionId) {
7373
+ return;
7374
+ }
7375
+ if (!current.endedAt && !current.closedAt) {
7344
7376
  return;
7345
7377
  }
7346
- fs6.rmSync(filePath(), { force: true });
7378
+ writeRecord({
7379
+ sessionId: current.sessionId,
7380
+ serviceUrl: current.serviceUrl,
7381
+ pid: current.pid,
7382
+ startedAt: current.startedAt
7383
+ });
7347
7384
  } catch {
7348
7385
  }
7349
7386
  }
@@ -7351,7 +7388,7 @@ function readActiveStudioSessionRaw() {
7351
7388
  try {
7352
7389
  const raw = fs6.readFileSync(filePath(), "utf-8");
7353
7390
  const parsed = JSON.parse(raw);
7354
- if (typeof parsed.sessionId === "string" && typeof parsed.serviceUrl === "string" && typeof parsed.pid === "number" && typeof parsed.startedAt === "string") {
7391
+ if (typeof parsed.sessionId === "string" && typeof parsed.serviceUrl === "string" && typeof parsed.pid === "number" && typeof parsed.startedAt === "string" && (parsed.closedAt === void 0 || typeof parsed.closedAt === "string") && (parsed.endedAt === void 0 || typeof parsed.endedAt === "string")) {
7355
7392
  return parsed;
7356
7393
  }
7357
7394
  return null;
@@ -7932,10 +7969,9 @@ async function openStudioTo(path14, opts) {
7932
7969
  done: Promise.resolve()
7933
7970
  };
7934
7971
  }
7935
- if (opts.forceNew) {
7936
- clearActiveStudioSession();
7937
- }
7938
- const existing = opts.forceNew || opts.sessionId ? null : readActiveStudioSession();
7972
+ const active = opts.forceNew ? null : readActiveStudioSession();
7973
+ const reachable = active && !active.closedAt ? active : null;
7974
+ const existing = opts.sessionId != null && (reachable?.sessionId !== opts.sessionId || reachable?.serviceUrl !== opts.serviceUrl) ? null : reachable;
7939
7975
  if (existing) {
7940
7976
  const client = {
7941
7977
  serviceUrl: existing.serviceUrl,
@@ -7944,6 +7980,9 @@ async function openStudioTo(path14, opts) {
7944
7980
  };
7945
7981
  const ack = await navigateStudioAndAwaitAck(client, path14);
7946
7982
  if (ack.acked) {
7983
+ if (existing.endedAt != null || existing.closedAt != null) {
7984
+ reactivateActiveStudioSession(existing.sessionId);
7985
+ }
7947
7986
  const poller2 = opts.onEvent ? startEventPoller(existing.serviceUrl, opts.apiKey, existing.sessionId, opts.onEvent, restoreFocus) : { abort: noop, done: Promise.resolve() };
7948
7987
  return {
7949
7988
  sessionId: existing.sessionId,
@@ -7952,11 +7991,9 @@ async function openStudioTo(path14, opts) {
7952
7991
  ...poller2
7953
7992
  };
7954
7993
  }
7955
- if (ack.reason === "session-ended" || ack.reason === "push-failed") {
7956
- if (ack.reason === "session-ended") {
7957
- clearActiveStudioSession();
7958
- }
7959
- } else {
7994
+ const tabGone = ack.reason === "session-ended" || ack.reason === "push-failed" || ack.reason === "timeout";
7995
+ const canReopenSilently = tabGone && (existing.endedAt != null || opts.sessionId != null);
7996
+ if (!canReopenSilently) {
7960
7997
  throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
7961
7998
  }
7962
7999
  }
@@ -7990,7 +8027,7 @@ function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus)
7990
8027
  }
7991
8028
  sessionEndGraceTimer = setTimeout(() => {
7992
8029
  sessionEndGraceTimer = null;
7993
- clearActiveStudioSession();
8030
+ markActiveStudioSessionClosed(sessionId);
7994
8031
  restoreFocus();
7995
8032
  onEvent({ ...event, type: "studio:session-ended" });
7996
8033
  abortController.abort();
@@ -8010,7 +8047,7 @@ function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus)
8010
8047
  clearTimeout(sessionEndGraceTimer);
8011
8048
  sessionEndGraceTimer = null;
8012
8049
  }
8013
- clearActiveStudioSession();
8050
+ markActiveStudioSessionEnded(sessionId);
8014
8051
  restoreFocus();
8015
8052
  onEvent(event);
8016
8053
  abortController.abort();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitfab-cli",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
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",