agentic-api 1.0.6 → 2.0.31
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 +336 -76
- package/dist/src/agents/agents.example.d.ts +3 -0
- package/dist/src/agents/agents.example.js +38 -0
- package/dist/src/agents/authentication.js +2 -0
- package/dist/src/agents/prompts.d.ts +2 -2
- package/dist/src/agents/prompts.js +112 -49
- package/dist/src/agents/reducer.core.d.ts +12 -0
- package/dist/src/agents/reducer.core.js +207 -0
- package/dist/src/agents/reducer.d.ts +3 -0
- package/dist/src/agents/reducer.example.d.ts +28 -0
- package/dist/src/agents/reducer.example.js +118 -0
- package/dist/src/agents/reducer.js +19 -0
- package/dist/src/agents/reducer.loaders.d.ts +34 -0
- package/dist/src/agents/reducer.loaders.js +122 -0
- package/dist/src/agents/reducer.process.d.ts +16 -0
- package/dist/src/agents/reducer.process.js +143 -0
- package/dist/src/agents/reducer.tools.d.ts +29 -0
- package/dist/src/agents/reducer.tools.js +157 -0
- package/dist/src/agents/reducer.types.d.ts +50 -0
- package/dist/src/agents/reducer.types.js +5 -0
- package/dist/src/agents/simulator.d.ts +47 -0
- package/dist/src/agents/simulator.executor.d.ts +26 -0
- package/dist/src/agents/simulator.executor.js +132 -0
- package/dist/src/agents/simulator.js +205 -0
- package/dist/src/agents/simulator.prompts.d.ts +16 -0
- package/dist/src/agents/simulator.prompts.js +108 -0
- package/dist/src/agents/simulator.types.d.ts +42 -0
- package/dist/src/agents/simulator.types.js +2 -0
- package/dist/src/agents/simulator.utils.d.ts +20 -0
- package/dist/src/agents/simulator.utils.js +87 -0
- package/dist/src/execute.d.ts +13 -6
- package/dist/src/execute.js +351 -85
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +14 -0
- package/dist/src/princing.openai.d.ts +9 -2
- package/dist/src/princing.openai.js +15 -11
- package/dist/src/prompts.d.ts +3 -2
- package/dist/src/prompts.js +159 -19
- package/dist/src/rag/embeddings.d.ts +103 -0
- package/dist/src/rag/embeddings.js +466 -0
- package/dist/src/rag/index.d.ts +12 -0
- package/dist/src/rag/index.js +40 -0
- package/dist/src/rag/lucene.d.ts +45 -0
- package/dist/src/rag/lucene.js +227 -0
- package/dist/src/rag/parser.d.ts +68 -0
- package/dist/src/rag/parser.js +192 -0
- package/dist/src/rag/tools.d.ts +76 -0
- package/dist/src/rag/tools.js +196 -0
- package/dist/src/rag/types.d.ts +178 -0
- package/dist/src/rag/types.js +21 -0
- package/dist/src/rag/usecase.d.ts +16 -0
- package/dist/src/rag/usecase.js +79 -0
- package/dist/src/rules/errors.d.ts +60 -0
- package/dist/src/rules/errors.js +97 -0
- package/dist/src/rules/git/git.e2e.helper.d.ts +104 -0
- package/dist/src/rules/git/git.e2e.helper.js +488 -0
- package/dist/src/rules/git/git.health.d.ts +66 -0
- package/dist/src/rules/git/git.health.js +354 -0
- package/dist/src/rules/git/git.helper.d.ts +129 -0
- package/dist/src/rules/git/git.helper.js +53 -0
- package/dist/src/rules/git/index.d.ts +6 -0
- package/dist/src/rules/git/index.js +76 -0
- package/dist/src/rules/git/repo.d.ts +128 -0
- package/dist/src/rules/git/repo.js +900 -0
- package/dist/src/rules/git/repo.pr.d.ts +137 -0
- package/dist/src/rules/git/repo.pr.js +589 -0
- package/dist/src/rules/git/repo.tools.d.ts +134 -0
- package/dist/src/rules/git/repo.tools.js +730 -0
- package/dist/src/rules/index.d.ts +8 -0
- package/dist/src/rules/index.js +25 -0
- package/dist/src/rules/messages.d.ts +17 -0
- package/dist/src/rules/messages.js +21 -0
- package/dist/src/rules/types.ctrl.d.ts +28 -0
- package/dist/src/rules/types.ctrl.js +2 -0
- package/dist/src/rules/types.d.ts +510 -0
- package/dist/src/rules/types.helpers.d.ts +132 -0
- package/dist/src/rules/types.helpers.js +2 -0
- package/dist/src/rules/types.js +33 -0
- package/dist/src/rules/user.mapper.d.ts +61 -0
- package/dist/src/rules/user.mapper.js +160 -0
- package/dist/src/rules/utils/slug.d.ts +22 -0
- package/dist/src/rules/utils/slug.js +35 -0
- package/dist/src/rules/utils.matter.d.ts +66 -0
- package/dist/src/rules/utils.matter.js +208 -0
- package/dist/src/rules/utils.slug.d.ts +22 -0
- package/dist/src/rules/utils.slug.js +35 -0
- package/dist/src/scrapper.d.ts +3 -2
- package/dist/src/scrapper.js +33 -37
- package/dist/src/stategraph/index.d.ts +8 -0
- package/dist/src/stategraph/index.js +21 -0
- package/dist/src/stategraph/stategraph.d.ts +91 -0
- package/dist/src/stategraph/stategraph.js +241 -0
- package/dist/src/stategraph/stategraph.storage.d.ts +41 -0
- package/dist/src/stategraph/stategraph.storage.js +166 -0
- package/dist/src/stategraph/types.d.ts +139 -0
- package/dist/src/stategraph/types.js +19 -0
- package/dist/src/types.d.ts +62 -39
- package/dist/src/types.js +53 -89
- package/dist/src/usecase.d.ts +4 -0
- package/dist/src/usecase.js +44 -0
- package/dist/src/utils.d.ts +12 -5
- package/dist/src/utils.js +30 -13
- package/package.json +9 -3
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.gitSyncPR = gitSyncPR;
|
|
37
|
+
exports.gitIsPRClosed = gitIsPRClosed;
|
|
38
|
+
exports.gitIsPRClosedRobust = gitIsPRClosedRobust;
|
|
39
|
+
exports.gitGetPRMetadata = gitGetPRMetadata;
|
|
40
|
+
exports.gitGetAllPR = gitGetAllPR;
|
|
41
|
+
exports.gitGetClosedPRs = gitGetClosedPRs;
|
|
42
|
+
exports.gitLoadPR = gitLoadPR;
|
|
43
|
+
exports.gitPRUpdateComments = gitPRUpdateComments;
|
|
44
|
+
exports.gitClosePR = gitClosePR;
|
|
45
|
+
exports.gitClosePRRobust = gitClosePRRobust;
|
|
46
|
+
exports.gitGetNextPRNumber = gitGetNextPRNumber;
|
|
47
|
+
exports.gitNewValidationRequest = gitNewValidationRequest;
|
|
48
|
+
exports.gitNewPR = gitNewPR;
|
|
49
|
+
const errors_1 = require("../errors");
|
|
50
|
+
const path_1 = require("path");
|
|
51
|
+
const fs = __importStar(require("fs/promises"));
|
|
52
|
+
const repo_tools_1 = require("./repo.tools");
|
|
53
|
+
/**
|
|
54
|
+
* Synchronise une branche PR avec son mergeBase pour corriger les références orphelines
|
|
55
|
+
*
|
|
56
|
+
* FIXME: Cette fonction a un problème de gestion des métadonnées manquantes.
|
|
57
|
+
*
|
|
58
|
+
* PROBLÈME IDENTIFIÉ:
|
|
59
|
+
* - Les branches de validation (rule-validation-*) DOIVENT avoir des métadonnées PR
|
|
60
|
+
* - Actuellement, si les métadonnées sont manquantes, la fonction fait un return silencieux
|
|
61
|
+
* - Ceci masque un vrai problème : pourquoi une branche de validation n'a pas de métadonnées ?
|
|
62
|
+
*
|
|
63
|
+
* CAUSES POSSIBLES:
|
|
64
|
+
* - Problème de namespace Git Notes (création vs lecture dans des namespaces différents)
|
|
65
|
+
* - Race condition entre création et lecture des Git Notes
|
|
66
|
+
* - Configuration E2E différente entre helper.newPR() et gitSyncPR()
|
|
67
|
+
* - Corruption des Git Notes lors de la simulation du "reset" dans le test
|
|
68
|
+
*
|
|
69
|
+
* SOLUTION RECOMMANDÉE:
|
|
70
|
+
* - Ajouter un diagnostic approfondi pour identifier pourquoi les métadonnées sont manquantes
|
|
71
|
+
* - Distinguer le traitement : strict pour branches de validation, tolérant pour autres branches
|
|
72
|
+
* - Corriger la cause racine au lieu de masquer le symptôme
|
|
73
|
+
*
|
|
74
|
+
* IMPACT MÉTIER:
|
|
75
|
+
* - Une branche de validation sans métadonnées indique un problème d'intégrité des données
|
|
76
|
+
* - Le comportement actuel (return silencieux) peut masquer des corruptions de données
|
|
77
|
+
* - Les utilisateurs pourraient perdre des informations de PR importantes
|
|
78
|
+
*
|
|
79
|
+
* @param git Instance Git
|
|
80
|
+
* @param branch Nom de la branche PR à synchroniser
|
|
81
|
+
* @param user Utilisateur qui effectue la synchronisation
|
|
82
|
+
* @returns Promise<void>
|
|
83
|
+
*/
|
|
84
|
+
async function gitSyncPR(git, branch, user) {
|
|
85
|
+
const gitConfig = (0, repo_tools_1.gitLoad)();
|
|
86
|
+
await (0, repo_tools_1.lock)('checkout');
|
|
87
|
+
let currentBranch;
|
|
88
|
+
try {
|
|
89
|
+
currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
90
|
+
// Vérifier que la branche PR existe
|
|
91
|
+
const allBranches = await (0, repo_tools_1.gitGetAllBranches)(git);
|
|
92
|
+
if (!allBranches.includes(branch)) {
|
|
93
|
+
throw new errors_1.BranchNotFoundError(branch);
|
|
94
|
+
}
|
|
95
|
+
// Charger les métadonnées de la PR
|
|
96
|
+
const pr = await gitLoadPR(git, branch);
|
|
97
|
+
if (!pr || !pr.metadata) {
|
|
98
|
+
throw new errors_1.GitOperationError(`PR metadata not found for branch ${branch}`, 'pr_sync', { branch });
|
|
99
|
+
}
|
|
100
|
+
if (pr.metadata.status === 'closed') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Vérifier que le mergeBase existe et est accessible
|
|
104
|
+
const mergeBase = pr.metadata.mergeBase;
|
|
105
|
+
if (!mergeBase) {
|
|
106
|
+
throw new errors_1.GitOperationError(`No merge base found for PR ${branch}`, 'pr_sync', { branch });
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
// Vérifier que le mergeBase est un commit valide
|
|
110
|
+
await git.raw(['rev-parse', '--verify', `${mergeBase}^{commit}`]);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw new errors_1.GitOperationError(`Merge base ${mergeBase} is not a valid commit for PR ${branch}`, 'pr_sync', { branch, mergeBase, error });
|
|
114
|
+
}
|
|
115
|
+
// Basculer vers la branche PR
|
|
116
|
+
await git.checkout(branch);
|
|
117
|
+
// Effectuer le merge avec stratégie "recursive" et option "theirs" pour gérer les conflits
|
|
118
|
+
try {
|
|
119
|
+
// const merge =await git.env({
|
|
120
|
+
// GIT_COMMITTER_NAME: user.name,
|
|
121
|
+
// GIT_COMMITTER_EMAIL: user.email
|
|
122
|
+
// }).raw([
|
|
123
|
+
//
|
|
124
|
+
// ours = (branche PR avec travail éditeur) → PRÉSERVÉ le travail éditeur
|
|
125
|
+
// theirs = (mergeBase avec modifications concurrentes) → IGNORÉ en cas de conflit
|
|
126
|
+
const merge = await git.raw([
|
|
127
|
+
'merge',
|
|
128
|
+
mergeBase,
|
|
129
|
+
'--no-ff',
|
|
130
|
+
'--strategy', 'recursive',
|
|
131
|
+
'--strategy-option', 'ours',
|
|
132
|
+
'-m', `Sync: Merge ${mergeBase} into ${branch}`
|
|
133
|
+
]);
|
|
134
|
+
//console.log('merge',merge);
|
|
135
|
+
}
|
|
136
|
+
catch (mergeError) {
|
|
137
|
+
console.log('mergeError', mergeError);
|
|
138
|
+
// FIXME: this is a fallback, we should not use it
|
|
139
|
+
// En cas d'échec du merge, tenter avec une stratégie plus agressive
|
|
140
|
+
try {
|
|
141
|
+
await git.raw(['merge', '--abort']).catch(() => { }); // Ignore si pas de merge en cours
|
|
142
|
+
await git.raw([
|
|
143
|
+
'merge',
|
|
144
|
+
mergeBase,
|
|
145
|
+
'--no-ff',
|
|
146
|
+
'--strategy', 'ours',
|
|
147
|
+
'-m', `Sync: Force merge ${mergeBase} into ${branch} (conflicts resolved)`
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
catch (fallbackError) {
|
|
151
|
+
throw new errors_1.GitOperationError(`Failed to sync PR ${branch} with merge base ${mergeBase}: ${mergeError}`, 'pr_sync', { branch, mergeBase, mergeError, fallbackError });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Préserver les fichiers originaux de la PR (ne pas recalculer après merge)
|
|
155
|
+
// Note: Après un merge réussi, gitGetDiffFiles retournera [] car il n'y a plus de différences
|
|
156
|
+
// Les fichiers de la PR sont une information historique qui doit être préservée
|
|
157
|
+
const updatedMetadata = {
|
|
158
|
+
...pr.metadata,
|
|
159
|
+
files: pr.metadata.files, // Préserver les fichiers originaux
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
lastSync: new Date().toISOString(), // Ajouter timestamp de synchronisation
|
|
162
|
+
};
|
|
163
|
+
// Get the commit hash of the HEAD of the branch
|
|
164
|
+
const headCommitHash = (await git.revparse([branch])).trim();
|
|
165
|
+
await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, updatedMetadata, gitConfig.gitNotes.namespace);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (error instanceof errors_1.GitOperationError || error instanceof errors_1.BranchNotFoundError) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
throw new errors_1.GitOperationError(`Failed to sync PR ${branch}: ${error}`, 'pr_sync', { branch, error });
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
if (currentBranch) {
|
|
175
|
+
try {
|
|
176
|
+
await git.checkout(currentBranch);
|
|
177
|
+
}
|
|
178
|
+
catch (checkoutError) {
|
|
179
|
+
if (gitConfig.verbose) {
|
|
180
|
+
console.error(`Failed to checkout back to ${currentBranch}: ${checkoutError}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
(0, repo_tools_1.unlock)('checkout');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Vérifie si une branche PR est fermée en regardant le message du dernier commit
|
|
189
|
+
* @param git Instance Git
|
|
190
|
+
* @param branch Nom de la branche PR
|
|
191
|
+
* @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
|
|
192
|
+
* @returns true si le PR est fermé (dernier commit contient le token de validation)
|
|
193
|
+
* DEPRECATED: use gitIsPRClosedRobust instead
|
|
194
|
+
*/
|
|
195
|
+
async function gitIsPRClosed(git, branch) {
|
|
196
|
+
return gitIsPRClosedRobust(git, branch);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Vérifie si une branche PR est fermée en utilisant Git Notes (avec fallback)
|
|
200
|
+
* Version robuste qui remplace gitIsPRClosed
|
|
201
|
+
* @param git Instance Git
|
|
202
|
+
* @param branch Nom de la branche PR
|
|
203
|
+
* @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
|
|
204
|
+
* @param config Configuration Git (optionnel, utilise la config globale)
|
|
205
|
+
* @returns true si le PR est fermé
|
|
206
|
+
*/
|
|
207
|
+
async function gitIsPRClosedRobust(git, branch, config) {
|
|
208
|
+
const gitConfig = (0, repo_tools_1.gitLoad)(config);
|
|
209
|
+
try {
|
|
210
|
+
const note = await gitGetPRMetadata(git, branch, config);
|
|
211
|
+
// If the branch is not found, then METADATA is Null, it is closed
|
|
212
|
+
if (!note) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return ['closed', 'merged'].includes(note.status);
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
// If the branch is not found, or not compliant with METADATA, it is closed
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Vérifie si une branche PR est fermée en utilisant Git Notes (avec fallback)
|
|
224
|
+
* Version robuste qui remplace gitIsPRClosed
|
|
225
|
+
* @param git Instance Git
|
|
226
|
+
* @param branch Nom de la branche PR
|
|
227
|
+
* @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
|
|
228
|
+
* @param config Configuration Git (optionnel, utilise la config globale)
|
|
229
|
+
* @returns true si le PR est fermé
|
|
230
|
+
*/
|
|
231
|
+
async function gitGetPRMetadata(git, branch, config) {
|
|
232
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
233
|
+
try {
|
|
234
|
+
// D'abord essayer de chercher sur le commit HEAD de la branche
|
|
235
|
+
let metadata = await (0, repo_tools_1.gitReadNote)(git, branch, gitConf.gitNotes.namespace, 20);
|
|
236
|
+
// DEPRECATED: we now we always use the last 20 commits to find the metadata
|
|
237
|
+
// if (!metadata) {
|
|
238
|
+
// metadata = await gitReadNote(git, branch, gitConf.gitNotes.namespace, 20);
|
|
239
|
+
// }
|
|
240
|
+
return metadata;
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
// This typically happens if the branch doesn't exist, which is a valid case.
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Récupère tous les pull requests (branches rule-validation-*) avec leur statut
|
|
249
|
+
* @param git Instance Gitno
|
|
250
|
+
* @param options Configuration pour filtrer les PRs (default: list open PRs)
|
|
251
|
+
* @returns Liste des PRs avec leur statut (au niveau de la branche)
|
|
252
|
+
*/
|
|
253
|
+
async function gitGetAllPR(git, options = {}) {
|
|
254
|
+
try {
|
|
255
|
+
const gitConfig = (0, repo_tools_1.gitLoad)();
|
|
256
|
+
const { closed: listOnlyClosed, validationPrefix = gitConfig.validationPrefix, } = options;
|
|
257
|
+
const allLocalBranchesRaw = await (0, repo_tools_1.gitGetAllBranches)(git);
|
|
258
|
+
const prCandidateBranches = allLocalBranchesRaw.filter((branch) => branch.startsWith(validationPrefix));
|
|
259
|
+
const prs = [];
|
|
260
|
+
for (const branch of prCandidateBranches) {
|
|
261
|
+
try {
|
|
262
|
+
const pr = await gitLoadPR(git, branch);
|
|
263
|
+
const isOpen = pr.metadata.status === 'open';
|
|
264
|
+
if (listOnlyClosed) {
|
|
265
|
+
if (!isOpen) {
|
|
266
|
+
prs.push(pr);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
if (isOpen) {
|
|
271
|
+
prs.push(pr);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
if (gitConfig.verbose)
|
|
277
|
+
console.error(`[gitGetAllPR] Error loading PR ${branch}:`, error);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return prs;
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
throw new errors_1.GitOperationError(`Failed to retrieve PRs: ${error}`, 'pr_listing', { options, originalError: error });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Finds closed PRs by scanning the history of the draft branch for merge commits with PR metadata.
|
|
289
|
+
* @param git SimpleGit instance
|
|
290
|
+
* @param gitConfig The git configuration
|
|
291
|
+
* @returns A list of PRInfo objects for closed PRs.
|
|
292
|
+
*/
|
|
293
|
+
async function gitGetClosedPRs(git, gitConfig) {
|
|
294
|
+
const log = await git.log({
|
|
295
|
+
from: gitConfig.draftBranch,
|
|
296
|
+
'--merges': null, // Only show merge commits
|
|
297
|
+
});
|
|
298
|
+
const prs = [];
|
|
299
|
+
for (const commit of log.all) {
|
|
300
|
+
const note = await (0, repo_tools_1.gitReadNote)(git, commit.hash, gitConfig.gitNotes.namespace, 1);
|
|
301
|
+
if (note && note.status === 'closed' && note.id) {
|
|
302
|
+
const pr = {
|
|
303
|
+
branch: `${gitConfig.validationPrefix}${note.id}`, // Reconstruct original branch name
|
|
304
|
+
files: note.files || [],
|
|
305
|
+
open: false,
|
|
306
|
+
lastCommit: commit.hash,
|
|
307
|
+
lastCommitMessage: commit.message,
|
|
308
|
+
lastModified: new Date(commit.date),
|
|
309
|
+
metadata: note,
|
|
310
|
+
toString() { return this.branch; }
|
|
311
|
+
};
|
|
312
|
+
prs.push(pr);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return prs;
|
|
316
|
+
}
|
|
317
|
+
async function gitLoadPR(git, branch) {
|
|
318
|
+
try {
|
|
319
|
+
// Load metadata from
|
|
320
|
+
const metadata = await gitGetPRMetadata(git, branch);
|
|
321
|
+
if (!metadata) {
|
|
322
|
+
throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
|
|
323
|
+
}
|
|
324
|
+
const files = metadata.files || await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md');
|
|
325
|
+
// Récupérer les infos du dernier commit de la branche
|
|
326
|
+
const log = await git.log({ from: branch, to: branch, maxCount: 1 });
|
|
327
|
+
const lastCommit = log.latest;
|
|
328
|
+
const pr = {
|
|
329
|
+
branch,
|
|
330
|
+
files,
|
|
331
|
+
open: metadata.status == 'open',
|
|
332
|
+
lastCommit: lastCommit?.hash,
|
|
333
|
+
lastCommitMessage: lastCommit?.message,
|
|
334
|
+
lastModified: lastCommit?.date ? new Date(lastCommit.date) : undefined,
|
|
335
|
+
metadata: metadata, // metadata should exist if we read a note
|
|
336
|
+
toString() { return this.branch; }
|
|
337
|
+
};
|
|
338
|
+
return pr;
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
throw new errors_1.GitOperationError(`Failed to retrieve PR: ${error}`, 'pr_load');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function gitPRUpdateComments(git, branch, details, config) {
|
|
345
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
346
|
+
const metadata = await gitGetPRMetadata(git, branch, config);
|
|
347
|
+
if (!metadata) {
|
|
348
|
+
throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
|
|
349
|
+
}
|
|
350
|
+
// FIXME: only the owner of the PR can update metadatas
|
|
351
|
+
metadata.comments = details.comments || metadata.comments;
|
|
352
|
+
metadata.readyForValidation = details.readyForValidation;
|
|
353
|
+
//
|
|
354
|
+
// update validator only if it is not already set
|
|
355
|
+
if (details.validator) {
|
|
356
|
+
metadata.validator = details.validator;
|
|
357
|
+
}
|
|
358
|
+
// Get the commit hash of the HEAD of the branch
|
|
359
|
+
const headCommitHash = (await git.revparse([branch])).trim();
|
|
360
|
+
await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConf.gitNotes.namespace);
|
|
361
|
+
return gitLoadPR(git, branch);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* DEPRECATED: use gitClosePRRobust instead
|
|
365
|
+
* Ferme un PR en ajoutant un commit avec le token de validation
|
|
366
|
+
* @param git Instance Git
|
|
367
|
+
* @param branch Branche PR à fermer
|
|
368
|
+
* @param author Utilisateur qui ferme le PR
|
|
369
|
+
* @param message Message optionnel à ajouter avant le token de validation
|
|
370
|
+
* @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
|
|
371
|
+
* @returns Hash du commit de fermeture
|
|
372
|
+
* DEPRECATED: use gitClosePRRobust instead
|
|
373
|
+
*/
|
|
374
|
+
async function gitClosePR(git, branch, author, message, validationToken) {
|
|
375
|
+
return gitClosePRRobust(git, branch, author, message);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Ferme un PR en utilisant Git Notes (avec fallback vers commit)
|
|
379
|
+
* Version robuste qui remplace gitClosePR
|
|
380
|
+
* @param git Instance Git
|
|
381
|
+
* @param branch Branche PR à fermer
|
|
382
|
+
* @param closedBy Utilisateur qui ferme le PR
|
|
383
|
+
* @param message Message optionnel à ajouter avant le token de validation
|
|
384
|
+
* @param config Configuration Git (optionnel, utilise la config globale)
|
|
385
|
+
* @returns Hash du commit de fermeture ou référence de la note
|
|
386
|
+
*/
|
|
387
|
+
async function gitClosePRRobust(git, branch, closedBy, message, config) {
|
|
388
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
389
|
+
// Validation des paramètres
|
|
390
|
+
if (!branch) {
|
|
391
|
+
throw new errors_1.PRClosureError('Branch name is required', branch, 'invalid_parameters');
|
|
392
|
+
}
|
|
393
|
+
if (!closedBy) {
|
|
394
|
+
throw new errors_1.PRClosureError('Closed by is required', branch, 'invalid_parameters');
|
|
395
|
+
}
|
|
396
|
+
if (!branch.startsWith(gitConf.validationPrefix)) {
|
|
397
|
+
throw new errors_1.PRClosureError(`Branch must start with ${gitConf.validationPrefix}`, branch, 'invalid_branch');
|
|
398
|
+
}
|
|
399
|
+
//
|
|
400
|
+
// lock checkout is global
|
|
401
|
+
await (0, repo_tools_1.lock)('checkout');
|
|
402
|
+
let currentBranch;
|
|
403
|
+
try {
|
|
404
|
+
currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
405
|
+
// 1. Read the existing metadata from the PR branch (includes search in recent commits)
|
|
406
|
+
const originalMetadata = await gitGetPRMetadata(git, branch, config);
|
|
407
|
+
if (!originalMetadata) {
|
|
408
|
+
throw new errors_1.PRClosureError(`Could not find metadata on branch ${branch}. Cannot close.`, branch, 'metadata_not_found');
|
|
409
|
+
}
|
|
410
|
+
// 3. Update the metadata note on the PR branch itself to mark it as closed
|
|
411
|
+
const finalMetadata = {
|
|
412
|
+
...originalMetadata,
|
|
413
|
+
status: 'closed',
|
|
414
|
+
closedBy: closedBy,
|
|
415
|
+
closedAt: new Date().toISOString(),
|
|
416
|
+
comments: [...(originalMetadata.comments || []), message || ''],
|
|
417
|
+
};
|
|
418
|
+
// Get the commit hash of the HEAD of the branch
|
|
419
|
+
const headCommitHash = (await git.revparse([branch])).trim();
|
|
420
|
+
await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, finalMetadata, gitConf.gitNotes.namespace);
|
|
421
|
+
// 4. Now, merge the updated PR branch into the target branch
|
|
422
|
+
await git.checkout(originalMetadata.mergeBase || gitConf.draftBranch);
|
|
423
|
+
const mergeResult = await git
|
|
424
|
+
.env({
|
|
425
|
+
GIT_AUTHOR_NAME: closedBy.name,
|
|
426
|
+
GIT_AUTHOR_EMAIL: closedBy.email,
|
|
427
|
+
GIT_COMMITTER_NAME: closedBy.name,
|
|
428
|
+
GIT_COMMITTER_EMAIL: closedBy.email,
|
|
429
|
+
})
|
|
430
|
+
.merge([
|
|
431
|
+
'--no-ff', '-X', 'theirs', '-m', `Merge branch '${branch}'`, branch
|
|
432
|
+
]);
|
|
433
|
+
const result = {
|
|
434
|
+
metadata: finalMetadata,
|
|
435
|
+
failed: false,
|
|
436
|
+
conflicts: mergeResult.conflicts,
|
|
437
|
+
raw: mergeResult,
|
|
438
|
+
};
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
if (error instanceof errors_1.PRClosureError)
|
|
443
|
+
throw error;
|
|
444
|
+
throw new errors_1.PRClosureError(`Failed to close PR ${branch}: ${error}`, branch, 'unknown');
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
if (currentBranch) {
|
|
448
|
+
await git.checkout(currentBranch);
|
|
449
|
+
}
|
|
450
|
+
(0, repo_tools_1.unlock)('checkout');
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Trouve le prochain numéro de PR disponible (protégé contre la concurrence)
|
|
455
|
+
* @param git Instance Git
|
|
456
|
+
* @param validationPrefix Préfixe des branches de validation
|
|
457
|
+
* @returns Prochain numéro de PR
|
|
458
|
+
*/
|
|
459
|
+
async function gitGetNextPRNumber(git, validationPrefix) {
|
|
460
|
+
await (0, repo_tools_1.lock)('pr-number');
|
|
461
|
+
try {
|
|
462
|
+
const allBranches = await (0, repo_tools_1.gitGetAllBranches)(git);
|
|
463
|
+
const prBranches = allBranches.filter((branch) => branch.startsWith(validationPrefix));
|
|
464
|
+
if (prBranches.length === 0) {
|
|
465
|
+
return 1;
|
|
466
|
+
}
|
|
467
|
+
const numbers = prBranches
|
|
468
|
+
.map((branch) => {
|
|
469
|
+
const numberPart = branch.substring(validationPrefix.length);
|
|
470
|
+
// Gérer les cas où le nom de branche contient plus que le numéro après le préfixe
|
|
471
|
+
const actualNumber = parseInt(numberPart.split('-')[0], 10);
|
|
472
|
+
return actualNumber;
|
|
473
|
+
})
|
|
474
|
+
.filter((num) => !isNaN(num));
|
|
475
|
+
return numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
throw new errors_1.GitOperationError(`Failed to get next PR number: ${error}`, 'pr_number', { validationPrefix, originalError: error });
|
|
479
|
+
}
|
|
480
|
+
finally {
|
|
481
|
+
(0, repo_tools_1.unlock)('pr-number');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Crée une nouvelle branche de validation (pour un PR vers rule-editor)
|
|
486
|
+
* à partir d'une branche source (typiquement rule-editor).
|
|
487
|
+
* Fait un commit initial vide et écrit une note Git avec les métadonnées du travail à faire.
|
|
488
|
+
* @param git Instance SimpleGit
|
|
489
|
+
* @param files Liste des fichiers qui seront concernés par ce travail de validation
|
|
490
|
+
* @param description Description du travail ou du PR
|
|
491
|
+
* @param author Utilisateur qui initie ce travail
|
|
492
|
+
* @param options Options (content when file creation, editorBranch, validationBranchPrefix)
|
|
493
|
+
* @returns PRInfo contenant les informations de la branche créée et ses métadonnées initiales
|
|
494
|
+
*/
|
|
495
|
+
async function gitNewValidationRequest(git, files, description, author, options = {}) {
|
|
496
|
+
await (0, repo_tools_1.lock)('new-pr');
|
|
497
|
+
let currentBranch;
|
|
498
|
+
try {
|
|
499
|
+
currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
500
|
+
const gitConfig = (0, repo_tools_1.gitLoad)();
|
|
501
|
+
const { content = options.content || [], editorBranch: sourceBranch = options.editorBranch || gitConfig.draftBranch, validationBranchPrefix = options.validationBranchPrefix || gitConfig.validationPrefix } = options;
|
|
502
|
+
if ((!files || !files.length)) {
|
|
503
|
+
throw new errors_1.PRCreationError('At least one file must be specified for a new validation branch');
|
|
504
|
+
}
|
|
505
|
+
if (!author) {
|
|
506
|
+
throw new errors_1.PRCreationError('Author is required', undefined, files);
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
const nextID = await gitGetNextPRNumber(git, validationBranchPrefix);
|
|
510
|
+
const newBranchName = `${validationBranchPrefix}${nextID}`;
|
|
511
|
+
await git.checkoutBranch(newBranchName, sourceBranch);
|
|
512
|
+
const initialCommitMessage = `#${nextID} ${description}`;
|
|
513
|
+
// create or update files
|
|
514
|
+
for (let idx = 0; idx < files.length; idx++) {
|
|
515
|
+
if (!content[idx]) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
// console.log('writeFile',files[idx],content[idx]);
|
|
519
|
+
// This writeFile is in-memory and stages the file.
|
|
520
|
+
await _writeFileAndCommit(git, files[idx], content[idx], author, gitConfig, initialCommitMessage);
|
|
521
|
+
}
|
|
522
|
+
const metadata = {
|
|
523
|
+
id: nextID,
|
|
524
|
+
status: 'open',
|
|
525
|
+
timestamp: new Date().toISOString(),
|
|
526
|
+
validator: '',
|
|
527
|
+
readyForValidation: false,
|
|
528
|
+
createdBy: author,
|
|
529
|
+
mergeBase: options.mergeBase || sourceBranch,
|
|
530
|
+
comments: [description || `Write description here...`],
|
|
531
|
+
files: files,
|
|
532
|
+
};
|
|
533
|
+
// Get the commit hash of the HEAD of the new branch
|
|
534
|
+
const headCommitHash = (await git.revparse([newBranchName])).trim();
|
|
535
|
+
await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConfig.gitNotes.namespace);
|
|
536
|
+
const log = await git.log({ from: newBranchName, to: newBranchName, maxCount: 1 });
|
|
537
|
+
const lastCommit = log.latest;
|
|
538
|
+
return {
|
|
539
|
+
branch: newBranchName,
|
|
540
|
+
files: files,
|
|
541
|
+
open: true,
|
|
542
|
+
metadata: metadata,
|
|
543
|
+
lastCommit: lastCommit?.hash,
|
|
544
|
+
lastCommitMessage: lastCommit?.message,
|
|
545
|
+
lastModified: lastCommit?.date ? new Date(lastCommit.date) : undefined,
|
|
546
|
+
toString() { return this.branch; }
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
if (error instanceof errors_1.PRCreationError || error instanceof errors_1.GitOperationError) {
|
|
551
|
+
throw error;
|
|
552
|
+
}
|
|
553
|
+
// Utiliser la variable "files" définie dans la portée externe du try
|
|
554
|
+
throw new errors_1.PRCreationError(`Failed to create validation branch: ${error}`, undefined, files);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
finally {
|
|
558
|
+
if (currentBranch) {
|
|
559
|
+
await git.checkout(currentBranch);
|
|
560
|
+
}
|
|
561
|
+
(0, repo_tools_1.unlock)('new-pr');
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* DEPRECATED
|
|
566
|
+
*/
|
|
567
|
+
async function gitNewPR(git, files, description, author, options = {}) {
|
|
568
|
+
return gitNewValidationRequest(git, files, description, author, options);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* PRIVATE TOOLS
|
|
572
|
+
*/
|
|
573
|
+
const _writeFileAndCommit = async (git, filePath, content, user, config, commitMsg) => {
|
|
574
|
+
const gitConf = config;
|
|
575
|
+
if (!gitConf || !gitConf.repoPath) {
|
|
576
|
+
throw new errors_1.GitOperationError('_writeFileAndCommit requires a config with a valid repoPath.', 'internal_error');
|
|
577
|
+
}
|
|
578
|
+
// Créer les répertoires parents si nécessaire
|
|
579
|
+
const fullPath = (0, path_1.join)(gitConf.repoPath, filePath);
|
|
580
|
+
const dir = (0, path_1.dirname)(fullPath);
|
|
581
|
+
await fs.mkdir(dir, { recursive: true });
|
|
582
|
+
// Écrire le fichier
|
|
583
|
+
await fs.writeFile(fullPath, content, { encoding: 'utf8', flag: 'w' });
|
|
584
|
+
await git.add(filePath);
|
|
585
|
+
const commit = await git.commit(commitMsg || `commit`, [filePath], {
|
|
586
|
+
'--author': `${user.name} <${user.email}>`
|
|
587
|
+
});
|
|
588
|
+
return commit;
|
|
589
|
+
};
|