Commit eeef701
authored
feat(export): client config export for Pi and OpenCode (#852)
* feat(export): add client-neutral config export core
Phase 010 of devlog/_plan/260731_client_config_export.
Lifts the OpenCode provider-block builder out of the launcher into a
client-neutral module and adds a Pi serializer beside it. `ocx opencode`
keeps every export it had and its launch path is untouched; the builder
now has one body shared by the launcher and future export surfaces.
The OpenCode output is pinned by a golden captured from the pre-refactor
implementation. Pi emits a models array, always omits `cost` (we have no
price data and zeros would assert "free"), and omits context/maxTokens
when no authoritative context window exists.
Neither serializer ever emits a live key: OpenCode carries
`{env:OPENCODEX_OPENCODE_API_KEY}`, Pi carries `$OPENCODEX_API_KEY`.
* feat(export): add the ocx export CLI command
Phase 020 of devlog/_plan/260731_client_config_export.
`ocx export --client <opencode|pi>` prints the client config. With --json
stdout is the config and nothing else, so an agent can pipe it; without
it, the JSON is followed by the destination path, the merge warning, the
env line, and a count of models shipping without context limits.
--out writes the file but refuses to clobber an existing one without
--force, since the obvious target is a populated config whose other
providers would be lost.
Disabled rows are filtered here, at the /api/models boundary: the export
core dedupes and sorts but cannot see visibility state. A non-array
models payload is an error rather than an empty-model config.
Also corrects two example paths in the plan docs that tripped
privacy:scan.
* feat(export): serve client configs from the management API
Phase 030 of devlog/_plan/260731_client_config_export.
GET /api/client-config?client=<opencode|pi> returns the same bytes the
CLI prints, wrapped in an envelope carrying the destination path, the env
var name, and the model counts the GUI needs to render its states.
The /api/models row logic moved into listManagementModelRows() and both
branches now call it. Copying it would have created a second definition
of which models exist, free to drift from the first; disabled/dedupe
precedence is ~40 lines and only correct in one place.
modelsWithoutLimits is counted from the serialized document rather than
re-derived from input rows, so the number cannot disagree with the bytes
the caller receives. A catalog failure is 503, never a partial 200.
* feat(gui): add the client config panel to the API tab
Phase 040 of devlog/_plan/260731_client_config_export.
Renders what GET /api/client-config returns: a segmented OpenCode/Pi
switch, the JSON itself, copy as the primary action, download as the
secondary one, plus the destination path and the env line the config
references.
Download names the file from the server envelope and announces that
nothing changed yet and the file must be merged. A downloaded config
that reads as "applied" is how someone loses the other providers in
their opencode.json.
Rendering it in a real browser caught two defects a static read missed:
a bare .awi-clientconfig-panel selector ties .api-panel's overflow on
specificity and loses, leaving a clipping ancestor between the JSON and
the page scroller; and a max-height on the JSON made a second capped
scroll region on a tab whose invariant is that the model catalog is the
only one. Both are fixed and the layout invariant test covers the second.
Placement is the fallback 003 §6 allows, since the connect-bar rework
has not landed yet.
* docs: document ocx export and the Pi client
Phase 050 of devlog/_plan/260731_client_config_export.
The export command shipped in the previous four commits with nothing in
docs-site pointing at it, and Pi had no page at all.
Adds the ocx export section to the CLI reference (English plus the four
translated copies), a section in the opencode guide on getting the
provider block into your own config, and a Pi guide.
Three things the docs have to get right, each verified against the code
while writing: the two clients use DIFFERENT env vars
(OPENCODEX_OPENCODE_API_KEY vs OPENCODEX_API_KEY), a loopback bind needs
no admission key at all since shouldInjectApiAuthHeader is just
!isLoopbackHostname, and an exported config must be merged rather than
replace an existing file.
BYOK is split rather than restated: the proxy admission key is what the
exported config references, provider keys stay in providers.<name>.apiKey
and keep their existing home in the providers guide.
The Pi schema is still unverified against a real install and the guide
says so.
* fix(gui): gate the client-config fetch behind an explicit cancel flag
React Doctor flagged the setter after `await` in the panel's effect. The
write was already safe — every result carries its request key and a
superseded run's key no longer matches — but the guard was an
`AbortController.signal.aborted` check, which states "the request was
cancelled", not "this run may no longer write".
An explicit `cancelled` flag set in the cleanup makes the invariant local
to the setter, so a reader (and the linter) can see why a stale resolve
cannot land. The abort stays: it stops the in-flight request, the flag
stops the write.
* test(cli): map /api/client-config to ocx export in the parity sweep
The headless-parity sweep asserts every GUI management endpoint has a
documented CLI resource. Phase 030 added GET /api/client-config without
registering it, so the sweep went red — correctly: the route and the CLI
command shipped in the same unit and `ocx export` is exactly its mirror.1 parent 18352b4 commit eeef701
38 files changed
Lines changed: 3953 additions & 199 deletions
File tree
- devlog/_plan/260731_client_config_export
- docs-site
- src/content/docs
- guides
- ja/reference
- ko/reference
- reference
- ru/reference
- zh-cn/reference
- gui
- src
- components/apikeys-workspace
- i18n
- pages
- tests
- src
- clients
- cli
- server/management
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
Lines changed: 155 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
Lines changed: 91 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
0 commit comments