pi-opencode-cli-bridge 0.0.0-stage → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 luongnv89
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,155 @@
1
- # Temporary Holding Version
1
+ # pi-opencode-cli-bridge
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Use the local [OpenCode CLI](https://opencode.ai) as a model backend for [Pi Coding Agent](https://pi.dev).
4
+
5
+ This is an unofficial bridge, not an OpenCode or Pi product. It registers the `opencode-cli` provider and sends model requests through `opencode run`.
6
+
7
+ ## Why this fork exists
8
+
9
+ The bridge started as `opencode-pi`. Rodrigo Jager's fork then kept OpenCode's native tool declarations on each request, which OpenCode's free tier expects, and blocked their execution with a plugin. On Pi 1.2 that version stopped working: Pi now carries the system prompt and tool list inside the transcript, and every request crashed with `message.content.map is not a function`.
10
+
11
+ This fork fixes that and closes the gaps found while reviewing the code:
12
+
13
+ - **Works with Pi 1.2.** The current system prompt, prompt sections and tool changes are read from the transcript, so tool calls work again.
14
+ - **Native OpenCode tools cannot run, even if the safety plugin fails.** A test with the real CLI showed that the plugin alone failed open: if it was missing or crashed at startup, OpenCode wrote files. The bridge's agent now also requires permission for every native tool, which `opencode run` refuses automatically.
15
+ - **No hung turns or orphaned processes.** Timeouts and cancellation kill the OpenCode process group, even when it ignores `SIGTERM`.
16
+ - **Unexpected CLI output no longer crashes Pi.** Malformed events end the turn with an error.
17
+ - **Clean shutdown.** Pi waits for temporary directories and OpenCode session records to be removed.
18
+ - **Standard Pi provider hooks.** `onPayload`, `onResponse` and `onProviderStreamEvent` work as with built-in providers.
19
+ - **Its own npm package**, with the original authors credited.
20
+
21
+ ## Credits and fork history
22
+
23
+ This package builds on other people's work:
24
+
25
+ - **[luongnv89 and the pi-extensions contributors](https://github.com/luongnv89/pi-extensions/tree/main/extensions/opencode-pi)** created the original `opencode-pi` extension.
26
+ - **[Rodrigo Manuel Jager Pinheiro](https://github.com/rodrigojager/opencode-pi)** extracted the extension and added the native-tool execution blocker for compatibility with OpenCode's free-tier request validation, plus Windows executable resolution.
27
+ - **[Gybra](https://github.com/Gybra/pi-opencode-cli-bridge)** maintains this fork, adds Pi 1.2 transcript compatibility, process and protocol hardening, provider hooks, a second native-tool guard with real-CLI integration tests, and packages it under a distinct name.
28
+
29
+ The original MIT copyright notice is preserved in [LICENSE](LICENSE). The new package name does not change that attribution.
30
+
31
+ ## Requirements
32
+
33
+ - Node.js **22.19.0 or newer**.
34
+ - Pi Coding Agent **1.2.0 or newer**; the development SDK is pinned to 1.2.0.
35
+ - OpenCode installed on the same machine and available on `PATH`, or selected with `OPENCODE_PI_BIN`. Tested with OpenCode 1.19.0; other versions, including 2.x, are unverified.
36
+
37
+ Check OpenCode before starting Pi:
38
+
39
+ ```sh
40
+ opencode --version
41
+ opencode models opencode --verbose
42
+ ```
43
+
44
+ ## Install
45
+
46
+ Install from npm with Pi's package manager:
47
+
48
+ ```sh
49
+ pi install npm:pi-opencode-cli-bridge
50
+ ```
51
+
52
+ Or track the GitHub repository directly:
53
+
54
+ ```sh
55
+ pi install git:github.com/Gybra/pi-opencode-cli-bridge
56
+ ```
57
+
58
+ Then restart Pi or run `/reload`. For the first switch from another bridge, restarting avoids retaining an old provider in memory.
59
+
60
+ If you already installed another `opencode-pi` source, use `pi list` and remove that source first. For example:
61
+
62
+ ```sh
63
+ pi uninstall npm:opencode-pi
64
+ # Or, if this is the source shown by pi list:
65
+ pi uninstall git:github.com/rodrigojager/opencode-pi
66
+ ```
67
+
68
+ The npm packages `opencode-pi`, `pi-opencode` and `pi-opencode-bridge` are different packages.
69
+
70
+ Manage the installation with `pi list`, `pi update <source>` and `pi uninstall <source>`, using the source you installed (`npm:pi-opencode-cli-bridge` or `git:github.com/Gybra/pi-opencode-cli-bridge`).
71
+
72
+ ## Usage
73
+
74
+ Choose an OpenCode model from `/model`, or start Pi directly:
75
+
76
+ ```sh
77
+ pi --provider opencode-cli --model opencode/deepseek-v4.1-flash-free --thinking off
78
+ ```
79
+
80
+ For an isolated text smoke test:
81
+
82
+ ```sh
83
+ pi -p --no-session --provider opencode-cli --model opencode/deepseek-v4.1-flash-free --thinking off "Reply with exactly OK"
84
+ ```
85
+
86
+ Model availability changes. Refresh the list with `/opencode-pi update` if the example model is no longer listed. Bundled free models do not require an OpenCode login; availability and server-side acceptance are controlled by OpenCode.
87
+
88
+ The provider ID `opencode-cli` and the command `/opencode-pi` retain their upstream names for compatibility:
89
+
90
+ ```text
91
+ /opencode-pi status
92
+ /opencode-pi models
93
+ /opencode-pi test
94
+ /opencode-pi update
95
+ /opencode-pi help
96
+ ```
97
+
98
+ The `test` command prints diagnostic commands; it does not send a model request itself.
99
+
100
+ ## Configuration
101
+
102
+ | Environment variable | Purpose |
103
+ | --- | --- |
104
+ | `OPENCODE_PI_BIN` | Path to the OpenCode executable. Defaults to `opencode`. |
105
+ | `OPENCODE_PI_MODELS` | Comma- or space-separated model IDs. IDs without a provider prefix receive `opencode/`. |
106
+
107
+ An explicit model list skips startup discovery and starts with conservative fallback capabilities. Run `/opencode-pi update` to discover image input, reasoning variants, token limits and costs.
108
+
109
+ Without an override, discovery selects active models whose reported input and output costs are zero. When cost metadata is missing, selection falls back to the upstream free-model naming rules. Review costs before explicitly selecting a paid model.
110
+
111
+ ## How it works
112
+
113
+ 1. The extension discovers models through the local CLI.
114
+ 2. It replays Pi's transcript system messages to recover the current system prompt and tool declarations, including later changes.
115
+ 3. It creates a temporary OpenCode project containing a `pi-model` agent and a `tool.execute.before` plugin. Native tool declarations remain on the request. Execution is stopped twice: the plugin throws before any native tool runs, and the agent sets every permission to `ask`, which non-interactive `opencode run` rejects automatically. The second layer still holds if the plugin is missing or fails to load.
116
+ 4. It sends the prompt over standard input to `opencode run --format json`. User and tool-result images are written to temporary files and attached only when the model supports images.
117
+ 5. Text markers such as `<pi_tool_call>{"name":"read","arguments":{"path":"example.txt"}}</pi_tool_call>` become Pi tool calls. Tool names must be in the current Pi tool list; execution then follows Pi's normal validation and permission pipeline.
118
+ 6. Successful continuation turns reuse an OpenCode session and send only the new transcript. Changes to the model, prompt, tools or transcript prefix start a fresh session.
119
+
120
+ Pi's provider instrumentation hooks follow the CLI transport, not OpenCode's internal HTTP traffic:
121
+
122
+ - `onPayload` receives `{ prompt }`, the exact text written to the CLI's standard input. Returning `{ prompt: "..." }` replaces it; any other non-`undefined` value fails the turn. A turn with a payload hook always starts a fresh OpenCode session.
123
+ - `onResponse` is called once the subprocess has started, with `status: 0` (no HTTP status exists) and the header `x-pi-transport: opencode-cli`.
124
+ - `onProviderStreamEvent` receives each validated `--format json` event in order, and is awaited before the event is processed.
125
+ - Provider-scoped `env` values are passed to the subprocess. The bridge's own configuration directory and update-check settings always take precedence.
126
+
127
+ Reasoning variants, image support and token limits follow CLI discovery. Token usage is taken from CLI events when available, otherwise estimated. Session records and temporary project directories are cleaned up on a best-effort basis.
128
+
129
+ ## Security, privacy and limits
130
+
131
+ - Prompts, system instructions, tool declarations, conversation text and attached images pass to OpenCode and its model provider. Do not send confidential data unless that provider's policies are suitable.
132
+ - The extension does not directly extract credentials or call model HTTP endpoints. The OpenCode subprocess still runs as your user and can use its own configuration, credentials and inherited environment.
133
+ - The native-tool guard is a guard rail, **not a sandbox**. An integration test runs the real OpenCode 1.19.0 CLI against a local model fixture that requests native `write` and `bash` calls, and checks that neither runs with the plugin intact, missing, or failing at startup. Other OpenCode versions, tools contributed by your own global OpenCode configuration (for example MCP servers), and a hostile local process are not covered.
134
+ - Pi and OpenCode can both retain local conversation data. Best-effort cleanup is not a guarantee of secure deletion.
135
+ - Free-tier model availability and accepted request shapes can change. This fork makes no guarantee of continued access or compliance with a hosted provider's terms; review the applicable terms yourself.
136
+ - Live tests with DeepSeek v4.1 Flash Free used thinking off. The first high-thinking test timed out; no default thinking setting is changed by this package.
137
+ - Timeouts and cancellation escalate from `SIGTERM` to `SIGKILL` on the CLI's process group after 250 ms. Malformed or unexpected CLI events end the turn with an error instead of crashing Pi. Shutdown waits for session cleanup.
138
+
139
+ See [SECURITY.md](SECURITY.md) for the trust boundaries and [docs/code-review.md](https://github.com/Gybra/pi-opencode-cli-bridge/blob/main/docs/code-review.md) for what was fixed and what remains. This is an experimental, unaudited fork.
140
+
141
+ ## Development
142
+
143
+ ```sh
144
+ npm ci --ignore-scripts
145
+ npm run typecheck
146
+ npm test
147
+ OPENCODE_PI_INTEGRATION_BIN="$(which opencode)" npm run test:integration
148
+ npm pack --dry-run
149
+ ```
150
+
151
+ `npm test` uses a fake OpenCode executable. `npm run test:integration` needs the real OpenCode CLI at exactly version 1.19.0 (on `PATH` or in `OPENCODE_PI_INTEGRATION_BIN`), runs it with isolated home and data directories against a local model fixture, and makes no network request. Neither suite needs API credentials. Development SDKs are pinned; Pi supplies those packages at runtime. The npm archive includes the extension source, README, security policy and license, not tests or installed dependencies.
152
+
153
+ ## License
154
+
155
+ MIT. See [LICENSE](LICENSE).
package/SECURITY.md ADDED
@@ -0,0 +1,18 @@
1
+ # Security policy
2
+
3
+ ## Reporting a problem
4
+
5
+ Never include prompts, conversation history, images, API keys, auth files or session databases in a public report. Use a minimal, sanitized reproduction. For sensitive findings, contact the repository maintainer privately before disclosing details.
6
+
7
+ ## Trust boundaries
8
+
9
+ - This extension and the OpenCode executable run with your user's operating-system permissions. Neither is a sandbox.
10
+ - Requests are delegated to the real OpenCode CLI. The extension does not directly read credentials, but the subprocess inherits the environment and uses OpenCode's own configuration and authentication.
11
+ - System instructions, tool declarations, transcript text and image attachments can reach the selected model provider. Provider retention and training policies apply.
12
+ - The current Pi tool list is checked before marker responses become Pi tool calls. Pi remains responsible for argument validation and tool permission checks.
13
+ - Native OpenCode tools are blocked twice: a generated `tool.execute.before` plugin, and an agent permission of `ask` for everything, which non-interactive `opencode run` rejects. Native declarations remain visible to the model. Both layers depend on OpenCode 1.19.0 behavior, verified by an integration test with the real CLI. They are not a security boundary against a hostile local process, your own OpenCode configuration (it is loaded and may add tools or agent overrides), or other OpenCode versions.
14
+ - Temporary project directories and OpenCode session records are removed best-effort. Pi's own conversation history is not deleted by the extension.
15
+
16
+ ## Known gaps
17
+
18
+ The [code review](https://github.com/Gybra/pi-opencode-cli-bridge/blob/main/docs/code-review.md) records the findings fixed in 0.1.0 and the residual risks. Only the latest release is maintained. There is no independently audited security guarantee.
package/package.json CHANGED
@@ -1,6 +1,66 @@
1
1
  {
2
2
  "name": "pi-opencode-cli-bridge",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Bridge the OpenCode CLI into Pi Coding Agent",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "files": [
8
+ "src/index.ts",
9
+ "src/process.ts",
10
+ "README.md",
11
+ "LICENSE",
12
+ "SECURITY.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "npm run build && node --test --test-timeout=30000 dist/index.test.js dist/process.test.js",
18
+ "test:integration": "npm run build && node --test --test-timeout=120000 dist/native-tools.integration.test.js",
19
+ "prepublishOnly": "npm run typecheck && npm test && npm run test:integration"
20
+ },
21
+ "pi": {
22
+ "extensions": [
23
+ "./src/index.ts"
24
+ ],
25
+ "image": "https://raw.githubusercontent.com/luongnv89/pi-extensions/main/assets/pi-opencode-cli-model-list.png"
26
+ },
27
+ "keywords": [
28
+ "pi-package",
29
+ "pi",
30
+ "opencode",
31
+ "bridge",
32
+ "free-models",
33
+ "custom-provider"
34
+ ],
35
+ "author": "pi-extensions contributors",
36
+ "contributors": [
37
+ { "name": "luongnv89", "url": "https://github.com/luongnv89" },
38
+ { "name": "Rodrigo Manuel Jager Pinheiro", "url": "https://github.com/rodrigojager" },
39
+ { "name": "Gybra", "url": "https://github.com/Gybra" }
40
+ ],
41
+ "license": "MIT",
42
+ "engines": {
43
+ "node": ">=22.19.0"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/Gybra/pi-opencode-cli-bridge.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/Gybra/pi-opencode-cli-bridge/issues"
51
+ },
52
+ "homepage": "https://github.com/Gybra/pi-opencode-cli-bridge",
53
+ "peerDependencies": {
54
+ "@earendil-works/pi-coding-agent": "*",
55
+ "@earendil-works/pi-ai": "*"
56
+ },
57
+ "devDependencies": {
58
+ "@earendil-works/pi-coding-agent": "1.2.0",
59
+ "@earendil-works/pi-ai": "1.2.0",
60
+ "@types/node": "22.20.4",
61
+ "typescript": "5.9.3"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }