opencode-ext-connector 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +4 -4
- package/dist/core/credential-authority-options.d.ts +18 -0
- package/dist/core/credential-authority-options.js +25 -0
- package/dist/core/options.d.ts +3 -15
- package/dist/core/options.js +3 -24
- package/dist/providers/claude/credential-authority-scheduler.js +13 -0
- package/dist/server.js +2 -2
- package/docs/README.ko.md +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.1 - 2026-09-23
|
|
4
|
+
|
|
5
|
+
- Restrict the `credentialAuthority` schema to a strict, deeply-frozen Claude CLI
|
|
6
|
+
object: unknown keys fail parsing, empty input normalizes to disabled defaults
|
|
7
|
+
with `leadMs: 300_000` and `retryMs: 300_000`, and only `claudeCli` is accepted
|
|
8
|
+
- Strip `ANTHROPIC_API_KEY` from every Claude authority child invocation so the
|
|
9
|
+
stored OAuth login is always used regardless of any in-memory key, without
|
|
10
|
+
mutating the parent environment
|
|
11
|
+
- Attempt every disposer on shutdown through `Promise.allSettled` and keep the
|
|
12
|
+
Claude authority failure as the primary error so the xAI authority, supervisor,
|
|
13
|
+
and runtime disposers still run alongside Claude while Claude stays primary
|
|
14
|
+
|
|
3
15
|
## 0.7.0 - 2026-09-23
|
|
4
16
|
|
|
5
17
|
- Add the opt-in `xaiOAuth.mode: "authority" | "consumer"` package subpath
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
## Status
|
|
21
21
|
|
|
22
|
-
> Independent unofficial community plugin, version **0.7.
|
|
22
|
+
> Independent unofficial community plugin, version **0.7.1**. 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.7.
|
|
58
|
+
"plugin": ["opencode-ext-connector@0.7.1"]
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
@@ -109,7 +109,7 @@ Choose `"reader"` on every instance that only consumes synchronized credentials:
|
|
|
109
109
|
|
|
110
110
|
`"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.
|
|
111
111
|
|
|
112
|
-
Set exactly one owner per shared Claude login. Owner mode requires Linux, util-linux `flock`, Claude Code `2.1.
|
|
112
|
+
Set exactly one owner per shared Claude login. Owner mode requires Linux, util-linux `flock`, Claude Code `2.1.265` 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`.
|
|
113
113
|
|
|
114
114
|
Do not combine `credentialRole` with `credentialManagement`, `credentialAuthority`, `credentialRefresh`, or `writeBackCredentials`. Existing low-level configurations remain supported for advanced control.
|
|
115
115
|
|
|
@@ -204,7 +204,7 @@ Low-level equivalent:
|
|
|
204
204
|
}
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
After saving the configuration, fully restart OpenCode. The timer requires Linux, util-linux `flock`, Claude Code `2.1.
|
|
207
|
+
After saving the configuration, fully restart OpenCode. The timer requires Linux, util-linux `flock`, Claude Code `2.1.265` or later on `PATH`, an authenticated Claude Code session, and a writable persistent state directory. It is silently disabled on other platforms.
|
|
208
208
|
|
|
209
209
|
`leadMs` and `retryMs` both default to `300000` (5 minutes). The connector schedules one restricted, single-turn Claude request at the lead boundary. A process-shared non-blocking lock prevents concurrent requests from processes using the same state directory; failures retry after `retryMs` without removing the Claude provider.
|
|
210
210
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export type ClaudeCliCredentialAuthority = {
|
|
3
|
+
readonly enabled: boolean;
|
|
4
|
+
readonly leadMs: number;
|
|
5
|
+
readonly retryMs: number;
|
|
6
|
+
};
|
|
7
|
+
export type CredentialAuthority = {
|
|
8
|
+
readonly claudeCli: ClaudeCliCredentialAuthority;
|
|
9
|
+
};
|
|
10
|
+
export type CredentialAuthorityInput = {
|
|
11
|
+
readonly claudeCli?: {
|
|
12
|
+
readonly enabled: boolean;
|
|
13
|
+
readonly leadMs?: number | undefined;
|
|
14
|
+
readonly retryMs?: number | undefined;
|
|
15
|
+
} | undefined;
|
|
16
|
+
};
|
|
17
|
+
export declare const CredentialAuthorityInputSchema: z.ZodType<CredentialAuthorityInput, CredentialAuthorityInput>;
|
|
18
|
+
export declare function resolveCredentialAuthority(input: CredentialAuthorityInput | undefined, claudeOwner: boolean): CredentialAuthority;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const SafeIntegerSchema = z.number().int().safe();
|
|
3
|
+
export const CredentialAuthorityInputSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
claudeCli: z
|
|
6
|
+
.object({
|
|
7
|
+
enabled: z.boolean(),
|
|
8
|
+
leadMs: SafeIntegerSchema.nonnegative().optional(),
|
|
9
|
+
retryMs: SafeIntegerSchema.positive().optional(),
|
|
10
|
+
})
|
|
11
|
+
.strict()
|
|
12
|
+
.readonly()
|
|
13
|
+
.optional(),
|
|
14
|
+
})
|
|
15
|
+
.strict()
|
|
16
|
+
.readonly();
|
|
17
|
+
export function resolveCredentialAuthority(input, claudeOwner) {
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
claudeCli: Object.freeze({
|
|
20
|
+
enabled: claudeOwner || (input?.claudeCli?.enabled ?? false),
|
|
21
|
+
leadMs: input?.claudeCli?.leadMs ?? 300_000,
|
|
22
|
+
retryMs: input?.claudeCli?.retryMs ?? 300_000,
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
}
|
package/dist/core/options.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { type CredentialAuthority, type CredentialAuthorityInput } from "./credential-authority-options.js";
|
|
2
3
|
import type { HealthPolicy } from "./health.js";
|
|
4
|
+
export type { ClaudeCliCredentialAuthority, CredentialAuthority, } from "./credential-authority-options.js";
|
|
3
5
|
export type CredentialRefreshMode = "auto" | "never";
|
|
4
6
|
export type CredentialManagement = "connector" | "external";
|
|
5
7
|
export type CredentialRole = "owner" | "reader";
|
|
@@ -11,27 +13,13 @@ export type CredentialRefreshPolicy = {
|
|
|
11
13
|
readonly mode: CredentialRefreshMode;
|
|
12
14
|
readonly leadMs: number;
|
|
13
15
|
};
|
|
14
|
-
export type ClaudeCliCredentialAuthority = {
|
|
15
|
-
readonly enabled: boolean;
|
|
16
|
-
readonly leadMs: number;
|
|
17
|
-
readonly retryMs: number;
|
|
18
|
-
};
|
|
19
|
-
export type CredentialAuthority = {
|
|
20
|
-
readonly claudeCli: ClaudeCliCredentialAuthority;
|
|
21
|
-
};
|
|
22
16
|
export type ConnectorOptionsInput = {
|
|
23
17
|
readonly providers?: readonly ("claude" | "cursor" | "command-code" | "ollama")[] | undefined;
|
|
24
18
|
readonly snapshotTimeoutMs?: number | undefined;
|
|
25
19
|
readonly credentialRole?: CredentialRole | undefined;
|
|
26
20
|
readonly xaiOAuth?: XaiOAuthOptions | undefined;
|
|
27
21
|
readonly credentialManagement?: CredentialManagement | undefined;
|
|
28
|
-
readonly credentialAuthority?:
|
|
29
|
-
readonly claudeCli: {
|
|
30
|
-
readonly enabled: boolean;
|
|
31
|
-
readonly leadMs?: number | undefined;
|
|
32
|
-
readonly retryMs?: number | undefined;
|
|
33
|
-
};
|
|
34
|
-
} | undefined;
|
|
22
|
+
readonly credentialAuthority?: CredentialAuthorityInput | undefined;
|
|
35
23
|
/** @deprecated Use credentialManagement instead. */
|
|
36
24
|
readonly writeBackCredentials?: boolean | undefined;
|
|
37
25
|
/** @deprecated Use credentialManagement instead. */
|
package/dist/core/options.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { CredentialAuthorityInputSchema, resolveCredentialAuthority, } from "./credential-authority-options.js";
|
|
2
3
|
const MaximumTimerMs = 2_147_483_647;
|
|
3
4
|
const PositiveSafeIntegerSchema = z.number().int().positive().max(MaximumTimerMs);
|
|
4
5
|
const NonNegativeSafeIntegerSchema = z.number().int().nonnegative().max(MaximumTimerMs);
|
|
@@ -10,26 +11,12 @@ const XaiOAuthSchema = z
|
|
|
10
11
|
.object({ mode: z.enum(["authority", "consumer"]) })
|
|
11
12
|
.strict()
|
|
12
13
|
.readonly();
|
|
13
|
-
const SafeIntegerSchema = z.number().int().safe();
|
|
14
14
|
const DefaultProviders = [
|
|
15
15
|
"claude",
|
|
16
16
|
"cursor",
|
|
17
17
|
"command-code",
|
|
18
18
|
"ollama",
|
|
19
19
|
];
|
|
20
|
-
const CredentialAuthorityInputSchema = z
|
|
21
|
-
.object({
|
|
22
|
-
claudeCli: z
|
|
23
|
-
.object({
|
|
24
|
-
enabled: z.boolean(),
|
|
25
|
-
leadMs: SafeIntegerSchema.nonnegative().optional(),
|
|
26
|
-
retryMs: SafeIntegerSchema.positive().optional(),
|
|
27
|
-
})
|
|
28
|
-
.strict()
|
|
29
|
-
.readonly(),
|
|
30
|
-
})
|
|
31
|
-
.strict()
|
|
32
|
-
.readonly();
|
|
33
20
|
function resolveCredentialOptions(input) {
|
|
34
21
|
const credentialManagement = input.credentialRole === undefined ? input.credentialManagement : "external";
|
|
35
22
|
switch (credentialManagement) {
|
|
@@ -99,7 +86,7 @@ const ConnectorOptionsInputSchema = z
|
|
|
99
86
|
message: "`credentialManagement` cannot be combined with deprecated `credentialRefresh` or `writeBackCredentials`",
|
|
100
87
|
});
|
|
101
88
|
}
|
|
102
|
-
if (input.credentialAuthority?.claudeCli
|
|
89
|
+
if (input.credentialAuthority?.claudeCli?.enabled === true &&
|
|
103
90
|
(input.credentialManagement !== "external" ||
|
|
104
91
|
!(input.providers ?? DefaultProviders).includes("claude"))) {
|
|
105
92
|
context.addIssue({
|
|
@@ -128,15 +115,7 @@ export const ConnectorOptionsSchema = ConnectorOptionsInputSchema.transform((inp
|
|
|
128
115
|
maximumBackoffMs: input.health?.maximumBackoffMs ?? 60_000,
|
|
129
116
|
});
|
|
130
117
|
const credentialOptions = resolveCredentialOptions(input);
|
|
131
|
-
const credentialAuthority =
|
|
132
|
-
claudeCli: Object.freeze({
|
|
133
|
-
enabled: input.credentialRole === "owner"
|
|
134
|
-
? true
|
|
135
|
-
: (input.credentialAuthority?.claudeCli.enabled ?? false),
|
|
136
|
-
leadMs: input.credentialAuthority?.claudeCli.leadMs ?? 300_000,
|
|
137
|
-
retryMs: input.credentialAuthority?.claudeCli.retryMs ?? 300_000,
|
|
138
|
-
}),
|
|
139
|
-
});
|
|
118
|
+
const credentialAuthority = resolveCredentialAuthority(input.credentialAuthority, input.credentialRole === "owner");
|
|
140
119
|
const xaiOAuth = input.xaiOAuth === undefined ? null : Object.freeze(input.xaiOAuth);
|
|
141
120
|
return Object.freeze({
|
|
142
121
|
providers: Object.freeze(input.providers ?? DefaultProviders),
|
|
@@ -8,6 +8,7 @@ const LOCK_FILE = "authority.lock";
|
|
|
8
8
|
const LOCK_CONFLICT_EXIT_CODE = 75;
|
|
9
9
|
const MAXIMUM_DELAY_MS = 2_147_483_647;
|
|
10
10
|
const AUTHORITY_PROMPT = "Reply OK without using tools.";
|
|
11
|
+
const ANTHROPIC_API_KEY = "ANTHROPIC_API_KEY";
|
|
11
12
|
function authorityStateDirectory(env) {
|
|
12
13
|
const configuredState = env.XDG_STATE_HOME;
|
|
13
14
|
if (configuredState !== undefined && configuredState.length > 0 && isAbsolute(configuredState)) {
|
|
@@ -24,6 +25,15 @@ async function createStateDirectory(path) {
|
|
|
24
25
|
function assertNeverProcessExit(exit) {
|
|
25
26
|
throw new InvalidArgumentError("processExit", exit);
|
|
26
27
|
}
|
|
28
|
+
function authorityChildEnvironment(env) {
|
|
29
|
+
const child = {};
|
|
30
|
+
for (const [key, value] of Object.entries(env)) {
|
|
31
|
+
if (key === ANTHROPIC_API_KEY || value === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
child[key] = value;
|
|
34
|
+
}
|
|
35
|
+
return Object.freeze(child);
|
|
36
|
+
}
|
|
27
37
|
export function createClaudeCredentialAuthorityScheduler(options) {
|
|
28
38
|
if (!Number.isSafeInteger(options.leadMs) || options.leadMs < 0) {
|
|
29
39
|
throw new InvalidArgumentError("leadMs");
|
|
@@ -55,6 +65,8 @@ export function createClaudeCredentialAuthorityScheduler(options) {
|
|
|
55
65
|
if (stateDirectory === null)
|
|
56
66
|
return null;
|
|
57
67
|
await ensureStateDirectory(stateDirectory);
|
|
68
|
+
// Use the stored OAuth login even when the parent has an API key.
|
|
69
|
+
const environment = authorityChildEnvironment(options.env);
|
|
58
70
|
const process = await options.processSupervisor.start({
|
|
59
71
|
executable: "flock",
|
|
60
72
|
arguments: [
|
|
@@ -77,6 +89,7 @@ export function createClaudeCredentialAuthorityScheduler(options) {
|
|
|
77
89
|
"json",
|
|
78
90
|
],
|
|
79
91
|
cwd: stateDirectory,
|
|
92
|
+
environment,
|
|
80
93
|
}, controller.signal);
|
|
81
94
|
activeProcess = process;
|
|
82
95
|
try {
|
package/dist/server.js
CHANGED
|
@@ -79,7 +79,7 @@ export const connectorServer = async (input, options) => {
|
|
|
79
79
|
})(input, options);
|
|
80
80
|
const processSupervisor = createProductionProcessSupervisor();
|
|
81
81
|
const claudeCliAuthority = connectorOptions.credentialAuthority.claudeCli;
|
|
82
|
-
const
|
|
82
|
+
const claudeCredentialAuthority = createClaudeCredentialAuthorityScheduler({
|
|
83
83
|
enabled: claudeCliAuthority.enabled,
|
|
84
84
|
clock,
|
|
85
85
|
leadMs: claudeCliAuthority.leadMs,
|
|
@@ -97,7 +97,7 @@ export const connectorServer = async (input, options) => {
|
|
|
97
97
|
const dispose = hooks.dispose;
|
|
98
98
|
const disposal = createAsyncDisposable(async () => {
|
|
99
99
|
const results = await Promise.allSettled([
|
|
100
|
-
Promise.resolve().then(() =>
|
|
100
|
+
Promise.resolve().then(() => claudeCredentialAuthority.dispose()),
|
|
101
101
|
Promise.resolve().then(() => xaiAuthority.dispose()),
|
|
102
102
|
Promise.resolve().then(() => processSupervisor.dispose()),
|
|
103
103
|
Promise.resolve().then(() => dispose?.()),
|
package/docs/README.ko.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
## 상태
|
|
21
21
|
|
|
22
|
-
> 독립적인 비공식 커뮤니티 플러그인, 버전 **0.7.
|
|
22
|
+
> 독립적인 비공식 커뮤니티 플러그인, 버전 **0.7.1**. 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.7.
|
|
58
|
+
"plugin": ["opencode-ext-connector@0.7.1"]
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
@@ -109,7 +109,7 @@ OpenCode는 플러그인 옵션을 두 요소 튜플의 두 번째 항목으로
|
|
|
109
109
|
|
|
110
110
|
`"owner"`는 내부적으로 external 자격 증명 관리를 선택하고 기본 타이밍으로 Claude CLI 권한을 활성화합니다. `"reader"`는 CLI 권한을 시작하지 않고 external 자격 증명 관리를 선택합니다. 이 역할은 자격 증명 소유권을 뜻하며 OpenCode가 물리 호스트에서 실행되는지, 샌드박스 게스트에서 실행되는지를 뜻하지 않습니다.
|
|
111
111
|
|
|
112
|
-
공유 Claude 로그인마다 owner를 정확히 하나만 설정하십시오. Owner 모드에는 Linux, util-linux `flock`, `PATH`의 Claude Code `2.1.
|
|
112
|
+
공유 Claude 로그인마다 owner를 정확히 하나만 설정하십시오. Owner 모드에는 Linux, util-linux `flock`, `PATH`의 Claude Code `2.1.265` 이상, 인증된 Claude Code 세션이 필요합니다. 각 권한 호출은 실제 모델 요청이며 계정 사용량을 소비할 수 있습니다. 폐기된 로그인은 여전히 대화형 `/login`이 필요합니다.
|
|
113
113
|
|
|
114
114
|
`credentialRole`을 `credentialManagement`, `credentialAuthority`, `credentialRefresh`, `writeBackCredentials`와 함께 사용하지 마십시오. 기존 저수준 설정은 고급 제어용으로 계속 지원됩니다.
|
|
115
115
|
|
|
@@ -204,7 +204,7 @@ Anthropic은 갱신할 때마다 refresh 토큰을 회전시키고 이전 토큰
|
|
|
204
204
|
}
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
설정을 저장한 뒤 OpenCode를 완전히 재시작하십시오. 타이머는 Linux, `PATH`의 util-linux `flock`, Claude Code `2.1.
|
|
207
|
+
설정을 저장한 뒤 OpenCode를 완전히 재시작하십시오. 타이머는 Linux, `PATH`의 util-linux `flock`, Claude Code `2.1.265` 이상, 인증된 Claude Code 세션, writable persistent 상태 디렉터리가 필요합니다. 다른 플랫폼에서는 조용히 비활성화됩니다.
|
|
208
208
|
|
|
209
209
|
`leadMs`와 `retryMs`의 기본값은 모두 `300000`(5분)입니다. 커넥터는 lead 경계에서 restricted single-turn Claude 요청 하나를 예약합니다. 프로세스 공유 non-blocking lock이 같은 상태 디렉터리를 사용하는 프로세스의 동시 요청을 막고, 실패하면 Claude 프로바이더를 제거하지 않은 채 `retryMs` 후 재시도합니다.
|
|
210
210
|
|