opencode-ext-connector 0.3.2 → 0.4.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/CHANGELOG.md +36 -0
- package/README.md +160 -40
- package/dist/core/options.d.ts +4 -0
- package/dist/core/options.js +35 -6
- package/dist/opencode/host-options.d.ts +8 -1
- package/dist/opencode/host-options.js +24 -7
- package/dist/opencode/language-factory.d.ts +1 -1
- package/dist/opencode/language-factory.js +3 -0
- package/dist/opencode/ollama-probe.d.ts +2 -1
- package/dist/opencode/ollama-probe.js +2 -2
- package/dist/opencode/ollama-production.d.ts +7 -0
- package/dist/opencode/ollama-production.js +19 -7
- package/dist/opencode/provider-entry.d.ts +1 -0
- package/dist/opencode/providers.d.ts +2 -0
- package/dist/opencode/providers.js +12 -5
- package/dist/opencode/v1-catalog.js +6 -1
- package/dist/opencode/v1-language.js +5 -2
- package/dist/opencode/v1-session-auth.d.ts +2 -1
- package/dist/opencode/v1-session-auth.js +7 -6
- package/dist/providers/command-code/language-model.js +46 -39
- package/dist/providers/command-code/open-generation.d.ts +16 -0
- package/dist/providers/command-code/open-generation.js +45 -0
- package/dist/providers/cursor/credential-retry.d.ts +14 -0
- package/dist/providers/cursor/credential-retry.js +26 -0
- package/dist/providers/cursor/direct-run-failure.d.ts +8 -0
- package/dist/providers/cursor/direct-run-failure.js +26 -0
- package/dist/providers/cursor/direct-run-types.d.ts +1 -0
- package/dist/providers/cursor/direct-run.js +40 -27
- package/dist/providers/cursor/direct-runtime.js +26 -39
- package/dist/providers/cursor/run-session.js +1 -1
- package/dist/providers/ollama/adapter.d.ts +2 -0
- package/dist/providers/ollama/adapter.js +3 -1
- package/dist/providers/ollama/endpoints.d.ts +8 -0
- package/dist/providers/ollama/endpoints.js +38 -0
- package/dist/providers/ollama/index.d.ts +1 -0
- package/dist/providers/ollama/index.js +1 -0
- package/dist/providers/ollama/language-model.d.ts +2 -0
- package/dist/providers/ollama/language-model.js +2 -0
- package/dist/providers/ollama/local-catalog.d.ts +2 -1
- package/dist/providers/ollama/local-catalog.js +3 -3
- package/dist/providers/ollama/runtime.d.ts +2 -0
- package/dist/providers/ollama/runtime.js +5 -5
- package/dist/sdk/ollama.d.ts +4 -1
- package/dist/server.js +33 -7
- package/docs/README.ko.md +160 -40
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 - 2026-09-11
|
|
4
|
+
|
|
5
|
+
- Add explicit `ollamaBaseURL` support for trusted remote and self-hosted Ollama
|
|
6
|
+
daemons across plugin options, `/connect`, provider projection, runtime, and
|
|
7
|
+
the `opencode-ext-connector/ollama` SDK entry
|
|
8
|
+
- Preserve daemon path prefixes, isolate process state by normalized base URL,
|
|
9
|
+
and reject Cloud hosts, credentials, redirects, and ambiguous URL forms
|
|
10
|
+
- Add the preferred connector-wide `credentialManagement: "connector" |
|
|
11
|
+
"external"` authority policy, capability-gated by each provider adapter
|
|
12
|
+
- Map Claude `"connector"` to automatic refresh with a `60_000` ms lead and
|
|
13
|
+
writeback, and `"external"` to never-refresh/no-write with credential re-read
|
|
14
|
+
after 401; omitting all credential-policy options preserves automatic refresh
|
|
15
|
+
with a `60_000` ms lead and no writeback, while legacy options still control
|
|
16
|
+
behavior when supplied without `credentialManagement`
|
|
17
|
+
- Keep Cursor direct generation and Command Code read-only under both modes:
|
|
18
|
+
on an exact HTTP 401 before output or effects, re-read only a changed,
|
|
19
|
+
non-null credential and retry once; this is neither refresh nor writeback.
|
|
20
|
+
Cursor legacy/compatibility generation remains one-shot; leave Ollama
|
|
21
|
+
unaffected
|
|
22
|
+
- Reject combining `credentialManagement` with `credentialRefresh` or
|
|
23
|
+
`writeBackCredentials`; accept those legacy options alone as deprecated for
|
|
24
|
+
one migration cycle, with custom lead times still using the legacy config
|
|
25
|
+
|
|
26
|
+
## 0.3.3 - 2026-09-06
|
|
27
|
+
|
|
28
|
+
- Document official npm package installation through OpenCode's `plugin` field,
|
|
29
|
+
including pinned installs, deterministic updates, and removal guidance
|
|
30
|
+
- Maintain active branch history by removing obsolete commit attribution
|
|
31
|
+
trailers while preserving source trees; existing published tags and
|
|
32
|
+
provenance remain unchanged
|
|
33
|
+
- Exercise package loading through the exact npm package spec from an isolated
|
|
34
|
+
offline OpenCode cache with no registry access
|
|
35
|
+
- Harden npm publishing with immutable action and toolchain pins, a no-OIDC
|
|
36
|
+
verification job, and a checksummed tarball-only OIDC publish job
|
|
37
|
+
- Keep provider and runtime behavior unchanged
|
|
38
|
+
|
|
3
39
|
## 0.3.2 - 2026-09-06
|
|
4
40
|
|
|
5
41
|
- Remove the obsolete npm token fallback so releases publish only through
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# OpenCode External Provider Connector
|
|
4
4
|
|
|
5
|
-
**One OpenCode plugin configuration for Claude, Cursor, Command Code, and Ollama — using existing vendor sessions and
|
|
5
|
+
**One OpenCode plugin configuration for Claude, Cursor, Command Code, and Ollama — using existing vendor sessions and a trusted Ollama daemon. No new OAuth.**
|
|
6
6
|
|
|
7
7
|
<p>
|
|
8
8
|
<img src="https://img.shields.io/badge/Bun-%3E%3D1.3.14-000000?style=for-the-badge" alt="Bun >=1.3.14" />
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
|
|
14
14
|
**English** · [한국어](docs/README.ko.md) · [Documentation](#documentation)
|
|
15
15
|
|
|
16
|
-
[Status](#status) · [Requirements](#requirements) · [Quick Install](#quick-install) · [Configuration](#configuration) · [First-Time Connection](#first-time-connection) · [Providers](#providers) · [Troubleshooting](#troubleshooting) · [Documentation](#documentation) · [Testing](#testing) · [License and Disclaimer](#license-and-disclaimer)
|
|
16
|
+
[Status](#status) · [Requirements](#requirements) · [Quick Install](#quick-install) · [Configuration](#configuration) · [Host/Guest Sandbox Setup](#hostguest-sandbox-setup) · [Update and Remove](#update-and-remove) · [First-Time Connection](#first-time-connection) · [Providers](#providers) · [Troubleshooting](#troubleshooting) · [Documentation](#documentation) · [Testing](#testing) · [License and Disclaimer](#license-and-disclaimer)
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
20
|
## Status
|
|
21
21
|
|
|
22
|
-
> Independent unofficial community plugin, version **0.
|
|
22
|
+
> Independent unofficial community plugin, version **0.4.0**. Package E2E tests exercise the legacy multi-function loader with the OpenCode CLI installed in CI. `@opencode-ai/plugin@1.18.18` is the compile-time plugin API target, not a runtime pin. Source is BSD-3-Clause. This project is not affiliated with, endorsed by, sponsored by, or authorized by OpenCode or any provider. Full terms are in [License and Disclaimer](#license-and-disclaimer).
|
|
23
23
|
|
|
24
|
-
Reuse the Claude, Cursor, Command Code, and Ollama sessions you already have. One `opencode.json` plugin entry publishes live catalogs into OpenCode. Claude and Cursor stay disconnected until OpenCode has a marker or OAuth record and the vendor session is present. Command Code may use an OpenCode-stored direct API key or an existing CLI session/key. Ollama requires the exact session marker plus a responsive
|
|
24
|
+
Reuse the Claude, Cursor, Command Code, and Ollama sessions you already have. One `opencode.json` plugin entry publishes live catalogs into OpenCode. Claude and Cursor stay disconnected until OpenCode has a marker or OAuth record and the vendor session is present. Command Code may use an OpenCode-stored direct API key or an existing CLI session/key. Ollama requires the exact session marker plus a responsive trusted daemon.
|
|
25
25
|
|
|
26
26
|
## Requirements
|
|
27
27
|
|
|
@@ -33,119 +33,239 @@ Reuse the Claude, Cursor, Command Code, and Ollama sessions you already have. On
|
|
|
33
33
|
| Claude | Existing Claude Code credentials (`~/.claude/.credentials.json` and/or macOS Keychain). The `claude` binary is optional. |
|
|
34
34
|
| Cursor | Existing Cursor CLI login (`~/.config/cursor/auth.json` or `CURSOR_ACCESS_TOKEN`) |
|
|
35
35
|
| Command Code | Existing API key (`COMMAND_CODE_API_KEY` or `~/.commandcode/auth.json`). The `command-code` binary is optional. |
|
|
36
|
-
| Ollama |
|
|
36
|
+
| Ollama | A trusted daemon; defaults to `http://localhost:11434`, with explicit remote/self-hosted URLs supported; run `ollama signin` separately for Cloud |
|
|
37
37
|
|
|
38
38
|
No vendor CLI has to be installed where OpenCode runs. Claude and Command Code requests carry a client version: the connector takes `ANTHROPIC_CLI_VERSION` / `COMMAND_CODE_CLI_VERSION` when set, otherwise an installed `claude` / `command-code` binary, otherwise the latest version published on the npm registry (`@anthropic-ai/claude-code`, `command-code`). Nothing is pinned in the package.
|
|
39
39
|
|
|
40
40
|
## Quick Install
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Choose either the global `~/.config/opencode/opencode.json` or project-level `opencode.json`, then add the package to the official singular `plugin` field:
|
|
43
43
|
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
```jsonc
|
|
45
|
+
{
|
|
46
|
+
"$schema": "https://opencode.ai/config.json",
|
|
47
|
+
"plugin": [
|
|
48
|
+
"opencode-ext-connector"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
47
51
|
```
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
OpenCode installs configured npm plugins with Bun at startup and caches them. For a reproducible installation, use an exact published version instead:
|
|
50
54
|
|
|
51
55
|
```jsonc
|
|
52
56
|
{
|
|
53
57
|
"$schema": "https://opencode.ai/config.json",
|
|
54
|
-
"plugin": [
|
|
55
|
-
"file:///absolute/path/to/opencode-ext-connector/dist/index.js"
|
|
56
|
-
]
|
|
58
|
+
"plugin": ["opencode-ext-connector@0.4.0"]
|
|
57
59
|
}
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
Fully quit and restart OpenCode after adding or changing the entry; a reload is not enough.
|
|
61
63
|
|
|
62
64
|
The one package entry exposes the catalog plugin plus the Claude, Cursor, Command Code, and Ollama auth hooks. Provider ids: `claude`, `cursor`, `command-code`, `ollama`. Model ids come from each provider's live catalog, with documented fallbacks `default` (Cursor) and `Qwen/Qwen3.8-Max` (Command Code) when a live list is empty.
|
|
63
65
|
|
|
64
66
|
## Configuration
|
|
65
67
|
|
|
66
|
-
OpenCode passes plugin options as the second item of a two-element tuple.
|
|
68
|
+
OpenCode passes plugin options as the second item of a two-element tuple. Use the npm package name as the first item.
|
|
67
69
|
|
|
68
70
|
Omitted `providers` enables all four. An explicit list is a strict allow-list. Explicit `[]` disables all.
|
|
69
71
|
|
|
70
72
|
| Option | Default | Meaning |
|
|
71
73
|
| --- | --- | --- |
|
|
72
74
|
| `providers` | all four | Provider ids to register: `claude`, `cursor`, `command-code`, `ollama`; explicit `[]` disables all |
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
75
|
+
| `ollamaBaseURL` | `"http://localhost:11434"` | Absolute `http` or `https` base for the trusted Ollama daemon; path prefixes are preserved |
|
|
76
|
+
| `credentialManagement` | omitted | Preferred authority policy: `"connector"` authorizes refresh and writeback where an adapter supports both; `"external"` prohibits connector refresh and writeback |
|
|
77
|
+
| `writeBackCredentials` | `false` | **Deprecated:** accepted alone for one migration cycle; controls Claude writeback after refresh |
|
|
78
|
+
| `credentialRefresh.mode` | `"auto"` | **Deprecated:** accepted alone for one migration cycle; controls Claude `"auto"` or `"never"` refresh behavior |
|
|
79
|
+
| `credentialRefresh.leadMs` | `60000` | **Deprecated:** accepted alone for one migration cycle; custom lead times still require this legacy configuration |
|
|
76
80
|
| `catalogReloadMs` | `300000` | Re-run catalog snapshots on this interval; `0` disables |
|
|
77
81
|
| `snapshotTimeoutMs` | `30000` | Per-provider snapshot deadline |
|
|
78
82
|
| `health.initialBackoffMs` | `1000` | Health backoff after a failed snapshot |
|
|
79
83
|
| `health.maximumBackoffMs` | `60000` | Health backoff cap |
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
`credentialManagement: "connector"` authorizes the connector to refresh and write back credentials where a provider adapter supports both. Today only Claude has that capability, mapping to automatic refresh with a `60_000` ms lead and writeback enabled:
|
|
82
86
|
|
|
83
87
|
```jsonc
|
|
84
88
|
{
|
|
85
89
|
"$schema": "https://opencode.ai/config.json",
|
|
86
90
|
"plugin": [
|
|
87
91
|
[
|
|
88
|
-
"
|
|
92
|
+
"opencode-ext-connector",
|
|
89
93
|
{
|
|
90
|
-
"
|
|
94
|
+
"credentialManagement": "connector"
|
|
91
95
|
}
|
|
92
96
|
]
|
|
93
97
|
]
|
|
94
98
|
}
|
|
95
99
|
```
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
Use external authority when another process manages credentials:
|
|
102
|
+
|
|
103
|
+
```jsonc
|
|
104
|
+
{
|
|
105
|
+
"$schema": "https://opencode.ai/config.json",
|
|
106
|
+
"plugin": [
|
|
107
|
+
[
|
|
108
|
+
"opencode-ext-connector",
|
|
109
|
+
{
|
|
110
|
+
"credentialManagement": "external"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`credentialManagement: "external"` prohibits connector refresh and writeback. Today Claude maps to never-refresh/no-write and can re-read externally managed credentials after a 401. When all credential-policy options are omitted, Claude preserves the legacy defaults: automatic refresh with a `60_000` ms lead and no writeback. If only `credentialManagement` is omitted, supplied deprecated `credentialRefresh` or `writeBackCredentials` options still control behavior. Cursor direct generation and Command Code remain read-only under both modes: on an exact pre-output HTTP 401, they re-read a changed, non-null credential and retry once only. This safe reread is not refresh or writeback, so both modes allow it; Cursor legacy/compatibility generation remains one-shot. Ollama is unaffected. This option does not log in, mint OAuth, synchronize machines, or imply that credentials use file storage.
|
|
118
|
+
|
|
119
|
+
For one migration cycle, `writeBackCredentials` and `credentialRefresh.*` remain accepted when used without `credentialManagement`; custom `credentialRefresh.leadMs` values still require the legacy configuration. Mixing the new option with either legacy option is rejected with: `` `credentialManagement` cannot be combined with deprecated `credentialRefresh` or `writeBackCredentials` ``.
|
|
120
|
+
|
|
121
|
+
Enabled providers remain disconnected until their provider-specific auth rule is met: Claude and Cursor need an OpenCode marker or OAuth record plus the vendor session; Command Code may use an OpenCode-stored direct API key or an existing CLI session/key; Ollama needs the exact session marker plus a responsive configured daemon.
|
|
122
|
+
|
|
123
|
+
To use an explicitly selected remote or self-hosted daemon, set the same flat option on the package entry:
|
|
124
|
+
|
|
125
|
+
```jsonc
|
|
126
|
+
{
|
|
127
|
+
"plugin": [["opencode-ext-connector", { "ollamaBaseURL": "https://ollama.example.test/team" }]]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The connector appends `/api/tags`, `/api/pull`, and `/api/chat` to that base. It rejects credentials, query strings, fragments, direct API-route bases, and `ollama.com` hosts. It never reads `OLLAMA_HOST`, sends cookies or authorization headers, follows redirects, configures custom CAs, or disables TLS verification. Ollama has no built-in daemon authentication, so use only a daemon and network path you trust; terminate HTTPS and enforce access policy outside this connector. Distinct normalized bases have isolated catalog leases and pull flights, while equivalent normalized bases share process-local state.
|
|
98
132
|
|
|
99
133
|
The connector always performs one initial catalog refresh. `snapshotTimeoutMs` applies to each provider snapshot, while `catalogReloadMs: 0` disables only periodic refresh. Periodic refreshes are fixed-delay and single-flight: the next delay begins after the current refresh settles. Health backoff suppresses repeated failures; transient failures retain the last-known catalog, while an explicit unavailable snapshot removes only connector-owned provider data.
|
|
100
134
|
|
|
101
135
|
OpenCode builds its active provider registry during instance setup. Periodic refresh updates the connector's retained catalog and health state, but new authentication or changed model membership becomes visible after normal OpenCode instance reconstruction. The connector never forces reconstruction or writes generated provider configuration. The `@opencode-ai/plugin@1.18.18` package is the plugin API this connector targets; it is not an OpenCode runtime pin.
|
|
102
136
|
|
|
103
|
-
With
|
|
137
|
+
With all credential-policy options omitted, refreshed Claude tokens stay in memory only. A stored refresh token that rotates can then stop working on the next process start — choose `credentialManagement: "connector"` if the connector should have authority to write updates too. If only `credentialManagement` is omitted, supplied deprecated options still control refresh and writeback.
|
|
104
138
|
|
|
105
139
|
### Sharing one Claude login across machines
|
|
106
140
|
|
|
107
141
|
Anthropic rotates the refresh token on every refresh and invalidates the previous one. Two copies of `~/.claude/.credentials.json` that both refresh will therefore break each other. Copying the file works only if exactly one machine refreshes and every other machine receives the result before its own copy expires:
|
|
108
142
|
|
|
109
|
-
- **
|
|
110
|
-
- **
|
|
111
|
-
-
|
|
143
|
+
- **Refresh authority** (where you log in): `credentialManagement: "connector"`. If you need a custom publication window, use the deprecated legacy options alone for this migration cycle, for example `writeBackCredentials: true` with `credentialRefresh: { mode: "auto", leadMs: 1800000 }`.
|
|
144
|
+
- **External-authority machines**: `credentialManagement: "external"`. They never contact the OAuth endpoint; when a request returns 401 they re-read externally managed credentials and retry once.
|
|
145
|
+
- Synchronize the externally managed credential material from the refresh-authority machine whenever it changes. The option itself does not synchronize machines or require file storage; if you copy `~/.claude/.credentials.json`, OpenCode's own `auth.json` only needs the `anthropic` record once and its other providers should remain untouched.
|
|
112
146
|
|
|
113
147
|
Machines that refresh on their own — including a Claude Code install that is used interactively — must not share the file. Log in separately there.
|
|
114
148
|
|
|
149
|
+
## Host/Guest Sandbox Setup
|
|
150
|
+
|
|
151
|
+
When OpenCode runs in a container, VM, or another sandbox, treat that runtime as the **guest** and the machine that owns the vendor logins and Ollama daemon as the **host**. The guest has its own `localhost`, home directory, environment, keychains, filesystem permissions, and network namespace. Host sessions are not visible unless you mount their files or inject their environment values explicitly.
|
|
152
|
+
|
|
153
|
+
The safest shared-session layout keeps each vendor login owned and refreshed by the host, mounts only the required vendor credential source read-only, and gives the guest a separate writable persistent OpenCode data directory:
|
|
154
|
+
|
|
155
|
+
| Provider | Host | Guest |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| Claude | Own and refresh the Claude Code login | Mount the Claude credential directory read-only, set `CLAUDE_CONFIG_DIR` to that guest path, and use `credentialManagement: "external"`; a host macOS Keychain is not available inside a Linux guest; resolve the client version with `ANTHROPIC_CLI_VERSION`, an installed `claude` binary, or npm registry access |
|
|
158
|
+
| Cursor | Own the Cursor CLI login | Mount the credential file at the guest's `${HOME}/.config/cursor/auth.json` read-only, or inject `CURSOR_ACCESS_TOKEN` through the sandbox's secret mechanism; install Node.js 22 or later in the guest |
|
|
159
|
+
| Command Code | Own the CLI login or API key | Mount `${HOME}/.commandcode/auth.json` read-only, or inject `COMMAND_CODE_API_KEY`; resolve the client version with `COMMAND_CODE_CLI_VERSION`, an installed `command-code` binary, or npm registry access |
|
|
160
|
+
| Ollama | Run the trusted daemon and run `ollama signin` there when Cloud access is needed | Copy no Ollama credential; connect only to the daemon selected by `ollamaBaseURL` |
|
|
161
|
+
|
|
162
|
+
For example, a Linux guest can use these paths and optional secret/version overrides; adapt the mount source and destination to the sandbox runtime:
|
|
163
|
+
|
|
164
|
+
```dotenv
|
|
165
|
+
HOME=/home/sandbox
|
|
166
|
+
XDG_DATA_HOME=/home/sandbox/.local/share
|
|
167
|
+
CLAUDE_CONFIG_DIR=/run/host-credentials/claude
|
|
168
|
+
CURSOR_ACCESS_TOKEN=<optional-sandbox-secret>
|
|
169
|
+
COMMAND_CODE_API_KEY=<optional-sandbox-secret>
|
|
170
|
+
ANTHROPIC_CLI_VERSION=<optional-compatible-version>
|
|
171
|
+
COMMAND_CODE_CLI_VERSION=<optional-compatible-version>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Set a writable persistent `XDG_DATA_HOME` for the guest. `/connect` writes OpenCode auth state to `${XDG_DATA_HOME}/opencode/auth.json`, or on Linux to `${HOME}/.local/share/opencode/auth.json` when `XDG_DATA_HOME` is unset. That file may contain a secret Claude OAuth record or Command Code API key; Cursor and Ollama use non-secret CLI-session markers, and Command Code can use either a marker or a key. Protect it as a secret-bearing file. Do not mount the entire host OpenCode data directory read-only when guest `/connect` or Claude writeback must update it.
|
|
175
|
+
|
|
176
|
+
Use this complete guest `opencode.json` when all four providers are enabled and the host owns the shared credentials:
|
|
177
|
+
|
|
178
|
+
```jsonc
|
|
179
|
+
{
|
|
180
|
+
"$schema": "https://opencode.ai/config.json",
|
|
181
|
+
"plugin": [
|
|
182
|
+
[
|
|
183
|
+
"opencode-ext-connector",
|
|
184
|
+
{
|
|
185
|
+
"providers": ["claude", "cursor", "command-code", "ollama"],
|
|
186
|
+
"ollamaBaseURL": "http://host.docker.internal:11434",
|
|
187
|
+
"credentialManagement": "external",
|
|
188
|
+
"catalogReloadMs": 300000,
|
|
189
|
+
"snapshotTimeoutMs": 30000,
|
|
190
|
+
"health": {
|
|
191
|
+
"initialBackoffMs": 1000,
|
|
192
|
+
"maximumBackoffMs": 60000
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`ollamaBaseURL` is a flat connector option in the package tuple, not an OpenCode provider option. The numeric values above are the connector defaults; `credentialManagement: "external"` and the host daemon URL are deliberate overrides for read-only host-owned credentials. Do not put vendor tokens in `opencode.json`; pass them through read-only mounts or the sandbox's secret injection mechanism.
|
|
201
|
+
|
|
202
|
+
For Docker Desktop, `host.docker.internal` normally resolves to the host. A Linux Docker bridge may also need `--add-host=host.docker.internal:host-gateway` or the Compose equivalent:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
extra_hosts:
|
|
206
|
+
- "host.docker.internal:host-gateway"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Ollama normally listens on host loopback. For bridge networking, the host may need to start it with `OLLAMA_HOST=0.0.0.0:11434`; restrict the exposed port with host firewall and sandbox network policy. `OLLAMA_HOST` configures the host daemon, while `ollamaBaseURL` configures this connector in the guest. Host networking makes guest `localhost` reach the host but reduces isolation and should be an explicit choice. Other sandbox runtimes need an equivalent host route and must allow outbound access to each enabled provider; allow `registry.npmjs.org` only when Claude or Command Code cannot resolve its client version from an environment value or installed binary.
|
|
210
|
+
|
|
211
|
+
Alternatively, the guest can own its vendor logins in persistent guest storage. In that mode, run vendor login flows in the guest instead of mounting host credentials. A guest that is the sole Claude refresh owner may use `credentialManagement: "connector"`. Never let the host and guest independently refresh credentials descended from the same Claude refresh token.
|
|
212
|
+
|
|
213
|
+
## Update and Remove
|
|
214
|
+
|
|
215
|
+
To update deterministically, check the desired published version, replace the `plugin` entry with that exact spec, then fully restart OpenCode:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm view opencode-ext-connector version
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
```jsonc
|
|
222
|
+
{
|
|
223
|
+
"$schema": "https://opencode.ai/config.json",
|
|
224
|
+
"plugin": ["opencode-ext-connector@<version>"]
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Do not rely on an unpinned entry to refresh its cached package automatically on restart. To remove the connector, remove its entry from `plugin` and fully restart OpenCode. Cached package files are inert when the package is not configured.
|
|
229
|
+
|
|
115
230
|
## First-Time Connection
|
|
116
231
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
232
|
+
If OpenCode runs in a sandbox, complete [Host/Guest Sandbox Setup](#hostguest-sandbox-setup), including writable OpenCode auth storage and host networking, before `/connect`.
|
|
233
|
+
|
|
234
|
+
1. **Fully restart OpenCode** after adding the npm package entry or version spec. Quit the process and start it again so the named legacy auth hooks load. A reload or periodic catalog refresh is not instance reconstruction.
|
|
235
|
+
2. **Confirm local prerequisites** for the providers you enabled. Claude and Cursor need their vendor sessions. Command Code needs either an API key you will store in OpenCode or an existing CLI session/key. Ollama needs a daemon you trust at the configured `ollamaBaseURL`; Cloud still requires a separate `ollama signin`.
|
|
236
|
+
3. **Run `/connect`** for each provider you want. Claude and Cursor record a marker or OAuth entry only after the vendor session is available. Command Code can store a direct API key in OpenCode or reuse an existing CLI session/key. Ollama stores the exact session marker only when the configured daemon responds. Models publish only after that provider-specific rule is met.
|
|
120
237
|
4. **Verify the catalogs.** Confirm Claude, Cursor, and Command Code models appear in OpenCode. For Ollama, run `opencode models ollama` and confirm locally pulled models plus Cloud tags discovered unauthenticated, without connector-supplied credentials.
|
|
121
238
|
|
|
122
|
-
Ollama `/connect` probes the
|
|
239
|
+
Ollama `/connect` probes the configured daemon and stores the exact session marker. It does not run `ollama signin` or handle Ollama credentials.
|
|
123
240
|
|
|
124
241
|
## Providers
|
|
125
242
|
|
|
126
243
|
| Provider | What it does |
|
|
127
244
|
| --- | --- |
|
|
128
|
-
| **Claude** | Reuses existing Claude Code credentials. Does not mint OAuth. Compatibility fetch sends CLI-compatible request metadata and streams Anthropic SSE on the built-in `anthropic` path. `
|
|
129
|
-
| **Cursor** | Calls Cursor's unpublished client protocol (`api2.cursor.sh` `AgentService`, Connect+protobuf over HTTP/2) with the CLI access token. A plugin-owned Node child communicates over private stdio, keeps tool results on the same bidi Run, never replays parked calls, opens no user-facing daemon, and never spawns `cursor-agent` for generation. Unofficial; not a public Cursor API. After protocol drift there is no implicit fallback — that provider fails. Requires Node.js 22 or later. Live catalog ids are used when present; otherwise the documented fallback is `default`. |
|
|
130
|
-
| **Command Code** | Calls `/alpha/generate` with CLI-compatible request metadata and streams provider-local NDJSON text and tool events. The client version comes from `COMMAND_CODE_CLI_VERSION`, an installed `command-code` binary, or the npm registry. Request metadata includes Node.js version, platform, architecture, and the absolute working directory. Live catalog ids are used when present; otherwise the documented fallback is `Qwen/Qwen3.8-Max`. |
|
|
131
|
-
| **Ollama** | Uses
|
|
245
|
+
| **Claude** | Reuses existing Claude Code credentials. Does not mint OAuth. Compatibility fetch sends CLI-compatible request metadata and streams Anthropic SSE on the built-in `anthropic` path. `credentialManagement: "connector"` maps to auto-refresh with a `60_000` ms lead and writeback; `"external"` maps to never-refresh/no-write with a credential re-read after 401. Omitting all credential-policy options preserves legacy auto/`60_000` behavior without writeback; if only `credentialManagement` is omitted, supplied deprecated options still control behavior. |
|
|
246
|
+
| **Cursor** | Calls Cursor's unpublished client protocol (`api2.cursor.sh` `AgentService`, Connect+protobuf over HTTP/2) with the CLI access token. Credentials remain read-only under both credential-management modes. Direct generation may re-read a changed, non-null credential and retry once only on an exact HTTP 401 before output or effects; this is not refresh or writeback. Legacy/compatibility generation remains one-shot. A plugin-owned Node child communicates over private stdio, keeps tool results on the same bidi Run, never replays parked calls, opens no user-facing daemon, and never spawns `cursor-agent` for generation. Unofficial; not a public Cursor API. After protocol drift there is no implicit fallback — that provider fails. Requires Node.js 22 or later. Live catalog ids are used when present; otherwise the documented fallback is `default`. |
|
|
247
|
+
| **Command Code** | Calls `/alpha/generate` with CLI-compatible request metadata and streams provider-local NDJSON text and tool events. Credentials remain read-only under both credential-management modes. On an exact HTTP 401 before output or effects, it may re-read a changed, non-null credential and retry once only; this is not refresh or writeback. The client version comes from `COMMAND_CODE_CLI_VERSION`, an installed `command-code` binary, or the npm registry. Request metadata includes Node.js version, platform, architecture, and the absolute working directory. Live catalog ids are used when present; otherwise the documented fallback is `Qwen/Qwen3.8-Max`. |
|
|
248
|
+
| **Ollama** | Unaffected by `credentialManagement`. Uses the trusted daemon selected by `ollamaBaseURL` (default `http://localhost:11434`) with `/api/tags`, `/api/pull`, and `/api/chat`; path prefixes are preserved. Publishes models already pulled there, plus exact Cloud tags discovered anonymously from Ollama's official Cloud search and library pages, without connector-supplied credentials. Local entries win exact duplicates. Incomplete Cloud refreshes retain the last complete list. Selecting an absent authorized Cloud tag pulls its lightweight remote reference on first use; concurrent pulls of the same tag and normalized base share one in-flight request, and a failed pull can be retried later. The daemon may then proxy Cloud-tag prompts under the user's Ollama Cloud subscription. The connector never uses an Ollama API key, the usage-billed direct Cloud API, `OLLAMA_HOST`, credentials, custom headers, cookies, or a direct Cloud generation endpoint. |
|
|
132
249
|
|
|
133
250
|
Provider health is isolated: one provider failing does not remove the others.
|
|
134
251
|
|
|
135
|
-
The standalone SDK entry is `opencode-ext-connector/ollama
|
|
252
|
+
The standalone SDK entry is `opencode-ext-connector/ollama`; pass `{ ollamaBaseURL }` to select the same trusted daemon. It can generate with models already present there; connector-managed Cloud auto-pull requires an active Ollama catalog lease for that normalized base.
|
|
136
253
|
|
|
137
254
|
## Troubleshooting
|
|
138
255
|
|
|
139
256
|
| Symptom | What to check |
|
|
140
257
|
| --- | --- |
|
|
141
|
-
| `/connect` methods missing |
|
|
142
|
-
| Provider enabled but no models | Omitted `providers` enables all four; an explicit list is a strict allow-list. Claude and Cursor need a marker or OAuth record plus the vendor session; Command Code may use an OpenCode-stored API key or a CLI session/key; Ollama needs the exact marker plus a responsive
|
|
143
|
-
| Claude works until the next start |
|
|
144
|
-
| Claude reports `invalid_grant` on
|
|
258
|
+
| `/connect` methods missing | Confirm `plugin` contains `"opencode-ext-connector"` or an exact published `"opencode-ext-connector@<version>"` spec, then fully restart OpenCode. |
|
|
259
|
+
| Provider enabled but no models | Omitted `providers` enables all four; an explicit list is a strict allow-list. Claude and Cursor need a marker or OAuth record plus the vendor session; Command Code may use an OpenCode-stored API key or a CLI session/key; Ollama needs the exact marker plus a responsive configured daemon. Fully restart after `/connect` so instance reconstruction picks up new membership. |
|
|
260
|
+
| Claude works until the next start | Omitting all credential-policy options preserves legacy in-memory refresh without writeback. A rotated refresh token can then fail on the next process start; use `credentialManagement: "connector"` when the connector should refresh and write back. If only `credentialManagement` is omitted, check supplied deprecated refresh/writeback options instead. |
|
|
261
|
+
| Claude reports `invalid_grant` on shared credentials | Another machine with the same login already refreshed and rotated the refresh token. Give one machine refresh authority with `credentialManagement: "connector"` and use `"external"` on the others, or log in separately. |
|
|
262
|
+
| Configuration rejects credential options | Do not combine the new and legacy options; the exact error is: `` `credentialManagement` cannot be combined with deprecated `credentialRefresh` or `writeBackCredentials` ``. Legacy options remain accepted alone for one migration cycle. |
|
|
145
263
|
| `Claude Code client version is unavailable` | No `ANTHROPIC_CLI_VERSION`, no `claude` binary, and `registry.npmjs.org` was unreachable. Set the variable or allow registry access. |
|
|
146
264
|
| Cursor generation fails | Node.js 22 or later is required. Generation uses the unpublished protocol through a private Node child, not `cursor-agent`. Protocol drift fails that provider; there is no implicit fallback. |
|
|
147
265
|
| Command Code generation fails | The client version could not be resolved: set `COMMAND_CODE_CLI_VERSION`, install `command-code`, or allow access to `registry.npmjs.org`. Request metadata includes Node.js version, platform, architecture, and the absolute working directory. |
|
|
148
|
-
| Ollama missing from `opencode models ollama` | Start a
|
|
266
|
+
| Ollama missing from `opencode models ollama` | Start a daemon you trust at `ollamaBaseURL` (or the default `localhost:11434`), then `/connect` so the exact session marker can be stored. Confirm any path prefix reaches Ollama's `/api/*` routes. Cloud tags are anonymous catalog entries; `OLLAMA_HOST`, API keys, credential headers, redirects, and direct Cloud generation are not used. |
|
|
267
|
+
| Host credentials exist but the guest provider is disconnected | Check the mount target and permissions, the guest's `HOME`, `CLAUDE_CONFIG_DIR`, injected secret environment, writable guest OpenCode `auth.json`, and whether `/connect` completed inside the guest. |
|
|
268
|
+
| Ollama works on the host but not in the guest | Guest `localhost` is usually not the host. Check `host.docker.internal` resolution, Linux `host-gateway` mapping, the daemon bind address, firewall and sandbox egress, and whether any base path prefix reaches Ollama's `/api/*` routes. |
|
|
149
269
|
| One provider is down | Failures are isolated. Transient snapshot failures keep the last-known catalog; an unavailable snapshot removes only that connector-owned provider. |
|
|
150
270
|
|
|
151
271
|
## Documentation
|
package/dist/core/options.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { HealthPolicy } from "./health.js";
|
|
3
3
|
export type CredentialRefreshMode = "auto" | "never";
|
|
4
|
+
export type CredentialManagement = "connector" | "external";
|
|
4
5
|
export type CredentialRefreshPolicy = {
|
|
5
6
|
readonly mode: CredentialRefreshMode;
|
|
6
7
|
readonly leadMs: number;
|
|
@@ -8,7 +9,10 @@ export type CredentialRefreshPolicy = {
|
|
|
8
9
|
export type ConnectorOptionsInput = {
|
|
9
10
|
readonly providers?: readonly ("claude" | "cursor" | "command-code" | "ollama")[] | undefined;
|
|
10
11
|
readonly snapshotTimeoutMs?: number | undefined;
|
|
12
|
+
readonly credentialManagement?: CredentialManagement | undefined;
|
|
13
|
+
/** @deprecated Use credentialManagement instead. */
|
|
11
14
|
readonly writeBackCredentials?: boolean | undefined;
|
|
15
|
+
/** @deprecated Use credentialManagement instead. */
|
|
12
16
|
readonly credentialRefresh?: {
|
|
13
17
|
readonly mode?: CredentialRefreshMode | undefined;
|
|
14
18
|
readonly leadMs?: number | undefined;
|
package/dist/core/options.js
CHANGED
|
@@ -4,16 +4,40 @@ const PositiveSafeIntegerSchema = z.number().int().positive().max(MaximumTimerMs
|
|
|
4
4
|
const NonNegativeSafeIntegerSchema = z.number().int().nonnegative().max(MaximumTimerMs);
|
|
5
5
|
const ProviderSchema = z.enum(["claude", "cursor", "command-code", "ollama"]);
|
|
6
6
|
const CredentialRefreshModeSchema = z.enum(["auto", "never"]);
|
|
7
|
+
const CredentialManagementSchema = z.enum(["connector", "external"]);
|
|
7
8
|
const DefaultProviders = [
|
|
8
9
|
"claude",
|
|
9
10
|
"cursor",
|
|
10
11
|
"command-code",
|
|
11
12
|
"ollama",
|
|
12
13
|
];
|
|
14
|
+
function resolveCredentialOptions(input) {
|
|
15
|
+
switch (input.credentialManagement) {
|
|
16
|
+
case undefined:
|
|
17
|
+
return {
|
|
18
|
+
credentialRefresh: Object.freeze({
|
|
19
|
+
mode: input.credentialRefresh?.mode ?? "auto",
|
|
20
|
+
leadMs: input.credentialRefresh?.leadMs ?? 60_000,
|
|
21
|
+
}),
|
|
22
|
+
writeBackCredentials: input.writeBackCredentials ?? false,
|
|
23
|
+
};
|
|
24
|
+
case "connector":
|
|
25
|
+
return {
|
|
26
|
+
credentialRefresh: Object.freeze({ mode: "auto", leadMs: 60_000 }),
|
|
27
|
+
writeBackCredentials: true,
|
|
28
|
+
};
|
|
29
|
+
case "external":
|
|
30
|
+
return {
|
|
31
|
+
credentialRefresh: Object.freeze({ mode: "never", leadMs: 60_000 }),
|
|
32
|
+
writeBackCredentials: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
13
36
|
const ConnectorOptionsInputSchema = z
|
|
14
37
|
.object({
|
|
15
38
|
providers: z.array(ProviderSchema).optional(),
|
|
16
39
|
snapshotTimeoutMs: PositiveSafeIntegerSchema.optional(),
|
|
40
|
+
credentialManagement: CredentialManagementSchema.optional(),
|
|
17
41
|
writeBackCredentials: z.boolean().optional(),
|
|
18
42
|
credentialRefresh: z
|
|
19
43
|
.object({
|
|
@@ -33,6 +57,14 @@ const ConnectorOptionsInputSchema = z
|
|
|
33
57
|
})
|
|
34
58
|
.strict()
|
|
35
59
|
.superRefine((input, context) => {
|
|
60
|
+
if (input.credentialManagement !== undefined &&
|
|
61
|
+
(input.credentialRefresh !== undefined || input.writeBackCredentials !== undefined)) {
|
|
62
|
+
context.addIssue({
|
|
63
|
+
code: "custom",
|
|
64
|
+
path: ["credentialManagement"],
|
|
65
|
+
message: "`credentialManagement` cannot be combined with deprecated `credentialRefresh` or `writeBackCredentials`",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
36
68
|
const initialBackoffMs = input.health?.initialBackoffMs ?? 1_000;
|
|
37
69
|
const maximumBackoffMs = input.health?.maximumBackoffMs ?? 60_000;
|
|
38
70
|
if (initialBackoffMs > maximumBackoffMs) {
|
|
@@ -44,15 +76,12 @@ export const ConnectorOptionsSchema = ConnectorOptionsInputSchema.transform((inp
|
|
|
44
76
|
initialBackoffMs: input.health?.initialBackoffMs ?? 1_000,
|
|
45
77
|
maximumBackoffMs: input.health?.maximumBackoffMs ?? 60_000,
|
|
46
78
|
});
|
|
47
|
-
const
|
|
48
|
-
mode: input.credentialRefresh?.mode ?? "auto",
|
|
49
|
-
leadMs: input.credentialRefresh?.leadMs ?? 60_000,
|
|
50
|
-
});
|
|
79
|
+
const credentialOptions = resolveCredentialOptions(input);
|
|
51
80
|
return Object.freeze({
|
|
52
81
|
providers: Object.freeze(input.providers ?? DefaultProviders),
|
|
53
82
|
snapshotTimeoutMs: input.snapshotTimeoutMs ?? 30_000,
|
|
54
|
-
writeBackCredentials:
|
|
55
|
-
credentialRefresh,
|
|
83
|
+
writeBackCredentials: credentialOptions.writeBackCredentials,
|
|
84
|
+
credentialRefresh: credentialOptions.credentialRefresh,
|
|
56
85
|
catalogReloadMs: input.catalogReloadMs ?? 300_000,
|
|
57
86
|
health,
|
|
58
87
|
});
|
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
import type { ConnectorOptionsInput } from "../core/options.js";
|
|
2
|
-
|
|
2
|
+
type HostConnectorOptionsInput = Omit<ConnectorOptionsInput, "credentialManagement" | "credentialRefresh" | "writeBackCredentials"> & {
|
|
3
|
+
readonly credentialManagement?: unknown;
|
|
4
|
+
readonly credentialRefresh?: unknown;
|
|
5
|
+
readonly writeBackCredentials?: unknown;
|
|
6
|
+
};
|
|
7
|
+
export declare function pickConnectorOptionsInput(input: unknown): HostConnectorOptionsInput;
|
|
8
|
+
export declare function pickOllamaBaseURL(input: unknown): unknown;
|
|
9
|
+
export {};
|
|
@@ -11,9 +11,6 @@ function pickCredentialRefresh(value) {
|
|
|
11
11
|
const rawMode = "mode" in value ? value.mode : undefined;
|
|
12
12
|
const mode = rawMode === "auto" || rawMode === "never" ? rawMode : undefined;
|
|
13
13
|
const leadMs = "leadMs" in value ? nonNegativeInteger(value.leadMs) : undefined;
|
|
14
|
-
if (mode === undefined && leadMs === undefined) {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
14
|
return { mode, leadMs };
|
|
18
15
|
}
|
|
19
16
|
function pickHealth(value) {
|
|
@@ -31,6 +28,20 @@ export function pickConnectorOptionsInput(input) {
|
|
|
31
28
|
if (typeof input !== "object" || input === null) {
|
|
32
29
|
return {};
|
|
33
30
|
}
|
|
31
|
+
const preservesCredentialPolicy = "credentialManagement" in input &&
|
|
32
|
+
(input.credentialManagement === "connector" || input.credentialManagement === "external");
|
|
33
|
+
const writeBackCredentials = "writeBackCredentials" in input && input.writeBackCredentials !== undefined
|
|
34
|
+
? preservesCredentialPolicy
|
|
35
|
+
? input.writeBackCredentials
|
|
36
|
+
: typeof input.writeBackCredentials === "boolean"
|
|
37
|
+
? input.writeBackCredentials
|
|
38
|
+
: undefined
|
|
39
|
+
: undefined;
|
|
40
|
+
const credentialRefresh = "credentialRefresh" in input && input.credentialRefresh !== undefined
|
|
41
|
+
? preservesCredentialPolicy
|
|
42
|
+
? input.credentialRefresh
|
|
43
|
+
: pickCredentialRefresh(input.credentialRefresh)
|
|
44
|
+
: undefined;
|
|
34
45
|
return {
|
|
35
46
|
providers: "providers" in input && Array.isArray(input.providers)
|
|
36
47
|
? input.providers.filter((provider) => provider === "claude" ||
|
|
@@ -39,11 +50,17 @@ export function pickConnectorOptionsInput(input) {
|
|
|
39
50
|
provider === "ollama")
|
|
40
51
|
: undefined,
|
|
41
52
|
snapshotTimeoutMs: "snapshotTimeoutMs" in input ? positiveInteger(input.snapshotTimeoutMs) : undefined,
|
|
42
|
-
writeBackCredentials
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
...(writeBackCredentials === undefined ? {} : { writeBackCredentials }),
|
|
54
|
+
...(!("credentialManagement" in input) || input.credentialManagement === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: { credentialManagement: input.credentialManagement }),
|
|
57
|
+
...(credentialRefresh === undefined ? {} : { credentialRefresh }),
|
|
46
58
|
catalogReloadMs: "catalogReloadMs" in input ? nonNegativeInteger(input.catalogReloadMs) : undefined,
|
|
47
59
|
health: "health" in input ? pickHealth(input.health) : undefined,
|
|
48
60
|
};
|
|
49
61
|
}
|
|
62
|
+
export function pickOllamaBaseURL(input) {
|
|
63
|
+
return typeof input === "object" && input !== null && "ollamaBaseURL" in input
|
|
64
|
+
? input.ollamaBaseURL
|
|
65
|
+
: undefined;
|
|
66
|
+
}
|
|
@@ -7,7 +7,7 @@ export type ConnectorLanguageDeps = {
|
|
|
7
7
|
readonly transport: HttpTransport;
|
|
8
8
|
readonly readClaudeToken: (signal: AbortSignal) => Promise<string | null>;
|
|
9
9
|
readonly cursorRuntime: CursorDirectRuntime;
|
|
10
|
-
readonly ollamaRuntime
|
|
10
|
+
readonly ollamaRuntime?: OllamaRuntime;
|
|
11
11
|
readonly commandCodeApiKey?: string;
|
|
12
12
|
};
|
|
13
13
|
export declare function createConnectorLanguage(deps: ConnectorLanguageDeps): (providerID: string, modelId: string) => LanguageModelV3 | null;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import type { OllamaEndpoints } from "../providers/ollama/endpoints.js";
|
|
1
2
|
import type { OllamaFetch } from "../providers/ollama/http.js";
|
|
2
|
-
export declare function probeLocalOllama(fetch: OllamaFetch): Promise<boolean>;
|
|
3
|
+
export declare function probeLocalOllama(fetch: OllamaFetch, endpoints: OllamaEndpoints): Promise<boolean>;
|
|
@@ -2,9 +2,9 @@ import { OperationCancelledError } from "../core/errors.js";
|
|
|
2
2
|
import { OllamaCatalogError } from "../providers/ollama/errors.js";
|
|
3
3
|
import { listLocalOllamaModels } from "../providers/ollama/local-catalog.js";
|
|
4
4
|
const OLLAMA_PROBE_TIMEOUT_MS = 5_000;
|
|
5
|
-
export async function probeLocalOllama(fetch) {
|
|
5
|
+
export async function probeLocalOllama(fetch, endpoints) {
|
|
6
6
|
try {
|
|
7
|
-
await listLocalOllamaModels(fetch, AbortSignal.timeout(OLLAMA_PROBE_TIMEOUT_MS));
|
|
7
|
+
await listLocalOllamaModels(fetch, AbortSignal.timeout(OLLAMA_PROBE_TIMEOUT_MS), endpoints);
|
|
8
8
|
return true;
|
|
9
9
|
}
|
|
10
10
|
catch (error) {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { type OllamaCatalogState } from "../providers/ollama/catalog-state.js";
|
|
2
|
+
import { type OllamaEndpoints } from "../providers/ollama/endpoints.js";
|
|
2
3
|
import { type OllamaRuntime } from "../providers/ollama/runtime.js";
|
|
4
|
+
export type ProductionOllamaBundle = {
|
|
5
|
+
readonly endpoints: OllamaEndpoints;
|
|
6
|
+
readonly catalog: OllamaCatalogState;
|
|
7
|
+
readonly runtime: OllamaRuntime;
|
|
8
|
+
};
|
|
9
|
+
export declare function getProductionOllamaBundle(input?: unknown): ProductionOllamaBundle;
|
|
3
10
|
export declare const productionOllamaCatalog: OllamaCatalogState;
|
|
4
11
|
export declare const productionOllamaRuntime: OllamaRuntime;
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { createOllamaCatalogState, } from "../providers/ollama/catalog-state.js";
|
|
2
|
+
import { parseOllamaEndpoints } from "../providers/ollama/endpoints.js";
|
|
2
3
|
import { productionOllamaFetch } from "../providers/ollama/http.js";
|
|
3
4
|
import { createOllamaRuntime } from "../providers/ollama/runtime.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
5
|
+
const bundles = new Map();
|
|
6
|
+
export function getProductionOllamaBundle(input) {
|
|
7
|
+
const endpoints = parseOllamaEndpoints(input);
|
|
8
|
+
const existing = bundles.get(endpoints.baseURL);
|
|
9
|
+
if (existing !== undefined)
|
|
10
|
+
return existing;
|
|
11
|
+
const catalog = createOllamaCatalogState({ fetch: productionOllamaFetch });
|
|
12
|
+
const bundle = Object.freeze({
|
|
13
|
+
endpoints,
|
|
14
|
+
catalog,
|
|
15
|
+
runtime: createOllamaRuntime({ endpoints, catalog, fetch: productionOllamaFetch }),
|
|
16
|
+
});
|
|
17
|
+
bundles.set(endpoints.baseURL, bundle);
|
|
18
|
+
return bundle;
|
|
19
|
+
}
|
|
20
|
+
const defaultBundle = getProductionOllamaBundle();
|
|
21
|
+
export const productionOllamaCatalog = defaultBundle.catalog;
|
|
22
|
+
export const productionOllamaRuntime = defaultBundle.runtime;
|