@1sat/cli 0.0.80 → 0.0.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1sat/cli",
3
- "version": "0.0.80",
3
+ "version": "0.0.81",
4
4
  "description": "CLI for 1Sat Ordinals SDK",
5
5
  "type": "module",
6
6
  "main": "./src/cli.ts",
@@ -26,11 +26,11 @@
26
26
  ],
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@1sat/actions": "0.0.186",
29
+ "@1sat/actions": "0.0.187",
30
30
  "@1sat/client": "0.0.43",
31
31
  "@1sat/types": "0.0.34",
32
- "@1sat/wallet-node": "0.0.59",
33
- "@1sat/wallet-server": "0.0.28",
32
+ "@1sat/wallet-node": "0.0.60",
33
+ "@1sat/wallet-server": "0.0.29",
34
34
  "@bsv/sdk": "^2.1.6",
35
35
  "@bsv/wallet-toolbox": "2.1.24",
36
36
  "chalk": "^5.0.0",
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Storage maintenance commands — operate directly on the serve wallet's
3
+ * storage database (config under server.storage in config.json).
4
+ */
5
+
6
+ import { nominateInvalidReqs, StorageBunSqlite } from '@1sat/wallet-node'
7
+ import { StorageProvider } from '@bsv/wallet-toolbox'
8
+ import { join } from 'node:path'
9
+ import type { GlobalFlags } from '../args'
10
+ import { extractFlag } from '../args'
11
+ import { ensureDataDir, loadConfig } from '../config'
12
+ import { printCommandHelp } from '../help'
13
+ import { fatal, output } from '../output'
14
+
15
+ export async function handleStorageCommand(
16
+ args: string[],
17
+ opts: GlobalFlags,
18
+ ): Promise<void> {
19
+ const [subcommand, ...rest] = args
20
+
21
+ switch (subcommand) {
22
+ case 'unfail':
23
+ return storageUnfail(rest, opts)
24
+ default:
25
+ printCommandHelp('storage', opts.json)
26
+ if (subcommand && subcommand !== 'help') {
27
+ process.exit(1)
28
+ }
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Nominate invalid proven_tx_reqs for recovery. Writes only the `unfail`
34
+ * status; the running monitor's TaskUnFail verifies each txid against
35
+ * the chain, restores the ones that actually mined, and returns the rest
36
+ * to `invalid`. Safe to run against a live serve instance — same
37
+ * single-field status update the monitor's own review tasks perform.
38
+ */
39
+ async function storageUnfail(
40
+ args: string[],
41
+ opts: GlobalFlags,
42
+ ): Promise<void> {
43
+ const rawWindow = extractFlag(args, '--window')
44
+ if (!rawWindow) {
45
+ fatal(
46
+ "--window is required: 'all' for every invalid record (one-time cleanup), or a duration like 30d / 12h / 90m",
47
+ )
48
+ }
49
+ const windowMsecs = parseWindow(rawWindow)
50
+
51
+ const storage = await openServeStorage(opts)
52
+ try {
53
+ const { nominated } = await nominateInvalidReqs(storage, windowMsecs)
54
+
55
+ if (opts.json) {
56
+ output({ nominated }, opts)
57
+ return
58
+ }
59
+
60
+ if (nominated.length === 0) {
61
+ console.log('No invalid reqs in window — nothing nominated.')
62
+ return
63
+ }
64
+ console.log(`Nominated ${nominated.length} invalid req(s) for unfail review:`)
65
+ for (const n of nominated) {
66
+ console.log(` ${n.provenTxReqId} ${n.txid}`)
67
+ }
68
+ console.log(
69
+ 'The running monitor (1sat serve / serve monitor) processes these on its next TaskUnFail tick: mined transactions are restored, the rest return to invalid.',
70
+ )
71
+ } finally {
72
+ await storage.destroy()
73
+ }
74
+ }
75
+
76
+ function parseWindow(raw: string): number | undefined {
77
+ if (raw === 'all') return undefined
78
+ const m = raw.match(/^(\d+)([dhm])$/)
79
+ if (!m) {
80
+ fatal(
81
+ `--window must be 'all' or a duration like 30d, 12h, 90m — got: ${raw}`,
82
+ )
83
+ }
84
+ const units: Record<string, number> = {
85
+ d: 24 * 60 * 60 * 1000,
86
+ h: 60 * 60 * 1000,
87
+ m: 60 * 1000,
88
+ }
89
+ return Number(m[1]) * units[m[2]]
90
+ }
91
+
92
+ /** Open the serve wallet's storage exactly as `1sat serve` resolves it. */
93
+ async function openServeStorage(opts: GlobalFlags): Promise<StorageProvider> {
94
+ const config = loadConfig()
95
+ const chain = opts.chain ?? config.chain ?? 'main'
96
+ const storageConfig = config.server?.storage ?? { provider: 'bun-sqlite' }
97
+ const baseOptions = StorageProvider.createStorageBaseOptions(chain)
98
+
99
+ let storage: StorageProvider
100
+ if (storageConfig.provider === 'pg') {
101
+ if (!storageConfig.dbUrl) {
102
+ fatal(
103
+ 'server.storage.provider is pg but server.storage.dbUrl is not set.',
104
+ )
105
+ }
106
+ const { StoragePg } = await import('@1sat/wallet-node')
107
+ storage = new StoragePg({ ...baseOptions, dbUrl: storageConfig.dbUrl })
108
+ } else {
109
+ storage = new StorageBunSqlite({
110
+ ...baseOptions,
111
+ filename: join(ensureDataDir(), `wallet-${chain}.db`),
112
+ })
113
+ }
114
+
115
+ await storage.makeAvailable()
116
+ return storage
117
+ }
package/src/help.ts CHANGED
@@ -552,6 +552,28 @@ export const COMMANDS: CommandSpec[] = [
552
552
  },
553
553
  ],
554
554
  },
555
+ {
556
+ group: 'Server',
557
+ name: 'storage',
558
+ description:
559
+ 'Serve-wallet storage maintenance (config under server.storage in config.json)',
560
+ subcommands: [
561
+ {
562
+ name: 'unfail',
563
+ description:
564
+ 'Nominate invalid proven_tx_reqs for chain re-check; the running monitor restores any that actually mined',
565
+ args: [
566
+ {
567
+ flag: '--window',
568
+ values: "<all|30d|12h|90m>",
569
+ required: true,
570
+ description:
571
+ "Only reqs created within the window; 'all' for every invalid record",
572
+ },
573
+ ],
574
+ },
575
+ ],
576
+ },
555
577
 
556
578
  // MCP
557
579
  {
package/src/main.ts CHANGED
@@ -21,6 +21,7 @@ import { handleOpnsCommand } from './commands/opns'
21
21
  import { handleOrdinalsCommand } from './commands/ordinals'
22
22
  import { handleRemoteCommand } from './commands/remote'
23
23
  import { handleServeCommand } from './commands/serve'
24
+ import { handleStorageCommand } from './commands/storage'
24
25
  import { handleSocialCommand } from './commands/social'
25
26
  import { handleSweepCommand } from './commands/sweep'
26
27
  import { handleTokensCommand } from './commands/tokens'
@@ -134,6 +135,10 @@ async function main(): Promise<void> {
134
135
  await handleServeCommand(rest, flags)
135
136
  break
136
137
 
138
+ case 'storage':
139
+ await handleStorageCommand(rest, flags)
140
+ break
141
+
137
142
  // Hidden: parent CLI spawns this after wallet destroy so monitor
138
143
  // stdout/stderr go to ~/.1sat/cli/monitor.log instead of the TTY.
139
144
  case '__monitor-once':