agentic-api 2.0.491 → 2.0.592
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/README.md +37 -34
- package/dist/src/agents/job.runner.d.ts +130 -0
- package/dist/src/agents/job.runner.js +339 -0
- package/dist/src/agents/reducer.core.d.ts +11 -1
- package/dist/src/agents/reducer.core.js +76 -86
- package/dist/src/agents/reducer.d.ts +1 -0
- package/dist/src/agents/reducer.factory.d.ts +46 -0
- package/dist/src/agents/reducer.factory.js +154 -0
- package/dist/src/agents/reducer.js +1 -0
- package/dist/src/agents/simulator.d.ts +26 -1
- package/dist/src/agents/simulator.dashboard.d.ts +140 -0
- package/dist/src/agents/simulator.dashboard.js +344 -0
- package/dist/src/agents/simulator.js +56 -0
- package/dist/src/agents/simulator.types.d.ts +38 -6
- package/dist/src/agents/simulator.utils.d.ts +22 -1
- package/dist/src/agents/simulator.utils.js +27 -0
- package/dist/src/execute/helpers.js +2 -2
- package/dist/src/execute/modelconfig.d.ts +21 -11
- package/dist/src/execute/modelconfig.js +29 -13
- package/dist/src/execute/responses.js +8 -7
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +21 -1
- package/dist/src/llm/config.d.ts +25 -0
- package/dist/src/llm/config.js +38 -0
- package/dist/src/llm/index.d.ts +48 -0
- package/dist/src/llm/index.js +115 -0
- package/dist/src/llm/openai.d.ts +6 -0
- package/dist/src/llm/openai.js +154 -0
- package/dist/src/llm/pricing.d.ts +26 -0
- package/dist/src/llm/pricing.js +129 -0
- package/dist/src/llm/xai.d.ts +17 -0
- package/dist/src/llm/xai.js +90 -0
- package/dist/src/pricing.llm.d.ts +3 -15
- package/dist/src/pricing.llm.js +10 -251
- package/dist/src/prompts.d.ts +0 -1
- package/dist/src/prompts.js +51 -118
- package/dist/src/rag/embeddings.d.ts +5 -1
- package/dist/src/rag/embeddings.js +15 -5
- package/dist/src/rag/parser.js +1 -1
- package/dist/src/rag/rag.manager.d.ts +44 -6
- package/dist/src/rag/rag.manager.js +138 -49
- package/dist/src/rag/types.d.ts +2 -0
- package/dist/src/rag/usecase.js +8 -11
- package/dist/src/rules/git/git.health.js +59 -4
- package/dist/src/rules/git/repo.d.ts +11 -4
- package/dist/src/rules/git/repo.js +64 -18
- package/dist/src/rules/git/repo.pr.d.ts +8 -0
- package/dist/src/rules/git/repo.pr.js +45 -1
- package/dist/src/rules/git/repo.tools.d.ts +5 -1
- package/dist/src/rules/git/repo.tools.js +54 -7
- package/dist/src/rules/types.d.ts +14 -0
- package/dist/src/rules/utils.matter.d.ts +0 -20
- package/dist/src/rules/utils.matter.js +42 -74
- package/dist/src/scrapper.js +2 -2
- package/dist/src/utils.d.ts +0 -8
- package/dist/src/utils.js +1 -28
- package/package.json +1 -1
|
@@ -59,6 +59,8 @@ const errors_1 = require("../errors");
|
|
|
59
59
|
const path_1 = require("path");
|
|
60
60
|
const fs = __importStar(require("fs/promises"));
|
|
61
61
|
const repo_tools_1 = require("./repo.tools");
|
|
62
|
+
const repo_pr_1 = require("./repo.pr");
|
|
63
|
+
const utils_matter_1 = require("../utils.matter");
|
|
62
64
|
/**
|
|
63
65
|
* Service singleton pour gérer le registre d'IDs en mémoire
|
|
64
66
|
*/
|
|
@@ -561,7 +563,7 @@ function gitEnsureMatterID(matter, config, branch, file) {
|
|
|
561
563
|
// (Si même fichier, registerExistingID ne lève pas d'erreur)
|
|
562
564
|
}
|
|
563
565
|
}
|
|
564
|
-
// Générer un nouvel ID
|
|
566
|
+
// Générer un nouvel ID (si aucun ID fourni ou ID invalide)
|
|
565
567
|
matter.id = gitGenerateNextID(config);
|
|
566
568
|
// FIXME: Double save ici - gitGenerateNextID() fait save() puis gitRegisterExistingID() fait save() aussi
|
|
567
569
|
// Refactorisation future: Retirer save() de gitGenerateNextID() et le faire uniquement dans gitRegisterExistingID()
|
|
@@ -999,13 +1001,20 @@ async function gitCheckConfiguration(git) {
|
|
|
999
1001
|
return check;
|
|
1000
1002
|
}
|
|
1001
1003
|
/**
|
|
1002
|
-
* Crée un fichier dans
|
|
1003
|
-
*
|
|
1004
|
+
* Crée ou modifie un fichier dans une branche Git et fait un commit automatique
|
|
1005
|
+
*
|
|
1006
|
+
* @param git Instance SimpleGit
|
|
1004
1007
|
* @param filePath Chemin du fichier
|
|
1005
1008
|
* @param PR Nom de la branche de Pull Request
|
|
1006
1009
|
* @param content Contenu du fichier
|
|
1007
|
-
* @param user Utilisateur qui
|
|
1008
|
-
* @param config Configuration Git
|
|
1010
|
+
* @param user Utilisateur qui fait le commit
|
|
1011
|
+
* @param config Configuration Git optionnelle
|
|
1012
|
+
* @returns Historique du commit créé
|
|
1013
|
+
*
|
|
1014
|
+
* @fixme Ajouter un paramètre optionnel `commitMessage?: string` pour permettre de personnaliser
|
|
1015
|
+
* le message de commit au lieu d'utiliser toujours `commit: ${filePath}` par défaut.
|
|
1016
|
+
* Cela permettrait aux callers de spécifier des messages plus descriptifs.
|
|
1017
|
+
*
|
|
1009
1018
|
* @throws GitOperationError si la création échoue
|
|
1010
1019
|
*/
|
|
1011
1020
|
async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
@@ -1019,8 +1028,7 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
1019
1028
|
if (gitConf.withID) {
|
|
1020
1029
|
try {
|
|
1021
1030
|
// Import dynamique pour éviter les dépendances circulaires
|
|
1022
|
-
const
|
|
1023
|
-
const parsed = matterParse(content);
|
|
1031
|
+
const parsed = (0, utils_matter_1.matterParse)(content);
|
|
1024
1032
|
if (gitConf.strictID) {
|
|
1025
1033
|
// Mode strict : valider que l'ID existe et est correct
|
|
1026
1034
|
validateMatter(parsed.matter);
|
|
@@ -1040,7 +1048,7 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
1040
1048
|
// Seulement si l'ID est manquant ou invalide
|
|
1041
1049
|
parsed.matter = gitEnsureMatterID(parsed.matter, gitConf, PR, filePath);
|
|
1042
1050
|
// Re-sérialiser avec l'ID mis à jour
|
|
1043
|
-
content = matterSerialize(parsed.content, parsed.matter);
|
|
1051
|
+
content = (0, utils_matter_1.matterSerialize)(parsed.content, parsed.matter);
|
|
1044
1052
|
}
|
|
1045
1053
|
else {
|
|
1046
1054
|
// ✅ L'ID existe déjà : gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement
|
|
@@ -1052,7 +1060,7 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
1052
1060
|
if (error.code === 'id_already_used') {
|
|
1053
1061
|
// En mode non-strict, générer un nouvel ID si doublon détecté
|
|
1054
1062
|
parsed.matter = gitEnsureMatterID(parsed.matter, gitConf, PR, filePath);
|
|
1055
|
-
content = matterSerialize(parsed.content, parsed.matter);
|
|
1063
|
+
content = (0, utils_matter_1.matterSerialize)(parsed.content, parsed.matter);
|
|
1056
1064
|
// ✅ Ne pas propager l'erreur, continuer avec le nouvel ID généré
|
|
1057
1065
|
}
|
|
1058
1066
|
else {
|
|
@@ -1089,13 +1097,15 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
1089
1097
|
//
|
|
1090
1098
|
// without note the branche is not a valid PR branch
|
|
1091
1099
|
const oldNote = await (0, repo_tools_1.gitReadNote)(git, PR, gitConf.gitNotes.namespace, 10);
|
|
1100
|
+
if (oldNote && gitConf.verbose) {
|
|
1101
|
+
console.debug('[gitCreateOrEditFile] metadata.files before update:', oldNote.files);
|
|
1102
|
+
}
|
|
1092
1103
|
const commit = await _writeFileAndCommit(git, filePath, content, user, gitConf, `commit: ${filePath}`);
|
|
1093
1104
|
// ✅ Mettre à jour le cache du matter après sauvegarde
|
|
1094
1105
|
// NOTE: Le cache est TOUJOURS mis à jour, indépendamment de withID
|
|
1095
1106
|
// C'est une opération de cache, pas une validation
|
|
1096
1107
|
try {
|
|
1097
|
-
const
|
|
1098
|
-
const parsed = matterParse(content);
|
|
1108
|
+
const parsed = (0, utils_matter_1.matterParse)(content);
|
|
1099
1109
|
idRegistryService.init(gitConf.repoPath);
|
|
1100
1110
|
idRegistryService.setMatterCache(PR, filePath, {
|
|
1101
1111
|
id: parsed.matter.id,
|
|
@@ -1124,11 +1134,15 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
1124
1134
|
}
|
|
1125
1135
|
// Determine the current head after the commit attempt.
|
|
1126
1136
|
const newHead = (await git.revparse(['HEAD'])).trim();
|
|
1127
|
-
|
|
1137
|
+
let newFiles = await (0, repo_tools_1.gitGetDiffFiles)(git, PR, oldNote.mergeBase);
|
|
1138
|
+
newFiles = await sanitizePRFiles(git, newFiles, PR);
|
|
1128
1139
|
const updatedNote = {
|
|
1129
1140
|
...oldNote,
|
|
1130
1141
|
files: newFiles,
|
|
1131
1142
|
};
|
|
1143
|
+
if (gitConf.verbose) {
|
|
1144
|
+
console.debug('[gitCreateOrEditFile] metadata.files after update:', updatedNote.files);
|
|
1145
|
+
}
|
|
1132
1146
|
// Write the note to the current HEAD of the PR branch.
|
|
1133
1147
|
// If no commit was made, this overwrites the note on oldHead.
|
|
1134
1148
|
// If a new commit was made, this writes the note on newHead.
|
|
@@ -1331,12 +1345,24 @@ async function gitRenameFile(git, oldFileName, newFileName, branch, user, config
|
|
|
1331
1345
|
//
|
|
1332
1346
|
//FIXME: add a check about the branch (file can be NEW and on branch)
|
|
1333
1347
|
const fullOldPath = (0, path_1.join)(gitConf.uploadPath, oldFileName);
|
|
1348
|
+
let result;
|
|
1334
1349
|
if ((0, fs_1.existsSync)(fullOldPath)) {
|
|
1335
|
-
|
|
1350
|
+
result = await gitRenameFile_fs(git, oldFileName, newFileName, branch, user, config);
|
|
1336
1351
|
}
|
|
1337
1352
|
else {
|
|
1338
|
-
|
|
1353
|
+
result = await gitRenameFile_git(git, oldFileName, newFileName, branch, user, config);
|
|
1354
|
+
if (branch !== 'NEW' && branch.startsWith(gitConf.validationPrefix)) {
|
|
1355
|
+
try {
|
|
1356
|
+
await (0, repo_pr_1.gitPRReplaceFile)(git, branch, { remove: oldFileName, add: newFileName }, gitConf);
|
|
1357
|
+
}
|
|
1358
|
+
catch (metadataError) {
|
|
1359
|
+
if (gitConf.verbose) {
|
|
1360
|
+
console.warn('⚠️ Failed to update PR metadata after rename:', metadataError);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1339
1364
|
}
|
|
1365
|
+
return result;
|
|
1340
1366
|
}
|
|
1341
1367
|
catch (error) {
|
|
1342
1368
|
// Pour les vraies branches Git, ne pas faire de fallback filesystem
|
|
@@ -1663,9 +1689,7 @@ function createUnhealthyStatus(branch, exists, accessible, clean, mergeInProgres
|
|
|
1663
1689
|
* Vérifie si un merge est en cours en regardant les fichiers Git
|
|
1664
1690
|
*/
|
|
1665
1691
|
async function checkMergeInProgress(repoPath) {
|
|
1666
|
-
const
|
|
1667
|
-
const { join } = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1668
|
-
const gitDir = join(repoPath, '.git');
|
|
1692
|
+
const gitDir = (0, path_1.join)(repoPath, '.git');
|
|
1669
1693
|
const mergeFiles = [
|
|
1670
1694
|
'MERGE_HEAD',
|
|
1671
1695
|
'MERGE_MODE',
|
|
@@ -1673,5 +1697,27 @@ async function checkMergeInProgress(repoPath) {
|
|
|
1673
1697
|
'CHERRY_PICK_HEAD',
|
|
1674
1698
|
'REVERT_HEAD'
|
|
1675
1699
|
];
|
|
1676
|
-
return mergeFiles.some(file => existsSync(join(gitDir, file)));
|
|
1700
|
+
return mergeFiles.some(file => (0, fs_1.existsSync)((0, path_1.join)(gitDir, file)));
|
|
1701
|
+
}
|
|
1702
|
+
async function sanitizePRFiles(git, files, branch) {
|
|
1703
|
+
if (!files || files.length === 0) {
|
|
1704
|
+
return [];
|
|
1705
|
+
}
|
|
1706
|
+
const checks = await Promise.all(files.map(async (file) => ({
|
|
1707
|
+
file,
|
|
1708
|
+
exists: await (0, repo_tools_1.gitFileExistsInBranch)(git, file, branch)
|
|
1709
|
+
})));
|
|
1710
|
+
const sanitized = [];
|
|
1711
|
+
const seen = new Set();
|
|
1712
|
+
for (const { file, exists } of checks) {
|
|
1713
|
+
if (!exists) {
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
if (seen.has(file)) {
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
seen.add(file);
|
|
1720
|
+
sanitized.push(file);
|
|
1721
|
+
}
|
|
1722
|
+
return sanitized;
|
|
1677
1723
|
}
|
|
@@ -79,6 +79,14 @@ export declare function gitGetAllPR(git: SimpleGit, options?: {
|
|
|
79
79
|
*/
|
|
80
80
|
export declare function gitGetClosedPRs(git: SimpleGit, gitConfig: RulesGitConfig): Promise<PRInfo[]>;
|
|
81
81
|
export declare function gitLoadPR(git: SimpleGit, branch: string): Promise<PRInfo>;
|
|
82
|
+
/**
|
|
83
|
+
* Remplace explicitement un nom de fichier dans les métadonnées d'une PR.
|
|
84
|
+
* Utilisé pour refléter immédiatement les renames sans dépendre de git diff.
|
|
85
|
+
*/
|
|
86
|
+
export declare function gitPRReplaceFile(git: SimpleGit, branch: string, options?: {
|
|
87
|
+
remove?: string;
|
|
88
|
+
add?: string;
|
|
89
|
+
}, config?: RulesGitConfig): Promise<void>;
|
|
82
90
|
export declare function gitPRUpdateComments(git: SimpleGit, branch: string, details: RulePullRequestDetails, config?: RulesGitConfig): Promise<PRInfo>;
|
|
83
91
|
/**
|
|
84
92
|
* DEPRECATED: use gitClosePRRobust instead
|
|
@@ -40,6 +40,7 @@ exports.gitGetPRMetadata = gitGetPRMetadata;
|
|
|
40
40
|
exports.gitGetAllPR = gitGetAllPR;
|
|
41
41
|
exports.gitGetClosedPRs = gitGetClosedPRs;
|
|
42
42
|
exports.gitLoadPR = gitLoadPR;
|
|
43
|
+
exports.gitPRReplaceFile = gitPRReplaceFile;
|
|
43
44
|
exports.gitPRUpdateComments = gitPRUpdateComments;
|
|
44
45
|
exports.gitClosePR = gitClosePR;
|
|
45
46
|
exports.gitClosePRRobust = gitClosePRRobust;
|
|
@@ -321,7 +322,7 @@ async function gitLoadPR(git, branch) {
|
|
|
321
322
|
if (!metadata) {
|
|
322
323
|
throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
|
|
323
324
|
}
|
|
324
|
-
const files = await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md');
|
|
325
|
+
const files = metadata.files || (await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md'));
|
|
325
326
|
// Récupérer les infos du dernier commit de la branche
|
|
326
327
|
const log = await git.log({ from: branch, to: branch, maxCount: 1 });
|
|
327
328
|
const lastCommit = log.latest;
|
|
@@ -341,6 +342,49 @@ async function gitLoadPR(git, branch) {
|
|
|
341
342
|
throw new errors_1.GitOperationError(`Failed to retrieve PR: ${error}`, 'pr_load');
|
|
342
343
|
}
|
|
343
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Remplace explicitement un nom de fichier dans les métadonnées d'une PR.
|
|
347
|
+
* Utilisé pour refléter immédiatement les renames sans dépendre de git diff.
|
|
348
|
+
*/
|
|
349
|
+
async function gitPRReplaceFile(git, branch, options = {}, config) {
|
|
350
|
+
const { remove, add } = options;
|
|
351
|
+
if (!remove && !add) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
355
|
+
const metadata = await gitGetPRMetadata(git, branch, config);
|
|
356
|
+
if (!metadata) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const currentFiles = Array.isArray(metadata.files) ? metadata.files : [];
|
|
360
|
+
const seen = new Set();
|
|
361
|
+
const nextFiles = [];
|
|
362
|
+
for (const file of currentFiles) {
|
|
363
|
+
if (!file) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (remove && file === remove) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (seen.has(file)) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
seen.add(file);
|
|
373
|
+
nextFiles.push(file);
|
|
374
|
+
}
|
|
375
|
+
if (add && !seen.has(add)) {
|
|
376
|
+
nextFiles.push(add);
|
|
377
|
+
seen.add(add);
|
|
378
|
+
}
|
|
379
|
+
const changed = currentFiles.length !== nextFiles.length ||
|
|
380
|
+
currentFiles.some((file, idx) => file !== nextFiles[idx]);
|
|
381
|
+
if (!changed) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
metadata.files = nextFiles;
|
|
385
|
+
const headCommitHash = (await git.revparse([branch])).trim();
|
|
386
|
+
await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConf.gitNotes.namespace);
|
|
387
|
+
}
|
|
344
388
|
async function gitPRUpdateComments(git, branch, details, config) {
|
|
345
389
|
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
346
390
|
const metadata = await gitGetPRMetadata(git, branch, config);
|
|
@@ -56,7 +56,11 @@ export declare function gitReadFileOutsideRepo(git: SimpleGit, filePath: string)
|
|
|
56
56
|
content: string;
|
|
57
57
|
date: Date;
|
|
58
58
|
}>;
|
|
59
|
-
export declare function gitGetFileHistory(git: SimpleGit, filename: string,
|
|
59
|
+
export declare function gitGetFileHistory(git: SimpleGit, filename: string, options?: {
|
|
60
|
+
hash?: string;
|
|
61
|
+
branch?: string;
|
|
62
|
+
maxCommits?: number;
|
|
63
|
+
}): Promise<GitCommitHistory[]>;
|
|
60
64
|
/**
|
|
61
65
|
* Récupère le dernier commit d'une branche (opération bas niveau atomique)
|
|
62
66
|
* @param git Instance SimpleGit
|
|
@@ -338,10 +338,23 @@ async function gitReadFileOutsideRepo(git, filePath) {
|
|
|
338
338
|
throw error;
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
|
-
async function gitGetFileHistory(git, filename,
|
|
341
|
+
async function gitGetFileHistory(git, filename, options) {
|
|
342
|
+
// - Normalisation des paramètres pour compatibilité
|
|
342
343
|
try {
|
|
343
|
-
|
|
344
|
-
|
|
344
|
+
// - Utiliser git.log() pour obtenir l'historique des commits
|
|
345
|
+
// - git.log() retourne déjà les commits du plus récent au plus ancien (ordre standard Git)
|
|
346
|
+
const branch = options?.branch || 'HEAD';
|
|
347
|
+
const logArgs = [];
|
|
348
|
+
if (branch !== 'HEAD') {
|
|
349
|
+
logArgs.push(branch);
|
|
350
|
+
}
|
|
351
|
+
logArgs.push('--', filename);
|
|
352
|
+
const log = await git.log(logArgs);
|
|
353
|
+
if (log.all.length === 0) {
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
356
|
+
// - Construire l'historique (déjà dans l'ordre du plus récent au plus ancien)
|
|
357
|
+
let all = log.all.map(commit => ({
|
|
345
358
|
hash: commit.hash,
|
|
346
359
|
date: new Date(commit.date),
|
|
347
360
|
message: commit.message,
|
|
@@ -350,10 +363,44 @@ async function gitGetFileHistory(git, filename, hash) {
|
|
|
350
363
|
email: commit.author_email
|
|
351
364
|
}
|
|
352
365
|
}));
|
|
353
|
-
// Si un hash est fourni,
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
366
|
+
// - Si un hash est fourni, s'assurer qu'il est inclus avant l'échantillonnage
|
|
367
|
+
let requestedHashIndex = -1;
|
|
368
|
+
if (options?.hash) {
|
|
369
|
+
requestedHashIndex = all.findIndex(commit => commit.hash === options.hash);
|
|
370
|
+
}
|
|
371
|
+
// - Limitation du nombre de commits avec échantillonnage uniforme
|
|
372
|
+
// - Garantit que le premier (plus récent) et dernier (plus ancien) commit sont inclus
|
|
373
|
+
// - Si un hash spécifique est demandé, il est aussi garanti d'être inclus
|
|
374
|
+
if (options?.maxCommits && all.length > options.maxCommits) {
|
|
375
|
+
const step = Math.ceil((all.length - 2) / (options.maxCommits - 2)) || 1;
|
|
376
|
+
const sampled = [all[0]]; // Premier commit (plus récent)
|
|
377
|
+
const includedIndices = new Set([0]); // Track des indices déjà inclus
|
|
378
|
+
// - Si un hash spécifique est demandé et n'est pas le premier, l'ajouter
|
|
379
|
+
if (requestedHashIndex > 0 && requestedHashIndex < all.length - 1) {
|
|
380
|
+
sampled.push(all[requestedHashIndex]);
|
|
381
|
+
includedIndices.add(requestedHashIndex);
|
|
382
|
+
}
|
|
383
|
+
// - Échantillonnage des commits intermédiaires
|
|
384
|
+
for (let i = 1; i < all.length - 1; i += step) {
|
|
385
|
+
if (!includedIndices.has(i)) {
|
|
386
|
+
sampled.push(all[i]);
|
|
387
|
+
includedIndices.add(i);
|
|
388
|
+
}
|
|
389
|
+
if (sampled.length >= options.maxCommits - 1)
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
// - Dernier commit (plus ancien) si on a encore de la place
|
|
393
|
+
if (sampled.length < options.maxCommits && all.length > 1 && !includedIndices.has(all.length - 1)) {
|
|
394
|
+
sampled.push(all[all.length - 1]);
|
|
395
|
+
}
|
|
396
|
+
// - Trier par date décroissante pour maintenir l'ordre (plus récent en premier)
|
|
397
|
+
sampled.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
398
|
+
all = sampled;
|
|
399
|
+
}
|
|
400
|
+
// - Si un hash est fourni, retourner le contenu du fichier à ce hash
|
|
401
|
+
if (options?.hash) {
|
|
402
|
+
const fileContent = await gitGetFileContent(git, filename, options.hash);
|
|
403
|
+
const index = all.findIndex(commit => commit.hash === options.hash);
|
|
357
404
|
if (index !== -1 && fileContent) {
|
|
358
405
|
all[index] = fileContent;
|
|
359
406
|
}
|
|
@@ -334,6 +334,20 @@ export interface GitCommitHistory {
|
|
|
334
334
|
/** Branche où s'est produit ce commit (optionnel) */
|
|
335
335
|
branch?: string;
|
|
336
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Structure simplifiée d'un commit pour la prévisualisation
|
|
339
|
+
*
|
|
340
|
+
* Utilisée dans loadPreview() pour retourner uniquement les informations essentielles
|
|
341
|
+
* sans le contenu complet du commit.
|
|
342
|
+
*/
|
|
343
|
+
export interface CommitPreview {
|
|
344
|
+
/** Hash unique du commit */
|
|
345
|
+
hash: string;
|
|
346
|
+
/** Date et heure du commit */
|
|
347
|
+
date: Date;
|
|
348
|
+
/** Nom de l'auteur du commit */
|
|
349
|
+
author: string;
|
|
350
|
+
}
|
|
337
351
|
/**
|
|
338
352
|
* Résumé d'un fichier Git avec son commit associé
|
|
339
353
|
*
|
|
@@ -40,23 +40,3 @@ export declare function matterParse(markdown: string): {
|
|
|
40
40
|
*/
|
|
41
41
|
export declare function matterSerializeFromRule(rule: Rule): string;
|
|
42
42
|
export declare function matterSerialize(content: string, matter: FrontMatter | any): string;
|
|
43
|
-
/**
|
|
44
|
-
* Extrait le front-matter (entre les deux premiers '---') et le contenu Markdown.
|
|
45
|
-
* Fonction 100% vanilla : aucun paquet externe requis.
|
|
46
|
-
* PROS:
|
|
47
|
-
* Code plus propre et maintenable
|
|
48
|
-
* - Typage TypeScript strict
|
|
49
|
-
* - Plus robuste (ignore lignes indentées)
|
|
50
|
-
* - Séparation des responsabilités
|
|
51
|
-
* - Meilleure architecture (parsing pur)
|
|
52
|
-
* CONS:
|
|
53
|
-
* - Perte de fonctionnalité (slugs/tags)
|
|
54
|
-
*
|
|
55
|
-
* @param markdown Texte Markdown brut
|
|
56
|
-
* @returns Objet avec matter, content et data
|
|
57
|
-
*/
|
|
58
|
-
export declare function matterParse_OTHER(markdown: string): {
|
|
59
|
-
matter: Record<string, any>;
|
|
60
|
-
content: string;
|
|
61
|
-
data: Record<string, any>;
|
|
62
|
-
};
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.matterParse = matterParse;
|
|
4
4
|
exports.matterSerializeFromRule = matterSerializeFromRule;
|
|
5
5
|
exports.matterSerialize = matterSerialize;
|
|
6
|
-
exports.matterParse_OTHER = matterParse_OTHER;
|
|
7
6
|
/**
|
|
8
7
|
* Parse le front-matter YAML et le contenu Markdown d'un document
|
|
9
8
|
*
|
|
@@ -152,85 +151,54 @@ function matterSerialize(content, matter) {
|
|
|
152
151
|
return acc + `${key}: '${value.toISOString()}'\n`;
|
|
153
152
|
}
|
|
154
153
|
else {
|
|
155
|
-
|
|
156
|
-
return acc + `${key}:
|
|
154
|
+
const formatted = formatYAMLString(value.toString());
|
|
155
|
+
return acc + `${key}: ${formatted}\n`;
|
|
157
156
|
}
|
|
158
157
|
}, '');
|
|
159
158
|
return `---
|
|
160
159
|
${result}---
|
|
161
160
|
${content}`;
|
|
162
161
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
* Code plus propre et maintenable
|
|
168
|
-
* - Typage TypeScript strict
|
|
169
|
-
* - Plus robuste (ignore lignes indentées)
|
|
170
|
-
* - Séparation des responsabilités
|
|
171
|
-
* - Meilleure architecture (parsing pur)
|
|
172
|
-
* CONS:
|
|
173
|
-
* - Perte de fonctionnalité (slugs/tags)
|
|
174
|
-
*
|
|
175
|
-
* @param markdown Texte Markdown brut
|
|
176
|
-
* @returns Objet avec matter, content et data
|
|
177
|
-
*/
|
|
178
|
-
function matterParse_OTHER(markdown) {
|
|
179
|
-
// 1. Séparer les lignes
|
|
180
|
-
const lines = markdown.split(/\r?\n/);
|
|
181
|
-
if (lines[0].trim() !== '---') {
|
|
182
|
-
return { matter: {}, content: markdown, data: {} }; // Pas de front-matter
|
|
162
|
+
function formatYAMLString(value) {
|
|
163
|
+
const shouldQuote = needsQuotes(value);
|
|
164
|
+
if (!shouldQuote) {
|
|
165
|
+
return value;
|
|
183
166
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
while (end < lines.length && lines[end].trim() !== '---')
|
|
187
|
-
end++;
|
|
188
|
-
// 3. Parser le bloc front-matter (simple clé-valeur)
|
|
189
|
-
const matter = {};
|
|
190
|
-
for (let i = 1; i < end; i++) {
|
|
191
|
-
const line = lines[i];
|
|
192
|
-
if (!line.trim())
|
|
193
|
-
continue; // ignorer les lignes vides
|
|
194
|
-
// Ignorer les lignes indentées (structures complexes non supportées)
|
|
195
|
-
if (line.startsWith(' ') || line.startsWith('\t')) {
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
const trimmedLine = line.trim();
|
|
199
|
-
if (trimmedLine.includes(':')) {
|
|
200
|
-
const [rawKey, ...rawValue] = trimmedLine.split(':');
|
|
201
|
-
const key = rawKey.trim();
|
|
202
|
-
const value = rawValue.join(':').trim();
|
|
203
|
-
if (value) {
|
|
204
|
-
// Détection et parsing des arrays YAML
|
|
205
|
-
if (value.startsWith('[') && value.endsWith(']')) {
|
|
206
|
-
try {
|
|
207
|
-
const parsed = JSON.parse(value);
|
|
208
|
-
if (Array.isArray(parsed)) {
|
|
209
|
-
matter[key] = parsed;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
matter[key] = value.replace(/^["']|["']$/g, '');
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
catch (e) {
|
|
216
|
-
matter[key] = value.replace(/^["']|["']$/g, '');
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// Tentative de typage léger : nombre, booléen, sinon chaîne
|
|
220
|
-
else if (/^(true|false)$/i.test(value)) {
|
|
221
|
-
matter[key] = value.toLowerCase() === 'true';
|
|
222
|
-
}
|
|
223
|
-
else if (!isNaN(Number(value)) && value !== '') {
|
|
224
|
-
matter[key] = Number(value);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
matter[key] = value.replace(/^["']|["']$/g, '');
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
167
|
+
if (process.env.DEBUG_YAML === '1') {
|
|
168
|
+
console.warn('[matterSerialize] quoting value:', value);
|
|
231
169
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
170
|
+
const escaped = value.replace(/'/g, "''");
|
|
171
|
+
return `'${escaped}'`;
|
|
172
|
+
}
|
|
173
|
+
function needsQuotes(value) {
|
|
174
|
+
if (value.length === 0) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const firstChar = value[0];
|
|
178
|
+
const lastChar = value[value.length - 1];
|
|
179
|
+
if (firstChar === ' ' || lastChar === ' ') {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
if (/[:#{}\[\],&*?]/.test(value)) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
if (/^-/.test(value)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
if (/^\d/.test(value)) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
if (value.includes('\n')) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (value.includes('- ')) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
if (value.includes('"')) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
if (value.includes("'")) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
236
204
|
}
|
package/dist/src/scrapper.js
CHANGED
|
@@ -13,7 +13,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
13
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
14
|
const jsdom_1 = require("jsdom");
|
|
15
15
|
const readability_1 = require("@mozilla/readability");
|
|
16
|
-
const
|
|
16
|
+
const pricing_1 = require("./llm/pricing");
|
|
17
17
|
const prompts_1 = require("./prompts");
|
|
18
18
|
const utils_1 = require("./utils");
|
|
19
19
|
const execute_1 = require("./execute");
|
|
@@ -37,7 +37,7 @@ async function extractCaptcha(base64Image, openai) {
|
|
|
37
37
|
messages: [{ role: "user", content }],
|
|
38
38
|
max_completion_tokens: 50,
|
|
39
39
|
});
|
|
40
|
-
const cost = (0,
|
|
40
|
+
const cost = (0, pricing_1.calculateCost)(model, response.usage);
|
|
41
41
|
// Récupérer la réponse markdown
|
|
42
42
|
const number = response.choices[0].message.content;
|
|
43
43
|
return { number, cost };
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { ParsedFunctionToolCall } from "openai/resources/beta/chat/completions";
|
|
2
2
|
import { AgentConfig, AgenticContext } from "./types";
|
|
3
|
-
import OpenAI from "openai";
|
|
4
|
-
/**
|
|
5
|
-
* global openai instance
|
|
6
|
-
*/
|
|
7
|
-
declare global {
|
|
8
|
-
var _openaiInstance_: OpenAI | undefined;
|
|
9
|
-
}
|
|
10
|
-
export declare const openaiInstance: (envKey?: string, baseUrl?: string) => OpenAI;
|
|
11
3
|
/**
|
|
12
4
|
* Converts a string to a URL-friendly slug
|
|
13
5
|
* @param text The text to convert to a slug
|
package/dist/src/utils.js
CHANGED
|
@@ -1,36 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.toSlug =
|
|
3
|
+
exports.toSlug = void 0;
|
|
7
4
|
exports.injectTransferTools = injectTransferTools;
|
|
8
5
|
exports.handleTransferCall = handleTransferCall;
|
|
9
6
|
const prompts_1 = require("./prompts");
|
|
10
|
-
const openai_1 = __importDefault(require("openai"));
|
|
11
|
-
const openaiInstance = function (envKey, baseUrl) {
|
|
12
|
-
if (globalThis._openaiInstance_) {
|
|
13
|
-
return globalThis._openaiInstance_;
|
|
14
|
-
}
|
|
15
|
-
if (!envKey) {
|
|
16
|
-
throw new Error('AI API key is missing');
|
|
17
|
-
}
|
|
18
|
-
//
|
|
19
|
-
// use multiple fallback if envKey is not set
|
|
20
|
-
const options = {
|
|
21
|
-
apiKey: process.env[envKey] || envKey || process.env.API_LLM_KEY || process.env.OPENAI_API_KEY,
|
|
22
|
-
timeout: 60000 * 15
|
|
23
|
-
};
|
|
24
|
-
if (baseUrl) {
|
|
25
|
-
options.baseURL = baseUrl;
|
|
26
|
-
}
|
|
27
|
-
globalThis._openaiInstance_ = new openai_1.default(options);
|
|
28
|
-
if (!globalThis._openaiInstance_) {
|
|
29
|
-
throw new Error('OpenAI instance has not been initialized');
|
|
30
|
-
}
|
|
31
|
-
return globalThis._openaiInstance_;
|
|
32
|
-
};
|
|
33
|
-
exports.openaiInstance = openaiInstance;
|
|
34
7
|
/**
|
|
35
8
|
* Converts a string to a URL-friendly slug
|
|
36
9
|
* @param text The text to convert to a slug
|
package/package.json
CHANGED