Commit Graph

18 Commits

Author SHA1 Message Date
Harshit Singh Bhandari 7ac1e2caa2
fix(desktop): handle Squirrel startup events + CI smoke-install the Windows build (#398)
* fix(desktop): handle Squirrel startup events + CI smoke-install the Windows build

Two Windows-install fixes.

1. Add electron-squirrel-startup handling. main.ts had no handler for the
   --squirrel-{install,updated,uninstall,obsolete} flags Squirrel runs the exe
   with during install/update/uninstall. Without it the install hook booted the
   full app (window + daemon spawn) and never exited, so the installer hung
   waiting on it. Now we create/remove shortcuts and quit immediately; it is a
   no-op on macOS/Linux.

2. Add a Windows CI smoke-install step. After the build, run Setup.exe --silent
   on the clean native x64 windows-latest runner, assert the install dir is
   created, and upload SquirrelSetup.log as an artifact. This captures the
   install log we otherwise cannot get and gives a build-vs-host verdict: a clean
   install proves the artifact is good, so a failing user machine is host-side
   (AV/disk/signing). The runner has no real-time AV, so it does NOT prove
   SmartScreen/Defender will accept the unsigned binaries on end-user machines;
   code-signing stays the durable fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci(windows): capture SquirrelSetup.log from the app dir, fix misleading warning

Update.exe writes SquirrelSetup.log into %LocalAppData%\AgentOrchestrator, not
SquirrelTemp; the smoke step looked only in SquirrelTemp and so printed
"failed before Update.exe ran" even on a successful install (exit 0, dir
created). Look in the app root dir first, fall back to SquirrelTemp, and drop
the false-failure wording.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:51:02 +05:30
Harshit Singh Bhandari 5440d2d1df
ci: consolidate per-OS testing builds into one matrix workflow
Replaces linux/windows/macos-testing-build.yml with a single testing-build.yml
(matrix over ubuntu/windows/macos) that publishes all artifacts to one
0.0.0-testing-<sha> prerelease. Manual dispatch + 0.0.0-testing-* tag push.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:40:58 +05:30
Harshit Singh Bhandari 2946212171
ci: add manual/tag macOS testing build (unsigned arm64 zip)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:17:11 +05:30
Harshit Singh Bhandari 665f2a26a6
ci: add manual/tag Windows testing build (Squirrel installer)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:41:10 +05:30
Harshit Singh Bhandari 15e01af7c6
ci(linux-testing): build .deb only, drop failing rpm maker
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:26:34 +05:30
Harshit Singh Bhandari 4c2e1eaa54
ci: add manual/tag Linux testing build; disable desktop-testing tag trigger
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:21:44 +05:30
Harshit Singh Bhandari d9dde9280f
ci: build unsigned desktop artifacts on 0.0.0-testing-* tags (#347)
Add a tag-triggered workflow that builds the Electron desktop app (with the
bundled Go daemon) on macOS, Windows, and Linux runners and attaches the
unsigned artifacts to a GitHub prerelease for end-to-end pipeline validation.

Signing/notarization is intentionally off until the certs and secrets exist,
so these builds are for validating packaging, not distribution.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:19:24 +05:30
Madhav Kumar 6885af26b2
feat(windows): ConPTY terminal, zellij discovery, agent launcher trampoline, codex shim resolution (#310)
* fix(daemon): self-heal a stale run-file instead of refusing to start

On Windows the desktop supervisor can only TerminateProcess the daemon
(no POSIX signal reaches a detached child), so the daemon's graceful
shutdown never runs and ~/.ao/running.json is never removed. The leaked
file survives into the next launch, and because Windows reuses PIDs
aggressively the recorded PID usually belongs to an unrelated process.
The startup pre-flight trusted PID liveness alone (runfile.CheckStale ->
processalive.Alive), so it concluded a daemon was "already running" and
exited with "refusing to start" on every restart. A dead daemon then
makes the renderer's loopback REST calls (e.g. Spawn Orchestrator) fail
silently.

Verify the recorded port is actually served by an AO daemon with the
recorded PID (a /healthz probe matching service + pid, the same ground
truth inspectDaemon already uses) before refusing. A run-file left by a
crashed, hard-killed, or reused-PID predecessor is treated as stale and
overwritten, so startup is robust to a leaked run-file from any cause.

Fixes #256

* fix(release): build the desktop daemon natively on each target OS

build-daemon.mjs compiles the bundled `ao` daemon with the build host's
GOOS and names it off the host platform (ao.exe only when the builder is
Windows). The release workflow ran only on macos-latest, so a Windows
package would ship a macOS binary named `ao` with no `ao.exe`, and the
app could not launch a valid Windows daemon ("This program cannot be run
in DOS mode" / binary not found).

Run the release as a per-OS matrix (macOS + Windows) so host == target
and each installer bundles a daemon compiled for its own platform, and
pin the Go toolchain with setup-go since build-daemon needs it on every
runner.

Fixes #235

* feat(terminal): Windows ConPTY support for /mux attach

Replaces the Windows stub in internal/terminal/pty_windows.go with a real ConPTY implementation backed by github.com/aymanbagabas/go-pty, so the daemon's /mux attach can stream a live terminal to the renderer on Windows.

PTYSource.AttachCommand now returns (argv, env, err). On Windows the zellij attach is spawned directly (no powershell.exe wrapper) — wrapping ConPTY startup around a shell surfaces as modal application-error dialogs — and the per-session ZELLIJ_SOCKET_DIR is delivered via the spawn's CreateProcess env block instead of an 'env -u NO_COLOR' shim. Unix continues to use the env-shim wrapper and returns nil env.

Adds go-pty v0.2.3 (+ bumps golang.org/x/sys to v0.44.0 transitively). Updates the in-process test fakes (terminal/fakes_test.go, httpd/terminal_mux_test.go) for the new signature.

* feat(zellij): discover zellij binary on Windows and raise command timeout

Defaults the zellij binary to whatever exec.LookPath finds first (preferring zellij.exe on Windows), falling back to LOCALAPPDATA\Programs\zellij\zellij.exe and ProgramFiles{,(x86)}\{zellij,Zellij}\zellij.exe so a fresh-installed Windows user gets a working runtime without setting Options.Binary.

Raises the per-command timeout from 5s to 30s on Windows: the first zellij invocation after install routinely takes longer than 5s on Windows due to filesystem/AV warmup, which was causing benign DeadlineExceeded failures during session create.

* feat(zellij,cli): Windows agent launcher trampoline for codex argv

On Windows, zellij's KDL `args` quoting cannot round-trip codex's --config key=value flags (or any argv with embedded quotes), and shell-wrapping the agent in powershell/cmd quoting is equally unsound. This adds a small launch trampoline so zellij runs a known-fixed argv and the real argv is delivered out-of-band.

How it works on Windows:

1. zellij.Runtime.writeLayout persists cfg.Argv to a temp JSON spec via the new agentlaunch package (AO_LAUNCH_SPEC env var points at the file).

2. The KDL layout runs the trampoline as `<ao.exe> launch` (windowsLaunchArgv); PATH is augmented so the trampoline resolves.

3. The new hidden `ao launch` subcommand reads the spec, deletes the temp file, and execs the real agent with cfg.Argv inside cfg.WorkspacePath.

Also adds:

- runner.Start fire-and-forget path (process_windows.go uses powershell.exe -EncodedCommand + Start-Process -WindowStyle Hidden with CREATE_NEW_CONSOLE so the daemon is not blocked on zellij's --create-background settling).

- powerShellEncodedCommand helper and switch from -Command to -EncodedCommand for the existing powershell shellLaunchSpec (avoids brittle KDL→PowerShell quoting round-trips).

Unix is unchanged: writeLayout passes cfg.Env straight through, createSession stays synchronous via runner.Run, and process_other.go is a stub that returns an error if anyone calls into the background path.

* feat(codex): Windows binary resolution, terminal compat flags, TOML literal strings

Three Windows-targeted refinements to the codex agent plugin so a default Windows install lands in a working state:

1. ResolveCodexBinary now follows .cmd/.ps1 shims to the underlying codex.exe (resolveNativeWindowsCodex + windowsNativeCodexCandidatesForShim). The npm-distributed codex shim cannot be exec'd directly under ConPTY without a shell wrapper; jumping straight to the .exe avoids that wrapper.

2. appendTerminalCompatibilityFlags adds Windows-specific args (e.g. --no-alt-screen) so codex's TUI renders correctly inside zellij's pane without the alternate-screen buffer churn that breaks ConPTY redraws.

3. hooks.go gains codexTOMLLiteralString / codexTOMLConfigString / containsTOMLControl so paths and other values with backslashes and quotes round-trip through codex's --config TOML parser using literal strings ('...') when basic strings would require unsafe escaping.

* fix(lint): paramTypeCombine in pty_unix.go, revive doc comments in agentlaunch, codex test quotes

* fix: stabilize windows zellij sessions

---------

Co-authored-by: harshitsinghbhandari <24b4506@iitb.ac.in>
Co-authored-by: Madhav <madhavkumar@microsoft.com>
Co-authored-by: Vaibhaav <user@example.com>
2026-06-18 21:51:05 +05:30
Ashish Huddar 785f060b71
ci: run the frontend vitest suite on pull requests (#172)
* ci: run the frontend vitest suite on pull requests

The renderer suite was dead for months with nothing noticing — no workflow
executed it (and until #171 it could not even run: vitest auto-loads only
vite.config.ts / vitest.config.ts, which the repo lacked). This job keeps
the revived suite alive.

Typecheck is intentionally left out until the pre-existing forge.config /
update-electron-app type errors are fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): regenerate package-lock.json in sync with package.json

The committed lock was missing dozens of entries (ansi-regex, error-ex,
minimatch, ...), so `npm ci` under CI's npm 10 hard-fails with "Missing:
<pkg> from lock file" — the new Frontend workflow caught it on its first
run. Newer local npm versions tolerated the drift, which is why it went
unnoticed. Regenerated with npm install (npm 10.9.8, lockfileVersion 3) and
validated with a clean `npm ci` + full vitest run (9/9 files, 99/99 tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:18:55 +05:30
yyovil c2c4404c7d
feat(frontend): scaffold for frontend with complimentary backend changes (#168)
* feat(frontend): rebuild Electron desktop UI as a React + Vite renderer

Replaces the skeleton Electron frontend with a full React 19 + TypeScript
renderer (Vite, electron-forge, contextBridge preload), plus the backend
additions it needs.

Renderer:
- TanStack Query + EventTransport (CDC SSE on /api/v1/events)
- TanStack Router file-system routing (hash history for the file:// origin)
- Tailwind + shadcn/ui, react-resizable-panels, Zustand UI state
- @xterm/xterm per-session PTY over /mux WebSocket + WebGL addon
- openapi-typescript + openapi-fetch types off openapi.yaml
- electron-forge packaging + update-electron-app auto-updater
- Vitest + RTL · Playwright

Backend:
- cors.go — allowlist-only CORS, handles Private Network Access preflight
  for app:// renderer -> loopback daemon
- session.TerminalHandleID exposed in domain + OpenAPI spec
- project.Path added to OpenAPI spec, service, store, and tests

DESIGN.md documents the emdash-matched dark UI (tokens, blue accent, status
glyph spec, orchestrator-led layout).

Co-authored-by: Ashish Huddar <ashish.hudar@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b1e334c1e54a

* chore: format with prettier [skip ci]

---------

Co-authored-by: Ashish Huddar <ashish.hudar@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-10 11:40:17 +05:30
yyovil 5982051651
chore: add prettier config and CI auto-formatter (#166)
* chore: add prettier config and CI auto-formatter

Adds .prettierrc and .prettierignore (config only, no local enforcement).
Formatting runs in CI via the prettier.yml workflow: on every push to a
non-main branch, Prettier rewrites changed files and commits the result back
using GITHUB_TOKEN. Developers never need to run Prettier locally.

Intentionally excludes husky/lint-staged — local pre-commit hooks are the
wrong layer for a formatter that the whole team doesn't need installed.

Also adds .envrc.local to .gitignore for personal local shell overrides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 27336650d2ee

* chore: format with prettier [skip ci]

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-10 09:09:17 +05:30
Harshit Singh Bhandari c7e3a0336e
ci(frontend): add react-doctor check for landing site (#151)
* ci(frontend): add react-doctor check for landing site

Adds a CI job that runs react-doctor (https://github.com/millionco/react-doctor)
against the Next.js 15 + React 19 landing site at frontend/src/landing/.
The landing has 29 .tsx components and no existing lint coverage; the only
prior frontend CI was a schema.ts drift check in go.yml.

- New workflow .github/workflows/react-doctor.yml, path-filtered to
  frontend/src/landing/** so backend-only PRs don't pay for it.
- doctor script + react-doctor devDep added to the landing package.
- Default --blocking=error: surfaces security, bugs, perf, a11y, and
  maintainability findings without failing CI on existing warnings
  (current baseline: 0 errors, 58 warnings, ~5s locally).
- Runs with --no-telemetry so CI runners don't ping react.doctor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci(frontend): also scope react-doctor push trigger to landing paths

Mirrors the PR path filter on push to main so backend-only merges don't
re-run the landing check. Unlike go.yml/cli-e2e.yml (which trigger on
broad path sets), this workflow only cares about frontend/src/landing/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci(frontend): use react-doctor GitHub Action, drop devDep + lockfile churn

Replaces the npm install + npm run doctor approach with the official
millionco/react-doctor@v2 composite action. The action manages its own
Node setup and react-doctor install on the runner, so the landing
package.json and lockfile stay untouched (no transitive-dep bloat from
react-doctor's 479-package tree).

The action also wires up sticky PR summary comments, inline review
comments, and commit statuses out of the box — requires pull-requests
and statuses write perms.
2026-06-07 07:11:35 +05:30
neversettle 6da5d93241
feat: automate OpenAPI spec + frontend TS type generation (issue #102) (#103)
* feat: add npm run api scripts and document API contract change flow

Adds three root package.json scripts mirroring the sqlc convention:
- api:spec — runs go generate to regenerate openapi.yaml
- api:ts   — runs openapi-typescript@7.4.4 to generate frontend/src/api/schema.ts
- api      — runs both in sequence (the single contributor command)

Documents the full flow in a new AGENTS.md "API contract changes" section
so contributors know which files to edit, how to regenerate, and what to
verify. Implements slice 1 of issue #102.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: correct paths in AGENTS.md API contract changes section

- api:ts equivalent command: use full relative path
  backend/internal/httpd/apispec/openapi.yaml, not bare openapi.yaml
- verify command: prefix with `cd backend &&` so it works from repo root

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add CI drift guard for OpenAPI spec and frontend TS types

- Adds api-drift job to go.yml: regenerates openapi.yaml + schema.ts
  via `npm run api`, then fails if git diff detects any change
- Expands go.yml path filter to trigger on package.json and schema.ts
- Commits initial schema.ts generated from the current spec
- Notes the CI guard in AGENTS.md API contract changes section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address review feedback on api-drift CI job

- Scope git diff to schema.ts only; openapi.yaml drift is already
  caught by TestBuild_MatchesEmbedded in the build-test Go job
- Add npm ci step so openapi-typescript is installed from the lockfile
  rather than re-fetched by npx on every run
- Move openapi-typescript from npx -y to a pinned devDependency
  (exact 7.4.4, no caret) with a committed package-lock.json
- Note in AGENTS.md Verify block that go test does not cover schema.ts drift

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 14:45:10 +05:30
prateek 8df074b1c9 chore(backend): add golangci-lint with a strong ruleset and clear the tree
Introduces backend/.golangci.yml (27 linters across correctness, dead-code/
boilerplate, style, and security), wires it into CI as a blocking job, and
fixes every finding so the tree starts at zero.

Config:
- 27 linters: errcheck, govet, staticcheck, errorlint, bodyclose,
  sqlclosecheck, rowserrcheck, nilerr, makezero, unused, unparam, unconvert,
  wastedassign, copyloopvar, prealloc, dupl, revive (incl. exported-symbol doc
  comments), gocritic, misspell, usestdlibvars, predeclared, nakedret, gosec, …
- Tuned for signal over noise: govet/shadow and gocritic hugeParam/rangeValCopy/
  unnamedResult disabled (idiomatic-Go false positives); sqlc-generated code and
  tests get scoped exclusions; gosec G304 excluded (paths are config/run-file/
  worktree-derived, not user input); nilerr excluded in cli/status.go (probe
  failures are the reported status, not a command error).

CI:
- New blocking lint job (golangci-lint-action, latest binary for Go-version
  compatibility).
- go-version now read from go.mod (was pinned 1.22 while go.mod declares 1.25).

Cleanup to reach zero (no behavior change):
- errcheck: wrap deferred/inline Close()/Remove()/Rollback() with `_ =`.
- gosec: tighten dir/file perms (0755->0750, 0644->0600).
- unparam: drop always-nil error return from startLifecycle; drop unused
  shellPath param (zellij PowerShell) and always-500 fallbackStatus param
  (writeProjectError).
- gocritic: regexp \d, s != "", switch->if, combined appends.
- revive: doc comments on all exported symbols; rename project.ProjectRow ->
  project.Row (stutter); rename `max` locals shadowing the builtin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 04:58:41 +05:30
itrytoohard c0bf99eb22 test(cli): port the E2E suite to cross-platform Go; slim the Docker harness
Replaces the growing bash smoke test with a Go os/exec suite behind the `e2e`
build tag (backend/internal/cli/e2e_test.go). It builds the real binary and
drives start/status/doctor/stop + the daemon-control HTTP surface against
isolated state (temp dir + OS-assigned free port), and now runs natively on
ubuntu + macOS + WINDOWS in CI — finally covering the Windows
CREATE_NEW_PROCESS_GROUP detach path and per-OS os.UserConfigDir resolution
that a Linux container can't observe. `go test -tags e2e -v` logs every command
and its output, replacing the bash -v flag.

- backend/internal/cli/e2e_test.go: 8 table-style TestE2E_* cases; strips any
  inherited AO_* env so a real daemon's AO_PORT can't leak in.
- test/cli/install-check.sh: small, linear fresh-install proof the Dockerfile
  runs (binary on PATH, no toolchain) — kept as the hardening tier.
- test/cli/Dockerfile: run install-check.sh instead of the full bash suite.
- .github/workflows/cli-e2e.yml: `native` is now a go test matrix over
  ubuntu+macos+windows; `container` builds the image and runs it with --init.
- Removes test/cli/smoke.sh and test/cli/run-local.sh (superseded by `go test`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 03:14:08 +05:30
itrytoohard a9e83011f1 docs(test): correct --init rationale (suite uses a fabricated dead PID)
The stale-daemon assertion does not depend on container PID-1 reaping — it
writes a fabricated dead PID rather than killing a real process. --init is
still run so the real daemon spawned by the `start` test (detached via setsid)
is reaped after `stop` instead of lingering as a zombie. Reword the README,
Dockerfile, and workflow comments to say that accurately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:37:51 +05:30
itrytoohard 3680ac5474 test(cli): add end-to-end smoke test + Docker/CI harness
Adds a fresh-machine, install→use→verify E2E test for the `ao` CLI and wires
it into CI. The suite drives the real binary (start/status/doctor/stop + the
daemon-control HTTP surface) against fully isolated state — its own temp
run-file, data dir, and an auto-picked free loopback port — so it never
collides with a developer's real AO install or daemon.

- test/cli/smoke.sh: 40 assertions covering install resolution, version/help
  (daemon hidden), doctor text+json (and that it does NOT migrate SQLite),
  status stopped/stale/ready, start fresh+idempotent, daemon-created store,
  /healthz identity, the /shutdown CSRF + DNS-rebinding guard (403 + survives),
  graceful/stale/idempotent stop, run-file ownership cleanup, exit codes
  (2 usage / 1 runtime), and completion for all four shells. It deliberately
  ignores an inherited AO_PORT and self-allocates a free port for isolation.
- test/cli/Dockerfile: models installing ao on a fresh machine — builds the
  binary, drops it on PATH in a clean Debian image with only runtime deps
  (git/tmux/curl), runs the suite as a non-root user.
- test/cli/run-local.sh: build-from-source + native run convenience wrapper.
- .github/workflows/cli-e2e.yml: two tiers — `native` runs the suite on a
  ubuntu+macos runner matrix (the real VMs, to cover the unix setsid detach and
  macOS os.UserConfigDir paths a Linux container can't), and `container` runs
  the fresh-machine Docker image with --init (real PID-1 reaper so the
  stale-daemon assertion is reliable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:33:56 +05:30
harshitsinghbhandari d824c4f767 ci: add Go build/test and gitleaks secret-scan workflows
- go.yml: gofmt check, build, vet, and race-enabled tests for backend/,
  triggered on backend changes and pushes to main.
- gitleaks.yml: secret scanning on PRs and main using gitleaks-action v1
  (license-free; v2 requires GITLEAKS_LICENSE for org repos).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:02:58 +05:30