@fenglimg/fabric-server 1.7.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-PTFSYO4Y.js → chunk-E3BHIUIW.js} +986 -166
- package/dist/{http-6LFZLHCN.js → http-MEFXOG3L.js} +82 -83
- package/dist/index.d.ts +147 -3
- package/dist/index.js +329 -140
- package/dist/static/assets/index-C-ba4ih0.js +10 -0
- package/dist/static/index.html +1 -1
- package/package.json +10 -4
- package/dist/static/assets/index-DEc8gkD_.js +0 -10
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENTS_MD_RESOURCE_URI,
|
|
3
|
-
AgentsMetaFileMissingError,
|
|
4
|
-
AgentsMetaInvalidError,
|
|
5
3
|
EVENT_LEDGER_PATH,
|
|
6
4
|
LEDGER_PATH,
|
|
7
5
|
LEGACY_LEDGER_PATH,
|
|
@@ -11,12 +9,13 @@ import {
|
|
|
11
9
|
getLedgerPath,
|
|
12
10
|
getLegacyLedgerPath,
|
|
13
11
|
getRules,
|
|
12
|
+
invalidateRuleSyncCooldown,
|
|
14
13
|
isNodeError,
|
|
15
14
|
readAgentsMeta,
|
|
16
15
|
readEventLedger,
|
|
17
16
|
runDoctorReport,
|
|
18
17
|
sha256
|
|
19
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-E3BHIUIW.js";
|
|
20
19
|
|
|
21
20
|
// src/http.ts
|
|
22
21
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
import chokidar2 from "chokidar";
|
|
29
28
|
|
|
30
29
|
// src/api/_error.ts
|
|
30
|
+
import { FabricError } from "@fenglimg/fabric-shared/errors";
|
|
31
31
|
function sendError(res, status, code, message, details) {
|
|
32
32
|
const payload = {
|
|
33
33
|
error: {
|
|
@@ -48,49 +48,15 @@ function sendUnknownError(res, error) {
|
|
|
48
48
|
sendError(res, normalized.status, normalized.code, normalized.message, normalized.details);
|
|
49
49
|
}
|
|
50
50
|
function normalizeApiError(error) {
|
|
51
|
-
if (error instanceof
|
|
51
|
+
if (error instanceof FabricError) {
|
|
52
52
|
return {
|
|
53
|
-
status: error.
|
|
53
|
+
status: error.httpStatus,
|
|
54
54
|
code: error.code,
|
|
55
|
-
message: error.message
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
if (error instanceof AgentsMetaFileMissingError) {
|
|
59
|
-
return {
|
|
60
|
-
status: 404,
|
|
61
|
-
code: error.code,
|
|
62
|
-
message: error.message
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
if (error instanceof AgentsMetaInvalidError) {
|
|
66
|
-
return {
|
|
67
|
-
status: 500,
|
|
68
|
-
code: error.code,
|
|
69
|
-
message: error.message
|
|
55
|
+
message: error.message,
|
|
56
|
+
details: error.details
|
|
70
57
|
};
|
|
71
58
|
}
|
|
72
59
|
if (error instanceof Error) {
|
|
73
|
-
if (error.message.startsWith("Path escapes project root:")) {
|
|
74
|
-
return {
|
|
75
|
-
status: 403,
|
|
76
|
-
code: "PATH_OUTSIDE_PROJECT_ROOT",
|
|
77
|
-
message: error.message
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
if (error.message.startsWith("Cannot find human lock entry:")) {
|
|
81
|
-
return {
|
|
82
|
-
status: 404,
|
|
83
|
-
code: "HUMAN_LOCK_ENTRY_NOT_FOUND",
|
|
84
|
-
message: error.message
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (error.message.startsWith("Cannot find ledger entry:")) {
|
|
88
|
-
return {
|
|
89
|
-
status: 404,
|
|
90
|
-
code: "LEDGER_ENTRY_NOT_FOUND",
|
|
91
|
-
message: error.message
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
60
|
return {
|
|
95
61
|
status: 500,
|
|
96
62
|
code: "INTERNAL_ERROR",
|
|
@@ -213,7 +179,7 @@ function parseLedgerLine(line, index) {
|
|
|
213
179
|
}
|
|
214
180
|
}
|
|
215
181
|
async function readLedgerFromEventLedger(projectRoot) {
|
|
216
|
-
const events = await readEventLedger(projectRoot);
|
|
182
|
+
const { events } = await readEventLedger(projectRoot);
|
|
217
183
|
const grouped = /* @__PURE__ */ new Map();
|
|
218
184
|
for (const event of events) {
|
|
219
185
|
const entry = projectLedgerEvent(event);
|
|
@@ -687,17 +653,26 @@ import { historyStateQuerySchema } from "@fenglimg/fabric-shared";
|
|
|
687
653
|
import { execFile } from "child_process";
|
|
688
654
|
import { promisify } from "util";
|
|
689
655
|
import { agentsMetaSchema as agentsMetaSchema2 } from "@fenglimg/fabric-shared";
|
|
656
|
+
import { IOFabricError, RuleError } from "@fenglimg/fabric-shared/errors";
|
|
690
657
|
var execFileAsync = promisify(execFile);
|
|
691
658
|
var AGENTS_META_GIT_PATH = ".fabric/agents.meta.json";
|
|
692
|
-
var
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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
|
+
}
|
|
701
676
|
};
|
|
702
677
|
async function rehydrateAgentsMetaAt(projectRoot, target) {
|
|
703
678
|
const ledger = await readLedger(projectRoot);
|
|
@@ -705,10 +680,8 @@ async function rehydrateAgentsMetaAt(projectRoot, target) {
|
|
|
705
680
|
const replayedEntries = ledger.slice(0, selectedIndex + 1);
|
|
706
681
|
const selectedEntry = replayedEntries.at(-1);
|
|
707
682
|
if (selectedEntry === void 0) {
|
|
708
|
-
throw new
|
|
709
|
-
"Cannot rehydrate history state because the ledger is empty."
|
|
710
|
-
"HISTORY_STATE_NOT_FOUND",
|
|
711
|
-
404
|
|
683
|
+
throw new HistoryStateNotFoundError(
|
|
684
|
+
"Cannot rehydrate history state because the ledger is empty."
|
|
712
685
|
);
|
|
713
686
|
}
|
|
714
687
|
const commitCandidates = collectCommitCandidates(replayedEntries);
|
|
@@ -743,10 +716,8 @@ function resolveTargetIndex(ledger, target) {
|
|
|
743
716
|
if ("ledgerEntryId" in target) {
|
|
744
717
|
const index = ledger.findIndex((entry) => entry.id === target.ledgerEntryId);
|
|
745
718
|
if (index === -1) {
|
|
746
|
-
throw new
|
|
747
|
-
`Cannot find ledger entry: ${target.ledgerEntryId}
|
|
748
|
-
"LEDGER_ENTRY_NOT_FOUND",
|
|
749
|
-
404
|
|
719
|
+
throw new LedgerEntryNotFoundError(
|
|
720
|
+
`Cannot find ledger entry: ${target.ledgerEntryId}`
|
|
750
721
|
);
|
|
751
722
|
}
|
|
752
723
|
return index;
|
|
@@ -756,10 +727,8 @@ function resolveTargetIndex(ledger, target) {
|
|
|
756
727
|
return index;
|
|
757
728
|
}
|
|
758
729
|
}
|
|
759
|
-
throw new
|
|
760
|
-
`Cannot find ledger entry at or before timestamp: ${new Date(target.timestamp).toISOString()}
|
|
761
|
-
"HISTORY_STATE_NOT_FOUND",
|
|
762
|
-
404
|
|
730
|
+
throw new HistoryStateNotFoundError(
|
|
731
|
+
`Cannot find ledger entry at or before timestamp: ${new Date(target.timestamp).toISOString()}`
|
|
763
732
|
);
|
|
764
733
|
}
|
|
765
734
|
function collectCommitCandidates(entries) {
|
|
@@ -869,7 +838,7 @@ async function annotateIntent(projectRoot, input) {
|
|
|
869
838
|
const entries = await readLedger(projectRoot);
|
|
870
839
|
const parentEntry = entries.find((entry2) => entry2.id === input.ledger_entry_id);
|
|
871
840
|
if (parentEntry === void 0) {
|
|
872
|
-
throw new
|
|
841
|
+
throw new LedgerEntryNotFoundError(`Cannot find ledger entry: ${input.ledger_entry_id}`);
|
|
873
842
|
}
|
|
874
843
|
const lastEntry = entries[entries.length - 1];
|
|
875
844
|
if (lastEntry?.source === "human" && lastEntry.parent_ledger_entry_id === input.ledger_entry_id && lastEntry.annotation === input.annotation) {
|
|
@@ -1205,7 +1174,7 @@ var JsonlEventStore = class {
|
|
|
1205
1174
|
return streamId;
|
|
1206
1175
|
}
|
|
1207
1176
|
async readEvents() {
|
|
1208
|
-
const eventLedgerEvents = await readEventLedger(this.projectRoot);
|
|
1177
|
+
const { events: eventLedgerEvents } = await readEventLedger(this.projectRoot);
|
|
1209
1178
|
const projectedEvents = eventLedgerEvents.flatMap((event) => {
|
|
1210
1179
|
if (event.event_type !== "mcp_event") {
|
|
1211
1180
|
return [];
|
|
@@ -1240,6 +1209,33 @@ var JsonlEventStore = class {
|
|
|
1240
1209
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => parseStoredMcpEvent(line)).filter((event) => event !== null);
|
|
1241
1210
|
}
|
|
1242
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
|
+
}
|
|
1243
1239
|
function createFabricHttpApp(options) {
|
|
1244
1240
|
const { projectRoot, host = DEFAULT_HOST, authToken, dashboardDistPath, dev } = options;
|
|
1245
1241
|
const app = createMcpExpressApp({ host });
|
|
@@ -1247,7 +1243,11 @@ function createFabricHttpApp(options) {
|
|
|
1247
1243
|
const sessions = /* @__PURE__ */ new Map();
|
|
1248
1244
|
process.env.FABRIC_PROJECT_ROOT = projectRoot;
|
|
1249
1245
|
const cacheWatcher = chokidar2.watch(
|
|
1250
|
-
[
|
|
1246
|
+
[
|
|
1247
|
+
".fabric/agents.meta.json",
|
|
1248
|
+
".fabric/bootstrap/README.md",
|
|
1249
|
+
".fabric/rules/**/*.md"
|
|
1250
|
+
],
|
|
1251
1251
|
{
|
|
1252
1252
|
cwd: projectRoot,
|
|
1253
1253
|
ignoreInitial: true,
|
|
@@ -1259,22 +1259,21 @@ function createFabricHttpApp(options) {
|
|
|
1259
1259
|
);
|
|
1260
1260
|
let agentsMdNotifyTimer;
|
|
1261
1261
|
let toolListNotifyTimer;
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
});
|
|
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);
|
|
1278
1277
|
let disposed = false;
|
|
1279
1278
|
app.dispose = async () => {
|
|
1280
1279
|
if (disposed) {
|
|
@@ -1407,5 +1406,5 @@ function isNodeError3(error) {
|
|
|
1407
1406
|
}
|
|
1408
1407
|
export {
|
|
1409
1408
|
createFabricHttpApp,
|
|
1410
|
-
|
|
1409
|
+
handleCacheWatcherEvent
|
|
1411
1410
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { Server } from 'node:http';
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { AgentsMeta, RuleTestIndex, AgentsLayer, AgentsTopologyType } from '@fenglimg/fabric-shared';
|
|
4
|
+
import { IOFabricError } from '@fenglimg/fabric-shared/errors';
|
|
5
|
+
|
|
6
|
+
interface InFlightTracker {
|
|
7
|
+
enter(requestId: string): void;
|
|
8
|
+
exit(requestId: string): void;
|
|
9
|
+
drain(deadlineMs: number): Promise<{
|
|
10
|
+
drained: number;
|
|
11
|
+
timed_out: number;
|
|
12
|
+
}>;
|
|
13
|
+
size(): number;
|
|
14
|
+
}
|
|
15
|
+
declare function createInFlightTracker(): InFlightTracker;
|
|
4
16
|
|
|
5
17
|
declare const LEDGER_PATH = ".fabric/.intent-ledger.jsonl";
|
|
6
18
|
declare const LEGACY_LEDGER_PATH = ".intent-ledger.jsonl";
|
|
@@ -10,7 +22,7 @@ declare function getLegacyLedgerPath(projectRoot: string): string;
|
|
|
10
22
|
declare function getEventLedgerPath(projectRoot: string): string;
|
|
11
23
|
|
|
12
24
|
type DoctorStatus = "ok" | "warn" | "error";
|
|
13
|
-
type DoctorIssueKind = "fixable_error" | "manual_error" | "warning";
|
|
25
|
+
type DoctorIssueKind = "fixable_error" | "manual_error" | "warning" | "info";
|
|
14
26
|
type DoctorCheck = {
|
|
15
27
|
name: string;
|
|
16
28
|
status: DoctorStatus;
|
|
@@ -18,6 +30,7 @@ type DoctorCheck = {
|
|
|
18
30
|
kind?: DoctorIssueKind;
|
|
19
31
|
code?: string;
|
|
20
32
|
fixable?: boolean;
|
|
33
|
+
actionHint?: string;
|
|
21
34
|
};
|
|
22
35
|
type DoctorIssue = {
|
|
23
36
|
code: string;
|
|
@@ -43,6 +56,7 @@ type DoctorSummary = {
|
|
|
43
56
|
fixableErrorCount: number;
|
|
44
57
|
manualErrorCount: number;
|
|
45
58
|
warningCount: number;
|
|
59
|
+
infoCount: number;
|
|
46
60
|
targetFiles: Record<string, boolean>;
|
|
47
61
|
};
|
|
48
62
|
type DoctorReport = {
|
|
@@ -51,6 +65,7 @@ type DoctorReport = {
|
|
|
51
65
|
fixable_errors: DoctorIssue[];
|
|
52
66
|
manual_errors: DoctorIssue[];
|
|
53
67
|
warnings: DoctorIssue[];
|
|
68
|
+
infos: DoctorIssue[];
|
|
54
69
|
summary: DoctorSummary;
|
|
55
70
|
};
|
|
56
71
|
type DoctorFixReport = {
|
|
@@ -88,8 +103,137 @@ declare function stableStringify(value: unknown): string;
|
|
|
88
103
|
/** MCP resource URI for the project's bootstrap README (L0 rules) file. */
|
|
89
104
|
declare const AGENTS_MD_RESOURCE_URI = "fabric://bootstrap-readme";
|
|
90
105
|
|
|
91
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Synchronously fsync the event ledger file to ensure OS page-cache buffers are
|
|
108
|
+
* flushed to durable storage. Must be called AFTER in-flight drain but BEFORE
|
|
109
|
+
* server.close() — Gemini G1 ordering requirement.
|
|
110
|
+
*
|
|
111
|
+
* Uses sync APIs intentionally: we are inside a signal handler and need
|
|
112
|
+
* guaranteed completion before process.exit().
|
|
113
|
+
*/
|
|
114
|
+
declare function flushAndSyncEventLedger(projectRoot: string): void;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* rule-sync.ts — Rule-sync orchestrator framework (R28, TASK-011)
|
|
118
|
+
*
|
|
119
|
+
* Public surface: ensureRulesFresh, reconcileRules + exported types.
|
|
120
|
+
* Internal helpers are co-located in this file.
|
|
121
|
+
* Does NOT wire any consumers (MCP tools, doctor, watchers).
|
|
122
|
+
*
|
|
123
|
+
* Distinction between the two public entry points:
|
|
124
|
+
*
|
|
125
|
+
* - `ensureRulesFresh`: detects drift, emits ledger events, invalidates cache.
|
|
126
|
+
* Does NOT rewrite agents.meta.json. Optimised for hot-path consumers (MCP tools).
|
|
127
|
+
*
|
|
128
|
+
* - `reconcileRules`: full scan + rewrites agents.meta.json (via rule-meta-builder)
|
|
129
|
+
* + emits ledger events. Used by startup (TASK-022) and doctor repair (TASK-023).
|
|
130
|
+
*/
|
|
131
|
+
interface RuleSyncOptions {
|
|
132
|
+
mode?: "incremental" | "full";
|
|
133
|
+
/** When true, invalid frontmatter throws RuleValidationError (default: false — collect as warning). */
|
|
134
|
+
throwOnInvalidFrontmatter?: boolean;
|
|
135
|
+
}
|
|
136
|
+
interface StructuredWarning {
|
|
137
|
+
code: string;
|
|
138
|
+
file: string;
|
|
139
|
+
line?: number;
|
|
140
|
+
action_hint: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Granular ledger event shape for rule-sync operations.
|
|
144
|
+
* These are returned in RuleSyncReport and also appended to the event ledger
|
|
145
|
+
* using the nearest available ledger event type (rule_drift_detected /
|
|
146
|
+
* baseline_synced). The shape below is what callers receive in `.events`.
|
|
147
|
+
*/
|
|
148
|
+
interface RuleSyncLedgerEvent {
|
|
149
|
+
type: "rule_content_changed" | "rule_added" | "rule_removed";
|
|
150
|
+
stable_id: string;
|
|
151
|
+
path: string;
|
|
152
|
+
prev_hash: string | null;
|
|
153
|
+
new_hash: string | null;
|
|
154
|
+
changed_fields: string[];
|
|
155
|
+
source: "ensureRulesFresh" | "reconcileRules";
|
|
156
|
+
}
|
|
157
|
+
/** Alias so the public API says LedgerEvent (as documented). */
|
|
158
|
+
type LedgerEvent = RuleSyncLedgerEvent;
|
|
159
|
+
interface RuleSyncReport {
|
|
160
|
+
status: "fresh" | "reconciled" | "errors";
|
|
161
|
+
events: LedgerEvent[];
|
|
162
|
+
warnings: StructuredWarning[];
|
|
163
|
+
reconciled_files?: string[];
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Detects drift between disk and agents.meta.json, emits ledger events, and
|
|
167
|
+
* invalidates the cache. Does NOT rewrite agents.meta.json. Optimised for
|
|
168
|
+
* hot-path consumers (MCP tools).
|
|
169
|
+
*/
|
|
170
|
+
declare function ensureRulesFresh(projectRoot: string, opts?: RuleSyncOptions): Promise<RuleSyncReport>;
|
|
171
|
+
interface ReconcileRulesOptions {
|
|
172
|
+
/** Identifies who triggered the reconcile; controls which summary ledger event is written. */
|
|
173
|
+
trigger?: "startup" | "doctor" | "manual";
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Full scan + rewrites agents.meta.json with ground-truth disk state + emits
|
|
177
|
+
* ledger events. Used by startup (TASK-022) and doctor repair (TASK-023).
|
|
178
|
+
* Returns reconciled_files listing all paths whose meta was updated.
|
|
179
|
+
*
|
|
180
|
+
* When `opts.trigger` is `'startup'`, a `meta_reconciled_on_startup` summary
|
|
181
|
+
* ledger event is appended after per-file drift events. Other trigger values
|
|
182
|
+
* append a `meta_reconciled` event. Omitting the trigger skips the summary.
|
|
183
|
+
*/
|
|
184
|
+
declare function reconcileRules(projectRoot: string, opts?: ReconcileRulesOptions): Promise<RuleSyncReport>;
|
|
185
|
+
|
|
186
|
+
declare class ServeLockHeldError extends IOFabricError {
|
|
187
|
+
readonly code = "SERVE_LOCK_HELD";
|
|
188
|
+
readonly httpStatus = 423;
|
|
189
|
+
}
|
|
190
|
+
interface LockState {
|
|
191
|
+
pid: number;
|
|
192
|
+
acquiredAt: number;
|
|
193
|
+
host?: string;
|
|
194
|
+
}
|
|
195
|
+
interface AcquireOptions {
|
|
196
|
+
force?: boolean;
|
|
197
|
+
}
|
|
198
|
+
declare function acquireLock(projectRoot: string, opts?: AcquireOptions): void;
|
|
199
|
+
declare function releaseLock(projectRoot: string): void;
|
|
200
|
+
declare function readLockState(projectRoot: string): LockState | null;
|
|
201
|
+
declare function checkLockOrThrow(projectRoot: string, opts?: AcquireOptions): void;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Returns an info-level startup message when CLAUDE.md or AGENTS.md exist at
|
|
205
|
+
* the project root, or null when neither is present.
|
|
206
|
+
*
|
|
207
|
+
* Extracted as a pure helper so unit tests can exercise it without spawning
|
|
208
|
+
* a full server (TASK-034).
|
|
209
|
+
*/
|
|
210
|
+
declare function formatPreexistingRootMessage(projectRoot: string): string | null;
|
|
211
|
+
|
|
212
|
+
declare function createFabricServer(tracker?: InFlightTracker): McpServer;
|
|
92
213
|
declare function startStdioServer(): Promise<void>;
|
|
214
|
+
/**
|
|
215
|
+
* Dependencies for the shutdown handler factory. Tests inject `exit` to assert
|
|
216
|
+
* exit-code behavior without terminating the test process.
|
|
217
|
+
*/
|
|
218
|
+
interface ShutdownHandlerDeps {
|
|
219
|
+
signal: NodeJS.Signals;
|
|
220
|
+
tracker: InFlightTracker;
|
|
221
|
+
projectRoot: string;
|
|
222
|
+
closeServer: () => Promise<void>;
|
|
223
|
+
/** Override for tests; defaults to `process.exit`. */
|
|
224
|
+
exit?: (code: number) => never;
|
|
225
|
+
/** Override for tests; defaults to 5000ms (Gemini G1). */
|
|
226
|
+
drainDeadlineMs?: number;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Builds a same-signal shutdown handler implementing server.md I1:
|
|
230
|
+
* - First invocation: drain in-flight (5s) → fsync ledger → close server → exit(0)
|
|
231
|
+
* - Second invocation of the same signal (while first is in flight): exit(1)
|
|
232
|
+
*
|
|
233
|
+
* Each call to this factory returns an independent handler with its own
|
|
234
|
+
* `invoked` flag, so per-signal dedup is isolated.
|
|
235
|
+
*/
|
|
236
|
+
declare function createShutdownHandler(deps: ShutdownHandlerDeps): () => void;
|
|
93
237
|
declare function startHttpServer(options: {
|
|
94
238
|
port: number;
|
|
95
239
|
projectRoot: string;
|
|
@@ -99,4 +243,4 @@ declare function startHttpServer(options: {
|
|
|
99
243
|
dev?: boolean;
|
|
100
244
|
}): Promise<Server>;
|
|
101
245
|
|
|
102
|
-
export { AGENTS_MD_RESOURCE_URI, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, LEDGER_PATH, LEGACY_LEDGER_PATH, type RuleMetaBuildResult, type RuleMetaBuildSource, type WriteRuleMetaOptions, buildRuleMeta, computeRuleTestIndex, computeRulesBasedAgentsMeta, createFabricServer, deriveRuleMetaLayer, deriveRuleMetaTopologyType, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, isSameRuleTestIndex, runDoctorFix, runDoctorReport, stableStringify, startHttpServer, startStdioServer, writeRuleMeta };
|
|
246
|
+
export { AGENTS_MD_RESOURCE_URI, type AcquireOptions, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type InFlightTracker, LEDGER_PATH, LEGACY_LEDGER_PATH, type LedgerEvent, type LockState, type ReconcileRulesOptions, type RuleMetaBuildResult, type RuleMetaBuildSource, type RuleSyncLedgerEvent, type RuleSyncOptions, type RuleSyncReport, ServeLockHeldError, type ShutdownHandlerDeps, type StructuredWarning, type WriteRuleMetaOptions, acquireLock, buildRuleMeta, checkLockOrThrow, computeRuleTestIndex, computeRulesBasedAgentsMeta, createFabricServer, createInFlightTracker, createShutdownHandler, deriveRuleMetaLayer, deriveRuleMetaTopologyType, ensureRulesFresh, flushAndSyncEventLedger, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, isSameRuleTestIndex, readLockState, reconcileRules, releaseLock, runDoctorFix, runDoctorReport, stableStringify, startHttpServer, startStdioServer, writeRuleMeta };
|