@buoy-gg/mcp 7.0.56 → 7.0.57
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/dist/camera-JI2FMAKN.mjs +1 -0
- package/dist/chunk-6EOB4FHJ.mjs +5 -0
- package/dist/chunk-EO7ZIMDE.mjs +47 -0
- package/dist/chunk-GBZURL4J.mjs +752 -0
- package/dist/chunk-MTTWR4T7.mjs +1 -0
- package/dist/{chunk-ALXRBSW6.mjs → chunk-OPHPYPJD.mjs} +4 -4
- package/dist/cli.js +590 -216
- package/dist/cli.mjs +7 -5
- package/dist/flowRun-LXHD5IKB.mjs +2 -0
- package/dist/index.d.mts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +512 -141
- package/dist/index.mjs +1 -1
- package/dist/webBench-QCHYXYCA.mjs +1 -0
- package/package.json +10 -7
- package/dist/camera-YKUHGME2.mjs +0 -1
- package/dist/chunk-RYFN5J56.mjs +0 -5
- package/dist/chunk-YX73KHIH.mjs +0 -427
- package/dist/webBench-2IFT74EV.mjs +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{SyncClient as u,createMcpServer as
|
|
3
|
-
`);setTimeout(()=>process.exit(0),2e3).unref();void(async()=>{await s.stop().catch(()=>{});await
|
|
2
|
+
import{createRequire as d}from"module";const h=d(import.meta.url);import{runInit as w}from"./chunk-EO7ZIMDE.mjs";import{SyncClient as u,createMcpServer as m,stopAllRecordingsNow as f}from"./chunk-GBZURL4J.mjs";import"./chunk-6EOB4FHJ.mjs";import"./chunk-OPHPYPJD.mjs";import"./chunk-MTTWR4T7.mjs";import{StdioServerTransport as g}from"@modelcontextprotocol/sdk/server/stdio.js";console.log=(...e)=>console.error(...e);console.info=(...e)=>console.error(...e);console.debug=(...e)=>console.error(...e);async function b(){const e=process.env.BUOY_BROKER_PORT;const s=new u({url:process.env.BUOY_BROKER_URL,port:e?Number(e):void 0,autoSpawnBroker:process.env.BUOY_NO_SPAWN!=="1"});const r=m(s);const o=new g;let n=false;const c=t=>{if(n)return;n=true;process.stderr.write(`[buoy-mcp] shutting down (${t})
|
|
3
|
+
`);f();setTimeout(()=>process.exit(0),2e3).unref();void(async()=>{await s.stop().catch(()=>{});await r.close().catch(()=>{});process.exit(0)})()};process.stdin.once("end",()=>c("stdin closed"));process.stdin.once("close",()=>c("stdin closed"));r.server.onclose=()=>c("transport closed");process.on("SIGINT",()=>c("SIGINT"));process.on("SIGTERM",()=>c("SIGTERM"));process.on("SIGHUP",()=>c("SIGHUP"));await r.connect(o);process.stderr.write(`[buoy-mcp] serving MCP over stdio; connecting to broker\u2026
|
|
4
4
|
`);s.start().then(t=>process.stderr.write(`[buoy-mcp] broker ${t}
|
|
5
5
|
`)).catch(t=>process.stderr.write(`[buoy-mcp] broker connection failed: ${t?.message??t} \u2014 tools will report no devices until it recovers
|
|
6
|
-
`))}async function
|
|
6
|
+
`))}async function y(){const e=process.env.BUOY_BROKER_PORT;const s=new u({url:process.env.BUOY_BROKER_URL,port:e?Number(e):void 0,autoSpawnBroker:false});try{const r=await Promise.race([s.start().then(()=>true,()=>false),new Promise(o=>setTimeout(()=>o(false),4e3))]);if(!r)return false;for(let o=0;o<10;o++){if(s.connectedDevices().some(n=>n.tier==="pro"))return true;await new Promise(n=>setTimeout(n,300))}return false}finally{await Promise.race([s.stop().catch(()=>{}),new Promise(r=>setTimeout(r,2e3))])}}if(process.argv[2]==="web-bench"){void(async()=>{try{const e=process.argv.slice(3);const s=e.find(a=>!a.startsWith("--"));if(!s)throw new Error("usage: buoy-mcp web-bench <config.json> [--json]");const{readFileSync:r}=await import("fs");const{runWebBench:o,formatWebBenchMarkdown:n}=await import("./webBench-QCHYXYCA.mjs");const{readEntitlement:c}=await import("./camera-JI2FMAKN.mjs");if(!c().isPro&&!await y()){throw new Error("web-bench is a Buoy Pro feature. Sign in to Buoy Desktop with a Pro account, or open an app signed in to Pro. See https://buoy.gg/pricing.")}const t=JSON.parse(r(s,"utf8"));const i=await o({...t,durationMs:t.durationMs??t.perCaseDurationMs},a=>process.stderr.write(`[web-bench] ${a}
|
|
7
7
|
`));const p=i.status==="failed"||i.browserLeftRunning?1:0;const l=(e.includes("--json")?JSON.stringify(i,null,2):n(i))+"\n";setTimeout(()=>process.exit(p),5e3).unref();process.stdout.write(l,()=>process.exit(p))}catch(e){process.stderr.write(`[buoy-mcp] web-bench failed: ${e?.message??e}
|
|
8
|
-
`,()=>process.exit(1));setTimeout(()=>process.exit(1),5e3).unref()}})()}else if(process.argv[2]==="
|
|
9
|
-
|
|
8
|
+
`,()=>process.exit(1));setTimeout(()=>process.exit(1),5e3).unref()}})()}else if(process.argv[2]==="flow"&&process.argv[3]==="run"){void(async()=>{try{const{parseFlowRunArgs:e,runFlows:s,formatFlowResults:r}=await import("./flowRun-LXHD5IKB.mjs");const o=e(process.argv.slice(4));const n=await s(o);const c=o.json?JSON.stringify(n,null,2):r(n);const t=n.every(i=>i.passed)?0:1;setTimeout(()=>process.exit(t),5e3).unref();process.stdout.write(`${c}
|
|
9
|
+
`,()=>process.exit(t))}catch(e){process.stderr.write(`[buoy-mcp] flow run failed: ${e?.message??e}
|
|
10
|
+
`,()=>process.exit(1));setTimeout(()=>process.exit(1),5e3).unref()}})()}else if(process.argv[2]==="init"){try{const e=process.argv.slice(3);let s="auto";if(e.includes("--local"))s="local";else if(e.includes("--npx"))s="npx";const r=e.indexOf("--registry");const o=r!==-1?e[r+1]:void 0;w({mode:s,registry:o});process.exit(0)}catch(e){process.stderr.write(`[buoy-mcp] init failed: ${e?.message??e}
|
|
11
|
+
`);process.exit(1)}}else{b().catch(e=>{process.stderr.write(`[buoy-mcp] fatal: ${e?.message??e}
|
|
10
12
|
`);process.exit(1)})}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createRequire as v}from"module";const E=v(import.meta.url);import{SyncClient as y,createMcpServer as R}from"./chunk-GBZURL4J.mjs";import"./chunk-6EOB4FHJ.mjs";import"./chunk-OPHPYPJD.mjs";import"./chunk-MTTWR4T7.mjs";import{promises as S}from"fs";import $ from"path";import{Client as _}from"@modelcontextprotocol/sdk/client/index.js";import{InMemoryTransport as B}from"@modelcontextprotocol/sdk/inMemory.js";function P(e){const o={names:[]};for(let n=0;n<e.length;n++){const t=e[n];if(t==="--device")o.deviceId=e[++n];else if(t==="--update-snapshots")o.updateSnapshots=true;else if(t==="--json")o.json=true;else if(t==="--wait")o.waitMs=Number(e[++n])*1e3;else if(t==="--all")continue;else if(!t.startsWith("--"))o.names.push(t);else throw new Error(`Unknown option ${t}.`)}return o}async function I(){const e=$.join(process.cwd(),".buoy","flows");return(await S.readdir(e).catch(()=>[])).filter(o=>o.endsWith(".json")).map(o=>o.replace(/\.json$/,"")).sort()}async function g(e,o=n=>process.stderr.write(`${n}
|
|
2
|
+
`)){const n=e.names.length?e.names:await I();if(n.length===0)throw new Error('No saved flows. Save one with run_flow and save:"name", or pass a flow name.');const t=process.env.BUOY_BROKER_PORT;const s=new y({url:process.env.BUOY_BROKER_URL,port:t?Number(t):void 0,autoSpawnBroker:process.env.BUOY_NO_SPAWN!=="1"});const p=R(s);const[w,f]=B.createLinkedPair();const i=new _({name:"buoy-flow-run",version:"1"});try{await p.connect(f);await i.connect(w);await s.start();const a=Date.now()+(e.waitMs??6e4);while(Date.now()<a&&!s.connectedDevices().some(r=>r.isPro&&(!e.deviceId||r.deviceId===e.deviceId))){await new Promise(r=>setTimeout(r,500))}if(!s.connectedDevices().some(r=>r.isPro)){throw new Error("No Buoy Pro app connected. Start the app (with Buoy) on a simulator or device, then run again.")}const u=[];for(const r of n){const m=Date.now();o(`\u25B6 ${r}`);const l=await i.callTool({name:"run_flow",arguments:{flow:r,include:"none",...e.deviceId?{deviceId:e.deviceId}:{},...e.updateSnapshots?{updateSnapshots:true}:{}}},void 0,{timeout:10*6e4});const h=(l.content??[]).filter(c=>c.type==="text").map(c=>c.text).join("\n");const d=l.isError!==true;u.push({name:r,passed:d,ms:Date.now()-m,report:h});o(`${d?"\u2713":"\u2717"} ${r} (${((Date.now()-m)/1e3).toFixed(1)}s)`)}return u}finally{await i.close().catch(()=>void 0);await Promise.race([s.stop().catch(()=>void 0),new Promise(a=>setTimeout(a,2e3))])}}function j(e){const o=e.filter(t=>!t.passed);const n=[`${e.length-o.length} of ${e.length} flows passed.`];for(const t of o){n.push("",`\u2717 ${t.name}`,t.report.trim(),` Rerun: npx @buoy-gg/mcp flow run ${t.name}`)}return n.join("\n")}export{j as formatFlowResults,P as parseFlowRunArgs,g as runFlows};
|
package/dist/index.d.mts
CHANGED
|
@@ -16,7 +16,6 @@ interface SyncClientOptions {
|
|
|
16
16
|
/** Optional logger — writes to stderr by default (stdout is the MCP channel). */
|
|
17
17
|
log?: (msg: string) => void;
|
|
18
18
|
}
|
|
19
|
-
/** A device the broker knows about, merged with its advertised tools. */
|
|
20
19
|
interface DeviceInfo {
|
|
21
20
|
deviceId: string;
|
|
22
21
|
deviceName: string;
|
|
@@ -53,6 +52,8 @@ type BrokerMode = "connected" | "spawned";
|
|
|
53
52
|
declare class SyncClient {
|
|
54
53
|
private socket;
|
|
55
54
|
private admitted;
|
|
55
|
+
/** When this client was made: the startup window for {@link awaitBroker}. */
|
|
56
|
+
private readonly createdAt;
|
|
56
57
|
private lockedDevices;
|
|
57
58
|
private hostAccount;
|
|
58
59
|
private broker;
|
|
@@ -64,6 +65,15 @@ declare class SyncClient {
|
|
|
64
65
|
private capabilities;
|
|
65
66
|
/** Latest device list from the broker's `all-devices-update`. */
|
|
66
67
|
private devices;
|
|
68
|
+
/**
|
|
69
|
+
* The device this session last named explicitly. Agents name a device once,
|
|
70
|
+
* then leave deviceId out on later calls; with several apps connected every
|
|
71
|
+
* one of those calls failed ("Multiple devices connected"), which cost a
|
|
72
|
+
* turn at the start of almost every benchmark run. An explicit id sets it,
|
|
73
|
+
* and so does the project-folder match in resolveDevice. It never switches on its own: if that device goes away, calls
|
|
74
|
+
* without an id fail instead of picking another app.
|
|
75
|
+
*/
|
|
76
|
+
private sessionDevice;
|
|
67
77
|
/** Latest snapshot payload per `${deviceId}::${toolId}`. */
|
|
68
78
|
private snapshots;
|
|
69
79
|
/**
|
|
@@ -82,6 +92,12 @@ declare class SyncClient {
|
|
|
82
92
|
private health;
|
|
83
93
|
/** Dev-server origin per device — see {@link noteDevServerOrigin}. */
|
|
84
94
|
private devServerOrigins;
|
|
95
|
+
/** Newest crash-report console entry id seen per device — see {@link noteCrashReport}. */
|
|
96
|
+
private crashSeen;
|
|
97
|
+
/** Lookups still working out a device's dev-server folder (origin probe, then lsof). */
|
|
98
|
+
private rootLookups;
|
|
99
|
+
/** Folder each device's dev server runs from — see {@link devServerRoot}. */
|
|
100
|
+
private devServerRoots;
|
|
85
101
|
/** Devices whose origin probe already ran (a production bundle has none). */
|
|
86
102
|
private originProbed;
|
|
87
103
|
/** Last time each device appeared in the broker's device list. */
|
|
@@ -134,6 +150,12 @@ declare class SyncClient {
|
|
|
134
150
|
* is blocked.
|
|
135
151
|
*/
|
|
136
152
|
requireProDevice(deviceId?: string): string;
|
|
153
|
+
/**
|
|
154
|
+
* Where a device stands on access: connected and Pro ("ready"), not
|
|
155
|
+
* connected ("offline"), connected but its account not announced yet
|
|
156
|
+
* ("pending"), or announced on a tier without MCP access ("denied").
|
|
157
|
+
*/
|
|
158
|
+
accessState(deviceId: string): "ready" | "offline" | "pending" | "denied";
|
|
137
159
|
private healthOf;
|
|
138
160
|
/** The device answered — it's alive. */
|
|
139
161
|
private noteOk;
|
|
@@ -164,6 +186,15 @@ declare class SyncClient {
|
|
|
164
186
|
* out" and keeps retrying the same dead device.
|
|
165
187
|
*/
|
|
166
188
|
unresponsiveNote(deviceId: string): string;
|
|
189
|
+
/**
|
|
190
|
+
* A fresh crash report from the console tool (its crash flush runs as the
|
|
191
|
+
* app's React tree dies) means actions waiting on that device will likely
|
|
192
|
+
* never be answered: navigate to a screen that throws used to sit out its
|
|
193
|
+
* full 15 s. Cut their wait to the short leash instead of failing them at
|
|
194
|
+
* once, and only after a ping goes unanswered: an ErrorBoundary can catch a
|
|
195
|
+
* render error and the app lives.
|
|
196
|
+
*/
|
|
197
|
+
private noteCrashReport;
|
|
167
198
|
/**
|
|
168
199
|
* The newest error line the console tool synced for this device, if any.
|
|
169
200
|
* Reads the cached console snapshot rather than the device (which, by the
|
|
@@ -188,6 +219,21 @@ declare class SyncClient {
|
|
|
188
219
|
* recover a crashed app instead of telling the caller to go do it by hand.
|
|
189
220
|
*/
|
|
190
221
|
private learnDevServerOrigin;
|
|
222
|
+
private trackRootLookup;
|
|
223
|
+
/**
|
|
224
|
+
* Wait (briefly) for project-folder lookups still running, so an agent's
|
|
225
|
+
* first call right after apps connect can still be matched to this
|
|
226
|
+
* project's app instead of failing with "Multiple devices connected". Only
|
|
227
|
+
* worth it when no device is chosen yet and several apps are connected.
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* Right after the MCP starts, the broker may not have admitted this client
|
|
231
|
+
* yet: a first call then failed with "A verified Buoy account ... required"
|
|
232
|
+
* although the account is fine. Every call gives admission (and the first
|
|
233
|
+
* device list) a moment; once admitted this returns at once.
|
|
234
|
+
*/
|
|
235
|
+
awaitBroker(): Promise<void>;
|
|
236
|
+
settleDeviceChoice(maxMs?: number): Promise<void>;
|
|
191
237
|
/** The dev-server origin last reported by this device, if any. */
|
|
192
238
|
devServerOrigin(deviceId: string): string | null;
|
|
193
239
|
/**
|
|
@@ -252,6 +298,16 @@ declare class SyncClient {
|
|
|
252
298
|
payload: unknown;
|
|
253
299
|
stale?: string;
|
|
254
300
|
}>;
|
|
301
|
+
/**
|
|
302
|
+
* The last snapshot a device sent for a tool, kept after the device drops
|
|
303
|
+
* off. A fatal render error tears down the app's React tree, and Buoy's
|
|
304
|
+
* connection with it; @buoy-gg/console flushes its logs out first, so this
|
|
305
|
+
* is where the app's last words are once it can no longer be asked.
|
|
306
|
+
*/
|
|
307
|
+
lastSync(deviceId: string, toolId: string): {
|
|
308
|
+
payload: unknown;
|
|
309
|
+
ageMs: number | null;
|
|
310
|
+
} | undefined;
|
|
255
311
|
/** Header line for data served from cache because the device went quiet. */
|
|
256
312
|
private staleNote;
|
|
257
313
|
private waitForSnapshot;
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,6 @@ interface SyncClientOptions {
|
|
|
16
16
|
/** Optional logger — writes to stderr by default (stdout is the MCP channel). */
|
|
17
17
|
log?: (msg: string) => void;
|
|
18
18
|
}
|
|
19
|
-
/** A device the broker knows about, merged with its advertised tools. */
|
|
20
19
|
interface DeviceInfo {
|
|
21
20
|
deviceId: string;
|
|
22
21
|
deviceName: string;
|
|
@@ -53,6 +52,8 @@ type BrokerMode = "connected" | "spawned";
|
|
|
53
52
|
declare class SyncClient {
|
|
54
53
|
private socket;
|
|
55
54
|
private admitted;
|
|
55
|
+
/** When this client was made: the startup window for {@link awaitBroker}. */
|
|
56
|
+
private readonly createdAt;
|
|
56
57
|
private lockedDevices;
|
|
57
58
|
private hostAccount;
|
|
58
59
|
private broker;
|
|
@@ -64,6 +65,15 @@ declare class SyncClient {
|
|
|
64
65
|
private capabilities;
|
|
65
66
|
/** Latest device list from the broker's `all-devices-update`. */
|
|
66
67
|
private devices;
|
|
68
|
+
/**
|
|
69
|
+
* The device this session last named explicitly. Agents name a device once,
|
|
70
|
+
* then leave deviceId out on later calls; with several apps connected every
|
|
71
|
+
* one of those calls failed ("Multiple devices connected"), which cost a
|
|
72
|
+
* turn at the start of almost every benchmark run. An explicit id sets it,
|
|
73
|
+
* and so does the project-folder match in resolveDevice. It never switches on its own: if that device goes away, calls
|
|
74
|
+
* without an id fail instead of picking another app.
|
|
75
|
+
*/
|
|
76
|
+
private sessionDevice;
|
|
67
77
|
/** Latest snapshot payload per `${deviceId}::${toolId}`. */
|
|
68
78
|
private snapshots;
|
|
69
79
|
/**
|
|
@@ -82,6 +92,12 @@ declare class SyncClient {
|
|
|
82
92
|
private health;
|
|
83
93
|
/** Dev-server origin per device — see {@link noteDevServerOrigin}. */
|
|
84
94
|
private devServerOrigins;
|
|
95
|
+
/** Newest crash-report console entry id seen per device — see {@link noteCrashReport}. */
|
|
96
|
+
private crashSeen;
|
|
97
|
+
/** Lookups still working out a device's dev-server folder (origin probe, then lsof). */
|
|
98
|
+
private rootLookups;
|
|
99
|
+
/** Folder each device's dev server runs from — see {@link devServerRoot}. */
|
|
100
|
+
private devServerRoots;
|
|
85
101
|
/** Devices whose origin probe already ran (a production bundle has none). */
|
|
86
102
|
private originProbed;
|
|
87
103
|
/** Last time each device appeared in the broker's device list. */
|
|
@@ -134,6 +150,12 @@ declare class SyncClient {
|
|
|
134
150
|
* is blocked.
|
|
135
151
|
*/
|
|
136
152
|
requireProDevice(deviceId?: string): string;
|
|
153
|
+
/**
|
|
154
|
+
* Where a device stands on access: connected and Pro ("ready"), not
|
|
155
|
+
* connected ("offline"), connected but its account not announced yet
|
|
156
|
+
* ("pending"), or announced on a tier without MCP access ("denied").
|
|
157
|
+
*/
|
|
158
|
+
accessState(deviceId: string): "ready" | "offline" | "pending" | "denied";
|
|
137
159
|
private healthOf;
|
|
138
160
|
/** The device answered — it's alive. */
|
|
139
161
|
private noteOk;
|
|
@@ -164,6 +186,15 @@ declare class SyncClient {
|
|
|
164
186
|
* out" and keeps retrying the same dead device.
|
|
165
187
|
*/
|
|
166
188
|
unresponsiveNote(deviceId: string): string;
|
|
189
|
+
/**
|
|
190
|
+
* A fresh crash report from the console tool (its crash flush runs as the
|
|
191
|
+
* app's React tree dies) means actions waiting on that device will likely
|
|
192
|
+
* never be answered: navigate to a screen that throws used to sit out its
|
|
193
|
+
* full 15 s. Cut their wait to the short leash instead of failing them at
|
|
194
|
+
* once, and only after a ping goes unanswered: an ErrorBoundary can catch a
|
|
195
|
+
* render error and the app lives.
|
|
196
|
+
*/
|
|
197
|
+
private noteCrashReport;
|
|
167
198
|
/**
|
|
168
199
|
* The newest error line the console tool synced for this device, if any.
|
|
169
200
|
* Reads the cached console snapshot rather than the device (which, by the
|
|
@@ -188,6 +219,21 @@ declare class SyncClient {
|
|
|
188
219
|
* recover a crashed app instead of telling the caller to go do it by hand.
|
|
189
220
|
*/
|
|
190
221
|
private learnDevServerOrigin;
|
|
222
|
+
private trackRootLookup;
|
|
223
|
+
/**
|
|
224
|
+
* Wait (briefly) for project-folder lookups still running, so an agent's
|
|
225
|
+
* first call right after apps connect can still be matched to this
|
|
226
|
+
* project's app instead of failing with "Multiple devices connected". Only
|
|
227
|
+
* worth it when no device is chosen yet and several apps are connected.
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* Right after the MCP starts, the broker may not have admitted this client
|
|
231
|
+
* yet: a first call then failed with "A verified Buoy account ... required"
|
|
232
|
+
* although the account is fine. Every call gives admission (and the first
|
|
233
|
+
* device list) a moment; once admitted this returns at once.
|
|
234
|
+
*/
|
|
235
|
+
awaitBroker(): Promise<void>;
|
|
236
|
+
settleDeviceChoice(maxMs?: number): Promise<void>;
|
|
191
237
|
/** The dev-server origin last reported by this device, if any. */
|
|
192
238
|
devServerOrigin(deviceId: string): string | null;
|
|
193
239
|
/**
|
|
@@ -252,6 +298,16 @@ declare class SyncClient {
|
|
|
252
298
|
payload: unknown;
|
|
253
299
|
stale?: string;
|
|
254
300
|
}>;
|
|
301
|
+
/**
|
|
302
|
+
* The last snapshot a device sent for a tool, kept after the device drops
|
|
303
|
+
* off. A fatal render error tears down the app's React tree, and Buoy's
|
|
304
|
+
* connection with it; @buoy-gg/console flushes its logs out first, so this
|
|
305
|
+
* is where the app's last words are once it can no longer be asked.
|
|
306
|
+
*/
|
|
307
|
+
lastSync(deviceId: string, toolId: string): {
|
|
308
|
+
payload: unknown;
|
|
309
|
+
ageMs: number | null;
|
|
310
|
+
} | undefined;
|
|
255
311
|
/** Header line for data served from cache because the device went quiet. */
|
|
256
312
|
private staleNote;
|
|
257
313
|
private waitForSnapshot;
|