opencode-tbot 0.1.33 → 0.1.35
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.ja.md +137 -48
- package/README.md +94 -46
- package/README.zh-CN.md +114 -63
- package/dist/assets/{plugin-config-LIr8LS0-.js → plugin-config-Be3vV2kr.js} +10 -2
- package/dist/assets/plugin-config-Be3vV2kr.js.map +1 -0
- package/dist/cli.js +54 -40
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +41 -18
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/plugin-config-LIr8LS0-.js.map +0 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { stderr, stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { parse } from \"jsonc-parser\";\nimport {\n DEFAULT_TELEGRAM_API_ROOT,\n type PluginConfigSource,\n} from \"./app/config.js\";\nimport { OPENCODE_TBOT_VERSION } from \"./app/package-info.js\";\nimport {\n mergePluginConfigSources,\n writePluginConfigFile,\n} from \"./app/plugin-config.js\";\nimport {\n getDefaultPluginLogDirectory,\n getGlobalPluginConfigFilePath,\n resolveWritableOpenCodeConfigFilePath,\n} from \"./app/opencode-paths.js\";\n\nexport interface InstallCommandOptions {\n botToken?: string;\n homeDir?: string;\n pluginSpec?: string;\n registerPlugin?: boolean;\n telegramApiRoot?: string;\n}\n\ninterface CliOptions extends InstallCommandOptions {\n command: \"help\" | \"install\" | \"update\" | \"version\";\n}\n\nexport interface PromptSessionLike {\n ask(question: string): Promise<string>;\n askSecret(question: string): Promise<string>;\n confirm(question: string, defaultValue: boolean): Promise<boolean>;\n close(): void;\n}\n\nexport interface CreatePromptSessionOptions {\n input?: NodeJS.ReadStream;\n output?: NodeJS.WriteStream;\n}\n\ninterface OpenCodeGlobalConfig {\n plugin?: string[];\n [key: string]: unknown;\n}\n\nconst DEFAULT_PLUGIN_SPEC = \"opencode-tbot@latest\";\nconst PROMPT_CANCELLED_ERROR = \"Prompt cancelled.\";\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n try {\n const exitCode = await runCli(argv);\n process.exitCode = exitCode;\n\n return exitCode;\n } catch (error) {\n stderr.write(`${formatCliError(error)}\\n`);\n process.exitCode = 1;\n\n return 1;\n }\n}\n\nexport async function runCli(argv: string[]): Promise<number> {\n const options = parseCliOptions(argv);\n\n if (options.command === \"help\") {\n stdout.write(`${buildHelpText()}\\n`);\n return 0;\n }\n\n if (options.command === \"version\") {\n stdout.write(`${OPENCODE_TBOT_VERSION}\\n`);\n return 0;\n }\n\n if (options.command === \"update\") {\n await updatePlugin(options);\n return 0;\n }\n\n await installPlugin(options);\n return 0;\n}\n\nexport async function installPlugin(options: InstallCommandOptions = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);\n const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\n const openCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const existingPluginConfig = await readPluginConfigFile(globalPluginConfigFilePath);\n const prompt = createPromptSession();\n\n try {\n const botToken = normalizeRequiredString(\n options.botToken ?? await prompt.askSecret(\"Telegram bot token: \"),\n \"Telegram bot token is required.\",\n );\n const telegramApiRoot = normalizeOptionalString(options.telegramApiRoot) ?? DEFAULT_TELEGRAM_API_ROOT;\n const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;\n const nextOpenCodeConfig = options.registerPlugin === false\n ? openCodeConfig\n : ensurePluginRegistered(openCodeConfig, pluginSpec);\n const nextPluginConfig = buildInstalledPluginConfig(\n existingPluginConfig,\n botToken,\n telegramApiRoot,\n );\n\n if (options.registerPlugin === false) {\n await ensureParentDirectory(openCodeConfigFilePath);\n } else {\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n }\n await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);\n\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(`Plugin config: ${globalPluginConfigFilePath}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n } finally {\n prompt.close();\n }\n}\n\nexport async function updatePlugin(options: Pick<InstallCommandOptions, \"homeDir\" | \"pluginSpec\"> = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);\n const openCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;\n const nextOpenCodeConfig = replacePluginRegistration(openCodeConfig, pluginSpec);\n\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(`Plugin config: ${getGlobalPluginConfigFilePath(homeDir)}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n}\n\nfunction parseCliOptions(argv: string[]): CliOptions {\n const args = [...argv];\n const first = args[0];\n const command = !first || first.startsWith(\"-\")\n ? \"install\"\n : first;\n const values = command === \"install\" || command === \"version\"\n ? args\n : args.slice(1);\n const options: CliOptions = {\n command: command === \"help\" || command === \"--help\" || command === \"-h\"\n ? \"help\"\n : command === \"version\" || command === \"--version\" || command === \"-v\"\n ? \"version\"\n : command === \"update\"\n ? \"update\"\n : \"install\",\n };\n\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n\n if (index === 0 && !value.startsWith(\"-\")) {\n continue;\n }\n\n switch (value) {\n case \"--bot-token\":\n options.botToken = values[++index];\n break;\n case \"--plugin-spec\":\n options.pluginSpec = values[++index];\n break;\n case \"--telegram-api-root\":\n options.telegramApiRoot = values[++index];\n break;\n case \"--skip-register\":\n options.registerPlugin = false;\n break;\n case \"--home-dir\":\n options.homeDir = values[++index];\n break;\n case \"--help\":\n case \"-h\":\n options.command = \"help\";\n break;\n case \"--version\":\n case \"-v\":\n options.command = \"version\";\n break;\n default:\n throw new Error(`Unknown argument: ${value}`);\n }\n }\n\n return options;\n}\n\nfunction buildInstalledPluginConfig(\n current: PluginConfigSource,\n botToken: string,\n telegramApiRoot: string,\n): PluginConfigSource {\n const merged = mergePluginConfigSources(current, {\n telegram: {\n botToken,\n apiRoot: telegramApiRoot,\n },\n });\n const { openrouter: _openrouter, ...nextConfig } = merged as PluginConfigSource & {\n openrouter?: unknown;\n };\n\n return nextConfig;\n}\n\nfunction ensurePluginRegistered(\n config: OpenCodeGlobalConfig,\n pluginSpec: string,\n): OpenCodeGlobalConfig {\n const plugins = Array.isArray(config.plugin)\n ? config.plugin.filter((item): item is string => typeof item === \"string\")\n : [];\n\n if (!plugins.includes(pluginSpec)) {\n plugins.push(pluginSpec);\n }\n\n return {\n ...config,\n plugin: plugins,\n };\n}\n\nfunction replacePluginRegistration(\n config: OpenCodeGlobalConfig,\n pluginSpec: string,\n): OpenCodeGlobalConfig {\n const currentPlugins = Array.isArray(config.plugin)\n ? config.plugin.filter((item): item is string => typeof item === \"string\")\n : [];\n const nextPlugins: string[] = [];\n let inserted = false;\n\n for (const currentPlugin of currentPlugins) {\n if (isOpencodeTbotPluginSpec(currentPlugin)) {\n if (!inserted) {\n nextPlugins.push(pluginSpec);\n inserted = true;\n }\n\n continue;\n }\n\n nextPlugins.push(currentPlugin);\n }\n\n if (!inserted) {\n nextPlugins.push(pluginSpec);\n }\n\n return {\n ...config,\n plugin: nextPlugins,\n };\n}\n\nasync function readPluginConfigFile(configFilePath: string): Promise<PluginConfigSource> {\n try {\n const content = await readFile(configFilePath, \"utf8\");\n const parsed = JSON.parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as PluginConfigSource\n : {};\n } catch (error) {\n if (isMissingFileError(error)) {\n return {};\n }\n\n throw error;\n }\n}\n\nasync function readJsoncObject<TObject extends Record<string, unknown>>(\n filePath: string,\n): Promise<TObject> {\n try {\n const content = await readFile(filePath, \"utf8\");\n const parsed = parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as TObject\n : {} as TObject;\n } catch (error) {\n if (isMissingFileError(error)) {\n return {} as TObject;\n }\n\n throw error;\n }\n}\n\nasync function writeJsonFile(filePath: string, value: Record<string, unknown>): Promise<void> {\n await ensureParentDirectory(filePath);\n await writeFile(filePath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n}\n\nasync function ensureParentDirectory(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n}\n\nexport function createPromptSession(options: CreatePromptSessionOptions = {}): PromptSessionLike {\n const input = options.input ?? stdin;\n const output = options.output ?? stdout;\n\n if (!input.isTTY || !output.isTTY) {\n return {\n ask: async () => \"\",\n askSecret: async () => \"\",\n async confirm(_question: string, defaultValue: boolean) {\n return defaultValue;\n },\n close() { },\n };\n }\n\n return {\n ask(question: string) {\n return askQuestion(input, output, question);\n },\n askSecret(question: string) {\n return askSecretQuestion(input, output, question);\n },\n async confirm(question: string, defaultValue: boolean) {\n const answer = normalizeOptionalString(await askQuestion(input, output, question));\n\n if (!answer) {\n return defaultValue;\n }\n\n if ([\"y\", \"yes\"].includes(answer.toLowerCase())) {\n return true;\n }\n\n if ([\"n\", \"no\"].includes(answer.toLowerCase())) {\n return false;\n }\n\n throw new Error(`Unsupported answer: ${answer}`);\n },\n close() { },\n };\n}\n\nasync function askQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n const readline = createInterface({ input, output });\n\n try {\n return await readline.question(question);\n } finally {\n readline.close();\n }\n}\n\nasync function askSecretQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n if (typeof input.setRawMode !== \"function\") {\n return askQuestion(input, output, question);\n }\n\n output.write(question);\n\n try {\n return await readMaskedInput(input, output);\n } finally {\n output.write(\"\\n\");\n }\n}\n\nasync function readMaskedInput(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n): Promise<string> {\n return new Promise((resolvePromise, rejectPromise) => {\n const buffer: string[] = [];\n let settled = false;\n\n const cleanup = () => {\n input.off(\"data\", handleData);\n input.off(\"error\", handleError);\n input.pause();\n input.setRawMode?.(false);\n };\n\n const rejectWith = (error: unknown) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n rejectPromise(error);\n };\n\n const resolveWith = (value: string) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n resolvePromise(value);\n };\n\n const handleError = (error: unknown) => {\n rejectWith(error);\n };\n\n const handleData = (chunk: string | Buffer) => {\n const text = Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : chunk;\n\n for (const character of text) {\n if (character === \"\\r\" || character === \"\\n\") {\n resolveWith(buffer.join(\"\"));\n return;\n }\n\n if (character === \"\\u0003\") {\n rejectWith(new Error(PROMPT_CANCELLED_ERROR));\n return;\n }\n\n if (character === \"\\u0008\" || character === \"\\u007f\") {\n if (buffer.length > 0) {\n buffer.pop();\n output.write(\"\\b \\b\");\n }\n continue;\n }\n\n if (character < \" \" || character === \"\\u007f\") {\n continue;\n }\n\n buffer.push(character);\n output.write(\"*\");\n }\n };\n\n input.setRawMode?.(true);\n input.resume();\n input.on(\"error\", handleError);\n input.on(\"data\", handleData);\n });\n}\n\nfunction normalizeOptionalString(value: string | undefined | null): string | null {\n const normalized = value?.trim();\n\n return normalized\n ? normalized\n : null;\n}\n\nfunction normalizeRequiredString(value: string | undefined | null, errorMessage: string): string {\n const normalized = normalizeOptionalString(value);\n\n if (!normalized) {\n throw new Error(errorMessage);\n }\n\n return normalized;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nfunction isOpencodeTbotPluginSpec(value: string): boolean {\n const normalized = value.trim();\n\n return normalized === \"opencode-tbot\" || normalized.startsWith(\"opencode-tbot@\");\n}\n\nasync function warnAboutLegacyLocalInstallations(homeDir: string): Promise<void> {\n const legacyInstallations = await findLegacyLocalInstallations(homeDir);\n\n if (legacyInstallations.length === 0) {\n return;\n }\n\n stdout.write(\"\\nDetected local opencode-tbot npm installation(s) that can make OpenCode show the plugin as file:///.../node_modules/...\\n\");\n\n for (const installation of legacyInstallations) {\n stdout.write(`- ${installation.packagePath}\\n`);\n stdout.write(` cleanup: cd \"${installation.rootDir}\" && npm uninstall opencode-tbot\\n`);\n }\n\n stdout.write(\"Recommended npm flow: npm exec --package opencode-tbot@latest opencode-tbot -- install\\n\");\n}\n\nasync function findLegacyLocalInstallations(homeDir: string) {\n const roots = [...new Set([\n resolve(process.cwd()),\n resolve(homeDir),\n ])];\n const installations: Array<{ packagePath: string; rootDir: string }> = [];\n\n for (const rootDir of roots) {\n const packagePath = join(rootDir, \"node_modules\", \"opencode-tbot\", \"package.json\");\n\n if (await pathExists(packagePath)) {\n installations.push({\n packagePath: join(rootDir, \"node_modules\", \"opencode-tbot\"),\n rootDir,\n });\n }\n }\n\n return installations;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction buildHelpText(): string {\n return [\n \"Usage: opencode-tbot [install|update] [options]\",\n \" opencode-tbot --version\",\n \"\",\n \"Recommended npm usage:\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- install\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- update\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- --version\",\n \"\",\n \"Commands:\",\n \" install\",\n \" update\",\n \"\",\n \"Options:\",\n \" --bot-token <token>\",\n \" --telegram-api-root <url>\",\n \" --plugin-spec <spec>\",\n \" --skip-register\",\n \" --home-dir <path>\",\n \" --version\",\n \" --help\",\n ].join(\"\\n\");\n}\n\nfunction formatCliError(error: unknown): string {\n return error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { stderr, stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { parse } from \"jsonc-parser\";\nimport {\n DEFAULT_TELEGRAM_API_ROOT,\n type PluginConfigSource,\n} from \"./app/config.js\";\nimport { OPENCODE_TBOT_VERSION } from \"./app/package-info.js\";\nimport {\n mergePluginConfigSources,\n writePluginConfigFile,\n} from \"./app/plugin-config.js\";\nimport {\n getDefaultPluginLogDirectory,\n getGlobalPluginBridgeFilePath,\n getGlobalPluginConfigFilePath,\n resolveWritableOpenCodeConfigFilePath,\n} from \"./app/opencode-paths.js\";\n\nexport interface InstallCommandOptions {\n botToken?: string;\n homeDir?: string;\n pluginSpec?: string;\n registerPlugin?: boolean;\n telegramApiRoot?: string;\n}\n\ninterface CliOptions extends InstallCommandOptions {\n command: \"help\" | \"install\" | \"update\" | \"version\";\n}\n\nexport interface PromptSessionLike {\n ask(question: string): Promise<string>;\n askSecret(question: string): Promise<string>;\n confirm(question: string, defaultValue: boolean): Promise<boolean>;\n close(): void;\n}\n\nexport interface CreatePromptSessionOptions {\n input?: NodeJS.ReadStream;\n output?: NodeJS.WriteStream;\n}\n\ninterface OpenCodeGlobalConfig {\n plugin?: unknown[];\n [key: string]: unknown;\n}\n\nconst PROMPT_CANCELLED_ERROR = \"Prompt cancelled.\";\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n try {\n const exitCode = await runCli(argv);\n process.exitCode = exitCode;\n\n return exitCode;\n } catch (error) {\n stderr.write(`${formatCliError(error)}\\n`);\n process.exitCode = 1;\n\n return 1;\n }\n}\n\nexport async function runCli(argv: string[]): Promise<number> {\n const options = parseCliOptions(argv);\n\n if (options.command === \"help\") {\n stdout.write(`${buildHelpText()}\\n`);\n return 0;\n }\n\n if (options.command === \"version\") {\n stdout.write(`${OPENCODE_TBOT_VERSION}\\n`);\n return 0;\n }\n\n if (options.command === \"update\") {\n await updatePlugin(options);\n return 0;\n }\n\n await installPlugin(options);\n return 0;\n}\n\nexport async function installPlugin(options: InstallCommandOptions = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);\n const globalPluginBridgeFilePath = getGlobalPluginBridgeFilePath(homeDir);\n const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\n const existingPluginConfig = await readPluginConfigFile(globalPluginConfigFilePath);\n const prompt = createPromptSession();\n\n try {\n const botToken = normalizeRequiredString(\n options.botToken ?? await prompt.askSecret(\"Telegram bot token: \"),\n \"Telegram bot token is required.\",\n );\n const telegramApiRoot = normalizeOptionalString(options.telegramApiRoot) ?? DEFAULT_TELEGRAM_API_ROOT;\n const nextPluginConfig = buildInstalledPluginConfig(\n existingPluginConfig,\n botToken,\n telegramApiRoot,\n );\n\n if (options.registerPlugin !== false) {\n await installGlobalPluginBridge({\n openCodeConfigFilePath,\n pluginBridgeFilePath: globalPluginBridgeFilePath,\n });\n }\n await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);\n\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(\n `Plugin bridge: ${globalPluginBridgeFilePath}${options.registerPlugin === false ? \" (skipped)\" : \"\"}\\n`,\n );\n stdout.write(`Plugin config: ${globalPluginConfigFilePath}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n } finally {\n prompt.close();\n }\n}\n\nexport async function updatePlugin(options: Pick<InstallCommandOptions, \"homeDir\" | \"pluginSpec\"> = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);\n const globalPluginBridgeFilePath = getGlobalPluginBridgeFilePath(homeDir);\n\n await installGlobalPluginBridge({\n openCodeConfigFilePath,\n pluginBridgeFilePath: globalPluginBridgeFilePath,\n });\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(`Plugin bridge: ${globalPluginBridgeFilePath}\\n`);\n stdout.write(`Plugin config: ${getGlobalPluginConfigFilePath(homeDir)}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n}\n\nfunction parseCliOptions(argv: string[]): CliOptions {\n const args = [...argv];\n const first = args[0];\n const command = !first || first.startsWith(\"-\")\n ? \"install\"\n : first;\n const values = command === \"install\" || command === \"version\"\n ? args\n : args.slice(1);\n const options: CliOptions = {\n command: command === \"help\" || command === \"--help\" || command === \"-h\"\n ? \"help\"\n : command === \"version\" || command === \"--version\" || command === \"-v\"\n ? \"version\"\n : command === \"update\"\n ? \"update\"\n : \"install\",\n };\n\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n\n if (index === 0 && !value.startsWith(\"-\")) {\n continue;\n }\n\n switch (value) {\n case \"--bot-token\":\n options.botToken = values[++index];\n break;\n case \"--plugin-spec\":\n options.pluginSpec = values[++index];\n break;\n case \"--telegram-api-root\":\n options.telegramApiRoot = values[++index];\n break;\n case \"--skip-register\":\n options.registerPlugin = false;\n break;\n case \"--home-dir\":\n options.homeDir = values[++index];\n break;\n case \"--help\":\n case \"-h\":\n options.command = \"help\";\n break;\n case \"--version\":\n case \"-v\":\n options.command = \"version\";\n break;\n default:\n throw new Error(`Unknown argument: ${value}`);\n }\n }\n\n return options;\n}\n\nfunction buildInstalledPluginConfig(\n current: PluginConfigSource,\n botToken: string,\n telegramApiRoot: string,\n): PluginConfigSource {\n const merged = mergePluginConfigSources(current, {\n telegram: {\n botToken,\n apiRoot: telegramApiRoot,\n },\n });\n const { openrouter: _openrouter, ...nextConfig } = merged as PluginConfigSource & {\n openrouter?: unknown;\n };\n\n return nextConfig;\n}\n\nfunction removePluginRegistration(\n config: OpenCodeGlobalConfig,\n): OpenCodeGlobalConfig {\n const currentPlugins = Array.isArray(config.plugin)\n ? config.plugin\n : [];\n const nextPlugins = currentPlugins.filter(\n (currentPlugin) => typeof currentPlugin !== \"string\" || !isOpencodeTbotPluginSpec(currentPlugin),\n );\n const nextConfig = {\n ...config,\n };\n\n if (nextPlugins.length > 0) {\n nextConfig.plugin = nextPlugins;\n } else {\n delete nextConfig.plugin;\n }\n\n return nextConfig;\n}\n\nasync function readPluginConfigFile(configFilePath: string): Promise<PluginConfigSource> {\n try {\n const content = await readFile(configFilePath, \"utf8\");\n const parsed = JSON.parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as PluginConfigSource\n : {};\n } catch (error) {\n if (isMissingFileError(error)) {\n return {};\n }\n\n throw error;\n }\n}\n\nasync function readJsoncObject<TObject extends Record<string, unknown>>(\n filePath: string,\n): Promise<TObject> {\n try {\n const content = await readFile(filePath, \"utf8\");\n const parsed = parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as TObject\n : {} as TObject;\n } catch (error) {\n if (isMissingFileError(error)) {\n return {} as TObject;\n }\n\n throw error;\n }\n}\n\nasync function writeJsonFile(filePath: string, value: Record<string, unknown>): Promise<void> {\n await ensureParentDirectory(filePath);\n await writeFile(filePath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n}\n\nasync function installGlobalPluginBridge(\n input: {\n openCodeConfigFilePath: string;\n pluginBridgeFilePath: string;\n },\n): Promise<void> {\n const pluginModuleUrl = await resolveInstalledPluginModuleUrl();\n\n await writePluginBridgeFile(input.pluginBridgeFilePath, pluginModuleUrl);\n await removeLegacyNpmPluginRegistration(input.openCodeConfigFilePath);\n}\n\nasync function ensureParentDirectory(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n}\n\nasync function writePluginBridgeFile(\n pluginBridgeFilePath: string,\n pluginModuleUrl: string,\n): Promise<void> {\n await ensureParentDirectory(pluginBridgeFilePath);\n await writeFile(\n pluginBridgeFilePath,\n buildPluginBridgeSource(pluginModuleUrl),\n \"utf8\",\n );\n}\n\nfunction buildPluginBridgeSource(pluginModuleUrl: string): string {\n return `export { default } from ${JSON.stringify(pluginModuleUrl)};\\n`;\n}\n\nasync function resolveInstalledPluginModuleUrl(): Promise<string> {\n const candidates = [\n new URL(\"./plugin.js\", import.meta.url),\n new URL(\"./plugin.ts\", import.meta.url),\n new URL(\"../src/plugin.ts\", import.meta.url),\n new URL(\"../dist/plugin.js\", import.meta.url),\n ];\n\n for (const candidate of candidates) {\n if (await pathExists(candidate)) {\n return candidate.href;\n }\n }\n\n throw new Error(\"Failed to resolve the opencode-tbot plugin entry for the global OpenCode bridge.\");\n}\n\nasync function removeLegacyNpmPluginRegistration(openCodeConfigFilePath: string): Promise<void> {\n if (!await pathExists(openCodeConfigFilePath)) {\n return;\n }\n\n const currentOpenCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const nextOpenCodeConfig = removePluginRegistration(currentOpenCodeConfig);\n\n if (areJsonObjectsEqual(currentOpenCodeConfig, nextOpenCodeConfig)) {\n return;\n }\n\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n}\n\nfunction areJsonObjectsEqual(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\n}\n\nexport function createPromptSession(options: CreatePromptSessionOptions = {}): PromptSessionLike {\n const input = options.input ?? stdin;\n const output = options.output ?? stdout;\n\n if (!input.isTTY || !output.isTTY) {\n return {\n ask: async () => \"\",\n askSecret: async () => \"\",\n async confirm(_question: string, defaultValue: boolean) {\n return defaultValue;\n },\n close() { },\n };\n }\n\n return {\n ask(question: string) {\n return askQuestion(input, output, question);\n },\n askSecret(question: string) {\n return askSecretQuestion(input, output, question);\n },\n async confirm(question: string, defaultValue: boolean) {\n const answer = normalizeOptionalString(await askQuestion(input, output, question));\n\n if (!answer) {\n return defaultValue;\n }\n\n if ([\"y\", \"yes\"].includes(answer.toLowerCase())) {\n return true;\n }\n\n if ([\"n\", \"no\"].includes(answer.toLowerCase())) {\n return false;\n }\n\n throw new Error(`Unsupported answer: ${answer}`);\n },\n close() { },\n };\n}\n\nasync function askQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n const readline = createInterface({ input, output });\n\n try {\n return await readline.question(question);\n } finally {\n readline.close();\n }\n}\n\nasync function askSecretQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n if (typeof input.setRawMode !== \"function\") {\n return askQuestion(input, output, question);\n }\n\n output.write(question);\n\n try {\n return await readMaskedInput(input, output);\n } finally {\n output.write(\"\\n\");\n }\n}\n\nasync function readMaskedInput(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n): Promise<string> {\n return new Promise((resolvePromise, rejectPromise) => {\n const buffer: string[] = [];\n let settled = false;\n\n const cleanup = () => {\n input.off(\"data\", handleData);\n input.off(\"error\", handleError);\n input.pause();\n input.setRawMode?.(false);\n };\n\n const rejectWith = (error: unknown) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n rejectPromise(error);\n };\n\n const resolveWith = (value: string) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n resolvePromise(value);\n };\n\n const handleError = (error: unknown) => {\n rejectWith(error);\n };\n\n const handleData = (chunk: string | Buffer) => {\n const text = Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : chunk;\n\n for (const character of text) {\n if (character === \"\\r\" || character === \"\\n\") {\n resolveWith(buffer.join(\"\"));\n return;\n }\n\n if (character === \"\\u0003\") {\n rejectWith(new Error(PROMPT_CANCELLED_ERROR));\n return;\n }\n\n if (character === \"\\u0008\" || character === \"\\u007f\") {\n if (buffer.length > 0) {\n buffer.pop();\n output.write(\"\\b \\b\");\n }\n continue;\n }\n\n if (character < \" \" || character === \"\\u007f\") {\n continue;\n }\n\n buffer.push(character);\n output.write(\"*\");\n }\n };\n\n input.setRawMode?.(true);\n input.resume();\n input.on(\"error\", handleError);\n input.on(\"data\", handleData);\n });\n}\n\nfunction normalizeOptionalString(value: string | undefined | null): string | null {\n const normalized = value?.trim();\n\n return normalized\n ? normalized\n : null;\n}\n\nfunction normalizeRequiredString(value: string | undefined | null, errorMessage: string): string {\n const normalized = normalizeOptionalString(value);\n\n if (!normalized) {\n throw new Error(errorMessage);\n }\n\n return normalized;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nfunction isOpencodeTbotPluginSpec(value: string): boolean {\n const normalized = value.trim();\n\n return normalized === \"opencode-tbot\" || normalized.startsWith(\"opencode-tbot@\");\n}\n\nasync function warnAboutLegacyLocalInstallations(homeDir: string): Promise<void> {\n const legacyInstallations = await findLegacyLocalInstallations(homeDir);\n\n if (legacyInstallations.length === 0) {\n return;\n }\n\n stdout.write(\n \"\\nDetected local opencode-tbot npm installation(s) that can make the global OpenCode plugin bridge depend on project-local node_modules paths.\\n\",\n );\n\n for (const installation of legacyInstallations) {\n stdout.write(`- ${installation.packagePath}\\n`);\n stdout.write(` cleanup: cd \"${installation.rootDir}\" && npm uninstall opencode-tbot\\n`);\n }\n\n stdout.write(\"Recommended npm flow: npm exec --package opencode-tbot@latest opencode-tbot -- install\\n\");\n}\n\nasync function findLegacyLocalInstallations(homeDir: string) {\n const roots = [...new Set([\n resolve(process.cwd()),\n resolve(homeDir),\n ])];\n const installations: Array<{ packagePath: string; rootDir: string }> = [];\n\n for (const rootDir of roots) {\n const packagePath = join(rootDir, \"node_modules\", \"opencode-tbot\", \"package.json\");\n\n if (await pathExists(packagePath)) {\n installations.push({\n packagePath: join(rootDir, \"node_modules\", \"opencode-tbot\"),\n rootDir,\n });\n }\n }\n\n return installations;\n}\n\nasync function pathExists(filePath: string | URL): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction buildHelpText(): string {\n return [\n \"Usage: opencode-tbot [install|update] [options]\",\n \" opencode-tbot --version\",\n \"\",\n \"Recommended npm usage:\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- install\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- update\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- --version\",\n \"\",\n \"Commands:\",\n \" install\",\n \" update\",\n \"\",\n \"Options:\",\n \" --bot-token <token>\",\n \" --telegram-api-root <url>\",\n \" --plugin-spec <spec> (deprecated; ignored)\",\n \" --skip-register\",\n \" --home-dir <path>\",\n \" --version\",\n \" --help\",\n ].join(\"\\n\");\n}\n\nfunction formatCliError(error: unknown): string {\n return error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error);\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "./assets/plugin-config-
|
|
1
|
+
import "./assets/plugin-config-Be3vV2kr.js";
|
|
2
2
|
import { TelegramBotPlugin, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests } from "./plugin.js";
|
|
3
3
|
export { TelegramBotPlugin, TelegramBotPlugin as default, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests };
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadAppConfig } from "./assets/plugin-config-
|
|
1
|
+
import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadAppConfig } from "./assets/plugin-config-Be3vV2kr.js";
|
|
2
2
|
import { appendFile, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join } from "node:path";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -3120,14 +3120,16 @@ function normalizePermissionReply(value) {
|
|
|
3120
3120
|
function extractSessionErrorMessage(error) {
|
|
3121
3121
|
if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
|
|
3122
3122
|
if (!isPlainRecord(error)) return null;
|
|
3123
|
+
if (typeof error.message === "string" && error.message.trim().length > 0) return error.message.trim();
|
|
3123
3124
|
if (isPlainRecord(error.data) && typeof error.data.message === "string" && error.data.message.trim().length > 0) return error.data.message.trim();
|
|
3124
3125
|
return asNonEmptyString(error.name);
|
|
3125
3126
|
}
|
|
3126
3127
|
function normalizeForegroundSessionError(error) {
|
|
3127
3128
|
if (error instanceof Error) return error;
|
|
3128
|
-
if (isPlainRecord(error)
|
|
3129
|
+
if (isPlainRecord(error)) {
|
|
3129
3130
|
const normalized = new Error(extractSessionErrorMessage(error) ?? "Unknown session error.");
|
|
3130
|
-
|
|
3131
|
+
const normalizedName = asNonEmptyString(error.name);
|
|
3132
|
+
if (normalizedName) normalized.name = normalizedName;
|
|
3131
3133
|
if (isPlainRecord(error.data)) normalized.data = error.data;
|
|
3132
3134
|
return normalized;
|
|
3133
3135
|
}
|
|
@@ -3920,14 +3922,18 @@ function normalizeError(error, copy) {
|
|
|
3920
3922
|
message: copy.errors.contextOverflow,
|
|
3921
3923
|
cause: extractMessage(error.data) ?? null
|
|
3922
3924
|
};
|
|
3923
|
-
if (isNamedError(error, "APIError"))
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3925
|
+
if (isNamedError(error, "APIError")) {
|
|
3926
|
+
const providerMessage = extractMessage(error.data);
|
|
3927
|
+
return {
|
|
3928
|
+
message: copy.errors.providerRequest,
|
|
3929
|
+
cause: joinNonEmptyParts([
|
|
3930
|
+
getProviderCompatibilityHint(providerMessage),
|
|
3931
|
+
providerMessage,
|
|
3932
|
+
extractStatusCode(error.data, copy),
|
|
3933
|
+
extractRetryable(error.data, copy)
|
|
3934
|
+
])
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3931
3937
|
if (isNamedError(error, "NotFoundError")) return {
|
|
3932
3938
|
message: copy.errors.notFound,
|
|
3933
3939
|
cause: extractMessage(error.data) ?? null
|
|
@@ -3945,6 +3951,10 @@ function normalizeError(error, copy) {
|
|
|
3945
3951
|
cause: extractMessage(error) ?? stringifyUnknown(error)
|
|
3946
3952
|
};
|
|
3947
3953
|
}
|
|
3954
|
+
function getProviderCompatibilityHint(message) {
|
|
3955
|
+
if (!message) return null;
|
|
3956
|
+
return /tool_choice parameter does not support being set to required or object in thinking mode/iu.test(message) ? "Current model/reasoning mode is incompatible with tool calling. Switch to a compatible model or disable thinking mode." : null;
|
|
3957
|
+
}
|
|
3948
3958
|
function isBadRequestError(error) {
|
|
3949
3959
|
return !!error && typeof error === "object" && "success" in error && error.success === false;
|
|
3950
3960
|
}
|
|
@@ -5837,14 +5847,15 @@ function getTelegramBotRuntimeRegistry() {
|
|
|
5837
5847
|
//#region src/plugin.ts
|
|
5838
5848
|
async function ensureTelegramBotPluginRuntime(options) {
|
|
5839
5849
|
const runtimeStateHolder = getTelegramBotPluginRuntimeStateHolder();
|
|
5840
|
-
const
|
|
5841
|
-
if (runtimeStateHolder.state && runtimeStateHolder.state.
|
|
5850
|
+
const explicitCwd = resolveExplicitPluginRuntimeCwd(options.context);
|
|
5851
|
+
if (runtimeStateHolder.state && explicitCwd === null) return runtimeStateHolder.state.runtimePromise;
|
|
5852
|
+
if (runtimeStateHolder.state && explicitCwd !== null && runtimeStateHolder.state.cwd !== explicitCwd) {
|
|
5842
5853
|
const activeState = runtimeStateHolder.state;
|
|
5843
5854
|
runtimeStateHolder.state = null;
|
|
5844
5855
|
await disposeTelegramBotPluginRuntimeState(activeState);
|
|
5845
5856
|
}
|
|
5846
5857
|
if (!runtimeStateHolder.state) {
|
|
5847
|
-
const runtimePromise = startPluginRuntime(options,
|
|
5858
|
+
const runtimePromise = startPluginRuntime(options, resolvePluginRuntimeCwd(options.context), requirePluginClient(options.context)).then((runtime) => {
|
|
5848
5859
|
if (runtimeStateHolder.state?.runtimePromise === runtimePromise) runtimeStateHolder.state.runtime = runtime;
|
|
5849
5860
|
return runtime;
|
|
5850
5861
|
}).catch((error) => {
|
|
@@ -5852,7 +5863,7 @@ async function ensureTelegramBotPluginRuntime(options) {
|
|
|
5852
5863
|
throw error;
|
|
5853
5864
|
});
|
|
5854
5865
|
runtimeStateHolder.state = {
|
|
5855
|
-
cwd,
|
|
5866
|
+
cwd: explicitCwd ?? resolvePluginRuntimeCwd(options.context),
|
|
5856
5867
|
runtime: null,
|
|
5857
5868
|
runtimePromise
|
|
5858
5869
|
};
|
|
@@ -5869,7 +5880,7 @@ async function resetTelegramBotPluginRuntimeForTests() {
|
|
|
5869
5880
|
runtimeStateHolder.state = null;
|
|
5870
5881
|
await disposeTelegramBotPluginRuntimeState(activeState);
|
|
5871
5882
|
}
|
|
5872
|
-
async function startPluginRuntime(options, cwd) {
|
|
5883
|
+
async function startPluginRuntime(options, cwd, client) {
|
|
5873
5884
|
const bootstrapApp = options.bootstrapApp ?? bootstrapPluginApp;
|
|
5874
5885
|
const prepareConfiguration = options.prepareConfiguration ?? preparePluginConfiguration;
|
|
5875
5886
|
const startRuntime = options.startRuntime ?? startTelegramBotRuntime;
|
|
@@ -5877,7 +5888,7 @@ async function startPluginRuntime(options, cwd) {
|
|
|
5877
5888
|
cwd,
|
|
5878
5889
|
config: options.config
|
|
5879
5890
|
});
|
|
5880
|
-
const { config, container } = bootstrapApp(
|
|
5891
|
+
const { config, container } = bootstrapApp(client, preparedConfiguration.config, { cwd: preparedConfiguration.cwd });
|
|
5881
5892
|
try {
|
|
5882
5893
|
if (preparedConfiguration.ignoredProjectConfigFilePath) container.logger.child({ component: "runtime" }).warn({
|
|
5883
5894
|
cwd: preparedConfiguration.cwd,
|
|
@@ -5904,7 +5915,19 @@ async function startPluginRuntime(options, cwd) {
|
|
|
5904
5915
|
}
|
|
5905
5916
|
}
|
|
5906
5917
|
function resolvePluginRuntimeCwd(context) {
|
|
5907
|
-
return context
|
|
5918
|
+
return resolveExplicitPluginRuntimeCwd(context) ?? process.cwd();
|
|
5919
|
+
}
|
|
5920
|
+
function resolveExplicitPluginRuntimeCwd(context) {
|
|
5921
|
+
return normalizePluginRuntimePath(context?.worktree) ?? normalizePluginRuntimePath(context?.directory);
|
|
5922
|
+
}
|
|
5923
|
+
function requirePluginClient(context) {
|
|
5924
|
+
if (context?.client) return context.client;
|
|
5925
|
+
throw new Error("OpenCode plugin context.client is required.");
|
|
5926
|
+
}
|
|
5927
|
+
function normalizePluginRuntimePath(value) {
|
|
5928
|
+
if (typeof value !== "string") return null;
|
|
5929
|
+
const normalized = value.trim();
|
|
5930
|
+
return normalized.length > 0 ? normalized : null;
|
|
5908
5931
|
}
|
|
5909
5932
|
async function disposeTelegramBotPluginRuntimeState(state) {
|
|
5910
5933
|
await (state.runtime ?? await state.runtimePromise.catch(() => null))?.dispose();
|