indelible-mcp 2.5.1 → 2.6.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/tools/save_project.js +51 -72
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -220,7 +220,7 @@ Commands:
|
|
|
220
220
|
|
|
221
221
|
function printHelp() {
|
|
222
222
|
console.log(`
|
|
223
|
-
Indelible MCP — Blockchain memory for Claude Code (v2.
|
|
223
|
+
Indelible MCP — Blockchain memory for Claude Code (v2.6.0)
|
|
224
224
|
|
|
225
225
|
Setup:
|
|
226
226
|
indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
|
|
@@ -316,7 +316,7 @@ function readStdin() {
|
|
|
316
316
|
|
|
317
317
|
const SERVER_INFO = {
|
|
318
318
|
name: 'indelible',
|
|
319
|
-
version: '2.
|
|
319
|
+
version: '2.6.0',
|
|
320
320
|
description: 'Blockchain-backed memory and code storage for Claude Code'
|
|
321
321
|
}
|
|
322
322
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* save_project tool — Save a directory to BSV blockchain
|
|
3
|
-
* Walks directory (respecting .gitignore),
|
|
4
|
-
*
|
|
3
|
+
* Walks directory (respecting .gitignore), encrypts all files client-side,
|
|
4
|
+
* bundles into a single OP_RETURN transaction.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'
|
|
@@ -9,7 +9,6 @@ import { join, relative, basename } from 'node:path'
|
|
|
9
9
|
import { loadConfig, saveConfig, getWif } from '../lib/config.js'
|
|
10
10
|
import { encrypt, sha256 } from '../lib/crypto.js'
|
|
11
11
|
import * as spv from '../lib/spv.js'
|
|
12
|
-
import { saveFile } from './save_file.js'
|
|
13
12
|
|
|
14
13
|
const ALWAYS_IGNORE = [
|
|
15
14
|
'node_modules', '.git', '.next', 'dist', 'build',
|
|
@@ -93,110 +92,90 @@ export async function saveProject(dirPath, options = {}) {
|
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
const timestamp = new Date().toISOString()
|
|
96
|
-
const savedFiles = []
|
|
97
95
|
let totalSize = 0
|
|
98
96
|
|
|
99
97
|
try {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
for (
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
success: false,
|
|
117
|
-
error: `Failed to save file ${file.relativePath}: ${result.error}`,
|
|
118
|
-
savedSoFar: savedFiles.length,
|
|
119
|
-
savedFiles
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Capture change UTXOs for next file
|
|
124
|
-
lastChangeUtxos = result.changeUtxos
|
|
125
|
-
|
|
126
|
-
savedFiles.push({
|
|
127
|
-
path: file.relativePath,
|
|
128
|
-
txid: result.txId,
|
|
129
|
-
size: result.size
|
|
98
|
+
// Read and encrypt all files into a single bundle
|
|
99
|
+
const bundleFiles = []
|
|
100
|
+
for (const file of allFiles) {
|
|
101
|
+
const content = readFileSync(file.absolutePath, 'utf-8')
|
|
102
|
+
const fileSize = Buffer.byteLength(content, 'utf-8')
|
|
103
|
+
const contentHash = `sha256:${sha256(content)}`
|
|
104
|
+
const encrypted = encrypt(content, wif)
|
|
105
|
+
const filename = file.relativePath.split(/[/\\]/).pop()
|
|
106
|
+
|
|
107
|
+
bundleFiles.push({
|
|
108
|
+
path_enc: encrypt(file.relativePath, wif),
|
|
109
|
+
filename_enc: encrypt(filename, wif),
|
|
110
|
+
size: fileSize,
|
|
111
|
+
content_hash: contentHash,
|
|
112
|
+
encrypted
|
|
130
113
|
})
|
|
131
|
-
totalSize +=
|
|
132
|
-
|
|
133
|
-
if (i < allFiles.length - 1) {
|
|
134
|
-
await new Promise(r => setTimeout(r, 200))
|
|
135
|
-
}
|
|
114
|
+
totalSize += fileSize
|
|
136
115
|
}
|
|
137
116
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
txid: f.txid,
|
|
142
|
-
size: f.size
|
|
143
|
-
}))
|
|
144
|
-
|
|
145
|
-
const manifest = {
|
|
146
|
-
protocol: 'indelible.project',
|
|
147
|
-
version: 2,
|
|
117
|
+
const payload = {
|
|
118
|
+
protocol: 'indelible.project-bundle',
|
|
119
|
+
version: 1,
|
|
148
120
|
name_enc: encrypt(projectName, wif),
|
|
149
|
-
file_count:
|
|
121
|
+
file_count: allFiles.length,
|
|
150
122
|
total_size: totalSize,
|
|
151
|
-
files:
|
|
123
|
+
files: bundleFiles,
|
|
152
124
|
owner: config.address,
|
|
153
125
|
timestamp
|
|
154
126
|
}
|
|
155
127
|
|
|
156
|
-
|
|
157
|
-
let utxos = (lastChangeUtxos && lastChangeUtxos.length > 0)
|
|
158
|
-
? lastChangeUtxos
|
|
159
|
-
: await spv.getUtxos(config.address)
|
|
128
|
+
const utxos = await spv.getUtxos(config.address)
|
|
160
129
|
if (!utxos || utxos.length === 0) {
|
|
161
|
-
return {
|
|
162
|
-
success: false,
|
|
163
|
-
error: 'No UTXOs for manifest tx. Files were saved but manifest was not.',
|
|
164
|
-
savedFiles
|
|
165
|
-
}
|
|
130
|
+
return { success: false, error: 'No UTXOs available. Fund your wallet first.' }
|
|
166
131
|
}
|
|
167
132
|
|
|
168
133
|
const { txHex, txId } = await spv.buildOpReturnTxWithChange(
|
|
169
|
-
wif, utxos, JSON.stringify(
|
|
134
|
+
wif, utxos, JSON.stringify(payload), 'INDELIBLE_PROJECT_BUNDLE'
|
|
170
135
|
)
|
|
171
|
-
|
|
172
|
-
const manifestTxId = result.txid || txId
|
|
136
|
+
await spv.broadcastTx(txHex)
|
|
173
137
|
|
|
174
138
|
// Track in config
|
|
175
139
|
const projects = config.project_txids || []
|
|
176
140
|
projects.push({
|
|
177
|
-
txId
|
|
141
|
+
txId,
|
|
178
142
|
name: projectName,
|
|
179
|
-
fileCount:
|
|
143
|
+
fileCount: allFiles.length,
|
|
180
144
|
totalSize,
|
|
181
145
|
timestamp
|
|
182
146
|
})
|
|
183
147
|
saveConfig({ ...config, project_txids: projects })
|
|
184
148
|
|
|
149
|
+
// Index in server Redis so Vault UI can see it
|
|
150
|
+
try {
|
|
151
|
+
const apiUrl = config.api_url || 'https://indelible.one'
|
|
152
|
+
await fetch(`${apiUrl}/api/files/project/index`, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: { 'Content-Type': 'application/json' },
|
|
155
|
+
body: JSON.stringify({
|
|
156
|
+
address: config.address,
|
|
157
|
+
txId,
|
|
158
|
+
name: projectName,
|
|
159
|
+
fileCount: allFiles.length,
|
|
160
|
+
totalSize,
|
|
161
|
+
timestamp,
|
|
162
|
+
bundle: true
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
} catch { /* don't block save on index failure */ }
|
|
166
|
+
|
|
185
167
|
return {
|
|
186
168
|
success: true,
|
|
187
|
-
txId
|
|
169
|
+
txId,
|
|
188
170
|
name: projectName,
|
|
189
|
-
fileCount:
|
|
171
|
+
fileCount: allFiles.length,
|
|
190
172
|
totalSize,
|
|
191
|
-
files:
|
|
192
|
-
message: `Project "${projectName}" saved to blockchain. ${savedFiles.length} files, ${totalSize} bytes total. Manifest txId: ${manifestTxId}`
|
|
173
|
+
message: `Project "${projectName}" saved as single tx bundle. ${allFiles.length} files, ${totalSize} bytes. txId: ${txId}`
|
|
193
174
|
}
|
|
194
175
|
} catch (error) {
|
|
195
176
|
return {
|
|
196
177
|
success: false,
|
|
197
|
-
error: `Failed to save project: ${error.message}
|
|
198
|
-
savedSoFar: savedFiles.length,
|
|
199
|
-
savedFiles
|
|
178
|
+
error: `Failed to save project: ${error.message}`
|
|
200
179
|
}
|
|
201
180
|
}
|
|
202
181
|
}
|