bitfab-cli 0.2.5 → 0.2.8
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 +386 -761
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3482,7 +3482,7 @@ var require_schemes = __commonJS({
|
|
|
3482
3482
|
urnComponent.nss = (uuidComponent.uuid || "").toLowerCase();
|
|
3483
3483
|
return urnComponent;
|
|
3484
3484
|
}
|
|
3485
|
-
var
|
|
3485
|
+
var http = (
|
|
3486
3486
|
/** @type {SchemeHandler} */
|
|
3487
3487
|
{
|
|
3488
3488
|
scheme: "http",
|
|
@@ -3495,7 +3495,7 @@ var require_schemes = __commonJS({
|
|
|
3495
3495
|
/** @type {SchemeHandler} */
|
|
3496
3496
|
{
|
|
3497
3497
|
scheme: "https",
|
|
3498
|
-
domainHost:
|
|
3498
|
+
domainHost: http.domainHost,
|
|
3499
3499
|
parse: httpParse,
|
|
3500
3500
|
serialize: httpSerialize
|
|
3501
3501
|
}
|
|
@@ -3539,7 +3539,7 @@ var require_schemes = __commonJS({
|
|
|
3539
3539
|
var SCHEMES = (
|
|
3540
3540
|
/** @type {Record<SchemeName, SchemeHandler>} */
|
|
3541
3541
|
{
|
|
3542
|
-
http
|
|
3542
|
+
http,
|
|
3543
3543
|
https,
|
|
3544
3544
|
ws,
|
|
3545
3545
|
wss,
|
|
@@ -6805,15 +6805,215 @@ 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/
|
|
6809
|
-
import
|
|
6808
|
+
// ../bitfab-plugin-lib/dist/buildInfo.js
|
|
6809
|
+
import crypto from "crypto";
|
|
6810
|
+
import fs from "fs";
|
|
6811
|
+
import os from "os";
|
|
6812
|
+
import path from "path";
|
|
6813
|
+
import { fileURLToPath } from "url";
|
|
6814
|
+
|
|
6815
|
+
// ../bitfab-plugin-lib/dist/chatSessions/index.js
|
|
6816
|
+
import { execFileSync } from "child_process";
|
|
6817
|
+
import fs5 from "fs";
|
|
6818
|
+
import os4 from "os";
|
|
6819
|
+
import path4 from "path";
|
|
6820
|
+
|
|
6821
|
+
// ../bitfab-plugin-lib/dist/skills.js
|
|
6822
|
+
var BITFAB_SKILLS = ["setup", "assistant", "update"];
|
|
6823
|
+
|
|
6824
|
+
// ../bitfab-plugin-lib/dist/hooks/skillDetection.js
|
|
6825
|
+
var SKILL_PATTERN = new RegExp(`^\\s*\\/bitfab[:-](?<skill>${BITFAB_SKILLS.join("|")})(?=\\s|$)`, "i");
|
|
6826
|
+
|
|
6827
|
+
// ../bitfab-plugin-lib/dist/chatSessions/arming.js
|
|
6828
|
+
import fs2 from "fs";
|
|
6829
|
+
import os2 from "os";
|
|
6830
|
+
import path2 from "path";
|
|
6831
|
+
|
|
6832
|
+
// ../bitfab-plugin-lib/dist/config.js
|
|
6833
|
+
import crypto2 from "crypto";
|
|
6834
|
+
import fs3 from "fs";
|
|
6835
|
+
import os3 from "os";
|
|
6836
|
+
import path3 from "path";
|
|
6837
|
+
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
6838
|
+
var GLOBAL_CONFIG_DIR = path3.join(os3.homedir(), ".config", "bitfab");
|
|
6839
|
+
var GLOBAL_CONFIG_FILE = path3.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
6840
|
+
var GLOBAL_CREDENTIALS_FILE = path3.join(GLOBAL_CONFIG_DIR, "credentials.json");
|
|
6841
|
+
var PROJECT_CONFIG_RELATIVE = path3.join(".bitfab", "config.local.json");
|
|
6842
|
+
function readJsonFile(filePath) {
|
|
6843
|
+
try {
|
|
6844
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
6845
|
+
return JSON.parse(content);
|
|
6846
|
+
} catch {
|
|
6847
|
+
return null;
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
function findProjectConfigPath(startDir = process.cwd()) {
|
|
6851
|
+
let dir = startDir;
|
|
6852
|
+
while (true) {
|
|
6853
|
+
const candidate = path3.join(dir, PROJECT_CONFIG_RELATIVE);
|
|
6854
|
+
if (fs3.existsSync(candidate)) {
|
|
6855
|
+
return candidate;
|
|
6856
|
+
}
|
|
6857
|
+
const parent = path3.dirname(dir);
|
|
6858
|
+
if (parent === dir) {
|
|
6859
|
+
return null;
|
|
6860
|
+
}
|
|
6861
|
+
dir = parent;
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
function getProjectConfigData() {
|
|
6865
|
+
const filePath = findProjectConfigPath();
|
|
6866
|
+
if (!filePath) {
|
|
6867
|
+
return null;
|
|
6868
|
+
}
|
|
6869
|
+
return readJsonFile(filePath);
|
|
6870
|
+
}
|
|
6871
|
+
function getConfigData() {
|
|
6872
|
+
return readJsonFile(GLOBAL_CONFIG_FILE) ?? {};
|
|
6873
|
+
}
|
|
6874
|
+
function getCredentialsData() {
|
|
6875
|
+
return readJsonFile(GLOBAL_CREDENTIALS_FILE) ?? {};
|
|
6876
|
+
}
|
|
6877
|
+
function getServiceUrl() {
|
|
6878
|
+
if (process.env.BITFAB_SERVICE_URL) {
|
|
6879
|
+
return process.env.BITFAB_SERVICE_URL;
|
|
6880
|
+
}
|
|
6881
|
+
const project = getProjectConfigData();
|
|
6882
|
+
if (project && typeof project.serviceUrl === "string") {
|
|
6883
|
+
return project.serviceUrl;
|
|
6884
|
+
}
|
|
6885
|
+
const config2 = getConfigData();
|
|
6886
|
+
return typeof config2.serviceUrl === "string" ? config2.serviceUrl : DEFAULT_SERVICE_URL;
|
|
6887
|
+
}
|
|
6888
|
+
function getApiKey() {
|
|
6889
|
+
if (process.env.BITFAB_API_KEY) {
|
|
6890
|
+
return process.env.BITFAB_API_KEY;
|
|
6891
|
+
}
|
|
6892
|
+
const creds = getCredentialsData();
|
|
6893
|
+
return typeof creds.apiKey === "string" ? creds.apiKey : null;
|
|
6894
|
+
}
|
|
6895
|
+
function getVerbose() {
|
|
6896
|
+
if (process.env.BITFAB_VERBOSE === "true" || process.env.BITFAB_VERBOSE === "1") {
|
|
6897
|
+
return true;
|
|
6898
|
+
}
|
|
6899
|
+
if (process.env.BITFAB_VERBOSE === "false" || process.env.BITFAB_VERBOSE === "0") {
|
|
6900
|
+
return false;
|
|
6901
|
+
}
|
|
6902
|
+
const config2 = getConfigData();
|
|
6903
|
+
return config2.verbose === true;
|
|
6904
|
+
}
|
|
6905
|
+
function getDebug() {
|
|
6906
|
+
if (process.env.BITFAB_DEBUG === "true" || process.env.BITFAB_DEBUG === "1") {
|
|
6907
|
+
return true;
|
|
6908
|
+
}
|
|
6909
|
+
const config2 = getConfigData();
|
|
6910
|
+
return config2.debug === true;
|
|
6911
|
+
}
|
|
6912
|
+
function getSessionLogConsentValue() {
|
|
6913
|
+
const config2 = getConfigData();
|
|
6914
|
+
if (config2.sessionLogConsent === true) {
|
|
6915
|
+
return true;
|
|
6916
|
+
}
|
|
6917
|
+
if (config2.sessionLogConsent === false) {
|
|
6918
|
+
return false;
|
|
6919
|
+
}
|
|
6920
|
+
return null;
|
|
6921
|
+
}
|
|
6922
|
+
function getConfig() {
|
|
6923
|
+
return {
|
|
6924
|
+
serviceUrl: getServiceUrl(),
|
|
6925
|
+
apiKey: getApiKey(),
|
|
6926
|
+
verbose: getVerbose(),
|
|
6927
|
+
debug: getDebug(),
|
|
6928
|
+
sessionLogConsent: getSessionLogConsentValue()
|
|
6929
|
+
};
|
|
6930
|
+
}
|
|
6931
|
+
function saveCredentials(apiKey) {
|
|
6932
|
+
fs3.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
6933
|
+
fs3.writeFileSync(GLOBAL_CREDENTIALS_FILE, `${JSON.stringify({ apiKey }, null, 2)}
|
|
6934
|
+
`);
|
|
6935
|
+
}
|
|
6936
|
+
function deleteCredentials() {
|
|
6937
|
+
try {
|
|
6938
|
+
fs3.unlinkSync(GLOBAL_CREDENTIALS_FILE);
|
|
6939
|
+
} catch {
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
function hasCredentials() {
|
|
6943
|
+
return getApiKey() !== null;
|
|
6944
|
+
}
|
|
6945
|
+
function getOrCreateInstallId() {
|
|
6946
|
+
const config2 = getConfigData();
|
|
6947
|
+
if (typeof config2.installId === "string" && config2.installId.length > 0) {
|
|
6948
|
+
return config2.installId;
|
|
6949
|
+
}
|
|
6950
|
+
const installId = crypto2.randomUUID();
|
|
6951
|
+
try {
|
|
6952
|
+
fs3.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
6953
|
+
fs3.writeFileSync(GLOBAL_CONFIG_FILE, `${JSON.stringify({ ...config2, installId }, null, 2)}
|
|
6954
|
+
`);
|
|
6955
|
+
} catch {
|
|
6956
|
+
}
|
|
6957
|
+
return installId;
|
|
6958
|
+
}
|
|
6959
|
+
|
|
6960
|
+
// ../bitfab-plugin-lib/dist/chatSessions/transcriptReader.js
|
|
6961
|
+
import fs4 from "fs";
|
|
6962
|
+
|
|
6963
|
+
// ../bitfab-plugin-lib/dist/clientHeaders.js
|
|
6964
|
+
import os5 from "os";
|
|
6965
|
+
function buildBitfabClientHeaders(pluginVersion, platform2) {
|
|
6966
|
+
const resolvedOptions = Intl.DateTimeFormat().resolvedOptions();
|
|
6967
|
+
return {
|
|
6968
|
+
"x-bitfab-client-name": platform2.cliBinary,
|
|
6969
|
+
"x-bitfab-client-display-name": platform2.displayName,
|
|
6970
|
+
"x-bitfab-client-version": pluginVersion,
|
|
6971
|
+
"x-bitfab-client-timezone": resolvedOptions.timeZone,
|
|
6972
|
+
"x-bitfab-client-locale": resolvedOptions.locale,
|
|
6973
|
+
"x-bitfab-client-os": os5.platform(),
|
|
6974
|
+
"x-bitfab-client-arch": os5.arch(),
|
|
6975
|
+
"x-bitfab-client-node-version": process.version,
|
|
6976
|
+
"x-bitfab-install-id": getOrCreateInstallId()
|
|
6977
|
+
};
|
|
6978
|
+
}
|
|
6979
|
+
function buildBitfabRequestHeaders(apiKey, pluginVersion, platform2) {
|
|
6980
|
+
return {
|
|
6981
|
+
"Content-Type": "application/json",
|
|
6982
|
+
Authorization: `Bearer ${apiKey}`,
|
|
6983
|
+
...buildBitfabClientHeaders(pluginVersion, platform2)
|
|
6984
|
+
};
|
|
6985
|
+
}
|
|
6986
|
+
|
|
6987
|
+
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
6988
|
+
import crypto3 from "crypto";
|
|
6989
|
+
|
|
6990
|
+
// ../bitfab-plugin-lib/dist/analytics.js
|
|
6991
|
+
async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
6992
|
+
const config2 = getConfig();
|
|
6993
|
+
const headers = buildBitfabRequestHeaders(apiKey, pluginVersion, platform2);
|
|
6994
|
+
try {
|
|
6995
|
+
const response = await fetch(`${config2.serviceUrl}/api/plugin/analytics/handoff`, {
|
|
6996
|
+
method: "POST",
|
|
6997
|
+
headers,
|
|
6998
|
+
body: JSON.stringify(body)
|
|
6999
|
+
});
|
|
7000
|
+
if (config2.debug && !response.ok) {
|
|
7001
|
+
console.error(`reportHandoff: server returned ${response.status} for ${body.flow}`);
|
|
7002
|
+
}
|
|
7003
|
+
} catch (err) {
|
|
7004
|
+
if (config2.debug) {
|
|
7005
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7006
|
+
console.error(`reportHandoff: request failed for ${body.flow}: ${message}`);
|
|
7007
|
+
}
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
6810
7010
|
|
|
6811
7011
|
// ../bitfab-plugin-lib/dist/browser.js
|
|
6812
7012
|
import { execSync, spawn } from "child_process";
|
|
6813
|
-
import
|
|
6814
|
-
import
|
|
7013
|
+
import fs6 from "fs";
|
|
7014
|
+
import os6 from "os";
|
|
6815
7015
|
function getChromiumBrowsers() {
|
|
6816
|
-
const platform2 =
|
|
7016
|
+
const platform2 = os6.platform();
|
|
6817
7017
|
if (platform2 === "darwin") {
|
|
6818
7018
|
return [
|
|
6819
7019
|
{
|
|
@@ -6901,7 +7101,7 @@ function getChromiumBrowsers() {
|
|
|
6901
7101
|
function findExistingBinary(paths) {
|
|
6902
7102
|
for (const candidate of paths) {
|
|
6903
7103
|
if (candidate.includes("/") || candidate.includes("\\")) {
|
|
6904
|
-
if (
|
|
7104
|
+
if (fs6.existsSync(candidate)) {
|
|
6905
7105
|
return candidate;
|
|
6906
7106
|
}
|
|
6907
7107
|
} else {
|
|
@@ -6915,10 +7115,10 @@ function findExistingBinary(paths) {
|
|
|
6915
7115
|
return null;
|
|
6916
7116
|
}
|
|
6917
7117
|
function getDefaultBrowserId() {
|
|
6918
|
-
const platform2 =
|
|
7118
|
+
const platform2 = os6.platform();
|
|
6919
7119
|
try {
|
|
6920
7120
|
if (platform2 === "darwin") {
|
|
6921
|
-
const plistPath = `${
|
|
7121
|
+
const plistPath = `${os6.homedir()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
|
|
6922
7122
|
const json2 = execSync(`plutil -convert json -o - "${plistPath}"`, {
|
|
6923
7123
|
stdio: ["ignore", "pipe", "ignore"],
|
|
6924
7124
|
encoding: "utf-8",
|
|
@@ -6959,7 +7159,7 @@ function matchChromiumBrowser(defaultId, browsers) {
|
|
|
6959
7159
|
var SCREEN_FRACTION = 0.95;
|
|
6960
7160
|
function getWorkArea() {
|
|
6961
7161
|
try {
|
|
6962
|
-
const platform2 =
|
|
7162
|
+
const platform2 = os6.platform();
|
|
6963
7163
|
if (platform2 === "darwin") {
|
|
6964
7164
|
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])'`, {
|
|
6965
7165
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -6982,641 +7182,101 @@ function getWorkArea() {
|
|
|
6982
7182
|
timeout: 3e3
|
|
6983
7183
|
});
|
|
6984
7184
|
const match = out.match(/(\d+)x(\d+)/);
|
|
6985
|
-
if (match) {
|
|
6986
|
-
return { x: 0, y: 0, width: Number(match[1]), height: Number(match[2]) };
|
|
6987
|
-
}
|
|
6988
|
-
} else if (platform2 === "win32") {
|
|
6989
|
-
const out = execSync(`powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $a=[System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea; $a.X.ToString() + ',' + $a.Y.ToString() + ',' + $a.Width.ToString() + ',' + $a.Height.ToString()"`, {
|
|
6990
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
6991
|
-
encoding: "utf-8",
|
|
6992
|
-
timeout: 3e3
|
|
6993
|
-
});
|
|
6994
|
-
const match = out.match(/(-?\d+),(-?\d+),(\d+),(\d+)/);
|
|
6995
|
-
if (match) {
|
|
6996
|
-
return {
|
|
6997
|
-
x: Number(match[1]),
|
|
6998
|
-
y: Number(match[2]),
|
|
6999
|
-
width: Number(match[3]),
|
|
7000
|
-
height: Number(match[4])
|
|
7001
|
-
};
|
|
7002
|
-
}
|
|
7003
|
-
}
|
|
7004
|
-
} catch {
|
|
7005
|
-
}
|
|
7006
|
-
return null;
|
|
7007
|
-
}
|
|
7008
|
-
function computeWindowArgs(area) {
|
|
7009
|
-
if (!area) {
|
|
7010
|
-
return [];
|
|
7011
|
-
}
|
|
7012
|
-
const w = Math.floor(area.width * SCREEN_FRACTION);
|
|
7013
|
-
const h = Math.floor(area.height * SCREEN_FRACTION);
|
|
7014
|
-
const x = area.x + Math.floor((area.width - w) / 2);
|
|
7015
|
-
const y = area.y + Math.floor((area.height - h) / 2);
|
|
7016
|
-
return [`--window-size=${w},${h}`, `--window-position=${x},${y}`];
|
|
7017
|
-
}
|
|
7018
|
-
function getWindowSizeArgs() {
|
|
7019
|
-
return computeWindowArgs(getWorkArea());
|
|
7020
|
-
}
|
|
7021
|
-
function spawnDetached(command, args) {
|
|
7022
|
-
const child = spawn(command, [...args], { detached: true, stdio: "ignore" });
|
|
7023
|
-
child.on("error", () => {
|
|
7024
|
-
});
|
|
7025
|
-
child.unref();
|
|
7026
|
-
}
|
|
7027
|
-
function openChromiumApp(binaryPath, url2, sizeArgs) {
|
|
7028
|
-
spawnDetached(binaryPath, [`--app=${url2}`, ...sizeArgs]);
|
|
7029
|
-
}
|
|
7030
|
-
function openOsDefault(url2) {
|
|
7031
|
-
const platform2 = os.platform();
|
|
7032
|
-
if (platform2 === "darwin") {
|
|
7033
|
-
spawnDetached("open", [url2]);
|
|
7034
|
-
} else if (platform2 === "win32") {
|
|
7035
|
-
spawnDetached("cmd", ["/c", "start", "", url2]);
|
|
7036
|
-
} else {
|
|
7037
|
-
spawnDetached("xdg-open", [url2]);
|
|
7038
|
-
}
|
|
7039
|
-
}
|
|
7040
|
-
function isCodexOnMacos() {
|
|
7041
|
-
if (process.platform !== "darwin") {
|
|
7042
|
-
return false;
|
|
7043
|
-
}
|
|
7044
|
-
return process.env.CODEX_SANDBOX === "seatbelt" || process.env.CODEX_CI === "1" || process.env.CODEX_THREAD_ID != null;
|
|
7045
|
-
}
|
|
7046
|
-
function shouldDisableChromelessWindows() {
|
|
7047
|
-
if (process.env.BITFAB_DISABLE_CHROME_APP_WINDOWS === "1") {
|
|
7048
|
-
return true;
|
|
7049
|
-
}
|
|
7050
|
-
return isCodexOnMacos();
|
|
7051
|
-
}
|
|
7052
|
-
function openChromelessWindow(url2) {
|
|
7053
|
-
if (shouldDisableChromelessWindows()) {
|
|
7054
|
-
openOsDefault(url2);
|
|
7055
|
-
return;
|
|
7056
|
-
}
|
|
7057
|
-
const browsers = getChromiumBrowsers();
|
|
7058
|
-
const defaultId = getDefaultBrowserId();
|
|
7059
|
-
const defaultChromium = matchChromiumBrowser(defaultId, browsers);
|
|
7060
|
-
const sizeArgs = getWindowSizeArgs();
|
|
7061
|
-
if (defaultChromium) {
|
|
7062
|
-
const binary = findExistingBinary(defaultChromium.binaryPaths);
|
|
7063
|
-
if (binary) {
|
|
7064
|
-
openChromiumApp(binary, url2, sizeArgs);
|
|
7065
|
-
return;
|
|
7066
|
-
}
|
|
7067
|
-
}
|
|
7068
|
-
if (defaultId !== null && defaultChromium === null) {
|
|
7069
|
-
openOsDefault(url2);
|
|
7070
|
-
return;
|
|
7071
|
-
}
|
|
7072
|
-
for (const browser of browsers) {
|
|
7073
|
-
const binary = findExistingBinary(browser.binaryPaths);
|
|
7074
|
-
if (binary) {
|
|
7075
|
-
openChromiumApp(binary, url2, sizeArgs);
|
|
7076
|
-
return;
|
|
7077
|
-
}
|
|
7078
|
-
}
|
|
7079
|
-
openOsDefault(url2);
|
|
7080
|
-
}
|
|
7081
|
-
|
|
7082
|
-
// ../bitfab-plugin-lib/dist/handoffTickets.js
|
|
7083
|
-
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
7084
|
-
async function createHandoffTicket(serviceUrl, kind, opts) {
|
|
7085
|
-
const headers = {
|
|
7086
|
-
"Content-Type": "application/json"
|
|
7087
|
-
};
|
|
7088
|
-
if (opts?.bearer) {
|
|
7089
|
-
headers.Authorization = `Bearer ${opts.bearer}`;
|
|
7090
|
-
}
|
|
7091
|
-
const res = await fetch(`${serviceUrl}/api/plugin/handoff-tickets`, {
|
|
7092
|
-
method: "POST",
|
|
7093
|
-
headers,
|
|
7094
|
-
body: JSON.stringify({
|
|
7095
|
-
kind,
|
|
7096
|
-
pkceChallenge: opts?.pkceChallenge,
|
|
7097
|
-
pkceMethod: opts?.pkceMethod,
|
|
7098
|
-
ttlSeconds: opts?.ttlSeconds
|
|
7099
|
-
})
|
|
7100
|
-
});
|
|
7101
|
-
if (!res.ok) {
|
|
7102
|
-
const body = await res.json().catch(() => ({}));
|
|
7103
|
-
throw new Error(body.error ?? `Failed to create handoff ticket (${res.status})`);
|
|
7104
|
-
}
|
|
7105
|
-
return await res.json();
|
|
7106
|
-
}
|
|
7107
|
-
async function pollHandoffTicket(serviceUrl, ticketId, opts) {
|
|
7108
|
-
const interval = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
7109
|
-
const deadline = Date.now() + opts.timeoutMs;
|
|
7110
|
-
while (Date.now() < deadline) {
|
|
7111
|
-
if (opts.abortSignal?.aborted) {
|
|
7112
|
-
return { expired: true };
|
|
7113
|
-
}
|
|
7114
|
-
const url2 = new URL(`${serviceUrl}/api/plugin/handoff-tickets/${ticketId}`);
|
|
7115
|
-
if (opts.verifier) {
|
|
7116
|
-
url2.searchParams.set("verifier", opts.verifier);
|
|
7117
|
-
}
|
|
7118
|
-
const headers = {};
|
|
7119
|
-
if (opts.bearer) {
|
|
7120
|
-
headers.Authorization = `Bearer ${opts.bearer}`;
|
|
7121
|
-
}
|
|
7122
|
-
try {
|
|
7123
|
-
const res = await fetch(url2.toString(), { headers });
|
|
7124
|
-
if (res.ok) {
|
|
7125
|
-
const body = await res.json();
|
|
7126
|
-
if (body.status === "completed" && body.data) {
|
|
7127
|
-
return { data: body.data };
|
|
7128
|
-
}
|
|
7129
|
-
if (body.status === "expired" || body.status === "cancelled") {
|
|
7130
|
-
return { expired: true };
|
|
7131
|
-
}
|
|
7132
|
-
}
|
|
7133
|
-
} catch {
|
|
7134
|
-
}
|
|
7135
|
-
await new Promise((resolve, reject) => {
|
|
7136
|
-
const timer = setTimeout(resolve, interval);
|
|
7137
|
-
opts.abortSignal?.addEventListener("abort", () => {
|
|
7138
|
-
clearTimeout(timer);
|
|
7139
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
7140
|
-
}, { once: true });
|
|
7141
|
-
}).catch(() => {
|
|
7142
|
-
});
|
|
7143
|
-
}
|
|
7144
|
-
return { expired: true };
|
|
7145
|
-
}
|
|
7146
|
-
|
|
7147
|
-
// ../bitfab-plugin-lib/dist/pkce.js
|
|
7148
|
-
import crypto from "crypto";
|
|
7149
|
-
function generatePkce() {
|
|
7150
|
-
const verifier = crypto.randomBytes(32).toString("base64url");
|
|
7151
|
-
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
|
|
7152
|
-
return { verifier, challenge };
|
|
7153
|
-
}
|
|
7154
|
-
|
|
7155
|
-
// ../bitfab-plugin-lib/dist/handoffTicketChannel.js
|
|
7156
|
-
async function startHandoffTicketChannel(opts) {
|
|
7157
|
-
const { verifier, challenge } = generatePkce();
|
|
7158
|
-
const abort = new AbortController();
|
|
7159
|
-
let done = false;
|
|
7160
|
-
let resolveFn = () => {
|
|
7161
|
-
};
|
|
7162
|
-
let rejectFn = () => {
|
|
7163
|
-
};
|
|
7164
|
-
const promise2 = new Promise((resolve, reject) => {
|
|
7165
|
-
resolveFn = (value) => {
|
|
7166
|
-
if (done) {
|
|
7167
|
-
return;
|
|
7168
|
-
}
|
|
7169
|
-
done = true;
|
|
7170
|
-
resolve(value);
|
|
7171
|
-
};
|
|
7172
|
-
rejectFn = (err) => {
|
|
7173
|
-
if (done) {
|
|
7174
|
-
return;
|
|
7175
|
-
}
|
|
7176
|
-
done = true;
|
|
7177
|
-
reject(err);
|
|
7178
|
-
};
|
|
7179
|
-
});
|
|
7180
|
-
let startError = null;
|
|
7181
|
-
let ticket = null;
|
|
7182
|
-
try {
|
|
7183
|
-
ticket = await createHandoffTicket(opts.serviceUrl, opts.kind, {
|
|
7184
|
-
pkceChallenge: challenge,
|
|
7185
|
-
pkceMethod: "S256",
|
|
7186
|
-
bearer: opts.bearer,
|
|
7187
|
-
ttlSeconds: opts.ttlSeconds
|
|
7188
|
-
});
|
|
7189
|
-
} catch (err) {
|
|
7190
|
-
startError = err instanceof Error ? err : new Error(String(err));
|
|
7191
|
-
}
|
|
7192
|
-
const available = ticket !== null;
|
|
7193
|
-
if (ticket) {
|
|
7194
|
-
pollHandoffTicket(opts.serviceUrl, ticket.id, {
|
|
7195
|
-
verifier,
|
|
7196
|
-
bearer: opts.bearer,
|
|
7197
|
-
timeoutMs: opts.timeoutMs,
|
|
7198
|
-
intervalMs: ticket.pollIntervalMs,
|
|
7199
|
-
abortSignal: abort.signal
|
|
7200
|
-
}).then((result) => {
|
|
7201
|
-
if ("expired" in result) {
|
|
7202
|
-
rejectFn(new Error("Handoff ticket expired before completion."));
|
|
7203
|
-
return;
|
|
7204
|
-
}
|
|
7205
|
-
resolveFn(result.data);
|
|
7206
|
-
}).catch((err) => {
|
|
7207
|
-
rejectFn(err instanceof Error ? err : new Error(String(err)));
|
|
7208
|
-
});
|
|
7209
|
-
} else {
|
|
7210
|
-
promise2.catch(() => {
|
|
7211
|
-
});
|
|
7212
|
-
}
|
|
7213
|
-
return {
|
|
7214
|
-
promise: promise2,
|
|
7215
|
-
get done() {
|
|
7216
|
-
return done;
|
|
7217
|
-
},
|
|
7218
|
-
available,
|
|
7219
|
-
startError,
|
|
7220
|
-
ticketId: ticket?.id ?? null,
|
|
7221
|
-
reject: rejectFn,
|
|
7222
|
-
close: () => {
|
|
7223
|
-
abort.abort();
|
|
7224
|
-
rejectFn(new Error("Handoff ticket channel closed."));
|
|
7225
|
-
}
|
|
7226
|
-
};
|
|
7227
|
-
}
|
|
7228
|
-
|
|
7229
|
-
// ../bitfab-plugin-lib/dist/loopback.js
|
|
7230
|
-
import http from "http";
|
|
7231
|
-
async function startLoopbackServer(port, routes) {
|
|
7232
|
-
let done = false;
|
|
7233
|
-
let resolveFn = () => {
|
|
7234
|
-
};
|
|
7235
|
-
let rejectFn = () => {
|
|
7236
|
-
};
|
|
7237
|
-
const promise2 = new Promise((resolve, reject) => {
|
|
7238
|
-
resolveFn = (value) => {
|
|
7239
|
-
if (done) {
|
|
7240
|
-
return;
|
|
7241
|
-
}
|
|
7242
|
-
done = true;
|
|
7243
|
-
resolve(value);
|
|
7244
|
-
};
|
|
7245
|
-
rejectFn = (err) => {
|
|
7246
|
-
if (done) {
|
|
7247
|
-
return;
|
|
7248
|
-
}
|
|
7249
|
-
done = true;
|
|
7250
|
-
reject(err);
|
|
7251
|
-
};
|
|
7252
|
-
});
|
|
7253
|
-
const server = http.createServer((req, res) => {
|
|
7254
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
7255
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
7256
|
-
res.setHeader("Access-Control-Allow-Private-Network", "true");
|
|
7257
|
-
if (req.method === "OPTIONS") {
|
|
7258
|
-
res.writeHead(204);
|
|
7259
|
-
res.end();
|
|
7260
|
-
return;
|
|
7261
|
-
}
|
|
7262
|
-
const url2 = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
7263
|
-
if (url2.pathname === "/health") {
|
|
7264
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
7265
|
-
res.end(JSON.stringify({ ok: true }));
|
|
7266
|
-
return;
|
|
7267
|
-
}
|
|
7268
|
-
const route = routes.find((r) => r.path === url2.pathname);
|
|
7269
|
-
if (route) {
|
|
7270
|
-
try {
|
|
7271
|
-
route.handle(url2.searchParams, res, resolveFn);
|
|
7272
|
-
} catch (err) {
|
|
7273
|
-
rejectFn(err);
|
|
7274
|
-
}
|
|
7275
|
-
return;
|
|
7276
|
-
}
|
|
7277
|
-
res.writeHead(404);
|
|
7278
|
-
res.end();
|
|
7279
|
-
});
|
|
7280
|
-
let bindError = null;
|
|
7281
|
-
try {
|
|
7282
|
-
await new Promise((resolve, reject) => {
|
|
7283
|
-
const onError = (err) => {
|
|
7284
|
-
server.removeListener("listening", onListening);
|
|
7285
|
-
reject(err);
|
|
7286
|
-
};
|
|
7287
|
-
const onListening = () => {
|
|
7288
|
-
server.removeListener("error", onError);
|
|
7289
|
-
resolve();
|
|
7290
|
-
};
|
|
7291
|
-
server.once("error", onError);
|
|
7292
|
-
server.once("listening", onListening);
|
|
7293
|
-
server.listen(port, "127.0.0.1");
|
|
7294
|
-
});
|
|
7295
|
-
} catch (err) {
|
|
7296
|
-
bindError = err;
|
|
7297
|
-
}
|
|
7298
|
-
const available = bindError === null;
|
|
7299
|
-
if (available) {
|
|
7300
|
-
server.on("error", (err) => {
|
|
7301
|
-
rejectFn(new Error(`Loopback server error: ${err.message}`));
|
|
7302
|
-
});
|
|
7303
|
-
} else {
|
|
7304
|
-
promise2.catch(() => {
|
|
7305
|
-
});
|
|
7306
|
-
}
|
|
7307
|
-
return {
|
|
7308
|
-
promise: promise2,
|
|
7309
|
-
get done() {
|
|
7310
|
-
return done;
|
|
7311
|
-
},
|
|
7312
|
-
available,
|
|
7313
|
-
bindError,
|
|
7314
|
-
resolve: resolveFn,
|
|
7315
|
-
reject: rejectFn,
|
|
7316
|
-
close: () => {
|
|
7317
|
-
if (available) {
|
|
7318
|
-
server.close();
|
|
7319
|
-
}
|
|
7320
|
-
}
|
|
7321
|
-
};
|
|
7322
|
-
}
|
|
7323
|
-
|
|
7324
|
-
// ../bitfab-plugin-lib/dist/browserHandoff.js
|
|
7325
|
-
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
7326
|
-
var DEFAULT_REDISPLAY_DELAY_MS = 4 * 1e3;
|
|
7327
|
-
var DEFAULT_HEARTBEAT_INTERVAL_MS = 30 * 1e3;
|
|
7328
|
-
function findOpenPort() {
|
|
7329
|
-
return new Promise((resolve, reject) => {
|
|
7330
|
-
const server = http2.createServer();
|
|
7331
|
-
server.on("error", (err) => {
|
|
7332
|
-
server.close(() => reject(err));
|
|
7333
|
-
});
|
|
7334
|
-
server.listen(0, "127.0.0.1", () => {
|
|
7335
|
-
const addr = server.address();
|
|
7336
|
-
if (addr && typeof addr === "object") {
|
|
7337
|
-
const port = addr.port;
|
|
7338
|
-
server.close(() => resolve(port));
|
|
7339
|
-
} else {
|
|
7340
|
-
reject(new Error("Could not find open port"));
|
|
7341
|
-
}
|
|
7342
|
-
});
|
|
7343
|
-
});
|
|
7344
|
-
}
|
|
7345
|
-
async function startBrowserHandoff(opts) {
|
|
7346
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
7347
|
-
const forceTicket = process.env.BITFAB_FORCE_TICKET === "1";
|
|
7348
|
-
let port = null;
|
|
7349
|
-
let portBindError = null;
|
|
7350
|
-
if (!forceTicket) {
|
|
7351
|
-
try {
|
|
7352
|
-
port = await findOpenPort();
|
|
7353
|
-
} catch (err) {
|
|
7354
|
-
portBindError = err;
|
|
7355
|
-
}
|
|
7356
|
-
}
|
|
7357
|
-
const loopbackUnavailable = {
|
|
7358
|
-
promise: new Promise(() => {
|
|
7359
|
-
}),
|
|
7360
|
-
get done() {
|
|
7361
|
-
return false;
|
|
7362
|
-
},
|
|
7363
|
-
available: false,
|
|
7364
|
-
bindError: portBindError ?? new Error("BITFAB_FORCE_TICKET=1 disabled loopback"),
|
|
7365
|
-
resolve: () => {
|
|
7366
|
-
},
|
|
7367
|
-
reject: () => {
|
|
7368
|
-
},
|
|
7369
|
-
close: () => {
|
|
7370
|
-
}
|
|
7371
|
-
};
|
|
7372
|
-
const [loopback, ticket] = await Promise.all([
|
|
7373
|
-
forceTicket || port === null ? Promise.resolve(loopbackUnavailable) : startLoopbackServer(port, opts.loopbackRoutes),
|
|
7374
|
-
startHandoffTicketChannel({
|
|
7375
|
-
serviceUrl: opts.serviceUrl,
|
|
7376
|
-
kind: opts.ticketKind,
|
|
7377
|
-
timeoutMs,
|
|
7378
|
-
ttlSeconds: opts.ticketTtlSeconds,
|
|
7379
|
-
bearer: opts.ticketBearer
|
|
7380
|
-
})
|
|
7381
|
-
]);
|
|
7382
|
-
const browserUrl = opts.buildUrl({
|
|
7383
|
-
port: loopback.available ? port : null,
|
|
7384
|
-
ticketId: ticket.ticketId
|
|
7385
|
-
});
|
|
7386
|
-
return { loopback, ticket, port, browserUrl, timeoutMs };
|
|
7387
|
-
}
|
|
7388
|
-
function attachBrowserHandoffKeepalive(opts) {
|
|
7389
|
-
const redisplayDelay = opts.redisplayDelayMs ?? DEFAULT_REDISPLAY_DELAY_MS;
|
|
7390
|
-
const heartbeatInterval = opts.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
7391
|
-
const startedAt = Date.now();
|
|
7392
|
-
const done = () => opts.loopback.done || opts.ticket.done;
|
|
7393
|
-
const redisplayTimer = setTimeout(() => {
|
|
7394
|
-
if (done()) {
|
|
7395
|
-
return;
|
|
7396
|
-
}
|
|
7397
|
-
console.error("");
|
|
7398
|
-
console.error("Still waiting for browser handoff. If the browser didn't open,");
|
|
7399
|
-
console.error("copy this URL into any browser \u2014 this session is still valid:");
|
|
7400
|
-
console.error("");
|
|
7401
|
-
console.error(` ${opts.browserUrl}`);
|
|
7402
|
-
console.error("");
|
|
7403
|
-
}, redisplayDelay);
|
|
7404
|
-
const heartbeat = process.stdout.isTTY ? null : setInterval(() => {
|
|
7405
|
-
if (done()) {
|
|
7406
|
-
return;
|
|
7407
|
-
}
|
|
7408
|
-
const elapsed = Math.round((Date.now() - startedAt) / 1e3);
|
|
7409
|
-
console.error(`[bitfab] waiting for browser handoff... ${elapsed}s elapsed, session still live`);
|
|
7410
|
-
}, heartbeatInterval);
|
|
7411
|
-
return () => {
|
|
7412
|
-
clearTimeout(redisplayTimer);
|
|
7413
|
-
if (heartbeat) {
|
|
7414
|
-
clearInterval(heartbeat);
|
|
7415
|
-
}
|
|
7416
|
-
};
|
|
7417
|
-
}
|
|
7418
|
-
|
|
7419
|
-
// ../bitfab-plugin-lib/dist/buildInfo.js
|
|
7420
|
-
import crypto2 from "crypto";
|
|
7421
|
-
import fs2 from "fs";
|
|
7422
|
-
import os2 from "os";
|
|
7423
|
-
import path from "path";
|
|
7424
|
-
import { fileURLToPath } from "url";
|
|
7425
|
-
|
|
7426
|
-
// ../bitfab-plugin-lib/dist/chatSessions/index.js
|
|
7427
|
-
import { execFileSync } from "child_process";
|
|
7428
|
-
import fs6 from "fs";
|
|
7429
|
-
import os5 from "os";
|
|
7430
|
-
import path4 from "path";
|
|
7431
|
-
|
|
7432
|
-
// ../bitfab-plugin-lib/dist/skills.js
|
|
7433
|
-
var BITFAB_SKILLS = ["setup", "assistant", "update"];
|
|
7434
|
-
|
|
7435
|
-
// ../bitfab-plugin-lib/dist/hooks/skillDetection.js
|
|
7436
|
-
var SKILL_PATTERN = new RegExp(`^\\s*\\/bitfab[:-](?<skill>${BITFAB_SKILLS.join("|")})(?=\\s|$)`, "i");
|
|
7437
|
-
|
|
7438
|
-
// ../bitfab-plugin-lib/dist/chatSessions/arming.js
|
|
7439
|
-
import fs3 from "fs";
|
|
7440
|
-
import os3 from "os";
|
|
7441
|
-
import path2 from "path";
|
|
7442
|
-
|
|
7443
|
-
// ../bitfab-plugin-lib/dist/config.js
|
|
7444
|
-
import crypto3 from "crypto";
|
|
7445
|
-
import fs4 from "fs";
|
|
7446
|
-
import os4 from "os";
|
|
7447
|
-
import path3 from "path";
|
|
7448
|
-
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
7449
|
-
var GLOBAL_CONFIG_DIR = path3.join(os4.homedir(), ".config", "bitfab");
|
|
7450
|
-
var GLOBAL_CONFIG_FILE = path3.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
7451
|
-
var GLOBAL_CREDENTIALS_FILE = path3.join(GLOBAL_CONFIG_DIR, "credentials.json");
|
|
7452
|
-
var PROJECT_CONFIG_RELATIVE = path3.join(".bitfab", "config.local.json");
|
|
7453
|
-
function readJsonFile(filePath) {
|
|
7454
|
-
try {
|
|
7455
|
-
const content = fs4.readFileSync(filePath, "utf-8");
|
|
7456
|
-
return JSON.parse(content);
|
|
7457
|
-
} catch {
|
|
7458
|
-
return null;
|
|
7459
|
-
}
|
|
7460
|
-
}
|
|
7461
|
-
function findProjectConfigPath(startDir = process.cwd()) {
|
|
7462
|
-
let dir = startDir;
|
|
7463
|
-
while (true) {
|
|
7464
|
-
const candidate = path3.join(dir, PROJECT_CONFIG_RELATIVE);
|
|
7465
|
-
if (fs4.existsSync(candidate)) {
|
|
7466
|
-
return candidate;
|
|
7467
|
-
}
|
|
7468
|
-
const parent = path3.dirname(dir);
|
|
7469
|
-
if (parent === dir) {
|
|
7470
|
-
return null;
|
|
7185
|
+
if (match) {
|
|
7186
|
+
return { x: 0, y: 0, width: Number(match[1]), height: Number(match[2]) };
|
|
7187
|
+
}
|
|
7188
|
+
} else if (platform2 === "win32") {
|
|
7189
|
+
const out = execSync(`powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $a=[System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea; $a.X.ToString() + ',' + $a.Y.ToString() + ',' + $a.Width.ToString() + ',' + $a.Height.ToString()"`, {
|
|
7190
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
7191
|
+
encoding: "utf-8",
|
|
7192
|
+
timeout: 3e3
|
|
7193
|
+
});
|
|
7194
|
+
const match = out.match(/(-?\d+),(-?\d+),(\d+),(\d+)/);
|
|
7195
|
+
if (match) {
|
|
7196
|
+
return {
|
|
7197
|
+
x: Number(match[1]),
|
|
7198
|
+
y: Number(match[2]),
|
|
7199
|
+
width: Number(match[3]),
|
|
7200
|
+
height: Number(match[4])
|
|
7201
|
+
};
|
|
7202
|
+
}
|
|
7471
7203
|
}
|
|
7472
|
-
|
|
7204
|
+
} catch {
|
|
7473
7205
|
}
|
|
7206
|
+
return null;
|
|
7474
7207
|
}
|
|
7475
|
-
function
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
return null;
|
|
7208
|
+
function computeWindowArgs(area) {
|
|
7209
|
+
if (!area) {
|
|
7210
|
+
return [];
|
|
7479
7211
|
}
|
|
7480
|
-
|
|
7212
|
+
const w = Math.floor(area.width * SCREEN_FRACTION);
|
|
7213
|
+
const h = Math.floor(area.height * SCREEN_FRACTION);
|
|
7214
|
+
const x = area.x + Math.floor((area.width - w) / 2);
|
|
7215
|
+
const y = area.y + Math.floor((area.height - h) / 2);
|
|
7216
|
+
return [`--window-size=${w},${h}`, `--window-position=${x},${y}`];
|
|
7481
7217
|
}
|
|
7482
|
-
function
|
|
7483
|
-
return
|
|
7218
|
+
function getWindowSizeArgs() {
|
|
7219
|
+
return computeWindowArgs(getWorkArea());
|
|
7484
7220
|
}
|
|
7485
|
-
function
|
|
7486
|
-
|
|
7221
|
+
function spawnDetached(command, args) {
|
|
7222
|
+
const child = spawn(command, [...args], { detached: true, stdio: "ignore" });
|
|
7223
|
+
child.on("error", () => {
|
|
7224
|
+
});
|
|
7225
|
+
child.unref();
|
|
7487
7226
|
}
|
|
7488
|
-
function
|
|
7489
|
-
|
|
7490
|
-
return process.env.BITFAB_SERVICE_URL;
|
|
7491
|
-
}
|
|
7492
|
-
const project = getProjectConfigData();
|
|
7493
|
-
if (project && typeof project.serviceUrl === "string") {
|
|
7494
|
-
return project.serviceUrl;
|
|
7495
|
-
}
|
|
7496
|
-
const config2 = getConfigData();
|
|
7497
|
-
return typeof config2.serviceUrl === "string" ? config2.serviceUrl : DEFAULT_SERVICE_URL;
|
|
7227
|
+
function openChromiumApp(binaryPath, url2, sizeArgs) {
|
|
7228
|
+
spawnDetached(binaryPath, [`--app=${url2}`, ...sizeArgs]);
|
|
7498
7229
|
}
|
|
7499
|
-
function
|
|
7500
|
-
|
|
7501
|
-
|
|
7230
|
+
function openOsDefault(url2) {
|
|
7231
|
+
const platform2 = os6.platform();
|
|
7232
|
+
if (platform2 === "darwin") {
|
|
7233
|
+
spawnDetached("open", [url2]);
|
|
7234
|
+
} else if (platform2 === "win32") {
|
|
7235
|
+
spawnDetached("cmd", ["/c", "start", "", url2]);
|
|
7236
|
+
} else {
|
|
7237
|
+
spawnDetached("xdg-open", [url2]);
|
|
7502
7238
|
}
|
|
7503
|
-
const creds = getCredentialsData();
|
|
7504
|
-
return typeof creds.apiKey === "string" ? creds.apiKey : null;
|
|
7505
7239
|
}
|
|
7506
|
-
function
|
|
7507
|
-
if (process.
|
|
7508
|
-
return true;
|
|
7509
|
-
}
|
|
7510
|
-
if (process.env.BITFAB_VERBOSE === "false" || process.env.BITFAB_VERBOSE === "0") {
|
|
7240
|
+
function isCodexOnMacos() {
|
|
7241
|
+
if (process.platform !== "darwin") {
|
|
7511
7242
|
return false;
|
|
7512
7243
|
}
|
|
7513
|
-
|
|
7514
|
-
return config2.verbose === true;
|
|
7515
|
-
}
|
|
7516
|
-
function getDebug() {
|
|
7517
|
-
if (process.env.BITFAB_DEBUG === "true" || process.env.BITFAB_DEBUG === "1") {
|
|
7518
|
-
return true;
|
|
7519
|
-
}
|
|
7520
|
-
const config2 = getConfigData();
|
|
7521
|
-
return config2.debug === true;
|
|
7244
|
+
return process.env.CODEX_SANDBOX === "seatbelt" || process.env.CODEX_CI === "1" || process.env.CODEX_THREAD_ID != null;
|
|
7522
7245
|
}
|
|
7523
|
-
function
|
|
7524
|
-
|
|
7525
|
-
if (config2.sessionLogConsent === true) {
|
|
7246
|
+
function shouldDisableChromelessWindows() {
|
|
7247
|
+
if (process.env.BITFAB_DISABLE_CHROME_APP_WINDOWS === "1") {
|
|
7526
7248
|
return true;
|
|
7527
7249
|
}
|
|
7528
|
-
|
|
7529
|
-
return false;
|
|
7530
|
-
}
|
|
7531
|
-
return null;
|
|
7532
|
-
}
|
|
7533
|
-
function getConfig() {
|
|
7534
|
-
return {
|
|
7535
|
-
serviceUrl: getServiceUrl(),
|
|
7536
|
-
apiKey: getApiKey(),
|
|
7537
|
-
verbose: getVerbose(),
|
|
7538
|
-
debug: getDebug(),
|
|
7539
|
-
sessionLogConsent: getSessionLogConsentValue()
|
|
7540
|
-
};
|
|
7541
|
-
}
|
|
7542
|
-
function saveCredentials(apiKey) {
|
|
7543
|
-
fs4.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
7544
|
-
fs4.writeFileSync(GLOBAL_CREDENTIALS_FILE, `${JSON.stringify({ apiKey }, null, 2)}
|
|
7545
|
-
`);
|
|
7250
|
+
return isCodexOnMacos();
|
|
7546
7251
|
}
|
|
7547
|
-
function
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7252
|
+
function openChromelessWindow(url2) {
|
|
7253
|
+
if (shouldDisableChromelessWindows()) {
|
|
7254
|
+
openOsDefault(url2);
|
|
7255
|
+
return;
|
|
7551
7256
|
}
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7257
|
+
const browsers = getChromiumBrowsers();
|
|
7258
|
+
const defaultId = getDefaultBrowserId();
|
|
7259
|
+
const defaultChromium = matchChromiumBrowser(defaultId, browsers);
|
|
7260
|
+
const sizeArgs = getWindowSizeArgs();
|
|
7261
|
+
if (defaultChromium) {
|
|
7262
|
+
const binary = findExistingBinary(defaultChromium.binaryPaths);
|
|
7263
|
+
if (binary) {
|
|
7264
|
+
openChromiumApp(binary, url2, sizeArgs);
|
|
7265
|
+
return;
|
|
7266
|
+
}
|
|
7560
7267
|
}
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
fs4.writeFileSync(GLOBAL_CONFIG_FILE, `${JSON.stringify({ ...config2, installId }, null, 2)}
|
|
7565
|
-
`);
|
|
7566
|
-
} catch {
|
|
7268
|
+
if (defaultId !== null && defaultChromium === null) {
|
|
7269
|
+
openOsDefault(url2);
|
|
7270
|
+
return;
|
|
7567
7271
|
}
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
// ../bitfab-plugin-lib/dist/clientHeaders.js
|
|
7575
|
-
import os6 from "os";
|
|
7576
|
-
function buildBitfabClientHeaders(pluginVersion, platform2) {
|
|
7577
|
-
const resolvedOptions = Intl.DateTimeFormat().resolvedOptions();
|
|
7578
|
-
return {
|
|
7579
|
-
"x-bitfab-client-name": platform2.cliBinary,
|
|
7580
|
-
"x-bitfab-client-display-name": platform2.displayName,
|
|
7581
|
-
"x-bitfab-client-version": pluginVersion,
|
|
7582
|
-
"x-bitfab-client-timezone": resolvedOptions.timeZone,
|
|
7583
|
-
"x-bitfab-client-locale": resolvedOptions.locale,
|
|
7584
|
-
"x-bitfab-client-os": os6.platform(),
|
|
7585
|
-
"x-bitfab-client-arch": os6.arch(),
|
|
7586
|
-
"x-bitfab-client-node-version": process.version,
|
|
7587
|
-
"x-bitfab-install-id": getOrCreateInstallId()
|
|
7588
|
-
};
|
|
7589
|
-
}
|
|
7590
|
-
function buildBitfabRequestHeaders(apiKey, pluginVersion, platform2) {
|
|
7591
|
-
return {
|
|
7592
|
-
"Content-Type": "application/json",
|
|
7593
|
-
Authorization: `Bearer ${apiKey}`,
|
|
7594
|
-
...buildBitfabClientHeaders(pluginVersion, platform2)
|
|
7595
|
-
};
|
|
7596
|
-
}
|
|
7597
|
-
|
|
7598
|
-
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
7599
|
-
import readline from "readline";
|
|
7600
|
-
|
|
7601
|
-
// ../bitfab-plugin-lib/dist/analytics.js
|
|
7602
|
-
async function reportHandoff(apiKey, pluginVersion, platform2, body) {
|
|
7603
|
-
const config2 = getConfig();
|
|
7604
|
-
const headers = buildBitfabRequestHeaders(apiKey, pluginVersion, platform2);
|
|
7605
|
-
try {
|
|
7606
|
-
const response = await fetch(`${config2.serviceUrl}/api/plugin/analytics/handoff`, {
|
|
7607
|
-
method: "POST",
|
|
7608
|
-
headers,
|
|
7609
|
-
body: JSON.stringify(body)
|
|
7610
|
-
});
|
|
7611
|
-
if (config2.debug && !response.ok) {
|
|
7612
|
-
console.error(`reportHandoff: server returned ${response.status} for ${body.flow}`);
|
|
7613
|
-
}
|
|
7614
|
-
} catch (err) {
|
|
7615
|
-
if (config2.debug) {
|
|
7616
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
7617
|
-
console.error(`reportHandoff: request failed for ${body.flow}: ${message}`);
|
|
7272
|
+
for (const browser of browsers) {
|
|
7273
|
+
const binary = findExistingBinary(browser.binaryPaths);
|
|
7274
|
+
if (binary) {
|
|
7275
|
+
openChromiumApp(binary, url2, sizeArgs);
|
|
7276
|
+
return;
|
|
7618
7277
|
}
|
|
7619
7278
|
}
|
|
7279
|
+
openOsDefault(url2);
|
|
7620
7280
|
}
|
|
7621
7281
|
|
|
7622
7282
|
// ../bitfab-plugin-lib/dist/frontmostApp.js
|
|
@@ -7766,16 +7426,7 @@ function recordFocus() {
|
|
|
7766
7426
|
|
|
7767
7427
|
// ../bitfab-plugin-lib/dist/commands/login.js
|
|
7768
7428
|
var LOGIN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
7769
|
-
var
|
|
7770
|
-
var CALLBACK_HTML = `<html>
|
|
7771
|
-
<body style="margin:0;background:#f8fafc;color:#0f172a;font-family:system-ui,-apple-system,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;overflow:hidden">
|
|
7772
|
-
<div style="text-align:center;max-width:400px">
|
|
7773
|
-
<h1 style="font-size:24px;font-weight:600;margin:0 0 16px 0">Bitfab</h1>
|
|
7774
|
-
<p style="margin:0;color:#059669">Authenticated! You can close this window.</p>
|
|
7775
|
-
<button onclick="window.close()" style="margin-top:16px;padding:8px 16px;background:#0f172a;color:white;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer">Close Window</button>
|
|
7776
|
-
</div>
|
|
7777
|
-
</body>
|
|
7778
|
-
</html>`;
|
|
7429
|
+
var POLL_INTERVAL_MS = 1500;
|
|
7779
7430
|
async function verifyToken(serviceUrl, token) {
|
|
7780
7431
|
try {
|
|
7781
7432
|
const res = await fetch(`${serviceUrl}/api/plugin/whoami`, {
|
|
@@ -7789,141 +7440,86 @@ async function verifyToken(serviceUrl, token) {
|
|
|
7789
7440
|
return null;
|
|
7790
7441
|
}
|
|
7791
7442
|
}
|
|
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
|
+
}
|
|
7792
7469
|
async function runLogin(platform2, pluginVersion, options) {
|
|
7793
7470
|
const exitOnComplete = options?.exitOnComplete ?? true;
|
|
7794
7471
|
const config2 = getConfig();
|
|
7795
7472
|
const restoreFocus = recordFocus();
|
|
7796
|
-
const
|
|
7797
|
-
const
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
timeoutMs: LOGIN_TIMEOUT_MS,
|
|
7801
|
-
buildUrl: ({ port, ticketId }) => {
|
|
7802
|
-
const urlParams = new URLSearchParams();
|
|
7803
|
-
if (port !== null) {
|
|
7804
|
-
urlParams.set("callbackPort", String(port));
|
|
7805
|
-
}
|
|
7806
|
-
if (ticketId !== null) {
|
|
7807
|
-
urlParams.set("ticket", ticketId);
|
|
7808
|
-
}
|
|
7809
|
-
const base = `${config2.serviceUrl}/plugin/auth/${platform2.authPath}`;
|
|
7810
|
-
const qs = urlParams.toString();
|
|
7811
|
-
return qs ? `${base}?${qs}` : base;
|
|
7812
|
-
},
|
|
7813
|
-
loopbackRoutes: [
|
|
7814
|
-
{
|
|
7815
|
-
path: "/callback",
|
|
7816
|
-
handle: (params, res, resolve) => {
|
|
7817
|
-
const token = params.get("token");
|
|
7818
|
-
if (token) {
|
|
7819
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
7820
|
-
res.end(CALLBACK_HTML);
|
|
7821
|
-
resolve({ token });
|
|
7822
|
-
} else {
|
|
7823
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
7824
|
-
res.end(JSON.stringify({ ok: false, error: "No token received" }));
|
|
7825
|
-
}
|
|
7826
|
-
}
|
|
7827
|
-
}
|
|
7828
|
-
]
|
|
7829
|
-
});
|
|
7830
|
-
console.log("\nOpening your browser to sign in...");
|
|
7473
|
+
const loginId = crypto3.randomUUID();
|
|
7474
|
+
const redirectUrl = `/studio/close?pluginLogin=true&loginId=${loginId}`;
|
|
7475
|
+
const browserUrl = `${config2.serviceUrl}/studio/sign-in?redirect_url=${encodeURIComponent(redirectUrl)}`;
|
|
7476
|
+
console.log("\nOpening Studio to sign in...");
|
|
7831
7477
|
console.log(` ${browserUrl}`);
|
|
7832
7478
|
console.log("");
|
|
7833
7479
|
console.log("(If the browser didn't open, visit the URL above manually.)");
|
|
7834
7480
|
openChromelessWindow(browserUrl);
|
|
7835
|
-
const
|
|
7836
|
-
loopback,
|
|
7837
|
-
ticket,
|
|
7838
|
-
browserUrl
|
|
7839
|
-
});
|
|
7840
|
-
let resolvePaste = () => {
|
|
7841
|
-
};
|
|
7842
|
-
let pasteDone = false;
|
|
7843
|
-
const pastePromise = new Promise((resolve) => {
|
|
7844
|
-
resolvePaste = (value) => {
|
|
7845
|
-
if (pasteDone) {
|
|
7846
|
-
return;
|
|
7847
|
-
}
|
|
7848
|
-
pasteDone = true;
|
|
7849
|
-
resolve(value);
|
|
7850
|
-
};
|
|
7851
|
-
});
|
|
7852
|
-
const rl = interactive ? readline.createInterface({
|
|
7853
|
-
input: process.stdin,
|
|
7854
|
-
output: process.stdout
|
|
7855
|
-
}) : null;
|
|
7856
|
-
const hintTimer = interactive ? setTimeout(() => {
|
|
7857
|
-
if (loopback.done || ticket.done || pasteDone) {
|
|
7858
|
-
return;
|
|
7859
|
-
}
|
|
7860
|
-
console.log("");
|
|
7861
|
-
console.log("Having trouble? If your browser shows a token, paste it here and press Enter:");
|
|
7862
|
-
}, PASTE_HINT_DELAY_MS) : null;
|
|
7863
|
-
rl?.on("line", async (line) => {
|
|
7864
|
-
if (loopback.done || ticket.done || pasteDone) {
|
|
7865
|
-
return;
|
|
7866
|
-
}
|
|
7867
|
-
const trimmed = line.trim();
|
|
7868
|
-
if (!trimmed) {
|
|
7869
|
-
return;
|
|
7870
|
-
}
|
|
7871
|
-
process.stdout.write("Verifying token... ");
|
|
7872
|
-
const who = await verifyToken(config2.serviceUrl, trimmed);
|
|
7873
|
-
if (who) {
|
|
7874
|
-
console.log("ok.");
|
|
7875
|
-
resolvePaste({ token: trimmed });
|
|
7876
|
-
} else {
|
|
7877
|
-
console.log("invalid token. Try again or complete the browser flow.");
|
|
7878
|
-
}
|
|
7879
|
-
});
|
|
7481
|
+
const abortController = new AbortController();
|
|
7880
7482
|
let loginTimer;
|
|
7881
7483
|
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
7882
|
-
loginTimer = setTimeout(() =>
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7484
|
+
loginTimer = setTimeout(() => {
|
|
7485
|
+
abortController.abort();
|
|
7486
|
+
reject(new Error("Authentication timed out after 10 minutes. Run login again."));
|
|
7487
|
+
}, LOGIN_TIMEOUT_MS);
|
|
7488
|
+
});
|
|
7489
|
+
const keepaliveTimer = setInterval(() => {
|
|
7490
|
+
process.stdout.write("");
|
|
7491
|
+
}, 3e4);
|
|
7492
|
+
try {
|
|
7493
|
+
const token = await Promise.race([
|
|
7494
|
+
pollLoginStatus(config2.serviceUrl, loginId, abortController.signal),
|
|
7495
|
+
timeoutPromise
|
|
7496
|
+
]);
|
|
7889
7497
|
if (loginTimer) {
|
|
7890
7498
|
clearTimeout(loginTimer);
|
|
7891
7499
|
}
|
|
7892
|
-
|
|
7893
|
-
ticket.close();
|
|
7894
|
-
rl?.close();
|
|
7895
|
-
}
|
|
7896
|
-
const racers = [];
|
|
7897
|
-
if (loopback.available) {
|
|
7898
|
-
racers.push(loopback.promise.then((v) => ({ token: v.token, wonBy: "loopback" })));
|
|
7899
|
-
}
|
|
7900
|
-
if (ticket.available) {
|
|
7901
|
-
racers.push(ticket.promise.then((v) => ({ token: v.token, wonBy: "ticket" })));
|
|
7902
|
-
}
|
|
7903
|
-
if (interactive) {
|
|
7904
|
-
racers.push(pastePromise.then((v) => ({ token: v.token, wonBy: "paste" })));
|
|
7905
|
-
}
|
|
7906
|
-
try {
|
|
7907
|
-
const { token, wonBy } = await Promise.race([...racers, timeoutPromise]);
|
|
7908
|
-
cleanup();
|
|
7500
|
+
clearInterval(keepaliveTimer);
|
|
7909
7501
|
saveCredentials(token);
|
|
7910
7502
|
const who = await verifyToken(config2.serviceUrl, token);
|
|
7911
7503
|
const greeting = who?.user.email ? `Logged in as ${who.user.email}.` : "Authentication successful.";
|
|
7912
7504
|
console.log(`
|
|
7913
7505
|
${greeting}`);
|
|
7914
|
-
console.log(
|
|
7506
|
+
console.log("Bitfab MCP tools are now active. (via studio)");
|
|
7915
7507
|
await reportHandoff(token, pluginVersion, platform2, {
|
|
7916
7508
|
flow: "auth.login",
|
|
7917
|
-
wonBy,
|
|
7918
|
-
loopbackAvailable:
|
|
7919
|
-
ticketAvailable:
|
|
7509
|
+
wonBy: "studio",
|
|
7510
|
+
loopbackAvailable: false,
|
|
7511
|
+
ticketAvailable: false
|
|
7920
7512
|
});
|
|
7921
7513
|
restoreFocus();
|
|
7922
7514
|
if (exitOnComplete) {
|
|
7923
7515
|
process.exit(0);
|
|
7924
7516
|
}
|
|
7925
7517
|
} catch (err) {
|
|
7926
|
-
|
|
7518
|
+
if (loginTimer) {
|
|
7519
|
+
clearTimeout(loginTimer);
|
|
7520
|
+
}
|
|
7521
|
+
clearInterval(keepaliveTimer);
|
|
7522
|
+
abortController.abort();
|
|
7927
7523
|
if (exitOnComplete) {
|
|
7928
7524
|
console.error(`
|
|
7929
7525
|
${err.message}`);
|
|
@@ -7953,6 +7549,9 @@ import fs7 from "fs";
|
|
|
7953
7549
|
import os8 from "os";
|
|
7954
7550
|
import path5 from "path";
|
|
7955
7551
|
|
|
7552
|
+
// ../bitfab-plugin-lib/dist/withStudioSession.js
|
|
7553
|
+
import crypto6 from "crypto";
|
|
7554
|
+
|
|
7956
7555
|
// ../bitfab-plugin-lib/dist/commands/openTracePlan.js
|
|
7957
7556
|
var OPEN_TRACE_PLAN_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
7958
7557
|
|
|
@@ -21753,21 +21352,12 @@ var inputFileSchema = external_exports.object({
|
|
|
21753
21352
|
verdicts: external_exports.array(external_exports.unknown()).min(1)
|
|
21754
21353
|
});
|
|
21755
21354
|
|
|
21756
|
-
// ../bitfab-plugin-lib/dist/commands/startDataset.js
|
|
21757
|
-
var START_DATASET_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
21758
|
-
|
|
21759
|
-
// ../bitfab-plugin-lib/dist/commands/startTemplatePreview.js
|
|
21760
|
-
import { randomUUID } from "crypto";
|
|
21761
|
-
|
|
21762
21355
|
// ../bitfab-plugin-lib/dist/activePreviewSession.js
|
|
21763
21356
|
import fs8 from "fs";
|
|
21764
21357
|
import os9 from "os";
|
|
21765
21358
|
import path6 from "path";
|
|
21766
21359
|
var FILE_PATH = path6.join(os9.homedir(), ".config", "bitfab", "active-preview-session.json");
|
|
21767
21360
|
|
|
21768
|
-
// ../bitfab-plugin-lib/dist/commands/startTemplatePreview.js
|
|
21769
|
-
var START_TEMPLATE_PREVIEW_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
|
|
21770
|
-
|
|
21771
21361
|
// ../bitfab-plugin-lib/dist/updates.js
|
|
21772
21362
|
import fs9 from "fs";
|
|
21773
21363
|
import os10 from "os";
|
|
@@ -21785,7 +21375,7 @@ import fs10 from "fs";
|
|
|
21785
21375
|
import path8 from "path";
|
|
21786
21376
|
|
|
21787
21377
|
// ../bitfab-plugin-lib/dist/commands/waitForTrace.js
|
|
21788
|
-
var
|
|
21378
|
+
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
21789
21379
|
|
|
21790
21380
|
// ../bitfab-plugin-lib/dist/hooks/captureHook.js
|
|
21791
21381
|
import fs12 from "fs";
|
|
@@ -27212,6 +26802,10 @@ function runClaudeInstall() {
|
|
|
27212
26802
|
]);
|
|
27213
26803
|
if (installOut.includes("already")) {
|
|
27214
26804
|
s.stop("Plugin already installed");
|
|
26805
|
+
if (isPluginDisabled()) {
|
|
26806
|
+
runClaude(["plugin", "enable", PLUGIN_KEY]);
|
|
26807
|
+
p.log.step("Plugin enabled");
|
|
26808
|
+
}
|
|
27215
26809
|
} else {
|
|
27216
26810
|
s.stop("Plugin installed");
|
|
27217
26811
|
}
|
|
@@ -27246,6 +26840,13 @@ function runClaudeUpdate(args, skipPermissions) {
|
|
|
27246
26840
|
...SHELL_OPTS
|
|
27247
26841
|
});
|
|
27248
26842
|
}
|
|
26843
|
+
function isPluginDisabled() {
|
|
26844
|
+
const listOut = runClaude(["plugin", "list"]);
|
|
26845
|
+
const pattern = new RegExp(
|
|
26846
|
+
`${PLUGIN_KEY.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n.*\\n.*\\n.*disabled`
|
|
26847
|
+
);
|
|
26848
|
+
return pattern.test(listOut);
|
|
26849
|
+
}
|
|
27249
26850
|
function runClaude(args) {
|
|
27250
26851
|
const result = spawnSync("claude", [...args], {
|
|
27251
26852
|
stdio: "pipe",
|
|
@@ -27557,6 +27158,20 @@ async function pickEditor(detected) {
|
|
|
27557
27158
|
}
|
|
27558
27159
|
return choice;
|
|
27559
27160
|
}
|
|
27161
|
+
async function resolveSkipPermissions(value) {
|
|
27162
|
+
if (value !== void 0) {
|
|
27163
|
+
return value;
|
|
27164
|
+
}
|
|
27165
|
+
const answer = await p4.confirm({
|
|
27166
|
+
message: "Skip permission prompts? (runs the agent autonomously)",
|
|
27167
|
+
initialValue: false
|
|
27168
|
+
});
|
|
27169
|
+
if (p4.isCancel(answer)) {
|
|
27170
|
+
p4.cancel("Cancelled.");
|
|
27171
|
+
process.exit(0);
|
|
27172
|
+
}
|
|
27173
|
+
return answer;
|
|
27174
|
+
}
|
|
27560
27175
|
async function resolveEditor({ editor }) {
|
|
27561
27176
|
const detected = detectInstalledEditors();
|
|
27562
27177
|
if (detected.length === 0) {
|
|
@@ -27591,15 +27206,18 @@ async function runInstall(opts) {
|
|
|
27591
27206
|
}
|
|
27592
27207
|
async function runSetup(opts) {
|
|
27593
27208
|
const chosen = await resolveEditor(opts);
|
|
27594
|
-
|
|
27209
|
+
const skip = await resolveSkipPermissions(opts.skipPermissions);
|
|
27210
|
+
SETUP_FN[chosen](skip);
|
|
27595
27211
|
}
|
|
27596
27212
|
async function runAssistant(opts, args) {
|
|
27597
27213
|
const chosen = await resolveEditor(opts);
|
|
27598
|
-
|
|
27214
|
+
const skip = await resolveSkipPermissions(opts.skipPermissions);
|
|
27215
|
+
ASSISTANT_FN[chosen](args, skip);
|
|
27599
27216
|
}
|
|
27600
27217
|
async function runUpdate2(opts, args) {
|
|
27601
27218
|
const chosen = await resolveEditor(opts);
|
|
27602
|
-
|
|
27219
|
+
const skip = await resolveSkipPermissions(opts.skipPermissions);
|
|
27220
|
+
UPDATE_FN[chosen](args, skip);
|
|
27603
27221
|
}
|
|
27604
27222
|
async function runInit(opts) {
|
|
27605
27223
|
p4.intro("bitfab");
|
|
@@ -27613,7 +27231,8 @@ async function runInit(opts) {
|
|
|
27613
27231
|
p4.log.step("Signing in to Bitfab");
|
|
27614
27232
|
await runLoginCommand({ exitOnComplete: false });
|
|
27615
27233
|
}
|
|
27616
|
-
|
|
27234
|
+
const skip = await resolveSkipPermissions(opts.skipPermissions);
|
|
27235
|
+
SETUP_FN[chosen](skip);
|
|
27617
27236
|
const detected = detectInstalledEditors();
|
|
27618
27237
|
const others = detected.filter((e) => e !== chosen);
|
|
27619
27238
|
if (others.length > 0) {
|
|
@@ -27663,7 +27282,8 @@ Commands:
|
|
|
27663
27282
|
|
|
27664
27283
|
Options:
|
|
27665
27284
|
--editor, -e <name> Target editor: claude, codex, cursor
|
|
27666
|
-
--skip-permissions Skip permission prompts
|
|
27285
|
+
--skip-permissions Skip permission prompts (runs the agent autonomously)
|
|
27286
|
+
--no-skip-permissions Keep permission prompts (default, skips the interactive question)
|
|
27667
27287
|
|
|
27668
27288
|
Examples:
|
|
27669
27289
|
bitfab init Full setup (detect editor, install, login, setup)
|
|
@@ -27675,7 +27295,7 @@ Examples:
|
|
|
27675
27295
|
function parseArgs(argv) {
|
|
27676
27296
|
const [command, ...tail] = argv;
|
|
27677
27297
|
let editor;
|
|
27678
|
-
let skipPermissions
|
|
27298
|
+
let skipPermissions;
|
|
27679
27299
|
const rest = [];
|
|
27680
27300
|
for (let i = 0; i < tail.length; i++) {
|
|
27681
27301
|
const arg = tail[i];
|
|
@@ -27690,6 +27310,8 @@ function parseArgs(argv) {
|
|
|
27690
27310
|
editor = arg.slice("--editor=".length);
|
|
27691
27311
|
} else if (arg === "--skip-permissions") {
|
|
27692
27312
|
skipPermissions = true;
|
|
27313
|
+
} else if (arg === "--no-skip-permissions") {
|
|
27314
|
+
skipPermissions = false;
|
|
27693
27315
|
} else if (arg !== void 0) {
|
|
27694
27316
|
rest.push(arg);
|
|
27695
27317
|
}
|
|
@@ -27742,3 +27364,6 @@ main().catch((err) => {
|
|
|
27742
27364
|
cancel2(message);
|
|
27743
27365
|
process.exit(1);
|
|
27744
27366
|
});
|
|
27367
|
+
export {
|
|
27368
|
+
parseArgs
|
|
27369
|
+
};
|