@sublang/slc 0.1.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.
- package/LICENSE +201 -0
- package/README.md +192 -0
- package/dist/app.d.ts +85 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +233 -0
- package/dist/app.js.map +1 -0
- package/dist/artifacts.d.ts +61 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +97 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +1 -0
- package/dist/cligent-agent.d.ts +11 -0
- package/dist/cligent-agent.d.ts.map +1 -0
- package/dist/cligent-agent.js +66 -0
- package/dist/cligent-agent.js.map +1 -0
- package/dist/compiled-executor.d.ts +42 -0
- package/dist/compiled-executor.d.ts.map +1 -0
- package/dist/compiled-executor.js +433 -0
- package/dist/compiled-executor.js.map +1 -0
- package/dist/config-file.d.ts +52 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +159 -0
- package/dist/config-file.js.map +1 -0
- package/dist/config.d.ts +82 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +161 -0
- package/dist/config.js.map +1 -0
- package/dist/entry-module.d.ts +22 -0
- package/dist/entry-module.d.ts.map +1 -0
- package/dist/entry-module.js +216 -0
- package/dist/entry-module.js.map +1 -0
- package/dist/execution.d.ts +71 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +207 -0
- package/dist/execution.js.map +1 -0
- package/dist/file-capability.d.ts +46 -0
- package/dist/file-capability.d.ts.map +1 -0
- package/dist/file-capability.js +234 -0
- package/dist/file-capability.js.map +1 -0
- package/dist/file-grants.d.ts +79 -0
- package/dist/file-grants.d.ts.map +1 -0
- package/dist/file-grants.js +133 -0
- package/dist/file-grants.js.map +1 -0
- package/dist/hash.d.ts +14 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +34 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/interpreter.d.ts +48 -0
- package/dist/interpreter.d.ts.map +1 -0
- package/dist/interpreter.js +118 -0
- package/dist/interpreter.js.map +1 -0
- package/dist/invocation.d.ts +64 -0
- package/dist/invocation.d.ts.map +1 -0
- package/dist/invocation.js +148 -0
- package/dist/invocation.js.map +1 -0
- package/dist/link.d.ts +63 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +172 -0
- package/dist/link.js.map +1 -0
- package/dist/markdown.d.ts +12 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +63 -0
- package/dist/markdown.js.map +1 -0
- package/dist/normalize.md +68 -0
- package/dist/phase-runner.d.ts +64 -0
- package/dist/phase-runner.d.ts.map +1 -0
- package/dist/phase-runner.js +322 -0
- package/dist/phase-runner.js.map +1 -0
- package/dist/phase.d.ts +62 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +111 -0
- package/dist/phase.js.map +1 -0
- package/dist/pin-closure.d.ts +19 -0
- package/dist/pin-closure.d.ts.map +1 -0
- package/dist/pin-closure.js +110 -0
- package/dist/pin-closure.js.map +1 -0
- package/dist/pin-currency.d.ts +55 -0
- package/dist/pin-currency.d.ts.map +1 -0
- package/dist/pin-currency.js +435 -0
- package/dist/pin-currency.js.map +1 -0
- package/dist/pin-generate.d.ts +46 -0
- package/dist/pin-generate.d.ts.map +1 -0
- package/dist/pin-generate.js +131 -0
- package/dist/pin-generate.js.map +1 -0
- package/dist/pin-paths.d.ts +11 -0
- package/dist/pin-paths.d.ts.map +1 -0
- package/dist/pin-paths.js +108 -0
- package/dist/pin-paths.js.map +1 -0
- package/dist/pins.d.ts +84 -0
- package/dist/pins.d.ts.map +1 -0
- package/dist/pins.js +265 -0
- package/dist/pins.js.map +1 -0
- package/dist/pipeline.d.ts +61 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +147 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/playbook-contract.d.ts +194 -0
- package/dist/playbook-contract.d.ts.map +1 -0
- package/dist/playbook-contract.js +180 -0
- package/dist/playbook-contract.js.map +1 -0
- package/dist/playbook-ports.d.ts +53 -0
- package/dist/playbook-ports.d.ts.map +1 -0
- package/dist/playbook-ports.js +217 -0
- package/dist/playbook-ports.js.map +1 -0
- package/dist/resolver.d.ts +68 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +133 -0
- package/dist/resolver.js.map +1 -0
- package/dist/runner.d.ts +45 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +506 -0
- package/dist/runner.js.map +1 -0
- package/dist/runtime-package.d.ts +7 -0
- package/dist/runtime-package.d.ts.map +1 -0
- package/dist/runtime-package.js +132 -0
- package/dist/runtime-package.js.map +1 -0
- package/dist/slc.config.template.yaml +5 -0
- package/dist/verify-coverage.d.ts +99 -0
- package/dist/verify-coverage.d.ts.map +1 -0
- package/dist/verify-coverage.js +1949 -0
- package/dist/verify-coverage.js.map +1 -0
- package/dist/verify-support.d.ts +10 -0
- package/dist/verify-support.d.ts.map +1 -0
- package/dist/verify-support.js +52 -0
- package/dist/verify-support.js.map +1 -0
- package/dist/verify.d.ts +393 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +1790 -0
- package/dist/verify.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
/**
|
|
4
|
+
* Execution boundary orchestrator and generic checks (DR-003).
|
|
5
|
+
*
|
|
6
|
+
* `runPhase` performs only generic mechanics (PHEXEC-1): it snapshots the
|
|
7
|
+
* protected inputs, runs an injected {@link PhaseExecutor} (interpreted in
|
|
8
|
+
* IR-001 Task 9, compiled later), then applies the generic checks — the target
|
|
9
|
+
* exists and its extension matches the declared one (PHEXEC-4); the source,
|
|
10
|
+
* objects, link target, and the chain's definition files are unchanged; and an
|
|
11
|
+
* optional `revalidate` hook confirms the pipeline chain still infers, catching
|
|
12
|
+
* added/removed phase files (PHEXEC-5). Input-mutating write-scope violations
|
|
13
|
+
* are caught after any executor outcome (PHEXEC-3, PHEXEC-6). A `blocked` or
|
|
14
|
+
* `error` result, a thrown executor, or any failed check becomes a failure
|
|
15
|
+
* report naming the phase, target, and reasons (PHEXEC-7, PHEXEC-9).
|
|
16
|
+
*
|
|
17
|
+
* The executor honors PHEXEC-2 by treating the passed definition as the
|
|
18
|
+
* semantic source of truth. Broader write-scope enforcement (sandbox or
|
|
19
|
+
* allowlist) is a host concern per DR-003 and is left as a future capability.
|
|
20
|
+
* See specs/dev/phase-execution.md.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash } from 'node:crypto';
|
|
23
|
+
import { lstat, readFile, readdir, readlink, stat } from 'node:fs/promises';
|
|
24
|
+
import { extname, join } from 'node:path';
|
|
25
|
+
/**
|
|
26
|
+
* Runs a single phase through the execution boundary: generic mechanics only,
|
|
27
|
+
* plus the DR-003 generic checks and blocked protocol (PHEXEC-1, PHEXEC-4..9).
|
|
28
|
+
*/
|
|
29
|
+
export async function runPhase(opts) {
|
|
30
|
+
const { request, phase, targetExt, executor } = opts;
|
|
31
|
+
const signal = opts.signal ?? new AbortController().signal;
|
|
32
|
+
const target = request.kind === 'compile' ? request.target : request.linked;
|
|
33
|
+
const inputs = request.kind === 'compile'
|
|
34
|
+
? [request.source, ...(request.references ?? [])]
|
|
35
|
+
: [...request.objects, request.linkTarget];
|
|
36
|
+
const definitions = [request.definitionPath, ...(opts.definitions ?? [])];
|
|
37
|
+
const protectedPaths = [...new Set([...inputs, ...definitions])];
|
|
38
|
+
const before = await snapshot(protectedPaths);
|
|
39
|
+
const reasons = [];
|
|
40
|
+
let result = null;
|
|
41
|
+
try {
|
|
42
|
+
result = await executor.run(request, signal);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
reasons.push(`executor threw: ${messageOf(error)}`);
|
|
46
|
+
}
|
|
47
|
+
if (result !== null && result.status !== 'ok') {
|
|
48
|
+
reasons.push(...reasonsFor(result));
|
|
49
|
+
}
|
|
50
|
+
if (result?.status === 'ok') {
|
|
51
|
+
if (!(await exists(target))) {
|
|
52
|
+
reasons.push(`expected target "${target}" was not written`);
|
|
53
|
+
}
|
|
54
|
+
else if (extname(target) !== targetExt) {
|
|
55
|
+
reasons.push(`target "${target}" extension does not match the declared "${targetExt}"`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Protected inputs and chain definitions are re-checked after any outcome, so
|
|
59
|
+
// a mutation is caught even when the executor blocks, errors, or throws
|
|
60
|
+
// (PHEXEC-5, PHEXEC-6).
|
|
61
|
+
const after = await snapshot(protectedPaths);
|
|
62
|
+
for (const path of protectedPaths) {
|
|
63
|
+
if (before.get(path) !== after.get(path)) {
|
|
64
|
+
reasons.push(`protected path "${path}" changed during the run`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (opts.revalidate) {
|
|
68
|
+
try {
|
|
69
|
+
await opts.revalidate();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
reasons.push(`pipeline chain is no longer valid: ${messageOf(error)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (reasons.length > 0) {
|
|
76
|
+
return failure(phase, target, reasons);
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, target, diagnostics: result?.diagnostics ?? [] };
|
|
79
|
+
}
|
|
80
|
+
/** Renders a failure report as a multi-line diagnostic string (PHEXEC-9). */
|
|
81
|
+
export function formatFailureReport(report) {
|
|
82
|
+
const lines = [`slc: phase "${report.phase}" failed at "${report.target}"`];
|
|
83
|
+
for (const reason of report.reasons) {
|
|
84
|
+
lines.push(` - ${reason}`);
|
|
85
|
+
}
|
|
86
|
+
return lines.join('\n');
|
|
87
|
+
}
|
|
88
|
+
function reasonsFor(result) {
|
|
89
|
+
if (result.diagnostics.length > 0) {
|
|
90
|
+
return result.diagnostics;
|
|
91
|
+
}
|
|
92
|
+
return [`phase reported ${result.status} without diagnostics`];
|
|
93
|
+
}
|
|
94
|
+
function failure(phase, target, reasons) {
|
|
95
|
+
return { ok: false, report: { phase, target, reasons } };
|
|
96
|
+
}
|
|
97
|
+
async function snapshot(paths) {
|
|
98
|
+
const entries = await Promise.all(paths.map(async (path) => [path, await pathIdentity(path)]));
|
|
99
|
+
return new Map(entries);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Returns a deterministic identity for one protected path. The kind prefix
|
|
103
|
+
* keeps a missing path, a file, and a directory distinct; directory identities
|
|
104
|
+
* cover every nested entry so modifying a directory link target cannot pass the
|
|
105
|
+
* DR-003 before/after check merely because `readFile(directory)` fails.
|
|
106
|
+
*/
|
|
107
|
+
async function pathIdentity(path) {
|
|
108
|
+
try {
|
|
109
|
+
const rootInfo = await lstat(path);
|
|
110
|
+
if (rootInfo.isSymbolicLink()) {
|
|
111
|
+
const target = await readlink(path);
|
|
112
|
+
return `symlink:${JSON.stringify(target)}:${await followedPathIdentity(path)}`;
|
|
113
|
+
}
|
|
114
|
+
return identityForInfo(path, rootInfo);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (isMissing(error))
|
|
118
|
+
return 'missing';
|
|
119
|
+
return `unreadable:${errorCode(error)}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function followedPathIdentity(path) {
|
|
123
|
+
try {
|
|
124
|
+
return await identityForInfo(path, await stat(path));
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (isMissing(error))
|
|
128
|
+
return 'missing';
|
|
129
|
+
return `unreadable:${errorCode(error)}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function identityForInfo(path, info) {
|
|
133
|
+
if (info.isFile())
|
|
134
|
+
return `file:${await fileDigest(path)}`;
|
|
135
|
+
if (info.isDirectory())
|
|
136
|
+
return `directory:${await treeDigest(path)}`;
|
|
137
|
+
return `other:${info.mode}:${info.size}:${info.mtimeMs}:${info.ctimeMs}`;
|
|
138
|
+
}
|
|
139
|
+
async function fileDigest(path) {
|
|
140
|
+
return createHash('sha256')
|
|
141
|
+
.update(await readFile(path))
|
|
142
|
+
.digest('hex');
|
|
143
|
+
}
|
|
144
|
+
/** Exact-content identity for a directory, including empty directories and links. */
|
|
145
|
+
async function treeDigest(root) {
|
|
146
|
+
const records = [];
|
|
147
|
+
await collectTreeRecords(root, '', records);
|
|
148
|
+
return createHash('sha256').update(JSON.stringify(records)).digest('hex');
|
|
149
|
+
}
|
|
150
|
+
async function collectTreeRecords(root, prefix, records) {
|
|
151
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
152
|
+
entries.sort((left, right) => compareNames(left.name, right.name));
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
155
|
+
const path = join(root, entry.name);
|
|
156
|
+
if (entry.isDirectory()) {
|
|
157
|
+
records.push(['directory', relative]);
|
|
158
|
+
await collectTreeRecords(path, relative, records);
|
|
159
|
+
}
|
|
160
|
+
else if (entry.isFile()) {
|
|
161
|
+
records.push(['file', relative, await fileDigest(path)]);
|
|
162
|
+
}
|
|
163
|
+
else if (entry.isSymbolicLink()) {
|
|
164
|
+
records.push(['symlink', relative, await readlink(path)]);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const info = await lstat(path);
|
|
168
|
+
records.push([
|
|
169
|
+
'other',
|
|
170
|
+
relative,
|
|
171
|
+
String(info.mode),
|
|
172
|
+
String(info.size),
|
|
173
|
+
String(info.mtimeMs),
|
|
174
|
+
String(info.ctimeMs),
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function compareNames(left, right) {
|
|
180
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
181
|
+
}
|
|
182
|
+
function isMissing(error) {
|
|
183
|
+
const code = errorCode(error);
|
|
184
|
+
return code === 'ENOENT' || code === 'ENOTDIR';
|
|
185
|
+
}
|
|
186
|
+
function errorCode(error) {
|
|
187
|
+
if (typeof error === 'object' &&
|
|
188
|
+
error !== null &&
|
|
189
|
+
'code' in error &&
|
|
190
|
+
typeof error.code === 'string') {
|
|
191
|
+
return error.code;
|
|
192
|
+
}
|
|
193
|
+
return 'unknown';
|
|
194
|
+
}
|
|
195
|
+
async function exists(path) {
|
|
196
|
+
try {
|
|
197
|
+
await stat(path);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function messageOf(error) {
|
|
205
|
+
return error instanceof Error ? error.message : String(error);
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=execution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.js","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Hash } from './hash.js';
|
|
2
|
+
/** A virtual POSIX path inside the run root (DR-008). */
|
|
3
|
+
export type SlcPath = string;
|
|
4
|
+
/** Machine-readable reason a capability operation failed (DR-008). */
|
|
5
|
+
export type FileErrorCode = 'invalid_path' | 'not_found' | 'not_file' | 'not_directory' | 'unauthorized' | 'stale' | 'io_error';
|
|
6
|
+
/** The result of a capability operation: a value, or a coded failure (DR-008). */
|
|
7
|
+
export type FileResult<T> = {
|
|
8
|
+
ok: true;
|
|
9
|
+
value: T;
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
code: FileErrorCode;
|
|
13
|
+
diagnostic: string;
|
|
14
|
+
};
|
|
15
|
+
/** One immediate child returned by {@link FileCapability.list} (DR-008). */
|
|
16
|
+
export interface DirectoryEntry {
|
|
17
|
+
name: string;
|
|
18
|
+
kind: 'file' | 'directory';
|
|
19
|
+
}
|
|
20
|
+
/** The artifact-facing file capability (FCAP-1; DR-008). */
|
|
21
|
+
export interface FileCapability {
|
|
22
|
+
read(path: SlcPath): Promise<FileResult<{
|
|
23
|
+
bytes: Uint8Array;
|
|
24
|
+
hash: Hash;
|
|
25
|
+
}>>;
|
|
26
|
+
list(path: SlcPath): Promise<FileResult<{
|
|
27
|
+
entries: DirectoryEntry[];
|
|
28
|
+
}>>;
|
|
29
|
+
write(path: SlcPath, bytes: Uint8Array, opts?: {
|
|
30
|
+
ifMatch?: Hash;
|
|
31
|
+
}): Promise<FileResult<{
|
|
32
|
+
hash: Hash;
|
|
33
|
+
}>>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a {@link FileCapability} rooted at `runRoot`, confining every operation
|
|
37
|
+
* to that directory after symlink resolution (FCAP-1..6).
|
|
38
|
+
*/
|
|
39
|
+
export declare function createFileCapability(runRoot: string): FileCapability;
|
|
40
|
+
/**
|
|
41
|
+
* Canonicalizes a virtual POSIX path to its normalized `/`-prefixed form — the key
|
|
42
|
+
* the host-side grant model matches on — or `null` when the path is
|
|
43
|
+
* platform-absolute or escapes the run root (FCAP-2).
|
|
44
|
+
*/
|
|
45
|
+
export declare function canonicalize(path: SlcPath): string | null;
|
|
46
|
+
//# sourceMappingURL=file-capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-capability.d.ts","sourceRoot":"","sources":["../src/file-capability.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,KAAK,IAAI,EAAa,MAAM,WAAW,CAAC;AAEjD,yDAAyD;AACzD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,sEAAsE;AACtE,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,WAAW,GACX,UAAU,GACV,eAAe,GACf,cAAc,GACd,OAAO,GACP,UAAU,CAAC;AAEf,kFAAkF;AAClF,MAAM,MAAM,UAAU,CAAC,CAAC,IACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC,CAAC,CAAC;IACxE,KAAK,CACH,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,UAAU,EACjB,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,IAAI,CAAA;KAAE,GACxB,OAAO,CAAC,UAAU,CAAC;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAoHpE;AAwDD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASzD"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
/**
|
|
4
|
+
* Host-supplied file capability for compiled phase artifacts (FCAP-1..6; DR-008).
|
|
5
|
+
*
|
|
6
|
+
* A {@link FileCapability} gives a compiled artifact deterministic, confined
|
|
7
|
+
* whole-file access through three operations — `read`, `list`, and `write` — over
|
|
8
|
+
* virtual POSIX paths inside a per-run root. A leading `/` names the run root, not
|
|
9
|
+
* an operating-system absolute path; paths are normalized (repeated separators
|
|
10
|
+
* collapsed, `.` removed, `..` resolved) and confined to the root after resolving
|
|
11
|
+
* symlinks, so a path that escapes the root or uses platform-absolute syntax (for
|
|
12
|
+
* example a Windows drive path) is rejected as `invalid_path` (FCAP-2). Reads and
|
|
13
|
+
* writes return the exact-byte `sha256:` hash with no content transformation
|
|
14
|
+
* (FCAP-3, FCAP-5); writes are atomic whole-file replacements and honor an
|
|
15
|
+
* `ifMatch` compare-and-swap that returns `stale` on a hash mismatch (FCAP-6);
|
|
16
|
+
* listing returns only immediate children, directories before files then
|
|
17
|
+
* lexicographic (FCAP-4).
|
|
18
|
+
*
|
|
19
|
+
* This module is the artifact-facing surface. The host-side per-run grant model
|
|
20
|
+
* that authorizes each operation (default-deny, writable target/linked only) is a
|
|
21
|
+
* later FCAP concern; this capability authorizes by run-root containment alone.
|
|
22
|
+
* See specs/dev/file-capability.md.
|
|
23
|
+
*/
|
|
24
|
+
import { randomUUID } from 'node:crypto';
|
|
25
|
+
import { readFile, readdir, realpath, rename, stat, writeFile, } from 'node:fs/promises';
|
|
26
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
27
|
+
import { hashBytes } from './hash.js';
|
|
28
|
+
/**
|
|
29
|
+
* Creates a {@link FileCapability} rooted at `runRoot`, confining every operation
|
|
30
|
+
* to that directory after symlink resolution (FCAP-1..6).
|
|
31
|
+
*/
|
|
32
|
+
export function createFileCapability(runRoot) {
|
|
33
|
+
const root = resolve(runRoot);
|
|
34
|
+
let realRoot = null;
|
|
35
|
+
const canonicalRoot = () => (realRoot ??= realpath(root));
|
|
36
|
+
async function resolveHost(path) {
|
|
37
|
+
if (isPlatformAbsolute(path)) {
|
|
38
|
+
return fail('invalid_path', `path "${path}" uses platform-absolute syntax`);
|
|
39
|
+
}
|
|
40
|
+
const segments = normalizeVirtual(path);
|
|
41
|
+
if (segments === null) {
|
|
42
|
+
return fail('invalid_path', `path "${path}" escapes the run root`);
|
|
43
|
+
}
|
|
44
|
+
const host = segments.length === 0 ? root : join(root, ...segments);
|
|
45
|
+
const real = await realpathAllowingMissing(host);
|
|
46
|
+
if (!isInside(await canonicalRoot(), real)) {
|
|
47
|
+
return fail('invalid_path', `path "${path}" escapes the run root after resolving symlinks`);
|
|
48
|
+
}
|
|
49
|
+
return { ok: true, value: host };
|
|
50
|
+
}
|
|
51
|
+
// Serialize writes per resolved host path so an ifMatch read-check-rename runs
|
|
52
|
+
// without another write to the same path interleaving at an await — making the
|
|
53
|
+
// compare-and-swap atomic across operations through this capability (FCAP-6).
|
|
54
|
+
const writeLocks = new Map();
|
|
55
|
+
function serializeWrite(host, op) {
|
|
56
|
+
const prior = writeLocks.get(host) ?? Promise.resolve();
|
|
57
|
+
const run = prior.then(op, op);
|
|
58
|
+
writeLocks.set(host, run.then(() => undefined, () => undefined));
|
|
59
|
+
return run;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
async read(path) {
|
|
63
|
+
const resolved = await resolveHost(path);
|
|
64
|
+
if (!resolved.ok) {
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const info = await stat(resolved.value);
|
|
69
|
+
if (info.isDirectory()) {
|
|
70
|
+
return fail('not_file', `path "${path}" is a directory`);
|
|
71
|
+
}
|
|
72
|
+
const bytes = await readFile(resolved.value);
|
|
73
|
+
return { ok: true, value: { bytes, hash: hashBytes(bytes) } };
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return fromFsError(error, path);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
async list(path) {
|
|
80
|
+
const resolved = await resolveHost(path);
|
|
81
|
+
if (!resolved.ok) {
|
|
82
|
+
return resolved;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const info = await stat(resolved.value);
|
|
86
|
+
if (!info.isDirectory()) {
|
|
87
|
+
return fail('not_directory', `path "${path}" is not a directory`);
|
|
88
|
+
}
|
|
89
|
+
const dirents = await readdir(resolved.value, { withFileTypes: true });
|
|
90
|
+
const entries = dirents
|
|
91
|
+
.map((entry) => ({
|
|
92
|
+
name: entry.name,
|
|
93
|
+
kind: entry.isDirectory() ? 'directory' : 'file',
|
|
94
|
+
}))
|
|
95
|
+
.sort(compareEntries);
|
|
96
|
+
return { ok: true, value: { entries } };
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return fromFsError(error, path);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
async write(path, bytes, opts) {
|
|
103
|
+
const resolved = await resolveHost(path);
|
|
104
|
+
if (!resolved.ok) {
|
|
105
|
+
return resolved;
|
|
106
|
+
}
|
|
107
|
+
const host = resolved.value;
|
|
108
|
+
return serializeWrite(host, async () => {
|
|
109
|
+
try {
|
|
110
|
+
const current = await currentHash(host);
|
|
111
|
+
if (current.kind === 'directory') {
|
|
112
|
+
return fail('not_file', `path "${path}" is a directory`);
|
|
113
|
+
}
|
|
114
|
+
if (opts?.ifMatch !== undefined && opts.ifMatch !== current.hash) {
|
|
115
|
+
return fail('stale', `path "${path}" did not match ifMatch ${opts.ifMatch}`);
|
|
116
|
+
}
|
|
117
|
+
await atomicWrite(host, bytes);
|
|
118
|
+
return { ok: true, value: { hash: hashBytes(bytes) } };
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return fromFsError(error, path);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** The current exact-byte hash of `host`, or `null` when it does not exist. */
|
|
128
|
+
async function currentHash(host) {
|
|
129
|
+
try {
|
|
130
|
+
const info = await stat(host);
|
|
131
|
+
if (info.isDirectory()) {
|
|
132
|
+
return { kind: 'directory' };
|
|
133
|
+
}
|
|
134
|
+
return { kind: 'file', hash: hashBytes(await readFile(host)) };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (isNotFound(error)) {
|
|
138
|
+
return { kind: 'absent', hash: null };
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** Atomically replaces or creates `host` by writing a sibling temp file and renaming. */
|
|
144
|
+
async function atomicWrite(host, bytes) {
|
|
145
|
+
const temp = join(dirname(host), `.${basename(host)}.${randomUUID()}.tmp`);
|
|
146
|
+
await writeFile(temp, bytes);
|
|
147
|
+
await rename(temp, host);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Normalizes a virtual POSIX path to in-root segments, treating a leading `/` as
|
|
151
|
+
* the root and resolving `.`/`..`; returns `null` when `..` escapes the root.
|
|
152
|
+
*/
|
|
153
|
+
function normalizeVirtual(path) {
|
|
154
|
+
const out = [];
|
|
155
|
+
for (const part of path.split('/')) {
|
|
156
|
+
if (part === '' || part === '.') {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (part === '..') {
|
|
160
|
+
if (out.length === 0) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
out.pop();
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
out.push(part);
|
|
167
|
+
}
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
/** Rejects Windows drive paths and UNC syntax (DR-008 platform-absolute paths). */
|
|
171
|
+
function isPlatformAbsolute(path) {
|
|
172
|
+
return /^[a-zA-Z]:[\\/]/.test(path) || path.startsWith('\\');
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Canonicalizes a virtual POSIX path to its normalized `/`-prefixed form — the key
|
|
176
|
+
* the host-side grant model matches on — or `null` when the path is
|
|
177
|
+
* platform-absolute or escapes the run root (FCAP-2).
|
|
178
|
+
*/
|
|
179
|
+
export function canonicalize(path) {
|
|
180
|
+
if (isPlatformAbsolute(path)) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const segments = normalizeVirtual(path);
|
|
184
|
+
if (segments === null) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return `/${segments.join('/')}`;
|
|
188
|
+
}
|
|
189
|
+
/** Resolves `host` through symlinks, falling back to the deepest existing ancestor. */
|
|
190
|
+
async function realpathAllowingMissing(host) {
|
|
191
|
+
try {
|
|
192
|
+
return await realpath(host);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (!isNotFound(error)) {
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
const parent = dirname(host);
|
|
199
|
+
if (parent === host) {
|
|
200
|
+
return host;
|
|
201
|
+
}
|
|
202
|
+
return join(await realpathAllowingMissing(parent), basename(host));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function isInside(root, candidate) {
|
|
206
|
+
if (candidate === root) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
const rel = relative(root, candidate);
|
|
210
|
+
return rel !== '' && rel !== '..' && !rel.startsWith(`..${sep}`);
|
|
211
|
+
}
|
|
212
|
+
/** Orders entries with directories before files, then lexicographically by name. */
|
|
213
|
+
function compareEntries(a, b) {
|
|
214
|
+
if (a.kind !== b.kind) {
|
|
215
|
+
return a.kind === 'directory' ? -1 : 1;
|
|
216
|
+
}
|
|
217
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
218
|
+
}
|
|
219
|
+
function fromFsError(error, path) {
|
|
220
|
+
if (isNotFound(error)) {
|
|
221
|
+
return fail('not_found', `path "${path}" was not found`);
|
|
222
|
+
}
|
|
223
|
+
return fail('io_error', `path "${path}": ${messageOf(error)}`);
|
|
224
|
+
}
|
|
225
|
+
function isNotFound(error) {
|
|
226
|
+
return error?.code === 'ENOENT';
|
|
227
|
+
}
|
|
228
|
+
function fail(code, diagnostic) {
|
|
229
|
+
return { ok: false, code, diagnostic };
|
|
230
|
+
}
|
|
231
|
+
function messageOf(error) {
|
|
232
|
+
return error instanceof Error ? error.message : String(error);
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=file-capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-capability.js","sourceRoot":"","sources":["../src/file-capability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side per-run grant model for the file capability (FCAP-11..14; DR-008).
|
|
3
|
+
*
|
|
4
|
+
* A compiled phase artifact reaches files only through a {@link FileCapability}
|
|
5
|
+
* the host builds for that run. The capability is default-deny: an operation
|
|
6
|
+
* succeeds only when its path is inside the run root (enforced by the artifact-
|
|
7
|
+
* facing layer in `file-capability.ts`) and is covered by a {@link Grant} of the
|
|
8
|
+
* matching access. {@link buildRunGrants} encodes the policy — the only writable
|
|
9
|
+
* path is the run's `target` (compile) or `linked` (link), and read grants are
|
|
10
|
+
* the run's source/object inputs, its link target when applicable, and the
|
|
11
|
+
* recorded semantic-input closure, each enumerated as a file grant.
|
|
12
|
+
* {@link createGuardedCapability} wraps a run-root capability with that grant set,
|
|
13
|
+
* reporting `unauthorized` for any ungranted access.
|
|
14
|
+
*
|
|
15
|
+
* Directory listing and recursive directory reads are withheld: until a pinning
|
|
16
|
+
* extension defines directory listing and subtree identities, this model grants
|
|
17
|
+
* neither (DR-008), so a pinned phase cannot widen its closure at run time.
|
|
18
|
+
* Scope failures (`invalid_path`, `unauthorized`) are distinguished by
|
|
19
|
+
* {@link isScopeFailure} so the executor can fail them like a generic check rather
|
|
20
|
+
* than a phase `BLOCKED`. See specs/dev/file-capability.md.
|
|
21
|
+
*/
|
|
22
|
+
import { type FileCapability, type FileErrorCode, type SlcPath } from './file-capability.js';
|
|
23
|
+
import { type Hash } from './hash.js';
|
|
24
|
+
/** Whether a grant authorizes reading or writing (DR-008). */
|
|
25
|
+
export type GrantAccess = 'read' | 'write';
|
|
26
|
+
/** Whether a grant covers a file or a directory (DR-008). */
|
|
27
|
+
export type GrantKind = 'file' | 'directory';
|
|
28
|
+
/** Why a path is granted, for audit and diagnostics (DR-008). */
|
|
29
|
+
export type GrantReason = 'source' | 'object' | 'linkTarget' | 'semanticInput' | 'target' | 'linked';
|
|
30
|
+
/** One host-side authorization over a normalized virtual path (DR-008). */
|
|
31
|
+
export interface Grant {
|
|
32
|
+
/** Normalized virtual path (see {@link canonicalize}). */
|
|
33
|
+
path: string;
|
|
34
|
+
access: GrantAccess;
|
|
35
|
+
kind: GrantKind;
|
|
36
|
+
/** Directory grants only: whether `list` is allowed. */
|
|
37
|
+
listing?: boolean;
|
|
38
|
+
/** Directory read grants only: whether descendant reads are covered. */
|
|
39
|
+
recursive?: boolean;
|
|
40
|
+
/** Expected identity for audit: file hash, listing identity, or subtree identity. */
|
|
41
|
+
identity?: Hash;
|
|
42
|
+
reason: GrantReason;
|
|
43
|
+
}
|
|
44
|
+
/** The run inputs a grant set is closed over (DR-008; the closure to authorize). */
|
|
45
|
+
export interface RunGrantSpec {
|
|
46
|
+
kind: 'compile' | 'link';
|
|
47
|
+
/** Compile source input. */
|
|
48
|
+
source?: SlcPath;
|
|
49
|
+
/** Link object artifacts. */
|
|
50
|
+
objects?: readonly SlcPath[];
|
|
51
|
+
/** Runtime link target, when the run consumes one. */
|
|
52
|
+
linkTarget?: SlcPath;
|
|
53
|
+
/** Compile writable target. */
|
|
54
|
+
target?: SlcPath;
|
|
55
|
+
/** Link writable artifact. */
|
|
56
|
+
linked?: SlcPath;
|
|
57
|
+
/** Recorded semantic-input closure files, each enumerated (DR-007). */
|
|
58
|
+
semanticInputs?: readonly {
|
|
59
|
+
path: SlcPath;
|
|
60
|
+
identity?: Hash;
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Builds the default-deny grant set for a run (FCAP-12, FCAP-13).
|
|
65
|
+
*
|
|
66
|
+
* The only writable grant is the run's `target` (compile) or `linked` (link); read
|
|
67
|
+
* grants enumerate the source/object inputs, the link target when present, and the
|
|
68
|
+
* semantic-input closure. Directory listing and recursive reads are not granted.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildRunGrants(spec: RunGrantSpec): Grant[];
|
|
71
|
+
/**
|
|
72
|
+
* Wraps a run-root capability with a grant set, denying every ungranted access
|
|
73
|
+
* (FCAP-11). Path validity and run-root containment are left to the wrapped
|
|
74
|
+
* capability, so an invalid or escaping path still reports `invalid_path`.
|
|
75
|
+
*/
|
|
76
|
+
export declare function createGuardedCapability(runRoot: string, grants: readonly Grant[]): FileCapability;
|
|
77
|
+
/** Reports whether a capability error is a host-enforced scope failure (FCAP-14). */
|
|
78
|
+
export declare function isScopeFailure(code: FileErrorCode): boolean;
|
|
79
|
+
//# sourceMappingURL=file-grants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-grants.d.ts","sourceRoot":"","sources":["../src/file-grants.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAElB,KAAK,OAAO,EAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3C,6DAA6D;AAC7D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE7C,iEAAiE;AACjE,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,QAAQ,GACR,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,QAAQ,CAAC;AAEb,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,sDAAsD;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,cAAc,CAAC,EAAE,SAAS;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;KAAE,EAAE,CAAC;CAChE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,KAAK,EAAE,CAqB1D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,KAAK,EAAE,GACvB,cAAc,CAahB;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE3D"}
|