@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
- return path_1.default.isAbsolute(filePath) ? filePath : path_1.default.join(cwd ?? process.cwd(), filePath);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@houwert/conductor",
3
- "version": "0.27.0",
3
+ "version": "0.27.2",
4
4
  "description": "CLI tool for mobile app interactions — optimized for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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 in-process inspection (iOS/tvOS simulator)
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. 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
- ```
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`. `--value` is JSON (a bare
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.