mobbdev 1.2.53 → 1.2.55
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.mjs
CHANGED
|
@@ -9918,7 +9918,7 @@ var _contextLogger = null;
|
|
|
9918
9918
|
var createContextLogger = async () => {
|
|
9919
9919
|
if (_contextLogger) return _contextLogger;
|
|
9920
9920
|
try {
|
|
9921
|
-
let
|
|
9921
|
+
let logger3;
|
|
9922
9922
|
try {
|
|
9923
9923
|
let module;
|
|
9924
9924
|
try {
|
|
@@ -9928,15 +9928,15 @@ var createContextLogger = async () => {
|
|
|
9928
9928
|
const sourcePath = "../../../../../tscommon/backend/src/utils/logger";
|
|
9929
9929
|
module = await import(sourcePath);
|
|
9930
9930
|
}
|
|
9931
|
-
|
|
9931
|
+
logger3 = module.logger;
|
|
9932
9932
|
} catch {
|
|
9933
9933
|
}
|
|
9934
|
-
if (
|
|
9934
|
+
if (logger3) {
|
|
9935
9935
|
_contextLogger = {
|
|
9936
|
-
info: (message, data) => data ?
|
|
9937
|
-
warn: (message, data) => data ?
|
|
9938
|
-
debug: (message, data) => data ?
|
|
9939
|
-
error: (message, data) => data ?
|
|
9936
|
+
info: (message, data) => data ? logger3.info(data, message) : logger3.info(message),
|
|
9937
|
+
warn: (message, data) => data ? logger3.warn(data, message) : logger3.warn(message),
|
|
9938
|
+
debug: (message, data) => data ? logger3.debug(data, message) : logger3.debug(message),
|
|
9939
|
+
error: (message, data) => data ? logger3.error(data, message) : logger3.error(message)
|
|
9940
9940
|
};
|
|
9941
9941
|
return _contextLogger;
|
|
9942
9942
|
}
|
|
@@ -9952,20 +9952,20 @@ var createContextLogger = async () => {
|
|
|
9952
9952
|
};
|
|
9953
9953
|
var contextLogger = {
|
|
9954
9954
|
info: async (message, data) => {
|
|
9955
|
-
const
|
|
9956
|
-
return
|
|
9955
|
+
const logger3 = await createContextLogger();
|
|
9956
|
+
return logger3.info(message, data);
|
|
9957
9957
|
},
|
|
9958
9958
|
debug: async (message, data) => {
|
|
9959
|
-
const
|
|
9960
|
-
return
|
|
9959
|
+
const logger3 = await createContextLogger();
|
|
9960
|
+
return logger3.debug(message, data);
|
|
9961
9961
|
},
|
|
9962
9962
|
warn: async (message, data) => {
|
|
9963
|
-
const
|
|
9964
|
-
return
|
|
9963
|
+
const logger3 = await createContextLogger();
|
|
9964
|
+
return logger3.warn(message, data);
|
|
9965
9965
|
},
|
|
9966
9966
|
error: async (message, data) => {
|
|
9967
|
-
const
|
|
9968
|
-
return
|
|
9967
|
+
const logger3 = await createContextLogger();
|
|
9968
|
+
return logger3.error(message, data);
|
|
9969
9969
|
}
|
|
9970
9970
|
};
|
|
9971
9971
|
|
|
@@ -11313,12 +11313,12 @@ var defaultLogger = {
|
|
|
11313
11313
|
}
|
|
11314
11314
|
}
|
|
11315
11315
|
};
|
|
11316
|
-
function createGitWithLogging(dirName,
|
|
11316
|
+
function createGitWithLogging(dirName, logger3 = defaultLogger) {
|
|
11317
11317
|
return simpleGit2(dirName, {
|
|
11318
11318
|
maxConcurrentProcesses: 6
|
|
11319
11319
|
}).outputHandler((bin, stdout2, stderr2) => {
|
|
11320
11320
|
const callID = Math.random();
|
|
11321
|
-
|
|
11321
|
+
logger3.info({ callID, bin }, "Start git CLI call");
|
|
11322
11322
|
let errChunks = [];
|
|
11323
11323
|
let outChunks = [];
|
|
11324
11324
|
let isStdoutDone = false;
|
|
@@ -11339,25 +11339,25 @@ function createGitWithLogging(dirName, logger2 = defaultLogger) {
|
|
|
11339
11339
|
err: `${errChunks.join("").slice(0, 200)}...`,
|
|
11340
11340
|
out: `${outChunks.join("").slice(0, 200)}...`
|
|
11341
11341
|
};
|
|
11342
|
-
|
|
11342
|
+
logger3.info(logObj, "git log output");
|
|
11343
11343
|
stderr2.removeListener("data", onStderrData);
|
|
11344
11344
|
stdout2.removeListener("data", onStdoutData);
|
|
11345
11345
|
errChunks = [];
|
|
11346
11346
|
outChunks = [];
|
|
11347
11347
|
}
|
|
11348
|
-
function markDone(
|
|
11349
|
-
if (
|
|
11348
|
+
function markDone(stream3) {
|
|
11349
|
+
if (stream3 === "stderr") isStderrDone = true;
|
|
11350
11350
|
else isStdoutDone = true;
|
|
11351
11351
|
finalizeAndCleanup();
|
|
11352
11352
|
}
|
|
11353
11353
|
stderr2.on("close", () => markDone("stderr"));
|
|
11354
11354
|
stdout2.on("close", () => markDone("stdout"));
|
|
11355
11355
|
stderr2.on("error", (error) => {
|
|
11356
|
-
|
|
11356
|
+
logger3.info({ callID, error: String(error) }, "git stderr stream error");
|
|
11357
11357
|
markDone("stderr");
|
|
11358
11358
|
});
|
|
11359
11359
|
stdout2.on("error", (error) => {
|
|
11360
|
-
|
|
11360
|
+
logger3.info({ callID, error: String(error) }, "git stdout stream error");
|
|
11361
11361
|
markDone("stdout");
|
|
11362
11362
|
});
|
|
11363
11363
|
});
|
|
@@ -13014,9 +13014,9 @@ async function uploadFile({
|
|
|
13014
13014
|
url,
|
|
13015
13015
|
uploadKey,
|
|
13016
13016
|
uploadFields,
|
|
13017
|
-
logger:
|
|
13017
|
+
logger: logger3
|
|
13018
13018
|
}) {
|
|
13019
|
-
const logInfo2 =
|
|
13019
|
+
const logInfo2 = logger3 || ((_message, _data) => {
|
|
13020
13020
|
});
|
|
13021
13021
|
logInfo2(`FileUpload: upload file start ${url}`);
|
|
13022
13022
|
logInfo2(`FileUpload: upload fields`, uploadFields);
|
|
@@ -13059,22 +13059,27 @@ import os from "os";
|
|
|
13059
13059
|
|
|
13060
13060
|
// src/utils/ConfigStoreService.ts
|
|
13061
13061
|
import Configstore from "configstore";
|
|
13062
|
-
function
|
|
13062
|
+
function getSanitizedDomain() {
|
|
13063
13063
|
const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
|
|
13064
13064
|
let domain = "";
|
|
13065
13065
|
try {
|
|
13066
13066
|
const url = new URL(API_URL2);
|
|
13067
13067
|
domain = url.hostname;
|
|
13068
|
-
} catch
|
|
13068
|
+
} catch {
|
|
13069
13069
|
domain = API_URL2.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/:\d+$/, "");
|
|
13070
13070
|
}
|
|
13071
|
-
|
|
13072
|
-
|
|
13071
|
+
return domain.replace(/\./g, "_");
|
|
13072
|
+
}
|
|
13073
|
+
function createConfigStore(defaultValues = { apiToken: "" }) {
|
|
13074
|
+
return new Configstore(`mobbdev-${getSanitizedDomain()}`, defaultValues);
|
|
13073
13075
|
}
|
|
13074
|
-
function
|
|
13075
|
-
return
|
|
13076
|
+
function createSessionConfigStore(sessionId) {
|
|
13077
|
+
return new Configstore(`mobbdev-${getSanitizedDomain()}-session-${sessionId}`);
|
|
13076
13078
|
}
|
|
13077
|
-
|
|
13079
|
+
function getSessionFilePrefix() {
|
|
13080
|
+
return `mobbdev-${getSanitizedDomain()}-session-`;
|
|
13081
|
+
}
|
|
13082
|
+
var configStore = createConfigStore();
|
|
13078
13083
|
|
|
13079
13084
|
// src/utils/computerName.ts
|
|
13080
13085
|
var STABLE_COMPUTER_NAME_CONFIG_KEY = "stableComputerName";
|
|
@@ -13305,6 +13310,10 @@ async function sanitizeDataWithCounts(obj) {
|
|
|
13305
13310
|
const sanitizedData = await sanitizeRecursive(obj);
|
|
13306
13311
|
return { sanitizedData, counts };
|
|
13307
13312
|
}
|
|
13313
|
+
async function sanitizeData(obj) {
|
|
13314
|
+
const result = await sanitizeDataWithCounts(obj);
|
|
13315
|
+
return result.sanitizedData;
|
|
13316
|
+
}
|
|
13308
13317
|
|
|
13309
13318
|
// src/args/commands/upload_ai_blame.ts
|
|
13310
13319
|
var defaultLogger2 = {
|
|
@@ -13358,9 +13367,9 @@ var PromptItemZ = z27.object({
|
|
|
13358
13367
|
}).optional()
|
|
13359
13368
|
});
|
|
13360
13369
|
var PromptItemArrayZ = z27.array(PromptItemZ);
|
|
13361
|
-
async function getRepositoryUrl() {
|
|
13370
|
+
async function getRepositoryUrl(workingDir) {
|
|
13362
13371
|
try {
|
|
13363
|
-
const gitService = new GitService(process.cwd());
|
|
13372
|
+
const gitService = new GitService(workingDir ?? process.cwd());
|
|
13364
13373
|
const isRepo = await gitService.isGitRepository();
|
|
13365
13374
|
if (!isRepo) {
|
|
13366
13375
|
return null;
|
|
@@ -13421,6 +13430,7 @@ function uploadAiBlameBuilder(args) {
|
|
|
13421
13430
|
}).strict();
|
|
13422
13431
|
}
|
|
13423
13432
|
async function uploadAiBlameHandlerFromExtension(args) {
|
|
13433
|
+
const shouldSanitize = args.sanitize ?? true;
|
|
13424
13434
|
const uploadArgs = {
|
|
13425
13435
|
prompt: [],
|
|
13426
13436
|
inference: [],
|
|
@@ -13434,28 +13444,50 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
13434
13444
|
let inferenceCounts;
|
|
13435
13445
|
let promptsUUID;
|
|
13436
13446
|
let inferenceUUID;
|
|
13447
|
+
const zeroCounts = {
|
|
13448
|
+
detections: { total: 0, high: 0, medium: 0, low: 0 }
|
|
13449
|
+
};
|
|
13450
|
+
let sanitizationDurationMs;
|
|
13437
13451
|
await withFile(async (promptFile) => {
|
|
13438
|
-
const promptsResult = await sanitizeDataWithCounts(args.prompts);
|
|
13439
|
-
promptsCounts = promptsResult.counts;
|
|
13440
13452
|
promptsUUID = path7.basename(promptFile.path, path7.extname(promptFile.path));
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13444
|
-
|
|
13445
|
-
|
|
13453
|
+
if (shouldSanitize) {
|
|
13454
|
+
const sanitizeStart = performance.now();
|
|
13455
|
+
const promptsResult = await sanitizeDataWithCounts(args.prompts);
|
|
13456
|
+
promptsCounts = promptsResult.counts;
|
|
13457
|
+
await fsPromises2.writeFile(
|
|
13458
|
+
promptFile.path,
|
|
13459
|
+
JSON.stringify(promptsResult.sanitizedData, null, 2),
|
|
13460
|
+
"utf-8"
|
|
13461
|
+
);
|
|
13462
|
+
sanitizationDurationMs = performance.now() - sanitizeStart;
|
|
13463
|
+
} else {
|
|
13464
|
+
promptsCounts = zeroCounts;
|
|
13465
|
+
await fsPromises2.writeFile(
|
|
13466
|
+
promptFile.path,
|
|
13467
|
+
JSON.stringify(args.prompts, null, 2),
|
|
13468
|
+
"utf-8"
|
|
13469
|
+
);
|
|
13470
|
+
}
|
|
13446
13471
|
uploadArgs.prompt.push(promptFile.path);
|
|
13447
13472
|
await withFile(async (inferenceFile) => {
|
|
13448
|
-
const inferenceResult = await sanitizeDataWithCounts(args.inference);
|
|
13449
|
-
inferenceCounts = inferenceResult.counts;
|
|
13450
13473
|
inferenceUUID = path7.basename(
|
|
13451
13474
|
inferenceFile.path,
|
|
13452
13475
|
path7.extname(inferenceFile.path)
|
|
13453
13476
|
);
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
inferenceResult.
|
|
13457
|
-
|
|
13458
|
-
|
|
13477
|
+
if (shouldSanitize) {
|
|
13478
|
+
const inferenceStart = performance.now();
|
|
13479
|
+
const inferenceResult = await sanitizeDataWithCounts(args.inference);
|
|
13480
|
+
inferenceCounts = inferenceResult.counts;
|
|
13481
|
+
await fsPromises2.writeFile(
|
|
13482
|
+
inferenceFile.path,
|
|
13483
|
+
inferenceResult.sanitizedData,
|
|
13484
|
+
"utf-8"
|
|
13485
|
+
);
|
|
13486
|
+
sanitizationDurationMs = (sanitizationDurationMs ?? 0) + (performance.now() - inferenceStart);
|
|
13487
|
+
} else {
|
|
13488
|
+
inferenceCounts = zeroCounts;
|
|
13489
|
+
await fsPromises2.writeFile(inferenceFile.path, args.inference, "utf-8");
|
|
13490
|
+
}
|
|
13459
13491
|
uploadArgs.inference.push(inferenceFile.path);
|
|
13460
13492
|
uploadArgs.model.push(args.model);
|
|
13461
13493
|
uploadArgs.toolName.push(args.tool);
|
|
@@ -13477,7 +13509,8 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
13477
13509
|
promptsCounts,
|
|
13478
13510
|
inferenceCounts,
|
|
13479
13511
|
promptsUUID,
|
|
13480
|
-
inferenceUUID
|
|
13512
|
+
inferenceUUID,
|
|
13513
|
+
sanitizationDurationMs
|
|
13481
13514
|
};
|
|
13482
13515
|
}
|
|
13483
13516
|
async function uploadAiBlameHandler(options) {
|
|
@@ -13486,7 +13519,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
13486
13519
|
exitOnError = true,
|
|
13487
13520
|
apiUrl,
|
|
13488
13521
|
webAppUrl,
|
|
13489
|
-
logger:
|
|
13522
|
+
logger: logger3 = defaultLogger2
|
|
13490
13523
|
} = options;
|
|
13491
13524
|
const prompts = args.prompt || [];
|
|
13492
13525
|
const inferences = args.inference || [];
|
|
@@ -13497,7 +13530,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
13497
13530
|
const sessionIds = args.sessionId || args["session-id"] || [];
|
|
13498
13531
|
if (prompts.length !== inferences.length) {
|
|
13499
13532
|
const errorMsg = "prompt and inference must have the same number of entries";
|
|
13500
|
-
|
|
13533
|
+
logger3.error(chalk4.red(errorMsg));
|
|
13501
13534
|
if (exitOnError) {
|
|
13502
13535
|
process.exit(1);
|
|
13503
13536
|
}
|
|
@@ -13517,7 +13550,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
13517
13550
|
]);
|
|
13518
13551
|
} catch {
|
|
13519
13552
|
const errorMsg = `File not found for session ${i + 1}`;
|
|
13520
|
-
|
|
13553
|
+
logger3.error(chalk4.red(errorMsg));
|
|
13521
13554
|
if (exitOnError) {
|
|
13522
13555
|
process.exit(1);
|
|
13523
13556
|
}
|
|
@@ -13550,7 +13583,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
13550
13583
|
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
13551
13584
|
if (uploadSessions.length !== sessions.length) {
|
|
13552
13585
|
const errorMsg = "Init failed to return expected number of sessions";
|
|
13553
|
-
|
|
13586
|
+
logger3.error(chalk4.red(errorMsg));
|
|
13554
13587
|
if (exitOnError) {
|
|
13555
13588
|
process.exit(1);
|
|
13556
13589
|
}
|
|
@@ -13594,7 +13627,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
13594
13627
|
};
|
|
13595
13628
|
});
|
|
13596
13629
|
try {
|
|
13597
|
-
|
|
13630
|
+
logger3.info(
|
|
13598
13631
|
`[UPLOAD] Calling finalizeAIBlameInferencesUploadRaw with ${finalizeSessions.length} sessions`
|
|
13599
13632
|
);
|
|
13600
13633
|
const finRes = await authenticatedClient.finalizeAIBlameInferencesUploadRaw(
|
|
@@ -13602,11 +13635,11 @@ async function uploadAiBlameHandler(options) {
|
|
|
13602
13635
|
sessions: finalizeSessions
|
|
13603
13636
|
}
|
|
13604
13637
|
);
|
|
13605
|
-
|
|
13638
|
+
logger3.info("[UPLOAD] Finalize response:", JSON.stringify(finRes, null, 2));
|
|
13606
13639
|
const status = finRes?.finalizeAIBlameInferencesUpload?.status;
|
|
13607
13640
|
if (status !== "OK") {
|
|
13608
13641
|
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
13609
|
-
|
|
13642
|
+
logger3.error(
|
|
13610
13643
|
chalk4.red(
|
|
13611
13644
|
`[UPLOAD] Finalize failed with status: ${status}, error: ${errorMsg}`
|
|
13612
13645
|
)
|
|
@@ -13616,9 +13649,9 @@ async function uploadAiBlameHandler(options) {
|
|
|
13616
13649
|
}
|
|
13617
13650
|
throw new Error(errorMsg);
|
|
13618
13651
|
}
|
|
13619
|
-
|
|
13652
|
+
logger3.info(chalk4.green("[UPLOAD] AI Blame uploads finalized successfully"));
|
|
13620
13653
|
} catch (error) {
|
|
13621
|
-
|
|
13654
|
+
logger3.error("[UPLOAD] Finalize threw error:", error);
|
|
13622
13655
|
throw error;
|
|
13623
13656
|
}
|
|
13624
13657
|
}
|
|
@@ -13628,6 +13661,110 @@ async function uploadAiBlameCommandHandler(args) {
|
|
|
13628
13661
|
|
|
13629
13662
|
// src/features/analysis/graphql/tracy-batch-upload.ts
|
|
13630
13663
|
var debug10 = Debug9("mobbdev:tracy-batch-upload");
|
|
13664
|
+
async function sanitizeRawData(rawData) {
|
|
13665
|
+
try {
|
|
13666
|
+
const sanitized = await sanitizeData(rawData);
|
|
13667
|
+
return JSON.stringify(sanitized);
|
|
13668
|
+
} catch (err) {
|
|
13669
|
+
console.warn(
|
|
13670
|
+
"[tracy] sanitizeRawData failed, falling back to unsanitized:",
|
|
13671
|
+
err.message
|
|
13672
|
+
);
|
|
13673
|
+
return JSON.stringify(rawData);
|
|
13674
|
+
}
|
|
13675
|
+
}
|
|
13676
|
+
async function prepareAndSendTracyRecords(client, rawRecords, workingDir) {
|
|
13677
|
+
const repositoryUrl = await getRepositoryUrl(workingDir);
|
|
13678
|
+
const { computerName, userName } = getSystemInfo();
|
|
13679
|
+
const clientVersion = packageJson.version;
|
|
13680
|
+
const serializedRawDataByIndex = /* @__PURE__ */ new Map();
|
|
13681
|
+
const records = await Promise.all(
|
|
13682
|
+
rawRecords.map(async (record, index) => {
|
|
13683
|
+
if (record.rawData != null) {
|
|
13684
|
+
const serialized = await sanitizeRawData(record.rawData);
|
|
13685
|
+
serializedRawDataByIndex.set(index, serialized);
|
|
13686
|
+
}
|
|
13687
|
+
const { rawData: _rawData, ...rest } = record;
|
|
13688
|
+
return {
|
|
13689
|
+
...rest,
|
|
13690
|
+
repositoryUrl: repositoryUrl ?? void 0,
|
|
13691
|
+
computerName,
|
|
13692
|
+
userName,
|
|
13693
|
+
clientVersion
|
|
13694
|
+
};
|
|
13695
|
+
})
|
|
13696
|
+
);
|
|
13697
|
+
const recordsWithRawData = rawRecords.map((r, i) => ({ recordId: r.recordId, index: i })).filter((entry) => serializedRawDataByIndex.has(entry.index));
|
|
13698
|
+
if (recordsWithRawData.length > 0) {
|
|
13699
|
+
debug10("Uploading %d rawData files to S3...", recordsWithRawData.length);
|
|
13700
|
+
const uploadUrlResult = await client.getTracyRawDataUploadUrl();
|
|
13701
|
+
const { url, uploadFieldsJSON, keyPrefix } = uploadUrlResult.getTracyRawDataUploadUrl;
|
|
13702
|
+
if (!url || !uploadFieldsJSON || !keyPrefix) {
|
|
13703
|
+
return {
|
|
13704
|
+
ok: false,
|
|
13705
|
+
errors: ["Failed to get S3 upload URL for rawData"]
|
|
13706
|
+
};
|
|
13707
|
+
}
|
|
13708
|
+
let uploadFields;
|
|
13709
|
+
try {
|
|
13710
|
+
uploadFields = JSON.parse(uploadFieldsJSON);
|
|
13711
|
+
} catch {
|
|
13712
|
+
return { ok: false, errors: ["Malformed uploadFieldsJSON from server"] };
|
|
13713
|
+
}
|
|
13714
|
+
const uploadResults = await Promise.allSettled(
|
|
13715
|
+
recordsWithRawData.map(async (entry) => {
|
|
13716
|
+
const rawDataJson = serializedRawDataByIndex.get(entry.index);
|
|
13717
|
+
if (!rawDataJson) {
|
|
13718
|
+
debug10("No serialized rawData for recordId=%s", entry.recordId);
|
|
13719
|
+
return;
|
|
13720
|
+
}
|
|
13721
|
+
const uploadKey = `${keyPrefix}${entry.recordId}.json`;
|
|
13722
|
+
await uploadFile({
|
|
13723
|
+
file: Buffer.from(rawDataJson, "utf-8"),
|
|
13724
|
+
url,
|
|
13725
|
+
uploadKey,
|
|
13726
|
+
uploadFields
|
|
13727
|
+
});
|
|
13728
|
+
records[entry.index].rawDataS3Key = uploadKey;
|
|
13729
|
+
})
|
|
13730
|
+
);
|
|
13731
|
+
const uploadErrors = uploadResults.filter((r) => r.status === "rejected").map((r) => r.reason.message);
|
|
13732
|
+
if (uploadErrors.length > 0) {
|
|
13733
|
+
debug10("S3 upload errors: %O", uploadErrors);
|
|
13734
|
+
}
|
|
13735
|
+
const missingS3Keys = recordsWithRawData.filter(
|
|
13736
|
+
(entry) => !records[entry.index].rawDataS3Key
|
|
13737
|
+
);
|
|
13738
|
+
if (missingS3Keys.length > 0) {
|
|
13739
|
+
const missingIds = missingS3Keys.map((e) => e.recordId);
|
|
13740
|
+
debug10("Records missing S3 keys after upload: %O", missingIds);
|
|
13741
|
+
return {
|
|
13742
|
+
ok: false,
|
|
13743
|
+
errors: [
|
|
13744
|
+
`Failed to upload rawData to S3 for ${missingS3Keys.length} record(s): ${missingIds.join(", ")}`,
|
|
13745
|
+
...uploadErrors
|
|
13746
|
+
]
|
|
13747
|
+
};
|
|
13748
|
+
}
|
|
13749
|
+
debug10("S3 uploads complete");
|
|
13750
|
+
}
|
|
13751
|
+
try {
|
|
13752
|
+
const result = await client.uploadTracyRecords({ records });
|
|
13753
|
+
if (result.uploadTracyRecords.status !== "OK") {
|
|
13754
|
+
return {
|
|
13755
|
+
ok: false,
|
|
13756
|
+
errors: [result.uploadTracyRecords.error ?? "Unknown server error"]
|
|
13757
|
+
};
|
|
13758
|
+
}
|
|
13759
|
+
} catch (err) {
|
|
13760
|
+
debug10("Upload failed: %s", err.message);
|
|
13761
|
+
return {
|
|
13762
|
+
ok: false,
|
|
13763
|
+
errors: [err.message]
|
|
13764
|
+
};
|
|
13765
|
+
}
|
|
13766
|
+
return { ok: true, errors: null };
|
|
13767
|
+
}
|
|
13631
13768
|
|
|
13632
13769
|
// src/mcp/services/types.ts
|
|
13633
13770
|
function detectIDE() {
|
|
@@ -14845,8 +14982,8 @@ if (typeof __filename !== "undefined") {
|
|
|
14845
14982
|
}
|
|
14846
14983
|
var costumeRequire = createRequire(moduleUrl);
|
|
14847
14984
|
var getCheckmarxPath = () => {
|
|
14848
|
-
const
|
|
14849
|
-
const cxFileName =
|
|
14985
|
+
const os14 = type();
|
|
14986
|
+
const cxFileName = os14 === "Windows_NT" ? "cx.exe" : "cx";
|
|
14850
14987
|
try {
|
|
14851
14988
|
return costumeRequire.resolve(`.bin/${cxFileName}`);
|
|
14852
14989
|
} catch (e) {
|
|
@@ -16159,229 +16296,383 @@ async function analyzeHandler(args) {
|
|
|
16159
16296
|
}
|
|
16160
16297
|
|
|
16161
16298
|
// src/features/claude_code/data_collector.ts
|
|
16299
|
+
import { createHash as createHash2 } from "crypto";
|
|
16300
|
+
import { open as open4, readdir, readFile, unlink } from "fs/promises";
|
|
16301
|
+
import path13 from "path";
|
|
16162
16302
|
import { z as z33 } from "zod";
|
|
16163
16303
|
init_client_generates();
|
|
16164
|
-
init_GitService();
|
|
16165
|
-
init_urlParser2();
|
|
16166
16304
|
|
|
16167
|
-
// src/utils/
|
|
16168
|
-
import
|
|
16169
|
-
import
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16305
|
+
// src/utils/shared-logger/create-logger.ts
|
|
16306
|
+
import Configstore2 from "configstore";
|
|
16307
|
+
import pino from "pino";
|
|
16308
|
+
|
|
16309
|
+
// src/utils/shared-logger/configstore-stream.ts
|
|
16310
|
+
import { writeFileSync } from "fs";
|
|
16311
|
+
import * as stream from "stream";
|
|
16312
|
+
var DEFAULT_MAX_LOGS = 1e3;
|
|
16313
|
+
var DEFAULT_MAX_HEARTBEAT = 100;
|
|
16314
|
+
var LOGS_KEY = "logs";
|
|
16315
|
+
var HEARTBEAT_KEY = "heartbeat";
|
|
16316
|
+
function createConfigstoreStream(store, opts) {
|
|
16317
|
+
const maxLogs = opts.maxLogs ?? DEFAULT_MAX_LOGS;
|
|
16318
|
+
const maxHeartbeat = opts.maxHeartbeat ?? DEFAULT_MAX_HEARTBEAT;
|
|
16319
|
+
const buffer = [];
|
|
16320
|
+
const heartbeatBuffer = [];
|
|
16321
|
+
let scopePath = opts.scopePath;
|
|
16322
|
+
function storeKey(base) {
|
|
16323
|
+
return scopePath ? `${base}:${scopePath}` : base;
|
|
16324
|
+
}
|
|
16325
|
+
function writeToDisk(entries, key, max) {
|
|
16181
16326
|
try {
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
|
|
16185
|
-
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
} else {
|
|
16193
|
-
throw err;
|
|
16327
|
+
const existing = store.get(key) ?? [];
|
|
16328
|
+
existing.push(...entries);
|
|
16329
|
+
const trimmed = existing.length > max ? existing.slice(-max) : existing;
|
|
16330
|
+
store.set(key, trimmed);
|
|
16331
|
+
} catch {
|
|
16332
|
+
try {
|
|
16333
|
+
const lines = `${entries.map((e) => JSON.stringify(e)).join("\n")}
|
|
16334
|
+
`;
|
|
16335
|
+
writeFileSync(`${store.path}.fallback`, lines, { flag: "a" });
|
|
16336
|
+
} catch {
|
|
16194
16337
|
}
|
|
16195
16338
|
}
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
16339
|
+
}
|
|
16340
|
+
const writable = new stream.Writable({
|
|
16341
|
+
write(chunk, _encoding, callback) {
|
|
16342
|
+
callback();
|
|
16343
|
+
try {
|
|
16344
|
+
const parsed = JSON.parse(chunk.toString());
|
|
16345
|
+
const entry = {
|
|
16346
|
+
timestamp: parsed.time ? new Date(parsed.time).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
16347
|
+
level: parsed.level ?? "info",
|
|
16348
|
+
message: parsed.msg ?? "",
|
|
16349
|
+
...parsed.durationMs !== void 0 && {
|
|
16350
|
+
durationMs: parsed.durationMs
|
|
16351
|
+
},
|
|
16352
|
+
...parsed.data !== void 0 && { data: parsed.data }
|
|
16353
|
+
};
|
|
16354
|
+
const isHeartbeat = parsed.heartbeat === true;
|
|
16355
|
+
if (opts.buffered) {
|
|
16356
|
+
if (isHeartbeat) {
|
|
16357
|
+
heartbeatBuffer.push(entry);
|
|
16358
|
+
} else {
|
|
16359
|
+
buffer.push(entry);
|
|
16360
|
+
}
|
|
16361
|
+
} else {
|
|
16362
|
+
if (isHeartbeat) {
|
|
16363
|
+
writeToDisk([entry], storeKey(HEARTBEAT_KEY), maxHeartbeat);
|
|
16364
|
+
} else {
|
|
16365
|
+
writeToDisk([entry], storeKey(LOGS_KEY), maxLogs);
|
|
16366
|
+
}
|
|
16367
|
+
}
|
|
16368
|
+
} catch {
|
|
16200
16369
|
}
|
|
16201
16370
|
}
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16371
|
+
});
|
|
16372
|
+
function flush() {
|
|
16373
|
+
if (buffer.length > 0) {
|
|
16374
|
+
writeToDisk(buffer, storeKey(LOGS_KEY), maxLogs);
|
|
16375
|
+
buffer.length = 0;
|
|
16376
|
+
}
|
|
16377
|
+
if (heartbeatBuffer.length > 0) {
|
|
16378
|
+
writeToDisk(heartbeatBuffer, storeKey(HEARTBEAT_KEY), maxHeartbeat);
|
|
16379
|
+
heartbeatBuffer.length = 0;
|
|
16380
|
+
}
|
|
16381
|
+
}
|
|
16382
|
+
function setScopePath(path27) {
|
|
16383
|
+
scopePath = path27;
|
|
16384
|
+
}
|
|
16385
|
+
return { writable, flush, setScopePath };
|
|
16386
|
+
}
|
|
16387
|
+
|
|
16388
|
+
// src/utils/shared-logger/dd-batch.ts
|
|
16389
|
+
import * as stream2 from "stream";
|
|
16390
|
+
import * as util from "util";
|
|
16391
|
+
import fetch5 from "cross-fetch";
|
|
16392
|
+
var DD_BATCH_INTERVAL_MS = 5e3;
|
|
16393
|
+
var DD_BATCH_MAX_SIZE = 50;
|
|
16394
|
+
function createDdBatch(config2) {
|
|
16395
|
+
let batch = [];
|
|
16396
|
+
let flushTimer = null;
|
|
16397
|
+
let pendingFlush = null;
|
|
16398
|
+
let errorLogged = false;
|
|
16399
|
+
function flush() {
|
|
16400
|
+
if (batch.length === 0) return;
|
|
16401
|
+
const toSend = batch;
|
|
16402
|
+
batch = [];
|
|
16403
|
+
pendingFlush = fetch5("https://http-intake.logs.datadoghq.com/api/v2/logs", {
|
|
16404
|
+
method: "POST",
|
|
16405
|
+
headers: {
|
|
16406
|
+
"Content-Type": "application/json",
|
|
16407
|
+
"DD-API-KEY": config2.apiKey
|
|
16408
|
+
},
|
|
16409
|
+
body: JSON.stringify(toSend)
|
|
16410
|
+
}).then(() => void 0).catch((error) => {
|
|
16411
|
+
if (!errorLogged) {
|
|
16412
|
+
errorLogged = true;
|
|
16413
|
+
config2.onError?.(
|
|
16414
|
+
`Error sending log to Datadog (further errors suppressed): ${util.inspect(error)}`
|
|
16415
|
+
);
|
|
16416
|
+
}
|
|
16417
|
+
});
|
|
16418
|
+
}
|
|
16419
|
+
function enqueue(message) {
|
|
16420
|
+
if (!config2.apiKey) return;
|
|
16421
|
+
batch.push({
|
|
16422
|
+
hostname: config2.hostname,
|
|
16423
|
+
ddsource: config2.ddsource,
|
|
16424
|
+
service: config2.service,
|
|
16425
|
+
ddtags: config2.ddtags,
|
|
16426
|
+
message
|
|
16427
|
+
});
|
|
16428
|
+
if (!flushTimer) {
|
|
16429
|
+
flushTimer = setInterval(flush, DD_BATCH_INTERVAL_MS);
|
|
16430
|
+
if (config2.unrefTimer) {
|
|
16431
|
+
flushTimer.unref();
|
|
16432
|
+
}
|
|
16433
|
+
}
|
|
16434
|
+
if (batch.length >= DD_BATCH_MAX_SIZE) {
|
|
16435
|
+
flush();
|
|
16436
|
+
}
|
|
16437
|
+
}
|
|
16438
|
+
async function flushAsync() {
|
|
16439
|
+
flush();
|
|
16440
|
+
await pendingFlush;
|
|
16205
16441
|
}
|
|
16442
|
+
function dispose() {
|
|
16443
|
+
flush();
|
|
16444
|
+
if (flushTimer) {
|
|
16445
|
+
clearInterval(flushTimer);
|
|
16446
|
+
flushTimer = null;
|
|
16447
|
+
}
|
|
16448
|
+
}
|
|
16449
|
+
function createPinoStream() {
|
|
16450
|
+
return new stream2.Writable({
|
|
16451
|
+
write(chunk, _encoding, callback) {
|
|
16452
|
+
callback();
|
|
16453
|
+
enqueue(chunk.toString());
|
|
16454
|
+
}
|
|
16455
|
+
});
|
|
16456
|
+
}
|
|
16457
|
+
return { enqueue, flush, flushAsync, dispose, createPinoStream };
|
|
16206
16458
|
}
|
|
16207
16459
|
|
|
16208
|
-
// src/
|
|
16209
|
-
import
|
|
16210
|
-
|
|
16211
|
-
|
|
16212
|
-
|
|
16213
|
-
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16229
|
-
|
|
16230
|
-
|
|
16231
|
-
|
|
16232
|
-
|
|
16233
|
-
|
|
16234
|
-
|
|
16235
|
-
|
|
16236
|
-
|
|
16237
|
-
|
|
16238
|
-
|
|
16239
|
-
|
|
16240
|
-
|
|
16241
|
-
|
|
16242
|
-
|
|
16243
|
-
|
|
16244
|
-
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16460
|
+
// src/utils/shared-logger/hostname.ts
|
|
16461
|
+
import { createHash } from "crypto";
|
|
16462
|
+
import os4 from "os";
|
|
16463
|
+
function hashString(input) {
|
|
16464
|
+
return createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
16465
|
+
}
|
|
16466
|
+
function getPlainHostname() {
|
|
16467
|
+
try {
|
|
16468
|
+
return `${os4.userInfo().username}@${os4.hostname()}`;
|
|
16469
|
+
} catch {
|
|
16470
|
+
return `unknown@${os4.hostname()}`;
|
|
16471
|
+
}
|
|
16472
|
+
}
|
|
16473
|
+
function getHashedHostname() {
|
|
16474
|
+
try {
|
|
16475
|
+
return `${hashString(os4.userInfo().username)}@${hashString(os4.hostname())}`;
|
|
16476
|
+
} catch {
|
|
16477
|
+
return `unknown@${hashString(os4.hostname())}`;
|
|
16478
|
+
}
|
|
16479
|
+
}
|
|
16480
|
+
|
|
16481
|
+
// src/utils/shared-logger/create-logger.ts
|
|
16482
|
+
function createLogger(config2) {
|
|
16483
|
+
const {
|
|
16484
|
+
namespace,
|
|
16485
|
+
scopePath,
|
|
16486
|
+
buffered = true,
|
|
16487
|
+
maxLogs,
|
|
16488
|
+
maxHeartbeat,
|
|
16489
|
+
dd,
|
|
16490
|
+
additionalStreams = []
|
|
16491
|
+
} = config2;
|
|
16492
|
+
const store = new Configstore2(namespace, {});
|
|
16493
|
+
const csStream = createConfigstoreStream(store, {
|
|
16494
|
+
buffered,
|
|
16495
|
+
scopePath,
|
|
16496
|
+
maxLogs,
|
|
16497
|
+
maxHeartbeat
|
|
16498
|
+
});
|
|
16499
|
+
let ddBatch = null;
|
|
16500
|
+
if (dd) {
|
|
16501
|
+
const hostname = dd.hostnameMode === "hashed" ? getHashedHostname() : getPlainHostname();
|
|
16502
|
+
const ddConfig = {
|
|
16503
|
+
apiKey: dd.apiKey,
|
|
16504
|
+
ddsource: dd.ddsource,
|
|
16505
|
+
service: dd.service,
|
|
16506
|
+
ddtags: dd.ddtags,
|
|
16507
|
+
hostname,
|
|
16508
|
+
unrefTimer: dd.unrefTimer,
|
|
16509
|
+
onError: dd.onError
|
|
16510
|
+
};
|
|
16511
|
+
ddBatch = createDdBatch(ddConfig);
|
|
16512
|
+
}
|
|
16513
|
+
const streams = [
|
|
16514
|
+
{ stream: csStream.writable, level: "info" }
|
|
16515
|
+
];
|
|
16516
|
+
if (ddBatch) {
|
|
16517
|
+
streams.push({ stream: ddBatch.createPinoStream(), level: "info" });
|
|
16518
|
+
}
|
|
16519
|
+
for (const extra of additionalStreams) {
|
|
16520
|
+
streams.push({
|
|
16521
|
+
stream: extra.stream,
|
|
16522
|
+
level: extra.level
|
|
16523
|
+
});
|
|
16524
|
+
}
|
|
16525
|
+
const pinoLogger = pino(
|
|
16526
|
+
{
|
|
16527
|
+
formatters: {
|
|
16528
|
+
level: (label) => ({ level: label })
|
|
16258
16529
|
}
|
|
16530
|
+
},
|
|
16531
|
+
pino.multistream(streams)
|
|
16532
|
+
);
|
|
16533
|
+
const info = pinoLogger.info.bind(pinoLogger);
|
|
16534
|
+
const warn = pinoLogger.warn.bind(pinoLogger);
|
|
16535
|
+
const error = pinoLogger.error.bind(pinoLogger);
|
|
16536
|
+
const debug23 = pinoLogger.debug.bind(pinoLogger);
|
|
16537
|
+
function heartbeat(message, data) {
|
|
16538
|
+
pinoLogger.info({ data, heartbeat: true }, message);
|
|
16539
|
+
}
|
|
16540
|
+
async function timed(label, fn) {
|
|
16541
|
+
const start = Date.now();
|
|
16542
|
+
try {
|
|
16543
|
+
const result = await fn();
|
|
16544
|
+
pinoLogger.info({ durationMs: Date.now() - start }, label);
|
|
16545
|
+
return result;
|
|
16546
|
+
} catch (err) {
|
|
16547
|
+
pinoLogger.error(
|
|
16548
|
+
{
|
|
16549
|
+
durationMs: Date.now() - start,
|
|
16550
|
+
data: err instanceof Error ? err.message : String(err)
|
|
16551
|
+
},
|
|
16552
|
+
`${label} [FAILED]`
|
|
16553
|
+
);
|
|
16554
|
+
throw err;
|
|
16259
16555
|
}
|
|
16260
16556
|
}
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16264
|
-
|
|
16265
|
-
|
|
16266
|
-
|
|
16267
|
-
};
|
|
16268
|
-
}
|
|
16269
|
-
async function parseTranscriptAndCreateTrace(transcriptPath, hookData, inference) {
|
|
16270
|
-
const content = await fsPromises4.readFile(transcriptPath, "utf-8");
|
|
16271
|
-
const lines = content.trim().split("\n");
|
|
16272
|
-
let currentToolIndex = -1;
|
|
16273
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
16274
|
-
const line = lines[i]?.trim() ?? "";
|
|
16275
|
-
if (!line.includes('"type":"tool_use"')) continue;
|
|
16276
|
-
const isEditTool = line.includes('"name":"Edit"');
|
|
16277
|
-
const isWriteTool = line.includes('"name":"Write"');
|
|
16278
|
-
if (isEditTool || isWriteTool) {
|
|
16279
|
-
currentToolIndex = i;
|
|
16280
|
-
break;
|
|
16557
|
+
function flushLogs2() {
|
|
16558
|
+
csStream.flush();
|
|
16559
|
+
}
|
|
16560
|
+
async function flushDdAsync() {
|
|
16561
|
+
if (ddBatch) {
|
|
16562
|
+
await ddBatch.flushAsync();
|
|
16281
16563
|
}
|
|
16282
16564
|
}
|
|
16283
|
-
|
|
16284
|
-
|
|
16285
|
-
|
|
16286
|
-
let model;
|
|
16287
|
-
let latestDate;
|
|
16288
|
-
for (let i = startIndex; i <= endIndex; i++) {
|
|
16289
|
-
const line = lines[i]?.trim() ?? "";
|
|
16290
|
-
const entry = JSON.parse(line);
|
|
16291
|
-
const lineResult = processTranscriptLine(entry);
|
|
16292
|
-
prompts.push(...lineResult.prompts);
|
|
16293
|
-
if (lineResult.model && !model) {
|
|
16294
|
-
model = lineResult.model;
|
|
16295
|
-
}
|
|
16296
|
-
if (lineResult.date) {
|
|
16297
|
-
if (!latestDate || lineResult.date > latestDate) {
|
|
16298
|
-
latestDate = lineResult.date;
|
|
16299
|
-
}
|
|
16300
|
-
}
|
|
16301
|
-
}
|
|
16302
|
-
prompts.push({
|
|
16303
|
-
type: "TOOL_EXECUTION",
|
|
16304
|
-
date: latestDate || /* @__PURE__ */ new Date(),
|
|
16305
|
-
tool: {
|
|
16306
|
-
name: hookData.tool_name,
|
|
16307
|
-
parameters: JSON.stringify(hookData.tool_input, null, 2),
|
|
16308
|
-
result: JSON.stringify(hookData.tool_response, null, 2),
|
|
16309
|
-
rawArguments: JSON.stringify(hookData.tool_input),
|
|
16310
|
-
accepted: true
|
|
16565
|
+
function disposeDd() {
|
|
16566
|
+
if (ddBatch) {
|
|
16567
|
+
ddBatch.dispose();
|
|
16311
16568
|
}
|
|
16312
|
-
}
|
|
16569
|
+
}
|
|
16313
16570
|
return {
|
|
16314
|
-
|
|
16315
|
-
|
|
16316
|
-
|
|
16317
|
-
|
|
16318
|
-
|
|
16319
|
-
|
|
16571
|
+
info,
|
|
16572
|
+
warn,
|
|
16573
|
+
error,
|
|
16574
|
+
debug: debug23,
|
|
16575
|
+
heartbeat,
|
|
16576
|
+
timed,
|
|
16577
|
+
flushLogs: flushLogs2,
|
|
16578
|
+
flushDdAsync,
|
|
16579
|
+
disposeDd,
|
|
16580
|
+
setScopePath: csStream.setScopePath
|
|
16320
16581
|
};
|
|
16321
16582
|
}
|
|
16322
16583
|
|
|
16584
|
+
// src/features/claude_code/hook_logger.ts
|
|
16585
|
+
var DD_RUM_TOKEN = true ? "" : "";
|
|
16586
|
+
var CLI_VERSION = true ? "1.2.55" : "unknown";
|
|
16587
|
+
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
16588
|
+
function createHookLogger(scopePath) {
|
|
16589
|
+
return createLogger({
|
|
16590
|
+
namespace: NAMESPACE,
|
|
16591
|
+
scopePath,
|
|
16592
|
+
dd: {
|
|
16593
|
+
apiKey: DD_RUM_TOKEN,
|
|
16594
|
+
ddsource: "mobbdev-cli",
|
|
16595
|
+
service: "mobbdev-cli-hook",
|
|
16596
|
+
ddtags: `version:${CLI_VERSION}`,
|
|
16597
|
+
hostnameMode: "hashed",
|
|
16598
|
+
unrefTimer: true
|
|
16599
|
+
}
|
|
16600
|
+
});
|
|
16601
|
+
}
|
|
16602
|
+
var logger = createHookLogger();
|
|
16603
|
+
var activeScopedLoggers = [];
|
|
16604
|
+
var hookLog = logger;
|
|
16605
|
+
function flushLogs() {
|
|
16606
|
+
logger.flushLogs();
|
|
16607
|
+
for (const scoped of activeScopedLoggers) {
|
|
16608
|
+
scoped.flushLogs();
|
|
16609
|
+
}
|
|
16610
|
+
activeScopedLoggers.length = 0;
|
|
16611
|
+
}
|
|
16612
|
+
async function flushDdLogs() {
|
|
16613
|
+
await logger.flushDdAsync();
|
|
16614
|
+
for (const scoped of activeScopedLoggers) {
|
|
16615
|
+
await scoped.flushDdAsync();
|
|
16616
|
+
}
|
|
16617
|
+
}
|
|
16618
|
+
function createScopedHookLog(scopePath) {
|
|
16619
|
+
const scoped = createHookLogger(scopePath);
|
|
16620
|
+
activeScopedLoggers.push(scoped);
|
|
16621
|
+
return scoped;
|
|
16622
|
+
}
|
|
16623
|
+
|
|
16323
16624
|
// src/features/claude_code/data_collector.ts
|
|
16324
|
-
var
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
newLines: z33.number(),
|
|
16329
|
-
lines: z33.array(z33.string())
|
|
16330
|
-
});
|
|
16331
|
-
var EditToolInputSchema = z33.object({
|
|
16332
|
-
file_path: z33.string(),
|
|
16333
|
-
old_string: z33.string(),
|
|
16334
|
-
new_string: z33.string()
|
|
16335
|
-
});
|
|
16336
|
-
var WriteToolInputSchema = z33.object({
|
|
16337
|
-
file_path: z33.string(),
|
|
16338
|
-
content: z33.string()
|
|
16339
|
-
});
|
|
16340
|
-
var EditToolResponseSchema = z33.object({
|
|
16341
|
-
filePath: z33.string(),
|
|
16342
|
-
oldString: z33.string().optional(),
|
|
16343
|
-
newString: z33.string().optional(),
|
|
16344
|
-
originalFile: z33.string().optional(),
|
|
16345
|
-
structuredPatch: z33.array(StructuredPatchItemSchema),
|
|
16346
|
-
userModified: z33.boolean().optional(),
|
|
16347
|
-
replaceAll: z33.boolean().optional()
|
|
16348
|
-
});
|
|
16349
|
-
var WriteToolResponseSchema = z33.object({
|
|
16350
|
-
type: z33.string().optional(),
|
|
16351
|
-
filePath: z33.string(),
|
|
16352
|
-
content: z33.string().optional(),
|
|
16353
|
-
structuredPatch: z33.array(z33.any()).optional()
|
|
16354
|
-
});
|
|
16625
|
+
var HOOK_COOLDOWN_MS = 1e4;
|
|
16626
|
+
var STALE_KEY_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
16627
|
+
var CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
16628
|
+
var COOLDOWN_KEY = "lastHookRunAt";
|
|
16355
16629
|
var HookDataSchema = z33.object({
|
|
16356
16630
|
session_id: z33.string(),
|
|
16357
16631
|
transcript_path: z33.string(),
|
|
16358
16632
|
cwd: z33.string(),
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
tool_response: z33.union([EditToolResponseSchema, WriteToolResponseSchema])
|
|
16633
|
+
hook_event_name: z33.string(),
|
|
16634
|
+
tool_name: z33.string(),
|
|
16635
|
+
tool_input: z33.unknown(),
|
|
16636
|
+
tool_response: z33.unknown()
|
|
16364
16637
|
});
|
|
16638
|
+
var STDIN_TIMEOUT_MS = 1e4;
|
|
16365
16639
|
async function readStdinData() {
|
|
16640
|
+
hookLog.debug("Reading stdin data");
|
|
16366
16641
|
return new Promise((resolve, reject) => {
|
|
16367
16642
|
let inputData = "";
|
|
16643
|
+
let settled = false;
|
|
16644
|
+
const timer = setTimeout(() => {
|
|
16645
|
+
if (!settled) {
|
|
16646
|
+
settled = true;
|
|
16647
|
+
process.stdin.destroy();
|
|
16648
|
+
reject(new Error("Timed out reading from stdin"));
|
|
16649
|
+
}
|
|
16650
|
+
}, STDIN_TIMEOUT_MS);
|
|
16368
16651
|
process.stdin.setEncoding("utf-8");
|
|
16369
16652
|
process.stdin.on("data", (chunk) => {
|
|
16370
16653
|
inputData += chunk;
|
|
16371
16654
|
});
|
|
16372
16655
|
process.stdin.on("end", () => {
|
|
16656
|
+
if (settled) return;
|
|
16657
|
+
settled = true;
|
|
16658
|
+
clearTimeout(timer);
|
|
16373
16659
|
try {
|
|
16374
16660
|
const parsedData = JSON.parse(inputData);
|
|
16661
|
+
hookLog.debug("Parsed stdin data", {
|
|
16662
|
+
keys: Object.keys(parsedData)
|
|
16663
|
+
});
|
|
16375
16664
|
resolve(parsedData);
|
|
16376
16665
|
} catch (error) {
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
)
|
|
16381
|
-
);
|
|
16666
|
+
const msg = `Failed to parse JSON from stdin: ${error.message}`;
|
|
16667
|
+
hookLog.error(msg);
|
|
16668
|
+
reject(new Error(msg));
|
|
16382
16669
|
}
|
|
16383
16670
|
});
|
|
16384
16671
|
process.stdin.on("error", (error) => {
|
|
16672
|
+
if (settled) return;
|
|
16673
|
+
settled = true;
|
|
16674
|
+
clearTimeout(timer);
|
|
16675
|
+
hookLog.error("Error reading from stdin", { error: error.message });
|
|
16385
16676
|
reject(new Error(`Error reading from stdin: ${error.message}`));
|
|
16386
16677
|
});
|
|
16387
16678
|
});
|
|
@@ -16389,144 +16680,329 @@ async function readStdinData() {
|
|
|
16389
16680
|
function validateHookData(data) {
|
|
16390
16681
|
return HookDataSchema.parse(data);
|
|
16391
16682
|
}
|
|
16392
|
-
function
|
|
16393
|
-
const
|
|
16394
|
-
const
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
|
|
16406
|
-
|
|
16407
|
-
|
|
16408
|
-
}
|
|
16409
|
-
if (hookData.tool_name === "Edit") {
|
|
16410
|
-
const editInput = hookData.tool_input;
|
|
16683
|
+
function generateSyntheticId(sessionId, timestamp, type2, lineIndex) {
|
|
16684
|
+
const input = `${sessionId ?? ""}:${timestamp ?? ""}:${type2 ?? ""}:${lineIndex}`;
|
|
16685
|
+
const hash = createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
16686
|
+
return `synth:${hash}`;
|
|
16687
|
+
}
|
|
16688
|
+
function getCursorKey(transcriptPath) {
|
|
16689
|
+
const hash = createHash2("sha256").update(transcriptPath).digest("hex").slice(0, 12);
|
|
16690
|
+
return `cursor.${hash}`;
|
|
16691
|
+
}
|
|
16692
|
+
async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore) {
|
|
16693
|
+
const cursor = sessionStore.get(getCursorKey(transcriptPath));
|
|
16694
|
+
let content;
|
|
16695
|
+
let fileSize;
|
|
16696
|
+
let lineIndexOffset;
|
|
16697
|
+
if (cursor?.byteOffset) {
|
|
16698
|
+
const fh = await open4(transcriptPath, "r");
|
|
16411
16699
|
try {
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
|
|
16700
|
+
const stat = await fh.stat();
|
|
16701
|
+
fileSize = stat.size;
|
|
16702
|
+
if (cursor.byteOffset >= stat.size) {
|
|
16703
|
+
hookLog.info("No new data in transcript file", { sessionId });
|
|
16704
|
+
return { entries: [], fileSize };
|
|
16705
|
+
}
|
|
16706
|
+
const buf = Buffer.alloc(stat.size - cursor.byteOffset);
|
|
16707
|
+
await fh.read(buf, 0, buf.length, cursor.byteOffset);
|
|
16708
|
+
content = buf.toString("utf-8");
|
|
16709
|
+
} finally {
|
|
16710
|
+
await fh.close();
|
|
16711
|
+
}
|
|
16712
|
+
lineIndexOffset = cursor.byteOffset;
|
|
16713
|
+
hookLog.debug("Read transcript file from offset", {
|
|
16714
|
+
transcriptPath,
|
|
16715
|
+
byteOffset: cursor.byteOffset,
|
|
16716
|
+
bytesRead: content.length
|
|
16717
|
+
});
|
|
16718
|
+
} else {
|
|
16719
|
+
content = await readFile(transcriptPath, "utf-8");
|
|
16720
|
+
fileSize = Buffer.byteLength(content, "utf-8");
|
|
16721
|
+
lineIndexOffset = 0;
|
|
16722
|
+
hookLog.debug("Read full transcript file", {
|
|
16723
|
+
transcriptPath,
|
|
16724
|
+
totalBytes: fileSize
|
|
16725
|
+
});
|
|
16726
|
+
}
|
|
16727
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
16728
|
+
const parsed = [];
|
|
16729
|
+
let malformedLines = 0;
|
|
16730
|
+
for (let i = 0; i < lines.length; i++) {
|
|
16731
|
+
try {
|
|
16732
|
+
const entry = JSON.parse(lines[i]);
|
|
16733
|
+
const recordId = entry.uuid ?? generateSyntheticId(
|
|
16734
|
+
entry.sessionId,
|
|
16735
|
+
entry.timestamp,
|
|
16736
|
+
entry.type,
|
|
16737
|
+
lineIndexOffset + i
|
|
16415
16738
|
);
|
|
16739
|
+
parsed.push({ ...entry, _recordId: recordId });
|
|
16416
16740
|
} catch {
|
|
16417
|
-
|
|
16741
|
+
malformedLines++;
|
|
16418
16742
|
}
|
|
16419
16743
|
}
|
|
16420
|
-
|
|
16744
|
+
if (malformedLines > 0) {
|
|
16745
|
+
hookLog.warn("Skipped malformed lines", { malformedLines, transcriptPath });
|
|
16746
|
+
}
|
|
16747
|
+
if (!cursor) {
|
|
16748
|
+
hookLog.info("First invocation for session \u2014 uploading all entries", {
|
|
16749
|
+
sessionId,
|
|
16750
|
+
totalEntries: parsed.length
|
|
16751
|
+
});
|
|
16752
|
+
} else {
|
|
16753
|
+
hookLog.info("Resuming from byte offset", {
|
|
16754
|
+
sessionId,
|
|
16755
|
+
byteOffset: cursor.byteOffset,
|
|
16756
|
+
newEntries: parsed.length
|
|
16757
|
+
});
|
|
16758
|
+
}
|
|
16759
|
+
return { entries: parsed, fileSize };
|
|
16760
|
+
}
|
|
16761
|
+
var FILTERED_PROGRESS_SUBTYPES = /* @__PURE__ */ new Set([
|
|
16762
|
+
// Incremental streaming output from running bash commands, emitted every
|
|
16763
|
+
// ~1 second. The final complete output is already captured in the "user"
|
|
16764
|
+
// tool_result entry when the command finishes.
|
|
16765
|
+
"bash_progress",
|
|
16766
|
+
// Records that the hook itself fired. Pure meta-noise — the hook
|
|
16767
|
+
// recording the fact that the hook ran.
|
|
16768
|
+
"hook_progress",
|
|
16769
|
+
// UI-only "waiting" indicator for background tasks (TaskOutput polling).
|
|
16770
|
+
// Contains only a task description and type — no session-relevant data.
|
|
16771
|
+
"waiting_for_task",
|
|
16772
|
+
// MCP tool start/completed timing events. Only unique data is elapsedTimeMs
|
|
16773
|
+
// which can be derived from tool_use/tool_result timestamps.
|
|
16774
|
+
"mcp_progress"
|
|
16775
|
+
]);
|
|
16776
|
+
var FILTERED_ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
16777
|
+
// Claude Code's internal undo/restore bookkeeping — tracks which files
|
|
16778
|
+
// have backups. No sessionId, no message, no model or tool data.
|
|
16779
|
+
"file-history-snapshot",
|
|
16780
|
+
// Internal task queue management (enqueue/remove/popAll). Duplicates data
|
|
16781
|
+
// already captured in user messages, agent_progress, and Task tool_use entries.
|
|
16782
|
+
"queue-operation",
|
|
16783
|
+
// Records the last user prompt text before a compaction or session restart.
|
|
16784
|
+
// Redundant — the actual user prompt is already captured in the 'user' entry.
|
|
16785
|
+
"last-prompt"
|
|
16786
|
+
]);
|
|
16787
|
+
var FILTERED_ASSISTANT_TOOLS = /* @__PURE__ */ new Set([
|
|
16788
|
+
// Polls for a sub-agent result. The input is just task_id + boilerplate
|
|
16789
|
+
// (block, timeout). The actual result is captured in the user:tool_result.
|
|
16790
|
+
"TaskOutput",
|
|
16791
|
+
// Discovers available deferred/MCP tools. The input is just a search query.
|
|
16792
|
+
// The discovered tools are captured in the user:tool_result.
|
|
16793
|
+
"ToolSearch"
|
|
16794
|
+
]);
|
|
16795
|
+
function filterEntries(entries) {
|
|
16796
|
+
const filtered = entries.filter((entry) => {
|
|
16797
|
+
const entryType = entry.type ?? "";
|
|
16798
|
+
if (FILTERED_ENTRY_TYPES.has(entryType)) {
|
|
16799
|
+
return false;
|
|
16800
|
+
}
|
|
16801
|
+
if (entryType === "progress") {
|
|
16802
|
+
const data = entry["data"];
|
|
16803
|
+
const subtype = typeof data?.["type"] === "string" ? data["type"] : "";
|
|
16804
|
+
return !FILTERED_PROGRESS_SUBTYPES.has(subtype);
|
|
16805
|
+
}
|
|
16806
|
+
if (entryType === "assistant") {
|
|
16807
|
+
const message = entry["message"];
|
|
16808
|
+
const content = message?.["content"];
|
|
16809
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
16810
|
+
const block = content[0];
|
|
16811
|
+
if (block["type"] === "tool_use" && typeof block["name"] === "string" && FILTERED_ASSISTANT_TOOLS.has(block["name"])) {
|
|
16812
|
+
return false;
|
|
16813
|
+
}
|
|
16814
|
+
}
|
|
16815
|
+
}
|
|
16816
|
+
return true;
|
|
16817
|
+
});
|
|
16818
|
+
return { filtered, filteredOut: entries.length - filtered.length };
|
|
16421
16819
|
}
|
|
16422
|
-
async function
|
|
16423
|
-
const
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16820
|
+
async function cleanupStaleSessions(sessionStore) {
|
|
16821
|
+
const lastCleanup = configStore.get("claudeCode.lastCleanupAt");
|
|
16822
|
+
if (lastCleanup && Date.now() - lastCleanup < CLEANUP_INTERVAL_MS) {
|
|
16823
|
+
return;
|
|
16824
|
+
}
|
|
16825
|
+
const now = Date.now();
|
|
16826
|
+
const prefix = getSessionFilePrefix();
|
|
16827
|
+
const configDir = path13.dirname(sessionStore.path);
|
|
16427
16828
|
try {
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
|
|
16442
|
-
|
|
16443
|
-
tool: {
|
|
16444
|
-
name: hookData.tool_name,
|
|
16445
|
-
parameters: JSON.stringify(hookData.tool_input, null, 2),
|
|
16446
|
-
result: JSON.stringify(hookData.tool_response, null, 2),
|
|
16447
|
-
rawArguments: JSON.stringify(hookData.tool_input),
|
|
16448
|
-
accepted: true
|
|
16829
|
+
const files = await readdir(configDir);
|
|
16830
|
+
let deletedCount = 0;
|
|
16831
|
+
for (const file of files) {
|
|
16832
|
+
if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
|
|
16833
|
+
const filePath = path13.join(configDir, file);
|
|
16834
|
+
try {
|
|
16835
|
+
const content = JSON.parse(await readFile(filePath, "utf-8"));
|
|
16836
|
+
let newest = 0;
|
|
16837
|
+
const cooldown = content[COOLDOWN_KEY];
|
|
16838
|
+
if (cooldown && cooldown > newest) newest = cooldown;
|
|
16839
|
+
const cursors = content["cursor"];
|
|
16840
|
+
if (cursors && typeof cursors === "object") {
|
|
16841
|
+
for (const val of Object.values(cursors)) {
|
|
16842
|
+
const c = val;
|
|
16843
|
+
if (c?.updatedAt && c.updatedAt > newest) newest = c.updatedAt;
|
|
16449
16844
|
}
|
|
16450
16845
|
}
|
|
16451
|
-
|
|
16452
|
-
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
inference,
|
|
16461
|
-
tracePayload
|
|
16462
|
-
};
|
|
16463
|
-
}
|
|
16464
|
-
async function getRepositoryUrl2(cwd) {
|
|
16465
|
-
try {
|
|
16466
|
-
const gitService = new GitService(cwd);
|
|
16467
|
-
const isRepo = await gitService.isGitRepository();
|
|
16468
|
-
if (!isRepo) {
|
|
16469
|
-
return null;
|
|
16846
|
+
if (newest > 0 && now - newest > STALE_KEY_MAX_AGE_MS) {
|
|
16847
|
+
await unlink(filePath);
|
|
16848
|
+
deletedCount++;
|
|
16849
|
+
}
|
|
16850
|
+
} catch {
|
|
16851
|
+
}
|
|
16852
|
+
}
|
|
16853
|
+
if (deletedCount > 0) {
|
|
16854
|
+
hookLog.info("Cleaned up stale session files", { deletedCount });
|
|
16470
16855
|
}
|
|
16471
|
-
const remoteUrl = await gitService.getRemoteUrl();
|
|
16472
|
-
const parsed = parseScmURL(remoteUrl);
|
|
16473
|
-
return parsed?.scmType === "GitHub" /* GitHub */ || parsed?.scmType === "GitLab" /* GitLab */ ? remoteUrl : null;
|
|
16474
16856
|
} catch {
|
|
16475
|
-
return null;
|
|
16476
16857
|
}
|
|
16858
|
+
configStore.set("claudeCode.lastCleanupAt", now);
|
|
16477
16859
|
}
|
|
16478
|
-
async function
|
|
16479
|
-
|
|
16480
|
-
const
|
|
16481
|
-
|
|
16860
|
+
async function processAndUploadTranscriptEntries() {
|
|
16861
|
+
hookLog.info("Hook invoked");
|
|
16862
|
+
const rawData = await readStdinData();
|
|
16863
|
+
const hookData = validateHookData(rawData);
|
|
16864
|
+
const sessionStore = createSessionConfigStore(hookData.session_id);
|
|
16865
|
+
await cleanupStaleSessions(sessionStore);
|
|
16866
|
+
const lastRunAt = sessionStore.get(COOLDOWN_KEY);
|
|
16867
|
+
if (lastRunAt && Date.now() - lastRunAt < HOOK_COOLDOWN_MS) {
|
|
16868
|
+
return { entriesUploaded: 0, entriesSkipped: 0, errors: 0 };
|
|
16869
|
+
}
|
|
16870
|
+
sessionStore.set(COOLDOWN_KEY, Date.now());
|
|
16871
|
+
const log2 = createScopedHookLog(hookData.cwd);
|
|
16872
|
+
log2.info("Hook data validated", {
|
|
16873
|
+
sessionId: hookData.session_id,
|
|
16874
|
+
toolName: hookData.tool_name,
|
|
16875
|
+
hookEvent: hookData.hook_event_name,
|
|
16876
|
+
cwd: hookData.cwd
|
|
16877
|
+
});
|
|
16482
16878
|
try {
|
|
16483
|
-
await
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16879
|
+
return await processTranscript(hookData, sessionStore, log2);
|
|
16880
|
+
} finally {
|
|
16881
|
+
log2.flushLogs();
|
|
16882
|
+
}
|
|
16883
|
+
}
|
|
16884
|
+
async function processTranscript(hookData, sessionStore, log2) {
|
|
16885
|
+
const cursorKey = getCursorKey(hookData.transcript_path);
|
|
16886
|
+
const { entries: rawEntries, fileSize } = await readNewTranscriptEntries(
|
|
16887
|
+
hookData.transcript_path,
|
|
16888
|
+
hookData.session_id,
|
|
16889
|
+
sessionStore
|
|
16890
|
+
);
|
|
16891
|
+
if (rawEntries.length === 0) {
|
|
16892
|
+
log2.info("No new entries to upload");
|
|
16893
|
+
return { entriesUploaded: 0, entriesSkipped: 0, errors: 0 };
|
|
16894
|
+
}
|
|
16895
|
+
const { filtered: entries, filteredOut } = filterEntries(rawEntries);
|
|
16896
|
+
if (filteredOut > 0) {
|
|
16897
|
+
log2.info("Filtered out noise entries", {
|
|
16898
|
+
filteredOut,
|
|
16899
|
+
remaining: entries.length
|
|
16900
|
+
});
|
|
16901
|
+
}
|
|
16902
|
+
if (entries.length === 0) {
|
|
16903
|
+
log2.info("All entries filtered out, nothing to upload");
|
|
16904
|
+
const lastEntry = rawEntries[rawEntries.length - 1];
|
|
16905
|
+
const prevCursor = sessionStore.get(cursorKey);
|
|
16906
|
+
const cursor = {
|
|
16907
|
+
id: lastEntry._recordId,
|
|
16908
|
+
byteOffset: fileSize,
|
|
16909
|
+
updatedAt: Date.now(),
|
|
16910
|
+
lastModel: prevCursor?.lastModel
|
|
16911
|
+
};
|
|
16912
|
+
sessionStore.set(cursorKey, cursor);
|
|
16913
|
+
return {
|
|
16914
|
+
entriesUploaded: 0,
|
|
16915
|
+
entriesSkipped: filteredOut,
|
|
16916
|
+
errors: 0
|
|
16917
|
+
};
|
|
16918
|
+
}
|
|
16919
|
+
const gqlClient = await log2.timed(
|
|
16920
|
+
"GQL auth",
|
|
16921
|
+
() => getAuthenticatedGQLClient({ isSkipPrompts: true })
|
|
16922
|
+
);
|
|
16923
|
+
const cursorForModel = sessionStore.get(cursorKey);
|
|
16924
|
+
let lastSeenModel = cursorForModel?.lastModel ?? null;
|
|
16925
|
+
const records = entries.map((entry) => {
|
|
16926
|
+
const { _recordId, ...rawEntry } = entry;
|
|
16927
|
+
const message = rawEntry["message"];
|
|
16928
|
+
const currentModel = message?.["model"] ?? null;
|
|
16929
|
+
if (currentModel && currentModel !== "<synthetic>") {
|
|
16930
|
+
lastSeenModel = currentModel;
|
|
16931
|
+
} else if (lastSeenModel && !currentModel) {
|
|
16932
|
+
if (message) {
|
|
16933
|
+
message["model"] = lastSeenModel;
|
|
16934
|
+
} else {
|
|
16935
|
+
rawEntry["message"] = { model: lastSeenModel };
|
|
16936
|
+
}
|
|
16937
|
+
}
|
|
16938
|
+
return {
|
|
16939
|
+
platform: "CLAUDE_CODE" /* ClaudeCode */,
|
|
16940
|
+
recordId: _recordId,
|
|
16941
|
+
recordTimestamp: entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
16489
16942
|
blameType: "CHAT" /* Chat */,
|
|
16490
|
-
|
|
16491
|
-
|
|
16943
|
+
rawData: rawEntry
|
|
16944
|
+
};
|
|
16945
|
+
});
|
|
16946
|
+
log2.info("Uploading batch", {
|
|
16947
|
+
count: records.length,
|
|
16948
|
+
skipped: filteredOut,
|
|
16949
|
+
firstRecordId: records[0]?.recordId,
|
|
16950
|
+
lastRecordId: records[records.length - 1]?.recordId
|
|
16951
|
+
});
|
|
16952
|
+
const result = await log2.timed(
|
|
16953
|
+
"Batch upload",
|
|
16954
|
+
() => prepareAndSendTracyRecords(gqlClient, records, hookData.cwd)
|
|
16955
|
+
);
|
|
16956
|
+
if (result.ok) {
|
|
16957
|
+
const lastRawEntry = rawEntries[rawEntries.length - 1];
|
|
16958
|
+
const cursor = {
|
|
16959
|
+
id: lastRawEntry._recordId,
|
|
16960
|
+
byteOffset: fileSize,
|
|
16961
|
+
updatedAt: Date.now(),
|
|
16962
|
+
lastModel: lastSeenModel ?? void 0
|
|
16963
|
+
};
|
|
16964
|
+
sessionStore.set(cursorKey, cursor);
|
|
16965
|
+
log2.heartbeat("Upload ok", {
|
|
16966
|
+
entriesUploaded: entries.length,
|
|
16967
|
+
entriesSkipped: filteredOut
|
|
16492
16968
|
});
|
|
16493
|
-
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
|
|
16497
|
-
|
|
16498
|
-
);
|
|
16499
|
-
uploadSuccess = false;
|
|
16969
|
+
return {
|
|
16970
|
+
entriesUploaded: entries.length,
|
|
16971
|
+
entriesSkipped: filteredOut,
|
|
16972
|
+
errors: 0
|
|
16973
|
+
};
|
|
16500
16974
|
}
|
|
16975
|
+
log2.error("Batch upload had errors", { errors: result.errors });
|
|
16501
16976
|
return {
|
|
16502
|
-
|
|
16503
|
-
|
|
16977
|
+
entriesUploaded: 0,
|
|
16978
|
+
entriesSkipped: filteredOut,
|
|
16979
|
+
errors: entries.length
|
|
16504
16980
|
};
|
|
16505
16981
|
}
|
|
16506
16982
|
|
|
16507
16983
|
// src/features/claude_code/install_hook.ts
|
|
16508
|
-
import
|
|
16509
|
-
import
|
|
16984
|
+
import fsPromises4 from "fs/promises";
|
|
16985
|
+
import os5 from "os";
|
|
16510
16986
|
import path14 from "path";
|
|
16511
16987
|
import chalk11 from "chalk";
|
|
16512
|
-
var CLAUDE_SETTINGS_PATH = path14.join(
|
|
16988
|
+
var CLAUDE_SETTINGS_PATH = path14.join(os5.homedir(), ".claude", "settings.json");
|
|
16513
16989
|
async function claudeSettingsExists() {
|
|
16514
16990
|
try {
|
|
16515
|
-
await
|
|
16991
|
+
await fsPromises4.access(CLAUDE_SETTINGS_PATH);
|
|
16516
16992
|
return true;
|
|
16517
16993
|
} catch {
|
|
16518
16994
|
return false;
|
|
16519
16995
|
}
|
|
16520
16996
|
}
|
|
16521
16997
|
async function readClaudeSettings() {
|
|
16522
|
-
const settingsContent = await
|
|
16998
|
+
const settingsContent = await fsPromises4.readFile(
|
|
16523
16999
|
CLAUDE_SETTINGS_PATH,
|
|
16524
17000
|
"utf-8"
|
|
16525
17001
|
);
|
|
16526
17002
|
return JSON.parse(settingsContent);
|
|
16527
17003
|
}
|
|
16528
17004
|
async function writeClaudeSettings(settings) {
|
|
16529
|
-
await
|
|
17005
|
+
await fsPromises4.writeFile(
|
|
16530
17006
|
CLAUDE_SETTINGS_PATH,
|
|
16531
17007
|
JSON.stringify(settings, null, 2),
|
|
16532
17008
|
"utf-8"
|
|
@@ -16569,7 +17045,8 @@ async function installMobbHooks(options = {}) {
|
|
|
16569
17045
|
}
|
|
16570
17046
|
}
|
|
16571
17047
|
const mobbHookConfig = {
|
|
16572
|
-
matcher:
|
|
17048
|
+
// Empty matcher = match all tools (Claude Code hook spec: empty string matches every PostToolUse event)
|
|
17049
|
+
matcher: "",
|
|
16573
17050
|
hooks: [
|
|
16574
17051
|
{
|
|
16575
17052
|
type: "command",
|
|
@@ -16578,7 +17055,7 @@ async function installMobbHooks(options = {}) {
|
|
|
16578
17055
|
]
|
|
16579
17056
|
};
|
|
16580
17057
|
const existingHookIndex = settings.hooks.PostToolUse.findIndex(
|
|
16581
|
-
(hook) => hook.
|
|
17058
|
+
(hook) => hook.hooks.some(
|
|
16582
17059
|
(h) => h.command?.includes("mobbdev@latest claude-code-process-hook")
|
|
16583
17060
|
)
|
|
16584
17061
|
);
|
|
@@ -16628,51 +17105,45 @@ var claudeCodeInstallHookHandler = async (argv) => {
|
|
|
16628
17105
|
}
|
|
16629
17106
|
};
|
|
16630
17107
|
var claudeCodeProcessHookHandler = async () => {
|
|
16631
|
-
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
const userPrompts = tracePayload.prompts.filter(
|
|
16639
|
-
(p) => p.type === "USER_PROMPT"
|
|
16640
|
-
);
|
|
16641
|
-
const assistantResponses = tracePayload.prompts.filter(
|
|
16642
|
-
(p) => p.type === "AI_RESPONSE"
|
|
16643
|
-
);
|
|
16644
|
-
const aiThinking = tracePayload.prompts.filter(
|
|
16645
|
-
(p) => p.type === "AI_THINKING"
|
|
16646
|
-
);
|
|
16647
|
-
console.log("Conversation context extracted:");
|
|
16648
|
-
console.log("- User prompts:", userPrompts.length);
|
|
16649
|
-
console.log("- Assistant responses:", assistantResponses.length);
|
|
16650
|
-
console.log("- AI thinking entries:", aiThinking.length);
|
|
16651
|
-
console.log("- Model:", tracePayload.model);
|
|
16652
|
-
const totalInputTokens = tracePayload.prompts.reduce(
|
|
16653
|
-
(sum, p) => sum + (p.tokens?.inputCount || 0),
|
|
16654
|
-
0
|
|
16655
|
-
);
|
|
16656
|
-
const totalOutputTokens = tracePayload.prompts.reduce(
|
|
16657
|
-
(sum, p) => sum + (p.tokens?.outputCount || 0),
|
|
16658
|
-
0
|
|
16659
|
-
);
|
|
16660
|
-
console.log("- Input tokens:", totalInputTokens);
|
|
16661
|
-
console.log("- Output tokens:", totalOutputTokens);
|
|
16662
|
-
console.log("Trace data formatted:");
|
|
16663
|
-
console.log("- Prompt items:", tracePayload.prompts.length);
|
|
16664
|
-
console.log("- Model:", tracePayload.model);
|
|
16665
|
-
console.log("- Tool:", tracePayload.tool);
|
|
16666
|
-
console.log("- Response time:", tracePayload.responseTime);
|
|
16667
|
-
console.log("- Upload success:", uploadSuccess ? "\u2705" : "\u274C");
|
|
16668
|
-
if (uploadSuccess) {
|
|
16669
|
-
console.log("\u2705 Claude Code trace uploaded successfully to Mobb backend");
|
|
17108
|
+
async function flushAndExit(code) {
|
|
17109
|
+
try {
|
|
17110
|
+
flushLogs();
|
|
17111
|
+
await flushDdLogs();
|
|
17112
|
+
} catch {
|
|
17113
|
+
} finally {
|
|
17114
|
+
process.exit(code);
|
|
16670
17115
|
}
|
|
16671
|
-
|
|
17116
|
+
}
|
|
17117
|
+
process.on("uncaughtException", (error) => {
|
|
17118
|
+
hookLog.error("Uncaught exception in hook", {
|
|
17119
|
+
error: String(error),
|
|
17120
|
+
stack: error.stack
|
|
17121
|
+
});
|
|
17122
|
+
void flushAndExit(1);
|
|
17123
|
+
});
|
|
17124
|
+
process.on("unhandledRejection", (reason) => {
|
|
17125
|
+
hookLog.error("Unhandled rejection in hook", {
|
|
17126
|
+
error: String(reason),
|
|
17127
|
+
stack: reason instanceof Error ? reason.stack : void 0
|
|
17128
|
+
});
|
|
17129
|
+
void flushAndExit(1);
|
|
17130
|
+
});
|
|
17131
|
+
let exitCode = 0;
|
|
17132
|
+
try {
|
|
17133
|
+
const result = await processAndUploadTranscriptEntries();
|
|
17134
|
+
hookLog.info("Claude Code upload complete", {
|
|
17135
|
+
entriesUploaded: result.entriesUploaded,
|
|
17136
|
+
entriesSkipped: result.entriesSkipped,
|
|
17137
|
+
errors: result.errors
|
|
17138
|
+
});
|
|
16672
17139
|
} catch (error) {
|
|
16673
|
-
|
|
16674
|
-
|
|
17140
|
+
exitCode = 1;
|
|
17141
|
+
hookLog.error("Failed to process Claude Code hook", {
|
|
17142
|
+
error: String(error),
|
|
17143
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
17144
|
+
});
|
|
16675
17145
|
}
|
|
17146
|
+
await flushAndExit(exitCode);
|
|
16676
17147
|
};
|
|
16677
17148
|
|
|
16678
17149
|
// src/mcp/core/McpServer.ts
|
|
@@ -16686,7 +17157,7 @@ import {
|
|
|
16686
17157
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
16687
17158
|
|
|
16688
17159
|
// src/mcp/Logger.ts
|
|
16689
|
-
import
|
|
17160
|
+
import Configstore3 from "configstore";
|
|
16690
17161
|
|
|
16691
17162
|
// src/mcp/services/WorkspaceService.ts
|
|
16692
17163
|
var WorkspaceService = class {
|
|
@@ -16772,7 +17243,7 @@ var Logger = class {
|
|
|
16772
17243
|
__publicField(this, "lastKnownPath", null);
|
|
16773
17244
|
this.host = WorkspaceService.getHost();
|
|
16774
17245
|
this.unknownPathSuffix = Math.floor(1e3 + Math.random() * 9e3).toString();
|
|
16775
|
-
this.mobbConfigStore = new
|
|
17246
|
+
this.mobbConfigStore = new Configstore3("mobb-logs", {});
|
|
16776
17247
|
this.mobbConfigStore.set("version", packageJson.version);
|
|
16777
17248
|
}
|
|
16778
17249
|
/**
|
|
@@ -16828,12 +17299,12 @@ var Logger = class {
|
|
|
16828
17299
|
this.mobbConfigStore.set(currentPath, [...logs, logMessage]);
|
|
16829
17300
|
}
|
|
16830
17301
|
};
|
|
16831
|
-
var
|
|
16832
|
-
var logInfo = (message, data) =>
|
|
16833
|
-
var logError = (message, data) =>
|
|
16834
|
-
var logWarn = (message, data) =>
|
|
16835
|
-
var logDebug = (message, data) =>
|
|
16836
|
-
var log =
|
|
17302
|
+
var logger2 = new Logger();
|
|
17303
|
+
var logInfo = (message, data) => logger2.log(message, "info", data);
|
|
17304
|
+
var logError = (message, data) => logger2.log(message, "error", data);
|
|
17305
|
+
var logWarn = (message, data) => logger2.log(message, "warn", data);
|
|
17306
|
+
var logDebug = (message, data) => logger2.log(message, "debug", data);
|
|
17307
|
+
var log = logger2.log.bind(logger2);
|
|
16837
17308
|
|
|
16838
17309
|
// src/mcp/services/McpGQLClient.ts
|
|
16839
17310
|
import crypto2 from "crypto";
|
|
@@ -17555,7 +18026,7 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
17555
18026
|
// src/mcp/services/McpUsageService/host.ts
|
|
17556
18027
|
import { execSync as execSync2 } from "child_process";
|
|
17557
18028
|
import fs13 from "fs";
|
|
17558
|
-
import
|
|
18029
|
+
import os6 from "os";
|
|
17559
18030
|
import path15 from "path";
|
|
17560
18031
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
17561
18032
|
var runCommand = (cmd) => {
|
|
@@ -17570,7 +18041,7 @@ var gitInfo = {
|
|
|
17570
18041
|
email: runCommand("git config user.email")
|
|
17571
18042
|
};
|
|
17572
18043
|
var getClaudeWorkspacePaths = () => {
|
|
17573
|
-
const home =
|
|
18044
|
+
const home = os6.homedir();
|
|
17574
18045
|
const claudeIdePath = path15.join(home, ".claude", "ide");
|
|
17575
18046
|
const workspacePaths = [];
|
|
17576
18047
|
if (!fs13.existsSync(claudeIdePath)) {
|
|
@@ -17599,7 +18070,7 @@ var getClaudeWorkspacePaths = () => {
|
|
|
17599
18070
|
return workspacePaths;
|
|
17600
18071
|
};
|
|
17601
18072
|
var getMCPConfigPaths = (hostName) => {
|
|
17602
|
-
const home =
|
|
18073
|
+
const home = os6.homedir();
|
|
17603
18074
|
const currentDir = process.env["WORKSPACE_FOLDER_PATHS"] || process.env["PWD"] || process.cwd();
|
|
17604
18075
|
switch (hostName.toLowerCase()) {
|
|
17605
18076
|
case "cursor":
|
|
@@ -17689,7 +18160,7 @@ var readMCPConfig = (hostName) => {
|
|
|
17689
18160
|
};
|
|
17690
18161
|
var getRunningProcesses = () => {
|
|
17691
18162
|
try {
|
|
17692
|
-
return
|
|
18163
|
+
return os6.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
|
|
17693
18164
|
} catch {
|
|
17694
18165
|
return "";
|
|
17695
18166
|
}
|
|
@@ -17764,7 +18235,7 @@ var versionCommands = {
|
|
|
17764
18235
|
}
|
|
17765
18236
|
};
|
|
17766
18237
|
var getProcessInfo = (pid) => {
|
|
17767
|
-
const platform2 =
|
|
18238
|
+
const platform2 = os6.platform();
|
|
17768
18239
|
try {
|
|
17769
18240
|
if (platform2 === "linux" || platform2 === "darwin") {
|
|
17770
18241
|
const output = execSync2(`ps -o pid=,ppid=,comm= -p ${pid}`, {
|
|
@@ -17883,7 +18354,7 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
17883
18354
|
const config2 = allConfigs[ide] || null;
|
|
17884
18355
|
const ideName = ide.charAt(0).toUpperCase() + ide.slice(1) || "Unknown";
|
|
17885
18356
|
let ideVersion = "Unknown";
|
|
17886
|
-
const platform2 =
|
|
18357
|
+
const platform2 = os6.platform();
|
|
17887
18358
|
const cmds = versionCommands[ideName]?.[platform2] ?? [];
|
|
17888
18359
|
for (const cmd of cmds) {
|
|
17889
18360
|
try {
|
|
@@ -17915,15 +18386,15 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
17915
18386
|
};
|
|
17916
18387
|
|
|
17917
18388
|
// src/mcp/services/McpUsageService/McpUsageService.ts
|
|
17918
|
-
import
|
|
17919
|
-
import
|
|
18389
|
+
import fetch6 from "node-fetch";
|
|
18390
|
+
import os8 from "os";
|
|
17920
18391
|
import { v4 as uuidv42, v5 as uuidv5 } from "uuid";
|
|
17921
18392
|
init_configs();
|
|
17922
18393
|
|
|
17923
18394
|
// src/mcp/services/McpUsageService/system.ts
|
|
17924
18395
|
init_configs();
|
|
17925
18396
|
import fs14 from "fs";
|
|
17926
|
-
import
|
|
18397
|
+
import os7 from "os";
|
|
17927
18398
|
import path16 from "path";
|
|
17928
18399
|
var MAX_DEPTH = 2;
|
|
17929
18400
|
var patterns = ["mcp", "claude"];
|
|
@@ -17958,8 +18429,8 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
17958
18429
|
};
|
|
17959
18430
|
var findSystemMCPConfigs = async () => {
|
|
17960
18431
|
try {
|
|
17961
|
-
const home =
|
|
17962
|
-
const platform2 =
|
|
18432
|
+
const home = os7.homedir();
|
|
18433
|
+
const platform2 = os7.platform();
|
|
17963
18434
|
const knownDirs = platform2 === "win32" ? [
|
|
17964
18435
|
path16.join(home, ".cursor"),
|
|
17965
18436
|
path16.join(home, "Documents"),
|
|
@@ -18031,7 +18502,7 @@ var McpUsageService = class {
|
|
|
18031
18502
|
generateHostId() {
|
|
18032
18503
|
const stored = configStore.get(this.configKey);
|
|
18033
18504
|
if (stored?.mcpHostId) return stored.mcpHostId;
|
|
18034
|
-
const interfaces =
|
|
18505
|
+
const interfaces = os8.networkInterfaces();
|
|
18035
18506
|
const macs = [];
|
|
18036
18507
|
for (const iface of Object.values(interfaces)) {
|
|
18037
18508
|
if (!iface) continue;
|
|
@@ -18039,7 +18510,7 @@ var McpUsageService = class {
|
|
|
18039
18510
|
if (net.mac && net.mac !== "00:00:00:00:00:00") macs.push(net.mac);
|
|
18040
18511
|
}
|
|
18041
18512
|
}
|
|
18042
|
-
const macString = macs.length ? macs.sort().join(",") : `${
|
|
18513
|
+
const macString = macs.length ? macs.sort().join(",") : `${os8.hostname()}-${uuidv42()}`;
|
|
18043
18514
|
const hostId = uuidv5(macString, uuidv5.DNS);
|
|
18044
18515
|
logDebug("[UsageService] Generated new host ID", { hostId });
|
|
18045
18516
|
return hostId;
|
|
@@ -18062,7 +18533,7 @@ var McpUsageService = class {
|
|
|
18062
18533
|
mcpHostId,
|
|
18063
18534
|
organizationId,
|
|
18064
18535
|
mcpVersion: packageJson.version,
|
|
18065
|
-
mcpOsName:
|
|
18536
|
+
mcpOsName: os8.platform(),
|
|
18066
18537
|
mcps: JSON.stringify(mcps),
|
|
18067
18538
|
status,
|
|
18068
18539
|
userName: user.name,
|
|
@@ -18102,7 +18573,7 @@ var McpUsageService = class {
|
|
|
18102
18573
|
}
|
|
18103
18574
|
logDebug("[UsageService] Before", { usageData });
|
|
18104
18575
|
try {
|
|
18105
|
-
const res = await
|
|
18576
|
+
const res = await fetch6(this.REST_API_URL, {
|
|
18106
18577
|
method: "POST",
|
|
18107
18578
|
headers: {
|
|
18108
18579
|
Accept: "application/json"
|
|
@@ -20383,22 +20854,22 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
20383
20854
|
|
|
20384
20855
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
20385
20856
|
import * as fs17 from "fs";
|
|
20386
|
-
import * as
|
|
20857
|
+
import * as os10 from "os";
|
|
20387
20858
|
import * as path18 from "path";
|
|
20388
20859
|
|
|
20389
20860
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
20390
20861
|
init_configs();
|
|
20391
20862
|
import * as fs16 from "fs";
|
|
20392
|
-
import
|
|
20863
|
+
import fetch7 from "node-fetch";
|
|
20393
20864
|
import * as path17 from "path";
|
|
20394
20865
|
|
|
20395
20866
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
20396
20867
|
import * as fs15 from "fs";
|
|
20397
|
-
import * as
|
|
20868
|
+
import * as os9 from "os";
|
|
20398
20869
|
|
|
20399
20870
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
20400
20871
|
import * as fs18 from "fs";
|
|
20401
|
-
import * as
|
|
20872
|
+
import * as os11 from "os";
|
|
20402
20873
|
import * as path19 from "path";
|
|
20403
20874
|
|
|
20404
20875
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
@@ -21956,7 +22427,7 @@ import {
|
|
|
21956
22427
|
mkdirSync,
|
|
21957
22428
|
readFileSync as readFileSync3,
|
|
21958
22429
|
unlinkSync,
|
|
21959
|
-
writeFileSync
|
|
22430
|
+
writeFileSync as writeFileSync2
|
|
21960
22431
|
} from "fs";
|
|
21961
22432
|
import fs22 from "fs/promises";
|
|
21962
22433
|
import parseDiff2 from "parse-diff";
|
|
@@ -22177,7 +22648,7 @@ var PatchApplicationService = class {
|
|
|
22177
22648
|
}
|
|
22178
22649
|
const dirPath = path22.dirname(normalizedFilePath);
|
|
22179
22650
|
mkdirSync(dirPath, { recursive: true });
|
|
22180
|
-
|
|
22651
|
+
writeFileSync2(normalizedFilePath, finalContent, "utf8");
|
|
22181
22652
|
return normalizedFilePath;
|
|
22182
22653
|
}
|
|
22183
22654
|
static resolvePathWithinRepo({
|
|
@@ -24843,18 +25314,18 @@ async function getGrpcClient(port, csrf3) {
|
|
|
24843
25314
|
|
|
24844
25315
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
24845
25316
|
import fs25 from "fs";
|
|
24846
|
-
import
|
|
25317
|
+
import os12 from "os";
|
|
24847
25318
|
import path25 from "path";
|
|
24848
25319
|
function getLogsDir() {
|
|
24849
25320
|
if (process.platform === "darwin") {
|
|
24850
|
-
return path25.join(
|
|
25321
|
+
return path25.join(os12.homedir(), "Library/Logs/JetBrains");
|
|
24851
25322
|
} else if (process.platform === "win32") {
|
|
24852
25323
|
return path25.join(
|
|
24853
|
-
process.env["LOCALAPPDATA"] || path25.join(
|
|
25324
|
+
process.env["LOCALAPPDATA"] || path25.join(os12.homedir(), "AppData/Local"),
|
|
24854
25325
|
"JetBrains"
|
|
24855
25326
|
);
|
|
24856
25327
|
} else {
|
|
24857
|
-
return path25.join(
|
|
25328
|
+
return path25.join(os12.homedir(), ".cache/JetBrains");
|
|
24858
25329
|
}
|
|
24859
25330
|
}
|
|
24860
25331
|
function parseIdeLogDir(ideLogDir) {
|
|
@@ -24912,7 +25383,7 @@ function findRunningCodeiumLanguageServers() {
|
|
|
24912
25383
|
var HookDataSchema2 = z47.object({
|
|
24913
25384
|
trajectory_id: z47.string()
|
|
24914
25385
|
});
|
|
24915
|
-
async function
|
|
25386
|
+
async function processAndUploadHookData() {
|
|
24916
25387
|
const tracePayload = await getTraceDataForHook();
|
|
24917
25388
|
if (!tracePayload) {
|
|
24918
25389
|
console.warn("Warning: Failed to retrieve chat data.");
|
|
@@ -25076,17 +25547,17 @@ function processChatStepCodeAction(step) {
|
|
|
25076
25547
|
}
|
|
25077
25548
|
|
|
25078
25549
|
// src/features/codeium_intellij/install_hook.ts
|
|
25079
|
-
import
|
|
25080
|
-
import
|
|
25550
|
+
import fsPromises5 from "fs/promises";
|
|
25551
|
+
import os13 from "os";
|
|
25081
25552
|
import path26 from "path";
|
|
25082
25553
|
import chalk14 from "chalk";
|
|
25083
25554
|
function getCodeiumHooksPath() {
|
|
25084
|
-
return path26.join(
|
|
25555
|
+
return path26.join(os13.homedir(), ".codeium", "hooks.json");
|
|
25085
25556
|
}
|
|
25086
25557
|
async function readCodeiumHooks() {
|
|
25087
25558
|
const hooksPath = getCodeiumHooksPath();
|
|
25088
25559
|
try {
|
|
25089
|
-
const content = await
|
|
25560
|
+
const content = await fsPromises5.readFile(hooksPath, "utf-8");
|
|
25090
25561
|
return JSON.parse(content);
|
|
25091
25562
|
} catch {
|
|
25092
25563
|
return {};
|
|
@@ -25095,8 +25566,8 @@ async function readCodeiumHooks() {
|
|
|
25095
25566
|
async function writeCodeiumHooks(config2) {
|
|
25096
25567
|
const hooksPath = getCodeiumHooksPath();
|
|
25097
25568
|
const dir = path26.dirname(hooksPath);
|
|
25098
|
-
await
|
|
25099
|
-
await
|
|
25569
|
+
await fsPromises5.mkdir(dir, { recursive: true });
|
|
25570
|
+
await fsPromises5.writeFile(
|
|
25100
25571
|
hooksPath,
|
|
25101
25572
|
JSON.stringify(config2, null, 2),
|
|
25102
25573
|
"utf-8"
|
|
@@ -25184,7 +25655,7 @@ var windsurfIntellijInstallHookHandler = async (argv) => {
|
|
|
25184
25655
|
};
|
|
25185
25656
|
var windsurfIntellijProcessHookHandler = async () => {
|
|
25186
25657
|
try {
|
|
25187
|
-
await
|
|
25658
|
+
await processAndUploadHookData();
|
|
25188
25659
|
process.exit(0);
|
|
25189
25660
|
} catch (error) {
|
|
25190
25661
|
console.error("Failed to process Windsurf IntelliJ hook data:", error);
|