bitfab-cli 0.2.179 → 0.2.181
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 +622 -92
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5155,8 +5155,8 @@ var require_utils = __commonJS({
|
|
|
5155
5155
|
}
|
|
5156
5156
|
return ind;
|
|
5157
5157
|
}
|
|
5158
|
-
function removeDotSegments(
|
|
5159
|
-
let input =
|
|
5158
|
+
function removeDotSegments(path21) {
|
|
5159
|
+
let input = path21;
|
|
5160
5160
|
const output = [];
|
|
5161
5161
|
let nextSlash = -1;
|
|
5162
5162
|
let len = 0;
|
|
@@ -5408,8 +5408,8 @@ var require_schemes = __commonJS({
|
|
|
5408
5408
|
wsComponent.secure = void 0;
|
|
5409
5409
|
}
|
|
5410
5410
|
if (wsComponent.resourceName) {
|
|
5411
|
-
const [
|
|
5412
|
-
wsComponent.path =
|
|
5411
|
+
const [path21, query] = wsComponent.resourceName.split("?");
|
|
5412
|
+
wsComponent.path = path21 && path21 !== "/" ? path21 : void 0;
|
|
5413
5413
|
wsComponent.query = query;
|
|
5414
5414
|
wsComponent.resourceName = void 0;
|
|
5415
5415
|
}
|
|
@@ -8802,12 +8802,12 @@ var require_dist = __commonJS({
|
|
|
8802
8802
|
throw new Error(`Unknown format "${name}"`);
|
|
8803
8803
|
return f;
|
|
8804
8804
|
};
|
|
8805
|
-
function addFormats(ajv, list,
|
|
8805
|
+
function addFormats(ajv, list, fs24, exportName) {
|
|
8806
8806
|
var _a2;
|
|
8807
8807
|
var _b;
|
|
8808
8808
|
(_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
8809
8809
|
for (const f of list)
|
|
8810
|
-
ajv.addFormat(f,
|
|
8810
|
+
ajv.addFormat(f, fs24[f]);
|
|
8811
8811
|
}
|
|
8812
8812
|
module.exports = exports = formatsPlugin;
|
|
8813
8813
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -9013,18 +9013,18 @@ async function closeStudio(client, message) {
|
|
|
9013
9013
|
data: message ? { message } : {}
|
|
9014
9014
|
});
|
|
9015
9015
|
}
|
|
9016
|
-
async function navigateStudio(client,
|
|
9017
|
-
if (!isValidStudioRoute(
|
|
9018
|
-
throw new Error(`Studio route not allowed: ${
|
|
9016
|
+
async function navigateStudio(client, path21) {
|
|
9017
|
+
if (!isValidStudioRoute(path21)) {
|
|
9018
|
+
throw new Error(`Studio route not allowed: ${path21}`);
|
|
9019
9019
|
}
|
|
9020
9020
|
return pushAgentSessionEvent(client, {
|
|
9021
9021
|
type: "agent:navigate",
|
|
9022
|
-
data: { path:
|
|
9022
|
+
data: { path: path21 }
|
|
9023
9023
|
});
|
|
9024
9024
|
}
|
|
9025
9025
|
var STUDIO_NAVIGATE_ACK_TIMEOUT_MS = 12e3;
|
|
9026
9026
|
var PING_PONG_TIMEOUT_MS = 5e3;
|
|
9027
|
-
function awaitNavigateAck(client,
|
|
9027
|
+
function awaitNavigateAck(client, path21, ackTimeoutMs, startCursor) {
|
|
9028
9028
|
const abortController = new AbortController();
|
|
9029
9029
|
let timer = null;
|
|
9030
9030
|
const result = new Promise((resolve2) => {
|
|
@@ -9032,7 +9032,7 @@ function awaitNavigateAck(client, path20, ackTimeoutMs, startCursor) {
|
|
|
9032
9032
|
resolve2({ acked: false, reason: "timeout" });
|
|
9033
9033
|
abortController.abort();
|
|
9034
9034
|
}, ackTimeoutMs);
|
|
9035
|
-
const pathOnly =
|
|
9035
|
+
const pathOnly = path21.split("?")[0];
|
|
9036
9036
|
pollAgentSessionEvents({
|
|
9037
9037
|
serviceUrl: client.serviceUrl,
|
|
9038
9038
|
apiKey: client.apiKey,
|
|
@@ -9118,18 +9118,18 @@ function awaitPong(client, pingTs) {
|
|
|
9118
9118
|
});
|
|
9119
9119
|
});
|
|
9120
9120
|
}
|
|
9121
|
-
async function navigateStudioAndAwaitAck(client,
|
|
9121
|
+
async function navigateStudioAndAwaitAck(client, path21, opts = {}) {
|
|
9122
9122
|
const ackTimeoutMs = opts.ackTimeoutMs ?? STUDIO_NAVIGATE_ACK_TIMEOUT_MS;
|
|
9123
|
-
if (!isValidStudioRoute(
|
|
9123
|
+
if (!isValidStudioRoute(path21)) {
|
|
9124
9124
|
return { acked: false, reason: "invalid-route" };
|
|
9125
9125
|
}
|
|
9126
9126
|
const startCursor = await fetchStreamTip(client);
|
|
9127
9127
|
try {
|
|
9128
|
-
await navigateStudio(client,
|
|
9128
|
+
await navigateStudio(client, path21);
|
|
9129
9129
|
} catch {
|
|
9130
9130
|
return { acked: false, reason: "push-failed" };
|
|
9131
9131
|
}
|
|
9132
|
-
const result = await awaitNavigateAck(client,
|
|
9132
|
+
const result = await awaitNavigateAck(client, path21, ackTimeoutMs, startCursor);
|
|
9133
9133
|
if (result.acked || result.reason !== "timeout") {
|
|
9134
9134
|
return result;
|
|
9135
9135
|
}
|
|
@@ -10077,20 +10077,20 @@ function resolveStudioInitialPath(input) {
|
|
|
10077
10077
|
return DEFAULT_STUDIO_PATH;
|
|
10078
10078
|
}
|
|
10079
10079
|
function buildStudioSessionUrl(args) {
|
|
10080
|
-
const { serviceUrl, path:
|
|
10081
|
-
if (
|
|
10082
|
-
return `${serviceUrl}${
|
|
10080
|
+
const { serviceUrl, path: path21, sessionId } = args;
|
|
10081
|
+
if (path21.includes("session=")) {
|
|
10082
|
+
return `${serviceUrl}${path21}`;
|
|
10083
10083
|
}
|
|
10084
|
-
const separator =
|
|
10085
|
-
return `${serviceUrl}${
|
|
10084
|
+
const separator = path21.includes("?") ? "&" : "?";
|
|
10085
|
+
return `${serviceUrl}${path21}${separator}session=${encodeURIComponent(sessionId)}`;
|
|
10086
10086
|
}
|
|
10087
10087
|
function resolveStudioSessionTarget(args) {
|
|
10088
|
-
const
|
|
10088
|
+
const path21 = resolveStudioInitialPath(args.path);
|
|
10089
10089
|
return {
|
|
10090
|
-
path:
|
|
10090
|
+
path: path21,
|
|
10091
10091
|
url: buildStudioSessionUrl({
|
|
10092
10092
|
serviceUrl: args.serviceUrl,
|
|
10093
|
-
path:
|
|
10093
|
+
path: path21,
|
|
10094
10094
|
sessionId: args.sessionId
|
|
10095
10095
|
})
|
|
10096
10096
|
};
|
|
@@ -10751,8 +10751,8 @@ function tailStudioEvents(key, onLine, opts) {
|
|
|
10751
10751
|
}
|
|
10752
10752
|
|
|
10753
10753
|
// ../bitfab-plugin-lib/dist/studioChannel.js
|
|
10754
|
-
function stripSessionId(
|
|
10755
|
-
return sessionId ?
|
|
10754
|
+
function stripSessionId(path21, sessionId) {
|
|
10755
|
+
return sessionId ? path21.split(sessionId).join("__SID__") : path21;
|
|
10756
10756
|
}
|
|
10757
10757
|
var DirectChannel = class {
|
|
10758
10758
|
apiKey;
|
|
@@ -10767,10 +10767,10 @@ var DirectChannel = class {
|
|
|
10767
10767
|
isPersistent() {
|
|
10768
10768
|
return false;
|
|
10769
10769
|
}
|
|
10770
|
-
async openOrNavigate(
|
|
10770
|
+
async openOrNavigate(path21, opts) {
|
|
10771
10771
|
if (!this.apiKey) {
|
|
10772
10772
|
const sessionId2 = opts?.sessionId ?? crypto8.randomUUID();
|
|
10773
|
-
const initialPath = resolveStudioInitialPath(
|
|
10773
|
+
const initialPath = resolveStudioInitialPath(path21);
|
|
10774
10774
|
const live = readActiveStudioSession();
|
|
10775
10775
|
if (live && live.source === "direct" && !live.authenticated && live.windowState !== "closed" && live.serviceUrl === this.serviceUrl && live.currentPath != null && stripSessionId(live.currentPath, live.sessionId) === stripSessionId(initialPath, sessionId2)) {
|
|
10776
10776
|
return { sessionId: live.sessionId, opened: false };
|
|
@@ -10801,11 +10801,11 @@ var DirectChannel = class {
|
|
|
10801
10801
|
serviceUrl: existing.serviceUrl,
|
|
10802
10802
|
apiKey: this.apiKey,
|
|
10803
10803
|
sessionId: existing.sessionId
|
|
10804
|
-
},
|
|
10804
|
+
}, path21);
|
|
10805
10805
|
if (!ack.acked) {
|
|
10806
10806
|
throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
|
|
10807
10807
|
}
|
|
10808
|
-
updateActiveStudioSession({ currentPath:
|
|
10808
|
+
updateActiveStudioSession({ currentPath: path21 });
|
|
10809
10809
|
return { sessionId: existing.sessionId, opened: false };
|
|
10810
10810
|
}
|
|
10811
10811
|
truncateStudioEventLog(resolveAgentSessionKey());
|
|
@@ -10814,7 +10814,7 @@ var DirectChannel = class {
|
|
|
10814
10814
|
serviceUrl: this.serviceUrl,
|
|
10815
10815
|
apiKey: this.apiKey,
|
|
10816
10816
|
sessionId,
|
|
10817
|
-
initialPath:
|
|
10817
|
+
initialPath: path21,
|
|
10818
10818
|
clientHeaders: opts?.clientHeaders
|
|
10819
10819
|
});
|
|
10820
10820
|
return { sessionId: session.sessionId, opened: true };
|
|
@@ -10881,18 +10881,18 @@ var DaemonChannel = class _DaemonChannel {
|
|
|
10881
10881
|
isPersistent() {
|
|
10882
10882
|
return true;
|
|
10883
10883
|
}
|
|
10884
|
-
async openOrNavigate(
|
|
10884
|
+
async openOrNavigate(path21, opts) {
|
|
10885
10885
|
const openRes = await this.client.open({
|
|
10886
10886
|
key: this.key,
|
|
10887
10887
|
serviceUrl: this.serviceUrl,
|
|
10888
10888
|
apiKey: this.apiKey,
|
|
10889
10889
|
sessionId: opts?.sessionId,
|
|
10890
|
-
initialPath:
|
|
10890
|
+
initialPath: path21,
|
|
10891
10891
|
clientHeaders: opts?.clientHeaders,
|
|
10892
10892
|
focusTarget: opts?.focusTarget
|
|
10893
10893
|
});
|
|
10894
10894
|
if (openRes.reconnected) {
|
|
10895
|
-
const navRes = await this.client.navigate(this.key,
|
|
10895
|
+
const navRes = await this.client.navigate(this.key, path21);
|
|
10896
10896
|
if (!navRes.acked) {
|
|
10897
10897
|
this.client.destroy();
|
|
10898
10898
|
throw new StudioNavigationError(navRes.reason ?? "unknown", void 0, openRes.sessionId);
|
|
@@ -10950,9 +10950,9 @@ function isDaemonEnabled() {
|
|
|
10950
10950
|
function withDirectFallback(primary, apiKey, serviceUrl) {
|
|
10951
10951
|
let active = primary;
|
|
10952
10952
|
return {
|
|
10953
|
-
async openOrNavigate(
|
|
10953
|
+
async openOrNavigate(path21, opts) {
|
|
10954
10954
|
try {
|
|
10955
|
-
return await active.openOrNavigate(
|
|
10955
|
+
return await active.openOrNavigate(path21, opts);
|
|
10956
10956
|
} catch (err) {
|
|
10957
10957
|
if (err instanceof StudioNavigationError || active !== primary) {
|
|
10958
10958
|
throw err;
|
|
@@ -10960,7 +10960,7 @@ function withDirectFallback(primary, apiKey, serviceUrl) {
|
|
|
10960
10960
|
console.error(`daemon failed, falling back to direct: ${err instanceof Error ? err.message : String(err)}`);
|
|
10961
10961
|
active.destroy();
|
|
10962
10962
|
active = new DirectChannel(apiKey, serviceUrl);
|
|
10963
|
-
return active.openOrNavigate(
|
|
10963
|
+
return active.openOrNavigate(path21, opts);
|
|
10964
10964
|
}
|
|
10965
10965
|
},
|
|
10966
10966
|
async close(sessionId, message) {
|
|
@@ -11285,7 +11285,7 @@ function createEventSubscription(channel, sessionId, onEvent, restoreFocus) {
|
|
|
11285
11285
|
done: sub.done
|
|
11286
11286
|
};
|
|
11287
11287
|
}
|
|
11288
|
-
async function openStudioTo(
|
|
11288
|
+
async function openStudioTo(path21, opts = {}) {
|
|
11289
11289
|
const config4 = getConfig();
|
|
11290
11290
|
const serviceUrl = config4.serviceUrl;
|
|
11291
11291
|
const apiKey = !opts.forceLogin && hasCredentials() ? config4.apiKey : null;
|
|
@@ -11363,7 +11363,7 @@ async function openStudioTo(path20, opts = {}) {
|
|
|
11363
11363
|
};
|
|
11364
11364
|
};
|
|
11365
11365
|
if (!apiKey) {
|
|
11366
|
-
const destination = opts.freshWindowPath ??
|
|
11366
|
+
const destination = opts.freshWindowPath ?? path21;
|
|
11367
11367
|
const destSeparator = destination.includes("?") ? "&" : "?";
|
|
11368
11368
|
const buildSignInPath = (sid) => {
|
|
11369
11369
|
const redirectPath = `${destination}${destSeparator}session=${encodeURIComponent(sid)}&pluginLogin=true`;
|
|
@@ -11408,11 +11408,11 @@ async function openStudioTo(path20, opts = {}) {
|
|
|
11408
11408
|
channel.setApiKey(loginApiKey);
|
|
11409
11409
|
return finish(sessionId2, openResult.opened, loginApiKey, openResult.opened ? signInUrl : void 0);
|
|
11410
11410
|
}
|
|
11411
|
-
const { sessionId, opened } = await channel.openOrNavigate(
|
|
11411
|
+
const { sessionId, opened } = await channel.openOrNavigate(path21, {
|
|
11412
11412
|
clientHeaders: opts.clientHeaders,
|
|
11413
11413
|
focusTarget: focusTarget ?? void 0
|
|
11414
11414
|
});
|
|
11415
|
-
const url2 = opened ? resolveStudioSessionTarget({ serviceUrl, path:
|
|
11415
|
+
const url2 = opened ? resolveStudioSessionTarget({ serviceUrl, path: path21, sessionId }).url : void 0;
|
|
11416
11416
|
if (url2) {
|
|
11417
11417
|
opts.onWindowOpened?.(url2);
|
|
11418
11418
|
}
|
|
@@ -12293,10 +12293,10 @@ function mergeDefs(...defs) {
|
|
|
12293
12293
|
function cloneDef(schema) {
|
|
12294
12294
|
return mergeDefs(schema._zod.def);
|
|
12295
12295
|
}
|
|
12296
|
-
function getElementAtPath(obj,
|
|
12297
|
-
if (!
|
|
12296
|
+
function getElementAtPath(obj, path21) {
|
|
12297
|
+
if (!path21)
|
|
12298
12298
|
return obj;
|
|
12299
|
-
return
|
|
12299
|
+
return path21.reduce((acc, key) => acc?.[key], obj);
|
|
12300
12300
|
}
|
|
12301
12301
|
function promiseAllObject(promisesObj) {
|
|
12302
12302
|
const keys = Object.keys(promisesObj);
|
|
@@ -12679,11 +12679,11 @@ function aborted(x, startIndex = 0) {
|
|
|
12679
12679
|
}
|
|
12680
12680
|
return false;
|
|
12681
12681
|
}
|
|
12682
|
-
function prefixIssues(
|
|
12682
|
+
function prefixIssues(path21, issues) {
|
|
12683
12683
|
return issues.map((iss) => {
|
|
12684
12684
|
var _a2;
|
|
12685
12685
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
12686
|
-
iss.path.unshift(
|
|
12686
|
+
iss.path.unshift(path21);
|
|
12687
12687
|
return iss;
|
|
12688
12688
|
});
|
|
12689
12689
|
}
|
|
@@ -12866,7 +12866,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12866
12866
|
}
|
|
12867
12867
|
function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
12868
12868
|
const result = { errors: [] };
|
|
12869
|
-
const processError = (error49,
|
|
12869
|
+
const processError = (error49, path21 = []) => {
|
|
12870
12870
|
var _a2, _b;
|
|
12871
12871
|
for (const issue2 of error49.issues) {
|
|
12872
12872
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -12876,7 +12876,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12876
12876
|
} else if (issue2.code === "invalid_element") {
|
|
12877
12877
|
processError({ issues: issue2.issues }, issue2.path);
|
|
12878
12878
|
} else {
|
|
12879
|
-
const fullpath = [...
|
|
12879
|
+
const fullpath = [...path21, ...issue2.path];
|
|
12880
12880
|
if (fullpath.length === 0) {
|
|
12881
12881
|
result.errors.push(mapper(issue2));
|
|
12882
12882
|
continue;
|
|
@@ -12908,8 +12908,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12908
12908
|
}
|
|
12909
12909
|
function toDotPath(_path) {
|
|
12910
12910
|
const segs = [];
|
|
12911
|
-
const
|
|
12912
|
-
for (const seg of
|
|
12911
|
+
const path21 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
12912
|
+
for (const seg of path21) {
|
|
12913
12913
|
if (typeof seg === "number")
|
|
12914
12914
|
segs.push(`[${seg}]`);
|
|
12915
12915
|
else if (typeof seg === "symbol")
|
|
@@ -24886,13 +24886,13 @@ function resolveRef(ref, ctx) {
|
|
|
24886
24886
|
if (!ref.startsWith("#")) {
|
|
24887
24887
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
24888
24888
|
}
|
|
24889
|
-
const
|
|
24890
|
-
if (
|
|
24889
|
+
const path21 = ref.slice(1).split("/").filter(Boolean);
|
|
24890
|
+
if (path21.length === 0) {
|
|
24891
24891
|
return ctx.rootSchema;
|
|
24892
24892
|
}
|
|
24893
24893
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
24894
|
-
if (
|
|
24895
|
-
const key =
|
|
24894
|
+
if (path21[0] === defsKey) {
|
|
24895
|
+
const key = path21[1];
|
|
24896
24896
|
if (!key || !ctx.defs[key]) {
|
|
24897
24897
|
throw new Error(`Reference not found: ${ref}`);
|
|
24898
24898
|
}
|
|
@@ -25730,8 +25730,8 @@ function getErrorMap2() {
|
|
|
25730
25730
|
|
|
25731
25731
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/helpers/parseUtil.js
|
|
25732
25732
|
var makeIssue = (params) => {
|
|
25733
|
-
const { data, path:
|
|
25734
|
-
const fullPath = [...
|
|
25733
|
+
const { data, path: path21, errorMaps, issueData } = params;
|
|
25734
|
+
const fullPath = [...path21, ...issueData.path || []];
|
|
25735
25735
|
const fullIssue = {
|
|
25736
25736
|
...issueData,
|
|
25737
25737
|
path: fullPath
|
|
@@ -25846,11 +25846,11 @@ var errorUtil;
|
|
|
25846
25846
|
|
|
25847
25847
|
// ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/types.js
|
|
25848
25848
|
var ParseInputLazyPath = class {
|
|
25849
|
-
constructor(parent, value,
|
|
25849
|
+
constructor(parent, value, path21, key) {
|
|
25850
25850
|
this._cachedPath = [];
|
|
25851
25851
|
this.parent = parent;
|
|
25852
25852
|
this.data = value;
|
|
25853
|
-
this._path =
|
|
25853
|
+
this._path = path21;
|
|
25854
25854
|
this._key = key;
|
|
25855
25855
|
}
|
|
25856
25856
|
get path() {
|
|
@@ -30957,6 +30957,27 @@ function cliUpdate(cfg, args, skipPermissions) {
|
|
|
30957
30957
|
}
|
|
30958
30958
|
}
|
|
30959
30959
|
|
|
30960
|
+
// src/redaction.ts
|
|
30961
|
+
var SECRET_PATTERN = /bf_[A-Za-z0-9]+/g;
|
|
30962
|
+
var STREAMING_SECRET_TAIL_LENGTH = 512;
|
|
30963
|
+
function redactSecrets(text) {
|
|
30964
|
+
return text.replace(SECRET_PATTERN, "bf_[redacted]");
|
|
30965
|
+
}
|
|
30966
|
+
var StreamingSecretRedactor = class {
|
|
30967
|
+
pending = "";
|
|
30968
|
+
redact(chunk2) {
|
|
30969
|
+
const text = this.pending + chunk2;
|
|
30970
|
+
const flushLength = Math.max(0, text.length - STREAMING_SECRET_TAIL_LENGTH);
|
|
30971
|
+
this.pending = text.slice(flushLength);
|
|
30972
|
+
return redactSecrets(text.slice(0, flushLength));
|
|
30973
|
+
}
|
|
30974
|
+
flush() {
|
|
30975
|
+
const redacted = redactSecrets(this.pending);
|
|
30976
|
+
this.pending = "";
|
|
30977
|
+
return redacted;
|
|
30978
|
+
}
|
|
30979
|
+
};
|
|
30980
|
+
|
|
30960
30981
|
// src/claude.ts
|
|
30961
30982
|
var CLI = "claude";
|
|
30962
30983
|
var SKIP_FLAG = "--dangerously-skip-permissions";
|
|
@@ -31035,9 +31056,27 @@ async function runClaudeAnalyzeRepo(captureOverride, limit) {
|
|
|
31035
31056
|
}
|
|
31036
31057
|
const command = limit === void 0 ? ANALYZE_REPO_COMMAND : `${ANALYZE_REPO_COMMAND} limit=${limit}`;
|
|
31037
31058
|
const out = fs20.createWriteStream(logPath);
|
|
31038
|
-
const
|
|
31039
|
-
|
|
31059
|
+
const spinner6 = p3.spinner();
|
|
31060
|
+
spinner6.start("Analyzing repo and drafting trace plans");
|
|
31040
31061
|
const stdoutChunks = [];
|
|
31062
|
+
const stdoutRedactor = new StreamingSecretRedactor();
|
|
31063
|
+
const stderrRedactor = new StreamingSecretRedactor();
|
|
31064
|
+
let redactorsFlushed = false;
|
|
31065
|
+
const flushRedactors = () => {
|
|
31066
|
+
if (redactorsFlushed) {
|
|
31067
|
+
return;
|
|
31068
|
+
}
|
|
31069
|
+
redactorsFlushed = true;
|
|
31070
|
+
const stdoutTail = stdoutRedactor.flush();
|
|
31071
|
+
if (stdoutTail !== "") {
|
|
31072
|
+
out.write(stdoutTail);
|
|
31073
|
+
stdoutChunks.push(stdoutTail);
|
|
31074
|
+
}
|
|
31075
|
+
const stderrTail = stderrRedactor.flush();
|
|
31076
|
+
if (stderrTail !== "") {
|
|
31077
|
+
out.write(stderrTail);
|
|
31078
|
+
}
|
|
31079
|
+
};
|
|
31041
31080
|
try {
|
|
31042
31081
|
const exitCode = await new Promise((resolve2, reject) => {
|
|
31043
31082
|
const child = spawn5(
|
|
@@ -31053,31 +31092,49 @@ async function runClaudeAnalyzeRepo(captureOverride, limit) {
|
|
|
31053
31092
|
{ stdio: ["ignore", "pipe", "pipe"], env, ...SHELL_OPTS }
|
|
31054
31093
|
);
|
|
31055
31094
|
child.stdout.on("data", (chunk2) => {
|
|
31056
|
-
out.write(chunk2);
|
|
31057
31095
|
const text = chunk2.toString();
|
|
31058
|
-
|
|
31096
|
+
const redacted = stdoutRedactor.redact(text);
|
|
31097
|
+
if (redacted !== "") {
|
|
31098
|
+
out.write(redacted);
|
|
31099
|
+
stdoutChunks.push(redacted);
|
|
31100
|
+
}
|
|
31059
31101
|
const label = latestActivityLabel(text);
|
|
31060
31102
|
if (label) {
|
|
31061
|
-
|
|
31103
|
+
spinner6.message(label);
|
|
31062
31104
|
}
|
|
31063
31105
|
});
|
|
31064
|
-
child.stderr.on(
|
|
31106
|
+
child.stderr.on(
|
|
31107
|
+
"data",
|
|
31108
|
+
(chunk2) => out.write(stderrRedactor.redact(chunk2.toString()))
|
|
31109
|
+
);
|
|
31065
31110
|
child.on("error", reject);
|
|
31066
|
-
child.on("close", (code) =>
|
|
31111
|
+
child.on("close", (code, signal) => {
|
|
31112
|
+
if (code === null) {
|
|
31113
|
+
reject(
|
|
31114
|
+
new Error(
|
|
31115
|
+
`analyze-repo terminated by signal ${signal ?? "unknown"}. See ${logPath}`
|
|
31116
|
+
)
|
|
31117
|
+
);
|
|
31118
|
+
return;
|
|
31119
|
+
}
|
|
31120
|
+
resolve2(code);
|
|
31121
|
+
});
|
|
31067
31122
|
});
|
|
31123
|
+
flushRedactors();
|
|
31068
31124
|
if (exitCode !== 0) {
|
|
31069
31125
|
throw new Error(
|
|
31070
31126
|
`analyze-repo exited with status ${exitCode}. See ${logPath}`
|
|
31071
31127
|
);
|
|
31072
31128
|
}
|
|
31073
31129
|
const uploaded = countUploadedTracePlans(stdoutChunks.join(""));
|
|
31074
|
-
|
|
31130
|
+
spinner6.stop(
|
|
31075
31131
|
uploaded === 0 ? "No AI workflows found - nothing to upload" : `Uploaded ${uploaded} draft trace plan${uploaded === 1 ? "" : "s"} to Bitfab`
|
|
31076
31132
|
);
|
|
31077
31133
|
} catch (err) {
|
|
31078
|
-
|
|
31134
|
+
spinner6.stop("analyze-repo failed");
|
|
31079
31135
|
throw err;
|
|
31080
31136
|
} finally {
|
|
31137
|
+
flushRedactors();
|
|
31081
31138
|
out.end();
|
|
31082
31139
|
}
|
|
31083
31140
|
p3.log.info(`Full run log: ${logPath}`);
|
|
@@ -31242,6 +31299,8 @@ var claudeAdapter = {
|
|
|
31242
31299
|
};
|
|
31243
31300
|
|
|
31244
31301
|
// src/codex.ts
|
|
31302
|
+
import { spawn as spawn6 } from "child_process";
|
|
31303
|
+
import crypto11 from "crypto";
|
|
31245
31304
|
import fs21 from "fs";
|
|
31246
31305
|
import os16 from "os";
|
|
31247
31306
|
import path18 from "path";
|
|
@@ -31251,7 +31310,9 @@ var SKIP_FLAG2 = "--full-auto";
|
|
|
31251
31310
|
var REPO2 = "Project-White-Rabbit/bitfab-codex-plugin";
|
|
31252
31311
|
var MARKETPLACE2 = "bitfab";
|
|
31253
31312
|
var PLUGIN_KEY2 = "bitfab@bitfab";
|
|
31313
|
+
var INTERNAL_PLUGIN_KEY = "bitfab@bitfab-internal";
|
|
31254
31314
|
var SETUP_COMMAND2 = "$bitfab:setup";
|
|
31315
|
+
var ANALYZE_REPO_COMMAND2 = "$bitfab:setup analyze-repo";
|
|
31255
31316
|
var ASSISTANT_COMMAND2 = "$bitfab:assistant";
|
|
31256
31317
|
var UPDATE_COMMAND2 = "$bitfab:update";
|
|
31257
31318
|
function isCodexLoginStatusLoggedIn(output) {
|
|
@@ -31271,6 +31332,12 @@ function ensureLatestCodexPlugin() {
|
|
|
31271
31332
|
ensureLatestPlugin(pullLatestCodexPlugin);
|
|
31272
31333
|
}
|
|
31273
31334
|
function runCodexInstall() {
|
|
31335
|
+
const configPath = path18.join(os16.homedir(), ".codex", "config.toml");
|
|
31336
|
+
if (isCodexPluginEnabled(configPath, INTERNAL_PLUGIN_KEY)) {
|
|
31337
|
+
p4.log.step("Codex dev plugin already active");
|
|
31338
|
+
p4.log.success("Bitfab plugin ready in Codex");
|
|
31339
|
+
return;
|
|
31340
|
+
}
|
|
31274
31341
|
const s = p4.spinner();
|
|
31275
31342
|
s.start("Adding bitfab marketplace");
|
|
31276
31343
|
const out = runCli(CLI2, ["plugin", "marketplace", "add", REPO2]);
|
|
@@ -31281,9 +31348,7 @@ function runCodexInstall() {
|
|
|
31281
31348
|
s.stop("Marketplace added");
|
|
31282
31349
|
}
|
|
31283
31350
|
s.start("Enabling bitfab plugin in config");
|
|
31284
|
-
const changed = enableCodexPlugin(
|
|
31285
|
-
path18.join(os16.homedir(), ".codex", "config.toml")
|
|
31286
|
-
);
|
|
31351
|
+
const changed = enableCodexPlugin(configPath);
|
|
31287
31352
|
if (changed) {
|
|
31288
31353
|
s.stop("Plugin enabled");
|
|
31289
31354
|
} else {
|
|
@@ -31294,13 +31359,229 @@ function runCodexInstall() {
|
|
|
31294
31359
|
}
|
|
31295
31360
|
p4.log.success("Bitfab plugin ready in Codex");
|
|
31296
31361
|
}
|
|
31297
|
-
function runCodexAnalyzeRepo() {
|
|
31298
|
-
|
|
31299
|
-
|
|
31300
|
-
|
|
31301
|
-
|
|
31302
|
-
|
|
31362
|
+
async function runCodexAnalyzeRepo(captureOverride, limit) {
|
|
31363
|
+
const auth = checkCodexAuth();
|
|
31364
|
+
if (!auth.ok) {
|
|
31365
|
+
throw new Error(auth.message);
|
|
31366
|
+
}
|
|
31367
|
+
const logPath = analyzeRepoLogPath2();
|
|
31368
|
+
const agentRunId = crypto11.randomUUID();
|
|
31369
|
+
const env = { ...process.env };
|
|
31370
|
+
env.BITFAB_AGENT_RUN_ID = agentRunId;
|
|
31371
|
+
if (captureOverride !== void 0) {
|
|
31372
|
+
env.BITFAB_CAPTURE_SESSIONS = captureOverride ? "1" : "0";
|
|
31373
|
+
}
|
|
31374
|
+
const command = limit === void 0 ? ANALYZE_REPO_COMMAND2 : `${ANALYZE_REPO_COMMAND2} limit=${limit}`;
|
|
31375
|
+
const out = fs21.createWriteStream(logPath);
|
|
31376
|
+
const spinner6 = p4.spinner();
|
|
31377
|
+
spinner6.start("Analyzing repo and drafting trace plans");
|
|
31378
|
+
const stdoutChunks = [];
|
|
31379
|
+
const stdoutRedactor = new StreamingSecretRedactor();
|
|
31380
|
+
const stderrRedactor = new StreamingSecretRedactor();
|
|
31381
|
+
let redactorsFlushed = false;
|
|
31382
|
+
const flushRedactors = () => {
|
|
31383
|
+
if (redactorsFlushed) {
|
|
31384
|
+
return;
|
|
31385
|
+
}
|
|
31386
|
+
redactorsFlushed = true;
|
|
31387
|
+
const stdoutTail = stdoutRedactor.flush();
|
|
31388
|
+
if (stdoutTail !== "") {
|
|
31389
|
+
out.write(stdoutTail);
|
|
31390
|
+
stdoutChunks.push(stdoutTail);
|
|
31391
|
+
}
|
|
31392
|
+
const stderrTail = stderrRedactor.flush();
|
|
31393
|
+
if (stderrTail !== "") {
|
|
31394
|
+
out.write(stderrTail);
|
|
31395
|
+
}
|
|
31396
|
+
};
|
|
31397
|
+
try {
|
|
31398
|
+
const exitCode = await new Promise((resolve2, reject) => {
|
|
31399
|
+
const child = spawn6(
|
|
31400
|
+
CLI2,
|
|
31401
|
+
[
|
|
31402
|
+
"--ask-for-approval",
|
|
31403
|
+
"on-request",
|
|
31404
|
+
"--config",
|
|
31405
|
+
'approvals_reviewer="auto_review"',
|
|
31406
|
+
"exec",
|
|
31407
|
+
"--json",
|
|
31408
|
+
"--sandbox",
|
|
31409
|
+
"danger-full-access",
|
|
31410
|
+
command
|
|
31411
|
+
],
|
|
31412
|
+
{ stdio: ["ignore", "pipe", "pipe"], env, ...SHELL_OPTS }
|
|
31413
|
+
);
|
|
31414
|
+
child.stdout.on("data", (chunk2) => {
|
|
31415
|
+
const text = chunk2.toString();
|
|
31416
|
+
const redacted = stdoutRedactor.redact(text);
|
|
31417
|
+
if (redacted !== "") {
|
|
31418
|
+
out.write(redacted);
|
|
31419
|
+
stdoutChunks.push(redacted);
|
|
31420
|
+
}
|
|
31421
|
+
const label = latestCodexActivityLabel(text);
|
|
31422
|
+
if (label) {
|
|
31423
|
+
spinner6.message(label);
|
|
31424
|
+
}
|
|
31425
|
+
});
|
|
31426
|
+
child.stderr.on(
|
|
31427
|
+
"data",
|
|
31428
|
+
(chunk2) => out.write(stderrRedactor.redact(chunk2.toString()))
|
|
31429
|
+
);
|
|
31430
|
+
child.on("error", reject);
|
|
31431
|
+
child.on("close", (code, signal) => {
|
|
31432
|
+
if (code === null) {
|
|
31433
|
+
reject(
|
|
31434
|
+
new Error(
|
|
31435
|
+
`analyze-repo terminated by signal ${signal ?? "unknown"}. See ${logPath}`
|
|
31436
|
+
)
|
|
31437
|
+
);
|
|
31438
|
+
return;
|
|
31439
|
+
}
|
|
31440
|
+
resolve2(code);
|
|
31441
|
+
});
|
|
31442
|
+
});
|
|
31443
|
+
flushRedactors();
|
|
31444
|
+
if (exitCode !== 0) {
|
|
31445
|
+
throw new Error(
|
|
31446
|
+
`analyze-repo exited with status ${exitCode}. See ${logPath}`
|
|
31447
|
+
);
|
|
31448
|
+
}
|
|
31449
|
+
const uploaded = countUploadedTracePlans2(stdoutChunks.join(""));
|
|
31450
|
+
spinner6.stop(
|
|
31451
|
+
uploaded === 0 ? "No AI workflows found - nothing to upload" : `Uploaded ${uploaded} draft trace plan${uploaded === 1 ? "" : "s"} to Bitfab`
|
|
31452
|
+
);
|
|
31453
|
+
} catch (err) {
|
|
31454
|
+
spinner6.stop("analyze-repo failed");
|
|
31455
|
+
throw err;
|
|
31456
|
+
} finally {
|
|
31457
|
+
flushRedactors();
|
|
31458
|
+
out.end();
|
|
31459
|
+
}
|
|
31460
|
+
p4.log.info(`Full run log: ${logPath}`);
|
|
31461
|
+
}
|
|
31462
|
+
function latestCodexActivityLabel(chunk2) {
|
|
31463
|
+
const lines = chunk2.split("\n").filter((l) => l.trim() !== "");
|
|
31464
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
31465
|
+
let event;
|
|
31466
|
+
try {
|
|
31467
|
+
event = JSON.parse(lines[i]);
|
|
31468
|
+
} catch {
|
|
31469
|
+
continue;
|
|
31470
|
+
}
|
|
31471
|
+
const label = codexActivityLabelFromEvent(event);
|
|
31472
|
+
if (label) {
|
|
31473
|
+
return label;
|
|
31474
|
+
}
|
|
31475
|
+
}
|
|
31476
|
+
return null;
|
|
31477
|
+
}
|
|
31478
|
+
function codexActivityLabelFromEvent(event) {
|
|
31479
|
+
const item = eventItem(event);
|
|
31480
|
+
if (!item) {
|
|
31481
|
+
return null;
|
|
31482
|
+
}
|
|
31483
|
+
if (isMcpToolItem(item, "create_trace_plan")) {
|
|
31484
|
+
return "Uploading a draft trace plan";
|
|
31485
|
+
}
|
|
31486
|
+
if (isMcpToolItem(item, "get_bitfab_api_key")) {
|
|
31487
|
+
return "Checking authentication";
|
|
31488
|
+
}
|
|
31489
|
+
const command = typeof item.command === "string" ? item.command : "";
|
|
31490
|
+
if (command.includes("rg ") || command.includes("rg --files")) {
|
|
31491
|
+
return "Searching the codebase";
|
|
31492
|
+
}
|
|
31493
|
+
if (command.includes("sed ") || command.includes("cat ") || command.includes("nl ")) {
|
|
31494
|
+
return "Reading files";
|
|
31495
|
+
}
|
|
31496
|
+
if (typeof item.type === "string" && item.type.includes("web")) {
|
|
31497
|
+
return "Reading SDK docs";
|
|
31498
|
+
}
|
|
31499
|
+
return null;
|
|
31500
|
+
}
|
|
31501
|
+
function countUploadedTracePlans2(logText) {
|
|
31502
|
+
const planToolUseIds = /* @__PURE__ */ new Set();
|
|
31503
|
+
const nonErrorResultIds = /* @__PURE__ */ new Set();
|
|
31504
|
+
let completedPlanCalls = 0;
|
|
31505
|
+
for (const line of logText.split("\n")) {
|
|
31506
|
+
if (line.trim() === "") {
|
|
31507
|
+
continue;
|
|
31508
|
+
}
|
|
31509
|
+
let event;
|
|
31510
|
+
try {
|
|
31511
|
+
event = JSON.parse(line);
|
|
31512
|
+
} catch {
|
|
31513
|
+
continue;
|
|
31514
|
+
}
|
|
31515
|
+
const item = eventItem(event);
|
|
31516
|
+
if (item && isMcpToolItem(item, "create_trace_plan")) {
|
|
31517
|
+
const status = typeof item.status === "string" ? item.status : "";
|
|
31518
|
+
if (status === "completed" || status === "" && eventType(event) === "item.completed") {
|
|
31519
|
+
completedPlanCalls++;
|
|
31520
|
+
}
|
|
31521
|
+
}
|
|
31522
|
+
for (const block of eventMessageContent2(event) ?? []) {
|
|
31523
|
+
if (typeof block !== "object" || block === null) {
|
|
31524
|
+
continue;
|
|
31525
|
+
}
|
|
31526
|
+
const b = block;
|
|
31527
|
+
if (b.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string" && b.name.includes("create_trace_plan")) {
|
|
31528
|
+
planToolUseIds.add(b.id);
|
|
31529
|
+
}
|
|
31530
|
+
if (b.type === "tool_result" && typeof b.tool_use_id === "string" && b.is_error !== true) {
|
|
31531
|
+
nonErrorResultIds.add(b.tool_use_id);
|
|
31532
|
+
}
|
|
31533
|
+
}
|
|
31534
|
+
}
|
|
31535
|
+
let pairedCount = 0;
|
|
31536
|
+
for (const id of planToolUseIds) {
|
|
31537
|
+
if (nonErrorResultIds.has(id)) {
|
|
31538
|
+
pairedCount++;
|
|
31539
|
+
}
|
|
31540
|
+
}
|
|
31541
|
+
return pairedCount + completedPlanCalls;
|
|
31542
|
+
}
|
|
31543
|
+
function isMcpToolItem(item, toolName) {
|
|
31544
|
+
if (typeof item.type === "string" && !item.type.includes("mcp")) {
|
|
31545
|
+
return false;
|
|
31546
|
+
}
|
|
31547
|
+
const directNames = [item.tool, item.name].filter(
|
|
31548
|
+
(value) => typeof value === "string"
|
|
31303
31549
|
);
|
|
31550
|
+
if (directNames.some((name) => name.includes(toolName))) {
|
|
31551
|
+
return true;
|
|
31552
|
+
}
|
|
31553
|
+
const toolCall = item.tool_call;
|
|
31554
|
+
if (typeof toolCall === "object" && toolCall !== null) {
|
|
31555
|
+
const nested = toolCall;
|
|
31556
|
+
return [nested.tool, nested.name].some(
|
|
31557
|
+
(value) => typeof value === "string" && value.includes(toolName)
|
|
31558
|
+
);
|
|
31559
|
+
}
|
|
31560
|
+
return false;
|
|
31561
|
+
}
|
|
31562
|
+
function eventType(event) {
|
|
31563
|
+
return typeof event === "object" && event !== null && typeof event.type === "string" ? event.type : "";
|
|
31564
|
+
}
|
|
31565
|
+
function eventItem(event) {
|
|
31566
|
+
if (typeof event !== "object" || event === null) {
|
|
31567
|
+
return null;
|
|
31568
|
+
}
|
|
31569
|
+
const item = event.item;
|
|
31570
|
+
return typeof item === "object" && item !== null ? item : null;
|
|
31571
|
+
}
|
|
31572
|
+
function eventMessageContent2(event) {
|
|
31573
|
+
if (typeof event !== "object" || event === null) {
|
|
31574
|
+
return null;
|
|
31575
|
+
}
|
|
31576
|
+
const message = event.message;
|
|
31577
|
+
const content = typeof message === "object" && message !== null ? message.content : void 0;
|
|
31578
|
+
return Array.isArray(content) ? content : null;
|
|
31579
|
+
}
|
|
31580
|
+
function analyzeRepoLogPath2() {
|
|
31581
|
+
const dir = path18.join(process.cwd(), ".bitfab", "logs");
|
|
31582
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
31583
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
31584
|
+
return path18.join(dir, `analyze-repo-${stamp}.jsonl`);
|
|
31304
31585
|
}
|
|
31305
31586
|
function enableCodexPlugin(configPath) {
|
|
31306
31587
|
let content = "";
|
|
@@ -31316,7 +31597,7 @@ enabled = true
|
|
|
31316
31597
|
"m"
|
|
31317
31598
|
);
|
|
31318
31599
|
const match = sectionRegex.exec(content);
|
|
31319
|
-
if (match
|
|
31600
|
+
if (match && sectionHasEnabledTrue(match[0])) {
|
|
31320
31601
|
return false;
|
|
31321
31602
|
}
|
|
31322
31603
|
let next;
|
|
@@ -31332,9 +31613,24 @@ ${desiredSection}`;
|
|
|
31332
31613
|
fs21.writeFileSync(configPath, next);
|
|
31333
31614
|
return true;
|
|
31334
31615
|
}
|
|
31616
|
+
function isCodexPluginEnabled(configPath, pluginKey) {
|
|
31617
|
+
if (!fs21.existsSync(configPath)) {
|
|
31618
|
+
return false;
|
|
31619
|
+
}
|
|
31620
|
+
const content = fs21.readFileSync(configPath, "utf-8");
|
|
31621
|
+
const sectionRegex = new RegExp(
|
|
31622
|
+
`\\[plugins\\."${escapeRegex2(pluginKey)}"\\][^\\[]*`,
|
|
31623
|
+
"m"
|
|
31624
|
+
);
|
|
31625
|
+
const match = sectionRegex.exec(content);
|
|
31626
|
+
return match ? sectionHasEnabledTrue(match[0]) : false;
|
|
31627
|
+
}
|
|
31335
31628
|
function escapeRegex2(s) {
|
|
31336
31629
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31337
31630
|
}
|
|
31631
|
+
function sectionHasEnabledTrue(section) {
|
|
31632
|
+
return section.split(/\r?\n/).some((line) => /^\s*enabled\s*=\s*true\s*(?:#.*)?$/.test(line));
|
|
31633
|
+
}
|
|
31338
31634
|
var config3 = {
|
|
31339
31635
|
cli: CLI2,
|
|
31340
31636
|
skipFlag: SKIP_FLAG2,
|
|
@@ -31355,12 +31651,16 @@ var codexAdapter = {
|
|
|
31355
31651
|
};
|
|
31356
31652
|
|
|
31357
31653
|
// src/cursor.ts
|
|
31358
|
-
import { spawn as
|
|
31654
|
+
import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
|
|
31655
|
+
import crypto12 from "crypto";
|
|
31656
|
+
import fs22 from "fs";
|
|
31359
31657
|
import os17 from "os";
|
|
31658
|
+
import path19 from "path";
|
|
31360
31659
|
import * as p5 from "@clack/prompts";
|
|
31361
31660
|
var REPO3 = "Project-White-Rabbit/bitfab-cursor-plugin";
|
|
31362
31661
|
var ADD_PLUGIN_CMD = `/add-plugin ${REPO3}`;
|
|
31363
31662
|
var SETUP_COMMAND3 = "/bitfab-setup";
|
|
31663
|
+
var ANALYZE_REPO_COMMAND3 = "/bitfab-setup analyze-repo";
|
|
31364
31664
|
var ASSISTANT_COMMAND3 = "/bitfab-assistant";
|
|
31365
31665
|
var UPDATE_COMMAND3 = "/bitfab-update";
|
|
31366
31666
|
function isCursorAgentStatusAuthenticated(output) {
|
|
@@ -31399,7 +31699,7 @@ function runCursorInstall() {
|
|
|
31399
31699
|
"Finish setup in Cursor"
|
|
31400
31700
|
);
|
|
31401
31701
|
p5.log.info("Opening Cursor...");
|
|
31402
|
-
const child =
|
|
31702
|
+
const child = spawn7("cursor", ["."], {
|
|
31403
31703
|
stdio: "ignore",
|
|
31404
31704
|
detached: true,
|
|
31405
31705
|
windowsHide: true,
|
|
@@ -31418,11 +31718,241 @@ function runCursorSetup(skipPermissions) {
|
|
|
31418
31718
|
}
|
|
31419
31719
|
p5.log.info(`To complete setup, run ${SETUP_COMMAND3} in Cursor.`);
|
|
31420
31720
|
}
|
|
31421
|
-
function runCursorAnalyzeRepo() {
|
|
31422
|
-
|
|
31423
|
-
|
|
31424
|
-
)
|
|
31425
|
-
|
|
31721
|
+
async function runCursorAnalyzeRepo(captureOverride, limit) {
|
|
31722
|
+
ensureCursorAuthenticated();
|
|
31723
|
+
const pluginDir = resolveCursorPluginDir();
|
|
31724
|
+
if (!pluginDir) {
|
|
31725
|
+
throw new Error(
|
|
31726
|
+
[
|
|
31727
|
+
"Could not find a Cursor Bitfab plugin directory to load headlessly.",
|
|
31728
|
+
`Install the plugin in Cursor with ${ADD_PLUGIN_CMD}, or run from the monorepo checkout so bitfab-cursor-plugin is available.`
|
|
31729
|
+
].join("\n")
|
|
31730
|
+
);
|
|
31731
|
+
}
|
|
31732
|
+
const logPath = analyzeRepoLogPath3();
|
|
31733
|
+
const agentRunId = crypto12.randomUUID();
|
|
31734
|
+
const env = { ...process.env };
|
|
31735
|
+
env.BITFAB_AGENT_RUN_ID = agentRunId;
|
|
31736
|
+
if (captureOverride !== void 0) {
|
|
31737
|
+
env.BITFAB_CAPTURE_SESSIONS = captureOverride ? "1" : "0";
|
|
31738
|
+
}
|
|
31739
|
+
const command = limit === void 0 ? ANALYZE_REPO_COMMAND3 : `${ANALYZE_REPO_COMMAND3} limit=${limit}`;
|
|
31740
|
+
const out = fs22.createWriteStream(logPath);
|
|
31741
|
+
const spinner6 = p5.spinner();
|
|
31742
|
+
spinner6.start("Analyzing repo and drafting trace plans");
|
|
31743
|
+
const stdoutChunks = [];
|
|
31744
|
+
const stdoutRedactor = new StreamingSecretRedactor();
|
|
31745
|
+
const stderrRedactor = new StreamingSecretRedactor();
|
|
31746
|
+
let redactorsFlushed = false;
|
|
31747
|
+
const flushRedactors = () => {
|
|
31748
|
+
if (redactorsFlushed) {
|
|
31749
|
+
return;
|
|
31750
|
+
}
|
|
31751
|
+
redactorsFlushed = true;
|
|
31752
|
+
const stdoutTail = stdoutRedactor.flush();
|
|
31753
|
+
if (stdoutTail !== "") {
|
|
31754
|
+
out.write(stdoutTail);
|
|
31755
|
+
stdoutChunks.push(stdoutTail);
|
|
31756
|
+
}
|
|
31757
|
+
const stderrTail = stderrRedactor.flush();
|
|
31758
|
+
if (stderrTail !== "") {
|
|
31759
|
+
out.write(stderrTail);
|
|
31760
|
+
}
|
|
31761
|
+
};
|
|
31762
|
+
try {
|
|
31763
|
+
const exitCode = await new Promise((resolve2, reject) => {
|
|
31764
|
+
const child = spawn7(
|
|
31765
|
+
"cursor",
|
|
31766
|
+
[
|
|
31767
|
+
"agent",
|
|
31768
|
+
"--print",
|
|
31769
|
+
"--output-format",
|
|
31770
|
+
"stream-json",
|
|
31771
|
+
"--trust",
|
|
31772
|
+
"--approve-mcps",
|
|
31773
|
+
"--force",
|
|
31774
|
+
"--plugin-dir",
|
|
31775
|
+
pluginDir,
|
|
31776
|
+
"--workspace",
|
|
31777
|
+
process.cwd(),
|
|
31778
|
+
command
|
|
31779
|
+
],
|
|
31780
|
+
{ stdio: ["ignore", "pipe", "pipe"], env, ...SHELL_OPTS }
|
|
31781
|
+
);
|
|
31782
|
+
child.stdout.on("data", (chunk2) => {
|
|
31783
|
+
const text = chunk2.toString();
|
|
31784
|
+
const redacted = stdoutRedactor.redact(text);
|
|
31785
|
+
if (redacted !== "") {
|
|
31786
|
+
out.write(redacted);
|
|
31787
|
+
stdoutChunks.push(redacted);
|
|
31788
|
+
}
|
|
31789
|
+
const label = latestCursorActivityLabel(text);
|
|
31790
|
+
if (label) {
|
|
31791
|
+
spinner6.message(label);
|
|
31792
|
+
}
|
|
31793
|
+
});
|
|
31794
|
+
child.stderr.on(
|
|
31795
|
+
"data",
|
|
31796
|
+
(chunk2) => out.write(stderrRedactor.redact(chunk2.toString()))
|
|
31797
|
+
);
|
|
31798
|
+
child.on("error", reject);
|
|
31799
|
+
child.on("close", (code, signal) => {
|
|
31800
|
+
if (code === null) {
|
|
31801
|
+
reject(
|
|
31802
|
+
new Error(
|
|
31803
|
+
`analyze-repo terminated by signal ${signal ?? "unknown"}. See ${logPath}`
|
|
31804
|
+
)
|
|
31805
|
+
);
|
|
31806
|
+
return;
|
|
31807
|
+
}
|
|
31808
|
+
resolve2(code);
|
|
31809
|
+
});
|
|
31810
|
+
});
|
|
31811
|
+
flushRedactors();
|
|
31812
|
+
if (exitCode !== 0) {
|
|
31813
|
+
throw new Error(
|
|
31814
|
+
`analyze-repo exited with status ${exitCode}. See ${logPath}`
|
|
31815
|
+
);
|
|
31816
|
+
}
|
|
31817
|
+
const uploaded = countUploadedTracePlans3(stdoutChunks.join(""));
|
|
31818
|
+
spinner6.stop(
|
|
31819
|
+
uploaded === 0 ? "No AI workflows found - nothing to upload" : `Uploaded ${uploaded} draft trace plan${uploaded === 1 ? "" : "s"} to Bitfab`
|
|
31820
|
+
);
|
|
31821
|
+
} catch (err) {
|
|
31822
|
+
spinner6.stop("analyze-repo failed");
|
|
31823
|
+
throw err;
|
|
31824
|
+
} finally {
|
|
31825
|
+
flushRedactors();
|
|
31826
|
+
out.end();
|
|
31827
|
+
}
|
|
31828
|
+
p5.log.info(`Full run log: ${logPath}`);
|
|
31829
|
+
}
|
|
31830
|
+
function latestCursorActivityLabel(chunk2) {
|
|
31831
|
+
const lines = chunk2.split("\n").filter((l) => l.trim() !== "");
|
|
31832
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
31833
|
+
let event;
|
|
31834
|
+
try {
|
|
31835
|
+
event = JSON.parse(lines[i]);
|
|
31836
|
+
} catch {
|
|
31837
|
+
continue;
|
|
31838
|
+
}
|
|
31839
|
+
const label = cursorActivityLabelFromEvent(event);
|
|
31840
|
+
if (label) {
|
|
31841
|
+
return label;
|
|
31842
|
+
}
|
|
31843
|
+
}
|
|
31844
|
+
return null;
|
|
31845
|
+
}
|
|
31846
|
+
function cursorActivityLabelFromEvent(event) {
|
|
31847
|
+
const toolCall = eventToolCall(event);
|
|
31848
|
+
if (!toolCall) {
|
|
31849
|
+
return null;
|
|
31850
|
+
}
|
|
31851
|
+
const mcpToolName = mcpToolCallName(toolCall);
|
|
31852
|
+
if (mcpToolName?.includes("create_trace_plan")) {
|
|
31853
|
+
return "Uploading a draft trace plan";
|
|
31854
|
+
}
|
|
31855
|
+
if (mcpToolName?.includes("get_bitfab_api_key")) {
|
|
31856
|
+
return "Checking authentication";
|
|
31857
|
+
}
|
|
31858
|
+
if ("grepToolCall" in toolCall) {
|
|
31859
|
+
return "Searching the codebase";
|
|
31860
|
+
}
|
|
31861
|
+
if ("readToolCall" in toolCall) {
|
|
31862
|
+
return "Reading files";
|
|
31863
|
+
}
|
|
31864
|
+
return null;
|
|
31865
|
+
}
|
|
31866
|
+
function eventToolCall(event) {
|
|
31867
|
+
if (typeof event !== "object" || event === null) {
|
|
31868
|
+
return null;
|
|
31869
|
+
}
|
|
31870
|
+
const toolCall = event.tool_call;
|
|
31871
|
+
return typeof toolCall === "object" && toolCall !== null ? toolCall : null;
|
|
31872
|
+
}
|
|
31873
|
+
function mcpToolCallName(toolCall) {
|
|
31874
|
+
const mcpToolCall = toolCall.mcpToolCall;
|
|
31875
|
+
if (typeof mcpToolCall !== "object" || mcpToolCall === null) {
|
|
31876
|
+
return null;
|
|
31877
|
+
}
|
|
31878
|
+
const call = mcpToolCall;
|
|
31879
|
+
const args = call.args;
|
|
31880
|
+
const names = [
|
|
31881
|
+
call.toolName,
|
|
31882
|
+
call.name,
|
|
31883
|
+
typeof args === "object" && args !== null ? args.name : void 0
|
|
31884
|
+
];
|
|
31885
|
+
return names.find((name) => typeof name === "string") ?? null;
|
|
31886
|
+
}
|
|
31887
|
+
function countUploadedTracePlans3(logText) {
|
|
31888
|
+
let completedPlanCalls = 0;
|
|
31889
|
+
for (const line of logText.split("\n")) {
|
|
31890
|
+
if (line.trim() === "") {
|
|
31891
|
+
continue;
|
|
31892
|
+
}
|
|
31893
|
+
let event;
|
|
31894
|
+
try {
|
|
31895
|
+
event = JSON.parse(line);
|
|
31896
|
+
} catch {
|
|
31897
|
+
continue;
|
|
31898
|
+
}
|
|
31899
|
+
if (isSuccessfulCreateTracePlanEvent(event)) {
|
|
31900
|
+
completedPlanCalls++;
|
|
31901
|
+
}
|
|
31902
|
+
}
|
|
31903
|
+
if (completedPlanCalls > 0) {
|
|
31904
|
+
return completedPlanCalls;
|
|
31905
|
+
}
|
|
31906
|
+
return 0;
|
|
31907
|
+
}
|
|
31908
|
+
function isSuccessfulCreateTracePlanEvent(event) {
|
|
31909
|
+
if (typeof event !== "object" || event === null) {
|
|
31910
|
+
return false;
|
|
31911
|
+
}
|
|
31912
|
+
const e = event;
|
|
31913
|
+
if (e.type !== "tool_call" || e.subtype !== "completed") {
|
|
31914
|
+
return false;
|
|
31915
|
+
}
|
|
31916
|
+
const toolCall = e.tool_call;
|
|
31917
|
+
if (typeof toolCall !== "object" || toolCall === null) {
|
|
31918
|
+
return false;
|
|
31919
|
+
}
|
|
31920
|
+
const mcpToolCall = toolCall.mcpToolCall;
|
|
31921
|
+
if (typeof mcpToolCall !== "object" || mcpToolCall === null) {
|
|
31922
|
+
return false;
|
|
31923
|
+
}
|
|
31924
|
+
const call = mcpToolCall;
|
|
31925
|
+
const args = call.args;
|
|
31926
|
+
const names = [
|
|
31927
|
+
call.toolName,
|
|
31928
|
+
call.name,
|
|
31929
|
+
typeof args === "object" && args !== null ? args.name : void 0
|
|
31930
|
+
];
|
|
31931
|
+
return names.some(
|
|
31932
|
+
(name) => typeof name === "string" && name.includes("create_trace_plan")
|
|
31933
|
+
) && hasSuccessfulToolResult(call.result);
|
|
31934
|
+
}
|
|
31935
|
+
function hasSuccessfulToolResult(result) {
|
|
31936
|
+
return typeof result === "object" && result !== null && "success" in result && !("error" in result);
|
|
31937
|
+
}
|
|
31938
|
+
function resolveCursorPluginDir() {
|
|
31939
|
+
const candidates = [
|
|
31940
|
+
path19.join(process.cwd(), "bitfab-cursor-plugin"),
|
|
31941
|
+
path19.join(os17.homedir(), ".cursor", "plugins", "local", "bitfab-dev"),
|
|
31942
|
+
path19.join(os17.homedir(), ".cursor", "plugins", "local", "bitfab")
|
|
31943
|
+
];
|
|
31944
|
+
for (const candidate of candidates) {
|
|
31945
|
+
if (fs22.existsSync(path19.join(candidate, ".cursor-plugin", "plugin.json")) && fs22.existsSync(path19.join(candidate, "mcp.json"))) {
|
|
31946
|
+
return candidate;
|
|
31947
|
+
}
|
|
31948
|
+
}
|
|
31949
|
+
return null;
|
|
31950
|
+
}
|
|
31951
|
+
function analyzeRepoLogPath3() {
|
|
31952
|
+
const dir = path19.join(process.cwd(), ".bitfab", "logs");
|
|
31953
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
31954
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
31955
|
+
return path19.join(dir, `analyze-repo-${stamp}.jsonl`);
|
|
31426
31956
|
}
|
|
31427
31957
|
function runCursorAssistant(args, skipPermissions) {
|
|
31428
31958
|
ensureCursorAuthenticated();
|
|
@@ -31498,12 +32028,12 @@ var platform = {
|
|
|
31498
32028
|
};
|
|
31499
32029
|
|
|
31500
32030
|
// src/version.ts
|
|
31501
|
-
import
|
|
31502
|
-
import
|
|
32031
|
+
import fs23 from "fs";
|
|
32032
|
+
import path20 from "path";
|
|
31503
32033
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
31504
32034
|
var CLI_VERSION = JSON.parse(
|
|
31505
|
-
|
|
31506
|
-
|
|
32035
|
+
fs23.readFileSync(
|
|
32036
|
+
path20.join(path20.dirname(fileURLToPath4(import.meta.url)), "../package.json"),
|
|
31507
32037
|
"utf-8"
|
|
31508
32038
|
)
|
|
31509
32039
|
).version;
|
|
@@ -31669,10 +32199,10 @@ async function runAnalyzeRepo(opts) {
|
|
|
31669
32199
|
p6.intro("Bitfab");
|
|
31670
32200
|
const chosen = await resolveEditor(opts);
|
|
31671
32201
|
p6.log.step(`Preparing ${ADAPTERS[chosen].label} plugin`);
|
|
31672
|
-
|
|
31673
|
-
|
|
31674
|
-
|
|
31675
|
-
|
|
32202
|
+
if (chosen === "cursor") {
|
|
32203
|
+
ADAPTERS[chosen].ensureLatest();
|
|
32204
|
+
} else {
|
|
32205
|
+
ADAPTERS[chosen].install();
|
|
31676
32206
|
}
|
|
31677
32207
|
await ensureAnalyzeRepoAuth();
|
|
31678
32208
|
const captureOverride = await resolveCaptureOverride(opts.uploadLogs);
|