bitfab-cli 0.2.5 → 0.2.9

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.
Files changed (2) hide show
  1. package/dist/index.js +427 -757
  2. 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 http3 = (
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: http3.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: http3,
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/browserHandoff.js
6809
- import http2 from "http";
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 fs from "fs";
6814
- import os from "os";
7013
+ import fs6 from "fs";
7014
+ import os6 from "os";
6815
7015
  function getChromiumBrowsers() {
6816
- const platform2 = os.platform();
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 (fs.existsSync(candidate)) {
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 = os.platform();
7118
+ const platform2 = os6.platform();
6919
7119
  try {
6920
7120
  if (platform2 === "darwin") {
6921
- const plistPath = `${os.homedir()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
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 = os.platform();
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"],
@@ -7006,617 +7206,77 @@ function getWorkArea() {
7006
7206
  return null;
7007
7207
  }
7008
7208
  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;
7471
- }
7472
- dir = parent;
7473
- }
7474
- }
7475
- function getProjectConfigData() {
7476
- const filePath = findProjectConfigPath();
7477
- if (!filePath) {
7478
- return null;
7209
+ if (!area) {
7210
+ return [];
7479
7211
  }
7480
- return readJsonFile(filePath);
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 getConfigData() {
7483
- return readJsonFile(GLOBAL_CONFIG_FILE) ?? {};
7218
+ function getWindowSizeArgs() {
7219
+ return computeWindowArgs(getWorkArea());
7484
7220
  }
7485
- function getCredentialsData() {
7486
- return readJsonFile(GLOBAL_CREDENTIALS_FILE) ?? {};
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 getServiceUrl() {
7489
- if (process.env.BITFAB_SERVICE_URL) {
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 getApiKey() {
7500
- if (process.env.BITFAB_API_KEY) {
7501
- return process.env.BITFAB_API_KEY;
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 getVerbose() {
7507
- if (process.env.BITFAB_VERBOSE === "true" || process.env.BITFAB_VERBOSE === "1") {
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
- const config2 = getConfigData();
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 getSessionLogConsentValue() {
7524
- const config2 = getConfigData();
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
- if (config2.sessionLogConsent === false) {
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 deleteCredentials() {
7548
- try {
7549
- fs4.unlinkSync(GLOBAL_CREDENTIALS_FILE);
7550
- } catch {
7252
+ function openChromelessWindow(url2) {
7253
+ if (shouldDisableChromelessWindows()) {
7254
+ openOsDefault(url2);
7255
+ return;
7551
7256
  }
7552
- }
7553
- function hasCredentials() {
7554
- return getApiKey() !== null;
7555
- }
7556
- function getOrCreateInstallId() {
7557
- const config2 = getConfigData();
7558
- if (typeof config2.installId === "string" && config2.installId.length > 0) {
7559
- return config2.installId;
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
- const installId = crypto3.randomUUID();
7562
- try {
7563
- fs4.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
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
- return installId;
7569
- }
7570
-
7571
- // ../bitfab-plugin-lib/dist/chatSessions/transcriptReader.js
7572
- import fs5 from "fs";
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 PASTE_HINT_DELAY_MS = 8 * 1e3;
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 interactive = process.stdin.isTTY === true;
7797
- const { loopback, ticket, browserUrl } = await startBrowserHandoff({
7798
- serviceUrl: config2.serviceUrl,
7799
- ticketKind: "auth.login",
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 stopKeepalive = attachBrowserHandoffKeepalive({
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(() => reject(new Error("Authentication timed out after 10 minutes. Run login again.")), LOGIN_TIMEOUT_MS);
7883
- });
7884
- function cleanup() {
7885
- stopKeepalive();
7886
- if (hintTimer) {
7887
- clearTimeout(hintTimer);
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
- loopback.close();
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(`Bitfab MCP tools are now active. (via ${wonBy})`);
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: loopback.available,
7919
- ticketAvailable: ticket.available
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
- cleanup();
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 DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
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
  }
@@ -27237,14 +26831,36 @@ function runClaudeAssistant(args, skipPermissions) {
27237
26831
  });
27238
26832
  }
27239
26833
  function runClaudeUpdate(args, skipPermissions) {
27240
- const cmd = [UPDATE_COMMAND, ...args].join(" ");
27241
- p.log.info(`Launching ${cmd}...
26834
+ const mode = args[0] === "plugin" || args[0] === "sdk" ? args[0] : "all";
26835
+ if (mode === "plugin" || mode === "all") {
26836
+ const s = p.spinner();
26837
+ s.start("Refreshing bitfab marketplace");
26838
+ runClaude(["plugin", "marketplace", "update", MARKETPLACE]);
26839
+ s.stop("Marketplace refreshed");
26840
+ s.start("Updating bitfab plugin");
26841
+ const out = runClaude(["plugin", "update", PLUGIN_KEY, "--scope", "user"]);
26842
+ if (out.includes("already") || out.includes("up to date")) {
26843
+ s.stop("Plugin already up to date");
26844
+ } else {
26845
+ s.stop("Plugin updated");
26846
+ }
26847
+ }
26848
+ if (mode === "sdk" || mode === "all") {
26849
+ p.log.info(`Launching ${UPDATE_COMMAND} sdk...
27242
26850
  `);
27243
- const flags = skipPermissions ? ["--dangerously-skip-permissions"] : [];
27244
- spawnSync("claude", [...flags, UPDATE_COMMAND, ...args], {
27245
- stdio: "inherit",
27246
- ...SHELL_OPTS
27247
- });
26851
+ const flags = skipPermissions ? ["--dangerously-skip-permissions"] : [];
26852
+ spawnSync("claude", [...flags, UPDATE_COMMAND, "sdk"], {
26853
+ stdio: "inherit",
26854
+ ...SHELL_OPTS
26855
+ });
26856
+ }
26857
+ }
26858
+ function isPluginDisabled() {
26859
+ const listOut = runClaude(["plugin", "list"]);
26860
+ const pattern = new RegExp(
26861
+ `${PLUGIN_KEY.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n.*\\n.*\\n.*disabled`
26862
+ );
26863
+ return pattern.test(listOut);
27248
26864
  }
27249
26865
  function runClaude(args) {
27250
26866
  const result = spawnSync("claude", [...args], {
@@ -27342,14 +26958,37 @@ function runCodexAssistant(args, skipPermissions) {
27342
26958
  });
27343
26959
  }
27344
26960
  function runCodexUpdate(args, skipPermissions) {
27345
- const cmd = [UPDATE_COMMAND2, ...args].join(" ");
27346
- p2.log.info(`Launching ${cmd}...
26961
+ const mode = args[0] === "plugin" || args[0] === "sdk" ? args[0] : "all";
26962
+ if (mode === "plugin" || mode === "all") {
26963
+ const s = p2.spinner();
26964
+ s.start("Upgrading bitfab marketplace plugin");
26965
+ const result = spawnSync2(
26966
+ "codex",
26967
+ ["plugin", "marketplace", "upgrade", "bitfab"],
26968
+ { stdio: "pipe", ...SHELL_OPTS2 }
26969
+ );
26970
+ if (result.status !== 0) {
26971
+ s.stop("Marketplace upgrade failed");
26972
+ throw new Error(
26973
+ `codex plugin marketplace upgrade failed${result.status === null ? "" : ` with status ${result.status}`}`
26974
+ );
26975
+ }
26976
+ const out = (result.stdout?.toString() ?? "") + (result.stderr?.toString() ?? "");
26977
+ if (out.includes("already") || out.includes("up to date")) {
26978
+ s.stop("Plugin already up to date");
26979
+ } else {
26980
+ s.stop("Plugin updated");
26981
+ }
26982
+ }
26983
+ if (mode === "sdk" || mode === "all") {
26984
+ p2.log.info(`Launching ${UPDATE_COMMAND2} sdk...
27347
26985
  `);
27348
- const flags = skipPermissions ? ["--full-auto"] : [];
27349
- spawnSync2("codex", [...flags, UPDATE_COMMAND2, ...args], {
27350
- stdio: "inherit",
27351
- ...SHELL_OPTS2
27352
- });
26986
+ const flags = skipPermissions ? ["--full-auto"] : [];
26987
+ spawnSync2("codex", [...flags, UPDATE_COMMAND2, "sdk"], {
26988
+ stdio: "inherit",
26989
+ ...SHELL_OPTS2
26990
+ });
26991
+ }
27353
26992
  }
27354
26993
  function enableCodexPlugin(configPath) {
27355
26994
  let content = "";
@@ -27437,8 +27076,15 @@ function runCursorUpdate(args, skipPermissions) {
27437
27076
  if (skipPermissions) {
27438
27077
  p3.log.warn("--skip-permissions is not supported for Cursor");
27439
27078
  }
27440
- const cmd = [UPDATE_COMMAND3, ...args].join(" ");
27441
- p3.log.info(`To update, run ${cmd} in Cursor.`);
27079
+ const mode = args[0] === "plugin" || args[0] === "sdk" ? args[0] : "all";
27080
+ const lines = [];
27081
+ if (mode === "plugin" || mode === "all") {
27082
+ lines.push(`Run ${UPDATE_COMMAND3} plugin (updates the plugin)`);
27083
+ }
27084
+ if (mode === "sdk" || mode === "all") {
27085
+ lines.push(`Run ${UPDATE_COMMAND3} sdk (updates installed SDKs)`);
27086
+ }
27087
+ p3.note(lines.join("\n"), "Update Bitfab in Cursor");
27442
27088
  }
27443
27089
  function copyToClipboard(text) {
27444
27090
  const cmd = clipboardCommand();
@@ -27557,6 +27203,20 @@ async function pickEditor(detected) {
27557
27203
  }
27558
27204
  return choice;
27559
27205
  }
27206
+ async function resolveSkipPermissions(value) {
27207
+ if (value !== void 0) {
27208
+ return value;
27209
+ }
27210
+ const answer = await p4.confirm({
27211
+ message: "Skip permission prompts? (runs the agent autonomously)",
27212
+ initialValue: false
27213
+ });
27214
+ if (p4.isCancel(answer)) {
27215
+ p4.cancel("Cancelled.");
27216
+ process.exit(0);
27217
+ }
27218
+ return answer;
27219
+ }
27560
27220
  async function resolveEditor({ editor }) {
27561
27221
  const detected = detectInstalledEditors();
27562
27222
  if (detected.length === 0) {
@@ -27591,15 +27251,18 @@ async function runInstall(opts) {
27591
27251
  }
27592
27252
  async function runSetup(opts) {
27593
27253
  const chosen = await resolveEditor(opts);
27594
- SETUP_FN[chosen](opts.skipPermissions);
27254
+ const skip = await resolveSkipPermissions(opts.skipPermissions);
27255
+ SETUP_FN[chosen](skip);
27595
27256
  }
27596
27257
  async function runAssistant(opts, args) {
27597
27258
  const chosen = await resolveEditor(opts);
27598
- ASSISTANT_FN[chosen](args, opts.skipPermissions);
27259
+ const skip = await resolveSkipPermissions(opts.skipPermissions);
27260
+ ASSISTANT_FN[chosen](args, skip);
27599
27261
  }
27600
27262
  async function runUpdate2(opts, args) {
27601
27263
  const chosen = await resolveEditor(opts);
27602
- UPDATE_FN[chosen](args, opts.skipPermissions);
27264
+ const skip = await resolveSkipPermissions(opts.skipPermissions);
27265
+ UPDATE_FN[chosen](args, skip);
27603
27266
  }
27604
27267
  async function runInit(opts) {
27605
27268
  p4.intro("bitfab");
@@ -27613,7 +27276,8 @@ async function runInit(opts) {
27613
27276
  p4.log.step("Signing in to Bitfab");
27614
27277
  await runLoginCommand({ exitOnComplete: false });
27615
27278
  }
27616
- SETUP_FN[chosen](opts.skipPermissions);
27279
+ const skip = await resolveSkipPermissions(opts.skipPermissions);
27280
+ SETUP_FN[chosen](skip);
27617
27281
  const detected = detectInstalledEditors();
27618
27282
  const others = detected.filter((e) => e !== chosen);
27619
27283
  if (others.length > 0) {
@@ -27658,12 +27322,13 @@ Commands:
27658
27322
  logout Remove stored credentials
27659
27323
  setup [--editor <name>] Launch /bitfab:setup in the editor
27660
27324
  assistant [--editor <name>] [args] Launch /bitfab:assistant in the editor
27661
- update [--editor <name>] [args] Launch /bitfab:update in the editor
27325
+ update [--editor <name>] [mode] Update plugin, then launch SDK update (mode: all|plugin|sdk)
27662
27326
  help Show this help
27663
27327
 
27664
27328
  Options:
27665
27329
  --editor, -e <name> Target editor: claude, codex, cursor
27666
- --skip-permissions Skip permission prompts in the launched agent
27330
+ --skip-permissions Skip permission prompts (runs the agent autonomously)
27331
+ --no-skip-permissions Keep permission prompts (default, skips the interactive question)
27667
27332
 
27668
27333
  Examples:
27669
27334
  bitfab init Full setup (detect editor, install, login, setup)
@@ -27675,7 +27340,7 @@ Examples:
27675
27340
  function parseArgs(argv) {
27676
27341
  const [command, ...tail] = argv;
27677
27342
  let editor;
27678
- let skipPermissions = false;
27343
+ let skipPermissions;
27679
27344
  const rest = [];
27680
27345
  for (let i = 0; i < tail.length; i++) {
27681
27346
  const arg = tail[i];
@@ -27690,6 +27355,8 @@ function parseArgs(argv) {
27690
27355
  editor = arg.slice("--editor=".length);
27691
27356
  } else if (arg === "--skip-permissions") {
27692
27357
  skipPermissions = true;
27358
+ } else if (arg === "--no-skip-permissions") {
27359
+ skipPermissions = false;
27693
27360
  } else if (arg !== void 0) {
27694
27361
  rest.push(arg);
27695
27362
  }
@@ -27742,3 +27409,6 @@ main().catch((err) => {
27742
27409
  cancel2(message);
27743
27410
  process.exit(1);
27744
27411
  });
27412
+ export {
27413
+ parseArgs
27414
+ };