@coinseeker/opencode-telegram-plugin 1.0.4 → 1.0.6
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 +3 -1
- package/dist/telegram-remote.js +175 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,10 +15,12 @@ Configure the npm package in `~/.config/opencode/opencode.json`:
|
|
|
15
15
|
|
|
16
16
|
```json
|
|
17
17
|
{
|
|
18
|
-
"plugin": ["@coinseeker/opencode-telegram-plugin"]
|
|
18
|
+
"plugin": ["@coinseeker/opencode-telegram-plugin@1.0.6"]
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
Current stable version: `@coinseeker/opencode-telegram-plugin@1.0.6`.
|
|
23
|
+
|
|
22
24
|
Restart OpenCode after editing the config. OpenCode resolves npm package plugins on startup.
|
|
23
25
|
|
|
24
26
|
## Configure Telegram
|
package/dist/telegram-remote.js
CHANGED
|
@@ -465,6 +465,37 @@ function loadConfig(opts) {
|
|
|
465
465
|
|
|
466
466
|
// src/bot.ts
|
|
467
467
|
import { Bot, GrammyError } from "grammy";
|
|
468
|
+
|
|
469
|
+
// src/lib/question-format.ts
|
|
470
|
+
function optionDescriptionText(question) {
|
|
471
|
+
const options = question.options.map((option, index) => {
|
|
472
|
+
const description = option.description.trim();
|
|
473
|
+
return description ? `${index + 1}. ${option.label}
|
|
474
|
+
> ${description}` : `${index + 1}. ${option.label}`;
|
|
475
|
+
});
|
|
476
|
+
return options.length > 0 ? `
|
|
477
|
+
|
|
478
|
+
${options.join("\n")}` : "";
|
|
479
|
+
}
|
|
480
|
+
function questionText(question) {
|
|
481
|
+
const header = question.header ? `\u2753 ${question.header}` : "\u2753 Question";
|
|
482
|
+
return `${header}
|
|
483
|
+
|
|
484
|
+
${question.question}${optionDescriptionText(question)}`;
|
|
485
|
+
}
|
|
486
|
+
function pendingQuestionText(questions, questionIndex) {
|
|
487
|
+
const question = questions[questionIndex];
|
|
488
|
+
const prefix = questions.length > 1 ? `Question ${questionIndex + 1}/${questions.length}
|
|
489
|
+
|
|
490
|
+
` : "";
|
|
491
|
+
const allQuestions = questions.length > 1 ? `All questions:
|
|
492
|
+
${questions.map((q, i) => `${i + 1}. ${q.header}: ${q.question}`).join("\n")}
|
|
493
|
+
|
|
494
|
+
` : "";
|
|
495
|
+
return `${allQuestions}${prefix}${questionText(question)}`;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/bot.ts
|
|
468
499
|
function createTelegramBot(opts) {
|
|
469
500
|
const { config, stateStore, logger, polling } = opts;
|
|
470
501
|
const bot = new Bot(config.botToken);
|
|
@@ -485,11 +516,13 @@ function createTelegramBot(opts) {
|
|
|
485
516
|
activeChatId = newChatId;
|
|
486
517
|
await stateStore.write({ chatId: newChatId, discoveredBy: process.pid });
|
|
487
518
|
logger.info("chat_id discovered", { chatId: newChatId });
|
|
488
|
-
await ctx.reply(
|
|
519
|
+
await ctx.reply(
|
|
520
|
+
`\u2705 Chat connected!
|
|
489
521
|
|
|
490
522
|
Your chat_id: ${newChatId}
|
|
491
523
|
|
|
492
|
-
This chat is now active for OpenCode notifications.`
|
|
524
|
+
This chat is now active for OpenCode notifications.`
|
|
525
|
+
);
|
|
493
526
|
}
|
|
494
527
|
}
|
|
495
528
|
await next();
|
|
@@ -497,7 +530,9 @@ This chat is now active for OpenCode notifications.`);
|
|
|
497
530
|
bot.catch((err) => {
|
|
498
531
|
const e = err.error;
|
|
499
532
|
if (e instanceof GrammyError && e.error_code === 409) {
|
|
500
|
-
logger.info("polling conflict (409) - another process took over", {
|
|
533
|
+
logger.info("polling conflict (409) - another process took over", {
|
|
534
|
+
description: e.description
|
|
535
|
+
});
|
|
501
536
|
} else {
|
|
502
537
|
logger.error("bot error", { error: String(e) });
|
|
503
538
|
}
|
|
@@ -508,7 +543,8 @@ This chat is now active for OpenCode notifications.`);
|
|
|
508
543
|
const messageId = ctx.callbackQuery.message?.message_id;
|
|
509
544
|
const chatId = ctx.chat?.id;
|
|
510
545
|
const userId = ctx.from?.id;
|
|
511
|
-
if (!questionDispatcher || messageId === void 0 || chatId === void 0 || userId === void 0)
|
|
546
|
+
if (!questionDispatcher || messageId === void 0 || chatId === void 0 || userId === void 0)
|
|
547
|
+
return;
|
|
512
548
|
await questionDispatcher.handleCallbackQuery(data, messageId, chatId, userId);
|
|
513
549
|
});
|
|
514
550
|
bot.callbackQuery(/^p:([^:]+):(o|a|r)$/, async (ctx) => {
|
|
@@ -563,17 +599,20 @@ This chat is now active for OpenCode notifications.`);
|
|
|
563
599
|
return { message_id: result.message_id };
|
|
564
600
|
},
|
|
565
601
|
async sendQuestionWithKeyboard(question, callbackData) {
|
|
566
|
-
const inlineKeyboard = question.options.map((option, index) => [
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
602
|
+
const inlineKeyboard = question.options.map((option, index) => [
|
|
603
|
+
{
|
|
604
|
+
text: option.label,
|
|
605
|
+
callback_data: callbackData[index] ?? ""
|
|
606
|
+
}
|
|
607
|
+
]);
|
|
570
608
|
if (callbackData[question.options.length]) {
|
|
571
|
-
inlineKeyboard.push([
|
|
609
|
+
inlineKeyboard.push([
|
|
610
|
+
{ text: "\u270F\uFE0F Custom answer", callback_data: callbackData[question.options.length] }
|
|
611
|
+
]);
|
|
572
612
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
${question.question}`, { reply_markup: { inline_keyboard: inlineKeyboard } });
|
|
613
|
+
return this.sendMessage(questionText(question), {
|
|
614
|
+
reply_markup: { inline_keyboard: inlineKeyboard }
|
|
615
|
+
});
|
|
577
616
|
},
|
|
578
617
|
async editMessage(messageId, text) {
|
|
579
618
|
const chatId = await requireChatId("editMessage");
|
|
@@ -772,6 +811,10 @@ function shouldSuppressIdle(sessionID) {
|
|
|
772
811
|
}
|
|
773
812
|
|
|
774
813
|
// src/events/session-idle.ts
|
|
814
|
+
var ROOT_IDLE_RECHECK_DELAY_MS = 2500;
|
|
815
|
+
function sleep(ms) {
|
|
816
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
817
|
+
}
|
|
775
818
|
async function resolveParentID(sessionId, ctx) {
|
|
776
819
|
const cachedParentID = ctx.sessionTitleService.getParentID(sessionId);
|
|
777
820
|
if (cachedParentID !== void 0) return cachedParentID;
|
|
@@ -788,6 +831,19 @@ async function resolveParentID(sessionId, ctx) {
|
|
|
788
831
|
return void 0;
|
|
789
832
|
}
|
|
790
833
|
}
|
|
834
|
+
async function hydrateDescendants(sessionId, ctx, seen = /* @__PURE__ */ new Set()) {
|
|
835
|
+
if (seen.has(sessionId)) return;
|
|
836
|
+
seen.add(sessionId);
|
|
837
|
+
try {
|
|
838
|
+
const result = await ctx.client.session.children({ path: { id: sessionId } });
|
|
839
|
+
for (const child of result.data ?? []) {
|
|
840
|
+
ctx.sessionTitleService.setSessionInfo(child);
|
|
841
|
+
await hydrateDescendants(child.id, ctx, seen);
|
|
842
|
+
}
|
|
843
|
+
} catch (err) {
|
|
844
|
+
ctx.logger.warn("session children fetch failed", { sessionId, error: String(err) });
|
|
845
|
+
}
|
|
846
|
+
}
|
|
791
847
|
async function sendIdleNotification(sessionId, ctx) {
|
|
792
848
|
if (shouldSuppressIdle(sessionId)) {
|
|
793
849
|
ctx.logger.info("idle suppressed - session was aborted", { sessionId });
|
|
@@ -808,9 +864,21 @@ async function sendIdleNotification(sessionId, ctx) {
|
|
|
808
864
|
async function flushDeferredParentIfReady(parentID, ctx) {
|
|
809
865
|
if (!ctx.sessionTitleService.hasDeferredIdleNotification(parentID)) return;
|
|
810
866
|
if (ctx.sessionTitleService.hasUnfinishedDescendants(parentID)) return;
|
|
867
|
+
if (ctx.sessionTitleService.getSessionStatus(parentID) !== "idle") {
|
|
868
|
+
ctx.sessionTitleService.clearDeferredIdleNotification(parentID);
|
|
869
|
+
ctx.logger.info("clearing deferred parent idle notification - parent resumed", { sessionId: parentID });
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
811
872
|
ctx.logger.info("sending deferred parent idle notification", { sessionId: parentID });
|
|
812
873
|
await sendIdleNotification(parentID, ctx);
|
|
813
874
|
}
|
|
875
|
+
async function deferParentIdleIfDescendantsRunning(sessionId, ctx) {
|
|
876
|
+
await hydrateDescendants(sessionId, ctx);
|
|
877
|
+
if (!ctx.sessionTitleService.hasUnfinishedDescendants(sessionId)) return false;
|
|
878
|
+
ctx.sessionTitleService.deferIdleNotification(sessionId);
|
|
879
|
+
ctx.logger.info("deferring parent idle notification - child sessions still running", { sessionId });
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
814
882
|
async function handleSessionIdle(event, ctx) {
|
|
815
883
|
const sessionId = event.properties.sessionID;
|
|
816
884
|
ctx.sessionTitleService.setSessionStatus(sessionId, "idle");
|
|
@@ -823,9 +891,15 @@ async function handleSessionIdle(event, ctx) {
|
|
|
823
891
|
if (parentID === void 0) {
|
|
824
892
|
ctx.logger.warn("session parentID unknown; sending idle notification", { sessionId });
|
|
825
893
|
}
|
|
826
|
-
if (
|
|
827
|
-
|
|
828
|
-
|
|
894
|
+
if (await deferParentIdleIfDescendantsRunning(sessionId, ctx)) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
await sleep(ctx.idleRecheckDelayMs ?? ROOT_IDLE_RECHECK_DELAY_MS);
|
|
898
|
+
if (ctx.sessionTitleService.getSessionStatus(sessionId) !== "idle") {
|
|
899
|
+
ctx.logger.info("idle notification skipped - session resumed during recheck delay", { sessionId });
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
if (await deferParentIdleIfDescendantsRunning(sessionId, ctx)) {
|
|
829
903
|
return;
|
|
830
904
|
}
|
|
831
905
|
await sendIdleNotification(sessionId, ctx);
|
|
@@ -1020,7 +1094,9 @@ function isQuestionInfo(value) {
|
|
|
1020
1094
|
if (typeof value.question !== "string") return false;
|
|
1021
1095
|
if (typeof value.header !== "string") return false;
|
|
1022
1096
|
if (!Array.isArray(value.options)) return false;
|
|
1023
|
-
return value.options.every(
|
|
1097
|
+
return value.options.every(
|
|
1098
|
+
(option) => typeof option === "object" && option !== null && isQuestionOption(option)
|
|
1099
|
+
);
|
|
1024
1100
|
}
|
|
1025
1101
|
function isEventQuestionAsked(event) {
|
|
1026
1102
|
if (event.type !== "question.asked") return false;
|
|
@@ -1029,15 +1105,20 @@ function isEventQuestionAsked(event) {
|
|
|
1029
1105
|
if (typeof props.id !== "string") return false;
|
|
1030
1106
|
if (typeof props.sessionID !== "string") return false;
|
|
1031
1107
|
if (!Array.isArray(props.questions)) return false;
|
|
1032
|
-
return props.questions.every(
|
|
1108
|
+
return props.questions.every(
|
|
1109
|
+
(question) => typeof question === "object" && question !== null && isQuestionInfo(question)
|
|
1110
|
+
);
|
|
1033
1111
|
}
|
|
1034
1112
|
function buildCallbackData2(shortHash, questionIndex, optionIndex) {
|
|
1035
1113
|
const data = `q:${shortHash}:${questionIndex}:${optionIndex}`;
|
|
1036
|
-
if (Buffer.byteLength(data, "utf8") > 64)
|
|
1114
|
+
if (Buffer.byteLength(data, "utf8") > 64)
|
|
1115
|
+
throw new Error("Telegram callback_data exceeds 64 bytes");
|
|
1037
1116
|
return data;
|
|
1038
1117
|
}
|
|
1039
1118
|
function callbackDataForQuestion(shortHash, questionIndex, question) {
|
|
1040
|
-
const data = question.options.map(
|
|
1119
|
+
const data = question.options.map(
|
|
1120
|
+
(_, optionIndex) => buildCallbackData2(shortHash, questionIndex, optionIndex)
|
|
1121
|
+
);
|
|
1041
1122
|
if (question.custom !== false) data.push(buildCallbackData2(shortHash, questionIndex, "c"));
|
|
1042
1123
|
return data;
|
|
1043
1124
|
}
|
|
@@ -1049,39 +1130,44 @@ function selectedAnswers(pending, questionIndex) {
|
|
|
1049
1130
|
}
|
|
1050
1131
|
function questionInlineKeyboard(shortHash, questionIndex, question, selected) {
|
|
1051
1132
|
const multiple = question.multiple === true;
|
|
1052
|
-
const inlineKeyboard = question.options.map((option, optionIndex) => [
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1133
|
+
const inlineKeyboard = question.options.map((option, optionIndex) => [
|
|
1134
|
+
{
|
|
1135
|
+
text: multiple && selected.includes(option.label) ? `\u2705 ${option.label}` : option.label,
|
|
1136
|
+
callback_data: buildCallbackData2(shortHash, questionIndex, optionIndex)
|
|
1137
|
+
}
|
|
1138
|
+
]);
|
|
1056
1139
|
if (question.custom !== false) {
|
|
1057
|
-
inlineKeyboard.push([
|
|
1140
|
+
inlineKeyboard.push([
|
|
1141
|
+
{ text: "\u270F\uFE0F Custom answer", callback_data: buildCallbackData2(shortHash, questionIndex, "c") }
|
|
1142
|
+
]);
|
|
1058
1143
|
}
|
|
1059
1144
|
if (multiple) {
|
|
1060
|
-
inlineKeyboard.push([
|
|
1145
|
+
inlineKeyboard.push([
|
|
1146
|
+
{ text: "\u2705 Done", callback_data: buildCallbackData2(shortHash, questionIndex, "d") }
|
|
1147
|
+
]);
|
|
1061
1148
|
}
|
|
1062
1149
|
return inlineKeyboard;
|
|
1063
1150
|
}
|
|
1064
1151
|
function questionPromptText(pending, questionIndex) {
|
|
1065
|
-
|
|
1066
|
-
const prefix = pending.questions.length > 1 ? `Question ${questionIndex + 1}/${pending.questions.length}
|
|
1067
|
-
|
|
1068
|
-
` : "";
|
|
1069
|
-
const allQuestions = pending.questions.length > 1 ? `All questions:
|
|
1070
|
-
${pending.questions.map((q, i) => `${i + 1}. ${q.header}: ${q.question}`).join("\n")}
|
|
1071
|
-
|
|
1072
|
-
` : "";
|
|
1073
|
-
return `${allQuestions}${prefix}\u2753 ${question.header}
|
|
1074
|
-
|
|
1075
|
-
${question.question}`;
|
|
1152
|
+
return pendingQuestionText(pending.questions, questionIndex);
|
|
1076
1153
|
}
|
|
1077
1154
|
function answerSummary(questions, answers) {
|
|
1078
|
-
return answers.map(
|
|
1155
|
+
return answers.map(
|
|
1156
|
+
(answer, index) => `${index + 1}. ${questions[index]?.header ?? "Question"}: ${answer.join(", ") || "(empty)"}`
|
|
1157
|
+
).join("\n");
|
|
1079
1158
|
}
|
|
1080
1159
|
async function editPromptForQuestion(ctx, pending, shortHash, questionIndex) {
|
|
1081
1160
|
const messageId = pending.telegramMessageIds[0];
|
|
1082
1161
|
const question = pending.questions[questionIndex];
|
|
1083
|
-
const inlineKeyboard = questionInlineKeyboard(
|
|
1084
|
-
|
|
1162
|
+
const inlineKeyboard = questionInlineKeyboard(
|
|
1163
|
+
shortHash,
|
|
1164
|
+
questionIndex,
|
|
1165
|
+
question,
|
|
1166
|
+
selectedAnswers(pending, questionIndex)
|
|
1167
|
+
);
|
|
1168
|
+
await ctx.bot.editMessageText(messageId, questionPromptText(pending, questionIndex), {
|
|
1169
|
+
reply_markup: { inline_keyboard: inlineKeyboard }
|
|
1170
|
+
});
|
|
1085
1171
|
}
|
|
1086
1172
|
async function completeIfReady(ctx, pending, shortHash) {
|
|
1087
1173
|
const nextIndex = pending.answersInProgress.findIndex((answer) => answer === null);
|
|
@@ -1095,12 +1181,21 @@ async function completeIfReady(ctx, pending, shortHash) {
|
|
|
1095
1181
|
const messageId = pending.telegramMessageIds[0];
|
|
1096
1182
|
try {
|
|
1097
1183
|
await ctx.replyToQuestion(pending.requestID, answers);
|
|
1098
|
-
await ctx.bot.editMessageRemoveKeyboard(
|
|
1099
|
-
|
|
1100
|
-
|
|
1184
|
+
await ctx.bot.editMessageRemoveKeyboard(
|
|
1185
|
+
messageId,
|
|
1186
|
+
`\u2705 Answered:
|
|
1187
|
+
${answerSummary(pending.questions, answers)}`
|
|
1188
|
+
);
|
|
1189
|
+
ctx.logger.info("question reply sent", {
|
|
1190
|
+
requestID: pending.requestID,
|
|
1191
|
+
sessionID: pending.sessionID
|
|
1192
|
+
});
|
|
1101
1193
|
} catch (err) {
|
|
1102
1194
|
await ctx.bot.editMessageRemoveKeyboard(messageId, "\u26A0\uFE0F Failed to send answer to opencode");
|
|
1103
|
-
ctx.logger.error("failed to send question reply", {
|
|
1195
|
+
ctx.logger.error("failed to send question reply", {
|
|
1196
|
+
error: String(err),
|
|
1197
|
+
requestID: pending.requestID
|
|
1198
|
+
});
|
|
1104
1199
|
} finally {
|
|
1105
1200
|
await ctx.pendingQuestions.deletePending(shortHash);
|
|
1106
1201
|
}
|
|
@@ -1113,7 +1208,11 @@ async function expirePending2(ctx, shortHash, pending, messageId) {
|
|
|
1113
1208
|
async function handleQuestionAsked(event, ctx) {
|
|
1114
1209
|
const request = event.properties;
|
|
1115
1210
|
if (request.questions.length === 0) return;
|
|
1116
|
-
const claimed = await claimOnce({
|
|
1211
|
+
const claimed = await claimOnce({
|
|
1212
|
+
claimsDir: ctx.claimsDir,
|
|
1213
|
+
key: `question.asked:${request.id}`,
|
|
1214
|
+
ttlMs: 5e3
|
|
1215
|
+
});
|
|
1117
1216
|
if (!claimed) return;
|
|
1118
1217
|
const shortHash = createQuestionShortHash(request.id);
|
|
1119
1218
|
const firstQuestion = request.questions[0];
|
|
@@ -1129,16 +1228,26 @@ async function handleQuestionAsked(event, ctx) {
|
|
|
1129
1228
|
answersInProgress: request.questions.map(() => null)
|
|
1130
1229
|
};
|
|
1131
1230
|
try {
|
|
1132
|
-
const message = request.questions.length === 1 && useSimpleQuestionKeyboard(firstQuestion) ? await ctx.bot.sendQuestionWithKeyboard(
|
|
1231
|
+
const message = request.questions.length === 1 && useSimpleQuestionKeyboard(firstQuestion) ? await ctx.bot.sendQuestionWithKeyboard(
|
|
1232
|
+
firstQuestion,
|
|
1233
|
+
callbackDataForQuestion(shortHash, 0, firstQuestion)
|
|
1234
|
+
) : await ctx.bot.sendMessage(questionPromptText(pending, 0), {
|
|
1133
1235
|
reply_markup: {
|
|
1134
1236
|
inline_keyboard: questionInlineKeyboard(shortHash, 0, firstQuestion, [])
|
|
1135
1237
|
}
|
|
1136
1238
|
});
|
|
1137
1239
|
pending.telegramMessageIds = [message.message_id];
|
|
1138
1240
|
await ctx.pendingQuestions.savePending(shortHash, pending);
|
|
1139
|
-
ctx.logger.info("question prompt sent", {
|
|
1241
|
+
ctx.logger.info("question prompt sent", {
|
|
1242
|
+
requestID: request.id,
|
|
1243
|
+
sessionID: request.sessionID,
|
|
1244
|
+
count: request.questions.length
|
|
1245
|
+
});
|
|
1140
1246
|
} catch (err) {
|
|
1141
|
-
ctx.logger.error("failed to send question prompt", {
|
|
1247
|
+
ctx.logger.error("failed to send question prompt", {
|
|
1248
|
+
error: String(err),
|
|
1249
|
+
requestID: request.id
|
|
1250
|
+
});
|
|
1142
1251
|
}
|
|
1143
1252
|
}
|
|
1144
1253
|
function createQuestionDispatcher(ctx) {
|
|
@@ -1162,12 +1271,26 @@ function createQuestionDispatcher(ctx) {
|
|
|
1162
1271
|
if (!question) return;
|
|
1163
1272
|
if (selection === "c") {
|
|
1164
1273
|
if (question.multiple === true) {
|
|
1165
|
-
await ctx.bot.editMessageText(messageId, questionPromptText(pending, questionIndex), {
|
|
1274
|
+
await ctx.bot.editMessageText(messageId, questionPromptText(pending, questionIndex), {
|
|
1275
|
+
reply_markup: { inline_keyboard: [] }
|
|
1276
|
+
});
|
|
1166
1277
|
} else {
|
|
1167
|
-
await ctx.bot.editMessageRemoveKeyboard(
|
|
1278
|
+
await ctx.bot.editMessageRemoveKeyboard(
|
|
1279
|
+
messageId,
|
|
1280
|
+
"\u270F\uFE0F Reply to the next message with your custom answer."
|
|
1281
|
+
);
|
|
1168
1282
|
}
|
|
1169
|
-
const prompt = await ctx.bot.replyWithForceReply(
|
|
1170
|
-
|
|
1283
|
+
const prompt = await ctx.bot.replyWithForceReply(
|
|
1284
|
+
"Type your custom answer",
|
|
1285
|
+
"Type your answer"
|
|
1286
|
+
);
|
|
1287
|
+
pending.awaitingCustomFor = {
|
|
1288
|
+
shortHash,
|
|
1289
|
+
questionIndex,
|
|
1290
|
+
chatId,
|
|
1291
|
+
userId,
|
|
1292
|
+
promptMessageId: prompt.message_id
|
|
1293
|
+
};
|
|
1171
1294
|
await ctx.pendingQuestions.savePending(shortHash, pending);
|
|
1172
1295
|
return;
|
|
1173
1296
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coinseeker/opencode-telegram-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Control and monitor OpenCode from Telegram with notifications, question replies, and subagent-aware completion.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/telegram-remote.js",
|