@reclaimprotocol/agent 0.1.26 → 0.1.28
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 +5 -5
- package/lib/authoring/tools.js +2 -1
- package/lib/consts.d.ts +1 -1
- package/lib/consts.js +1 -1
- package/lib/live/tunnel.d.ts +10 -7
- package/lib/live/tunnel.js +78 -56
- package/lib/mcp/api/operations.gen.d.ts +12 -8
- package/lib/mcp/api/operations.gen.js +378 -580
- package/lib/mcp/api/tools.d.ts +1 -1
- package/lib/mcp/instructions.js +7 -3
- package/lib/mcp/remap_errors.js +9 -5
- package/lib/mcp/start.js +1 -1
- package/lib/mcp/tools/agent/attach.d.ts +2 -2
- package/lib/mcp/tools/agent/attach.js +4 -0
- package/lib/mcp/tools/agent/build.d.ts +1 -1
- package/lib/mcp/tools/agent/live-view-url.d.ts +2 -0
- package/lib/mcp/tools/agent/live-view-url.js +11 -0
- package/lib/mcp/tools/agent/live-view.d.ts +4 -2
- package/lib/mcp/tools/agent/live-view.js +40 -54
- package/lib/mcp/tools/agent/propose.js +10 -4
- package/lib/mcp/tools/agent/publish.d.ts +1 -1
- package/lib/mcp/tools/agent/publish.js +49 -26
- package/lib/mcp/tools/agent/shared.js +0 -9
- package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
- package/lib/mcp/tools/authenticate/build.d.ts +1 -1
- package/lib/mcp/tools/authenticate/builder-access.d.ts +1 -1
- package/lib/mcp/tools/verification/result.js +12 -13
- package/lib/old/login-cdp.d.ts +17 -6
- package/lib/old/login-cdp.js +28 -11
- package/lib/old/to-register.d.ts +6 -0
- package/lib/old/to-register.js +35 -5
- package/lib/old/tools/authenticate.d.ts +5 -4
- package/lib/old/tools/authenticate.js +9 -5
- package/lib/old/tools/publish.d.ts +2 -2
- package/lib/old/tools/publish.js +47 -21
- package/lib/proof/attestor-auth.d.ts +13 -0
- package/lib/proof/attestor-auth.js +16 -0
- package/lib/proof/attestor.d.ts +7 -4
- package/lib/proof/attestor.js +7 -4
- package/lib/proof/index.d.ts +1 -0
- package/lib/proof/index.js +1 -0
- package/lib/proof/pod-prover.d.ts +2 -0
- package/lib/proof/pod-prover.js +5 -5
- package/lib/provider/injection-doc.d.ts +2 -0
- package/lib/provider/injection-doc.js +7 -1
- package/lib/provider/post-publish-browser.d.ts +12 -0
- package/lib/provider/post-publish-browser.js +40 -0
- package/lib/provider/to-version.d.ts +19 -7
- package/lib/provider/to-version.js +67 -25
- package/lib/tools/registry.d.ts +1 -1
- package/lib/tools/registry.js +7 -4
- package/package.json +2 -2
- package/skill/SKILL.builder.md +134 -13
- package/skill/SKILL.common.md +30 -19
- package/skill/SKILL.old.md +22 -12
- package/skill/builder-mode.md +19 -9
- package/skill/community-contributions.md +108 -0
- package/skill/hash-validation.md +8 -5
- package/skill/interception.md +35 -22
- package/skill/old-mode.md +11 -5
- package/skill/user-script.md +41 -10
- package/src/authoring/tools.ts +2 -1
- package/src/consts.ts +1 -2
- package/src/live/tunnel.ts +112 -66
- package/src/mcp/api/operations.gen.ts +384 -584
- package/src/mcp/api/tools.ts +1 -1
- package/src/mcp/instructions.ts +7 -3
- package/src/mcp/remap_errors.ts +9 -7
- package/src/mcp/start.ts +1 -1
- package/src/mcp/tools/agent/attach.ts +6 -2
- package/src/mcp/tools/agent/build.ts +1 -1
- package/src/mcp/tools/agent/live-view-url.ts +13 -0
- package/src/mcp/tools/agent/live-view.ts +42 -54
- package/src/mcp/tools/agent/propose.ts +10 -4
- package/src/mcp/tools/agent/publish.ts +63 -28
- package/src/mcp/tools/agent/shared.ts +5 -0
- package/src/mcp/tools/authenticate/authenticate.ts +1 -1
- package/src/mcp/tools/authenticate/build.ts +1 -1
- package/src/mcp/tools/authenticate/builder-access.ts +1 -1
- package/src/mcp/tools/verification/result.ts +11 -13
- package/src/old/login-cdp.ts +38 -11
- package/src/old/to-register.ts +56 -7
- package/src/old/tools/authenticate.ts +9 -5
- package/src/old/tools/publish.ts +64 -22
- package/src/proof/attestor-auth.ts +33 -0
- package/src/proof/attestor.ts +14 -8
- package/src/proof/index.ts +4 -0
- package/src/proof/pod-prover.ts +7 -5
- package/src/provider/injection-doc.ts +9 -1
- package/src/provider/post-publish-browser.ts +56 -0
- package/src/provider/to-version.ts +104 -27
- package/src/tools/registry.ts +12 -9
package/src/live/tunnel.ts
CHANGED
|
@@ -24,42 +24,27 @@ import { type CloudflaredBinary, resolveCloudflared } from './cloudflared.ts'
|
|
|
24
24
|
|
|
25
25
|
/** How long to wait for cloudflared to report its hostname before giving up. */
|
|
26
26
|
const URL_TIMEOUT_MS = 30_000
|
|
27
|
-
/** After the hostname is printed, Cloudflare needs
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
/** After the hostname is printed, Cloudflare needs time before the edge routes
|
|
28
|
+
* to it. Probe briefly, then return the still-running tunnel as propagating so
|
|
29
|
+
* slow DNS or edge setup does not destroy an otherwise healthy share. */
|
|
30
30
|
const READY_TIMEOUT_MS = 20_000
|
|
31
31
|
const READY_POLL_MS = 1_000
|
|
32
|
+
const STOP_TIMEOUT_MS = 5_000
|
|
32
33
|
|
|
33
34
|
/** `https://<something>.trycloudflare.com`, as printed on cloudflared's log
|
|
34
35
|
* stream. Anchored to the scheme so a hostname inside prose can't match. */
|
|
35
36
|
const QUICK_TUNNEL_URL = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i
|
|
36
37
|
|
|
37
|
-
/**
|
|
38
|
-
* cloudflared says this once per edge connection it establishes, and until it
|
|
39
|
-
* does, the hostname resolves but routes nowhere.
|
|
40
|
-
*
|
|
41
|
-
* This is the signal to trust, because it is LOCAL. Asking whether we can fetch
|
|
42
|
-
* the public URL ourselves conflates two questions — is the tunnel up, and does
|
|
43
|
-
* this machine have egress to Cloudflare's edge — and reports "not ready" when
|
|
44
|
-
* only the second fails. Observed: a tunnel printed its hostname and registered
|
|
45
|
-
* nothing for 12s, which is indistinguishable from a blocked fetch unless you
|
|
46
|
-
* read cloudflared's own account of it.
|
|
47
|
-
*/
|
|
48
|
-
const REGISTERED = /Registered tunnel connection|connIndex=/i
|
|
49
|
-
|
|
50
38
|
/** Tail of cloudflared's log kept for error messages. */
|
|
51
39
|
const LOG_TAIL_BYTES = 4096
|
|
52
40
|
|
|
53
41
|
export interface Tunnel {
|
|
54
42
|
/** The public HTTPS origin Cloudflare assigned. */
|
|
55
43
|
url: string
|
|
56
|
-
/** Whether the
|
|
57
|
-
* elapsed. False means it may still be propagating rather than broken — say
|
|
58
|
-
* so rather than treating the share as failed. */
|
|
44
|
+
/** Whether the public viewer answered during the initial readiness probe. */
|
|
59
45
|
ready: boolean
|
|
60
|
-
/** cloudflared
|
|
61
|
-
|
|
62
|
-
diagnostics?: string
|
|
46
|
+
/** Bounded cloudflared output when the edge is still propagating. */
|
|
47
|
+
cloudflaredLog?: string
|
|
63
48
|
/** Terminate the tunnel. Safe to call more than once. */
|
|
64
49
|
stop: () => Promise<void>
|
|
65
50
|
/** Which cloudflared ran, and whether we had to fetch it. */
|
|
@@ -69,37 +54,43 @@ export interface Tunnel {
|
|
|
69
54
|
/**
|
|
70
55
|
* Wait until the tunnel is actually carrying traffic.
|
|
71
56
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* - the public origin answered. ANY HTTP response counts, including a 404:
|
|
76
|
-
* the live-view server 404s unknown paths, so a status code at all proves
|
|
77
|
-
* the hop is live. Kept as a second opinion in case a cloudflared release
|
|
78
|
-
* rewords its log.
|
|
57
|
+
* A cloudflared "Registered tunnel connection" log is not enough: Cloudflare
|
|
58
|
+
* can report one while the assigned hostname still returns DNS NXDOMAIN. The
|
|
59
|
+
* public viewer itself must return a successful response.
|
|
79
60
|
*/
|
|
80
61
|
async function waitUntilRouted(
|
|
81
62
|
url: string,
|
|
82
|
-
|
|
63
|
+
{
|
|
64
|
+
isRunning,
|
|
65
|
+
pollMs,
|
|
66
|
+
timeoutMs,
|
|
67
|
+
}: {
|
|
68
|
+
isRunning: () => boolean
|
|
69
|
+
pollMs: number
|
|
70
|
+
timeoutMs: number
|
|
71
|
+
},
|
|
83
72
|
): Promise<boolean> {
|
|
84
|
-
const deadline = Date.now() +
|
|
73
|
+
const deadline = Date.now() + timeoutMs
|
|
85
74
|
while(Date.now() < deadline) {
|
|
86
|
-
if(
|
|
87
|
-
return
|
|
75
|
+
if(!isRunning()) {
|
|
76
|
+
return false
|
|
88
77
|
}
|
|
89
78
|
|
|
90
79
|
try {
|
|
91
|
-
await fetch(url, {
|
|
80
|
+
const response = await fetch(url, {
|
|
92
81
|
method: 'GET',
|
|
93
82
|
redirect: 'manual',
|
|
94
|
-
signal: AbortSignal.timeout(
|
|
83
|
+
signal: AbortSignal.timeout(pollMs * 2),
|
|
95
84
|
})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
85
|
+
if(response.ok) {
|
|
86
|
+
return true
|
|
87
|
+
}
|
|
88
|
+
} catch{}
|
|
89
|
+
|
|
90
|
+
await new Promise((r) => setTimeout(r, pollMs))
|
|
100
91
|
}
|
|
101
92
|
|
|
102
|
-
return
|
|
93
|
+
return false
|
|
103
94
|
}
|
|
104
95
|
|
|
105
96
|
/**
|
|
@@ -111,7 +102,20 @@ async function waitUntilRouted(
|
|
|
111
102
|
*/
|
|
112
103
|
export async function startTunnel(
|
|
113
104
|
port: number,
|
|
114
|
-
{
|
|
105
|
+
{
|
|
106
|
+
autoInstall = true,
|
|
107
|
+
probePath = '/',
|
|
108
|
+
readyPollMs = READY_POLL_MS,
|
|
109
|
+
readyTimeoutMs = READY_TIMEOUT_MS,
|
|
110
|
+
}: {
|
|
111
|
+
autoInstall?: boolean
|
|
112
|
+
/** Public path that must answer before the tunnel is returned. */
|
|
113
|
+
probePath?: string
|
|
114
|
+
/** Test-only timing override. */
|
|
115
|
+
readyPollMs?: number
|
|
116
|
+
/** Test-only timing override. */
|
|
117
|
+
readyTimeoutMs?: number
|
|
118
|
+
} = {},
|
|
115
119
|
): Promise<Tunnel> {
|
|
116
120
|
// Resolves an existing cloudflared, or fetches one on first use. Throws
|
|
117
121
|
// with an actionable message when neither is possible.
|
|
@@ -121,25 +125,14 @@ export async function startTunnel(
|
|
|
121
125
|
'--no-autoupdate',
|
|
122
126
|
'--url', `http://127.0.0.1:${port}`,
|
|
123
127
|
], { stdio: ['ignore', 'pipe', 'pipe'] })
|
|
128
|
+
const exited = new Promise<void>((resolve) => {
|
|
129
|
+
child.once('exit', () => resolve())
|
|
130
|
+
})
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
child.kill('SIGTERM')
|
|
128
|
-
}
|
|
129
|
-
}
|
|
132
|
+
let stopPromise: Promise<void> | undefined
|
|
133
|
+
const stop = () => stopPromise ??= stopChild()
|
|
130
134
|
|
|
131
135
|
return new Promise<Tunnel>((resolve, reject) => {
|
|
132
|
-
const timer = setTimeout(() => {
|
|
133
|
-
void stop()
|
|
134
|
-
reject(new Error(
|
|
135
|
-
'cloudflared did not report a tunnel hostname within '
|
|
136
|
-
+ `${URL_TIMEOUT_MS / 1000}s. Check network egress to Cloudflare, `
|
|
137
|
-
+ 'or run `cloudflared tunnel --url http://127.0.0.1:'
|
|
138
|
-
+ `${port}` + '` by hand to see its output.',
|
|
139
|
-
))
|
|
140
|
-
}, URL_TIMEOUT_MS)
|
|
141
|
-
|
|
142
|
-
let registered = false
|
|
143
136
|
let tail = ''
|
|
144
137
|
let settled = false
|
|
145
138
|
const finish = (fn: () => void) => {
|
|
@@ -152,30 +145,60 @@ export async function startTunnel(
|
|
|
152
145
|
fn()
|
|
153
146
|
}
|
|
154
147
|
|
|
148
|
+
const timer = setTimeout(() => {
|
|
149
|
+
finish(() => {
|
|
150
|
+
void stop().then(() => reject(new Error(
|
|
151
|
+
'cloudflared did not report a tunnel hostname within '
|
|
152
|
+
+ `${URL_TIMEOUT_MS / 1000}s. Check network egress to `
|
|
153
|
+
+ 'Cloudflare, or run `cloudflared tunnel --url '
|
|
154
|
+
+ `http://127.0.0.1:${port}` + '` by hand to see its output.',
|
|
155
|
+
)))
|
|
156
|
+
})
|
|
157
|
+
}, URL_TIMEOUT_MS)
|
|
158
|
+
|
|
155
159
|
// cloudflared logs the assigned hostname to stderr, not stdout. Watch
|
|
156
160
|
// both so a future version that switches streams still works.
|
|
157
161
|
const onChunk = (chunk: Buffer) => {
|
|
158
162
|
const text = chunk.toString()
|
|
159
|
-
// Keep watching after the hostname
|
|
160
|
-
//
|
|
163
|
+
// Keep watching after the hostname so a readiness failure can include
|
|
164
|
+
// cloudflared's account of what happened.
|
|
161
165
|
tail = (tail + text).slice(-LOG_TAIL_BYTES)
|
|
162
|
-
if(REGISTERED.test(text)) {
|
|
163
|
-
registered = true
|
|
164
|
-
}
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
// Search the accumulated tail so a URL split across stream chunks is not
|
|
168
|
+
// missed. The tail is bounded and the URL appears near startup.
|
|
169
|
+
const match = QUICK_TUNNEL_URL.exec(tail)
|
|
167
170
|
if(match) {
|
|
168
171
|
const url = match[0]
|
|
169
172
|
finish(() => {
|
|
170
|
-
|
|
173
|
+
const probeUrl = new URL(probePath, `${url}/`).toString()
|
|
174
|
+
void waitUntilRouted(probeUrl, {
|
|
175
|
+
isRunning: () => child.exitCode === null
|
|
176
|
+
&& child.signalCode === null,
|
|
177
|
+
pollMs: readyPollMs,
|
|
178
|
+
timeoutMs: readyTimeoutMs,
|
|
179
|
+
}).then((ready) => {
|
|
171
180
|
if(ready) {
|
|
172
181
|
resolve({ url, ready, stop, binary })
|
|
173
182
|
return
|
|
174
183
|
}
|
|
175
184
|
|
|
185
|
+
if(child.exitCode !== null || child.signalCode !== null) {
|
|
186
|
+
reject(new Error(
|
|
187
|
+
'cloudflared exited before the public viewer became reachable. '
|
|
188
|
+
+ 'cloudflared output:\n' + tail,
|
|
189
|
+
))
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
176
193
|
LOGGER.warn({ url, tail },
|
|
177
|
-
'tunnel
|
|
178
|
-
resolve({
|
|
194
|
+
'tunnel still propagating; returning the live hostname')
|
|
195
|
+
resolve({
|
|
196
|
+
url,
|
|
197
|
+
ready,
|
|
198
|
+
cloudflaredLog: tail,
|
|
199
|
+
stop,
|
|
200
|
+
binary,
|
|
201
|
+
})
|
|
179
202
|
})
|
|
180
203
|
})
|
|
181
204
|
}
|
|
@@ -205,4 +228,27 @@ export async function startTunnel(
|
|
|
205
228
|
)))
|
|
206
229
|
})
|
|
207
230
|
})
|
|
231
|
+
|
|
232
|
+
async function stopChild() {
|
|
233
|
+
if(child.exitCode !== null || child.signalCode !== null) {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let timeout: NodeJS.Timeout | undefined
|
|
238
|
+
const timedOut = new Promise<void>((resolve) => {
|
|
239
|
+
timeout = setTimeout(resolve, STOP_TIMEOUT_MS)
|
|
240
|
+
})
|
|
241
|
+
child.kill('SIGTERM')
|
|
242
|
+
await Promise.race([
|
|
243
|
+
exited,
|
|
244
|
+
timedOut,
|
|
245
|
+
])
|
|
246
|
+
clearTimeout(timeout)
|
|
247
|
+
if(child.exitCode !== null || child.signalCode !== null) {
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
child.kill('SIGKILL')
|
|
252
|
+
await exited
|
|
253
|
+
}
|
|
208
254
|
}
|