opencode-ext-connector 0.5.0 → 0.6.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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-09-16
4
+
5
+ - Add the recommended `credentialRole: "owner" | "reader"` option for shared
6
+ Claude logins: one Linux owner runs the existing Claude CLI authority while
7
+ readers consume externally managed credentials without refreshing them
8
+ - Keep the existing low-level credential policy options available for advanced
9
+ control, but reject combining any of them with `credentialRole`
10
+ - Keep omitted credential policy behavior and non-Claude providers unchanged
11
+
3
12
  ## 0.5.0 - 2026-09-15
4
13
 
5
14
  - Add the disabled-by-default Claude CLI credential authority for Linux
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  ## Status
21
21
 
22
- > Independent unofficial community plugin, version **0.5.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).
22
+ > Independent unofficial community plugin, version **0.6.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
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
 
@@ -55,7 +55,7 @@ OpenCode installs configured npm plugins with Bun at startup and caches them. Fo
55
55
  ```jsonc
56
56
  {
57
57
  "$schema": "https://opencode.ai/config.json",
58
- "plugin": ["opencode-ext-connector@0.5.0"]
58
+ "plugin": ["opencode-ext-connector@0.6.0"]
59
59
  }
60
60
  ```
61
61
 
@@ -73,7 +73,8 @@ Omitted `providers` enables all four. An explicit list is a strict allow-list. E
73
73
  | --- | --- | --- |
74
74
  | `providers` | all four | Provider ids to register: `claude`, `cursor`, `command-code`, `ollama`; explicit `[]` disables all |
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 |
76
+ | `credentialRole` | omitted | Recommended shared-login setup: `"owner"` runs the Claude CLI authority on the one credential-owning instance; `"reader"` only reads externally managed credentials |
77
+ | `credentialManagement` | omitted | Advanced policy: `"connector"` authorizes refresh and writeback where an adapter supports both; `"external"` prohibits connector refresh and writeback |
77
78
  | `writeBackCredentials` | `false` | **Deprecated:** accepted alone for one migration cycle; controls Claude writeback after refresh |
78
79
  | `credentialRefresh.mode` | `"auto"` | **Deprecated:** accepted alone for one migration cycle; controls Claude `"auto"` or `"never"` refresh behavior |
79
80
  | `credentialRefresh.leadMs` | `60000` | **Deprecated:** accepted alone for one migration cycle; custom lead times still require this legacy configuration |
@@ -85,6 +86,34 @@ Omitted `providers` enables all four. An explicit list is a strict allow-list. E
85
86
  | `health.initialBackoffMs` | `1000` | Health backoff after a failed snapshot |
86
87
  | `health.maximumBackoffMs` | `60000` | Health backoff cap |
87
88
 
89
+ ### Credential ownership (recommended)
90
+
91
+ Choose `"owner"` on the one Linux instance that owns and refreshes the shared Claude Code login:
92
+
93
+ ```jsonc
94
+ {
95
+ "$schema": "https://opencode.ai/config.json",
96
+ "plugin": [["opencode-ext-connector", { "credentialRole": "owner" }]]
97
+ }
98
+ ```
99
+
100
+ Choose `"reader"` on every instance that only consumes synchronized credentials:
101
+
102
+ ```jsonc
103
+ {
104
+ "$schema": "https://opencode.ai/config.json",
105
+ "plugin": [["opencode-ext-connector", { "credentialRole": "reader" }]]
106
+ }
107
+ ```
108
+
109
+ `"owner"` internally selects external credential management and enables the Claude CLI authority with its default timing. `"reader"` selects external credential management without starting the CLI authority. The role describes credential ownership, not whether OpenCode runs on a physical host or in a sandbox guest.
110
+
111
+ Set exactly one owner per shared Claude login. Owner mode requires Linux, util-linux `flock`, Claude Code `2.1.259` or later on `PATH`, and an authenticated Claude Code session. Each authority invocation is a real model request and may consume account usage. A revoked login still requires interactive `/login`.
112
+
113
+ Do not combine `credentialRole` with `credentialManagement`, `credentialAuthority`, `credentialRefresh`, or `writeBackCredentials`. Existing low-level configurations remain supported for advanced control.
114
+
115
+ ### Advanced credential policy
116
+
88
117
  `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:
89
118
 
90
119
  ```jsonc
@@ -143,17 +172,17 @@ With all credential-policy options omitted, refreshed Claude tokens stay in memo
143
172
 
144
173
  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:
145
174
 
146
- - **Refresh authority** (where you log in): choose exactly one refresh path. Use `credentialManagement: "connector"`, or on Linux use `credentialManagement: "external"` with the opt-in [Claude CLI authority timer](#opt-in-claude-cli-authority-timer-claude-only). The parser rejects enabling the CLI timer in connector mode. If you need a custom publication window for connector mode, use the deprecated legacy options alone for this migration cycle, for example `writeBackCredentials: true` with `credentialRefresh: { mode: "auto", leadMs: 1800000 }`.
147
- - **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.
175
+ - **Credential owner** (where you log in): use `credentialRole: "owner"` on one Linux instance. For advanced control, use `credentialManagement: "connector"`, or combine `credentialManagement: "external"` with the opt-in [Claude CLI authority timer](#opt-in-claude-cli-authority-timer-claude-only).
176
+ - **Credential readers**: use `credentialRole: "reader"`. They never contact the OAuth endpoint; when a request returns 401 they re-read externally managed credentials and retry once.
148
177
  - 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.
149
178
 
150
179
  Machines that refresh on their own — including a Claude Code install that is used interactively — must not share the file. Log in separately there.
151
180
 
152
181
  ### Opt-in Claude CLI authority timer (Claude-only)
153
182
 
154
- This Linux-only option lets one host use an existing Claude Code login as the refresh authority for an externally managed credential file. It is disabled by default, does not log in or mint tokens, and requires `credentialManagement: "external"`.
183
+ This is the advanced form of `credentialRole: "owner"`, useful when custom `leadMs` or `retryMs` values are required. It is disabled by default, does not log in or mint tokens, and requires `credentialManagement: "external"`.
155
184
 
156
- Minimal configuration:
185
+ Low-level equivalent:
157
186
 
158
187
  ```jsonc
159
188
  {
@@ -190,7 +219,7 @@ The safest shared-session layout keeps each vendor login owned and refreshed by
190
219
 
191
220
  | Provider | Host | Guest |
192
221
  | --- | --- | --- |
193
- | 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 |
222
+ | 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 `credentialRole: "reader"`; 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 |
194
223
  | 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 |
195
224
  | 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 |
196
225
  | 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` |
@@ -220,7 +249,7 @@ Use this complete guest `opencode.json` when all four providers are enabled and
220
249
  {
221
250
  "providers": ["claude", "cursor", "command-code", "ollama"],
222
251
  "ollamaBaseURL": "http://host.docker.internal:11434",
223
- "credentialManagement": "external",
252
+ "credentialRole": "reader",
224
253
  "catalogReloadMs": 300000,
225
254
  "snapshotTimeoutMs": 30000,
226
255
  "health": {
@@ -233,7 +262,7 @@ Use this complete guest `opencode.json` when all four providers are enabled and
233
262
  }
234
263
  ```
235
264
 
236
- `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.
265
+ `ollamaBaseURL` is a flat connector option in the package tuple, not an OpenCode provider option. The numeric values above are the connector defaults; `credentialRole: "reader"` 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.
237
266
 
238
267
  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:
239
268
 
@@ -244,7 +273,7 @@ extra_hosts:
244
273
 
245
274
  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.
246
275
 
247
- 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.
276
+ 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 Linux guest that is the sole Claude refresh owner may use `credentialRole: "owner"`; low-level `credentialManagement: "connector"` remains available for connector-managed OAuth refresh and writeback. Never let the host and guest independently refresh credentials descended from the same Claude refresh token.
248
277
 
249
278
  ## Update and Remove
250
279
 
@@ -278,7 +307,7 @@ Ollama `/connect` probes the configured daemon and stores the exact session mark
278
307
 
279
308
  | Provider | What it does |
280
309
  | --- | --- |
281
- | **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. On Linux only, `credentialManagement: "external"` plus the opt-in `credentialAuthority.claudeCli.enabled: true` timer (see [Opt-in Claude CLI authority timer](#opt-in-claude-cli-authority-timer-claude-only)) can keep an externally managed credential file current by invoking the local `claude` CLI; the timer is off by default and does not log in or extend the underlying session lifetime. Omitting all credential-policy options preserves legacy auto/`60_000` behavior without writeback; if only `credentialManagement` is omitted, supplied deprecated options still control behavior. |
310
+ | **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. For shared logins, `credentialRole: "owner"` enables the Linux-only Claude CLI authority on one instance, while `"reader"` keeps every other instance read-only with a credential re-read after 401. Low-level `credentialManagement: "connector"` maps to auto-refresh with a `60_000` ms lead and writeback; `"external"` maps to never-refresh/no-write. Omitting all credential-policy options preserves legacy auto/`60_000` behavior without writeback; if only `credentialManagement` is omitted, supplied deprecated options still control behavior. |
282
311
  | **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`. |
283
312
  | **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`. |
284
313
  | **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. |
@@ -294,8 +323,8 @@ The standalone SDK entry is `opencode-ext-connector/ollama`; pass `{ ollamaBaseU
294
323
  | `/connect` methods missing | Confirm `plugin` contains `"opencode-ext-connector"` or an exact published `"opencode-ext-connector@<version>"` spec, then fully restart OpenCode. |
295
324
  | 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. |
296
325
  | 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. |
297
- | Claude reports `invalid_grant` on shared credentials | Another machine with the same login already refreshed and rotated the refresh token. Every refresher counts: `credentialManagement: "connector"` refreshes through the OAuth endpoint, and the opt-in `credentialAuthority.claudeCli.enabled` timer refreshes through the local `claude` CLI. Pick exactly one authority: either connector mode, or external mode with the CLI timer on one Linux machine. Keep every other machine on `"external"` with the timer disabled, or log in separately. |
298
- | 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. |
326
+ | Claude reports `invalid_grant` on shared credentials | Another machine with the same login already refreshed and rotated the refresh token. Set `credentialRole: "owner"` on exactly one Linux instance and `"reader"` everywhere else, or use exactly one documented low-level authority path. Every refresher counts, including connector OAuth refresh and the Claude CLI timer. |
327
+ | Configuration rejects credential options | Do not combine `credentialRole` with any low-level credential option. The exact role-conflict error is: `` `credentialRole` cannot be combined with `credentialManagement`, `credentialAuthority`, `credentialRefresh`, or `writeBackCredentials` ``. Low-level options remain accepted alone; `credentialManagement` still cannot be combined with deprecated refresh/writeback options. |
299
328
  | `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. |
300
329
  | 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. |
301
330
  | 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. |
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import type { HealthPolicy } from "./health.js";
3
3
  export type CredentialRefreshMode = "auto" | "never";
4
4
  export type CredentialManagement = "connector" | "external";
5
+ export type CredentialRole = "owner" | "reader";
5
6
  export type CredentialRefreshPolicy = {
6
7
  readonly mode: CredentialRefreshMode;
7
8
  readonly leadMs: number;
@@ -17,6 +18,7 @@ export type CredentialAuthority = {
17
18
  export type ConnectorOptionsInput = {
18
19
  readonly providers?: readonly ("claude" | "cursor" | "command-code" | "ollama")[] | undefined;
19
20
  readonly snapshotTimeoutMs?: number | undefined;
21
+ readonly credentialRole?: CredentialRole | undefined;
20
22
  readonly credentialManagement?: CredentialManagement | undefined;
21
23
  readonly credentialAuthority?: {
22
24
  readonly claudeCli: {
@@ -5,6 +5,7 @@ const NonNegativeSafeIntegerSchema = z.number().int().nonnegative().max(MaximumT
5
5
  const ProviderSchema = z.enum(["claude", "cursor", "command-code", "ollama"]);
6
6
  const CredentialRefreshModeSchema = z.enum(["auto", "never"]);
7
7
  const CredentialManagementSchema = z.enum(["connector", "external"]);
8
+ const CredentialRoleSchema = z.enum(["owner", "reader"]);
8
9
  const SafeIntegerSchema = z.number().int().safe();
9
10
  const DefaultProviders = [
10
11
  "claude",
@@ -26,7 +27,8 @@ const CredentialAuthorityInputSchema = z
26
27
  .strict()
27
28
  .readonly();
28
29
  function resolveCredentialOptions(input) {
29
- switch (input.credentialManagement) {
30
+ const credentialManagement = input.credentialRole === undefined ? input.credentialManagement : "external";
31
+ switch (credentialManagement) {
30
32
  case undefined:
31
33
  return {
32
34
  credentialRefresh: Object.freeze({
@@ -51,6 +53,7 @@ const ConnectorOptionsInputSchema = z
51
53
  .object({
52
54
  providers: z.array(ProviderSchema).optional(),
53
55
  snapshotTimeoutMs: PositiveSafeIntegerSchema.optional(),
56
+ credentialRole: CredentialRoleSchema.optional(),
54
57
  credentialManagement: CredentialManagementSchema.optional(),
55
58
  credentialAuthority: CredentialAuthorityInputSchema.optional(),
56
59
  writeBackCredentials: z.boolean().optional(),
@@ -72,6 +75,17 @@ const ConnectorOptionsInputSchema = z
72
75
  })
73
76
  .strict()
74
77
  .superRefine((input, context) => {
78
+ if (input.credentialRole !== undefined &&
79
+ (input.credentialManagement !== undefined ||
80
+ input.credentialAuthority !== undefined ||
81
+ input.credentialRefresh !== undefined ||
82
+ input.writeBackCredentials !== undefined)) {
83
+ context.addIssue({
84
+ code: "custom",
85
+ path: ["credentialRole"],
86
+ message: "`credentialRole` cannot be combined with `credentialManagement`, `credentialAuthority`, `credentialRefresh`, or `writeBackCredentials`",
87
+ });
88
+ }
75
89
  if (input.credentialManagement !== undefined &&
76
90
  (input.credentialRefresh !== undefined || input.writeBackCredentials !== undefined)) {
77
91
  context.addIssue({
@@ -89,6 +103,14 @@ const ConnectorOptionsInputSchema = z
89
103
  message: "Claude CLI credential authority requires external credential management and the Claude provider",
90
104
  });
91
105
  }
106
+ if (input.credentialRole === "owner" &&
107
+ !(input.providers ?? DefaultProviders).includes("claude")) {
108
+ context.addIssue({
109
+ code: "custom",
110
+ path: ["credentialRole"],
111
+ message: "Credential owner role requires the Claude provider",
112
+ });
113
+ }
92
114
  const initialBackoffMs = input.health?.initialBackoffMs ?? 1_000;
93
115
  const maximumBackoffMs = input.health?.maximumBackoffMs ?? 60_000;
94
116
  if (initialBackoffMs > maximumBackoffMs) {
@@ -103,7 +125,9 @@ export const ConnectorOptionsSchema = ConnectorOptionsInputSchema.transform((inp
103
125
  const credentialOptions = resolveCredentialOptions(input);
104
126
  const credentialAuthority = Object.freeze({
105
127
  claudeCli: Object.freeze({
106
- enabled: input.credentialAuthority?.claudeCli.enabled ?? false,
128
+ enabled: input.credentialRole === "owner"
129
+ ? true
130
+ : (input.credentialAuthority?.claudeCli.enabled ?? false),
107
131
  leadMs: input.credentialAuthority?.claudeCli.leadMs ?? 300_000,
108
132
  retryMs: input.credentialAuthority?.claudeCli.retryMs ?? 300_000,
109
133
  }),
@@ -1,8 +1,9 @@
1
1
  import type { ConnectorOptionsInput } from "../core/options.js";
2
- type HostConnectorOptionsInput = Omit<ConnectorOptionsInput, "credentialAuthority" | "credentialManagement" | "credentialRefresh" | "writeBackCredentials"> & {
2
+ type HostConnectorOptionsInput = Omit<ConnectorOptionsInput, "credentialAuthority" | "credentialManagement" | "credentialRefresh" | "credentialRole" | "writeBackCredentials"> & {
3
3
  readonly credentialAuthority?: unknown;
4
4
  readonly credentialManagement?: unknown;
5
5
  readonly credentialRefresh?: unknown;
6
+ readonly credentialRole?: unknown;
6
7
  readonly writeBackCredentials?: unknown;
7
8
  };
8
9
  export declare function pickConnectorOptionsInput(input: unknown): HostConnectorOptionsInput;
@@ -28,8 +28,10 @@ export function pickConnectorOptionsInput(input) {
28
28
  if (typeof input !== "object" || input === null) {
29
29
  return {};
30
30
  }
31
- const preservesCredentialPolicy = "credentialManagement" in input &&
32
- (input.credentialManagement === "connector" || input.credentialManagement === "external");
31
+ const preservesCredentialPolicy = ("credentialManagement" in input &&
32
+ (input.credentialManagement === "connector" || input.credentialManagement === "external")) ||
33
+ ("credentialRole" in input &&
34
+ (input.credentialRole === "owner" || input.credentialRole === "reader"));
33
35
  const writeBackCredentials = "writeBackCredentials" in input && input.writeBackCredentials !== undefined
34
36
  ? preservesCredentialPolicy
35
37
  ? input.writeBackCredentials
@@ -51,6 +53,9 @@ export function pickConnectorOptionsInput(input) {
51
53
  : undefined,
52
54
  snapshotTimeoutMs: "snapshotTimeoutMs" in input ? positiveInteger(input.snapshotTimeoutMs) : undefined,
53
55
  ...(writeBackCredentials === undefined ? {} : { writeBackCredentials }),
56
+ ...(!("credentialRole" in input) || input.credentialRole === undefined
57
+ ? {}
58
+ : { credentialRole: input.credentialRole }),
54
59
  ...(!("credentialManagement" in input) || input.credentialManagement === undefined
55
60
  ? {}
56
61
  : { credentialManagement: input.credentialManagement }),
package/docs/README.ko.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  ## 상태
21
21
 
22
- > 독립적인 비공식 커뮤니티 플러그인, 버전 **0.5.0**. CI에 설치된 OpenCode CLI를 대상으로 legacy multi-function 로더를 패키지 E2E 테스트로 검증합니다. `@opencode-ai/plugin@1.18.18`은 컴파일 시 사용하는 플러그인 API 대상이며 OpenCode 런타임 버전 고정이 아닙니다. 소스는 BSD-3-Clause입니다. 이 프로젝트는 OpenCode 또는 어떤 프로바이더와도 제휴, 보증, 후원, 승인 관계가 없습니다. 전체 조건은 [라이선스 및 면책 조항](#라이선스-및-면책-조항)에 있습니다.
22
+ > 독립적인 비공식 커뮤니티 플러그인, 버전 **0.6.0**. CI에 설치된 OpenCode CLI를 대상으로 legacy multi-function 로더를 패키지 E2E 테스트로 검증합니다. `@opencode-ai/plugin@1.18.18`은 컴파일 시 사용하는 플러그인 API 대상이며 OpenCode 런타임 버전 고정이 아닙니다. 소스는 BSD-3-Clause입니다. 이 프로젝트는 OpenCode 또는 어떤 프로바이더와도 제휴, 보증, 후원, 승인 관계가 없습니다. 전체 조건은 [라이선스 및 면책 조항](#라이선스-및-면책-조항)에 있습니다.
23
23
 
24
24
  이미 가지고 있는 Claude, Cursor, Command Code, Ollama 세션을 재사용합니다. `opencode.json` 플러그인 항목 하나가 라이브 카탈로그를 OpenCode에 공개합니다. Claude와 Cursor는 OpenCode에 마커 또는 OAuth 레코드가 있고 벤더 세션이 있을 때까지 연결되지 않은 상태로 유지됩니다. Command Code는 OpenCode에 저장된 직접 API 키 또는 기존 CLI 세션/키를 사용할 수 있습니다. Ollama는 정확한 세션 마커와 응답하는 신뢰된 데몬이 필요합니다.
25
25
 
@@ -55,7 +55,7 @@ OpenCode는 시작 시 Bun으로 설정된 npm 플러그인을 설치하고 캐
55
55
  ```jsonc
56
56
  {
57
57
  "$schema": "https://opencode.ai/config.json",
58
- "plugin": ["opencode-ext-connector@0.5.0"]
58
+ "plugin": ["opencode-ext-connector@0.6.0"]
59
59
  }
60
60
  ```
61
61
 
@@ -73,7 +73,8 @@ OpenCode는 플러그인 옵션을 두 요소 튜플의 두 번째 항목으로
73
73
  | --- | --- | --- |
74
74
  | `providers` | 네 프로바이더 모두 | 등록할 프로바이더 id: `claude`, `cursor`, `command-code`, `ollama`; 명시적 `[]`는 모두 비활성화 |
75
75
  | `ollamaBaseURL` | `"http://localhost:11434"` | 신뢰하는 Ollama 데몬의 절대 `http` 또는 `https` base; 경로 prefix를 보존 |
76
- | `credentialManagement` | 생략 | 권장 권한 정책: `"connector"`는 어댑터가 갱신과 writeback을 모두 지원할 이를 허용하고, `"external"`은 커넥터의 갱신과 writeback을 금지 |
76
+ | `credentialRole` | 생략 | 권장 공유 로그인 설정: `"owner"`는 자격 증명을 소유한 단일 인스턴스에서 Claude CLI 권한을 실행하고, `"reader"`는 외부 관리 자격 증명만 읽음 |
77
+ | `credentialManagement` | 생략 | 고급 정책: `"connector"`는 어댑터가 갱신과 writeback을 모두 지원할 때 이를 허용하고, `"external"`은 커넥터의 갱신과 writeback을 금지 |
77
78
  | `writeBackCredentials` | `false` | **사용 중단 예정:** 한 번의 마이그레이션 주기 동안 단독 사용 시 허용되며, 갱신 후 Claude writeback을 제어 |
78
79
  | `credentialRefresh.mode` | `"auto"` | **사용 중단 예정:** 한 번의 마이그레이션 주기 동안 단독 사용 시 허용되며, Claude의 `"auto"` 또는 `"never"` 갱신 동작을 제어 |
79
80
  | `credentialRefresh.leadMs` | `60000` | **사용 중단 예정:** 한 번의 마이그레이션 주기 동안 단독 사용 시 허용되며, 사용자 지정 리드 타임에는 여전히 이 레거시 설정이 필요 |
@@ -85,6 +86,34 @@ OpenCode는 플러그인 옵션을 두 요소 튜플의 두 번째 항목으로
85
86
  | `health.initialBackoffMs` | `1000` | 스냅샷 실패 후 health backoff |
86
87
  | `health.maximumBackoffMs` | `60000` | health backoff 상한 |
87
88
 
89
+ ### 자격 증명 소유권 (권장)
90
+
91
+ 공유 Claude Code 로그인을 소유하고 갱신하는 단일 Linux 인스턴스에서 `"owner"`를 선택하십시오:
92
+
93
+ ```jsonc
94
+ {
95
+ "$schema": "https://opencode.ai/config.json",
96
+ "plugin": [["opencode-ext-connector", { "credentialRole": "owner" }]]
97
+ }
98
+ ```
99
+
100
+ 동기화된 자격 증명만 소비하는 모든 인스턴스에서는 `"reader"`를 선택하십시오:
101
+
102
+ ```jsonc
103
+ {
104
+ "$schema": "https://opencode.ai/config.json",
105
+ "plugin": [["opencode-ext-connector", { "credentialRole": "reader" }]]
106
+ }
107
+ ```
108
+
109
+ `"owner"`는 내부적으로 external 자격 증명 관리를 선택하고 기본 타이밍으로 Claude CLI 권한을 활성화합니다. `"reader"`는 CLI 권한을 시작하지 않고 external 자격 증명 관리를 선택합니다. 이 역할은 자격 증명 소유권을 뜻하며 OpenCode가 물리 호스트에서 실행되는지, 샌드박스 게스트에서 실행되는지를 뜻하지 않습니다.
110
+
111
+ 공유 Claude 로그인마다 owner를 정확히 하나만 설정하십시오. Owner 모드에는 Linux, util-linux `flock`, `PATH`의 Claude Code `2.1.259` 이상, 인증된 Claude Code 세션이 필요합니다. 각 권한 호출은 실제 모델 요청이며 계정 사용량을 소비할 수 있습니다. 폐기된 로그인은 여전히 대화형 `/login`이 필요합니다.
112
+
113
+ `credentialRole`을 `credentialManagement`, `credentialAuthority`, `credentialRefresh`, `writeBackCredentials`와 함께 사용하지 마십시오. 기존 저수준 설정은 고급 제어용으로 계속 지원됩니다.
114
+
115
+ ### 고급 자격 증명 정책
116
+
88
117
  `credentialManagement: "connector"`는 프로바이더 어댑터가 갱신과 writeback을 모두 지원하는 경우 커넥터에 그 권한을 부여합니다. 현재 이 기능을 지원하는 것은 Claude뿐이며, 만료 `60_000`ms 전 자동 갱신과 writeback 활성화로 매핑됩니다:
89
118
 
90
119
  ```jsonc
@@ -143,17 +172,17 @@ OpenCode는 인스턴스 구성 중에 활성 프로바이더 레지스트리를
143
172
 
144
173
  Anthropic은 갱신할 때마다 refresh 토큰을 회전시키고 이전 토큰을 무효화합니다. 따라서 `~/.claude/.credentials.json` 사본 두 개가 각자 갱신하면 서로를 깨뜨립니다. 파일 복사는 정확히 한 머신만 갱신하고, 나머지 머신이 자기 사본이 만료되기 전에 그 결과를 받을 때만 동작합니다:
145
174
 
146
- - **갱신 권한 머신** (로그인한 곳): 정확히 하나의 갱신 경로를 선택하십시오. `credentialManagement: "connector"`를 사용하거나, Linux에서는 `credentialManagement: "external"`과 옵트인 [Claude CLI 권한 타이머](#옵트인-claude-cli-권한-타이머-claude-전용)를 함께 사용하십시오. 파서는 connector 모드에서 CLI 타이머 활성화를 거부합니다. connector 모드에 사용자 지정 배포 여유 시간이 필요하면 이번 마이그레이션 주기에는 사용 중단 예정인 레거시 옵션만 사용하십시오. 예: `writeBackCredentials: true`와 `credentialRefresh: { mode: "auto", leadMs: 1800000 }`.
147
- - **외부 권한 머신**: `credentialManagement: "external"`. OAuth 엔드포인트에 접속하지 않으며, 요청이 401을 반환하면 외부에서 관리되는 자격 증명을 다시 읽고 한 번 재시도합니다.
175
+ - **자격 증명 owner** (로그인한 곳): 단일 Linux 인스턴스에서 `credentialRole: "owner"`를 사용하십시오. 고급 제어가 필요하면 `credentialManagement: "connector"`를 사용하거나 `credentialManagement: "external"`과 옵트인 [Claude CLI 권한 타이머](#옵트인-claude-cli-권한-타이머-claude-전용)를 결합하십시오.
176
+ - **자격 증명 reader**: `credentialRole: "reader"`를 사용하십시오. OAuth 엔드포인트에 접속하지 않으며, 요청이 401을 반환하면 외부에서 관리되는 자격 증명을 다시 읽고 한 번 재시도합니다.
148
177
  - 갱신 권한 머신의 외부 관리 자격 증명 자료가 바뀔 때마다 동기화하십시오. 이 옵션 자체는 머신을 동기화하거나 파일 저장을 요구하지 않습니다. `~/.claude/.credentials.json`을 복사하는 경우 OpenCode 자체 `auth.json`에는 `anthropic` 레코드만 한 번 있으면 되며 다른 프로바이더는 건드리지 마십시오.
149
178
 
150
179
  대화형으로 사용하는 Claude Code 설치처럼 스스로 갱신하는 머신은 파일을 공유하면 안 됩니다. 그곳에서는 별도로 로그인하십시오.
151
180
 
152
181
  ### 옵트인 Claude CLI 권한 타이머 (Claude 전용)
153
182
 
154
- Linux 전용 옵션은 단일 호스트의 기존 Claude Code 로그인을 외부 관리 자격 증명 파일의 갱신 권한으로 사용합니다. 기본값은 꺼짐이며, 로그인하거나 토큰을 발급하지 않고 `credentialManagement: "external"`이 필요합니다.
183
+ 이 옵션은 사용자 지정 `leadMs` 또는 `retryMs`가 필요할 쓰는 `credentialRole: "owner"`의 고급 형태입니다. 기본값은 꺼짐이며, 로그인하거나 토큰을 발급하지 않고 `credentialManagement: "external"`이 필요합니다.
155
184
 
156
- 최소 설정:
185
+ 저수준 등가 설정:
157
186
 
158
187
  ```jsonc
159
188
  {
@@ -190,7 +219,7 @@ OpenCode가 컨테이너, VM 또는 다른 샌드박스에서 실행될 때 그
190
219
 
191
220
  | 프로바이더 | 호스트 | 게스트 |
192
221
  | --- | --- | --- |
193
- | Claude | Claude Code 로그인을 소유하고 갱신 | Claude 자격 증명 디렉터리를 read-only로 mount하고 `CLAUDE_CONFIG_DIR`을 그 게스트 경로로 설정하며 `credentialManagement: "external"`을 사용; 호스트 macOS Keychain은 Linux 게스트 안에서 사용할 수 없음; `ANTHROPIC_CLI_VERSION`, 설치된 `claude` 바이너리 또는 npm registry 접근으로 클라이언트 버전 확인 |
222
+ | Claude | Claude Code 로그인을 소유하고 갱신 | Claude 자격 증명 디렉터리를 read-only로 mount하고 `CLAUDE_CONFIG_DIR`을 그 게스트 경로로 설정하며 `credentialRole: "reader"`를 사용; 호스트 macOS Keychain은 Linux 게스트 안에서 사용할 수 없음; `ANTHROPIC_CLI_VERSION`, 설치된 `claude` 바이너리 또는 npm registry 접근으로 클라이언트 버전 확인 |
194
223
  | Cursor | Cursor CLI 로그인을 소유 | 자격 증명 파일을 게스트의 `${HOME}/.config/cursor/auth.json`에 read-only로 mount하거나 샌드박스 secret 기능으로 `CURSOR_ACCESS_TOKEN` 주입; 게스트에 Node.js 22 이상 설치 |
195
224
  | Command Code | CLI 로그인 또는 API 키를 소유 | `${HOME}/.commandcode/auth.json`을 read-only로 mount하거나 `COMMAND_CODE_API_KEY` 주입; `COMMAND_CODE_CLI_VERSION`, 설치된 `command-code` 바이너리 또는 npm registry 접근으로 클라이언트 버전 확인 |
196
225
  | Ollama | 신뢰하는 데몬을 실행하고 Cloud 접근이 필요하면 그곳에서 `ollama signin` 실행 | Ollama 자격 증명을 복사하지 않고 `ollamaBaseURL`로 선택한 데몬에만 연결 |
@@ -220,7 +249,7 @@ COMMAND_CODE_CLI_VERSION=<optional-compatible-version>
220
249
  {
221
250
  "providers": ["claude", "cursor", "command-code", "ollama"],
222
251
  "ollamaBaseURL": "http://host.docker.internal:11434",
223
- "credentialManagement": "external",
252
+ "credentialRole": "reader",
224
253
  "catalogReloadMs": 300000,
225
254
  "snapshotTimeoutMs": 30000,
226
255
  "health": {
@@ -233,7 +262,7 @@ COMMAND_CODE_CLI_VERSION=<optional-compatible-version>
233
262
  }
234
263
  ```
235
264
 
236
- `ollamaBaseURL`은 OpenCode 프로바이더 옵션이 아니라 패키지 tuple에 넣는 flat 커넥터 옵션입니다. 위 숫자 값은 커넥터 기본값이고, `credentialManagement: "external"`과 호스트 데몬 URL은 read-only 호스트 소유 자격 증명을 위한 의도적인 override입니다. `opencode.json`에 벤더 토큰을 넣지 말고 read-only mount 또는 샌드박스 secret 주입 기능을 사용하십시오.
265
+ `ollamaBaseURL`은 OpenCode 프로바이더 옵션이 아니라 패키지 tuple에 넣는 flat 커넥터 옵션입니다. 위 숫자 값은 커넥터 기본값이고, `credentialRole: "reader"`와 호스트 데몬 URL은 read-only 호스트 소유 자격 증명을 위한 의도적인 override입니다. `opencode.json`에 벤더 토큰을 넣지 말고 read-only mount 또는 샌드박스 secret 주입 기능을 사용하십시오.
237
266
 
238
267
  Docker Desktop에서는 보통 `host.docker.internal`이 호스트로 resolve됩니다. Linux Docker bridge에는 `--add-host=host.docker.internal:host-gateway` 또는 다음 Compose 설정이 추가로 필요할 수 있습니다:
239
268
 
@@ -244,7 +273,7 @@ extra_hosts:
244
273
 
245
274
  Ollama는 보통 호스트 loopback에서 수신합니다. Bridge networking에서는 호스트가 `OLLAMA_HOST=0.0.0.0:11434`로 시작해야 할 수 있으며, 노출된 port를 호스트 firewall과 샌드박스 network policy로 제한하십시오. `OLLAMA_HOST`는 호스트 데몬을 설정하고 `ollamaBaseURL`은 게스트의 이 커넥터를 설정합니다. Host networking을 사용하면 게스트 `localhost`가 호스트에 도달하지만 격리가 약해지므로 명시적으로 선택해야 합니다. 다른 샌드박스 런타임도 이에 해당하는 호스트 route가 필요하며, 활성화한 각 프로바이더로 outbound 접근을 허용해야 합니다. Claude 또는 Command Code가 환경 값이나 설치된 바이너리에서 클라이언트 버전을 확인할 수 없을 때만 `registry.npmjs.org` 접근을 허용하십시오.
246
275
 
247
- 대신 게스트가 persistent guest storage에서 자체 벤더 로그인을 소유할 수도 있습니다. 이 모드에서는 호스트 자격 증명을 mount하지 말고 게스트에서 벤더 로그인 flow를 실행하십시오. 게스트가 유일한 Claude 갱신 소유자라면 `credentialManagement: "connector"`를 사용할 수 있습니다. 호스트와 게스트가 같은 Claude refresh token에서 파생된 자격 증명을 각자 갱신하게 해서는 안 됩니다.
276
+ 대신 게스트가 persistent guest storage에서 자체 벤더 로그인을 소유할 수도 있습니다. 이 모드에서는 호스트 자격 증명을 mount하지 말고 게스트에서 벤더 로그인 flow를 실행하십시오. Linux 게스트가 유일한 Claude 갱신 owner라면 `credentialRole: "owner"`를 사용할 수 있으며, 커넥터가 OAuth 갱신과 writeback을 관리해야 할 때는 저수준 `credentialManagement: "connector"`도 계속 사용할 수 있습니다. 호스트와 게스트가 같은 Claude refresh token에서 파생된 자격 증명을 각자 갱신하게 해서는 안 됩니다.
248
277
 
249
278
  ## 업데이트 및 제거
250
279
 
@@ -278,7 +307,7 @@ Ollama `/connect`는 설정된 데몬을 조사하고 정확한 세션 마커를
278
307
 
279
308
  | 프로바이더 | 하는 일 |
280
309
  | --- | --- |
281
- | **Claude** | 기존 Claude Code 자격 증명을 재사용합니다. OAuth를 발급하지 않습니다. 호환 fetch가 CLI 호환 요청 메타데이터를 보내고, 내장 `anthropic` 경로에서 Anthropic SSE를 스트림합니다. `credentialManagement: "connector"`는 `60_000`ms 리드 타임의 자동 갱신과 writeback으로, `"external"`은 갱신 함/writeback 401 이후 자격 증명 다시 읽기로 매핑됩니다. Linux에서만 `credentialManagement: "external"`에 옵트인 `credentialAuthority.claudeCli.enabled: true` 타이머([옵트인 Claude CLI 권한 타이머](#옵트인-claude-cli-권한-타이머-claude-전용) 참조)를 더해 로컬 `claude` CLI를 호출함으로써 외부 관리 자격 증명 파일을 최신 상태로 유지할 수 있습니다. 이 타이머는 기본값이 꺼져 있으며 로그인하지도 기반 세션 수명을 연장하지도 않습니다. 모든 자격 증명 정책 옵션을 생략하면 레거시 자동 갱신/`60_000` 동작과 writeback 안 함이 유지되며, `credentialManagement`만 생략하면 제공된 사용 중단 예정 옵션이 계속 동작을 제어합니다. |
310
+ | **Claude** | 기존 Claude Code 자격 증명을 재사용합니다. OAuth를 발급하지 않습니다. 호환 fetch가 CLI 호환 요청 메타데이터를 보내고, 내장 `anthropic` 경로에서 Anthropic SSE를 스트림합니다. 공유 로그인에서는 `credentialRole: "owner"`가 단일 인스턴스의 Linux 전용 Claude CLI 권한을 활성화하고, `"reader"`는 나머지 인스턴스를 읽기 전용으로 유지하면서 401 이후 자격 증명을 다시 읽습니다. 저수준 `credentialManagement: "connector"`는 `60_000`ms 리드 타임의 자동 갱신과 writeback으로, `"external"`은 갱신 함/writeback 함으로 매핑됩니다. 모든 자격 증명 정책 옵션을 생략하면 레거시 자동 갱신/`60_000` 동작과 writeback 안 함이 유지되며, `credentialManagement`만 생략하면 제공된 사용 중단 예정 옵션이 계속 동작을 제어합니다. |
282
311
  | **Cursor** | CLI 액세스 토큰으로 Cursor의 미공개 클라이언트 프로토콜(`api2.cursor.sh` `AgentService`, HTTP/2 위의 Connect+protobuf)을 호출합니다. 두 자격 증명 관리 모드 모두 자격 증명은 읽기 전용입니다. direct 생성은 정확한 HTTP 401이 출력이나 효과 전에 발생할 때만 null이 아니며 변경된 자격 증명을 다시 읽고 한 번 재시도할 수 있으며, 이는 갱신이나 writeback이 아닙니다. legacy/compatibility 생성은 한 번만 시도합니다. 플러그인이 소유한 Node 자식 프로세스가 private stdio로 통신하고, 툴 결과를 같은 bidi Run에 유지하며, parked call을 절대 재실행하지 않고, 사용자 대면 데몬을 열지 않으며, 생성에 `cursor-agent`를 절대 spawn하지 않습니다. 비공식이며 공개 Cursor API가 아닙니다. 프로토콜이 어긋난 뒤에는 암시적 fallback이 없습니다 — 해당 프로바이더가 실패합니다. Node.js 22 이상이 필요합니다. 라이브 카탈로그 id가 있으면 그것을 쓰고, 없으면 문서화된 fallback은 `default`입니다. |
283
312
  | **Command Code** | CLI 호환 요청 메타데이터와 함께 `/alpha/generate`를 호출하고, 프로바이더 로컬 NDJSON 텍스트와 툴 이벤트를 스트림합니다. 두 자격 증명 관리 모드 모두 자격 증명은 읽기 전용입니다. 정확한 HTTP 401이 출력이나 효과 전에 발생하면 null이 아니며 변경된 자격 증명을 다시 읽고 한 번만 재시도할 수 있으며, 이는 갱신이나 writeback이 아닙니다. 클라이언트 버전은 `COMMAND_CODE_CLI_VERSION`, 설치된 `command-code` 바이너리, 또는 npm registry에서 가져옵니다. 요청 메타데이터에는 Node.js 버전, 플랫폼, 아키텍처, 절대 작업 디렉터리가 포함됩니다. 라이브 카탈로그 id가 있으면 그것을 쓰고, 없으면 문서화된 fallback은 `Qwen/Qwen3.8-Max`입니다. |
284
313
  | **Ollama** | `credentialManagement`의 영향을 받지 않습니다. `ollamaBaseURL`로 선택한 신뢰된 데몬(기본값 `http://localhost:11434`)의 `/api/tags`, `/api/pull`, `/api/chat`을 사용하며 경로 prefix를 보존합니다. 이미 pull된 모델과, 커넥터 자격 증명 없이 Ollama 공식 Cloud 검색 및 library 페이지에서 익명으로 발견한 정확한 Cloud 태그를 공개합니다. 정확히 중복되는 항목은 로컬이 이깁니다. 불완전한 Cloud 갱신은 마지막 완전한 목록을 유지합니다. 없는 인가된 Cloud 태그를 선택하면 최초 사용 시 lightweight remote reference를 pull합니다. 같은 정규화 base와 태그의 동시 pull은 하나의 in-flight 요청을 공유하며 실패한 pull은 재시도할 수 있습니다. 데몬은 사용자의 Ollama Cloud 구독으로 Cloud 태그 프롬프트를 proxy할 수 있습니다. 커넥터는 Ollama API 키, 사용량 과금 direct Cloud API, `OLLAMA_HOST`, 자격 증명/custom header, cookie, direct Cloud 생성 endpoint를 사용하지 않습니다. |
@@ -294,8 +323,8 @@ Ollama `/connect`는 설정된 데몬을 조사하고 정확한 세션 마커를
294
323
  | `/connect` 메서드가 없음 | `plugin`에 `"opencode-ext-connector"` 또는 정확한 공개 `"opencode-ext-connector@<version>"` spec이 있는지 확인한 뒤 OpenCode를 완전히 재시작하십시오. |
295
324
  | 프로바이더가 활성화됐지만 모델이 없음 | `providers`를 생략하면 네 프로바이더가 모두 활성화됩니다. 명시적 목록은 엄격한 allow-list입니다. Claude와 Cursor는 마커 또는 OAuth 레코드와 벤더 세션이 필요하고, Command Code는 OpenCode에 저장된 API 키 또는 CLI 세션/키를 사용할 수 있으며, Ollama는 정확한 마커와 응답하는 설정된 데몬이 필요합니다. `/connect` 후 완전히 재시작해야 인스턴스 재생성이 새 소속을 반영합니다. |
296
325
  | Claude가 다음 시작 전까지만 동작함 | 모든 자격 증명 정책 옵션을 생략하면 레거시의 메모리 내 갱신과 writeback 안 함이 유지됩니다. 회전된 refresh 토큰은 다음 프로세스 시작에서 실패할 수 있습니다. 커넥터가 갱신하고 기록해야 한다면 `credentialManagement: "connector"`를 사용하십시오. `credentialManagement`만 생략했다면 제공된 사용 중단 예정 갱신/writeback 옵션을 확인하십시오. |
297
- | 공유 자격 증명에서 Claude가 `invalid_grant`를 보고함 | 같은 로그인을 쓰는 다른 머신이 이미 갱신해서 refresh 토큰이 회전됐습니다. 모든 갱신자가 집계됩니다. `credentialManagement: "connector"`는 OAuth 엔드포인트를 통해 갱신하고, 옵트인 `credentialAuthority.claudeCli.enabled` 타이머는 로컬 `claude` CLI를 통해 갱신합니다. connector 모드 또는 단일 Linux 머신의 external 모드와 CLI 타이머 정확히 하나만 권한으로 선택하십시오. 나머지 모든 머신은 타이머가 비활성화된 `"external"`로 유지하거나 각 머신에서 별도로 로그인하십시오. |
298
- | 설정이 자격 증명 옵션을 거부함 | 옵션과 레거시 옵션을 함께 사용하지 마십시오. 정확한 오류는 다음과 같습니다: `` `credentialManagement` cannot be combined with deprecated `credentialRefresh` or `writeBackCredentials` ``. 레거시 옵션은 번의 마이그레이션 주기 동안 단독으로 계속 허용됩니다. |
326
+ | 공유 자격 증명에서 Claude가 `invalid_grant`를 보고함 | 같은 로그인을 쓰는 다른 머신이 이미 갱신해서 refresh 토큰이 회전됐습니다. 단일 Linux 인스턴스에 `credentialRole: "owner"`를, 나머지 모든 인스턴스에 `"reader"`를 설정하거나 문서화된 저수준 권한 경로를 정확히 하나만 사용하십시오. 커넥터 OAuth 갱신과 Claude CLI 타이머를 포함해 모든 갱신자가 집계됩니다. |
327
+ | 설정이 자격 증명 옵션을 거부함 | `credentialRole`을 어떤 저수준 자격 증명 옵션과도 함께 사용하지 마십시오. 정확한 역할 충돌 오류는 다음과 같습니다: `` `credentialRole` cannot be combined with `credentialManagement`, `credentialAuthority`, `credentialRefresh`, or `writeBackCredentials` ``. 저수준 옵션은 단독으로 계속 허용되며, `credentialManagement`는 여전히 사용 중단 예정인 갱신/writeback 옵션과 함께 사용할 수 없습니다. |
299
328
  | `Claude Code client version is unavailable` | `ANTHROPIC_CLI_VERSION`도, `claude` 바이너리도 없고 `registry.npmjs.org`에 접근할 수 없었습니다. 변수를 설정하거나 registry 접근을 허용하십시오. |
300
329
  | Cursor 생성이 실패함 | Node.js 22 이상이 필요합니다. 생성은 `cursor-agent`가 아니라 private Node 자식 프로세스를 통한 미공개 프로토콜을 사용합니다. 프로토콜이 어긋나면 해당 프로바이더가 실패하며, 암시적 fallback은 없습니다. |
301
330
  | Command Code 생성이 실패함 | 클라이언트 버전을 확인할 수 없었습니다: `COMMAND_CODE_CLI_VERSION`을 설정하거나, `command-code`를 설치하거나, `registry.npmjs.org` 접근을 허용하십시오. 요청 메타데이터에는 Node.js 버전, 플랫폼, 아키텍처, 절대 작업 디렉터리가 포함됩니다. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-ext-connector",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "External provider connector for OpenCode",
5
5
  "type": "module",
6
6
  "license": "BSD-3-Clause",