Skip to content

Secure auth flow, model picker, and durable sidebar metrics#3

Open
MrFok wants to merge 18 commits into
mainfrom
secure-auth-and-model-picker
Open

Secure auth flow, model picker, and durable sidebar metrics#3
MrFok wants to merge 18 commits into
mainfrom
secure-auth-and-model-picker

Conversation

@MrFok

@MrFok MrFok commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Summary

Hardens the TTC plugin auth flow, replaces free-text model selection with a docs-grounded picker, and fixes sidebar metrics so they survive OpenCode session switches and process restarts.

Auth flow

  • Renames the provider id to opencode-ttc-plugin while preserving legacy the-token-company-plugin reads.
  • Adds opencode-ttc-plugin login / logout plus /ttc-login / /ttc-logout.
  • Rejects command-line secrets via --key; supports no-echo TTY input and --stdin.
  • Writes auth entries through the shared auth-store helper with atomic temp+rename, 0o600 file mode, 0o700 dir mode, corrupt-file refusal, and legacy-entry rotation.

Model/settings UX

  • Replaces free-text model input with curated bear-2 / bear-1.2 options plus a custom-id escape hatch.
  • Collapses auth controls in /ttc to one contextual row.
  • Refreshes sidebar auth status after login/logout without waiting for the next transform.

Sidebar metrics durability

  • Removes the session-change path that immediately set sidebar state to null and made metrics appear to zero out on cmd+x+l session switches.
  • Gates rendered metrics by loaded session id so stale metrics are not shown for the wrong session.
  • Hydrates in-memory session totals from the persisted sidebar state before writing new transform results, so leaving/restarting OpenCode no longer clobbers cumulative metrics.
  • Makes first-use hydration concurrency-safe so overlapping transforms share one hydrated stats object.

Verification

  • npm test -> 55/55 pass
  • git diff --check
  • Independent correctness/security review -> no findings
  • Independent maintainability review -> found hydration race + duplicate TUI loads; both fixed and covered by regression tests
  • npm run smoke:plugin blocked locally: TTC_API_KEY is not set

Regression Coverage

  • tests/opencode-ttc-plugin.test.js: hydrates persisted sidebar state before writing resume metrics.
  • tests/opencode-ttc-plugin.test.js: concurrent first-use hydration shares one stats object.
  • tests/opencode-ttc-plugin.test.js: session-switch helpers avoid rendering stale state as zeroed metrics.
  • Existing auth-store, slash command, model picker, and config tests continue to pass.

Manual QA Needed

With a real TTC key in OpenCode:

  1. Send a compressible prompt in session A.
  2. Switch sessions with cmd+x+l, then switch back to A.
  3. Restart OpenCode and resume A.
  4. Send another compressible prompt.

Expected: A's sidebar totals restore and continue accumulating instead of resetting.

Generated with opencode

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Token Compression UI and auth management commands (/ttc and /ttc-logout), plus settings flows for adding/removing API keys.
    • Added CLI login/logout commands (TTY prompt and --stdin), and improved sidebar session stats that persist/resume across conversations.
  • Documentation
    • Overhauled the README: new setup/login workflows, updated configuration/model guidance, and expanded troubleshooting/security details.
  • Bug Fixes
    • Improved handling of legacy API key providers and more reliable session/auth state transitions (including safer behavior after logout and with missing/corrupt auth data).

MrFok added 5 commits June 17, 2026 11:53
Introduces lib/auth-store.js as the single source of truth for auth-store
I/O across CLI and TUI. All writes are atomic (temp file + rename(2)),
set file mode 0o600 and directory mode 0o700, refuse to overwrite a
corrupt existing file, and replace any symlink at the target path so a
planted symlink cannot exfiltrate the key. XDG_DATA_HOME is validated
as absolute; relative paths fall back to $HOME/.local/share to keep the
key out of the working directory.
AUTH_PROVIDER_ID is now opencode-ttc-plugin (was the-token-company-
plugin) so it matches the package and TUI id. resolveApiKeyFromAuthStore
still reads entries written under the legacy id so existing users do not
need to re-auth.

CLI gains login and logout subcommands backed by the shared auth-store
module. login never accepts the key as a --key <value> argument (would
leak via ps aux and shell history); instead it prompts on the TTY with
echo off or reads from stdin via --stdin. logout clears both current and
legacy entries. doctor reports which id the key is stored under.
Sidebar polls auth.json directly via loadAuthStatus. When the cached
state file still says missing_auth but a key has landed, the sidebar
overrides to 'authenticated — send a message to start compressing'
instead of staying stale until the next transform fires.

/ttc settings menu now shows exactly one compact auth row conditional
on auth state: 'Add API key' when missing, 'Remove API key' (with
confirmation) when present. Reduces two wordy rows to one and gives a
clearer affordance.

Model picker replaces the free-text input with a curated list grounded
in thetokencompany.com/docs/compression: bear-2 (Recommended) and
bear-1.2, plus a custom-id escape hatch for enterprise fine-tunes.
Stops silent typos like 'bear-2.0' (not a real model id) entering the
config.

Login/logout failures now surface the actual reason (empty key, corrupt
auth store, write failure) instead of a generic 'Login failed' message.
Adds regression coverage for the security invariants the new auth-store
module relies on: file mode 0o600 after write, atomic rename surviving
a concurrent writer, and corrupt existing file being refused rather
than silently overwritten (which would destroy other providers' keys).

Adds coverage for the legacy auth id read fallback, the preference for
the current id when both exist, and the conditional single-row auth
state in the /ttc settings menu.
README setup section now leads with the plugin-managed login paths
(opencode-ttc-plugin login --stdin and /ttc-login) before the opencode
auth menu, and explicitly notes that the second prompt users see during
opencode auth login is opencode's separate LLM-provider auth, not a
duplicate TTC prompt.

Adds a Models section grounded in thetokencompany.com/docs/compression
listing bear-2 (Recommended) and bear-1.2, explains there is no list
endpoint so the picker is curated, and tells users how to diagnose an
unexpected model id via doctor --verbose.

Security and network policy section now documents the auth-write
guarantees: atomic temp+rename, 0o600 file mode, 0o700 directory mode,
refusal to overwrite a corrupt file, symlink replacement via rename,
relative-XDG fallback, and the CLI never accepting the key as a
--key <value> argument.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22cfca0e0b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/cli.mjs Outdated
Comment on lines +306 to +309
if (isTTY && typeof input.setRawMode === "function") {
try { input.setRawMode(true); } catch { /* ignore */ }
}
rl.question(query, (answer) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop echoing API keys during login

For the interactive TTY path, this still leaves readline echoing the key: running XDG_DATA_HOME=$(mktemp -d) node scripts/cli.mjs login in a TTY printed the typed ttc_secret before the success message. Since the new login flow is advertised as “no echo,” this leaks the TTC key to the terminal/scrollback; use a muted readline output or a real password prompt instead of raw mode plus rl.question.

Useful? React with 👍 / 👎.

@MrFok

MrFok commented Jun 17, 2026

Copy link
Copy Markdown
Owner Author

Codex P1 (echo of API key during login) — fixed in 309dc3e4

The original code passed process.stdout as readline's output, which makes readline echo each typed char itself (independent of setRawMode).

Fix: write the prompt directly to process.stdout, then hand readline a muted Writable (write: (_chunk, _enc, cb) => cb()) so its echo writes go nowhere. terminal: true stays on so line editing (backspace, etc.) still works.

Verified with a real pty.fork() harness typing ttc_SECRET_value char-by-char at 60ms intervals:

[PROMPT]: "Enter TTC API key (from thetokencompany.com): "
[AFTER]:  "\r\nSaved TTC API key under 'opencode-ttc-plugin'...\r\n"
full typed string echoed back: False
SECRET fragment echoed:        False
ttc_ fragment echoed:          False

Only the prompt and the post-submit success line appear — the secret itself does not.

MrFok added 2 commits June 17, 2026 12:10
Codex review on PR #3 caught that the promptSecret helper was still
echoing typed characters: readline.createInterface({ input, output,
terminal: true }) does its own echo to the output stream regardless of
setRawMode on stdin. The advertised 'no echo' guarantee was not actually
honored.

Fix: write the prompt directly to process.stdout, then pass readline a
muted Writable (write: (_chunk, _enc, cb) => cb()) as its output so
echo writes are dropped. terminal: true stays on so backspace and line
editing still work.

Verified by typing 'ttc_SECRET_value' char-by-char at 60ms intervals
through a real pty.fork() harness: the captured output contains the
prompt and the post-submit success line, but none of 'ttc_SECRET_value',
'SECRET', or 'ttc_'.
…leshooting

The README had drifted from what the plugin actually does. Restructured
the whole thing around what a user needs to know, with new sections:

- Requirements (Node 20+, OpenCode 1.4+, TTC key)
- Sidebar: status dot colors, every status line users can see, what
  each one means (including the new 'authenticated — send a message'
  override and what 'waiting for metrics' implies)
- Slash commands: /ttc, /ttc-login, /ttc-logout in one table
- What gets compressed: message selection (latest user turn by default,
  compressSystem / compressHistory toggles) plus the full skip-pattern
  table (code fence, diff, stack trace, JSON, schema, ...) so users
  seeing 'skipped: code fence' know why
- Troubleshooting: missing auth after login, phantom model id like
  'bear-2.0', the two-prompt opencode auth login, compression not
  firing, legacy auth id

Fixed the broken ''' fences in the LLM install block (was using three
single-quotes; should be triple backticks). Tightened the intro to one
paragraph that says what TTC does and why you'd use it. Numbered the
top-level sections so the table of contents is implicit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6382d70c26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/auth-store.js
return { ok: false, reason: "auth_store_read_failed", authFilePath, error };
}

parsed[providerID] = { type: "api", key: trimmed };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove legacy TTC keys when saving a replacement

For users upgrading from the old the-token-company-plugin provider ID, logging in with the new flow writes the replacement key under opencode-ttc-plugin but leaves the old TTC key in auth.json. Since the runtime now prefers the new entry and logout explicitly removes both IDs, key rotation through opencode-ttc-plugin login leaves a stale secret on disk indefinitely; delete the legacy provider entries before writing the current one.

Useful? React with 👍 / 👎.

Codex P2 on PR #3: when a user with the legacy the-token-company-plugin
provider id logs in via the new flow, the new id gets written but the
old entry stayed on disk indefinitely. logout already cleared both; now
login does too.

writeAuthEntry accepts legacyProviderIDs (defaults to LEGACY_AUTH_PROVIDER_IDS)
and deletes any of those entries from the parsed store before writing
the new one. Unrelated providers (anthropic, openai, etc.) are never
touched. Returns removedLegacyIDs so callers can report what got cleaned.

CLI persistKey prints 'Removed stale legacy entries: ...' when any were
cleared. TUI performLogin toast appends the same note.

Regression tests:
- pre-existing legacy + current + anthropic + openai -> after login,
  legacy gone, current rotated, anthropic + openai preserved verbatim
- fresh login (no legacy) returns removedLegacyIDs: []
@MrFok

MrFok commented Jun 17, 2026

Copy link
Copy Markdown
Owner Author

Codex P2 (stale legacy TTC keys after rotation) — fixed in d3ff2fc

writeAuthEntry now takes legacyProviderIDs (defaults to LEGACY_AUTH_PROVIDER_IDS = ["the-token-company-plugin"]) and deletes any of those entries from the parsed store before writing the new one. Unrelated providers (anthropic, openai, etc.) are never touched — only the legacy TTC id is cleaned up.

Returns removedLegacyIDs: [...] so callers can report what got cleared. CLI prints Removed stale legacy entries: ..., TUI toast appends the same note.

End-to-end verification (starting with the-token-company-plugin + anthropic on disk):

BEFORE: {"the-token-company-plugin":{"type":"api","key":"ttc_OLD_legacy"},
         "anthropic":{"type":"api","key":"sk-ant-keepMe"}}

$ printf %s "ttc_NEW_rotated" | opencode-ttc-plugin login --stdin
Saved TTC API key under 'opencode-ttc-plugin' at .../auth.json
Removed stale legacy entries: the-token-company-plugin

AFTER:  {"anthropic":{"type":"api","key":"sk-ant-keepMe"},
         "opencode-ttc-plugin":{"type":"api","key":"ttc_NEW_rotated"}}

ttc_OLD_legacy gone, sk-ant-keepMe preserved verbatim. Regression tests cover both the rotation case and the fresh-login (no legacy) case.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d3ff2fc33e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/auth-store.js
Comment on lines +13 to +16
if (raw && isAbsolute(raw)) {
return join(raw, "opencode", "auth.json");
}
return join(homedir(), ".local", "share", "opencode", "auth.json");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Share auth path resolution with the transform

When XDG_DATA_HOME is set to a relative path, login/TUI now use this helper and fall back to ~/.local/share, but the message transform still computes the auth path with its own getAuthStorePath in opencode-plugins/ttc-message-transform.js:436-440, which reads from the relative path instead. In that environment opencode-ttc-plugin login, doctor, and the sidebar can report a saved key while compression continues with missing auth; update the transform to use this shared helper or the same fallback.

Useful? React with 👍 / 👎.

Comment thread lib/auth-store.js
parsed[providerID] = { type: "api", key: trimmed };

try {
await atomicWriteJson(authFilePath, parsed, { writeFileImpl, renameImpl, mkdirImpl, chmodImpl, rmImpl });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize auth-store read-modify-writes

During a concurrent auth change, this writes the snapshot read earlier over the whole auth.json. If another provider is added or rotated after readAuthStore but before this rename, the temp-file rename atomically replaces the file with stale contents and drops that provider; temp+rename prevents partial writes but does not serialize or merge concurrent read-modify-write operations. Use a lock or re-read/merge before replacing the file to avoid losing unrelated credentials.

Useful? React with 👍 / 👎.

Codex P2 #1 on PR #3 (path resolution mismatch): the message transform
still had its own getAuthStorePath that did not validate XDG_DATA_HOME.
When XDG_DATA_HOME was relative (invalid per spec but possible), login
fell back to $HOME/.local/share while the transform read from the
relative path — so login succeeded and compression silently failed with
missing_auth.

Fix: delete the duplicate; the plugin now imports getAuthStorePath from
../lib/auth-store.js. Same signature, same return shape, one source of
truth. Tests still pass through the plugin's _test re-export.

Regression test pins the fallback behavior through the plugin's exported
helper: relative paths, './data', bare 'data', and empty string all
fall back to $HOME/.local/share/opencode/auth.json.

Codex P2 #2 on PR #3 (concurrent RMW not serialized): legitimate but
disproportionate to fix. Atomic rename already prevents partial writes
(addressing the original thermo H1); full serialization would need
file locking (POSIX-only) or optimistic retry loops (deadlock potential)
for an edge case where the realistic blast radius is 're-enter one key.'
Documented as a known limitation in Troubleshooting with the recovery
path.
@MrFok

MrFok commented Jun 17, 2026

Copy link
Copy Markdown
Owner Author

Codex P2 (path resolution mismatch) — fixed in 1dff861

You were right — the transform still had its own getAuthStorePath at opencode-plugins/ttc-message-transform.js:436 that did not validate XDG_DATA_HOME. With a relative XDG_DATA_HOME, login fell back to $HOME/.local/share while the transform read from the relative path — so login succeeded and compression silently failed with missing_auth. Real bug.

Fix: deleted the duplicate. The plugin now imports getAuthStorePath from ../lib/auth-store.js. Same signature, same return shape, one source of truth. Re-exported via the plugin's _test block so existing tests keep working unchanged.

Regression test added pins the fallback behavior through the plugin's exported helper:

XDG_DATA_HOME: "relative/data"  ->  $HOME/.local/share/opencode/auth.json
XDG_DATA_HOME: "./data"          ->  $HOME/.local/share/opencode/auth.json
XDG_DATA_HOME: "data"            ->  $HOME/.local/share/opencode/auth.json
XDG_DATA_HOME: ""                ->  $HOME/.local/share/opencode/auth.json
XDG_DATA_HOME: undefined         ->  $HOME/.local/share/opencode/auth.json

Codex P2 (concurrent RMW not serialized) — declined with documented limitation

Legitimate observation, but declining to fix. Atomic temp+rename (which we already do) prevents partial writes and was the explicit ask from the original thermo review of H1. Full serialization would require either POSIX-only file locking or an optimistic re-read-merge-retry loop — meaningful complexity and its own deadlock/starvation edge cases, for a scenario whose realistic blast radius is:

  • Two user-initiated single-shot operations (opencode-ttc-plugin login and opencode auth login) collide mid-rotation
  • Worst case: one provider key gets dropped
  • Recovery: re-enter the dropped key

For a single-user opencode plugin that almost exclusively READS auth.json at message-transform time and only WRITES during rare explicit user logins, the cost-benefit doesn't pencil out. Documented as a known limitation in README Troubleshooting with the recovery path so users aren't blindsided.

If this becomes a real support burden we'll revisit — happy to revisit the call if you have data showing it fires in practice.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1dff86153e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { gzipSync } from "node:zlib";
import { getAuthStorePath } from "../lib/auth-store.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the CLI-installed plugin self-contained

When users follow the documented opencode-ttc-plugin install path, install() only copies ttc-message-transform.js into ~/.config/opencode/plugins (scripts/cli.mjs:407-409). From that location this new relative import resolves to ~/.config/opencode/lib/auth-store.js, which is never copied or created, so OpenCode fails to load the installed plugin with ERR_MODULE_NOT_FOUND before compression can run. Either keep the server plugin self-contained or install the helper alongside it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Restored local in the plugin (self-contained) and added a parity test comparing it to the shared helper. Legacy Installed ttc-message-transform.js to /Users/DrFok/.config/opencode/plugins/ttc-message-transform.js now works; 53/53 tests + single-file import smoke pass. See opencode-plugins/ttc-message-transform.js:436 and tests/opencode-ttc-plugin.test.js:547.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Restored local getAuthStorePath in the plugin (self-contained) and added a parity test comparing it to the shared helper. Legacy single-file install now works; 53/53 tests + single-file import smoke pass. See opencode-plugins/ttc-message-transform.js:436 and tests/opencode-ttc-plugin.test.js:547.

@MrFok MrFok changed the title Secure auth flow, model picker, and sidebar refresh Secure auth flow, model picker, and durable sidebar metrics Jun 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e83811a533

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tui/index.tsx Outdated
Comment on lines +77 to +78
if (current?.status === "missing_auth" && authStatus().hasKey) {
return "waiting";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear stale sidebar status after auth-store logout

When a key is removed via /ttc-logout or opencode-ttc-plugin logout while the last sidebar state was compressed, skipped, or another active state, this branch never uses the now-empty auth store because it only upgrades stale missing_auth to waiting. Logout does not rewrite the per-session state file, so auth-store-backed sessions keep showing a green active status until another message is sent even though the transform will no longer compress; consider downgrading stale auth-store states when current.config.authSource === "auth_store" and authStatus().hasKey is false.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. When auth is removed, visibleState() returns null (hiding stale compressed/skipped), effectiveStatus() returns missing_auth, status shows "missing TTC auth". The existing 2s poll via loadAuthStatus makes logout take effect immediately. 55/55 tests. See tui/index.tsx:69.

When auth is removed via /ttc-logout or CLI, hide any previously written compressed/skipped sidebar state and show 'missing TTC auth' instead of stale metrics.

- gate visibleState on hasAuth()
- force effectiveStatus to 'missing_auth' when no key
- statusLine shows 'missing TTC auth' when unauthed
- polling via loadAuthStatus already provides fresh auth status

Fixes Codex P2 on PR #3.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11a2a136fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { gzipSync } from "node:zlib";
import { getAuthStorePath } from "../lib/auth-store.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep legacy installs loadable

Fresh evidence in the current patch shows the plugin still imports ../lib/auth-store.js, while the legacy installer only copies ttc-message-transform.js into ~/.config/opencode/plugins (scripts/cli.mjs:407-408). In that documented opencode-ttc-plugin install path, this import resolves to ~/.config/opencode/lib/auth-store.js, which is never installed, so OpenCode fails to load the plugin with ERR_MODULE_NOT_FOUND before compression or auth can run. Keep the copied plugin self-contained or install the helper beside it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Removed the ../lib/auth-store.js import from the plugin. Inlined getAuthStorePath (with the same isAbsolute relative-XDG validation) directly in ttc-message-transform.js. The legacy opencode-ttc-plugin install path only copies the single plugin file; this keeps it loadable. 55/55 tests + single-file import smoke pass. See opencode-plugins/ttc-message-transform.js:487 (local helper) and the test at line 535.

- Remove import of ../lib/auth-store.js from ttc-message-transform.js
- Inline getAuthStorePath (with isAbsolute XDG validation) inside the plugin
- Legacy installer copies only the single .js file; external import would 404
- Parity with lib/auth-store.js is covered by existing path tests + manual smoke

This is the same P1 that was previously fixed but the commit was not the current head.
Addresses Codex P1 'Keep legacy installs loadable'.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d4f96ebdc3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tui/index.tsx
Comment on lines +72 to +73
if (!hasAuth()) {
return null; // hide any stale compressed/skipped state once logged out

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat TTC_API_KEY auth as valid in sidebar

When users rely on the supported TTC_API_KEY environment variable instead of an auth-store entry, the transform still compresses and writes state with authSource: "env", but loadAuthStatus() only checks the auth store, so this branch hides that valid state and the sidebar reports missing TTC auth forever. Consider accepting env-backed state/auth here instead of treating every missing auth-store key as logged out.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. loadAuthStatus() and hasAuthEntry() now short-circuit on TTC_API_KEY env (return hasKey: true, providerID: "env"). Sidebar will render metrics instead of hiding behind "missing TTC auth", and the /ttc settings row will correctly show "Remove API key" (or not offer login) when env is the source. This also prevents spurious second prompts for env users. Store-backed logins (including via opencode auth provider) were already covered. 55/55 pass. See lib/auth-store.js:74 and tui/sidebar-state.js:43.

MrFok added 2 commits June 17, 2026 14:57
- loadAuthStatus() now short-circuits on TTC_API_KEY (returns hasKey:true, providerID:'env')
- hasAuthEntry() now short-circuits on TTC_API_KEY (returns hasKey:true, providerID:'env')
- This makes sidebar show metrics instead of 'missing TTC auth' when using env
- Makes settings menu show 'Remove API key' instead of 'Add' when env is set
- Prevents spurious second login prompts for users on env auth

Addresses latest Codex P2. 55/55 tests still pass.
…Entry

This prevents false 'missing auth' in sidebar and settings menu for env users,
which was causing extra 'add API key' prompts after opencode auth login or direct use of TTC_API_KEY.

56/56 tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a68d83f56

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tui/settings.js Outdated
Comment on lines +357 to +362
const authOption = auth.hasKey
? {
title: "Remove API key",
value: "ttc-logout",
description: "Revoke saved key"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t offer logout for env-only auth

When TTC_API_KEY is set but there is no auth-store entry, hasAuthEntry() returns { hasKey: true, providerID: "env" }, so this branch renders “Remove API key”. Selecting it calls removeAuthEntry(), which cannot unset the environment variable and reports no saved entry while the key remains active; it also prevents users from adding or rotating a persistent key from this menu. Branch on providerID or store-only auth state instead of hasKey alone.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8370585. The /ttc settings auth row now checks persisted auth-store state with hasAuthEntry({ env: {} }) instead of treating TTC_API_KEY as removable saved auth. Env-only users now see Add API key so they can persist/rotate a saved key; users with a saved entry still see Remove API key. Regression: settings menu shows the persisted-state row correctly even while TTC_API_KEY is set. npm test: 57/57 pass.

- Drop /ttc-login slash command entirely
- Drop 'Add API key' row from /ttc settings (only 'Remove' when present)
- Remove internal promptLogin / performLogin paths
- 'Add' path (if reached) now tells user to use opencode auth login
- Update tests (no more login registration, menu auth row is now only logout)
- Update README (remove references to /ttc-login and custom prompt)
- CLI login (opencode-ttc-plugin login) remains for direct use

Goal: one place to enter the key (native auth provider or direct CLI) to stop double prompts.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6de830a011

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/auth-store.js Outdated
Comment on lines +188 to +189
if (error?.code === "ENOENT" || error?.code === "AUTH_STORE_NOT_REGULAR_FILE") {
return { ok: true, authFilePath, removedAny: false };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refuse logout when the auth store is a symlink

When auth.json is a symlink, such as in a dotfile-managed OpenCode setup, readAuthStore() throws AUTH_STORE_NOT_REGULAR_FILE here and logout reports success/no entry without removing anything. The runtime transform still reads the same path with readFile and follows the symlink, so the TTC key remains active after opencode-ttc-plugin logout; return an error or remove/replace the symlink instead of treating it as an empty store.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8370585. removeAuthEntry() no longer reports success when readAuthStore() rejects a non-regular auth path (including symlink-backed auth.json). It now returns ok:false with reason auth_store_not_regular_file, so CLI/TUI logout fail visibly instead of claiming the key was removed while runtime could still read it. Regression added: logout refuses unsupported auth-store paths instead of claiming success. npm test: 57/57 pass.

MrFok added 2 commits June 17, 2026 16:41
…or provider

- Remove custom prompts/authorize from the registered auth provider in the plugin.
  This makes opencode auth login use its standard single API key prompt for TTC
  instead of triggering a second prompt.

- Restore /ttc-login, /ttc-logout slash commands and the auth row in /ttc settings menu
  (Add when missing, Remove when present). The TUI custom prompt is kept for direct use.

- Keep CLI login/logout fully working.

- 56/56 tests pass.

Addresses the double prompt when running 'opencode auth login' for the TTC provider.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Introduces a shared lib/auth-store.js module for atomic JSON-based API key persistence, migrates the plugin provider ID to opencode-ttc-plugin with legacy fallback, adds persisted sidebar session stats hydration, wires login/logout flows into both the CLI (scripts/cli.mjs) and TUI (tui/settings.js), updates the sidebar component to be auth-aware, and rewrites the README to match.

Changes

Auth Store, Login/Logout, and Session Stats Persistence

Layer / File(s) Summary
Shared auth-store module
lib/auth-store.js
New module defining AUTH_PROVIDER_ID, LEGACY_AUTH_PROVIDER_IDS, getAuthStorePath (XDG/fallback), readAuthStore, findAuthEntry, hasAuthEntry, writeAuthEntry, removeAuthEntry, and a private atomicWriteJson helper with restrictive permissions and temp-file rename semantics.
Plugin: legacy provider ID migration and session stats hydration
opencode-plugins/ttc-message-transform.js, opencode-plugins/ttc-message-transform-core.js
Renames AUTH_PROVIDER_ID to opencode-ttc-plugin, adds LEGACY_AUTH_PROVIDER_IDS, expands resolveApiKeyFromAuthStore to scan legacy IDs, introduces hydrateSessionStatsFromSidebarState and an async loadPersistedSessionStats with promise deduplication, and threads env-resolved statePath through the transform path.
TUI sidebar-state auth and render helpers
tui/sidebar-state.js
Adds SHARED_AUTH_PROVIDER_IDS, delegates getAuthStorePath, implements loadAuthStatus (env key override + auth JSON parse), and adds shouldRenderSidebarState and emptySidebarStateText render guards.
TUI settings: login/logout flows, model picker, and slash command registration
tui/settings.js
Adds TTC_LOGIN/TTC_LOGOUT command constants, KNOWN_MODELS, re-exports auth-store helpers, implements selectModel picker, login/logout dialog flows with toasts, dynamically toggles "Add/Remove API key" in the menu via hasAuthEntry, and registers /ttc-login and /ttc-logout slash commands.
TUI sidebar component: auth-aware refresh and rendering
tui/index.tsx
Adds signals for auth status and per-session loading, rewrites refresh() to concurrently load sidebar state and auth status with a session-ID race guard, and derives visibleState, effectiveStatus, status dot color, and empty-state text via createMemo.
CLI login/logout commands and doctor improvements
scripts/cli.mjs
Imports auth-store helpers, removes local auth-path logic, adds masked TTY and --stdin secret readers, implements loginCommand/logoutCommand using writeAuthEntry/removeAuthEntry, improves doctor output with provider-ID and KNOWN_MODELS details, and converts main() to async.
Tests: auth store, session hydration, sidebar helpers, TUI auth flows
tests/opencode-ttc-plugin.test.js
Adds tests for getAuthStorePath XDG fallback, persisted session resume and concurrent hydration deduplication, sidebar render helpers, provider ID migration and legacy precedence, /ttc-login//ttc-logout slash commands, settings menu auth-row toggling, and writeAuthEntry/removeAuthEntry security properties.
Package version bump, file list, and README update
package.json, README.md
Bumps version to 0.1.5, adds lib/auth-store.js to published files, and rewrites the README to document the updated plugin, sidebar, slash commands, CLI, configuration, models, compression behavior, troubleshooting, and security policy.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the three main changes: secure auth flow, model picker, and durable sidebar metrics. It aligns with PR objectives and covers the key improvements without being vague or unrelated.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch secure-auth-and-model-picker

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8370585400

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tui/settings.js Outdated
Comment on lines +294 to +298
dialog.replace(() => api.ui.DialogPrompt({
title: "TTC API Key",
placeholder: "ttc_...",
value: "",
onConfirm: (nextValue) => void performLogin(api, dialog, nextValue),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the visible TUI API-key prompt

When users select /ttc-login or the settings menu's Add API key row, this sends the TTC secret through the generic DialogPrompt with no password/masking option, so the key is entered as ordinary visible text in the TUI even though the flow is intended to avoid custom/echoed secret prompts. Route this action to the native auth flow or use a masked secret prompt instead of reusing the plain settings prompt.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6387ae6. /ttc-login and the Add API key row no longer open DialogPrompt or accept a visible API key. They now show a DialogAlert that points users to the native single-prompt auth flow (opencode auth login -> opencode-ttc-plugin) or the non-echoing CLI (opencode-ttc-plugin login). Added regression test: /ttc-login never opens DialogPrompt. npm test: 60/60 pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/opencode-ttc-plugin.test.js (1)

1206-1239: ⚡ Quick win

Add coverage for auth directory permission mode (0o700)

This test verifies file mode (0o600) but not the parent auth directory mode (0o700), which is part of the hardening contract and can regress silently.

Suggested test addition
     const { statSync } = await import("node:fs");
     const mode = statSync(authPath).mode & 0o777;
     assert.equal(mode, 0o600, `auth file should be 0o600, got 0o${mode.toString(8)}`);
+    const dirMode = statSync(dirname(authPath)).mode & 0o777;
+    assert.equal(dirMode, 0o700, `auth directory should be 0o700, got 0o${dirMode.toString(8)}`);
🤖 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 `@tests/opencode-ttc-plugin.test.js` around lines 1206 - 1239, The test
currently only verifies the file mode (0o600) of the auth.json file but does not
verify the parent auth directory mode (0o700). After the assertion that checks
the auth file mode in the writeTuiAuthEntry test, add an additional assertion
that checks the mode of the parent directory (the "opencode" directory) using
statSync on the directory path. Verify that the directory mode equals 0o700
using the same bitwise operation pattern (mode & 0o777) to ensure the directory
has the correct restricted permissions as part of the security hardening
contract.
🤖 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 `@lib/auth-store.js`:
- Around line 201-204: The condition `if (parsed[candidateID])` at line 202 uses
truthiness testing, which incorrectly skips deletion of provider keys that have
falsy values like null, empty strings, or 0. Replace this truthiness check with
an own-property check such as `Object.prototype.hasOwnProperty.call(parsed,
candidateID)` or the `in` operator to ensure all provider keys are properly
removed regardless of their stored value, allowing removedAny to be correctly
set to true when entries are actually deleted.

In `@opencode-plugins/ttc-message-transform.js`:
- Line 220: The numberOrZero function currently does not guard against Infinity
values, which can corrupt session metrics. Modify the function to check if the
converted number is finite using Number.isFinite() before applying the fallback
to 0 and the Math.max() clamping. The logic should verify that Number(value)
produces a finite number before using it; if it is not finite (including
Infinity or NaN), default to 0 instead.

In `@scripts/cli.mjs`:
- Around line 379-391: The async function logoutCommand is being called without
await, which allows the process to exit before the logout operation completes
and prevents error handling from taking effect. Find the call to logoutCommand
around line 657 and add the await keyword to ensure the async function completes
before proceeding. This applies to both the primary call and any other calls
mentioned in the comment.

---

Nitpick comments:
In `@tests/opencode-ttc-plugin.test.js`:
- Around line 1206-1239: The test currently only verifies the file mode (0o600)
of the auth.json file but does not verify the parent auth directory mode
(0o700). After the assertion that checks the auth file mode in the
writeTuiAuthEntry test, add an additional assertion that checks the mode of the
parent directory (the "opencode" directory) using statSync on the directory
path. Verify that the directory mode equals 0o700 using the same bitwise
operation pattern (mode & 0o777) to ensure the directory has the correct
restricted permissions as part of the security hardening contract.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e890c58e-a4cc-4b88-b629-f45544649c40

📥 Commits

Reviewing files that changed from the base of the PR and between 897cb14 and 8370585.

📒 Files selected for processing (10)
  • README.md
  • lib/auth-store.js
  • opencode-plugins/ttc-message-transform-core.js
  • opencode-plugins/ttc-message-transform.js
  • package.json
  • scripts/cli.mjs
  • tests/opencode-ttc-plugin.test.js
  • tui/index.tsx
  • tui/settings.js
  • tui/sidebar-state.js

Comment thread lib/auth-store.js
Comment thread opencode-plugins/ttc-message-transform.js Outdated
Comment thread scripts/cli.mjs
@MrFok

MrFok commented Jun 18, 2026

Copy link
Copy Markdown
Owner Author

CodeRabbit nitpick also fixed in 6387ae6: the auth helper test now verifies the parent opencode auth directory is mode 0o700 in addition to auth.json mode 0o600. Full verification: npm test 60/60 pass, syntax checks pass, npm pack --dry-run passed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
opencode-plugins/ttc-message-transform.js (1)

490-496: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider importing getAuthStorePath from lib/auth-store.js to eliminate duplication.

This function is identical to the one in lib/auth-store.js. While both implementations are now in sync (both validate isAbsolute), maintaining two copies creates a maintenance risk where they could diverge in future updates.

♻️ Suggested refactor
+import { getAuthStorePath } from "../lib/auth-store.js";
+
 // ... at lines 490-496, remove the local function:
-function getAuthStorePath(env = process.env) {
-  const raw = String(env.XDG_DATA_HOME ?? "").trim();
-  if (raw && isAbsolute(raw)) {
-    return join(raw, "opencode", "auth.json");
-  }
-  return join(homedir(), ".local", "share", "opencode", "auth.json");
-}
🤖 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 `@opencode-plugins/ttc-message-transform.js` around lines 490 - 496, The
function getAuthStorePath in ttc-message-transform.js is a duplicate of the one
already present in lib/auth-store.js, creating maintenance risk if they diverge.
Remove the getAuthStorePath function definition (currently at lines 490-496)
from ttc-message-transform.js and instead import it from lib/auth-store.js at
the top of the file with the other imports. This ensures a single source of
truth for this shared utility function while maintaining the same functionality
throughout the codebase.
README.md (1)

247-247: ⚠️ Potential issue | 🟡 Minor

The sidebar 2-second polling interval claim is verified and accurate.

Line 247 correctly states the sidebar polls every 2 seconds—this is confirmed in tui/index.tsx:59 where setInterval(() => setRefreshCount((count) => count + 1), 2000) implements the 2000ms polling mechanism. The plugin controls this directly; it is not OpenCode's default behavior.

The apparent conflict with line 70 (immediate refresh) is resolved: the sidebar uses both mechanisms. Beyond polling, it subscribes to OpenCode events (message.updated and session.status at tui/index.tsx:57-58) which trigger immediate refresh via triggerRefresh. The 2-second polling serves as a fallback.

The README statement is accurate but could clarify that the sidebar also refreshes immediately on auth-related events, not solely on the 2-second interval.

🤖 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 `@README.md` at line 247, Update line 247 in the README to clarify that the
sidebar uses two refresh mechanisms, not just the 2-second polling interval. The
current statement only mentions polling but should also explain that the sidebar
subscribes to auth-related events (message.updated and session.status, as
implemented in tui/index.tsx at lines 57-58) which trigger immediate refresh,
while the 2-second polling interval (setInterval at tui/index.tsx line 59)
serves as a fallback. Modify the text to make it clear the sidebar refreshes
both immediately on events and periodically on the 2-second interval.
🤖 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 `@tui/settings.js`:
- Around line 266-272: The promptLogin function (and confirmLogout function at
lines 280-303) lacks defensive guards to validate the api and dialog parameters
before using them, while openTtcSettingsMenu has such guards. Since promptLogin
and confirmLogout are both entry points via slash commands at lines 445 and 455,
add defensive guard checks at the start of both functions to validate that api
and dialog are valid objects before calling dialog.replace(), returning early if
either parameter is invalid. Apply the same validation pattern used in
openTtcSettingsMenu to ensure consistency across all entry points.

---

Outside diff comments:
In `@opencode-plugins/ttc-message-transform.js`:
- Around line 490-496: The function getAuthStorePath in ttc-message-transform.js
is a duplicate of the one already present in lib/auth-store.js, creating
maintenance risk if they diverge. Remove the getAuthStorePath function
definition (currently at lines 490-496) from ttc-message-transform.js and
instead import it from lib/auth-store.js at the top of the file with the other
imports. This ensures a single source of truth for this shared utility function
while maintaining the same functionality throughout the codebase.

In `@README.md`:
- Line 247: Update line 247 in the README to clarify that the sidebar uses two
refresh mechanisms, not just the 2-second polling interval. The current
statement only mentions polling but should also explain that the sidebar
subscribes to auth-related events (message.updated and session.status, as
implemented in tui/index.tsx at lines 57-58) which trigger immediate refresh,
while the 2-second polling interval (setInterval at tui/index.tsx line 59)
serves as a fallback. Modify the text to make it clear the sidebar refreshes
both immediately on events and periodically on the 2-second interval.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3b3fbb7f-4748-4da6-a1da-94c347d47092

📥 Commits

Reviewing files that changed from the base of the PR and between 8370585 and 6387ae6.

📒 Files selected for processing (6)
  • README.md
  • lib/auth-store.js
  • opencode-plugins/ttc-message-transform.js
  • scripts/cli.mjs
  • tests/opencode-ttc-plugin.test.js
  • tui/settings.js

Comment thread tui/settings.js
Comment on lines +266 to +272
function promptLogin(api, dialog) {
dialog.replace(() => api.ui.DialogAlert({
title: "Add TTC API Key",
message: "Use `opencode auth login` and choose opencode-ttc-plugin, or run `opencode-ttc-plugin login` in a terminal. The TUI does not accept API keys because plain dialogs can display secrets.",
onConfirm: () => openTtcSettingsMenu(api, dialog)
}));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider adding defensive guards for consistency with openTtcSettingsMenu.

openTtcSettingsMenu guards against invalid api/dialog at line 323, but promptLogin and confirmLogout are also entry points via slash commands (lines 445, 455) and lack similar guards. If the dialog object is unexpectedly invalid, dialog.replace() would throw.

🛡️ Proposed defensive guard
 function promptLogin(api, dialog) {
+  if (!api?.ui || !dialog?.replace) return;
   dialog.replace(() => api.ui.DialogAlert({
     title: "Add TTC API Key",
     message: "Use `opencode auth login` and choose opencode-ttc-plugin, or run `opencode-ttc-plugin login` in a terminal. The TUI does not accept API keys because plain dialogs can display secrets.",
     onConfirm: () => openTtcSettingsMenu(api, dialog)
   }));
 }
 async function confirmLogout(api, dialog) {
+  if (!api?.ui || !dialog?.replace) return;
   dialog.replace(() => api.ui.DialogConfirm({

Also applies to: 280-303

🤖 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 `@tui/settings.js` around lines 266 - 272, The promptLogin function (and
confirmLogout function at lines 280-303) lacks defensive guards to validate the
api and dialog parameters before using them, while openTtcSettingsMenu has such
guards. Since promptLogin and confirmLogout are both entry points via slash
commands at lines 445 and 455, add defensive guard checks at the start of both
functions to validate that api and dialog are valid objects before calling
dialog.replace(), returning early if either parameter is invalid. Apply the same
validation pattern used in openTtcSettingsMenu to ensure consistency across all
entry points.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant