@futdevpro/fdp-agent-memory 0.1.0 → 1.1.12
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/LICENSE +21 -0
- package/README.md +7 -7
- package/build/package.json +6 -5
- package/build/src/_cli/_collections/fam-arg.util.js +48 -0
- package/build/src/_cli/_collections/fam-cli.const.js +40 -0
- package/build/src/_cli/_collections/fam-output.util.js +86 -0
- package/build/src/_cli/_collections/fam-project-discovery.util.js +98 -0
- package/build/src/_cli/_commands/capture.command.js +73 -0
- package/build/src/_cli/_commands/config.command.js +93 -0
- package/build/src/_cli/_commands/doctor.command.js +124 -0
- package/build/src/_cli/_commands/errors.command.js +66 -0
- package/build/src/_cli/_commands/export.command.js +65 -0
- package/build/src/_cli/_commands/find-duplicates.command.js +97 -0
- package/build/src/_cli/_commands/import.command.js +136 -0
- package/build/src/_cli/_commands/init.command.js +147 -0
- package/build/src/_cli/_commands/read.command.js +109 -0
- package/build/src/_cli/_commands/scan-projects.command.js +138 -0
- package/build/src/_cli/_commands/scan.command.js +98 -0
- package/build/src/_cli/_commands/seed.command.js +40 -0
- package/build/src/_cli/_commands/serve.command.js +350 -0
- package/build/src/_cli/_commands/start.command.js +134 -0
- package/build/src/_cli/_commands/stats.command.js +54 -0
- package/build/src/_cli/_commands/write.command.js +103 -0
- package/build/src/_cli/_models/interfaces/fam-cli-global-options.interface.js +2 -0
- package/build/src/_cli/_models/interfaces/fam-cli-output.interface.js +9 -0
- package/build/src/_cli/_models/interfaces/fam-client-result.interface.js +2 -0
- package/build/src/_cli/_services/fam-client.service.js +140 -0
- package/build/src/_cli/register-commands.js +86 -0
- package/build/src/_collections/config-catalog.const.js +67 -1
- package/build/src/_collections/fam-console.util.js +367 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +158 -4
- package/build/src/_collections/fam-error-factory.util.js +0 -9
- package/build/src/_collections/fam-mcp-bridge.util.js +49 -0
- package/build/src/_collections/fam-reference-code.util.js +105 -0
- package/build/src/_collections/fam-version.const.js +10 -0
- package/build/src/_models/data-models/fam-entry-base-properties.const.js +1 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
- package/build/src/_models/data-models/fam-ingest-run.data-model.js +3 -1
- package/build/src/_models/data-models/fam-reference.data-model.js +7 -0
- package/build/src/_modules/capture/_collections/fam-capture.const.js +11 -0
- package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +87 -0
- package/build/src/_modules/capture/index.js +8 -0
- package/build/src/_modules/embedding/_collections/fam-embedding-prefix.util.js +77 -0
- package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +202 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +33 -9
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +21 -2
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +135 -0
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +42 -32
- package/build/src/_modules/embedding/index.js +4 -1
- package/build/src/_modules/export/_collections/fam-export.const.js +22 -0
- package/build/src/_modules/export/_services/fam-export.control-service.js +64 -0
- package/build/src/_modules/export/index.js +8 -0
- package/build/src/_modules/ingest/_collections/fam-famignore.util.js +83 -0
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +59 -48
- package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +134 -0
- package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +57 -0
- package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +60 -0
- package/build/src/_modules/ingest/_collections/fam-scan-weight.util.js +53 -0
- package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +37 -14
- package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +34 -0
- package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +200 -14
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +4 -1
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +7 -4
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +346 -17
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +25 -2
- package/build/src/_modules/ingest/index.js +3 -1
- package/build/src/_modules/mcp/_collections/fam-active-rules.util.js +56 -0
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +47 -6
- package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +224 -18
- package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +53 -1
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +104 -8
- package/build/src/_modules/mcp/index.js +4 -4
- package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +66 -3
- package/build/src/_modules/migration/_collections/fam-prompt-aggregate.util.js +143 -0
- package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +19 -0
- package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +6 -0
- package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +5 -0
- package/build/src/_modules/migration/_services/fam-agent-memory-reader.service.js +125 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +101 -18
- package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +53 -0
- package/build/src/_modules/migration/index.js +3 -1
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +78 -4
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +293 -50
- package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +6 -3
- package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +18 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +79 -20
- package/build/src/_routes/server/api/api.controller.js +34 -2
- package/build/src/_routes/server/client-app/client-app.control-service.js +1 -1
- package/build/src/_routes/server/server-status/server-status.controller.js +2 -1
- package/build/src/app.server.js +13 -1
- package/build/src/environments/environment.js +1 -1
- package/build/src/index.js +1 -1
- package/client-dist/{chunk-GHKRM4SM.js → chunk-I77GXVAQ.js} +1 -1
- package/client-dist/{chunk-LMTL7GA3.js → chunk-YXHWCJ5O.js} +1 -1
- package/client-dist/index.html +1 -1
- package/client-dist/{main-2KWB3QYK.js → main-PJPEDVJT.js} +1 -1
- package/package.json +6 -5
|
@@ -4,13 +4,23 @@ exports.FAM_Ingest_ControlService = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const crypto = tslib_1.__importStar(require("crypto"));
|
|
6
6
|
const fs = tslib_1.__importStar(require("fs"));
|
|
7
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
8
|
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
9
|
+
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
8
10
|
const error_codes_const_1 = require("../../../_collections/error-codes.const");
|
|
9
11
|
const errors_control_service_1 = require("../../../_routes/server/errors/errors.control-service");
|
|
10
12
|
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
11
13
|
const fam_entry_data_model_1 = require("../../../_models/data-models/fam-entry.data-model");
|
|
14
|
+
const fam_reference_code_util_1 = require("../../../_collections/fam-reference-code.util");
|
|
12
15
|
const embedding_1 = require("../../embedding");
|
|
13
16
|
const scope_reference_1 = require("../../scope-reference");
|
|
17
|
+
const fam_content_hash_util_1 = require("../_collections/fam-content-hash.util");
|
|
18
|
+
const fam_project_identity_util_1 = require("../_collections/fam-project-identity.util");
|
|
19
|
+
const fam_scan_path_util_1 = require("../_collections/fam-scan-path.util");
|
|
20
|
+
const fam_scan_progress_util_1 = require("../_collections/fam-scan-progress.util");
|
|
21
|
+
const fam_scan_summary_util_1 = require("../_collections/fam-scan-summary.util");
|
|
22
|
+
const scope_reference_2 = require("../../scope-reference");
|
|
23
|
+
const fam_reference_data_model_1 = require("../../../_models/data-models/fam-reference.data-model");
|
|
14
24
|
const fam_chunker_control_service_1 = require("./fam-chunker.control-service");
|
|
15
25
|
const fam_delta_compare_util_1 = require("./fam-delta-compare.util");
|
|
16
26
|
const fam_ingest_run_data_service_1 = require("./fam-ingest-run.data-service");
|
|
@@ -36,6 +46,15 @@ const fam_scan_control_service_1 = require("./fam-scan.control-service");
|
|
|
36
46
|
*/
|
|
37
47
|
class FAM_Ingest_ControlService {
|
|
38
48
|
static _instance;
|
|
49
|
+
/**
|
|
50
|
+
* Bináris-tartalom heurisztika (FAM-REV-064): egy NUL-karakter (U+0000) az első ~8KB-ban → bináris
|
|
51
|
+
* (a UTF-8 szöveg sosem tartalmaz NUL-t). A denylist-en átcsúszott, ismeretlen-kiterjesztésű bináris
|
|
52
|
+
* fájl szűrője — így nem kerül szemét-chunk a generic chunkerbe. Pure + statikus (tesztelhető).
|
|
53
|
+
*/
|
|
54
|
+
static isLikelyBinary(content) {
|
|
55
|
+
const sample = content.length > 8192 ? content.slice(0, 8192) : content;
|
|
56
|
+
return sample.indexOf(String.fromCharCode(0)) !== -1;
|
|
57
|
+
}
|
|
39
58
|
/** Default issuer az ingest-műveletekhez. */
|
|
40
59
|
issuer = 'FAM_Ingest_ControlService';
|
|
41
60
|
static getInstance() {
|
|
@@ -90,6 +109,8 @@ class FAM_Ingest_ControlService {
|
|
|
90
109
|
for (const chunk of chunks) {
|
|
91
110
|
if (chunk._id) {
|
|
92
111
|
await dataService.deleteData(chunk._id);
|
|
112
|
+
// Pool-coherence (FAM-REV-046): a vektort az in-memory LVS-pool-ból is kivesszük.
|
|
113
|
+
embedding_1.FAM_VectorSearch_ControlService.getInstance().removeVector(registryEntry.table, chunk._id);
|
|
93
114
|
deleted++;
|
|
94
115
|
}
|
|
95
116
|
}
|
|
@@ -106,7 +127,9 @@ class FAM_Ingest_ControlService {
|
|
|
106
127
|
*/
|
|
107
128
|
async runScan(request) {
|
|
108
129
|
const issuer = request.issuer ?? this.issuer;
|
|
109
|
-
|
|
130
|
+
// A pre-open (scope-resolve / discovery) hiba-summary-khoz kell egy id MIELŐTT a run-rekord
|
|
131
|
+
// megnyílna; a sikeres `open` UTÁN ezt a Dynamo-generált `_id`-re cseréljük (lásd lentebb).
|
|
132
|
+
let runId = crypto.randomUUID();
|
|
110
133
|
const startedAt = Date.now();
|
|
111
134
|
// (1) Scope reference-resolve (MP-3 `resolveForWrite`) — KÖTELEZŐ. Hiba → run `failed`.
|
|
112
135
|
let canonicalScope;
|
|
@@ -132,6 +155,7 @@ class FAM_Ingest_ControlService {
|
|
|
132
155
|
include: request.include,
|
|
133
156
|
exclude: request.exclude,
|
|
134
157
|
tableOverride: request.tableOverride,
|
|
158
|
+
weights: request.weights,
|
|
135
159
|
});
|
|
136
160
|
}
|
|
137
161
|
catch (error) {
|
|
@@ -140,25 +164,34 @@ class FAM_Ingest_ControlService {
|
|
|
140
164
|
error: error, scopePath: canonicalScope,
|
|
141
165
|
});
|
|
142
166
|
}
|
|
143
|
-
// (3) A run megnyitása (a chunkok ezzel az `ingestRunId`-vel hivatkoznak rá).
|
|
167
|
+
// (3) A run megnyitása (a chunkok ezzel az `ingestRunId`-vel hivatkoznak rá). A `_id`-t a Dynamo
|
|
168
|
+
// generálja (Mongo ObjectId — a `FAM_Scope` mintája; a client-generált UUID nem cast-olható
|
|
169
|
+
// `findById`-dal). Innentől a Dynamo-`_id` a runId.
|
|
144
170
|
const runDataService = new fam_ingest_run_data_service_1.FAM_IngestRun_DataService({ issuer: issuer });
|
|
145
|
-
await runDataService.open({
|
|
146
|
-
runId: runId,
|
|
171
|
+
const openedRun = await runDataService.open({
|
|
147
172
|
trigger: request.operation,
|
|
148
173
|
table: discovery.tables,
|
|
149
174
|
scopePath: canonicalScope,
|
|
150
175
|
rootPath: request.path,
|
|
151
176
|
});
|
|
177
|
+
runId = openedRun._id ?? runId;
|
|
152
178
|
// (4) Per-fájl feldolgozás (flow-limit kötegekben; a per-fájl hiba NEM buktatja a run-t).
|
|
153
179
|
const verdicts = { new: 0, modified: 0, equal: 0, deleted: 0 };
|
|
154
180
|
const errors = [];
|
|
155
181
|
let chunkCount = 0;
|
|
156
182
|
const flowLimit = await this.resolveFlowLimit(canonicalScope);
|
|
157
|
-
|
|
183
|
+
const isDryRun = request.dryRun ?? false;
|
|
184
|
+
const total = discovery.routedFiles.length;
|
|
185
|
+
// Futás-közbeni progress (descriptive user feedback): a NAGY (workspace-szintű) scan haladását
|
|
186
|
+
// throttle-olt `[famIngest]` sorokkal jelezzük a konzolon (a `dryRun` NEM embeddel → ott nincs progress).
|
|
187
|
+
const progressIntervalMs = await this.resolveScanNumber('scan.progressIntervalMs', canonicalScope, 2000);
|
|
188
|
+
let lastProgressAt = startedAt;
|
|
189
|
+
for (let i = 0; i < total; i += flowLimit) {
|
|
158
190
|
const batch = discovery.routedFiles.slice(i, i + flowLimit);
|
|
159
191
|
const results = await Promise.all(batch.map((file) => this.processFile({
|
|
160
192
|
file: file, runId: runId, scopePath: canonicalScope,
|
|
161
|
-
|
|
193
|
+
resolvedRoot: discovery.resolvedRoot,
|
|
194
|
+
dryRun: isDryRun, issuer: issuer, errors: errors,
|
|
162
195
|
})));
|
|
163
196
|
for (const result of results) {
|
|
164
197
|
if (!result) {
|
|
@@ -167,6 +200,42 @@ class FAM_Ingest_ControlService {
|
|
|
167
200
|
chunkCount += result.chunkCount;
|
|
168
201
|
this.tallyVerdicts(verdicts, result.deltaItems);
|
|
169
202
|
}
|
|
203
|
+
// Throttle-olt progress-jelzés (a min. időköz a config; a végső 100% MINDIG kibocsátódik). dryRun-on
|
|
204
|
+
// kihagyva (nincs valódi embed-munka, a becslést a summary.estimate adja).
|
|
205
|
+
const processed = Math.min(i + flowLimit, total);
|
|
206
|
+
const now = Date.now();
|
|
207
|
+
if (!isDryRun && (processed >= total || now - lastProgressAt >= progressIntervalMs)) {
|
|
208
|
+
lastProgressAt = now;
|
|
209
|
+
fsm_dynamo_1.DyFM_Log.info(fam_scan_progress_util_1.FAM_ScanProgress_Util.progressLine({
|
|
210
|
+
processed: processed, total: total, chunkCount: chunkCount,
|
|
211
|
+
verdicts: verdicts, elapsedSeconds: (now - startedAt) / 1000,
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// (4b) Törölt-fájl reconciliation (whole-file orphan) — CSAK folder/project scan (a `scan-file`
|
|
216
|
+
// egyetlen fájlt céloz, nem reconciliál). A lemezről eltűnt fájlok chunkjai soft-delete-elődnek; a
|
|
217
|
+
// számuk a `verdicts.deleted`-be (a per-fájl, fájlon-belüli `deleted`-ek mellé).
|
|
218
|
+
if (request.operation !== 'scan-file') {
|
|
219
|
+
verdicts.deleted += await this.reconcileDeletedFiles({
|
|
220
|
+
discovery: discovery, scopePath: canonicalScope, issuer: issuer, dryRun: request.dryRun ?? false,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// (4c) File-rendszer összefoglaló (knowledge) — generált „mi van a projektben" entry a mappastruktúrából
|
|
224
|
+
// (folder/project scan, non-dryRun, van route-olt fájl). Best-effort: a hiba NEM buktatja a scant.
|
|
225
|
+
if (!isDryRun && request.operation !== 'scan-file' && discovery.routedFiles.length) {
|
|
226
|
+
try {
|
|
227
|
+
await this.persistScanSummary({ discovery: discovery, scopePath: canonicalScope, runId: runId, issuer: issuer });
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
fsm_dynamo_1.DyFM_Log.warn(`[famIngest] fs-summary generálás sikertelen (a scan kész): ${error?.message}`);
|
|
231
|
+
}
|
|
232
|
+
// (4d) Projekt-identitás (git + package.json) → kereshető knowledge entry + reference-alias. Best-effort.
|
|
233
|
+
try {
|
|
234
|
+
await this.persistProjectIdentity({ discovery: discovery, scopePath: canonicalScope, runId: runId, issuer: issuer });
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
fsm_dynamo_1.DyFM_Log.warn(`[famIngest] project-identity generálás sikertelen (a scan kész): ${error?.message}`);
|
|
238
|
+
}
|
|
170
239
|
}
|
|
171
240
|
// (5) A run lezárása (atomikus `$set`) + MON-esemény.
|
|
172
241
|
const status = errors.length ? 'partial-failed' : 'ok';
|
|
@@ -182,6 +251,15 @@ class FAM_Ingest_ControlService {
|
|
|
182
251
|
filesProcessed: discovery.routedFiles.length, filesSkipped: discovery.skippedFiles.length,
|
|
183
252
|
chunkCount: chunkCount, verdicts: verdicts, durationMs: durationMs, status: status, errors: errors,
|
|
184
253
|
};
|
|
254
|
+
// Pre-scan idő-becslés CSAK dryRun-on (descriptive user feedback): mennyi lesz a TÉNYLEGES scan embed-ideje.
|
|
255
|
+
if (isDryRun) {
|
|
256
|
+
const msPerChunk = await this.resolveScanNumber('scan.estimateMsPerChunk', canonicalScope, 250);
|
|
257
|
+
summary.estimate = fam_scan_progress_util_1.FAM_ScanProgress_Util.estimate({
|
|
258
|
+
chunksToEmbed: verdicts.new + verdicts.modified, msPerChunk: msPerChunk,
|
|
259
|
+
});
|
|
260
|
+
fsm_dynamo_1.DyFM_Log.info(`[famIngest] dryRun kész: ${total} fájl, ${chunkCount} chunk, `
|
|
261
|
+
+ `${verdicts.new + verdicts.modified} embeddelendő → becsült idő ${summary.estimate.note}`);
|
|
262
|
+
}
|
|
185
263
|
this.emitMonEvent(summary);
|
|
186
264
|
return summary;
|
|
187
265
|
}
|
|
@@ -194,6 +272,11 @@ class FAM_Ingest_ControlService {
|
|
|
194
272
|
const table = set.file.route;
|
|
195
273
|
try {
|
|
196
274
|
const content = fs.readFileSync(set.file.absolutePath, 'utf-8');
|
|
275
|
+
// FAM-REV-064 bináris-tartalom guard: a denylist-en átcsúszott, ismeretlen-kiterjesztésű bináris
|
|
276
|
+
// (null-byte az első ~8KB-ban) NE kerüljön a generic chunkerbe (szemét-chunk ellen). Skip → 0 chunk.
|
|
277
|
+
if (FAM_Ingest_ControlService.isLikelyBinary(content)) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
197
280
|
const chunks = await fam_chunker_control_service_1.FAM_Chunker_ControlService.getInstance().chunkFile({
|
|
198
281
|
content: content, relativePath: set.file.relativePath, table: table, scopePath: set.scopePath,
|
|
199
282
|
});
|
|
@@ -202,13 +285,14 @@ class FAM_Ingest_ControlService {
|
|
|
202
285
|
throw new Error(`No store-registry entry for table '${table}'.`);
|
|
203
286
|
}
|
|
204
287
|
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: set.issuer });
|
|
205
|
-
// Existing-by-key betöltés
|
|
206
|
-
|
|
288
|
+
// Existing-by-key betöltés a KANONIKUS abszolút útvonalon (FAM-REV-050) — a scan-root-független
|
|
289
|
+
// fájl-identitás, így a delta bármely scan-gyökérről ugyanazt a fájlt matcheli (nincs duplikáció).
|
|
290
|
+
const existing = await this.loadExistingChunks(dataService, set.file.absolutePath, set.scopePath);
|
|
207
291
|
const deltaItems = fam_delta_compare_util_1.FAM_DeltaCompare_Util.compare(chunks, existing);
|
|
208
292
|
if (!set.dryRun) {
|
|
209
293
|
await this.applyVerdicts({
|
|
210
294
|
deltaItems: deltaItems, file: set.file, table: table, runId: set.runId,
|
|
211
|
-
scopePath: set.scopePath, dataService: dataService,
|
|
295
|
+
scopePath: set.scopePath, root: set.resolvedRoot, dataService: dataService,
|
|
212
296
|
});
|
|
213
297
|
}
|
|
214
298
|
return {
|
|
@@ -247,11 +331,47 @@ class FAM_Ingest_ControlService {
|
|
|
247
331
|
async applyVerdicts(set) {
|
|
248
332
|
for (const item of set.deltaItems) {
|
|
249
333
|
if (item.verdict === 'equal') {
|
|
334
|
+
// Metaadat-backfill (FAM-REV-048 + FEAT-003): az equal-content entry `source.root`-ja ÉS
|
|
335
|
+
// `referenceCodes`-a frissül, ha eltér — a re-scan így a TELJES korpuszt root-tudatossá teszi
|
|
336
|
+
// (reconciliation-lefedettség) ÉS a kód-index-frissítést (pl. Tailwind-denylist-bővítés) a régi
|
|
337
|
+
// chunkokra is alkalmazza, RE-EMBED NÉLKÜL. Mindkettő FELTÉTELES (no-op, ha már stimmel),
|
|
338
|
+
// best-effort: a hiba NEM buktatja a scan-t (a content már naprakész).
|
|
339
|
+
if (item.existingId) {
|
|
340
|
+
try {
|
|
341
|
+
if (set.root) {
|
|
342
|
+
await set.dataService.backfillSourceRoot(item.existingId, set.root);
|
|
343
|
+
}
|
|
344
|
+
// A default display-relatív út a friss scan-gyökérhez igazodjon (FAM-REV-050) —
|
|
345
|
+
// egy korábban más root-reprezentációval (pl. scan-file basename) ingestelt chunk konzisztenssé.
|
|
346
|
+
await set.dataService.backfillSourceFilePath(item.existingId, set.file.relativePath);
|
|
347
|
+
if (item.chunk) {
|
|
348
|
+
await set.dataService.backfillReferenceCodes(item.existingId, fam_reference_code_util_1.FAM_ReferenceCode_Util.extract(item.chunk.content));
|
|
349
|
+
// Pozíció-backfill (FAM-REV-061): a chunker pozíció-fix a régi equal-chunkokra is,
|
|
350
|
+
// re-embed nélkül (a friss chunk charStart/charEnd/sor-pozíciója felülír, ha eltér).
|
|
351
|
+
await set.dataService.backfillPosition(item.existingId, item.chunk.position);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
// best-effort metaadat-frissítés — a content már naprakész (equal), nem eszkaláljuk.
|
|
356
|
+
}
|
|
357
|
+
// FAM-REV-066: ha a meglévő equal-chunk NEM completed (transient embed-`error`/`pending`,
|
|
358
|
+
// pl. LM Studio HTTP 500 alatt), ÚJRA-embeddeljük (a content egyezik, csak a vektor hiányzik)
|
|
359
|
+
// → a tranziens embed-hibák a következő re-scankor AUTO-GYÓGYULNAK (nincs beragadt error).
|
|
360
|
+
if (item.chunk && item.existingEmbeddingStatus && item.existingEmbeddingStatus !== 'completed') {
|
|
361
|
+
await this.persistAndEmbed({
|
|
362
|
+
chunk: item.chunk, existingId: item.existingId, contentHash: item.contentHash,
|
|
363
|
+
file: set.file, table: set.table, runId: set.runId, scopePath: set.scopePath,
|
|
364
|
+
root: set.root, dataService: set.dataService,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
250
368
|
continue;
|
|
251
369
|
}
|
|
252
370
|
if (item.verdict === 'deleted') {
|
|
253
371
|
if (item.existingId) {
|
|
254
372
|
await set.dataService.deleteData(item.existingId);
|
|
373
|
+
// Pool-coherence (FAM-REV-046): a törölt chunk vektora is ki a pool-ból.
|
|
374
|
+
embedding_1.FAM_VectorSearch_ControlService.getInstance().removeVector(set.table, item.existingId);
|
|
255
375
|
}
|
|
256
376
|
continue;
|
|
257
377
|
}
|
|
@@ -260,7 +380,7 @@ class FAM_Ingest_ControlService {
|
|
|
260
380
|
await this.persistAndEmbed({
|
|
261
381
|
chunk: item.chunk, existingId: item.existingId, contentHash: item.contentHash,
|
|
262
382
|
file: set.file, table: set.table, runId: set.runId, scopePath: set.scopePath,
|
|
263
|
-
dataService: set.dataService,
|
|
383
|
+
root: set.root, dataService: set.dataService,
|
|
264
384
|
});
|
|
265
385
|
}
|
|
266
386
|
}
|
|
@@ -278,13 +398,17 @@ class FAM_Ingest_ControlService {
|
|
|
278
398
|
contentHash: set.contentHash,
|
|
279
399
|
embeddingStatus: 'pending',
|
|
280
400
|
scopePath: set.scopePath,
|
|
401
|
+
// Súly-kiemelés (FAM-REV-055): a scan-`weights`-ből feloldott fájl-súly (undefined → a tár model-default).
|
|
402
|
+
weight: set.file.weight,
|
|
281
403
|
sourceFilePath: set.file.relativePath,
|
|
282
404
|
chunkIndex: set.chunk.chunkIndex,
|
|
283
405
|
chunkTotal: set.chunk.chunkTotal,
|
|
284
406
|
chunkType: set.chunk.chunkType,
|
|
285
407
|
position: set.chunk.position,
|
|
286
408
|
headingPath: set.chunk.headingPath,
|
|
287
|
-
source: {
|
|
409
|
+
source: {
|
|
410
|
+
type: 'scan', path: set.file.relativePath, root: set.root, absolutePath: set.file.absolutePath,
|
|
411
|
+
},
|
|
288
412
|
addedBy: 'scan',
|
|
289
413
|
ingestRunId: set.runId,
|
|
290
414
|
});
|
|
@@ -296,22 +420,218 @@ class FAM_Ingest_ControlService {
|
|
|
296
420
|
table: set.table, entry: saved, callType: 'embed-write',
|
|
297
421
|
});
|
|
298
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* A file-rendszer-összefoglaló (`knowledge`, kind `fs-summary`) generálása + idempotens upsert-je a scan után.
|
|
425
|
+
* A `FAM_ScanSummary_Util` a mappastruktúrából építi a kereshető „mi van a projektben" szöveget. **Identitás:**
|
|
426
|
+
* `source.type='scan-summary'` + `source.absolutePath=<scan-gyökér>` + a levél-scope → re-scankor a MEGLÉVŐ-t
|
|
427
|
+
* frissíti (nincs duplikáció). **Idempotencia:** ha a `contentHash` változatlan (ugyanaz a fájl-lista), NO-OP
|
|
428
|
+
* (nincs fölös re-embed). A `scan-summary` source-type-ot a reconciliation (ami `scan`-t szűr) sosem érinti.
|
|
429
|
+
*/
|
|
430
|
+
async persistScanSummary(set) {
|
|
431
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(fam_table_type_enum_1.FAM_Table.knowledge);
|
|
432
|
+
if (!registryEntry) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: set.issuer });
|
|
436
|
+
const scopeLabel = set.scopePath.map((ref) => ref.canonicalName).join(' > ') || '(globális)';
|
|
437
|
+
const content = fam_scan_summary_util_1.FAM_ScanSummary_Util.build({
|
|
438
|
+
scopeLabel: scopeLabel,
|
|
439
|
+
root: set.discovery.resolvedRoot,
|
|
440
|
+
relativePaths: set.discovery.routedFiles.map((file) => file.relativePath),
|
|
441
|
+
skippedCount: set.discovery.skippedFiles.length,
|
|
442
|
+
tables: set.discovery.tables.map((table) => String(table)),
|
|
443
|
+
});
|
|
444
|
+
const contentHash = fam_content_hash_util_1.FAM_ContentHash_Util.hash(content);
|
|
445
|
+
// Meglévő összefoglaló a (source-type + scan-gyökér + levél-scope) identitáson.
|
|
446
|
+
const leafScopeId = set.scopePath.length ? set.scopePath[set.scopePath.length - 1].scopeId : undefined;
|
|
447
|
+
const filter = {};
|
|
448
|
+
filter['source.type'] = fam_scan_summary_util_1.FAM_ScanSummary_Util.SOURCE_TYPE;
|
|
449
|
+
filter['source.absolutePath'] = set.discovery.resolvedRoot;
|
|
450
|
+
filter.kind = fam_scan_summary_util_1.FAM_ScanSummary_Util.KIND; // ne ütközzön a project-identity-vel (azonos source-type + root + scope)
|
|
451
|
+
if (leafScopeId) {
|
|
452
|
+
filter['scopePath.scopeId'] = leafScopeId;
|
|
453
|
+
}
|
|
454
|
+
const existing = await dataService.findHydratableList(filter);
|
|
455
|
+
const existingEntry = existing[0];
|
|
456
|
+
// Változatlan fájl-lista → NO-OP (nincs fölös re-embed).
|
|
457
|
+
if (existingEntry && existingEntry.contentHash === contentHash) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
461
|
+
_id: existingEntry?._id,
|
|
462
|
+
table: fam_table_type_enum_1.FAM_Table.knowledge,
|
|
463
|
+
content: content,
|
|
464
|
+
contentHash: contentHash,
|
|
465
|
+
embeddingStatus: 'pending',
|
|
466
|
+
scopePath: set.scopePath,
|
|
467
|
+
kind: fam_scan_summary_util_1.FAM_ScanSummary_Util.KIND,
|
|
468
|
+
tags: ['fs-summary', 'filesystem', 'scan-summary'],
|
|
469
|
+
weight: 2,
|
|
470
|
+
source: {
|
|
471
|
+
type: 'scan-summary', root: set.discovery.resolvedRoot, absolutePath: set.discovery.resolvedRoot,
|
|
472
|
+
},
|
|
473
|
+
addedBy: 'scan',
|
|
474
|
+
ingestRunId: set.runId,
|
|
475
|
+
});
|
|
476
|
+
const saved = await dataService.saveData(entry);
|
|
477
|
+
await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().embedAndPersist({
|
|
478
|
+
table: fam_table_type_enum_1.FAM_Table.knowledge, entry: saved, callType: 'embed-write',
|
|
479
|
+
});
|
|
480
|
+
fsm_dynamo_1.DyFM_Log.info(`[famIngest] fs-summary ${existingEntry ? 'frissítve' : 'létrehozva'} `
|
|
481
|
+
+ `(${scopeLabel}): ${set.discovery.routedFiles.length} fájl, ${set.discovery.tables.length} tár`);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* A scan-gyökér PROJEKT-IDENTITÁSA (git + `package.json`) → (a) kereshető `knowledge` „project-identity"
|
|
485
|
+
* entry (név/verzió/leírás/függőségek/git-repo), (b) reference-alias (a package-név + rövid alakok → a
|
|
486
|
+
* projekt scope-ja). Idempotens (a knowledge a contentHash-en; a reference a scope-on). Best-effort.
|
|
487
|
+
*/
|
|
488
|
+
async persistProjectIdentity(set) {
|
|
489
|
+
const identity = fam_project_identity_util_1.FAM_ProjectIdentity_Util.detect(set.discovery.resolvedRoot);
|
|
490
|
+
if (!fam_project_identity_util_1.FAM_ProjectIdentity_Util.hasIdentity(identity)) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const scopeLabel = set.scopePath.map((ref) => ref.canonicalName).join(' > ') || '(globális)';
|
|
494
|
+
const leaf = set.scopePath.length ? set.scopePath[set.scopePath.length - 1] : undefined;
|
|
495
|
+
// (a) knowledge „project-identity" entry — idempotens (kind + source-type + root + scope; contentHash-skip).
|
|
496
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(fam_table_type_enum_1.FAM_Table.knowledge);
|
|
497
|
+
if (registryEntry) {
|
|
498
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: set.issuer });
|
|
499
|
+
const content = fam_project_identity_util_1.FAM_ProjectIdentity_Util.buildContent(identity, scopeLabel, set.discovery.resolvedRoot);
|
|
500
|
+
const contentHash = fam_content_hash_util_1.FAM_ContentHash_Util.hash(content);
|
|
501
|
+
const filter = {};
|
|
502
|
+
filter['source.type'] = fam_scan_summary_util_1.FAM_ScanSummary_Util.SOURCE_TYPE;
|
|
503
|
+
filter['source.absolutePath'] = set.discovery.resolvedRoot;
|
|
504
|
+
filter.kind = fam_project_identity_util_1.FAM_ProjectIdentity_Util.KIND;
|
|
505
|
+
if (leaf) {
|
|
506
|
+
filter['scopePath.scopeId'] = leaf.scopeId;
|
|
507
|
+
}
|
|
508
|
+
const existing = await dataService.findHydratableList(filter);
|
|
509
|
+
const existingEntry = existing[0];
|
|
510
|
+
if (!existingEntry || existingEntry.contentHash !== contentHash) {
|
|
511
|
+
const entry = new fam_entry_data_model_1.FAM_Entry({
|
|
512
|
+
_id: existingEntry?._id,
|
|
513
|
+
table: fam_table_type_enum_1.FAM_Table.knowledge,
|
|
514
|
+
content: content,
|
|
515
|
+
contentHash: contentHash,
|
|
516
|
+
embeddingStatus: 'pending',
|
|
517
|
+
scopePath: set.scopePath,
|
|
518
|
+
kind: fam_project_identity_util_1.FAM_ProjectIdentity_Util.KIND,
|
|
519
|
+
tags: ['project-identity', 'package', ...(identity.isGit ? ['git'] : [])],
|
|
520
|
+
weight: 3,
|
|
521
|
+
source: {
|
|
522
|
+
type: 'scan-summary', root: set.discovery.resolvedRoot, absolutePath: set.discovery.resolvedRoot,
|
|
523
|
+
},
|
|
524
|
+
addedBy: 'scan',
|
|
525
|
+
ingestRunId: set.runId,
|
|
526
|
+
});
|
|
527
|
+
const saved = await dataService.saveData(entry);
|
|
528
|
+
await embedding_1.FAM_EmbeddingPipeline_ControlService.getInstance().embedAndPersist({
|
|
529
|
+
table: fam_table_type_enum_1.FAM_Table.knowledge, entry: saved, callType: 'embed-write',
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// (b) reference-alias (a package-név + rövid alakok → a projekt scope-ja) — string/exact-match feloldás.
|
|
534
|
+
if (leaf) {
|
|
535
|
+
await this.persistProjectReference(identity, leaf, set.discovery.resolvedRoot, set.issuer);
|
|
536
|
+
}
|
|
537
|
+
fsm_dynamo_1.DyFM_Log.info(`[famIngest] project-identity (${scopeLabel}): `
|
|
538
|
+
+ `${identity.packageName ?? '(nincs package.json)'}${identity.isGit ? ' [git]' : ''}`
|
|
539
|
+
+ `${identity.gitRepo ? ` ${identity.gitRepo}` : ''}, ${identity.dependencies.length} függőség`);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* A projekt reference-aliasának idempotens upsert-je (a package-név + rövid alakok + repo → a projekt
|
|
543
|
+
* scope-ja, `category:'project-identity'`). A meglévőt a `canonicalScopeRef.scopeId`-n találja (re-scankor
|
|
544
|
+
* frissít, nem duplikál). A package-nevek exact/string-match-en oldódnak (nincs szükség embed-re).
|
|
545
|
+
*/
|
|
546
|
+
async persistProjectReference(identity, leaf, dirRoot, issuer) {
|
|
547
|
+
const referenceDataService = new scope_reference_2.FAM_Reference_DataService({ issuer: issuer });
|
|
548
|
+
const filter = { category: 'project-identity' };
|
|
549
|
+
filter['canonicalScopeRef.scopeId'] = leaf.scopeId;
|
|
550
|
+
const existing = await referenceDataService.findDataList(filter);
|
|
551
|
+
const model = new fam_reference_data_model_1.FAM_Reference_DataModel({
|
|
552
|
+
_id: existing[0]?._id,
|
|
553
|
+
canonicalTerm: identity.packageName ?? leaf.canonicalName,
|
|
554
|
+
aliasTerms: fam_project_identity_util_1.FAM_ProjectIdentity_Util.buildAliases(identity, path.basename(dirRoot), leaf.canonicalName),
|
|
555
|
+
description: identity.description
|
|
556
|
+
?? `A(z) '${leaf.canonicalName}' projekt (${identity.packageName ?? 'nincs package.json'}).`,
|
|
557
|
+
category: 'project-identity',
|
|
558
|
+
scopeLayer: leaf.layer,
|
|
559
|
+
canonicalScopeRef: leaf,
|
|
560
|
+
});
|
|
561
|
+
await referenceDataService.saveData(model);
|
|
562
|
+
}
|
|
299
563
|
// =========================================================================
|
|
300
564
|
// helpers
|
|
301
565
|
// =========================================================================
|
|
302
566
|
/**
|
|
303
567
|
* A fájl meglévő (nem soft-deleted) chunkjai a táron + scope-on belül (`Map`-elésre a delta-
|
|
304
568
|
* kulcshoz, SP-4.3). A `findHydratableList` az aktívakat adja (a soft-delete-elteket kihagyja).
|
|
305
|
-
* A
|
|
569
|
+
* A fájl-identitás a KANONIKUS `source.absolutePath` (FAM-REV-050) — scan-root-független, így a
|
|
570
|
+
* delta bármely scan-gyökérről ugyanazt a fájlt matcheli. A scope-szűkítés a LEVÉL scopeId-jára.
|
|
306
571
|
*/
|
|
307
|
-
async loadExistingChunks(dataService,
|
|
308
|
-
const filter = {
|
|
572
|
+
async loadExistingChunks(dataService, absolutePath, scopePath) {
|
|
573
|
+
const filter = {};
|
|
574
|
+
filter['source.absolutePath'] = absolutePath;
|
|
309
575
|
if (scopePath.length) {
|
|
310
576
|
const leaf = scopePath[scopePath.length - 1];
|
|
311
577
|
filter['scopePath.scopeId'] = leaf.scopeId;
|
|
312
578
|
}
|
|
313
579
|
return dataService.findHydratableList(filter);
|
|
314
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* Törölt-fájl reconciliation (whole-file orphan, dsgn-004 §4.3 kiterjesztés). A scan-folder/scan-project
|
|
583
|
+
* UTÁN a táron maradt, de a lemezről ELTŰNT fájlok chunkjait soft-delete-eli (+ pool-removeVector). A
|
|
584
|
+
* fájlon-BELÜLi chunk-törlést a `FAM_DeltaCompare_Util` `deleted` verdiktje kezeli; EZ a teljes fájl
|
|
585
|
+
* eltűnését (amit az újra-scan különben sosem látna, mert a bejárás csak a meglévő fájlokat találja).
|
|
586
|
+
*
|
|
587
|
+
* **Subtree-tudatos a KANONIKUS abszolút úton (FAM-REV-050):** a present-set a most bejárt fájlok
|
|
588
|
+
* `absolutePath`-jai; orphan = az a `scan`-forrású, aktív entry, aminek az `source.absolutePath`-ja a
|
|
589
|
+
* jelen scan-gyökér (`resolvedRoot`) ALATT van (`isInside`), DE NINCS a present-set-ben → a fájl eltűnt
|
|
590
|
+
* a gyökér alól. A subtree-szűkítés MOST az absolutePath-on (`isInside`), NEM a `source.root`-egyezésen —
|
|
591
|
+
* így egy al-mappa scan a tágabb gyökérről ingestelt, de a saját subtree-jébe eső fájlokat is helyesen
|
|
592
|
+
* reconciliálja (és kívülre sosem nyúl). Az `absolutePath` nélküli (régi, migrálatlan) entry-ket kihagyja
|
|
593
|
+
* (no-migration safe). `dryRun` → csak számol. Visszaadja a reconciliált (soft-deleted) chunkok számát.
|
|
594
|
+
*/
|
|
595
|
+
async reconcileDeletedFiles(set) {
|
|
596
|
+
const presentPaths = new Set();
|
|
597
|
+
for (const routed of set.discovery.routedFiles) {
|
|
598
|
+
presentPaths.add(routed.absolutePath);
|
|
599
|
+
}
|
|
600
|
+
for (const skipped of set.discovery.skippedFiles) {
|
|
601
|
+
presentPaths.add(skipped.absolutePath);
|
|
602
|
+
}
|
|
603
|
+
const leafScopeId = set.scopePath.length ? set.scopePath[set.scopePath.length - 1].scopeId : undefined;
|
|
604
|
+
let reconciled = 0;
|
|
605
|
+
for (const table of set.discovery.tables) {
|
|
606
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(table);
|
|
607
|
+
if (!registryEntry) {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const dataService = new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: set.issuer });
|
|
611
|
+
const filter = {};
|
|
612
|
+
filter['source.type'] = 'scan';
|
|
613
|
+
if (leafScopeId) {
|
|
614
|
+
filter['scopePath.scopeId'] = leafScopeId;
|
|
615
|
+
}
|
|
616
|
+
const entries = await dataService.findHydratableList(filter);
|
|
617
|
+
for (const entry of entries) {
|
|
618
|
+
const absolutePath = entry.source?.absolutePath;
|
|
619
|
+
// Migrálatlan (absolutePath nélküli) entry → kihagyjuk; csak a jelen gyökér ALATTI fájlokat nézzük.
|
|
620
|
+
if (!entry._id || !absolutePath
|
|
621
|
+
|| !fam_scan_path_util_1.FAM_ScanPath_Util.isInside(set.discovery.resolvedRoot, absolutePath)
|
|
622
|
+
|| presentPaths.has(absolutePath)) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
// Orphan: a forrásfájl eltűnt a gyökér alól → soft-delete + pool-kivétel (FAM-REV-046 coherence).
|
|
626
|
+
if (!set.dryRun) {
|
|
627
|
+
await dataService.deleteData(entry._id);
|
|
628
|
+
embedding_1.FAM_VectorSearch_ControlService.getInstance().removeVector(table, entry._id);
|
|
629
|
+
}
|
|
630
|
+
reconciled++;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return reconciled;
|
|
634
|
+
}
|
|
315
635
|
/** A verdikt-számok aggregálása a `FAM_IngestRun.verdicts`-be. */
|
|
316
636
|
tallyVerdicts(verdicts, items) {
|
|
317
637
|
for (const item of items) {
|
|
@@ -328,6 +648,11 @@ class FAM_Ingest_ControlService {
|
|
|
328
648
|
const value = typeof resolved.value === 'number' ? resolved.value : 20;
|
|
329
649
|
return Math.min(Math.max(value, 1), 200);
|
|
330
650
|
}
|
|
651
|
+
/** Egy scan-szám-config feloldása a scope-kontextussal + fallback-kel (a `scan.estimateMsPerChunk` / `scan.progressIntervalMs`-hez). */
|
|
652
|
+
async resolveScanNumber(key, scopePath, fallback) {
|
|
653
|
+
const resolved = await config_control_service_1.FAM_Config_ControlService.getInstance().resolve(key, { scopePath: scopePath });
|
|
654
|
+
return typeof resolved.value === 'number' ? resolved.value : fallback;
|
|
655
|
+
}
|
|
331
656
|
/**
|
|
332
657
|
* A `failed` run-summary (a scope-resolve / felderítés bukásakor). A run rekord-mentés best-effort
|
|
333
658
|
* (a hiba MÁR persistált); a hívónak a `failed` summary jelzi, hogy a scan nem futott le.
|
|
@@ -343,12 +668,16 @@ class FAM_Ingest_ControlService {
|
|
|
343
668
|
};
|
|
344
669
|
try {
|
|
345
670
|
const runDataService = new fam_ingest_run_data_service_1.FAM_IngestRun_DataService({ issuer: this.issuer });
|
|
346
|
-
|
|
347
|
-
|
|
671
|
+
// A `_id`-t a Dynamo generálja (ObjectId); a persistált run-rekordra a Dynamo-`_id`-vel
|
|
672
|
+
// hivatkozunk a `close`-ban (a client-UUID `findById`-dal nem cast-olható).
|
|
673
|
+
const openedRun = await runDataService.open({
|
|
674
|
+
trigger: set.request.operation, table: [],
|
|
348
675
|
scopePath: set.scopePath, rootPath: set.request.path,
|
|
349
676
|
});
|
|
677
|
+
const persistedRunId = openedRun._id ?? set.runId;
|
|
678
|
+
summary.ingestRunId = persistedRunId;
|
|
350
679
|
await runDataService.close({
|
|
351
|
-
runId:
|
|
680
|
+
runId: persistedRunId, table: [], filesProcessed: 0, filesSkipped: 0, chunkCount: 0,
|
|
352
681
|
verdicts: summary.verdicts, durationMs: summary.durationMs, status: 'failed', errors: summary.errors,
|
|
353
682
|
});
|
|
354
683
|
}
|
|
@@ -4,11 +4,14 @@ exports.FAM_Scan_ControlService = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("fs"));
|
|
6
6
|
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
7
8
|
const config_catalog_const_1 = require("../../../_collections/config-catalog.const");
|
|
8
9
|
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
10
|
+
const fam_famignore_util_1 = require("../_collections/fam-famignore.util");
|
|
9
11
|
const fam_file_routing_util_1 = require("../_collections/fam-file-routing.util");
|
|
10
12
|
const fam_glob_match_util_1 = require("../_collections/fam-glob-match.util");
|
|
11
13
|
const fam_scan_path_util_1 = require("../_collections/fam-scan-path.util");
|
|
14
|
+
const fam_scan_weight_util_1 = require("../_collections/fam-scan-weight.util");
|
|
12
15
|
const fam_secret_exclude_util_1 = require("../_collections/fam-secret-exclude.util");
|
|
13
16
|
/**
|
|
14
17
|
* `FAM_Scan_ControlService` (SP-4.1, dsgn-004 §1/§2/§6) — a `write(scan-*)` fájl-felderítő + routing
|
|
@@ -41,9 +44,17 @@ class FAM_Scan_ControlService {
|
|
|
41
44
|
*/
|
|
42
45
|
async discover(set) {
|
|
43
46
|
const resolvedRoot = fam_scan_path_util_1.FAM_ScanPath_Util.resolveRoot(set.path, this.issuer);
|
|
44
|
-
|
|
47
|
+
// Effektív ignore: config `scan.ignorePatterns` + input `exclude` + a scan-GYÖKÉR `.fdpfamignore`-ja
|
|
48
|
+
// (in-repo, gitignore-szerű; folder/project scan-nél — a scan-file egyetlen explicit fájlt céloz). HOZZÁADÓDIK.
|
|
49
|
+
const baseIgnore = await this.resolveIgnorePatterns(set.scopePath, set.exclude);
|
|
50
|
+
const famIgnore = set.operation === 'scan-file' ? [] : fam_famignore_util_1.FAM_FamIgnore_Util.loadFromDir(resolvedRoot);
|
|
51
|
+
if (famIgnore.length) {
|
|
52
|
+
fsm_dynamo_1.DyFM_Log.info(`[famIngest] .fdpfamignore betöltve (${resolvedRoot}): ${famIgnore.length / 2} minta-sor`);
|
|
53
|
+
}
|
|
54
|
+
const ignorePatterns = [...baseIgnore, ...famIgnore];
|
|
45
55
|
const maxFileSizeBytes = await this.resolveMaxFileSize(set.scopePath);
|
|
46
56
|
const followSymlinks = await this.resolveFollowSymlinks(set.scopePath);
|
|
57
|
+
const testFileWeight = await this.resolveTestFileWeight(set.scopePath);
|
|
47
58
|
const routedFiles = [];
|
|
48
59
|
const skippedFiles = [];
|
|
49
60
|
// A felderített abszolút fájlok (file = 1 db; folder/project = rekurzív bejárás).
|
|
@@ -61,6 +72,8 @@ class FAM_Scan_ControlService {
|
|
|
61
72
|
includePatterns: set.include,
|
|
62
73
|
maxFileSizeBytes: maxFileSizeBytes,
|
|
63
74
|
tableOverride: set.tableOverride,
|
|
75
|
+
weights: set.weights,
|
|
76
|
+
testFileWeight: testFileWeight,
|
|
64
77
|
});
|
|
65
78
|
if (routed.route === 'skip') {
|
|
66
79
|
skippedFiles.push(routed);
|
|
@@ -108,7 +121,12 @@ class FAM_Scan_ControlService {
|
|
|
108
121
|
if (route === 'skip') {
|
|
109
122
|
return { ...base, route: 'skip', skipReason: 'unrouted file-type' };
|
|
110
123
|
}
|
|
111
|
-
|
|
124
|
+
// (6) Súly-kiemelés (FAM-REV-055/058): explicit scan-`weights` → az; különben teszt-/spec-fájl → a default
|
|
125
|
+
// `scan.testFileWeight` (a próza-nehéz specek ne rangsoroljanak az implementáció fölé); különben model-default.
|
|
126
|
+
return {
|
|
127
|
+
...base, route: route,
|
|
128
|
+
weight: fam_scan_weight_util_1.FAM_ScanWeight_Util.resolveWithDefault(set.relativePath, set.weights, set.testFileWeight),
|
|
129
|
+
};
|
|
112
130
|
}
|
|
113
131
|
/**
|
|
114
132
|
* Rekurzív könyvtár-bejárás (a `scan-folder`/`scan-project` felderítője). Az ignore-patterns a
|
|
@@ -207,5 +225,10 @@ class FAM_Scan_ControlService {
|
|
|
207
225
|
const resolved = await config_control_service_1.FAM_Config_ControlService.getInstance().resolve('scan.followSymlinks', { scopePath: scopePath });
|
|
208
226
|
return typeof resolved.value === 'boolean' ? resolved.value : false;
|
|
209
227
|
}
|
|
228
|
+
/** A teszt-/spec-fájlok default súlya a config-ból (`scan.testFileWeight`, default 0.5 — FAM-REV-058). */
|
|
229
|
+
async resolveTestFileWeight(scopePath) {
|
|
230
|
+
const resolved = await config_control_service_1.FAM_Config_ControlService.getInstance().resolve('scan.testFileWeight', { scopePath: scopePath });
|
|
231
|
+
return typeof resolved.value === 'number' ? resolved.value : 0.5;
|
|
232
|
+
}
|
|
210
233
|
}
|
|
211
234
|
exports.FAM_Scan_ControlService = FAM_Scan_ControlService;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* (MVP1 csak fájlnév-/útvonal-alapú secret-exclude).
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.FAM_IngestRun_DataService = exports.FAM_ContentHash_Util = exports.FAM_DeltaCompare_Util = exports.FAM_SlidingChunker_Util = exports.FAM_MdChunker_Util = exports.FAM_TsChunker_Util = exports.FAM_Chunker_ControlService = exports.FAM_GlobMatch_Util = exports.FAM_SecretExclude_Util = exports.FAM_ScanPath_Util = exports.FAM_FileRouting_Util = exports.FAM_Scan_ControlService = exports.FAM_Ingest_ControlService = void 0;
|
|
15
|
+
exports.FAM_IngestRun_DataService = exports.FAM_ContentHash_Util = exports.FAM_DeltaCompare_Util = exports.FAM_SlidingChunker_Util = exports.FAM_MdChunker_Util = exports.FAM_TsChunker_Util = exports.FAM_Chunker_ControlService = exports.FAM_GlobMatch_Util = exports.FAM_SecretExclude_Util = exports.FAM_ScanPath_Util = exports.FAM_ScanProgress_Util = exports.FAM_FileRouting_Util = exports.FAM_Scan_ControlService = exports.FAM_Ingest_ControlService = void 0;
|
|
16
16
|
// SP-4.4 — ingest-orchestrátor (a `write(scan-*)` belépési pontja + group-by-run/delete-run)
|
|
17
17
|
var fam_ingest_control_service_1 = require("./_services/fam-ingest.control-service");
|
|
18
18
|
Object.defineProperty(exports, "FAM_Ingest_ControlService", { enumerable: true, get: function () { return fam_ingest_control_service_1.FAM_Ingest_ControlService; } });
|
|
@@ -21,6 +21,8 @@ var fam_scan_control_service_1 = require("./_services/fam-scan.control-service")
|
|
|
21
21
|
Object.defineProperty(exports, "FAM_Scan_ControlService", { enumerable: true, get: function () { return fam_scan_control_service_1.FAM_Scan_ControlService; } });
|
|
22
22
|
var fam_file_routing_util_1 = require("./_collections/fam-file-routing.util");
|
|
23
23
|
Object.defineProperty(exports, "FAM_FileRouting_Util", { enumerable: true, get: function () { return fam_file_routing_util_1.FAM_FileRouting_Util; } });
|
|
24
|
+
var fam_scan_progress_util_1 = require("./_collections/fam-scan-progress.util");
|
|
25
|
+
Object.defineProperty(exports, "FAM_ScanProgress_Util", { enumerable: true, get: function () { return fam_scan_progress_util_1.FAM_ScanProgress_Util; } });
|
|
24
26
|
var fam_scan_path_util_1 = require("./_collections/fam-scan-path.util");
|
|
25
27
|
Object.defineProperty(exports, "FAM_ScanPath_Util", { enumerable: true, get: function () { return fam_scan_path_util_1.FAM_ScanPath_Util; } });
|
|
26
28
|
var fam_secret_exclude_util_1 = require("./_collections/fam-secret-exclude.util");
|