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
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,25 @@ opencode plugin file://$PWD

## Authenticate

Requires the plugin to be installed (see above). Uses the **Command Code Go plan ($1/mo)** — same browser login as `cmd login`. No Studio API key.
Requires the plugin to be installed (see above). Preferred: **Command Code Go plan ($1/mo)** browser login (same as `cmd login`). You can also paste an API key / session token.

```bash
# Option A — sync from Command Code CLI (recommended)
npm i -g command-code@latest
cmd login # browser → Studio CLI auth (Go plan). Laguna S 2.1 is $0 credits.
cmd login # browser → Studio CLI auth (Go plan)
opencode auth login --provider command-code
# pick "Use existing cmd login session"

# Option B — browser OAuth (Go $1)
opencode auth login --provider command-code
# pick "Login with Command Code (Go $1)"

# Option C — paste API key / session token
opencode auth login --provider command-code
# pick "Enter Command Code API key" and paste into the prompt field
```

Then start OpenCode, pick provider **command-code**, and choose Laguna S 2.1 free:
Then start OpenCode, pick provider **command-code**, and choose any model from the live catalog (`cmd --list-models`). Laguna S 2.1 free is the default when available:

```bash
opencode run "Summarise this repository in five bullets." --model command-code/laguna-s-2.1-free
Expand All @@ -92,8 +96,9 @@ Laguna S 2.1 free requires an active Go (or higher) account with credits on file
| | |
|---|---|
| **Gateway proxy** | Talks to `api.commandcode.ai/alpha/generate` — same transport as the `cmd` CLI (`mode=agent`). |
| **Laguna S 2.1 free** | 256k context, reasoning, $0 while the deal lasts. Default aliases resolve here. |
| **Go-plan auth** | Browser OAuth like `cmd login` ($1/mo). Syncs `~/.commandcode/auth.json`. No Studio API key. |
| **Live model catalog** | Discovered from `cmd --list-models` (enriched by the installed CLI’s models.md) — not hardcoded. |
| **Laguna S 2.1 free** | Default when present — 256k context, $0 while the deal lasts. |
| **Auth options** | Go-plan browser OAuth, sync from `cmd login`, or paste an API key / session token. |
| **Agent-grade tools** | OpenCode tool calls park and resume; MCP-prefixed tools are tracked separately. |
| **Attachments** | Images, PDFs, text/binary files from OpenCode. Text-only models get image placeholders (Command Code behavior). |
| **Auto-compact** | Context-fraction tips + tiered client compact before the 256k window overflows. |
Expand All @@ -109,9 +114,9 @@ OpenCode
└─ poolside/laguna-s-2.1-free (default)
```

Model catalog: aliases `laguna-s-2.1-free` / `laguna` / `default` (and siblings) map to
`poolside/laguna-s-2.1-free`. Auth credentials are mirrored between OpenCode `auth.json`
and `~/.commandcode/auth.json`.
Model catalog: live from `cmd --list-models`. Aliases like `laguna` / `laguna-s-2.1-free`
resolve to `poolside/laguna-s-2.1-free` when that model is present. Auth credentials are
mirrored between OpenCode `auth.json` and `~/.commandcode/auth.json`.

## Requirements

Expand Down Expand Up @@ -157,10 +162,10 @@ Local pin refresh after a release:
## FAQ

**Do I need a Studio API key?**
No. Use Go-plan browser login (or sync from `cmd login`).
No. Prefer Go-plan browser login or sync from `cmd login`. You can also use **Enter Command Code API key** if you already have a session token.

**Is Laguna free?**
Yes — `poolside/laguna-s-2.1-free` is the default free model ($0 while the deal lasts).
**Are only Laguna models available?**
No. The plugin loads the full live catalog from `cmd --list-models`. Laguna is just the default free model used for testing.

**Why isn’t Command Code listed without the plugin?**
OpenCode doesn’t ship Command Code as a built-in provider; this plugin registers it.
Expand Down
63 changes: 55 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/**
* OpenCode Command Code Auth Plugin
*
* Enables Command Code (Laguna S 2.1 free and gateway models) inside OpenCode via:
* 1. Command Code Go-plan browser OAuth (`cmd login`) — no API key
* Enables Command Code models inside OpenCode via:
* 1. Go-plan browser OAuth, CLI session sync, or pasted API key
* 2. Local OpenAI-compatible proxy → api.commandcode.ai /alpha/generate
* 3. Tools/MCP park-resume, attachments, compact, and usage accounting
* 3. Dynamic model catalog from `cmd --list-models`
* 4. Tools/MCP park-resume, attachments, compact, and usage accounting
*
* Register in opencode.json:
* { "plugin": ["@otto-assistant/opencode-commandcode"] }
Expand All @@ -16,6 +17,8 @@ import {
resetPendingCommandLogin,
startCommandBrowserLogin,
syncCommandCodeCredentialsToOpenCode,
validateCommandApiKey,
writeCommandCodeAuth,
} from "./auth-login.js";
import {
DEFAULT_MODEL_ID,
Expand All @@ -33,6 +36,7 @@ import {
buildConfigVariants,
buildEffortVariants,
getCommandModels,
refreshCommandModels,
type CommandModel,
} from "./models.js";
import {
Expand Down Expand Up @@ -265,7 +269,7 @@ async function loadRuntime(
provider?: { models?: Record<string, unknown> },
): Promise<{ port: number; providerModels: Record<string, unknown> } | undefined> {
await resolveAccessToken(input, getAuth);
const models = getCommandModels();
const models = refreshCommandModels();
await startProxy(async () => resolveAccessToken(input, getAuth));
const providerModels = buildProviderModels(models);
if (provider) provider.models = providerModels;
Expand Down Expand Up @@ -310,7 +314,8 @@ export const CommandCodePlugin: Plugin = async (
return synced?.access ?? null;
});

// Always seed Laguna catalog so the provider is discoverable pre-login.
// Always seed the live catalog so the provider is discoverable.
refreshCommandModels();
ensureProviderConfig(config as Record<string, any>, getCommandModels());
},

Expand Down Expand Up @@ -413,7 +418,7 @@ export const CommandCodePlugin: Plugin = async (
return {
url: current.url,
instructions:
"Opens Command Code Studio CLI login (Go plan, $1/mo). Approve in the browser — session is posted back automatically. Laguna S 2.1 free costs $0 credits. No API key.",
"Opens Command Code Studio CLI login (Go plan, $1/mo). Approve in the browser — session is posted back automatically. Live models load from `cmd --list-models` after login.",
method: "auto" as const,
async callback() {
try {
Expand Down Expand Up @@ -443,7 +448,7 @@ export const CommandCodePlugin: Plugin = async (
return {
url: "https://commandcode.ai/docs/quickstart",
instructions:
"If you already ran `npm i -g command-code@latest && cmd login` (Go $1 plan), click Complete to sync ~/.commandcode/auth.json. No API key.",
"If you already ran `npm i -g command-code@latest && cmd login` (Go $1 plan), click Complete to sync ~/.commandcode/auth.json.",
method: "auto" as const,
async callback() {
const again = syncCommandCodeCredentialsToOpenCode();
Expand All @@ -458,6 +463,43 @@ export const CommandCodePlugin: Plugin = async (
};
},
},
{
type: "api",
label: "Enter Command Code API key",
prompts: [
{
type: "text",
key: "apiKey",
message: "Paste your Command Code API key / session token",
placeholder: "user_… or Studio key",
validate: (value) => {
if (!value || !value.trim()) return "API key is required";
if (value.trim().length < 16) return "Key looks too short";
return undefined;
},
},
],
async authorize(inputs) {
const key = inputs?.apiKey?.trim() || inputs?.key?.trim() || "";
if (!key) return { type: "failed" as const };
const check = await validateCommandApiKey(key);
if (!check.valid) {
log.warn(
"[opencode-commandcode] API key validation failed",
check.error,
);
return { type: "failed" as const };
}
writeCommandCodeAuth({
key,
access: key,
refresh: "api-key",
expires: Date.now() + 1000 * 60 * 60 * 24 * 365,
});
refreshCommandModels();
return { type: "success" as const, key };
},
},
],
},
};
Expand All @@ -466,7 +508,12 @@ export const CommandCodePlugin: Plugin = async (
export default CommandCodePlugin;

export { detectCommandCode } from "./detect.js";
export { getCommandModels, COMMAND_CODE_MODELS } from "./models.js";
export {
getCommandModels,
listCommandCodeModels,
refreshCommandModels,
invalidateCommandModelCache,
} from "./models.js";
export {
startProxy,
stopProxy,
Expand Down
Loading