feat: auth vault phases 3+4 - failover, isolation, encryption (#23) - #34
Conversation
auth run: wrap CLI with auto-failover on rate limit (5 retries). auth isolate add/ls/delete: isolated HOME profiles for parallel sessions. auth exec: run command with isolated profile HOME. auth login: login flow with auto-backup on success. auth_crypt: AES-256-GCM vault encryption with PBKDF2 key derivation. Copilot added to auth catalog. Daemon detection helpers for codex app-server.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds AES-256-GCM passphrase-based encryption for vault auth files, an isolate subsystem for running agents under redirected HOME directories, a retry-capable auth runner with daemon detection/reload, new CLI subcommands wiring these features, and new Winget package manifest files for v1.1.0. ChangesEncrypted vault and isolated auth execution
Estimated code review effort: 4 (Complex) | ~60 minutes Winget Package Manifests
Estimated code review effort: 1 (Trivial) | ~3 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant auth as auth.rs
participant auth_crypt
participant Vault as Vault filesystem
User->>auth: backup()
auth->>auth_crypt: get_passphrase()
auth_crypt-->>auth: passphrase or None
alt passphrase present
auth->>auth_crypt: encrypt(file bytes, passphrase)
auth_crypt-->>auth: nonce||ciphertext
auth->>Vault: write encrypted blob
else no passphrase
auth->>Vault: write plaintext
end
User->>auth: activate()
auth->>Vault: read stored file
alt passphrase present
auth->>auth_crypt: decrypt(blob, passphrase)
auth_crypt-->>auth: plaintext or None
alt decryption fails
auth-->>User: warn and skip file
else success
auth->>Vault: write decrypted file to HOME
end
else no passphrase
auth->>Vault: write plaintext to HOME
end
sequenceDiagram
participant CLI
participant auth_runner
participant Agent as Agent process
participant auth_db
participant auth as auth.rs
CLI->>auth_runner: run(agent, args, json)
loop up to MAX_RETRIES
auth_runner->>Agent: spawn_and_capture()
Agent-->>auth_runner: exit code, stderr
auth_runner->>auth_runner: categorize_exit()
alt RateLimited
auth_runner->>auth_db: record cooldown for profile
auth_runner->>auth: rotate to next profile
auth_runner->>CLI: log retry (unless json)
else Success
auth_runner-->>CLI: return
else Failure
auth_runner-->>CLI: exit(code)
end
end
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
src/auth_runner.rs (1)
16-44: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider a short backoff between retries.
On a rate-limit, the loop rotates and immediately re-spawns. If rotation yields the same (or another already-limited) profile, it can burn all 5 retries near-instantly without giving quotas time to recover. A brief sleep (optionally increasing) before retrying would make the failover more effective.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/auth_runner.rs` around lines 16 - 44, The retry loop in auth_runner::spawn_and_capture handling ExitKind::RateLimited rotates profiles and immediately respawns, which can exhaust retries too quickly. Add a short backoff before the next iteration in this branch, ideally after auth_db::set_cooldown and crate::auth::rotate, and consider making it small but increasing per retry so repeated rate limits have time to recover.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/auth_crypt.rs`:
- Around line 20-27: The interactive passphrase prompt in prompt_passphrase
currently uses stdin::read_line, which echoes the secret to the terminal.
Replace the input path with a hidden-entry reader such as rpassword so typed
characters are not displayed, while keeping the existing prompt and empty-input
handling in prompt_passphrase unchanged.
- Line 8: The PBKDF2 setup in auth_crypt uses a fixed compile-time SALT and a
low iteration count, so update the key derivation in
auth_crypt::encrypt/auth_crypt::decrypt to generate a unique random salt per
vault or per ciphertext and persist it alongside the encrypted payload, then
read it back during decryption. Also raise the PBKDF2 iteration count from the
current hardcoded value to a substantially higher one aligned with current
guidance, and keep the salt/iteration handling centralized so both encryption
and decryption stay in sync.
In `@src/auth_runner.rs`:
- Around line 65-72: The spawn-and-capture path currently panics on launch
failure via the spawn_agent expectation, which gives a bad CLI experience when
the binary is missing or not executable. Update spawn_and_capture to handle the
Command::new(...).spawn() failure gracefully, mirroring auth_exec: report a
user-friendly error, include the agent/binary context, and exit with a non-zero
status instead of panicking.
- Around line 129-149: `reload_daemon` only handles the `codex` agent, so
`claude-code` and other agents end up with empty process names and a bad
`pkill`/no-op path. Update the agent-to-process mapping in `reload_daemon` (and
keep it consistent with `daemon_running`) so `claude-code` is handled
explicitly, or add an early return for unsupported agents before invoking
`pkill`/`taskkill`. Use the existing `agent` match and `names` selection to
locate the fix.
In `@src/auth.rs`:
- Around line 881-894: The activate_into helper is copying vault files directly
with fs::copy instead of using the decrypt-aware restore flow. Update
activate_into to reuse the same passphrase-aware restoration logic as activate,
using the existing vault/profile handling in auth::activate and
profile_dir/catalog_for so files are decrypted before being written into
target_dir. Ensure the restore path handles both encrypted and unencrypted
backups consistently, rather than manually copying rel-targeted files.
- Around line 862-872: The post-login backup path in auth_login is writing vault
files with plain fs::copy instead of the encrypted backup flow, so update this
loop to use the same auth_crypt::encrypt-backed path as backup() and preserve
the shared format marker. Locate the logic that iterates CATALOG files after a
successful login, and replace direct copying of src to dest with the same
encryption/serialization step used by backup() so the new auth state is stored
encrypted at rest.
- Around line 803-835: Set the isolated home environment variables in auth_exec
and auth_login, not just HOME, because Windows programs may ignore HOME and read
USERPROFILE (and sometimes HOMEDRIVE/HOMEPATH) instead. Update the command
environment setup around std::process::Command in auth_exec and the login flow
in auth_login so the isolated profile directory is exposed through the
platform-appropriate home variables alongside HOME, using the existing
agent/profile directory logic as the source of truth.
- Around line 94-105: The backup logic in `backup()` currently writes auth file
bytes without any explicit format marker, so later readers like `activate()`,
`activate_into()`, and `auth_login()` can misinterpret plaintext vs encrypted
vault entries based only on the current passphrase state. Update the vault write
path in `backup()` to prefix each stored entry with a small header/version that
records whether the payload is plaintext or ciphertext, and then change the
corresponding read/restore paths to branch on that marker instead of inferring
from `get_passphrase()`. Use the existing `auth_crypt` and `vault` handling in
`src/auth.rs` as the main place to locate and apply the fix.
---
Nitpick comments:
In `@src/auth_runner.rs`:
- Around line 16-44: The retry loop in auth_runner::spawn_and_capture handling
ExitKind::RateLimited rotates profiles and immediately respawns, which can
exhaust retries too quickly. Add a short backoff before the next iteration in
this branch, ideally after auth_db::set_cooldown and crate::auth::rotate, and
consider making it small but increasing per retry so repeated rate limits have
time to recover.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6fe3a3a6-5493-41be-a919-5ef8a8c9ea01
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
Cargo.tomlsrc/auth.rssrc/auth_crypt.rssrc/auth_runner.rssrc/main.rswinget/getappz.agentflare.installer.yamlwinget/getappz.agentflare.locale.en-US.yamlwinget/getappz.agentflare.yaml
| use sha2::Sha256; | ||
| use std::io::Write; | ||
|
|
||
| const SALT: &[u8] = b"agentflare-vault-salt-v1"; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Hardcoded salt undermines PBKDF2; iteration count is low.
SALT is a fixed compile-time constant, so every installation derives the key solely from the passphrase. This removes the salt's purpose (per-vault uniqueness / precomputation resistance) and means a single precomputed dictionary attacks all vaults. Prefer a random per-vault (or per-file) salt persisted alongside the ciphertext, similar to how the nonce is already prepended in encrypt.
Additionally, 100,000 iterations is below current guidance for PBKDF2-HMAC-SHA256 (OWASP suggests ~600,000). Consider raising it.
Also applies to: 29-31
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth_crypt.rs` at line 8, The PBKDF2 setup in auth_crypt uses a fixed
compile-time SALT and a low iteration count, so update the key derivation in
auth_crypt::encrypt/auth_crypt::decrypt to generate a unique random salt per
vault or per ciphertext and persist it alongside the encrypted payload, then
read it back during decryption. Also raise the PBKDF2 iteration count from the
current hardcoded value to a substantially higher one aligned with current
guidance, and keep the salt/iteration handling centralized so both encryption
and decryption stay in sync.
| fn prompt_passphrase() -> Option<String> { | ||
| print!("vault passphrase: "); | ||
| std::io::stdout().flush().ok(); | ||
| let mut input = String::new(); | ||
| std::io::stdin().read_line(&mut input).ok()?; | ||
| let pw = input.trim().to_string(); | ||
| if pw.is_empty() { None } else { Some(pw) } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Passphrase is echoed to the terminal.
std::io::stdin().read_line echoes typed characters, so the vault passphrase is visible on screen (and any terminal capture). Use a hidden-input reader (e.g. the rpassword crate) for the interactive prompt.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth_crypt.rs` around lines 20 - 27, The interactive passphrase prompt in
prompt_passphrase currently uses stdin::read_line, which echoes the secret to
the terminal. Replace the input path with a hidden-entry reader such as
rpassword so typed characters are not displayed, while keeping the existing
prompt and empty-input handling in prompt_passphrase unchanged.
| fn spawn_and_capture(agent: &str, args: &[String]) -> (i32, String) { | ||
| let binary = find_binary(agent); | ||
| let mut child = Command::new(&binary) | ||
| .args(args) | ||
| .stdout(Stdio::inherit()) | ||
| .stderr(Stdio::piped()) | ||
| .spawn() | ||
| .expect("spawn agent"); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Panic on spawn failure yields a poor CLI experience.
.expect("spawn agent") aborts with a Rust panic/backtrace if the binary is missing or not executable. Since find_binary falls back to the bare agent name, this is a reachable path. Prefer graceful error reporting + exit, consistent with auth_exec.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth_runner.rs` around lines 65 - 72, The spawn-and-capture path
currently panics on launch failure via the spawn_agent expectation, which gives
a bad CLI experience when the binary is missing or not executable. Update
spawn_and_capture to handle the Command::new(...).spawn() failure gracefully,
mirroring auth_exec: report a user-friendly error, include the agent/binary
context, and exit with a non-zero status instead of panicking.
| let names = match agent { | ||
| "codex" => &["codex"][..], | ||
| _ => &[], | ||
| }; | ||
| // SIGTERM on Unix, taskkill on Windows | ||
| #[cfg(windows)] | ||
| { | ||
| for name in names { | ||
| std::process::Command::new("taskkill") | ||
| .args(["/IM", &format!("{name}.exe")]) | ||
| .output() | ||
| .map_err(|e| format!("taskkill: {e}"))?; | ||
| } | ||
| } | ||
| #[cfg(not(windows))] | ||
| { | ||
| std::process::Command::new("pkill") | ||
| .args(names) | ||
| .output() | ||
| .map_err(|e| format!("pkill: {e}"))?; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
reload_daemon silently no-ops (or misfires) for non-codex agents.
daemon_running recognizes claude-code, but here names is &[] for anything but codex. On Unix this invokes pkill with no pattern, which returns a usage error rather than reloading anything; on Windows the loop body never runs. Either handle claude-code explicitly or return early for unsupported agents to avoid the empty-args call.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth_runner.rs` around lines 129 - 149, `reload_daemon` only handles the
`codex` agent, so `claude-code` and other agents end up with empty process names
and a bad `pkill`/no-op path. Update the agent-to-process mapping in
`reload_daemon` (and keep it consistent with `daemon_running`) so `claude-code`
is handled explicitly, or add an early return for unsupported agents before
invoking `pkill`/`taskkill`. Use the existing `agent` match and `names`
selection to locate the fix.
| let passphrase = auth_crypt::get_passphrase(); | ||
| for &rel in cat.files { | ||
| let src = home().join(rel); | ||
| let dest = vault.join(rel.rsplit('/').next().unwrap_or(rel)); | ||
| if src.exists() { | ||
| fs::copy(&src, &dest).expect("copy"); | ||
| let data = fs::read(&src).expect("read"); | ||
| if let Some(ref pw) = passphrase { | ||
| let encrypted = auth_crypt::encrypt(&data, pw).expect("encrypt"); | ||
| fs::write(&dest, encrypted).expect("write"); | ||
| } else { | ||
| fs::write(&dest, data).expect("write"); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant functions and show compact structure first.
ast-grep outline src/auth.rs --view expanded >/tmp/auth_outline.txt || true
sed -n '1,220p' /tmp/auth_outline.txt
# Show the relevant line ranges around the reported locations and related helpers.
nl -ba src/auth.rs | sed -n '70,130p'
echo '---'
nl -ba src/auth.rs | sed -n '170,220p'
echo '---'
nl -ba src/auth.rs | sed -n '850,910p'
echo '---'
nl -ba src/auth.rs | sed -n '1,70p'Repository: getappz/agentflare
Length of output: 3521
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for any magic header / version marker / encryption marker logic.
rg -n "magic|header|version|marker|encrypted|decrypt|encrypt|passphrase|get_passphrase|fs::copy|activate_into|auth_login|backup\(" src/auth.rs src -g '!target' || true
# Inspect any types or helper functions that might tag encrypted payloads.
python3 - <<'PY'
from pathlib import Path
p = Path('src/auth.rs')
text = p.read_text()
for needle in ['get_passphrase', 'encrypt(', 'decrypt(', 'activate_into', 'auth_login', 'backup(']:
idx = text.find(needle)
print(f'[{needle}] first_occurrence={idx}')
PYRepository: getappz/agentflare
Length of output: 20872
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the relevant sections with line numbers using tools available in the sandbox.
sed -n '81,210p' src/auth.rs | cat -n
echo '---'
sed -n '837,905p' src/auth.rs | cat -n
echo '---'
sed -n '907,980p' src/auth.rs | cat -n
# Also show the auth_crypt helpers.
echo '---'
sed -n '1,90p' src/auth_crypt.rs | cat -nRepository: getappz/agentflare
Length of output: 14208
Add an explicit vault-format marker before copying auth files
backup() writes either ciphertext or plaintext, but activate(), activate_into(), and auth_login() infer how to handle the bytes from the current passphrase state. That can write encrypted vault data back into live auth files as raw ciphertext, or treat plaintext vault files as encrypted and skip them. Prefix vault entries with a small header/version and branch on that instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth.rs` around lines 94 - 105, The backup logic in `backup()` currently
writes auth file bytes without any explicit format marker, so later readers like
`activate()`, `activate_into()`, and `auth_login()` can misinterpret plaintext
vs encrypted vault entries based only on the current passphrase state. Update
the vault write path in `backup()` to prefix each stored entry with a small
header/version that records whether the payload is plaintext or ciphertext, and
then change the corresponding read/restore paths to branch on that marker
instead of inferring from `get_passphrase()`. Use the existing `auth_crypt` and
`vault` handling in `src/auth.rs` as the main place to locate and apply the fix.
| pub fn auth_exec(agent: &str, profile: &str, args: &[String], json: bool) { | ||
| let dir = isolates_dir().join(agent).join(profile); | ||
| if !dir.exists() { | ||
| if json { | ||
| println!("{}", serde_json::json!({"error": "isolated profile not found"})); | ||
| } else { | ||
| eprintln!("error: isolated profile '{agent}/{profile}' not found — run 'auth isolate add' first"); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| if args.is_empty() { | ||
| eprintln!("error: no command specified after --"); | ||
| return; | ||
| } | ||
|
|
||
| let binary = &args[0]; | ||
| let rest = &args[1..]; | ||
|
|
||
| let status = std::process::Command::new(binary) | ||
| .args(rest) | ||
| .env("HOME", &dir) | ||
| .spawn() | ||
| .and_then(|mut c| c.wait()) | ||
| .unwrap_or_else(|e| { | ||
| eprintln!("error: {e}"); | ||
| std::process::exit(1); | ||
| }); | ||
|
|
||
| if !status.success() { | ||
| std::process::exit(status.code().unwrap_or(1)); | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '760,880p' src/auth.rsRepository: getappz/agentflare
Length of output: 4055
🏁 Script executed:
rg -n --hidden --glob '!target' --glob '!node_modules' 'USERPROFILE|HOMEDRIVE|HOMEPATH|HOME' src README.md Cargo.tomlRepository: getappz/agentflare
Length of output: 1164
🏁 Script executed:
sed -n '1,120p' src/paths.rs && printf '\n---\n' && sed -n '150,180p' README.md && printf '\n---\n' && sed -n '288,340p' src/main.rsRepository: getappz/agentflare
Length of output: 4765
🏁 Script executed:
rg -n --hidden --glob '!target' --glob '!node_modules' 'install\.ps1|cross-compile|Windows|USERPROFILE|HOMEDRIVE|HOMEPATH' README.md src .githubRepository: getappz/agentflare
Length of output: 1776
Set the Windows home variables too
auth_exec and auth_login only override HOME. On Windows, many programs read USERPROFILE (and sometimes HOMEDRIVE/HOMEPATH) for the home directory, so the isolated profile can be ignored there. Set the platform-appropriate variable(s) alongside HOME.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth.rs` around lines 803 - 835, Set the isolated home environment
variables in auth_exec and auth_login, not just HOME, because Windows programs
may ignore HOME and read USERPROFILE (and sometimes HOMEDRIVE/HOMEPATH) instead.
Update the command environment setup around std::process::Command in auth_exec
and the login flow in auth_login so the isolated profile directory is exposed
through the platform-appropriate home variables alongside HOME, using the
existing agent/profile directory logic as the source of truth.
| if status.success() { | ||
| // After login, backup the new auth state | ||
| let dir = isolates_dir().join(agent).join(profile); | ||
| for &rel in CATALOG.iter().find(|c| c.agent_key == agent).map(|c| c.files).unwrap_or(&[]) { | ||
| let dest = profile_dir(agent, profile).join(rel.rsplit('/').next().unwrap_or(rel)); | ||
| let src = dir.join(rel); | ||
| if src.exists() { | ||
| fs::create_dir_all(dest.parent().unwrap()).ok(); | ||
| fs::copy(&src, &dest).ok(); | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
auth_login writes post-login auth into the vault unencrypted.
The backup loop here uses fs::copy directly, bypassing auth_crypt::encrypt. Credentials captured via the login flow are therefore stored in plaintext in the vault, inconsistent with backup() and defeating encryption-at-rest for exactly the secrets this flow produces. Route these through the same encryption path (and shared format marker) as backup().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth.rs` around lines 862 - 872, The post-login backup path in auth_login
is writing vault files with plain fs::copy instead of the encrypted backup flow,
so update this loop to use the same auth_crypt::encrypt-backed path as backup()
and preserve the shared format marker. Locate the logic that iterates CATALOG
files after a successful login, and replace direct copying of src to dest with
the same encryption/serialization step used by backup() so the new auth state is
stored encrypted at rest.
| fn activate_into(agent: &str, profile: &str, target_dir: &std::path::Path) { | ||
| let cat = match catalog_for(agent) { Some(c) => c, None => { return; } }; | ||
| let vault = profile_dir(agent, profile); | ||
| for &rel in cat.files { | ||
| let src = vault.join(rel.rsplit('/').next().unwrap_or(rel)); | ||
| if src.exists() { | ||
| let dest = target_dir.join(rel); | ||
| if let Some(parent) = dest.parent() { | ||
| fs::create_dir_all(parent).ok(); | ||
| } | ||
| fs::copy(&src, &dest).ok(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift
activate_into copies vault files without decrypting.
Unlike activate(), this helper does a raw fs::copy (Line 891) with no passphrase handling. When the vault was backed up with a passphrase, the isolate HOME receives encrypted bytes, so the agent reads corrupted credentials. It should reuse the same decrypt-aware restore path as activate().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/auth.rs` around lines 881 - 894, The activate_into helper is copying
vault files directly with fs::copy instead of using the decrypt-aware restore
flow. Update activate_into to reuse the same passphrase-aware restoration logic
as activate, using the existing vault/profile handling in auth::activate and
profile_dir/catalog_for so files are decrypted before being written into
target_dir. Ensure the restore path handles both encrypted and unencrypted
backups consistently, rather than manually copying rel-targeted files.
Triggers on release published. Uses komac to update winget manifests automatically and create PR against microsoft/winget-pkgs. Keeps last 5 versions via komac cleanup.
| runs-on: windows-latest | ||
| steps: | ||
| - name: Install komac | ||
| run: winget install --id russellbanks.Komac --accept-source-agreements --accept-package-agreements | ||
|
|
||
| - name: Update winget manifest | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| KOMAC_FORK_OWNER: getappz | ||
| run: | | ||
| $id = "getappz.agentflare" | ||
| $urls = gh release view --json assets --jq '.assets[] | select(.name | test("\\.(exe|msi)$")) | .url' | ||
| if (-not $urls) { throw "No winget installer assets found" } | ||
| komac update $id --version "${{ github.event.release.tag_name }}" --urls $urls | ||
| komac cleanup --only-merged |
auth run: wrap CLI with auto-failover on rate limit (5 retries).
auth isolate add/ls/delete: isolated HOME profiles for parallel sessions.
auth exec: run command with isolated profile HOME.
auth login: login flow with auto-backup on success.
auth_crypt: AES-256-GCM vault encryption with PBKDF2 key derivation.
Copilot added to auth catalog.
Daemon detection helpers for codex app-server.
Summary by CodeRabbit