@gjsify/devtools-mcp 0.52.0 → 0.54.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/README.md CHANGED
@@ -54,6 +54,23 @@ A `DevtoolsToolProfile` maps the generic + app-specific DBus methods to MCP tool
54
54
  - **browser** — the [`@gjsify/devtools-browser`](../devtools-browser) web-debugging tools: `navigate`, `page_screenshot`, `eval_js`, `inspect_element`, `dom_tree`, `get_network`, … (+ generics).
55
55
  - **cdp** — the [`@gjsify/devtools-cdp`](../devtools-cdp) WebKit-remote-inspector tools: `cdp_send` (any `Domain.command`), `cdp_connect` / `cdp_discover_targets` / `cdp_drain_events`, plus ~13 curated typed tools (`cdp_runtime_evaluate`, `cdp_dom_get_document`, `cdp_css_get_computed_style_for_node`, …) generated from the protocol spec (+ generics).
56
56
 
57
+ ## Quick start — `gjsify devtools` (for scripts, not agents)
58
+
59
+ The bridge is the wrong shape for a Makefile, a CI step or a shell script: an MCP answer is base64
60
+ inside a JSON-RPC frame, so `Screenshot` cannot be piped into a file. `gjsify devtools` is the same
61
+ client with an argv front end — `shot`, `find`, `activate`, `key`, `tree`, `property`, `resize`,
62
+ `present`, `status`, `actions`, `css`, `gsettings`, `instances`, `toplevels`, `focused`:
63
+
64
+ ```bash
65
+ gjsify devtools shot /tmp/app.png --bus-name org.example.App # file + the size the PNG carries
66
+ gjsify devtools find Adw.StatusPage:error --bus-name org.example.App # stdout = the widget path
67
+ gjsify devtools help # every operation and flag
68
+ ```
69
+
70
+ It speaks the same transport precedence as the bridge above, `--address` included, so it works on a
71
+ bus-less host where `gdbus` does not. Set `gjsify.devtools.busNameBase` in `package.json` and the
72
+ flag stops being needed.
73
+
57
74
  ## Library API
58
75
 
59
76
  ```ts
@@ -66,9 +83,25 @@ await runDevtoolsMcp(storybookProfile('org.example.Storybook'));
66
83
  await runDevtoolsMcp({ name: 'my-app-devtools', version: '0.10.0', busNameBase: 'org.example.App' });
67
84
  ```
68
85
 
86
+ ### `runDevtoolsCli(argv, options?)` — the argv front end
87
+
88
+ ```ts
89
+ import { runDevtoolsCli } from '@gjsify/devtools-mcp';
90
+
91
+ // → 0 done, 1 the app said no, 2 the request itself was wrong.
92
+ await runDevtoolsCli(['find', 'Adw.StatusPage:error', '--bus-name', 'org.example.App']);
93
+ ```
94
+
95
+ It returns the exit code rather than calling `System.exit`, so a host owns the process. `options
96
+ .createClient` swaps in any `DevtoolsCliClient` — a test fake, or a transport of your own — which is
97
+ the same structural seam `DbusDevtoolsClient` is declared behind. The screenshot invariants (empty
98
+ bytes are a failure, the size comes from the PNG) live in [`@gjsify/devtools`](../devtools)'s
99
+ `captureShot`, so a caller driving the plane from its own code gets them by importing a function.
100
+
69
101
  ## Exports
70
102
 
71
103
  - `runDevtoolsMcp(profile)` — build the MCP server, register the profile's tools, serve over `GjsStdioTransport`.
104
+ - `runDevtoolsCli(argv, options?)` — the argv front end behind `gjsify devtools`; returns the exit code. `DevtoolsCliClient` (the subset of `DbusDevtoolsClient` it uses), `DevtoolsCliGlobalOptions`, `RunDevtoolsCliOptions`.
72
105
  - `DbusDevtoolsClient` — the DBus client (`control()` for raw GVariant replies, `jsonCall()` for `…->s` JSON methods, `transport` for the resolved transport, `describeTarget()` for diagnostics).
73
106
  - `chooseClientTransport(input)` — the precedence above as a pure function; `connectToDevtools(ctx, deps?)` — that precedence turned into a live connection (dial-then-fallback; `deps` exists so every row is testable on a host with no bus daemon); `ClientTransportChoice`, `DbusDevtoolsClientOptions`, `DevtoolsInstanceRef`.
74
107
  - `registerGenericTools`, `storybookProfile` / `registerStorybookTools`.
package/lib/esm/cli.js ADDED
@@ -0,0 +1,3 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{DbusDevtoolsClient as e}from"./dbus-client.js";import t from"@girs/glib-2.0";import{captureShot as n}from"@gjsify/devtools/shot";function usage(){return[`usage: gjsify devtools <operation> [operands] [options]`,``,`operations:`,` shot [out.png] Screenshot to a file; prints the size the PNG actually carries`,` find <selector> first visible widget matching Type / :css-class / Type:css-class → its path`,` activate <path> activate the widget at a widget path (exits 1 if it is inert)`,` key <accel> [path] send a key to a path, or to the focused widget when omitted`,` tree [path] dump the widget tree as JSON (--depth N, default 8)`,` property <path> <p> read one GObject property off a widget`,` focused the focused widget`,` toplevels the live toplevel windows`,` resize <w> <h> resize the active window (prints the size ASKED for — read the PNG for the real one)`,` present raise + focus the active window`,` status liveness + whatever extensions contribute`,` actions [scope name] list the GActions, or activate scope.name`,` css [name] list installed CSS providers, or install one (--file <css>)`,` gsettings <schema> dump an installed GSettings schema`,` instances reachable devtools-enabled instances`,``,`options:`,` --bus-name <id> app DBus base name (required unless package.json#gjsify.devtools.busNameBase)`,` --address <addr> peer D-Bus address (unix:path=… / nonce-tcp:…) instead of the session bus`,` --instance <label> instance label of a multi-instance app`,` --timeout <seconds> poll GetStatus until the app answers (default 30; 0 = do not wait)`,` --scope <s> shot: what to capture — "window" (default) or a widget path`,` --settle <ms> shot: pause before the first capture`,` --retries <n> shot: attempts after an empty answer (default 8)`].join(`
2
+ `)}function parseArgv(e){let t=[],n=new Map;for(let r=0;r<e.length;r++){let i=e[r];if(!i.startsWith(`--`)){t.push(i);continue}let a=i.indexOf(`=`);if(a!==-1){n.set(i.slice(2,a),i.slice(a+1));continue}let o=i.slice(2),s=e[r+1];s!==void 0&&!s.startsWith(`--`)?(n.set(o,s),r++):n.set(o,`true`)}let[r=``,...i]=t;return{operation:r,operands:i,flags:n}}function flagInt(e,t,n){let r=e.get(t);if(r===void 0)return n;let i=Number(r);return Number.isFinite(i)?i:n}function sleep(e){return new Promise(n=>{t.timeout_add(t.PRIORITY_DEFAULT,e,()=>(n(),t.SOURCE_REMOVE))})}async function printJson(e,t,n,r=null){console.log(await e.jsonCall(t,n,r))}async function firstString(e){let[t]=e.recursiveUnpack();return t}async function firstBoolean(e){let[t]=e.recursiveUnpack();return t}async function waitForControlPlane(e,t,n){if(n<=0)return;let r=Date.now()+n*1e3,i;do try{await e.control(t,`GetStatus`,null,`(s)`);return}catch(e){i=e,await sleep(250)}while(Date.now()<r);throw Error(`${e.describeTarget()} did not answer GetStatus within ${n}s — is the app running with GJSIFY_DEVTOOLS=1? Last error: ${i}`)}async function runOperation(e,r,i){let{operation:a,operands:o,flags:s}=i,strv=e=>t.Variant.new_string(e);switch(a){case`shot`:{let i=await n(async n=>(await e.control(r,`Screenshot`,t.Variant.new_tuple([strv(n)]),`(ay)`)).get_child_value(0).deepUnpack(),o[0]??`screenshot.png`,s.get(`scope`)??`window`,{settleMs:flagInt(s,`settle`,0),retries:flagInt(s,`retries`,8)});return console.log(`wrote ${i.path} (${i.byteLength} bytes, ${i.width}×${i.height})`),0}case`find`:{let n=o[0];if(!n)throw Error(`usage: gjsify devtools find <Type[:css-class]>`);let i=await firstString(await e.control(r,`FindWidget`,t.Variant.new_tuple([strv(n)]),`(s)`));return i?(console.log(i),0):(console.error(`no visible widget matches "${n}"`),1)}case`activate`:{let n=o[0];if(!n)throw Error(`usage: gjsify devtools activate <widget-path>`);return await firstBoolean(await e.control(r,`ActivateWidget`,t.Variant.new_tuple([strv(n)]),`(b)`))?(console.log(`activated ${n}`),0):(console.error(`ActivateWidget refused ${n} — it has no default activation`),1)}case`key`:{let[n,i=``]=o;if(!n)throw Error(`usage: gjsify devtools key <accel> [widget-path]`);return await firstBoolean(await e.control(r,`SendKey`,t.Variant.new_tuple([strv(n),strv(i)]),`(b)`))?(console.log(`sent ${n} to ${i||`the focused widget`}`),0):(console.error(`${i||`the focused widget`} has no key controller — "${n}" went nowhere`),1)}case`tree`:{let[n=``]=o;return await printJson(e,r,`DumpTree`,t.Variant.new_tuple([strv(n),t.Variant.new_int32(flagInt(s,`depth`,8))])),0}case`property`:{let[n,i]=o;if(!n||!i)throw Error(`usage: gjsify devtools property <widget-path> <property>`);return await printJson(e,r,`GetProperty`,t.Variant.new_tuple([strv(n),strv(i)])),0}case`focused`:return await printJson(e,r,`GetFocused`),0;case`toplevels`:return await printJson(e,r,`ListToplevels`),0;case`status`:return await printJson(e,r,`GetStatus`),0;case`resize`:{let[n,i]=o.map(Number);if(!Number.isFinite(n)||!Number.isFinite(i))throw Error(`usage: gjsify devtools resize <width> <height>`);return await e.control(r,`ResizeWindow`,t.Variant.new_tuple([t.Variant.new_int32(n),t.Variant.new_int32(i)]),`(ii)`),console.log(`asked for ${n}×${i} — the app reports the size it was given, not the one it took`),0}case`present`:return await e.control(r,`PresentWindow`,null,null),console.log(`presented`),0;case`actions`:{let[n,i,a]=o;return!n||!i?(await printJson(e,r,`ListActions`),0):(await e.control(r,`ActivateAction`,t.Variant.new_tuple([strv(n),strv(i),strv(a??``)]),null),console.log(`activated ${n}.${i}`),0)}case`css`:{let[n]=o,i=s.get(`file`);if(!n||!i)return await printJson(e,r,`DumpCss`),0;let a=await readTextFile(i);return await firstBoolean(await e.control(r,`SwapCss`,t.Variant.new_tuple([strv(n),strv(a)]),`(b)`))?(console.log(`applied CSS provider "${n}"`),0):(console.error(`no display — CSS provider "${n}" was not applied`),1)}case`gsettings`:{let[n]=o;if(!n)throw Error(`usage: gjsify devtools gsettings <schema>`);return await printJson(e,r,`DumpGSettings`,t.Variant.new_tuple([strv(n)])),0}case`instances`:return console.log(JSON.stringify(await e.listInstances(),null,2)),0;default:return console.error(`unknown operation "${a}"\n\n${usage()}`),2}}async function readTextFile(e){let{default:t}=await import(`@girs/gio-2.0`),[n,r]=t.File.new_for_path(e).load_contents(null);if(!n)throw Error(`cannot read ${e}`);return new TextDecoder().decode(r)}async function runDevtoolsCli(t,n={}){let r=parseArgv(t);if(r.operation===``||r.operation===`help`||r.flags.get(`help`)===`true`)return console.log(usage()),r.operation===``?2:0;let i=r.flags.get(`bus-name`);if(!i)return console.error(`gjsify devtools: no app to talk to — pass --bus-name <org.example.App>, or set package.json#gjsify.devtools.busNameBase.
3
+ `),2;let a=r.flags.get(`address`),o=r.flags.get(`instance`),s=n.createClient?n.createClient(i,a,o):new e(i,{address:a,instance:o});try{return await waitForControlPlane(s,o,flagInt(r.flags,`timeout`,30)),await runOperation(s,o,r)}catch(e){return console.error(`gjsify devtools ${r.operation}: ${e instanceof Error?e.message:e}`),1}}export{runDevtoolsCli};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{chooseClientTransport as e}from"./transport-choice.js";import t from"@girs/gio-2.0";import n from"@girs/glib-2.0";import{DEVTOOLS_ADDRESS_ENV as r,DEVTOOLS_INSTANCE_ENV as i,DEVTOOLS_INTERFACE as a,devtoolsAddressFilePath as o,resolveBusAddress as s}from"@gjsify/devtools-protocol";const c=`org.freedesktop.DBus`;function envValue(e){let t=n.getenv(e);return t&&t!==``?t:null}function readAddressFile(e){if(!n.file_test(e,n.FileTest.EXISTS))return null;try{let[t,r]=n.file_get_contents(e);if(!t)return null;let i=new TextDecoder().decode(r).trim();return i===``?null:i}catch{return null}}function deleteAddressFile(e){try{t.File.new_for_path(e).delete(null)}catch{}}function dialPeer(e){return t.DBusConnection.new_for_address_sync(e,t.DBusConnectionFlags.AUTHENTICATION_CLIENT,null,null)}function threeWaysIn(e,t){return`Launch the app with GJSIFY_DEVTOOLS=1 (it then publishes ${o(`<runtime-dir>`,e,t)}), or set ${r} on both sides, or pass --address to \`gjsify debug\`.`}function connectToDevtools(n,i={}){let a=i.dial??dialPeer,o=i.sessionBus??(()=>t.bus_get_sync(t.BusType.SESSION,null)),s=n.addressFileValue,c=null;for(;;){let t=e({optionAddress:n.optionAddress,envAddress:n.envAddress,addressFileValue:s,sessionBusAvailable:n.sessionBusAvailable});if(t.kind===`session-bus`)try{return{bus:o(),transport:t}}catch(e){throw Error(`[gjsify-devtools-mcp] the session bus advertised in DBUS_SESSION_BUS_ADDRESS stopped answering while connecting to ${n.busNameBase}: ${e}. ${threeWaysIn(n.busNameBase,n.instance)}`)}if(t.kind===`unavailable`){let e=c?` (the address this app had published, ${c}, is dead — that stale claim was just removed)`:``;throw Error(`[gjsify-devtools-mcp] no way to reach ${n.busNameBase}: this machine has no session bus, and no peer address was given${e}. ${threeWaysIn(n.busNameBase,n.instance)}`)}try{return{bus:a(t.address),transport:t}}catch(e){if(t.source!==`address-file`)throw Error(`[gjsify-devtools-mcp] cannot reach ${n.busNameBase} at ${t.address} (from ${t.source===`option`?`--address`:r}): ${e}. Start the app with GJSIFY_DEVTOOLS=1 ${r}=${t.address} so it listens there, or omit the address and let the published address file / the session bus decide.`);console.error(`[gjsify-devtools-mcp] the address published for ${n.busNameBase} (${t.address} in ${n.addressFilePath}) does not answer: ${e}. Removing that stale claim — an app killed with Ctrl-C or SIGKILL never gets to retract it — and continuing down the precedence.`),deleteAddressFile(n.addressFilePath),c=t.address,s=null}}}function sessionBusAvailable(){if(!envValue(`DBUS_SESSION_BUS_ADDRESS`))return!1;try{return t.bus_get_sync(t.BusType.SESSION,null),!0}catch{return!1}}var DbusDevtoolsClient=class{constructor(e,t={}){this.busNameBase=e;let a=t.instance??envValue(i)??void 0,s=o(n.get_user_runtime_dir(),e,a),{bus:c,transport:l}=connectToDevtools({busNameBase:e,instance:a,addressFilePath:s,optionAddress:t.address,envAddress:envValue(r),addressFileValue:readAddressFile(s),sessionBusAvailable:sessionBusAvailable()});this._bus=c,this.transport=l}describeTarget(e){return this.transport.kind===`peer`?`peer address ${this.transport.address} (from ${this.transport.source})`:`session bus name ${this.resolve(e).busName}`}resolve(e){return s(this.busNameBase,e)}_destination(e){return this.transport.kind===`peer`?null:this.resolve(e).busName}control(e,r,i,o){let{objectPath:s}=this.resolve(e),c=this._destination(e);return new Promise((e,l)=>{this._bus.call(c,s,a,r,i,o?n.VariantType.new(o):null,t.DBusCallFlags.NONE,-1,null,(t,n)=>{try{e(t.call_finish(n))}catch(e){l(e)}})})}async jsonCall(e,t,n=null){let[r]=(await this.control(e,t,n,`(s)`)).recursiveUnpack();return JSON.stringify(JSON.parse(r),null,2)}nameHasOwner(e){return this.transport.kind===`peer`?Promise.resolve(!this._bus.is_closed()):new Promise(r=>{this._bus.call(c,`/org/freedesktop/DBus`,c,`NameHasOwner`,n.Variant.new_tuple([n.Variant.new_string(e)]),n.VariantType.new(`(b)`),t.DBusCallFlags.NONE,-1,null,(e,t)=>{try{r(e.call_finish(t).recursiveUnpack()[0])}catch{r(!1)}})})}listInstances(){if(this.transport.kind===`peer`){let e=this.transport.address;return Promise.resolve(this._bus.is_closed()?[]:[{instance:this.resolve().instance,address:e}])}return new Promise(e=>{this._bus.call(c,`/org/freedesktop/DBus`,c,`ListNames`,null,n.VariantType.new(`(as)`),t.DBusCallFlags.NONE,-1,null,(t,n)=>{try{let r=t.call_finish(n).recursiveUnpack()[0],i=this.busNameBase;e(r.filter(e=>e===i||e.startsWith(`${i}.`)).map(e=>({instance:e===i?`default`:e.slice(i.length+1),busName:e})))}catch{e([])}})})}};export{DbusDevtoolsClient,connectToDevtools};
1
+ import"./_virtual/_rolldown/runtime.js";import{chooseClientTransport as e}from"./transport-choice.js";import t from"@girs/glib-2.0";import n from"@girs/gio-2.0";import{DEVTOOLS_ADDRESS_ENV as r,DEVTOOLS_INSTANCE_ENV as i,DEVTOOLS_INTERFACE as a,devtoolsAddressFilePath as o,resolveBusAddress as s}from"@gjsify/devtools-protocol";const c=`org.freedesktop.DBus`;function envValue(e){let n=t.getenv(e);return n&&n!==``?n:null}function readAddressFile(e){if(!t.file_test(e,t.FileTest.EXISTS))return null;try{let[n,r]=t.file_get_contents(e);if(!n)return null;let i=new TextDecoder().decode(r).trim();return i===``?null:i}catch{return null}}function deleteAddressFile(e){try{n.File.new_for_path(e).delete(null)}catch{}}function dialPeer(e){return n.DBusConnection.new_for_address_sync(e,n.DBusConnectionFlags.AUTHENTICATION_CLIENT,null,null)}function threeWaysIn(e,t){return`Launch the app with GJSIFY_DEVTOOLS=1 (it then publishes ${o(`<runtime-dir>`,e,t)}), or set ${r} on both sides, or pass --address to \`gjsify debug\`.`}function connectToDevtools(t,i={}){let a=i.dial??dialPeer,o=i.sessionBus??(()=>n.bus_get_sync(n.BusType.SESSION,null)),s=t.addressFileValue,c=null;for(;;){let n=e({optionAddress:t.optionAddress,envAddress:t.envAddress,addressFileValue:s,sessionBusAvailable:t.sessionBusAvailable});if(n.kind===`session-bus`)try{return{bus:o(),transport:n}}catch(e){throw Error(`[gjsify-devtools-mcp] the session bus advertised in DBUS_SESSION_BUS_ADDRESS stopped answering while connecting to ${t.busNameBase}: ${e}. ${threeWaysIn(t.busNameBase,t.instance)}`)}if(n.kind===`unavailable`){let e=c?` (the address this app had published, ${c}, is dead — that stale claim was just removed)`:``;throw Error(`[gjsify-devtools-mcp] no way to reach ${t.busNameBase}: this machine has no session bus, and no peer address was given${e}. ${threeWaysIn(t.busNameBase,t.instance)}`)}try{return{bus:a(n.address),transport:n}}catch(e){if(n.source!==`address-file`)throw Error(`[gjsify-devtools-mcp] cannot reach ${t.busNameBase} at ${n.address} (from ${n.source===`option`?`--address`:r}): ${e}. Start the app with GJSIFY_DEVTOOLS=1 ${r}=${n.address} so it listens there, or omit the address and let the published address file / the session bus decide.`);console.error(`[gjsify-devtools-mcp] the address published for ${t.busNameBase} (${n.address} in ${t.addressFilePath}) does not answer: ${e}. Removing that stale claim — an app killed with Ctrl-C or SIGKILL never gets to retract it — and continuing down the precedence.`),deleteAddressFile(t.addressFilePath),c=n.address,s=null}}}function sessionBusAvailable(){if(!envValue(`DBUS_SESSION_BUS_ADDRESS`))return!1;try{return n.bus_get_sync(n.BusType.SESSION,null),!0}catch{return!1}}var DbusDevtoolsClient=class{constructor(e,n={}){this.busNameBase=e;let a=n.instance??envValue(i)??void 0,s=o(t.get_user_runtime_dir(),e,a),{bus:c,transport:l}=connectToDevtools({busNameBase:e,instance:a,addressFilePath:s,optionAddress:n.address,envAddress:envValue(r),addressFileValue:readAddressFile(s),sessionBusAvailable:sessionBusAvailable()});this._bus=c,this.transport=l}describeTarget(e){return this.transport.kind===`peer`?`peer address ${this.transport.address} (from ${this.transport.source})`:`session bus name ${this.resolve(e).busName}`}resolve(e){return s(this.busNameBase,e)}_destination(e){return this.transport.kind===`peer`?null:this.resolve(e).busName}control(e,r,i,o){let{objectPath:s}=this.resolve(e),c=this._destination(e);return new Promise((e,l)=>{this._bus.call(c,s,a,r,i,o?t.VariantType.new(o):null,n.DBusCallFlags.NONE,-1,null,(t,n)=>{try{e(t.call_finish(n))}catch(e){l(e)}})})}async jsonCall(e,t,n=null){let[r]=(await this.control(e,t,n,`(s)`)).recursiveUnpack();return JSON.stringify(JSON.parse(r),null,2)}nameHasOwner(e){return this.transport.kind===`peer`?Promise.resolve(!this._bus.is_closed()):new Promise(r=>{this._bus.call(c,`/org/freedesktop/DBus`,c,`NameHasOwner`,t.Variant.new_tuple([t.Variant.new_string(e)]),t.VariantType.new(`(b)`),n.DBusCallFlags.NONE,-1,null,(e,t)=>{try{r(e.call_finish(t).recursiveUnpack()[0])}catch{r(!1)}})})}listInstances(){if(this.transport.kind===`peer`){let e=this.transport.address;return Promise.resolve(this._bus.is_closed()?[]:[{instance:this.resolve().instance,address:e}])}return new Promise(e=>{this._bus.call(c,`/org/freedesktop/DBus`,c,`ListNames`,null,t.VariantType.new(`(as)`),n.DBusCallFlags.NONE,-1,null,(t,n)=>{try{let r=t.call_finish(n).recursiveUnpack()[0],i=this.busNameBase;e(r.filter(e=>e===i||e.startsWith(`${i}.`)).map(e=>({instance:e===i?`default`:e.slice(i.length+1),busName:e})))}catch{e([])}})})}};export{DbusDevtoolsClient,connectToDevtools};
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{chooseClientTransport as e}from"./transport-choice.js";import{DbusDevtoolsClient as t}from"./dbus-client.js";import{fail as n,image as r,ok as i}from"./tool-result.js";import{dbusError as a}from"./error-map.js";import{registerGenericTools as o}from"./generic-tools.js";import{GjsStdioTransport as s}from"./stdio-transport.js";import{runDevtoolsMcp as c}from"./run-server.js";import{registerStorybookTools as l,storybookProfile as u}from"./profiles/storybook.js";import{browserProfile as d,registerBrowserTools as f}from"./profiles/browser.js";import{cdpProfile as p,registerCdpTools as m}from"./profiles/cdp.js";import{NsCdpClient as h}from"./transports/ns-cdp-client.js";import{nativescriptProfile as g,registerNsGenericTools as _,runNativescriptMcp as v}from"./profiles/nativescript.js";export*from"@gjsify/devtools-protocol";export{t as DbusDevtoolsClient,s as GjsStdioTransport,h as NsCdpClient,d as browserProfile,p as cdpProfile,e as chooseClientTransport,a as dbusError,n as fail,r as image,g as nativescriptProfile,i as ok,f as registerBrowserTools,m as registerCdpTools,o as registerGenericTools,_ as registerNsGenericTools,l as registerStorybookTools,c as runDevtoolsMcp,v as runNativescriptMcp,u as storybookProfile};
1
+ import{chooseClientTransport as e}from"./transport-choice.js";import{DbusDevtoolsClient as t}from"./dbus-client.js";import{runDevtoolsCli as n}from"./cli.js";import{fail as r,image as i,ok as a}from"./tool-result.js";import{dbusError as o}from"./error-map.js";import{registerGenericTools as s}from"./generic-tools.js";import{GjsStdioTransport as c}from"./stdio-transport.js";import{runDevtoolsMcp as l}from"./run-server.js";import{registerStorybookTools as u,storybookProfile as d}from"./profiles/storybook.js";import{browserProfile as f,registerBrowserTools as p}from"./profiles/browser.js";import{cdpProfile as m,registerCdpTools as h}from"./profiles/cdp.js";import{NsCdpClient as g}from"./transports/ns-cdp-client.js";import{nativescriptProfile as _,registerNsGenericTools as v,runNativescriptMcp as y}from"./profiles/nativescript.js";export*from"@gjsify/devtools-protocol";export{t as DbusDevtoolsClient,c as GjsStdioTransport,g as NsCdpClient,f as browserProfile,m as cdpProfile,e as chooseClientTransport,o as dbusError,r as fail,i as image,_ as nativescriptProfile,a as ok,p as registerBrowserTools,h as registerCdpTools,s as registerGenericTools,v as registerNsGenericTools,u as registerStorybookTools,n as runDevtoolsCli,l as runDevtoolsMcp,y as runNativescriptMcp,d as storybookProfile};
@@ -1 +1 @@
1
- import"../_virtual/_rolldown/runtime.js";import{fail as e,image as t,ok as n}from"../tool-result.js";import{GjsStdioTransport as r}from"../stdio-transport.js";import{NsCdpClient as i}from"../transports/ns-cdp-client.js";import a from"@girs/gio-2.0";import o from"@girs/glib-2.0";import{z as s}from"zod";import{McpServer as c}from"@modelcontextprotocol/sdk/server/mcp.js";async function call(e,t,n={}){let r={method:t,params:n},i=await e.request(r);if(!i.ok)throw Error(`${i.error.code}: ${i.error.message}`);return i.result}function okJson(e){return n(JSON.stringify(e,null,2))}function registerNsGenericTools(e){let{server:n,client:r}=e;n.registerTool(`get_status`,{description:`JSON snapshot of the running NativeScript app: app id, platform, instance, root view, pause state.`,inputSchema:s.object({})},async()=>{try{return okJson(await call(r,`GetStatus`))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`list_toplevels`,{description:"List the app's toplevel(s) — NativeScript has a single root view (`toplevel:0`).",inputSchema:s.object({})},async()=>{try{return okJson(await call(r,`ListToplevels`))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`dump_tree`,{description:'Dump the native view tree as JSON from `root` (a view path like "toplevel:0/child:2", or omit for the root view), bounded by `depth`. Each node: path, type (NativeScript component), name, cssClasses, visible, optional text.',inputSchema:s.object({root:s.string().optional(),depth:s.number().optional()})},async({root:t,depth:n})=>{try{return okJson(await call(r,`DumpTree`,{root:t??``,depth:n??8}))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`get_property`,{description:`Read a view's property by view path + property name (JSON-safe value).`,inputSchema:s.object({path:s.string(),prop:s.string()})},async({path:t,prop:n})=>{try{return okJson(await call(r,`GetProperty`,{path:t,prop:n}))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`screenshot`,{description:`Capture a PNG screenshot of the root native view, rendered in-process by the app (no adb, no screen-record permission). Needs the app foregrounded + laid out.`,inputSchema:s.object({})},async()=>{try{let n=await call(r,`Screenshot`);return!n||!n.data?e.fail(`Screenshot returned no data — the root view is not laid out yet (bring the app to the foreground and retry), or device_screenshot as a fallback.`):t(n.data)}catch(t){return e.fail(messageOf(t))}}),registerDeviceScreenshot(e)}function registerDeviceScreenshot(e){e.server.registerTool(`device_screenshot`,{description:"PNG of the WHOLE Android device/emulator screen via `adb exec-out screencap -p` (host-side; works without the in-app devtools agent). Pass `serial` to target a specific device.",inputSchema:s.object({serial:s.string().optional()})},async({serial:n})=>{try{let r=await adbScreencap(n);return r?t(r):e.fail("adb screencap returned no data — is a device connected (`adb devices`)?")}catch(t){return e.fail(`adb screencap failed: ${messageOf(t)}`)}})}function adbScreencap(e){let t=[`adb`,...e?[`-s`,e]:[],`exec-out`,`screencap`,`-p`],n=a.Subprocess.new(t,a.SubprocessFlags.STDOUT_PIPE|a.SubprocessFlags.STDERR_SILENCE);return new Promise((e,t)=>{n.communicate_async(null,null,(r,i)=>{try{let[,t]=n.communicate_finish(i),r=t?.get_data();if(!r||r.length===0)return e(null);e(o.base64_encode(r))}catch(e){t(e)}})})}function messageOf(e){return e instanceof Error?e.message:String(e)}function nativescriptProfile(e){return{name:`gjsify-nativescript-devtools`,version:`0.11.0`,connect:e}}async function runNativescriptMcp(t){let a=new i(t.connect),s=new c({name:t.name,version:t.version}),l={server:s,client:a,ok:n,fail:e};registerNsGenericTools(l),t.registerTools?.(l);let u=o.MainLoop.new(null,!1),d=new r(()=>u.quit());a.connect().then(()=>console.error(`[gjsify-devtools-mcp] ${t.name} connected to the NativeScript inspector`),e=>console.error(`[gjsify-devtools-mcp] NS inspector not reachable yet (tools will retry): ${messageOf(e)}`)),s.connect(d).then(()=>console.error(`[gjsify-devtools-mcp] ${t.name} on stdio (NativeScript CDP)`),e=>{console.error(`[gjsify-devtools-mcp] failed to connect:`,e),u.quit()}),u.run()}export{nativescriptProfile,registerNsGenericTools,runNativescriptMcp};
1
+ import"../_virtual/_rolldown/runtime.js";import{fail as e,image as t,ok as n}from"../tool-result.js";import{GjsStdioTransport as r}from"../stdio-transport.js";import{NsCdpClient as i}from"../transports/ns-cdp-client.js";import a from"@girs/glib-2.0";import o from"@girs/gio-2.0";import{z as s}from"zod";import{McpServer as c}from"@modelcontextprotocol/sdk/server/mcp.js";async function call(e,t,n={}){let r={method:t,params:n},i=await e.request(r);if(!i.ok)throw Error(`${i.error.code}: ${i.error.message}`);return i.result}function okJson(e){return n(JSON.stringify(e,null,2))}function registerNsGenericTools(e){let{server:n,client:r}=e;n.registerTool(`get_status`,{description:`JSON snapshot of the running NativeScript app: app id, platform, instance, root view, pause state.`,inputSchema:s.object({})},async()=>{try{return okJson(await call(r,`GetStatus`))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`list_toplevels`,{description:"List the app's toplevel(s) — NativeScript has a single root view (`toplevel:0`).",inputSchema:s.object({})},async()=>{try{return okJson(await call(r,`ListToplevels`))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`dump_tree`,{description:'Dump the native view tree as JSON from `root` (a view path like "toplevel:0/child:2", or omit for the root view), bounded by `depth`. Each node: path, type (NativeScript component), name, cssClasses, visible, optional text.',inputSchema:s.object({root:s.string().optional(),depth:s.number().optional()})},async({root:t,depth:n})=>{try{return okJson(await call(r,`DumpTree`,{root:t??``,depth:n??8}))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`get_property`,{description:`Read a view's property by view path + property name (JSON-safe value).`,inputSchema:s.object({path:s.string(),prop:s.string()})},async({path:t,prop:n})=>{try{return okJson(await call(r,`GetProperty`,{path:t,prop:n}))}catch(t){return e.fail(messageOf(t))}}),n.registerTool(`screenshot`,{description:`Capture a PNG screenshot of the root native view, rendered in-process by the app (no adb, no screen-record permission). Needs the app foregrounded + laid out.`,inputSchema:s.object({})},async()=>{try{let n=await call(r,`Screenshot`);return!n||!n.data?e.fail(`Screenshot returned no data — the root view is not laid out yet (bring the app to the foreground and retry), or device_screenshot as a fallback.`):t(n.data)}catch(t){return e.fail(messageOf(t))}}),registerDeviceScreenshot(e)}function registerDeviceScreenshot(e){e.server.registerTool(`device_screenshot`,{description:"PNG of the WHOLE Android device/emulator screen via `adb exec-out screencap -p` (host-side; works without the in-app devtools agent). Pass `serial` to target a specific device.",inputSchema:s.object({serial:s.string().optional()})},async({serial:n})=>{try{let r=await adbScreencap(n);return r?t(r):e.fail("adb screencap returned no data — is a device connected (`adb devices`)?")}catch(t){return e.fail(`adb screencap failed: ${messageOf(t)}`)}})}function adbScreencap(e){let t=[`adb`,...e?[`-s`,e]:[],`exec-out`,`screencap`,`-p`],n=o.Subprocess.new(t,o.SubprocessFlags.STDOUT_PIPE|o.SubprocessFlags.STDERR_SILENCE);return new Promise((e,t)=>{n.communicate_async(null,null,(r,i)=>{try{let[,t]=n.communicate_finish(i),r=t?.get_data();if(!r||r.length===0)return e(null);e(a.base64_encode(r))}catch(e){t(e)}})})}function messageOf(e){return e instanceof Error?e.message:String(e)}function nativescriptProfile(e){return{name:`gjsify-nativescript-devtools`,version:`0.11.0`,connect:e}}async function runNativescriptMcp(t){let o=new i(t.connect),s=new c({name:t.name,version:t.version}),l={server:s,client:o,ok:n,fail:e};registerNsGenericTools(l),t.registerTools?.(l);let u=a.MainLoop.new(null,!1),d=new r(()=>u.quit());o.connect().then(()=>console.error(`[gjsify-devtools-mcp] ${t.name} connected to the NativeScript inspector`),e=>console.error(`[gjsify-devtools-mcp] NS inspector not reachable yet (tools will retry): ${messageOf(e)}`)),s.connect(d).then(()=>console.error(`[gjsify-devtools-mcp] ${t.name} on stdio (NativeScript CDP)`),e=>{console.error(`[gjsify-devtools-mcp] failed to connect:`,e),u.quit()}),u.run()}export{nativescriptProfile,registerNsGenericTools,runNativescriptMcp};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import e from"@girs/gio-2.0";import t from"@girs/glib-2.0";import n from"@girs/giounix-2.0";e._promisify(e.DataInputStream.prototype,`read_line_async`,`read_line_finish`);var GjsStdioTransport=class{constructor(t){this.onExit=t,this._in=null,this._out=null,this._cancellable=new e.Cancellable,this._closed=!1,this._encoder=new TextEncoder,this._decoder=new TextDecoder}async start(){if(this._in)throw Error(`GjsStdioTransport already started`);let t=n.InputStream.new(0,!1);this._in=new e.DataInputStream({base_stream:t}),this._out=n.OutputStream.new(1,!1),this._readLoop()}async _readLoop(){for(;!this._closed&&this._in;){let e;try{[e]=await this._in.read_line_async(t.PRIORITY_DEFAULT,this._cancellable)}catch(e){this._closed||this.onerror?.(e instanceof Error?e:Error(String(e)));break}if(e===null)break;let n=this._decoder.decode(e).trim();if(n)try{this.onmessage?.(JSON.parse(n))}catch(e){this.onerror?.(e instanceof Error?e:Error(String(e)))}}this._closed||(this._closed=!0,this.onclose?.()),this.onExit?.()}async send(e){if(!this._out)throw Error(`GjsStdioTransport not started`);this._out.write_all(this._encoder.encode(`${JSON.stringify(e)}\n`),null)}async close(){this._closed||(this._closed=!0,this._cancellable.cancel(),this.onclose?.(),this.onExit?.())}};export{GjsStdioTransport};
1
+ import"./_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";import t from"@girs/gio-2.0";import n from"@girs/giounix-2.0";t._promisify(t.DataInputStream.prototype,`read_line_async`,`read_line_finish`);var GjsStdioTransport=class{constructor(e){this.onExit=e,this._in=null,this._out=null,this._cancellable=new t.Cancellable,this._closed=!1,this._encoder=new TextEncoder,this._decoder=new TextDecoder}async start(){if(this._in)throw Error(`GjsStdioTransport already started`);let e=n.InputStream.new(0,!1);this._in=new t.DataInputStream({base_stream:e}),this._out=n.OutputStream.new(1,!1),this._readLoop()}async _readLoop(){for(;!this._closed&&this._in;){let t;try{[t]=await this._in.read_line_async(e.PRIORITY_DEFAULT,this._cancellable)}catch(e){this._closed||this.onerror?.(e instanceof Error?e:Error(String(e)));break}if(t===null)break;let n=this._decoder.decode(t).trim();if(n)try{this.onmessage?.(JSON.parse(n))}catch(e){this.onerror?.(e instanceof Error?e:Error(String(e)))}}this._closed||(this._closed=!0,this.onclose?.()),this.onExit?.()}async send(e){if(!this._out)throw Error(`GjsStdioTransport not started`);this._out.write_all(this._encoder.encode(`${JSON.stringify(e)}\n`),null)}async close(){this._closed||(this._closed=!0,this._cancellable.cancel(),this.onclose?.(),this.onExit?.())}};export{GjsStdioTransport};
@@ -1 +1 @@
1
- import"../_virtual/_rolldown/runtime.js";import e from"@girs/gio-2.0";import t from"@girs/glib-2.0";import n from"@girs/soup-3.0";e._promisify(n.Session.prototype,`send_and_read_async`,`send_and_read_finish`),e._promisify(n.Session.prototype,`websocket_connect_async`,`websocket_connect_finish`);var NsCdpClient=class{constructor(e={}){this._session=new n.Session,this._connection=null,this._nextId=1,this._pending=new Map,this._decoder=new TextDecoder,this._encoder=new TextEncoder,this._host=e.host??`127.0.0.1`,this._port=e.port??9222,this._explicitUrl=e.url}async connect(){if(this._connection)return;let e=this._explicitUrl??await this._discoverDebuggerUrl(),r=t.Uri.parse(e,t.UriFlags.NONE),i=new n.Message({method:`GET`,uri:r}),a=await this._session.websocket_connect_async(i,null,null,t.PRIORITY_DEFAULT,null);a.max_incoming_payload_size=100*1024*1024,a.connect(`message`,(e,t,r)=>{t===n.WebsocketDataType.TEXT&&this._onMessage(this._decoder.decode(r.toArray()))}),a.connect(`closed`,()=>this._onClosed()),this._connection=a,await this._call(`Runtime.enable`,{})}async request(e){let t=JSON.stringify(e),n=`globalThis.__adwDevtools.dispatch(${JSON.stringify(t)})`,r=await this._call(`Runtime.evaluate`,{expression:n,awaitPromise:!0,returnByValue:!0});if(r.exceptionDetails)throw Error(`CDP Runtime.evaluate threw: ${r.exceptionDetails.text??`unknown error`}`);let i=r.result?.value;if(typeof i!=`string`)throw Error(`NativeScript devtools agent returned no response — is installDevtools() called with GJSIFY_DEVTOOLS=1?`);return JSON.parse(i)}async close(){this._connection&&=(this._connection.close(n.WebsocketCloseCode.NORMAL,null),null);for(let e of this._pending.values())e.reject(Error(`CDP connection closed`));this._pending.clear()}async _discoverDebuggerUrl(){let e=`http://${this._host}:${this._port}/json`,r=new n.Message({method:`GET`,uri:t.Uri.parse(e,t.UriFlags.NONE)}),i;try{i=await this._session.send_and_read_async(r,t.PRIORITY_DEFAULT,null)}catch(t){throw Error(`Cannot reach the NativeScript inspector at ${e} — run \`ns debug android\` (or forward the port with \`adb forward tcp:${this._port} …\`). (${t instanceof Error?t.message:String(t)})`)}let a=r.get_status();if(a!==n.Status.OK)throw Error(`Inspector discovery failed: ${e} returned HTTP ${a}`);let o=this._decoder.decode(i.toArray()),s;try{s=JSON.parse(o)}catch{throw Error(`Inspector discovery returned non-JSON from ${e}`)}let c=s.find(e=>typeof e.webSocketDebuggerUrl==`string`)?.webSocketDebuggerUrl;if(!c)throw Error(`No webSocketDebuggerUrl in the inspector target list at ${e}`);return c}_call(e,r){if(!this._connection)return Promise.reject(Error(`CDP connection not open`));let i=this._nextId++,a=JSON.stringify({id:i,method:e,params:r});return new Promise((e,r)=>{this._pending.set(i,{resolve:e,reject:r}),this._connection?.send_message(n.WebsocketDataType.TEXT,new t.Bytes(this._encoder.encode(a)))})}_onMessage(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t.id!=`number`)return;let n=this._pending.get(t.id);n&&(this._pending.delete(t.id),t.error?n.reject(Error(`CDP error ${t.error.code??``}: ${t.error.message??`unknown`}`.trim())):n.resolve(t.result))}_onClosed(){this._connection=null;for(let e of this._pending.values())e.reject(Error(`CDP connection closed by the device`));this._pending.clear()}};export{NsCdpClient};
1
+ import"../_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";import t from"@girs/gio-2.0";import n from"@girs/soup-3.0";t._promisify(n.Session.prototype,`send_and_read_async`,`send_and_read_finish`),t._promisify(n.Session.prototype,`websocket_connect_async`,`websocket_connect_finish`);var NsCdpClient=class{constructor(e={}){this._session=new n.Session,this._connection=null,this._nextId=1,this._pending=new Map,this._decoder=new TextDecoder,this._encoder=new TextEncoder,this._host=e.host??`127.0.0.1`,this._port=e.port??9222,this._explicitUrl=e.url}async connect(){if(this._connection)return;let t=this._explicitUrl??await this._discoverDebuggerUrl(),r=e.Uri.parse(t,e.UriFlags.NONE),i=new n.Message({method:`GET`,uri:r}),a=await this._session.websocket_connect_async(i,null,null,e.PRIORITY_DEFAULT,null);a.max_incoming_payload_size=100*1024*1024,a.connect(`message`,(e,t,r)=>{t===n.WebsocketDataType.TEXT&&this._onMessage(this._decoder.decode(r.toArray()))}),a.connect(`closed`,()=>this._onClosed()),this._connection=a,await this._call(`Runtime.enable`,{})}async request(e){let t=JSON.stringify(e),n=`globalThis.__adwDevtools.dispatch(${JSON.stringify(t)})`,r=await this._call(`Runtime.evaluate`,{expression:n,awaitPromise:!0,returnByValue:!0});if(r.exceptionDetails)throw Error(`CDP Runtime.evaluate threw: ${r.exceptionDetails.text??`unknown error`}`);let i=r.result?.value;if(typeof i!=`string`)throw Error(`NativeScript devtools agent returned no response — is installDevtools() called with GJSIFY_DEVTOOLS=1?`);return JSON.parse(i)}async close(){this._connection&&=(this._connection.close(n.WebsocketCloseCode.NORMAL,null),null);for(let e of this._pending.values())e.reject(Error(`CDP connection closed`));this._pending.clear()}async _discoverDebuggerUrl(){let t=`http://${this._host}:${this._port}/json`,r=new n.Message({method:`GET`,uri:e.Uri.parse(t,e.UriFlags.NONE)}),i;try{i=await this._session.send_and_read_async(r,e.PRIORITY_DEFAULT,null)}catch(e){throw Error(`Cannot reach the NativeScript inspector at ${t} — run \`ns debug android\` (or forward the port with \`adb forward tcp:${this._port} …\`). (${e instanceof Error?e.message:String(e)})`)}let a=r.get_status();if(a!==n.Status.OK)throw Error(`Inspector discovery failed: ${t} returned HTTP ${a}`);let o=this._decoder.decode(i.toArray()),s;try{s=JSON.parse(o)}catch{throw Error(`Inspector discovery returned non-JSON from ${t}`)}let c=s.find(e=>typeof e.webSocketDebuggerUrl==`string`)?.webSocketDebuggerUrl;if(!c)throw Error(`No webSocketDebuggerUrl in the inspector target list at ${t}`);return c}_call(t,r){if(!this._connection)return Promise.reject(Error(`CDP connection not open`));let i=this._nextId++,a=JSON.stringify({id:i,method:t,params:r});return new Promise((t,r)=>{this._pending.set(i,{resolve:t,reject:r}),this._connection?.send_message(n.WebsocketDataType.TEXT,new e.Bytes(this._encoder.encode(a)))})}_onMessage(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t.id!=`number`)return;let n=this._pending.get(t.id);n&&(this._pending.delete(t.id),t.error?n.reject(Error(`CDP error ${t.error.code??``}: ${t.error.message??`unknown`}`.trim())):n.resolve(t.result))}_onClosed(){this._connection=null;for(let e of this._pending.values())e.reject(Error(`CDP connection closed by the device`));this._pending.clear()}};export{NsCdpClient};
@@ -0,0 +1,33 @@
1
+ import GLib from '@girs/glib-2.0';
2
+ /** Options every subcommand shares. */
3
+ export interface DevtoolsCliGlobalOptions {
4
+ /** App DBus base name. Required — there is nothing to fall back to that is not a guess. */
5
+ busName: string;
6
+ address?: string;
7
+ instance?: string;
8
+ /** Seconds to poll `GetStatus` for before giving up; 0 means "do not wait". */
9
+ timeout: number;
10
+ }
11
+ /**
12
+ * The part of {@link DbusDevtoolsClient} this runner uses.
13
+ *
14
+ * Declared structurally rather than taking the class, so a caller can drive a control plane over
15
+ * a transport of its own — and so the dispatch is testable without a bus, which is also the only
16
+ * place a "refused" reply can be produced at all.
17
+ */
18
+ export interface DevtoolsCliClient {
19
+ describeTarget(label?: string): string;
20
+ listInstances(): Promise<unknown[]>;
21
+ jsonCall(instance: string | undefined, method: string, params?: GLib.Variant | null): Promise<string>;
22
+ control(instance: string | undefined, method: string, params: GLib.Variant | null, replyType: string | null): Promise<GLib.Variant>;
23
+ }
24
+ /** Injection seam for {@link runDevtoolsCli}; unset means "dial a real app". */
25
+ export interface RunDevtoolsCliOptions {
26
+ createClient?: (busName: string, address: string | undefined, instance: string | undefined) => DevtoolsCliClient;
27
+ }
28
+ /**
29
+ * Entry point for the `gjsify devtools` client bundle: parse argv, connect, wait, dispatch.
30
+ *
31
+ * @returns the process exit code — 0 done, 1 the app said no, 2 the request itself was wrong.
32
+ */
33
+ export declare function runDevtoolsCli(argv: readonly string[], options?: RunDevtoolsCliOptions): Promise<number>;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -1,4 +1,6 @@
1
1
  export { runDevtoolsMcp } from './run-server.js';
2
+ export { runDevtoolsCli } from './cli.js';
3
+ export type { DevtoolsCliClient, DevtoolsCliGlobalOptions, RunDevtoolsCliOptions } from './cli.js';
2
4
  export { DbusDevtoolsClient } from './dbus-client.js';
3
5
  export type { DbusDevtoolsClientOptions, DevtoolsInstanceRef } from './dbus-client.js';
4
6
  export { chooseClientTransport } from './transport-choice.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/devtools-mcp",
3
- "version": "0.52.0",
3
+ "version": "0.54.0",
4
4
  "description": "MCP↔devtools bridge for GJS apps — exposes a running app's org.gjsify.Devtools control plane (DBus) to an AI agent as MCP tools",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -39,12 +39,13 @@
39
39
  "@girs/gjs": "5.4.0",
40
40
  "@girs/glib-2.0": "5.4.0",
41
41
  "@girs/soup-3.0": "5.4.0",
42
- "@gjsify/devtools-protocol": "^0.52.0",
42
+ "@gjsify/devtools": "^0.54.0",
43
+ "@gjsify/devtools-protocol": "^0.54.0",
43
44
  "@modelcontextprotocol/sdk": "^1.29.0",
44
45
  "zod": "^4.4.3"
45
46
  },
46
47
  "peerDependencies": {
47
- "@gjsify/devtools-cdp": "^0.52.0"
48
+ "@gjsify/devtools-cdp": "^0.54.0"
48
49
  },
49
50
  "peerDependenciesMeta": {
50
51
  "@gjsify/devtools-cdp": {
@@ -52,9 +53,9 @@
52
53
  }
53
54
  },
54
55
  "devDependencies": {
55
- "@gjsify/cli": "^0.52.0",
56
- "@gjsify/devtools-cdp": "^0.52.0",
57
- "@gjsify/unit": "^0.52.0",
56
+ "@gjsify/cli": "^0.54.0",
57
+ "@gjsify/devtools-cdp": "^0.54.0",
58
+ "@gjsify/unit": "^0.54.0",
58
59
  "@types/node": "^25.9.2",
59
60
  "typescript": "^6.0.3"
60
61
  },