@abelxiaoxing/cadence 1.8.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -117
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/agents/design-explorer.md +14 -18
- package/agents/diagnosis-worker.md +14 -14
- package/agents/implementation-worker.md +16 -17
- package/config/workflow-task.example.json +7 -0
- package/docs/remote-access.md +60 -0
- package/docs/reuse-and-distribution.md +44 -0
- package/docs/service-management.md +108 -0
- package/licenses/bundled-web.txt +6245 -0
- package/licenses/pi-web-ui-MIT.txt +21 -0
- package/licenses/pi-web-ui-reuse.md +22 -0
- package/package.json +94 -21
- package/src/index.ts +21 -1802
- package/src/native-build.ts +6 -0
- package/src/native-client.ts +103 -0
- package/src/native-export.ts +58 -0
- package/src/native-history.ts +359 -0
- package/src/operator-cli.mjs +453 -1937
- package/src/plan-proposal.ts +87 -0
- package/src/sdk-worker.mjs +549 -0
- package/src/service/contracts/active.ts +82 -0
- package/src/service/contracts/budget.ts +148 -0
- package/src/service/contracts/build.ts +67 -0
- package/src/service/contracts/capture.ts +48 -0
- package/src/service/contracts/core.ts +33 -0
- package/src/service/contracts/current.ts +261 -0
- package/src/service/contracts/domain.ts +68 -0
- package/src/service/contracts/history.ts +2245 -0
- package/src/service/contracts/models.ts +139 -0
- package/src/service/coordination.ts +862 -0
- package/src/service/private-files.ts +204 -0
- package/src/service-cli.mjs +5592 -0
- package/src/service-client.ts +747 -0
- package/src/service-contract.ts +245 -0
- package/src/service-host.mjs +22382 -0
- package/src/service-http-client.ts +425 -0
- package/src/service-wire.ts +372 -0
- package/src/task-shell.ts +1045 -0
- package/src/task-store.ts +479 -0
- package/src/task-types.ts +184 -0
- package/src/verification-diagnostics.ts +29 -72
- package/src/worker-capture.mjs +1197 -0
- package/src/worker-public.mjs +1753 -0
- package/src/worker-redaction.mjs +608 -0
- package/src/worker-tools.mjs +202 -0
- package/web/dist/assets/KaTeX_AMS-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic.woff +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Italic.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Italic.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic.woff +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-Italic.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-Italic.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Script-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Script-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/web/dist/assets/cadence.css +2 -0
- package/web/dist/assets/cadence.js +7 -0
- package/web/dist/assets/markdown.js +296 -0
- package/web/dist/assets/react.js +9 -0
- package/web/dist/assets/rolldown-runtime.js +1 -0
- package/web/dist/assets/timeline-client.js +41 -0
- package/web/dist/assets/vis-timeline.css +1274 -0
- package/web/dist/assets/vis-timeline.js +32 -0
- package/web/dist/index.html +18 -0
- package/web/dist/manifest.json +505 -0
- package/config/plan-draft.example.json +0 -125
- package/config/plan-draft.multiple-tasks.example.json +0 -375
- package/config/plan-draft.quick.example.json +0 -78
- package/prompts/abel-design.md +0 -382
- package/prompts/abel-diagnose.md +0 -71
- package/prompts/abel-implement.md +0 -284
- package/prompts/abel-init.md +0 -52
- package/src/activation.ts +0 -44
- package/src/activity-contracts.ts +0 -82
- package/src/agent-registry.ts +0 -53
- package/src/apply-transaction.ts +0 -1437
- package/src/artifact-store.ts +0 -213
- package/src/build-windows-job.mjs +0 -95
- package/src/candidate-artifact.ts +0 -143
- package/src/candidate-context.ts +0 -289
- package/src/candidate-patch.ts +0 -271
- package/src/candidate-workspace.ts +0 -80
- package/src/canonical.ts +0 -16
- package/src/change-contract-schema.ts +0 -191
- package/src/change-contract.ts +0 -324
- package/src/change-verification.ts +0 -1617
- package/src/contracts.ts +0 -2466
- package/src/control-contracts.ts +0 -382
- package/src/delivery-compiler.ts +0 -1805
- package/src/delivery-revision.ts +0 -42
- package/src/design-control.ts +0 -1299
- package/src/design-diagnostics.ts +0 -181
- package/src/design-journal.ts +0 -1773
- package/src/directory-sync.ts +0 -36
- package/src/durable-contracts.ts +0 -227
- package/src/durable-workflow.ts +0 -822
- package/src/evidence-draft.ts +0 -122
- package/src/execution-profile.ts +0 -64
- package/src/execution-retention.ts +0 -77
- package/src/file-snapshot.ts +0 -163
- package/src/implement-continuation.ts +0 -559
- package/src/implement-graph.ts +0 -690
- package/src/implement-plan.ts +0 -72
- package/src/install-config.d.mts +0 -6
- package/src/isolation-backend.ts +0 -353
- package/src/model-source.ts +0 -22
- package/src/openspec-cli.ts +0 -494
- package/src/operator-cli-entry.mjs +0 -24
- package/src/operator-tools.ts +0 -309
- package/src/package-candidate-process.ts +0 -206
- package/src/package-candidate.ts +0 -24
- package/src/package-delivery.ts +0 -486
- package/src/package-state.ts +0 -320
- package/src/package-verification.ts +0 -635
- package/src/package-workflow.ts +0 -305
- package/src/packet-runtime.ts +0 -459
- package/src/phase-execution.ts +0 -2163
- package/src/pi-adapter.ts +0 -19
- package/src/plan-draft-summary.ts +0 -527
- package/src/plan-draft.ts +0 -502
- package/src/remove-path.ts +0 -32
- package/src/run-state.ts +0 -420
- package/src/run-store.ts +0 -1179
- package/src/safe-path.ts +0 -77
- package/src/scoped-grep-worker.mjs +0 -23
- package/src/scoped-tools.ts +0 -775
- package/src/single-task-draft.ts +0 -142
- package/src/sqlite-schema.ts +0 -171
- package/src/state-root.ts +0 -215
- package/src/storage-schema.ts +0 -360
- package/src/subagent-activity.ts +0 -1417
- package/src/subagent-process.ts +0 -476
- package/src/task-ledger.ts +0 -1338
- package/src/transport-budget.ts +0 -50
- package/src/verification-capability.ts +0 -1191
- package/src/verification-environment-io.ts +0 -389
- package/src/verification-environment.ts +0 -162
- package/src/verification-identity.ts +0 -77
- package/src/verification-scan.ts +0 -64
- package/src/windows-job-backend.ts +0 -419
- package/src/windows-job.c +0 -149
- package/src/workflow-engine.ts +0 -28
- package/src/workflow-policy.ts +0 -1670
- package/src/workflow-recovery-policy.ts +0 -97
- package/src/workflow-scheduling.ts +0 -105
- package/src/workflow-state-machine.ts +0 -4341
- package/src/workflow-status.ts +0 -576
- package/src/workspace-io-entry.mjs +0 -76
- package/src/workspace-io-worker.mjs +0 -1312
- package/src/workspace-io.ts +0 -89
- package/src/workspace-store.ts +0 -634
package/src/evidence-draft.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
-
import { Type } from "typebox";
|
|
3
|
-
import { AGENTS_IMPACTS, RELATIVE_PATH_PATTERN } from "./contracts.ts";
|
|
4
|
-
|
|
5
|
-
// The authoring boundary is smaller than the sealed EvidenceResult. Only
|
|
6
|
-
// code-owned identity and explicitly advisory fields may be omitted.
|
|
7
|
-
const advisoryStrings = () => Type.Optional(Type.Array(Type.String()));
|
|
8
|
-
const relativePath = () =>
|
|
9
|
-
Type.String({
|
|
10
|
-
pattern: RELATIVE_PATH_PATTERN,
|
|
11
|
-
minLength: 1,
|
|
12
|
-
maxLength: 512,
|
|
13
|
-
description:
|
|
14
|
-
"Canonical workspace-relative path or module slug; no absolute paths, ./, .., backslashes, or trailing slash.",
|
|
15
|
-
});
|
|
16
|
-
const pathSet = (minItems = 0) =>
|
|
17
|
-
Type.Array(relativePath(), { uniqueItems: true, minItems });
|
|
18
|
-
|
|
19
|
-
export function evidenceDraftSchema(requestId: string, role: string) {
|
|
20
|
-
const identity = {
|
|
21
|
-
id: Type.Optional(
|
|
22
|
-
Type.Literal(requestId, {
|
|
23
|
-
description:
|
|
24
|
-
"Omit: bound by the parent result adapter. If supplied, must match this request.",
|
|
25
|
-
}),
|
|
26
|
-
),
|
|
27
|
-
role: Type.Optional(Type.Literal(role)),
|
|
28
|
-
kind: Type.Optional(Type.Literal("evidence")),
|
|
29
|
-
};
|
|
30
|
-
return role === "design-explorer"
|
|
31
|
-
? Type.Object(
|
|
32
|
-
{
|
|
33
|
-
...identity,
|
|
34
|
-
packet_id: Type.Optional(Type.Literal(requestId)),
|
|
35
|
-
module_name: relativePath(),
|
|
36
|
-
scope: pathSet(1),
|
|
37
|
-
files_read: pathSet(),
|
|
38
|
-
evidence: Type.Array(
|
|
39
|
-
Type.Object(
|
|
40
|
-
{
|
|
41
|
-
claim: Type.String({ minLength: 1 }),
|
|
42
|
-
path: relativePath(),
|
|
43
|
-
line_start: Type.Integer({ minimum: 1 }),
|
|
44
|
-
line_end: Type.Integer({
|
|
45
|
-
minimum: 1,
|
|
46
|
-
description:
|
|
47
|
-
"Inclusive end line, greater than or equal to line_start.",
|
|
48
|
-
}),
|
|
49
|
-
},
|
|
50
|
-
{ additionalProperties: false },
|
|
51
|
-
),
|
|
52
|
-
),
|
|
53
|
-
existing_structures: advisoryStrings(),
|
|
54
|
-
existing_conventions: advisoryStrings(),
|
|
55
|
-
constraints_discovered: Type.Array(Type.String()),
|
|
56
|
-
open_questions: Type.Array(Type.String()),
|
|
57
|
-
dependencies: advisoryStrings(),
|
|
58
|
-
write_set_hints: Type.Optional(pathSet()),
|
|
59
|
-
validation_hints: advisoryStrings(),
|
|
60
|
-
agents_impact_hints: advisoryStrings(),
|
|
61
|
-
risks: Type.Array(Type.String()),
|
|
62
|
-
success_criteria_hints: advisoryStrings(),
|
|
63
|
-
},
|
|
64
|
-
{ additionalProperties: false },
|
|
65
|
-
)
|
|
66
|
-
: Type.Object(
|
|
67
|
-
{
|
|
68
|
-
...identity,
|
|
69
|
-
conclusions: Type.Array(Type.String()),
|
|
70
|
-
citations: Type.Array(
|
|
71
|
-
Type.Object(
|
|
72
|
-
{ path: relativePath(), lines: Type.String() },
|
|
73
|
-
{ additionalProperties: false },
|
|
74
|
-
),
|
|
75
|
-
),
|
|
76
|
-
constraints: Type.Array(Type.String()),
|
|
77
|
-
dependencies: advisoryStrings(),
|
|
78
|
-
risks: Type.Array(Type.String()),
|
|
79
|
-
blockingQuestions: Type.Array(Type.String()),
|
|
80
|
-
hints: Type.Optional(
|
|
81
|
-
Type.Object(
|
|
82
|
-
{
|
|
83
|
-
writeSet: pathSet(),
|
|
84
|
-
verification: Type.String(),
|
|
85
|
-
agentsImpact: StringEnum(AGENTS_IMPACTS),
|
|
86
|
-
},
|
|
87
|
-
{ additionalProperties: false },
|
|
88
|
-
),
|
|
89
|
-
),
|
|
90
|
-
},
|
|
91
|
-
{ additionalProperties: false },
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function completeEvidenceDraft(
|
|
96
|
-
value: unknown,
|
|
97
|
-
requestId: string,
|
|
98
|
-
role: string,
|
|
99
|
-
): unknown {
|
|
100
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
101
|
-
const draft = value as Record<string, unknown>;
|
|
102
|
-
const defaults: Record<string, unknown> = {
|
|
103
|
-
id: requestId,
|
|
104
|
-
role,
|
|
105
|
-
kind: "evidence",
|
|
106
|
-
dependencies: [],
|
|
107
|
-
...(role === "design-explorer"
|
|
108
|
-
? {
|
|
109
|
-
packet_id: requestId,
|
|
110
|
-
existing_structures: [],
|
|
111
|
-
existing_conventions: [],
|
|
112
|
-
write_set_hints: [],
|
|
113
|
-
validation_hints: [],
|
|
114
|
-
agents_impact_hints: [],
|
|
115
|
-
success_criteria_hints: [],
|
|
116
|
-
}
|
|
117
|
-
: { hints: { writeSet: [], verification: "", agentsImpact: "none" } }),
|
|
118
|
-
};
|
|
119
|
-
// Defaults mean "no advisory information supplied", never verified facts or
|
|
120
|
-
// authority. Explicit wrong values (including null/undefined) survive validation.
|
|
121
|
-
return { ...defaults, ...draft };
|
|
122
|
-
}
|
package/src/execution-profile.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
|
-
export interface ExecutionProfile {
|
|
5
|
-
mode: "isolated" | "local-trusted" | "host-trusted";
|
|
6
|
-
timeoutMs: number;
|
|
7
|
-
bwrapPath: string;
|
|
8
|
-
inheritEnvironment: string[];
|
|
9
|
-
windowsHelperPath?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Operator configuration only. Unknown modes never silently disable isolation. */
|
|
13
|
-
export function executionProfile(
|
|
14
|
-
env: NodeJS.ProcessEnv = process.env,
|
|
15
|
-
): ExecutionProfile {
|
|
16
|
-
const mode = env.ABEL_EXECUTION_MODE ?? "isolated";
|
|
17
|
-
if (
|
|
18
|
-
mode !== "isolated" &&
|
|
19
|
-
mode !== "local-trusted" &&
|
|
20
|
-
mode !== "host-trusted"
|
|
21
|
-
)
|
|
22
|
-
throw new Error("execution-mode-invalid");
|
|
23
|
-
const timeoutMs = Number(env.ABEL_VERIFICATION_TIMEOUT_MS ?? 600_000);
|
|
24
|
-
if (
|
|
25
|
-
!Number.isSafeInteger(timeoutMs) ||
|
|
26
|
-
timeoutMs < 1 ||
|
|
27
|
-
timeoutMs > 86_400_000
|
|
28
|
-
)
|
|
29
|
-
throw new Error("verification-timeout-invalid");
|
|
30
|
-
const inheritEnvironment = (env.ABEL_VERIFICATION_ENV ?? "")
|
|
31
|
-
.split(",")
|
|
32
|
-
.map((name) => name.trim())
|
|
33
|
-
.filter(Boolean);
|
|
34
|
-
if (
|
|
35
|
-
inheritEnvironment.some((name) => !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name))
|
|
36
|
-
)
|
|
37
|
-
throw new Error("verification-environment-names-invalid");
|
|
38
|
-
if (mode === "isolated" && inheritEnvironment.length)
|
|
39
|
-
throw new Error("verification-environment-requires-trusted-mode");
|
|
40
|
-
if (
|
|
41
|
-
mode === "host-trusted" &&
|
|
42
|
-
env.ABEL_WINDOWS_JOB_HELPER !== undefined &&
|
|
43
|
-
(!env.ABEL_WINDOWS_JOB_HELPER ||
|
|
44
|
-
env.ABEL_WINDOWS_JOB_HELPER.includes("\0") ||
|
|
45
|
-
!(
|
|
46
|
-
path.isAbsolute(env.ABEL_WINDOWS_JOB_HELPER) ||
|
|
47
|
-
path.win32.isAbsolute(env.ABEL_WINDOWS_JOB_HELPER)
|
|
48
|
-
))
|
|
49
|
-
)
|
|
50
|
-
throw new Error("windows-job-helper-path-invalid");
|
|
51
|
-
return {
|
|
52
|
-
mode,
|
|
53
|
-
timeoutMs,
|
|
54
|
-
bwrapPath: env.ABEL_BWRAP_PATH ?? "/usr/bin/bwrap",
|
|
55
|
-
inheritEnvironment,
|
|
56
|
-
...(mode === "host-trusted"
|
|
57
|
-
? {
|
|
58
|
-
windowsHelperPath:
|
|
59
|
-
env.ABEL_WINDOWS_JOB_HELPER ??
|
|
60
|
-
fileURLToPath(new URL("./windows-job.exe", import.meta.url)),
|
|
61
|
-
}
|
|
62
|
-
: {}),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
closeSync,
|
|
4
|
-
fsyncSync,
|
|
5
|
-
lstatSync,
|
|
6
|
-
openSync,
|
|
7
|
-
readdirSync,
|
|
8
|
-
unlinkSync,
|
|
9
|
-
writeFileSync,
|
|
10
|
-
} from "node:fs";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
import { syncDirectory } from "./directory-sync.ts";
|
|
13
|
-
|
|
14
|
-
const prefix = ".cadence-execution-";
|
|
15
|
-
const active = new Set<string>();
|
|
16
|
-
const retained = new Map<string, readonly string[]>();
|
|
17
|
-
/** Written before launching a host process. Absence of a settlement receipt
|
|
18
|
-
* survives host loss; reopening must not infer ownership from a recycled PID. */
|
|
19
|
-
export function retainExecution(ownerRoot: string, roots: readonly string[]) {
|
|
20
|
-
assertExecutionsSettled(ownerRoot);
|
|
21
|
-
const file = path.join(ownerRoot, `${prefix}${randomUUID()}.json`);
|
|
22
|
-
const fd = openSync(file, "wx", 0o600);
|
|
23
|
-
try {
|
|
24
|
-
writeFileSync(
|
|
25
|
-
fd,
|
|
26
|
-
JSON.stringify({
|
|
27
|
-
version: 1,
|
|
28
|
-
roots: roots.map((root) => path.resolve(root)),
|
|
29
|
-
}),
|
|
30
|
-
);
|
|
31
|
-
fsyncSync(fd);
|
|
32
|
-
} finally {
|
|
33
|
-
closeSync(fd);
|
|
34
|
-
}
|
|
35
|
-
syncDirectory(ownerRoot);
|
|
36
|
-
active.add(file);
|
|
37
|
-
retained.set(file, roots);
|
|
38
|
-
return {
|
|
39
|
-
settled() {
|
|
40
|
-
unlinkSync(file);
|
|
41
|
-
syncDirectory(ownerRoot);
|
|
42
|
-
active.delete(file);
|
|
43
|
-
retained.delete(file);
|
|
44
|
-
},
|
|
45
|
-
uncertain() {
|
|
46
|
-
active.delete(file);
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
export function assertExecutionsSettled(
|
|
51
|
-
ownerRoot: string,
|
|
52
|
-
includeActive = false,
|
|
53
|
-
): void {
|
|
54
|
-
if (!lstatSync(ownerRoot, { throwIfNoEntry: false })) return;
|
|
55
|
-
if (
|
|
56
|
-
readdirSync(ownerRoot).some(
|
|
57
|
-
(name) =>
|
|
58
|
-
name.startsWith(prefix) &&
|
|
59
|
-
(includeActive || !active.has(path.join(ownerRoot, name))),
|
|
60
|
-
)
|
|
61
|
-
)
|
|
62
|
-
throw new Error("isolation-termination-unconfirmed");
|
|
63
|
-
}
|
|
64
|
-
export function isExecutionRetained(root: string): boolean {
|
|
65
|
-
const resolved = path.resolve(root);
|
|
66
|
-
return [...retained.values()].some((roots) =>
|
|
67
|
-
roots.some((candidate) => {
|
|
68
|
-
const relative = path.relative(resolved, path.resolve(candidate));
|
|
69
|
-
return (
|
|
70
|
-
relative === "" ||
|
|
71
|
-
(relative !== ".." &&
|
|
72
|
-
!relative.startsWith(`..${path.sep}`) &&
|
|
73
|
-
!path.isAbsolute(relative))
|
|
74
|
-
);
|
|
75
|
-
}),
|
|
76
|
-
);
|
|
77
|
-
}
|
package/src/file-snapshot.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
// File-level snapshots: content hashes for observed files, deterministic
|
|
2
|
-
// recursive content manifests for observed directories, and explicit absent
|
|
3
|
-
// markers for proposed new paths. Currency is decided per bound path; no
|
|
4
|
-
// global workspace revision participates.
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import { lstatSync, readdirSync, readFileSync, readlinkSync } from "node:fs";
|
|
7
|
-
import { join, resolve } from "node:path";
|
|
8
|
-
|
|
9
|
-
import { observeSafePath } from "./safe-path.ts";
|
|
10
|
-
|
|
11
|
-
export interface FileBound {
|
|
12
|
-
kind: "file";
|
|
13
|
-
sha256: string;
|
|
14
|
-
bytes: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface DirBound {
|
|
18
|
-
kind: "dir";
|
|
19
|
-
manifest: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface AbsentBound {
|
|
23
|
-
kind: "absent";
|
|
24
|
-
absent: true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type Bound = Record<string, FileBound | DirBound | AbsentBound>;
|
|
28
|
-
|
|
29
|
-
function sha256(bytes: Buffer): string {
|
|
30
|
-
return createHash("sha256").update(bytes).digest("hex");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Hash bytes with the same identity used by file and workspace snapshots. */
|
|
34
|
-
export function snapshotContentHash(bytes: Uint8Array): string {
|
|
35
|
-
return createHash("sha256").update(bytes).digest("hex");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Snapshot one existing regular file under root. */
|
|
39
|
-
export function snapshotFile(root: string, relPath: string): FileBound | null {
|
|
40
|
-
if (observeSafePath(root, relPath).kind !== "file") return null;
|
|
41
|
-
const abs = resolve(root, relPath);
|
|
42
|
-
const bytes = readFileSync(abs);
|
|
43
|
-
return { sha256: sha256(bytes), bytes: bytes.length, kind: "file" };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function updateManifest(
|
|
47
|
-
hash: ReturnType<typeof createHash>,
|
|
48
|
-
...fields: Array<string | Buffer>
|
|
49
|
-
): void {
|
|
50
|
-
for (const field of fields) {
|
|
51
|
-
const bytes = typeof field === "string" ? Buffer.from(field) : field;
|
|
52
|
-
hash.update(`${bytes.length}:`);
|
|
53
|
-
hash.update(bytes);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function recursiveDirectoryManifest(abs: string): string {
|
|
58
|
-
const hash = createHash("sha256");
|
|
59
|
-
updateManifest(hash, "cadence-directory-manifest");
|
|
60
|
-
const visit = (directory: string, prefix: string): void => {
|
|
61
|
-
const names = readdirSync(directory).sort();
|
|
62
|
-
for (const name of names) {
|
|
63
|
-
const relative = prefix ? `${prefix}/${name}` : name;
|
|
64
|
-
const current = join(directory, name);
|
|
65
|
-
const stat = lstatSync(current);
|
|
66
|
-
if (stat.isDirectory()) {
|
|
67
|
-
updateManifest(hash, "dir", relative, String(stat.mode & 0o777));
|
|
68
|
-
visit(current, relative);
|
|
69
|
-
} else if (stat.isFile()) {
|
|
70
|
-
const bytes = readFileSync(current);
|
|
71
|
-
updateManifest(
|
|
72
|
-
hash,
|
|
73
|
-
"file",
|
|
74
|
-
relative,
|
|
75
|
-
String(stat.mode & 0o777),
|
|
76
|
-
String(bytes.length),
|
|
77
|
-
sha256(bytes),
|
|
78
|
-
);
|
|
79
|
-
} else if (stat.isSymbolicLink()) {
|
|
80
|
-
updateManifest(hash, "symlink", relative, readlinkSync(current));
|
|
81
|
-
} else {
|
|
82
|
-
updateManifest(
|
|
83
|
-
hash,
|
|
84
|
-
"special",
|
|
85
|
-
relative,
|
|
86
|
-
String(stat.mode),
|
|
87
|
-
String(stat.size),
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
visit(abs, "");
|
|
93
|
-
return hash.digest("hex");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Deterministic recursive directory manifest bound to installed content. */
|
|
97
|
-
export function snapshotDirManifest(
|
|
98
|
-
root: string,
|
|
99
|
-
relDir: string,
|
|
100
|
-
): DirBound | null {
|
|
101
|
-
if (observeSafePath(root, relDir).kind !== "directory") return null;
|
|
102
|
-
const abs = resolve(root, relDir);
|
|
103
|
-
return { manifest: recursiveDirectoryManifest(abs), kind: "dir" };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Build a bound map for the given file paths, observed directories, and absent markers. */
|
|
107
|
-
export function snapshotFiles(
|
|
108
|
-
root: string,
|
|
109
|
-
files: string[],
|
|
110
|
-
opts: { absent?: string[] } = {},
|
|
111
|
-
): Bound {
|
|
112
|
-
const bound: Bound = {};
|
|
113
|
-
for (const p of [...new Set(files)]) {
|
|
114
|
-
const entry = snapshotFile(root, p);
|
|
115
|
-
if (entry) bound[p] = entry;
|
|
116
|
-
else bound[p] = { kind: "absent", absent: true };
|
|
117
|
-
}
|
|
118
|
-
for (const p of opts.absent ?? []) {
|
|
119
|
-
bound[p] = { kind: "absent", absent: true };
|
|
120
|
-
}
|
|
121
|
-
return bound;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Build a bound map for observed directories (deterministic manifests). */
|
|
125
|
-
export function snapshotDirManifests(root: string, dirs: string[]): Bound {
|
|
126
|
-
const bound: Bound = {};
|
|
127
|
-
for (const p of [...new Set(dirs)]) {
|
|
128
|
-
const entry = snapshotDirManifest(root, p);
|
|
129
|
-
if (entry) bound[p] = entry;
|
|
130
|
-
else bound[p] = { kind: "absent", absent: true };
|
|
131
|
-
}
|
|
132
|
-
return bound;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** Recapture each bound path and report whether the bound state is current. */
|
|
136
|
-
export function isCurrent(root: string, bound: Bound): boolean {
|
|
137
|
-
for (const [rel, expected] of Object.entries(bound)) {
|
|
138
|
-
if (expected.kind === "dir") {
|
|
139
|
-
const now = snapshotDirManifest(root, rel);
|
|
140
|
-
if (!now || now.manifest !== expected.manifest) return false;
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const observation = observeSafePath(root, rel);
|
|
144
|
-
if (expected.kind === "file") {
|
|
145
|
-
if (observation.kind !== "file") return false;
|
|
146
|
-
const now = snapshotFile(root, rel);
|
|
147
|
-
if (
|
|
148
|
-
!now ||
|
|
149
|
-
now.sha256 !== expected.sha256 ||
|
|
150
|
-
now.bytes !== expected.bytes
|
|
151
|
-
)
|
|
152
|
-
return false;
|
|
153
|
-
} else if (expected.absent && observation.kind !== "absent") {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Merge bound maps (read observations + write targets). */
|
|
161
|
-
export function mergeBounds(...bounds: Bound[]): Bound {
|
|
162
|
-
return Object.assign({}, ...bounds);
|
|
163
|
-
}
|