diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ac4050d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,62 @@ +name: CI + +# build-artifacts.yml is workflow_dispatch-only and builds without testing, so +# nothing ran on a pull request. That is how 4d63d75 landed on main having +# deleted three imported assets: `go vet` stays clean, and only a build or a +# test run reaches those paths. + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + go: + name: Go + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + cache-dependency-path: go.sum + + - run: go vet ./... + + # -race because the write coordination lock in internal/app is the kind of + # thing a plain run will not exercise. + - run: go test -race ./... + + frontend: + name: Frontend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: pnpm/action-setup@v6 + with: + version: 11.17.0 + + - uses: actions/setup-node@v7 + with: + node-version: 26 + cache: pnpm + cache-dependency-path: frontend/pnpm-lock.yaml + + - name: Install dependencies + working-directory: frontend + run: pnpm install --frozen-lockfile + + - name: Test + working-directory: frontend + run: pnpm run test + + # Runs tsc --noEmit before vite, so a type error fails here rather than in + # a release build. This is the step that would have caught 4d63d75. + - name: Build + working-directory: frontend + run: pnpm run build diff --git a/agents.lock.json b/agents.lock.json index 02fc7b3..241ad64 100644 --- a/agents.lock.json +++ b/agents.lock.json @@ -132,6 +132,45 @@ "windows" ], "rank": 5 + }, + "cursor": { + "name": "Cursor", + "group": "platform", + "command": "cursor", + "config_mode": "guide", + "platforms": [ + "macos", + "linux", + "windows" + ], + "guide": "Use Cursor subscription or official account settings; OneAgent does not write a private Cursor configuration.", + "rank": 6 + }, + "openclaw": { + "name": "OpenClaw", + "group": "gateway", + "command": "openclaw", + "config_mode": "guide", + "platforms": [ + "macos", + "linux", + "windows" + ], + "guide": "Install from OpenClaw docs, then run: openclaw onboard --install-daemon && openclaw gateway status", + "rank": 7 + }, + "hermes": { + "name": "Hermes", + "group": "gateway", + "command": "hermes", + "config_mode": "guide", + "platforms": [ + "macos", + "linux", + "windows" + ], + "guide": "Install Hermes, then configure models with hermes model or ~/.hermes/config.yaml.", + "rank": 8 } } } diff --git a/frontend/dist/.keep b/frontend/dist/.keep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/icons/assets/cursor.svg b/frontend/src/components/icons/assets/cursor.svg new file mode 100644 index 0000000..f814fdc --- /dev/null +++ b/frontend/src/components/icons/assets/cursor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/icons/assets/hermes.png b/frontend/src/components/icons/assets/hermes.png new file mode 100644 index 0000000..2d629d2 Binary files /dev/null and b/frontend/src/components/icons/assets/hermes.png differ diff --git a/frontend/src/components/icons/assets/openclaw.svg b/frontend/src/components/icons/assets/openclaw.svg new file mode 100644 index 0000000..7bfb7fc --- /dev/null +++ b/frontend/src/components/icons/assets/openclaw.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/internal/app/settings_test.go b/internal/app/settings_test.go index 73ad543..3c8b2bd 100644 --- a/internal/app/settings_test.go +++ b/internal/app/settings_test.go @@ -342,11 +342,19 @@ type countingRunner struct { runs int } -func (r *countingRunner) Run(ctx context.Context, argv []string, env map[string]string, timeout time.Duration) (process.Result, error) { +// Answers the probe itself rather than delegating to a real process. Delegating +// made the result depend on the host: the caller simulates macOS, so the argv is +// `defaults read -g AppleLocale`, which succeeds on a developer's Mac and is not +// a command at all on Linux. A probe that cannot run reports "unanswered" and is +// retried by design, so on Linux the count was 5 rather than 1 -- the assertion +// failed while the caching it describes was working correctly. +func (r *countingRunner) Run(context.Context, []string, map[string]string, time.Duration) (process.Result, error) { r.runs++ - return r.Runner.Run(ctx, argv, env, timeout) + return process.Result{ExitCode: 0, Stdout: "en_US\n"}, nil } +func (r *countingRunner) LookPath(string) (string, bool) { return "", false } + // The same preference has to reach npm, not just the runtime download. The fake // runner records every environment, so the assertion is the registry npm was // actually pointed at. diff --git a/internal/app/testdata/status-empty-linux-arm64.json b/internal/app/testdata/status-empty-linux-arm64.json index 61fae6e..65a5bf5 100644 --- a/internal/app/testdata/status-empty-linux-arm64.json +++ b/internal/app/testdata/status-empty-linux-arm64.json @@ -7,20 +7,21 @@ }, "capabilities": { "canInstall": { - "codex": false, + "aider": false, "claude-code": false, - "opencode": false, + "codex": false, + "cursor": false, + "hermes": false, "kilo-cli": false, - "aider": false, "openclaw": false, - "hermes": false, - "cursor": false, - "kiro": false, - "gemini-cli": false, - "cline": false, - "continue": false, - "qwen-code": false, - "kilo-vscode": false + "opencode": false + }, + "missingRuntime": { + "aider": "uv", + "claude-code": "node", + "codex": "node", + "kilo-cli": "node", + "opencode": "node" }, "supportedAgentIds": [ "codex", @@ -28,234 +29,131 @@ "opencode", "kilo-cli", "aider", - "openclaw", - "hermes", "cursor", - "kiro", - "gemini-cli", - "cline", - "continue", - "qwen-code", - "kilo-vscode" - ], - "missingRuntime": { - "aider": "uv", - "claude-code": "node", - "codex": "node", - "kilo-cli": "node", - "opencode": "node" - } + "openclaw", + "hermes" + ] }, "agents": { - "codex": { + "aider": { "installed": false, - "config": "${HOME}/.codex/config.toml", "configured": false, "guideOnly": false, + "config": "${HOME}/.oneagent/aider.env", "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, "claude-code": { "installed": false, - "config": "${HOME}/.claude/settings.json", - "configured": false, - "guideOnly": false, - "version": null, - "lockedVersion": null, - "canInstall": false, - "provider": null, - "model": null, - "baseUrl": null, - "updatedAt": null, - "detected": null, - "profileId": null - }, - "opencode": { - "installed": false, - "config": "${HOME}/.config/opencode/opencode.json", - "configured": false, - "guideOnly": false, - "version": null, - "lockedVersion": null, - "canInstall": false, - "provider": null, - "model": null, - "baseUrl": null, - "updatedAt": null, - "detected": null, - "profileId": null - }, - "kilo-cli": { - "installed": false, - "config": "${HOME}/.config/kilo/kilo.jsonc", "configured": false, "guideOnly": false, + "config": "${HOME}/.claude/settings.json", "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, - "aider": { + "codex": { "installed": false, - "config": "${HOME}/.oneagent/aider.env", "configured": false, "guideOnly": false, + "config": "${HOME}/.codex/config.toml", "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null - }, - "openclaw": { - "installed": false, - "config": "", - "configured": false, - "guideOnly": true, - "version": null, - "lockedVersion": null, - "canInstall": false, - "provider": null, - "model": null, - "baseUrl": null, - "updatedAt": null, - "detected": null, - "profileId": null - }, - "hermes": { - "installed": false, - "config": "", - "configured": false, - "guideOnly": true, - "version": null, - "lockedVersion": null, - "canInstall": false, - "provider": null, - "model": null, - "baseUrl": null, - "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, "cursor": { "installed": false, - "config": "", "configured": false, "guideOnly": true, - "version": null, - "lockedVersion": null, - "canInstall": false, - "provider": null, - "model": null, - "baseUrl": null, - "updatedAt": null, - "detected": null, - "profileId": null - }, - "kiro": { - "installed": false, "config": "", - "configured": false, - "guideOnly": true, "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, - "gemini-cli": { + "hermes": { "installed": false, - "config": "", "configured": false, "guideOnly": true, - "version": null, - "lockedVersion": null, - "canInstall": false, - "provider": null, - "model": null, - "baseUrl": null, - "updatedAt": null, - "detected": null, - "profileId": null - }, - "cline": { - "installed": false, "config": "", - "configured": false, - "guideOnly": true, "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, - "continue": { + "kilo-cli": { "installed": false, - "config": "", "configured": false, - "guideOnly": true, + "guideOnly": false, + "config": "${HOME}/.config/kilo/kilo.jsonc", "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, - "qwen-code": { + "openclaw": { "installed": false, - "config": "", "configured": false, "guideOnly": true, + "config": "", "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null }, - "kilo-vscode": { + "opencode": { "installed": false, - "config": "", "configured": false, - "guideOnly": true, + "guideOnly": false, + "config": "${HOME}/.config/opencode/opencode.json", "version": null, "lockedVersion": null, "canInstall": false, "provider": null, + "profileId": null, "model": null, "baseUrl": null, "updatedAt": null, - "detected": null, - "profileId": null + "detected": null } }, "catalog": [ @@ -291,22 +189,6 @@ "platformNote": "", "rank": 2 }, - { - "id": "cursor", - "name": "Cursor", - "group": "platform", - "configMode": "guide", - "guideOnly": true, - "lockedVersion": null, - "protocol": null, - "platforms": [ - "macos", - "linux", - "windows" - ], - "platformNote": "", - "rank": 3 - }, { "id": "opencode", "name": "OpenCode", @@ -321,55 +203,7 @@ "windows" ], "platformNote": "", - "rank": 4 - }, - { - "id": "openclaw", - "name": "OpenClaw", - "group": "gateway", - "configMode": "guide", - "guideOnly": true, - "lockedVersion": null, - "protocol": null, - "platforms": [ - "macos", - "linux", - "windows" - ], - "platformNote": "", - "rank": 5 - }, - { - "id": "hermes", - "name": "Hermes", - "group": "gateway", - "configMode": "guide", - "guideOnly": true, - "lockedVersion": null, - "protocol": null, - "platforms": [ - "macos", - "linux", - "windows" - ], - "platformNote": "", - "rank": 6 - }, - { - "id": "gemini-cli", - "name": "Gemini CLI", - "group": "platform", - "configMode": "guide", - "guideOnly": true, - "lockedVersion": null, - "protocol": null, - "platforms": [ - "macos", - "linux", - "windows" - ], - "platformNote": "", - "rank": 7 + "rank": 3 }, { "id": "kilo-cli", @@ -385,7 +219,7 @@ "windows" ], "platformNote": "", - "rank": 8 + "rank": 4 }, { "id": "aider", @@ -401,11 +235,11 @@ "windows" ], "platformNote": "", - "rank": 9 + "rank": 5 }, { - "id": "kiro", - "name": "Kiro", + "id": "cursor", + "name": "Cursor", "group": "platform", "configMode": "guide", "guideOnly": true, @@ -417,44 +251,12 @@ "windows" ], "platformNote": "", - "rank": 10 - }, - { - "id": "cline", - "name": "Cline", - "group": "ide", - "configMode": "guide", - "guideOnly": true, - "lockedVersion": null, - "protocol": null, - "platforms": [ - "macos", - "linux", - "windows" - ], - "platformNote": "", - "rank": 11 - }, - { - "id": "continue", - "name": "Continue", - "group": "ide", - "configMode": "guide", - "guideOnly": true, - "lockedVersion": null, - "protocol": null, - "platforms": [ - "macos", - "linux", - "windows" - ], - "platformNote": "", - "rank": 12 + "rank": 6 }, { - "id": "qwen-code", - "name": "Qwen Code", - "group": "ide", + "id": "openclaw", + "name": "OpenClaw", + "group": "gateway", "configMode": "guide", "guideOnly": true, "lockedVersion": null, @@ -465,12 +267,12 @@ "windows" ], "platformNote": "", - "rank": 13 + "rank": 7 }, { - "id": "kilo-vscode", - "name": "Kilo VS Code", - "group": "ide", + "id": "hermes", + "name": "Hermes", + "group": "gateway", "configMode": "guide", "guideOnly": true, "lockedVersion": null, @@ -481,7 +283,7 @@ "windows" ], "platformNote": "", - "rank": 14 + "rank": 8 } ], "groups": [ @@ -503,17 +305,17 @@ } ], "providers": { - "ppio": { - "name": "PPIO", - "home": "https://ppio.com/", - "base_url": "https://api.ppio.com/openai", - "anthropic_base_url": "https://api.ppio.com/anthropic" - }, "novita": { "name": "Novita", "home": "https://novita.ai/", "base_url": "https://api.novita.ai/openai", "anthropic_base_url": "https://api.novita.ai/anthropic" + }, + "ppio": { + "name": "PPIO", + "home": "https://ppio.com/", + "base_url": "https://api.ppio.com/openai", + "anthropic_base_url": "https://api.ppio.com/anthropic" } }, "mirrors": [ @@ -533,26 +335,25 @@ } ], "paths": { - "logs": "${HOME}/.oneagent/logs", - "profile": "${HOME}/.oneagent/profile.json", - "codex_config": "${HOME}/.codex/config.toml", + "aider_config": "${HOME}/.oneagent/aider.env", "claude-code_config": "${HOME}/.claude/settings.json", - "opencode_config": "${HOME}/.config/opencode/opencode.json", + "codex_config": "${HOME}/.codex/config.toml", "kilo-cli_config": "${HOME}/.config/kilo/kilo.jsonc", - "aider_config": "${HOME}/.oneagent/aider.env" + "logs": "${HOME}/.oneagent/logs", + "opencode_config": "${HOME}/.config/opencode/opencode.json", + "profile": "${HOME}/.oneagent/profile.json" }, "backups": { - "codex": false, + "aider": false, "claude-code": false, - "opencode": false, + "codex": false, "kilo-cli": false, - "aider": false, + "opencode": false, "profile": false }, "profiles": [], "activeProfile": null, - "environment": null, - "environmentError": null, + "firstRun": true, "runtimes": [ { "id": "node", @@ -587,6 +388,8 @@ "requiredByHint": "Aider" } ], + "environment": null, + "environmentError": null, "desktopAgent": { "id": "desktop-agent", "name": "ChatGPT Desktop", @@ -596,6 +399,5 @@ "source": "unknown", "configPath": "${HOME}/.codex/config.toml", "configSharedWith": "Codex" - }, - "firstRun": true + } }