@fenglimg/fabric-server 1.6.0 → 1.8.0-rc.1
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/dist/chunk-E3BHIUIW.js +2903 -0
- package/dist/{http-DJCTLGF4.js → http-MEFXOG3L.js} +348 -278
- package/dist/index.d.ts +187 -61
- package/dist/index.js +329 -681
- package/dist/static/assets/index-C-ba4ih0.js +10 -0
- package/dist/static/assets/index-FoBU5Kta.css +1 -0
- package/dist/static/index.html +9 -7
- package/package.json +10 -4
- package/dist/chunk-TZCE2K4D.js +0 -1447
- package/dist/static/assets/index-B5hhHHl2.css +0 -1
- package/dist/static/assets/index-LJh6IezM.js +0 -14
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENTS_MD_RESOURCE_URI,
|
|
3
|
-
|
|
4
|
-
AgentsMetaInvalidError,
|
|
3
|
+
EVENT_LEDGER_PATH,
|
|
5
4
|
LEDGER_PATH,
|
|
6
5
|
LEGACY_LEDGER_PATH,
|
|
7
|
-
|
|
8
|
-
approveHumanLock,
|
|
6
|
+
appendEventLedgerEvent,
|
|
9
7
|
contextCache,
|
|
10
|
-
|
|
8
|
+
getEventLedgerPath,
|
|
11
9
|
getLedgerPath,
|
|
12
10
|
getLegacyLedgerPath,
|
|
13
11
|
getRules,
|
|
12
|
+
invalidateRuleSyncCooldown,
|
|
13
|
+
isNodeError,
|
|
14
14
|
readAgentsMeta,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
runDoctorReport
|
|
20
|
-
} from "./chunk-TZCE2K4D.js";
|
|
15
|
+
readEventLedger,
|
|
16
|
+
runDoctorReport,
|
|
17
|
+
sha256
|
|
18
|
+
} from "./chunk-E3BHIUIW.js";
|
|
21
19
|
|
|
22
20
|
// src/http.ts
|
|
23
|
-
import { randomUUID } from "crypto";
|
|
24
|
-
import {
|
|
21
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
22
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
25
23
|
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
26
24
|
import {
|
|
27
25
|
StreamableHTTPServerTransport
|
|
@@ -29,6 +27,7 @@ import {
|
|
|
29
27
|
import chokidar2 from "chokidar";
|
|
30
28
|
|
|
31
29
|
// src/api/_error.ts
|
|
30
|
+
import { FabricError } from "@fenglimg/fabric-shared/errors";
|
|
32
31
|
function sendError(res, status, code, message, details) {
|
|
33
32
|
const payload = {
|
|
34
33
|
error: {
|
|
@@ -49,49 +48,15 @@ function sendUnknownError(res, error) {
|
|
|
49
48
|
sendError(res, normalized.status, normalized.code, normalized.message, normalized.details);
|
|
50
49
|
}
|
|
51
50
|
function normalizeApiError(error) {
|
|
52
|
-
if (error instanceof
|
|
51
|
+
if (error instanceof FabricError) {
|
|
53
52
|
return {
|
|
54
|
-
status: error.
|
|
53
|
+
status: error.httpStatus,
|
|
55
54
|
code: error.code,
|
|
56
|
-
message: error.message
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
if (error instanceof AgentsMetaFileMissingError) {
|
|
60
|
-
return {
|
|
61
|
-
status: 404,
|
|
62
|
-
code: error.code,
|
|
63
|
-
message: error.message
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
if (error instanceof AgentsMetaInvalidError) {
|
|
67
|
-
return {
|
|
68
|
-
status: 500,
|
|
69
|
-
code: error.code,
|
|
70
|
-
message: error.message
|
|
55
|
+
message: error.message,
|
|
56
|
+
details: error.details
|
|
71
57
|
};
|
|
72
58
|
}
|
|
73
59
|
if (error instanceof Error) {
|
|
74
|
-
if (error.message.startsWith("Path escapes project root:")) {
|
|
75
|
-
return {
|
|
76
|
-
status: 403,
|
|
77
|
-
code: "PATH_OUTSIDE_PROJECT_ROOT",
|
|
78
|
-
message: error.message
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
if (error.message.startsWith("Cannot find human lock entry:")) {
|
|
82
|
-
return {
|
|
83
|
-
status: 404,
|
|
84
|
-
code: "HUMAN_LOCK_ENTRY_NOT_FOUND",
|
|
85
|
-
message: error.message
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
if (error.message.startsWith("Cannot find ledger entry:")) {
|
|
89
|
-
return {
|
|
90
|
-
status: 404,
|
|
91
|
-
code: "LEDGER_ENTRY_NOT_FOUND",
|
|
92
|
-
message: error.message
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
60
|
return {
|
|
96
61
|
status: 500,
|
|
97
62
|
code: "INTERNAL_ERROR",
|
|
@@ -117,21 +82,186 @@ function registerDoctorApi(app, projectRoot) {
|
|
|
117
82
|
}
|
|
118
83
|
|
|
119
84
|
// src/api/events.ts
|
|
120
|
-
import {
|
|
121
|
-
import { open, readFile, stat } from "fs/promises";
|
|
85
|
+
import { open, readFile as readFile2, stat } from "fs/promises";
|
|
122
86
|
import { join } from "path";
|
|
123
87
|
import {
|
|
124
88
|
agentsMetaSchema,
|
|
125
89
|
fabricEventSchema,
|
|
126
90
|
forensicReportSchema,
|
|
127
|
-
|
|
128
|
-
ledgerEntrySchema
|
|
91
|
+
ledgerEntrySchema as ledgerEntrySchema2
|
|
129
92
|
} from "@fenglimg/fabric-shared";
|
|
93
|
+
import { eventLedgerEventSchema } from "@fenglimg/fabric-shared";
|
|
130
94
|
import chokidar from "chokidar";
|
|
95
|
+
|
|
96
|
+
// src/services/read-ledger.ts
|
|
97
|
+
import { randomUUID } from "crypto";
|
|
98
|
+
import { access, copyFile, readFile, rm } from "fs/promises";
|
|
99
|
+
import { ledgerEntrySchema } from "@fenglimg/fabric-shared";
|
|
100
|
+
async function resolveLedgerPaths(projectRoot) {
|
|
101
|
+
const primaryPath = getLedgerPath(projectRoot);
|
|
102
|
+
const legacyPath = getLegacyLedgerPath(projectRoot);
|
|
103
|
+
const [primaryExists, legacyExists] = await Promise.all([
|
|
104
|
+
pathExists(primaryPath),
|
|
105
|
+
pathExists(legacyPath)
|
|
106
|
+
]);
|
|
107
|
+
return {
|
|
108
|
+
primaryPath,
|
|
109
|
+
legacyPath,
|
|
110
|
+
readPath: primaryExists ? primaryPath : legacyPath,
|
|
111
|
+
usingLegacy: !primaryExists && legacyExists
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function readLedger(projectRoot, options = {}) {
|
|
115
|
+
const [legacyEntries, eventEntries] = await Promise.all([
|
|
116
|
+
readLegacyLedger(projectRoot),
|
|
117
|
+
readLedgerFromEventLedger(projectRoot)
|
|
118
|
+
]);
|
|
119
|
+
const entries = mergeLedgerEntries(legacyEntries, eventEntries);
|
|
120
|
+
return entries.filter((entry) => options.source === void 0 || entry.source === options.source).filter((entry) => options.since === void 0 || entry.ts >= options.since);
|
|
121
|
+
}
|
|
122
|
+
async function readLegacyLedger(projectRoot) {
|
|
123
|
+
const { readPath } = await resolveLedgerPaths(projectRoot);
|
|
124
|
+
let raw;
|
|
125
|
+
try {
|
|
126
|
+
raw = await readFile(readPath, "utf8");
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line, index) => parseLedgerLine(line, index)).filter((entry) => entry !== null);
|
|
134
|
+
}
|
|
135
|
+
async function appendLedgerEntry(projectRoot, entry) {
|
|
136
|
+
const nextEntry = createStoredLedgerEntry(entry);
|
|
137
|
+
for (const affectedPath of nextEntry.affected_paths) {
|
|
138
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
139
|
+
event_type: "edit_intent_checked",
|
|
140
|
+
ts: nextEntry.ts,
|
|
141
|
+
path: affectedPath,
|
|
142
|
+
compliant: true,
|
|
143
|
+
intent: nextEntry.intent,
|
|
144
|
+
ledger_entry_id: nextEntry.id,
|
|
145
|
+
ledger_source: nextEntry.source,
|
|
146
|
+
commit_sha: nextEntry.source === "ai" ? nextEntry.commit_sha : void 0,
|
|
147
|
+
parent_sha: nextEntry.source === "human" ? nextEntry.parent_sha : void 0,
|
|
148
|
+
parent_ledger_entry_id: nextEntry.source === "human" ? nextEntry.parent_ledger_entry_id : void 0,
|
|
149
|
+
diff_stat: nextEntry.source === "human" ? nextEntry.diff_stat : void 0,
|
|
150
|
+
annotation: nextEntry.source === "human" ? nextEntry.annotation : void 0,
|
|
151
|
+
matched_rule_context_ts: null,
|
|
152
|
+
window_ms: 0
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return nextEntry;
|
|
156
|
+
}
|
|
157
|
+
function createStoredLedgerEntry(entry) {
|
|
158
|
+
return ledgerEntrySchema.parse({
|
|
159
|
+
...entry,
|
|
160
|
+
id: entry.id ?? `ledger:${randomUUID()}`
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function parseLedgerLine(line, index) {
|
|
164
|
+
try {
|
|
165
|
+
const parsed = JSON.parse(line);
|
|
166
|
+
if (parsed.kind === "mcp-event") {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const result = ledgerEntrySchema.safeParse(parsed);
|
|
170
|
+
if (!result.success) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
...result.data,
|
|
175
|
+
id: result.data.id ?? createDerivedId(index, line)
|
|
176
|
+
};
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async function readLedgerFromEventLedger(projectRoot) {
|
|
182
|
+
const { events } = await readEventLedger(projectRoot);
|
|
183
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
184
|
+
for (const event of events) {
|
|
185
|
+
const entry = projectLedgerEvent(event);
|
|
186
|
+
if (entry === null) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const existing = grouped.get(entry.id);
|
|
190
|
+
if (existing === void 0) {
|
|
191
|
+
grouped.set(entry.id, entry);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
grouped.set(entry.id, {
|
|
195
|
+
...existing,
|
|
196
|
+
ts: Math.min(existing.ts, entry.ts),
|
|
197
|
+
affected_paths: dedupeStrings([...existing.affected_paths, ...entry.affected_paths])
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return Array.from(grouped.values());
|
|
201
|
+
}
|
|
202
|
+
function projectLedgerEvent(event) {
|
|
203
|
+
if (event.event_type !== "edit_intent_checked") {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
const base = {
|
|
207
|
+
id: event.ledger_entry_id,
|
|
208
|
+
ts: event.ts,
|
|
209
|
+
intent: event.intent,
|
|
210
|
+
affected_paths: [event.path]
|
|
211
|
+
};
|
|
212
|
+
if (event.ledger_source === "human") {
|
|
213
|
+
return {
|
|
214
|
+
...base,
|
|
215
|
+
source: "human",
|
|
216
|
+
parent_sha: event.parent_sha ?? event.ledger_entry_id,
|
|
217
|
+
parent_ledger_entry_id: event.parent_ledger_entry_id,
|
|
218
|
+
diff_stat: event.diff_stat ?? "event-ledger",
|
|
219
|
+
annotation: event.annotation
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
...base,
|
|
224
|
+
source: "ai",
|
|
225
|
+
commit_sha: event.commit_sha
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function mergeLedgerEntries(legacyEntries, eventEntries) {
|
|
229
|
+
const byId = /* @__PURE__ */ new Map();
|
|
230
|
+
for (const entry of [...legacyEntries, ...eventEntries]) {
|
|
231
|
+
if (!byId.has(entry.id)) {
|
|
232
|
+
byId.set(entry.id, entry);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return Array.from(byId.values()).sort((left, right) => left.ts - right.ts);
|
|
236
|
+
}
|
|
237
|
+
function dedupeStrings(values) {
|
|
238
|
+
return Array.from(new Set(values));
|
|
239
|
+
}
|
|
240
|
+
function createDerivedId(index, line) {
|
|
241
|
+
return `ledger:${index + 1}:${sha256(line).slice("sha256:".length)}`;
|
|
242
|
+
}
|
|
243
|
+
async function pathExists(path) {
|
|
244
|
+
try {
|
|
245
|
+
await access(path);
|
|
246
|
+
return true;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/api/events.ts
|
|
131
256
|
var AGENTS_META_PATH = ".fabric/agents.meta.json";
|
|
132
|
-
var HUMAN_LOCK_PATH = ".fabric/human-lock.json";
|
|
133
257
|
var FORENSIC_PATH = ".fabric/forensic.json";
|
|
134
|
-
var WATCHED_PATHS = [
|
|
258
|
+
var WATCHED_PATHS = [
|
|
259
|
+
AGENTS_META_PATH,
|
|
260
|
+
FORENSIC_PATH,
|
|
261
|
+
EVENT_LEDGER_PATH,
|
|
262
|
+
LEDGER_PATH,
|
|
263
|
+
LEGACY_LEDGER_PATH
|
|
264
|
+
];
|
|
135
265
|
var CONNECTION_LIMIT = 10;
|
|
136
266
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
137
267
|
var WATCH_DEBOUNCE_MS = 75;
|
|
@@ -173,7 +303,8 @@ function createEventsHandler(options) {
|
|
|
173
303
|
activeLedgerPath: getLedgerPath(projectRoot),
|
|
174
304
|
ledgerOffset: 0,
|
|
175
305
|
ledgerRemainder: "",
|
|
176
|
-
|
|
306
|
+
eventLedgerOffset: 0,
|
|
307
|
+
eventLedgerRemainder: "",
|
|
177
308
|
nextEventId: 1,
|
|
178
309
|
ringBuffer: new RingBuffer(RING_BUFFER_CAPACITY)
|
|
179
310
|
};
|
|
@@ -252,7 +383,8 @@ async function ensureWatcher(state, projectRoot) {
|
|
|
252
383
|
state.activeLedgerPath = ledgerState.path;
|
|
253
384
|
state.ledgerOffset = ledgerState.size;
|
|
254
385
|
state.ledgerRemainder = "";
|
|
255
|
-
state.
|
|
386
|
+
state.eventLedgerOffset = await readFileSize(getEventLedgerPath(projectRoot));
|
|
387
|
+
state.eventLedgerRemainder = "";
|
|
256
388
|
const watcher = chokidar.watch([...WATCHED_PATHS], {
|
|
257
389
|
cwd: projectRoot,
|
|
258
390
|
ignoreInitial: true,
|
|
@@ -306,13 +438,13 @@ async function readEventsForFile(state, projectRoot, relativePath) {
|
|
|
306
438
|
const event = await readMetaUpdatedEvent(projectRoot);
|
|
307
439
|
return event === null ? [] : [event];
|
|
308
440
|
}
|
|
309
|
-
if (relativePath === HUMAN_LOCK_PATH) {
|
|
310
|
-
return await readHumanLockEvents(state, projectRoot);
|
|
311
|
-
}
|
|
312
441
|
if (relativePath === FORENSIC_PATH) {
|
|
313
442
|
const event = await readDriftDetectedEvent(projectRoot);
|
|
314
443
|
return event === null ? [] : [event];
|
|
315
444
|
}
|
|
445
|
+
if (relativePath === EVENT_LEDGER_PATH) {
|
|
446
|
+
return await readEventLedgerAppendedEvents(state, projectRoot);
|
|
447
|
+
}
|
|
316
448
|
if (relativePath === LEDGER_PATH || relativePath === LEGACY_LEDGER_PATH) {
|
|
317
449
|
return await readLedgerAppendedEvents(state, projectRoot);
|
|
318
450
|
}
|
|
@@ -342,40 +474,6 @@ async function readDriftDetectedEvent(projectRoot) {
|
|
|
342
474
|
payload: parsed
|
|
343
475
|
};
|
|
344
476
|
}
|
|
345
|
-
async function readHumanLockEvents(state, projectRoot) {
|
|
346
|
-
const previousSnapshot = state.humanLockSnapshot;
|
|
347
|
-
const currentSnapshot = await readHumanLockSnapshot(projectRoot);
|
|
348
|
-
state.humanLockSnapshot = currentSnapshot;
|
|
349
|
-
const changedEntries = currentSnapshot.locked.filter((entry) => {
|
|
350
|
-
const key = getHumanLockKey(entry);
|
|
351
|
-
return previousSnapshot.hashByKey.get(key) !== entry.hash;
|
|
352
|
-
});
|
|
353
|
-
const approvedEntries = changedEntries.filter((entry) => {
|
|
354
|
-
const key = getHumanLockKey(entry);
|
|
355
|
-
return currentSnapshot.actualHashByKey.get(key) === entry.hash;
|
|
356
|
-
});
|
|
357
|
-
const driftChanged = !areSetsEqual(previousSnapshot.driftedKeys, currentSnapshot.driftedKeys);
|
|
358
|
-
const events = [];
|
|
359
|
-
if (approvedEntries.length > 0 || changedEntries.length > 0 && currentSnapshot.drifted.length === 0) {
|
|
360
|
-
events.push({
|
|
361
|
-
type: "lock:approved",
|
|
362
|
-
payload: {
|
|
363
|
-
locked: currentSnapshot.locked,
|
|
364
|
-
approved: approvedEntries.length > 0 ? approvedEntries : changedEntries
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
if (currentSnapshot.drifted.length > 0 && (driftChanged || approvedEntries.length === 0)) {
|
|
369
|
-
events.push({
|
|
370
|
-
type: "lock:drift",
|
|
371
|
-
payload: {
|
|
372
|
-
locked: currentSnapshot.locked,
|
|
373
|
-
drifted: currentSnapshot.drifted
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
return events;
|
|
378
|
-
}
|
|
379
477
|
async function readLedgerAppendedEvents(state, projectRoot) {
|
|
380
478
|
const ledgerState = await resolveLedgerWatchState(projectRoot);
|
|
381
479
|
const ledgerPath = ledgerState.path;
|
|
@@ -407,6 +505,31 @@ async function readLedgerAppendedEvents(state, projectRoot) {
|
|
|
407
505
|
await handle.close();
|
|
408
506
|
}
|
|
409
507
|
}
|
|
508
|
+
async function readEventLedgerAppendedEvents(state, projectRoot) {
|
|
509
|
+
const eventLedgerPath = getEventLedgerPath(projectRoot);
|
|
510
|
+
const nextSize = await readFileSize(eventLedgerPath);
|
|
511
|
+
if (nextSize < state.eventLedgerOffset) {
|
|
512
|
+
state.eventLedgerOffset = 0;
|
|
513
|
+
state.eventLedgerRemainder = "";
|
|
514
|
+
}
|
|
515
|
+
if (nextSize === state.eventLedgerOffset) {
|
|
516
|
+
return [];
|
|
517
|
+
}
|
|
518
|
+
const startOffset = state.eventLedgerOffset;
|
|
519
|
+
state.eventLedgerOffset = nextSize;
|
|
520
|
+
const handle = await open(eventLedgerPath, "r");
|
|
521
|
+
try {
|
|
522
|
+
const length = nextSize - startOffset;
|
|
523
|
+
const buffer = Buffer.alloc(length);
|
|
524
|
+
await handle.read(buffer, 0, length, startOffset);
|
|
525
|
+
const chunk = `${state.eventLedgerRemainder}${buffer.toString("utf8")}`;
|
|
526
|
+
const lines = chunk.split(/\r?\n/);
|
|
527
|
+
state.eventLedgerRemainder = chunk.endsWith("\n") ? "" : lines.pop() ?? "";
|
|
528
|
+
return lines.map((line) => line.trim()).filter((line) => line.length > 0).map(parseEventLedgerAppendedEvent).filter((event) => event !== null);
|
|
529
|
+
} finally {
|
|
530
|
+
await handle.close();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
410
533
|
async function resolveLedgerWatchState(projectRoot) {
|
|
411
534
|
const paths = await resolveLedgerPaths(projectRoot);
|
|
412
535
|
const path = paths.usingLegacy ? paths.legacyPath : paths.primaryPath;
|
|
@@ -419,7 +542,7 @@ function parseLedgerAppendedEvent(line) {
|
|
|
419
542
|
if (parsed.kind === "mcp-event") {
|
|
420
543
|
return null;
|
|
421
544
|
}
|
|
422
|
-
const validation =
|
|
545
|
+
const validation = ledgerEntrySchema2.safeParse(parsed);
|
|
423
546
|
if (!validation.success) {
|
|
424
547
|
return null;
|
|
425
548
|
}
|
|
@@ -431,6 +554,26 @@ function parseLedgerAppendedEvent(line) {
|
|
|
431
554
|
return null;
|
|
432
555
|
}
|
|
433
556
|
}
|
|
557
|
+
function parseEventLedgerAppendedEvent(line) {
|
|
558
|
+
try {
|
|
559
|
+
const parsed = eventLedgerEventSchema.safeParse(JSON.parse(line));
|
|
560
|
+
if (!parsed.success || parsed.data.event_type !== "edit_intent_checked") {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
type: "ledger:appended",
|
|
565
|
+
payload: {
|
|
566
|
+
id: parsed.data.ledger_entry_id,
|
|
567
|
+
ts: parsed.data.ts,
|
|
568
|
+
source: "ai",
|
|
569
|
+
intent: parsed.data.intent,
|
|
570
|
+
affected_paths: [parsed.data.path]
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
} catch {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
434
577
|
function broadcastEvent(state, event) {
|
|
435
578
|
const payload = fabricEventSchema.parse(event);
|
|
436
579
|
const eventId = state.nextEventId++;
|
|
@@ -460,68 +603,6 @@ data: ${data}
|
|
|
460
603
|
}
|
|
461
604
|
}
|
|
462
605
|
}
|
|
463
|
-
async function readHumanLockSnapshot(projectRoot) {
|
|
464
|
-
const humanLockPath = join(projectRoot, HUMAN_LOCK_PATH);
|
|
465
|
-
const raw = await readUtf8File(humanLockPath);
|
|
466
|
-
if (raw === null) {
|
|
467
|
-
return createEmptyHumanLockSnapshot();
|
|
468
|
-
}
|
|
469
|
-
const parsed = humanLockFileSchema.parse(JSON.parse(raw));
|
|
470
|
-
const locked = parsed.locked ?? [];
|
|
471
|
-
const actualHashByKey = await readActualHumanLockHashes(projectRoot, locked);
|
|
472
|
-
const drifted = locked.filter((entry) => actualHashByKey.get(getHumanLockKey(entry)) !== entry.hash);
|
|
473
|
-
return {
|
|
474
|
-
locked,
|
|
475
|
-
drifted,
|
|
476
|
-
driftedKeys: new Set(drifted.map((entry) => getHumanLockKey(entry))),
|
|
477
|
-
hashByKey: new Map(locked.map((entry) => [getHumanLockKey(entry), entry.hash])),
|
|
478
|
-
actualHashByKey
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
async function readActualHumanLockHashes(projectRoot, locked) {
|
|
482
|
-
const uniqueFiles = Array.from(new Set(locked.map((entry) => entry.file)));
|
|
483
|
-
const fileContents = await Promise.all(
|
|
484
|
-
uniqueFiles.map(async (file) => {
|
|
485
|
-
const raw = await readUtf8File(join(projectRoot, file));
|
|
486
|
-
return [file, raw];
|
|
487
|
-
})
|
|
488
|
-
);
|
|
489
|
-
const contentByFile = new Map(fileContents);
|
|
490
|
-
return new Map(
|
|
491
|
-
locked.map((entry) => {
|
|
492
|
-
const content = contentByFile.get(entry.file);
|
|
493
|
-
return [getHumanLockKey(entry), content == null ? "missing" : hashLockedContent(content, entry)];
|
|
494
|
-
})
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
function hashLockedContent(content, entry) {
|
|
498
|
-
const lines = content.split(/\r?\n/);
|
|
499
|
-
const slice = lines.slice(Math.max(entry.start_line - 1, 0), Math.max(entry.end_line, 0)).join("\n");
|
|
500
|
-
return `sha256:${createHash("sha256").update(slice).digest("hex")}`;
|
|
501
|
-
}
|
|
502
|
-
function getHumanLockKey(entry) {
|
|
503
|
-
return `${entry.file}:${entry.start_line}:${entry.end_line}`;
|
|
504
|
-
}
|
|
505
|
-
function createEmptyHumanLockSnapshot() {
|
|
506
|
-
return {
|
|
507
|
-
locked: [],
|
|
508
|
-
drifted: [],
|
|
509
|
-
driftedKeys: /* @__PURE__ */ new Set(),
|
|
510
|
-
hashByKey: /* @__PURE__ */ new Map(),
|
|
511
|
-
actualHashByKey: /* @__PURE__ */ new Map()
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
function areSetsEqual(left, right) {
|
|
515
|
-
if (left.size !== right.size) {
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
|
-
for (const value of left) {
|
|
519
|
-
if (!right.has(value)) {
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return true;
|
|
524
|
-
}
|
|
525
606
|
function readLastEventId(req) {
|
|
526
607
|
const header = req.headers["last-event-id"];
|
|
527
608
|
const headerValue = Array.isArray(header) ? header[0] : header;
|
|
@@ -542,9 +623,9 @@ function normalizePath(value) {
|
|
|
542
623
|
}
|
|
543
624
|
async function readUtf8File(path) {
|
|
544
625
|
try {
|
|
545
|
-
return await
|
|
626
|
+
return await readFile2(path, "utf8");
|
|
546
627
|
} catch (error) {
|
|
547
|
-
if (
|
|
628
|
+
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
548
629
|
return null;
|
|
549
630
|
}
|
|
550
631
|
throw error;
|
|
@@ -555,13 +636,13 @@ async function readFileSize(path) {
|
|
|
555
636
|
const fileStat = await stat(path);
|
|
556
637
|
return fileStat.size;
|
|
557
638
|
} catch (error) {
|
|
558
|
-
if (
|
|
639
|
+
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
559
640
|
return 0;
|
|
560
641
|
}
|
|
561
642
|
throw error;
|
|
562
643
|
}
|
|
563
644
|
}
|
|
564
|
-
function
|
|
645
|
+
function isNodeError2(error) {
|
|
565
646
|
return error instanceof Error;
|
|
566
647
|
}
|
|
567
648
|
|
|
@@ -572,17 +653,26 @@ import { historyStateQuerySchema } from "@fenglimg/fabric-shared";
|
|
|
572
653
|
import { execFile } from "child_process";
|
|
573
654
|
import { promisify } from "util";
|
|
574
655
|
import { agentsMetaSchema as agentsMetaSchema2 } from "@fenglimg/fabric-shared";
|
|
656
|
+
import { IOFabricError, RuleError } from "@fenglimg/fabric-shared/errors";
|
|
575
657
|
var execFileAsync = promisify(execFile);
|
|
576
658
|
var AGENTS_META_GIT_PATH = ".fabric/agents.meta.json";
|
|
577
|
-
var
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
659
|
+
var HistoryStateNotFoundError = class extends IOFabricError {
|
|
660
|
+
code = "HISTORY_STATE_NOT_FOUND";
|
|
661
|
+
httpStatus = 404;
|
|
662
|
+
constructor(message, opts) {
|
|
663
|
+
super(message, {
|
|
664
|
+
actionHint: opts?.actionHint ?? "Ensure the ledger exists and the requested timestamp or entry ID is within its range"
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
var LedgerEntryNotFoundError = class extends RuleError {
|
|
669
|
+
code = "LEDGER_ENTRY_NOT_FOUND";
|
|
670
|
+
httpStatus = 404;
|
|
671
|
+
constructor(message, opts) {
|
|
672
|
+
super(message, {
|
|
673
|
+
actionHint: opts?.actionHint ?? "Verify the ledger entry ID exists in the current ledger"
|
|
674
|
+
});
|
|
675
|
+
}
|
|
586
676
|
};
|
|
587
677
|
async function rehydrateAgentsMetaAt(projectRoot, target) {
|
|
588
678
|
const ledger = await readLedger(projectRoot);
|
|
@@ -590,10 +680,8 @@ async function rehydrateAgentsMetaAt(projectRoot, target) {
|
|
|
590
680
|
const replayedEntries = ledger.slice(0, selectedIndex + 1);
|
|
591
681
|
const selectedEntry = replayedEntries.at(-1);
|
|
592
682
|
if (selectedEntry === void 0) {
|
|
593
|
-
throw new
|
|
594
|
-
"Cannot rehydrate history state because the ledger is empty."
|
|
595
|
-
"HISTORY_STATE_NOT_FOUND",
|
|
596
|
-
404
|
|
683
|
+
throw new HistoryStateNotFoundError(
|
|
684
|
+
"Cannot rehydrate history state because the ledger is empty."
|
|
597
685
|
);
|
|
598
686
|
}
|
|
599
687
|
const commitCandidates = collectCommitCandidates(replayedEntries);
|
|
@@ -628,10 +716,8 @@ function resolveTargetIndex(ledger, target) {
|
|
|
628
716
|
if ("ledgerEntryId" in target) {
|
|
629
717
|
const index = ledger.findIndex((entry) => entry.id === target.ledgerEntryId);
|
|
630
718
|
if (index === -1) {
|
|
631
|
-
throw new
|
|
632
|
-
`Cannot find ledger entry: ${target.ledgerEntryId}
|
|
633
|
-
"LEDGER_ENTRY_NOT_FOUND",
|
|
634
|
-
404
|
|
719
|
+
throw new LedgerEntryNotFoundError(
|
|
720
|
+
`Cannot find ledger entry: ${target.ledgerEntryId}`
|
|
635
721
|
);
|
|
636
722
|
}
|
|
637
723
|
return index;
|
|
@@ -641,10 +727,8 @@ function resolveTargetIndex(ledger, target) {
|
|
|
641
727
|
return index;
|
|
642
728
|
}
|
|
643
729
|
}
|
|
644
|
-
throw new
|
|
645
|
-
`Cannot find ledger entry at or before timestamp: ${new Date(target.timestamp).toISOString()}
|
|
646
|
-
"HISTORY_STATE_NOT_FOUND",
|
|
647
|
-
404
|
|
730
|
+
throw new HistoryStateNotFoundError(
|
|
731
|
+
`Cannot find ledger entry at or before timestamp: ${new Date(target.timestamp).toISOString()}`
|
|
648
732
|
);
|
|
649
733
|
}
|
|
650
734
|
function collectCommitCandidates(entries) {
|
|
@@ -746,58 +830,6 @@ function registerHistoryApi(app, projectRoot) {
|
|
|
746
830
|
});
|
|
747
831
|
}
|
|
748
832
|
|
|
749
|
-
// src/api/human-lock.ts
|
|
750
|
-
import { humanLockApproveRequestSchema, humanLockFileParamsSchema } from "@fenglimg/fabric-shared";
|
|
751
|
-
function registerHumanLockApi(app, projectRoot) {
|
|
752
|
-
app.get("/api/human-lock", async (_req, res) => {
|
|
753
|
-
try {
|
|
754
|
-
await readAgentsMeta(projectRoot);
|
|
755
|
-
res.json(await readHumanLock(projectRoot));
|
|
756
|
-
} catch (error) {
|
|
757
|
-
sendUnknownError(res, error);
|
|
758
|
-
}
|
|
759
|
-
});
|
|
760
|
-
app.get(/^\/api\/human-lock\/(.+)$/, async (req, res) => {
|
|
761
|
-
const rawFile = typeof req.params[0] === "string" ? decodeURIComponent(req.params[0]) : "";
|
|
762
|
-
const validation = humanLockFileParamsSchema.safeParse({
|
|
763
|
-
file: rawFile
|
|
764
|
-
});
|
|
765
|
-
if (!validation.success) {
|
|
766
|
-
sendValidationError(res, "Invalid human-lock file path", validation.error.flatten());
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
try {
|
|
770
|
-
await readAgentsMeta(projectRoot);
|
|
771
|
-
const entry = await readHumanLockEntry(projectRoot, validation.data.file);
|
|
772
|
-
if (entry === null) {
|
|
773
|
-
sendError(
|
|
774
|
-
res,
|
|
775
|
-
404,
|
|
776
|
-
"HUMAN_LOCK_ENTRY_NOT_FOUND",
|
|
777
|
-
`Cannot find human lock entry: ${validation.data.file}`
|
|
778
|
-
);
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
res.json(entry);
|
|
782
|
-
} catch (error) {
|
|
783
|
-
sendUnknownError(res, error);
|
|
784
|
-
}
|
|
785
|
-
});
|
|
786
|
-
app.post("/api/human-lock/approve", async (req, res) => {
|
|
787
|
-
const validation = humanLockApproveRequestSchema.safeParse(req.body);
|
|
788
|
-
if (!validation.success) {
|
|
789
|
-
sendValidationError(res, "Invalid human-lock approval payload", validation.error.flatten());
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
try {
|
|
793
|
-
await readAgentsMeta(projectRoot);
|
|
794
|
-
res.json(await approveHumanLock(projectRoot, validation.data));
|
|
795
|
-
} catch (error) {
|
|
796
|
-
sendUnknownError(res, error);
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
|
|
801
833
|
// src/api/intent.ts
|
|
802
834
|
import { annotateIntentRequestSchema } from "@fenglimg/fabric-shared";
|
|
803
835
|
|
|
@@ -806,7 +838,7 @@ async function annotateIntent(projectRoot, input) {
|
|
|
806
838
|
const entries = await readLedger(projectRoot);
|
|
807
839
|
const parentEntry = entries.find((entry2) => entry2.id === input.ledger_entry_id);
|
|
808
840
|
if (parentEntry === void 0) {
|
|
809
|
-
throw new
|
|
841
|
+
throw new LedgerEntryNotFoundError(`Cannot find ledger entry: ${input.ledger_entry_id}`);
|
|
810
842
|
}
|
|
811
843
|
const lastEntry = entries[entries.length - 1];
|
|
812
844
|
if (lastEntry?.source === "human" && lastEntry.parent_ledger_entry_id === input.ledger_entry_id && lastEntry.annotation === input.annotation) {
|
|
@@ -1065,7 +1097,7 @@ function warnMissingDashboard(staticDir) {
|
|
|
1065
1097
|
}
|
|
1066
1098
|
|
|
1067
1099
|
// src/middleware/bearer-auth.ts
|
|
1068
|
-
import { createHash
|
|
1100
|
+
import { createHash, timingSafeEqual } from "crypto";
|
|
1069
1101
|
function createBearerAuthMiddleware(token) {
|
|
1070
1102
|
const expectedDigest = hashToken(token);
|
|
1071
1103
|
return function bearerAuthMiddleware(req, res, next) {
|
|
@@ -1098,30 +1130,25 @@ function tokensMatch(token, expectedDigest) {
|
|
|
1098
1130
|
return timingSafeEqual(hashToken(token), expectedDigest);
|
|
1099
1131
|
}
|
|
1100
1132
|
function hashToken(token) {
|
|
1101
|
-
return
|
|
1133
|
+
return createHash("sha256").update(token, "utf8").digest();
|
|
1102
1134
|
}
|
|
1103
1135
|
|
|
1104
1136
|
// src/http.ts
|
|
1105
1137
|
var DEFAULT_HOST = "127.0.0.1";
|
|
1106
1138
|
var NOTIFY_DEBOUNCE_MS = 200;
|
|
1107
1139
|
var JsonlEventStore = class {
|
|
1108
|
-
constructor(projectRoot
|
|
1140
|
+
constructor(projectRoot) {
|
|
1109
1141
|
this.projectRoot = projectRoot;
|
|
1110
|
-
this.ledgerPath = ledgerPath;
|
|
1111
1142
|
}
|
|
1112
1143
|
projectRoot;
|
|
1113
|
-
ledgerPath;
|
|
1114
1144
|
async storeEvent(streamId, message) {
|
|
1115
|
-
const eventId =
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
eventId,
|
|
1119
|
-
streamId,
|
|
1145
|
+
const eventId = randomUUID2();
|
|
1146
|
+
await appendEventLedgerEvent(this.projectRoot, {
|
|
1147
|
+
event_type: "mcp_event",
|
|
1148
|
+
mcp_event_id: eventId,
|
|
1149
|
+
stream_id: streamId,
|
|
1120
1150
|
message
|
|
1121
|
-
};
|
|
1122
|
-
await ensureParentDirectory(this.ledgerPath);
|
|
1123
|
-
await appendFile(this.ledgerPath, `${JSON.stringify(entry)}
|
|
1124
|
-
`, "utf8");
|
|
1151
|
+
});
|
|
1125
1152
|
return eventId;
|
|
1126
1153
|
}
|
|
1127
1154
|
async getStreamIdForEventId(eventId) {
|
|
@@ -1147,15 +1174,30 @@ var JsonlEventStore = class {
|
|
|
1147
1174
|
return streamId;
|
|
1148
1175
|
}
|
|
1149
1176
|
async readEvents() {
|
|
1177
|
+
const { events: eventLedgerEvents } = await readEventLedger(this.projectRoot);
|
|
1178
|
+
const projectedEvents = eventLedgerEvents.flatMap((event) => {
|
|
1179
|
+
if (event.event_type !== "mcp_event") {
|
|
1180
|
+
return [];
|
|
1181
|
+
}
|
|
1182
|
+
return [{
|
|
1183
|
+
kind: "mcp-event",
|
|
1184
|
+
eventId: event.mcp_event_id,
|
|
1185
|
+
streamId: event.stream_id,
|
|
1186
|
+
message: event.message
|
|
1187
|
+
}];
|
|
1188
|
+
});
|
|
1189
|
+
if (projectedEvents.length > 0) {
|
|
1190
|
+
return projectedEvents;
|
|
1191
|
+
}
|
|
1150
1192
|
let raw;
|
|
1151
1193
|
try {
|
|
1152
|
-
raw = await
|
|
1194
|
+
raw = await readFile3(getLedgerPath(this.projectRoot), "utf8");
|
|
1153
1195
|
} catch (error) {
|
|
1154
|
-
if (
|
|
1196
|
+
if (isNodeError3(error) && error.code === "ENOENT") {
|
|
1155
1197
|
try {
|
|
1156
|
-
raw = await
|
|
1198
|
+
raw = await readFile3(getLegacyLedgerPath(this.projectRoot), "utf8");
|
|
1157
1199
|
} catch (legacyError) {
|
|
1158
|
-
if (
|
|
1200
|
+
if (isNodeError3(legacyError) && legacyError.code === "ENOENT") {
|
|
1159
1201
|
return [];
|
|
1160
1202
|
}
|
|
1161
1203
|
throw legacyError;
|
|
@@ -1167,15 +1209,45 @@ var JsonlEventStore = class {
|
|
|
1167
1209
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => parseStoredMcpEvent(line)).filter((event) => event !== null);
|
|
1168
1210
|
}
|
|
1169
1211
|
};
|
|
1212
|
+
function handleCacheWatcherEvent(relativePath, projectRoot, sessions, timers) {
|
|
1213
|
+
const normalized = relativePath.replaceAll("\\", "/");
|
|
1214
|
+
if (normalized === ".fabric/agents.meta.json") {
|
|
1215
|
+
contextCache.invalidate("file_watch", projectRoot);
|
|
1216
|
+
clearTimeout(timers.getToolListTimer());
|
|
1217
|
+
timers.setToolListTimer(
|
|
1218
|
+
setTimeout(() => {
|
|
1219
|
+
notifyAllSessions(sessions, "tools/list_changed");
|
|
1220
|
+
}, NOTIFY_DEBOUNCE_MS)
|
|
1221
|
+
);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (normalized === ".fabric/bootstrap/README.md") {
|
|
1225
|
+
contextCache.invalidate("file_watch", projectRoot);
|
|
1226
|
+
clearTimeout(timers.getAgentsMdTimer());
|
|
1227
|
+
timers.setAgentsMdTimer(
|
|
1228
|
+
setTimeout(() => {
|
|
1229
|
+
notifyAllSessions(sessions, "resource_updated", AGENTS_MD_RESOURCE_URI);
|
|
1230
|
+
}, NOTIFY_DEBOUNCE_MS)
|
|
1231
|
+
);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
if (normalized.startsWith(".fabric/rules/") && normalized.endsWith(".md")) {
|
|
1235
|
+
contextCache.invalidate("file_watch", projectRoot);
|
|
1236
|
+
invalidateRuleSyncCooldown(projectRoot);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1170
1239
|
function createFabricHttpApp(options) {
|
|
1171
1240
|
const { projectRoot, host = DEFAULT_HOST, authToken, dashboardDistPath, dev } = options;
|
|
1172
1241
|
const app = createMcpExpressApp({ host });
|
|
1173
|
-
const
|
|
1174
|
-
const eventStore = new JsonlEventStore(projectRoot, ledgerPath);
|
|
1242
|
+
const eventStore = new JsonlEventStore(projectRoot);
|
|
1175
1243
|
const sessions = /* @__PURE__ */ new Map();
|
|
1176
1244
|
process.env.FABRIC_PROJECT_ROOT = projectRoot;
|
|
1177
1245
|
const cacheWatcher = chokidar2.watch(
|
|
1178
|
-
[
|
|
1246
|
+
[
|
|
1247
|
+
".fabric/agents.meta.json",
|
|
1248
|
+
".fabric/bootstrap/README.md",
|
|
1249
|
+
".fabric/rules/**/*.md"
|
|
1250
|
+
],
|
|
1179
1251
|
{
|
|
1180
1252
|
cwd: projectRoot,
|
|
1181
1253
|
ignoreInitial: true,
|
|
@@ -1187,22 +1259,21 @@ function createFabricHttpApp(options) {
|
|
|
1187
1259
|
);
|
|
1188
1260
|
let agentsMdNotifyTimer;
|
|
1189
1261
|
let toolListNotifyTimer;
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
});
|
|
1262
|
+
const onCacheWatcherEvent = (relativePath) => {
|
|
1263
|
+
handleCacheWatcherEvent(relativePath, projectRoot, sessions, {
|
|
1264
|
+
getAgentsMdTimer: () => agentsMdNotifyTimer,
|
|
1265
|
+
getToolListTimer: () => toolListNotifyTimer,
|
|
1266
|
+
setAgentsMdTimer: (t) => {
|
|
1267
|
+
agentsMdNotifyTimer = t;
|
|
1268
|
+
},
|
|
1269
|
+
setToolListTimer: (t) => {
|
|
1270
|
+
toolListNotifyTimer = t;
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
};
|
|
1274
|
+
cacheWatcher.on("change", onCacheWatcherEvent);
|
|
1275
|
+
cacheWatcher.on("add", onCacheWatcherEvent);
|
|
1276
|
+
cacheWatcher.on("unlink", onCacheWatcherEvent);
|
|
1206
1277
|
let disposed = false;
|
|
1207
1278
|
app.dispose = async () => {
|
|
1208
1279
|
if (disposed) {
|
|
@@ -1226,7 +1297,6 @@ function createFabricHttpApp(options) {
|
|
|
1226
1297
|
registerHistoryApi(app, projectRoot);
|
|
1227
1298
|
registerScanApi(app, projectRoot);
|
|
1228
1299
|
registerDoctorApi(app, projectRoot);
|
|
1229
|
-
registerHumanLockApi(app, projectRoot);
|
|
1230
1300
|
registerIntentApi(app, projectRoot);
|
|
1231
1301
|
app.get("/events", createEventsHandler({ projectRoot }));
|
|
1232
1302
|
app.all("/mcp", async (req, res) => {
|
|
@@ -1268,7 +1338,7 @@ async function createSession(eventStore, sessions) {
|
|
|
1268
1338
|
const { createFabricServer } = await import("./index.js");
|
|
1269
1339
|
const server = createFabricServer();
|
|
1270
1340
|
const transport = new StreamableHTTPServerTransport({
|
|
1271
|
-
sessionIdGenerator:
|
|
1341
|
+
sessionIdGenerator: randomUUID2,
|
|
1272
1342
|
enableJsonResponse: true,
|
|
1273
1343
|
eventStore,
|
|
1274
1344
|
onsessioninitialized: async (sessionId) => {
|
|
@@ -1331,10 +1401,10 @@ function writeJsonRpcError(res, status, code, message) {
|
|
|
1331
1401
|
id: null
|
|
1332
1402
|
});
|
|
1333
1403
|
}
|
|
1334
|
-
function
|
|
1404
|
+
function isNodeError3(error) {
|
|
1335
1405
|
return error instanceof Error;
|
|
1336
1406
|
}
|
|
1337
1407
|
export {
|
|
1338
1408
|
createFabricHttpApp,
|
|
1339
|
-
|
|
1409
|
+
handleCacheWatcherEvent
|
|
1340
1410
|
};
|