feat: add external integration (JSON export file + show --json/--threshold)#105
Open
vmvarela wants to merge 10 commits into
Open
feat: add external integration (JSON export file + show --json/--threshold)#105vmvarela wants to merge 10 commits into
vmvarela wants to merge 10 commits into
Conversation
10 tasks
- Drop --cached entirely (it only aliased --json, which is already cache-only by design); remove it from code, usage text, and README. - Merge the duplicated --threshold / --threshold= parse branches. - Remove the two empty 'Warn but accept' blocks for --threshold > 100. - Fix README: --json is cache-only (no network); export write errors are silently ignored, not logged. - Sync SHOW_USAGE with --json/--threshold options.
Satisfies issue slkiser#102 acceptance criterion: a failed export write must log a warning and never affect TUI rendering. The fire-and-forget catch now emits console.warn instead of swallowing the error silently. Docs updated.
A user with onlyCurrentModel:true would compute a different quota-state cache key than the TUI background writer (which always uses onlyCurrentModel:false, no session). The CLI --json path was reading under a key nobody wrote under, producing 'unavailable' for every provider despite fresh cache data. Now the CLI normalizes its provider context the same way the TUI export writer does, so it reads the entries the TUI wrote. This fixes the core external-integration use case for anyone with onlyCurrentModel:true in their config.
- Remove lib.quota-export-types.test.ts (119 lines): tested JSON.stringify/parse behavior, which is runtime-standard and already validated by TypeScript - Consolidate resolveExportPath tests: 4 separate tests → 1 with multiple assertions - Remove trivial/redundant tests in lib.quota-export.test.ts: * 'omits resetAt when entry has no resetTimeIso' (covered by test 1) * 'sets cacheAgeSeconds to 0 when no ok/error providers exist' (edge case) * 'propagates fromCache flag' (already verified in test 1) * 'handles multiple providers with mixed statuses' (redundant with tests 1, 4, 5) - Remove duplicate test in tui-runtime.test.ts: * 'does not call buildQuotaExport when export is disabled' (identical to test 1) - Consolidate lib.cli-show.test.ts: * Remove redundant '--json output includes all expected QuotaExport schema fields' * Consolidate 4 --threshold validation tests into 1 with multiple assertions * Remove trivial '--threshold > 100 is accepted' test Total: 40 insertions, 283 deletions + 119 (deleted file) = ~362 lines removed. All 1040 tests still pass (3 pre-existing pricing failures unrelated to this PR).
…threshold exit 2
- Remove 'stale' from CachedProviderRead: computed in two branches of
readCachedProviderResult but never read by any production consumer
(buildQuotaExport only uses .hit/.result/.timestamp). The field was
introduced by this PR and has no external users.
- Collapse the two now-redundant 'stale: false'/'stale: true' tests in
quota-state.test.ts into a single cache-hit test; disk-read coverage
is retained by 'populates inMemoryCache from disk'.
- Strip leftover 'stale:' keys from quota-export mock setups.
- Document the existing exit-code 2 ('no cached quota to compare') in
SHOW_USAGE and at the return site, distinguishing it from exit 1
('below threshold') per issue slkiser#102.
Net: 4 insertions, 70 deletions. All 1039 non-pricing tests pass.
…visible home content; reduce tests - Fix: register homeBottom in resolveTuiSurfaceRegistration when export.enabled is true, even if compact status and announcements are both disabled. Otherwise the export file is never written. - Guard: writeTuiQuotaExportIfEnabled now respects config.enabled === false. - Docs: update bin usage to show --json/--threshold flags and exit-2 docs. - Tests: consolidate 3 buildQuotaExport mapping tests into one (percent entry + value-kind + window omission); add test for export-only homeBottom registration. Net: 49 insertions, 68 deletions. 1038 tests pass (3 pre-existing pricing failures unrelated).
- Stop deriving machine-readable 'window' from entry.name (single-window providers with window-like names like 'Monthly Premium Requests' would emit a spurious window that consumers branch on). Derive from entry.label only. - Add sanitizeSingleLineDisplaySnippet to provider error messages before writing to disk, matching the redaction guarantee the non-JSON CLI path already provides. - Add test regression entry proving a name-matches-but-label-doesn't entry gets no window. - Update error test to assert ANSI escapes and control characters are stripped.
Both the CLI show --json and TUI periodic writer need the same cache-key-aligning normalization (onlyCurrentModel: false, showSessionTokens: false, session: {}). Duplicating this in two places is a latent footgun — if one drifts, exports silently become all-unavailable. Extract into createExportProviderContext(runtime) so the contract lives in one documented place.
- Collapse two near-identical --threshold exit-0/exit-1 tests into an it.each table (~35 lines saved) - Drop writeQuotaExport re-throw test from unit file (error propagation asserted at integration layer in tui-runtime) - Trim wiring-only data-shape assertions from tui-runtime export test (mapping owned by unit file); keep TUI-specific path-resolution assertion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #102
Summary
Adds two complementary surfaces for consuming quota data from external tools — no live network fetches, all reads from the existing per-provider disk cache.
What changed
New:
show --json/--threshold/--provideropencode-quota showgains three new flags:--json— emits aQuotaExportJSON document to stdout instead of human-readable text. Reads exclusively from disk cache, never triggers a network fetch.--threshold <pct>— exit1if any enabled provider is below<pct>%remaining; exit2when no provider returned anokstatus. Requires--json.--provider <id>— filters the output to a single provider.New: TUI background export writer
When
export.enabledistrue, the TUI writes aQuotaExportfile to disk after each 60 s background refresh. The file is written atomically (writeJsonAtomic). Write errors are fire-and-forgotten and never affect TUI rendering.New config fields
| Field | Default | Meaning |
|---|---|---|
|
export.enabled|false| Write export file after each background refresh ||
export.path|""| Export path; empty =$XDG_CACHE_HOME/opencode/quota-export.json;~/expanded |New types
QuotaExport,QuotaExportEntry,QuotaExportProviderinsrc/lib/quota-export-types.ts— the shared JSON schema consumed by both surfaces.Files
quota-export-types.ts,quota-export.tsquota-state.ts(readCachedProviderResult,ignoreExpiry)types.ts,config.tscli-show.ts,opencode-quota.tstui-runtime.ts,tui.tsxREADME.md(External integration section + JSON schema + examples)lib.quota-export-types.test.ts,lib.quota-export.test.ts,quota-state.test.ts,tui-runtime.test.ts,lib.cli-show.test.tsTests
All new and existing tests pass.