diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_4md0a6r9hjtd/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_4md0a6r9hjtd/summary.md new file mode 100644 index 000000000..baf04ac7e --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_4md0a6r9hjtd/summary.md @@ -0,0 +1,33 @@ +# Trajectory: Review and fix PR 1028 + +> **Status:** ✅ Completed +> **Confidence:** 86% +> **Started:** June 3, 2026 at 05:35 AM +> **Completed:** June 3, 2026 at 05:41 AM + +--- + +## Summary + +Reviewed PR 1028 path rename, kept new .agentworkforce/relay behavior, added legacy installer cleanup for uninstall, and validated TS tests/typecheck. + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts + +- **Chose:** Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts +- **Reasoning:** The PR intentionally moves new runtime/global paths to ~/.agentworkforce/relay, but users upgrading from old installer layouts still need uninstall to remove old dashboard assets and bin directories. + +--- + +## Chapters + +### 1. Work + +_Agent: default_ + +- Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts: Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_4md0a6r9hjtd/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_4md0a6r9hjtd/trajectory.json new file mode 100644 index 000000000..63f47572b --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_4md0a6r9hjtd/trajectory.json @@ -0,0 +1,53 @@ +{ + "id": "traj_4md0a6r9hjtd", + "version": 1, + "task": { + "title": "Review and fix PR 1028" + }, + "status": "completed", + "startedAt": "2026-06-03T05:35:04.174Z", + "completedAt": "2026-06-03T05:41:21.816Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-06-03T05:39:59.848Z" + } + ], + "chapters": [ + { + "id": "chap_jfgf7qdbqv3z", + "title": "Work", + "agentName": "default", + "startedAt": "2026-06-03T05:39:59.848Z", + "endedAt": "2026-06-03T05:41:21.816Z", + "events": [ + { + "ts": 1780465199849, + "type": "decision", + "content": "Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts: Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts", + "raw": { + "question": "Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts", + "chosen": "Kept uninstall cleanup for legacy ~/.agent-relay installer artifacts", + "alternatives": [], + "reasoning": "The PR intentionally moves new runtime/global paths to ~/.agentworkforce/relay, but users upgrading from old installer layouts still need uninstall to remove old dashboard assets and bin directories." + }, + "significance": "high" + } + ] + } + ], + "retrospective": { + "summary": "Reviewed PR 1028 path rename, kept new .agentworkforce/relay behavior, added legacy installer cleanup for uninstall, and validated TS tests/typecheck.", + "approach": "Standard approach", + "confidence": 0.86 + }, + "commits": [], + "filesChanged": [], + "projectId": "AgentWorkforce/relay", + "tags": [], + "_trace": { + "startRef": "be1f0dc5ec01a3b6f4bfdbb3921f7b8b089b0281", + "endRef": "be1f0dc5ec01a3b6f4bfdbb3921f7b8b089b0281" + } +} diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_7vb67ogdp9x7/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_7vb67ogdp9x7/summary.md new file mode 100644 index 000000000..784ccd228 --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_7vb67ogdp9x7/summary.md @@ -0,0 +1,14 @@ +# Trajectory: Review and fix PR 1028 + +> **Status:** ✅ Completed +> **Confidence:** 86% +> **Started:** June 3, 2026 at 05:30 AM +> **Completed:** June 3, 2026 at 05:34 AM + +--- + +## Summary + +Reviewed PR 1028 path rename, ran stale path sweeps, built TypeScript packages, and verified full Vitest plus typecheck. No code fixes were required after local review. + +**Approach:** Standard approach diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_7vb67ogdp9x7/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_7vb67ogdp9x7/trajectory.json new file mode 100644 index 000000000..aac8d2341 --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_7vb67ogdp9x7/trajectory.json @@ -0,0 +1,25 @@ +{ + "id": "traj_7vb67ogdp9x7", + "version": 1, + "task": { + "title": "Review and fix PR 1028" + }, + "status": "completed", + "startedAt": "2026-06-03T05:30:21.088Z", + "completedAt": "2026-06-03T05:34:56.500Z", + "agents": [], + "chapters": [], + "retrospective": { + "summary": "Reviewed PR 1028 path rename, ran stale path sweeps, built TypeScript packages, and verified full Vitest plus typecheck. No code fixes were required after local review.", + "approach": "Standard approach", + "confidence": 0.86 + }, + "commits": [], + "filesChanged": [], + "projectId": "AgentWorkforce/relay", + "tags": [], + "_trace": { + "startRef": "3387f9f4d6e2dfc700a1133246dbff16b0722acc", + "endRef": "3387f9f4d6e2dfc700a1133246dbff16b0722acc" + } +} diff --git a/.gitignore b/.gitignore index 9123f35b7..e6fe0ed66 100644 --- a/.gitignore +++ b/.gitignore @@ -80,7 +80,7 @@ __pycache__/ *.egg-info/ .pytest_cache/ -.agent-relay/ +.agentworkforce/relay/ .relay/ .sst diff --git a/.npmignore b/.npmignore index f7d99ce68..69244ceb3 100644 --- a/.npmignore +++ b/.npmignore @@ -58,7 +58,7 @@ packages/*/tests/ .relay/ .openskills/ .trails/ -.agent-relay/ +.agentworkforce/relay/ .turbo/ .cursor/ .mcp.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 11f325c2e..0909452c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Relay stores per-project runtime state in `.agentworkforce/relay/` (was `.agent-relay/`), and the global data/log home moves from `~/.agent-relay`, `$XDG_DATA_HOME/agent-relay`, and platform equivalents to `agentworkforce/relay`. The `~/.config/agent-relay` config directory is unchanged. - Upgraded relaycast to 2.x (`@relaycast/sdk` and the `relaycast` Rust crate): spawn/release now run as relaycast actions. The broker registers `spawn`/`release` actions on startup and handles `action.invoked` (reading input via the actions API and reporting completion) in place of the removed `command.invoked` protocol; `@agent-relay/openclaw` surfaces `action.invoked` instead of channel slash-commands. - `agent-relay`, `@agent-relay/sdk`, and `@agent-relay/openclaw` now consume `@relaycast/sdk` 2.1.x for Relaycast's latest inbox delivery behavior. - `README.md` and `packages/sdk/README.md` now present Agent Relay around three public SDK categories: messaging, delivery, and actions. @@ -44,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Changes +- Relay's on-disk state directory is renamed from `.agent-relay/` to `.agentworkforce/relay/`, and the global `~/.agent-relay`, `$XDG_DATA_HOME/agent-relay`, platform data dirs, and broker log dirs move under `agentworkforce/relay`. Existing brokers and state under the old paths are not migrated. - `@agent-relay/sdk` is scoped to communication primitives; managed broker startup, PTY/headless harness spawning, workflow supervision, and harness lifecycle helpers move to optional `@agent-relay/harness-driver`. - `@agent-relay/sdk` removes root and subpath exports for broker clients, spawn facades, PTY/headless helpers, workflow/consensus/shadow helpers, communicate adapters, browser/worker entry points, and GitHub/Slack primitive adapters. - `agent-relay` removes spawn-first, workflow/swarm, DLQ, activity, log, and `on` command trees from the default CLI package. @@ -58,6 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Migration Guidance +- Stop any running broker before upgrading, then remove the stale `.agent-relay/` directory (and `~/.agent-relay`, `$XDG_DATA_HOME/agent-relay` if present) and restart with `agent-relay up`; state is recreated under `.agentworkforce/relay/`. The broker re-adds `.agentworkforce/relay/` to `.git/info/exclude`, leaving any tracked `.agentworkforce/trajectories/` untouched. - Install `@agent-relay/harness-driver` for code that starts brokers, spawns PTY/headless agents, waits for managed harness state, or runs supervised workflows; keep `@agent-relay/sdk` for identities, messages, delivery/read state, presence, and commands. - Replace `agent-relay up/status/down` with `agent-relay driver up/status/down` when you want Agent Relay to manage the local harness boundary. - Replace SDK spawn calls with driver actions (`agent.create`, `agent.release`, `agent.status`) when agents need to request managed harness work through MCP. @@ -71,6 +74,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `sdk-swift`: replace `RelayCast(apiKey:baseURL:)` with `AgentRelayClient(apiKey:baseURL:)`. The public API surface is otherwise unchanged. - Import `HarnessDriverClient` (was `AgentRelayClient`) from `@agent-relay/harness-driver`; the `connect()`/`spawn()` API is unchanged. Update companion type names (`HarnessDriverClientOptions`, `RuntimeSpawnOptions`, `BrokerInitArgs`, `HarnessDriverEvents`, `HarnessDriverProtocolError`) at import sites. +### Removed + +- `@agent-relay/config` removes the unused `getGlobalPaths()` and `listProjects()` exports (legacy global-storage helpers) and drops the `.agent-relay.json` project-root config fallback; shadow config now loads only from `.agentworkforce/relay/config.json`. +- `@agent-relay/config` removes the legacy `/tmp/relay-outbox` symlink: `RelayFileWriter` no longer creates it on `ensureDirectories()`, and the `getLegacyOutboxPath()` method and `RelayPaths.legacyOutboxDir` field are gone. +- `agent-relay` drops the legacy `~/.agent-relay/dashboard` static-asset fallback from broker startup; dashboard assets resolve only from `~/.relay/dashboard`. (Uninstall still purges legacy install dirs.) + ### Fixed - `@agent-relay/cloud`: CLI browser login ignores stray localhost callbacks with an invalid state parameter, so first-time sign-ins are not shown a false hosted error or aborted before the real OAuth callback returns. diff --git a/TELEMETRY.md b/TELEMETRY.md index ee5f6c898..9e858247d 100644 --- a/TELEMETRY.md +++ b/TELEMETRY.md @@ -75,7 +75,7 @@ export DO_NOT_TRACK=1 **Option 3: Configuration file** -Create or edit `~/.agent-relay/telemetry.json`: +Create or edit `~/.agentworkforce/relay/telemetry.json`: ```json { diff --git a/crates/broker/src/cli/mod.rs b/crates/broker/src/cli/mod.rs index 97c0aba2a..254e1fae2 100644 --- a/crates/broker/src/cli/mod.rs +++ b/crates/broker/src/cli/mod.rs @@ -140,16 +140,16 @@ pub(crate) struct DumpPtyCommand { pub(crate) format: DumpPtyFormat, /// Override the broker base URL. Falls back to RELAY_BROKER_URL, then to - /// reading `.agent-relay/connection.json` in the current directory. + /// reading `.agentworkforce/relay/connection.json` in the current directory. #[arg(long)] pub(crate) broker_url: Option, /// Override the broker API key. Falls back to RELAY_BROKER_API_KEY, then - /// to reading `.agent-relay/connection.json` in the current directory. + /// to reading `.agentworkforce/relay/connection.json` in the current directory. #[arg(long)] pub(crate) api_key: Option, - /// Override the directory containing `.agent-relay/connection.json` when + /// Override the directory containing `.agentworkforce/relay/connection.json` when /// auto-discovering the broker. #[arg(long)] pub(crate) state_dir: Option, @@ -232,7 +232,7 @@ pub(crate) struct InitCommand { pub(crate) api_bind: String, /// Enable persistence: write state, pending-deliveries, lock, and PID files - /// to `.agent-relay/` in the working directory. MCP configuration is injected + /// to `.agentworkforce/relay/` in the working directory. MCP configuration is injected /// into spawned agents at launch time instead of being written to project /// config files. When omitted (the default), runtime files are written to a /// deterministic temp directory and cleaned up opportunistically; identity @@ -242,7 +242,7 @@ pub(crate) struct InitCommand { pub(crate) persist: bool, /// Override the directory used for broker state files (connection.json, - /// locks, state, pending-deliveries). Defaults to `.agent-relay/` in the + /// locks, state, pending-deliveries). Defaults to `.agentworkforce/relay/` in the /// working directory when `--persist` is set, or a temp directory otherwise. #[arg(long)] pub(crate) state_dir: Option, diff --git a/crates/broker/src/runtime/connection.rs b/crates/broker/src/runtime/connection.rs index 1e04d461d..fd9e44345 100644 --- a/crates/broker/src/runtime/connection.rs +++ b/crates/broker/src/runtime/connection.rs @@ -11,11 +11,11 @@ pub(crate) struct BrokerConnection { /// /// 1. Explicit CLI args (`--broker-url`, `--api-key`). When `--broker-url` /// is supplied without an API key, we still attempt to fall back to the -/// API key from env / `.agent-relay/connection.json` so users don't have +/// API key from env / `.agentworkforce/relay/connection.json` so users don't have /// to repeat `--api-key` for every dump-pty invocation. /// 2. Env vars `RELAY_BROKER_URL` / `RELAY_BROKER_API_KEY`. /// 3. `connection.json` in the supplied state dir, otherwise -/// `.agent-relay/connection.json` directly under the current working +/// `.agentworkforce/relay/connection.json` directly under the current working /// directory. The bare `cwd` is intentionally NOT probed — an unrelated /// `connection.json` sitting in the user's repo root must not silently /// redirect the snapshot request (and its broker API key) elsewhere. @@ -31,7 +31,7 @@ pub(crate) fn discover_broker_connection( let cwd = std::env::current_dir().ok()?; let roots: Vec = match state_dir { Some(dir) => vec![dir.to_path_buf()], - None => vec![cwd.join(".agent-relay")], + None => vec![cwd.join(".agentworkforce/relay")], }; for root in roots { let path = root.join("connection.json"); @@ -77,7 +77,7 @@ pub(crate) fn discover_broker_connection( let cwd = std::env::current_dir().context("failed to read current directory")?; let search_roots: Vec = match state_dir { Some(dir) => vec![dir.to_path_buf()], - None => vec![cwd.join(".agent-relay")], + None => vec![cwd.join(".agentworkforce/relay")], }; for root in &search_roots { @@ -112,7 +112,7 @@ pub(crate) fn discover_broker_connection( anyhow::bail!( "could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, \ - or run from a directory containing .agent-relay/connection.json" + or run from a directory containing .agentworkforce/relay/connection.json" ); } diff --git a/crates/broker/src/runtime/init.rs b/crates/broker/src/runtime/init.rs index 6f76ceebc..f2ba8345c 100644 --- a/crates/broker/src/runtime/init.rs +++ b/crates/broker/src/runtime/init.rs @@ -33,11 +33,11 @@ pub(crate) async fn run_init(cmd: InitCommand, telemetry: TelemetryClient) -> Re let paths = if cmd.persist || custom_state_dir.is_some() { ensure_runtime_paths(&runtime_cwd, &resolved_name, custom_state_dir.as_deref())? } else { - // Warn only if there is *actual broker state* in .agent-relay/ from a + // Warn only if there is *actual broker state* in .agentworkforce/relay/ from a // prior `--persist` run that could confuse this ephemeral run. // - // The SDK workflow runner ALWAYS writes .agent-relay/step-outputs/ and - // .agent-relay/team/worker-logs/ regardless of broker mode (those are + // The SDK workflow runner ALWAYS writes .agentworkforce/relay/step-outputs/ and + // .agentworkforce/relay/team/worker-logs/ regardless of broker mode (those are // durable artifacts, not broker state), so a bare directory check fires // on virtually every workflow run — a noisy false positive. // @@ -45,9 +45,9 @@ pub(crate) async fn run_init(cmd: InitCommand, telemetry: TelemetryClient) -> Re // (the persist-mode helper in runtime/paths.rs) writes it as // `state-{safe_name}.json`, where `safe_name` is the sanitized broker // name — so the exact filename varies by run. Glob for any - // `state-*.json` entry in `.agent-relay/` and surface every match so + // `state-*.json` entry in `.agentworkforce/relay/` and surface every match so // the user can see exactly what's stale regardless of broker name. - let stale_dir = runtime_cwd.join(".agent-relay"); + let stale_dir = runtime_cwd.join(".agentworkforce/relay"); let stale_state_files: Vec = std::fs::read_dir(&stale_dir) .ok() .into_iter() diff --git a/crates/broker/src/runtime/paths.rs b/crates/broker/src/runtime/paths.rs index eef67eda5..9a7bdb0c3 100644 --- a/crates/broker/src/runtime/paths.rs +++ b/crates/broker/src/runtime/paths.rs @@ -11,11 +11,11 @@ pub(crate) struct RuntimePaths { } /// Returns the continuity directory path derived from the state file path. -/// State path is always `{cwd}/.agent-relay/state.json`, so parent is `{cwd}/.agent-relay/`. +/// State path is always `{cwd}/.agentworkforce/relay/state.json`, so parent is `{cwd}/.agentworkforce/relay/`. pub(crate) fn continuity_dir(state_path: &Path) -> PathBuf { state_path .parent() - .expect("state_path always has a parent (.agent-relay/)") + .expect("state_path always has a parent (.agentworkforce/relay/)") .join("continuity") } @@ -74,7 +74,7 @@ pub(crate) fn ensure_runtime_paths( ) -> Result { let root = state_dir .map(PathBuf::from) - .unwrap_or_else(|| cwd.join(".agent-relay")); + .unwrap_or_else(|| cwd.join(".agentworkforce/relay")); std::fs::create_dir_all(&root) .with_context(|| format!("failed to create runtime dir {}", root.display()))?; @@ -150,7 +150,7 @@ pub(crate) fn ensure_runtime_paths( } } // PID file missing or unreadable while lock is held — treat as stale. - // This happens when the user deletes .agent-relay/ while an old broker + // This happens when the user deletes .agentworkforce/relay/ while an old broker // is still alive, or during the shutdown race (PID deleted before flock // released). tracing::warn!( diff --git a/crates/broker/src/runtime/tests.rs b/crates/broker/src/runtime/tests.rs index b9bede14f..9dd2f7087 100644 --- a/crates/broker/src/runtime/tests.rs +++ b/crates/broker/src/runtime/tests.rs @@ -1893,29 +1893,33 @@ fn is_pid_alive_eperm_means_alive() { #[test] fn continuity_dir_derives_correct_path_from_state_json() { - let state_path = std::path::Path::new("/project/.agent-relay/state.json"); + let state_path = std::path::Path::new("/project/.agentworkforce/relay/state.json"); let result = continuity_dir(state_path); assert_eq!( result, - std::path::PathBuf::from("/project/.agent-relay/continuity") + std::path::PathBuf::from("/project/.agentworkforce/relay/continuity") ); } #[test] fn continuity_dir_works_with_nested_project_path() { - let state_path = std::path::Path::new("/home/user/projects/my-app/.agent-relay/state.json"); + let state_path = + std::path::Path::new("/home/user/projects/my-app/.agentworkforce/relay/state.json"); let result = continuity_dir(state_path); assert_eq!( result, - std::path::PathBuf::from("/home/user/projects/my-app/.agent-relay/continuity") + std::path::PathBuf::from("/home/user/projects/my-app/.agentworkforce/relay/continuity") ); } #[test] fn continuity_dir_preserves_relative_paths() { - let state_path = std::path::Path::new(".agent-relay/state.json"); + let state_path = std::path::Path::new(".agentworkforce/relay/state.json"); let result = continuity_dir(state_path); - assert_eq!(result, std::path::PathBuf::from(".agent-relay/continuity")); + assert_eq!( + result, + std::path::PathBuf::from(".agentworkforce/relay/continuity") + ); } #[test] diff --git a/crates/broker/src/runtime/util.rs b/crates/broker/src/runtime/util.rs index 4eb8c0d72..ae160f08e 100644 --- a/crates/broker/src/runtime/util.rs +++ b/crates/broker/src/runtime/util.rs @@ -81,10 +81,10 @@ fn sanitize_filename_segment(value: &str) -> String { /// Platform-standard directory for broker tracing logs. /// -/// - macOS: `~/Library/Logs/agent-relay` -/// - Linux / other Unix: `$XDG_STATE_HOME/agent-relay/logs` (defaults to -/// `~/.local/state/agent-relay/logs`) -/// - Windows: `%LOCALAPPDATA%\agent-relay\Logs` +/// - macOS: `~/Library/Logs/agentworkforce/relay` +/// - Linux / other Unix: `$XDG_STATE_HOME/agentworkforce/relay/logs` (defaults to +/// `~/.local/state/agentworkforce/relay/logs`) +/// - Windows: `%LOCALAPPDATA%\agentworkforce\relay\Logs` /// /// Returns `None` only when neither the platform-specific directory nor the /// home directory can be resolved. @@ -92,18 +92,23 @@ pub(crate) fn broker_log_dir() -> Option { #[cfg(target_os = "macos")] { let home = dirs::home_dir()?; - Some(home.join("Library").join("Logs").join("agent-relay")) + Some( + home.join("Library") + .join("Logs") + .join("agentworkforce") + .join("relay"), + ) } #[cfg(target_os = "windows")] { let local = dirs::data_local_dir()?; - Some(local.join("agent-relay").join("Logs")) + Some(local.join("agentworkforce").join("relay").join("Logs")) } #[cfg(all(not(target_os = "macos"), not(target_os = "windows")))] { let state = dirs::state_dir() .or_else(|| dirs::home_dir().map(|h| h.join(".local").join("state")))?; - Some(state.join("agent-relay").join("logs")) + Some(state.join("agentworkforce").join("relay").join("logs")) } } @@ -452,17 +457,19 @@ mod tests { if cfg!(target_os = "macos") { assert!( - path_str.contains("/Library/Logs/agent-relay"), + path_str.contains("/Library/Logs/agentworkforce/relay"), "expected macOS Library/Logs path, got: {path_str}" ); } else if cfg!(target_os = "windows") { assert!( - path_str.to_ascii_lowercase().contains("agent-relay/logs"), + path_str + .to_ascii_lowercase() + .contains("agentworkforce/relay/logs"), "expected Windows LocalAppData layout, got: {path_str}" ); } else { assert!( - path_str.contains("agent-relay/logs"), + path_str.contains("agentworkforce/relay/logs"), "expected Unix state path, got: {path_str}" ); } diff --git a/crates/broker/src/telemetry.rs b/crates/broker/src/telemetry.rs index 7f34d2ca0..72a175d8a 100644 --- a/crates/broker/src/telemetry.rs +++ b/crates/broker/src/telemetry.rs @@ -6,7 +6,7 @@ //! Opt-out: //! - Set `AGENT_RELAY_TELEMETRY_DISABLED=1` (or `true`) //! - Set `DO_NOT_TRACK=1` (cross-tool convention, https://consoledonottrack.com) -//! - Or write `{"enabled": false}` to `~/.agent-relay/telemetry.json` +//! - Or write `{"enabled": false}` to `~/.agentworkforce/relay/telemetry.json` use std::path::PathBuf; @@ -185,7 +185,7 @@ impl TelemetryEvent { } // --------------------------------------------------------------------------- -// Preferences file (~/.agent-relay/telemetry.json) +// Preferences file (~/.agentworkforce/relay/telemetry.json) // --------------------------------------------------------------------------- #[derive(Debug, Default, Serialize, Deserialize)] @@ -197,7 +197,7 @@ struct TelemetryPrefs { } fn prefs_path() -> Option { - dirs::home_dir().map(|h| h.join(".agent-relay").join("telemetry.json")) + dirs::home_dir().map(|h| h.join(".agentworkforce/relay").join("telemetry.json")) } fn load_prefs() -> TelemetryPrefs { @@ -232,7 +232,8 @@ fn machine_id_path() -> Option { dirs::home_dir().map(|h| { h.join(".local") .join("share") - .join("agent-relay") + .join("agentworkforce") + .join("relay") .join("machine-id") }) } diff --git a/crates/broker/tests/continuity.rs b/crates/broker/tests/continuity.rs index 1fbe5d0c6..c25f7c768 100644 --- a/crates/broker/tests/continuity.rs +++ b/crates/broker/tests/continuity.rs @@ -14,24 +14,27 @@ use std::path::Path; fn continuity_dir(state_path: &Path) -> std::path::PathBuf { state_path .parent() - .expect("state_path always has a parent (.agent-relay/)") + .expect("state_path always has a parent (.agentworkforce/relay/)") .join("continuity") } #[test] fn continuity_dir_from_state_path() { - let state_path = Path::new("/project/.agent-relay/state.json"); + let state_path = Path::new("/project/.agentworkforce/relay/state.json"); let result = continuity_dir(state_path); - assert_eq!(result, Path::new("/project/.agent-relay/continuity")); + assert_eq!( + result, + Path::new("/project/.agentworkforce/relay/continuity") + ); } #[test] fn continuity_dir_from_deeply_nested_state_path() { - let state_path = Path::new("/home/user/work/repo/.agent-relay/state.json"); + let state_path = Path::new("/home/user/work/repo/.agentworkforce/relay/state.json"); let result = continuity_dir(state_path); assert_eq!( result, - Path::new("/home/user/work/repo/.agent-relay/continuity") + Path::new("/home/user/work/repo/.agentworkforce/relay/continuity") ); } diff --git a/install.sh b/install.sh index e0a1f4931..8aeb171b6 100755 --- a/install.sh +++ b/install.sh @@ -6,7 +6,7 @@ set -e # # Options (set as environment variables): # AGENT_RELAY_VERSION - Specific version to install (default: latest) -# AGENT_RELAY_INSTALL_DIR - Installation directory (default: ~/.agent-relay) +# AGENT_RELAY_INSTALL_DIR - Installation directory (default: ~/.agentworkforce/relay) # AGENT_RELAY_BIN_DIR - Binary directory (default: ~/.local/bin) # AGENT_RELAY_NO_DASHBOARD - Skip dashboard installation (default: false) # AGENT_RELAY_TELEMETRY_DISABLED - Disable anonymous install telemetry (default: false) @@ -14,7 +14,7 @@ set -e REPO_RELAY="AgentWorkforce/relay" REPO_DASHBOARD="AgentWorkforce/relay-dashboard" VERSION="${AGENT_RELAY_VERSION:-latest}" -INSTALL_DIR="${AGENT_RELAY_INSTALL_DIR:-$HOME/.agent-relay}" +INSTALL_DIR="${AGENT_RELAY_INSTALL_DIR:-$HOME/.agentworkforce/relay}" BIN_DIR="${AGENT_RELAY_BIN_DIR:-$HOME/.local/bin}" ORIGINAL_PATH="${PATH:-}" STANDALONE_FAILURE_REASON="" @@ -966,7 +966,7 @@ case "${1:-}" in echo "" echo "Environment variables:" echo " AGENT_RELAY_VERSION Specific version to install (default: latest)" - echo " AGENT_RELAY_INSTALL_DIR Installation directory (default: ~/.agent-relay)" + echo " AGENT_RELAY_INSTALL_DIR Installation directory (default: ~/.agentworkforce/relay)" echo " AGENT_RELAY_BIN_DIR Binary directory (default: ~/.local/bin)" echo " AGENT_RELAY_NO_DASHBOARD Skip dashboard installation (default: false)" echo " AGENT_RELAY_TELEMETRY_DISABLED Disable anonymous install telemetry (default: false)" diff --git a/packages/cli/src/cli/commands/core.test.ts b/packages/cli/src/cli/commands/core.test.ts index e0bd98b4d..ae901ebdb 100644 --- a/packages/cli/src/cli/commands/core.test.ts +++ b/packages/cli/src/cli/commands/core.test.ts @@ -1,4 +1,5 @@ import { Command } from 'commander'; +import os from 'node:os'; import { beforeEach, describe, expect, it, vi } from 'vitest'; const sdkStatusClient = { @@ -105,10 +106,10 @@ function createHarness(options?: { checkForUpdatesResult?: Awaited>; }) { const projectRoot = '/tmp/project'; - const dataDir = '/tmp/project/.agent-relay'; - const relaySockPath = '/tmp/project/.agent-relay/relay.sock'; - const connectionPath = '/tmp/project/.agent-relay/connection.json'; - const runtimePath = '/tmp/project/.agent-relay/runtime.json'; + const dataDir = '/tmp/project/.agentworkforce/relay'; + const relaySockPath = '/tmp/project/.agentworkforce/relay/relay.sock'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; + const runtimePath = '/tmp/project/.agentworkforce/relay/runtime.json'; const fs = options?.fs ?? createFsMock(); const relay = options?.relay ?? createRelayMock(); @@ -122,8 +123,8 @@ function createHarness(options?: { getProjectPaths: vi.fn(() => ({ projectRoot, dataDir, - teamDir: '/tmp/project/.agent-relay/teams', - dbPath: '/tmp/project/.agent-relay/messages.db', + teamDir: '/tmp/project/.agentworkforce/relay/teams', + dbPath: '/tmp/project/.agentworkforce/relay/messages.db', projectId: 'project', })), loadTeamsConfig: vi.fn(() => options?.teamsConfig ?? null), @@ -230,7 +231,7 @@ describe('registerCoreCommands', () => { }); it('up exits early when connection metadata points to a running process', async () => { - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(3030) }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if (pid === 3030 && signal === 0) { @@ -255,7 +256,7 @@ describe('registerCoreCommands', () => { const relay = createRelayMock({ getStatus: vi.fn(async () => { throw new Error( - 'broker exited (code=1, signal=null): Error: another broker instance is already running in this directory (/tmp/project/.agent-relay)' + 'broker exited (code=1, signal=null): Error: another broker instance is already running in this directory (/tmp/project/.agentworkforce/relay)' ); }), }); @@ -265,7 +266,7 @@ describe('registerCoreCommands', () => { expect(exitCode).toBe(1); expect(deps.error).toHaveBeenCalledWith( - 'Broker already running for this project (lock: /tmp/project/.agent-relay).' + 'Broker already running for this project (lock: /tmp/project/.agentworkforce/relay).' ); expect(deps.error).toHaveBeenCalledWith( 'Run `agent-relay status` to inspect it, then `agent-relay down` to stop it.' @@ -448,7 +449,7 @@ describe('registerCoreCommands', () => { const fs = createFsMock(); const sleepImpl = vi.fn(async (ms: number) => { now += ms; - fs.writeFileSync('/tmp/project/.agent-relay/connection.json', connectionFile(4242)); + fs.writeFileSync('/tmp/project/.agentworkforce/relay/connection.json', connectionFile(4242)); }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if ((pid === 9001 || pid === 4242) && signal === 0) return; @@ -571,7 +572,7 @@ describe('registerCoreCommands', () => { const fs = createFsMock(); const sleepImpl = vi.fn(async (ms: number) => { now += ms; - fs.writeFileSync('/tmp/project/.agent-relay/connection.json', connectionFile(4242)); + fs.writeFileSync('/tmp/project/.agentworkforce/relay/connection.json', connectionFile(4242)); }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if ((pid === 9001 || pid === 4242) && signal === 0) return; @@ -648,10 +649,10 @@ describe('registerCoreCommands', () => { 'khaliqgant 111 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /bin/zsh -lc BROKER=/tmp/project/target/release/agent-relay-broker node /tmp/agent-relay.js down --force', 'khaliqgant 222 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /opt/bin/agent-relay-broker init --name project --channels general --persist', 'khaliqgant 333 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /opt/bin/agent-relay-broker init --name project --channels general --persist', - 'khaliqgant 444 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /opt/bin/agent-relay-broker init --state-dir /tmp/project/.agent-relay --persist', - 'khaliqgant 555 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /opt/bin/agent-relay-broker init --state-dir /tmp/project-other/.agent-relay --persist', - 'khaliqgant 666 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /Users/test/.agent-relay/bin/agent-relay up --no-dashboard --foreground', - 'khaliqgant 777 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /Users/test/.agent-relay/bin/agent-relay status --wait-for=30', + 'khaliqgant 444 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /opt/bin/agent-relay-broker init --state-dir /tmp/project/.agentworkforce/relay --persist', + 'khaliqgant 555 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /opt/bin/agent-relay-broker init --state-dir /tmp/project-other/.agentworkforce/relay --persist', + 'khaliqgant 666 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /Users/test/.agentworkforce/relay/bin/agent-relay up --no-dashboard --foreground', + 'khaliqgant 777 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /Users/test/.agentworkforce/relay/bin/agent-relay status --wait-for=30', ].join('\n'), stderr: '', }; @@ -710,7 +711,7 @@ describe('registerCoreCommands', () => { return { stdout: [ 'USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND', - 'khaliqgant 777 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /Users/test/.agent-relay/bin/agent-relay up --no-dashboard --foreground', + 'khaliqgant 777 0.0 0.0 1 1 ?? S 1:00PM 0:00.01 /Users/test/.agentworkforce/relay/bin/agent-relay up --no-dashboard --foreground', ].join('\n'), stderr: '', }; @@ -722,7 +723,7 @@ describe('registerCoreCommands', () => { }); const sleepImpl = vi.fn(async (ms: number) => { now += ms; - fs.writeFileSync('/tmp/project/.agent-relay/connection.json', connectionFile(4242)); + fs.writeFileSync('/tmp/project/.agentworkforce/relay/connection.json', connectionFile(4242)); }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if (signal === 0) { @@ -753,11 +754,11 @@ describe('registerCoreCommands', () => { it('up --no-dashboard replaces a live broker PID whose API never becomes ready', async () => { const spawnedProcess = createSpawnedProcessMock({ pid: 9001 }); const runningPids = new Set([3030, 9001, 4242]); - const fs = createFsMock({ ['/tmp/project/.agent-relay/connection.json']: connectionFile(3030) }); + const fs = createFsMock({ ['/tmp/project/.agentworkforce/relay/connection.json']: connectionFile(3030) }); let now = 0; const sleepImpl = vi.fn(async (ms: number) => { now += ms; - fs.writeFileSync('/tmp/project/.agent-relay/connection.json', connectionFile(4242)); + fs.writeFileSync('/tmp/project/.agentworkforce/relay/connection.json', connectionFile(4242)); }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if (signal === 0) { @@ -781,7 +782,7 @@ describe('registerCoreCommands', () => { expect(exitCode).toBe(0); expect(killImpl).toHaveBeenCalledWith(3030, 'SIGTERM'); - expect(fs.unlinkSync).toHaveBeenCalledWith('/tmp/project/.agent-relay/connection.json'); + expect(fs.unlinkSync).toHaveBeenCalledWith('/tmp/project/.agentworkforce/relay/connection.json'); expect(deps.warn).toHaveBeenCalledWith( 'Broker process is running but the API is not ready; killing half-started broker (pid: 3030).' ); @@ -796,7 +797,7 @@ describe('registerCoreCommands', () => { const fs = createFsMock(); const sleepImpl = vi.fn(async (ms: number) => { now += ms; - fs.writeFileSync('/tmp/project/.agent-relay/connection.json', connectionFile(4242)); + fs.writeFileSync('/tmp/project/.agentworkforce/relay/connection.json', connectionFile(4242)); }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if (signal === 0) { @@ -894,9 +895,9 @@ describe('registerCoreCommands', () => { }); it('down stops broker and cleans stale files', async () => { - const connectionPath = '/tmp/project/.agent-relay/connection.json'; - const relaySockPath = '/tmp/project/.agent-relay/relay.sock'; - const runtimePath = '/tmp/project/.agent-relay/runtime.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; + const relaySockPath = '/tmp/project/.agentworkforce/relay/relay.sock'; + const runtimePath = '/tmp/project/.agentworkforce/relay/runtime.json'; const fs = createFsMock({ [connectionPath]: connectionFile(3030), @@ -940,7 +941,7 @@ describe('registerCoreCommands', () => { }); it('status checks broker status and prints metrics', async () => { - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(4242) }); sdkStatusClient.getStatus.mockResolvedValueOnce({ agent_count: 4, pending_delivery_count: 2 }); sdkStatusClient.getSession.mockResolvedValueOnce({ workspace_key: 'rk_live_test123' }); @@ -959,7 +960,7 @@ describe('registerCoreCommands', () => { }); it('status omits workspace key and observer when broker has no workspace_key', async () => { - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(4242) }); sdkStatusClient.getStatus.mockResolvedValueOnce({ agent_count: 0, pending_delivery_count: 0 }); sdkStatusClient.getSession.mockResolvedValueOnce({}); @@ -976,7 +977,7 @@ describe('registerCoreCommands', () => { }); it('status cleans stale connection metadata when broker is not running', async () => { - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(9999) }); const killImpl = vi.fn(() => { const err = new Error('gone') as Error & { code?: string }; @@ -998,7 +999,7 @@ describe('registerCoreCommands', () => { const fs = createFsMock(); const sleepImpl = vi.fn(async (ms: number) => { now += ms; - fs.writeFileSync('/tmp/project/.agent-relay/connection.json', connectionFile(4242)); + fs.writeFileSync('/tmp/project/.agentworkforce/relay/connection.json', connectionFile(4242)); }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if (pid === 4242 && signal === 0) return; @@ -1021,7 +1022,7 @@ describe('registerCoreCommands', () => { it('status --wait-for waits for the broker API after the PID appears', async () => { let now = 0; - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(4242) }); const sleepImpl = vi.fn(async (ms: number) => { now += ms; @@ -1055,7 +1056,7 @@ describe('registerCoreCommands', () => { it('status --wait-for treats getStatus success as ready even when session lookup fails', async () => { const now = 0; - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(4242) }); const killImpl = vi.fn((pid: number, signal?: NodeJS.Signals | number) => { if (pid === 4242 && signal === 0) return; @@ -1092,7 +1093,7 @@ describe('registerCoreCommands', () => { it('status --wait-for reports STARTING and exits non-zero when the PID is live but the API is unready', async () => { let now = 0; - const connectionPath = '/tmp/project/.agent-relay/connection.json'; + const connectionPath = '/tmp/project/.agentworkforce/relay/connection.json'; const fs = createFsMock({ [connectionPath]: connectionFile(4242) }); const sleepImpl = vi.fn(async (ms: number) => { now += ms; @@ -1162,6 +1163,35 @@ describe('registerCoreCommands', () => { expect(deps.execCommand).not.toHaveBeenCalled(); }); + it('uninstall dry-run covers renamed and legacy installer asset directories', async () => { + const { deps } = createHarness(); + const program = new Command(); + registerCoreMaintenance(program, deps); + const home = os.homedir(); + const paths = [ + `${home}/.agentworkforce/relay/dashboard/out`, + `${home}/.agentworkforce/relay/dashboard/.version`, + `${home}/.agent-relay/dashboard/out`, + `${home}/.agent-relay/dashboard/.version`, + `${home}/.agentworkforce/relay/bin`, + `${home}/.agent-relay/bin`, + ]; + for (const filePath of paths) { + deps.fs.writeFileSync(filePath, ''); + } + + const exitCode = await runCommand(program, ['uninstall', '--dry-run']); + + expect(exitCode).toBeUndefined(); + for (const filePath of paths.slice(0, 4)) { + expect(deps.log).toHaveBeenCalledWith(`[dry-run] Would remove dashboard asset path: ${filePath}`); + } + for (const filePath of paths.slice(4)) { + expect(deps.log).toHaveBeenCalledWith(`[dry-run] Would remove directory: ${filePath}`); + } + expect(deps.execCommand).not.toHaveBeenCalled(); + }); + it('up always logs the workspace key after broker starts', async () => { const relay = createRelayMock(); const { program, deps } = createHarness({ relay }); diff --git a/packages/cli/src/cli/commands/core.ts b/packages/cli/src/cli/commands/core.ts index dd4aa4547..d11166612 100644 --- a/packages/cli/src/cli/commands/core.ts +++ b/packages/cli/src/cli/commands/core.ts @@ -170,7 +170,7 @@ function findDashboardBinaryDefault(fileSystem: CoreFileSystem): string | null { const searchPaths = [ path.join(homeDir, '.local', 'bin', binaryName), - path.join(homeDir, '.agent-relay', 'bin', binaryName), + path.join(homeDir, '.agentworkforce/relay', 'bin', binaryName), path.join('/usr/local/bin', binaryName), ]; @@ -341,7 +341,10 @@ export function registerCoreCommands(program: Command, overrides: Partial', 'Use a pre-established Relaycast workspace key') - .option('--state-dir ', 'Directory for broker state and connection files (default: .agent-relay/)') + .option( + '--state-dir ', + 'Directory for broker state and connection files (default: .agentworkforce/relay/)' + ) .option('--broker-name ', 'Override the broker name (defaults to project directory basename)') .action( async (options: { diff --git a/packages/cli/src/cli/commands/doctor.test.ts b/packages/cli/src/cli/commands/doctor.test.ts index 9d3c5a261..d69d5901c 100644 --- a/packages/cli/src/cli/commands/doctor.test.ts +++ b/packages/cli/src/cli/commands/doctor.test.ts @@ -178,7 +178,7 @@ function writeConnection(overrides: Partial<{ url: string; api_key: string; pid: beforeEach(() => { tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'doctor-test-')); - dataDir = path.join(tempRoot, '.agent-relay'); + dataDir = path.join(tempRoot, '.agentworkforce/relay'); process.env.AGENT_RELAY_STORAGE_TYPE = 'sqlite'; process.env.AGENT_RELAY_STORAGE_PATH = path.join(dataDir, 'messages.sqlite'); mockStore = new Map(); diff --git a/packages/cli/src/cli/commands/local-agent.ts b/packages/cli/src/cli/commands/local-agent.ts index 3a0343c54..f9420535d 100644 --- a/packages/cli/src/cli/commands/local-agent.ts +++ b/packages/cli/src/cli/commands/local-agent.ts @@ -176,7 +176,7 @@ export function registerLocalAgentCommands( .option('--mode ', 'drive | view | passthrough', 'view') .option('--broker-url ', 'Broker base URL (overrides RELAY_BROKER_URL and connection.json)') .option('--api-key ', 'Broker API key (overrides RELAY_BROKER_API_KEY and connection.json)') - .option('--state-dir ', 'Directory containing connection.json (default: .agent-relay/)') + .option('--state-dir ', 'Directory containing connection.json (default: .agentworkforce/relay/)') .action(async (name: string, options: Record) => { const mode = (options.mode as string) ?? 'view'; if (mode !== 'drive' && mode !== 'view' && mode !== 'passthrough') { diff --git a/packages/cli/src/cli/lib/attach-drive.test.ts b/packages/cli/src/cli/lib/attach-drive.test.ts index 783a3b6b0..4de0408b8 100644 --- a/packages/cli/src/cli/lib/attach-drive.test.ts +++ b/packages/cli/src/cli/lib/attach-drive.test.ts @@ -309,7 +309,7 @@ function createHarness(opts: FetchScript = {}): { const deps: DriveDependencies = { readConnectionFile: vi.fn(() => ({ url: 'http://localhost:3889', api_key: 'k' })), - getDefaultStateDir: vi.fn(() => '/tmp/fake/.agent-relay'), + getDefaultStateDir: vi.fn(() => '/tmp/fake/.agentworkforce/relay'), env: {}, createWebSocket: vi.fn((url: string, headers: Record) => { const socket = new FakeWebSocket(url, headers); diff --git a/packages/cli/src/cli/lib/attach-passthrough.test.ts b/packages/cli/src/cli/lib/attach-passthrough.test.ts index ef0aee58f..9d83f100e 100644 --- a/packages/cli/src/cli/lib/attach-passthrough.test.ts +++ b/packages/cli/src/cli/lib/attach-passthrough.test.ts @@ -267,7 +267,7 @@ function createHarness(opts: FetchScript = {}): { const deps: PassthroughDependencies = { readConnectionFile: vi.fn(() => ({ url: 'http://localhost:3889', api_key: 'k' })), - getDefaultStateDir: vi.fn(() => '/tmp/fake/.agent-relay'), + getDefaultStateDir: vi.fn(() => '/tmp/fake/.agentworkforce/relay'), env: {}, createWebSocket: vi.fn((url: string, headers: Record) => { const socket = new FakeWebSocket(url, headers); diff --git a/packages/cli/src/cli/lib/attach-view.test.ts b/packages/cli/src/cli/lib/attach-view.test.ts index bdb42e6f0..b4df02cdb 100644 --- a/packages/cli/src/cli/lib/attach-view.test.ts +++ b/packages/cli/src/cli/lib/attach-view.test.ts @@ -77,7 +77,7 @@ function createHarness(overrides: HarnessOverrides = {}): { const deps: ViewDependencies = { readConnectionFile: vi.fn(() => overrides.connectionFile ?? null), - getDefaultStateDir: vi.fn(() => overrides.defaultStateDir ?? '/tmp/fake/.agent-relay'), + getDefaultStateDir: vi.fn(() => overrides.defaultStateDir ?? '/tmp/fake/.agentworkforce/relay'), env: overrides.env ?? {}, createWebSocket: vi.fn((url: string, headers: Record) => { const socket = new FakeWebSocket(url, headers); diff --git a/packages/cli/src/cli/lib/attach-view.ts b/packages/cli/src/cli/lib/attach-view.ts index 99600c7bf..1db7a5a70 100644 --- a/packages/cli/src/cli/lib/attach-view.ts +++ b/packages/cli/src/cli/lib/attach-view.ts @@ -90,10 +90,10 @@ function readConnectionFileFromDisk(stateDir: string): unknown { } function defaultStateDir(): string { - // Match the Rust broker's discovery convention: `.agent-relay/` under the + // Match the Rust broker's discovery convention: `.agentworkforce/relay/` under the // project root (resolved the same way the rest of the CLI does it). const projectRoot = getProjectPaths().projectRoot; - return path.join(projectRoot, '.agent-relay'); + return path.join(projectRoot, '.agentworkforce/relay'); } function withDefaults(overrides: Partial = {}): ViewDependencies { @@ -136,7 +136,7 @@ function readString(obj: unknown, key: string): string | undefined { * * 1. `--broker-url` / `--api-key` CLI flags * 2. `RELAY_BROKER_URL` / `RELAY_BROKER_API_KEY` environment variables - * 3. `/connection.json` (default `.agent-relay/connection.json`) + * 3. `/connection.json` (default `.agentworkforce/relay/connection.json`) * * Matches the resolution order used by `agent-relay-broker dump-pty` so users * don't have to learn two patterns. @@ -224,7 +224,7 @@ export async function runViewSession( if (!connection) { deps.error( 'Error: could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, ' + - 'or run from a directory containing .agent-relay/connection.json.' + 'or run from a directory containing .agentworkforce/relay/connection.json.' ); return 1; } diff --git a/packages/cli/src/cli/lib/attach.ts b/packages/cli/src/cli/lib/attach.ts index 80e802896..c3a041208 100644 --- a/packages/cli/src/cli/lib/attach.ts +++ b/packages/cli/src/cli/lib/attach.ts @@ -155,7 +155,7 @@ export function prepareAttachTarget( if (!connection) { deps.error( 'Error: could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, ' + - 'or run from a directory containing .agent-relay/connection.json.' + 'or run from a directory containing .agentworkforce/relay/connection.json.' ); return null; } diff --git a/packages/cli/src/cli/lib/broker-connection.test.ts b/packages/cli/src/cli/lib/broker-connection.test.ts index 41fece78c..e78330705 100644 --- a/packages/cli/src/cli/lib/broker-connection.test.ts +++ b/packages/cli/src/cli/lib/broker-connection.test.ts @@ -5,7 +5,7 @@ import { resolveBrokerConnection, toWsUrl, type BrokerConnectionDeps } from './b function makeDeps(overrides: Partial = {}): BrokerConnectionDeps { return { readConnectionFile: vi.fn(() => null), - getDefaultStateDir: vi.fn(() => '/tmp/fake/.agent-relay'), + getDefaultStateDir: vi.fn(() => '/tmp/fake/.agentworkforce/relay'), env: {}, ...overrides, }; diff --git a/packages/cli/src/cli/lib/broker-connection.ts b/packages/cli/src/cli/lib/broker-connection.ts index d837dbfd7..dcd7c3a08 100644 --- a/packages/cli/src/cli/lib/broker-connection.ts +++ b/packages/cli/src/cli/lib/broker-connection.ts @@ -7,7 +7,7 @@ * * 1. `--broker-url` / `--api-key` CLI flags * 2. `RELAY_BROKER_URL` / `RELAY_BROKER_API_KEY` environment variables - * 3. `/connection.json` (default `.agent-relay/connection.json`) + * 3. `/connection.json` (default `.agentworkforce/relay/connection.json`) */ import fs from 'node:fs'; @@ -46,10 +46,10 @@ export function readConnectionFileFromDisk(stateDir: string): unknown { } } -/** Default state-directory: `.agent-relay/` under the resolved project root. */ +/** Default state-directory: `.agentworkforce/relay/` under the resolved project root. */ export function defaultStateDir(): string { const projectRoot = getProjectPaths().projectRoot; - return path.join(projectRoot, '.agent-relay'); + return path.join(projectRoot, '.agentworkforce/relay'); } function isStringObject(value: unknown): value is Record { diff --git a/packages/cli/src/cli/lib/broker-lifecycle.ts b/packages/cli/src/cli/lib/broker-lifecycle.ts index 198e3494f..a99679543 100644 --- a/packages/cli/src/cli/lib/broker-lifecycle.ts +++ b/packages/cli/src/cli/lib/broker-lifecycle.ts @@ -491,7 +491,7 @@ async function recoverHalfStartedBroker( if (!stopped) { deps.error( `Failed to stop half-started broker process (pid: ${readiness.conn.pid}). ` + - 'Run `agent-relay down --force` to retry cleanup, or remove `.agent-relay/` after stopping the process.' + 'Run `agent-relay down --force` to retry cleanup, or remove `.agentworkforce/relay/` after stopping the process.' ); return 'blocked'; } @@ -504,7 +504,7 @@ async function recoverHalfStartedBroker( if (orphanCleanup.killedCount < orphanCleanup.matchedCount) { deps.error( 'Failed to stop all half-started broker processes. ' + - 'Run `agent-relay down --force` to retry cleanup, or remove `.agent-relay/` after stopping the processes.' + 'Run `agent-relay down --force` to retry cleanup, or remove `.agentworkforce/relay/` after stopping the processes.' ); return 'blocked'; } @@ -724,8 +724,7 @@ function resolveDashboardStaticDir(dashboardBinary: string | null, deps: CoreDep // Standalone installs download UI assets to ~/.relay/dashboard/out. const standaloneDashboardOutDir = path.join(homeDir, '.relay', 'dashboard', 'out'); - const legacyDashboardOutDir = path.join(homeDir, '.agent-relay', 'dashboard', 'out'); - return pickDashboardStaticDir([standaloneDashboardOutDir, legacyDashboardOutDir], deps); + return pickDashboardStaticDir([standaloneDashboardOutDir], deps); } function normalizeLocalhostRelayUrl(relayUrl: string): string { @@ -1301,7 +1300,7 @@ export async function runUpCommand(options: UpOptions, deps: CoreDependencies): if (!stopped) { deps.error( `Failed to stop half-started broker process (pid: ${cleanupPid}). ` + - 'Run `agent-relay down --force` to retry cleanup, or remove `.agent-relay/` after stopping the process.' + 'Run `agent-relay down --force` to retry cleanup, or remove `.agentworkforce/relay/` after stopping the process.' ); } } @@ -1471,7 +1470,7 @@ export async function runUpCommand(options: UpOptions, deps: CoreDependencies): } // Kill any orphaned broker processes for this project that lost their PID - // files (e.g. user deleted .agent-relay/ while broker was running). + // files (e.g. user deleted .agentworkforce/relay/ while broker was running). await killOrphanedBrokerProcesses(paths.projectRoot, deps); const started = await startBrokerWithPortFallback(paths, dashboardPort, deps, options.brokerName); diff --git a/packages/cli/src/cli/lib/core-maintenance.ts b/packages/cli/src/cli/lib/core-maintenance.ts index e28c53888..2dc969be8 100644 --- a/packages/cli/src/cli/lib/core-maintenance.ts +++ b/packages/cli/src/cli/lib/core-maintenance.ts @@ -11,6 +11,7 @@ const MCP_CONFIG_FILE = '.mcp.json'; const RELAYCAST_SERVER_KEY = 'relaycast'; const ZED_SETTINGS_PATH = path.join('.config', 'zed', 'settings.json'); const DEFAULT_ZED_SERVER_NAME = 'Agent Relay'; +const INSTALL_DIR_NAMES = ['.agentworkforce/relay', '.agent-relay'] as const; function toErrorMessage(err: unknown): string { return err instanceof Error ? err.message : String(err); @@ -256,12 +257,15 @@ export async function runUninstallCommand( // --- Dashboard static assets removal --- const homeDir = os.homedir(); - for (const assetPath of [ + const dashboardAssetPaths = [ path.join(homeDir, '.relay', 'dashboard', 'out'), path.join(homeDir, '.relay', 'dashboard', '.version'), - path.join(homeDir, '.agent-relay', 'dashboard', 'out'), - path.join(homeDir, '.agent-relay', 'dashboard', '.version'), - ]) { + ...INSTALL_DIR_NAMES.flatMap((installDir) => [ + path.join(homeDir, installDir, 'dashboard', 'out'), + path.join(homeDir, installDir, 'dashboard', '.version'), + ]), + ]; + for (const assetPath of dashboardAssetPaths) { if (deps.fs.existsSync(assetPath)) { if (isDryRun) { deps.log(`[dry-run] Would remove dashboard asset path: ${assetPath}`); @@ -284,7 +288,7 @@ export async function runUninstallCommand( // --- Binary removal (standalone binaries + npm packages) --- const standaloneBinDir = path.join(homeDir, '.local', 'bin'); - const installBinDir = path.join(homeDir, '.agent-relay', 'bin'); + const installBinDirs = INSTALL_DIR_NAMES.map((installDir) => path.join(homeDir, installDir, 'bin')); // Remove standalone binaries from ~/.local/bin for (const binaryName of ['agent-relay', 'relay-dashboard-server', 'relay-acp']) { @@ -318,8 +322,11 @@ export async function runUninstallCommand( } } - // Remove broker binary from ~/.agent-relay/bin/ (not the parent dir which stores global data) - if (deps.fs.existsSync(installBinDir)) { + // Remove installer bin dirs without deleting the parent data directories. + for (const installBinDir of installBinDirs) { + if (!deps.fs.existsSync(installBinDir)) { + continue; + } if (isDryRun) { deps.log(`[dry-run] Would remove directory: ${installBinDir}`); } else { diff --git a/packages/cli/src/cli/lib/project-broker-client.test.ts b/packages/cli/src/cli/lib/project-broker-client.test.ts index f62c58abb..8de73923d 100644 --- a/packages/cli/src/cli/lib/project-broker-client.test.ts +++ b/packages/cli/src/cli/lib/project-broker-client.test.ts @@ -22,11 +22,13 @@ describe('project broker client resolution', () => { const { connectProjectBrokerClient, getProjectBrokerConnectionPath } = await import('./project-broker-client.js'); - expect(getProjectBrokerConnectionPath('/tmp/project')).toBe('/tmp/project/.agent-relay/connection.json'); + expect(getProjectBrokerConnectionPath('/tmp/project')).toBe( + '/tmp/project/.agentworkforce/relay/connection.json' + ); expect(connectProjectBrokerClient('/tmp/project')).toBe(client); expect(connectMock).toHaveBeenCalledWith({ cwd: '/tmp/project', - connectionPath: '/tmp/project/.agent-relay/connection.json', + connectionPath: '/tmp/project/.agentworkforce/relay/connection.json', }); }); }); diff --git a/packages/cli/src/cli/lib/project-broker-client.ts b/packages/cli/src/cli/lib/project-broker-client.ts index e2eeed1c8..5117732d0 100644 --- a/packages/cli/src/cli/lib/project-broker-client.ts +++ b/packages/cli/src/cli/lib/project-broker-client.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import { HarnessDriverClient } from '@agent-relay/harness-driver'; export function getProjectBrokerConnectionPath(projectRoot: string): string { - return path.join(projectRoot, '.agent-relay', 'connection.json'); + return path.join(projectRoot, '.agentworkforce/relay', 'connection.json'); } export function connectProjectBrokerClient(projectRoot: string): HarnessDriverClient { diff --git a/packages/cli/src/cli/lib/workspace-store.ts b/packages/cli/src/cli/lib/workspace-store.ts index f38c7fcee..8e95bc4db 100644 --- a/packages/cli/src/cli/lib/workspace-store.ts +++ b/packages/cli/src/cli/lib/workspace-store.ts @@ -15,7 +15,7 @@ export interface WorkspaceStore { const RESERVED_WORKSPACE_NAMES = new Set(['__proto__', 'prototype', 'constructor']); export function workspaceStorePath(env: NodeJS.ProcessEnv = process.env): string { - const dir = env.AGENT_RELAY_HOME ?? path.join(os.homedir(), '.agent-relay'); + const dir = env.AGENT_RELAY_HOME ?? path.join(os.homedir(), '.agentworkforce/relay'); return path.join(dir, 'workspaces.json'); } diff --git a/packages/cli/src/cost/tracker.ts b/packages/cli/src/cost/tracker.ts index 68f0e2414..369c62973 100644 --- a/packages/cli/src/cost/tracker.ts +++ b/packages/cli/src/cost/tracker.ts @@ -21,7 +21,7 @@ interface StartedStep { startedAtMs: number; } -const DEFAULT_USAGE_FILE_PATH = path.join(os.homedir(), '.agent-relay', 'usage.jsonl'); +const DEFAULT_USAGE_FILE_PATH = path.join(os.homedir(), '.agentworkforce/relay', 'usage.jsonl'); export class CostTracker { private readonly usageFilePath: string; diff --git a/packages/cloud/src/audit.ts b/packages/cloud/src/audit.ts index 042e04562..bd51f1f63 100644 --- a/packages/cloud/src/audit.ts +++ b/packages/cloud/src/audit.ts @@ -10,7 +10,7 @@ export interface PermissionAuditEntry { type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; -const DEFAULT_PERMISSION_AUDIT_RELATIVE_PATH = path.join('.agent-relay', 'permission-audit.json'); +const DEFAULT_PERMISSION_AUDIT_RELATIVE_PATH = path.join('.agentworkforce/relay', 'permission-audit.json'); function isPlainObject(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); diff --git a/packages/cloud/src/types.ts b/packages/cloud/src/types.ts index b64c3e99c..1febb2745 100644 --- a/packages/cloud/src/types.ts +++ b/packages/cloud/src/types.ts @@ -208,7 +208,7 @@ export type GetPatchesResponse = { export const SUPPORTED_PROVIDERS = ['anthropic', 'openai', 'google', 'cursor', 'opencode', 'droid'] as const; export const REFRESH_WINDOW_MS = 60_000; -export const AUTH_FILE_PATH = path.join(os.homedir(), '.agent-relay', 'cloud-auth.json'); +export const AUTH_FILE_PATH = path.join(os.homedir(), '.agentworkforce/relay', 'cloud-auth.json'); export function defaultApiUrl(): string { return process.env.CLOUD_API_URL?.trim() || 'https://agentrelay.com/cloud'; diff --git a/packages/config/src/bridge-config.ts b/packages/config/src/bridge-config.ts index 481353655..5b5bd1d62 100644 --- a/packages/config/src/bridge-config.ts +++ b/packages/config/src/bridge-config.ts @@ -22,7 +22,7 @@ export interface ProjectConfig { } const CONFIG_PATHS = [ - path.join(os.homedir(), '.agent-relay', 'bridge.json'), + path.join(os.homedir(), '.agentworkforce/relay', 'bridge.json'), path.join(os.homedir(), '.config', 'agent-relay', 'bridge.json'), ]; diff --git a/packages/config/src/project-namespace.ts b/packages/config/src/project-namespace.ts index fcb52a9c3..796b0b3c8 100644 --- a/packages/config/src/project-namespace.ts +++ b/packages/config/src/project-namespace.ts @@ -2,7 +2,7 @@ * Project Namespace Utility * * Generates project-specific paths for agent-relay data storage. - * Data is stored in .agent-relay/ within the project root directory. + * Data is stored in .agentworkforce/relay/ within the project root directory. * This allows multiple projects to use agent-relay simultaneously * without conflicts, and keeps data with the project. */ @@ -10,35 +10,9 @@ import crypto from 'node:crypto'; import path from 'node:path'; import fs from 'node:fs'; -import os from 'node:os'; - -/** - * Get the global base directory for agent-relay data (used for cross-project data). - * Priority: - * 1. AGENT_RELAY_DATA_DIR environment variable - * 2. XDG_DATA_HOME/agent-relay (Linux/macOS standard) - * 3. ~/.agent-relay (fallback) - */ -function getGlobalBaseDir(): string { - // Explicit override - if (process.env.AGENT_RELAY_DATA_DIR) { - return process.env.AGENT_RELAY_DATA_DIR; - } - - // XDG Base Directory Specification - const xdgDataHome = process.env.XDG_DATA_HOME; - if (xdgDataHome) { - return path.join(xdgDataHome, 'agent-relay'); - } - - // Default: ~/.agent-relay - return path.join(os.homedir(), '.agent-relay'); -} - -const GLOBAL_BASE_DIR = getGlobalBaseDir(); /** Directory name within project root */ -const PROJECT_DATA_DIR = '.agent-relay'; +const PROJECT_DATA_DIR = '.agentworkforce/relay'; /** * Generate a short hash of a path for namespacing @@ -65,7 +39,7 @@ export function findProjectRoot(startDir: string = process.cwd()): string { let current = path.resolve(startDir); const root = path.parse(current).root; - const markers = ['.git', 'package.json', 'Cargo.toml', 'go.mod', 'pyproject.toml', '.agent-relay']; + const markers = ['.git', 'package.json', 'Cargo.toml', 'go.mod', 'pyproject.toml', '.agentworkforce/relay']; while (current !== root) { for (const marker of markers) { @@ -101,7 +75,7 @@ export interface ProjectPaths { export function getProjectPaths(projectRoot?: string): ProjectPaths { const root = projectRoot ?? findProjectRoot(); const projectId = hashPath(root); - // Store data in project-local .agent-relay/ directory + // Store data in project-local .agentworkforce/relay/ directory const dataDir = path.join(root, PROJECT_DATA_DIR); return { @@ -115,21 +89,7 @@ export function getProjectPaths(projectRoot?: string): ProjectPaths { } /** - * Get the default paths (for backwards compatibility or explicit global usage) - */ -export function getGlobalPaths(): ProjectPaths { - return { - dataDir: GLOBAL_BASE_DIR, - teamDir: path.join(GLOBAL_BASE_DIR, 'team'), - dbPath: path.join(GLOBAL_BASE_DIR, 'messages.sqlite'), - socketPath: path.join(GLOBAL_BASE_DIR, 'relay.sock'), - projectRoot: process.cwd(), - projectId: 'global', - }; -} - -/** - * Add .agent-relay/ to .git/info/exclude (local per-repo gitignore). + * Add .agentworkforce/relay/ to .git/info/exclude (local per-repo gitignore). * * This file is: * - Per-repository (not shared across clones) @@ -159,15 +119,15 @@ function ensureGitExclude(projectRoot: string): { modified: boolean } { let content = ''; if (fs.existsSync(excludePath)) { content = fs.readFileSync(excludePath, 'utf-8'); - // Check if .agent-relay is already in exclude + // Check if .agentworkforce/relay is already in exclude const lines = content.split('\n'); const hasEntry = lines.some((line) => { const trimmed = line.trim(); return ( - trimmed === '.agent-relay' || - trimmed === '.agent-relay/' || - trimmed === '/.agent-relay' || - trimmed === '/.agent-relay/' + trimmed === '.agentworkforce/relay' || + trimmed === '.agentworkforce/relay/' || + trimmed === '/.agentworkforce/relay' || + trimmed === '/.agentworkforce/relay/' ); }); if (hasEntry) { @@ -175,8 +135,8 @@ function ensureGitExclude(projectRoot: string): { modified: boolean } { } } - // Add .agent-relay/ to exclude - const newEntry = '.agent-relay/'; + // Add .agentworkforce/relay/ to exclude + const newEntry = '.agentworkforce/relay/'; const newContent = content.endsWith('\n') || content === '' ? `${content}${newEntry}\n` : `${content}\n${newEntry}\n`; @@ -203,7 +163,7 @@ export function ensureProjectDir(projectRoot?: string): ProjectPaths { // Add to .git/info/exclude (local per-repo gitignore, not committed) const excludeResult = ensureGitExclude(paths.projectRoot); if (excludeResult.modified) { - console.log('[agent-relay] Added .agent-relay/ to .git/info/exclude'); + console.log('[agent-relay] Added .agentworkforce/relay/ to .git/info/exclude'); } } if (!fs.existsSync(paths.teamDir)) { @@ -228,39 +188,6 @@ export function ensureProjectDir(projectRoot?: string): ProjectPaths { return paths; } -/** - * List all known projects (scans global base dir for legacy data) - * Note: With project-local storage, this only finds projects that - * used the old global storage location. - */ -export function listProjects(): Array<{ projectId: string; projectRoot: string; dataDir: string }> { - if (!fs.existsSync(GLOBAL_BASE_DIR)) { - return []; - } - - const projects: Array<{ projectId: string; projectRoot: string; dataDir: string }> = []; - - for (const entry of fs.readdirSync(GLOBAL_BASE_DIR)) { - const dataDir = path.join(GLOBAL_BASE_DIR, entry); - const markerPath = path.join(dataDir, '.project'); - - if (fs.existsSync(markerPath)) { - try { - const info = JSON.parse(fs.readFileSync(markerPath, 'utf-8')); - projects.push({ - projectId: entry, - projectRoot: info.projectRoot, - dataDir, - }); - } catch { - // Invalid marker, skip - } - } - } - - return projects; -} - /** * Detect the actual workspace directory for cloud deployments. * diff --git a/packages/config/src/relay-file-writer.ts b/packages/config/src/relay-file-writer.ts index 7beb322a2..5df7f9d27 100644 --- a/packages/config/src/relay-file-writer.ts +++ b/packages/config/src/relay-file-writer.ts @@ -5,7 +5,7 @@ * to the file system with a structured directory layout. * * Directory structure (project-local): - * {projectRoot}/.agent-relay/ + * {projectRoot}/.agentworkforce/relay/ * outbox/{agent-name}/ # Agent outbox messages * attachments/{agent-name}/{ts}/ # Attachments organized by timestamp * meta/ # Configuration and state files @@ -37,8 +37,6 @@ export interface RelayPaths { attachmentsDir: string; /** Meta directory for configuration/state */ metaDir: string; - /** Legacy outbox path (for backward compatibility) */ - legacyOutboxDir: string; } export interface AgentPaths extends RelayPaths { @@ -76,7 +74,6 @@ const MAX_SOCKET_PATH_LENGTH = 107; const DEFAULT_OUTBOX_DIR = 'outbox'; const DEFAULT_ATTACHMENTS_DIR = 'attachments'; const DEFAULT_META_DIR = 'meta'; -const LEGACY_OUTBOX_BASE = '/tmp/relay-outbox'; // ============================================================================ // Path Resolution @@ -93,8 +90,8 @@ function hashWorkspaceId(workspaceId: string): string { * Get the base directory for relay data. * Priority: * 1. AGENT_RELAY_DATA_DIR environment variable - * 2. XDG_DATA_HOME/agent-relay (Linux/macOS standard) - * 3. ~/.agent-relay (fallback) + * 2. XDG_DATA_HOME/agentworkforce/relay (Linux/macOS standard) + * 3. ~/.agentworkforce/relay (fallback) */ function getBaseDir(): string { // Explicit override @@ -105,11 +102,11 @@ function getBaseDir(): string { // XDG Base Directory Specification const xdgDataHome = process.env.XDG_DATA_HOME; if (xdgDataHome) { - return path.join(xdgDataHome, 'agent-relay'); + return path.join(xdgDataHome, 'agentworkforce', 'relay'); } - // Default: ~/.agent-relay - return path.join(os.homedir(), '.agent-relay'); + // Default: ~/.agentworkforce/relay + return path.join(os.homedir(), '.agentworkforce/relay'); } /** @@ -132,13 +129,12 @@ function getWorkspacePaths(workspaceId: string): RelayPaths { outboxDir: path.join(workspaceDir, DEFAULT_OUTBOX_DIR), attachmentsDir: path.join(workspaceDir, DEFAULT_ATTACHMENTS_DIR), metaDir: path.join(workspaceDir, DEFAULT_META_DIR), - legacyOutboxDir: LEGACY_OUTBOX_BASE, }; } /** * Get local (non-workspace) relay paths. - * Uses ~/.agent-relay for persistent storage. + * Uses ~/.agentworkforce/relay for persistent storage. */ function getLocalPaths(): RelayPaths { const baseDir = getBaseDir(); @@ -148,7 +144,6 @@ function getLocalPaths(): RelayPaths { outboxDir: path.join(baseDir, DEFAULT_OUTBOX_DIR), attachmentsDir: path.join(baseDir, DEFAULT_ATTACHMENTS_DIR), metaDir: path.join(baseDir, DEFAULT_META_DIR), - legacyOutboxDir: LEGACY_OUTBOX_BASE, }; } @@ -201,7 +196,7 @@ export class RelayFileWriter { /** * Get the outbox path that agents should write to. - * Always returns the canonical ~/.agent-relay path. + * Always returns the canonical ~/.agentworkforce/relay path. * In workspace mode, this path is symlinked to the actual workspace path. */ getOutboxPath(): string { @@ -209,71 +204,14 @@ export class RelayFileWriter { return this.paths.agentOutbox; } - /** - * Get the legacy outbox path (for backwards compatibility symlinks). - */ - getLegacyOutboxPath(): string { - return path.join(this.paths.legacyOutboxDir, this.agentName); - } - /** * Ensure all necessary directories exist for this agent. - * In workspace mode, also sets up symlinks from canonical path to workspace path. */ async ensureDirectories(): Promise { // Create agent-specific directories at canonical path await fs.promises.mkdir(this.paths.agentOutbox, { recursive: true }); await fs.promises.mkdir(this.paths.agentAttachments, { recursive: true }); await fs.promises.mkdir(this.paths.metaDir, { recursive: true }); - - // In workspace mode, set up symlinks so canonical path routes to workspace - // (Note: The orchestrator handles symlink setup, this is just for standalone use) - if (this.paths.isWorkspace) { - await this.setupWorkspaceSymlinks(); - } - } - - /** - * Set up symlinks for workspace mode. - * Creates symlink from legacy /tmp/relay-outbox path to workspace path. - * (The orchestrator creates the canonical→workspace symlink) - */ - private async setupWorkspaceSymlinks(): Promise { - const legacyPath = path.join(this.paths.legacyOutboxDir, this.agentName); - - try { - await this.createSymlinkSafe(legacyPath, this.paths.agentOutbox); - } catch (err: any) { - console.error(`[relay-file-writer] Failed to setup workspace symlinks: ${err.message}`); - } - } - - /** - * Helper to create a symlink, cleaning up existing path first. - */ - private async createSymlinkSafe(linkPath: string, targetPath: string): Promise { - const linkParent = path.dirname(linkPath); - await fs.promises.mkdir(linkParent, { recursive: true }); - - try { - const stats = await fs.promises.lstat(linkPath); - if (stats.isSymbolicLink()) { - const target = await fs.promises.readlink(linkPath); - if (target === targetPath) { - return; // Already correctly configured - } - await fs.promises.unlink(linkPath); - } else if (stats.isDirectory()) { - await fs.promises.rm(linkPath, { recursive: true, force: true }); - } - } catch (err: any) { - if (err.code !== 'ENOENT') { - throw err; - } - // Path doesn't exist - proceed to create symlink - } - - await fs.promises.symlink(targetPath, linkPath); } /** @@ -481,7 +419,7 @@ export function getBaseRelayPaths(workspaceId?: string): RelayPaths { */ export function getAgentOutboxTemplate(_agentNameVar = '$AGENT_RELAY_NAME'): string { // Agents should use $AGENT_RELAY_OUTBOX which is set by the orchestrator - // This handles both local (project-local .agent-relay/) and cloud (workspace) modes + // This handles both local (project-local .agentworkforce/relay/) and cloud (workspace) modes return '$AGENT_RELAY_OUTBOX'; } @@ -494,7 +432,6 @@ export async function ensureBaseDirectories(workspaceId?: string): Promise = { }; /** - * Possible locations for .agent-relay.json (in order of precedence) + * Path to the shadow config file (`.agentworkforce/relay/config.json`) */ -function getConfigPaths(projectRoot: string): string[] { - return [path.join(projectRoot, '.agent-relay', 'config.json'), path.join(projectRoot, '.agent-relay.json')]; +function getConfigFilePath(projectRoot: string): string { + return path.join(projectRoot, '.agentworkforce/relay', 'config.json'); } /** @@ -98,19 +96,17 @@ function getConfigPaths(projectRoot: string): string[] { * Returns null if no config found */ export function loadAgentRelayConfig(projectRoot: string): AgentRelayConfig | null { - const configPaths = getConfigPaths(projectRoot); - - for (const configPath of configPaths) { - if (fs.existsSync(configPath)) { - try { - const content = fs.readFileSync(configPath, 'utf-8'); - const config = JSON.parse(content) as AgentRelayConfig; - - console.log(`[shadow-config] Loaded config from ${configPath}`); - return config; - } catch (err) { - console.error(`[shadow-config] Failed to parse ${configPath}:`, err); - } + const configPath = getConfigFilePath(projectRoot); + + if (fs.existsSync(configPath)) { + try { + const content = fs.readFileSync(configPath, 'utf-8'); + const config = JSON.parse(content) as AgentRelayConfig; + + console.log(`[shadow-config] Loaded config from ${configPath}`); + return config; + } catch (err) { + console.error(`[shadow-config] Failed to parse ${configPath}:`, err); } } @@ -199,11 +195,6 @@ export function hasShadowConfig(projectRoot: string): boolean { * Get the config file path that would be used */ export function getConfigPath(projectRoot: string): string | null { - const configPaths = getConfigPaths(projectRoot); - for (const configPath of configPaths) { - if (fs.existsSync(configPath)) { - return configPath; - } - } - return null; + const configPath = getConfigFilePath(projectRoot); + return fs.existsSync(configPath) ? configPath : null; } diff --git a/packages/config/src/teams-config.ts b/packages/config/src/teams-config.ts index ed393d479..5628ca087 100644 --- a/packages/config/src/teams-config.ts +++ b/packages/config/src/teams-config.ts @@ -4,7 +4,7 @@ * * teams.json can be placed in: * - Project root: ./teams.json - * - Agent-relay dir: ./.agent-relay/teams.json + * - Agent-relay dir: ./.agentworkforce/relay/teams.json */ import fs from 'node:fs'; @@ -46,11 +46,14 @@ export interface TeamsConfig { * Possible locations for teams.json (in order of precedence) */ function getTeamsConfigPaths(projectRoot: string): string[] { - return [path.join(projectRoot, '.agent-relay', 'teams.json'), path.join(projectRoot, 'teams.json')]; + return [ + path.join(projectRoot, '.agentworkforce/relay', 'teams.json'), + path.join(projectRoot, 'teams.json'), + ]; } /** - * Load teams.json from project root or .agent-relay directory + * Load teams.json from project root or .agentworkforce/relay directory * Returns null if no config found. * Results are cached and only reloaded when the file changes. */ diff --git a/packages/harness-driver/src/client.ts b/packages/harness-driver/src/client.ts index 6f0711b3e..b4b3321fc 100644 --- a/packages/harness-driver/src/client.ts +++ b/packages/harness-driver/src/client.ts @@ -370,7 +370,7 @@ export class HarnessDriverClient { /** * Connect to an already-running broker by reading its connection file. * - * The broker writes `connection.json` to its data directory ({cwd}/.agent-relay/ + * The broker writes `connection.json` to its data directory ({cwd}/.agentworkforce/relay/ * in persist mode). This method reads that file to get the URL and API key. * * @param cwd — project directory (default: process.cwd()) @@ -384,7 +384,8 @@ export class HarnessDriverClient { const cwd = options?.cwd ?? process.cwd(); const stateDir = process.env.AGENT_RELAY_STATE_DIR; const connPath = - options?.connectionPath ?? path.join(stateDir ?? path.join(cwd, '.agent-relay'), 'connection.json'); + options?.connectionPath ?? + path.join(stateDir ?? path.join(cwd, '.agentworkforce/relay'), 'connection.json'); if (!existsSync(connPath)) { throw new Error( diff --git a/packages/sdk/.gitignore b/packages/sdk/.gitignore index 411742ac6..aa98419ae 100644 --- a/packages/sdk/.gitignore +++ b/packages/sdk/.gitignore @@ -1 +1 @@ -.agent-relay/ +.agentworkforce/relay/ diff --git a/packages/telemetry/src/config.ts b/packages/telemetry/src/config.ts index 95ed342ed..1cfffa960 100644 --- a/packages/telemetry/src/config.ts +++ b/packages/telemetry/src/config.ts @@ -1,5 +1,5 @@ /** - * Telemetry preference storage (~/.agent-relay/telemetry.json) + * Telemetry preference storage (~/.agentworkforce/relay/telemetry.json) */ import fs from 'node:fs'; @@ -17,7 +17,7 @@ export interface TelemetryPrefs { } export function getPrefsPath(): string { - const configDir = process.env.AGENT_RELAY_DATA_DIR || path.join(os.homedir(), '.agent-relay'); + const configDir = process.env.AGENT_RELAY_DATA_DIR || path.join(os.homedir(), '.agentworkforce/relay'); return path.join(configDir, 'telemetry.json'); } @@ -75,7 +75,7 @@ export function isDisabledByEnv(): boolean { * Check if telemetry is enabled. * Order of precedence: * 1. AGENT_RELAY_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1 -> disabled - * 2. ~/.agent-relay/telemetry.json -> use stored pref + * 2. ~/.agentworkforce/relay/telemetry.json -> use stored pref * 3. Default -> enabled */ export function isTelemetryEnabled(): boolean { diff --git a/packages/telemetry/src/machine-id.ts b/packages/telemetry/src/machine-id.ts index 12d8136c8..3a5dc6a94 100644 --- a/packages/telemetry/src/machine-id.ts +++ b/packages/telemetry/src/machine-id.ts @@ -1,6 +1,6 @@ /** * Machine ID utilities for anonymous user identification. - * Uses existing machine-id file at ~/.local/share/agent-relay/machine-id + * Uses existing machine-id file at ~/.local/share/agentworkforce/relay/machine-id */ import { createHash, randomBytes } from 'node:crypto'; @@ -10,7 +10,7 @@ import os from 'node:os'; export function getMachineIdPath(): string { const dataDir = - process.env.AGENT_RELAY_DATA_DIR || path.join(os.homedir(), '.local', 'share', 'agent-relay'); + process.env.AGENT_RELAY_DATA_DIR || path.join(os.homedir(), '.local', 'share', 'agentworkforce', 'relay'); return path.join(dataDir, 'machine-id'); } diff --git a/packages/utils/src/discovery.ts b/packages/utils/src/discovery.ts index 73ca6fde8..499a1de49 100644 --- a/packages/utils/src/discovery.ts +++ b/packages/utils/src/discovery.ts @@ -116,11 +116,11 @@ function getDataDir(): string { const platform = process.platform; if (platform === 'darwin') { - return join(homedir(), 'Library', 'Application Support', 'agent-relay'); + return join(homedir(), 'Library', 'Application Support', 'agentworkforce', 'relay'); } else if (platform === 'win32') { - return join(process.env.APPDATA || homedir(), 'agent-relay'); + return join(process.env.APPDATA || homedir(), 'agentworkforce', 'relay'); } else { - return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'agent-relay'); + return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'agentworkforce', 'relay'); } } @@ -197,7 +197,7 @@ export function discoverSocket(options: CloudConnectionOptions = {}): DiscoveryR }; } - // 4. Project-local socket (created by broker in project's .agent-relay directory) + // 4. Project-local socket (created by broker in project's .agentworkforce/relay directory) // This is the primary path for local development // First try cwd, then scan up to find project root const projectRoot = findProjectRoot(process.cwd()); @@ -207,11 +207,11 @@ export function discoverSocket(options: CloudConnectionOptions = {}): DiscoveryR } for (const dir of searchDirs) { - const projectLocalSocket = join(dir, '.agent-relay', 'relay.sock'); + const projectLocalSocket = join(dir, '.agentworkforce/relay', 'relay.sock'); if (existsSync(projectLocalSocket)) { // Read project ID from marker file if available let projectId = 'local'; - const markerPath = join(dir, '.agent-relay', '.project'); + const markerPath = join(dir, '.agentworkforce/relay', '.project'); if (existsSync(markerPath)) { try { const marker = JSON.parse(readFileSync(markerPath, 'utf-8')); @@ -279,10 +279,10 @@ export function discoverSocket(options: CloudConnectionOptions = {}): DiscoveryR } } - // 6. Check active broker marker file (~/.agent-relay/active-broker.json) - // This allows discovery when cwd doesn't contain .agent-relay/ + // 6. Check active broker marker file (~/.agentworkforce/relay/active-broker.json) + // This allows discovery when cwd doesn't contain .agentworkforce/relay/ try { - const markerCandidates = [join(homedir(), '.agent-relay', 'active-broker.json')]; + const markerCandidates = [join(homedir(), '.agentworkforce/relay', 'active-broker.json')]; for (const markerPath of markerCandidates) { if (!existsSync(markerPath)) { continue; @@ -427,7 +427,7 @@ export function getCloudEnvironmentSummary(): Record * * Priority order: * 1. RELAY_AGENT_NAME environment variable (explicit) - * 2. Identity file in .agent-relay directory (written by wrapper) + * 2. Identity file in .agentworkforce/relay directory (written by wrapper) * 3. Scan outbox directories to find agent's outbox * * @param _discovery - Optional discovery result (reserved for future use) @@ -440,7 +440,7 @@ export function discoverAgentName(_discovery?: DiscoveryResult | null): string | return envName; } - // 2. Identity file in .agent-relay directory + // 2. Identity file in .agentworkforce/relay directory // The wrapper creates this file with the agent name const projectRoot = findProjectRoot(process.cwd()); const searchDirs = [process.cwd()]; @@ -449,7 +449,7 @@ export function discoverAgentName(_discovery?: DiscoveryResult | null): string | } for (const dir of searchDirs) { - const relayDir = join(dir, '.agent-relay'); + const relayDir = join(dir, '.agentworkforce/relay'); if (!existsSync(relayDir)) continue; // First check for per-process identity files @@ -524,7 +524,7 @@ export function discoverAgentName(_discovery?: DiscoveryResult | null): string | // 3. Check outbox directories for a match // If only one agent's outbox exists, assume we're that agent for (const dir of searchDirs) { - const outboxDir = join(dir, '.agent-relay', 'outbox'); + const outboxDir = join(dir, '.agentworkforce/relay', 'outbox'); if (existsSync(outboxDir)) { try { const agents = readdirSync(outboxDir, { withFileTypes: true }) diff --git a/packages/utils/src/relay-pty-path.test.ts b/packages/utils/src/relay-pty-path.test.ts index 86dff51e7..aa1f7f961 100644 --- a/packages/utils/src/relay-pty-path.test.ts +++ b/packages/utils/src/relay-pty-path.test.ts @@ -250,17 +250,17 @@ describe('findRelayPtyBinary - search path verification', () => { }); describe('Bash installer (curl | bash)', () => { - // install.sh places binary at ~/.agent-relay/bin/relay-pty + // install.sh places binary at ~/.agentworkforce/relay/bin/relay-pty // and optionally at ~/.local/bin/relay-pty - it('should include ~/.agent-relay/bin/ in search paths', () => { + it('should include ~/.agentworkforce/relay/bin/ in search paths', () => { const callerDirname = '/some/random/path'; findRelayPtyBinary(callerDirname); const paths = getLastSearchPaths(); const home = process.env.HOME || ''; - expect(paths.some((p) => p.startsWith(`${home}/.agent-relay/bin/`))).toBe(true); + expect(paths.some((p) => p.startsWith(`${home}/.agentworkforce/relay/bin/`))).toBe(true); }); it('should include ~/.local/bin/ in search paths', () => { diff --git a/packages/utils/src/relay-pty-path.ts b/packages/utils/src/relay-pty-path.ts index f290ec94a..6657e6077 100644 --- a/packages/utils/src/relay-pty-path.ts +++ b/packages/utils/src/relay-pty-path.ts @@ -209,11 +209,11 @@ export function findRelayPtyBinary(callerDirname: string): string | null { } // Bash installer locations (curl | bash install method) - // install.sh puts relay-pty at $INSTALL_DIR/bin/ (default: ~/.agent-relay/bin/) + // install.sh puts relay-pty at $INSTALL_DIR/bin/ (default: ~/.agentworkforce/relay/bin/) const bashInstallerDir = process.env.AGENT_RELAY_INSTALL_DIR ? path.join(process.env.AGENT_RELAY_INSTALL_DIR, 'bin') : home - ? path.join(home, '.agent-relay', 'bin') + ? path.join(home, '.agentworkforce/relay', 'bin') : null; const bashInstallerBinDir = process.env.AGENT_RELAY_BIN_DIR || (home ? path.join(home, '.local', 'bin') : null); @@ -246,7 +246,7 @@ export function findRelayPtyBinary(callerDirname: string): string | null { } // Bash installer paths (curl | bash install method) - // install.sh downloads relay-pty to ~/.agent-relay/bin/relay-pty + // install.sh downloads relay-pty to ~/.agentworkforce/relay/bin/relay-pty if (bashInstallerDir) { if (platformBinary) { candidates.push(path.join(bashInstallerDir, platformBinary)); diff --git a/packages/utils/src/update-checker.test.ts b/packages/utils/src/update-checker.test.ts index fb3a599e7..6ba44be85 100644 --- a/packages/utils/src/update-checker.test.ts +++ b/packages/utils/src/update-checker.test.ts @@ -158,12 +158,12 @@ describe('update-checker', () => { }); describe('cache path', () => { - it('cache is stored in ~/.agent-relay directory', () => { - const expectedDir = path.join(os.homedir(), '.agent-relay'); + it('cache is stored in ~/.agentworkforce/relay directory', () => { + const expectedDir = path.join(os.homedir(), '.agentworkforce/relay'); const expectedFile = path.join(expectedDir, 'update-cache.json'); // We can verify the path structure is correct - expect(expectedFile).toContain('.agent-relay'); + expect(expectedFile).toContain('.agentworkforce/relay'); expect(expectedFile).toContain('update-cache.json'); }); }); diff --git a/packages/utils/src/update-checker.ts b/packages/utils/src/update-checker.ts index e6dcf6a58..d6c511de0 100644 --- a/packages/utils/src/update-checker.ts +++ b/packages/utils/src/update-checker.ts @@ -23,7 +23,7 @@ interface UpdateCache { } function getCachePath(): string { - const cacheDir = path.join(os.homedir(), '.agent-relay'); + const cacheDir = path.join(os.homedir(), '.agentworkforce/relay'); return path.join(cacheDir, 'update-cache.json'); } diff --git a/scripts/e2e-test.sh b/scripts/e2e-test.sh index c8925a859..b755ef710 100755 --- a/scripts/e2e-test.sh +++ b/scripts/e2e-test.sh @@ -161,7 +161,7 @@ fi sleep 1 # Start broker+dashboard in background, redirect output to log file -DAEMON_LOG="$PROJECT_DIR/.agent-relay/e2e-daemon.log" +DAEMON_LOG="$PROJECT_DIR/.agentworkforce/relay/e2e-daemon.log" mkdir -p "$(dirname "$DAEMON_LOG")" "$CLI_CMD" local up --port "$DASHBOARD_PORT" > "$DAEMON_LOG" 2>&1 & DAEMON_PID=$! diff --git a/scripts/test-spawn-refactor.sh b/scripts/test-spawn-refactor.sh index fb5eaec9d..24d3e3666 100755 --- a/scripts/test-spawn-refactor.sh +++ b/scripts/test-spawn-refactor.sh @@ -305,10 +305,10 @@ lsof -ti:$DASHBOARD_PORT | xargs kill -9 2>/dev/null || true sleep 1 # Clean stale socket -rm -f "$PROJECT_DIR/.agent-relay/relay.sock" 2>/dev/null || true +rm -f "$PROJECT_DIR/.agentworkforce/relay/relay.sock" 2>/dev/null || true # Start daemon with local build -DAEMON_LOG="$PROJECT_DIR/.agent-relay/test-spawn-refactor.log" +DAEMON_LOG="$PROJECT_DIR/.agentworkforce/relay/test-spawn-refactor.log" mkdir -p "$(dirname "$DAEMON_LOG")" "$CLI_CMD" up --dashboard --port "$DASHBOARD_PORT" > "$DAEMON_LOG" 2>&1 & DAEMON_PID=$! @@ -378,7 +378,7 @@ else fi # Test daemon socket is functional (CLI uses socket, which requires the daemon's PID file) -if [ -S "$PROJECT_DIR/.agent-relay/relay.sock" ]; then +if [ -S "$PROJECT_DIR/.agentworkforce/relay/relay.sock" ]; then pass "Daemon socket exists" TOTAL=$((TOTAL + 1)) else @@ -386,7 +386,7 @@ else fi # Verify daemon PID file -if [ -f "$PROJECT_DIR/.agent-relay/relay.sock.pid" ]; then +if [ -f "$PROJECT_DIR/.agentworkforce/relay/relay.sock.pid" ]; then pass "Daemon PID file exists" TOTAL=$((TOTAL + 1)) else diff --git a/tests/integration/broker/continuity.test.ts b/tests/integration/broker/continuity.test.ts index 8290061bd..4e2f88ddf 100644 --- a/tests/integration/broker/continuity.test.ts +++ b/tests/integration/broker/continuity.test.ts @@ -30,9 +30,9 @@ function skipIfMissing(t: TestContext): boolean { return false; } -/** Resolve the .agent-relay/continuity directory relative to cwd. */ +/** Resolve the .agentworkforce/relay/continuity directory relative to cwd. */ function continuityDir(cwd: string): string { - return path.resolve(cwd, '.agent-relay', 'continuity'); + return path.resolve(cwd, '.agentworkforce/relay', 'continuity'); } /** Read a continuity JSON file for a given agent name. */ diff --git a/tests/integration/broker/lockfile.test.ts b/tests/integration/broker/lockfile.test.ts index c28e1bac4..0db413343 100644 --- a/tests/integration/broker/lockfile.test.ts +++ b/tests/integration/broker/lockfile.test.ts @@ -45,7 +45,7 @@ function brokerNameForDir(cwd: string, base: 'locktest' | 'inittest' = 'locktest /** * Spawn a broker `init` process in the given cwd. - * Returns the child process. The broker will create .agent-relay/ in cwd. + * Returns the child process. The broker will create .agentworkforce/relay/ in cwd. */ function spawnBroker(cwd: string, env: NodeJS.ProcessEnv): ChildProcess { const bin = brokerBin(); @@ -119,7 +119,7 @@ async function waitForPidFile( timeoutMs = 15_000, brokerName = brokerNameForDir(cwd, 'locktest') ): Promise { - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerName)); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerName)); const start = Date.now(); while (Date.now() - start < timeoutMs) { if (fs.existsSync(pidPath)) { @@ -186,7 +186,7 @@ async function waitForPidFileRemoved( timeoutMs = 10_000, brokerName = brokerNameForDir(cwd, 'locktest') ): Promise { - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerName)); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerName)); const start = Date.now(); while (Date.now() - start < timeoutMs) { if (!fs.existsSync(pidPath)) return; @@ -205,7 +205,7 @@ async function waitForNewPid( timeoutMs = 15_000, brokerName = brokerNameForDir(cwd, 'locktest') ): Promise { - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerName)); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerName)); const start = Date.now(); while (Date.now() - start < timeoutMs) { if (fs.existsSync(pidPath)) { @@ -279,7 +279,7 @@ test('lockfile: PID file is removed after graceful shutdown', { timeout: 30_000 await new Promise((r) => setTimeout(r, 500)); // PID file should be cleaned up - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); assert.ok( !fs.existsSync(pidPath), `PID file should be removed after graceful shutdown. Stderr:\n${stderr.lines.slice(-10).join('\n')}` @@ -365,8 +365,12 @@ test('lockfile: stale lock from dead process is recovered', { timeout: 30_000 }, await waitForExit(first).catch(() => {}); // PID file and lock should still exist (stale) - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); - const lockPath = path.join(cwd, '.agent-relay', `broker-${brokerNameForDir(cwd, 'locktest')}.lock`); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); + const lockPath = path.join( + cwd, + '.agentworkforce/relay', + `broker-${brokerNameForDir(cwd, 'locktest')}.lock` + ); assert.ok(fs.existsSync(pidPath), 'PID file should persist after SIGKILL'); assert.ok(fs.existsSync(lockPath), 'Lock file should persist after SIGKILL'); @@ -433,7 +437,7 @@ test('lockfile: rapid sequential restarts do not leave stale locks', { timeout: if (skipIfMissing(t)) return; const apiKey = await ensureApiKey(); const cwd = makeTempDir(); - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); const iterations = 5; try { @@ -513,7 +517,7 @@ test('lockfile: recovered broker also cleans up PID on exit', { timeout: 45_000 if (skipIfMissing(t)) return; const apiKey = await ensureApiKey(); const cwd = makeTempDir(); - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); try { // Phase 1: Start broker and SIGKILL it (leaves stale artifacts) @@ -567,8 +571,12 @@ test( if (skipIfMissing(t)) return; const apiKey = await ensureApiKey(); const cwd = makeTempDir(); - const lockPath = path.join(cwd, '.agent-relay', `broker-${brokerNameForDir(cwd, 'locktest')}.lock`); - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); + const lockPath = path.join( + cwd, + '.agentworkforce/relay', + `broker-${brokerNameForDir(cwd, 'locktest')}.lock` + ); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); try { // Start and SIGKILL a broker @@ -613,7 +621,7 @@ test('lockfile: stdin EOF triggers clean shutdown with PID cleanup', { timeout: if (skipIfMissing(t)) return; const apiKey = await ensureApiKey(); const cwd = makeTempDir(); - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'locktest'))); try { const child = spawnBroker(cwd, { ...process.env, RELAY_API_KEY: apiKey }); @@ -661,7 +669,7 @@ test( if (skipIfMissing(t)) return; const apiKey = await ensureApiKey(); const cwd = makeTempDir(); - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'inittest'))); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'inittest'))); const port = randomPort(); try { @@ -731,7 +739,7 @@ test('lockfile: init --api-port — stale lock recovery after SIGKILL', { timeou if (skipIfMissing(t)) return; const apiKey = await ensureApiKey(); const cwd = makeTempDir(); - const pidPath = path.join(cwd, '.agent-relay', brokerPidFile(brokerNameForDir(cwd, 'inittest'))); + const pidPath = path.join(cwd, '.agentworkforce/relay', brokerPidFile(brokerNameForDir(cwd, 'inittest'))); const port1 = randomPort(); const port2 = port1 + 1; diff --git a/web/app/telemetry/page.tsx b/web/app/telemetry/page.tsx index b8dc47e77..41a2ab28a 100644 --- a/web/app/telemetry/page.tsx +++ b/web/app/telemetry/page.tsx @@ -199,7 +199,7 @@ export default function TelemetryPage() { Events are sent to PostHog (our analytics provider) over HTTPS.
  • - Preferences are stored locally at ~/.agent-relay/telemetry.json. + Preferences are stored locally at ~/.agentworkforce/relay/telemetry.json.
  • The telemetry module is designed to be infallible — if anything fails, it diff --git a/web/content/docs/cli-broker-lifecycle.mdx b/web/content/docs/cli-broker-lifecycle.mdx index f7842d49f..87612345d 100644 --- a/web/content/docs/cli-broker-lifecycle.mdx +++ b/web/content/docs/cli-broker-lifecycle.mdx @@ -24,7 +24,7 @@ Useful flags: | `--background` | Detach and leave the broker running. | | `--foreground` | Keep a no-dashboard broker attached to this terminal. | | `--workspace-key ` | Join a pre-existing Relay workspace. | -| `--state-dir ` | Write runtime state outside `.agent-relay/`. | +| `--state-dir ` | Write runtime state outside `.agentworkforce/relay/`. | | `--broker-name ` | Override the broker identity. | | `--verbose` | Enable verbose startup logging. | @@ -65,14 +65,14 @@ Use `--force` for stale state or stuck processes. Use `--all` only when intentio ## State Directory -By default, local runtime state is written under `.agent-relay/` in the project. That directory stores connection metadata that attach commands use to find the running broker. +By default, local runtime state is written under `.agentworkforce/relay/` in the project. That directory stores connection metadata that attach commands use to find the running broker. Use `--state-dir` when a project needs isolated runtime files: ```bash -agent-relay local up --state-dir .agent-relay-staging -agent-relay local agent attach reviewer --state-dir .agent-relay-staging -agent-relay local down --state-dir .agent-relay-staging +agent-relay local up --state-dir .agentworkforce/relay-staging +agent-relay local agent attach reviewer --state-dir .agentworkforce/relay-staging +agent-relay local down --state-dir .agentworkforce/relay-staging ``` ## Update And Uninstall