@gjsify/devtools 0.36.0 → 0.38.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/lib/types/devtools-service.d.ts +26 -36
- package/lib/types/extension.d.ts +4 -5
- package/lib/types/install.d.ts +16 -25
- package/lib/types/peer-transport.d.ts +12 -19
- package/package.json +4 -4
|
@@ -3,24 +3,20 @@ import Gtk from '@girs/gtk-4.0';
|
|
|
3
3
|
import type { InstallDevtoolsOptions } from './extension.js';
|
|
4
4
|
import { type DevtoolsPeerServer } from './peer-transport.js';
|
|
5
5
|
/**
|
|
6
|
-
* The permanent `org.gjsify.Devtools` DBus interface for a GTK app
|
|
7
|
-
* external tooling (the MCP bridge, `gdbus`, scripts)
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* it survives window recreation.
|
|
6
|
+
* The permanent `org.gjsify.Devtools` DBus interface for a GTK app, through which
|
|
7
|
+
* external tooling (the MCP bridge, `gdbus`, scripts) inspects, screenshots and drives
|
|
8
|
+
* it. Generic methods live here; app extensions contribute pause-guarded methods merged
|
|
9
|
+
* into the same interface. The live window is resolved lazily per call, so the service
|
|
10
|
+
* survives window recreation.
|
|
12
11
|
*/
|
|
13
12
|
export declare class DevtoolsService {
|
|
14
13
|
private readonly _app;
|
|
15
14
|
private readonly _options;
|
|
16
15
|
/**
|
|
17
|
-
* One `Gio.DBusExportedObject` PER CONNECTION. The session bus is a single
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* every later client would see `UnknownMethod` for all 26 methods. Keyed by
|
|
22
|
-
* connection, so `export()` stays idempotent PER connection while serving
|
|
23
|
-
* any number of them.
|
|
16
|
+
* One `Gio.DBusExportedObject` PER CONNECTION. The session bus is a single shared
|
|
17
|
+
* pipe, but the peer transport hands out one `Gio.DBusConnection` per client with no
|
|
18
|
+
* shared object registry, so a single-slot field would export to the FIRST peer only
|
|
19
|
+
* and every later client would see `UnknownMethod` for every method.
|
|
24
20
|
*/
|
|
25
21
|
private readonly _exported;
|
|
26
22
|
private _peer;
|
|
@@ -40,9 +36,9 @@ export declare class DevtoolsService {
|
|
|
40
36
|
*/
|
|
41
37
|
unexport(connection?: Gio.DBusConnection): void;
|
|
42
38
|
/**
|
|
43
|
-
* Record the peer server hosting this service
|
|
44
|
-
*
|
|
45
|
-
*
|
|
39
|
+
* Record the peer server hosting this service and the file its address went to, so
|
|
40
|
+
* `unexport()` closes the socket AND retracts the claim, and callers can read both
|
|
41
|
+
* back instead of scraping the app's stderr.
|
|
46
42
|
*/
|
|
47
43
|
attachPeerServer(peer: DevtoolsPeerServer, addressFile?: string | null): void;
|
|
48
44
|
/** The peer address clients must dial, or `null` when this service is on a bus. */
|
|
@@ -54,29 +50,23 @@ export declare class DevtoolsService {
|
|
|
54
50
|
/**
|
|
55
51
|
* `Screenshot(scope) -> ay` — PNG bytes of the active window via the GSK renderer.
|
|
56
52
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* (the interface XML is unchanged) — `wrapJSObject` falls back to
|
|
68
|
-
* `<Name>Async` when the bare name is absent. Tracked upstream in
|
|
69
|
-
* status/upstream-patch-candidates.md; collapse back to a plain `async
|
|
70
|
-
* Screenshot` once the gjs fix ships.
|
|
53
|
+
* Uses the `<Name>Async` manual-reply convention: the method takes the raw
|
|
54
|
+
* `invocation` and calls `return_value()` itself, and `wrapJSObject` finds it because
|
|
55
|
+
* it falls back to `<Name>Async` when the bare name is absent, so the wire method
|
|
56
|
+
* stays `Screenshot`.
|
|
57
|
+
*
|
|
58
|
+
* The shape exists because gjs 1.86.0 mis-marshalled the resolved value of ANY
|
|
59
|
+
* Promise-returning exported method (`org.gnome.gjs.JSError.ValueError: Service
|
|
60
|
+
* implementation returned an incorrect value type`), while a manual reply was
|
|
61
|
+
* unaffected. That no longer reproduces on gjs 1.88.1 — a plain `async Screenshot()`
|
|
62
|
+
* returning `ay` marshals correctly — so this can collapse back into one.
|
|
71
63
|
*/
|
|
72
64
|
ScreenshotAsync(_params: unknown[], invocation: Gio.DBusMethodInvocation): Promise<void>;
|
|
73
65
|
/**
|
|
74
|
-
* Capture the active window to PNG, warming up across frames
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* contract is preserved as the genuine-failure signal (window truly never
|
|
79
|
-
* realises / is occluded).
|
|
66
|
+
* Capture the active window to PNG, warming up across frames: a just-launched or
|
|
67
|
+
* mid-layout window yields a zero-size GSK frame, so the first `captureWidgetPng` can
|
|
68
|
+
* be empty. Empty bytes remain the genuine-failure signal, for a window that never
|
|
69
|
+
* realises at all.
|
|
80
70
|
*/
|
|
81
71
|
private _captureActiveWindowPng;
|
|
82
72
|
/** `ListActions() -> s` — JSON of the `app.*` + `win.*` actions. */
|
package/lib/types/extension.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type Gio from '@girs/gio-2.0';
|
|
2
2
|
import type { MethodKind } from '@gjsify/devtools-protocol';
|
|
3
3
|
/**
|
|
4
|
-
* App-specific extension of the devtools control plane
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* carries a pause classification the guard enforces.
|
|
4
|
+
* App-specific extension of the devtools control plane: extra DBus methods (the
|
|
5
|
+
* map-editor's PaintTile, say) without forking the generic core. The `<method>` XML is
|
|
6
|
+
* merged into `org.gjsify.Devtools`, the handlers attach as concrete methods taking
|
|
7
|
+
* positional DBus args, and each carries a pause classification the guard enforces.
|
|
9
8
|
*/
|
|
10
9
|
export interface DevtoolsExtension {
|
|
11
10
|
/** `<method>…</method>` XML fragments merged into the interface. */
|
package/lib/types/install.d.ts
CHANGED
|
@@ -18,8 +18,7 @@ export type DevtoolsTransportChoice =
|
|
|
18
18
|
kind: 'unregistered';
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
-
* The transport PRECEDENCE, as one pure function so
|
|
22
|
-
* rather than merely described:
|
|
21
|
+
* The transport PRECEDENCE, as one pure function so a spec can check it:
|
|
23
22
|
*
|
|
24
23
|
* | requested address | app has bus connection | session bus answers | choice |
|
|
25
24
|
* |---|---|---|---|
|
|
@@ -28,16 +27,11 @@ export type DevtoolsTransportChoice =
|
|
|
28
27
|
* | unset | no | no | `peer`, auto-picked — this machine has no usable bus |
|
|
29
28
|
* | unset | no | yes | `unregistered` — the bus is fine, the CALL SITE is early |
|
|
30
29
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* The fourth row deliberately does NOT fall back. The bus works, so the honest
|
|
38
|
-
* diagnosis is that `installDevtools` ran before the application registered
|
|
39
|
-
* (call it from `startup`); standing a socket up there would paper over a
|
|
40
|
-
* call-site bug and hide it behind a working-looking address.
|
|
30
|
+
* Row three AUTO-PICKS rather than failing: `GJSIFY_DEVTOOLS=1` is an explicit request
|
|
31
|
+
* for a control plane, so refusing it with one stderr line is the silent absence this
|
|
32
|
+
* transport exists to remove. Row four deliberately does NOT fall back — the bus works,
|
|
33
|
+
* so the honest diagnosis is a call site that ran before registration, and a socket
|
|
34
|
+
* there would hide that behind a working-looking address.
|
|
41
35
|
*/
|
|
42
36
|
export declare function chooseDevtoolsTransport(input: {
|
|
43
37
|
requestedAddress?: string | null;
|
|
@@ -45,11 +39,9 @@ export declare function chooseDevtoolsTransport(input: {
|
|
|
45
39
|
sessionBusUsable: boolean;
|
|
46
40
|
}): DevtoolsTransportChoice;
|
|
47
41
|
/**
|
|
48
|
-
* The diagnostic for "no connection to export on"
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* hunting a bug in their own app when the machine simply has no session bus.
|
|
52
|
-
* Three distinguishable states, three answers.
|
|
42
|
+
* The diagnostic for "no connection to export on", which must NAME THE RIGHT CAUSE:
|
|
43
|
+
* blaming the call site unconditionally sent every macOS reader hunting a bug in their
|
|
44
|
+
* own app when the machine simply has no session bus. Three states, three answers.
|
|
53
45
|
*/
|
|
54
46
|
export declare function describeMissingConnection(sessionBusAddress: string | null, busAnswers: boolean): string;
|
|
55
47
|
/**
|
|
@@ -59,15 +51,14 @@ export declare function describeMissingConnection(sessionBusAddress: string | nu
|
|
|
59
51
|
* handler — the session-bus connection + object path are only available after
|
|
60
52
|
* the application has registered.
|
|
61
53
|
*
|
|
62
|
-
* Needs NO session bus (macOS, Windows): with none
|
|
63
|
-
*
|
|
64
|
-
*
|
|
54
|
+
* Needs NO session bus (macOS, Windows): with none it listens on a socket of its own
|
|
55
|
+
* and publishes the address — precedence in {@link chooseDevtoolsTransport},
|
|
56
|
+
* `options.address` / `GJSIFY_DEVTOOLS_ADDRESS` pins it.
|
|
65
57
|
*
|
|
66
|
-
* TOTAL BY CONTRACT: it never throws. Every failure
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* statements (`present()`, `onStartup()`) GJS would silently skip on a throw.
|
|
58
|
+
* TOTAL BY CONTRACT: it never throws. Every failure is reported on stderr with the
|
|
59
|
+
* address and the way out, and returns `null` like the disabled path, because callers
|
|
60
|
+
* wire it into `startup`/`activate` handlers whose remaining statements GJS would
|
|
61
|
+
* silently skip on a throw.
|
|
71
62
|
*
|
|
72
63
|
* @example
|
|
73
64
|
* ```ts
|
|
@@ -11,20 +11,16 @@ export interface DevtoolsPeerServer {
|
|
|
11
11
|
stop(): void;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* A peer transport that could not be stood up. `message` is already the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `Gio.DBusServer` failure reads `Fehler beim Binden an Adresse
|
|
18
|
-
* (GUnixSocketAddress): Die Adresse wird bereits verwendet`, which names neither
|
|
19
|
-
* the address nor a way out).
|
|
14
|
+
* A peer transport that could not be stood up. `message` is already the operator-facing
|
|
15
|
+
* sentence, which `installDevtools` logs verbatim: the bare `Gio.DBusServer` failure is
|
|
16
|
+
* LOCALISED and names neither the address nor a way out.
|
|
20
17
|
*/
|
|
21
18
|
export declare class DevtoolsPeerServerError extends Error {
|
|
22
19
|
readonly address: string;
|
|
23
20
|
readonly reason: 'address-in-use' | 'occupied-by-other-file' | 'listen-failed';
|
|
24
21
|
/**
|
|
25
|
-
* The GIO failure underneath,
|
|
26
|
-
*
|
|
27
|
-
* `lib: es2020`, which predates it.
|
|
22
|
+
* The GIO failure underneath, as a field rather than `Error(message, { cause })`:
|
|
23
|
+
* this package compiles to `lib: es2020`, which predates that option.
|
|
28
24
|
*/
|
|
29
25
|
readonly cause?: unknown;
|
|
30
26
|
constructor(message: string, address: string, reason: 'address-in-use' | 'occupied-by-other-file' | 'listen-failed', cause?: unknown);
|
|
@@ -33,17 +29,14 @@ export declare class DevtoolsPeerServerError extends Error {
|
|
|
33
29
|
* Listen on `address` (auto-picked when omitted) and export `service` at
|
|
34
30
|
* `objectPath` on every incoming peer connection.
|
|
35
31
|
*
|
|
36
|
-
* Each peer connection is its own `Gio.DBusConnection` with no shared name
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* see `UnknownMethod` for every call, the exact silent-absence failure this
|
|
41
|
-
* whole transport exists to remove.
|
|
32
|
+
* Each peer connection is its own `Gio.DBusConnection` with no shared name registry, so
|
|
33
|
+
* the interface must be exported PER CONNECTION — hence `DevtoolsService.export()`
|
|
34
|
+
* tracking exports per connection. A second client would otherwise attach to a
|
|
35
|
+
* connection with nothing on it and see `UnknownMethod` for every call.
|
|
42
36
|
*
|
|
43
|
-
* Throws a {@link DevtoolsPeerServerError} when the address cannot be listened on
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* catches it: an opt-in diagnostic must never abort an app's lifecycle.
|
|
37
|
+
* Throws a {@link DevtoolsPeerServerError} when the address cannot be listened on, with
|
|
38
|
+
* a message saying what to do about it; `installDevtools` catches it, because an opt-in
|
|
39
|
+
* diagnostic must never abort an app's lifecycle.
|
|
47
40
|
*/
|
|
48
41
|
export declare function startDevtoolsPeerServer(service: DevtoolsService, objectPath: string, address?: string): DevtoolsPeerServer;
|
|
49
42
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/devtools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "In-app DBus devtools control plane for GJS/GTK apps — inspect, screenshot, and drive a running app over org.gjsify.Devtools (gdbus or the MCP bridge)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"@girs/gobject-2.0": "^4.1.0",
|
|
44
44
|
"@girs/graphene-1.0": "^4.1.0",
|
|
45
45
|
"@girs/gtk-4.0": "^4.1.0",
|
|
46
|
-
"@gjsify/devtools-protocol": "^0.
|
|
46
|
+
"@gjsify/devtools-protocol": "^0.38.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@gjsify/cli": "^0.
|
|
50
|
-
"@gjsify/unit": "^0.
|
|
49
|
+
"@gjsify/cli": "^0.38.0",
|
|
50
|
+
"@gjsify/unit": "^0.38.0",
|
|
51
51
|
"@types/node": "^25.9.2",
|
|
52
52
|
"typescript": "^6.0.3"
|
|
53
53
|
},
|