@ycodium-ai/plugin-im 0.2.2566 → 0.2.2570
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 +9 -1
- package/dist/index.js +648 -20
- package/locales/en.json +44 -1
- package/locales/zh-CN.json +44 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -51,10 +51,18 @@ written as plugin facts (`ctx.events.append`) and folded back by a pure function
|
|
|
51
51
|
archived or moved to another project is dropped on the next message.
|
|
52
52
|
|
|
53
53
|
Chat commands: `/stop`, `/status`, `/new`, `/sessions`, `/resume`, `/model`,
|
|
54
|
-
`/plan`, `/team`, `/thread`, `/help`. `/sessions` and `/resume` only list and
|
|
54
|
+
`/plan`, `/team`, `/thread`, `/help`, `/panel`. `/sessions` and `/resume` only list and
|
|
55
55
|
switch threads this plugin opened, since the host lets a plugin send only into
|
|
56
56
|
its own threads.
|
|
57
57
|
|
|
58
|
+
On Lark, `/panel` sends a command panel card (`/help` stays text and ends with a pointer to it; other platforms' `/help` does not mention it): every command above
|
|
59
|
+
is a button, and the ones that take an argument (a thread, a team, a model) open
|
|
60
|
+
a second view on the same card with a Back button. Each card keeps its own view
|
|
61
|
+
stack in memory (`src/panelController.ts`), so after a restart an old card only
|
|
62
|
+
answers that it has expired. A tap runs the same handler as the typed command and
|
|
63
|
+
waits in the same per-group queue as typed messages. Another platform opts in by
|
|
64
|
+
implementing the optional `panel` surface on its adapter (`src/adapter.ts`).
|
|
65
|
+
|
|
58
66
|
The **Reply language** setting (Chinese by default, or English) picks the
|
|
59
67
|
language of everything the bot says itself. The settings card follows the
|
|
60
68
|
interface language instead. The copy lives in `locales/en.json` and
|
package/dist/index.js
CHANGED
|
@@ -126208,6 +126208,7 @@ var ZH_ALIASES = {
|
|
|
126208
126208
|
\u72B6\u6001: "status",
|
|
126209
126209
|
\u65B0\u5BF9\u8BDD: "new",
|
|
126210
126210
|
\u5E2E\u52A9: "help",
|
|
126211
|
+
\u9762\u677F: "panel",
|
|
126211
126212
|
\u4F1A\u8BDD\u5217\u8868: "sessions"
|
|
126212
126213
|
};
|
|
126213
126214
|
var resolveTeam = (query, teams) => {
|
|
@@ -126225,6 +126226,7 @@ var parseChatCommand = (input) => {
|
|
|
126225
126226
|
if (/^\/status$/i.test(trimmed)) return { kind: "status" };
|
|
126226
126227
|
if (/^\/new$/i.test(trimmed)) return { kind: "new" };
|
|
126227
126228
|
if (/^\/help$/i.test(trimmed)) return { kind: "help" };
|
|
126229
|
+
if (/^\/panel$/i.test(trimmed)) return { kind: "panel" };
|
|
126228
126230
|
if (/^\/sessions$/i.test(trimmed)) return { kind: "sessions" };
|
|
126229
126231
|
const resume = resumeArgument(input.text, input.chinese);
|
|
126230
126232
|
if (resume !== void 0) {
|
|
@@ -126249,7 +126251,7 @@ var parseChatCommand = (input) => {
|
|
|
126249
126251
|
}
|
|
126250
126252
|
return { kind: "message" };
|
|
126251
126253
|
};
|
|
126252
|
-
var helpText = (t) => [
|
|
126254
|
+
var helpText = (t, options = {}) => [
|
|
126253
126255
|
t("help.title"),
|
|
126254
126256
|
t("help.stop"),
|
|
126255
126257
|
t("help.status"),
|
|
@@ -126260,7 +126262,8 @@ var helpText = (t) => [
|
|
|
126260
126262
|
t("help.plan"),
|
|
126261
126263
|
t("help.team"),
|
|
126262
126264
|
t("help.thread"),
|
|
126263
|
-
t("help.aliases")
|
|
126265
|
+
t("help.aliases"),
|
|
126266
|
+
...options.panel === true ? ["", t("help.panel")] : []
|
|
126264
126267
|
].join("\n");
|
|
126265
126268
|
var describeTargets = (target, teams, t) => {
|
|
126266
126269
|
const current = target.kind === "thread" ? t("targets.currentThread") : t("targets.currentTeam", {
|
|
@@ -131972,6 +131975,40 @@ var buildApprovalSettledCard = (settlement, t) => ({
|
|
|
131972
131975
|
}
|
|
131973
131976
|
]
|
|
131974
131977
|
});
|
|
131978
|
+
var PANEL_TEMPLATE = {
|
|
131979
|
+
menu: "blue",
|
|
131980
|
+
busy: "grey",
|
|
131981
|
+
notice: "orange"
|
|
131982
|
+
};
|
|
131983
|
+
var panelButton = (button) => ({
|
|
131984
|
+
tag: "button",
|
|
131985
|
+
text: { tag: "plain_text", content: button.label },
|
|
131986
|
+
type: button.danger === true ? "danger" : "default",
|
|
131987
|
+
value: button.action.arg === void 0 ? { pn: button.action.op } : { pn: button.action.op, a: button.action.arg }
|
|
131988
|
+
});
|
|
131989
|
+
var buildPanelCard = (card) => {
|
|
131990
|
+
const elements = [];
|
|
131991
|
+
if (card.text !== "") elements.push({ tag: "markdown", content: stripAngleBrackets(card.text) });
|
|
131992
|
+
for (const group of card.groups) {
|
|
131993
|
+
if (group.buttons.length === 0) continue;
|
|
131994
|
+
if (group.heading !== null) {
|
|
131995
|
+
elements.push({ tag: "markdown", content: `**${stripAngleBrackets(group.heading)}**` });
|
|
131996
|
+
}
|
|
131997
|
+
elements.push({ tag: "action", actions: group.buttons.map(panelButton) });
|
|
131998
|
+
}
|
|
131999
|
+
if (card.nav.length > 0) {
|
|
132000
|
+
elements.push({ tag: "hr" }, { tag: "action", actions: card.nav.map(panelButton) });
|
|
132001
|
+
}
|
|
132002
|
+
if (elements.length === 0) elements.push({ tag: "markdown", content: " " });
|
|
132003
|
+
return {
|
|
132004
|
+
config: { wide_screen_mode: true },
|
|
132005
|
+
header: {
|
|
132006
|
+
title: { tag: "plain_text", content: card.title },
|
|
132007
|
+
template: PANEL_TEMPLATE[card.tone]
|
|
132008
|
+
},
|
|
132009
|
+
elements
|
|
132010
|
+
};
|
|
132011
|
+
};
|
|
131975
132012
|
|
|
131976
132013
|
// src/larkInbound.ts
|
|
131977
132014
|
var import_node_sdk = __toESM(require_lib2(), 1);
|
|
@@ -132087,26 +132124,44 @@ var fetchMessageResource = async (input) => {
|
|
|
132087
132124
|
}
|
|
132088
132125
|
return new Uint8Array(await response.arrayBuffer());
|
|
132089
132126
|
};
|
|
132090
|
-
var
|
|
132127
|
+
var cardActionValue = (event) => {
|
|
132091
132128
|
const roots = [event, event?.event];
|
|
132092
132129
|
for (const root of roots) {
|
|
132093
132130
|
const value = root?.action?.value;
|
|
132094
132131
|
if (value === void 0 || value === null) continue;
|
|
132095
|
-
|
|
132096
|
-
|
|
132097
|
-
|
|
132098
|
-
|
|
132099
|
-
|
|
132100
|
-
record = void 0;
|
|
132101
|
-
}
|
|
132102
|
-
} else {
|
|
132103
|
-
record = value;
|
|
132132
|
+
if (typeof value !== "string") return value;
|
|
132133
|
+
try {
|
|
132134
|
+
return JSON.parse(value);
|
|
132135
|
+
} catch {
|
|
132136
|
+
continue;
|
|
132104
132137
|
}
|
|
132105
|
-
const token = record?.["tk"];
|
|
132106
|
-
if (typeof token === "string" && token.length > 0) return token;
|
|
132107
132138
|
}
|
|
132108
132139
|
return null;
|
|
132109
132140
|
};
|
|
132141
|
+
var cardActionToken = (event) => {
|
|
132142
|
+
const token = cardActionValue(event)?.["tk"];
|
|
132143
|
+
return typeof token === "string" && token.length > 0 ? token : null;
|
|
132144
|
+
};
|
|
132145
|
+
var cardActionPanel = (event) => {
|
|
132146
|
+
const value = cardActionValue(event);
|
|
132147
|
+
const op = value?.["pn"];
|
|
132148
|
+
if (typeof op !== "string" || op === "") return null;
|
|
132149
|
+
const arg = value?.["a"];
|
|
132150
|
+
const roots = [event, event?.event];
|
|
132151
|
+
const pick = (read) => {
|
|
132152
|
+
for (const root of roots) {
|
|
132153
|
+
if (typeof root !== "object" || root === null) continue;
|
|
132154
|
+
const candidate = read(root);
|
|
132155
|
+
if (typeof candidate === "string" && candidate !== "") return candidate;
|
|
132156
|
+
}
|
|
132157
|
+
return null;
|
|
132158
|
+
};
|
|
132159
|
+
const context = (root) => typeof root["context"] === "object" && root["context"] !== null ? root["context"] : void 0;
|
|
132160
|
+
const cardId = pick((root) => context(root)?.["open_message_id"] ?? root["open_message_id"]);
|
|
132161
|
+
const chatId = pick((root) => context(root)?.["open_chat_id"] ?? root["open_chat_id"]);
|
|
132162
|
+
if (cardId === null || chatId === null) return null;
|
|
132163
|
+
return { op, arg: typeof arg === "string" ? arg : null, cardId, chatId };
|
|
132164
|
+
};
|
|
132110
132165
|
var cardActionOperator = (event) => {
|
|
132111
132166
|
const roots = [event, event?.event];
|
|
132112
132167
|
for (const root of roots) {
|
|
@@ -132127,6 +132182,11 @@ var startLarkInbound = (input) => {
|
|
|
132127
132182
|
// 卡片点击走的也是 event 帧(实测过),所以在同一个 dispatcher 上注册就行,不用开
|
|
132128
132183
|
// webhook。这条不需要 @:点按钮直接推给应用。
|
|
132129
132184
|
"card.action.trigger": (event) => {
|
|
132185
|
+
const panel = cardActionPanel(event);
|
|
132186
|
+
if (panel !== null) {
|
|
132187
|
+
const notice2 = input.onPanelClick?.(panel, cardActionOperator(event)) ?? null;
|
|
132188
|
+
return notice2 === null ? {} : { toast: { type: "info", content: notice2 } };
|
|
132189
|
+
}
|
|
132130
132190
|
const token = cardActionToken(event);
|
|
132131
132191
|
if (token === null) return {};
|
|
132132
132192
|
const notice = input.onCardAction(token, cardActionOperator(event));
|
|
@@ -132185,6 +132245,13 @@ var makeLarkAdapter = (deps) => {
|
|
|
132185
132245
|
appSecret,
|
|
132186
132246
|
onError: handlers.onError,
|
|
132187
132247
|
onCardAction: handlers.onApproval,
|
|
132248
|
+
onPanelClick: (click, operatorOpenId) => handlers.onPanelClick?.({
|
|
132249
|
+
chatId: click.chatId,
|
|
132250
|
+
cardId: click.cardId,
|
|
132251
|
+
operatorId: operatorOpenId,
|
|
132252
|
+
op: click.op,
|
|
132253
|
+
arg: click.arg
|
|
132254
|
+
}) ?? null,
|
|
132188
132255
|
onMessage: (message) => {
|
|
132189
132256
|
handlers.onMessage({
|
|
132190
132257
|
messageId: message.messageId,
|
|
@@ -132263,6 +132330,22 @@ var makeLarkAdapter = (deps) => {
|
|
|
132263
132330
|
});
|
|
132264
132331
|
}
|
|
132265
132332
|
},
|
|
132333
|
+
panel: {
|
|
132334
|
+
open: async (binding, card) => sendCard({
|
|
132335
|
+
http: deps.http,
|
|
132336
|
+
token: await tenantToken(binding),
|
|
132337
|
+
chatId: binding.chatId,
|
|
132338
|
+
card: buildPanelCard(card)
|
|
132339
|
+
}),
|
|
132340
|
+
update: async (binding, cardId, card) => {
|
|
132341
|
+
await patchCard({
|
|
132342
|
+
http: deps.http,
|
|
132343
|
+
token: await tenantToken(binding),
|
|
132344
|
+
messageId: cardId,
|
|
132345
|
+
card: buildPanelCard(card)
|
|
132346
|
+
});
|
|
132347
|
+
}
|
|
132348
|
+
},
|
|
132266
132349
|
acknowledge: async (binding, messageId) => addReaction({
|
|
132267
132350
|
http: deps.http,
|
|
132268
132351
|
token: await tenantToken(binding),
|
|
@@ -132425,7 +132508,8 @@ var en_default = {
|
|
|
132425
132508
|
sessions: "\xB7 /sessions \u2014 list the sessions this bot opened for this group's project",
|
|
132426
132509
|
resume: "\xB7 /resume number \u2014 switch to one of them (a number from /sessions, or the start of its id)",
|
|
132427
132510
|
model: "\xB7 /model \u2014 see the model this group uses; /model number or /model instance/model switches it",
|
|
132428
|
-
plan: "\xB7 /plan \u2014 toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it"
|
|
132511
|
+
plan: "\xB7 /plan \u2014 toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it",
|
|
132512
|
+
panel: "Send /panel to open the command panel with a button for every command."
|
|
132429
132513
|
},
|
|
132430
132514
|
sessions: {
|
|
132431
132515
|
title: "Sessions this bot opened for this group's project ({{count}}):",
|
|
@@ -132498,6 +132582,48 @@ var en_default = {
|
|
|
132498
132582
|
chosen: "Chosen: {{label}}",
|
|
132499
132583
|
elsewhere: "Already handled elsewhere (the web app or another client)."
|
|
132500
132584
|
}
|
|
132585
|
+
},
|
|
132586
|
+
panel: {
|
|
132587
|
+
title: "Command panel",
|
|
132588
|
+
hint: "A button does the same as typing its command. You can still type.",
|
|
132589
|
+
category: {
|
|
132590
|
+
session: "Session",
|
|
132591
|
+
target: "Send to",
|
|
132592
|
+
system: "Settings and status"
|
|
132593
|
+
},
|
|
132594
|
+
cmd: {
|
|
132595
|
+
new: "New session",
|
|
132596
|
+
stop: "Stop this turn",
|
|
132597
|
+
sessions: "Sessions",
|
|
132598
|
+
team: "Send to a team",
|
|
132599
|
+
thread: "Normal session",
|
|
132600
|
+
model: "Model",
|
|
132601
|
+
planOn: "Plan mode: on",
|
|
132602
|
+
planOff: "Plan mode: off",
|
|
132603
|
+
status: "Status",
|
|
132604
|
+
help: "Text help"
|
|
132605
|
+
},
|
|
132606
|
+
back: "Back",
|
|
132607
|
+
prev: "Previous",
|
|
132608
|
+
next: "Next",
|
|
132609
|
+
page: "Page {{page}} of {{total}}",
|
|
132610
|
+
loading: "Loading\u2026",
|
|
132611
|
+
working: "Working on it\u2026",
|
|
132612
|
+
expired: "This panel has expired. Send /panel again.",
|
|
132613
|
+
busy: "The last tap is still being handled. Give it a moment.",
|
|
132614
|
+
loadFailed: "Could not read that just now. Try again in a moment.",
|
|
132615
|
+
sessions: {
|
|
132616
|
+
title: "Pick a session",
|
|
132617
|
+
hint: "Tap one to switch to it."
|
|
132618
|
+
},
|
|
132619
|
+
teams: {
|
|
132620
|
+
title: "Pick a team",
|
|
132621
|
+
hint: "Tap a team and the next messages go to it.",
|
|
132622
|
+
unavailable: "Could not read the team list."
|
|
132623
|
+
},
|
|
132624
|
+
models: {
|
|
132625
|
+
title: "Pick a model"
|
|
132626
|
+
}
|
|
132501
132627
|
}
|
|
132502
132628
|
};
|
|
132503
132629
|
|
|
@@ -132632,7 +132758,8 @@ var zh_CN_default = {
|
|
|
132632
132758
|
sessions: "\xB7 /sessions \u2014\u2014 \u5217\u51FA\u672C\u673A\u5668\u4EBA\u4E3A\u8FD9\u4E2A\u7FA4\u7684\u9879\u76EE\u5F00\u8FC7\u7684\u4F1A\u8BDD",
|
|
132633
132759
|
resume: "\xB7 /resume \u7F16\u53F7 \u2014\u2014 \u5207\u5230\u5176\u4E2D\u4E00\u6761\uFF08/sessions \u91CC\u7684\u7F16\u53F7\uFF0C\u6216\u4F1A\u8BDD id \u7684\u5F00\u5934\uFF09",
|
|
132634
132760
|
model: "\xB7 /model \u2014\u2014 \u770B\u8FD9\u4E2A\u7FA4\u7528\u7684\u6A21\u578B\uFF1B/model \u7F16\u53F7 \u6216 /model \u5B9E\u4F8B/\u6A21\u578B \u5207\u6362",
|
|
132635
|
-
plan: "\xB7 /plan \u2014\u2014 \u5F00\u5173\u8BA1\u5212\u6A21\u5F0F\uFF08agent \u5148\u51FA\u8BA1\u5212\u3001\u4E0D\u52A8\u624B\uFF09\uFF1B/plan on\u3001/plan off \u660E\u786E\u8BBE\u5B9A"
|
|
132761
|
+
plan: "\xB7 /plan \u2014\u2014 \u5F00\u5173\u8BA1\u5212\u6A21\u5F0F\uFF08agent \u5148\u51FA\u8BA1\u5212\u3001\u4E0D\u52A8\u624B\uFF09\uFF1B/plan on\u3001/plan off \u660E\u786E\u8BBE\u5B9A",
|
|
132762
|
+
panel: "\u53D1 /panel \u6253\u5F00\u53EF\u70B9\u7684\u547D\u4EE4\u9762\u677F\u3002"
|
|
132636
132763
|
},
|
|
132637
132764
|
sessions: {
|
|
132638
132765
|
title: "\u672C\u673A\u5668\u4EBA\u4E3A\u8FD9\u4E2A\u7FA4\u7684\u9879\u76EE\u5F00\u8FC7\u7684\u4F1A\u8BDD\uFF08{{count}} \u6761\uFF09\uFF1A",
|
|
@@ -132705,6 +132832,48 @@ var zh_CN_default = {
|
|
|
132705
132832
|
chosen: "\u5DF2\u9009\uFF1A{{label}}",
|
|
132706
132833
|
elsewhere: "\u5DF2\u5728\u522B\u5904\u5904\u7406\uFF08\u7F51\u9875\u6216\u5176\u4ED6\u5BA2\u6237\u7AEF\uFF09\u3002"
|
|
132707
132834
|
}
|
|
132835
|
+
},
|
|
132836
|
+
panel: {
|
|
132837
|
+
title: "\u547D\u4EE4\u9762\u677F",
|
|
132838
|
+
hint: "\u70B9\u6309\u94AE\u7B49\u4E8E\u6253\u51FA\u5BF9\u5E94\u7684\u547D\u4EE4\uFF0C\u4E5F\u53EF\u4EE5\u76F4\u63A5\u6253\u5B57\u3002",
|
|
132839
|
+
category: {
|
|
132840
|
+
session: "\u4F1A\u8BDD",
|
|
132841
|
+
target: "\u6D3E\u7ED9\u8C01",
|
|
132842
|
+
system: "\u8BBE\u7F6E\u4E0E\u72B6\u6001"
|
|
132843
|
+
},
|
|
132844
|
+
cmd: {
|
|
132845
|
+
new: "\u65B0\u4F1A\u8BDD",
|
|
132846
|
+
stop: "\u505C\u6B62\u672C\u8F6E",
|
|
132847
|
+
sessions: "\u4F1A\u8BDD\u5217\u8868",
|
|
132848
|
+
team: "\u6D3E\u7ED9\u56E2\u961F",
|
|
132849
|
+
thread: "\u666E\u901A\u4F1A\u8BDD",
|
|
132850
|
+
model: "\u6A21\u578B",
|
|
132851
|
+
planOn: "\u8BA1\u5212\u6A21\u5F0F\uFF1A\u5F00",
|
|
132852
|
+
planOff: "\u8BA1\u5212\u6A21\u5F0F\uFF1A\u5173",
|
|
132853
|
+
status: "\u72B6\u6001",
|
|
132854
|
+
help: "\u6587\u5B57\u8BF4\u660E"
|
|
132855
|
+
},
|
|
132856
|
+
back: "\u8FD4\u56DE",
|
|
132857
|
+
prev: "\u4E0A\u4E00\u9875",
|
|
132858
|
+
next: "\u4E0B\u4E00\u9875",
|
|
132859
|
+
page: "\u7B2C {{page}} / {{total}} \u9875",
|
|
132860
|
+
loading: "\u52A0\u8F7D\u4E2D\u2026",
|
|
132861
|
+
working: "\u5904\u7406\u4E2D\u2026",
|
|
132862
|
+
expired: "\u9762\u677F\u5DF2\u8FC7\u671F\uFF0C\u518D\u53D1\u4E00\u6B21 /panel\u3002",
|
|
132863
|
+
busy: "\u4E0A\u4E00\u6B65\u8FD8\u5728\u5904\u7406\uFF0C\u7A0D\u7B49\u4E00\u4E0B\u3002",
|
|
132864
|
+
loadFailed: "\u521A\u624D\u6CA1\u8BFB\u51FA\u6765\uFF0C\u7A0D\u540E\u518D\u8BD5\u3002",
|
|
132865
|
+
sessions: {
|
|
132866
|
+
title: "\u9009\u4E00\u6761\u4F1A\u8BDD",
|
|
132867
|
+
hint: "\u70B9\u4E00\u6761\u5207\u8FC7\u53BB\u3002"
|
|
132868
|
+
},
|
|
132869
|
+
teams: {
|
|
132870
|
+
title: "\u9009\u4E00\u652F\u961F",
|
|
132871
|
+
hint: "\u70B9\u4E00\u652F\u961F\uFF0C\u63A5\u4E0B\u6765\u7684\u6D3B\u6D3E\u7ED9\u5B83\u3002",
|
|
132872
|
+
unavailable: "\u8BFB\u4E0D\u5230\u56E2\u961F\u5217\u8868\u3002"
|
|
132873
|
+
},
|
|
132874
|
+
models: {
|
|
132875
|
+
title: "\u9009\u6A21\u578B"
|
|
132876
|
+
}
|
|
132708
132877
|
}
|
|
132709
132878
|
};
|
|
132710
132879
|
|
|
@@ -132845,6 +133014,385 @@ var pickSession = (arg, listed, rows) => {
|
|
|
132845
133014
|
return matches.length === 1 ? matches[0]?.threadId ?? null : null;
|
|
132846
133015
|
};
|
|
132847
133016
|
|
|
133017
|
+
// src/panelCommands.ts
|
|
133018
|
+
var PANEL_CATEGORIES = ["session", "target", "system"];
|
|
133019
|
+
var PANEL_COMMANDS = [
|
|
133020
|
+
{
|
|
133021
|
+
id: "new",
|
|
133022
|
+
category: "session",
|
|
133023
|
+
line: "/new",
|
|
133024
|
+
kind: "new",
|
|
133025
|
+
labelKey: "panel.cmd.new",
|
|
133026
|
+
action: { op: "run", arg: "new" }
|
|
133027
|
+
},
|
|
133028
|
+
{
|
|
133029
|
+
id: "stop",
|
|
133030
|
+
category: "session",
|
|
133031
|
+
line: "/stop",
|
|
133032
|
+
kind: "stop",
|
|
133033
|
+
labelKey: "panel.cmd.stop",
|
|
133034
|
+
action: { op: "run", arg: "stop" }
|
|
133035
|
+
},
|
|
133036
|
+
{
|
|
133037
|
+
id: "sessions",
|
|
133038
|
+
category: "session",
|
|
133039
|
+
line: "/sessions",
|
|
133040
|
+
kind: "sessions",
|
|
133041
|
+
labelKey: "panel.cmd.sessions",
|
|
133042
|
+
action: { op: "open", arg: "sessions" }
|
|
133043
|
+
},
|
|
133044
|
+
{
|
|
133045
|
+
id: "team",
|
|
133046
|
+
category: "target",
|
|
133047
|
+
line: "/team",
|
|
133048
|
+
kind: "showTargets",
|
|
133049
|
+
labelKey: "panel.cmd.team",
|
|
133050
|
+
action: { op: "open", arg: "teams" }
|
|
133051
|
+
},
|
|
133052
|
+
{
|
|
133053
|
+
id: "thread",
|
|
133054
|
+
category: "target",
|
|
133055
|
+
line: "/thread",
|
|
133056
|
+
kind: "selectThread",
|
|
133057
|
+
labelKey: "panel.cmd.thread",
|
|
133058
|
+
action: { op: "run", arg: "thread" }
|
|
133059
|
+
},
|
|
133060
|
+
{
|
|
133061
|
+
id: "model",
|
|
133062
|
+
category: "system",
|
|
133063
|
+
line: "/model",
|
|
133064
|
+
kind: "model",
|
|
133065
|
+
labelKey: "panel.cmd.model",
|
|
133066
|
+
action: { op: "open", arg: "models" }
|
|
133067
|
+
},
|
|
133068
|
+
{
|
|
133069
|
+
id: "plan",
|
|
133070
|
+
category: "system",
|
|
133071
|
+
line: "/plan",
|
|
133072
|
+
kind: "plan",
|
|
133073
|
+
// 实际标签随当前状态在「开 / 关」间翻,见控制器;这里放关着时的那个。
|
|
133074
|
+
labelKey: "panel.cmd.planOff",
|
|
133075
|
+
action: { op: "run", arg: "plan" }
|
|
133076
|
+
},
|
|
133077
|
+
{
|
|
133078
|
+
id: "status",
|
|
133079
|
+
category: "system",
|
|
133080
|
+
line: "/status",
|
|
133081
|
+
kind: "status",
|
|
133082
|
+
labelKey: "panel.cmd.status",
|
|
133083
|
+
action: { op: "run", arg: "status" }
|
|
133084
|
+
},
|
|
133085
|
+
{
|
|
133086
|
+
id: "help",
|
|
133087
|
+
category: "system",
|
|
133088
|
+
// 这个按钮发的是文字版命令列表,不是又开一张面板。
|
|
133089
|
+
line: "/help",
|
|
133090
|
+
kind: "help",
|
|
133091
|
+
labelKey: "panel.cmd.help",
|
|
133092
|
+
action: { op: "run", arg: "help" }
|
|
133093
|
+
}
|
|
133094
|
+
];
|
|
133095
|
+
var PANEL_RUN_LINES = Object.fromEntries(
|
|
133096
|
+
PANEL_COMMANDS.filter((spec) => spec.action.op === "run" && spec.id !== "help").map((spec) => [
|
|
133097
|
+
spec.id,
|
|
133098
|
+
spec.line
|
|
133099
|
+
])
|
|
133100
|
+
);
|
|
133101
|
+
|
|
133102
|
+
// src/panelController.ts
|
|
133103
|
+
var PANEL_PAGE_SIZE = 8;
|
|
133104
|
+
var PANEL_CARD_LIMIT = 200;
|
|
133105
|
+
var LABEL_MAX_CHARS = 60;
|
|
133106
|
+
var PICK_KINDS = /* @__PURE__ */ new Set(["sessions", "teams", "models"]);
|
|
133107
|
+
var clip = (label) => {
|
|
133108
|
+
const chars = Array.from(label);
|
|
133109
|
+
return chars.length <= LABEL_MAX_CHARS ? label : `${chars.slice(0, LABEL_MAX_CHARS - 1).join("")}\u2026`;
|
|
133110
|
+
};
|
|
133111
|
+
var pageCount = (total) => Math.max(1, Math.ceil(total / PANEL_PAGE_SIZE));
|
|
133112
|
+
var makePanelController = (deps) => {
|
|
133113
|
+
const { t } = deps;
|
|
133114
|
+
const cards = /* @__PURE__ */ new Map();
|
|
133115
|
+
const tails = /* @__PURE__ */ new Map();
|
|
133116
|
+
const post = (cardId, job) => {
|
|
133117
|
+
const run = (tails.get(cardId) ?? Promise.resolve()).then(job).catch((error) => deps.warn("panel could not update a card", error)).then(() => {
|
|
133118
|
+
if (tails.get(cardId) === run) tails.delete(cardId);
|
|
133119
|
+
});
|
|
133120
|
+
tails.set(cardId, run);
|
|
133121
|
+
return run;
|
|
133122
|
+
};
|
|
133123
|
+
const remember = (entry) => {
|
|
133124
|
+
cards.delete(entry.cardId);
|
|
133125
|
+
cards.set(entry.cardId, entry);
|
|
133126
|
+
if (cards.size > PANEL_CARD_LIMIT) {
|
|
133127
|
+
const oldest = cards.keys().next().value;
|
|
133128
|
+
if (oldest !== void 0) cards.delete(oldest);
|
|
133129
|
+
}
|
|
133130
|
+
};
|
|
133131
|
+
const button = (label, op, arg, danger) => ({
|
|
133132
|
+
label: clip(label),
|
|
133133
|
+
action: { op, arg },
|
|
133134
|
+
...danger === true ? { danger } : {}
|
|
133135
|
+
});
|
|
133136
|
+
const busyCard = (title, text2) => ({
|
|
133137
|
+
title,
|
|
133138
|
+
tone: "busy",
|
|
133139
|
+
text: text2,
|
|
133140
|
+
groups: [],
|
|
133141
|
+
nav: []
|
|
133142
|
+
});
|
|
133143
|
+
const viewTitle = (kind) => t(`panel.${kind}.title`);
|
|
133144
|
+
const menuCard = (chatKey) => ({
|
|
133145
|
+
title: t("panel.title"),
|
|
133146
|
+
tone: "menu",
|
|
133147
|
+
text: t("panel.hint"),
|
|
133148
|
+
groups: PANEL_CATEGORIES.map(
|
|
133149
|
+
(category) => ({
|
|
133150
|
+
heading: t(`panel.category.${category}`),
|
|
133151
|
+
buttons: PANEL_COMMANDS.filter((spec) => spec.category === category).map(
|
|
133152
|
+
(spec) => button(
|
|
133153
|
+
// 计划模式的按钮把当前状态写在标签上,点了之后标签跟着翻。
|
|
133154
|
+
spec.id === "plan" ? t(deps.planOn(chatKey) ? "panel.cmd.planOn" : "panel.cmd.planOff") : t(spec.labelKey),
|
|
133155
|
+
spec.action.op,
|
|
133156
|
+
spec.action.arg,
|
|
133157
|
+
spec.id === "stop"
|
|
133158
|
+
)
|
|
133159
|
+
)
|
|
133160
|
+
})
|
|
133161
|
+
),
|
|
133162
|
+
nav: []
|
|
133163
|
+
});
|
|
133164
|
+
const pickerCard = (input) => {
|
|
133165
|
+
const total = pageCount(input.items.length);
|
|
133166
|
+
const page = Math.min(Math.max(input.page, 0), total - 1);
|
|
133167
|
+
const shown = input.items.slice(page * PANEL_PAGE_SIZE, (page + 1) * PANEL_PAGE_SIZE);
|
|
133168
|
+
const nav = [];
|
|
133169
|
+
if (input.depth > 1) nav.push(button(t("panel.back"), "back"));
|
|
133170
|
+
if (page > 0) nav.push(button(t("panel.prev"), "page", String(page - 1)));
|
|
133171
|
+
if (page < total - 1) nav.push(button(t("panel.next"), "page", String(page + 1)));
|
|
133172
|
+
const pageNote = total > 1 ? t("panel.page", { page: page + 1, total }) : "";
|
|
133173
|
+
return {
|
|
133174
|
+
title: viewTitle(input.kind),
|
|
133175
|
+
tone: "menu",
|
|
133176
|
+
text: [input.text, pageNote].filter((part) => part !== "").join("\n"),
|
|
133177
|
+
groups: shown.map((item) => ({ heading: null, buttons: [item] })),
|
|
133178
|
+
nav
|
|
133179
|
+
};
|
|
133180
|
+
};
|
|
133181
|
+
const render = (entry) => {
|
|
133182
|
+
const view = entry.stack.at(-1) ?? { kind: "menu" };
|
|
133183
|
+
const depth = entry.stack.length;
|
|
133184
|
+
switch (view.kind) {
|
|
133185
|
+
case "menu":
|
|
133186
|
+
return menuCard(entry.chatKey);
|
|
133187
|
+
case "notice":
|
|
133188
|
+
return {
|
|
133189
|
+
title: view.title,
|
|
133190
|
+
tone: "notice",
|
|
133191
|
+
text: view.text,
|
|
133192
|
+
groups: [],
|
|
133193
|
+
nav: depth > 1 ? [button(t("panel.back"), "back")] : []
|
|
133194
|
+
};
|
|
133195
|
+
case "sessions": {
|
|
133196
|
+
const now = deps.now();
|
|
133197
|
+
return pickerCard({
|
|
133198
|
+
kind: "sessions",
|
|
133199
|
+
page: view.page,
|
|
133200
|
+
depth,
|
|
133201
|
+
text: view.rows.length === 0 ? t("sessions.empty") : t("panel.sessions.hint"),
|
|
133202
|
+
items: view.rows.map(
|
|
133203
|
+
(row, index) => button(sessionLine(row, index + 1, now, t), "pick", `sessions:${row.threadId}`)
|
|
133204
|
+
)
|
|
133205
|
+
});
|
|
133206
|
+
}
|
|
133207
|
+
case "teams":
|
|
133208
|
+
return pickerCard({
|
|
133209
|
+
kind: "teams",
|
|
133210
|
+
page: view.page,
|
|
133211
|
+
depth,
|
|
133212
|
+
text: view.teams === null ? t("panel.teams.unavailable") : view.teams.length === 0 ? t("command.noTeams") : t("panel.teams.hint"),
|
|
133213
|
+
items: (view.teams ?? []).map(
|
|
133214
|
+
(team) => button(team.title, "pick", `teams:${team.teamId}`)
|
|
133215
|
+
)
|
|
133216
|
+
});
|
|
133217
|
+
case "models": {
|
|
133218
|
+
const { models } = view;
|
|
133219
|
+
const current = models?.current ?? null;
|
|
133220
|
+
const isCurrent = (choice) => current !== null && choice.instanceId === current.instanceId && choice.model === current.model;
|
|
133221
|
+
return pickerCard({
|
|
133222
|
+
kind: "models",
|
|
133223
|
+
page: view.page,
|
|
133224
|
+
depth,
|
|
133225
|
+
text: models === null || models.choices.length === 0 ? t("model.unavailable") : current === null ? t("model.currentUnknown") : t("model.current", {
|
|
133226
|
+
model: modelLabel(
|
|
133227
|
+
models.choices.find(isCurrent) ?? {
|
|
133228
|
+
instanceName: current.instanceId,
|
|
133229
|
+
modelName: current.model
|
|
133230
|
+
}
|
|
133231
|
+
)
|
|
133232
|
+
}),
|
|
133233
|
+
items: (models?.choices ?? []).map(
|
|
133234
|
+
(choice) => button(
|
|
133235
|
+
`${isCurrent(choice) ? "\u2605 " : ""}${modelLabel(choice)}`,
|
|
133236
|
+
"pick",
|
|
133237
|
+
`models:${choice.instanceId}/${choice.model}`
|
|
133238
|
+
)
|
|
133239
|
+
)
|
|
133240
|
+
});
|
|
133241
|
+
}
|
|
133242
|
+
}
|
|
133243
|
+
};
|
|
133244
|
+
const show = (entry) => post(entry.cardId, async () => {
|
|
133245
|
+
const surface = deps.surface(entry.chatKey);
|
|
133246
|
+
if (surface === null) return;
|
|
133247
|
+
const card = render(entry);
|
|
133248
|
+
try {
|
|
133249
|
+
await surface.update(entry.cardId, card);
|
|
133250
|
+
} catch (error) {
|
|
133251
|
+
deps.warn("panel could not update a card, posting a new one", error);
|
|
133252
|
+
const replacement = await surface.open(card);
|
|
133253
|
+
if (replacement !== null) {
|
|
133254
|
+
cards.delete(entry.cardId);
|
|
133255
|
+
entry.cardId = replacement;
|
|
133256
|
+
remember(entry);
|
|
133257
|
+
}
|
|
133258
|
+
}
|
|
133259
|
+
});
|
|
133260
|
+
const placeholder = (entry, title, text2) => {
|
|
133261
|
+
void post(entry.cardId, async () => {
|
|
133262
|
+
await deps.surface(entry.chatKey)?.update(entry.cardId, busyCard(title, text2));
|
|
133263
|
+
});
|
|
133264
|
+
};
|
|
133265
|
+
const loadView = async (chatKey, kind) => {
|
|
133266
|
+
switch (kind) {
|
|
133267
|
+
case "sessions":
|
|
133268
|
+
return { kind, page: 0, rows: await deps.loadSessions(chatKey) };
|
|
133269
|
+
case "teams":
|
|
133270
|
+
return { kind, page: 0, teams: await deps.loadTeams(chatKey) };
|
|
133271
|
+
case "models":
|
|
133272
|
+
return { kind, page: 0, models: await deps.loadModels(chatKey) };
|
|
133273
|
+
}
|
|
133274
|
+
};
|
|
133275
|
+
const backToMenu = (entry) => {
|
|
133276
|
+
entry.stack = [{ kind: "menu" }];
|
|
133277
|
+
return show(entry);
|
|
133278
|
+
};
|
|
133279
|
+
const guardBusy = (entry, work) => {
|
|
133280
|
+
entry.busy = true;
|
|
133281
|
+
return work().finally(() => {
|
|
133282
|
+
entry.busy = false;
|
|
133283
|
+
});
|
|
133284
|
+
};
|
|
133285
|
+
const operate = (entry, job) => {
|
|
133286
|
+
entry.busy = true;
|
|
133287
|
+
deps.defer(() => {
|
|
133288
|
+
placeholder(entry, t("panel.title"), t("panel.working"));
|
|
133289
|
+
deps.enqueue(
|
|
133290
|
+
entry.chatKey,
|
|
133291
|
+
() => guardBusy(entry, async () => {
|
|
133292
|
+
try {
|
|
133293
|
+
await job();
|
|
133294
|
+
} catch (error) {
|
|
133295
|
+
deps.warn("panel could not run a command", error);
|
|
133296
|
+
}
|
|
133297
|
+
await backToMenu(entry);
|
|
133298
|
+
})
|
|
133299
|
+
);
|
|
133300
|
+
});
|
|
133301
|
+
};
|
|
133302
|
+
const enter = (entry, kind) => {
|
|
133303
|
+
entry.busy = true;
|
|
133304
|
+
deps.defer(() => {
|
|
133305
|
+
placeholder(entry, viewTitle(kind), t("panel.loading"));
|
|
133306
|
+
deps.enqueue(
|
|
133307
|
+
entry.chatKey,
|
|
133308
|
+
() => guardBusy(entry, async () => {
|
|
133309
|
+
try {
|
|
133310
|
+
entry.stack.push(await loadView(entry.chatKey, kind));
|
|
133311
|
+
} catch (error) {
|
|
133312
|
+
deps.warn("panel could not load a view", error);
|
|
133313
|
+
entry.stack.push({
|
|
133314
|
+
kind: "notice",
|
|
133315
|
+
title: viewTitle(kind),
|
|
133316
|
+
text: t("panel.loadFailed")
|
|
133317
|
+
});
|
|
133318
|
+
}
|
|
133319
|
+
await show(entry);
|
|
133320
|
+
})
|
|
133321
|
+
);
|
|
133322
|
+
});
|
|
133323
|
+
};
|
|
133324
|
+
const navigate = (entry, change) => {
|
|
133325
|
+
change();
|
|
133326
|
+
deps.defer(() => void show(entry));
|
|
133327
|
+
};
|
|
133328
|
+
return {
|
|
133329
|
+
/** 开一张新面板卡(`/help`)。发不出就抛,调用方退回文字版。 */
|
|
133330
|
+
open: async (chatKey) => {
|
|
133331
|
+
const surface = deps.surface(chatKey);
|
|
133332
|
+
if (surface === null) throw new Error("this chat has no panel");
|
|
133333
|
+
const entry = { cardId: "", chatKey, stack: [{ kind: "menu" }], busy: false };
|
|
133334
|
+
const cardId = await surface.open(menuCard(chatKey));
|
|
133335
|
+
if (cardId === null) throw new Error("the panel card was sent without an id");
|
|
133336
|
+
entry.cardId = cardId;
|
|
133337
|
+
remember(entry);
|
|
133338
|
+
},
|
|
133339
|
+
/**
|
|
133340
|
+
* 处理一次点击,同步回给点击者的一句提示(null 就是没话说)。取数据、跑命令、改卡都
|
|
133341
|
+
* 在回调应答之后做。
|
|
133342
|
+
*/
|
|
133343
|
+
click: (click) => {
|
|
133344
|
+
const entry = cards.get(click.cardId);
|
|
133345
|
+
if (entry === void 0 || entry.chatKey !== click.chatKey) return t("panel.expired");
|
|
133346
|
+
if (entry.busy) return t("panel.busy");
|
|
133347
|
+
const top = entry.stack.at(-1) ?? { kind: "menu" };
|
|
133348
|
+
const arg = click.arg ?? "";
|
|
133349
|
+
switch (click.op) {
|
|
133350
|
+
case "back":
|
|
133351
|
+
if (entry.stack.length > 1) navigate(entry, () => void entry.stack.pop());
|
|
133352
|
+
return null;
|
|
133353
|
+
case "page": {
|
|
133354
|
+
const page = Number(arg);
|
|
133355
|
+
if ((top.kind === "sessions" || top.kind === "teams" || top.kind === "models") && Number.isInteger(page) && page >= 0) {
|
|
133356
|
+
navigate(entry, () => {
|
|
133357
|
+
entry.stack[entry.stack.length - 1] = { ...top, page };
|
|
133358
|
+
});
|
|
133359
|
+
}
|
|
133360
|
+
return null;
|
|
133361
|
+
}
|
|
133362
|
+
case "open":
|
|
133363
|
+
if (top.kind === "menu" && PICK_KINDS.has(arg)) enter(entry, arg);
|
|
133364
|
+
return null;
|
|
133365
|
+
case "run": {
|
|
133366
|
+
if (top.kind !== "menu") return null;
|
|
133367
|
+
if (arg === "help") {
|
|
133368
|
+
operate(entry, () => deps.sendHelpText(entry.chatKey));
|
|
133369
|
+
return null;
|
|
133370
|
+
}
|
|
133371
|
+
const line = PANEL_RUN_LINES[arg];
|
|
133372
|
+
if (line !== void 0) operate(entry, () => deps.runLine(entry.chatKey, line));
|
|
133373
|
+
return null;
|
|
133374
|
+
}
|
|
133375
|
+
case "pick": {
|
|
133376
|
+
const split = arg.indexOf(":");
|
|
133377
|
+
const key = split < 0 ? "" : arg.slice(split + 1);
|
|
133378
|
+
const line = top.kind === "sessions" && arg.startsWith("sessions:") ? top.rows.find((row) => row.threadId === key) === void 0 ? null : `/resume ${key}` : top.kind === "teams" && arg.startsWith("teams:") ? top.teams?.find((team) => team.teamId === key) === void 0 ? null : `/team ${key}` : top.kind === "models" && arg.startsWith("models:") ? top.models?.choices.find(
|
|
133379
|
+
(choice) => `${choice.instanceId}/${choice.model}` === key
|
|
133380
|
+
) === void 0 ? null : `/model ${key}` : null;
|
|
133381
|
+
if (line === null) {
|
|
133382
|
+
deps.warn("panel ignored a pick that is not in the current view");
|
|
133383
|
+
return null;
|
|
133384
|
+
}
|
|
133385
|
+
operate(entry, () => deps.runLine(entry.chatKey, line));
|
|
133386
|
+
return null;
|
|
133387
|
+
}
|
|
133388
|
+
default:
|
|
133389
|
+
deps.warn(`panel ignored an unknown action: ${click.op}`);
|
|
133390
|
+
return null;
|
|
133391
|
+
}
|
|
133392
|
+
}
|
|
133393
|
+
};
|
|
133394
|
+
};
|
|
133395
|
+
|
|
132848
133396
|
// src/telegramApi.ts
|
|
132849
133397
|
var API_BASE = "https://api.telegram.org";
|
|
132850
133398
|
var POLL_TIMEOUT_SECONDS = 45;
|
|
@@ -134405,9 +134953,10 @@ ${final.text}`;
|
|
|
134405
134953
|
commit("chat-updated", { chatKey, plan: next });
|
|
134406
134954
|
await deliver(binding, t(next ? "plan.on" : "plan.off"));
|
|
134407
134955
|
};
|
|
134408
|
-
const
|
|
134956
|
+
const hasPanel = (binding) => adapterFor(binding.platform)?.panel !== void 0;
|
|
134957
|
+
const handleCommand = async (binding, text2, teams) => {
|
|
134409
134958
|
const command = parseChatCommand({
|
|
134410
|
-
text:
|
|
134959
|
+
text: text2,
|
|
134411
134960
|
teams,
|
|
134412
134961
|
// 中文别名只在回话语言是中文时认,免得英文用户打一个「停」就被当成命令。
|
|
134413
134962
|
chinese: t.language === "zh-CN"
|
|
@@ -134451,7 +135000,20 @@ ${final.text}`;
|
|
|
134451
135000
|
await deliver(binding, t("command.noTeams"));
|
|
134452
135001
|
return true;
|
|
134453
135002
|
case "help":
|
|
134454
|
-
await deliver(binding, helpText(t));
|
|
135003
|
+
await deliver(binding, helpText(t, { panel: hasPanel(binding) }));
|
|
135004
|
+
return true;
|
|
135005
|
+
case "panel":
|
|
135006
|
+
if (hasPanel(binding)) {
|
|
135007
|
+
try {
|
|
135008
|
+
await panel.open(chatKey);
|
|
135009
|
+
return true;
|
|
135010
|
+
} catch (error) {
|
|
135011
|
+
ctx.log.warn("chat could not open the command panel", {
|
|
135012
|
+
error: describeError(error)
|
|
135013
|
+
});
|
|
135014
|
+
}
|
|
135015
|
+
}
|
|
135016
|
+
await deliver(binding, helpText(t, { panel: false }));
|
|
134455
135017
|
return true;
|
|
134456
135018
|
case "sessions":
|
|
134457
135019
|
await showSessions(binding);
|
|
@@ -134640,9 +135202,75 @@ ${final.text}`;
|
|
|
134640
135202
|
});
|
|
134641
135203
|
chatQueues.set(chatKey, run);
|
|
134642
135204
|
};
|
|
135205
|
+
const panel = makePanelController({
|
|
135206
|
+
t,
|
|
135207
|
+
surface: (chatKey) => {
|
|
135208
|
+
const binding = bindingOfChat(chatKey);
|
|
135209
|
+
const surface = binding === void 0 ? void 0 : adapterFor(binding.platform)?.panel;
|
|
135210
|
+
if (binding === void 0 || surface === void 0) return null;
|
|
135211
|
+
return {
|
|
135212
|
+
open: (card) => surface.open(binding, card),
|
|
135213
|
+
update: (cardId, card) => surface.update(binding, cardId, card)
|
|
135214
|
+
};
|
|
135215
|
+
},
|
|
135216
|
+
planOn: (chatKey) => chatOf(state, chatKey).plan,
|
|
135217
|
+
loadSessions: async (chatKey) => {
|
|
135218
|
+
const binding = bindingOfChat(chatKey);
|
|
135219
|
+
if (binding === void 0) return [];
|
|
135220
|
+
const rows = await sessionRowsFor(binding);
|
|
135221
|
+
listedByChat.set(chatKey, listedThreadIds(rows));
|
|
135222
|
+
return rows;
|
|
135223
|
+
},
|
|
135224
|
+
loadTeams: async (chatKey) => {
|
|
135225
|
+
const binding = bindingOfChat(chatKey);
|
|
135226
|
+
return binding === void 0 ? null : fetchTeams(binding);
|
|
135227
|
+
},
|
|
135228
|
+
loadModels: async (chatKey) => {
|
|
135229
|
+
const binding = bindingOfChat(chatKey);
|
|
135230
|
+
const instances = await ctx.providers.listInstances().catch(() => null);
|
|
135231
|
+
if (binding === void 0 || instances === null) return null;
|
|
135232
|
+
return { current: await currentModelOf(binding), choices: modelChoices(instances) };
|
|
135233
|
+
},
|
|
135234
|
+
runLine: async (chatKey, line) => {
|
|
135235
|
+
const binding = bindingOfChat(chatKey);
|
|
135236
|
+
if (binding === void 0) return;
|
|
135237
|
+
await handleCommand(binding, line, await fetchTeams(binding) ?? []);
|
|
135238
|
+
},
|
|
135239
|
+
sendHelpText: async (chatKey) => {
|
|
135240
|
+
const binding = bindingOfChat(chatKey);
|
|
135241
|
+
if (binding !== void 0) await deliver(binding, helpText(t));
|
|
135242
|
+
},
|
|
135243
|
+
enqueue: enqueueForChat,
|
|
135244
|
+
// 改卡必须排在回调应答之后:飞书在应答不带改卡时会把卡恢复成点击前的样子。
|
|
135245
|
+
defer: (run) => {
|
|
135246
|
+
setTimeout(run, 0);
|
|
135247
|
+
},
|
|
135248
|
+
now: () => Date.now(),
|
|
135249
|
+
warn: (message, error) => {
|
|
135250
|
+
ctx.log.warn(`chat ${message}`, error === void 0 ? {} : { error: describeError(error) });
|
|
135251
|
+
}
|
|
135252
|
+
});
|
|
135253
|
+
const handlePanelClick = (platform, click) => {
|
|
135254
|
+
const target = bindingForChat(platform, click.chatId);
|
|
135255
|
+
if (target === void 0 || target.projectId === "") return null;
|
|
135256
|
+
if (!isAllowedSender(target, click.operatorId)) {
|
|
135257
|
+
ctx.log.warn("chat ignored a panel click from someone not on the allow list", {
|
|
135258
|
+
group: target.displayName,
|
|
135259
|
+
operator: click.operatorId
|
|
135260
|
+
});
|
|
135261
|
+
return t("reply.notAllowed");
|
|
135262
|
+
}
|
|
135263
|
+
return panel.click({
|
|
135264
|
+
chatKey: chatKeyOf(target),
|
|
135265
|
+
cardId: click.cardId,
|
|
135266
|
+
op: click.op,
|
|
135267
|
+
arg: click.arg
|
|
135268
|
+
});
|
|
135269
|
+
};
|
|
134643
135270
|
const inboundHandlersFor = (platform) => ({
|
|
134644
135271
|
onError: (message) => ctx.log.warn("chat inbound connection failed", { platform, message }),
|
|
134645
135272
|
onApproval: handleApprovalClick,
|
|
135273
|
+
onPanelClick: (click) => handlePanelClick(platform, click),
|
|
134646
135274
|
onMessage: (message) => {
|
|
134647
135275
|
const target = bindingForChat(platform, message.chatId);
|
|
134648
135276
|
if (target === void 0 || target.projectId === "") return;
|
|
@@ -134668,7 +135296,7 @@ ${final.text}`;
|
|
|
134668
135296
|
}
|
|
134669
135297
|
enqueueForChat(chatKeyOf(target), async () => {
|
|
134670
135298
|
const teams = await fetchTeams(target);
|
|
134671
|
-
if (await handleCommand(target, message, teams ?? [])) return;
|
|
135299
|
+
if (await handleCommand(target, message.text, teams ?? [])) return;
|
|
134672
135300
|
const project = await ctx.projections.getProject(target.projectId);
|
|
134673
135301
|
if (project === null || project.archivedAt != null) {
|
|
134674
135302
|
if (project === null) {
|
package/locales/en.json
CHANGED
|
@@ -128,7 +128,8 @@
|
|
|
128
128
|
"sessions": "· /sessions — list the sessions this bot opened for this group's project",
|
|
129
129
|
"resume": "· /resume number — switch to one of them (a number from /sessions, or the start of its id)",
|
|
130
130
|
"model": "· /model — see the model this group uses; /model number or /model instance/model switches it",
|
|
131
|
-
"plan": "· /plan — toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it"
|
|
131
|
+
"plan": "· /plan — toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it",
|
|
132
|
+
"panel": "Send /panel to open the command panel with a button for every command."
|
|
132
133
|
},
|
|
133
134
|
"sessions": {
|
|
134
135
|
"title": "Sessions this bot opened for this group's project ({{count}}):",
|
|
@@ -201,5 +202,47 @@
|
|
|
201
202
|
"chosen": "Chosen: {{label}}",
|
|
202
203
|
"elsewhere": "Already handled elsewhere (the web app or another client)."
|
|
203
204
|
}
|
|
205
|
+
},
|
|
206
|
+
"panel": {
|
|
207
|
+
"title": "Command panel",
|
|
208
|
+
"hint": "A button does the same as typing its command. You can still type.",
|
|
209
|
+
"category": {
|
|
210
|
+
"session": "Session",
|
|
211
|
+
"target": "Send to",
|
|
212
|
+
"system": "Settings and status"
|
|
213
|
+
},
|
|
214
|
+
"cmd": {
|
|
215
|
+
"new": "New session",
|
|
216
|
+
"stop": "Stop this turn",
|
|
217
|
+
"sessions": "Sessions",
|
|
218
|
+
"team": "Send to a team",
|
|
219
|
+
"thread": "Normal session",
|
|
220
|
+
"model": "Model",
|
|
221
|
+
"planOn": "Plan mode: on",
|
|
222
|
+
"planOff": "Plan mode: off",
|
|
223
|
+
"status": "Status",
|
|
224
|
+
"help": "Text help"
|
|
225
|
+
},
|
|
226
|
+
"back": "Back",
|
|
227
|
+
"prev": "Previous",
|
|
228
|
+
"next": "Next",
|
|
229
|
+
"page": "Page {{page}} of {{total}}",
|
|
230
|
+
"loading": "Loading…",
|
|
231
|
+
"working": "Working on it…",
|
|
232
|
+
"expired": "This panel has expired. Send /panel again.",
|
|
233
|
+
"busy": "The last tap is still being handled. Give it a moment.",
|
|
234
|
+
"loadFailed": "Could not read that just now. Try again in a moment.",
|
|
235
|
+
"sessions": {
|
|
236
|
+
"title": "Pick a session",
|
|
237
|
+
"hint": "Tap one to switch to it."
|
|
238
|
+
},
|
|
239
|
+
"teams": {
|
|
240
|
+
"title": "Pick a team",
|
|
241
|
+
"hint": "Tap a team and the next messages go to it.",
|
|
242
|
+
"unavailable": "Could not read the team list."
|
|
243
|
+
},
|
|
244
|
+
"models": {
|
|
245
|
+
"title": "Pick a model"
|
|
246
|
+
}
|
|
204
247
|
}
|
|
205
248
|
}
|
package/locales/zh-CN.json
CHANGED
|
@@ -128,7 +128,8 @@
|
|
|
128
128
|
"sessions": "· /sessions —— 列出本机器人为这个群的项目开过的会话",
|
|
129
129
|
"resume": "· /resume 编号 —— 切到其中一条(/sessions 里的编号,或会话 id 的开头)",
|
|
130
130
|
"model": "· /model —— 看这个群用的模型;/model 编号 或 /model 实例/模型 切换",
|
|
131
|
-
"plan": "· /plan —— 开关计划模式(agent 先出计划、不动手);/plan on、/plan off 明确设定"
|
|
131
|
+
"plan": "· /plan —— 开关计划模式(agent 先出计划、不动手);/plan on、/plan off 明确设定",
|
|
132
|
+
"panel": "发 /panel 打开可点的命令面板。"
|
|
132
133
|
},
|
|
133
134
|
"sessions": {
|
|
134
135
|
"title": "本机器人为这个群的项目开过的会话({{count}} 条):",
|
|
@@ -201,5 +202,47 @@
|
|
|
201
202
|
"chosen": "已选:{{label}}",
|
|
202
203
|
"elsewhere": "已在别处处理(网页或其他客户端)。"
|
|
203
204
|
}
|
|
205
|
+
},
|
|
206
|
+
"panel": {
|
|
207
|
+
"title": "命令面板",
|
|
208
|
+
"hint": "点按钮等于打出对应的命令,也可以直接打字。",
|
|
209
|
+
"category": {
|
|
210
|
+
"session": "会话",
|
|
211
|
+
"target": "派给谁",
|
|
212
|
+
"system": "设置与状态"
|
|
213
|
+
},
|
|
214
|
+
"cmd": {
|
|
215
|
+
"new": "新会话",
|
|
216
|
+
"stop": "停止本轮",
|
|
217
|
+
"sessions": "会话列表",
|
|
218
|
+
"team": "派给团队",
|
|
219
|
+
"thread": "普通会话",
|
|
220
|
+
"model": "模型",
|
|
221
|
+
"planOn": "计划模式:开",
|
|
222
|
+
"planOff": "计划模式:关",
|
|
223
|
+
"status": "状态",
|
|
224
|
+
"help": "文字说明"
|
|
225
|
+
},
|
|
226
|
+
"back": "返回",
|
|
227
|
+
"prev": "上一页",
|
|
228
|
+
"next": "下一页",
|
|
229
|
+
"page": "第 {{page}} / {{total}} 页",
|
|
230
|
+
"loading": "加载中…",
|
|
231
|
+
"working": "处理中…",
|
|
232
|
+
"expired": "面板已过期,再发一次 /panel。",
|
|
233
|
+
"busy": "上一步还在处理,稍等一下。",
|
|
234
|
+
"loadFailed": "刚才没读出来,稍后再试。",
|
|
235
|
+
"sessions": {
|
|
236
|
+
"title": "选一条会话",
|
|
237
|
+
"hint": "点一条切过去。"
|
|
238
|
+
},
|
|
239
|
+
"teams": {
|
|
240
|
+
"title": "选一支队",
|
|
241
|
+
"hint": "点一支队,接下来的活派给它。",
|
|
242
|
+
"unavailable": "读不到团队列表。"
|
|
243
|
+
},
|
|
244
|
+
"models": {
|
|
245
|
+
"title": "选模型"
|
|
246
|
+
}
|
|
204
247
|
}
|
|
205
248
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/plugin-im",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2570",
|
|
4
4
|
"description": "Talk to your agents from Telegram, WeCom or Lark.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"im",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"esbuild": "0.28.1",
|
|
28
28
|
"markdown-it": "^15.0.2",
|
|
29
29
|
"vite-plus": "0.2.2",
|
|
30
|
-
"@ycodium-ai/plugin-api": "0.2.
|
|
30
|
+
"@ycodium-ai/plugin-api": "0.2.2570"
|
|
31
31
|
},
|
|
32
32
|
"ycodium": {
|
|
33
33
|
"plugin": "./dist/index.js",
|