@cogcoin/client 1.0.1 → 1.1.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 +4 -2
- package/dist/bitcoind/client/factory.d.ts +0 -8
- package/dist/bitcoind/client/factory.js +1 -59
- package/dist/bitcoind/client/managed-client.d.ts +1 -3
- package/dist/bitcoind/client/managed-client.js +3 -47
- package/dist/bitcoind/indexer-daemon-main.js +173 -28
- package/dist/bitcoind/indexer-daemon.d.ts +14 -3
- package/dist/bitcoind/indexer-daemon.js +145 -29
- package/dist/bitcoind/indexer-monitor.d.ts +12 -0
- package/dist/bitcoind/indexer-monitor.js +89 -0
- package/dist/bitcoind/progress/follow-scene.d.ts +7 -1
- package/dist/bitcoind/progress/follow-scene.js +87 -4
- package/dist/bitcoind/progress/tty-renderer.d.ts +2 -0
- package/dist/bitcoind/progress/tty-renderer.js +2 -0
- package/dist/bitcoind/retryable-rpc.js +3 -0
- package/dist/bitcoind/service.d.ts +1 -0
- package/dist/bitcoind/service.js +31 -9
- package/dist/bitcoind/testing.d.ts +0 -1
- package/dist/bitcoind/testing.js +0 -1
- package/dist/bitcoind/types.d.ts +5 -2
- package/dist/cli/commands/follow.js +44 -49
- package/dist/cli/commands/mining-admin.js +65 -2
- package/dist/cli/commands/mining-read.js +43 -3
- package/dist/cli/commands/mining-runtime.js +91 -73
- package/dist/cli/commands/service-runtime.js +42 -2
- package/dist/cli/commands/status.js +3 -1
- package/dist/cli/commands/sync.js +50 -90
- package/dist/cli/commands/update.d.ts +2 -0
- package/dist/cli/commands/update.js +101 -0
- package/dist/cli/commands/wallet-admin.js +21 -3
- package/dist/cli/commands/wallet-read.js +2 -0
- package/dist/cli/context.js +36 -1
- package/dist/cli/managed-indexer-observer.d.ts +33 -0
- package/dist/cli/managed-indexer-observer.js +163 -0
- package/dist/cli/mining-format.d.ts +3 -1
- package/dist/cli/mining-format.js +63 -0
- package/dist/cli/mining-json.d.ts +11 -1
- package/dist/cli/mining-json.js +15 -0
- package/dist/cli/output.js +74 -2
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +28 -0
- package/dist/cli/prompt.js +109 -0
- package/dist/cli/read-json.d.ts +26 -1
- package/dist/cli/read-json.js +48 -0
- package/dist/cli/runner.js +8 -2
- package/dist/cli/signals.d.ts +12 -0
- package/dist/cli/signals.js +31 -13
- package/dist/cli/types.d.ts +13 -4
- package/dist/cli/update-notifier.js +7 -222
- package/dist/cli/update-service.d.ts +34 -0
- package/dist/cli/update-service.js +152 -0
- package/dist/client/initialization.js +5 -0
- package/dist/semver.d.ts +12 -0
- package/dist/semver.js +68 -0
- package/dist/wallet/lifecycle.d.ts +10 -0
- package/dist/wallet/mining/config.js +64 -3
- package/dist/wallet/mining/control.d.ts +5 -1
- package/dist/wallet/mining/control.js +269 -26
- package/dist/wallet/mining/domain-prompts.d.ts +17 -0
- package/dist/wallet/mining/domain-prompts.js +130 -0
- package/dist/wallet/mining/index.d.ts +2 -1
- package/dist/wallet/mining/index.js +1 -0
- package/dist/wallet/mining/provider-model.d.ts +30 -0
- package/dist/wallet/mining/provider-model.js +134 -0
- package/dist/wallet/mining/runner.d.ts +156 -5
- package/dist/wallet/mining/runner.js +1019 -399
- package/dist/wallet/mining/runtime-artifacts.js +1 -0
- package/dist/wallet/mining/sentence-protocol.d.ts +1 -0
- package/dist/wallet/mining/sentences.d.ts +2 -2
- package/dist/wallet/mining/sentences.js +32 -6
- package/dist/wallet/mining/types.d.ts +35 -1
- package/dist/wallet/mining/visualizer.d.ts +3 -0
- package/dist/wallet/mining/visualizer.js +132 -15
- package/dist/wallet/read/context.d.ts +1 -0
- package/dist/wallet/read/context.js +15 -7
- package/dist/wallet/state/client-password-agent.js +4 -1
- package/dist/wallet/state/client-password.js +15 -8
- package/dist/wallet/tx/common.js +1 -1
- package/package.json +3 -2
|
@@ -2,11 +2,14 @@ import { acquireFileLock } from "../fs/lock.js";
|
|
|
2
2
|
import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
|
|
3
3
|
import { createDefaultWalletSecretProvider, createWalletSecretReference, } from "../state/provider.js";
|
|
4
4
|
import { loadWalletState } from "../state/storage.js";
|
|
5
|
+
import { isRootDomainName } from "../read/filter.js";
|
|
5
6
|
import { appendMiningEvent, getLastMiningEventTimestamp, loadMiningRuntimeStatus, readMiningEvents, saveMiningRuntimeStatus, followMiningEvents, } from "./runtime-artifacts.js";
|
|
6
7
|
import { requestMiningGenerationPreemption } from "./coordination.js";
|
|
7
8
|
import { normalizeMiningPublishState, normalizeMiningStateRecord } from "./state.js";
|
|
8
9
|
import { loadClientConfig, saveBuiltInMiningProviderConfig } from "./config.js";
|
|
9
10
|
import { MINING_WORKER_API_VERSION, MINING_WORKER_HEARTBEAT_STALE_MS, } from "./constants.js";
|
|
11
|
+
import { estimateBuiltInModelDailyCost, getBuiltInProviderModelCatalog, getRecommendedBuiltInProviderModel, MINING_MODEL_DAILY_COST_ESTIMATE_ASSUMPTION, resolveBuiltInProviderSelection, } from "./provider-model.js";
|
|
12
|
+
const KEEP_CURRENT_MODEL_SELECTION = "__keep_current__";
|
|
10
13
|
function createMiningEvent(kind, message, options = {}) {
|
|
11
14
|
return {
|
|
12
15
|
schemaVersion: 1,
|
|
@@ -23,8 +26,14 @@ function buildProviderInspection(options) {
|
|
|
23
26
|
provider: null,
|
|
24
27
|
status: "error",
|
|
25
28
|
message: options.error,
|
|
29
|
+
modelId: null,
|
|
30
|
+
effectiveModel: null,
|
|
26
31
|
modelOverride: null,
|
|
32
|
+
modelSelectionSource: null,
|
|
33
|
+
usingDefaultModel: null,
|
|
27
34
|
extraPromptConfigured: false,
|
|
35
|
+
estimatedDailyCostUsd: null,
|
|
36
|
+
estimatedDailyCostDisplay: null,
|
|
28
37
|
};
|
|
29
38
|
}
|
|
30
39
|
if (options.config === null) {
|
|
@@ -33,19 +42,50 @@ function buildProviderInspection(options) {
|
|
|
33
42
|
provider: null,
|
|
34
43
|
status: "missing",
|
|
35
44
|
message: "Built-in mining provider is not configured yet.",
|
|
45
|
+
modelId: null,
|
|
46
|
+
effectiveModel: null,
|
|
36
47
|
modelOverride: null,
|
|
48
|
+
modelSelectionSource: null,
|
|
49
|
+
usingDefaultModel: null,
|
|
37
50
|
extraPromptConfigured: false,
|
|
51
|
+
estimatedDailyCostUsd: null,
|
|
52
|
+
estimatedDailyCostDisplay: null,
|
|
38
53
|
};
|
|
39
54
|
}
|
|
55
|
+
const selection = resolveBuiltInProviderSelection(options.config);
|
|
56
|
+
const estimate = options.eligibleRootCount === null
|
|
57
|
+
? null
|
|
58
|
+
: estimateBuiltInModelDailyCost(options.config.provider, selection.modelId, options.eligibleRootCount);
|
|
40
59
|
return {
|
|
41
60
|
configured: true,
|
|
42
61
|
provider: options.config.provider,
|
|
43
62
|
status: "ready",
|
|
44
63
|
message: null,
|
|
64
|
+
modelId: selection.modelId,
|
|
65
|
+
effectiveModel: selection.effectiveModel,
|
|
45
66
|
modelOverride: options.config.modelOverride,
|
|
67
|
+
modelSelectionSource: selection.modelSelectionSource,
|
|
68
|
+
usingDefaultModel: selection.usingDefaultModel,
|
|
46
69
|
extraPromptConfigured: options.config.extraPrompt !== null && options.config.extraPrompt.length > 0,
|
|
70
|
+
estimatedDailyCostUsd: estimate?.estimatedDailyCostUsd ?? null,
|
|
71
|
+
estimatedDailyCostDisplay: estimate?.estimatedDailyCostDisplay ?? null,
|
|
47
72
|
};
|
|
48
73
|
}
|
|
74
|
+
function countEligibleAnchoredRoots(localState) {
|
|
75
|
+
const state = localState.state;
|
|
76
|
+
if (state === null || state === undefined) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
let count = 0;
|
|
80
|
+
for (const domain of state.domains) {
|
|
81
|
+
if (isRootDomainName(domain.name)
|
|
82
|
+
&& domain.canonicalChainStatus === "anchored"
|
|
83
|
+
&& domain.currentOwnerScriptPubKeyHex === state.funding.scriptPubKeyHex) {
|
|
84
|
+
count += 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return count;
|
|
88
|
+
}
|
|
49
89
|
async function isProcessAlive(pid) {
|
|
50
90
|
if (pid === null) {
|
|
51
91
|
return false;
|
|
@@ -61,10 +101,13 @@ async function isProcessAlive(pid) {
|
|
|
61
101
|
return true;
|
|
62
102
|
}
|
|
63
103
|
}
|
|
64
|
-
function mapProviderState(provider, localState) {
|
|
104
|
+
function mapProviderState(provider, localState, existingRuntime) {
|
|
65
105
|
const miningState = localState.state?.miningState === undefined
|
|
66
106
|
? null
|
|
67
107
|
: normalizeMiningStateRecord(localState.state.miningState);
|
|
108
|
+
if (existingRuntime?.currentPhase === "waiting-provider" && existingRuntime.providerState !== null) {
|
|
109
|
+
return existingRuntime.providerState;
|
|
110
|
+
}
|
|
68
111
|
if (miningState?.state === "paused" && miningState.pauseReason?.includes("rate-limit")) {
|
|
69
112
|
return "rate-limited";
|
|
70
113
|
}
|
|
@@ -167,7 +210,7 @@ async function buildMiningRuntimeSnapshot(options) {
|
|
|
167
210
|
localState: options.localState,
|
|
168
211
|
nowUnixMs: options.nowUnixMs,
|
|
169
212
|
});
|
|
170
|
-
const providerState = mapProviderState(options.provider, options.localState);
|
|
213
|
+
const providerState = mapProviderState(options.provider, options.localState, options.existingRuntime);
|
|
171
214
|
const indexerDaemonState = mapIndexerDaemonState(options.indexer);
|
|
172
215
|
const corePublishState = mapCorePublishState(options.nodeHealth, options.nodeStatus);
|
|
173
216
|
const existing = options.existingRuntime;
|
|
@@ -289,7 +332,10 @@ export async function inspectMiningControlPlane(options) {
|
|
|
289
332
|
paths,
|
|
290
333
|
provider,
|
|
291
334
|
});
|
|
292
|
-
const providerInspection = buildProviderInspection(
|
|
335
|
+
const providerInspection = buildProviderInspection({
|
|
336
|
+
...providerConfig,
|
|
337
|
+
eligibleRootCount: countEligibleAnchoredRoots(options.localState),
|
|
338
|
+
});
|
|
293
339
|
const existingRuntime = await loadMiningRuntimeStatus(paths.miningStatusPath).catch(() => null);
|
|
294
340
|
const lastEventAtUnixMs = await getLastMiningEventTimestamp(paths.miningEventsPath).catch(() => null);
|
|
295
341
|
const nodeBestHeight = options.nodeStatus?.nodeBestHeight ?? null;
|
|
@@ -323,6 +369,20 @@ function normalizeProviderChoice(raw) {
|
|
|
323
369
|
const value = raw.trim().toLowerCase();
|
|
324
370
|
return value === "openai" || value === "anthropic" ? value : null;
|
|
325
371
|
}
|
|
372
|
+
function describeModelSelectionSource(source) {
|
|
373
|
+
switch (source) {
|
|
374
|
+
case "catalog":
|
|
375
|
+
return "catalog";
|
|
376
|
+
case "custom":
|
|
377
|
+
return "custom";
|
|
378
|
+
case "legacy-default":
|
|
379
|
+
return "legacy-default";
|
|
380
|
+
case "legacy-custom":
|
|
381
|
+
return "legacy-custom";
|
|
382
|
+
default:
|
|
383
|
+
throw new Error(`unsupported_model_selection_source:${String(source)}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
326
386
|
function writeBuiltInMiningProviderDisclosure(prompter) {
|
|
327
387
|
prompter.writeLine("Built-in mining provider disclosure:");
|
|
328
388
|
prompter.writeLine("The built-in mining provider will send the following to the selected provider:");
|
|
@@ -332,27 +392,194 @@ function writeBuiltInMiningProviderDisclosure(prompter) {
|
|
|
332
392
|
prompter.writeLine("- referenced previous-block hash");
|
|
333
393
|
prompter.writeLine("- optional extra prompt when configured");
|
|
334
394
|
}
|
|
335
|
-
async function
|
|
395
|
+
async function promptForMiningProviderModelSelectionFallback(prompter, options) {
|
|
396
|
+
prompter.writeLine(options.message);
|
|
397
|
+
for (const [index, option] of options.options.entries()) {
|
|
398
|
+
const description = option.description == null || option.description.length === 0
|
|
399
|
+
? ""
|
|
400
|
+
: ` - ${option.description}`;
|
|
401
|
+
prompter.writeLine(`${index + 1}. ${option.label}${description}`);
|
|
402
|
+
}
|
|
403
|
+
if (options.footer != null && options.footer.length > 0) {
|
|
404
|
+
prompter.writeLine(options.footer);
|
|
405
|
+
}
|
|
406
|
+
while (true) {
|
|
407
|
+
const answer = (await prompter.prompt(`Choice [1-${options.options.length}]: `)).trim();
|
|
408
|
+
if (/^(q|quit|esc|escape)$/i.test(answer)) {
|
|
409
|
+
throw new Error("mining_setup_canceled");
|
|
410
|
+
}
|
|
411
|
+
const choice = Number.parseInt(answer, 10);
|
|
412
|
+
if (Number.isInteger(choice) && choice >= 1 && choice <= options.options.length) {
|
|
413
|
+
return options.options[choice - 1].value;
|
|
414
|
+
}
|
|
415
|
+
prompter.writeLine(`Enter a number from 1 to ${options.options.length}, or q to cancel.`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function formatMiningProviderDisplayName(provider) {
|
|
419
|
+
return provider === "openai" ? "OpenAI" : "Anthropic";
|
|
420
|
+
}
|
|
421
|
+
async function promptMiningSetupYesNo(prompter, message, defaultAnswer) {
|
|
422
|
+
const prompt = `${message}${defaultAnswer ? " [Y/n]: " : " [y/N]: "}`;
|
|
423
|
+
while (true) {
|
|
424
|
+
const answer = (await prompter.prompt(prompt)).trim().toLowerCase();
|
|
425
|
+
if (answer.length === 0) {
|
|
426
|
+
return defaultAnswer;
|
|
427
|
+
}
|
|
428
|
+
if (answer === "y" || answer === "yes") {
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
if (answer === "n" || answer === "no") {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
if (answer === "q" || answer === "quit" || answer === "esc" || answer === "escape") {
|
|
435
|
+
throw new Error("mining_setup_canceled");
|
|
436
|
+
}
|
|
437
|
+
prompter.writeLine("Enter y or n, or q to cancel.");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function buildMiningProviderModelSelectorOptions(provider, eligibleRootCount, currentConfig) {
|
|
441
|
+
const catalogOptions = getBuiltInProviderModelCatalog(provider).map((entry) => {
|
|
442
|
+
const estimate = estimateBuiltInModelDailyCost(provider, entry.modelId, eligibleRootCount);
|
|
443
|
+
return {
|
|
444
|
+
label: entry.label,
|
|
445
|
+
description: `${entry.modelId} - ${estimate?.estimatedDailyCostDisplay ?? "n/a"}`,
|
|
446
|
+
value: entry.modelId,
|
|
447
|
+
};
|
|
448
|
+
});
|
|
449
|
+
const selection = currentConfig === null ? null : resolveBuiltInProviderSelection(currentConfig);
|
|
450
|
+
const options = [...catalogOptions];
|
|
451
|
+
let initialValue = getRecommendedBuiltInProviderModel(provider);
|
|
452
|
+
if (selection !== null) {
|
|
453
|
+
if (selection.modelSelectionSource === "custom" || selection.modelSelectionSource === "legacy-custom") {
|
|
454
|
+
initialValue = "custom";
|
|
455
|
+
}
|
|
456
|
+
else if (catalogOptions.some((entry) => entry.value === selection.modelId)) {
|
|
457
|
+
initialValue = selection.modelId;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
options.push({
|
|
461
|
+
label: "Current configured model",
|
|
462
|
+
description: `${selection.modelId} - current saved setting`,
|
|
463
|
+
value: KEEP_CURRENT_MODEL_SELECTION,
|
|
464
|
+
});
|
|
465
|
+
initialValue = KEEP_CURRENT_MODEL_SELECTION;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
options.push({
|
|
469
|
+
label: "Custom model ID...",
|
|
470
|
+
description: "",
|
|
471
|
+
value: "custom",
|
|
472
|
+
});
|
|
473
|
+
return {
|
|
474
|
+
initialValue,
|
|
475
|
+
options,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
async function promptForMiningProviderConfig(prompter, eligibleRootCount, options = {}) {
|
|
336
479
|
writeBuiltInMiningProviderDisclosure(prompter);
|
|
337
|
-
const
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
480
|
+
const currentConfig = options.currentConfig ?? null;
|
|
481
|
+
const rememberedConfigs = options.rememberedConfigs ?? {};
|
|
482
|
+
let provider;
|
|
483
|
+
let rememberedConfig = currentConfig;
|
|
484
|
+
let reuseSavedApiKey = false;
|
|
485
|
+
if (currentConfig !== null) {
|
|
486
|
+
const useDifferentProviderOrApiKey = await promptMiningSetupYesNo(prompter, "Use a different provider or API key?", false);
|
|
487
|
+
if (!useDifferentProviderOrApiKey) {
|
|
488
|
+
provider = currentConfig.provider;
|
|
489
|
+
reuseSavedApiKey = true;
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
const providerInput = await prompter.prompt("Provider (openai/anthropic): ");
|
|
493
|
+
const selectedProvider = normalizeProviderChoice(providerInput);
|
|
494
|
+
if (selectedProvider === null) {
|
|
495
|
+
throw new Error("mining_setup_invalid_provider");
|
|
496
|
+
}
|
|
497
|
+
provider = selectedProvider;
|
|
498
|
+
rememberedConfig = rememberedConfigs[provider] ?? null;
|
|
499
|
+
if (rememberedConfig !== null) {
|
|
500
|
+
reuseSavedApiKey = await promptMiningSetupYesNo(prompter, `Use saved ${formatMiningProviderDisplayName(provider)} API key?`, true);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
const providerInput = await prompter.prompt("Provider (openai/anthropic): ");
|
|
506
|
+
const selectedProvider = normalizeProviderChoice(providerInput);
|
|
507
|
+
if (selectedProvider === null) {
|
|
508
|
+
throw new Error("mining_setup_invalid_provider");
|
|
509
|
+
}
|
|
510
|
+
provider = selectedProvider;
|
|
511
|
+
rememberedConfig = rememberedConfigs[provider] ?? null;
|
|
512
|
+
if (rememberedConfig !== null) {
|
|
513
|
+
reuseSavedApiKey = await promptMiningSetupYesNo(prompter, `Use saved ${formatMiningProviderDisplayName(provider)} API key?`, true);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const selectorModelOptions = buildMiningProviderModelSelectorOptions(provider, eligibleRootCount, rememberedConfig);
|
|
517
|
+
const selectorOptions = {
|
|
518
|
+
message: "Choose the mining model:",
|
|
519
|
+
options: selectorModelOptions.options,
|
|
520
|
+
initialValue: selectorModelOptions.initialValue,
|
|
521
|
+
footer: MINING_MODEL_DAILY_COST_ESTIMATE_ASSUMPTION,
|
|
522
|
+
};
|
|
523
|
+
const selectedModelId = prompter.selectOption == null
|
|
524
|
+
? await promptForMiningProviderModelSelectionFallback(prompter, selectorOptions)
|
|
525
|
+
: await prompter.selectOption(selectorOptions);
|
|
526
|
+
let modelSelectionSource;
|
|
527
|
+
let modelOverride;
|
|
528
|
+
if (selectedModelId === KEEP_CURRENT_MODEL_SELECTION) {
|
|
529
|
+
if (rememberedConfig === null) {
|
|
530
|
+
throw new Error("mining_setup_missing_model_id");
|
|
531
|
+
}
|
|
532
|
+
modelSelectionSource = rememberedConfig.modelSelectionSource;
|
|
533
|
+
modelOverride = rememberedConfig.modelOverride;
|
|
534
|
+
}
|
|
535
|
+
else if (selectedModelId === "custom") {
|
|
536
|
+
const currentCustomModel = rememberedConfig !== null
|
|
537
|
+
&& (rememberedConfig.modelSelectionSource === "custom" || rememberedConfig.modelSelectionSource === "legacy-custom")
|
|
538
|
+
? rememberedConfig.modelOverride
|
|
539
|
+
: null;
|
|
540
|
+
const customModelId = (await prompter.prompt(currentCustomModel === null
|
|
541
|
+
? "Custom model ID: "
|
|
542
|
+
: `Custom model ID (blank to keep current: ${currentCustomModel}): `)).trim();
|
|
543
|
+
if (customModelId.length === 0) {
|
|
544
|
+
if (currentCustomModel === null) {
|
|
545
|
+
throw new Error("mining_setup_missing_model_id");
|
|
546
|
+
}
|
|
547
|
+
modelSelectionSource = rememberedConfig?.modelSelectionSource ?? "custom";
|
|
548
|
+
modelOverride = currentCustomModel;
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
modelSelectionSource = "custom";
|
|
552
|
+
modelOverride = customModelId;
|
|
553
|
+
}
|
|
341
554
|
}
|
|
342
|
-
|
|
555
|
+
else {
|
|
556
|
+
modelSelectionSource = "catalog";
|
|
557
|
+
modelOverride = selectedModelId;
|
|
558
|
+
}
|
|
559
|
+
const apiKey = reuseSavedApiKey && rememberedConfig !== null
|
|
560
|
+
? rememberedConfig.apiKey
|
|
561
|
+
: (await prompter.prompt("API key: ")).trim();
|
|
343
562
|
if (apiKey.length === 0) {
|
|
344
563
|
throw new Error("mining_setup_missing_api_key");
|
|
345
564
|
}
|
|
346
|
-
const
|
|
347
|
-
|
|
565
|
+
const extraPromptInput = (await prompter.prompt(rememberedConfig === null
|
|
566
|
+
? "Extra prompt (optional, blank for none): "
|
|
567
|
+
: `Extra prompt (optional, blank to keep current: ${rememberedConfig.extraPrompt ?? "none"}): `)).trim();
|
|
568
|
+
const extraPrompt = extraPromptInput.length === 0
|
|
569
|
+
? rememberedConfig?.extraPrompt ?? null
|
|
570
|
+
: extraPromptInput;
|
|
348
571
|
return {
|
|
349
572
|
provider,
|
|
350
573
|
apiKey,
|
|
351
|
-
extraPrompt: extraPrompt.length === 0 ? null : extraPrompt,
|
|
352
|
-
modelOverride
|
|
574
|
+
extraPrompt: extraPrompt === null || extraPrompt.length === 0 ? null : extraPrompt,
|
|
575
|
+
modelOverride,
|
|
576
|
+
modelSelectionSource,
|
|
353
577
|
updatedAtUnixMs: Date.now(),
|
|
354
578
|
};
|
|
355
579
|
}
|
|
580
|
+
export async function promptForMiningProviderConfigForTesting(prompter, eligibleRootCount, options = {}) {
|
|
581
|
+
return await promptForMiningProviderConfig(prompter, eligibleRootCount, options);
|
|
582
|
+
}
|
|
356
583
|
export async function setupBuiltInMining(options) {
|
|
357
584
|
if (!options.prompter.isInteractive) {
|
|
358
585
|
throw new Error("mine_setup_requires_tty");
|
|
@@ -375,9 +602,28 @@ export async function setupBuiltInMining(options) {
|
|
|
375
602
|
}, {
|
|
376
603
|
provider,
|
|
377
604
|
});
|
|
605
|
+
const localState = {
|
|
606
|
+
availability: "ready",
|
|
607
|
+
clientPasswordReadiness: "ready",
|
|
608
|
+
unlockRequired: false,
|
|
609
|
+
walletRootId: loaded.state.walletRootId,
|
|
610
|
+
state: loaded.state,
|
|
611
|
+
source: loaded.source,
|
|
612
|
+
hasPrimaryStateFile: true,
|
|
613
|
+
hasBackupStateFile: true,
|
|
614
|
+
message: null,
|
|
615
|
+
};
|
|
616
|
+
const eligibleRootCount = countEligibleAnchoredRoots(localState) ?? 0;
|
|
617
|
+
const clientConfig = await loadClientConfig({
|
|
618
|
+
path: paths.clientConfigPath,
|
|
619
|
+
provider,
|
|
620
|
+
}).catch(() => null);
|
|
378
621
|
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-started", "Started built-in mining provider setup.", { timestampUnixMs: nowUnixMs }));
|
|
379
622
|
try {
|
|
380
|
-
const config = await promptForMiningProviderConfig(options.prompter
|
|
623
|
+
const config = await promptForMiningProviderConfig(options.prompter, eligibleRootCount, {
|
|
624
|
+
currentConfig: clientConfig?.mining.builtIn ?? null,
|
|
625
|
+
rememberedConfigs: clientConfig?.mining.builtInByProvider ?? {},
|
|
626
|
+
});
|
|
381
627
|
config.updatedAtUnixMs = nowUnixMs;
|
|
382
628
|
await saveBuiltInMiningProviderConfig({
|
|
383
629
|
path: paths.clientConfigPath,
|
|
@@ -385,20 +631,10 @@ export async function setupBuiltInMining(options) {
|
|
|
385
631
|
secretReference: createWalletSecretReference(loaded.state.walletRootId),
|
|
386
632
|
config,
|
|
387
633
|
});
|
|
388
|
-
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-completed", `Configured the built-in ${config.provider} mining provider.`, { timestampUnixMs: nowUnixMs }));
|
|
634
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-completed", `Configured the built-in ${config.provider} mining provider with model ${config.modelOverride} (${describeModelSelectionSource(config.modelSelectionSource)}).`, { timestampUnixMs: nowUnixMs }));
|
|
389
635
|
return refreshMiningRuntimeStatus({
|
|
390
636
|
provider,
|
|
391
|
-
localState
|
|
392
|
-
availability: "ready",
|
|
393
|
-
clientPasswordReadiness: "ready",
|
|
394
|
-
unlockRequired: false,
|
|
395
|
-
walletRootId: loaded.state.walletRootId,
|
|
396
|
-
state: loaded.state,
|
|
397
|
-
source: loaded.source,
|
|
398
|
-
hasPrimaryStateFile: true,
|
|
399
|
-
hasBackupStateFile: true,
|
|
400
|
-
message: null,
|
|
401
|
-
},
|
|
637
|
+
localState,
|
|
402
638
|
bitcoind: {
|
|
403
639
|
health: "unavailable",
|
|
404
640
|
status: null,
|
|
@@ -421,6 +657,13 @@ export async function setupBuiltInMining(options) {
|
|
|
421
657
|
});
|
|
422
658
|
}
|
|
423
659
|
catch (error) {
|
|
660
|
+
if (error instanceof Error && error.message === "mining_setup_canceled") {
|
|
661
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-canceled", "Canceled built-in mining provider setup.", {
|
|
662
|
+
level: "warn",
|
|
663
|
+
timestampUnixMs: nowUnixMs,
|
|
664
|
+
}));
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
424
667
|
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-failed", error instanceof Error ? error.message : String(error), {
|
|
425
668
|
level: "error",
|
|
426
669
|
timestampUnixMs: nowUnixMs,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WalletReadContext } from "../read/index.js";
|
|
2
|
+
import { type WalletSecretProvider } from "../state/provider.js";
|
|
3
|
+
import type { WalletRuntimePaths } from "../runtime.js";
|
|
4
|
+
import type { MiningDomainPromptListResult, MiningDomainPromptMutationResult } from "./types.js";
|
|
5
|
+
export declare function canonicalizeMiningDomainPromptName(domainName: string): string;
|
|
6
|
+
export declare function inspectMiningDomainPromptState(options: {
|
|
7
|
+
paths: WalletRuntimePaths;
|
|
8
|
+
provider: WalletSecretProvider;
|
|
9
|
+
readContext: WalletReadContext;
|
|
10
|
+
}): Promise<MiningDomainPromptListResult>;
|
|
11
|
+
export declare function updateMiningDomainPrompt(options: {
|
|
12
|
+
paths: WalletRuntimePaths;
|
|
13
|
+
provider: WalletSecretProvider;
|
|
14
|
+
readContext: WalletReadContext;
|
|
15
|
+
domainName: string;
|
|
16
|
+
prompt: string | null;
|
|
17
|
+
}): Promise<MiningDomainPromptMutationResult>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { findWalletDomain, isMineableWalletDomain } from "../read/index.js";
|
|
2
|
+
import { createWalletSecretReference } from "../state/provider.js";
|
|
3
|
+
import { loadClientConfig, saveClientConfig } from "./config.js";
|
|
4
|
+
function createEmptyClientConfig() {
|
|
5
|
+
return {
|
|
6
|
+
schemaVersion: 1,
|
|
7
|
+
mining: {
|
|
8
|
+
builtIn: null,
|
|
9
|
+
domainExtraPrompts: {},
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function canonicalizeMiningDomainPromptName(domainName) {
|
|
14
|
+
return domainName.trim().toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
function fallbackPromptConfigured(config) {
|
|
17
|
+
const extraPrompt = config?.mining.builtIn?.extraPrompt ?? null;
|
|
18
|
+
return extraPrompt !== null && extraPrompt.length > 0;
|
|
19
|
+
}
|
|
20
|
+
function listMineableDomains(readContext) {
|
|
21
|
+
const domains = new Map();
|
|
22
|
+
for (const domain of readContext.model?.domains ?? []) {
|
|
23
|
+
if (!isMineableWalletDomain(readContext, domain)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
domains.set(canonicalizeMiningDomainPromptName(domain.name), {
|
|
27
|
+
name: domain.name,
|
|
28
|
+
domainId: domain.domainId,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return domains;
|
|
32
|
+
}
|
|
33
|
+
function buildPromptEntries(options) {
|
|
34
|
+
const entries = new Map();
|
|
35
|
+
const mineableDomains = listMineableDomains(options.readContext);
|
|
36
|
+
for (const domain of mineableDomains.values()) {
|
|
37
|
+
const canonicalDomainName = canonicalizeMiningDomainPromptName(domain.name);
|
|
38
|
+
const prompt = options.domainExtraPrompts[canonicalDomainName] ?? null;
|
|
39
|
+
entries.set(canonicalDomainName, {
|
|
40
|
+
domain,
|
|
41
|
+
mineable: true,
|
|
42
|
+
prompt,
|
|
43
|
+
effectivePromptSource: prompt !== null
|
|
44
|
+
? "domain"
|
|
45
|
+
: options.fallbackPromptConfigured
|
|
46
|
+
? "global-fallback"
|
|
47
|
+
: "none",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
for (const [domainName, prompt] of Object.entries(options.domainExtraPrompts)) {
|
|
51
|
+
if (entries.has(domainName)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const found = findWalletDomain(options.readContext, domainName);
|
|
55
|
+
entries.set(domainName, {
|
|
56
|
+
domain: {
|
|
57
|
+
name: domainName,
|
|
58
|
+
domainId: found?.domain.domainId ?? null,
|
|
59
|
+
},
|
|
60
|
+
mineable: false,
|
|
61
|
+
prompt,
|
|
62
|
+
effectivePromptSource: "domain",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return [...entries.values()].sort((left, right) => left.domain.name.localeCompare(right.domain.name));
|
|
66
|
+
}
|
|
67
|
+
function isMineableTarget(readContext, domainName) {
|
|
68
|
+
const domain = readContext.model?.domains.find((entry) => canonicalizeMiningDomainPromptName(entry.name) === domainName);
|
|
69
|
+
return domain === undefined ? false : isMineableWalletDomain(readContext, domain);
|
|
70
|
+
}
|
|
71
|
+
export async function inspectMiningDomainPromptState(options) {
|
|
72
|
+
const config = await loadClientConfig({
|
|
73
|
+
path: options.paths.clientConfigPath,
|
|
74
|
+
provider: options.provider,
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
fallbackPromptConfigured: fallbackPromptConfigured(config),
|
|
78
|
+
prompts: buildPromptEntries({
|
|
79
|
+
readContext: options.readContext,
|
|
80
|
+
domainExtraPrompts: config?.mining.domainExtraPrompts ?? {},
|
|
81
|
+
fallbackPromptConfigured: fallbackPromptConfigured(config),
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export async function updateMiningDomainPrompt(options) {
|
|
86
|
+
const canonicalDomainName = canonicalizeMiningDomainPromptName(options.domainName);
|
|
87
|
+
const config = await loadClientConfig({
|
|
88
|
+
path: options.paths.clientConfigPath,
|
|
89
|
+
provider: options.provider,
|
|
90
|
+
});
|
|
91
|
+
const currentPrompts = {
|
|
92
|
+
...(config?.mining.domainExtraPrompts ?? {}),
|
|
93
|
+
};
|
|
94
|
+
const existingPrompt = currentPrompts[canonicalDomainName] ?? null;
|
|
95
|
+
const mineable = isMineableTarget(options.readContext, canonicalDomainName);
|
|
96
|
+
const found = findWalletDomain(options.readContext, canonicalDomainName);
|
|
97
|
+
const nextPrompt = options.prompt === null || options.prompt.trim().length === 0
|
|
98
|
+
? null
|
|
99
|
+
: options.prompt.trim();
|
|
100
|
+
if (!mineable && existingPrompt === null) {
|
|
101
|
+
throw new Error("mine_prompt_domain_not_mineable");
|
|
102
|
+
}
|
|
103
|
+
if (options.readContext.localState.walletRootId === null) {
|
|
104
|
+
throw new Error("wallet_uninitialized");
|
|
105
|
+
}
|
|
106
|
+
if (nextPrompt === null) {
|
|
107
|
+
delete currentPrompts[canonicalDomainName];
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
currentPrompts[canonicalDomainName] = nextPrompt;
|
|
111
|
+
}
|
|
112
|
+
const nextConfig = config ?? createEmptyClientConfig();
|
|
113
|
+
nextConfig.mining.domainExtraPrompts = currentPrompts;
|
|
114
|
+
await saveClientConfig({
|
|
115
|
+
path: options.paths.clientConfigPath,
|
|
116
|
+
provider: options.provider,
|
|
117
|
+
secretReference: createWalletSecretReference(options.readContext.localState.walletRootId),
|
|
118
|
+
config: nextConfig,
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
domain: {
|
|
122
|
+
name: canonicalDomainName,
|
|
123
|
+
domainId: found?.domain.domainId ?? null,
|
|
124
|
+
},
|
|
125
|
+
previousPrompt: existingPrompt,
|
|
126
|
+
prompt: nextPrompt,
|
|
127
|
+
status: nextPrompt === null ? "cleared" : "updated",
|
|
128
|
+
fallbackPromptConfigured: fallbackPromptConfigured(config),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
|
|
2
|
+
export { canonicalizeMiningDomainPromptName, inspectMiningDomainPromptState, updateMiningDomainPrompt, } from "./domain-prompts.js";
|
|
2
3
|
export { followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
|
|
3
4
|
export { ensureBuiltInMiningSetupIfNeeded, runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, type MiningStartResult, } from "./runner.js";
|
|
4
5
|
export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
|
|
5
6
|
export type { MiningSentenceCandidateV1, MiningSentenceGenerationRequestV1, MiningSentenceGenerationResponseV1, } from "./sentence-protocol.js";
|
|
6
7
|
export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";
|
|
7
|
-
export type { ClientConfigV1, MiningControlPlaneView, MiningEventRecord, MiningProviderConfigRecord, MiningProviderInspection, MiningRuntimeStatusV1, MiningServiceHealth, } from "./types.js";
|
|
8
|
+
export type { ClientConfigV1, MiningControlPlaneView, MiningDomainPromptEntry, MiningDomainPromptListResult, MiningDomainPromptMutationResult, MiningEventRecord, MiningModelSelectionSource, MiningProviderConfigRecord, MiningProviderInspection, MiningRuntimeStatusV1, MiningServiceHealth, } from "./types.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
|
|
2
|
+
export { canonicalizeMiningDomainPromptName, inspectMiningDomainPromptState, updateMiningDomainPrompt, } from "./domain-prompts.js";
|
|
2
3
|
export { followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
|
|
3
4
|
export { ensureBuiltInMiningSetupIfNeeded, runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, } from "./runner.js";
|
|
4
5
|
export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MiningModelSelectionSource, MiningProviderConfigRecord, MiningProviderKind } from "./types.js";
|
|
2
|
+
export interface BuiltInProviderModelCatalogEntry {
|
|
3
|
+
label: string;
|
|
4
|
+
modelId: string;
|
|
5
|
+
inputUsdPerMillionTokens: number;
|
|
6
|
+
outputUsdPerMillionTokens: number;
|
|
7
|
+
}
|
|
8
|
+
export interface BuiltInProviderSelection {
|
|
9
|
+
modelId: string;
|
|
10
|
+
effectiveModel: string;
|
|
11
|
+
modelSelectionSource: MiningModelSelectionSource;
|
|
12
|
+
usingDefaultModel: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface BuiltInModelDailyCostEstimate {
|
|
15
|
+
estimatedDailyCostUsd: number;
|
|
16
|
+
estimatedDailyCostDisplay: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const MINING_MODEL_DAILY_COST_ESTIMATE_ASSUMPTION = "Approximate daily cost assumes 144 sentence-generation calls/day using your current anchored root count, standard token pricing, no caching, and no extra prompt.";
|
|
19
|
+
export declare function getLegacyBuiltInProviderDefaultModel(provider: MiningProviderKind): string;
|
|
20
|
+
export declare function getRecommendedBuiltInProviderModel(provider: MiningProviderKind): string;
|
|
21
|
+
export declare function getBuiltInProviderModelCatalog(provider: MiningProviderKind): readonly BuiltInProviderModelCatalogEntry[];
|
|
22
|
+
export declare function findBuiltInProviderModelCatalogEntry(provider: MiningProviderKind, modelId: string): BuiltInProviderModelCatalogEntry | null;
|
|
23
|
+
export declare function normalizeMiningModelSelectionSource(raw: unknown, modelOverride: string | null): MiningModelSelectionSource;
|
|
24
|
+
export declare function normalizeMiningProviderConfigRecord(config: MiningProviderConfigRecord): MiningProviderConfigRecord;
|
|
25
|
+
export declare function resolveBuiltInProviderModel(provider: MiningProviderKind, modelOverride: string | null): {
|
|
26
|
+
effectiveModel: string;
|
|
27
|
+
usingDefaultModel: boolean;
|
|
28
|
+
};
|
|
29
|
+
export declare function resolveBuiltInProviderSelection(config: Pick<MiningProviderConfigRecord, "provider" | "modelOverride" | "modelSelectionSource">): BuiltInProviderSelection;
|
|
30
|
+
export declare function estimateBuiltInModelDailyCost(provider: MiningProviderKind, modelId: string, eligibleRootCount: number): BuiltInModelDailyCostEstimate | null;
|