opencode-tbot 0.1.0 โ 0.1.2
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.md +24 -72
- package/README.zh-CN.md +25 -73
- package/dist/assets/{plugin-config-Crgl_PZz.js โ plugin-config-BYsYAzvx.js} +6 -45
- package/dist/assets/plugin-config-BYsYAzvx.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +276 -152
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -3
- package/INSTALL.md +0 -92
- package/dist/assets/plugin-config-Crgl_PZz.js.map +0 -1
package/dist/plugin.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { i as preparePluginConfiguration, s as loadAppConfig } from "./assets/plugin-config-
|
|
2
|
-
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
|
-
import { basename, dirname, extname } from "node:path";
|
|
1
|
+
import { i as preparePluginConfiguration, s as loadAppConfig } from "./assets/plugin-config-BYsYAzvx.js";
|
|
2
|
+
import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, extname, join } from "node:path";
|
|
4
|
+
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
import { OpenRouter } from "@openrouter/sdk";
|
|
6
7
|
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
8
9
|
import { run } from "@grammyjs/runner";
|
|
9
10
|
import { Bot, InlineKeyboard } from "grammy";
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
10
13
|
//#region src/infra/utils/redact.ts
|
|
11
14
|
var REDACTED = "[REDACTED]";
|
|
12
15
|
var DEFAULT_PREVIEW_LENGTH = 160;
|
|
@@ -243,7 +246,14 @@ var OpenCodeClient = class {
|
|
|
243
246
|
this.fetchFn = fetchFn;
|
|
244
247
|
}
|
|
245
248
|
async getHealth() {
|
|
246
|
-
|
|
249
|
+
const healthEndpoint = this.client.global?.health;
|
|
250
|
+
if (typeof healthEndpoint === "function") return unwrapSdkData(await healthEndpoint.call(this.client.global, SDK_OPTIONS));
|
|
251
|
+
const rawClient = getRawSdkClient(this.client);
|
|
252
|
+
if (!rawClient) throw new Error("OpenCode SDK client does not expose a compatible health endpoint.");
|
|
253
|
+
return unwrapSdkData(await rawClient.get({
|
|
254
|
+
url: "/global/health",
|
|
255
|
+
...SDK_OPTIONS
|
|
256
|
+
}));
|
|
247
257
|
}
|
|
248
258
|
async abortSession(sessionId) {
|
|
249
259
|
return unwrapSdkData(await this.client.session.abort({ sessionID: sessionId }, SDK_OPTIONS));
|
|
@@ -490,6 +500,9 @@ function unwrapSdkData(response) {
|
|
|
490
500
|
if (response && typeof response === "object" && "data" in response) return response.data;
|
|
491
501
|
return response;
|
|
492
502
|
}
|
|
503
|
+
function getRawSdkClient(client) {
|
|
504
|
+
return client.client ?? client._client ?? null;
|
|
505
|
+
}
|
|
493
506
|
async function resolveProviderAvailability(config, fetchFn) {
|
|
494
507
|
const configuredProviders = Object.entries(config.provider ?? {});
|
|
495
508
|
const availabilityEntries = await Promise.all(configuredProviders.map(async ([providerId, providerConfig]) => [providerId, await fetchProviderAvailableModelIds(providerConfig, fetchFn)]));
|
|
@@ -582,7 +595,7 @@ async function readStateFile(filePath, createDefaultState) {
|
|
|
582
595
|
const content = await readFile(filePath, "utf8");
|
|
583
596
|
return JSON.parse(content);
|
|
584
597
|
} catch (error) {
|
|
585
|
-
if (isMissingFileError(error)) return createDefaultState();
|
|
598
|
+
if (isMissingFileError$1(error)) return createDefaultState();
|
|
586
599
|
throw error;
|
|
587
600
|
}
|
|
588
601
|
}
|
|
@@ -595,7 +608,7 @@ async function writeStateFile(filePath, state) {
|
|
|
595
608
|
function cloneState(state) {
|
|
596
609
|
return JSON.parse(JSON.stringify(state));
|
|
597
610
|
}
|
|
598
|
-
function isMissingFileError(error) {
|
|
611
|
+
function isMissingFileError$1(error) {
|
|
599
612
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
600
613
|
}
|
|
601
614
|
//#endregion
|
|
@@ -949,11 +962,13 @@ var GetPathUseCase = class {
|
|
|
949
962
|
//#endregion
|
|
950
963
|
//#region src/use-cases/get-status.usecase.ts
|
|
951
964
|
var GetStatusUseCase = class {
|
|
952
|
-
constructor(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase) {
|
|
965
|
+
constructor(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase, listSessionsUseCase, sessionRepo) {
|
|
953
966
|
this.getHealthUseCase = getHealthUseCase;
|
|
954
967
|
this.getPathUseCase = getPathUseCase;
|
|
955
968
|
this.listLspUseCase = listLspUseCase;
|
|
956
969
|
this.listMcpUseCase = listMcpUseCase;
|
|
970
|
+
this.listSessionsUseCase = listSessionsUseCase;
|
|
971
|
+
this.sessionRepo = sessionRepo;
|
|
957
972
|
}
|
|
958
973
|
async execute(input) {
|
|
959
974
|
const [health, path, lsp, mcp] = await Promise.allSettled([
|
|
@@ -962,9 +977,13 @@ var GetStatusUseCase = class {
|
|
|
962
977
|
this.listLspUseCase.execute({ chatId: input.chatId }),
|
|
963
978
|
this.listMcpUseCase.execute({ chatId: input.chatId })
|
|
964
979
|
]);
|
|
980
|
+
const pathResult = mapSettledResult(path);
|
|
981
|
+
const [plugins, workspace] = await Promise.all([loadConfiguredPluginsResult(pathResult), loadWorkspaceStatusResult(input.chatId, pathResult, this.listSessionsUseCase, this.sessionRepo)]);
|
|
965
982
|
return {
|
|
966
983
|
health: mapSettledResult(health),
|
|
967
|
-
path:
|
|
984
|
+
path: pathResult,
|
|
985
|
+
plugins,
|
|
986
|
+
workspace,
|
|
968
987
|
lsp: mapSettledResult(lsp),
|
|
969
988
|
mcp: mapSettledResult(mcp)
|
|
970
989
|
};
|
|
@@ -980,6 +999,88 @@ function mapSettledResult(result) {
|
|
|
980
999
|
status: "error"
|
|
981
1000
|
};
|
|
982
1001
|
}
|
|
1002
|
+
async function loadConfiguredPluginsResult(path) {
|
|
1003
|
+
if (path.status === "error") return {
|
|
1004
|
+
error: path.error,
|
|
1005
|
+
status: "error"
|
|
1006
|
+
};
|
|
1007
|
+
try {
|
|
1008
|
+
return {
|
|
1009
|
+
data: await loadConfiguredPlugins(path.data.config),
|
|
1010
|
+
status: "ok"
|
|
1011
|
+
};
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
return {
|
|
1014
|
+
error,
|
|
1015
|
+
status: "error"
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
async function loadConfiguredPlugins(configFilePath) {
|
|
1020
|
+
const resolvedConfigFilePath = await resolveOpenCodeConfigFilePath(configFilePath);
|
|
1021
|
+
let content;
|
|
1022
|
+
try {
|
|
1023
|
+
content = await readFile(resolvedConfigFilePath, "utf8");
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
if (isMissingFileError(error)) return {
|
|
1026
|
+
configFilePath: resolvedConfigFilePath,
|
|
1027
|
+
plugins: []
|
|
1028
|
+
};
|
|
1029
|
+
throw error;
|
|
1030
|
+
}
|
|
1031
|
+
const parseErrors = [];
|
|
1032
|
+
const parsed = parse(content, parseErrors, { allowTrailingComma: true });
|
|
1033
|
+
if (parseErrors.length > 0) {
|
|
1034
|
+
const errorSummary = parseErrors.map((error) => printParseErrorCode(error.error)).join(", ");
|
|
1035
|
+
throw new Error(`Failed to parse ${resolvedConfigFilePath}: ${errorSummary}`);
|
|
1036
|
+
}
|
|
1037
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {
|
|
1038
|
+
configFilePath: resolvedConfigFilePath,
|
|
1039
|
+
plugins: []
|
|
1040
|
+
};
|
|
1041
|
+
const pluginSpecs = Array.isArray(parsed.plugin) ? parsed.plugin : [];
|
|
1042
|
+
return {
|
|
1043
|
+
configFilePath: resolvedConfigFilePath,
|
|
1044
|
+
plugins: [...new Set(pluginSpecs.filter((value) => typeof value === "string").map((value) => value.trim()).filter((value) => value.length > 0))]
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
async function loadWorkspaceStatusResult(chatId, path, listSessionsUseCase, sessionRepo) {
|
|
1048
|
+
if (path.status === "error") return {
|
|
1049
|
+
error: path.error,
|
|
1050
|
+
status: "error"
|
|
1051
|
+
};
|
|
1052
|
+
const binding = await sessionRepo.getByChatId(chatId);
|
|
1053
|
+
let currentProject = path.data.directory;
|
|
1054
|
+
let currentSession = binding?.sessionId ?? null;
|
|
1055
|
+
try {
|
|
1056
|
+
const sessions = await listSessionsUseCase.execute({ chatId });
|
|
1057
|
+
currentProject = sessions.currentDirectory;
|
|
1058
|
+
currentSession = sessions.currentSessionId ? formatSessionStatusLabel(sessions.sessions.find((session) => session.id === sessions.currentSessionId) ?? null, sessions.currentSessionId) : null;
|
|
1059
|
+
} catch {}
|
|
1060
|
+
return {
|
|
1061
|
+
data: {
|
|
1062
|
+
currentProject,
|
|
1063
|
+
currentSession
|
|
1064
|
+
},
|
|
1065
|
+
status: "ok"
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
function formatSessionStatusLabel(session, fallbackId) {
|
|
1069
|
+
if (!session) return fallbackId;
|
|
1070
|
+
const title = session.title.trim() || session.slug || session.id;
|
|
1071
|
+
return title === session.slug ? title : `${title} (${session.slug})`;
|
|
1072
|
+
}
|
|
1073
|
+
async function resolveOpenCodeConfigFilePath(configPath) {
|
|
1074
|
+
try {
|
|
1075
|
+
return (await stat(configPath)).isDirectory() ? join(configPath, "opencode.json") : configPath;
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
if (isMissingFileError(error)) return configPath;
|
|
1078
|
+
throw error;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function isMissingFileError(error) {
|
|
1082
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1083
|
+
}
|
|
983
1084
|
function resolveSelectedAgent(agents, requestedAgentName) {
|
|
984
1085
|
const visibleAgents = agents.filter((agent) => !agent.hidden);
|
|
985
1086
|
if (requestedAgentName) {
|
|
@@ -1499,9 +1600,9 @@ function createContainer(config, opencodeClient, logger) {
|
|
|
1499
1600
|
const listAgentsUseCase = new ListAgentsUseCase(sessionRepo, opencodeClient);
|
|
1500
1601
|
const listLspUseCase = new ListLspUseCase(sessionRepo, opencodeClient);
|
|
1501
1602
|
const listMcpUseCase = new ListMcpUseCase(sessionRepo, opencodeClient);
|
|
1502
|
-
const getStatusUseCase = new GetStatusUseCase(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase);
|
|
1503
|
-
const listModelsUseCase = new ListModelsUseCase(sessionRepo, opencodeClient);
|
|
1504
1603
|
const listSessionsUseCase = new ListSessionsUseCase(sessionRepo, opencodeClient);
|
|
1604
|
+
const getStatusUseCase = new GetStatusUseCase(getHealthUseCase, getPathUseCase, listLspUseCase, listMcpUseCase, listSessionsUseCase, sessionRepo);
|
|
1605
|
+
const listModelsUseCase = new ListModelsUseCase(sessionRepo, opencodeClient);
|
|
1505
1606
|
const renameSessionUseCase = new RenameSessionUseCase(sessionRepo, opencodeClient, logger);
|
|
1506
1607
|
const sendPromptUseCase = new SendPromptUseCase(sessionRepo, opencodeClient, logger, foregroundSessionTracker);
|
|
1507
1608
|
const switchAgentUseCase = new SwitchAgentUseCase(sessionRepo, opencodeClient, logger);
|
|
@@ -2504,6 +2605,23 @@ function stringifyUnknown(value) {
|
|
|
2504
2605
|
}
|
|
2505
2606
|
}
|
|
2506
2607
|
//#endregion
|
|
2608
|
+
//#region src/app/package-info.ts
|
|
2609
|
+
var OPENCODE_TBOT_VERSION = resolvePackageVersion();
|
|
2610
|
+
function resolvePackageVersion() {
|
|
2611
|
+
let directory = dirname(fileURLToPath(import.meta.url));
|
|
2612
|
+
while (true) {
|
|
2613
|
+
const packageFilePath = join(directory, "package.json");
|
|
2614
|
+
if (existsSync(packageFilePath)) try {
|
|
2615
|
+
const parsed = JSON.parse(readFileSync(packageFilePath, "utf8"));
|
|
2616
|
+
if (typeof parsed.version === "string" && parsed.version.trim().length > 0) return parsed.version;
|
|
2617
|
+
} catch {}
|
|
2618
|
+
const parentDirectory = dirname(directory);
|
|
2619
|
+
if (parentDirectory === directory) break;
|
|
2620
|
+
directory = parentDirectory;
|
|
2621
|
+
}
|
|
2622
|
+
return "unknown";
|
|
2623
|
+
}
|
|
2624
|
+
//#endregion
|
|
2507
2625
|
//#region src/bot/presenters/message.presenter.ts
|
|
2508
2626
|
var VARIANT_ORDER = [
|
|
2509
2627
|
"minimal",
|
|
@@ -2517,24 +2635,22 @@ var VARIANT_ORDER = [
|
|
|
2517
2635
|
function presentStatusMessage(input, copy = BOT_COPY) {
|
|
2518
2636
|
const layout = getStatusLayoutCopy(copy);
|
|
2519
2637
|
const sections = [
|
|
2520
|
-
presentStatusPlainSection(layout.
|
|
2638
|
+
presentStatusPlainSection(layout.overviewTitle, presentStatusPlainOverviewLines(input, copy, layout)),
|
|
2521
2639
|
presentStatusPlainSection(layout.workspaceTitle, presentStatusPlainWorkspaceLines(input, copy, layout)),
|
|
2640
|
+
presentStatusPlainSection(layout.pluginsTitle, presentStatusPlainPluginLines(input, copy, layout)),
|
|
2522
2641
|
presentStatusPlainSection(layout.mcpTitle, presentStatusPlainMcpLines(input, copy, layout)),
|
|
2523
|
-
presentStatusPlainSection(layout.lspTitle, presentStatusPlainLspLines(input, copy, layout))
|
|
2524
|
-
layout.divider,
|
|
2525
|
-
`${layout.lastUpdatedLabel}: ${formatStatusDate(/* @__PURE__ */ new Date())}`
|
|
2642
|
+
presentStatusPlainSection(layout.lspTitle, presentStatusPlainLspLines(input, copy, layout))
|
|
2526
2643
|
];
|
|
2527
2644
|
return presentStatusSections(layout.pageTitle, sections);
|
|
2528
2645
|
}
|
|
2529
2646
|
function presentStatusMarkdownMessage(input, copy = BOT_COPY) {
|
|
2530
2647
|
const layout = getStatusLayoutCopy(copy);
|
|
2531
2648
|
const sections = [
|
|
2532
|
-
presentStatusMarkdownSection(layout.
|
|
2649
|
+
presentStatusMarkdownSection(layout.overviewTitle, presentStatusMarkdownOverviewLines(input, copy, layout)),
|
|
2533
2650
|
presentStatusMarkdownSection(layout.workspaceTitle, presentStatusMarkdownWorkspaceLines(input, copy, layout)),
|
|
2651
|
+
presentStatusMarkdownSection(layout.pluginsTitle, presentStatusMarkdownPluginLines(input, copy, layout)),
|
|
2534
2652
|
presentStatusMarkdownSection(layout.mcpTitle, presentStatusMarkdownMcpLines(input, copy, layout)),
|
|
2535
|
-
presentStatusMarkdownSection(layout.lspTitle, presentStatusMarkdownLspLines(input, copy, layout))
|
|
2536
|
-
layout.divider,
|
|
2537
|
-
`_${layout.lastUpdatedLabel}: ${formatStatusDate(/* @__PURE__ */ new Date())}_`
|
|
2653
|
+
presentStatusMarkdownSection(layout.lspTitle, presentStatusMarkdownLspLines(input, copy, layout))
|
|
2538
2654
|
];
|
|
2539
2655
|
return presentStatusSections(`# ${layout.pageTitle}`, sections);
|
|
2540
2656
|
}
|
|
@@ -2551,205 +2667,213 @@ function presentStatusPlainSection(title, lines) {
|
|
|
2551
2667
|
function presentStatusMarkdownSection(title, lines) {
|
|
2552
2668
|
return [`## ${title}`, ...lines].join("\n");
|
|
2553
2669
|
}
|
|
2554
|
-
function
|
|
2555
|
-
|
|
2556
|
-
|
|
2670
|
+
function presentStatusPlainOverviewLines(input, copy, layout) {
|
|
2671
|
+
const lines = [presentPlainStatusBullet(layout.connectivityLabel, input.health.status === "error" ? layout.errorStatus : formatHealthBadge(input.health.data.healthy, layout))];
|
|
2672
|
+
if (input.health.status === "error") return [
|
|
2673
|
+
...lines,
|
|
2674
|
+
...presentStatusPlainErrorDetailLines(input.health.error, copy, layout),
|
|
2675
|
+
presentPlainStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION)
|
|
2676
|
+
];
|
|
2677
|
+
return [
|
|
2678
|
+
...lines,
|
|
2679
|
+
presentPlainStatusBullet(layout.openCodeVersionLabel, input.health.data.version),
|
|
2680
|
+
presentPlainStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION)
|
|
2681
|
+
];
|
|
2557
2682
|
}
|
|
2558
|
-
function
|
|
2559
|
-
|
|
2560
|
-
|
|
2683
|
+
function presentStatusMarkdownOverviewLines(input, copy, layout) {
|
|
2684
|
+
const lines = [presentMarkdownStatusBullet(layout.connectivityLabel, input.health.status === "error" ? layout.errorStatus : formatHealthBadge(input.health.data.healthy, layout))];
|
|
2685
|
+
if (input.health.status === "error") return [
|
|
2686
|
+
...lines,
|
|
2687
|
+
...presentStatusMarkdownErrorDetailLines(input.health.error, copy, layout),
|
|
2688
|
+
presentMarkdownStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION)
|
|
2689
|
+
];
|
|
2690
|
+
return [
|
|
2691
|
+
...lines,
|
|
2692
|
+
presentMarkdownStatusBullet(layout.openCodeVersionLabel, input.health.data.version),
|
|
2693
|
+
presentMarkdownStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION)
|
|
2694
|
+
];
|
|
2561
2695
|
}
|
|
2562
2696
|
function presentStatusPlainWorkspaceLines(input, copy, layout) {
|
|
2563
|
-
if (input.
|
|
2564
|
-
return [presentPlainStatusBullet(layout.
|
|
2697
|
+
if (input.workspace.status === "error") return presentStatusPlainErrorLines(input.workspace.error, copy, layout);
|
|
2698
|
+
return [presentPlainStatusBullet(layout.currentProjectLabel, input.workspace.data.currentProject), presentPlainStatusBullet(layout.currentSessionLabel, input.workspace.data.currentSession ?? copy.common.notSelected)];
|
|
2565
2699
|
}
|
|
2566
2700
|
function presentStatusMarkdownWorkspaceLines(input, copy, layout) {
|
|
2567
|
-
if (input.
|
|
2568
|
-
return [presentMarkdownStatusBullet(layout.
|
|
2701
|
+
if (input.workspace.status === "error") return presentStatusMarkdownErrorLines(input.workspace.error, copy, layout);
|
|
2702
|
+
return [presentMarkdownStatusBullet(layout.currentProjectLabel, input.workspace.data.currentProject, { codeValue: true }), presentMarkdownStatusBullet(layout.currentSessionLabel, input.workspace.data.currentSession ?? copy.common.notSelected)];
|
|
2703
|
+
}
|
|
2704
|
+
function presentStatusPlainPluginLines(input, copy, layout) {
|
|
2705
|
+
if (input.plugins.status === "error") return presentStatusPlainErrorLines(input.plugins.error, copy, layout);
|
|
2706
|
+
if (input.plugins.data.plugins.length === 0) return [...presentPlainEmptyStatusLines(layout.noPluginsMessage, layout)];
|
|
2707
|
+
return [`- ${layout.configuredPluginsLabel}:`, ...input.plugins.data.plugins.map((plugin) => ` - ${plugin}`)];
|
|
2708
|
+
}
|
|
2709
|
+
function presentStatusMarkdownPluginLines(input, copy, layout) {
|
|
2710
|
+
if (input.plugins.status === "error") return presentStatusMarkdownErrorLines(input.plugins.error, copy, layout);
|
|
2711
|
+
if (input.plugins.data.plugins.length === 0) return [...presentMarkdownEmptyStatusLines(layout.noPluginsMessage, layout)];
|
|
2712
|
+
return [`- **${layout.configuredPluginsLabel}:**`, ...input.plugins.data.plugins.map((plugin) => ` - \`${plugin}\``)];
|
|
2569
2713
|
}
|
|
2570
2714
|
function presentStatusPlainLspLines(input, copy, layout) {
|
|
2571
|
-
return
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
layout.
|
|
2575
|
-
|
|
2715
|
+
if (input.lsp.status === "error") return presentStatusPlainErrorLines(input.lsp.error, copy, layout);
|
|
2716
|
+
if (input.lsp.data.statuses.length === 0) return presentPlainEmptyStatusLines(copy.lsp.none, layout);
|
|
2717
|
+
return input.lsp.data.statuses.flatMap((status) => presentPlainStatusGroup(status.name, [{
|
|
2718
|
+
label: layout.statusLabel,
|
|
2719
|
+
value: formatLspStatusBadge(status)
|
|
2720
|
+
}, {
|
|
2721
|
+
codeValue: !!status.root,
|
|
2722
|
+
label: layout.rootLabel,
|
|
2723
|
+
value: status.root || "-"
|
|
2724
|
+
}]));
|
|
2576
2725
|
}
|
|
2577
2726
|
function presentStatusMarkdownLspLines(input, copy, layout) {
|
|
2578
|
-
return
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
layout.
|
|
2582
|
-
|
|
2727
|
+
if (input.lsp.status === "error") return presentStatusMarkdownErrorLines(input.lsp.error, copy, layout);
|
|
2728
|
+
if (input.lsp.data.statuses.length === 0) return presentMarkdownEmptyStatusLines(copy.lsp.none, layout);
|
|
2729
|
+
return input.lsp.data.statuses.flatMap((status) => presentMarkdownStatusGroup(status.name, [{
|
|
2730
|
+
label: layout.statusLabel,
|
|
2731
|
+
value: formatLspStatusBadge(status)
|
|
2732
|
+
}, {
|
|
2733
|
+
codeValue: !!status.root,
|
|
2734
|
+
label: layout.rootLabel,
|
|
2735
|
+
value: status.root || "-"
|
|
2736
|
+
}]));
|
|
2583
2737
|
}
|
|
2584
2738
|
function presentStatusPlainMcpLines(input, copy, layout) {
|
|
2585
|
-
return
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
layout.
|
|
2589
|
-
|
|
2739
|
+
if (input.mcp.status === "error") return presentStatusPlainErrorLines(input.mcp.error, copy, layout);
|
|
2740
|
+
if (input.mcp.data.statuses.length === 0) return presentPlainEmptyStatusLines(copy.mcp.none, layout);
|
|
2741
|
+
return input.mcp.data.statuses.flatMap(({ name, status }) => presentPlainStatusGroup(name, [{
|
|
2742
|
+
label: layout.statusLabel,
|
|
2743
|
+
value: formatMcpStatusBadge(status, layout)
|
|
2744
|
+
}, {
|
|
2745
|
+
label: layout.mcpNotesLabel,
|
|
2746
|
+
value: formatMcpStatusNotes(status, copy, layout)
|
|
2747
|
+
}]));
|
|
2590
2748
|
}
|
|
2591
2749
|
function presentStatusMarkdownMcpLines(input, copy, layout) {
|
|
2592
|
-
return
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
layout.
|
|
2596
|
-
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
layout.errorStatus,
|
|
2602
|
-
flattenStatusError(input.lsp.error, copy)
|
|
2603
|
-
]];
|
|
2604
|
-
if (input.lsp.data.statuses.length === 0) return [[
|
|
2605
|
-
"-",
|
|
2606
|
-
layout.noneStatus,
|
|
2607
|
-
copy.lsp.none
|
|
2608
|
-
]];
|
|
2609
|
-
return input.lsp.data.statuses.map((status) => [
|
|
2610
|
-
status.name,
|
|
2611
|
-
formatLspStatusBadge(status, copy),
|
|
2612
|
-
status.root || "-"
|
|
2613
|
-
]);
|
|
2614
|
-
}
|
|
2615
|
-
function buildMcpStatusRows(input, copy, layout) {
|
|
2616
|
-
if (input.mcp.status === "error") return [[
|
|
2617
|
-
"-",
|
|
2618
|
-
layout.errorStatus,
|
|
2619
|
-
flattenStatusError(input.mcp.error, copy)
|
|
2620
|
-
]];
|
|
2621
|
-
if (input.mcp.data.statuses.length === 0) return [[
|
|
2622
|
-
"-",
|
|
2623
|
-
layout.noneStatus,
|
|
2624
|
-
copy.mcp.none
|
|
2625
|
-
]];
|
|
2626
|
-
return input.mcp.data.statuses.map(({ name, status }) => [
|
|
2627
|
-
name,
|
|
2628
|
-
formatMcpStatusBadge(status, copy, layout),
|
|
2629
|
-
formatMcpStatusNotes(status, layout)
|
|
2630
|
-
]);
|
|
2631
|
-
}
|
|
2632
|
-
function buildPlainStatusTable(headers, rows) {
|
|
2633
|
-
return [
|
|
2634
|
-
formatPlainTableRow(headers),
|
|
2635
|
-
formatPlainTableSeparator(headers.length),
|
|
2636
|
-
...rows.map((row) => formatPlainTableRow(row))
|
|
2637
|
-
];
|
|
2750
|
+
if (input.mcp.status === "error") return presentStatusMarkdownErrorLines(input.mcp.error, copy, layout);
|
|
2751
|
+
if (input.mcp.data.statuses.length === 0) return presentMarkdownEmptyStatusLines(copy.mcp.none, layout);
|
|
2752
|
+
return input.mcp.data.statuses.flatMap(({ name, status }) => presentMarkdownStatusGroup(name, [{
|
|
2753
|
+
label: layout.statusLabel,
|
|
2754
|
+
value: formatMcpStatusBadge(status, layout)
|
|
2755
|
+
}, {
|
|
2756
|
+
label: layout.mcpNotesLabel,
|
|
2757
|
+
value: formatMcpStatusNotes(status, copy, layout)
|
|
2758
|
+
}], { codeName: true }));
|
|
2638
2759
|
}
|
|
2639
|
-
function
|
|
2640
|
-
return [
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
];
|
|
2760
|
+
function presentStatusPlainErrorLines(error, copy, layout) {
|
|
2761
|
+
return [presentPlainStatusBullet(layout.statusLabel, layout.errorStatus), ...presentStatusPlainErrorDetailLines(error, copy, layout)];
|
|
2762
|
+
}
|
|
2763
|
+
function presentStatusPlainErrorDetailLines(error, copy, layout) {
|
|
2764
|
+
return splitStatusLines(presentError(error, copy)).map((line) => presentPlainStatusBullet(layout.detailsLabel, line));
|
|
2645
2765
|
}
|
|
2646
|
-
function
|
|
2647
|
-
return
|
|
2766
|
+
function presentStatusMarkdownErrorLines(error, copy, layout) {
|
|
2767
|
+
return [presentMarkdownStatusBullet(layout.statusLabel, layout.errorStatus), ...presentStatusMarkdownErrorDetailLines(error, copy, layout)];
|
|
2648
2768
|
}
|
|
2649
|
-
function
|
|
2650
|
-
return
|
|
2769
|
+
function presentStatusMarkdownErrorDetailLines(error, copy, layout) {
|
|
2770
|
+
return splitStatusLines(presentError(error, copy)).map((line) => presentMarkdownStatusBullet(layout.detailsLabel, line));
|
|
2651
2771
|
}
|
|
2652
|
-
function
|
|
2653
|
-
return
|
|
2772
|
+
function presentPlainEmptyStatusLines(message, layout) {
|
|
2773
|
+
return [presentPlainStatusBullet(layout.statusLabel, layout.noneStatus), presentPlainStatusBullet(layout.detailsLabel, message)];
|
|
2654
2774
|
}
|
|
2655
|
-
function
|
|
2656
|
-
|
|
2657
|
-
return [presentPlainStatusBullet(layout.statusLabel, layout.errorStatus), ...detailLines.map((line) => presentPlainStatusBullet(layout.detailsLabel, line))];
|
|
2775
|
+
function presentMarkdownEmptyStatusLines(message, layout) {
|
|
2776
|
+
return [presentMarkdownStatusBullet(layout.statusLabel, layout.noneStatus), presentMarkdownStatusBullet(layout.detailsLabel, message)];
|
|
2658
2777
|
}
|
|
2659
|
-
function
|
|
2660
|
-
|
|
2661
|
-
return [presentMarkdownStatusBullet(layout.statusLabel, layout.errorStatus), ...detailLines.map((line) => presentMarkdownStatusBullet(layout.detailsLabel, line))];
|
|
2778
|
+
function presentPlainStatusGroup(name, details) {
|
|
2779
|
+
return [`- ${normalizeStatusInlineValue(name)}`, ...details.map((detail) => ` - ${detail.label}: ${formatStatusValue(detail.value)}`)];
|
|
2662
2780
|
}
|
|
2663
|
-
function
|
|
2664
|
-
return
|
|
2781
|
+
function presentMarkdownStatusGroup(name, details, options = {}) {
|
|
2782
|
+
return [`- ${options.codeName ? `\`${normalizeStatusInlineValue(name)}\`` : `**${normalizeStatusInlineValue(name)}**`}`, ...details.map((detail) => detail.codeValue ? ` - **${detail.label}:** \`${formatStatusValue(detail.value)}\`` : ` - **${detail.label}:** ${formatStatusValue(detail.value)}`)];
|
|
2665
2783
|
}
|
|
2666
2784
|
function presentPlainStatusBullet(label, value) {
|
|
2667
|
-
return `- ${label}: ${value}`;
|
|
2785
|
+
return `- ${label}: ${formatStatusValue(value)}`;
|
|
2668
2786
|
}
|
|
2669
2787
|
function presentMarkdownStatusBullet(label, value, options = {}) {
|
|
2670
|
-
return options.codeValue ? `- **${label}:** \`${value}\`` : `- **${label}:** ${value}`;
|
|
2788
|
+
return options.codeValue ? `- **${label}:** \`${formatStatusValue(value)}\`` : `- **${label}:** ${formatStatusValue(value)}`;
|
|
2671
2789
|
}
|
|
2672
2790
|
function splitStatusLines(text) {
|
|
2673
2791
|
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
2674
2792
|
}
|
|
2675
2793
|
function formatHealthBadge(healthy, layout) {
|
|
2676
|
-
return healthy ?
|
|
2794
|
+
return healthy ? "๐ข" : layout.errorStatus;
|
|
2677
2795
|
}
|
|
2678
|
-
function formatLspStatusBadge(status
|
|
2796
|
+
function formatLspStatusBadge(status) {
|
|
2679
2797
|
switch (status.status) {
|
|
2680
|
-
case "connected": return
|
|
2681
|
-
case "error": return
|
|
2798
|
+
case "connected": return "๐ข";
|
|
2799
|
+
case "error": return "๐ด";
|
|
2682
2800
|
}
|
|
2683
2801
|
return status.status;
|
|
2684
2802
|
}
|
|
2685
|
-
function formatMcpStatusBadge(status,
|
|
2803
|
+
function formatMcpStatusBadge(status, layout) {
|
|
2686
2804
|
switch (status.status) {
|
|
2687
|
-
case "connected": return
|
|
2688
|
-
case "disabled": return
|
|
2689
|
-
case "needs_auth": return
|
|
2805
|
+
case "connected": return "๐ข";
|
|
2806
|
+
case "disabled": return "โช";
|
|
2807
|
+
case "needs_auth": return "๐ก";
|
|
2690
2808
|
case "failed": return layout.mcpFailedStatus;
|
|
2691
2809
|
case "needs_client_registration": return layout.mcpRegistrationRequiredStatus;
|
|
2692
2810
|
}
|
|
2693
2811
|
return status;
|
|
2694
2812
|
}
|
|
2695
|
-
function formatMcpStatusNotes(status, layout) {
|
|
2813
|
+
function formatMcpStatusNotes(status, copy, layout) {
|
|
2696
2814
|
switch (status.status) {
|
|
2697
2815
|
case "connected": return layout.okLabel;
|
|
2698
|
-
case "disabled": return
|
|
2699
|
-
case "needs_auth": return
|
|
2816
|
+
case "disabled": return copy.mcp.disabled;
|
|
2817
|
+
case "needs_auth": return copy.mcp.needsAuth;
|
|
2700
2818
|
case "failed": return status.error;
|
|
2701
2819
|
case "needs_client_registration": return status.error;
|
|
2702
2820
|
}
|
|
2703
2821
|
return status;
|
|
2704
2822
|
}
|
|
2705
|
-
function
|
|
2706
|
-
|
|
2823
|
+
function formatStatusValue(value) {
|
|
2824
|
+
const normalized = value.replace(/\r\n?/g, "\n").split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join(" / ");
|
|
2825
|
+
return normalized.length > 0 ? normalized : "-";
|
|
2826
|
+
}
|
|
2827
|
+
function normalizeStatusInlineValue(value) {
|
|
2828
|
+
return formatStatusValue(value);
|
|
2707
2829
|
}
|
|
2708
2830
|
function getStatusLayoutCopy(copy) {
|
|
2709
2831
|
if (copy.systemStatus.title === BOT_COPY.systemStatus.title) return {
|
|
2710
|
-
|
|
2832
|
+
connectivityLabel: "Connectivity",
|
|
2833
|
+
configuredPluginsLabel: "Configured Plugins",
|
|
2834
|
+
currentProjectLabel: "Current Project",
|
|
2835
|
+
currentSessionLabel: "Current Session",
|
|
2711
2836
|
detailsLabel: "Details",
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
languageLabel: "Language",
|
|
2716
|
-
lastUpdatedLabel: "Last updated",
|
|
2717
|
-
lspTitle: "๐ง LSP (Language Server)",
|
|
2837
|
+
errorStatus: "๐ด",
|
|
2838
|
+
lspTitle: "๐ง LSP",
|
|
2839
|
+
mcpFailedStatus: "๐ด",
|
|
2718
2840
|
mcpNotesLabel: "Notes",
|
|
2719
|
-
mcpRegistrationRequiredStatus: "๐ก
|
|
2720
|
-
mcpTitle: "๐ MCP
|
|
2721
|
-
|
|
2722
|
-
noneStatus: "โช
|
|
2841
|
+
mcpRegistrationRequiredStatus: "๐ก",
|
|
2842
|
+
mcpTitle: "๐ MCP",
|
|
2843
|
+
noPluginsMessage: "No plugins configured in the OpenCode config.",
|
|
2844
|
+
noneStatus: "โช",
|
|
2845
|
+
openCodeVersionLabel: "OpenCode Version",
|
|
2723
2846
|
okLabel: "OK",
|
|
2847
|
+
overviewTitle: "๐ฅ๏ธ Overview",
|
|
2724
2848
|
pageTitle: "๐ Service Status",
|
|
2849
|
+
pluginsTitle: "๐งฉ Plugins",
|
|
2725
2850
|
rootLabel: "Root",
|
|
2726
|
-
serverTitle: "๐ฅ๏ธ Server",
|
|
2727
|
-
serviceLabel: "Service",
|
|
2728
2851
|
statusLabel: "Status",
|
|
2729
|
-
|
|
2852
|
+
tbotVersionLabel: "opencode-tbot Version",
|
|
2730
2853
|
workspaceTitle: "๐ Workspace"
|
|
2731
2854
|
};
|
|
2732
2855
|
return {
|
|
2733
|
-
|
|
2856
|
+
connectivityLabel: "่ฟ้ๆง",
|
|
2857
|
+
configuredPluginsLabel: "ๅทฒ้
็ฝฎๆไปถ",
|
|
2858
|
+
currentProjectLabel: "ๅฝๅ้กน็ฎ",
|
|
2859
|
+
currentSessionLabel: "ๅฝๅไผ่ฏ",
|
|
2734
2860
|
detailsLabel: "่ฏฆๆ
",
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
languageLabel: "่ฏญ่จ",
|
|
2739
|
-
lastUpdatedLabel: "ๆๅๆดๆฐ",
|
|
2740
|
-
lspTitle: "๐ง LSP (Language Server)",
|
|
2861
|
+
errorStatus: "๐ด",
|
|
2862
|
+
lspTitle: "๐ง LSP",
|
|
2863
|
+
mcpFailedStatus: "๐ด",
|
|
2741
2864
|
mcpNotesLabel: "่ฏดๆ",
|
|
2742
|
-
mcpRegistrationRequiredStatus: "๐ก
|
|
2743
|
-
mcpTitle: "๐ MCP
|
|
2744
|
-
|
|
2745
|
-
noneStatus: "โช
|
|
2865
|
+
mcpRegistrationRequiredStatus: "๐ก",
|
|
2866
|
+
mcpTitle: "๐ MCP",
|
|
2867
|
+
noPluginsMessage: "ๅฝๅ OpenCode ้
็ฝฎไธญๆช้
็ฝฎๆไปถใ",
|
|
2868
|
+
noneStatus: "โช",
|
|
2869
|
+
openCodeVersionLabel: "OpenCode็ๆฌ",
|
|
2746
2870
|
okLabel: "ๆญฃๅธธ",
|
|
2871
|
+
overviewTitle: "๐ฅ๏ธ ๆฆ่ง",
|
|
2747
2872
|
pageTitle: "๐ ๆๅก็ถๆ",
|
|
2873
|
+
pluginsTitle: "๐งฉ ๆไปถ",
|
|
2748
2874
|
rootLabel: "ๆ น็ฎๅฝ",
|
|
2749
|
-
serverTitle: "๐ฅ๏ธ ๆๅก็ซฏ",
|
|
2750
|
-
serviceLabel: "ๆๅก",
|
|
2751
2875
|
statusLabel: "็ถๆ",
|
|
2752
|
-
|
|
2876
|
+
tbotVersionLabel: "opencode-tbot็ๆฌ",
|
|
2753
2877
|
workspaceTitle: "๐ ๅทฅไฝๅบ"
|
|
2754
2878
|
};
|
|
2755
2879
|
}
|