bitfab-cli 0.2.76 → 0.2.78
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.
- package/dist/index.js +104 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6801,10 +6801,6 @@ var require_dist = __commonJS({
|
|
|
6801
6801
|
// src/index.ts
|
|
6802
6802
|
import { cancel as cancel2 } from "@clack/prompts";
|
|
6803
6803
|
|
|
6804
|
-
// src/init.ts
|
|
6805
|
-
import { execSync as execSync3 } from "child_process";
|
|
6806
|
-
import * as p4 from "@clack/prompts";
|
|
6807
|
-
|
|
6808
6804
|
// ../bitfab-plugin-lib/dist/errors.js
|
|
6809
6805
|
var NotAuthenticatedError = class extends Error {
|
|
6810
6806
|
constructor(message) {
|
|
@@ -7614,7 +7610,7 @@ function printBlockingNotice(until) {
|
|
|
7614
7610
|
|
|
7615
7611
|
// ../bitfab-plugin-lib/dist/studioTeardown.js
|
|
7616
7612
|
var WINDOW_CLOSE_GRACE_PERIOD_MS = 1e4;
|
|
7617
|
-
function createWindowCloseArbiter(handlers) {
|
|
7613
|
+
function createWindowCloseArbiter(handlers, graceMs = WINDOW_CLOSE_GRACE_PERIOD_MS) {
|
|
7618
7614
|
let graceTimer = null;
|
|
7619
7615
|
const cancel3 = () => {
|
|
7620
7616
|
if (graceTimer) {
|
|
@@ -7629,7 +7625,7 @@ function createWindowCloseArbiter(handlers) {
|
|
|
7629
7625
|
graceTimer = setTimeout(() => {
|
|
7630
7626
|
graceTimer = null;
|
|
7631
7627
|
handlers.onConfirmed(event);
|
|
7632
|
-
},
|
|
7628
|
+
}, graceMs);
|
|
7633
7629
|
return;
|
|
7634
7630
|
}
|
|
7635
7631
|
if (event.type === "studio:session-started" && graceTimer) {
|
|
@@ -8196,6 +8192,11 @@ async function openStudioTo(path15, opts = {}) {
|
|
|
8196
8192
|
const signInPath = `/studio/sign-in?redirect_url=${encodeURIComponent(redirectPath)}`;
|
|
8197
8193
|
const signInUrl = `${serviceUrl}${signInPath}&session=${encodeURIComponent(sessionId2)}`;
|
|
8198
8194
|
const windowPid = openChromelessWindow(signInUrl);
|
|
8195
|
+
writeActiveStudioSession({
|
|
8196
|
+
sessionId: sessionId2,
|
|
8197
|
+
serviceUrl,
|
|
8198
|
+
windowPid: windowPid ?? void 0
|
|
8199
|
+
});
|
|
8199
8200
|
opts.onLoginRequired?.(sessionId2, signInUrl);
|
|
8200
8201
|
const abortController = new AbortController();
|
|
8201
8202
|
const timer = setTimeout(() => abortController.abort(), LOGIN_TIMEOUT_MS);
|
|
@@ -8222,11 +8223,6 @@ async function openStudioTo(path15, opts = {}) {
|
|
|
8222
8223
|
apiKey: loginApiKey,
|
|
8223
8224
|
sessionId: sessionId2
|
|
8224
8225
|
});
|
|
8225
|
-
writeActiveStudioSession({
|
|
8226
|
-
sessionId: sessionId2,
|
|
8227
|
-
serviceUrl,
|
|
8228
|
-
windowPid: windowPid ?? void 0
|
|
8229
|
-
});
|
|
8230
8226
|
const poller2 = startEventPoller(serviceUrl, loginApiKey, sessionId2, opts.onEvent, restoreFocus, { claim: opts.monitor !== "wait" });
|
|
8231
8227
|
return {
|
|
8232
8228
|
sessionId: sessionId2,
|
|
@@ -8395,7 +8391,7 @@ function awaitReauthLogin(args) {
|
|
|
8395
8391
|
});
|
|
8396
8392
|
});
|
|
8397
8393
|
}
|
|
8398
|
-
function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus, { claim = true } = {}) {
|
|
8394
|
+
function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus, { claim = true, fromStreamStart = false, graceMs, forceClear = false } = {}) {
|
|
8399
8395
|
printBlockingNotice(BLOCKING_NOTICE_UNTIL);
|
|
8400
8396
|
if (claim) {
|
|
8401
8397
|
claimStudioMonitor();
|
|
@@ -8418,15 +8414,19 @@ function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus,
|
|
|
8418
8414
|
};
|
|
8419
8415
|
const arbiter = createWindowCloseArbiter({
|
|
8420
8416
|
onConfirmed: (event) => {
|
|
8421
|
-
|
|
8417
|
+
if (forceClear) {
|
|
8418
|
+
clearActiveStudioSession(sessionId, { force: true });
|
|
8419
|
+
} else {
|
|
8420
|
+
clearActiveStudioSession(sessionId);
|
|
8421
|
+
}
|
|
8422
8422
|
notifyEnded(event);
|
|
8423
8423
|
},
|
|
8424
8424
|
onRefuted: () => {
|
|
8425
8425
|
console.error("Studio reconnected after page refresh");
|
|
8426
8426
|
}
|
|
8427
|
-
});
|
|
8427
|
+
}, graceMs);
|
|
8428
8428
|
const done = (async () => {
|
|
8429
|
-
const startCursor = await fetchStreamTip({ serviceUrl, apiKey, sessionId });
|
|
8429
|
+
const startCursor = fromStreamStart ? void 0 : await fetchStreamTip({ serviceUrl, apiKey, sessionId });
|
|
8430
8430
|
if (abortController.signal.aborted) {
|
|
8431
8431
|
return;
|
|
8432
8432
|
}
|
|
@@ -8479,8 +8479,62 @@ function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus,
|
|
|
8479
8479
|
done
|
|
8480
8480
|
};
|
|
8481
8481
|
}
|
|
8482
|
+
var LOGIN_MONITOR_DRAIN_MS = 8e3;
|
|
8483
|
+
async function runLoginWindowMonitor(sessionId) {
|
|
8484
|
+
const config2 = getConfig();
|
|
8485
|
+
const apiKey = config2.apiKey;
|
|
8486
|
+
if (!apiKey) {
|
|
8487
|
+
return;
|
|
8488
|
+
}
|
|
8489
|
+
const poller = startEventPoller(
|
|
8490
|
+
config2.serviceUrl,
|
|
8491
|
+
apiKey,
|
|
8492
|
+
sessionId,
|
|
8493
|
+
() => {
|
|
8494
|
+
},
|
|
8495
|
+
() => {
|
|
8496
|
+
},
|
|
8497
|
+
// claim: false — this is a transient cleanup reader with a no-op event
|
|
8498
|
+
// handler, NOT the session's real monitor. If it claimed the lock, a
|
|
8499
|
+
// subsequent `openStudioTo` reuse with monitor:"start" (e.g. a flow's
|
|
8500
|
+
// backgrounded open right after a PARKED login) would lose the atomic
|
|
8501
|
+
// claim, assume a live monitor is forwarding events, and fire-and-exit —
|
|
8502
|
+
// leaving the session with no real monitor while this one forwards
|
|
8503
|
+
// nothing. Reading without claiming lets it clean up the pointer
|
|
8504
|
+
// (window-closed → clear) without shadowing the real monitor.
|
|
8505
|
+
//
|
|
8506
|
+
// forceClear: the clear runs in a detached child while the login/init
|
|
8507
|
+
// parent that WROTE the pointer may still be alive (init keeps running with
|
|
8508
|
+
// exitOnComplete:false, even spawnSync-ing the editor), so a non-force clear
|
|
8509
|
+
// would be refused by the writer-alive guard and the closed window's
|
|
8510
|
+
// pointer would leak. Safe — the clear still matches on the unique
|
|
8511
|
+
// sessionId.
|
|
8512
|
+
{ claim: false, fromStreamStart: true, graceMs: 0, forceClear: true }
|
|
8513
|
+
);
|
|
8514
|
+
let drainTimer;
|
|
8515
|
+
const drain = new Promise((resolve) => {
|
|
8516
|
+
drainTimer = setTimeout(resolve, LOGIN_MONITOR_DRAIN_MS);
|
|
8517
|
+
});
|
|
8518
|
+
await Promise.race([poller.done, drain]);
|
|
8519
|
+
if (drainTimer) {
|
|
8520
|
+
clearTimeout(drainTimer);
|
|
8521
|
+
}
|
|
8522
|
+
poller.abort();
|
|
8523
|
+
}
|
|
8482
8524
|
|
|
8483
8525
|
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
8526
|
+
function trySpawnDetachedMonitor(sessionId) {
|
|
8527
|
+
try {
|
|
8528
|
+
const pid = spawnDetached(process.execPath, [
|
|
8529
|
+
...process.argv.slice(1),
|
|
8530
|
+
"--drain",
|
|
8531
|
+
sessionId
|
|
8532
|
+
]);
|
|
8533
|
+
return pid != null;
|
|
8534
|
+
} catch {
|
|
8535
|
+
return false;
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
8484
8538
|
async function verifyToken(serviceUrl, token) {
|
|
8485
8539
|
try {
|
|
8486
8540
|
const res = await fetch(`${serviceUrl}/api/plugin/whoami`, {
|
|
@@ -8496,6 +8550,18 @@ async function verifyToken(serviceUrl, token) {
|
|
|
8496
8550
|
}
|
|
8497
8551
|
async function runLogin(platform2, pluginVersion, options) {
|
|
8498
8552
|
const exitOnComplete = options?.exitOnComplete ?? true;
|
|
8553
|
+
const drainIdx = process.argv.indexOf("--drain");
|
|
8554
|
+
if (drainIdx !== -1) {
|
|
8555
|
+
const sessionId = process.argv[drainIdx + 1];
|
|
8556
|
+
if (!sessionId) {
|
|
8557
|
+
throw new Error("--drain requires a sessionId argument");
|
|
8558
|
+
}
|
|
8559
|
+
await runLoginWindowMonitor(sessionId);
|
|
8560
|
+
if (exitOnComplete) {
|
|
8561
|
+
process.exit(0);
|
|
8562
|
+
}
|
|
8563
|
+
return;
|
|
8564
|
+
}
|
|
8499
8565
|
const force = options?.force ?? process.argv.includes("--force");
|
|
8500
8566
|
const config2 = getConfig();
|
|
8501
8567
|
if (!force && hasCredentials() && config2.apiKey) {
|
|
@@ -8510,7 +8576,6 @@ Already logged in as ${identity}. Run the login command with --force to re-authe
|
|
|
8510
8576
|
return;
|
|
8511
8577
|
}
|
|
8512
8578
|
}
|
|
8513
|
-
console.error("NOTE: this command blocks until sign-in completes in the browser (10-minute timeout).");
|
|
8514
8579
|
console.log("\nOpening Studio to sign in...");
|
|
8515
8580
|
try {
|
|
8516
8581
|
const result = await openStudioTo("/studio", {
|
|
@@ -8522,7 +8587,6 @@ If the browser didn't open, visit this URL manually:
|
|
|
8522
8587
|
${signInUrl}`);
|
|
8523
8588
|
}
|
|
8524
8589
|
});
|
|
8525
|
-
result.abort();
|
|
8526
8590
|
const apiKey = getConfig().apiKey;
|
|
8527
8591
|
if (apiKey) {
|
|
8528
8592
|
await reportHandoff(apiKey, pluginVersion, platform2, {
|
|
@@ -8537,6 +8601,9 @@ ${signInUrl}`);
|
|
|
8537
8601
|
${greeting}`);
|
|
8538
8602
|
console.log("Bitfab MCP tools are now active. (via studio)");
|
|
8539
8603
|
}
|
|
8604
|
+
if (!trySpawnDetachedMonitor(result.sessionId)) {
|
|
8605
|
+
await runLoginWindowMonitor(result.sessionId);
|
|
8606
|
+
}
|
|
8540
8607
|
if (exitOnComplete) {
|
|
8541
8608
|
process.exit(0);
|
|
8542
8609
|
}
|
|
@@ -27780,6 +27847,10 @@ var McpZodTypeKind;
|
|
|
27780
27847
|
// ../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_@cfworker+json-schema@4.1.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
27781
27848
|
import process3 from "process";
|
|
27782
27849
|
|
|
27850
|
+
// src/init.ts
|
|
27851
|
+
import { execSync as execSync3 } from "child_process";
|
|
27852
|
+
import * as p4 from "@clack/prompts";
|
|
27853
|
+
|
|
27783
27854
|
// src/claude.ts
|
|
27784
27855
|
import { spawnSync } from "child_process";
|
|
27785
27856
|
import fs15 from "fs";
|
|
@@ -28423,7 +28494,22 @@ function parseArgs2(argv) {
|
|
|
28423
28494
|
}
|
|
28424
28495
|
return { command, editor, skipPermissions, rest };
|
|
28425
28496
|
}
|
|
28497
|
+
async function handleDrainArg(argv) {
|
|
28498
|
+
const drainIdx = argv.indexOf("--drain");
|
|
28499
|
+
if (drainIdx === -1) {
|
|
28500
|
+
return false;
|
|
28501
|
+
}
|
|
28502
|
+
const sessionId = argv[drainIdx + 1];
|
|
28503
|
+
if (!sessionId) {
|
|
28504
|
+
throw new Error("--drain requires a sessionId argument");
|
|
28505
|
+
}
|
|
28506
|
+
await runLoginWindowMonitor(sessionId);
|
|
28507
|
+
return true;
|
|
28508
|
+
}
|
|
28426
28509
|
async function main() {
|
|
28510
|
+
if (await handleDrainArg(process.argv)) {
|
|
28511
|
+
return;
|
|
28512
|
+
}
|
|
28427
28513
|
const { command, editor, skipPermissions, rest } = parseArgs2(
|
|
28428
28514
|
process.argv.slice(2)
|
|
28429
28515
|
);
|
|
@@ -28480,5 +28566,6 @@ main().catch((err) => {
|
|
|
28480
28566
|
process.exit(1);
|
|
28481
28567
|
});
|
|
28482
28568
|
export {
|
|
28569
|
+
handleDrainArg,
|
|
28483
28570
|
parseArgs2 as parseArgs
|
|
28484
28571
|
};
|