@cogcoin/client 1.1.0 → 1.1.2

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.
@@ -18,6 +18,14 @@ function createBuiltInProviderNotFoundError(options) {
18
18
  : `${providerLabel} returned HTTP 404 for model "${options.model}". The configured model override may be invalid. Rerun \`cogcoin mine setup\` to clear or correct it.`;
19
19
  return new MiningProviderRequestError("not-found", message);
20
20
  }
21
+ function createBuiltInProviderTimeoutError(options) {
22
+ const providerName = options.provider === "anthropic" ? "Anthropic" : "OpenAI";
23
+ const seconds = Number.isInteger(options.timeoutMs / 1_000)
24
+ ? String(options.timeoutMs / 1_000)
25
+ : (options.timeoutMs / 1_000).toFixed(1);
26
+ const unit = seconds === "1" ? "second" : "seconds";
27
+ return new MiningProviderRequestError("unavailable", `The built-in ${providerName} mining provider timed out after ${seconds} ${unit}.`);
28
+ }
21
29
  function buildSystemPrompt(extraPrompt) {
22
30
  const lines = [
23
31
  "You are helping generate candidate Cogcoin mining sentences.",
@@ -70,12 +78,39 @@ function parseProviderJsonResponse(options) {
70
78
  }
71
79
  }
72
80
  function createProviderSignal(signal, timeoutMs) {
73
- const timeoutSignal = AbortSignal.timeout(timeoutMs);
74
- return signal === undefined ? timeoutSignal : AbortSignal.any([signal, timeoutSignal]);
81
+ const controller = new AbortController();
82
+ let didTimeout = false;
83
+ const timer = setTimeout(() => {
84
+ didTimeout = true;
85
+ controller.abort(new DOMException("The operation was aborted due to timeout", "TimeoutError"));
86
+ }, timeoutMs);
87
+ timer.unref?.();
88
+ const handleAbort = () => {
89
+ controller.abort(signal?.reason);
90
+ };
91
+ if (signal !== undefined) {
92
+ if (signal.aborted) {
93
+ handleAbort();
94
+ }
95
+ else {
96
+ signal.addEventListener("abort", handleAbort, { once: true });
97
+ }
98
+ }
99
+ return {
100
+ signal: controller.signal,
101
+ didTimeout() {
102
+ return didTimeout;
103
+ },
104
+ dispose() {
105
+ clearTimeout(timer);
106
+ signal?.removeEventListener("abort", handleAbort);
107
+ },
108
+ };
75
109
  }
76
110
  async function requestBuiltInSentences(options) {
77
111
  const fetchImpl = options.fetchImpl ?? fetch;
78
- const providerSignal = createProviderSignal(options.signal, Math.min(MINING_BUILTIN_TIMEOUT_MS, options.request.limits.timeoutMs));
112
+ const timeoutMs = Math.min(MINING_BUILTIN_TIMEOUT_MS, options.request.limits.timeoutMs);
113
+ const providerSignal = createProviderSignal(options.signal, timeoutMs);
79
114
  try {
80
115
  if (options.provider === "openai") {
81
116
  const { effectiveModel: model, usingDefaultModel } = resolveBuiltInProviderModel(options.provider, options.modelOverride);
@@ -98,7 +133,7 @@ async function requestBuiltInSentences(options) {
98
133
  },
99
134
  ],
100
135
  }),
101
- signal: providerSignal,
136
+ signal: providerSignal.signal,
102
137
  });
103
138
  if (response.status === 401 || response.status === 403) {
104
139
  throw new MiningProviderRequestError("auth-error", "The built-in OpenAI mining provider rejected the configured API key.");
@@ -145,7 +180,7 @@ async function requestBuiltInSentences(options) {
145
180
  },
146
181
  ],
147
182
  }),
148
- signal: providerSignal,
183
+ signal: providerSignal.signal,
149
184
  });
150
185
  if (response.status === 401 || response.status === 403) {
151
186
  throw new MiningProviderRequestError("auth-error", "The built-in Anthropic mining provider rejected the configured API key.");
@@ -177,11 +212,21 @@ async function requestBuiltInSentences(options) {
177
212
  if (error instanceof MiningProviderRequestError) {
178
213
  throw error;
179
214
  }
180
- if (error instanceof Error && error.name === "AbortError") {
181
- throw new MiningProviderRequestError("unavailable", "Mining sentence generation was aborted.");
215
+ if (providerSignal.didTimeout()) {
216
+ throw createBuiltInProviderTimeoutError({
217
+ provider: options.provider,
218
+ timeoutMs,
219
+ });
220
+ }
221
+ if (error instanceof Error
222
+ && (error.name === "AbortError" || error.name === "TimeoutError")) {
223
+ throw error;
182
224
  }
183
225
  throw new MiningProviderRequestError("unavailable", error instanceof Error ? error.message : String(error));
184
226
  }
227
+ finally {
228
+ providerSignal.dispose();
229
+ }
185
230
  }
186
231
  function extractOpenAiText(payload) {
187
232
  if (payload !== null && typeof payload === "object") {
@@ -30,6 +30,7 @@ export interface MiningFollowVisualizerState {
30
30
  settledBoardEntries: MiningSentenceBoardEntry[];
31
31
  provisionalRequiredWords: readonly string[];
32
32
  provisionalEntry: MiningProvisionalSentenceEntry;
33
+ provisionalBroadcastTxid: string | null;
33
34
  latestSentence: string | null;
34
35
  latestTxid: string | null;
35
36
  recentWin: MiningRecentWinSummary | null;
@@ -133,6 +133,16 @@ function formatRequiredWordsLine(words) {
133
133
  }
134
134
  return `Required words: ${words.map((word) => word.toUpperCase()).join(", ")}`;
135
135
  }
136
+ function formatProvisionalTxLinkLine(entry, txid) {
137
+ if (entry.domainName === null || entry.sentence === null || txid === null) {
138
+ return "";
139
+ }
140
+ const normalizedTxid = normalizeInlineText(txid);
141
+ if (normalizedTxid.length === 0) {
142
+ return "";
143
+ }
144
+ return `View at: https://mempool.space/tx/${normalizedTxid}`;
145
+ }
136
146
  function formatProvisionalSentenceRow(entry, requiredWords) {
137
147
  if (entry.domainName === null || entry.sentence === null) {
138
148
  return ["", "", ""];
@@ -151,6 +161,7 @@ export function createEmptyMiningFollowVisualizerState() {
151
161
  domainName: null,
152
162
  sentence: null,
153
163
  },
164
+ provisionalBroadcastTxid: null,
154
165
  latestSentence: null,
155
166
  latestTxid: null,
156
167
  recentWin: null,
@@ -173,6 +184,7 @@ function cloneMiningFollowVisualizerState(state) {
173
184
  provisionalEntry: {
174
185
  ...state.provisionalEntry,
175
186
  },
187
+ provisionalBroadcastTxid: state.provisionalBroadcastTxid,
176
188
  recentWin: state.recentWin === null
177
189
  ? null
178
190
  : {
@@ -375,6 +387,7 @@ export class MiningFollowVisualizer {
375
387
  : formatSentenceRow(entry);
376
388
  }).flat(),
377
389
  "----------",
390
+ formatProvisionalTxLinkLine(uiState.provisionalEntry, uiState.provisionalBroadcastTxid),
378
391
  formatRequiredWordsLine(uiState.provisionalRequiredWords),
379
392
  ...formatProvisionalSentenceRow(uiState.provisionalEntry, uiState.provisionalRequiredWords),
380
393
  ],
@@ -1,5 +1,6 @@
1
1
  import { access, constants } from "node:fs/promises";
2
2
  import { deserializeIndexerState, loadBundledGenesisParameters } from "@cogcoin/indexer";
3
+ import { readPackageVersionFromDisk } from "../../package-version.js";
3
4
  import { attachOrStartIndexerDaemon, INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
4
5
  import { createRpcClient } from "../../bitcoind/node.js";
5
6
  import { UNINITIALIZED_WALLET_ROOT_ID } from "../../bitcoind/service-paths.js";
@@ -495,6 +496,9 @@ async function readFundingSpendableSats(options) {
495
496
  }
496
497
  }
497
498
  export async function openWalletReadContext(options) {
499
+ const expectedIndexerBinaryVersion = options.expectedIndexerBinaryVersion === undefined
500
+ ? await readPackageVersionFromDisk()
501
+ : options.expectedIndexerBinaryVersion;
498
502
  const startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_SERVICE_START_TIMEOUT_MS;
499
503
  const now = options.now ?? Date.now();
500
504
  const localState = await inspectWalletLocalState({
@@ -545,7 +549,7 @@ export async function openWalletReadContext(options) {
545
549
  walletRootId,
546
550
  startupTimeoutMs,
547
551
  ensureBackgroundFollow: true,
548
- expectedBinaryVersion: options.expectedIndexerBinaryVersion,
552
+ expectedBinaryVersion: expectedIndexerBinaryVersion,
549
553
  });
550
554
  }
551
555
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogcoin/client",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Store-backed Cogcoin client with wallet flows, SQLite persistence, and managed Bitcoin Core integration.",
5
5
  "license": "MIT",
6
6
  "type": "module",