opencode-input-translator 0.1.0 → 0.1.2

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/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{createOpenAICompatible as e}from"@ai-sdk/openai-compatible";import{Output as t,generateText as n}from"ai";import{z as r}from"zod";function i(e){if(!e||e.trim().length===0)return!0;let t=e.replace(/```[\s\S]*?```/g,` `);t=t.replace(/`[^`]*`/g,` `);let n=0,r=0;for(let e of t){let t=e.codePointAt(0);t!==void 0&&(t>=65&&t<=90||t>=97&&t<=122||t>=192&&t<=591?n++:(t>=19968&&t<=40959||t>=44032&&t<=55215||t>=1024&&t<=1279||t>=1536&&t<=1791||t>=3584&&t<=3711||t>=2304&&t<=2431)&&r++)}let i=n+r;return i===0?!0:n/i>=.7}function a(e){let t=[],n=0,r=e;return r=r.replace(/```[\s\S]*?```/g,e=>{let r=`__CODE_BLOCK_${n++}__`;return t.push({placeholder:r,original:e}),r}),r=r.replace(/`[^`]+`/g,e=>{let r=`__CODE_BLOCK_${n++}__`;return t.push({placeholder:r,original:e}),r}),{prose:r,blocks:t}}function o(e,t){let n=e;for(let e of t)n=n.replace(e.placeholder,e.original);return n}const s=r.object({translation:r.string().describe(`The English translation of the input text`)});async function c(r,i){try{let{output:a}=await n({model:e({name:`translator`,baseURL:`${i.baseUrl}/v1`,apiKey:i.apiKey,supportsStructuredOutputs:!0})(i.model),output:t.object({schema:s}),system:`Translate the following text to English. If the text is already in English, output it unchanged (as is).`,prompt:r,maxRetries:3,abortSignal:AbortSignal.timeout(10*1e3)});if(!a)throw Error(`No content in response`);return{translated:!0,text:a.translation}}catch(e){return console.warn(`[translateToEnglish] error:`,e),{translated:!1,text:r}}}const l=async({client:e})=>{let t=process.env.TRANSLATOR_API_KEY,n=process.env.TRANSLATOR_BASE_URL,r=process.env.TRANSLATOR_MODEL??`gpt-5-nano-2025-08-07`;if(!t||!n)return await e.app.log({body:{service:`input-translator`,level:`warn`,message:`Missing TRANSLATOR_API_KEY or TRANSLATOR_BASE_URL. Input Translator Plugin is disabled.`}}),{};let s={apiKey:t,baseUrl:n,model:r};return{"experimental.chat.messages.transform":async(e,t)=>{for(let e of t.messages)if(e.info.role===`user`)for(let t of e.parts){if(t.type!==`text`||t.text.trim().length===0||t.synthetic===!0||t.ignored===!0||t.metadata?.__translated===!0||i(t.text))continue;let{prose:e,blocks:n}=a(t.text);if(e.trim().length===0)continue;let r=await c(e,s);r.translated&&(t.text=o(r.text,n),t.metadata={...t.metadata,__translated:!0})}}}};export{l as InputTranslatorPlugin,l as default};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/detector.ts","../src/extractor.ts","../src/translator.ts","../src/index.ts"],"sourcesContent":["export function isEnglish(text: string): boolean {\n if (!text || text.trim().length === 0) return true;\n\n let prose = text.replace(/```[\\s\\S]*?```/g, ' ');\n prose = prose.replace(/`[^`]*`/g, ' ');\n\n let latinCount = 0;\n let nonLatinCount = 0;\n\n for (const ch of prose) {\n const cp = ch.codePointAt(0);\n if (cp === undefined) continue;\n\n if (\n (cp >= 0x0041 && cp <= 0x005a) || // A-Z\n (cp >= 0x0061 && cp <= 0x007a) || // a-z\n (cp >= 0x00c0 && cp <= 0x024f) // Latin Extended\n ) {\n latinCount++;\n } else if (\n (cp >= 0x4e00 && cp <= 0x9fff) || // CJK\n (cp >= 0xac00 && cp <= 0xd7af) || // Hangul\n (cp >= 0x0400 && cp <= 0x04ff) || // Cyrillic\n (cp >= 0x0600 && cp <= 0x06ff) || // Arabic\n (cp >= 0x0e00 && cp <= 0x0e7f) || // Thai\n (cp >= 0x0900 && cp <= 0x097f) // Devanagari\n ) {\n nonLatinCount++;\n }\n }\n\n const total = latinCount + nonLatinCount;\n if (total === 0) return true;\n\n return latinCount / total >= 0.7;\n}\n","import type { CodeBlock } from './types.ts';\n\nexport function extractCodeBlocks(text: string): {\n prose: string;\n blocks: CodeBlock[];\n} {\n const blocks: CodeBlock[] = [];\n let index = 0;\n let prose = text;\n\n // 1. Extract fenced code blocks (``` ... ```) first\n prose = prose.replace(/```[\\s\\S]*?```/g, (match) => {\n const placeholder = `__CODE_BLOCK_${index++}__`;\n blocks.push({ placeholder, original: match });\n return placeholder;\n });\n\n // 2. Extract inline code (` ... `)\n prose = prose.replace(/`[^`]+`/g, (match) => {\n const placeholder = `__CODE_BLOCK_${index++}__`;\n blocks.push({ placeholder, original: match });\n return placeholder;\n });\n\n return { prose, blocks };\n}\n\nexport function restoreCodeBlocks(text: string, blocks: CodeBlock[]): string {\n let result = text;\n for (const block of blocks) {\n result = result.replace(block.placeholder, block.original);\n }\n return result;\n}\n","import { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport type { TranslationResult, TranslatorConfig } from './types.ts';\n\nconst translationSchema = z.object({\n translation: z.string().describe('The English translation of the input text'),\n});\n\nexport async function translateToEnglish(\n text: string,\n config: TranslatorConfig,\n): Promise<TranslationResult> {\n try {\n const provider = createOpenAICompatible({\n name: 'translator',\n baseURL: `${config.baseUrl}/v1`,\n apiKey: config.apiKey,\n supportsStructuredOutputs: true,\n });\n\n const { output: result } = await generateText({\n model: provider(config.model),\n output: Output.object({\n schema: translationSchema,\n }),\n system:\n 'Translate the following text to English. If the text is already in English, output it unchanged (as is).',\n prompt: text,\n maxRetries: 3,\n abortSignal: AbortSignal.timeout(10 * 1000),\n });\n\n if (!result) throw new Error('No content in response');\n\n return { translated: true, text: result.translation };\n } catch (err) {\n console.warn('[translateToEnglish] error:', err);\n return { translated: false, text };\n }\n}\n","import type { Plugin } from '@opencode-ai/plugin';\nimport { isEnglish } from './detector.ts';\nimport { extractCodeBlocks, restoreCodeBlocks } from './extractor.ts';\nimport { translateToEnglish } from './translator.ts';\nimport type { TranslatorConfig } from './types.ts';\n\nexport const InputTranslatorPlugin: Plugin = async ({ client }) => {\n const apiKey = process.env.TRANSLATOR_API_KEY;\n const baseUrl = process.env.TRANSLATOR_BASE_URL;\n const model = process.env.TRANSLATOR_MODEL ?? 'gpt-5-nano-2025-08-07';\n\n if (!apiKey || !baseUrl) {\n await client.app.log({\n body: {\n service: 'input-translator',\n level: 'warn',\n message:\n 'Missing TRANSLATOR_API_KEY or TRANSLATOR_BASE_URL. Input Translator Plugin is disabled.',\n },\n });\n return {};\n }\n\n const config: TranslatorConfig = { apiKey, baseUrl, model };\n\n return {\n 'experimental.chat.messages.transform': async (_input, output) => {\n for (const message of output.messages) {\n if (message.info.role !== 'user') continue;\n\n for (const part of message.parts) {\n if (part.type !== 'text') continue;\n if (part.text.trim().length === 0) continue;\n if (part.synthetic === true || part.ignored === true) continue;\n if (part.metadata?.__translated === true) continue;\n if (isEnglish(part.text)) continue;\n\n const { prose, blocks } = extractCodeBlocks(part.text);\n if (prose.trim().length === 0) continue;\n\n const result = await translateToEnglish(prose, config);\n if (result.translated) {\n part.text = restoreCodeBlocks(result.text, blocks);\n part.metadata = { ...part.metadata, __translated: true };\n }\n }\n }\n },\n };\n};\n\nexport default InputTranslatorPlugin;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "opencode-input-translator",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "OpenCode plugin that translates non-English user input to English",
5
5
  "type": "module",
6
- "types": "dist/index.d.ts",
7
6
  "exports": {
8
- ".": "./dist/index.mjs",
7
+ ".": "./dist/index.js",
9
8
  "./package.json": "./package.json"
10
9
  },
11
10
  "files": [
@@ -16,7 +15,7 @@
16
15
  "check:fix": "biome check --fix",
17
16
  "typecheck": "tsc --noEmit",
18
17
  "build": "bun run check && tsdown",
19
- "local-deploy": "bun run build && cp dist/index.mjs ~/.opencode/plugins/opencode-input-translator.mjs",
18
+ "local-deploy": "bun run build && cp dist/index.js .opencode/plugins/opencode-input-translator.js",
20
19
  "prepublishOnly": "bun run build"
21
20
  },
22
21
  "repository": {
package/dist/index.d.mts DELETED
@@ -1,7 +0,0 @@
1
- import { Plugin } from "@opencode-ai/plugin";
2
-
3
- //#region src/index.d.ts
4
- declare const InputTranslatorPlugin: Plugin;
5
- //#endregion
6
- export { InputTranslatorPlugin, InputTranslatorPlugin as default };
7
- //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs DELETED
@@ -1,133 +0,0 @@
1
- import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
2
- import { Output, generateText } from "ai";
3
- import { z } from "zod";
4
-
5
- //#region src/detector.ts
6
- function isEnglish(text) {
7
- if (!text || text.trim().length === 0) return true;
8
- let prose = text.replace(/```[\s\S]*?```/g, " ");
9
- prose = prose.replace(/`[^`]*`/g, " ");
10
- let latinCount = 0;
11
- let nonLatinCount = 0;
12
- for (const ch of prose) {
13
- const cp = ch.codePointAt(0);
14
- if (cp === void 0) continue;
15
- if (cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 192 && cp <= 591) latinCount++;
16
- else if (cp >= 19968 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 1024 && cp <= 1279 || cp >= 1536 && cp <= 1791 || cp >= 3584 && cp <= 3711 || cp >= 2304 && cp <= 2431) nonLatinCount++;
17
- }
18
- const total = latinCount + nonLatinCount;
19
- if (total === 0) return true;
20
- return latinCount / total >= .7;
21
- }
22
-
23
- //#endregion
24
- //#region src/extractor.ts
25
- function extractCodeBlocks(text) {
26
- const blocks = [];
27
- let index = 0;
28
- let prose = text;
29
- prose = prose.replace(/```[\s\S]*?```/g, (match) => {
30
- const placeholder = `__CODE_BLOCK_${index++}__`;
31
- blocks.push({
32
- placeholder,
33
- original: match
34
- });
35
- return placeholder;
36
- });
37
- prose = prose.replace(/`[^`]+`/g, (match) => {
38
- const placeholder = `__CODE_BLOCK_${index++}__`;
39
- blocks.push({
40
- placeholder,
41
- original: match
42
- });
43
- return placeholder;
44
- });
45
- return {
46
- prose,
47
- blocks
48
- };
49
- }
50
- function restoreCodeBlocks(text, blocks) {
51
- let result = text;
52
- for (const block of blocks) result = result.replace(block.placeholder, block.original);
53
- return result;
54
- }
55
-
56
- //#endregion
57
- //#region src/translator.ts
58
- const translationSchema = z.object({ translation: z.string().describe("The English translation of the input text") });
59
- async function translateToEnglish(text, config) {
60
- try {
61
- const { output: result } = await generateText({
62
- model: createOpenAICompatible({
63
- name: "translator",
64
- baseURL: `${config.baseUrl}/v1`,
65
- apiKey: config.apiKey,
66
- supportsStructuredOutputs: true
67
- })(config.model),
68
- output: Output.object({ schema: translationSchema }),
69
- system: "Translate the following text to English. If the text is already in English, output it unchanged (as is).",
70
- prompt: text,
71
- maxRetries: 3,
72
- abortSignal: AbortSignal.timeout(10 * 1e3)
73
- });
74
- if (!result) throw new Error("No content in response");
75
- return {
76
- translated: true,
77
- text: result.translation
78
- };
79
- } catch (err) {
80
- console.warn("[translateToEnglish] error:", err);
81
- return {
82
- translated: false,
83
- text
84
- };
85
- }
86
- }
87
-
88
- //#endregion
89
- //#region src/index.ts
90
- const InputTranslatorPlugin = async ({ client }) => {
91
- const apiKey = process.env.TRANSLATOR_API_KEY;
92
- const baseUrl = process.env.TRANSLATOR_BASE_URL;
93
- const model = process.env.TRANSLATOR_MODEL ?? "gpt-5-nano-2025-08-07";
94
- if (!apiKey || !baseUrl) {
95
- await client.app.log({ body: {
96
- service: "input-translator",
97
- level: "warn",
98
- message: "Missing TRANSLATOR_API_KEY or TRANSLATOR_BASE_URL. Input Translator Plugin is disabled."
99
- } });
100
- return {};
101
- }
102
- const config = {
103
- apiKey,
104
- baseUrl,
105
- model
106
- };
107
- return { "experimental.chat.messages.transform": async (_input, output) => {
108
- for (const message of output.messages) {
109
- if (message.info.role !== "user") continue;
110
- for (const part of message.parts) {
111
- if (part.type !== "text") continue;
112
- if (part.text.trim().length === 0) continue;
113
- if (part.synthetic === true || part.ignored === true) continue;
114
- if (part.metadata?.__translated === true) continue;
115
- if (isEnglish(part.text)) continue;
116
- const { prose, blocks } = extractCodeBlocks(part.text);
117
- if (prose.trim().length === 0) continue;
118
- const result = await translateToEnglish(prose, config);
119
- if (result.translated) {
120
- part.text = restoreCodeBlocks(result.text, blocks);
121
- part.metadata = {
122
- ...part.metadata,
123
- __translated: true
124
- };
125
- }
126
- }
127
- }
128
- } };
129
- };
130
-
131
- //#endregion
132
- export { InputTranslatorPlugin, InputTranslatorPlugin as default };
133
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/detector.ts","../src/extractor.ts","../src/translator.ts","../src/index.ts"],"sourcesContent":["export function isEnglish(text: string): boolean {\n if (!text || text.trim().length === 0) return true;\n\n let prose = text.replace(/```[\\s\\S]*?```/g, ' ');\n prose = prose.replace(/`[^`]*`/g, ' ');\n\n let latinCount = 0;\n let nonLatinCount = 0;\n\n for (const ch of prose) {\n const cp = ch.codePointAt(0);\n if (cp === undefined) continue;\n\n if (\n (cp >= 0x0041 && cp <= 0x005a) || // A-Z\n (cp >= 0x0061 && cp <= 0x007a) || // a-z\n (cp >= 0x00c0 && cp <= 0x024f) // Latin Extended\n ) {\n latinCount++;\n } else if (\n (cp >= 0x4e00 && cp <= 0x9fff) || // CJK\n (cp >= 0xac00 && cp <= 0xd7af) || // Hangul\n (cp >= 0x0400 && cp <= 0x04ff) || // Cyrillic\n (cp >= 0x0600 && cp <= 0x06ff) || // Arabic\n (cp >= 0x0e00 && cp <= 0x0e7f) || // Thai\n (cp >= 0x0900 && cp <= 0x097f) // Devanagari\n ) {\n nonLatinCount++;\n }\n }\n\n const total = latinCount + nonLatinCount;\n if (total === 0) return true;\n\n return latinCount / total >= 0.7;\n}\n","import type { CodeBlock } from './types.ts';\n\nexport function extractCodeBlocks(text: string): {\n prose: string;\n blocks: CodeBlock[];\n} {\n const blocks: CodeBlock[] = [];\n let index = 0;\n let prose = text;\n\n // 1. Extract fenced code blocks (``` ... ```) first\n prose = prose.replace(/```[\\s\\S]*?```/g, (match) => {\n const placeholder = `__CODE_BLOCK_${index++}__`;\n blocks.push({ placeholder, original: match });\n return placeholder;\n });\n\n // 2. Extract inline code (` ... `)\n prose = prose.replace(/`[^`]+`/g, (match) => {\n const placeholder = `__CODE_BLOCK_${index++}__`;\n blocks.push({ placeholder, original: match });\n return placeholder;\n });\n\n return { prose, blocks };\n}\n\nexport function restoreCodeBlocks(text: string, blocks: CodeBlock[]): string {\n let result = text;\n for (const block of blocks) {\n result = result.replace(block.placeholder, block.original);\n }\n return result;\n}\n","import { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport type { TranslationResult, TranslatorConfig } from './types.ts';\n\nconst translationSchema = z.object({\n translation: z.string().describe('The English translation of the input text'),\n});\n\nexport async function translateToEnglish(\n text: string,\n config: TranslatorConfig,\n): Promise<TranslationResult> {\n try {\n const provider = createOpenAICompatible({\n name: 'translator',\n baseURL: `${config.baseUrl}/v1`,\n apiKey: config.apiKey,\n supportsStructuredOutputs: true,\n });\n\n const { output: result } = await generateText({\n model: provider(config.model),\n output: Output.object({\n schema: translationSchema,\n }),\n system:\n 'Translate the following text to English. If the text is already in English, output it unchanged (as is).',\n prompt: text,\n maxRetries: 3,\n abortSignal: AbortSignal.timeout(10 * 1000),\n });\n\n if (!result) throw new Error('No content in response');\n\n return { translated: true, text: result.translation };\n } catch (err) {\n console.warn('[translateToEnglish] error:', err);\n return { translated: false, text };\n }\n}\n","import type { Plugin } from '@opencode-ai/plugin';\nimport { isEnglish } from './detector.ts';\nimport { extractCodeBlocks, restoreCodeBlocks } from './extractor.ts';\nimport { translateToEnglish } from './translator.ts';\nimport type { TranslatorConfig } from './types.ts';\n\nexport const InputTranslatorPlugin: Plugin = async ({ client }) => {\n const apiKey = process.env.TRANSLATOR_API_KEY;\n const baseUrl = process.env.TRANSLATOR_BASE_URL;\n const model = process.env.TRANSLATOR_MODEL ?? 'gpt-5-nano-2025-08-07';\n\n if (!apiKey || !baseUrl) {\n await client.app.log({\n body: {\n service: 'input-translator',\n level: 'warn',\n message:\n 'Missing TRANSLATOR_API_KEY or TRANSLATOR_BASE_URL. Input Translator Plugin is disabled.',\n },\n });\n return {};\n }\n\n const config: TranslatorConfig = { apiKey, baseUrl, model };\n\n return {\n 'experimental.chat.messages.transform': async (_input, output) => {\n for (const message of output.messages) {\n if (message.info.role !== 'user') continue;\n\n for (const part of message.parts) {\n if (part.type !== 'text') continue;\n if (part.text.trim().length === 0) continue;\n if (part.synthetic === true || part.ignored === true) continue;\n if (part.metadata?.__translated === true) continue;\n if (isEnglish(part.text)) continue;\n\n const { prose, blocks } = extractCodeBlocks(part.text);\n if (prose.trim().length === 0) continue;\n\n const result = await translateToEnglish(prose, config);\n if (result.translated) {\n part.text = restoreCodeBlocks(result.text, blocks);\n part.metadata = { ...part.metadata, __translated: true };\n }\n }\n }\n },\n };\n};\n\nexport default InputTranslatorPlugin;\n"],"mappings":"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"}