indelible-mcp 2.9.0 → 3.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/package.json +1 -1
- package/src/index.js +2 -2
- package/src/lib/api-client.js +31 -0
- package/src/lib/spv.js +58 -18
- package/src/tools/setup_wallet.js +14 -2
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -223,7 +223,7 @@ Commands:
|
|
|
223
223
|
|
|
224
224
|
function printHelp() {
|
|
225
225
|
console.log(`
|
|
226
|
-
Indelible MCP — Blockchain memory for Claude Code (
|
|
226
|
+
Indelible MCP — Blockchain memory for Claude Code (v3.0.0)
|
|
227
227
|
|
|
228
228
|
Setup:
|
|
229
229
|
indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
|
|
@@ -324,7 +324,7 @@ function readStdin() {
|
|
|
324
324
|
|
|
325
325
|
const SERVER_INFO = {
|
|
326
326
|
name: 'indelible',
|
|
327
|
-
version: '
|
|
327
|
+
version: '3.0.0',
|
|
328
328
|
description: 'Blockchain-backed memory and code storage for Claude Code'
|
|
329
329
|
}
|
|
330
330
|
|
package/src/lib/api-client.js
CHANGED
|
@@ -116,6 +116,37 @@ export async function checkProTier(wif) {
|
|
|
116
116
|
return { ok: true, plan: 'unknown' }
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Auto-generate relay API key for SPV bridge access
|
|
121
|
+
* Logs in with address, then calls generate-key endpoint
|
|
122
|
+
* @param {string} address - BSV address
|
|
123
|
+
* @param {string} apiUrl - Optional API URL override
|
|
124
|
+
* @returns {Promise<{key: string, tier: string}|null>}
|
|
125
|
+
*/
|
|
126
|
+
export async function ensureRelayKey(address, apiUrl) {
|
|
127
|
+
if (!apiUrl) apiUrl = getApiUrl()
|
|
128
|
+
try {
|
|
129
|
+
const authRes = await fetch(`${apiUrl}/api/auth/wif`, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: { 'Content-Type': 'application/json' },
|
|
132
|
+
body: JSON.stringify({ address }),
|
|
133
|
+
signal: AbortSignal.timeout(10000)
|
|
134
|
+
})
|
|
135
|
+
if (!authRes.ok) return null
|
|
136
|
+
const { token } = await authRes.json()
|
|
137
|
+
if (!token) return null
|
|
138
|
+
const keyRes = await fetch(`${apiUrl}/api/relay/generate-key`, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
|
|
141
|
+
body: JSON.stringify({}),
|
|
142
|
+
signal: AbortSignal.timeout(10000)
|
|
143
|
+
})
|
|
144
|
+
if (!keyRes.ok) return null
|
|
145
|
+
const keyData = await keyRes.json()
|
|
146
|
+
return { key: keyData.key, tier: keyData.tier }
|
|
147
|
+
} catch { return null }
|
|
148
|
+
}
|
|
149
|
+
|
|
119
150
|
/**
|
|
120
151
|
* Check if server is reachable
|
|
121
152
|
*/
|
package/src/lib/spv.js
CHANGED
|
@@ -8,33 +8,66 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Transaction, P2PKH, PrivateKey, SatoshisPerKilobyte, LockingScript, OP } from '@bsv/sdk'
|
|
11
|
-
import { loadConfig } from './config.js'
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
import { loadConfig, saveConfig } from './config.js'
|
|
12
|
+
|
|
13
|
+
const GATEWAY_URL = 'http://155.138.238.167:8080'
|
|
14
|
+
const GATEWAY_BRIDGE = { url: GATEWAY_URL, name: 'relay-gateway' }
|
|
15
|
+
|
|
16
|
+
// Old individual bridge IPs — used to detect configs that need migration
|
|
17
|
+
const OLD_BRIDGE_IPS = [
|
|
18
|
+
'45.76.19.199', '107.191.49.18', '155.138.254.224',
|
|
19
|
+
'144.202.50.135', '155.138.216.126'
|
|
19
20
|
]
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
let migrationDone = false
|
|
23
|
+
|
|
24
|
+
async function getBridges() {
|
|
22
25
|
const config = loadConfig()
|
|
23
|
-
|
|
26
|
+
let apiKey = config?.spv_api_key || ''
|
|
27
|
+
let bridges = config?.spv_bridges || []
|
|
28
|
+
|
|
29
|
+
// Auto-migrate: detect old individual bridge IPs → swap to gateway
|
|
30
|
+
if (!migrationDone && bridges.length > 0) {
|
|
31
|
+
const hasOldIps = bridges.some(b => {
|
|
32
|
+
const url = typeof b === 'string' ? b : b.url
|
|
33
|
+
return OLD_BRIDGE_IPS.some(ip => url.includes(ip))
|
|
34
|
+
})
|
|
35
|
+
if (hasOldIps) {
|
|
36
|
+
bridges = [GATEWAY_BRIDGE]
|
|
37
|
+
config.spv_bridges = bridges
|
|
38
|
+
// Auto-generate relay key if missing
|
|
39
|
+
if (!apiKey || !apiKey.startsWith('relay_sk_')) {
|
|
40
|
+
try {
|
|
41
|
+
const { ensureRelayKey } = await import('./api-client.js')
|
|
42
|
+
const address = config?.address
|
|
43
|
+
if (address) {
|
|
44
|
+
const result = await ensureRelayKey(address)
|
|
45
|
+
if (result) {
|
|
46
|
+
apiKey = result.key
|
|
47
|
+
config.spv_api_key = apiKey
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} catch { /* backend unreachable — save bridge migration, skip key */ }
|
|
51
|
+
}
|
|
52
|
+
saveConfig(config)
|
|
53
|
+
migrationDone = true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
24
56
|
|
|
25
|
-
if (
|
|
26
|
-
return
|
|
57
|
+
if (bridges.length > 0) {
|
|
58
|
+
return bridges.map(b => ({
|
|
27
59
|
url: typeof b === 'string' ? b : b.url,
|
|
28
60
|
name: typeof b === 'string' ? b : (b.name || b.url),
|
|
29
61
|
apiKey
|
|
30
62
|
}))
|
|
31
63
|
}
|
|
32
64
|
|
|
33
|
-
|
|
65
|
+
// No config — use gateway default
|
|
66
|
+
return [{ ...GATEWAY_BRIDGE, apiKey }]
|
|
34
67
|
}
|
|
35
68
|
|
|
36
69
|
async function spvFetch(path, options = {}) {
|
|
37
|
-
const bridges = getBridges()
|
|
70
|
+
const bridges = await getBridges()
|
|
38
71
|
let lastError = null
|
|
39
72
|
|
|
40
73
|
for (const bridge of bridges) {
|
|
@@ -73,9 +106,16 @@ export async function checkHealth() {
|
|
|
73
106
|
}
|
|
74
107
|
|
|
75
108
|
export async function getUtxos(address) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
109
|
+
try {
|
|
110
|
+
const res = await spvFetch(`/api/address/${address}/unspent`)
|
|
111
|
+
if (res.ok) return res.json()
|
|
112
|
+
} catch { /* bridge unavailable */ }
|
|
113
|
+
|
|
114
|
+
// WoC lookup for address-based indexing
|
|
115
|
+
const wocRes = await fetch(`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent`)
|
|
116
|
+
if (wocRes.ok) return wocRes.json()
|
|
117
|
+
|
|
118
|
+
throw new Error(`Failed to get UTXOs for ${address}`)
|
|
79
119
|
}
|
|
80
120
|
|
|
81
121
|
export async function getAddressHistory(address) {
|
|
@@ -104,7 +144,7 @@ export async function getRawTxHex(txid) {
|
|
|
104
144
|
}
|
|
105
145
|
|
|
106
146
|
export async function broadcastTx(rawTxHex) {
|
|
107
|
-
const bridges = getBridges()
|
|
147
|
+
const bridges = await getBridges()
|
|
108
148
|
let firstSuccess = null
|
|
109
149
|
const errors = []
|
|
110
150
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { PrivateKey } from '@bsv/sdk'
|
|
11
11
|
import { loadConfig, saveConfig, getConfigPath } from '../lib/config.js'
|
|
12
12
|
import { sha256, encryptWif } from '../lib/crypto.js'
|
|
13
|
-
import { register, checkConnection } from '../lib/api-client.js'
|
|
13
|
+
import { register, checkConnection, ensureRelayKey } from '../lib/api-client.js'
|
|
14
14
|
import { setPassword } from 'cross-keychain'
|
|
15
15
|
|
|
16
16
|
const DEFAULT_API_URL = 'https://indelible.one'
|
|
@@ -84,6 +84,8 @@ export async function setupWallet(apiUrl = DEFAULT_API_URL, importWif, pin) {
|
|
|
84
84
|
address,
|
|
85
85
|
api_key: apiKey,
|
|
86
86
|
api_url: apiUrl,
|
|
87
|
+
spv_bridges: [{ url: 'http://155.138.238.167:8080', name: 'relay-gateway' }],
|
|
88
|
+
spv_api_key: '',
|
|
87
89
|
created_at: new Date().toISOString()
|
|
88
90
|
}
|
|
89
91
|
|
|
@@ -105,7 +107,16 @@ export async function setupWallet(apiUrl = DEFAULT_API_URL, importWif, pin) {
|
|
|
105
107
|
registered = true
|
|
106
108
|
} catch { /* will register on first use */ }
|
|
107
109
|
|
|
110
|
+
// Auto-generate relay API key
|
|
111
|
+
let relayKey = null
|
|
108
112
|
const serverConnected = await checkConnection()
|
|
113
|
+
if (serverConnected) {
|
|
114
|
+
relayKey = await ensureRelayKey(address, apiUrl)
|
|
115
|
+
if (relayKey) {
|
|
116
|
+
config.spv_api_key = relayKey.key
|
|
117
|
+
saveConfig(config)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
109
120
|
|
|
110
121
|
return {
|
|
111
122
|
success: true,
|
|
@@ -114,6 +125,7 @@ export async function setupWallet(apiUrl = DEFAULT_API_URL, importWif, pin) {
|
|
|
114
125
|
keychainStored,
|
|
115
126
|
registered,
|
|
116
127
|
serverConnected,
|
|
117
|
-
|
|
128
|
+
relayTier: relayKey?.tier || null,
|
|
129
|
+
message: `Wallet imported and encrypted! Address: ${address}.${keychainStored ? ' PIN stored in OS keychain.' : ' Set INDELIBLE_PIN env var for MCP mode.'}${registered ? ' Registered with Indelible.' : ' Will register on first save.'}${relayKey ? ` Relay key generated (${relayKey.tier}).` : ''}`
|
|
118
130
|
}
|
|
119
131
|
}
|