Skill: olares/olares-shared (version 4.0.1)

--- name: olares-shared version: 4.0.1 description: "Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dashboard, cluster). Covers the Olares profile model (one profile = one Olares instance + one Olares ID, e.g. [REDACTED_SECRET]), first-time Olares login with password and optional TOTP, importing an existing refresh_token, switching / listing / removing Olares profiles, OS-keychain token storage keyed by Olares ID, automatic access_token refresh on 401/403, and the full Olares auth-error recovery table. Use when the user mentions Olares, Olares ID, olares-cli, OpenClaw on Olares, profile, login, logout, two-factor / 2FA / TOTP, refresh token, keychain, or sees errors like 'server rejected the access token', 'refresh token for X became invalid', 'no access token for X', 'already authenticated', or 'two-factor authentication required'." metadata: requires: bins: ["olares-cli"] cliHelp: "olares-cli profile --help" --- # olares-cli shared rules Foundation for every other `olares-cli` skill. Every business verb under `cluster` / `files` / `market` / `settings` / `dashboard` rides the active profile's token. **Read this first.** > **Source of truth for flags & syntax is always `olares-cli <command> --help`.** This file only carries what `--help` cannot give: the profile mental model, agent-driven login flow, token-storage backends, refresh semantics, and the error → fix matrix. ## Profile model One profile = one Olares instance + one user identity, keyed by **olaresId** (e.g. `[REDACTED_SECRET]`). Each profile owns its own access_token / refresh_token pair, stored in the OS keychain. | Command | Purpose | |---------|---------| | `olares-cli profile login` | Mode A — password (+ TOTP if 2FA is on); auto-creates the profile on first run | | `olares-cli profile import` | Mode B — bootstrap an access_token from an existing refresh_token | | `olares-cli profile list` | List every profile, mark the current one, show login status per profile | | `olares-cli profile use <name\|->` | Switch the current profile; `-` reverts to the previous one (like `cd -`) | | `olares-cli profile remove <name>` | Delete a profile and its stored token in one shot | > **There is no `auth login` / `auth logout` namespace and no per-invocation `--profile` override flag.** Everything lives under `profile`. "Logout" is `profile remove`. Identity is whichever profile is currently selected; to target a different one, run `olares-cli profile use <name>` first. ## Login modes ### Mode A — password (+ optional TOTP) ```bash olares-cli profile login --olares-id <olaresId> ``` - Interactive: prompts for password (echo disabled); prompts for TOTP if 2FA is enabled. - Scripted: pipe via `--password-stdin`; if 2FA is on, you MUST also pass `--totp <code>` because there is no second prompt. - **There is no `--password <plaintext>` flag** — passwords are never accepted on the command line. ### Mode B — existing refresh_token ```bash olares-cli profile import --olares-id <olaresId> --refresh-token "$OLARES_REFRESH_TOKEN" ``` Exchanges the refresh_token for an access_token once via `/api/refresh` and writes both to the keychain. **Read the token from an env var or secret manager — never inline plaintext.** ### Agent-driven login (recommended) When you (an AI agent) drive the login on the user's behalf, do NOT pass password / TOTP as command-line arguments. Spawn `olares-cli profile login --olares-id <id>` as a background process so it parks at the password prompt, forward the prompt to the user, and read its output after the command exits to confirm success. ## Switching and inspecting profiles `profile list` output: ``` NAME OLARES-ID STATUS * alice [REDACTED_SECRET] logged-in (23h59m) bob [REDACTED_SECRET] expired eve [REDACTED_SECRET] invalidated frank [REDACTED_SECRET] never ``` | STATUS | Meaning | Recovery | |--------|---------|----------| | `logged-in (Xh Ym)` | Token valid; column shows time-to-expiry | — | | `logged-in` | Token present but JWT has no exp claim (can't verify locally) | Trust until the server says no | | `expired` | JWT exp is in the past | `profile login` | | `invalidated` | Server explicitly rejected the refresh leg | `profile login` directly (no need to `profile remove` first) | | `never` | No token has ever been stored | `profile login` or `profile import` | The leading `*` marks the current profile. `profile use` accepts either the NAME alias or the olaresId. ## Token storage | OS | Backend | Location | |------|---------|----------| | darwin | macOS Keychain | service `olares-cli`, account = olaresId | | linux | AES-256-GCM file | under `~/.local/share/olares-cli/` | | windows | DPAPI | `HKCU\Software\OlaresCli\keychain` | After `login` / `import` succeeds, the CLI prints `token stored via <backend> (service "olares-cli", account "<id>")`. If the backend resolves to `file-fallback` (sandboxed / CI environments), be aware the token now sits in a file with **different security properties than the system keychain**. > **The plaintext `~/.olares-cli/tokens.json` from older builds is deprecated** — if a user upgraded and suddenly appears "logged out", `profile login` is the fix. ## Re-authentication rules `profile login` and `profile import` both reject the case "a still-valid token already exists for this olaresId" — to force-overwrite, run `profile remove <id>` first. Expired / invalidated / never-logged-in profiles get the new token written in place; this lets scripts call `login` after `invalidated` without an extra `remove` step. ## Automatic token refresh **The CLI rotates expired access_tokens transparently.** Users do NOT need to run `profile login` just because their access_token aged out — only when the *refresh_token itself* becomes invalid. - Replayable requests (every JSON verb, `files cat`, `files download`, `files rm`, `market` verbs, …): on 401/403 the transport calls `/api/refresh` and retries once with the new token. - Streaming uploads (`files upload` chunks): pre-decode the JWT exp; if within 60s of expiry, refresh BEFORE sending, because once a `*os.File` chunk is consumed it can't be replayed on a 401. Across goroutines AND across concurrent `olares-cli` processes, `/api/refresh` is hit at most once per stale token (in-process mutex + cross-process flock). > **Do not implement custom retry/backoff loops on top of auth errors.** Once you see `ErrTokenInvalidated` or `ErrNotLoggedIn`, only `profile login` / `profile import` will help. ## Auth error recovery table | Error message (excerpt) | Meaning | Fix | |-------------------------|---------|-----| | `refresh token for <id> became invalid at <ts>` | `/api/refresh` returned 401/403 — the grant is dead | `olares-cli profile login --olares-id <id>` | | `no access token for <id>` | Profile selected but keychain has no entry | `olares-cli profile login` or `profile import` | | `server rejected the access token (HTTP 401)` / `(HTTP 403)` | After auto-refresh the server still rejects (rare) | `olares-cli profile login --olares-id <id>` | | `--olares-id is required` | login / import invoked without olaresId | Add `--olares-id <id>` | | `already authenticated for <id> (expires in ...)` | Still-valid token exists | `olares-cli profile remove <id>` then re-run | | `a token is already stored for <id> but its expiry can't be determined client-side` | Token present but JWT carries no exp claim | `profile remove <id>` then re-run | | `two-factor authentication required: re-run with --totp <code>` | 2FA on, non-TTY context | Re-run with `--totp <code>`, or run interactively | | `password is empty` / `TOTP code is empty` | stdin / TTY returned an empty string | Check for premature EOF or an empty pipe | | `profile <name> not found` | `profile use` / `profile remove` referenced an unknown profile | `profile list` to see the actual names | > **Do not silently retry auth errors.** 401/403 after auto-refresh and `already authenticated` are deterministic — follow the table; blind retries make it worse. ## Security rules - **Never** invent a `--password <plaintext>` argument (it does not exist). Passwords go through the TTY or `--password-stdin` fed by a secret pipe. - **Never** echo `access_token` / `refresh_token` to the terminal. When passing a refresh_token to `profile import`, source it from an environment variable: `--refresh-token "$OLARES_REFRESH_TOKEN"`. - **Confirm intent before write/delete actions** (`profile remove`, `files rm`, `files upload --overwrite`, `cluster pod delete`, …). Do not act unilaterally on the user's behalf. - **TOTP is not a password** — it is single-use and short-lived, so the CLI echoes it to make manual entry less error-prone. Never persist a TOTP in a shared script.


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)