mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
feat/flow-bash-scripts
64 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
aa029bd6ec | chore: remove comments | ||
|
|
c33e4ab062 |
ci(windows): make the path filter cover the graph its comment claims
The filter's own comment states the rule - "editing one changes what this job runs without touching any file listed above it" - and naming files one by one left 18 of the 33 files the three `.sh` test files import outside it. Two of those decide a Windows outcome: `configuration-core/src/paths.ts` holds the `USERPROFILE` branch `flow-script-interpreter.test.ts` asserts the global config path against, and `registry/src/file-inputs.ts` is where `SCRIPT_FILE_NAME_PATTERN` gained `sh`, so the change that admits `.sh` steps at all could be edited with this job unrun. Both packages are small and wholly inside that graph, so each is taken whole. `utils/capability.ts` joins the named files. A new test walks the same graph and holds the filter to it, so the list cannot rot again; it also pins that the three files carrying `taskkill /t` are named. Dropping either glob fails it. Its scope is the three `.sh` files this job was extended with - the seven other tests the job runs reach most of the tool server between them, which the filter never claimed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Z6tHq3XazgZbrS4YT73xs |
||
|
|
375ef48643 |
ci(windows): re-run the Windows job for the rest of the bash path's graph
The comment added beside the two test helpers states the rule the filter is
meant to satisfy - "editing one changes what this job runs without touching
any file listed above it" - and applied it to the helpers but not to
`packages/configuration-core`, which is in the real import graph of all three
newly-listed test files.
That package is not incidental to Windows here: this branch puts
`WINDOWS_ROOTED_PATH_RE` and the win32 arm of `asAbsolutePath` in
`config-schema.ts`, and `flow-script-interpreter.ts` reads that same regex
back - the comment beside it calls it "the one rule, shared with the tool
server's own interpreter check". So a pull request whose only change was to
that shared rule touched none of the listed paths, skipped this job, and was
never exercised against a real Windows host, a real `where.exe` or a real
Git-for-Windows `bash.exe` - though three of the tests this job was extended
to run exist to pin exactly that rule.
`config-access.ts` and `utils/{electron-env,format-error,secrets,timing}.ts`
are in the same graph and are added with it.
|
||
|
|
6fff86eaf3 |
feat(flows): run a .sh script step under bash
A `script` step's work is as often a shell one-liner as a Node program: `curl` an internal API, `psql -f` a seed file, `adb shell` a settings change. This adds `.sh` as a second language on the existing step - one new extension, no new key, no new tool, no new step kind and no new report field. The contract is one line each: input is the environment, the output document is the file named by `$ARGENT_OUTPUT`, the verdict is the exit status, and a failure reason is the file named by `$ARGENT_REASON`. Bash runs inside the runner rather than instead of it. The executor forks the same `flow-script-runner.mjs`, as the entry module rather than as an `--import` preload, and the runner spawns bash as its own child. That keeps one process group per step, so the parent's group stop and both watchdogs' group kills reach a bash descendant unchanged - which matters because a bash polling loop is exactly the orphan the watchdogs exist for, and a shell has no thread to watch a descriptor from. |
||
|
|
b570f4c484 |
perf(ci): take typechecking off the unit-test critical path (#1110)
`Unit Tests` is the entire PR wall clock: **468s of run + 61s of
queue**, while the other five gating jobs finish in 7-118s. Typechecking
is 64s of that, sits in series ahead of the tests, and needs none of
their output.
| | before | after |
|---|---|---|
| typecheck (64s) | serial, inside the test job | own job, runs
alongside - **116s, parallel** |
| npm cache | none on any gating job | `cache: npm`, except knip |
| superseded PR runs | keep holding a runner | cancelled |
This is scaffolding, not the fix. The job is dominated by
`tool-server/test/flows`, and that needs its own PR - measurements and a
rejected shortcut below.
`Typecheck` is a new check. It needs adding to the required status
checks on `main`, or typecheck failures stop blocking merge.
<details>
<summary>Where the 8 minutes is</summary>
`Unit Tests` run `34367669322`, per-step:
| step | time |
|---|---|
| setup + checkout + node | 8s |
| npm ci | 12s |
| tsc --build | 29s |
| Typecheck tests (13 serial `tsc`) | 62s |
| **Run tests** | **350s** |
| everything else | 5s |
`Run tests`, per package: tool-server 317.7s, the other 12 packages 28s
combined.
Inside tool-server (368 local test files): `test/flows` is 591s across
54 files, everything else 51s across 314. Half the suite is 5 files; 80%
is 13, all flows.
Those files drive the flow engine through its real `SETTLE_TIMEOUT_MS`
(3000), `DEFAULT_ACTION_TIMEOUT_MS` (7500) and `POST_LAUNCH_SETTLE_MS`
(1500). The five worst run in isolation at **87.4s wall, 7% CPU**.
`flow-gesture-settle.test.ts` is 37 tests at a 3.02s median - the settle
window waited out per test.
</details>
<details>
<summary>npm cache, per job (install step, against the uncached
baseline)</summary>
| job | before | after |
|---|---|---|
| ESLint (docs tree) | 26s | 16s |
| Prettier check | 17s | 11s |
| Unit tests | 12s | 10s |
| Knip | 6s | 8s + 1s save |
Knip installs with `--ignore-scripts` and pulls the least of any job
here, so restore and save cost more than the download they replace. It
keeps the uncached path.
</details>
<details>
<summary>Over-subscribing the worker pool: tried on this PR,
reverted</summary>
Flooring `maxWorkers` at 8 was in the first commit here. On this PR's
own CI run it halved the suite - **317.7s -> 152.1s** - and broke one
test.
`flow-composition.test.ts` went **26.5s -> 86.8s** and blew the 60s
budget it sets for itself. It pumps a fake clock in a busy loop rather
than sleeping, so it is CPU-bound where the rest of `test/flows` is
idle, and two workers per core starve it superlinearly. Suite-wide,
`import` went 106s -> 210s for the same reason.
Reverted in the second commit. Over-subscription is only a way to hide
the sleeping; once the flow tests stop waiting in real time, one worker
per core is the right number and nothing is starved.
</details>
<details>
<summary>What actually reaches 1 minute</summary>
Vitest parallelises per file and `flow-gesture-settle.test.ts` alone is
86.5s, so bin-packing the real per-file durations gives 161s at 4
workers and 87s at 8, 16, 32 or 64. **No runner size or CI matrix shard
beats ~87s of test time.**
Getting under a minute needs the flow engine's timing made injectable,
with those tests injecting small values. 13 flow files assert absolute
durations and need individual attention.
Two shortcuts do not work:
- Scaling the engine's timing constants 10x down reaches 131s of test
time and 52.9s wall at 4 workers, but is unstable: a rerun of the same
config hung 3 files to ~600s. The fixtures' own values (`readDelayMs =
400`, flow `timeout: 600`, `DEFAULT_LONG_PRESS_MS` 800) do not scale
with it, so the relationships invert.
- `vi.useFakeTimers({ shouldAdvanceTime: true })` passes 37/37 but gives
no speedup - `shouldAdvanceTime` advances fake time 1:1 with real time
by design.
`flow-composition.test.ts` already demonstrates the pattern that does
work: fake clock plus an explicit pump loop.
</details>
Not caching `dist/`: this repo has a history of dist-skew making
assertions pass vacuously, and a stale build artifact is a worse failure
than a slow job.
No docs update needed: CI-internal, no user-facing capability changed.
|
||
|
|
86a2668fb0 |
test: stop the suite and the process environment leaking into each other (#1058)
Test env leakage, both directions. **Out** — six `argent-tools-client` suites pointed `HOME`/`USERPROFILE` at a temp dir, deleted the dir, and never restored, so a `--no-isolate` run handed every later file a home that no longer exists. Lifted the redirect into a shared `test/helpers/home-redirect.ts`; the three suites that already hand-rolled the save/restore moved onto it too. **In** — `packages/telemetry` had no `setupFiles` at all, so its unit tests read the developer's shell. Added `test/setup/clear-telemetry-env.ts` (ARGENT_ prefix sweep + `DO_NOT_TRACK` + the cloud-agent detector's markers), wired via `setupFiles`, following the `@argent/tool-server` precedent — and dropped the per-file guards it replaces. **Pinned, not just fixed** — a `test/setup/assert-env-restored.ts` in each of the three packages fails any suite that leaves a watched variable changed, so dropping a restore can no longer pass. Each watches `HOME` and `USERPROFILE`, plus `PATH` (`argent-tools-client`, `tool-server`) or `CI` and the five `OTEL_EXPORTER_OTLP_*` (`telemetry`) — the ones the sweep deliberately leaves ambient and individual tests then set by hand. Each of the three `clear-*-env` setup files has a pin test that re-imports it against planted sentinels; `packages/tool-server`'s existing copy gains the mixed-case one it was missing. `packages/tool-server` is in the diff because it has the same two gaps: it originated the prefix sweep (both pre-existing copies were case-sensitive, so they are fixed together) and it has the largest redirect surface of the three — 12 files hand-rolling the redirect plus 17 going through `scopeTempHome` — with no guard. It is already green under one; deleting `scopeTempHome`'s restore turns 17 files red. Two of its suites restored `HOME` by assigning back a saved value that can be `undefined`, writing the string `"undefined"`; the guard caught both on a host with no ambient `HOME`, and they are fixed here. `windows-e2e.yml` runs seven `tool-server` files through the config this branch edits, but neither that config nor `test/setup/**` was in its `paths:` filter — so the new setup file would have reached windows-latest unverified. Added both. No docs change: test infrastructure only, no user-facing behaviour. Fixes #1028 Fixes #839 Same-class problems found while reviewing this and filed rather than folded in: #1068 (`argent-cli` fails under `DO_NOT_TRACK=1`), #1069 (the `ios-device-runner` suites pin only `HOME`, so the redirect is inert on Windows — its second half, the restore that wrote the string `"undefined"`, is fixed here because the new guard caught it), #1070 (`useTempHome` shares one saved-`HOME` slot, so a nested `scopeHome()` restores a deleted dir), #1071 (`tool-server`'s sweep is prefix-only, so an exported `ADB_SERVER_SOCKET` reddens its suite), #1072 (`external-devices.test.ts` reads the developer's real `~/.argent/providers`) and #1073 (`flow-script-environment.test.ts` fails with no ambient `HOME`). <details> <summary>#1028 — probe results, six files</summary> `test/zz-home-probe.test.ts` from the issue body, run per suite. Vitest's sequencer orders by cached duration and falls back to file size, so the cache is cleared before each run to keep the tiny probe last: ``` rm -rf node_modules/.vite/vitest ../../node_modules/.vite/vitest npx vitest run test/<suite>.test.ts test/zz-home-probe.test.ts --no-isolate --no-file-parallelism ``` | suite | before | after | | --- | --- | --- | | `launcher-state` | 1 failed \| 23 passed — `argent-state-test-… exists=false` | 24 passed | | `launcher-spawn` | 1 failed \| 8 passed — `argent-spawn-test-… exists=false` | 9 passed | | `launcher-version-gate` | 1 failed \| 7 passed — `argent-version-gate-test-… exists=false` | 8 passed | | `launcher-duplicate-spawn` | 1 failed \| 10 passed — `argent-dup-test-… exists=false` | 11 passed | | `launcher-exit` | 1 failed \| 1 passed — `argent-exit-test-… exists=false` | 2 passed | | `link-config` | 1 failed \| 43 passed — `argent-link-config-test-… exists=false` | 44 passed | | `launcher-sweep` (control, fixed in #713) | passed | passed | | `launcher-ps-guard` (control, fixed in #713) | passed | passed | The probe itself is a diagnostic and is not part of the diff — `test/setup/assert-env-restored.ts` is the durable guard, and it needs no `--no-isolate` to fire. </details> <details> <summary>#839 — the stated repro is stale; here is what is actually left</summary> The issue was filed 2026-08-18. #714 landed 2026-08-19 and hand-enumerated the variables inside `index.test.ts` (a `CONSENT_ENV_KEYS` `beforeEach`) and `base-props.test.ts` (eight `delete`s). On `aa90873b5` all three named repros are green: ``` $ DO_NOT_TRACK=1 npx vitest run --root packages/telemetry → 319 passed $ ARGENT_TELEMETRY=0 npx vitest run --root packages/telemetry → 319 passed $ CURSOR_AGENT_WORKER_ID=x npx vitest run --root packages/telemetry → 319 passed ``` What remains is the structural gap the issue names: the protection is per-file and per-variable, nothing pins it, and `vitest.config.ts` still has no `setupFiles`. This PR moves the property to the setup file and deletes the two hand-rolled guards, so the setup file is what holds it — see the mutation section, where removing the `setupFiles` line reproduces the issue's 17 and 1 exactly. Post-change sweep, one hostile value per run, 329 tests each: `DO_NOT_TRACK=1`, `ARGENT_TELEMETRY=0`, `ARGENT_TELEMETRY_DEBUG=1`, `CLAUDE_CODE_ENVIRONMENT_KIND=byoc`, `CLAUDE_CODE_ENTRYPOINT=remote`, `CLAUDE_CODE_REMOTE_SESSION_ID=x`, `CURSOR_AGENT_WORKER_ID=x`, `CURSOR_WORKER_POOL_NAME=x`, `GITHUB_ACTIONS=true`, `GITHUB_ACTOR=copilot`, `GITHUB_WORKFLOW_REF=…copilot-swe-agent.yml@refs/heads/main`, `REPLIT_AGENT=1`, `ARGENT_HOST`/`ARGENT_PORT`/`ARGENT_AUTH_TOKEN`, `CI=1`, `CI=false`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_HEADERS`, the real CI combination, and all eleven at once — 329 passed every time. `GITHUB_ACTIONS` is a ci-info vendor input as well as a copilot-agent marker, so clearing it does move `isCi()` — but only its GitHub Actions branch, and an Actions run also sets `CI`, which `isCi()` answers ahead of any vendor check. `GITHUB_ACTOR` and `GITHUB_WORKFLOW_REF` appear in neither `ci-info/vendors.json` nor `ci-detect.ts`'s generic list, so they cost nothing. The rest of the ci-info surface and `OTEL_*` are deliberately left alone; the setup file says so. </details> <details> <summary>Mutation testing — every guard, applied by hand, verified to fail, reverted</summary> | mutation | result | | --- | --- | | drop `setupFiles` from `packages/telemetry/vitest.config.ts` | `DO_NOT_TRACK=1` → 19 failed (**17 in `index.test.ts`**, matching the issue, + the two registration pins) | | drop `CURSOR_WORKER_POOL_NAME` from `CLEARED_ENV_VARS` | drift test red | | move an `env.` read into `src/detect/vendor.ts` | drift test red, naming `detect/vendor.ts` | | rewrite a read as `env["X"]`, `const { X } = env`, `const { X }: NodeJS.ProcessEnv = env` or `const { X = "d" } = env` | drift test red | | add `cloud-agent-detect.ts` to `UNSWEPT_SRC_FILES` | drift test red | | revert `toUpperCase()` in any of the three sweeps | that package's mixed-case case red | | drop `restoreHome()` from one suite / from all nine | 1 file / 9 files red, each naming its deleted dir | | drop `scopeTempHome()`'s restore in `tool-server` | 17 files red | | drop the hand-rolled `CI` restore in `base-props.test.ts` | red, naming that file (green before; under `--no-isolate`, 7 unrelated failures in `otel-endpoint.test.ts`) | | narrow either guard's filter to variables that were already set | `assert-env-restored.test.ts` red | | replace the leaked half of the guard's message with a constant | `assert-env-restored.test.ts` red | | rename one function's `env` parameter in `cloud-agent-detect.ts` | drift test red — the read is found under the new binding | | narrow any guard's filter to a subset of `SCOPED_ENV_VARS` | `assert-env-restored.test.ts` red — the pin plants every entry | | drop the hand-rolled OTLP restore in `otel-endpoint-live.test.ts` | red, naming all four leaked variables | | move the guard's `AMBIENT` snapshot inside `assertEnvRestored` | `assert-env-restored.test.ts` red in all three packages | | delete the guard's `afterAll(...)` registration | `assert-env-restored.test.ts` red in all three packages | | drop `restoreHome()` from `artifacts.test.ts`'s two `afterEach` | red — the file-level guard catches a per-test omission | | drop ps-guard's `PATH` restore | red, naming the file | | empty `leakedEnvVars`, or drop a name from `SCOPED_ENV_VARS` | `assert-env-restored.test.ts` red | | replace the helper's delete branch with a plain assignment | `home-redirect.test.ts` red | | drop `USERPROFILE` from the helper | `home-redirect.test.ts` red | </details> <details> <summary>Local CI parity</summary> ``` npx vitest run --root packages/argent-tools-client 213 passed | 1 skipped npx vitest run --root packages/argent-tools-client --no-isolate 213 passed | 1 skipped npx vitest run --root packages/telemetry 329 passed npx vitest run --root packages/telemetry --no-isolate 329 passed npx vitest run --root packages/tool-server 6166 passed | 1 skipped npx tsc --noEmit -p packages/{argent-tools-client,telemetry,tool-server}/tsconfig.test.json clean npm run format / npm run lint / npm run knip clean ``` </details> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Tests** - Improved test isolation by consistently restoring temporary environment settings after test runs. - Added safeguards that detect leaked environment settings and flag incomplete cleanup. - Added coverage to ensure telemetry-related environment variables are cleared before tests and do not affect results. - Added cross-platform checks for environment variable handling, including mixed-case names. - Expanded validation for home-directory redirection and restoration behavior. - Added checks confirming environment cleanup runs in the correct order across test suites. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9c4bc9ef5b |
feat(flows): run an external script as a flow step (#865)
Stacked on #864, which adds the executor. This branch adds the `script:` step itself: the YAML directive, the runner integration, the CLI and MCP rendering, and the authoring reference. ```yaml - script: { path: ../../scripts/seed-order.mjs } - script: { path: ../../scripts/seed-order.mjs, timeout: 60000 } ``` A `script:` step runs an external `.mjs` file from the project, in a fresh Node process. What the script does is its own business — seeding an order, creating a test account, cleaning up what a run left behind are the reasons flows have wanted one. It drives no device, so a flow of nothing but scripts runs with nothing booted. ## What it adds - **The directive.** The value is always a map; `path` is required and obeys the same name rules a `run:` target does, spelled for `.mjs`. It resolves against the directory of the flow file containing the step, so a fragment reaches the same script whichever flow composed it. A mis-cased path is refused rather than run, because macOS and Windows would open it and Linux CI would not. - **The report.** The script's stdout and stderr come back on the step and print under the step line, on a pass as well as a failure — it is the only record of what the step did to the backend. Both renderers (CLI, MCP) carry it, live and buffered. - **The fail/error split.** A `fail` is the script's own answer; an `error` is the host's. That is what lets CI tell a regression from the machine it ran on. - **Two boundaries.** An uploaded flow carrying a `script:` step is refused before anything runs — its `.mjs` never left the client. A flow whose script step sits beside a `run:` step still resolves a device. ## Review follow-ups The last three commits answer a review of the three above them. - A launch behind a leading `script:` was not read as the flow's leading launch, so a Chromium flow that seeded a backend first hoisted no boot, bound to whatever browser happened to be up, and passed a launch it never performed. The same lead-in walked a fragment past the `executionPrerequisite` refusal its `echo:`-led twin is given. Both readers now share one exhaustively-switched predicate. - Coverage: the whole failure-kind table, the mis-cased arm CI never ran, the two unreached `scriptFileProblem` arms, symlink resolution for a script path, and the CLI live-progress closure that no test had executed. - Docs: the reference claimed the log was secret-redacted (it is not), never mentioned the environment allowlist, the working directory, the time-limit ceiling or the log caps, stated a fail/error rule that was not the code's, and was missing from its own table of contents. The skills stated a closed list of legal unrecorded insertions that excluded the only way this step can ever be written. ## Verification `npm run build`, `npx eslint . --max-warnings 0`, `npx prettier --check .`, `npm run knip`, `npm run typecheck:scripts` and `npm run test:scripts` are green, as are the three `tsconfig.test.json` typechecks. Suites: tool-server 357 files / 4552 passed, `@argent/cli` 469, `@argent/mcp` 83, `@argent/registry` 90. Reproduced end to end against a real tool server and a real Electron app, not only in tests: a flow of `script:` then `launch:` now boots its own Chromium instance and reports it, where before it attached to a stray browser and reported a green launch for an app path that did not exist. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for local `.mjs` scripts as flow steps, including configurable timeouts, validation, cancellation, and device-free execution. - Script output is displayed in CLI runs and included in MCP flow results, with indentation and truncation notices. - Script failures now provide clearer diagnostics and verdicts. - Flow authoring tools recognize and guide creation of script steps. - **Documentation** - Added guidance for script syntax, execution limits, output handling, classification, and remote-run restrictions. - Updated flow creation and QA guidance to include script steps. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Hubert Gancarczyk <claude-hubert.gancarczyk@swmansion.com> |
||
|
|
1a7e97fb14 |
feat(flows): run a local script in a bounded child process (#864)
Adds the executor a flow `script:` step runs on: a fresh Node child per step, a protocol over its IPC channel, and the two watchdogs that make a hung or runaway script the runner's problem rather than the host's. The step itself is not here. This branch stops at the executor and its unit tests; `feat/flow-script-step` stacks the YAML directive, the runner integration, and the reference docs on top. ## What it does - **One child per step.** Spawned with an old-space heap limit, an explicit working directory, and an environment built from an allowlist rather than copied from the tool server. - **A deadline and a lifeline.** A separate watchdog holds the deadline, so a script that wedges the event loop still dies on time; a second one reaps the whole process tree when the parent goes away, so a grandchild cannot outlive the run. - **A concurrency queue.** Slots are bounded per server. A step that never gets one is refused with a message that says so, and an aborted run frees its slot immediately. - **Log budgets.** 64 KiB per step and 256 KiB per run, counted on the bytes the report keeps: redaction and V8 frame collapsing both run before anything is counted, so what the limits bound is the size of the report rather than the size of the script's writes. - **Secret hold-back.** The scrub walks a chunk and stops where a value could still begin, so neither half of a value split across two chunks is released on its own, and a shorter value is never taken where the longer one containing it has not arrived yet. - **A failure taxonomy.** Twelve kinds, split into what the script did (it threw, it did not load, it exited non-zero, it wrote an unusable `output`) and what the host did to it (a limit, a signal, a spawn that failed, a queue slot it never got). ## Docs `packages/docs/docs/reference/configuration.mdx` lists the two configuration keys this branch adds, `scripts.maxTimeoutMs` and `scripts.heapLimitMb`. The `script:` step itself is documented with the step, on `feat/flow-script-step`. ## Verification `npm run build`, `npx eslint . --max-warnings 0`, `npx prettier --check .`, `npm run knip`, `npm run typecheck:scripts`, the test typecheck across every workspace, the tool-server suite (4719 passed, 1 skipped) and `npm run test:scripts` (92 tests) are green, as is `npx docusaurus build`. The executor's own behaviour is covered by the eight `test/flows/script/` files added here, against real child processes, and the compiled `dist/` executor was driven against its copied runner assets over every path the review reached. Each review fix carries the run that reproduced it before the change and the mutation that proves the new test fails without it: the prefix-secret leak and all three npm `node-options` routes were reproduced end to end through the executor against real child processes, and the five coverage findings were confirmed by re-applying the exact mutation each thread named. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added reliable execution of trusted flow scripts with concurrency control, cancellation, timeouts, memory limits, logging, and detailed failure reporting. * Added safeguards for process cleanup, watchdog termination, output validation, and secret redaction. * Added global configuration for maximum script runtime and memory usage. * **Bug Fixes** * Improved handling of script failures, stalled processes, malformed output, and child-process termination. * **Documentation** * Documented script resource limits and global configuration behavior. * **Chores** * Updated builds and packaging to include required flow-script runtime assets. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Hubert Gancarczyk <claude-hubert.gancarczyk@swmansion.com> |
||
|
|
1e69c90981 |
feat(ios-device): add physical iOS support (#944)
# Physical iOS device support Try it now (cabled iPhone, unlocked, Developer Mode on): ```bash argent run list-devices # copy the 25-char UDID argent run launch-app --udid <UDID> --bundleId com.apple.Preferences ``` First launch builds and signs the on-device runner: 3 to 5 minutes, keep the phone unlocked. Every later start: seconds, from cache. No signing setup needed if an Apple ID is signed into Xcode; the team is detected from the keychain. ## What this adds - Physical iPhone support, on by default: a cabled iPhone simply appears in `list-devices`. iPhone-only: physical iPads are skipped at discovery. - 15 tools ported to hardware. Flows replay on device. - Zero-config signing from the keychain; `ARGENT_IOS_TEAM_ID` is an override, nothing is persisted. - Self-contained npm install: the runner's Swift sources ship in the package. New package `@argent/ios-device-runner` (an XCUITest bundle hosting an HTTP command server on the device), new tool-server backend under `src/utils/ios-device/`. - The one always-on cost: `list-devices` on macOS now runs physical discovery in parallel with the other platform probes, under the existing 8s devicectl timeout; off-macOS it early-returns. ## Suggested review order Rough estimate: half a day for the full pass. 1. `packages/ios-device-runner/PROTOCOL.md`, then `README.md` (wire contract and reliability model, ~15 min) 2. `packages/tool-server/src/utils/ios-device/` (devicectl, usbmux, team-detect, runner-build, runner-commands: the core) 3. `packages/tool-server/src/blueprints/ios-device-runner.ts` (service lifecycle, crash recovery) 4. Per-tool device arms and the capability gates (`ios-physical-device-classification.test.ts` is the ratchet) 5. Docs page, the two new skills, the Swift runner sources ## Requirements 1. macOS host with full Xcode (iOS platform installed); uses `xcrun devicectl` and `xcodebuild`. 2. An Apple ID signed into Xcode; automatic signing mints the runner's provisioning profile. 3. USB cable only. Wi-Fi is deliberately unsupported: the transport is usbmux, and a Wi-Fi-only device lists as `paired`, not `connected`. 4. iPhone paired with and trusting the Mac, Developer Mode on, A12/2018 or newer (modern 8+16-hex UDID, the CoreDevice floor). Legacy 40-hex UDIDs are not classified as iOS. 5. Signing resolves as: `ARGENT_IOS_TEAM_ID` in the tool-server's environment (explicit override, CI) -> keychain detection (newest identity first, memoized per server run, never persisted). One certificate: nothing to do. Several teams: newest wins; the first tool result names the pick, the runner-ups, and the override. No certificate: the error walks through Xcode > Settings > Accounts and Manage Certificates > + > Apple Development, then retry; it never names the env var, since a team id alone cannot sign. Bundle ids derive per team (`com.argent.runner.t<teamid>`). `ARGENT_IOS_RUNNER_PROJECT` overrides the project path for unusual layouts. Build artifacts cache under `~/.argent/ios-device-runner`, keyed by runner sources, Xcode/SDK version, and signing. A team with no registered devices self-heals: the failed generic build retries against the connected phone, which registers it. ## How it works 1. `xcrun devicectl` (CoreDevice) is the control plane: discovery, readiness, app lifecycle. Readiness gates on the wired transport, so an unplugged phone is refused before any build starts. 2. A signed XCUITest runner is built lazily with the resolved team, started via `xcodebuild test-without-building`, and parks as an HTTP command server for 24h. On an npm install the Xcode project is the copy shipped inside the package. 3. The listener binds to the device's loopback interface; the host reaches it over usbmuxd with a dependency-free `node:net` client. 4. Mutating commands are journaled by `commandId`; a lost reply is resolved by asking `status` for the journaled fate, never by replay (at-most-once delivery). 5. Routing hangs off `resolveDevice` UDID-shape classification plus a per-tool capability allowlist. CLI and MCP layers are untouched; outside tool-server, docs, and the runner package the diff is failure-code constants (registry), two agent skills plus rule updates (skills), the pack step (packages/argent), and the e2e smoke script. ## Supported tools | Tool | Status | Notes | | ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | launch-app | Supported | Plain `devicectl` launch. System UI (SpringBoard/Spotlight) registers a session only; no launch attempt. Carries a note when signing is not resolvable yet. | | restart-app | Supported | System UI bundle ids rejected before any device contact. | | reinstall-app | Supported | Same system UI pre-flight. | | screenshot | Supported | Served through the runner; device-wide, needs no registered app. (No host-side devicectl screenshot exists on any toolchain; that dead route was removed.) | | describe | Supported | AX tree snapshot via the runner. Snapshots the session app's process, so another process's dialogs are not in the tree. A backgrounded session app fails with an actionable error instead of being silently re-fronted. Focus polling is off for this source; typing uses a fixed settle. | | keyboard | Supported | Types into the focused element; typing with nothing focused is refused with "tap the text field first". `enter` and `backspace` are the named keys (both exist as software-keyboard characters; there is no per-keycode HID surface on hardware). Whitespace-only keys rejected, not silently "typed". | | gesture-tap | Supported | `clickCount > 1` dispatches as one native multi-tap gesture, not separate taps. | | gesture-swipe | Supported | Single-finger drag between two points. | | gesture-custom | Supported | Two shapes only: press-hold and straight single-finger drag. Second finger or `Move` waypoints rejected with authoring guidance, before any runner starts. | | button | Supported | `home`, `volumeUp`, `volumeDown`, `actionButton` (volume buttons are simulator-unavailable in the SDK, not device-unavailable). `power`/`appSwitch` have no public XCUIDevice API and reject before device contact; a button the model lacks gets a typed refusal via `hasHardwareButton`. | | await-ui-element | Supported | Polls the same runner snapshot describe returns; waits and taps see identical frames. Degrades to `cause: "unreadable"` with a note on a backgrounded app, never a raw throw. | | await-screen-idle | Supported | Same describe path; resolves not-settled instead of throwing, so a follow-up screenshot still runs. | | run-sequence | Supported | Outer gate only; each step pre-flights its own capability. | | open-url | Supported | devicectl launches the receiving app with the URL as its launch payload. http(s) defaults to Safari; any other scheme needs the new optional `bundleId` param (hardware cannot resolve a scheme's handler system-wide). | | screenshot-diff | Supported | Live captures through the runner (full-resolution, device-wide, no registered app needed); the diff itself runs on the host. Baselines are per device model/resolution; `rotation` is ignored on hardware. | Flows run through these tools; the device flow tree gets the same `subtreeText` hoisting as every other platform, so container-scoped `text` asserts work. ## Not supported (400 at the capability gate) 1. gesture-pinch, gesture-rotate: no two-finger coordinate API on hardware. Pinch/rotate flow directives also fail upfront with a clear error. 2. screen-recording-start/stop (video): simulator-only capture pipeline. 3. rotate, paste, shake, settings-permissions: simulator-only backends, not ported (paste also hits the iOS 16+ cross-app paste permission prompt). 4. native-devtools, debugger, profiler families: simulator-side instrumentation (DYLD injection does not exist on hardware). 5. Physical iPads: filtered at discovery for now. A test ratchet (`ios-physical-device-classification.test.ts`) keeps `apple.device` declarations in sync with the ported list. ## Core contracts 1. Observation never mutates the screen: a backgrounded session app makes `describe`/`await-*` fail with an error naming the three ways out (screenshot, launch-app, or launch-app com.apple.springboard). Mutating commands do re-front, and their result carries `reactivated: true`. 2. The runner listener is unauthenticated; mitigation is the loopback-only bind. 3. `list-devices` states: `connected` (cabled now, usable) vs `paired` (unreachable, never auto-bound). Wired transport alone decides; a Wi-Fi-migrated CoreDevice tunnel does not count. 4. The multi-team signing pick rides the first affected tool result as a one-shot note (the pre-existing notes channel). The `list-devices` result shape is unchanged from main: devices and avds, nothing else. 5. Rejections are typed 400 validation errors end to end, so a batch flow run skips one failing flow instead of aborting. Also: the runner is a first-class device-owned service (`stop-simulator-server` and `stop-all-simulator-servers` reap it, detached xcodebuild child included; a crashed runner respawns on the next call). A passcode-locked phone refuses launches; the devicectl 10002 hint also covers a pending system prompt on an unlocked phone. Build failures surface xcodebuild's own `error:` lines with signing hints (no registered devices, free-team app-id cap, missing provisioning, the `errSecInternalComponent` partition-list fix). An app XCTest reports as `.unknown` (a swipe-killed foreign app) is refused with launch-app guidance. Status-bar pinning is skipped on hardware; pixel-settle masks the top band. ## Testing - Unit coverage, hardware-independent: UDID classification and capability routing; usbmux framing and transport; devicectl list/hints parsing incl. the silent iPhone-only filter; keychain detection against real certificate fixtures; runner build/cache self-heals (poisoned cache, concurrent checkout, fresh-team retry); crash/journal recovery; the backgrounded-observation and reactivated contracts; keyboard guards incl. the not-focused refusal; system-UI pre-flight; per-tool device arms. - Manual smoke on a real device: `scripts/e2e-ios-physical-device.mjs`. ## Docs and skills 1. `features/physical-ios-devices.mdx`: the user-facing walkthrough (zero-config signing with the sign-in fallback, prerequisites, app-scoped model, troubleshooting). 2. `fundamentals/supported-platforms.mdx`: physical iPhones in the platform matrix (iPads not supported yet). 3. `reference/tools.mdx` and `reference/configuration.mdx`: list-devices states and the `ARGENT_IOS_TEAM_ID` override. 4. Two skills ship with the package via `argent init`: `argent-ios-device-setup` (setup ladder plus a troubleshooting table keyed to real error texts) and `argent-ios-device-interact` (the app-scoped hardware contract, supported subset with alternatives, gesture recipes, pitfalls). The agent rule and sibling skills route to them. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added experimental USB-connected physical iPhone support. * Physical iPhones now support discovery, app lifecycle actions, URL opening, accessibility snapshots, screenshots, taps, swipes, custom gestures, keyboard input, waits, flows, and screenshot comparisons. * Added automatic signing-team detection with optional `ARGENT_IOS_TEAM_ID` configuration. * Added clear handling for paired or unavailable devices and unsupported hardware actions. * **Documentation** * Added setup, troubleshooting, capability, and physical-device usage guidance. * Clarified that physical iPads and several simulator-only tools and gestures remain unsupported. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2740a3ec38 |
chore(knip): clear the parked dead-code backlog and drop the ceiling to 0 (#770)
> Built on top of #769, so this PR carries that commit too — the workflows only run for PRs targeting `main`, which is why it is based there rather than on the branch. #769 on its own is the minimal, low-risk fix for the currently-red job; if it merges first this shrinks to its own commit, and if this one merges first it closes #769 as well. ## What this does #572 landed the gate with 215 findings parked under `--max-issues`. That bounded the backlog but left it in place, and the first thing to touch it (#663) pushed the total to 218 and turned the job red. This clears the backlog and sets the ceiling to **0**, so the next unused export fails the job instead of consuming headroom nobody knew was there. | | before | after | | --- | ---: | ---: | | Unused exports | 47 | 0 | | Unused exported types | 156 | 0 | | Unused exported class members | 12 | 0 | | `--max-issues` | 215 | **0** | ## Scope check first Every workspace holding a finding is `private: true`. `@swmansion/argent` — the one published package — had **zero**, so nothing here moves a public API. ## Two shapes of fix **Still referenced inside its own file → drop `export`** (146 types, 36 exports). The declaration stays put; it just leaves the module's public surface. `tsc` keeps emitting these into the `.d.ts` as local declarations that the exported signatures reference. For the 146 **types** that is the whole story: they are erased, so the emitted `.js` is byte-identical. For the 36 **values** it is not. Dropping `export` from a `const`/`function`/`class` removes its `exports.X` binding from the emitted `dist/*.js` — 36 declarations across 26 files here. `packages/tool-server/dist/utils/device-info.js` no longer carries `exports.REMOTE_PREFIX` or `exports.VEGA_SERIAL_PREFIX`; `telemetry/dist/posthog.js` loses `exports.POSTHOG_PROJECT_TOKEN`; in the ESM-emitting `argent-installer`, `isTempRunnerPath` / `resolveLocalArgentDir` / `PACKAGE_ROOT` leave the module namespace object entirely. Nothing in the repo or in the `packages/argent-private` submodule at the pinned revision reads any of them by path, so nothing breaks. Flagging it because a `require()` of a built `dist/*.js` loses a dropped `export` exactly the way it lost the deleted `__resetAndroidDevtoolsInstallCache` this branch had to restore — so this half of the diff does deserve runtime scrutiny, not a skim. **Referenced nowhere → delete.** Orphaned test seams (`__test`, `_testValidators`), stale compatibility re-exports, and a few real orphans. The ones worth a reviewer's eye: - **`localSimctl`** — the `SimctlBackend` strategy is only ever constructed with `remoteSimctl`. What keeps the local iOS impl off it is the handler signature: `buildIosLaunchHandler` / `buildIosRestartHandler` read `services.nativeDevtools`, and `ios-remote` is the only platform that declares that service, so the local impl — which resolves native-devtools per device inside its own handler — has nothing to hand them and shells out to `xcrun` itself. - **`sendCharInsert`** — removing the `__sendCharInsert` test alias exposed the underlying helper as unreferenced. It is superseded, not unwired: the Chromium keyboard impl dispatches its own `type: "char"` event inline (`keyboard/platforms/chromium.ts:58`), which is exactly what this helper did. Chromium typing is unaffected. - **Error metadata fields** — `ServiceNotFoundError.serviceId`, `ToolNotFoundError.toolId` and their two siblings were never read. The `e.serviceId` in `registry-error-events.test.ts` is a field on the event object the handler pushes, not on the error class; the `err.toolId` in `http.ts` is `NotImplementedOnPlatformError`, a different class that keeps its fields. Flagging these in case they are wanted as deliberate diagnostics. Deleting `SourceMapsRegistry.toGeneratedPosition` / `findMatchingSource` cascaded into their only helper (`buildSourceCandidates`) and then into `projectRoot`, which had no remaining reader — so the constructor parameter goes too. `StubSourceMapsRegistry` existed only to pass `super("")`, so it goes with it. ## The three cross-workspace members `ArtifactStore.register`, `ArtifactStore.list` and `TypedEventEmitter.off` are all called from other workspaces — the artifact route and screenshot tools in `tool-server`, and `registry-listener` in `telemetry`. They are reported unused anyway, and the reason is the **unbuilt tree** the gate runs against: every workspace resolves `main`/`types` to a `dist/` that does not exist, so `@argent/registry` resolves to nothing and the cross-workspace edge never forms. Build the tree and all three findings disappear on their own; a control member that really is dead is still reported in the built run, so the pass is live either way. Nothing about this is specific to `classMembers` — every cross-workspace reference is invisible the same way. Each is exempted at the declaration with a `@public` JSDoc tag rather than by name in `knip.jsonc`, for one reason: **scope**. `ignoreMembers` matches a name across the whole workspace, so it would also hide a future dead `register`/`list`/`off` on any other class in `packages/registry`; `@public` binds to the member it is written on. What `@public` does **not** buy is a staleness check, and neither does `ignoreMembers`. Measured on a clean unbuilt worktree: a bogus `ignoreMembers` name and a redundant `@public` each leave the run at exit 0 with no hint, while a bogus `ignoreDependencies` name exits 1 with `Remove from ignoreDependencies`. `treatConfigHintsAsErrors` is live and simply has no producer for either exemption style — so when one of the three stops being earned, nothing says so, and the JSDoc at each member is the only record a re-audit has. Each one names its callers, and `off` records how to re-derive them (rename the member and read the `Property 'off' does not exist` errors; grepping `.off(` over-reports, since 14 of the 38 call-site hits are Node `EventEmitter`s). One correction to the commit message on that change: it says 35 hits where the exact count is 38 — 14 Node `EventEmitter`, 24 `TypedEventEmitter` (21 production, 3 in a test). The 14 and the 21 are right. `knip.jsonc`, `CONTRIBUTING.md` step 5 and the workflow's failure-explanation step all described a parked backlog and a ceiling to stay under. All three are rewritten for a gate that must simply come back empty, and all three now name the `@public` escape hatch — the remedy for a symbol whose only caller is out of knip's reach. ## Verification Run against a clean worktree with **no build output**, the way CI counts: - `npm run knip` — **exit 0, prints nothing**. Both passes empty, no config hints. - `npx tsc --build` — clean. - `npm run lint` (`eslint . --max-warnings 0`) — clean. - `npx prettier --check .` — clean. - `npm test --workspaces` — tool-server 329 files / 3857 passed; registry, telemetry, update-core, tools-client, mcp, cli green. - `npm run typecheck:tests --workspaces` — 13 workspaces define the script and all 13 are clean, but **the command itself exits 1**: npm errors `Missing script: "typecheck:tests"` for the three that do not define it rather than skipping them. Worth knowing before re-running this bullet and reading the exit code as a failure. End-to-end on a tool-server built from this branch, driven over its HTTP API: - **Chromium** (Electron smoke app) — `describe`, `screenshot`, `gesture-tap` (counter advanced 0 → 1), `POST /api/clipboard/text` read back out of the renderer, the WS `clipboardSync` command, and `debugger-connect` / `debugger-status` / `debugger-evaluate` (`sourceMapReady: true` with `StubSourceMapsRegistry` gone). - **iOS** — `boot-device`, `launch-app`, `describe` via ax-service, `screenshot`. - **tvOS** — `boot-device`, `launch-app` on an Apple TV udid, focus-driven `describe`. The registry snapshot then carries `NativeDevtools:<tvOS udid>`, confirming injection **is** resolved on tvOS through the local impl. - **Android** — `boot-device` on a dedicated AVD, `describe` via android-devtools, `gesture-tap`. - **Metro source maps** — the registry driven against a real 12.5 MB Metro `.map` over loopback, the way `Debugger.scriptParsed` drives it: allowlist passes and `waitForPending()` resolves on both, while retained heap after gc drops from 17.3 MB to 3.6 MB. ## Review follow-ups Five commits on top, one per finding. The base is back on `main`, so the workflows run again. - **`docs(launch-app)`** — the services comment explained the empty shape through `LaunchAppAndroidServices`, deleted here, and skipped `LaunchAppVegaServices`, which is still there. Same edit in `restart-app/types.ts`. - **`docs(registry)`** — the `@public` tag on `ArtifactStore.register` named 2 of 7 callers. Derived the full set the way the sibling tag on `off` prescribes: renamed the member, read the 16 `TS2339` errors `tsc --build` reports across flow-visual (5), screenshot (4), native-profiler-stop (2), screenshot-diff (2), native-profiler-analyze, react-profiler-analyze and screen-recording-stop. The re-derivation recipe is recorded too — `.register(` greps as badly as `.off(`. - **`docs(knip)`** — "the build, the tests and this gate all stay green on a wrong delete" held for argent-private and not for the cross-workspace case that justifies three of the four `@public` tags. Deleting `ArtifactStore.register` gives 16 `TS2339` errors and `9 failed | 2 passed` in `test/artifacts.test.ts`; only the gate stays green. Split in the workflow comment, the failure annotation and `CONTRIBUTING.md` step 5. - **`perf(debugger)`** — `doRegister`'s `data:` branch still base64-decoded and `JSON.parse`d a payload it dropped, inside a `catch` that swallows. Both arms returned the same `void`, so the branch collapses to an early return; `scriptUrl` / `scriptId` go with it, since their last reader was the deleted `this.maps.push(...)` and `doRegister` is private. - **`test(debugger)`** — the allowlist check moved to a top-level early return with nothing pinning it: delete the line and both SSRF files still pass 17/17. Two tests now assert `doRegister` consults it — four rejected URLs reach no `fetch`, and the loopback `*.map` Metro emits still does. Re-verified after: `npm run knip` exit 0 and empty on a `tsc --build --clean` tree, `tsc --build`, `eslint . --max-warnings 0`, `prettier --check .` all clean, and `npm test --workspaces` with tool-server at 329 files / 3854 passed. The one red test, `argent-installer`'s `globalPath returns ~/.config/opencode/opencode.json`, fails identically with these commits stashed — it reads the real `~/.config/opencode`, which holds a `.jsonc` on this machine. End-to-end on a tool-server run from this branch over its HTTP API, on every platform: - **Chromium** — `launch-app`, `describe`, `screenshot` (returned an artifact handle, `GET /artifacts/<id>` served the 137542-byte PNG back), `gesture-tap` (counter 0 → 1), `debugger-connect` / `debugger-status` (`sourceMapReady: true`) / `debugger-evaluate` (read `taps: 1` out of the page). - **iOS** — `launch-app`, `describe` via native-devtools, `restart-app`, `screenshot`. - **tvOS** — `launch-app`, focus-driven `describe` (12 focusables in TVSettings), `tv-remote`, `restart-app`. - **Android** — `launch-app`, `describe` via android-devtools, `restart-app`, `screenshot`. - **Vega** — `boot-device` on the `tv` VVD, `describe` via the automation toolkit. - **Metro** — the real `debugger-connect` / `debugger-status` / `debugger-evaluate` tools against a stand-in Metro (HTTP `/json/list` + CDP WebSocket) emitting three `Debugger.scriptParsed` events: a loopback `*.map` on a counting server, a 1.4 MB inline `data:` map, and `http://169.254.169.254/latest.map`. `sourceMapReady: true`, `loadedScripts: 3`, evaluate 42, the loopback map fetched exactly once and the metadata URL never. A malformed inline payload gives byte-identical output, which is the finding. ### Merge with `main` Putting the base back on `main` made CI resolve the merge, and the Unit Tests job went red on a conflict no side could see on its own: `#771` fixed the same red gate this branch's first commit did, the other way round — it made `component-names.test.ts` import `StrippedName`, `ComponentAnnotation` and `ComponentNameResolution` instead of dropping their `export`. Merged `main` in, reproduced it (`tsc --noEmit -p tsconfig.test.json` → three `TS2459`s) and took main's resolution: the three exports are back. The gate stays green because knip treats test files as entry points, so a type a test imports is not an unused export. All nine workflows are green on the head commit. |
||
|
|
b232114422 |
docs: audit every comment in src against the code it describes (#931)
Audits every comment in `src/` and `scripts/` against the code it
describes.
**462 files, 57 commits, net −8,194 lines.** Comment text only — the
whole branch is code-identical to `main`.
## Method
One subagent per file, strictly sequential. Scope was `src/` +
`scripts/` (459 files); three more files were added at the end because
they carried dead references of the same kind — two test headers citing
design docs that do not exist, and `publish-npm.yml` citing a retired
workflow. Each agent verified every comment — line, block, JSDoc, file
header, trailing — against the surrounding code and the rest of the
repo, following identifiers, paths, tool ids, config keys, env vars and
issue links to see whether they still exist and still behave as
described. Rules:
- **A false or misleading comment is deleted, not reworded.** If a claim
could not be confirmed by reading the source, it went. That is why the
deletion count is so much larger than the rewrite count.
- Survivors are cut to the shortest form carrying something the code
does not already say. Restatement, preamble, hedging, changelog prose
and ASCII banners are gone; the non-obvious *why* stays.
- Preserved byte-identical: license headers, pragmas and directives
(`@ts-*`, `eslint-disable`, shebangs, `/// <reference>`), JSDoc tag
tokens, everything inside a string or template literal, and the sole
comment inside an otherwise empty block (ESLint `no-empty` counts a
comment-bearing block as non-empty).
## Verification
Every file passed two independent gates before being recorded as done:
1. `comments-only` — the required check.
2. A second comment-stripping comparator with a proper mode stack,
written for this pass because `comments-only`'s flat scanner desyncs on
nested template literals and quote-bearing regex literals and then
reports comment lines as code changes. Two files hit that false FAIL
(`utils/android-profiler/pipeline/index.ts`,
`scripts/extract-tools.mjs`); in both the "changed code" it printed was
literally `//` lines, and the second checker confirmed the code was
byte-identical.
After the last file, all 462 changed files were re-checked against
`main` with the same comparator, rather than trusting any agent's
self-report. **459 code-identical; 2 are non-code (`.svg`, `.md`); 1
intentional.**
The intentional one is `packages/argent/scripts/bundle-tools.cjs`: the
changed template literal *is* the comment header of the file it
generates, `packages/native-devtools-android/src/bundled-meta.ts`.
Fixing only the generated file would have been reverted by the next
build, so the generator changed too — and it has been verified to
reproduce the committed generated file byte-for-byte.
## Representative false claims removed
Not wording nits — statements a reader would have acted on:
- **Reversed directions.** `proxyStart`'s JSDoc had the tunnel backwards
(it is a reverse tunnel: the host binds first and the simulator dials
in). A `paste()` doc had the pasteboard copy direction reversed.
- **Contradicted by the code below it.** A timeout budget multiplied by
three where the probes run concurrently — the same comment said so six
lines later. A "warn once" that warns on every call. A "binary search"
that is a linear scan.
- **Named things that do not exist.** A `vega-fast-cli` binary, a
`finish-recording.ts`, a `publish-next.yml` workflow, two
`profiler-react19-*.md` design docs, a `DebuggerTarget.ts`, a commit
hash git does not know, two tool ids, an `ensureEnv` cycle.
- **Wrong by construction.** "Welford accumulators" across four files
where the code keeps naive `n`/`sum`/`sumSq`; `sum`/`sumSq` documented
over `actualDuration` when reduce sums `selfDuration`; a strict-mode
halving written `n/2` where the code ceils; field docs listing enum
values the producers never emit.
- **Guarantees the code does not make.** A validation matrix claiming to
cover "EVERY tool" that skips flagless ones; a Pareto cutoff that
`slice(0, 20)` makes inert; an idempotence claim where the real rule is
at-or-ahead; a capability note describing a clean 400 the shape-based
device resolver can never produce.
- **Unverifiable assertions** about prebuilt binaries, external CLIs and
the cloud SDK — deleted rather than kept as folklore, since nothing in
the repo can confirm them.
- **Stale numbers**: invented Android tool versions, hard-coded tool
counts and description lengths that had drifted.
## Review
A Fable agent reviewed both halves adversarially for over-deletion,
misread code, `no-empty` hazards and byte-identity violations.
Second-half verdict: **SHIP**, with two one-line restores, both applied
in the final commit — the `npm view ""` rationale behind a blank-token
guard, and the note that `argent-mcp` keeps a copy of
`SECRET_PLACEHOLDER_MARKER` it cannot import.
## Code issues surfaced but deliberately not fixed
This pass changes comments only. Eight genuine findings are logged for a
follow-up:
1. `telemetry/src/consent.ts` — a non-ENOENT read error returns null and
falls through to the default-on path, so file errors *can* silently flip
telemetry on.
2. `chromium-server/navigation.ts` — `navigate()` is reachable from
`POST /api/navigate` with only a `typeof === "string"` check; open-url's
schema is a bare `z.string()`, so the "already validated by zod" premise
never held.
3. `http.ts` — `constantTimeEqual` returns early on a length mismatch,
so the auth token's length is observable.
4. `describe/index.ts:~114` — the ios-remote branch passes `{ isTvOs:
false }` unconditionally, so a remote tvOS simulator takes the iOS
ax-service path, though `isRemoteTvOsSimulator` exists and shake/paste
do use it.
5. `devices/boot-device.ts` — `-crash-report-mode never` is passed
unconditionally *and* appended again by the feature-detecting path, so
every emulator spawn passes it twice.
6. `react-profiler/pipeline/04-rank.ts` — `PARETO_THRESHOLD_PCT` is
dead: `slice(0, 20)` always wins.
7. `reaped-sessions.ts` — a user-facing hint string tells the agent that
`react-profiler-start { force: true }` disposes the debugger and
profiler session; it does not. Left byte-identical because it is a
string literal, not a comment.
8. `utils/simctl-backend.ts` — `localSimctl` is exported with no
importers anywhere.
## Docs
No documentation change is needed: this pass touches only source
comments, and no user-facing capability, tool, CLI flag, config key or
flow-file behaviour changed.
---------
Co-authored-by: filip131311 <f.kaminski2000@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
36aa14a5fc |
docs: add Argent documentation (#814)
Adds the Argent documentation site, published to [docs.swmansion.com/argent](https://docs.swmansion.com/argent): a branded Docusaurus project under `packages/docs/`, the full first set of pages, and the CI that checks and deploys it. <img width="1512" height="945" alt="Screenshot 2026-08-21 at 13 05 04" src="https://github.com/user-attachments/assets/4607e143-4359-49a8-93a2-aaa3af4c9534" /> ## Site A standalone Docusaurus 3.9 project, built on the shared Software Mansion docs theme ([`@swmansion/t-rex-ui`](https://www.npmjs.com/package/@swmansion/t-rex-ui)) and restyled for Argent: - **Palette** from [argent.swmansion.com](https://argent.swmansion.com): dark `#0D0F26`, blue `#99DAFF`, lavender `#E4E1FF`, light `#FEFEFE`, mist `#F1F1F1`. The `--swm-*` token names stay, since the shared theme resolves against them. - **Typography**: DM Sans and DM Mono, matching the landing page. - **Sidebar** with lucide icons on every page entry (`sidebar_custom_props.icon` in the front matter, registered in `src/theme/SidebarIcon`), the logo kept on the page background and the panel inset from the edge. - **Video component** for embedded screen recordings, with `scripts/encode-video.sh` producing a web-sized MP4 and a poster frame for each clip in `static/video/`. - **Copy page button** and a few theme fixes: inline code badges in tables, paginator hover. - **No landing page.** That stays at argent.swmansion.com. The root route redirects to Getting started. ## Content Three sections in `docs/`: - **Fundamentals**: getting started, installation, supported platforms. - **Features**: interacting with apps, flows, network, screen recording, lens, visual regression, profiling, debugging. Conceptual overviews with recordings, each linking to the tools reference. - **Reference**: tools, CLI, configuration, flow YAML, editors, telemetry. Prose follows Simplified Technical English. The conventions (style, front matter, icons, checks) are written down in `packages/docs/CLAUDE.md`, and a root `CLAUDE.md` adds a checklist so that code changes to tools, CLI, configuration or flows update the matching docs page in the same pull request. ## CI and deploy - `Docs build` runs `format:check`, `lint`, `typecheck` and `build` on every pull request that touches `packages/docs/`. The build has `onBrokenLinks: "throw"`, so it catches links left dangling by a moved page. - `Docs publish` deploys `packages/docs/build` through the GitHub Actions Pages source on every push to `main` that touches the docs. The repository Pages source needs to be set to **GitHub Actions** for the first run. ## Notes for review - The site is excluded from the root `packages/*` workspaces (`!packages/docs`) and keeps its own `package.json` and `package-lock.json`, so its dependency tree stays out of the toolkit's lockfile. Root Prettier, ESLint and knip ignore it; it formats and lints itself with the repo `.prettierrc` and its own `eslint.config.mjs`. `check-workspace-versions.mjs` skips it so its `0.0.0` version is not read as drift. - `webpack` is pinned to `5.105.4` and `@docusaurus/plugin-content-docs` / `theme-common` to `3.9.2` via `overrides`. Newer webpack fails Docusaurus 3.9's ProgressPlugin option validation, and a hoisted `plugin-content-docs@3.10.2` produced a duplicate React context that crashed SSR. - **Search is not wired up yet.** The shared theme always mounts a DocSearch bar, so an `algolia` block has to be present. It reads `ALGOLIA_APP_ID` / `ALGOLIA_API_KEY` / `ALGOLIA_INDEX_NAME` from the environment and the bar stays hidden until Argent has its own DocSearch application. Follow-up PR. ## Testing - `npm run format:check`, `npm run lint`, `npm run typecheck` and `npm run build` pass in `packages/docs/`. - Root `prettier --check`, `eslint` and `npm run knip` pass. - Walked the served production build in the browser in light and dark themes while iterating: root redirect, docs pages, sidebar icons, videos, TOC, footer. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
06585ae8fe |
fix(ci): stamp the canary version before the bundle bakes it into telemetry (#840)
`next`-channel builds report the **previous release's version** in every telemetry event. ## What happens `publish-npm.yml` ran `npm run build -w @swmansion/argent` (which bundles, and bakes `ARGENT_CLI_VERSION` in via esbuild `define`) *before* `Compute and stamp canary version` wrote the canary version into `packages/argent/package.json`. The define therefore captured whatever version was checked out — the last release. Verified against the published artifact: ``` $ npm pack @swmansion/argent@0.21.1-next.4 $ grep -A5 'function readCliVersion' package/dist/cli-cmds.mjs function readCliVersion() { if (true) { return "0.21.0"; # <- published as 0.21.1-next.4 } ``` and end to end, running that published build: ``` {"event":"toolserver:start","properties":{"cli_version":"0.21.0", ...}} ``` So every canary's events are attributed to the preceding stable version, and `next` traffic is indistinguishable from `latest` traffic in the collector. The step comment asserted the opposite — "the CLI reads its version from package.json at runtime — nothing bakes it in". That is true of `argent --version`, which is why the existing `Verify argent --version` check stayed green: it reads `package.json` at runtime and so is correct however the build is sequenced. Telemetry's `cli_version` is a different value, and nothing looked at it. The comment predates nothing — `ARGENT_CLI_VERSION` has been baked in since #274, and the unified workflow (#565) carried the claim in. ## Change - Move `Compute and stamp canary version` to sit between `npx tsc --build` and the bundle build. It stays after `npm ci` (the checked-out lockfile still matches the original version at install time), which was the other constraint the old comment recorded. - Add `Verify the bundled telemetry version matches the canary version`, asserting the literal each bundle actually carries. It scans every `dist/*.{mjs,cjs}`, skips bundles that carry no telemetry (`mcp-server`), and fails if none carries one — so the check cannot silently stop looking at anything. - Correct the stale comment. Only the canary path was affected; a dispatch release publishes the ref's verbatim version, which is already correct in `package.json` at checkout. ## Verification Built the real bundle in a worktree and ran the new guard, extracted verbatim from the workflow YAML: | ordering | baked | guard | | --- | --- | --- | | build, then stamp `0.21.1-next.99` (old) | `0.21.0` | exits 1 on all 3 telemetry bundles, no false failure on `mcp-server` | | stamp `0.21.1-next.99`, then build (new) | `0.21.1-next.99` | passes, "Verified the baked telemetry version in 3 bundle(s)" | End to end on the rebuilt bundle, `ARGENT_TELEMETRY_DEBUG=1` under an isolated `HOME`: ``` 2 "cli_version":"0.21.1-next.99" 1 "event":"cli:run_fail" 1 "event":"toolserver:start" ``` Also confirmed the bundle build does not run a workspace-version check, so building with `packages/argent` ahead of the other workspaces succeeds (exit 0), and `next-canary-version.mjs --write` touches only `packages/argent/package.json`. |
||
|
|
0a6dbec87c |
feat(telemetry): export events via OpenTelemetry OTLP logs instead of PostHog (#570)
## What
Swaps the `posthog-node` transport for an OpenTelemetry Logs exporter.
Everything above the transport stays as it is - opt-out consent,
per-machine identity and fingerprint, the sanitizer allowlist, the typed
event surface, crash diagnostics. Only the ~3-method client boundary
changes.
One analytics event becomes one OTLP/HTTP log record:
| PostHog | OpenTelemetry |
| --- | --- |
| `capture({ event, distinctId, properties })` | `logger.emit({ body:
event, attributes: { distinct_id, "event.name", ...props } })` |
| event name | log-record `body` |
| `distinctId` | `distinct_id` attribute |
| event/base properties | log-record attributes |
| `client.shutdown()` | `LoggerProvider.shutdown()` (force-flush +
teardown) |
Records go to a hard-coded Software Mansion collector. The endpoint and
the authorization header are passed to the exporter explicitly in code,
and an explicit value beats the corresponding `OTEL_EXPORTER_OTLP_*`
variable - the same anti-exfiltration property the fixed PostHog host
had.
## Changes
- `packages/telemetry/src/posthog.ts` becomes `otel.ts`:
`LoggerProvider` + `BatchLogRecordProcessor` + `OTLPLogExporter`
(HTTP/JSON). Queue size 20 and a 10 s flush interval mirror the old
client; the per-export deadline is 1.5 s, kept at or below `index.ts`'s
drain budget so a stalled export can't hold a short-lived command open.
- Dependencies: drop `posthog-node`, add
`@opentelemetry/{api,api-logs,sdk-logs,exporter-logs-otlp-http,resources}`.
They inline cleanly into the esbuild bundles.
- Build-time ingest token via esbuild `define`
(`ARGENT_OTEL_INGEST_TOKEN`), same mechanism as `ARGENT_CLI_VERSION`.
Unset in a dev build leaves the client unconstructed and telemetry
inert.
- Base props: drop `$process_person_profile` (a PostHog directive,
meaningless to a collector) and rename `$session_id` to `session_id`.
Null-valued attributes are dropped, since OTel rejects them -
`cloud_agent` is null on the common path, and absence means the same
thing.
- `Telemetry.md`: the "who we disclose to" and "international transfers"
sections describe a Software Mansion-operated OTLP collector instead of
PostHog as a third-party processor.
## A hang this uncovered
`timeoutMillis` only bounds a request once its socket is connected,
because the exporter applies it with `req.setTimeout()`. Against a
collector address that drops packets rather than refusing them - a
corporate egress filter, a dead host behind a firewall - the socket sits
in the connecting state where no export deadline reaches it, and the
process stays alive for the OS connect timeout: 75 s past a `shutdown()`
that had already resolved at 1.75 s.
Pairing the export deadline with an agent-level socket timeout, armed at
socket creation so it covers connection establishment too, fixes it:
| collector state | before | after |
| --- | --- | --- |
| healthy | 0.18 s, delivered | 0.18 s, delivered |
| connection refused | 0.94 s | 1.03 s |
| blackholed (http and https) | 75.0 s | 1.6 s |
| connected but never responds | - | 1.6 s |
`posthog-node` exited in 10.6 s under the same blackhole (undici aborts
a connecting socket), so this was a regression as well as a hang. A unit
test that fails without the fix guards it.
## Found in review
Each of these was reproduced before it was changed, and each is pinned
by a test that fails without the fix.
- **The environment's OTLP headers were forwarded to the collector.**
The SDK merges `OTEL_EXPORTER_OTLP_HEADERS` / `_LOGS_HEADERS` into every
request, keeping any key the code does not set itself. Out in the world
that variable holds the developer's *own* observability credential - a
Honeycomb team key, a Dynatrace `Api-Token`, Grafana Cloud basic auth -
so any machine already running OpenTelemetry shipped that third-party
secret here on every batch, without it passing the sanitizer or
appearing in `Telemetry.md`. Reproduced by watching `x-honeycomb-team:
hcaik_…` arrive at a capture server. The variables are now cleared
across the exporter constructor (where the SDK resolves its header set)
and restored after.
- **`httpAgentOptions` silently disabled connection reuse.** Supplying
it replaces the agent the SDK would build, whose default is `keepAlive:
true`. Measured on loopback: three batches shared one socket by default,
and opened three sockets with `httpAgentOptions` - a fresh TCP+TLS
handshake per 10 s batch in the long-lived tool-server. `keepAlive` is
restated alongside the timeout.
- **`markDisabled()`'s drain had no grace period.** It raced flat
against `SHORT_FLUSH_TIMEOUT_MS` while `shutdown()` allowed `timeoutMs +
250`. Since the exporter's own deadline equals that budget, the flat
race fired at the same instant and always abandoned the batch instead of
letting it finish. Both call sites share one `raceDrain()` now.
- **Export failures reached nobody.** The batch processor hands errors
to OpenTelemetry's global handler, so `LoggerProvider.shutdown()`
resolves identically for a delivered batch and a rejected token - making
`shutdown()`'s `catch` unreachable. The SDK's diagnostics are now routed
into the existing debug channel under `ARGENT_TELEMETRY_DEBUG=1`; a run
without the flag stays silent so no host application's diag logger is
disturbed.
- **The build-time token now beats the `__ARGENT_OTEL_TOKEN_TEST` seam**
rather than losing to it, so the seam reaches unbundled source only.
Verified with esbuild across all three build modes.
- **The endpoint guard test mocked the exporter it guards.** It could
only show that the code passes a `url`; an SDK upgrade that changed the
precedence would have kept it green. `otel-endpoint-live.test.ts` drives
the real exporter against loopback servers - dropping the explicit
`url`, the explicit `authorization`, or the header clearing each fails
it.
- **The wire shape had no pins at all.** Bypassing the sanitizer
entirely, sending an explicit `null`, renaming `service.name` and
dropping `event.name` all left the suite green. Four tests now fail on
those mutations. The sanitizer bypass was unpinned on `main` too.
## Before this ships
1. ~~**Collector URL**~~ - done in
|
||
|
|
c876fe8dbc |
ci: add knip anti-dead-code gate (#572)
## What Adds a CI gate for dead code, using [Knip](https://knip.dev). We didn't have one. ESLint's `no-unused-vars` only sees unused locals inside a module, so whole files nobody imports, exports nobody uses, and dependencies declared but never required all slipped through. Knip covers that gap in one tool (roughly `ts-prune` + `depcheck` + `unimported` combined). The new `.github/workflows/knip.yml` workflow ("Dead Code", job `Knip`) runs `npm run knip` on push and PR to `main`. `CONTRIBUTING.md`'s pull-request checklist gains it as a step, with the reason it has to run against a tree with no build output. Knip reads source directly, mapping each package's `dist` entry back to `src`, so the job needs no workspace build. It installs with `--ignore-scripts`: an analysis that only reads `package.json` and source gains nothing from the seven packages here that carry a native install script, six of which reach this runner (`fsevents` is darwin-only). Five try a prebuilt binary before compiling, and `dtrace-provider` always runs node-gyp but ends in `|| node suppress-error.js` — and off mac/solaris/freebsd its `binding.gyp` picks a stub target with no sources, so on ubuntu-latest node-gyp builds nothing for it and `node-pty` is the only package that reaches a compiler at all — an `optionalDependency`, whose failure npm ignores. The exposure is a bump that leaves a package with no prebuild for the runner, which is exactly what `dependabot.yml` pins `tree-sitter` against (0.25.0 ships no Node 24 prebuild and its compile breaks `npm ci`). While adding it I checked whether any other installed checker was missing a gate. None were: ESLint (`lint.yml`), Prettier (`format.yml`), `tsc` build + `typecheck:tests` + `typecheck:scripts` + vitest (`unit-tests.yml`), the workspace-version / focused-test / merge-marker checks (`repo-hygiene.yml`) and lockfile sync (`lockfile.yml`) are all already enforced. Dead code was the only gap. ## How it's enforced `npm run knip` is two invocations: ``` knip --exclude exports,types && knip --include exports,types,classMembers,nsExports,nsTypes --max-issues 206 ``` Every issue type knip reports is an error — that is its default severity, so `knip.jsonc` sets no `rules` block at all. The pre-existing backlog is bounded by the `--max-issues` ceiling on the second pass instead of being hidden behind a non-failing severity: it cannot grow unnoticed, and the number comes down as follow-up passes pay it off. When a change legitimately adds an export nothing imports yet, the number moves up in the same commit — that bump is what review should see, rather than the contributor deleting someone else's backlog to make room. Two PRs making that identical one-line edit merge without conflicting, so `main` lands one short of the truth and its push-triggered run is what catches it. Splitting the passes also keeps a real failure out from under the backlog — the enforced classes report on their own, and the second pass only runs once the first is clean. The second pass names `classMembers`, `nsExports` and `nsTypes` explicitly because they sit in knip's own `defaultExcludedIssueTypes`: `--exclude exports,types` never covered them and `--include exports,types` did not ask for them, so a new unused class member cost nothing and moving an export behind opaque namespace use took it out of the ceiling's scope entirely. `classMembers` has 12 findings on the unbuilt tree the ceiling counts (9 on a built one); the other two have none today. None of the three is gated at zero — all five parked classes share the one ceiling, so a new `nsExports` finding passes if something else in the backlog is retired in the same PR. Naming `classMembers` also turns knip's `isSkipLibs` off (`create-options.js:122`), which makes it search declaration files for external references and **suppresses** 44 export/type findings the same run reports without it — 57/181 becomes 44/150. That, not the 12 findings it adds, is why the ceiling is lower than the two counts alone would suggest; `--include-libs` with no `classMembers` reproduces 44/150 exactly. The ceiling counts what CI counts, against a tree with no build output. Whether a workspace's `main`/`types` path resolves to an existing file changes the result, and not by way of anything in that file — a zero-byte `packages/registry/dist/index.js` moves the count just as a real build does, while a zero-byte `dist/zzz.js` does nothing. A built tree reports fewer issues — 43/147/9 = 199 built, against 44/150/12 = 206 unbuilt — so `npm run knip` passing after a local `tsc --build` is not evidence the job will pass, which is how the Knip job first went red on 199. The ceiling is 206 and it is exact: 206 passes, 205 fails. Remove every `dist/` before trusting a number from it. The ceiling is one total across those classes, so it bounds the backlog rather than each class: paying off one parked export licences one new one elsewhere in the same PR. Knip has no per-class ceiling. `treatConfigHintsAsErrors` is on, so an exemption that stops suppressing anything fails the job rather than printing a hint below the backlog. ## Config `knip.jsonc` documents the false positives so the gate is both green and meaningful. - **`@swmansion/argent`** is a thin dispatcher whose real payload is an esbuild bundle of the other workspaces (`packages/argent/scripts/bundle-tools.cjs`). Knip can't follow imports across that boundary, so packages consumed only by the bundled-in code read as unused; 11 are exempted by name here, though not all for that reason — the http3-quiche transport is pulled in by `@fails-components/webtransport` itself at runtime, and no first-party file imports it. They're exempted **by name**, not behind a catch-all: `ignoreDependencies` also suppresses `unlisted`, and a pattern that matches everything would take this workspace's own `src/` and `scripts/` out of that check — where an import with no matching declaration is a genuine bug rather than a bundler artifact. (It reaches `unresolved` only nominally: knip matches the list against a package name parsed out of the specifier, and an unresolvable relative path yields none, so even a catch-all leaves those reported. `ignoreUnresolved` is the option that does silence them.) - **`tool-server`** reaches a few packages in ways static analysis can't see. The exemptions cover two distinct classes, kept apart in the config so each stated reason matches the finding it silences: - *declared but loaded too dynamically to detect* (would be reported as **unused dependencies**): `tree-sitter` / `tree-sitter-typescript` via dynamic `_require`, and the http3-quiche transport as a runtime companion of `@fails-components/webtransport`; - *imported but deliberately absent from that `package.json`* (would be reported as **unlisted dependencies**): `sharp` (kept out so the ~30 MB native binary isn't a cost every consumer pays) and `electron` (an `optionalDependency` of the published package at one call site, the Electron main-process API at the other). - Root `scripts/`: knip resolves those from `package.json` scripts **and** from `run:` steps in `.github/workflows` via its github-actions plugin — multi-line `run: |` blocks included. What the plugin doesn't parse is a PowerShell body, so `scripts/ci/windows-chromium-e2e.mjs` — invoked at `windows-e2e.yml:176`, in the `windows-latest` job where PowerShell is the default shell — is the one entry declared by hand. ### Known limits - `ignoreDependencies` exempts a package **name across its whole workspace**, in both directions; knip has nothing narrower. While `sharp` is listed for `tool-server`, an undeclared `import "sharp"` anywhere in that workspace passes too. The list is held to packages that genuinely cannot be declared, for exactly that reason. - Knip analyzes JavaScript and TypeScript only (`DEFAULT_EXTENSIONS`), so **only 11 of the 30 files under `scripts/` are in the gate at all**. The other 19 — 15 `.sh` files, `e2e-full/lib/report.py`, `ci/e2e-chromium-page.html`, and e2e-full's `README.md` and `results/.gitignore` — are invisible to it however they are reached — including the whole `scripts/e2e-full/` harness (13 files), invoked from nothing outside its own directory, its only mention elsewhere being a `.prettierignore` entry. - Reachability's widest hole is the workspace entry. Whatever `src/index.ts` re-exports is reached, so a single `export { x } from "./dead-module";` takes that module out of `files` altogether — verified against a module reported as an unused file one run earlier. The same line applied to parked exports retires them from the count without deleting anything: re-exporting `equalsCI`, `includesCI` and `Settled` from `tool-server`'s entry takes 206 to 203 and frees three ceiling slots. The ceiling bounds what knip can see, not what is dead. - Test files are entry points, so a production module kept alive only by its own test still passes `files`. Being reached by a test says nothing about being reachable in production, which is what the check approximates, and two modules sit in exactly that gap: `tools/paste` is imported by two test files and registered by nothing, so it is unreachable over MCP and HTTP, and `utils/ios-profiler/lifecycle.ts` is a re-export barrel whose header keeps it "for source compatibility with existing imports", of which its own test is now the only one — its four production consumers all import `profiler-shared/lifecycle` directly. That single test import is exactly what keeps `files` from reporting it: repoint it and knip returns `Unused files (1)`. `knip --production` is the lens that reports the class; the repo isn't clean under it — 13 unused files (those two, seven root scripts, `packages/argent/scripts/{bundle-tools,observe,sync-readme}.cjs` and `packages/registry/tests/helpers.ts`) with the export/type backlog at 227/206 — measured with `classMembers` off, so comparable to the 57/181 above rather than to the ceiling's 44/150 — so a production-mode pass is a follow-up of its own. - The same entry treatment hides the test trees themselves. Root `package.json`'s `test:scripts` runs `node --test`, which is what makes knip's node plugin contribute its test entries, and that plugin registers four entry patterns in **every** workspace: `**/test/**`, plus `*{.,-,_}test.<ext>`, `test-*.<ext>` and `test.<ext>` matched anywhere. So none of the **380** JS/TS files under `packages/*/test/` can be reported unused, and under `packages/*/tests/` only a name none of those three match can — of today's 15 that is `registry/tests/helpers.ts` alone, which is why it is the lone `tests/` entry in the `--production` list above. A `tests/test-utils.ts` or `tests/test.ts` would be just as invisible. A dead `zz.test.ts` planted in `packages/registry/tests/` passes; a dead `zz-helper.ts` in the same directory fails. - `knip@5.88.1` is the last 5.x release ever published — 6.0.0 landed the next day and `latest` is now 6.x. The pin is deliberate: knip 6 removed the `classMembers` issue type outright, and the shipped script hard-errors on it (`ERROR: Invalid issue type: classMembers`, exit 2). Upgrading is therefore not a version bump — it means re-deriving the ceiling, rewriting the paragraph that explains it, and resolving the pass-1 findings knip 6 adds (six unlisted binaries: `tail`, `plutil`, `xctrace`, `netstat`, `pkill`, `pgrep`; two unresolved imports: `/usr/bin/osascript`, `/usr/libexec/PlistBuddy`). Dependabot's `npm-minor-patch` group cannot move it, since no 5.x exists above 5.88.1; the `npm-major` group will keep offering the 6.x jump. - Knip folds `optionalDependencies` into the declared set, so importing `electron` or `node-pty` is not `unlisted` — they pass because it read them. What it never reports is an optionalDependency nothing uses, so only that direction is outside the gate, along with `argent-cli`'s runtime `req("node-pty")`, resolved through a `createRequire(import.meta.url)` in `lens-pty.ts`, which knip can't see either. ## Dead code this found - `@types/semver` and `@argent/telemetry` from `packages/argent` — both were being suppressed by exemptions whose stated bundler-boundary reason could not apply. esbuild strips types and never resolves `@types/*`, and `packages/argent` names `semver` nowhere; `@argent/telemetry` is aliased to an absolute source path by `bundle-tools.cjs`, so the declaration bought nothing. Removing them is proved safe by building: four of the five bundles still emit and telemetry is inlined in each with no runtime `require` of it. The fifth, preview-window's main, sits behind a downloaded native asset absent from a script-free install, so it is covered by mechanism rather than observation — `bundle-tools.cjs` passes the same `alias` map to every build, and esbuild's `alias` bypasses node resolution entirely: deleting `node_modules/@argent/telemetry` outright leaves all four emitted bundles byte-identical. Removed alongside, but **not** gate-reported: the `benchmark` npm script in `packages/argent`, whose `scripts/benchmark.cjs` does not exist. A dangling script target is a blind spot of this gate — restoring the script leaves knip at exit 0. - Four files with no importers: the `render-hook` injected script, react-profiler's `source-map` + `snippet-extractor`, and `scripts/setup-in-workspace.cjs` (referenced nowhere, and its own usage string names a `scripts/setup-project.cjs` that doesn't exist). - `superagent` in `tool-server`'s `test/http-streaming.test.ts` — imported for a type but declared in no manifest in the repo, resolving only through npm hoisting of supertest's own dependency. Reverting that import to `superagent` reports `Unlisted dependencies (1) … :3:32` and exits 1; `@types/supertest/index.d.ts:10` re-exports the identical type (`type Response = superagent.Response`). - `@clack/prompts` and `@moq/lite` from `tool-server`'s `package.json` — declared, imported nowhere. (`@moq/net` is the one actually used; `@moq/lite` isn't a peer of it.) Also deleted here, but **not** something this gate reported: `src/tools/simulator/simulator-server.ts`. It sat in the `files` blind spot above — `interaction-messages.test.ts` imported it, which is enough to count as reached. `setup-registry.ts` never registers it, so it was unreachable over MCP and HTTP, and that catalog roll-call was its only reference; the import and the catalog count (77 → 76) went with it. ## Lockfile Adding knip moves two transitive packages: `@napi-rs/wasm-runtime` 1.1.5 → 1.1.6 and `@tybys/wasm-util` 0.10.2 → 0.10.3. Knip pulls in `@oxc-resolver/binding-wasm32-wasi`, which floors `@napi-rs/wasm-runtime` at `^1.1.6`, lifting the hoisted entry that had resolved to 1.1.5 for `@rolldown/binding-wasm32-wasi`'s `^1.1.4`, which then cascades to `@tybys/wasm-util`. No runtime impact — both are support packages for the wasm32-wasi fallback bindings, which don't get installed where a native binding exists; `npm ls` reports both extraneous on darwin-arm64. Those two are the only resolved versions that change in the diff; the rest is knip's own tree (49 packages added) and `@moq/lite` removed. ## Verification - `npm run knip` exits 0. - **Coverage of the enforced set.** With no `rules` block, knip's included set is 15 issue types: ten enforced at zero tolerance by the first pass, plus the five the second pass names under the ceiling. Eleven mutations cover all ten, each flipping the gate to exit 1 and back to 0 on revert: | class | mutation | | --- | --- | | `files` | a throwaway `.mjs` in `scripts/`; a throwaway `.ts` in `packages/argent/src` | | `dependencies` | a bogus declared dep in `packages/tool-server/package.json` | | `devDependencies` | a bogus declared devDep in the same manifest | | `optionalPeerDependencies` | `bytes` moved to an optional `peerDependency` there | | `unlisted` | an undeclared import in `packages/argent/src/cli.ts` | | `unresolved` | an unresolvable relative specifier in the same file | | `binaries` | a root `package.json` script invoking a nonexistent binary | | `duplicates` | `export default sleep` alongside the existing named export in `utils/timing.ts` | | `enumMembers` | an unused member on an enum a second module imports and uses | | `catalog` | a `catalog` block in the root `package.json` naming a package nothing uses | One nuance worth recording: `duplicates` is narrower than it looks. It reports a symbol exported both by name and as `default` (`sleep|default`); a second *named* alias of the same symbol is not a duplicate, and a freshly added symbol nothing references is not reported at all — so several naive probes read as a miss. `enumMembers` needed a shape the repo does not currently contain, not an unreachable one. The only exported enum here, `ServiceState`, is re-exported through `@argent/registry`'s entry, where knip counts exports as used, so an unused member added to it is **not** reported — but an enum in a non-entry module that another module imports and uses does report one (`Blue ZzColor …/zz-enum.ts:3:3`, exit 1; deleting the unused member returns exit 0). That is the ninth of the ten covered. `catalog` is reachable here despite the repo using npm workspaces: knip's catalog loader falls back to `manifest.catalog` when there is no pnpm or yarn workspace file, so a `catalog` block in the root `package.json` reports `Unused catalog entries (1)` and exits 1. - The two new mechanisms discriminate as well: one extra unused export takes the backlog to 207 and fails the ceiling, and a stale `ignoreDependencies` entry produces `Configuration hints (1) … Remove from ignoreDependencies` and exit 1. - `npm ci --ignore-scripts` in a clean worktree, followed by `npx knip`, is green — the job doesn't need any of the postinstalls it now skips. - `tsc --build`, `typecheck:tests` (all workspaces), `typecheck:scripts`, `test:scripts`, `eslint . --max-warnings 0`, `prettier --check .` and the full workspace vitest suite (4711 passed, 3 skipped across 13 workspaces; 3070 of those are `@argent/tool-server` alone) are green; `package-lock.json` is untouched by the review pass. |
||
|
|
68ceb314d6 |
ci: cut over npm publishing to the unified trusted-publishing workflow (#571)
## Why Cutover step 3+4 of the plan from #565. `publish-npm.yml` is now registered on npmjs.com as the **trusted publisher** for `@swmansion/argent` (2026-07-24) and the OIDC publish path is verified live: `v0.17.1-next.1` published with a SLSA provenance attestation via a workflow_dispatch of the unified workflow. Extra urgency: token-based publishing died on 2026-07-24 (~11:20–15:20 UTC window; npm's 2FA-bypass token restriction) — **`publish-next.yml`'s canary now fails on every main push** (identical E404 on the registry PUT, e.g. run 30104847558). The old workflows aren't just redundant, they're broken; only the trusted-publisher workflow can publish. ## What - Delete `publish.yml` and `publish-next.yml` — `publish-npm.yml` is the only publish path. - `sync-next-tag.yml`: drop the two retired workflow names from the `workflow_run` trigger. - `publish-npm.yml`: remove `NODE_AUTH_TOKEN` from both publish steps (OIDC authenticates; the rest of the job already ran token-less) and update the header — the cutover plan is replaced by a note that this file's name is load-bearing for the npmjs.com registration. - `NPM_TOKEN` stays as a secret: `sync-next-tag.yml` still needs it for dist-tag mutation, which OIDC doesn't cover. Note the token may currently be dead for dist-tag writes too — the post-publish sync run succeeded but had no mutation to make; if a real re-point fails, a fresh granular token is needed. ## After merging Set the repo variable to enable canary-on-push (kept as the canary kill-switch): ``` gh variable set PUBLISH_NPM_UNIFIED --repo software-mansion/argent --body "1" ``` Ordering is safe: until the variable is set, a main push publishes nothing (the old canary workflow is gone, the new one skips) — no double-publish window exists. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
2546788a8d |
ci: unified publish-npm workflow (provenance-ready, trusted-publishing prerequisite) (#565)
## Why
npm provenance / trusted publishing for `@swmansion/argent` requires
registering **one** workflow file as the package's trusted publisher on
npmjs.com — but we currently publish from two (`publish.yml` for stable,
`publish-next.yml` for canaries/prereleases). This PR adds their unified
successor, **`publish-npm.yml`**, without touching the old workflows:
they keep running unchanged until cutover.
## Design
One file, mode derived from the trigger and the version — nothing is
spelled twice:
| Trigger | Behavior |
|---|---|
| `push` to `main` | Canary: version derived + stamped
(`<base>-next.<k>`), published under `next` with the same registry-race
retry loop as before. **Dormant until the `PUBLISH_NPM_UNIFIED` repo
variable is set to `1`** — otherwise every main push would publish two
canaries while `publish-next.yml` still exists. |
| `workflow_dispatch` | Publishes the ref's `package.json` version
verbatim. Dist-tag derived from the version itself: prerelease → `next`,
stable → `latest`. The optional `channel` input is an assertion (fails
on mismatch), not a selector — you can't accidentally ship a prerelease
to `latest` or vice versa. `dry_run` runs the full build + bundle
verification + pack without publishing. |
Provenance is on from day one: the job has `id-token: write` and every
publish path uses `--provenance` (repo is public, `repository.url`
matches — the two things the registry verifies). Token auth
(`NPM_TOKEN`) is kept for now; npm ≥ 11.5.1 is ensured in the job so the
OIDC exchange takes over transparently once the trusted publisher is
registered.
`sync-next-tag.yml` now also triggers on the new workflow, so `next`
keeps getting re-pointed after publishes from either generation. (Until
cutover a main push may trigger the sync twice — once per publish
workflow — which is harmless: the script is idempotent.)
## Cutover checklist (follow-up, also documented in the workflow header)
1. Test: `workflow_dispatch` with `dry_run`, then a real prerelease
dispatch (`v*-next.N` ref).
2. On npmjs.com, register `publish-npm.yml` as the trusted publisher for
`@swmansion/argent` (org `software-mansion`, repo `argent`).
3. Set repo variable `PUBLISH_NPM_UNIFIED=1` and delete `publish.yml` +
`publish-next.yml` in the same PR (also dropping their names from
`sync-next-tag.yml`'s `workflow_run` trigger).
4. Remove `NODE_AUTH_TOKEN` from the publish steps (`NPM_TOKEN` stays
for `sync-next-tag.yml` — dist-tag mutation isn't covered by OIDC);
optionally restrict the package's publishing access to the trusted
publisher only.
## Behavior differences vs the old pair
- Stable dispatch now publishes with an explicit `--tag latest` (same
effect as the old default) and `--provenance`.
- The old `publish-next.yml` dispatch guard ("refuse non-prerelease
versions") is generalized: a stable-version ref dispatched here goes to
`latest` instead of erroring. Set `channel: next` if you want the old
refusal behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
7400179b5b |
Windows support: Android + Chromium host control plane (#421)
## What Makes Argent run on Windows. iOS Simulator is macOS-only (Apple), so on Windows that means the **Android + Chromium** host control plane. The blocker was twofold: (1) no Windows `simulator-server` binary, and (2) POSIX-only assumptions in the tool-server. Companion radon PR builds the binary and runs the Android emulator E2E on Windows: software-mansion/radon#134. ## Changes **Windows-aware tool-server / resolver / bundling** - New shared `commandOnPath()` helper — `where` on Windows, `command -v` via `/bin/sh` on POSIX. `android-binary`, `check-deps`, and `vega-cli` previously hardcoded `/bin/sh`, which never matches on Windows. - `android-binary`: append `.exe` to `adb`/`emulator` in the `$ANDROID_HOME` fallback; probe `%LOCALAPPDATA%\Android\Sdk` (Android Studio's Windows default). - simulator-server resolver + dispatcher + `bundle-tools` + `download-simulator-server.sh`: ship and resolve `simulator-server.exe` on `win32` (new `simulatorServerBinaryName()`), add `win32` to the bundled host keys, add the `simulator-server-argent-windows.exe` download target with PE arch validation. - `stop-metro`: resolve listening PIDs via `netstat -ano` on Windows (`lsof` is POSIX-only and threw ENOENT). - `adb.ts`: AVD root resolution used `$HOME` + `startsWith("/")` (rejects `C:\…`); now `os.homedir()` + `path.isAbsolute()`. - **`boot-device`**: the emulator was spawned `detached` with `stdio:"ignore"`; on Windows a detached emulator with NUL stdout/stderr never reaches `sys.boot_completed` (verified on `windows-latest`: the identical flag set boots fine with real handles, hangs with NUL). Redirect the detached emulator's output to a temp log file on win32. POSIX keeps `"ignore"`. **Tests** — cross-platform unit coverage for `commandOnPath` (both branches), win32 `.exe` Android resolution, win32 `.exe` simulator-server resolution, and a win32 AVD drive-path case; dep-gate tests now mock at the `commandOnPath` boundary so they're platform-agnostic. **CI** — `windows-e2e.yml` (`windows-chromium` on `windows-latest`): runs the resolver/dep unit tests natively on Windows, confirms `adb.exe` resolution via the SDK root, then drives a **real headless Chrome over CDP** through the tool-server (discover → screenshot → describe → tap → observe the DOM mutation). Flow in `scripts/ci/windows-chromium-e2e.mjs` (also runnable locally / in Docker). The `simulator-server.exe` build + the full **Android-emulator E2E on Windows** live in radon CI (the binary's source is the private radon repo). ## Verification (all green) - **Chromium control plane on real Windows** (`windows-latest`): `✓ discovered chromium-cdp-9222 → ✓ screenshot → ✓ describe → ✓ gesture-tap → ✓ DOM mutation observed`. Resolver/dep unit tests run natively; `adb.exe` resolves via the SDK root (it's *not* on PATH on the runner — the gap the resolver closes). - **Android emulator on real Windows** (radon#134, `windows-latest` / WHPX): the real Argent path — `boot-device` → `{ booted: true }` → screenshot via `simulator-server.exe` → gesture-tap — all green. (The boot-device fix above is what makes the detached emulator reach `boot_completed`.) - **Local clean-room E2E**: the Chromium flow runs green in a headless Linux Docker container (fresh `npm ci`, no host pollution). - **No regressions**: full tool-server suite (1624) + native-devtools-ios (16); ESLint/Prettier/typecheck clean; **Wayland E2E** (Linux AVD boot) and **Vega VVD E2E** still pass (they exercise the changed `adb.ts` / `boot-device.ts` / `vega-cli.ts` paths). ## Notes Android-emulator E2E needs WHPX nested virtualization — confirmed available on the current hosted `windows-latest` image (Server 2025); the emulator boots hardware-accelerated in ~3 min. --------- Co-authored-by: Ignacy Łątka <claude-hubert.gancarczyk@swmansion.com> Co-authored-by: Ignacy Łątka <latekvo@users.noreply.github.com> |
||
|
|
5d3f7bc62b |
fix(ci): check tcp ax-service at its platform-neutral bin/tcp path (#540)
## Problem Every **Publish prerelease to npm (next)** run on main has failed since #536 landed (e.g. [run 29897926322](https://github.com/software-mansion/argent/actions/runs/29897926322/job/88851967242)), blocking canaries — including the canary that would ship the ios-remote ax-service fix itself. The failing step is the sanity check: ``` test -x packages/argent/bin/darwin/tcp/ax-service ``` But the binary is fine — the job log right above the failure shows `✓ Copied ax-service (tcp) binary → bin/tcp/ax-service`. The whole pipeline deliberately uses the **platform-neutral** `bin/tcp/` slot, not `bin/darwin/tcp/`: - `scripts/download-native-binaries.sh` writes to `bin/tcp/ax-service` - `bundle-tools.cjs` copies from/to `bin/tcp/ax-service` - `axServiceBinaryPathTcp()` / `tcpBinDir()` in `native-devtools-ios` resolve `bin/tcp` on every host The neutrality is intentional: the tcp ax-service is a darwin/iOS-sim binary uploaded to and `simctl spawn`'d on the *remote* macOS orchestrator, so a Linux host must still resolve it — `bin/linux/tcp` would never match. Only the sanity checks (and their comments) got the path wrong. ## Fix - Point the check in `publish-next.yml` and `publish.yml` at `packages/argent/bin/tcp/ax-service` (the stable-release workflow had the identical wrong path and would have failed the same way on the next release). - Correct the comment claiming `axServiceBinaryPathTcp()` resolves `bin/darwin/tcp`. Note: `packages/argent-private/.github/workflows/build-native-binaries.yml:549` has the same stale comment ("download-native-binaries.sh writes it to bin/darwin/tcp/ax-service") — that lives in the private submodule repo, so it isn't part of this PR. ## Testing Verified against the failing run's log (binary lands at `bin/tcp/ax-service`, check looks in `bin/darwin/tcp/`) and cross-checked all three path producers/consumers listed above. `grep -rn 'darwin/tcp'` now only matches comments explaining why the path is *not* used. |
||
|
|
b90970ffa8 |
ci: ship TCP-transport iOS binaries in the published package (#487)
## Why
argent's `ios-remote` (sim-remote) support drives **ax-service** and
**native-devtools** over a TCP socket, resolving them from
`bin/darwin/tcp/ax-service` and `dylibs/tcp/*.dylib`. `bundle-tools.cjs`
already stages those into the published package **when present**, but
`download-native-binaries.sh` never fetched them — so
`@swmansion/argent` 0.15.0 shipped with no TCP variant at all, and every
`ios-remote` `describe` / `native-*` call failed with "not found".
## What
- **`download-native-binaries.sh`** — fetch the TCP dylibs tarball into
`dylibs/tcp/` and `tcp-ax-service` into `bin/darwin/tcp/ax-service`
(best-effort: skipped with a warning on pre-sim-remote release tags),
with the same Mach-O platform + codesign verification applied to the
iOS/tvOS assets.
- **`publish.yml` / `publish-next.yml`** — hard `test -x`/`test -f`
gates for the four TCP files so a missing build fails the publish loudly
instead of silently regressing (mirrors the existing tvOS gates).
- **`package.json`** — add `build:ios-binaries:tcp` and wire it into
`pack:mcp:local` so local packs match CI.
`bundle-tools.cjs` needs no change — its `ax-service (tcp)` asset entry
and recursive `dylibs/` copy already handle the `tcp/` slot.
## Verified locally
- `npm pack --dry-run -w @swmansion/argent` now lists
`bin/darwin/tcp/ax-service` +
`dylibs/tcp/{libArgentInjectionBootstrap,libKeyboardPatch,libNativeDevtoolsIos}.dylib`.
- `package-manifest.test.ts` (download↔bundle-tools path agreement) and
`native-devtools-ios/index.test.ts` (TCP path resolution) pass.
- End-to-end against a real sim-remote device: with the TCP binaries in
place, `describe` returns the full ax tree and `native-describe-screen`
/ `native-find-views` / `native-full-hierarchy` all return `status: ok`.
## Paired PR
The producer side — the workflow that builds, signs, and uploads these
TCP release assets — is **software-mansion/argent-private#26**. Both
must land for a release to ship the TCP binaries.
## Known follow-up (out of scope here)
`axServiceBinaryPathTcp()` resolves
`bin/<hostPlatformKey()>/tcp/ax-service`, and `bundle-tools.cjs` stages
the TCP ax-service **darwin-only**. So `describe` via ios-remote works
from a macOS argent host (verified), but a **Linux** host would hit "not
found" at `bin/linux/tcp/`. The TCP dylibs are fine cross-host (flat
`dylibs/tcp/`, un-gated). Full Linux-host support means staging the
darwin TCP ax-service into each host's `tcp/` dir, modeled on the
per-platform `simulator-server` loop — left as a follow-up.
|
||
|
|
22ff30acb4 |
ci(vega-vvd-e2e): build host image on runner, drop prebuilt GHCR image (#445)
## Summary [`finloop/vega-virtual-device-action`](https://github.com/finloop/vega-virtual-device-action) shipped a new version that **builds the `vega-virtual-device-host` image on the consumer runner** from its bundled Dockerfile (cached via the GitHub Actions cache). ## Changes (`.github/workflows/vega-vvd-e2e.yml`) - **Re-pin the action** `fe151d9 → a0f1776` ([latest `main`](https://github.com/finloop/vega-virtual-device-action/commit/a0f17767abafb27ec9b58974d94643fc46a4a35d)). That commit's bundled Dockerfile carries the latest Vega SDK (`.sdk-version` = `0.23.8358`), so the image the action builds uses it. - **Drop the prebuilt SDK image.** Removed the `image:`/`pull:` inputs and the pinned `HOST_IMAGE` GHCR digest (`ghcr.io/finloop/vega-virtual-device-host@sha256:2110746…`). With no `image:`, the action builds the host image locally with the pinned SDK. Also removed the now-unused `host-image` `workflow_dispatch` input. |
||
|
|
c4a9f0bd8a |
ci: publish a "next" canary on every push to main (#423)
**Update after team discussion:** the open question from the original
version of this PR ("should we fire `next` on every push to main?") got
a yes, so this now does exactly that — continuous canary publishing from
`main`. Original tag-sync wiring is still here too. Branch is merged up
to current `main`.
### what it does
every push to main publishes a canary prerelease under the `next`
dist-tag, so `next` always tracks the tip of main.
- **"Publish prerelease to npm (next)"** now also triggers on `push:
branches: [main]`. on that path it derives a version, stamps it into
`packages/argent/package.json`, and publishes `--tag next`. the manual
`workflow_dispatch` path is untouched (gated behind `if:
github.event_name == 'workflow_dispatch'`).
- **version scheme** = `<patch-bump of the highest published
version>-next.<counter>`. a prerelease of the next patch still outranks
the current `latest` (so `next` lands above it) but stays under the
eventual stable. the **counter resets per base**: next index after the
highest `<base>-next.<k>` already on npm, or 0 if the base has no
canaries yet — so each patch line starts at `.0`:
```
0.13.1-next.0, 0.13.1-next.1, ... ← latest is 0.13.0
... 0.13.1 ships stable ...
0.13.2-next.0, 0.13.2-next.1, ... ← counter restarts
```
lives in `scripts/next-canary-version.mjs` (+ tests) as a pure function
of the published-versions list, reusing the semver helpers from the sync
script.
- **`argent -v` reports the right thing.** the CLI reads its version
from `package.json` at runtime, so stamping is enough — and there's an
in-pipeline step that runs the built `argent --version` and fails the
publish if it doesn't match the version about to ship. verified
end-to-end against the real registry: computed `0.13.1-next.0`, stamped,
built, `argent --version` → `0.13.1-next.0`.
- **collisions self-heal.** the counter is registry-derived, so canary
publishes are serialized (a `concurrency` group) to kill the concurrent
race; the publish step additionally retries a version-exists collision
(npm read-after-write lag on back-to-back runs) by recomputing. any
other publish failure still fails fast.
- canary builds pull the floating `argent-main` / `radon-main` signed
binaries. sync workflow still fires after every publish (self-heals
`next`, and still does the real work after a *stable* release).
### stuff to sanity-check before merge
- this publishes to npm on **every** merge to main — a lot of versions +
a full build/publish run each time. fine if that's the intent (it's the
whole point), just calling it out.
- **native binaries on the canary path are the floating `argent-main`
tag** — if radon's binary build lags a native-source change on main, a
canary can ship fresh JS against slightly-older signed
dylibs/ax-service/APK with no signal (the *missing* case fails loud +
safe; the *stale* case is silent). there's no per-commit signed-binary
build to pin against, so this is inherent to canary-from-main rather
than something this PR can fix — flagging it as the one real "preview
channel can be skewed" caveat.
- during a rapid burst of merges, GitHub's concurrency supersedes
intermediate queued runs — published `-next.N` stay contiguous from 0,
but not literally every commit in a burst gets its own canary.
- like any push trigger, it only goes live once this is on main.
|
||
|
|
b8afbed183 |
feat(tv): Apple TV (tvOS) and Android TV support (#374)
Adds end-to-end support for driving **Apple TV (tvOS)** and **Android TV (leanback)** targets through argent, using a focus-driven interaction model (TVs have no touchscreen — interaction is moving a focus highlight with the remote). This PR was reworked after review to **reuse the existing cross-platform tool surface and the `tv-remote` tool from the Vega/Fire TV work (#366)** rather than ship a separate family of `tv-*` tools. ## What this delivers TV targets are driven by the **same cross-platform tools as every other platform**, routed through the existing `dispatchByPlatform` fork mechanism — there are no dedicated `tv-describe`/`tv-navigate`/`tv-type`/`tv-set-focus` tools: - **`describe`** — on a `runtimeKind: "tv"` target, returns the focus-driven view (focused + focusable elements) instead of a tap tree. On Android TV it auto-falls-back to the full `uiautomator` tree when the RN focus engine exposes nothing. - **`tv-remote`** — the cross-platform remote/D-pad tool from #366, now extended with `ios` (Apple TV HID daemon) and `android` (Android TV adb keyevents) branches alongside the existing `vega` branch. Single button, a `repeat`, or a whole path (`["up","right","select"]`) in one call. - **`keyboard`** — types into the focused field on a TV target (named keys are rejected there — they're navigation, which belongs to `tv-remote`). - **`button`** stays hardware-only (phones/tablets); it is not a TV tool. **Apple TV** runs two native daemons (in-sim AX service + host-side HID daemon), shipped via the `argent-private` submodule. **Android TV** reuses the same tool surface, adb-backed (`input keyevent`, `uiautomator dump`, `input text`). ## Remote vocabulary parity `tv-remote` exposes the full 16-button vocabulary (`up`/`down`/`left`/`right`/`select`/`back`/`home`/`menu`/`playPause`/`rewind`/`fastForward`/`next`/`previous`/`volumeUp`/`volumeDown`/`mute`): | | Apple TV | Android TV | Vega | |---|---|---|---| | D-pad / select / back / menu / home / playPause | ✅ | ✅ | ✅ | | media-transport + volume/mute | ❌ rejected | ✅ | ✅ | Media-transport/volume keys **genuinely work on Android TV** (real keycodes — verified live: `volumeUp` moved `STREAM_MUSIC`, `mute` toggled per `dumpsys audio`). On the **Apple TV simulator** they are **rejected with a clear error**: on-device testing confirmed the tvOS sim's HID stack silently drops Consumer-Control events, so returning success would be a lie. ## Key design points - **tvOS UDIDs are UUID-shaped and indistinguishable from iOS by shape.** Handled with `runtimeKind` detection (tvOS runtime string for Apple TV; `pm list features` leanback/television for Android TV — *not* `ro.build.characteristics`, which lies on TV emulators). - Converted launch-app / restart-app / screenshot / screenshot-diff / run-sequence from **eager** to **lazy** service resolution, so a tvOS target never spins up (and hangs on) the iOS-only simulator-server / native-devtools blueprints. - Native injection selects the platform-matched (TVOSSIMULATOR) dylib slice; daemons recycle across sim reboots and app relaunches so injection and focus survive. - SKILL.md footprint minimized per review: the two TV skills were collapsed into a single lean **`argent-tv-interact`** (~40 lines). ## Verification - `npm run build`, ESLint, Prettier, and the full tool-server suite (**1565 tests**) all pass; CI green (incl. the Apple TV / Android TV / Vega e2e jobs). - Core flows verified live against real apps on both an Apple TV 4K simulator and a Google ATV emulator. ## Dependency / merge order Pins the `argent-private` submodule to the tip of its `feat/tv-support` branch (software-mansion/argent-private#20). **Merge that PR to `main` first, then bump the submodule pointer here to the merged SHA before merging this PR.** 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
05e619e55d |
ci: device E2E smoke tests for every hosted-CI platform (#371)
## What `wayland-e2e.yml` boots an Android AVD on Linux under headless Weston and asserts the full pipeline works without a display. It guards exactly one cell of the support matrix. This adds the same boot→screenshot→gesture-tap smoke test for every other (target × host) cell that can run on GitHub-hosted runners. | target | host | job | status | |---|---|---|---| | Android emulator | Linux (KVM) | `wayland-e2e.yml` (existing) | ✅ | | iOS simulator | macOS | `ios-sim-macos` | ✅ | | Chromium / Electron | Linux (Xvfb) | `chromium-linux` | ✅ | | Chromium / Electron | macOS | `chromium-macos` | ✅ | ## How - **`scripts/e2e/drive-device.sh`** — shared driver. Boots one device through the tool-server, then asserts `booted:true` → screenshot has real (non-blank) pixels → `gesture-tap` round-trips. Every tool takes the device id as `udid` and screenshots come back as the same `data.image.hostPath` envelope on all platforms, so the body is uniform; each job just supplies the cell-specific boot JSON + device id. - **`packages/tool-server/test/fixtures/electron-smoke-app/`** — minimal self-contained Electron app the Chromium jobs point `electronAppPath` at. It vendors its own electron via its own lockfile (kept out of the repo root lockfile); CI `npm ci`s it so `boot-electron` resolves `./node_modules/.bin/electron`. - **`.github/workflows/e2e-device-smoke.yml`** — three jobs, `workflow_dispatch` + path-filtered `pull_request`. iOS downloads the darwin native binaries (injection must succeed for `bootIos` to report `booted:true`); Chromium-Linux runs under Xvfb with `--no-sandbox`. The tool-server is started with `TS_NODE_TRANSPILE_ONLY=1` so cold ts-node startup doesn't flakily exceed the readiness poll on loaded macOS runners (types are still enforced by the typecheck job). ## Known gaps (supported on real machines, not runnable on hosted runners) - **Android emulator on macOS** — the arm64 emulator needs HVF (`-enable-hvf`), but hosted macOS runners are themselves VMs with no nested virtualization. Confirmed by running it: qemu dies with `HVF error: HV_UNSUPPORTED` ~18s into boot, regardless of GPU mode or RAM. The Android boot/screenshot/tap path is already regression-guarded on Linux via KVM (`wayland-e2e.yml`), so macOS adds no runnable coverage. - **Physical devices** — real iOS (CoreDevice HID) and real Android (adb-USB) have no attached hardware on hosted runners. Both would need self-hosted runners (a Mac exposing HVF / wired-up devices). Documented in the workflow header. ## Verification - All checks green: the 3 device jobs + ESLint + Prettier + Unit tests. - Locally (before CI): ran `drive-device.sh` end-to-end against a real tool-server → boot `booted:true`, screenshot **142 KB** (floor 20 KB), tap `tapped:true`, exit 0; and confirmed via the Argent stack that a tap at (0.5, 0.5) lands on the fixture's centered button and increments its counter. |
||
|
|
a5902e3186 |
chore(ci)(deps): bump the github-actions group across 1 directory with 4 updates (#403)
Bumps the github-actions group with 4 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [actions/setup-node](https://github.com/actions/setup-node), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [finloop/vega-virtual-device-action](https://github.com/finloop/vega-virtual-device-action). Updates `actions/checkout` from 4 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>What's Changed</h2> <ul> <li>block checking out fork pr for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> <li>Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the minor-actions-dependencies group across 1 directory by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2458">actions/checkout#2458</a></li> <li>Bump flatted from 3.3.1 to 3.4.2 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2460">actions/checkout#2460</a></li> <li>Bump js-yaml from 4.1.0 to 4.2.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2461">actions/checkout#2461</a></li> <li>Bump <code>@actions/core</code> and <code>@actions/tool-cache</code> and Remove uuid by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2459">actions/checkout#2459</a></li> <li>upgrade module to esm and update dependencies by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2463">actions/checkout#2463</a></li> <li>Bump the minor-npm-dependencies group across 1 directory with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2462">actions/checkout#2462</a></li> <li>getting ready for checkout v7 release by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2464">actions/checkout#2464</a></li> <li>update error wording by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2467">actions/checkout#2467</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.3...v7.0.0">https://github.com/actions/checkout/compare/v6.0.3...v7.0.0</a></p> <h2>v6.0.3</h2> <h2>What's Changed</h2> <ul> <li>Update changelog by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2357">actions/checkout#2357</a></li> <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li> <li>Update changelog for v6.0.3 by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2446">actions/checkout#2446</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/yaananth"><code>@yaananth</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6...v6.0.3">https://github.com/actions/checkout/compare/v6...v6.0.3</a></p> <h2>v6.0.2</h2> <h2>What's Changed</h2> <ul> <li>Add orchestration_id to git user-agent when ACTIONS_ORCHESTRATION_ID is set by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2355">actions/checkout#2355</a></li> <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.1...v6.0.2">https://github.com/actions/checkout/compare/v6.0.1...v6.0.2</a></p> <h2>v6.0.1</h2> <h2>What's Changed</h2> <ul> <li>Update all references from v5 and v4 to v6 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2314">actions/checkout#2314</a></li> <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li> <li>Clarify v6 README by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2328">actions/checkout#2328</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6...v6.0.1">https://github.com/actions/checkout/compare/v6...v6.0.1</a></p> <h2>v6.0.0</h2> <h2>What's Changed</h2> <ul> <li>Update README to include Node.js 24 support details and requirements by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li> <li>Persist creds to a separate file by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li> <li>v6-beta by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2298">actions/checkout#2298</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p> <blockquote> <h1>Changelog</h1> <h2>v7.0.0</h2> <ul> <li>Block checking out fork PR for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> <li>Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the minor-actions-dependencies group across 1 directory by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2458">actions/checkout#2458</a></li> <li>Bump flatted from 3.3.1 to 3.4.2 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2460">actions/checkout#2460</a></li> <li>Bump js-yaml from 4.1.0 to 4.2.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2461">actions/checkout#2461</a></li> <li>Bump <code>@actions/core</code> and <code>@actions/tool-cache</code> and Remove uuid by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2459">actions/checkout#2459</a></li> <li>upgrade module to esm and update dependencies by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2463">actions/checkout#2463</a></li> <li>Bump the minor-npm-dependencies group across 1 directory with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2462">actions/checkout#2462</a></li> </ul> <h2>v6.0.3</h2> <ul> <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li> <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> </ul> <h2>v6.0.2</h2> <ul> <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li> </ul> <h2>v6.0.1</h2> <ul> <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li> </ul> <h2>v6.0.0</h2> <ul> <li>Persist creds to a separate file by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li> <li>Update README to include Node.js 24 support details and requirements by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li> </ul> <h2>v5.0.1</h2> <ul> <li>Port v6 cleanup to v5 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li> </ul> <h2>v5.0.0</h2> <ul> <li>Update actions checkout to use node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li> </ul> <h2>v4.3.1</h2> <ul> <li>Port v6 cleanup to v4 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li> </ul> <h2>v4.3.0</h2> <ul> <li>docs: update README.md by <a href="https://github.com/motss"><code>@motss</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li> <li>Add internal repos for checking out multiple repositories by <a href="https://github.com/mouismail"><code>@mouismail</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li> <li>Documentation update - add recommended permissions to Readme by <a href="https://github.com/benwells"><code>@benwells</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li> <li>Adjust positioning of user email note and permissions heading by <a href="https://github.com/joshmgross"><code>@joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li> <li>Update CODEOWNERS for actions by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li> <li>Update package dependencies by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li> </ul> <h2>v4.2.2</h2> <ul> <li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li> <li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li> </ul> <h2>v4.2.1</h2> <ul> <li>Check out other refs/* by commit if provided, fall back to ref by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/checkout/commit/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"><code>9c091bb</code></a> update error wording (<a href="https://redirect.github.com/actions/checkout/issues/2467">#2467</a>)</li> <li><a href="https://github.com/actions/checkout/commit/1044a6dea927916f2c38ba5aeffbc0a847b1221a"><code>1044a6d</code></a> getting ready for checkout v7 release (<a href="https://redirect.github.com/actions/checkout/issues/2464">#2464</a>)</li> <li><a href="https://github.com/actions/checkout/commit/f0282184c7ce73ab54c7e4ab5a617122602e575f"><code>f028218</code></a> Bump the minor-npm-dependencies group across 1 directory with 3 updates (<a href="https://redirect.github.com/actions/checkout/issues/2462">#2462</a>)</li> <li><a href="https://github.com/actions/checkout/commit/d914b262ffc244530a203ab40decab34c3abf34d"><code>d914b26</code></a> upgrade module to esm and update dependencies (<a href="https://redirect.github.com/actions/checkout/issues/2463">#2463</a>)</li> <li><a href="https://github.com/actions/checkout/commit/537c7ef99cef6e5ddb5e7ff5d16d14510503801d"><code>537c7ef</code></a> Bump <code>@actions/core</code> and <code>@actions/tool-cache</code> and Remove uuid (<a href="https://redirect.github.com/actions/checkout/issues/2459">#2459</a>)</li> <li><a href="https://github.com/actions/checkout/commit/130a169078a413d3a5246a393625e8e742f387f6"><code>130a169</code></a> Bump js-yaml from 4.1.0 to 4.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2461">#2461</a>)</li> <li><a href="https://github.com/actions/checkout/commit/7d09575332117a40b46e5e020664df234cd416f3"><code>7d09575</code></a> Bump flatted from 3.3.1 to 3.4.2 (<a href="https://redirect.github.com/actions/checkout/issues/2460">#2460</a>)</li> <li><a href="https://github.com/actions/checkout/commit/0f9f3aa320cb53abeb534aeb54048075d9697a0e"><code>0f9f3aa</code></a> Bump actions/publish-immutable-action (<a href="https://redirect.github.com/actions/checkout/issues/2458">#2458</a>)</li> <li><a href="https://github.com/actions/checkout/commit/f9e715a95fcd1f9253f77dd28f11e88d2d6460c7"><code>f9e715a</code></a> block checking out fork pr for pull_request_target and workflow_run (<a href="https://redirect.github.com/actions/checkout/issues/2454">#2454</a>)</li> <li><a href="https://github.com/actions/checkout/commit/df4cb1c069e1874edd31b4311f1884172cec0e10"><code>df4cb1c</code></a> Update changelog for v6.0.3 (<a href="https://redirect.github.com/actions/checkout/issues/2446">#2446</a>)</li> <li>Additional commits viewable in <a href="https://github.com/actions/checkout/compare/v4...v7">compare view</a></li> </ul> </details> <br /> Updates `actions/setup-node` from 4 to 6 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/setup-node/releases">actions/setup-node's releases</a>.</em></p> <blockquote> <h2>v6.0.0</h2> <h2>What's Changed</h2> <p><strong>Breaking Changes</strong></p> <ul> <li>Limit automatic caching to npm, update workflows and documentation by <a href="https://github.com/priyagupta108"><code>@priyagupta108</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1374">actions/setup-node#1374</a></li> </ul> <p><strong>Dependency Upgrades</strong></p> <ul> <li>Upgrade ts-jest from 29.1.2 to 29.4.1 and document breaking changes in v5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1336">#1336</a></li> <li>Upgrade prettier from 2.8.8 to 3.6.2 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1334">#1334</a></li> <li>Upgrade actions/publish-action from 0.3.0 to 0.4.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1362">#1362</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-node/compare/v5...v6.0.0">https://github.com/actions/setup-node/compare/v5...v6.0.0</a></p> <h2>v5.0.0</h2> <h2>What's Changed</h2> <h3>Breaking Changes</h3> <ul> <li>Enhance caching in setup-node with automatic package manager detection by <a href="https://github.com/priya-kinthali"><code>@priya-kinthali</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1348">actions/setup-node#1348</a></li> </ul> <p>This update, introduces automatic caching when a valid <code>packageManager</code> field is present in your <code>package.json</code>. This aims to improve workflow performance and make dependency management more seamless. To disable this automatic caching, set <code>package-manager-cache: false</code></p> <pre lang="yaml"><code>steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: package-manager-cache: false </code></pre> <ul> <li>Upgrade action to use node24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1325">actions/setup-node#1325</a></li> </ul> <p>Make sure your runner is on version v2.327.1 or later to ensure compatibility with this release. <a href="https://github.com/actions/runner/releases/tag/v2.327.1">See Release Notes</a></p> <h3>Dependency Upgrades</h3> <ul> <li>Upgrade <code>@octokit/request-error</code> and <code>@actions/github</code> by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1227">actions/setup-node#1227</a></li> <li>Upgrade uuid from 9.0.1 to 11.1.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1273">actions/setup-node#1273</a></li> <li>Upgrade undici from 5.28.5 to 5.29.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1295">actions/setup-node#1295</a></li> <li>Upgrade form-data to bring in fix for critical vulnerability by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1332">actions/setup-node#1332</a></li> <li>Upgrade actions/checkout from 4 to 5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-node/pull/1345">actions/setup-node#1345</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/priya-kinthali"><code>@priya-kinthali</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-node/pull/1348">actions/setup-node#1348</a></li> <li><a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-node/pull/1325">actions/setup-node#1325</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-node/compare/v4...v5.0.0">https://github.com/actions/setup-node/compare/v4...v5.0.0</a></p> <h2>v4.4.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/setup-node/commit/48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e"><code>48b55a0</code></a> Update Node.js versions in versions.yml and bump package to v6.4.0 (<a href="https://redirect.github.com/actions/setup-node/issues/1533">#1533</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/ab72c7e7eba0eaa11f8cab0f5679243900c2cac9"><code>ab72c7e</code></a> Upgrade <a href="https://github.com/actions"><code>@actions</code></a> dependencies (<a href="https://redirect.github.com/actions/setup-node/issues/1525">#1525</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/53b83947a5a98c8d113130e565377fae1a50d02f"><code>53b8394</code></a> Bump minimatch from 3.1.2 to 3.1.5 (<a href="https://redirect.github.com/actions/setup-node/issues/1498">#1498</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/54045abd5dcd3b0fee9ca02fa24c57545834c9cc"><code>54045ab</code></a> Scope test lockfiles by package manager and update cache tests (<a href="https://redirect.github.com/actions/setup-node/issues/1495">#1495</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/c882bffdbd4df51ace6b940023952e8669c9932a"><code>c882bff</code></a> Replace uuid with crypto.randomUUID() (<a href="https://redirect.github.com/actions/setup-node/issues/1378">#1378</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/774c1d62961e73038a114d59c8847023c003194d"><code>774c1d6</code></a> feat(node-version-file): support parsing <code>devEngines</code> field (<a href="https://redirect.github.com/actions/setup-node/issues/1283">#1283</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/efcb663fc60e97218a2b2d6d827f7830f164739e"><code>efcb663</code></a> fix: remove hardcoded bearer (<a href="https://redirect.github.com/actions/setup-node/issues/1467">#1467</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/d02c89dce7e1ba9ef629ce0680989b3a1cc72edb"><code>d02c89d</code></a> Fix npm audit issues (<a href="https://redirect.github.com/actions/setup-node/issues/1491">#1491</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/6044e13b5dc448c55e2357c09f80417699197238"><code>6044e13</code></a> Docs: bump actions/checkout from v5 to v6 (<a href="https://redirect.github.com/actions/setup-node/issues/1468">#1468</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/8e494633d082d609d1e9ff931be32f8a44f1f657"><code>8e49463</code></a> Fix README typo (<a href="https://redirect.github.com/actions/setup-node/issues/1226">#1226</a>)</li> <li>Additional commits viewable in <a href="https://github.com/actions/setup-node/compare/v4...v6">compare view</a></li> </ul> </details> <br /> Updates `actions/upload-artifact` from 4 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>v7 What's new</h2> <h3>Direct Uploads</h3> <p>Adds support for uploading single files directly (unzipped). Callers can set the new <code>archive</code> parameter to <code>false</code> to skip zipping the file during upload. Right now, we only support single files. The action will fail if the glob passed resolves to multiple files. The <code>name</code> parameter is also ignored with this setting. Instead, the name of the artifact will be the name of the uploaded file.</p> <h3>ESM</h3> <p>To support new versions of the <code>@actions/*</code> packages, we've upgraded the package to ESM.</p> <h2>What's Changed</h2> <ul> <li>Add proxy integration test by <a href="https://github.com/Link"><code>@Link</code></a>- in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> <li>Upgrade the module to ESM and bump dependencies by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/762">actions/upload-artifact#762</a></li> <li>Support direct file uploads by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/764">actions/upload-artifact#764</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Link"><code>@Link</code></a>- made their first contribution in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v6...v7.0.0">https://github.com/actions/upload-artifact/compare/v6...v7.0.0</a></p> <h2>v6.0.0</h2> <h2>v6 - What's new</h2> <blockquote> <p>[!IMPORTANT] actions/upload-artifact@v6 now runs on Node.js 24 (<code>runs.using: node24</code>) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.</p> </blockquote> <h3>Node.js 24</h3> <p>This release updates the runtime to Node.js 24. v5 had preliminary support for Node.js 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.</p> <h2>What's Changed</h2> <ul> <li>Upload Artifact Node 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/719">actions/upload-artifact#719</a></li> <li>fix: update <code>@actions/artifact</code> for Node.js 24 punycode deprecation by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/744">actions/upload-artifact#744</a></li> <li>prepare release v6.0.0 for Node.js 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/745">actions/upload-artifact#745</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0">https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0</a></p> <h2>v5.0.0</h2> <h2>What's Changed</h2> <p><strong>BREAKING CHANGE:</strong> this update supports Node <code>v24.x</code>. This is not a breaking change per-se but we're treating it as such.</p> <ul> <li>Update README.md by <a href="https://github.com/GhadimiR"><code>@GhadimiR</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/681">actions/upload-artifact#681</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/712">actions/upload-artifact#712</a></li> <li>Readme: spell out the first use of GHES by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/727">actions/upload-artifact#727</a></li> <li>Update GHES guidance to include reference to Node 20 version by <a href="https://github.com/patrikpolyak"><code>@patrikpolyak</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/725">actions/upload-artifact#725</a></li> <li>Bump <code>@actions/artifact</code> to <code>v4.0.0</code></li> <li>Prepare <code>v5.0.0</code> by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/734">actions/upload-artifact#734</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/upload-artifact/commit/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"><code>043fb46</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/797">#797</a> from actions/yacaovsnc/update-dependency</li> <li><a href="https://github.com/actions/upload-artifact/commit/634250c1388765ea7ed0f053e636f1f399000b94"><code>634250c</code></a> Include changes in typespec/ts-http-runtime 0.3.5</li> <li><a href="https://github.com/actions/upload-artifact/commit/e454baaac2be505c9450e11b8f3215c6fc023ce8"><code>e454baa</code></a> Readme: bump all the example versions to v7 (<a href="https://redirect.github.com/actions/upload-artifact/issues/796">#796</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/74fad66b98a6d799dc004d3353ccd0e6f6b2530e"><code>74fad66</code></a> Update the readme with direct upload details (<a href="https://redirect.github.com/actions/upload-artifact/issues/795">#795</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"><code>bbbca2d</code></a> Support direct file uploads (<a href="https://redirect.github.com/actions/upload-artifact/issues/764">#764</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/589182c5a4cec8920b8c1bce3e2fab1c97a02296"><code>589182c</code></a> Upgrade the module to ESM and bump dependencies (<a href="https://redirect.github.com/actions/upload-artifact/issues/762">#762</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/47309c993abb98030a35d55ef7ff34b7fa1074b5"><code>47309c9</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/754">#754</a> from actions/Link-/add-proxy-integration-tests</li> <li><a href="https://github.com/actions/upload-artifact/commit/02a8460834e70dab0ce194c64360c59dc1475ef0"><code>02a8460</code></a> Add proxy integration test</li> <li><a href="https://github.com/actions/upload-artifact/commit/b7c566a772e6b6bfb58ed0dc250532a479d7789f"><code>b7c566a</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/745">#745</a> from actions/upload-artifact-v6-release</li> <li><a href="https://github.com/actions/upload-artifact/commit/e516bc8500aaf3d07d591fcd4ae6ab5f9c391d5b"><code>e516bc8</code></a> docs: correct description of Node.js 24 support in README</li> <li>Additional commits viewable in <a href="https://github.com/actions/upload-artifact/compare/v4...v7">compare view</a></li> </ul> </details> <br /> Updates `finloop/vega-virtual-device-action` from ed210f760d5aec5cd3f7e54e1529d353b4e66a11 to fe151d9a807b64e2838080e16a9ff2ea55ee48f6 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/finloop/vega-virtual-device-action/commit/fe151d9a807b64e2838080e16a9ff2ea55ee48f6"><code>fe151d9</code></a> Merge pull request <a href="https://redirect.github.com/finloop/vega-virtual-device-action/issues/18">#18</a> from finloop/claude/stupefied-easley-603dfc</li> <li><a href="https://github.com/finloop/vega-virtual-device-action/commit/f1cea162e668d824e70110b1bd791eb17824dcf2"><code>f1cea16</code></a> Bump Vega SDK 0.22.6759 -> 0.23.8128</li> <li>See full diff in <a href="https://github.com/finloop/vega-virtual-device-action/compare/ed210f760d5aec5cd3f7e54e1529d353b4e66a11...fe151d9a807b64e2838080e16a9ff2ea55ee48f6">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
10ecc9e53d |
fix(release): auto-sync the npm 'next' dist-tag to the highest published version (#357)
Auto-sync the npm `next` dist-tag to the highest published version (prerelease or stable) so it stops drifting behind `latest`. - New dependency-free `scripts/sync-next-dist-tag.mjs` (SemVer comparator verified against the `semver` lib in `scripts/sync-next-dist-tag.test.mjs`). - Runs as a post-publish step in `publish.yml` and `publish-next.yml` (with `continue-on-error` so a transient registry blip never fails an already-successful release), plus a manual `Sync 'next' dist-tag` workflow with a dry-run toggle. - Only ever advances `next` (never regresses it) to survive a stale post-publish packument read. - Package-name arg parsing rejects flag/blank tokens so the scoped `@swmansion/argent` default reliably wins. |
||
|
|
0bed3624a0 |
feat(vega): Amazon Vega (Fire TV) platform support (#366)
## Summary
Adds **Vega (Amazon Fire TV)** as a third Argent platform alongside iOS
and Android. Vega apps are React Native 0.72 + Hermes running on a QEMU
**Virtual Device (VVD)**, driven by a **D-pad tv-remote rather than
touch**. This PR extends Argent's platform abstraction so the existing
tool surface gates correctly on `vega`, and adds the Vega-specific tools
needed to actually drive a Fire TV app end to end.
### Platform abstraction
- `Platform` gains `vega`; `DeviceKind` gains `vvd` (the QEMU VVD);
`ToolCapability` gains a `vega { vvd, device }` block; `ToolDependency`
gains `vega`.
- `assertSupported()` selects the `vega` capability matrix for Vega
devices; `dispatchByPlatform()` gains an optional `vega` branch, and a
Vega dispatch with no branch throws `NotImplementedOnPlatformError`
(501).
- New `vega-cli` util (modeled on `adb.ts`) resolves `vega`/`kepler`
from `PATH` or `~/vega/bin` with the same hardened exec/quoting;
`check-deps` resolves and hints the `vega` dependency.
- The `simulator-server` blueprint is never wired for `vega` (Vega uses
`adb` + the `vega`/`kepler` CLI, not the proprietary binary).
### How Vega is wired
- **Input / screenshot / describe** go over `adb` (input via
`inputd-cli`, screenshots host-side via the Android emulator console,
element tree via the on-device automation toolkit).
- **App lifecycle** goes over the `vega`/`kepler` CLI.
- v1 is **Virtual-Device-only**: every Vega capability is declared
`vvd`. Physical Fire TV is out of scope and untested — the tool-server
does not connect to or detect hardware, so `resolveDevice` classifies
every `amazon-` serial as a VVD by shape. A physical device is therefore
not classified as `device` and not rejected at the capability gate;
supporting and gating real hardware is deferred to a version where it
can be tested.
- Vega runs **React Native 0.72**, which predates the new React Native
DevTools. The CDP/Hermes debugger surface (`debugger-*`, React/native
profilers) depends on integration points the new DevTools provides, so
**debugging and profiling are out of scope for this PR** — Vega
profiling/crash analysis is handled by the
`amazon-devices-buildertools-mcp` server instead.
### Also includes
- `argent-vega` skill documenting the full Vega workflow (target
discovery, the describe → compute-path → `tv-remote` navigation loop,
text injection).
- Vega VVD end-to-end CI workflow.
## Feature support by platform
Legend: ✅ supported · ❌ not supported · ⚠️ degraded / conditional · —
not applicable
| Feature (tool) | iOS | Android | Chromium | **Vega (VVD)** | Vega
notes |
|---|:---:|:---:|:---:|:---:|---|
| **Device discovery** (`list-devices`) | ✅ | ✅ | ✅ | **✅** | Tagged
`platform:"vega"`, `kind:"vvd"`; pass `serial` as `udid` |
| **Boot / start device** | ✅ | ✅ | — | ✅ | |
| **Launch app** (`launch-app`) | ✅ | ✅ | ✅ | **✅** | `bundleId` =
interactive-component app id from `manifest.toml` |
| **Restart app** (`restart-app`) | ✅ | ✅ | ✅ | **✅** | Terminate +
relaunch |
| **Reinstall app** (`reinstall-app`) | ✅ | ✅ | ❌ | **✅** | Uninstall +
install a `.vpkg` |
| **Screenshot** (`screenshot`) | ✅ | ✅ | ✅ | **✅** | Host-side via
emulator console (needs `adb`); rotation ignored (fixed landscape) |
| **Describe / element tree** (`describe`) | ✅ | ✅ | ✅ | **✅** |
On-device automation toolkit; surfaces `[focused]`/`[selected]` +
normalized frames |
| **Touch gestures** (`gesture-tap`/`swipe`/`scroll`/`drag`/`custom`) |
✅ | ✅ | ✅ | **❌** | Vega is tv-remote-driven; gestures error — use
`tv-remote` |
| **Pinch / rotate gesture** (`gesture-pinch`/`gesture-rotate`) | ✅ | ❌
| ⚠️ | **❌** | N/A on a TV |
| **Remote / D-pad** (`tv-remote`) | — | — | — | **✅** | Vega-only tool;
single key, repeat, or a whole path in one call |
| **Hardware buttons** (`button`) | ✅ | ✅ | — | **❌** | Remote keys live
in `remote` instead |
| **Text input** (`keyboard`) | ✅ | ✅ | ✅ | **✅** | Whole-string
injection via `inputd-cli send_text` |
| **Rotate orientation** (`rotate`) | ✅ | ✅ | — | **❌** | TV framebuffer
is fixed landscape |
| **Open URL / deep link** (`open-url`) | ✅ | ✅ | ✅ | **❌** | Not wired
on Vega |
| **JS debugger** (`debugger-*`) | ✅ | ✅ | ✅ | **❌** | RN 0.72 predates
the new RN DevTools — out of scope for this PR |
| **Native devtools / AX** (`native-describe-screen`, etc.) | ✅ | ❌ | —
| **❌** | iOS-only |
| **Native profiler** (`native-profiler-*`) | ✅ | ✅ | — | **❌** | Use
`amazon-devices-buildertools-mcp` (Perfetto) instead |
| **React profiler** (`react-profiler-*`) | ✅ | ✅ | — | **❌** | Out of
scope; RN 0.72 / new RN DevTools not supported |
| **Screenshot diff** (`screenshot-diff`) | ✅ | ✅ | ✅ | **✅** |
Host-side image comparison; platform-agnostic |
| **Flows record/replay** (`flow-*`) | ✅ | ✅ | ✅ | **✅** | Records any
tool-call sequence |
## Test plan
- [x] `list-devices` surfaces connected Vega devices (`platform:"vega"`,
`kind:"vvd"`)
- [x] `launch-app`, `restart-app`, `reinstall-app`,
`list-installed-apps` work on Vega
- [x] `tv-remote` drives focus (single key, `repeat`, and multi-key
path) and `keyboard` injects whole strings
- [x] `screenshot` returns a frame via the host-side VVD path
- [x] `describe` returns the on-screen element tree with `[focused]`
state and normalized coordinates
- [x] Vega VVD e2e CI workflow passes
- [ ] Test multiple platforms being driven at the same time
- [x] Android + Vega
- [ ] iOS + Vega
- [ ] chrome + Vega
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
b165bc39ee |
ci(wayland-e2e): poll for a warm screenshot instead of a single fixed sleep (#379)
## Problem
The **Boot + drive AVD on headless Weston** job's *"Screenshot returns
real pixels"* step takes a single screenshot 15s after `boot_completed`
and asserts it is > 20 KB. That single shot races **two** independent
warm-up delays:
1. SurfaceFlinger painting the lockscreen UI, and
2. the simulator-server's screenshot stream, which only starts warming
on the **first capture request**.
On slow/contended runners the one-and-only capture comes back as either:
- `{"error":"...no image to export"}` — a cold stream, response has no
`.data` envelope, so the step's `json.load(...)['data']` raises
`KeyError: 'data'`; or
- an all-zero framebuffer that PNG-compresses to ~3–7 KB and fails the
`> 20 KB` check.
Either way the step fails even though the boot was healthy. This is
intermittent — the same commit passes most runs and fails some.
## Fix
Replace the single fixed `sleep 15` + one capture with a **bounded
poll** (15 attempts, 4s apart). Each request also warms the stream, and
the loop exits as soon as a frame exceeds 20 KB. A cold-stream error
response is tolerated and retried rather than aborting the step.
Genuinely broken framebuffers still fail, now with per-attempt
diagnostics in the log.
- Healthy boots pass in the first attempt or two (no slower than before
in the common case).
- Worst-case added headroom is ~60s, only spent when the framebuffer
never warms — which is a real failure worth surfacing.
## Verification
The workflow file is unchanged elsewhere; only this step's `run:` script
changed.
- `prettier --check` passes on the workflow file.
- Extracted the exact `run:` script and ran it against a mock `curl`
covering: cold-error → all-zero frame → real frame (exits 0 on the real
frame); always-cold (exits 1 after 15 attempts); always all-zero
framebuffer (exits 1). Exit codes confirmed 0 / 1 respectively.
|
||
|
|
dc0930d50c |
ci: prevent package-lock drift + add repo-hygiene checks (#362)
## Why We hit a mismatch between a locally-generated `package-lock.json` and the one on `main`. `npm ci` validates the lockfile loosely and accepts a stale lock that a fresh `npm install` would rewrite, so the drift slipped through. This adds CI to catch that, plus a few related common-mistake guards. ## What **`Lockfile` workflow** (Node 20) — runs `npm install --package-lock-only --ignore-scripts` and fails if `package-lock.json` changes. This is the gap `npm ci` leaves: it asserts the lock is exactly what npm produces, not merely "consistent enough". **`Repo Hygiene` workflow** — three fast static guards: - **Merge-conflict markers** — fail on any `<<<<<<<` / `=======` / `>>>>>>>` left in a tracked file. - **Focused tests** — fail on `describe/it/test/bench.only` in `*.test.ts(x)` / `*.spec.ts(x)`. Uses a POSIX-ERE word-boundary pattern so it actually fires under `git grep -E` (a `\b` pattern silently matches nothing there). - **Workspace version sync** — fail when `packages/*` versions drift, via `scripts/check-workspace-versions.mjs` (also `npm run check:versions`). To make versions consistent, `@argent/ui` is bumped `0.0.1 → 0.12.0` (it is `private`, so no publish impact). ## Verification (Node 20 / npm 10.8.2, matching CI) - Lockfile check: no false positive on `main`; idempotent; catches a tampered lock. - Merge-marker & `.only` greps: proven to catch real violations and reject lookalikes (`await.only`, `audit.only`, `foo.it.only`); clean across the repo. - `check-workspace-versions.mjs`: reports all packages at `0.12.0`; passes `eslint` and `typecheck:scripts`. - All new/changed files pass `prettier --check`; both workflow YAMLs parse. ## Note: npm version coupling The check runs on Node 20 (npm 10.8.2), matching the committed lock's format. npm 11 (Node 24) regenerates the same lock minus cosmetic `"peer": true` markers — no resolution change, but a contributor who regenerates on npm 11 would trip the strict diff. The repo pins no toolchain today (CI mixes Node 20 and 24). Pinning npm/Node repo-wide would fully close this; left out of scope here — happy to add it as a follow-up. |
||
|
|
b02ad64527 |
chore(ci)(deps): bump the github-actions group across 1 directory with 2 updates (#309)
Bumps the github-actions group with 2 updates in the / directory: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/setup-java](https://github.com/actions/setup-java). Updates `actions/upload-artifact` from 4 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>v7 What's new</h2> <h3>Direct Uploads</h3> <p>Adds support for uploading single files directly (unzipped). Callers can set the new <code>archive</code> parameter to <code>false</code> to skip zipping the file during upload. Right now, we only support single files. The action will fail if the glob passed resolves to multiple files. The <code>name</code> parameter is also ignored with this setting. Instead, the name of the artifact will be the name of the uploaded file.</p> <h3>ESM</h3> <p>To support new versions of the <code>@actions/*</code> packages, we've upgraded the package to ESM.</p> <h2>What's Changed</h2> <ul> <li>Add proxy integration test by <a href="https://github.com/Link"><code>@Link</code></a>- in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> <li>Upgrade the module to ESM and bump dependencies by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/762">actions/upload-artifact#762</a></li> <li>Support direct file uploads by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/764">actions/upload-artifact#764</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Link"><code>@Link</code></a>- made their first contribution in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v6...v7.0.0">https://github.com/actions/upload-artifact/compare/v6...v7.0.0</a></p> <h2>v6.0.0</h2> <h2>v6 - What's new</h2> <blockquote> <p>[!IMPORTANT] actions/upload-artifact@v6 now runs on Node.js 24 (<code>runs.using: node24</code>) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.</p> </blockquote> <h3>Node.js 24</h3> <p>This release updates the runtime to Node.js 24. v5 had preliminary support for Node.js 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.</p> <h2>What's Changed</h2> <ul> <li>Upload Artifact Node 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/719">actions/upload-artifact#719</a></li> <li>fix: update <code>@actions/artifact</code> for Node.js 24 punycode deprecation by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/744">actions/upload-artifact#744</a></li> <li>prepare release v6.0.0 for Node.js 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/745">actions/upload-artifact#745</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0">https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0</a></p> <h2>v5.0.0</h2> <h2>What's Changed</h2> <p><strong>BREAKING CHANGE:</strong> this update supports Node <code>v24.x</code>. This is not a breaking change per-se but we're treating it as such.</p> <ul> <li>Update README.md by <a href="https://github.com/GhadimiR"><code>@GhadimiR</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/681">actions/upload-artifact#681</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/712">actions/upload-artifact#712</a></li> <li>Readme: spell out the first use of GHES by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/727">actions/upload-artifact#727</a></li> <li>Update GHES guidance to include reference to Node 20 version by <a href="https://github.com/patrikpolyak"><code>@patrikpolyak</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/725">actions/upload-artifact#725</a></li> <li>Bump <code>@actions/artifact</code> to <code>v4.0.0</code></li> <li>Prepare <code>v5.0.0</code> by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/734">actions/upload-artifact#734</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/upload-artifact/commit/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"><code>043fb46</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/797">#797</a> from actions/yacaovsnc/update-dependency</li> <li><a href="https://github.com/actions/upload-artifact/commit/634250c1388765ea7ed0f053e636f1f399000b94"><code>634250c</code></a> Include changes in typespec/ts-http-runtime 0.3.5</li> <li><a href="https://github.com/actions/upload-artifact/commit/e454baaac2be505c9450e11b8f3215c6fc023ce8"><code>e454baa</code></a> Readme: bump all the example versions to v7 (<a href="https://redirect.github.com/actions/upload-artifact/issues/796">#796</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/74fad66b98a6d799dc004d3353ccd0e6f6b2530e"><code>74fad66</code></a> Update the readme with direct upload details (<a href="https://redirect.github.com/actions/upload-artifact/issues/795">#795</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"><code>bbbca2d</code></a> Support direct file uploads (<a href="https://redirect.github.com/actions/upload-artifact/issues/764">#764</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/589182c5a4cec8920b8c1bce3e2fab1c97a02296"><code>589182c</code></a> Upgrade the module to ESM and bump dependencies (<a href="https://redirect.github.com/actions/upload-artifact/issues/762">#762</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/47309c993abb98030a35d55ef7ff34b7fa1074b5"><code>47309c9</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/754">#754</a> from actions/Link-/add-proxy-integration-tests</li> <li><a href="https://github.com/actions/upload-artifact/commit/02a8460834e70dab0ce194c64360c59dc1475ef0"><code>02a8460</code></a> Add proxy integration test</li> <li><a href="https://github.com/actions/upload-artifact/commit/b7c566a772e6b6bfb58ed0dc250532a479d7789f"><code>b7c566a</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/745">#745</a> from actions/upload-artifact-v6-release</li> <li><a href="https://github.com/actions/upload-artifact/commit/e516bc8500aaf3d07d591fcd4ae6ab5f9c391d5b"><code>e516bc8</code></a> docs: correct description of Node.js 24 support in README</li> <li>Additional commits viewable in <a href="https://github.com/actions/upload-artifact/compare/v4...v7">compare view</a></li> </ul> </details> <br /> Updates `actions/setup-java` from 4 to 5 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/setup-java/releases">actions/setup-java's releases</a>.</em></p> <blockquote> <h2>v5.0.0</h2> <h2>What's Changed</h2> <h3>Breaking Changes</h3> <ul> <li>Upgrade to node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/888">actions/setup-java#888</a></li> </ul> <p>Make sure your runner is updated to this version or newer to use this release. v2.327.1 <a href="https://github.com/actions/runner/releases/tag/v2.327.1">Release Notes</a></p> <h3>Dependency Upgrades</h3> <ul> <li>Upgrade Publish Immutable Action by <a href="https://github.com/HarithaVattikuti"><code>@HarithaVattikuti</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/798">actions/setup-java#798</a></li> <li>Upgrade eslint-plugin-jest from 27.9.0 to 28.11.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-java/pull/730">actions/setup-java#730</a></li> <li>Upgrade undici from 5.28.5 to 5.29.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-java/pull/833">actions/setup-java#833</a></li> <li>Upgrade form-data to bring in fix for critical vulnerability by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/887">actions/setup-java#887</a></li> <li>Upgrade actions/checkout from 4 to 5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/setup-java/pull/896">actions/setup-java#896</a></li> </ul> <h3>Bug Fixes</h3> <ul> <li>Prevent default installation of JetBrains pre-releases by <a href="https://github.com/priyagupta108"><code>@priyagupta108</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/859">actions/setup-java#859</a></li> <li>Improve Error Handling for Setup-Java Action to Help Debug Intermittent Failures by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/848">actions/setup-java#848</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-java/pull/848">actions/setup-java#848</a></li> <li><a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-java/pull/888">actions/setup-java#888</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-java/compare/v4...v5.0.0">https://github.com/actions/setup-java/compare/v4...v5.0.0</a></p> <h2>v4.8.0</h2> <h2>What's Changed</h2> <ul> <li>License and Audit Fixes by <a href="https://github.com/HarithaVattikuti"><code>@HarithaVattikuti</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/960">actions/setup-java#960</a></li> <li>Update SapMachine URLs by <a href="https://github.com/RealCLanger"><code>@RealCLanger</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/965">actions/setup-java#965</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-java/compare/v4...v4.8.0">https://github.com/actions/setup-java/compare/v4...v4.8.0</a></p> <h2>v4.7.1</h2> <h2>What's Changed</h2> <h3>Documentation changes</h3> <ul> <li>Add Documentation to Recommend Using GraalVM JDK 17 Version to 17.0.12 to Align with GFTC License Terms by <a href="https://github.com/aparnajyothi-y"><code>@aparnajyothi-y</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/704">actions/setup-java#704</a></li> <li>Remove duplicated GraalVM section in documentation by <a href="https://github.com/Marcono1234"><code>@Marcono1234</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/716">actions/setup-java#716</a></li> </ul> <h3>Dependency updates:</h3> <ul> <li>Upgrade <code>@action/cache</code> from 4.0.0 to 4.0.2 by <a href="https://github.com/aparnajyothi-y"><code>@aparnajyothi-y</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/766">actions/setup-java#766</a></li> <li>Upgrade <code>@actions/glob</code> from 0.4.0 to 0.5.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/744">actions/setup-java#744</a></li> <li>Upgrade ts-jest from 29.1.2 to 29.2.5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/743">actions/setup-java#743</a></li> <li>Upgrade <code>@action/cache</code> to 4.0.3 by <a href="https://github.com/aparnajyothi-y"><code>@aparnajyothi-y</code></a> in <a href="https://redirect.github.com/actions/setup-java/pull/773">actions/setup-java#773</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-java/compare/v4...v4.7.1">https://github.com/actions/setup-java/compare/v4...v4.7.1</a></p> <h2>v4.7.0</h2> <h2>What's Changed</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/setup-java/commit/be666c2fcd27ec809703dec50e508c2fdc7f6654"><code>be666c2</code></a> Chore: Version Update and Checkout Update to v6 (<a href="https://redirect.github.com/actions/setup-java/issues/973">#973</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/f7a6fefba97e80156950e16f2a9dafc8579b7d05"><code>f7a6fef</code></a> Bump actions/checkout from 5 to 6 (<a href="https://redirect.github.com/actions/setup-java/issues/961">#961</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/d81c4e45f3ac973cc936d79104023e20054ba578"><code>d81c4e4</code></a> Upgrade <code>@actions/cache</code> to v5 (<a href="https://redirect.github.com/actions/setup-java/issues/968">#968</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/1b1bbe1085cb6ab21b5b19b7bebc091a9430026a"><code>1b1bbe1</code></a> readme update (<a href="https://redirect.github.com/actions/setup-java/issues/972">#972</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/5d7b2146334bacf88728daaa70414a99f5164e0f"><code>5d7b214</code></a> Retry on HTTP 522 Connection timed out (<a href="https://redirect.github.com/actions/setup-java/issues/964">#964</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/f2beeb24e141e01a676f977032f5a29d81c9e27e"><code>f2beeb2</code></a> Bump actions/publish-action from 0.3.0 to 0.4.0 (<a href="https://redirect.github.com/actions/setup-java/issues/912">#912</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/4e7e684fbb6e33f88ecb2cf1e6b3797739cf499b"><code>4e7e684</code></a> feat: Add support for <code>.sdkmanrc</code> file in <code>java-version-file</code> parameter (<a href="https://redirect.github.com/actions/setup-java/issues/736">#736</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/46c56d6f92c88cf540acf95a12a4a41197499222"><code>46c56d6</code></a> Add GitHub Token Support for GraalVM and Refactor Code (<a href="https://redirect.github.com/actions/setup-java/issues/849">#849</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/66b945764b75604b3cfd644c3ada5232cf6c90c6"><code>66b9457</code></a> Update SapMachine URLs (<a href="https://redirect.github.com/actions/setup-java/issues/955">#955</a>)</li> <li><a href="https://github.com/actions/setup-java/commit/6ba5449b7dcda52941806a19f0cf626b6420191e"><code>6ba5449</code></a> Enhance error logging for network failures to include endpoint/IP details, ad...</li> <li>Additional commits viewable in <a href="https://github.com/actions/setup-java/compare/v4...v5">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ignacy Łątka <latkaignacy@gmail.com> |
||
|
|
acfe80ab6b |
chore: add ESLint with typescript-eslint (#349)
## What Adds type-aware linting to the workspace. Until now the only static gate on JS/TS was Prettier (formatting) and `tsc` — there was no linter, so the whole class of bugs `tsc` doesn't catch (floating promises, misused promises, throwing non-Errors, unsafe stringification, dead code) went unguarded across ~450 TS files. - **`eslint.config.mjs`** — flat config. `recommendedTypeChecked` runs on the TS sources via an explicit `project` list (each package keeps tests in a separate `tsconfig.test.json` that `projectService` can't auto-discover, so it's listed explicitly). Type-checked rules are disabled for the loosely-typed plain-JS build/dev scripts. The `argent-private` submodule and build artifacts are ignored (mirrors `.prettierignore`). - **`lint` / `lint:fix`** npm scripts. - **`.github/workflows/lint.yml`** — builds the workspace (type-aware rules need the referenced projects' declarations) then runs `eslint .`. Node 24 (ESLint 10 requires `^20.19 || ^22.13 || >=24`). ## Rule calibration This is an adoption PR, so it's deliberately calibrated rather than maximal: - **Kept on (bug-catchers):** `no-floating-promises`, `no-misused-promises` (with `checksVoidReturn: false` to drop the noisy callback variant), `only-throw-error`, `no-base-to-string`, `prefer-promise-reject-errors`, `no-unused-vars` (`_`-prefix escape hatch), plus the ESLint recommended set. - **Parked off as documented debt:** the high-volume style/`any`-family rules — `no-unsafe-*`, `require-await`, `no-unnecessary-type-assertion`, `restrict-template-expressions`, `unbound-method`, `no-explicit-any`, etc. These flag broad classes of pre-existing code and are out of scope here; each is commented as a ratchet target for follow-up passes. All findings from the kept-on rules are fixed in this PR (dead code/imports, error-cause chaining, **3 genuine floating promises**, non-Error throws, unsafe stringification). Stale unused `eslint-disable` directives left over in the tree were removed by `--fix`. ## Verification Locally, all green: `eslint .` (0 errors), `tsc --build`, `typecheck:tests`/`typecheck:scripts`, the full `vitest` suite (no behavior change from the fixes), and `prettier --check`. ## Not included (intentionally) Other static-analysis gates discussed (CodeQL, dependency-audit, ShellCheck, actionlint) are out of scope for this PR. |
||
|
|
9b027096d7 |
feat: Electron support (#247)
## Summary Adds Electron (Chromium + CDP) as a third device platform alongside iOS and Android. Argent can boot an Electron app, list it next to simulators/emulators, drive it with the same tool surface where it makes sense — taps, swipes, keyboard, screenshots, describe, navigation — and debug it via the four ported `debugger-*` tools. Mirrors the sim-server architecture in TypeScript: a per-device server with HTTP API, MJPEG screencast, refcounted CDP screencast, FPS tracker, clipboard, and a WebSocket command bus. ## What works on Electron | group | tools | status | |---|---|---| | Lifecycle | `boot-device` (with `electronAppPath`), `launch-app`, `open-url` | ✅ works | | Vision | `screenshot`, `describe` (DOM walker w/ shadow-DOM + same-origin iframes) | ✅ works | | Input | `gesture-tap`, `gesture-swipe`, `keyboard` (text + named keys) | ✅ works | | Sequencing | `run-sequence` (per-step capability check) | ✅ works | | **JS debugger** | `debugger-connect`, `debugger-status`, `debugger-evaluate`, `debugger-log-registry` | ✅ **direct CDP** | | Discovery | `list-devices` (Electron entries shown alongside sims/emulators) | ✅ works | | Cleanup | `stop-simulator-server`, `stop-all-simulator-servers`, `stop-metro` | ✅ works | | Misc | `update-argent`, `dismiss-update`, `gather-workspace-data`, all `flow-*` recording tools | ✅ works | ### Debugger port — how it routes Dispatch lives in `tools/debugger/debugger-service-ref.ts`: an Electron device id (`electron-cdp-<port>`) routes to a new `ElectronJsRuntimeDebugger` blueprint that wraps the page CDP session already opened by `boot-device`. Everything else stays on the Metro-driven `JsRuntimeDebugger` blueprint. The new blueprint exposes the same `JsRuntimeDebuggerApi` shape so tool code is unchanged. `Runtime.consoleAPICalled` events feed into the existing `LogFileWriter` + cluster pipeline — `debugger-log-registry` returns the same shape on Electron as it does on Metro. ## What is cleanly rejected with `Tool 'X' is not supported on electron app` HTTP capability gate declines these up-front (HTTP 400): | group | tools | |---|---| | Mobile gestures | `gesture-pinch`, `gesture-rotate`, `gesture-custom` | | Mobile controls | `button`, `rotate` | | App lifecycle | `restart-app`, `reinstall-app` | | iOS-native | `paste`, all `native-*`, `native-profiler-*` | | RN-only debugger | `debugger-component-tree`, `debugger-reload-metro`, `debugger-inspect-element` | | RN-only network | `view-network-logs`, `view-network-request-details` | | RN-only profiler | all `react-profiler-*` (incl. `component-source`), all `profiler-*` query / combined tools | ## Verification history Three commits resolved findings from two parallel verification swarms: - `507b295` — swarm v1 fixes: early-disconnect race in new blueprint, NaN timestamp crash, disconnect listener leak, missing test coverage for 13 of 15 lockouts, missing disconnect→terminated propagation test, stale skill + tool descriptions. - `dd0f185` — pre-existing crash: `spawn()` in boot-electron lacked an `'error'` event handler; ENOENT escalated to `uncaughtException`. + 4 regression test cases. - `95161ab` — swarm v2 fixes: orphan promise rejection when pid-less spawn fires deferred `'error'`, misleading rationale comment, stale cross-skill references calling this "Metro debugging only". Branch was also cherry-picked onto v0.8.0 (`e1327b4`) to pick up #245. ## Follow-ups left for separate PRs - **CPU profiling on Electron** — Chromium's `Profiler.start`/`Profiler.stop` work fine; only the V8 sample format differs from Hermes'. A normalizer in the query layer would unlock `react-profiler-cpu-summary` + `profiler-cpu-query` for arbitrary Electron renderers (without the React-DevTools commit half). - **Source-mapped element inspection on Electron** — `debugger-inspect-element` could be rebuilt on top of `DOM.getNodeForLocation` + CSS source maps, but it'd be a from-scratch implementation rather than a port. ## New sim-server-equivalent HTTP surface Mounted at `/electron-server/:deviceId/*` — not advertised to MCP, consumed by preview UIs and integration tests: - `GET /viewport`, `GET /stream.mjpeg` (multipart-JPEG screencast) - `POST /api/screenshot`, `/api/clipboard/text`, `/api/fps`, `/api/navigate`, `/api/reload`, `/api/history/back`, `/api/history/forward` - `WS /ws` (input commands + event bus) Sharp is loaded as an optional dependency — screenshots still work without it (no rotate/resize, just raw PNG). ## Test plan - [x] 786 vitest cases pass; new coverage for the dispatch helper, blueprint factory edge cases (early disconnect, NaN timestamp, dispose symmetry), 15-tool table-driven lockout assertion, and 5 boot-electron spawn-error scenarios. - [x] Build + prettier clean. - [x] E2E swept the original 27 surfaces (11 MCP tools, 8 HTTP endpoints, MJPEG, WebSocket, 6 capability rejections, session cleanup). - [x] E2E swept the debugger surface against a live Electron app — connect / status / evaluate (`1+1`, JSON-stringified globals, `document.title`) / log-registry (clusters + log file). - [x] E2E swept the 17 lockouts — every one returns HTTP 400 with `Tool 'X' is not supported on electron app`. - [x] Two rounds of parallel verification swarm; every real finding addressed. - [ ] Reviewer drives an Electron app end-to-end via MCP and confirms describe / tap / swipe / keyboard / debugger-evaluate land correctly. --------- Co-authored-by: Filip131311 <contact@filipkaminski.com> |
||
|
|
73cf47bae6 |
feat: support arm64 Linux simulator-server binary (#328)
## Summary The only Linux simulator-server binary argent ships today is x86_64 (`simulator-server-argent-linux` — verified: `ELF 64-bit … x86-64`). On arm64 Linux hosts (Lima/UTM VMs on Apple Silicon, arm cloud instances, Raspberry Pi-class devices) the package has no usable binary — the x86_64 ELF would only fail with `ENOEXEC` at spawn time. This PR teaches the whole binary pipeline about a **host platform key**: `process.platform` everywhere, except arm64 Linux which maps to `linux-arm64`. macOS keeps a single `darwin/` dir because its binary is universal (lipo); Linux binaries are single-arch ELFs, so each arch gets its own platform-keyed directory. x86_64 Linux keeps the existing `linux` key for backward compatibility. ## Changes - **`@argent/native-devtools-ios` resolver** — new exported `hostPlatformKey()`; `simulatorServerBinaryPath()` resolves `bin/linux-arm64/` on arm64 Linux and names the searched key in its error message. - **`argent-simulator-server.cjs` dispatcher** (npm `bin` entry) — mirrors the same key (duplicated deliberately; the dispatcher ships as a standalone file). - **`bundle-tools.cjs`** — `linux-arm64` added to the supported host platform dirs; copied into the package when staged, warn-and-skip otherwise (same as x86_64 Linux). - **`scripts/download-simulator-server.sh`** — downloads `simulator-server-argent-linux-arm64` into `bin/linux-arm64/`; tolerates the asset being absent (warn + skip) until the upstream release ships it, so publishing keeps working in the meantime. - **`scripts/dev.cjs`** — copies the current host's binary by key. - **publish / publish-next / build-package-artifact workflows** — `linux-arm64` added to the binary inventory; gated by the existing `ARGENT_REQUIRE_LINUX_BINARY` flag (now covering both Linux keys via a `linux*` glob). ## Tests - Resolver: new arch-aware suite — `linux`+`arm64` → `bin/linux-arm64`, `linux`+`x64` → `bin/linux`, `darwin`+`arm64` → `bin/darwin`, and the missing-binary error names `linux-arm64`. - Dispatcher: end-to-end test faking `process.platform`/`process.arch` in a child process to exercise the real resolution path on any host. - Full suite: 146 files / 1651 tests pass. `tsc --build` clean. - `download-simulator-server.sh` exercised against the live `radon-main` release: downloads darwin + linux, warn-and-skips the not-yet-published arm64 asset. ## Dependencies Companion PR software-mansion/radon#126 adds the `ubuntu-24.04-arm` build leg that publishes `simulator-server-argent-linux-arm64` to `simulator-server-releases`. This PR is safe to merge first (the download script and verify steps tolerate the missing asset); arm64 hosts start working once a release containing the new asset is cut. |
||
|
|
825b731cbf |
Add npm package artifact workflow (#320)
## Summary - Add a manual workflow that checks out `main`, builds the `@swmansion/argent` package using the same asset downloads and runtime checks as publishing, and packs it without publishing to npm. - Upload the generated `.tgz` as the `argent-main-npm-package` GitHub Actions artifact. ## Test plan - `npx prettier --check .github/workflows/build-package-artifact.yml` Made with [Cursor](https://cursor.com) Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
3ccd89f333 |
feat: add native android profiling (#275)
# Android native profiling via in-process Perfetto WASM Connected with: https://github.com/software-mansion/argent-private/pull/16 ## TL;DR Adds **Android** to the native profiler (previously iOS-only) and does it with a brand-new engine: instead of shipping Perfetto's `trace_processor_shell` native binary and forking it per query, we now run **Perfetto's trace-processor compiled to WebAssembly, in-process** inside the tool-server. - **One ~13 MB `trace_processor.wasm`** runs on every OS/arch — no per-platform binary, no subprocess, fully offline. - The native profiler is refactored from an iOS-only implementation into a **platform-dispatch facade** (`platforms/ios.ts` + `platforms/android.ts`) over a new **`profiler-shared/`** layer, so iOS and Android share the aggregator, types, lifecycle, and time-alignment code. - New **release/pack pipeline** fetches + sha256-verifies the WASM bundle from `argent-private-releases`, and bundles it (plus SQL queries and the Perfetto TraceConfig) into the published `@swmansion/argent` package. End-to-end on a 76 MB trace with 1013 jank rows: **6.3 s** total analyze (the older per-hang-subprocess approach was projected at ~47 minutes and never finished within the tool-call deadline). --- ## TESTING MANUALLY To test manually run the following packing command checked-out on this branch: ``` npm run pack:mcp -- argent-@pFornagiel-native-android-devtools-perfetto ``` This will bundle the package with the new binaries. After this PR is fixed and the binaries are resolved from the main release, the `argent-@pFornagiel-native-android-devtools-perfetto` release can be deleted. ## Why Android needed native CPU/hang/memory profiling to reach parity with iOS. The natural engine is Perfetto's trace-processor, but the obvious ways to ship it are both bad: - **`trace_processor_shell` binary** — per-platform (a mac-arm64 pack shipped a mac-arm64-only tarball), pinned an OS/arch matrix, and cost a fresh ~1.3 s subprocess fork **per query**. With one subprocess forked per detected hang, a large trace projected to ~47 minutes. - **`get.perfetto.dev/trace_processor` Python launcher** — adds a runtime Python 3 dependency, resolves "latest" at first run (defeats version pinning), and makes the first analyze slow via a network fetch. The WASM engine is one cross-platform artifact, in-process and warm-cached: the trace is parsed **once** and reused across the whole analyze run plus every drill-down query. --- ## The three big pieces ### 1. In-process Perfetto WASM engine New package surface in `@argent/native-devtools-android`: | File | Role | | --- | --- | | `src/wasm-trace-processor.ts` (+476) | The whole engine: boot, glue patching, warm-cache, fault containment, chunked parse, cell decoding | | `src/perfetto-engine.d.ts` (+49) | Hand-written types for the version-agnostic RPC decoder (imported by path, so tsc can't see its own `.d.ts`) | | `src/errors.ts` (+68) | `TraceProcessorUnavailableError` (`wasm_load_failed` / `wasm_path_invalid`) | | `src/bundled-meta.ts` (+12) | `PERFETTO_VERSION` stamp, regenerated at pack time | | `src/index.ts` | Re-exports + package-root accessors (`traceProcessorQueriesDir`, `traceConfigPath`) | Public surface: `resolveTraceProcessorAssets()`, `queryWarm<Row>(tracePath, sql)`, `ensureTraceProcessorReady(tracePath)`, `disposeWarmEngine(tracePath)`. Highlights of how it works: - **Boots Google's prebuilt web/worker wasm under Node.** Installs a minimal worker scope (`self`, `WorkerGlobalScope`, `location`), reads the wasm bytes, patches the Emscripten glue (`useMemory64=false`, hand the bytes in directly, expose the bridge), and drives it over a `MessageChannel` + `EngineBase` subclass. Glue patches are applied **by unique string anchors that throw if they don't match** — the canary for glue-format drift on a Perfetto bump. - **Warm-engine cache, one per trace path.** LRU with `MAX_WARM_ENGINES = 3` (bounds resident memory at ~26–76 MB trace + wasm heap each), 5-min idle dispose (timer `unref()`'d), no failed-promise caching. - **Fault containment (`fatal` promise).** `EngineBase.fail()` throws synchronously inside the `MessagePort.onmessage` handler (e.g. an RPC framing error); left unhandled it becomes a Node `uncaughtException` and would **kill the entire tool-server**, wiping every device + profiler session. Each engine exposes a `fatal: Promise<never>`; the handler try/catches, rejects `fatal`, evicts the engine, and every load/query is `Promise.race`'d against it — so a contained fault is a clean rejection and the server stays up. - **Chunked trace parse.** Perfetto's RPC ring buffer rejects any frame > 64 MiB, so the trace is fed in 32 MiB chunks before `notifyEof()`. ### 2. Android native profiler + the shared/dispatch refactor **Capture** (`utils/android-profiler/capture.ts`, `detect-app.ts`): spawns `adb shell perfetto --txt -c - --background-wait -o /data/.../argent-<ts>.pftrace`, piping the TraceConfig on **stdin** (SELinux denies `shell:s0` writes under `/data/misc/perfetto-traces/`, so the config can't be staged as a file), waits for the PID on stdout, then on stop sends `kill -TERM`, polls `/proc/<pid>`, and `adb pull`s the trace. **Pipeline** (`utils/android-profiler/pipeline/`): PerfettoSQL does the parsing and aggregation, so the Android pipeline is intentionally **two files, not iOS's four**. The per-hang annotation pass is **batched** — `runBatchedHangFolds` inlines all hang windows as a `VALUES` table into one SQL script (two derived views unioned in a single terminal SELECT, because the engine returns only the final statement's rows) and runs it once against the warm engine. **The platform-dispatch refactor** — `native-profiler-start/stop/analyze` shrank from monolithic iOS files (~160–330 lines each) into **thin routers** (~60–110 lines) that branch on `api.platform`: ``` native-profiler-start ─┬─ ios? → platforms/ios.ts (xctrace) └─ android → platforms/android.ts (adb perfetto) ``` A new **`utils/profiler-shared/`** layer holds everything both backends use: | File | What was hoisted | | --- | --- | | `types.ts` | Unified `Bottleneck = CpuHotspot \| UiHang \| MemoryLeak \| MemoryRssGrowth`, each now carrying a `platform` field; shared `NativeProfilerAnalyzeResult`; `RECORDING_CAP_MS` | | `aggregate.ts` | Generic `aggregateCpuHotspots(AggregatorInputRow[])` — burst windowing (`BURST_GAP_MS=500`), severity banding (RED >15%, YELLOW 3–15%, <3% dropped). iOS feeds it via a pre-pass; Android feeds it precomputed SQL rows | | `lifecycle.ts` | `shutdownChild` SIGINT→SIGTERM→SIGKILL ladder + `waitForChildExit` (no PID-reuse hazard) | | `thread.ts` | `normalizeThreadName` for both xctrace and Perfetto thread names | | `time-align.ts` | New `buildPerfettoAnchor()` + `windowsOverlap()` alongside the existing iOS/React anchors | | `format.ts` | `formatBytes` (moved out of the combined report) | iOS was refactored to fit this shape: `ios-profiler/lifecycle.ts` and `types.ts` become thin re-exports; `pipeline/02-aggregate.ts` shrank from ~200 to ~60 lines (keeps its iOS-only dominant-function pre-pass, delegates grouping/bursting/ severity to the shared aggregator). **No iOS behavior change** — same outputs, shared internals. **Session blueprint** (`native-profiler-session.ts`): generic `capturePid` / `captureProcess` (was `xctracePid` / `xctraceProcess`), new `platform` and `androidOnDeviceTracePath` fields, and **platform-branched dispose** (iOS SIGKILLs xctrace; Android `kill -KILL`s perfetto, `rm`s the device trace, and disposes the warm WASM engine). **Drill-down tools**: `profiler-load.ts` learns to restore Android `.pftrace` sessions (with an optional metadata sidecar / `app_process` override); `profiler-stack-query.ts` splits into iOS and Android branches. Unlike iOS (which caches `parsedData` in memory), Android **re-queries the `.pftrace`** per drill-down — the warm engine is the cache, so each query costs only SQL execution time. ### 3. Packaging & release pipeline The WASM bundle's three third-party artifacts (`trace_processor.wasm`, `engine_bundle.node.js`, `engine.mjs`) + `LICENSE` are **never committed** (`.gitignore` lists the generated files but not the dir). They're built and sha256-checksummed in `argent-private` CI, published to `argent-private-releases`, and on our side: - **`scripts/download-trace-processor.sh`** (+88, new) — `gh release download` → verify tarball sha256 (**FATAL on mismatch** — the blobs are unsigned) → extract → re-verify via `SHA256SUMS` → assert all four present. - **`scripts/pack-mcp.cjs`** (+60, new) — single pack entry point: downloads simulator-server, native binaries, **and** the trace-processor bundle, then builds and `npm pack`s `@swmansion/argent`. `package.json`'s `pack` / `pack:mcp` now call this script. - **`packages/argent/scripts/bundle-tools.cjs`** (heavily reworked, +574/-...) — copies `assets/trace-processor/`, `assets/queries/`, and `argent.tracecfg.pbtxt` into `packages/argent/assets/`, and regenerates `bundled-meta.ts` to stamp the Perfetto version. - **`publish.yml` / `publish-next.yml`** — run the download step and then **verify**: all four WASM files present, the wasm magic-byte check (`head -c4 … | grep $'\x00asm'`), plus the queries dir, TraceConfig, manifest, and Android helper APK. Asset reorg: `native-devtools-android` now ships `dist/`, `bin/`, and `assets/` (SQL queries, TraceConfig, manifest moved under `assets/`); `.gitignore` swaps the old per-file `packages/argent/manifest.json` entry for the whole `packages/argent/assets/` dir. --- ## Testing 15 new test files under `packages/tool-server/test/android-perfetto/` cover the engine and pipeline, including: - `dispatch.test.ts` — start/stop/analyze route to the right platform backend. - `aggregate-shared.test.ts` — the shared aggregator on both iOS and Android-shaped input. - `hang-folds-batched.test.ts` / `hang-fold.test.ts` — the batched per-hang SQL fold. - `hang-severity.test.ts` — jank-reason → RED/YELLOW classification. - `dispose.test.ts` / `dispose-warm-engine.test.ts` — session dispose kills the daemon, rms the device trace, and tears down the warm engine. - `stop-recovery.test.ts` — `profilingActive` is forced false even if `adb pull` fails (so a retry-start doesn't wedge). - `run-tp.test.ts` — `renderSqlTemplate` token rendering + the "unused substitution" guard. - `manifest-hint.test.ts` — zero-callstack detection emits the manifest hint. - `profiler-load.test.ts` — `.pftrace` session restore with/without the metadata sidecar. Plus `native-profiler-analyze-failure.test.ts` for the engine-unavailable banner, and updated iOS tests for the shared-lifecycle refactor. `@argent/native-devtools-android` gains its own vitest setup (`vitest.config.ts`, `tsconfig.test.json`, `test/errors.test.ts`). --- ## Notes & follow-ups - **Manifest requirement.** Perfetto's `linux.perf` data source needs `/proc/<pid>/mem` access, granted only to a **debuggable** or `<profileable android:shell="true"/>` app. A release build without either silently produces zero callstacks; the pipeline detects this and emits a **manifest hint** instead of an "All clear" report. - **Version pinning lives in the `argent-private` submodule** (`PERFETTO_VERSION` / `PERFETTO_WASM_TAG` / `LYNX_TRACE_PROCESSOR_VERSION`). Bumping is documented in `argent-private/docs/ANDROID_PERFETTO.md`. JS↔engine version skew is structurally impossible (version-agnostic decoder + vendored wasm). - **`disposeWarmEngine` is exported but not wired into session-stop** — warm engines are reclaimed only by the idle timer + LRU cap. Acceptable today (bounded memory); wire it into session dispose if explicit teardown is ever needed. - **Submodule bump.** This PR moves `packages/argent-private` to `22e55f90` — see the companion `argent-private` PR description, including its merge-hygiene note (that branch should pick up latest `argent-private` main before the pointer is finalized). ### Companion docs added in this branch - `utils/android-profiler/ANDROID_PROFILER_REFERENCE.md` — stack, capture, queries, manifest requirement. - `utils/android-profiler/PIPELINE_DESIGN.md` — the "why" (two-file pipeline, shared-aggregator hoist, re-query-don't-cache, batched fold). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3d2d254004 |
fix(wayland-e2e): use ARGENT_PORT instead of PORT to start tool-server (#304)
## Summary - The tool-server reads its port from `ARGENT_PORT` (index.ts:72), not the generic `PORT` env var - The CI step was setting `PORT=3033` which the process silently ignored, causing it to bind on the default 3001 - The health-poll loop and every downstream curl targeted 3033, so startup always timed out |
||
|
|
80661fad8f |
feat: Linux support (#249)
This PR makes Linux work.
# AI Summary
<details>
Brings Argent up on Linux as a host: per-platform npm bundle, renderer
selection that actually works on hybrid-GPU / Wayland / containerized
Linux, hot-boot guarding that survives the SwiftShader composite gap, a
real Linux preflight, and a Wayland-headless E2E job in CI. macOS stays
on
the path it was on before (no `-gpu` flag was passed; AVD config
decided);
nothing about the iOS Simulator behavior changes.
The diff is broader than "ship the Linux artifact" — calling it out
below
because several pieces affect macOS too and reviewers should be primed
for
that rather than skim past expecting "just packaging."
### What's actually in here
**npm packaging (the original framing — still load-bearing)**
| File | Change |
|---|---|
| `packages/argent/scripts/argent-simulator-server.cjs` (new) | Node
dispatcher exposed as the `argent-simulator-server` bin entry. Joins
`process.platform` into the bin path and execs. Forwards
SIGTERM/SIGINT/SIGHUP parent→child so non-TTY supervisors can't orphan
the binary. |
| `packages/argent/scripts/bundle-tools.cjs` | Copies per-platform
binaries from `packages/native-devtools-ios/bin/<platform>/` into
`packages/argent/bin/<platform>/`. darwin required, linux best-effort. |
| `packages/argent/package.json` | `bin.argent-simulator-server` points
at the dispatcher. |
| `packages/native-devtools-ios/src/index.ts` | Resolver joins
`process.platform` into the simulator-server bin path. iOS-only dylibs
and `ax-service` throw `"requires a macOS host"` on Linux callers so
they get the root cause, not "file not found". |
| `scripts/download-simulator-server.sh` | Downloads
`simulator-server-argent-{macos,linux}` into platform-keyed subdirs.
**Purges each platform dir before its download attempt** so a failed
download can't leave stale binaries in place from a prior run. |
| `scripts/dev.cjs` | Local dev copies the current host's binary into
`bin/<process.platform>/` to match the resolver layout. |
| `.github/workflows/publish{,-next}.yml` | Replaces single `test -x
darwin/...` with a per-platform inventory loop. darwin always required;
Linux gated by `ARGENT_REQUIRE_LINUX_BINARY=1` (flip once radon#118
lands). |
**Linux runtime — emulator boot path**
| File | Change |
|---|---|
| `packages/tool-server/src/tools/devices/boot-device.ts` |
`selectGpuMode()`: Linux defaults to `-gpu swiftshader` (`auto` lands on
lavapipe with ~10× cold-boot slowdown; `-gpu host` produces silent
black-window failures on Optimus / NVIDIA+libglvnd / Wayland-on-hybrid /
containers). macOS keeps `auto` (ANGLE→Metal).
`ARGENT_EMULATOR_GPU_MODE` overrides, **validated against `emulator
-help-gpu`'s whitelist** so a typoed value fails fast instead of burning
the whole boot budget. `ARGENT_EMULATOR_NO_WINDOW=1` appends
`-no-window` for headless contexts (CI / containers / Wayland sessions
with no Qt wayland plugin). |
| `packages/tool-server/src/tools/devices/boot-device.ts` (cont.) |
**Cold-boot:** `awaitFirstRealFrame` polls SwiftShader's lockscreen
composite (PNG byte-count via `screencap -p`); blocks `booted:true`
until a real frame shows up. Without this, callers chaining `boot-device
→ screenshot` get an all-black PNG because `sys.boot_completed=1` fires
5–60 s before the first composite. |
| `packages/tool-server/src/tools/devices/boot-device.ts` (cont.) |
**Hot-boot:** `assertScreencapAlive` polls within an 8 s budget instead
of single-probing. The earlier single-probe caught the sticky-blank
state but also killed the emulator on every transient snapshot-restore
blank, forcing "every boot is cold" cycles. Sticky vs transient is
discriminated by whether the budget exhausts. |
| `packages/tool-server/src/utils/linux-preflight.ts` (new) |
`linuxBootDiagnostics()` checks `/dev/kvm` access. `diagnoseAvdSizing()`
warns when a target AVD's `hw.ramSize`/`vm.heapSize` is below the floor
an aggressive RN/Expo app needs (2G/228M avdmanager defaults wedge under
load). |
| `packages/tool-server/src/utils/android-binary.ts` |
`defaultAndroidRoots()` probes typical SDK install locations when
`ANDROID_HOME`/`ANDROID_SDK_ROOT` are unset — **cross-platform**,
benefits macOS launches from GUI-launched MCP hosts (Claude Code Desktop
etc.) where shell rc env doesn't propagate. |
**Cross-platform fixes that landed alongside**
| File | Change |
|---|---|
| `packages/argent-installer/src/utils.ts` | `getLatestVersion()` now
redirects `npm view`'s stderr to `ignore` so `EBADDEVENGINES` warnings
from a consumer project's `package.json` (e.g. Bluesky's pnpm pin) don't
leak to the user's terminal. **Cross-platform fix**, not Linux-specific.
|
**CI**
| File | Change |
|---|---|
| `.github/workflows/wayland-e2e.yml` (new) | Boots an AVD through the
tool-server under headless Weston; asserts `hw.gpu.mode = swiftshader`
and that screenshots + gesture-tap work without a display. Currently
runs on `pull_request` for the affected paths + `workflow_dispatch`.
Pinned to `radon-daily` for the simulator-server until radon#118 ships a
Linux asset to `radon-main`. |
### Tests added in this PR
- `packages/argent/test/dispatcher.test.ts` — e2e against tmpdir-staged
dispatcher + fake `<platform>/simulator-server`: missing-binary error
path, exit-code passthrough, argv passthrough, SIGTERM forwarding.
- `packages/tool-server/test/native-devtools-ios-resolver.test.ts` —
`requireDarwin` gates for dylibs and `ax-service` on Linux, per-platform
`simulator-server` path resolution, missing-binary error,
`ARGENT_NATIVE_DEVTOOLS_DIR` override.
- `packages/tool-server/test/linux-preflight.test.ts` — KVM diagnostic
shape + `diagnoseAvdSizing` (ram low, heap low, both, neither, missing
keys).
- `packages/tool-server/test/boot-device-hotboot.test.ts` — extended
with `it.each([linux→swiftshader, darwin→auto])` so a single CI runner
exercises both GPU branches (was runner-platform-dependent, regressions
could pass on both runners individually). New cases for the wedged-frame
fallback (under fake timers because the polling budget is short but
real-time enough to exceed vitest's 5 s default), the GPU-mode whitelist
rejection, and the cold-boot composite-wait timeout.
- `packages/tool-server/test/android-binary.test.ts` — extended for
`defaultAndroidRoots` probe ordering and HOME pinning.
### Verification
- `tsc --build`: clean apart from pre-existing
`@argent/native-devtools-android` import errors unrelated to this
branch.
- `vitest run` in `packages/tool-server` and `packages/argent`: 689 + 11
= 700 tests passing; the 4 pre-existing test-file load failures
(native-devtools-android missing) reproduce on `main` and are out of
scope.
- `npm pack -w @swmansion/argent`: produces `swmansion-argent-0.8.0.tgz`
with the per-platform layout.
- Wayland-e2e: triggered on this branch — link will be in a PR comment
once the run completes.
- Manually verified end-to-end on a Wayland-on-hybrid-GPU developer
laptop: AVD boots through the tool-server with `-gpu swiftshader`,
screencap returns real frames, gesture tools work.
### Dependencies / what still gates this
- **radon#118** must land before the Linux artifact ships through the
default channel. The download script tolerates the asset being absent
today (best-effort), so this PR is technically safe to merge before —
Linux installs just won't get a working `simulator-server` until the
upstream artifact is published. Once it lands:
- Drop the `radon-daily` arg in `.github/workflows/wayland-e2e.yml`.
- Flip `ARGENT_REQUIRE_LINUX_BINARY` to `1` in both publish workflows.
- **wayland-e2e is not a required status check** today (no branch
protection on `main` requires it). Worth turning on before merging
anything else that touches the Linux boot path.
### Related
- radon#118 — produces the `simulator-server-argent-linux` artifact this
PR consumes.
- argent-private#11 — cross-builds the iOS Simulator dylibs on Linux.
Strictly orthogonal: those dylibs are macOS-runtime-only regardless of
where they're built, and this PR keeps them under `dylibs/` with a clean
error path on Linux callers.
</details>
Fixes #246
|
||
|
|
2180dd8e6a |
ci: typecheck test files in CI (#202)
We've had lots of type errors in our tests, scripts and other non-core
code despite all CI being green. This PR fixes that.
This PR also removes some redundant features like `@ts-check` inline
directives which have been replaced by proper ts config files.
<details>
## AI Summary
Audit of every CI workflow against every package's `package.json` and
`tsconfig` surfaced several gaps where CI did partial coverage. This PR
closes them and fixes every pre-existing type error the new gates
surfaced.
## Gaps closed
| # | Gap | Fix |
|---|---|---|
| 1 | `tsc --build` only covers `src/**`; test files (`test/**`,
`tests/**`) never typecheck. Vitest transforms tests with esbuild —
strips types instead of checking them. | New `tsconfig.test.json` per
test-bearing package (`composite: false`, `noEmit`, `rootDir: "."`,
includes `src/**` + `test/**` (or `tests/**`) + `vitest.config.ts`). New
`typecheck:tests` npm script per package. New CI step `npm run
typecheck:tests --workspaces --if-present`. |
| 2 | Build/publish scripts (`scripts/*.{cjs,mjs}`,
`packages/argent/scripts/*.cjs`, `packages/skills/scripts/install.js`) —
JS, never typechecked. `sync-readme.cjs` runs in `prepack`;
`postinstall.cjs` runs on every install; `bundle-tools.cjs` is
build-critical. A typo only surfaces at runtime. | Root
`tsconfig.scripts.json` with `allowJs` + `checkJs` + `noImplicitReturns`
covers all script paths. Root `typecheck:scripts` npm script. New CI
step. Per-file `// @ts-check` comments removed — tsconfig drives
inclusion. |
| 3 | `argent-cli` had no `typecheck:tests` script. When tests get added
there they'd silently bypass the gate. | Added `tsconfig.test.json` +
`typecheck:tests` script. `--if-present` would have skipped it anyway,
so this is future-proofing. |
| 4 | Local `prettier --check .` walked into the `argent-private`
submodule and reported violations there. CI didn't see this because
`actions/checkout@v4` doesn't init submodules — so local `prettier
--check` did not match CI behavior. | New `.prettierignore` excludes the
submodule plus `dist/`, `node_modules/`, lock files, `tsbuildinfo`.
No-op in CI; aligns local with remote. |
## Test type errors fixed
These were already present. The `npm test` passed because esbuild
stripped the offending types:
- **argent-installer**: widen `scope` literal type for
dead-code-elimination guard; non-null `addAllowlist!`/`removeAllowlist!`
(optional methods on adapter).
- **argent-mcp**: add `.js` extensions for NodeNext module resolution.
- **registry**: broaden `StaticBlueprintResult` blueprint api from `{
id; deps? }` to `Record<string, unknown>` so existing tests can pass
arbitrary api shapes.
- **tool-server**:
- non-null `zodSchema!` on tool defs that always carry one
(`launchAppTool`, `restartAppTool`, etc.)
- cast sentinel `"ignored"` strings to `DeviceInfo` in factory-rejection
tests
- match the 4-arg `dispatchByPlatform<IosServices, AndroidServices,
Params, Result>` signature (tests still passed the old 3-arg shape)
- `assertFlowRunResult` type guard for the `FlowRunResult |
FlowPrerequisiteNotice` union before `.steps` access
- swap broken `typeof import("supertest").default` for static `import
supertest` (supertest is `export = supertest`)
- fill in the 4 fields missing from a `NativeProfilerSessionApi` mock
(`xctraceProcess`, `recordingTimedOut`, `recordingExitedUnexpectedly`,
`lastExitInfo`)
- cast a vitest `Mock` to a plain function at one direct call site where
the `Procedure | Constructable` union loses callability
- fix `update-checker.test.ts` reading `../../package.json` (off-by-one
— went past tool-server; was relying on a Vite resolver quirk)
- replace one cross-package `../../../registry/src/index` import with
`@argent/registry` so registry's private `services` field isn't compared
between src and dist declarations
All fixes are type-level. Full `npm test --workspaces --if-present`
still reports **991 passed across 87 test files**.
</details>
|
||
|
|
fbe7b087e7 |
chore(deps): batch-apply Dependabot updates (#214–#227) (#228)
This PR consolidates every open Dependabot PR that requires **no source-code changes** into a single branch with one regenerated, coherent lockfile. | PR(s) | Update | Status | |---|---|---| | #214 | GitHub Actions group — checkout@v6, setup-node@v6, setup-python@v6, github-script@v9, upload-artifact@v7 | ✅ Applied | | #215 | prettier 3.8.3, @clack/prompts 1.4.0, semver 7.8.0, yaml 2.9.0, @modelcontextprotocol/sdk 1.29.0, esbuild 0.28.0 | ✅ Applied | | #216 / #221 / #224 / #227 | typescript 6.0.3 | ✅ Applied | | #217 / #222 / #223 | @types/node 25.9.0 | ✅ Applied | | #220 / #226 | vitest 4.1.6 | ✅ Applied | | #218 / #225 | zod 4 | ⏸️ Excluded — requires code migration | | #219 | express 5 | ⏸️ Excluded — requires code migration | | #215 (partial) | tree-sitter 0.25.0 | ⏸️ Excluded — see below | **tree-sitter 0.25.0** ships no prebuilt binaries and its `binding.gyp` hard-codes `-std=c++17`, which fails to compile against Node 20+/24 C++20 `cppgc` headers — a hard `npm install` blocker (would also break CI). Kept at `^0.21.1`; all other #215 updates applied. zod 4 and express 5 are excluded here because they require real source changes; tracked separately. Verified: `tsc --build` clean, esbuild bundles, **1010 unit tests pass**, **zero source files modified**. |
||
|
|
0dca6bbfef |
fix: build workspace deps before packing/publishing argent (#172)
## Summary - The `publish` and `publish-next` workflows ran `npm run build -w @swmansion/argent` directly, but after the recent CLI/MCP/main split, `@swmansion/argent` imports types from `@argent/cli`, `@argent/mcp`, `@argent/installer`, `@argent/tools-client`. Those packages expose types from their own `dist/index.d.ts`, which doesn't exist until they're built — so argent's `tsc` failed with `TS2307: Cannot find module '@argent/...'` (e.g. [run 25161244218](https://github.com/software-mansion/argent/actions/runs/25161244218)). - Added `npx tsc --build` before the per-workspace argent build in both publish workflows so the sibling packages produce their `dist/` first. This mirrors what `unit-tests.yml` already does, which is why the regression didn't surface on PRs. - Applied the same fix to the root `pack` script (`npm run pack`) since it had the same latent bug for local packing. ## Test plan - [x] Reproduced the failure locally by wiping every `dist/` and `tsconfig.tsbuildinfo`, then running `npm run build -w @swmansion/argent` — same four `TS2307` errors as CI. - [x] Verified `npx tsc --build && npm run build -w @swmansion/argent` succeeds from a clean state. - [ ] Re-run the `Publish prerelease to npm (next)` workflow against this branch (or after merge) to confirm green. |
||
|
|
e42308106c |
ci: add workflow to publish prereleases under npm next tag (#163)
## Summary - Adds `.github/workflows/publish-next.yml`, a manual `workflow_dispatch` workflow that publishes `@swmansion/argent` to npm under the `next` dist-tag so users can opt into prereleases via `npm install @swmansion/argent@next` without affecting `latest`. - Mirrors the existing `publish.yml` (same checkout / native binaries download / build / smoke checks); the only functional differences are `npm publish --tag next` and a pre-publish guard that fails fast unless `packages/argent/package.json` has a SemVer prerelease version (contains `-`, e.g. `0.6.0-next.0`). ## Why `npm publish` defaults to the `latest` dist-tag even for prerelease SemVer versions. Without an explicit `--tag next`, a published `0.6.0-next.0` would be served to anyone running `npm install @swmansion/argent`. This workflow makes prerelease publishing safe and explicit. ## Usage 1. On a release branch, bump `packages/argent/package.json` to e.g. `0.6.0-next.0` and tag `v0.6.0-next.0` (the existing `release.yml` will create a GitHub Release). 2. Run **Actions → Publish prerelease to npm (next) → Run workflow** with `ref: v0.6.0-next.0`. 3. Verify with `npm dist-tag ls @swmansion/argent` — `latest` is unchanged, `next` points at the new prerelease. ## Test plan - [ ] Dry-run the workflow against a prerelease tag and confirm `npm pack --dry-run` succeeds and the publish step runs `--tag next`. - [ ] After a real prerelease publish, confirm `npm view @swmansion/argent dist-tags` shows `next: <version>` and `latest` is untouched. - [ ] Sanity check the guard: trigger against a non-prerelease ref (e.g. `v0.5.3`) and confirm the workflow fails at the "Verify package version is a prerelease" step before running `npm publish`. |
||
|
|
6355620c4d |
refactor(mcp): split mcp package into argent, argent-cli, argent-installer, argent-mcp, argent-tools-client (#162)
## Summary - Splits the monolithic `packages/mcp` package into five focused packages: - `argent` — the user-facing CLI entry (`argent` binary, init/uninstall/update flows, bundled-paths, scripts). - `argent-cli` — CLI runner internals (flag-parser, run, server, tools). - `argent-installer` — `init`/`update`/`uninstall` flows, MCP config adapters, skills installer. - `argent-mcp` — MCP server itself (mcp-server, tool-mapping, auto-screenshot, content adapters). - `argent-tools-client` — HTTP client + launcher for the tool-server. - Adds Gemini agent definitions, VSCode MCP config, and per-package `tsconfig.json` / `vitest.config.ts`. - Updates root `tsconfig.json`, `scripts/dev.cjs`, CI workflows, and `package.json` to wire the new packages together. ## Test plan - [ ] `npm install` resolves the new workspaces cleanly - [ ] `npm run build` succeeds across all packages - [ ] `npm test` passes (renamed test files keep their assertions) - [ ] `npx argent init` still produces the expected MCP config in a sample project - [ ] `npx argent update` / `argent uninstall` exercise the installer package - [ ] CI workflows (`publish.yml`, `unit-tests.yml`) run on this branch |
||
|
|
9fee899e5f | chore: bump package versions to 0.5.3 for mcp, registry, skills, and tool-server (#149) | ||
|
|
066aad2452 | fix: fix problematic CLI install, bump package versions to 0.5.2 for mcp, registry, skills, and tool-server | ||
|
|
1896097443 |
fix: pass GH_TOKEN to download steps in publish workflow (#142)
gh CLI requires GH_TOKEN to be set in GitHub Actions even for public repos. Without it the download steps fail with exit code 4. |
||
|
|
3979e2780d |
bump version (#136)
* init * chore: update package versions to 0.4.5 for registry, skills, and tool-server --------- Co-authored-by: pFornagiel <pawel.fornagiel2@gmail.com> |
||
|
|
7db8305fff |
feat: pin native binaries to versioned release tags and publish to npm (#125)
- download-native-binaries.sh now requires an explicit release tag (e.g.
argent-v0.4.3) and fails fast with a clear error if the release doesn't
exist in argent-private-releases
- publish.yml is now manual-only (workflow_dispatch) with a ref input
(branch/tag/SHA); derives the binaries tag as argent-{ref} and checks
out the repo at that ref
- publish.yml now targets npm (registry.npmjs.org + NPM_TOKEN) instead
of GitHub Packages
- download:native-binaries npm script auto-derives the tag from
packages/mcp/package.json version for local pack:mcp usage
|
||
|
|
f30948fe46 | feat: download native binaries from argent-private-releases instead of building locally | ||
|
|
d082ea2fd2 | feat: download simulator server | ||
|
|
724ffc8ca5 |
ci: add prettier format check (#111)
* ci: add prettier format check workflow
* test: prettier err
* Revert "test: prettier err"
This reverts commit
|