From 49380950638a9d43567c0f2681d2d593d600e8fe Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 20:50:57 +0000 Subject: [PATCH 1/2] Release Aiden Agent 0.60.0 Bump package.json and package-lock.json so the Release desktop workflow publishes v0.60.0, and record the MCP move to pi-mcp (#568), Instant UI (#576) and the harness reliability foundation (#553 via #577) as shipping in 0.60.0 in the plan index. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01SE574SfMr5QCRmoYLXy2D7 --- docs/aiden-remote-api-v1.md | 2 +- docs/plans/README.md | 8 ++++---- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/aiden-remote-api-v1.md b/docs/aiden-remote-api-v1.md index ddc773dbc..2c4aa283d 100644 --- a/docs/aiden-remote-api-v1.md +++ b/docs/aiden-remote-api-v1.md @@ -288,7 +288,7 @@ These routes were frozen as contract in Phase 1 and implemented in Phase 4 throu - `POST /bots/{botId}/restore`: `If-Match` plus `Idempotency-Key`, returning the restored authoritative detail. - `GET /bot-favorites` and `PATCH /bot-favorites`: at most 20 unique, non-archived Bot IDs; update is whole-list replacement under `If-Match`, so membership and order change atomically. A replacement may omit archived Bots and may still edit unrelated active favorites, but adding an archived Bot returns `bot_archived`. No successful favorites response retains an archived Bot ID. - `GET /bot-conversations?cursor=…&query=…&botId=…&limit=…`: newest-first stable pages of at most 50 items, with a 200-scalar search query and 128-scalar cursor. Search is confined to Bot name/purpose, conversation title, and the bounded previews actually projected by the Mac. Every item has `updatedAt >= createdAt`. `canRespondToApproval: true` is valid only while `activityState` is `waiting_for_approval`; a waiting row may still be non-respondable when the paired device lacks authority. -- `POST /bots/{botId}/chats`: open-or-create the Bot's one persistent chat. The frozen v1 response remains `201` and the operation identifier remains `createBotChat` for compatibility with shipped clients, even when the existing chat is returned. When it already exists, the Mac returns it unchanged and ignores creation-only provider/model input. When absent, an empty body inherits the Bot provider/model; otherwise one exact providerId/modelId pair is required, plus `Idempotency-Key`. Partial pairs never fall back. The creation pair must be currently available; an unavailable selection fails closed without fallback. The Mac injects the authoritative `botId` and hidden managed-home workspace; neither identifier is accepted in the body, and every successful response includes that authoritative `botId`. Concurrent calls and retries converge on the same chat. Legacy duplicate chats remain readable for recovery, but only the deterministic canonical chat is writable or projected in the Bot inbox. After 0.59.0, while a host serves `bot-durable-session-v1`, `POST /chats/{chatId}/turns` on a Bot's chat answers `400 invalid_request` ("Update Aiden on this device to chat with this Bot…") and appends nothing: the Bot's conversation is its durable session, and a workspace-chat turn would run outside it. Paired desktops open a host's Bot through `GET /bots/{botId}/session`, its `/session/events` stream and the session routes, as phones do, and negotiate `bot:activity` when the host announces `bot-activity-v1` (they do not take `bot:cards`). A desktop that misses that answer (the host was briefly unreachable) asks again on its next reconnect; a refusal is final. Desktops offer Queue and Steer only while they hold `bot:activity`, because without it the host sends them no `queued` list. A desktop reads a newer host's session as far as it understands it, as phones do: an unknown session state reads as `unavailable`, an entry it cannot parse is left out, and a stream event it cannot parse makes it read `GET /bots/{botId}/session` again instead of dropping the stream. After a clean `closed` event it reopens the stream at once (up to twice in a row, then with backoff). `BotSession` and `BotMessageReceipt` compose the state fields from `BotSessionStateFields` and close with `unevaluatedProperties: false`; `BotSessionState` alone stays closed for Resume, Dismiss and Stop. A host error's own `message`, when short and printable, is what a paired desktop shows for a refused request. +- `POST /bots/{botId}/chats`: open-or-create the Bot's one persistent chat. The frozen v1 response remains `201` and the operation identifier remains `createBotChat` for compatibility with shipped clients, even when the existing chat is returned. When it already exists, the Mac returns it unchanged and ignores creation-only provider/model input. When absent, an empty body inherits the Bot provider/model; otherwise one exact providerId/modelId pair is required, plus `Idempotency-Key`. Partial pairs never fall back. The creation pair must be currently available; an unavailable selection fails closed without fallback. The Mac injects the authoritative `botId` and hidden managed-home workspace; neither identifier is accepted in the body, and every successful response includes that authoritative `botId`. Concurrent calls and retries converge on the same chat. Legacy duplicate chats remain readable for recovery, but only the deterministic canonical chat is writable or projected in the Bot inbox. From 0.60.0, while a host serves `bot-durable-session-v1`, `POST /chats/{chatId}/turns` on a Bot's chat answers `400 invalid_request` ("Update Aiden on this device to chat with this Bot…") and appends nothing: the Bot's conversation is its durable session, and a workspace-chat turn would run outside it. Paired desktops open a host's Bot through `GET /bots/{botId}/session`, its `/session/events` stream and the session routes, as phones do, and negotiate `bot:activity` when the host announces `bot-activity-v1` (they do not take `bot:cards`). A desktop that misses that answer (the host was briefly unreachable) asks again on its next reconnect; a refusal is final. Desktops offer Queue and Steer only while they hold `bot:activity`, because without it the host sends them no `queued` list. A desktop reads a newer host's session as far as it understands it, as phones do: an unknown session state reads as `unavailable`, an entry it cannot parse is left out, and a stream event it cannot parse makes it read `GET /bots/{botId}/session` again instead of dropping the stream. After a clean `closed` event it reopens the stream at once (up to twice in a row, then with backoff). `BotSession` and `BotMessageReceipt` compose the state fields from `BotSessionStateFields` and close with `unevaluatedProperties: false`; `BotSessionState` alone stays closed for Resume, Dismiss and Stop. A host error's own `message`, when short and printable, is what a paired desktop shows for a refused request. - `GET /bot-capabilities?botId=…`: safe revisioned provider/model, image-input capability, file-scope, shell, connection/MCP, skill, and other-capability catalog plus the full notice status. The optional, exact `botId` selects an authenticated existing Bot so its saved unavailable choices can remain visible; omitting it returns the generic creation catalog. Unknown, duplicate, malformed, empty, and overlong query values fail closed. `supportsImages` is explicit and unknown capability fails closed. The response never returns display copy that the client could mistake for authority. - The catalog’s additive `skillsEnabled` boolean defaults to `true` when omitted. When `false`, skill inventory discovery and incarnation reconciliation pause; target-Bot catalogs may retain that Bot’s authenticated saved skill IDs as unavailable choices. Clients may preserve or remove those saved choices during unrelated edits, but must not offer unavailable unselected skills as new choices. The Mac enforces exact saved ownership and per-chat narrowing, withholds skills from all runtime contexts, and restores unchanged selections when the global gate reopens. Invalid provided values fail decoding. - `PATCH /bots/{botId}/capabilities`: `If-Match` Full/Custom update with the exact current `catalogRevision`. Full requires `confirmedForeground: true` and may carry one exact provider/model pair; omitting both preserves the saved choice for compatibility with older clients, while supplying only one is invalid. Custom contains exactly one currently available provider/model pair plus only exact positive opaque selections from that catalog. Optional `visionModel` uses three-state semantics: omitted preserves the companion, `null` clears it, and an exact provider/model object sets it only when the catalog marks that model image-capable. A supplied pair becomes revisioned durable authority, changing either primary or companion fences active turns, and the canonical persistent chat mirrors only the primary model. An unavailable model remains visibly blocked and is never replaced by fallback. diff --git a/docs/plans/README.md b/docs/plans/README.md index fc8d178ca..288ea4e0a 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -2,14 +2,14 @@ This directory is the source of truth for Aiden's implementation plans. The engineer changing a plan owns its status row and should update it in the same change when a meaningful milestone lands. -Last refreshed 2026-10-20 against `main` at `126c2dd1` (0.59.0 release). "On main" means merged after 0.59.0 and not yet in a release. +Last refreshed 2026-10-21 against `main` at `32020413` (0.60.0 release). "On main" means merged after 0.60.0 and not yet in a release. 0.60.0 ships PRs #560–#566, #568, #572, #576 and #577 (#553). Research addition 2026-10-03 against `main` at `3f86d41af` (0.52.0): [all-PR inventory and improvement audit](aiden-pr-improvement-audit-20261003.md), [implemented Aiden knowledge/actions MVP (PR #312)](https://github.com/sambitcreate/aiden-agent/pull/312), and [json-render research and interactive chat controls specification](aiden-chat-controls-json-render-research.md). The implemented MVP includes desktop, iOS, Android and CLI TUI controls. This adds research and the bounded implementation; older release-status rows have not been globally reconciled. The Pi 1.0 stack (#299–#307) merged through PR #357 (Pi 1.0.3, 0.54.0), main pinned Pi 1.2.0 in 0.57.0 (PR #471) and now pins Pi 1.3.0 (PR #505, 0.58.0); only the frozen legacy journal harness stays on 0.87.1. Since 2026-10-20 the desktop and CLI builds also patch pi-ai 1.3.0 with a backport of Pi #11142 (Responses prompt cache kept across tool changes) until a Pi release contains it; drop `scripts/patch-pi-ai-tool-cache.mjs` and its patch on that bump. ## Active and partial -- [Aiden harness quality and reliability upgrade](aiden-harness-quality-upgrade-plan.md) — Active; foundation merged in [PR #553](https://github.com/sambitcreate/aiden-agent/pull/553) (2026-10-21, 0.60.0): H0/H1, private verification/protected offline evaluation, portable durable CLI Bots, local packaged transcript budgets and workflow-evidence drafts. Live pilot (H2c) awaits provider/model/spend configuration; adoption UI (H5b) depends on that gate. Signed/physical acceptance remains separate. Follows the verified Aiden/Harness² sitrep: memory-index retention, Assistant recovery, tool discovery, verification receipts and protected task evaluation, CLI durable Bot parity, packaged transcript performance, then explicit workflow experiments. Active desktop/CLI Pi packages remain at 1.3.0 and the legacy alias at 0.87.1; an upstream dependency upgrade has a separate gate. This is the current pin observation for this plan; older version/status statements retain their historical context and need reconciliation before implementation. Research baseline and acceptance gates are recorded in the plan. -- [MCP audit and move to Pi's MCP client](mcp-pi-migration-plan.md) — Implemented on `feature/mcp-servers-audit-b21018` (2026-10-21); live sign-in acceptance per connector remains (owner accounts). Root cause of failed Figma sign-in: Aiden dropped the client secret the server issues, and Figma, Vercel and Supabase accept only confidential clients; Figma also registered as "Codex", and the catalog was a copy of Codex's plugin directory (45 of 66 entries could not be set up). Shipped: client secrets kept, desktop and CLI MCP client and OAuth on `@earendil-works/pi-mcp` 1.3.0 (legacy SSE removed, SDK client imports blocked), sign-in hardening (background never registers, transient refresh failures keep the sign-in, provider errors and a failure page, durable needs-sign-in and Sign out, pre-registered clients, CIMD behind an unset URL), Pi's built-in CLI MCP extension off, an Aiden-owned connector catalog with Figma Desktop and an unavailable hosted Figma, the feature renamed Connectors (`/connectors`, `/mcp` alias), fingerprinted provider API-key sharing opt-in in Settings → Providers, and privacy-safe MCP failure diagnostics. All 18 hosted OAuth connectors pass `pi-mcp` discovery. Open: host the CIMD document, Figma approval for "Aiden Agent". +- [Aiden harness quality and reliability upgrade](aiden-harness-quality-upgrade-plan.md) — Active; foundation merged in [PR #553](https://github.com/sambitcreate/aiden-agent/pull/553) via #577 (2026-10-21), shipped in 0.60.0: H0/H1, private verification/protected offline evaluation, portable durable CLI Bots, local packaged transcript budgets and workflow-evidence drafts. Live pilot (H2c) awaits provider/model/spend configuration; adoption UI (H5b) depends on that gate. Signed/physical acceptance remains separate. Follows the verified Aiden/Harness² sitrep: memory-index retention, Assistant recovery, tool discovery, verification receipts and protected task evaluation, CLI durable Bot parity, packaged transcript performance, then explicit workflow experiments. Active desktop/CLI Pi packages remain at 1.3.0 and the legacy alias at 0.87.1; an upstream dependency upgrade has a separate gate. This is the current pin observation for this plan; older version/status statements retain their historical context and need reconciliation before implementation. Research baseline and acceptance gates are recorded in the plan. +- [MCP audit and move to Pi's MCP client](mcp-pi-migration-plan.md) — Merged in [PR #568](https://github.com/sambitcreate/aiden-agent/pull/568) (2026-10-21); shipped in 0.60.0; live sign-in acceptance per connector remains (owner accounts). Root cause of failed Figma sign-in: Aiden dropped the client secret the server issues, and Figma, Vercel and Supabase accept only confidential clients; Figma also registered as "Codex", and the catalog was a copy of Codex's plugin directory (45 of 66 entries could not be set up). Shipped: client secrets kept, desktop and CLI MCP client and OAuth on `@earendil-works/pi-mcp` 1.3.0 (legacy SSE removed, SDK client imports blocked), sign-in hardening (background never registers, transient refresh failures keep the sign-in, provider errors and a failure page, durable needs-sign-in and Sign out, pre-registered clients, CIMD behind an unset URL), Pi's built-in CLI MCP extension off, an Aiden-owned connector catalog with Figma Desktop and an unavailable hosted Figma, the feature renamed Connectors (`/connectors`, `/mcp` alias), fingerprinted provider API-key sharing opt-in in Settings → Providers, and privacy-safe MCP failure diagnostics. All 18 hosted OAuth connectors pass `pi-mcp` discovery. Open: host the CIMD document, Figma approval for "Aiden Agent". - [Built-in Aiden knowledge and app actions](aiden-app-knowledge-actions-plan.md) — Merged in [PR #312](https://github.com/sambitcreate/aiden-agent/pull/312) (2026-10-15); shipped in 0.57.0. Physical release acceptance remains open. Bundled offline help and owned skill, real json-render desktop cards, native iOS/Android paired-host controls and Pi CLI selectors/admin commands. Remote revision 19, explicit owner consent, domain gates, replay receipts and registered behavioral tests. Built on Pi 0.87.1 public APIs and merged onto main's Pi 1.2.0 pins; wider actions remain separate follow-ups. [Contract research](aiden-chat-controls-json-render-research.md). - [Interactive visuals on phones](phone-visual-viewer-plan.md) — Merged in [PR #514](https://github.com/sambitcreate/aiden-agent/pull/514) (2026-10-19); desktop host side shipped in 0.59.0, phones need a new TestFlight/Android build. Owner chose tap to open: phones keep the inline snapshot and open a visual full screen in a locked-down web view served by the Mac (`GET /chats/{chatId}/visuals/{visualId}/document`, Remote revision 35, `chat-visual-viewer-v1`). Covers `render_ui` and `render_artifact`; follow-ups confirm on the phone and only fill the composer. - [Quick View cards](../superpowers/plans/2026-10-15-quick-view-cards.md) — Merged in [PR #487](https://github.com/sambitcreate/aiden-agent/pull/487) (2026-10-15); shipped in 0.57.0. Quick View becomes Claude-Code-style stacked cards (Environment, Changes, Context, Subagents) at `SidebarListItem` density. @@ -59,7 +59,7 @@ Research addition 2026-10-03 against `main` at `3f86d41af` (0.52.0): [all-PR inv | -------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Workspace Artifacts](workspace-artifacts-plan.md) | Implemented | Plan written and built 2026-10-19 in PR #554 (all five phases, each reviewed), merged 2026-10-19 and shipped in 0.59.0; Playwright e2e coverage is a follow-up; user guide in [docs/workspace-artifacts.md](../workspace-artifacts.md). Persistent, interactive Aiden UI pages owned by a workspace, shown in an **Artifacts** tab of the Environment panel (offered in the tools launcher) that appears only once an artifact exists. Built on the `render_ui` catalog, with host-run data sources (shell commands and workspace files) under the chat's own workspace permission, a `workspace_artifact` agent tool, and a built-in skill. Desktop (macOS and Linux) only for v1; no CLI, phone or Remote protocol change. | | [Cache-first loading states](cache-first-loading-states-plan.md) | Active | Plan approved (2026-10-17) with desktop read snapshots, recent-chat prefetch and queued sends. iOS Home (I1) and cache-first chat open (part of I2) shipped in PR #516 (merged 2026-10-17). Audits every blank or text-only loading state on iOS, Android and the desktop renderer, and proposes one rule: saved data first, shaped skeletons only for a true first read, empty only when confirmed, quiet connection status. Starts with the iOS home screen, which hides cached Recents behind "Connecting to Aiden Agent…". No Remote protocol change. The write and navigation side (optimistic edits, instant opens) moved to [Instant UI and optimistic updates](instant-ui-optimistic-updates-plan.md) on 2026-10-21. | -| [Instant UI and optimistic updates](instant-ui-optimistic-updates-plan.md) | Implemented | Built 2026-10-21 as the write and navigation side of cache-first loading: every tap changes the screen before the round trip. Desktop Bots show a pending send echo keyed by one idempotent request id, reopen from a remembered live view, and apply rename, create, delete, routine and access edits to the cache before main answers (rollback on error). iOS keeps the Mac's `/server` features per pairing for Bot routes while connecting, opens new chats and Bots from the create receipt, flips routine and scheduled toggles at once, and renames or removes chats in Recents before the Mac answers. Android follows the pairing's refreshed client in an open chat (Retry used to strand it), reads the durable-session gate from saved features, flips routine toggles at once, and opens saved chat deep links before the connection is up. No Remote protocol change. Deferred: send while connecting, phone Steer/Queue bubbles, cold-cache transcript skeleton. | +| [Instant UI and optimistic updates](instant-ui-optimistic-updates-plan.md) | Implemented | Merged in PR #576 (2026-10-21); desktop part shipped in 0.60.0, phones need a new TestFlight/Android build. Built 2026-10-21 as the write and navigation side of cache-first loading: every tap changes the screen before the round trip. Desktop Bots show a pending send echo keyed by one idempotent request id, reopen from a remembered live view, and apply rename, create, delete, routine and access edits to the cache before main answers (rollback on error). iOS keeps the Mac's `/server` features per pairing for Bot routes while connecting, opens new chats and Bots from the create receipt, flips routine and scheduled toggles at once, and renames or removes chats in Recents before the Mac answers. Android follows the pairing's refreshed client in an open chat (Retry used to strand it), reads the durable-session gate from saved features, flips routine toggles at once, and opens saved chat deep links before the connection is up. No Remote protocol change. Deferred: send while connecting, phone Steer/Queue bubbles, cold-cache transcript skeleton. | | [Aiden CLI](aiden-cli-plan.md) | Active | Merged to main in PR #121 (2026-09-26) and shipped in 0.50.0 on pinned Pi 0.87.1; now pinned to Pi 1.3.0 (PR #505, 0.58.0). Phases 0–6 implementation complete; macOS/Linux CLI (57 tests each), Linux native helpers, complete shared subagent suites, root TypeScript/lint, and Android client checks pass. Physical iPhone acceptance is pending an unlocked device. Phase 6 delivered QR remote pairing, scheduled-run notifications (mobile push + desktop), shared desktop memory, daemon autostart, and prebuilt binaries; see the [checklist](aiden-cli-parity-checklist.md). | | [Simulator Devices](simulator-devices-plan.md) | Partial | Phases 0–7 merged in PR #252 (2026-09-26) and shipped in 0.50.0 behind `AIDEN_EXPERIMENTAL_DEVICES`; on by default on macOS from 0.55.0 (2026-10-07), with `AIDEN_EXPERIMENTAL_DEVICES=0` as the kill switch. Environment **Simulator** tab plus `device_*` agent tools, ported from T3 Code (MIT, `1c127066`). Consent-gated pinned `expo-device-hub@0.12.0` + `agent-device@0.21.12`; token-authenticated loopback proxy. Phases 0–3 done (spike, flagged tab shell, main-process toolchain/host/proxy/service/IPC, live stream viewer with controls and screenshot-to-chat; `test:devices` 96 pass; fake-hub Electron E2E; real-Mac acceptance passed). Phase 4 (agent `device_*` tools), Phase 5 (simulators on paired Macs over Aiden Remote; [plan](simulator-devices-phase-5-peers.md)) and Phase 6 (procedural 3D device frames; [plan](simulator-devices-phase-6-3d.md)) done. Phase 7 done: Settings → **Simulator** (consent switches, pinned/installed helper versions, prune, remove installed tools) and [`docs/devices.md`](../devices.md); onboarding coverage is a follow-up now that the flag defaults on. Agent guidance prefers `device_*`/`agent-device` for the watched device but allows shell `xcrun simctl`/`xcodebuild`/`adb` for builds, installs, logs, port forwarding, and diagnostics (T3 #13908; PR #263, 0.51.0). Remaining: real-Mac acceptance for Phases 4–6 (the flag was defaulted on before it). Android emulators and SSH hosts shipped with the [T3 parity upgrade](simulator-devices-t3-parity-plan.md) (PR #391, 0.55.0). | | [Simulator Devices: T3 parity upgrade](simulator-devices-t3-parity-plan.md) | Implemented | Merged in PR #391 (2026-10-08) and shipped in 0.55.0. Six workstreams port T3 Code's newer Devices work: Android emulators and foldables, full 3D feel with original procedural hardware models and iPhone Duo 3D, SSH hosts and tool-version UI, per-device tabs and a floating player, accessibility overlay/event log/erase/clipboard/recording/multi-touch/screenshot save, and a native mobile viewer. Apple GLBs stay unbundled. | diff --git a/package-lock.json b/package-lock.json index 2f0c280ea..4af2fafb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aiden-agent", - "version": "0.59.0", + "version": "0.60.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aiden-agent", - "version": "0.59.0", + "version": "0.60.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7b69de427..b01cfffe6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aiden-agent", - "version": "0.59.0", + "version": "0.60.0", "private": true, "description": "A desktop AI workspace agent for local and hosted models", "keywords": [ From 3ad7fd3dee2d18d93910d697c950d1d8cf1ba5b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 21:45:29 +0000 Subject: [PATCH 2/2] Install and warm the Electron binary before the E2E specs run Electron resolves its binary lazily, so on a fresh CI shard the first spec downloaded it and then paid the first-launch signature check inside its own 60s budget. browser-throttling.spec.ts runs first in shard 3 and failed its first attempt that way on the 0.60.0 release PR (passed on retry, which --fail-on-flaky-tests rejects). A Playwright global setup now resolves and starts the binary once before any spec. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01SE574SfMr5QCRmoYLXy2D7 --- playwright.config.ts | 1 + tests/e2e/global-setup.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/e2e/global-setup.ts diff --git a/playwright.config.ts b/playwright.config.ts index c7e6fb1b2..670ef8860 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -9,6 +9,7 @@ const liveLmStudioAcceptance = process.env.AIDEN_E2E_LIVE_LMSTUDIO === "1"; */ const config: PlaywrightTestConfig = { testDir: "./tests/e2e", + globalSetup: "./tests/e2e/global-setup.ts", testMatch: liveLmStudioAcceptance ? "**/*.live.spec.ts" : "**/*.spec.ts", testIgnore: liveLmStudioAcceptance ? "**/._*" diff --git a/tests/e2e/global-setup.ts b/tests/e2e/global-setup.ts new file mode 100644 index 000000000..f0ec9012c --- /dev/null +++ b/tests/e2e/global-setup.ts @@ -0,0 +1,14 @@ +import { execFile } from "node:child_process"; +import { createRequire } from "node:module"; +import { promisify } from "node:util"; + +/** + * Resolve and start the Electron binary once before any spec. Resolving it + * downloads a missing binary, and the first start of a fresh binary on macOS + * waits for its signature check; neither belongs inside a spec's own timeout. + */ +export default async function globalSetup(): Promise { + const electron = createRequire(import.meta.url)("electron") as string; + // Warming only: a refused start (for example as root) is left to the specs. + await promisify(execFile)(electron, ["--version"], { timeout: 120_000 }).catch(() => undefined); +}