bitfab-cli 0.2.21 → 0.2.23

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 +58 -20
  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) {
7341
7351
  try {
7342
7352
  const current = readActiveStudioSessionRaw();
7343
- if (current && current.pid !== process.pid) {
7353
+ if (!current || current.sessionId !== sessionId || current[field]) {
7344
7354
  return;
7345
7355
  }
7346
- fs6.rmSync(filePath(), { force: true });
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) {
7370
+ try {
7371
+ const current = readActiveStudioSessionRaw();
7372
+ if (!current || current.sessionId !== sessionId) {
7373
+ return;
7374
+ }
7375
+ if (!current.endedAt && !current.closedAt) {
7376
+ return;
7377
+ }
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,11 +7969,9 @@ async function openStudioTo(path14, opts) {
7932
7969
  done: Promise.resolve()
7933
7970
  };
7934
7971
  }
7935
- if (opts.forceNew) {
7936
- clearActiveStudioSession();
7937
- }
7938
7972
  const active = opts.forceNew ? null : readActiveStudioSession();
7939
- const existing = opts.sessionId != null && (active?.sessionId !== opts.sessionId || active?.serviceUrl !== opts.serviceUrl) ? null : active;
7973
+ const reachable = active && !active.closedAt ? active : null;
7974
+ const existing = opts.sessionId != null && (reachable?.sessionId !== opts.sessionId || reachable?.serviceUrl !== opts.serviceUrl) ? null : reachable;
7940
7975
  if (existing) {
7941
7976
  const client = {
7942
7977
  serviceUrl: existing.serviceUrl,
@@ -7945,6 +7980,9 @@ async function openStudioTo(path14, opts) {
7945
7980
  };
7946
7981
  const ack = await navigateStudioAndAwaitAck(client, path14);
7947
7982
  if (ack.acked) {
7983
+ if (existing.endedAt != null || existing.closedAt != null) {
7984
+ reactivateActiveStudioSession(existing.sessionId);
7985
+ }
7948
7986
  const poller2 = opts.onEvent ? startEventPoller(existing.serviceUrl, opts.apiKey, existing.sessionId, opts.onEvent, restoreFocus) : { abort: noop, done: Promise.resolve() };
7949
7987
  return {
7950
7988
  sessionId: existing.sessionId,
@@ -7953,9 +7991,9 @@ async function openStudioTo(path14, opts) {
7953
7991
  ...poller2
7954
7992
  };
7955
7993
  }
7956
- if (ack.reason === "session-ended" || ack.reason === "push-failed" || opts.sessionId != null && ack.reason === "timeout") {
7957
- clearActiveStudioSession();
7958
- } 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) {
7959
7997
  throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
7960
7998
  }
7961
7999
  }
@@ -7989,7 +8027,7 @@ function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus)
7989
8027
  }
7990
8028
  sessionEndGraceTimer = setTimeout(() => {
7991
8029
  sessionEndGraceTimer = null;
7992
- clearActiveStudioSession();
8030
+ markActiveStudioSessionClosed(sessionId);
7993
8031
  restoreFocus();
7994
8032
  onEvent({ ...event, type: "studio:session-ended" });
7995
8033
  abortController.abort();
@@ -8009,7 +8047,7 @@ function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus)
8009
8047
  clearTimeout(sessionEndGraceTimer);
8010
8048
  sessionEndGraceTimer = null;
8011
8049
  }
8012
- clearActiveStudioSession();
8050
+ markActiveStudioSessionEnded(sessionId);
8013
8051
  restoreFocus();
8014
8052
  onEvent(event);
8015
8053
  abortController.abort();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitfab-cli",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
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",