@houwert/conductor 0.24.1 → 0.25.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/dist/commands/input-server.js +28 -0
- package/dist/commands/launch-app.js +30 -3
- package/dist/commands/native-rn.js +110 -0
- package/dist/commands/native.js +320 -0
- package/dist/daemon/client.js +5 -0
- package/dist/daemon/input-backends.js +203 -0
- package/dist/daemon/input-protocol.js +40 -0
- package/dist/daemon/input-router.js +110 -0
- package/dist/daemon/input-server.js +124 -0
- package/dist/daemon/server.js +84 -0
- package/dist/drivers/bootstrap.js +49 -0
- package/dist/drivers/eval-compiler.js +131 -0
- package/dist/drivers/ios-hid.js +95 -0
- package/dist/drivers/ios-inproc.js +198 -0
- package/dist/drivers/ios.js +39 -8
- package/dist/drivers/metro-scripts.js +174 -0
- package/dist/index.js +141 -0
- package/dist/runner.js +33 -0
- package/package.json +1 -1
- package/skills/conductor-device-interact/SKILL.md +13 -0
- package/skills/conductor-device-setup/SKILL.md +1 -1
- package/skills/conductor-inspect/SKILL.md +43 -0
- package/skills/conductor-metro-debugger/SKILL.md +10 -0
package/dist/index.js
CHANGED
|
@@ -9,6 +9,8 @@ const verbose_js_1 = require("./verbose.js");
|
|
|
9
9
|
const sdk_js_1 = require("./android/sdk.js");
|
|
10
10
|
const list_devices_js_1 = require("./commands/list-devices.js");
|
|
11
11
|
const launch_app_js_1 = require("./commands/launch-app.js");
|
|
12
|
+
const native_js_1 = require("./commands/native.js");
|
|
13
|
+
const native_rn_js_1 = require("./commands/native-rn.js");
|
|
12
14
|
const stop_app_js_1 = require("./commands/stop-app.js");
|
|
13
15
|
const clear_state_js_1 = require("./commands/clear-state.js");
|
|
14
16
|
const uninstall_app_js_1 = require("./commands/uninstall-app.js");
|
|
@@ -27,6 +29,7 @@ const run_flow_inline_js_1 = require("./commands/run-flow-inline.js");
|
|
|
27
29
|
const press_key_js_1 = require("./commands/press-key.js");
|
|
28
30
|
const session_js_1 = require("./commands/session.js");
|
|
29
31
|
const daemon_js_1 = require("./commands/daemon.js");
|
|
32
|
+
const input_server_js_1 = require("./commands/input-server.js");
|
|
30
33
|
const install_js_1 = require("./commands/install.js");
|
|
31
34
|
const init_js_1 = require("./commands/init.js");
|
|
32
35
|
const device_pool_js_1 = require("./commands/device-pool.js");
|
|
@@ -80,6 +83,27 @@ const COMMAND_HELP = {
|
|
|
80
83
|
'download-app': download_app_js_1.HELP,
|
|
81
84
|
'install-app': install_app_js_1.HELP,
|
|
82
85
|
'launch-app': launch_app_js_1.HELP,
|
|
86
|
+
'native-ping': native_js_1.PING_HELP,
|
|
87
|
+
'native-inspect': native_js_1.INSPECT_HELP,
|
|
88
|
+
'native-nav': native_js_1.NAV_HELP,
|
|
89
|
+
'native-screenshot': native_js_1.SCREENSHOT_HELP,
|
|
90
|
+
'native-image': native_js_1.IMAGE_HELP,
|
|
91
|
+
'native-snapshot': native_js_1.SNAPSHOT_HELP,
|
|
92
|
+
'native-view': native_js_1.VIEW_HELP,
|
|
93
|
+
'native-set': native_js_1.SET_HELP,
|
|
94
|
+
'native-props': native_js_1.PROPS_HELP,
|
|
95
|
+
'native-rn-set': native_rn_js_1.RN_SET_HELP,
|
|
96
|
+
'native-rn-props': native_rn_js_1.RN_PROPS_HELP,
|
|
97
|
+
'native-constraints': native_js_1.CONSTRAINTS_HELP,
|
|
98
|
+
'native-hittest': native_js_1.HITTEST_HELP,
|
|
99
|
+
'native-highlight': native_js_1.HIGHLIGHT_HELP,
|
|
100
|
+
'native-find': native_js_1.FIND_HELP,
|
|
101
|
+
'native-raw': native_js_1.RAW_HELP,
|
|
102
|
+
'native-console': native_js_1.CONSOLE_HELP,
|
|
103
|
+
'native-network': native_js_1.NETWORK_HELP,
|
|
104
|
+
'native-heap': native_js_1.HEAP_HELP,
|
|
105
|
+
'native-appearance': native_js_1.APPEARANCE_HELP,
|
|
106
|
+
'native-eval': native_js_1.EVAL_HELP,
|
|
83
107
|
'stop-app': stop_app_js_1.HELP,
|
|
84
108
|
'clear-state': clear_state_js_1.HELP,
|
|
85
109
|
'uninstall-app': uninstall_app_js_1.HELP,
|
|
@@ -110,6 +134,7 @@ const COMMAND_HELP = {
|
|
|
110
134
|
'daemon-start': daemon_js_1.HELP_DAEMON_START,
|
|
111
135
|
'daemon-stop': daemon_js_1.HELP_DAEMON_STOP,
|
|
112
136
|
'daemon-status': daemon_js_1.HELP_DAEMON_STATUS,
|
|
137
|
+
'input-server': input_server_js_1.HELP,
|
|
113
138
|
'device-pool': device_pool_js_1.HELP,
|
|
114
139
|
'run-parallel': run_parallel_js_1.HELP,
|
|
115
140
|
'run-sequence': run_sequence_js_1.HELP,
|
|
@@ -240,6 +265,9 @@ async function main() {
|
|
|
240
265
|
'cdp-url',
|
|
241
266
|
'cdp-target',
|
|
242
267
|
'duration',
|
|
268
|
+
'react-tag',
|
|
269
|
+
'path',
|
|
270
|
+
'value',
|
|
243
271
|
],
|
|
244
272
|
alias: { h: 'help', v: 'verbose', V: 'version', o: 'output', y: 'yes' },
|
|
245
273
|
});
|
|
@@ -424,9 +452,119 @@ async function main() {
|
|
|
424
452
|
clearKeychain: argv['clear-keychain'],
|
|
425
453
|
stopApp: argv['stop-app'] !== false,
|
|
426
454
|
launchArgs,
|
|
455
|
+
inject: argv['inject'],
|
|
427
456
|
});
|
|
428
457
|
break;
|
|
429
458
|
}
|
|
459
|
+
case 'native-ping': {
|
|
460
|
+
exitCode = await (0, native_js_1.nativePing)(opts, sessionName);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case 'native-inspect': {
|
|
464
|
+
exitCode = await (0, native_js_1.nativeInspect)(opts, sessionName);
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case 'native-nav': {
|
|
468
|
+
exitCode = await (0, native_js_1.nativeNav)(opts, sessionName);
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
case 'native-screenshot': {
|
|
472
|
+
exitCode = await (0, native_js_1.nativeScreenshot)(argv['output'], opts, sessionName);
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
case 'native-image': {
|
|
476
|
+
exitCode = await (0, native_js_1.nativeImage)(rest[0], argv['output'], opts, sessionName);
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
case 'native-snapshot': {
|
|
480
|
+
exitCode = await (0, native_js_1.nativeSnapshot)(rest[0], argv['with-subviews'], argv['output'], opts, sessionName);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case 'native-view': {
|
|
484
|
+
exitCode = await (0, native_js_1.nativeView)(rest[0], opts, sessionName);
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
case 'native-set': {
|
|
488
|
+
exitCode = await (0, native_js_1.nativeSet)(rest, opts, sessionName);
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case 'native-props': {
|
|
492
|
+
exitCode = await (0, native_js_1.nativeProps)(rest[0], opts, sessionName);
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case 'native-rn-set': {
|
|
496
|
+
const rnOpts = {
|
|
497
|
+
port: argv['port'] !== undefined ? Number(argv['port']) : undefined,
|
|
498
|
+
targetIndex: argv['target'] !== undefined ? Number(argv['target']) : undefined,
|
|
499
|
+
};
|
|
500
|
+
exitCode = await (0, native_rn_js_1.nativeRnSet)({
|
|
501
|
+
reactTag: argv['react-tag'],
|
|
502
|
+
path: argv['path'],
|
|
503
|
+
value: argv['value'],
|
|
504
|
+
}, opts, sessionName, rnOpts);
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
case 'native-rn-props': {
|
|
508
|
+
const rnOpts = {
|
|
509
|
+
port: argv['port'] !== undefined ? Number(argv['port']) : undefined,
|
|
510
|
+
targetIndex: argv['target'] !== undefined ? Number(argv['target']) : undefined,
|
|
511
|
+
};
|
|
512
|
+
exitCode = await (0, native_rn_js_1.nativeRnProps)({ reactTag: argv['react-tag'] }, opts, sessionName, rnOpts);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
case 'native-constraints': {
|
|
516
|
+
exitCode = await (0, native_js_1.nativeConstraints)(rest[0], opts, sessionName);
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case 'native-hittest': {
|
|
520
|
+
exitCode = await (0, native_js_1.nativeHittest)(rest[0], opts, sessionName);
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case 'native-highlight': {
|
|
524
|
+
exitCode = await (0, native_js_1.nativeHighlight)(rest[0], opts, sessionName);
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case 'native-find': {
|
|
528
|
+
exitCode = await (0, native_js_1.nativeFind)({
|
|
529
|
+
className: argv['class'],
|
|
530
|
+
text: argv['text'],
|
|
531
|
+
}, opts, sessionName);
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case 'native-raw': {
|
|
535
|
+
exitCode = await (0, native_js_1.nativeRaw)(rest[0], argv['output'], opts, sessionName);
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
case 'native-console': {
|
|
539
|
+
exitCode = await (0, native_js_1.nativeConsole)(argv['since'] !== undefined ? Number(argv['since']) : undefined, opts, sessionName);
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
case 'native-network': {
|
|
543
|
+
exitCode = await (0, native_js_1.nativeNetwork)(argv['since'] !== undefined ? Number(argv['since']) : undefined, opts, sessionName);
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case 'native-heap': {
|
|
547
|
+
exitCode = await (0, native_js_1.nativeHeap)({
|
|
548
|
+
className: argv['class'],
|
|
549
|
+
pattern: argv['pattern'],
|
|
550
|
+
read: argv['read'],
|
|
551
|
+
key: argv['key'],
|
|
552
|
+
}, opts, sessionName);
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
case 'native-appearance': {
|
|
556
|
+
exitCode = await (0, native_js_1.nativeAppearance)({
|
|
557
|
+
style: rest[0],
|
|
558
|
+
direction: argv['direction'],
|
|
559
|
+
contentSize: argv['content-size'],
|
|
560
|
+
animSpeed: argv['anim-speed'],
|
|
561
|
+
}, opts, sessionName);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case 'native-eval': {
|
|
565
|
+
exitCode = await (0, native_js_1.nativeEval)(rest.join(' '), argv['mode'] === 'full' ? 'full' : 'expr', opts, sessionName);
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
430
568
|
case 'stop-app': {
|
|
431
569
|
const appId = rest[0];
|
|
432
570
|
exitCode = await (0, stop_app_js_1.stopApp)(appId, opts, sessionName);
|
|
@@ -706,6 +844,9 @@ async function main() {
|
|
|
706
844
|
case 'daemon-status':
|
|
707
845
|
exitCode = await (0, daemon_js_1.daemonStatusCmd)(opts, sessionName);
|
|
708
846
|
break;
|
|
847
|
+
case 'input-server':
|
|
848
|
+
exitCode = await (0, input_server_js_1.inputServer)(opts, sessionName);
|
|
849
|
+
break;
|
|
709
850
|
case 'device-pool': {
|
|
710
851
|
const acquire = argv['acquire'];
|
|
711
852
|
const release = argv['release'];
|
package/dist/runner.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.detectFirstDevice = detectFirstDevice;
|
|
4
4
|
exports.getDriver = getDriver;
|
|
5
5
|
exports.prewarmDriver = prewarmDriver;
|
|
6
|
+
exports.inputServerInfo = inputServerInfo;
|
|
6
7
|
exports.runDirect = runDirect;
|
|
7
8
|
exports.spawnCommand = spawnCommand;
|
|
8
9
|
exports.runInlineFlow = runInlineFlow;
|
|
@@ -243,6 +244,38 @@ async function prewarmDriver(deviceId) {
|
|
|
243
244
|
/* best-effort: a later command will report a real failure */
|
|
244
245
|
}
|
|
245
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolve the streaming-input socket for a session's device, starting the
|
|
249
|
+
* daemon (and its driver + input server) if needed. Returns the loopback
|
|
250
|
+
* WebSocket URL the host IDE connects to. Throws if the platform has no
|
|
251
|
+
* streaming input (web/vega) or the port never comes up.
|
|
252
|
+
*/
|
|
253
|
+
async function inputServerInfo(sessionName = 'default') {
|
|
254
|
+
const deviceId = await resolveDeviceId(sessionName);
|
|
255
|
+
if (!deviceId) {
|
|
256
|
+
throw new Error('No device found. Connect a device or start a simulator, then run again.');
|
|
257
|
+
}
|
|
258
|
+
const platform = await (0, bootstrap_js_1.detectPlatform)(deviceId);
|
|
259
|
+
if (platform !== 'ios' && platform !== 'tvos' && platform !== 'android') {
|
|
260
|
+
throw new Error(`Streaming input is not available for ${platform} devices.`);
|
|
261
|
+
}
|
|
262
|
+
await (0, client_js_1.startDaemon)(deviceId);
|
|
263
|
+
// The input server starts just after the driver — poll status until its port appears.
|
|
264
|
+
const deadline = Date.now() + 60000;
|
|
265
|
+
while (Date.now() < deadline) {
|
|
266
|
+
const status = await (0, client_js_1.fetchDaemonStatus)(deviceId);
|
|
267
|
+
if (status && typeof status.inputPort === 'number') {
|
|
268
|
+
return {
|
|
269
|
+
device: deviceId,
|
|
270
|
+
platform,
|
|
271
|
+
inputPort: status.inputPort,
|
|
272
|
+
url: `ws://127.0.0.1:${status.inputPort}/input`,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
276
|
+
}
|
|
277
|
+
throw new Error(`Input server for ${deviceId} did not come up within timeout.`);
|
|
278
|
+
}
|
|
246
279
|
/**
|
|
247
280
|
* Execute a function with the driver for the given session.
|
|
248
281
|
* Returns a RunResult for consistent error handling across commands.
|
package/package.json
CHANGED
|
@@ -51,6 +51,19 @@ conductor assert-visible "Dashboard"
|
|
|
51
51
|
| `conductor gesture <json\|--file path>` | Play a multi-touch path |
|
|
52
52
|
| `conductor clipboard read` / `clipboard write <text>` / `paste` | Clipboard (iOS) |
|
|
53
53
|
| `conductor list-options [command]` | List valid values for enumerated params |
|
|
54
|
+
| `conductor input-server` | Start (if needed) and print the streaming-input WebSocket URL for the device |
|
|
55
|
+
|
|
56
|
+
## Streaming input (host IDEs)
|
|
57
|
+
|
|
58
|
+
For continuous, low-latency input (live drags, fast typing) a host IDE can open
|
|
59
|
+
one persistent WebSocket per device instead of spawning a command per event.
|
|
60
|
+
`conductor input-server` ensures the daemon + driver are up and prints the
|
|
61
|
+
loopback URL (`ws://127.0.0.1:<port>/input`; also in `daemon-status --json` as
|
|
62
|
+
`inputPort`). The server sends a `hello` with per-platform capabilities, then
|
|
63
|
+
accepts normalized (0..1) frames: `pointer{id,phase,x,y}`, `key{code,mods,down}`,
|
|
64
|
+
`text{value}`, `button{name}`, `scroll{x,y,dx,dy}`, `tvremote{button}`. Conductor
|
|
65
|
+
owns coord→device translation and keymaps. For scripted, one-off actions use the
|
|
66
|
+
discrete commands above — this is for interactive host UIs.
|
|
54
67
|
|
|
55
68
|
## Discovering valid values
|
|
56
69
|
|
|
@@ -74,7 +74,7 @@ screen recording, clipboard, `clear-state`/`uninstall-app`.
|
|
|
74
74
|
| Command | Purpose |
|
|
75
75
|
| ----------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
76
76
|
| `conductor install-app <path>` | Install .app / .ipa / .apk |
|
|
77
|
-
| `conductor launch-app <appId>` | Launch app (saved to session); `--no-stop-app`, `--argument key=value` |
|
|
77
|
+
| `conductor launch-app <appId>` | Launch app (saved to session); `--no-stop-app`, `--argument key=value`, `--inject` |
|
|
78
78
|
| `conductor stop-app [<appId>]` | Stop app |
|
|
79
79
|
| `conductor uninstall-app <appId>` | Uninstall app |
|
|
80
80
|
| `conductor copy-app <bundleId> --from <id> --to <id>` | Copy an installed app between iOS simulators |
|
|
@@ -29,6 +29,49 @@ conductor capture-ui --output /tmp/screen.json
|
|
|
29
29
|
conductor tap-on @e5
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## Native in-process inspection (iOS/tvOS simulator)
|
|
33
|
+
|
|
34
|
+
The commands above observe the app **externally** (accessibility snapshots), so
|
|
35
|
+
they can't see real component colors, fonts, or the view-controller stack. When
|
|
36
|
+
you need that native detail, launch the app with an injected in-process library
|
|
37
|
+
and use the `native-*` commands. Requires `launch-app <appId> --inject` first
|
|
38
|
+
(iOS/tvOS simulator only).
|
|
39
|
+
|
|
40
|
+
| Command | Purpose |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `conductor native-ping` | Verify the injected in-process control library is alive |
|
|
43
|
+
| `conductor native-inspect` | Real UIView/CALayer tree: resolved colors (`#RRGGBBAA`), fonts, text (incl. React Native Fabric), corner radius, borders, shadows, gradients, and each node's `absFrame` |
|
|
44
|
+
| `conductor native-nav` | Navigation state: `UINavigationController` stacks, tab selection, presented controllers, titles |
|
|
45
|
+
| `conductor native-screenshot --output <p.png>` | In-process PNG of the key window |
|
|
46
|
+
| `conductor native-image <x,y,w,h> --output <p.png>` | Extract a component as a PNG — pass a node's `absFrame` from `native-inspect` |
|
|
47
|
+
| `conductor native-snapshot <id> --output <p.png>` | Isolated PNG of one view's own content (transparent) — per-layer texture for a 3D explosion; `--with-subviews` composites the subtree |
|
|
48
|
+
| `conductor native-console [--since <n>]` / `native-network [--since <n>]` | App stdout/stderr + captured HTTP; poll with the returned `cursor` |
|
|
49
|
+
| `conductor native-heap --pattern <s> \| --class <name> \| --read <addr> [--key <keyPath>]` | Live-object browser (find classes/instances, read a property off an address) |
|
|
50
|
+
| `conductor native-appearance <light\|dark\|system> \| --direction <ltr\|rtl> \| --anim-speed <n>` | Force appearance / RTL / freeze animations app-wide |
|
|
51
|
+
| `conductor native-eval '<swift>'` | Compile & run arbitrary Swift inside the app (full UIKit / ObjC-runtime access); `--mode full` for a whole function body. e.g. `native-eval 'UIScreen.main.bounds'` |
|
|
52
|
+
| `conductor native-raw <path>` | Escape hatch — GET any in-process endpoint (e.g. `'/get?id=..&keyPath=layer.cornerRadius'`, `'/class?id=..'`, `'/responders?id=..'`, `'/swiftui'`, `'/defaults'`, `'/focus'`, `'/snapshots?scale=0.5'`). Full list in `packages/ios-inproc/README.md`. |
|
|
53
|
+
| `conductor native-view <id>` | Full property detail for one view (class chain, transform, layer, gestures, text/font) |
|
|
54
|
+
| `conductor native-set <id> <key> <value>` | **Live-edit** a property: alpha, hidden, backgroundColor, tintColor, cornerRadius, borderWidth, borderColor, frame, text, textColor. `text`/`textColor` work on RN Fabric text views too |
|
|
55
|
+
| `conductor native-props <id>` | React Native Fabric props: typed `ViewProps` + the raw JS prop bag (Fabric host views only) |
|
|
56
|
+
|
|
57
|
+
> **Editing RN Fabric text/props:** the native plane can't set text on `RCTParagraphComponentView` (no native setter) and `native-props` returns `rawProps: null` on Fabric. Edit through React instead with `conductor native-rn-set --react-tag <n> --path children --value '"…"'` (and read raw JSX props with `native-rn-props --react-tag <n>`). `reactTag` comes from this tree's `rn.reactTag`. See the conductor-metro-debugger skill. Dev builds only.
|
|
58
|
+
| `conductor native-constraints <id>` | Auto Layout constraints affecting a view + ambiguity |
|
|
59
|
+
| `conductor native-hittest <x,y>` | Topmost view at a point + ancestor chain (select-by-point) |
|
|
60
|
+
| `conductor native-highlight <id>` | Flash a highlight over the view on the device |
|
|
61
|
+
| `conductor native-find [--class <name>] [--text <s>]` | Search views by class and/or text |
|
|
62
|
+
|
|
63
|
+
Every `native-inspect` node has a stable `id` (for this launch). The Reveal-style loop:
|
|
64
|
+
inspect → pick an `id` → `native-view` for detail → `native-set` to edit live → see it
|
|
65
|
+
on the device. IDs are pointer-based and reset each launch, so re-inspect after relaunch.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
conductor launch-app com.example.app --inject
|
|
69
|
+
conductor native-inspect # tree with ids, colors, fonts, absFrame
|
|
70
|
+
conductor native-view 0x10280d0c0 # full detail for a view
|
|
71
|
+
conductor native-set 0x10280d0c0 backgroundColor '#FF3B30FF' # live-edit, visible on device
|
|
72
|
+
conductor native-image 816,286,288,288 --output /tmp/avatar.png
|
|
73
|
+
```
|
|
74
|
+
|
|
32
75
|
## Assertions
|
|
33
76
|
|
|
34
77
|
| Command | Purpose |
|
|
@@ -18,6 +18,16 @@ Playwright web.
|
|
|
18
18
|
| `conductor debug component-tree [--port N]` | On-screen React component tree |
|
|
19
19
|
| `conductor debug inspect-element <x,y>` | React component at a screen point |
|
|
20
20
|
| `conductor debug log-registry [--source metro]` | Summarize recent Metro/Hermes console logs |
|
|
21
|
+
| `conductor native-rn-set --react-tag <n> --path <dot.path> --value <json>` | Live-edit an RN component's props via React DevTools `overrideProps` (text via `--path children`, color via `--path style.color`). Dev builds only |
|
|
22
|
+
| `conductor native-rn-props --react-tag <n>` | Raw JSX props (`memoizedProps`) of an RN fiber by reactTag — the JS-side truth for Fabric where native `/props` `rawProps` is null |
|
|
23
|
+
|
|
24
|
+
`--react-tag` comes from `native-inspect`'s `rn.reactTag`. `--value` is JSON (a bare
|
|
25
|
+
string works for text). `--path` is a dot path into props: `children`, `style.color`,
|
|
26
|
+
`style.fontSize`, `accessibilityLabel`. `style.color` works whether the component's
|
|
27
|
+
`style` is an object or a composed array. These drive React itself over Metro CDP, so
|
|
28
|
+
they need a **dev/debug build** (the DevTools backend must be active) and a running
|
|
29
|
+
Metro; on a release build they return a clear "not a dev build" error. Output is
|
|
30
|
+
`{"status":"ok","applied":true}` or `{"status":"error","message":"…"}`.
|
|
21
31
|
|
|
22
32
|
## Logs
|
|
23
33
|
|