@houwert/conductor 0.27.0 → 0.27.2
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.
|
@@ -7,11 +7,13 @@ exports.parseFlowFile = parseFlowFile;
|
|
|
7
7
|
exports.parseFlowString = parseFlowString;
|
|
8
8
|
exports.executeFlow = executeFlow;
|
|
9
9
|
exports.resolvePoint = resolvePoint;
|
|
10
|
+
exports.resolvePath = resolvePath;
|
|
10
11
|
/**
|
|
11
12
|
* Native Conductor YAML flow parser and executor.
|
|
12
13
|
* Parses flow YAML files and executes commands directly using IOSDriver / AndroidDriver.
|
|
13
14
|
*/
|
|
14
15
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
16
|
+
const node_fs_1 = require("node:fs");
|
|
15
17
|
const path_1 = __importDefault(require("path"));
|
|
16
18
|
const node_vm_1 = __importDefault(require("node:vm"));
|
|
17
19
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
@@ -1368,8 +1370,72 @@ async function executeCommandBody(key, val, driver, opts) {
|
|
|
1368
1370
|
}
|
|
1369
1371
|
}
|
|
1370
1372
|
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
1373
|
+
// Resolve a `file:` reference from within a flow. A path starting with `@` is an
|
|
1374
|
+
// alias of the form `@name/rest`, where `name` maps to a directory declared under
|
|
1375
|
+
// `paths:` in the nearest `config.yaml`. Everything else keeps the historical
|
|
1376
|
+
// behavior: resolved relative to the flow file's directory (`cwd`).
|
|
1377
|
+
// Mirrors the plexinc/maestro FlowPathResolver.
|
|
1371
1378
|
function resolvePath(filePath, cwd) {
|
|
1372
|
-
|
|
1379
|
+
const base = cwd ?? process.cwd();
|
|
1380
|
+
if (filePath.startsWith('@'))
|
|
1381
|
+
return resolvePathAlias(filePath, base);
|
|
1382
|
+
return path_1.default.isAbsolute(filePath) ? filePath : path_1.default.join(base, filePath);
|
|
1383
|
+
}
|
|
1384
|
+
const CONFIG_FILE_NAMES = ['config.yaml', 'config.yml'];
|
|
1385
|
+
// Cache discovered config → paths map for the process lifetime (flows are short-lived).
|
|
1386
|
+
const configPathsCache = new Map();
|
|
1387
|
+
function resolvePathAlias(requestedPath, startDir) {
|
|
1388
|
+
const body = requestedPath.slice(1);
|
|
1389
|
+
const separator = body.indexOf('/');
|
|
1390
|
+
const alias = separator >= 0 ? body.slice(0, separator) : body;
|
|
1391
|
+
const remainder = separator >= 0 ? body.slice(separator + 1) : '';
|
|
1392
|
+
const configPath = findWorkspaceConfig(startDir);
|
|
1393
|
+
if (!configPath) {
|
|
1394
|
+
throw new Error(`Path alias '@${alias}' used but no config.yaml was found in any parent directory. ` +
|
|
1395
|
+
'Declare aliases under `paths:` in a workspace config.yaml.');
|
|
1396
|
+
}
|
|
1397
|
+
const paths = readWorkspacePaths(configPath);
|
|
1398
|
+
const target = paths[alias];
|
|
1399
|
+
if (target === undefined) {
|
|
1400
|
+
const known = Object.keys(paths).sort();
|
|
1401
|
+
throw new Error(`Unknown path alias '@${alias}' referenced in a flow. ` +
|
|
1402
|
+
`Known aliases in ${configPath}: [${known.join(', ')}]`);
|
|
1403
|
+
}
|
|
1404
|
+
const configDir = path_1.default.dirname(path_1.default.resolve(configPath));
|
|
1405
|
+
const targetDir = path_1.default.normalize(path_1.default.resolve(configDir, target));
|
|
1406
|
+
if (!(0, node_fs_1.existsSync)(targetDir) || !(0, node_fs_1.statSync)(targetDir).isDirectory()) {
|
|
1407
|
+
throw new Error(`Path alias '@${alias}' points to '${targetDir}', which is not an existing directory.`);
|
|
1408
|
+
}
|
|
1409
|
+
return path_1.default.normalize(path_1.default.resolve(targetDir, remainder));
|
|
1410
|
+
}
|
|
1411
|
+
function findWorkspaceConfig(startDir) {
|
|
1412
|
+
let dir = path_1.default.resolve(startDir);
|
|
1413
|
+
while (dir) {
|
|
1414
|
+
for (const name of CONFIG_FILE_NAMES) {
|
|
1415
|
+
const candidate = path_1.default.join(dir, name);
|
|
1416
|
+
if ((0, node_fs_1.existsSync)(candidate))
|
|
1417
|
+
return candidate;
|
|
1418
|
+
}
|
|
1419
|
+
const parent = path_1.default.dirname(dir);
|
|
1420
|
+
dir = parent === dir ? null : parent;
|
|
1421
|
+
}
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
function readWorkspacePaths(configPath) {
|
|
1425
|
+
const cached = configPathsCache.get(configPath);
|
|
1426
|
+
if (cached)
|
|
1427
|
+
return cached;
|
|
1428
|
+
let paths = {};
|
|
1429
|
+
try {
|
|
1430
|
+
const doc = js_yaml_1.default.load((0, node_fs_1.readFileSync)(configPath, 'utf-8'));
|
|
1431
|
+
if (doc && typeof doc.paths === 'object' && doc.paths)
|
|
1432
|
+
paths = doc.paths;
|
|
1433
|
+
}
|
|
1434
|
+
catch {
|
|
1435
|
+
// A malformed config leaves aliases unresolved; the unknown-alias error below is clearer.
|
|
1436
|
+
}
|
|
1437
|
+
configPathsCache.set(configPath, paths);
|
|
1438
|
+
return paths;
|
|
1373
1439
|
}
|
|
1374
1440
|
function resolveAppId(val, cmdName) {
|
|
1375
1441
|
if (typeof val === 'string' && val)
|
package/package.json
CHANGED
|
@@ -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`, `--inject` |
|
|
77
|
+
| `conductor launch-app <appId>` | Launch app (saved to session); `--no-stop-app`, `--argument key=value`, `--inject` (enables the `native-*` in-process instrument — see `conductor-native`) |
|
|
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,48 +29,13 @@ conductor capture-ui --output /tmp/screen.json
|
|
|
29
29
|
conductor tap-on @e5
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
## Native
|
|
32
|
+
## Native internals (colors, fonts, layers, live-edit)
|
|
33
33
|
|
|
34
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.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(iOS/tvOS simulator
|
|
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
|
-
```
|
|
35
|
+
they can't see real component colors, fonts, or the view-controller stack. For
|
|
36
|
+
that native detail — or to live-edit a running app's view properties, force
|
|
37
|
+
appearance/RTL, or run Swift in-process — use the **`conductor-native`** skill
|
|
38
|
+
(`launch-app --inject` + the `native-*` commands; iOS/tvOS simulator, dev builds).
|
|
74
39
|
|
|
75
40
|
## Assertions
|
|
76
41
|
|
|
@@ -21,7 +21,8 @@ Playwright web.
|
|
|
21
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
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
23
|
|
|
24
|
-
`--react-tag` comes from `native-inspect`'s `rn.reactTag
|
|
24
|
+
`--react-tag` comes from `native-inspect`'s `rn.reactTag` (see `conductor-native`
|
|
25
|
+
for the native-plane instrument). `--value` is JSON (a bare
|
|
25
26
|
string works for text). `--path` is a dot path into props: `children`, `style.color`,
|
|
26
27
|
`style.fontSize`, `accessibilityLabel`. `style.color` works whether the component's
|
|
27
28
|
`style` is an object or a composed array. These drive React itself over Metro CDP, so
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conductor-native
|
|
3
|
+
description: Inspect and live-edit a running app's native internals with the conductor CLI's in-process instrument (iOS/tvOS simulator, requires launch-app --inject). Use when you need real native view details the accessibility tree can't show — resolved colors/fonts/layers, the UIViewController/navigation stack, Auto Layout constraints, live-object heap — or to tweak the running app in place: set a view's properties (color, text, frame), force dark/RTL/animation state, or run arbitrary Swift inside the process.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Conductor — native in-process inspection & live editing
|
|
7
|
+
|
|
8
|
+
The external commands in `conductor-inspect` observe the app through the
|
|
9
|
+
**accessibility tree**, so they can't see real component colors, fonts, layers,
|
|
10
|
+
or the view-controller stack. When you need that native detail — or want to
|
|
11
|
+
tweak the running app in place — launch with an injected in-process library and
|
|
12
|
+
use the `native-*` commands.
|
|
13
|
+
|
|
14
|
+
Requires `launch-app <appId> --inject` first. **iOS/tvOS simulator, dev builds
|
|
15
|
+
only.** IDs are pointer-based and reset each launch, so re-inspect after relaunch.
|
|
16
|
+
|
|
17
|
+
## Inspect the native plane
|
|
18
|
+
|
|
19
|
+
| Command | Purpose |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `conductor native-ping` | Verify the injected in-process control library is alive |
|
|
22
|
+
| `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` |
|
|
23
|
+
| `conductor native-nav` | Navigation state: `UINavigationController` stacks, tab selection, presented controllers, titles |
|
|
24
|
+
| `conductor native-view <id>` | Full property detail for one view (class chain, transform, layer, gestures, text/font) |
|
|
25
|
+
| `conductor native-props <id>` | React Native Fabric props: typed `ViewProps` + the raw JS prop bag (Fabric host views only) |
|
|
26
|
+
| `conductor native-constraints <id>` | Auto Layout constraints affecting a view + ambiguity |
|
|
27
|
+
| `conductor native-hittest <x,y>` | Topmost view at a point + ancestor chain (select-by-point) |
|
|
28
|
+
| `conductor native-find [--class <name>] [--text <s>]` | Search views by class and/or text |
|
|
29
|
+
| `conductor native-heap --pattern <s> \| --class <name> \| --read <addr> [--key <keyPath>]` | Live-object browser (find classes/instances, read a property off an address) |
|
|
30
|
+
| `conductor native-console [--since <n>]` / `native-network [--since <n>]` | App stdout/stderr + captured HTTP; poll with the returned `cursor` |
|
|
31
|
+
| `conductor native-screenshot --output <p.png>` | In-process PNG of the key window |
|
|
32
|
+
| `conductor native-image <x,y,w,h> --output <p.png>` | Extract a component as a PNG — pass a node's `absFrame` from `native-inspect` |
|
|
33
|
+
| `conductor native-snapshot <id> --output <p.png>` | Isolated PNG of one view's own content (transparent); `--with-subviews` composites the subtree |
|
|
34
|
+
| `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`. |
|
|
35
|
+
|
|
36
|
+
## Live-edit the running app
|
|
37
|
+
|
|
38
|
+
| Command | Purpose |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `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 |
|
|
41
|
+
| `conductor native-highlight <id>` | Flash a highlight over the view on the device |
|
|
42
|
+
| `conductor native-appearance <light\|dark\|system> \| --direction <ltr\|rtl> \| --anim-speed <n>` | Force appearance / RTL / freeze animations app-wide |
|
|
43
|
+
| `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'` |
|
|
44
|
+
|
|
45
|
+
> **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.
|
|
46
|
+
|
|
47
|
+
## The Reveal-style loop
|
|
48
|
+
|
|
49
|
+
Every `native-inspect` node has a stable `id` (for this launch): inspect →
|
|
50
|
+
pick an `id` → `native-view` for detail → `native-set` to edit live → see it on
|
|
51
|
+
the device. Re-inspect after relaunch (IDs reset).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
conductor launch-app com.example.app --inject
|
|
55
|
+
conductor native-inspect # tree with ids, colors, fonts, absFrame
|
|
56
|
+
conductor native-view 0x10280d0c0 # full detail for a view
|
|
57
|
+
conductor native-set 0x10280d0c0 backgroundColor '#FF3B30FF' # live-edit, visible on device
|
|
58
|
+
conductor native-image 816,286,288,288 --output /tmp/avatar.png
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Related
|
|
62
|
+
|
|
63
|
+
- `conductor-inspect` — external observation (a11y snapshots, screenshots, `@eN` refs) + assertions.
|
|
64
|
+
- `conductor-metro-debugger` — the React/JS plane: `native-rn-set` / `native-rn-props`, `debug evaluate`, component tree, logs, network.
|