@cogcoin/client 0.5.14 → 1.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 +80 -25
- package/dist/app-paths.d.ts +5 -6
- package/dist/app-paths.js +8 -16
- package/dist/art/balance.txt +10 -0
- package/dist/art/welcome.txt +16 -0
- package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
- package/dist/bitcoind/bootstrap/controller.js +53 -1
- package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
- package/dist/bitcoind/client/follow-block-times.js +1 -1
- package/dist/bitcoind/client/internal-types.d.ts +7 -3
- package/dist/bitcoind/client/managed-client.d.ts +4 -2
- package/dist/bitcoind/client/managed-client.js +14 -0
- package/dist/bitcoind/client/sync-engine.js +72 -11
- package/dist/bitcoind/hash-order.d.ts +4 -0
- package/dist/bitcoind/hash-order.js +13 -0
- package/dist/bitcoind/indexer-daemon-main.js +11 -3
- package/dist/bitcoind/normalize.js +3 -2
- package/dist/bitcoind/processing-start-height.d.ts +5 -0
- package/dist/bitcoind/processing-start-height.js +7 -0
- package/dist/bitcoind/progress/constants.d.ts +4 -0
- package/dist/bitcoind/progress/constants.js +4 -0
- package/dist/bitcoind/progress/controller.d.ts +2 -1
- package/dist/bitcoind/progress/controller.js +3 -3
- package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
- package/dist/bitcoind/progress/follow-scene.js +29 -6
- package/dist/bitcoind/progress/formatting.d.ts +1 -0
- package/dist/bitcoind/progress/formatting.js +6 -0
- package/dist/bitcoind/progress/train-scene.js +37 -18
- package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
- package/dist/bitcoind/progress/tty-renderer.js +8 -4
- package/dist/bitcoind/rpc.d.ts +2 -1
- package/dist/bitcoind/rpc.js +3 -0
- package/dist/bitcoind/types.d.ts +16 -0
- package/dist/bytes.d.ts +1 -0
- package/dist/bytes.js +3 -0
- package/dist/cli/art.d.ts +2 -0
- package/dist/cli/art.js +37 -0
- package/dist/cli/commands/client-admin.d.ts +2 -0
- package/dist/cli/commands/client-admin.js +91 -0
- package/dist/cli/commands/follow.js +0 -2
- package/dist/cli/commands/mining-admin.js +6 -47
- package/dist/cli/commands/mining-read.js +11 -50
- package/dist/cli/commands/mining-runtime.js +38 -3
- package/dist/cli/commands/service-runtime.js +0 -2
- package/dist/cli/commands/status.js +8 -2
- package/dist/cli/commands/sync.js +51 -4
- package/dist/cli/commands/wallet-admin.js +142 -136
- package/dist/cli/commands/wallet-mutation.js +91 -79
- package/dist/cli/commands/wallet-read.js +15 -18
- package/dist/cli/context.js +4 -14
- package/dist/cli/mining-format.d.ts +0 -1
- package/dist/cli/mining-format.js +5 -37
- package/dist/cli/mining-json.d.ts +0 -18
- package/dist/cli/mining-json.js +0 -35
- package/dist/cli/mutation-command-groups.d.ts +1 -2
- package/dist/cli/mutation-command-groups.js +0 -5
- package/dist/cli/mutation-json.d.ts +24 -145
- package/dist/cli/mutation-json.js +30 -136
- package/dist/cli/mutation-resolved-json.d.ts +0 -7
- package/dist/cli/mutation-resolved-json.js +4 -10
- package/dist/cli/mutation-success.d.ts +2 -0
- package/dist/cli/mutation-success.js +11 -1
- package/dist/cli/mutation-text-format.js +1 -3
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +254 -231
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +93 -122
- package/dist/cli/preview-json.d.ts +17 -120
- package/dist/cli/preview-json.js +14 -97
- package/dist/cli/prompt.js +8 -13
- package/dist/cli/read-json.d.ts +15 -37
- package/dist/cli/read-json.js +44 -140
- package/dist/cli/runner.js +10 -13
- package/dist/cli/types.d.ts +8 -17
- package/dist/cli/types.js +0 -2
- package/dist/cli/wallet-format.d.ts +1 -0
- package/dist/cli/wallet-format.js +205 -144
- package/dist/cli/workflow-hints.d.ts +3 -3
- package/dist/cli/workflow-hints.js +11 -8
- package/dist/client/default-client.d.ts +3 -1
- package/dist/client/default-client.js +45 -2
- package/dist/client/factory.js +1 -1
- package/dist/client/initialization.js +23 -0
- package/dist/client/persistence.js +5 -5
- package/dist/client/store-adapter.js +1 -0
- package/dist/sqlite/checkpoints.d.ts +1 -0
- package/dist/sqlite/checkpoints.js +7 -0
- package/dist/sqlite/store.js +14 -1
- package/dist/types.d.ts +1 -0
- package/dist/wallet/coin-control.d.ts +41 -11
- package/dist/wallet/coin-control.js +100 -357
- package/dist/wallet/descriptor-normalization.d.ts +1 -3
- package/dist/wallet/descriptor-normalization.js +0 -16
- package/dist/wallet/lifecycle.d.ts +7 -99
- package/dist/wallet/lifecycle.js +513 -968
- package/dist/wallet/managed-core-wallet.d.ts +13 -0
- package/dist/wallet/managed-core-wallet.js +20 -0
- package/dist/wallet/mining/constants.d.ts +5 -12
- package/dist/wallet/mining/constants.js +5 -12
- package/dist/wallet/mining/control.d.ts +1 -13
- package/dist/wallet/mining/control.js +45 -349
- package/dist/wallet/mining/index.d.ts +3 -4
- package/dist/wallet/mining/index.js +1 -2
- package/dist/wallet/mining/runner.d.ts +179 -6
- package/dist/wallet/mining/runner.js +891 -501
- package/dist/wallet/mining/runtime-artifacts.js +23 -3
- package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
- package/dist/wallet/mining/sentence-protocol.js +123 -0
- package/dist/wallet/mining/sentences.d.ts +4 -8
- package/dist/wallet/mining/sentences.js +3 -52
- package/dist/wallet/mining/state.d.ts +11 -6
- package/dist/wallet/mining/state.js +7 -6
- package/dist/wallet/mining/types.d.ts +2 -30
- package/dist/wallet/mining/visualizer.d.ts +31 -3
- package/dist/wallet/mining/visualizer.js +135 -13
- package/dist/wallet/read/context.d.ts +0 -2
- package/dist/wallet/read/context.js +119 -140
- package/dist/wallet/read/filter.js +2 -11
- package/dist/wallet/read/index.d.ts +1 -1
- package/dist/wallet/read/project.js +24 -77
- package/dist/wallet/read/types.d.ts +10 -25
- package/dist/wallet/reset.d.ts +0 -1
- package/dist/wallet/reset.js +60 -138
- package/dist/wallet/root-resolution.d.ts +1 -5
- package/dist/wallet/root-resolution.js +0 -18
- package/dist/wallet/runtime.d.ts +0 -6
- package/dist/wallet/runtime.js +0 -8
- package/dist/wallet/state/client-password-agent.js +208 -0
- package/dist/wallet/state/client-password.d.ts +65 -0
- package/dist/wallet/state/client-password.js +952 -0
- package/dist/wallet/state/crypto.d.ts +1 -20
- package/dist/wallet/state/crypto.js +0 -63
- package/dist/wallet/state/provider.d.ts +23 -11
- package/dist/wallet/state/provider.js +248 -290
- package/dist/wallet/state/storage.d.ts +2 -2
- package/dist/wallet/state/storage.js +48 -16
- package/dist/wallet/tx/anchor.d.ts +3 -28
- package/dist/wallet/tx/anchor.js +349 -1240
- package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
- package/dist/wallet/tx/bitcoin-transfer.js +200 -0
- package/dist/wallet/tx/cog.d.ts +5 -1
- package/dist/wallet/tx/cog.js +149 -185
- package/dist/wallet/tx/common.d.ts +74 -10
- package/dist/wallet/tx/common.js +315 -138
- package/dist/wallet/tx/domain-admin.d.ts +3 -1
- package/dist/wallet/tx/domain-admin.js +61 -99
- package/dist/wallet/tx/domain-market.d.ts +5 -1
- package/dist/wallet/tx/domain-market.js +221 -228
- package/dist/wallet/tx/field.d.ts +4 -10
- package/dist/wallet/tx/field.js +84 -914
- package/dist/wallet/tx/identity-selector.d.ts +9 -3
- package/dist/wallet/tx/identity-selector.js +17 -35
- package/dist/wallet/tx/index.d.ts +3 -1
- package/dist/wallet/tx/index.js +2 -1
- package/dist/wallet/tx/register.d.ts +3 -1
- package/dist/wallet/tx/register.js +62 -220
- package/dist/wallet/tx/reputation.d.ts +3 -1
- package/dist/wallet/tx/reputation.js +58 -95
- package/dist/wallet/types.d.ts +8 -122
- package/package.json +5 -5
- package/dist/wallet/archive.d.ts +0 -4
- package/dist/wallet/archive.js +0 -41
- package/dist/wallet/mining/hook-protocol.d.ts +0 -47
- package/dist/wallet/mining/hook-protocol.js +0 -161
- package/dist/wallet/mining/hook-runner.js +0 -52
- package/dist/wallet/mining/hooks.d.ts +0 -38
- package/dist/wallet/mining/hooks.js +0 -520
- package/dist/wallet/state/explicit-lock.d.ts +0 -4
- package/dist/wallet/state/explicit-lock.js +0 -19
- package/dist/wallet/state/session.d.ts +0 -12
- package/dist/wallet/state/session.js +0 -23
- /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
|
@@ -1,520 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { access, constants, lstat, mkdir, readFile, readdir, realpath, stat, writeFile, } from "node:fs/promises";
|
|
4
|
-
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { MINING_HOOK_COOLDOWN_MS, MINING_HOOK_FAILURE_THRESHOLD, MINING_HOOK_STDERR_MAX_BYTES, MINING_HOOK_STDOUT_MAX_BYTES, MINING_HOOK_TERMINATE_GRACE_MS, MINING_HOOK_VALIDATION_TIMEOUT_MS, } from "./constants.js";
|
|
7
|
-
import { MINING_HOOK_VALIDATION_FIXTURES, normalizeHookResponse, parseStrictJsonValue, } from "./hook-protocol.js";
|
|
8
|
-
const DEFAULT_MINING_HOOK_TEMPLATE = `export async function generateSentences(request) {
|
|
9
|
-
const domains = Array.isArray(request?.rootDomains) ? request.rootDomains : [];
|
|
10
|
-
const candidates = domains.map((domain) => {
|
|
11
|
-
const requiredWords = Array.isArray(domain?.requiredWords)
|
|
12
|
-
? domain.requiredWords.filter((word) => typeof word === "string" && word.length > 0).join(" ")
|
|
13
|
-
: "abandon ability able about above";
|
|
14
|
-
const domainName = typeof domain?.domainName === "string" && domain.domainName.length > 0
|
|
15
|
-
? domain.domainName
|
|
16
|
-
: "domain";
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
domainId: domain.domainId,
|
|
20
|
-
sentence: \`\${domainName} sentence using \${requiredWords}.\`,
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
schemaVersion: 1,
|
|
26
|
-
requestId: typeof request?.requestId === "string" ? request.requestId : "",
|
|
27
|
-
candidates,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
`;
|
|
31
|
-
const DEFAULT_MINING_HOOK_PACKAGE_JSON = {
|
|
32
|
-
name: "cogcoin-mining-hooks",
|
|
33
|
-
private: true,
|
|
34
|
-
type: "module",
|
|
35
|
-
};
|
|
36
|
-
const HOOK_ENV_ALLOWLIST = [
|
|
37
|
-
"HOME",
|
|
38
|
-
"USERPROFILE",
|
|
39
|
-
"APPDATA",
|
|
40
|
-
"LOCALAPPDATA",
|
|
41
|
-
"TMPDIR",
|
|
42
|
-
"TMP",
|
|
43
|
-
"TEMP",
|
|
44
|
-
"SystemRoot",
|
|
45
|
-
"WINDIR",
|
|
46
|
-
"ComSpec",
|
|
47
|
-
"PATHEXT",
|
|
48
|
-
"SSL_CERT_FILE",
|
|
49
|
-
"SSL_CERT_DIR",
|
|
50
|
-
"NODE_EXTRA_CA_CERTS",
|
|
51
|
-
];
|
|
52
|
-
const RECOGNIZED_LOCKFILES = [
|
|
53
|
-
"package-lock.json",
|
|
54
|
-
"npm-shrinkwrap.json",
|
|
55
|
-
"pnpm-lock.yaml",
|
|
56
|
-
"yarn.lock",
|
|
57
|
-
"bun.lockb",
|
|
58
|
-
];
|
|
59
|
-
function mapStoredValidationState(raw) {
|
|
60
|
-
switch (raw) {
|
|
61
|
-
case "current":
|
|
62
|
-
case "validated":
|
|
63
|
-
return "current";
|
|
64
|
-
case "stale":
|
|
65
|
-
return "stale";
|
|
66
|
-
case "failed":
|
|
67
|
-
return "failed";
|
|
68
|
-
default:
|
|
69
|
-
return "never";
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function mapOperatorToLegacyValidationState(operatorState) {
|
|
73
|
-
switch (operatorState) {
|
|
74
|
-
case "current":
|
|
75
|
-
return "validated";
|
|
76
|
-
case "stale":
|
|
77
|
-
return "stale";
|
|
78
|
-
case "failed":
|
|
79
|
-
return "failed";
|
|
80
|
-
default:
|
|
81
|
-
return "unknown";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async function pathExists(path) {
|
|
85
|
-
try {
|
|
86
|
-
await access(path, constants.F_OK);
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async function hashFile(path) {
|
|
94
|
-
const hash = createHash("sha256");
|
|
95
|
-
hash.update(await readFile(path));
|
|
96
|
-
return hash.digest("hex");
|
|
97
|
-
}
|
|
98
|
-
async function listFilesRecursively(root, options) {
|
|
99
|
-
let entries;
|
|
100
|
-
try {
|
|
101
|
-
entries = await readdir(root, { withFileTypes: true });
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
const files = [];
|
|
110
|
-
for (const entry of entries) {
|
|
111
|
-
const fullPath = join(root, entry.name);
|
|
112
|
-
const relativePath = relative(root, fullPath);
|
|
113
|
-
const isNodeModules = entry.name === "node_modules";
|
|
114
|
-
const isExcludedRoot = entry.name === ".cache" || entry.name === "tmp" || entry.name === "logs";
|
|
115
|
-
if (entry.isDirectory()) {
|
|
116
|
-
if ((!options.includeNodeModules && isNodeModules) || isExcludedRoot) {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (options.includeNodeModules && relativePath === join("node_modules", ".cache")) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
files.push(...await listFilesRecursively(fullPath, options));
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (entry.isFile()) {
|
|
126
|
-
files.push(fullPath);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return files;
|
|
130
|
-
}
|
|
131
|
-
async function computeFingerprint(options) {
|
|
132
|
-
const files = await listFilesRecursively(options.root, {
|
|
133
|
-
includeNodeModules: options.includeNodeModules,
|
|
134
|
-
});
|
|
135
|
-
if (files.length === 0) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
const descriptors = await Promise.all(files
|
|
139
|
-
.sort()
|
|
140
|
-
.map(async (filePath) => ({
|
|
141
|
-
relativePath: relative(options.root, filePath),
|
|
142
|
-
sha256Hex: await hashFile(filePath),
|
|
143
|
-
})));
|
|
144
|
-
const hash = createHash("sha256");
|
|
145
|
-
hash.update(JSON.stringify({
|
|
146
|
-
schemaVersion: 1,
|
|
147
|
-
files: descriptors,
|
|
148
|
-
}));
|
|
149
|
-
return hash.digest("hex");
|
|
150
|
-
}
|
|
151
|
-
async function assessPackageJson(packagePath) {
|
|
152
|
-
let parsed;
|
|
153
|
-
try {
|
|
154
|
-
parsed = JSON.parse(await readFile(packagePath, "utf8"));
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
158
|
-
return {
|
|
159
|
-
status: "missing",
|
|
160
|
-
message: "package.json is missing for the custom mining hook.",
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
status: "invalid",
|
|
165
|
-
message: "package.json is not valid JSON or could not be read.",
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
if (parsed === null
|
|
169
|
-
|| typeof parsed !== "object"
|
|
170
|
-
|| parsed.type !== "module") {
|
|
171
|
-
return {
|
|
172
|
-
status: "invalid",
|
|
173
|
-
message: "package.json must set \"type\": \"module\" for custom mining hooks.",
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
return {
|
|
177
|
-
status: "valid",
|
|
178
|
-
message: null,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
async function collectTrustPaths(options) {
|
|
182
|
-
const trustPaths = [
|
|
183
|
-
dirname(options.hookRootPath),
|
|
184
|
-
options.hookRootPath,
|
|
185
|
-
options.entrypointPath,
|
|
186
|
-
options.packagePath,
|
|
187
|
-
];
|
|
188
|
-
for (const lockfile of RECOGNIZED_LOCKFILES) {
|
|
189
|
-
const lockfilePath = `${options.hookRootPath}/${lockfile}`;
|
|
190
|
-
if (await pathExists(lockfilePath)) {
|
|
191
|
-
trustPaths.push(lockfilePath);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const nodeModulesPath = `${options.hookRootPath}/node_modules`;
|
|
195
|
-
if (await pathExists(nodeModulesPath)) {
|
|
196
|
-
trustPaths.push(nodeModulesPath);
|
|
197
|
-
}
|
|
198
|
-
return [...new Set(trustPaths)];
|
|
199
|
-
}
|
|
200
|
-
function isPathInsideRoot(rootPath, candidatePath) {
|
|
201
|
-
const relativePath = relative(rootPath, candidatePath);
|
|
202
|
-
return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath));
|
|
203
|
-
}
|
|
204
|
-
async function assessTrust(options) {
|
|
205
|
-
const trustPaths = await collectTrustPaths(options);
|
|
206
|
-
const hooksRootPath = dirname(options.hookRootPath);
|
|
207
|
-
let hooksRootRealPath;
|
|
208
|
-
try {
|
|
209
|
-
hooksRootRealPath = await realpath(hooksRootPath);
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
213
|
-
return {
|
|
214
|
-
status: "missing",
|
|
215
|
-
message: `Hook path ${hooksRootPath} does not exist yet.`,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
for (const path of trustPaths) {
|
|
221
|
-
let metadata;
|
|
222
|
-
try {
|
|
223
|
-
metadata = await lstat(path);
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
227
|
-
return {
|
|
228
|
-
status: "missing",
|
|
229
|
-
message: `Hook path ${path} does not exist yet.`,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
if (metadata.isSymbolicLink()) {
|
|
235
|
-
return {
|
|
236
|
-
status: "untrusted",
|
|
237
|
-
message: `Hook path ${path} uses a symbolic link or reparse point.`,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
const canonicalPath = await realpath(path);
|
|
241
|
-
if (!isPathInsideRoot(hooksRootRealPath, canonicalPath)) {
|
|
242
|
-
return {
|
|
243
|
-
status: "untrusted",
|
|
244
|
-
message: `Hook path ${path} resolves outside the Cogcoin hooks root.`,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
if (process.platform === "win32") {
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
const resolvedMetadata = await stat(path);
|
|
251
|
-
if (typeof process.getuid === "function" && resolvedMetadata.uid !== process.getuid()) {
|
|
252
|
-
return {
|
|
253
|
-
status: "untrusted",
|
|
254
|
-
message: `Hook path ${path} is not owned by the current user.`,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
if ((resolvedMetadata.mode & 0o022) !== 0) {
|
|
258
|
-
return {
|
|
259
|
-
status: "untrusted",
|
|
260
|
-
message: `Hook path ${path} is writable by group or others.`,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return {
|
|
265
|
-
status: "trusted",
|
|
266
|
-
message: null,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
function buildHookChildEnvironment(parentEnv) {
|
|
270
|
-
const environment = {
|
|
271
|
-
COGCOIN_HOOK_KIND: "mining/generate-sentences",
|
|
272
|
-
COGCOIN_HOOK_SCHEMA_VERSION: "1",
|
|
273
|
-
NODE_ENV: "production",
|
|
274
|
-
TZ: "UTC",
|
|
275
|
-
};
|
|
276
|
-
for (const key of HOOK_ENV_ALLOWLIST) {
|
|
277
|
-
const value = parentEnv[key];
|
|
278
|
-
if (typeof value === "string" && value.length > 0) {
|
|
279
|
-
environment[key] = value;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return environment;
|
|
283
|
-
}
|
|
284
|
-
async function terminateChildProcess(child) {
|
|
285
|
-
if (child.exitCode !== null || child.killed) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
child.kill("SIGTERM");
|
|
289
|
-
const exited = await new Promise((resolve) => {
|
|
290
|
-
const timer = setTimeout(() => resolve(false), MINING_HOOK_TERMINATE_GRACE_MS);
|
|
291
|
-
child.once("close", () => {
|
|
292
|
-
clearTimeout(timer);
|
|
293
|
-
resolve(true);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
if (!exited && child.exitCode === null) {
|
|
297
|
-
child.kill("SIGKILL");
|
|
298
|
-
if (child.exitCode === null) {
|
|
299
|
-
await new Promise((resolve) => {
|
|
300
|
-
child.once("close", () => resolve());
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
async function runHookRunner(options) {
|
|
306
|
-
const runnerPath = fileURLToPath(new URL("./hook-runner.js", import.meta.url));
|
|
307
|
-
const child = spawn(process.execPath, [
|
|
308
|
-
runnerPath,
|
|
309
|
-
options.entrypointPath,
|
|
310
|
-
], {
|
|
311
|
-
cwd: options.hookRootPath,
|
|
312
|
-
env: buildHookChildEnvironment(process.env),
|
|
313
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
314
|
-
windowsHide: true,
|
|
315
|
-
});
|
|
316
|
-
let stdout = "";
|
|
317
|
-
let stderr = "";
|
|
318
|
-
let stdoutBytes = 0;
|
|
319
|
-
let stderrBytes = 0;
|
|
320
|
-
let timedOut = false;
|
|
321
|
-
let aborted = false;
|
|
322
|
-
let stdoutOverflow = false;
|
|
323
|
-
let stderrOverflow = false;
|
|
324
|
-
let terminated = null;
|
|
325
|
-
const terminate = () => {
|
|
326
|
-
terminated ??= terminateChildProcess(child);
|
|
327
|
-
return terminated;
|
|
328
|
-
};
|
|
329
|
-
const timeout = setTimeout(() => {
|
|
330
|
-
timedOut = true;
|
|
331
|
-
void terminate();
|
|
332
|
-
}, options.timeoutMs);
|
|
333
|
-
const abortListener = () => {
|
|
334
|
-
aborted = true;
|
|
335
|
-
void terminate();
|
|
336
|
-
};
|
|
337
|
-
options.signal?.addEventListener("abort", abortListener, { once: true });
|
|
338
|
-
child.stdin.on("error", () => undefined);
|
|
339
|
-
child.stdin.end(`${JSON.stringify(options.request)}\n`, "utf8");
|
|
340
|
-
child.stdout.on("data", (chunk) => {
|
|
341
|
-
const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk;
|
|
342
|
-
stdoutBytes += Buffer.byteLength(text);
|
|
343
|
-
if (stdoutBytes > MINING_HOOK_STDOUT_MAX_BYTES) {
|
|
344
|
-
stdoutOverflow = true;
|
|
345
|
-
void terminate();
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
stdout += text;
|
|
349
|
-
});
|
|
350
|
-
child.stderr.on("data", (chunk) => {
|
|
351
|
-
const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk;
|
|
352
|
-
stderrBytes += Buffer.byteLength(text);
|
|
353
|
-
if (stderrBytes > MINING_HOOK_STDERR_MAX_BYTES) {
|
|
354
|
-
stderrOverflow = true;
|
|
355
|
-
void terminate();
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
stderr += text;
|
|
359
|
-
});
|
|
360
|
-
const exit = await new Promise((resolve, reject) => {
|
|
361
|
-
child.once("error", reject);
|
|
362
|
-
child.once("close", (code, signal) => resolve({ code, signal }));
|
|
363
|
-
});
|
|
364
|
-
clearTimeout(timeout);
|
|
365
|
-
options.signal?.removeEventListener("abort", abortListener);
|
|
366
|
-
if (stdoutOverflow) {
|
|
367
|
-
throw new Error(`Custom mining hook stdout exceeded ${MINING_HOOK_STDOUT_MAX_BYTES} bytes.`);
|
|
368
|
-
}
|
|
369
|
-
if (stderrOverflow) {
|
|
370
|
-
throw new Error(`Custom mining hook stderr exceeded ${MINING_HOOK_STDERR_MAX_BYTES} bytes.`);
|
|
371
|
-
}
|
|
372
|
-
if (timedOut) {
|
|
373
|
-
throw new Error("Custom mining hook request timed out.");
|
|
374
|
-
}
|
|
375
|
-
if (aborted) {
|
|
376
|
-
throw new Error("Custom mining hook request aborted.");
|
|
377
|
-
}
|
|
378
|
-
if (exit.code !== 0 || exit.signal !== null) {
|
|
379
|
-
const diagnostic = stderr.trim().length > 0
|
|
380
|
-
? stderr.trim()
|
|
381
|
-
: stdout.trim().length > 0
|
|
382
|
-
? stdout.trim()
|
|
383
|
-
: "Custom mining hook request failed.";
|
|
384
|
-
throw new Error(diagnostic);
|
|
385
|
-
}
|
|
386
|
-
return { stdout, stderr };
|
|
387
|
-
}
|
|
388
|
-
export async function runGenerateSentencesHookRequest(options) {
|
|
389
|
-
const { stdout } = await runHookRunner({
|
|
390
|
-
hookRootPath: options.hookRootPath,
|
|
391
|
-
entrypointPath: options.entrypointPath,
|
|
392
|
-
request: options.request,
|
|
393
|
-
signal: options.signal,
|
|
394
|
-
timeoutMs: options.timeoutMs ?? MINING_HOOK_VALIDATION_TIMEOUT_MS,
|
|
395
|
-
});
|
|
396
|
-
const response = parseStrictJsonValue(stdout, "Custom mining hook stdout was not valid JSON.");
|
|
397
|
-
return normalizeHookResponse({
|
|
398
|
-
request: options.request,
|
|
399
|
-
response,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
export async function ensureMiningHookTemplate(options) {
|
|
403
|
-
const entrypointExists = await pathExists(options.entrypointPath);
|
|
404
|
-
let created = false;
|
|
405
|
-
if (!entrypointExists) {
|
|
406
|
-
await mkdir(options.hookRootPath, { recursive: true });
|
|
407
|
-
await writeFile(options.entrypointPath, DEFAULT_MINING_HOOK_TEMPLATE, {
|
|
408
|
-
encoding: "utf8",
|
|
409
|
-
mode: 0o600,
|
|
410
|
-
});
|
|
411
|
-
created = true;
|
|
412
|
-
}
|
|
413
|
-
if (!await pathExists(options.packagePath)) {
|
|
414
|
-
await mkdir(options.hookRootPath, { recursive: true });
|
|
415
|
-
await writeFile(options.packagePath, `${JSON.stringify(DEFAULT_MINING_HOOK_PACKAGE_JSON, null, 2)}\n`, {
|
|
416
|
-
encoding: "utf8",
|
|
417
|
-
mode: 0o600,
|
|
418
|
-
});
|
|
419
|
-
created = true;
|
|
420
|
-
}
|
|
421
|
-
return created;
|
|
422
|
-
}
|
|
423
|
-
export async function validateCustomMiningHook(options) {
|
|
424
|
-
const entrypointExists = await pathExists(options.entrypointPath);
|
|
425
|
-
if (!entrypointExists) {
|
|
426
|
-
throw new Error("Custom mining hook entrypoint is missing.");
|
|
427
|
-
}
|
|
428
|
-
const packageAssessment = await assessPackageJson(options.packagePath);
|
|
429
|
-
if (packageAssessment.status !== "valid") {
|
|
430
|
-
throw new Error(packageAssessment.message ?? "Custom mining hook package.json is invalid.");
|
|
431
|
-
}
|
|
432
|
-
const trust = await assessTrust(options);
|
|
433
|
-
if (trust.status !== "trusted") {
|
|
434
|
-
throw new Error(trust.message ?? "Custom mining hook trust checks failed.");
|
|
435
|
-
}
|
|
436
|
-
const launchFingerprint = await computeFingerprint({
|
|
437
|
-
root: options.hookRootPath,
|
|
438
|
-
includeNodeModules: false,
|
|
439
|
-
});
|
|
440
|
-
const fullFingerprint = await computeFingerprint({
|
|
441
|
-
root: options.hookRootPath,
|
|
442
|
-
includeNodeModules: true,
|
|
443
|
-
});
|
|
444
|
-
if (launchFingerprint === null || fullFingerprint === null) {
|
|
445
|
-
throw new Error("Custom mining hook files are incomplete.");
|
|
446
|
-
}
|
|
447
|
-
for (const request of MINING_HOOK_VALIDATION_FIXTURES) {
|
|
448
|
-
await runGenerateSentencesHookRequest({
|
|
449
|
-
hookRootPath: options.hookRootPath,
|
|
450
|
-
entrypointPath: options.entrypointPath,
|
|
451
|
-
request,
|
|
452
|
-
timeoutMs: MINING_HOOK_VALIDATION_TIMEOUT_MS,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
return {
|
|
456
|
-
launchFingerprint,
|
|
457
|
-
fullFingerprint,
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
export async function inspectMiningHookState(options) {
|
|
461
|
-
const entrypointExists = await pathExists(options.entrypointPath);
|
|
462
|
-
const packageAssessment = await assessPackageJson(options.packagePath);
|
|
463
|
-
const trust = await assessTrust(options);
|
|
464
|
-
const currentLaunchFingerprint = await computeFingerprint({
|
|
465
|
-
root: options.hookRootPath,
|
|
466
|
-
includeNodeModules: false,
|
|
467
|
-
});
|
|
468
|
-
const currentFullFingerprint = options.verify
|
|
469
|
-
? await computeFingerprint({
|
|
470
|
-
root: options.hookRootPath,
|
|
471
|
-
includeNodeModules: true,
|
|
472
|
-
})
|
|
473
|
-
: null;
|
|
474
|
-
const storedState = options.localState;
|
|
475
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
476
|
-
let operatorValidationState = mapStoredValidationState(storedState?.validationState);
|
|
477
|
-
if (storedState !== null && operatorValidationState === "current") {
|
|
478
|
-
const launchFingerprintMatches = storedState.validatedLaunchFingerprint !== null
|
|
479
|
-
&& currentLaunchFingerprint !== null
|
|
480
|
-
&& storedState.validatedLaunchFingerprint === currentLaunchFingerprint;
|
|
481
|
-
const fullFingerprintMatches = !options.verify
|
|
482
|
-
|| storedState.validatedFullFingerprint === null
|
|
483
|
-
|| currentFullFingerprint === null
|
|
484
|
-
|| storedState.validatedFullFingerprint === currentFullFingerprint;
|
|
485
|
-
if (trust.status !== "trusted" || !launchFingerprintMatches || !fullFingerprintMatches) {
|
|
486
|
-
operatorValidationState = "stale";
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
const cooldownUntilUnixMs = storedState?.cooldownUntilUnixMs ?? null;
|
|
490
|
-
const cooldownActive = cooldownUntilUnixMs !== null && cooldownUntilUnixMs > nowUnixMs;
|
|
491
|
-
return {
|
|
492
|
-
mode: storedState?.mode ?? "unavailable",
|
|
493
|
-
entrypointPath: options.entrypointPath,
|
|
494
|
-
packagePath: options.packagePath,
|
|
495
|
-
entrypointExists,
|
|
496
|
-
packageStatus: packageAssessment.status,
|
|
497
|
-
packageMessage: packageAssessment.message,
|
|
498
|
-
trustStatus: trust.status,
|
|
499
|
-
trustMessage: trust.message,
|
|
500
|
-
validationState: storedState === null
|
|
501
|
-
? "unavailable"
|
|
502
|
-
: mapOperatorToLegacyValidationState(operatorValidationState),
|
|
503
|
-
operatorValidationState,
|
|
504
|
-
validationError: storedState?.lastValidationError ?? null,
|
|
505
|
-
validatedAtUnixMs: storedState?.lastValidationAtUnixMs ?? null,
|
|
506
|
-
validatedLaunchFingerprint: storedState?.validatedLaunchFingerprint ?? null,
|
|
507
|
-
validatedFullFingerprint: storedState?.validatedFullFingerprint ?? null,
|
|
508
|
-
currentLaunchFingerprint,
|
|
509
|
-
currentFullFingerprint,
|
|
510
|
-
verifyUsed: options.verify,
|
|
511
|
-
cooldownUntilUnixMs,
|
|
512
|
-
cooldownActive,
|
|
513
|
-
consecutiveFailureCount: storedState?.consecutiveFailureCount ?? 0,
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
export function shouldEnterHookCooldown(options) {
|
|
517
|
-
return options.consecutiveFailureCount >= MINING_HOOK_FAILURE_THRESHOLD
|
|
518
|
-
? options.nowUnixMs + MINING_HOOK_COOLDOWN_MS
|
|
519
|
-
: null;
|
|
520
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { WalletExplicitLockStateV1 } from "../types.js";
|
|
2
|
-
export declare function loadWalletExplicitLock(lockPath: string): Promise<WalletExplicitLockStateV1 | null>;
|
|
3
|
-
export declare function saveWalletExplicitLock(lockPath: string, state: WalletExplicitLockStateV1): Promise<void>;
|
|
4
|
-
export declare function clearWalletExplicitLock(lockPath: string): Promise<void>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { readFile, rm } from "node:fs/promises";
|
|
2
|
-
import { writeJsonFileAtomic } from "../fs/atomic.js";
|
|
3
|
-
export async function loadWalletExplicitLock(lockPath) {
|
|
4
|
-
try {
|
|
5
|
-
return JSON.parse(await readFile(lockPath, "utf8"));
|
|
6
|
-
}
|
|
7
|
-
catch (error) {
|
|
8
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
throw error;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
export async function saveWalletExplicitLock(lockPath, state) {
|
|
15
|
-
await writeJsonFileAtomic(lockPath, state, { mode: 0o600 });
|
|
16
|
-
}
|
|
17
|
-
export async function clearWalletExplicitLock(lockPath) {
|
|
18
|
-
await rm(lockPath, { force: true });
|
|
19
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { UnlockSessionStateV1 } from "../types.js";
|
|
2
|
-
import type { WalletSecretProvider, WalletSecretReference } from "./provider.js";
|
|
3
|
-
export type UnlockSessionSaveAccess = Uint8Array | string | {
|
|
4
|
-
provider: WalletSecretProvider;
|
|
5
|
-
secretReference: WalletSecretReference;
|
|
6
|
-
};
|
|
7
|
-
export type UnlockSessionLoadAccess = Uint8Array | string | {
|
|
8
|
-
provider: WalletSecretProvider;
|
|
9
|
-
};
|
|
10
|
-
export declare function saveUnlockSession(sessionPath: string, session: UnlockSessionStateV1, access: UnlockSessionSaveAccess): Promise<void>;
|
|
11
|
-
export declare function loadUnlockSession(sessionPath: string, access: UnlockSessionLoadAccess): Promise<UnlockSessionStateV1>;
|
|
12
|
-
export declare function clearUnlockSession(sessionPath: string): Promise<void>;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { readFile, rm } from "node:fs/promises";
|
|
2
|
-
import { writeJsonFileAtomic } from "../fs/atomic.js";
|
|
3
|
-
import { decryptJsonWithPassphrase, decryptJsonWithSecretProvider, encryptJsonWithPassphrase, encryptJsonWithSecretProvider, } from "./crypto.js";
|
|
4
|
-
export async function saveUnlockSession(sessionPath, session, access) {
|
|
5
|
-
const envelope = typeof access === "string" || access instanceof Uint8Array
|
|
6
|
-
? await encryptJsonWithPassphrase(session, access, {
|
|
7
|
-
format: "cogcoin-wallet-unlock-session",
|
|
8
|
-
})
|
|
9
|
-
: await encryptJsonWithSecretProvider(session, access.provider, access.secretReference, {
|
|
10
|
-
format: "cogcoin-wallet-unlock-session",
|
|
11
|
-
});
|
|
12
|
-
await writeJsonFileAtomic(sessionPath, envelope, { mode: 0o600 });
|
|
13
|
-
}
|
|
14
|
-
export async function loadUnlockSession(sessionPath, access) {
|
|
15
|
-
const raw = await readFile(sessionPath, "utf8");
|
|
16
|
-
const envelope = JSON.parse(raw);
|
|
17
|
-
return typeof access === "string" || access instanceof Uint8Array
|
|
18
|
-
? decryptJsonWithPassphrase(envelope, access)
|
|
19
|
-
: decryptJsonWithSecretProvider(envelope, access.provider);
|
|
20
|
-
}
|
|
21
|
-
export async function clearUnlockSession(sessionPath) {
|
|
22
|
-
await rm(sessionPath, { force: true });
|
|
23
|
-
}
|
|
File without changes
|