@fenglimg/fabric-server 0.1.0 → 1.0.0

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.
@@ -0,0 +1,199 @@
1
+ // src/meta-reader.ts
2
+ import { readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { agentsMetaSchema } from "@fenglimg/fabric-shared";
5
+ import { agentsMetaNodeSchema, agentsMetaSchema as agentsMetaSchema2 } from "@fenglimg/fabric-shared";
6
+ var AgentsMetaFileMissingError = class extends Error {
7
+ constructor(metaPath) {
8
+ super(`Fabric agents metadata file is missing: ${metaPath}`);
9
+ this.metaPath = metaPath;
10
+ this.name = "AgentsMetaFileMissingError";
11
+ }
12
+ metaPath;
13
+ code = "FABRIC_META_MISSING";
14
+ };
15
+ var AgentsMetaInvalidError = class extends Error {
16
+ constructor(metaPath, cause) {
17
+ const detail = cause instanceof Error ? cause.message : String(cause);
18
+ super(`Fabric agents metadata file is invalid: ${metaPath}. ${detail}`);
19
+ this.metaPath = metaPath;
20
+ this.name = "AgentsMetaInvalidError";
21
+ }
22
+ metaPath;
23
+ code = "FABRIC_META_INVALID";
24
+ };
25
+ function getAgentsMetaPath(projectRoot) {
26
+ return join(projectRoot, ".fabric", "agents.meta.json");
27
+ }
28
+ function resolveProjectRoot() {
29
+ return process.env.FABRIC_PROJECT_ROOT ?? process.cwd();
30
+ }
31
+ function readAgentsMeta(projectRoot) {
32
+ const metaPath = getAgentsMetaPath(projectRoot);
33
+ let raw;
34
+ try {
35
+ raw = readFileSync(metaPath, "utf8");
36
+ } catch (error) {
37
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
38
+ throw new AgentsMetaFileMissingError(metaPath);
39
+ }
40
+ throw error;
41
+ }
42
+ try {
43
+ return agentsMetaSchema.parse(JSON.parse(raw));
44
+ } catch (error) {
45
+ throw new AgentsMetaInvalidError(metaPath, error);
46
+ }
47
+ }
48
+
49
+ // src/services/read-ledger.ts
50
+ import { randomUUID } from "crypto";
51
+ import { appendFile, readFile } from "fs/promises";
52
+ import { join as join2 } from "path";
53
+ import { ledgerEntrySchema } from "@fenglimg/fabric-shared";
54
+
55
+ // src/services/_shared.ts
56
+ import { resolve, sep } from "path";
57
+ import { createHash } from "crypto";
58
+ import { rename, writeFile } from "fs/promises";
59
+ var FABRIC_DIR = ".fabric";
60
+ var HUMAN_LOCK_FILE = "human-lock.json";
61
+ var LEDGER_FILE = ".intent-ledger.jsonl";
62
+ async function atomicWriteText(path, content) {
63
+ const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
64
+ await writeFile(tempPath, content, "utf8");
65
+ await rename(tempPath, path);
66
+ }
67
+ function sha256(content) {
68
+ return `sha256:${createHash("sha256").update(content).digest("hex")}`;
69
+ }
70
+ function isNodeError(error) {
71
+ return error instanceof Error;
72
+ }
73
+ function assertPathWithinProjectRoot(projectRoot, file) {
74
+ const normalizedProjectRoot = resolve(projectRoot);
75
+ const absolutePath = resolve(normalizedProjectRoot, file);
76
+ const rootPrefix = normalizedProjectRoot.endsWith(sep) ? normalizedProjectRoot : `${normalizedProjectRoot}${sep}`;
77
+ if (!absolutePath.startsWith(rootPrefix)) {
78
+ throw new Error(`Path escapes project root: ${file}`);
79
+ }
80
+ return absolutePath;
81
+ }
82
+
83
+ // src/services/read-ledger.ts
84
+ async function readLedger(projectRoot, options = {}) {
85
+ const ledgerPath = join2(projectRoot, LEDGER_FILE);
86
+ let raw;
87
+ try {
88
+ raw = await readFile(ledgerPath, "utf8");
89
+ } catch (error) {
90
+ if (isNodeError(error) && error.code === "ENOENT") {
91
+ return [];
92
+ }
93
+ throw error;
94
+ }
95
+ return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line, index) => parseLedgerLine(line, index)).filter((entry) => entry !== null).filter((entry) => options.source === void 0 || entry.source === options.source).filter((entry) => options.since === void 0 || entry.ts >= options.since);
96
+ }
97
+ async function appendLedgerEntry(projectRoot, entry) {
98
+ const ledgerPath = join2(projectRoot, LEDGER_FILE);
99
+ const nextEntry = ledgerEntrySchema.parse({
100
+ ...entry,
101
+ id: entry.id ?? `ledger:${randomUUID()}`
102
+ });
103
+ await appendFile(ledgerPath, `${JSON.stringify(nextEntry)}
104
+ `, "utf8");
105
+ return nextEntry;
106
+ }
107
+ function parseLedgerLine(line, index) {
108
+ try {
109
+ const parsed = JSON.parse(line);
110
+ if (parsed.kind === "mcp-event") {
111
+ return null;
112
+ }
113
+ const result = ledgerEntrySchema.safeParse(parsed);
114
+ if (!result.success) {
115
+ return null;
116
+ }
117
+ return {
118
+ ...result.data,
119
+ id: result.data.id ?? createDerivedId(index, line)
120
+ };
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+ function createDerivedId(index, line) {
126
+ return `ledger:${index + 1}:${sha256(line).slice("sha256:".length)}`;
127
+ }
128
+
129
+ // src/services/read-human-lock.ts
130
+ import { readFile as readFile2 } from "fs/promises";
131
+ import { join as join3 } from "path";
132
+ import { humanLockEntrySchema } from "@fenglimg/fabric-shared";
133
+ async function readHumanLock(projectRoot) {
134
+ const document = await readHumanLockDocument(projectRoot);
135
+ return await Promise.all(
136
+ document.locked.map(async (entry) => {
137
+ const currentHash = await hashHumanLockedContent(projectRoot, entry);
138
+ return {
139
+ ...entry,
140
+ drift: currentHash !== entry.hash,
141
+ current_hash: currentHash
142
+ };
143
+ })
144
+ );
145
+ }
146
+ async function readHumanLockEntry(projectRoot, file) {
147
+ const entries = await readHumanLock(projectRoot);
148
+ return entries.find((entry) => entry.file === file) ?? null;
149
+ }
150
+ async function readHumanLockDocument(projectRoot) {
151
+ const humanLockPath = join3(projectRoot, FABRIC_DIR, HUMAN_LOCK_FILE);
152
+ const raw = await readFile2(humanLockPath, "utf8");
153
+ const parsed = JSON.parse(raw);
154
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
155
+ throw new Error(`Fabric human lock file is invalid: ${humanLockPath}`);
156
+ }
157
+ const rawObject = parsed;
158
+ const lockedResult = humanLockEntrySchema.array().safeParse(rawObject.locked ?? []);
159
+ if (!lockedResult.success) {
160
+ throw new Error(`Fabric human lock file is invalid: ${humanLockPath}`);
161
+ }
162
+ return {
163
+ path: humanLockPath,
164
+ rawObject,
165
+ locked: lockedResult.data
166
+ };
167
+ }
168
+ async function hashHumanLockedContent(projectRoot, entry) {
169
+ const targetPath = assertPathWithinProjectRoot(projectRoot, entry.file);
170
+ let content;
171
+ try {
172
+ content = await readFile2(targetPath, "utf8");
173
+ } catch (error) {
174
+ if (isNodeError(error) && error.code === "ENOENT") {
175
+ return "missing";
176
+ }
177
+ throw error;
178
+ }
179
+ const lines = content.split(/\r?\n/);
180
+ const slice = lines.slice(Math.max(entry.start_line - 1, 0), Math.max(entry.end_line, 0)).join("\n");
181
+ return sha256(slice);
182
+ }
183
+
184
+ export {
185
+ AgentsMetaFileMissingError,
186
+ AgentsMetaInvalidError,
187
+ resolveProjectRoot,
188
+ readAgentsMeta,
189
+ FABRIC_DIR,
190
+ atomicWriteText,
191
+ sha256,
192
+ assertPathWithinProjectRoot,
193
+ readLedger,
194
+ appendLedgerEntry,
195
+ readHumanLock,
196
+ readHumanLockEntry,
197
+ readHumanLockDocument,
198
+ hashHumanLockedContent
199
+ };