@futdevpro/fdp-agent-memory 1.1.30 → 1.1.112
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/build/package.json +1 -1
- package/build/src/_cli/_commands/find-duplicates.command.js +41 -16
- package/build/src/_cli/_commands/scan-projects.command.js +113 -113
- package/build/src/_cli/_commands/scan.command.js +2 -0
- package/build/src/_cli/_commands/serve.command.js +110 -10
- package/build/src/_cli/_services/fam-client.service.js +25 -1
- package/build/src/_cli/register-commands.js +28 -0
- package/build/src/_collections/config-catalog.const.js +148 -6
- package/build/src/_collections/error-codes.const.js +2 -0
- package/build/src/_collections/fam-console.util.js +38 -260
- package/build/src/_collections/fam-db-models.const.js +2 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +20 -6
- package/build/src/_collections/fam-error-context.util.js +1 -0
- package/build/src/_collections/fam-mcp-bridge.util.js +15 -1
- package/build/src/_collections/fam-operation-queue.service.js +85 -0
- package/build/src/_collections/fam-project-discovery.util.js +148 -0
- package/build/src/_collections/fam-request-origin.util.js +41 -0
- package/build/src/_collections/fam-retry.util.js +48 -0
- package/build/src/_collections/fam-scan-progress-sink.service.js +26 -0
- package/build/src/_enums/fam-rule-scope.type-enum.js +25 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +29 -0
- package/build/src/_models/data-models/fam-memory.data-model.js +5 -0
- package/build/src/_models/data-models/fam-rules.data-model.js +24 -0
- package/build/src/_models/data-models/fam-scan-job.data-model.js +73 -0
- package/build/src/_models/data-models/fam-scope.data-model.js +9 -0
- package/build/src/_modules/embedding/_services/fam-dedup-warn.control-service.js +40 -0
- package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +74 -16
- package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +50 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +31 -5
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +142 -9
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +9 -0
- package/build/src/_modules/embedding/_services/fam-hydration-coordinator.control-service.js +159 -0
- package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +41 -4
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +67 -12
- package/build/src/_modules/embedding/index.js +5 -1
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +10 -0
- package/build/src/_modules/ingest/_collections/fam-frontmatter.util.js +134 -0
- package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +30 -0
- package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +5 -2
- package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +55 -29
- package/build/src/_modules/ingest/_collections/fam-split-chunker.util.js +139 -0
- package/build/src/_modules/ingest/_models/interfaces/fam-scan-job.interface.js +2 -0
- package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +10 -0
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +68 -1
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +1 -1
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +173 -37
- package/build/src/_modules/ingest/_services/fam-scan-job.control-service.js +429 -0
- package/build/src/_modules/ingest/_services/fam-scan-job.data-service.js +34 -0
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +6 -4
- package/build/src/_modules/ingest/index.js +6 -1
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +151 -8
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +70 -0
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +36 -0
- package/build/src/_modules/retrieval/_collections/fam-lexical-match.util.js +69 -0
- package/build/src/_modules/retrieval/_collections/fam-memory-activation.util.js +66 -0
- package/build/src/_modules/retrieval/_collections/fam-rule-injection.util.js +125 -0
- package/build/src/_modules/retrieval/_collections/fam-rule-propagation.util.js +95 -0
- package/build/src/_modules/retrieval/_collections/fam-rule-scope-context.util.js +74 -0
- package/build/src/_modules/retrieval/_services/fam-global-rule.control-service.js +82 -0
- package/build/src/_modules/retrieval/_services/fam-memory-cold-search.control-service.js +121 -0
- package/build/src/_modules/retrieval/_services/fam-memory-dormancy.control-service.js +99 -0
- package/build/src/_modules/retrieval/_services/fam-memory-reactivation.control-service.js +68 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +15 -2
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +197 -5
- package/build/src/_modules/retrieval/_services/fam-rule-injection.control-service.js +102 -0
- package/build/src/_modules/retrieval/index.js +12 -1
- package/build/src/_modules/scope-reference/_collections/fam-scope-maintenance.util.js +76 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-maintenance.control-service.js +233 -0
- package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +25 -0
- package/build/src/_routes/server/api/api.controller.js +270 -0
- package/build/src/_routes/server/api/fam-doctor.control-service.js +156 -0
- package/build/src/app.server.js +13 -10
- package/client-dist/{chunk-YXHWCJ5O.js → chunk-GF3FJP7E.js} +71 -71
- package/client-dist/chunk-NMUCMQY6.js +1 -0
- package/client-dist/favicon.ico +0 -0
- package/client-dist/index.html +3 -3
- package/client-dist/main-YK4YIVAQ.js +2 -0
- package/package.json +1 -1
- package/build/src/_cli/_collections/fam-project-discovery.util.js +0 -98
- package/client-dist/chunk-I77GXVAQ.js +0 -1
- package/client-dist/main-PJPEDVJT.js +0 -2
package/build/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fdp-agent-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.112",
|
|
4
4
|
"description": "Local-first, vector-backed multi-table agent memory exposed as an MCP server (read/write/capabilities). Public, FDP-Templates-free, no auth.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -10,6 +10,9 @@ const fam_cli_const_1 = require("../_collections/fam-cli.const");
|
|
|
10
10
|
* 2026-06-21). Egy tár vektor-pool-jában megkeresi a NAGYON HASONLÓ (koszinusz ≥ `--threshold`) entry-ket és
|
|
11
11
|
* cluster-ekbe vonja — **NEM töröl**, csak megmutatja az ismétlődéseket (a user dönt). A `GET /api/duplicates/:table`
|
|
12
12
|
* REST-tükre. `--threshold` (0..1, default 0.95), `--neighbors`, `--max-entries` (O(n²)-cap), `--max-clusters`.
|
|
13
|
+
* CROSS-PROJECT mód (user-FR 2026-07-15): a `--path-regex`/`--exclude-path-regex`/`--exclude-root-regex`/
|
|
14
|
+
* `--projects`/`--exclude-projects` Mongo-prefilterrel szűkíti a jelölt-halmazt (célzott scan a nagy táron),
|
|
15
|
+
* a `--min-projects` csak a legalább ennyi KÜLÖNBÖZŐ projektet átfedő clustereket adja.
|
|
13
16
|
*/
|
|
14
17
|
class FAM_FindDuplicates_Util {
|
|
15
18
|
/** A `find-duplicates` parancs-deszkriptor. */
|
|
@@ -22,6 +25,12 @@ class FAM_FindDuplicates_Util {
|
|
|
22
25
|
.option('--neighbors <n>', 'szomszéd-szám/entry (default 10)')
|
|
23
26
|
.option('--max-entries <n>', 'entry-cap az O(n²) ellen (default 3000; a capped jelzi, ha több)')
|
|
24
27
|
.option('--max-clusters <n>', 'a visszaadott cluster-ek max száma (default 100)')
|
|
28
|
+
.option('--path-regex <regex>', 'a sourceFilePath include-regexe (case-insensitive; célzott scan)')
|
|
29
|
+
.option('--exclude-path-regex <regex>', 'a sourceFilePath exclude-regexe (pl. spec/vendor zaj)')
|
|
30
|
+
.option('--exclude-root-regex <regex>', 'a source.root exclude-regexe (pl. STALE-projects)')
|
|
31
|
+
.option('--projects <list>', 'csak ezek a projektek (vessző-szeparált canonicalName lista)')
|
|
32
|
+
.option('--exclude-projects <list>', 'kizárt projektek (vessző-szeparált; fork-zaj elnyomása)')
|
|
33
|
+
.option('--min-projects <n>', 'csak a legalább ennyi KÜLÖNBÖZŐ projektet átfedő clusterek')
|
|
25
34
|
.action(async () => {
|
|
26
35
|
fam_output_util_1.FAM_Output_Util.exit(await FAM_FindDuplicates_Util.run(command));
|
|
27
36
|
});
|
|
@@ -37,6 +46,12 @@ class FAM_FindDuplicates_Util {
|
|
|
37
46
|
neighbors: local.neighbors,
|
|
38
47
|
maxEntries: local.maxEntries,
|
|
39
48
|
maxClusters: local.maxClusters,
|
|
49
|
+
pathRegex: local.pathRegex,
|
|
50
|
+
excludePathRegex: local.excludePathRegex,
|
|
51
|
+
excludeRootRegex: local.excludeRootRegex,
|
|
52
|
+
projects: local.projects,
|
|
53
|
+
excludeProjects: local.excludeProjects,
|
|
54
|
+
minProjects: local.minProjects,
|
|
40
55
|
});
|
|
41
56
|
const path = `/api/duplicates/${encodeURIComponent(local.table)}${query}`;
|
|
42
57
|
const result = await client.get(path);
|
|
@@ -57,19 +72,22 @@ class FAM_FindDuplicates_Util {
|
|
|
57
72
|
}
|
|
58
73
|
/** A query-string összeállítása a megadott (definiált) opciókból. */
|
|
59
74
|
static buildQuery(options) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
// A CLI kebab-opciók → REST camelCase query-kulcsok leképezése (a definiáltakból).
|
|
76
|
+
const mapping = [
|
|
77
|
+
['threshold', options.threshold],
|
|
78
|
+
['neighbors', options.neighbors],
|
|
79
|
+
['maxEntries', options.maxEntries],
|
|
80
|
+
['maxClusters', options.maxClusters],
|
|
81
|
+
['pathRegex', options.pathRegex],
|
|
82
|
+
['excludePathRegex', options.excludePathRegex],
|
|
83
|
+
['excludeRootRegex', options.excludeRootRegex],
|
|
84
|
+
['projects', options.projects],
|
|
85
|
+
['excludeProjects', options.excludeProjects],
|
|
86
|
+
['minProjects', options.minProjects],
|
|
87
|
+
];
|
|
88
|
+
const parts = mapping
|
|
89
|
+
.filter(([, value]) => value !== undefined)
|
|
90
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`);
|
|
73
91
|
return parts.length ? `?${parts.join('&')}` : '';
|
|
74
92
|
}
|
|
75
93
|
/** Ember-olvasható összegzés: scan-fejléc (+ capped-figyelmeztetés) + a top cluster-ek tagokkal. */
|
|
@@ -77,7 +95,8 @@ class FAM_FindDuplicates_Util {
|
|
|
77
95
|
const lines = [];
|
|
78
96
|
lines.push(`Near-duplikátum scan — '${report.table}' (küszöb ${report.threshold}):`);
|
|
79
97
|
lines.push(` vizsgált ${report.scanned} / ${report.totalInPool} entry`
|
|
80
|
-
+ `${report.
|
|
98
|
+
+ `${report.filtered ? ' (szűrt jelölt-halmaz)' : ''}`
|
|
99
|
+
+ `${report.capped ? ' ⚠ CAPPED (a jelölt-halmaz nagyobb — emeld a --max-entries-t vagy szűkíts tovább)' : ''}`);
|
|
81
100
|
lines.push(` ${report.clusterCount} duplikátum-cluster, összesen ${report.duplicateEntryCount} érintett entry`);
|
|
82
101
|
if (!report.clusterCount) {
|
|
83
102
|
lines.push(' ✓ nincs a küszöb feletti near-duplikátum.');
|
|
@@ -85,10 +104,16 @@ class FAM_FindDuplicates_Util {
|
|
|
85
104
|
}
|
|
86
105
|
for (const [index, cluster] of report.clusters.entries()) {
|
|
87
106
|
lines.push('');
|
|
88
|
-
|
|
107
|
+
const projectInfo = cluster.projects?.length
|
|
108
|
+
? ` — ${cluster.projectCount} projekt: ${cluster.projects.join(', ')}`
|
|
109
|
+
: '';
|
|
110
|
+
lines.push(` #${index + 1} — ${cluster.size} elem (score ${cluster.minScore.toFixed(3)}…${cluster.maxScore.toFixed(3)})${projectInfo}:`);
|
|
89
111
|
for (const member of cluster.members) {
|
|
112
|
+
const location = member.sourceFilePath
|
|
113
|
+
? ` ${member.project ? `[${member.project}] ` : ''}${member.sourceFilePath}`
|
|
114
|
+
: '';
|
|
90
115
|
const snippet = member.snippet ? `: ${member.snippet}` : '';
|
|
91
|
-
lines.push(` • [${member.id}]${member.kind ? ` (${member.kind})` : ''}${snippet}`);
|
|
116
|
+
lines.push(` • [${member.id}]${member.kind ? ` (${member.kind})` : ''}${location}${snippet}`);
|
|
92
117
|
}
|
|
93
118
|
}
|
|
94
119
|
return lines.join('\n');
|
|
@@ -1,159 +1,159 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FAM_ScanProjects_Util = void 0;
|
|
4
|
-
const
|
|
4
|
+
const path_1 = require("path");
|
|
5
5
|
const commander_1 = require("commander");
|
|
6
6
|
const fam_cooldown_util_1 = require("../../_collections/fam-cooldown.util");
|
|
7
7
|
const fam_output_util_1 = require("../_collections/fam-output.util");
|
|
8
8
|
const fam_client_service_1 = require("../_services/fam-client.service");
|
|
9
|
-
const fam_cli_const_1 = require("../_collections/fam-cli.const");
|
|
10
|
-
const fam_project_discovery_util_1 = require("../_collections/fam-project-discovery.util");
|
|
11
9
|
/**
|
|
12
|
-
* `fam scan-projects` — több projekt
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* `fam scan-projects` (FEAT-008, THIN-CLI) — több projekt scan-je **SZERVER-OLDALI vezényléssel**. A CLI itt már
|
|
11
|
+
* CSAK egy vékony eszköz: elindítja a job-ot (`POST /api/scan/start`) és poll-ozza az állapotát
|
|
12
|
+
* (`GET /api/scan/status/:jobId`), kiírva a haladást. A tényleges terhet (felderítés + soros feldolgozás + queue +
|
|
13
|
+
* cooldown + progress) a SZERVER viszi — nála van minden (adat, hidratáció). A `--config` fájlt is a SZERVER olvassa.
|
|
16
14
|
*/
|
|
17
15
|
class FAM_ScanProjects_Util {
|
|
18
16
|
/** A `scan-projects` parancs-deszkriptor. */
|
|
19
17
|
static command() {
|
|
20
18
|
const command = new commander_1.Command('scan-projects');
|
|
21
19
|
command
|
|
22
|
-
.description('Több projekt
|
|
23
|
-
+ '
|
|
24
|
-
.option('--root <dir>', 'projekt-gyökerek auto-felderítése e mappa alatt (package.json/.git markerek)')
|
|
25
|
-
.option('--config <file.json>', 'explicit projekt-lista JSON — elemenként `path` + `project`
|
|
20
|
+
.description('Több projekt SZERVER-OLDALI scan-je (a szerver vezényel: felderítés + soros feldolgozás + '
|
|
21
|
+
+ 'queue + cooldown). A CLI csak elindítja a job-ot és poll-ozza az állapotát.')
|
|
22
|
+
.option('--root <dir>', 'projekt-gyökerek auto-felderítése e mappa alatt (a SZERVEREN, package.json/.git markerek)')
|
|
23
|
+
.option('--config <file.json>', 'explicit projekt-lista JSON — a SZERVER olvassa (elemenként `path` + `project`)')
|
|
26
24
|
.option('--depth <n>', 'auto-felderítés max mélysége (default 3)', '3')
|
|
27
25
|
.option('--org <name>', 'default organization az auto-felderítéshez', 'FutDevPro')
|
|
28
26
|
.option('--table <documents|codebase>', 'default cél-tár', 'codebase')
|
|
29
|
-
.option('--
|
|
30
|
-
.option('--cooldown <
|
|
31
|
-
|
|
32
|
-
.option('--
|
|
33
|
-
|
|
34
|
-
+ '
|
|
35
|
-
|
|
36
|
-
+ ' site-packages/dist/build/Logs-féle junkot; a projekt-specifikusat ott add hozzá), és FUTTASD\n'
|
|
37
|
-
+ ' ELŐSZÖR `--dry-run`-nal (per-projekt fájl/chunk-becslés). Utólag kitakarítani (törlés +\n'
|
|
38
|
-
+ ' re-embed minden projekten) sokkal drágább, mint eleve tisztán szkennelni.\n')
|
|
27
|
+
.option('--cooldown <ms>', 'projekt-cooldown override (ms) — felülírja a szerver `scan.projectCooldownMs` configot')
|
|
28
|
+
.option('--cooldown-min-chunks <n>', 'a feltételes cooldown küszöbe (≥ ennyi embed-chunk → vár)')
|
|
29
|
+
.option('--poll <ms>', 'a status-poll periódusa (ms)', '2000')
|
|
30
|
+
.option('--memory-path <dir>', '„SCAN-ALL": EXTRA cél a workspace mellé — az agent-memória mappa ADDITÍV '
|
|
31
|
+
+ 'szkenje (preserveHistory: a kompaktáláskor kieső *.md-k MEGMARADNAK) a `memory` tárba, '
|
|
32
|
+
+ '`project=agent-memory` scope-ra. Önállóan is futtatható (csak a memória).')
|
|
33
|
+
.option('--memory-scope <name>', 'a memória-cél projekt-scope neve (default `agent-memory`)', 'agent-memory')
|
|
39
34
|
.action(async () => {
|
|
40
35
|
fam_output_util_1.FAM_Output_Util.exit(await FAM_ScanProjects_Util.run(command));
|
|
41
36
|
});
|
|
42
37
|
return command;
|
|
43
38
|
}
|
|
44
|
-
/**
|
|
39
|
+
/** Indít egy szerver-oldali scan-job-ot, majd poll-ozza az állapotát a befejezésig (progress a stderr-re). */
|
|
45
40
|
static async run(command) {
|
|
46
41
|
const globals = command.optsWithGlobals();
|
|
47
42
|
const local = command.opts();
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
if (local.config) {
|
|
51
|
-
specs = fam_project_discovery_util_1.FAM_ProjectDiscovery_Util.parseConfig((0, fs_1.readFileSync)(local.config, 'utf-8'));
|
|
52
|
-
}
|
|
53
|
-
else if (local.root) {
|
|
54
|
-
specs = fam_project_discovery_util_1.FAM_ProjectDiscovery_Util.discover(local.root, Number(local.depth) || 3, { org: local.org, table: local.table });
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
return fam_output_util_1.FAM_Output_Util.failure({
|
|
58
|
-
options: globals, command: 'scan-projects',
|
|
59
|
-
error: {
|
|
60
|
-
errorCode: 'FAM-CLI-SCANP-001',
|
|
61
|
-
message: '`--root <dir>` (auto-felderítés) VAGY `--config <file.json>` (explicit lista) kötelező.',
|
|
62
|
-
fixHint: 'Pl.: fam scan-projects --root E:/.../NPM-packages vagy --config projects.json',
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
43
|
+
if (!local.root && !local.config && !local.memoryPath) {
|
|
68
44
|
return fam_output_util_1.FAM_Output_Util.failure({
|
|
69
45
|
options: globals, command: 'scan-projects',
|
|
70
|
-
error: {
|
|
46
|
+
error: {
|
|
47
|
+
errorCode: 'FAM-CLI-SCANP-001',
|
|
48
|
+
message: '`--root <dir>` (auto-felderítés) VAGY `--config <file.json>` (explicit lista) VAGY `--memory-path <dir>` kötelező.',
|
|
49
|
+
fixHint: 'Pl.: fam scan-projects --root E:/.../NPM-packages vagy --config projects.json vagy SCAN-ALL: --root <workspace> --memory-path <mem-dir>',
|
|
50
|
+
},
|
|
71
51
|
});
|
|
72
52
|
}
|
|
73
|
-
|
|
53
|
+
// A start-body — a felderítést/olvasást a SZERVER végzi (thin-CLI). A `sourceLocation` (a CLI cwd-je) MINDIG
|
|
54
|
+
// megy, hogy a szerver a relatív útvonalakat a HÍVÓ helyéhez oldja fel (a szerver cwd-je eltér). A `--root`/
|
|
55
|
+
// `--config` abszolútként is átmegy (belt-and-suspenders).
|
|
56
|
+
const body = { sourceLocation: process.cwd() };
|
|
57
|
+
if (local.config) {
|
|
58
|
+
body.configPath = (0, path_1.resolve)(local.config);
|
|
59
|
+
}
|
|
60
|
+
else if (local.root) {
|
|
61
|
+
body.root = (0, path_1.resolve)(local.root);
|
|
62
|
+
body.depth = Number(local.depth) || 3;
|
|
63
|
+
body.org = local.org;
|
|
64
|
+
body.table = local.table;
|
|
65
|
+
}
|
|
66
|
+
// „SCAN-ALL": a memória-mappa ADDITÍV cél a fő lista UTÁN appendelve (root-szintű `project=<scope>` scope, `memory`
|
|
67
|
+
// tár, preserveHistory → a kompaktáláskor kieső *.md-k megmaradnak). Önállóan is mehet (csak ez, root/config nélkül).
|
|
68
|
+
if (local.memoryPath) {
|
|
69
|
+
body.extraTargets = [{
|
|
70
|
+
path: (0, path_1.resolve)(local.memoryPath),
|
|
71
|
+
project: local.memoryScope ?? 'agent-memory',
|
|
72
|
+
table: 'memory',
|
|
73
|
+
preserveHistory: true,
|
|
74
|
+
scopePath: [{ layer: 'project', rawName: local.memoryScope ?? 'agent-memory' }],
|
|
75
|
+
}];
|
|
76
|
+
}
|
|
77
|
+
if (local.cooldown !== undefined) {
|
|
78
|
+
body.cooldownMs = Number(local.cooldown);
|
|
79
|
+
}
|
|
80
|
+
if (local.cooldownMinChunks !== undefined) {
|
|
81
|
+
body.cooldownMinChunks = Number(local.cooldownMinChunks);
|
|
82
|
+
}
|
|
83
|
+
const client = new fam_client_service_1.FAM_CliClient_Service(globals.serverUrl);
|
|
84
|
+
// A `start` a szerver FAST-PATH-ja miatt MÁR a (rész)job-ot adja vissza: ha a job ≤ a szerver wait-ablakán
|
|
85
|
+
// belül végzett → kész eredmény poll NÉLKÜL; különben futó job → poll-ozunk.
|
|
86
|
+
const start = await client.post('/api/scan/start', body);
|
|
87
|
+
if (!start.ok || !start.data) {
|
|
74
88
|
return fam_output_util_1.FAM_Output_Util.failure({
|
|
75
89
|
options: globals, command: 'scan-projects',
|
|
76
|
-
error: { errorCode: 'FAM-CLI-SCANP-
|
|
77
|
-
exitCode: fam_cli_const_1.FAM_CliExitCode.operationError,
|
|
90
|
+
error: start.error ?? { errorCode: 'FAM-CLI-SCANP-002', message: 'A scan-job indítása sikertelen.' },
|
|
78
91
|
});
|
|
79
92
|
}
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
FAM_ScanProjects_Util.progress(globals, ` HIBA: ${real.error?.message ?? 'ismeretlen scan-hiba'}`);
|
|
114
|
-
projects.push({ project: spec.project, error: real.error });
|
|
115
|
-
if (local.stopOnError) {
|
|
93
|
+
const jobId = start.data.jobId;
|
|
94
|
+
const printed = new Set();
|
|
95
|
+
FAM_ScanProjects_Util.progress(globals, `=== scan-job a SZERVEREN: ${jobId} (${start.data.total} projekt) ===`);
|
|
96
|
+
let last = start.data;
|
|
97
|
+
FAM_ScanProjects_Util.printNew(globals, last, printed);
|
|
98
|
+
if (!FAM_ScanProjects_Util.isTerminal(last.status)) {
|
|
99
|
+
// Nem fért a fast-path-ba → poll a befejezésig (a frissen kész projekteket egyszer írjuk ki). ETA-jelzés.
|
|
100
|
+
const eta = last.etaSeconds !== undefined ? `, ETA ~${last.etaSeconds}s` : '';
|
|
101
|
+
FAM_ScanProjects_Util.progress(globals, ` …fut (${last.currentIndex}/${last.total}${eta}) — poll-ozok`);
|
|
102
|
+
const pollMs = Number(local.poll) || 2000;
|
|
103
|
+
let lastSubProgress = '';
|
|
104
|
+
for (;;) {
|
|
105
|
+
await fam_cooldown_util_1.FAM_Cooldown_Util.sleep(pollMs);
|
|
106
|
+
const status = await client.get(`/api/scan/status/${jobId}`);
|
|
107
|
+
if (!status.ok || !status.data) {
|
|
108
|
+
return fam_output_util_1.FAM_Output_Util.failure({
|
|
109
|
+
options: globals, command: 'scan-projects',
|
|
110
|
+
error: status.error ?? { errorCode: 'FAM-CLI-SCANP-003', message: 'A scan-job állapota nem lekérdezhető.' },
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
last = status.data;
|
|
114
|
+
FAM_ScanProjects_Util.printNew(globals, last, printed);
|
|
115
|
+
// Within-project sub-progress (user-FR): egy HOSSZÚ projekt KÖZBEN is lássunk haladást (nem csak a
|
|
116
|
+
// per-projekt befejezést). Dedup: csak változásnál írunk.
|
|
117
|
+
const sub = last.currentProgress;
|
|
118
|
+
if (sub && last.status === 'running') {
|
|
119
|
+
const line = ` ↳ ${sub.scopeLabel}: ${sub.processed}/${sub.total} fájl (${sub.pct}%)`;
|
|
120
|
+
if (line !== lastSubProgress) {
|
|
121
|
+
lastSubProgress = line;
|
|
122
|
+
FAM_ScanProjects_Util.progress(globals, line);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (FAM_ScanProjects_Util.isTerminal(last.status)) {
|
|
116
126
|
break;
|
|
117
127
|
}
|
|
118
128
|
}
|
|
119
|
-
// Projekt-cooldown: a projektek KÖZÉ (NEM az utolsó után) — gép-tehermentesítő szünet nagy multi-scanhez.
|
|
120
|
-
if (projectCooldownMs > 0 && i < specs.length - 1) {
|
|
121
|
-
FAM_ScanProjects_Util.progress(globals, ` cooldown ${projectCooldownMs}ms…`);
|
|
122
|
-
await fam_cooldown_util_1.FAM_Cooldown_Util.sleep(projectCooldownMs);
|
|
123
|
-
}
|
|
124
129
|
}
|
|
125
|
-
FAM_ScanProjects_Util.progress(globals, `===
|
|
130
|
+
FAM_ScanProjects_Util.progress(globals, `=== ${last.status.toUpperCase()}: ${last.okCount}/${last.total} ok ===`);
|
|
126
131
|
return fam_output_util_1.FAM_Output_Util.success({
|
|
127
132
|
options: globals, command: 'scan-projects',
|
|
128
|
-
data: { total:
|
|
133
|
+
data: { jobId: jobId, status: last.status, total: last.total, ok: last.okCount, projects: last.projects },
|
|
129
134
|
});
|
|
130
135
|
}
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
* cooldown sosem buktatja a scant). Minden út a `FAM_Cooldown_Util.resolveMs`-en megy át (clamp + biztonság).
|
|
135
|
-
*/
|
|
136
|
-
static async resolveCooldownMs(client, cliValue) {
|
|
137
|
-
if (cliValue !== undefined) {
|
|
138
|
-
return fam_cooldown_util_1.FAM_Cooldown_Util.resolveMs(cliValue);
|
|
139
|
-
}
|
|
140
|
-
const result = await client.get('/api/config');
|
|
141
|
-
const value = result.ok ? result.data?.effective?.['scan.projectCooldownMs'] : 0;
|
|
142
|
-
return fam_cooldown_util_1.FAM_Cooldown_Util.resolveMs(value);
|
|
136
|
+
/** Terminál-e a job-státusz (kész/bukott/megszakítva). */
|
|
137
|
+
static isTerminal(status) {
|
|
138
|
+
return status === 'completed' || status === 'failed' || status === 'cancelled';
|
|
143
139
|
}
|
|
144
|
-
/** A
|
|
145
|
-
static
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
140
|
+
/** A frissen (ok/error) befejezett projekteket EGYSZER írja ki (a `printed` halmaz dedupol). */
|
|
141
|
+
static printNew(globals, job, printed) {
|
|
142
|
+
for (const project of job.projects) {
|
|
143
|
+
if ((project.status === 'ok' || project.status === 'error') && !printed.has(project.project)) {
|
|
144
|
+
printed.add(project.project);
|
|
145
|
+
if (project.status === 'ok') {
|
|
146
|
+
FAM_ScanProjects_Util.progress(globals, ` [${printed.size}/${job.total}] ${project.project}: `
|
|
147
|
+
+ `${project.filesProcessed ?? '?'} fájl, ${project.chunks ?? '?'} chunk, `
|
|
148
|
+
+ `${JSON.stringify(project.verdicts ?? {})} (${project.seconds ?? '?'}s)`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
FAM_ScanProjects_Util.progress(globals, ` [${printed.size}/${job.total}] ${project.project}: HIBA — ${project.error?.message ?? 'ismeretlen'}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
155
|
}
|
|
156
|
-
/** Ember-olvasható haladás-sor a stderr-re (
|
|
156
|
+
/** Ember-olvasható haladás-sor a stderr-re (`--json` stdout-ot nem szennyezi; `--quiet` esetén néma). */
|
|
157
157
|
static progress(globals, line) {
|
|
158
158
|
if (!globals.quiet) {
|
|
159
159
|
process.stderr.write(`${line}\n`);
|
|
@@ -26,6 +26,7 @@ class FAM_Scan_Util {
|
|
|
26
26
|
.option('--exclude <glob,...>', 'exclude glob-szűrők (a secret-guard mellé)')
|
|
27
27
|
.option('--scope <layer=name,...>', 'scope-lánc (KÖTELEZŐ)')
|
|
28
28
|
.option('--dry-run', 'nem ír; tervezett fájl/chunk/verdikt-szám', false)
|
|
29
|
+
.option('--preserve-history', 'ADDITÍV: a forrásból eltűnt fájlok bejegyzéseit NEM törli (superset; pl. kompaktálódó memória)', false)
|
|
29
30
|
.addHelpText('before', '\n⚠️ TISZTA SCAN — ELŐBB az IGNORE-FÁJL:\n'
|
|
30
31
|
+ ' 1) Hozz létre `.fdpfamignore`-t a scan-gyökérben (a `.gitignore` mintájára) a projekt-\n'
|
|
31
32
|
+ ' specifikus junk-mappákra (a default már fedi: node_modules/Library/__pycache__/\n'
|
|
@@ -80,6 +81,7 @@ class FAM_Scan_Util {
|
|
|
80
81
|
include: fam_arg_util_1.FAM_Arg_Util.parseList(local.include),
|
|
81
82
|
exclude: exclude,
|
|
82
83
|
dryRun: local.dryRun === true,
|
|
84
|
+
preserveHistory: local.preserveHistory === true,
|
|
83
85
|
},
|
|
84
86
|
};
|
|
85
87
|
const client = new fam_client_service_1.FAM_CliClient_Service(globals.serverUrl);
|
|
@@ -23,9 +23,8 @@ const fam_client_service_1 = require("../_services/fam-client.service");
|
|
|
23
23
|
* - **FEAT-002** („egy szerver, sok bridge"): ha már fut egy egészséges primary a porton → NEM indítunk
|
|
24
24
|
* másodikat (idempotens reuse). Az MCP-kliensek külön `fam start`-ja úgyis ehhez a REST-primary-hoz
|
|
25
25
|
* bridge-el.
|
|
26
|
-
* - **
|
|
27
|
-
*
|
|
28
|
-
* fájlba ír, így `stdio:'ignore'` mellett is működik.
|
|
26
|
+
* - **Saját látható ablak:** a detach-olt szerver a SAJÁT, látható konzol-ablakában fut (`spawnServerWindow`) —
|
|
27
|
+
* az ablak KÖZVETLENÜL a node-ot futtatja, a szerver élő stdout/stderr-jét mutatja, és túléli a CLI/agent kilépését.
|
|
29
28
|
*
|
|
30
29
|
* A boot **REST-only** (a belépő argumentum-nélküli ága): tiszta HTTP-primary, MCP-stdio nélkül (a detach-olt
|
|
31
30
|
* processnek nincs stdin-kliense). A heap-plafon a nagy korpusz boot-hidratálásához emelt (`--max-old-space-size`,
|
|
@@ -43,6 +42,109 @@ class FAM_Serve_Util {
|
|
|
43
42
|
static HEALTH_POLL_MS = 1000;
|
|
44
43
|
/** Az utolsó `fam serve` indítás állapot-fájlja (port + db + heap) — a `--restart`/auto-restart innen tudja, mit. */
|
|
45
44
|
static STATE_FILE = (0, path_1.join)((0, os_1.tmpdir)(), 'fam-serve-state.json');
|
|
45
|
+
/**
|
|
46
|
+
* Az `ensureRunning()` egyidejű-spawn védő in-flight promise-e: ha több párhuzamos hívó (pl. több MCP-tool-hívás
|
|
47
|
+
* vagy bridge + CLI) EGYSZERRE találja le-állva a szervert, NE indítson mindenki külön példányt — az első spawn
|
|
48
|
+
* promise-ét osztjuk (process-en belül). A cross-process verseny benign (a port-preflight a 2. spawnt elejti).
|
|
49
|
+
*/
|
|
50
|
+
static ensureInFlight = null;
|
|
51
|
+
/** Az utolsó (process-en belüli) ensure-spawn epoch-ms-e — a sorozatos tool-hívások ne nyissanak több ablakot, amíg az előző primary bootol. */
|
|
52
|
+
static lastEnsureSpawnAt = 0;
|
|
53
|
+
/** Az ensure-spawn cooldown-ja ms-ban: ennyin belül egy újabb le-állt-észlelés NEM spawn-ol újra (a bootoló primary-t várjuk). */
|
|
54
|
+
static ENSURE_SPAWN_COOLDOWN_MS = 15000;
|
|
55
|
+
/**
|
|
56
|
+
* `ensureRunning()` — **ÚJRAHASZNÁLHATÓ ensure-szemantika MINDEN FAM-funkcióhoz** (user-FR 2026-06-22: „minden
|
|
57
|
+
* fam funkciónak automatikusan gondoskodnia kell róla, hogy a fam el legyen indítva, ha nincs"). Health-check a
|
|
58
|
+
* (FAM_SERVER_URL/default `127.0.0.1:39265`) primary-n; ha fut → no-op (`already-running`). Ha NEM fut ÉS a cél
|
|
59
|
+
* **loopback** → leválasztott (detached) REST-primary spawn (a `fam serve` magjával: saját ablak, túléli a hívót)
|
|
60
|
+
* + health-bevárás. Remote cél → `remote-unreachable` (nem indíthatunk távoli szervert). Egyidejűség-védett.
|
|
61
|
+
*
|
|
62
|
+
* A hívók: az **MCP bridge** (unreachable → ensure + retry, önheal), a **`fam start` no-primary ág** (detached
|
|
63
|
+
* primary + bridge), és a **CLI data-parancsok** (pre-action). Best-effort, nem dob — a `reason` viszi a kimenetet.
|
|
64
|
+
*/
|
|
65
|
+
static async ensureRunning(options) {
|
|
66
|
+
const client = new fam_client_service_1.FAM_CliClient_Service();
|
|
67
|
+
const baseUrl = client.getBaseUrl();
|
|
68
|
+
const port = FAM_Serve_Util.resolvePortFromUrl(baseUrl) ?? FAM_Serve_Util.resolvePort();
|
|
69
|
+
if (await client.isReachable()) {
|
|
70
|
+
return { running: true, spawned: false, url: baseUrl, port: port, reason: 'already-running' };
|
|
71
|
+
}
|
|
72
|
+
if (!FAM_Serve_Util.isLoopbackUrl(baseUrl)) {
|
|
73
|
+
// Távoli (nem-loopback) primary-t nem tudunk lokálisan felhúzni — csak jelezzük.
|
|
74
|
+
return { running: false, spawned: false, url: baseUrl, port: port, reason: 'remote-unreachable' };
|
|
75
|
+
}
|
|
76
|
+
if (FAM_Serve_Util.ensureInFlight) {
|
|
77
|
+
return FAM_Serve_Util.ensureInFlight;
|
|
78
|
+
}
|
|
79
|
+
// Spawn-spam védelem: ha az imént (cooldown-on belül) MÁR spawn-oltunk egy primary-t, az még BOOTOL-hat
|
|
80
|
+
// (pool-hidratálás) — ne nyissunk újabb ablakot minden tool-hívásra; jelezzük, hogy „indul" (a hívó retry-zik).
|
|
81
|
+
if (Date.now() - FAM_Serve_Util.lastEnsureSpawnAt < FAM_Serve_Util.ENSURE_SPAWN_COOLDOWN_MS) {
|
|
82
|
+
return { running: false, spawned: false, url: baseUrl, port: port, reason: 'boot-timeout' };
|
|
83
|
+
}
|
|
84
|
+
FAM_Serve_Util.lastEnsureSpawnAt = Date.now();
|
|
85
|
+
FAM_Serve_Util.ensureInFlight = FAM_Serve_Util.spawnDetachedForEnsure({
|
|
86
|
+
port: port, baseUrl: baseUrl, client: client, waitMs: options?.waitMs,
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
return await FAM_Serve_Util.ensureInFlight;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
FAM_Serve_Util.ensureInFlight = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Az `ensureRunning` spawn-magja (NEM CLI — nincs `FAM_Output`): a `serve` detached-spawn + health-wait logikájának
|
|
97
|
+
* újrahasznált, riport-mentes változata. Felhúzza a REST-only primary-t a saját ablakában, és bevárja a `/api/health`-et.
|
|
98
|
+
*/
|
|
99
|
+
static async spawnDetachedForEnsure(set) {
|
|
100
|
+
const entry = FAM_Serve_Util.resolveServerEntry();
|
|
101
|
+
if (!entry) {
|
|
102
|
+
return { running: false, spawned: false, url: set.baseUrl, port: set.port, reason: 'no-entry' };
|
|
103
|
+
}
|
|
104
|
+
const envOverrides = {
|
|
105
|
+
FAM_SERVER_PORT: String(set.port),
|
|
106
|
+
FAM_LOG_FILE: (0, path_1.join)((0, os_1.tmpdir)(), `fam-server-${set.port}.log`),
|
|
107
|
+
};
|
|
108
|
+
const heapMb = FAM_Serve_Util.resolveHeapMb();
|
|
109
|
+
const nodeArgs = heapMb > 0 ? [`--max-old-space-size=${heapMb}`] : [];
|
|
110
|
+
try {
|
|
111
|
+
fam_console_util_1.FAM_Console_Util.spawnServerWindow({
|
|
112
|
+
command: process.execPath, args: [...nodeArgs, entry], envOverrides: envOverrides, port: set.port,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return { running: false, spawned: false, url: set.baseUrl, port: set.port, reason: 'spawn-failed' };
|
|
117
|
+
}
|
|
118
|
+
const waitMs = set.waitMs ?? FAM_Serve_Util.DEFAULT_WAIT_SEC * 1000;
|
|
119
|
+
const healthy = await FAM_Serve_Util.waitForHealthy(set.client, waitMs);
|
|
120
|
+
if (healthy) {
|
|
121
|
+
FAM_Serve_Util.writeServeState({ port: set.port, heapMb: heapMb });
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
running: healthy, spawned: true, url: set.baseUrl, port: set.port,
|
|
125
|
+
reason: healthy ? 'spawned-healthy' : 'boot-timeout',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** A port kinyerése egy base-URL-ből (`http://host:port`); nincs explicit port / hiba → `undefined` (a hívó default-ol). */
|
|
129
|
+
static resolvePortFromUrl(url) {
|
|
130
|
+
try {
|
|
131
|
+
const parsedPort = Number(new URL(url).port);
|
|
132
|
+
return Number.isInteger(parsedPort) && parsedPort > 0 ? parsedPort : undefined;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Loopback-cél-e az URL (`127.0.0.1`/`localhost`/`::1`/`0.0.0.0`)? Csak ilyet húzhatunk fel lokálisan. */
|
|
139
|
+
static isLoopbackUrl(url) {
|
|
140
|
+
try {
|
|
141
|
+
const host = new URL(url).hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
142
|
+
return host === '127.0.0.1' || host === 'localhost' || host === '::1' || host === '0.0.0.0';
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
46
148
|
/** A `serve` parancs-deszkriptor. */
|
|
47
149
|
static command() {
|
|
48
150
|
const command = new commander_1.Command('serve');
|
|
@@ -173,13 +275,11 @@ class FAM_Serve_Util {
|
|
|
173
275
|
},
|
|
174
276
|
});
|
|
175
277
|
}
|
|
176
|
-
// A
|
|
177
|
-
//
|
|
178
|
-
// dupla ablak; a user-direktíva szerint az Activity Monitor így redundáns).
|
|
278
|
+
// A detached-szerver env-override-jai (a többit a szerver a saját `.env`-jéből olvassa). A szerver a SAJÁT
|
|
279
|
+
// látható ablakában fut (`spawnServerWindow`) → az ablak a szerver konzolja (nincs külön monitor-ablak).
|
|
179
280
|
const envOverrides = {
|
|
180
281
|
FAM_SERVER_PORT: String(set.port),
|
|
181
|
-
|
|
182
|
-
// File-log → a detached szerver logjai (import-progress is) FÁJLBÓL OLVASHATÓK (tail), ablak nélkül.
|
|
282
|
+
// File-log → a detached szerver logjai (import-progress is) FÁJLBÓL is OLVASHATÓK (tail) a látható ablak MELLETT.
|
|
183
283
|
FAM_LOG_FILE: (0, path_1.join)((0, os_1.tmpdir)(), `fam-server-${set.port}.log`),
|
|
184
284
|
};
|
|
185
285
|
if (set.local.mongodbUri) {
|
|
@@ -198,8 +298,8 @@ class FAM_Serve_Util {
|
|
|
198
298
|
return FAM_Serve_Util.runForeground([...nodeArgs, entry], { ...process.env, ...envOverrides });
|
|
199
299
|
}
|
|
200
300
|
// DETACHED: a szervert egy KÜLÖN, LÁTHATÓ konzol-ablakban indítjuk (a `start`/terminál teljesen leválasztja
|
|
201
|
-
// a CLI-ről → túléli; az ablak a szerver élő stdout/stderr-jét mutatja). REST-only primary (a belépő
|
|
202
|
-
// argumentum-nélküli ága).
|
|
301
|
+
// a CLI-ről → túléli a hívót; az ablak a szerver élő stdout/stderr-jét mutatja). REST-only primary (a belépő
|
|
302
|
+
// argumentum-nélküli ága). Egy szerver = egy látható ablak (nincs külön activity-monitor).
|
|
203
303
|
try {
|
|
204
304
|
fam_console_util_1.FAM_Console_Util.spawnServerWindow({
|
|
205
305
|
command: process.execPath,
|
|
@@ -64,6 +64,23 @@ class FAM_CliClient_Service {
|
|
|
64
64
|
async put(path, body) {
|
|
65
65
|
return this.request('PUT', path, body);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* EVENT-SIGNING aláírás-headerek (user-FR 2026-06-25): a CLI minden REST-hívása `fam-cli`-ként alá van írva; ha az
|
|
69
|
+
* agent beállítja a `FAM_SESSION_ID` env-et, az a hívó session-azonosítója (több egyidejű workspace-session
|
|
70
|
+
* elkülönítéséhez). A `FAM_WORKSPACE` env vagy a CLI `cwd` adja a workspace-kontextust.
|
|
71
|
+
*/
|
|
72
|
+
signingHeaders() {
|
|
73
|
+
const headers = { 'x-fam-agent': 'fam-cli' };
|
|
74
|
+
const sessionId = process.env.FAM_SESSION_ID;
|
|
75
|
+
if (sessionId && sessionId.trim()) {
|
|
76
|
+
headers['x-fam-session'] = sessionId.trim();
|
|
77
|
+
}
|
|
78
|
+
const workspace = process.env.FAM_WORKSPACE || process.cwd();
|
|
79
|
+
if (workspace) {
|
|
80
|
+
headers['x-fam-workspace'] = workspace;
|
|
81
|
+
}
|
|
82
|
+
return headers;
|
|
83
|
+
}
|
|
67
84
|
/**
|
|
68
85
|
* A közös HTTP-réteg: a server-választ `FAM_ClientResult`-tá fordítja. Elérhetetlen szerver →
|
|
69
86
|
* `unreachable:true`; a `{ ok:false, error }` test → `error` átvitel; egyéb non-2xx → szintetikus
|
|
@@ -71,11 +88,18 @@ class FAM_CliClient_Service {
|
|
|
71
88
|
*/
|
|
72
89
|
async request(method, path, body) {
|
|
73
90
|
const url = `${this.baseUrl}${path}`;
|
|
91
|
+
// EVENT-SIGNING (user-FR 2026-06-25): a CLI MINDEN hívását aláírjuk → a `fam scan-projects` (és minden más)
|
|
92
|
+
// beazonosíthatóan `fam-cli`-ként szerepel a job-okon. A `FAM_SESSION_ID` env (ha az agent beállítja) a hívó
|
|
93
|
+
// session-azonosítója. Egy helyen, a közös request-rétegben → minden CLI-parancs aláírt.
|
|
94
|
+
const headers = this.signingHeaders();
|
|
95
|
+
if (body !== undefined) {
|
|
96
|
+
headers['content-type'] = 'application/json';
|
|
97
|
+
}
|
|
74
98
|
let response;
|
|
75
99
|
try {
|
|
76
100
|
response = await fetch(url, {
|
|
77
101
|
method: method,
|
|
78
|
-
headers:
|
|
102
|
+
headers: headers,
|
|
79
103
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
80
104
|
});
|
|
81
105
|
}
|