opencode-tbot 0.1.16 → 0.1.17

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/plugin.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { c as loadAppConfig, i as preparePluginConfiguration, o as OPENCODE_TBOT_VERSION } from "./assets/plugin-config-DA71_jD3.js";
2
- import { createRequire } from "node:module";
3
2
  import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
4
3
  import { basename, dirname, extname, isAbsolute, join } from "node:path";
5
4
  import { parse, printParseErrorCode } from "jsonc-parser";
@@ -7,7 +6,6 @@ import { z } from "zod";
7
6
  import { OpenRouter } from "@openrouter/sdk";
8
7
  import { createOpencodeClient } from "@opencode-ai/sdk/v2/client";
9
8
  import { randomUUID } from "node:crypto";
10
- import { spawn } from "node:child_process";
11
9
  import { run } from "@grammyjs/runner";
12
10
  import { Bot, InlineKeyboard } from "grammy";
13
11
  //#region src/infra/utils/redact.ts
@@ -1180,145 +1178,6 @@ var NOOP_FOREGROUND_SESSION_TRACKER = {
1180
1178
  }
1181
1179
  };
1182
1180
  //#endregion
1183
- //#region src/services/voice-transcription/audio-transcoder.ts
1184
- var OPENROUTER_SUPPORTED_AUDIO_FORMATS = ["mp3", "wav"];
1185
- var VoiceTranscodingFailedError = class extends Error {
1186
- data;
1187
- constructor(message) {
1188
- super(message);
1189
- this.name = "VoiceTranscodingFailedError";
1190
- this.data = { message };
1191
- }
1192
- };
1193
- var DEFAULT_TRANSCODE_TIMEOUT_MS = 15e3;
1194
- var FfmpegAudioTranscoder = class {
1195
- ffmpegPath;
1196
- spawnProcess;
1197
- timeoutMs;
1198
- constructor(options) {
1199
- this.ffmpegPath = options.ffmpegPath?.trim() || null;
1200
- this.spawnProcess = options.spawnProcess ?? defaultSpawnProcess;
1201
- this.timeoutMs = options.timeoutMs ?? DEFAULT_TRANSCODE_TIMEOUT_MS;
1202
- }
1203
- async transcode(input) {
1204
- if (!this.ffmpegPath) throw new VoiceTranscodingFailedError(buildTranscodingMessage(input.sourceFormat, input.targetFormat, "Bundled ffmpeg is unavailable."));
1205
- if (input.targetFormat !== "wav") throw new VoiceTranscodingFailedError(buildTranscodingMessage(input.sourceFormat, input.targetFormat, `Unsupported transcode target: ${input.targetFormat}.`));
1206
- return {
1207
- data: await runFfmpegTranscode({
1208
- data: toUint8Array$1(input.data),
1209
- ffmpegPath: this.ffmpegPath,
1210
- filename: input.filename,
1211
- sourceFormat: input.sourceFormat,
1212
- spawnProcess: this.spawnProcess,
1213
- timeoutMs: this.timeoutMs,
1214
- targetFormat: input.targetFormat
1215
- }),
1216
- filename: replaceExtension(input.filename, ".wav"),
1217
- format: "wav",
1218
- mimeType: "audio/wav"
1219
- };
1220
- }
1221
- };
1222
- async function runFfmpegTranscode(input) {
1223
- return await new Promise((resolve, reject) => {
1224
- const child = input.spawnProcess(input.ffmpegPath, buildFfmpegArgs(input.targetFormat), {
1225
- stdio: [
1226
- "pipe",
1227
- "pipe",
1228
- "pipe"
1229
- ],
1230
- windowsHide: true
1231
- });
1232
- const stdoutChunks = [];
1233
- const stderrChunks = [];
1234
- let settled = false;
1235
- let timedOut = false;
1236
- const timer = setTimeout(() => {
1237
- timedOut = true;
1238
- child.kill();
1239
- }, input.timeoutMs);
1240
- const cleanup = () => {
1241
- clearTimeout(timer);
1242
- };
1243
- const rejectOnce = (message) => {
1244
- if (settled) return;
1245
- settled = true;
1246
- cleanup();
1247
- reject(new VoiceTranscodingFailedError(buildTranscodingMessage(input.sourceFormat, input.targetFormat, message)));
1248
- };
1249
- const resolveOnce = (value) => {
1250
- if (settled) return;
1251
- settled = true;
1252
- cleanup();
1253
- resolve(value);
1254
- };
1255
- child.stdout.on("data", (chunk) => {
1256
- stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1257
- });
1258
- child.stderr.on("data", (chunk) => {
1259
- stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1260
- });
1261
- child.once("error", (error) => {
1262
- rejectOnce(`Failed to start bundled ffmpeg: ${error.message}`);
1263
- });
1264
- child.once("close", (code, signal) => {
1265
- if (timedOut) {
1266
- rejectOnce(`Bundled ffmpeg timed out after ${input.timeoutMs} ms.`);
1267
- return;
1268
- }
1269
- if (code !== 0) {
1270
- rejectOnce(Buffer.concat(stderrChunks).toString("utf8").trim() || `Bundled ffmpeg exited with code ${code}${signal ? ` (${signal})` : ""}.`);
1271
- return;
1272
- }
1273
- const output = Buffer.concat(stdoutChunks);
1274
- if (output.length === 0) {
1275
- rejectOnce("Bundled ffmpeg returned empty audio output.");
1276
- return;
1277
- }
1278
- resolveOnce(new Uint8Array(output));
1279
- });
1280
- child.stdin.on("error", (error) => {
1281
- rejectOnce(`Failed to write audio data to bundled ffmpeg: ${error.message}`);
1282
- });
1283
- child.stdin.write(Buffer.from(input.data));
1284
- child.stdin.end();
1285
- });
1286
- }
1287
- function buildFfmpegArgs(targetFormat) {
1288
- if (targetFormat !== "wav") throw new Error(`Unsupported target format: ${targetFormat}`);
1289
- return [
1290
- "-hide_banner",
1291
- "-loglevel",
1292
- "error",
1293
- "-i",
1294
- "pipe:0",
1295
- "-f",
1296
- "wav",
1297
- "-acodec",
1298
- "pcm_s16le",
1299
- "-ac",
1300
- "1",
1301
- "-ar",
1302
- "16000",
1303
- "pipe:1"
1304
- ];
1305
- }
1306
- function buildTranscodingMessage(sourceFormat, targetFormat, reason) {
1307
- return `Failed to transcode audio from ${sourceFormat} to ${targetFormat}. ${reason}`;
1308
- }
1309
- function replaceExtension(filename, nextExtension) {
1310
- const trimmedFilename = basename(filename).trim();
1311
- if (!trimmedFilename) return `telegram-voice${nextExtension}`;
1312
- const currentExtension = extname(trimmedFilename);
1313
- return currentExtension ? `${trimmedFilename.slice(0, -currentExtension.length)}${nextExtension}` : `${trimmedFilename}${nextExtension}`;
1314
- }
1315
- function toUint8Array$1(data) {
1316
- return data instanceof Uint8Array ? data : new Uint8Array(data);
1317
- }
1318
- function defaultSpawnProcess(command, args, options) {
1319
- return spawn(command, args, options);
1320
- }
1321
- //#endregion
1322
1181
  //#region src/services/voice-transcription/openrouter-voice.client.ts
1323
1182
  var VoiceTranscriptionNotConfiguredError = class extends Error {
1324
1183
  data;
@@ -1356,16 +1215,11 @@ var DisabledVoiceTranscriptionClient = class {
1356
1215
  }
1357
1216
  };
1358
1217
  var OpenRouterVoiceTranscriptionClient = class {
1359
- audioTranscoder;
1360
1218
  model;
1361
1219
  sdk;
1362
1220
  timeoutMs;
1363
1221
  transcriptionPrompt;
1364
- constructor(options, sdk, audioTranscoder = new FfmpegAudioTranscoder({
1365
- ffmpegPath: null,
1366
- timeoutMs: options.timeoutMs
1367
- })) {
1368
- this.audioTranscoder = audioTranscoder;
1222
+ constructor(options, sdk) {
1369
1223
  this.model = options.model;
1370
1224
  this.sdk = sdk;
1371
1225
  this.timeoutMs = options.timeoutMs;
@@ -1378,8 +1232,8 @@ var OpenRouterVoiceTranscriptionClient = class {
1378
1232
  };
1379
1233
  }
1380
1234
  async transcribe(input) {
1381
- const preparedAudio = await prepareAudioForOpenRouter(input, resolveAudioFormat(input.filename, input.mimeType), this.audioTranscoder);
1382
- const audioData = toBase64(preparedAudio.data);
1235
+ const format = resolveAudioFormat(input.filename, input.mimeType);
1236
+ const audioData = toBase64(input.data);
1383
1237
  const prompt = buildTranscriptionPrompt(this.transcriptionPrompt);
1384
1238
  let response;
1385
1239
  try {
@@ -1393,7 +1247,7 @@ var OpenRouterVoiceTranscriptionClient = class {
1393
1247
  type: "input_audio",
1394
1248
  inputAudio: {
1395
1249
  data: audioData,
1396
- format: preparedAudio.format
1250
+ format
1397
1251
  }
1398
1252
  }]
1399
1253
  }],
@@ -1403,53 +1257,23 @@ var OpenRouterVoiceTranscriptionClient = class {
1403
1257
  } }, { timeoutMs: this.timeoutMs });
1404
1258
  } catch (error) {
1405
1259
  throw new VoiceTranscriptionFailedError(buildTranscriptionErrorMessage(error, {
1406
- format: preparedAudio.format,
1260
+ format,
1407
1261
  model: this.model
1408
1262
  }));
1409
1263
  }
1410
1264
  return { text: extractTranscript(response) };
1411
1265
  }
1412
1266
  };
1413
- async function prepareAudioForOpenRouter(input, sourceFormat, audioTranscoder) {
1414
- if (isOpenRouterSupportedAudioFormat(sourceFormat)) return {
1415
- data: toUint8Array(input.data),
1416
- format: sourceFormat
1417
- };
1418
- const transcoded = await audioTranscoder.transcode({
1419
- data: input.data,
1420
- filename: input.filename,
1421
- sourceFormat,
1422
- targetFormat: "wav"
1423
- });
1424
- return {
1425
- data: transcoded.data,
1426
- format: transcoded.format
1427
- };
1428
- }
1429
1267
  var MIME_TYPE_FORMAT_MAP = {
1430
- "audio/aac": "aac",
1431
- "audio/aiff": "aiff",
1432
- "audio/flac": "flac",
1433
- "audio/m4a": "m4a",
1434
1268
  "audio/mp3": "mp3",
1435
- "audio/mp4": "m4a",
1436
1269
  "audio/mpeg": "mp3",
1437
1270
  "audio/ogg": "ogg",
1438
1271
  "audio/wav": "wav",
1439
1272
  "audio/wave": "wav",
1440
- "audio/x-aac": "aac",
1441
- "audio/x-aiff": "aiff",
1442
- "audio/x-flac": "flac",
1443
- "audio/x-m4a": "m4a",
1444
1273
  "audio/x-wav": "wav",
1445
1274
  "audio/vnd.wave": "wav"
1446
1275
  };
1447
1276
  var FILE_EXTENSION_FORMAT_MAP = {
1448
- ".aac": "aac",
1449
- ".aif": "aiff",
1450
- ".aiff": "aiff",
1451
- ".flac": "flac",
1452
- ".m4a": "m4a",
1453
1277
  ".mp3": "mp3",
1454
1278
  ".oga": "ogg",
1455
1279
  ".ogg": "ogg",
@@ -1469,9 +1293,6 @@ function toBase64(data) {
1469
1293
  function toUint8Array(data) {
1470
1294
  return data instanceof Uint8Array ? data : new Uint8Array(data);
1471
1295
  }
1472
- function isOpenRouterSupportedAudioFormat(format) {
1473
- return OPENROUTER_SUPPORTED_AUDIO_FORMATS.includes(format);
1474
- }
1475
1296
  function buildTranscriptionPrompt(transcriptionPrompt) {
1476
1297
  const basePrompt = [
1477
1298
  "Transcribe the provided audio verbatim.",
@@ -2342,7 +2163,6 @@ function resolveExtension(mimeType) {
2342
2163
  }
2343
2164
  //#endregion
2344
2165
  //#region src/app/container.ts
2345
- var require = createRequire(import.meta.url);
2346
2166
  function createAppContainer(config, client) {
2347
2167
  const logger = createOpenCodeAppLogger(client, { level: config.logLevel });
2348
2168
  return createContainer(config, createOpenCodeClientFromSdkClient(client), logger);
@@ -2419,19 +2239,8 @@ function createVoiceTranscriptionClient(config) {
2419
2239
  }, new OpenRouter({
2420
2240
  apiKey: config.apiKey,
2421
2241
  timeoutMs: config.timeoutMs
2422
- }), new FfmpegAudioTranscoder({
2423
- ffmpegPath: loadBundledFfmpegPath(),
2424
- timeoutMs: config.timeoutMs
2425
2242
  })) : new DisabledVoiceTranscriptionClient();
2426
2243
  }
2427
- function loadBundledFfmpegPath() {
2428
- try {
2429
- const ffmpegInstaller = require("@ffmpeg-installer/ffmpeg");
2430
- return typeof ffmpegInstaller.path === "string" && ffmpegInstaller.path.trim().length > 0 ? ffmpegInstaller.path : null;
2431
- } catch {
2432
- return null;
2433
- }
2434
- }
2435
2244
  //#endregion
2436
2245
  //#region src/app/bootstrap.ts
2437
2246
  function bootstrapPluginApp(client, configSource = {}, options = {}) {
@@ -2664,7 +2473,6 @@ var EN_BOT_COPY = {
2664
2473
  structuredOutput: "Structured output validation failed.",
2665
2474
  voiceNotConfigured: "Voice transcription is not configured.",
2666
2475
  voiceDownload: "Failed to download the Telegram voice file.",
2667
- voiceTranscoding: "Voice audio preprocessing failed.",
2668
2476
  voiceTranscription: "Voice transcription failed.",
2669
2477
  voiceEmpty: "Voice transcription returned empty text.",
2670
2478
  voiceUnsupported: "Voice message file is too large or unsupported.",
@@ -2874,7 +2682,6 @@ var ZH_CN_BOT_COPY = {
2874
2682
  structuredOutput: "结构化输出校验失败。",
2875
2683
  voiceNotConfigured: "未配置语音转写服务。",
2876
2684
  voiceDownload: "下载 Telegram 语音文件失败。",
2877
- voiceTranscoding: "语音转码失败。",
2878
2685
  voiceTranscription: "语音转写失败。",
2879
2686
  voiceEmpty: "语音转写结果为空。",
2880
2687
  voiceUnsupported: "语音文件过大或不受支持。",
@@ -3231,10 +3038,6 @@ function normalizeError(error, copy) {
3231
3038
  message: copy.errors.voiceDownload,
3232
3039
  cause: extractMessage(error.data) ?? null
3233
3040
  };
3234
- if (isNamedError(error, "VoiceTranscodingFailedError")) return {
3235
- message: copy.errors.voiceTranscoding,
3236
- cause: extractMessage(error.data) ?? null
3237
- };
3238
3041
  if (isNamedError(error, "VoiceTranscriptionFailedError")) return {
3239
3042
  message: copy.errors.voiceTranscription,
3240
3043
  cause: extractMessage(error.data) ?? null