bitfab-cli 0.2.84 → 0.2.85
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 +900 -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,18 @@ var NotAuthenticatedError = class extends Error {
|
|
|
8738
8742
|
this.name = "NotAuthenticatedError";
|
|
8739
8743
|
}
|
|
8740
8744
|
};
|
|
8745
|
+
var StudioNavigationError = class extends Error {
|
|
8746
|
+
reason;
|
|
8747
|
+
blockedReason;
|
|
8748
|
+
staleSessionId;
|
|
8749
|
+
constructor(reason, blockedReason, staleSessionId) {
|
|
8750
|
+
super(blockedReason ? `Studio navigation failed (${reason}): ${blockedReason}` : `Studio navigation failed: ${reason}`);
|
|
8751
|
+
this.reason = reason;
|
|
8752
|
+
this.blockedReason = blockedReason;
|
|
8753
|
+
this.staleSessionId = staleSessionId;
|
|
8754
|
+
this.name = "StudioNavigationError";
|
|
8755
|
+
}
|
|
8756
|
+
};
|
|
8741
8757
|
|
|
8742
8758
|
// ../bitfab-plugin-lib/dist/studioRoutes.js
|
|
8743
8759
|
var STUDIO_ROUTE_PATTERNS = [
|
|
@@ -8805,7 +8821,7 @@ async function pollAgentSessionEvents(opts) {
|
|
|
8805
8821
|
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
8806
8822
|
const client = {
|
|
8807
8823
|
serviceUrl: opts.serviceUrl,
|
|
8808
|
-
apiKey: opts.apiKey,
|
|
8824
|
+
apiKey: opts.apiKey ?? "",
|
|
8809
8825
|
sessionId: opts.sessionId
|
|
8810
8826
|
};
|
|
8811
8827
|
let since = opts.startCursor ?? null;
|
|
@@ -8816,8 +8832,9 @@ async function pollAgentSessionEvents(opts) {
|
|
|
8816
8832
|
if (since) {
|
|
8817
8833
|
url2.searchParams.set("since", since);
|
|
8818
8834
|
}
|
|
8835
|
+
const authHeaders = opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : { [STUDIO_SESSION_SCOPE_HEADER]: STUDIO_SESSION_SCOPE_VALUE };
|
|
8819
8836
|
const res = await fetch(url2.toString(), {
|
|
8820
|
-
headers:
|
|
8837
|
+
headers: authHeaders,
|
|
8821
8838
|
signal: opts.abortSignal
|
|
8822
8839
|
});
|
|
8823
8840
|
if (res.ok) {
|
|
@@ -8857,124 +8874,13 @@ async function pollAgentSessionEvents(opts) {
|
|
|
8857
8874
|
});
|
|
8858
8875
|
}
|
|
8859
8876
|
}
|
|
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
8877
|
var STUDIO_SESSION_SCOPE_HEADER = "x-bitfab-auth-scope";
|
|
8902
8878
|
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
8879
|
async function fetchStreamTip(client) {
|
|
8975
8880
|
const PAGE_LIMIT = 500;
|
|
8976
8881
|
const MAX_PAGES = 10;
|
|
8977
8882
|
let tip;
|
|
8883
|
+
const authHeaders = client.apiKey ? { Authorization: `Bearer ${client.apiKey}` } : { [STUDIO_SESSION_SCOPE_HEADER]: STUDIO_SESSION_SCOPE_VALUE };
|
|
8978
8884
|
try {
|
|
8979
8885
|
for (let page = 0; page < MAX_PAGES; page++) {
|
|
8980
8886
|
const url2 = new URL(`${client.serviceUrl}/api/studio/events`);
|
|
@@ -8984,7 +8890,7 @@ async function fetchStreamTip(client) {
|
|
|
8984
8890
|
url2.searchParams.set("since", tip);
|
|
8985
8891
|
}
|
|
8986
8892
|
const res = await fetch(url2.toString(), {
|
|
8987
|
-
headers:
|
|
8893
|
+
headers: authHeaders
|
|
8988
8894
|
});
|
|
8989
8895
|
if (!res.ok) {
|
|
8990
8896
|
return tip;
|
|
@@ -9004,18 +8910,24 @@ async function fetchStreamTip(client) {
|
|
|
9004
8910
|
}
|
|
9005
8911
|
return tip;
|
|
9006
8912
|
}
|
|
9007
|
-
async function
|
|
9008
|
-
|
|
9009
|
-
|
|
8913
|
+
async function closeStudio(client, message) {
|
|
8914
|
+
return pushAgentSessionEvent(client, {
|
|
8915
|
+
type: "agent:close-studio",
|
|
8916
|
+
data: message ? { message } : {}
|
|
8917
|
+
});
|
|
8918
|
+
}
|
|
8919
|
+
async function navigateStudio(client, path18) {
|
|
8920
|
+
if (!isValidStudioRoute(path18)) {
|
|
8921
|
+
throw new Error(`Studio route not allowed: ${path18}`);
|
|
9010
8922
|
}
|
|
9011
8923
|
return pushAgentSessionEvent(client, {
|
|
9012
8924
|
type: "agent:navigate",
|
|
9013
|
-
data: { path:
|
|
8925
|
+
data: { path: path18 }
|
|
9014
8926
|
});
|
|
9015
8927
|
}
|
|
9016
8928
|
var STUDIO_NAVIGATE_ACK_TIMEOUT_MS = 12e3;
|
|
9017
8929
|
var PING_PONG_TIMEOUT_MS = 5e3;
|
|
9018
|
-
function awaitNavigateAck(client,
|
|
8930
|
+
function awaitNavigateAck(client, path18, ackTimeoutMs, startCursor) {
|
|
9019
8931
|
const abortController = new AbortController();
|
|
9020
8932
|
let timer = null;
|
|
9021
8933
|
const result = new Promise((resolve) => {
|
|
@@ -9023,7 +8935,7 @@ function awaitNavigateAck(client, path15, ackTimeoutMs, startCursor) {
|
|
|
9023
8935
|
resolve({ acked: false, reason: "timeout" });
|
|
9024
8936
|
abortController.abort();
|
|
9025
8937
|
}, ackTimeoutMs);
|
|
9026
|
-
const pathOnly =
|
|
8938
|
+
const pathOnly = path18.split("?")[0];
|
|
9027
8939
|
pollAgentSessionEvents({
|
|
9028
8940
|
serviceUrl: client.serviceUrl,
|
|
9029
8941
|
apiKey: client.apiKey,
|
|
@@ -9109,18 +9021,18 @@ function awaitPong(client, pingTs) {
|
|
|
9109
9021
|
});
|
|
9110
9022
|
});
|
|
9111
9023
|
}
|
|
9112
|
-
async function navigateStudioAndAwaitAck(client,
|
|
9024
|
+
async function navigateStudioAndAwaitAck(client, path18, opts = {}) {
|
|
9113
9025
|
const ackTimeoutMs = opts.ackTimeoutMs ?? STUDIO_NAVIGATE_ACK_TIMEOUT_MS;
|
|
9114
|
-
if (!isValidStudioRoute(
|
|
9026
|
+
if (!isValidStudioRoute(path18)) {
|
|
9115
9027
|
return { acked: false, reason: "invalid-route" };
|
|
9116
9028
|
}
|
|
9117
9029
|
const startCursor = await fetchStreamTip(client);
|
|
9118
9030
|
try {
|
|
9119
|
-
await navigateStudio(client,
|
|
9031
|
+
await navigateStudio(client, path18);
|
|
9120
9032
|
} catch {
|
|
9121
9033
|
return { acked: false, reason: "push-failed" };
|
|
9122
9034
|
}
|
|
9123
|
-
const result = await awaitNavigateAck(client,
|
|
9035
|
+
const result = await awaitNavigateAck(client, path18, ackTimeoutMs, startCursor);
|
|
9124
9036
|
if (result.acked || result.reason !== "timeout") {
|
|
9125
9037
|
return result;
|
|
9126
9038
|
}
|
|
@@ -9181,9 +9093,9 @@ var GLOBAL_CONFIG_FILE = path3.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
|
9181
9093
|
var GLOBAL_CREDENTIALS_FILE = path3.join(GLOBAL_CONFIG_DIR, "credentials.json");
|
|
9182
9094
|
var PROJECT_CONFIG_RELATIVE = path3.join(".bitfab", "config.local.json");
|
|
9183
9095
|
var PROJECT_CREDENTIALS_RELATIVE = path3.join(".bitfab", "credentials.local.json");
|
|
9184
|
-
function readJsonFile(
|
|
9096
|
+
function readJsonFile(filePath) {
|
|
9185
9097
|
try {
|
|
9186
|
-
const content = fs3.readFileSync(
|
|
9098
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
9187
9099
|
return JSON.parse(content);
|
|
9188
9100
|
} catch {
|
|
9189
9101
|
return null;
|
|
@@ -9204,18 +9116,18 @@ function findProjectFile(relativePath, startDir = process.cwd()) {
|
|
|
9204
9116
|
}
|
|
9205
9117
|
}
|
|
9206
9118
|
function getProjectConfigData() {
|
|
9207
|
-
const
|
|
9208
|
-
if (!
|
|
9119
|
+
const filePath = findProjectFile(PROJECT_CONFIG_RELATIVE);
|
|
9120
|
+
if (!filePath) {
|
|
9209
9121
|
return null;
|
|
9210
9122
|
}
|
|
9211
|
-
return readJsonFile(
|
|
9123
|
+
return readJsonFile(filePath);
|
|
9212
9124
|
}
|
|
9213
9125
|
function getProjectCredentialsData() {
|
|
9214
|
-
const
|
|
9215
|
-
if (!
|
|
9126
|
+
const filePath = findProjectFile(PROJECT_CREDENTIALS_RELATIVE);
|
|
9127
|
+
if (!filePath) {
|
|
9216
9128
|
return null;
|
|
9217
9129
|
}
|
|
9218
|
-
return readJsonFile(
|
|
9130
|
+
return readJsonFile(filePath) ?? {};
|
|
9219
9131
|
}
|
|
9220
9132
|
function getConfigData() {
|
|
9221
9133
|
return readJsonFile(GLOBAL_CONFIG_FILE) ?? {};
|
|
@@ -9345,10 +9257,34 @@ function buildBitfabRequestHeaders(apiKey, pluginVersion, platform2) {
|
|
|
9345
9257
|
|
|
9346
9258
|
// ../bitfab-plugin-lib/dist/activeStudioSession.js
|
|
9347
9259
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9260
|
+
import crypto4 from "crypto";
|
|
9261
|
+
import fs7 from "fs";
|
|
9262
|
+
import os7 from "os";
|
|
9263
|
+
import path6 from "path";
|
|
9264
|
+
|
|
9265
|
+
// ../bitfab-plugin-lib/dist/agentSessionKey.js
|
|
9348
9266
|
import crypto3 from "crypto";
|
|
9349
9267
|
import fs6 from "fs";
|
|
9350
9268
|
import os6 from "os";
|
|
9351
9269
|
import path5 from "path";
|
|
9270
|
+
var FALLBACK_KEY_PATH = path5.join(os6.homedir(), ".config", "bitfab", "agent-session-key");
|
|
9271
|
+
function readOrCreateFallbackKey() {
|
|
9272
|
+
try {
|
|
9273
|
+
const existing = fs6.readFileSync(FALLBACK_KEY_PATH, "utf-8").trim();
|
|
9274
|
+
if (existing) {
|
|
9275
|
+
return existing;
|
|
9276
|
+
}
|
|
9277
|
+
} catch {
|
|
9278
|
+
}
|
|
9279
|
+
const key = crypto3.randomUUID();
|
|
9280
|
+
fs6.mkdirSync(path5.dirname(FALLBACK_KEY_PATH), { recursive: true });
|
|
9281
|
+
fs6.writeFileSync(FALLBACK_KEY_PATH, `${key}
|
|
9282
|
+
`);
|
|
9283
|
+
return key;
|
|
9284
|
+
}
|
|
9285
|
+
function resolveAgentSessionKey() {
|
|
9286
|
+
return process.env.CLAUDE_CODE_SESSION_ID ?? process.env.CURSOR_SESSION_ID ?? process.env.CODEX_THREAD_ID ?? readOrCreateFallbackKey();
|
|
9287
|
+
}
|
|
9352
9288
|
|
|
9353
9289
|
// ../bitfab-plugin-lib/dist/getAgentSessionId.js
|
|
9354
9290
|
function getAgentSessionId() {
|
|
@@ -9356,186 +9292,104 @@ function getAgentSessionId() {
|
|
|
9356
9292
|
}
|
|
9357
9293
|
|
|
9358
9294
|
// ../bitfab-plugin-lib/dist/activeStudioSession.js
|
|
9295
|
+
var SESSIONS_DIR = path6.join(os7.homedir(), ".config", "bitfab", "sessions");
|
|
9359
9296
|
function shortHash(input) {
|
|
9360
|
-
return
|
|
9297
|
+
return crypto4.createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
9361
9298
|
}
|
|
9362
|
-
function
|
|
9363
|
-
return shortHash(
|
|
9299
|
+
function sessionFilePathForKey(key) {
|
|
9300
|
+
return path6.join(SESSIONS_DIR, `${shortHash(key)}.json`);
|
|
9364
9301
|
}
|
|
9365
|
-
function
|
|
9366
|
-
return
|
|
9302
|
+
function sessionFilePath() {
|
|
9303
|
+
return sessionFilePathForKey(resolveAgentSessionKey());
|
|
9367
9304
|
}
|
|
9368
|
-
function
|
|
9369
|
-
|
|
9370
|
-
fs6.mkdirSync(path5.dirname(target), { recursive: true });
|
|
9305
|
+
function writeStateFile(target, state) {
|
|
9306
|
+
fs7.mkdirSync(path6.dirname(target), { recursive: true });
|
|
9371
9307
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
9372
|
-
|
|
9308
|
+
fs7.writeFileSync(tmp, `${JSON.stringify(state, null, 2)}
|
|
9373
9309
|
`);
|
|
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
|
-
}
|
|
9310
|
+
fs7.renameSync(tmp, target);
|
|
9406
9311
|
}
|
|
9407
|
-
function
|
|
9312
|
+
function readStateFile(filePath) {
|
|
9408
9313
|
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");
|
|
9314
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
9425
9315
|
const parsed = JSON.parse(raw);
|
|
9426
|
-
if (parsed.
|
|
9316
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.serviceUrl !== "string") {
|
|
9427
9317
|
return null;
|
|
9428
9318
|
}
|
|
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;
|
|
9319
|
+
return parsed;
|
|
9439
9320
|
} catch {
|
|
9440
9321
|
return null;
|
|
9441
9322
|
}
|
|
9442
9323
|
}
|
|
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;
|
|
9324
|
+
function legacySessionScope() {
|
|
9325
|
+
return shortHash(getAgentSessionId() ?? process.cwd());
|
|
9478
9326
|
}
|
|
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();
|
|
9327
|
+
function legacyFilePath() {
|
|
9328
|
+
return path6.join(os7.homedir(), ".config", "bitfab", `active-studio-session.${legacySessionScope()}.json`);
|
|
9487
9329
|
}
|
|
9488
|
-
function
|
|
9330
|
+
function readLegacySession() {
|
|
9489
9331
|
try {
|
|
9490
|
-
const
|
|
9491
|
-
|
|
9492
|
-
|
|
9332
|
+
const raw = fs7.readFileSync(legacyFilePath(), "utf-8");
|
|
9333
|
+
const parsed = JSON.parse(raw);
|
|
9334
|
+
if (parsed.closedAt != null || parsed.endedAt != null) {
|
|
9335
|
+
return null;
|
|
9493
9336
|
}
|
|
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)) {
|
|
9337
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.serviceUrl !== "string" || typeof parsed.pid !== "number" || typeof parsed.startedAt !== "string") {
|
|
9519
9338
|
return null;
|
|
9520
9339
|
}
|
|
9521
|
-
|
|
9340
|
+
const legacy = parsed;
|
|
9341
|
+
return {
|
|
9342
|
+
sessionId: legacy.sessionId,
|
|
9343
|
+
serviceUrl: legacy.serviceUrl,
|
|
9344
|
+
windowPid: legacy.windowPid ?? null,
|
|
9345
|
+
pollerPid: legacy.pid,
|
|
9346
|
+
source: "direct",
|
|
9347
|
+
openedAt: legacy.startedAt,
|
|
9348
|
+
currentPath: null,
|
|
9349
|
+
authenticated: true,
|
|
9350
|
+
windowState: "open",
|
|
9351
|
+
agentIntent: "open",
|
|
9352
|
+
lastBrowserPingAt: null,
|
|
9353
|
+
browserConnected: false,
|
|
9354
|
+
lastEventAt: null,
|
|
9355
|
+
lastEventCursor: null,
|
|
9356
|
+
agentSessionKey: null
|
|
9357
|
+
};
|
|
9522
9358
|
} catch {
|
|
9523
9359
|
return null;
|
|
9524
9360
|
}
|
|
9525
9361
|
}
|
|
9526
|
-
function
|
|
9527
|
-
const
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9362
|
+
function writeActiveStudioSession(init) {
|
|
9363
|
+
const state = {
|
|
9364
|
+
sessionId: init.sessionId,
|
|
9365
|
+
serviceUrl: init.serviceUrl,
|
|
9366
|
+
windowPid: init.windowPid ?? null,
|
|
9367
|
+
pollerPid: process.pid,
|
|
9368
|
+
source: init.source ?? "direct",
|
|
9369
|
+
openedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9370
|
+
currentPath: init.currentPath ?? null,
|
|
9371
|
+
authenticated: init.authenticated ?? true,
|
|
9372
|
+
windowState: "open",
|
|
9373
|
+
agentIntent: "open",
|
|
9374
|
+
lastBrowserPingAt: null,
|
|
9375
|
+
browserConnected: false,
|
|
9376
|
+
lastEventAt: null,
|
|
9377
|
+
lastEventCursor: null,
|
|
9378
|
+
agentSessionKey: null
|
|
9379
|
+
};
|
|
9380
|
+
writeStateFile(sessionFilePath(), state);
|
|
9531
9381
|
}
|
|
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.`;
|
|
9382
|
+
function readActiveStudioSession() {
|
|
9383
|
+
return readStateFile(sessionFilePath()) ?? readLegacySession();
|
|
9536
9384
|
}
|
|
9537
|
-
function
|
|
9538
|
-
|
|
9385
|
+
function updateActiveStudioSession(updates) {
|
|
9386
|
+
const current = readStateFile(sessionFilePath());
|
|
9387
|
+
if (!current) {
|
|
9388
|
+
return null;
|
|
9389
|
+
}
|
|
9390
|
+
const updated = { ...current, ...updates };
|
|
9391
|
+
writeStateFile(sessionFilePath(), updated);
|
|
9392
|
+
return updated;
|
|
9539
9393
|
}
|
|
9540
9394
|
|
|
9541
9395
|
// ../bitfab-plugin-lib/dist/parseArgs.js
|
|
@@ -9604,42 +9458,15 @@ function extractCommandName(argv) {
|
|
|
9604
9458
|
return argv[1]?.replace(/.*\//, "").replace(/\.[jt]s$/, "") ?? "command";
|
|
9605
9459
|
}
|
|
9606
9460
|
|
|
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
|
-
}
|
|
9461
|
+
// ../bitfab-plugin-lib/dist/studioChannel.js
|
|
9462
|
+
import crypto6 from "crypto";
|
|
9636
9463
|
|
|
9637
9464
|
// ../bitfab-plugin-lib/dist/browser.js
|
|
9638
9465
|
import { execSync, spawn } from "child_process";
|
|
9639
|
-
import
|
|
9640
|
-
import
|
|
9466
|
+
import fs8 from "fs";
|
|
9467
|
+
import os8 from "os";
|
|
9641
9468
|
function getChromiumBrowsers() {
|
|
9642
|
-
const platform2 =
|
|
9469
|
+
const platform2 = os8.platform();
|
|
9643
9470
|
if (platform2 === "darwin") {
|
|
9644
9471
|
return [
|
|
9645
9472
|
{
|
|
@@ -9727,7 +9554,7 @@ function getChromiumBrowsers() {
|
|
|
9727
9554
|
function findExistingBinary(paths) {
|
|
9728
9555
|
for (const candidate of paths) {
|
|
9729
9556
|
if (candidate.includes("/") || candidate.includes("\\")) {
|
|
9730
|
-
if (
|
|
9557
|
+
if (fs8.existsSync(candidate)) {
|
|
9731
9558
|
return candidate;
|
|
9732
9559
|
}
|
|
9733
9560
|
} else {
|
|
@@ -9741,10 +9568,10 @@ function findExistingBinary(paths) {
|
|
|
9741
9568
|
return null;
|
|
9742
9569
|
}
|
|
9743
9570
|
function getDefaultBrowserId() {
|
|
9744
|
-
const platform2 =
|
|
9571
|
+
const platform2 = os8.platform();
|
|
9745
9572
|
try {
|
|
9746
9573
|
if (platform2 === "darwin") {
|
|
9747
|
-
const plistPath = `${
|
|
9574
|
+
const plistPath = `${os8.homedir()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
|
|
9748
9575
|
const json2 = execSync(`plutil -convert json -o - "${plistPath}"`, {
|
|
9749
9576
|
stdio: ["ignore", "pipe", "ignore"],
|
|
9750
9577
|
encoding: "utf-8",
|
|
@@ -9785,7 +9612,7 @@ function matchChromiumBrowser(defaultId, browsers) {
|
|
|
9785
9612
|
var SCREEN_FRACTION = 0.95;
|
|
9786
9613
|
function getWorkArea() {
|
|
9787
9614
|
try {
|
|
9788
|
-
const platform2 =
|
|
9615
|
+
const platform2 = os8.platform();
|
|
9789
9616
|
if (platform2 === "darwin") {
|
|
9790
9617
|
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
9618
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -9845,6 +9672,9 @@ function getWindowSizeArgs() {
|
|
|
9845
9672
|
return computeWindowArgs(getWorkArea());
|
|
9846
9673
|
}
|
|
9847
9674
|
function spawnDetached(command, args) {
|
|
9675
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
9676
|
+
throw new Error("spawnDetached called in a test environment without being mocked");
|
|
9677
|
+
}
|
|
9848
9678
|
const child = spawn(command, [...args], { detached: true, stdio: "ignore" });
|
|
9849
9679
|
child.on("error", () => {
|
|
9850
9680
|
});
|
|
@@ -9855,14 +9685,14 @@ function openChromiumApp(binaryPath, url2, sizeArgs) {
|
|
|
9855
9685
|
return spawnDetached(binaryPath, [`--app=${url2}`, ...sizeArgs]);
|
|
9856
9686
|
}
|
|
9857
9687
|
function openOsDefault(url2) {
|
|
9858
|
-
const platform2 =
|
|
9688
|
+
const platform2 = os8.platform();
|
|
9859
9689
|
if (platform2 === "darwin") {
|
|
9860
|
-
spawnDetached("open", [url2]);
|
|
9861
|
-
}
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
spawnDetached("xdg-open", [url2]);
|
|
9690
|
+
return spawnDetached("open", [url2]);
|
|
9691
|
+
}
|
|
9692
|
+
if (platform2 === "win32") {
|
|
9693
|
+
return spawnDetached("cmd", ["/c", "start", "", url2]);
|
|
9865
9694
|
}
|
|
9695
|
+
return spawnDetached("xdg-open", [url2]);
|
|
9866
9696
|
}
|
|
9867
9697
|
function isCodexOnMacos() {
|
|
9868
9698
|
if (process.platform !== "darwin") {
|
|
@@ -9876,7 +9706,22 @@ function shouldDisableChromelessWindows() {
|
|
|
9876
9706
|
}
|
|
9877
9707
|
return isCodexOnMacos();
|
|
9878
9708
|
}
|
|
9709
|
+
var WINDOW_OPEN_RATE_WINDOW_MS = 3e4;
|
|
9710
|
+
var WINDOW_OPEN_RATE_LIMIT = 3;
|
|
9711
|
+
var recentOpens = [];
|
|
9879
9712
|
function openChromelessWindow(url2) {
|
|
9713
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
9714
|
+
throw new Error("openChromelessWindow called in a test environment without being mocked");
|
|
9715
|
+
}
|
|
9716
|
+
const now = Date.now();
|
|
9717
|
+
while (recentOpens.length > 0 && now - recentOpens[0] > WINDOW_OPEN_RATE_WINDOW_MS) {
|
|
9718
|
+
recentOpens.shift();
|
|
9719
|
+
}
|
|
9720
|
+
if (recentOpens.length >= WINDOW_OPEN_RATE_LIMIT) {
|
|
9721
|
+
console.error(`[browser] rate limit: ${recentOpens.length} windows opened in the last ${WINDOW_OPEN_RATE_WINDOW_MS / 1e3}s, refusing to open another`);
|
|
9722
|
+
return null;
|
|
9723
|
+
}
|
|
9724
|
+
recentOpens.push(now);
|
|
9880
9725
|
if (shouldDisableChromelessWindows()) {
|
|
9881
9726
|
openOsDefault(url2);
|
|
9882
9727
|
return null;
|
|
@@ -9932,11 +9777,537 @@ async function createStudioSession({ serviceUrl, apiKey, sessionId, initialPath
|
|
|
9932
9777
|
writeActiveStudioSession({
|
|
9933
9778
|
sessionId,
|
|
9934
9779
|
serviceUrl,
|
|
9935
|
-
windowPid
|
|
9780
|
+
windowPid,
|
|
9781
|
+
currentPath: initialPath
|
|
9936
9782
|
});
|
|
9937
9783
|
return { sessionId, serviceUrl };
|
|
9938
9784
|
}
|
|
9939
9785
|
|
|
9786
|
+
// ../bitfab-plugin-lib/dist/daemon/client.js
|
|
9787
|
+
import net2 from "net";
|
|
9788
|
+
|
|
9789
|
+
// ../bitfab-plugin-lib/dist/daemon/ensure.js
|
|
9790
|
+
import { fork } from "child_process";
|
|
9791
|
+
import crypto5 from "crypto";
|
|
9792
|
+
import fs9 from "fs";
|
|
9793
|
+
import net from "net";
|
|
9794
|
+
import path8 from "path";
|
|
9795
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9796
|
+
|
|
9797
|
+
// ../bitfab-plugin-lib/dist/daemon/protocol.js
|
|
9798
|
+
import os9 from "os";
|
|
9799
|
+
import path7 from "path";
|
|
9800
|
+
var BITFAB_CONFIG_DIR = path7.join(os9.homedir(), ".config", "bitfab");
|
|
9801
|
+
var SOCKET_PATH = path7.join(BITFAB_CONFIG_DIR, "studio-daemon.sock");
|
|
9802
|
+
var PID_FILE_PATH = path7.join(BITFAB_CONFIG_DIR, "studio-daemon.pid");
|
|
9803
|
+
var BROWSER_HEALTH_INTERVAL_MS = 10 * 1e3;
|
|
9804
|
+
var ENSURE_POLL_INTERVAL_MS = 200;
|
|
9805
|
+
var ENSURE_TIMEOUT_MS = 5e3;
|
|
9806
|
+
var PING_TIMEOUT_MS = 2e3;
|
|
9807
|
+
function serialize(msg) {
|
|
9808
|
+
return `${JSON.stringify(msg)}
|
|
9809
|
+
`;
|
|
9810
|
+
}
|
|
9811
|
+
function isResponse(msg) {
|
|
9812
|
+
return typeof msg === "object" && msg !== null && "ok" in msg;
|
|
9813
|
+
}
|
|
9814
|
+
function isPush(msg) {
|
|
9815
|
+
return typeof msg === "object" && msg !== null && "event" in msg;
|
|
9816
|
+
}
|
|
9817
|
+
|
|
9818
|
+
// ../bitfab-plugin-lib/dist/daemon/ensure.js
|
|
9819
|
+
var LOCK_STALE_MS = 1e4;
|
|
9820
|
+
function processIsAlive(pid) {
|
|
9821
|
+
try {
|
|
9822
|
+
process.kill(pid, 0);
|
|
9823
|
+
return true;
|
|
9824
|
+
} catch (err) {
|
|
9825
|
+
return err.code !== "ESRCH";
|
|
9826
|
+
}
|
|
9827
|
+
}
|
|
9828
|
+
function readPid(pidPath) {
|
|
9829
|
+
try {
|
|
9830
|
+
const raw = fs9.readFileSync(pidPath, "utf-8").trim();
|
|
9831
|
+
const pid = Number.parseInt(raw, 10);
|
|
9832
|
+
return Number.isNaN(pid) ? null : pid;
|
|
9833
|
+
} catch {
|
|
9834
|
+
return null;
|
|
9835
|
+
}
|
|
9836
|
+
}
|
|
9837
|
+
async function pingSocket(socketPath) {
|
|
9838
|
+
return new Promise((resolve) => {
|
|
9839
|
+
const timeout = setTimeout(() => {
|
|
9840
|
+
socket.destroy();
|
|
9841
|
+
resolve({ alive: false, build: null });
|
|
9842
|
+
}, PING_TIMEOUT_MS);
|
|
9843
|
+
const socket = net.createConnection(socketPath, () => {
|
|
9844
|
+
socket.write('{"cmd":"ping"}\n');
|
|
9845
|
+
});
|
|
9846
|
+
let buffer = "";
|
|
9847
|
+
socket.on("data", (chunk) => {
|
|
9848
|
+
buffer += chunk.toString();
|
|
9849
|
+
if (buffer.includes("\n")) {
|
|
9850
|
+
clearTimeout(timeout);
|
|
9851
|
+
socket.destroy();
|
|
9852
|
+
try {
|
|
9853
|
+
const msg = JSON.parse(buffer.split("\n")[0]);
|
|
9854
|
+
resolve({ alive: msg.ok === true, build: msg.build ?? null });
|
|
9855
|
+
} catch {
|
|
9856
|
+
resolve({ alive: false, build: null });
|
|
9857
|
+
}
|
|
9858
|
+
}
|
|
9859
|
+
});
|
|
9860
|
+
socket.on("error", () => {
|
|
9861
|
+
clearTimeout(timeout);
|
|
9862
|
+
resolve({ alive: false, build: null });
|
|
9863
|
+
});
|
|
9864
|
+
});
|
|
9865
|
+
}
|
|
9866
|
+
async function waitForSocket(socketPath, timeoutMs) {
|
|
9867
|
+
const deadline = Date.now() + timeoutMs;
|
|
9868
|
+
while (Date.now() < deadline) {
|
|
9869
|
+
const { alive } = await pingSocket(socketPath);
|
|
9870
|
+
if (alive) {
|
|
9871
|
+
return;
|
|
9872
|
+
}
|
|
9873
|
+
await new Promise((r) => setTimeout(r, ENSURE_POLL_INTERVAL_MS));
|
|
9874
|
+
}
|
|
9875
|
+
throw new Error(`daemon did not become ready within ${timeoutMs}ms`);
|
|
9876
|
+
}
|
|
9877
|
+
function localBuildFingerprint() {
|
|
9878
|
+
try {
|
|
9879
|
+
const thisDir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
9880
|
+
const serverPath = path8.join(thisDir, "server.js");
|
|
9881
|
+
const content = fs9.readFileSync(serverPath);
|
|
9882
|
+
return crypto5.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
9883
|
+
} catch {
|
|
9884
|
+
return "unknown";
|
|
9885
|
+
}
|
|
9886
|
+
}
|
|
9887
|
+
async function killDaemon(pidPath, socketPath) {
|
|
9888
|
+
const pid = readPid(pidPath);
|
|
9889
|
+
if (pid !== null) {
|
|
9890
|
+
try {
|
|
9891
|
+
process.kill(pid, "SIGTERM");
|
|
9892
|
+
} catch {
|
|
9893
|
+
}
|
|
9894
|
+
const deadline = Date.now() + 5e3;
|
|
9895
|
+
while (processIsAlive(pid) && Date.now() < deadline) {
|
|
9896
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
9897
|
+
}
|
|
9898
|
+
if (processIsAlive(pid)) {
|
|
9899
|
+
try {
|
|
9900
|
+
process.kill(pid, "SIGKILL");
|
|
9901
|
+
} catch {
|
|
9902
|
+
}
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
cleanupStaleFiles(socketPath, pidPath);
|
|
9906
|
+
}
|
|
9907
|
+
function spawnDaemon(socketPath, pidPath) {
|
|
9908
|
+
const thisDir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
9909
|
+
const mainPath = path8.join(thisDir, "main.js");
|
|
9910
|
+
if (!fs9.existsSync(mainPath)) {
|
|
9911
|
+
throw new Error(`daemon entry point not found: ${mainPath}`);
|
|
9912
|
+
}
|
|
9913
|
+
const child = fork(mainPath, [socketPath, pidPath], {
|
|
9914
|
+
detached: true,
|
|
9915
|
+
stdio: "ignore",
|
|
9916
|
+
execArgv: []
|
|
9917
|
+
});
|
|
9918
|
+
child.unref();
|
|
9919
|
+
}
|
|
9920
|
+
function cleanupStaleFiles(socketPath, pidPath) {
|
|
9921
|
+
try {
|
|
9922
|
+
fs9.unlinkSync(socketPath);
|
|
9923
|
+
} catch {
|
|
9924
|
+
}
|
|
9925
|
+
try {
|
|
9926
|
+
fs9.unlinkSync(pidPath);
|
|
9927
|
+
} catch {
|
|
9928
|
+
}
|
|
9929
|
+
}
|
|
9930
|
+
function lockPath(socketPath) {
|
|
9931
|
+
return `${socketPath}.lock`;
|
|
9932
|
+
}
|
|
9933
|
+
function acquireLock(socketPath) {
|
|
9934
|
+
const lock = lockPath(socketPath);
|
|
9935
|
+
try {
|
|
9936
|
+
fs9.mkdirSync(path8.dirname(lock), { recursive: true });
|
|
9937
|
+
fs9.writeFileSync(lock, `${process.pid}
|
|
9938
|
+
`, { flag: "wx" });
|
|
9939
|
+
return true;
|
|
9940
|
+
} catch (err) {
|
|
9941
|
+
if (err.code !== "EEXIST") {
|
|
9942
|
+
return true;
|
|
9943
|
+
}
|
|
9944
|
+
try {
|
|
9945
|
+
const stat = fs9.statSync(lock);
|
|
9946
|
+
if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) {
|
|
9947
|
+
fs9.unlinkSync(lock);
|
|
9948
|
+
fs9.writeFileSync(lock, `${process.pid}
|
|
9949
|
+
`, { flag: "wx" });
|
|
9950
|
+
return true;
|
|
9951
|
+
}
|
|
9952
|
+
} catch {
|
|
9953
|
+
}
|
|
9954
|
+
return false;
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
function releaseLock(socketPath) {
|
|
9958
|
+
try {
|
|
9959
|
+
fs9.unlinkSync(lockPath(socketPath));
|
|
9960
|
+
} catch {
|
|
9961
|
+
}
|
|
9962
|
+
}
|
|
9963
|
+
async function ensureDaemon(socketPath = SOCKET_PATH, pidPath = PID_FILE_PATH) {
|
|
9964
|
+
const pid = readPid(pidPath);
|
|
9965
|
+
if (pid !== null && processIsAlive(pid)) {
|
|
9966
|
+
const ping = await pingSocket(socketPath);
|
|
9967
|
+
if (ping.alive) {
|
|
9968
|
+
const localBuild = localBuildFingerprint();
|
|
9969
|
+
if (ping.build && ping.build !== localBuild && localBuild !== "unknown") {
|
|
9970
|
+
console.error(`[daemon] build mismatch (running: ${ping.build}, local: ${localBuild}), restarting`);
|
|
9971
|
+
await killDaemon(pidPath, socketPath);
|
|
9972
|
+
} else {
|
|
9973
|
+
return;
|
|
9974
|
+
}
|
|
9975
|
+
}
|
|
9976
|
+
}
|
|
9977
|
+
if (!acquireLock(socketPath)) {
|
|
9978
|
+
await waitForSocket(socketPath, ENSURE_TIMEOUT_MS);
|
|
9979
|
+
return;
|
|
9980
|
+
}
|
|
9981
|
+
try {
|
|
9982
|
+
cleanupStaleFiles(socketPath, pidPath);
|
|
9983
|
+
spawnDaemon(socketPath, pidPath);
|
|
9984
|
+
await waitForSocket(socketPath, ENSURE_TIMEOUT_MS);
|
|
9985
|
+
} finally {
|
|
9986
|
+
releaseLock(socketPath);
|
|
9987
|
+
}
|
|
9988
|
+
}
|
|
9989
|
+
|
|
9990
|
+
// ../bitfab-plugin-lib/dist/daemon/client.js
|
|
9991
|
+
var DaemonClient = class {
|
|
9992
|
+
socket = null;
|
|
9993
|
+
buffer = "";
|
|
9994
|
+
responseQueue = [];
|
|
9995
|
+
eventHandler = null;
|
|
9996
|
+
socketPath;
|
|
9997
|
+
constructor(socketPath) {
|
|
9998
|
+
this.socketPath = socketPath ?? SOCKET_PATH;
|
|
9999
|
+
}
|
|
10000
|
+
async connect() {
|
|
10001
|
+
await ensureDaemon(this.socketPath);
|
|
10002
|
+
return new Promise((resolve, reject) => {
|
|
10003
|
+
const socket = net2.createConnection(this.socketPath, () => {
|
|
10004
|
+
this.socket = socket;
|
|
10005
|
+
resolve();
|
|
10006
|
+
});
|
|
10007
|
+
socket.on("data", (chunk) => {
|
|
10008
|
+
this.buffer += chunk.toString();
|
|
10009
|
+
this.drain();
|
|
10010
|
+
});
|
|
10011
|
+
socket.on("error", (err) => {
|
|
10012
|
+
if (!this.socket) {
|
|
10013
|
+
reject(err);
|
|
10014
|
+
return;
|
|
10015
|
+
}
|
|
10016
|
+
this.socket = null;
|
|
10017
|
+
});
|
|
10018
|
+
socket.on("close", () => {
|
|
10019
|
+
this.socket = null;
|
|
10020
|
+
});
|
|
10021
|
+
});
|
|
10022
|
+
}
|
|
10023
|
+
drain() {
|
|
10024
|
+
let idx = this.buffer.indexOf("\n");
|
|
10025
|
+
while (idx !== -1) {
|
|
10026
|
+
const line = this.buffer.slice(0, idx).trim();
|
|
10027
|
+
this.buffer = this.buffer.slice(idx + 1);
|
|
10028
|
+
if (line) {
|
|
10029
|
+
try {
|
|
10030
|
+
const msg = JSON.parse(line);
|
|
10031
|
+
if (isResponse(msg) && this.responseQueue.length > 0) {
|
|
10032
|
+
const resolver = this.responseQueue.shift();
|
|
10033
|
+
resolver(msg);
|
|
10034
|
+
} else if (isPush(msg) && this.eventHandler) {
|
|
10035
|
+
this.eventHandler(msg);
|
|
10036
|
+
}
|
|
10037
|
+
} catch {
|
|
10038
|
+
}
|
|
10039
|
+
}
|
|
10040
|
+
idx = this.buffer.indexOf("\n");
|
|
10041
|
+
}
|
|
10042
|
+
}
|
|
10043
|
+
sendCommand(cmd) {
|
|
10044
|
+
if (!this.socket) {
|
|
10045
|
+
return Promise.reject(new Error("not connected"));
|
|
10046
|
+
}
|
|
10047
|
+
return new Promise((resolve, reject) => {
|
|
10048
|
+
this.responseQueue.push(resolve);
|
|
10049
|
+
try {
|
|
10050
|
+
this.socket.write(serialize(cmd));
|
|
10051
|
+
} catch (err) {
|
|
10052
|
+
this.responseQueue.pop();
|
|
10053
|
+
reject(err);
|
|
10054
|
+
}
|
|
10055
|
+
});
|
|
10056
|
+
}
|
|
10057
|
+
async open(opts) {
|
|
10058
|
+
const cmd = { cmd: "open", ...opts };
|
|
10059
|
+
const res = await this.sendCommand(cmd);
|
|
10060
|
+
if (!res.ok) {
|
|
10061
|
+
throw new Error(res.error);
|
|
10062
|
+
}
|
|
10063
|
+
return res;
|
|
10064
|
+
}
|
|
10065
|
+
async navigate(key, navPath) {
|
|
10066
|
+
const res = await this.sendCommand({ cmd: "navigate", key, path: navPath });
|
|
10067
|
+
if (!res.ok) {
|
|
10068
|
+
throw new Error(res.error);
|
|
10069
|
+
}
|
|
10070
|
+
return res;
|
|
10071
|
+
}
|
|
10072
|
+
async close(key) {
|
|
10073
|
+
const res = await this.sendCommand({ cmd: "close", key });
|
|
10074
|
+
if (!res.ok) {
|
|
10075
|
+
throw new Error(res.error);
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
async detach(key) {
|
|
10079
|
+
const res = await this.sendCommand({ cmd: "detach", key });
|
|
10080
|
+
if (!res.ok) {
|
|
10081
|
+
throw new Error(res.error);
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
10084
|
+
async status() {
|
|
10085
|
+
const res = await this.sendCommand({ cmd: "status" });
|
|
10086
|
+
if (!res.ok) {
|
|
10087
|
+
throw new Error(res.error);
|
|
10088
|
+
}
|
|
10089
|
+
return res;
|
|
10090
|
+
}
|
|
10091
|
+
async ping() {
|
|
10092
|
+
const res = await this.sendCommand({ cmd: "ping" });
|
|
10093
|
+
if (!res.ok) {
|
|
10094
|
+
throw new Error(res.error);
|
|
10095
|
+
}
|
|
10096
|
+
}
|
|
10097
|
+
onEvent(handler) {
|
|
10098
|
+
this.eventHandler = handler;
|
|
10099
|
+
}
|
|
10100
|
+
destroy() {
|
|
10101
|
+
this.eventHandler = null;
|
|
10102
|
+
this.responseQueue = [];
|
|
10103
|
+
if (this.socket) {
|
|
10104
|
+
this.socket.destroy();
|
|
10105
|
+
this.socket = null;
|
|
10106
|
+
}
|
|
10107
|
+
}
|
|
10108
|
+
};
|
|
10109
|
+
|
|
10110
|
+
// ../bitfab-plugin-lib/dist/studioChannel.js
|
|
10111
|
+
var DirectChannel = class {
|
|
10112
|
+
apiKey;
|
|
10113
|
+
serviceUrl;
|
|
10114
|
+
constructor(apiKey, serviceUrl) {
|
|
10115
|
+
this.apiKey = apiKey;
|
|
10116
|
+
this.serviceUrl = serviceUrl;
|
|
10117
|
+
}
|
|
10118
|
+
setApiKey(apiKey) {
|
|
10119
|
+
this.apiKey = apiKey;
|
|
10120
|
+
}
|
|
10121
|
+
async openOrNavigate(path18, opts) {
|
|
10122
|
+
const existing = readActiveStudioSession();
|
|
10123
|
+
if (existing && existing.windowState !== "closed") {
|
|
10124
|
+
const ack = await navigateStudioAndAwaitAck({
|
|
10125
|
+
serviceUrl: existing.serviceUrl,
|
|
10126
|
+
apiKey: this.apiKey,
|
|
10127
|
+
sessionId: existing.sessionId
|
|
10128
|
+
}, path18);
|
|
10129
|
+
if (!ack.acked) {
|
|
10130
|
+
throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
|
|
10131
|
+
}
|
|
10132
|
+
return { sessionId: existing.sessionId, opened: false };
|
|
10133
|
+
}
|
|
10134
|
+
const sessionId = opts?.sessionId ?? crypto6.randomUUID();
|
|
10135
|
+
const session = await createStudioSession({
|
|
10136
|
+
serviceUrl: this.serviceUrl,
|
|
10137
|
+
apiKey: this.apiKey,
|
|
10138
|
+
sessionId,
|
|
10139
|
+
initialPath: path18,
|
|
10140
|
+
clientHeaders: opts?.clientHeaders
|
|
10141
|
+
});
|
|
10142
|
+
return { sessionId: session.sessionId, opened: true };
|
|
10143
|
+
}
|
|
10144
|
+
async close(sessionId, message) {
|
|
10145
|
+
await closeStudio({ serviceUrl: this.serviceUrl, apiKey: this.apiKey, sessionId }, message);
|
|
10146
|
+
}
|
|
10147
|
+
subscribe(sessionId, onEvent, onError) {
|
|
10148
|
+
const abortController = new AbortController();
|
|
10149
|
+
const done = (async () => {
|
|
10150
|
+
const startCursor = await fetchStreamTip({
|
|
10151
|
+
serviceUrl: this.serviceUrl,
|
|
10152
|
+
apiKey: this.apiKey,
|
|
10153
|
+
sessionId
|
|
10154
|
+
});
|
|
10155
|
+
if (abortController.signal.aborted) {
|
|
10156
|
+
return;
|
|
10157
|
+
}
|
|
10158
|
+
await pollAgentSessionEvents({
|
|
10159
|
+
serviceUrl: this.serviceUrl,
|
|
10160
|
+
apiKey: this.apiKey,
|
|
10161
|
+
sessionId,
|
|
10162
|
+
startCursor,
|
|
10163
|
+
abortSignal: abortController.signal,
|
|
10164
|
+
onEvent,
|
|
10165
|
+
onError
|
|
10166
|
+
});
|
|
10167
|
+
})().catch((err) => {
|
|
10168
|
+
if (!abortController.signal.aborted) {
|
|
10169
|
+
onError(err instanceof Error ? err : new Error(String(err)));
|
|
10170
|
+
}
|
|
10171
|
+
});
|
|
10172
|
+
return { abort: () => abortController.abort(), done };
|
|
10173
|
+
}
|
|
10174
|
+
destroy() {
|
|
10175
|
+
}
|
|
10176
|
+
};
|
|
10177
|
+
var DaemonChannel = class _DaemonChannel {
|
|
10178
|
+
client;
|
|
10179
|
+
key;
|
|
10180
|
+
apiKey;
|
|
10181
|
+
serviceUrl;
|
|
10182
|
+
doneResolve = null;
|
|
10183
|
+
constructor(client, key, apiKey, serviceUrl) {
|
|
10184
|
+
this.client = client;
|
|
10185
|
+
this.key = key;
|
|
10186
|
+
this.apiKey = apiKey;
|
|
10187
|
+
this.serviceUrl = serviceUrl;
|
|
10188
|
+
}
|
|
10189
|
+
static async connect(apiKey, serviceUrl) {
|
|
10190
|
+
const client = new DaemonClient();
|
|
10191
|
+
await client.connect();
|
|
10192
|
+
const key = resolveAgentSessionKey();
|
|
10193
|
+
return new _DaemonChannel(client, key, apiKey, serviceUrl);
|
|
10194
|
+
}
|
|
10195
|
+
setApiKey(apiKey) {
|
|
10196
|
+
this.apiKey = apiKey;
|
|
10197
|
+
}
|
|
10198
|
+
async openOrNavigate(path18, opts) {
|
|
10199
|
+
const openRes = await this.client.open({
|
|
10200
|
+
key: this.key,
|
|
10201
|
+
serviceUrl: this.serviceUrl,
|
|
10202
|
+
apiKey: this.apiKey,
|
|
10203
|
+
sessionId: opts?.sessionId,
|
|
10204
|
+
initialPath: path18,
|
|
10205
|
+
clientHeaders: opts?.clientHeaders
|
|
10206
|
+
});
|
|
10207
|
+
if (openRes.reconnected) {
|
|
10208
|
+
const navRes = await this.client.navigate(this.key, path18);
|
|
10209
|
+
if (!navRes.acked) {
|
|
10210
|
+
this.client.destroy();
|
|
10211
|
+
throw new StudioNavigationError(navRes.reason ?? "unknown", void 0, openRes.sessionId);
|
|
10212
|
+
}
|
|
10213
|
+
writeActiveStudioSession({
|
|
10214
|
+
sessionId: openRes.sessionId,
|
|
10215
|
+
serviceUrl: this.serviceUrl,
|
|
10216
|
+
source: "daemon",
|
|
10217
|
+
currentPath: path18
|
|
10218
|
+
});
|
|
10219
|
+
return { sessionId: openRes.sessionId, opened: false };
|
|
10220
|
+
}
|
|
10221
|
+
writeActiveStudioSession({
|
|
10222
|
+
sessionId: openRes.sessionId,
|
|
10223
|
+
serviceUrl: this.serviceUrl,
|
|
10224
|
+
source: "daemon",
|
|
10225
|
+
currentPath: path18
|
|
10226
|
+
});
|
|
10227
|
+
return { sessionId: openRes.sessionId, opened: true };
|
|
10228
|
+
}
|
|
10229
|
+
async close(sessionId, message) {
|
|
10230
|
+
await Promise.all([
|
|
10231
|
+
closeStudio({ serviceUrl: this.serviceUrl, apiKey: this.apiKey, sessionId }, message),
|
|
10232
|
+
this.client.close(this.key).catch(() => {
|
|
10233
|
+
})
|
|
10234
|
+
]);
|
|
10235
|
+
}
|
|
10236
|
+
subscribe(_sessionId, onEvent, _onError) {
|
|
10237
|
+
const done = new Promise((resolve) => {
|
|
10238
|
+
this.doneResolve = resolve;
|
|
10239
|
+
});
|
|
10240
|
+
this.client.onEvent((push) => {
|
|
10241
|
+
const event = {
|
|
10242
|
+
id: "",
|
|
10243
|
+
type: push.event,
|
|
10244
|
+
data: push.data
|
|
10245
|
+
};
|
|
10246
|
+
onEvent(event);
|
|
10247
|
+
});
|
|
10248
|
+
return {
|
|
10249
|
+
abort: () => this.destroy(),
|
|
10250
|
+
done
|
|
10251
|
+
};
|
|
10252
|
+
}
|
|
10253
|
+
destroy() {
|
|
10254
|
+
this.client.detach(this.key).catch(() => {
|
|
10255
|
+
});
|
|
10256
|
+
this.client.destroy();
|
|
10257
|
+
if (this.doneResolve) {
|
|
10258
|
+
this.doneResolve();
|
|
10259
|
+
this.doneResolve = null;
|
|
10260
|
+
}
|
|
10261
|
+
}
|
|
10262
|
+
};
|
|
10263
|
+
function isDaemonEnabled() {
|
|
10264
|
+
return process.env.BITFAB_STUDIO_DAEMON !== "0";
|
|
10265
|
+
}
|
|
10266
|
+
function withDirectFallback(primary, apiKey, serviceUrl) {
|
|
10267
|
+
let active = primary;
|
|
10268
|
+
return {
|
|
10269
|
+
async openOrNavigate(path18, opts) {
|
|
10270
|
+
try {
|
|
10271
|
+
return await active.openOrNavigate(path18, opts);
|
|
10272
|
+
} catch (err) {
|
|
10273
|
+
if (err instanceof StudioNavigationError || active !== primary) {
|
|
10274
|
+
throw err;
|
|
10275
|
+
}
|
|
10276
|
+
console.error(`daemon failed, falling back to direct: ${err instanceof Error ? err.message : String(err)}`);
|
|
10277
|
+
active.destroy();
|
|
10278
|
+
active = new DirectChannel(apiKey, serviceUrl);
|
|
10279
|
+
return active.openOrNavigate(path18, opts);
|
|
10280
|
+
}
|
|
10281
|
+
},
|
|
10282
|
+
async close(sessionId, message) {
|
|
10283
|
+
await active.close(sessionId, message);
|
|
10284
|
+
},
|
|
10285
|
+
subscribe(sessionId, onEvent, onError) {
|
|
10286
|
+
return active.subscribe(sessionId, onEvent, onError);
|
|
10287
|
+
},
|
|
10288
|
+
setApiKey(key) {
|
|
10289
|
+
active.setApiKey(key);
|
|
10290
|
+
},
|
|
10291
|
+
destroy() {
|
|
10292
|
+
active.destroy();
|
|
10293
|
+
}
|
|
10294
|
+
};
|
|
10295
|
+
}
|
|
10296
|
+
async function resolveChannel(apiKey, serviceUrl) {
|
|
10297
|
+
if (isDaemonEnabled()) {
|
|
10298
|
+
try {
|
|
10299
|
+
const daemon = await DaemonChannel.connect(apiKey, serviceUrl);
|
|
10300
|
+
return withDirectFallback(daemon, apiKey, serviceUrl);
|
|
10301
|
+
} catch (err) {
|
|
10302
|
+
if (err instanceof StudioNavigationError) {
|
|
10303
|
+
throw err;
|
|
10304
|
+
}
|
|
10305
|
+
console.error(`daemon unavailable, falling back to direct: ${err instanceof Error ? err.message : String(err)}`);
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
return new DirectChannel(apiKey, serviceUrl);
|
|
10309
|
+
}
|
|
10310
|
+
|
|
9940
10311
|
// ../bitfab-plugin-lib/dist/replayCapabilities.js
|
|
9941
10312
|
var semver = __toESM(require_semver2(), 1);
|
|
9942
10313
|
|
|
@@ -9945,12 +10316,12 @@ var semver3 = __toESM(require_semver2(), 1);
|
|
|
9945
10316
|
|
|
9946
10317
|
// ../bitfab-plugin-lib/dist/installedSdk.js
|
|
9947
10318
|
var semver2 = __toESM(require_semver2(), 1);
|
|
9948
|
-
import
|
|
9949
|
-
import
|
|
10319
|
+
import fs11 from "fs";
|
|
10320
|
+
import path10 from "path";
|
|
9950
10321
|
|
|
9951
10322
|
// ../bitfab-plugin-lib/dist/workspaces.js
|
|
9952
|
-
import
|
|
9953
|
-
import
|
|
10323
|
+
import fs10 from "fs";
|
|
10324
|
+
import path9 from "path";
|
|
9954
10325
|
|
|
9955
10326
|
// ../bitfab-plugin-lib/dist/analytics.js
|
|
9956
10327
|
async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
@@ -9974,11 +10345,11 @@ async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
|
9974
10345
|
}
|
|
9975
10346
|
|
|
9976
10347
|
// ../bitfab-plugin-lib/dist/commands/openStudioTo.js
|
|
9977
|
-
import
|
|
10348
|
+
import crypto7 from "crypto";
|
|
9978
10349
|
|
|
9979
10350
|
// ../bitfab-plugin-lib/dist/frontmostApp.js
|
|
9980
10351
|
import { execFileSync as execFileSync3, spawn as spawn2 } from "child_process";
|
|
9981
|
-
import
|
|
10352
|
+
import os10 from "os";
|
|
9982
10353
|
function findAncestorApp() {
|
|
9983
10354
|
try {
|
|
9984
10355
|
let pid = process.ppid;
|
|
@@ -10039,7 +10410,7 @@ function focusItermSession(sessionId) {
|
|
|
10039
10410
|
spawnQuiet("osascript", ["-e", script]);
|
|
10040
10411
|
}
|
|
10041
10412
|
function getFrontmostApp() {
|
|
10042
|
-
const platform2 =
|
|
10413
|
+
const platform2 = os10.platform();
|
|
10043
10414
|
try {
|
|
10044
10415
|
if (platform2 === "darwin") {
|
|
10045
10416
|
return execFileSync3("osascript", [
|
|
@@ -10073,7 +10444,7 @@ function focusApp(identifier) {
|
|
|
10073
10444
|
if (!identifier) {
|
|
10074
10445
|
return;
|
|
10075
10446
|
}
|
|
10076
|
-
const platform2 =
|
|
10447
|
+
const platform2 = os10.platform();
|
|
10077
10448
|
if (platform2 === "darwin") {
|
|
10078
10449
|
spawnQuiet("osascript", [
|
|
10079
10450
|
"-e",
|
|
@@ -10102,145 +10473,144 @@ function focusApp(identifier) {
|
|
|
10102
10473
|
spawnQuiet("powershell.exe", ["-NoProfile", "-Command", script]);
|
|
10103
10474
|
}
|
|
10104
10475
|
}
|
|
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 {
|
|
10476
|
+
function recordFocus() {
|
|
10477
|
+
const platform2 = os10.platform();
|
|
10478
|
+
if (platform2 === "linux") {
|
|
10479
|
+
const windowId = process.env.WINDOWID;
|
|
10480
|
+
if (windowId) {
|
|
10481
|
+
return () => spawnQuiet("xdotool", ["windowactivate", windowId]);
|
|
10482
|
+
}
|
|
10483
|
+
}
|
|
10484
|
+
if (platform2 === "darwin") {
|
|
10485
|
+
const app = findAncestorApp();
|
|
10486
|
+
if (app) {
|
|
10487
|
+
const itermSessionId = process.env.ITERM_SESSION_ID ?? null;
|
|
10488
|
+
return () => focusAncestorApp(app, itermSessionId);
|
|
10150
10489
|
}
|
|
10151
|
-
await new Promise((resolve) => setTimeout(resolve, SESSION_READY_POLL_MS));
|
|
10152
10490
|
}
|
|
10153
|
-
|
|
10491
|
+
const frontmost = getFrontmostApp();
|
|
10492
|
+
return () => focusApp(frontmost);
|
|
10154
10493
|
}
|
|
10155
10494
|
|
|
10156
10495
|
// ../bitfab-plugin-lib/dist/commands/openStudioTo.js
|
|
10496
|
+
var WINDOW_CLOSE_GRACE_PERIOD_MS = 1e4;
|
|
10157
10497
|
var LOGIN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
};
|
|
10172
|
-
|
|
10498
|
+
function createEventSubscription(channel, sessionId, onEvent, restoreFocus) {
|
|
10499
|
+
let graceTimer = null;
|
|
10500
|
+
let endedNotified = false;
|
|
10501
|
+
const clearGraceTimer = () => {
|
|
10502
|
+
if (graceTimer) {
|
|
10503
|
+
clearTimeout(graceTimer);
|
|
10504
|
+
graceTimer = null;
|
|
10505
|
+
}
|
|
10506
|
+
};
|
|
10507
|
+
const notifyEnded = (baseEvent) => {
|
|
10508
|
+
if (!endedNotified) {
|
|
10509
|
+
endedNotified = true;
|
|
10510
|
+
restoreFocus();
|
|
10511
|
+
onEvent({ ...baseEvent, type: "studio:session-ended" });
|
|
10512
|
+
}
|
|
10513
|
+
clearGraceTimer();
|
|
10514
|
+
sub.abort();
|
|
10515
|
+
};
|
|
10516
|
+
const sub = channel.subscribe(sessionId, (event) => {
|
|
10517
|
+
if (event.type === "studio:window-closed") {
|
|
10518
|
+
clearGraceTimer();
|
|
10519
|
+
graceTimer = setTimeout(() => {
|
|
10520
|
+
graceTimer = null;
|
|
10521
|
+
updateActiveStudioSession({ windowState: "closed" });
|
|
10522
|
+
notifyEnded(event);
|
|
10523
|
+
}, WINDOW_CLOSE_GRACE_PERIOD_MS);
|
|
10524
|
+
return;
|
|
10525
|
+
}
|
|
10526
|
+
if (event.type === "studio:session-started") {
|
|
10527
|
+
if (graceTimer) {
|
|
10528
|
+
clearGraceTimer();
|
|
10529
|
+
console.error("Studio reconnected after page refresh");
|
|
10530
|
+
}
|
|
10531
|
+
return;
|
|
10532
|
+
}
|
|
10533
|
+
if (event.type === "studio:session-ended") {
|
|
10534
|
+
clearGraceTimer();
|
|
10535
|
+
notifyEnded(event);
|
|
10536
|
+
return;
|
|
10537
|
+
}
|
|
10538
|
+
if (event.type === "studio:session-closed") {
|
|
10539
|
+
return;
|
|
10540
|
+
}
|
|
10541
|
+
if (event.type === "studio:not-member") {
|
|
10542
|
+
restoreFocus();
|
|
10543
|
+
onEvent(event);
|
|
10544
|
+
sub.abort();
|
|
10545
|
+
return;
|
|
10546
|
+
}
|
|
10547
|
+
if (event.type === "studio:return-to-agent" || event.type === "studio:edit-with-agent") {
|
|
10548
|
+
restoreFocus();
|
|
10549
|
+
}
|
|
10550
|
+
onEvent(event);
|
|
10551
|
+
}, (err) => {
|
|
10552
|
+
console.error(`studio event stream: ${err.message}`);
|
|
10553
|
+
});
|
|
10554
|
+
return {
|
|
10555
|
+
abort: () => {
|
|
10556
|
+
clearGraceTimer();
|
|
10557
|
+
sub.abort();
|
|
10558
|
+
},
|
|
10559
|
+
done: sub.done
|
|
10560
|
+
};
|
|
10561
|
+
}
|
|
10562
|
+
async function openStudioTo(path18, opts = {}) {
|
|
10173
10563
|
const config2 = getConfig();
|
|
10174
10564
|
const serviceUrl = config2.serviceUrl;
|
|
10175
10565
|
const apiKey = !opts.forceLogin && hasCredentials() ? config2.apiKey : null;
|
|
10176
10566
|
const noop = () => {
|
|
10177
10567
|
};
|
|
10178
10568
|
const restoreFocus = recordFocus();
|
|
10569
|
+
const channel = await resolveChannel(apiKey ?? "", serviceUrl);
|
|
10179
10570
|
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
|
|
10571
|
+
const sessionId2 = crypto7.randomUUID();
|
|
10572
|
+
const destination = opts.freshWindowPath ?? path18;
|
|
10573
|
+
const destSeparator = destination.includes("?") ? "&" : "?";
|
|
10574
|
+
const redirectPath = `${destination}${destSeparator}session=${encodeURIComponent(sessionId2)}&pluginLogin=true`;
|
|
10575
|
+
const signInInitialPath = `/studio/sign-in?redirect_url=${encodeURIComponent(redirectPath)}&session=${encodeURIComponent(sessionId2)}`;
|
|
10576
|
+
await channel.openOrNavigate(signInInitialPath, {
|
|
10577
|
+
clientHeaders: opts.clientHeaders,
|
|
10578
|
+
sessionId: sessionId2
|
|
10210
10579
|
});
|
|
10580
|
+
const signInUrl = `${serviceUrl}${signInInitialPath}`;
|
|
10211
10581
|
opts.onLoginRequired?.(sessionId2, signInUrl);
|
|
10212
|
-
const
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10582
|
+
const loginApiKey = await new Promise((resolve, reject) => {
|
|
10583
|
+
const timer = setTimeout(() => {
|
|
10584
|
+
sub.abort();
|
|
10585
|
+
reject(new Error("Login polling aborted"));
|
|
10586
|
+
}, LOGIN_TIMEOUT_MS);
|
|
10587
|
+
const keepalive = setInterval(() => process.stderr.write(""), 3e4);
|
|
10588
|
+
const cleanup = () => {
|
|
10589
|
+
clearTimeout(timer);
|
|
10590
|
+
clearInterval(keepalive);
|
|
10591
|
+
};
|
|
10592
|
+
const sub = channel.subscribe(sessionId2, (event) => {
|
|
10593
|
+
if (event.type === "studio:authenticated" && typeof event.data?.token === "string") {
|
|
10594
|
+
cleanup();
|
|
10595
|
+
sub.abort();
|
|
10596
|
+
resolve(event.data.token);
|
|
10597
|
+
}
|
|
10598
|
+
}, (err) => {
|
|
10599
|
+
cleanup();
|
|
10600
|
+
reject(err);
|
|
10221
10601
|
});
|
|
10222
|
-
}
|
|
10223
|
-
clearTimeout(timer);
|
|
10224
|
-
clearInterval(keepalive);
|
|
10225
|
-
throw err;
|
|
10226
|
-
}
|
|
10227
|
-
clearTimeout(timer);
|
|
10228
|
-
clearInterval(keepalive);
|
|
10602
|
+
});
|
|
10229
10603
|
saveCredentials(loginApiKey);
|
|
10230
10604
|
opts.onAuthenticated?.(sessionId2);
|
|
10605
|
+
channel.setApiKey(loginApiKey);
|
|
10231
10606
|
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" });
|
|
10607
|
+
const subscription2 = createEventSubscription(channel, sessionId2, opts.onEvent, restoreFocus);
|
|
10238
10608
|
return {
|
|
10239
10609
|
sessionId: sessionId2,
|
|
10240
10610
|
serviceUrl,
|
|
10241
10611
|
apiKey: loginApiKey,
|
|
10242
10612
|
opened: true,
|
|
10243
|
-
...
|
|
10613
|
+
...subscription2
|
|
10244
10614
|
};
|
|
10245
10615
|
}
|
|
10246
10616
|
restoreFocus();
|
|
@@ -10253,299 +10623,20 @@ async function openStudioTo(path15, opts = {}) {
|
|
|
10253
10623
|
done: Promise.resolve()
|
|
10254
10624
|
};
|
|
10255
10625
|
}
|
|
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,
|
|
10626
|
+
const { sessionId, opened } = await channel.openOrNavigate(path18, {
|
|
10303
10627
|
clientHeaders: opts.clientHeaders
|
|
10304
10628
|
});
|
|
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();
|
|
10629
|
+
const subscription = opts.onEvent ? createEventSubscription(channel, sessionId, opts.onEvent, restoreFocus) : { abort: () => channel.destroy(), done: Promise.resolve() };
|
|
10351
10630
|
return {
|
|
10352
10631
|
sessionId,
|
|
10353
10632
|
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
10633
|
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();
|
|
10634
|
+
opened,
|
|
10635
|
+
...subscription
|
|
10636
|
+
};
|
|
10534
10637
|
}
|
|
10535
10638
|
|
|
10536
10639
|
// ../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
10640
|
async function verifyToken(serviceUrl, token) {
|
|
10550
10641
|
try {
|
|
10551
10642
|
const res = await fetch(`${serviceUrl}/api/plugin/whoami`, {
|
|
@@ -10561,23 +10652,6 @@ async function verifyToken(serviceUrl, token) {
|
|
|
10561
10652
|
}
|
|
10562
10653
|
async function runLogin(platform2, pluginVersion, options) {
|
|
10563
10654
|
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
10655
|
const force = options?.force ?? process.argv.includes("--force");
|
|
10582
10656
|
const config2 = getConfig();
|
|
10583
10657
|
if (!force && hasCredentials() && config2.apiKey) {
|
|
@@ -10617,9 +10691,7 @@ ${signInUrl}`);
|
|
|
10617
10691
|
${greeting}`);
|
|
10618
10692
|
console.log("Bitfab MCP tools are now active. (via studio)");
|
|
10619
10693
|
}
|
|
10620
|
-
|
|
10621
|
-
await runLoginWindowMonitor(result.sessionId);
|
|
10622
|
-
}
|
|
10694
|
+
result.abort();
|
|
10623
10695
|
if (exitOnComplete) {
|
|
10624
10696
|
process.exit(0);
|
|
10625
10697
|
}
|
|
@@ -11424,10 +11496,10 @@ function mergeDefs(...defs) {
|
|
|
11424
11496
|
function cloneDef(schema) {
|
|
11425
11497
|
return mergeDefs(schema._zod.def);
|
|
11426
11498
|
}
|
|
11427
|
-
function getElementAtPath(obj,
|
|
11428
|
-
if (!
|
|
11499
|
+
function getElementAtPath(obj, path18) {
|
|
11500
|
+
if (!path18)
|
|
11429
11501
|
return obj;
|
|
11430
|
-
return
|
|
11502
|
+
return path18.reduce((acc, key) => acc?.[key], obj);
|
|
11431
11503
|
}
|
|
11432
11504
|
function promiseAllObject(promisesObj) {
|
|
11433
11505
|
const keys = Object.keys(promisesObj);
|
|
@@ -11810,11 +11882,11 @@ function aborted(x, startIndex = 0) {
|
|
|
11810
11882
|
}
|
|
11811
11883
|
return false;
|
|
11812
11884
|
}
|
|
11813
|
-
function prefixIssues(
|
|
11885
|
+
function prefixIssues(path18, issues) {
|
|
11814
11886
|
return issues.map((iss) => {
|
|
11815
11887
|
var _a2;
|
|
11816
11888
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
11817
|
-
iss.path.unshift(
|
|
11889
|
+
iss.path.unshift(path18);
|
|
11818
11890
|
return iss;
|
|
11819
11891
|
});
|
|
11820
11892
|
}
|
|
@@ -11997,7 +12069,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
|
|
|
11997
12069
|
}
|
|
11998
12070
|
function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
11999
12071
|
const result = { errors: [] };
|
|
12000
|
-
const processError = (error49,
|
|
12072
|
+
const processError = (error49, path18 = []) => {
|
|
12001
12073
|
var _a2, _b;
|
|
12002
12074
|
for (const issue2 of error49.issues) {
|
|
12003
12075
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -12007,7 +12079,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12007
12079
|
} else if (issue2.code === "invalid_element") {
|
|
12008
12080
|
processError({ issues: issue2.issues }, issue2.path);
|
|
12009
12081
|
} else {
|
|
12010
|
-
const fullpath = [...
|
|
12082
|
+
const fullpath = [...path18, ...issue2.path];
|
|
12011
12083
|
if (fullpath.length === 0) {
|
|
12012
12084
|
result.errors.push(mapper(issue2));
|
|
12013
12085
|
continue;
|
|
@@ -12039,8 +12111,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12039
12111
|
}
|
|
12040
12112
|
function toDotPath(_path) {
|
|
12041
12113
|
const segs = [];
|
|
12042
|
-
const
|
|
12043
|
-
for (const seg of
|
|
12114
|
+
const path18 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
12115
|
+
for (const seg of path18) {
|
|
12044
12116
|
if (typeof seg === "number")
|
|
12045
12117
|
segs.push(`[${seg}]`);
|
|
12046
12118
|
else if (typeof seg === "symbol")
|
|
@@ -24017,13 +24089,13 @@ function resolveRef(ref, ctx) {
|
|
|
24017
24089
|
if (!ref.startsWith("#")) {
|
|
24018
24090
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
24019
24091
|
}
|
|
24020
|
-
const
|
|
24021
|
-
if (
|
|
24092
|
+
const path18 = ref.slice(1).split("/").filter(Boolean);
|
|
24093
|
+
if (path18.length === 0) {
|
|
24022
24094
|
return ctx.rootSchema;
|
|
24023
24095
|
}
|
|
24024
24096
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
24025
|
-
if (
|
|
24026
|
-
const key =
|
|
24097
|
+
if (path18[0] === defsKey) {
|
|
24098
|
+
const key = path18[1];
|
|
24027
24099
|
if (!key || !ctx.defs[key]) {
|
|
24028
24100
|
throw new Error(`Reference not found: ${ref}`);
|
|
24029
24101
|
}
|
|
@@ -24455,18 +24527,18 @@ var inputFileSchema = external_exports.object({
|
|
|
24455
24527
|
import { spawn as spawn3 } from "child_process";
|
|
24456
24528
|
|
|
24457
24529
|
// ../bitfab-plugin-lib/dist/activePreviewSession.js
|
|
24458
|
-
import
|
|
24459
|
-
import
|
|
24460
|
-
import
|
|
24461
|
-
var FILE_PATH =
|
|
24530
|
+
import fs12 from "fs";
|
|
24531
|
+
import os11 from "os";
|
|
24532
|
+
import path11 from "path";
|
|
24533
|
+
var FILE_PATH = path11.join(os11.homedir(), ".config", "bitfab", "active-preview-session.json");
|
|
24462
24534
|
|
|
24463
24535
|
// ../bitfab-plugin-lib/dist/commands/status.js
|
|
24464
|
-
import
|
|
24536
|
+
import path13 from "path";
|
|
24465
24537
|
|
|
24466
24538
|
// ../bitfab-plugin-lib/dist/updates.js
|
|
24467
|
-
import
|
|
24468
|
-
import
|
|
24469
|
-
import
|
|
24539
|
+
import fs13 from "fs";
|
|
24540
|
+
import os12 from "os";
|
|
24541
|
+
import path12 from "path";
|
|
24470
24542
|
|
|
24471
24543
|
// ../bitfab-plugin-lib/dist/commands/update.js
|
|
24472
24544
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -24475,15 +24547,15 @@ import { execSync as execSync2 } from "child_process";
|
|
|
24475
24547
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
24476
24548
|
|
|
24477
24549
|
// ../bitfab-plugin-lib/dist/hooks/captureHook.js
|
|
24478
|
-
import
|
|
24550
|
+
import fs14 from "fs";
|
|
24479
24551
|
|
|
24480
24552
|
// ../bitfab-plugin-lib/dist/hooks/userPromptSubmit.js
|
|
24481
|
-
import
|
|
24553
|
+
import fs15 from "fs";
|
|
24482
24554
|
|
|
24483
24555
|
// ../bitfab-plugin-lib/dist/installScope.js
|
|
24484
|
-
import
|
|
24485
|
-
import
|
|
24486
|
-
import
|
|
24556
|
+
import fs16 from "fs";
|
|
24557
|
+
import os13 from "os";
|
|
24558
|
+
import path14 from "path";
|
|
24487
24559
|
|
|
24488
24560
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/helpers/util.js
|
|
24489
24561
|
var util;
|
|
@@ -24844,8 +24916,8 @@ function getErrorMap2() {
|
|
|
24844
24916
|
|
|
24845
24917
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/helpers/parseUtil.js
|
|
24846
24918
|
var makeIssue = (params) => {
|
|
24847
|
-
const { data, path:
|
|
24848
|
-
const fullPath = [...
|
|
24919
|
+
const { data, path: path18, errorMaps, issueData } = params;
|
|
24920
|
+
const fullPath = [...path18, ...issueData.path || []];
|
|
24849
24921
|
const fullIssue = {
|
|
24850
24922
|
...issueData,
|
|
24851
24923
|
path: fullPath
|
|
@@ -24960,11 +25032,11 @@ var errorUtil;
|
|
|
24960
25032
|
|
|
24961
25033
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/types.js
|
|
24962
25034
|
var ParseInputLazyPath = class {
|
|
24963
|
-
constructor(parent, value,
|
|
25035
|
+
constructor(parent, value, path18, key) {
|
|
24964
25036
|
this._cachedPath = [];
|
|
24965
25037
|
this.parent = parent;
|
|
24966
25038
|
this.data = value;
|
|
24967
|
-
this._path =
|
|
25039
|
+
this._path = path18;
|
|
24968
25040
|
this._key = key;
|
|
24969
25041
|
}
|
|
24970
25042
|
get path() {
|
|
@@ -29858,15 +29930,11 @@ var McpZodTypeKind;
|
|
|
29858
29930
|
// ../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
29931
|
import process3 from "process";
|
|
29860
29932
|
|
|
29861
|
-
// src/init.ts
|
|
29862
|
-
import { execSync as execSync3 } from "child_process";
|
|
29863
|
-
import * as p4 from "@clack/prompts";
|
|
29864
|
-
|
|
29865
29933
|
// src/claude.ts
|
|
29866
29934
|
import { spawnSync } from "child_process";
|
|
29867
|
-
import
|
|
29868
|
-
import
|
|
29869
|
-
import
|
|
29935
|
+
import fs17 from "fs";
|
|
29936
|
+
import os14 from "os";
|
|
29937
|
+
import path15 from "path";
|
|
29870
29938
|
import * as p from "@clack/prompts";
|
|
29871
29939
|
var REPO = "Project-White-Rabbit/bitfab-claude-plugin";
|
|
29872
29940
|
var MARKETPLACE = "bitfab";
|
|
@@ -29886,7 +29954,7 @@ function runClaudeInstall() {
|
|
|
29886
29954
|
}
|
|
29887
29955
|
s.start("Enabling auto-updates");
|
|
29888
29956
|
const changed = enableAutoUpdate(
|
|
29889
|
-
|
|
29957
|
+
path15.join(os14.homedir(), ".claude", "settings.json")
|
|
29890
29958
|
);
|
|
29891
29959
|
if (changed) {
|
|
29892
29960
|
s.stop("Auto-updates enabled");
|
|
@@ -29977,8 +30045,8 @@ function runClaude(args) {
|
|
|
29977
30045
|
}
|
|
29978
30046
|
function enableAutoUpdate(settingsPath) {
|
|
29979
30047
|
let settings = {};
|
|
29980
|
-
if (
|
|
29981
|
-
const raw =
|
|
30048
|
+
if (fs17.existsSync(settingsPath)) {
|
|
30049
|
+
const raw = fs17.readFileSync(settingsPath, "utf-8");
|
|
29982
30050
|
settings = raw.trim() === "" ? {} : JSON.parse(raw);
|
|
29983
30051
|
}
|
|
29984
30052
|
const marketplaces = settings.extraKnownMarketplaces ?? {};
|
|
@@ -29992,17 +30060,17 @@ function enableAutoUpdate(settingsPath) {
|
|
|
29992
30060
|
autoUpdate: true
|
|
29993
30061
|
};
|
|
29994
30062
|
settings.extraKnownMarketplaces = marketplaces;
|
|
29995
|
-
|
|
29996
|
-
|
|
30063
|
+
fs17.mkdirSync(path15.dirname(settingsPath), { recursive: true });
|
|
30064
|
+
fs17.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
29997
30065
|
`);
|
|
29998
30066
|
return true;
|
|
29999
30067
|
}
|
|
30000
30068
|
|
|
30001
30069
|
// src/codex.ts
|
|
30002
30070
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
30003
|
-
import
|
|
30004
|
-
import
|
|
30005
|
-
import
|
|
30071
|
+
import fs18 from "fs";
|
|
30072
|
+
import os15 from "os";
|
|
30073
|
+
import path16 from "path";
|
|
30006
30074
|
import * as p2 from "@clack/prompts";
|
|
30007
30075
|
var REPO2 = "Project-White-Rabbit/bitfab-codex-plugin";
|
|
30008
30076
|
var PLUGIN_KEY2 = "bitfab@bitfab";
|
|
@@ -30030,7 +30098,7 @@ function runCodexInstall() {
|
|
|
30030
30098
|
}
|
|
30031
30099
|
s.start("Enabling bitfab plugin in config");
|
|
30032
30100
|
const changed = enableCodexPlugin(
|
|
30033
|
-
|
|
30101
|
+
path16.join(os15.homedir(), ".codex", "config.toml")
|
|
30034
30102
|
);
|
|
30035
30103
|
if (changed) {
|
|
30036
30104
|
s.stop("Plugin enabled");
|
|
@@ -30093,8 +30161,8 @@ function runCodexUpdate(args, skipPermissions) {
|
|
|
30093
30161
|
}
|
|
30094
30162
|
function enableCodexPlugin(configPath) {
|
|
30095
30163
|
let content = "";
|
|
30096
|
-
if (
|
|
30097
|
-
content =
|
|
30164
|
+
if (fs18.existsSync(configPath)) {
|
|
30165
|
+
content = fs18.readFileSync(configPath, "utf-8");
|
|
30098
30166
|
}
|
|
30099
30167
|
const header = `[plugins."${PLUGIN_KEY2}"]`;
|
|
30100
30168
|
const desiredSection = `${header}
|
|
@@ -30117,8 +30185,8 @@ enabled = true
|
|
|
30117
30185
|
|
|
30118
30186
|
${desiredSection}`;
|
|
30119
30187
|
}
|
|
30120
|
-
|
|
30121
|
-
|
|
30188
|
+
fs18.mkdirSync(path16.dirname(configPath), { recursive: true });
|
|
30189
|
+
fs18.writeFileSync(configPath, next);
|
|
30122
30190
|
return true;
|
|
30123
30191
|
}
|
|
30124
30192
|
function escapeRegex2(s) {
|
|
@@ -30127,7 +30195,7 @@ function escapeRegex2(s) {
|
|
|
30127
30195
|
|
|
30128
30196
|
// src/cursor.ts
|
|
30129
30197
|
import { spawn as spawn4, spawnSync as spawnSync3 } from "child_process";
|
|
30130
|
-
import
|
|
30198
|
+
import os16 from "os";
|
|
30131
30199
|
import * as p3 from "@clack/prompts";
|
|
30132
30200
|
var REPO3 = "Project-White-Rabbit/bitfab-cursor-plugin";
|
|
30133
30201
|
var ADD_PLUGIN_CMD = `/add-plugin ${REPO3}`;
|
|
@@ -30199,13 +30267,13 @@ function copyToClipboard(text) {
|
|
|
30199
30267
|
return result.status === 0;
|
|
30200
30268
|
}
|
|
30201
30269
|
function clipboardCommand() {
|
|
30202
|
-
if (
|
|
30270
|
+
if (os16.platform() === "darwin") {
|
|
30203
30271
|
return { bin: "pbcopy", args: [] };
|
|
30204
30272
|
}
|
|
30205
|
-
if (
|
|
30273
|
+
if (os16.platform() === "linux") {
|
|
30206
30274
|
return { bin: "xclip", args: ["-selection", "clipboard"] };
|
|
30207
30275
|
}
|
|
30208
|
-
if (
|
|
30276
|
+
if (os16.platform() === "win32") {
|
|
30209
30277
|
return { bin: "clip", args: [] };
|
|
30210
30278
|
}
|
|
30211
30279
|
return null;
|
|
@@ -30229,12 +30297,12 @@ var platform = {
|
|
|
30229
30297
|
};
|
|
30230
30298
|
|
|
30231
30299
|
// src/version.ts
|
|
30232
|
-
import
|
|
30233
|
-
import
|
|
30234
|
-
import { fileURLToPath as
|
|
30300
|
+
import fs19 from "fs";
|
|
30301
|
+
import path17 from "path";
|
|
30302
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
30235
30303
|
var CLI_VERSION = JSON.parse(
|
|
30236
|
-
|
|
30237
|
-
|
|
30304
|
+
fs19.readFileSync(
|
|
30305
|
+
path17.join(path17.dirname(fileURLToPath3(import.meta.url)), "../package.json"),
|
|
30238
30306
|
"utf-8"
|
|
30239
30307
|
)
|
|
30240
30308
|
).version;
|
|
@@ -30509,22 +30577,7 @@ function parseArgs2(argv) {
|
|
|
30509
30577
|
}
|
|
30510
30578
|
return { command, editor, skipPermissions, rest };
|
|
30511
30579
|
}
|
|
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
30580
|
async function main() {
|
|
30525
|
-
if (await handleDrainArg(process.argv)) {
|
|
30526
|
-
return;
|
|
30527
|
-
}
|
|
30528
30581
|
const { command, editor, skipPermissions, rest } = parseArgs2(
|
|
30529
30582
|
process.argv.slice(2)
|
|
30530
30583
|
);
|
|
@@ -30581,7 +30634,6 @@ main().catch((err) => {
|
|
|
30581
30634
|
process.exit(1);
|
|
30582
30635
|
});
|
|
30583
30636
|
export {
|
|
30584
|
-
handleDrainArg,
|
|
30585
30637
|
parseArgs2 as parseArgs,
|
|
30586
30638
|
wantsHelp
|
|
30587
30639
|
};
|