Skip to content

feat(cli): add lightning code to set up OpenCode for code.lightning.ai - #192

Merged
dmitsf merged 3 commits into
mainfrom
feat/lightning-code-setup
Oct 9, 2026
Merged

dmitsf merged 3 commits into
mainfrom
feat/lightning-code-setup

Conversation

@rusenask

@rusenask rusenask commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds a lightning code command group that sets up coding agents to use code.lightning.ai, starting with OpenCode:

  • lightning code setup opencode: adds Lightning as a model provider and creates an API key that bills the chosen org.
  • lightning code status: shows which tool is set up, with which org, key and default model.
  • lightning code remove opencode: undoes the setup and revokes the key.
  • lightning code token: creates a key for any other tool (Claude Code, Cursor, scripts).

pi and dsh can be added later as more tools in the same flow.

Usage

$ lightning code setup opencode --org my-org
OpenCode now uses code.lightning.ai, billed to My Org [my-org].
  Provider: lightning in ~/.config/opencode/opencode.json
  API key:  opencode on my-laptop 2026-10-09 in ~/.local/share/opencode/auth.json
  Default:  lightning/deepseek-v4.1-flash

Start coding with `opencode`, and switch models with /models.

Without --org, a terminal session lists your orgs with their plans and asks which one pays. The default is the org used last time, or the first paid one. Without a terminal, --org is required.

$ lightning code setup opencode
Which organization should pay for code.lightning.ai?
   1. Karolis Org (personal)  (Free, upgrade needed)
   2. Achira  (Enterprise)
   3. august03  (Teams)
   ...
Organization [2]:

A Free org stops with an upgrade link, and no key is created:

$ lightning code setup opencode --org my-free-org
Error: my-free-org is on the Free plan, which doesn't include code.lightning.ai.
Upgrade to Pro or Teams to use it: https://lightning.ai/me/settings/coding-usage?coding_org=<org id>
Compare plans: https://lightning.ai/pricing

Other options:

$ lightning code setup opencode --org my-org --dry-run              # show the config diff, write nothing
$ lightning code setup opencode --org my-org --model glm-5.3        # make GLM-5.3 the default
$ lightning code setup opencode --org my-org --rotate-key           # new key, old one revoked
$ lightning code setup opencode --org my-org --force                # replace a hand-made lightning provider

$ lightning code status
OpenCode
  Org:     my-org
  API key: opencode on my-laptop 2026-10-09 (01m4g15x16e9h261rzhy0sjnq7)
  Config:  ~/.config/opencode/opencode.json
  Default: lightning/deepseek-v4.1-flash

$ lightning code remove opencode
Removed the lightning provider from ~/.config/opencode/opencode.json
Removed the default model lightning/deepseek-v4.1-flash
Removed the API key from ~/.local/share/opencode/auth.json
Revoked the API key 'opencode on my-laptop 2026-10-09'.

Keys for other tools:

$ export OPENAI_BASE_URL=https://code.lightning.ai/v1
$ export OPENAI_API_KEY=$(lightning code token --org my-org)

$ export ANTHROPIC_BASE_URL=https://code.lightning.ai
$ export ANTHROPIC_AUTH_TOKEN=$(lightning code token --org my-org --name claude-code)

$ lightning code token --org my-org --json   # api_key, base_url, key_id, key_name, org, org_id

How it avoids overwriting the user's config

  • Only provider.lightning is changed. It's inserted into the global opencode.jsonc or opencode.json (whichever OpenCode reads last) by a small JSONC editor, lightning_sdk/utils/jsonc.py. Comments, key order and the user's other settings stay byte-for-byte. The file is backed up as *.lightning-backup first.
  • The key goes into OpenCode's own auth.json (mode 0600), never into the config file. This is where opencode auth login stores keys, and OpenCode gives it to the provider with the same id. The entry's metadata records the org and key id for re-runs, status and remove.
  • The default model is DeepSeek V4.1 Flash (lightning/deepseek-v4.1-flash). It's set only if the user has no default yet, or when --model is passed.
  • A lightning provider the user added by hand needs --force to be replaced, e.g. one pasted from the Coding Usage settings page with the key inline.
  • Setup warns about settings that would override it: OPENCODE_CONFIG* / OPENCODE_AUTH_CONTENT, enabled_providers / disabled_providers, and project opencode.json files that set their own model or lightning provider.

Plans and keys

  • The plan comes from GetBillingSubscription; the backend already reports a lapsed subscription as Free.
    • Pro, Teams and Enterprise are accepted.
    • Free gets the upgrade link.
    • Orgs with disable_coding_agents are refused.
  • Keys use the org's Member role and no teamspace, the same as the Coding Usage settings page. They can be listed and deleted with lightning api-key.
  • Re-runs reuse the key if it still exists. Switching org, or passing --rotate-key, creates a new key and revokes the old one. If writing the files fails, the new key is revoked.
  • Following the CLI's no-implicit-interaction rule, there are no confirm prompts or browser opening. The only prompt is the org picker, and only when stdin and stdout are terminals.

Models come from /v1/models

setup reads the model list from https://code.lightning.ai/v1/models (public, no key), using the OpenRouter-style fields added in gridai/grid#47892, so a new model shows up without a CLI release:

OpenCode setting From /v1/models
model key / id id (lightning-ai/ prefix dropped for the key)
name name
limit.context context_length
limit.output top_provider.max_completion_tokens (131,072)
attachment, modalities.input architecture.input_modalities
tool_call / reasoning tools / reasoning_effort in supported_parameters
  • If the fetch fails, setup says so and uses a built-in list matching what the API serves today. A test checks the built-in list against a saved copy of the live response.
  • --model is checked against the fetched list. An unknown model is an error that lists the served ones.
  • If DeepSeek V4.1 Flash isn't served, the default falls back to the first listed model.
  • A default on a Lightning model that's no longer served is replaced with the current default.
  • Stating 131,072 as the output limit is safe. OpenCode sends at most 32,000 as max_tokens, and its compaction keeps prompts within limit.context minus that. So prompt plus max_tokens never goes over the advertised context, and the Flash models' 400 on oversized requests can't happen from OpenCode.

python/examples/code/opencode.json shows what setup writes on a fresh machine, from the built-in list. A test fails if the two stop matching.

Docs and examples

  • python/examples/code_cli.rst: tutorial
  • python/examples/code/opencode.json, opencode-auth.json: example files
  • python/docs/cli/code.rst: CLI reference page

Testing

End to end in a Lightning Studio (lightningai-engineering/skills, CPU), with this branch installed from a wheel and OpenCode 1.18.35 installed with curl -fsSL https://opencode.ai/install | bash:

  • setup without --org and without a terminal: asks for --org and lists the orgs.
  • setup --org lightningai-engineering: creates the key, writes auth.json with mode 600, and status reports the setup.
  • opencode models lightning lists all 3 models. opencode auth list shows the lightning credential.
  • opencode run "Reply with exactly the word pong" answers pong via lightning/glm-5.3, so the key from auth.json authenticates against code.lightning.ai.
  • After the default changed to DeepSeek V4.1 Flash: a fresh setup sets "model": "lightning/deepseek-v4.1-flash", and opencode run without -m answers pong via deepseek-v4.1-flash.
  • With the model list from the live /v1/models: setup configured all 3 models with output 131,072 and image input on both Flash models, and --model gpt-9 is rejected with the served list.
  • Image input through OpenCode (opencode run … -f blue.png): DeepSeek V4.1 Flash and GLM-5.3 Flash both answer Blue.
  • A re-run reuses the key. --model glm-5.3-flash switches the default, and opencode run answers via glm-5.3-flash. The user's comments and settings survive.
  • --rotate-key revokes the old key (checked with lightning api-key list), and opencode run still works with the new key.
  • A Free org gets the upgrade error, and no key is created.
  • token --json returns a working key.
  • The org picker in a real terminal (via script) lists all orgs with their plans, and its default is the org used last time.
  • A config pasted from the web UI (apiKey inline) is refused without --force. With --force it's backed up and replaced, and opencode run works.
  • remove restores the user's config, revokes the key, and OpenCode reports Provider not found: lightning.
  • All test keys were revoked afterwards.

Locally:

  • tests/cli/code and tests/utils/test_jsonc.py (58 tests): JSONC splice and remove round-trips, parsing /v1/models (a saved copy of the live response) and falling back when it's unreachable, setup, re-run, rotate, switching org, Free plan, dry run, foreign config, invalid config, a write failure revoking the new key, status, remove, token, the org picker, and the example drift test.
  • tests/cli/test_entrypoint.py and tests/cli/test_non_interactive_contract.py pass.
  • ruff and mypy are clean.

🤖 Generated with Claude Code

`lightning code setup opencode` adds code.lightning.ai as the `lightning`
provider in OpenCode's global config and creates an API key that bills the
org the user picks (--org, or a numbered prompt in a terminal). Pro, Teams
and Enterprise orgs are accepted; a Free org gets a link to upgrade instead.

The user's config is never rewritten: the provider block is spliced into the
JSONC file so comments and other settings stay byte-for-byte, and the key goes
into OpenCode's auth.json (0600) rather than the config file. Re-runs reuse
the key, switching org or --rotate-key replaces and revokes it, and --dry-run
shows the diff.

Also adds `lightning code status`, `lightning code remove opencode` (removes
our entries and revokes the key) and `lightning code token` for other tools.
@dmitsf
dmitsf merged commit dd8b5c8 into main Oct 9, 2026
25 checks passed
@dmitsf
dmitsf deleted the feat/lightning-code-setup branch October 9, 2026 15:17
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.

3 participants