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