Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@
- renamed the managed install-state durability timestamp to
`lastDurableSetupAt` while keeping legacy `lastSuccessfulSetupAt` readable as
a backward-compatible migration path
- clarified the safe API key rotation flow in CLI success output and docs,
including the OpenCode Desktop restart needed after refreshing the managed
secret file

### Bug Fixes

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,16 @@ That rerun flow refreshes GonkaGate-managed config, secret storage, and
install-state metadata. It also normalizes only installer-owned GonkaGate
activation in the old target instead of deleting unrelated OpenCode settings.

To replace the GonkaGate API key later, rerun setup with a safe secret input:

```bash
printf '%s' "$GONKAGATE_API_KEY" | npx @gonkagate/opencode-setup --api-key-stdin --scope project --yes
```

If you use OpenCode Desktop, restart the desktop app after rerunning setup so
its sidecar reloads the managed secret file. The Desktop custom-provider form
does not replace the installer-managed `~/.gonkagate/opencode/api-key` file.

For `project` scope:

- user-level config still owns the provider definition and secret binding
Expand Down
5 changes: 4 additions & 1 deletion docs/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ Current validated picker entries:
`provider.gonkagate.options.apiKey` overrides are always blocked in v1
because current upstream docs do not clearly prove equivalent inline
`{file:...}` substitution parity for this secret-binding contract.
15. Tell the user to run plain `opencode`.
15. Tell the user to run plain `opencode`, and show the safe rerun command for
replacing the managed API key later. OpenCode Desktop users should restart
the desktop app after rerunning setup so its sidecar reloads
`~/.gonkagate/opencode/api-key`.

## Why User-Level Provider Ownership

Expand Down
17 changes: 17 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ The safe options are:
- `GONKAGATE_API_KEY`
- `--api-key-stdin`

## Why does OpenCode Desktop still say "Invalid credentials" after I pasted a new key?

If setup was installed by `@gonkagate/opencode-setup`, GonkaGate auth is owned
by the managed secret file at `~/.gonkagate/opencode/api-key`. The OpenCode
config points at that file with
`provider.gonkagate.options.apiKey = {file:~/.gonkagate/opencode/api-key}`.

The OpenCode Desktop custom-provider form can show a new key while the running
sidecar still resolves the installer-managed file. To replace the key, rerun
setup instead of relying on the Desktop form:

```bash
printf '%s' "$GONKAGATE_API_KEY" | npx @gonkagate/opencode-setup --api-key-stdin --scope project --yes
```

Then restart OpenCode Desktop so it reloads the managed secret file.

## Why does non-interactive setup require `--scope` or `--yes`?

Because the installer needs an explicit safe way to choose between `user` and
Expand Down
3 changes: 3 additions & 0 deletions src/cli/render.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CommanderError } from "commander";
import { CONTRACT_METADATA } from "../constants/contract.js";
import type { InstallFlowResult } from "../install/contracts/install-flow.js";
import { redactSecretBearingText } from "../install/redact.js";
import type {
Expand Down Expand Up @@ -96,6 +97,8 @@ function renderHumanResult(result: InstallFlowResult): string {
`Model: ${result.modelDisplayName} (${result.modelRef})`,
`Scope: ${formatScopeLabel(result.scope)}`,
"Next: opencode",
`Rotate key later: printf '%s' "$GONKAGATE_API_KEY" | ${CONTRACT_METADATA.publicEntrypoint} --api-key-stdin --scope ${result.scope} --yes`,
"OpenCode Desktop: restart after rerunning setup so it reloads the managed key file.",
"",
].join("\n");
}
Expand Down
12 changes: 10 additions & 2 deletions test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ test("CLI emits structured JSON failed payloads for resolved-config mismatches",
}
});

test("human-readable success output ends with the minimal next step", async () => {
test("human-readable success output includes next step and key rotation guidance", async () => {
const fixture = await createCliFixture();

try {
Expand All @@ -492,7 +492,15 @@ test("human-readable success output ends with the minimal next step", async () =
fixture.stdout.contents,
/GonkaGate is configured for OpenCode\./,
);
assert.match(fixture.stdout.contents, /Next: opencode\n$/);
assert.match(fixture.stdout.contents, /Next: opencode/);
assert.match(
fixture.stdout.contents,
/Rotate key later: printf '%s' "\$GONKAGATE_API_KEY" \| npx @gonkagate\/opencode-setup --api-key-stdin --scope project --yes/,
);
assert.match(
fixture.stdout.contents,
/OpenCode Desktop: restart after rerunning setup so it reloads the managed key file\.\n$/,
);
} finally {
await fixture.harness.cleanup();
}
Expand Down
3 changes: 3 additions & 0 deletions test/docs-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ test("README captures the shipped runtime truth and current opencode contract",
/WSL/i,
/--api-key-stdin/,
/GONKAGATE_API_KEY/,
/OpenCode Desktop[\s\S]*restart[\s\S]*managed secret file/i,
/~\/\.gonkagate\/opencode\/backups\/project-config/,
]);

Expand Down Expand Up @@ -139,6 +140,7 @@ test("implementation docs capture the shipped setup architecture and boundaries"
/WSL/i,
/inherited user-profile ACLs|does not attempt to rewrite Windows ACLs/i,
/project scope writes only activation settings/i,
/OpenCode Desktop[\s\S]*restart[\s\S]*~\/\.gonkagate\/opencode\/api-key/i,
/~\/\.gonkagate\/opencode\/backups\/project-config/,
]);

Expand All @@ -164,6 +166,7 @@ test("implementation docs capture the shipped setup architecture and boundaries"
/provider options|model options|headers|compatibility metadata/i,
/provider\.gonkagate\.models[\s\S]*\/models|\/models[\s\S]*provider\.gonkagate\.models/i,
/provider\.gonkagate\.options\.apiKey/i,
/OpenCode Desktop[\s\S]*Invalid credentials[\s\S]*--api-key-stdin/i,
/raw `opencode debug config` output/i,
]);

Expand Down
Loading