bitfab-cli 0.2.9 → 0.2.11
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 +670 -93
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6805,6 +6805,307 @@ import { cancel as cancel2 } from "@clack/prompts";
|
|
|
6805
6805
|
import { execSync as execSync3 } from "child_process";
|
|
6806
6806
|
import * as p4 from "@clack/prompts";
|
|
6807
6807
|
|
|
6808
|
+
// ../bitfab-plugin-lib/dist/errors.js
|
|
6809
|
+
var NotAuthenticatedError = class extends Error {
|
|
6810
|
+
constructor(message) {
|
|
6811
|
+
super(message ?? "Not authenticated. Run the login command to connect your Bitfab account.");
|
|
6812
|
+
this.name = "NotAuthenticatedError";
|
|
6813
|
+
}
|
|
6814
|
+
};
|
|
6815
|
+
|
|
6816
|
+
// ../bitfab-plugin-lib/dist/studioRoutes.js
|
|
6817
|
+
var STUDIO_ROUTE_PATTERNS = [
|
|
6818
|
+
/^\/studio$/,
|
|
6819
|
+
/^\/studio\/experiments$/,
|
|
6820
|
+
/^\/studio\/close$/,
|
|
6821
|
+
/^\/studio\/sign-in$/,
|
|
6822
|
+
/^\/studio\/trace-plan\/[^/]+$/,
|
|
6823
|
+
/^\/studio\/trace-functions\/[^/]+\/datasets\/labeled$/,
|
|
6824
|
+
/^\/studio\/trace-functions\/[^/]+\/datasets\/[^/]+$/,
|
|
6825
|
+
/^\/studio\/trace-functions\/[^/]+\/template-preview$/,
|
|
6826
|
+
/^\/studio\/trace-functions\/[^/]+\/template-preview\/[^/]+$/,
|
|
6827
|
+
/^\/studio\/auth\/[^/]+$/
|
|
6828
|
+
];
|
|
6829
|
+
function isValidStudioRoute(input) {
|
|
6830
|
+
const pathname = input.split("?")[0];
|
|
6831
|
+
return STUDIO_ROUTE_PATTERNS.some((pattern) => pattern.test(pathname));
|
|
6832
|
+
}
|
|
6833
|
+
|
|
6834
|
+
// ../bitfab-plugin-lib/dist/agentSessionChannel.js
|
|
6835
|
+
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
6836
|
+
async function createAgentSession(opts) {
|
|
6837
|
+
if (!opts.apiKey) {
|
|
6838
|
+
throw new NotAuthenticatedError();
|
|
6839
|
+
}
|
|
6840
|
+
const url2 = `${opts.serviceUrl}/api/studio/sessions`;
|
|
6841
|
+
const res = await fetch(url2, {
|
|
6842
|
+
method: "POST",
|
|
6843
|
+
headers: {
|
|
6844
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
6845
|
+
"Content-Type": "application/json",
|
|
6846
|
+
...opts.extraHeaders
|
|
6847
|
+
},
|
|
6848
|
+
body: JSON.stringify({ sessionId: opts.sessionId })
|
|
6849
|
+
});
|
|
6850
|
+
if (!res.ok) {
|
|
6851
|
+
if (res.status === 401) {
|
|
6852
|
+
throw new NotAuthenticatedError("Session expired or invalid. Run the login command to re-authenticate.");
|
|
6853
|
+
}
|
|
6854
|
+
throw new Error(`createAgentSession failed (${res.status}): ${await res.text()}`);
|
|
6855
|
+
}
|
|
6856
|
+
return await res.json();
|
|
6857
|
+
}
|
|
6858
|
+
async function pushAgentSessionEvent(client, event) {
|
|
6859
|
+
const url2 = `${client.serviceUrl}/api/studio/events`;
|
|
6860
|
+
const res = await fetch(url2, {
|
|
6861
|
+
method: "POST",
|
|
6862
|
+
headers: {
|
|
6863
|
+
Authorization: `Bearer ${client.apiKey}`,
|
|
6864
|
+
"Content-Type": "application/json"
|
|
6865
|
+
},
|
|
6866
|
+
body: JSON.stringify({
|
|
6867
|
+
session: client.sessionId,
|
|
6868
|
+
type: event.type,
|
|
6869
|
+
data: event.data
|
|
6870
|
+
})
|
|
6871
|
+
});
|
|
6872
|
+
if (!res.ok) {
|
|
6873
|
+
throw new Error(`pushAgentSessionEvent failed (${res.status}): ${await res.text()}`);
|
|
6874
|
+
}
|
|
6875
|
+
return await res.json();
|
|
6876
|
+
}
|
|
6877
|
+
async function pollAgentSessionEvents(opts) {
|
|
6878
|
+
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
6879
|
+
const client = {
|
|
6880
|
+
serviceUrl: opts.serviceUrl,
|
|
6881
|
+
apiKey: opts.apiKey,
|
|
6882
|
+
sessionId: opts.sessionId
|
|
6883
|
+
};
|
|
6884
|
+
let since = opts.startCursor ?? null;
|
|
6885
|
+
while (!opts.abortSignal.aborted) {
|
|
6886
|
+
try {
|
|
6887
|
+
const url2 = new URL(`${opts.serviceUrl}/api/studio/events`);
|
|
6888
|
+
url2.searchParams.set("session", opts.sessionId);
|
|
6889
|
+
if (since) {
|
|
6890
|
+
url2.searchParams.set("since", since);
|
|
6891
|
+
}
|
|
6892
|
+
const res = await fetch(url2.toString(), {
|
|
6893
|
+
headers: { Authorization: `Bearer ${opts.apiKey}` },
|
|
6894
|
+
signal: opts.abortSignal
|
|
6895
|
+
});
|
|
6896
|
+
if (res.ok) {
|
|
6897
|
+
const body = await res.json();
|
|
6898
|
+
for (const event of body.events) {
|
|
6899
|
+
if (event.type === "studio:ping") {
|
|
6900
|
+
sendPong(client, event.data.ts).catch(() => {
|
|
6901
|
+
});
|
|
6902
|
+
}
|
|
6903
|
+
opts.onEvent(event);
|
|
6904
|
+
since = event.id;
|
|
6905
|
+
}
|
|
6906
|
+
if (opts.onBrowserStatus && typeof body.browserConnected === "boolean") {
|
|
6907
|
+
opts.onBrowserStatus(body.browserConnected);
|
|
6908
|
+
}
|
|
6909
|
+
} else if (opts.onError) {
|
|
6910
|
+
opts.onError(new Error(`agent-session poll failed (${res.status})`));
|
|
6911
|
+
}
|
|
6912
|
+
} catch (err) {
|
|
6913
|
+
if (opts.abortSignal.aborted) {
|
|
6914
|
+
return;
|
|
6915
|
+
}
|
|
6916
|
+
if (opts.onError) {
|
|
6917
|
+
opts.onError(err instanceof Error ? err : new Error(String(err)));
|
|
6918
|
+
}
|
|
6919
|
+
}
|
|
6920
|
+
await new Promise((resolve) => {
|
|
6921
|
+
const onAbort = () => {
|
|
6922
|
+
clearTimeout(timer);
|
|
6923
|
+
resolve();
|
|
6924
|
+
};
|
|
6925
|
+
const timer = setTimeout(() => {
|
|
6926
|
+
opts.abortSignal.removeEventListener("abort", onAbort);
|
|
6927
|
+
resolve();
|
|
6928
|
+
}, interval);
|
|
6929
|
+
opts.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
6930
|
+
});
|
|
6931
|
+
}
|
|
6932
|
+
}
|
|
6933
|
+
async function pollLoginEvents(opts) {
|
|
6934
|
+
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
6935
|
+
let since = null;
|
|
6936
|
+
while (!opts.abortSignal.aborted) {
|
|
6937
|
+
try {
|
|
6938
|
+
const url2 = new URL(`${opts.serviceUrl}/api/studio/sessions/poll`);
|
|
6939
|
+
url2.searchParams.set("session", opts.sessionId);
|
|
6940
|
+
if (since) {
|
|
6941
|
+
url2.searchParams.set("since", since);
|
|
6942
|
+
}
|
|
6943
|
+
const res = await fetch(url2.toString(), {
|
|
6944
|
+
signal: opts.abortSignal
|
|
6945
|
+
});
|
|
6946
|
+
if (res.ok) {
|
|
6947
|
+
const body = await res.json();
|
|
6948
|
+
for (const event of body.events) {
|
|
6949
|
+
since = event.id;
|
|
6950
|
+
if (event.type === "studio:authenticated" && typeof event.data.token === "string") {
|
|
6951
|
+
return event.data.token;
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
} catch {
|
|
6956
|
+
if (opts.abortSignal.aborted) {
|
|
6957
|
+
break;
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
await new Promise((resolve) => {
|
|
6961
|
+
const onAbort = () => {
|
|
6962
|
+
clearTimeout(timer);
|
|
6963
|
+
resolve();
|
|
6964
|
+
};
|
|
6965
|
+
const timer = setTimeout(() => {
|
|
6966
|
+
opts.abortSignal.removeEventListener("abort", onAbort);
|
|
6967
|
+
resolve();
|
|
6968
|
+
}, interval);
|
|
6969
|
+
opts.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
6970
|
+
});
|
|
6971
|
+
}
|
|
6972
|
+
throw new Error("Login polling aborted");
|
|
6973
|
+
}
|
|
6974
|
+
async function navigateStudio(client, path14) {
|
|
6975
|
+
if (!isValidStudioRoute(path14)) {
|
|
6976
|
+
throw new Error(`Studio route not allowed: ${path14}`);
|
|
6977
|
+
}
|
|
6978
|
+
return pushAgentSessionEvent(client, {
|
|
6979
|
+
type: "agent:navigate",
|
|
6980
|
+
data: { path: path14 }
|
|
6981
|
+
});
|
|
6982
|
+
}
|
|
6983
|
+
var STUDIO_NAVIGATE_ACK_TIMEOUT_MS = 12e3;
|
|
6984
|
+
var PING_PONG_TIMEOUT_MS = 5e3;
|
|
6985
|
+
function awaitNavigateAck(client, path14, ackTimeoutMs) {
|
|
6986
|
+
const abortController = new AbortController();
|
|
6987
|
+
let timer = null;
|
|
6988
|
+
const result = new Promise((resolve) => {
|
|
6989
|
+
timer = setTimeout(() => {
|
|
6990
|
+
resolve({ acked: false, reason: "timeout" });
|
|
6991
|
+
abortController.abort();
|
|
6992
|
+
}, ackTimeoutMs);
|
|
6993
|
+
const pathOnly = path14.split("?")[0];
|
|
6994
|
+
pollAgentSessionEvents({
|
|
6995
|
+
serviceUrl: client.serviceUrl,
|
|
6996
|
+
apiKey: client.apiKey,
|
|
6997
|
+
sessionId: client.sessionId,
|
|
6998
|
+
abortSignal: abortController.signal,
|
|
6999
|
+
onEvent: (event) => {
|
|
7000
|
+
if (event.type === "studio:navigated" && event.data.path === pathOnly) {
|
|
7001
|
+
if (timer) {
|
|
7002
|
+
clearTimeout(timer);
|
|
7003
|
+
}
|
|
7004
|
+
resolve({ acked: true });
|
|
7005
|
+
abortController.abort();
|
|
7006
|
+
return;
|
|
7007
|
+
}
|
|
7008
|
+
if (event.type === "studio:navigation-blocked" && event.data.path === pathOnly) {
|
|
7009
|
+
if (timer) {
|
|
7010
|
+
clearTimeout(timer);
|
|
7011
|
+
}
|
|
7012
|
+
resolve({
|
|
7013
|
+
acked: false,
|
|
7014
|
+
reason: "blocked",
|
|
7015
|
+
blockedReason: event.data.reason
|
|
7016
|
+
});
|
|
7017
|
+
abortController.abort();
|
|
7018
|
+
return;
|
|
7019
|
+
}
|
|
7020
|
+
if (event.type === "studio:session-ended" || event.type === "studio:session-closed") {
|
|
7021
|
+
if (timer) {
|
|
7022
|
+
clearTimeout(timer);
|
|
7023
|
+
}
|
|
7024
|
+
resolve({ acked: false, reason: "session-ended" });
|
|
7025
|
+
abortController.abort();
|
|
7026
|
+
}
|
|
7027
|
+
},
|
|
7028
|
+
onError: () => {
|
|
7029
|
+
}
|
|
7030
|
+
}).catch(() => {
|
|
7031
|
+
if (abortController.signal.aborted) {
|
|
7032
|
+
return;
|
|
7033
|
+
}
|
|
7034
|
+
if (timer) {
|
|
7035
|
+
clearTimeout(timer);
|
|
7036
|
+
}
|
|
7037
|
+
resolve({ acked: false, reason: "timeout" });
|
|
7038
|
+
});
|
|
7039
|
+
});
|
|
7040
|
+
return result;
|
|
7041
|
+
}
|
|
7042
|
+
function awaitPong(client, pingTs) {
|
|
7043
|
+
return new Promise((resolve) => {
|
|
7044
|
+
const abortController = new AbortController();
|
|
7045
|
+
let received = false;
|
|
7046
|
+
const timer = setTimeout(() => {
|
|
7047
|
+
abortController.abort();
|
|
7048
|
+
resolve(false);
|
|
7049
|
+
}, PING_PONG_TIMEOUT_MS);
|
|
7050
|
+
pollAgentSessionEvents({
|
|
7051
|
+
serviceUrl: client.serviceUrl,
|
|
7052
|
+
apiKey: client.apiKey,
|
|
7053
|
+
sessionId: client.sessionId,
|
|
7054
|
+
abortSignal: abortController.signal,
|
|
7055
|
+
onEvent: (event) => {
|
|
7056
|
+
if (event.type === "studio:pong" && event.data.replyTo === pingTs) {
|
|
7057
|
+
received = true;
|
|
7058
|
+
clearTimeout(timer);
|
|
7059
|
+
abortController.abort();
|
|
7060
|
+
resolve(true);
|
|
7061
|
+
}
|
|
7062
|
+
},
|
|
7063
|
+
onError: () => {
|
|
7064
|
+
}
|
|
7065
|
+
}).catch(() => {
|
|
7066
|
+
if (!received) {
|
|
7067
|
+
clearTimeout(timer);
|
|
7068
|
+
resolve(false);
|
|
7069
|
+
}
|
|
7070
|
+
});
|
|
7071
|
+
});
|
|
7072
|
+
}
|
|
7073
|
+
async function navigateStudioAndAwaitAck(client, path14, opts = {}) {
|
|
7074
|
+
const ackTimeoutMs = opts.ackTimeoutMs ?? STUDIO_NAVIGATE_ACK_TIMEOUT_MS;
|
|
7075
|
+
if (!isValidStudioRoute(path14)) {
|
|
7076
|
+
return { acked: false, reason: "invalid-route" };
|
|
7077
|
+
}
|
|
7078
|
+
try {
|
|
7079
|
+
await navigateStudio(client, path14);
|
|
7080
|
+
} catch {
|
|
7081
|
+
return { acked: false, reason: "push-failed" };
|
|
7082
|
+
}
|
|
7083
|
+
const result = await awaitNavigateAck(client, path14, ackTimeoutMs);
|
|
7084
|
+
if (result.acked || result.reason !== "timeout") {
|
|
7085
|
+
return result;
|
|
7086
|
+
}
|
|
7087
|
+
const pingTs = Date.now();
|
|
7088
|
+
try {
|
|
7089
|
+
await pushAgentSessionEvent(client, {
|
|
7090
|
+
type: "agent:ping",
|
|
7091
|
+
data: { ts: pingTs }
|
|
7092
|
+
});
|
|
7093
|
+
} catch {
|
|
7094
|
+
return result;
|
|
7095
|
+
}
|
|
7096
|
+
const pongReceived = await awaitPong(client, pingTs);
|
|
7097
|
+
if (pongReceived) {
|
|
7098
|
+
return { acked: true };
|
|
7099
|
+
}
|
|
7100
|
+
return result;
|
|
7101
|
+
}
|
|
7102
|
+
async function sendPong(client, replyTo) {
|
|
7103
|
+
return pushAgentSessionEvent(client, {
|
|
7104
|
+
type: "agent:pong",
|
|
7105
|
+
data: { ts: Date.now(), replyTo }
|
|
7106
|
+
});
|
|
7107
|
+
}
|
|
7108
|
+
|
|
6808
7109
|
// ../bitfab-plugin-lib/dist/buildInfo.js
|
|
6809
7110
|
import crypto from "crypto";
|
|
6810
7111
|
import fs from "fs";
|
|
@@ -6839,18 +7140,19 @@ var GLOBAL_CONFIG_DIR = path3.join(os3.homedir(), ".config", "bitfab");
|
|
|
6839
7140
|
var GLOBAL_CONFIG_FILE = path3.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
6840
7141
|
var GLOBAL_CREDENTIALS_FILE = path3.join(GLOBAL_CONFIG_DIR, "credentials.json");
|
|
6841
7142
|
var PROJECT_CONFIG_RELATIVE = path3.join(".bitfab", "config.local.json");
|
|
6842
|
-
|
|
7143
|
+
var PROJECT_CREDENTIALS_RELATIVE = path3.join(".bitfab", "credentials.local.json");
|
|
7144
|
+
function readJsonFile(filePath2) {
|
|
6843
7145
|
try {
|
|
6844
|
-
const content = fs3.readFileSync(
|
|
7146
|
+
const content = fs3.readFileSync(filePath2, "utf-8");
|
|
6845
7147
|
return JSON.parse(content);
|
|
6846
7148
|
} catch {
|
|
6847
7149
|
return null;
|
|
6848
7150
|
}
|
|
6849
7151
|
}
|
|
6850
|
-
function
|
|
7152
|
+
function findProjectFile(relativePath, startDir = process.cwd()) {
|
|
6851
7153
|
let dir = startDir;
|
|
6852
7154
|
while (true) {
|
|
6853
|
-
const candidate = path3.join(dir,
|
|
7155
|
+
const candidate = path3.join(dir, relativePath);
|
|
6854
7156
|
if (fs3.existsSync(candidate)) {
|
|
6855
7157
|
return candidate;
|
|
6856
7158
|
}
|
|
@@ -6862,11 +7164,18 @@ function findProjectConfigPath(startDir = process.cwd()) {
|
|
|
6862
7164
|
}
|
|
6863
7165
|
}
|
|
6864
7166
|
function getProjectConfigData() {
|
|
6865
|
-
const
|
|
6866
|
-
if (!
|
|
7167
|
+
const filePath2 = findProjectFile(PROJECT_CONFIG_RELATIVE);
|
|
7168
|
+
if (!filePath2) {
|
|
7169
|
+
return null;
|
|
7170
|
+
}
|
|
7171
|
+
return readJsonFile(filePath2);
|
|
7172
|
+
}
|
|
7173
|
+
function getProjectCredentialsData() {
|
|
7174
|
+
const filePath2 = findProjectFile(PROJECT_CREDENTIALS_RELATIVE);
|
|
7175
|
+
if (!filePath2) {
|
|
6867
7176
|
return null;
|
|
6868
7177
|
}
|
|
6869
|
-
return readJsonFile(
|
|
7178
|
+
return readJsonFile(filePath2) ?? {};
|
|
6870
7179
|
}
|
|
6871
7180
|
function getConfigData() {
|
|
6872
7181
|
return readJsonFile(GLOBAL_CONFIG_FILE) ?? {};
|
|
@@ -6889,6 +7198,10 @@ function getApiKey() {
|
|
|
6889
7198
|
if (process.env.BITFAB_API_KEY) {
|
|
6890
7199
|
return process.env.BITFAB_API_KEY;
|
|
6891
7200
|
}
|
|
7201
|
+
const projectCreds = getProjectCredentialsData();
|
|
7202
|
+
if (projectCreds !== null) {
|
|
7203
|
+
return typeof projectCreds.apiKey === "string" ? projectCreds.apiKey : null;
|
|
7204
|
+
}
|
|
6892
7205
|
const creds = getCredentialsData();
|
|
6893
7206
|
return typeof creds.apiKey === "string" ? creds.apiKey : null;
|
|
6894
7207
|
}
|
|
@@ -6929,6 +7242,12 @@ function getConfig() {
|
|
|
6929
7242
|
};
|
|
6930
7243
|
}
|
|
6931
7244
|
function saveCredentials(apiKey) {
|
|
7245
|
+
const projectFile = findProjectFile(PROJECT_CREDENTIALS_RELATIVE);
|
|
7246
|
+
if (projectFile) {
|
|
7247
|
+
fs3.writeFileSync(projectFile, `${JSON.stringify({ apiKey }, null, 2)}
|
|
7248
|
+
`);
|
|
7249
|
+
return;
|
|
7250
|
+
}
|
|
6932
7251
|
fs3.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
6933
7252
|
fs3.writeFileSync(GLOBAL_CREDENTIALS_FILE, `${JSON.stringify({ apiKey }, null, 2)}
|
|
6934
7253
|
`);
|
|
@@ -6984,36 +7303,62 @@ function buildBitfabRequestHeaders(apiKey, pluginVersion, platform2) {
|
|
|
6984
7303
|
};
|
|
6985
7304
|
}
|
|
6986
7305
|
|
|
6987
|
-
// ../bitfab-plugin-lib/dist/
|
|
7306
|
+
// ../bitfab-plugin-lib/dist/activeStudioSession.js
|
|
6988
7307
|
import crypto3 from "crypto";
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
7308
|
+
import fs6 from "fs";
|
|
7309
|
+
import os6 from "os";
|
|
7310
|
+
import path5 from "path";
|
|
7311
|
+
function cwdHash() {
|
|
7312
|
+
return crypto3.createHash("sha256").update(process.cwd()).digest("hex").slice(0, 16);
|
|
7313
|
+
}
|
|
7314
|
+
function filePath() {
|
|
7315
|
+
return path5.join(os6.homedir(), ".config", "bitfab", `active-studio-session.${cwdHash()}.json`);
|
|
7316
|
+
}
|
|
7317
|
+
function writeActiveStudioSession(session) {
|
|
7318
|
+
const target = filePath();
|
|
7319
|
+
fs6.mkdirSync(path5.dirname(target), { recursive: true });
|
|
7320
|
+
const payload = {
|
|
7321
|
+
...session,
|
|
7322
|
+
pid: process.pid,
|
|
7323
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7324
|
+
};
|
|
7325
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
7326
|
+
fs6.writeFileSync(tmp, `${JSON.stringify(payload, null, 2)}
|
|
7327
|
+
`);
|
|
7328
|
+
fs6.renameSync(tmp, target);
|
|
7329
|
+
}
|
|
7330
|
+
function clearActiveStudioSession() {
|
|
6994
7331
|
try {
|
|
6995
|
-
const
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
body: JSON.stringify(body)
|
|
6999
|
-
});
|
|
7000
|
-
if (config2.debug && !response.ok) {
|
|
7001
|
-
console.error(`reportHandoff: server returned ${response.status} for ${body.flow}`);
|
|
7332
|
+
const current = readActiveStudioSessionRaw();
|
|
7333
|
+
if (current && current.pid !== process.pid) {
|
|
7334
|
+
return;
|
|
7002
7335
|
}
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7336
|
+
fs6.rmSync(filePath(), { force: true });
|
|
7337
|
+
} catch {
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
function readActiveStudioSessionRaw() {
|
|
7341
|
+
try {
|
|
7342
|
+
const raw = fs6.readFileSync(filePath(), "utf-8");
|
|
7343
|
+
const parsed = JSON.parse(raw);
|
|
7344
|
+
if (typeof parsed.sessionId === "string" && typeof parsed.serviceUrl === "string" && typeof parsed.pid === "number" && typeof parsed.startedAt === "string") {
|
|
7345
|
+
return parsed;
|
|
7007
7346
|
}
|
|
7347
|
+
return null;
|
|
7348
|
+
} catch {
|
|
7349
|
+
return null;
|
|
7008
7350
|
}
|
|
7009
7351
|
}
|
|
7352
|
+
function readActiveStudioSession() {
|
|
7353
|
+
return readActiveStudioSessionRaw();
|
|
7354
|
+
}
|
|
7010
7355
|
|
|
7011
7356
|
// ../bitfab-plugin-lib/dist/browser.js
|
|
7012
7357
|
import { execSync, spawn } from "child_process";
|
|
7013
|
-
import
|
|
7014
|
-
import
|
|
7358
|
+
import fs7 from "fs";
|
|
7359
|
+
import os7 from "os";
|
|
7015
7360
|
function getChromiumBrowsers() {
|
|
7016
|
-
const platform2 =
|
|
7361
|
+
const platform2 = os7.platform();
|
|
7017
7362
|
if (platform2 === "darwin") {
|
|
7018
7363
|
return [
|
|
7019
7364
|
{
|
|
@@ -7101,7 +7446,7 @@ function getChromiumBrowsers() {
|
|
|
7101
7446
|
function findExistingBinary(paths) {
|
|
7102
7447
|
for (const candidate of paths) {
|
|
7103
7448
|
if (candidate.includes("/") || candidate.includes("\\")) {
|
|
7104
|
-
if (
|
|
7449
|
+
if (fs7.existsSync(candidate)) {
|
|
7105
7450
|
return candidate;
|
|
7106
7451
|
}
|
|
7107
7452
|
} else {
|
|
@@ -7115,10 +7460,10 @@ function findExistingBinary(paths) {
|
|
|
7115
7460
|
return null;
|
|
7116
7461
|
}
|
|
7117
7462
|
function getDefaultBrowserId() {
|
|
7118
|
-
const platform2 =
|
|
7463
|
+
const platform2 = os7.platform();
|
|
7119
7464
|
try {
|
|
7120
7465
|
if (platform2 === "darwin") {
|
|
7121
|
-
const plistPath = `${
|
|
7466
|
+
const plistPath = `${os7.homedir()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
|
|
7122
7467
|
const json2 = execSync(`plutil -convert json -o - "${plistPath}"`, {
|
|
7123
7468
|
stdio: ["ignore", "pipe", "ignore"],
|
|
7124
7469
|
encoding: "utf-8",
|
|
@@ -7159,7 +7504,7 @@ function matchChromiumBrowser(defaultId, browsers) {
|
|
|
7159
7504
|
var SCREEN_FRACTION = 0.95;
|
|
7160
7505
|
function getWorkArea() {
|
|
7161
7506
|
try {
|
|
7162
|
-
const platform2 =
|
|
7507
|
+
const platform2 = os7.platform();
|
|
7163
7508
|
if (platform2 === "darwin") {
|
|
7164
7509
|
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])'`, {
|
|
7165
7510
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -7228,7 +7573,7 @@ function openChromiumApp(binaryPath, url2, sizeArgs) {
|
|
|
7228
7573
|
spawnDetached(binaryPath, [`--app=${url2}`, ...sizeArgs]);
|
|
7229
7574
|
}
|
|
7230
7575
|
function openOsDefault(url2) {
|
|
7231
|
-
const platform2 =
|
|
7576
|
+
const platform2 = os7.platform();
|
|
7232
7577
|
if (platform2 === "darwin") {
|
|
7233
7578
|
spawnDetached("open", [url2]);
|
|
7234
7579
|
} else if (platform2 === "win32") {
|
|
@@ -7279,9 +7624,61 @@ function openChromelessWindow(url2) {
|
|
|
7279
7624
|
openOsDefault(url2);
|
|
7280
7625
|
}
|
|
7281
7626
|
|
|
7627
|
+
// ../bitfab-plugin-lib/dist/commands/createStudioSession.js
|
|
7628
|
+
function ensureStudioPath(p5) {
|
|
7629
|
+
if (!p5.startsWith("/studio")) {
|
|
7630
|
+
return "/studio";
|
|
7631
|
+
}
|
|
7632
|
+
return p5;
|
|
7633
|
+
}
|
|
7634
|
+
async function createStudioSession({ serviceUrl, apiKey, sessionId, initialPath = "/studio", clientHeaders }) {
|
|
7635
|
+
if (!apiKey) {
|
|
7636
|
+
throw new NotAuthenticatedError();
|
|
7637
|
+
}
|
|
7638
|
+
initialPath = ensureStudioPath(initialPath);
|
|
7639
|
+
if (!isValidStudioRoute(initialPath)) {
|
|
7640
|
+
throw new Error(`Studio route not allowed: ${initialPath}`);
|
|
7641
|
+
}
|
|
7642
|
+
await createAgentSession({
|
|
7643
|
+
serviceUrl,
|
|
7644
|
+
apiKey,
|
|
7645
|
+
sessionId,
|
|
7646
|
+
extraHeaders: clientHeaders
|
|
7647
|
+
});
|
|
7648
|
+
writeActiveStudioSession({ sessionId, serviceUrl });
|
|
7649
|
+
const separator = initialPath.includes("?") ? "&" : "?";
|
|
7650
|
+
const url2 = `${serviceUrl}${initialPath}${separator}session=${encodeURIComponent(sessionId)}`;
|
|
7651
|
+
openChromelessWindow(url2);
|
|
7652
|
+
return { sessionId, serviceUrl };
|
|
7653
|
+
}
|
|
7654
|
+
|
|
7655
|
+
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
7656
|
+
import crypto5 from "crypto";
|
|
7657
|
+
|
|
7658
|
+
// ../bitfab-plugin-lib/dist/analytics.js
|
|
7659
|
+
async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
7660
|
+
const config2 = getConfig();
|
|
7661
|
+
const headers = buildBitfabRequestHeaders(apiKey, pluginVersion, platform2);
|
|
7662
|
+
try {
|
|
7663
|
+
const response = await fetch(`${config2.serviceUrl}/api/plugin/analytics/handoff`, {
|
|
7664
|
+
method: "POST",
|
|
7665
|
+
headers,
|
|
7666
|
+
body: JSON.stringify(body)
|
|
7667
|
+
});
|
|
7668
|
+
if (config2.debug && !response.ok) {
|
|
7669
|
+
console.error(`reportHandoff: server returned ${response.status} for ${body.flow}`);
|
|
7670
|
+
}
|
|
7671
|
+
} catch (err) {
|
|
7672
|
+
if (config2.debug) {
|
|
7673
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7674
|
+
console.error(`reportHandoff: request failed for ${body.flow}: ${message}`);
|
|
7675
|
+
}
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
|
|
7282
7679
|
// ../bitfab-plugin-lib/dist/frontmostApp.js
|
|
7283
7680
|
import { execFileSync as execFileSync2, spawn as spawn2 } from "child_process";
|
|
7284
|
-
import
|
|
7681
|
+
import os8 from "os";
|
|
7285
7682
|
function findAncestorApp() {
|
|
7286
7683
|
try {
|
|
7287
7684
|
let pid = process.ppid;
|
|
@@ -7342,7 +7739,7 @@ function focusItermSession(sessionId) {
|
|
|
7342
7739
|
spawnQuiet("osascript", ["-e", script]);
|
|
7343
7740
|
}
|
|
7344
7741
|
function getFrontmostApp() {
|
|
7345
|
-
const platform2 =
|
|
7742
|
+
const platform2 = os8.platform();
|
|
7346
7743
|
try {
|
|
7347
7744
|
if (platform2 === "darwin") {
|
|
7348
7745
|
return execFileSync2("osascript", [
|
|
@@ -7376,7 +7773,7 @@ function focusApp(identifier) {
|
|
|
7376
7773
|
if (!identifier) {
|
|
7377
7774
|
return;
|
|
7378
7775
|
}
|
|
7379
|
-
const platform2 =
|
|
7776
|
+
const platform2 = os8.platform();
|
|
7380
7777
|
if (platform2 === "darwin") {
|
|
7381
7778
|
spawnQuiet("osascript", [
|
|
7382
7779
|
"-e",
|
|
@@ -7406,7 +7803,7 @@ function focusApp(identifier) {
|
|
|
7406
7803
|
}
|
|
7407
7804
|
}
|
|
7408
7805
|
function recordFocus() {
|
|
7409
|
-
const platform2 =
|
|
7806
|
+
const platform2 = os8.platform();
|
|
7410
7807
|
if (platform2 === "linux") {
|
|
7411
7808
|
const windowId = process.env.WINDOWID;
|
|
7412
7809
|
if (windowId) {
|
|
@@ -7424,9 +7821,151 @@ function recordFocus() {
|
|
|
7424
7821
|
return () => focusApp(frontmost);
|
|
7425
7822
|
}
|
|
7426
7823
|
|
|
7427
|
-
// ../bitfab-plugin-lib/dist/commands/
|
|
7824
|
+
// ../bitfab-plugin-lib/dist/commands/openStudioTo.js
|
|
7825
|
+
import crypto4 from "crypto";
|
|
7826
|
+
|
|
7827
|
+
// ../bitfab-plugin-lib/dist/commands/loginForSession.js
|
|
7428
7828
|
var LOGIN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
7429
|
-
|
|
7829
|
+
|
|
7830
|
+
// ../bitfab-plugin-lib/dist/commands/openStudioTo.js
|
|
7831
|
+
var SESSION_CLOSE_GRACE_PERIOD_MS = 1e4;
|
|
7832
|
+
var StudioNavigationError = class extends Error {
|
|
7833
|
+
reason;
|
|
7834
|
+
blockedReason;
|
|
7835
|
+
staleSessionId;
|
|
7836
|
+
constructor(reason, blockedReason, staleSessionId) {
|
|
7837
|
+
super(blockedReason ? `Studio navigation failed (${reason}): ${blockedReason}` : `Studio navigation failed: ${reason}`);
|
|
7838
|
+
this.reason = reason;
|
|
7839
|
+
this.blockedReason = blockedReason;
|
|
7840
|
+
this.staleSessionId = staleSessionId;
|
|
7841
|
+
this.name = "StudioNavigationError";
|
|
7842
|
+
}
|
|
7843
|
+
};
|
|
7844
|
+
async function openStudioTo(path14, opts) {
|
|
7845
|
+
const sessionId = opts.sessionId ?? crypto4.randomUUID();
|
|
7846
|
+
const noop = () => {
|
|
7847
|
+
};
|
|
7848
|
+
const restoreFocus = recordFocus();
|
|
7849
|
+
if (!opts.apiKey) {
|
|
7850
|
+
const separator = path14.includes("?") ? "&" : "?";
|
|
7851
|
+
const url2 = `${opts.serviceUrl}${path14}${separator}session=${encodeURIComponent(sessionId)}`;
|
|
7852
|
+
openChromelessWindow(url2);
|
|
7853
|
+
return {
|
|
7854
|
+
sessionId,
|
|
7855
|
+
serviceUrl: opts.serviceUrl,
|
|
7856
|
+
opened: true,
|
|
7857
|
+
abort: noop,
|
|
7858
|
+
done: Promise.resolve()
|
|
7859
|
+
};
|
|
7860
|
+
}
|
|
7861
|
+
if (opts.forceNew) {
|
|
7862
|
+
clearActiveStudioSession();
|
|
7863
|
+
}
|
|
7864
|
+
const existing = opts.forceNew ? null : readActiveStudioSession();
|
|
7865
|
+
if (existing) {
|
|
7866
|
+
const client = {
|
|
7867
|
+
serviceUrl: existing.serviceUrl,
|
|
7868
|
+
apiKey: opts.apiKey,
|
|
7869
|
+
sessionId: existing.sessionId
|
|
7870
|
+
};
|
|
7871
|
+
const ack = await navigateStudioAndAwaitAck(client, path14);
|
|
7872
|
+
if (ack.acked) {
|
|
7873
|
+
const poller2 = opts.onEvent ? startEventPoller(existing.serviceUrl, opts.apiKey, existing.sessionId, opts.onEvent, restoreFocus) : { abort: noop, done: Promise.resolve() };
|
|
7874
|
+
return {
|
|
7875
|
+
sessionId: existing.sessionId,
|
|
7876
|
+
serviceUrl: existing.serviceUrl,
|
|
7877
|
+
opened: false,
|
|
7878
|
+
...poller2
|
|
7879
|
+
};
|
|
7880
|
+
}
|
|
7881
|
+
if (ack.reason === "session-ended" || ack.reason === "push-failed") {
|
|
7882
|
+
if (ack.reason === "session-ended") {
|
|
7883
|
+
clearActiveStudioSession();
|
|
7884
|
+
}
|
|
7885
|
+
} else {
|
|
7886
|
+
throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
|
|
7887
|
+
}
|
|
7888
|
+
}
|
|
7889
|
+
const session = await createStudioSession({
|
|
7890
|
+
serviceUrl: opts.serviceUrl,
|
|
7891
|
+
apiKey: opts.apiKey,
|
|
7892
|
+
sessionId,
|
|
7893
|
+
initialPath: path14,
|
|
7894
|
+
clientHeaders: opts.clientHeaders
|
|
7895
|
+
});
|
|
7896
|
+
const poller = opts.onEvent ? startEventPoller(session.serviceUrl, opts.apiKey, session.sessionId, opts.onEvent, restoreFocus) : { abort: noop, done: Promise.resolve() };
|
|
7897
|
+
return {
|
|
7898
|
+
sessionId: session.sessionId,
|
|
7899
|
+
serviceUrl: session.serviceUrl,
|
|
7900
|
+
opened: true,
|
|
7901
|
+
...poller
|
|
7902
|
+
};
|
|
7903
|
+
}
|
|
7904
|
+
function startEventPoller(serviceUrl, apiKey, sessionId, onEvent, restoreFocus) {
|
|
7905
|
+
const abortController = new AbortController();
|
|
7906
|
+
let sessionEndGraceTimer = null;
|
|
7907
|
+
const done = pollAgentSessionEvents({
|
|
7908
|
+
serviceUrl,
|
|
7909
|
+
apiKey,
|
|
7910
|
+
sessionId,
|
|
7911
|
+
abortSignal: abortController.signal,
|
|
7912
|
+
onEvent: (event) => {
|
|
7913
|
+
if (event.type === "studio:session-closed") {
|
|
7914
|
+
if (sessionEndGraceTimer) {
|
|
7915
|
+
return;
|
|
7916
|
+
}
|
|
7917
|
+
sessionEndGraceTimer = setTimeout(() => {
|
|
7918
|
+
sessionEndGraceTimer = null;
|
|
7919
|
+
clearActiveStudioSession();
|
|
7920
|
+
restoreFocus();
|
|
7921
|
+
onEvent({ ...event, type: "studio:session-ended" });
|
|
7922
|
+
abortController.abort();
|
|
7923
|
+
}, SESSION_CLOSE_GRACE_PERIOD_MS);
|
|
7924
|
+
return;
|
|
7925
|
+
}
|
|
7926
|
+
if (event.type === "studio:session-started") {
|
|
7927
|
+
if (sessionEndGraceTimer) {
|
|
7928
|
+
clearTimeout(sessionEndGraceTimer);
|
|
7929
|
+
sessionEndGraceTimer = null;
|
|
7930
|
+
console.error("Studio reconnected after page refresh");
|
|
7931
|
+
}
|
|
7932
|
+
return;
|
|
7933
|
+
}
|
|
7934
|
+
if (event.type === "studio:session-ended") {
|
|
7935
|
+
if (sessionEndGraceTimer) {
|
|
7936
|
+
clearTimeout(sessionEndGraceTimer);
|
|
7937
|
+
sessionEndGraceTimer = null;
|
|
7938
|
+
}
|
|
7939
|
+
clearActiveStudioSession();
|
|
7940
|
+
restoreFocus();
|
|
7941
|
+
onEvent(event);
|
|
7942
|
+
abortController.abort();
|
|
7943
|
+
return;
|
|
7944
|
+
}
|
|
7945
|
+
if (event.type === "studio:not-member") {
|
|
7946
|
+
restoreFocus();
|
|
7947
|
+
onEvent(event);
|
|
7948
|
+
abortController.abort();
|
|
7949
|
+
return;
|
|
7950
|
+
}
|
|
7951
|
+
if (event.type === "studio:return-to-agent" || event.type === "studio:edit-with-agent") {
|
|
7952
|
+
restoreFocus();
|
|
7953
|
+
}
|
|
7954
|
+
onEvent(event);
|
|
7955
|
+
},
|
|
7956
|
+
onError: (err) => {
|
|
7957
|
+
console.error(`studio agent-event poll: ${err.message}`);
|
|
7958
|
+
}
|
|
7959
|
+
}).catch((err) => {
|
|
7960
|
+
if (!abortController.signal.aborted) {
|
|
7961
|
+
console.error(`studio agent-event poll terminated: ${err.message}`);
|
|
7962
|
+
}
|
|
7963
|
+
});
|
|
7964
|
+
return { abort: () => abortController.abort(), done };
|
|
7965
|
+
}
|
|
7966
|
+
|
|
7967
|
+
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
7968
|
+
var LOGIN_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
7430
7969
|
async function verifyToken(serviceUrl, token) {
|
|
7431
7970
|
try {
|
|
7432
7971
|
const res = await fetch(`${serviceUrl}/api/plugin/whoami`, {
|
|
@@ -7440,58 +7979,50 @@ async function verifyToken(serviceUrl, token) {
|
|
|
7440
7979
|
return null;
|
|
7441
7980
|
}
|
|
7442
7981
|
}
|
|
7443
|
-
async function pollLoginStatus(serviceUrl, loginId, signal) {
|
|
7444
|
-
while (!signal.aborted) {
|
|
7445
|
-
try {
|
|
7446
|
-
const url2 = `${serviceUrl}/api/studio/login-status?loginId=${encodeURIComponent(loginId)}`;
|
|
7447
|
-
const res = await fetch(url2, { signal });
|
|
7448
|
-
if (res.ok) {
|
|
7449
|
-
const body = await res.json();
|
|
7450
|
-
if (body.status === "complete" && body.token) {
|
|
7451
|
-
return body.token;
|
|
7452
|
-
}
|
|
7453
|
-
}
|
|
7454
|
-
} catch {
|
|
7455
|
-
if (signal.aborted) {
|
|
7456
|
-
throw new Error("Login polling aborted");
|
|
7457
|
-
}
|
|
7458
|
-
}
|
|
7459
|
-
await new Promise((resolve) => {
|
|
7460
|
-
const timer = setTimeout(resolve, POLL_INTERVAL_MS);
|
|
7461
|
-
signal.addEventListener("abort", () => {
|
|
7462
|
-
clearTimeout(timer);
|
|
7463
|
-
resolve();
|
|
7464
|
-
}, { once: true });
|
|
7465
|
-
});
|
|
7466
|
-
}
|
|
7467
|
-
throw new Error("Login polling aborted");
|
|
7468
|
-
}
|
|
7469
7982
|
async function runLogin(platform2, pluginVersion, options) {
|
|
7470
7983
|
const exitOnComplete = options?.exitOnComplete ?? true;
|
|
7471
7984
|
const config2 = getConfig();
|
|
7472
7985
|
const restoreFocus = recordFocus();
|
|
7473
|
-
const
|
|
7474
|
-
const
|
|
7475
|
-
const
|
|
7986
|
+
const sessionId = crypto5.randomUUID();
|
|
7987
|
+
const redirectPath = `/studio/close?pluginLogin=true&session=${sessionId}&autoClose=true&message=${encodeURIComponent("Login complete")}`;
|
|
7988
|
+
const signInPath = `/studio/sign-in?redirect_url=${encodeURIComponent(redirectPath)}`;
|
|
7476
7989
|
console.log("\nOpening Studio to sign in...");
|
|
7477
|
-
console.log(
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7990
|
+
console.log("(If the browser didn't open, visit the Studio URL manually.)");
|
|
7991
|
+
try {
|
|
7992
|
+
await openStudioTo(signInPath, {
|
|
7993
|
+
apiKey: hasCredentials() ? config2.apiKey : null,
|
|
7994
|
+
serviceUrl: config2.serviceUrl,
|
|
7995
|
+
sessionId
|
|
7996
|
+
});
|
|
7997
|
+
} catch (err) {
|
|
7998
|
+
if (err instanceof StudioNavigationError) {
|
|
7999
|
+
await openStudioTo(signInPath, {
|
|
8000
|
+
apiKey: null,
|
|
8001
|
+
serviceUrl: config2.serviceUrl,
|
|
8002
|
+
sessionId
|
|
8003
|
+
});
|
|
8004
|
+
} else {
|
|
8005
|
+
throw err;
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
7481
8008
|
const abortController = new AbortController();
|
|
7482
8009
|
let loginTimer;
|
|
7483
8010
|
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
7484
8011
|
loginTimer = setTimeout(() => {
|
|
7485
8012
|
abortController.abort();
|
|
7486
8013
|
reject(new Error("Authentication timed out after 10 minutes. Run login again."));
|
|
7487
|
-
},
|
|
8014
|
+
}, LOGIN_TIMEOUT_MS2);
|
|
7488
8015
|
});
|
|
7489
8016
|
const keepaliveTimer = setInterval(() => {
|
|
7490
8017
|
process.stdout.write("");
|
|
7491
8018
|
}, 3e4);
|
|
7492
8019
|
try {
|
|
7493
8020
|
const token = await Promise.race([
|
|
7494
|
-
|
|
8021
|
+
pollLoginEvents({
|
|
8022
|
+
serviceUrl: config2.serviceUrl,
|
|
8023
|
+
sessionId,
|
|
8024
|
+
abortSignal: abortController.signal
|
|
8025
|
+
}),
|
|
7495
8026
|
timeoutPromise
|
|
7496
8027
|
]);
|
|
7497
8028
|
if (loginTimer) {
|
|
@@ -7499,11 +8030,6 @@ async function runLogin(platform2, pluginVersion, options) {
|
|
|
7499
8030
|
}
|
|
7500
8031
|
clearInterval(keepaliveTimer);
|
|
7501
8032
|
saveCredentials(token);
|
|
7502
|
-
const who = await verifyToken(config2.serviceUrl, token);
|
|
7503
|
-
const greeting = who?.user.email ? `Logged in as ${who.user.email}.` : "Authentication successful.";
|
|
7504
|
-
console.log(`
|
|
7505
|
-
${greeting}`);
|
|
7506
|
-
console.log("Bitfab MCP tools are now active. (via studio)");
|
|
7507
8033
|
await reportHandoff(token, pluginVersion, platform2, {
|
|
7508
8034
|
flow: "auth.login",
|
|
7509
8035
|
wonBy: "studio",
|
|
@@ -7511,6 +8037,11 @@ ${greeting}`);
|
|
|
7511
8037
|
ticketAvailable: false
|
|
7512
8038
|
});
|
|
7513
8039
|
restoreFocus();
|
|
8040
|
+
const who = await verifyToken(config2.serviceUrl, token);
|
|
8041
|
+
const greeting = who?.user.email ? `Logged in as ${who.user.email}.` : "Authentication successful.";
|
|
8042
|
+
console.log(`
|
|
8043
|
+
${greeting}`);
|
|
8044
|
+
console.log("Bitfab MCP tools are now active. (via studio)");
|
|
7514
8045
|
if (exitOnComplete) {
|
|
7515
8046
|
process.exit(0);
|
|
7516
8047
|
}
|
|
@@ -7540,19 +8071,8 @@ function runLogout() {
|
|
|
7540
8071
|
console.log("Logged out of Bitfab.");
|
|
7541
8072
|
}
|
|
7542
8073
|
|
|
7543
|
-
// ../bitfab-plugin-lib/dist/commands/openStudio.js
|
|
7544
|
-
import crypto5 from "crypto";
|
|
7545
|
-
|
|
7546
|
-
// ../bitfab-plugin-lib/dist/activeStudioSession.js
|
|
7547
|
-
import crypto4 from "crypto";
|
|
7548
|
-
import fs7 from "fs";
|
|
7549
|
-
import os8 from "os";
|
|
7550
|
-
import path5 from "path";
|
|
7551
|
-
|
|
7552
|
-
// ../bitfab-plugin-lib/dist/withStudioSession.js
|
|
7553
|
-
import crypto6 from "crypto";
|
|
7554
|
-
|
|
7555
8074
|
// ../bitfab-plugin-lib/dist/commands/openTracePlan.js
|
|
8075
|
+
import crypto6 from "crypto";
|
|
7556
8076
|
var OPEN_TRACE_PLAN_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
7557
8077
|
|
|
7558
8078
|
// ../bitfab-plugin-lib/dist/commands/persistReplayLabels.js
|
|
@@ -21352,6 +21872,12 @@ var inputFileSchema = external_exports.object({
|
|
|
21352
21872
|
verdicts: external_exports.array(external_exports.unknown()).min(1)
|
|
21353
21873
|
});
|
|
21354
21874
|
|
|
21875
|
+
// ../bitfab-plugin-lib/dist/commands/startDataset.js
|
|
21876
|
+
import crypto7 from "crypto";
|
|
21877
|
+
|
|
21878
|
+
// ../bitfab-plugin-lib/dist/commands/startTemplatePreview.js
|
|
21879
|
+
import crypto8 from "crypto";
|
|
21880
|
+
|
|
21355
21881
|
// ../bitfab-plugin-lib/dist/activePreviewSession.js
|
|
21356
21882
|
import fs8 from "fs";
|
|
21357
21883
|
import os9 from "os";
|
|
@@ -27312,6 +27838,47 @@ function runLogoutCommand() {
|
|
|
27312
27838
|
runLogout();
|
|
27313
27839
|
}
|
|
27314
27840
|
|
|
27841
|
+
// src/updateCheck.ts
|
|
27842
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/bitfab-cli/latest";
|
|
27843
|
+
var TIMEOUT_MS = 3e3;
|
|
27844
|
+
function isNewer(latest, current) {
|
|
27845
|
+
const parse3 = (v) => v.split(".").map(Number);
|
|
27846
|
+
const [lMaj = 0, lMin = 0, lPat = 0] = parse3(latest);
|
|
27847
|
+
const [cMaj = 0, cMin = 0, cPat = 0] = parse3(current);
|
|
27848
|
+
if (lMaj !== cMaj) {
|
|
27849
|
+
return lMaj > cMaj;
|
|
27850
|
+
}
|
|
27851
|
+
if (lMin !== cMin) {
|
|
27852
|
+
return lMin > cMin;
|
|
27853
|
+
}
|
|
27854
|
+
return lPat > cPat;
|
|
27855
|
+
}
|
|
27856
|
+
function startUpdateCheck() {
|
|
27857
|
+
const controller = new AbortController();
|
|
27858
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
27859
|
+
const current = getVersion();
|
|
27860
|
+
const done = fetch(NPM_REGISTRY_URL, { signal: controller.signal }).then(
|
|
27861
|
+
(res) => res.ok ? res.json() : null
|
|
27862
|
+
).then((data) => {
|
|
27863
|
+
const latest = data?.version;
|
|
27864
|
+
if (latest && isNewer(latest, current)) {
|
|
27865
|
+
process.stderr.write(
|
|
27866
|
+
`
|
|
27867
|
+
Update available: ${current} -> ${latest}
|
|
27868
|
+
Run \`npx bitfab-cli@latest\` to get the newest version.
|
|
27869
|
+
|
|
27870
|
+
`
|
|
27871
|
+
);
|
|
27872
|
+
}
|
|
27873
|
+
}).catch(() => {
|
|
27874
|
+
}).finally(() => clearTimeout(timer));
|
|
27875
|
+
return () => {
|
|
27876
|
+
controller.abort();
|
|
27877
|
+
done.catch(() => {
|
|
27878
|
+
});
|
|
27879
|
+
};
|
|
27880
|
+
}
|
|
27881
|
+
|
|
27315
27882
|
// src/index.ts
|
|
27316
27883
|
var HELP_TEXT = `Usage: bitfab <command> [options]
|
|
27317
27884
|
|
|
@@ -27337,7 +27904,7 @@ Examples:
|
|
|
27337
27904
|
bitfab setup --skip-permissions Setup without permission prompts
|
|
27338
27905
|
bitfab assistant --skip-permissions Run assistant autonomously
|
|
27339
27906
|
`;
|
|
27340
|
-
function
|
|
27907
|
+
function parseArgs2(argv) {
|
|
27341
27908
|
const [command, ...tail] = argv;
|
|
27342
27909
|
let editor;
|
|
27343
27910
|
let skipPermissions;
|
|
@@ -27364,44 +27931,54 @@ function parseArgs(argv) {
|
|
|
27364
27931
|
return { command, editor, skipPermissions, rest };
|
|
27365
27932
|
}
|
|
27366
27933
|
async function main() {
|
|
27367
|
-
const { command, editor, skipPermissions, rest } =
|
|
27934
|
+
const { command, editor, skipPermissions, rest } = parseArgs2(
|
|
27368
27935
|
process.argv.slice(2)
|
|
27369
27936
|
);
|
|
27937
|
+
const abortUpdateCheck = startUpdateCheck();
|
|
27370
27938
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
27371
27939
|
process.stdout.write(HELP_TEXT);
|
|
27940
|
+
abortUpdateCheck();
|
|
27372
27941
|
return;
|
|
27373
27942
|
}
|
|
27374
27943
|
if (command === "init") {
|
|
27375
27944
|
await runInit({ editor, skipPermissions });
|
|
27945
|
+
abortUpdateCheck();
|
|
27376
27946
|
return;
|
|
27377
27947
|
}
|
|
27378
27948
|
if (command === "plugin-install") {
|
|
27379
27949
|
await runInstall({ editor, skipPermissions });
|
|
27950
|
+
abortUpdateCheck();
|
|
27380
27951
|
return;
|
|
27381
27952
|
}
|
|
27382
27953
|
if (command === "login") {
|
|
27383
27954
|
await runLoginCommand();
|
|
27955
|
+
abortUpdateCheck();
|
|
27384
27956
|
return;
|
|
27385
27957
|
}
|
|
27386
27958
|
if (command === "logout") {
|
|
27387
27959
|
runLogoutCommand();
|
|
27960
|
+
abortUpdateCheck();
|
|
27388
27961
|
return;
|
|
27389
27962
|
}
|
|
27390
27963
|
if (command === "setup") {
|
|
27391
27964
|
await runSetup({ editor, skipPermissions });
|
|
27965
|
+
abortUpdateCheck();
|
|
27392
27966
|
return;
|
|
27393
27967
|
}
|
|
27394
27968
|
if (command === "assistant") {
|
|
27395
27969
|
await runAssistant({ editor, skipPermissions }, rest);
|
|
27970
|
+
abortUpdateCheck();
|
|
27396
27971
|
return;
|
|
27397
27972
|
}
|
|
27398
27973
|
if (command === "update") {
|
|
27399
27974
|
await runUpdate2({ editor, skipPermissions }, rest);
|
|
27975
|
+
abortUpdateCheck();
|
|
27400
27976
|
return;
|
|
27401
27977
|
}
|
|
27402
27978
|
process.stderr.write(`Unknown command: ${command}
|
|
27403
27979
|
|
|
27404
27980
|
${HELP_TEXT}`);
|
|
27981
|
+
abortUpdateCheck();
|
|
27405
27982
|
process.exit(1);
|
|
27406
27983
|
}
|
|
27407
27984
|
main().catch((err) => {
|
|
@@ -27410,5 +27987,5 @@ main().catch((err) => {
|
|
|
27410
27987
|
process.exit(1);
|
|
27411
27988
|
});
|
|
27412
27989
|
export {
|
|
27413
|
-
parseArgs
|
|
27990
|
+
parseArgs2 as parseArgs
|
|
27414
27991
|
};
|