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/README.ja.md +16 -4
- package/README.md +15 -5
- package/README.zh-CN.md +15 -5
- package/dist/assets/{plugin-config-jkAZYbFW.js → plugin-config-LIr8LS0-.js} +25 -2
- package/dist/assets/plugin-config-LIr8LS0-.js.map +1 -0
- package/dist/cli.js +5 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +71 -66
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/plugin-config-jkAZYbFW.js.map +0 -1
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
2097
|
-
if (
|
|
2098
|
-
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
|
-
|
|
2138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
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
|
|
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: "🖥️ 概览",
|