opencode-tbot 0.1.32 → 0.1.33

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,7 +1,6 @@
1
- import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadAppConfig } from "./assets/plugin-config-jkAZYbFW.js";
1
+ import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadAppConfig } from "./assets/plugin-config-LIr8LS0-.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
- import { parse, printParseErrorCode } from "jsonc-parser";
5
4
  import { randomUUID } from "node:crypto";
6
5
  import { createOpencodeClient } from "@opencode-ai/sdk";
7
6
  import { run } from "@grammyjs/runner";
@@ -919,6 +918,9 @@ var OpenCodeClient = class {
919
918
  this.modelCache.promise = refreshPromise;
920
919
  return refreshPromise;
921
920
  }
921
+ async listConfiguredPlugins() {
922
+ return normalizeConfiguredPluginSpecs((await this.loadConfig()).plugin);
923
+ }
922
924
  async promptSession(input) {
923
925
  const startedAt = Date.now();
924
926
  const promptText = input.prompt?.trim() ?? "";
@@ -1539,6 +1541,19 @@ function normalizePermissionRequest(permission) {
1539
1541
  sessionID
1540
1542
  };
1541
1543
  }
1544
+ function normalizeConfiguredPluginSpecs(value) {
1545
+ if (!Array.isArray(value)) return [];
1546
+ const normalizedPlugins = [];
1547
+ const seenPlugins = /* @__PURE__ */ new Set();
1548
+ for (const item of value) {
1549
+ if (typeof item !== "string") continue;
1550
+ const normalizedItem = item.trim();
1551
+ if (normalizedItem.length === 0 || seenPlugins.has(normalizedItem)) continue;
1552
+ seenPlugins.add(normalizedItem);
1553
+ normalizedPlugins.push(normalizedItem);
1554
+ }
1555
+ return normalizedPlugins;
1556
+ }
1542
1557
  function normalizePermissionPatterns$1(permission) {
1543
1558
  if (Array.isArray(permission.patterns)) return permission.patterns.filter((value) => typeof value === "string");
1544
1559
  if (typeof permission.pattern === "string" && permission.pattern.trim().length > 0) return [permission.pattern];
@@ -1739,7 +1754,7 @@ async function readStateFile(filePath, createDefaultState) {
1739
1754
  const content = await readFile(filePath, "utf8");
1740
1755
  return JSON.parse(content);
1741
1756
  } catch (error) {
1742
- if (isMissingFileError$1(error)) return createDefaultState();
1757
+ if (isMissingFileError(error)) return createDefaultState();
1743
1758
  throw error;
1744
1759
  }
1745
1760
  }
@@ -1752,7 +1767,7 @@ async function writeStateFile(filePath, state) {
1752
1767
  function cloneState(state) {
1753
1768
  return JSON.parse(JSON.stringify(state));
1754
1769
  }
1755
- function isMissingFileError$1(error) {
1770
+ function isMissingFileError(error) {
1756
1771
  return error instanceof Error && "code" in error && error.code === "ENOENT";
1757
1772
  }
1758
1773
  //#endregion
@@ -2056,27 +2071,30 @@ var GetPathUseCase = class {
2056
2071
  //#endregion
2057
2072
  //#region src/use-cases/get-status.usecase.ts
2058
2073
  var GetStatusUseCase = class {
2059
- constructor(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase, listSessionsUseCase, sessionRepo) {
2074
+ constructor(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase, listSessionsUseCase, sessionRepo, configuredPluginReader) {
2060
2075
  this.getHealthUseCase = getHealthUseCase;
2061
2076
  this.getPathUseCase = getPathUseCase;
2062
2077
  this.listLspUseCase = listLspUseCase;
2063
2078
  this.listMcpUseCase = listMcpUseCase;
2064
2079
  this.listSessionsUseCase = listSessionsUseCase;
2065
2080
  this.sessionRepo = sessionRepo;
2081
+ this.configuredPluginReader = configuredPluginReader;
2066
2082
  }
2067
2083
  async execute(input) {
2068
- const [health, path, lsp, mcp] = await Promise.allSettled([
2084
+ const [health, path, lsp, mcp, plugins] = await Promise.allSettled([
2069
2085
  this.getHealthUseCase.execute(),
2070
2086
  this.getPathUseCase.execute(),
2071
2087
  this.listLspUseCase.execute({ chatId: input.chatId }),
2072
- this.listMcpUseCase.execute({ chatId: input.chatId })
2088
+ this.listMcpUseCase.execute({ chatId: input.chatId }),
2089
+ this.configuredPluginReader.listConfiguredPlugins()
2073
2090
  ]);
2074
2091
  const pathResult = mapSettledResult(path);
2075
- const [plugins, workspace] = await Promise.all([loadConfiguredPluginsResult(pathResult), loadWorkspaceStatusResult(input.chatId, pathResult, this.listSessionsUseCase, this.sessionRepo)]);
2092
+ const pluginResult = loadConfiguredPluginsResult(plugins);
2093
+ const workspace = await loadWorkspaceStatusResult(input.chatId, pathResult, this.listSessionsUseCase, this.sessionRepo);
2076
2094
  return {
2077
2095
  health: mapSettledResult(health),
2078
2096
  path: pathResult,
2079
- plugins,
2097
+ plugins: pluginResult,
2080
2098
  workspace,
2081
2099
  lsp: mapSettledResult(lsp),
2082
2100
  mcp: mapSettledResult(mcp)
@@ -2093,49 +2111,14 @@ function mapSettledResult(result) {
2093
2111
  status: "error"
2094
2112
  };
2095
2113
  }
2096
- async function loadConfiguredPluginsResult(path) {
2097
- if (path.status === "error") return {
2098
- error: path.error,
2114
+ function loadConfiguredPluginsResult(result) {
2115
+ if (result.status === "rejected") return {
2116
+ error: result.reason,
2099
2117
  status: "error"
2100
2118
  };
2101
- try {
2102
- return {
2103
- data: await loadConfiguredPlugins(path.data.config),
2104
- status: "ok"
2105
- };
2106
- } catch (error) {
2107
- return {
2108
- error,
2109
- status: "error"
2110
- };
2111
- }
2112
- }
2113
- async function loadConfiguredPlugins(configFilePath) {
2114
- const resolvedConfigFilePath = await resolveOpenCodeConfigFilePath(configFilePath);
2115
- let content;
2116
- try {
2117
- content = await readFile(resolvedConfigFilePath, "utf8");
2118
- } catch (error) {
2119
- if (isMissingFileError(error)) return {
2120
- configFilePath: resolvedConfigFilePath,
2121
- plugins: []
2122
- };
2123
- throw error;
2124
- }
2125
- const parseErrors = [];
2126
- const parsed = parse(content, parseErrors, { allowTrailingComma: true });
2127
- if (parseErrors.length > 0) {
2128
- const errorSummary = parseErrors.map((error) => printParseErrorCode(error.error)).join(", ");
2129
- throw new Error(`Failed to parse ${resolvedConfigFilePath}: ${errorSummary}`);
2130
- }
2131
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {
2132
- configFilePath: resolvedConfigFilePath,
2133
- plugins: []
2134
- };
2135
- const pluginSpecs = Array.isArray(parsed.plugin) ? parsed.plugin : [];
2136
2119
  return {
2137
- configFilePath: resolvedConfigFilePath,
2138
- plugins: [...new Set(pluginSpecs.filter((value) => typeof value === "string").map((value) => value.trim()).filter((value) => value.length > 0))]
2120
+ data: { plugins: normalizeConfiguredPluginLabels(result.value) },
2121
+ status: "ok"
2139
2122
  };
2140
2123
  }
2141
2124
  async function loadWorkspaceStatusResult(chatId, path, listSessionsUseCase, sessionRepo) {
@@ -2178,17 +2161,43 @@ function isUsableWorkspacePath(value) {
2178
2161
  const normalized = value.trim();
2179
2162
  return normalized.length > 0 && normalized !== "/" && normalized !== "\\" && isAbsolute(normalized);
2180
2163
  }
2181
- async function resolveOpenCodeConfigFilePath(configPath) {
2164
+ function normalizeConfiguredPluginLabels(plugins) {
2165
+ const normalizedPlugins = [];
2166
+ const seenPlugins = /* @__PURE__ */ new Set();
2167
+ for (const plugin of plugins) {
2168
+ const normalizedPlugin = normalizeConfiguredPluginLabel(plugin);
2169
+ if (normalizedPlugin.length === 0 || seenPlugins.has(normalizedPlugin)) continue;
2170
+ seenPlugins.add(normalizedPlugin);
2171
+ normalizedPlugins.push(normalizedPlugin);
2172
+ }
2173
+ return normalizedPlugins;
2174
+ }
2175
+ function normalizeConfiguredPluginLabel(plugin) {
2176
+ const normalizedPlugin = plugin.trim();
2177
+ const localPluginName = extractLocalPluginName(normalizedPlugin);
2178
+ return localPluginName ? `${localPluginName} (local plugin)` : normalizedPlugin;
2179
+ }
2180
+ function extractLocalPluginName(plugin) {
2181
+ const filePath = parseFilePluginPath(plugin);
2182
+ if (!filePath) return null;
2183
+ const pathSegments = filePath.split("/").filter((segment) => segment.length > 0);
2184
+ const fileName = pathSegments.at(-1);
2185
+ const parentDirectoryName = pathSegments.at(-2);
2186
+ if (!fileName || parentDirectoryName !== "plugins") return null;
2187
+ const pluginName = fileName.replace(/\.(?:[cm]?js|[cm]?ts)$/iu, "");
2188
+ return pluginName.trim().length > 0 ? pluginName.trim() : null;
2189
+ }
2190
+ function parseFilePluginPath(plugin) {
2191
+ if (!plugin.startsWith("file://")) return null;
2182
2192
  try {
2183
- return (await stat(configPath)).isDirectory() ? join(configPath, "opencode.json") : configPath;
2184
- } catch (error) {
2185
- if (isMissingFileError(error)) return configPath;
2186
- throw error;
2193
+ const fileUrl = new URL(plugin);
2194
+ if (fileUrl.protocol !== "file:") return null;
2195
+ const normalizedPath = decodeURIComponent(fileUrl.pathname).replace(/^\/([A-Za-z]:)/u, "$1").replace(/\\/gu, "/");
2196
+ return normalizedPath.length > 0 ? normalizedPath : null;
2197
+ } catch {
2198
+ return null;
2187
2199
  }
2188
2200
  }
2189
- function isMissingFileError(error) {
2190
- return error instanceof Error && "code" in error && error.code === "ENOENT";
2191
- }
2192
2201
  function isSelectableAgent(agent) {
2193
2202
  return !agent.hidden && agent.mode !== "subagent";
2194
2203
  }
@@ -2798,7 +2807,7 @@ function createContainer(config, opencodeClient, logger) {
2798
2807
  const listLspUseCase = new ListLspUseCase(sessionRepo, opencodeClient);
2799
2808
  const listMcpUseCase = new ListMcpUseCase(sessionRepo, opencodeClient);
2800
2809
  const listSessionsUseCase = new ListSessionsUseCase(sessionRepo, opencodeClient);
2801
- const getStatusUseCase = new GetStatusUseCase(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase, listSessionsUseCase, sessionRepo);
2810
+ const getStatusUseCase = new GetStatusUseCase(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase, listSessionsUseCase, sessionRepo, opencodeClient);
2802
2811
  const listModelsUseCase = new ListModelsUseCase(sessionRepo, opencodeClient);
2803
2812
  const renameSessionUseCase = new RenameSessionUseCase(sessionRepo, opencodeClient, opencodeLogger);
2804
2813
  const sendPromptUseCase = new SendPromptUseCase(sessionRepo, opencodeClient, promptLogger);
@@ -3015,11 +3024,7 @@ async function handleSessionIdle(runtime, event) {
3015
3024
  component: "plugin-event",
3016
3025
  sessionId: event.sessionId
3017
3026
  });
3018
- if (runtime.container.foregroundSessionTracker.clear(event.sessionId)) {
3019
- logPluginEvent(logger, { event: "plugin-event.session.idle.foreground_suppressed" }, "session idle notification suppressed for foreground Telegram session");
3020
- return;
3021
- }
3022
- await notifyBoundChats(runtime, event.sessionId, `Session finished.\n\nSession: ${event.sessionId}`);
3027
+ if (runtime.container.foregroundSessionTracker.clear(event.sessionId)) logPluginEvent(logger, { event: "plugin-event.session.idle.foreground_suppressed" }, "session idle notification suppressed for foreground Telegram session");
3023
3028
  }
3024
3029
  async function handleSessionStatus(runtime, event) {
3025
3030
  if (event.statusType !== "idle") return;
@@ -4369,7 +4374,7 @@ function getStatusLayoutCopy(copy) {
4369
4374
  mcpNotesLabel: "Notes",
4370
4375
  mcpRegistrationRequiredStatus: "🟡",
4371
4376
  mcpTitle: "🔌 MCP",
4372
- noPluginsMessage: "No plugins configured in the OpenCode config.",
4377
+ noPluginsMessage: "No plugins detected in the current OpenCode setup.",
4373
4378
  noneStatus: "⚪",
4374
4379
  openCodeVersionLabel: "OpenCode Version",
4375
4380
  overviewTitle: "🖥️ Overview",
@@ -4390,7 +4395,7 @@ function getStatusLayoutCopy(copy) {
4390
4395
  mcpNotesLabel: "補足",
4391
4396
  mcpRegistrationRequiredStatus: "🟡",
4392
4397
  mcpTitle: "🔌 MCP",
4393
- noPluginsMessage: "現在の OpenCode 設定にはプラグインが設定されていません。",
4398
+ noPluginsMessage: "現在の OpenCode 構成ではプラグインが検出されていません。",
4394
4399
  noneStatus: "⚪",
4395
4400
  openCodeVersionLabel: "OpenCode バージョン",
4396
4401
  overviewTitle: "🖥️ 概要",
@@ -4411,7 +4416,7 @@ function getStatusLayoutCopy(copy) {
4411
4416
  mcpNotesLabel: "说明",
4412
4417
  mcpRegistrationRequiredStatus: "🟡",
4413
4418
  mcpTitle: "🔌 MCP",
4414
- noPluginsMessage: "当前 OpenCode 配置中未配置插件。",
4419
+ noPluginsMessage: "当前 OpenCode 环境中未检测到插件。",
4415
4420
  noneStatus: "⚪",
4416
4421
  openCodeVersionLabel: "OpenCode版本",
4417
4422
  overviewTitle: "🖥️ 概览",