@futdevpro/fdp-agent-memory 1.1.12 → 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/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.14",
|
|
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": {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FAM_Serve_Util = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
4
5
|
const child_process_1 = require("child_process");
|
|
6
|
+
const net = tslib_1.__importStar(require("net"));
|
|
5
7
|
const fs_1 = require("fs");
|
|
6
8
|
const os_1 = require("os");
|
|
7
9
|
const path_1 = require("path");
|
|
@@ -147,7 +149,13 @@ class FAM_Serve_Util {
|
|
|
147
149
|
if (action === 'restart') {
|
|
148
150
|
fam_output_util_1.FAM_Output_Util.logInfo(globals, `FAM szerver RESTART: a(z) ${port} porton futó szerver leállítása…`);
|
|
149
151
|
await FAM_Serve_Util.stopServerOnPort(port);
|
|
150
|
-
|
|
152
|
+
// A health-down NEM elég: az új szerver bind-preflight-je a TÉNYLEGESEN SZABAD (bindolható) portot
|
|
153
|
+
// igényli (a kill utáni process-halál + TIME_WAIT lassú lehet) → a port bindolhatóságát várjuk.
|
|
154
|
+
const freed = await FAM_Serve_Util.waitForPortFree(port, 12000);
|
|
155
|
+
if (!freed) {
|
|
156
|
+
fam_output_util_1.FAM_Output_Util.logInfo(globals, `⚠ a(z) ${port} port 12s alatt nem szabadult fel — az indítás megpróbálom, `
|
|
157
|
+
+ 'de ha bind-hiba van, futtasd újra a `fam serve --restart`-ot.');
|
|
158
|
+
}
|
|
151
159
|
}
|
|
152
160
|
return FAM_Serve_Util.spawnServer({ globals: globals, local: local, port: port, healthUrl: healthUrl, client: client });
|
|
153
161
|
}
|
|
@@ -265,14 +273,24 @@ class FAM_Serve_Util {
|
|
|
265
273
|
return false;
|
|
266
274
|
}
|
|
267
275
|
/** A port LE-állásának bevárása (a `--restart` kill után, mielőtt újraindítunk), felső korláttal. */
|
|
268
|
-
static async
|
|
276
|
+
static async waitForPortFree(port, timeoutMs) {
|
|
269
277
|
const deadline = Date.now() + timeoutMs;
|
|
270
278
|
while (Date.now() < deadline) {
|
|
271
|
-
if (
|
|
272
|
-
return;
|
|
279
|
+
if (await FAM_Serve_Util.isPortFree(port)) {
|
|
280
|
+
return true;
|
|
273
281
|
}
|
|
274
282
|
await FAM_Serve_Util.sleep(FAM_Serve_Util.HEALTH_POLL_MS);
|
|
275
283
|
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
/** Bindolható-e (szabad-e) a port? Egy próba-`listen` a loopbackon, azonnali close — `true`, ha köthető. */
|
|
287
|
+
static isPortFree(port) {
|
|
288
|
+
return new Promise((resolveCheck) => {
|
|
289
|
+
const tester = net.createServer();
|
|
290
|
+
tester.once('error', () => resolveCheck(false));
|
|
291
|
+
tester.once('listening', () => tester.close(() => resolveCheck(true)));
|
|
292
|
+
tester.listen(port, '127.0.0.1');
|
|
293
|
+
});
|
|
276
294
|
}
|
|
277
295
|
/**
|
|
278
296
|
* A megadott porton FIGYELŐ process leállítása (a `--restart` magja). Platform-natív, fail-safe: Windows →
|
|
@@ -284,7 +302,12 @@ class FAM_Serve_Util {
|
|
|
284
302
|
const command = isWin ? (process.env.ComSpec ?? 'powershell.exe') : 'sh';
|
|
285
303
|
const psScript = `$ErrorActionPreference='SilentlyContinue';`
|
|
286
304
|
+ `(Get-NetTCPConnection -LocalPort ${port} -State Listen).OwningProcess | Select-Object -Unique | `
|
|
287
|
-
+ `ForEach-Object { Stop-Process -Id $_ -Force }
|
|
305
|
+
+ `ForEach-Object { Stop-Process -Id $_ -Force };`
|
|
306
|
+
// A serve-ablakot (a fam-server-<port>.ps1-t futtató powershell) is leállítjuk → nincs árva,
|
|
307
|
+
// „[FAM szerver kilépett]" zombi-ablak a restart után (különben halmozódnak).
|
|
308
|
+
+ `Get-CimInstance Win32_Process -Filter "Name='powershell.exe'" | `
|
|
309
|
+
+ `Where-Object { $_.CommandLine -like '*fam-server-${port}*' } | `
|
|
310
|
+
+ `ForEach-Object { Stop-Process -Id $_.ProcessId -Force }`;
|
|
288
311
|
const args = isWin
|
|
289
312
|
? ['-NoProfile', '-Command', psScript]
|
|
290
313
|
: ['-c', `lsof -ti tcp:${port} | xargs -r kill -9`];
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_GitRepo_Util = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
/**
|
|
8
|
+
* `FAM_GitRepo_Util` (user-FR 2026-06-21) — egy scannelt fájlhoz felderíti a **git-repo provenance-t**:
|
|
9
|
+
* a repo-gyökeret (`.git` felfelé keresve), a **GitHub/remote URL-t** (`.git/config` `remote.origin.url`,
|
|
10
|
+
* normalizálva), a **branch-et** (`.git/HEAD`) és a **repo-gyökérhez relatív utat**. **Függőség-mentes**
|
|
11
|
+
* (NEM `git`-parancs — közvetlenül a `.git/` fájlokból olvas; gyors + offline). A scan **repoRoot-onként cache-eli**
|
|
12
|
+
* (egy mappa-scan ~1 repo → egyszer olvas), így a per-fájl költség elhanyagolható.
|
|
13
|
+
*
|
|
14
|
+
* Miért így (a user „relative path + GitHub repo" döntése): a repo-URL + a **REPO-relatív** út KANONIKUS,
|
|
15
|
+
* gép-független, kattintható citáció (`<repoUrl>/blob/<branch>/<repoRelativePath>`) — szemben a gép-specifikus
|
|
16
|
+
* abszolút úttal vagy a törékeny „projektnévből kiszámolt" relatív úttal.
|
|
17
|
+
*/
|
|
18
|
+
class FAM_GitRepo_Util {
|
|
19
|
+
/** Meddig megyünk felfelé `.git`-et keresve (biztonsági korlát a végtelen ciklus ellen). */
|
|
20
|
+
static MAX_WALK_UP = 40;
|
|
21
|
+
/** Belső repoRoot→info cache (a scan-során a `.git` nem változik; egy repo → egyszer olvas). */
|
|
22
|
+
static _cache = new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Egy scannelt fájl **scan-source git-mezői** (a `FAM_Source`-ba spread-elve): `repoUrl` / `repoName` /
|
|
25
|
+
* `repoRelativePath` (a repo-gyökérhez) / `repoBranch`. Cache-elt (belső statikus Map) + fail-safe (üres
|
|
26
|
+
* objektum, ha a fájl NINCS git-repóban / hiba) — a scan SOHA nem bukik el a git-felderítésen.
|
|
27
|
+
*/
|
|
28
|
+
static sourceFieldsFor(absFilePath) {
|
|
29
|
+
try {
|
|
30
|
+
const info = FAM_GitRepo_Util.resolve(absFilePath, FAM_GitRepo_Util._cache);
|
|
31
|
+
if (!info) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
repoUrl: info.repoUrl,
|
|
36
|
+
repoName: info.repoName,
|
|
37
|
+
repoRelativePath: FAM_GitRepo_Util.relativePath(absFilePath, info.repoRoot),
|
|
38
|
+
repoBranch: info.repoBranch,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A teljes repo-info felderítése egy fájl abszolút útjából, **cache-elve a repoRoot-on** (a hívó adja a
|
|
47
|
+
* Map-et; egy scan végig egyet használ). `null`, ha a fájl NINCS git-repóban. A relatív utat a hívó a
|
|
48
|
+
* `relativePath`-szal számolja (a repoRoot ismeretében).
|
|
49
|
+
*/
|
|
50
|
+
static resolve(absFilePath, cache) {
|
|
51
|
+
const repoRoot = FAM_GitRepo_Util.findRepoRoot(absFilePath);
|
|
52
|
+
if (!repoRoot) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const cached = cache.get(repoRoot);
|
|
56
|
+
if (cached !== undefined) {
|
|
57
|
+
return cached;
|
|
58
|
+
}
|
|
59
|
+
const remote = FAM_GitRepo_Util.readRemoteUrl(repoRoot);
|
|
60
|
+
const info = {
|
|
61
|
+
repoRoot: repoRoot,
|
|
62
|
+
repoUrl: remote?.url,
|
|
63
|
+
repoName: remote?.name,
|
|
64
|
+
repoBranch: FAM_GitRepo_Util.readBranch(repoRoot),
|
|
65
|
+
};
|
|
66
|
+
cache.set(repoRoot, info);
|
|
67
|
+
return info;
|
|
68
|
+
}
|
|
69
|
+
/** A repo-gyökér keresése felfelé (`.git` mappa VAGY fájl — a submodule `.git` fájl). `null`, ha nincs. */
|
|
70
|
+
static findRepoRoot(absStartPath) {
|
|
71
|
+
let dir = absStartPath;
|
|
72
|
+
try {
|
|
73
|
+
// Ha a start egy fájl, a könyvtárából indulunk; ha mappa, magából.
|
|
74
|
+
if (fs.existsSync(absStartPath) && fs.statSync(absStartPath).isFile()) {
|
|
75
|
+
dir = path.dirname(absStartPath);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
dir = path.dirname(absStartPath);
|
|
80
|
+
}
|
|
81
|
+
for (let i = 0; i < FAM_GitRepo_Util.MAX_WALK_UP; i++) {
|
|
82
|
+
if (fs.existsSync(path.join(dir, '.git'))) {
|
|
83
|
+
return dir;
|
|
84
|
+
}
|
|
85
|
+
const parent = path.dirname(dir);
|
|
86
|
+
if (parent === dir) {
|
|
87
|
+
return null; // elértük a filesystem-gyökeret
|
|
88
|
+
}
|
|
89
|
+
dir = parent;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A `remote.origin.url` kiolvasása a `.git/config`-ból + normalizálva (`https://github.com/owner/repo`).
|
|
95
|
+
* `undefined`, ha nincs origin / nem olvasható. (A submodule `.git` FÁJL `gitdir:`-re mutat — ezt is követjük.)
|
|
96
|
+
*/
|
|
97
|
+
static readRemoteUrl(repoRoot) {
|
|
98
|
+
try {
|
|
99
|
+
const gitPath = path.join(repoRoot, '.git');
|
|
100
|
+
const configPath = fs.statSync(gitPath).isDirectory()
|
|
101
|
+
? path.join(gitPath, 'config')
|
|
102
|
+
: path.join(FAM_GitRepo_Util.resolveGitdir(repoRoot, gitPath), 'config');
|
|
103
|
+
if (!fs.existsSync(configPath)) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
107
|
+
const rawUrl = FAM_GitRepo_Util.extractOriginUrl(raw);
|
|
108
|
+
return rawUrl ? FAM_GitRepo_Util.normalizeRepoUrl(rawUrl) : undefined;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** A `.git/HEAD`-ből az aktuális branch (`ref: refs/heads/<branch>`); detached SHA / hiba → `undefined`. */
|
|
115
|
+
static readBranch(repoRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const gitPath = path.join(repoRoot, '.git');
|
|
118
|
+
const headPath = fs.statSync(gitPath).isDirectory()
|
|
119
|
+
? path.join(gitPath, 'HEAD')
|
|
120
|
+
: path.join(FAM_GitRepo_Util.resolveGitdir(repoRoot, gitPath), 'HEAD');
|
|
121
|
+
const head = fs.readFileSync(headPath, 'utf-8').trim();
|
|
122
|
+
const match = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
123
|
+
return match ? match[1].trim() : undefined;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** A fájl REPO-gyökérhez relatív útja (POSIX-szeparátorral, a kattintható citációhoz). */
|
|
130
|
+
static relativePath(absFilePath, repoRoot) {
|
|
131
|
+
return path.relative(repoRoot, absFilePath).replace(/\\/g, '/');
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A nyers remote-URL normalizálása kattintható HTTPS-formára + `owner/repo` névre. Lefedi:
|
|
135
|
+
* `git@host:owner/repo.git`, `https://host/owner/repo.git`, `ssh://git@host/owner/repo.git`. A `.git`-suffix
|
|
136
|
+
* + a beágyazott credential levágva. Nem-github host esetén a host marad (gitlab/bitbucket is kattintható).
|
|
137
|
+
*/
|
|
138
|
+
static normalizeRepoUrl(rawUrl) {
|
|
139
|
+
let url = rawUrl.trim();
|
|
140
|
+
// scp-szerű: git@host:owner/repo(.git) → https://host/owner/repo
|
|
141
|
+
const scp = url.match(/^[\w.-]+@([\w.-]+):(.+)$/);
|
|
142
|
+
if (scp) {
|
|
143
|
+
url = `https://${scp[1]}/${scp[2]}`;
|
|
144
|
+
}
|
|
145
|
+
url = url
|
|
146
|
+
.replace(/^ssh:\/\/(?:[\w.-]+@)?/, 'https://')
|
|
147
|
+
.replace(/^git:\/\//, 'https://')
|
|
148
|
+
.replace(/^https?:\/\/[^@/]+@/, 'https://') // beágyazott credential levágása
|
|
149
|
+
.replace(/\.git$/, '')
|
|
150
|
+
.replace(/\/+$/, '');
|
|
151
|
+
const nameMatch = url.match(/^https?:\/\/[\w.-]+\/(.+?\/[^/]+)$/);
|
|
152
|
+
return { url: url, name: nameMatch ? nameMatch[1] : undefined };
|
|
153
|
+
}
|
|
154
|
+
/** A `[remote "origin"]` szekció `url = …` sora a `.git/config`-ból (defenzív, szekció-tudatos). */
|
|
155
|
+
static extractOriginUrl(configText) {
|
|
156
|
+
let inOrigin = false;
|
|
157
|
+
for (const rawLine of configText.split(/\r?\n/)) {
|
|
158
|
+
const line = rawLine.trim();
|
|
159
|
+
const section = line.match(/^\[remote\s+"([^"]+)"\]$/);
|
|
160
|
+
if (section) {
|
|
161
|
+
inOrigin = section[1] === 'origin';
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (line.startsWith('[')) {
|
|
165
|
+
inOrigin = false;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (inOrigin) {
|
|
169
|
+
const url = line.match(/^url\s*=\s*(.+)$/);
|
|
170
|
+
if (url) {
|
|
171
|
+
return url[1].trim();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
/** A submodule `.git` FÁJL (`gitdir: <relpath>`) feloldása a tényleges git-könyvtárra. */
|
|
178
|
+
static resolveGitdir(repoRoot, gitFilePath) {
|
|
179
|
+
try {
|
|
180
|
+
const content = fs.readFileSync(gitFilePath, 'utf-8').trim();
|
|
181
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
182
|
+
if (match) {
|
|
183
|
+
const target = match[1].trim();
|
|
184
|
+
return path.isAbsolute(target) ? target : path.resolve(repoRoot, target);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* fall through */
|
|
189
|
+
}
|
|
190
|
+
return gitFilePath;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
exports.FAM_GitRepo_Util = FAM_GitRepo_Util;
|
|
@@ -15,6 +15,7 @@ const fam_reference_code_util_1 = require("../../../_collections/fam-reference-c
|
|
|
15
15
|
const embedding_1 = require("../../embedding");
|
|
16
16
|
const scope_reference_1 = require("../../scope-reference");
|
|
17
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");
|
|
18
19
|
const fam_project_identity_util_1 = require("../_collections/fam-project-identity.util");
|
|
19
20
|
const fam_scan_path_util_1 = require("../_collections/fam-scan-path.util");
|
|
20
21
|
const fam_scan_progress_util_1 = require("../_collections/fam-scan-progress.util");
|
|
@@ -408,6 +409,8 @@ class FAM_Ingest_ControlService {
|
|
|
408
409
|
headingPath: set.chunk.headingPath,
|
|
409
410
|
source: {
|
|
410
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),
|
|
411
414
|
},
|
|
412
415
|
addedBy: 'scan',
|
|
413
416
|
ingestRunId: set.runId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fdp-agent-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
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": {
|