@cogcoin/client 0.5.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.
Files changed (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -0
  3. package/dist/app-paths.d.ts +38 -0
  4. package/dist/app-paths.js +121 -0
  5. package/dist/art/banner.txt +13 -0
  6. package/dist/art/scroll.txt +13 -0
  7. package/dist/art/train-car.txt +6 -0
  8. package/dist/art/train-smoke.txt +6 -0
  9. package/dist/art/train.txt +6 -0
  10. package/dist/bitcoind/bootstrap/chainstate.d.ts +4 -0
  11. package/dist/bitcoind/bootstrap/chainstate.js +13 -0
  12. package/dist/bitcoind/bootstrap/constants.d.ts +7 -0
  13. package/dist/bitcoind/bootstrap/constants.js +12 -0
  14. package/dist/bitcoind/bootstrap/controller.d.ts +29 -0
  15. package/dist/bitcoind/bootstrap/controller.js +101 -0
  16. package/dist/bitcoind/bootstrap/download.d.ts +2 -0
  17. package/dist/bitcoind/bootstrap/download.js +196 -0
  18. package/dist/bitcoind/bootstrap/headers.d.ts +13 -0
  19. package/dist/bitcoind/bootstrap/headers.js +61 -0
  20. package/dist/bitcoind/bootstrap/paths.d.ts +4 -0
  21. package/dist/bitcoind/bootstrap/paths.js +15 -0
  22. package/dist/bitcoind/bootstrap/snapshot-file.d.ts +7 -0
  23. package/dist/bitcoind/bootstrap/snapshot-file.js +42 -0
  24. package/dist/bitcoind/bootstrap/state.d.ts +40 -0
  25. package/dist/bitcoind/bootstrap/state.js +70 -0
  26. package/dist/bitcoind/bootstrap/types.d.ts +28 -0
  27. package/dist/bitcoind/bootstrap/types.js +1 -0
  28. package/dist/bitcoind/bootstrap.d.ts +8 -0
  29. package/dist/bitcoind/bootstrap.js +7 -0
  30. package/dist/bitcoind/client/factory.d.ts +3 -0
  31. package/dist/bitcoind/client/factory.js +57 -0
  32. package/dist/bitcoind/client/follow-block-times.d.ts +8 -0
  33. package/dist/bitcoind/client/follow-block-times.js +25 -0
  34. package/dist/bitcoind/client/follow-loop.d.ts +10 -0
  35. package/dist/bitcoind/client/follow-loop.js +57 -0
  36. package/dist/bitcoind/client/internal-types.d.ts +63 -0
  37. package/dist/bitcoind/client/internal-types.js +18 -0
  38. package/dist/bitcoind/client/managed-client.d.ts +20 -0
  39. package/dist/bitcoind/client/managed-client.js +197 -0
  40. package/dist/bitcoind/client/rate-tracker.d.ts +2 -0
  41. package/dist/bitcoind/client/rate-tracker.js +24 -0
  42. package/dist/bitcoind/client/sync-engine.d.ts +3 -0
  43. package/dist/bitcoind/client/sync-engine.js +143 -0
  44. package/dist/bitcoind/client.d.ts +1 -0
  45. package/dist/bitcoind/client.js +1 -0
  46. package/dist/bitcoind/errors.d.ts +1 -0
  47. package/dist/bitcoind/errors.js +49 -0
  48. package/dist/bitcoind/index.d.ts +2 -0
  49. package/dist/bitcoind/index.js +1 -0
  50. package/dist/bitcoind/indexer-daemon-main.d.ts +1 -0
  51. package/dist/bitcoind/indexer-daemon-main.js +472 -0
  52. package/dist/bitcoind/indexer-daemon.d.ts +107 -0
  53. package/dist/bitcoind/indexer-daemon.js +391 -0
  54. package/dist/bitcoind/node.d.ts +8 -0
  55. package/dist/bitcoind/node.js +219 -0
  56. package/dist/bitcoind/normalize.d.ts +3 -0
  57. package/dist/bitcoind/normalize.js +47 -0
  58. package/dist/bitcoind/progress/assets.d.ts +10 -0
  59. package/dist/bitcoind/progress/assets.js +90 -0
  60. package/dist/bitcoind/progress/constants.d.ts +48 -0
  61. package/dist/bitcoind/progress/constants.js +53 -0
  62. package/dist/bitcoind/progress/controller.d.ts +28 -0
  63. package/dist/bitcoind/progress/controller.js +188 -0
  64. package/dist/bitcoind/progress/follow-scene.d.ts +40 -0
  65. package/dist/bitcoind/progress/follow-scene.js +367 -0
  66. package/dist/bitcoind/progress/formatting.d.ts +23 -0
  67. package/dist/bitcoind/progress/formatting.js +227 -0
  68. package/dist/bitcoind/progress/quote-scene.d.ts +4 -0
  69. package/dist/bitcoind/progress/quote-scene.js +137 -0
  70. package/dist/bitcoind/progress/train-scene.d.ts +9 -0
  71. package/dist/bitcoind/progress/train-scene.js +92 -0
  72. package/dist/bitcoind/progress/tty-renderer.d.ts +18 -0
  73. package/dist/bitcoind/progress/tty-renderer.js +150 -0
  74. package/dist/bitcoind/progress.d.ts +7 -0
  75. package/dist/bitcoind/progress.js +7 -0
  76. package/dist/bitcoind/quotes.d.ts +24 -0
  77. package/dist/bitcoind/quotes.js +195 -0
  78. package/dist/bitcoind/rpc.d.ts +71 -0
  79. package/dist/bitcoind/rpc.js +322 -0
  80. package/dist/bitcoind/service-paths.d.ts +19 -0
  81. package/dist/bitcoind/service-paths.js +49 -0
  82. package/dist/bitcoind/service.d.ts +40 -0
  83. package/dist/bitcoind/service.js +735 -0
  84. package/dist/bitcoind/testing.d.ts +9 -0
  85. package/dist/bitcoind/testing.js +9 -0
  86. package/dist/bitcoind/types.d.ts +396 -0
  87. package/dist/bitcoind/types.js +3 -0
  88. package/dist/bytes.d.ts +9 -0
  89. package/dist/bytes.js +36 -0
  90. package/dist/cli/commands/follow.d.ts +2 -0
  91. package/dist/cli/commands/follow.js +43 -0
  92. package/dist/cli/commands/mining-admin.d.ts +2 -0
  93. package/dist/cli/commands/mining-admin.js +92 -0
  94. package/dist/cli/commands/mining-read.d.ts +2 -0
  95. package/dist/cli/commands/mining-read.js +173 -0
  96. package/dist/cli/commands/mining-runtime.d.ts +2 -0
  97. package/dist/cli/commands/mining-runtime.js +108 -0
  98. package/dist/cli/commands/status.d.ts +2 -0
  99. package/dist/cli/commands/status.js +31 -0
  100. package/dist/cli/commands/sync.d.ts +2 -0
  101. package/dist/cli/commands/sync.js +52 -0
  102. package/dist/cli/commands/wallet-admin.d.ts +2 -0
  103. package/dist/cli/commands/wallet-admin.js +175 -0
  104. package/dist/cli/commands/wallet-mutation.d.ts +2 -0
  105. package/dist/cli/commands/wallet-mutation.js +681 -0
  106. package/dist/cli/commands/wallet-read.d.ts +2 -0
  107. package/dist/cli/commands/wallet-read.js +265 -0
  108. package/dist/cli/context.d.ts +3 -0
  109. package/dist/cli/context.js +75 -0
  110. package/dist/cli/io.d.ts +3 -0
  111. package/dist/cli/io.js +12 -0
  112. package/dist/cli/mining-format.d.ts +5 -0
  113. package/dist/cli/mining-format.js +156 -0
  114. package/dist/cli/mining-json.d.ts +49 -0
  115. package/dist/cli/mining-json.js +89 -0
  116. package/dist/cli/mutation-command-groups.d.ts +15 -0
  117. package/dist/cli/mutation-command-groups.js +71 -0
  118. package/dist/cli/mutation-json.d.ts +430 -0
  119. package/dist/cli/mutation-json.js +311 -0
  120. package/dist/cli/mutation-resolved-json.d.ts +124 -0
  121. package/dist/cli/mutation-resolved-json.js +129 -0
  122. package/dist/cli/mutation-success.d.ts +20 -0
  123. package/dist/cli/mutation-success.js +47 -0
  124. package/dist/cli/mutation-text-format.d.ts +22 -0
  125. package/dist/cli/mutation-text-format.js +171 -0
  126. package/dist/cli/mutation-text-write.d.ts +13 -0
  127. package/dist/cli/mutation-text-write.js +16 -0
  128. package/dist/cli/output.d.ts +185 -0
  129. package/dist/cli/output.js +1085 -0
  130. package/dist/cli/parse.d.ts +3 -0
  131. package/dist/cli/parse.js +971 -0
  132. package/dist/cli/preview-json.d.ts +416 -0
  133. package/dist/cli/preview-json.js +293 -0
  134. package/dist/cli/prompt.d.ts +3 -0
  135. package/dist/cli/prompt.js +33 -0
  136. package/dist/cli/read-json.d.ts +187 -0
  137. package/dist/cli/read-json.js +675 -0
  138. package/dist/cli/runner.d.ts +2 -0
  139. package/dist/cli/runner.js +129 -0
  140. package/dist/cli/signals.d.ts +3 -0
  141. package/dist/cli/signals.js +63 -0
  142. package/dist/cli/status-format.d.ts +2 -0
  143. package/dist/cli/status-format.js +48 -0
  144. package/dist/cli/types.d.ts +148 -0
  145. package/dist/cli/types.js +2 -0
  146. package/dist/cli/wallet-format.d.ts +29 -0
  147. package/dist/cli/wallet-format.js +637 -0
  148. package/dist/cli/workflow-hints.d.ts +13 -0
  149. package/dist/cli/workflow-hints.js +94 -0
  150. package/dist/cli-runner.d.ts +3 -0
  151. package/dist/cli-runner.js +3 -0
  152. package/dist/cli.d.ts +2 -0
  153. package/dist/cli.js +6 -0
  154. package/dist/client/default-client.d.ts +11 -0
  155. package/dist/client/default-client.js +118 -0
  156. package/dist/client/factory.d.ts +2 -0
  157. package/dist/client/factory.js +15 -0
  158. package/dist/client/initialization.d.ts +6 -0
  159. package/dist/client/initialization.js +30 -0
  160. package/dist/client/persistence.d.ts +5 -0
  161. package/dist/client/persistence.js +28 -0
  162. package/dist/client/store-adapter.d.ts +3 -0
  163. package/dist/client/store-adapter.js +20 -0
  164. package/dist/client.d.ts +2 -0
  165. package/dist/client.js +2 -0
  166. package/dist/index.d.ts +2 -0
  167. package/dist/index.js +1 -0
  168. package/dist/passive-status.d.ts +36 -0
  169. package/dist/passive-status.js +100 -0
  170. package/dist/sqlite/better-sqlite3.d.ts +26 -0
  171. package/dist/sqlite/better-sqlite3.js +4 -0
  172. package/dist/sqlite/checkpoints.d.ts +11 -0
  173. package/dist/sqlite/checkpoints.js +27 -0
  174. package/dist/sqlite/driver.d.ts +17 -0
  175. package/dist/sqlite/driver.js +98 -0
  176. package/dist/sqlite/index.d.ts +4 -0
  177. package/dist/sqlite/index.js +9 -0
  178. package/dist/sqlite/migrate.d.ts +2 -0
  179. package/dist/sqlite/migrate.js +37 -0
  180. package/dist/sqlite/store.d.ts +3 -0
  181. package/dist/sqlite/store.js +122 -0
  182. package/dist/sqlite/tip-meta.d.ts +26 -0
  183. package/dist/sqlite/tip-meta.js +97 -0
  184. package/dist/sqlite/types.d.ts +10 -0
  185. package/dist/sqlite/types.js +1 -0
  186. package/dist/types.d.ts +55 -0
  187. package/dist/types.js +1 -0
  188. package/dist/wallet/archive.d.ts +4 -0
  189. package/dist/wallet/archive.js +39 -0
  190. package/dist/wallet/cogop/constants.d.ts +32 -0
  191. package/dist/wallet/cogop/constants.js +32 -0
  192. package/dist/wallet/cogop/index.d.ts +32 -0
  193. package/dist/wallet/cogop/index.js +213 -0
  194. package/dist/wallet/cogop/numeric.d.ts +3 -0
  195. package/dist/wallet/cogop/numeric.js +24 -0
  196. package/dist/wallet/cogop/scriptpubkey.d.ts +2 -0
  197. package/dist/wallet/cogop/scriptpubkey.js +13 -0
  198. package/dist/wallet/cogop/validate-name.d.ts +2 -0
  199. package/dist/wallet/cogop/validate-name.js +18 -0
  200. package/dist/wallet/fs/atomic.d.ts +6 -0
  201. package/dist/wallet/fs/atomic.js +46 -0
  202. package/dist/wallet/fs/lock.d.ts +19 -0
  203. package/dist/wallet/fs/lock.js +61 -0
  204. package/dist/wallet/fs/status-file.d.ts +1 -0
  205. package/dist/wallet/fs/status-file.js +4 -0
  206. package/dist/wallet/lifecycle.d.ts +193 -0
  207. package/dist/wallet/lifecycle.js +1475 -0
  208. package/dist/wallet/material.d.ts +45 -0
  209. package/dist/wallet/material.js +118 -0
  210. package/dist/wallet/mining/config.d.ts +18 -0
  211. package/dist/wallet/mining/config.js +44 -0
  212. package/dist/wallet/mining/constants.d.ts +24 -0
  213. package/dist/wallet/mining/constants.js +24 -0
  214. package/dist/wallet/mining/control.d.ts +53 -0
  215. package/dist/wallet/mining/control.js +758 -0
  216. package/dist/wallet/mining/coordination.d.ts +40 -0
  217. package/dist/wallet/mining/coordination.js +121 -0
  218. package/dist/wallet/mining/hook-protocol.d.ts +47 -0
  219. package/dist/wallet/mining/hook-protocol.js +161 -0
  220. package/dist/wallet/mining/hook-runner.d.ts +1 -0
  221. package/dist/wallet/mining/hook-runner.js +52 -0
  222. package/dist/wallet/mining/hooks.d.ts +38 -0
  223. package/dist/wallet/mining/hooks.js +520 -0
  224. package/dist/wallet/mining/index.d.ts +8 -0
  225. package/dist/wallet/mining/index.js +6 -0
  226. package/dist/wallet/mining/runner.d.ts +155 -0
  227. package/dist/wallet/mining/runner.js +2574 -0
  228. package/dist/wallet/mining/runtime-artifacts.d.ts +17 -0
  229. package/dist/wallet/mining/runtime-artifacts.js +166 -0
  230. package/dist/wallet/mining/sentences.d.ts +23 -0
  231. package/dist/wallet/mining/sentences.js +281 -0
  232. package/dist/wallet/mining/state.d.ts +9 -0
  233. package/dist/wallet/mining/state.js +75 -0
  234. package/dist/wallet/mining/types.d.ts +141 -0
  235. package/dist/wallet/mining/types.js +1 -0
  236. package/dist/wallet/mining/visualizer.d.ts +19 -0
  237. package/dist/wallet/mining/visualizer.js +134 -0
  238. package/dist/wallet/mining/worker-main.d.ts +1 -0
  239. package/dist/wallet/mining/worker-main.js +17 -0
  240. package/dist/wallet/read/context.d.ts +20 -0
  241. package/dist/wallet/read/context.js +532 -0
  242. package/dist/wallet/read/filter.d.ts +9 -0
  243. package/dist/wallet/read/filter.js +42 -0
  244. package/dist/wallet/read/index.d.ts +4 -0
  245. package/dist/wallet/read/index.js +3 -0
  246. package/dist/wallet/read/project.d.ts +11 -0
  247. package/dist/wallet/read/project.js +300 -0
  248. package/dist/wallet/read/types.d.ts +144 -0
  249. package/dist/wallet/read/types.js +1 -0
  250. package/dist/wallet/runtime.d.ts +26 -0
  251. package/dist/wallet/runtime.js +28 -0
  252. package/dist/wallet/state/crypto.d.ts +31 -0
  253. package/dist/wallet/state/crypto.js +127 -0
  254. package/dist/wallet/state/provider.d.ts +37 -0
  255. package/dist/wallet/state/provider.js +312 -0
  256. package/dist/wallet/state/session.d.ts +12 -0
  257. package/dist/wallet/state/session.js +23 -0
  258. package/dist/wallet/state/storage.d.ts +19 -0
  259. package/dist/wallet/state/storage.js +55 -0
  260. package/dist/wallet/tx/anchor.d.ts +40 -0
  261. package/dist/wallet/tx/anchor.js +1210 -0
  262. package/dist/wallet/tx/cog.d.ts +92 -0
  263. package/dist/wallet/tx/cog.js +1055 -0
  264. package/dist/wallet/tx/common.d.ts +89 -0
  265. package/dist/wallet/tx/common.js +156 -0
  266. package/dist/wallet/tx/confirm.d.ts +15 -0
  267. package/dist/wallet/tx/confirm.js +24 -0
  268. package/dist/wallet/tx/domain-admin.d.ts +105 -0
  269. package/dist/wallet/tx/domain-admin.js +869 -0
  270. package/dist/wallet/tx/domain-market.d.ts +112 -0
  271. package/dist/wallet/tx/domain-market.js +1365 -0
  272. package/dist/wallet/tx/field.d.ts +101 -0
  273. package/dist/wallet/tx/field.js +1853 -0
  274. package/dist/wallet/tx/identity-selector.d.ts +12 -0
  275. package/dist/wallet/tx/identity-selector.js +52 -0
  276. package/dist/wallet/tx/index.d.ts +7 -0
  277. package/dist/wallet/tx/index.js +7 -0
  278. package/dist/wallet/tx/journal.d.ts +5 -0
  279. package/dist/wallet/tx/journal.js +31 -0
  280. package/dist/wallet/tx/register.d.ts +68 -0
  281. package/dist/wallet/tx/register.js +952 -0
  282. package/dist/wallet/tx/reputation.d.ts +72 -0
  283. package/dist/wallet/tx/reputation.js +693 -0
  284. package/dist/wallet/tx/targets.d.ts +7 -0
  285. package/dist/wallet/tx/targets.js +122 -0
  286. package/dist/wallet/types.d.ts +249 -0
  287. package/dist/wallet/types.js +1 -0
  288. package/dist/writing_quotes.json +1654 -0
  289. package/package.json +78 -0
@@ -0,0 +1,520 @@
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
+ }
@@ -0,0 +1,8 @@
1
+ export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
2
+ export { disableMiningHooks, enableMiningHooks, followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
3
+ export { runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, type MiningStartResult, } from "./runner.js";
4
+ export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
5
+ export { ensureMiningHookTemplate, inspectMiningHookState, runGenerateSentencesHookRequest, validateCustomMiningHook, } from "./hooks.js";
6
+ export type { GenerateSentencesHookCandidateV1, GenerateSentencesHookRequestV1, GenerateSentencesHookResponseV1, MiningHookOperatorValidationState, } from "./hook-protocol.js";
7
+ export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";
8
+ export type { ClientConfigV1, MiningControlPlaneView, MiningEventRecord, MiningHookInspection, MiningProviderConfigRecord, MiningProviderInspection, MiningRuntimeStatusV1, MiningServiceHealth, } from "./types.js";
@@ -0,0 +1,6 @@
1
+ export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
2
+ export { disableMiningHooks, enableMiningHooks, followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
3
+ export { runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, } from "./runner.js";
4
+ export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
5
+ export { ensureMiningHookTemplate, inspectMiningHookState, runGenerateSentencesHookRequest, validateCustomMiningHook, } from "./hooks.js";
6
+ export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";
@@ -0,0 +1,155 @@
1
+ import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
2
+ import { createRpcClient } from "../../bitcoind/node.js";
3
+ import type { ProgressOutputMode } from "../../bitcoind/types.js";
4
+ import { type MutationSender, type WalletMutationRpcClient } from "../tx/common.js";
5
+ import { type WalletPrompter } from "../lifecycle.js";
6
+ import { openWalletReadContext } from "../read/index.js";
7
+ import { type WalletRuntimePaths } from "../runtime.js";
8
+ import { type WalletSecretProvider } from "../state/provider.js";
9
+ import type { MiningStateRecord } from "../types.js";
10
+ import type { MiningRuntimeStatusV1 } from "./types.js";
11
+ type MiningRpcClient = WalletMutationRpcClient & {
12
+ getBlockchainInfo(): Promise<{
13
+ blocks: number;
14
+ bestblockhash: string;
15
+ initialblockdownload?: boolean;
16
+ }>;
17
+ getNetworkInfo(): Promise<{
18
+ networkactive: boolean;
19
+ connections_out?: number;
20
+ }>;
21
+ getMempoolInfo(): Promise<{
22
+ loaded: boolean;
23
+ }>;
24
+ getRawMempool(): Promise<string[]>;
25
+ getRawMempoolVerbose(): Promise<{
26
+ txids: string[];
27
+ mempool_sequence: string | number;
28
+ }>;
29
+ getMempoolEntry(txid: string): Promise<{
30
+ vsize: number;
31
+ fees: {
32
+ base: number;
33
+ ancestor: number;
34
+ descendant: number;
35
+ };
36
+ ancestorsize?: number;
37
+ descendantsize?: number;
38
+ }>;
39
+ getRawTransaction(txid: string, verbose?: boolean): Promise<{
40
+ txid: string;
41
+ hash?: string;
42
+ vin: Array<{
43
+ txid?: string;
44
+ prevout?: {
45
+ scriptPubKey?: {
46
+ hex?: string;
47
+ };
48
+ };
49
+ }>;
50
+ vout: Array<{
51
+ n: number;
52
+ value: number | string;
53
+ scriptPubKey?: {
54
+ hex?: string;
55
+ };
56
+ }>;
57
+ }>;
58
+ getTransaction(walletName: string, txid: string): Promise<{
59
+ txid: string;
60
+ confirmations: number;
61
+ blockhash?: string;
62
+ walletconflicts?: string[];
63
+ }>;
64
+ sendRawTransaction(hex: string): Promise<string>;
65
+ saveMempool?(): Promise<null>;
66
+ };
67
+ interface RunnerDependencies {
68
+ openReadContext?: typeof openWalletReadContext;
69
+ attachService?: typeof attachOrStartManagedBitcoindService;
70
+ rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => MiningRpcClient;
71
+ fetchImpl?: typeof fetch;
72
+ }
73
+ export interface RunForegroundMiningOptions extends RunnerDependencies {
74
+ dataDir: string;
75
+ databasePath: string;
76
+ provider?: WalletSecretProvider;
77
+ prompter: WalletPrompter;
78
+ stdout?: {
79
+ write(chunk: string): void;
80
+ };
81
+ stderr?: {
82
+ isTTY?: boolean;
83
+ columns?: number;
84
+ write(chunk: string): boolean | void;
85
+ };
86
+ signal?: AbortSignal;
87
+ progressOutput?: ProgressOutputMode;
88
+ paths?: WalletRuntimePaths;
89
+ }
90
+ export interface StartBackgroundMiningOptions extends RunnerDependencies {
91
+ dataDir: string;
92
+ databasePath: string;
93
+ provider?: WalletSecretProvider;
94
+ prompter: WalletPrompter;
95
+ paths?: WalletRuntimePaths;
96
+ }
97
+ export interface StopBackgroundMiningOptions extends RunnerDependencies {
98
+ dataDir: string;
99
+ databasePath: string;
100
+ provider?: WalletSecretProvider;
101
+ paths?: WalletRuntimePaths;
102
+ }
103
+ export interface MiningStartResult {
104
+ started: boolean;
105
+ snapshot: MiningRuntimeStatusV1 | null;
106
+ }
107
+ export declare function runForegroundMining(options: RunForegroundMiningOptions): Promise<void>;
108
+ export declare function startBackgroundMining(options: StartBackgroundMiningOptions): Promise<MiningStartResult>;
109
+ export declare function stopBackgroundMining(options: StopBackgroundMiningOptions): Promise<MiningRuntimeStatusV1 | null>;
110
+ export declare function runBackgroundMiningWorker(options: RunnerDependencies & {
111
+ dataDir: string;
112
+ databasePath: string;
113
+ runId: string;
114
+ provider?: WalletSecretProvider;
115
+ paths?: WalletRuntimePaths;
116
+ }): Promise<void>;
117
+ export declare function handleDetectedMiningRuntimeResumeForTesting(options: {
118
+ dataDir: string;
119
+ databasePath: string;
120
+ provider: WalletSecretProvider;
121
+ paths: WalletRuntimePaths;
122
+ runMode: "foreground" | "background";
123
+ backgroundWorkerPid: number | null;
124
+ backgroundWorkerRunId: string | null;
125
+ detectedAtUnixMs: number;
126
+ openReadContext: typeof openWalletReadContext;
127
+ }): Promise<void>;
128
+ export declare function performMiningCycleForTesting(options: {
129
+ dataDir: string;
130
+ databasePath: string;
131
+ provider: WalletSecretProvider;
132
+ paths: WalletRuntimePaths;
133
+ runMode: "foreground" | "background";
134
+ backgroundWorkerPid: number | null;
135
+ backgroundWorkerRunId: string | null;
136
+ signal?: AbortSignal;
137
+ fetchImpl?: typeof fetch;
138
+ openReadContext: typeof openWalletReadContext;
139
+ attachService: typeof attachOrStartManagedBitcoindService;
140
+ rpcFactory: (config: Parameters<typeof createRpcClient>[0]) => MiningRpcClient;
141
+ stdout?: {
142
+ write(chunk: string): void;
143
+ };
144
+ }): Promise<void>;
145
+ export declare function shouldTreatCandidateAsFeeBumpForTesting(options: {
146
+ liveState: MiningStateRecord;
147
+ candidate: {
148
+ domainId: number;
149
+ sender: MutationSender;
150
+ encodedSentenceBytes: Uint8Array;
151
+ referencedBlockHashDisplay: string;
152
+ targetBlockHeight: number;
153
+ };
154
+ }): boolean;
155
+ export {};