@ycodium-ai/plugin-im 0.2.3192 → 0.2.3208
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/assets/ui.js +7 -4
- package/dist/index.js +1289 -562
- package/locales/en.json +37 -16
- package/locales/zh-CN.json +38 -17
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -137,7 +137,7 @@ var require_punycode = __commonJS({
|
|
|
137
137
|
}
|
|
138
138
|
return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
|
|
139
139
|
};
|
|
140
|
-
var
|
|
140
|
+
var decode3 = function(input) {
|
|
141
141
|
const output = [];
|
|
142
142
|
const inputLength = input.length;
|
|
143
143
|
let i = 0;
|
|
@@ -249,7 +249,7 @@ var require_punycode = __commonJS({
|
|
|
249
249
|
};
|
|
250
250
|
var toUnicode = function(input) {
|
|
251
251
|
return mapDomain(input, function(string) {
|
|
252
|
-
return regexPunycode.test(string) ?
|
|
252
|
+
return regexPunycode.test(string) ? decode3(string.slice(4).toLowerCase()) : string;
|
|
253
253
|
});
|
|
254
254
|
};
|
|
255
255
|
var toASCII = function(input) {
|
|
@@ -275,7 +275,7 @@ var require_punycode = __commonJS({
|
|
|
275
275
|
"decode": ucs2decode,
|
|
276
276
|
"encode": ucs2encode
|
|
277
277
|
},
|
|
278
|
-
"decode":
|
|
278
|
+
"decode": decode3,
|
|
279
279
|
"encode": encode2,
|
|
280
280
|
"toASCII": toASCII,
|
|
281
281
|
"toUnicode": toUnicode
|
|
@@ -18475,7 +18475,7 @@ var require_utils = __commonJS({
|
|
|
18475
18475
|
return acc;
|
|
18476
18476
|
}, target2);
|
|
18477
18477
|
};
|
|
18478
|
-
var
|
|
18478
|
+
var decode3 = function(str2, defaultDecoder, charset) {
|
|
18479
18479
|
var strWithoutPlus = str2.replace(/\+/g, " ");
|
|
18480
18480
|
if (charset === "iso-8859-1") {
|
|
18481
18481
|
return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);
|
|
@@ -18601,7 +18601,7 @@ var require_utils = __commonJS({
|
|
|
18601
18601
|
assign,
|
|
18602
18602
|
combine,
|
|
18603
18603
|
compact,
|
|
18604
|
-
decode:
|
|
18604
|
+
decode: decode3,
|
|
18605
18605
|
encode: encode2,
|
|
18606
18606
|
isBuffer,
|
|
18607
18607
|
isOverflow,
|
|
@@ -23360,7 +23360,7 @@ var require_websocket = __commonJS({
|
|
|
23360
23360
|
var http = __require("http");
|
|
23361
23361
|
var net = __require("net");
|
|
23362
23362
|
var tls = __require("tls");
|
|
23363
|
-
var { randomBytes, createHash:
|
|
23363
|
+
var { randomBytes, createHash: createHash7 } = __require("crypto");
|
|
23364
23364
|
var { Duplex, Readable } = __require("stream");
|
|
23365
23365
|
var { URL: URL2 } = __require("url");
|
|
23366
23366
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -24028,7 +24028,7 @@ var require_websocket = __commonJS({
|
|
|
24028
24028
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
24029
24029
|
return;
|
|
24030
24030
|
}
|
|
24031
|
-
const digest =
|
|
24031
|
+
const digest = createHash7("sha1").update(key + GUID).digest("base64");
|
|
24032
24032
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
24033
24033
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
24034
24034
|
return;
|
|
@@ -24397,7 +24397,7 @@ var require_websocket_server = __commonJS({
|
|
|
24397
24397
|
var EventEmitter = __require("events");
|
|
24398
24398
|
var http = __require("http");
|
|
24399
24399
|
var { Duplex } = __require("stream");
|
|
24400
|
-
var { createHash:
|
|
24400
|
+
var { createHash: createHash7 } = __require("crypto");
|
|
24401
24401
|
var extension = require_extension();
|
|
24402
24402
|
var PerMessageDeflate = require_permessage_deflate();
|
|
24403
24403
|
var subprotocol = require_subprotocol();
|
|
@@ -24704,7 +24704,7 @@ var require_websocket_server = __commonJS({
|
|
|
24704
24704
|
);
|
|
24705
24705
|
}
|
|
24706
24706
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
24707
|
-
const digest =
|
|
24707
|
+
const digest = createHash7("sha1").update(key + GUID).digest("base64");
|
|
24708
24708
|
const headers = [
|
|
24709
24709
|
"HTTP/1.1 101 Switching Protocols",
|
|
24710
24710
|
"Upgrade: websocket",
|
|
@@ -24912,7 +24912,7 @@ var require_base64 = __commonJS({
|
|
|
24912
24912
|
return String.fromCharCode.apply(String, chunk.slice(0, i2));
|
|
24913
24913
|
};
|
|
24914
24914
|
var invalidEncoding = "invalid encoding";
|
|
24915
|
-
base64.decode = function
|
|
24915
|
+
base64.decode = function decode3(string, buffer, offset) {
|
|
24916
24916
|
var start = offset;
|
|
24917
24917
|
var j = 0, t;
|
|
24918
24918
|
for (var i2 = 0; i2 < string.length; ) {
|
|
@@ -121305,7 +121305,7 @@ var require_lib2 = __commonJS({
|
|
|
121305
121305
|
Header.encodeDelimited = function encodeDelimited(message, writer) {
|
|
121306
121306
|
return this.encode(message, writer).ldelim();
|
|
121307
121307
|
};
|
|
121308
|
-
Header.decode = function
|
|
121308
|
+
Header.decode = function decode4(reader, length) {
|
|
121309
121309
|
if (!(reader instanceof $Reader))
|
|
121310
121310
|
reader = $Reader.create(reader);
|
|
121311
121311
|
let end = length === void 0 ? reader.len : reader.pos + length, message = new $root.pbbp2.Header();
|
|
@@ -121451,7 +121451,7 @@ var require_lib2 = __commonJS({
|
|
|
121451
121451
|
Frame.encodeDelimited = function encodeDelimited(message, writer) {
|
|
121452
121452
|
return this.encode(message, writer).ldelim();
|
|
121453
121453
|
};
|
|
121454
|
-
Frame.decode = function
|
|
121454
|
+
Frame.decode = function decode4(reader, length) {
|
|
121455
121455
|
if (!(reader instanceof $Reader))
|
|
121456
121456
|
reader = $Reader.create(reader);
|
|
121457
121457
|
let end = length === void 0 ? reader.len : reader.pos + length, message = new $root.pbbp2.Frame();
|
|
@@ -121678,7 +121678,7 @@ var require_lib2 = __commonJS({
|
|
|
121678
121678
|
})();
|
|
121679
121679
|
return pbbp22;
|
|
121680
121680
|
})();
|
|
121681
|
-
var
|
|
121681
|
+
var decode3 = (buffer) => {
|
|
121682
121682
|
return pbbp2.Frame.decode(buffer);
|
|
121683
121683
|
};
|
|
121684
121684
|
var WSConfig = class {
|
|
@@ -122177,7 +122177,7 @@ var require_lib2 = __commonJS({
|
|
|
122177
122177
|
const wsInstance = this.wsConfig.getWSInstance();
|
|
122178
122178
|
wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.on("message", (buffer) => __awaiter(this, void 0, void 0, function* () {
|
|
122179
122179
|
this.clearLiveness();
|
|
122180
|
-
const data =
|
|
122180
|
+
const data = decode3(buffer);
|
|
122181
122181
|
const { method } = data;
|
|
122182
122182
|
if (method === FrameType.control) {
|
|
122183
122183
|
yield this.handleControlData(data);
|
|
@@ -126185,22 +126185,26 @@ var makeAcknowledgements = (core) => {
|
|
|
126185
126185
|
host.log.warn("chat could not acknowledge the message", { error: describeError(error) });
|
|
126186
126186
|
}
|
|
126187
126187
|
};
|
|
126188
|
-
const
|
|
126188
|
+
const takeWorking = (binding, threadId, outcome) => {
|
|
126189
126189
|
const adapter = adapterFor(binding.platform);
|
|
126190
|
+
const clear = adapter?.clearAcknowledge?.bind(adapter);
|
|
126190
126191
|
const pending = acknowledgements.get(threadId);
|
|
126191
|
-
if (pending === void 0 ||
|
|
126192
|
+
if (pending === void 0 || clear === void 0) return async () => void 0;
|
|
126192
126193
|
acknowledgements.delete(threadId);
|
|
126193
|
-
|
|
126194
|
-
|
|
126195
|
-
|
|
126196
|
-
|
|
126197
|
-
|
|
126198
|
-
|
|
126199
|
-
|
|
126194
|
+
return async () => {
|
|
126195
|
+
for (const entry of pending) {
|
|
126196
|
+
try {
|
|
126197
|
+
await clear(binding, entry.messageId, entry.ackId, outcome);
|
|
126198
|
+
} catch (error) {
|
|
126199
|
+
host.log.warn("chat could not clear the acknowledgement", {
|
|
126200
|
+
error: describeError(error)
|
|
126201
|
+
});
|
|
126202
|
+
}
|
|
126200
126203
|
}
|
|
126201
|
-
}
|
|
126204
|
+
};
|
|
126202
126205
|
};
|
|
126203
|
-
|
|
126206
|
+
const clearWorking = (binding, threadId, outcome) => takeWorking(binding, threadId, outcome)();
|
|
126207
|
+
return { markWorking, takeWorking, clearWorking };
|
|
126204
126208
|
};
|
|
126205
126209
|
|
|
126206
126210
|
// src/botConfig.ts
|
|
@@ -126401,7 +126405,7 @@ var makeConclusionLedger = (core) => {
|
|
|
126401
126405
|
return false;
|
|
126402
126406
|
inFlight.add(key);
|
|
126403
126407
|
try {
|
|
126404
|
-
const sent = await send();
|
|
126408
|
+
const sent = await send(() => conclusionDebtsOf(core.state)[key] !== void 0);
|
|
126405
126409
|
if (sent) settle(debt);
|
|
126406
126410
|
return sent;
|
|
126407
126411
|
} finally {
|
|
@@ -126837,6 +126841,7 @@ var readString = (row, key) => {
|
|
|
126837
126841
|
|
|
126838
126842
|
// src/settingsContract.ts
|
|
126839
126843
|
var SETTINGS_VIEW_COMMAND = "settings.view";
|
|
126844
|
+
var SETTINGS_CONNECTIONS_COMMAND = "settings.connections";
|
|
126840
126845
|
var SETTINGS_SAVE_BOT_COMMAND = "settings.saveBot";
|
|
126841
126846
|
var SETTINGS_REMOVE_BOT_COMMAND = "settings.removeBot";
|
|
126842
126847
|
var SETTINGS_SAVE_GENERAL_COMMAND = "settings.saveGeneral";
|
|
@@ -126877,6 +126882,7 @@ var foldFlowItem = (snapshot, item) => {
|
|
|
126877
126882
|
}
|
|
126878
126883
|
}
|
|
126879
126884
|
};
|
|
126885
|
+
var connectionHintKey = (hint) => hint === "notAvailable" ? "settingsSection.connection.reason.not-available" : `rowAction.checkConnection.hint.${hint}`;
|
|
126880
126886
|
|
|
126881
126887
|
// src/targetKind.ts
|
|
126882
126888
|
var TARGET_KIND_OPTIONS = [
|
|
@@ -127410,6 +127416,31 @@ var makeChatQueue = (core) => {
|
|
|
127410
127416
|
// src/chatSetup.ts
|
|
127411
127417
|
import { threadOrNull } from "@ycodium-ai/plugin-api";
|
|
127412
127418
|
|
|
127419
|
+
// src/cooldown.ts
|
|
127420
|
+
var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
|
|
127421
|
+
const lastAt = /* @__PURE__ */ new Map();
|
|
127422
|
+
return {
|
|
127423
|
+
tryAcquire: (key) => {
|
|
127424
|
+
const current = now();
|
|
127425
|
+
const previous = lastAt.get(key);
|
|
127426
|
+
if (previous !== void 0 && current - previous < windowMs) return false;
|
|
127427
|
+
lastAt.delete(key);
|
|
127428
|
+
lastAt.set(key, current);
|
|
127429
|
+
if (lastAt.size > maxKeys) {
|
|
127430
|
+
for (const [candidate, at] of lastAt) {
|
|
127431
|
+
if (current - at >= windowMs) lastAt.delete(candidate);
|
|
127432
|
+
}
|
|
127433
|
+
while (lastAt.size > maxKeys) {
|
|
127434
|
+
const oldest = lastAt.keys().next().value;
|
|
127435
|
+
if (oldest === void 0) break;
|
|
127436
|
+
lastAt.delete(oldest);
|
|
127437
|
+
}
|
|
127438
|
+
}
|
|
127439
|
+
return true;
|
|
127440
|
+
}
|
|
127441
|
+
};
|
|
127442
|
+
};
|
|
127443
|
+
|
|
127413
127444
|
// src/projectList.ts
|
|
127414
127445
|
var PROJECT_LIST_MAX = 20;
|
|
127415
127446
|
var projectLine = (row, position) => `${String(position)}. ${row.title.replace(/\s+/g, " ").trim()}${row.current ? " \u2605" : ""}`;
|
|
@@ -127498,16 +127529,21 @@ var pickSession = (arg, listed, rows) => {
|
|
|
127498
127529
|
var makeChatSetup = (core, deps) => {
|
|
127499
127530
|
const { host, t, adapterFor, commit, deliver, bindingOf: bindingOf2, hasPanel } = core;
|
|
127500
127531
|
const { projectRowsFor, listedProjectsByChat } = deps.rows;
|
|
127501
|
-
const
|
|
127502
|
-
|
|
127503
|
-
|
|
127532
|
+
const pendingMessages = makeBoundedMap(100);
|
|
127533
|
+
const projectPromptCooldown = makeCooldown(3e4, () => Date.now());
|
|
127534
|
+
const loadProjectRows = async (binding) => {
|
|
127504
127535
|
try {
|
|
127505
|
-
|
|
127536
|
+
return await projectRowsFor(chatKeyOf(binding));
|
|
127506
127537
|
} catch (error) {
|
|
127507
127538
|
host.log.warn("chat could not list the projects", { error: describeError(error) });
|
|
127508
127539
|
await deliver(binding, t("panel.loadFailed"));
|
|
127509
|
-
return;
|
|
127540
|
+
return null;
|
|
127510
127541
|
}
|
|
127542
|
+
};
|
|
127543
|
+
const promptProject = async (binding, available) => {
|
|
127544
|
+
const chatKey = chatKeyOf(binding);
|
|
127545
|
+
const rows = available ?? await loadProjectRows(binding);
|
|
127546
|
+
if (rows === null) return;
|
|
127511
127547
|
listedProjectsByChat.set(chatKey, listedProjectIds(rows));
|
|
127512
127548
|
if (rows.length > 0 && hasPanel(binding)) {
|
|
127513
127549
|
try {
|
|
@@ -127527,14 +127563,8 @@ var makeChatSetup = (core, deps) => {
|
|
|
127527
127563
|
return;
|
|
127528
127564
|
}
|
|
127529
127565
|
const chatKey = chatKeyOf(binding);
|
|
127530
|
-
|
|
127531
|
-
|
|
127532
|
-
rows = await projectRowsFor(chatKey);
|
|
127533
|
-
} catch (error) {
|
|
127534
|
-
host.log.warn("chat could not list the projects", { error: describeError(error) });
|
|
127535
|
-
await deliver(binding, t("panel.loadFailed"));
|
|
127536
|
-
return;
|
|
127537
|
-
}
|
|
127566
|
+
const rows = await loadProjectRows(binding);
|
|
127567
|
+
if (rows === null) return;
|
|
127538
127568
|
const picked = pickProject(arg, listedProjectsByChat.get(chatKey), rows);
|
|
127539
127569
|
if (picked === null) {
|
|
127540
127570
|
await deliver(binding, t("repo.notFound", { query: arg }));
|
|
@@ -127566,6 +127596,9 @@ var makeChatSetup = (core, deps) => {
|
|
|
127566
127596
|
listedProjectsByChat.delete(chatKey);
|
|
127567
127597
|
host.log.info("chat picked a project", { bot: binding.displayName, projectId: picked });
|
|
127568
127598
|
await deliver(binding, t("repo.done", { title: target2.title }));
|
|
127599
|
+
const pending = pendingMessages.get(chatKey);
|
|
127600
|
+
pendingMessages.forget((key) => key === chatKey);
|
|
127601
|
+
if (pending !== void 0) await deps.resumeMessage(binding, pending);
|
|
127569
127602
|
};
|
|
127570
127603
|
const handleGroup = async (binding, name, senderId) => {
|
|
127571
127604
|
const adapter = adapterFor(binding.platform);
|
|
@@ -127593,7 +127626,27 @@ var makeChatSetup = (core, deps) => {
|
|
|
127593
127626
|
await deliver(binding, t("group.created", { name }));
|
|
127594
127627
|
await promptProject(bindingOf2(binding, chatId));
|
|
127595
127628
|
};
|
|
127596
|
-
|
|
127629
|
+
const prepareMessage = async (binding, message) => {
|
|
127630
|
+
const chatKey = chatKeyOf(binding);
|
|
127631
|
+
const asked = message.text.trim();
|
|
127632
|
+
if (/^[1-9]\d{0,2}$/u.test(asked) && listedProjectsByChat.has(chatKey)) {
|
|
127633
|
+
await handleRepo(binding, asked);
|
|
127634
|
+
return false;
|
|
127635
|
+
}
|
|
127636
|
+
const rows = await loadProjectRows(binding);
|
|
127637
|
+
if (rows === null) return false;
|
|
127638
|
+
const only = rows.length === 1 ? rows[0] : void 0;
|
|
127639
|
+
if (only !== void 0) {
|
|
127640
|
+
commit("chat-updated", { chatKey, projectId: only.projectId });
|
|
127641
|
+
pendingMessages.forget((key) => key === chatKey);
|
|
127642
|
+
await deliver(binding, t("repo.autoSelected", { title: only.title }));
|
|
127643
|
+
return true;
|
|
127644
|
+
}
|
|
127645
|
+
pendingMessages.set(chatKey, message);
|
|
127646
|
+
if (projectPromptCooldown.tryAcquire(chatKey)) await promptProject(binding, rows);
|
|
127647
|
+
return false;
|
|
127648
|
+
};
|
|
127649
|
+
return { promptProject, handleRepo, handleGroup, prepareMessage };
|
|
127597
127650
|
};
|
|
127598
127651
|
|
|
127599
127652
|
// src/commands.ts
|
|
@@ -127686,7 +127739,6 @@ var parseChatCommand = (input) => {
|
|
|
127686
127739
|
return query === "" ? { kind: "selectTeam" } : { kind: "help" };
|
|
127687
127740
|
}
|
|
127688
127741
|
if (isThreadCommand(input.text, input.chinese)) return { kind: "selectThread" };
|
|
127689
|
-
if (/^\/[a-z][a-z0-9_-]*/i.test(trimmed)) return { kind: "help" };
|
|
127690
127742
|
if (input.chinese) {
|
|
127691
127743
|
const alias = ZH_ALIASES[trimmed];
|
|
127692
127744
|
if (alias !== void 0) return { kind: alias };
|
|
@@ -127943,13 +127995,58 @@ var makeCommands = (core, deps) => {
|
|
|
127943
127995
|
};
|
|
127944
127996
|
|
|
127945
127997
|
// src/keyedQueue.ts
|
|
127946
|
-
var makeKeyedQueue = (onError) => {
|
|
127947
|
-
const
|
|
127948
|
-
|
|
127949
|
-
const
|
|
127950
|
-
|
|
127998
|
+
var makeKeyedQueue = (onError, options = {}) => {
|
|
127999
|
+
const queues = /* @__PURE__ */ new Map();
|
|
128000
|
+
const run = (key, work, kind = {}) => {
|
|
128001
|
+
const queue = queues.get(key) ?? { tail: Promise.resolve(), size: 0, droppable: 0 };
|
|
128002
|
+
const droppable = kind.droppable === true;
|
|
128003
|
+
if (droppable && options.limit !== void 0 && queue.droppable >= options.limit) {
|
|
128004
|
+
options.onFull?.(key, options.limit);
|
|
128005
|
+
return false;
|
|
128006
|
+
}
|
|
128007
|
+
queue.size += 1;
|
|
128008
|
+
if (droppable) queue.droppable += 1;
|
|
128009
|
+
queue.tail = queue.tail.then(work).catch(onError).then(() => {
|
|
128010
|
+
queue.size -= 1;
|
|
128011
|
+
if (droppable) queue.droppable -= 1;
|
|
128012
|
+
if (queue.size === 0 && queues.get(key) === queue) queues.delete(key);
|
|
127951
128013
|
});
|
|
127952
|
-
|
|
128014
|
+
queues.set(key, queue);
|
|
128015
|
+
return true;
|
|
128016
|
+
};
|
|
128017
|
+
return Object.assign(run, { activeKeys: () => queues.size });
|
|
128018
|
+
};
|
|
128019
|
+
|
|
128020
|
+
// src/redact.ts
|
|
128021
|
+
var DETAIL_MAX_CHARS = 200;
|
|
128022
|
+
var MASK = "***";
|
|
128023
|
+
var TOKEN_LIKE = /[A-Za-z0-9_=-]{24,}/g;
|
|
128024
|
+
var MIN_SECRET_LENGTH = 6;
|
|
128025
|
+
var botRedactor = (columns, values) => {
|
|
128026
|
+
const redactor = makeRedactor();
|
|
128027
|
+
for (const column of columns) {
|
|
128028
|
+
if (column.kind !== "password") continue;
|
|
128029
|
+
const value2 = values[column.key];
|
|
128030
|
+
redactor.register(value2);
|
|
128031
|
+
if (value2 !== void 0) redactor.register(encodeURIComponent(value2));
|
|
128032
|
+
}
|
|
128033
|
+
return redactor;
|
|
128034
|
+
};
|
|
128035
|
+
var makeRedactor = () => {
|
|
128036
|
+
const secrets = /* @__PURE__ */ new Set();
|
|
128037
|
+
const redact = (text4) => {
|
|
128038
|
+
let out = text4;
|
|
128039
|
+
for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {
|
|
128040
|
+
out = out.split(secret).join(MASK);
|
|
128041
|
+
}
|
|
128042
|
+
return out.replace(TOKEN_LIKE, MASK);
|
|
128043
|
+
};
|
|
128044
|
+
return {
|
|
128045
|
+
register: (secret) => {
|
|
128046
|
+
if (typeof secret === "string" && secret.length >= MIN_SECRET_LENGTH) secrets.add(secret);
|
|
128047
|
+
},
|
|
128048
|
+
redact,
|
|
128049
|
+
detail: (text4) => redact(text4).slice(0, DETAIL_MAX_CHARS)
|
|
127953
128050
|
};
|
|
127954
128051
|
};
|
|
127955
128052
|
|
|
@@ -127967,26 +128064,61 @@ var connectionKey = (adapter, bot) => JSON.stringify([
|
|
|
127967
128064
|
var startConnections = (core, inboundHandlersFor) => {
|
|
127968
128065
|
const { host, adapterFor, ready } = core;
|
|
127969
128066
|
const live = /* @__PURE__ */ new Map();
|
|
128067
|
+
const statuses = /* @__PURE__ */ new Map();
|
|
128068
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
128069
|
+
const notify = () => {
|
|
128070
|
+
const snapshot = Object.fromEntries(statuses);
|
|
128071
|
+
for (const listener of [...listeners]) listener(snapshot);
|
|
128072
|
+
};
|
|
128073
|
+
const record3 = (rowId, status) => {
|
|
128074
|
+
const previous = statuses.get(rowId);
|
|
128075
|
+
if (previous?.state === status.state && previous.error === status.error && previous.reason === status.reason && previous.duplicateOf === status.duplicateOf && previous.hint === status.hint)
|
|
128076
|
+
return;
|
|
128077
|
+
statuses.set(rowId, status);
|
|
128078
|
+
notify();
|
|
128079
|
+
};
|
|
128080
|
+
const remove = (rowId) => {
|
|
128081
|
+
if (statuses.delete(rowId)) notify();
|
|
128082
|
+
};
|
|
127970
128083
|
const enqueueForRow = makeKeyedQueue((error) => {
|
|
127971
128084
|
host.log.warn("chat could not start an inbound connection", { error: describeError(error) });
|
|
127972
128085
|
});
|
|
127973
128086
|
let disposed = false;
|
|
127974
128087
|
const open = (bot, key) => {
|
|
127975
128088
|
const adapter = adapterFor(bot.platform);
|
|
128089
|
+
const redactor = botRedactor(adapter?.columns ?? [], bot.values);
|
|
127976
128090
|
let closed = false;
|
|
127977
128091
|
let closeConnection;
|
|
128092
|
+
record3(bot.rowId, { state: "connecting" });
|
|
127978
128093
|
enqueueForRow(bot.rowId, async () => {
|
|
127979
128094
|
try {
|
|
127980
128095
|
await ready;
|
|
127981
128096
|
if (closed || adapter === void 0) return;
|
|
127982
|
-
const connection = await adapter.connect(bot,
|
|
128097
|
+
const connection = await adapter.connect(bot, {
|
|
128098
|
+
...inboundHandlersFor(bot),
|
|
128099
|
+
onConnectionState: (status) => {
|
|
128100
|
+
if (closed) return;
|
|
128101
|
+
if (status.state === "connected") {
|
|
128102
|
+
record3(bot.rowId, status);
|
|
128103
|
+
return;
|
|
128104
|
+
}
|
|
128105
|
+
record3(bot.rowId, {
|
|
128106
|
+
state: "failed",
|
|
128107
|
+
...status.reason === void 0 ? {} : { reason: status.reason },
|
|
128108
|
+
...status.error === void 0 ? {} : { error: redactor.redact(describeError(status.error)) },
|
|
128109
|
+
...status.hint === void 0 ? {} : { hint: status.hint }
|
|
128110
|
+
});
|
|
128111
|
+
}
|
|
128112
|
+
});
|
|
127983
128113
|
if (closed) connection.close();
|
|
127984
128114
|
else closeConnection = () => connection.close();
|
|
127985
128115
|
} catch (error) {
|
|
128116
|
+
const detail = redactor.redact(describeError(error));
|
|
128117
|
+
if (!closed) record3(bot.rowId, { state: "failed", error: detail });
|
|
127986
128118
|
host.log.warn("chat could not start an inbound connection", {
|
|
127987
128119
|
bot: bot.displayName,
|
|
127988
128120
|
platform: bot.platform,
|
|
127989
|
-
error:
|
|
128121
|
+
error: detail
|
|
127990
128122
|
});
|
|
127991
128123
|
}
|
|
127992
128124
|
});
|
|
@@ -128001,19 +128133,25 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128001
128133
|
const reconcile = () => {
|
|
128002
128134
|
const bots = disposed ? [] : core.bots();
|
|
128003
128135
|
const wanted = /* @__PURE__ */ new Map();
|
|
128004
|
-
const credentials3 = /* @__PURE__ */ new
|
|
128136
|
+
const credentials3 = /* @__PURE__ */ new Map();
|
|
128137
|
+
const duplicates = /* @__PURE__ */ new Map();
|
|
128005
128138
|
for (const bot of bots) {
|
|
128006
128139
|
const adapter = adapterFor(bot.platform);
|
|
128007
128140
|
if (adapter === void 0) continue;
|
|
128008
128141
|
const fingerprint = `${bot.platform}:${adapter.columns.map((column) => bot.values[column.key] ?? "").join(" ")}`;
|
|
128009
|
-
|
|
128142
|
+
const duplicateOf = credentials3.get(fingerprint);
|
|
128143
|
+
if (duplicateOf !== void 0) {
|
|
128144
|
+
duplicates.set(bot.rowId, duplicateOf);
|
|
128145
|
+
if (statuses.get(bot.rowId)?.duplicateOf !== duplicateOf) {
|
|
128146
|
+
record3(bot.rowId, { state: "duplicate", duplicateOf });
|
|
128147
|
+
}
|
|
128010
128148
|
host.log.warn("chat did not connect a bot that repeats another bot's credentials", {
|
|
128011
128149
|
bot: bot.displayName,
|
|
128012
128150
|
platform: bot.platform
|
|
128013
128151
|
});
|
|
128014
128152
|
continue;
|
|
128015
128153
|
}
|
|
128016
|
-
credentials3.
|
|
128154
|
+
credentials3.set(fingerprint, bot.rowId);
|
|
128017
128155
|
wanted.set(bot.rowId, { bot, key: connectionKey(adapter, bot) });
|
|
128018
128156
|
}
|
|
128019
128157
|
for (const [rowId, connection] of live) {
|
|
@@ -128021,6 +128159,9 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128021
128159
|
connection.close();
|
|
128022
128160
|
live.delete(rowId);
|
|
128023
128161
|
}
|
|
128162
|
+
for (const rowId of statuses.keys()) {
|
|
128163
|
+
if (!wanted.has(rowId) && !duplicates.has(rowId)) remove(rowId);
|
|
128164
|
+
}
|
|
128024
128165
|
for (const [rowId, { bot, key }] of wanted) {
|
|
128025
128166
|
if (!live.has(rowId)) live.set(rowId, open(bot, key));
|
|
128026
128167
|
}
|
|
@@ -128029,8 +128170,18 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128029
128170
|
host.onDispose(() => {
|
|
128030
128171
|
disposed = true;
|
|
128031
128172
|
reconcile();
|
|
128173
|
+
listeners.clear();
|
|
128032
128174
|
});
|
|
128033
|
-
return {
|
|
128175
|
+
return {
|
|
128176
|
+
reconcile,
|
|
128177
|
+
subscribe: (listener) => {
|
|
128178
|
+
if (!disposed) listeners.add(listener);
|
|
128179
|
+
listener(Object.fromEntries(statuses));
|
|
128180
|
+
return () => {
|
|
128181
|
+
listeners.delete(listener);
|
|
128182
|
+
};
|
|
128183
|
+
}
|
|
128184
|
+
};
|
|
128034
128185
|
};
|
|
128035
128186
|
|
|
128036
128187
|
// src/deliveryHttp.ts
|
|
@@ -128279,6 +128430,7 @@ ${token}` : void 0;
|
|
|
128279
128430
|
};
|
|
128280
128431
|
|
|
128281
128432
|
// src/core.ts
|
|
128433
|
+
var OUTBOUND_QUEUE_LIMIT = 100;
|
|
128282
128434
|
var makeImCore = (input) => {
|
|
128283
128435
|
const { host, t, adapters } = input;
|
|
128284
128436
|
const adapterFor = (platform) => adapters.find((adapter) => adapter.platform === platform);
|
|
@@ -128290,7 +128442,7 @@ var makeImCore = (input) => {
|
|
|
128290
128442
|
init: emptyState,
|
|
128291
128443
|
apply: foldPluginEvent(input.pluginId)
|
|
128292
128444
|
});
|
|
128293
|
-
const
|
|
128445
|
+
const chatReady = stateHandle.get().then(
|
|
128294
128446
|
(restored) => {
|
|
128295
128447
|
state = restored;
|
|
128296
128448
|
},
|
|
@@ -128298,6 +128450,7 @@ var makeImCore = (input) => {
|
|
|
128298
128450
|
host.log.warn("chat could not restore its saved state", { error: describeError(error) });
|
|
128299
128451
|
}
|
|
128300
128452
|
);
|
|
128453
|
+
const ready = Promise.all([chatReady, input.inboundReady]).then(() => void 0);
|
|
128301
128454
|
let persisting = Promise.resolve();
|
|
128302
128455
|
const commit = (name, data) => {
|
|
128303
128456
|
state = foldImEvent(state, name, data);
|
|
@@ -128313,10 +128466,21 @@ var makeImCore = (input) => {
|
|
|
128313
128466
|
const bindChat = (chatKey, threadId) => {
|
|
128314
128467
|
commit("chat-updated", { chatKey, threadId });
|
|
128315
128468
|
};
|
|
128316
|
-
const outbound = makeKeyedQueue(
|
|
128317
|
-
|
|
128318
|
-
|
|
128319
|
-
|
|
128469
|
+
const outbound = makeKeyedQueue(
|
|
128470
|
+
(error) => {
|
|
128471
|
+
host.log.warn("chat could not deliver queued work", { error: describeError(error) });
|
|
128472
|
+
},
|
|
128473
|
+
{
|
|
128474
|
+
limit: OUTBOUND_QUEUE_LIMIT,
|
|
128475
|
+
onFull: (chatKey, limit) => {
|
|
128476
|
+
host.log.warn("chat queue is full; refused a conclusion until restart", {
|
|
128477
|
+
chatKey,
|
|
128478
|
+
limit
|
|
128479
|
+
});
|
|
128480
|
+
}
|
|
128481
|
+
}
|
|
128482
|
+
);
|
|
128483
|
+
const postImages = async (binding, attachments, onFailure) => {
|
|
128320
128484
|
const adapter = adapterFor(binding.platform);
|
|
128321
128485
|
if (adapter?.sendImage === void 0) return;
|
|
128322
128486
|
for (const attachment of attachments) {
|
|
@@ -128333,19 +128497,22 @@ var makeImCore = (input) => {
|
|
|
128333
128497
|
name: attachment.name,
|
|
128334
128498
|
error: describeError(error)
|
|
128335
128499
|
});
|
|
128500
|
+
await onFailure?.(error);
|
|
128336
128501
|
}
|
|
128337
128502
|
}
|
|
128338
128503
|
};
|
|
128339
|
-
const deliver = async (binding, text4, attachments = []) => {
|
|
128340
|
-
|
|
128504
|
+
const deliver = async (binding, text4, attachments = [], options = {}) => {
|
|
128505
|
+
const { retry, onFailure } = options;
|
|
128506
|
+
await postImages(binding, attachments, onFailure);
|
|
128341
128507
|
if (text4.trim() === "") return true;
|
|
128342
128508
|
const adapter = adapterFor(binding.platform);
|
|
128343
128509
|
if (adapter === void 0) return false;
|
|
128344
128510
|
try {
|
|
128345
|
-
await adapter.sendText(binding, text4);
|
|
128511
|
+
await adapter.sendText(binding, text4, { retry });
|
|
128346
128512
|
return true;
|
|
128347
128513
|
} catch (error) {
|
|
128348
128514
|
host.log.warn("chat could not deliver", { error: describeError(error) });
|
|
128515
|
+
await onFailure?.(error);
|
|
128349
128516
|
return false;
|
|
128350
128517
|
}
|
|
128351
128518
|
};
|
|
@@ -128377,6 +128544,92 @@ var makeImCore = (input) => {
|
|
|
128377
128544
|
// src/conclusionDelivery.ts
|
|
128378
128545
|
import { threadOrNull as threadOrNull3 } from "@ycodium-ai/plugin-api";
|
|
128379
128546
|
|
|
128547
|
+
// src/abortableSleep.ts
|
|
128548
|
+
var abortableSleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
128549
|
+
if (signal?.aborted === true) {
|
|
128550
|
+
reject(new Error("aborted"));
|
|
128551
|
+
return;
|
|
128552
|
+
}
|
|
128553
|
+
const onAbort = () => {
|
|
128554
|
+
clearTimeout(timer);
|
|
128555
|
+
reject(new Error("aborted"));
|
|
128556
|
+
};
|
|
128557
|
+
const timer = setTimeout(() => {
|
|
128558
|
+
signal?.removeEventListener("abort", onAbort);
|
|
128559
|
+
resolve();
|
|
128560
|
+
}, ms);
|
|
128561
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
128562
|
+
});
|
|
128563
|
+
|
|
128564
|
+
// src/deliveryFailure.ts
|
|
128565
|
+
import * as NodeCrypto2 from "node:crypto";
|
|
128566
|
+
|
|
128567
|
+
// src/diagnostics.ts
|
|
128568
|
+
var PREFIX = {
|
|
128569
|
+
conn: "IM-CONN",
|
|
128570
|
+
msg: "IM-MSG",
|
|
128571
|
+
setup: "IM-SETUP"
|
|
128572
|
+
};
|
|
128573
|
+
var shortSalt = () => {
|
|
128574
|
+
const bytes = new Uint8Array(2);
|
|
128575
|
+
crypto.getRandomValues(bytes);
|
|
128576
|
+
return Array.from(bytes, (b) => b.toString(16).toUpperCase().padStart(2, "0")).join("");
|
|
128577
|
+
};
|
|
128578
|
+
var stableDiagnosticRef = (kind, digest) => `${PREFIX[kind]}-${digest.slice(0, 16).toUpperCase()}`;
|
|
128579
|
+
var diagnosticRef = (kind, nowMs = Date.now()) => {
|
|
128580
|
+
const stamp = nowMs.toString(36).toUpperCase();
|
|
128581
|
+
return `${PREFIX[kind]}-${stamp}-${shortSalt()}`;
|
|
128582
|
+
};
|
|
128583
|
+
var formatDiagnosticCopy = (report) => [
|
|
128584
|
+
`ok=${report.ok ? "yes" : "no"}`,
|
|
128585
|
+
`platform=${report.platform}`,
|
|
128586
|
+
`bot=${report.botName}`,
|
|
128587
|
+
`row=${report.rowId}`,
|
|
128588
|
+
`stage=${report.stage}`,
|
|
128589
|
+
`at=${report.at}`,
|
|
128590
|
+
`detail=${report.detail}`
|
|
128591
|
+
].join("\n");
|
|
128592
|
+
|
|
128593
|
+
// src/deliveryFailure.ts
|
|
128594
|
+
var deliveryFailure = (core, binding, threadId, turnId) => async (error) => {
|
|
128595
|
+
const rejection = core.adapterFor(binding.platform)?.describeDeliveryRejection?.(error, binding);
|
|
128596
|
+
if (rejection === void 0) return;
|
|
128597
|
+
const { recipients } = rejection;
|
|
128598
|
+
const digest = NodeCrypto2.createHash("sha256").update(JSON.stringify([threadId, turnId, binding.platform, binding.rowId, recipients])).digest("hex");
|
|
128599
|
+
const id = `im.delivery-failed:${digest}`;
|
|
128600
|
+
const reference2 = stableDiagnosticRef("msg", digest);
|
|
128601
|
+
const payload = {
|
|
128602
|
+
platform: binding.platform,
|
|
128603
|
+
recipients,
|
|
128604
|
+
hint: rejection.hint,
|
|
128605
|
+
providerCode: rejection.providerCode,
|
|
128606
|
+
reference: reference2,
|
|
128607
|
+
sourceTurnId: turnId
|
|
128608
|
+
};
|
|
128609
|
+
core.host.log.warn("chat recipient rejected delivery", { threadId, ...payload });
|
|
128610
|
+
if (core.state.opened[threadId] === void 0) return;
|
|
128611
|
+
try {
|
|
128612
|
+
await core.host.threads.externalMessages.append({
|
|
128613
|
+
requestId: id,
|
|
128614
|
+
threadId,
|
|
128615
|
+
messages: [],
|
|
128616
|
+
activities: [
|
|
128617
|
+
{
|
|
128618
|
+
id,
|
|
128619
|
+
tone: "error",
|
|
128620
|
+
kind: "im.delivery-failed",
|
|
128621
|
+
summary: rejection.summary,
|
|
128622
|
+
payload,
|
|
128623
|
+
turnId: null,
|
|
128624
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
128625
|
+
}
|
|
128626
|
+
]
|
|
128627
|
+
});
|
|
128628
|
+
} catch {
|
|
128629
|
+
core.host.log.warn("chat could not append delivery failure notice", { threadId, reference: reference2 });
|
|
128630
|
+
}
|
|
128631
|
+
};
|
|
128632
|
+
|
|
128380
128633
|
// src/threadMessages.ts
|
|
128381
128634
|
import {
|
|
128382
128635
|
isThreadNotFound,
|
|
@@ -128428,20 +128681,30 @@ async function messagesAfterLastUser(threads, threadId) {
|
|
|
128428
128681
|
}
|
|
128429
128682
|
|
|
128430
128683
|
// src/conclusionDelivery.ts
|
|
128684
|
+
var ALWAYS_OWED = () => true;
|
|
128431
128685
|
var makeConclusionDelivery = (core, anchors) => {
|
|
128432
128686
|
let disposed = false;
|
|
128687
|
+
const stopped = new AbortController();
|
|
128433
128688
|
core.host.onDispose(() => {
|
|
128434
128689
|
disposed = true;
|
|
128690
|
+
stopped.abort();
|
|
128435
128691
|
});
|
|
128436
128692
|
const marked = (text4, recovered) => recovered ? `${core.t("reply.conclusionRecovered")}
|
|
128437
128693
|
${text4}` : text4;
|
|
128694
|
+
const sleep3 = (ms) => abortableSleep(ms, stopped.signal).catch(() => void 0);
|
|
128695
|
+
const retryFor = (threadId, owed = ALWAYS_OWED) => ({
|
|
128696
|
+
sleep: sleep3,
|
|
128697
|
+
shouldContinue: async () => !disposed && owed() && await threadOrNull3(core.host.threads, threadId) !== null
|
|
128698
|
+
});
|
|
128438
128699
|
return {
|
|
128700
|
+
retryFor,
|
|
128439
128701
|
prepareConclusion: async (threadId, turnId = null) => {
|
|
128440
128702
|
const thread = await threadOrNull3(core.host.threads, threadId);
|
|
128441
128703
|
const final = thread === null ? void 0 : await latestMessage(
|
|
128442
128704
|
core.host.threads,
|
|
128443
128705
|
threadId,
|
|
128444
|
-
|
|
128706
|
+
// 给了轮次就只取这一轮的正文:排队等着的结论不能拿到下一轮的话。没记轮次的老消息照旧认。
|
|
128707
|
+
(message) => message.role === "assistant" && message.text.trim() !== "" && (turnId === null || message.turnId === null || message.turnId === turnId)
|
|
128445
128708
|
);
|
|
128446
128709
|
const attachments = final === void 0 ? [] : await attachmentsOf(core.host.threads, threadId, final);
|
|
128447
128710
|
return async (binding, options) => {
|
|
@@ -128451,12 +128714,22 @@ ${text4}` : text4;
|
|
|
128451
128714
|
|
|
128452
128715
|
${final.text}`, options.recovered);
|
|
128453
128716
|
const anchored = await anchors.anchored(binding, threadId, turnId ?? final.turnId);
|
|
128454
|
-
return !disposed && core.deliver(anchored, text4, attachments
|
|
128717
|
+
return !disposed && core.deliver(anchored, text4, attachments, {
|
|
128718
|
+
retry: retryFor(threadId, options.owed),
|
|
128719
|
+
onFailure: deliveryFailure(core, binding, threadId, turnId ?? final.turnId)
|
|
128720
|
+
});
|
|
128455
128721
|
};
|
|
128456
128722
|
},
|
|
128457
|
-
|
|
128723
|
+
/**
|
|
128724
|
+
* 发一段已经写好的结论文字(失败通知、卡片定稿失败时的文字兜底):锚点、停用检查、重发与
|
|
128725
|
+
* 拒收提醒都在这里,调用方只管写文字。
|
|
128726
|
+
*/
|
|
128727
|
+
sendText: async (binding, threadId, notice, recovered, turnId = null, owed = ALWAYS_OWED) => {
|
|
128458
128728
|
const anchored = await anchors.anchored(binding, threadId, turnId);
|
|
128459
|
-
return !disposed && core.deliver(anchored, marked(notice, recovered)
|
|
128729
|
+
return !disposed && core.deliver(anchored, marked(notice, recovered), [], {
|
|
128730
|
+
retry: retryFor(threadId, owed),
|
|
128731
|
+
onFailure: deliveryFailure(core, binding, threadId, turnId)
|
|
128732
|
+
});
|
|
128460
128733
|
}
|
|
128461
128734
|
};
|
|
128462
128735
|
};
|
|
@@ -128709,63 +128982,15 @@ var makeInboundAttachments = (core) => {
|
|
|
128709
128982
|
|
|
128710
128983
|
// src/inbound.ts
|
|
128711
128984
|
import { projectOrNull } from "@ycodium-ai/plugin-api";
|
|
128712
|
-
|
|
128713
|
-
// src/boundedSeen.ts
|
|
128714
|
-
var makeBoundedSeen = (limit) => {
|
|
128715
|
-
const seen = /* @__PURE__ */ new Set();
|
|
128716
|
-
return {
|
|
128717
|
-
has: (key) => seen.has(key),
|
|
128718
|
-
/** 记下这个键:第一次见返回 true;已经记着返回 false(不重排,最早记的先被忘)。 */
|
|
128719
|
-
claim: (key) => {
|
|
128720
|
-
if (seen.has(key)) return false;
|
|
128721
|
-
seen.add(key);
|
|
128722
|
-
if (seen.size > limit) {
|
|
128723
|
-
const oldest = seen.values().next().value;
|
|
128724
|
-
if (oldest !== void 0) seen.delete(oldest);
|
|
128725
|
-
}
|
|
128726
|
-
return true;
|
|
128727
|
-
}
|
|
128728
|
-
};
|
|
128729
|
-
};
|
|
128730
|
-
|
|
128731
|
-
// src/cooldown.ts
|
|
128732
|
-
var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
|
|
128733
|
-
const lastAt = /* @__PURE__ */ new Map();
|
|
128734
|
-
return {
|
|
128735
|
-
tryAcquire: (key) => {
|
|
128736
|
-
const current = now();
|
|
128737
|
-
const previous = lastAt.get(key);
|
|
128738
|
-
if (previous !== void 0 && current - previous < windowMs) return false;
|
|
128739
|
-
lastAt.delete(key);
|
|
128740
|
-
lastAt.set(key, current);
|
|
128741
|
-
if (lastAt.size > maxKeys) {
|
|
128742
|
-
for (const [candidate, at] of lastAt) {
|
|
128743
|
-
if (current - at >= windowMs) lastAt.delete(candidate);
|
|
128744
|
-
}
|
|
128745
|
-
while (lastAt.size > maxKeys) {
|
|
128746
|
-
const oldest = lastAt.keys().next().value;
|
|
128747
|
-
if (oldest === void 0) break;
|
|
128748
|
-
lastAt.delete(oldest);
|
|
128749
|
-
}
|
|
128750
|
-
}
|
|
128751
|
-
return true;
|
|
128752
|
-
}
|
|
128753
|
-
};
|
|
128754
|
-
};
|
|
128755
|
-
|
|
128756
|
-
// src/inbound.ts
|
|
128757
|
-
var SEEN_MESSAGE_LIMIT = 256;
|
|
128758
128985
|
var ARCHIVED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
128759
128986
|
var UNSUPPORTED_REPLY_COOLDOWN_MS = 6e4;
|
|
128760
128987
|
var NOT_ALLOWED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
128761
128988
|
var NOT_ALLOWED_REPLY_KEY_LIMIT = 1e3;
|
|
128762
|
-
var PROJECT_PROMPT_COOLDOWN_MS = 3e4;
|
|
128763
128989
|
var makeInbound = (core, deps) => {
|
|
128764
128990
|
const { host, t, adapterFor, commit, deliver, bindingOf: bindingOf2, bots } = core;
|
|
128765
128991
|
const { enqueueForChat } = deps;
|
|
128766
128992
|
const { handleCommand, targetOf } = deps.commands;
|
|
128767
|
-
const {
|
|
128768
|
-
const { listedProjectsByChat } = deps.rows;
|
|
128993
|
+
const { prepareMessage } = deps.chatSetup;
|
|
128769
128994
|
const { questionFlow, handleButtonClick } = deps.interactions;
|
|
128770
128995
|
const { answerByToken } = deps.approvals;
|
|
128771
128996
|
const { handlePanelClick } = deps.panelWiring;
|
|
@@ -128779,10 +129004,9 @@ var makeInbound = (core, deps) => {
|
|
|
128779
129004
|
() => Date.now(),
|
|
128780
129005
|
NOT_ALLOWED_REPLY_KEY_LIMIT
|
|
128781
129006
|
);
|
|
128782
|
-
const
|
|
128783
|
-
const
|
|
128784
|
-
const
|
|
128785
|
-
const claimCallback = (rowId, callbackId) => callbackId === void 0 || callbackId === "" || !alreadyHandled(`c:${rowId}:${callbackId}`);
|
|
129007
|
+
const { inboundState } = deps;
|
|
129008
|
+
const alreadyHandled = (rowId, key) => !inboundState.claim(rowId, key);
|
|
129009
|
+
const claimCallback = (rowId, callbackId) => callbackId === void 0 || callbackId === "" || !alreadyHandled(rowId, `c:${callbackId}`);
|
|
128786
129010
|
const turnAwayStranger = (target2, sender) => {
|
|
128787
129011
|
host.log.warn("chat ignored a message from someone not on the allow list", {
|
|
128788
129012
|
bot: target2.displayName,
|
|
@@ -128822,6 +129046,61 @@ var makeInbound = (core, deps) => {
|
|
|
128822
129046
|
const threadId = await startTurn(speaker, message);
|
|
128823
129047
|
if (threadId !== null) await markWorking(speaker, threadId, message.messageId);
|
|
128824
129048
|
};
|
|
129049
|
+
const resumeMessage = async (current, message) => {
|
|
129050
|
+
inheritFromParent(current, message.chatId, message.parentChatId);
|
|
129051
|
+
let target2 = {
|
|
129052
|
+
...bindingOf2(current, message.chatId),
|
|
129053
|
+
reply: anchorOfMessage(adapterFor(current.platform), message)
|
|
129054
|
+
};
|
|
129055
|
+
const chatKey = chatKeyOf(target2);
|
|
129056
|
+
if (await handleCommand(target2, message.text, message.senderId)) return;
|
|
129057
|
+
if (target2.projectId === "") {
|
|
129058
|
+
if (!await prepareMessage(target2, message)) return;
|
|
129059
|
+
target2 = { ...bindingOf2(current, message.chatId), reply: target2.reply };
|
|
129060
|
+
}
|
|
129061
|
+
const boundThread = chatOf(core.state, chatKey).threadId;
|
|
129062
|
+
if (boundThread !== null && message.text !== "" && await questionFlow.answerByText(chatKey, boundThread, message.text)) {
|
|
129063
|
+
return;
|
|
129064
|
+
}
|
|
129065
|
+
const project = await projectOrNull(host.projects, target2.projectId);
|
|
129066
|
+
if (project === null || project.archivedAt != null) {
|
|
129067
|
+
if (project === null) {
|
|
129068
|
+
host.log.warn("chat cannot start work: the chat's project is gone", {
|
|
129069
|
+
bot: target2.displayName,
|
|
129070
|
+
projectId: target2.projectId
|
|
129071
|
+
});
|
|
129072
|
+
}
|
|
129073
|
+
if (archivedReplyCooldown.tryAcquire(chatKey)) {
|
|
129074
|
+
await deliver(
|
|
129075
|
+
target2,
|
|
129076
|
+
project === null ? t("reply.projectMissing") : t("reply.projectArchived", { title: project.title })
|
|
129077
|
+
);
|
|
129078
|
+
}
|
|
129079
|
+
return;
|
|
129080
|
+
}
|
|
129081
|
+
const routed = targetOf(target2);
|
|
129082
|
+
const attachmentsOnly = message.text.trim() === "" && message.resources.length > 0;
|
|
129083
|
+
if (routed.kind === "team") {
|
|
129084
|
+
if (message.resources.length > 0) await deliver(target2, t("reply.attachmentsNoTeam"));
|
|
129085
|
+
if (attachmentsOnly) return;
|
|
129086
|
+
const leadThreadId = await startTeamGoal(target2, message);
|
|
129087
|
+
if (leadThreadId !== null) await markWorking(target2, leadThreadId, message.messageId);
|
|
129088
|
+
return;
|
|
129089
|
+
}
|
|
129090
|
+
if (attachmentsOnly) {
|
|
129091
|
+
await receiveAttachmentsOnly(target2, message);
|
|
129092
|
+
return;
|
|
129093
|
+
}
|
|
129094
|
+
const branchChatId = hasWaitingAttachments(chatKey) ? void 0 : message.branchChatId;
|
|
129095
|
+
if (branchChatId === void 0) {
|
|
129096
|
+
await openTurn(target2, message);
|
|
129097
|
+
return;
|
|
129098
|
+
}
|
|
129099
|
+
enqueueForChat(chatKeyOf({ rowId: current.rowId, chatId: branchChatId }), async () => {
|
|
129100
|
+
inheritFromParent(current, branchChatId, message.chatId);
|
|
129101
|
+
await openTurn({ ...bindingOf2(current, branchChatId), reply: target2.reply }, message);
|
|
129102
|
+
});
|
|
129103
|
+
};
|
|
128825
129104
|
const inboundHandlersFor = (bot) => ({
|
|
128826
129105
|
onError: (message) => host.log.warn("chat inbound connection failed", {
|
|
128827
129106
|
platform: bot.platform,
|
|
@@ -128844,7 +129123,7 @@ var makeInbound = (core, deps) => {
|
|
|
128844
129123
|
if (current === void 0) return;
|
|
128845
129124
|
const message = settleChat(current, received);
|
|
128846
129125
|
const arrived = bindingOf2(current, message.chatId);
|
|
128847
|
-
if (alreadyHandled(`m:${
|
|
129126
|
+
if (alreadyHandled(current.rowId, `m:${message.chatId}:${message.messageId}`)) return;
|
|
128848
129127
|
if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
|
|
128849
129128
|
commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
|
|
128850
129129
|
}
|
|
@@ -128871,69 +129150,129 @@ var makeInbound = (core, deps) => {
|
|
|
128871
129150
|
});
|
|
128872
129151
|
return;
|
|
128873
129152
|
}
|
|
128874
|
-
enqueueForChat(chatKeyOf(arrived),
|
|
128875
|
-
|
|
128876
|
-
|
|
128877
|
-
|
|
128878
|
-
|
|
128879
|
-
|
|
128880
|
-
|
|
128881
|
-
|
|
128882
|
-
|
|
128883
|
-
|
|
128884
|
-
|
|
128885
|
-
|
|
128886
|
-
|
|
128887
|
-
|
|
128888
|
-
|
|
128889
|
-
|
|
128890
|
-
|
|
128891
|
-
|
|
128892
|
-
|
|
128893
|
-
|
|
129153
|
+
enqueueForChat(chatKeyOf(arrived), () => resumeMessage(current, message));
|
|
129154
|
+
}
|
|
129155
|
+
});
|
|
129156
|
+
return { inboundHandlersFor, resumeMessage };
|
|
129157
|
+
};
|
|
129158
|
+
|
|
129159
|
+
// src/inboundStore.ts
|
|
129160
|
+
import * as NodeFSP from "node:fs/promises";
|
|
129161
|
+
import * as NodePath from "node:path";
|
|
129162
|
+
var SEEN_LIMIT = 1e3;
|
|
129163
|
+
var fileIo = {
|
|
129164
|
+
readFile: (path) => NodeFSP.readFile(path, "utf8"),
|
|
129165
|
+
mkdir: async (path) => {
|
|
129166
|
+
await NodeFSP.mkdir(path, { recursive: true });
|
|
129167
|
+
},
|
|
129168
|
+
writeFile: (path, contents) => NodeFSP.writeFile(path, contents, "utf8"),
|
|
129169
|
+
rename: (from, to) => NodeFSP.rename(from, to)
|
|
129170
|
+
};
|
|
129171
|
+
var isRecord2 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
129172
|
+
var decode = (contents) => {
|
|
129173
|
+
const value2 = JSON.parse(contents);
|
|
129174
|
+
if (!isRecord2(value2) || value2["version"] !== 1 || !isRecord2(value2["bots"]))
|
|
129175
|
+
throw new Error("invalid inbound store");
|
|
129176
|
+
const bots = /* @__PURE__ */ new Map();
|
|
129177
|
+
for (const [rowId, row] of Object.entries(value2["bots"])) {
|
|
129178
|
+
if (!isRecord2(row) || !Array.isArray(row["seenIds"]) || !row["seenIds"].every((id) => typeof id === "string"))
|
|
129179
|
+
throw new Error("invalid inbound bot row");
|
|
129180
|
+
const telegram = row["telegram"];
|
|
129181
|
+
if (telegram !== void 0 && (!isRecord2(telegram) || typeof telegram["tokenHash"] !== "string" || telegram["tokenHash"] === "" || typeof telegram["offset"] !== "number" || !Number.isSafeInteger(telegram["offset"]) || telegram["offset"] < 0))
|
|
129182
|
+
throw new Error("invalid telegram cursor");
|
|
129183
|
+
bots.set(rowId, {
|
|
129184
|
+
seenIds: row["seenIds"].slice(-SEEN_LIMIT),
|
|
129185
|
+
...telegram === void 0 ? {} : {
|
|
129186
|
+
telegram: {
|
|
129187
|
+
tokenHash: telegram["tokenHash"],
|
|
129188
|
+
offset: telegram["offset"]
|
|
128894
129189
|
}
|
|
128895
|
-
|
|
128896
|
-
|
|
128897
|
-
|
|
128898
|
-
|
|
128899
|
-
|
|
128900
|
-
|
|
128901
|
-
|
|
128902
|
-
|
|
128903
|
-
|
|
128904
|
-
|
|
128905
|
-
|
|
128906
|
-
|
|
128907
|
-
|
|
129190
|
+
}
|
|
129191
|
+
});
|
|
129192
|
+
}
|
|
129193
|
+
return bots;
|
|
129194
|
+
};
|
|
129195
|
+
var makeInboundStore = (input) => {
|
|
129196
|
+
const io = input.io ?? fileIo;
|
|
129197
|
+
const path = NodePath.join(input.dataDir, "inbound.json");
|
|
129198
|
+
const temporary = `${path}.tmp`;
|
|
129199
|
+
let bots = /* @__PURE__ */ new Map();
|
|
129200
|
+
let dirty = false;
|
|
129201
|
+
let writing;
|
|
129202
|
+
const warn = (message, error) => input.log.warn(message, { error: describeError(error) });
|
|
129203
|
+
const ready = io.readFile(path).then(
|
|
129204
|
+
(contents) => {
|
|
129205
|
+
try {
|
|
129206
|
+
bots = decode(contents);
|
|
129207
|
+
} catch (error) {
|
|
129208
|
+
warn("chat could not restore inbound state", error);
|
|
129209
|
+
}
|
|
129210
|
+
},
|
|
129211
|
+
(error) => {
|
|
129212
|
+
if (!isRecord2(error) || error["code"] !== "ENOENT")
|
|
129213
|
+
warn("chat could not restore inbound state", error);
|
|
129214
|
+
}
|
|
129215
|
+
);
|
|
129216
|
+
const write = () => {
|
|
129217
|
+
writing ??= Promise.resolve().then(async () => {
|
|
129218
|
+
try {
|
|
129219
|
+
while (dirty) {
|
|
129220
|
+
dirty = false;
|
|
129221
|
+
const contents = JSON.stringify({ version: 1, bots: Object.fromEntries(bots) });
|
|
129222
|
+
try {
|
|
129223
|
+
await io.mkdir(input.dataDir);
|
|
129224
|
+
await io.writeFile(temporary, contents);
|
|
129225
|
+
await io.rename(temporary, path);
|
|
129226
|
+
} catch (error) {
|
|
129227
|
+
dirty = true;
|
|
129228
|
+
warn("chat could not save inbound state", error);
|
|
129229
|
+
break;
|
|
128908
129230
|
}
|
|
128909
|
-
return;
|
|
128910
|
-
}
|
|
128911
|
-
const routed = targetOf(target2);
|
|
128912
|
-
const attachmentsOnly = message.text.trim() === "" && message.resources.length > 0;
|
|
128913
|
-
if (routed.kind === "team") {
|
|
128914
|
-
if (message.resources.length > 0) await deliver(target2, t("reply.attachmentsNoTeam"));
|
|
128915
|
-
if (attachmentsOnly) return;
|
|
128916
|
-
const leadThreadId = await startTeamGoal(target2, message);
|
|
128917
|
-
if (leadThreadId !== null) await markWorking(target2, leadThreadId, message.messageId);
|
|
128918
|
-
return;
|
|
128919
|
-
}
|
|
128920
|
-
if (attachmentsOnly) {
|
|
128921
|
-
await receiveAttachmentsOnly(target2, message);
|
|
128922
|
-
return;
|
|
128923
129231
|
}
|
|
128924
|
-
|
|
128925
|
-
|
|
128926
|
-
|
|
128927
|
-
|
|
128928
|
-
|
|
128929
|
-
|
|
128930
|
-
|
|
128931
|
-
|
|
128932
|
-
|
|
128933
|
-
|
|
129232
|
+
} finally {
|
|
129233
|
+
writing = void 0;
|
|
129234
|
+
}
|
|
129235
|
+
});
|
|
129236
|
+
return writing;
|
|
129237
|
+
};
|
|
129238
|
+
const changed = () => {
|
|
129239
|
+
dirty = true;
|
|
129240
|
+
void write();
|
|
129241
|
+
};
|
|
129242
|
+
const rowOf = (rowId) => {
|
|
129243
|
+
let row = bots.get(rowId);
|
|
129244
|
+
if (row === void 0) {
|
|
129245
|
+
row = { seenIds: [] };
|
|
129246
|
+
bots.set(rowId, row);
|
|
128934
129247
|
}
|
|
128935
|
-
|
|
128936
|
-
|
|
129248
|
+
return row;
|
|
129249
|
+
};
|
|
129250
|
+
return {
|
|
129251
|
+
ready,
|
|
129252
|
+
claim: (rowId, id) => {
|
|
129253
|
+
const row = rowOf(rowId);
|
|
129254
|
+
if (row.seenIds.includes(id)) return false;
|
|
129255
|
+
row.seenIds.push(id);
|
|
129256
|
+
if (row.seenIds.length > SEEN_LIMIT) row.seenIds.shift();
|
|
129257
|
+
changed();
|
|
129258
|
+
return true;
|
|
129259
|
+
},
|
|
129260
|
+
readCursor: (rowId, tokenHash) => {
|
|
129261
|
+
const cursor = bots.get(rowId)?.telegram;
|
|
129262
|
+
return cursor?.tokenHash === tokenHash ? cursor.offset : void 0;
|
|
129263
|
+
},
|
|
129264
|
+
saveCursor: (rowId, tokenHash, offset) => {
|
|
129265
|
+
const row = rowOf(rowId);
|
|
129266
|
+
if (row.telegram?.tokenHash === tokenHash && row.telegram.offset === offset) return;
|
|
129267
|
+
if (row.telegram?.tokenHash !== tokenHash) row.seenIds = [];
|
|
129268
|
+
row.telegram = { tokenHash, offset };
|
|
129269
|
+
changed();
|
|
129270
|
+
},
|
|
129271
|
+
flush: async () => {
|
|
129272
|
+
await ready;
|
|
129273
|
+
if (dirty || writing) await write();
|
|
129274
|
+
}
|
|
129275
|
+
};
|
|
128937
129276
|
};
|
|
128938
129277
|
|
|
128939
129278
|
// src/interactions.ts
|
|
@@ -130583,29 +130922,6 @@ var sleep = async (deps) => {
|
|
|
130583
130922
|
}
|
|
130584
130923
|
};
|
|
130585
130924
|
|
|
130586
|
-
// src/redact.ts
|
|
130587
|
-
var DETAIL_MAX_CHARS = 200;
|
|
130588
|
-
var MASK = "***";
|
|
130589
|
-
var TOKEN_LIKE = /[A-Za-z0-9_=-]{24,}/g;
|
|
130590
|
-
var MIN_SECRET_LENGTH = 6;
|
|
130591
|
-
var makeRedactor = () => {
|
|
130592
|
-
const secrets = /* @__PURE__ */ new Set();
|
|
130593
|
-
const redact = (text4) => {
|
|
130594
|
-
let out = text4;
|
|
130595
|
-
for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {
|
|
130596
|
-
out = out.split(secret).join(MASK);
|
|
130597
|
-
}
|
|
130598
|
-
return out.replace(TOKEN_LIKE, MASK);
|
|
130599
|
-
};
|
|
130600
|
-
return {
|
|
130601
|
-
register: (secret) => {
|
|
130602
|
-
if (typeof secret === "string" && secret.length >= MIN_SECRET_LENGTH) secrets.add(secret);
|
|
130603
|
-
},
|
|
130604
|
-
redact,
|
|
130605
|
-
detail: (text4) => redact(text4).slice(0, DETAIL_MAX_CHARS)
|
|
130606
|
-
};
|
|
130607
|
-
};
|
|
130608
|
-
|
|
130609
130925
|
// src/lark/setup/cookieJar.ts
|
|
130610
130926
|
var MAX_REDIRECT_HOPS = 10;
|
|
130611
130927
|
var parseSetCookie = (responseUrl, header) => {
|
|
@@ -131323,21 +131639,7 @@ var buildChecklist = () => ({
|
|
|
131323
131639
|
});
|
|
131324
131640
|
var makeRealClock = () => ({
|
|
131325
131641
|
now: () => Date.now(),
|
|
131326
|
-
sleep:
|
|
131327
|
-
if (signal?.aborted === true) {
|
|
131328
|
-
reject(new Error("aborted"));
|
|
131329
|
-
return;
|
|
131330
|
-
}
|
|
131331
|
-
const onAbort = () => {
|
|
131332
|
-
clearTimeout(timer);
|
|
131333
|
-
reject(new Error("aborted"));
|
|
131334
|
-
};
|
|
131335
|
-
const timer = setTimeout(() => {
|
|
131336
|
-
signal?.removeEventListener("abort", onAbort);
|
|
131337
|
-
resolve();
|
|
131338
|
-
}, ms);
|
|
131339
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
131340
|
-
})
|
|
131642
|
+
sleep: abortableSleep
|
|
131341
131643
|
});
|
|
131342
131644
|
var saveAndConclude = async (ctx, updateSettings, result) => {
|
|
131343
131645
|
try {
|
|
@@ -131429,6 +131731,9 @@ var registerLarkSetup = (ctx, options = {}) => {
|
|
|
131429
131731
|
return runner;
|
|
131430
131732
|
};
|
|
131431
131733
|
|
|
131734
|
+
// src/larkApi.ts
|
|
131735
|
+
import { parseRetryAfterMs } from "@ycodium-ai/plugin-api";
|
|
131736
|
+
|
|
131432
131737
|
// src/larkPost.ts
|
|
131433
131738
|
var styledText = (text4, style) => {
|
|
131434
131739
|
let out = text4;
|
|
@@ -131608,6 +131913,33 @@ var parseLarkReply = (message) => {
|
|
|
131608
131913
|
}
|
|
131609
131914
|
};
|
|
131610
131915
|
|
|
131916
|
+
// src/sendRetry.ts
|
|
131917
|
+
var NO_RETRY = { retry: false };
|
|
131918
|
+
var SEND_RETRY_DELAYS_MS = [1e3, 4e3, 15e3];
|
|
131919
|
+
var MAX_WAIT_MS = 6e4;
|
|
131920
|
+
var SendStoppedError = class extends Error {
|
|
131921
|
+
constructor(cause) {
|
|
131922
|
+
super("send stopped before retrying", { cause });
|
|
131923
|
+
this.name = "SendStoppedError";
|
|
131924
|
+
}
|
|
131925
|
+
};
|
|
131926
|
+
var sendWithRetry = async (request, verdictOf, retry) => {
|
|
131927
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
131928
|
+
try {
|
|
131929
|
+
return await request();
|
|
131930
|
+
} catch (error) {
|
|
131931
|
+
const delayMs = SEND_RETRY_DELAYS_MS[attempt];
|
|
131932
|
+
if (retry === void 0 || delayMs === void 0) throw error;
|
|
131933
|
+
const verdict = verdictOf(error);
|
|
131934
|
+
if (!verdict.retry) throw error;
|
|
131935
|
+
const waitMs = verdict.afterMs ?? delayMs;
|
|
131936
|
+
if (waitMs > MAX_WAIT_MS) throw error;
|
|
131937
|
+
await retry.sleep(waitMs);
|
|
131938
|
+
if (!await retry.shouldContinue()) throw new SendStoppedError(error);
|
|
131939
|
+
}
|
|
131940
|
+
}
|
|
131941
|
+
};
|
|
131942
|
+
|
|
131611
131943
|
// src/splitMessage.ts
|
|
131612
131944
|
var splitMessage = (text4, limit) => {
|
|
131613
131945
|
if (text4.length <= limit) return [text4];
|
|
@@ -131640,19 +131972,39 @@ var LARK_TEXT_MAX_CHARS = 3500;
|
|
|
131640
131972
|
var LarkApiError = class extends Error {
|
|
131641
131973
|
operation;
|
|
131642
131974
|
code;
|
|
131643
|
-
|
|
131975
|
+
status;
|
|
131976
|
+
retryAfterMs;
|
|
131977
|
+
constructor(operation, code2, message, http = {}) {
|
|
131644
131978
|
super(`lark ${operation} failed (code ${String(code2 ?? "?")}): ${message}`);
|
|
131645
131979
|
this.name = "LarkApiError";
|
|
131646
131980
|
this.operation = operation;
|
|
131647
131981
|
this.code = code2;
|
|
131982
|
+
this.status = http.status;
|
|
131983
|
+
this.retryAfterMs = http.retryAfterMs;
|
|
131648
131984
|
}
|
|
131649
131985
|
};
|
|
131650
|
-
var isLarkPermissionError = (error) => error instanceof LarkApiError && (error.code === 99991672 || error.code === 99991679 ||
|
|
131986
|
+
var isLarkPermissionError = (error) => error instanceof LarkApiError && (error.code === 99991672 || error.code === 99991679 || error.status === 401 || error.status === 403);
|
|
131987
|
+
var LARK_RATE_LIMITED = 99991400;
|
|
131988
|
+
var isReplyRejected = (error) => error instanceof LarkApiError && error.status === void 0 && error.code !== LARK_RATE_LIMITED;
|
|
131989
|
+
var larkSendVerdict = (error) => {
|
|
131990
|
+
if (!(error instanceof LarkApiError)) return NO_RETRY;
|
|
131991
|
+
if (error.status === 429 || error.code === LARK_RATE_LIMITED) {
|
|
131992
|
+
return { retry: true, afterMs: error.retryAfterMs };
|
|
131993
|
+
}
|
|
131994
|
+
if (error.status !== void 0 && error.status >= 500) {
|
|
131995
|
+
return { retry: true, afterMs: error.retryAfterMs };
|
|
131996
|
+
}
|
|
131997
|
+
return NO_RETRY;
|
|
131998
|
+
};
|
|
131651
131999
|
var readEnvelope = async (operation, response) => {
|
|
131652
132000
|
if (!response.ok) {
|
|
131653
132001
|
await response.body?.cancel().catch(() => void 0);
|
|
131654
|
-
throw
|
|
131655
|
-
status: response.status
|
|
132002
|
+
throw new LarkApiError(operation, void 0, `HTTP ${String(response.status)}`, {
|
|
132003
|
+
status: response.status,
|
|
132004
|
+
retryAfterMs: parseRetryAfterMs(
|
|
132005
|
+
response.headers.get("retry-after") ?? response.headers.get("x-ogw-ratelimit-reset"),
|
|
132006
|
+
Date.now()
|
|
132007
|
+
) ?? void 0
|
|
131656
132008
|
});
|
|
131657
132009
|
}
|
|
131658
132010
|
const body = await response.json();
|
|
@@ -131679,35 +132031,37 @@ var postMessage = async (input) => {
|
|
|
131679
132031
|
authorization: `Bearer ${input.token}`,
|
|
131680
132032
|
"content-type": "application/json; charset=utf-8"
|
|
131681
132033
|
};
|
|
131682
|
-
const message = {
|
|
131683
|
-
|
|
132034
|
+
const message = {
|
|
132035
|
+
msg_type: input.msgType,
|
|
132036
|
+
content: JSON.stringify(input.content),
|
|
132037
|
+
...input.retry === void 0 ? {} : { uuid: crypto.randomUUID() }
|
|
132038
|
+
};
|
|
132039
|
+
const post = (url, body) => sendWithRetry(
|
|
132040
|
+
async () => (await readEnvelope(
|
|
132041
|
+
input.operation,
|
|
132042
|
+
await input.http(url, { method: "POST", headers, body: JSON.stringify(body) })
|
|
132043
|
+
)).data,
|
|
132044
|
+
larkSendVerdict,
|
|
132045
|
+
input.retry
|
|
132046
|
+
);
|
|
131684
132047
|
if (input.reply !== void 0) {
|
|
131685
132048
|
try {
|
|
131686
|
-
const
|
|
132049
|
+
const sent2 = await post(
|
|
131687
132050
|
`${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.reply.messageId)}/reply`,
|
|
131688
|
-
{
|
|
131689
|
-
method: "POST",
|
|
131690
|
-
headers,
|
|
131691
|
-
body: JSON.stringify({
|
|
131692
|
-
...message,
|
|
131693
|
-
...input.reply.inThread ? { reply_in_thread: true } : {}
|
|
131694
|
-
})
|
|
131695
|
-
}
|
|
132051
|
+
{ ...message, ...input.reply.inThread ? { reply_in_thread: true } : {} }
|
|
131696
132052
|
);
|
|
131697
|
-
|
|
131698
|
-
|
|
131699
|
-
return sent?.message_id ?? null;
|
|
132053
|
+
if (input.reply.inThread) input.onThreadReply?.(sent2?.thread_id || void 0);
|
|
132054
|
+
return sent2?.message_id ?? null;
|
|
131700
132055
|
} catch (error) {
|
|
131701
|
-
if (!(error
|
|
132056
|
+
if (!isReplyRejected(error)) throw error;
|
|
131702
132057
|
}
|
|
131703
132058
|
}
|
|
131704
132059
|
const idType = input.receiveIdType ?? "chat_id";
|
|
131705
|
-
const
|
|
131706
|
-
|
|
131707
|
-
|
|
131708
|
-
body: JSON.stringify({ receive_id: input.chatId, ...message })
|
|
132060
|
+
const sent = await post(`${LARK_BASE}/im/v1/messages?receive_id_type=${idType}`, {
|
|
132061
|
+
receive_id: input.chatId,
|
|
132062
|
+
...message
|
|
131709
132063
|
});
|
|
131710
|
-
return
|
|
132064
|
+
return sent?.message_id ?? null;
|
|
131711
132065
|
};
|
|
131712
132066
|
var readMessage = async (input) => {
|
|
131713
132067
|
const response = await input.http(
|
|
@@ -131962,9 +132316,9 @@ function getDecodeCache(exclude) {
|
|
|
131962
132316
|
}
|
|
131963
132317
|
return cache;
|
|
131964
132318
|
}
|
|
131965
|
-
function
|
|
132319
|
+
function decode2(string, exclude) {
|
|
131966
132320
|
if (typeof exclude !== "string") {
|
|
131967
|
-
exclude =
|
|
132321
|
+
exclude = decode2.defaultChars;
|
|
131968
132322
|
}
|
|
131969
132323
|
const cache = getDecodeCache(exclude);
|
|
131970
132324
|
return string.replace(/(%[a-f0-9]{2})+/gi, function(seq) {
|
|
@@ -132023,9 +132377,9 @@ function decode(string, exclude) {
|
|
|
132023
132377
|
return result;
|
|
132024
132378
|
});
|
|
132025
132379
|
}
|
|
132026
|
-
|
|
132027
|
-
|
|
132028
|
-
var decode_default =
|
|
132380
|
+
decode2.defaultChars = ";/?:@&=+$,#";
|
|
132381
|
+
decode2.componentChars = "";
|
|
132382
|
+
var decode_default = decode2;
|
|
132029
132383
|
|
|
132030
132384
|
// ../../node_modules/.pnpm/mdurl@2.1.0/node_modules/mdurl/lib/encode.mjs
|
|
132031
132385
|
var encodeCache = {};
|
|
@@ -133046,12 +133400,12 @@ var LinkifyIt = class {
|
|
|
133046
133400
|
*
|
|
133047
133401
|
* See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).
|
|
133048
133402
|
*/
|
|
133049
|
-
add(schema,
|
|
133050
|
-
if (!
|
|
133403
|
+
add(schema, definition2 = null) {
|
|
133404
|
+
if (!definition2) delete this.__schemas__[schema];
|
|
133051
133405
|
else {
|
|
133052
133406
|
const def = {
|
|
133053
133407
|
normalize: (match, self2) => self2.normalize(match),
|
|
133054
|
-
...
|
|
133408
|
+
...definition2
|
|
133055
133409
|
};
|
|
133056
133410
|
this.__schemas__[schema] = def;
|
|
133057
133411
|
}
|
|
@@ -137310,7 +137664,7 @@ var UNSUPPORTED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
|
137310
137664
|
"share_chat",
|
|
137311
137665
|
"share_user",
|
|
137312
137666
|
"system",
|
|
137313
|
-
"
|
|
137667
|
+
"merge_forward",
|
|
137314
137668
|
"interactive"
|
|
137315
137669
|
]);
|
|
137316
137670
|
var botIdsOf = (mentions, appId) => {
|
|
@@ -137482,6 +137836,7 @@ var cardActionOperator = (event) => {
|
|
|
137482
137836
|
}
|
|
137483
137837
|
return null;
|
|
137484
137838
|
};
|
|
137839
|
+
var isLarkAppId = (appId) => /^cli_[0-9a-fA-F]{16}$/u.test(appId);
|
|
137485
137840
|
var startLarkInbound = (input) => {
|
|
137486
137841
|
const dispatcher = new import_node_sdk.EventDispatcher({}).register({
|
|
137487
137842
|
"im.message.receive_v1": (event) => {
|
|
@@ -137523,19 +137878,59 @@ var startLarkInbound = (input) => {
|
|
|
137523
137878
|
return notice === null ? {} : { toast: { type: "info", content: notice } };
|
|
137524
137879
|
}
|
|
137525
137880
|
});
|
|
137881
|
+
if (!isLarkAppId(input.appId)) {
|
|
137882
|
+
input.onConnectionState?.({ state: "failed", reason: "invalid-app-id" });
|
|
137883
|
+
return { close: () => void 0 };
|
|
137884
|
+
}
|
|
137885
|
+
let closed = false;
|
|
137886
|
+
const connected = () => {
|
|
137887
|
+
if (!closed) input.onConnectionState?.({ state: "connected" });
|
|
137888
|
+
};
|
|
137889
|
+
const failed = (error) => {
|
|
137890
|
+
if (!closed) {
|
|
137891
|
+
input.onConnectionState?.({ state: "failed", reason: "disconnected", error });
|
|
137892
|
+
}
|
|
137893
|
+
};
|
|
137526
137894
|
const client = new import_node_sdk.WSClient({
|
|
137527
137895
|
appId: input.appId,
|
|
137528
137896
|
appSecret: input.appSecret,
|
|
137529
137897
|
...input.domain === void 0 ? {} : { domain: input.domain },
|
|
137530
137898
|
autoReconnect: true,
|
|
137531
|
-
|
|
137899
|
+
onReady: connected,
|
|
137900
|
+
onReconnected: connected,
|
|
137901
|
+
onReconnecting: () => failed(),
|
|
137902
|
+
onError: (cause) => {
|
|
137903
|
+
if (closed) return;
|
|
137904
|
+
failed(cause);
|
|
137905
|
+
input.onError(cause.message);
|
|
137906
|
+
}
|
|
137532
137907
|
});
|
|
137533
|
-
void client.start({ eventDispatcher: dispatcher });
|
|
137534
|
-
return {
|
|
137908
|
+
void client.start({ eventDispatcher: dispatcher }).catch(failed);
|
|
137909
|
+
return {
|
|
137910
|
+
close: () => {
|
|
137911
|
+
closed = true;
|
|
137912
|
+
client.close({ force: true });
|
|
137913
|
+
}
|
|
137914
|
+
};
|
|
137535
137915
|
};
|
|
137536
137916
|
|
|
137537
|
-
// src/
|
|
137538
|
-
|
|
137917
|
+
// src/boundedSeen.ts
|
|
137918
|
+
var makeBoundedSeen = (limit) => {
|
|
137919
|
+
const seen = /* @__PURE__ */ new Set();
|
|
137920
|
+
return {
|
|
137921
|
+
has: (key) => seen.has(key),
|
|
137922
|
+
/** 记下这个键:第一次见返回 true;已经记着返回 false(不重排,最早记的先被忘)。 */
|
|
137923
|
+
claim: (key) => {
|
|
137924
|
+
if (seen.has(key)) return false;
|
|
137925
|
+
seen.add(key);
|
|
137926
|
+
if (seen.size > limit) {
|
|
137927
|
+
const oldest = seen.values().next().value;
|
|
137928
|
+
if (oldest !== void 0) seen.delete(oldest);
|
|
137929
|
+
}
|
|
137930
|
+
return true;
|
|
137931
|
+
}
|
|
137932
|
+
};
|
|
137933
|
+
};
|
|
137539
137934
|
|
|
137540
137935
|
// src/deadline.ts
|
|
137541
137936
|
var withDeadline2 = async (timeoutMs, load) => {
|
|
@@ -137554,6 +137949,7 @@ var withDeadline2 = async (timeoutMs, load) => {
|
|
|
137554
137949
|
};
|
|
137555
137950
|
|
|
137556
137951
|
// src/replyReference.ts
|
|
137952
|
+
import * as NodeCrypto3 from "node:crypto";
|
|
137557
137953
|
var CONTENT_MAX = 8e3;
|
|
137558
137954
|
var ATTACHMENTS_MAX = 20;
|
|
137559
137955
|
var AUTHOR_MAX = 256;
|
|
@@ -137626,15 +138022,16 @@ var formatReplyReference = (reference2) => {
|
|
|
137626
138022
|
...lines.map((line) => `> ${line}`)
|
|
137627
138023
|
].join("\n");
|
|
137628
138024
|
};
|
|
137629
|
-
var textWithInboundContext = (message, rowId, t) => {
|
|
137630
|
-
const
|
|
137631
|
-
|
|
137632
|
-
|
|
137633
|
-
|
|
138025
|
+
var textWithInboundContext = (message, rowId, t, topicRoot) => {
|
|
138026
|
+
const text4 = [
|
|
138027
|
+
formatReplyReference(topicRoot),
|
|
138028
|
+
formatReplyReference(message.replyTo),
|
|
138029
|
+
message.text
|
|
138030
|
+
].filter((part) => part !== null).join("\n\n");
|
|
137634
138031
|
if (message.chatKind !== "group" || message.senderId === null) return text4;
|
|
137635
138032
|
let name = cleanString(message.senderName, AUTHOR_MAX).value;
|
|
137636
138033
|
if (!name) {
|
|
137637
|
-
const code2 =
|
|
138034
|
+
const code2 = NodeCrypto3.createHash("sha256").update(JSON.stringify([rowId, message.senderId])).digest("hex").slice(0, 4);
|
|
137638
138035
|
name = t("source.member", { code: code2 });
|
|
137639
138036
|
}
|
|
137640
138037
|
const chat = cleanString(message.chatTitle, AUTHOR_MAX).value;
|
|
@@ -137687,6 +138084,26 @@ var makeLookup = (rowId, load) => {
|
|
|
137687
138084
|
};
|
|
137688
138085
|
var makeLarkMessageContext = (deps) => {
|
|
137689
138086
|
let contactDenied = false;
|
|
138087
|
+
const deliveredRoots = makeBoundedSeen(CACHE_LIMIT);
|
|
138088
|
+
const loadReference = (messageId) => loadReplyReference(async (signal) => {
|
|
138089
|
+
const token = await deps.tenantToken();
|
|
138090
|
+
signal.throwIfAborted();
|
|
138091
|
+
return fetchMessageReference({ http: deps.http, token, messageId, signal });
|
|
138092
|
+
});
|
|
138093
|
+
const topicRootForSession = (message) => {
|
|
138094
|
+
const topic = message.topic;
|
|
138095
|
+
if (topic === void 0 || topic.rootGiven === false) return void 0;
|
|
138096
|
+
return async (recipientId) => {
|
|
138097
|
+
const key = JSON.stringify([message.chatId, topic.rootMessageId, recipientId]);
|
|
138098
|
+
if (deliveredRoots.has(key)) return void 0;
|
|
138099
|
+
return {
|
|
138100
|
+
reference: topic.rootMessageId === message.messageId ? void 0 : await loadReference(topic.rootMessageId),
|
|
138101
|
+
confirmConsumed: () => {
|
|
138102
|
+
deliveredRoots.claim(key);
|
|
138103
|
+
}
|
|
138104
|
+
};
|
|
138105
|
+
};
|
|
138106
|
+
};
|
|
137690
138107
|
const userName = makeLookup(deps.rowId, async (openId, signal) => {
|
|
137691
138108
|
try {
|
|
137692
138109
|
const token = await deps.tenantToken();
|
|
@@ -137713,7 +138130,7 @@ var makeLarkMessageContext = (deps) => {
|
|
|
137713
138130
|
});
|
|
137714
138131
|
return name instanceof Promise || chat instanceof Promise ? Promise.all([name, chat]).then(([senderName, info]) => fields(senderName, info)) : fields(name, chat);
|
|
137715
138132
|
};
|
|
137716
|
-
return { chatInfo, forMessage };
|
|
138133
|
+
return { chatInfo, forMessage, loadReference, topicRootForSession };
|
|
137717
138134
|
};
|
|
137718
138135
|
|
|
137719
138136
|
// src/larkAdapter.ts
|
|
@@ -137755,6 +138172,7 @@ var larkConfigIssues = (binding) => {
|
|
|
137755
138172
|
const { appId, appSecret } = credentials(binding);
|
|
137756
138173
|
const issues = [];
|
|
137757
138174
|
if (appId === "") issues.push({ column: "larkAppId", code: "required" });
|
|
138175
|
+
else if (!isLarkAppId(appId)) issues.push({ column: "larkAppId", code: "format" });
|
|
137758
138176
|
if (appSecret === "") issues.push({ column: "larkAppSecret", code: "required" });
|
|
137759
138177
|
return issues;
|
|
137760
138178
|
};
|
|
@@ -137842,7 +138260,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137842
138260
|
return rootMessageId === void 0 ? void 0 : groupId;
|
|
137843
138261
|
},
|
|
137844
138262
|
validate: larkConfigIssues,
|
|
137845
|
-
isConfigured: (binding) => larkConfigIssues(binding).
|
|
138263
|
+
isConfigured: (binding) => larkConfigIssues(binding).every((issue) => issue.code !== "required"),
|
|
137846
138264
|
connect: (binding, handlers) => {
|
|
137847
138265
|
const { appId, appSecret } = credentials(binding);
|
|
137848
138266
|
const port = {
|
|
@@ -137871,6 +138289,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137871
138289
|
topicOnMention: topicOnMentionOf(binding),
|
|
137872
138290
|
isSoloGroup: soloGroupFor(context),
|
|
137873
138291
|
onError: handlers.onError,
|
|
138292
|
+
...handlers.onConnectionState === void 0 ? {} : { onConnectionState: handlers.onConnectionState },
|
|
137874
138293
|
onCardAction: handlers.onButton,
|
|
137875
138294
|
onPanelClick: (click, operatorOpenId) => handlers.onPanelClick?.({
|
|
137876
138295
|
chatId: click.chatId,
|
|
@@ -137883,14 +138302,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137883
138302
|
const { topic } = message;
|
|
137884
138303
|
const source = context.forMessage(message);
|
|
137885
138304
|
const parentMessageId = message.parentMessageId;
|
|
137886
|
-
const reference2 = parentMessageId === void 0 ? null :
|
|
137887
|
-
async (signal) => fetchMessageReference({
|
|
137888
|
-
http: deps.http,
|
|
137889
|
-
token: await tenantToken(binding),
|
|
137890
|
-
messageId: parentMessageId,
|
|
137891
|
-
signal
|
|
137892
|
-
})
|
|
137893
|
-
);
|
|
138305
|
+
const reference2 = parentMessageId === void 0 ? null : context.loadReference(parentMessageId);
|
|
137894
138306
|
const deliver = (names, replyTo) => handlers.onMessage({
|
|
137895
138307
|
messageId: message.messageId,
|
|
137896
138308
|
// 已经开出来的话题就是它自己那个聊天;将要开出来的(主窗口里 @ 机器人)先算群的,
|
|
@@ -137906,6 +138318,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137906
138318
|
senderId: message.senderOpenId,
|
|
137907
138319
|
...names,
|
|
137908
138320
|
text: message.text,
|
|
138321
|
+
topicRootForSession: context.topicRootForSession(message),
|
|
137909
138322
|
...replyTo === void 0 ? {} : { replyTo },
|
|
137910
138323
|
chatKind: message.chatType === "p2p" ? "private" : "group",
|
|
137911
138324
|
replyInThread: message.inThread,
|
|
@@ -137960,7 +138373,8 @@ var makeLarkAdapter = (deps) => {
|
|
|
137960
138373
|
const messageId = await sendChatText({
|
|
137961
138374
|
...target2,
|
|
137962
138375
|
receiveIdType,
|
|
137963
|
-
text: chunk
|
|
138376
|
+
text: chunk,
|
|
138377
|
+
retry: options?.retry
|
|
137964
138378
|
});
|
|
137965
138379
|
deps.log.info("lark delivered a message", { messageId, bot: binding.displayName });
|
|
137966
138380
|
}
|
|
@@ -137974,6 +138388,9 @@ var makeLarkAdapter = (deps) => {
|
|
|
137974
138388
|
detail: "missing app id or secret in saved config"
|
|
137975
138389
|
};
|
|
137976
138390
|
}
|
|
138391
|
+
if (!isLarkAppId(appId)) {
|
|
138392
|
+
return { stage: "credentials", ok: false, detail: "malformed app id in saved config" };
|
|
138393
|
+
}
|
|
137977
138394
|
try {
|
|
137978
138395
|
const { expiresInSeconds } = await fetchTenantToken({ http: deps.http, appId, appSecret });
|
|
137979
138396
|
return {
|
|
@@ -138295,7 +138712,7 @@ var en_default = {
|
|
|
138295
138712
|
},
|
|
138296
138713
|
wecomAppListenPort: {
|
|
138297
138714
|
label: "Local callback port (optional)",
|
|
138298
|
-
description: "Defaults to 30987. Self-built apps listening on the same address share one port.",
|
|
138715
|
+
description: "Defaults to 30987. Self-built apps listening on the same address share one port; rows with different listen addresses (say 0.0.0.0 and 127.0.0.1) need different ports, or the second one fails to listen.",
|
|
138299
138716
|
placeholder: "30987"
|
|
138300
138717
|
},
|
|
138301
138718
|
wecomAppListenHost: {
|
|
@@ -138313,6 +138730,7 @@ var en_default = {
|
|
|
138313
138730
|
}
|
|
138314
138731
|
},
|
|
138315
138732
|
reply: {
|
|
138733
|
+
wecomDeliveryFailed: "This reply didn't reach {{recipient}} in WeCom: they are outside this app's visible range. Add them to the app's visible range in the WeCom admin console, then ask the agent to send it again.",
|
|
138316
138734
|
conclusionRecovered: "(Finished while restarting; delivered now)",
|
|
138317
138735
|
notAllowed: "You are not on this bot's allow list, so this tap had no effect.",
|
|
138318
138736
|
noDefaultModel: 'Project "{{title}}" has no agent selected yet, so a thread cannot be started. Send /model to pick one, or set a default in the project settings, then message again.',
|
|
@@ -138641,6 +139059,7 @@ var en_default = {
|
|
|
138641
139059
|
}
|
|
138642
139060
|
},
|
|
138643
139061
|
repo: {
|
|
139062
|
+
autoSelected: "Using project {{title}}.",
|
|
138644
139063
|
needProject: "This chat has no project yet, so I won't start any work. Pick one and I will.",
|
|
138645
139064
|
currentLine: 'This chat works on project "{{title}}".',
|
|
138646
139065
|
noProjects: "There are no projects on this machine yet. Add one in Ycodium first.",
|
|
@@ -138668,13 +139087,16 @@ var en_default = {
|
|
|
138668
139087
|
description: "Verify platform credentials still work; does not open a session or call a model.",
|
|
138669
139088
|
running: "Checking {{rowId}}\u2026",
|
|
138670
139089
|
done: "Connection OK",
|
|
138671
|
-
failed: "Connection failed
|
|
139090
|
+
failed: "Connection failed",
|
|
138672
139091
|
notSaved: "Save this row first (credentials are read from saved config only).",
|
|
138673
139092
|
hint: {
|
|
138674
|
-
badToken: "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key
|
|
138675
|
-
unauthorized: "Telegram does not accept this token. It may have been revoked; get the current one from BotFather
|
|
138676
|
-
notBot: "This token does not belong to a bot. Use the token BotFather gave you
|
|
138677
|
-
webhookActive: "This bot has a webhook set by another service, so it cannot receive messages here. Remove the webhook there, then check again
|
|
139093
|
+
badToken: "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key.",
|
|
139094
|
+
unauthorized: "Telegram does not accept this token. It may have been revoked; get the current one from BotFather.",
|
|
139095
|
+
notBot: "This token does not belong to a bot. Use the token BotFather gave you.",
|
|
139096
|
+
webhookActive: "This bot has a webhook set by another service, so it cannot receive messages here. Remove the webhook there, then check again.",
|
|
139097
|
+
wecomTrustedIp: "Add this machine's outbound public IP to the app's trusted IP list in the WeCom admin console.",
|
|
139098
|
+
wecomInvalidUser: "Check that the recipient, department or tag exists and is in this app's visible range.",
|
|
139099
|
+
wecomInvalidAgent: "Check the AgentId against this self-built application in the WeCom admin console."
|
|
138678
139100
|
}
|
|
138679
139101
|
},
|
|
138680
139102
|
copyDiagnostics: {
|
|
@@ -138682,24 +139104,24 @@ var en_default = {
|
|
|
138682
139104
|
description: "Run a connection probe and produce copy-paste diagnostics (no secrets).",
|
|
138683
139105
|
running: "Building diagnostics for {{rowId}}\u2026",
|
|
138684
139106
|
done: "Diagnostics ready \u2014 see details below",
|
|
138685
|
-
failed: "Diagnostics failed
|
|
139107
|
+
failed: "Diagnostics failed",
|
|
138686
139108
|
notSaved: "Save this row first before copying diagnostics."
|
|
138687
139109
|
},
|
|
138688
139110
|
diagnostic: {
|
|
138689
|
-
ref: "
|
|
139111
|
+
ref: "Reference: {{ref}}\n{{detail}}"
|
|
138690
139112
|
},
|
|
138691
139113
|
larkRepair: {
|
|
138692
139114
|
label: "Repair scopes",
|
|
138693
|
-
description: "
|
|
139115
|
+
description: "Check whether the saved credentials can mint a token; scopes, event subscriptions and publishing are not checked automatically.",
|
|
138694
139116
|
step: {
|
|
138695
|
-
scopes: "
|
|
138696
|
-
events: "
|
|
138697
|
-
publish: "
|
|
139117
|
+
scopes: "Scopes \u2014 not checked",
|
|
139118
|
+
events: "Event subscriptions \u2014 not checked",
|
|
139119
|
+
publish: "Publishing \u2014 not checked",
|
|
138698
139120
|
verify: "Verify the app can still mint a tenant token"
|
|
138699
139121
|
},
|
|
138700
|
-
done: "
|
|
138701
|
-
hint: "
|
|
138702
|
-
failed: "Could not
|
|
139122
|
+
done: "Only token issuance was verified. Scopes, event subscriptions and publishing were not checked automatically; review them on the Feishu open platform.",
|
|
139123
|
+
hint: "Reference: {{ref}}\n{{detail}}\nReview scopes, event subscriptions and publishing on the Feishu open platform; none were checked automatically.",
|
|
139124
|
+
failed: "Could not mint a token with the saved credentials. Scopes, event subscriptions and publishing were not checked automatically; review them on the Feishu open platform.",
|
|
138703
139125
|
notLark: "This row is not a saved Lark bot."
|
|
138704
139126
|
}
|
|
138705
139127
|
},
|
|
@@ -138738,7 +139160,23 @@ var en_default = {
|
|
|
138738
139160
|
removeFailed: "Couldn't delete this bot.",
|
|
138739
139161
|
status: {
|
|
138740
139162
|
ready: "Ready",
|
|
138741
|
-
incomplete: "Incomplete"
|
|
139163
|
+
incomplete: "Incomplete",
|
|
139164
|
+
connecting: "Connecting",
|
|
139165
|
+
connected: "Connected",
|
|
139166
|
+
notConnected: "Not connected"
|
|
139167
|
+
},
|
|
139168
|
+
connection: {
|
|
139169
|
+
failed: "The last connection attempt failed.",
|
|
139170
|
+
details: "Details: {{error}}",
|
|
139171
|
+
reason: {
|
|
139172
|
+
"not-available": "This platform can't be connected yet.",
|
|
139173
|
+
"invalid-app-id": "The App ID isn't in the right format.",
|
|
139174
|
+
disconnected: "The connection to the platform is down.",
|
|
139175
|
+
"subscription-failed": "The platform didn't confirm the subscription.",
|
|
139176
|
+
"token-unavailable": "Couldn't get an access token.",
|
|
139177
|
+
"credentials-rejected": "The platform rejected the credentials."
|
|
139178
|
+
},
|
|
139179
|
+
duplicate: "Uses the same credentials as \u201C{{name}}\u201D. Only that row connects."
|
|
138742
139180
|
},
|
|
138743
139181
|
secret: {
|
|
138744
139182
|
saved: "Saved. Leave blank to keep it.",
|
|
@@ -138977,7 +139415,7 @@ var zh_CN_default = {
|
|
|
138977
139415
|
},
|
|
138978
139416
|
wecomAppListenPort: {
|
|
138979
139417
|
label: "\u672C\u673A\u56DE\u8C03\u7AEF\u53E3\uFF08\u53EF\u9009\uFF09",
|
|
138980
|
-
description: "\u9ED8\u8BA4 30987\u3002\u76D1\u542C\u5730\u5740\u76F8\u540C\u7684\u81EA\u5EFA\u5E94\u7528\u5171\u7528\u4E00\u4E2A\u7AEF\u53E3\u3002",
|
|
139418
|
+
description: "\u9ED8\u8BA4 30987\u3002\u76D1\u542C\u5730\u5740\u76F8\u540C\u7684\u81EA\u5EFA\u5E94\u7528\u5171\u7528\u4E00\u4E2A\u7AEF\u53E3\uFF1B\u76D1\u542C\u5730\u5740\u4E0D\u540C\u7684\u4E24\u884C\uFF08\u6BD4\u5982 0.0.0.0 \u548C 127.0.0.1\uFF09\u8981\u7528\u4E0D\u540C\u7AEF\u53E3\uFF0C\u5426\u5219\u540E\u4E00\u4E2A\u76D1\u542C\u4E0D\u8D77\u6765\u3002",
|
|
138981
139419
|
placeholder: "30987"
|
|
138982
139420
|
},
|
|
138983
139421
|
wecomAppListenHost: {
|
|
@@ -138995,6 +139433,7 @@ var zh_CN_default = {
|
|
|
138995
139433
|
}
|
|
138996
139434
|
},
|
|
138997
139435
|
reply: {
|
|
139436
|
+
wecomDeliveryFailed: "\u8FD9\u6761\u56DE\u590D\u6CA1\u9001\u5230\u4F01\u4E1A\u5FAE\u4FE1\u91CC\u7684 {{recipient}}\uFF1A\u5BF9\u65B9\u4E0D\u5728\u8FD9\u4E2A\u5E94\u7528\u7684\u53EF\u89C1\u8303\u56F4\u3002\u5230\u4F01\u4E1A\u5FAE\u4FE1\u7BA1\u7406\u540E\u53F0\u628A\u4ED6\u4EEC\u52A0\u8FDB\u5E94\u7528\u7684\u53EF\u89C1\u8303\u56F4\uFF0C\u518D\u8BA9 agent \u91CD\u53D1\u4E00\u6B21\u3002",
|
|
138998
139437
|
conclusionRecovered: "\uFF08\u91CD\u542F\u671F\u95F4\u5B8C\u6210\uFF0C\u73B0\u5728\u8865\u53D1\uFF09",
|
|
138999
139438
|
notAllowed: "\u4F60\u4E0D\u5728\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u5141\u8BB8\u540D\u5355\u91CC\uFF0C\u8FD9\u6B21\u70B9\u51FB\u6CA1\u6709\u751F\u6548\u3002",
|
|
139000
139439
|
noDefaultModel: "\u9879\u76EE\u300C{{title}}\u300D\u8FD8\u6CA1\u9009 agent\uFF0C\u5F00\u4E0D\u4E86\u4F1A\u8BDD\u3002\u53D1 /model \u9009\u4E00\u4E2A\uFF0C\u6216\u8005\u5230\u9879\u76EE\u8BBE\u7F6E\u91CC\u8BBE\u4E2A\u9ED8\u8BA4\uFF0C\u518D\u53D1\u4E00\u6B21\u6D88\u606F\u3002",
|
|
@@ -139323,6 +139762,7 @@ var zh_CN_default = {
|
|
|
139323
139762
|
}
|
|
139324
139763
|
},
|
|
139325
139764
|
repo: {
|
|
139765
|
+
autoSelected: "\u4F7F\u7528\u9879\u76EE\u300C{{title}}\u300D\u3002",
|
|
139326
139766
|
needProject: "\u8FD9\u4E2A\u804A\u5929\u8FD8\u6CA1\u9009\u9879\u76EE\uFF0C\u9009\u5B9A\u4E4B\u524D\u6211\u4E0D\u5F00\u5DE5\u3002\u9009\u4E00\u4E2A\u5C31\u5F00\u59CB\u3002",
|
|
139327
139767
|
currentLine: "\u8FD9\u4E2A\u804A\u5929\u73B0\u5728\u5728\u9879\u76EE\u300C{{title}}\u300D\u4E0A\u3002",
|
|
139328
139768
|
noProjects: "\u8FD9\u53F0\u673A\u5668\u4E0A\u8FD8\u6CA1\u6709\u9879\u76EE\uFF0C\u5148\u5230 Ycodium \u91CC\u6DFB\u52A0\u4E00\u4E2A\u3002",
|
|
@@ -139350,13 +139790,16 @@ var zh_CN_default = {
|
|
|
139350
139790
|
description: "\u9A8C\u8BC1\u5E73\u53F0\u51ED\u636E\u662F\u5426\u4ECD\u53EF\u7528\uFF1B\u4E0D\u4F1A\u5F00\u4F1A\u8BDD\uFF0C\u4E5F\u4E0D\u4F1A\u8C03\u6A21\u578B\u3002",
|
|
139351
139791
|
running: "\u6B63\u5728\u68C0\u67E5 {{rowId}} \u2026",
|
|
139352
139792
|
done: "\u8FDE\u63A5\u6B63\u5E38",
|
|
139353
|
-
failed: "\u8FDE\u63A5\u5931\u8D25
|
|
139793
|
+
failed: "\u8FDE\u63A5\u5931\u8D25",
|
|
139354
139794
|
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u68C0\u67E5\u8FDE\u63A5\uFF08\u51ED\u636E\u53EA\u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u8BFB\u53D6\uFF09\u3002",
|
|
139355
139795
|
hint: {
|
|
139356
|
-
badToken: "\u8FD9\u4E0D\u50CF\u673A\u5668\u4EBA\u7684 token\u3002\u5230 BotFather \u91CD\u65B0\u590D\u5236\u4E00\u4EFD\uFF1A\u6570\u5B57\u3001\u5192\u53F7\uFF0C\u540E\u9762\u662F\u5BC6\u94A5\
|
|
139357
|
-
unauthorized: "Telegram \u4E0D\u8BA4\u8FD9\u4E2A token\uFF0C\u53EF\u80FD\u5DF2\u7ECF\u4F5C\u5E9F\u3002\u5230 BotFather \u53D6\u73B0\u5728\u7684\u90A3\u4E00\u4EFD\
|
|
139358
|
-
notBot: "\u8FD9\u4E2A token \u4E0D\u662F\u673A\u5668\u4EBA\u7684\u3002\u8BF7\u7528 BotFather \u7ED9\u7684\u90A3\u4E00\u4EFD\
|
|
139359
|
-
webhookActive: "\u8FD9\u4E2A\u673A\u5668\u4EBA\u88AB\u522B\u7684\u670D\u52A1\u8BBE\u4E86 webhook\uFF0C\u5728\u8FD9\u91CC\u6536\u4E0D\u5230\u6D88\u606F\u3002\u5148\u5230\u90A3\u8FB9\u64A4\u6389 webhook\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\
|
|
139796
|
+
badToken: "\u8FD9\u4E0D\u50CF\u673A\u5668\u4EBA\u7684 token\u3002\u5230 BotFather \u91CD\u65B0\u590D\u5236\u4E00\u4EFD\uFF1A\u6570\u5B57\u3001\u5192\u53F7\uFF0C\u540E\u9762\u662F\u5BC6\u94A5\u3002",
|
|
139797
|
+
unauthorized: "Telegram \u4E0D\u8BA4\u8FD9\u4E2A token\uFF0C\u53EF\u80FD\u5DF2\u7ECF\u4F5C\u5E9F\u3002\u5230 BotFather \u53D6\u73B0\u5728\u7684\u90A3\u4E00\u4EFD\u3002",
|
|
139798
|
+
notBot: "\u8FD9\u4E2A token \u4E0D\u662F\u673A\u5668\u4EBA\u7684\u3002\u8BF7\u7528 BotFather \u7ED9\u7684\u90A3\u4E00\u4EFD\u3002",
|
|
139799
|
+
webhookActive: "\u8FD9\u4E2A\u673A\u5668\u4EBA\u88AB\u522B\u7684\u670D\u52A1\u8BBE\u4E86 webhook\uFF0C\u5728\u8FD9\u91CC\u6536\u4E0D\u5230\u6D88\u606F\u3002\u5148\u5230\u90A3\u8FB9\u64A4\u6389 webhook\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
|
|
139800
|
+
wecomTrustedIp: "\u628A\u8FD9\u53F0\u673A\u5668\u7684\u51FA\u7AD9\u516C\u7F51 IP \u52A0\u8FDB\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u5E94\u7528\u7684\u4F01\u4E1A\u53EF\u4FE1 IP\u3002",
|
|
139801
|
+
wecomInvalidUser: "\u786E\u8BA4\u6536\u4EF6\u4EBA\u3001\u90E8\u95E8\u6216\u6807\u7B7E\u5B58\u5728\uFF0C\u5E76\u5728\u8FD9\u4E2A\u81EA\u5EFA\u5E94\u7528\u7684\u53EF\u89C1\u8303\u56F4\u5185\u3002",
|
|
139802
|
+
wecomInvalidAgent: "\u6838\u5BF9\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u81EA\u5EFA\u5E94\u7528\u7684 AgentId\u3002"
|
|
139360
139803
|
}
|
|
139361
139804
|
},
|
|
139362
139805
|
copyDiagnostics: {
|
|
@@ -139364,24 +139807,24 @@ var zh_CN_default = {
|
|
|
139364
139807
|
description: "\u8DD1\u4E00\u6B21\u8FDE\u63A5\u63A2\u6D4B\uFF0C\u751F\u6210\u53EF\u590D\u5236\u7684\u8BCA\u65AD\u6587\u672C\uFF08\u4E0D\u542B\u5BC6\u94A5\uFF09\u3002",
|
|
139365
139808
|
running: "\u6B63\u5728\u751F\u6210\u8BCA\u65AD {{rowId}} \u2026",
|
|
139366
139809
|
done: "\u8BCA\u65AD\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
139367
|
-
failed: "\u8BCA\u65AD\u5931\u8D25
|
|
139810
|
+
failed: "\u8BCA\u65AD\u5931\u8D25",
|
|
139368
139811
|
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u590D\u5236\u8BCA\u65AD\u3002"
|
|
139369
139812
|
},
|
|
139370
139813
|
diagnostic: {
|
|
139371
|
-
ref: "\u53C2\u8003\u53F7
|
|
139814
|
+
ref: "\u53C2\u8003\u53F7\uFF1A{{ref}}\n{{detail}}"
|
|
139372
139815
|
},
|
|
139373
139816
|
larkRepair: {
|
|
139374
139817
|
label: "\u8865\u5168\u6743\u9650",
|
|
139375
|
-
description: "\
|
|
139818
|
+
description: "\u68C0\u67E5\u5DF2\u4FDD\u5B58\u7684\u51ED\u636E\u80FD\u5426\u6362\u53D6 token\uFF1B\u6743\u9650\u3001\u4E8B\u4EF6\u8BA2\u9605\u548C\u53D1\u5E03\u4E0D\u4F1A\u81EA\u52A8\u68C0\u67E5\u3002",
|
|
139376
139819
|
step: {
|
|
139377
|
-
scopes: "\
|
|
139378
|
-
events: "\
|
|
139379
|
-
publish: "\
|
|
139820
|
+
scopes: "\u6743\u9650\uFF1A\u672A\u68C0\u67E5",
|
|
139821
|
+
events: "\u4E8B\u4EF6\u8BA2\u9605\uFF1A\u672A\u68C0\u67E5",
|
|
139822
|
+
publish: "\u53D1\u5E03\uFF1A\u672A\u68C0\u67E5",
|
|
139380
139823
|
verify: "\u9A8C\u8BC1\u5E94\u7528\u4ECD\u53EF\u6362\u53D6 tenant token"
|
|
139381
139824
|
},
|
|
139382
|
-
done: "\u51ED\u636E\
|
|
139383
|
-
hint: "\u53C2\u8003\u53F7
|
|
139384
|
-
failed: "\
|
|
139825
|
+
done: "\u4EC5\u6838\u5B9E\u4E86\u51ED\u636E\u4ECD\u80FD\u6362\u53D6 token\uFF1B\u6743\u9650\u3001\u4E8B\u4EF6\u8BA2\u9605\u548C\u53D1\u5E03\u6CA1\u6709\u81EA\u52A8\u68C0\u67E5\uFF0C\u8BF7\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u6838\u5BF9\u3002",
|
|
139826
|
+
hint: "\u53C2\u8003\u53F7\uFF1A{{ref}}\n{{detail}}\n\u8BF7\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u6838\u5BF9\u6743\u9650\u3001\u4E8B\u4EF6\u8BA2\u9605\u548C\u53D1\u5E03\uFF1B\u8FD9\u4E09\u9879\u5747\u672A\u81EA\u52A8\u68C0\u67E5\u3002",
|
|
139827
|
+
failed: "\u5DF2\u4FDD\u5B58\u7684\u51ED\u636E\u672A\u80FD\u6362\u53D6 token\uFF1B\u6743\u9650\u3001\u4E8B\u4EF6\u8BA2\u9605\u548C\u53D1\u5E03\u6CA1\u6709\u81EA\u52A8\u68C0\u67E5\uFF0C\u8BF7\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u6838\u5BF9\u3002",
|
|
139385
139828
|
notLark: "\u8FD9\u4E00\u884C\u4E0D\u662F\u5DF2\u4FDD\u5B58\u7684\u98DE\u4E66\u673A\u5668\u4EBA\u3002"
|
|
139386
139829
|
}
|
|
139387
139830
|
},
|
|
@@ -139419,8 +139862,24 @@ var zh_CN_default = {
|
|
|
139419
139862
|
copyFailed: "\u6CA1\u590D\u5236\u4E0A\u3002",
|
|
139420
139863
|
removeFailed: "\u6CA1\u80FD\u5220\u6389\u8FD9\u4E2A\u673A\u5668\u4EBA\u3002",
|
|
139421
139864
|
status: {
|
|
139422
|
-
ready: "\
|
|
139423
|
-
incomplete: "\
|
|
139865
|
+
ready: "\u5C31\u7EEA",
|
|
139866
|
+
incomplete: "\u5F85\u8865\u5168",
|
|
139867
|
+
connecting: "\u8FDE\u63A5\u4E2D",
|
|
139868
|
+
connected: "\u5DF2\u8FDE\u63A5",
|
|
139869
|
+
notConnected: "\u672A\u8FDE\u63A5"
|
|
139870
|
+
},
|
|
139871
|
+
connection: {
|
|
139872
|
+
failed: "\u4E0A\u6B21\u8FDE\u63A5\u6CA1\u6709\u6210\u529F\u3002",
|
|
139873
|
+
details: "\u8BE6\u60C5\uFF1A{{error}}",
|
|
139874
|
+
reason: {
|
|
139875
|
+
"not-available": "\u8FD9\u4E2A\u5E73\u53F0\u8FD8\u4E0D\u80FD\u8FDE\u63A5\u3002",
|
|
139876
|
+
"invalid-app-id": "App ID \u683C\u5F0F\u4E0D\u6B63\u786E\u3002",
|
|
139877
|
+
disconnected: "\u548C\u5E73\u53F0\u7684\u8FDE\u63A5\u65AD\u4E86\u3002",
|
|
139878
|
+
"subscription-failed": "\u5E73\u53F0\u6CA1\u6709\u786E\u8BA4\u8BA2\u9605\u3002",
|
|
139879
|
+
"token-unavailable": "\u6CA1\u80FD\u6362\u5230\u8BBF\u95EE\u4EE4\u724C\u3002",
|
|
139880
|
+
"credentials-rejected": "\u5E73\u53F0\u62D2\u7EDD\u4E86\u51ED\u636E\u3002"
|
|
139881
|
+
},
|
|
139882
|
+
duplicate: "\u548C\u300C{{name}}\u300D\u7528\u7684\u662F\u540C\u4E00\u4EFD\u51ED\u636E\uFF0C\u53EA\u8FDE\u90A3\u4E00\u884C\u3002"
|
|
139424
139883
|
},
|
|
139425
139884
|
secret: {
|
|
139426
139885
|
saved: "\u5DF2\u7ECF\u5B58\u8FC7\uFF0C\u7559\u7A7A\u5C31\u4FDD\u6301\u539F\u503C\u3002",
|
|
@@ -139601,6 +140060,16 @@ var makeModelPlan = (core, deps) => {
|
|
|
139601
140060
|
// src/notifications.ts
|
|
139602
140061
|
import { readThreadFailed, threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
|
|
139603
140062
|
|
|
140063
|
+
// src/conclusionOutbox.ts
|
|
140064
|
+
var enqueueConclusion = (core, ledger, binding, debt, send) => core.outbound(
|
|
140065
|
+
chatKeyOf(binding),
|
|
140066
|
+
async () => {
|
|
140067
|
+
if (debt === void 0) await send();
|
|
140068
|
+
else await ledger.deliver(debt, send);
|
|
140069
|
+
},
|
|
140070
|
+
{ droppable: debt !== void 0 }
|
|
140071
|
+
);
|
|
140072
|
+
|
|
139604
140073
|
// src/conclusionRecovery.ts
|
|
139605
140074
|
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
139606
140075
|
var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
@@ -139608,6 +140077,12 @@ var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
|
139608
140077
|
core.host.onDispose(() => {
|
|
139609
140078
|
disposed = true;
|
|
139610
140079
|
});
|
|
140080
|
+
const couldNotRecover = (debt, error) => {
|
|
140081
|
+
core.host.log.warn("chat could not recover a conclusion", {
|
|
140082
|
+
...debt,
|
|
140083
|
+
error: describeError(error)
|
|
140084
|
+
});
|
|
140085
|
+
};
|
|
139611
140086
|
await core.ready;
|
|
139612
140087
|
for (const debt of Object.values(conclusionDebtsOf(core.state))) {
|
|
139613
140088
|
if (disposed) return;
|
|
@@ -139634,24 +140109,27 @@ var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
|
139634
140109
|
}
|
|
139635
140110
|
const binding = core.bindingForChatKey(debt.chatKey);
|
|
139636
140111
|
if (binding === void 0) continue;
|
|
139637
|
-
|
|
139638
|
-
|
|
139639
|
-
|
|
139640
|
-
|
|
139641
|
-
|
|
139642
|
-
|
|
139643
|
-
|
|
139644
|
-
|
|
139645
|
-
|
|
140112
|
+
enqueueConclusion(core, ledger, binding, debt, async (owed) => {
|
|
140113
|
+
try {
|
|
140114
|
+
if (latest.state === "error") {
|
|
140115
|
+
return delivery.sendText(
|
|
140116
|
+
binding,
|
|
140117
|
+
debt.threadId,
|
|
140118
|
+
failures.forFailure(debt.threadId, execution.session?.lastError),
|
|
140119
|
+
true,
|
|
140120
|
+
latest.turnId,
|
|
140121
|
+
owed
|
|
140122
|
+
);
|
|
140123
|
+
}
|
|
140124
|
+
const send = await delivery.prepareConclusion(debt.threadId, latest.turnId);
|
|
140125
|
+
return send(binding, { recovered: true, streaming: false, owed });
|
|
140126
|
+
} catch (error) {
|
|
140127
|
+
couldNotRecover(debt, error);
|
|
140128
|
+
return false;
|
|
139646
140129
|
}
|
|
139647
|
-
const send = await delivery.prepareConclusion(debt.threadId, latest.turnId);
|
|
139648
|
-
return send(binding, { recovered: true, streaming: false });
|
|
139649
140130
|
});
|
|
139650
140131
|
} catch (error) {
|
|
139651
|
-
|
|
139652
|
-
...debt,
|
|
139653
|
-
error: describeError(error)
|
|
139654
|
-
});
|
|
140132
|
+
couldNotRecover(debt, error);
|
|
139655
140133
|
}
|
|
139656
140134
|
}
|
|
139657
140135
|
};
|
|
@@ -139674,7 +140152,7 @@ var outcomeOfSession = (status) => {
|
|
|
139674
140152
|
var THREAD_PROJECT_CACHE_LIMIT = 256;
|
|
139675
140153
|
var makeNotifications = (core, deps) => {
|
|
139676
140154
|
const { host, adapterFor, bindingForChatKey, ready } = core;
|
|
139677
|
-
const {
|
|
140155
|
+
const { takeWorking } = deps;
|
|
139678
140156
|
const { turnCards } = deps.replyCards;
|
|
139679
140157
|
const { failures, conclusions, delivery } = deps;
|
|
139680
140158
|
void recoverConclusions(core, conclusions, delivery, failures);
|
|
@@ -139697,6 +140175,11 @@ var makeNotifications = (core, deps) => {
|
|
|
139697
140175
|
});
|
|
139698
140176
|
const streamsTo = (binding, threadId) => adapterFor(binding.platform)?.turnCards !== void 0 && owns(binding, threadId);
|
|
139699
140177
|
const owns = (binding, threadId) => chatOfThread(core.state, threadId) === chatKeyOf(binding);
|
|
140178
|
+
const later = (binding, work) => {
|
|
140179
|
+
core.outbound(chatKeyOf(binding), async () => {
|
|
140180
|
+
await work();
|
|
140181
|
+
});
|
|
140182
|
+
};
|
|
139700
140183
|
const threadsWithSomethingToReport = /* @__PURE__ */ new Set();
|
|
139701
140184
|
host.reactor({
|
|
139702
140185
|
name: "im.assistant-messages",
|
|
@@ -139722,29 +140205,38 @@ var makeNotifications = (core, deps) => {
|
|
|
139722
140205
|
const owes = Object.values(conclusionDebtsOf(core.state)).some(
|
|
139723
140206
|
(debt2) => debt2.threadId === threadId
|
|
139724
140207
|
);
|
|
139725
|
-
const { debt, handled, execution } = said || owes ? await conclusions.forTurn(threadId) : { debt: void 0, handled: false, execution: null };
|
|
140208
|
+
const { debt, handled, execution } = said || owes || targets.length > 0 ? await conclusions.forTurn(threadId) : { debt: void 0, handled: false, execution: null };
|
|
139726
140209
|
if (debt !== void 0) {
|
|
139727
140210
|
const binding = bindingForChatKey(debt.chatKey);
|
|
139728
140211
|
if (binding !== void 0 && !streamsTo(binding, threadId) && execution?.session?.activeTurnId == null && execution?.latestTurn?.state !== "running" && execution?.latestTurn?.state !== "error" && payload.session?.status !== "error") {
|
|
139729
|
-
|
|
139730
|
-
|
|
139731
|
-
|
|
139732
|
-
|
|
139733
|
-
);
|
|
139734
|
-
return send(binding, { recovered: false, streaming: false });
|
|
140212
|
+
const turnId = execution?.latestTurn?.turnId ?? null;
|
|
140213
|
+
enqueueConclusion(core, conclusions, binding, debt, async (owed) => {
|
|
140214
|
+
const send = await delivery.prepareConclusion(threadId, turnId);
|
|
140215
|
+
return send(binding, { recovered: false, streaming: false, owed });
|
|
139735
140216
|
});
|
|
139736
140217
|
}
|
|
139737
140218
|
} else if (said && !handled) {
|
|
139738
140219
|
const send = await delivery.prepareConclusion(threadId);
|
|
139739
140220
|
for (const binding of targets) {
|
|
139740
|
-
|
|
139741
|
-
|
|
139742
|
-
|
|
139743
|
-
|
|
140221
|
+
const streaming = streamsTo(binding, threadId);
|
|
140222
|
+
enqueueConclusion(
|
|
140223
|
+
core,
|
|
140224
|
+
conclusions,
|
|
140225
|
+
binding,
|
|
140226
|
+
void 0,
|
|
140227
|
+
() => send(binding, { recovered: false, streaming })
|
|
140228
|
+
);
|
|
139744
140229
|
}
|
|
139745
140230
|
}
|
|
139746
140231
|
const outcome = outcomeOfSession(payload.session?.status);
|
|
139747
|
-
for (const binding of targets)
|
|
140232
|
+
for (const binding of targets) {
|
|
140233
|
+
const withdraw = takeWorking(binding, threadId, outcome);
|
|
140234
|
+
turnCards.afterTurn(
|
|
140235
|
+
threadId,
|
|
140236
|
+
() => later(binding, withdraw),
|
|
140237
|
+
execution?.latestTurn?.turnId ?? null
|
|
140238
|
+
);
|
|
140239
|
+
}
|
|
139748
140240
|
}
|
|
139749
140241
|
});
|
|
139750
140242
|
host.events.on("thread.failed", async (event) => {
|
|
@@ -139753,7 +140245,7 @@ var makeNotifications = (core, deps) => {
|
|
|
139753
140245
|
threadsWithSomethingToReport.delete(threadId);
|
|
139754
140246
|
await ready;
|
|
139755
140247
|
const targets = targetsForProject(threadId, await projectOfThread(threadId));
|
|
139756
|
-
const { debt, handled } = await conclusions.forTurn(threadId);
|
|
140248
|
+
const { debt, handled, execution } = await conclusions.forTurn(threadId);
|
|
139757
140249
|
const debtBinding = debt === void 0 ? void 0 : bindingForChatKey(debt.chatKey);
|
|
139758
140250
|
const recipients = debt !== void 0 ? debtBinding === void 0 ? [] : [debtBinding] : targets;
|
|
139759
140251
|
const textTargets = handled ? [] : recipients.filter(
|
|
@@ -139763,16 +140255,20 @@ var makeNotifications = (core, deps) => {
|
|
|
139763
140255
|
const notice = textTargets.length > 0 || drawingTurn !== null ? failures.forFailure(threadId, readThreadFailed(event)?.detail) : null;
|
|
139764
140256
|
if (notice !== null && drawingTurn !== null)
|
|
139765
140257
|
failures.holdForCard(threadId, drawingTurn, notice);
|
|
139766
|
-
for (const binding of targets) {
|
|
139767
|
-
await clearWorking(binding, threadId, "failed");
|
|
139768
|
-
}
|
|
139769
140258
|
if (notice !== null) {
|
|
139770
140259
|
for (const binding of textTargets) {
|
|
139771
|
-
const send = () => delivery.
|
|
139772
|
-
|
|
139773
|
-
else await send();
|
|
140260
|
+
const send = (owed) => delivery.sendText(binding, threadId, notice, false, null, owed);
|
|
140261
|
+
enqueueConclusion(core, conclusions, binding, debt, send);
|
|
139774
140262
|
}
|
|
139775
140263
|
}
|
|
140264
|
+
for (const binding of targets) {
|
|
140265
|
+
const withdraw = takeWorking(binding, threadId, "failed");
|
|
140266
|
+
turnCards.afterTurn(
|
|
140267
|
+
threadId,
|
|
140268
|
+
() => later(binding, withdraw),
|
|
140269
|
+
execution?.latestTurn?.turnId ?? null
|
|
140270
|
+
);
|
|
140271
|
+
}
|
|
139776
140272
|
});
|
|
139777
140273
|
const rememberThreadProject = (threadId, projectId) => {
|
|
139778
140274
|
projectByThread.set(threadId, projectId);
|
|
@@ -140333,32 +140829,6 @@ var makeProjectRows = (core) => {
|
|
|
140333
140829
|
return { projectRowsFor, listedProjectsByChat };
|
|
140334
140830
|
};
|
|
140335
140831
|
|
|
140336
|
-
// src/diagnostics.ts
|
|
140337
|
-
var PREFIX = {
|
|
140338
|
-
conn: "IM-CONN",
|
|
140339
|
-
msg: "IM-MSG",
|
|
140340
|
-
setup: "IM-SETUP"
|
|
140341
|
-
};
|
|
140342
|
-
var shortSalt = () => {
|
|
140343
|
-
const bytes = new Uint8Array(2);
|
|
140344
|
-
crypto.getRandomValues(bytes);
|
|
140345
|
-
return Array.from(bytes, (b) => b.toString(16).toUpperCase().padStart(2, "0")).join("");
|
|
140346
|
-
};
|
|
140347
|
-
var diagnosticRef = (kind, nowMs = Date.now()) => {
|
|
140348
|
-
const stamp = nowMs.toString(36).toUpperCase();
|
|
140349
|
-
return `${PREFIX[kind]}-${stamp}-${shortSalt()}`;
|
|
140350
|
-
};
|
|
140351
|
-
var formatDiagnosticCopy = (report) => [
|
|
140352
|
-
`ref=${report.ref}`,
|
|
140353
|
-
`ok=${report.ok ? "yes" : "no"}`,
|
|
140354
|
-
`platform=${report.platform}`,
|
|
140355
|
-
`bot=${report.botName}`,
|
|
140356
|
-
`row=${report.rowId}`,
|
|
140357
|
-
`stage=${report.stage}`,
|
|
140358
|
-
`at=${report.at}`,
|
|
140359
|
-
`detail=${report.detail}`
|
|
140360
|
-
].join("\n");
|
|
140361
|
-
|
|
140362
140832
|
// src/failureNotices.ts
|
|
140363
140833
|
var makeFailureNotices = (core) => {
|
|
140364
140834
|
const heldForCard = /* @__PURE__ */ new Map();
|
|
@@ -140450,20 +140920,12 @@ var trailingReply = (messages) => {
|
|
|
140450
140920
|
var ROTATE = "rotate";
|
|
140451
140921
|
var isDrawing = (live) => live.card !== null || live.reopen;
|
|
140452
140922
|
var RECENT_ENDED_LIMIT = 256;
|
|
140923
|
+
var turnKey = (threadId, turnId) => JSON.stringify([threadId, turnId]);
|
|
140453
140924
|
var makeTurnCards = (deps) => {
|
|
140454
140925
|
const slots = /* @__PURE__ */ new Map();
|
|
140455
|
-
const recentEnded =
|
|
140926
|
+
const recentEnded = makeBoundedSeen(RECENT_ENDED_LIMIT);
|
|
140456
140927
|
const erroredThreads = /* @__PURE__ */ new Set();
|
|
140457
140928
|
let disposed = false;
|
|
140458
|
-
const rememberEnded = (threadId, turnId) => {
|
|
140459
|
-
if (turnId === null) return;
|
|
140460
|
-
recentEnded.delete(threadId);
|
|
140461
|
-
recentEnded.set(threadId, turnId);
|
|
140462
|
-
if (recentEnded.size > RECENT_ENDED_LIMIT) {
|
|
140463
|
-
const oldest = recentEnded.keys().next().value;
|
|
140464
|
-
if (oldest !== void 0) recentEnded.delete(oldest);
|
|
140465
|
-
}
|
|
140466
|
-
};
|
|
140467
140929
|
const readTexts = async (live, progress) => {
|
|
140468
140930
|
const texts = /* @__PURE__ */ new Map();
|
|
140469
140931
|
for (const segment of progress.segments) {
|
|
@@ -140515,29 +140977,28 @@ var makeTurnCards = (deps) => {
|
|
|
140515
140977
|
const turnId = progress.turnId ?? live?.turnId ?? null;
|
|
140516
140978
|
const target2 = live?.target ?? deps.resolveTarget(threadId, turnId);
|
|
140517
140979
|
if (target2 === null) return;
|
|
140518
|
-
rememberEnded(threadId, turnId);
|
|
140519
140980
|
if (end.status === "error") erroredThreads.add(threadId);
|
|
140520
140981
|
const title = live?.title ?? await deps.titleOf(threadId);
|
|
140521
140982
|
const view = await finalView(title, progress, live, end);
|
|
140522
|
-
|
|
140523
|
-
|
|
140524
|
-
|
|
140525
|
-
|
|
140526
|
-
|
|
140527
|
-
|
|
140528
|
-
|
|
140529
|
-
|
|
140530
|
-
|
|
140531
|
-
|
|
140532
|
-
|
|
140533
|
-
|
|
140534
|
-
|
|
140535
|
-
|
|
140983
|
+
const card = live?.card ?? null;
|
|
140984
|
+
const finished = card !== null && await card.finish(view);
|
|
140985
|
+
if (card !== null) deps.onClosed(card.cardId);
|
|
140986
|
+
const send = async () => finished || card === null && await openCard(target2, view) !== null;
|
|
140987
|
+
const sent = deps.finalize === void 0 ? await send() : await deps.finalize(threadId, turnId, target2.chatKey, send);
|
|
140988
|
+
if (!sent) await deps.fallback(threadId, turnId, target2.chatKey, view);
|
|
140989
|
+
if (turnId !== null) recentEnded.claim(turnKey(threadId, turnId));
|
|
140990
|
+
const callbacks = slot.afterTurn.get(turnId) ?? [];
|
|
140991
|
+
slot.afterTurn.delete(turnId);
|
|
140992
|
+
if (turnId !== null) {
|
|
140993
|
+
callbacks.push(...slot.afterTurn.get(null) ?? []);
|
|
140994
|
+
slot.afterTurn.delete(null);
|
|
140995
|
+
}
|
|
140996
|
+
for (const after of callbacks) after();
|
|
140536
140997
|
};
|
|
140537
140998
|
const handle = async (slot, threadId, item) => {
|
|
140538
140999
|
await deps.ready;
|
|
140539
141000
|
if (disposed) return;
|
|
140540
|
-
if (item.turnId !== null && recentEnded.
|
|
141001
|
+
if (item.turnId !== null && recentEnded.has(turnKey(threadId, item.turnId))) return;
|
|
140541
141002
|
let live = slot.live;
|
|
140542
141003
|
if (live !== null) {
|
|
140543
141004
|
const sameTurn = item.turnId === null || live.turnId === null || live.turnId === item.turnId;
|
|
@@ -140621,7 +141082,11 @@ var makeTurnCards = (deps) => {
|
|
|
140621
141082
|
}
|
|
140622
141083
|
} finally {
|
|
140623
141084
|
slot.running = false;
|
|
140624
|
-
if (slot.live === null && slot.queue.length === 0)
|
|
141085
|
+
if (slot.live === null && slot.queue.length === 0) {
|
|
141086
|
+
slots.delete(threadId);
|
|
141087
|
+
for (const callbacks of slot.afterTurn.values()) for (const after of callbacks) after();
|
|
141088
|
+
slot.afterTurn.clear();
|
|
141089
|
+
}
|
|
140625
141090
|
}
|
|
140626
141091
|
};
|
|
140627
141092
|
return {
|
|
@@ -140630,7 +141095,7 @@ var makeTurnCards = (deps) => {
|
|
|
140630
141095
|
if (disposed) return;
|
|
140631
141096
|
let slot = slots.get(threadId);
|
|
140632
141097
|
if (slot === void 0) {
|
|
140633
|
-
slot = { queue: [], running: false, live: null };
|
|
141098
|
+
slot = { queue: [], running: false, live: null, afterTurn: /* @__PURE__ */ new Map() };
|
|
140634
141099
|
slots.set(threadId, slot);
|
|
140635
141100
|
}
|
|
140636
141101
|
const last = slot.queue.at(-1);
|
|
@@ -140644,6 +141109,18 @@ var makeTurnCards = (deps) => {
|
|
|
140644
141109
|
void pump(threadId, slot);
|
|
140645
141110
|
}
|
|
140646
141111
|
},
|
|
141112
|
+
/** 撤回执只等所属轮次收尾;没有轮次 id 时仍等当前线程槽,不能在出站队列里等自己。 */
|
|
141113
|
+
afterTurn: (threadId, work, turnId = null) => {
|
|
141114
|
+
if (disposed) return;
|
|
141115
|
+
const slot = slots.get(threadId);
|
|
141116
|
+
if (slot === void 0 || turnId !== null && recentEnded.has(turnKey(threadId, turnId)))
|
|
141117
|
+
work();
|
|
141118
|
+
else {
|
|
141119
|
+
const callbacks = slot.afterTurn.get(turnId) ?? [];
|
|
141120
|
+
callbacks.push(work);
|
|
141121
|
+
slot.afterTurn.set(turnId, callbacks);
|
|
141122
|
+
}
|
|
141123
|
+
},
|
|
140647
141124
|
/**
|
|
140648
141125
|
* 审批或提问答完了:封存这条会话开着的卡,下一拍另开一张。立刻返回;没有在画的卡、或
|
|
140649
141126
|
* 平台不换卡,就什么都不做。
|
|
@@ -140670,7 +141147,10 @@ var makeTurnCards = (deps) => {
|
|
|
140670
141147
|
/** 插件停用:放下所有在画的卡,不再发任何更新。卡会停在最后一拍,重启时被标「已中断」。 */
|
|
140671
141148
|
dispose: () => {
|
|
140672
141149
|
disposed = true;
|
|
140673
|
-
for (const slot of slots.values())
|
|
141150
|
+
for (const slot of slots.values()) {
|
|
141151
|
+
slot.live?.card?.abandon();
|
|
141152
|
+
slot.afterTurn.clear();
|
|
141153
|
+
}
|
|
140674
141154
|
slots.clear();
|
|
140675
141155
|
}
|
|
140676
141156
|
};
|
|
@@ -140678,7 +141158,7 @@ var makeTurnCards = (deps) => {
|
|
|
140678
141158
|
|
|
140679
141159
|
// src/replyCards.ts
|
|
140680
141160
|
var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
140681
|
-
const { host, adapterFor, commit,
|
|
141161
|
+
const { host, adapterFor, commit, bindingForChatKey, bindingOfThread, ready } = core;
|
|
140682
141162
|
const stopRequested = /* @__PURE__ */ new Set();
|
|
140683
141163
|
const turnCards = makeTurnCards({
|
|
140684
141164
|
ready,
|
|
@@ -140737,22 +141217,28 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
|
140737
141217
|
onClosed: (cardId) => commit("turn-card-closed", { cardId }),
|
|
140738
141218
|
finalize: async (threadId, turnId, chatKey, send) => {
|
|
140739
141219
|
const { debt, handled } = await conclusions.forTurn(threadId, turnId);
|
|
140740
|
-
if (handled) return;
|
|
140741
|
-
if (debt === void 0)
|
|
140742
|
-
|
|
140743
|
-
return;
|
|
140744
|
-
}
|
|
140745
|
-
if (!await conclusions.deliver(debt, send)) return;
|
|
141220
|
+
if (handled) return true;
|
|
141221
|
+
if (debt === void 0) return send();
|
|
141222
|
+
if (!await conclusions.deliver(debt, send)) return false;
|
|
140746
141223
|
const binding = bindingForChatKey(chatKey);
|
|
140747
|
-
if (binding === void 0) return;
|
|
141224
|
+
if (binding === void 0) return true;
|
|
140748
141225
|
const sendConclusion = await delivery.prepareConclusion(threadId, turnId);
|
|
140749
141226
|
await sendConclusion(binding, { recovered: false, streaming: true });
|
|
141227
|
+
return true;
|
|
140750
141228
|
},
|
|
140751
141229
|
fallback: async (threadId, turnId, chatKey, view) => {
|
|
141230
|
+
const { debt, handled } = await conclusions.forTurn(threadId, turnId);
|
|
141231
|
+
if (handled) return;
|
|
140752
141232
|
const binding = bindingForChatKey(chatKey);
|
|
140753
|
-
if (binding === void 0) return
|
|
141233
|
+
if (binding === void 0) return;
|
|
140754
141234
|
const text4 = [view.body, view.errorText ?? ""].filter((part) => part !== "").join("\n\n");
|
|
140755
|
-
|
|
141235
|
+
enqueueConclusion(
|
|
141236
|
+
core,
|
|
141237
|
+
conclusions,
|
|
141238
|
+
binding,
|
|
141239
|
+
debt,
|
|
141240
|
+
(owed) => delivery.sendText(binding, threadId, text4, false, turnId, owed)
|
|
141241
|
+
);
|
|
140756
141242
|
},
|
|
140757
141243
|
schedule: (run, delayMs) => {
|
|
140758
141244
|
const timer = setTimeout(run, delayMs);
|
|
@@ -141122,8 +141608,8 @@ var makeSessions = (core) => {
|
|
|
141122
141608
|
};
|
|
141123
141609
|
|
|
141124
141610
|
// src/teamGoals.ts
|
|
141125
|
-
import * as
|
|
141126
|
-
var
|
|
141611
|
+
import * as NodeCrypto4 from "node:crypto";
|
|
141612
|
+
var isRecord3 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
141127
141613
|
var teamGoalRequestId = (binding, messageId) => {
|
|
141128
141614
|
const identity = JSON.stringify([
|
|
141129
141615
|
"im",
|
|
@@ -141132,7 +141618,7 @@ var teamGoalRequestId = (binding, messageId) => {
|
|
|
141132
141618
|
binding.chatId,
|
|
141133
141619
|
messageId
|
|
141134
141620
|
]);
|
|
141135
|
-
return
|
|
141621
|
+
return NodeCrypto4.createHash("sha256").update(identity).digest("hex");
|
|
141136
141622
|
};
|
|
141137
141623
|
var makeTeamGoals = (core) => {
|
|
141138
141624
|
const { host, t, deliver, commit } = core;
|
|
@@ -141153,17 +141639,19 @@ var makeTeamGoals = (core) => {
|
|
|
141153
141639
|
};
|
|
141154
141640
|
const startTeamGoal = async (binding, message) => {
|
|
141155
141641
|
try {
|
|
141642
|
+
const requestId = teamGoalRequestId(binding, message.messageId);
|
|
141643
|
+
const topicRoot = await message.topicRootForSession?.(requestId);
|
|
141156
141644
|
const result = await host.plugins.invoke({
|
|
141157
141645
|
pluginId: "team",
|
|
141158
141646
|
command: "startGoal",
|
|
141159
141647
|
payload: {
|
|
141160
141648
|
projectId: binding.projectId,
|
|
141161
|
-
goal: textWithInboundContext(message, binding.rowId, t),
|
|
141649
|
+
goal: textWithInboundContext(message, binding.rowId, t, topicRoot?.reference),
|
|
141162
141650
|
titleSeed: message.text,
|
|
141163
|
-
requestId
|
|
141651
|
+
requestId
|
|
141164
141652
|
}
|
|
141165
141653
|
});
|
|
141166
|
-
if (
|
|
141654
|
+
if (isRecord3(result) && result._tag === "TeamGoalRejection") {
|
|
141167
141655
|
await deliver(
|
|
141168
141656
|
binding,
|
|
141169
141657
|
t(
|
|
@@ -141172,9 +141660,10 @@ var makeTeamGoals = (core) => {
|
|
|
141172
141660
|
);
|
|
141173
141661
|
return null;
|
|
141174
141662
|
}
|
|
141175
|
-
if (!
|
|
141663
|
+
if (!isRecord3(result) || typeof result.leadThreadId !== "string" || result.leadThreadId === "" || result.leadName !== null && typeof result.leadName !== "string") {
|
|
141176
141664
|
throw new Error("team startGoal returned an invalid result");
|
|
141177
141665
|
}
|
|
141666
|
+
topicRoot?.confirmConsumed();
|
|
141178
141667
|
commit("team-goal-started", { chatKey: chatKeyOf(binding), threadId: result.leadThreadId });
|
|
141179
141668
|
await deliver(
|
|
141180
141669
|
binding,
|
|
@@ -141194,6 +141683,9 @@ var makeTeamGoals = (core) => {
|
|
|
141194
141683
|
return { hasRosterFor, startTeamGoal };
|
|
141195
141684
|
};
|
|
141196
141685
|
|
|
141686
|
+
// src/telegramAdapter.ts
|
|
141687
|
+
import * as NodeCrypto5 from "node:crypto";
|
|
141688
|
+
|
|
141197
141689
|
// src/telegramApi.ts
|
|
141198
141690
|
var API_BASE = "https://api.telegram.org";
|
|
141199
141691
|
var POLL_TIMEOUT_SECONDS = 45;
|
|
@@ -141218,6 +141710,10 @@ var target = (chatId) => {
|
|
|
141218
141710
|
const { chat, thread } = splitChatId(chatId);
|
|
141219
141711
|
return { chat_id: chat, ...thread === null ? {} : { message_thread_id: thread } };
|
|
141220
141712
|
};
|
|
141713
|
+
var replyParameters = (messageId) => {
|
|
141714
|
+
const id = Number(messageId);
|
|
141715
|
+
return messageId === void 0 || !Number.isSafeInteger(id) ? {} : { reply_parameters: { message_id: id, allow_sending_without_reply: true } };
|
|
141716
|
+
};
|
|
141221
141717
|
var chatOnly = (chatId) => ({ chat_id: splitChatId(chatId).chat });
|
|
141222
141718
|
var TelegramApiError = class extends Error {
|
|
141223
141719
|
code;
|
|
@@ -141230,6 +141726,10 @@ var TelegramApiError = class extends Error {
|
|
|
141230
141726
|
this.retryAfterSeconds = retryAfterSeconds;
|
|
141231
141727
|
}
|
|
141232
141728
|
};
|
|
141729
|
+
var telegramSendVerdict = (error) => error instanceof TelegramApiError && (error.code === 429 || error.code === 503 && error.retryAfterSeconds !== void 0) ? {
|
|
141730
|
+
retry: true,
|
|
141731
|
+
afterMs: error.retryAfterSeconds === void 0 ? void 0 : error.retryAfterSeconds * 1e3
|
|
141732
|
+
} : NO_RETRY;
|
|
141233
141733
|
var isNotModified = (error) => error instanceof TelegramApiError && error.code === 400 && error.message.toLowerCase().includes("message is not modified");
|
|
141234
141734
|
var PHOTO_NAME = "photo.jpg";
|
|
141235
141735
|
var PHOTO_MIME = "image/jpeg";
|
|
@@ -141345,13 +141845,15 @@ var makeTelegramApi = (input) => {
|
|
|
141345
141845
|
}
|
|
141346
141846
|
return envelope.result;
|
|
141347
141847
|
};
|
|
141348
|
-
const upload = async (method, field, chatId, file) => {
|
|
141848
|
+
const upload = async (method, field, chatId, file, replyMessageId) => {
|
|
141349
141849
|
const form = new FormData();
|
|
141350
141850
|
const where = target(chatId);
|
|
141351
141851
|
form.set("chat_id", where.chat_id);
|
|
141352
141852
|
if (where.message_thread_id !== void 0) {
|
|
141353
141853
|
form.set("message_thread_id", String(where.message_thread_id));
|
|
141354
141854
|
}
|
|
141855
|
+
const reply = replyParameters(replyMessageId).reply_parameters;
|
|
141856
|
+
if (reply !== void 0) form.set("reply_parameters", JSON.stringify(reply));
|
|
141355
141857
|
form.set(field, new Blob([new Uint8Array(file.bytes)], { type: file.mimeType }), file.name);
|
|
141356
141858
|
const response = await send(
|
|
141357
141859
|
method,
|
|
@@ -141374,9 +141876,10 @@ var makeTelegramApi = (input) => {
|
|
|
141374
141876
|
(row) => row.map((button) => ({ text: button.text, callback_data: button.data }))
|
|
141375
141877
|
)
|
|
141376
141878
|
});
|
|
141377
|
-
const sendSingle = async (method, chatId, body, rows) => {
|
|
141879
|
+
const sendSingle = async (method, chatId, body, rows, replyMessageId) => {
|
|
141378
141880
|
const sent = await call(method, {
|
|
141379
141881
|
...target(chatId),
|
|
141882
|
+
...replyParameters(replyMessageId),
|
|
141380
141883
|
...body,
|
|
141381
141884
|
...rows === void 0 ? {} : { reply_markup: keyboard(rows) }
|
|
141382
141885
|
});
|
|
@@ -141436,21 +141939,26 @@ var makeTelegramApi = (input) => {
|
|
|
141436
141939
|
},
|
|
141437
141940
|
POLL_CALL_TIMEOUT_MS
|
|
141438
141941
|
),
|
|
141439
|
-
sendMessage: async (chatId, text4) => {
|
|
141440
|
-
for (const chunk of splitForTelegram(text4)) {
|
|
141441
|
-
|
|
141942
|
+
sendMessage: async (chatId, text4, retry, replyMessageId) => {
|
|
141943
|
+
for (const [index, chunk] of splitForTelegram(text4).entries()) {
|
|
141944
|
+
const reply = index === 0 ? replyMessageId : void 0;
|
|
141945
|
+
await sendWithRetry(
|
|
141946
|
+
() => sendSingle("sendMessage", chatId, { text: chunk }, void 0, reply),
|
|
141947
|
+
telegramSendVerdict,
|
|
141948
|
+
retry
|
|
141949
|
+
);
|
|
141442
141950
|
}
|
|
141443
141951
|
},
|
|
141444
141952
|
/**
|
|
141445
141953
|
* 发一条消息,回它的 id。调用方保证 `text` 不超过单条上限(边写边更新的回复要自己
|
|
141446
141954
|
* 在上限处翻篇,这里不替它切)。带 `rows` 就挂一块内联键盘。
|
|
141447
141955
|
*/
|
|
141448
|
-
sendOne: (chatId, text4, rows) => sendSingle("sendMessage", chatId, { text: text4 }, rows),
|
|
141956
|
+
sendOne: (chatId, text4, rows, replyMessageId) => sendSingle("sendMessage", chatId, { text: text4 }, rows, replyMessageId),
|
|
141449
141957
|
/**
|
|
141450
141958
|
* 发一条富文本,回它的 id。`markdown` 已是平台要的样子。带 `rows` 就挂一块内联键盘。
|
|
141451
141959
|
* 调用方保证不超富文本单条上限(这里不替它切)。
|
|
141452
141960
|
*/
|
|
141453
|
-
sendRichOne: (chatId, markdown, rows) => sendSingle("sendRichMessage", chatId, { rich_message: { markdown } }, rows),
|
|
141961
|
+
sendRichOne: (chatId, markdown, rows, replyMessageId) => sendSingle("sendRichMessage", chatId, { rich_message: { markdown } }, rows, replyMessageId),
|
|
141454
141962
|
/**
|
|
141455
141963
|
* 改一条已发出的消息。内容没变(`message is not modified`)当成功。`rows` 不给就是
|
|
141456
141964
|
* 去掉按钮:改了文字又要保留按钮的调用方得自己再传一次。
|
|
@@ -141462,20 +141970,21 @@ var makeTelegramApi = (input) => {
|
|
|
141462
141970
|
*/
|
|
141463
141971
|
editRich: (chatId, messageId, markdown, rows = []) => editSingle(chatId, messageId, { rich_message: { markdown } }, rows),
|
|
141464
141972
|
/** 带按钮的审批消息,一行一个按钮;回消息 id,之后才改得了它(平台没给就是 null)。 */
|
|
141465
|
-
sendButtons: (chatId, text4, buttons) => sendSingle(
|
|
141973
|
+
sendButtons: (chatId, text4, buttons, replyMessageId) => sendSingle(
|
|
141466
141974
|
"sendMessage",
|
|
141467
141975
|
chatId,
|
|
141468
141976
|
{ text: text4 },
|
|
141469
|
-
buttons.map((button) => [{ text: button.label, data: button.token }])
|
|
141977
|
+
buttons.map((button) => [{ text: button.label, data: button.token }]),
|
|
141978
|
+
replyMessageId
|
|
141470
141979
|
),
|
|
141471
141980
|
/** 答完点击,不然客户端上那个按钮一直转圈。带 `text` 时点击者会看到一句提示。 */
|
|
141472
141981
|
answerCallback: (callbackQueryId, text4) => call("answerCallbackQuery", {
|
|
141473
141982
|
callback_query_id: callbackQueryId,
|
|
141474
141983
|
...text4 === void 0 ? {} : { text: text4 }
|
|
141475
141984
|
}),
|
|
141476
|
-
sendPhoto: (chatId, photo) => upload("sendPhoto", "photo", chatId, photo),
|
|
141985
|
+
sendPhoto: (chatId, photo, replyMessageId) => upload("sendPhoto", "photo", chatId, photo, replyMessageId),
|
|
141477
141986
|
/** 以文件形式发(不压缩、不限尺寸比例):什么类型都行,音频与视频也走这条。 */
|
|
141478
|
-
sendDocument: (chatId, file) => upload("sendDocument", "document", chatId, file),
|
|
141987
|
+
sendDocument: (chatId, file, replyMessageId) => upload("sendDocument", "document", chatId, file, replyMessageId),
|
|
141479
141988
|
/** 取一个文件的字节。要两步:先换路径,再按路径下载。 */
|
|
141480
141989
|
fetchFile: async (fileId, maxBytes) => {
|
|
141481
141990
|
const file = await call("getFile", {
|
|
@@ -141687,7 +142196,7 @@ var makeTurnWriter = (deps) => {
|
|
|
141687
142196
|
const put = async (text4) => {
|
|
141688
142197
|
const bounded = text4.length <= deps.limit ? text4 : text4.slice(0, deps.limit);
|
|
141689
142198
|
if (live === null) {
|
|
141690
|
-
const id = await deps.messages.send(bounded);
|
|
142199
|
+
const id = await deps.messages.send(bounded, first === null);
|
|
141691
142200
|
if (id === null) throw new Error("telegram sent a message without an id");
|
|
141692
142201
|
live = id;
|
|
141693
142202
|
first ??= id;
|
|
@@ -141827,19 +142336,18 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141827
142336
|
limit: TELEGRAM_TEXT_MAX_CHARS,
|
|
141828
142337
|
...resume === void 0 ? {} : { resume },
|
|
141829
142338
|
messages: {
|
|
141830
|
-
send: (text4) =>
|
|
141831
|
-
|
|
141832
|
-
|
|
141833
|
-
|
|
141834
|
-
|
|
142339
|
+
send: (text4, first) => {
|
|
142340
|
+
const reply = first ? slot.binding.reply?.messageId : void 0;
|
|
142341
|
+
return withRichFallback(
|
|
142342
|
+
toTelegramRichMarkdown(text4),
|
|
142343
|
+
async (markdown) => (await apiFor(slot.binding)).sendRichOne(chatId, markdown, void 0, reply),
|
|
142344
|
+
async () => (await apiFor(slot.binding)).sendOne(chatId, text4, void 0, reply)
|
|
142345
|
+
);
|
|
142346
|
+
},
|
|
141835
142347
|
edit: (messageId, text4) => withRichFallback(
|
|
141836
142348
|
toTelegramRichMarkdown(text4),
|
|
141837
|
-
async (markdown) =>
|
|
141838
|
-
|
|
141839
|
-
},
|
|
141840
|
-
async () => {
|
|
141841
|
-
await (await apiFor(slot.binding)).editMessage(chatId, messageId, text4);
|
|
141842
|
-
}
|
|
142349
|
+
async (markdown) => (await apiFor(slot.binding)).editRich(chatId, messageId, markdown),
|
|
142350
|
+
async () => (await apiFor(slot.binding)).editMessage(chatId, messageId, text4)
|
|
141843
142351
|
)
|
|
141844
142352
|
}
|
|
141845
142353
|
});
|
|
@@ -141847,6 +142355,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141847
142355
|
platform: "telegram",
|
|
141848
142356
|
icon: "telegram",
|
|
141849
142357
|
label: "Telegram",
|
|
142358
|
+
quotesReplies: true,
|
|
141850
142359
|
columns: COLUMNS2,
|
|
141851
142360
|
validate: telegramConfigIssues,
|
|
141852
142361
|
isConfigured: (binding) => telegramConfigIssues(binding).length === 0,
|
|
@@ -141855,7 +142364,12 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141855
142364
|
};
|
|
141856
142365
|
const stopped = new Promise((resolve) => stop = resolve);
|
|
141857
142366
|
let running = true;
|
|
141858
|
-
|
|
142367
|
+
const tokenHash = NodeCrypto5.createHash("sha256").update(tokenOf(binding)).digest("hex");
|
|
142368
|
+
let offset = deps.readCursor(binding.rowId, tokenHash);
|
|
142369
|
+
const advanceOffset = (update) => {
|
|
142370
|
+
offset = nextOffset([update], offset ?? 0);
|
|
142371
|
+
deps.saveCursor(binding.rowId, tokenHash, offset);
|
|
142372
|
+
};
|
|
141859
142373
|
let acknowledged = 0;
|
|
141860
142374
|
let backoff = BACKOFF_START_MS;
|
|
141861
142375
|
let botUsername;
|
|
@@ -141868,7 +142382,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141868
142382
|
});
|
|
141869
142383
|
const publishAck = () => {
|
|
141870
142384
|
const api = liveApi;
|
|
141871
|
-
if (api === void 0 || offset <= acknowledged) return;
|
|
142385
|
+
if (api === void 0 || offset === void 0 || offset <= acknowledged) return;
|
|
141872
142386
|
const next = offset;
|
|
141873
142387
|
acknowledged = next;
|
|
141874
142388
|
void api.getUpdates(next, 0).catch(() => void 0);
|
|
@@ -141876,6 +142390,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141876
142390
|
const poll = async () => {
|
|
141877
142391
|
for (; ; ) {
|
|
141878
142392
|
if (!running) return;
|
|
142393
|
+
let checkingConnection = true;
|
|
141879
142394
|
try {
|
|
141880
142395
|
const api = await apiFor(binding);
|
|
141881
142396
|
liveApi = api;
|
|
@@ -141884,6 +142399,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141884
142399
|
const own = await api.getMe();
|
|
141885
142400
|
botUsername = own.username ?? null;
|
|
141886
142401
|
botId = own.id;
|
|
142402
|
+
if (running) handlers.onConnectionState?.({ state: "connected" });
|
|
141887
142403
|
}
|
|
141888
142404
|
if (!running) return;
|
|
141889
142405
|
if (!menuSent) {
|
|
@@ -141895,9 +142411,17 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141895
142411
|
});
|
|
141896
142412
|
});
|
|
141897
142413
|
}
|
|
142414
|
+
if (offset === void 0) {
|
|
142415
|
+
const backlog = await api.getUpdates(-1, 0);
|
|
142416
|
+
if (!running) return;
|
|
142417
|
+
offset = nextOffset(backlog, 0);
|
|
142418
|
+
deps.saveCursor(binding.rowId, tokenHash, offset);
|
|
142419
|
+
}
|
|
141898
142420
|
acknowledged = offset;
|
|
141899
142421
|
const updates = await api.getUpdates(offset);
|
|
141900
142422
|
if (!running) return;
|
|
142423
|
+
handlers.onConnectionState?.({ state: "connected" });
|
|
142424
|
+
checkingConnection = false;
|
|
141901
142425
|
backoff = BACKOFF_START_MS;
|
|
141902
142426
|
for (const update of updates) {
|
|
141903
142427
|
if (!running) break;
|
|
@@ -141905,13 +142429,13 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141905
142429
|
if (callback?.data !== void 0) {
|
|
141906
142430
|
const operatorId = callback.from === void 0 ? null : String(callback.from.id);
|
|
141907
142431
|
const notice = callback.data.startsWith(PANEL_DATA_PREFIX) ? panelClick(binding.rowId, callback, operatorId, handlers) : handlers.onButton(callback.data, operatorId, callback.id);
|
|
141908
|
-
|
|
142432
|
+
advanceOffset(update);
|
|
141909
142433
|
await api.answerCallback(callback.id, notice ?? void 0).catch(() => void 0);
|
|
141910
142434
|
continue;
|
|
141911
142435
|
}
|
|
141912
142436
|
const message = update.message;
|
|
141913
142437
|
if (message === void 0) {
|
|
141914
|
-
|
|
142438
|
+
advanceOffset(update);
|
|
141915
142439
|
continue;
|
|
141916
142440
|
}
|
|
141917
142441
|
const resource = resourceOf2(message);
|
|
@@ -141920,7 +142444,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141920
142444
|
botUsername
|
|
141921
142445
|
);
|
|
141922
142446
|
if (text4 === null) {
|
|
141923
|
-
|
|
142447
|
+
advanceOffset(update);
|
|
141924
142448
|
continue;
|
|
141925
142449
|
}
|
|
141926
142450
|
const gated = !passesGroupGate2({
|
|
@@ -141934,12 +142458,12 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141934
142458
|
const solo = binding.groupMention === "always" && await isSoloGroup(String(message.chat.id));
|
|
141935
142459
|
if (!running) break;
|
|
141936
142460
|
if (!solo) {
|
|
141937
|
-
|
|
142461
|
+
advanceOffset(update);
|
|
141938
142462
|
continue;
|
|
141939
142463
|
}
|
|
141940
142464
|
}
|
|
141941
142465
|
if (!running) break;
|
|
141942
|
-
|
|
142466
|
+
advanceOffset(update);
|
|
141943
142467
|
const senderId = message.from === void 0 ? null : String(message.from.id);
|
|
141944
142468
|
const senderName = telegramDisplayName(message.from);
|
|
141945
142469
|
const chatKind = message.chat.type === "private" ? "private" : "group";
|
|
@@ -141982,6 +142506,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141982
142506
|
}
|
|
141983
142507
|
} catch (error) {
|
|
141984
142508
|
if (!running) return;
|
|
142509
|
+
if (checkingConnection) handlers.onConnectionState?.({ state: "failed", error });
|
|
141985
142510
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
141986
142511
|
await Promise.race([(deps.sleep ?? defaultSleep)(backoff), stopped]);
|
|
141987
142512
|
backoff = Math.min(backoff * 2, BACKOFF_MAX_MS);
|
|
@@ -141999,9 +142524,9 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141999
142524
|
}
|
|
142000
142525
|
};
|
|
142001
142526
|
},
|
|
142002
|
-
sendText: async (binding, text4) => {
|
|
142527
|
+
sendText: async (binding, text4, options) => {
|
|
142003
142528
|
const api = await apiFor(binding);
|
|
142004
|
-
await api.sendMessage(binding.chatId, text4);
|
|
142529
|
+
await api.sendMessage(binding.chatId, text4, options?.retry, binding.reply?.messageId);
|
|
142005
142530
|
},
|
|
142006
142531
|
checkConnection: async (bot) => {
|
|
142007
142532
|
const token = tokenOf(bot);
|
|
@@ -142070,25 +142595,30 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142070
142595
|
sendImage: async (binding, image2) => {
|
|
142071
142596
|
const api = await apiFor(binding);
|
|
142072
142597
|
try {
|
|
142073
|
-
await api.sendPhoto(binding.chatId, image2);
|
|
142598
|
+
await api.sendPhoto(binding.chatId, image2, binding.reply?.messageId);
|
|
142074
142599
|
} catch (error) {
|
|
142075
142600
|
if (!(error instanceof TelegramApiError && error.code === 400)) throw error;
|
|
142076
142601
|
deps.log.info("telegram could not send the picture as a photo, sending it as a file", {
|
|
142077
142602
|
name: image2.name,
|
|
142078
142603
|
reason: error.message
|
|
142079
142604
|
});
|
|
142080
|
-
await api.sendDocument(binding.chatId, image2);
|
|
142605
|
+
await api.sendDocument(binding.chatId, image2, binding.reply?.messageId);
|
|
142081
142606
|
}
|
|
142082
142607
|
},
|
|
142083
142608
|
sendFile: async (binding, file) => {
|
|
142084
142609
|
const api = await apiFor(binding);
|
|
142085
|
-
await api.sendDocument(binding.chatId, file);
|
|
142610
|
+
await api.sendDocument(binding.chatId, file, binding.reply?.messageId);
|
|
142086
142611
|
},
|
|
142087
142612
|
// 单张照片 10 MB、单个文件 50 MB。这两个数来自 Bot API 文档的记忆,本仓库里没有核对过。
|
|
142088
142613
|
outboundLimits: { imageBytes: 10 * 1024 * 1024, fileBytes: 50 * 1024 * 1024 },
|
|
142089
142614
|
askApproval: async (binding, request) => {
|
|
142090
142615
|
const api = await apiFor(binding);
|
|
142091
|
-
return api.sendButtons(
|
|
142616
|
+
return api.sendButtons(
|
|
142617
|
+
binding.chatId,
|
|
142618
|
+
request.body,
|
|
142619
|
+
request.options,
|
|
142620
|
+
binding.reply?.messageId
|
|
142621
|
+
);
|
|
142092
142622
|
},
|
|
142093
142623
|
settleApproval: async (binding, cardId, settlement) => {
|
|
142094
142624
|
const api = await apiFor(binding);
|
|
@@ -142140,7 +142670,7 @@ ${line}`
|
|
|
142140
142670
|
const api = await apiFor(binding);
|
|
142141
142671
|
const rendered = renderPanel(card, 1);
|
|
142142
142672
|
const cardId = await withFloodWait(
|
|
142143
|
-
() => api.sendOne(binding.chatId, rendered.text, rendered.rows)
|
|
142673
|
+
() => api.sendOne(binding.chatId, rendered.text, rendered.rows, binding.reply?.messageId)
|
|
142144
142674
|
);
|
|
142145
142675
|
if (cardId !== null) {
|
|
142146
142676
|
panelKeyboards.set(`${binding.rowId}:${binding.chatId}:${cardId}`, {
|
|
@@ -142165,7 +142695,9 @@ ${line}`
|
|
|
142165
142695
|
open: async (binding, card) => {
|
|
142166
142696
|
const api = await apiFor(binding);
|
|
142167
142697
|
const rendered = renderChoiceCard(card);
|
|
142168
|
-
return withFloodWait(
|
|
142698
|
+
return withFloodWait(
|
|
142699
|
+
() => api.sendOne(binding.chatId, rendered.text, rendered.rows, binding.reply?.messageId)
|
|
142700
|
+
);
|
|
142169
142701
|
},
|
|
142170
142702
|
update: async (binding, cardId, card) => {
|
|
142171
142703
|
const api = await apiFor(binding);
|
|
@@ -142260,6 +142792,7 @@ var makeTurns = (core, deps) => {
|
|
|
142260
142792
|
const text4 = message.text;
|
|
142261
142793
|
const threadId = await ensureThread(binding, text4 || message.resources[0]?.name || "Chat");
|
|
142262
142794
|
if (threadId === null) return null;
|
|
142795
|
+
const topicRoot = await message.topicRootForSession?.(threadId);
|
|
142263
142796
|
const carried = waitingFor(chatKey, threadId);
|
|
142264
142797
|
pendingByChat.delete(chatKey);
|
|
142265
142798
|
const own = await collectResources(
|
|
@@ -142297,7 +142830,10 @@ var makeTurns = (core, deps) => {
|
|
|
142297
142830
|
message: {
|
|
142298
142831
|
messageId: userMessageId,
|
|
142299
142832
|
role: "user",
|
|
142300
|
-
text: [
|
|
142833
|
+
text: [
|
|
142834
|
+
textWithInboundContext(message, binding.rowId, t, topicRoot?.reference),
|
|
142835
|
+
...notes
|
|
142836
|
+
].join("\n"),
|
|
142301
142837
|
attachments: attachments.map(
|
|
142302
142838
|
(attachment) => attachment.type === "image" ? { ...attachment, type: "image" } : { ...attachment, type: "file" }
|
|
142303
142839
|
)
|
|
@@ -142307,6 +142843,7 @@ var makeTurns = (core, deps) => {
|
|
|
142307
142843
|
interactionMode: interactionModeOf(chatKey),
|
|
142308
142844
|
...intentAction !== void 0 ? { intentAction } : {}
|
|
142309
142845
|
});
|
|
142846
|
+
topicRoot?.confirmConsumed();
|
|
142310
142847
|
if (intentAction?.kind !== "steer") {
|
|
142311
142848
|
deps.conclusions.owe({
|
|
142312
142849
|
chatKey,
|
|
@@ -142435,8 +142972,15 @@ var ROW_ACTION_ITEM_SCHEMA = makeFlowItemSchema({
|
|
|
142435
142972
|
doneRequired: ["messageKey", "fills"]
|
|
142436
142973
|
});
|
|
142437
142974
|
var botOf = (config2, adapters, rowId) => {
|
|
142438
|
-
const
|
|
142439
|
-
|
|
142975
|
+
const table2 = config2[BOTS_FIELD];
|
|
142976
|
+
if (typeof table2 !== "object" || table2 === null || Array.isArray(table2) || !Object.hasOwn(table2, rowId))
|
|
142977
|
+
return void 0;
|
|
142978
|
+
const row = table2[rowId];
|
|
142979
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) return void 0;
|
|
142980
|
+
const raw = row;
|
|
142981
|
+
const platform = platformOf(raw);
|
|
142982
|
+
const adapter = adapters.find((candidate) => candidate.platform === platform);
|
|
142983
|
+
return { ...readBotView(rowId, raw, adapter ?? { label: platform }), platform };
|
|
142440
142984
|
};
|
|
142441
142985
|
var reportOf = (bot, stage, ok, detail, hint) => ({
|
|
142442
142986
|
ref: diagnosticRef("conn"),
|
|
@@ -142452,12 +142996,9 @@ var reportOf = (bot, stage, ok, detail, hint) => ({
|
|
|
142452
142996
|
var probe = async (adapters, bot) => {
|
|
142453
142997
|
const adapter = adapters.find((candidate) => candidate.platform === bot.platform);
|
|
142454
142998
|
if (adapter?.checkConnection === void 0) {
|
|
142455
|
-
return reportOf(bot, "platform", false, `unsupported platform ${bot.platform}
|
|
142456
|
-
}
|
|
142457
|
-
const redactor = makeRedactor();
|
|
142458
|
-
for (const column of adapter.columns) {
|
|
142459
|
-
if (column.kind === "password") redactor.register(bot.values[column.key]);
|
|
142999
|
+
return reportOf(bot, "platform", false, `unsupported platform ${bot.platform}`, "notAvailable");
|
|
142460
143000
|
}
|
|
143001
|
+
const redactor = botRedactor(adapter.columns, bot.values);
|
|
142461
143002
|
try {
|
|
142462
143003
|
const result = await adapter.checkConnection(bot);
|
|
142463
143004
|
return reportOf(
|
|
@@ -142482,10 +143023,15 @@ var emitReport = (emit, report, action) => {
|
|
|
142482
143023
|
});
|
|
142483
143024
|
return;
|
|
142484
143025
|
}
|
|
143026
|
+
const suggested = report.hint !== void 0 && action.keyPrefix === "rowAction.checkConnection";
|
|
142485
143027
|
emit({
|
|
142486
143028
|
kind: "failed",
|
|
142487
|
-
reasonKey: report.hint !== void 0
|
|
142488
|
-
|
|
143029
|
+
reasonKey: suggested && report.hint !== void 0 ? connectionHintKey(report.hint) : `${action.keyPrefix}.failed`,
|
|
143030
|
+
// 参考号由界面写在细节第一行(「参考号:…」),结论句里不带。
|
|
143031
|
+
params: {
|
|
143032
|
+
ref: report.ref,
|
|
143033
|
+
...suggested && report.hint !== void 0 ? { hint: report.hint } : {}
|
|
143034
|
+
},
|
|
142489
143035
|
detail
|
|
142490
143036
|
});
|
|
142491
143037
|
};
|
|
@@ -142537,9 +143083,9 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142537
143083
|
send({
|
|
142538
143084
|
kind: "steps",
|
|
142539
143085
|
steps: [
|
|
142540
|
-
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "
|
|
142541
|
-
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "
|
|
142542
|
-
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "
|
|
143086
|
+
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "skipped" },
|
|
143087
|
+
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "skipped" },
|
|
143088
|
+
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "skipped" },
|
|
142543
143089
|
{ id: "verify", labelKey: "rowAction.larkRepair.step.verify", state: "running" }
|
|
142544
143090
|
]
|
|
142545
143091
|
});
|
|
@@ -142556,9 +143102,9 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142556
143102
|
send({
|
|
142557
143103
|
kind: "steps",
|
|
142558
143104
|
steps: [
|
|
142559
|
-
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "
|
|
142560
|
-
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "
|
|
142561
|
-
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "
|
|
143105
|
+
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "skipped" },
|
|
143106
|
+
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "skipped" },
|
|
143107
|
+
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "skipped" },
|
|
142562
143108
|
{
|
|
142563
143109
|
id: "verify",
|
|
142564
143110
|
labelKey: "rowAction.larkRepair.step.verify",
|
|
@@ -142574,7 +143120,7 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142574
143120
|
warnings: [
|
|
142575
143121
|
{
|
|
142576
143122
|
key: "rowAction.larkRepair.hint",
|
|
142577
|
-
params: { ref, detail: formatDiagnosticCopy(
|
|
143123
|
+
params: { ref, detail: formatDiagnosticCopy(report) }
|
|
142578
143124
|
}
|
|
142579
143125
|
]
|
|
142580
143126
|
});
|
|
@@ -142584,14 +143130,14 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142584
143130
|
kind: "failed",
|
|
142585
143131
|
reasonKey: "rowAction.larkRepair.failed",
|
|
142586
143132
|
params: { ref },
|
|
142587
|
-
detail: formatDiagnosticCopy(
|
|
143133
|
+
detail: formatDiagnosticCopy(report)
|
|
142588
143134
|
});
|
|
142589
143135
|
}
|
|
142590
143136
|
});
|
|
142591
143137
|
};
|
|
142592
143138
|
|
|
142593
143139
|
// src/settingsView.ts
|
|
142594
|
-
var
|
|
143140
|
+
var isRecord4 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
142595
143141
|
var hasStoredSecret = (row, key) => {
|
|
142596
143142
|
const value2 = row[key];
|
|
142597
143143
|
return typeof value2 === "string" && value2 !== "";
|
|
@@ -142609,10 +143155,10 @@ var targetTable = () => ({
|
|
|
142609
143155
|
});
|
|
142610
143156
|
var targetViews = (config2) => {
|
|
142611
143157
|
const table2 = config2[DELIVERY_TARGETS_FIELD];
|
|
142612
|
-
if (!
|
|
143158
|
+
if (!isRecord4(table2)) return [];
|
|
142613
143159
|
const columns = deliveryTargetColumns();
|
|
142614
143160
|
return Object.entries(table2).flatMap(([rowId, raw]) => {
|
|
142615
|
-
if (!
|
|
143161
|
+
if (!isRecord4(raw)) return [];
|
|
142616
143162
|
const issues = targetIssues(raw);
|
|
142617
143163
|
const values = Object.fromEntries(
|
|
142618
143164
|
columns.map((column) => [column.key, readString(raw, column.key)])
|
|
@@ -142624,9 +143170,9 @@ var buildSettingsView = (config2, adapters) => {
|
|
|
142624
143170
|
const columns = buildBotColumns(adapters);
|
|
142625
143171
|
const table2 = config2[BOTS_FIELD];
|
|
142626
143172
|
const bots = [];
|
|
142627
|
-
if (
|
|
143173
|
+
if (isRecord4(table2)) {
|
|
142628
143174
|
for (const [rowId, raw] of Object.entries(table2)) {
|
|
142629
|
-
if (!
|
|
143175
|
+
if (!isRecord4(raw)) continue;
|
|
142630
143176
|
const platform = platformOf(raw);
|
|
142631
143177
|
const values = {};
|
|
142632
143178
|
const secrets = {};
|
|
@@ -142689,10 +143235,10 @@ var applyRowEdit = (config2, { field, columns }, edit) => {
|
|
|
142689
143235
|
for (const key of clearSecrets) {
|
|
142690
143236
|
if (!secretColumns.has(key)) return columnError("not-a-secret", key);
|
|
142691
143237
|
}
|
|
142692
|
-
const table2 =
|
|
143238
|
+
const table2 = isRecord4(config2[field]) ? config2[field] : {};
|
|
142693
143239
|
const requested = edit.rowId;
|
|
142694
143240
|
const current = requested === void 0 ? {} : Object.hasOwn(table2, requested) ? table2[requested] : void 0;
|
|
142695
|
-
if (!
|
|
143241
|
+
if (!isRecord4(current)) return { ok: false, code: "unknown-row" };
|
|
142696
143242
|
const rowId = requested ?? nextRowId(table2);
|
|
142697
143243
|
const nextRow = { ...current, ...edit.values };
|
|
142698
143244
|
const clearing = clearSecrets.filter((key) => !Object.hasOwn(secrets, key));
|
|
@@ -142710,7 +143256,7 @@ var applyBotEdit = (config2, adapters, edit) => applyRowEdit(config2, { field: B
|
|
|
142710
143256
|
var applyTargetEdit = (config2, edit) => applyRowEdit(config2, targetTable(), edit);
|
|
142711
143257
|
var removeRow = (config2, field, rowId) => {
|
|
142712
143258
|
const table2 = config2[field];
|
|
142713
|
-
if (!
|
|
143259
|
+
if (!isRecord4(table2) || !Object.hasOwn(table2, rowId)) return null;
|
|
142714
143260
|
const nextTable = { ...table2 };
|
|
142715
143261
|
delete nextTable[rowId];
|
|
142716
143262
|
return { ...config2, [field]: nextTable };
|
|
@@ -142784,8 +143330,29 @@ var generalWriteOptions = (payload, next) => {
|
|
|
142784
143330
|
}
|
|
142785
143331
|
return void 0;
|
|
142786
143332
|
};
|
|
142787
|
-
var
|
|
142788
|
-
|
|
143333
|
+
var registerSettingsCommands = (ctx, adapters, updateSettings, connections) => {
|
|
143334
|
+
const viewAfterWrite = async () => buildSettingsView(await ctx.settings.read(), adapters);
|
|
143335
|
+
ctx.stream({
|
|
143336
|
+
name: SETTINGS_CONNECTIONS_COMMAND,
|
|
143337
|
+
description: "Read bot connection states as an initial snapshot and subsequent changes.",
|
|
143338
|
+
scope: "read",
|
|
143339
|
+
payloadSchema: EMPTY_PAYLOAD,
|
|
143340
|
+
buffer: { latest: 1 },
|
|
143341
|
+
handle: async (_payload, emit, signal) => {
|
|
143342
|
+
if (signal.aborted) return;
|
|
143343
|
+
const unsubscribe = connections.subscribe((snapshot) => {
|
|
143344
|
+
if (!signal.aborted) emit(snapshot);
|
|
143345
|
+
});
|
|
143346
|
+
try {
|
|
143347
|
+
await new Promise((resolve) => {
|
|
143348
|
+
if (signal.aborted) resolve();
|
|
143349
|
+
else signal.addEventListener("abort", () => resolve(), { once: true });
|
|
143350
|
+
});
|
|
143351
|
+
} finally {
|
|
143352
|
+
unsubscribe();
|
|
143353
|
+
}
|
|
143354
|
+
}
|
|
143355
|
+
});
|
|
142789
143356
|
ctx.command({
|
|
142790
143357
|
name: SETTINGS_VIEW_COMMAND,
|
|
142791
143358
|
description: "Read the chat-bridge settings view, bot columns, and this machine's proxies.",
|
|
@@ -142812,7 +143379,7 @@ var registerSettingsCommands = (ctx, adapters, updateSettings) => {
|
|
|
142812
143379
|
return { result, write: { config: next, options: { secrets, clearSecrets } } };
|
|
142813
143380
|
});
|
|
142814
143381
|
if (!edited.ok) return edited;
|
|
142815
|
-
return { ok: true, rowId: edited.rowId, view: await viewAfterWrite(
|
|
143382
|
+
return { ok: true, rowId: edited.rowId, view: await viewAfterWrite() };
|
|
142816
143383
|
};
|
|
142817
143384
|
const removeRow2 = async (remove) => {
|
|
142818
143385
|
const removed = await updateSettings((config2) => {
|
|
@@ -142820,7 +143387,7 @@ var registerSettingsCommands = (ctx, adapters, updateSettings) => {
|
|
|
142820
143387
|
return next === null ? { result: false } : { result: true, write: { config: next } };
|
|
142821
143388
|
});
|
|
142822
143389
|
if (!removed) return { ok: false, code: "unknown-row" };
|
|
142823
|
-
return { ok: true, view: await viewAfterWrite(
|
|
143390
|
+
return { ok: true, view: await viewAfterWrite() };
|
|
142824
143391
|
};
|
|
142825
143392
|
ctx.command({
|
|
142826
143393
|
name: SETTINGS_SAVE_BOT_COMMAND,
|
|
@@ -142871,7 +143438,7 @@ var registerSettingsCommands = (ctx, adapters, updateSettings) => {
|
|
|
142871
143438
|
write: options === void 0 ? { config: next } : { config: next, options }
|
|
142872
143439
|
};
|
|
142873
143440
|
});
|
|
142874
|
-
return { ok: true, view: await viewAfterWrite(
|
|
143441
|
+
return { ok: true, view: await viewAfterWrite() };
|
|
142875
143442
|
}
|
|
142876
143443
|
});
|
|
142877
143444
|
};
|
|
@@ -142899,12 +143466,13 @@ var makeWechatAdapter = (deps) => {
|
|
|
142899
143466
|
columns: COLUMNS3,
|
|
142900
143467
|
validate: wechatConfigIssues,
|
|
142901
143468
|
isConfigured: (bot) => wechatConfigIssues(bot).length === 0,
|
|
142902
|
-
connect: (bot,
|
|
143469
|
+
connect: (bot, handlers) => {
|
|
142903
143470
|
deps.log.warn("wechat iLink connect is stubbed pending security-review gate (YCO-236)", {
|
|
142904
143471
|
bot: bot.displayName,
|
|
142905
143472
|
hasToken: tokenOf2(bot) !== "",
|
|
142906
143473
|
textLimit: WECHAT_TEXT_MAX_CHARS
|
|
142907
143474
|
});
|
|
143475
|
+
handlers.onConnectionState?.({ state: "failed", reason: "not-available" });
|
|
142908
143476
|
return { close: () => void 0 };
|
|
142909
143477
|
},
|
|
142910
143478
|
sendText: async (binding, _text) => {
|
|
@@ -142927,7 +143495,7 @@ var makeWechatAdapter = (deps) => {
|
|
|
142927
143495
|
};
|
|
142928
143496
|
|
|
142929
143497
|
// src/wecomUpload.ts
|
|
142930
|
-
import * as
|
|
143498
|
+
import * as NodeCrypto6 from "node:crypto";
|
|
142931
143499
|
var WECOM_CHUNK_BYTES = 512 * 1024;
|
|
142932
143500
|
var WECOM_MAX_CHUNKS = 100;
|
|
142933
143501
|
var WECOM_MIN_UPLOAD_BYTES = 5;
|
|
@@ -142960,7 +143528,7 @@ var uploadWecomMedia = async (input) => {
|
|
|
142960
143528
|
filename: clampName(input.filename),
|
|
142961
143529
|
total_size: bytes.byteLength,
|
|
142962
143530
|
total_chunks: totalChunks,
|
|
142963
|
-
md5:
|
|
143531
|
+
md5: NodeCrypto6.createHash("md5").update(bytes).digest("hex")
|
|
142964
143532
|
});
|
|
142965
143533
|
const uploadId = init.upload_id;
|
|
142966
143534
|
if (typeof uploadId !== "string" || uploadId === "") {
|
|
@@ -143047,7 +143615,7 @@ var wecomReplyReference = (quote) => {
|
|
|
143047
143615
|
};
|
|
143048
143616
|
|
|
143049
143617
|
// src/wecomApi.ts
|
|
143050
|
-
var DEFAULT_WECOM_ENDPOINT = "wss://openws.work.weixin.qq.com
|
|
143618
|
+
var DEFAULT_WECOM_ENDPOINT = "wss://openws.work.weixin.qq.com";
|
|
143051
143619
|
var PING_INTERVAL_MS = 3e4;
|
|
143052
143620
|
var MAX_MISSED_PONGS = 2;
|
|
143053
143621
|
var SUBSCRIBE_TIMEOUT_MS = 1e4;
|
|
@@ -143184,9 +143752,10 @@ var runWecomClient = (input) => {
|
|
|
143184
143752
|
const message = parseWecomMessage(frame, input.botId);
|
|
143185
143753
|
if (message !== null) input.handlers.onMessage(message);
|
|
143186
143754
|
};
|
|
143187
|
-
const finish = () => {
|
|
143755
|
+
const finish = (error = new Error("WeCom connection closed"), reason = "disconnected") => {
|
|
143188
143756
|
if (finished) return;
|
|
143189
143757
|
finished = true;
|
|
143758
|
+
if (!closed) input.handlers.onConnectionState?.({ state: "failed", error, reason });
|
|
143190
143759
|
clearInterval(heartbeat);
|
|
143191
143760
|
openStreams.clear();
|
|
143192
143761
|
void input.socket.close().catch(() => void 0);
|
|
@@ -143204,6 +143773,7 @@ var runWecomClient = (input) => {
|
|
|
143204
143773
|
if (!closed) {
|
|
143205
143774
|
input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143206
143775
|
}
|
|
143776
|
+
finish(error);
|
|
143207
143777
|
}
|
|
143208
143778
|
finish();
|
|
143209
143779
|
};
|
|
@@ -143211,7 +143781,7 @@ var runWecomClient = (input) => {
|
|
|
143211
143781
|
if (finished) return;
|
|
143212
143782
|
if (missedPong >= MAX_MISSED_PONGS) {
|
|
143213
143783
|
input.handlers.onError("socket hang up: missed pong");
|
|
143214
|
-
finish();
|
|
143784
|
+
finish(new Error("socket hang up: missed pong"));
|
|
143215
143785
|
return;
|
|
143216
143786
|
}
|
|
143217
143787
|
missedPong += 1;
|
|
@@ -143232,10 +143802,13 @@ var runWecomClient = (input) => {
|
|
|
143232
143802
|
settleAck(subscribeId, error instanceof Error ? error : new Error(String(error)));
|
|
143233
143803
|
});
|
|
143234
143804
|
const ready = subscribed.then(
|
|
143235
|
-
() =>
|
|
143805
|
+
() => {
|
|
143806
|
+
if (!finished && !closed) input.handlers.onConnectionState?.({ state: "connected" });
|
|
143807
|
+
return !finished && !closed;
|
|
143808
|
+
},
|
|
143236
143809
|
(error) => {
|
|
143237
143810
|
if (!closed) input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143238
|
-
finish();
|
|
143811
|
+
finish(error, "subscription-failed");
|
|
143239
143812
|
return false;
|
|
143240
143813
|
}
|
|
143241
143814
|
);
|
|
@@ -143370,7 +143943,7 @@ var runWecomClient = (input) => {
|
|
|
143370
143943
|
};
|
|
143371
143944
|
|
|
143372
143945
|
// src/wecomMedia.ts
|
|
143373
|
-
import * as
|
|
143946
|
+
import * as NodeCrypto7 from "node:crypto";
|
|
143374
143947
|
|
|
143375
143948
|
// src/wecomPadding.ts
|
|
143376
143949
|
var PADDING_BLOCK = 32;
|
|
@@ -143401,7 +143974,7 @@ var decryptWecomMedia = (cipher, aesKey) => {
|
|
|
143401
143974
|
if (cipher.byteLength === 0 || cipher.byteLength % AES_BLOCK !== 0) {
|
|
143402
143975
|
throw new WecomDecryptError("the ciphertext is not a whole number of blocks");
|
|
143403
143976
|
}
|
|
143404
|
-
const decipher =
|
|
143977
|
+
const decipher = NodeCrypto7.createDecipheriv("aes-256-cbc", key, key.subarray(0, IV_BYTES));
|
|
143405
143978
|
decipher.setAutoPadding(false);
|
|
143406
143979
|
const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
|
|
143407
143980
|
const unpadded = stripPadding(plain);
|
|
@@ -143544,6 +144117,9 @@ ${kept}`;
|
|
|
143544
144117
|
botSecret,
|
|
143545
144118
|
handlers: {
|
|
143546
144119
|
onError: handlers.onError,
|
|
144120
|
+
onConnectionState: (state) => {
|
|
144121
|
+
if (!stopped) handlers.onConnectionState?.(state);
|
|
144122
|
+
},
|
|
143547
144123
|
onMessage: (message) => {
|
|
143548
144124
|
if (message.chatType !== "single") return;
|
|
143549
144125
|
client.rememberRequest(message.chatId, message.requestId);
|
|
@@ -143585,6 +144161,8 @@ ${kept}`;
|
|
|
143585
144161
|
try {
|
|
143586
144162
|
if (await runOnce()) backoff = BACKOFF_START_MS2;
|
|
143587
144163
|
} catch (error) {
|
|
144164
|
+
if (stopped) return;
|
|
144165
|
+
handlers.onConnectionState?.({ state: "failed", error, reason: "disconnected" });
|
|
143588
144166
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143589
144167
|
}
|
|
143590
144168
|
if (stopped) return;
|
|
@@ -143602,6 +144180,7 @@ ${kept}`;
|
|
|
143602
144180
|
}
|
|
143603
144181
|
};
|
|
143604
144182
|
},
|
|
144183
|
+
// 不看 `options.retry`:长连接的回执没有核实过的限流码,也没有幂等键,失败一律说不清发没发出去。
|
|
143605
144184
|
sendText: async (binding, text4) => {
|
|
143606
144185
|
const client = clients.get(binding.rowId);
|
|
143607
144186
|
if (client === void 0) {
|
|
@@ -143618,12 +144197,36 @@ ${kept}`;
|
|
|
143618
144197
|
detail: "missing bot id or secret in saved config"
|
|
143619
144198
|
};
|
|
143620
144199
|
}
|
|
144200
|
+
let stage = "websocket";
|
|
143621
144201
|
try {
|
|
143622
144202
|
const socket = await deps.connectSocket(endpoint);
|
|
143623
|
-
|
|
143624
|
-
|
|
144203
|
+
stage = "aibot_subscribe";
|
|
144204
|
+
let detail = "WeCom connection closed before subscribe ack";
|
|
144205
|
+
let closing;
|
|
144206
|
+
const client = runWecomClient({
|
|
144207
|
+
socket: {
|
|
144208
|
+
send: (data) => socket.send(data),
|
|
144209
|
+
messages: socket.messages,
|
|
144210
|
+
close: () => closing ??= socket.close()
|
|
144211
|
+
},
|
|
144212
|
+
botId,
|
|
144213
|
+
botSecret,
|
|
144214
|
+
handlers: {
|
|
144215
|
+
onMessage: () => void 0,
|
|
144216
|
+
onError: (error) => {
|
|
144217
|
+
detail = error;
|
|
144218
|
+
}
|
|
144219
|
+
}
|
|
144220
|
+
});
|
|
144221
|
+
try {
|
|
144222
|
+
const ok = await client.ready;
|
|
144223
|
+
return { stage, ok, detail: ok ? `subscription confirmed (${endpoint})` : detail };
|
|
144224
|
+
} finally {
|
|
144225
|
+
client.close();
|
|
144226
|
+
await closing;
|
|
144227
|
+
}
|
|
143625
144228
|
} catch (error) {
|
|
143626
|
-
return { stage
|
|
144229
|
+
return { stage, ok: false, detail: describeError(error) };
|
|
143627
144230
|
}
|
|
143628
144231
|
},
|
|
143629
144232
|
askApproval: async (binding, request) => {
|
|
@@ -143680,18 +144283,25 @@ ${kept}`;
|
|
|
143680
144283
|
// src/wecomAppApi.ts
|
|
143681
144284
|
var WECOM_APP_DEFAULT_API_BASE = "https://qyapi.weixin.qq.com";
|
|
143682
144285
|
var WECOM_APP_TEXT_MAX_BYTES = 2048;
|
|
144286
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
144287
|
+
var MEDIA_TIMEOUT_MS = 6e4;
|
|
143683
144288
|
var WecomAppApiError = class extends Error {
|
|
143684
144289
|
code;
|
|
143685
144290
|
providerCode;
|
|
143686
144291
|
hint;
|
|
144292
|
+
invalidUsers;
|
|
143687
144293
|
constructor(code2, message, extras) {
|
|
143688
144294
|
super(message);
|
|
143689
144295
|
this.name = "WecomAppApiError";
|
|
143690
144296
|
this.code = code2;
|
|
143691
144297
|
this.providerCode = extras?.providerCode;
|
|
143692
144298
|
this.hint = extras?.hint;
|
|
144299
|
+
this.invalidUsers = extras?.invalidUsers ?? [];
|
|
143693
144300
|
}
|
|
143694
144301
|
};
|
|
144302
|
+
var TOKEN_INVALID_CODES = /* @__PURE__ */ new Set(["40014", "42001"]);
|
|
144303
|
+
var RATE_LIMITED_CODES = /* @__PURE__ */ new Set(["45009", "45033"]);
|
|
144304
|
+
var wecomAppSendVerdict = (error) => error instanceof WecomAppApiError && error.providerCode !== void 0 && (RATE_LIMITED_CODES.has(error.providerCode) || TOKEN_INVALID_CODES.has(error.providerCode)) ? { retry: true } : NO_RETRY;
|
|
143695
144305
|
var cleanString2 = (value2) => {
|
|
143696
144306
|
if (typeof value2 !== "string") return null;
|
|
143697
144307
|
const trimmed = value2.trim();
|
|
@@ -143727,6 +144337,7 @@ var splitUtf8ByBytes = (text4, maxBytes = WECOM_APP_TEXT_MAX_BYTES) => {
|
|
|
143727
144337
|
if (current.length > 0) chunks.push(current);
|
|
143728
144338
|
return chunks;
|
|
143729
144339
|
};
|
|
144340
|
+
var parseInvalidUsers = (value2) => typeof value2 === "string" ? value2.split("|").map((user) => user.trim()).filter(Boolean) : [];
|
|
143730
144341
|
var WecomAppApi = class {
|
|
143731
144342
|
corpId;
|
|
143732
144343
|
corpSecret;
|
|
@@ -143778,9 +144389,13 @@ var WecomAppApi = class {
|
|
|
143778
144389
|
async sendText(input) {
|
|
143779
144390
|
const chunks = splitUtf8ByBytes(input.content);
|
|
143780
144391
|
for (const chunk of chunks) {
|
|
143781
|
-
await
|
|
143782
|
-
|
|
143783
|
-
|
|
144392
|
+
await sendWithRetry(
|
|
144393
|
+
() => this.#send(
|
|
144394
|
+
{ touser: input.userId, msgtype: "text", text: { content: chunk } },
|
|
144395
|
+
input.signal
|
|
144396
|
+
),
|
|
144397
|
+
wecomAppSendVerdict,
|
|
144398
|
+
input.retry
|
|
143784
144399
|
);
|
|
143785
144400
|
}
|
|
143786
144401
|
}
|
|
@@ -143807,12 +144422,16 @@ var WecomAppApi = class {
|
|
|
143807
144422
|
new Blob([new Uint8Array(input.bytes)], { type: input.mimeType }),
|
|
143808
144423
|
input.name
|
|
143809
144424
|
);
|
|
143810
|
-
const payload = await this.#requestJson(
|
|
143811
|
-
|
|
143812
|
-
|
|
143813
|
-
|
|
143814
|
-
|
|
143815
|
-
|
|
144425
|
+
const payload = await this.#requestJson(
|
|
144426
|
+
url,
|
|
144427
|
+
{
|
|
144428
|
+
method: "POST",
|
|
144429
|
+
// 不设 content-type:multipart 的 boundary 要让 fetch 自己写。
|
|
144430
|
+
body: form,
|
|
144431
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
144432
|
+
},
|
|
144433
|
+
MEDIA_TIMEOUT_MS
|
|
144434
|
+
);
|
|
143816
144435
|
const mediaId = payload["media_id"];
|
|
143817
144436
|
if (typeof mediaId === "string" && mediaId !== "") return mediaId;
|
|
143818
144437
|
throw this.#apiError(Number(payload["errcode"] ?? -1), payload["errmsg"]);
|
|
@@ -143826,10 +144445,17 @@ var WecomAppApi = class {
|
|
|
143826
144445
|
const url = new URL("/cgi-bin/media/get", this.apiBaseUrl);
|
|
143827
144446
|
url.searchParams.set("access_token", token);
|
|
143828
144447
|
url.searchParams.set("media_id", input.mediaId);
|
|
143829
|
-
|
|
143830
|
-
|
|
143831
|
-
|
|
143832
|
-
|
|
144448
|
+
return this.#withRequest(
|
|
144449
|
+
url,
|
|
144450
|
+
{
|
|
144451
|
+
method: "GET",
|
|
144452
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
144453
|
+
},
|
|
144454
|
+
MEDIA_TIMEOUT_MS,
|
|
144455
|
+
(response) => this.#readMedia(response, input.maxBytes)
|
|
144456
|
+
);
|
|
144457
|
+
}
|
|
144458
|
+
async #readMedia(response, maxBytes) {
|
|
143833
144459
|
const contentType = response.headers.get("content-type") ?? "";
|
|
143834
144460
|
if (contentType.includes("application/json") || contentType.includes("text/plain")) {
|
|
143835
144461
|
let payload = {};
|
|
@@ -143849,7 +144475,7 @@ var WecomAppApi = class {
|
|
|
143849
144475
|
`WeCom media/get failed (HTTP ${String(response.status)}).`
|
|
143850
144476
|
);
|
|
143851
144477
|
}
|
|
143852
|
-
const bytes = await readBodyLimited(response,
|
|
144478
|
+
const bytes = await readBodyLimited(response, maxBytes);
|
|
143853
144479
|
const name = filenameFromContentDisposition(response.headers.get("content-disposition"));
|
|
143854
144480
|
const mimeType = contentType.split(";")[0]?.trim();
|
|
143855
144481
|
return {
|
|
@@ -143868,55 +144494,76 @@ var WecomAppApi = class {
|
|
|
143868
144494
|
body: JSON.stringify({ ...message, agentid: Number.parseInt(this.agentId, 10) }),
|
|
143869
144495
|
...signal === void 0 ? {} : { signal }
|
|
143870
144496
|
});
|
|
143871
|
-
const errcode = Number(payload["errcode"] ?? 0);
|
|
143872
|
-
if (errcode !== 0) throw this.#apiError(errcode, payload["errmsg"]);
|
|
143873
144497
|
const invalid = payload["invaliduser"];
|
|
143874
144498
|
if (typeof invalid === "string" && invalid.trim() !== "") {
|
|
143875
144499
|
throw new WecomAppApiError("invalid-user", `WeCom did not deliver to: ${invalid.trim()}.`, {
|
|
143876
144500
|
providerCode: "81013",
|
|
143877
|
-
hint: "
|
|
144501
|
+
hint: "wecomInvalidUser",
|
|
144502
|
+
invalidUsers: parseInvalidUsers(invalid)
|
|
143878
144503
|
});
|
|
143879
144504
|
}
|
|
143880
144505
|
}
|
|
143881
|
-
#apiError(errcode, errmsg) {
|
|
144506
|
+
#apiError(errcode, errmsg, invalidUsers) {
|
|
143882
144507
|
const code2 = String(errcode);
|
|
143883
144508
|
const message = typeof errmsg === "string" && errmsg !== "" ? `WeCom API failed (${code2}): ${errmsg}` : `WeCom API failed (${code2}).`;
|
|
143884
|
-
if (
|
|
144509
|
+
if (TOKEN_INVALID_CODES.has(code2)) this.invalidateToken();
|
|
143885
144510
|
if (errcode === 60020) {
|
|
143886
144511
|
return new WecomAppApiError("trusted-ip", "WeCom rejected the outbound IP (errcode 60020).", {
|
|
143887
144512
|
providerCode: code2,
|
|
143888
|
-
hint: "
|
|
144513
|
+
hint: "wecomTrustedIp"
|
|
143889
144514
|
});
|
|
143890
144515
|
}
|
|
143891
144516
|
if (errcode === 81013) {
|
|
143892
144517
|
return new WecomAppApiError("invalid-user", message, {
|
|
143893
144518
|
providerCode: code2,
|
|
143894
|
-
hint: "
|
|
144519
|
+
hint: "wecomInvalidUser",
|
|
144520
|
+
invalidUsers: parseInvalidUsers(invalidUsers)
|
|
143895
144521
|
});
|
|
143896
144522
|
}
|
|
143897
144523
|
if (errcode === 40056) {
|
|
143898
144524
|
return new WecomAppApiError("invalid-agent", message, {
|
|
143899
144525
|
providerCode: code2,
|
|
143900
|
-
hint: "
|
|
144526
|
+
hint: "wecomInvalidAgent"
|
|
143901
144527
|
});
|
|
143902
144528
|
}
|
|
143903
144529
|
return new WecomAppApiError("api-failed", message, { providerCode: code2 });
|
|
143904
144530
|
}
|
|
143905
|
-
async #
|
|
143906
|
-
const
|
|
143907
|
-
|
|
144531
|
+
async #withRequest(url, init, timeoutMs, read) {
|
|
144532
|
+
const deadline = new AbortController();
|
|
144533
|
+
const signal = AbortSignal.any([deadline.signal, ...init.signal ? [init.signal] : []]);
|
|
144534
|
+
const timeout = new WecomAppApiError(
|
|
144535
|
+
"timeout",
|
|
144536
|
+
`WeCom ${url.pathname} timed out after ${timeoutMs}ms.`
|
|
144537
|
+
);
|
|
144538
|
+
const timer = setTimeout(() => deadline.abort(timeout), timeoutMs);
|
|
143908
144539
|
try {
|
|
143909
|
-
|
|
143910
|
-
} catch {
|
|
143911
|
-
throw
|
|
143912
|
-
|
|
143913
|
-
|
|
143914
|
-
);
|
|
143915
|
-
}
|
|
143916
|
-
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
143917
|
-
throw new WecomAppApiError("bad-response", "WeCom returned a non-object JSON body.");
|
|
144540
|
+
return await read(await this.#fetch(url, { ...init, signal }));
|
|
144541
|
+
} catch (error) {
|
|
144542
|
+
if (signal.aborted) throw signal.reason;
|
|
144543
|
+
throw error;
|
|
144544
|
+
} finally {
|
|
144545
|
+
clearTimeout(timer);
|
|
143918
144546
|
}
|
|
143919
|
-
|
|
144547
|
+
}
|
|
144548
|
+
async #requestJson(url, init, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
144549
|
+
return this.#withRequest(url, init, timeoutMs, async (response) => {
|
|
144550
|
+
let payload;
|
|
144551
|
+
try {
|
|
144552
|
+
payload = await response.json();
|
|
144553
|
+
} catch {
|
|
144554
|
+
throw new WecomAppApiError(
|
|
144555
|
+
"bad-response",
|
|
144556
|
+
`WeCom returned non-JSON (HTTP ${String(response.status)}).`
|
|
144557
|
+
);
|
|
144558
|
+
}
|
|
144559
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
144560
|
+
throw new WecomAppApiError("bad-response", "WeCom returned a non-object JSON body.");
|
|
144561
|
+
}
|
|
144562
|
+
const result = payload;
|
|
144563
|
+
const errcode = Number(result["errcode"] ?? 0);
|
|
144564
|
+
if (errcode !== 0) throw this.#apiError(errcode, result["errmsg"], result["invaliduser"]);
|
|
144565
|
+
return result;
|
|
144566
|
+
});
|
|
143920
144567
|
}
|
|
143921
144568
|
};
|
|
143922
144569
|
|
|
@@ -143925,7 +144572,7 @@ import * as NodeHttp2 from "node:http";
|
|
|
143925
144572
|
import * as NodeNet from "node:net";
|
|
143926
144573
|
|
|
143927
144574
|
// src/wecomAppCrypto.ts
|
|
143928
|
-
import * as
|
|
144575
|
+
import * as NodeCrypto8 from "node:crypto";
|
|
143929
144576
|
var AES_KEY_BYTES = 32;
|
|
143930
144577
|
var RANDOM_BYTES = 16;
|
|
143931
144578
|
var WecomAppCryptoError = class extends Error {
|
|
@@ -143948,13 +144595,13 @@ var timingSafeEqualString2 = (a, b) => {
|
|
|
143948
144595
|
const left = Buffer.from(a);
|
|
143949
144596
|
const right = Buffer.from(b);
|
|
143950
144597
|
if (left.length === 0 || left.length !== right.length) return false;
|
|
143951
|
-
return
|
|
144598
|
+
return NodeCrypto8.timingSafeEqual(left, right);
|
|
143952
144599
|
};
|
|
143953
|
-
var computeSignature = (token, timestamp, nonce, encrypt) =>
|
|
144600
|
+
var computeSignature = (token, timestamp, nonce, encrypt) => NodeCrypto8.createHash("sha1").update([token, timestamp, nonce, encrypt].sort().join("")).digest("hex");
|
|
143954
144601
|
var verifySignature = (token, timestamp, nonce, encrypt, signature) => timingSafeEqualString2(computeSignature(token, timestamp, nonce, encrypt), signature);
|
|
143955
144602
|
var decrypt = (encodingAESKey, encryptText, expectReceiveId) => {
|
|
143956
144603
|
const { key, iv } = parseEncodingAESKey(encodingAESKey);
|
|
143957
|
-
const decipher =
|
|
144604
|
+
const decipher = NodeCrypto8.createDecipheriv("aes-256-cbc", key, iv);
|
|
143958
144605
|
decipher.setAutoPadding(false);
|
|
143959
144606
|
let deciphered;
|
|
143960
144607
|
try {
|
|
@@ -144013,10 +144660,10 @@ var createWecomAppCrypto = (input) => {
|
|
|
144013
144660
|
decrypt: (encryptText) => decrypt(encodingAESKey, encryptText, corpId).message
|
|
144014
144661
|
};
|
|
144015
144662
|
};
|
|
144016
|
-
var deriveCallbackPathSecret = (token, encodingAESKey) =>
|
|
144663
|
+
var deriveCallbackPathSecret = (token, encodingAESKey) => NodeCrypto8.createHash("sha256").update(`${token.trim()}:${encodingAESKey.trim()}`).digest("hex").slice(0, 24);
|
|
144017
144664
|
|
|
144018
144665
|
// src/wecomAppParse.ts
|
|
144019
|
-
var
|
|
144666
|
+
var isRecord5 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
144020
144667
|
var isXmlFormat = (raw) => {
|
|
144021
144668
|
const trimmed = raw.trim();
|
|
144022
144669
|
return trimmed.startsWith("<") && trimmed.endsWith(">");
|
|
@@ -144062,7 +144709,7 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144062
144709
|
} catch {
|
|
144063
144710
|
return null;
|
|
144064
144711
|
}
|
|
144065
|
-
if (!
|
|
144712
|
+
if (!isRecord5(data)) return null;
|
|
144066
144713
|
const fromRaw = data["from"];
|
|
144067
144714
|
const textRaw = data["text"];
|
|
144068
144715
|
const imageRaw = data["image"];
|
|
@@ -144071,10 +144718,10 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144071
144718
|
msgtype: String(data["msgtype"] ?? data["MsgType"] ?? "").toLowerCase(),
|
|
144072
144719
|
createTime: data["createTime"] !== void 0 ? String(data["createTime"]) : data["CreateTime"] !== void 0 ? String(data["CreateTime"]) : void 0,
|
|
144073
144720
|
agentId: data["agentid"] !== void 0 ? String(data["agentid"]) : data["AgentID"] !== void 0 ? String(data["AgentID"]) : void 0,
|
|
144074
|
-
from:
|
|
144721
|
+
from: isRecord5(fromRaw) ? { userid: typeof fromRaw["userid"] === "string" ? fromRaw["userid"] : void 0 } : typeof data["FromUserName"] === "string" ? { userid: data["FromUserName"] } : void 0,
|
|
144075
144722
|
to: typeof data["to"] === "string" ? data["to"] : void 0,
|
|
144076
|
-
text:
|
|
144077
|
-
image:
|
|
144723
|
+
text: isRecord5(textRaw) ? { content: typeof textRaw["content"] === "string" ? textRaw["content"] : void 0 } : void 0,
|
|
144724
|
+
image: isRecord5(imageRaw) ? { url: typeof imageRaw["url"] === "string" ? imageRaw["url"] : void 0 } : void 0,
|
|
144078
144725
|
mediaId: typeof data["mediaid"] === "string" ? data["mediaid"] : void 0,
|
|
144079
144726
|
recognition: typeof data["recognition"] === "string" ? data["recognition"] : void 0,
|
|
144080
144727
|
event: String(data["event"] ?? data["eventtype"] ?? "").toLowerCase() || void 0,
|
|
@@ -144460,6 +145107,7 @@ var WECOM_APP_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
|
144460
145107
|
var WECOM_APP_FILE_BYTES = 20 * 1024 * 1024;
|
|
144461
145108
|
var WECOM_APP_MIN_BYTES = 5;
|
|
144462
145109
|
var WECOM_APP_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png"]);
|
|
145110
|
+
var CREDENTIAL_ERROR_CODES = /* @__PURE__ */ new Set(["40001", "40013", "40014", "40056", "42001", "60020"]);
|
|
144463
145111
|
var joinCallbackUrl = (baseOrOrigin, path) => `${baseOrOrigin}${path}`;
|
|
144464
145112
|
var wecomAppPublicFields = (bot) => {
|
|
144465
145113
|
const c = wecomAppCredentials(bot);
|
|
@@ -144485,6 +145133,8 @@ var OUTBOUND_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
144485
145133
|
"wecomAppSecret",
|
|
144486
145134
|
"wecomAppApiBaseUrl"
|
|
144487
145135
|
]);
|
|
145136
|
+
var PROBE_BACKOFF_START_MS = 1e3;
|
|
145137
|
+
var PROBE_BACKOFF_MAX_MS = 6e4;
|
|
144488
145138
|
var makeWecomAppAdapter = (deps) => {
|
|
144489
145139
|
const apis = /* @__PURE__ */ new Map();
|
|
144490
145140
|
const newApi = (c) => new WecomAppApi({
|
|
@@ -144495,17 +145145,30 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144495
145145
|
fetch: deps.fetch
|
|
144496
145146
|
});
|
|
144497
145147
|
const callbackHub = makeWecomAppCallbackHub();
|
|
144498
|
-
const
|
|
145148
|
+
const connectionFor = (binding) => {
|
|
144499
145149
|
const api = apis.get(binding.rowId);
|
|
144500
145150
|
if (api === void 0) {
|
|
144501
145151
|
throw new ImNotConnectedError(`wecom-app bot '${binding.displayName}' is not connected.`);
|
|
144502
145152
|
}
|
|
144503
145153
|
return api;
|
|
144504
145154
|
};
|
|
145155
|
+
const deliver = async (binding, send) => {
|
|
145156
|
+
const connection = connectionFor(binding);
|
|
145157
|
+
try {
|
|
145158
|
+
await send(connection.api);
|
|
145159
|
+
connection.report({ state: "connected" });
|
|
145160
|
+
} catch (error) {
|
|
145161
|
+
if (error instanceof WecomAppApiError && error.providerCode !== void 0 && CREDENTIAL_ERROR_CODES.has(error.providerCode)) {
|
|
145162
|
+
connection.report({ state: "failed", error, reason: "credentials-rejected" });
|
|
145163
|
+
}
|
|
145164
|
+
throw error;
|
|
145165
|
+
}
|
|
145166
|
+
};
|
|
144505
145167
|
const sendMedia = async (binding, mediaType, file) => {
|
|
144506
|
-
|
|
144507
|
-
|
|
144508
|
-
|
|
145168
|
+
await deliver(binding, async (api) => {
|
|
145169
|
+
const mediaId = await api.uploadMedia({ mediaType, ...file });
|
|
145170
|
+
await api.sendMedia({ userId: binding.chatId, mediaType, mediaId });
|
|
145171
|
+
});
|
|
144509
145172
|
deps.log.info(`wecom-app delivered ${mediaType === "image" ? "an image" : "a file"}`, {
|
|
144510
145173
|
name: file.name
|
|
144511
145174
|
});
|
|
@@ -144556,6 +145219,19 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144556
145219
|
(issue) => issue.code !== "required" && issue.column !== "wecomAppEncodingAESKey"
|
|
144557
145220
|
),
|
|
144558
145221
|
publicFields: wecomAppPublicFields,
|
|
145222
|
+
// 收件人不在应用可见范围(invaliduser / 81013):说清是谁、为什么、怎么办。
|
|
145223
|
+
describeDeliveryRejection: (error, binding) => {
|
|
145224
|
+
if (!(error instanceof WecomAppApiError) || error.code !== "invalid-user") return void 0;
|
|
145225
|
+
const recipients = [
|
|
145226
|
+
...new Set(error.invalidUsers.length > 0 ? error.invalidUsers : [binding.chatId])
|
|
145227
|
+
].sort();
|
|
145228
|
+
return {
|
|
145229
|
+
recipients,
|
|
145230
|
+
summary: deps.translate("reply.wecomDeliveryFailed", { recipient: recipients.join(", ") }),
|
|
145231
|
+
hint: error.hint,
|
|
145232
|
+
providerCode: error.providerCode
|
|
145233
|
+
};
|
|
145234
|
+
},
|
|
144559
145235
|
/**
|
|
144560
145236
|
* 自建应用是回调式渠道:入站由企微推到我们的 listener,没有可探的长连接;能探的是出站
|
|
144561
145237
|
* 凭据——和真实发送同一条路,向 gettoken 换一次 access_token。
|
|
@@ -144586,7 +145262,12 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144586
145262
|
detail: "access_token ok (callback reachability is not probed)"
|
|
144587
145263
|
};
|
|
144588
145264
|
} catch (error) {
|
|
144589
|
-
return {
|
|
145265
|
+
return {
|
|
145266
|
+
stage: "gettoken",
|
|
145267
|
+
ok: false,
|
|
145268
|
+
detail: describeError(error),
|
|
145269
|
+
...error instanceof WecomAppApiError && error.hint !== void 0 ? { hint: error.hint } : {}
|
|
145270
|
+
};
|
|
144590
145271
|
}
|
|
144591
145272
|
},
|
|
144592
145273
|
connect: async (bot, handlers) => {
|
|
@@ -144600,7 +145281,14 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144600
145281
|
const lease = await callbackHub.acquire({ host: config2.listenHost, port: config2.listenPort });
|
|
144601
145282
|
const server = lease.server;
|
|
144602
145283
|
const api = newApi(c);
|
|
144603
|
-
|
|
145284
|
+
let closed = false;
|
|
145285
|
+
const report = (state) => {
|
|
145286
|
+
if (closed) return;
|
|
145287
|
+
handlers.onConnectionState?.(
|
|
145288
|
+
state.state === "failed" && state.error instanceof WecomAppApiError && state.error.hint !== void 0 ? { ...state, hint: state.error.hint } : state
|
|
145289
|
+
);
|
|
145290
|
+
};
|
|
145291
|
+
apis.set(bot.rowId, { api, report });
|
|
144604
145292
|
let path;
|
|
144605
145293
|
try {
|
|
144606
145294
|
path = server.register({
|
|
@@ -144616,7 +145304,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144616
145304
|
await lease.release();
|
|
144617
145305
|
throw error;
|
|
144618
145306
|
}
|
|
144619
|
-
deps.log.info("wecom-app
|
|
145307
|
+
deps.log.info("wecom-app callback listening", {
|
|
144620
145308
|
bot: bot.displayName,
|
|
144621
145309
|
callbackPath: path,
|
|
144622
145310
|
callbackUrl: joinCallbackUrl(
|
|
@@ -144625,8 +145313,26 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144625
145313
|
),
|
|
144626
145314
|
listen: `${server.host}:${String(server.port)}`
|
|
144627
145315
|
});
|
|
145316
|
+
const probe2 = new AbortController();
|
|
145317
|
+
void (async () => {
|
|
145318
|
+
let backoff = PROBE_BACKOFF_START_MS;
|
|
145319
|
+
while (!probe2.signal.aborted) {
|
|
145320
|
+
try {
|
|
145321
|
+
await api.getAccessToken(probe2.signal);
|
|
145322
|
+
report({ state: "connected" });
|
|
145323
|
+
return;
|
|
145324
|
+
} catch (error) {
|
|
145325
|
+
if (probe2.signal.aborted) return;
|
|
145326
|
+
report({ state: "failed", error, reason: "token-unavailable" });
|
|
145327
|
+
await (deps.sleep ?? abortableSleep)(backoff, probe2.signal).catch(() => void 0);
|
|
145328
|
+
backoff = Math.min(backoff * 2, PROBE_BACKOFF_MAX_MS);
|
|
145329
|
+
}
|
|
145330
|
+
}
|
|
145331
|
+
})();
|
|
144628
145332
|
return {
|
|
144629
145333
|
close: () => {
|
|
145334
|
+
closed = true;
|
|
145335
|
+
probe2.abort();
|
|
144630
145336
|
server.unregister(bot.rowId);
|
|
144631
145337
|
apis.delete(bot.rowId);
|
|
144632
145338
|
void lease.release().catch((error) => {
|
|
@@ -144642,15 +145348,18 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144642
145348
|
if (options?.targetKind === "group") {
|
|
144643
145349
|
throw new Error("wecom-app can only deliver to a user; group targets are not supported.");
|
|
144644
145350
|
}
|
|
144645
|
-
await
|
|
145351
|
+
await deliver(
|
|
145352
|
+
binding,
|
|
145353
|
+
(api) => api.sendText({ userId: binding.chatId, content: text4, retry: options?.retry })
|
|
145354
|
+
);
|
|
144646
145355
|
},
|
|
144647
145356
|
// D376:企业微信自建应用没有表情回执也没有流式,开工时先回一句文字,结论照常另发。没有要撤的
|
|
144648
145357
|
// 东西,回 null;发不出去由核心记一条 warn,不拦这一轮。
|
|
144649
145358
|
acknowledge: async (binding) => {
|
|
144650
|
-
await
|
|
144651
|
-
|
|
144652
|
-
content: deps.translate("reply.received")
|
|
144653
|
-
|
|
145359
|
+
await deliver(
|
|
145360
|
+
binding,
|
|
145361
|
+
(api) => api.sendText({ userId: binding.chatId, content: deps.translate("reply.received") })
|
|
145362
|
+
);
|
|
144654
145363
|
return null;
|
|
144655
145364
|
},
|
|
144656
145365
|
sendImage: (binding, image2) => sendMedia(binding, "image", image2),
|
|
@@ -144661,11 +145370,16 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144661
145370
|
minBytes: WECOM_APP_MIN_BYTES
|
|
144662
145371
|
},
|
|
144663
145372
|
imageMimeTypes: WECOM_APP_IMAGE_TYPES,
|
|
144664
|
-
fetchResource: (binding, input) =>
|
|
145373
|
+
fetchResource: (binding, input) => connectionFor(binding).api.downloadMedia({
|
|
145374
|
+
mediaId: input.resource.key,
|
|
145375
|
+
maxBytes: input.maxBytes
|
|
145376
|
+
}),
|
|
144665
145377
|
askApproval: async (binding, request) => {
|
|
144666
|
-
|
|
144667
|
-
|
|
144668
|
-
|
|
145378
|
+
if (!apis.has(binding.rowId)) return null;
|
|
145379
|
+
await deliver(
|
|
145380
|
+
binding,
|
|
145381
|
+
(api) => api.sendText({ userId: binding.chatId, content: numberedApprovalText(request) })
|
|
145382
|
+
);
|
|
144669
145383
|
return null;
|
|
144670
145384
|
},
|
|
144671
145385
|
answerByText: (_binding, text4, options) => answerByNumber(text4, options)
|
|
@@ -144673,10 +145387,16 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144673
145387
|
};
|
|
144674
145388
|
|
|
144675
145389
|
// src/index.ts
|
|
144676
|
-
var
|
|
145390
|
+
var definition = {
|
|
144677
145391
|
id: "im",
|
|
144678
|
-
apiVersion:
|
|
144679
|
-
activate: async (ctx) => {
|
|
145392
|
+
apiVersion: 19,
|
|
145393
|
+
activate: async (ctx, inboundIo) => {
|
|
145394
|
+
const inboundState = makeInboundStore({
|
|
145395
|
+
dataDir: ctx.paths.dataDir,
|
|
145396
|
+
log: ctx.log,
|
|
145397
|
+
io: inboundIo
|
|
145398
|
+
});
|
|
145399
|
+
ctx.onDispose(inboundState.flush);
|
|
144680
145400
|
const { t, setLanguage } = makeLiveTranslate(
|
|
144681
145401
|
parseReplyLanguage(ctx.config[REPLY_LANGUAGE_FIELD])
|
|
144682
145402
|
);
|
|
@@ -144698,6 +145418,8 @@ var imPlugin = definePlugin({
|
|
|
144698
145418
|
// Telegram 在不少网络里要绕代理,而绕不绕、绕哪个是按行配的 —— 这是插件面上
|
|
144699
145419
|
// `ctx.proxies` 的第一个真实消费者。
|
|
144700
145420
|
fetchVia: (proxy) => ctx.proxies.fetch(proxy),
|
|
145421
|
+
readCursor: inboundState.readCursor,
|
|
145422
|
+
saveCursor: inboundState.saveCursor,
|
|
144701
145423
|
translate: t,
|
|
144702
145424
|
log: {
|
|
144703
145425
|
info: (message, fields) => ctx.log.info(message, fields),
|
|
@@ -144736,7 +145458,6 @@ var imPlugin = definePlugin({
|
|
|
144736
145458
|
const updateSettings = makeSettingsUpdater(ctx.settings);
|
|
144737
145459
|
registerLarkSetup(ctx, { updateSettings });
|
|
144738
145460
|
registerSettingsRowActions(ctx, adapters);
|
|
144739
|
-
registerSettingsCommands(ctx, adapters, updateSettings);
|
|
144740
145461
|
const core = makeImCore({
|
|
144741
145462
|
host: {
|
|
144742
145463
|
log: ctx.log,
|
|
@@ -144755,7 +145476,8 @@ var imPlugin = definePlugin({
|
|
|
144755
145476
|
config: () => ctx.config,
|
|
144756
145477
|
derivedState: ctx.derivedState,
|
|
144757
145478
|
t,
|
|
144758
|
-
adapters
|
|
145479
|
+
adapters,
|
|
145480
|
+
inboundReady: inboundState.ready
|
|
144759
145481
|
});
|
|
144760
145482
|
const acknowledgements = makeAcknowledgements(core);
|
|
144761
145483
|
const failures = makeFailureNotices(core);
|
|
@@ -144771,7 +145493,7 @@ var imPlugin = definePlugin({
|
|
|
144771
145493
|
);
|
|
144772
145494
|
const teams = makeTeamGoals(core);
|
|
144773
145495
|
const notifications = makeNotifications(core, {
|
|
144774
|
-
|
|
145496
|
+
takeWorking: acknowledgements.takeWorking,
|
|
144775
145497
|
replyCards,
|
|
144776
145498
|
failures,
|
|
144777
145499
|
conclusions,
|
|
@@ -144806,6 +145528,7 @@ var imPlugin = definePlugin({
|
|
|
144806
145528
|
});
|
|
144807
145529
|
const chatSetup = makeChatSetup(core, {
|
|
144808
145530
|
rows: projectRows,
|
|
145531
|
+
resumeMessage: (bot, message) => inbound.resumeMessage(bot, message),
|
|
144809
145532
|
openPicker: (chatKey, rows, reply) => panelWiring.panel.openProjects(chatKey, rows, reply)
|
|
144810
145533
|
});
|
|
144811
145534
|
const commands = makeCommands(core, {
|
|
@@ -144818,10 +145541,10 @@ var imPlugin = definePlugin({
|
|
|
144818
145541
|
openPanel: (chatKey, reply) => panelWiring.panel.open(chatKey, reply)
|
|
144819
145542
|
});
|
|
144820
145543
|
const inbound = makeInbound(core, {
|
|
145544
|
+
inboundState,
|
|
144821
145545
|
enqueueForChat: chatQueue.enqueueForChat,
|
|
144822
145546
|
commands,
|
|
144823
145547
|
chatSetup,
|
|
144824
|
-
rows: projectRows,
|
|
144825
145548
|
interactions,
|
|
144826
145549
|
approvals,
|
|
144827
145550
|
panelWiring,
|
|
@@ -144847,6 +145570,7 @@ var imPlugin = definePlugin({
|
|
|
144847
145570
|
]
|
|
144848
145571
|
});
|
|
144849
145572
|
const connections = startConnections(core, inbound.inboundHandlersFor);
|
|
145573
|
+
registerSettingsCommands(ctx, adapters, updateSettings, connections);
|
|
144850
145574
|
const { bots, connecting } = connections.reconcile();
|
|
144851
145575
|
const deliveryHttp = makeDeliveryHttp({
|
|
144852
145576
|
// 每次请求按此刻的设置找目标:投递目标表改了不用重开监听。
|
|
@@ -144864,8 +145588,11 @@ var imPlugin = definePlugin({
|
|
|
144864
145588
|
}
|
|
144865
145589
|
ctx.log.info("chat plugin active", { bots, connecting });
|
|
144866
145590
|
}
|
|
144867
|
-
}
|
|
145591
|
+
};
|
|
145592
|
+
var activateImPlugin = definition.activate;
|
|
145593
|
+
var imPlugin = definePlugin(definition);
|
|
144868
145594
|
var index_default = imPlugin;
|
|
144869
145595
|
export {
|
|
145596
|
+
activateImPlugin,
|
|
144870
145597
|
index_default as default
|
|
144871
145598
|
};
|