arcane-os 0.82.0 → 0.83.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/CHANGELOG.md +14 -0
- package/README.md +6 -1
- package/browser-runtime/core/app-control.mjs +70 -21
- package/docs/reference/cli.md +3 -0
- package/docs/reference/inventory/package-api.json +2 -2
- package/docs/reference/native-app-control.md +68 -9
- package/docs/reference/sdk-api.md +5 -2
- package/package.json +1 -1
- package/src/cli/app-control.mjs +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.83.0
|
|
4
|
+
|
|
5
|
+
- Add optional `shadowPath` to native app-control inspection and actions. Follow
|
|
6
|
+
an explicit ordered path through open shadow hosts, then inspect complete
|
|
7
|
+
content or operate a selector within that root. Nested host records expose
|
|
8
|
+
their full paths; omitted or empty paths preserve existing light-DOM calls.
|
|
9
|
+
- Accept a complete inspection JSON object through `arcane app-control inspect
|
|
10
|
+
--request`, including shadow paths and document generation. Existing selector,
|
|
11
|
+
action, capture and connection lifecycle behavior remains unchanged.
|
|
12
|
+
- Publish the matching Windows helper with the native host archive. Closed
|
|
13
|
+
shadow roots and frame documents remain outside this capability. Preserve
|
|
14
|
+
Core, profiles, full payloads and existing dependencies. Package verification
|
|
15
|
+
is separate from actual built-application acceptance.
|
|
16
|
+
|
|
3
17
|
## 0.82.0
|
|
4
18
|
|
|
5
19
|
- Add `connectAppControl` from `arcane-os/core/app-control` and the
|
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# Arcane OS SDK
|
|
4
4
|
|
|
5
|
+
[](https://github.com/TheWizardNexus/arcane-os-sdk/actions/workflows/check.yml?query=branch%3Amain)
|
|
6
|
+
[](https://www.npmjs.com/package/arcane-os)
|
|
7
|
+
[](https://www.npmjs.com/package/arcane-os)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
5
10
|
<p align="center">
|
|
6
11
|
<strong>Build, test, package, and manage Arcane applications inside or outside Arcane OS.</strong><br>
|
|
7
12
|
Keep proprietary source in its own repository while using the same headless workflow for apps and shared runtime work in the Arcane checkout.
|
|
@@ -20,7 +25,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
|
|
|
20
25
|
share the theme, packaging, event, cancellation,
|
|
21
26
|
and browser run contracts while retaining their selected application layout.
|
|
22
27
|
|
|
23
|
-
This checkout defines the `0.
|
|
28
|
+
This checkout defines the `0.83.0` SDK contract. Development applications declare
|
|
24
29
|
`arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
|
|
25
30
|
designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
|
|
26
31
|
to refresh and compare the installed SDK and lockfile with the published channel;
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* Runs inside the existing app document. The native host serializes this named
|
|
3
3
|
* function with toString(), so all helpers must remain inside its lexical scope.
|
|
4
4
|
*
|
|
5
|
-
* inspect({selector?}) reads the top-level light DOM
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* inspect({selector?,shadowPath?}) reads the top-level light DOM or the open
|
|
6
|
+
* shadow root selected by an ordered array of unique host selectors. Omission
|
|
7
|
+
* of selector selects that whole root; a CSS selector may select any number of
|
|
8
|
+
* elements within it. HTML, textContent, attributes and live control state are
|
|
9
|
+
* returned completely. Frame documents and unselected shadow-root contents stay
|
|
10
|
+
* outside this scope. Closed shadow roots cannot be discovered or entered.
|
|
10
11
|
* domRole/domName are DOM-derived hints, not an accessibility-tree computation.
|
|
11
12
|
*
|
|
12
|
-
* act({action,selector
|
|
13
|
+
* act({action,selector,shadowPath?,...}) requires one current CSS match in the
|
|
14
|
+
* selected root. fill uses an exact
|
|
13
15
|
* string value; select uses either value:string or values:string[]. scroll uses
|
|
14
16
|
* absolute left/top CSS-pixel offsets, preserving any omitted axis. Actions use
|
|
15
17
|
* DOM APIs and untrusted events without focus or desktop input. Results describe
|
|
@@ -57,19 +59,56 @@ export function runAppControl(operation, params = {}) {
|
|
|
57
59
|
throw error;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
function queryElements(selector) {
|
|
62
|
+
function queryElements(selector, root = document) {
|
|
61
63
|
if (typeof selector !== 'string') {
|
|
62
64
|
fail('INVALID_ARGUMENT', 'selector must be a CSS selector string.');
|
|
63
65
|
}
|
|
64
|
-
return Array.from(
|
|
66
|
+
return Array.from(root.querySelectorAll(selector));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function selectedRoot() {
|
|
70
|
+
const shadowPath = params.shadowPath === undefined ? [] : params.shadowPath;
|
|
71
|
+
if (!Array.isArray(shadowPath)) {
|
|
72
|
+
fail('INVALID_ARGUMENT', 'shadowPath must be an array of CSS host selectors.');
|
|
73
|
+
}
|
|
74
|
+
let root = document;
|
|
75
|
+
for (let index = 0; index < shadowPath.length; index += 1) {
|
|
76
|
+
const selector = shadowPath[index];
|
|
77
|
+
let hosts;
|
|
78
|
+
try {
|
|
79
|
+
hosts = queryElements(selector, root);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
error.details = {shadowPath, index, selector};
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
if (hosts.length !== 1) {
|
|
85
|
+
fail(
|
|
86
|
+
'ARCANE_APP_CONTROL_TARGET_COUNT',
|
|
87
|
+
'Each shadowPath selector requires exactly one matching host.',
|
|
88
|
+
{shadowPath, index, selector, matches: hosts.length}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const shadowRoot = hosts[0].shadowRoot;
|
|
92
|
+
if (!shadowRoot) {
|
|
93
|
+
fail(
|
|
94
|
+
'ARCANE_APP_CONTROL_UNSUPPORTED',
|
|
95
|
+
'The selected host has no accessible open shadow root. Closed and absent roots cannot be distinguished.',
|
|
96
|
+
{shadowPath, index, selector}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
root = shadowRoot;
|
|
100
|
+
}
|
|
101
|
+
return {root, shadowPath};
|
|
65
102
|
}
|
|
66
103
|
|
|
67
104
|
function inspectDocument() {
|
|
105
|
+
const {root, shadowPath} = selectedRoot();
|
|
106
|
+
const isDocument = root === document;
|
|
68
107
|
const roots = params.selector === undefined
|
|
69
|
-
? (document.documentElement ? [document.documentElement] : [])
|
|
70
|
-
: queryElements(params.selector);
|
|
108
|
+
? (isDocument ? (document.documentElement ? [document.documentElement] : []) : [root])
|
|
109
|
+
: queryElements(params.selector, root);
|
|
71
110
|
const rootSet = new Set(roots);
|
|
72
|
-
const elements = Array.from(
|
|
111
|
+
const elements = Array.from(root.querySelectorAll('*'));
|
|
73
112
|
const idCounts = new Map();
|
|
74
113
|
const childCounts = new Map();
|
|
75
114
|
const childIndexes = new Map();
|
|
@@ -88,7 +127,7 @@ export function runAppControl(operation, params = {}) {
|
|
|
88
127
|
const index = (childCounts.get(parent) ?? 0) + 1;
|
|
89
128
|
childCounts.set(parent, index);
|
|
90
129
|
childIndexes.set(element, index);
|
|
91
|
-
if (rootSet.has(element) || scope.has(parent)) {
|
|
130
|
+
if (rootSet.has(element) || rootSet.has(element.parentNode) || scope.has(parent)) {
|
|
92
131
|
scope.add(element);
|
|
93
132
|
}
|
|
94
133
|
}
|
|
@@ -105,14 +144,16 @@ export function runAppControl(operation, params = {}) {
|
|
|
105
144
|
shadowHosts.push(
|
|
106
145
|
{
|
|
107
146
|
selector: uniqueSelector(element),
|
|
108
|
-
mode: element.shadowRoot.mode
|
|
147
|
+
mode: element.shadowRoot.mode,
|
|
148
|
+
shadowPath: [...shadowPath, uniqueSelector(element)]
|
|
109
149
|
}
|
|
110
150
|
);
|
|
111
151
|
}
|
|
112
152
|
}
|
|
113
153
|
|
|
114
154
|
return {
|
|
115
|
-
scope: 'top-level-light-dom',
|
|
155
|
+
scope: isDocument ? 'top-level-light-dom' : 'open-shadow-root',
|
|
156
|
+
...(isDocument ? {} : {shadowPath}),
|
|
116
157
|
title: document.title,
|
|
117
158
|
readyState: document.readyState,
|
|
118
159
|
doctype: document.doctype
|
|
@@ -122,16 +163,19 @@ export function runAppControl(operation, params = {}) {
|
|
|
122
163
|
controls,
|
|
123
164
|
frames,
|
|
124
165
|
shadowHosts,
|
|
125
|
-
activeElement:
|
|
126
|
-
? describeElement(
|
|
166
|
+
activeElement: root.activeElement
|
|
167
|
+
? describeElement(root.activeElement, uniqueSelector(root.activeElement))
|
|
127
168
|
: null,
|
|
128
|
-
scrollingElement: document.scrollingElement
|
|
169
|
+
scrollingElement: isDocument && document.scrollingElement
|
|
129
170
|
? uniqueSelector(document.scrollingElement)
|
|
130
171
|
: null,
|
|
131
172
|
selection: {text: document.getSelection()?.toString() ?? ''}
|
|
132
173
|
};
|
|
133
174
|
|
|
134
175
|
function describeRoot(element) {
|
|
176
|
+
if (element === root && !isDocument) {
|
|
177
|
+
return {selector: null, html: root.innerHTML, text: root.textContent, state: null};
|
|
178
|
+
}
|
|
135
179
|
return {
|
|
136
180
|
selector: uniqueSelector(element),
|
|
137
181
|
html: element.outerHTML,
|
|
@@ -141,7 +185,7 @@ export function runAppControl(operation, params = {}) {
|
|
|
141
185
|
}
|
|
142
186
|
|
|
143
187
|
function uniqueSelector(element) {
|
|
144
|
-
if (!element.isConnected || element.getRootNode() !==
|
|
188
|
+
if (!element.isConnected || element.getRootNode() !== root) return null;
|
|
145
189
|
const path = [];
|
|
146
190
|
let current = element;
|
|
147
191
|
let prefix = '';
|
|
@@ -161,7 +205,10 @@ export function runAppControl(operation, params = {}) {
|
|
|
161
205
|
for (let index = path.length - 1; index >= 0; index -= 1) {
|
|
162
206
|
const part = path[index];
|
|
163
207
|
const segment = `*:nth-child(${childIndexes.get(part)})`;
|
|
164
|
-
|
|
208
|
+
// A ShadowRoot is not an element matched by :root or :scope.
|
|
209
|
+
// The parent exclusion anchors its top-level child in this tree.
|
|
210
|
+
prefix = prefix ? `${prefix} > ${segment}`
|
|
211
|
+
: isDocument ? ':root' : `${segment}:not(* > *)`;
|
|
165
212
|
selectors.set(part, prefix);
|
|
166
213
|
}
|
|
167
214
|
return prefix;
|
|
@@ -190,7 +237,7 @@ export function runAppControl(operation, params = {}) {
|
|
|
190
237
|
if (labelledBy) {
|
|
191
238
|
const labels = [];
|
|
192
239
|
for (const id of labelledBy.split(/\s+/u)) {
|
|
193
|
-
const label =
|
|
240
|
+
const label = element.getRootNode().getElementById?.(id);
|
|
194
241
|
if (label) labels.push(label.textContent);
|
|
195
242
|
}
|
|
196
243
|
if (labels.length) return {source: 'aria-labelledby', value: labels.join(' ')};
|
|
@@ -274,7 +321,8 @@ export function runAppControl(operation, params = {}) {
|
|
|
274
321
|
}
|
|
275
322
|
|
|
276
323
|
function actOnElement() {
|
|
277
|
-
const
|
|
324
|
+
const {root, shadowPath} = selectedRoot();
|
|
325
|
+
const targets = queryElements(params.selector, root);
|
|
278
326
|
if (targets.length !== 1) {
|
|
279
327
|
fail(
|
|
280
328
|
'ARCANE_APP_CONTROL_TARGET_COUNT',
|
|
@@ -328,6 +376,7 @@ export function runAppControl(operation, params = {}) {
|
|
|
328
376
|
action,
|
|
329
377
|
method,
|
|
330
378
|
invoked: true,
|
|
379
|
+
...(root === document ? {} : {shadowPath}),
|
|
331
380
|
eventsDispatched,
|
|
332
381
|
target: describeElement(target, params.selector)
|
|
333
382
|
};
|
package/docs/reference/cli.md
CHANGED
|
@@ -15,6 +15,9 @@ acknowledgement to stderr, then complete result JSON to stdout, or complete erro
|
|
|
15
15
|
JSON to stderr and a nonzero exit. Capture writes its image to the explicit
|
|
16
16
|
`--output` filename and returns metadata. Its options are scoped to that command;
|
|
17
17
|
the general output-format parser below applies to the other commands.
|
|
18
|
+
Inspection accepts either `--selector` or `--request <json-file>`; the latter
|
|
19
|
+
reads the complete inspection object, including an explicit `shadowPath` through
|
|
20
|
+
open shadow roots. Actions continue to use `--request` for their complete object.
|
|
18
21
|
|
|
19
22
|
## Command inventory
|
|
20
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sdkVersion": "0.
|
|
3
|
+
"sdkVersion": "0.82.0",
|
|
4
4
|
"environment": {
|
|
5
5
|
"runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"summary": "Connects to one explicit running native window for background document inspection, viewport capture and targeted DOM actions.",
|
|
25
25
|
"availability": "Node local IPC on Windows, Linux and macOS; the current window adapter is Windows WebView2, with other native adapters unavailable",
|
|
26
26
|
"protocol": "arcane/1 app.control.status, app.control.inspect, app.control.capture and app.control.act over the existing framed local connection. The window host selects native.launchContext.appControl.endpoint and preserves its existing Core child, origin, profile and lifetime.",
|
|
27
|
-
"normalization": "Returns endpoint, closed, status(options), inspect(parameters={},options), capture(parameters={},options), act(parameters,options), and close(). Parameters pass unchanged; per-operation options preserve Core signal/timeout behavior. Status reports actual app/window/document readiness. Inspect returns complete selected
|
|
27
|
+
"normalization": "Returns endpoint, closed, status(options), inspect(parameters={},options), capture(parameters={},options), act(parameters,options), and close(). Parameters pass unchanged; per-operation options preserve Core signal/timeout behavior. Status reports actual app/window/document readiness. Inspect returns complete selected DOM HTML/text and live control state with DOM-derived role/name hints. Optional shadowPath on inspect and act is an ordered array of unique CSS host selectors through open shadow roots; selector resolves in the final root, with omitted/empty paths retaining top-level light DOM. Whole-shadow-root inspection returns complete innerHTML/textContent with null selector/state; nested shadow-host descriptors provide their full path. Frames remain descriptors and closed roots are unavailable. Capture returns mimeType image/png, complete base64 data, documentGeneration and URL for the rendered viewport. Act requires documentGeneration and one CSS selector with click, exact-string fill, exact-value select or absolute-offset scroll. The native document marker prevents queued actions crossing replacement; cancellation before dispatch prevents execution while dispatched work is observed through completion without retry or reversal. close() cancels only this connection, never the application or Core. No launch, automatic reconnect, desktop activation, global input or public arbitrary evaluator. CLI inspection accepts either --selector or a complete --request JSON object. See native-app-control.md for complete result fields, CLI and platform scope."
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"id": "local-ai-execution-devices:createExecutionDeviceCatalog",
|
|
@@ -74,9 +74,9 @@ connection or other business result.
|
|
|
74
74
|
| --- | --- |
|
|
75
75
|
| `connectAppControl({endpoint, signal?, onError?})` | Connect once to the explicit running host. It never launches an app, creates Core, changes a profile or reconnects automatically. |
|
|
76
76
|
| `status(options?)` | Returns app identity, platform/host, current URL, document generation, readiness, window title/state/client dimensions and available operations. Readiness describes the document, not models or application services. |
|
|
77
|
-
| `inspect(parameters={}, options?)` | Optional CSS `selector` selects zero or more
|
|
77
|
+
| `inspect(parameters={}, options?)` | Optional `shadowPath` selects an open shadow root. Optional CSS `selector` selects zero or more elements within the selected root; omission selects the whole root. Optional `documentGeneration` selects the expected document. Returns complete HTML/text, live control state and DOM-derived role/name hints. |
|
|
78
78
|
| `capture(parameters={}, options?)` | Returns `{mimeType:'image/png', data, documentGeneration, url}`. `data` contains the complete base64 PNG. Optional `documentGeneration` selects the expected document. |
|
|
79
|
-
| `act(parameters, options?)` | Requires `documentGeneration`, one unambiguous CSS `selector`, and the action fields below. |
|
|
79
|
+
| `act(parameters, options?)` | Requires `documentGeneration`, one unambiguous CSS `selector` within the root selected by optional `shadowPath`, and the action fields below. |
|
|
80
80
|
| `close()` | Closes this caller's connection and cancels its pending requests. It never closes the app or Core. Returns the connection's completion promise. |
|
|
81
81
|
| `closed` | Connection lifetime promise; actual transport failures remain observable. |
|
|
82
82
|
| `endpoint` | The caller-selected local endpoint. |
|
|
@@ -90,19 +90,73 @@ is retained in the host diagnostics instead of being represented as delivered.
|
|
|
90
90
|
|
|
91
91
|
## Document operations
|
|
92
92
|
|
|
93
|
-
Inspection
|
|
93
|
+
Inspection defaults to the top-level light DOM. Its result contains `scope`, `title`,
|
|
94
94
|
`readyState`, `doctype`, `roots`, `controls`, `frames`, `shadowHosts`,
|
|
95
95
|
`activeElement`, `scrollingElement`, `selection`, `documentGeneration`, and
|
|
96
96
|
`url`. Each root retains its complete `html` and `text`; controls retain their
|
|
97
97
|
attributes and current form, selection, disabled, scrolling and related state.
|
|
98
98
|
Generated CSS selectors are useful for the inspected document; a later DOM edit
|
|
99
99
|
can change their meaning. Application-authored stable selectors are preferable
|
|
100
|
-
when available.
|
|
100
|
+
when available. Component replacement or mutation can make a selector stale
|
|
101
|
+
without changing the native document generation.
|
|
102
|
+
|
|
103
|
+
### Open shadow roots
|
|
104
|
+
|
|
105
|
+
`inspect` and `act` accept `shadowPath`, an ordered array of CSS host selectors.
|
|
106
|
+
The first selector resolves inside the top-level document; each later selector
|
|
107
|
+
resolves inside the preceding host's open shadow root. Every host selector must
|
|
108
|
+
match exactly one element. The operation's `selector` then resolves only inside
|
|
109
|
+
the final root. An omitted or empty `shadowPath` retains the ordinary light-DOM
|
|
110
|
+
scope. Selecting a root never recursively enters its nested shadow roots.
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const shadowPath = ['#appearance-panel', 'theme-switcher'];
|
|
114
|
+
const panel = await app.inspect({shadowPath});
|
|
115
|
+
await app.act({
|
|
116
|
+
documentGeneration: panel.documentGeneration,
|
|
117
|
+
shadowPath,
|
|
118
|
+
selector: 'button[data-theme="dark"]',
|
|
119
|
+
action: 'click'
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Shadow inspection returns `scope:'open-shadow-root'` and the selected
|
|
124
|
+
`shadowPath`. Its element selectors are local to that root. With no `selector`,
|
|
125
|
+
`roots` contains one complete root record with `selector:null`, its full
|
|
126
|
+
`innerHTML` as `html`, its full `textContent` as `text`, and `state:null`; a
|
|
127
|
+
ShadowRoot itself is not an element or an action target. With a selector, roots
|
|
128
|
+
retain their element HTML and state. Controls, frames and open-shadow hosts are
|
|
129
|
+
described within this selected tree. Each `shadowHosts` descriptor retains its
|
|
130
|
+
local `selector` and `mode`, and adds the full `shadowPath` needed to inspect
|
|
131
|
+
that host's shadow root, including nested hosts. Pass that path to the next
|
|
132
|
+
inspection; pair selectors from its result with that same path for actions.
|
|
133
|
+
|
|
134
|
+
`activeElement` comes from the selected root. `scrollingElement` is `null` for a
|
|
135
|
+
shadow-root scope because the root has no document scrolling element; scroll
|
|
136
|
+
actions still target elements. `selection.text`, `title`, `readyState`, `doctype`
|
|
137
|
+
and native URL/generation remain document-level information. Role/name label
|
|
138
|
+
references resolve within the element's own tree. Inspection follows the DOM
|
|
139
|
+
tree, not the composed rendering tree: slot-assigned light-DOM nodes remain in
|
|
140
|
+
their own containing DOM tree and are not duplicated inside the selected shadow
|
|
141
|
+
root.
|
|
142
|
+
|
|
143
|
+
A missing or ambiguous host produces document error `ARCANE_APP_CONTROL_TARGET_COUNT`
|
|
144
|
+
with the complete path, zero-based `index`, `selector` and match count. Invalid
|
|
145
|
+
path or CSS input reports its actual error before any action. A host without an
|
|
146
|
+
accessible open root produces document error `ARCANE_APP_CONTROL_UNSUPPORTED`
|
|
147
|
+
with the path and failing step. The Windows client rejects these document failures
|
|
148
|
+
with top-level `error.code === 'ARCANE_APP_CONTROL_FAILED'`; the original document
|
|
149
|
+
error, including its `code` and `details`, is available at
|
|
150
|
+
`error.data.find(entry => entry.key === 'documentError').value`.
|
|
151
|
+
Closed roots cannot be discovered, entered, or distinguished from
|
|
152
|
+
absent roots through this API. No closed-root interception is installed.
|
|
153
|
+
Shadow actions add the selected `shadowPath` to their result and retain the
|
|
154
|
+
actual target state after dispatch, including disconnection by the action.
|
|
101
155
|
|
|
102
156
|
Role/name hints are derived from DOM attributes and labels, not a native
|
|
103
|
-
accessibility-tree computation. Frame documents and shadow-root
|
|
104
|
-
outside
|
|
105
|
-
closed shadow roots cannot be discovered. Capture is the current rendered
|
|
157
|
+
accessibility-tree computation. Frame documents and unselected shadow-root
|
|
158
|
+
contents are outside the selected scope. Frame elements and open-shadow hosts
|
|
159
|
+
are described; closed shadow roots cannot be discovered. Capture is the current rendered
|
|
106
160
|
viewport, not an offscreen full-document rendering. Covered and minimized-window
|
|
107
161
|
capture behavior requires execution evidence in the selected environment.
|
|
108
162
|
|
|
@@ -130,13 +184,18 @@ markers are transient document lifecycle state and are not written to saved data
|
|
|
130
184
|
```powershell
|
|
131
185
|
arcane app-control status --endpoint '\\.\pipe\arcane-pm-control'
|
|
132
186
|
arcane app-control inspect --endpoint '\\.\pipe\arcane-pm-control' --selector '#pm-content'
|
|
187
|
+
arcane app-control inspect --endpoint '\\.\pipe\arcane-pm-control' --request '.arcane\inspection.json'
|
|
133
188
|
arcane app-control capture --endpoint '\\.\pipe\arcane-pm-control' --output '.arcane\view.png'
|
|
134
189
|
arcane app-control act --endpoint '\\.\pipe\arcane-pm-control' --request '.arcane\action.json'
|
|
135
190
|
```
|
|
136
191
|
|
|
137
192
|
The action file is the complete action parameter object, including the actual
|
|
138
|
-
`documentGeneration` returned by the preceding status or inspection.
|
|
139
|
-
|
|
193
|
+
`documentGeneration` returned by the preceding status or inspection. Inspection
|
|
194
|
+
also accepts `--request` for a complete parameter object, such as
|
|
195
|
+
`{"shadowPath":["#appearance-panel","theme-switcher"]}` or the same object with
|
|
196
|
+
`selector` and `documentGeneration`. Inspection's `--request` and `--selector`
|
|
197
|
+
options are mutually exclusive. The CLI reads the selected file completely.
|
|
198
|
+
Paths are relative to the current directory. Capture
|
|
140
199
|
writes the PNG to the explicitly selected file and prints its metadata. The other
|
|
141
200
|
operations print complete JSON. `app-control --help` describes this command's
|
|
142
201
|
options; other Arcane commands retain their existing global output-format option.
|
|
@@ -8903,8 +8903,11 @@ document operations. No application is launched, no desktop window is activated,
|
|
|
8903
8903
|
and no automatic reconnect or action retry occurs.
|
|
8904
8904
|
|
|
8905
8905
|
`status(options)` reports current app/window/document readiness.
|
|
8906
|
-
`inspect(parameters={},options)` returns complete selected
|
|
8907
|
-
and live control state.
|
|
8906
|
+
`inspect(parameters={},options)` returns complete selected DOM HTML/text
|
|
8907
|
+
and live control state. Inspection and actions accept optional `shadowPath`, an
|
|
8908
|
+
ordered array of unique CSS host selectors through open shadow roots; their
|
|
8909
|
+
`selector` resolves inside the final root. Omission retains top-level light DOM.
|
|
8910
|
+
Closed roots remain unavailable. `capture(parameters={},options)` returns a full base64 PNG
|
|
8908
8911
|
of the rendered viewport. `act(parameters,options)` requires a returned
|
|
8909
8912
|
`documentGeneration`, one CSS selector, and `click`, `fill`, `select` or `scroll`
|
|
8910
8913
|
fields. Options preserve the Core client signal/timeout contract. `close()`
|
package/package.json
CHANGED
package/src/cli/app-control.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {CoreError, serializeCoreError} from '../../browser-runtime/core/contract
|
|
|
5
5
|
|
|
6
6
|
const HELP_TEXT=`Usage:
|
|
7
7
|
arcane app-control status --endpoint <pipe-or-socket>
|
|
8
|
-
arcane app-control inspect --endpoint <pipe-or-socket> [--selector <css-selector>]
|
|
8
|
+
arcane app-control inspect --endpoint <pipe-or-socket> [--selector <css-selector> | --request <json-file>]
|
|
9
9
|
arcane app-control capture --endpoint <pipe-or-socket> --output <png-file>
|
|
10
10
|
arcane app-control act --endpoint <pipe-or-socket> --request <json-file>
|
|
11
11
|
|
|
@@ -14,6 +14,8 @@ Capture writes the PNG to --output and prints its metadata. Request and output
|
|
|
14
14
|
filenames are relative to the current directory. This command does not launch
|
|
15
15
|
an application or change its profile. --output selects the capture filename;
|
|
16
16
|
the other Arcane commands retain their existing output-format option.
|
|
17
|
+
Inspect --request reads the complete parameter object, including shadowPath and
|
|
18
|
+
optional documentGeneration. It cannot be combined with --selector.
|
|
17
19
|
`;
|
|
18
20
|
|
|
19
21
|
function usage(message) {
|
|
@@ -25,7 +27,7 @@ function parseArguments(argv) {
|
|
|
25
27
|
const [action] = argv;
|
|
26
28
|
const allowed = {
|
|
27
29
|
status: ['endpoint'],
|
|
28
|
-
inspect: ['endpoint', 'selector'],
|
|
30
|
+
inspect: ['endpoint', 'selector', 'request'],
|
|
29
31
|
capture: ['endpoint', 'output'],
|
|
30
32
|
act: ['endpoint', 'request']
|
|
31
33
|
}[action];
|
|
@@ -47,6 +49,9 @@ function parseArguments(argv) {
|
|
|
47
49
|
if (!values.endpoint) usage('App control requires --endpoint <pipe-or-socket>.');
|
|
48
50
|
if (action === 'capture' && !values.output) usage('App-control capture requires --output <png-file>.');
|
|
49
51
|
if (action === 'act' && !values.request) usage('App-control act requires --request <json-file>.');
|
|
52
|
+
if (action === 'inspect' && values.request !== undefined && values.selector !== undefined) {
|
|
53
|
+
usage('App-control inspect accepts --selector or --request, not both.');
|
|
54
|
+
}
|
|
50
55
|
return {action, ...values};
|
|
51
56
|
}
|
|
52
57
|
|
|
@@ -71,7 +76,7 @@ export async function runAppControlCli(argv, {cwd = process.cwd(), stdout = proc
|
|
|
71
76
|
stderr.write(`${JSON.stringify({status: 'running', operation: selection.action,
|
|
72
77
|
endpoint: selection.endpoint})}\n`);
|
|
73
78
|
const signal = controller.signal;
|
|
74
|
-
const parameters = selection.
|
|
79
|
+
const parameters = selection.request !== undefined
|
|
75
80
|
? JSON.parse(await readFile(path.resolve(cwd, selection.request), {encoding: 'utf8', signal}))
|
|
76
81
|
: selection.selector === undefined ? {} : {selector: selection.selector};
|
|
77
82
|
connection = await connectAppControl({endpoint: selection.endpoint, signal, onError: failed});
|