@ycodium-ai/plugin-im 0.2.3192 → 0.2.3202
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 +912 -364
- package/locales/en.json +36 -16
- package/locales/zh-CN.json +37 -17
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -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: createHash6 } = __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 = createHash6("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: createHash6 } = __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 = createHash6("sha1").update(key + GUID).digest("base64");
|
|
24708
24708
|
const headers = [
|
|
24709
24709
|
"HTTP/1.1 101 Switching Protocols",
|
|
24710
24710
|
"Upgrade: websocket",
|
|
@@ -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 = [
|
|
@@ -127943,13 +127949,58 @@ var makeCommands = (core, deps) => {
|
|
|
127943
127949
|
};
|
|
127944
127950
|
|
|
127945
127951
|
// src/keyedQueue.ts
|
|
127946
|
-
var makeKeyedQueue = (onError) => {
|
|
127947
|
-
const
|
|
127948
|
-
|
|
127949
|
-
const
|
|
127950
|
-
|
|
127952
|
+
var makeKeyedQueue = (onError, options = {}) => {
|
|
127953
|
+
const queues = /* @__PURE__ */ new Map();
|
|
127954
|
+
const run = (key, work, kind = {}) => {
|
|
127955
|
+
const queue = queues.get(key) ?? { tail: Promise.resolve(), size: 0, droppable: 0 };
|
|
127956
|
+
const droppable = kind.droppable === true;
|
|
127957
|
+
if (droppable && options.limit !== void 0 && queue.droppable >= options.limit) {
|
|
127958
|
+
options.onFull?.(key, options.limit);
|
|
127959
|
+
return false;
|
|
127960
|
+
}
|
|
127961
|
+
queue.size += 1;
|
|
127962
|
+
if (droppable) queue.droppable += 1;
|
|
127963
|
+
queue.tail = queue.tail.then(work).catch(onError).then(() => {
|
|
127964
|
+
queue.size -= 1;
|
|
127965
|
+
if (droppable) queue.droppable -= 1;
|
|
127966
|
+
if (queue.size === 0 && queues.get(key) === queue) queues.delete(key);
|
|
127951
127967
|
});
|
|
127952
|
-
|
|
127968
|
+
queues.set(key, queue);
|
|
127969
|
+
return true;
|
|
127970
|
+
};
|
|
127971
|
+
return Object.assign(run, { activeKeys: () => queues.size });
|
|
127972
|
+
};
|
|
127973
|
+
|
|
127974
|
+
// src/redact.ts
|
|
127975
|
+
var DETAIL_MAX_CHARS = 200;
|
|
127976
|
+
var MASK = "***";
|
|
127977
|
+
var TOKEN_LIKE = /[A-Za-z0-9_=-]{24,}/g;
|
|
127978
|
+
var MIN_SECRET_LENGTH = 6;
|
|
127979
|
+
var botRedactor = (columns, values) => {
|
|
127980
|
+
const redactor = makeRedactor();
|
|
127981
|
+
for (const column of columns) {
|
|
127982
|
+
if (column.kind !== "password") continue;
|
|
127983
|
+
const value2 = values[column.key];
|
|
127984
|
+
redactor.register(value2);
|
|
127985
|
+
if (value2 !== void 0) redactor.register(encodeURIComponent(value2));
|
|
127986
|
+
}
|
|
127987
|
+
return redactor;
|
|
127988
|
+
};
|
|
127989
|
+
var makeRedactor = () => {
|
|
127990
|
+
const secrets = /* @__PURE__ */ new Set();
|
|
127991
|
+
const redact = (text4) => {
|
|
127992
|
+
let out = text4;
|
|
127993
|
+
for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {
|
|
127994
|
+
out = out.split(secret).join(MASK);
|
|
127995
|
+
}
|
|
127996
|
+
return out.replace(TOKEN_LIKE, MASK);
|
|
127997
|
+
};
|
|
127998
|
+
return {
|
|
127999
|
+
register: (secret) => {
|
|
128000
|
+
if (typeof secret === "string" && secret.length >= MIN_SECRET_LENGTH) secrets.add(secret);
|
|
128001
|
+
},
|
|
128002
|
+
redact,
|
|
128003
|
+
detail: (text4) => redact(text4).slice(0, DETAIL_MAX_CHARS)
|
|
127953
128004
|
};
|
|
127954
128005
|
};
|
|
127955
128006
|
|
|
@@ -127967,26 +128018,61 @@ var connectionKey = (adapter, bot) => JSON.stringify([
|
|
|
127967
128018
|
var startConnections = (core, inboundHandlersFor) => {
|
|
127968
128019
|
const { host, adapterFor, ready } = core;
|
|
127969
128020
|
const live = /* @__PURE__ */ new Map();
|
|
128021
|
+
const statuses = /* @__PURE__ */ new Map();
|
|
128022
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
128023
|
+
const notify = () => {
|
|
128024
|
+
const snapshot = Object.fromEntries(statuses);
|
|
128025
|
+
for (const listener of [...listeners]) listener(snapshot);
|
|
128026
|
+
};
|
|
128027
|
+
const record3 = (rowId, status) => {
|
|
128028
|
+
const previous = statuses.get(rowId);
|
|
128029
|
+
if (previous?.state === status.state && previous.error === status.error && previous.reason === status.reason && previous.duplicateOf === status.duplicateOf && previous.hint === status.hint)
|
|
128030
|
+
return;
|
|
128031
|
+
statuses.set(rowId, status);
|
|
128032
|
+
notify();
|
|
128033
|
+
};
|
|
128034
|
+
const remove = (rowId) => {
|
|
128035
|
+
if (statuses.delete(rowId)) notify();
|
|
128036
|
+
};
|
|
127970
128037
|
const enqueueForRow = makeKeyedQueue((error) => {
|
|
127971
128038
|
host.log.warn("chat could not start an inbound connection", { error: describeError(error) });
|
|
127972
128039
|
});
|
|
127973
128040
|
let disposed = false;
|
|
127974
128041
|
const open = (bot, key) => {
|
|
127975
128042
|
const adapter = adapterFor(bot.platform);
|
|
128043
|
+
const redactor = botRedactor(adapter?.columns ?? [], bot.values);
|
|
127976
128044
|
let closed = false;
|
|
127977
128045
|
let closeConnection;
|
|
128046
|
+
record3(bot.rowId, { state: "connecting" });
|
|
127978
128047
|
enqueueForRow(bot.rowId, async () => {
|
|
127979
128048
|
try {
|
|
127980
128049
|
await ready;
|
|
127981
128050
|
if (closed || adapter === void 0) return;
|
|
127982
|
-
const connection = await adapter.connect(bot,
|
|
128051
|
+
const connection = await adapter.connect(bot, {
|
|
128052
|
+
...inboundHandlersFor(bot),
|
|
128053
|
+
onConnectionState: (status) => {
|
|
128054
|
+
if (closed) return;
|
|
128055
|
+
if (status.state === "connected") {
|
|
128056
|
+
record3(bot.rowId, status);
|
|
128057
|
+
return;
|
|
128058
|
+
}
|
|
128059
|
+
record3(bot.rowId, {
|
|
128060
|
+
state: "failed",
|
|
128061
|
+
...status.reason === void 0 ? {} : { reason: status.reason },
|
|
128062
|
+
...status.error === void 0 ? {} : { error: redactor.redact(describeError(status.error)) },
|
|
128063
|
+
...status.hint === void 0 ? {} : { hint: status.hint }
|
|
128064
|
+
});
|
|
128065
|
+
}
|
|
128066
|
+
});
|
|
127983
128067
|
if (closed) connection.close();
|
|
127984
128068
|
else closeConnection = () => connection.close();
|
|
127985
128069
|
} catch (error) {
|
|
128070
|
+
const detail = redactor.redact(describeError(error));
|
|
128071
|
+
if (!closed) record3(bot.rowId, { state: "failed", error: detail });
|
|
127986
128072
|
host.log.warn("chat could not start an inbound connection", {
|
|
127987
128073
|
bot: bot.displayName,
|
|
127988
128074
|
platform: bot.platform,
|
|
127989
|
-
error:
|
|
128075
|
+
error: detail
|
|
127990
128076
|
});
|
|
127991
128077
|
}
|
|
127992
128078
|
});
|
|
@@ -128001,19 +128087,25 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128001
128087
|
const reconcile = () => {
|
|
128002
128088
|
const bots = disposed ? [] : core.bots();
|
|
128003
128089
|
const wanted = /* @__PURE__ */ new Map();
|
|
128004
|
-
const credentials3 = /* @__PURE__ */ new
|
|
128090
|
+
const credentials3 = /* @__PURE__ */ new Map();
|
|
128091
|
+
const duplicates = /* @__PURE__ */ new Map();
|
|
128005
128092
|
for (const bot of bots) {
|
|
128006
128093
|
const adapter = adapterFor(bot.platform);
|
|
128007
128094
|
if (adapter === void 0) continue;
|
|
128008
128095
|
const fingerprint = `${bot.platform}:${adapter.columns.map((column) => bot.values[column.key] ?? "").join(" ")}`;
|
|
128009
|
-
|
|
128096
|
+
const duplicateOf = credentials3.get(fingerprint);
|
|
128097
|
+
if (duplicateOf !== void 0) {
|
|
128098
|
+
duplicates.set(bot.rowId, duplicateOf);
|
|
128099
|
+
if (statuses.get(bot.rowId)?.duplicateOf !== duplicateOf) {
|
|
128100
|
+
record3(bot.rowId, { state: "duplicate", duplicateOf });
|
|
128101
|
+
}
|
|
128010
128102
|
host.log.warn("chat did not connect a bot that repeats another bot's credentials", {
|
|
128011
128103
|
bot: bot.displayName,
|
|
128012
128104
|
platform: bot.platform
|
|
128013
128105
|
});
|
|
128014
128106
|
continue;
|
|
128015
128107
|
}
|
|
128016
|
-
credentials3.
|
|
128108
|
+
credentials3.set(fingerprint, bot.rowId);
|
|
128017
128109
|
wanted.set(bot.rowId, { bot, key: connectionKey(adapter, bot) });
|
|
128018
128110
|
}
|
|
128019
128111
|
for (const [rowId, connection] of live) {
|
|
@@ -128021,6 +128113,9 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128021
128113
|
connection.close();
|
|
128022
128114
|
live.delete(rowId);
|
|
128023
128115
|
}
|
|
128116
|
+
for (const rowId of statuses.keys()) {
|
|
128117
|
+
if (!wanted.has(rowId) && !duplicates.has(rowId)) remove(rowId);
|
|
128118
|
+
}
|
|
128024
128119
|
for (const [rowId, { bot, key }] of wanted) {
|
|
128025
128120
|
if (!live.has(rowId)) live.set(rowId, open(bot, key));
|
|
128026
128121
|
}
|
|
@@ -128029,8 +128124,18 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128029
128124
|
host.onDispose(() => {
|
|
128030
128125
|
disposed = true;
|
|
128031
128126
|
reconcile();
|
|
128127
|
+
listeners.clear();
|
|
128032
128128
|
});
|
|
128033
|
-
return {
|
|
128129
|
+
return {
|
|
128130
|
+
reconcile,
|
|
128131
|
+
subscribe: (listener) => {
|
|
128132
|
+
if (!disposed) listeners.add(listener);
|
|
128133
|
+
listener(Object.fromEntries(statuses));
|
|
128134
|
+
return () => {
|
|
128135
|
+
listeners.delete(listener);
|
|
128136
|
+
};
|
|
128137
|
+
}
|
|
128138
|
+
};
|
|
128034
128139
|
};
|
|
128035
128140
|
|
|
128036
128141
|
// src/deliveryHttp.ts
|
|
@@ -128279,6 +128384,7 @@ ${token}` : void 0;
|
|
|
128279
128384
|
};
|
|
128280
128385
|
|
|
128281
128386
|
// src/core.ts
|
|
128387
|
+
var OUTBOUND_QUEUE_LIMIT = 100;
|
|
128282
128388
|
var makeImCore = (input) => {
|
|
128283
128389
|
const { host, t, adapters } = input;
|
|
128284
128390
|
const adapterFor = (platform) => adapters.find((adapter) => adapter.platform === platform);
|
|
@@ -128313,10 +128419,21 @@ var makeImCore = (input) => {
|
|
|
128313
128419
|
const bindChat = (chatKey, threadId) => {
|
|
128314
128420
|
commit("chat-updated", { chatKey, threadId });
|
|
128315
128421
|
};
|
|
128316
|
-
const outbound = makeKeyedQueue(
|
|
128317
|
-
|
|
128318
|
-
|
|
128319
|
-
|
|
128422
|
+
const outbound = makeKeyedQueue(
|
|
128423
|
+
(error) => {
|
|
128424
|
+
host.log.warn("chat could not deliver queued work", { error: describeError(error) });
|
|
128425
|
+
},
|
|
128426
|
+
{
|
|
128427
|
+
limit: OUTBOUND_QUEUE_LIMIT,
|
|
128428
|
+
onFull: (chatKey, limit) => {
|
|
128429
|
+
host.log.warn("chat queue is full; refused a conclusion until restart", {
|
|
128430
|
+
chatKey,
|
|
128431
|
+
limit
|
|
128432
|
+
});
|
|
128433
|
+
}
|
|
128434
|
+
}
|
|
128435
|
+
);
|
|
128436
|
+
const postImages = async (binding, attachments, onFailure) => {
|
|
128320
128437
|
const adapter = adapterFor(binding.platform);
|
|
128321
128438
|
if (adapter?.sendImage === void 0) return;
|
|
128322
128439
|
for (const attachment of attachments) {
|
|
@@ -128333,19 +128450,22 @@ var makeImCore = (input) => {
|
|
|
128333
128450
|
name: attachment.name,
|
|
128334
128451
|
error: describeError(error)
|
|
128335
128452
|
});
|
|
128453
|
+
await onFailure?.(error);
|
|
128336
128454
|
}
|
|
128337
128455
|
}
|
|
128338
128456
|
};
|
|
128339
|
-
const deliver = async (binding, text4, attachments = []) => {
|
|
128340
|
-
|
|
128457
|
+
const deliver = async (binding, text4, attachments = [], options = {}) => {
|
|
128458
|
+
const { retry, onFailure } = options;
|
|
128459
|
+
await postImages(binding, attachments, onFailure);
|
|
128341
128460
|
if (text4.trim() === "") return true;
|
|
128342
128461
|
const adapter = adapterFor(binding.platform);
|
|
128343
128462
|
if (adapter === void 0) return false;
|
|
128344
128463
|
try {
|
|
128345
|
-
await adapter.sendText(binding, text4);
|
|
128464
|
+
await adapter.sendText(binding, text4, { retry });
|
|
128346
128465
|
return true;
|
|
128347
128466
|
} catch (error) {
|
|
128348
128467
|
host.log.warn("chat could not deliver", { error: describeError(error) });
|
|
128468
|
+
await onFailure?.(error);
|
|
128349
128469
|
return false;
|
|
128350
128470
|
}
|
|
128351
128471
|
};
|
|
@@ -128377,6 +128497,92 @@ var makeImCore = (input) => {
|
|
|
128377
128497
|
// src/conclusionDelivery.ts
|
|
128378
128498
|
import { threadOrNull as threadOrNull3 } from "@ycodium-ai/plugin-api";
|
|
128379
128499
|
|
|
128500
|
+
// src/abortableSleep.ts
|
|
128501
|
+
var abortableSleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
128502
|
+
if (signal?.aborted === true) {
|
|
128503
|
+
reject(new Error("aborted"));
|
|
128504
|
+
return;
|
|
128505
|
+
}
|
|
128506
|
+
const onAbort = () => {
|
|
128507
|
+
clearTimeout(timer);
|
|
128508
|
+
reject(new Error("aborted"));
|
|
128509
|
+
};
|
|
128510
|
+
const timer = setTimeout(() => {
|
|
128511
|
+
signal?.removeEventListener("abort", onAbort);
|
|
128512
|
+
resolve();
|
|
128513
|
+
}, ms);
|
|
128514
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
128515
|
+
});
|
|
128516
|
+
|
|
128517
|
+
// src/deliveryFailure.ts
|
|
128518
|
+
import * as NodeCrypto2 from "node:crypto";
|
|
128519
|
+
|
|
128520
|
+
// src/diagnostics.ts
|
|
128521
|
+
var PREFIX = {
|
|
128522
|
+
conn: "IM-CONN",
|
|
128523
|
+
msg: "IM-MSG",
|
|
128524
|
+
setup: "IM-SETUP"
|
|
128525
|
+
};
|
|
128526
|
+
var shortSalt = () => {
|
|
128527
|
+
const bytes = new Uint8Array(2);
|
|
128528
|
+
crypto.getRandomValues(bytes);
|
|
128529
|
+
return Array.from(bytes, (b) => b.toString(16).toUpperCase().padStart(2, "0")).join("");
|
|
128530
|
+
};
|
|
128531
|
+
var stableDiagnosticRef = (kind, digest) => `${PREFIX[kind]}-${digest.slice(0, 16).toUpperCase()}`;
|
|
128532
|
+
var diagnosticRef = (kind, nowMs = Date.now()) => {
|
|
128533
|
+
const stamp = nowMs.toString(36).toUpperCase();
|
|
128534
|
+
return `${PREFIX[kind]}-${stamp}-${shortSalt()}`;
|
|
128535
|
+
};
|
|
128536
|
+
var formatDiagnosticCopy = (report) => [
|
|
128537
|
+
`ok=${report.ok ? "yes" : "no"}`,
|
|
128538
|
+
`platform=${report.platform}`,
|
|
128539
|
+
`bot=${report.botName}`,
|
|
128540
|
+
`row=${report.rowId}`,
|
|
128541
|
+
`stage=${report.stage}`,
|
|
128542
|
+
`at=${report.at}`,
|
|
128543
|
+
`detail=${report.detail}`
|
|
128544
|
+
].join("\n");
|
|
128545
|
+
|
|
128546
|
+
// src/deliveryFailure.ts
|
|
128547
|
+
var deliveryFailure = (core, binding, threadId, turnId) => async (error) => {
|
|
128548
|
+
const rejection = core.adapterFor(binding.platform)?.describeDeliveryRejection?.(error, binding);
|
|
128549
|
+
if (rejection === void 0) return;
|
|
128550
|
+
const { recipients } = rejection;
|
|
128551
|
+
const digest = NodeCrypto2.createHash("sha256").update(JSON.stringify([threadId, turnId, binding.platform, binding.rowId, recipients])).digest("hex");
|
|
128552
|
+
const id = `im.delivery-failed:${digest}`;
|
|
128553
|
+
const reference2 = stableDiagnosticRef("msg", digest);
|
|
128554
|
+
const payload = {
|
|
128555
|
+
platform: binding.platform,
|
|
128556
|
+
recipients,
|
|
128557
|
+
hint: rejection.hint,
|
|
128558
|
+
providerCode: rejection.providerCode,
|
|
128559
|
+
reference: reference2,
|
|
128560
|
+
sourceTurnId: turnId
|
|
128561
|
+
};
|
|
128562
|
+
core.host.log.warn("chat recipient rejected delivery", { threadId, ...payload });
|
|
128563
|
+
if (core.state.opened[threadId] === void 0) return;
|
|
128564
|
+
try {
|
|
128565
|
+
await core.host.threads.externalMessages.append({
|
|
128566
|
+
requestId: id,
|
|
128567
|
+
threadId,
|
|
128568
|
+
messages: [],
|
|
128569
|
+
activities: [
|
|
128570
|
+
{
|
|
128571
|
+
id,
|
|
128572
|
+
tone: "error",
|
|
128573
|
+
kind: "im.delivery-failed",
|
|
128574
|
+
summary: rejection.summary,
|
|
128575
|
+
payload,
|
|
128576
|
+
turnId: null,
|
|
128577
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
128578
|
+
}
|
|
128579
|
+
]
|
|
128580
|
+
});
|
|
128581
|
+
} catch {
|
|
128582
|
+
core.host.log.warn("chat could not append delivery failure notice", { threadId, reference: reference2 });
|
|
128583
|
+
}
|
|
128584
|
+
};
|
|
128585
|
+
|
|
128380
128586
|
// src/threadMessages.ts
|
|
128381
128587
|
import {
|
|
128382
128588
|
isThreadNotFound,
|
|
@@ -128428,20 +128634,30 @@ async function messagesAfterLastUser(threads, threadId) {
|
|
|
128428
128634
|
}
|
|
128429
128635
|
|
|
128430
128636
|
// src/conclusionDelivery.ts
|
|
128637
|
+
var ALWAYS_OWED = () => true;
|
|
128431
128638
|
var makeConclusionDelivery = (core, anchors) => {
|
|
128432
128639
|
let disposed = false;
|
|
128640
|
+
const stopped = new AbortController();
|
|
128433
128641
|
core.host.onDispose(() => {
|
|
128434
128642
|
disposed = true;
|
|
128643
|
+
stopped.abort();
|
|
128435
128644
|
});
|
|
128436
128645
|
const marked = (text4, recovered) => recovered ? `${core.t("reply.conclusionRecovered")}
|
|
128437
128646
|
${text4}` : text4;
|
|
128647
|
+
const sleep3 = (ms) => abortableSleep(ms, stopped.signal).catch(() => void 0);
|
|
128648
|
+
const retryFor = (threadId, owed = ALWAYS_OWED) => ({
|
|
128649
|
+
sleep: sleep3,
|
|
128650
|
+
shouldContinue: async () => !disposed && owed() && await threadOrNull3(core.host.threads, threadId) !== null
|
|
128651
|
+
});
|
|
128438
128652
|
return {
|
|
128653
|
+
retryFor,
|
|
128439
128654
|
prepareConclusion: async (threadId, turnId = null) => {
|
|
128440
128655
|
const thread = await threadOrNull3(core.host.threads, threadId);
|
|
128441
128656
|
const final = thread === null ? void 0 : await latestMessage(
|
|
128442
128657
|
core.host.threads,
|
|
128443
128658
|
threadId,
|
|
128444
|
-
|
|
128659
|
+
// 给了轮次就只取这一轮的正文:排队等着的结论不能拿到下一轮的话。没记轮次的老消息照旧认。
|
|
128660
|
+
(message) => message.role === "assistant" && message.text.trim() !== "" && (turnId === null || message.turnId === null || message.turnId === turnId)
|
|
128445
128661
|
);
|
|
128446
128662
|
const attachments = final === void 0 ? [] : await attachmentsOf(core.host.threads, threadId, final);
|
|
128447
128663
|
return async (binding, options) => {
|
|
@@ -128451,12 +128667,22 @@ ${text4}` : text4;
|
|
|
128451
128667
|
|
|
128452
128668
|
${final.text}`, options.recovered);
|
|
128453
128669
|
const anchored = await anchors.anchored(binding, threadId, turnId ?? final.turnId);
|
|
128454
|
-
return !disposed && core.deliver(anchored, text4, attachments
|
|
128670
|
+
return !disposed && core.deliver(anchored, text4, attachments, {
|
|
128671
|
+
retry: retryFor(threadId, options.owed),
|
|
128672
|
+
onFailure: deliveryFailure(core, binding, threadId, turnId ?? final.turnId)
|
|
128673
|
+
});
|
|
128455
128674
|
};
|
|
128456
128675
|
},
|
|
128457
|
-
|
|
128676
|
+
/**
|
|
128677
|
+
* 发一段已经写好的结论文字(失败通知、卡片定稿失败时的文字兜底):锚点、停用检查、重发与
|
|
128678
|
+
* 拒收提醒都在这里,调用方只管写文字。
|
|
128679
|
+
*/
|
|
128680
|
+
sendText: async (binding, threadId, notice, recovered, turnId = null, owed = ALWAYS_OWED) => {
|
|
128458
128681
|
const anchored = await anchors.anchored(binding, threadId, turnId);
|
|
128459
|
-
return !disposed && core.deliver(anchored, marked(notice, recovered)
|
|
128682
|
+
return !disposed && core.deliver(anchored, marked(notice, recovered), [], {
|
|
128683
|
+
retry: retryFor(threadId, owed),
|
|
128684
|
+
onFailure: deliveryFailure(core, binding, threadId, turnId)
|
|
128685
|
+
});
|
|
128460
128686
|
}
|
|
128461
128687
|
};
|
|
128462
128688
|
};
|
|
@@ -130583,29 +130809,6 @@ var sleep = async (deps) => {
|
|
|
130583
130809
|
}
|
|
130584
130810
|
};
|
|
130585
130811
|
|
|
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
130812
|
// src/lark/setup/cookieJar.ts
|
|
130610
130813
|
var MAX_REDIRECT_HOPS = 10;
|
|
130611
130814
|
var parseSetCookie = (responseUrl, header) => {
|
|
@@ -131323,21 +131526,7 @@ var buildChecklist = () => ({
|
|
|
131323
131526
|
});
|
|
131324
131527
|
var makeRealClock = () => ({
|
|
131325
131528
|
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
|
-
})
|
|
131529
|
+
sleep: abortableSleep
|
|
131341
131530
|
});
|
|
131342
131531
|
var saveAndConclude = async (ctx, updateSettings, result) => {
|
|
131343
131532
|
try {
|
|
@@ -131429,6 +131618,9 @@ var registerLarkSetup = (ctx, options = {}) => {
|
|
|
131429
131618
|
return runner;
|
|
131430
131619
|
};
|
|
131431
131620
|
|
|
131621
|
+
// src/larkApi.ts
|
|
131622
|
+
import { parseRetryAfterMs } from "@ycodium-ai/plugin-api";
|
|
131623
|
+
|
|
131432
131624
|
// src/larkPost.ts
|
|
131433
131625
|
var styledText = (text4, style) => {
|
|
131434
131626
|
let out = text4;
|
|
@@ -131608,6 +131800,33 @@ var parseLarkReply = (message) => {
|
|
|
131608
131800
|
}
|
|
131609
131801
|
};
|
|
131610
131802
|
|
|
131803
|
+
// src/sendRetry.ts
|
|
131804
|
+
var NO_RETRY = { retry: false };
|
|
131805
|
+
var SEND_RETRY_DELAYS_MS = [1e3, 4e3, 15e3];
|
|
131806
|
+
var MAX_WAIT_MS = 6e4;
|
|
131807
|
+
var SendStoppedError = class extends Error {
|
|
131808
|
+
constructor(cause) {
|
|
131809
|
+
super("send stopped before retrying", { cause });
|
|
131810
|
+
this.name = "SendStoppedError";
|
|
131811
|
+
}
|
|
131812
|
+
};
|
|
131813
|
+
var sendWithRetry = async (request, verdictOf, retry) => {
|
|
131814
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
131815
|
+
try {
|
|
131816
|
+
return await request();
|
|
131817
|
+
} catch (error) {
|
|
131818
|
+
const delayMs = SEND_RETRY_DELAYS_MS[attempt];
|
|
131819
|
+
if (retry === void 0 || delayMs === void 0) throw error;
|
|
131820
|
+
const verdict = verdictOf(error);
|
|
131821
|
+
if (!verdict.retry) throw error;
|
|
131822
|
+
const waitMs = verdict.afterMs ?? delayMs;
|
|
131823
|
+
if (waitMs > MAX_WAIT_MS) throw error;
|
|
131824
|
+
await retry.sleep(waitMs);
|
|
131825
|
+
if (!await retry.shouldContinue()) throw new SendStoppedError(error);
|
|
131826
|
+
}
|
|
131827
|
+
}
|
|
131828
|
+
};
|
|
131829
|
+
|
|
131611
131830
|
// src/splitMessage.ts
|
|
131612
131831
|
var splitMessage = (text4, limit) => {
|
|
131613
131832
|
if (text4.length <= limit) return [text4];
|
|
@@ -131640,19 +131859,39 @@ var LARK_TEXT_MAX_CHARS = 3500;
|
|
|
131640
131859
|
var LarkApiError = class extends Error {
|
|
131641
131860
|
operation;
|
|
131642
131861
|
code;
|
|
131643
|
-
|
|
131862
|
+
status;
|
|
131863
|
+
retryAfterMs;
|
|
131864
|
+
constructor(operation, code2, message, http = {}) {
|
|
131644
131865
|
super(`lark ${operation} failed (code ${String(code2 ?? "?")}): ${message}`);
|
|
131645
131866
|
this.name = "LarkApiError";
|
|
131646
131867
|
this.operation = operation;
|
|
131647
131868
|
this.code = code2;
|
|
131869
|
+
this.status = http.status;
|
|
131870
|
+
this.retryAfterMs = http.retryAfterMs;
|
|
131648
131871
|
}
|
|
131649
131872
|
};
|
|
131650
|
-
var isLarkPermissionError = (error) => error instanceof LarkApiError && (error.code === 99991672 || error.code === 99991679 ||
|
|
131873
|
+
var isLarkPermissionError = (error) => error instanceof LarkApiError && (error.code === 99991672 || error.code === 99991679 || error.status === 401 || error.status === 403);
|
|
131874
|
+
var LARK_RATE_LIMITED = 99991400;
|
|
131875
|
+
var isReplyRejected = (error) => error instanceof LarkApiError && error.status === void 0 && error.code !== LARK_RATE_LIMITED;
|
|
131876
|
+
var larkSendVerdict = (error) => {
|
|
131877
|
+
if (!(error instanceof LarkApiError)) return NO_RETRY;
|
|
131878
|
+
if (error.status === 429 || error.code === LARK_RATE_LIMITED) {
|
|
131879
|
+
return { retry: true, afterMs: error.retryAfterMs };
|
|
131880
|
+
}
|
|
131881
|
+
if (error.status !== void 0 && error.status >= 500) {
|
|
131882
|
+
return { retry: true, afterMs: error.retryAfterMs };
|
|
131883
|
+
}
|
|
131884
|
+
return NO_RETRY;
|
|
131885
|
+
};
|
|
131651
131886
|
var readEnvelope = async (operation, response) => {
|
|
131652
131887
|
if (!response.ok) {
|
|
131653
131888
|
await response.body?.cancel().catch(() => void 0);
|
|
131654
|
-
throw
|
|
131655
|
-
status: response.status
|
|
131889
|
+
throw new LarkApiError(operation, void 0, `HTTP ${String(response.status)}`, {
|
|
131890
|
+
status: response.status,
|
|
131891
|
+
retryAfterMs: parseRetryAfterMs(
|
|
131892
|
+
response.headers.get("retry-after") ?? response.headers.get("x-ogw-ratelimit-reset"),
|
|
131893
|
+
Date.now()
|
|
131894
|
+
) ?? void 0
|
|
131656
131895
|
});
|
|
131657
131896
|
}
|
|
131658
131897
|
const body = await response.json();
|
|
@@ -131679,35 +131918,37 @@ var postMessage = async (input) => {
|
|
|
131679
131918
|
authorization: `Bearer ${input.token}`,
|
|
131680
131919
|
"content-type": "application/json; charset=utf-8"
|
|
131681
131920
|
};
|
|
131682
|
-
const message = {
|
|
131683
|
-
|
|
131921
|
+
const message = {
|
|
131922
|
+
msg_type: input.msgType,
|
|
131923
|
+
content: JSON.stringify(input.content),
|
|
131924
|
+
...input.retry === void 0 ? {} : { uuid: crypto.randomUUID() }
|
|
131925
|
+
};
|
|
131926
|
+
const post = (url, body) => sendWithRetry(
|
|
131927
|
+
async () => (await readEnvelope(
|
|
131928
|
+
input.operation,
|
|
131929
|
+
await input.http(url, { method: "POST", headers, body: JSON.stringify(body) })
|
|
131930
|
+
)).data,
|
|
131931
|
+
larkSendVerdict,
|
|
131932
|
+
input.retry
|
|
131933
|
+
);
|
|
131684
131934
|
if (input.reply !== void 0) {
|
|
131685
131935
|
try {
|
|
131686
|
-
const
|
|
131936
|
+
const sent2 = await post(
|
|
131687
131937
|
`${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
|
-
}
|
|
131938
|
+
{ ...message, ...input.reply.inThread ? { reply_in_thread: true } : {} }
|
|
131696
131939
|
);
|
|
131697
|
-
|
|
131698
|
-
|
|
131699
|
-
return sent?.message_id ?? null;
|
|
131940
|
+
if (input.reply.inThread) input.onThreadReply?.(sent2?.thread_id || void 0);
|
|
131941
|
+
return sent2?.message_id ?? null;
|
|
131700
131942
|
} catch (error) {
|
|
131701
|
-
if (!(error
|
|
131943
|
+
if (!isReplyRejected(error)) throw error;
|
|
131702
131944
|
}
|
|
131703
131945
|
}
|
|
131704
131946
|
const idType = input.receiveIdType ?? "chat_id";
|
|
131705
|
-
const
|
|
131706
|
-
|
|
131707
|
-
|
|
131708
|
-
body: JSON.stringify({ receive_id: input.chatId, ...message })
|
|
131947
|
+
const sent = await post(`${LARK_BASE}/im/v1/messages?receive_id_type=${idType}`, {
|
|
131948
|
+
receive_id: input.chatId,
|
|
131949
|
+
...message
|
|
131709
131950
|
});
|
|
131710
|
-
return
|
|
131951
|
+
return sent?.message_id ?? null;
|
|
131711
131952
|
};
|
|
131712
131953
|
var readMessage = async (input) => {
|
|
131713
131954
|
const response = await input.http(
|
|
@@ -137310,7 +137551,7 @@ var UNSUPPORTED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
|
137310
137551
|
"share_chat",
|
|
137311
137552
|
"share_user",
|
|
137312
137553
|
"system",
|
|
137313
|
-
"
|
|
137554
|
+
"merge_forward",
|
|
137314
137555
|
"interactive"
|
|
137315
137556
|
]);
|
|
137316
137557
|
var botIdsOf = (mentions, appId) => {
|
|
@@ -137482,6 +137723,7 @@ var cardActionOperator = (event) => {
|
|
|
137482
137723
|
}
|
|
137483
137724
|
return null;
|
|
137484
137725
|
};
|
|
137726
|
+
var isLarkAppId = (appId) => /^cli_[0-9a-fA-F]{16}$/u.test(appId);
|
|
137485
137727
|
var startLarkInbound = (input) => {
|
|
137486
137728
|
const dispatcher = new import_node_sdk.EventDispatcher({}).register({
|
|
137487
137729
|
"im.message.receive_v1": (event) => {
|
|
@@ -137523,20 +137765,42 @@ var startLarkInbound = (input) => {
|
|
|
137523
137765
|
return notice === null ? {} : { toast: { type: "info", content: notice } };
|
|
137524
137766
|
}
|
|
137525
137767
|
});
|
|
137768
|
+
if (!isLarkAppId(input.appId)) {
|
|
137769
|
+
input.onConnectionState?.({ state: "failed", reason: "invalid-app-id" });
|
|
137770
|
+
return { close: () => void 0 };
|
|
137771
|
+
}
|
|
137772
|
+
let closed = false;
|
|
137773
|
+
const connected = () => {
|
|
137774
|
+
if (!closed) input.onConnectionState?.({ state: "connected" });
|
|
137775
|
+
};
|
|
137776
|
+
const failed = (error) => {
|
|
137777
|
+
if (!closed) {
|
|
137778
|
+
input.onConnectionState?.({ state: "failed", reason: "disconnected", error });
|
|
137779
|
+
}
|
|
137780
|
+
};
|
|
137526
137781
|
const client = new import_node_sdk.WSClient({
|
|
137527
137782
|
appId: input.appId,
|
|
137528
137783
|
appSecret: input.appSecret,
|
|
137529
137784
|
...input.domain === void 0 ? {} : { domain: input.domain },
|
|
137530
137785
|
autoReconnect: true,
|
|
137531
|
-
|
|
137786
|
+
onReady: connected,
|
|
137787
|
+
onReconnected: connected,
|
|
137788
|
+
onReconnecting: () => failed(),
|
|
137789
|
+
onError: (cause) => {
|
|
137790
|
+
if (closed) return;
|
|
137791
|
+
failed(cause);
|
|
137792
|
+
input.onError(cause.message);
|
|
137793
|
+
}
|
|
137532
137794
|
});
|
|
137533
|
-
void client.start({ eventDispatcher: dispatcher });
|
|
137534
|
-
return {
|
|
137795
|
+
void client.start({ eventDispatcher: dispatcher }).catch(failed);
|
|
137796
|
+
return {
|
|
137797
|
+
close: () => {
|
|
137798
|
+
closed = true;
|
|
137799
|
+
client.close({ force: true });
|
|
137800
|
+
}
|
|
137801
|
+
};
|
|
137535
137802
|
};
|
|
137536
137803
|
|
|
137537
|
-
// src/replyReference.ts
|
|
137538
|
-
import * as NodeCrypto2 from "node:crypto";
|
|
137539
|
-
|
|
137540
137804
|
// src/deadline.ts
|
|
137541
137805
|
var withDeadline2 = async (timeoutMs, load) => {
|
|
137542
137806
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
@@ -137554,6 +137818,7 @@ var withDeadline2 = async (timeoutMs, load) => {
|
|
|
137554
137818
|
};
|
|
137555
137819
|
|
|
137556
137820
|
// src/replyReference.ts
|
|
137821
|
+
import * as NodeCrypto3 from "node:crypto";
|
|
137557
137822
|
var CONTENT_MAX = 8e3;
|
|
137558
137823
|
var ATTACHMENTS_MAX = 20;
|
|
137559
137824
|
var AUTHOR_MAX = 256;
|
|
@@ -137626,15 +137891,16 @@ var formatReplyReference = (reference2) => {
|
|
|
137626
137891
|
...lines.map((line) => `> ${line}`)
|
|
137627
137892
|
].join("\n");
|
|
137628
137893
|
};
|
|
137629
|
-
var textWithInboundContext = (message, rowId, t) => {
|
|
137630
|
-
const
|
|
137631
|
-
|
|
137632
|
-
|
|
137633
|
-
|
|
137894
|
+
var textWithInboundContext = (message, rowId, t, topicRoot) => {
|
|
137895
|
+
const text4 = [
|
|
137896
|
+
formatReplyReference(topicRoot),
|
|
137897
|
+
formatReplyReference(message.replyTo),
|
|
137898
|
+
message.text
|
|
137899
|
+
].filter((part) => part !== null).join("\n\n");
|
|
137634
137900
|
if (message.chatKind !== "group" || message.senderId === null) return text4;
|
|
137635
137901
|
let name = cleanString(message.senderName, AUTHOR_MAX).value;
|
|
137636
137902
|
if (!name) {
|
|
137637
|
-
const code2 =
|
|
137903
|
+
const code2 = NodeCrypto3.createHash("sha256").update(JSON.stringify([rowId, message.senderId])).digest("hex").slice(0, 4);
|
|
137638
137904
|
name = t("source.member", { code: code2 });
|
|
137639
137905
|
}
|
|
137640
137906
|
const chat = cleanString(message.chatTitle, AUTHOR_MAX).value;
|
|
@@ -137687,6 +137953,26 @@ var makeLookup = (rowId, load) => {
|
|
|
137687
137953
|
};
|
|
137688
137954
|
var makeLarkMessageContext = (deps) => {
|
|
137689
137955
|
let contactDenied = false;
|
|
137956
|
+
const deliveredRoots = makeBoundedSeen(CACHE_LIMIT);
|
|
137957
|
+
const loadReference = (messageId) => loadReplyReference(async (signal) => {
|
|
137958
|
+
const token = await deps.tenantToken();
|
|
137959
|
+
signal.throwIfAborted();
|
|
137960
|
+
return fetchMessageReference({ http: deps.http, token, messageId, signal });
|
|
137961
|
+
});
|
|
137962
|
+
const topicRootForSession = (message) => {
|
|
137963
|
+
const topic = message.topic;
|
|
137964
|
+
if (topic === void 0 || topic.rootGiven === false) return void 0;
|
|
137965
|
+
return async (recipientId) => {
|
|
137966
|
+
const key = JSON.stringify([message.chatId, topic.rootMessageId, recipientId]);
|
|
137967
|
+
if (deliveredRoots.has(key)) return void 0;
|
|
137968
|
+
return {
|
|
137969
|
+
reference: topic.rootMessageId === message.messageId ? void 0 : await loadReference(topic.rootMessageId),
|
|
137970
|
+
confirmConsumed: () => {
|
|
137971
|
+
deliveredRoots.claim(key);
|
|
137972
|
+
}
|
|
137973
|
+
};
|
|
137974
|
+
};
|
|
137975
|
+
};
|
|
137690
137976
|
const userName = makeLookup(deps.rowId, async (openId, signal) => {
|
|
137691
137977
|
try {
|
|
137692
137978
|
const token = await deps.tenantToken();
|
|
@@ -137713,7 +137999,7 @@ var makeLarkMessageContext = (deps) => {
|
|
|
137713
137999
|
});
|
|
137714
138000
|
return name instanceof Promise || chat instanceof Promise ? Promise.all([name, chat]).then(([senderName, info]) => fields(senderName, info)) : fields(name, chat);
|
|
137715
138001
|
};
|
|
137716
|
-
return { chatInfo, forMessage };
|
|
138002
|
+
return { chatInfo, forMessage, loadReference, topicRootForSession };
|
|
137717
138003
|
};
|
|
137718
138004
|
|
|
137719
138005
|
// src/larkAdapter.ts
|
|
@@ -137755,6 +138041,7 @@ var larkConfigIssues = (binding) => {
|
|
|
137755
138041
|
const { appId, appSecret } = credentials(binding);
|
|
137756
138042
|
const issues = [];
|
|
137757
138043
|
if (appId === "") issues.push({ column: "larkAppId", code: "required" });
|
|
138044
|
+
else if (!isLarkAppId(appId)) issues.push({ column: "larkAppId", code: "format" });
|
|
137758
138045
|
if (appSecret === "") issues.push({ column: "larkAppSecret", code: "required" });
|
|
137759
138046
|
return issues;
|
|
137760
138047
|
};
|
|
@@ -137842,7 +138129,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137842
138129
|
return rootMessageId === void 0 ? void 0 : groupId;
|
|
137843
138130
|
},
|
|
137844
138131
|
validate: larkConfigIssues,
|
|
137845
|
-
isConfigured: (binding) => larkConfigIssues(binding).
|
|
138132
|
+
isConfigured: (binding) => larkConfigIssues(binding).every((issue) => issue.code !== "required"),
|
|
137846
138133
|
connect: (binding, handlers) => {
|
|
137847
138134
|
const { appId, appSecret } = credentials(binding);
|
|
137848
138135
|
const port = {
|
|
@@ -137871,6 +138158,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137871
138158
|
topicOnMention: topicOnMentionOf(binding),
|
|
137872
138159
|
isSoloGroup: soloGroupFor(context),
|
|
137873
138160
|
onError: handlers.onError,
|
|
138161
|
+
...handlers.onConnectionState === void 0 ? {} : { onConnectionState: handlers.onConnectionState },
|
|
137874
138162
|
onCardAction: handlers.onButton,
|
|
137875
138163
|
onPanelClick: (click, operatorOpenId) => handlers.onPanelClick?.({
|
|
137876
138164
|
chatId: click.chatId,
|
|
@@ -137883,14 +138171,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137883
138171
|
const { topic } = message;
|
|
137884
138172
|
const source = context.forMessage(message);
|
|
137885
138173
|
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
|
-
);
|
|
138174
|
+
const reference2 = parentMessageId === void 0 ? null : context.loadReference(parentMessageId);
|
|
137894
138175
|
const deliver = (names, replyTo) => handlers.onMessage({
|
|
137895
138176
|
messageId: message.messageId,
|
|
137896
138177
|
// 已经开出来的话题就是它自己那个聊天;将要开出来的(主窗口里 @ 机器人)先算群的,
|
|
@@ -137906,6 +138187,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
137906
138187
|
senderId: message.senderOpenId,
|
|
137907
138188
|
...names,
|
|
137908
138189
|
text: message.text,
|
|
138190
|
+
topicRootForSession: context.topicRootForSession(message),
|
|
137909
138191
|
...replyTo === void 0 ? {} : { replyTo },
|
|
137910
138192
|
chatKind: message.chatType === "p2p" ? "private" : "group",
|
|
137911
138193
|
replyInThread: message.inThread,
|
|
@@ -137960,7 +138242,8 @@ var makeLarkAdapter = (deps) => {
|
|
|
137960
138242
|
const messageId = await sendChatText({
|
|
137961
138243
|
...target2,
|
|
137962
138244
|
receiveIdType,
|
|
137963
|
-
text: chunk
|
|
138245
|
+
text: chunk,
|
|
138246
|
+
retry: options?.retry
|
|
137964
138247
|
});
|
|
137965
138248
|
deps.log.info("lark delivered a message", { messageId, bot: binding.displayName });
|
|
137966
138249
|
}
|
|
@@ -137974,6 +138257,9 @@ var makeLarkAdapter = (deps) => {
|
|
|
137974
138257
|
detail: "missing app id or secret in saved config"
|
|
137975
138258
|
};
|
|
137976
138259
|
}
|
|
138260
|
+
if (!isLarkAppId(appId)) {
|
|
138261
|
+
return { stage: "credentials", ok: false, detail: "malformed app id in saved config" };
|
|
138262
|
+
}
|
|
137977
138263
|
try {
|
|
137978
138264
|
const { expiresInSeconds } = await fetchTenantToken({ http: deps.http, appId, appSecret });
|
|
137979
138265
|
return {
|
|
@@ -138295,7 +138581,7 @@ var en_default = {
|
|
|
138295
138581
|
},
|
|
138296
138582
|
wecomAppListenPort: {
|
|
138297
138583
|
label: "Local callback port (optional)",
|
|
138298
|
-
description: "Defaults to 30987. Self-built apps listening on the same address share one port.",
|
|
138584
|
+
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
138585
|
placeholder: "30987"
|
|
138300
138586
|
},
|
|
138301
138587
|
wecomAppListenHost: {
|
|
@@ -138313,6 +138599,7 @@ var en_default = {
|
|
|
138313
138599
|
}
|
|
138314
138600
|
},
|
|
138315
138601
|
reply: {
|
|
138602
|
+
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
138603
|
conclusionRecovered: "(Finished while restarting; delivered now)",
|
|
138317
138604
|
notAllowed: "You are not on this bot's allow list, so this tap had no effect.",
|
|
138318
138605
|
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.',
|
|
@@ -138668,13 +138955,16 @@ var en_default = {
|
|
|
138668
138955
|
description: "Verify platform credentials still work; does not open a session or call a model.",
|
|
138669
138956
|
running: "Checking {{rowId}}\u2026",
|
|
138670
138957
|
done: "Connection OK",
|
|
138671
|
-
failed: "Connection failed
|
|
138958
|
+
failed: "Connection failed",
|
|
138672
138959
|
notSaved: "Save this row first (credentials are read from saved config only).",
|
|
138673
138960
|
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
|
|
138961
|
+
badToken: "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key.",
|
|
138962
|
+
unauthorized: "Telegram does not accept this token. It may have been revoked; get the current one from BotFather.",
|
|
138963
|
+
notBot: "This token does not belong to a bot. Use the token BotFather gave you.",
|
|
138964
|
+
webhookActive: "This bot has a webhook set by another service, so it cannot receive messages here. Remove the webhook there, then check again.",
|
|
138965
|
+
wecomTrustedIp: "Add this machine's outbound public IP to the app's trusted IP list in the WeCom admin console.",
|
|
138966
|
+
wecomInvalidUser: "Check that the recipient, department or tag exists and is in this app's visible range.",
|
|
138967
|
+
wecomInvalidAgent: "Check the AgentId against this self-built application in the WeCom admin console."
|
|
138678
138968
|
}
|
|
138679
138969
|
},
|
|
138680
138970
|
copyDiagnostics: {
|
|
@@ -138682,24 +138972,24 @@ var en_default = {
|
|
|
138682
138972
|
description: "Run a connection probe and produce copy-paste diagnostics (no secrets).",
|
|
138683
138973
|
running: "Building diagnostics for {{rowId}}\u2026",
|
|
138684
138974
|
done: "Diagnostics ready \u2014 see details below",
|
|
138685
|
-
failed: "Diagnostics failed
|
|
138975
|
+
failed: "Diagnostics failed",
|
|
138686
138976
|
notSaved: "Save this row first before copying diagnostics."
|
|
138687
138977
|
},
|
|
138688
138978
|
diagnostic: {
|
|
138689
|
-
ref: "
|
|
138979
|
+
ref: "Reference: {{ref}}\n{{detail}}"
|
|
138690
138980
|
},
|
|
138691
138981
|
larkRepair: {
|
|
138692
138982
|
label: "Repair scopes",
|
|
138693
|
-
description: "
|
|
138983
|
+
description: "Check whether the saved credentials can mint a token; scopes, event subscriptions and publishing are not checked automatically.",
|
|
138694
138984
|
step: {
|
|
138695
|
-
scopes: "
|
|
138696
|
-
events: "
|
|
138697
|
-
publish: "
|
|
138985
|
+
scopes: "Scopes \u2014 not checked",
|
|
138986
|
+
events: "Event subscriptions \u2014 not checked",
|
|
138987
|
+
publish: "Publishing \u2014 not checked",
|
|
138698
138988
|
verify: "Verify the app can still mint a tenant token"
|
|
138699
138989
|
},
|
|
138700
|
-
done: "
|
|
138701
|
-
hint: "
|
|
138702
|
-
failed: "Could not
|
|
138990
|
+
done: "Only token issuance was verified. Scopes, event subscriptions and publishing were not checked automatically; review them on the Feishu open platform.",
|
|
138991
|
+
hint: "Reference: {{ref}}\n{{detail}}\nReview scopes, event subscriptions and publishing on the Feishu open platform; none were checked automatically.",
|
|
138992
|
+
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
138993
|
notLark: "This row is not a saved Lark bot."
|
|
138704
138994
|
}
|
|
138705
138995
|
},
|
|
@@ -138738,7 +139028,23 @@ var en_default = {
|
|
|
138738
139028
|
removeFailed: "Couldn't delete this bot.",
|
|
138739
139029
|
status: {
|
|
138740
139030
|
ready: "Ready",
|
|
138741
|
-
incomplete: "Incomplete"
|
|
139031
|
+
incomplete: "Incomplete",
|
|
139032
|
+
connecting: "Connecting",
|
|
139033
|
+
connected: "Connected",
|
|
139034
|
+
notConnected: "Not connected"
|
|
139035
|
+
},
|
|
139036
|
+
connection: {
|
|
139037
|
+
failed: "The last connection attempt failed.",
|
|
139038
|
+
details: "Details: {{error}}",
|
|
139039
|
+
reason: {
|
|
139040
|
+
"not-available": "This platform can't be connected yet.",
|
|
139041
|
+
"invalid-app-id": "The App ID isn't in the right format.",
|
|
139042
|
+
disconnected: "The connection to the platform is down.",
|
|
139043
|
+
"subscription-failed": "The platform didn't confirm the subscription.",
|
|
139044
|
+
"token-unavailable": "Couldn't get an access token.",
|
|
139045
|
+
"credentials-rejected": "The platform rejected the credentials."
|
|
139046
|
+
},
|
|
139047
|
+
duplicate: "Uses the same credentials as \u201C{{name}}\u201D. Only that row connects."
|
|
138742
139048
|
},
|
|
138743
139049
|
secret: {
|
|
138744
139050
|
saved: "Saved. Leave blank to keep it.",
|
|
@@ -138977,7 +139283,7 @@ var zh_CN_default = {
|
|
|
138977
139283
|
},
|
|
138978
139284
|
wecomAppListenPort: {
|
|
138979
139285
|
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",
|
|
139286
|
+
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
139287
|
placeholder: "30987"
|
|
138982
139288
|
},
|
|
138983
139289
|
wecomAppListenHost: {
|
|
@@ -138995,6 +139301,7 @@ var zh_CN_default = {
|
|
|
138995
139301
|
}
|
|
138996
139302
|
},
|
|
138997
139303
|
reply: {
|
|
139304
|
+
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
139305
|
conclusionRecovered: "\uFF08\u91CD\u542F\u671F\u95F4\u5B8C\u6210\uFF0C\u73B0\u5728\u8865\u53D1\uFF09",
|
|
138999
139306
|
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
139307
|
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",
|
|
@@ -139350,13 +139657,16 @@ var zh_CN_default = {
|
|
|
139350
139657
|
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
139658
|
running: "\u6B63\u5728\u68C0\u67E5 {{rowId}} \u2026",
|
|
139352
139659
|
done: "\u8FDE\u63A5\u6B63\u5E38",
|
|
139353
|
-
failed: "\u8FDE\u63A5\u5931\u8D25
|
|
139660
|
+
failed: "\u8FDE\u63A5\u5931\u8D25",
|
|
139354
139661
|
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
139662
|
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\
|
|
139663
|
+
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",
|
|
139664
|
+
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",
|
|
139665
|
+
notBot: "\u8FD9\u4E2A token \u4E0D\u662F\u673A\u5668\u4EBA\u7684\u3002\u8BF7\u7528 BotFather \u7ED9\u7684\u90A3\u4E00\u4EFD\u3002",
|
|
139666
|
+
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",
|
|
139667
|
+
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",
|
|
139668
|
+
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",
|
|
139669
|
+
wecomInvalidAgent: "\u6838\u5BF9\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u81EA\u5EFA\u5E94\u7528\u7684 AgentId\u3002"
|
|
139360
139670
|
}
|
|
139361
139671
|
},
|
|
139362
139672
|
copyDiagnostics: {
|
|
@@ -139364,24 +139674,24 @@ var zh_CN_default = {
|
|
|
139364
139674
|
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
139675
|
running: "\u6B63\u5728\u751F\u6210\u8BCA\u65AD {{rowId}} \u2026",
|
|
139366
139676
|
done: "\u8BCA\u65AD\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
139367
|
-
failed: "\u8BCA\u65AD\u5931\u8D25
|
|
139677
|
+
failed: "\u8BCA\u65AD\u5931\u8D25",
|
|
139368
139678
|
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u590D\u5236\u8BCA\u65AD\u3002"
|
|
139369
139679
|
},
|
|
139370
139680
|
diagnostic: {
|
|
139371
|
-
ref: "\u53C2\u8003\u53F7
|
|
139681
|
+
ref: "\u53C2\u8003\u53F7\uFF1A{{ref}}\n{{detail}}"
|
|
139372
139682
|
},
|
|
139373
139683
|
larkRepair: {
|
|
139374
139684
|
label: "\u8865\u5168\u6743\u9650",
|
|
139375
|
-
description: "\
|
|
139685
|
+
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
139686
|
step: {
|
|
139377
|
-
scopes: "\
|
|
139378
|
-
events: "\
|
|
139379
|
-
publish: "\
|
|
139687
|
+
scopes: "\u6743\u9650\uFF1A\u672A\u68C0\u67E5",
|
|
139688
|
+
events: "\u4E8B\u4EF6\u8BA2\u9605\uFF1A\u672A\u68C0\u67E5",
|
|
139689
|
+
publish: "\u53D1\u5E03\uFF1A\u672A\u68C0\u67E5",
|
|
139380
139690
|
verify: "\u9A8C\u8BC1\u5E94\u7528\u4ECD\u53EF\u6362\u53D6 tenant token"
|
|
139381
139691
|
},
|
|
139382
|
-
done: "\u51ED\u636E\
|
|
139383
|
-
hint: "\u53C2\u8003\u53F7
|
|
139384
|
-
failed: "\
|
|
139692
|
+
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",
|
|
139693
|
+
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",
|
|
139694
|
+
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
139695
|
notLark: "\u8FD9\u4E00\u884C\u4E0D\u662F\u5DF2\u4FDD\u5B58\u7684\u98DE\u4E66\u673A\u5668\u4EBA\u3002"
|
|
139386
139696
|
}
|
|
139387
139697
|
},
|
|
@@ -139419,8 +139729,24 @@ var zh_CN_default = {
|
|
|
139419
139729
|
copyFailed: "\u6CA1\u590D\u5236\u4E0A\u3002",
|
|
139420
139730
|
removeFailed: "\u6CA1\u80FD\u5220\u6389\u8FD9\u4E2A\u673A\u5668\u4EBA\u3002",
|
|
139421
139731
|
status: {
|
|
139422
|
-
ready: "\
|
|
139423
|
-
incomplete: "\
|
|
139732
|
+
ready: "\u5C31\u7EEA",
|
|
139733
|
+
incomplete: "\u5F85\u8865\u5168",
|
|
139734
|
+
connecting: "\u8FDE\u63A5\u4E2D",
|
|
139735
|
+
connected: "\u5DF2\u8FDE\u63A5",
|
|
139736
|
+
notConnected: "\u672A\u8FDE\u63A5"
|
|
139737
|
+
},
|
|
139738
|
+
connection: {
|
|
139739
|
+
failed: "\u4E0A\u6B21\u8FDE\u63A5\u6CA1\u6709\u6210\u529F\u3002",
|
|
139740
|
+
details: "\u8BE6\u60C5\uFF1A{{error}}",
|
|
139741
|
+
reason: {
|
|
139742
|
+
"not-available": "\u8FD9\u4E2A\u5E73\u53F0\u8FD8\u4E0D\u80FD\u8FDE\u63A5\u3002",
|
|
139743
|
+
"invalid-app-id": "App ID \u683C\u5F0F\u4E0D\u6B63\u786E\u3002",
|
|
139744
|
+
disconnected: "\u548C\u5E73\u53F0\u7684\u8FDE\u63A5\u65AD\u4E86\u3002",
|
|
139745
|
+
"subscription-failed": "\u5E73\u53F0\u6CA1\u6709\u786E\u8BA4\u8BA2\u9605\u3002",
|
|
139746
|
+
"token-unavailable": "\u6CA1\u80FD\u6362\u5230\u8BBF\u95EE\u4EE4\u724C\u3002",
|
|
139747
|
+
"credentials-rejected": "\u5E73\u53F0\u62D2\u7EDD\u4E86\u51ED\u636E\u3002"
|
|
139748
|
+
},
|
|
139749
|
+
duplicate: "\u548C\u300C{{name}}\u300D\u7528\u7684\u662F\u540C\u4E00\u4EFD\u51ED\u636E\uFF0C\u53EA\u8FDE\u90A3\u4E00\u884C\u3002"
|
|
139424
139750
|
},
|
|
139425
139751
|
secret: {
|
|
139426
139752
|
saved: "\u5DF2\u7ECF\u5B58\u8FC7\uFF0C\u7559\u7A7A\u5C31\u4FDD\u6301\u539F\u503C\u3002",
|
|
@@ -139601,6 +139927,16 @@ var makeModelPlan = (core, deps) => {
|
|
|
139601
139927
|
// src/notifications.ts
|
|
139602
139928
|
import { readThreadFailed, threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
|
|
139603
139929
|
|
|
139930
|
+
// src/conclusionOutbox.ts
|
|
139931
|
+
var enqueueConclusion = (core, ledger, binding, debt, send) => core.outbound(
|
|
139932
|
+
chatKeyOf(binding),
|
|
139933
|
+
async () => {
|
|
139934
|
+
if (debt === void 0) await send();
|
|
139935
|
+
else await ledger.deliver(debt, send);
|
|
139936
|
+
},
|
|
139937
|
+
{ droppable: debt !== void 0 }
|
|
139938
|
+
);
|
|
139939
|
+
|
|
139604
139940
|
// src/conclusionRecovery.ts
|
|
139605
139941
|
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
139606
139942
|
var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
@@ -139608,6 +139944,12 @@ var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
|
139608
139944
|
core.host.onDispose(() => {
|
|
139609
139945
|
disposed = true;
|
|
139610
139946
|
});
|
|
139947
|
+
const couldNotRecover = (debt, error) => {
|
|
139948
|
+
core.host.log.warn("chat could not recover a conclusion", {
|
|
139949
|
+
...debt,
|
|
139950
|
+
error: describeError(error)
|
|
139951
|
+
});
|
|
139952
|
+
};
|
|
139611
139953
|
await core.ready;
|
|
139612
139954
|
for (const debt of Object.values(conclusionDebtsOf(core.state))) {
|
|
139613
139955
|
if (disposed) return;
|
|
@@ -139634,24 +139976,27 @@ var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
|
139634
139976
|
}
|
|
139635
139977
|
const binding = core.bindingForChatKey(debt.chatKey);
|
|
139636
139978
|
if (binding === void 0) continue;
|
|
139637
|
-
|
|
139638
|
-
|
|
139639
|
-
|
|
139640
|
-
|
|
139641
|
-
|
|
139642
|
-
|
|
139643
|
-
|
|
139644
|
-
|
|
139645
|
-
|
|
139979
|
+
enqueueConclusion(core, ledger, binding, debt, async (owed) => {
|
|
139980
|
+
try {
|
|
139981
|
+
if (latest.state === "error") {
|
|
139982
|
+
return delivery.sendText(
|
|
139983
|
+
binding,
|
|
139984
|
+
debt.threadId,
|
|
139985
|
+
failures.forFailure(debt.threadId, execution.session?.lastError),
|
|
139986
|
+
true,
|
|
139987
|
+
latest.turnId,
|
|
139988
|
+
owed
|
|
139989
|
+
);
|
|
139990
|
+
}
|
|
139991
|
+
const send = await delivery.prepareConclusion(debt.threadId, latest.turnId);
|
|
139992
|
+
return send(binding, { recovered: true, streaming: false, owed });
|
|
139993
|
+
} catch (error) {
|
|
139994
|
+
couldNotRecover(debt, error);
|
|
139995
|
+
return false;
|
|
139646
139996
|
}
|
|
139647
|
-
const send = await delivery.prepareConclusion(debt.threadId, latest.turnId);
|
|
139648
|
-
return send(binding, { recovered: true, streaming: false });
|
|
139649
139997
|
});
|
|
139650
139998
|
} catch (error) {
|
|
139651
|
-
|
|
139652
|
-
...debt,
|
|
139653
|
-
error: describeError(error)
|
|
139654
|
-
});
|
|
139999
|
+
couldNotRecover(debt, error);
|
|
139655
140000
|
}
|
|
139656
140001
|
}
|
|
139657
140002
|
};
|
|
@@ -139674,7 +140019,7 @@ var outcomeOfSession = (status) => {
|
|
|
139674
140019
|
var THREAD_PROJECT_CACHE_LIMIT = 256;
|
|
139675
140020
|
var makeNotifications = (core, deps) => {
|
|
139676
140021
|
const { host, adapterFor, bindingForChatKey, ready } = core;
|
|
139677
|
-
const {
|
|
140022
|
+
const { takeWorking } = deps;
|
|
139678
140023
|
const { turnCards } = deps.replyCards;
|
|
139679
140024
|
const { failures, conclusions, delivery } = deps;
|
|
139680
140025
|
void recoverConclusions(core, conclusions, delivery, failures);
|
|
@@ -139697,6 +140042,11 @@ var makeNotifications = (core, deps) => {
|
|
|
139697
140042
|
});
|
|
139698
140043
|
const streamsTo = (binding, threadId) => adapterFor(binding.platform)?.turnCards !== void 0 && owns(binding, threadId);
|
|
139699
140044
|
const owns = (binding, threadId) => chatOfThread(core.state, threadId) === chatKeyOf(binding);
|
|
140045
|
+
const later = (binding, work) => {
|
|
140046
|
+
core.outbound(chatKeyOf(binding), async () => {
|
|
140047
|
+
await work();
|
|
140048
|
+
});
|
|
140049
|
+
};
|
|
139700
140050
|
const threadsWithSomethingToReport = /* @__PURE__ */ new Set();
|
|
139701
140051
|
host.reactor({
|
|
139702
140052
|
name: "im.assistant-messages",
|
|
@@ -139719,32 +140069,38 @@ var makeNotifications = (core, deps) => {
|
|
|
139719
140069
|
const said = threadsWithSomethingToReport.delete(threadId);
|
|
139720
140070
|
await ready;
|
|
139721
140071
|
const targets = targetsForProject(threadId, await projectOfThread(threadId));
|
|
139722
|
-
const
|
|
139723
|
-
(debt2) => debt2.threadId === threadId
|
|
139724
|
-
);
|
|
139725
|
-
const { debt, handled, execution } = said || owes ? await conclusions.forTurn(threadId) : { debt: void 0, handled: false, execution: null };
|
|
140072
|
+
const { debt, handled, execution } = await conclusions.forTurn(threadId);
|
|
139726
140073
|
if (debt !== void 0) {
|
|
139727
140074
|
const binding = bindingForChatKey(debt.chatKey);
|
|
139728
140075
|
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 });
|
|
140076
|
+
const turnId = execution?.latestTurn?.turnId ?? null;
|
|
140077
|
+
enqueueConclusion(core, conclusions, binding, debt, async (owed) => {
|
|
140078
|
+
const send = await delivery.prepareConclusion(threadId, turnId);
|
|
140079
|
+
return send(binding, { recovered: false, streaming: false, owed });
|
|
139735
140080
|
});
|
|
139736
140081
|
}
|
|
139737
140082
|
} else if (said && !handled) {
|
|
139738
140083
|
const send = await delivery.prepareConclusion(threadId);
|
|
139739
140084
|
for (const binding of targets) {
|
|
139740
|
-
|
|
139741
|
-
|
|
139742
|
-
|
|
139743
|
-
|
|
140085
|
+
const streaming = streamsTo(binding, threadId);
|
|
140086
|
+
enqueueConclusion(
|
|
140087
|
+
core,
|
|
140088
|
+
conclusions,
|
|
140089
|
+
binding,
|
|
140090
|
+
void 0,
|
|
140091
|
+
() => send(binding, { recovered: false, streaming })
|
|
140092
|
+
);
|
|
139744
140093
|
}
|
|
139745
140094
|
}
|
|
139746
140095
|
const outcome = outcomeOfSession(payload.session?.status);
|
|
139747
|
-
for (const binding of targets)
|
|
140096
|
+
for (const binding of targets) {
|
|
140097
|
+
const withdraw = takeWorking(binding, threadId, outcome);
|
|
140098
|
+
turnCards.afterTurn(
|
|
140099
|
+
threadId,
|
|
140100
|
+
() => later(binding, withdraw),
|
|
140101
|
+
execution?.latestTurn?.turnId ?? null
|
|
140102
|
+
);
|
|
140103
|
+
}
|
|
139748
140104
|
}
|
|
139749
140105
|
});
|
|
139750
140106
|
host.events.on("thread.failed", async (event) => {
|
|
@@ -139753,7 +140109,7 @@ var makeNotifications = (core, deps) => {
|
|
|
139753
140109
|
threadsWithSomethingToReport.delete(threadId);
|
|
139754
140110
|
await ready;
|
|
139755
140111
|
const targets = targetsForProject(threadId, await projectOfThread(threadId));
|
|
139756
|
-
const { debt, handled } = await conclusions.forTurn(threadId);
|
|
140112
|
+
const { debt, handled, execution } = await conclusions.forTurn(threadId);
|
|
139757
140113
|
const debtBinding = debt === void 0 ? void 0 : bindingForChatKey(debt.chatKey);
|
|
139758
140114
|
const recipients = debt !== void 0 ? debtBinding === void 0 ? [] : [debtBinding] : targets;
|
|
139759
140115
|
const textTargets = handled ? [] : recipients.filter(
|
|
@@ -139763,16 +140119,20 @@ var makeNotifications = (core, deps) => {
|
|
|
139763
140119
|
const notice = textTargets.length > 0 || drawingTurn !== null ? failures.forFailure(threadId, readThreadFailed(event)?.detail) : null;
|
|
139764
140120
|
if (notice !== null && drawingTurn !== null)
|
|
139765
140121
|
failures.holdForCard(threadId, drawingTurn, notice);
|
|
139766
|
-
for (const binding of targets) {
|
|
139767
|
-
await clearWorking(binding, threadId, "failed");
|
|
139768
|
-
}
|
|
139769
140122
|
if (notice !== null) {
|
|
139770
140123
|
for (const binding of textTargets) {
|
|
139771
|
-
const send = () => delivery.
|
|
139772
|
-
|
|
139773
|
-
else await send();
|
|
140124
|
+
const send = (owed) => delivery.sendText(binding, threadId, notice, false, null, owed);
|
|
140125
|
+
enqueueConclusion(core, conclusions, binding, debt, send);
|
|
139774
140126
|
}
|
|
139775
140127
|
}
|
|
140128
|
+
for (const binding of targets) {
|
|
140129
|
+
const withdraw = takeWorking(binding, threadId, "failed");
|
|
140130
|
+
turnCards.afterTurn(
|
|
140131
|
+
threadId,
|
|
140132
|
+
() => later(binding, withdraw),
|
|
140133
|
+
execution?.latestTurn?.turnId ?? null
|
|
140134
|
+
);
|
|
140135
|
+
}
|
|
139776
140136
|
});
|
|
139777
140137
|
const rememberThreadProject = (threadId, projectId) => {
|
|
139778
140138
|
projectByThread.set(threadId, projectId);
|
|
@@ -140333,32 +140693,6 @@ var makeProjectRows = (core) => {
|
|
|
140333
140693
|
return { projectRowsFor, listedProjectsByChat };
|
|
140334
140694
|
};
|
|
140335
140695
|
|
|
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
140696
|
// src/failureNotices.ts
|
|
140363
140697
|
var makeFailureNotices = (core) => {
|
|
140364
140698
|
const heldForCard = /* @__PURE__ */ new Map();
|
|
@@ -140450,20 +140784,12 @@ var trailingReply = (messages) => {
|
|
|
140450
140784
|
var ROTATE = "rotate";
|
|
140451
140785
|
var isDrawing = (live) => live.card !== null || live.reopen;
|
|
140452
140786
|
var RECENT_ENDED_LIMIT = 256;
|
|
140787
|
+
var turnKey = (threadId, turnId) => JSON.stringify([threadId, turnId]);
|
|
140453
140788
|
var makeTurnCards = (deps) => {
|
|
140454
140789
|
const slots = /* @__PURE__ */ new Map();
|
|
140455
|
-
const recentEnded =
|
|
140790
|
+
const recentEnded = makeBoundedSeen(RECENT_ENDED_LIMIT);
|
|
140456
140791
|
const erroredThreads = /* @__PURE__ */ new Set();
|
|
140457
140792
|
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
140793
|
const readTexts = async (live, progress) => {
|
|
140468
140794
|
const texts = /* @__PURE__ */ new Map();
|
|
140469
140795
|
for (const segment of progress.segments) {
|
|
@@ -140515,29 +140841,28 @@ var makeTurnCards = (deps) => {
|
|
|
140515
140841
|
const turnId = progress.turnId ?? live?.turnId ?? null;
|
|
140516
140842
|
const target2 = live?.target ?? deps.resolveTarget(threadId, turnId);
|
|
140517
140843
|
if (target2 === null) return;
|
|
140518
|
-
rememberEnded(threadId, turnId);
|
|
140519
140844
|
if (end.status === "error") erroredThreads.add(threadId);
|
|
140520
140845
|
const title = live?.title ?? await deps.titleOf(threadId);
|
|
140521
140846
|
const view = await finalView(title, progress, live, end);
|
|
140522
|
-
|
|
140523
|
-
|
|
140524
|
-
|
|
140525
|
-
|
|
140526
|
-
|
|
140527
|
-
|
|
140528
|
-
|
|
140529
|
-
|
|
140530
|
-
|
|
140531
|
-
|
|
140532
|
-
|
|
140533
|
-
|
|
140534
|
-
|
|
140535
|
-
|
|
140847
|
+
const card = live?.card ?? null;
|
|
140848
|
+
const finished = card !== null && await card.finish(view);
|
|
140849
|
+
if (card !== null) deps.onClosed(card.cardId);
|
|
140850
|
+
const send = async () => finished || card === null && await openCard(target2, view) !== null;
|
|
140851
|
+
const sent = deps.finalize === void 0 ? await send() : await deps.finalize(threadId, turnId, target2.chatKey, send);
|
|
140852
|
+
if (!sent) await deps.fallback(threadId, turnId, target2.chatKey, view);
|
|
140853
|
+
if (turnId !== null) recentEnded.claim(turnKey(threadId, turnId));
|
|
140854
|
+
const callbacks = slot.afterTurn.get(turnId) ?? [];
|
|
140855
|
+
slot.afterTurn.delete(turnId);
|
|
140856
|
+
if (turnId !== null) {
|
|
140857
|
+
callbacks.push(...slot.afterTurn.get(null) ?? []);
|
|
140858
|
+
slot.afterTurn.delete(null);
|
|
140859
|
+
}
|
|
140860
|
+
for (const after of callbacks) after();
|
|
140536
140861
|
};
|
|
140537
140862
|
const handle = async (slot, threadId, item) => {
|
|
140538
140863
|
await deps.ready;
|
|
140539
140864
|
if (disposed) return;
|
|
140540
|
-
if (item.turnId !== null && recentEnded.
|
|
140865
|
+
if (item.turnId !== null && recentEnded.has(turnKey(threadId, item.turnId))) return;
|
|
140541
140866
|
let live = slot.live;
|
|
140542
140867
|
if (live !== null) {
|
|
140543
140868
|
const sameTurn = item.turnId === null || live.turnId === null || live.turnId === item.turnId;
|
|
@@ -140621,7 +140946,11 @@ var makeTurnCards = (deps) => {
|
|
|
140621
140946
|
}
|
|
140622
140947
|
} finally {
|
|
140623
140948
|
slot.running = false;
|
|
140624
|
-
if (slot.live === null && slot.queue.length === 0)
|
|
140949
|
+
if (slot.live === null && slot.queue.length === 0) {
|
|
140950
|
+
slots.delete(threadId);
|
|
140951
|
+
for (const callbacks of slot.afterTurn.values()) for (const after of callbacks) after();
|
|
140952
|
+
slot.afterTurn.clear();
|
|
140953
|
+
}
|
|
140625
140954
|
}
|
|
140626
140955
|
};
|
|
140627
140956
|
return {
|
|
@@ -140630,7 +140959,7 @@ var makeTurnCards = (deps) => {
|
|
|
140630
140959
|
if (disposed) return;
|
|
140631
140960
|
let slot = slots.get(threadId);
|
|
140632
140961
|
if (slot === void 0) {
|
|
140633
|
-
slot = { queue: [], running: false, live: null };
|
|
140962
|
+
slot = { queue: [], running: false, live: null, afterTurn: /* @__PURE__ */ new Map() };
|
|
140634
140963
|
slots.set(threadId, slot);
|
|
140635
140964
|
}
|
|
140636
140965
|
const last = slot.queue.at(-1);
|
|
@@ -140644,6 +140973,18 @@ var makeTurnCards = (deps) => {
|
|
|
140644
140973
|
void pump(threadId, slot);
|
|
140645
140974
|
}
|
|
140646
140975
|
},
|
|
140976
|
+
/** 撤回执只等所属轮次收尾;没有轮次 id 时仍等当前线程槽,不能在出站队列里等自己。 */
|
|
140977
|
+
afterTurn: (threadId, work, turnId = null) => {
|
|
140978
|
+
if (disposed) return;
|
|
140979
|
+
const slot = slots.get(threadId);
|
|
140980
|
+
if (slot === void 0 || turnId !== null && recentEnded.has(turnKey(threadId, turnId)))
|
|
140981
|
+
work();
|
|
140982
|
+
else {
|
|
140983
|
+
const callbacks = slot.afterTurn.get(turnId) ?? [];
|
|
140984
|
+
callbacks.push(work);
|
|
140985
|
+
slot.afterTurn.set(turnId, callbacks);
|
|
140986
|
+
}
|
|
140987
|
+
},
|
|
140647
140988
|
/**
|
|
140648
140989
|
* 审批或提问答完了:封存这条会话开着的卡,下一拍另开一张。立刻返回;没有在画的卡、或
|
|
140649
140990
|
* 平台不换卡,就什么都不做。
|
|
@@ -140670,7 +141011,10 @@ var makeTurnCards = (deps) => {
|
|
|
140670
141011
|
/** 插件停用:放下所有在画的卡,不再发任何更新。卡会停在最后一拍,重启时被标「已中断」。 */
|
|
140671
141012
|
dispose: () => {
|
|
140672
141013
|
disposed = true;
|
|
140673
|
-
for (const slot of slots.values())
|
|
141014
|
+
for (const slot of slots.values()) {
|
|
141015
|
+
slot.live?.card?.abandon();
|
|
141016
|
+
slot.afterTurn.clear();
|
|
141017
|
+
}
|
|
140674
141018
|
slots.clear();
|
|
140675
141019
|
}
|
|
140676
141020
|
};
|
|
@@ -140678,7 +141022,7 @@ var makeTurnCards = (deps) => {
|
|
|
140678
141022
|
|
|
140679
141023
|
// src/replyCards.ts
|
|
140680
141024
|
var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
140681
|
-
const { host, adapterFor, commit,
|
|
141025
|
+
const { host, adapterFor, commit, bindingForChatKey, bindingOfThread, ready } = core;
|
|
140682
141026
|
const stopRequested = /* @__PURE__ */ new Set();
|
|
140683
141027
|
const turnCards = makeTurnCards({
|
|
140684
141028
|
ready,
|
|
@@ -140737,22 +141081,28 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
|
140737
141081
|
onClosed: (cardId) => commit("turn-card-closed", { cardId }),
|
|
140738
141082
|
finalize: async (threadId, turnId, chatKey, send) => {
|
|
140739
141083
|
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;
|
|
141084
|
+
if (handled) return true;
|
|
141085
|
+
if (debt === void 0) return send();
|
|
141086
|
+
if (!await conclusions.deliver(debt, send)) return false;
|
|
140746
141087
|
const binding = bindingForChatKey(chatKey);
|
|
140747
|
-
if (binding === void 0) return;
|
|
141088
|
+
if (binding === void 0) return true;
|
|
140748
141089
|
const sendConclusion = await delivery.prepareConclusion(threadId, turnId);
|
|
140749
141090
|
await sendConclusion(binding, { recovered: false, streaming: true });
|
|
141091
|
+
return true;
|
|
140750
141092
|
},
|
|
140751
141093
|
fallback: async (threadId, turnId, chatKey, view) => {
|
|
141094
|
+
const { debt, handled } = await conclusions.forTurn(threadId, turnId);
|
|
141095
|
+
if (handled) return;
|
|
140752
141096
|
const binding = bindingForChatKey(chatKey);
|
|
140753
|
-
if (binding === void 0) return
|
|
141097
|
+
if (binding === void 0) return;
|
|
140754
141098
|
const text4 = [view.body, view.errorText ?? ""].filter((part) => part !== "").join("\n\n");
|
|
140755
|
-
|
|
141099
|
+
enqueueConclusion(
|
|
141100
|
+
core,
|
|
141101
|
+
conclusions,
|
|
141102
|
+
binding,
|
|
141103
|
+
debt,
|
|
141104
|
+
(owed) => delivery.sendText(binding, threadId, text4, false, turnId, owed)
|
|
141105
|
+
);
|
|
140756
141106
|
},
|
|
140757
141107
|
schedule: (run, delayMs) => {
|
|
140758
141108
|
const timer = setTimeout(run, delayMs);
|
|
@@ -141122,7 +141472,7 @@ var makeSessions = (core) => {
|
|
|
141122
141472
|
};
|
|
141123
141473
|
|
|
141124
141474
|
// src/teamGoals.ts
|
|
141125
|
-
import * as
|
|
141475
|
+
import * as NodeCrypto4 from "node:crypto";
|
|
141126
141476
|
var isRecord2 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
141127
141477
|
var teamGoalRequestId = (binding, messageId) => {
|
|
141128
141478
|
const identity = JSON.stringify([
|
|
@@ -141132,7 +141482,7 @@ var teamGoalRequestId = (binding, messageId) => {
|
|
|
141132
141482
|
binding.chatId,
|
|
141133
141483
|
messageId
|
|
141134
141484
|
]);
|
|
141135
|
-
return
|
|
141485
|
+
return NodeCrypto4.createHash("sha256").update(identity).digest("hex");
|
|
141136
141486
|
};
|
|
141137
141487
|
var makeTeamGoals = (core) => {
|
|
141138
141488
|
const { host, t, deliver, commit } = core;
|
|
@@ -141153,14 +141503,16 @@ var makeTeamGoals = (core) => {
|
|
|
141153
141503
|
};
|
|
141154
141504
|
const startTeamGoal = async (binding, message) => {
|
|
141155
141505
|
try {
|
|
141506
|
+
const requestId = teamGoalRequestId(binding, message.messageId);
|
|
141507
|
+
const topicRoot = await message.topicRootForSession?.(requestId);
|
|
141156
141508
|
const result = await host.plugins.invoke({
|
|
141157
141509
|
pluginId: "team",
|
|
141158
141510
|
command: "startGoal",
|
|
141159
141511
|
payload: {
|
|
141160
141512
|
projectId: binding.projectId,
|
|
141161
|
-
goal: textWithInboundContext(message, binding.rowId, t),
|
|
141513
|
+
goal: textWithInboundContext(message, binding.rowId, t, topicRoot?.reference),
|
|
141162
141514
|
titleSeed: message.text,
|
|
141163
|
-
requestId
|
|
141515
|
+
requestId
|
|
141164
141516
|
}
|
|
141165
141517
|
});
|
|
141166
141518
|
if (isRecord2(result) && result._tag === "TeamGoalRejection") {
|
|
@@ -141175,6 +141527,7 @@ var makeTeamGoals = (core) => {
|
|
|
141175
141527
|
if (!isRecord2(result) || typeof result.leadThreadId !== "string" || result.leadThreadId === "" || result.leadName !== null && typeof result.leadName !== "string") {
|
|
141176
141528
|
throw new Error("team startGoal returned an invalid result");
|
|
141177
141529
|
}
|
|
141530
|
+
topicRoot?.confirmConsumed();
|
|
141178
141531
|
commit("team-goal-started", { chatKey: chatKeyOf(binding), threadId: result.leadThreadId });
|
|
141179
141532
|
await deliver(
|
|
141180
141533
|
binding,
|
|
@@ -141230,6 +141583,10 @@ var TelegramApiError = class extends Error {
|
|
|
141230
141583
|
this.retryAfterSeconds = retryAfterSeconds;
|
|
141231
141584
|
}
|
|
141232
141585
|
};
|
|
141586
|
+
var telegramSendVerdict = (error) => error instanceof TelegramApiError && (error.code === 429 || error.code === 503 && error.retryAfterSeconds !== void 0) ? {
|
|
141587
|
+
retry: true,
|
|
141588
|
+
afterMs: error.retryAfterSeconds === void 0 ? void 0 : error.retryAfterSeconds * 1e3
|
|
141589
|
+
} : NO_RETRY;
|
|
141233
141590
|
var isNotModified = (error) => error instanceof TelegramApiError && error.code === 400 && error.message.toLowerCase().includes("message is not modified");
|
|
141234
141591
|
var PHOTO_NAME = "photo.jpg";
|
|
141235
141592
|
var PHOTO_MIME = "image/jpeg";
|
|
@@ -141436,9 +141793,13 @@ var makeTelegramApi = (input) => {
|
|
|
141436
141793
|
},
|
|
141437
141794
|
POLL_CALL_TIMEOUT_MS
|
|
141438
141795
|
),
|
|
141439
|
-
sendMessage: async (chatId, text4) => {
|
|
141796
|
+
sendMessage: async (chatId, text4, retry) => {
|
|
141440
141797
|
for (const chunk of splitForTelegram(text4)) {
|
|
141441
|
-
await
|
|
141798
|
+
await sendWithRetry(
|
|
141799
|
+
() => call("sendMessage", { ...target(chatId), text: chunk }),
|
|
141800
|
+
telegramSendVerdict,
|
|
141801
|
+
retry
|
|
141802
|
+
);
|
|
141442
141803
|
}
|
|
141443
141804
|
},
|
|
141444
141805
|
/**
|
|
@@ -141876,6 +142237,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141876
142237
|
const poll = async () => {
|
|
141877
142238
|
for (; ; ) {
|
|
141878
142239
|
if (!running) return;
|
|
142240
|
+
let checkingConnection = true;
|
|
141879
142241
|
try {
|
|
141880
142242
|
const api = await apiFor(binding);
|
|
141881
142243
|
liveApi = api;
|
|
@@ -141884,6 +142246,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141884
142246
|
const own = await api.getMe();
|
|
141885
142247
|
botUsername = own.username ?? null;
|
|
141886
142248
|
botId = own.id;
|
|
142249
|
+
if (running) handlers.onConnectionState?.({ state: "connected" });
|
|
141887
142250
|
}
|
|
141888
142251
|
if (!running) return;
|
|
141889
142252
|
if (!menuSent) {
|
|
@@ -141898,6 +142261,8 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141898
142261
|
acknowledged = offset;
|
|
141899
142262
|
const updates = await api.getUpdates(offset);
|
|
141900
142263
|
if (!running) return;
|
|
142264
|
+
handlers.onConnectionState?.({ state: "connected" });
|
|
142265
|
+
checkingConnection = false;
|
|
141901
142266
|
backoff = BACKOFF_START_MS;
|
|
141902
142267
|
for (const update of updates) {
|
|
141903
142268
|
if (!running) break;
|
|
@@ -141982,6 +142347,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141982
142347
|
}
|
|
141983
142348
|
} catch (error) {
|
|
141984
142349
|
if (!running) return;
|
|
142350
|
+
if (checkingConnection) handlers.onConnectionState?.({ state: "failed", error });
|
|
141985
142351
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
141986
142352
|
await Promise.race([(deps.sleep ?? defaultSleep)(backoff), stopped]);
|
|
141987
142353
|
backoff = Math.min(backoff * 2, BACKOFF_MAX_MS);
|
|
@@ -141999,9 +142365,9 @@ var makeTelegramAdapter = (deps) => {
|
|
|
141999
142365
|
}
|
|
142000
142366
|
};
|
|
142001
142367
|
},
|
|
142002
|
-
sendText: async (binding, text4) => {
|
|
142368
|
+
sendText: async (binding, text4, options) => {
|
|
142003
142369
|
const api = await apiFor(binding);
|
|
142004
|
-
await api.sendMessage(binding.chatId, text4);
|
|
142370
|
+
await api.sendMessage(binding.chatId, text4, options?.retry);
|
|
142005
142371
|
},
|
|
142006
142372
|
checkConnection: async (bot) => {
|
|
142007
142373
|
const token = tokenOf(bot);
|
|
@@ -142260,6 +142626,7 @@ var makeTurns = (core, deps) => {
|
|
|
142260
142626
|
const text4 = message.text;
|
|
142261
142627
|
const threadId = await ensureThread(binding, text4 || message.resources[0]?.name || "Chat");
|
|
142262
142628
|
if (threadId === null) return null;
|
|
142629
|
+
const topicRoot = await message.topicRootForSession?.(threadId);
|
|
142263
142630
|
const carried = waitingFor(chatKey, threadId);
|
|
142264
142631
|
pendingByChat.delete(chatKey);
|
|
142265
142632
|
const own = await collectResources(
|
|
@@ -142297,7 +142664,10 @@ var makeTurns = (core, deps) => {
|
|
|
142297
142664
|
message: {
|
|
142298
142665
|
messageId: userMessageId,
|
|
142299
142666
|
role: "user",
|
|
142300
|
-
text: [
|
|
142667
|
+
text: [
|
|
142668
|
+
textWithInboundContext(message, binding.rowId, t, topicRoot?.reference),
|
|
142669
|
+
...notes
|
|
142670
|
+
].join("\n"),
|
|
142301
142671
|
attachments: attachments.map(
|
|
142302
142672
|
(attachment) => attachment.type === "image" ? { ...attachment, type: "image" } : { ...attachment, type: "file" }
|
|
142303
142673
|
)
|
|
@@ -142307,6 +142677,7 @@ var makeTurns = (core, deps) => {
|
|
|
142307
142677
|
interactionMode: interactionModeOf(chatKey),
|
|
142308
142678
|
...intentAction !== void 0 ? { intentAction } : {}
|
|
142309
142679
|
});
|
|
142680
|
+
topicRoot?.confirmConsumed();
|
|
142310
142681
|
if (intentAction?.kind !== "steer") {
|
|
142311
142682
|
deps.conclusions.owe({
|
|
142312
142683
|
chatKey,
|
|
@@ -142435,8 +142806,15 @@ var ROW_ACTION_ITEM_SCHEMA = makeFlowItemSchema({
|
|
|
142435
142806
|
doneRequired: ["messageKey", "fills"]
|
|
142436
142807
|
});
|
|
142437
142808
|
var botOf = (config2, adapters, rowId) => {
|
|
142438
|
-
const
|
|
142439
|
-
|
|
142809
|
+
const table2 = config2[BOTS_FIELD];
|
|
142810
|
+
if (typeof table2 !== "object" || table2 === null || Array.isArray(table2) || !Object.hasOwn(table2, rowId))
|
|
142811
|
+
return void 0;
|
|
142812
|
+
const row = table2[rowId];
|
|
142813
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) return void 0;
|
|
142814
|
+
const raw = row;
|
|
142815
|
+
const platform = platformOf(raw);
|
|
142816
|
+
const adapter = adapters.find((candidate) => candidate.platform === platform);
|
|
142817
|
+
return { ...readBotView(rowId, raw, adapter ?? { label: platform }), platform };
|
|
142440
142818
|
};
|
|
142441
142819
|
var reportOf = (bot, stage, ok, detail, hint) => ({
|
|
142442
142820
|
ref: diagnosticRef("conn"),
|
|
@@ -142452,12 +142830,9 @@ var reportOf = (bot, stage, ok, detail, hint) => ({
|
|
|
142452
142830
|
var probe = async (adapters, bot) => {
|
|
142453
142831
|
const adapter = adapters.find((candidate) => candidate.platform === bot.platform);
|
|
142454
142832
|
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]);
|
|
142833
|
+
return reportOf(bot, "platform", false, `unsupported platform ${bot.platform}`, "notAvailable");
|
|
142460
142834
|
}
|
|
142835
|
+
const redactor = botRedactor(adapter.columns, bot.values);
|
|
142461
142836
|
try {
|
|
142462
142837
|
const result = await adapter.checkConnection(bot);
|
|
142463
142838
|
return reportOf(
|
|
@@ -142482,10 +142857,15 @@ var emitReport = (emit, report, action) => {
|
|
|
142482
142857
|
});
|
|
142483
142858
|
return;
|
|
142484
142859
|
}
|
|
142860
|
+
const suggested = report.hint !== void 0 && action.keyPrefix === "rowAction.checkConnection";
|
|
142485
142861
|
emit({
|
|
142486
142862
|
kind: "failed",
|
|
142487
|
-
reasonKey: report.hint !== void 0
|
|
142488
|
-
|
|
142863
|
+
reasonKey: suggested && report.hint !== void 0 ? connectionHintKey(report.hint) : `${action.keyPrefix}.failed`,
|
|
142864
|
+
// 参考号由界面写在细节第一行(「参考号:…」),结论句里不带。
|
|
142865
|
+
params: {
|
|
142866
|
+
ref: report.ref,
|
|
142867
|
+
...suggested && report.hint !== void 0 ? { hint: report.hint } : {}
|
|
142868
|
+
},
|
|
142489
142869
|
detail
|
|
142490
142870
|
});
|
|
142491
142871
|
};
|
|
@@ -142537,9 +142917,9 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142537
142917
|
send({
|
|
142538
142918
|
kind: "steps",
|
|
142539
142919
|
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: "
|
|
142920
|
+
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "skipped" },
|
|
142921
|
+
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "skipped" },
|
|
142922
|
+
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "skipped" },
|
|
142543
142923
|
{ id: "verify", labelKey: "rowAction.larkRepair.step.verify", state: "running" }
|
|
142544
142924
|
]
|
|
142545
142925
|
});
|
|
@@ -142556,9 +142936,9 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142556
142936
|
send({
|
|
142557
142937
|
kind: "steps",
|
|
142558
142938
|
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: "
|
|
142939
|
+
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "skipped" },
|
|
142940
|
+
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "skipped" },
|
|
142941
|
+
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "skipped" },
|
|
142562
142942
|
{
|
|
142563
142943
|
id: "verify",
|
|
142564
142944
|
labelKey: "rowAction.larkRepair.step.verify",
|
|
@@ -142574,7 +142954,7 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142574
142954
|
warnings: [
|
|
142575
142955
|
{
|
|
142576
142956
|
key: "rowAction.larkRepair.hint",
|
|
142577
|
-
params: { ref, detail: formatDiagnosticCopy(
|
|
142957
|
+
params: { ref, detail: formatDiagnosticCopy(report) }
|
|
142578
142958
|
}
|
|
142579
142959
|
]
|
|
142580
142960
|
});
|
|
@@ -142584,7 +142964,7 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142584
142964
|
kind: "failed",
|
|
142585
142965
|
reasonKey: "rowAction.larkRepair.failed",
|
|
142586
142966
|
params: { ref },
|
|
142587
|
-
detail: formatDiagnosticCopy(
|
|
142967
|
+
detail: formatDiagnosticCopy(report)
|
|
142588
142968
|
});
|
|
142589
142969
|
}
|
|
142590
142970
|
});
|
|
@@ -142784,8 +143164,29 @@ var generalWriteOptions = (payload, next) => {
|
|
|
142784
143164
|
}
|
|
142785
143165
|
return void 0;
|
|
142786
143166
|
};
|
|
142787
|
-
var
|
|
142788
|
-
|
|
143167
|
+
var registerSettingsCommands = (ctx, adapters, updateSettings, connections) => {
|
|
143168
|
+
const viewAfterWrite = async () => buildSettingsView(await ctx.settings.read(), adapters);
|
|
143169
|
+
ctx.stream({
|
|
143170
|
+
name: SETTINGS_CONNECTIONS_COMMAND,
|
|
143171
|
+
description: "Read bot connection states as an initial snapshot and subsequent changes.",
|
|
143172
|
+
scope: "read",
|
|
143173
|
+
payloadSchema: EMPTY_PAYLOAD,
|
|
143174
|
+
buffer: { latest: 1 },
|
|
143175
|
+
handle: async (_payload, emit, signal) => {
|
|
143176
|
+
if (signal.aborted) return;
|
|
143177
|
+
const unsubscribe = connections.subscribe((snapshot) => {
|
|
143178
|
+
if (!signal.aborted) emit(snapshot);
|
|
143179
|
+
});
|
|
143180
|
+
try {
|
|
143181
|
+
await new Promise((resolve) => {
|
|
143182
|
+
if (signal.aborted) resolve();
|
|
143183
|
+
else signal.addEventListener("abort", () => resolve(), { once: true });
|
|
143184
|
+
});
|
|
143185
|
+
} finally {
|
|
143186
|
+
unsubscribe();
|
|
143187
|
+
}
|
|
143188
|
+
}
|
|
143189
|
+
});
|
|
142789
143190
|
ctx.command({
|
|
142790
143191
|
name: SETTINGS_VIEW_COMMAND,
|
|
142791
143192
|
description: "Read the chat-bridge settings view, bot columns, and this machine's proxies.",
|
|
@@ -142812,7 +143213,7 @@ var registerSettingsCommands = (ctx, adapters, updateSettings) => {
|
|
|
142812
143213
|
return { result, write: { config: next, options: { secrets, clearSecrets } } };
|
|
142813
143214
|
});
|
|
142814
143215
|
if (!edited.ok) return edited;
|
|
142815
|
-
return { ok: true, rowId: edited.rowId, view: await viewAfterWrite(
|
|
143216
|
+
return { ok: true, rowId: edited.rowId, view: await viewAfterWrite() };
|
|
142816
143217
|
};
|
|
142817
143218
|
const removeRow2 = async (remove) => {
|
|
142818
143219
|
const removed = await updateSettings((config2) => {
|
|
@@ -142820,7 +143221,7 @@ var registerSettingsCommands = (ctx, adapters, updateSettings) => {
|
|
|
142820
143221
|
return next === null ? { result: false } : { result: true, write: { config: next } };
|
|
142821
143222
|
});
|
|
142822
143223
|
if (!removed) return { ok: false, code: "unknown-row" };
|
|
142823
|
-
return { ok: true, view: await viewAfterWrite(
|
|
143224
|
+
return { ok: true, view: await viewAfterWrite() };
|
|
142824
143225
|
};
|
|
142825
143226
|
ctx.command({
|
|
142826
143227
|
name: SETTINGS_SAVE_BOT_COMMAND,
|
|
@@ -142871,7 +143272,7 @@ var registerSettingsCommands = (ctx, adapters, updateSettings) => {
|
|
|
142871
143272
|
write: options === void 0 ? { config: next } : { config: next, options }
|
|
142872
143273
|
};
|
|
142873
143274
|
});
|
|
142874
|
-
return { ok: true, view: await viewAfterWrite(
|
|
143275
|
+
return { ok: true, view: await viewAfterWrite() };
|
|
142875
143276
|
}
|
|
142876
143277
|
});
|
|
142877
143278
|
};
|
|
@@ -142899,12 +143300,13 @@ var makeWechatAdapter = (deps) => {
|
|
|
142899
143300
|
columns: COLUMNS3,
|
|
142900
143301
|
validate: wechatConfigIssues,
|
|
142901
143302
|
isConfigured: (bot) => wechatConfigIssues(bot).length === 0,
|
|
142902
|
-
connect: (bot,
|
|
143303
|
+
connect: (bot, handlers) => {
|
|
142903
143304
|
deps.log.warn("wechat iLink connect is stubbed pending security-review gate (YCO-236)", {
|
|
142904
143305
|
bot: bot.displayName,
|
|
142905
143306
|
hasToken: tokenOf2(bot) !== "",
|
|
142906
143307
|
textLimit: WECHAT_TEXT_MAX_CHARS
|
|
142907
143308
|
});
|
|
143309
|
+
handlers.onConnectionState?.({ state: "failed", reason: "not-available" });
|
|
142908
143310
|
return { close: () => void 0 };
|
|
142909
143311
|
},
|
|
142910
143312
|
sendText: async (binding, _text) => {
|
|
@@ -142927,7 +143329,7 @@ var makeWechatAdapter = (deps) => {
|
|
|
142927
143329
|
};
|
|
142928
143330
|
|
|
142929
143331
|
// src/wecomUpload.ts
|
|
142930
|
-
import * as
|
|
143332
|
+
import * as NodeCrypto5 from "node:crypto";
|
|
142931
143333
|
var WECOM_CHUNK_BYTES = 512 * 1024;
|
|
142932
143334
|
var WECOM_MAX_CHUNKS = 100;
|
|
142933
143335
|
var WECOM_MIN_UPLOAD_BYTES = 5;
|
|
@@ -142960,7 +143362,7 @@ var uploadWecomMedia = async (input) => {
|
|
|
142960
143362
|
filename: clampName(input.filename),
|
|
142961
143363
|
total_size: bytes.byteLength,
|
|
142962
143364
|
total_chunks: totalChunks,
|
|
142963
|
-
md5:
|
|
143365
|
+
md5: NodeCrypto5.createHash("md5").update(bytes).digest("hex")
|
|
142964
143366
|
});
|
|
142965
143367
|
const uploadId = init.upload_id;
|
|
142966
143368
|
if (typeof uploadId !== "string" || uploadId === "") {
|
|
@@ -143047,7 +143449,7 @@ var wecomReplyReference = (quote) => {
|
|
|
143047
143449
|
};
|
|
143048
143450
|
|
|
143049
143451
|
// src/wecomApi.ts
|
|
143050
|
-
var DEFAULT_WECOM_ENDPOINT = "wss://openws.work.weixin.qq.com
|
|
143452
|
+
var DEFAULT_WECOM_ENDPOINT = "wss://openws.work.weixin.qq.com";
|
|
143051
143453
|
var PING_INTERVAL_MS = 3e4;
|
|
143052
143454
|
var MAX_MISSED_PONGS = 2;
|
|
143053
143455
|
var SUBSCRIBE_TIMEOUT_MS = 1e4;
|
|
@@ -143184,9 +143586,10 @@ var runWecomClient = (input) => {
|
|
|
143184
143586
|
const message = parseWecomMessage(frame, input.botId);
|
|
143185
143587
|
if (message !== null) input.handlers.onMessage(message);
|
|
143186
143588
|
};
|
|
143187
|
-
const finish = () => {
|
|
143589
|
+
const finish = (error = new Error("WeCom connection closed"), reason = "disconnected") => {
|
|
143188
143590
|
if (finished) return;
|
|
143189
143591
|
finished = true;
|
|
143592
|
+
if (!closed) input.handlers.onConnectionState?.({ state: "failed", error, reason });
|
|
143190
143593
|
clearInterval(heartbeat);
|
|
143191
143594
|
openStreams.clear();
|
|
143192
143595
|
void input.socket.close().catch(() => void 0);
|
|
@@ -143204,6 +143607,7 @@ var runWecomClient = (input) => {
|
|
|
143204
143607
|
if (!closed) {
|
|
143205
143608
|
input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143206
143609
|
}
|
|
143610
|
+
finish(error);
|
|
143207
143611
|
}
|
|
143208
143612
|
finish();
|
|
143209
143613
|
};
|
|
@@ -143211,7 +143615,7 @@ var runWecomClient = (input) => {
|
|
|
143211
143615
|
if (finished) return;
|
|
143212
143616
|
if (missedPong >= MAX_MISSED_PONGS) {
|
|
143213
143617
|
input.handlers.onError("socket hang up: missed pong");
|
|
143214
|
-
finish();
|
|
143618
|
+
finish(new Error("socket hang up: missed pong"));
|
|
143215
143619
|
return;
|
|
143216
143620
|
}
|
|
143217
143621
|
missedPong += 1;
|
|
@@ -143232,10 +143636,13 @@ var runWecomClient = (input) => {
|
|
|
143232
143636
|
settleAck(subscribeId, error instanceof Error ? error : new Error(String(error)));
|
|
143233
143637
|
});
|
|
143234
143638
|
const ready = subscribed.then(
|
|
143235
|
-
() =>
|
|
143639
|
+
() => {
|
|
143640
|
+
if (!finished && !closed) input.handlers.onConnectionState?.({ state: "connected" });
|
|
143641
|
+
return !finished && !closed;
|
|
143642
|
+
},
|
|
143236
143643
|
(error) => {
|
|
143237
143644
|
if (!closed) input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143238
|
-
finish();
|
|
143645
|
+
finish(error, "subscription-failed");
|
|
143239
143646
|
return false;
|
|
143240
143647
|
}
|
|
143241
143648
|
);
|
|
@@ -143370,7 +143777,7 @@ var runWecomClient = (input) => {
|
|
|
143370
143777
|
};
|
|
143371
143778
|
|
|
143372
143779
|
// src/wecomMedia.ts
|
|
143373
|
-
import * as
|
|
143780
|
+
import * as NodeCrypto6 from "node:crypto";
|
|
143374
143781
|
|
|
143375
143782
|
// src/wecomPadding.ts
|
|
143376
143783
|
var PADDING_BLOCK = 32;
|
|
@@ -143401,7 +143808,7 @@ var decryptWecomMedia = (cipher, aesKey) => {
|
|
|
143401
143808
|
if (cipher.byteLength === 0 || cipher.byteLength % AES_BLOCK !== 0) {
|
|
143402
143809
|
throw new WecomDecryptError("the ciphertext is not a whole number of blocks");
|
|
143403
143810
|
}
|
|
143404
|
-
const decipher =
|
|
143811
|
+
const decipher = NodeCrypto6.createDecipheriv("aes-256-cbc", key, key.subarray(0, IV_BYTES));
|
|
143405
143812
|
decipher.setAutoPadding(false);
|
|
143406
143813
|
const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
|
|
143407
143814
|
const unpadded = stripPadding(plain);
|
|
@@ -143544,6 +143951,9 @@ ${kept}`;
|
|
|
143544
143951
|
botSecret,
|
|
143545
143952
|
handlers: {
|
|
143546
143953
|
onError: handlers.onError,
|
|
143954
|
+
onConnectionState: (state) => {
|
|
143955
|
+
if (!stopped) handlers.onConnectionState?.(state);
|
|
143956
|
+
},
|
|
143547
143957
|
onMessage: (message) => {
|
|
143548
143958
|
if (message.chatType !== "single") return;
|
|
143549
143959
|
client.rememberRequest(message.chatId, message.requestId);
|
|
@@ -143585,6 +143995,8 @@ ${kept}`;
|
|
|
143585
143995
|
try {
|
|
143586
143996
|
if (await runOnce()) backoff = BACKOFF_START_MS2;
|
|
143587
143997
|
} catch (error) {
|
|
143998
|
+
if (stopped) return;
|
|
143999
|
+
handlers.onConnectionState?.({ state: "failed", error, reason: "disconnected" });
|
|
143588
144000
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143589
144001
|
}
|
|
143590
144002
|
if (stopped) return;
|
|
@@ -143602,6 +144014,7 @@ ${kept}`;
|
|
|
143602
144014
|
}
|
|
143603
144015
|
};
|
|
143604
144016
|
},
|
|
144017
|
+
// 不看 `options.retry`:长连接的回执没有核实过的限流码,也没有幂等键,失败一律说不清发没发出去。
|
|
143605
144018
|
sendText: async (binding, text4) => {
|
|
143606
144019
|
const client = clients.get(binding.rowId);
|
|
143607
144020
|
if (client === void 0) {
|
|
@@ -143618,12 +144031,36 @@ ${kept}`;
|
|
|
143618
144031
|
detail: "missing bot id or secret in saved config"
|
|
143619
144032
|
};
|
|
143620
144033
|
}
|
|
144034
|
+
let stage = "websocket";
|
|
143621
144035
|
try {
|
|
143622
144036
|
const socket = await deps.connectSocket(endpoint);
|
|
143623
|
-
|
|
143624
|
-
|
|
144037
|
+
stage = "aibot_subscribe";
|
|
144038
|
+
let detail = "WeCom connection closed before subscribe ack";
|
|
144039
|
+
let closing;
|
|
144040
|
+
const client = runWecomClient({
|
|
144041
|
+
socket: {
|
|
144042
|
+
send: (data) => socket.send(data),
|
|
144043
|
+
messages: socket.messages,
|
|
144044
|
+
close: () => closing ??= socket.close()
|
|
144045
|
+
},
|
|
144046
|
+
botId,
|
|
144047
|
+
botSecret,
|
|
144048
|
+
handlers: {
|
|
144049
|
+
onMessage: () => void 0,
|
|
144050
|
+
onError: (error) => {
|
|
144051
|
+
detail = error;
|
|
144052
|
+
}
|
|
144053
|
+
}
|
|
144054
|
+
});
|
|
144055
|
+
try {
|
|
144056
|
+
const ok = await client.ready;
|
|
144057
|
+
return { stage, ok, detail: ok ? `subscription confirmed (${endpoint})` : detail };
|
|
144058
|
+
} finally {
|
|
144059
|
+
client.close();
|
|
144060
|
+
await closing;
|
|
144061
|
+
}
|
|
143625
144062
|
} catch (error) {
|
|
143626
|
-
return { stage
|
|
144063
|
+
return { stage, ok: false, detail: describeError(error) };
|
|
143627
144064
|
}
|
|
143628
144065
|
},
|
|
143629
144066
|
askApproval: async (binding, request) => {
|
|
@@ -143680,18 +144117,25 @@ ${kept}`;
|
|
|
143680
144117
|
// src/wecomAppApi.ts
|
|
143681
144118
|
var WECOM_APP_DEFAULT_API_BASE = "https://qyapi.weixin.qq.com";
|
|
143682
144119
|
var WECOM_APP_TEXT_MAX_BYTES = 2048;
|
|
144120
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
144121
|
+
var MEDIA_TIMEOUT_MS = 6e4;
|
|
143683
144122
|
var WecomAppApiError = class extends Error {
|
|
143684
144123
|
code;
|
|
143685
144124
|
providerCode;
|
|
143686
144125
|
hint;
|
|
144126
|
+
invalidUsers;
|
|
143687
144127
|
constructor(code2, message, extras) {
|
|
143688
144128
|
super(message);
|
|
143689
144129
|
this.name = "WecomAppApiError";
|
|
143690
144130
|
this.code = code2;
|
|
143691
144131
|
this.providerCode = extras?.providerCode;
|
|
143692
144132
|
this.hint = extras?.hint;
|
|
144133
|
+
this.invalidUsers = extras?.invalidUsers ?? [];
|
|
143693
144134
|
}
|
|
143694
144135
|
};
|
|
144136
|
+
var TOKEN_INVALID_CODES = /* @__PURE__ */ new Set(["40014", "42001"]);
|
|
144137
|
+
var RATE_LIMITED_CODES = /* @__PURE__ */ new Set(["45009", "45033"]);
|
|
144138
|
+
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
144139
|
var cleanString2 = (value2) => {
|
|
143696
144140
|
if (typeof value2 !== "string") return null;
|
|
143697
144141
|
const trimmed = value2.trim();
|
|
@@ -143727,6 +144171,7 @@ var splitUtf8ByBytes = (text4, maxBytes = WECOM_APP_TEXT_MAX_BYTES) => {
|
|
|
143727
144171
|
if (current.length > 0) chunks.push(current);
|
|
143728
144172
|
return chunks;
|
|
143729
144173
|
};
|
|
144174
|
+
var parseInvalidUsers = (value2) => typeof value2 === "string" ? value2.split("|").map((user) => user.trim()).filter(Boolean) : [];
|
|
143730
144175
|
var WecomAppApi = class {
|
|
143731
144176
|
corpId;
|
|
143732
144177
|
corpSecret;
|
|
@@ -143778,9 +144223,13 @@ var WecomAppApi = class {
|
|
|
143778
144223
|
async sendText(input) {
|
|
143779
144224
|
const chunks = splitUtf8ByBytes(input.content);
|
|
143780
144225
|
for (const chunk of chunks) {
|
|
143781
|
-
await
|
|
143782
|
-
|
|
143783
|
-
|
|
144226
|
+
await sendWithRetry(
|
|
144227
|
+
() => this.#send(
|
|
144228
|
+
{ touser: input.userId, msgtype: "text", text: { content: chunk } },
|
|
144229
|
+
input.signal
|
|
144230
|
+
),
|
|
144231
|
+
wecomAppSendVerdict,
|
|
144232
|
+
input.retry
|
|
143784
144233
|
);
|
|
143785
144234
|
}
|
|
143786
144235
|
}
|
|
@@ -143807,12 +144256,16 @@ var WecomAppApi = class {
|
|
|
143807
144256
|
new Blob([new Uint8Array(input.bytes)], { type: input.mimeType }),
|
|
143808
144257
|
input.name
|
|
143809
144258
|
);
|
|
143810
|
-
const payload = await this.#requestJson(
|
|
143811
|
-
|
|
143812
|
-
|
|
143813
|
-
|
|
143814
|
-
|
|
143815
|
-
|
|
144259
|
+
const payload = await this.#requestJson(
|
|
144260
|
+
url,
|
|
144261
|
+
{
|
|
144262
|
+
method: "POST",
|
|
144263
|
+
// 不设 content-type:multipart 的 boundary 要让 fetch 自己写。
|
|
144264
|
+
body: form,
|
|
144265
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
144266
|
+
},
|
|
144267
|
+
MEDIA_TIMEOUT_MS
|
|
144268
|
+
);
|
|
143816
144269
|
const mediaId = payload["media_id"];
|
|
143817
144270
|
if (typeof mediaId === "string" && mediaId !== "") return mediaId;
|
|
143818
144271
|
throw this.#apiError(Number(payload["errcode"] ?? -1), payload["errmsg"]);
|
|
@@ -143826,10 +144279,17 @@ var WecomAppApi = class {
|
|
|
143826
144279
|
const url = new URL("/cgi-bin/media/get", this.apiBaseUrl);
|
|
143827
144280
|
url.searchParams.set("access_token", token);
|
|
143828
144281
|
url.searchParams.set("media_id", input.mediaId);
|
|
143829
|
-
|
|
143830
|
-
|
|
143831
|
-
|
|
143832
|
-
|
|
144282
|
+
return this.#withRequest(
|
|
144283
|
+
url,
|
|
144284
|
+
{
|
|
144285
|
+
method: "GET",
|
|
144286
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
144287
|
+
},
|
|
144288
|
+
MEDIA_TIMEOUT_MS,
|
|
144289
|
+
(response) => this.#readMedia(response, input.maxBytes)
|
|
144290
|
+
);
|
|
144291
|
+
}
|
|
144292
|
+
async #readMedia(response, maxBytes) {
|
|
143833
144293
|
const contentType = response.headers.get("content-type") ?? "";
|
|
143834
144294
|
if (contentType.includes("application/json") || contentType.includes("text/plain")) {
|
|
143835
144295
|
let payload = {};
|
|
@@ -143849,7 +144309,7 @@ var WecomAppApi = class {
|
|
|
143849
144309
|
`WeCom media/get failed (HTTP ${String(response.status)}).`
|
|
143850
144310
|
);
|
|
143851
144311
|
}
|
|
143852
|
-
const bytes = await readBodyLimited(response,
|
|
144312
|
+
const bytes = await readBodyLimited(response, maxBytes);
|
|
143853
144313
|
const name = filenameFromContentDisposition(response.headers.get("content-disposition"));
|
|
143854
144314
|
const mimeType = contentType.split(";")[0]?.trim();
|
|
143855
144315
|
return {
|
|
@@ -143868,55 +144328,76 @@ var WecomAppApi = class {
|
|
|
143868
144328
|
body: JSON.stringify({ ...message, agentid: Number.parseInt(this.agentId, 10) }),
|
|
143869
144329
|
...signal === void 0 ? {} : { signal }
|
|
143870
144330
|
});
|
|
143871
|
-
const errcode = Number(payload["errcode"] ?? 0);
|
|
143872
|
-
if (errcode !== 0) throw this.#apiError(errcode, payload["errmsg"]);
|
|
143873
144331
|
const invalid = payload["invaliduser"];
|
|
143874
144332
|
if (typeof invalid === "string" && invalid.trim() !== "") {
|
|
143875
144333
|
throw new WecomAppApiError("invalid-user", `WeCom did not deliver to: ${invalid.trim()}.`, {
|
|
143876
144334
|
providerCode: "81013",
|
|
143877
|
-
hint: "
|
|
144335
|
+
hint: "wecomInvalidUser",
|
|
144336
|
+
invalidUsers: parseInvalidUsers(invalid)
|
|
143878
144337
|
});
|
|
143879
144338
|
}
|
|
143880
144339
|
}
|
|
143881
|
-
#apiError(errcode, errmsg) {
|
|
144340
|
+
#apiError(errcode, errmsg, invalidUsers) {
|
|
143882
144341
|
const code2 = String(errcode);
|
|
143883
144342
|
const message = typeof errmsg === "string" && errmsg !== "" ? `WeCom API failed (${code2}): ${errmsg}` : `WeCom API failed (${code2}).`;
|
|
143884
|
-
if (
|
|
144343
|
+
if (TOKEN_INVALID_CODES.has(code2)) this.invalidateToken();
|
|
143885
144344
|
if (errcode === 60020) {
|
|
143886
144345
|
return new WecomAppApiError("trusted-ip", "WeCom rejected the outbound IP (errcode 60020).", {
|
|
143887
144346
|
providerCode: code2,
|
|
143888
|
-
hint: "
|
|
144347
|
+
hint: "wecomTrustedIp"
|
|
143889
144348
|
});
|
|
143890
144349
|
}
|
|
143891
144350
|
if (errcode === 81013) {
|
|
143892
144351
|
return new WecomAppApiError("invalid-user", message, {
|
|
143893
144352
|
providerCode: code2,
|
|
143894
|
-
hint: "
|
|
144353
|
+
hint: "wecomInvalidUser",
|
|
144354
|
+
invalidUsers: parseInvalidUsers(invalidUsers)
|
|
143895
144355
|
});
|
|
143896
144356
|
}
|
|
143897
144357
|
if (errcode === 40056) {
|
|
143898
144358
|
return new WecomAppApiError("invalid-agent", message, {
|
|
143899
144359
|
providerCode: code2,
|
|
143900
|
-
hint: "
|
|
144360
|
+
hint: "wecomInvalidAgent"
|
|
143901
144361
|
});
|
|
143902
144362
|
}
|
|
143903
144363
|
return new WecomAppApiError("api-failed", message, { providerCode: code2 });
|
|
143904
144364
|
}
|
|
143905
|
-
async #
|
|
143906
|
-
const
|
|
143907
|
-
|
|
144365
|
+
async #withRequest(url, init, timeoutMs, read) {
|
|
144366
|
+
const deadline = new AbortController();
|
|
144367
|
+
const signal = AbortSignal.any([deadline.signal, ...init.signal ? [init.signal] : []]);
|
|
144368
|
+
const timeout = new WecomAppApiError(
|
|
144369
|
+
"timeout",
|
|
144370
|
+
`WeCom ${url.pathname} timed out after ${timeoutMs}ms.`
|
|
144371
|
+
);
|
|
144372
|
+
const timer = setTimeout(() => deadline.abort(timeout), timeoutMs);
|
|
143908
144373
|
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.");
|
|
144374
|
+
return await read(await this.#fetch(url, { ...init, signal }));
|
|
144375
|
+
} catch (error) {
|
|
144376
|
+
if (signal.aborted) throw signal.reason;
|
|
144377
|
+
throw error;
|
|
144378
|
+
} finally {
|
|
144379
|
+
clearTimeout(timer);
|
|
143918
144380
|
}
|
|
143919
|
-
|
|
144381
|
+
}
|
|
144382
|
+
async #requestJson(url, init, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
144383
|
+
return this.#withRequest(url, init, timeoutMs, async (response) => {
|
|
144384
|
+
let payload;
|
|
144385
|
+
try {
|
|
144386
|
+
payload = await response.json();
|
|
144387
|
+
} catch {
|
|
144388
|
+
throw new WecomAppApiError(
|
|
144389
|
+
"bad-response",
|
|
144390
|
+
`WeCom returned non-JSON (HTTP ${String(response.status)}).`
|
|
144391
|
+
);
|
|
144392
|
+
}
|
|
144393
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
144394
|
+
throw new WecomAppApiError("bad-response", "WeCom returned a non-object JSON body.");
|
|
144395
|
+
}
|
|
144396
|
+
const result = payload;
|
|
144397
|
+
const errcode = Number(result["errcode"] ?? 0);
|
|
144398
|
+
if (errcode !== 0) throw this.#apiError(errcode, result["errmsg"], result["invaliduser"]);
|
|
144399
|
+
return result;
|
|
144400
|
+
});
|
|
143920
144401
|
}
|
|
143921
144402
|
};
|
|
143922
144403
|
|
|
@@ -143925,7 +144406,7 @@ import * as NodeHttp2 from "node:http";
|
|
|
143925
144406
|
import * as NodeNet from "node:net";
|
|
143926
144407
|
|
|
143927
144408
|
// src/wecomAppCrypto.ts
|
|
143928
|
-
import * as
|
|
144409
|
+
import * as NodeCrypto7 from "node:crypto";
|
|
143929
144410
|
var AES_KEY_BYTES = 32;
|
|
143930
144411
|
var RANDOM_BYTES = 16;
|
|
143931
144412
|
var WecomAppCryptoError = class extends Error {
|
|
@@ -143948,13 +144429,13 @@ var timingSafeEqualString2 = (a, b) => {
|
|
|
143948
144429
|
const left = Buffer.from(a);
|
|
143949
144430
|
const right = Buffer.from(b);
|
|
143950
144431
|
if (left.length === 0 || left.length !== right.length) return false;
|
|
143951
|
-
return
|
|
144432
|
+
return NodeCrypto7.timingSafeEqual(left, right);
|
|
143952
144433
|
};
|
|
143953
|
-
var computeSignature = (token, timestamp, nonce, encrypt) =>
|
|
144434
|
+
var computeSignature = (token, timestamp, nonce, encrypt) => NodeCrypto7.createHash("sha1").update([token, timestamp, nonce, encrypt].sort().join("")).digest("hex");
|
|
143954
144435
|
var verifySignature = (token, timestamp, nonce, encrypt, signature) => timingSafeEqualString2(computeSignature(token, timestamp, nonce, encrypt), signature);
|
|
143955
144436
|
var decrypt = (encodingAESKey, encryptText, expectReceiveId) => {
|
|
143956
144437
|
const { key, iv } = parseEncodingAESKey(encodingAESKey);
|
|
143957
|
-
const decipher =
|
|
144438
|
+
const decipher = NodeCrypto7.createDecipheriv("aes-256-cbc", key, iv);
|
|
143958
144439
|
decipher.setAutoPadding(false);
|
|
143959
144440
|
let deciphered;
|
|
143960
144441
|
try {
|
|
@@ -144013,7 +144494,7 @@ var createWecomAppCrypto = (input) => {
|
|
|
144013
144494
|
decrypt: (encryptText) => decrypt(encodingAESKey, encryptText, corpId).message
|
|
144014
144495
|
};
|
|
144015
144496
|
};
|
|
144016
|
-
var deriveCallbackPathSecret = (token, encodingAESKey) =>
|
|
144497
|
+
var deriveCallbackPathSecret = (token, encodingAESKey) => NodeCrypto7.createHash("sha256").update(`${token.trim()}:${encodingAESKey.trim()}`).digest("hex").slice(0, 24);
|
|
144017
144498
|
|
|
144018
144499
|
// src/wecomAppParse.ts
|
|
144019
144500
|
var isRecord4 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
@@ -144460,6 +144941,7 @@ var WECOM_APP_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
|
144460
144941
|
var WECOM_APP_FILE_BYTES = 20 * 1024 * 1024;
|
|
144461
144942
|
var WECOM_APP_MIN_BYTES = 5;
|
|
144462
144943
|
var WECOM_APP_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png"]);
|
|
144944
|
+
var CREDENTIAL_ERROR_CODES = /* @__PURE__ */ new Set(["40001", "40013", "40014", "40056", "42001", "60020"]);
|
|
144463
144945
|
var joinCallbackUrl = (baseOrOrigin, path) => `${baseOrOrigin}${path}`;
|
|
144464
144946
|
var wecomAppPublicFields = (bot) => {
|
|
144465
144947
|
const c = wecomAppCredentials(bot);
|
|
@@ -144485,6 +144967,8 @@ var OUTBOUND_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
144485
144967
|
"wecomAppSecret",
|
|
144486
144968
|
"wecomAppApiBaseUrl"
|
|
144487
144969
|
]);
|
|
144970
|
+
var PROBE_BACKOFF_START_MS = 1e3;
|
|
144971
|
+
var PROBE_BACKOFF_MAX_MS = 6e4;
|
|
144488
144972
|
var makeWecomAppAdapter = (deps) => {
|
|
144489
144973
|
const apis = /* @__PURE__ */ new Map();
|
|
144490
144974
|
const newApi = (c) => new WecomAppApi({
|
|
@@ -144495,17 +144979,30 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144495
144979
|
fetch: deps.fetch
|
|
144496
144980
|
});
|
|
144497
144981
|
const callbackHub = makeWecomAppCallbackHub();
|
|
144498
|
-
const
|
|
144982
|
+
const connectionFor = (binding) => {
|
|
144499
144983
|
const api = apis.get(binding.rowId);
|
|
144500
144984
|
if (api === void 0) {
|
|
144501
144985
|
throw new ImNotConnectedError(`wecom-app bot '${binding.displayName}' is not connected.`);
|
|
144502
144986
|
}
|
|
144503
144987
|
return api;
|
|
144504
144988
|
};
|
|
144989
|
+
const deliver = async (binding, send) => {
|
|
144990
|
+
const connection = connectionFor(binding);
|
|
144991
|
+
try {
|
|
144992
|
+
await send(connection.api);
|
|
144993
|
+
connection.report({ state: "connected" });
|
|
144994
|
+
} catch (error) {
|
|
144995
|
+
if (error instanceof WecomAppApiError && error.providerCode !== void 0 && CREDENTIAL_ERROR_CODES.has(error.providerCode)) {
|
|
144996
|
+
connection.report({ state: "failed", error, reason: "credentials-rejected" });
|
|
144997
|
+
}
|
|
144998
|
+
throw error;
|
|
144999
|
+
}
|
|
145000
|
+
};
|
|
144505
145001
|
const sendMedia = async (binding, mediaType, file) => {
|
|
144506
|
-
|
|
144507
|
-
|
|
144508
|
-
|
|
145002
|
+
await deliver(binding, async (api) => {
|
|
145003
|
+
const mediaId = await api.uploadMedia({ mediaType, ...file });
|
|
145004
|
+
await api.sendMedia({ userId: binding.chatId, mediaType, mediaId });
|
|
145005
|
+
});
|
|
144509
145006
|
deps.log.info(`wecom-app delivered ${mediaType === "image" ? "an image" : "a file"}`, {
|
|
144510
145007
|
name: file.name
|
|
144511
145008
|
});
|
|
@@ -144556,6 +145053,19 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144556
145053
|
(issue) => issue.code !== "required" && issue.column !== "wecomAppEncodingAESKey"
|
|
144557
145054
|
),
|
|
144558
145055
|
publicFields: wecomAppPublicFields,
|
|
145056
|
+
// 收件人不在应用可见范围(invaliduser / 81013):说清是谁、为什么、怎么办。
|
|
145057
|
+
describeDeliveryRejection: (error, binding) => {
|
|
145058
|
+
if (!(error instanceof WecomAppApiError) || error.code !== "invalid-user") return void 0;
|
|
145059
|
+
const recipients = [
|
|
145060
|
+
...new Set(error.invalidUsers.length > 0 ? error.invalidUsers : [binding.chatId])
|
|
145061
|
+
].sort();
|
|
145062
|
+
return {
|
|
145063
|
+
recipients,
|
|
145064
|
+
summary: deps.translate("reply.wecomDeliveryFailed", { recipient: recipients.join(", ") }),
|
|
145065
|
+
hint: error.hint,
|
|
145066
|
+
providerCode: error.providerCode
|
|
145067
|
+
};
|
|
145068
|
+
},
|
|
144559
145069
|
/**
|
|
144560
145070
|
* 自建应用是回调式渠道:入站由企微推到我们的 listener,没有可探的长连接;能探的是出站
|
|
144561
145071
|
* 凭据——和真实发送同一条路,向 gettoken 换一次 access_token。
|
|
@@ -144586,7 +145096,12 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144586
145096
|
detail: "access_token ok (callback reachability is not probed)"
|
|
144587
145097
|
};
|
|
144588
145098
|
} catch (error) {
|
|
144589
|
-
return {
|
|
145099
|
+
return {
|
|
145100
|
+
stage: "gettoken",
|
|
145101
|
+
ok: false,
|
|
145102
|
+
detail: describeError(error),
|
|
145103
|
+
...error instanceof WecomAppApiError && error.hint !== void 0 ? { hint: error.hint } : {}
|
|
145104
|
+
};
|
|
144590
145105
|
}
|
|
144591
145106
|
},
|
|
144592
145107
|
connect: async (bot, handlers) => {
|
|
@@ -144600,7 +145115,14 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144600
145115
|
const lease = await callbackHub.acquire({ host: config2.listenHost, port: config2.listenPort });
|
|
144601
145116
|
const server = lease.server;
|
|
144602
145117
|
const api = newApi(c);
|
|
144603
|
-
|
|
145118
|
+
let closed = false;
|
|
145119
|
+
const report = (state) => {
|
|
145120
|
+
if (closed) return;
|
|
145121
|
+
handlers.onConnectionState?.(
|
|
145122
|
+
state.state === "failed" && state.error instanceof WecomAppApiError && state.error.hint !== void 0 ? { ...state, hint: state.error.hint } : state
|
|
145123
|
+
);
|
|
145124
|
+
};
|
|
145125
|
+
apis.set(bot.rowId, { api, report });
|
|
144604
145126
|
let path;
|
|
144605
145127
|
try {
|
|
144606
145128
|
path = server.register({
|
|
@@ -144616,7 +145138,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144616
145138
|
await lease.release();
|
|
144617
145139
|
throw error;
|
|
144618
145140
|
}
|
|
144619
|
-
deps.log.info("wecom-app
|
|
145141
|
+
deps.log.info("wecom-app callback listening", {
|
|
144620
145142
|
bot: bot.displayName,
|
|
144621
145143
|
callbackPath: path,
|
|
144622
145144
|
callbackUrl: joinCallbackUrl(
|
|
@@ -144625,8 +145147,26 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144625
145147
|
),
|
|
144626
145148
|
listen: `${server.host}:${String(server.port)}`
|
|
144627
145149
|
});
|
|
145150
|
+
const probe2 = new AbortController();
|
|
145151
|
+
void (async () => {
|
|
145152
|
+
let backoff = PROBE_BACKOFF_START_MS;
|
|
145153
|
+
while (!probe2.signal.aborted) {
|
|
145154
|
+
try {
|
|
145155
|
+
await api.getAccessToken(probe2.signal);
|
|
145156
|
+
report({ state: "connected" });
|
|
145157
|
+
return;
|
|
145158
|
+
} catch (error) {
|
|
145159
|
+
if (probe2.signal.aborted) return;
|
|
145160
|
+
report({ state: "failed", error, reason: "token-unavailable" });
|
|
145161
|
+
await (deps.sleep ?? abortableSleep)(backoff, probe2.signal).catch(() => void 0);
|
|
145162
|
+
backoff = Math.min(backoff * 2, PROBE_BACKOFF_MAX_MS);
|
|
145163
|
+
}
|
|
145164
|
+
}
|
|
145165
|
+
})();
|
|
144628
145166
|
return {
|
|
144629
145167
|
close: () => {
|
|
145168
|
+
closed = true;
|
|
145169
|
+
probe2.abort();
|
|
144630
145170
|
server.unregister(bot.rowId);
|
|
144631
145171
|
apis.delete(bot.rowId);
|
|
144632
145172
|
void lease.release().catch((error) => {
|
|
@@ -144642,15 +145182,18 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144642
145182
|
if (options?.targetKind === "group") {
|
|
144643
145183
|
throw new Error("wecom-app can only deliver to a user; group targets are not supported.");
|
|
144644
145184
|
}
|
|
144645
|
-
await
|
|
145185
|
+
await deliver(
|
|
145186
|
+
binding,
|
|
145187
|
+
(api) => api.sendText({ userId: binding.chatId, content: text4, retry: options?.retry })
|
|
145188
|
+
);
|
|
144646
145189
|
},
|
|
144647
145190
|
// D376:企业微信自建应用没有表情回执也没有流式,开工时先回一句文字,结论照常另发。没有要撤的
|
|
144648
145191
|
// 东西,回 null;发不出去由核心记一条 warn,不拦这一轮。
|
|
144649
145192
|
acknowledge: async (binding) => {
|
|
144650
|
-
await
|
|
144651
|
-
|
|
144652
|
-
content: deps.translate("reply.received")
|
|
144653
|
-
|
|
145193
|
+
await deliver(
|
|
145194
|
+
binding,
|
|
145195
|
+
(api) => api.sendText({ userId: binding.chatId, content: deps.translate("reply.received") })
|
|
145196
|
+
);
|
|
144654
145197
|
return null;
|
|
144655
145198
|
},
|
|
144656
145199
|
sendImage: (binding, image2) => sendMedia(binding, "image", image2),
|
|
@@ -144661,11 +145204,16 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144661
145204
|
minBytes: WECOM_APP_MIN_BYTES
|
|
144662
145205
|
},
|
|
144663
145206
|
imageMimeTypes: WECOM_APP_IMAGE_TYPES,
|
|
144664
|
-
fetchResource: (binding, input) =>
|
|
145207
|
+
fetchResource: (binding, input) => connectionFor(binding).api.downloadMedia({
|
|
145208
|
+
mediaId: input.resource.key,
|
|
145209
|
+
maxBytes: input.maxBytes
|
|
145210
|
+
}),
|
|
144665
145211
|
askApproval: async (binding, request) => {
|
|
144666
|
-
|
|
144667
|
-
|
|
144668
|
-
|
|
145212
|
+
if (!apis.has(binding.rowId)) return null;
|
|
145213
|
+
await deliver(
|
|
145214
|
+
binding,
|
|
145215
|
+
(api) => api.sendText({ userId: binding.chatId, content: numberedApprovalText(request) })
|
|
145216
|
+
);
|
|
144669
145217
|
return null;
|
|
144670
145218
|
},
|
|
144671
145219
|
answerByText: (_binding, text4, options) => answerByNumber(text4, options)
|
|
@@ -144675,7 +145223,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144675
145223
|
// src/index.ts
|
|
144676
145224
|
var imPlugin = definePlugin({
|
|
144677
145225
|
id: "im",
|
|
144678
|
-
apiVersion:
|
|
145226
|
+
apiVersion: 18,
|
|
144679
145227
|
activate: async (ctx) => {
|
|
144680
145228
|
const { t, setLanguage } = makeLiveTranslate(
|
|
144681
145229
|
parseReplyLanguage(ctx.config[REPLY_LANGUAGE_FIELD])
|
|
@@ -144736,7 +145284,6 @@ var imPlugin = definePlugin({
|
|
|
144736
145284
|
const updateSettings = makeSettingsUpdater(ctx.settings);
|
|
144737
145285
|
registerLarkSetup(ctx, { updateSettings });
|
|
144738
145286
|
registerSettingsRowActions(ctx, adapters);
|
|
144739
|
-
registerSettingsCommands(ctx, adapters, updateSettings);
|
|
144740
145287
|
const core = makeImCore({
|
|
144741
145288
|
host: {
|
|
144742
145289
|
log: ctx.log,
|
|
@@ -144771,7 +145318,7 @@ var imPlugin = definePlugin({
|
|
|
144771
145318
|
);
|
|
144772
145319
|
const teams = makeTeamGoals(core);
|
|
144773
145320
|
const notifications = makeNotifications(core, {
|
|
144774
|
-
|
|
145321
|
+
takeWorking: acknowledgements.takeWorking,
|
|
144775
145322
|
replyCards,
|
|
144776
145323
|
failures,
|
|
144777
145324
|
conclusions,
|
|
@@ -144847,6 +145394,7 @@ var imPlugin = definePlugin({
|
|
|
144847
145394
|
]
|
|
144848
145395
|
});
|
|
144849
145396
|
const connections = startConnections(core, inbound.inboundHandlersFor);
|
|
145397
|
+
registerSettingsCommands(ctx, adapters, updateSettings, connections);
|
|
144850
145398
|
const { bots, connecting } = connections.reconcile();
|
|
144851
145399
|
const deliveryHttp = makeDeliveryHttp({
|
|
144852
145400
|
// 每次请求按此刻的设置找目标:投递目标表改了不用重开监听。
|