@gjsify/devtools-mcp 0.63.1-next.235 → 0.63.1-next.237
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 +1 -0
- package/lib/esm/cli.js +2 -2
- package/lib/esm/generic-tools.js +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ client with an argv front end — `shot`, `find`, `activate`, `key`, `tree`, `pr
|
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
65
|
gjsify devtools shot /tmp/app.png --bus-name org.example.App # file + the size the PNG carries
|
|
66
|
+
gjsify devtools shot /tmp/app@2x.png --scale 2 --bus-name org.example.App # 2× device pixels
|
|
66
67
|
gjsify devtools find Adw.StatusPage:error --bus-name org.example.App # stdout = the widget path
|
|
67
68
|
gjsify devtools help # every operation and flag
|
|
68
69
|
```
|
package/lib/esm/cli.js
CHANGED
|
@@ -1,3 +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(),
|
|
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`,` --scale <n> shot: device pixels per logical pixel (default: the window's own factor)`,` --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 flagNumber(e,t){let n=e.get(t);if(n===void 0)return;let r=Number(n);return Number.isFinite(r)?r:void 0}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=o[0]??`screenshot.png`,a=s.get(`scope`)??`window`,c=flagNumber(s,`scale`),l=await n(async n=>(c===void 0?await e.control(r,`Screenshot`,t.Variant.new_tuple([strv(n)]),`(ay)`):await e.control(r,`ScreenshotScaled`,t.Variant.new_tuple([strv(n),t.Variant.new_double(c)]),`(ay)`)).get_child_value(0).deepUnpack(),i,a,{settleMs:flagInt(s,`settle`,0),retries:flagInt(s,`retries`,8)});return console.log(`wrote ${l.path} (${l.byteLength} bytes, ${l.width}×${l.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
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};
|
package/lib/esm/generic-tools.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{image as e}from"./tool-result.js";import t from"@girs/glib-2.0";import{z as n}from"zod";const strv=e=>t.Variant.new_string(e),r={instance:n.string().optional().describe(`App instance label; omit for the default app`)};function delay(e){return new Promise(n=>{t.timeout_add(t.PRIORITY_DEFAULT,e,()=>(n(),t.SOURCE_REMOVE))})}function registerGenericTools(i,a=`all`){let{server:o,client:s,ok:c,fail:l,dbusError:u}=i,want=e=>a===`all`||a.includes(e);want(`get_status`)&&o.registerTool(`get_status`,{description:`JSON snapshot of the running app: app id, active window, toplevel count, focused widget, pause state, plus any app-specific status contributed by extensions.`,inputSchema:n.object({...r})},async({instance:e})=>{try{return c(await s.jsonCall(e,`GetStatus`))}catch(t){return u(t,e)}}),want(`screenshot`)&&o.registerTool(`screenshot`,{description:"Capture a PNG screenshot via the GTK GSK pipeline. `scope` chooses WHAT is captured: the active window by default, or a single widget inside it by path. Needs a visible window (auto-raises + retries once when the capture comes back empty).",inputSchema:n.object({scope:n.string().optional().describe('What to capture: omitted, "window" or "active" for the active window, or a `toplevel:N/child:M` path from dump_tree for one widget. An explicit path matching no live widget is an error, not a silent whole-window shot.'),...r})},async({scope:n,instance:
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{image as e}from"./tool-result.js";import t from"@girs/glib-2.0";import{z as n}from"zod";const strv=e=>t.Variant.new_string(e),r={instance:n.string().optional().describe(`App instance label; omit for the default app`)};function delay(e){return new Promise(n=>{t.timeout_add(t.PRIORITY_DEFAULT,e,()=>(n(),t.SOURCE_REMOVE))})}function registerGenericTools(i,a=`all`){let{server:o,client:s,ok:c,fail:l,dbusError:u}=i,want=e=>a===`all`||a.includes(e);want(`get_status`)&&o.registerTool(`get_status`,{description:`JSON snapshot of the running app: app id, active window, toplevel count, focused widget, pause state, plus any app-specific status contributed by extensions.`,inputSchema:n.object({...r})},async({instance:e})=>{try{return c(await s.jsonCall(e,`GetStatus`))}catch(t){return u(t,e)}}),want(`screenshot`)&&o.registerTool(`screenshot`,{description:"Capture a PNG screenshot via the GTK GSK pipeline. `scope` chooses WHAT is captured: the active window by default, or a single widget inside it by path. `scale` chooses at what resolution. Needs a visible window (auto-raises + retries once when the capture comes back empty).",inputSchema:n.object({scope:n.string().optional().describe('What to capture: omitted, "window" or "active" for the active window, or a `toplevel:N/child:M` path from dump_tree for one widget. An explicit path matching no live widget is an error, not a silent whole-window shot.'),scale:n.number().optional().describe(`Device pixels per logical pixel. Omitted: the window's own scale factor, so a HiDPI window captures at the resolution it is drawn at. 2 re-renders text and symbolic icons at twice the resolution — it does not magnify. Needs an app on @gjsify/devtools >= 0.64.`),...r})},async({scope:n,scale:r,instance:i})=>{try{let grab=async()=>(r===void 0?await s.control(i,`Screenshot`,t.Variant.new_tuple([strv(n??`window`)]),`(ay)`):await s.control(i,`ScreenshotScaled`,t.Variant.new_tuple([strv(n??`window`),t.Variant.new_double(r)]),`(ay)`)).get_child_value(0).deepUnpack(),a=await grab();return(!a||a.length===0)&&(await s.control(i,`PresentWindow`,null,null),await delay(350),a=await grab()),!a||a.length===0?l(`Screenshot returned no data — the window is likely occluded or minimized. Bring it to the foreground (present_window) and retry.`):e(t.base64_encode(a))}catch(e){return u(e,i)}}),want(`list_actions`)&&o.registerTool(`list_actions`,{description:`List the app.* and win.* GActions (name, enabled, parameter/state types) that activate_action / change_action_state can drive.`,inputSchema:n.object({...r})},async({instance:e})=>{try{return c(await s.jsonCall(e,`ListActions`))}catch(t){return u(t,e)}}),want(`activate_action`)&&o.registerTool(`activate_action`,{description:`Activate a GAction by scope + name, with an optional parameter (a plain JSON value coerced to the action's declared type).`,inputSchema:n.object({scope:n.enum([`app`,`win`]),name:n.string(),value:n.union([n.string(),n.number(),n.boolean()]).optional(),...r})},async({scope:e,name:n,value:r,instance:i})=>{try{let a=r===void 0?``:JSON.stringify(r);return await s.control(i,`ActivateAction`,t.Variant.new_tuple([strv(e),strv(n),strv(a)]),null),c(`Activated ${e}.${n}`)}catch(e){return u(e,i)}}),want(`change_action_state`)&&o.registerTool(`change_action_state`,{description:`Set a stateful GAction’s state — for idempotent toggles like win.toggle-grid.`,inputSchema:n.object({scope:n.enum([`app`,`win`]),name:n.string(),value:n.union([n.string(),n.number(),n.boolean()]),...r})},async({scope:e,name:n,value:r,instance:i})=>{try{return await s.control(i,`ChangeActionState`,t.Variant.new_tuple([strv(e),strv(n),strv(JSON.stringify(r))]),null),c(`Set state ${e}.${n} = ${JSON.stringify(r)}`)}catch(e){return u(e,i)}}),want(`present_window`)&&o.registerTool(`present_window`,{description:`Raise + focus the app’s active window.`,inputSchema:n.object({...r})},async({instance:e})=>{try{return await s.control(e,`PresentWindow`,null,null),c(`Presented the window.`)}catch(t){return u(t,e)}}),want(`resize_window`)&&o.registerTool(`resize_window`,{description:`Resize the active window to an absolute pixel size (exercises responsive breakpoints). Returns the requested size.`,inputSchema:n.object({width:n.number(),height:n.number(),...r})},async({width:e,height:n,instance:r})=>{try{let[i,a]=(await s.control(r,`ResizeWindow`,t.Variant.new_tuple([t.Variant.new_int32(e),t.Variant.new_int32(n)]),`(ii)`)).recursiveUnpack();return c(`Resized to ${i}×${a}.`)}catch(e){return u(e,r)}}),want(`list_toplevels`)&&o.registerTool(`list_toplevels`,{description:`List the app’s live toplevel windows (path, type, title, mapped, focused).`,inputSchema:n.object({...r})},async({instance:e})=>{try{return c(await s.jsonCall(e,`ListToplevels`))}catch(t){return u(t,e)}}),want(`dump_tree`)&&o.registerTool(`dump_tree`,{description:'Dump the widget tree as JSON from `root` (a widget path like "toplevel:0/child:2", or omit for the active window), bounded by `depth`.',inputSchema:n.object({root:n.string().optional(),depth:n.number().optional(),...r})},async({root:e,depth:n,instance:r})=>{try{let i=t.Variant.new_tuple([strv(e??``),t.Variant.new_int32(n??8)]);return c(await s.jsonCall(r,`DumpTree`,i))}catch(e){return u(e,r)}}),want(`get_property`)&&o.registerTool(`get_property`,{description:`Read a widget’s GObject property by widget path + property name (JSON-safe value).`,inputSchema:n.object({path:n.string(),prop:n.string(),...r})},async({path:e,prop:n,instance:r})=>{try{let i=t.Variant.new_tuple([strv(e),strv(n)]);return c(await s.jsonCall(r,`GetProperty`,i))}catch(e){return u(e,r)}}),want(`get_focused`)&&o.registerTool(`get_focused`,{description:`The currently-focused widget (path, name, type), or null.`,inputSchema:n.object({...r})},async({instance:e})=>{try{return c(await s.jsonCall(e,`GetFocused`))}catch(t){return u(t,e)}}),want(`find_widget`)&&o.registerTool(`find_widget`,{description:"Find the first widget matching a selector and return its widget path, searched depth-first from the active window. Selector is a GType name, a `:css-class`, or both (`GtkButton:suggested-action`). A GType name matches SUBCLASSES too, so `AdwDialog` reaches the application's own Adw.Dialog subclass, and an interface name matches every implementor (`GtkAccessible` matches almost anything); an exact match wins over both. Invisible and unmapped subtrees are skipped, so a hit is something a user can actually reach. Saves dumping the whole tree to hunt for a path — and unlike a path written down earlier, a selector survives a widget being inserted above the target. Returns an empty result when nothing matches.",inputSchema:n.object({selector:n.string(),...r})},async({selector:e,instance:n})=>{try{let[r]=(await s.control(n,`FindWidget`,t.Variant.new_tuple([strv(e)]),`(s)`)).recursiveUnpack();return c(r||`No visible widget matches "${e}".`)}catch(e){return u(e,n)}}),want(`activate_widget`)&&o.registerTool(`activate_widget`,{description:`Activate the widget at a widget path (from dump_tree) — the click-drive counterpart of get_property: a Button emits clicked, an Entry activates, a Toggle toggles; a list/nav/preference row (GtkListBoxRow/AdwActionRow) is clicked on its GtkListBox (selected + row-activated). Returns whether the widget activated.`,inputSchema:n.object({path:n.string(),...r})},async({path:e,instance:n})=>{try{let[r]=(await s.control(n,`ActivateWidget`,t.Variant.new_tuple([strv(e)]),`(b)`)).recursiveUnpack();return c(r?`Activated widget at "${e}".`:`Widget at "${e}" is not activatable (no default activation).`)}catch(e){return u(e,n)}}),want(`send_key`)&&o.registerTool(`send_key`,{description:`Deliver a key to a widget's key controllers — the half of headless GUI driving that activate_widget does not cover. Accelerator syntax is GTK's own: "Delete", "Left", "<primary>s", "<shift>Up". Omit path to target whatever has the keyboard focus. Proves the HANDLER runs and what it changes, not that GDK would route a real key press there — for that, also read get_property(path, "focusable"), since an unfocusable widget swallows every key silently.`,inputSchema:n.object({accelerator:n.string(),path:n.string().optional(),...r})},async({accelerator:e,path:n,instance:r})=>{try{let[i]=(await s.control(r,`SendKey`,t.Variant.new_tuple([strv(e),strv(n??``)]),`(b)`)).recursiveUnpack();return c(i?`Sent "${e}" to ${n||`the focused widget`}.`:`${n||`The focused widget`} has no key controller — "${e}" went nowhere.`)}catch(e){return u(e,r)}}),want(`dump_gsettings`)&&o.registerTool(`dump_gsettings`,{description:`Dump every key + value of an installed GSettings schema (read-only).`,inputSchema:n.object({schema:n.string(),...r})},async({schema:e,instance:n})=>{try{return c(await s.jsonCall(n,`DumpGSettings`,t.Variant.new_tuple([strv(e)])))}catch(e){return u(e,n)}}),want(`dump_css`)&&o.registerTool(`dump_css`,{description:`List the devtools-installed CSS provider names.`,inputSchema:n.object({...r})},async({instance:e})=>{try{return c(await s.jsonCall(e,`DumpCss`))}catch(t){return u(t,e)}}),want(`swap_css`)&&o.registerTool(`swap_css`,{description:`Live-install or replace a named CSS provider (high priority) — re-theme the running app without a restart. Returns applied.`,inputSchema:n.object({name:n.string(),css:n.string(),...r})},async({name:e,css:n,instance:r})=>{try{let[i]=(await s.control(r,`SwapCss`,t.Variant.new_tuple([strv(e),strv(n)]),`(b)`)).recursiveUnpack();return c(i?`Applied CSS provider "${e}".`:`No display — CSS not applied.`)}catch(e){return u(e,r)}}),want(`list_instances`)&&o.registerTool(`list_instances`,{description:`List reachable devtools-enabled app instances — every default/labelled bus name on the session bus, or (on the bus-less peer transport) the single app this bridge is dialling.`,inputSchema:n.object({})},async()=>{try{return c(JSON.stringify(await s.listInstances(),null,2))}catch(e){return u(e)}})}export{registerGenericTools};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/devtools-mcp",
|
|
3
|
-
"version": "0.63.1-next.
|
|
3
|
+
"version": "0.63.1-next.237",
|
|
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,13 +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": "0.63.1-next.
|
|
43
|
-
"@gjsify/devtools-protocol": "0.63.1-next.
|
|
42
|
+
"@gjsify/devtools": "0.63.1-next.237",
|
|
43
|
+
"@gjsify/devtools-protocol": "0.63.1-next.237",
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
45
45
|
"zod": "^4.4.3"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@gjsify/devtools-cdp": "0.63.1-next.
|
|
48
|
+
"@gjsify/devtools-cdp": "0.63.1-next.237"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"@gjsify/devtools-cdp": {
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@gjsify/cli": "0.63.1-next.
|
|
57
|
-
"@gjsify/devtools-cdp": "0.63.1-next.
|
|
58
|
-
"@gjsify/unit": "0.63.1-next.
|
|
56
|
+
"@gjsify/cli": "0.63.1-next.237",
|
|
57
|
+
"@gjsify/devtools-cdp": "0.63.1-next.237",
|
|
58
|
+
"@gjsify/unit": "0.63.1-next.237",
|
|
59
59
|
"@types/node": "^25.9.2",
|
|
60
60
|
"typescript": "^6.0.3"
|
|
61
61
|
},
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"url": "https://github.com/gjsify/gjsify/issues"
|
|
79
79
|
},
|
|
80
80
|
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/framework/devtools-mcp#readme",
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "a349db400a7e68befc11ba6381ddb0e4caff09b6"
|
|
82
82
|
}
|