opencode-translate 0.2.3 → 0.3.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/README.md +7 -5
- package/package.json +1 -1
- package/src/activation/parts.ts +1 -1
- package/src/constants/options.ts +15 -5
- package/src/constants/plugin.ts +0 -1
- package/src/constants/types.ts +1 -1
- package/src/translator/index.ts +3 -3
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ Add it to `~/.config/opencode/opencode.json`:
|
|
|
58
58
|
{
|
|
59
59
|
"plugin": [
|
|
60
60
|
["opencode-translate", {
|
|
61
|
-
"
|
|
61
|
+
"model": "anthropic/claude-haiku-4-5",
|
|
62
62
|
"triggerKeywords": ["$en"],
|
|
63
63
|
"lang": "Korean",
|
|
64
64
|
"verbose": false
|
|
@@ -84,12 +84,14 @@ $en 프로젝트 루트의 package.json을 읽고 요약해줘
|
|
|
84
84
|
|
|
85
85
|
| Option | Type | Default |
|
|
86
86
|
| --- | --- | --- |
|
|
87
|
-
| `
|
|
87
|
+
| `model` | string | Required |
|
|
88
88
|
| `triggerKeywords` | string[] | `[$en]` |
|
|
89
89
|
| `lang` | string | Required |
|
|
90
90
|
| `apiKey` | string | `undefined` |
|
|
91
91
|
| `verbose` | boolean | `false` |
|
|
92
92
|
|
|
93
|
+
Set `model` to the translator model in `provider/model-id` form, such as `"anthropic/claude-haiku-4-5"`.
|
|
94
|
+
|
|
93
95
|
Set `lang` to the full name of the language the user reads and writes, such as `"Korean"`, `"Japanese"`, or `"Brazilian Portuguese"`. The value is injected directly into translation prompts, so language code mapping is not required.
|
|
94
96
|
|
|
95
97
|
## Privacy
|
|
@@ -97,7 +99,7 @@ Set `lang` to the full name of the language the user reads and writes, such as `
|
|
|
97
99
|
Using this plugin means text goes to two model providers per turn:
|
|
98
100
|
|
|
99
101
|
- the normal OpenCode chat provider
|
|
100
|
-
- the configured `
|
|
102
|
+
- the configured `model` provider
|
|
101
103
|
|
|
102
104
|
If you need strict single-provider or self-hosted-only behavior, do not enable this plugin.
|
|
103
105
|
|
|
@@ -109,7 +111,7 @@ For OAuth records that OpenCode does not expose through the plugin SDK, the plug
|
|
|
109
111
|
|
|
110
112
|
## Anthropic OAuth Support
|
|
111
113
|
|
|
112
|
-
If `
|
|
114
|
+
If `model` uses Anthropic and OpenCode auth is backed by Anthropic OAuth (Claude Pro/Max), the plugin reuses those OAuth credentials for translation requests.
|
|
113
115
|
|
|
114
116
|
Anthropic's `/v1/messages` endpoint rejects OAuth-authenticated requests that do not match the Claude Code CLI fingerprint (response: `429 rate_limit_error` with an empty `"Error"` message). To pass, the plugin applies the same transformation that `@ex-machina/opencode-anthropic-auth` uses for OpenCode's main chat, but only for its own translator requests:
|
|
115
117
|
|
|
@@ -131,7 +133,7 @@ If you prefer a plain API key, set `ANTHROPIC_API_KEY`, use `opencode auth login
|
|
|
131
133
|
|
|
132
134
|
## OpenAI OAuth Support
|
|
133
135
|
|
|
134
|
-
If `
|
|
136
|
+
If `model` uses OpenAI and OpenCode auth is backed by the ChatGPT/Codex OAuth flow, the plugin reuses those OAuth credentials for translation requests.
|
|
135
137
|
|
|
136
138
|
For OAuth-backed OpenAI requests, the plugin routes the OpenAI AI SDK request to `https://chatgpt.com/backend-api/codex/responses`, adds the required Codex beta/originator headers, normalizes the request body to Codex's expected typed `input` shape, and converts Codex SSE responses back to JSON for translation calls. This supports models such as `openai/gpt-5.5` when your ChatGPT plan has access.
|
|
137
139
|
|
package/package.json
CHANGED
package/src/activation/parts.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { createSyntheticPartID } from "../translator"
|
|
9
9
|
|
|
10
10
|
export function createActivationBannerText(options: ResolvedTranslateOptions): string {
|
|
11
|
-
const { modelID } = parseTranslatorModel(options.
|
|
11
|
+
const { modelID } = parseTranslatorModel(options.model)
|
|
12
12
|
return `✓ Translation mode enabled · translator: ${modelID} · language: ${options.lang}`
|
|
13
13
|
}
|
|
14
14
|
|
package/src/constants/options.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import { AUTH_ENV_FALLBACK,
|
|
1
|
+
import { AUTH_ENV_FALLBACK, DEFAULT_TRIGGER_KEYWORDS, PLUGIN_NAME } from "./plugin"
|
|
2
2
|
import type { ProviderInfo, ResolvedTranslateOptions } from "./types"
|
|
3
3
|
|
|
4
4
|
export function resolveOptions(options: Record<string, unknown>): ResolvedTranslateOptions {
|
|
5
|
+
const model = typeof options.model === "string" ? options.model.trim() : ""
|
|
6
|
+
if (!model) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`[${PLUGIN_NAME}:INVALID_OPTIONS] options.model is required. Set it to the translator model, e.g. "anthropic/claude-haiku-4-5".`,
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
const slash = model.indexOf("/")
|
|
12
|
+
if (slash < 1 || slash === model.length - 1) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`[${PLUGIN_NAME}:INVALID_OPTIONS] options.model must be in provider/model-id form, e.g. "anthropic/claude-haiku-4-5".`,
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
const lang = typeof options.lang === "string" ? options.lang.trim() : ""
|
|
6
19
|
if (!lang) {
|
|
7
20
|
throw new Error(
|
|
@@ -14,10 +27,7 @@ export function resolveOptions(options: Record<string, unknown>): ResolvedTransl
|
|
|
14
27
|
: DEFAULT_TRIGGER_KEYWORDS
|
|
15
28
|
|
|
16
29
|
return {
|
|
17
|
-
|
|
18
|
-
typeof options.translatorModel === "string" && options.translatorModel.includes("/")
|
|
19
|
-
? options.translatorModel
|
|
20
|
-
: DEFAULT_TRANSLATOR_MODEL,
|
|
30
|
+
model,
|
|
21
31
|
triggerKeywords: triggerKeywords.length > 0 ? triggerKeywords : [...DEFAULT_TRIGGER_KEYWORDS],
|
|
22
32
|
lang,
|
|
23
33
|
apiKey: typeof options.apiKey === "string" && options.apiKey.length > 0 ? options.apiKey : undefined,
|
package/src/constants/plugin.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export const PLUGIN_NAME = "opencode-translate"
|
|
2
2
|
export const SPEC_VERSION = 2
|
|
3
3
|
export const LLM_LANGUAGE = "English"
|
|
4
|
-
export const DEFAULT_TRANSLATOR_MODEL = "anthropic/claude-haiku-4-5"
|
|
5
4
|
export const DEFAULT_TRIGGER_KEYWORDS = ["$en"]
|
|
6
5
|
export const OAUTH_DUMMY_KEY = "opencode-oauth-dummy-key"
|
|
7
6
|
export const NONCE_PATTERN = /^[0-9a-f]{32}$/
|
package/src/constants/types.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promis
|
|
|
5
5
|
type ProviderSource = "env" | "config" | "custom" | "api"
|
|
6
6
|
|
|
7
7
|
export interface ResolvedTranslateOptions {
|
|
8
|
-
|
|
8
|
+
model: string
|
|
9
9
|
triggerKeywords: string[]
|
|
10
10
|
lang: string
|
|
11
11
|
apiKey?: string
|
package/src/translator/index.ts
CHANGED
|
@@ -70,8 +70,8 @@ export function createTranslator(
|
|
|
70
70
|
if (input.sourceLanguage === input.targetLanguage) return input.text
|
|
71
71
|
|
|
72
72
|
const startedAt = now()
|
|
73
|
-
const { providerID, modelID } = parseTranslatorModel(options.
|
|
74
|
-
const credentials = await credentialResolver.resolve(options.
|
|
73
|
+
const { providerID, modelID } = parseTranslatorModel(options.model)
|
|
74
|
+
const credentials = await credentialResolver.resolve(options.model)
|
|
75
75
|
const modelInfo = resolveModelInfo(credentials.provider, modelID)
|
|
76
76
|
const factory = await loadFactory(providerID, modelInfo)
|
|
77
77
|
const provider = instantiateProvider(factory, providerID, credentials, modelInfo)
|
|
@@ -113,7 +113,7 @@ export function createTranslator(
|
|
|
113
113
|
chars_out: translated.length,
|
|
114
114
|
ms: now() - startedAt,
|
|
115
115
|
cached: false,
|
|
116
|
-
model: options.
|
|
116
|
+
model: options.model,
|
|
117
117
|
},
|
|
118
118
|
},
|
|
119
119
|
})
|