🎁 Get the FREE AI Skills Starter GuideSubscribe →
BytesAgainBytesAgain
🦀 ClawHub

Olares Shared (olares-cli foundation)

by @olares

Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dash...

Versionv4.0.1
Downloads649
TERMINAL
clawhub install olares-shared

📖 About This Skill


name: olares-shared version: 1.1.0 description: "Shared olares-cli foundation: profile model, first-time login (profile login with password + TOTP), bootstrapping a profile from an existing refresh token (profile import), switching/listing/removing profiles, the global --profile flag, where access/refresh tokens live in the OS keychain, automatic access_token refresh via /api/refresh (transparent reactive retry on 401/403; pro-active JWT-exp refresh for streaming uploads; cross-goroutine + cross-process deduplication via flock), and how to recover from auth errors (HTTP 401/403, refresh token invalidated, not logged in, two-factor authentication required). Use whenever the user is configuring olares-cli for the first time, logging in or importing credentials, switching/listing/removing profiles, asking how token refresh works, scripting parallel olares-cli invocations, or seeing 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'; also use when the user asks about refresh tokens, the keychain, olaresId, or profile management." metadata: requires: bins: ["olares-cli"] cliHelp: "olares-cli profile --help"

olares-cli shared rules

This skill explains: what a profile is, how to obtain access credentials for it, where those credentials live, and how to recover when the server rejects a token. Every olares-cli files ... (and other business) command depends on the profile selection + auth flow described here.

Profile model

One profile = one Olares instance + one user identity. The identity is uniquely keyed by an olaresId (e.g. alice@olares.com). Each profile owns its own access_token / refresh_token pair, stored in the OS keychain.

The profile command tree exposes 5 verbs (see cmd/ctl/profile/root.go):

| Command | Purpose | |---------|---------| | olares-cli profile login | Authenticate with a password (+ TOTP if 2FA is on); auto-creates the profile on first run (mode A) | | olares-cli profile import | Bootstrap an access_token from an existing refresh_token (mode B) | | olares-cli profile list | List every profile, mark the current one, show login status per profile | | olares-cli profile use | Switch the current profile; - reverts to the previous one (analogous to cd -) | | olares-cli profile remove | Delete a profile and its stored token in one shot |

> There is no olares-cli auth login / auth logout namespace. Every auth-related action lives under profile. "Logout" is profile remove.

Global --profile flag

The root command registers a persistent --profile flag (see cmd/ctl/root.go L57). It overrides the currently-selected profile for one invocation without flipping the persisted current pointer:

# Doesn't change current; this single ls runs against alice's credentials.
olares-cli files ls drive/Home/ --profile alice@olares.com

Use this for: scripting parallel operations against multiple profiles, sanity-checking a specific profile's status, and avoiding pollution of the interactive terminal's current pointer.

First-time login (mode A: password + optional TOTP)

olares-cli profile login --olares-id 

Behavior (see cmd/ctl/profile/login.go and cmd/ctl/profile/credentials.go):

  • Profile does not exist → auto-created
  • Profile exists, token expired or invalidated → reuse the profile entry, write a fresh token
  • Profile exists, token still valid → rejected with a hint to run olares-cli profile remove first
  • Password

  • Default: read from the controlling TTY with echo disabled
  • Scripts: --password-stdin, e.g. printf '%s' "$PW" | olares-cli profile login --olares-id --password-stdin
  • There is no --password </code> flag.</strong> The CLI deliberately omits it so passwords never leak into shell history or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ps</code> output.</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">2FA / TOTP</h4></p><p style="margin:8px 0">When the server's <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/firstfactor</code> returns <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">fa2=true</code>, a second factor is required:</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">TTY: the CLI prompts <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">two-factor code for <id>:</code> automatically; the user types the 6-digit code</li> <li style="color:#94a3b8;margin:3px 0">Non-TTY (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--password-stdin</code>, CI, etc.): you MUST pass <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--totp <code></code> up front, otherwise the command fails with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">two-factor authentication required: re-run with --totp <code></code></li></p><p style="margin:8px 0">> <strong style="color:#e5e7eb">The CLI does not try to guess whether 2FA is enabled.</strong> It probes every login with a targetURL that triggers Authelia's 2FA policy. Accounts without 2FA pass through transparently; accounts with 2FA get prompted for TOTP and then proceed.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Agent-driven login (recommended pattern)</h4></p><p style="margin:8px 0">When you (an AI agent) drive the login on the user's behalf, <strong style="color:#e5e7eb">do not</strong> pass the password or TOTP as plaintext command-line arguments. Recommended flow:</p><p style="margin:8px 0">1. Spawn <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile login --olares-id <id></code> as a background process so it parks at the password prompt. 2. Forward the prompt verbatim to the user and wait for them to type the password / TOTP into their own terminal. 3. After the command exits, read its output to confirm whether the login succeeded.</p><p style="margin:8px 0">Alternatively, instruct the user to run the login in their terminal themselves; the agent then takes over for follow-up command orchestration.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Bootstrap from an existing refresh_token (mode B)</h3></p><p style="margin:8px 0">If the user already has a refresh_token (from LarePass, the wizard activation flow, or any other source), there is no need to run through password + 2FA again:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">olares-cli profile import --olares-id <olaresId> --refresh-token <tok> </code></pre></p><p style="margin:8px 0">The CLI exchanges the refresh_token for an access_token via a single <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> call (see <a href="cli/cmd/ctl/profile/import.go" target="_blank" rel="noopener" style="color:#6366f1"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">cmd/ctl/profile/import.go</code></a>) and writes both into the keychain. The "reject if a valid token already exists" rule from <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">login</code> applies here too.</p><p style="margin:8px 0">> <strong style="color:#e5e7eb">Never write <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--refresh-token <tok></code> as plaintext in scripts.</strong> Read it from an environment variable or a secret manager: > <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">> olares-cli profile import --olares-id <id> --refresh-token "$OLARES_REFRESH_TOKEN" > </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Switching and inspecting profiles</h3></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile list</code></h4></p><p style="margin:8px 0">Output (see <a href="cli/cmd/ctl/profile/list.go" target="_blank" rel="noopener" style="color:#6366f1"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">cmd/ctl/profile/list.go</code></a>):</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"> NAME OLARES-ID STATUS * alice alice@olares.com logged-in (23h59m) bob bob@olares.com expired eve eve@olares.com invalidated frank frank@olares.com never </code></pre></p><p style="margin:8px 0">| STATUS | Meaning | Recovery | |--------|---------|----------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">logged-in (Xh Ym)</code> | Token is valid; column shows time-to-expiry | — | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">logged-in</code> | Token is present but its JWT has no exp claim, so we can't verify locally | Trust until the server says no | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">expired</code> | Token JWT exp is in the past | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> to re-authenticate | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">invalidated</code> | The server explicitly rejected the refresh leg (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> returned 401/403) | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> directly — no need to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile remove</code> first | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">never</code> | No token has ever been stored for this profile | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code> |</p><p style="margin:8px 0">The leading <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">*</code> marks the current profile.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile use <name|-></code></h4></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">olares-cli profile use alice # by NAME alias olares-cli profile use alice@olares.com # by olaresId (also accepted) olares-cli profile use - # back to the previous current (errors when none) </code></pre></p><p style="margin:8px 0">Updates <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">currentProfile</code> and <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">previousProfile</code> inside <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">~/.config/olares-cli/config.json</code>.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile remove <name></code></h4></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">olares-cli profile remove alice </code></pre></p><p style="margin:8px 0">Performs four actions atomically:</p><p style="margin:8px 0">1. Removes the profile entry from <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">config.json</code>. 2. Deletes the stored token for that olaresId from the keychain. 3. If the removed profile was current, current falls back to the previous (when valid) or to the first remaining profile. 4. If the removed profile was the last one, the keychain namespace itself is purged so no orphan entries remain in Keychain Access.app / regedit / etc.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Token storage</h3></p><p style="margin:8px 0">| OS | Backend | Location | |------|---------|----------| | darwin | macOS Keychain | service <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli</code>, account = olaresId | | linux | AES-256-GCM file | under <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">~/.local/share/olares-cli/</code> | | windows | DPAPI | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">HKCU\Software\OlaresCli\keychain</code> |</p><p style="margin:8px 0"><strong style="color:#e5e7eb">The plaintext <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">~/.olares-cli/tokens.json</code> from older builds is deprecated</strong> — tokens written there by previous versions are no longer read. If the user upgraded and suddenly appears "logged out", the correct fix is <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> to repopulate the new storage.</p><p style="margin:8px 0">After <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">login</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">import</code> succeeds, the CLI prints a line like <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">token stored via <backend> (service "olares-cli", account "<id>")</code>. That message is the source of truth for "where did my token actually land". If the backend resolves to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">file-fallback</code> (sandboxed / CI environments without access to a system keychain), be aware: that token is now sitting in a file with <strong style="color:#e5e7eb">different security properties than the system keychain</strong>.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Re-authentication rules (critical)</h3></p><p style="margin:8px 0"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> and <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code> both apply the same logic per olaresId:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"> ┌───────────────────────────────────────┐ profile not exist ──▶│ Auto-create and write the new token │ └───────────────────────────────────────┘ ┌──────────────────────────────────────────────┐ token expired ──▶│ Reuse the profile entry, write a new token │ └──────────────────────────────────────────────┘ ┌──────────────────────────────────────────────┐ token invalidated ──▶│ Reuse the profile entry, write a new token │ └──────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────┐ token still valid ──▶│ Reject; tell the user to run profile remove <id> first │ └──────────────────────────────────────────────────────────┘ </code></pre></p><p style="margin:8px 0">Logic lives in <a href="cli/cmd/ctl/profile/credentials.go" target="_blank" rel="noopener" style="color:#6366f1"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">cmd/ctl/profile/credentials.go</code></a> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ensureProfileWritable</code>. If a script needs unconditional overwrite, it MUST <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile remove</code> first and then <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code>.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Automatic token refresh</h3></p><p style="margin:8px 0"><strong style="color:#e5e7eb">The CLI rotates expired access_tokens transparently.</strong> Users do NOT need to run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> just because their access_token aged out — only when the *refresh_token* itself becomes invalid.</p><p style="margin:8px 0">The refresh logic lives in <a href="cli/pkg/cmdutil/factory.go" target="_blank" rel="noopener" style="color:#6366f1"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">cli/pkg/cmdutil/factory.go</code></a> (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">refreshingTransport</code>) and <a href="cli/pkg/credential/refresher.go" target="_blank" rel="noopener" style="color:#6366f1"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">cli/pkg/credential/refresher.go</code></a>. Every <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">*http.Client</code> the Factory hands out has it wired in.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Two trigger paths</h4></p><p style="margin:8px 0">| Trigger | Applies to | Behavior | |---------|------------|----------| | <strong style="color:#e5e7eb">Reactive (401/403 + retry)</strong> | Replayable bodies — every JSON / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">files cat</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">files download</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">files rm</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">market</code> JSON verb | Send with current token. On 401/403, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code>, retry the same request once with the new token. One extra round-trip in the rare expiry case; zero overhead in steady state. | | <strong style="color:#e5e7eb">Pro-active (JWT exp + skew)</strong> | Non-replayable bodies — <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">files upload</code> chunks (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">*os.File</code>) | Decode the access_token's JWT exp before sending. If within 60s of expiry (or already past), <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> first, send with the new token. Required because once a streaming body is consumed by the first send we can't replay it on a 401. |</p><p style="margin:8px 0">The pro-active skew is hardcoded at 60s in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">cli/pkg/cmdutil/factory.go</code> (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">preflightSkew</code>) — comfortably absorbs client↔server clock drift plus the time from local decode to the request landing on the server. Tokens issued without an <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">exp</code> claim, or values that don't decode as a JWT at all, skip the pre-flight gracefully and fall back to the reactive path.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Concurrency</h4></p><p style="margin:8px 0">Across goroutines AND across concurrent <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli</code> processes, <strong style="color:#e5e7eb"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> is hit at most once per stale token</strong>:</p><p style="margin:8px 0">1. Process-wide <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">sync.Mutex</code> — losers wait, then read whatever the winner persisted. 2. Compare-after-Get against the keychain — short-circuits when a sibling already rotated the token. 3. On-disk <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">flock</code> under <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><config-dir>/locks/<sanitized-olaresId>.refresh.lock</code> — serializes across processes; bounded by a 30s acquire timeout so a stuck peer can't hang the CLI. 4. Re-check inside the flock — collapses any final race that snuck in between the in-process and on-disk locks.</p><p style="margin:8px 0">For most users this is invisible. It matters when you script multiple <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli</code> invocations in parallel: they will not stampede <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code>.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">When refresh itself fails</h4></p><p style="margin:8px 0">| Outcome | What the user sees | Fix | |---------|---------------------|-----| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> returns 200 + new tokens | (silent — request retried, command succeeds) | — | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> returns 401/403 (refresh_token revoked / expired / rotated by another login) | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">refresh token for <id> became invalid at <ts>; please run: olares-cli profile login --olares-id <id></code> (typed <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">*ErrTokenInvalidated</code>) | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile login --olares-id <id></code> | | No token in keychain at all | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">no access token for <id>; run: olares-cli profile login --olares-id <id></code> (typed <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">*ErrNotLoggedIn</code>) | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile login</code> (or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code>) | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> returns 5xx / network error | Surfaced verbatim from the transport | Retry the command — the grant itself is still valid |</p><p style="margin:8px 0">The keychain entry is stamped <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">InvalidatedAt</code> on the 401 path so subsequent commands skip the network round-trip and go straight to the CTA. <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile list</code> shows these as <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">invalidated</code>.</p><p style="margin:8px 0">> <strong style="color:#e5e7eb">Do not implement custom retry/backoff loops on top of these errors.</strong> The transport already handles the recoverable cases; once you see <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ErrTokenInvalidated</code> or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ErrNotLoggedIn</code>, only <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile login</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code> will help.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Auth error recovery table</h3></p><p style="margin:8px 0">| Error message (excerpt) | Meaning | Fix | |-------------------------|---------|-----| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">refresh token for <id> became invalid at <ts></code> | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/refresh</code> itself returned 401/403 — the grant is dead | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile login --olares-id <id></code> | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">no access token for <id></code> | Profile selected but keychain has no entry | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile login</code> or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code> | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">server rejected the access token (HTTP 401)</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">(HTTP 403)</code> | After auto-refresh the server still rejects (rare; usually a server-side state drift) | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile login --olares-id <id></code> | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--olares-id is required</code> | login / import was invoked without olaresId | Add <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--olares-id <id></code> | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">already authenticated for <id> (expires in ...)</code> | A still-valid token exists for this olaresId | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">olares-cli profile remove <id></code> and re-run login / import | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">a token is already stored for <id> but its expiry can't be determined client-side</code> | Token present but JWT carries no exp claim, so we conservatively reject | Same: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile remove <id></code> and re-run | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">two-factor authentication required: re-run with --totp <code></code> | 2FA is on and we're in a non-TTY context (no way to prompt) | Re-run with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--totp <code></code>, or run interactively in a TTY | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">password is empty</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">TOTP code is empty</code> | stdin / TTY returned an empty string | Check for premature EOF or an empty pipe | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile <name> not found</code> | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile use</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile remove</code> referenced an unknown profile | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile list</code> to see the actual names |</p><p style="margin:8px 0">> <strong style="color:#e5e7eb">Do not silently retry auth errors.</strong> 401/403 after auto-refresh and <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">already authenticated</code> are deterministic — follow the table; blind retries make the situation worse.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">dev / internal flags</h3></p><p style="margin:8px 0">For internal debugging or self-hosted dev environments only — <strong style="color:#e5e7eb">never include these in user-facing examples or scripts</strong>:</p><p style="margin:8px 0">| Flag | Use | |------|-----| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--auth-url-override <url></code> | Hard-pin the Authelia URL instead of deriving it from olaresId | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--local-url-prefix <label></code> | Inject an extra label between the auth subdomain and the terminus name | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--insecure-skip-verify</code> | Disable TLS verification (only for self-signed local environments) |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Security rules</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Never</strong> invent a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--password <plaintext></code> argument (it does not exist). Passwords go through the TTY or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--password-stdin</code> fed by a secret pipe.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Never</strong> echo <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">access_token</code> / <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">refresh_token</code> to the terminal. When passing a refresh_token to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile import</code>, source it from an environment variable or external secret store: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--refresh-token "$OLARES_REFRESH_TOKEN"</code>.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Confirm intent before write/delete actions</strong> (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">profile remove</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">files rm</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">files upload --overwrite</code>, ...). Do not act unilaterally on the user's behalf.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">TOTP is not a password</strong> — it is single-use and short-lived, so the CLI deliberately echoes it to make manual entry less error-prone (matching <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">gh auth login</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">aws sso login</code>, kubectl OIDC plugins). That said, never persist a TOTP in a shared script.</li> </p></div></section></div><div class="two-col-side"></div></div></div><script> document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn => { btn.addEventListener('click', () => { const cmd = btn.getAttribute('data-cmd'); if (!cmd) return; navigator.clipboard.writeText(cmd).then(() => { const orig = btn.textContent; btn.textContent = 'Copied!'; setTimeout(() => btn.textContent = orig, 1500); }).catch(() => {}); }); }); </script><!--$--><!--/$--></main><footer style="background:var(--bg-primary);border-top:1px solid var(--border-secondary);margin-top:60px"><div style="border-top:1px solid var(--border-light);max-width:1200px;margin:0 auto;padding:24px 20px"><div style="display:flex;justify-content:space-between;flex-wrap:wrap;gap:24px;margin-bottom:24px"><div><div style="font-weight:700;color:var(--text-muted);margin-bottom:8px">BytesAgain</div><div style="color:var(--text-muted3);font-size:.82em;max-width:200px">Discover the best AI agent skills for your workflow.</div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Explore</div><div style="margin-bottom:6px"><a href="/skills" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Skills</a></div><div style="margin-bottom:6px"><a href="/articles" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Articles</a></div><div style="margin-bottom:6px"><a href="/use-case" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Cases</a></div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Company</div><div style="margin-bottom:6px"><a href="/about" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">About</a></div><div style="margin-bottom:6px"><a href="/contact" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Contact</a></div><div style="margin-bottom:6px"><a href="/privacy-policy" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Privacy Policy</a></div><div style="margin-bottom:6px"><a href="/terms" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Terms</a></div><div style="margin-bottom:6px"><a href="/feedback" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Feedback</a></div></div></div><div style="border-top:1px solid var(--border-light);padding-top:16px"><div style="color:var(--text-muted4);font-size:.8em;margin-bottom:8px">© <!-- -->2026<!-- --> BytesAgain. All rights reserved.</div><div style="color:var(--text-muted5);font-size:.75em;line-height:1.6;max-width:720px">BytesAgain is an independent skill directory. We index and link to third-party content (ClawHub, GitHub, LobeHub, Dify, etc.) for informational purposes only. All trademarks, skill names, and content are the property of their respective owners. BytesAgain does not claim ownership of any indexed content.</div></div></div></footer><button style="position:fixed;bottom:28px;right:28px;z-index:1000;width:48px;height:48px;border-radius:50%;border:none;cursor:pointer;background:linear-gradient(135deg,#667eea,#00d4ff);color:#fff;font-size:1.3em;box-shadow:0 4px 20px #667eea66;display:flex;align-items:center;justify-content:center;transition:transform .2s">💬</button><script src="/_next/static/chunks/0ze4gu236oq96.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[62894,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"LangProvider\"]\n3:I[89220,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ThemeProvider\"]\n4:I[16988,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\ne:I[68027,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\",1]\n:HL[\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"style\"]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n5:Td5e,"])</script><script>self.__next_f.push([1,"[{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"description\":\"Search 60,000+ verified AI agent skills via MCP API or REST. Supports 7 languages. Free, no auth required.\",\"inLanguage\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"ja\",\"ko\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https://bytesagain.com/skills?q={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}},{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://bytesagain.com/og-image.png\"},\"description\":\"AI agent skill directory. Search 60,000+ skills, 1,000+ use cases, and community requests.\",\"foundingDate\":\"2026\",\"foundingLocation\":{\"@type\":\"Place\",\"name\":\"Global\"},\"sameAs\":[\"https://x.com/bytesagain\",\"https://github.com/bytesagain/ai-skills\",\"https://clawhub.ai/profile/bytesagain\"],\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"hello@bytesagain.com\",\"contactType\":\"customer support\"},\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"value\":1}},{\"@context\":\"https://schema.org\",\"@type\":\"WebApplication\",\"name\":\"BytesAgain AI Skills Search\",\"url\":\"https://bytesagain.com\",\"applicationCategory\":\"DeveloperApplication\",\"operatingSystem\":\"Web\",\"description\":\"Search engine and MCP API for 60,000+ AI agent skills. Semantic search, role recommendations, and use case packs.\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"featureList\":[\"Search 60,000+ AI agent skills\",\"Role-based recommendations for developers, creators, and traders\",\"1,000+ curated use case packs\",\"Free MCP API and REST API\",\"Multi-language search (EN, ZH, ES, FR, DE, JA, KO)\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":\"https://bytesagain.com/skills?q={search_term_string}\",\"query-input\":\"required name=search_term_string\"},\"dateModified\":\"2026-07-19\"},{\"@context\":\"https://schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"What is BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"BytesAgain is a curated directory of 60,000+ AI agent skills from ClawHub, GitHub, LobeHub, and Dify. Search skills by keyword in 7 languages, browse by role (developer, creator, trader, marketer) or by use case.\"}},{\"@type\":\"Question\",\"name\":\"How do I find AI skills on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Use the search bar on BytesAgain.com to search by keyword in 7 languages. You can also browse by role (developer, creator, trader, marketer) or by use case. Each skill shows install instructions for Claude, Cursor, OpenClaw, Continue, and more.\"}},{\"@type\":\"Question\",\"name\":\"Is BytesAgain free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, BytesAgain is completely free. No registration required for searching skills. The MCP API is also free with rate limits.\"}},{\"@type\":\"Question\",\"name\":\"Does BytesAgain have an API for AI agents?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! BytesAgain provides a free MCP SSE endpoint at /api/mcp/sse for AI agents, plus a REST API at /api/mcp?action=search\u0026q=\u003cquery\u003e. No authentication needed.\"}},{\"@type\":\"Question\",\"name\":\"Can I request a new AI skill on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! Visit the Requests page on BytesAgain.com to submit a skill request. Your request will be visible to the community and notified to the site admin.\"}}]}]"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"skill\",\"olares-shared\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"skill\",{\"children\":[[\"slug\",\"olares-shared\",\"d\",null],{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"llms\",\"href\":\"/llms.txt\"}],[\"$\",\"link\",null,{\"rel\":\"llms-full\",\"href\":\"/llms-full.txt\"}],[\"$\",\"script\",null,{\"async\":true,\"src\":\"https://www.googletagmanager.com/gtag/js?id=G-3C1MM9FWYF\"}],[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n window.dataLayer = window.dataLayer || [];\\n function gtag(){dataLayer.push(arguments);}\\n gtag('js', new Date());\\n gtag('config', 'G-3C1MM9FWYF');\\n \"}}]]}],[\"$\",\"body\",null,{\"className\":\"geist_9e050971-module__05dp7a__className\",\"style\":{\"margin\":0},\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[[\"$\",\"div\",null,{\"style\":{\"width\":\"100%\",\"background\":\"var(--bg-subscribe)\",\"borderBottom\":\"1px solid var(--border-primary)\",\"padding\":\"8px 20px\",\"textAlign\":\"center\",\"fontSize\":\".82em\",\"color\":\"#818cf8\"},\"children\":[\"🎁 \",[\"$\",\"strong\",null,{\"style\":{\"color\":\"var(--text-primary)\"},\"children\":\"Get the FREE AI Skills Starter Guide\"}],\" — \",[\"$\",\"a\",null,{\"href\":\"/register\",\"style\":{\"color\":\"#00d4ff\",\"textDecoration\":\"underline\"},\"children\":\"Subscribe →\"}]]}],[\"$\",\"$L4\",null,{}],[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$5\"}}],\"$L6\",\"$L7\",\"$L8\"]}]}]}]]}]]}],{\"children\":[\"$L9\",{\"children\":[\"$La\",{\"children\":[\"$Lb\",{},null,false,null]},null,false,\"$@c\"]},null,false,\"$@c\"]},null,false,null],\"$Ld\",false]],\"m\":\"$undefined\",\"G\":[\"$e\",[\"$Lf\"]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"10:I[39756,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n11:I[37457,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n12:I[22016,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0ka051yepewro.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"\"]\n13:I[90940,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n14:I[16397,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n16:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"OutletBoundary\"]\n17:\"$Sreact.suspense\"\n1a:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ViewportBoundary\"]\n1c:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"MetadataBoundary\"]\n"])</script><script>self.__next_f.push([1,"6:[\"$\",\"main\",null,{\"children\":[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"main\",null,{\"style\":{\"minHeight\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"background\":\"#050611\",\"color\":\"#e5e7eb\"},\"children\":[[\"$\",\"style\",null,{\"children\":\"\\n .nf-box { text-align: center; padding: 60px 32px; }\\n .nf-code { font-size: 6rem; font-weight: 900; color: #22d3ee; line-height: 1; margin: 0; }\\n .nf-title { font-size: 1.8rem; font-weight: 800; margin: 12px 0 8px; }\\n .nf-desc { color: var(--text-muted2); font-size: 1rem; margin-bottom: 32px; max-width: 440px; }\\n .nf-link { display: inline-block; padding: 12px 28px; background: linear-gradient(135deg,#34d399,#22d3ee); color: #000; font-weight: 900; border-radius: 12px; text-decoration: none; }\\n \"}],[\"$\",\"div\",null,{\"className\":\"nf-box\",\"children\":[[\"$\",\"p\",null,{\"className\":\"nf-code\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"nf-title\",\"children\":\"Page Not Found\"}],[\"$\",\"p\",null,{\"className\":\"nf-desc\",\"children\":\"The skill or page you're looking for doesn't exist or has been moved.\"}],[\"$\",\"$L12\",null,{\"className\":\"nf-link\",\"href\":\"/\",\"children\":\"Back to BytesAgain\"}]]}]]}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]\n"])</script><script>self.__next_f.push([1,"7:[\"$\",\"$L13\",null,{}]\n8:[\"$\",\"$L14\",null,{}]\n9:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\na:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\nb:[\"$\",\"$1\",\"c\",{\"children\":[\"$L15\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L16\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@18\"}]}]]}]\n19:[]\nc:\"$W19\"\nd:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L1a\",null,{\"children\":\"$L1b\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1c\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1d\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\nf:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n"])</script><script>self.__next_f.push([1,"1b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"1e:I[27201,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"IconMark\"]\n18:null\n"])</script><script>self.__next_f.push([1,"1d:[[\"$\",\"title\",\"0\",{\"children\":\"Olares Shared (olares-cli foundation) — AI Agent Skill | BytesAgain | BytesAgain\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dash...\"}],[\"$\",\"meta\",\"2\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"3\",{\"name\":\"googlebot\",\"content\":\"index, follow, max-image-preview:large, max-snippet:-1\"}],[\"$\",\"meta\",\"4\",{\"name\":\"llms-txt\",\"content\":\"https://bytesagain.com/llms.txt\"}],[\"$\",\"meta\",\"5\",{\"name\":\"llms-full-txt\",\"content\":\"https://bytesagain.com/llms-full.txt\"}],[\"$\",\"link\",\"6\",{\"rel\":\"canonical\",\"href\":\"https://bytesagain.com/skill/olares-shared\"}],[\"$\",\"meta\",\"7\",{\"name\":\"baidu-site-verification\",\"content\":\"codeva-0evUqX1TFs\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:title\",\"content\":\"Olares Shared (olares-cli foundation) — AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:description\",\"content\":\"Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dash...\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:url\",\"content\":\"https://bytesagain.com/skill/olares-shared\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:site_name\",\"content\":\"BytesAgain\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"Olares Shared (olares-cli foundation) — AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dash...\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"22\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1e\",\"23\",{}]]\n"])</script><script>self.__next_f.push([1,"1f:T1562,"])</script><script>self.__next_f.push([1,"\n .skill-page { max-width: 1100px; margin: 0 auto; padding: 32px 20px 80px; }\n .two-col { display: flex; gap: 32px; align-items: flex-start; }\n .two-col-main { flex: 1; min-width: 0; }\n .two-col-side { width: 300px; flex-shrink: 0; }\n @media (max-width: 860px) {\n .two-col { flex-direction: column; }\n .two-col-side { width: 100%; }\n }\n .breadcrumb { font-size: .82em; color: var(--text-muted2); margin-bottom: 28px; }\n .breadcrumb a { color: #818cf8; text-decoration: none; }\n .breadcrumb a:hover { text-decoration: underline; }\n .skill-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 20px; padding: 28px; margin-bottom: 24px; }\n .skill-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n .skill-badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .skill-top-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }\n .badge { display: inline-flex; align-items: center; gap: 5px; font-size: .75em; font-weight: 600; padding: 4px 12px; border-radius: 999px; border: 1px solid transparent; }\n .skill-title { font-size: 1.6em; font-weight: 800; color: var(--text-primary); margin: 0 0 4px; line-height: 1.2; }\n .skill-owner { font-size: .82em; color: var(--text-muted2); margin: 0 0 14px; }\n .skill-owner span { color: #818cf8; }\n .skill-desc { font-size: .92em; color: var(--text-secondary); line-height: 1.65; margin: 0 0 16px; }\n .skill-meta { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border-card); }\n .meta-item { display: flex; flex-direction: column; gap: 2px; }\n .meta-label { font-size: .7em; color: var(--text-muted5); text-transform: uppercase; letter-spacing: 1px; font-weight: 600; }\n .meta-value { font-size: .92em; color: var(--text-muted2); font-weight: 600; }\n .tags-row { display: flex; gap: 6px; flex-wrap: wrap; }\n .tag { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 3px 10px; text-decoration: none; }\n .tag:hover { background: #6366f125; }\n .install-box { background: var(--bg-deep); border: 1px solid var(--border-card); border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .install-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--border-card); }\n .install-dots { display: flex; gap: 6px; }\n .dot { width: 10px; height: 10px; border-radius: 50%; }\n .install-label { font-size: .72em; color: var(--text-muted5); font-family: monospace; letter-spacing: 1px; }\n .install-body { padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n .install-cmd { color: var(--text-code);\n font-family: 'Courier New', monospace; font-size: 1em; }\n .copy-btn { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: all .15s; }\n .copy-btn:hover { background: #6366f125; }\n .btn-secondary { display: inline-flex; align-items: center; gap: 8px; padding: 13px 24px; background: transparent; border: 1px solid var(--border-card); border-radius: 10px; color: #6b7280; text-decoration: none; font-weight: 600; font-size: .95em; transition: all .15s; }\n .btn-secondary:hover { border-color: #818cf8; color: #818cf8; }\n .ours-badge { display: inline-flex; align-items: center; gap: 6px; font-size: .72em; font-weight: 700; color: #22d3ee; background: #22d3ee10; border: 1px solid #22d3ee30; border-radius: 999px; padding: 4px 14px; }\n .section-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 16px; padding: 22px 24px; margin-bottom: 20px; }\n .section-title { color: var(--text-primary); font-size: 1.08em; font-weight: 800; margin: 0 0 12px; display: flex; align-items: center; gap: 8px; }\n /* Script box */\n .script-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; background: var(--bg-input); border-bottom: 1px solid var(--border-card); }\n .script-filename { font-size: .72em; color: var(--text-muted2); font-family: 'Courier New', monospace; }\n .script-copy-btn { font-size: .72em; color: #6366f1; background: none; border: 1px solid #6366f130; border-radius: 4px; padding: 2px 10px; cursor: pointer; }\n .script-copy-btn:hover { background: #6366f115; }\n .script-body { padding: 14px 16px; font-family: 'Courier New', monospace; font-size: .82em; line-height: 1.6; color: var(--text-code); overflow-x: auto; max-height: 420px; overflow-y: auto; white-space: pre; }\n /* Articles */\n .article-card { display: block; background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: 10px; padding: 14px 16px; text-decoration: none; transition: border-color .15s; }\n .article-card:hover { border-color: #6366f1; }\n @media (max-width: 600px) {\n .skill-card { padding: 20px; }\n .skill-title { font-size: 1.5em; }\n }\n "])</script><script>self.__next_f.push([1,"15:[[\"$\",\"style\",null,{\"children\":\"$1f\"}],\"$L20\",\"$L21\"]\n"])</script><script>self.__next_f.push([1,"22:I[78297,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n23:T9b97,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\nname: olares-shared\nversion: 1.1.0\ndescription: \"Shared olares-cli foundation: profile model, first-time login (profile login with password + TOTP), bootstrapping a profile from an existing refresh token (profile import), switching/listing/removing profiles, the global --profile flag, where access/refresh tokens live in the OS keychain, automatic access_token refresh via /api/refresh (transparent reactive retry on 401/403; pro-active JWT-exp refresh for streaming uploads; cross-goroutine + cross-process deduplication via flock), and how to recover from auth errors (HTTP 401/403, refresh token invalidated, not logged in, two-factor authentication required). Use whenever the user is configuring olares-cli for the first time, logging in or importing credentials, switching/listing/removing profiles, asking how token refresh works, scripting parallel olares-cli invocations, or seeing 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'; also use when the user asks about refresh tokens, the keychain, olaresId, or profile management.\"\nmetadata:\n requires:\n bins: [\"olares-cli\"]\n cliHelp: \"olares-cli profile --help\"\n\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eolares-cli shared rules\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThis skill explains: what a profile is, how to obtain access credentials for it, where those credentials live, and how to recover when the server rejects a token. \u003cstrong style=\"color:#e5e7eb\"\u003eEvery \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli files ...\u003c/code\u003e (and other business) command depends on the profile selection + auth flow described here.\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eProfile model\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOne profile = one Olares instance + one user identity. The identity is uniquely keyed by an \u003cstrong style=\"color:#e5e7eb\"\u003eolaresId\u003c/strong\u003e (e.g. \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ealice@olares.com\u003c/code\u003e). Each profile owns its own access_token / refresh_token pair, stored in the OS keychain.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile\u003c/code\u003e command tree exposes 5 verbs (see \u003ca href=\"cli/cmd/ctl/profile/root.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/profile/root.go\u003c/code\u003e\u003c/a\u003e):\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Command | Purpose |\n|---------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login\u003c/code\u003e | Authenticate with a password (+ TOTP if 2FA is on); auto-creates the profile on first run (mode A) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile import\u003c/code\u003e | Bootstrap an access_token from an existing refresh_token (mode B) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile list\u003c/code\u003e | List every profile, mark the current one, show login status per profile |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile use \u003cname\\|-\u003e\u003c/code\u003e | Switch the current profile; \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e-\u003c/code\u003e reverts to the previous one (analogous to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecd -\u003c/code\u003e) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile remove \u003cname\u003e\u003c/code\u003e | Delete a profile and its stored token in one shot |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003e \u003cstrong style=\"color:#e5e7eb\"\u003eThere is no \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli auth login\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eauth logout\u003c/code\u003e namespace.\u003c/strong\u003e Every auth-related action lives under \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile\u003c/code\u003e. \"Logout\" is \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eGlobal \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--profile\u003c/code\u003e flag\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe root command registers a persistent \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--profile \u003colaresId\u003e\u003c/code\u003e flag (see \u003ca href=\"cli/cmd/ctl/root.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/root.go\u003c/code\u003e\u003c/a\u003e L57). It overrides the currently-selected profile for one invocation without flipping the persisted current pointer:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Doesn't change current; this single ls runs against alice's credentials.\nolares-cli files ls drive/Home/ --profile alice@olares.com\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUse this for: scripting parallel operations against multiple profiles, sanity-checking a specific profile's status, and avoiding pollution of the interactive terminal's current pointer.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eFirst-time login (mode A: password + optional TOTP)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eolares-cli profile login --olares-id \u003colaresId\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eBehavior (see \u003ca href=\"cli/cmd/ctl/profile/login.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/profile/login.go\u003c/code\u003e\u003c/a\u003e and \u003ca href=\"cli/cmd/ctl/profile/credentials.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/profile/credentials.go\u003c/code\u003e\u003c/a\u003e):\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eProfile does not exist → auto-created\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eProfile exists, token expired or invalidated → reuse the profile entry, write a fresh token\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eProfile exists, token still valid → \u003cstrong style=\"color:#e5e7eb\"\u003erejected\u003c/strong\u003e with a hint to run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile remove \u003cid\u003e\u003c/code\u003e first\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePassword\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDefault: read from the controlling TTY with echo disabled\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eScripts: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--password-stdin\u003c/code\u003e, e.g. \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprintf '%s' \"$PW\" | olares-cli profile login --olares-id \u003cid\u003e --password-stdin\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eThere is no \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--password \u003cplaintext\u003e\u003c/code\u003e flag.\u003c/strong\u003e The CLI deliberately omits it so passwords never leak into shell history or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eps\u003c/code\u003e output.\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003e2FA / TOTP\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the server's \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/firstfactor\u003c/code\u003e returns \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efa2=true\u003c/code\u003e, a second factor is required:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eTTY: the CLI prompts \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etwo-factor code for \u003cid\u003e:\u003c/code\u003e automatically; the user types the 6-digit code\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eNon-TTY (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--password-stdin\u003c/code\u003e, CI, etc.): you MUST pass \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--totp \u003ccode\u003e\u003c/code\u003e up front, otherwise the command fails with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etwo-factor authentication required: re-run with --totp \u003ccode\u003e\u003c/code\u003e\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003e \u003cstrong style=\"color:#e5e7eb\"\u003eThe CLI does not try to guess whether 2FA is enabled.\u003c/strong\u003e It probes every login with a targetURL that triggers Authelia's 2FA policy. Accounts without 2FA pass through transparently; accounts with 2FA get prompted for TOTP and then proceed.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eAgent-driven login (recommended pattern)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen you (an AI agent) drive the login on the user's behalf, \u003cstrong style=\"color:#e5e7eb\"\u003edo not\u003c/strong\u003e pass the password or TOTP as plaintext command-line arguments. Recommended flow:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Spawn \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login --olares-id \u003cid\u003e\u003c/code\u003e as a background process so it parks at the password prompt.\n2. Forward the prompt verbatim to the user and wait for them to type the password / TOTP into their own terminal.\n3. After the command exits, read its output to confirm whether the login succeeded.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAlternatively, instruct the user to run the login in their terminal themselves; the agent then takes over for follow-up command orchestration.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eBootstrap from an existing refresh_token (mode B)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf the user already has a refresh_token (from LarePass, the wizard activation flow, or any other source), there is no need to run through password + 2FA again:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eolares-cli profile import --olares-id \u003colaresId\u003e --refresh-token \u003ctok\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe CLI exchanges the refresh_token for an access_token via a single \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e call (see \u003ca href=\"cli/cmd/ctl/profile/import.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/profile/import.go\u003c/code\u003e\u003c/a\u003e) and writes both into the keychain. The \"reject if a valid token already exists\" rule from \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elogin\u003c/code\u003e applies here too.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003e \u003cstrong style=\"color:#e5e7eb\"\u003eNever write \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--refresh-token \u003ctok\u003e\u003c/code\u003e as plaintext in scripts.\u003c/strong\u003e Read it from an environment variable or a secret manager:\n\u003e \u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e\u003e olares-cli profile import --olares-id \u003cid\u003e --refresh-token \"$OLARES_REFRESH_TOKEN\"\n\u003e \u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSwitching and inspecting profiles\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile list\u003c/code\u003e\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOutput (see \u003ca href=\"cli/cmd/ctl/profile/list.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/profile/list.go\u003c/code\u003e\u003c/a\u003e):\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e NAME OLARES-ID STATUS\n* alice alice@olares.com logged-in (23h59m)\n bob bob@olares.com expired\n eve eve@olares.com invalidated\n frank frank@olares.com never\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| STATUS | Meaning | Recovery |\n|--------|---------|----------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elogged-in (Xh Ym)\u003c/code\u003e | Token is valid; column shows time-to-expiry | — |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elogged-in\u003c/code\u003e | Token is present but its JWT has no exp claim, so we can't verify locally | Trust until the server says no |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eexpired\u003c/code\u003e | Token JWT exp is in the past | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e to re-authenticate |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003einvalidated\u003c/code\u003e | The server explicitly rejected the refresh leg (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e returned 401/403) | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e directly — no need to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove\u003c/code\u003e first |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enever\u003c/code\u003e | No token has ever been stored for this profile | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe leading \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e*\u003c/code\u003e marks the current profile.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile use \u003cname|-\u003e\u003c/code\u003e\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eolares-cli profile use alice # by NAME alias\nolares-cli profile use alice@olares.com # by olaresId (also accepted)\nolares-cli profile use - # back to the previous current (errors when none)\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUpdates \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecurrentProfile\u003c/code\u003e and \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epreviousProfile\u003c/code\u003e inside \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.config/olares-cli/config.json\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove \u003cname\u003e\u003c/code\u003e\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eolares-cli profile remove alice\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003ePerforms four actions atomically:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Removes the profile entry from \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003econfig.json\u003c/code\u003e.\n2. Deletes the stored token for that olaresId from the keychain.\n3. If the removed profile was current, current falls back to the previous (when valid) or to the first remaining profile.\n4. If the removed profile was the last one, the keychain namespace itself is purged so no orphan entries remain in Keychain Access.app / regedit / etc.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eToken storage\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| OS | Backend | Location |\n|------|---------|----------|\n| darwin | macOS Keychain | service \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli\u003c/code\u003e, account = olaresId |\n| linux | AES-256-GCM file | under \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.local/share/olares-cli/\u003c/code\u003e |\n| windows | DPAPI | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eHKCU\\Software\\OlaresCli\\keychain\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eThe plaintext \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.olares-cli/tokens.json\u003c/code\u003e from older builds is deprecated\u003c/strong\u003e — tokens written there by previous versions are no longer read. If the user upgraded and suddenly appears \"logged out\", the correct fix is \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e to repopulate the new storage.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAfter \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elogin\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eimport\u003c/code\u003e succeeds, the CLI prints a line like \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etoken stored via \u003cbackend\u003e (service \"olares-cli\", account \"\u003cid\u003e\")\u003c/code\u003e. That message is the source of truth for \"where did my token actually land\". If the backend resolves to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efile-fallback\u003c/code\u003e (sandboxed / CI environments without access to a system keychain), be aware: that token is now sitting in a file with \u003cstrong style=\"color:#e5e7eb\"\u003edifferent security properties than the system keychain\u003c/strong\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eRe-authentication rules (critical)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e and \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e both apply the same logic per olaresId:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e ┌───────────────────────────────────────┐\nprofile not exist ──▶│ Auto-create and write the new token │\n └───────────────────────────────────────┘\n ┌──────────────────────────────────────────────┐\ntoken expired ──▶│ Reuse the profile entry, write a new token │\n └──────────────────────────────────────────────┘\n ┌──────────────────────────────────────────────┐\ntoken invalidated ──▶│ Reuse the profile entry, write a new token │\n └──────────────────────────────────────────────┘\n ┌──────────────────────────────────────────────────────────┐\ntoken still valid ──▶│ Reject; tell the user to run profile remove \u003cid\u003e first │\n └──────────────────────────────────────────────────────────┘\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eLogic lives in \u003ca href=\"cli/cmd/ctl/profile/credentials.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecmd/ctl/profile/credentials.go\u003c/code\u003e\u003c/a\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eensureProfileWritable\u003c/code\u003e. If a script needs unconditional overwrite, it MUST \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove\u003c/code\u003e first and then \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eAutomatic token refresh\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eThe CLI rotates expired access_tokens transparently.\u003c/strong\u003e Users do NOT need to run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e just because their access_token aged out — only when the *refresh_token* itself becomes invalid.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe refresh logic lives in \u003ca href=\"cli/pkg/cmdutil/factory.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecli/pkg/cmdutil/factory.go\u003c/code\u003e\u003c/a\u003e (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003erefreshingTransport\u003c/code\u003e) and \u003ca href=\"cli/pkg/credential/refresher.go\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecli/pkg/credential/refresher.go\u003c/code\u003e\u003c/a\u003e. Every \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e*http.Client\u003c/code\u003e the Factory hands out has it wired in.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eTwo trigger paths\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Trigger | Applies to | Behavior |\n|---------|------------|----------|\n| \u003cstrong style=\"color:#e5e7eb\"\u003eReactive (401/403 + retry)\u003c/strong\u003e | Replayable bodies — every JSON / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efiles cat\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efiles download\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efiles rm\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003emarket\u003c/code\u003e JSON verb | Send with current token. On 401/403, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e, retry the same request once with the new token. One extra round-trip in the rare expiry case; zero overhead in steady state. |\n| \u003cstrong style=\"color:#e5e7eb\"\u003ePro-active (JWT exp + skew)\u003c/strong\u003e | Non-replayable bodies — \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efiles upload\u003c/code\u003e chunks (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e*os.File\u003c/code\u003e) | Decode the access_token's JWT exp before sending. If within 60s of expiry (or already past), \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e first, send with the new token. Required because once a streaming body is consumed by the first send we can't replay it on a 401. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe pro-active skew is hardcoded at 60s in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecli/pkg/cmdutil/factory.go\u003c/code\u003e (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epreflightSkew\u003c/code\u003e) — comfortably absorbs client↔server clock drift plus the time from local decode to the request landing on the server. Tokens issued without an \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eexp\u003c/code\u003e claim, or values that don't decode as a JWT at all, skip the pre-flight gracefully and fall back to the reactive path.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eConcurrency\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAcross goroutines AND across concurrent \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli\u003c/code\u003e processes, \u003cstrong style=\"color:#e5e7eb\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e is hit at most once per stale token\u003c/strong\u003e:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Process-wide \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003esync.Mutex\u003c/code\u003e — losers wait, then read whatever the winner persisted.\n2. Compare-after-Get against the keychain — short-circuits when a sibling already rotated the token.\n3. On-disk \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eflock\u003c/code\u003e under \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cconfig-dir\u003e/locks/\u003csanitized-olaresId\u003e.refresh.lock\u003c/code\u003e — serializes across processes; bounded by a 30s acquire timeout so a stuck peer can't hang the CLI.\n4. Re-check inside the flock — collapses any final race that snuck in between the in-process and on-disk locks.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eFor most users this is invisible. It matters when you script multiple \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli\u003c/code\u003e invocations in parallel: they will not stampede \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eWhen refresh itself fails\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Outcome | What the user sees | Fix |\n|---------|---------------------|-----|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e returns 200 + new tokens | (silent — request retried, command succeeds) | — |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e returns 401/403 (refresh_token revoked / expired / rotated by another login) | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003erefresh token for \u003cid\u003e became invalid at \u003cts\u003e; please run: olares-cli profile login --olares-id \u003cid\u003e\u003c/code\u003e (typed \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e*ErrTokenInvalidated\u003c/code\u003e) | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login --olares-id \u003cid\u003e\u003c/code\u003e |\n| No token in keychain at all | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eno access token for \u003cid\u003e; run: olares-cli profile login --olares-id \u003cid\u003e\u003c/code\u003e (typed \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e*ErrNotLoggedIn\u003c/code\u003e) | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login\u003c/code\u003e (or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e returns 5xx / network error | Surfaced verbatim from the transport | Retry the command — the grant itself is still valid |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe keychain entry is stamped \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eInvalidatedAt\u003c/code\u003e on the 401 path so subsequent commands skip the network round-trip and go straight to the CTA. \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile list\u003c/code\u003e shows these as \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003einvalidated\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003e \u003cstrong style=\"color:#e5e7eb\"\u003eDo not implement custom retry/backoff loops on top of these errors.\u003c/strong\u003e The transport already handles the recoverable cases; once you see \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eErrTokenInvalidated\u003c/code\u003e or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eErrNotLoggedIn\u003c/code\u003e, only \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile login\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e will help.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eAuth error recovery table\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Error message (excerpt) | Meaning | Fix |\n|-------------------------|---------|-----|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003erefresh token for \u003cid\u003e became invalid at \u003cts\u003e\u003c/code\u003e | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/refresh\u003c/code\u003e itself returned 401/403 — the grant is dead | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login --olares-id \u003cid\u003e\u003c/code\u003e |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eno access token for \u003cid\u003e\u003c/code\u003e | Profile selected but keychain has no entry | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login\u003c/code\u003e or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eserver rejected the access token (HTTP 401)\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e(HTTP 403)\u003c/code\u003e | After auto-refresh the server still rejects (rare; usually a server-side state drift) | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile login --olares-id \u003cid\u003e\u003c/code\u003e |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--olares-id is required\u003c/code\u003e | login / import was invoked without olaresId | Add \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--olares-id \u003cid\u003e\u003c/code\u003e |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ealready authenticated for \u003cid\u003e (expires in ...)\u003c/code\u003e | A still-valid token exists for this olaresId | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eolares-cli profile remove \u003cid\u003e\u003c/code\u003e and re-run login / import |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ea token is already stored for \u003cid\u003e but its expiry can't be determined client-side\u003c/code\u003e | Token present but JWT carries no exp claim, so we conservatively reject | Same: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove \u003cid\u003e\u003c/code\u003e and re-run |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etwo-factor authentication required: re-run with --totp \u003ccode\u003e\u003c/code\u003e | 2FA is on and we're in a non-TTY context (no way to prompt) | Re-run with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--totp \u003ccode\u003e\u003c/code\u003e, or run interactively in a TTY |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epassword is empty\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eTOTP code is empty\u003c/code\u003e | stdin / TTY returned an empty string | Check for premature EOF or an empty pipe |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile \u003cname\u003e not found\u003c/code\u003e | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile use\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove\u003c/code\u003e referenced an unknown profile | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile list\u003c/code\u003e to see the actual names |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003e \u003cstrong style=\"color:#e5e7eb\"\u003eDo not silently retry auth errors.\u003c/strong\u003e 401/403 after auto-refresh and \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ealready authenticated\u003c/code\u003e are deterministic — follow the table; blind retries make the situation worse.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003edev / internal flags\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eFor internal debugging or self-hosted dev environments only — \u003cstrong style=\"color:#e5e7eb\"\u003enever include these in user-facing examples or scripts\u003c/strong\u003e:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Flag | Use |\n|------|-----|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--auth-url-override \u003curl\u003e\u003c/code\u003e | Hard-pin the Authelia URL instead of deriving it from olaresId |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--local-url-prefix \u003clabel\u003e\u003c/code\u003e | Inject an extra label between the auth subdomain and the terminus name |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--insecure-skip-verify\u003c/code\u003e | Disable TLS verification (only for self-signed local environments) |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSecurity rules\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNever\u003c/strong\u003e invent a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--password \u003cplaintext\u003e\u003c/code\u003e argument (it does not exist). Passwords go through the TTY or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--password-stdin\u003c/code\u003e fed by a secret pipe.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNever\u003c/strong\u003e echo \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eaccess_token\u003c/code\u003e / \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003erefresh_token\u003c/code\u003e to the terminal. When passing a refresh_token to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile import\u003c/code\u003e, source it from an environment variable or external secret store: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--refresh-token \"$OLARES_REFRESH_TOKEN\"\u003c/code\u003e.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eConfirm intent before write/delete actions\u003c/strong\u003e (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprofile remove\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efiles rm\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efiles upload --overwrite\u003c/code\u003e, ...). Do not act unilaterally on the user's behalf.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eTOTP is not a password\u003c/strong\u003e — it is single-use and short-lived, so the CLI deliberately echoes it to make manual entry less error-prone (matching \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003egh auth login\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eaws sso login\u003c/code\u003e, kubectl OIDC plugins). That said, never persist a TOTP in a shared script.\u003c/li\u003e\n\u003c/p\u003e"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"skill-page\",\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"SoftwareApplication\\\",\\\"name\\\":\\\"Olares Shared (olares-cli foundation)\\\",\\\"description\\\":\\\"Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dash...\\\",\\\"url\\\":\\\"https://bytesagain.com/skill/olares-shared\\\",\\\"applicationCategory\\\":\\\"requires-oauth-token\\\",\\\"operatingSystem\\\":\\\"Any\\\",\\\"offers\\\":{\\\"@type\\\":\\\"Offer\\\",\\\"price\\\":\\\"0\\\",\\\"priceCurrency\\\":\\\"USD\\\"},\\\"publisher\\\":{\\\"@type\\\":\\\"Organization\\\",\\\"name\\\":\\\"BytesAgain\\\",\\\"url\\\":\\\"https://bytesagain.com\\\"}}\"}}],[\"$\",\"div\",null,{\"className\":\"breadcrumb\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/\",\"children\":\"BytesAgain\"}],\" › \",[\"$\",\"a\",null,{\"href\":\"/skills\",\"children\":\"Skills\"}],\" › \",\"Olares Shared (olares-cli foundation)\"]}],[\"$\",\"div\",null,{\"className\":\"two-col\",\"children\":[[\"$\",\"div\",null,{\"className\":\"two-col-main\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-card\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-badges\",\"children\":[[\"$\",\"span\",null,{\"className\":\"badge\",\"style\":{\"color\":\"#818cf8\",\"background\":\"#818cf822\",\"borderColor\":\"#818cf844\"},\"children\":[\"🦀\",\" \",\"ClawHub\"]}],false]}],[\"$\",\"div\",null,{\"className\":\"skill-top-actions\",\"children\":[\"$\",\"$L22\",null,{\"slug\":\"olares-shared\"}]}]]}],[\"$\",\"h1\",null,{\"className\":\"skill-title\",\"children\":\"Olares Shared (olares-cli foundation)\"}],[\"$\",\"p\",null,{\"className\":\"skill-owner\",\"children\":[\"by \",[\"$\",\"span\",null,{\"children\":[\"@\",\"olares\"]}]]}],[\"$\",\"p\",null,{\"className\":\"skill-desc\",\"children\":\"Olares profile and authentication foundation for olares-cli — required prerequisite for every other olares-cli skill on Olares (files, market, settings, dash...\"}],[\"$\",\"div\",null,{\"className\":\"skill-meta\",\"children\":[[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Version\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":[\"v\",\"4.0.1\"]}]]}],[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Downloads\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":\"649\"}]]}],false,false,false,[\"$\",\"div\",null,{\"className\":\"meta-item\",\"style\":{\"flexDirection\":\"row\",\"gap\":6,\"alignItems\":\"center\"},\"children\":[[\"$\",\"a\",\"crypto-defi\",{\"href\":\"/?q=crypto-defi\",\"className\":\"tag\",\"children\":[\"#\",\"crypto-defi\"]}],[\"$\",\"a\",\"security\",{\"href\":\"/?q=security\",\"className\":\"tag\",\"children\":[\"#\",\"security\"]}],[\"$\",\"a\",\"legal\",{\"href\":\"/?q=legal\",\"className\":\"tag\",\"children\":[\"#\",\"legal\"]}]]}]]}],[\"$\",\"div\",null,{\"style\":{\"marginTop\":6},\"children\":[\"$\",\"a\",null,{\"href\":\"https://clawhub.ai/olares/olares-shared\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"btn-secondary\",\"style\":{\"padding\":\"6px 12px\",\"fontSize\":\".82em\",\"borderRadius\":8,\"background\":\"transparent\",\"border\":\"1px solid var(--border-card)\",\"color\":\"var(--text-muted2)\",\"textDecoration\":\"none\",\"whiteSpace\":\"nowrap\"},\"children\":[\"View on \",\"ClawHub\",\" →\"]}]}]]}],[\"$\",\"div\",null,{\"className\":\"install-box\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-dots\",\"children\":[[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#ef4444\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#eab308\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#22c55e\"}}]]}],[\"$\",\"span\",null,{\"className\":\"install-label\",\"children\":\"TERMINAL\"}]]}],[\"$\",\"div\",null,{\"className\":\"install-body\",\"style\":{\"flexWrap\":\"wrap\"},\"children\":[[\"$\",\"code\",null,{\"className\":\"install-cmd\",\"children\":\"clawhub install olares-shared\"}],[\"$\",\"button\",null,{\"className\":\"copy-btn\",\"data-cmd\":\"clawhub install olares-shared\",\"style\":{\"fontWeight\":700},\"children\":\"Copy\"}]]}]]}],[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"📖 About This Skill\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$23\"}}]]}],null,null,null,null,null,null,null,false,false]}],\"$L24\"]}]]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn =\u003e {\\n btn.addEventListener('click', () =\u003e {\\n const cmd = btn.getAttribute('data-cmd');\\n if (!cmd) return;\\n navigator.clipboard.writeText(cmd).then(() =\u003e {\\n const orig = btn.textContent;\\n btn.textContent = 'Copied!';\\n setTimeout(() =\u003e btn.textContent = orig, 1500);\\n }).catch(() =\u003e {});\\n });\\n });\\n \"}}]\n"])</script><script>self.__next_f.push([1,"25:I[71521,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n24:[\"$\",\"div\",null,{\"className\":\"two-col-side\",\"children\":[\"$\",\"$L25\",null,{\"category\":\"requires-oauth-token\",\"currentSlug\":\"olares-shared\",\"name\":\"Olares Shared (olares-cli foundation)\",\"tags\":[\"crypto-defi\",\"security\",\"legal\"]}]}]\n"])</script></body></html>