@1sat/cli 0.0.13 → 0.0.15
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 +4 -3
- package/src/commands/mcp-proxy.ts +59 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1sat/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "CLI for 1Sat Ordinals SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/cli.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"keywords": ["1sat", "bsv", "ordinals", "cli"],
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@1sat/actions": "0.0.
|
|
19
|
+
"@1sat/actions": "0.0.64",
|
|
20
20
|
"@1sat/client": "0.0.17",
|
|
21
21
|
"@1sat/types": "0.0.14",
|
|
22
22
|
"@1sat/wallet-node": ">=0.0.13",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"chalk": "^5.0.0",
|
|
25
25
|
"@clack/prompts": "^0.8.0",
|
|
26
26
|
"bitcoin-backup": "^0.0.11",
|
|
27
|
-
"dotenv": "^17.0.0"
|
|
27
|
+
"dotenv": "^17.0.0",
|
|
28
|
+
"evlog": "^2.10.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@types/bun": "^1.3.9",
|
|
@@ -7,18 +7,17 @@
|
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
8
8
|
import { Hash, KeyDeriver, PrivateKey, Signature, Utils } from '@bsv/sdk'
|
|
9
9
|
import type { WalletProtocol } from '@bsv/sdk'
|
|
10
|
+
import { createLogger, initLogger } from 'evlog'
|
|
10
11
|
|
|
11
12
|
const { toBase64, toArray } = Utils
|
|
12
13
|
|
|
14
|
+
initLogger({ env: { service: '1sat-mcp-proxy' } })
|
|
15
|
+
|
|
13
16
|
const MCP_URL = process.env.ONESAT_MCP_URL ?? 'http://127.0.0.1:3322'
|
|
14
17
|
const KEY_DIR = `${process.env.HOME}/.1sat-wallet`
|
|
15
18
|
const CLIENT_KEY_PATH = `${KEY_DIR}/mcp-agent.key`
|
|
16
19
|
const AUTH_PROTOCOL_ID: WalletProtocol = [2, 'authrite message signature']
|
|
17
20
|
|
|
18
|
-
function log(msg: string): void {
|
|
19
|
-
process.stderr.write(`[1sat mcp-proxy] ${msg}\n`)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
21
|
function generateNonce(): string {
|
|
23
22
|
const bytes = new Uint8Array(32)
|
|
24
23
|
crypto.getRandomValues(bytes)
|
|
@@ -32,7 +31,9 @@ function getClientKey(): PrivateKey {
|
|
|
32
31
|
}
|
|
33
32
|
const key = PrivateKey.fromRandom()
|
|
34
33
|
writeFileSync(CLIENT_KEY_PATH, key.toWif(), { mode: 0o600 })
|
|
35
|
-
log
|
|
34
|
+
const log = createLogger({ context: 'startup' })
|
|
35
|
+
log.set({ event: 'key_generated', path: CLIENT_KEY_PATH })
|
|
36
|
+
log.emit()
|
|
36
37
|
return key
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -75,7 +76,10 @@ async function handshake(key: PrivateKey): Promise<Session> {
|
|
|
75
76
|
const sig = Signature.fromDER(data.signature as string, 'hex')
|
|
76
77
|
if (!serverPub.verify(Array.from(msgHash), sig)) throw new Error('Server signature verification failed')
|
|
77
78
|
|
|
78
|
-
log
|
|
79
|
+
const log = createLogger({ context: 'auth' })
|
|
80
|
+
log.set({ event: 'handshake_complete', serverIdentityKey: (data.identityKey as string).slice(0, 16) })
|
|
81
|
+
log.emit()
|
|
82
|
+
|
|
79
83
|
return { serverIdentityKey: data.identityKey as string, serverNonce, clientKey: key }
|
|
80
84
|
}
|
|
81
85
|
|
|
@@ -98,17 +102,25 @@ function signHeaders(session: Session, pathname: string): Record<string, string>
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
export async function handleMcpProxyCommand(): Promise<void> {
|
|
105
|
+
const startLog = createLogger({ context: 'startup' })
|
|
106
|
+
|
|
101
107
|
try {
|
|
102
108
|
await fetch(MCP_URL, { signal: AbortSignal.timeout(2000) })
|
|
103
109
|
} catch {
|
|
104
|
-
|
|
110
|
+
startLog.set({ event: 'server_unreachable', url: MCP_URL })
|
|
111
|
+
startLog.emit()
|
|
112
|
+
process.stderr.write(`[1sat mcp-proxy] Server not reachable at ${MCP_URL} — is 1Sat wallet running?\n`)
|
|
105
113
|
process.exit(1)
|
|
106
114
|
}
|
|
107
115
|
|
|
116
|
+
startLog.set({ event: 'started', url: MCP_URL })
|
|
117
|
+
startLog.emit()
|
|
118
|
+
|
|
108
119
|
const key = getClientKey()
|
|
109
120
|
const session = await handshake(key)
|
|
110
121
|
|
|
111
122
|
let mcpSessionId: string | null = null
|
|
123
|
+
let requestCount = 0
|
|
112
124
|
const decoder = new TextDecoder()
|
|
113
125
|
const reader = Bun.stdin.stream().getReader()
|
|
114
126
|
let buffer = ''
|
|
@@ -125,6 +137,9 @@ export async function handleMcpProxyCommand(): Promise<void> {
|
|
|
125
137
|
buffer = buffer.slice(newlineIdx + 1)
|
|
126
138
|
if (!line) continue
|
|
127
139
|
|
|
140
|
+
requestCount++
|
|
141
|
+
const reqLog = createLogger({ context: 'proxy' })
|
|
142
|
+
|
|
128
143
|
const headers: Record<string, string> = {
|
|
129
144
|
'Content-Type': 'application/json',
|
|
130
145
|
Accept: 'application/json, text/event-stream',
|
|
@@ -136,6 +151,11 @@ export async function handleMcpProxyCommand(): Promise<void> {
|
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
try {
|
|
154
|
+
let method: string | undefined
|
|
155
|
+
try {
|
|
156
|
+
method = JSON.parse(line).method
|
|
157
|
+
} catch {}
|
|
158
|
+
|
|
139
159
|
const res = await fetch(`${MCP_URL}/mcp`, { method: 'POST', headers, body: line })
|
|
140
160
|
|
|
141
161
|
const sessionHeader = res.headers.get('mcp-session-id')
|
|
@@ -143,21 +163,47 @@ export async function handleMcpProxyCommand(): Promise<void> {
|
|
|
143
163
|
|
|
144
164
|
const contentType = res.headers.get('content-type') ?? ''
|
|
145
165
|
|
|
166
|
+
reqLog.set({
|
|
167
|
+
event: 'request',
|
|
168
|
+
requestNum: requestCount,
|
|
169
|
+
method,
|
|
170
|
+
status: res.status,
|
|
171
|
+
contentType: contentType.split(';')[0],
|
|
172
|
+
sessionId: mcpSessionId?.slice(0, 8),
|
|
173
|
+
})
|
|
174
|
+
reqLog.emit()
|
|
175
|
+
|
|
146
176
|
if (contentType.includes('text/event-stream')) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
177
|
+
// Stream SSE events incrementally — res.text() would hang
|
|
178
|
+
// because SSE connections stay open indefinitely
|
|
179
|
+
const sseReader = res.body!.getReader()
|
|
180
|
+
const sseDecoder = new TextDecoder()
|
|
181
|
+
let sseBuf = ''
|
|
182
|
+
while (true) {
|
|
183
|
+
const { done: sseDone, value: sseValue } = await sseReader.read()
|
|
184
|
+
if (sseDone) break
|
|
185
|
+
sseBuf += sseDecoder.decode(sseValue, { stream: true })
|
|
186
|
+
const sseLines = sseBuf.split('\n')
|
|
187
|
+
sseBuf = sseLines.pop()!
|
|
188
|
+
for (const sseLine of sseLines) {
|
|
189
|
+
if (sseLine.startsWith('data: ')) {
|
|
190
|
+
const data = sseLine.slice(6).trim()
|
|
191
|
+
if (data) process.stdout.write(`${data}\n`)
|
|
192
|
+
}
|
|
152
193
|
}
|
|
153
194
|
}
|
|
195
|
+
if (sseBuf.startsWith('data: ')) {
|
|
196
|
+
const data = sseBuf.slice(6).trim()
|
|
197
|
+
if (data) process.stdout.write(`${data}\n`)
|
|
198
|
+
}
|
|
154
199
|
} else {
|
|
155
200
|
const body = await res.text()
|
|
156
201
|
if (body.trim()) process.stdout.write(`${body.trim()}\n`)
|
|
157
202
|
}
|
|
158
203
|
} catch (err) {
|
|
159
204
|
const msg = err instanceof Error ? err.message : String(err)
|
|
160
|
-
|
|
205
|
+
reqLog.set({ event: 'request_failed', requestNum: requestCount, error: msg })
|
|
206
|
+
reqLog.emit()
|
|
161
207
|
process.stdout.write(`${JSON.stringify({
|
|
162
208
|
jsonrpc: '2.0',
|
|
163
209
|
error: { code: -32000, message: `MCP proxy error: ${msg}` },
|