Commit Graph
216 Commits
Author SHA1 Message Date
lefarcenandGitHub 1b35224471 fix(release): pass RELEASE_COMMIT/RUN_ID to every publish step (follow-up to #4144) (#4147)
* fix(release): pass RELEASE_COMMIT/RUN_ID to every publish step so manifests aren't stale

Follow-up to #4144. Setting RELEASE_COMMIT/RELEASE_RUN_ID only on the
publish-metadata step made publish-metadata.ts validateManifest() reject the
per-platform manifests it aggregates:

  refusing stale mac_arm64 platform manifest for 0.10.0.nightly.39: github.runId=0

The staleness guard compares each platform manifest's github.{runId,commit}
against the publish step's currentRunId/currentCommit. The platform build jobs
write those manifests but never received the RELEASE_* env, so they stamped
runId=0 while the metadata step now expected the real runId.

Mirror release-beta.yml: set RELEASE_BRANCH/REPOSITORY/RUN_ATTEMPT/RUN_ID/
WORKFLOW workflow-wide so platform jobs inherit them, and pass RELEASE_COMMIT to
every publish step (4 platforms + metadata) so manifest github.commit matches.

* fix(release): use resolved branch for RELEASE_BRANCH on every publish step

Address review: a workflow-wide RELEASE_BRANCH: ${{ github.ref_name }} stamps
the caller's ref under workflow_call / inputs.ref, so per-platform manifests
would disagree with the aggregate metadata (which uses needs.metadata.outputs.
branch). Drop the top-level github.ref_name and pass RELEASE_BRANCH:
${{ needs.metadata.outputs.branch }} on each publish step instead, matching the
resolved ref the metadata job built. Keep RUN_ID/RUN_ATTEMPT/REPOSITORY/WORKFLOW
workflow-wide (identical for caller and callee). Regression assertion added.
2026-06-11 18:02:22 +08:00
lefarcenandGitHub 0cb06386cc fix(release): restore nightly stableVersion + github attribution for stable promotion (#4144)
The #3995 unified-publisher refactor dropped two fields the stable promotion
gate (scripts/release-stable.ts) requires on a validated nightly:

- publish-metadata.ts omitted stableVersion for the nightly channel, so no
  nightly carried the base version it is a candidate for.
- release-stable.yml never passed RELEASE_COMMIT/BRANCH/REPOSITORY/WORKFLOW/
  RUN_* to the unified publisher (release-beta.yml does), so metadata.github.*
  shipped empty.

Both made every 0.10.0 nightly un-promotable. Restore the nightly stableVersion
field and the RELEASE_* attribution env, mirroring release-beta.yml.
2026-06-11 16:45:41 +08:00
lefarcenandGitHub 1df027c02f chore(amr): bump bundled vela CLI to 0.0.16 (#4071)
* chore(amr): bump bundled vela CLI to 0.0.16

Picks up powerformer/vela#277 (forward OpenCode token usage on the ACP
session/prompt result), so packaged builds that bundle the vela CLI report
real provider token counts for AMR runs instead of token_count_source:unknown.

* chore(nix): refresh pnpm-deps hashes for vela CLI 0.0.16 bump

The lockfile change shifts the daemon/web pnpm-deps fixed-output hashes;
regenerate nix/pnpm-deps.nix so 'Validate Nix flake' passes (hashes taken
from the CI nix-hash-refresh artifact).
2026-06-10 09:46:45 +00:00
44350ad03c fix(packaging): serve baked plugin previews in the packaged app (#4035)
* fix(packaging): serve baked plugin previews in the packaged app

The packaged gallery fell back to live, GPU-expensive iframes instead of the
baked hover-pan clips, because of two gaps that only bite the packaged layout:

1. The baked-preview manifest (data/plugin-previews/manifest.json) was never
   bundled — it's absent from the packaged resource trees. The clips live on R2,
   but without the manifest the daemon has no plugin -> clip mapping.
2. Even bundled, the daemon resolved the manifest dir from PROJECT_ROOT. For the
   prebundled sidecar (Resources/app/prebundled/daemon) PROJECT_ROOT resolves to
   Resources/app — two levels up — which has no data/. The bundled resources
   actually live under OD_RESOURCE_ROOT (Resources/open-design), where every
   other tree (skills, plugins, design-systems…) is resolved from.

Fix both: add data/plugin-previews to BUNDLED_RESOURCE_TREES, and resolve
PLUGIN_PREVIEWS_DIR against DAEMON_RESOURCE_ROOT like the other resource dirs
(explicit OD_PLUGIN_PREVIEWS_DIR override + the dev PROJECT_ROOT layout still
win). Verified end-to-end: a freshly built+installed mac app's packaged daemon
now returns bakedPreview blocks for 126 plugins (R2 URLs) instead of none.

* test(tools-pack): assert the plugin-preview manifest is bundled

* test(daemon): cover packaged plugin-previews dir resolution from OD_RESOURCE_ROOT

Address review: extract resolveDaemonPluginPreviewsDir and prove it resolves the
bundled manifest under the resource root in the packaged layout (and falls back
to PROJECT_ROOT in dev). Also seed data/plugin-previews in the win-resources and
mac packager fixtures so copyBundledResourceTrees' new tree doesn't ENOENT.

* fix(daemon): resolve OD_PLUGIN_PREVIEWS_DIR override from the injected env

Address review: the override branch delegated to resolvePluginPreviewsDir, which
re-reads process.env — so an injected env was ignored and the override path was
untestable. Resolve it directly from env.OD_PLUGIN_PREVIEWS_DIR (absolute
passthrough, relative against projectRoot) and add an override test.

* fix(tools-pack): include plugin-previews in the Windows resource-tree cache key

Address review: adding data/plugin-previews to the bundled tree didn't feed the
Windows resource-tree cache key, so a builder with an existing cache entry would
reuse the old bundle without manifest.json. Hash data/plugin-previews into the
key, bump the cache schema version, and add a regression proving a manifest.json
change forces a cache miss.

---------

Co-authored-by: audit <a@b.c>
2026-06-10 07:45:52 +00:00
3f5fe0f96c feat(amr): forward AMR_CLIENT_SOURCE to vela + source on wallet links (#4005)
* fix(landing): keep auth redirects on AMR wallet

* feat(amr): tag vela CLI launches with AMR_CLIENT_SOURCE + source on wallet links

So vela analytics can attribute Open Design's command funnel and model spend
back to this host (source=open_design), and the AMR wallet landing carries the
source for the web page_view.

- runtimes/env: set AMR_CLIENT_SOURCE=open_design for the amr agent spawn (not
  PII, so no telemetry-consent gate)
- integrations/vela: spawnVelaLogin forwards OD_INSTALLATION_ID (consent-gated)
- web/daemon: AMR wallet recharge URL gains ?source=open_design

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(web): expect ?source=open_design on AMR console/recharge URLs

The AMR console/recharge links the OD app opens into vela web now carry
?source=open_design so vela attributes the visit to Open Design. Update the
console/recharge URL assertions accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(daemon): expect ?source=open_design on AMR insufficient-balance recharge URL

DEFAULT_AMR_RECHARGE_URL now carries the source param for attribution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(amr): bump bundled vela CLI to 0.0.15 + fix e2e recharge URL assertion

- tools/pack: bundle @powerformer/vela-cli 0.0.15 (the release carrying the
  source-attribution logic that reads AMR_CLIENT_SOURCE and tags model_request)
- e2e: expect the insufficient-balance recharge URL to carry ?source=open_design
  (addresses review on DEFAULT_AMR_RECHARGE_URL)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(nix): refresh pnpm-deps hashes after vela CLI bump

The tools/pack @powerformer/vela-cli bump changed pnpm-lock.yaml, invalidating
the daemon/web fetchPnpmDeps fixed-output hashes. Update to the values the Nix
flake check computed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: audit <a@b.c>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:02:48 +00:00
PerishFireandGitHub f9433a7a07 release: harden packaged launcher validation (#3995)
* Unify release metadata publishing

* Allow empty release asset suffix

* Harden beta-s release publishing

* Clean beta Windows release namespace

* Clean Windows launcher namespace

* Retry transient mac notarization uploads

* Control mac notarization upload mode

* Notarize release mac DMGs after build

* Harden release mac DMG notarization retries

* Make beta-s mac updates payload-first

* Bake mac updater metadata URL

* Add unsafe DMG install helper

* Fix beta-s feed and About version

* Improve release validation observability

* Build launcher proto before packaging

* Notarize release-stable mac artifacts

* Enable mac notarization in tools-pack

* test: update packaged release workflow assertions
2026-06-09 11:50:01 +00:00
2ce9d09f41 Fix Windows launcher beta update lifecycle (#3960)
* fix windows beta updater acceptance harness

* fix: harden windows launcher beta updates

* fix: align launcher payload e2e relaunch stub

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: gate windows uninstall version sync

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: verify launcher payload before activation

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-09 06:00:59 +00:00
CaprikaGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
3abedf09c8 [codex] Bump packaged vela-cli to 0.0.13 (#3897)
* Bump packaged vela-cli to 0.0.13

* chore(nix): refresh pnpm deps hash

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-08 09:06:51 +00:00
PerishFireandGitHub 10dfd32a3e Revert "feat: add screenshot-based visual validation to critique loop (#3660)" (#3865)
This reverts commit 931780c914.
2026-06-08 06:24:44 +00:00
netteeGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
931780c914 feat: add screenshot-based visual validation to critique loop (#3660)
* feat(daemon): add visual validation atom

Generated-By: looper 0.9.3 (runner=worker, agent=codex)

* fix(daemon): fail closed in visual validation

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): narrow visual validation defaults

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): add visual validation to default critique stages

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): harden visual validation review fixes

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): fail closed in visual validation discovery

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* test(daemon): tighten visual validation discovery regression

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): match visual validation reference viewport

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): tighten visual validation capture flow

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): fail closed across visual validation refs

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): honor metadata entry file in visual validation

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): capture visual validation through preview route

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): fail closed without preview context

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): disable pre-start visual validation worker

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): narrow visual validation spec discovery

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): stop advertising visual validation as runnable

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): restore visual validation critique worker

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): delay visual validation until run success

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* test(daemon): cover post-run visual validation scheduling

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): gate post-run visual validation before finish

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* chore(nix): refresh pnpm deps hash

* fix(daemon): preserve deferred pipeline ordering

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(packaging): bundle Chromium for visual validation

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(scenarios): keep visual validation out of default critique loop

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(packaging): bundle Playwright headless shell

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(packaging): invalidate Playwright resource cache

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* fix(daemon): include visual-validation in bundled atom roster

Generated-By: looper 0.9.3 (runner=fixer, agent=codex)

* chore(nix): refresh pnpm deps hash

* fix: restore pre-run plugin surfaces and nix hashes

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: preserve pre-run surfaces and tolerate headed-only chromium bundles

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: launch visual validation with bundled chromium

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: avoid Playwright fixture teardown races

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: fail packaged visual validation on shell-only chromium

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: isolate synthetic Playwright test bundles

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-08 04:03:43 +00:00
7a1ad1a438 feat(packaged): add launcher payload updates (#3812)
* feat(packaged): add launcher payload runtime

* ci: focus launcher payload windows smoke

* ci: publish beta mac x64 launcher payload

* test(e2e): harden payload updater smoke

* fix(pack): inject launch config for installed windows smoke

* fix(daemon): allow packaged payload resource roots

* fix(updater): relaunch after payload update

* test(e2e): refresh windows updater UI status

* test(e2e): ensure windows smoke reaches main shell

* test(e2e): skip onboarding before windows updater smoke

* fix(updater): detach windows payload relaunch helper

* test(e2e): run payload smoke without updater dry run

* fix(desktop): relaunch windows payload updates on quit

* fix(updater): preserve installed payload relaunch target

* fix(updater): require launcher target for payload updates

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* test(e2e): align mac x64 artifact mode assertion

* test(e2e): canonicalize launcher relaunch root

* fix(updater): validate launcher payload config before activation

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix(updater): require live launcher target for payload updates

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-07 07:38:57 +00:00
0582749df5 Refactor beta release workflows (#3741)
* Refactor beta release workflows

* Fix beta workflow runner bootstrap

* Fix self-hosted beta release bootstrap

* Use pnpm cmd in self-hosted beta Windows bootstrap

* Pass mac notarization profile to beta builds

* Allow direct Apple notarization credentials

* Fix beta Windows asset handoff

* Skip Windows report zip in beta publish

* Fix beta metadata workflow cleanup

* Stabilize self-hosted beta metadata checkout

* Retry self-hosted beta manifest downloads

* Use release storage for self-hosted manifests

* Isolate self-hosted beta metadata scripts

* Add Windows pack phase diagnostics

* Disable beta self-hosted Windows security checks

* Remove beta Windows security preflight

* Add release-beta-s version reservation

* Fix beta daily workflow caller inputs

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Guard beta latest publish and build-only runs

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Tighten beta latest publish guards

* Fix beta publish artifact suffix handling

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Fix beta metadata suffix and Windows publish target handling

* Fix self-hosted beta release ref and publish guards

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Fix beta release commit identity wiring

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Fix hosted beta checkout pinning

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-05 13:01:54 +00:00
CaprikaGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
02a1a5a537 [codex] fix connector refresh propagation (#3723)
* fix connector refresh propagation

* address connector refresh review feedback

* bump vela cli to 0.0.12

* chore(nix): refresh pnpm deps hash

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-05 10:31:46 +00:00
817885bc34 feat(amr): forward installationId to the vela CLI for analytics correlation (#3634)
* feat(amr): forward installationId to the vela CLI for analytics correlation

When the daemon spawns the vela (amr) CLI, pass this installation's id as
OD_INSTALLATION_ID once the user has consented to telemetry. vela attaches it
to its analytics events so CLI activity can be correlated back to the
open-design installation that launched it.

Resolution mirrors the /api/analytics/config handler: consent =
telemetry.metrics === true; channel-root installation.json wins over the legacy
app-config.json field. Reads are synchronous and best-effort — any missing
file / parse error / withheld consent simply omits the id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(amr): bump bundled @powerformer/vela-cli to 0.0.11

0.0.11 is the first vela CLI release that consumes OD_INSTALLATION_ID (the env
forwarded by this PR's daemon change), so bundle it to ship the installationId
analytics correlation end-to-end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(amr): mirror readAppConfig defaults in installationId forwarding

Address review: the previous helper bailed out entirely when app-config.json
was missing or corrupt, so vela correlation was skipped even though the web
analytics config (via readAppConfig) would still emit events with the same
installationId. Now match readAppConfig / applyTelemetryDefaults semantics:

- a missing/corrupt app-config.json is treated as an empty config, not a hard
  failure — we still consult the channel-root installation.json for the id;
- telemetry defaults to on (opt-out model); the id is withheld only when the
  user has explicitly set telemetry.metrics to a non-true value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(nix): refresh pnpm-deps hashes for vela-cli 0.0.11 bump

The bundled @powerformer/vela-cli bump changed pnpm-lock.yaml, so the
fixed-output pnpm-deps derivation hashes no longer matched. Apply the
CI-generated hash refresh (nix-hash-refresh artifact).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(amr): resolve installationId via shared readAppConfigSync

Address review: instead of hand-replicating readAppConfig's parsing in the
spawn-env helper (which kept diverging on edge cases like invalid telemetry
shapes), reuse the real logic. Add a synchronous mirror of readAppConfig:

- installation.ts: extract parseInstallationFile + add readInstallationFileSync
- app-config.ts: add exported readAppConfigSync (same filterAllowedKeys /
  validateTelemetry / applyTelemetryDefaults pipeline as the async path; only
  skips the best-effort migration write)
- env.ts: amrAnalyticsIdentityEnv now calls readAppConfigSync and gates on
  telemetry.metrics === true (matching analytics.ts), so vela correlation can
  no longer drift from the web analytics config for any config shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(nix): refresh pnpm-deps hashes after main merge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 09:44:07 +00:00
556eb7f065 chore(release): bump main to 0.10.0 + clear plugin context after send (#3708 residual) (#3731)
* fix(web): clear plugin context after send

* chore(release): bump version 0.9.1 -> 0.10.0 (prep main for 0.10.0 beta from main; clears the beta anti-regression gate)

---------

Co-authored-by: yinjialu <2481044901@qq.com>
Co-authored-by: lefarcen <ontf116@gmail.com>
2026-06-05 08:35:31 +00:00
8f6cdaaba1 Harden release-beta-s self-hosted beta lane (#3641)
* Fix Windows smoke launcher payload layout

* Harden release-beta-s runner probe

* Use cmd shell for release-beta-s probe

* Add win beta build script for self-hosted lane

* Keep release-beta-s checkout lightweight

* Use explicit local beta version for release-beta-s

* Install dependencies in beta build script

* Repair Electron dist before Windows beta build

* Add release-beta-s timings and diagnostics

* Polish release-beta-s observability guardrails

* Trace tools-pack cache materialization

* Reuse materialized workspace build cache

* Trace Windows packaging segments

* Trace Windows archive process details

* Reuse installer unpacked materialization

* Preserve reusable Windows unpacked projection

* Add fast smoke telemetry for self-hosted beta

* Cache Windows archive outputs for beta lane

* Tighten fast Windows smoke lane

* Scope Windows beta artifact reporting by target

* Clean stale Windows target artifacts

* Wire Windows Authenticode signing

* Expose self-hosted beta validation modes

* Add core Windows smoke profile

* Decouple core smoke from updater fixture version

* Trace Windows smoke lifecycle steps

* Skip unpacked materialization on NSIS cache hit

* Add Nexu S3 beta publisher

* Harden self-hosted Windows beta lane

* Use Windows PowerShell for publish probe

* Run publish probe via repo script

* Require explicit self-hosted S3 public origin

* Run real installer acceptance for external feeds

* Skip ready-prompt checks for external feeds

* Relax optional self-hosted update inputs

* Tolerate BOM in self-hosted publish probe

* Converge self-hosted beta metadata and signing flow

* Run self-hosted beta metadata prep on the runner lane

* Use Windows PowerShell for self-hosted beta metadata checks

* Bypass execution policy in self-hosted beta metadata checks

* Split self-hosted beta metadata publish

* Suppress mc output in beta publish helpers

* Split Windows NSIS payload cache layers

* Require fresh tool dist metadata

* Preserve Windows overlay payload paths

* Default beta-s gate to core

* Optimize win beta packaging reuse

* simplify windows full smoke lifecycle

* preserve onboarding state after windows upgrade smoke

* fix windows tools-pack test portability

* recover stale tools-pack locks

* Allow auto signing fallback for beta builds

* Add self-hosted beta mac arm64 lane

* Quote self-hosted signing choices

* Run self-hosted beta metadata on mac lane

* Use shallow metadata checkout on self-hosted beta

* Use sparse beta metadata checkout

* Make mac release profile bash-compatible

* Allow file sparse metadata checkout

* Isolate self-hosted metadata checkout

* Clear sparse skip-worktree before mac build

* Isolate self-hosted mac build checkout

* Run self-hosted beta publish without aws cli

* Use unsigned beta prefix for mixed platform publish

* Sparse checkout self-hosted beta publish

* Publish mac beta platform before metadata

* Pass mac publish release profile

* Probe mac signing keychain import

* Expand mac signing preflight variants

* Use sudo keychain helper for self-hosted mac signing

* Route self-hosted mac codesign through sudo wrapper

* Normalize mac signing identity name

* Harden self-hosted mac notarization

* Optimize self-hosted mac release cache

* Fix mac signing helper cleanup

* Retry transient mac notarization uploads

* Keep mac notarization fail-fast

* Reduce mac symlink diagnostics noise

* Normalize self-hosted beta platform inputs

* chore: bump beta base version to 0.9.1

* chore: tune release tools-pack cache retention

* test: add temporary cache validation marker

* chore: key windows nsis base payload by content

* Revert "test: add temporary cache validation marker"

This reverts commit e8487bfd3e.

* chore: probe windows nsis payload cache before materializing

* chore: align self-hosted beta smoke modes

* chore: skip mac smoke report upload when disabled

* chore: default self-hosted beta to publish both platforms

* chore: make beta signing modes explicit

* Add release report artifacts and metatool metadata

* Fix Windows release report zip publishing

* Remove artifact handoff from release beta self-hosted lane

* Fix release beta merge follow-ups

* Fix release beta install regressions

* Restore web build dependencies

* Route Windows beta downloads through mirrors

* Use PowerShell 7 for Windows beta lane

* Fix release beta preflight gates

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Fix Windows beta smoke update reuse

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Fix release beta review follow-ups

* Refresh Nix pnpm dependency hashes

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Fix Windows signtool fallback resolution

* Reject stale beta platform manifests

* Validate beta platform manifest run identity

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Validate beta manifest commit identity

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Allow beta metadata publish reruns

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Build tools-pack fully in beta Windows bootstrap

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Emit r2 metadata for Windows beta publish

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Stabilize beta publish browser test

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Stabilize Windows beta metadata smoke test

Generated-By: looper 0.9.4 (runner=fixer, agent=codex)

* Restore multi-platform Vela CLI package

* Refresh Nix pnpm dependency hashes

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-05 03:00:55 +00:00
CaprikaGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
86d92e9586 [codex] bump vela-cli to 0.0.10 (#3577)
* bump vela-cli to 0.0.10

* chore(nix): refresh pnpm deps hash

* chore(nix): refresh pnpm deps hash

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-03 15:12:06 +00:00
lefarcen fa59871428 Merge origin/main into release/v0.9.0 — back-merge to reconcile v0.9.0 with main
Syncs release/v0.9.0 with main so PR #3282 (release/v0.9.0 → main) merges
cleanly. 17 conflicts resolved by commit-time + per-area ownership:

- Comment/edit/preview: keep main (newer). FileViewer combines main's #3438
  (pin manual-edit inspector, no full-height panel) + #3347 deck/queue work
  WITH release's #3401 hover-card anti-flicker grafted on; #2879 Button kept.
- Toolbar: footer layout (reviewer-validated, ca03a7e0f) overrides main #3447 —
  CLI/agent selector in the composer footer, settings gear in the header, no
  settings item in the AvatarMenu dropdown.
- AMR: keep release's client-side model polling (mergeAmrModelsIntoAgents) and
  all release AMR fixes (#3450 sign-out, #3504 console, #3487 acp tool-only
  guard, amrCloudVisible onboarding, #3511 analytics provider id).
- Pet composer button stays removed (release decision); only /pet slash props
  retained.
- Pickers: main's providerModelsCache approach.

Validation: web typecheck + guard + full web suite (2638) + daemon suite green.
2026-06-03 17:21:00 +08:00
Marc ChanGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
b6105a8cab refactor(web): extract shared UI primitives (#2879)
* refactor(web): extract shared UI primitives

* fix(web): include components in packaged builds

* fix(nix): refresh daemon pnpm deps hash

* fix(components): trim unused primitive exports

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* refactor(components): split primitives into modules

* fix(web): restore finalize cancel link

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* docs: prefer shared web components

* refactor(web): migrate easy primitives

* fix(web): preserve migrated control styling

* fix(nix): refresh daemon pnpm deps hash

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* fix(web): keep Continue in CLI action styling

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* fix(web): keep finalize action styling

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* chore(nix): refresh pnpm deps hash

* fix(web): preserve MCP row label styling

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* fix(web): resolve ChatComposer merge conflict

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(e2e): stop leaked tools-dev runtime before retry

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* ci: include packages/components in change scopes

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(pack): include components in linux internal packages

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(pack): ship components package in packed installs

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(pack): align win app test with internal packages

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(components): use global visually hidden class

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(components): add development export for source builds

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(components): restore selected custom select state

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(tools-pack): rebuild components for packaged web builds

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* test(tools-pack): cover components workspace artifacts

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(ci): include components in tools-pack scope

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-03 03:22:29 +00:00
lefarcenGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
f591ecc9d8 chore(tools-pack): bump vela-cli to 0.0.9 (#3507)
* chore(tools-pack): bump vela-cli to 0.0.9

* chore(nix): refresh pnpm deps hash

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-02 19:16:23 +08:00
lefarcenGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
06b57ad832 chore(tools-pack): bump @powerformer/vela-cli to 0.0.8 (#3486)
* chore(tools-pack): bump @powerformer/vela-cli to 0.0.8

* chore(nix): refresh pnpm deps hash

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-02 16:55:04 +08:00
CaprikaGitHubCodexopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
23229ed94f chore(pack): bump vela cli to 0.0.7 (#3441)
* chore(pack): bump vela cli to 0.0.7

* chore(nix): refresh pnpm deps hash

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-01 22:06:28 +08:00
lefarcenGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
5916cfa12d chore(pack): ship AMR on Windows/Intel-mac via vela-cli 0.0.6 + require-vela-cli guard (#3310)
* chore(pack): bump @powerformer/vela-cli to 0.0.6 for multi-platform AMR

vela-cli 0.0.6 publishes per-platform native packages for all five targets
(darwin-arm64/x64, linux-arm64/x64, win32-x64); 0.0.4 only shipped
darwin-arm64. tools-pack bundles the CLI by resolving @powerformer/vela-cli
for the packaging host, so this lets the Windows and Intel-mac packaged apps
bundle Vela/AMR instead of silently shipping without it.

* chore(nix): refresh pnpm deps hash

* ci(release): require bundled Vela CLI for mac/win packaging

tools-pack bundles the Vela CLI optionally and silently skips it when the
platform binary cannot be resolved — which is exactly how Windows and
Intel-mac shipped without AMR unnoticed. Now that vela-cli 0.0.6 publishes
all platform packages, pass --require-vela-cli to the mac (arm64 + intel) and
win build commands so packaging fails loudly if the bundled CLI is missing
instead of producing an AMR-less app. Linux (containerized) is left as-is for
a separate follow-up.

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-29 20:58:13 +08:00
lefarcen dacd2be814 Merge remote-tracking branch 'origin/main' into release/v0.9.0 2026-05-29 18:15:17 +08:00
2ea2c91a92 fix(pack): add missing download and host packages to Linux INTERNAL_PACKAGES (#2837)
* fix(pack): add missing download and host packages to Linux INTERNAL_PACKAGES

The native (non-containerized) Linux AppImage build fails with npm 404
errors because @open-design/download and @open-design/host — runtime
dependencies of @open-design/desktop and @open-design/web — were not
included in the INTERNAL_PACKAGES list. Without tarballs for these two
packages, npm install in the assembled app directory tries to resolve
them from the public registry where they don't exist.

Add both packages to INTERNAL_PACKAGES and their build steps to
buildWorkspaceArtifacts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(pack): apply same download/host fix to mac and win lanes, add regression test

1. Extend the INTERNAL_PACKAGES fix to mac/constants.ts, mac/workspace.ts, win/constants.ts, and win/app.ts so all three pack lanes produce tarballs for @open-design/download and @open-design/host.

2. Add internal-packages-coverage.test.ts that derives required workspace runtime deps from apps/desktop and apps/web package.json files and asserts every pack lane's INTERNAL_PACKAGES includes them. This prevents the same drift from recurring when a new workspace dependency is added.

3. Update win-app.test.ts and workspace-build.test.ts mock directory lists to include the two new packages.

* fix(pack): include runtime packages in workspace build cache

* fix(pack): install platform with desktop prebundle packages

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-05-29 09:44:03 +00:00
lefarcenandGitHub 2ea4fcf981 fix(pack): stop release builds depending on node_modules Electron dist (#3289)
Nightly/stable mac arm64 and win x64 packaging failed on release/v0.9.0:

- mac arm64: the "Verify mac Electron framework symlinks" step exited 1
  because node_modules' electron dist had no
  `Electron Framework.framework`.
- win x64: electron-builder failed renaming `electron.exe` ->
  `Open Design.exe` with ENOENT because `electron.exe` was missing from
  the same dist.

Root cause: pnpm does not reliably materialize Electron's prebuilt dist
(a symlinked `.framework` on mac, the unpacked binaries on win) under
`node_modules/.pnpm/electron@*/.../dist` on the CI runners. Both failing
runs were cache-cold (the win job logged "Cache not found"), and the
beta workflow's non-fatal inspect step shows the identical missing
framework on a green run — so it is not a GHA-cache or download-speed
problem, and it is not reproducible locally.

The mac builder never passed `electronDist` and has stayed green because
electron-builder downloads its own Electron (~1s for 116 MB on CI). The
win and linux builders pinned `electronDist` at the fragile node_modules
dist, and stable/preview still ran the old fatal verify step (beta was
already relaxed to a non-fatal inspect).

This change aligns every platform with the mac approach:

- win/linux: drop `electronDist` so electron-builder downloads its own
  complete Electron for the target platform.
- release-stable.yml / release-preview.yml: replace the fatal "Verify
  mac Electron framework symlinks" step with the non-fatal "Inspect"
  step already used by release-beta.yml (electron-builder is the source
  of truth for whether packaging works).

Validated: tools-pack typecheck, guard, and the win/linux/win-app/
workspace-build pack test suites all pass. Real win/linux packaging is
validated by the release workflow itself.
2026-05-29 16:44:55 +08:00
lefarcen e69306ecbe chore(release): prepare 0.9.0 2026-05-29 15:40:34 +08:00
youcef zrandGitHub d6d42c3600 fix(pack): bundle download and host packages in Linux AppImage assembly (#2845)
The Linux AppImage path assembles INTERNAL_PACKAGES as `file:` tarballs
and runs `npm install --omit=dev` in an isolated app directory. `pnpm
pack` rewrites each tarball's `workspace:*` refs to a concrete version,
so any runtime @open-design/* dependency missing from INTERNAL_PACKAGES
is resolved from the public npm registry and 404s.

Linux ships webOutputMode "server" and tarball-installs every
INTERNAL_PACKAGES entry, including @open-design/desktop and
@open-design/web. @open-design/host (dep of web + desktop, added in
#2246) and @open-design/download (dep of desktop, added in #2677) landed
after the Linux package list was written and were never added to it, so
`pnpm exec tools-pack linux build --to appimage` fails with:

  npm error 404 Not Found - GET .../@open-design%2fdownload

mac/win default to "standalone", where desktop/web/packaged/daemon are
prebundled with esbuild and excluded from the tarball install
(shouldInstallInternalPackageFor{Mac,Win}Prebundle). The packages they
do install have no download/host dependency, so those lanes correctly
omit them and need no change — this fix stays scoped to linux.ts and
touches no mac/win or workspace-build code.

Add both packages to the Linux INTERNAL_PACKAGES and build them in
buildWorkspaceArtifacts (download depends on platform). Add a cross-lane
regression test that, for each lane, derives the set it actually installs
(honoring the standalone prebundle exclusion) and asserts that set is
closed under its runtime @open-design/* dependencies. The test is red on
the linux lane without this fix and green with it, while mac/win pass
either way — encoding why only Linux needs these packages.
2026-05-29 07:25:03 +00:00
CaprikaGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>lefarcen
76c7d31c53 chore: bump vela cli to 0.0.4 (#3239)
* chore: bump vela cli to 0.0.4-test.0

* chore: refresh lockfile for vela cli 0.0.4-test.0

* chore(nix): refresh pnpm deps hash

* fix: materialize electron before mac release checks

* fix: rebuild electron when mac framework links are invalid

* revert: drop release workflow experiments

* chore(nix): refresh pnpm deps hash

* fix: stop blocking beta mac release on electron symlink preflight

* fix: stop using custom electron dist for beta mac packaging

* fix: guard oversized chat images and opencode overflow

* chore: bump vela cli to 0.0.4

* chore(nix): refresh pnpm deps hash

* fix(daemon): surface prompt-image stat failures instead of dropping them

resolveSafePromptImagePaths only swallowed unresolvable path input; once a
path was confirmed inside UPLOAD_DIR and existed, a statSync failure
(EACCES/EPERM, a file vanishing mid-run) silently dropped the image and let
the run continue without that prompt context. Since this helper is now also
the 1 MB enforcement point, that turned an infra/validation failure into a
'successful' run with missing required context.

Collect those into a new failedImages bucket and fail the run with
INTERNAL_ERROR at the call site, mirroring the oversized-image guard. Add a
unit test covering statSync throwing.

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
2026-05-29 06:41:17 +00:00
lefarcenGitHuba1chztAmyMasonCaprikaopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
df8a0faff6 feat(runtimes): register AMR (vela) as an ACP stdio agent (#2355)
* feat(runtimes): register AMR (vela) as an ACP stdio agent

AMR is the vela CLI's ACP runtime mode. `vela agent run --runtime opencode`
speaks ACP JSON-RPC over stdio (see vela's
`specs/current/runtime/manual-agent-run-openrouter.md`); per
`docs/new-agent-runtime-acp.md` we expose it through the same `streamFormat:
'acp-json-rpc'` transport that already powers Hermes, Devin, Kimi, etc.

The new `defs/amr.ts` is the entire wiring — `buildArgs` returns
`['agent', 'run', '--runtime', 'opencode']`, `fetchModels` reuses
`detectAcpModels`, and the fallback list seeds the OpenRouter ids vela's
e2e baseline uses. `executables.ts`/`app-config.ts`/`metadata.ts` get the
matching `VELA_BIN`/`VELA_LINK_URL`/`VELA_RUNTIME_KEY`/`VELA_OPENCODE_BIN`
allowlist + install/docs URLs, so users can configure the per-agent env in
Settings without leaking into other adapters.

Coverage: `tests/fixtures/fake-vela.mjs` is a minimal ACP stub that returns
the documented `initialize` / `session/new` / `session/set_model` /
`session/prompt` shapes; `tests/amr-acp-integration.test.ts` spawns it via
`child_process.spawn` and drives a full turn through `attachAcpSession` and
`detectAcpModels`, so the ACP transport contract for AMR is end-to-end
verified locally even before a real `vela` binary is installed.

Validated:
- pnpm guard
- pnpm typecheck (all workspace projects)
- pnpm --filter @open-design/daemon test (2881/2881)

Deferred: real OpenRouter-backed turn through a built `vela` binary —
the runtime def needs no changes for that path, only `VELA_RUNTIME_KEY`
and `VELA_LINK_URL` in env (or Settings).

* fix(runtimes/amr): pin a concrete default model and bare openai ids

End-to-end validation against a freshly-built `vela` (nexu-io/vela@main)
+ OpenRouter surfaced two contract details the first AMR runtime def
got wrong:

1. vela rejects `session/prompt` with `session/set_model must be called
   before session/prompt`. attachAcpSession in apps/daemon/src/acp.ts
   skips set_model whenever the picked model is the synthetic 'default'
   id, so AMR's fallback list must NOT include DEFAULT_MODEL_OPTION. The
   def now ships a concrete `gpt-5.4-mini` as both `fetchModels`'
   default option and `fallbackModels[0]`, which makes attachAcpSession
   always send a real `session/set_model` for AMR turns.

2. `vela --runtime opencode` auto-prepends `openai/` to whatever modelId
   it forwards to opencode's openai provider. With OpenRouter-style ids
   like `openai/gpt-5.4-mini`, opencode receives the double-prefixed
   `openai/openai/gpt-5.4-mini` and replies `ProviderModelNotFoundError`.
   The new fallback list ships the bare ids opencode's openai registry
   actually knows about (gpt-5.4, gpt-5.4-mini, gpt-5.4-fast, etc.).

Stub + tests:
- tests/fixtures/fake-vela.mjs now enforces the set_model gate the same
  way real vela does, so a regression that silently goes back to
  model: 'default' would surface as a fatal error in tests instead of a
  hidden production failure.
- tests/amr-acp-integration.test.ts pins both contracts: no 'default' /
  no 'openai/' prefix in fallbackModels, and a negative case that
  asserts session/prompt fails when no model is set.

Adds `apps/daemon/scripts/verify-amr-real-vela.mjs` — a small dev-time
runner that drives `attachAcpSession` against a real `vela` binary and
prints the daemon's chat events, so future protocol drift can be checked
against an actual OpenRouter call.

Verified locally: `vela agent run --runtime opencode` + OpenRouter
returns the prompted string ("AMR-E2E-PASS") through the full daemon
pipeline; daemon test suite stays 2883/2883.

* fix(runtimes/amr): substitute concrete model when chat run sends 'default'

A plugin-driven AMR run from the UI surfaced a real-world hole in the
prior commit:

  json-rpc id 3: session/set_model must be called before session/prompt

The Default-design-router plugin (and any caller that doesn't pin a
real model) sends `model: 'default'` straight through, which the AMR
runtime def cannot accept — vela rejects `session/prompt` without
`session/set_model` and attachAcpSession skips set_model whenever
model === 'default'. Just leaving DEFAULT_MODEL_OPTION out of the
adapter's `fallbackModels` is not enough: the chat-run handler in
server.ts still forwarded 'default' verbatim.

This adds `resolveModelForAgent(def, resolved, env?)` as the
single source of truth for the substitution:

  1. If the caller picked a real id, pass it through.
  2. Else, if `def.defaultModelEnvVar` is set and the daemon process
     env has a non-empty value for it, return that (operator escape
     hatch — see below).
  3. Else, if the def's `fallbackModels` does NOT contain a 'default'
     id, return `fallbackModels[0].id`.
  4. Else, return the original value (the historic shape — defs that
     list 'default' themselves are untouched).

AMR sets `defaultModelEnvVar: 'VELA_DEFAULT_MODEL'`, so when
opencode's openai-provider registry deprecates `gpt-5.4-mini`
upstream, an operator can swap the fallback id without a code change
by exporting `VELA_DEFAULT_MODEL=gpt-5.5` before launching tools-dev
/ od. Worth noting the env var must live in the daemon's `process.env`
(Settings-UI per-agent env values only reach the spawned child, not
the daemon's resolver) — the new field's docblock spells this out.

Coverage:
- `tests/runtimes/resolve-model.test.ts` — 8 unit tests covering all
  four resolver branches plus the env-override happy path / fallback /
  ignore-when-user-picked-a-real-id case.
- `pnpm --filter @open-design/daemon typecheck` clean.

* chore(runtimes/amr): move AMR to the top of the base agent list

So `AMR (vela)` shows up first in the agent picker / status views,
ahead of claude / codex. Pure ordering change; no behavior delta.

* feat(amr): Sign-in / Sign-out button on the AMR Settings card

The first half of the AMR work assumed the operator would set
VELA_RUNTIME_KEY / VELA_LINK_URL on the daemon process and never
surfaced login state to users. This adds the missing UX so a fresh
install can drive the full path from Settings:

  - GET  /api/integrations/vela/status   reads ~/.vela/config.json
    for the active profile and returns { loggedIn, profile, user }
    (without leaking the runtime/control keys themselves).
  - POST /api/integrations/vela/login    spawns `vela login` once
    (409 if one is already in flight). The vela CLI opens the user's
    browser to the device-authorization page itself — Open Design
    only needs to kick the subprocess off.
  - POST /api/integrations/vela/logout   removes ~/.vela/config.json
    so the next status read returns logged-out.

`AmrAgentCard` is a dedicated agent-card component for AMR because
the existing `<button>` row can't host an interactive sub-control
(nested interactive elements). It polls /status after a login click
until the daemon reports loggedIn=true (or 5 minutes elapse), and
exposes a Sign-out action on hover. Other adapters (claude, codex,
hermes, …) keep their existing `<button>` card.

i18n: 8 new keys (settings.amrLogin / Logout / LoggingIn / etc.)
added to en + zh-CN. Other locales spread `en` and inherit the
English copy until translations land.

Coverage:
- `tests/integrations/vela.test.ts` pins the config.json reader
  against a tmp HOME — including the negative case where a profile
  has user info but no runtimeKey (still logged-out), and the
  secret-leak guard ("rt-secret-*" must not appear in the projection
  payload).
- `tests/components/AmrAgentCard.test.tsx` covers all four UI
  states (logged-out, logging-in, logged-in, logging-out) plus the
  click-propagation invariant the divergent card was built to keep.

`pnpm --filter @open-design/daemon test` 2901 / 2901 passing.
`pnpm --filter @open-design/web test` 1719 / 1719 passing.
`pnpm typecheck` + `pnpm guard` clean.

Dev script side-effects: `apps/daemon/scripts/verify-amr-real-vela.mjs`
no longer requires both VELA_RUNTIME_KEY and VELA_LINK_URL — if
VELA_PROFILE is set, the vela CLI is allowed to resolve credentials
from `~/.vela/config.json`. Added the two AMR `.mjs` fixtures to
`scripts/guard.ts` allowlist with the executable-fixture / dev-runner
rationale.

* fix(connection-test): substitute model for AMR before attachAcpSession

The chat-run path in server.ts already routes the requested model through
`resolveModelForAgent` so AMR / vela (whose CLI demands an explicit
`session/set_model` before `session/prompt`) gets the def's first
concrete fallback id when the chat run ships `model: 'default'`.
`connectionTest.ts` was wiring `attachAcpSession({ ..., model: model ?? null })`
directly, which made the Test Connection button on the AMR Settings
card deadlock with the same `session/set_model must be called before
session/prompt` error the chat-run path already handles — surfaced as a
permanent "Testing connection…" spinner in the UI.

Reuse the same helper here so Test Connection mirrors chat-run behavior.

* test(amr): three-layer end-to-end coverage for the AMR login + turn flow

The PR up to this point shipped runtime + UI code with unit-level Vitest
coverage. This commit adds the cross-layer regression net the live demo
relied on:

1. apps/daemon/tests/integrations/vela.routes.test.ts (HTTP, Vitest)
   Spins up the real daemon Express app via `startServer({port:0,...})`,
   persists `agentCliEnv.amr.VELA_BIN = <fake>` into app-config.json,
   and exercises every /api/integrations/vela/* endpoint against the
   extended fake-vela stub:
     - status reads ~/.vela/config.json under various states
     - login spawns the fake, waits for config.json to appear, returns
       pid + startedAt + profile
     - 409 already-running guard with the stub's delay knob
     - logout removes the file (idempotent)
     - secrets (runtimeKey / controlKey) never leak in the projection
     - login → status round-trip flips loggedIn=false → true

2. e2e/tests/amr/turn.test.ts (tools-dev orchestrated, Vitest)
   Boots a namespaced daemon + web pair through `createSmokeSuite`,
   inlines a self-contained fake `vela` binary that handles BOTH
   `vela login` (writes ~/.vela/config.json) and
   `vela agent run --runtime opencode` (ACP stdio with the
   `session/set_model must precede session/prompt` gate the real binary
   enforces), then drives a complete /api/runs lifecycle for
   `agentId: 'amr', model: 'default'` and asserts the assistant message
   captures the fake's streamed text. This is the test that would have
   surfaced today's plugin-default-model regression (the `set_model
   before prompt` error) at PR time instead of demo time.

3. e2e/ui/amr-login-pill.test.ts (Playwright)
   Mocks /api/agents + /api/integrations/vela/{status,login,logout}
   to drive the Settings AMR card through the full Sign in → Signed in
   → Sign out cycle. Pins the AmrLoginPill polling contract and the
   aria-label semantics (the pill's accessible name is "Sign out" once
   logged in, regardless of which label the hover-state text shows).

fake-vela.mjs extensions:
   - Handles `vela login` argv by writing
     ~/.vela/config.json for the active VELA_PROFILE and exiting 0 —
     mirrors real vela's on-disk side-effect without the device-auth
     loop.
   - FAKE_VELA_LOGIN_DELAY_MS knob so route tests can observe the
     in-flight state of the spawn lifecycle.
   - FAKE_VELA_LOGIN_USER_EMAIL / _USER_PLAN to assert the surfaced
     user fields end-to-end.

Validated:
   - `pnpm guard` + `pnpm typecheck` (all workspace projects)
   - `pnpm --filter @open-design/daemon test`: 2998 / 2998 passing,
     including the new 8-test integration suite.
   - `cd e2e && pnpm test tests/amr`: 1 / 1 passing.
   - `cd e2e && pnpm exec playwright test ui/amr-login-pill.test.ts`:
     1 / 1 passing (6.7s).

* feat(amr): package native cli and refine login ui

* feat(amr): wire vela cli beta packaging

* docs(amr): document vela ci packaging review

* docs(amr): refine vela ci integration review

* fix(ci): refresh nix pnpm dependency hashes

* fix(pack): clean up Vela CLI packaging

* fix(pack): bundle Vela CLI support files

* fix(amr): recover login attempts from stale auth state

* test: expand AMR and automations coverage

* fix(amr): address review follow-ups

* test(web): align tasks fixtures with contracts

* fix(daemon): type wildcard route params

* fix(ci): refresh PR merge validation

* fix(amr): clear env credentials on logout

* feat(settings): inline local CLI model configuration

* fix(amr): recognize daemon env credentials

* [codex] Fix Vela companion packaging (#2979)

* Fix Vela companion packaging

* Update Nix pnpm dependency hashes

* [codex] Surface AMR account failures (#2980)

* fix: surface AMR account failures

* fix: cover AMR recovery error guidance

* chore: bump beta base version to 0.8.1 (#2990)

* Fix AMR profile and packaged runtime review issues

* Detect packaged AMR OpenCode companion tree

* feat(web): polish AMR frontend flows

* Polish AMR onboarding card

* fix: read AMR login state from dot-amr config (#3048)

* test: tighten AMR credential and packaging coverage

* test: restore AMR executable test env helper

* [codex] Fix packaged mac Dock identity and AMR label (#3076)

* Fix packaged mac sidecar Dock identity

* Rename AMR assistant label

* Fix AMR live models and dot-amr login state (#3073)

* fix: read AMR login state from dot-amr config

* fix: load live AMR models before runs

* fix: point AMR onboarding link to production wallet

* fix: address AMR model review feedback

* fix: persist live AMR model fallback

* [codex] Fix AMR link catalog model ids (#3088)

* Fix packaged mac sidecar Dock identity

* Rename AMR assistant label

* Fix AMR link catalog model ids

* Fix AMR model normalization typecheck

* Use live AMR model for default runs

* fix: polish AMR runtime settings UI

* Accelerate AMR startup defaults (#3092)

* Surface AMR insufficient balance wallet URL (#3099)

* fix(web): polish onboarding controls (#3112)

* fix(web): show CLI scan loading state

* Avoid duplicate AMR wallet recharge links (#3117)

* Avoid duplicate AMR wallet recharge links

* Use Vela CLI 0.0.3 test package

* chore(nix): refresh pnpm deps hash

* Fix AMR wallet guidance display

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>

* chore(pack): pin Vela CLI 0.0.3-test.1 (#3127)

* chore(nix): refresh pnpm deps hash

* chore(pack): pin Vela CLI 0.0.3

* chore(nix): refresh pnpm deps hash

* fix(web): suppress AMR exit 130 fallback (#3136)

* feat(web): nudge users to hosted AMR on model/auth/quota failures (#3083)

* feat(web): nudge users to hosted AMR on model/auth/quota failures

When a non-AMR agent run fails with an auth / quota / upstream model
error, surface an inline nudge under the error pill linking to Open
Design's hosted AMR gateway (https://open-design.ai/amr). The nudge
fires `surface_view` (element=run_failed_toast) on impression and
`ui_click` (element=go_amr) on the link.

Also teach the daemon to classify CLI-agent auth/quota/upstream failures
(Claude Code, codex, ...) into specific API error codes
(AGENT_AUTH_REQUIRED / RATE_LIMITED / UPSTREAM_UNAVAILABLE) instead of
the generic AGENT_EXECUTION_FAILED, so both the error message and the
nudge key off accurate codes. AMR's own runs are excluded from the
nudge — they keep the dedicated sign-in / recharge affordances.

* feat(web): rework failed-run AMR guidance into per-case error UI

Replace the single inline nudge with a per-case failed-run experience
driven by the run's error code + agent:

- The error card is now neutral gray (was red) and always carries a
  retry button; it is driven by the persisted per-message error event so
  it survives a reload.
- Non-AMR agent hitting a model/auth/quota wall: a theme-color promotion
  card under the error card offers "switch to AMR & retry" — switches the
  run to AMR, opens Settings on the AMR card, and auto-retries once the
  account signs in (ProjectView polls vela login status, independent of
  the Settings pill lifecycle, with success / 5-min-timeout / unmount
  exits).
- AMR agent unauthorized: clearer copy + an "authorize & retry" button.
- AMR agent out of balance: clearer copy + a "top up" button to the AMR
  wallet, with manual retry.
- Settings AMR card: when opened from the nudge, it scrolls into view and
  pulses, and an authorize-button coachmark (a fake hand cursor that
  rises in and dismisses on hover) points at the sign-in control when not
  yet authorized.

analytics: surface_view (run_failed_toast) on the promotion card and
ui_click (go_amr) on its action are retained. i18n adds chat.amrCard.*
and chat.amrError.* (en / zh-CN / zh-TW translated; other locales fall
back to en) and drops the old chat.amrErrorGuidance keys.

* fix(daemon): require status context for numeric service-failure codes

Per review on #3083: the model-service classifier matched bare HTTP
status numbers (`500`, `502`, `429`, `401`), so ordinary CLI output like
`line 500`, `read 502 bytes`, or `exit code 401` could be misclassified
as a provider outage / auth wall and wrongly surface the AMR nudge. Now
a status number only counts when it carries explicit context (`HTTP 500`,
`status 503`, `code: 401`, `502 Bad Gateway`); textual provider phrases
(overloaded, bad gateway, service unavailable, rate limit, …) are
unchanged. Adds fixtures proving unrelated numeric output stays null.

* fix(web): keep error pill for failed runs ChatPane's card doesn't cover

Per review on #3083: the per-message gray error pill was suppressed for
every persisted error status event, but ChatPane only renders the
replacement top-level error card for `retryableAssistantMessage` (the
last failed assistant). So a failed turn that is no longer last (after a
follow-up) or an older failed run in history showed neither the pill nor
the card — its error detail vanished, undercutting reload/history
survival. ChatPane now passes `errorCardOwnerId` (the assistant id whose
error the card represents); AssistantMessage suppresses only that one
pill and keeps rendering StatusPill for all other error events.

* fix(daemon): don't treat a process exit code as an HTTP status

Follow-up to review on #3083: the status-context helper accepted a bare
`code` prefix, so `exit code 401` / `process exited with code 429` still
matched and got classified as AGENT_AUTH_REQUIRED / RATE_LIMITED (the
very `exit code 401` case the comment calls out as noise). `code` now
only counts when qualified (`status code` / `error code` / `response
code`) or punctuation-bound (`code: 401`); bare `exit code N` no longer
matches. Adds fixtures for exit-code lines returning null.

* chore(web): translate AMR card / error keys for 16 remaining locales

PR #3083 added 10 new `chat.amrCard.*` / `chat.amrError.*` keys but only
provided en/zh-CN/zh-TW translations; the other 16 locales fell back to
English. Translate the card title/body, three chips, primary CTA, and
the AMR self-error (auth / balance) messages and buttons for ar, de,
es-ES, fa, fr, hu, id, it, ja, ko, pl, pt-BR, ru, th, tr, uk.

* fix(amr): address review feedback on #2355

Targeted fixes for the unresolved review threads on #2355. Each fix
includes / updates a focused test.

- runtimes/executables.ts: `packagedVelaOpenCodeCompanionTree` now
  verifies the inner `opencode` executable exists + is runnable, not
  just the directory. This closes the false-positive availability path
  that let `detectAgents()` surface AMR as available even when the
  packaged companion was empty / partially copied (mrcfps, 4 threads).

- runtimes/executables.ts: `resolveAmrOpenCodeExecutable` now prefers
  the bundled `<OD_RESOURCE_ROOT>/bin/libexec/opencode/opencode` over a
  stale `opencode` on the user's PATH, so packaged AMR builds can't be
  hijacked by a global installation.

- web/EntryShell.tsx: when the Local CLI scan returns an available
  agent and the previously-selected agent is AMR, switch the selection
  to the first available local agent so the runtime and persisted
  agent agree before Continue.

- server.ts (model-probe branch): for AMR, check `readVelaLoginStatus`
  BEFORE rejecting on an empty live-model catalog — a signed-out user
  was getting `AMR_MODEL_UNAVAILABLE` ("choose a model") instead of
  the correct `AMR_AUTH_REQUIRED` (sign-in affordance).

- server.ts (default model fallback): if the user asked for the AMR
  agent default and the cached id is no longer in the FRESH catalog,
  fall back to `liveModels[0]` from the probe instead of rejecting the
  run as `AMR_MODEL_UNAVAILABLE`.

- integrations/vela.ts: route `vela login` through
  `createCommandInvocation` so an npm/Node-style `vela.cmd` / `.bat`
  shim on Windows gets the correct `cmd.exe /d /s /c …` wrapping with
  verbatim args (matches `execAgentFile` / chat-run spawning).

- tools/pack/src/linux.ts: in containerized Linux builds, bind-mount
  the host directory of `OPEN_DESIGN_VELA_CLI_BIN` and rewrite the env
  to the container-side path. The host path was being passed in as-is
  even though the default container only mounts /project, /tools-pack
  and cache/home — `copyOptionalVelaCliBinary` saw a missing path.

Deferred (out of scope for this PR):
- `od amr status/login/logout/cancel` CLI subcommands (AGENTS.md
  UI/CLI dual-track rule, server.ts:5763) — sizable surface; tracked
  for a separate focused PR.
- Strict `--require-vela-cli` for Windows + mac-x64 beta builds:
  prematurely blocked — `@powerformer/vela-cli` only publishes the
  `darwin-arm64` platform binary today; adding the flag elsewhere
  would fail the builds. Revisit once win/x64/linux binaries ship.

* fix(amr): hoist sendAmrAccountFailure above the AMR catalog preflight (TDZ)

The new signed-out AMR branch in the catalog preflight at server.ts:10875
calls `sendAmrAccountFailure(...)` to emit AMR_AUTH_REQUIRED, but the
const declaration sat ~100 lines below at the outer function scope. Because
`const` is TDZ-aware, that branch would have thrown `ReferenceError:
Cannot access 'sendAmrAccountFailure' before initialization` for the
exact users it tries to help — defeating the original intent.

Hoist the helper to just above the AMR preflight block so it's available
to every AMR code path in this function. Behavior elsewhere is unchanged.

Also rerun the daemon test suite: `launch.test.ts > resolveAgentLaunch
uses packaged built-in Vela for AMR` was creating the
`<resourceRoot>/bin/libexec/opencode/` companion *directory* only, but
this PR's earlier tightening of `packagedVelaOpenCodeCompanionTree`
also requires the inner `opencode` executable. Add it to that fixture
to match the new contract; the test was a sibling of the executables /
env-and-detection fixtures already updated in 13fc4f4.

Addresses #2355 review (mrcfps, 2026-05-28).

* feat(web): add hover cancel for AMR login (#3158)

* feat(web): add hover cancel for AMR login

* fix(web): don't bounce AmrLoginPill back to 'Signing in…' after local cancel

Both codex-connector (P2) and looper (CHANGES_REQUESTED) on this PR
flagged the same race in the new local-cancel path: `handleCancelLogin`
dispatches `notifyAmrLoginStatusChanged('login-canceled')` immediately
after `/login/cancel` returns, but the `AMR_LOGIN_STATUS_EVENT` listener
unconditionally re-enters `refresh()` and then restarts polling
whenever `/api/integrations/vela/status` still reports
`loginInFlight: true`.

That is a real race because the daemon's `cancelVelaLogin()` only sends
SIGTERM (escalating to SIGKILL after `LOGIN_CANCEL_KILL_GRACE_MS` =
2000 ms) and keeps the child in `activeLoginProcs` until it actually
exits — so the first `/status` read after a successful cancel can
legally still come back as in-flight. Under that window the pill flips
back to 'Signing in…' and can later surface the timeout/error path even
though the user already canceled, defeating the behavior promised in
the PR description.

Fix the listener instead of every dispatch site: in the
`login-canceled` branch, after the local reset (stopPolling +
setPending(null) + clear refs), optimistically mark every subscribed
pill instance as not-in-flight (`setStatus((c) => c ? { ...c,
loginInFlight: false } : c)`) and `return` — skip the
refresh-and-reconcile branch below entirely. The next explicit refresh
(component mount, user interaction, or a `status-changed` event) will
pick up the daemon's confirmed state once the child has actually
exited.

Add a focused regression test that holds `/api/integrations/vela/status`
at `loginInFlight: true` even after a successful `/login/cancel`,
asserting that the pill stays at the Canceled → Authorize sequence and
never bounces back to 'Signing in…'. This test fails on the pre-fix
listener and passes on the new behavior; existing
'cancels an in-flight AMR sign-in…' and 'reconciles late AMR browser
completion to Signed in after local cancel' tests continue to pass.

Addresses review feedback on #3158 (chatgpt-codex-connector, nettee).

---------

Co-authored-by: lefarcen <935902669@qq.com>

---------

Co-authored-by: a1chzt <chizblank@gmail.com>
Co-authored-by: Amy <1184569493@qq.com>
Co-authored-by: Mason <jinmeihong0201@gmail.com>
Co-authored-by: Caprika <56862773+alchemistklk@users.noreply.github.com>
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-28 05:09:55 +00:00
bfcafc81fd feat(pack): add Windows portable zip target alongside NSIS installer (#2937)
Adds a new `--to zip` (and `--to all`) tools-pack Windows build target that
produces a portable `.zip` from the cached `win-unpacked` tree using the
bundled 7z. The zip lays files at the archive root so users can extract it
anywhere and launch `Open Design.exe` without going through the NSIS
installer, addressing the no-install download request.

Release plumbing is updated to publish the portable zip and its sha256
beside the existing installer on R2 for beta, preview, and stable channels
(default on, gated by `WINDOWS_INCLUDE_ZIP`/`WIN_INCLUDE_ZIP`). The
electron-updater `latest.yml` feed continues to point only at the
installer; the zip is a manual-download convenience and is intentionally
excluded from the in-app updater.

Closes #1121

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)

Co-authored-by: libertecode <libertecode@proton.me>
2026-05-26 06:14:44 +00:00
Nicholas-XiongandGitHub ed5d569c57 fix(pack): add PowerShell 7 fallback for Windows installer process detection (#2799)
Fixes #2257

The Windows installer hardcoded calls to powershell.exe for running
instance detection, which fails on systems that only have PowerShell 7
(pwsh.exe) installed. This caused a false positive 'still running'
error during first-time installation.

Changes:
- DetectRunningInstances: try pwsh.exe first, fall back to powershell.exe
- CloseRunningInstances: same fallback logic with detailed logging
- Maintains backward compatibility with Windows PowerShell 5.1
- Provides clear error messages when neither PowerShell is available

The installer now works correctly on:
- Systems with only PowerShell 5.1 (powershell.exe)
- Systems with only PowerShell 7 (pwsh.exe)
- Systems with both installed
- Logs detailed errors when neither is available

Signed-off-by: xxiaoxiong <2482929840@qq.com>
2026-05-23 15:10:03 +00:00
lefarcenandGitHub a37d11fe72 Merge pull request #2461 from nexu-io/release/v0.8.0
release: Open Design 0.8.0 — Everything is a plugin. Headless. Plugins create plugins.
2026-05-23 12:38:36 +08:00
lefarcen c14baf07d3 Merge origin/main into release/v0.8.0
PR #2461 sync prep — resolves 14 conflicts merging 84 main-side commits
on top of 58 release-side commits accumulated during the 0.8.0 cycle.

Resolution summary:

Take main (theirs) where main carried deliberate forward progress:
- apps/web/src/components/PluginCard.tsx — 7 hunks, i18n migration:
  hardcoded English aria-labels/titles replaced with t() calls keyed
  on pluginCard.* (all 8 keys verified present in en.ts).
- apps/web/src/components/TasksView.tsx — 1 hunk, source-ingestion
  feature: sortedRoutines (newest-first), sourceIngestionTemplates,
  patchSourceForm, submitSourceIngestion. activeCount/pausedCount
  semantics preserved (now keyed on sortedRoutines, count unchanged).
- e2e/ui/app.test.ts — new node:fs/promises + tmpdir + path + @/timeouts
  imports needed by main-side test helpers.
- e2e/ui/settings-local-cli-codex-fallback.test.ts — menu-dismissal
  helper block added by main.

Keep both sides where each added a different field to the same object
literal:
- apps/web/src/components/ProjectView.tsx (locale + analyticsHints
  spread).
- apps/web/src/components/DesignSystemFlow.tsx (locale + analyticsHints).

Take release (ours) where release carried deliberate work that ships
0.8.0:
- CHANGELOG.md — release-side 0.8.0 entry + PR link refs; main's
  Unreleased section was the same body of work, now finalized.
- apps/landing-page/public/{apple-touch-icon,favicon}.png +
  apps/web/public/app-icon.svg — release-side visual refresh assets
  consistent with 0.8.0 stable ship.
- tools/pack/src/linux.ts — packageVersion const required by line 466;
  taking main's empty line would build-error.
- e2e/ui/project-management-flows.test.ts +
  e2e/ui/settings-api-protocol.test.ts +
  e2e/ui/settings-memory-routines.test.ts — release-side release-smoke
  hardening (shangxinyu1 + PerishFire) takes precedence on overlap.

Closes-issue / unblocks: PR #2461 sync release/v0.8.0 → main.
2026-05-23 12:17:18 +08:00
Marc ChanandGitHub 135c6b42d8 fix(ci): lint workflow changes with actionlint (#2742)
* fix(ci): lint workflow changes with actionlint

* fix(ci): lint workflow changes with actionlint

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)

* fix(ci): lint workflow changes with actionlint

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)

* fix(ci): lint workflow changes with actionlint

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
2026-05-23 12:12:55 +08:00
2879aceb3a Fix mac tools-pack Node fallback (#2160)
* Fix mac tools-pack Node fallback

Co-authored-by: multica-agent <github@multica.ai>

* Preserve mac native rebuild stat diagnostics

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-23 11:46:19 +08:00
Jetsung ChanandGitHub 914c5b3a15 fix(pack): add missing package.json fields for Linux AppImage build (#2276)
Fix electron-builder failure in `pnpm exec tools-pack linux build --to appimage`
by adding required description, author, and repository fields to generated
package.json in tools/pack/src/linux.ts.
2026-05-22 16:30:44 +08:00
lefarcenandGitHub b2b94dbde7 feat(desktop): follow OS language in packaged builds (cherry-pick of #2544 into release/v0.8.0) (#2560)
* feat(desktop): follow OS language in packaged builds

Packaged Electron currently shows Open Design in en-US regardless of
the OS language setting, because the renderer's i18n picks its locale
from `navigator.language` and Chromium hard-codes that to en-US unless
the host process intervenes. Browser users and `tools-dev` users are
unaffected because their `navigator.language` already reflects the
OS / browser preference.

This change:

- Adds `applyOsLocaleSwitch(app)` in `@open-design/desktop/main`. It
  reads `app.getPreferredSystemLanguages()[0]` and (when called before
  Electron's `ready` event) points Chromium's `--lang` flag at it, so
  the renderer's `navigator.language` follows the OS. Safe to call
  more than once: `appendSwitch` is a no-op once `app.isReady()`.
- Calls the helper from both Electron entries: `apps/packaged` before
  its own `whenReady`, and `runDesktopMain` for tools-dev parity.
- Forwards the resolved locale through
  `BrowserWindow.webPreferences.additionalArguments` as
  `--od-os-locale=<bcp-47>`, parsed by the preload and exposed at
  `window.__od__.client.osLocale`. The host bridge type
  (`OpenDesignHostClient.osLocale`) is extended accordingly.
- Updates `detectInitialLocale` in `apps/web/src/i18n/index.tsx` to
  read that field as a new step between the existing localStorage and
  navigator fallbacks. Browser/web continues to fall through to
  `navigator.languages` unchanged.

The explicit `osLocale` channel exists in addition to `--lang` because
some `app.getPreferredSystemLanguages()` strings (e.g. `zh-Hant-TW`,
`pt-PT`) need to round-trip through `resolveSystemLocale` to land on
the right supported locale, which Chromium's `navigator.language`
cannot do on its own.

* fix(web): route OS locale read through getOpenDesignHost

The first cut of detectInitialLocale read `window.__od__.client.osLocale`
directly, which trips `tests/host-boundary.test.ts` — that guard test
keeps web source from referencing preload globals by name so the
boundary stays single-source. Switch to `getOpenDesignHost()` from
`@open-design/host`, and rewrite the i18n test to install the host via
`installMockOpenDesignHost` instead of poking the global directly.

* fix(tools-pack): unblock mac packaged build on pnpm workspaces

Two independent issues prevented `pnpm tools-pack mac build --to all`
from completing on a clean macOS workspace, both unrelated to the
desktop OS-locale change in this PR but bundled here because verifying
that change end-to-end required the packaged pipeline to actually
finish.

1. `apps/web/.next/standalone/node_modules/.pnpm/node_modules/<pkg>`
   contained dangling symlinks left by Next's nft trace (e.g. a
   `semver -> ../semver@5.7.2/node_modules/semver` link to a
   `.pnpm/<pkg>@<ver>` directory pnpm never created). The downstream
   `cp { dereference: true }` aborted the whole packaged pipeline
   with ENOENT. Walk every artifact tree before copy and unlink
   symlinks whose target doesn't resolve. Targets that *do* resolve
   stay untouched.

2. Next 16's standalone build under pnpm workspaces does not hoist
   peer-dep packages (react, react-dom, styled-jsx) into
   `<standalone>/apps/web/node_modules`. The downstream
   `web-standalone-after-pack.cjs` audit then does
   `createRequire(server.js).resolve('react/package.json')`, whose
   module walk falls out of the standalone tree and aborts the
   electron-builder phase. Add a `hoistStandaloneNextPeerDeps` step
   for the web standalone artifact only: it locates the
   `<pkg>@<version>` (not peer-resolved sibling) directory under
   `.pnpm` and symlinks it into `apps/web/node_modules/<pkg>`. The
   subsequent `cp { dereference: true }` then writes the real
   directory into the cache so the packaged tree stays self-contained.

Verified by `pnpm tools-pack mac build --to all` succeeding end-to-end
(zip + dmg + app), then `pnpm tools-pack mac install` and
`pnpm exec tools-pack mac inspect --expr` reading the desired
`__od__.client.osLocale` from the packaged renderer.

* feat(desktop): fold encodeURIComponent + manual locale source + pet window from #2554

Three defensive improvements lifted from @Eli-tangerine's parallel
implementation on #2554, kept consistent with the OS-locale chain
already on this branch:

- The argv value crossing main → preload is now wrapped with
  encodeURIComponent / decodeURIComponent so a locale string with `;`,
  `=`, or any other Chromium argv special char round-trips cleanly.
  BCP-47 region tags don't carry those today, but the renderer parser
  no longer has to assume it.
- `setLocale` now also writes `open-design:locale-source = "manual"`
  to localStorage, and `detectInitialLocale` only treats the stored
  locale as winning when that marker is present. An untagged value
  (left over from a future auto-write path, or a stale install) no
  longer pins the app to an old language once the host injects a
  fresh OS locale. Today `setLocale` is the only writer so the marker
  has no behaviour difference yet — this is a defensive net.
- `createDesktopPetWindow` now receives `osLocale` and forwards the
  same `additionalArguments` as the main `BrowserWindow`, so the
  pet renderer's `__od__.client.osLocale` is consistent with the main
  window's instead of being silently undefined.

Co-authored idea credit: changes mirror the locale-piece of
@Eli-tangerine on #2554 — that PR is closing in favour of this one.

Tests: detect-initial-locale gets a new "untagged localStorage value
loses to host locale" case. desktop 62/62, host 13/13, web i18n +
host-boundary 15/15 stay green.

* feat(web): fold onboarding view styles from #2554

Pulls the 747-line addition to `apps/web/src/styles/home/entry-layout.css`
from @Eli-tangerine's #2554 — the visual layer for the global onboarding
flow (`/onboarding` view, Connect / About-you / Design-system steps).
The view itself was already plumbed through `EntryShell.tsx`; this adds
the styling that makes it shippable on v0.8.0.

#2554 is closing in favour of this branch, so the CSS lands here so the
onboarding work doesn't get dropped on the floor.

Co-authored idea credit: @Eli-tangerine — original styling on #2554.

* fix(tools-pack): make hoistStandaloneNextPeerDeps idempotent across builds

Addresses non-blocking review by @PerishCode on #2560: the previous
`if (await pathExists(linkPath)) continue;` guard uses `access()`,
which follows symlinks. A stale symlink from a previous build whose
`.pnpm/<pkg>@<version>` target moved (e.g. after a react/react-dom
version bump that invalidates the workspace-build cache key and forces
a re-run) reports as missing through `pathExists`, then `symlink()`
rejects with EEXIST and the unhandled rejection aborts the packaged
build.

Switch to `lstat` (which does not follow the link) so we can tell
"genuinely empty slot", "real directory left by Next" and "stale
symlink" apart, then unlink stale entries before re-creating. Also
move `stripBrokenSymlinks` ahead of `hoistStandaloneNextPeerDeps` in
`copyWorkspaceBuildArtifactsToCache` so any leftover dangling links
that survived a previous run are cleared before hoist tries to write.
2026-05-21 18:23:20 +08:00
PerishFireandGitHub 526c7f7c26 Fix packaged auto-update release validation (#2565)
* fix: tighten packaged updater flow

* test: prune noisy extended ui coverage

* fix: hide unpublished release artifacts

* test: validate release updater channels

* fix: align prerelease release namespaces
2026-05-21 18:15:53 +08:00
Marc ChanandGitHub 4c222f55be chore(pack): update electron icons (#2538) 2026-05-21 16:16:34 +08:00
lefarcen 50a4dc8a62 Merge origin/main into release/v0.8.0 2026-05-21 13:17:52 +08:00
85276df284 chore(deps): patch security override and patch bumps (#2306)
- Add pnpm override: protobufjs 8.4.0 (CVE-2026-45740, GHSA-jggg-4jg4-v7c6)
- Bump postcss 8.5.14 -> 8.5.15 in apps/web (and root override)
- Bump tsx 4.22.2 -> 4.22.3 across all workspace packages

Co-authored-by: Patrick A <259201958+eefynet@users.noreply.github.com>
2026-05-21 11:51:54 +08:00
ebf4a3ffca feat(release): upload browser sourcemaps to PostHog for packaged builds (#2508)
* i18n: add translations for media provider coming soon section (#2415)

* i18n: add translations for media provider coming soon section

- Add 'settings.mediaProviderComingSoonHint' key to all 19 locales
- Replace hardcoded English strings in SettingsDialog.tsx with i18n keys
- Reuse existing 'tasks.comingSoon' and 'settings.agentInstall.docs' keys
- Resolves TODO(i18n) comment at line 5091

* fix: escape single quotes in translation strings

* fix: escape all single quotes in English translation string

* feat(release): upload browser sourcemaps to PostHog for packaged builds

Next.js was emitting minified JS with no browser sourcemaps, so PostHog
Error Tracking surfaces frames like fO / fz / s4 / tD instead of real
file:line locations. This wires up the full pipeline:

- apps/web/next.config.ts: enable productionBrowserSourceMaps so next build
  emits .js.map alongside each chunk.
- tools/pack/src/web-sourcemaps.ts: new helper that runs after next build
  and before any packaging step copies the web output into the Electron
  resources. Uses @posthog/cli to inject chunk IDs and upload sourcemaps
  to PostHog, then ALWAYS strips every .map under .next/static so source
  never ships inside an installer (saves ~14 MB per packaged image too).
- tools/pack/src/{mac/workspace,win/app,linux}.ts: call processWebSourcemaps
  immediately after the @open-design/web build step.
- tools/pack/src/config.ts: read POSTHOG_CLI_API_KEY + POSTHOG_CLI_PROJECT_ID
  (with POSTHOG_PERSONAL_API_KEY / POSTHOG_PROJECT_ID aliases) and expose
  them on ToolPackConfig with the same shape as the existing posthogKey /
  posthogHost fields.
- .github/workflows/release-{beta,preview,stable}.yml: pass the new secrets
  through so all three release channels symbolicate stacks.

When the API key is missing (PR builds, forks, local contributor builds),
the helper logs and skips the upload — but still strips .map files. The
strip step is unconditional because shipping a sourcemap is equivalent to
shipping the source.

Adds tools/pack/tests/web-sourcemaps.test.ts covering: missing chunks dir
silently noop, no-map noop, strip-only path when credentials are absent,
recursive walker for nested subdirectories. CLI happy path is left to the
release workflow itself.

Required follow-up (cannot push from code): add a repo secret named
POSTHOG_CLI_API_KEY (the phx_ personal API key) and a repo var named
POSTHOG_CLI_PROJECT_ID (the numeric project id, 420348 for our project)
in nexu-io/open-design settings before merging.

* fix(web-sourcemaps): use management host for CLI, not ingest host

POSTHOG_HOST is the ingest URL (us.i.posthog.com) used by the runtime SDK
to POST events to /capture/. The @posthog/cli sourcemap upload talks to
the **management** API (us.posthog.com) and gets a 404 on the ingest
host. The two are not interchangeable.

Adds a separate `posthogCliHost` field on ToolPackConfig sourced from
POSTHOG_CLI_HOST (with no fallback to POSTHOG_HOST). When the env is
unset the @posthog/cli defaults to the US Cloud app host on its own,
which is correct for our project — so this PR doesn't need a new repo
variable for it.

---------

Co-authored-by: Nicholas-Xiong <2482929840@qq.com>
2026-05-21 11:48:57 +08:00
PerishFireandGitHub 33b20aa9cb Fix Windows reinstall detection and duplicate plugin nav (#2466)
* Fix Windows silent reinstall detection

* Remove duplicate entry plugins nav
2026-05-20 22:08:44 +08:00
lefarcen aedbb9dbe4 release: Open Design 0.8.0
Bumps 14 workspace package.json files from 0.7.0 to 0.8.0:
- root, apps/{web,daemon,desktop,landing-page}
- packages/{contracts,host,platform,sidecar,sidecar-proto}
- tools/{dev,pack,pr}, e2e

apps/packaged was already at 0.8.0 from the preview lane.
Independently versioned packages keep their own tracks.

Adds CHANGELOG [0.8.0] - 2026-05-20 entry covering the
305 PRs merged since 0.7.0 by 75 contributors:

- Plugin engine rebuild + Plugin Registry surface
- Headless by default (desktop is thin wrapper around CLI)
- Critique Theater Phases 9 through 16
- 149 design systems with structured tokens.css
- Italian locale + CJK font fallback
- Leonardo.ai, ElevenLabs, SenseAudio providers
- Windows packaged auto-update
- Visual refresh + Quick-brief discovery overhaul
- PostHog v2 analytics
- Manual edit UX overhaul
2026-05-20 21:22:17 +08:00
“wangchenglong” 69d3bf4f71 Refresh Open Design app visuals 2026-05-20 20:28:15 +08:00
PerishFireandGitHub 899c9fe4d8 Support nightly and preview package identity (#2437) 2026-05-20 19:46:39 +08:00
PerishFireandGitHub 15d08d4158 feat: add windows packaged auto update flow (#2362) 2026-05-20 12:56:14 +08:00
lefarcenandGitHub 80d305858b feat(diagnostics): add one-click log export from Settings → About (#798)
* feat(diagnostics): add one-click log export from Settings → About

Adds a new "Export diagnostics" entry under the About section that bundles
daemon/web/desktop logs, machine info, and recent macOS crash reports into
a zip the user can share when reporting issues.

- Browser hits a new daemon HTTP endpoint and triggers a download.
- Electron uses an IPC bridge with the native save dialog and reveals the
  saved file in Finder/Explorer; the Help menu also exposes it as a
  fallback when the daemon is unresponsive.

Packaging + redaction lives in a new @open-design/diagnostics package so
both surfaces share it. Sensitive JSON keys, URL query secrets, and the
current user's home path are redacted before packaging.

* build(nix): include packages/diagnostics in daemon build targets

The Nix daemon derivation builds workspace siblings in dependency order
before compiling apps/daemon. Without @open-design/diagnostics in that
list, the daemon TypeScript build fails inside the Nix sandbox with
`Cannot find module '@open-design/diagnostics'` because pnpm install
only creates the symlink — the dist output that the package.json
exports point at isn't produced until each sibling's build script runs.

* build(tools-pack): include @open-design/diagnostics in packaged INTERNAL_PACKAGES

Without this, packaged win/mac/linux builds fail with `npm error 404` when
the post-build `npm install --omit=dev --no-package-lock` step in the
assembled app tries to resolve `@open-design/diagnostics@0.2.0` from the
public npm registry. The package is workspace-private, so it has to be
tarballed via `pnpm pack` and file:-referenced from the assembled
package.json like every other internal workspace dep that daemon/desktop
depend on.

Also wires the package's `pnpm --filter ... build` into the pre-pack
workspace build step so the dist/ exists before pnpm pack runs, and
updates the two test fixtures (`win-app.test.ts`, `workspace-build.test.ts`)
that mirror INTERNAL_PACKAGES.

The diagnostics package itself is repinned to exact dependency versions
already used elsewhere in the workspace (`jszip 3.10.1`, `@types/node
20.19.39`, `esbuild 0.28.0`, `typescript 5.9.3`, `vitest 4.1.6`) so it
passes the new `pnpm guard` exact-version rule and produces a minimal
lockfile diff vs main (additions only, no resolution-string churn).

* fix(diagnostics): include `~` in bearer-token redaction char class

RFC 6750 token68 syntax allows `~`, so tokens like `Authorization: Bearer
abcd~efgh` were only partially matched by `HTTP_AUTH_SCHEME_RE`. The
regex stopped at the first `~`, leaving the tail (`~efgh`) un-redacted in
the exported diagnostics zip — a clear leak since this feature explicitly
generates support bundles for external sharing.

Add `~` to the character class and a regression test.

* fix(diagnostics): only collect renderer.log from desktop

`buildSidecarLogSources` unconditionally added `logs/${app}/renderer.log`
for daemon/web/desktop, but only the desktop runtime writes a renderer
log (see apps/desktop/src/main/runtime.ts) — daemon and web are pure
Node services with no Electron renderer. Every export therefore produced
missing-file placeholders and manifest warnings for the two phantom
paths, polluting the bundle.

Gate the renderer.log source on APP_KEYS.DESKTOP so the daemon-side
collector matches the desktop-side collector in apps/desktop/src/main/
diagnostics.ts:63.

* fix(diagnostics): mirror desktop-side renderer.log gate

The previous fix only updated the daemon-side `buildSidecarLogSources`
in `apps/daemon/src/diagnostics-export.ts`. The desktop-side collector
at `apps/desktop/src/main/diagnostics.ts` had an identical copy of the
same bug that I overlooked: it also unconditionally added
`logs/${appKey}/renderer.log` for daemon/web/desktop, producing
missing-file placeholders + manifest warnings for the two phantom paths
on every desktop-initiated export.

Apply the same `appKey === APP_KEYS.DESKTOP` gate here so both export
entry points (browser via daemon HTTP, Electron via native save dialog)
emit the same clean manifest.

* feat(diagnostics): add `od diagnostics export` CLI subcommand

AGENTS.md's dual-track capability-exposure contract requires every
user-facing feature to ship on both the web UI and the `od` CLI. The
diagnostics export was only reachable through Settings → About and the
desktop Help menu; this commit closes the loop with an `od diagnostics
export [<path>] [--json]` subcommand registered in SUBCOMMAND_MAP.

The CLI is a thin shell over the existing GET /api/diagnostics/export
endpoint — same zip output, same redaction, same crash-report scope.
Defaults to writing `open-design-diagnostics-<timestamp>.zip` in the
current directory; `--output <path>` or a positional arg overrides.
`--json` prints `{path, sizeBytes}` for shell pipelines.

Use cases this unlocks:
- A CI script can `od diagnostics export ~/artifacts/bundle.zip` after
  a failed run.
- Bug reporters on headless boxes can grab a bundle without booting
  the web UI.
- `od doctor` follow-ups can collect a full snapshot when a probe fails.

* fix(diagnostics): surface non-sidecar launch in manifest warnings

`buildSidecarLogSources()` returns `[]` when the daemon has no sidecar
runtime context, which is the standard `od` (plain) launch path —
`runDaemonCliStartup()` -> `startDaemonRuntime()` does not pass a
runtime. Settings → About and the new `od diagnostics export` previously
reported success but produced a bundle with only the summary JSONs, so
operators could not tell "no logs because plain launch" from "no logs
because something genuinely broke."

- Extend `DiagnosticsContext` with an optional upstream `warnings:
  string[]` that `buildManifest` merges into the manifest warnings.
- Emit STANDALONE_LAUNCH_WARNING from the daemon handler when
  `options.runtime == null`. The warning names the limitation and
  points the user at the sidecar entry points that DO capture logs.
- Add a regression spec at `apps/daemon/tests/diagnostics-export.test.ts`
  that drives the handler with `runtime: null` and asserts the warning
  surfaces in `summary/manifest.json` (and that `files` is empty so a
  user reading the bundle does not confuse "no log sources" with
  "missing files").
2026-05-20 09:10:51 +08:00
PerishFireandGitHub 2c128e0e91 refactor desktop host bridge (#2246) 2026-05-19 18:27:05 +08:00