@akagilnc/pi-workflow-roles 0.1.3412 → 0.1.3439
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/archivist-record-entry.js +21 -12
- package/dist/archivist-record-topology.js +20 -0
- package/dist/auditor-dossier-tool.js +67 -76
- package/dist/canonical-json.js +66 -69
- package/dist/compliance-transport.js +99 -86
- package/dist/countersign-contracts.js +23 -25
- package/dist/doctor-contracts.js +169 -145
- package/dist/engine-detour-tool.js +83 -87
- package/dist/engine-detour.js +148 -183
- package/dist/evidence-child-executor.js +10 -1
- package/dist/exact-utf8.js +11 -10
- package/dist/git-object-id.js +10 -5
- package/dist/gleaner-left-contracts.js +33 -35
- package/dist/grok/production-host.js +229 -118
- package/dist/inspector-contracts.js +28 -31
- package/dist/institutional-resolution.js +101 -104
- package/dist/ledger-session-read.js +241 -0
- package/dist/merger-contracts.js +66 -56
- package/dist/notary-contracts.js +91 -100
- package/dist/open-tool-schema.js +32 -31
- package/dist/package-contracts/collector-output.js +56 -43
- package/dist/package-contracts/fixer-output.js +52 -50
- package/dist/package-contracts/fixer-packet.js +67 -64
- package/dist/package-contracts/gatekeeper-output.js +48 -56
- package/dist/package-contracts/judge-output.js +23 -20
- package/dist/package-contracts/navigator-output.js +35 -40
- package/dist/package-contracts/reviewer-output.js +94 -110
- package/dist/package-contracts/terminating-infrastructure.js +62 -90
- package/dist/package-contracts/worker-output.js +30 -11
- package/dist/package-resources/engine-material.js +58 -94
- package/dist/public-cli/config.js +420 -484
- package/dist/public-cli/main.js +1219 -524
- package/dist/public-cli/registry.js +107 -114
- package/dist/receipt-delivery-policy.js +74 -76
- package/dist/session-opening-materials.js +44 -47
- package/dist/sha256.js +5 -3
- package/dist/sitian-appender.js +204 -210
- package/dist/sitian-contracts.js +15 -19
- package/dist/sitian-facade.js +5 -10
- package/dist/sitian-reader.js +37 -46
- package/dist/stream-idle-guard.js +63 -71
- package/dist/ticket-seat-memory.js +244 -0
- package/package.json +1 -1
- package/scripts/build-package.mjs +41 -0
- package/souls/audit-law.md +4 -0
- package/souls/countersign.md +1 -1
- package/souls/gleaner-left.md +1 -1
- package/souls/inspector.md +1 -1
- package/souls/notary.md +1 -1
- package/souls/reviewer.md +1 -1
- package/src/analyst-gate-cycles-read.ts +152 -83
- package/src/analyst-ledger.ts +170 -43
- package/src/archivist-record-entry.ts +32 -11
- package/src/archivist-record-topology.ts +39 -0
- package/src/evidence-child-executor.ts +14 -1
- package/src/grok/production-host.ts +44 -9
- package/src/host-contracts.ts +18 -0
- package/src/host-transition-prior-native.ts +8 -1
- package/src/ledger-session-read.ts +56 -0
- package/src/pi/durable-principal.ts +6 -0
- package/src/public-cli/inspector-run.ts +21 -10
- package/src/public-cli/invocation.ts +4 -20
- package/src/public-cli/notary-run.ts +21 -4
- package/src/public-cli/post-admission.ts +333 -42
- package/src/public-cli/settlement.ts +266 -81
- package/src/session-opening-materials.ts +4 -2
- package/src/ticket-seat-memory.ts +454 -0
package/dist/sitian-appender.js
CHANGED
|
@@ -1,237 +1,231 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sitian Appender kernel (ADR 0065).
|
|
3
|
-
* Computes destination automatically from ledger topology without destination parameters.
|
|
4
|
-
* Owns volume open, torn-tail recovery, entry-level idempotency, and commit boundary.
|
|
5
|
-
*/
|
|
6
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
appendFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
unlinkSync,
|
|
7
|
+
writeFileSync
|
|
8
|
+
} from "node:fs";
|
|
8
9
|
import { basename, dirname, join, resolve } from "node:path";
|
|
9
10
|
import { resolveBookKeyFromGit } from "./activation-ledger-git.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
11
|
+
import {
|
|
12
|
+
activationBookDirectory,
|
|
13
|
+
ensureRealDirectoryTree,
|
|
14
|
+
errorText,
|
|
15
|
+
physicallyContainedIn,
|
|
16
|
+
resolveActivationLedgerHome,
|
|
17
|
+
resolveActivationLedgerHomeForPath
|
|
18
|
+
} from "./activation-ledger-topology.js";
|
|
19
|
+
import {
|
|
20
|
+
SitianInfrastructureError
|
|
21
|
+
} from "./sitian-contracts.js";
|
|
12
22
|
function isRecord(value) {
|
|
13
|
-
|
|
23
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
24
|
}
|
|
15
25
|
function errorCodeOf(error) {
|
|
16
|
-
|
|
26
|
+
return error.code;
|
|
17
27
|
}
|
|
18
28
|
function identityClaimPath(recordFile, identity) {
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
const digest = createHash("sha256").update(identity, "utf8").digest("hex");
|
|
30
|
+
return `${recordFile}.id-${digest}`;
|
|
21
31
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Exclusive create at `path` exactly once across processes.
|
|
24
|
-
* Reuses the package's existing `wx` / O_EXCL zero-content occupancy primitive
|
|
25
|
-
* (same shape as activation-ledger-session / archivist-record-entry).
|
|
26
|
-
*/
|
|
27
32
|
function createExclusiveFile(path, contents) {
|
|
28
|
-
|
|
33
|
+
writeFileSync(path, contents, { encoding: "utf8", flag: "wx" });
|
|
29
34
|
}
|
|
30
35
|
function sealTornTail(recordFile) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
36
|
+
if (!existsSync(recordFile)) return;
|
|
37
|
+
const buffer = readFileSync(recordFile);
|
|
38
|
+
if (buffer.length > 0 && buffer[buffer.length - 1] !== 10) {
|
|
39
|
+
appendFileSync(recordFile, "\n", "utf8");
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
function findIdentityPointer(recordFile, identity, kind, level) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Malformed lines (including substate b preserved bad lines) are ignored during self-check
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return undefined;
|
|
43
|
+
if (!existsSync(recordFile)) return void 0;
|
|
44
|
+
const text = readFileSync(recordFile, "utf8");
|
|
45
|
+
for (const line of text.split("\n")) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (!trimmed) continue;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(trimmed);
|
|
50
|
+
if (isRecord(parsed) && parsed.identity === identity) {
|
|
51
|
+
return { identity, recordFile, kind, level };
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return void 0;
|
|
57
57
|
}
|
|
58
58
|
function unlinkAbsentOk(path) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return error instanceof Error ? error : new Error(errorText(error));
|
|
67
|
-
}
|
|
59
|
+
try {
|
|
60
|
+
unlinkSync(path);
|
|
61
|
+
return void 0;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (errorCodeOf(error) === "ENOENT") return void 0;
|
|
64
|
+
return error instanceof Error ? error : new Error(errorText(error));
|
|
65
|
+
}
|
|
68
66
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Same-identity uniqueness under normal concurrent writers.
|
|
71
|
-
*
|
|
72
|
-
* Exclusive per-identity claim (`wx`) makes check+append atomic for one
|
|
73
|
-
* identity. The winner appends under the claim and always releases it on both
|
|
74
|
-
* normal and throwing exits. A loser that already sees the published row
|
|
75
|
-
* returns that pointer; a loser that hits claim EEXIST without a published row
|
|
76
|
-
* fails as SitianInfrastructureError (knownCause session). Crash residue can
|
|
77
|
-
* leave that one identity blocked — identity-scoped, never a volume lock. No
|
|
78
|
-
* wait loop, write-ahead recovery, PID reclaim, or compare-and-unlink.
|
|
79
|
-
*/
|
|
80
67
|
function appendWithIdentityClaim(recordFile, record, row) {
|
|
68
|
+
sealTornTail(recordFile);
|
|
69
|
+
const claimPath = identityClaimPath(recordFile, record.identity);
|
|
70
|
+
const existing = findIdentityPointer(
|
|
71
|
+
recordFile,
|
|
72
|
+
record.identity,
|
|
73
|
+
record.kind,
|
|
74
|
+
record.level
|
|
75
|
+
);
|
|
76
|
+
if (existing !== void 0) {
|
|
77
|
+
unlinkAbsentOk(claimPath);
|
|
78
|
+
return existing;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
createExclusiveFile(claimPath, "");
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (errorCodeOf(error) !== "EEXIST") throw error;
|
|
81
84
|
sealTornTail(recordFile);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
85
|
+
const published = findIdentityPointer(
|
|
86
|
+
recordFile,
|
|
87
|
+
record.identity,
|
|
88
|
+
record.kind,
|
|
89
|
+
record.level
|
|
90
|
+
);
|
|
91
|
+
if (published !== void 0) {
|
|
92
|
+
unlinkAbsentOk(claimPath);
|
|
93
|
+
return published;
|
|
94
|
+
}
|
|
95
|
+
throw new SitianInfrastructureError(
|
|
96
|
+
`Sitian identity claim at ${claimPath} already exists for identity ${record.identity}`,
|
|
97
|
+
{ cause: error }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
let primaryFailure;
|
|
101
|
+
let result;
|
|
102
|
+
let cleanupFailure;
|
|
103
|
+
try {
|
|
104
|
+
sealTornTail(recordFile);
|
|
105
|
+
const raced = findIdentityPointer(
|
|
106
|
+
recordFile,
|
|
107
|
+
record.identity,
|
|
108
|
+
record.kind,
|
|
109
|
+
record.level
|
|
110
|
+
);
|
|
111
|
+
if (raced !== void 0) {
|
|
112
|
+
result = raced;
|
|
113
|
+
} else {
|
|
114
|
+
appendFileSync(recordFile, row, "utf8");
|
|
115
|
+
result = {
|
|
116
|
+
identity: record.identity,
|
|
117
|
+
recordFile,
|
|
118
|
+
kind: record.kind,
|
|
119
|
+
level: record.level
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
primaryFailure = error;
|
|
124
|
+
} finally {
|
|
125
|
+
cleanupFailure = unlinkAbsentOk(claimPath);
|
|
126
|
+
}
|
|
127
|
+
if (primaryFailure !== void 0) {
|
|
128
|
+
if (primaryFailure instanceof SitianInfrastructureError) throw primaryFailure;
|
|
129
|
+
throw new SitianInfrastructureError(
|
|
130
|
+
`Sitian appender persistence failure: ${errorText(primaryFailure)}`,
|
|
131
|
+
{ cause: primaryFailure }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (cleanupFailure !== void 0) {
|
|
135
|
+
throw new SitianInfrastructureError(
|
|
136
|
+
`Sitian identity claim at ${claimPath} could not be released after append for identity ${record.identity}: ${errorText(cleanupFailure)}`,
|
|
137
|
+
{ cause: cleanupFailure }
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
137
141
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"post-seal-anomaly"
|
|
142
|
+
const S4_SUBMISSION_LEDGER_KINDS = /* @__PURE__ */ new Set([
|
|
143
|
+
"candidate",
|
|
144
|
+
"roundContext",
|
|
145
|
+
"outcome",
|
|
146
|
+
"sealed",
|
|
147
|
+
"post-seal-anomaly"
|
|
145
148
|
]);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return kind;
|
|
149
|
+
function resolveSitianVolumeCategory(kind) {
|
|
150
|
+
if (S4_SUBMISSION_LEDGER_KINDS.has(kind)) {
|
|
151
|
+
return "submission-ledger";
|
|
152
|
+
}
|
|
153
|
+
return kind;
|
|
152
154
|
}
|
|
153
155
|
function safeBookKey(cwd) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
156
|
+
try {
|
|
157
|
+
return resolveBookKeyFromGit(cwd);
|
|
158
|
+
} catch {
|
|
159
|
+
return basename(resolve(cwd)) || "default";
|
|
160
|
+
}
|
|
160
161
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
sessionDir = join(bookDir, category);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const recordFile = join(sessionDir, "records.jsonl");
|
|
191
|
-
return { sessionDir, recordFile, ledgerHome };
|
|
162
|
+
function resolveSitianRecordPathInLedger(input, ledgerHome) {
|
|
163
|
+
const cwd = input.cwd ?? process.cwd();
|
|
164
|
+
const category = resolveSitianVolumeCategory(input.kind);
|
|
165
|
+
let sessionDir;
|
|
166
|
+
if (input.sessionParent !== void 0 && input.sessionParent.length > 0 && physicallyContainedIn(ledgerHome, input.sessionParent)) {
|
|
167
|
+
sessionDir = join(dirname(input.sessionParent), category);
|
|
168
|
+
} else {
|
|
169
|
+
const bookKey = safeBookKey(cwd);
|
|
170
|
+
const bookDir = activationBookDirectory(ledgerHome, bookKey);
|
|
171
|
+
if (input.subject !== void 0) {
|
|
172
|
+
let subjectStr;
|
|
173
|
+
if (typeof input.subject === "string") {
|
|
174
|
+
subjectStr = input.subject;
|
|
175
|
+
} else if (typeof input.subject.runId === "string" && input.subject.runId.length > 0) {
|
|
176
|
+
subjectStr = input.subject.runId;
|
|
177
|
+
} else {
|
|
178
|
+
subjectStr = JSON.stringify(input.subject);
|
|
179
|
+
}
|
|
180
|
+
const digest = createHash("sha256").update(subjectStr).digest("hex").slice(0, 32);
|
|
181
|
+
sessionDir = join(bookDir, category, digest);
|
|
182
|
+
} else {
|
|
183
|
+
sessionDir = join(bookDir, category);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const recordFile = join(sessionDir, "records.jsonl");
|
|
187
|
+
return { sessionDir, recordFile, ledgerHome };
|
|
192
188
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
? resolveActivationLedgerHome(input.home)
|
|
197
|
-
: resolveActivationLedgerHomeForPath(input.sessionParent);
|
|
198
|
-
return resolveSitianRecordPathInLedger(input, ledgerHome);
|
|
189
|
+
function resolveSitianRecordPath(input) {
|
|
190
|
+
const ledgerHome = input.home !== void 0 && input.home.length > 0 ? resolveActivationLedgerHome(input.home) : resolveActivationLedgerHomeForPath(input.sessionParent);
|
|
191
|
+
return resolveSitianRecordPathInLedger(input, ledgerHome);
|
|
199
192
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
if (error instanceof SitianInfrastructureError)
|
|
234
|
-
throw error;
|
|
235
|
-
throw new SitianInfrastructureError(`Sitian appender persistence failure: ${errorText(error)}`, { cause: error });
|
|
236
|
-
}
|
|
193
|
+
function appendSitianRecord(input) {
|
|
194
|
+
try {
|
|
195
|
+
const { sessionDir, recordFile, ledgerHome } = resolveSitianRecordPath(input);
|
|
196
|
+
ensureRealDirectoryTree(ledgerHome, sessionDir);
|
|
197
|
+
const identity = input.identity ?? randomUUID();
|
|
198
|
+
const timestamp = input.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
199
|
+
const host = input.host ?? "pi";
|
|
200
|
+
const record = {
|
|
201
|
+
level: input.level,
|
|
202
|
+
kind: input.kind,
|
|
203
|
+
identity,
|
|
204
|
+
...input.subject === void 0 ? {} : { subject: input.subject },
|
|
205
|
+
...input.sessionParent === void 0 ? {} : { sessionParent: input.sessionParent },
|
|
206
|
+
...input.priorEventId === void 0 ? {} : { priorEventId: input.priorEventId },
|
|
207
|
+
timestamp,
|
|
208
|
+
host,
|
|
209
|
+
...input.source === void 0 ? {} : { source: input.source },
|
|
210
|
+
...input.payload === void 0 ? {} : { payload: input.payload },
|
|
211
|
+
...input.raw === void 0 ? {} : { raw: input.raw },
|
|
212
|
+
...input.usage === void 0 ? {} : { usage: input.usage }
|
|
213
|
+
};
|
|
214
|
+
const row = `${JSON.stringify(record)}
|
|
215
|
+
`;
|
|
216
|
+
return appendWithIdentityClaim(recordFile, record, row);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
if (error instanceof SitianInfrastructureError) throw error;
|
|
219
|
+
throw new SitianInfrastructureError(
|
|
220
|
+
`Sitian appender persistence failure: ${errorText(error)}`,
|
|
221
|
+
{ cause: error }
|
|
222
|
+
);
|
|
223
|
+
}
|
|
237
224
|
}
|
|
225
|
+
export {
|
|
226
|
+
S4_SUBMISSION_LEDGER_KINDS,
|
|
227
|
+
appendSitianRecord,
|
|
228
|
+
resolveSitianRecordPath,
|
|
229
|
+
resolveSitianRecordPathInLedger,
|
|
230
|
+
resolveSitianVolumeCategory
|
|
231
|
+
};
|
package/dist/sitian-contracts.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/** Lift direct-cause fs errno onto a wrap error (no chain walk). */
|
|
6
|
-
export function attachDirectErrnoCode(error, cause) {
|
|
7
|
-
if (cause === null || typeof cause !== "object" || !("code" in cause))
|
|
8
|
-
return;
|
|
9
|
-
const code = cause.code;
|
|
10
|
-
if (typeof code === "string")
|
|
11
|
-
error.code = code;
|
|
1
|
+
function attachDirectErrnoCode(error, cause) {
|
|
2
|
+
if (cause === null || typeof cause !== "object" || !("code" in cause)) return;
|
|
3
|
+
const code = cause.code;
|
|
4
|
+
if (typeof code === "string") error.code = code;
|
|
12
5
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
6
|
+
class SitianInfrastructureError extends Error {
|
|
7
|
+
knownCause = "session";
|
|
8
|
+
constructor(message, options) {
|
|
9
|
+
super(message, options);
|
|
10
|
+
this.name = "SitianInfrastructureError";
|
|
11
|
+
attachDirectErrnoCode(this, options?.cause);
|
|
12
|
+
}
|
|
21
13
|
}
|
|
14
|
+
export {
|
|
15
|
+
SitianInfrastructureError,
|
|
16
|
+
attachDirectErrnoCode
|
|
17
|
+
};
|
package/dist/sitian-facade.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sitian Facade (司天门面) — Sole entrypoint for record reporting across all producers.
|
|
3
|
-
* ADR 0065 single record entry, #520 r8.
|
|
4
|
-
*/
|
|
5
1
|
import { appendSitianRecord } from "./sitian-appender.js";
|
|
6
2
|
export * from "./sitian-contracts.js";
|
|
7
3
|
export * from "./sitian-appender.js";
|
|
8
4
|
export * from "./sitian-reader.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Returns typed RecordPointer with durable record identity and readable file location.
|
|
12
|
-
*/
|
|
13
|
-
export function sitianReport(input) {
|
|
14
|
-
return appendSitianRecord(input);
|
|
5
|
+
function sitianReport(input) {
|
|
6
|
+
return appendSitianRecord(input);
|
|
15
7
|
}
|
|
8
|
+
export {
|
|
9
|
+
sitianReport
|
|
10
|
+
};
|
package/dist/sitian-reader.js
CHANGED
|
@@ -1,52 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sitian Reader kernel (ADR 0068 / #520 r8).
|
|
3
|
-
* Sole authoritative read path for canonical Sitian records.
|
|
4
|
-
* Traversal contract:
|
|
5
|
-
* - Malformed lines are exposed as typed malformed diagnostics and traversal continues.
|
|
6
|
-
* - Zero whitewashing, zero deduplication.
|
|
7
|
-
* - Canonical rows after malformed lines are always reachable.
|
|
8
|
-
*/
|
|
9
1
|
import { existsSync } from "node:fs";
|
|
10
2
|
import { readFile } from "node:fs/promises";
|
|
11
3
|
function isRecord(value) {
|
|
12
|
-
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
5
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
async function readSitianRecords(recordFile) {
|
|
7
|
+
if (!existsSync(recordFile)) {
|
|
8
|
+
return { records: [], diagnostics: [] };
|
|
9
|
+
}
|
|
10
|
+
const text = await readFile(recordFile, "utf8");
|
|
11
|
+
const lines = text.split("\n");
|
|
12
|
+
const records = [];
|
|
13
|
+
const diagnostics = [];
|
|
14
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
15
|
+
const line = lines[index];
|
|
16
|
+
if (!line.trim()) continue;
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(line);
|
|
19
|
+
if (isRecord(parsed)) {
|
|
20
|
+
records.push(parsed);
|
|
21
|
+
} else {
|
|
22
|
+
const typeDesc = parsed === null ? "null" : Array.isArray(parsed) ? "array" : typeof parsed;
|
|
23
|
+
diagnostics.push({
|
|
24
|
+
kind: "malformed",
|
|
25
|
+
line: index + 1,
|
|
26
|
+
raw: line,
|
|
27
|
+
error: `expected JSON object, got ${typeDesc}`
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
diagnostics.push({
|
|
32
|
+
kind: "malformed",
|
|
33
|
+
line: index + 1,
|
|
34
|
+
raw: line,
|
|
35
|
+
error: error instanceof Error ? error.message : String(error)
|
|
36
|
+
});
|
|
18
37
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const records = [];
|
|
22
|
-
const diagnostics = [];
|
|
23
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
24
|
-
const line = lines[index];
|
|
25
|
-
if (!line.trim())
|
|
26
|
-
continue;
|
|
27
|
-
try {
|
|
28
|
-
const parsed = JSON.parse(line);
|
|
29
|
-
if (isRecord(parsed)) {
|
|
30
|
-
records.push(parsed);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
const typeDesc = parsed === null ? "null" : Array.isArray(parsed) ? "array" : typeof parsed;
|
|
34
|
-
diagnostics.push({
|
|
35
|
-
kind: "malformed",
|
|
36
|
-
line: index + 1,
|
|
37
|
-
raw: line,
|
|
38
|
-
error: `expected JSON object, got ${typeDesc}`,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
diagnostics.push({
|
|
44
|
-
kind: "malformed",
|
|
45
|
-
line: index + 1,
|
|
46
|
-
raw: line,
|
|
47
|
-
error: error instanceof Error ? error.message : String(error),
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return { records, diagnostics };
|
|
38
|
+
}
|
|
39
|
+
return { records, diagnostics };
|
|
52
40
|
}
|
|
41
|
+
export {
|
|
42
|
+
readSitianRecords
|
|
43
|
+
};
|