bitfab-cli 0.2.84 → 0.2.86
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 +924 -848
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5152,8 +5152,8 @@ var require_utils = __commonJS({
|
|
|
5152
5152
|
}
|
|
5153
5153
|
return ind;
|
|
5154
5154
|
}
|
|
5155
|
-
function removeDotSegments(
|
|
5156
|
-
let input =
|
|
5155
|
+
function removeDotSegments(path18) {
|
|
5156
|
+
let input = path18;
|
|
5157
5157
|
const output = [];
|
|
5158
5158
|
let nextSlash = -1;
|
|
5159
5159
|
let len = 0;
|
|
@@ -5352,8 +5352,8 @@ var require_schemes = __commonJS({
|
|
|
5352
5352
|
wsComponent.secure = void 0;
|
|
5353
5353
|
}
|
|
5354
5354
|
if (wsComponent.resourceName) {
|
|
5355
|
-
const [
|
|
5356
|
-
wsComponent.path =
|
|
5355
|
+
const [path18, query] = wsComponent.resourceName.split("?");
|
|
5356
|
+
wsComponent.path = path18 && path18 !== "/" ? path18 : void 0;
|
|
5357
5357
|
wsComponent.query = query;
|
|
5358
5358
|
wsComponent.resourceName = void 0;
|
|
5359
5359
|
}
|
|
@@ -5505,10 +5505,10 @@ var require_fast_uri = __commonJS({
|
|
|
5505
5505
|
function normalize(uri, options) {
|
|
5506
5506
|
if (typeof uri === "string") {
|
|
5507
5507
|
uri = /** @type {T} */
|
|
5508
|
-
|
|
5508
|
+
serialize2(parse3(uri, options), options);
|
|
5509
5509
|
} else if (typeof uri === "object") {
|
|
5510
5510
|
uri = /** @type {T} */
|
|
5511
|
-
parse3(
|
|
5511
|
+
parse3(serialize2(uri, options), options);
|
|
5512
5512
|
}
|
|
5513
5513
|
return uri;
|
|
5514
5514
|
}
|
|
@@ -5516,13 +5516,13 @@ var require_fast_uri = __commonJS({
|
|
|
5516
5516
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
5517
5517
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
5518
5518
|
schemelessOptions.skipEscape = true;
|
|
5519
|
-
return
|
|
5519
|
+
return serialize2(resolved, schemelessOptions);
|
|
5520
5520
|
}
|
|
5521
5521
|
function resolveComponent(base, relative, options, skipNormalization) {
|
|
5522
5522
|
const target = {};
|
|
5523
5523
|
if (!skipNormalization) {
|
|
5524
|
-
base = parse3(
|
|
5525
|
-
relative = parse3(
|
|
5524
|
+
base = parse3(serialize2(base, options), options);
|
|
5525
|
+
relative = parse3(serialize2(relative, options), options);
|
|
5526
5526
|
}
|
|
5527
5527
|
options = options || {};
|
|
5528
5528
|
if (!options.tolerant && relative.scheme) {
|
|
@@ -5574,19 +5574,19 @@ var require_fast_uri = __commonJS({
|
|
|
5574
5574
|
function equal(uriA, uriB, options) {
|
|
5575
5575
|
if (typeof uriA === "string") {
|
|
5576
5576
|
uriA = unescape(uriA);
|
|
5577
|
-
uriA =
|
|
5577
|
+
uriA = serialize2(normalizeComponentEncoding(parse3(uriA, options), true), { ...options, skipEscape: true });
|
|
5578
5578
|
} else if (typeof uriA === "object") {
|
|
5579
|
-
uriA =
|
|
5579
|
+
uriA = serialize2(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true });
|
|
5580
5580
|
}
|
|
5581
5581
|
if (typeof uriB === "string") {
|
|
5582
5582
|
uriB = unescape(uriB);
|
|
5583
|
-
uriB =
|
|
5583
|
+
uriB = serialize2(normalizeComponentEncoding(parse3(uriB, options), true), { ...options, skipEscape: true });
|
|
5584
5584
|
} else if (typeof uriB === "object") {
|
|
5585
|
-
uriB =
|
|
5585
|
+
uriB = serialize2(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true });
|
|
5586
5586
|
}
|
|
5587
5587
|
return uriA.toLowerCase() === uriB.toLowerCase();
|
|
5588
5588
|
}
|
|
5589
|
-
function
|
|
5589
|
+
function serialize2(cmpts, opts) {
|
|
5590
5590
|
const component = {
|
|
5591
5591
|
host: cmpts.host,
|
|
5592
5592
|
scheme: cmpts.scheme,
|
|
@@ -5742,7 +5742,7 @@ var require_fast_uri = __commonJS({
|
|
|
5742
5742
|
resolve,
|
|
5743
5743
|
resolveComponent,
|
|
5744
5744
|
equal,
|
|
5745
|
-
serialize,
|
|
5745
|
+
serialize: serialize2,
|
|
5746
5746
|
parse: parse3
|
|
5747
5747
|
};
|
|
5748
5748
|
module.exports = fastUri;
|
|
@@ -8715,12 +8715,12 @@ var require_dist = __commonJS({
|
|
|
8715
8715
|
throw new Error(`Unknown format "${name}"`);
|
|
8716
8716
|
return f;
|
|
8717
8717
|
};
|
|
8718
|
-
function addFormats(ajv, list,
|
|
8718
|
+
function addFormats(ajv, list, fs20, exportName) {
|
|
8719
8719
|
var _a2;
|
|
8720
8720
|
var _b;
|
|
8721
8721
|
(_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
8722
8722
|
for (const f of list)
|
|
8723
|
-
ajv.addFormat(f,
|
|
8723
|
+
ajv.addFormat(f, fs20[f]);
|
|
8724
8724
|
}
|
|
8725
8725
|
module.exports = exports = formatsPlugin;
|
|
8726
8726
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -8731,6 +8731,10 @@ var require_dist = __commonJS({
|
|
|
8731
8731
|
// src/index.ts
|
|
8732
8732
|
import { cancel as cancel2 } from "@clack/prompts";
|
|
8733
8733
|
|
|
8734
|
+
// src/init.ts
|
|
8735
|
+
import { execSync as execSync3 } from "child_process";
|
|
8736
|
+
import * as p4 from "@clack/prompts";
|
|
8737
|
+
|
|
8734
8738
|
// ../bitfab-plugin-lib/dist/errors.js
|
|
8735
8739
|
var NotAuthenticatedError = class extends Error {
|
|
8736
8740
|
constructor(message) {
|
|
@@ -8738,6 +8742,24 @@ var NotAuthenticatedError = class extends Error {
|
|
|
8738
8742
|
this.name = "NotAuthenticatedError";
|
|
8739
8743
|
}
|
|
8740
8744
|
};
|
|
8745
|
+
var DaemonUnavailableError = class extends Error {
|
|
8746
|
+
constructor(message) {
|
|
8747
|
+
super(message);
|
|
8748
|
+
this.name = "DaemonUnavailableError";
|
|
8749
|
+
}
|
|
8750
|
+
};
|
|
8751
|
+
var StudioNavigationError = class extends Error {
|
|
8752
|
+
reason;
|
|
8753
|
+
blockedReason;
|
|
8754
|
+
staleSessionId;
|
|
8755
|
+
constructor(reason, blockedReason, staleSessionId) {
|
|
8756
|
+
super(blockedReason ? `Studio navigation failed (${reason}): ${blockedReason}` : `Studio navigation failed: ${reason}`);
|
|
8757
|
+
this.reason = reason;
|
|
8758
|
+
this.blockedReason = blockedReason;
|
|
8759
|
+
this.staleSessionId = staleSessionId;
|
|
8760
|
+
this.name = "StudioNavigationError";
|
|
8761
|
+
}
|
|
8762
|
+
};
|
|
8741
8763
|
|
|
8742
8764
|
// ../bitfab-plugin-lib/dist/studioRoutes.js
|
|
8743
8765
|
var STUDIO_ROUTE_PATTERNS = [
|
|
@@ -8805,7 +8827,7 @@ async function pollAgentSessionEvents(opts) {
|
|
|
8805
8827
|
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
8806
8828
|
const client = {
|
|
8807
8829
|
serviceUrl: opts.serviceUrl,
|
|
8808
|
-
apiKey: opts.apiKey,
|
|
8830
|
+
apiKey: opts.apiKey ?? "",
|
|
8809
8831
|
sessionId: opts.sessionId
|
|
8810
8832
|
};
|
|
8811
8833
|
let since = opts.startCursor ?? null;
|
|
@@ -8816,8 +8838,9 @@ async function pollAgentSessionEvents(opts) {
|
|
|
8816
8838
|
if (since) {
|
|
8817
8839
|
url2.searchParams.set("since", since);
|
|
8818
8840
|
}
|
|
8841
|
+
const authHeaders = opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : { [STUDIO_SESSION_SCOPE_HEADER]: STUDIO_SESSION_SCOPE_VALUE };
|
|
8819
8842
|
const res = await fetch(url2.toString(), {
|
|
8820
|
-
headers:
|
|
8843
|
+
headers: authHeaders,
|
|
8821
8844
|
signal: opts.abortSignal
|
|
8822
8845
|
});
|
|
8823
8846
|
if (res.ok) {
|
|
@@ -8857,124 +8880,13 @@ async function pollAgentSessionEvents(opts) {
|
|
|
8857
8880
|
});
|
|
8858
8881
|
}
|
|
8859
8882
|
}
|
|
8860
|
-
async function pollLoginEvents(opts) {
|
|
8861
|
-
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
8862
|
-
let since = null;
|
|
8863
|
-
while (!opts.abortSignal.aborted) {
|
|
8864
|
-
try {
|
|
8865
|
-
const url2 = new URL(`${opts.serviceUrl}/api/studio/sessions/poll`);
|
|
8866
|
-
url2.searchParams.set("session", opts.sessionId);
|
|
8867
|
-
if (since) {
|
|
8868
|
-
url2.searchParams.set("since", since);
|
|
8869
|
-
}
|
|
8870
|
-
const res = await fetch(url2.toString(), {
|
|
8871
|
-
signal: opts.abortSignal
|
|
8872
|
-
});
|
|
8873
|
-
if (res.ok) {
|
|
8874
|
-
const body = await res.json();
|
|
8875
|
-
for (const event of body.events) {
|
|
8876
|
-
since = event.id;
|
|
8877
|
-
if (event.type === "studio:authenticated" && typeof event.data.token === "string") {
|
|
8878
|
-
return event.data.token;
|
|
8879
|
-
}
|
|
8880
|
-
}
|
|
8881
|
-
}
|
|
8882
|
-
} catch {
|
|
8883
|
-
if (opts.abortSignal.aborted) {
|
|
8884
|
-
break;
|
|
8885
|
-
}
|
|
8886
|
-
}
|
|
8887
|
-
await new Promise((resolve) => {
|
|
8888
|
-
const onAbort = () => {
|
|
8889
|
-
clearTimeout(timer);
|
|
8890
|
-
resolve();
|
|
8891
|
-
};
|
|
8892
|
-
const timer = setTimeout(() => {
|
|
8893
|
-
opts.abortSignal.removeEventListener("abort", onAbort);
|
|
8894
|
-
resolve();
|
|
8895
|
-
}, interval);
|
|
8896
|
-
opts.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
8897
|
-
});
|
|
8898
|
-
}
|
|
8899
|
-
throw new Error("Login polling aborted");
|
|
8900
|
-
}
|
|
8901
8883
|
var STUDIO_SESSION_SCOPE_HEADER = "x-bitfab-auth-scope";
|
|
8902
8884
|
var STUDIO_SESSION_SCOPE_VALUE = "session";
|
|
8903
|
-
async function requestSessionReauth(opts) {
|
|
8904
|
-
try {
|
|
8905
|
-
const res = await fetch(`${opts.serviceUrl}/api/studio/events`, {
|
|
8906
|
-
method: "POST",
|
|
8907
|
-
headers: {
|
|
8908
|
-
"Content-Type": "application/json",
|
|
8909
|
-
[STUDIO_SESSION_SCOPE_HEADER]: STUDIO_SESSION_SCOPE_VALUE
|
|
8910
|
-
},
|
|
8911
|
-
body: JSON.stringify({
|
|
8912
|
-
session: opts.sessionId,
|
|
8913
|
-
type: "agent:request-reauth",
|
|
8914
|
-
data: { redirectPath: opts.redirectPath }
|
|
8915
|
-
})
|
|
8916
|
-
});
|
|
8917
|
-
if (!res.ok) {
|
|
8918
|
-
return { ok: false };
|
|
8919
|
-
}
|
|
8920
|
-
const body = await res.json();
|
|
8921
|
-
return {
|
|
8922
|
-
ok: true,
|
|
8923
|
-
pushedEventId: body.id,
|
|
8924
|
-
signInUrl: `${opts.serviceUrl}${body.signInPath}`
|
|
8925
|
-
};
|
|
8926
|
-
} catch {
|
|
8927
|
-
return { ok: false };
|
|
8928
|
-
}
|
|
8929
|
-
}
|
|
8930
|
-
async function pollSessionAuthEvents(opts) {
|
|
8931
|
-
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
8932
|
-
let since = opts.startCursor ?? null;
|
|
8933
|
-
while (!opts.abortSignal.aborted) {
|
|
8934
|
-
try {
|
|
8935
|
-
const url2 = new URL(`${opts.serviceUrl}/api/studio/events`);
|
|
8936
|
-
url2.searchParams.set("session", opts.sessionId);
|
|
8937
|
-
if (since) {
|
|
8938
|
-
url2.searchParams.set("since", since);
|
|
8939
|
-
}
|
|
8940
|
-
const res = await fetch(url2.toString(), {
|
|
8941
|
-
headers: {
|
|
8942
|
-
[STUDIO_SESSION_SCOPE_HEADER]: STUDIO_SESSION_SCOPE_VALUE
|
|
8943
|
-
},
|
|
8944
|
-
signal: opts.abortSignal
|
|
8945
|
-
});
|
|
8946
|
-
if (res.ok) {
|
|
8947
|
-
const body = await res.json();
|
|
8948
|
-
for (const event of body.events) {
|
|
8949
|
-
since = event.id;
|
|
8950
|
-
opts.onEvent(event);
|
|
8951
|
-
}
|
|
8952
|
-
if (body.cursor) {
|
|
8953
|
-
since = body.cursor;
|
|
8954
|
-
}
|
|
8955
|
-
}
|
|
8956
|
-
} catch {
|
|
8957
|
-
if (opts.abortSignal.aborted) {
|
|
8958
|
-
return;
|
|
8959
|
-
}
|
|
8960
|
-
}
|
|
8961
|
-
await new Promise((resolve) => {
|
|
8962
|
-
const onAbort = () => {
|
|
8963
|
-
clearTimeout(timer);
|
|
8964
|
-
resolve();
|
|
8965
|
-
};
|
|
8966
|
-
const timer = setTimeout(() => {
|
|
8967
|
-
opts.abortSignal.removeEventListener("abort", onAbort);
|
|
8968
|
-
resolve();
|
|
8969
|
-
}, interval);
|
|
8970
|
-
opts.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
8971
|
-
});
|
|
8972
|
-
}
|
|
8973
|
-
}
|
|
8974
8885
|
async function fetchStreamTip(client) {
|
|
8975
8886
|
const PAGE_LIMIT = 500;
|
|
8976
8887
|
const MAX_PAGES = 10;
|
|
8977
8888
|
let tip;
|
|
8889
|
+
const authHeaders = client.apiKey ? { Authorization: `Bearer ${client.apiKey}` } : { [STUDIO_SESSION_SCOPE_HEADER]: STUDIO_SESSION_SCOPE_VALUE };
|
|
8978
8890
|
try {
|
|
8979
8891
|
for (let page = 0; page < MAX_PAGES; page++) {
|
|
8980
8892
|
const url2 = new URL(`${client.serviceUrl}/api/studio/events`);
|
|
@@ -8984,7 +8896,7 @@ async function fetchStreamTip(client) {
|
|
|
8984
8896
|
url2.searchParams.set("since", tip);
|
|
8985
8897
|
}
|
|
8986
8898
|
const res = await fetch(url2.toString(), {
|
|
8987
|
-
headers:
|
|
8899
|
+
headers: authHeaders
|
|
8988
8900
|
});
|
|
8989
8901
|
if (!res.ok) {
|
|
8990
8902
|
return tip;
|
|
@@ -9004,18 +8916,24 @@ async function fetchStreamTip(client) {
|
|
|
9004
8916
|
}
|
|
9005
8917
|
return tip;
|
|
9006
8918
|
}
|
|
9007
|
-
async function
|
|
9008
|
-
|
|
9009
|
-
|
|
8919
|
+
async function closeStudio(client, message) {
|
|
8920
|
+
return pushAgentSessionEvent(client, {
|
|
8921
|
+
type: "agent:close-studio",
|
|
8922
|
+
data: message ? { message } : {}
|
|
8923
|
+
});
|
|
8924
|
+
}
|
|
8925
|
+
async function navigateStudio(client, path18) {
|
|
8926
|
+
if (!isValidStudioRoute(path18)) {
|
|
8927
|
+
throw new Error(`Studio route not allowed: ${path18}`);
|
|
9010
8928
|
}
|
|
9011
8929
|
return pushAgentSessionEvent(client, {
|
|
9012
8930
|
type: "agent:navigate",
|
|
9013
|
-
data: { path:
|
|
8931
|
+
data: { path: path18 }
|
|
9014
8932
|
});
|
|
9015
8933
|
}
|
|
9016
8934
|
var STUDIO_NAVIGATE_ACK_TIMEOUT_MS = 12e3;
|
|
9017
8935
|
var PING_PONG_TIMEOUT_MS = 5e3;
|
|
9018
|
-
function awaitNavigateAck(client,
|
|
8936
|
+
function awaitNavigateAck(client, path18, ackTimeoutMs, startCursor) {
|
|
9019
8937
|
const abortController = new AbortController();
|
|
9020
8938
|
let timer = null;
|
|
9021
8939
|
const result = new Promise((resolve) => {
|
|
@@ -9023,7 +8941,7 @@ function awaitNavigateAck(client, path15, ackTimeoutMs, startCursor) {
|
|
|
9023
8941
|
resolve({ acked: false, reason: "timeout" });
|
|
9024
8942
|
abortController.abort();
|
|
9025
8943
|
}, ackTimeoutMs);
|
|
9026
|
-
const pathOnly =
|
|
8944
|
+
const pathOnly = path18.split("?")[0];
|
|
9027
8945
|
pollAgentSessionEvents({
|
|
9028
8946
|
serviceUrl: client.serviceUrl,
|
|
9029
8947
|
apiKey: client.apiKey,
|
|
@@ -9109,18 +9027,18 @@ function awaitPong(client, pingTs) {
|
|
|
9109
9027
|
});
|
|
9110
9028
|
});
|
|
9111
9029
|
}
|
|
9112
|
-
async function navigateStudioAndAwaitAck(client,
|
|
9030
|
+
async function navigateStudioAndAwaitAck(client, path18, opts = {}) {
|
|
9113
9031
|
const ackTimeoutMs = opts.ackTimeoutMs ?? STUDIO_NAVIGATE_ACK_TIMEOUT_MS;
|
|
9114
|
-
if (!isValidStudioRoute(
|
|
9032
|
+
if (!isValidStudioRoute(path18)) {
|
|
9115
9033
|
return { acked: false, reason: "invalid-route" };
|
|
9116
9034
|
}
|
|
9117
9035
|
const startCursor = await fetchStreamTip(client);
|
|
9118
9036
|
try {
|
|
9119
|
-
await navigateStudio(client,
|
|
9037
|
+
await navigateStudio(client, path18);
|
|
9120
9038
|
} catch {
|
|
9121
9039
|
return { acked: false, reason: "push-failed" };
|
|
9122
9040
|
}
|
|
9123
|
-
const result = await awaitNavigateAck(client,
|
|
9041
|
+
const result = await awaitNavigateAck(client, path18, ackTimeoutMs, startCursor);
|
|
9124
9042
|
if (result.acked || result.reason !== "timeout") {
|
|
9125
9043
|
return result;
|
|
9126
9044
|
}
|
|
@@ -9181,9 +9099,9 @@ var GLOBAL_CONFIG_FILE = path3.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
|
9181
9099
|
var GLOBAL_CREDENTIALS_FILE = path3.join(GLOBAL_CONFIG_DIR, "credentials.json");
|
|
9182
9100
|
var PROJECT_CONFIG_RELATIVE = path3.join(".bitfab", "config.local.json");
|
|
9183
9101
|
var PROJECT_CREDENTIALS_RELATIVE = path3.join(".bitfab", "credentials.local.json");
|
|
9184
|
-
function readJsonFile(
|
|
9102
|
+
function readJsonFile(filePath) {
|
|
9185
9103
|
try {
|
|
9186
|
-
const content = fs3.readFileSync(
|
|
9104
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
9187
9105
|
return JSON.parse(content);
|
|
9188
9106
|
} catch {
|
|
9189
9107
|
return null;
|
|
@@ -9204,18 +9122,18 @@ function findProjectFile(relativePath, startDir = process.cwd()) {
|
|
|
9204
9122
|
}
|
|
9205
9123
|
}
|
|
9206
9124
|
function getProjectConfigData() {
|
|
9207
|
-
const
|
|
9208
|
-
if (!
|
|
9125
|
+
const filePath = findProjectFile(PROJECT_CONFIG_RELATIVE);
|
|
9126
|
+
if (!filePath) {
|
|
9209
9127
|
return null;
|
|
9210
9128
|
}
|
|
9211
|
-
return readJsonFile(
|
|
9129
|
+
return readJsonFile(filePath);
|
|
9212
9130
|
}
|
|
9213
9131
|
function getProjectCredentialsData() {
|
|
9214
|
-
const
|
|
9215
|
-
if (!
|
|
9132
|
+
const filePath = findProjectFile(PROJECT_CREDENTIALS_RELATIVE);
|
|
9133
|
+
if (!filePath) {
|
|
9216
9134
|
return null;
|
|
9217
9135
|
}
|
|
9218
|
-
return readJsonFile(
|
|
9136
|
+
return readJsonFile(filePath) ?? {};
|
|
9219
9137
|
}
|
|
9220
9138
|
function getConfigData() {
|
|
9221
9139
|
return readJsonFile(GLOBAL_CONFIG_FILE) ?? {};
|
|
@@ -9345,10 +9263,34 @@ function buildBitfabRequestHeaders(apiKey, pluginVersion, platform2) {
|
|
|
9345
9263
|
|
|
9346
9264
|
// ../bitfab-plugin-lib/dist/activeStudioSession.js
|
|
9347
9265
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9266
|
+
import crypto4 from "crypto";
|
|
9267
|
+
import fs7 from "fs";
|
|
9268
|
+
import os7 from "os";
|
|
9269
|
+
import path6 from "path";
|
|
9270
|
+
|
|
9271
|
+
// ../bitfab-plugin-lib/dist/agentSessionKey.js
|
|
9348
9272
|
import crypto3 from "crypto";
|
|
9349
9273
|
import fs6 from "fs";
|
|
9350
9274
|
import os6 from "os";
|
|
9351
9275
|
import path5 from "path";
|
|
9276
|
+
var FALLBACK_KEY_PATH = path5.join(os6.homedir(), ".config", "bitfab", "agent-session-key");
|
|
9277
|
+
function readOrCreateFallbackKey() {
|
|
9278
|
+
try {
|
|
9279
|
+
const existing = fs6.readFileSync(FALLBACK_KEY_PATH, "utf-8").trim();
|
|
9280
|
+
if (existing) {
|
|
9281
|
+
return existing;
|
|
9282
|
+
}
|
|
9283
|
+
} catch {
|
|
9284
|
+
}
|
|
9285
|
+
const key = crypto3.randomUUID();
|
|
9286
|
+
fs6.mkdirSync(path5.dirname(FALLBACK_KEY_PATH), { recursive: true });
|
|
9287
|
+
fs6.writeFileSync(FALLBACK_KEY_PATH, `${key}
|
|
9288
|
+
`);
|
|
9289
|
+
return key;
|
|
9290
|
+
}
|
|
9291
|
+
function resolveAgentSessionKey() {
|
|
9292
|
+
return process.env.CLAUDE_CODE_SESSION_ID ?? process.env.CURSOR_SESSION_ID ?? process.env.CODEX_THREAD_ID ?? readOrCreateFallbackKey();
|
|
9293
|
+
}
|
|
9352
9294
|
|
|
9353
9295
|
// ../bitfab-plugin-lib/dist/getAgentSessionId.js
|
|
9354
9296
|
function getAgentSessionId() {
|
|
@@ -9356,186 +9298,104 @@ function getAgentSessionId() {
|
|
|
9356
9298
|
}
|
|
9357
9299
|
|
|
9358
9300
|
// ../bitfab-plugin-lib/dist/activeStudioSession.js
|
|
9301
|
+
var SESSIONS_DIR = path6.join(os7.homedir(), ".config", "bitfab", "sessions");
|
|
9359
9302
|
function shortHash(input) {
|
|
9360
|
-
return
|
|
9303
|
+
return crypto4.createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
9361
9304
|
}
|
|
9362
|
-
function
|
|
9363
|
-
return shortHash(
|
|
9305
|
+
function sessionFilePathForKey(key) {
|
|
9306
|
+
return path6.join(SESSIONS_DIR, `${shortHash(key)}.json`);
|
|
9364
9307
|
}
|
|
9365
|
-
function
|
|
9366
|
-
return
|
|
9308
|
+
function sessionFilePath() {
|
|
9309
|
+
return sessionFilePathForKey(resolveAgentSessionKey());
|
|
9367
9310
|
}
|
|
9368
|
-
function
|
|
9369
|
-
|
|
9370
|
-
fs6.mkdirSync(path5.dirname(target), { recursive: true });
|
|
9311
|
+
function writeStateFile(target, state) {
|
|
9312
|
+
fs7.mkdirSync(path6.dirname(target), { recursive: true });
|
|
9371
9313
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
9372
|
-
|
|
9314
|
+
fs7.writeFileSync(tmp, `${JSON.stringify(state, null, 2)}
|
|
9373
9315
|
`);
|
|
9374
|
-
|
|
9375
|
-
}
|
|
9376
|
-
function writeActiveStudioSession(session) {
|
|
9377
|
-
writeRecord({
|
|
9378
|
-
...session,
|
|
9379
|
-
pid: process.pid,
|
|
9380
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9381
|
-
});
|
|
9382
|
-
}
|
|
9383
|
-
function isProcessAlive(pid) {
|
|
9384
|
-
try {
|
|
9385
|
-
process.kill(pid, 0);
|
|
9386
|
-
return true;
|
|
9387
|
-
} catch (err) {
|
|
9388
|
-
return err.code === "EPERM";
|
|
9389
|
-
}
|
|
9390
|
-
}
|
|
9391
|
-
function processStartToken(pid) {
|
|
9392
|
-
try {
|
|
9393
|
-
const out = execFileSync2("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
9394
|
-
encoding: "utf-8",
|
|
9395
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
9396
|
-
// Bound it: a local `ps -p` is instant, but with no timeout a wedged `ps`
|
|
9397
|
-
// (severe load, a stuck process table) could block the monitor claim. On
|
|
9398
|
-
// timeout this throws, we return null, and the caller degrades to a plain
|
|
9399
|
-
// liveness check — never a hang.
|
|
9400
|
-
timeout: 2e3
|
|
9401
|
-
}).trim();
|
|
9402
|
-
return out || null;
|
|
9403
|
-
} catch {
|
|
9404
|
-
return null;
|
|
9405
|
-
}
|
|
9316
|
+
fs7.renameSync(tmp, target);
|
|
9406
9317
|
}
|
|
9407
|
-
function
|
|
9318
|
+
function readStateFile(filePath) {
|
|
9408
9319
|
try {
|
|
9409
|
-
const
|
|
9410
|
-
if (!current || current.sessionId !== sessionId) {
|
|
9411
|
-
return false;
|
|
9412
|
-
}
|
|
9413
|
-
if (!opts.force && current.pid !== process.pid && isProcessAlive(current.pid)) {
|
|
9414
|
-
return false;
|
|
9415
|
-
}
|
|
9416
|
-
fs6.unlinkSync(filePath());
|
|
9417
|
-
return true;
|
|
9418
|
-
} catch {
|
|
9419
|
-
return false;
|
|
9420
|
-
}
|
|
9421
|
-
}
|
|
9422
|
-
function readActiveStudioSessionRaw() {
|
|
9423
|
-
try {
|
|
9424
|
-
const raw = fs6.readFileSync(filePath(), "utf-8");
|
|
9320
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
9425
9321
|
const parsed = JSON.parse(raw);
|
|
9426
|
-
if (parsed.
|
|
9322
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.serviceUrl !== "string") {
|
|
9427
9323
|
return null;
|
|
9428
9324
|
}
|
|
9429
|
-
|
|
9430
|
-
return {
|
|
9431
|
-
sessionId: parsed.sessionId,
|
|
9432
|
-
serviceUrl: parsed.serviceUrl,
|
|
9433
|
-
pid: parsed.pid,
|
|
9434
|
-
startedAt: parsed.startedAt,
|
|
9435
|
-
...typeof parsed.windowPid === "number" ? { windowPid: parsed.windowPid } : {}
|
|
9436
|
-
};
|
|
9437
|
-
}
|
|
9438
|
-
return null;
|
|
9325
|
+
return parsed;
|
|
9439
9326
|
} catch {
|
|
9440
9327
|
return null;
|
|
9441
9328
|
}
|
|
9442
9329
|
}
|
|
9443
|
-
function
|
|
9444
|
-
return
|
|
9445
|
-
}
|
|
9446
|
-
function monitorLockPath() {
|
|
9447
|
-
return path5.join(os6.homedir(), ".config", "bitfab", `active-studio-session.${sessionScope()}.monitor.lock`);
|
|
9448
|
-
}
|
|
9449
|
-
function tryClaimStudioMonitor() {
|
|
9450
|
-
const lock = monitorLockPath();
|
|
9451
|
-
fs6.mkdirSync(path5.dirname(lock), { recursive: true });
|
|
9452
|
-
for (let attempt = 0; attempt < 10; attempt++) {
|
|
9453
|
-
try {
|
|
9454
|
-
const fd = fs6.openSync(lock, "wx");
|
|
9455
|
-
fs6.writeSync(fd, monitorLockBody(process.pid));
|
|
9456
|
-
fs6.closeSync(fd);
|
|
9457
|
-
stampMonitorPid();
|
|
9458
|
-
return true;
|
|
9459
|
-
} catch (err) {
|
|
9460
|
-
if (err.code !== "EEXIST") {
|
|
9461
|
-
throw err;
|
|
9462
|
-
}
|
|
9463
|
-
const owner = readLockOwner(lock);
|
|
9464
|
-
if (owner?.pid === process.pid) {
|
|
9465
|
-
return true;
|
|
9466
|
-
}
|
|
9467
|
-
if (owner && isMonitorOwnerLive(owner)) {
|
|
9468
|
-
return false;
|
|
9469
|
-
}
|
|
9470
|
-
try {
|
|
9471
|
-
fs6.unlinkSync(lock);
|
|
9472
|
-
} catch {
|
|
9473
|
-
}
|
|
9474
|
-
}
|
|
9475
|
-
}
|
|
9476
|
-
stampMonitorPid();
|
|
9477
|
-
return true;
|
|
9330
|
+
function legacySessionScope() {
|
|
9331
|
+
return shortHash(getAgentSessionId() ?? process.cwd());
|
|
9478
9332
|
}
|
|
9479
|
-
function
|
|
9480
|
-
|
|
9481
|
-
const lock = monitorLockPath();
|
|
9482
|
-
fs6.mkdirSync(path5.dirname(lock), { recursive: true });
|
|
9483
|
-
fs6.writeFileSync(lock, monitorLockBody(process.pid));
|
|
9484
|
-
} catch {
|
|
9485
|
-
}
|
|
9486
|
-
stampMonitorPid();
|
|
9333
|
+
function legacyFilePath() {
|
|
9334
|
+
return path6.join(os7.homedir(), ".config", "bitfab", `active-studio-session.${legacySessionScope()}.json`);
|
|
9487
9335
|
}
|
|
9488
|
-
function
|
|
9336
|
+
function readLegacySession() {
|
|
9489
9337
|
try {
|
|
9490
|
-
const
|
|
9491
|
-
|
|
9492
|
-
|
|
9338
|
+
const raw = fs7.readFileSync(legacyFilePath(), "utf-8");
|
|
9339
|
+
const parsed = JSON.parse(raw);
|
|
9340
|
+
if (parsed.closedAt != null || parsed.endedAt != null) {
|
|
9341
|
+
return null;
|
|
9493
9342
|
}
|
|
9494
|
-
|
|
9495
|
-
}
|
|
9496
|
-
}
|
|
9497
|
-
function monitorLockBody(pid) {
|
|
9498
|
-
return `${pid}
|
|
9499
|
-
${processStartToken(pid) ?? ""}`;
|
|
9500
|
-
}
|
|
9501
|
-
function isMonitorOwnerLive(owner) {
|
|
9502
|
-
if (!isProcessAlive(owner.pid)) {
|
|
9503
|
-
return false;
|
|
9504
|
-
}
|
|
9505
|
-
if (!owner.start) {
|
|
9506
|
-
return true;
|
|
9507
|
-
}
|
|
9508
|
-
const current = processStartToken(owner.pid);
|
|
9509
|
-
if (current === null) {
|
|
9510
|
-
return true;
|
|
9511
|
-
}
|
|
9512
|
-
return current === owner.start;
|
|
9513
|
-
}
|
|
9514
|
-
function readLockOwner(lock) {
|
|
9515
|
-
try {
|
|
9516
|
-
const [pidLine = "", ...rest] = fs6.readFileSync(lock, "utf-8").split("\n");
|
|
9517
|
-
const pid = Number.parseInt(pidLine.trim(), 10);
|
|
9518
|
-
if (!Number.isInteger(pid)) {
|
|
9343
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.serviceUrl !== "string" || typeof parsed.pid !== "number" || typeof parsed.startedAt !== "string") {
|
|
9519
9344
|
return null;
|
|
9520
9345
|
}
|
|
9521
|
-
|
|
9346
|
+
const legacy = parsed;
|
|
9347
|
+
return {
|
|
9348
|
+
sessionId: legacy.sessionId,
|
|
9349
|
+
serviceUrl: legacy.serviceUrl,
|
|
9350
|
+
windowPid: legacy.windowPid ?? null,
|
|
9351
|
+
pollerPid: legacy.pid,
|
|
9352
|
+
source: "direct",
|
|
9353
|
+
openedAt: legacy.startedAt,
|
|
9354
|
+
currentPath: null,
|
|
9355
|
+
authenticated: true,
|
|
9356
|
+
windowState: "open",
|
|
9357
|
+
agentIntent: "open",
|
|
9358
|
+
lastBrowserPingAt: null,
|
|
9359
|
+
browserConnected: false,
|
|
9360
|
+
lastEventAt: null,
|
|
9361
|
+
lastEventCursor: null,
|
|
9362
|
+
agentSessionKey: null
|
|
9363
|
+
};
|
|
9522
9364
|
} catch {
|
|
9523
9365
|
return null;
|
|
9524
9366
|
}
|
|
9525
9367
|
}
|
|
9526
|
-
function
|
|
9527
|
-
const
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9368
|
+
function writeActiveStudioSession(init) {
|
|
9369
|
+
const state = {
|
|
9370
|
+
sessionId: init.sessionId,
|
|
9371
|
+
serviceUrl: init.serviceUrl,
|
|
9372
|
+
windowPid: init.windowPid ?? null,
|
|
9373
|
+
pollerPid: process.pid,
|
|
9374
|
+
source: init.source ?? "direct",
|
|
9375
|
+
openedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9376
|
+
currentPath: init.currentPath ?? null,
|
|
9377
|
+
authenticated: init.authenticated ?? true,
|
|
9378
|
+
windowState: "open",
|
|
9379
|
+
agentIntent: "open",
|
|
9380
|
+
lastBrowserPingAt: null,
|
|
9381
|
+
browserConnected: false,
|
|
9382
|
+
lastEventAt: null,
|
|
9383
|
+
lastEventCursor: null,
|
|
9384
|
+
agentSessionKey: null
|
|
9385
|
+
};
|
|
9386
|
+
writeStateFile(sessionFilePath(), state);
|
|
9531
9387
|
}
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
function blockingNote(until) {
|
|
9535
|
-
return `BLOCKING: this command does not exit until ${until}. Agents: run it in the background and read its stdout incrementally. A foreground call hangs the conversation until the process exits.`;
|
|
9388
|
+
function readActiveStudioSession() {
|
|
9389
|
+
return readStateFile(sessionFilePath()) ?? readLegacySession();
|
|
9536
9390
|
}
|
|
9537
|
-
function
|
|
9538
|
-
|
|
9391
|
+
function updateActiveStudioSession(updates) {
|
|
9392
|
+
const current = readStateFile(sessionFilePath());
|
|
9393
|
+
if (!current) {
|
|
9394
|
+
return null;
|
|
9395
|
+
}
|
|
9396
|
+
const updated = { ...current, ...updates };
|
|
9397
|
+
writeStateFile(sessionFilePath(), updated);
|
|
9398
|
+
return updated;
|
|
9539
9399
|
}
|
|
9540
9400
|
|
|
9541
9401
|
// ../bitfab-plugin-lib/dist/parseArgs.js
|
|
@@ -9604,42 +9464,15 @@ function extractCommandName(argv) {
|
|
|
9604
9464
|
return argv[1]?.replace(/.*\//, "").replace(/\.[jt]s$/, "") ?? "command";
|
|
9605
9465
|
}
|
|
9606
9466
|
|
|
9607
|
-
// ../bitfab-plugin-lib/dist/
|
|
9608
|
-
|
|
9609
|
-
function createWindowCloseArbiter(handlers, graceMs = WINDOW_CLOSE_GRACE_PERIOD_MS) {
|
|
9610
|
-
let graceTimer = null;
|
|
9611
|
-
const cancel3 = () => {
|
|
9612
|
-
if (graceTimer) {
|
|
9613
|
-
clearTimeout(graceTimer);
|
|
9614
|
-
graceTimer = null;
|
|
9615
|
-
}
|
|
9616
|
-
};
|
|
9617
|
-
return {
|
|
9618
|
-
handleEvent(event) {
|
|
9619
|
-
if (event.type === "studio:window-closed") {
|
|
9620
|
-
cancel3();
|
|
9621
|
-
graceTimer = setTimeout(() => {
|
|
9622
|
-
graceTimer = null;
|
|
9623
|
-
handlers.onConfirmed(event);
|
|
9624
|
-
}, graceMs);
|
|
9625
|
-
return;
|
|
9626
|
-
}
|
|
9627
|
-
if (event.type === "studio:session-started" && graceTimer) {
|
|
9628
|
-
cancel3();
|
|
9629
|
-
handlers.onRefuted?.();
|
|
9630
|
-
}
|
|
9631
|
-
},
|
|
9632
|
-
hasPendingCandidate: () => graceTimer != null,
|
|
9633
|
-
cancel: cancel3
|
|
9634
|
-
};
|
|
9635
|
-
}
|
|
9467
|
+
// ../bitfab-plugin-lib/dist/studioChannel.js
|
|
9468
|
+
import crypto6 from "crypto";
|
|
9636
9469
|
|
|
9637
9470
|
// ../bitfab-plugin-lib/dist/browser.js
|
|
9638
9471
|
import { execSync, spawn } from "child_process";
|
|
9639
|
-
import
|
|
9640
|
-
import
|
|
9472
|
+
import fs8 from "fs";
|
|
9473
|
+
import os8 from "os";
|
|
9641
9474
|
function getChromiumBrowsers() {
|
|
9642
|
-
const platform2 =
|
|
9475
|
+
const platform2 = os8.platform();
|
|
9643
9476
|
if (platform2 === "darwin") {
|
|
9644
9477
|
return [
|
|
9645
9478
|
{
|
|
@@ -9727,7 +9560,7 @@ function getChromiumBrowsers() {
|
|
|
9727
9560
|
function findExistingBinary(paths) {
|
|
9728
9561
|
for (const candidate of paths) {
|
|
9729
9562
|
if (candidate.includes("/") || candidate.includes("\\")) {
|
|
9730
|
-
if (
|
|
9563
|
+
if (fs8.existsSync(candidate)) {
|
|
9731
9564
|
return candidate;
|
|
9732
9565
|
}
|
|
9733
9566
|
} else {
|
|
@@ -9741,10 +9574,10 @@ function findExistingBinary(paths) {
|
|
|
9741
9574
|
return null;
|
|
9742
9575
|
}
|
|
9743
9576
|
function getDefaultBrowserId() {
|
|
9744
|
-
const platform2 =
|
|
9577
|
+
const platform2 = os8.platform();
|
|
9745
9578
|
try {
|
|
9746
9579
|
if (platform2 === "darwin") {
|
|
9747
|
-
const plistPath = `${
|
|
9580
|
+
const plistPath = `${os8.homedir()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
|
|
9748
9581
|
const json2 = execSync(`plutil -convert json -o - "${plistPath}"`, {
|
|
9749
9582
|
stdio: ["ignore", "pipe", "ignore"],
|
|
9750
9583
|
encoding: "utf-8",
|
|
@@ -9785,7 +9618,7 @@ function matchChromiumBrowser(defaultId, browsers) {
|
|
|
9785
9618
|
var SCREEN_FRACTION = 0.95;
|
|
9786
9619
|
function getWorkArea() {
|
|
9787
9620
|
try {
|
|
9788
|
-
const platform2 =
|
|
9621
|
+
const platform2 = os8.platform();
|
|
9789
9622
|
if (platform2 === "darwin") {
|
|
9790
9623
|
const out = execSync(`osascript -l JavaScript -e 'ObjC.import("AppKit"); const s = $.NSScreen.mainScreen; const f = s.frame; const v = s.visibleFrame; const topY = f.size.height - (v.origin.y + v.size.height); JSON.stringify([v.origin.x, topY, v.size.width, v.size.height])'`, {
|
|
9791
9624
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -9845,6 +9678,9 @@ function getWindowSizeArgs() {
|
|
|
9845
9678
|
return computeWindowArgs(getWorkArea());
|
|
9846
9679
|
}
|
|
9847
9680
|
function spawnDetached(command, args) {
|
|
9681
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
9682
|
+
throw new Error("spawnDetached called in a test environment without being mocked");
|
|
9683
|
+
}
|
|
9848
9684
|
const child = spawn(command, [...args], { detached: true, stdio: "ignore" });
|
|
9849
9685
|
child.on("error", () => {
|
|
9850
9686
|
});
|
|
@@ -9855,14 +9691,14 @@ function openChromiumApp(binaryPath, url2, sizeArgs) {
|
|
|
9855
9691
|
return spawnDetached(binaryPath, [`--app=${url2}`, ...sizeArgs]);
|
|
9856
9692
|
}
|
|
9857
9693
|
function openOsDefault(url2) {
|
|
9858
|
-
const platform2 =
|
|
9694
|
+
const platform2 = os8.platform();
|
|
9859
9695
|
if (platform2 === "darwin") {
|
|
9860
|
-
spawnDetached("open", [url2]);
|
|
9861
|
-
}
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
spawnDetached("xdg-open", [url2]);
|
|
9696
|
+
return spawnDetached("open", [url2]);
|
|
9697
|
+
}
|
|
9698
|
+
if (platform2 === "win32") {
|
|
9699
|
+
return spawnDetached("cmd", ["/c", "start", "", url2]);
|
|
9865
9700
|
}
|
|
9701
|
+
return spawnDetached("xdg-open", [url2]);
|
|
9866
9702
|
}
|
|
9867
9703
|
function isCodexOnMacos() {
|
|
9868
9704
|
if (process.platform !== "darwin") {
|
|
@@ -9876,7 +9712,22 @@ function shouldDisableChromelessWindows() {
|
|
|
9876
9712
|
}
|
|
9877
9713
|
return isCodexOnMacos();
|
|
9878
9714
|
}
|
|
9715
|
+
var WINDOW_OPEN_RATE_WINDOW_MS = 3e4;
|
|
9716
|
+
var WINDOW_OPEN_RATE_LIMIT = 3;
|
|
9717
|
+
var recentOpens = [];
|
|
9879
9718
|
function openChromelessWindow(url2) {
|
|
9719
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
9720
|
+
throw new Error("openChromelessWindow called in a test environment without being mocked");
|
|
9721
|
+
}
|
|
9722
|
+
const now = Date.now();
|
|
9723
|
+
while (recentOpens.length > 0 && now - recentOpens[0] > WINDOW_OPEN_RATE_WINDOW_MS) {
|
|
9724
|
+
recentOpens.shift();
|
|
9725
|
+
}
|
|
9726
|
+
if (recentOpens.length >= WINDOW_OPEN_RATE_LIMIT) {
|
|
9727
|
+
console.error(`[browser] rate limit: ${recentOpens.length} windows opened in the last ${WINDOW_OPEN_RATE_WINDOW_MS / 1e3}s, refusing to open another`);
|
|
9728
|
+
return null;
|
|
9729
|
+
}
|
|
9730
|
+
recentOpens.push(now);
|
|
9880
9731
|
if (shouldDisableChromelessWindows()) {
|
|
9881
9732
|
openOsDefault(url2);
|
|
9882
9733
|
return null;
|
|
@@ -9932,11 +9783,555 @@ async function createStudioSession({ serviceUrl, apiKey, sessionId, initialPath
|
|
|
9932
9783
|
writeActiveStudioSession({
|
|
9933
9784
|
sessionId,
|
|
9934
9785
|
serviceUrl,
|
|
9935
|
-
windowPid
|
|
9786
|
+
windowPid,
|
|
9787
|
+
currentPath: initialPath
|
|
9936
9788
|
});
|
|
9937
9789
|
return { sessionId, serviceUrl };
|
|
9938
9790
|
}
|
|
9939
9791
|
|
|
9792
|
+
// ../bitfab-plugin-lib/dist/daemon/client.js
|
|
9793
|
+
import net2 from "net";
|
|
9794
|
+
|
|
9795
|
+
// ../bitfab-plugin-lib/dist/daemon/ensure.js
|
|
9796
|
+
import { fork } from "child_process";
|
|
9797
|
+
import crypto5 from "crypto";
|
|
9798
|
+
import fs9 from "fs";
|
|
9799
|
+
import net from "net";
|
|
9800
|
+
import path8 from "path";
|
|
9801
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9802
|
+
|
|
9803
|
+
// ../bitfab-plugin-lib/dist/daemon/protocol.js
|
|
9804
|
+
import os9 from "os";
|
|
9805
|
+
import path7 from "path";
|
|
9806
|
+
var BITFAB_CONFIG_DIR = path7.join(os9.homedir(), ".config", "bitfab");
|
|
9807
|
+
var SOCKET_PATH = path7.join(BITFAB_CONFIG_DIR, "studio-daemon.sock");
|
|
9808
|
+
var PID_FILE_PATH = path7.join(BITFAB_CONFIG_DIR, "studio-daemon.pid");
|
|
9809
|
+
var BROWSER_HEALTH_INTERVAL_MS = 10 * 1e3;
|
|
9810
|
+
var ENSURE_POLL_INTERVAL_MS = 200;
|
|
9811
|
+
var ENSURE_TIMEOUT_MS = 5e3;
|
|
9812
|
+
var PING_TIMEOUT_MS = 2e3;
|
|
9813
|
+
function serialize(msg) {
|
|
9814
|
+
return `${JSON.stringify(msg)}
|
|
9815
|
+
`;
|
|
9816
|
+
}
|
|
9817
|
+
function isResponse(msg) {
|
|
9818
|
+
return typeof msg === "object" && msg !== null && "ok" in msg;
|
|
9819
|
+
}
|
|
9820
|
+
function isPush(msg) {
|
|
9821
|
+
return typeof msg === "object" && msg !== null && "event" in msg;
|
|
9822
|
+
}
|
|
9823
|
+
|
|
9824
|
+
// ../bitfab-plugin-lib/dist/daemon/ensure.js
|
|
9825
|
+
var LOCK_STALE_MS = 1e4;
|
|
9826
|
+
function processIsAlive(pid) {
|
|
9827
|
+
try {
|
|
9828
|
+
process.kill(pid, 0);
|
|
9829
|
+
return true;
|
|
9830
|
+
} catch (err) {
|
|
9831
|
+
return err.code !== "ESRCH";
|
|
9832
|
+
}
|
|
9833
|
+
}
|
|
9834
|
+
function readPid(pidPath) {
|
|
9835
|
+
try {
|
|
9836
|
+
const raw = fs9.readFileSync(pidPath, "utf-8").trim();
|
|
9837
|
+
const pid = Number.parseInt(raw, 10);
|
|
9838
|
+
return Number.isNaN(pid) ? null : pid;
|
|
9839
|
+
} catch {
|
|
9840
|
+
return null;
|
|
9841
|
+
}
|
|
9842
|
+
}
|
|
9843
|
+
async function pingSocket(socketPath) {
|
|
9844
|
+
return new Promise((resolve) => {
|
|
9845
|
+
const timeout = setTimeout(() => {
|
|
9846
|
+
socket.destroy();
|
|
9847
|
+
resolve({ alive: false, build: null });
|
|
9848
|
+
}, PING_TIMEOUT_MS);
|
|
9849
|
+
const socket = net.createConnection(socketPath, () => {
|
|
9850
|
+
socket.write('{"cmd":"ping"}\n');
|
|
9851
|
+
});
|
|
9852
|
+
let buffer = "";
|
|
9853
|
+
socket.on("data", (chunk) => {
|
|
9854
|
+
buffer += chunk.toString();
|
|
9855
|
+
if (buffer.includes("\n")) {
|
|
9856
|
+
clearTimeout(timeout);
|
|
9857
|
+
socket.destroy();
|
|
9858
|
+
try {
|
|
9859
|
+
const msg = JSON.parse(buffer.split("\n")[0]);
|
|
9860
|
+
resolve({ alive: msg.ok === true, build: msg.build ?? null });
|
|
9861
|
+
} catch {
|
|
9862
|
+
resolve({ alive: false, build: null });
|
|
9863
|
+
}
|
|
9864
|
+
}
|
|
9865
|
+
});
|
|
9866
|
+
socket.on("error", () => {
|
|
9867
|
+
clearTimeout(timeout);
|
|
9868
|
+
resolve({ alive: false, build: null });
|
|
9869
|
+
});
|
|
9870
|
+
});
|
|
9871
|
+
}
|
|
9872
|
+
async function waitForSocket(socketPath, timeoutMs) {
|
|
9873
|
+
const deadline = Date.now() + timeoutMs;
|
|
9874
|
+
while (Date.now() < deadline) {
|
|
9875
|
+
const { alive } = await pingSocket(socketPath);
|
|
9876
|
+
if (alive) {
|
|
9877
|
+
return;
|
|
9878
|
+
}
|
|
9879
|
+
await new Promise((r) => setTimeout(r, ENSURE_POLL_INTERVAL_MS));
|
|
9880
|
+
}
|
|
9881
|
+
throw new Error(`daemon did not become ready within ${timeoutMs}ms`);
|
|
9882
|
+
}
|
|
9883
|
+
function localBuildFingerprint() {
|
|
9884
|
+
try {
|
|
9885
|
+
const thisDir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
9886
|
+
const serverPath = path8.join(thisDir, "server.js");
|
|
9887
|
+
const content = fs9.readFileSync(serverPath);
|
|
9888
|
+
return crypto5.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
9889
|
+
} catch {
|
|
9890
|
+
return "unknown";
|
|
9891
|
+
}
|
|
9892
|
+
}
|
|
9893
|
+
async function killDaemon(pidPath, socketPath) {
|
|
9894
|
+
const pid = readPid(pidPath);
|
|
9895
|
+
if (pid !== null) {
|
|
9896
|
+
try {
|
|
9897
|
+
process.kill(pid, "SIGTERM");
|
|
9898
|
+
} catch {
|
|
9899
|
+
}
|
|
9900
|
+
const deadline = Date.now() + 5e3;
|
|
9901
|
+
while (processIsAlive(pid) && Date.now() < deadline) {
|
|
9902
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
9903
|
+
}
|
|
9904
|
+
if (processIsAlive(pid)) {
|
|
9905
|
+
try {
|
|
9906
|
+
process.kill(pid, "SIGKILL");
|
|
9907
|
+
} catch {
|
|
9908
|
+
}
|
|
9909
|
+
}
|
|
9910
|
+
}
|
|
9911
|
+
cleanupStaleFiles(socketPath, pidPath);
|
|
9912
|
+
}
|
|
9913
|
+
function spawnDaemon(socketPath, pidPath) {
|
|
9914
|
+
const thisDir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
9915
|
+
const mainPath = path8.join(thisDir, "main.js");
|
|
9916
|
+
if (!fs9.existsSync(mainPath)) {
|
|
9917
|
+
throw new DaemonUnavailableError(`daemon entry point not found: ${mainPath}`);
|
|
9918
|
+
}
|
|
9919
|
+
const child = fork(mainPath, [socketPath, pidPath], {
|
|
9920
|
+
detached: true,
|
|
9921
|
+
stdio: "ignore",
|
|
9922
|
+
execArgv: []
|
|
9923
|
+
});
|
|
9924
|
+
child.unref();
|
|
9925
|
+
}
|
|
9926
|
+
function cleanupStaleFiles(socketPath, pidPath) {
|
|
9927
|
+
try {
|
|
9928
|
+
fs9.unlinkSync(socketPath);
|
|
9929
|
+
} catch {
|
|
9930
|
+
}
|
|
9931
|
+
try {
|
|
9932
|
+
fs9.unlinkSync(pidPath);
|
|
9933
|
+
} catch {
|
|
9934
|
+
}
|
|
9935
|
+
}
|
|
9936
|
+
function lockPath(socketPath) {
|
|
9937
|
+
return `${socketPath}.lock`;
|
|
9938
|
+
}
|
|
9939
|
+
function acquireLock(socketPath) {
|
|
9940
|
+
const lock = lockPath(socketPath);
|
|
9941
|
+
try {
|
|
9942
|
+
fs9.mkdirSync(path8.dirname(lock), { recursive: true });
|
|
9943
|
+
fs9.writeFileSync(lock, `${process.pid}
|
|
9944
|
+
`, { flag: "wx" });
|
|
9945
|
+
return true;
|
|
9946
|
+
} catch (err) {
|
|
9947
|
+
if (err.code !== "EEXIST") {
|
|
9948
|
+
return true;
|
|
9949
|
+
}
|
|
9950
|
+
try {
|
|
9951
|
+
const stat = fs9.statSync(lock);
|
|
9952
|
+
if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) {
|
|
9953
|
+
fs9.unlinkSync(lock);
|
|
9954
|
+
fs9.writeFileSync(lock, `${process.pid}
|
|
9955
|
+
`, { flag: "wx" });
|
|
9956
|
+
return true;
|
|
9957
|
+
}
|
|
9958
|
+
} catch {
|
|
9959
|
+
}
|
|
9960
|
+
return false;
|
|
9961
|
+
}
|
|
9962
|
+
}
|
|
9963
|
+
function releaseLock(socketPath) {
|
|
9964
|
+
try {
|
|
9965
|
+
fs9.unlinkSync(lockPath(socketPath));
|
|
9966
|
+
} catch {
|
|
9967
|
+
}
|
|
9968
|
+
}
|
|
9969
|
+
async function ensureDaemon(socketPath = SOCKET_PATH, pidPath = PID_FILE_PATH) {
|
|
9970
|
+
const pid = readPid(pidPath);
|
|
9971
|
+
if (pid !== null && processIsAlive(pid)) {
|
|
9972
|
+
const ping = await pingSocket(socketPath);
|
|
9973
|
+
if (ping.alive) {
|
|
9974
|
+
const localBuild = localBuildFingerprint();
|
|
9975
|
+
if (ping.build && ping.build !== localBuild && localBuild !== "unknown") {
|
|
9976
|
+
console.error(`[daemon] build mismatch (running: ${ping.build}, local: ${localBuild}), restarting`);
|
|
9977
|
+
await killDaemon(pidPath, socketPath);
|
|
9978
|
+
} else {
|
|
9979
|
+
return;
|
|
9980
|
+
}
|
|
9981
|
+
}
|
|
9982
|
+
}
|
|
9983
|
+
if (!acquireLock(socketPath)) {
|
|
9984
|
+
await waitForSocket(socketPath, ENSURE_TIMEOUT_MS);
|
|
9985
|
+
return;
|
|
9986
|
+
}
|
|
9987
|
+
try {
|
|
9988
|
+
cleanupStaleFiles(socketPath, pidPath);
|
|
9989
|
+
spawnDaemon(socketPath, pidPath);
|
|
9990
|
+
await waitForSocket(socketPath, ENSURE_TIMEOUT_MS);
|
|
9991
|
+
} finally {
|
|
9992
|
+
releaseLock(socketPath);
|
|
9993
|
+
}
|
|
9994
|
+
}
|
|
9995
|
+
|
|
9996
|
+
// ../bitfab-plugin-lib/dist/daemon/client.js
|
|
9997
|
+
var DaemonClient = class {
|
|
9998
|
+
socket = null;
|
|
9999
|
+
buffer = "";
|
|
10000
|
+
responseQueue = [];
|
|
10001
|
+
eventHandler = null;
|
|
10002
|
+
socketPath;
|
|
10003
|
+
constructor(socketPath) {
|
|
10004
|
+
this.socketPath = socketPath ?? SOCKET_PATH;
|
|
10005
|
+
}
|
|
10006
|
+
async connect() {
|
|
10007
|
+
await ensureDaemon(this.socketPath);
|
|
10008
|
+
return new Promise((resolve, reject) => {
|
|
10009
|
+
const socket = net2.createConnection(this.socketPath, () => {
|
|
10010
|
+
this.socket = socket;
|
|
10011
|
+
resolve();
|
|
10012
|
+
});
|
|
10013
|
+
socket.on("data", (chunk) => {
|
|
10014
|
+
this.buffer += chunk.toString();
|
|
10015
|
+
this.drain();
|
|
10016
|
+
});
|
|
10017
|
+
socket.on("error", (err) => {
|
|
10018
|
+
if (!this.socket) {
|
|
10019
|
+
reject(err);
|
|
10020
|
+
return;
|
|
10021
|
+
}
|
|
10022
|
+
this.socket = null;
|
|
10023
|
+
});
|
|
10024
|
+
socket.on("close", () => {
|
|
10025
|
+
this.socket = null;
|
|
10026
|
+
});
|
|
10027
|
+
});
|
|
10028
|
+
}
|
|
10029
|
+
drain() {
|
|
10030
|
+
let idx = this.buffer.indexOf("\n");
|
|
10031
|
+
while (idx !== -1) {
|
|
10032
|
+
const line = this.buffer.slice(0, idx).trim();
|
|
10033
|
+
this.buffer = this.buffer.slice(idx + 1);
|
|
10034
|
+
if (line) {
|
|
10035
|
+
try {
|
|
10036
|
+
const msg = JSON.parse(line);
|
|
10037
|
+
if (isResponse(msg) && this.responseQueue.length > 0) {
|
|
10038
|
+
const resolver = this.responseQueue.shift();
|
|
10039
|
+
resolver(msg);
|
|
10040
|
+
} else if (isPush(msg) && this.eventHandler) {
|
|
10041
|
+
this.eventHandler(msg);
|
|
10042
|
+
}
|
|
10043
|
+
} catch {
|
|
10044
|
+
}
|
|
10045
|
+
}
|
|
10046
|
+
idx = this.buffer.indexOf("\n");
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
10049
|
+
sendCommand(cmd) {
|
|
10050
|
+
if (!this.socket) {
|
|
10051
|
+
return Promise.reject(new Error("not connected"));
|
|
10052
|
+
}
|
|
10053
|
+
return new Promise((resolve, reject) => {
|
|
10054
|
+
this.responseQueue.push(resolve);
|
|
10055
|
+
try {
|
|
10056
|
+
this.socket.write(serialize(cmd));
|
|
10057
|
+
} catch (err) {
|
|
10058
|
+
this.responseQueue.pop();
|
|
10059
|
+
reject(err);
|
|
10060
|
+
}
|
|
10061
|
+
});
|
|
10062
|
+
}
|
|
10063
|
+
async open(opts) {
|
|
10064
|
+
const cmd = { cmd: "open", ...opts };
|
|
10065
|
+
const res = await this.sendCommand(cmd);
|
|
10066
|
+
if (!res.ok) {
|
|
10067
|
+
throw new Error(res.error);
|
|
10068
|
+
}
|
|
10069
|
+
return res;
|
|
10070
|
+
}
|
|
10071
|
+
async navigate(key, navPath) {
|
|
10072
|
+
const res = await this.sendCommand({ cmd: "navigate", key, path: navPath });
|
|
10073
|
+
if (!res.ok) {
|
|
10074
|
+
throw new Error(res.error);
|
|
10075
|
+
}
|
|
10076
|
+
return res;
|
|
10077
|
+
}
|
|
10078
|
+
async close(key) {
|
|
10079
|
+
const res = await this.sendCommand({ cmd: "close", key });
|
|
10080
|
+
if (!res.ok) {
|
|
10081
|
+
throw new Error(res.error);
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
10084
|
+
async detach(key) {
|
|
10085
|
+
const res = await this.sendCommand({ cmd: "detach", key });
|
|
10086
|
+
if (!res.ok) {
|
|
10087
|
+
throw new Error(res.error);
|
|
10088
|
+
}
|
|
10089
|
+
}
|
|
10090
|
+
async status() {
|
|
10091
|
+
const res = await this.sendCommand({ cmd: "status" });
|
|
10092
|
+
if (!res.ok) {
|
|
10093
|
+
throw new Error(res.error);
|
|
10094
|
+
}
|
|
10095
|
+
return res;
|
|
10096
|
+
}
|
|
10097
|
+
async ping() {
|
|
10098
|
+
const res = await this.sendCommand({ cmd: "ping" });
|
|
10099
|
+
if (!res.ok) {
|
|
10100
|
+
throw new Error(res.error);
|
|
10101
|
+
}
|
|
10102
|
+
}
|
|
10103
|
+
onEvent(handler) {
|
|
10104
|
+
this.eventHandler = handler;
|
|
10105
|
+
}
|
|
10106
|
+
destroy() {
|
|
10107
|
+
this.eventHandler = null;
|
|
10108
|
+
this.responseQueue = [];
|
|
10109
|
+
if (this.socket) {
|
|
10110
|
+
this.socket.destroy();
|
|
10111
|
+
this.socket = null;
|
|
10112
|
+
}
|
|
10113
|
+
}
|
|
10114
|
+
};
|
|
10115
|
+
|
|
10116
|
+
// ../bitfab-plugin-lib/dist/studioChannel.js
|
|
10117
|
+
var DirectChannel = class {
|
|
10118
|
+
apiKey;
|
|
10119
|
+
serviceUrl;
|
|
10120
|
+
constructor(apiKey, serviceUrl) {
|
|
10121
|
+
this.apiKey = apiKey;
|
|
10122
|
+
this.serviceUrl = serviceUrl;
|
|
10123
|
+
}
|
|
10124
|
+
setApiKey(apiKey) {
|
|
10125
|
+
this.apiKey = apiKey;
|
|
10126
|
+
}
|
|
10127
|
+
async openOrNavigate(path18, opts) {
|
|
10128
|
+
if (!this.apiKey) {
|
|
10129
|
+
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)}`;
|
|
10135
|
+
const windowPid = openChromelessWindow(url2);
|
|
10136
|
+
writeActiveStudioSession({
|
|
10137
|
+
sessionId: sessionId2,
|
|
10138
|
+
serviceUrl: this.serviceUrl,
|
|
10139
|
+
windowPid,
|
|
10140
|
+
currentPath: initialPath
|
|
10141
|
+
});
|
|
10142
|
+
return { sessionId: sessionId2, opened: true };
|
|
10143
|
+
}
|
|
10144
|
+
const existing = readActiveStudioSession();
|
|
10145
|
+
if (existing && existing.windowState !== "closed") {
|
|
10146
|
+
const ack = await navigateStudioAndAwaitAck({
|
|
10147
|
+
serviceUrl: existing.serviceUrl,
|
|
10148
|
+
apiKey: this.apiKey,
|
|
10149
|
+
sessionId: existing.sessionId
|
|
10150
|
+
}, path18);
|
|
10151
|
+
if (!ack.acked) {
|
|
10152
|
+
throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
|
|
10153
|
+
}
|
|
10154
|
+
return { sessionId: existing.sessionId, opened: false };
|
|
10155
|
+
}
|
|
10156
|
+
const sessionId = opts?.sessionId ?? crypto6.randomUUID();
|
|
10157
|
+
const session = await createStudioSession({
|
|
10158
|
+
serviceUrl: this.serviceUrl,
|
|
10159
|
+
apiKey: this.apiKey,
|
|
10160
|
+
sessionId,
|
|
10161
|
+
initialPath: path18,
|
|
10162
|
+
clientHeaders: opts?.clientHeaders
|
|
10163
|
+
});
|
|
10164
|
+
return { sessionId: session.sessionId, opened: true };
|
|
10165
|
+
}
|
|
10166
|
+
async close(sessionId, message) {
|
|
10167
|
+
await closeStudio({ serviceUrl: this.serviceUrl, apiKey: this.apiKey, sessionId }, message);
|
|
10168
|
+
}
|
|
10169
|
+
subscribe(sessionId, onEvent, onError) {
|
|
10170
|
+
const abortController = new AbortController();
|
|
10171
|
+
const done = (async () => {
|
|
10172
|
+
const startCursor = await fetchStreamTip({
|
|
10173
|
+
serviceUrl: this.serviceUrl,
|
|
10174
|
+
apiKey: this.apiKey,
|
|
10175
|
+
sessionId
|
|
10176
|
+
});
|
|
10177
|
+
if (abortController.signal.aborted) {
|
|
10178
|
+
return;
|
|
10179
|
+
}
|
|
10180
|
+
await pollAgentSessionEvents({
|
|
10181
|
+
serviceUrl: this.serviceUrl,
|
|
10182
|
+
apiKey: this.apiKey,
|
|
10183
|
+
sessionId,
|
|
10184
|
+
startCursor,
|
|
10185
|
+
abortSignal: abortController.signal,
|
|
10186
|
+
onEvent,
|
|
10187
|
+
onError
|
|
10188
|
+
});
|
|
10189
|
+
})().catch((err) => {
|
|
10190
|
+
if (!abortController.signal.aborted) {
|
|
10191
|
+
onError(err instanceof Error ? err : new Error(String(err)));
|
|
10192
|
+
}
|
|
10193
|
+
});
|
|
10194
|
+
return { abort: () => abortController.abort(), done };
|
|
10195
|
+
}
|
|
10196
|
+
destroy() {
|
|
10197
|
+
}
|
|
10198
|
+
};
|
|
10199
|
+
var DaemonChannel = class _DaemonChannel {
|
|
10200
|
+
client;
|
|
10201
|
+
key;
|
|
10202
|
+
apiKey;
|
|
10203
|
+
serviceUrl;
|
|
10204
|
+
doneResolve = null;
|
|
10205
|
+
constructor(client, key, apiKey, serviceUrl) {
|
|
10206
|
+
this.client = client;
|
|
10207
|
+
this.key = key;
|
|
10208
|
+
this.apiKey = apiKey;
|
|
10209
|
+
this.serviceUrl = serviceUrl;
|
|
10210
|
+
}
|
|
10211
|
+
static async connect(apiKey, serviceUrl) {
|
|
10212
|
+
const client = new DaemonClient();
|
|
10213
|
+
await client.connect();
|
|
10214
|
+
const key = resolveAgentSessionKey();
|
|
10215
|
+
return new _DaemonChannel(client, key, apiKey, serviceUrl);
|
|
10216
|
+
}
|
|
10217
|
+
setApiKey(apiKey) {
|
|
10218
|
+
this.apiKey = apiKey;
|
|
10219
|
+
}
|
|
10220
|
+
async openOrNavigate(path18, opts) {
|
|
10221
|
+
const openRes = await this.client.open({
|
|
10222
|
+
key: this.key,
|
|
10223
|
+
serviceUrl: this.serviceUrl,
|
|
10224
|
+
apiKey: this.apiKey,
|
|
10225
|
+
sessionId: opts?.sessionId,
|
|
10226
|
+
initialPath: path18,
|
|
10227
|
+
clientHeaders: opts?.clientHeaders
|
|
10228
|
+
});
|
|
10229
|
+
if (openRes.reconnected) {
|
|
10230
|
+
const navRes = await this.client.navigate(this.key, path18);
|
|
10231
|
+
if (!navRes.acked) {
|
|
10232
|
+
this.client.destroy();
|
|
10233
|
+
throw new StudioNavigationError(navRes.reason ?? "unknown", void 0, openRes.sessionId);
|
|
10234
|
+
}
|
|
10235
|
+
writeActiveStudioSession({
|
|
10236
|
+
sessionId: openRes.sessionId,
|
|
10237
|
+
serviceUrl: this.serviceUrl,
|
|
10238
|
+
source: "daemon",
|
|
10239
|
+
currentPath: path18
|
|
10240
|
+
});
|
|
10241
|
+
return { sessionId: openRes.sessionId, opened: false };
|
|
10242
|
+
}
|
|
10243
|
+
writeActiveStudioSession({
|
|
10244
|
+
sessionId: openRes.sessionId,
|
|
10245
|
+
serviceUrl: this.serviceUrl,
|
|
10246
|
+
source: "daemon",
|
|
10247
|
+
currentPath: path18
|
|
10248
|
+
});
|
|
10249
|
+
return { sessionId: openRes.sessionId, opened: true };
|
|
10250
|
+
}
|
|
10251
|
+
async close(sessionId, message) {
|
|
10252
|
+
await Promise.all([
|
|
10253
|
+
closeStudio({ serviceUrl: this.serviceUrl, apiKey: this.apiKey, sessionId }, message),
|
|
10254
|
+
this.client.close(this.key).catch(() => {
|
|
10255
|
+
})
|
|
10256
|
+
]);
|
|
10257
|
+
}
|
|
10258
|
+
subscribe(_sessionId, onEvent, _onError) {
|
|
10259
|
+
const done = new Promise((resolve) => {
|
|
10260
|
+
this.doneResolve = resolve;
|
|
10261
|
+
});
|
|
10262
|
+
this.client.onEvent((push) => {
|
|
10263
|
+
const event = {
|
|
10264
|
+
id: "",
|
|
10265
|
+
type: push.event,
|
|
10266
|
+
data: push.data
|
|
10267
|
+
};
|
|
10268
|
+
onEvent(event);
|
|
10269
|
+
});
|
|
10270
|
+
return {
|
|
10271
|
+
abort: () => this.destroy(),
|
|
10272
|
+
done
|
|
10273
|
+
};
|
|
10274
|
+
}
|
|
10275
|
+
destroy() {
|
|
10276
|
+
this.client.detach(this.key).catch(() => {
|
|
10277
|
+
});
|
|
10278
|
+
this.client.destroy();
|
|
10279
|
+
if (this.doneResolve) {
|
|
10280
|
+
this.doneResolve();
|
|
10281
|
+
this.doneResolve = null;
|
|
10282
|
+
}
|
|
10283
|
+
}
|
|
10284
|
+
};
|
|
10285
|
+
function isDaemonEnabled() {
|
|
10286
|
+
return process.env.BITFAB_STUDIO_DAEMON !== "0";
|
|
10287
|
+
}
|
|
10288
|
+
function withDirectFallback(primary, apiKey, serviceUrl) {
|
|
10289
|
+
let active = primary;
|
|
10290
|
+
return {
|
|
10291
|
+
async openOrNavigate(path18, opts) {
|
|
10292
|
+
try {
|
|
10293
|
+
return await active.openOrNavigate(path18, opts);
|
|
10294
|
+
} catch (err) {
|
|
10295
|
+
if (err instanceof StudioNavigationError || active !== primary) {
|
|
10296
|
+
throw err;
|
|
10297
|
+
}
|
|
10298
|
+
console.error(`daemon failed, falling back to direct: ${err instanceof Error ? err.message : String(err)}`);
|
|
10299
|
+
active.destroy();
|
|
10300
|
+
active = new DirectChannel(apiKey, serviceUrl);
|
|
10301
|
+
return active.openOrNavigate(path18, opts);
|
|
10302
|
+
}
|
|
10303
|
+
},
|
|
10304
|
+
async close(sessionId, message) {
|
|
10305
|
+
await active.close(sessionId, message);
|
|
10306
|
+
},
|
|
10307
|
+
subscribe(sessionId, onEvent, onError) {
|
|
10308
|
+
return active.subscribe(sessionId, onEvent, onError);
|
|
10309
|
+
},
|
|
10310
|
+
setApiKey(key) {
|
|
10311
|
+
active.setApiKey(key);
|
|
10312
|
+
},
|
|
10313
|
+
destroy() {
|
|
10314
|
+
active.destroy();
|
|
10315
|
+
}
|
|
10316
|
+
};
|
|
10317
|
+
}
|
|
10318
|
+
async function resolveChannel(apiKey, serviceUrl) {
|
|
10319
|
+
if (isDaemonEnabled()) {
|
|
10320
|
+
try {
|
|
10321
|
+
const daemon = await DaemonChannel.connect(apiKey, serviceUrl);
|
|
10322
|
+
return withDirectFallback(daemon, apiKey, serviceUrl);
|
|
10323
|
+
} catch (err) {
|
|
10324
|
+
if (err instanceof StudioNavigationError) {
|
|
10325
|
+
throw err;
|
|
10326
|
+
}
|
|
10327
|
+
if (!(err instanceof DaemonUnavailableError)) {
|
|
10328
|
+
console.error(`daemon unavailable, falling back to direct: ${err instanceof Error ? err.message : String(err)}`);
|
|
10329
|
+
}
|
|
10330
|
+
}
|
|
10331
|
+
}
|
|
10332
|
+
return new DirectChannel(apiKey, serviceUrl);
|
|
10333
|
+
}
|
|
10334
|
+
|
|
9940
10335
|
// ../bitfab-plugin-lib/dist/replayCapabilities.js
|
|
9941
10336
|
var semver = __toESM(require_semver2(), 1);
|
|
9942
10337
|
|
|
@@ -9945,12 +10340,12 @@ var semver3 = __toESM(require_semver2(), 1);
|
|
|
9945
10340
|
|
|
9946
10341
|
// ../bitfab-plugin-lib/dist/installedSdk.js
|
|
9947
10342
|
var semver2 = __toESM(require_semver2(), 1);
|
|
9948
|
-
import
|
|
9949
|
-
import
|
|
10343
|
+
import fs11 from "fs";
|
|
10344
|
+
import path10 from "path";
|
|
9950
10345
|
|
|
9951
10346
|
// ../bitfab-plugin-lib/dist/workspaces.js
|
|
9952
|
-
import
|
|
9953
|
-
import
|
|
10347
|
+
import fs10 from "fs";
|
|
10348
|
+
import path9 from "path";
|
|
9954
10349
|
|
|
9955
10350
|
// ../bitfab-plugin-lib/dist/analytics.js
|
|
9956
10351
|
async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
@@ -9974,11 +10369,11 @@ async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
|
9974
10369
|
}
|
|
9975
10370
|
|
|
9976
10371
|
// ../bitfab-plugin-lib/dist/commands/openStudioTo.js
|
|
9977
|
-
import
|
|
10372
|
+
import crypto7 from "crypto";
|
|
9978
10373
|
|
|
9979
10374
|
// ../bitfab-plugin-lib/dist/frontmostApp.js
|
|
9980
10375
|
import { execFileSync as execFileSync3, spawn as spawn2 } from "child_process";
|
|
9981
|
-
import
|
|
10376
|
+
import os10 from "os";
|
|
9982
10377
|
function findAncestorApp() {
|
|
9983
10378
|
try {
|
|
9984
10379
|
let pid = process.ppid;
|
|
@@ -10039,7 +10434,7 @@ function focusItermSession(sessionId) {
|
|
|
10039
10434
|
spawnQuiet("osascript", ["-e", script]);
|
|
10040
10435
|
}
|
|
10041
10436
|
function getFrontmostApp() {
|
|
10042
|
-
const platform2 =
|
|
10437
|
+
const platform2 = os10.platform();
|
|
10043
10438
|
try {
|
|
10044
10439
|
if (platform2 === "darwin") {
|
|
10045
10440
|
return execFileSync3("osascript", [
|
|
@@ -10073,7 +10468,7 @@ function focusApp(identifier) {
|
|
|
10073
10468
|
if (!identifier) {
|
|
10074
10469
|
return;
|
|
10075
10470
|
}
|
|
10076
|
-
const platform2 =
|
|
10471
|
+
const platform2 = os10.platform();
|
|
10077
10472
|
if (platform2 === "darwin") {
|
|
10078
10473
|
spawnQuiet("osascript", [
|
|
10079
10474
|
"-e",
|
|
@@ -10102,145 +10497,144 @@ function focusApp(identifier) {
|
|
|
10102
10497
|
spawnQuiet("powershell.exe", ["-NoProfile", "-Command", script]);
|
|
10103
10498
|
}
|
|
10104
10499
|
}
|
|
10105
|
-
function recordFocus() {
|
|
10106
|
-
const platform2 =
|
|
10107
|
-
if (platform2 === "linux") {
|
|
10108
|
-
const windowId = process.env.WINDOWID;
|
|
10109
|
-
if (windowId) {
|
|
10110
|
-
return () => spawnQuiet("xdotool", ["windowactivate", windowId]);
|
|
10111
|
-
}
|
|
10112
|
-
}
|
|
10113
|
-
if (platform2 === "darwin") {
|
|
10114
|
-
const app = findAncestorApp();
|
|
10115
|
-
if (app) {
|
|
10116
|
-
const itermSessionId = process.env.ITERM_SESSION_ID ?? null;
|
|
10117
|
-
return () => focusAncestorApp(app, itermSessionId);
|
|
10118
|
-
}
|
|
10119
|
-
}
|
|
10120
|
-
const frontmost = getFrontmostApp();
|
|
10121
|
-
return () => focusApp(frontmost);
|
|
10122
|
-
}
|
|
10123
|
-
|
|
10124
|
-
// ../bitfab-plugin-lib/dist/commands/sessionReady.js
|
|
10125
|
-
var SESSION_READY_TIMEOUT_MS = 3e4;
|
|
10126
|
-
var SESSION_READY_POLL_MS = 500;
|
|
10127
|
-
async function waitForSessionReady(opts) {
|
|
10128
|
-
const deadline = Date.now() + SESSION_READY_TIMEOUT_MS;
|
|
10129
|
-
let since = null;
|
|
10130
|
-
while (Date.now() < deadline) {
|
|
10131
|
-
try {
|
|
10132
|
-
const url2 = new URL(`${opts.serviceUrl}/api/studio/events`);
|
|
10133
|
-
url2.searchParams.set("session", opts.sessionId);
|
|
10134
|
-
if (since) {
|
|
10135
|
-
url2.searchParams.set("since", since);
|
|
10136
|
-
}
|
|
10137
|
-
const res = await fetch(url2.toString(), {
|
|
10138
|
-
headers: { Authorization: `Bearer ${opts.apiKey}` }
|
|
10139
|
-
});
|
|
10140
|
-
if (res.ok) {
|
|
10141
|
-
const body = await res.json();
|
|
10142
|
-
for (const event of body.events) {
|
|
10143
|
-
since = event.id;
|
|
10144
|
-
if (event.type === "studio:session-started") {
|
|
10145
|
-
return;
|
|
10146
|
-
}
|
|
10147
|
-
}
|
|
10148
|
-
}
|
|
10149
|
-
} catch {
|
|
10500
|
+
function recordFocus() {
|
|
10501
|
+
const platform2 = os10.platform();
|
|
10502
|
+
if (platform2 === "linux") {
|
|
10503
|
+
const windowId = process.env.WINDOWID;
|
|
10504
|
+
if (windowId) {
|
|
10505
|
+
return () => spawnQuiet("xdotool", ["windowactivate", windowId]);
|
|
10506
|
+
}
|
|
10507
|
+
}
|
|
10508
|
+
if (platform2 === "darwin") {
|
|
10509
|
+
const app = findAncestorApp();
|
|
10510
|
+
if (app) {
|
|
10511
|
+
const itermSessionId = process.env.ITERM_SESSION_ID ?? null;
|
|
10512
|
+
return () => focusAncestorApp(app, itermSessionId);
|
|
10150
10513
|
}
|
|
10151
|
-
await new Promise((resolve) => setTimeout(resolve, SESSION_READY_POLL_MS));
|
|
10152
10514
|
}
|
|
10153
|
-
|
|
10515
|
+
const frontmost = getFrontmostApp();
|
|
10516
|
+
return () => focusApp(frontmost);
|
|
10154
10517
|
}
|
|
10155
10518
|
|
|
10156
10519
|
// ../bitfab-plugin-lib/dist/commands/openStudioTo.js
|
|
10520
|
+
var WINDOW_CLOSE_GRACE_PERIOD_MS = 1e4;
|
|
10157
10521
|
var LOGIN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
};
|
|
10172
|
-
|
|
10522
|
+
function createEventSubscription(channel, sessionId, onEvent, restoreFocus) {
|
|
10523
|
+
let graceTimer = null;
|
|
10524
|
+
let endedNotified = false;
|
|
10525
|
+
const clearGraceTimer = () => {
|
|
10526
|
+
if (graceTimer) {
|
|
10527
|
+
clearTimeout(graceTimer);
|
|
10528
|
+
graceTimer = null;
|
|
10529
|
+
}
|
|
10530
|
+
};
|
|
10531
|
+
const notifyEnded = (baseEvent) => {
|
|
10532
|
+
if (!endedNotified) {
|
|
10533
|
+
endedNotified = true;
|
|
10534
|
+
restoreFocus();
|
|
10535
|
+
onEvent({ ...baseEvent, type: "studio:session-ended" });
|
|
10536
|
+
}
|
|
10537
|
+
clearGraceTimer();
|
|
10538
|
+
sub.abort();
|
|
10539
|
+
};
|
|
10540
|
+
const sub = channel.subscribe(sessionId, (event) => {
|
|
10541
|
+
if (event.type === "studio:window-closed") {
|
|
10542
|
+
clearGraceTimer();
|
|
10543
|
+
graceTimer = setTimeout(() => {
|
|
10544
|
+
graceTimer = null;
|
|
10545
|
+
updateActiveStudioSession({ windowState: "closed" });
|
|
10546
|
+
notifyEnded(event);
|
|
10547
|
+
}, WINDOW_CLOSE_GRACE_PERIOD_MS);
|
|
10548
|
+
return;
|
|
10549
|
+
}
|
|
10550
|
+
if (event.type === "studio:session-started") {
|
|
10551
|
+
if (graceTimer) {
|
|
10552
|
+
clearGraceTimer();
|
|
10553
|
+
console.error("Studio reconnected after page refresh");
|
|
10554
|
+
}
|
|
10555
|
+
return;
|
|
10556
|
+
}
|
|
10557
|
+
if (event.type === "studio:session-ended") {
|
|
10558
|
+
clearGraceTimer();
|
|
10559
|
+
notifyEnded(event);
|
|
10560
|
+
return;
|
|
10561
|
+
}
|
|
10562
|
+
if (event.type === "studio:session-closed") {
|
|
10563
|
+
return;
|
|
10564
|
+
}
|
|
10565
|
+
if (event.type === "studio:not-member") {
|
|
10566
|
+
restoreFocus();
|
|
10567
|
+
onEvent(event);
|
|
10568
|
+
sub.abort();
|
|
10569
|
+
return;
|
|
10570
|
+
}
|
|
10571
|
+
if (event.type === "studio:return-to-agent" || event.type === "studio:edit-with-agent") {
|
|
10572
|
+
restoreFocus();
|
|
10573
|
+
}
|
|
10574
|
+
onEvent(event);
|
|
10575
|
+
}, (err) => {
|
|
10576
|
+
console.error(`studio event stream: ${err.message}`);
|
|
10577
|
+
});
|
|
10578
|
+
return {
|
|
10579
|
+
abort: () => {
|
|
10580
|
+
clearGraceTimer();
|
|
10581
|
+
sub.abort();
|
|
10582
|
+
},
|
|
10583
|
+
done: sub.done
|
|
10584
|
+
};
|
|
10585
|
+
}
|
|
10586
|
+
async function openStudioTo(path18, opts = {}) {
|
|
10173
10587
|
const config2 = getConfig();
|
|
10174
10588
|
const serviceUrl = config2.serviceUrl;
|
|
10175
10589
|
const apiKey = !opts.forceLogin && hasCredentials() ? config2.apiKey : null;
|
|
10176
10590
|
const noop = () => {
|
|
10177
10591
|
};
|
|
10178
10592
|
const restoreFocus = recordFocus();
|
|
10593
|
+
const channel = await resolveChannel(apiKey ?? "", serviceUrl);
|
|
10179
10594
|
if (!apiKey) {
|
|
10180
|
-
const
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
throw new StudioNavigationError("push-failed", void 0, existing2.sessionId);
|
|
10189
|
-
}
|
|
10190
|
-
return loginViaExistingWindow({
|
|
10191
|
-
serviceUrl: existing2.serviceUrl,
|
|
10192
|
-
sessionId: existing2.sessionId,
|
|
10193
|
-
startCursor: reauth.pushedEventId,
|
|
10194
|
-
signInUrl: reauth.signInUrl,
|
|
10195
|
-
restoreFocus,
|
|
10196
|
-
opts
|
|
10197
|
-
});
|
|
10198
|
-
}
|
|
10199
|
-
const sessionId2 = crypto4.randomUUID();
|
|
10200
|
-
const freshPath = opts.freshWindowPath ?? path15;
|
|
10201
|
-
const separator = freshPath.includes("?") ? "&" : "?";
|
|
10202
|
-
const redirectPath = `${freshPath}${separator}session=${encodeURIComponent(sessionId2)}&pluginLogin=true`;
|
|
10203
|
-
const signInPath = `/studio/sign-in?redirect_url=${encodeURIComponent(redirectPath)}`;
|
|
10204
|
-
const signInUrl = `${serviceUrl}${signInPath}&session=${encodeURIComponent(sessionId2)}`;
|
|
10205
|
-
const windowPid = openChromelessWindow(signInUrl);
|
|
10206
|
-
writeActiveStudioSession({
|
|
10207
|
-
sessionId: sessionId2,
|
|
10208
|
-
serviceUrl,
|
|
10209
|
-
windowPid: windowPid ?? void 0
|
|
10595
|
+
const sessionId2 = crypto7.randomUUID();
|
|
10596
|
+
const destination = opts.freshWindowPath ?? path18;
|
|
10597
|
+
const destSeparator = destination.includes("?") ? "&" : "?";
|
|
10598
|
+
const redirectPath = `${destination}${destSeparator}session=${encodeURIComponent(sessionId2)}&pluginLogin=true`;
|
|
10599
|
+
const signInInitialPath = `/studio/sign-in?redirect_url=${encodeURIComponent(redirectPath)}&session=${encodeURIComponent(sessionId2)}`;
|
|
10600
|
+
await channel.openOrNavigate(signInInitialPath, {
|
|
10601
|
+
clientHeaders: opts.clientHeaders,
|
|
10602
|
+
sessionId: sessionId2
|
|
10210
10603
|
});
|
|
10604
|
+
const signInUrl = `${serviceUrl}${signInInitialPath}`;
|
|
10211
10605
|
opts.onLoginRequired?.(sessionId2, signInUrl);
|
|
10212
|
-
const
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10606
|
+
const loginApiKey = await new Promise((resolve, reject) => {
|
|
10607
|
+
const timer = setTimeout(() => {
|
|
10608
|
+
sub.abort();
|
|
10609
|
+
reject(new Error("Login polling aborted"));
|
|
10610
|
+
}, LOGIN_TIMEOUT_MS);
|
|
10611
|
+
const keepalive = setInterval(() => process.stderr.write(""), 3e4);
|
|
10612
|
+
const cleanup = () => {
|
|
10613
|
+
clearTimeout(timer);
|
|
10614
|
+
clearInterval(keepalive);
|
|
10615
|
+
};
|
|
10616
|
+
const sub = channel.subscribe(sessionId2, (event) => {
|
|
10617
|
+
if (event.type === "studio:authenticated" && typeof event.data?.token === "string") {
|
|
10618
|
+
cleanup();
|
|
10619
|
+
sub.abort();
|
|
10620
|
+
resolve(event.data.token);
|
|
10621
|
+
}
|
|
10622
|
+
}, (err) => {
|
|
10623
|
+
cleanup();
|
|
10624
|
+
reject(err);
|
|
10221
10625
|
});
|
|
10222
|
-
}
|
|
10223
|
-
clearTimeout(timer);
|
|
10224
|
-
clearInterval(keepalive);
|
|
10225
|
-
throw err;
|
|
10226
|
-
}
|
|
10227
|
-
clearTimeout(timer);
|
|
10228
|
-
clearInterval(keepalive);
|
|
10626
|
+
});
|
|
10229
10627
|
saveCredentials(loginApiKey);
|
|
10230
10628
|
opts.onAuthenticated?.(sessionId2);
|
|
10629
|
+
channel.setApiKey(loginApiKey);
|
|
10231
10630
|
if (opts.onEvent) {
|
|
10232
|
-
|
|
10233
|
-
serviceUrl,
|
|
10234
|
-
apiKey: loginApiKey,
|
|
10235
|
-
sessionId: sessionId2
|
|
10236
|
-
});
|
|
10237
|
-
const poller2 = startEventPoller(serviceUrl, loginApiKey, sessionId2, opts.onEvent, restoreFocus, { claim: opts.monitor !== "wait" });
|
|
10631
|
+
const subscription2 = createEventSubscription(channel, sessionId2, opts.onEvent, restoreFocus);
|
|
10238
10632
|
return {
|
|
10239
10633
|
sessionId: sessionId2,
|
|
10240
10634
|
serviceUrl,
|
|
10241
10635
|
apiKey: loginApiKey,
|
|
10242
10636
|
opened: true,
|
|
10243
|
-
...
|
|
10637
|
+
...subscription2
|
|
10244
10638
|
};
|
|
10245
10639
|
}
|
|
10246
10640
|
restoreFocus();
|
|
@@ -10253,299 +10647,20 @@ async function openStudioTo(path15, opts = {}) {
|
|
|
10253
10647
|
done: Promise.resolve()
|
|
10254
10648
|
};
|
|
10255
10649
|
}
|
|
10256
|
-
const
|
|
10257
|
-
if (existing) {
|
|
10258
|
-
const client = {
|
|
10259
|
-
serviceUrl: existing.serviceUrl,
|
|
10260
|
-
apiKey,
|
|
10261
|
-
sessionId: existing.sessionId
|
|
10262
|
-
};
|
|
10263
|
-
const ack = await navigateStudioAndAwaitAck(client, path15);
|
|
10264
|
-
if (!ack.acked) {
|
|
10265
|
-
throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
|
|
10266
|
-
}
|
|
10267
|
-
if (opts.onEvent && opts.monitor === "wait") {
|
|
10268
|
-
const poller2 = startEventPoller(existing.serviceUrl, apiKey, existing.sessionId, opts.onEvent, restoreFocus, { claim: false });
|
|
10269
|
-
return {
|
|
10270
|
-
sessionId: existing.sessionId,
|
|
10271
|
-
serviceUrl: existing.serviceUrl,
|
|
10272
|
-
apiKey,
|
|
10273
|
-
opened: false,
|
|
10274
|
-
...poller2
|
|
10275
|
-
};
|
|
10276
|
-
}
|
|
10277
|
-
if (opts.onEvent && opts.monitor === "start" && tryClaimStudioMonitor()) {
|
|
10278
|
-
const poller2 = startEventPoller(existing.serviceUrl, apiKey, existing.sessionId, opts.onEvent, restoreFocus, { claim: true });
|
|
10279
|
-
return {
|
|
10280
|
-
sessionId: existing.sessionId,
|
|
10281
|
-
serviceUrl: existing.serviceUrl,
|
|
10282
|
-
apiKey,
|
|
10283
|
-
opened: false,
|
|
10284
|
-
...poller2
|
|
10285
|
-
};
|
|
10286
|
-
}
|
|
10287
|
-
restoreFocus();
|
|
10288
|
-
return {
|
|
10289
|
-
sessionId: existing.sessionId,
|
|
10290
|
-
serviceUrl: existing.serviceUrl,
|
|
10291
|
-
apiKey,
|
|
10292
|
-
opened: false,
|
|
10293
|
-
abort: noop,
|
|
10294
|
-
done: Promise.resolve()
|
|
10295
|
-
};
|
|
10296
|
-
}
|
|
10297
|
-
const sessionId = crypto4.randomUUID();
|
|
10298
|
-
const session = await createStudioSession({
|
|
10299
|
-
serviceUrl,
|
|
10300
|
-
apiKey,
|
|
10301
|
-
sessionId,
|
|
10302
|
-
initialPath: path15,
|
|
10650
|
+
const { sessionId, opened } = await channel.openOrNavigate(path18, {
|
|
10303
10651
|
clientHeaders: opts.clientHeaders
|
|
10304
10652
|
});
|
|
10305
|
-
const
|
|
10306
|
-
return {
|
|
10307
|
-
sessionId: session.sessionId,
|
|
10308
|
-
serviceUrl: session.serviceUrl,
|
|
10309
|
-
apiKey,
|
|
10310
|
-
opened: true,
|
|
10311
|
-
...poller
|
|
10312
|
-
};
|
|
10313
|
-
}
|
|
10314
|
-
async function loginViaExistingWindow(args) {
|
|
10315
|
-
const { serviceUrl, sessionId } = args;
|
|
10316
|
-
const noop = () => {
|
|
10317
|
-
};
|
|
10318
|
-
const keepalive = setInterval(() => process.stderr.write(""), 3e4);
|
|
10319
|
-
let loginApiKey;
|
|
10320
|
-
try {
|
|
10321
|
-
loginApiKey = await awaitReauthLogin({
|
|
10322
|
-
serviceUrl,
|
|
10323
|
-
sessionId,
|
|
10324
|
-
startCursor: args.startCursor,
|
|
10325
|
-
// Only fired once the sign-in page proved the window alive AND is
|
|
10326
|
-
// waiting on the user; a still-valid browser session re-auths silently
|
|
10327
|
-
// and the caller never surfaces a sign-in prompt.
|
|
10328
|
-
onAck: () => args.opts.onLoginRequired?.(sessionId, args.signInUrl)
|
|
10329
|
-
});
|
|
10330
|
-
} finally {
|
|
10331
|
-
clearInterval(keepalive);
|
|
10332
|
-
}
|
|
10333
|
-
saveCredentials(loginApiKey);
|
|
10334
|
-
args.opts.onAuthenticated?.(sessionId);
|
|
10335
|
-
if (args.opts.onEvent && (args.opts.monitor === "wait" || args.opts.monitor === "start" && tryClaimStudioMonitor())) {
|
|
10336
|
-
await waitForSessionReady({
|
|
10337
|
-
serviceUrl,
|
|
10338
|
-
apiKey: loginApiKey,
|
|
10339
|
-
sessionId
|
|
10340
|
-
});
|
|
10341
|
-
const poller = startEventPoller(serviceUrl, loginApiKey, sessionId, args.opts.onEvent, args.restoreFocus, { claim: args.opts.monitor !== "wait" });
|
|
10342
|
-
return {
|
|
10343
|
-
sessionId,
|
|
10344
|
-
serviceUrl,
|
|
10345
|
-
apiKey: loginApiKey,
|
|
10346
|
-
opened: false,
|
|
10347
|
-
...poller
|
|
10348
|
-
};
|
|
10349
|
-
}
|
|
10350
|
-
args.restoreFocus();
|
|
10653
|
+
const subscription = opts.onEvent ? createEventSubscription(channel, sessionId, opts.onEvent, restoreFocus) : { abort: () => channel.destroy(), done: Promise.resolve() };
|
|
10351
10654
|
return {
|
|
10352
10655
|
sessionId,
|
|
10353
10656
|
serviceUrl,
|
|
10354
|
-
apiKey: loginApiKey,
|
|
10355
|
-
opened: false,
|
|
10356
|
-
abort: noop,
|
|
10357
|
-
done: Promise.resolve()
|
|
10358
|
-
};
|
|
10359
|
-
}
|
|
10360
|
-
function awaitReauthLogin(args) {
|
|
10361
|
-
return new Promise((resolve, reject) => {
|
|
10362
|
-
const abortController = new AbortController();
|
|
10363
|
-
let settled = false;
|
|
10364
|
-
let acked = false;
|
|
10365
|
-
const settle = (fn) => {
|
|
10366
|
-
if (settled) {
|
|
10367
|
-
return;
|
|
10368
|
-
}
|
|
10369
|
-
settled = true;
|
|
10370
|
-
clearTimeout(ackTimer);
|
|
10371
|
-
clearTimeout(loginTimer);
|
|
10372
|
-
abortController.abort();
|
|
10373
|
-
fn();
|
|
10374
|
-
};
|
|
10375
|
-
const ackTimer = setTimeout(() => {
|
|
10376
|
-
settle(() => reject(new StudioNavigationError("timeout", void 0, args.sessionId)));
|
|
10377
|
-
}, REAUTH_ACK_TIMEOUT_MS);
|
|
10378
|
-
const loginTimer = setTimeout(() => {
|
|
10379
|
-
settle(() => reject(new Error("Login polling aborted")));
|
|
10380
|
-
}, LOGIN_TIMEOUT_MS);
|
|
10381
|
-
pollSessionAuthEvents({
|
|
10382
|
-
serviceUrl: args.serviceUrl,
|
|
10383
|
-
sessionId: args.sessionId,
|
|
10384
|
-
startCursor: args.startCursor,
|
|
10385
|
-
abortSignal: abortController.signal,
|
|
10386
|
-
onEvent: (event) => {
|
|
10387
|
-
if (event.type === "studio:auth-required" && !acked) {
|
|
10388
|
-
acked = true;
|
|
10389
|
-
clearTimeout(ackTimer);
|
|
10390
|
-
args.onAck();
|
|
10391
|
-
return;
|
|
10392
|
-
}
|
|
10393
|
-
if (event.type === "studio:authenticated") {
|
|
10394
|
-
const token = event.data.token;
|
|
10395
|
-
if (typeof token === "string") {
|
|
10396
|
-
settle(() => resolve(token));
|
|
10397
|
-
}
|
|
10398
|
-
}
|
|
10399
|
-
}
|
|
10400
|
-
}).catch((err) => {
|
|
10401
|
-
settle(() => reject(err instanceof Error ? err : new Error(String(err))));
|
|
10402
|
-
});
|
|
10403
|
-
});
|
|
10404
|
-
}
|
|
10405
|
-
function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus, { claim = true, fromStreamStart = false, graceMs, forceClear = false } = {}) {
|
|
10406
|
-
printBlockingNotice(BLOCKING_NOTICE_UNTIL);
|
|
10407
|
-
if (claim) {
|
|
10408
|
-
claimStudioMonitor();
|
|
10409
|
-
}
|
|
10410
|
-
const abortController = new AbortController();
|
|
10411
|
-
let endedNotified = false;
|
|
10412
|
-
const releaseLock = () => {
|
|
10413
|
-
if (claim) {
|
|
10414
|
-
releaseStudioMonitor();
|
|
10415
|
-
}
|
|
10416
|
-
};
|
|
10417
|
-
const notifyEnded = (baseEvent) => {
|
|
10418
|
-
if (!endedNotified) {
|
|
10419
|
-
endedNotified = true;
|
|
10420
|
-
restoreFocus();
|
|
10421
|
-
onEvent({ ...baseEvent, type: "studio:session-ended" });
|
|
10422
|
-
}
|
|
10423
|
-
releaseLock();
|
|
10424
|
-
abortController.abort();
|
|
10425
|
-
};
|
|
10426
|
-
const arbiter = createWindowCloseArbiter({
|
|
10427
|
-
onConfirmed: (event) => {
|
|
10428
|
-
if (forceClear) {
|
|
10429
|
-
clearActiveStudioSession(sessionId, { force: true });
|
|
10430
|
-
} else {
|
|
10431
|
-
clearActiveStudioSession(sessionId);
|
|
10432
|
-
}
|
|
10433
|
-
notifyEnded(event);
|
|
10434
|
-
},
|
|
10435
|
-
onRefuted: () => {
|
|
10436
|
-
console.error("Studio reconnected after page refresh");
|
|
10437
|
-
}
|
|
10438
|
-
}, graceMs);
|
|
10439
|
-
const done = (async () => {
|
|
10440
|
-
const startCursor = fromStreamStart ? void 0 : await fetchStreamTip({ serviceUrl, apiKey, sessionId });
|
|
10441
|
-
if (abortController.signal.aborted) {
|
|
10442
|
-
return;
|
|
10443
|
-
}
|
|
10444
|
-
await pollAgentSessionEvents({
|
|
10445
|
-
serviceUrl,
|
|
10446
|
-
apiKey,
|
|
10447
|
-
sessionId,
|
|
10448
|
-
startCursor,
|
|
10449
|
-
abortSignal: abortController.signal,
|
|
10450
|
-
onEvent: (event) => {
|
|
10451
|
-
if (event.type === "studio:window-closed" || event.type === "studio:session-started") {
|
|
10452
|
-
arbiter.handleEvent(event);
|
|
10453
|
-
return;
|
|
10454
|
-
}
|
|
10455
|
-
if (event.type === "studio:session-ended") {
|
|
10456
|
-
arbiter.cancel();
|
|
10457
|
-
notifyEnded(event);
|
|
10458
|
-
return;
|
|
10459
|
-
}
|
|
10460
|
-
if (event.type === "studio:session-closed") {
|
|
10461
|
-
return;
|
|
10462
|
-
}
|
|
10463
|
-
if (event.type === "studio:not-member") {
|
|
10464
|
-
restoreFocus();
|
|
10465
|
-
onEvent(event);
|
|
10466
|
-
releaseLock();
|
|
10467
|
-
abortController.abort();
|
|
10468
|
-
return;
|
|
10469
|
-
}
|
|
10470
|
-
if (event.type === "studio:return-to-agent" || event.type === "studio:edit-with-agent") {
|
|
10471
|
-
restoreFocus();
|
|
10472
|
-
}
|
|
10473
|
-
onEvent(event);
|
|
10474
|
-
},
|
|
10475
|
-
onError: (err) => {
|
|
10476
|
-
console.error(`studio agent-event poll: ${err.message}`);
|
|
10477
|
-
}
|
|
10478
|
-
});
|
|
10479
|
-
})().catch((err) => {
|
|
10480
|
-
if (!abortController.signal.aborted) {
|
|
10481
|
-
console.error(`studio agent-event poll terminated: ${err.message}`);
|
|
10482
|
-
}
|
|
10483
|
-
releaseLock();
|
|
10484
|
-
});
|
|
10485
|
-
return {
|
|
10486
|
-
abort: () => {
|
|
10487
|
-
releaseLock();
|
|
10488
|
-
abortController.abort();
|
|
10489
|
-
},
|
|
10490
|
-
done
|
|
10491
|
-
};
|
|
10492
|
-
}
|
|
10493
|
-
var LOGIN_MONITOR_DRAIN_MS = 8e3;
|
|
10494
|
-
async function runLoginWindowMonitor(sessionId) {
|
|
10495
|
-
const config2 = getConfig();
|
|
10496
|
-
const apiKey = config2.apiKey;
|
|
10497
|
-
if (!apiKey) {
|
|
10498
|
-
return;
|
|
10499
|
-
}
|
|
10500
|
-
const poller = startEventPoller(
|
|
10501
|
-
config2.serviceUrl,
|
|
10502
10657
|
apiKey,
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
() => {
|
|
10507
|
-
},
|
|
10508
|
-
// claim: false — this is a transient cleanup reader with a no-op event
|
|
10509
|
-
// handler, NOT the session's real monitor. If it claimed the lock, a
|
|
10510
|
-
// subsequent `openStudioTo` reuse with monitor:"start" (e.g. a flow's
|
|
10511
|
-
// backgrounded open right after a PARKED login) would lose the atomic
|
|
10512
|
-
// claim, assume a live monitor is forwarding events, and fire-and-exit —
|
|
10513
|
-
// leaving the session with no real monitor while this one forwards
|
|
10514
|
-
// nothing. Reading without claiming lets it clean up the pointer
|
|
10515
|
-
// (window-closed → clear) without shadowing the real monitor.
|
|
10516
|
-
//
|
|
10517
|
-
// forceClear: the clear runs in a detached child while the login/init
|
|
10518
|
-
// parent that WROTE the pointer may still be alive (init keeps running with
|
|
10519
|
-
// exitOnComplete:false, even spawnSync-ing the editor), so a non-force clear
|
|
10520
|
-
// would be refused by the writer-alive guard and the closed window's
|
|
10521
|
-
// pointer would leak. Safe — the clear still matches on the unique
|
|
10522
|
-
// sessionId.
|
|
10523
|
-
{ claim: false, fromStreamStart: true, graceMs: 0, forceClear: true }
|
|
10524
|
-
);
|
|
10525
|
-
let drainTimer;
|
|
10526
|
-
const drain = new Promise((resolve) => {
|
|
10527
|
-
drainTimer = setTimeout(resolve, LOGIN_MONITOR_DRAIN_MS);
|
|
10528
|
-
});
|
|
10529
|
-
await Promise.race([poller.done, drain]);
|
|
10530
|
-
if (drainTimer) {
|
|
10531
|
-
clearTimeout(drainTimer);
|
|
10532
|
-
}
|
|
10533
|
-
poller.abort();
|
|
10658
|
+
opened,
|
|
10659
|
+
...subscription
|
|
10660
|
+
};
|
|
10534
10661
|
}
|
|
10535
10662
|
|
|
10536
10663
|
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
10537
|
-
function trySpawnDetachedMonitor(sessionId) {
|
|
10538
|
-
try {
|
|
10539
|
-
const pid = spawnDetached(process.execPath, [
|
|
10540
|
-
...process.argv.slice(1),
|
|
10541
|
-
"--drain",
|
|
10542
|
-
sessionId
|
|
10543
|
-
]);
|
|
10544
|
-
return pid != null;
|
|
10545
|
-
} catch {
|
|
10546
|
-
return false;
|
|
10547
|
-
}
|
|
10548
|
-
}
|
|
10549
10664
|
async function verifyToken(serviceUrl, token) {
|
|
10550
10665
|
try {
|
|
10551
10666
|
const res = await fetch(`${serviceUrl}/api/plugin/whoami`, {
|
|
@@ -10561,23 +10676,6 @@ async function verifyToken(serviceUrl, token) {
|
|
|
10561
10676
|
}
|
|
10562
10677
|
async function runLogin(platform2, pluginVersion, options) {
|
|
10563
10678
|
const exitOnComplete = options?.exitOnComplete ?? true;
|
|
10564
|
-
const drainIdx = process.argv.indexOf("--drain");
|
|
10565
|
-
if (drainIdx !== -1) {
|
|
10566
|
-
const sessionId = process.argv[drainIdx + 1];
|
|
10567
|
-
if (!sessionId) {
|
|
10568
|
-
throw new Error("--drain requires a sessionId argument");
|
|
10569
|
-
}
|
|
10570
|
-
await runLoginWindowMonitor(sessionId);
|
|
10571
|
-
if (exitOnComplete) {
|
|
10572
|
-
process.exit(0);
|
|
10573
|
-
}
|
|
10574
|
-
return;
|
|
10575
|
-
}
|
|
10576
|
-
if (exitOnComplete) {
|
|
10577
|
-
printHelpIfRequested({
|
|
10578
|
-
description: "Sign in to Bitfab. Opens Studio in the browser for authentication and saves the API key locally. Takes no arguments."
|
|
10579
|
-
});
|
|
10580
|
-
}
|
|
10581
10679
|
const force = options?.force ?? process.argv.includes("--force");
|
|
10582
10680
|
const config2 = getConfig();
|
|
10583
10681
|
if (!force && hasCredentials() && config2.apiKey) {
|
|
@@ -10617,9 +10715,7 @@ ${signInUrl}`);
|
|
|
10617
10715
|
${greeting}`);
|
|
10618
10716
|
console.log("Bitfab MCP tools are now active. (via studio)");
|
|
10619
10717
|
}
|
|
10620
|
-
|
|
10621
|
-
await runLoginWindowMonitor(result.sessionId);
|
|
10622
|
-
}
|
|
10718
|
+
result.abort();
|
|
10623
10719
|
if (exitOnComplete) {
|
|
10624
10720
|
process.exit(0);
|
|
10625
10721
|
}
|
|
@@ -11424,10 +11520,10 @@ function mergeDefs(...defs) {
|
|
|
11424
11520
|
function cloneDef(schema) {
|
|
11425
11521
|
return mergeDefs(schema._zod.def);
|
|
11426
11522
|
}
|
|
11427
|
-
function getElementAtPath(obj,
|
|
11428
|
-
if (!
|
|
11523
|
+
function getElementAtPath(obj, path18) {
|
|
11524
|
+
if (!path18)
|
|
11429
11525
|
return obj;
|
|
11430
|
-
return
|
|
11526
|
+
return path18.reduce((acc, key) => acc?.[key], obj);
|
|
11431
11527
|
}
|
|
11432
11528
|
function promiseAllObject(promisesObj) {
|
|
11433
11529
|
const keys = Object.keys(promisesObj);
|
|
@@ -11810,11 +11906,11 @@ function aborted(x, startIndex = 0) {
|
|
|
11810
11906
|
}
|
|
11811
11907
|
return false;
|
|
11812
11908
|
}
|
|
11813
|
-
function prefixIssues(
|
|
11909
|
+
function prefixIssues(path18, issues) {
|
|
11814
11910
|
return issues.map((iss) => {
|
|
11815
11911
|
var _a2;
|
|
11816
11912
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
11817
|
-
iss.path.unshift(
|
|
11913
|
+
iss.path.unshift(path18);
|
|
11818
11914
|
return iss;
|
|
11819
11915
|
});
|
|
11820
11916
|
}
|
|
@@ -11997,7 +12093,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
|
|
|
11997
12093
|
}
|
|
11998
12094
|
function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
11999
12095
|
const result = { errors: [] };
|
|
12000
|
-
const processError = (error49,
|
|
12096
|
+
const processError = (error49, path18 = []) => {
|
|
12001
12097
|
var _a2, _b;
|
|
12002
12098
|
for (const issue2 of error49.issues) {
|
|
12003
12099
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -12007,7 +12103,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12007
12103
|
} else if (issue2.code === "invalid_element") {
|
|
12008
12104
|
processError({ issues: issue2.issues }, issue2.path);
|
|
12009
12105
|
} else {
|
|
12010
|
-
const fullpath = [...
|
|
12106
|
+
const fullpath = [...path18, ...issue2.path];
|
|
12011
12107
|
if (fullpath.length === 0) {
|
|
12012
12108
|
result.errors.push(mapper(issue2));
|
|
12013
12109
|
continue;
|
|
@@ -12039,8 +12135,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12039
12135
|
}
|
|
12040
12136
|
function toDotPath(_path) {
|
|
12041
12137
|
const segs = [];
|
|
12042
|
-
const
|
|
12043
|
-
for (const seg of
|
|
12138
|
+
const path18 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
12139
|
+
for (const seg of path18) {
|
|
12044
12140
|
if (typeof seg === "number")
|
|
12045
12141
|
segs.push(`[${seg}]`);
|
|
12046
12142
|
else if (typeof seg === "symbol")
|
|
@@ -24017,13 +24113,13 @@ function resolveRef(ref, ctx) {
|
|
|
24017
24113
|
if (!ref.startsWith("#")) {
|
|
24018
24114
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
24019
24115
|
}
|
|
24020
|
-
const
|
|
24021
|
-
if (
|
|
24116
|
+
const path18 = ref.slice(1).split("/").filter(Boolean);
|
|
24117
|
+
if (path18.length === 0) {
|
|
24022
24118
|
return ctx.rootSchema;
|
|
24023
24119
|
}
|
|
24024
24120
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
24025
|
-
if (
|
|
24026
|
-
const key =
|
|
24121
|
+
if (path18[0] === defsKey) {
|
|
24122
|
+
const key = path18[1];
|
|
24027
24123
|
if (!key || !ctx.defs[key]) {
|
|
24028
24124
|
throw new Error(`Reference not found: ${ref}`);
|
|
24029
24125
|
}
|
|
@@ -24455,18 +24551,18 @@ var inputFileSchema = external_exports.object({
|
|
|
24455
24551
|
import { spawn as spawn3 } from "child_process";
|
|
24456
24552
|
|
|
24457
24553
|
// ../bitfab-plugin-lib/dist/activePreviewSession.js
|
|
24458
|
-
import
|
|
24459
|
-
import
|
|
24460
|
-
import
|
|
24461
|
-
var FILE_PATH =
|
|
24554
|
+
import fs12 from "fs";
|
|
24555
|
+
import os11 from "os";
|
|
24556
|
+
import path11 from "path";
|
|
24557
|
+
var FILE_PATH = path11.join(os11.homedir(), ".config", "bitfab", "active-preview-session.json");
|
|
24462
24558
|
|
|
24463
24559
|
// ../bitfab-plugin-lib/dist/commands/status.js
|
|
24464
|
-
import
|
|
24560
|
+
import path13 from "path";
|
|
24465
24561
|
|
|
24466
24562
|
// ../bitfab-plugin-lib/dist/updates.js
|
|
24467
|
-
import
|
|
24468
|
-
import
|
|
24469
|
-
import
|
|
24563
|
+
import fs13 from "fs";
|
|
24564
|
+
import os12 from "os";
|
|
24565
|
+
import path12 from "path";
|
|
24470
24566
|
|
|
24471
24567
|
// ../bitfab-plugin-lib/dist/commands/update.js
|
|
24472
24568
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -24475,15 +24571,15 @@ import { execSync as execSync2 } from "child_process";
|
|
|
24475
24571
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
24476
24572
|
|
|
24477
24573
|
// ../bitfab-plugin-lib/dist/hooks/captureHook.js
|
|
24478
|
-
import
|
|
24574
|
+
import fs14 from "fs";
|
|
24479
24575
|
|
|
24480
24576
|
// ../bitfab-plugin-lib/dist/hooks/userPromptSubmit.js
|
|
24481
|
-
import
|
|
24577
|
+
import fs15 from "fs";
|
|
24482
24578
|
|
|
24483
24579
|
// ../bitfab-plugin-lib/dist/installScope.js
|
|
24484
|
-
import
|
|
24485
|
-
import
|
|
24486
|
-
import
|
|
24580
|
+
import fs16 from "fs";
|
|
24581
|
+
import os13 from "os";
|
|
24582
|
+
import path14 from "path";
|
|
24487
24583
|
|
|
24488
24584
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/helpers/util.js
|
|
24489
24585
|
var util;
|
|
@@ -24844,8 +24940,8 @@ function getErrorMap2() {
|
|
|
24844
24940
|
|
|
24845
24941
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/helpers/parseUtil.js
|
|
24846
24942
|
var makeIssue = (params) => {
|
|
24847
|
-
const { data, path:
|
|
24848
|
-
const fullPath = [...
|
|
24943
|
+
const { data, path: path18, errorMaps, issueData } = params;
|
|
24944
|
+
const fullPath = [...path18, ...issueData.path || []];
|
|
24849
24945
|
const fullIssue = {
|
|
24850
24946
|
...issueData,
|
|
24851
24947
|
path: fullPath
|
|
@@ -24960,11 +25056,11 @@ var errorUtil;
|
|
|
24960
25056
|
|
|
24961
25057
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/types.js
|
|
24962
25058
|
var ParseInputLazyPath = class {
|
|
24963
|
-
constructor(parent, value,
|
|
25059
|
+
constructor(parent, value, path18, key) {
|
|
24964
25060
|
this._cachedPath = [];
|
|
24965
25061
|
this.parent = parent;
|
|
24966
25062
|
this.data = value;
|
|
24967
|
-
this._path =
|
|
25063
|
+
this._path = path18;
|
|
24968
25064
|
this._key = key;
|
|
24969
25065
|
}
|
|
24970
25066
|
get path() {
|
|
@@ -29858,15 +29954,11 @@ var McpZodTypeKind;
|
|
|
29858
29954
|
// ../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
|
|
29859
29955
|
import process3 from "process";
|
|
29860
29956
|
|
|
29861
|
-
// src/init.ts
|
|
29862
|
-
import { execSync as execSync3 } from "child_process";
|
|
29863
|
-
import * as p4 from "@clack/prompts";
|
|
29864
|
-
|
|
29865
29957
|
// src/claude.ts
|
|
29866
29958
|
import { spawnSync } from "child_process";
|
|
29867
|
-
import
|
|
29868
|
-
import
|
|
29869
|
-
import
|
|
29959
|
+
import fs17 from "fs";
|
|
29960
|
+
import os14 from "os";
|
|
29961
|
+
import path15 from "path";
|
|
29870
29962
|
import * as p from "@clack/prompts";
|
|
29871
29963
|
var REPO = "Project-White-Rabbit/bitfab-claude-plugin";
|
|
29872
29964
|
var MARKETPLACE = "bitfab";
|
|
@@ -29886,7 +29978,7 @@ function runClaudeInstall() {
|
|
|
29886
29978
|
}
|
|
29887
29979
|
s.start("Enabling auto-updates");
|
|
29888
29980
|
const changed = enableAutoUpdate(
|
|
29889
|
-
|
|
29981
|
+
path15.join(os14.homedir(), ".claude", "settings.json")
|
|
29890
29982
|
);
|
|
29891
29983
|
if (changed) {
|
|
29892
29984
|
s.stop("Auto-updates enabled");
|
|
@@ -29977,8 +30069,8 @@ function runClaude(args) {
|
|
|
29977
30069
|
}
|
|
29978
30070
|
function enableAutoUpdate(settingsPath) {
|
|
29979
30071
|
let settings = {};
|
|
29980
|
-
if (
|
|
29981
|
-
const raw =
|
|
30072
|
+
if (fs17.existsSync(settingsPath)) {
|
|
30073
|
+
const raw = fs17.readFileSync(settingsPath, "utf-8");
|
|
29982
30074
|
settings = raw.trim() === "" ? {} : JSON.parse(raw);
|
|
29983
30075
|
}
|
|
29984
30076
|
const marketplaces = settings.extraKnownMarketplaces ?? {};
|
|
@@ -29992,17 +30084,17 @@ function enableAutoUpdate(settingsPath) {
|
|
|
29992
30084
|
autoUpdate: true
|
|
29993
30085
|
};
|
|
29994
30086
|
settings.extraKnownMarketplaces = marketplaces;
|
|
29995
|
-
|
|
29996
|
-
|
|
30087
|
+
fs17.mkdirSync(path15.dirname(settingsPath), { recursive: true });
|
|
30088
|
+
fs17.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
29997
30089
|
`);
|
|
29998
30090
|
return true;
|
|
29999
30091
|
}
|
|
30000
30092
|
|
|
30001
30093
|
// src/codex.ts
|
|
30002
30094
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
30003
|
-
import
|
|
30004
|
-
import
|
|
30005
|
-
import
|
|
30095
|
+
import fs18 from "fs";
|
|
30096
|
+
import os15 from "os";
|
|
30097
|
+
import path16 from "path";
|
|
30006
30098
|
import * as p2 from "@clack/prompts";
|
|
30007
30099
|
var REPO2 = "Project-White-Rabbit/bitfab-codex-plugin";
|
|
30008
30100
|
var PLUGIN_KEY2 = "bitfab@bitfab";
|
|
@@ -30030,7 +30122,7 @@ function runCodexInstall() {
|
|
|
30030
30122
|
}
|
|
30031
30123
|
s.start("Enabling bitfab plugin in config");
|
|
30032
30124
|
const changed = enableCodexPlugin(
|
|
30033
|
-
|
|
30125
|
+
path16.join(os15.homedir(), ".codex", "config.toml")
|
|
30034
30126
|
);
|
|
30035
30127
|
if (changed) {
|
|
30036
30128
|
s.stop("Plugin enabled");
|
|
@@ -30093,8 +30185,8 @@ function runCodexUpdate(args, skipPermissions) {
|
|
|
30093
30185
|
}
|
|
30094
30186
|
function enableCodexPlugin(configPath) {
|
|
30095
30187
|
let content = "";
|
|
30096
|
-
if (
|
|
30097
|
-
content =
|
|
30188
|
+
if (fs18.existsSync(configPath)) {
|
|
30189
|
+
content = fs18.readFileSync(configPath, "utf-8");
|
|
30098
30190
|
}
|
|
30099
30191
|
const header = `[plugins."${PLUGIN_KEY2}"]`;
|
|
30100
30192
|
const desiredSection = `${header}
|
|
@@ -30117,8 +30209,8 @@ enabled = true
|
|
|
30117
30209
|
|
|
30118
30210
|
${desiredSection}`;
|
|
30119
30211
|
}
|
|
30120
|
-
|
|
30121
|
-
|
|
30212
|
+
fs18.mkdirSync(path16.dirname(configPath), { recursive: true });
|
|
30213
|
+
fs18.writeFileSync(configPath, next);
|
|
30122
30214
|
return true;
|
|
30123
30215
|
}
|
|
30124
30216
|
function escapeRegex2(s) {
|
|
@@ -30127,7 +30219,7 @@ function escapeRegex2(s) {
|
|
|
30127
30219
|
|
|
30128
30220
|
// src/cursor.ts
|
|
30129
30221
|
import { spawn as spawn4, spawnSync as spawnSync3 } from "child_process";
|
|
30130
|
-
import
|
|
30222
|
+
import os16 from "os";
|
|
30131
30223
|
import * as p3 from "@clack/prompts";
|
|
30132
30224
|
var REPO3 = "Project-White-Rabbit/bitfab-cursor-plugin";
|
|
30133
30225
|
var ADD_PLUGIN_CMD = `/add-plugin ${REPO3}`;
|
|
@@ -30199,13 +30291,13 @@ function copyToClipboard(text) {
|
|
|
30199
30291
|
return result.status === 0;
|
|
30200
30292
|
}
|
|
30201
30293
|
function clipboardCommand() {
|
|
30202
|
-
if (
|
|
30294
|
+
if (os16.platform() === "darwin") {
|
|
30203
30295
|
return { bin: "pbcopy", args: [] };
|
|
30204
30296
|
}
|
|
30205
|
-
if (
|
|
30297
|
+
if (os16.platform() === "linux") {
|
|
30206
30298
|
return { bin: "xclip", args: ["-selection", "clipboard"] };
|
|
30207
30299
|
}
|
|
30208
|
-
if (
|
|
30300
|
+
if (os16.platform() === "win32") {
|
|
30209
30301
|
return { bin: "clip", args: [] };
|
|
30210
30302
|
}
|
|
30211
30303
|
return null;
|
|
@@ -30229,12 +30321,12 @@ var platform = {
|
|
|
30229
30321
|
};
|
|
30230
30322
|
|
|
30231
30323
|
// src/version.ts
|
|
30232
|
-
import
|
|
30233
|
-
import
|
|
30234
|
-
import { fileURLToPath as
|
|
30324
|
+
import fs19 from "fs";
|
|
30325
|
+
import path17 from "path";
|
|
30326
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
30235
30327
|
var CLI_VERSION = JSON.parse(
|
|
30236
|
-
|
|
30237
|
-
|
|
30328
|
+
fs19.readFileSync(
|
|
30329
|
+
path17.join(path17.dirname(fileURLToPath3(import.meta.url)), "../package.json"),
|
|
30238
30330
|
"utf-8"
|
|
30239
30331
|
)
|
|
30240
30332
|
).version;
|
|
@@ -30509,22 +30601,7 @@ function parseArgs2(argv) {
|
|
|
30509
30601
|
}
|
|
30510
30602
|
return { command, editor, skipPermissions, rest };
|
|
30511
30603
|
}
|
|
30512
|
-
async function handleDrainArg(argv) {
|
|
30513
|
-
const drainIdx = argv.indexOf("--drain");
|
|
30514
|
-
if (drainIdx === -1) {
|
|
30515
|
-
return false;
|
|
30516
|
-
}
|
|
30517
|
-
const sessionId = argv[drainIdx + 1];
|
|
30518
|
-
if (!sessionId) {
|
|
30519
|
-
throw new Error("--drain requires a sessionId argument");
|
|
30520
|
-
}
|
|
30521
|
-
await runLoginWindowMonitor(sessionId);
|
|
30522
|
-
return true;
|
|
30523
|
-
}
|
|
30524
30604
|
async function main() {
|
|
30525
|
-
if (await handleDrainArg(process.argv)) {
|
|
30526
|
-
return;
|
|
30527
|
-
}
|
|
30528
30605
|
const { command, editor, skipPermissions, rest } = parseArgs2(
|
|
30529
30606
|
process.argv.slice(2)
|
|
30530
30607
|
);
|
|
@@ -30581,7 +30658,6 @@ main().catch((err) => {
|
|
|
30581
30658
|
process.exit(1);
|
|
30582
30659
|
});
|
|
30583
30660
|
export {
|
|
30584
|
-
handleDrainArg,
|
|
30585
30661
|
parseArgs2 as parseArgs,
|
|
30586
30662
|
wantsHelp
|
|
30587
30663
|
};
|