opencode-windows-encoding 4.1.0 → 5.0.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/dist/v1.js +1 -1
- package/dist/v1.js.map +1 -1
- package/dist/v2.js +1 -1
- package/dist/v2.js.map +1 -1
- package/package.json +1 -1
package/dist/v1.js
CHANGED
package/dist/v1.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/encoding-core.ts","../src/v2.ts","../src/v1.ts"],"sourcesContent":["/**\n * 共享核心 — UTF-8 编码前缀注入逻辑\n *\n * 由 V1(v1.ts)与 V2(v2.ts)入口共同复用;\n * 仅依赖 Node.js 内置模块,不引用任何插件包类型。\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"node:path\"\n\n// 调试日志(写入临时目录,默认关闭,设 OPENCODE_UTF8_DEBUG=1 开启)\nconst DEBUG = process.env.OPENCODE_UTF8_DEBUG === \"1\"\nconst LOG = join(tmpdir(), \"utf8-plugin.log\")\nexport function flog(msg: string) {\n if (!DEBUG) return\n try { appendFileSync(LOG, `[${new Date().toISOString()}] ${msg}\\n`, \"utf8\") } catch {}\n}\n\nexport type ShellKind = \"pwsh\" | \"bash\" | \"cmd\"\n\n// ── 各 shell 的 UTF-8 编码前缀与命令分隔符 ──\nexport const ENC: Record<ShellKind, { prefix: string; sep: string; marker: string }> = {\n pwsh: {\n prefix:\n \"[Console]::OutputEncoding=[Console]::InputEncoding=[Text.Encoding]::UTF8;\" +\n \"$OutputEncoding=[Text.Encoding]::UTF8;\" +\n \"$env:PYTHONIOENCODING='utf-8';\",\n sep: \"\\n\",\n marker: \"OutputEncoding\",\n },\n bash: {\n prefix: \"export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8;\",\n sep: \"\\n\",\n marker: \"LC_ALL\",\n },\n cmd: {\n prefix: \"chcp 65001 >nul\",\n sep: \" & \",\n marker: \"chcp\",\n },\n}\n\n/** 由 shell 路径/名称(可能含目录与 .exe 后缀)归一化为 shell 类型;无法识别返回 undefined(不注入) */\nexport function detectShellKind(shell: string | undefined): ShellKind | undefined {\n if (!shell) return undefined\n const base = shell\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .pop()!\n .toLowerCase()\n .replace(/\\.exe$/, \"\")\n if (base === \"pwsh\" || base === \"powershell\") return \"pwsh\"\n if (base === \"cmd\") return \"cmd\"\n return \"bash\" // bash / zsh / sh / dash / ksh 等 POSIX shell\n}\n\n/** 提取 opencode 在命令前追加的 set VAR=\"value\" && 前缀 */\nexport function stripSetPrefixes(cmd: string): { prefixes: string; cleanCmd: string } {\n const m = cmd.match(/^((?:set\\s+\\w+=\"[^\"]*\"\\s*&&\\s*)+)/)\n if (m) return { prefixes: m[1], cleanCmd: cmd.slice(m[1].length) }\n return { prefixes: \"\", cleanCmd: cmd }\n}\n\n/** 在命令前注入 UTF-8 编码前缀;已含 marker(幂等)时返回 undefined */\nexport function injectUtf8Prefix(cmd: string, kind: ShellKind): string | undefined {\n const { prefixes, cleanCmd } = stripSetPrefixes(cmd)\n flog(` orig: ${cleanCmd.slice(0, 120)}`)\n\n const { prefix, sep, marker } = ENC[kind]\n\n // 防止重复注入\n if (cleanCmd.includes(marker)) { flog(\" skip (idempotent)\"); return undefined }\n\n return prefixes + prefix + sep + cleanCmd\n}\n","/**\n * OpenCode V2 Plugin — UTF-8 Encoding Fix for Windows\n *\n * V2 契约({ id, setup })。setup 在含 shell hook 域的 V2 宿主上注册\n * shell create.before hook(事件契约对照 opencode dev 分支\n * packages/plugin/src/promise/shell.ts 核实);宿主缺少 shell 域\n * (如 opencode 1.18.x)时降级为 no-op——保证插件始终可加载、不报错,\n * 待宿主升级后自动生效。类型为自包含最小结构声明,仅依赖结构匹配。\n */\n\nimport { detectShellKind, flog, injectUtf8Prefix } from \"./encoding-core.js\"\n\n/** shell create.before 事件(对照 dev 分支 ShellCreateBefore) */\nexport interface ShellCreateBeforeEvent {\n command: string\n cwd: string\n timeout: number\n shell: string\n env: Record<string, string | undefined>\n}\nexport interface HookRegistration { dispose(): Promise<void> | void }\ninterface ShellDomain {\n hook(\n name: \"create.before\",\n cb: (event: ShellCreateBeforeEvent) => void | Promise<void>,\n ): Promise<HookRegistration> | HookRegistration\n}\n/** V2 宿主上下文的最小结构声明:仅声明本插件用到的(可选)shell 域 */\nexport interface PluginContextV2 {\n shell?: ShellDomain\n}\ninterface PluginV2 {\n id: string\n setup(ctx: PluginContextV2): void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>\n}\n\n/** V2 setup:有 shell hook 域则注册注入,否则静默降级(no-op) */\nexport async function setup(ctx: PluginContextV2) {\n flog(\"=== LOADED (v2) ===\")\n const shell = ctx?.shell\n if (!shell || typeof shell.hook !== \"function\") {\n flog(\" host has no shell hook domain; degrade to no-op\")\n return\n }\n const registration = await shell.hook(\"create.before\", (event) => {\n flog(`[shell.create.before] shell=\"${event.shell}\"`)\n const kind = detectShellKind(event.shell)\n if (!kind) { flog(\" skip (unknown shell)\"); return }\n flog(` shell kind: ${kind}`)\n const next = injectUtf8Prefix(event.command, kind)\n if (next === undefined) return // 幂等 skip 日志已由 core 输出\n event.command = next\n flog(\" INJECTED\")\n })\n return async () => { await registration.dispose?.() }\n}\n\nconst Utf8EncodingPluginV2: PluginV2 = {\n id: \"utf8-encoding\",\n setup,\n}\nexport default Utf8EncodingPluginV2\n","/**\n * OpenCode Plugin — UTF-8 Encoding Fix for Windows (V1)\n *\n * V1 入口(tool.execute.before 契约),共享逻辑见 ./encoding-core.ts。\n *\n * 工作原理:拦截所有 bash/shell 工具调用,读取 opencode 配置的 shell\n * (pwsh / bash / cmd),按对应 shell 在命令前注入对应的 UTF-8 编码配置,\n * 解决中文/非 ASCII 字符乱码问题。未配置 shell 时不注入。\n */\n\nimport type { PluginInput } from \"@opencode-ai/plugin\"\nimport { detectShellKind, flog, injectUtf8Prefix, type ShellKind } from \"./encoding-core.js\"\nimport { setup } from \"./v2.js\"\n\n/** 带超时的 Promise(插件内不引入额外运行时依赖) */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const t = setTimeout(() => reject(new Error(\"timeout\")), ms)\n p.then(\n (v) => { clearTimeout(t); resolve(v) },\n (e) => { clearTimeout(t); reject(e) },\n )\n })\n}\n\n/** 读取 opencode 配置中的 shell,失败/超时/未配置返回 undefined(不注入,永不 reject) */\nasync function resolveShellKind(client: PluginInput[\"client\"]): Promise<ShellKind | undefined> {\n try {\n const res = await withTimeout(client.config.get(), 3000)\n const shell = (res.data as unknown as { shell?: string } | undefined)?.shell\n return detectShellKind(shell)\n } catch {\n return undefined\n }\n}\n\nexport const Utf8EncodingPlugin = async (input: PluginInput) => {\n flog(\"=== LOADED ===\")\n\n // 惰性检测:绝不在插件加载阶段调用 client.config.get()\n //(会因 httpapi 未就绪 + 请求无超时而永久挂起,导致 opencode 启动死锁)。\n // 首次 tool hook 触发时再查,结果缓存。\n let kindPromise: Promise<ShellKind | undefined> | null = null\n const getKind = () => (kindPromise ??= resolveShellKind(input.client))\n\n return {\n \"tool.execute.before\": async (input: { tool: string; sessionID: string; callID: string }, output: { args: any }) => {\n const tool = String(input?.tool ?? \"\")\n flog(`[tool.before] tool=\"${tool}\"`)\n\n if (tool !== \"bash\" && tool !== \"shell\") return\n\n const args = output.args\n if (!args) { flog(\" no args\"); return }\n\n const cmd = args.command\n if (typeof cmd !== \"string\" || !cmd) {\n flog(` args keys: ${JSON.stringify(Object.keys(args))}`)\n return\n }\n\n const kind = await getKind()\n if (!kind) { flog(\" skip (no shell configured)\"); return }\n flog(` shell kind: ${kind}`)\n\n const next = injectUtf8Prefix(cmd, kind)\n if (next === undefined) return // 幂等 skip 日志已由 core 输出\n\n args.command = next\n flog(\" INJECTED\")\n },\n }\n}\n\n/**\n * 三合一模块导出:\n * - V1 宿主(含 opencode ≤1.17、1.18.x 本地路径引用):调用 `server`(V1 裸函数亦可,\n * 具名导出 Utf8EncodingPlugin 保留);\n * - V2 宿主(npm 包规格 / plugins 目录自动发现):校验 `id` + `setup`,\n * 1.18.x 无 shell 域时 setup 降级 no-op,未来含 shell hook 的宿主自动启用注入。\n */\nconst pluginModule = {\n id: \"utf8-encoding\",\n server: Utf8EncodingPlugin,\n setup,\n}\nexport default pluginModule\n"],"mappings":"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","names":["input"]}
|
|
1
|
+
{"version":3,"sources":["../src/encoding-core.ts","../src/v2.ts","../src/v1.ts"],"sourcesContent":["/**\n * 共享核心 — UTF-8 编码前缀注入逻辑\n *\n * 由 V1(v1.ts)与 V2(v2.ts)入口共同复用;\n * 仅依赖 Node.js 内置模块,不引用任何插件包类型。\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"node:path\"\n\n// 调试日志(写入临时目录,默认关闭,设 OPENCODE_UTF8_DEBUG=1 开启)\nconst DEBUG = process.env.OPENCODE_UTF8_DEBUG === \"1\"\nconst LOG = join(tmpdir(), \"utf8-plugin.log\")\nexport function flog(msg: string) {\n if (!DEBUG) return\n try { appendFileSync(LOG, `[${new Date().toISOString()}] ${msg}\\n`, \"utf8\") } catch {}\n}\n\nexport type ShellKind = \"pwsh\" | \"bash\" | \"cmd\"\n\n// ── 各 shell 的 UTF-8 编码前缀与命令分隔符 ──\nexport const ENC: Record<ShellKind, { prefix: string; sep: string; marker: string }> = {\n pwsh: {\n prefix:\n \"[Console]::OutputEncoding=[Console]::InputEncoding=[Text.Encoding]::UTF8;\" +\n \"$OutputEncoding=[Text.Encoding]::UTF8;\" +\n \"$env:PYTHONIOENCODING='utf-8';\",\n sep: \"\\n\",\n marker: \"OutputEncoding\",\n },\n bash: {\n prefix: \"export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8;\",\n sep: \"\\n\",\n marker: \"LC_ALL\",\n },\n cmd: {\n prefix: \"chcp 65001 >nul\",\n sep: \" & \",\n marker: \"chcp\",\n },\n}\n\n/** 由 shell 路径/名称(可能含目录与 .exe 后缀)归一化为 shell 类型;无法识别返回 undefined(不注入) */\nexport function detectShellKind(shell: string | undefined): ShellKind | undefined {\n if (!shell) return undefined\n const base = shell\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .pop()!\n .toLowerCase()\n .replace(/\\.exe$/, \"\")\n if (base === \"pwsh\" || base === \"powershell\") return \"pwsh\"\n if (base === \"cmd\") return \"cmd\"\n return \"bash\" // bash / zsh / sh / dash / ksh 等 POSIX shell\n}\n\n/** 提取 opencode 在命令前追加的 set VAR=\"value\" && 前缀 */\nexport function stripSetPrefixes(cmd: string): { prefixes: string; cleanCmd: string } {\n const m = cmd.match(/^((?:set\\s+\\w+=\"[^\"]*\"\\s*&&\\s*)+)/)\n if (m) return { prefixes: m[1], cleanCmd: cmd.slice(m[1].length) }\n return { prefixes: \"\", cleanCmd: cmd }\n}\n\n/** 在命令前注入 UTF-8 编码前缀;已含 marker(幂等)时返回 undefined */\nexport function injectUtf8Prefix(cmd: string, kind: ShellKind): string | undefined {\n const { prefixes, cleanCmd } = stripSetPrefixes(cmd)\n flog(` orig: ${cleanCmd.slice(0, 120)}`)\n\n const { prefix, sep, marker } = ENC[kind]\n\n // 防止重复注入\n if (cleanCmd.includes(marker)) { flog(\" skip (idempotent)\"); return undefined }\n\n return prefixes + prefix + sep + cleanCmd\n}\n","/**\n * OpenCode V2 Plugin — UTF-8 Encoding Fix for Windows\n *\n * V2 契约({ id, setup })。setup 在含 shell hook 域的 V2 宿主上注册\n * shell create.before hook(事件契约对照 opencode dev 分支\n * packages/plugin/src/promise/shell.ts 核实);宿主缺少 shell 域\n * (如 opencode 1.18.x)时降级为 no-op——保证插件始终可加载、不报错,\n * 待宿主升级后自动生效。类型为自包含最小结构声明,仅依赖结构匹配。\n */\n\nimport { detectShellKind, flog, injectUtf8Prefix } from \"./encoding-core.js\"\n\n/** shell create.before 事件(对照 dev 分支 ShellCreateBefore) */\nexport interface ShellCreateBeforeEvent {\n command: string\n cwd: string\n timeout: number\n shell: string\n env: Record<string, string | undefined>\n}\nexport interface HookRegistration { dispose(): Promise<void> | void }\ninterface ShellDomain {\n hook(\n name: \"create.before\",\n cb: (event: ShellCreateBeforeEvent) => void | Promise<void>,\n ): Promise<HookRegistration> | HookRegistration\n}\n/** V2 宿主上下文的最小结构声明:仅声明本插件用到的(可选)shell 域 */\nexport interface PluginContextV2 {\n shell?: ShellDomain\n}\ninterface PluginV2 {\n id: string\n setup(ctx: PluginContextV2): void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>\n}\n\n/** V2 setup:有 shell hook 域则注册注入,否则静默降级(no-op) */\nexport async function setup(ctx: PluginContextV2) {\n flog(\"=== LOADED (v2) ===\")\n const shell = ctx?.shell\n if (!shell || typeof shell.hook !== \"function\") {\n flog(\" host has no shell hook domain; degrade to no-op\")\n return\n }\n const registration = await shell.hook(\"create.before\", (event) => {\n flog(`[shell.create.before] shell=\"${event.shell}\"`)\n const kind = detectShellKind(event.shell)\n if (!kind) { flog(\" skip (unknown shell)\"); return }\n flog(` shell kind: ${kind}`)\n const next = injectUtf8Prefix(event.command, kind)\n if (next === undefined) return // 幂等 skip 日志已由 core 输出\n event.command = next\n flog(\" INJECTED\")\n })\n return async () => { await registration.dispose?.() }\n}\n\nconst Utf8EncodingPluginV2: PluginV2 = {\n id: \"windows-encoding\",\n setup,\n}\nexport default Utf8EncodingPluginV2\n","/**\n * OpenCode Plugin — UTF-8 Encoding Fix for Windows (V1)\n *\n * V1 入口(tool.execute.before 契约),共享逻辑见 ./encoding-core.ts。\n *\n * 工作原理:拦截所有 bash/shell 工具调用,读取 opencode 配置的 shell\n * (pwsh / bash / cmd),按对应 shell 在命令前注入对应的 UTF-8 编码配置,\n * 解决中文/非 ASCII 字符乱码问题。未配置 shell 时不注入。\n */\n\nimport type { PluginInput } from \"@opencode-ai/plugin\"\nimport { detectShellKind, flog, injectUtf8Prefix, type ShellKind } from \"./encoding-core.js\"\nimport { setup } from \"./v2.js\"\n\n/** 带超时的 Promise(插件内不引入额外运行时依赖) */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const t = setTimeout(() => reject(new Error(\"timeout\")), ms)\n p.then(\n (v) => { clearTimeout(t); resolve(v) },\n (e) => { clearTimeout(t); reject(e) },\n )\n })\n}\n\n/** 读取 opencode 配置中的 shell,失败/超时/未配置返回 undefined(不注入,永不 reject) */\nasync function resolveShellKind(client: PluginInput[\"client\"]): Promise<ShellKind | undefined> {\n try {\n const res = await withTimeout(client.config.get(), 3000)\n const shell = (res.data as unknown as { shell?: string } | undefined)?.shell\n return detectShellKind(shell)\n } catch {\n return undefined\n }\n}\n\nexport const Utf8EncodingPlugin = async (input: PluginInput) => {\n flog(\"=== LOADED ===\")\n\n // 惰性检测:绝不在插件加载阶段调用 client.config.get()\n //(会因 httpapi 未就绪 + 请求无超时而永久挂起,导致 opencode 启动死锁)。\n // 首次 tool hook 触发时再查,结果缓存。\n let kindPromise: Promise<ShellKind | undefined> | null = null\n const getKind = () => (kindPromise ??= resolveShellKind(input.client))\n\n return {\n \"tool.execute.before\": async (input: { tool: string; sessionID: string; callID: string }, output: { args: any }) => {\n const tool = String(input?.tool ?? \"\")\n flog(`[tool.before] tool=\"${tool}\"`)\n\n if (tool !== \"bash\" && tool !== \"shell\") return\n\n const args = output.args\n if (!args) { flog(\" no args\"); return }\n\n const cmd = args.command\n if (typeof cmd !== \"string\" || !cmd) {\n flog(` args keys: ${JSON.stringify(Object.keys(args))}`)\n return\n }\n\n const kind = await getKind()\n if (!kind) { flog(\" skip (no shell configured)\"); return }\n flog(` shell kind: ${kind}`)\n\n const next = injectUtf8Prefix(cmd, kind)\n if (next === undefined) return // 幂等 skip 日志已由 core 输出\n\n args.command = next\n flog(\" INJECTED\")\n },\n }\n}\n\n/**\n * 三合一模块导出:\n * - V1 宿主(含 opencode ≤1.17、1.18.x 本地路径引用):调用 `server`(V1 裸函数亦可,\n * 具名导出 Utf8EncodingPlugin 保留);\n * - V2 宿主(npm 包规格 / plugins 目录自动发现):校验 `id` + `setup`,\n * 1.18.x 无 shell 域时 setup 降级 no-op,未来含 shell hook 的宿主自动启用注入。\n */\nconst pluginModule = {\n id: \"windows-encoding\",\n server: Utf8EncodingPlugin,\n setup,\n}\nexport default pluginModule\n"],"mappings":"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","names":["input"]}
|
package/dist/v2.js
CHANGED
package/dist/v2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/encoding-core.ts","../src/v2.ts"],"sourcesContent":["/**\n * 共享核心 — UTF-8 编码前缀注入逻辑\n *\n * 由 V1(v1.ts)与 V2(v2.ts)入口共同复用;\n * 仅依赖 Node.js 内置模块,不引用任何插件包类型。\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"node:path\"\n\n// 调试日志(写入临时目录,默认关闭,设 OPENCODE_UTF8_DEBUG=1 开启)\nconst DEBUG = process.env.OPENCODE_UTF8_DEBUG === \"1\"\nconst LOG = join(tmpdir(), \"utf8-plugin.log\")\nexport function flog(msg: string) {\n if (!DEBUG) return\n try { appendFileSync(LOG, `[${new Date().toISOString()}] ${msg}\\n`, \"utf8\") } catch {}\n}\n\nexport type ShellKind = \"pwsh\" | \"bash\" | \"cmd\"\n\n// ── 各 shell 的 UTF-8 编码前缀与命令分隔符 ──\nexport const ENC: Record<ShellKind, { prefix: string; sep: string; marker: string }> = {\n pwsh: {\n prefix:\n \"[Console]::OutputEncoding=[Console]::InputEncoding=[Text.Encoding]::UTF8;\" +\n \"$OutputEncoding=[Text.Encoding]::UTF8;\" +\n \"$env:PYTHONIOENCODING='utf-8';\",\n sep: \"\\n\",\n marker: \"OutputEncoding\",\n },\n bash: {\n prefix: \"export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8;\",\n sep: \"\\n\",\n marker: \"LC_ALL\",\n },\n cmd: {\n prefix: \"chcp 65001 >nul\",\n sep: \" & \",\n marker: \"chcp\",\n },\n}\n\n/** 由 shell 路径/名称(可能含目录与 .exe 后缀)归一化为 shell 类型;无法识别返回 undefined(不注入) */\nexport function detectShellKind(shell: string | undefined): ShellKind | undefined {\n if (!shell) return undefined\n const base = shell\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .pop()!\n .toLowerCase()\n .replace(/\\.exe$/, \"\")\n if (base === \"pwsh\" || base === \"powershell\") return \"pwsh\"\n if (base === \"cmd\") return \"cmd\"\n return \"bash\" // bash / zsh / sh / dash / ksh 等 POSIX shell\n}\n\n/** 提取 opencode 在命令前追加的 set VAR=\"value\" && 前缀 */\nexport function stripSetPrefixes(cmd: string): { prefixes: string; cleanCmd: string } {\n const m = cmd.match(/^((?:set\\s+\\w+=\"[^\"]*\"\\s*&&\\s*)+)/)\n if (m) return { prefixes: m[1], cleanCmd: cmd.slice(m[1].length) }\n return { prefixes: \"\", cleanCmd: cmd }\n}\n\n/** 在命令前注入 UTF-8 编码前缀;已含 marker(幂等)时返回 undefined */\nexport function injectUtf8Prefix(cmd: string, kind: ShellKind): string | undefined {\n const { prefixes, cleanCmd } = stripSetPrefixes(cmd)\n flog(` orig: ${cleanCmd.slice(0, 120)}`)\n\n const { prefix, sep, marker } = ENC[kind]\n\n // 防止重复注入\n if (cleanCmd.includes(marker)) { flog(\" skip (idempotent)\"); return undefined }\n\n return prefixes + prefix + sep + cleanCmd\n}\n","/**\n * OpenCode V2 Plugin — UTF-8 Encoding Fix for Windows\n *\n * V2 契约({ id, setup })。setup 在含 shell hook 域的 V2 宿主上注册\n * shell create.before hook(事件契约对照 opencode dev 分支\n * packages/plugin/src/promise/shell.ts 核实);宿主缺少 shell 域\n * (如 opencode 1.18.x)时降级为 no-op——保证插件始终可加载、不报错,\n * 待宿主升级后自动生效。类型为自包含最小结构声明,仅依赖结构匹配。\n */\n\nimport { detectShellKind, flog, injectUtf8Prefix } from \"./encoding-core.js\"\n\n/** shell create.before 事件(对照 dev 分支 ShellCreateBefore) */\nexport interface ShellCreateBeforeEvent {\n command: string\n cwd: string\n timeout: number\n shell: string\n env: Record<string, string | undefined>\n}\nexport interface HookRegistration { dispose(): Promise<void> | void }\ninterface ShellDomain {\n hook(\n name: \"create.before\",\n cb: (event: ShellCreateBeforeEvent) => void | Promise<void>,\n ): Promise<HookRegistration> | HookRegistration\n}\n/** V2 宿主上下文的最小结构声明:仅声明本插件用到的(可选)shell 域 */\nexport interface PluginContextV2 {\n shell?: ShellDomain\n}\ninterface PluginV2 {\n id: string\n setup(ctx: PluginContextV2): void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>\n}\n\n/** V2 setup:有 shell hook 域则注册注入,否则静默降级(no-op) */\nexport async function setup(ctx: PluginContextV2) {\n flog(\"=== LOADED (v2) ===\")\n const shell = ctx?.shell\n if (!shell || typeof shell.hook !== \"function\") {\n flog(\" host has no shell hook domain; degrade to no-op\")\n return\n }\n const registration = await shell.hook(\"create.before\", (event) => {\n flog(`[shell.create.before] shell=\"${event.shell}\"`)\n const kind = detectShellKind(event.shell)\n if (!kind) { flog(\" skip (unknown shell)\"); return }\n flog(` shell kind: ${kind}`)\n const next = injectUtf8Prefix(event.command, kind)\n if (next === undefined) return // 幂等 skip 日志已由 core 输出\n event.command = next\n flog(\" INJECTED\")\n })\n return async () => { await registration.dispose?.() }\n}\n\nconst Utf8EncodingPluginV2: PluginV2 = {\n id: \"
|
|
1
|
+
{"version":3,"sources":["../src/encoding-core.ts","../src/v2.ts"],"sourcesContent":["/**\n * 共享核心 — UTF-8 编码前缀注入逻辑\n *\n * 由 V1(v1.ts)与 V2(v2.ts)入口共同复用;\n * 仅依赖 Node.js 内置模块,不引用任何插件包类型。\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"node:path\"\n\n// 调试日志(写入临时目录,默认关闭,设 OPENCODE_UTF8_DEBUG=1 开启)\nconst DEBUG = process.env.OPENCODE_UTF8_DEBUG === \"1\"\nconst LOG = join(tmpdir(), \"utf8-plugin.log\")\nexport function flog(msg: string) {\n if (!DEBUG) return\n try { appendFileSync(LOG, `[${new Date().toISOString()}] ${msg}\\n`, \"utf8\") } catch {}\n}\n\nexport type ShellKind = \"pwsh\" | \"bash\" | \"cmd\"\n\n// ── 各 shell 的 UTF-8 编码前缀与命令分隔符 ──\nexport const ENC: Record<ShellKind, { prefix: string; sep: string; marker: string }> = {\n pwsh: {\n prefix:\n \"[Console]::OutputEncoding=[Console]::InputEncoding=[Text.Encoding]::UTF8;\" +\n \"$OutputEncoding=[Text.Encoding]::UTF8;\" +\n \"$env:PYTHONIOENCODING='utf-8';\",\n sep: \"\\n\",\n marker: \"OutputEncoding\",\n },\n bash: {\n prefix: \"export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8;\",\n sep: \"\\n\",\n marker: \"LC_ALL\",\n },\n cmd: {\n prefix: \"chcp 65001 >nul\",\n sep: \" & \",\n marker: \"chcp\",\n },\n}\n\n/** 由 shell 路径/名称(可能含目录与 .exe 后缀)归一化为 shell 类型;无法识别返回 undefined(不注入) */\nexport function detectShellKind(shell: string | undefined): ShellKind | undefined {\n if (!shell) return undefined\n const base = shell\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .pop()!\n .toLowerCase()\n .replace(/\\.exe$/, \"\")\n if (base === \"pwsh\" || base === \"powershell\") return \"pwsh\"\n if (base === \"cmd\") return \"cmd\"\n return \"bash\" // bash / zsh / sh / dash / ksh 等 POSIX shell\n}\n\n/** 提取 opencode 在命令前追加的 set VAR=\"value\" && 前缀 */\nexport function stripSetPrefixes(cmd: string): { prefixes: string; cleanCmd: string } {\n const m = cmd.match(/^((?:set\\s+\\w+=\"[^\"]*\"\\s*&&\\s*)+)/)\n if (m) return { prefixes: m[1], cleanCmd: cmd.slice(m[1].length) }\n return { prefixes: \"\", cleanCmd: cmd }\n}\n\n/** 在命令前注入 UTF-8 编码前缀;已含 marker(幂等)时返回 undefined */\nexport function injectUtf8Prefix(cmd: string, kind: ShellKind): string | undefined {\n const { prefixes, cleanCmd } = stripSetPrefixes(cmd)\n flog(` orig: ${cleanCmd.slice(0, 120)}`)\n\n const { prefix, sep, marker } = ENC[kind]\n\n // 防止重复注入\n if (cleanCmd.includes(marker)) { flog(\" skip (idempotent)\"); return undefined }\n\n return prefixes + prefix + sep + cleanCmd\n}\n","/**\n * OpenCode V2 Plugin — UTF-8 Encoding Fix for Windows\n *\n * V2 契约({ id, setup })。setup 在含 shell hook 域的 V2 宿主上注册\n * shell create.before hook(事件契约对照 opencode dev 分支\n * packages/plugin/src/promise/shell.ts 核实);宿主缺少 shell 域\n * (如 opencode 1.18.x)时降级为 no-op——保证插件始终可加载、不报错,\n * 待宿主升级后自动生效。类型为自包含最小结构声明,仅依赖结构匹配。\n */\n\nimport { detectShellKind, flog, injectUtf8Prefix } from \"./encoding-core.js\"\n\n/** shell create.before 事件(对照 dev 分支 ShellCreateBefore) */\nexport interface ShellCreateBeforeEvent {\n command: string\n cwd: string\n timeout: number\n shell: string\n env: Record<string, string | undefined>\n}\nexport interface HookRegistration { dispose(): Promise<void> | void }\ninterface ShellDomain {\n hook(\n name: \"create.before\",\n cb: (event: ShellCreateBeforeEvent) => void | Promise<void>,\n ): Promise<HookRegistration> | HookRegistration\n}\n/** V2 宿主上下文的最小结构声明:仅声明本插件用到的(可选)shell 域 */\nexport interface PluginContextV2 {\n shell?: ShellDomain\n}\ninterface PluginV2 {\n id: string\n setup(ctx: PluginContextV2): void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>\n}\n\n/** V2 setup:有 shell hook 域则注册注入,否则静默降级(no-op) */\nexport async function setup(ctx: PluginContextV2) {\n flog(\"=== LOADED (v2) ===\")\n const shell = ctx?.shell\n if (!shell || typeof shell.hook !== \"function\") {\n flog(\" host has no shell hook domain; degrade to no-op\")\n return\n }\n const registration = await shell.hook(\"create.before\", (event) => {\n flog(`[shell.create.before] shell=\"${event.shell}\"`)\n const kind = detectShellKind(event.shell)\n if (!kind) { flog(\" skip (unknown shell)\"); return }\n flog(` shell kind: ${kind}`)\n const next = injectUtf8Prefix(event.command, kind)\n if (next === undefined) return // 幂等 skip 日志已由 core 输出\n event.command = next\n flog(\" INJECTED\")\n })\n return async () => { await registration.dispose?.() }\n}\n\nconst Utf8EncodingPluginV2: PluginV2 = {\n id: \"windows-encoding\",\n setup,\n}\nexport default Utf8EncodingPluginV2\n"],"mappings":";AAOA,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,YAAY;AAGrB,IAAM,QAAQ,QAAQ,IAAI,wBAAwB;AAClD,IAAM,MAAM,KAAK,OAAO,GAAG,iBAAiB;AACrC,SAAS,KAAK,KAAa;AAChC,MAAI,CAAC,MAAO;AACZ,MAAI;AAAE,mBAAe,KAAK,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,GAAG;AAAA,GAAM,MAAM;AAAA,EAAE,QAAQ;AAAA,EAAC;AACvF;AAKO,IAAM,MAA0E;AAAA,EACrF,MAAM;AAAA,IACJ,QACE;AAAA,IAGF,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AAAA,EACA,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AACF;AAGO,SAAS,gBAAgB,OAAkD;AAChF,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,MACV,QAAQ,OAAO,GAAG,EAClB,MAAM,GAAG,EACT,IAAI,EACJ,YAAY,EACZ,QAAQ,UAAU,EAAE;AACvB,MAAI,SAAS,UAAU,SAAS,aAAc,QAAO;AACrD,MAAI,SAAS,MAAO,QAAO;AAC3B,SAAO;AACT;AAGO,SAAS,iBAAiB,KAAqD;AACpF,QAAM,IAAI,IAAI,MAAM,mCAAmC;AACvD,MAAI,EAAG,QAAO,EAAE,UAAU,EAAE,CAAC,GAAG,UAAU,IAAI,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE;AACjE,SAAO,EAAE,UAAU,IAAI,UAAU,IAAI;AACvC;AAGO,SAAS,iBAAiB,KAAa,MAAqC;AACjF,QAAM,EAAE,UAAU,SAAS,IAAI,iBAAiB,GAAG;AACnD,OAAK,WAAW,SAAS,MAAM,GAAG,GAAG,CAAC,EAAE;AAExC,QAAM,EAAE,QAAQ,KAAK,OAAO,IAAI,IAAI,IAAI;AAGxC,MAAI,SAAS,SAAS,MAAM,GAAG;AAAE,SAAK,qBAAqB;AAAG,WAAO;AAAA,EAAU;AAE/E,SAAO,WAAW,SAAS,MAAM;AACnC;;;ACtCA,eAAsB,MAAM,KAAsB;AAChD,OAAK,qBAAqB;AAC1B,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,SAAS,OAAO,MAAM,SAAS,YAAY;AAC9C,SAAK,mDAAmD;AACxD;AAAA,EACF;AACA,QAAM,eAAe,MAAM,MAAM,KAAK,iBAAiB,CAAC,UAAU;AAChE,SAAK,gCAAgC,MAAM,KAAK,GAAG;AACnD,UAAM,OAAO,gBAAgB,MAAM,KAAK;AACxC,QAAI,CAAC,MAAM;AAAE,WAAK,wBAAwB;AAAG;AAAA,IAAO;AACpD,SAAK,iBAAiB,IAAI,EAAE;AAC5B,UAAM,OAAO,iBAAiB,MAAM,SAAS,IAAI;AACjD,QAAI,SAAS,OAAW;AACxB,UAAM,UAAU;AAChB,SAAK,YAAY;AAAA,EACnB,CAAC;AACD,SAAO,YAAY;AAAE,UAAM,aAAa,UAAU;AAAA,EAAE;AACtD;AAEA,IAAM,uBAAiC;AAAA,EACrC,IAAI;AAAA,EACJ;AACF;AACA,IAAO,aAAQ;","names":[]}
|