@abelxiaoxing/cadence 1.8.4 → 2.0.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/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 +92 -20
- 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
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
constants,
|
|
3
|
-
cpSync,
|
|
4
|
-
lstatSync,
|
|
5
|
-
mkdirSync,
|
|
6
|
-
readdirSync,
|
|
7
|
-
readlinkSync,
|
|
8
|
-
realpathSync,
|
|
9
|
-
symlinkSync,
|
|
10
|
-
writeFileSync,
|
|
11
|
-
} from "node:fs";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
import type { ExecutionProfile } from "./execution-profile.ts";
|
|
14
|
-
import type { IsolationMount } from "./isolation-backend.ts";
|
|
15
|
-
import { removePathSync } from "./remove-path.ts";
|
|
16
|
-
import type { VerificationRunnerBinding } from "./verification-capability.ts";
|
|
17
|
-
|
|
18
|
-
const CACHES = [".vite", ".vite-temp"];
|
|
19
|
-
function within(root: string, candidate: string): boolean {
|
|
20
|
-
const relative = path.relative(root, candidate);
|
|
21
|
-
return (
|
|
22
|
-
relative !== ".." &&
|
|
23
|
-
!relative.startsWith(`..${path.sep}`) &&
|
|
24
|
-
!path.isAbsolute(relative)
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** A disposable dependency view: immutable packages and independently writable caches. */
|
|
29
|
-
export function prepareVerificationEnvironmentIo(
|
|
30
|
-
root: string,
|
|
31
|
-
dependencyOwner: string,
|
|
32
|
-
runnerBindings: readonly VerificationRunnerBinding[],
|
|
33
|
-
profile: ExecutionProfile,
|
|
34
|
-
environment: NodeJS.ProcessEnv,
|
|
35
|
-
checkCancelled: () => void,
|
|
36
|
-
privateRoot: string,
|
|
37
|
-
) {
|
|
38
|
-
const cleanup = () =>
|
|
39
|
-
removePathSync(privateRoot, { recursive: true, force: true });
|
|
40
|
-
try {
|
|
41
|
-
const local = profile.mode !== "isolated";
|
|
42
|
-
const native = profile.mode === "host-trusted";
|
|
43
|
-
if (local && realpathSync(root) === realpathSync(dependencyOwner))
|
|
44
|
-
throw new Error("trusted-candidate-required");
|
|
45
|
-
const mounts: IsolationMount[] = [
|
|
46
|
-
{ source: privateRoot, target: "/cadence", writable: true },
|
|
47
|
-
];
|
|
48
|
-
for (const directory of [
|
|
49
|
-
"home",
|
|
50
|
-
"cache",
|
|
51
|
-
"tmp",
|
|
52
|
-
"reports",
|
|
53
|
-
"dependencies",
|
|
54
|
-
]) {
|
|
55
|
-
mkdirSync(path.join(privateRoot, directory), { mode: 0o700 });
|
|
56
|
-
}
|
|
57
|
-
if (native) {
|
|
58
|
-
mkdirSync(path.join(privateRoot, "home/AppData/Roaming"), {
|
|
59
|
-
recursive: true,
|
|
60
|
-
});
|
|
61
|
-
mkdirSync(path.join(privateRoot, "home/AppData/Local"), {
|
|
62
|
-
recursive: true,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
writeFileSync(path.join(privateRoot, "home/user.npmrc"), "");
|
|
66
|
-
writeFileSync(path.join(privateRoot, "home/global.npmrc"), "");
|
|
67
|
-
const dependencies = path.join(dependencyOwner, "node_modules");
|
|
68
|
-
const stat = lstatSync(dependencies, { throwIfNoEntry: false });
|
|
69
|
-
if (stat && !native) {
|
|
70
|
-
const target = path.join(root, "node_modules");
|
|
71
|
-
const targetStat = lstatSync(target, { throwIfNoEntry: false });
|
|
72
|
-
if (
|
|
73
|
-
!stat.isDirectory() ||
|
|
74
|
-
stat.isSymbolicLink() ||
|
|
75
|
-
(targetStat &&
|
|
76
|
-
(!targetStat.isDirectory() || targetStat.isSymbolicLink()))
|
|
77
|
-
) {
|
|
78
|
-
throw new Error("dependency-path-unsafe");
|
|
79
|
-
}
|
|
80
|
-
if (!targetStat) mkdirSync(target, { mode: 0o700 });
|
|
81
|
-
const view = path.join(privateRoot, "dependencies");
|
|
82
|
-
mounts.push({ source: view, target: "/workspace/node_modules" });
|
|
83
|
-
const entries = readdirSync(dependencies);
|
|
84
|
-
if (entries.length > 20_000) throw new Error("dependency-view-too-large");
|
|
85
|
-
const canonical = realpathSync(dependencies);
|
|
86
|
-
let viewEntries = 0;
|
|
87
|
-
const projectEntry = (name: string) => {
|
|
88
|
-
checkCancelled();
|
|
89
|
-
if (++viewEntries > 20_000)
|
|
90
|
-
throw new Error("dependency-view-too-large");
|
|
91
|
-
if (CACHES.includes(name)) return;
|
|
92
|
-
const source = path.join(dependencies, name);
|
|
93
|
-
const placeholder = path.join(view, name);
|
|
94
|
-
const entry = lstatSync(source);
|
|
95
|
-
if (entry.isSymbolicLink()) {
|
|
96
|
-
const resolved = realpathSync(source);
|
|
97
|
-
if (within(canonical, resolved)) {
|
|
98
|
-
symlinkSync(
|
|
99
|
-
path.relative(path.dirname(source), resolved),
|
|
100
|
-
placeholder,
|
|
101
|
-
);
|
|
102
|
-
} else if (within(realpathSync(dependencyOwner), resolved)) {
|
|
103
|
-
const relative = path.relative(
|
|
104
|
-
realpathSync(dependencyOwner),
|
|
105
|
-
resolved,
|
|
106
|
-
);
|
|
107
|
-
const candidate = path.join(root, relative);
|
|
108
|
-
if (!lstatSync(candidate, { throwIfNoEntry: false }))
|
|
109
|
-
throw new Error("workspace-dependency-missing");
|
|
110
|
-
symlinkSync(
|
|
111
|
-
local
|
|
112
|
-
? candidate
|
|
113
|
-
: `/workspace/${relative.split(path.sep).join("/")}`,
|
|
114
|
-
placeholder,
|
|
115
|
-
);
|
|
116
|
-
} else throw new Error("dependency-path-unsafe");
|
|
117
|
-
} else if (
|
|
118
|
-
entry.isDirectory() &&
|
|
119
|
-
name.startsWith("@") &&
|
|
120
|
-
!name.includes(path.sep)
|
|
121
|
-
) {
|
|
122
|
-
mkdirSync(placeholder);
|
|
123
|
-
for (const child of readdirSync(source))
|
|
124
|
-
projectEntry(path.join(name, child));
|
|
125
|
-
} else {
|
|
126
|
-
if (entry.isDirectory()) mkdirSync(placeholder);
|
|
127
|
-
else if (entry.isFile()) writeFileSync(placeholder, "");
|
|
128
|
-
else throw new Error("dependency-path-unsafe");
|
|
129
|
-
mounts.push({
|
|
130
|
-
source,
|
|
131
|
-
target: `/workspace/node_modules/${name.split(path.sep).join("/")}`,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
for (const name of entries) projectEntry(name);
|
|
136
|
-
for (const cache of CACHES) {
|
|
137
|
-
mkdirSync(path.join(view, cache));
|
|
138
|
-
const source = path.join(privateRoot, "cache", cache);
|
|
139
|
-
mkdirSync(source);
|
|
140
|
-
mounts.push({
|
|
141
|
-
source,
|
|
142
|
-
target: `/workspace/node_modules/${cache}`,
|
|
143
|
-
writable: true,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (local && stat && !native) {
|
|
148
|
-
// Private copies keep package caches/writes away from consumer dependencies.
|
|
149
|
-
const copyEntry = (name: string) => {
|
|
150
|
-
checkCancelled();
|
|
151
|
-
const source = path.join(dependencies, name);
|
|
152
|
-
const destination = path.join(root, "node_modules", name);
|
|
153
|
-
if (CACHES.includes(name)) {
|
|
154
|
-
mkdirSync(destination, { recursive: true });
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
removeEntry(destination, checkCancelled);
|
|
158
|
-
const stat = lstatSync(source);
|
|
159
|
-
if (stat.isSymbolicLink()) {
|
|
160
|
-
const resolved = realpathSync(source);
|
|
161
|
-
const translated = within(canonicalRoot(dependencyOwner), resolved)
|
|
162
|
-
? path.join(
|
|
163
|
-
root,
|
|
164
|
-
path.relative(canonicalRoot(dependencyOwner), resolved),
|
|
165
|
-
)
|
|
166
|
-
: resolved;
|
|
167
|
-
symlinkSync(translated, destination);
|
|
168
|
-
} else if (
|
|
169
|
-
stat.isDirectory() &&
|
|
170
|
-
name.startsWith("@") &&
|
|
171
|
-
!name.includes(path.sep)
|
|
172
|
-
) {
|
|
173
|
-
mkdirSync(destination);
|
|
174
|
-
for (const child of readdirSync(source))
|
|
175
|
-
copyEntry(path.join(name, child));
|
|
176
|
-
} else
|
|
177
|
-
cpSync(source, destination, {
|
|
178
|
-
recursive: true,
|
|
179
|
-
filter: () => {
|
|
180
|
-
checkCancelled();
|
|
181
|
-
return true;
|
|
182
|
-
},
|
|
183
|
-
mode: constants.COPYFILE_FICLONE,
|
|
184
|
-
verbatimSymlinks: true,
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
for (const name of readdirSync(path.join(privateRoot, "dependencies")))
|
|
188
|
-
copyEntry(name);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (native && stat) {
|
|
192
|
-
const target = path.join(root, "node_modules");
|
|
193
|
-
const targetStat = lstatSync(target, { throwIfNoEntry: false });
|
|
194
|
-
if (
|
|
195
|
-
!stat.isDirectory() ||
|
|
196
|
-
stat.isSymbolicLink() ||
|
|
197
|
-
(targetStat &&
|
|
198
|
-
(!targetStat.isDirectory() || targetStat.isSymbolicLink()))
|
|
199
|
-
)
|
|
200
|
-
throw new Error("dependency-path-unsafe");
|
|
201
|
-
const owner = realpathSync(dependencyOwner);
|
|
202
|
-
let count = 0;
|
|
203
|
-
const copy = (source: string, destination: string) => {
|
|
204
|
-
checkCancelled();
|
|
205
|
-
if (++count > 500_000) throw new Error("dependency-view-too-large");
|
|
206
|
-
const entry = lstatSync(source);
|
|
207
|
-
if (entry.isSymbolicLink()) {
|
|
208
|
-
const resolved = realpathSync(source);
|
|
209
|
-
if (!within(owner, resolved))
|
|
210
|
-
throw new Error("dependency-path-unsafe");
|
|
211
|
-
const candidate = path.join(root, path.relative(owner, resolved));
|
|
212
|
-
if (
|
|
213
|
-
!within(realpathSync(dependencies), resolved) &&
|
|
214
|
-
!lstatSync(candidate, { throwIfNoEntry: false })
|
|
215
|
-
)
|
|
216
|
-
throw new Error("workspace-dependency-missing");
|
|
217
|
-
const linked = lstatSync(resolved);
|
|
218
|
-
if (linked.isDirectory()) {
|
|
219
|
-
// Junctions do not require Windows Developer Mode/admin privileges.
|
|
220
|
-
symlinkSync(
|
|
221
|
-
candidate,
|
|
222
|
-
destination,
|
|
223
|
-
process.platform === "win32" ? "junction" : "dir",
|
|
224
|
-
);
|
|
225
|
-
} else if (linked.isFile()) {
|
|
226
|
-
const file = within(realpathSync(dependencies), resolved)
|
|
227
|
-
? resolved
|
|
228
|
-
: candidate;
|
|
229
|
-
cpSync(file, destination, { mode: constants.COPYFILE_FICLONE });
|
|
230
|
-
} else throw new Error("dependency-path-unsafe");
|
|
231
|
-
} else if (entry.isDirectory()) {
|
|
232
|
-
mkdirSync(destination, { recursive: true });
|
|
233
|
-
for (const name of readdirSync(source)) {
|
|
234
|
-
if (source === dependencies && CACHES.includes(name)) {
|
|
235
|
-
mkdirSync(path.join(destination, name));
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
copy(path.join(source, name), path.join(destination, name));
|
|
239
|
-
}
|
|
240
|
-
} else if (entry.isFile())
|
|
241
|
-
cpSync(source, destination, { mode: constants.COPYFILE_FICLONE });
|
|
242
|
-
else throw new Error("dependency-path-unsafe");
|
|
243
|
-
};
|
|
244
|
-
removeEntry(target, checkCancelled);
|
|
245
|
-
copy(dependencies, target);
|
|
246
|
-
}
|
|
247
|
-
const sources = new Map<string, string>();
|
|
248
|
-
const bindings = runnerBindings.map((binding) => {
|
|
249
|
-
if (native) {
|
|
250
|
-
const translate = (value: string) =>
|
|
251
|
-
path.isAbsolute(value) && within(dependencyOwner, value)
|
|
252
|
-
? path.join(root, path.relative(dependencyOwner, value))
|
|
253
|
-
: value;
|
|
254
|
-
return {
|
|
255
|
-
...binding,
|
|
256
|
-
executablePath: translate(binding.executablePath),
|
|
257
|
-
...(binding.fixedArgs
|
|
258
|
-
? { fixedArgs: binding.fixedArgs.map(translate) }
|
|
259
|
-
: {}),
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
if (local || !binding.mountSource) return { ...binding };
|
|
263
|
-
const source = path.resolve(binding.mountSource);
|
|
264
|
-
const stat = lstatSync(source);
|
|
265
|
-
const executable = realpathSync(binding.executablePath);
|
|
266
|
-
if (
|
|
267
|
-
!stat.isDirectory() ||
|
|
268
|
-
stat.isSymbolicLink() ||
|
|
269
|
-
!within(source, executable)
|
|
270
|
-
) {
|
|
271
|
-
throw new Error("runner-mount-unavailable");
|
|
272
|
-
}
|
|
273
|
-
let target = sources.get(source);
|
|
274
|
-
if (!target) {
|
|
275
|
-
target = `/cadence-runners/${sources.size}`;
|
|
276
|
-
sources.set(source, target);
|
|
277
|
-
mounts.push({ source, target });
|
|
278
|
-
}
|
|
279
|
-
const translate = (file: string) =>
|
|
280
|
-
path.posix.join(target, ...path.relative(source, file).split(path.sep));
|
|
281
|
-
return {
|
|
282
|
-
...binding,
|
|
283
|
-
// Preserve an installation's relative launcher link (e.g. bin/npm).
|
|
284
|
-
// Exposing lib/node_modules/npm/bin on PATH selects npm's Windows
|
|
285
|
-
// installer shell shim during a nested `npm run` on Linux.
|
|
286
|
-
executablePath: translate(
|
|
287
|
-
lstatSync(binding.executablePath).isSymbolicLink() &&
|
|
288
|
-
!path.isAbsolute(readlinkSync(binding.executablePath))
|
|
289
|
-
? binding.executablePath
|
|
290
|
-
: executable,
|
|
291
|
-
),
|
|
292
|
-
...(binding.fixedArgs
|
|
293
|
-
? {
|
|
294
|
-
fixedArgs: binding.fixedArgs.map((arg) =>
|
|
295
|
-
path.isAbsolute(arg) && within(source, arg)
|
|
296
|
-
? translate(arg)
|
|
297
|
-
: arg,
|
|
298
|
-
),
|
|
299
|
-
}
|
|
300
|
-
: {}),
|
|
301
|
-
};
|
|
302
|
-
});
|
|
303
|
-
checkCancelled();
|
|
304
|
-
return {
|
|
305
|
-
privateRoot,
|
|
306
|
-
mounts: native ? [] : mounts,
|
|
307
|
-
bindings,
|
|
308
|
-
reportRoot: local ? privateRoot : "/cadence",
|
|
309
|
-
environment: {
|
|
310
|
-
...(local
|
|
311
|
-
? Object.fromEntries(
|
|
312
|
-
profile.inheritEnvironment.flatMap((name) =>
|
|
313
|
-
environment[name] === undefined
|
|
314
|
-
? []
|
|
315
|
-
: [[name, environment[name] ?? ""]],
|
|
316
|
-
),
|
|
317
|
-
)
|
|
318
|
-
: {}),
|
|
319
|
-
...(native
|
|
320
|
-
? Object.fromEntries(
|
|
321
|
-
Object.entries(environment).filter(([key]) =>
|
|
322
|
-
["SYSTEMROOT", "WINDIR", "COMSPEC", "PATHEXT"].includes(
|
|
323
|
-
key.toUpperCase(),
|
|
324
|
-
),
|
|
325
|
-
),
|
|
326
|
-
)
|
|
327
|
-
: {}),
|
|
328
|
-
...(native
|
|
329
|
-
? {
|
|
330
|
-
USERPROFILE: path.join(privateRoot, "home"),
|
|
331
|
-
APPDATA: path.join(privateRoot, "home", "AppData", "Roaming"),
|
|
332
|
-
LOCALAPPDATA: path.join(privateRoot, "home", "AppData", "Local"),
|
|
333
|
-
TEMP: path.join(privateRoot, "tmp"),
|
|
334
|
-
TMP: path.join(privateRoot, "tmp"),
|
|
335
|
-
}
|
|
336
|
-
: {}),
|
|
337
|
-
CI: "1",
|
|
338
|
-
HOME: local ? path.join(privateRoot, "home") : "/cadence/home",
|
|
339
|
-
XDG_CACHE_HOME: local
|
|
340
|
-
? path.join(privateRoot, "cache")
|
|
341
|
-
: "/cadence/cache",
|
|
342
|
-
TMPDIR: local ? path.join(privateRoot, "tmp") : "/cadence/tmp",
|
|
343
|
-
npm_config_cache: local
|
|
344
|
-
? path.join(privateRoot, "cache/npm")
|
|
345
|
-
: "/cadence/cache/npm",
|
|
346
|
-
npm_config_userconfig: local
|
|
347
|
-
? path.join(privateRoot, "home/user.npmrc")
|
|
348
|
-
: "/cadence/home/user.npmrc",
|
|
349
|
-
npm_config_globalconfig: local
|
|
350
|
-
? path.join(privateRoot, "home/global.npmrc")
|
|
351
|
-
: "/cadence/home/global.npmrc",
|
|
352
|
-
PATH: [
|
|
353
|
-
...new Set(
|
|
354
|
-
// Honor explicitly resolved private runtimes before system fallbacks.
|
|
355
|
-
[
|
|
356
|
-
...bindings.filter((binding) => binding.mountSource),
|
|
357
|
-
...bindings.filter((binding) => !binding.mountSource),
|
|
358
|
-
].map((binding) =>
|
|
359
|
-
(local ? path : path.posix).dirname(binding.executablePath),
|
|
360
|
-
),
|
|
361
|
-
),
|
|
362
|
-
...(local ? (environment.PATH ?? "").split(path.delimiter) : []),
|
|
363
|
-
...(native ? [] : ["/usr/local/bin", "/usr/bin", "/bin"]),
|
|
364
|
-
].join(local ? path.delimiter : ":"),
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
} catch (error) {
|
|
368
|
-
cleanup();
|
|
369
|
-
throw error;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function canonicalRoot(root: string): string {
|
|
374
|
-
return realpathSync(root);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/** Per-entry cancellation, including replacement of an earlier dependency view. */
|
|
378
|
-
function removeEntry(file: string, checkCancelled: () => void): void {
|
|
379
|
-
checkCancelled();
|
|
380
|
-
const stat = lstatSync(file, { throwIfNoEntry: false });
|
|
381
|
-
if (!stat) return;
|
|
382
|
-
if (stat.isDirectory() && !stat.isSymbolicLink())
|
|
383
|
-
for (const name of readdirSync(file))
|
|
384
|
-
removeEntry(path.join(file, name), checkCancelled);
|
|
385
|
-
removePathSync(file, {
|
|
386
|
-
recursive: stat.isDirectory() && !stat.isSymbolicLink(),
|
|
387
|
-
force: true,
|
|
388
|
-
});
|
|
389
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { realpathSync } from "node:fs";
|
|
3
|
-
import { mkdtemp } from "node:fs/promises";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
type StructuredVerificationContract,
|
|
7
|
-
VERIFICATION_CONFIGURATION_PATHS,
|
|
8
|
-
} from "./contracts.ts";
|
|
9
|
-
import { executionProfile } from "./execution-profile.ts";
|
|
10
|
-
import {
|
|
11
|
-
resolveVerificationRunner,
|
|
12
|
-
type VerificationRunnerBinding,
|
|
13
|
-
validateVerificationAdapterCapability,
|
|
14
|
-
verificationRunnerFiles,
|
|
15
|
-
} from "./verification-capability.ts";
|
|
16
|
-
import type { prepareVerificationEnvironmentIo } from "./verification-environment-io.ts";
|
|
17
|
-
import type { WorkspaceIoMetrics } from "./workspace-io.ts";
|
|
18
|
-
import { runWorkspaceIo } from "./workspace-io.ts";
|
|
19
|
-
|
|
20
|
-
export async function captureVerificationEnvironmentIdentity(
|
|
21
|
-
dependencyOwner: string,
|
|
22
|
-
verifications: readonly StructuredVerificationContract[],
|
|
23
|
-
signal?: AbortSignal,
|
|
24
|
-
): Promise<string> {
|
|
25
|
-
const roots = [path.join(dependencyOwner, "node_modules")];
|
|
26
|
-
const unavailable: string[] = [];
|
|
27
|
-
const profile = executionProfile();
|
|
28
|
-
if (profile.mode === "host-trusted")
|
|
29
|
-
roots.push(
|
|
30
|
-
path.resolve(profile.windowsHelperPath ?? ""),
|
|
31
|
-
path.resolve(`${profile.windowsHelperPath}.json`),
|
|
32
|
-
);
|
|
33
|
-
else roots.push(path.resolve(profile.bwrapPath));
|
|
34
|
-
if (
|
|
35
|
-
profile.mode === "host-trusted" &&
|
|
36
|
-
process.env.ComSpec &&
|
|
37
|
-
path.isAbsolute(process.env.ComSpec)
|
|
38
|
-
)
|
|
39
|
-
roots.push(process.env.ComSpec);
|
|
40
|
-
const node = resolveVerificationRunner("node");
|
|
41
|
-
if (node) roots.push(realpathSync(node.executablePath));
|
|
42
|
-
for (const verification of verifications) {
|
|
43
|
-
// Identity describes installed tools; admission/execution separately owns config authorization.
|
|
44
|
-
const capability = validateVerificationAdapterCapability(
|
|
45
|
-
dependencyOwner,
|
|
46
|
-
verification,
|
|
47
|
-
{ executionWritePaths: VERIFICATION_CONFIGURATION_PATHS },
|
|
48
|
-
);
|
|
49
|
-
if (!capability.ok) {
|
|
50
|
-
if (
|
|
51
|
-
![
|
|
52
|
-
"runner-missing",
|
|
53
|
-
"local-executable-missing",
|
|
54
|
-
"script-missing",
|
|
55
|
-
"script-command-mismatch",
|
|
56
|
-
"verification-config-mismatch",
|
|
57
|
-
].includes(capability.diagnostic.code)
|
|
58
|
-
)
|
|
59
|
-
throw new Error("verification-environment-unavailable");
|
|
60
|
-
// Missing task capabilities are observable identities. The exact adapter
|
|
61
|
-
// still rejects that task; independent tasks may use their own runners.
|
|
62
|
-
unavailable.push(capability.diagnostic.code);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
roots.push(
|
|
66
|
-
...verificationRunnerFiles(
|
|
67
|
-
dependencyOwner,
|
|
68
|
-
verification,
|
|
69
|
-
capability.runnerBindings,
|
|
70
|
-
),
|
|
71
|
-
);
|
|
72
|
-
for (const binding of capability.runnerBindings)
|
|
73
|
-
if (binding.mountSource) roots.push(binding.mountSource);
|
|
74
|
-
}
|
|
75
|
-
const installed = await runWorkspaceIo<string>({
|
|
76
|
-
root: dependencyOwner,
|
|
77
|
-
artifactRoot: dependencyOwner,
|
|
78
|
-
operation: "verificationIdentity",
|
|
79
|
-
args: [roots],
|
|
80
|
-
signal,
|
|
81
|
-
});
|
|
82
|
-
return createHash("sha256")
|
|
83
|
-
.update(installed)
|
|
84
|
-
.update(JSON.stringify(unavailable.sort()))
|
|
85
|
-
.update(JSON.stringify(profile))
|
|
86
|
-
.update(
|
|
87
|
-
profile.mode === "host-trusted"
|
|
88
|
-
? JSON.stringify([
|
|
89
|
-
process.platform,
|
|
90
|
-
process.arch,
|
|
91
|
-
process.versions.node,
|
|
92
|
-
["PATH", "SystemRoot", "WINDIR", "ComSpec", "PATHEXT"].map(
|
|
93
|
-
(name) => [name, process.env[name] ?? null],
|
|
94
|
-
),
|
|
95
|
-
])
|
|
96
|
-
: "",
|
|
97
|
-
)
|
|
98
|
-
.update(
|
|
99
|
-
JSON.stringify(
|
|
100
|
-
profile.inheritEnvironment.map((name) => [
|
|
101
|
-
name,
|
|
102
|
-
process.env[name] ?? null,
|
|
103
|
-
]),
|
|
104
|
-
),
|
|
105
|
-
)
|
|
106
|
-
.digest("hex");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** All dependency traversal/copy/removal runs outside the host event loop. */
|
|
110
|
-
export async function prepareVerificationEnvironment(
|
|
111
|
-
root: string,
|
|
112
|
-
dependencyOwner: string,
|
|
113
|
-
runnerBindings: readonly VerificationRunnerBinding[],
|
|
114
|
-
signal?: AbortSignal,
|
|
115
|
-
onMetrics?: (metrics: WorkspaceIoMetrics) => void,
|
|
116
|
-
) {
|
|
117
|
-
signal?.throwIfAborted();
|
|
118
|
-
const profile = executionProfile();
|
|
119
|
-
const privateRoot = await mkdtemp(
|
|
120
|
-
path.join(path.dirname(root), ".cadence-verification-"),
|
|
121
|
-
);
|
|
122
|
-
const cleanup = () =>
|
|
123
|
-
runWorkspaceIo<void>({
|
|
124
|
-
root,
|
|
125
|
-
artifactRoot: root,
|
|
126
|
-
operation: "cleanupVerification",
|
|
127
|
-
args: [privateRoot],
|
|
128
|
-
});
|
|
129
|
-
try {
|
|
130
|
-
const prepared = await runWorkspaceIo<
|
|
131
|
-
ReturnType<typeof prepareVerificationEnvironmentIo>
|
|
132
|
-
>({
|
|
133
|
-
root,
|
|
134
|
-
artifactRoot: root,
|
|
135
|
-
operation: "prepareVerification",
|
|
136
|
-
args: [
|
|
137
|
-
root,
|
|
138
|
-
dependencyOwner,
|
|
139
|
-
runnerBindings,
|
|
140
|
-
profile,
|
|
141
|
-
Object.fromEntries(
|
|
142
|
-
[
|
|
143
|
-
"PATH",
|
|
144
|
-
...(profile.mode === "host-trusted"
|
|
145
|
-
? ["SystemRoot", "WINDIR", "ComSpec", "PATHEXT"]
|
|
146
|
-
: []),
|
|
147
|
-
...profile.inheritEnvironment,
|
|
148
|
-
].flatMap((name) =>
|
|
149
|
-
process.env[name] === undefined ? [] : [[name, process.env[name]]],
|
|
150
|
-
),
|
|
151
|
-
),
|
|
152
|
-
privateRoot,
|
|
153
|
-
],
|
|
154
|
-
signal,
|
|
155
|
-
onMetrics,
|
|
156
|
-
});
|
|
157
|
-
return { ...prepared, cleanup };
|
|
158
|
-
} catch (error) {
|
|
159
|
-
await cleanup();
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
closeSync,
|
|
4
|
-
constants,
|
|
5
|
-
fstatSync,
|
|
6
|
-
lstatSync,
|
|
7
|
-
openSync,
|
|
8
|
-
readdirSync,
|
|
9
|
-
readlinkSync,
|
|
10
|
-
readSync,
|
|
11
|
-
} from "node:fs";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
|
|
14
|
-
/** Content identity, including executable modes and links. Never follows dependency links. */
|
|
15
|
-
export function verificationEnvironmentDigest(
|
|
16
|
-
roots: readonly string[],
|
|
17
|
-
checkCancelled: () => void = () => {},
|
|
18
|
-
) {
|
|
19
|
-
const digest = createHash("sha256");
|
|
20
|
-
let entries = 0;
|
|
21
|
-
let bytes = 0;
|
|
22
|
-
const started = Date.now();
|
|
23
|
-
const buffer = Buffer.alloc(256 * 1024);
|
|
24
|
-
const visit = (file: string, relative: string, dependencyRoot: boolean) => {
|
|
25
|
-
checkCancelled();
|
|
26
|
-
if (++entries > 500_000 || Date.now() - started > 60_000)
|
|
27
|
-
throw new Error("verification-environment-limit");
|
|
28
|
-
const stat = lstatSync(file, { throwIfNoEntry: false });
|
|
29
|
-
digest.update(JSON.stringify([relative, stat ? stat.mode & 0o777 : null]));
|
|
30
|
-
if (!stat) {
|
|
31
|
-
digest.update("absent");
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (stat.isSymbolicLink()) {
|
|
35
|
-
digest.update(JSON.stringify(["link", readlinkSync(file)]));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (stat.isDirectory()) {
|
|
39
|
-
digest.update("directory");
|
|
40
|
-
for (const name of readdirSync(file).sort()) {
|
|
41
|
-
if (dependencyRoot && [".vite", ".vite-temp"].includes(name)) continue;
|
|
42
|
-
visit(path.join(file, name), `${relative}/${name}`, false);
|
|
43
|
-
}
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (!stat.isFile()) throw new Error("verification-environment-unsafe");
|
|
47
|
-
const fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
48
|
-
try {
|
|
49
|
-
const before = fstatSync(fd);
|
|
50
|
-
if (before.ino !== stat.ino || before.dev !== stat.dev)
|
|
51
|
-
throw new Error("verification-environment-changed");
|
|
52
|
-
const content = createHash("sha256");
|
|
53
|
-
for (;;) {
|
|
54
|
-
const count = readSync(fd, buffer, 0, buffer.length, null);
|
|
55
|
-
if (count === 0) break;
|
|
56
|
-
checkCancelled();
|
|
57
|
-
bytes += count;
|
|
58
|
-
if (bytes > 16 * 1024 ** 3 || Date.now() - started > 60_000)
|
|
59
|
-
throw new Error("verification-environment-limit");
|
|
60
|
-
content.update(buffer.subarray(0, count));
|
|
61
|
-
}
|
|
62
|
-
const after = fstatSync(fd);
|
|
63
|
-
if (
|
|
64
|
-
before.size !== after.size ||
|
|
65
|
-
before.mtimeMs !== after.mtimeMs ||
|
|
66
|
-
before.ctimeMs !== after.ctimeMs
|
|
67
|
-
)
|
|
68
|
-
throw new Error("verification-environment-changed");
|
|
69
|
-
digest.update(JSON.stringify(["file", content.digest("hex")]));
|
|
70
|
-
} finally {
|
|
71
|
-
closeSync(fd);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
for (const root of [...new Set(roots)].sort())
|
|
75
|
-
visit(root, root, path.basename(root) === "node_modules");
|
|
76
|
-
return digest.digest("hex");
|
|
77
|
-
}
|
package/src/verification-scan.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/** Only callers admitted before I/O begins share a scan. A later currentness check
|
|
2
|
-
* must observe bytes after its own admission, not join an older in-flight scan.
|
|
3
|
-
* Last cancellation drains
|
|
4
|
-
* the worker before returning; a cancelled scan never admits a new subscriber. */
|
|
5
|
-
export function coalesceVerificationScans<P>(
|
|
6
|
-
scan: (plan: P, signal: AbortSignal) => Promise<string>,
|
|
7
|
-
) {
|
|
8
|
-
type Pending = {
|
|
9
|
-
controller: AbortController;
|
|
10
|
-
promise: Promise<string>;
|
|
11
|
-
subscribers: number;
|
|
12
|
-
started: boolean;
|
|
13
|
-
};
|
|
14
|
-
const pending = new Map<string, Pending>();
|
|
15
|
-
return (plan: P, signal?: AbortSignal): Promise<string> => {
|
|
16
|
-
signal?.throwIfAborted();
|
|
17
|
-
const key = JSON.stringify(plan);
|
|
18
|
-
let entry = pending.get(key);
|
|
19
|
-
if (!entry || entry.started || entry.controller.signal.aborted) {
|
|
20
|
-
const controller = new AbortController();
|
|
21
|
-
const created: Pending = {
|
|
22
|
-
controller,
|
|
23
|
-
subscribers: 0,
|
|
24
|
-
started: false,
|
|
25
|
-
promise: Promise.resolve(""),
|
|
26
|
-
};
|
|
27
|
-
created.promise = Promise.resolve()
|
|
28
|
-
.then(() => {
|
|
29
|
-
created.started = true;
|
|
30
|
-
return scan(plan, controller.signal);
|
|
31
|
-
})
|
|
32
|
-
.finally(() => {
|
|
33
|
-
if (pending.get(key) === created) pending.delete(key);
|
|
34
|
-
});
|
|
35
|
-
entry = created;
|
|
36
|
-
pending.set(key, entry);
|
|
37
|
-
}
|
|
38
|
-
const shared = entry;
|
|
39
|
-
shared.subscribers++;
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
let cancelled = false;
|
|
42
|
-
const abort = () => {
|
|
43
|
-
if (cancelled) return;
|
|
44
|
-
cancelled = true;
|
|
45
|
-
if (--shared.subscribers === 0) shared.controller.abort(signal?.reason);
|
|
46
|
-
};
|
|
47
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
48
|
-
if (signal?.aborted) abort();
|
|
49
|
-
const settle = (value?: string, error?: unknown) => {
|
|
50
|
-
signal?.removeEventListener("abort", abort);
|
|
51
|
-
if (!cancelled) shared.subscribers--;
|
|
52
|
-
if (cancelled)
|
|
53
|
-
reject(signal?.reason ?? new DOMException("cancelled", "AbortError"));
|
|
54
|
-
else if (error !== undefined) reject(error);
|
|
55
|
-
else if (value !== undefined) resolve(value);
|
|
56
|
-
else reject(new Error("verification-scan-result-unavailable"));
|
|
57
|
-
};
|
|
58
|
-
shared.promise.then(
|
|
59
|
-
(value) => settle(value),
|
|
60
|
-
(error) => settle(undefined, error),
|
|
61
|
-
);
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
}
|