pi-commandcode-provider 0.4.3 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/CONTRIBUTING.md +18 -0
- package/README.md +100 -72
- package/index.ts +84 -91
- package/package.json +15 -6
- package/scripts/pi-authenticated.mjs +49 -0
- package/scripts/pi-isolated.mjs +78 -0
- package/src/converters.ts +64 -85
- package/src/core.ts +107 -31
- package/src/cost.ts +16 -4
- package/src/json-schema.ts +382 -0
- package/src/models.ts +251 -15
- package/src/overflow.ts +120 -0
- package/src/pricing.ts +226 -0
- package/src/runtime.ts +279 -0
- package/src/types.ts +19 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.5.1 - 2026-08-11
|
|
6
|
+
|
|
7
|
+
- Add model-specific image input capabilities from the `command-code@1.15.1` catalog and forward user and tool-result images using the current Command Code wire format.
|
|
8
|
+
- Update the Command Code client version header to `1.15.1`.
|
|
9
|
+
|
|
10
|
+
### Contributors
|
|
11
|
+
|
|
12
|
+
- @DiyarD — reported missing vision support for GPT-5.6 Luna, Muse Spark 1.2, and other vision-capable models.
|
|
13
|
+
|
|
14
|
+
## 0.5.0 - 2026-08-07
|
|
15
|
+
|
|
16
|
+
- Stop replaying completed assistant reasoning traces to Command Code while preserving visible text and completed tool calls in follow-up request history.
|
|
17
|
+
- Add `/commandcode-refresh` and `/commandcode-status` commands for safe model-catalog refreshes and redacted diagnostics.
|
|
18
|
+
- Bound model discovery to a configurable 10-second timeout so a slow Provider API cannot block pi startup; timed-out discovery uses the validated cache when available.
|
|
19
|
+
- Normalize Command Code context overflow failures so pi can auto-compact and retry, while leaving unrelated rate-limit and capacity errors unchanged.
|
|
20
|
+
- Keep the legacy `/alpha/generate` integration explicitly text-only: image input and image tool results are rejected instead of being silently dropped, and models do not claim image capability until the protocol exposes documented support and limits.
|
|
21
|
+
- Replace blanket reasoning metadata with model-specific Command Code effort support. Known models expose a `thinkingLevelMap`, and selected supported Pi levels are forwarded as `params.reasoning_effort`; unsupported or unknown models do not receive reasoning request fields.
|
|
22
|
+
- Add repository commands for testing the current checkout either in a logged-out, automatically cleaned-up pi environment or with existing credentials and only Command Code models enabled.
|
|
23
|
+
- Refresh display pricing for the current Command Code model catalog, remove expired Qwen promotional rates, add current free and discounted models, and require review when temporary prices expire.
|
|
24
|
+
- Use the host-provided `pi-ai` and `pi-coding-agent` core packages instead of installing private runtime copies, including for local and out-of-store development checkouts.
|
|
25
|
+
- Fix cached input tokens being counted twice.
|
|
26
|
+
|
|
27
|
+
### Contributors
|
|
28
|
+
|
|
29
|
+
- @IfkumRfnl — fixed cached input token accounting.
|
|
30
|
+
|
|
3
31
|
## 0.4.3 - 2026-08-02
|
|
4
32
|
|
|
5
33
|
- Allow pi to start when model discovery is unavailable. The provider now caches the last successfully fetched model catalog so previously discovered Command Code models remain selectable offline; a first offline start without a cache keeps Command Code unavailable until `/reload` succeeds.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -21,9 +21,27 @@ npm run test:models
|
|
|
21
21
|
npm run test:oauth
|
|
22
22
|
npm run test:abort
|
|
23
23
|
npm run test:stream
|
|
24
|
+
npm run test:pi-isolated
|
|
25
|
+
npm run test:pi-authenticated
|
|
24
26
|
npm run test:pi-local
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
Start an isolated pi instance with only the current checkout installed and no existing Command Code credentials:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm run pi:isolated
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run `/login` inside pi. Temporary credentials, configuration, and sessions are deleted when pi exits.
|
|
36
|
+
|
|
37
|
+
Start the current checkout with your existing pi credentials and only Command Code models in the model picker:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm run pi:authenticated
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Both commands accept additional pi arguments after `--`, for example `npm run pi:authenticated -- --model claude-sonnet-4-6`.
|
|
44
|
+
|
|
27
45
|
Before opening a PR, run:
|
|
28
46
|
|
|
29
47
|
```sh
|
package/README.md
CHANGED
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
# pi-commandcode-provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/patlux/pi-commandcode-provider/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/patlux/pi-commandcode-provider/actions/workflows/memory-benchmark.yml)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A custom provider for [pi](https://github.com/earendil-works/pi) that connects to the [Command Code](https://commandcode.ai) Provider API.
|
|
6
7
|
|
|
7
|
-
> **
|
|
8
|
-
|
|
9
|
-
> 💰 **Current offers:** Command Code offers [4× usage of DeepSeek V4 Pro](https://commandcode.ai/docs/resources/pricing-limits#deepseek-v4-pro-4x-usage) and [2× usage of Qwen 3.7 Max](https://commandcode.ai/docs/resources/pricing-limits#qwen-3.7-max-2x-usage).
|
|
10
|
-
|
|
11
|
-
## Models
|
|
12
|
-
|
|
13
|
-
Models are fetched live from Command Code's Provider API at startup, so new models like Qwen 3.7 Max show up without a package release.
|
|
14
|
-
|
|
15
|
-
You can list the current Command Code models with:
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
pi -e index.ts --list-models
|
|
19
|
-
```
|
|
8
|
+
> **Disclaimer:** This is an unofficial, community-maintained integration. It is not affiliated with, endorsed by, or supported by Command Code. You need your own Command Code account and API key or subscription. Command Code's terms, availability, and pricing apply.
|
|
20
9
|
|
|
21
10
|
## Install
|
|
22
11
|
|
|
@@ -24,59 +13,49 @@ pi -e index.ts --list-models
|
|
|
24
13
|
pi install npm:pi-commandcode-provider
|
|
25
14
|
```
|
|
26
15
|
|
|
27
|
-
|
|
16
|
+
Start or reload pi, then authenticate:
|
|
28
17
|
|
|
29
|
-
```
|
|
30
|
-
|
|
18
|
+
```txt
|
|
19
|
+
/login
|
|
31
20
|
```
|
|
32
21
|
|
|
33
|
-
|
|
22
|
+
Select **Use a subscription**, then **Command Code**. Complete the browser flow and choose a model with `/model`.
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
/reload
|
|
37
|
-
```
|
|
24
|
+
## Oh My Pi
|
|
38
25
|
|
|
39
|
-
|
|
26
|
+
Install the same package in [Oh My Pi](https://github.com/can1357/oh-my-pi):
|
|
40
27
|
|
|
41
28
|
```sh
|
|
42
29
|
omp plugin install pi-commandcode-provider
|
|
43
30
|
```
|
|
44
31
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```txt
|
|
48
|
-
/reload
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Setup
|
|
52
|
-
|
|
53
|
-
Set your Command Code API key using one of these methods:
|
|
54
|
-
|
|
55
|
-
### 1. Browser login (recommended)
|
|
56
|
-
|
|
57
|
-
In pi, run:
|
|
32
|
+
Restart OMP or run `/reload`, then use `/login` and select **Use a subscription** followed by **Command Code**.
|
|
58
33
|
|
|
59
|
-
|
|
60
|
-
/login
|
|
61
|
-
```
|
|
34
|
+
## Authentication
|
|
62
35
|
|
|
63
|
-
|
|
36
|
+
### Browser login
|
|
64
37
|
|
|
65
|
-
|
|
38
|
+
Run `/login` in pi or OMP. Select **Use a subscription**, then **Command Code**. The browser flow stores the returned credential in the host's auth file.
|
|
66
39
|
|
|
67
|
-
|
|
40
|
+
<img width="1520" height="554" alt="Select Command Code in pi's login dialog" src="https://github.com/user-attachments/assets/071e929a-6f49-4803-bfec-7a31368fb12a" />
|
|
68
41
|
|
|
69
|
-
|
|
42
|
+
If automatic transfer from the browser fails, copy the API key shown by Command Code and paste it into the terminal prompt.
|
|
70
43
|
|
|
71
|
-
###
|
|
44
|
+
### Environment variable
|
|
72
45
|
|
|
73
46
|
```sh
|
|
74
47
|
export COMMANDCODE_API_KEY="user_..."
|
|
75
48
|
```
|
|
76
49
|
|
|
77
|
-
###
|
|
50
|
+
### Auth file
|
|
51
|
+
|
|
52
|
+
The provider also reads existing credentials from:
|
|
78
53
|
|
|
79
|
-
|
|
54
|
+
- `~/.commandcode/auth.json`
|
|
55
|
+
- `~/.pi/agent/auth.json`
|
|
56
|
+
- `~/.omp/agent/auth.json`
|
|
57
|
+
|
|
58
|
+
Supported examples:
|
|
80
59
|
|
|
81
60
|
```json
|
|
82
61
|
{
|
|
@@ -84,8 +63,6 @@ Create `~/.commandcode/auth.json`:
|
|
|
84
63
|
}
|
|
85
64
|
```
|
|
86
65
|
|
|
87
|
-
The official Command Code CLI auth shape is also supported:
|
|
88
|
-
|
|
89
66
|
```json
|
|
90
67
|
{
|
|
91
68
|
"command-code": {
|
|
@@ -95,8 +72,6 @@ The official Command Code CLI auth shape is also supported:
|
|
|
95
72
|
}
|
|
96
73
|
```
|
|
97
74
|
|
|
98
|
-
Or use a pi/OMP auth file at `~/.pi/agent/auth.json` or `~/.omp/agent/auth.json`:
|
|
99
|
-
|
|
100
75
|
```json
|
|
101
76
|
{
|
|
102
77
|
"commandcode": "user_..."
|
|
@@ -105,55 +80,108 @@ Or use a pi/OMP auth file at `~/.pi/agent/auth.json` or `~/.omp/agent/auth.json`
|
|
|
105
80
|
|
|
106
81
|
## Usage
|
|
107
82
|
|
|
108
|
-
|
|
83
|
+
Open `/model` and select one of the models provided by Command Code. Model availability changes over time and is refreshed from the Provider API when the extension loads.
|
|
109
84
|
|
|
110
|
-
|
|
111
|
-
|
|
85
|
+
### Reasoning support
|
|
86
|
+
|
|
87
|
+
Reasoning metadata is enriched only for models whose Command Code effort support is known. Those models register a model-specific `thinkingLevelMap`, so pi and OMP expose only supported levels. A selected supported level is sent as the documented `params.reasoning_effort` field; `off`, unsupported levels, and newly discovered models without metadata do not add reasoning fields to the request. No prompt instructions are injected.
|
|
88
|
+
|
|
89
|
+
Reasoning blocks from completed assistant turns remain visible in pi's local session, but are not replayed to Command Code in later requests. Only the assistant's user-visible text and completed tool calls are sent back as history. This matches the current Command Code CLI behavior and prevents prior private reasoning traces from interfering with reasoning on follow-up turns.
|
|
90
|
+
|
|
91
|
+
List Command Code models from the terminal:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
pi --list-models commandcode
|
|
112
95
|
```
|
|
113
96
|
|
|
114
|
-
|
|
97
|
+
In OMP, use:
|
|
115
98
|
|
|
116
99
|
```sh
|
|
117
|
-
|
|
118
|
-
omp -e index.ts --list-models
|
|
100
|
+
omp models
|
|
119
101
|
```
|
|
120
102
|
|
|
121
|
-
|
|
103
|
+
For non-interactive OMP requests, use a provider-qualified model ID shown by `omp models`. For example:
|
|
122
104
|
|
|
123
105
|
```sh
|
|
124
106
|
omp -p "hello" --model commandcode/deepseek/deepseek-v4-flash
|
|
125
107
|
```
|
|
126
108
|
|
|
127
|
-
|
|
109
|
+
## Model discovery and offline behavior
|
|
128
110
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
On startup, the provider fetches:
|
|
111
|
+
The provider fetches the current model catalog from:
|
|
132
112
|
|
|
133
113
|
```txt
|
|
134
114
|
https://api.commandcode.ai/provider/v1/models
|
|
135
115
|
```
|
|
136
116
|
|
|
137
|
-
The last
|
|
117
|
+
The last successful catalog is cached at `<agent-dir>/commandcode-models.json`. For pi this is `~/.pi/agent/commandcode-models.json` by default. Compatible hosts such as OMP use their own agent directory.
|
|
118
|
+
|
|
119
|
+
If the endpoint is temporarily unavailable, the provider uses the cached catalog. On a first offline start without a cache, pi still loads, but Command Code models remain unavailable until the connection is restored and `/commandcode-refresh` succeeds.
|
|
120
|
+
|
|
121
|
+
While pi is running, use these provider commands without restarting:
|
|
122
|
+
|
|
123
|
+
- `/commandcode-refresh` fetches and re-registers the current model catalog. Overlapping refreshes are coalesced, and a failed refresh keeps the last valid catalog active.
|
|
124
|
+
- `/commandcode-status` shows redacted discovery diagnostics, including the source, model count, timestamps, cache path, endpoint, and warning.
|
|
125
|
+
|
|
126
|
+
The following environment variables are intended for tests, local mocks, and compatible API endpoints:
|
|
127
|
+
|
|
128
|
+
- `COMMANDCODE_API_BASE`
|
|
129
|
+
- `COMMANDCODE_MODELS_URL`
|
|
130
|
+
- `COMMANDCODE_MODELS_CACHE`
|
|
131
|
+
- `COMMANDCODE_MODELS_TIMEOUT_MS` (defaults to 10 seconds; invalid or non-positive values use the default)
|
|
132
|
+
|
|
133
|
+
## Image input
|
|
134
|
+
|
|
135
|
+
The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.15.1`; unknown models default to text-only until their upstream metadata is reviewed.
|
|
136
|
+
|
|
137
|
+
For vision-capable models, image blocks from user messages and tool results are forwarded in Command Code's current data-URL wire format. Text-only models reject image content before making a network request instead of silently dropping it.
|
|
138
|
+
|
|
139
|
+
## Pricing display
|
|
140
|
+
|
|
141
|
+
The Command Code Provider API does not currently include prices in its model catalog. This extension therefore keeps a static table for models with known prices so pi can display estimated request costs.
|
|
142
|
+
|
|
143
|
+
Models missing from that table display zero cost in pi. This does **not** mean that Command Code will bill the request at zero. Check the current [Command Code pricing](https://commandcode.ai/docs/resources/pricing-limits) before relying on the displayed value.
|
|
144
|
+
|
|
145
|
+
## Update and remove
|
|
146
|
+
|
|
147
|
+
Update installed pi packages:
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
pi update --extensions
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Remove the provider:
|
|
138
154
|
|
|
139
|
-
|
|
155
|
+
```sh
|
|
156
|
+
pi remove npm:pi-commandcode-provider
|
|
157
|
+
```
|
|
140
158
|
|
|
141
|
-
|
|
159
|
+
For OMP:
|
|
142
160
|
|
|
143
|
-
|
|
161
|
+
```sh
|
|
162
|
+
omp plugin upgrade pi-commandcode-provider
|
|
163
|
+
omp plugin uninstall pi-commandcode-provider
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Development
|
|
144
167
|
|
|
145
|
-
|
|
146
|
-
- Models **not** in the table fall back to zero cost. When the Provider API adds a `cost` field, the static table can be removed.
|
|
168
|
+
Start an isolated pi instance with only the current checkout installed and no existing Command Code credentials:
|
|
147
169
|
|
|
148
|
-
|
|
170
|
+
```sh
|
|
171
|
+
npm run pi:isolated
|
|
172
|
+
```
|
|
149
173
|
|
|
150
|
-
|
|
174
|
+
Run `/login` inside pi. Temporary credentials, configuration, and sessions are deleted when pi exits.
|
|
151
175
|
|
|
152
|
-
|
|
176
|
+
Start the current checkout with your existing pi credentials and only Command Code models in the model picker:
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
npm run pi:authenticated
|
|
180
|
+
```
|
|
153
181
|
|
|
154
|
-
|
|
182
|
+
Both commands accept additional pi arguments after `--`, for example `npm run pi:authenticated -- --model claude-sonnet-4-6`.
|
|
155
183
|
|
|
156
|
-
See [
|
|
184
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup and tests. See [RELEASE.md](RELEASE.md) for the release process.
|
|
157
185
|
|
|
158
186
|
## License
|
|
159
187
|
|
package/index.ts
CHANGED
|
@@ -2,96 +2,45 @@
|
|
|
2
2
|
* Command Code provider for pi.
|
|
3
3
|
*
|
|
4
4
|
* Connects pi to Command Code's API (https://api.commandcode.ai/alpha/generate).
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 1. Run `/login`, then select Command Code — opens browser to commandcode.ai, auto-stores API key
|
|
8
|
-
* 2. Set COMMANDCODE_API_KEY environment variable
|
|
9
|
-
* 3. Place API key in `~/.commandcode/auth.json` or `~/.pi/agent/auth.json`
|
|
10
|
-
* as {"apiKey": "user_..."} or {"commandcode": "user_..."}
|
|
11
|
-
*
|
|
12
|
-
* Models are fetched from Command Code's Provider API at startup.
|
|
5
|
+
* The provider uses pi's legacy extension registration surface because the
|
|
6
|
+
* current pi host exposes `registerProvider(name, config)`, including OMP.
|
|
13
7
|
*/
|
|
14
8
|
|
|
15
9
|
import { AssistantMessageEventStream } from "@earendil-works/pi-ai"
|
|
16
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getAgentDir,
|
|
12
|
+
type ExtensionAPI,
|
|
13
|
+
type ExtensionCommandContext,
|
|
14
|
+
type ProviderConfig,
|
|
15
|
+
} from "@earendil-works/pi-coding-agent"
|
|
17
16
|
import { join } from "node:path"
|
|
18
17
|
|
|
19
18
|
import { COMMAND_CODE_CLI_VERSION, createStreamCommandCode, DEFAULT_API_BASE } from "./src/core.ts"
|
|
20
19
|
import { calculateCommandCodeCost } from "./src/cost.ts"
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
cacheWrite: number
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const ZERO_MODEL_COST: CommandCodeModelCost = {
|
|
37
|
-
input: 0,
|
|
38
|
-
output: 0,
|
|
39
|
-
cacheRead: 0,
|
|
40
|
-
cacheWrite: 0,
|
|
41
|
-
}
|
|
20
|
+
import {
|
|
21
|
+
DEFAULT_MODELS_URL,
|
|
22
|
+
getModelsTimeoutMs,
|
|
23
|
+
inputModalitiesForModel,
|
|
24
|
+
loadCommandCodeModels,
|
|
25
|
+
thinkingMetadataForModel,
|
|
26
|
+
type CommandCodeModel,
|
|
27
|
+
} from "./src/models.ts"
|
|
28
|
+
import { getApiKey as getOAuthApiKey, login, refreshToken } from "./src/oauth.ts"
|
|
29
|
+
import { MODEL_COSTS, ZERO_MODEL_COST } from "./src/pricing.ts"
|
|
30
|
+
import { createCommandCodeRuntime } from "./src/runtime.ts"
|
|
31
|
+
import { normalizeCommandCodeMessage } from "./src/overflow.ts"
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"claude-haiku-4-5-20251001": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
50
|
-
"gpt-5.5": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
|
|
51
|
-
"gpt-5.4": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },
|
|
52
|
-
"gpt-5.3-codex": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
|
|
53
|
-
"gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
|
|
54
|
-
"google/gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },
|
|
55
|
-
"google/gemini-3.1-flash-lite": { input: 0.25, output: 1.5, cacheRead: 0.03, cacheWrite: 0 },
|
|
56
|
-
// 4× usage deal: 75% off (permanent, no expiry)
|
|
57
|
-
"deepseek/deepseek-v4-pro": { input: 0.435, output: 0.87, cacheRead: 0.003625, cacheWrite: 0 },
|
|
58
|
-
"deepseek/deepseek-v4-flash": { input: 0.14, output: 0.28, cacheRead: 0.028, cacheWrite: 0 },
|
|
59
|
-
"moonshotai/Kimi-K2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0 },
|
|
60
|
-
"moonshotai/Kimi-K2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0 },
|
|
61
|
-
"zai-org/GLM-5.1": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
|
|
62
|
-
"zai-org/GLM-5": { input: 1, output: 3.2, cacheRead: 0.2, cacheWrite: 0 },
|
|
63
|
-
"MiniMaxAI/MiniMax-M2.7": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
|
|
64
|
-
"MiniMaxAI/MiniMax-M2.5": { input: 0.27, output: 0.95, cacheRead: 0.03, cacheWrite: 0 },
|
|
65
|
-
"Qwen/Qwen3.6-Max-Preview": { input: 1.3, output: 7.8, cacheRead: 0.26, cacheWrite: 1.63 },
|
|
66
|
-
"Qwen/Qwen3.6-Plus": { input: 0.5, output: 3, cacheRead: 0.1, cacheWrite: 0 },
|
|
67
|
-
// 2× usage deal: 50% off through June 22, 2026
|
|
68
|
-
"Qwen/Qwen3.7-Max": { input: 1.25, output: 3.75, cacheRead: 0.25, cacheWrite: 1.56 },
|
|
69
|
-
"stepfun/Step-3.5-Flash": { input: 0.1, output: 0.3, cacheRead: 0.02, cacheWrite: 0 },
|
|
70
|
-
"xiaomi/mimo-v2.5-pro": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
71
|
-
"xiaomi/mimo-v2.5": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const streamCommandCode = createStreamCommandCode({
|
|
75
|
-
createStream: () => new AssistantMessageEventStream(),
|
|
76
|
-
calculateCost: calculateCommandCodeCost,
|
|
77
|
-
apiBase: API_BASE,
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
// Extension entry point
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
|
|
84
|
-
export default async function (pi: ExtensionAPI) {
|
|
85
|
-
const { models, warning } = await loadCommandCodeModels({
|
|
86
|
-
url: MODELS_URL,
|
|
87
|
-
cachePath: MODELS_CACHE_PATH,
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
if (warning) console.warn(`[commandcode] ${warning}`)
|
|
91
|
-
|
|
92
|
-
pi.registerProvider("commandcode", {
|
|
33
|
+
function createProviderConfig(
|
|
34
|
+
models: readonly CommandCodeModel[],
|
|
35
|
+
apiBase: string,
|
|
36
|
+
streamCommandCode: ProviderConfig["streamSimple"],
|
|
37
|
+
): ProviderConfig {
|
|
38
|
+
return {
|
|
93
39
|
name: "Command Code",
|
|
94
|
-
baseUrl:
|
|
40
|
+
baseUrl: apiBase,
|
|
41
|
+
// Keep environment authentication dynamic. OAuth credentials are resolved
|
|
42
|
+
// by pi's oauth registration, while the custom stream retains its own
|
|
43
|
+
// request-time legacy-file fallback for older compatible hosts.
|
|
95
44
|
apiKey: "$COMMANDCODE_API_KEY",
|
|
96
45
|
authHeader: true,
|
|
97
46
|
api: "commandcode-custom",
|
|
@@ -104,16 +53,60 @@ export default async function (pi: ExtensionAPI) {
|
|
|
104
53
|
name: "Command Code",
|
|
105
54
|
login,
|
|
106
55
|
refreshToken,
|
|
107
|
-
getApiKey,
|
|
56
|
+
getApiKey: getOAuthApiKey,
|
|
108
57
|
},
|
|
109
|
-
models: models.map(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
58
|
+
models: models.map(createProviderModel),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function createProviderModel(model: {
|
|
63
|
+
id: string
|
|
64
|
+
name: string
|
|
65
|
+
reasoning: boolean
|
|
66
|
+
contextWindow: number
|
|
67
|
+
maxTokens: number
|
|
68
|
+
}) {
|
|
69
|
+
return {
|
|
70
|
+
id: model.id,
|
|
71
|
+
name: model.name,
|
|
72
|
+
reasoning: model.reasoning,
|
|
73
|
+
...(thinkingMetadataForModel(model.id) ?? {}),
|
|
74
|
+
input: inputModalitiesForModel(model.id),
|
|
75
|
+
cost: MODEL_COSTS[model.id] ?? ZERO_MODEL_COST,
|
|
76
|
+
contextWindow: model.contextWindow,
|
|
77
|
+
maxTokens: model.maxTokens,
|
|
78
|
+
} as const
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default async function (pi: ExtensionAPI) {
|
|
82
|
+
const apiBase = process.env.COMMANDCODE_API_BASE ?? DEFAULT_API_BASE
|
|
83
|
+
const modelsUrl = process.env.COMMANDCODE_MODELS_URL ?? DEFAULT_MODELS_URL
|
|
84
|
+
const modelsTimeoutMs = getModelsTimeoutMs()
|
|
85
|
+
const modelsCachePath =
|
|
86
|
+
process.env.COMMANDCODE_MODELS_CACHE ?? join(getAgentDir(), "commandcode-models.json")
|
|
87
|
+
const streamCommandCode = createStreamCommandCode({
|
|
88
|
+
createStream: () => new AssistantMessageEventStream(),
|
|
89
|
+
calculateCost: calculateCommandCodeCost,
|
|
90
|
+
apiBase,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
pi.on("message_end", async (event, ctx) => {
|
|
94
|
+
if (event.message.role !== "assistant") return
|
|
95
|
+
const normalized = normalizeCommandCodeMessage(event.message, ctx.model?.provider)
|
|
96
|
+
return normalized ? { message: normalized.message } : undefined
|
|
118
97
|
})
|
|
98
|
+
|
|
99
|
+
const runtime = createCommandCodeRuntime<ProviderConfig, ExtensionCommandContext>(pi, {
|
|
100
|
+
endpoint: modelsUrl,
|
|
101
|
+
cachePath: modelsCachePath,
|
|
102
|
+
loadModels: () =>
|
|
103
|
+
loadCommandCodeModels({
|
|
104
|
+
url: modelsUrl,
|
|
105
|
+
cachePath: modelsCachePath,
|
|
106
|
+
timeoutMs: modelsTimeoutMs,
|
|
107
|
+
}),
|
|
108
|
+
createProviderConfig: (models) => createProviderConfig(models, apiBase, streamCommandCode),
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await runtime.initialize()
|
|
119
112
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-commandcode-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "pi custom provider for Command Code API (commandcode.ai)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"index.ts",
|
|
23
23
|
"src/",
|
|
24
|
+
"scripts/",
|
|
24
25
|
"README.md",
|
|
25
26
|
"CHANGELOG.md",
|
|
26
27
|
"CONTRIBUTING.md",
|
|
@@ -28,19 +29,26 @@
|
|
|
28
29
|
"LICENSE"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
31
|
-
"test": "npm run typecheck && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-stream.ts && tsx tests/test-retry.ts && node tests/test-pi-local.mjs && node tests/test-omp-compat.mjs",
|
|
32
|
+
"test": "npm run typecheck && tsx tests/test-package-manifest.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-retry.ts && node tests/test-pi-isolated.mjs && node tests/test-pi-authenticated.mjs && node tests/test-pi-local.mjs && node tests/test-omp-compat.mjs",
|
|
32
33
|
"typecheck": "tsc --noEmit",
|
|
33
34
|
"format:check": "prettier --check '**/*.{ts,mjs,json,md}'",
|
|
34
35
|
"format": "prettier --write '**/*.{ts,mjs,json,md}'",
|
|
36
|
+
"pi:isolated": "node scripts/pi-isolated.mjs",
|
|
37
|
+
"pi:authenticated": "node scripts/pi-authenticated.mjs",
|
|
35
38
|
"test:unit": "tsx tests/test-pure-functions.ts",
|
|
36
39
|
"test:models": "tsx tests/test-models.ts",
|
|
40
|
+
"test:runtime": "tsx tests/test-runtime.ts",
|
|
37
41
|
"test:pricing": "tsx tests/test-pricing.ts",
|
|
38
42
|
"test:oauth": "tsx tests/test-oauth.ts",
|
|
39
43
|
"test:abort": "tsx tests/test-abort.ts",
|
|
44
|
+
"test:overflow": "tsx tests/test-overflow.ts",
|
|
40
45
|
"test:stream": "tsx tests/test-stream.ts",
|
|
41
46
|
"test:retry": "tsx tests/test-retry.ts",
|
|
47
|
+
"test:pi-isolated": "node tests/test-pi-isolated.mjs",
|
|
48
|
+
"test:pi-authenticated": "node tests/test-pi-authenticated.mjs",
|
|
42
49
|
"test:pi-local": "node tests/test-pi-local.mjs",
|
|
43
50
|
"test:smoke": "node tests/test-smoke.mjs",
|
|
51
|
+
"test:e2e:live": "node tests/test-live-e2e.mjs",
|
|
44
52
|
"test:cost": "tsx tests/test-cost.ts"
|
|
45
53
|
},
|
|
46
54
|
"pi": {
|
|
@@ -54,13 +62,14 @@
|
|
|
54
62
|
"tsx": "4.22.4",
|
|
55
63
|
"typescript": "6.0.3"
|
|
56
64
|
},
|
|
57
|
-
"dependencies": {
|
|
58
|
-
"@earendil-works/pi-ai": "0.75.5"
|
|
59
|
-
},
|
|
60
65
|
"peerDependencies": {
|
|
61
|
-
"@earendil-works/pi-
|
|
66
|
+
"@earendil-works/pi-ai": "*",
|
|
67
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
62
68
|
},
|
|
63
69
|
"peerDependenciesMeta": {
|
|
70
|
+
"@earendil-works/pi-ai": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
64
73
|
"@earendil-works/pi-coding-agent": {
|
|
65
74
|
"optional": true
|
|
66
75
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "node:child_process"
|
|
4
|
+
import { dirname, resolve } from "node:path"
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
|
|
7
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..")
|
|
8
|
+
const extensionPath = resolve(repoRoot, "index.ts")
|
|
9
|
+
|
|
10
|
+
const env = {
|
|
11
|
+
...process.env,
|
|
12
|
+
PI_SKIP_VERSION_CHECK: "1",
|
|
13
|
+
}
|
|
14
|
+
delete env.COMMANDCODE_API_KEY
|
|
15
|
+
|
|
16
|
+
const child = spawn(
|
|
17
|
+
"pi",
|
|
18
|
+
[
|
|
19
|
+
"--no-extensions",
|
|
20
|
+
"--extension",
|
|
21
|
+
extensionPath,
|
|
22
|
+
"--provider",
|
|
23
|
+
"commandcode",
|
|
24
|
+
"--model",
|
|
25
|
+
"gpt-5.6-luna",
|
|
26
|
+
"--models",
|
|
27
|
+
"commandcode/*",
|
|
28
|
+
...process.argv.slice(2),
|
|
29
|
+
],
|
|
30
|
+
{
|
|
31
|
+
cwd: repoRoot,
|
|
32
|
+
env,
|
|
33
|
+
stdio: "inherit",
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
38
|
+
process.on(signal, () => child.kill(signal))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
child.once("error", (error) => {
|
|
42
|
+
console.error(`Could not start pi: ${error.message}`)
|
|
43
|
+
process.exitCode = 1
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
child.once("exit", (status, signal) => {
|
|
47
|
+
if (signal) process.kill(process.pid, signal)
|
|
48
|
+
process.exitCode = status ?? 1
|
|
49
|
+
})
|