@ycodium-ai/plugin-im 0.2.2962 → 0.2.2966
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -8
- package/dist/index.js +2539 -361
- package/locales/en.json +168 -8
- package/locales/zh-CN.json +168 -8
- package/package.json +12 -4
package/dist/index.js
CHANGED
|
@@ -22669,24 +22669,24 @@ var require_sender = __commonJS({
|
|
|
22669
22669
|
* @public
|
|
22670
22670
|
*/
|
|
22671
22671
|
ping(data, mask, cb) {
|
|
22672
|
-
let
|
|
22672
|
+
let byteLength2;
|
|
22673
22673
|
let readOnly;
|
|
22674
22674
|
if (typeof data === "string") {
|
|
22675
|
-
|
|
22675
|
+
byteLength2 = Buffer.byteLength(data);
|
|
22676
22676
|
readOnly = false;
|
|
22677
22677
|
} else if (isBlob(data)) {
|
|
22678
|
-
|
|
22678
|
+
byteLength2 = data.size;
|
|
22679
22679
|
readOnly = false;
|
|
22680
22680
|
} else {
|
|
22681
22681
|
data = toBuffer(data);
|
|
22682
|
-
|
|
22682
|
+
byteLength2 = data.length;
|
|
22683
22683
|
readOnly = toBuffer.readOnly;
|
|
22684
22684
|
}
|
|
22685
|
-
if (
|
|
22685
|
+
if (byteLength2 > 125) {
|
|
22686
22686
|
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
22687
22687
|
}
|
|
22688
22688
|
const options = {
|
|
22689
|
-
[kByteLength]:
|
|
22689
|
+
[kByteLength]: byteLength2,
|
|
22690
22690
|
fin: true,
|
|
22691
22691
|
generateMask: this._generateMask,
|
|
22692
22692
|
mask,
|
|
@@ -22716,24 +22716,24 @@ var require_sender = __commonJS({
|
|
|
22716
22716
|
* @public
|
|
22717
22717
|
*/
|
|
22718
22718
|
pong(data, mask, cb) {
|
|
22719
|
-
let
|
|
22719
|
+
let byteLength2;
|
|
22720
22720
|
let readOnly;
|
|
22721
22721
|
if (typeof data === "string") {
|
|
22722
|
-
|
|
22722
|
+
byteLength2 = Buffer.byteLength(data);
|
|
22723
22723
|
readOnly = false;
|
|
22724
22724
|
} else if (isBlob(data)) {
|
|
22725
|
-
|
|
22725
|
+
byteLength2 = data.size;
|
|
22726
22726
|
readOnly = false;
|
|
22727
22727
|
} else {
|
|
22728
22728
|
data = toBuffer(data);
|
|
22729
|
-
|
|
22729
|
+
byteLength2 = data.length;
|
|
22730
22730
|
readOnly = toBuffer.readOnly;
|
|
22731
22731
|
}
|
|
22732
|
-
if (
|
|
22732
|
+
if (byteLength2 > 125) {
|
|
22733
22733
|
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
22734
22734
|
}
|
|
22735
22735
|
const options = {
|
|
22736
|
-
[kByteLength]:
|
|
22736
|
+
[kByteLength]: byteLength2,
|
|
22737
22737
|
fin: true,
|
|
22738
22738
|
generateMask: this._generateMask,
|
|
22739
22739
|
mask,
|
|
@@ -22774,23 +22774,23 @@ var require_sender = __commonJS({
|
|
|
22774
22774
|
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
|
22775
22775
|
let opcode = options.binary ? 2 : 1;
|
|
22776
22776
|
let rsv1 = options.compress;
|
|
22777
|
-
let
|
|
22777
|
+
let byteLength2;
|
|
22778
22778
|
let readOnly;
|
|
22779
22779
|
if (typeof data === "string") {
|
|
22780
|
-
|
|
22780
|
+
byteLength2 = Buffer.byteLength(data);
|
|
22781
22781
|
readOnly = false;
|
|
22782
22782
|
} else if (isBlob(data)) {
|
|
22783
|
-
|
|
22783
|
+
byteLength2 = data.size;
|
|
22784
22784
|
readOnly = false;
|
|
22785
22785
|
} else {
|
|
22786
22786
|
data = toBuffer(data);
|
|
22787
|
-
|
|
22787
|
+
byteLength2 = data.length;
|
|
22788
22788
|
readOnly = toBuffer.readOnly;
|
|
22789
22789
|
}
|
|
22790
22790
|
if (this._firstFragment) {
|
|
22791
22791
|
this._firstFragment = false;
|
|
22792
22792
|
if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) {
|
|
22793
|
-
rsv1 =
|
|
22793
|
+
rsv1 = byteLength2 >= perMessageDeflate._threshold;
|
|
22794
22794
|
}
|
|
22795
22795
|
this._compress = rsv1;
|
|
22796
22796
|
} else {
|
|
@@ -22799,7 +22799,7 @@ var require_sender = __commonJS({
|
|
|
22799
22799
|
}
|
|
22800
22800
|
if (options.fin) this._firstFragment = true;
|
|
22801
22801
|
const opts = {
|
|
22802
|
-
[kByteLength]:
|
|
22802
|
+
[kByteLength]: byteLength2,
|
|
22803
22803
|
fin: options.fin,
|
|
22804
22804
|
generateMask: this._generateMask,
|
|
22805
22805
|
mask: options.mask,
|
|
@@ -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: createHash3 } = __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 = createHash3("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: createHash3 } = __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 = createHash3("sha1").update(key + GUID).digest("base64");
|
|
24708
24708
|
const headers = [
|
|
24709
24709
|
"HTTP/1.1 101 Switching Protocols",
|
|
24710
24710
|
"Upgrade: websocket",
|
|
@@ -126159,11 +126159,67 @@ ${lines.join("\n")}
|
|
|
126159
126159
|
// src/index.ts
|
|
126160
126160
|
import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
126161
126161
|
|
|
126162
|
+
// src/errors.ts
|
|
126163
|
+
var describeError = (error) => error instanceof Error ? error.message : String(error);
|
|
126164
|
+
|
|
126165
|
+
// src/acknowledgements.ts
|
|
126166
|
+
var makeAcknowledgements = (core) => {
|
|
126167
|
+
const { host, adapterFor } = core;
|
|
126168
|
+
const acknowledgements = /* @__PURE__ */ new Map();
|
|
126169
|
+
const markWorking = async (binding, threadId, messageId) => {
|
|
126170
|
+
const adapter = adapterFor(binding.platform);
|
|
126171
|
+
if (adapter?.acknowledge === void 0) return;
|
|
126172
|
+
try {
|
|
126173
|
+
const ackId = await adapter.acknowledge(binding, messageId);
|
|
126174
|
+
if (ackId === null) return;
|
|
126175
|
+
const pending = acknowledgements.get(threadId) ?? [];
|
|
126176
|
+
pending.push({ messageId, ackId });
|
|
126177
|
+
acknowledgements.set(threadId, pending);
|
|
126178
|
+
host.log.info("chat acknowledged a message", { threadId, messageId });
|
|
126179
|
+
} catch (error) {
|
|
126180
|
+
host.log.warn("chat could not acknowledge the message", { error: describeError(error) });
|
|
126181
|
+
}
|
|
126182
|
+
};
|
|
126183
|
+
const clearWorking = async (binding, threadId) => {
|
|
126184
|
+
const adapter = adapterFor(binding.platform);
|
|
126185
|
+
const pending = acknowledgements.get(threadId);
|
|
126186
|
+
if (pending === void 0 || adapter?.clearAcknowledge === void 0) return;
|
|
126187
|
+
acknowledgements.delete(threadId);
|
|
126188
|
+
for (const entry of pending) {
|
|
126189
|
+
try {
|
|
126190
|
+
await adapter.clearAcknowledge(binding, entry.messageId, entry.ackId);
|
|
126191
|
+
} catch (error) {
|
|
126192
|
+
host.log.warn("chat could not clear the acknowledgement", {
|
|
126193
|
+
error: describeError(error)
|
|
126194
|
+
});
|
|
126195
|
+
}
|
|
126196
|
+
}
|
|
126197
|
+
};
|
|
126198
|
+
return { markWorking, clearWorking };
|
|
126199
|
+
};
|
|
126200
|
+
|
|
126162
126201
|
// src/botConfig.ts
|
|
126163
126202
|
import {
|
|
126164
126203
|
PLUGIN_PROXY_DIRECT
|
|
126165
126204
|
} from "@ycodium-ai/plugin-api";
|
|
126166
126205
|
|
|
126206
|
+
// src/attachedThreads.ts
|
|
126207
|
+
var ATTACHED_THREAD_LIMIT = 200;
|
|
126208
|
+
var attachedKey = (chatKey, threadId) => `${chatKey}
|
|
126209
|
+
${threadId}`;
|
|
126210
|
+
var hasAttached = (state, chatKey, threadId) => state.attached?.[attachedKey(chatKey, threadId)] !== void 0;
|
|
126211
|
+
var attachedOnChat = (state, chatKey, projectId) => Object.values(state.attached ?? {}).filter(
|
|
126212
|
+
(record2) => record2.chatKey === chatKey && record2.projectId === projectId
|
|
126213
|
+
);
|
|
126214
|
+
var nonEmpty = (value) => typeof value === "string" && value !== "" ? value : null;
|
|
126215
|
+
var readAttached = (data) => {
|
|
126216
|
+
const chatKey = nonEmpty(data["chatKey"]);
|
|
126217
|
+
const threadId = nonEmpty(data["threadId"]);
|
|
126218
|
+
const projectId = nonEmpty(data["projectId"]);
|
|
126219
|
+
const attachedAt = nonEmpty(data["attachedAt"]);
|
|
126220
|
+
return chatKey === null || threadId === null || projectId === null || attachedAt === null ? null : { chatKey, threadId, projectId, attachedAt };
|
|
126221
|
+
};
|
|
126222
|
+
|
|
126167
126223
|
// src/chatState.ts
|
|
126168
126224
|
var IM_STATE_KEY = "chats";
|
|
126169
126225
|
var IM_STATE_VERSION = 1;
|
|
@@ -126188,7 +126244,8 @@ var DEFAULT_CHAT_RECORD = {
|
|
|
126188
126244
|
threadId: null,
|
|
126189
126245
|
target: { kind: "thread" },
|
|
126190
126246
|
model: null,
|
|
126191
|
-
plan: false
|
|
126247
|
+
plan: false,
|
|
126248
|
+
chatKind: null
|
|
126192
126249
|
};
|
|
126193
126250
|
var chatOf = (state, chatKey) => ({
|
|
126194
126251
|
...DEFAULT_CHAT_RECORD,
|
|
@@ -126232,11 +126289,11 @@ var openQueueOfThread = (state, threadId) => Object.values(queueOf(state)).filte
|
|
|
126232
126289
|
(record2) => record2.threadId === threadId && record2.settled === void 0
|
|
126233
126290
|
);
|
|
126234
126291
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
126235
|
-
var
|
|
126292
|
+
var nonEmpty2 = (value) => typeof value === "string" && value !== "" ? value : null;
|
|
126236
126293
|
var readTarget = (value) => {
|
|
126237
126294
|
if (!isRecord(value)) return null;
|
|
126238
126295
|
if (value["kind"] === "thread") return { kind: "thread" };
|
|
126239
|
-
const teamId =
|
|
126296
|
+
const teamId = nonEmpty2(value["teamId"]);
|
|
126240
126297
|
return value["kind"] === "team" && teamId !== null ? { kind: "team", teamId } : null;
|
|
126241
126298
|
};
|
|
126242
126299
|
var readButtons = (value) => {
|
|
@@ -126244,8 +126301,8 @@ var readButtons = (value) => {
|
|
|
126244
126301
|
const buttons = [];
|
|
126245
126302
|
for (const entry of value) {
|
|
126246
126303
|
if (!isRecord(entry)) return null;
|
|
126247
|
-
const token =
|
|
126248
|
-
const decision =
|
|
126304
|
+
const token = nonEmpty2(entry["token"]);
|
|
126305
|
+
const decision = nonEmpty2(entry["decision"]);
|
|
126249
126306
|
const label = typeof entry["label"] === "string" ? entry["label"] : null;
|
|
126250
126307
|
if (token === null || decision === null || label === null) return null;
|
|
126251
126308
|
buttons.push({ token, label, decision });
|
|
@@ -126257,8 +126314,8 @@ var readQuestionOptions = (value) => {
|
|
|
126257
126314
|
const options = [];
|
|
126258
126315
|
for (const entry of value) {
|
|
126259
126316
|
if (!isRecord(entry)) return null;
|
|
126260
|
-
const token =
|
|
126261
|
-
const optionValue =
|
|
126317
|
+
const token = nonEmpty2(entry["token"]);
|
|
126318
|
+
const optionValue = nonEmpty2(entry["value"]);
|
|
126262
126319
|
if (token === null || optionValue === null || typeof entry["label"] !== "string") return null;
|
|
126263
126320
|
const planAction = entry["planAction"];
|
|
126264
126321
|
options.push({
|
|
@@ -126276,10 +126333,10 @@ var readQuestionItems = (value) => {
|
|
|
126276
126333
|
const items = [];
|
|
126277
126334
|
for (const entry of value) {
|
|
126278
126335
|
if (!isRecord(entry)) return null;
|
|
126279
|
-
const id =
|
|
126336
|
+
const id = nonEmpty2(entry["id"]);
|
|
126280
126337
|
const options = readQuestionOptions(entry["options"]);
|
|
126281
126338
|
if (id === null || options === null || typeof entry["question"] !== "string") return null;
|
|
126282
|
-
const submitToken =
|
|
126339
|
+
const submitToken = nonEmpty2(entry["submitToken"]);
|
|
126283
126340
|
items.push({
|
|
126284
126341
|
id,
|
|
126285
126342
|
header: typeof entry["header"] === "string" ? entry["header"] : "",
|
|
@@ -126334,29 +126391,33 @@ var foldImEvent = (state, name, data) => {
|
|
|
126334
126391
|
if (!isRecord(data)) return state;
|
|
126335
126392
|
switch (name) {
|
|
126336
126393
|
case "chat-updated": {
|
|
126337
|
-
const chatKey =
|
|
126394
|
+
const chatKey = nonEmpty2(data["chatKey"]);
|
|
126338
126395
|
if (chatKey === null) return state;
|
|
126339
126396
|
const patch = {};
|
|
126340
126397
|
if (data["projectId"] === null) patch.projectId = null;
|
|
126341
|
-
else if (
|
|
126398
|
+
else if (nonEmpty2(data["projectId"]) !== null) patch.projectId = nonEmpty2(data["projectId"]);
|
|
126342
126399
|
if (data["threadId"] === null) patch.threadId = null;
|
|
126343
|
-
else if (
|
|
126400
|
+
else if (nonEmpty2(data["threadId"]) !== null) patch.threadId = nonEmpty2(data["threadId"]);
|
|
126344
126401
|
const target = readTarget(data["target"]);
|
|
126345
126402
|
if (target !== null) patch.target = target;
|
|
126346
126403
|
if (data["model"] === null) patch.model = null;
|
|
126347
126404
|
else if (isRecord(data["model"])) {
|
|
126348
|
-
const instanceId =
|
|
126349
|
-
const model =
|
|
126405
|
+
const instanceId = nonEmpty2(data["model"]["instanceId"]);
|
|
126406
|
+
const model = nonEmpty2(data["model"]["model"]);
|
|
126350
126407
|
if (instanceId !== null && model !== null) patch.model = { instanceId, model };
|
|
126351
126408
|
}
|
|
126352
126409
|
if (typeof data["plan"] === "boolean") patch.plan = data["plan"];
|
|
126410
|
+
if (data["chatKind"] === null) patch.chatKind = null;
|
|
126411
|
+
else if (data["chatKind"] === "private" || data["chatKind"] === "group") {
|
|
126412
|
+
patch.chatKind = data["chatKind"];
|
|
126413
|
+
}
|
|
126353
126414
|
return withChat(state, chatKey, patch);
|
|
126354
126415
|
}
|
|
126355
126416
|
case "thread-opened": {
|
|
126356
|
-
const chatKey =
|
|
126357
|
-
const threadId =
|
|
126358
|
-
const projectId =
|
|
126359
|
-
const openedAt =
|
|
126417
|
+
const chatKey = nonEmpty2(data["chatKey"]);
|
|
126418
|
+
const threadId = nonEmpty2(data["threadId"]);
|
|
126419
|
+
const projectId = nonEmpty2(data["projectId"]);
|
|
126420
|
+
const openedAt = nonEmpty2(data["openedAt"]);
|
|
126360
126421
|
if (chatKey === null || threadId === null || projectId === null || openedAt === null) {
|
|
126361
126422
|
return state;
|
|
126362
126423
|
}
|
|
@@ -126369,10 +126430,17 @@ var foldImEvent = (state, name, data) => {
|
|
|
126369
126430
|
)
|
|
126370
126431
|
};
|
|
126371
126432
|
}
|
|
126433
|
+
case "thread-attached": {
|
|
126434
|
+
const record2 = readAttached(data);
|
|
126435
|
+
if (record2 === null) return state;
|
|
126436
|
+
const key = attachedKey(record2.chatKey, record2.threadId);
|
|
126437
|
+
const attached = { ...state.attached, [key]: record2 };
|
|
126438
|
+
return { ...state, attached: keepNewest(attached, ATTACHED_THREAD_LIMIT) };
|
|
126439
|
+
}
|
|
126372
126440
|
case "approval-offered": {
|
|
126373
|
-
const chatKey =
|
|
126374
|
-
const threadId =
|
|
126375
|
-
const requestId =
|
|
126441
|
+
const chatKey = nonEmpty2(data["chatKey"]);
|
|
126442
|
+
const threadId = nonEmpty2(data["threadId"]);
|
|
126443
|
+
const requestId = nonEmpty2(data["requestId"]);
|
|
126376
126444
|
const options = readButtons(data["options"]);
|
|
126377
126445
|
if (chatKey === null || threadId === null || requestId === null || options === null) {
|
|
126378
126446
|
return state;
|
|
@@ -126389,7 +126457,7 @@ var foldImEvent = (state, name, data) => {
|
|
|
126389
126457
|
};
|
|
126390
126458
|
}
|
|
126391
126459
|
case "approval-resolved": {
|
|
126392
|
-
const requestId =
|
|
126460
|
+
const requestId = nonEmpty2(data["requestId"]);
|
|
126393
126461
|
const existing = requestId === null ? void 0 : state.approvals[requestId];
|
|
126394
126462
|
if (requestId === null || existing === void 0) return state;
|
|
126395
126463
|
return {
|
|
@@ -126398,8 +126466,8 @@ var foldImEvent = (state, name, data) => {
|
|
|
126398
126466
|
};
|
|
126399
126467
|
}
|
|
126400
126468
|
case "approval-carded": {
|
|
126401
|
-
const requestId =
|
|
126402
|
-
const cardId =
|
|
126469
|
+
const requestId = nonEmpty2(data["requestId"]);
|
|
126470
|
+
const cardId = nonEmpty2(data["cardId"]);
|
|
126403
126471
|
const existing = requestId === null ? void 0 : state.approvals[requestId];
|
|
126404
126472
|
if (requestId === null || cardId === null || existing === void 0) return state;
|
|
126405
126473
|
return {
|
|
@@ -126408,9 +126476,9 @@ var foldImEvent = (state, name, data) => {
|
|
|
126408
126476
|
};
|
|
126409
126477
|
}
|
|
126410
126478
|
case "question-offered": {
|
|
126411
|
-
const chatKey =
|
|
126412
|
-
const threadId =
|
|
126413
|
-
const requestId =
|
|
126479
|
+
const chatKey = nonEmpty2(data["chatKey"]);
|
|
126480
|
+
const threadId = nonEmpty2(data["threadId"]);
|
|
126481
|
+
const requestId = nonEmpty2(data["requestId"]);
|
|
126414
126482
|
const items = readQuestionItems(data["items"]);
|
|
126415
126483
|
if (chatKey === null || threadId === null || requestId === null || items === null) {
|
|
126416
126484
|
return state;
|
|
@@ -126427,12 +126495,12 @@ var foldImEvent = (state, name, data) => {
|
|
|
126427
126495
|
};
|
|
126428
126496
|
}
|
|
126429
126497
|
case "question-carded": {
|
|
126430
|
-
const cardId =
|
|
126498
|
+
const cardId = nonEmpty2(data["cardId"]);
|
|
126431
126499
|
if (cardId === null) return state;
|
|
126432
126500
|
return withQuestionItem(
|
|
126433
126501
|
state,
|
|
126434
|
-
|
|
126435
|
-
|
|
126502
|
+
nonEmpty2(data["requestId"]),
|
|
126503
|
+
nonEmpty2(data["questionId"]),
|
|
126436
126504
|
(item) => ({ ...item, cardId })
|
|
126437
126505
|
);
|
|
126438
126506
|
}
|
|
@@ -126441,8 +126509,8 @@ var foldImEvent = (state, name, data) => {
|
|
|
126441
126509
|
if (!Array.isArray(selected)) return state;
|
|
126442
126510
|
return withQuestionItem(
|
|
126443
126511
|
state,
|
|
126444
|
-
|
|
126445
|
-
|
|
126512
|
+
nonEmpty2(data["requestId"]),
|
|
126513
|
+
nonEmpty2(data["questionId"]),
|
|
126446
126514
|
(item) => ({ ...item, selected })
|
|
126447
126515
|
);
|
|
126448
126516
|
}
|
|
@@ -126452,13 +126520,13 @@ var foldImEvent = (state, name, data) => {
|
|
|
126452
126520
|
const label = typeof data["label"] === "string" ? data["label"] : "";
|
|
126453
126521
|
return withQuestionItem(
|
|
126454
126522
|
state,
|
|
126455
|
-
|
|
126456
|
-
|
|
126523
|
+
nonEmpty2(data["requestId"]),
|
|
126524
|
+
nonEmpty2(data["questionId"]),
|
|
126457
126525
|
(item) => ({ ...item, answer, answerLabel: label })
|
|
126458
126526
|
);
|
|
126459
126527
|
}
|
|
126460
126528
|
case "question-resolved": {
|
|
126461
|
-
const requestId =
|
|
126529
|
+
const requestId = nonEmpty2(data["requestId"]);
|
|
126462
126530
|
const existing = requestId === null ? void 0 : questionsOf(state)[requestId];
|
|
126463
126531
|
if (requestId === null || existing === void 0) return state;
|
|
126464
126532
|
const ending = data["ending"];
|
|
@@ -126475,12 +126543,12 @@ var foldImEvent = (state, name, data) => {
|
|
|
126475
126543
|
};
|
|
126476
126544
|
}
|
|
126477
126545
|
case "queue-offered": {
|
|
126478
|
-
const chatKey =
|
|
126479
|
-
const threadId =
|
|
126480
|
-
const intentId =
|
|
126481
|
-
const steerToken =
|
|
126482
|
-
const withdrawToken =
|
|
126483
|
-
const resumeToken =
|
|
126546
|
+
const chatKey = nonEmpty2(data["chatKey"]);
|
|
126547
|
+
const threadId = nonEmpty2(data["threadId"]);
|
|
126548
|
+
const intentId = nonEmpty2(data["intentId"]);
|
|
126549
|
+
const steerToken = nonEmpty2(data["steerToken"]);
|
|
126550
|
+
const withdrawToken = nonEmpty2(data["withdrawToken"]);
|
|
126551
|
+
const resumeToken = nonEmpty2(data["resumeToken"]);
|
|
126484
126552
|
if (chatKey === null || threadId === null || intentId === null || steerToken === null || withdrawToken === null || resumeToken === null) {
|
|
126485
126553
|
return state;
|
|
126486
126554
|
}
|
|
@@ -126505,30 +126573,30 @@ var foldImEvent = (state, name, data) => {
|
|
|
126505
126573
|
};
|
|
126506
126574
|
}
|
|
126507
126575
|
case "queue-carded": {
|
|
126508
|
-
const cardId =
|
|
126576
|
+
const cardId = nonEmpty2(data["cardId"]);
|
|
126509
126577
|
if (cardId === null) return state;
|
|
126510
|
-
return withQueueRecord(state,
|
|
126578
|
+
return withQueueRecord(state, nonEmpty2(data["intentId"]), (record2) => ({
|
|
126511
126579
|
...record2,
|
|
126512
126580
|
cardId
|
|
126513
126581
|
}));
|
|
126514
126582
|
}
|
|
126515
126583
|
case "queue-steering":
|
|
126516
|
-
return withQueueRecord(state,
|
|
126584
|
+
return withQueueRecord(state, nonEmpty2(data["intentId"]), (record2) => ({
|
|
126517
126585
|
...record2,
|
|
126518
126586
|
steering: data["steering"] === true
|
|
126519
126587
|
}));
|
|
126520
126588
|
case "queue-settled": {
|
|
126521
126589
|
const outcome = data["outcome"];
|
|
126522
126590
|
if (outcome !== "steered" && outcome !== "sent" && outcome !== "removed") return state;
|
|
126523
|
-
return withQueueRecord(state,
|
|
126591
|
+
return withQueueRecord(state, nonEmpty2(data["intentId"]), (record2) => ({
|
|
126524
126592
|
...record2,
|
|
126525
126593
|
settled: outcome
|
|
126526
126594
|
}));
|
|
126527
126595
|
}
|
|
126528
126596
|
case "turn-card-opened": {
|
|
126529
|
-
const cardId =
|
|
126530
|
-
const chatKey =
|
|
126531
|
-
const threadId =
|
|
126597
|
+
const cardId = nonEmpty2(data["cardId"]);
|
|
126598
|
+
const chatKey = nonEmpty2(data["chatKey"]);
|
|
126599
|
+
const threadId = nonEmpty2(data["threadId"]);
|
|
126532
126600
|
if (cardId === null || chatKey === null || threadId === null) return state;
|
|
126533
126601
|
const title = typeof data["title"] === "string" ? data["title"] : "";
|
|
126534
126602
|
return {
|
|
@@ -126540,7 +126608,7 @@ var foldImEvent = (state, name, data) => {
|
|
|
126540
126608
|
};
|
|
126541
126609
|
}
|
|
126542
126610
|
case "turn-card-closed": {
|
|
126543
|
-
const cardId =
|
|
126611
|
+
const cardId = nonEmpty2(data["cardId"]);
|
|
126544
126612
|
if (cardId === null) return state;
|
|
126545
126613
|
const { [cardId]: _closed, ...rest } = openTurnCardsOf(state);
|
|
126546
126614
|
return { ...state, turnCards: rest };
|
|
@@ -126557,6 +126625,131 @@ var foldPluginEvent = (pluginId) => (state, event) => {
|
|
|
126557
126625
|
return foldImEvent(state, fullName.slice(fullName.lastIndexOf("/") + 1), event.payload["data"]);
|
|
126558
126626
|
};
|
|
126559
126627
|
|
|
126628
|
+
// src/configRead.ts
|
|
126629
|
+
var readString = (row, key) => {
|
|
126630
|
+
const value = row[key];
|
|
126631
|
+
return typeof value === "string" ? value.trim() : "";
|
|
126632
|
+
};
|
|
126633
|
+
|
|
126634
|
+
// src/targetKind.ts
|
|
126635
|
+
var TARGET_KIND_OPTIONS = [
|
|
126636
|
+
{ value: "chat", labelKey: "settings.deliveryKind.chat" },
|
|
126637
|
+
{ value: "user", labelKey: "settings.deliveryKind.user" },
|
|
126638
|
+
{ value: "group", labelKey: "settings.deliveryKind.group" }
|
|
126639
|
+
];
|
|
126640
|
+
var TARGET_KINDS = new Set(
|
|
126641
|
+
TARGET_KIND_OPTIONS.map((option) => option.value)
|
|
126642
|
+
);
|
|
126643
|
+
var isTargetKind = (value) => TARGET_KINDS.has(value);
|
|
126644
|
+
|
|
126645
|
+
// src/deliveryTargets.ts
|
|
126646
|
+
var DELIVERY_TARGETS_FIELD = "deliveryTargets";
|
|
126647
|
+
var DELIVERY_HTTP_ENABLED_FIELD = "deliveryHttpEnabled";
|
|
126648
|
+
var DELIVERY_HTTP_PORT_FIELD = "deliveryHttpPort";
|
|
126649
|
+
var DELIVERY_HTTP_TOKEN_FIELD = "deliveryHttpToken";
|
|
126650
|
+
var DEFAULT_DELIVERY_HTTP_PORT = 3847;
|
|
126651
|
+
var TARGET_ID_PATTERN = /^[A-Za-z0-9._:@-]{1,128}$/;
|
|
126652
|
+
var isValidTargetId = (value) => TARGET_ID_PATTERN.test(value);
|
|
126653
|
+
var readDeliveryTargets = (config2) => {
|
|
126654
|
+
const table2 = config2[DELIVERY_TARGETS_FIELD];
|
|
126655
|
+
if (typeof table2 !== "object" || table2 === null) return [];
|
|
126656
|
+
return Object.entries(table2).flatMap(([rowId, row]) => {
|
|
126657
|
+
if (typeof row !== "object" || row === null) return [];
|
|
126658
|
+
const record2 = row;
|
|
126659
|
+
const botId = readString(record2, "botId");
|
|
126660
|
+
const targetId = readString(record2, "targetId");
|
|
126661
|
+
const platformChatId = readString(record2, "platformChatId");
|
|
126662
|
+
const kind = readString(record2, "kind") || "chat";
|
|
126663
|
+
if (botId === "" || targetId === "" || platformChatId === "" || !isTargetKind(kind)) return [];
|
|
126664
|
+
if (!isValidTargetId(targetId)) return [];
|
|
126665
|
+
return [
|
|
126666
|
+
{
|
|
126667
|
+
rowId,
|
|
126668
|
+
botId,
|
|
126669
|
+
targetId,
|
|
126670
|
+
name: readString(record2, "name") || targetId,
|
|
126671
|
+
kind,
|
|
126672
|
+
platformChatId
|
|
126673
|
+
}
|
|
126674
|
+
];
|
|
126675
|
+
});
|
|
126676
|
+
};
|
|
126677
|
+
var findDeliveryTarget = (targets, botId, targetId) => targets.find((t) => t.botId === botId && t.targetId === targetId);
|
|
126678
|
+
var readDeliveryHttpEnabled = (config2) => config2[DELIVERY_HTTP_ENABLED_FIELD] === true || config2[DELIVERY_HTTP_ENABLED_FIELD] === "true";
|
|
126679
|
+
var readDeliveryHttpPort = (config2) => {
|
|
126680
|
+
const raw = config2[DELIVERY_HTTP_PORT_FIELD];
|
|
126681
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number.parseInt(raw.trim(), 10) : DEFAULT_DELIVERY_HTTP_PORT;
|
|
126682
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535) return DEFAULT_DELIVERY_HTTP_PORT;
|
|
126683
|
+
return n;
|
|
126684
|
+
};
|
|
126685
|
+
var readDeliveryHttpToken = (config2) => {
|
|
126686
|
+
const value = config2[DELIVERY_HTTP_TOKEN_FIELD];
|
|
126687
|
+
return typeof value === "string" ? value : "";
|
|
126688
|
+
};
|
|
126689
|
+
var buildDeliverySettingsFields = () => [
|
|
126690
|
+
{
|
|
126691
|
+
key: DELIVERY_HTTP_ENABLED_FIELD,
|
|
126692
|
+
kind: "boolean",
|
|
126693
|
+
labelKey: "settings.deliveryHttp.enabled.label",
|
|
126694
|
+
descriptionKey: "settings.deliveryHttp.enabled.description"
|
|
126695
|
+
},
|
|
126696
|
+
{
|
|
126697
|
+
key: DELIVERY_HTTP_PORT_FIELD,
|
|
126698
|
+
kind: "number",
|
|
126699
|
+
labelKey: "settings.deliveryHttp.port.label",
|
|
126700
|
+
descriptionKey: "settings.deliveryHttp.port.description",
|
|
126701
|
+
showWhen: { key: DELIVERY_HTTP_ENABLED_FIELD, equals: ["true"] }
|
|
126702
|
+
},
|
|
126703
|
+
{
|
|
126704
|
+
key: DELIVERY_HTTP_TOKEN_FIELD,
|
|
126705
|
+
kind: "password",
|
|
126706
|
+
labelKey: "settings.deliveryHttp.token.label",
|
|
126707
|
+
descriptionKey: "settings.deliveryHttp.token.description",
|
|
126708
|
+
showWhen: { key: DELIVERY_HTTP_ENABLED_FIELD, equals: ["true"] }
|
|
126709
|
+
},
|
|
126710
|
+
{
|
|
126711
|
+
key: DELIVERY_TARGETS_FIELD,
|
|
126712
|
+
kind: "table",
|
|
126713
|
+
labelKey: "settings.deliveryTargets.label",
|
|
126714
|
+
descriptionKey: "settings.deliveryTargets.description",
|
|
126715
|
+
columns: [
|
|
126716
|
+
{
|
|
126717
|
+
key: "botId",
|
|
126718
|
+
kind: "text",
|
|
126719
|
+
labelKey: "column.deliveryBotId.label",
|
|
126720
|
+
descriptionKey: "column.deliveryBotId.description",
|
|
126721
|
+
placeholderKey: "column.deliveryBotId.placeholder"
|
|
126722
|
+
},
|
|
126723
|
+
{
|
|
126724
|
+
key: "targetId",
|
|
126725
|
+
kind: "text",
|
|
126726
|
+
labelKey: "column.deliveryTargetId.label",
|
|
126727
|
+
descriptionKey: "column.deliveryTargetId.description",
|
|
126728
|
+
placeholderKey: "column.deliveryTargetId.placeholder"
|
|
126729
|
+
},
|
|
126730
|
+
{
|
|
126731
|
+
key: "name",
|
|
126732
|
+
kind: "text",
|
|
126733
|
+
labelKey: "column.deliveryName.label",
|
|
126734
|
+
placeholderKey: "column.deliveryName.placeholder"
|
|
126735
|
+
},
|
|
126736
|
+
{
|
|
126737
|
+
key: "kind",
|
|
126738
|
+
kind: "select",
|
|
126739
|
+
labelKey: "column.deliveryKind.label",
|
|
126740
|
+
options: [...TARGET_KIND_OPTIONS]
|
|
126741
|
+
},
|
|
126742
|
+
{
|
|
126743
|
+
key: "platformChatId",
|
|
126744
|
+
kind: "text",
|
|
126745
|
+
labelKey: "column.deliveryPlatformChatId.label",
|
|
126746
|
+
descriptionKey: "column.deliveryPlatformChatId.description",
|
|
126747
|
+
placeholderKey: "column.deliveryPlatformChatId.placeholder"
|
|
126748
|
+
}
|
|
126749
|
+
]
|
|
126750
|
+
}
|
|
126751
|
+
];
|
|
126752
|
+
|
|
126560
126753
|
// src/botConfig.ts
|
|
126561
126754
|
var BOTS_FIELD = "bots";
|
|
126562
126755
|
var REPLY_LANGUAGE_FIELD = "replyLanguage";
|
|
@@ -126569,11 +126762,66 @@ var INBOUND_RUNTIME_MODES = /* @__PURE__ */ new Set([
|
|
|
126569
126762
|
"full-access"
|
|
126570
126763
|
]);
|
|
126571
126764
|
var DEFAULT_INBOUND_RUNTIME_MODE = "auto-accept-edits";
|
|
126765
|
+
var QUEUE_MODE_OPTIONS = [
|
|
126766
|
+
{ value: "queue", labelKey: "settings.queueMode.queue" },
|
|
126767
|
+
{ value: "steer", labelKey: "settings.queueMode.steer" }
|
|
126768
|
+
];
|
|
126769
|
+
var QUEUE_MODES = new Set(QUEUE_MODE_OPTIONS.map((option) => option.value));
|
|
126770
|
+
var DEFAULT_QUEUE_MODE = "queue";
|
|
126771
|
+
var ACCESS_MODE_OPTIONS = [
|
|
126772
|
+
{ value: "allowlist", labelKey: "settings.accessMode.allowlist" },
|
|
126773
|
+
{ value: "open", labelKey: "settings.accessMode.open" }
|
|
126774
|
+
];
|
|
126775
|
+
var ACCESS_MODES = new Set(
|
|
126776
|
+
ACCESS_MODE_OPTIONS.map((option) => option.value)
|
|
126777
|
+
);
|
|
126778
|
+
var DEFAULT_ACCESS_MODE = "allowlist";
|
|
126779
|
+
var LARK_PANEL_MODE_OPTIONS = [
|
|
126780
|
+
{ value: "default", labelKey: "settings.larkPanelMode.default" },
|
|
126781
|
+
{ value: "minimal", labelKey: "settings.larkPanelMode.minimal" }
|
|
126782
|
+
];
|
|
126783
|
+
var LARK_PANEL_MODES = new Set(
|
|
126784
|
+
LARK_PANEL_MODE_OPTIONS.map((option) => option.value)
|
|
126785
|
+
);
|
|
126786
|
+
var DEFAULT_LARK_PANEL_MODE = "default";
|
|
126572
126787
|
var chatKeyOf = (binding) => `${binding.rowId}:${binding.chatId}`;
|
|
126573
|
-
var
|
|
126574
|
-
|
|
126575
|
-
|
|
126788
|
+
var parseQueueMode = (value) => QUEUE_MODES.has(value) ? value : DEFAULT_QUEUE_MODE;
|
|
126789
|
+
var parseAccessMode = (value) => ACCESS_MODES.has(value) ? value : DEFAULT_ACCESS_MODE;
|
|
126790
|
+
var parseLarkPanelMode = (value) => LARK_PANEL_MODES.has(value) ? value : DEFAULT_LARK_PANEL_MODE;
|
|
126791
|
+
var parseOnOff = (value, defaultOn) => {
|
|
126792
|
+
if (value === "on") return true;
|
|
126793
|
+
if (value === "off") return false;
|
|
126794
|
+
return defaultOn;
|
|
126576
126795
|
};
|
|
126796
|
+
var ROW_ACTION_COMMANDS = {
|
|
126797
|
+
checkConnection: "im.settings.checkConnection",
|
|
126798
|
+
copyDiagnostics: "im.settings.copyDiagnostics",
|
|
126799
|
+
larkRepair: "im.settings.larkRepair"
|
|
126800
|
+
};
|
|
126801
|
+
var BOT_ROW_ACTIONS = [
|
|
126802
|
+
{
|
|
126803
|
+
id: "checkConnection",
|
|
126804
|
+
labelKey: "rowAction.checkConnection.label",
|
|
126805
|
+
descriptionKey: "rowAction.checkConnection.description",
|
|
126806
|
+
command: ROW_ACTION_COMMANDS.checkConnection,
|
|
126807
|
+
fills: []
|
|
126808
|
+
},
|
|
126809
|
+
{
|
|
126810
|
+
id: "copyDiagnostics",
|
|
126811
|
+
labelKey: "rowAction.copyDiagnostics.label",
|
|
126812
|
+
descriptionKey: "rowAction.copyDiagnostics.description",
|
|
126813
|
+
command: ROW_ACTION_COMMANDS.copyDiagnostics,
|
|
126814
|
+
fills: []
|
|
126815
|
+
},
|
|
126816
|
+
{
|
|
126817
|
+
id: "larkRepair",
|
|
126818
|
+
labelKey: "rowAction.larkRepair.label",
|
|
126819
|
+
descriptionKey: "rowAction.larkRepair.description",
|
|
126820
|
+
command: ROW_ACTION_COMMANDS.larkRepair,
|
|
126821
|
+
showWhen: { key: "platform", equals: ["lark"] },
|
|
126822
|
+
fills: []
|
|
126823
|
+
}
|
|
126824
|
+
];
|
|
126577
126825
|
var buildSettingsCard = (adapters) => {
|
|
126578
126826
|
const platformColumn = {
|
|
126579
126827
|
key: "platform",
|
|
@@ -126619,11 +126867,20 @@ var buildSettingsCard = (adapters) => {
|
|
|
126619
126867
|
},
|
|
126620
126868
|
platformColumn,
|
|
126621
126869
|
...credentialColumns,
|
|
126870
|
+
{
|
|
126871
|
+
key: "accessMode",
|
|
126872
|
+
kind: "select",
|
|
126873
|
+
labelKey: "column.accessMode.label",
|
|
126874
|
+
descriptionKey: "column.accessMode.description",
|
|
126875
|
+
options: [...ACCESS_MODE_OPTIONS]
|
|
126876
|
+
},
|
|
126622
126877
|
{
|
|
126623
126878
|
key: "allowedSenders",
|
|
126624
126879
|
kind: "text",
|
|
126625
126880
|
labelKey: "column.allowedSenders.label",
|
|
126626
|
-
descriptionKey: "column.allowedSenders.description"
|
|
126881
|
+
descriptionKey: "column.allowedSenders.description",
|
|
126882
|
+
// 缺省/空串=allowlist(与 parseAccessMode 一致),仍显示白名单框。
|
|
126883
|
+
showWhen: { key: "accessMode", equals: ["allowlist", ""] }
|
|
126627
126884
|
},
|
|
126628
126885
|
{
|
|
126629
126886
|
// 选项由宿主填,连三态一起——插件只声明「这个格子装的是代理」,宿主因此
|
|
@@ -126655,9 +126912,37 @@ var buildSettingsCard = (adapters) => {
|
|
|
126655
126912
|
{ value: "never", labelKey: "settings.groupMention.never" },
|
|
126656
126913
|
{ value: "ambient", labelKey: "settings.groupMention.ambient" }
|
|
126657
126914
|
]
|
|
126915
|
+
},
|
|
126916
|
+
{
|
|
126917
|
+
key: "queueMode",
|
|
126918
|
+
kind: "select",
|
|
126919
|
+
labelKey: "column.queueMode.label",
|
|
126920
|
+
descriptionKey: "column.queueMode.description",
|
|
126921
|
+
options: [...QUEUE_MODE_OPTIONS]
|
|
126922
|
+
},
|
|
126923
|
+
{
|
|
126924
|
+
key: "larkPanelMode",
|
|
126925
|
+
kind: "select",
|
|
126926
|
+
labelKey: "column.larkPanelMode.label",
|
|
126927
|
+
descriptionKey: "column.larkPanelMode.description",
|
|
126928
|
+
showWhen: { key: "platform", equals: ["lark"] },
|
|
126929
|
+
options: [...LARK_PANEL_MODE_OPTIONS]
|
|
126930
|
+
},
|
|
126931
|
+
{
|
|
126932
|
+
key: "wecomNumberedHints",
|
|
126933
|
+
kind: "select",
|
|
126934
|
+
labelKey: "column.wecomNumberedHints.label",
|
|
126935
|
+
descriptionKey: "column.wecomNumberedHints.description",
|
|
126936
|
+
showWhen: { key: "platform", equals: ["wecom"] },
|
|
126937
|
+
options: [
|
|
126938
|
+
{ value: "on", labelKey: "settings.onOff.on" },
|
|
126939
|
+
{ value: "off", labelKey: "settings.onOff.off" }
|
|
126940
|
+
]
|
|
126658
126941
|
}
|
|
126659
|
-
]
|
|
126660
|
-
|
|
126942
|
+
],
|
|
126943
|
+
rowActions: [...BOT_ROW_ACTIONS]
|
|
126944
|
+
},
|
|
126945
|
+
...buildDeliverySettingsFields()
|
|
126661
126946
|
]
|
|
126662
126947
|
};
|
|
126663
126948
|
};
|
|
@@ -126678,10 +126963,14 @@ var readBot = (adapterFor, rowId, row) => {
|
|
|
126678
126963
|
displayName: readString(row, "displayName") || adapter.label,
|
|
126679
126964
|
// 空串=跟机器默认(省略),`direct`=明确不加代理(null),其余就是那个 id。
|
|
126680
126965
|
proxy: proxyValue === "" ? void 0 : proxyValue === PLUGIN_PROXY_DIRECT ? null : proxyValue,
|
|
126966
|
+
accessMode: parseAccessMode(readString(row, "accessMode")),
|
|
126681
126967
|
allowedSenders,
|
|
126682
126968
|
values,
|
|
126683
126969
|
runtimeMode: INBOUND_RUNTIME_MODES.has(requested) ? requested : DEFAULT_INBOUND_RUNTIME_MODE,
|
|
126684
|
-
groupMention: parseGroupMention(readString(row, "groupMention"))
|
|
126970
|
+
groupMention: parseGroupMention(readString(row, "groupMention")),
|
|
126971
|
+
queueMode: parseQueueMode(readString(row, "queueMode")),
|
|
126972
|
+
larkPanelMode: parseLarkPanelMode(readString(row, "larkPanelMode")),
|
|
126973
|
+
wecomNumberedHints: parseOnOff(readString(row, "wecomNumberedHints"), true)
|
|
126685
126974
|
};
|
|
126686
126975
|
return adapter.isConfigured(bot) ? bot : null;
|
|
126687
126976
|
};
|
|
@@ -126703,147 +126992,51 @@ var findBindingByChatKey = (bots, state, chatKey) => {
|
|
|
126703
126992
|
const bot = bots.find((candidate) => chatKey.startsWith(`${candidate.rowId}:`));
|
|
126704
126993
|
return bot === void 0 ? void 0 : bindingOf(bot, chatKey.slice(bot.rowId.length + 1), state);
|
|
126705
126994
|
};
|
|
126706
|
-
var isAllowedSender = (bot, sender) =>
|
|
126707
|
-
|
|
126708
|
-
|
|
126709
|
-
|
|
126710
|
-
const tails = /* @__PURE__ */ new Map();
|
|
126711
|
-
return (key, work) => {
|
|
126712
|
-
const run = (tails.get(key) ?? Promise.resolve()).then(work).catch(onError).then(() => {
|
|
126713
|
-
if (tails.get(key) === run) tails.delete(key);
|
|
126714
|
-
});
|
|
126715
|
-
tails.set(key, run);
|
|
126716
|
-
};
|
|
126995
|
+
var isAllowedSender = (bot, sender) => {
|
|
126996
|
+
if (sender === null || sender === "") return false;
|
|
126997
|
+
if (bot.accessMode === "open") return true;
|
|
126998
|
+
return bot.allowedSenders.has(sender);
|
|
126717
126999
|
};
|
|
126718
127000
|
|
|
126719
|
-
// src/
|
|
126720
|
-
var
|
|
126721
|
-
|
|
126722
|
-
|
|
126723
|
-
const
|
|
126724
|
-
|
|
126725
|
-
const stateHandle = input.derivedState({
|
|
126726
|
-
key: IM_STATE_KEY,
|
|
126727
|
-
version: IM_STATE_VERSION,
|
|
126728
|
-
events: ["plugin.event-appended"],
|
|
126729
|
-
init: emptyState,
|
|
126730
|
-
apply: foldPluginEvent(input.pluginId)
|
|
126731
|
-
});
|
|
126732
|
-
const ready = stateHandle.get().then(
|
|
126733
|
-
(restored) => {
|
|
126734
|
-
state = restored;
|
|
126735
|
-
},
|
|
126736
|
-
(error) => {
|
|
126737
|
-
host.log.warn("chat could not restore its saved state", { error: describeError(error) });
|
|
126738
|
-
}
|
|
126739
|
-
);
|
|
126740
|
-
let persisting = Promise.resolve();
|
|
126741
|
-
const commit = (name, data) => {
|
|
126742
|
-
state = foldImEvent(state, name, data);
|
|
126743
|
-
persisting = persisting.then(
|
|
126744
|
-
() => host.events.append({ name, aggregate: { kind: "plugin", id: input.pluginId }, data })
|
|
126745
|
-
).then(
|
|
126746
|
-
() => void 0,
|
|
126747
|
-
(error) => {
|
|
126748
|
-
host.log.warn("chat could not save its state", { name, error: describeError(error) });
|
|
126749
|
-
}
|
|
126750
|
-
);
|
|
126751
|
-
};
|
|
126752
|
-
const bindChat = (chatKey, threadId) => {
|
|
126753
|
-
commit("chat-updated", { chatKey, threadId });
|
|
126754
|
-
};
|
|
126755
|
-
const outbound = makeKeyedQueue((error) => {
|
|
126756
|
-
host.log.warn("chat could not deliver a card", { error: describeError(error) });
|
|
126757
|
-
});
|
|
126758
|
-
const postImages = async (binding, attachments) => {
|
|
126759
|
-
const adapter = adapterFor(binding.platform);
|
|
126760
|
-
if (adapter?.sendImage === void 0) return;
|
|
126761
|
-
for (const attachment of attachments) {
|
|
126762
|
-
if (attachment.type !== "image") continue;
|
|
126763
|
-
try {
|
|
126764
|
-
const bytes = await host.attachments.read(attachment.id);
|
|
126765
|
-
await adapter.sendImage(binding, {
|
|
126766
|
-
bytes,
|
|
126767
|
-
name: attachment.name,
|
|
126768
|
-
mimeType: attachment.mimeType
|
|
126769
|
-
});
|
|
126770
|
-
} catch (error) {
|
|
126771
|
-
host.log.warn("chat could not deliver an image", {
|
|
126772
|
-
name: attachment.name,
|
|
126773
|
-
error: describeError(error)
|
|
126774
|
-
});
|
|
126775
|
-
}
|
|
126776
|
-
}
|
|
126777
|
-
};
|
|
126778
|
-
const deliver = async (binding, text3, attachments = []) => {
|
|
126779
|
-
await postImages(binding, attachments);
|
|
126780
|
-
if (text3.trim() === "") return;
|
|
126781
|
-
try {
|
|
126782
|
-
await adapterFor(binding.platform)?.sendText(binding, text3);
|
|
126783
|
-
} catch (error) {
|
|
126784
|
-
host.log.warn("chat could not deliver", { error: describeError(error) });
|
|
126785
|
-
}
|
|
126786
|
-
};
|
|
126787
|
-
const bots = () => readBots(input.config, adapterFor);
|
|
126788
|
-
const bindingForChatKey = (chatKey) => findBindingByChatKey(bots(), state, chatKey);
|
|
126789
|
-
return {
|
|
126790
|
-
host,
|
|
126791
|
-
t,
|
|
126792
|
-
adapterFor,
|
|
126793
|
-
get state() {
|
|
126794
|
-
return state;
|
|
126795
|
-
},
|
|
126796
|
-
ready,
|
|
126797
|
-
commit,
|
|
126798
|
-
bindChat,
|
|
126799
|
-
outbound,
|
|
126800
|
-
deliver,
|
|
126801
|
-
bots,
|
|
126802
|
-
bindingOf: (bot, chatId) => bindingOf(bot, chatId, state),
|
|
126803
|
-
bindingForChatKey,
|
|
126804
|
-
bindingOfThread: (threadId) => {
|
|
126805
|
-
const chatKey = chatOfThread(state, threadId);
|
|
126806
|
-
return chatKey === void 0 ? void 0 : bindingForChatKey(chatKey);
|
|
126807
|
-
},
|
|
126808
|
-
hasPanel: (binding) => adapterFor(binding.platform)?.panel !== void 0
|
|
126809
|
-
};
|
|
127001
|
+
// src/numberedReply.ts
|
|
127002
|
+
var optionIndexFrom = (text3, count) => {
|
|
127003
|
+
const trimmed = text3.trim();
|
|
127004
|
+
if (!/^\d+$/.test(trimmed)) return null;
|
|
127005
|
+
const index = Number.parseInt(trimmed, 10);
|
|
127006
|
+
return Number.isInteger(index) && index >= 1 && index <= count ? index : null;
|
|
126810
127007
|
};
|
|
126811
|
-
|
|
126812
|
-
|
|
126813
|
-
|
|
126814
|
-
|
|
126815
|
-
|
|
126816
|
-
|
|
126817
|
-
|
|
126818
|
-
|
|
126819
|
-
|
|
126820
|
-
|
|
126821
|
-
|
|
126822
|
-
|
|
126823
|
-
|
|
126824
|
-
|
|
126825
|
-
|
|
126826
|
-
|
|
126827
|
-
|
|
126828
|
-
|
|
126829
|
-
|
|
126830
|
-
|
|
126831
|
-
|
|
126832
|
-
|
|
126833
|
-
|
|
126834
|
-
acknowledgements.delete(threadId);
|
|
126835
|
-
for (const entry of pending) {
|
|
126836
|
-
try {
|
|
126837
|
-
await adapter.clearAcknowledge(binding, entry.messageId, entry.ackId);
|
|
126838
|
-
} catch (error) {
|
|
126839
|
-
host.log.warn("chat could not clear the acknowledgement", {
|
|
126840
|
-
error: describeError(error)
|
|
126841
|
-
});
|
|
126842
|
-
}
|
|
126843
|
-
}
|
|
126844
|
-
};
|
|
126845
|
-
return { markWorking, clearWorking };
|
|
127008
|
+
var DECISION_WORDS = /* @__PURE__ */ new Map([
|
|
127009
|
+
["\u6279\u51C6", "accept"],
|
|
127010
|
+
["\u540C\u610F", "accept"],
|
|
127011
|
+
["\u5141\u8BB8", "accept"],
|
|
127012
|
+
["approve", "accept"],
|
|
127013
|
+
["allow", "accept"],
|
|
127014
|
+
["yes", "accept"],
|
|
127015
|
+
["\u62D2\u7EDD", "decline"],
|
|
127016
|
+
["\u4E0D\u540C\u610F", "decline"],
|
|
127017
|
+
["deny", "decline"],
|
|
127018
|
+
["decline", "decline"],
|
|
127019
|
+
["no", "decline"]
|
|
127020
|
+
]);
|
|
127021
|
+
var answerByNumber = (text3, options) => {
|
|
127022
|
+
const index = optionIndexFrom(text3, options.length);
|
|
127023
|
+
if (index !== null) return options[index - 1]?.token ?? null;
|
|
127024
|
+
const said = text3.trim().toLowerCase();
|
|
127025
|
+
if (said.length === 0) return null;
|
|
127026
|
+
const byLabel = options.find((option) => option.label.trim().toLowerCase() === said);
|
|
127027
|
+
if (byLabel !== void 0) return byLabel.token;
|
|
127028
|
+
const decision = DECISION_WORDS.get(said);
|
|
127029
|
+
if (decision === void 0) return null;
|
|
127030
|
+
return options.find((option) => option.decision === decision)?.token ?? null;
|
|
126846
127031
|
};
|
|
127032
|
+
var approvalReplyHint = (t, binding) => binding.platform === "wecom" && binding.wecomNumberedHints ? t("approval.replyHintFull") : t("approval.replyHint");
|
|
127033
|
+
var numberedApprovalText = (request) => [
|
|
127034
|
+
request.body,
|
|
127035
|
+
"",
|
|
127036
|
+
...request.options.map((option, index) => `${String(index + 1)}. ${option.label}`),
|
|
127037
|
+
"",
|
|
127038
|
+
request.replyHint
|
|
127039
|
+
].join("\n");
|
|
126847
127040
|
|
|
126848
127041
|
// src/approvals.ts
|
|
126849
127042
|
var makeApprovals = (core) => {
|
|
@@ -126886,7 +127079,7 @@ var makeApprovals = (core) => {
|
|
|
126886
127079
|
title: offer.title,
|
|
126887
127080
|
body: offer.body,
|
|
126888
127081
|
options: offer.buttons,
|
|
126889
|
-
replyHint: t
|
|
127082
|
+
replyHint: approvalReplyHint(t, binding)
|
|
126890
127083
|
});
|
|
126891
127084
|
host.log.info("chat asked for an approval", { threadId, requestId: offer.requestId });
|
|
126892
127085
|
if (cardId === null) return;
|
|
@@ -127224,6 +127417,7 @@ var helpText = (t, options = {}) => [
|
|
|
127224
127417
|
t("help.new"),
|
|
127225
127418
|
t("help.sessions"),
|
|
127226
127419
|
t("help.resume"),
|
|
127420
|
+
t("help.syncNote"),
|
|
127227
127421
|
t("help.model"),
|
|
127228
127422
|
t("help.plan"),
|
|
127229
127423
|
t("help.repo"),
|
|
@@ -127531,6 +127725,109 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
127531
127725
|
return { bots: bots.length, connecting: started.size };
|
|
127532
127726
|
};
|
|
127533
127727
|
|
|
127728
|
+
// src/keyedQueue.ts
|
|
127729
|
+
var makeKeyedQueue = (onError) => {
|
|
127730
|
+
const tails = /* @__PURE__ */ new Map();
|
|
127731
|
+
return (key, work) => {
|
|
127732
|
+
const run = (tails.get(key) ?? Promise.resolve()).then(work).catch(onError).then(() => {
|
|
127733
|
+
if (tails.get(key) === run) tails.delete(key);
|
|
127734
|
+
});
|
|
127735
|
+
tails.set(key, run);
|
|
127736
|
+
};
|
|
127737
|
+
};
|
|
127738
|
+
|
|
127739
|
+
// src/core.ts
|
|
127740
|
+
var makeImCore = (input) => {
|
|
127741
|
+
const { host, t, adapters } = input;
|
|
127742
|
+
const adapterFor = (platform) => adapters.find((adapter) => adapter.platform === platform);
|
|
127743
|
+
let state = emptyState();
|
|
127744
|
+
const stateHandle = input.derivedState({
|
|
127745
|
+
key: IM_STATE_KEY,
|
|
127746
|
+
version: IM_STATE_VERSION,
|
|
127747
|
+
events: ["plugin.event-appended"],
|
|
127748
|
+
init: emptyState,
|
|
127749
|
+
apply: foldPluginEvent(input.pluginId)
|
|
127750
|
+
});
|
|
127751
|
+
const ready = stateHandle.get().then(
|
|
127752
|
+
(restored) => {
|
|
127753
|
+
state = restored;
|
|
127754
|
+
},
|
|
127755
|
+
(error) => {
|
|
127756
|
+
host.log.warn("chat could not restore its saved state", { error: describeError(error) });
|
|
127757
|
+
}
|
|
127758
|
+
);
|
|
127759
|
+
let persisting = Promise.resolve();
|
|
127760
|
+
const commit = (name, data) => {
|
|
127761
|
+
state = foldImEvent(state, name, data);
|
|
127762
|
+
persisting = persisting.then(
|
|
127763
|
+
() => host.events.append({ name, aggregate: { kind: "plugin", id: input.pluginId }, data })
|
|
127764
|
+
).then(
|
|
127765
|
+
() => void 0,
|
|
127766
|
+
(error) => {
|
|
127767
|
+
host.log.warn("chat could not save its state", { name, error: describeError(error) });
|
|
127768
|
+
}
|
|
127769
|
+
);
|
|
127770
|
+
};
|
|
127771
|
+
const bindChat = (chatKey, threadId) => {
|
|
127772
|
+
commit("chat-updated", { chatKey, threadId });
|
|
127773
|
+
};
|
|
127774
|
+
const outbound = makeKeyedQueue((error) => {
|
|
127775
|
+
host.log.warn("chat could not deliver a card", { error: describeError(error) });
|
|
127776
|
+
});
|
|
127777
|
+
const postImages = async (binding, attachments) => {
|
|
127778
|
+
const adapter = adapterFor(binding.platform);
|
|
127779
|
+
if (adapter?.sendImage === void 0) return;
|
|
127780
|
+
for (const attachment of attachments) {
|
|
127781
|
+
if (attachment.type !== "image") continue;
|
|
127782
|
+
try {
|
|
127783
|
+
const bytes = await host.attachments.read(attachment.id);
|
|
127784
|
+
await adapter.sendImage(binding, {
|
|
127785
|
+
bytes,
|
|
127786
|
+
name: attachment.name,
|
|
127787
|
+
mimeType: attachment.mimeType
|
|
127788
|
+
});
|
|
127789
|
+
} catch (error) {
|
|
127790
|
+
host.log.warn("chat could not deliver an image", {
|
|
127791
|
+
name: attachment.name,
|
|
127792
|
+
error: describeError(error)
|
|
127793
|
+
});
|
|
127794
|
+
}
|
|
127795
|
+
}
|
|
127796
|
+
};
|
|
127797
|
+
const deliver = async (binding, text3, attachments = []) => {
|
|
127798
|
+
await postImages(binding, attachments);
|
|
127799
|
+
if (text3.trim() === "") return;
|
|
127800
|
+
try {
|
|
127801
|
+
await adapterFor(binding.platform)?.sendText(binding, text3);
|
|
127802
|
+
} catch (error) {
|
|
127803
|
+
host.log.warn("chat could not deliver", { error: describeError(error) });
|
|
127804
|
+
}
|
|
127805
|
+
};
|
|
127806
|
+
const bots = () => readBots(input.config, adapterFor);
|
|
127807
|
+
const bindingForChatKey = (chatKey) => findBindingByChatKey(bots(), state, chatKey);
|
|
127808
|
+
return {
|
|
127809
|
+
host,
|
|
127810
|
+
t,
|
|
127811
|
+
adapterFor,
|
|
127812
|
+
get state() {
|
|
127813
|
+
return state;
|
|
127814
|
+
},
|
|
127815
|
+
ready,
|
|
127816
|
+
commit,
|
|
127817
|
+
bindChat,
|
|
127818
|
+
outbound,
|
|
127819
|
+
deliver,
|
|
127820
|
+
bots,
|
|
127821
|
+
bindingOf: (bot, chatId) => bindingOf(bot, chatId, state),
|
|
127822
|
+
bindingForChatKey,
|
|
127823
|
+
bindingOfThread: (threadId) => {
|
|
127824
|
+
const chatKey = chatOfThread(state, threadId);
|
|
127825
|
+
return chatKey === void 0 ? void 0 : bindingForChatKey(chatKey);
|
|
127826
|
+
},
|
|
127827
|
+
hasPanel: (binding) => adapterFor(binding.platform)?.panel !== void 0
|
|
127828
|
+
};
|
|
127829
|
+
};
|
|
127830
|
+
|
|
127534
127831
|
// src/attachments.ts
|
|
127535
127832
|
var IMAGE_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
127536
127833
|
var FILE_LIMIT_BYTES = 25 * 1024 * 1024;
|
|
@@ -127777,6 +128074,24 @@ var makeInboundAttachments = (core) => {
|
|
|
127777
128074
|
return { pendingByChat, pendingFor, collectResources };
|
|
127778
128075
|
};
|
|
127779
128076
|
|
|
128077
|
+
// src/boundedSeen.ts
|
|
128078
|
+
var makeBoundedSeen = (limit) => {
|
|
128079
|
+
const seen = /* @__PURE__ */ new Set();
|
|
128080
|
+
return {
|
|
128081
|
+
has: (key) => seen.has(key),
|
|
128082
|
+
/** 记下这个键:第一次见返回 true;已经记着返回 false(不重排,最早记的先被忘)。 */
|
|
128083
|
+
claim: (key) => {
|
|
128084
|
+
if (seen.has(key)) return false;
|
|
128085
|
+
seen.add(key);
|
|
128086
|
+
if (seen.size > limit) {
|
|
128087
|
+
const oldest = seen.values().next().value;
|
|
128088
|
+
if (oldest !== void 0) seen.delete(oldest);
|
|
128089
|
+
}
|
|
128090
|
+
return true;
|
|
128091
|
+
}
|
|
128092
|
+
};
|
|
128093
|
+
};
|
|
128094
|
+
|
|
127780
128095
|
// src/cooldown.ts
|
|
127781
128096
|
var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
|
|
127782
128097
|
const lastAt = /* @__PURE__ */ new Map();
|
|
@@ -127829,18 +128144,9 @@ var makeInbound = (core, deps) => {
|
|
|
127829
128144
|
NOT_ALLOWED_REPLY_KEY_LIMIT
|
|
127830
128145
|
);
|
|
127831
128146
|
const projectPromptCooldown = makeCooldown(PROJECT_PROMPT_COOLDOWN_MS, () => Date.now());
|
|
127832
|
-
const
|
|
127833
|
-
const seenOrder = [];
|
|
128147
|
+
const handledMessages = makeBoundedSeen(SEEN_MESSAGE_LIMIT);
|
|
127834
128148
|
const alreadyHandled = (target, messageId) => {
|
|
127835
|
-
|
|
127836
|
-
if (seen.has(key)) return true;
|
|
127837
|
-
seen.add(key);
|
|
127838
|
-
seenOrder.push(key);
|
|
127839
|
-
if (seenOrder.length > SEEN_MESSAGE_LIMIT) {
|
|
127840
|
-
const oldest = seenOrder.shift();
|
|
127841
|
-
if (oldest !== void 0) seen.delete(oldest);
|
|
127842
|
-
}
|
|
127843
|
-
return false;
|
|
128149
|
+
return !handledMessages.claim(`${chatKeyOf(target)}:${messageId}`);
|
|
127844
128150
|
};
|
|
127845
128151
|
const turnAwayStranger = (target, sender) => {
|
|
127846
128152
|
host.log.warn("chat ignored a message from someone not on the allow list", {
|
|
@@ -127867,6 +128173,9 @@ var makeInbound = (core, deps) => {
|
|
|
127867
128173
|
if (current === void 0) return;
|
|
127868
128174
|
const arrived = bindingOf2(current, message.chatId);
|
|
127869
128175
|
if (alreadyHandled(arrived, message.messageId)) return;
|
|
128176
|
+
if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
|
|
128177
|
+
commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
|
|
128178
|
+
}
|
|
127870
128179
|
if (!isAllowedSender(arrived, message.senderId)) {
|
|
127871
128180
|
turnAwayStranger(arrived, message.senderId);
|
|
127872
128181
|
return;
|
|
@@ -127883,11 +128192,7 @@ var makeInbound = (core, deps) => {
|
|
|
127883
128192
|
return;
|
|
127884
128193
|
}
|
|
127885
128194
|
const open = openApprovalOf(core.state, chatKeyOf(arrived));
|
|
127886
|
-
const answerToken = open === void 0 ? null : adapterFor(arrived.platform)?.answerByText?.(
|
|
127887
|
-
arrived,
|
|
127888
|
-
message.text,
|
|
127889
|
-
open.options.map(({ label, token }) => ({ label, token }))
|
|
127890
|
-
) ?? null;
|
|
128195
|
+
const answerToken = open === void 0 ? null : adapterFor(arrived.platform)?.answerByText?.(arrived, message.text, open.options) ?? null;
|
|
127891
128196
|
if (answerToken !== null) {
|
|
127892
128197
|
void answerByToken(answerToken).catch((error) => {
|
|
127893
128198
|
host.log.warn("chat could not answer the approval", { error: describeError(error) });
|
|
@@ -128541,7 +128846,7 @@ var makeQueueFlow = (host, deps) => {
|
|
|
128541
128846
|
}
|
|
128542
128847
|
return handledWith(null);
|
|
128543
128848
|
};
|
|
128544
|
-
return { noteQueued, refresh, click };
|
|
128849
|
+
return { noteQueued, refresh, click, readExecution: deps.readExecution };
|
|
128545
128850
|
};
|
|
128546
128851
|
|
|
128547
128852
|
// src/interactions.ts
|
|
@@ -128766,24 +129071,10 @@ var TIMEOUT_MS = {
|
|
|
128766
129071
|
var RETRY_BACKOFF_MS = 500;
|
|
128767
129072
|
var RETRY_AFTER_CAP_MS = 1e4;
|
|
128768
129073
|
var CALL_SPACING_MS = 150;
|
|
128769
|
-
var DETAIL_MAX_CHARS = 200;
|
|
128770
129074
|
|
|
128771
|
-
// src/
|
|
128772
|
-
var
|
|
128773
|
-
|
|
128774
|
-
"identity",
|
|
128775
|
-
"findApp",
|
|
128776
|
-
"createApp",
|
|
128777
|
-
"enableBot",
|
|
128778
|
-
"longConnection",
|
|
128779
|
-
"scopes",
|
|
128780
|
-
"events",
|
|
128781
|
-
"callbacks",
|
|
128782
|
-
"publish",
|
|
128783
|
-
"secret"
|
|
128784
|
-
];
|
|
128785
|
-
var STEP_STATES = ["pending", "running", "done", "skipped", "failed"];
|
|
128786
|
-
var SETUP_ITEM_SCHEMA = {
|
|
129075
|
+
// src/flowItemSchema.ts
|
|
129076
|
+
var FLOW_STEP_STATES = ["pending", "running", "done", "skipped", "failed"];
|
|
129077
|
+
var makeFlowItemSchema = (shape) => ({
|
|
128787
129078
|
type: "object",
|
|
128788
129079
|
additionalProperties: false,
|
|
128789
129080
|
required: ["kind"],
|
|
@@ -128801,14 +129092,13 @@ var SETUP_ITEM_SCHEMA = {
|
|
|
128801
129092
|
additionalProperties: false,
|
|
128802
129093
|
required: ["id", "labelKey", "state"],
|
|
128803
129094
|
properties: {
|
|
128804
|
-
id:
|
|
129095
|
+
id: shape.stepId,
|
|
128805
129096
|
labelKey: { type: "string" },
|
|
128806
|
-
state: { type: "string", enum: [...
|
|
129097
|
+
state: { type: "string", enum: [...FLOW_STEP_STATES] }
|
|
128807
129098
|
}
|
|
128808
129099
|
}
|
|
128809
129100
|
},
|
|
128810
|
-
|
|
128811
|
-
fills: { type: "object", additionalProperties: false },
|
|
129101
|
+
fills: shape.fills,
|
|
128812
129102
|
messageKey: { type: "string" },
|
|
128813
129103
|
warnings: {
|
|
128814
129104
|
type: "array",
|
|
@@ -128826,13 +129116,34 @@ var SETUP_ITEM_SCHEMA = {
|
|
|
128826
129116
|
detail: { type: "string" }
|
|
128827
129117
|
},
|
|
128828
129118
|
oneOf: [
|
|
128829
|
-
{ properties: { kind: { const: "qr" } }, required: [
|
|
129119
|
+
{ properties: { kind: { const: "qr" } }, required: [...shape.qrRequired] },
|
|
128830
129120
|
{ properties: { kind: { const: "status" } }, required: ["statusKey"] },
|
|
128831
129121
|
{ properties: { kind: { const: "steps" } }, required: ["steps"] },
|
|
128832
|
-
{ properties: { kind: { const: "done" } }, required: [
|
|
129122
|
+
{ properties: { kind: { const: "done" } }, required: [...shape.doneRequired] },
|
|
128833
129123
|
{ properties: { kind: { const: "failed" } }, required: ["reasonKey"] }
|
|
128834
129124
|
]
|
|
128835
|
-
};
|
|
129125
|
+
});
|
|
129126
|
+
|
|
129127
|
+
// src/lark/setup/setupItemSchema.ts
|
|
129128
|
+
var SETUP_STEP_IDS = [
|
|
129129
|
+
"login",
|
|
129130
|
+
"identity",
|
|
129131
|
+
"findApp",
|
|
129132
|
+
"createApp",
|
|
129133
|
+
"enableBot",
|
|
129134
|
+
"longConnection",
|
|
129135
|
+
"scopes",
|
|
129136
|
+
"events",
|
|
129137
|
+
"callbacks",
|
|
129138
|
+
"publish",
|
|
129139
|
+
"secret"
|
|
129140
|
+
];
|
|
129141
|
+
var SETUP_ITEM_SCHEMA = makeFlowItemSchema({
|
|
129142
|
+
stepId: { type: "string", enum: [...SETUP_STEP_IDS] },
|
|
129143
|
+
fills: { type: "object", additionalProperties: false },
|
|
129144
|
+
qrRequired: ["payload", "expiresAt"],
|
|
129145
|
+
doneRequired: ["messageKey"]
|
|
129146
|
+
});
|
|
128836
129147
|
|
|
128837
129148
|
// src/lark/setup/saveBot.ts
|
|
128838
129149
|
var OPEN_ID_PREFIX = "ou_";
|
|
@@ -129675,7 +129986,8 @@ var sleep = async (deps) => {
|
|
|
129675
129986
|
}
|
|
129676
129987
|
};
|
|
129677
129988
|
|
|
129678
|
-
// src/
|
|
129989
|
+
// src/redact.ts
|
|
129990
|
+
var DETAIL_MAX_CHARS = 200;
|
|
129679
129991
|
var MASK = "***";
|
|
129680
129992
|
var TOKEN_LIKE = /[A-Za-z0-9_=-]{24,}/g;
|
|
129681
129993
|
var MIN_SECRET_LENGTH = 6;
|
|
@@ -130462,7 +130774,8 @@ var fetchTenantToken = async (input) => {
|
|
|
130462
130774
|
return { token: body.tenant_access_token, expiresInSeconds: body.expire ?? 0 };
|
|
130463
130775
|
};
|
|
130464
130776
|
var sendChatText = async (input) => {
|
|
130465
|
-
const
|
|
130777
|
+
const idType = input.receiveIdType ?? "chat_id";
|
|
130778
|
+
const response = await input.http(`${LARK_BASE}/im/v1/messages?receive_id_type=${idType}`, {
|
|
130466
130779
|
method: "POST",
|
|
130467
130780
|
headers: {
|
|
130468
130781
|
authorization: `Bearer ${input.token}`,
|
|
@@ -136069,9 +136382,9 @@ var styledText = (text3, style) => {
|
|
|
136069
136382
|
if (style?.includes("underline")) out = `<u>${out}</u>`;
|
|
136070
136383
|
return out;
|
|
136071
136384
|
};
|
|
136072
|
-
var
|
|
136385
|
+
var nonEmpty3 = (value) => typeof value === "string" && value !== "";
|
|
136073
136386
|
var resourceOf = (element, index) => {
|
|
136074
|
-
if (element.tag === "img" &&
|
|
136387
|
+
if (element.tag === "img" && nonEmpty3(element.image_key)) {
|
|
136075
136388
|
return {
|
|
136076
136389
|
fileKey: element.image_key,
|
|
136077
136390
|
kind: "image",
|
|
@@ -136079,14 +136392,14 @@ var resourceOf = (element, index) => {
|
|
|
136079
136392
|
mimeType: "image/png"
|
|
136080
136393
|
};
|
|
136081
136394
|
}
|
|
136082
|
-
if ((element.tag === "media" || element.tag === "file") &&
|
|
136395
|
+
if ((element.tag === "media" || element.tag === "file") && nonEmpty3(element.file_key)) {
|
|
136083
136396
|
const video = element.tag === "media";
|
|
136084
|
-
const name =
|
|
136397
|
+
const name = nonEmpty3(element.file_name) ? element.file_name : video ? `video-${String(index)}.mp4` : `file-${String(index)}`;
|
|
136085
136398
|
return {
|
|
136086
136399
|
fileKey: element.file_key,
|
|
136087
136400
|
kind: "file",
|
|
136088
136401
|
name,
|
|
136089
|
-
mimeType: video && !
|
|
136402
|
+
mimeType: video && !nonEmpty3(element.file_name) ? "video/mp4" : mimeFromName(name)
|
|
136090
136403
|
};
|
|
136091
136404
|
}
|
|
136092
136405
|
return null;
|
|
@@ -136098,11 +136411,11 @@ var serializeElement = (element, index, botIds) => {
|
|
|
136098
136411
|
return styledText(element.text ?? "", element.style);
|
|
136099
136412
|
case "a": {
|
|
136100
136413
|
const label = element.text ?? element.href ?? "";
|
|
136101
|
-
return
|
|
136414
|
+
return nonEmpty3(element.href) ? `[${label}](${element.href})` : label;
|
|
136102
136415
|
}
|
|
136103
136416
|
case "at": {
|
|
136104
136417
|
if (element.user_id !== void 0 && botIds.has(element.user_id)) return "";
|
|
136105
|
-
return
|
|
136418
|
+
return nonEmpty3(element.user_name) ? `@${element.user_name}` : "";
|
|
136106
136419
|
}
|
|
136107
136420
|
case "code_block":
|
|
136108
136421
|
return `\`\`\`${element.language ?? ""}
|
|
@@ -136142,7 +136455,7 @@ var serializePost = (content, botIds = /* @__PURE__ */ new Set()) => {
|
|
|
136142
136455
|
for (const element of elements) {
|
|
136143
136456
|
const index = indexOf.get(element);
|
|
136144
136457
|
const key = element.tag === "img" ? element.image_key : element.file_key;
|
|
136145
|
-
if (index !== void 0 &&
|
|
136458
|
+
if (index !== void 0 && nonEmpty3(key)) {
|
|
136146
136459
|
placeholderByKey.set(key, placeholderOf(element, index));
|
|
136147
136460
|
}
|
|
136148
136461
|
}
|
|
@@ -136469,6 +136782,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
136469
136782
|
chatId: message.chatId,
|
|
136470
136783
|
senderId: message.senderOpenId,
|
|
136471
136784
|
text: message.text,
|
|
136785
|
+
chatKind: message.chatType === "p2p" ? "private" : "group",
|
|
136472
136786
|
resources: message.resources.map((resource) => ({
|
|
136473
136787
|
key: resource.fileKey,
|
|
136474
136788
|
name: resource.name,
|
|
@@ -136486,18 +136800,40 @@ var makeLarkAdapter = (deps) => {
|
|
|
136486
136800
|
name: input.name,
|
|
136487
136801
|
memberOpenIds: input.memberIds
|
|
136488
136802
|
}),
|
|
136489
|
-
sendText: async (binding, text3) => {
|
|
136803
|
+
sendText: async (binding, text3, options) => {
|
|
136490
136804
|
const token = await tenantToken(binding);
|
|
136805
|
+
const receiveIdType = options?.targetKind === "user" ? "open_id" : "chat_id";
|
|
136491
136806
|
for (const chunk of splitForLark(text3)) {
|
|
136492
136807
|
const messageId = await sendChatText({
|
|
136493
136808
|
http: deps.http,
|
|
136494
136809
|
token,
|
|
136495
136810
|
chatId: binding.chatId,
|
|
136811
|
+
receiveIdType,
|
|
136496
136812
|
text: chunk
|
|
136497
136813
|
});
|
|
136498
136814
|
deps.log.info("lark delivered a message", { messageId, bot: binding.displayName });
|
|
136499
136815
|
}
|
|
136500
136816
|
},
|
|
136817
|
+
checkConnection: async (bot) => {
|
|
136818
|
+
const { appId, appSecret } = credentials(bot);
|
|
136819
|
+
if (appId === "" || appSecret === "") {
|
|
136820
|
+
return {
|
|
136821
|
+
stage: "credentials",
|
|
136822
|
+
ok: false,
|
|
136823
|
+
detail: "missing app id or secret in saved config"
|
|
136824
|
+
};
|
|
136825
|
+
}
|
|
136826
|
+
try {
|
|
136827
|
+
const { expiresInSeconds } = await fetchTenantToken({ http: deps.http, appId, appSecret });
|
|
136828
|
+
return {
|
|
136829
|
+
stage: "tenant_access_token",
|
|
136830
|
+
ok: true,
|
|
136831
|
+
detail: `token ok; expiresIn=${String(expiresInSeconds)}s`
|
|
136832
|
+
};
|
|
136833
|
+
} catch (error) {
|
|
136834
|
+
return { stage: "tenant_access_token", ok: false, detail: describeError(error) };
|
|
136835
|
+
}
|
|
136836
|
+
},
|
|
136501
136837
|
sendImage: async (binding, image2) => {
|
|
136502
136838
|
const token = await tenantToken(binding);
|
|
136503
136839
|
const imageKey = await uploadImage({
|
|
@@ -136630,7 +136966,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
136630
136966
|
var en_default = {
|
|
136631
136967
|
settings: {
|
|
136632
136968
|
title: "Chat",
|
|
136633
|
-
description: "Connect chat bots to Ycodium. One row per bot: create the bot on its platform, paste its credentials, and list the people allowed to use it. For Lark, Connect a Lark bot above does all of that with one scan.
|
|
136969
|
+
description: "Connect chat bots to Ycodium. One row per bot: create the bot on its platform, paste its credentials, and pick an access mode (allowlist / open). In allowlist mode, list the people allowed to use it. For Lark, Connect a Lark bot above does all of that with one scan. Admitted people message the bot (or add it to a group), pick a project in the chat, and can start work.",
|
|
136634
136970
|
bots: "Bots",
|
|
136635
136971
|
language: {
|
|
136636
136972
|
label: "Reply language",
|
|
@@ -136648,12 +136984,53 @@ var en_default = {
|
|
|
136648
136984
|
always: "Only when it is mentioned",
|
|
136649
136985
|
never: "Every message",
|
|
136650
136986
|
ambient: "Every message, except ones aimed at someone else"
|
|
136987
|
+
},
|
|
136988
|
+
deliveryHttp: {
|
|
136989
|
+
enabled: {
|
|
136990
|
+
label: "Enable proactive delivery HTTP",
|
|
136991
|
+
description: "Off by default. When on, listens only on 127.0.0.1 so local scripts can POST botId+targetId text. Do not expose publicly; set a Bearer token when other local processes might call it."
|
|
136992
|
+
},
|
|
136993
|
+
port: {
|
|
136994
|
+
label: "Delivery HTTP port",
|
|
136995
|
+
description: "Bound to 127.0.0.1 only. Default 3847."
|
|
136996
|
+
},
|
|
136997
|
+
token: {
|
|
136998
|
+
label: "Delivery HTTP token",
|
|
136999
|
+
description: "Optional. When set, requests must send Authorization: Bearer <token>."
|
|
137000
|
+
}
|
|
137001
|
+
},
|
|
137002
|
+
deliveryTargets: {
|
|
137003
|
+
label: "Delivery targets",
|
|
137004
|
+
description: "Callers store only botId (settings row id) and targetId. Keep platform chat ids here; do not send them on each request."
|
|
137005
|
+
},
|
|
137006
|
+
deliveryKind: {
|
|
137007
|
+
chat: "Chat",
|
|
137008
|
+
user: "User / DM",
|
|
137009
|
+
group: "Group"
|
|
137010
|
+
},
|
|
137011
|
+
queueMode: {
|
|
137012
|
+
queue: "Queue (default)",
|
|
137013
|
+
steer: "Steer (auto-join the running turn)"
|
|
137014
|
+
},
|
|
137015
|
+
onOff: {
|
|
137016
|
+
on: "On",
|
|
137017
|
+
off: "Off"
|
|
137018
|
+
},
|
|
137019
|
+
larkPanelMode: {
|
|
137020
|
+
default: "Default (all commands)",
|
|
137021
|
+
minimal: "Minimal"
|
|
137022
|
+
},
|
|
137023
|
+
accessMode: {
|
|
137024
|
+
allowlist: "Allowlist only (default)",
|
|
137025
|
+
open: "Allow everyone"
|
|
136651
137026
|
}
|
|
136652
137027
|
},
|
|
136653
137028
|
platform: {
|
|
136654
137029
|
lark: "Lark",
|
|
136655
137030
|
telegram: "Telegram",
|
|
136656
|
-
wecom: "WeCom"
|
|
137031
|
+
wecom: "WeCom",
|
|
137032
|
+
"wecom-app": "WeCom App",
|
|
137033
|
+
wechat: "WeChat (experimental)"
|
|
136657
137034
|
},
|
|
136658
137035
|
column: {
|
|
136659
137036
|
displayName: {
|
|
@@ -136681,7 +137058,7 @@ var en_default = {
|
|
|
136681
137058
|
},
|
|
136682
137059
|
allowedSenders: {
|
|
136683
137060
|
label: "Allowed people",
|
|
136684
|
-
description: "Comma-separated user ids from that platform.
|
|
137061
|
+
description: "Comma-separated user ids from that platform. Used only in allowlist mode; while the list is empty, nobody can. Message the bot and it tells you your own id."
|
|
136685
137062
|
},
|
|
136686
137063
|
proxy: {
|
|
136687
137064
|
label: "Proxy",
|
|
@@ -136689,11 +137066,86 @@ var en_default = {
|
|
|
136689
137066
|
},
|
|
136690
137067
|
runtimeMode: {
|
|
136691
137068
|
label: "What a chat message may do",
|
|
136692
|
-
description: "
|
|
137069
|
+
description: "Admitted people start turns from chat, so this is deliberately not full access by default. Whichever mode asks, the request lands in the chat."
|
|
136693
137070
|
},
|
|
136694
137071
|
groupMention: {
|
|
136695
137072
|
label: "In groups, answer",
|
|
136696
137073
|
description: "When the bot answers in a group (unset means only when it is mentioned); direct chats always count. WeCom only delivers messages that mention the bot, so this changes nothing there."
|
|
137074
|
+
},
|
|
137075
|
+
deliveryBotId: {
|
|
137076
|
+
label: "Bot ID",
|
|
137077
|
+
description: "The settings-table row id for that bot (opaque).",
|
|
137078
|
+
placeholder: "bot row id"
|
|
137079
|
+
},
|
|
137080
|
+
deliveryTargetId: {
|
|
137081
|
+
label: "Target ID",
|
|
137082
|
+
description: "Caller alias unique under that bot; immutable after save.",
|
|
137083
|
+
placeholder: "release-alerts"
|
|
137084
|
+
},
|
|
137085
|
+
deliveryName: {
|
|
137086
|
+
label: "Display name",
|
|
137087
|
+
placeholder: "Release alerts"
|
|
137088
|
+
},
|
|
137089
|
+
deliveryKind: {
|
|
137090
|
+
label: "Kind"
|
|
137091
|
+
},
|
|
137092
|
+
deliveryPlatformChatId: {
|
|
137093
|
+
label: "Platform chat ID",
|
|
137094
|
+
description: "Lark open_id/chat_id, Telegram chat id, WeCom user/group id, etc.",
|
|
137095
|
+
placeholder: "oc_\u2026 / ou_\u2026 / -100\u2026"
|
|
137096
|
+
},
|
|
137097
|
+
queueMode: {
|
|
137098
|
+
label: "New messages while busy",
|
|
137099
|
+
description: "When a turn is already running, how to handle new plain-text messages. Queue waits for the current turn (Steer is still a button); Steer auto-joins the running turn."
|
|
137100
|
+
},
|
|
137101
|
+
larkPanelMode: {
|
|
137102
|
+
label: "Command panel",
|
|
137103
|
+
description: "Which commands /panel (or typing /) lists. Minimal hides low-frequency system items; typing the same command still works."
|
|
137104
|
+
},
|
|
137105
|
+
wecomNumberedHints: {
|
|
137106
|
+
label: "Numbered-reply hints",
|
|
137107
|
+
description: "WeCom has no buttons; approvals and questions use reply numbers. When on, include fuller usage hints."
|
|
137108
|
+
},
|
|
137109
|
+
wecomAppCorpId: {
|
|
137110
|
+
label: "Corp ID",
|
|
137111
|
+
placeholder: "ww\u2026"
|
|
137112
|
+
},
|
|
137113
|
+
wecomAppAgentId: {
|
|
137114
|
+
label: "AgentId",
|
|
137115
|
+
placeholder: "1000002"
|
|
137116
|
+
},
|
|
137117
|
+
wecomAppSecret: {
|
|
137118
|
+
label: "App Secret"
|
|
137119
|
+
},
|
|
137120
|
+
wecomAppToken: {
|
|
137121
|
+
label: "Callback Token"
|
|
137122
|
+
},
|
|
137123
|
+
wecomAppEncodingAESKey: {
|
|
137124
|
+
label: "EncodingAESKey",
|
|
137125
|
+
placeholder: "43 alphanumeric chars"
|
|
137126
|
+
},
|
|
137127
|
+
wecomAppCallbackBaseUrl: {
|
|
137128
|
+
label: "Public callback base URL (optional)",
|
|
137129
|
+
description: "When set, logs print the full callback URL to paste into WeCom \u201CReceive messages \u2192 API receive\u201D. Default listener is 127.0.0.1; use a reverse proxy or tunnel.",
|
|
137130
|
+
placeholder: "https://im.example.com or http://IP:30987"
|
|
137131
|
+
},
|
|
137132
|
+
wecomAppApiBaseUrl: {
|
|
137133
|
+
label: "API proxy base (optional)",
|
|
137134
|
+
description: "Base URL for gettoken / message/send. Leave empty for qyapi.weixin.qq.com. Outbound IP must be on the trusted IP list.",
|
|
137135
|
+
placeholder: "https://qyapi.weixin.qq.com"
|
|
137136
|
+
},
|
|
137137
|
+
wecomAppListenPort: {
|
|
137138
|
+
label: "Local callback port (optional)",
|
|
137139
|
+
description: "Defaults to 30987 on 127.0.0.1. Shared across self-built apps.",
|
|
137140
|
+
placeholder: "30987"
|
|
137141
|
+
},
|
|
137142
|
+
wechatBotToken: {
|
|
137143
|
+
label: "WeChat bot token",
|
|
137144
|
+
description: "bot_token from QR binding. Live iLink login is gated by proposal 0069; filling this in does not connect yet."
|
|
137145
|
+
},
|
|
137146
|
+
accessMode: {
|
|
137147
|
+
label: "Access mode",
|
|
137148
|
+
description: 'Allowlist admits only "Allowed people"; open admits any identifiable sender. New bots default to allowlist.'
|
|
136697
137149
|
}
|
|
136698
137150
|
},
|
|
136699
137151
|
reply: {
|
|
@@ -136722,7 +137174,8 @@ var en_default = {
|
|
|
136722
137174
|
allow: "Allow",
|
|
136723
137175
|
deny: "Deny",
|
|
136724
137176
|
alreadyHandled: "Already handled",
|
|
136725
|
-
replyHint: "Reply with the number."
|
|
137177
|
+
replyHint: "Reply with the number, or reply approve/deny.",
|
|
137178
|
+
replyHintFull: "Reply with the number. You can also reply approve/deny; for multi-select questions, comma-separate numbers."
|
|
136726
137179
|
},
|
|
136727
137180
|
question: {
|
|
136728
137181
|
title: "\u2753 Question",
|
|
@@ -136815,7 +137268,8 @@ var en_default = {
|
|
|
136815
137268
|
plan: "\xB7 /plan \u2014 toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it",
|
|
136816
137269
|
panel: "Send /panel to open the command panel with a button for every command.",
|
|
136817
137270
|
repo: "\xB7 /repo \u2014 pick the project this chat works on; /repo number or /repo name picks directly",
|
|
136818
|
-
group: "\xB7 /group name \u2014 Lark only: the bot creates a group and adds you (bots on other platforms cannot)"
|
|
137271
|
+
group: "\xB7 /group name \u2014 Lark only: the bot creates a group and adds you (bots on other platforms cannot)",
|
|
137272
|
+
syncNote: "\xB7 After a private chat attaches a session, web user text and final assistant text sync here (groups never sync)."
|
|
136819
137273
|
},
|
|
136820
137274
|
sessions: {
|
|
136821
137275
|
title: "Sessions this bot opened for this chat's project ({{count}}):",
|
|
@@ -136827,7 +137281,8 @@ var en_default = {
|
|
|
136827
137281
|
badge: {
|
|
136828
137282
|
current: "\u2605 current",
|
|
136829
137283
|
running: "\u25CF running",
|
|
136830
|
-
elsewhere: "in another chat"
|
|
137284
|
+
elsewhere: "in another chat",
|
|
137285
|
+
web: "web"
|
|
136831
137286
|
},
|
|
136832
137287
|
age: {
|
|
136833
137288
|
justNow: "just now",
|
|
@@ -136842,7 +137297,10 @@ var en_default = {
|
|
|
136842
137297
|
already: "This chat is already on that session.",
|
|
136843
137298
|
gone: "That session was deleted or archived, so it cannot be resumed.",
|
|
136844
137299
|
runningElsewhere: "That session is running a turn in another chat \u2014 stop it there first.",
|
|
136845
|
-
done: 'Switched to "{{title}}". Send a message to keep going from there.'
|
|
137300
|
+
done: 'Switched to "{{title}}". Send a message to keep going from there.',
|
|
137301
|
+
confirmWeb: '"{{title}}" was opened on the web. Send /resume with the same number again (or /resume confirm) to attach; /resume cancel to abort.',
|
|
137302
|
+
cancelled: "Attach cancelled.",
|
|
137303
|
+
nothingPending: "No web session is waiting for confirm. Use /sessions to see the list."
|
|
136846
137304
|
},
|
|
136847
137305
|
model: {
|
|
136848
137306
|
current: "Model: {{model}}",
|
|
@@ -137044,6 +137502,44 @@ var en_default = {
|
|
|
137044
137502
|
unsupported: "Bots on this platform cannot create groups. Create the group in the app and add the bot to it.",
|
|
137045
137503
|
noSender: "I cannot tell who you are on this platform, so I cannot add you to a new group.",
|
|
137046
137504
|
failed: "Could not create the group. Check that the app is allowed to create groups and add members, then try again."
|
|
137505
|
+
},
|
|
137506
|
+
sync: {
|
|
137507
|
+
webUser: "Web: {{text}}"
|
|
137508
|
+
},
|
|
137509
|
+
rowAction: {
|
|
137510
|
+
checkConnection: {
|
|
137511
|
+
label: "Check connection",
|
|
137512
|
+
description: "Verify platform credentials still work; does not open a session or call a model.",
|
|
137513
|
+
running: "Checking {{rowId}}\u2026",
|
|
137514
|
+
done: "Connection OK",
|
|
137515
|
+
failed: "Connection failed (ref {{ref}})",
|
|
137516
|
+
notSaved: "Save this row first (credentials are read from saved config only)."
|
|
137517
|
+
},
|
|
137518
|
+
copyDiagnostics: {
|
|
137519
|
+
label: "Copy diagnostics",
|
|
137520
|
+
description: "Run a connection probe and produce copy-paste diagnostics (no secrets).",
|
|
137521
|
+
running: "Building diagnostics for {{rowId}}\u2026",
|
|
137522
|
+
done: "Diagnostics ready \u2014 see details below",
|
|
137523
|
+
failed: "Diagnostics failed (ref {{ref}})",
|
|
137524
|
+
notSaved: "Save this row first before copying diagnostics."
|
|
137525
|
+
},
|
|
137526
|
+
diagnostic: {
|
|
137527
|
+
ref: "Ref {{ref}}\n{{detail}}"
|
|
137528
|
+
},
|
|
137529
|
+
larkRepair: {
|
|
137530
|
+
label: "Repair scopes",
|
|
137531
|
+
description: "Verify the Lark app can still mint a token and list scope / publish steps.",
|
|
137532
|
+
step: {
|
|
137533
|
+
scopes: "Confirm message / resource / panel scopes",
|
|
137534
|
+
events: "Confirm long connection and event subscriptions",
|
|
137535
|
+
publish: "Publish if scopes were added",
|
|
137536
|
+
verify: "Verify the app can still mint a tenant token"
|
|
137537
|
+
},
|
|
137538
|
+
done: "Credentials work; add any missing scopes in the Feishu console and publish",
|
|
137539
|
+
hint: "Ref {{ref}}\n{{detail}}\nIf scopes are missing, request them on the Feishu open platform and publish; a chat /repair command will follow.",
|
|
137540
|
+
failed: "Could not verify the Lark app (ref {{ref}})",
|
|
137541
|
+
notLark: "This row is not a saved Lark bot."
|
|
137542
|
+
}
|
|
137047
137543
|
}
|
|
137048
137544
|
};
|
|
137049
137545
|
|
|
@@ -137051,7 +137547,7 @@ var en_default = {
|
|
|
137051
137547
|
var zh_CN_default = {
|
|
137052
137548
|
settings: {
|
|
137053
137549
|
title: "\u804A\u5929",
|
|
137054
|
-
description: "\u628A\u804A\u5929\u673A\u5668\u4EBA\u63A5\u5230 Ycodium\u3002\u4E00\u884C\u4E00\u4E2A\u673A\u5668\u4EBA\uFF1A\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u5EFA\u597D\u673A\u5668\u4EBA\u3001\u628A\u51ED\u636E\u586B\u8FDB\u6765\uFF0C\u518D\u628A\u5141\u8BB8\u4F7F\u7528\u5B83\u7684\u4EBA\u586B\u8FDB\u300C\u5141\u8BB8\u7684\u4EBA\u300D\u3002\u98DE\u4E66\u53EF\u4EE5\u7528\u4E0A\u9762\u7684\u300C\u8FDE\u63A5\u98DE\u4E66\u673A\u5668\u4EBA\u300D\uFF0C\u626B\u4E00\u6B21\u7801\u5168\u90E8\u914D\u597D\u3002\
|
|
137550
|
+
description: "\u628A\u804A\u5929\u673A\u5668\u4EBA\u63A5\u5230 Ycodium\u3002\u4E00\u884C\u4E00\u4E2A\u673A\u5668\u4EBA\uFF1A\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u5EFA\u597D\u673A\u5668\u4EBA\u3001\u628A\u51ED\u636E\u586B\u8FDB\u6765\uFF0C\u518D\u9009\u8BBF\u95EE\u6A21\u5F0F\uFF08\u4EC5\u767D\u540D\u5355\uFF0F\u5141\u8BB8\u6240\u6709\u4EBA\uFF09\u3002\u4EC5\u767D\u540D\u5355\u65F6\u628A\u5141\u8BB8\u4F7F\u7528\u5B83\u7684\u4EBA\u586B\u8FDB\u300C\u5141\u8BB8\u7684\u4EBA\u300D\u3002\u98DE\u4E66\u53EF\u4EE5\u7528\u4E0A\u9762\u7684\u300C\u8FDE\u63A5\u98DE\u4E66\u673A\u5668\u4EBA\u300D\uFF0C\u626B\u4E00\u6B21\u7801\u5168\u90E8\u914D\u597D\u3002\u83B7\u51C6\u7684\u4EBA\u79C1\u804A\u673A\u5668\u4EBA\uFF08\u6216\u628A\u5B83\u62C9\u8FDB\u7FA4\uFF09\u3001\u5728\u804A\u5929\u91CC\u9009\u597D\u9879\u76EE\uFF0C\u5C31\u80FD\u6D3E\u6D3B\u3002",
|
|
137055
137551
|
bots: "\u673A\u5668\u4EBA",
|
|
137056
137552
|
language: {
|
|
137057
137553
|
label: "\u56DE\u8BDD\u8BED\u8A00",
|
|
@@ -137069,12 +137565,53 @@ var zh_CN_default = {
|
|
|
137069
137565
|
always: "\u88AB @ \u624D\u7406",
|
|
137070
137566
|
never: "\u6BCF\u53E5\u90FD\u7406",
|
|
137071
137567
|
ambient: "\u6BCF\u53E5\u90FD\u7406\uFF0C\u660E\u663E\u5BF9\u522B\u4EBA\u8BF4\u7684\u9664\u5916"
|
|
137568
|
+
},
|
|
137569
|
+
deliveryHttp: {
|
|
137570
|
+
enabled: {
|
|
137571
|
+
label: "\u5F00\u542F\u4E3B\u52A8\u6295\u9012 HTTP",
|
|
137572
|
+
description: "\u9ED8\u8BA4\u5173\u95ED\u3002\u5F00\u542F\u540E\u53EA\u5728\u672C\u673A 127.0.0.1 \u76D1\u542C\uFF0C\u4F9B\u811A\u672C\u7528 botId+targetId \u63A8\u6587\u5B57\u3002\u4E0D\u8981\u66B4\u9732\u5230\u516C\u7F51\uFF1B\u9700\u8981\u65F6\u53EF\u518D\u914D Bearer token\u3002"
|
|
137573
|
+
},
|
|
137574
|
+
port: {
|
|
137575
|
+
label: "\u6295\u9012 HTTP \u7AEF\u53E3",
|
|
137576
|
+
description: "\u53EA\u542C 127.0.0.1\u3002\u9ED8\u8BA4 3847\u3002"
|
|
137577
|
+
},
|
|
137578
|
+
token: {
|
|
137579
|
+
label: "\u6295\u9012 HTTP Token",
|
|
137580
|
+
description: "\u53EF\u9009\u3002\u586B\u4E86\u5219\u8BF7\u6C42\u5FC5\u987B\u5E26 Authorization: Bearer <token>\u3002\u540C\u673A\u5176\u5B83\u8FDB\u7A0B\u4E5F\u80FD\u6253 loopback\uFF0C\u5EFA\u8BAE\u751F\u4EA7\u5F00\u542F\u3002"
|
|
137581
|
+
}
|
|
137582
|
+
},
|
|
137583
|
+
deliveryTargets: {
|
|
137584
|
+
label: "\u6295\u9012\u76EE\u6807",
|
|
137585
|
+
description: "\u8C03\u7528\u65B9\u53EA\u4FDD\u5B58 botId\uFF08\u673A\u5668\u4EBA\u884C id\uFF09\u4E0E targetId\u3002\u5E73\u53F0\u539F\u751F\u804A\u5929 id \u5199\u5728\u8FD9\u91CC\uFF0C\u4E0D\u8981\u968F\u6BCF\u6B21\u8BF7\u6C42\u63D0\u4EA4\u3002"
|
|
137586
|
+
},
|
|
137587
|
+
deliveryKind: {
|
|
137588
|
+
chat: "\u804A\u5929",
|
|
137589
|
+
user: "\u7528\u6237\uFF0F\u79C1\u804A",
|
|
137590
|
+
group: "\u7FA4"
|
|
137591
|
+
},
|
|
137592
|
+
queueMode: {
|
|
137593
|
+
queue: "\u6392\u961F\uFF08\u9ED8\u8BA4\uFF09",
|
|
137594
|
+
steer: "\u63D2\u8BDD\uFF08\u81EA\u52A8\u5E76\u5165\u5F53\u524D\u8F6E\uFF09"
|
|
137595
|
+
},
|
|
137596
|
+
onOff: {
|
|
137597
|
+
on: "\u5F00",
|
|
137598
|
+
off: "\u5173"
|
|
137599
|
+
},
|
|
137600
|
+
larkPanelMode: {
|
|
137601
|
+
default: "\u9ED8\u8BA4\uFF08\u5168\u90E8\u547D\u4EE4\uFF09",
|
|
137602
|
+
minimal: "\u7CBE\u7B80"
|
|
137603
|
+
},
|
|
137604
|
+
accessMode: {
|
|
137605
|
+
allowlist: "\u4EC5\u767D\u540D\u5355\uFF08\u9ED8\u8BA4\uFF09",
|
|
137606
|
+
open: "\u5141\u8BB8\u6240\u6709\u4EBA"
|
|
137072
137607
|
}
|
|
137073
137608
|
},
|
|
137074
137609
|
platform: {
|
|
137075
137610
|
lark: "\u98DE\u4E66",
|
|
137076
137611
|
telegram: "Telegram",
|
|
137077
|
-
wecom: "\u4F01\u4E1A\u5FAE\u4FE1"
|
|
137612
|
+
wecom: "\u4F01\u4E1A\u5FAE\u4FE1",
|
|
137613
|
+
"wecom-app": "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528",
|
|
137614
|
+
wechat: "\u5FAE\u4FE1\uFF08\u5B9E\u9A8C\uFF09"
|
|
137078
137615
|
},
|
|
137079
137616
|
column: {
|
|
137080
137617
|
displayName: {
|
|
@@ -137102,7 +137639,7 @@ var zh_CN_default = {
|
|
|
137102
137639
|
},
|
|
137103
137640
|
allowedSenders: {
|
|
137104
137641
|
label: "\u5141\u8BB8\u7684\u4EBA",
|
|
137105
|
-
description: "\u8BE5\u5E73\u53F0\u7684\u7528\u6237 id\uFF0C\u9017\u53F7\u5206\u9694\u3002\
|
|
137642
|
+
description: "\u8BE5\u5E73\u53F0\u7684\u7528\u6237 id\uFF0C\u9017\u53F7\u5206\u9694\u3002\u4EC5\u5728\u300C\u4EC5\u767D\u540D\u5355\u300D\u6A21\u5F0F\u4E0B\u751F\u6548\uFF1B\u7559\u7A7A\u5219\u8C01\u90FD\u4E0D\u80FD\u3002\u7ED9\u673A\u5668\u4EBA\u53D1\u4E00\u53E5\u8BDD\uFF0C\u5B83\u4F1A\u544A\u8BC9\u4F60\u81EA\u5DF1\u7684 id\u3002"
|
|
137106
137643
|
},
|
|
137107
137644
|
proxy: {
|
|
137108
137645
|
label: "\u4EE3\u7406",
|
|
@@ -137110,11 +137647,86 @@ var zh_CN_default = {
|
|
|
137110
137647
|
},
|
|
137111
137648
|
runtimeMode: {
|
|
137112
137649
|
label: "\u804A\u5929\u6D88\u606F\u80FD\u505A\u4EC0\u4E48",
|
|
137113
|
-
description: "\
|
|
137650
|
+
description: "\u83B7\u51C6\u7684\u4EBA\u90FD\u80FD\u4ECE\u804A\u5929\u91CC\u5F00\u4E00\u8F6E\uFF0C\u6240\u4EE5\u9ED8\u8BA4\u6545\u610F\u4E0D\u7ED9\u6EE1\u6743\u9650\u3002\u4E0D\u8BBA\u9009\u54EA\u4E00\u6863\uFF0C\u8981\u62CD\u677F\u7684\u4E8B\u90FD\u4F1A\u6446\u5230\u804A\u5929\u91CC\u3002"
|
|
137114
137651
|
},
|
|
137115
137652
|
groupMention: {
|
|
137116
137653
|
label: "\u7FA4\u91CC\u4EC0\u4E48\u65F6\u5019\u7406\u5B83",
|
|
137117
137654
|
description: "\u673A\u5668\u4EBA\u5728\u7FA4\u91CC\u4EC0\u4E48\u65F6\u5019\u56DE\u5E94\uFF08\u4E0D\u9009\u5C31\u662F\u88AB @ \u624D\u7406\uFF09\uFF1B\u5355\u804A\u6C38\u8FDC\u7406\u3002\u4F01\u4E1A\u5FAE\u4FE1\u53EA\u628A @ \u5230\u673A\u5668\u4EBA\u7684\u7FA4\u6D88\u606F\u63A8\u8FC7\u6765\uFF0C\u8FD9\u4E00\u9879\u5BF9\u5B83\u6CA1\u6709\u6548\u679C\u3002"
|
|
137655
|
+
},
|
|
137656
|
+
deliveryBotId: {
|
|
137657
|
+
label: "Bot ID",
|
|
137658
|
+
description: "\u8BBE\u7F6E\u8868\u91CC\u8BE5\u673A\u5668\u4EBA\u90A3\u4E00\u884C\u7684 id\uFF08\u4E0D\u900F\u660E\u5B57\u7B26\u4E32\uFF09\u3002",
|
|
137659
|
+
placeholder: "\u673A\u5668\u4EBA\u884C id"
|
|
137660
|
+
},
|
|
137661
|
+
deliveryTargetId: {
|
|
137662
|
+
label: "Target ID",
|
|
137663
|
+
description: "\u540C\u4E00\u673A\u5668\u4EBA\u4E0B\u552F\u4E00\u7684\u522B\u540D\uFF1B\u4FDD\u5B58\u540E\u52FF\u6539\u3002",
|
|
137664
|
+
placeholder: "release-alerts"
|
|
137665
|
+
},
|
|
137666
|
+
deliveryName: {
|
|
137667
|
+
label: "\u663E\u793A\u540D",
|
|
137668
|
+
placeholder: "\u53D1\u5E03\u544A\u8B66\u7FA4"
|
|
137669
|
+
},
|
|
137670
|
+
deliveryKind: {
|
|
137671
|
+
label: "\u7C7B\u578B"
|
|
137672
|
+
},
|
|
137673
|
+
deliveryPlatformChatId: {
|
|
137674
|
+
label: "\u5E73\u53F0\u804A\u5929 ID",
|
|
137675
|
+
description: "\u98DE\u4E66 open_id\uFF0Fchat_id\u3001Telegram chat id\u3001\u4F01\u5FAE\u7528\u6237\uFF0F\u7FA4 id \u7B49\u3002",
|
|
137676
|
+
placeholder: "oc_\u2026 / ou_\u2026 / -100\u2026"
|
|
137677
|
+
},
|
|
137678
|
+
queueMode: {
|
|
137679
|
+
label: "\u5FD9\u65F6\u65B0\u6D88\u606F",
|
|
137680
|
+
description: "\u5F53\u524D\u4EFB\u52A1\u8FD8\u5728\u8DD1\u65F6\uFF0C\u65B0\u6765\u7684\u666E\u901A\u6587\u5B57\u600E\u4E48\u5904\u7406\u3002\u6392\u961F\u4F1A\u7B49\u5F53\u524D\u8F6E\u7ED3\u675F\uFF08\u53EF\u70B9\u63D2\u8BDD\uFF09\uFF1B\u63D2\u8BDD\u5219\u81EA\u52A8\u5E76\u5165\u5F53\u524D\u8F6E\uFF0C\u65E0\u9700\u6BCF\u6B21\u70B9\u6309\u94AE\u3002"
|
|
137681
|
+
},
|
|
137682
|
+
larkPanelMode: {
|
|
137683
|
+
label: "\u6307\u4EE4\u9762\u677F",
|
|
137684
|
+
description: "\u804A\u5929\u91CC /panel \u6216\u6253 / \u65F6\u5217\u51FA\u7684\u547D\u4EE4\u8303\u56F4\u3002\u7CBE\u7B80\u9690\u85CF\u4F4E\u9891\u7CFB\u7EDF\u9879\uFF1B\u624B\u52A8\u8F93\u5165\u540C\u4E00\u547D\u4EE4\u4ECD\u53EF\u7528\u3002"
|
|
137685
|
+
},
|
|
137686
|
+
wecomNumberedHints: {
|
|
137687
|
+
label: "\u7F16\u53F7\u5E94\u7B54\u63D0\u793A",
|
|
137688
|
+
description: "\u4F01\u5FAE\u6CA1\u6709\u6309\u94AE\uFF0C\u5BA1\u6279\u4E0E\u63D0\u95EE\u9760\u56DE\u7F16\u53F7\u3002\u5F00\u542F\u65F6\u9644\u5E26\u66F4\u5B8C\u6574\u7684\u7528\u6CD5\u8BF4\u660E\u3002"
|
|
137689
|
+
},
|
|
137690
|
+
wecomAppCorpId: {
|
|
137691
|
+
label: "\u4F01\u4E1A ID\uFF08CorpID\uFF09",
|
|
137692
|
+
placeholder: "ww\u2026"
|
|
137693
|
+
},
|
|
137694
|
+
wecomAppAgentId: {
|
|
137695
|
+
label: "\u5E94\u7528 AgentId",
|
|
137696
|
+
placeholder: "1000002"
|
|
137697
|
+
},
|
|
137698
|
+
wecomAppSecret: {
|
|
137699
|
+
label: "\u5E94\u7528 Secret"
|
|
137700
|
+
},
|
|
137701
|
+
wecomAppToken: {
|
|
137702
|
+
label: "\u56DE\u8C03 Token"
|
|
137703
|
+
},
|
|
137704
|
+
wecomAppEncodingAESKey: {
|
|
137705
|
+
label: "EncodingAESKey",
|
|
137706
|
+
placeholder: "43 \u4F4D\u5B57\u6BCD\u6216\u6570\u5B57"
|
|
137707
|
+
},
|
|
137708
|
+
wecomAppCallbackBaseUrl: {
|
|
137709
|
+
label: "\u516C\u7F51\u56DE\u8C03\u57FA\u5740\uFF08\u53EF\u9009\uFF09",
|
|
137710
|
+
description: "\u586B\u516C\u7F51\u53EF\u8FBE\u7684 http(s) \u57FA\u5740\u540E\uFF0C\u65E5\u5FD7\u4F1A\u6253\u5370\u5B8C\u6574\u56DE\u8C03 URL\uFF0C\u628A\u5B83\u8D34\u8FDB\u4F01\u5FAE\u300C\u63A5\u6536\u6D88\u606F \u2192 \u8BBE\u7F6E API \u63A5\u6536\u300D\u3002\u4E0D\u586B\u53EA\u663E\u793A\u672C\u673A\u8DEF\u5F84\u3002\u9ED8\u8BA4\u76D1\u542C 127.0.0.1\uFF0C\u9700\u53CD\u4EE3\u6216\u96A7\u9053\u3002",
|
|
137711
|
+
placeholder: "https://im.example.com \u6216 http://IP:30987"
|
|
137712
|
+
},
|
|
137713
|
+
wecomAppApiBaseUrl: {
|
|
137714
|
+
label: "API \u4EE3\u7406\u5730\u5740\uFF08\u53EF\u9009\uFF09",
|
|
137715
|
+
description: "\u51FA\u7AD9\u8C03\u7528 gettoken / message/send \u7684\u57FA\u5740\u3002\u6D77\u5916\u673A\u5668\u53EF\u586B\u53CD\u4EE3\uFF1B\u7559\u7A7A\u76F4\u8FDE qyapi.weixin.qq.com\u3002\u51FA\u7AD9 IP \u987B\u5728\u4F01\u4E1A\u53EF\u4FE1 IP \u5217\u8868\u3002",
|
|
137716
|
+
placeholder: "https://qyapi.weixin.qq.com"
|
|
137717
|
+
},
|
|
137718
|
+
wecomAppListenPort: {
|
|
137719
|
+
label: "\u672C\u673A\u56DE\u8C03\u7AEF\u53E3\uFF08\u53EF\u9009\uFF09",
|
|
137720
|
+
description: "\u9ED8\u8BA4 30987\uFF0C\u53EA\u542C 127.0.0.1\u3002\u591A\u53F0\u81EA\u5EFA\u5E94\u7528\u5171\u7528\u4E00\u4E2A\u7AEF\u53E3\u3002",
|
|
137721
|
+
placeholder: "30987"
|
|
137722
|
+
},
|
|
137723
|
+
wechatBotToken: {
|
|
137724
|
+
label: "\u5FAE\u4FE1\u673A\u5668\u4EBA\u4EE4\u724C",
|
|
137725
|
+
description: "\u626B\u7801\u7ED1\u5B9A\u540E\u5F97\u5230\u7684 bot_token\u3002\u771F\u767B\u5F55\u5C1A\u672A\u5F00\u653E\uFF1A\u586B\u4E86\u4E5F\u4E0D\u4F1A\u8FDE\u4E0A iLink\uFF0C\u89C1\u63D0\u6848 0069 \u5B89\u5168\u8BC4\u5BA1\u3002"
|
|
137726
|
+
},
|
|
137727
|
+
accessMode: {
|
|
137728
|
+
label: "\u8BBF\u95EE\u6A21\u5F0F",
|
|
137729
|
+
description: "\u4EC5\u767D\u540D\u5355\uFF1D\u53EA\u6709\u300C\u5141\u8BB8\u7684\u4EBA\u300D\u80FD\u89E6\u53D1\uFF1B\u5141\u8BB8\u6240\u6709\u4EBA\uFF1D\u5E73\u53F0\u80FD\u8BC6\u522B\u7684\u53D1\u9001\u8005\u5747\u53EF\u3002\u65B0\u673A\u5668\u4EBA\u9ED8\u8BA4\u4EC5\u767D\u540D\u5355\u3002"
|
|
137118
137730
|
}
|
|
137119
137731
|
},
|
|
137120
137732
|
reply: {
|
|
@@ -137143,7 +137755,8 @@ var zh_CN_default = {
|
|
|
137143
137755
|
allow: "\u5141\u8BB8",
|
|
137144
137756
|
deny: "\u62D2\u7EDD",
|
|
137145
137757
|
alreadyHandled: "\u5DF2\u5904\u7406",
|
|
137146
|
-
replyHint: "\u56DE\u590D\u7F16\u53F7\
|
|
137758
|
+
replyHint: "\u56DE\u590D\u7F16\u53F7\uFF0C\u6216\u56DE\u590D\u300C\u6279\u51C6\u300D/\u300C\u62D2\u7EDD\u300D\u3002",
|
|
137759
|
+
replyHintFull: "\u56DE\u590D\u7F16\u53F7\u5373\u53EF\u3002\u4E5F\u53EF\u56DE\u590D\u300C\u6279\u51C6\u300D/\u300C\u62D2\u7EDD\u300D\uFF1B\u591A\u9009\u63D0\u95EE\u7528\u9017\u53F7\u5206\u9694\u7F16\u53F7\u3002"
|
|
137147
137760
|
},
|
|
137148
137761
|
question: {
|
|
137149
137762
|
title: "\u2753 \u63D0\u95EE",
|
|
@@ -137236,7 +137849,8 @@ var zh_CN_default = {
|
|
|
137236
137849
|
plan: "\xB7 /plan \u2014\u2014 \u5F00\u5173\u8BA1\u5212\u6A21\u5F0F\uFF08agent \u5148\u51FA\u8BA1\u5212\u3001\u4E0D\u52A8\u624B\uFF09\uFF1B/plan on\u3001/plan off \u660E\u786E\u8BBE\u5B9A",
|
|
137237
137850
|
panel: "\u53D1 /panel \u6253\u5F00\u53EF\u70B9\u7684\u547D\u4EE4\u9762\u677F\u3002",
|
|
137238
137851
|
repo: "\xB7 /repo \u2014\u2014 \u9009\u8FD9\u4E2A\u804A\u5929\u505A\u54EA\u4E2A\u9879\u76EE\uFF1B/repo \u7F16\u53F7 \u6216 /repo \u9879\u76EE\u540D \u76F4\u63A5\u9009",
|
|
137239
|
-
group: "\xB7 /group \u7FA4\u540D \u2014\u2014 \u4EC5\u98DE\u4E66\uFF1A\u673A\u5668\u4EBA\u5EFA\u4E00\u4E2A\u7FA4\u5E76\u628A\u4F60\u62C9\u8FDB\u53BB\uFF08\u522B\u7684\u5E73\u53F0\u7684\u673A\u5668\u4EBA\u5EFA\u4E0D\u4E86\u7FA4\uFF09"
|
|
137852
|
+
group: "\xB7 /group \u7FA4\u540D \u2014\u2014 \u4EC5\u98DE\u4E66\uFF1A\u673A\u5668\u4EBA\u5EFA\u4E00\u4E2A\u7FA4\u5E76\u628A\u4F60\u62C9\u8FDB\u53BB\uFF08\u522B\u7684\u5E73\u53F0\u7684\u673A\u5668\u4EBA\u5EFA\u4E0D\u4E86\u7FA4\uFF09",
|
|
137853
|
+
syncNote: "\xB7 \u79C1\u804A\u9644\u7740\u4F1A\u8BDD\u540E\uFF0C\u7F51\u9875\u4E0A\u7684\u7528\u6237\u6587\u5B57\u4E0E\u7EC8\u6001\u52A9\u624B\u6587\u5B57\u4F1A\u540C\u6B65\u5230\u6B64\uFF08\u7FA4\u804A\u4E0D\u540C\u6B65\uFF09\u3002"
|
|
137240
137854
|
},
|
|
137241
137855
|
sessions: {
|
|
137242
137856
|
title: "\u672C\u673A\u5668\u4EBA\u4E3A\u8FD9\u4E2A\u804A\u5929\u7684\u9879\u76EE\u5F00\u8FC7\u7684\u4F1A\u8BDD\uFF08{{count}} \u6761\uFF09\uFF1A",
|
|
@@ -137248,7 +137862,8 @@ var zh_CN_default = {
|
|
|
137248
137862
|
badge: {
|
|
137249
137863
|
current: "\u2605 \u5F53\u524D",
|
|
137250
137864
|
running: "\u25CF \u5728\u8DD1",
|
|
137251
|
-
elsewhere: "\u63A5\u5728\u522B\u7684\u804A\u5929"
|
|
137865
|
+
elsewhere: "\u63A5\u5728\u522B\u7684\u804A\u5929",
|
|
137866
|
+
web: "\u7F51\u9875"
|
|
137252
137867
|
},
|
|
137253
137868
|
age: {
|
|
137254
137869
|
justNow: "\u521A\u521A",
|
|
@@ -137263,7 +137878,10 @@ var zh_CN_default = {
|
|
|
137263
137878
|
already: "\u8FD9\u4E2A\u804A\u5929\u5DF2\u7ECF\u5728\u90A3\u6761\u4F1A\u8BDD\u91CC\u4E86\u3002",
|
|
137264
137879
|
gone: "\u90A3\u6761\u4F1A\u8BDD\u5DF2\u88AB\u5220\u9664\u6216\u5F52\u6863\uFF0C\u63A5\u4E0D\u4E0A\u4E86\u3002",
|
|
137265
137880
|
runningElsewhere: "\u90A3\u6761\u4F1A\u8BDD\u6B63\u5728\u522B\u7684\u804A\u5929\u91CC\u8DD1\u7740\u4E00\u8F6E\u2014\u2014\u5148\u5230\u90A3\u8FB9\u505C\u6389\u5B83\u3002",
|
|
137266
|
-
done: "\u5DF2\u5207\u5230\u300C{{title}}\u300D\u3002\u53D1\u4E00\u53E5\u8BDD\u5C31\u4ECE\u90A3\u91CC\u63A5\u7740\u804A\u3002"
|
|
137881
|
+
done: "\u5DF2\u5207\u5230\u300C{{title}}\u300D\u3002\u53D1\u4E00\u53E5\u8BDD\u5C31\u4ECE\u90A3\u91CC\u63A5\u7740\u804A\u3002",
|
|
137882
|
+
confirmWeb: "\u300C{{title}}\u300D\u662F\u7F51\u9875\u5F00\u7684\u4F1A\u8BDD\u3002\u518D\u53D1\u4E00\u6B21 /resume \u540C\u4E00\u7F16\u53F7\uFF08\u6216 /resume confirm\uFF09\u786E\u8BA4\u9644\u7740\uFF1B/resume cancel \u53D6\u6D88\u3002",
|
|
137883
|
+
cancelled: "\u5DF2\u53D6\u6D88\u9644\u7740\u3002",
|
|
137884
|
+
nothingPending: "\u6CA1\u6709\u5F85\u786E\u8BA4\u7684\u7F51\u9875\u4F1A\u8BDD\u3002\u7528 /sessions \u770B\u770B\u6709\u54EA\u4E9B\u3002"
|
|
137267
137885
|
},
|
|
137268
137886
|
model: {
|
|
137269
137887
|
current: "\u6A21\u578B\uFF1A{{model}}",
|
|
@@ -137465,6 +138083,44 @@ var zh_CN_default = {
|
|
|
137465
138083
|
unsupported: "\u8FD9\u4E2A\u5E73\u53F0\u7684\u673A\u5668\u4EBA\u5EFA\u4E0D\u4E86\u7FA4\u3002\u8BF7\u5728 App \u91CC\u81EA\u5DF1\u5EFA\u7FA4\uFF0C\u518D\u628A\u673A\u5668\u4EBA\u62C9\u8FDB\u53BB\u3002",
|
|
137466
138084
|
noSender: "\u6211\u8BA4\u4E0D\u51FA\u4F60\u5728\u8FD9\u4E2A\u5E73\u53F0\u4E0A\u662F\u8C01\uFF0C\u6CA1\u6CD5\u628A\u4F60\u62C9\u8FDB\u65B0\u7FA4\u3002",
|
|
137467
138085
|
failed: "\u6CA1\u80FD\u5EFA\u7FA4\u3002\u8BF7\u786E\u8BA4\u8FD9\u4E2A\u5E94\u7528\u6709\u5EFA\u7FA4\u548C\u62C9\u4EBA\u7684\u6743\u9650\uFF0C\u518D\u8BD5\u4E00\u6B21\u3002"
|
|
138086
|
+
},
|
|
138087
|
+
sync: {
|
|
138088
|
+
webUser: "\u7F51\u9875\uFF1A{{text}}"
|
|
138089
|
+
},
|
|
138090
|
+
rowAction: {
|
|
138091
|
+
checkConnection: {
|
|
138092
|
+
label: "\u68C0\u67E5\u8FDE\u63A5",
|
|
138093
|
+
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",
|
|
138094
|
+
running: "\u6B63\u5728\u68C0\u67E5 {{rowId}} \u2026",
|
|
138095
|
+
done: "\u8FDE\u63A5\u6B63\u5E38",
|
|
138096
|
+
failed: "\u8FDE\u63A5\u5931\u8D25\uFF08\u53C2\u8003\u53F7 {{ref}}\uFF09",
|
|
138097
|
+
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"
|
|
138098
|
+
},
|
|
138099
|
+
copyDiagnostics: {
|
|
138100
|
+
label: "\u590D\u5236\u8BCA\u65AD",
|
|
138101
|
+
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",
|
|
138102
|
+
running: "\u6B63\u5728\u751F\u6210\u8BCA\u65AD {{rowId}} \u2026",
|
|
138103
|
+
done: "\u8BCA\u65AD\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
138104
|
+
failed: "\u8BCA\u65AD\u5931\u8D25\uFF08\u53C2\u8003\u53F7 {{ref}}\uFF09",
|
|
138105
|
+
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u590D\u5236\u8BCA\u65AD\u3002"
|
|
138106
|
+
},
|
|
138107
|
+
diagnostic: {
|
|
138108
|
+
ref: "\u53C2\u8003\u53F7 {{ref}}\n{{detail}}"
|
|
138109
|
+
},
|
|
138110
|
+
larkRepair: {
|
|
138111
|
+
label: "\u8865\u5168\u6743\u9650",
|
|
138112
|
+
description: "\u6838\u5BF9\u98DE\u4E66\u5E94\u7528\u4ECD\u80FD\u6362 token\uFF0C\u5E76\u63D0\u793A\u9700\u8981\u8865\u7684\u6743\u9650\u4E0E\u53D1\u5E03\u6B65\u9AA4\u3002",
|
|
138113
|
+
step: {
|
|
138114
|
+
scopes: "\u786E\u8BA4\u6D88\u606F / \u8D44\u6E90 / \u9762\u677F\u7B49\u6743\u9650",
|
|
138115
|
+
events: "\u786E\u8BA4\u957F\u8FDE\u63A5\u4E0E\u4E8B\u4EF6\u8BA2\u9605",
|
|
138116
|
+
publish: "\u5982\u6709\u589E\u91CF\u6743\u9650\uFF0C\u5B8C\u6210\u53D1\u5E03\u5BA1\u6279",
|
|
138117
|
+
verify: "\u9A8C\u8BC1\u5E94\u7528\u4ECD\u53EF\u6362\u53D6 tenant token"
|
|
138118
|
+
},
|
|
138119
|
+
done: "\u51ED\u636E\u53EF\u7528\uFF1B\u82E5\u6743\u9650\u4E0D\u8DB3\u8BF7\u6309\u98DE\u4E66\u63A7\u5236\u53F0\u8865\u9F50\u540E\u53D1\u5E03",
|
|
138120
|
+
hint: "\u53C2\u8003\u53F7 {{ref}}\n{{detail}}\n\u7F3A\u6743\u9650\u65F6\u53EF\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u5BF9\u8BE5\u5E94\u7528\u589E\u91CF\u7533\u8BF7\u5E76\u53D1\u5E03\uFF1B\u804A\u5929\u4FA7 /repair \u547D\u4EE4\u540E\u7EED\u8865\u9F50\u3002",
|
|
138121
|
+
failed: "\u65E0\u6CD5\u9A8C\u8BC1\u98DE\u4E66\u5E94\u7528\uFF08\u53C2\u8003\u53F7 {{ref}}\uFF09",
|
|
138122
|
+
notLark: "\u8FD9\u4E00\u884C\u4E0D\u662F\u5DF2\u4FDD\u5B58\u7684\u98DE\u4E66\u673A\u5668\u4EBA\u3002"
|
|
138123
|
+
}
|
|
137468
138124
|
}
|
|
137469
138125
|
};
|
|
137470
138126
|
|
|
@@ -137752,6 +138408,14 @@ var PANEL_RUN_LINES = Object.fromEntries(
|
|
|
137752
138408
|
spec.line
|
|
137753
138409
|
])
|
|
137754
138410
|
);
|
|
138411
|
+
var MINIMAL_HIDDEN = /* @__PURE__ */ new Set([
|
|
138412
|
+
"status",
|
|
138413
|
+
"help",
|
|
138414
|
+
"plan",
|
|
138415
|
+
"team",
|
|
138416
|
+
"thread"
|
|
138417
|
+
]);
|
|
138418
|
+
var panelCommandsFor = (mode = "default") => mode === "minimal" ? PANEL_COMMANDS.filter((spec) => !MINIMAL_HIDDEN.has(spec.id)) : PANEL_COMMANDS;
|
|
137755
138419
|
|
|
137756
138420
|
// src/sessionList.ts
|
|
137757
138421
|
var SESSION_LIST_MAX = 20;
|
|
@@ -137773,7 +138437,8 @@ var sessionLine = (row, position, now, t) => {
|
|
|
137773
138437
|
const badges = [
|
|
137774
138438
|
row.current ? t("sessions.badge.current") : null,
|
|
137775
138439
|
row.running ? t("sessions.badge.running") : null,
|
|
137776
|
-
row.elsewhere ? t("sessions.badge.elsewhere") : null
|
|
138440
|
+
row.elsewhere ? t("sessions.badge.elsewhere") : null,
|
|
138441
|
+
row.origin === "web" ? t("sessions.badge.web") : null
|
|
137777
138442
|
].filter((badge) => badge !== null);
|
|
137778
138443
|
return [`${String(position)}. ${title}`, age, ...badges, shortId(row.threadId)].filter((part) => part !== "").join(" \xB7 ");
|
|
137779
138444
|
};
|
|
@@ -137855,26 +138520,29 @@ var makePanelController = (deps) => {
|
|
|
137855
138520
|
return current === void 0 ? t("repo.needProject") : `${t("repo.currentLine", { title: current.title })}
|
|
137856
138521
|
${t("panel.projects.hint")}`;
|
|
137857
138522
|
};
|
|
137858
|
-
const menuCard = (chatKey) =>
|
|
137859
|
-
|
|
137860
|
-
|
|
137861
|
-
|
|
137862
|
-
|
|
137863
|
-
(
|
|
137864
|
-
|
|
137865
|
-
|
|
137866
|
-
(
|
|
137867
|
-
|
|
137868
|
-
spec
|
|
137869
|
-
|
|
137870
|
-
|
|
137871
|
-
|
|
138523
|
+
const menuCard = (chatKey) => {
|
|
138524
|
+
const commands = panelCommandsFor(deps.panelMode(chatKey));
|
|
138525
|
+
return {
|
|
138526
|
+
title: t("panel.title"),
|
|
138527
|
+
tone: "menu",
|
|
138528
|
+
text: t("panel.hint"),
|
|
138529
|
+
groups: PANEL_CATEGORIES.map(
|
|
138530
|
+
(category) => ({
|
|
138531
|
+
heading: t(`panel.category.${category}`),
|
|
138532
|
+
buttons: commands.filter((spec) => spec.category === category).map(
|
|
138533
|
+
(spec) => button(
|
|
138534
|
+
// 计划模式的按钮把当前状态写在标签上,点了之后标签跟着翻。
|
|
138535
|
+
spec.id === "plan" ? t(deps.planOn(chatKey) ? "panel.cmd.planOn" : "panel.cmd.planOff") : t(spec.labelKey),
|
|
138536
|
+
spec.action.op,
|
|
138537
|
+
spec.action.arg,
|
|
138538
|
+
spec.id === "stop"
|
|
138539
|
+
)
|
|
137872
138540
|
)
|
|
137873
|
-
)
|
|
137874
|
-
|
|
137875
|
-
|
|
137876
|
-
|
|
137877
|
-
}
|
|
138541
|
+
})
|
|
138542
|
+
),
|
|
138543
|
+
nav: []
|
|
138544
|
+
};
|
|
138545
|
+
};
|
|
137878
138546
|
const pickerCard = (input) => {
|
|
137879
138547
|
const total = pageCount(input.items.length);
|
|
137880
138548
|
const page = Math.min(Math.max(input.page, 0), total - 1);
|
|
@@ -138175,6 +138843,10 @@ var makePanelWiring = (core, deps) => {
|
|
|
138175
138843
|
};
|
|
138176
138844
|
},
|
|
138177
138845
|
planOn: (chatKey) => chatOf(core.state, chatKey).plan,
|
|
138846
|
+
panelMode: (chatKey) => {
|
|
138847
|
+
const binding = bindingForChatKey(chatKey);
|
|
138848
|
+
return binding?.platform === "lark" ? binding.larkPanelMode : "default";
|
|
138849
|
+
},
|
|
138178
138850
|
loadSessions: async (chatKey) => {
|
|
138179
138851
|
const binding = bindingForChatKey(chatKey);
|
|
138180
138852
|
if (binding === void 0) return [];
|
|
@@ -138570,6 +139242,242 @@ var makeReplyCards = (core) => {
|
|
|
138570
139242
|
return { turnCards, failureSummaries, stopRequested };
|
|
138571
139243
|
};
|
|
138572
139244
|
|
|
139245
|
+
// src/adapter.ts
|
|
139246
|
+
var ImNotConnectedError = class extends Error {
|
|
139247
|
+
constructor(message) {
|
|
139248
|
+
super(message);
|
|
139249
|
+
this.name = "ImNotConnectedError";
|
|
139250
|
+
}
|
|
139251
|
+
};
|
|
139252
|
+
|
|
139253
|
+
// src/deliveryContract.ts
|
|
139254
|
+
var DELIVERY_SEND_ERROR_CODES = [
|
|
139255
|
+
"bad-request",
|
|
139256
|
+
"unknown-bot",
|
|
139257
|
+
"unknown-target",
|
|
139258
|
+
"bot-not-connected",
|
|
139259
|
+
"delivery-failed"
|
|
139260
|
+
];
|
|
139261
|
+
var isDeliverySendErrorCode = (value) => typeof value === "string" && DELIVERY_SEND_ERROR_CODES.includes(value);
|
|
139262
|
+
var DeliverySendError = class extends Error {
|
|
139263
|
+
code;
|
|
139264
|
+
constructor(code2, message) {
|
|
139265
|
+
super(message);
|
|
139266
|
+
this.name = "DeliverySendError";
|
|
139267
|
+
this.code = code2;
|
|
139268
|
+
}
|
|
139269
|
+
};
|
|
139270
|
+
|
|
139271
|
+
// src/delivery.ts
|
|
139272
|
+
var makeDeliverySender = (core, config2) => {
|
|
139273
|
+
return async (input) => {
|
|
139274
|
+
const bots = core.bots();
|
|
139275
|
+
const bot = bots.find((candidate) => candidate.rowId === input.botId);
|
|
139276
|
+
if (bot === void 0) {
|
|
139277
|
+
throw new DeliverySendError("unknown-bot", `Unknown bot '${input.botId}'.`);
|
|
139278
|
+
}
|
|
139279
|
+
const target = findDeliveryTarget(readDeliveryTargets(config2), input.botId, input.targetId);
|
|
139280
|
+
if (target === void 0) {
|
|
139281
|
+
throw new DeliverySendError(
|
|
139282
|
+
"unknown-target",
|
|
139283
|
+
`Unknown target '${input.targetId}' for bot '${input.botId}'.`
|
|
139284
|
+
);
|
|
139285
|
+
}
|
|
139286
|
+
const binding = core.bindingOf(bot, target.platformChatId);
|
|
139287
|
+
try {
|
|
139288
|
+
await core.adapterFor(bot.platform)?.sendText(binding, input.text, { targetKind: target.kind });
|
|
139289
|
+
} catch (error) {
|
|
139290
|
+
throw new DeliverySendError(
|
|
139291
|
+
error instanceof ImNotConnectedError ? "bot-not-connected" : "delivery-failed",
|
|
139292
|
+
error instanceof Error ? error.message : String(error)
|
|
139293
|
+
);
|
|
139294
|
+
}
|
|
139295
|
+
return { sent: true };
|
|
139296
|
+
};
|
|
139297
|
+
};
|
|
139298
|
+
|
|
139299
|
+
// src/deliveryHttp.ts
|
|
139300
|
+
import * as NodeCrypto from "node:crypto";
|
|
139301
|
+
import * as NodeHttp from "node:http";
|
|
139302
|
+
|
|
139303
|
+
// src/boundedBody.ts
|
|
139304
|
+
var readBoundedBody = (req, limit) => new Promise((resolve) => {
|
|
139305
|
+
const chunks = [];
|
|
139306
|
+
let size = 0;
|
|
139307
|
+
let settled = false;
|
|
139308
|
+
const finish = (result) => {
|
|
139309
|
+
if (settled) return;
|
|
139310
|
+
settled = true;
|
|
139311
|
+
resolve(result);
|
|
139312
|
+
};
|
|
139313
|
+
req.on("data", (chunk) => {
|
|
139314
|
+
if (settled) return;
|
|
139315
|
+
size += chunk.length;
|
|
139316
|
+
if (size > limit) {
|
|
139317
|
+
finish({ kind: "too-large" });
|
|
139318
|
+
req.destroy();
|
|
139319
|
+
return;
|
|
139320
|
+
}
|
|
139321
|
+
chunks.push(chunk);
|
|
139322
|
+
});
|
|
139323
|
+
req.on("end", () => finish({ kind: "ok", bytes: Buffer.concat(chunks) }));
|
|
139324
|
+
req.on("error", () => finish({ kind: "read-failed" }));
|
|
139325
|
+
req.on("close", () => finish({ kind: "read-failed" }));
|
|
139326
|
+
});
|
|
139327
|
+
|
|
139328
|
+
// src/deliveryHttp.ts
|
|
139329
|
+
var LOOPBACK = "127.0.0.1";
|
|
139330
|
+
var MAX_BODY_BYTES = 1 * 1024 * 1024;
|
|
139331
|
+
var PATH = "/v1/delivery/messages";
|
|
139332
|
+
var statusFor = (code2) => {
|
|
139333
|
+
switch (code2) {
|
|
139334
|
+
case "bad-request":
|
|
139335
|
+
return 400;
|
|
139336
|
+
case "unauthorized":
|
|
139337
|
+
return 401;
|
|
139338
|
+
case "unknown-bot":
|
|
139339
|
+
case "unknown-target":
|
|
139340
|
+
return 404;
|
|
139341
|
+
case "bot-not-connected":
|
|
139342
|
+
return 503;
|
|
139343
|
+
case "delivery-failed":
|
|
139344
|
+
return 502;
|
|
139345
|
+
case "method-not-allowed":
|
|
139346
|
+
return 405;
|
|
139347
|
+
case "unsupported-media-type":
|
|
139348
|
+
return 415;
|
|
139349
|
+
case "payload-too-large":
|
|
139350
|
+
return 413;
|
|
139351
|
+
}
|
|
139352
|
+
};
|
|
139353
|
+
var writeJson = (res, status, body) => {
|
|
139354
|
+
const payload = JSON.stringify(body);
|
|
139355
|
+
res.writeHead(status, {
|
|
139356
|
+
"content-type": "application/json; charset=utf-8",
|
|
139357
|
+
"content-length": Buffer.byteLength(payload),
|
|
139358
|
+
"cache-control": "no-store"
|
|
139359
|
+
});
|
|
139360
|
+
res.end(payload);
|
|
139361
|
+
};
|
|
139362
|
+
var writeError = (res, code2, message) => {
|
|
139363
|
+
writeJson(res, statusFor(code2), { error: { code: code2, message } });
|
|
139364
|
+
};
|
|
139365
|
+
var isLoopbackRemote = (remote) => {
|
|
139366
|
+
if (remote === void 0 || remote === "") return false;
|
|
139367
|
+
return remote === "127.0.0.1" || remote === "::1" || remote === "::ffff:127.0.0.1";
|
|
139368
|
+
};
|
|
139369
|
+
var timingSafeEqualString = (a, b) => {
|
|
139370
|
+
const left = Buffer.from(a);
|
|
139371
|
+
const right = Buffer.from(b);
|
|
139372
|
+
if (left.length !== right.length) return false;
|
|
139373
|
+
return NodeCrypto.timingSafeEqual(left, right);
|
|
139374
|
+
};
|
|
139375
|
+
var parseSendBody = (raw) => {
|
|
139376
|
+
let parsed;
|
|
139377
|
+
try {
|
|
139378
|
+
parsed = JSON.parse(raw.toString("utf8"));
|
|
139379
|
+
} catch {
|
|
139380
|
+
return "bad-request";
|
|
139381
|
+
}
|
|
139382
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return "bad-request";
|
|
139383
|
+
const record2 = parsed;
|
|
139384
|
+
const botId = record2["botId"];
|
|
139385
|
+
const targetId = record2["targetId"];
|
|
139386
|
+
const text3 = record2["text"];
|
|
139387
|
+
const format2 = record2["format"];
|
|
139388
|
+
if (typeof botId !== "string" || botId.trim() === "") return "bad-request";
|
|
139389
|
+
if (typeof targetId !== "string" || targetId.trim() === "") return "bad-request";
|
|
139390
|
+
if (typeof text3 !== "string" || text3.trim() === "") return "bad-request";
|
|
139391
|
+
if (format2 !== void 0 && format2 !== "plain" && format2 !== "markdown") return "bad-request";
|
|
139392
|
+
for (const key of Object.keys(record2)) {
|
|
139393
|
+
if (key !== "botId" && key !== "targetId" && key !== "text" && key !== "format") {
|
|
139394
|
+
return "bad-request";
|
|
139395
|
+
}
|
|
139396
|
+
}
|
|
139397
|
+
return {
|
|
139398
|
+
botId: botId.trim(),
|
|
139399
|
+
targetId: targetId.trim(),
|
|
139400
|
+
text: text3,
|
|
139401
|
+
format: format2 === "markdown" ? "markdown" : format2 === "plain" ? "plain" : void 0
|
|
139402
|
+
};
|
|
139403
|
+
};
|
|
139404
|
+
var startDeliveryHttpServer = async (options) => {
|
|
139405
|
+
const server = NodeHttp.createServer((req, res) => {
|
|
139406
|
+
void (async () => {
|
|
139407
|
+
if (!isLoopbackRemote(req.socket.remoteAddress)) {
|
|
139408
|
+
writeError(res, "unauthorized", "Only loopback clients are accepted.");
|
|
139409
|
+
return;
|
|
139410
|
+
}
|
|
139411
|
+
const url = new URL(req.url ?? "/", `http://${LOOPBACK}`);
|
|
139412
|
+
if (url.pathname !== PATH) {
|
|
139413
|
+
writeError(res, "bad-request", `Unknown path '${url.pathname}'.`);
|
|
139414
|
+
return;
|
|
139415
|
+
}
|
|
139416
|
+
if (req.method !== "POST") {
|
|
139417
|
+
res.setHeader("allow", "POST");
|
|
139418
|
+
writeError(res, "method-not-allowed", "Only POST is accepted.");
|
|
139419
|
+
return;
|
|
139420
|
+
}
|
|
139421
|
+
const contentType = (req.headers["content-type"] ?? "").split(";")[0]?.trim().toLowerCase();
|
|
139422
|
+
if (contentType !== "application/json") {
|
|
139423
|
+
writeError(res, "unsupported-media-type", "Content-Type must be application/json.");
|
|
139424
|
+
return;
|
|
139425
|
+
}
|
|
139426
|
+
if (options.token !== "") {
|
|
139427
|
+
const header = req.headers.authorization ?? "";
|
|
139428
|
+
const prefix = "Bearer ";
|
|
139429
|
+
if (!header.startsWith(prefix) || !timingSafeEqualString(header.slice(prefix.length), options.token)) {
|
|
139430
|
+
writeError(res, "unauthorized", "Bearer token required.");
|
|
139431
|
+
return;
|
|
139432
|
+
}
|
|
139433
|
+
}
|
|
139434
|
+
const read = await readBoundedBody(req, MAX_BODY_BYTES);
|
|
139435
|
+
if (read.kind !== "ok") {
|
|
139436
|
+
if (read.kind === "too-large") {
|
|
139437
|
+
writeError(res, "payload-too-large", "JSON body exceeds 1 MiB.");
|
|
139438
|
+
} else {
|
|
139439
|
+
writeError(res, "bad-request", "Failed to read body.");
|
|
139440
|
+
}
|
|
139441
|
+
return;
|
|
139442
|
+
}
|
|
139443
|
+
const parsed = parseSendBody(read.bytes);
|
|
139444
|
+
if (typeof parsed === "string") {
|
|
139445
|
+
writeError(res, parsed, "Invalid JSON body.");
|
|
139446
|
+
return;
|
|
139447
|
+
}
|
|
139448
|
+
try {
|
|
139449
|
+
const result = await options.send(parsed);
|
|
139450
|
+
writeJson(res, 200, { sent: result.sent });
|
|
139451
|
+
} catch (error) {
|
|
139452
|
+
const rawCode = error instanceof Error ? error.code : void 0;
|
|
139453
|
+
const code2 = isDeliverySendErrorCode(rawCode) ? rawCode : "delivery-failed";
|
|
139454
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
139455
|
+
options.log?.warn("delivery http send failed", { code: code2, message });
|
|
139456
|
+
writeError(res, code2, message);
|
|
139457
|
+
}
|
|
139458
|
+
})().catch((error) => {
|
|
139459
|
+
options.log?.warn("delivery http handler crashed", {
|
|
139460
|
+
error: error instanceof Error ? error.message : String(error)
|
|
139461
|
+
});
|
|
139462
|
+
if (!res.headersSent) writeError(res, "delivery-failed", "Internal error.");
|
|
139463
|
+
});
|
|
139464
|
+
});
|
|
139465
|
+
await new Promise((resolve, reject) => {
|
|
139466
|
+
server.once("error", reject);
|
|
139467
|
+
server.listen(options.port, LOOPBACK, () => resolve());
|
|
139468
|
+
});
|
|
139469
|
+
const address = server.address();
|
|
139470
|
+
const port = address.port;
|
|
139471
|
+
options.log?.info("delivery http listening", { host: LOOPBACK, port, path: PATH });
|
|
139472
|
+
return {
|
|
139473
|
+
port,
|
|
139474
|
+
origin: `http://${LOOPBACK}:${String(port)}`,
|
|
139475
|
+
close: () => new Promise((resolve, reject) => {
|
|
139476
|
+
server.close((error) => error ? reject(error) : resolve());
|
|
139477
|
+
})
|
|
139478
|
+
};
|
|
139479
|
+
};
|
|
139480
|
+
|
|
138573
139481
|
// src/sendFile.ts
|
|
138574
139482
|
var failure = (code2, text3) => ({
|
|
138575
139483
|
text: text3,
|
|
@@ -138711,28 +139619,91 @@ var makeSendFileTool = (deps) => ({
|
|
|
138711
139619
|
|
|
138712
139620
|
// src/sessions.ts
|
|
138713
139621
|
var SESSION_SCAN_LIMIT = 50;
|
|
139622
|
+
var WEB_LIST_PAGE = 100;
|
|
138714
139623
|
var makeSessions = (core) => {
|
|
138715
139624
|
const { host, t, commit, bindChat, deliver } = core;
|
|
138716
139625
|
const listedByChat = /* @__PURE__ */ new Map();
|
|
139626
|
+
const pendingAttachByChat = /* @__PURE__ */ new Map();
|
|
139627
|
+
const rowFromThread = (thread, chatKey, origin) => {
|
|
139628
|
+
if (thread.archivedAt != null) return null;
|
|
139629
|
+
return {
|
|
139630
|
+
threadId: thread.threadId,
|
|
139631
|
+
title: thread.title,
|
|
139632
|
+
updatedAt: thread.updatedAt,
|
|
139633
|
+
running: thread.session?.activeTurnId != null,
|
|
139634
|
+
current: chatOf(core.state, chatKey).threadId === thread.threadId,
|
|
139635
|
+
elsewhere: Object.entries(core.state.chats).some(
|
|
139636
|
+
([otherKey, record2]) => otherKey !== chatKey && record2.threadId === thread.threadId
|
|
139637
|
+
),
|
|
139638
|
+
origin
|
|
139639
|
+
};
|
|
139640
|
+
};
|
|
139641
|
+
const webShellsForProject = async (projectId) => {
|
|
139642
|
+
const listThreads = host.projections.listThreads;
|
|
139643
|
+
if (listThreads === void 0 || projectId === "") return [];
|
|
139644
|
+
const shells = [];
|
|
139645
|
+
let cursor = void 0;
|
|
139646
|
+
for (let page = 0; page < 5; page += 1) {
|
|
139647
|
+
const result = await listThreads({
|
|
139648
|
+
limit: WEB_LIST_PAGE,
|
|
139649
|
+
includeArchived: false,
|
|
139650
|
+
...cursor === void 0 || cursor === null ? {} : { cursor }
|
|
139651
|
+
});
|
|
139652
|
+
for (const shell of result.threads) {
|
|
139653
|
+
if (shell.projectId !== projectId) continue;
|
|
139654
|
+
if (shell.archivedAt != null) continue;
|
|
139655
|
+
shells.push({
|
|
139656
|
+
threadId: shell.threadId,
|
|
139657
|
+
title: shell.title,
|
|
139658
|
+
updatedAt: shell.updatedAt,
|
|
139659
|
+
archivedAt: shell.archivedAt,
|
|
139660
|
+
session: {
|
|
139661
|
+
activeTurnId: shell.sessionStatus === "running" ? "running" : null
|
|
139662
|
+
}
|
|
139663
|
+
});
|
|
139664
|
+
}
|
|
139665
|
+
if (result.nextCursor === null) break;
|
|
139666
|
+
cursor = result.nextCursor;
|
|
139667
|
+
}
|
|
139668
|
+
return shells;
|
|
139669
|
+
};
|
|
138717
139670
|
const sessionRowsFor = async (binding) => {
|
|
138718
139671
|
const chatKey = chatKeyOf(binding);
|
|
139672
|
+
if (binding.projectId === "") return [];
|
|
139673
|
+
const openedIds = new Set(
|
|
139674
|
+
Object.entries(core.state.opened).filter(([, opened]) => opened.projectId === binding.projectId).map(([threadId]) => threadId)
|
|
139675
|
+
);
|
|
138719
139676
|
const candidates = Object.entries(core.state.opened).filter(([, opened]) => opened.projectId === binding.projectId).toReversed().slice(0, SESSION_SCAN_LIMIT);
|
|
138720
|
-
const
|
|
139677
|
+
const ownedThreads = await Promise.all(
|
|
138721
139678
|
candidates.map(([threadId]) => host.projections.getThread(threadId))
|
|
138722
139679
|
);
|
|
138723
139680
|
const rows = [];
|
|
138724
|
-
|
|
138725
|
-
|
|
138726
|
-
|
|
138727
|
-
|
|
138728
|
-
|
|
138729
|
-
|
|
138730
|
-
|
|
138731
|
-
|
|
138732
|
-
|
|
138733
|
-
|
|
138734
|
-
|
|
138735
|
-
})
|
|
139681
|
+
const seen = /* @__PURE__ */ new Set();
|
|
139682
|
+
for (const thread of ownedThreads) {
|
|
139683
|
+
if (thread === null) continue;
|
|
139684
|
+
const row = rowFromThread(thread, chatKey, "plugin");
|
|
139685
|
+
if (row === null) continue;
|
|
139686
|
+
rows.push(row);
|
|
139687
|
+
seen.add(row.threadId);
|
|
139688
|
+
}
|
|
139689
|
+
const shells = await webShellsForProject(binding.projectId);
|
|
139690
|
+
const listed = new Set(shells.map((shell) => shell.threadId));
|
|
139691
|
+
const attached = await Promise.all(
|
|
139692
|
+
attachedOnChat(core.state, chatKey, binding.projectId).filter(({ threadId }) => !listed.has(threadId)).map(({ threadId }) => host.projections.getThread(threadId))
|
|
139693
|
+
);
|
|
139694
|
+
for (const thread of attached) {
|
|
139695
|
+
if (thread === null || seen.has(thread.threadId) || openedIds.has(thread.threadId)) continue;
|
|
139696
|
+
const row = rowFromThread(thread, chatKey, "web");
|
|
139697
|
+
if (row === null) continue;
|
|
139698
|
+
rows.push(row);
|
|
139699
|
+
seen.add(row.threadId);
|
|
139700
|
+
}
|
|
139701
|
+
for (const shell of shells) {
|
|
139702
|
+
if (seen.has(shell.threadId) || openedIds.has(shell.threadId)) continue;
|
|
139703
|
+
const row = rowFromThread(shell, chatKey, "web");
|
|
139704
|
+
if (row === null) continue;
|
|
139705
|
+
rows.push(row);
|
|
139706
|
+
seen.add(row.threadId);
|
|
138736
139707
|
}
|
|
138737
139708
|
return rows.toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
138738
139709
|
};
|
|
@@ -138741,16 +139712,59 @@ var makeSessions = (core) => {
|
|
|
138741
139712
|
listedByChat.set(chatKeyOf(binding), listedThreadIds(rows));
|
|
138742
139713
|
await deliver(binding, formatSessions(rows, Date.now(), t));
|
|
138743
139714
|
};
|
|
139715
|
+
const bindToSession = async (binding, chatKey, target) => {
|
|
139716
|
+
for (const [otherKey, record2] of Object.entries(core.state.chats)) {
|
|
139717
|
+
if (otherKey !== chatKey && record2.threadId === target.threadId) bindChat(otherKey, null);
|
|
139718
|
+
}
|
|
139719
|
+
if (target.origin === "web" && !hasAttached(core.state, chatKey, target.threadId)) {
|
|
139720
|
+
commit("thread-attached", {
|
|
139721
|
+
chatKey,
|
|
139722
|
+
threadId: target.threadId,
|
|
139723
|
+
projectId: binding.projectId,
|
|
139724
|
+
attachedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
139725
|
+
});
|
|
139726
|
+
}
|
|
139727
|
+
commit("chat-updated", { chatKey, threadId: target.threadId, target: { kind: "thread" } });
|
|
139728
|
+
pendingAttachByChat.delete(chatKey);
|
|
139729
|
+
listedByChat.delete(chatKey);
|
|
139730
|
+
await deliver(binding, t("resume.done", { title: target.title }));
|
|
139731
|
+
};
|
|
138744
139732
|
const resumeSession = async (binding, arg) => {
|
|
138745
139733
|
const chatKey = chatKeyOf(binding);
|
|
138746
|
-
const
|
|
138747
|
-
|
|
138748
|
-
if (picked === null) {
|
|
139734
|
+
const wanted = arg.trim();
|
|
139735
|
+
if (wanted === "") {
|
|
138749
139736
|
await deliver(binding, t("resume.notFound", { query: arg }));
|
|
138750
139737
|
return;
|
|
138751
139738
|
}
|
|
138752
|
-
|
|
139739
|
+
if (wanted.toLowerCase() === "cancel") {
|
|
139740
|
+
if (!pendingAttachByChat.has(chatKey)) {
|
|
139741
|
+
await deliver(binding, t("resume.nothingPending"));
|
|
139742
|
+
return;
|
|
139743
|
+
}
|
|
139744
|
+
pendingAttachByChat.delete(chatKey);
|
|
139745
|
+
await deliver(binding, t("resume.cancelled"));
|
|
139746
|
+
return;
|
|
139747
|
+
}
|
|
139748
|
+
const confirming = wanted.toLowerCase() === "confirm";
|
|
139749
|
+
const pending = pendingAttachByChat.get(chatKey);
|
|
139750
|
+
if (confirming && pending === void 0) {
|
|
139751
|
+
await deliver(binding, t("resume.nothingPending"));
|
|
139752
|
+
return;
|
|
139753
|
+
}
|
|
139754
|
+
const rows = await sessionRowsFor(binding);
|
|
139755
|
+
let targetId;
|
|
139756
|
+
if (confirming) {
|
|
139757
|
+
targetId = pending ?? null;
|
|
139758
|
+
} else {
|
|
139759
|
+
targetId = pickSession(wanted, listedByChat.get(chatKey), rows);
|
|
139760
|
+
if (targetId === null) {
|
|
139761
|
+
await deliver(binding, t("resume.notFound", { query: arg }));
|
|
139762
|
+
return;
|
|
139763
|
+
}
|
|
139764
|
+
}
|
|
139765
|
+
const target = rows.find((row) => row.threadId === targetId);
|
|
138753
139766
|
if (target === void 0) {
|
|
139767
|
+
if (confirming) pendingAttachByChat.delete(chatKey);
|
|
138754
139768
|
await deliver(binding, t("resume.gone"));
|
|
138755
139769
|
return;
|
|
138756
139770
|
}
|
|
@@ -138759,6 +139773,7 @@ var makeSessions = (core) => {
|
|
|
138759
139773
|
return;
|
|
138760
139774
|
}
|
|
138761
139775
|
if (target.current) {
|
|
139776
|
+
if (confirming) pendingAttachByChat.delete(chatKey);
|
|
138762
139777
|
await deliver(binding, t("resume.already"));
|
|
138763
139778
|
return;
|
|
138764
139779
|
}
|
|
@@ -138766,12 +139781,12 @@ var makeSessions = (core) => {
|
|
|
138766
139781
|
await deliver(binding, t("resume.runningElsewhere"));
|
|
138767
139782
|
return;
|
|
138768
139783
|
}
|
|
138769
|
-
|
|
138770
|
-
|
|
139784
|
+
if (target.origin === "web" && !confirming && pending !== target.threadId && !hasAttached(core.state, chatKey, target.threadId)) {
|
|
139785
|
+
pendingAttachByChat.set(chatKey, target.threadId);
|
|
139786
|
+
await deliver(binding, t("resume.confirmWeb", { title: target.title }));
|
|
139787
|
+
return;
|
|
138771
139788
|
}
|
|
138772
|
-
|
|
138773
|
-
listedByChat.delete(chatKey);
|
|
138774
|
-
await deliver(binding, t("resume.done", { title: target.title }));
|
|
139789
|
+
await bindToSession(binding, chatKey, target);
|
|
138775
139790
|
};
|
|
138776
139791
|
return { listedByChat, sessionRowsFor, showSessions, resumeSession };
|
|
138777
139792
|
};
|
|
@@ -139299,6 +140314,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
139299
140314
|
chatId: String(message.chat.id),
|
|
139300
140315
|
senderId: message.from === void 0 ? null : String(message.from.id),
|
|
139301
140316
|
text: "",
|
|
140317
|
+
chatKind: message.chat.type === "private" ? "private" : "group",
|
|
139302
140318
|
resources: [],
|
|
139303
140319
|
unsupportedType: unsupported
|
|
139304
140320
|
});
|
|
@@ -139309,6 +140325,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
139309
140325
|
chatId: String(message.chat.id),
|
|
139310
140326
|
senderId: message.from === void 0 ? null : String(message.from.id),
|
|
139311
140327
|
text: text3,
|
|
140328
|
+
chatKind: message.chat.type === "private" ? "private" : "group",
|
|
139312
140329
|
resources: resource === null ? [] : [
|
|
139313
140330
|
{
|
|
139314
140331
|
key: resource.fileId,
|
|
@@ -139342,6 +140359,18 @@ var makeTelegramAdapter = (deps) => {
|
|
|
139342
140359
|
const api = await apiFor(binding);
|
|
139343
140360
|
await api.sendMessage(binding.chatId, text3);
|
|
139344
140361
|
},
|
|
140362
|
+
checkConnection: async (bot) => {
|
|
140363
|
+
if (tokenOf(bot) === "") {
|
|
140364
|
+
return { stage: "credentials", ok: false, detail: "missing bot token in saved config" };
|
|
140365
|
+
}
|
|
140366
|
+
try {
|
|
140367
|
+
const me = await (await apiFor(bot)).getMe();
|
|
140368
|
+
const username = me.username !== void 0 && me.username !== "" ? `@${me.username}` : "(no username)";
|
|
140369
|
+
return { stage: "getMe", ok: true, detail: `username=${username}` };
|
|
140370
|
+
} catch (error) {
|
|
140371
|
+
return { stage: "getMe", ok: false, detail: describeError(error) };
|
|
140372
|
+
}
|
|
140373
|
+
},
|
|
139345
140374
|
sendImage: async (binding, image2) => {
|
|
139346
140375
|
const api = await apiFor(binding);
|
|
139347
140376
|
try {
|
|
@@ -139539,6 +140568,22 @@ var makeTurns = (core, deps) => {
|
|
|
139539
140568
|
const attachments = [...carried?.attachments ?? [], ...own.attachments];
|
|
139540
140569
|
const notes = [...carried?.notes ?? [], ...own.notes];
|
|
139541
140570
|
const userMessageId = crypto.randomUUID();
|
|
140571
|
+
let intentAction;
|
|
140572
|
+
if (binding.queueMode === "steer") {
|
|
140573
|
+
try {
|
|
140574
|
+
const execution = await queueFlow.readExecution(threadId);
|
|
140575
|
+
const activeTurnId = execution?.activeTurnId ?? null;
|
|
140576
|
+
if (activeTurnId !== null) {
|
|
140577
|
+
intentAction = {
|
|
140578
|
+
kind: "steer",
|
|
140579
|
+
source: "automatic",
|
|
140580
|
+
turnId: activeTurnId,
|
|
140581
|
+
commandId: crypto.randomUUID()
|
|
140582
|
+
};
|
|
140583
|
+
}
|
|
140584
|
+
} catch {
|
|
140585
|
+
}
|
|
140586
|
+
}
|
|
139542
140587
|
try {
|
|
139543
140588
|
await host.commands.dispatch({
|
|
139544
140589
|
type: "thread.turn.start",
|
|
@@ -139553,12 +140598,14 @@ var makeTurns = (core, deps) => {
|
|
|
139553
140598
|
titleSeed: titleFrom(text3 || attachments[0]?.name || "Chat"),
|
|
139554
140599
|
runtimeMode: binding.runtimeMode,
|
|
139555
140600
|
interactionMode: interactionModeOf(chatKey),
|
|
140601
|
+
...intentAction !== void 0 ? { intentAction } : {},
|
|
139556
140602
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
139557
140603
|
});
|
|
139558
140604
|
} catch (error) {
|
|
139559
140605
|
if (carried !== void 0) pendingByChat.set(chatKey, carried);
|
|
139560
140606
|
throw error;
|
|
139561
140607
|
}
|
|
140608
|
+
deps.noteInboundUserMessage?.(userMessageId);
|
|
139562
140609
|
host.log.info("chat started a turn from a message", {
|
|
139563
140610
|
bot: binding.displayName,
|
|
139564
140611
|
chatId: binding.chatId,
|
|
@@ -139597,8 +140644,303 @@ var makeTurns = (core, deps) => {
|
|
|
139597
140644
|
return { threadStillUsable, ensureThread, startTurn, receiveAttachmentsOnly };
|
|
139598
140645
|
};
|
|
139599
140646
|
|
|
140647
|
+
// src/webSync.ts
|
|
140648
|
+
var isWebSyncChat = (state, chatKey, threadId) => {
|
|
140649
|
+
const record2 = chatOf(state, chatKey);
|
|
140650
|
+
return record2.chatKind === "private" && record2.threadId === threadId;
|
|
140651
|
+
};
|
|
140652
|
+
var IM_ORIGIN_LIMIT = 256;
|
|
140653
|
+
var MIRRORED_LIMIT = 256;
|
|
140654
|
+
var makeWebSync = (core) => {
|
|
140655
|
+
const { host, t, deliver, outbound, bindingForChatKey, ready } = core;
|
|
140656
|
+
const imOriginated = makeBoundedSeen(IM_ORIGIN_LIMIT);
|
|
140657
|
+
const mirrored = makeBoundedSeen(MIRRORED_LIMIT);
|
|
140658
|
+
const noteInboundUserMessage = (messageId) => {
|
|
140659
|
+
imOriginated.claim(messageId);
|
|
140660
|
+
};
|
|
140661
|
+
const syncBindingFor = (threadId) => {
|
|
140662
|
+
const chatKey = chatOfThread(core.state, threadId);
|
|
140663
|
+
if (chatKey === void 0) return void 0;
|
|
140664
|
+
if (!isWebSyncChat(core.state, chatKey, threadId)) return void 0;
|
|
140665
|
+
return bindingForChatKey(chatKey);
|
|
140666
|
+
};
|
|
140667
|
+
host.reactor({
|
|
140668
|
+
name: "im.web-sync-user",
|
|
140669
|
+
events: ["thread.message-sent"],
|
|
140670
|
+
handle: async (event) => {
|
|
140671
|
+
const payload = event.payload;
|
|
140672
|
+
if (payload.role !== "user" || payload.streaming !== false) return;
|
|
140673
|
+
if (typeof payload.threadId !== "string" || typeof payload.messageId !== "string") return;
|
|
140674
|
+
if (imOriginated.has(payload.messageId)) return;
|
|
140675
|
+
if (!mirrored.claim(`user:${payload.messageId}`)) return;
|
|
140676
|
+
const { threadId, messageId } = payload;
|
|
140677
|
+
await ready;
|
|
140678
|
+
const binding = syncBindingFor(threadId);
|
|
140679
|
+
if (binding === void 0) return;
|
|
140680
|
+
outbound(chatKeyOf(binding), async () => {
|
|
140681
|
+
try {
|
|
140682
|
+
const projected = await host.projections.getThreadMessage(messageId);
|
|
140683
|
+
const raw = projected?.text?.trim() || (typeof payload.text === "string" ? payload.text.trim() : "");
|
|
140684
|
+
if (raw === "") return;
|
|
140685
|
+
await deliver(binding, t("sync.webUser", { text: raw }));
|
|
140686
|
+
} catch (error) {
|
|
140687
|
+
host.log.warn("web\u2194im sync failed on user message", {
|
|
140688
|
+
messageId,
|
|
140689
|
+
error: describeError(error)
|
|
140690
|
+
});
|
|
140691
|
+
}
|
|
140692
|
+
});
|
|
140693
|
+
}
|
|
140694
|
+
});
|
|
140695
|
+
return { noteInboundUserMessage };
|
|
140696
|
+
};
|
|
140697
|
+
|
|
140698
|
+
// src/diagnostics.ts
|
|
140699
|
+
var PREFIX = {
|
|
140700
|
+
conn: "IM-CONN",
|
|
140701
|
+
msg: "IM-MSG",
|
|
140702
|
+
setup: "IM-SETUP"
|
|
140703
|
+
};
|
|
140704
|
+
var shortSalt = () => {
|
|
140705
|
+
const bytes = new Uint8Array(2);
|
|
140706
|
+
crypto.getRandomValues(bytes);
|
|
140707
|
+
return Array.from(bytes, (b) => b.toString(16).toUpperCase().padStart(2, "0")).join("");
|
|
140708
|
+
};
|
|
140709
|
+
var diagnosticRef = (kind, nowMs = Date.now()) => {
|
|
140710
|
+
const stamp = nowMs.toString(36).toUpperCase();
|
|
140711
|
+
return `${PREFIX[kind]}-${stamp}-${shortSalt()}`;
|
|
140712
|
+
};
|
|
140713
|
+
var formatDiagnosticCopy = (report) => [
|
|
140714
|
+
`ref=${report.ref}`,
|
|
140715
|
+
`ok=${report.ok ? "yes" : "no"}`,
|
|
140716
|
+
`platform=${report.platform}`,
|
|
140717
|
+
`bot=${report.botName}`,
|
|
140718
|
+
`row=${report.rowId}`,
|
|
140719
|
+
`stage=${report.stage}`,
|
|
140720
|
+
`at=${report.at}`,
|
|
140721
|
+
`detail=${report.detail}`
|
|
140722
|
+
].join("\n");
|
|
140723
|
+
|
|
140724
|
+
// src/settingsRowActions.ts
|
|
140725
|
+
var ROW_PAYLOAD_SCHEMA = {
|
|
140726
|
+
type: "object",
|
|
140727
|
+
required: ["rowId", "values"],
|
|
140728
|
+
properties: {
|
|
140729
|
+
rowId: { type: "string" },
|
|
140730
|
+
values: { type: "object" }
|
|
140731
|
+
},
|
|
140732
|
+
additionalProperties: false
|
|
140733
|
+
};
|
|
140734
|
+
var ROW_ACTION_ITEM_SCHEMA = makeFlowItemSchema({
|
|
140735
|
+
stepId: { type: "string" },
|
|
140736
|
+
fills: { type: "object" },
|
|
140737
|
+
qrRequired: ["payload"],
|
|
140738
|
+
doneRequired: ["messageKey", "fills"]
|
|
140739
|
+
});
|
|
140740
|
+
var botOf = (config2, adapters, rowId) => {
|
|
140741
|
+
const adapterFor = (platform) => adapters.find((adapter) => adapter.platform === platform);
|
|
140742
|
+
return readBots(config2, adapterFor).find((bot) => bot.rowId === rowId);
|
|
140743
|
+
};
|
|
140744
|
+
var reportOf = (bot, stage, ok, detail) => ({
|
|
140745
|
+
ref: diagnosticRef("conn"),
|
|
140746
|
+
platform: bot.platform,
|
|
140747
|
+
botName: bot.displayName,
|
|
140748
|
+
rowId: bot.rowId,
|
|
140749
|
+
stage,
|
|
140750
|
+
ok,
|
|
140751
|
+
detail,
|
|
140752
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
140753
|
+
});
|
|
140754
|
+
var probe = async (adapters, bot) => {
|
|
140755
|
+
const adapter = adapters.find((candidate) => candidate.platform === bot.platform);
|
|
140756
|
+
if (adapter?.checkConnection === void 0) {
|
|
140757
|
+
return reportOf(bot, "platform", false, `unsupported platform ${bot.platform}`);
|
|
140758
|
+
}
|
|
140759
|
+
const redactor = makeRedactor();
|
|
140760
|
+
for (const column of adapter.columns) {
|
|
140761
|
+
if (column.kind === "password") redactor.register(bot.values[column.key]);
|
|
140762
|
+
}
|
|
140763
|
+
try {
|
|
140764
|
+
const result = await adapter.checkConnection(bot);
|
|
140765
|
+
return reportOf(
|
|
140766
|
+
bot,
|
|
140767
|
+
result.stage,
|
|
140768
|
+
result.ok,
|
|
140769
|
+
result.ok ? result.detail : redactor.redact(result.detail)
|
|
140770
|
+
);
|
|
140771
|
+
} catch (error) {
|
|
140772
|
+
return reportOf(bot, "check", false, redactor.redact(describeError(error)));
|
|
140773
|
+
}
|
|
140774
|
+
};
|
|
140775
|
+
var emitReport = (emit, report, action) => {
|
|
140776
|
+
const detail = action.copyable ? formatDiagnosticCopy(report) : report.detail;
|
|
140777
|
+
if (report.ok) {
|
|
140778
|
+
emit({
|
|
140779
|
+
kind: "done",
|
|
140780
|
+
fills: {},
|
|
140781
|
+
messageKey: `${action.keyPrefix}.done`,
|
|
140782
|
+
warnings: [{ key: "rowAction.diagnostic.ref", params: { ref: report.ref, detail } }]
|
|
140783
|
+
});
|
|
140784
|
+
return;
|
|
140785
|
+
}
|
|
140786
|
+
emit({
|
|
140787
|
+
kind: "failed",
|
|
140788
|
+
reasonKey: `${action.keyPrefix}.failed`,
|
|
140789
|
+
params: { ref: report.ref },
|
|
140790
|
+
detail
|
|
140791
|
+
});
|
|
140792
|
+
};
|
|
140793
|
+
var registerSettingsRowActions = (ctx, adapters) => {
|
|
140794
|
+
const resolveBot = (rowId) => botOf(ctx.config, adapters, rowId);
|
|
140795
|
+
const registerProbeAction = (action) => {
|
|
140796
|
+
ctx.stream({
|
|
140797
|
+
name: action.name,
|
|
140798
|
+
description: action.summary,
|
|
140799
|
+
scope: "operate",
|
|
140800
|
+
payloadSchema: ROW_PAYLOAD_SCHEMA,
|
|
140801
|
+
itemSchema: ROW_ACTION_ITEM_SCHEMA,
|
|
140802
|
+
buffer: { latest: 8 },
|
|
140803
|
+
handle: async (payload, emit) => {
|
|
140804
|
+
const rowId = typeof payload["rowId"] === "string" ? payload["rowId"] : "";
|
|
140805
|
+
emit({ kind: "status", statusKey: `${action.keyPrefix}.running`, params: { rowId } });
|
|
140806
|
+
const bot = resolveBot(rowId);
|
|
140807
|
+
if (bot === void 0) {
|
|
140808
|
+
emit({ kind: "failed", reasonKey: `${action.keyPrefix}.notSaved`, params: { rowId } });
|
|
140809
|
+
return;
|
|
140810
|
+
}
|
|
140811
|
+
emitReport(emit, await probe(adapters, bot), action);
|
|
140812
|
+
}
|
|
140813
|
+
});
|
|
140814
|
+
};
|
|
140815
|
+
registerProbeAction({
|
|
140816
|
+
name: ROW_ACTION_COMMANDS.checkConnection,
|
|
140817
|
+
summary: "Probe an IM bot's platform credentials without calling a model.",
|
|
140818
|
+
keyPrefix: "rowAction.checkConnection",
|
|
140819
|
+
copyable: false
|
|
140820
|
+
});
|
|
140821
|
+
registerProbeAction({
|
|
140822
|
+
name: ROW_ACTION_COMMANDS.copyDiagnostics,
|
|
140823
|
+
summary: "Probe an IM bot and return a copy-paste diagnostic report.",
|
|
140824
|
+
keyPrefix: "rowAction.copyDiagnostics",
|
|
140825
|
+
copyable: true
|
|
140826
|
+
});
|
|
140827
|
+
ctx.stream({
|
|
140828
|
+
name: ROW_ACTION_COMMANDS.larkRepair,
|
|
140829
|
+
description: "Guide Lark scope repair and verify the saved app can still mint a token.",
|
|
140830
|
+
scope: "operate",
|
|
140831
|
+
payloadSchema: ROW_PAYLOAD_SCHEMA,
|
|
140832
|
+
itemSchema: ROW_ACTION_ITEM_SCHEMA,
|
|
140833
|
+
buffer: { latest: 8 },
|
|
140834
|
+
handle: async (payload, emit) => {
|
|
140835
|
+
const rowId = typeof payload["rowId"] === "string" ? payload["rowId"] : "";
|
|
140836
|
+
const send = (item) => emit(item);
|
|
140837
|
+
const ref = diagnosticRef("setup");
|
|
140838
|
+
send({
|
|
140839
|
+
kind: "steps",
|
|
140840
|
+
steps: [
|
|
140841
|
+
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "pending" },
|
|
140842
|
+
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "pending" },
|
|
140843
|
+
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "pending" },
|
|
140844
|
+
{ id: "verify", labelKey: "rowAction.larkRepair.step.verify", state: "running" }
|
|
140845
|
+
]
|
|
140846
|
+
});
|
|
140847
|
+
const bot = resolveBot(rowId);
|
|
140848
|
+
if (bot === void 0 || bot.platform !== "lark") {
|
|
140849
|
+
send({
|
|
140850
|
+
kind: "failed",
|
|
140851
|
+
reasonKey: "rowAction.larkRepair.notLark",
|
|
140852
|
+
params: { rowId, ref }
|
|
140853
|
+
});
|
|
140854
|
+
return;
|
|
140855
|
+
}
|
|
140856
|
+
const report = await probe(adapters, bot);
|
|
140857
|
+
send({
|
|
140858
|
+
kind: "steps",
|
|
140859
|
+
steps: [
|
|
140860
|
+
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "done" },
|
|
140861
|
+
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "done" },
|
|
140862
|
+
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "done" },
|
|
140863
|
+
{
|
|
140864
|
+
id: "verify",
|
|
140865
|
+
labelKey: "rowAction.larkRepair.step.verify",
|
|
140866
|
+
state: report.ok ? "done" : "failed"
|
|
140867
|
+
}
|
|
140868
|
+
]
|
|
140869
|
+
});
|
|
140870
|
+
if (report.ok) {
|
|
140871
|
+
send({
|
|
140872
|
+
kind: "done",
|
|
140873
|
+
fills: {},
|
|
140874
|
+
messageKey: "rowAction.larkRepair.done",
|
|
140875
|
+
warnings: [
|
|
140876
|
+
{
|
|
140877
|
+
key: "rowAction.larkRepair.hint",
|
|
140878
|
+
params: { ref, detail: formatDiagnosticCopy({ ...report, ref }) }
|
|
140879
|
+
}
|
|
140880
|
+
]
|
|
140881
|
+
});
|
|
140882
|
+
return;
|
|
140883
|
+
}
|
|
140884
|
+
send({
|
|
140885
|
+
kind: "failed",
|
|
140886
|
+
reasonKey: "rowAction.larkRepair.failed",
|
|
140887
|
+
params: { ref },
|
|
140888
|
+
detail: formatDiagnosticCopy({ ...report, ref })
|
|
140889
|
+
});
|
|
140890
|
+
}
|
|
140891
|
+
});
|
|
140892
|
+
};
|
|
140893
|
+
|
|
140894
|
+
// src/wechatApi.ts
|
|
140895
|
+
var WECHAT_TEXT_MAX_CHARS = 1800;
|
|
140896
|
+
|
|
140897
|
+
// src/wechatAdapter.ts
|
|
140898
|
+
var COLUMNS3 = [
|
|
140899
|
+
{
|
|
140900
|
+
key: "wechatBotToken",
|
|
140901
|
+
kind: "password",
|
|
140902
|
+
labelKey: "column.wechatBotToken.label",
|
|
140903
|
+
descriptionKey: "column.wechatBotToken.description"
|
|
140904
|
+
}
|
|
140905
|
+
];
|
|
140906
|
+
var tokenOf2 = (bot) => bot.values["wechatBotToken"] ?? "";
|
|
140907
|
+
var makeWechatAdapter = (deps) => {
|
|
140908
|
+
return {
|
|
140909
|
+
platform: "wechat",
|
|
140910
|
+
icon: "wechat",
|
|
140911
|
+
// 平台名与设置表的平台选项同一个键,免得默认机器人名与选项写成两种叫法。
|
|
140912
|
+
label: deps.translate("platform.wechat"),
|
|
140913
|
+
columns: COLUMNS3,
|
|
140914
|
+
isConfigured: (bot) => tokenOf2(bot) !== "",
|
|
140915
|
+
connect: (bot, _handlers) => {
|
|
140916
|
+
deps.log.warn("wechat iLink connect is stubbed pending security-review gate (YCO-236)", {
|
|
140917
|
+
bot: bot.displayName,
|
|
140918
|
+
hasToken: tokenOf2(bot) !== "",
|
|
140919
|
+
textLimit: WECHAT_TEXT_MAX_CHARS
|
|
140920
|
+
});
|
|
140921
|
+
return { close: () => void 0 };
|
|
140922
|
+
},
|
|
140923
|
+
sendText: async (binding, _text) => {
|
|
140924
|
+
deps.log.warn("wechat iLink sendText is stubbed pending security-review gate (YCO-236)", {
|
|
140925
|
+
bot: binding.displayName,
|
|
140926
|
+
chatId: binding.chatId
|
|
140927
|
+
});
|
|
140928
|
+
throw new ImNotConnectedError("wechat iLink is not connected yet (stubbed).");
|
|
140929
|
+
},
|
|
140930
|
+
askApproval: async (binding, request) => {
|
|
140931
|
+
deps.log.warn("wechat iLink askApproval is stubbed; approval text not sent", {
|
|
140932
|
+
bot: binding.displayName,
|
|
140933
|
+
chatId: binding.chatId,
|
|
140934
|
+
chars: numberedApprovalText(request).length
|
|
140935
|
+
});
|
|
140936
|
+
return null;
|
|
140937
|
+
},
|
|
140938
|
+
answerByText: (_binding, text3, options) => answerByNumber(text3, options)
|
|
140939
|
+
};
|
|
140940
|
+
};
|
|
140941
|
+
|
|
139600
140942
|
// src/wecomUpload.ts
|
|
139601
|
-
import * as
|
|
140943
|
+
import * as NodeCrypto2 from "node:crypto";
|
|
139602
140944
|
var WECOM_CHUNK_BYTES = 512 * 1024;
|
|
139603
140945
|
var WECOM_MAX_CHUNKS = 100;
|
|
139604
140946
|
var WECOM_MIN_UPLOAD_BYTES = 5;
|
|
@@ -139631,7 +140973,7 @@ var uploadWecomMedia = async (input) => {
|
|
|
139631
140973
|
filename: clampName(input.filename),
|
|
139632
140974
|
total_size: bytes.byteLength,
|
|
139633
140975
|
total_chunks: totalChunks,
|
|
139634
|
-
md5:
|
|
140976
|
+
md5: NodeCrypto2.createHash("md5").update(bytes).digest("hex")
|
|
139635
140977
|
});
|
|
139636
140978
|
const uploadId = init.upload_id;
|
|
139637
140979
|
if (typeof uploadId !== "string" || uploadId === "") {
|
|
@@ -140003,8 +141345,22 @@ var runWecomClient = (input) => {
|
|
|
140003
141345
|
};
|
|
140004
141346
|
|
|
140005
141347
|
// src/wecomMedia.ts
|
|
140006
|
-
import * as
|
|
141348
|
+
import * as NodeCrypto3 from "node:crypto";
|
|
141349
|
+
|
|
141350
|
+
// src/wecomPadding.ts
|
|
140007
141351
|
var PADDING_BLOCK = 32;
|
|
141352
|
+
var stripPadding = (plain) => {
|
|
141353
|
+
const pad = plain[plain.byteLength - 1] ?? 0;
|
|
141354
|
+
if (pad < 1 || pad > PADDING_BLOCK || pad > plain.byteLength) {
|
|
141355
|
+
return { reason: "the padding length is out of range" };
|
|
141356
|
+
}
|
|
141357
|
+
for (let index = plain.byteLength - pad; index < plain.byteLength; index += 1) {
|
|
141358
|
+
if (plain[index] !== pad) return { reason: "the padding bytes are not all equal" };
|
|
141359
|
+
}
|
|
141360
|
+
return { bytes: plain.subarray(0, plain.byteLength - pad) };
|
|
141361
|
+
};
|
|
141362
|
+
|
|
141363
|
+
// src/wecomMedia.ts
|
|
140008
141364
|
var KEY_BYTES = 32;
|
|
140009
141365
|
var IV_BYTES = 16;
|
|
140010
141366
|
var AES_BLOCK = 16;
|
|
@@ -140020,17 +141376,12 @@ var decryptWecomMedia = (cipher, aesKey) => {
|
|
|
140020
141376
|
if (cipher.byteLength === 0 || cipher.byteLength % AES_BLOCK !== 0) {
|
|
140021
141377
|
throw new WecomDecryptError("the ciphertext is not a whole number of blocks");
|
|
140022
141378
|
}
|
|
140023
|
-
const decipher =
|
|
141379
|
+
const decipher = NodeCrypto3.createDecipheriv("aes-256-cbc", key, key.subarray(0, IV_BYTES));
|
|
140024
141380
|
decipher.setAutoPadding(false);
|
|
140025
141381
|
const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
|
|
140026
|
-
const
|
|
140027
|
-
if (
|
|
140028
|
-
|
|
140029
|
-
}
|
|
140030
|
-
for (let index = plain.byteLength - pad; index < plain.byteLength; index += 1) {
|
|
140031
|
-
if (plain[index] !== pad) throw new WecomDecryptError("the padding bytes are not all equal");
|
|
140032
|
-
}
|
|
140033
|
-
return new Uint8Array(plain.subarray(0, plain.byteLength - pad));
|
|
141382
|
+
const unpadded = stripPadding(plain);
|
|
141383
|
+
if ("reason" in unpadded) throw new WecomDecryptError(unpadded.reason);
|
|
141384
|
+
return new Uint8Array(unpadded.bytes);
|
|
140034
141385
|
};
|
|
140035
141386
|
var decodeQuietly = (value) => {
|
|
140036
141387
|
try {
|
|
@@ -140084,7 +141435,7 @@ var WECOM_CARD_THROTTLE_MS = 400;
|
|
|
140084
141435
|
var LIVE_STREAM_LIMIT = 200;
|
|
140085
141436
|
var BACKOFF_START_MS2 = 1e3;
|
|
140086
141437
|
var BACKOFF_MAX_MS2 = 6e4;
|
|
140087
|
-
var
|
|
141438
|
+
var COLUMNS4 = [
|
|
140088
141439
|
{ key: "wecomBotId", kind: "text", labelKey: "column.wecomBotId.label" },
|
|
140089
141440
|
{ key: "wecomBotSecret", kind: "password", labelKey: "column.wecomBotSecret.label" }
|
|
140090
141441
|
];
|
|
@@ -140094,12 +141445,6 @@ var credentials2 = (binding) => ({
|
|
|
140094
141445
|
botSecret: binding.values["wecomBotSecret"] ?? "",
|
|
140095
141446
|
endpoint: normalizeWecomEndpoint(binding.values["wecomEndpoint"] || DEFAULT_WECOM_ENDPOINT)
|
|
140096
141447
|
});
|
|
140097
|
-
var optionIndexFrom = (text3, count) => {
|
|
140098
|
-
const trimmed = text3.trim();
|
|
140099
|
-
if (!/^\d+$/.test(trimmed)) return null;
|
|
140100
|
-
const index = Number.parseInt(trimmed, 10);
|
|
140101
|
-
return Number.isInteger(index) && index >= 1 && index <= count ? index : null;
|
|
140102
|
-
};
|
|
140103
141448
|
var makeWecomAdapter = (deps) => {
|
|
140104
141449
|
const clients = /* @__PURE__ */ new Map();
|
|
140105
141450
|
const t = deps.translate;
|
|
@@ -140142,7 +141487,7 @@ ${kept}`;
|
|
|
140142
141487
|
platform: "wecom",
|
|
140143
141488
|
icon: "wecom",
|
|
140144
141489
|
label: "WeCom",
|
|
140145
|
-
columns:
|
|
141490
|
+
columns: COLUMNS4,
|
|
140146
141491
|
isConfigured: (binding) => {
|
|
140147
141492
|
const { botId, botSecret } = credentials2(binding);
|
|
140148
141493
|
return botId !== "" && botSecret !== "";
|
|
@@ -140177,6 +141522,8 @@ ${kept}`;
|
|
|
140177
141522
|
chatId: message.chatId,
|
|
140178
141523
|
senderId: message.senderUserId,
|
|
140179
141524
|
text: message.text,
|
|
141525
|
+
// 群里 @ 到机器人的消息被折算成 single 才放行;真实的聊天类型只留在 conversation。
|
|
141526
|
+
chatKind: message.conversation === "single" ? "private" : "group",
|
|
140180
141527
|
// 下载地址与密钥打成一个键:核心只管把它原样交回来。
|
|
140181
141528
|
resources: message.resources.map((resource) => ({
|
|
140182
141529
|
key: JSON.stringify({ url: resource.url, aesKey: resource.aesKey ?? null }),
|
|
@@ -140226,20 +141573,29 @@ ${kept}`;
|
|
|
140226
141573
|
sendText: async (binding, text3) => {
|
|
140227
141574
|
const client = clients.get(binding.rowId);
|
|
140228
141575
|
if (client === void 0) {
|
|
140229
|
-
|
|
140230
|
-
return;
|
|
141576
|
+
throw new ImNotConnectedError(`wecom bot '${binding.displayName}' has no connection.`);
|
|
140231
141577
|
}
|
|
140232
141578
|
await client.sendText(binding.chatId, text3);
|
|
140233
141579
|
},
|
|
141580
|
+
checkConnection: async (bot) => {
|
|
141581
|
+
const { botId, botSecret, endpoint } = credentials2(bot);
|
|
141582
|
+
if (botId === "" || botSecret === "") {
|
|
141583
|
+
return {
|
|
141584
|
+
stage: "credentials",
|
|
141585
|
+
ok: false,
|
|
141586
|
+
detail: "missing bot id or secret in saved config"
|
|
141587
|
+
};
|
|
141588
|
+
}
|
|
141589
|
+
try {
|
|
141590
|
+
const socket = await deps.connectSocket(endpoint);
|
|
141591
|
+
await socket.close();
|
|
141592
|
+
return { stage: "websocket", ok: true, detail: `endpoint reachable (${endpoint})` };
|
|
141593
|
+
} catch (error) {
|
|
141594
|
+
return { stage: "websocket", ok: false, detail: describeError(error) };
|
|
141595
|
+
}
|
|
141596
|
+
},
|
|
140234
141597
|
askApproval: async (binding, request) => {
|
|
140235
|
-
|
|
140236
|
-
request.body,
|
|
140237
|
-
"",
|
|
140238
|
-
...request.options.map((option, index) => `${String(index + 1)}. ${option.label}`),
|
|
140239
|
-
"",
|
|
140240
|
-
request.replyHint
|
|
140241
|
-
];
|
|
140242
|
-
await (clients.get(binding.rowId)?.sendText(binding.chatId, lines.join("\n")) ?? Promise.resolve());
|
|
141598
|
+
await (clients.get(binding.rowId)?.sendText(binding.chatId, numberedApprovalText(request)) ?? Promise.resolve());
|
|
140243
141599
|
return null;
|
|
140244
141600
|
},
|
|
140245
141601
|
turnCards: {
|
|
@@ -140266,10 +141622,7 @@ ${kept}`;
|
|
|
140266
141622
|
// 图 10 MB(只认 png / jpg / jpeg / gif)、文件 20 MB,来自企微官方文档「智能机器人长连接」。
|
|
140267
141623
|
outboundLimits: { imageBytes: 10 * 1024 * 1024, fileBytes: 20 * 1024 * 1024, minBytes: 5 },
|
|
140268
141624
|
imageMimeTypes: /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif"]),
|
|
140269
|
-
answerByText: (_binding, text3, options) =>
|
|
140270
|
-
const index = optionIndexFrom(text3, options.length);
|
|
140271
|
-
return index === null ? null : options[index - 1]?.token ?? null;
|
|
140272
|
-
},
|
|
141625
|
+
answerByText: (_binding, text3, options) => answerByNumber(text3, options),
|
|
140273
141626
|
fetchResource: async (binding, input) => {
|
|
140274
141627
|
const parsed = JSON.parse(input.resource.key);
|
|
140275
141628
|
const media = await fetchWecomMedia({
|
|
@@ -140292,6 +141645,783 @@ ${kept}`;
|
|
|
140292
141645
|
};
|
|
140293
141646
|
};
|
|
140294
141647
|
|
|
141648
|
+
// src/wecomAppApi.ts
|
|
141649
|
+
var WECOM_APP_DEFAULT_API_BASE = "https://qyapi.weixin.qq.com";
|
|
141650
|
+
var WECOM_APP_TEXT_MAX_BYTES = 2048;
|
|
141651
|
+
var WecomAppApiError = class extends Error {
|
|
141652
|
+
code;
|
|
141653
|
+
providerCode;
|
|
141654
|
+
hint;
|
|
141655
|
+
constructor(code2, message, extras) {
|
|
141656
|
+
super(message);
|
|
141657
|
+
this.name = "WecomAppApiError";
|
|
141658
|
+
this.code = code2;
|
|
141659
|
+
this.providerCode = extras?.providerCode;
|
|
141660
|
+
this.hint = extras?.hint;
|
|
141661
|
+
}
|
|
141662
|
+
};
|
|
141663
|
+
var cleanString = (value) => {
|
|
141664
|
+
if (typeof value !== "string") return null;
|
|
141665
|
+
const trimmed = value.trim();
|
|
141666
|
+
return trimmed === "" ? null : trimmed;
|
|
141667
|
+
};
|
|
141668
|
+
var normalizeApiBaseUrl = (value) => {
|
|
141669
|
+
const raw = cleanString(value);
|
|
141670
|
+
if (raw === null) return WECOM_APP_DEFAULT_API_BASE;
|
|
141671
|
+
if (!/^https?:\/\//iu.test(raw)) {
|
|
141672
|
+
throw new WecomAppApiError("invalid-api-base", "API base must be an http(s) URL.");
|
|
141673
|
+
}
|
|
141674
|
+
return raw.replace(/\/+$/u, "");
|
|
141675
|
+
};
|
|
141676
|
+
var byteLength = (value) => Buffer.byteLength(value, "utf8");
|
|
141677
|
+
var splitUtf8ByBytes = (text3, maxBytes = WECOM_APP_TEXT_MAX_BYTES) => {
|
|
141678
|
+
const value = typeof text3 === "string" ? text3 : "";
|
|
141679
|
+
if (value === "") return [""];
|
|
141680
|
+
const chunks = [];
|
|
141681
|
+
let current = "";
|
|
141682
|
+
let currentBytes = 0;
|
|
141683
|
+
for (const char of value) {
|
|
141684
|
+
const size = byteLength(char);
|
|
141685
|
+
if (currentBytes + size > maxBytes && current.length > 0) {
|
|
141686
|
+
chunks.push(current);
|
|
141687
|
+
current = char;
|
|
141688
|
+
currentBytes = size;
|
|
141689
|
+
continue;
|
|
141690
|
+
}
|
|
141691
|
+
current += char;
|
|
141692
|
+
currentBytes += size;
|
|
141693
|
+
}
|
|
141694
|
+
if (current.length > 0) chunks.push(current);
|
|
141695
|
+
return chunks;
|
|
141696
|
+
};
|
|
141697
|
+
var WecomAppApi = class {
|
|
141698
|
+
corpId;
|
|
141699
|
+
corpSecret;
|
|
141700
|
+
agentId;
|
|
141701
|
+
apiBaseUrl;
|
|
141702
|
+
#fetch;
|
|
141703
|
+
#token = null;
|
|
141704
|
+
#expiresAt = 0;
|
|
141705
|
+
#pending = null;
|
|
141706
|
+
constructor(options) {
|
|
141707
|
+
this.corpId = options.corpId.trim();
|
|
141708
|
+
this.corpSecret = options.corpSecret.trim();
|
|
141709
|
+
this.agentId = options.agentId.trim();
|
|
141710
|
+
this.apiBaseUrl = normalizeApiBaseUrl(options.apiBaseUrl);
|
|
141711
|
+
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
141712
|
+
}
|
|
141713
|
+
invalidateToken() {
|
|
141714
|
+
this.#token = null;
|
|
141715
|
+
this.#expiresAt = 0;
|
|
141716
|
+
}
|
|
141717
|
+
async getAccessToken(signal) {
|
|
141718
|
+
if (this.#token !== null && Date.now() < this.#expiresAt) return this.#token;
|
|
141719
|
+
if (this.#pending !== null) return this.#pending;
|
|
141720
|
+
this.#pending = this.#fetchToken(signal).finally(() => {
|
|
141721
|
+
this.#pending = null;
|
|
141722
|
+
});
|
|
141723
|
+
return this.#pending;
|
|
141724
|
+
}
|
|
141725
|
+
async #fetchToken(signal) {
|
|
141726
|
+
const url = new URL("/cgi-bin/gettoken", this.apiBaseUrl);
|
|
141727
|
+
url.searchParams.set("corpid", this.corpId);
|
|
141728
|
+
url.searchParams.set("corpsecret", this.corpSecret);
|
|
141729
|
+
const payload = await this.#requestJson(url, {
|
|
141730
|
+
method: "GET",
|
|
141731
|
+
...signal === void 0 ? {} : { signal }
|
|
141732
|
+
});
|
|
141733
|
+
const accessToken = payload["access_token"];
|
|
141734
|
+
if (typeof accessToken !== "string" || accessToken === "") {
|
|
141735
|
+
throw new WecomAppApiError("token-missing", "WeCom did not return access_token.", {
|
|
141736
|
+
providerCode: payload["errcode"] === void 0 ? void 0 : String(payload["errcode"])
|
|
141737
|
+
});
|
|
141738
|
+
}
|
|
141739
|
+
this.#token = accessToken;
|
|
141740
|
+
const lifetime = Number(payload["expires_in"]);
|
|
141741
|
+
const ttlMs = Number.isFinite(lifetime) && lifetime > 60 ? (lifetime - 60) * 1e3 : 7e3 * 1e3;
|
|
141742
|
+
this.#expiresAt = Date.now() + ttlMs;
|
|
141743
|
+
return accessToken;
|
|
141744
|
+
}
|
|
141745
|
+
async sendText(input) {
|
|
141746
|
+
const chunks = splitUtf8ByBytes(input.content);
|
|
141747
|
+
for (const chunk of chunks) {
|
|
141748
|
+
await this.#sendOnce({
|
|
141749
|
+
touser: input.userId,
|
|
141750
|
+
msgtype: "text",
|
|
141751
|
+
agentid: Number.parseInt(this.agentId, 10),
|
|
141752
|
+
text: { content: chunk },
|
|
141753
|
+
signal: input.signal
|
|
141754
|
+
});
|
|
141755
|
+
}
|
|
141756
|
+
}
|
|
141757
|
+
async #sendOnce(body) {
|
|
141758
|
+
const token = await this.getAccessToken(body.signal);
|
|
141759
|
+
const url = new URL("/cgi-bin/message/send", this.apiBaseUrl);
|
|
141760
|
+
url.searchParams.set("access_token", token);
|
|
141761
|
+
const payload = await this.#requestJson(url, {
|
|
141762
|
+
method: "POST",
|
|
141763
|
+
headers: { "content-type": "application/json; charset=utf-8" },
|
|
141764
|
+
body: JSON.stringify({
|
|
141765
|
+
touser: body.touser,
|
|
141766
|
+
msgtype: body.msgtype,
|
|
141767
|
+
agentid: body.agentid,
|
|
141768
|
+
text: body.text
|
|
141769
|
+
}),
|
|
141770
|
+
...body.signal === void 0 ? {} : { signal: body.signal }
|
|
141771
|
+
});
|
|
141772
|
+
const errcode = Number(payload["errcode"] ?? 0);
|
|
141773
|
+
if (errcode === 0) return;
|
|
141774
|
+
if (errcode === 60020) {
|
|
141775
|
+
throw new WecomAppApiError("trusted-ip", "WeCom rejected the outbound IP (errcode 60020).", {
|
|
141776
|
+
providerCode: "60020",
|
|
141777
|
+
hint: "\u628A\u51FA\u7AD9\u516C\u7F51 IP \u52A0\u8FDB\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u7684\u300C\u4F01\u4E1A\u53EF\u4FE1 IP\u300D\u3002"
|
|
141778
|
+
});
|
|
141779
|
+
}
|
|
141780
|
+
if (errcode === 40014 || errcode === 42001) {
|
|
141781
|
+
this.invalidateToken();
|
|
141782
|
+
}
|
|
141783
|
+
throw new WecomAppApiError(
|
|
141784
|
+
"send-failed",
|
|
141785
|
+
typeof payload["errmsg"] === "string" ? payload["errmsg"] : `WeCom message/send failed (${String(errcode)}).`,
|
|
141786
|
+
{ providerCode: String(errcode) }
|
|
141787
|
+
);
|
|
141788
|
+
}
|
|
141789
|
+
async #requestJson(url, init) {
|
|
141790
|
+
const response = await this.#fetch(url, init);
|
|
141791
|
+
let payload;
|
|
141792
|
+
try {
|
|
141793
|
+
payload = await response.json();
|
|
141794
|
+
} catch {
|
|
141795
|
+
throw new WecomAppApiError(
|
|
141796
|
+
"bad-response",
|
|
141797
|
+
`WeCom returned non-JSON (HTTP ${String(response.status)}).`
|
|
141798
|
+
);
|
|
141799
|
+
}
|
|
141800
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
141801
|
+
throw new WecomAppApiError("bad-response", "WeCom returned a non-object JSON body.");
|
|
141802
|
+
}
|
|
141803
|
+
return payload;
|
|
141804
|
+
}
|
|
141805
|
+
};
|
|
141806
|
+
|
|
141807
|
+
// src/wecomAppCallback.ts
|
|
141808
|
+
import * as NodeHttp2 from "node:http";
|
|
141809
|
+
|
|
141810
|
+
// src/wecomAppCrypto.ts
|
|
141811
|
+
import * as NodeCrypto4 from "node:crypto";
|
|
141812
|
+
var AES_KEY_BYTES = 32;
|
|
141813
|
+
var RANDOM_BYTES = 16;
|
|
141814
|
+
var WecomAppCryptoError = class extends Error {
|
|
141815
|
+
code;
|
|
141816
|
+
constructor(code2, message) {
|
|
141817
|
+
super(message);
|
|
141818
|
+
this.name = "WecomAppCryptoError";
|
|
141819
|
+
this.code = code2;
|
|
141820
|
+
}
|
|
141821
|
+
};
|
|
141822
|
+
var isValidEncodingAESKey = (value) => value.length === 43 && /^[A-Za-z0-9]+$/.test(value);
|
|
141823
|
+
var parseEncodingAESKey = (encodingAESKey) => {
|
|
141824
|
+
const key = Buffer.from(`${encodingAESKey}=`, "base64");
|
|
141825
|
+
if (key.length !== AES_KEY_BYTES) {
|
|
141826
|
+
throw new WecomAppCryptoError("invalid-aes-key", "EncodingAESKey must decode to 32 bytes.");
|
|
141827
|
+
}
|
|
141828
|
+
return { key, iv: key.subarray(0, 16) };
|
|
141829
|
+
};
|
|
141830
|
+
var timingSafeEqualString2 = (a, b) => {
|
|
141831
|
+
const left = Buffer.from(a);
|
|
141832
|
+
const right = Buffer.from(b);
|
|
141833
|
+
if (left.length === 0 || left.length !== right.length) return false;
|
|
141834
|
+
return NodeCrypto4.timingSafeEqual(left, right);
|
|
141835
|
+
};
|
|
141836
|
+
var computeSignature = (token, timestamp, nonce, encrypt) => NodeCrypto4.createHash("sha1").update([token, timestamp, nonce, encrypt].sort().join("")).digest("hex");
|
|
141837
|
+
var verifySignature = (token, timestamp, nonce, encrypt, signature) => timingSafeEqualString2(computeSignature(token, timestamp, nonce, encrypt), signature);
|
|
141838
|
+
var decrypt = (encodingAESKey, encryptText, expectReceiveId) => {
|
|
141839
|
+
const { key, iv } = parseEncodingAESKey(encodingAESKey);
|
|
141840
|
+
const decipher = NodeCrypto4.createDecipheriv("aes-256-cbc", key, iv);
|
|
141841
|
+
decipher.setAutoPadding(false);
|
|
141842
|
+
let deciphered;
|
|
141843
|
+
try {
|
|
141844
|
+
const unpadded = stripPadding(
|
|
141845
|
+
Buffer.concat([decipher.update(encryptText, "base64"), decipher.final()])
|
|
141846
|
+
);
|
|
141847
|
+
if ("reason" in unpadded) {
|
|
141848
|
+
throw new WecomAppCryptoError("bad-padding", `Invalid padding: ${unpadded.reason}.`);
|
|
141849
|
+
}
|
|
141850
|
+
deciphered = unpadded.bytes;
|
|
141851
|
+
} catch (error) {
|
|
141852
|
+
if (error instanceof WecomAppCryptoError) throw error;
|
|
141853
|
+
throw new WecomAppCryptoError(
|
|
141854
|
+
"decrypt-failed",
|
|
141855
|
+
error instanceof Error ? error.message : String(error)
|
|
141856
|
+
);
|
|
141857
|
+
}
|
|
141858
|
+
if (deciphered.length < RANDOM_BYTES + 4) {
|
|
141859
|
+
throw new WecomAppCryptoError("bad-plaintext", "Deciphered buffer too short.");
|
|
141860
|
+
}
|
|
141861
|
+
const length = deciphered.readUInt32BE(RANDOM_BYTES);
|
|
141862
|
+
const msgStart = RANDOM_BYTES + 4;
|
|
141863
|
+
const msgEnd = msgStart + length;
|
|
141864
|
+
if (msgEnd > deciphered.length) {
|
|
141865
|
+
throw new WecomAppCryptoError("bad-plaintext", "Declared message length exceeds buffer.");
|
|
141866
|
+
}
|
|
141867
|
+
const message = deciphered.subarray(msgStart, msgEnd).toString("utf8");
|
|
141868
|
+
const receiveId = deciphered.subarray(msgEnd).toString("utf8");
|
|
141869
|
+
if (expectReceiveId !== void 0 && expectReceiveId !== "" && !timingSafeEqualString2(receiveId, expectReceiveId)) {
|
|
141870
|
+
throw new WecomAppCryptoError(
|
|
141871
|
+
"receive-id-mismatch",
|
|
141872
|
+
"Decrypted receiveId does not match CorpID."
|
|
141873
|
+
);
|
|
141874
|
+
}
|
|
141875
|
+
return { message, receiveId };
|
|
141876
|
+
};
|
|
141877
|
+
var createWecomAppCrypto = (input) => {
|
|
141878
|
+
const token = input.token.trim();
|
|
141879
|
+
const encodingAESKey = input.encodingAESKey.trim();
|
|
141880
|
+
const corpId = input.corpId.trim();
|
|
141881
|
+
if (!token || !encodingAESKey || !corpId) {
|
|
141882
|
+
throw new WecomAppCryptoError(
|
|
141883
|
+
"missing-credentials",
|
|
141884
|
+
"token, encodingAESKey and corpId are required."
|
|
141885
|
+
);
|
|
141886
|
+
}
|
|
141887
|
+
if (!isValidEncodingAESKey(encodingAESKey)) {
|
|
141888
|
+
throw new WecomAppCryptoError(
|
|
141889
|
+
"invalid-aes-key",
|
|
141890
|
+
"EncodingAESKey must be 43 alphanumeric characters."
|
|
141891
|
+
);
|
|
141892
|
+
}
|
|
141893
|
+
parseEncodingAESKey(encodingAESKey);
|
|
141894
|
+
return {
|
|
141895
|
+
verifySignature: (signature, timestamp, nonce, encryptText) => verifySignature(token, timestamp, nonce, encryptText, signature),
|
|
141896
|
+
decrypt: (encryptText) => decrypt(encodingAESKey, encryptText, corpId).message
|
|
141897
|
+
};
|
|
141898
|
+
};
|
|
141899
|
+
var deriveCallbackPathSecret = (token, encodingAESKey) => NodeCrypto4.createHash("sha256").update(`${token.trim()}:${encodingAESKey.trim()}`).digest("hex").slice(0, 24);
|
|
141900
|
+
|
|
141901
|
+
// src/wecomAppParse.ts
|
|
141902
|
+
var isRecord2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
141903
|
+
var isXmlFormat = (raw) => {
|
|
141904
|
+
const trimmed = raw.trim();
|
|
141905
|
+
return trimmed.startsWith("<") && trimmed.endsWith(">");
|
|
141906
|
+
};
|
|
141907
|
+
var parseWecomAppXmlBody = (xml) => {
|
|
141908
|
+
const result = {};
|
|
141909
|
+
const source = typeof xml === "string" ? xml : "";
|
|
141910
|
+
const cdataPattern = /<([\w:-]+)><!\[CDATA\[([\s\S]*?)\]\]><\/\1>/gu;
|
|
141911
|
+
let match;
|
|
141912
|
+
while ((match = cdataPattern.exec(source)) !== null) {
|
|
141913
|
+
result[match[1]] = match[2];
|
|
141914
|
+
}
|
|
141915
|
+
const simplePattern = /<([\w:-]+)>([^<>]*)<\/\1>/gu;
|
|
141916
|
+
while ((match = simplePattern.exec(source)) !== null) {
|
|
141917
|
+
if (result[match[1]] === void 0) result[match[1]] = match[2];
|
|
141918
|
+
}
|
|
141919
|
+
return result;
|
|
141920
|
+
};
|
|
141921
|
+
var parseWecomAppPlainMessage = (raw) => {
|
|
141922
|
+
const source = typeof raw === "string" ? raw.trim() : "";
|
|
141923
|
+
if (!source) return null;
|
|
141924
|
+
if (isXmlFormat(source)) {
|
|
141925
|
+
const xml = parseWecomAppXmlBody(source);
|
|
141926
|
+
return {
|
|
141927
|
+
msgid: xml["MsgId"] ?? xml["msgid"],
|
|
141928
|
+
msgtype: (xml["MsgType"] ?? xml["msgtype"] ?? "").toLowerCase(),
|
|
141929
|
+
createTime: xml["CreateTime"] ?? xml["createTime"],
|
|
141930
|
+
agentId: xml["AgentID"] ?? xml["agentid"],
|
|
141931
|
+
from: xml["FromUserName"] ? { userid: xml["FromUserName"] } : void 0,
|
|
141932
|
+
to: xml["ToUserName"],
|
|
141933
|
+
text: xml["Content"] !== void 0 ? { content: xml["Content"] } : void 0,
|
|
141934
|
+
image: xml["PicUrl"] ? { url: xml["PicUrl"] } : void 0,
|
|
141935
|
+
mediaId: xml["MediaId"] ?? xml["mediaid"],
|
|
141936
|
+
recognition: xml["Recognition"],
|
|
141937
|
+
event: (xml["Event"] ?? xml["event"] ?? "").toLowerCase() || void 0,
|
|
141938
|
+
eventKey: xml["EventKey"],
|
|
141939
|
+
chatid: xml["ChatId"]
|
|
141940
|
+
};
|
|
141941
|
+
}
|
|
141942
|
+
let data;
|
|
141943
|
+
try {
|
|
141944
|
+
data = JSON.parse(source);
|
|
141945
|
+
} catch {
|
|
141946
|
+
return null;
|
|
141947
|
+
}
|
|
141948
|
+
if (!isRecord2(data)) return null;
|
|
141949
|
+
const fromRaw = data["from"];
|
|
141950
|
+
const textRaw = data["text"];
|
|
141951
|
+
const imageRaw = data["image"];
|
|
141952
|
+
return {
|
|
141953
|
+
msgid: typeof data["msgid"] === "string" ? data["msgid"] : typeof data["MsgId"] === "string" ? data["MsgId"] : void 0,
|
|
141954
|
+
msgtype: String(data["msgtype"] ?? data["MsgType"] ?? "").toLowerCase(),
|
|
141955
|
+
createTime: data["createTime"] !== void 0 ? String(data["createTime"]) : data["CreateTime"] !== void 0 ? String(data["CreateTime"]) : void 0,
|
|
141956
|
+
agentId: data["agentid"] !== void 0 ? String(data["agentid"]) : data["AgentID"] !== void 0 ? String(data["AgentID"]) : void 0,
|
|
141957
|
+
from: isRecord2(fromRaw) ? { userid: typeof fromRaw["userid"] === "string" ? fromRaw["userid"] : void 0 } : typeof data["FromUserName"] === "string" ? { userid: data["FromUserName"] } : void 0,
|
|
141958
|
+
to: typeof data["to"] === "string" ? data["to"] : void 0,
|
|
141959
|
+
text: isRecord2(textRaw) ? { content: typeof textRaw["content"] === "string" ? textRaw["content"] : void 0 } : void 0,
|
|
141960
|
+
image: isRecord2(imageRaw) ? { url: typeof imageRaw["url"] === "string" ? imageRaw["url"] : void 0 } : void 0,
|
|
141961
|
+
mediaId: typeof data["mediaid"] === "string" ? data["mediaid"] : void 0,
|
|
141962
|
+
recognition: typeof data["recognition"] === "string" ? data["recognition"] : void 0,
|
|
141963
|
+
event: String(data["event"] ?? data["eventtype"] ?? "").toLowerCase() || void 0,
|
|
141964
|
+
eventKey: typeof data["eventkey"] === "string" ? data["eventkey"] : void 0,
|
|
141965
|
+
chatid: typeof data["chatid"] === "string" ? data["chatid"] : void 0
|
|
141966
|
+
};
|
|
141967
|
+
};
|
|
141968
|
+
|
|
141969
|
+
// src/wecomAppCallback.ts
|
|
141970
|
+
var WECOM_APP_DEFAULT_CALLBACK_HOST = "127.0.0.1";
|
|
141971
|
+
var WECOM_APP_DEFAULT_CALLBACK_PORT = 30987;
|
|
141972
|
+
var WECOM_APP_CALLBACK_PATH_PREFIX = "/wecom-app-callback";
|
|
141973
|
+
var MAX_BODY_BYTES2 = 1 * 1024 * 1024;
|
|
141974
|
+
var writeText = (res, status, text3, headers = {}) => {
|
|
141975
|
+
if (res.writableEnded || res.destroyed) return;
|
|
141976
|
+
const body = text3;
|
|
141977
|
+
res.writeHead(status, {
|
|
141978
|
+
"content-type": "text/plain; charset=utf-8",
|
|
141979
|
+
"content-length": Buffer.byteLength(body),
|
|
141980
|
+
"cache-control": "no-store",
|
|
141981
|
+
...headers
|
|
141982
|
+
});
|
|
141983
|
+
res.end(body);
|
|
141984
|
+
};
|
|
141985
|
+
var writeEmpty = (res) => {
|
|
141986
|
+
if (res.writableEnded || res.destroyed) return;
|
|
141987
|
+
res.statusCode = 200;
|
|
141988
|
+
res.end();
|
|
141989
|
+
};
|
|
141990
|
+
var callbackRoutePath = (botRowId, token, encodingAESKey) => `${WECOM_APP_CALLBACK_PATH_PREFIX}/${encodeURIComponent(botRowId)}/${deriveCallbackPathSecret(token, encodingAESKey)}`;
|
|
141991
|
+
var startWecomAppCallbackServer = async (options) => {
|
|
141992
|
+
const host = options?.host ?? WECOM_APP_DEFAULT_CALLBACK_HOST;
|
|
141993
|
+
const listenPort = options?.port ?? WECOM_APP_DEFAULT_CALLBACK_PORT;
|
|
141994
|
+
const routes = /* @__PURE__ */ new Map();
|
|
141995
|
+
const cryptoFor = (route) => createWecomAppCrypto({
|
|
141996
|
+
token: route.token,
|
|
141997
|
+
encodingAESKey: route.encodingAESKey,
|
|
141998
|
+
corpId: route.corpId
|
|
141999
|
+
});
|
|
142000
|
+
const handleGet = (route, query, res) => {
|
|
142001
|
+
const timestamp = query.get("timestamp") ?? "";
|
|
142002
|
+
const nonce = query.get("nonce") ?? "";
|
|
142003
|
+
const echostr = query.get("echostr") ?? "";
|
|
142004
|
+
const signature = query.get("msg_signature") ?? query.get("msgsignature") ?? query.get("signature") ?? "";
|
|
142005
|
+
if (!timestamp || !nonce || !echostr || !signature) {
|
|
142006
|
+
writeText(res, 400, "missing query params");
|
|
142007
|
+
return;
|
|
142008
|
+
}
|
|
142009
|
+
let crypto2;
|
|
142010
|
+
try {
|
|
142011
|
+
crypto2 = cryptoFor(route);
|
|
142012
|
+
} catch (error) {
|
|
142013
|
+
route.log?.warn("wecom-app route credentials incomplete", {
|
|
142014
|
+
bot: route.botRowId,
|
|
142015
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142016
|
+
});
|
|
142017
|
+
writeText(res, 503, "route not ready");
|
|
142018
|
+
return;
|
|
142019
|
+
}
|
|
142020
|
+
try {
|
|
142021
|
+
if (!crypto2.verifySignature(signature, timestamp, nonce, echostr)) {
|
|
142022
|
+
writeText(res, 401, "unauthorized");
|
|
142023
|
+
return;
|
|
142024
|
+
}
|
|
142025
|
+
const plaintext = crypto2.decrypt(echostr);
|
|
142026
|
+
route.log?.info("wecom-app URL verification succeeded", { bot: route.botRowId });
|
|
142027
|
+
writeText(res, 200, plaintext);
|
|
142028
|
+
} catch (error) {
|
|
142029
|
+
route.log?.warn("wecom-app URL verification failed", {
|
|
142030
|
+
bot: route.botRowId,
|
|
142031
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142032
|
+
});
|
|
142033
|
+
writeText(res, 400, "verification failed");
|
|
142034
|
+
}
|
|
142035
|
+
};
|
|
142036
|
+
const handlePost = async (route, query, req, res) => {
|
|
142037
|
+
const read = await readBoundedBody(req, MAX_BODY_BYTES2);
|
|
142038
|
+
if (read.kind === "too-large") {
|
|
142039
|
+
writeText(res, 413, "payload too large");
|
|
142040
|
+
return;
|
|
142041
|
+
}
|
|
142042
|
+
if (read.kind === "read-failed") {
|
|
142043
|
+
writeText(res, 400, "failed to read body");
|
|
142044
|
+
return;
|
|
142045
|
+
}
|
|
142046
|
+
const raw = read.bytes.toString("utf8");
|
|
142047
|
+
if (!raw.trim()) {
|
|
142048
|
+
writeText(res, 400, "empty payload");
|
|
142049
|
+
return;
|
|
142050
|
+
}
|
|
142051
|
+
let encrypt = "";
|
|
142052
|
+
let signature = "";
|
|
142053
|
+
let timestamp = "";
|
|
142054
|
+
let nonce = "";
|
|
142055
|
+
if (isXmlFormat(raw)) {
|
|
142056
|
+
const xml = parseWecomAppXmlBody(raw);
|
|
142057
|
+
encrypt = xml["Encrypt"] ?? "";
|
|
142058
|
+
signature = xml["MsgSignature"] ?? query.get("msg_signature") ?? query.get("msgsignature") ?? "";
|
|
142059
|
+
timestamp = xml["TimeStamp"] ?? query.get("timestamp") ?? "";
|
|
142060
|
+
nonce = xml["Nonce"] ?? query.get("nonce") ?? "";
|
|
142061
|
+
} else {
|
|
142062
|
+
let parsed;
|
|
142063
|
+
try {
|
|
142064
|
+
parsed = JSON.parse(raw);
|
|
142065
|
+
} catch {
|
|
142066
|
+
writeText(res, 400, "invalid payload");
|
|
142067
|
+
return;
|
|
142068
|
+
}
|
|
142069
|
+
encrypt = typeof parsed["encrypt"] === "string" ? parsed["encrypt"] : "";
|
|
142070
|
+
signature = typeof parsed["msgsignature"] === "string" ? parsed["msgsignature"] : typeof parsed["MsgSignature"] === "string" ? parsed["MsgSignature"] : query.get("msg_signature") ?? "";
|
|
142071
|
+
timestamp = String(
|
|
142072
|
+
parsed["timestamp"] ?? parsed["TimeStamp"] ?? query.get("timestamp") ?? ""
|
|
142073
|
+
);
|
|
142074
|
+
nonce = String(parsed["nonce"] ?? parsed["Nonce"] ?? query.get("nonce") ?? "");
|
|
142075
|
+
}
|
|
142076
|
+
if (!encrypt || !signature || !timestamp || !nonce) {
|
|
142077
|
+
writeText(res, 400, "missing encrypted payload");
|
|
142078
|
+
return;
|
|
142079
|
+
}
|
|
142080
|
+
let crypto2;
|
|
142081
|
+
try {
|
|
142082
|
+
crypto2 = cryptoFor(route);
|
|
142083
|
+
} catch {
|
|
142084
|
+
writeText(res, 503, "route not ready");
|
|
142085
|
+
return;
|
|
142086
|
+
}
|
|
142087
|
+
let plaintext;
|
|
142088
|
+
try {
|
|
142089
|
+
if (!crypto2.verifySignature(signature, timestamp, nonce, encrypt)) {
|
|
142090
|
+
writeText(res, 401, "unauthorized");
|
|
142091
|
+
return;
|
|
142092
|
+
}
|
|
142093
|
+
plaintext = crypto2.decrypt(encrypt);
|
|
142094
|
+
} catch (error) {
|
|
142095
|
+
route.log?.warn("wecom-app callback signature or decrypt failed", {
|
|
142096
|
+
bot: route.botRowId,
|
|
142097
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142098
|
+
});
|
|
142099
|
+
writeText(res, 401, "unauthorized");
|
|
142100
|
+
return;
|
|
142101
|
+
}
|
|
142102
|
+
const message = parseWecomAppPlainMessage(plaintext);
|
|
142103
|
+
if (message === null) {
|
|
142104
|
+
writeText(res, 400, "invalid message");
|
|
142105
|
+
return;
|
|
142106
|
+
}
|
|
142107
|
+
writeEmpty(res);
|
|
142108
|
+
try {
|
|
142109
|
+
await route.onInbound(message);
|
|
142110
|
+
} catch (error) {
|
|
142111
|
+
route.log?.warn("wecom-app inbound handler failed", {
|
|
142112
|
+
bot: route.botRowId,
|
|
142113
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142114
|
+
});
|
|
142115
|
+
}
|
|
142116
|
+
};
|
|
142117
|
+
const server = NodeHttp2.createServer((req, res) => {
|
|
142118
|
+
void (async () => {
|
|
142119
|
+
if (req.method !== "GET" && req.method !== "POST") {
|
|
142120
|
+
writeText(res, 405, "method not allowed", { allow: "GET, POST" });
|
|
142121
|
+
return;
|
|
142122
|
+
}
|
|
142123
|
+
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
142124
|
+
const route = routes.get(url.pathname);
|
|
142125
|
+
if (route === void 0) {
|
|
142126
|
+
writeText(res, 404, "not found");
|
|
142127
|
+
return;
|
|
142128
|
+
}
|
|
142129
|
+
if (req.method === "GET") {
|
|
142130
|
+
handleGet(route, url.searchParams, res);
|
|
142131
|
+
return;
|
|
142132
|
+
}
|
|
142133
|
+
await handlePost(route, url.searchParams, req, res);
|
|
142134
|
+
})().catch((error) => {
|
|
142135
|
+
if (!res.headersSent) writeText(res, 500, "internal error");
|
|
142136
|
+
void error;
|
|
142137
|
+
});
|
|
142138
|
+
});
|
|
142139
|
+
await new Promise((resolve, reject) => {
|
|
142140
|
+
server.once("error", reject);
|
|
142141
|
+
server.listen(listenPort, host, () => resolve());
|
|
142142
|
+
});
|
|
142143
|
+
const address = server.address();
|
|
142144
|
+
const port = address.port;
|
|
142145
|
+
return {
|
|
142146
|
+
host,
|
|
142147
|
+
port,
|
|
142148
|
+
origin: `http://${host}:${String(port)}`,
|
|
142149
|
+
register: (route) => {
|
|
142150
|
+
const path = callbackRoutePath(route.botRowId, route.token, route.encodingAESKey);
|
|
142151
|
+
routes.set(path, { ...route, path });
|
|
142152
|
+
route.log?.info("wecom-app callback route registered", {
|
|
142153
|
+
bot: route.botRowId,
|
|
142154
|
+
path,
|
|
142155
|
+
origin: `http://${host}:${String(port)}`
|
|
142156
|
+
});
|
|
142157
|
+
return path;
|
|
142158
|
+
},
|
|
142159
|
+
unregister: (botRowId) => {
|
|
142160
|
+
for (const [path, route] of routes.entries()) {
|
|
142161
|
+
if (route.botRowId === botRowId) routes.delete(path);
|
|
142162
|
+
}
|
|
142163
|
+
},
|
|
142164
|
+
close: () => new Promise((resolve, reject) => {
|
|
142165
|
+
routes.clear();
|
|
142166
|
+
server.close((error) => error ? reject(error) : resolve());
|
|
142167
|
+
})
|
|
142168
|
+
};
|
|
142169
|
+
};
|
|
142170
|
+
var makeWecomAppCallbackHub = () => {
|
|
142171
|
+
const open = /* @__PURE__ */ new Map();
|
|
142172
|
+
const closing = /* @__PURE__ */ new Map();
|
|
142173
|
+
const acquire = async (options) => {
|
|
142174
|
+
const host = options?.host ?? WECOM_APP_DEFAULT_CALLBACK_HOST;
|
|
142175
|
+
const port = options?.port ?? WECOM_APP_DEFAULT_CALLBACK_PORT;
|
|
142176
|
+
const key = `${host}:${String(port)}`;
|
|
142177
|
+
let entry = open.get(key);
|
|
142178
|
+
if (entry === void 0) {
|
|
142179
|
+
const created = {
|
|
142180
|
+
server: (closing.get(key) ?? Promise.resolve()).then(
|
|
142181
|
+
() => startWecomAppCallbackServer({ host, port })
|
|
142182
|
+
),
|
|
142183
|
+
users: 0
|
|
142184
|
+
};
|
|
142185
|
+
entry = created;
|
|
142186
|
+
open.set(key, created);
|
|
142187
|
+
created.server.catch(() => {
|
|
142188
|
+
if (open.get(key) === created) open.delete(key);
|
|
142189
|
+
});
|
|
142190
|
+
}
|
|
142191
|
+
const owner = entry;
|
|
142192
|
+
owner.users += 1;
|
|
142193
|
+
let server;
|
|
142194
|
+
try {
|
|
142195
|
+
server = await owner.server;
|
|
142196
|
+
} catch (error) {
|
|
142197
|
+
owner.users -= 1;
|
|
142198
|
+
throw error;
|
|
142199
|
+
}
|
|
142200
|
+
let released = false;
|
|
142201
|
+
return {
|
|
142202
|
+
server,
|
|
142203
|
+
release: async () => {
|
|
142204
|
+
if (released) return;
|
|
142205
|
+
released = true;
|
|
142206
|
+
owner.users -= 1;
|
|
142207
|
+
if (owner.users > 0) return;
|
|
142208
|
+
if (open.get(key) === owner) open.delete(key);
|
|
142209
|
+
const done = server.close().catch(() => void 0);
|
|
142210
|
+
closing.set(key, done);
|
|
142211
|
+
await done;
|
|
142212
|
+
if (closing.get(key) === done) closing.delete(key);
|
|
142213
|
+
}
|
|
142214
|
+
};
|
|
142215
|
+
};
|
|
142216
|
+
return { acquire };
|
|
142217
|
+
};
|
|
142218
|
+
|
|
142219
|
+
// src/wecomAppAdapter.ts
|
|
142220
|
+
var COLUMNS5 = [
|
|
142221
|
+
{
|
|
142222
|
+
key: "wecomAppCorpId",
|
|
142223
|
+
kind: "text",
|
|
142224
|
+
labelKey: "column.wecomAppCorpId.label",
|
|
142225
|
+
placeholderKey: "column.wecomAppCorpId.placeholder"
|
|
142226
|
+
},
|
|
142227
|
+
{
|
|
142228
|
+
key: "wecomAppAgentId",
|
|
142229
|
+
kind: "text",
|
|
142230
|
+
labelKey: "column.wecomAppAgentId.label",
|
|
142231
|
+
placeholderKey: "column.wecomAppAgentId.placeholder"
|
|
142232
|
+
},
|
|
142233
|
+
{ key: "wecomAppSecret", kind: "password", labelKey: "column.wecomAppSecret.label" },
|
|
142234
|
+
{ key: "wecomAppToken", kind: "password", labelKey: "column.wecomAppToken.label" },
|
|
142235
|
+
{
|
|
142236
|
+
key: "wecomAppEncodingAESKey",
|
|
142237
|
+
kind: "password",
|
|
142238
|
+
labelKey: "column.wecomAppEncodingAESKey.label",
|
|
142239
|
+
placeholderKey: "column.wecomAppEncodingAESKey.placeholder"
|
|
142240
|
+
},
|
|
142241
|
+
{
|
|
142242
|
+
key: "wecomAppCallbackBaseUrl",
|
|
142243
|
+
kind: "text",
|
|
142244
|
+
labelKey: "column.wecomAppCallbackBaseUrl.label",
|
|
142245
|
+
descriptionKey: "column.wecomAppCallbackBaseUrl.description",
|
|
142246
|
+
placeholderKey: "column.wecomAppCallbackBaseUrl.placeholder"
|
|
142247
|
+
},
|
|
142248
|
+
{
|
|
142249
|
+
key: "wecomAppApiBaseUrl",
|
|
142250
|
+
kind: "text",
|
|
142251
|
+
labelKey: "column.wecomAppApiBaseUrl.label",
|
|
142252
|
+
descriptionKey: "column.wecomAppApiBaseUrl.description",
|
|
142253
|
+
placeholderKey: "column.wecomAppApiBaseUrl.placeholder"
|
|
142254
|
+
},
|
|
142255
|
+
{
|
|
142256
|
+
key: "wecomAppListenPort",
|
|
142257
|
+
kind: "text",
|
|
142258
|
+
labelKey: "column.wecomAppListenPort.label",
|
|
142259
|
+
descriptionKey: "column.wecomAppListenPort.description",
|
|
142260
|
+
placeholderKey: "column.wecomAppListenPort.placeholder"
|
|
142261
|
+
}
|
|
142262
|
+
];
|
|
142263
|
+
var wecomAppCredentials = (bot) => ({
|
|
142264
|
+
corpId: bot.values["wecomAppCorpId"] ?? "",
|
|
142265
|
+
agentId: bot.values["wecomAppAgentId"] ?? "",
|
|
142266
|
+
corpSecret: bot.values["wecomAppSecret"] ?? "",
|
|
142267
|
+
token: bot.values["wecomAppToken"] ?? "",
|
|
142268
|
+
encodingAESKey: bot.values["wecomAppEncodingAESKey"] ?? "",
|
|
142269
|
+
callbackBaseUrl: (bot.values["wecomAppCallbackBaseUrl"] ?? "").trim().replace(/\/+$/u, ""),
|
|
142270
|
+
apiBaseUrl: (bot.values["wecomAppApiBaseUrl"] ?? "").trim(),
|
|
142271
|
+
listenPortRaw: (bot.values["wecomAppListenPort"] ?? "").trim()
|
|
142272
|
+
});
|
|
142273
|
+
var parseListenPort = (raw) => {
|
|
142274
|
+
if (raw === "") return WECOM_APP_DEFAULT_CALLBACK_PORT;
|
|
142275
|
+
const n = Number.parseInt(raw, 10);
|
|
142276
|
+
if (!Number.isInteger(n) || n < 0 || n > 65535) return WECOM_APP_DEFAULT_CALLBACK_PORT;
|
|
142277
|
+
return n;
|
|
142278
|
+
};
|
|
142279
|
+
var makeWecomAppAdapter = (deps) => {
|
|
142280
|
+
const apis = /* @__PURE__ */ new Map();
|
|
142281
|
+
const newApi = (c) => new WecomAppApi({
|
|
142282
|
+
corpId: c.corpId,
|
|
142283
|
+
corpSecret: c.corpSecret,
|
|
142284
|
+
agentId: c.agentId,
|
|
142285
|
+
apiBaseUrl: c.apiBaseUrl || void 0,
|
|
142286
|
+
fetch: deps.fetch
|
|
142287
|
+
});
|
|
142288
|
+
const callbackHub = makeWecomAppCallbackHub();
|
|
142289
|
+
const toInbound = (message, handlers) => {
|
|
142290
|
+
if (message.msgtype === "event") return;
|
|
142291
|
+
const senderId = message.from?.userid ?? null;
|
|
142292
|
+
const chatId = message.chatid ?? message.from?.userid ?? "";
|
|
142293
|
+
if (chatId === "") return;
|
|
142294
|
+
if (message.msgtype === "text") {
|
|
142295
|
+
handlers.onMessage({
|
|
142296
|
+
messageId: message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`,
|
|
142297
|
+
chatId,
|
|
142298
|
+
senderId,
|
|
142299
|
+
text: message.text?.content?.trim() ?? "",
|
|
142300
|
+
resources: []
|
|
142301
|
+
});
|
|
142302
|
+
return;
|
|
142303
|
+
}
|
|
142304
|
+
if (message.msgtype === "image") {
|
|
142305
|
+
handlers.onMessage({
|
|
142306
|
+
messageId: message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`,
|
|
142307
|
+
chatId,
|
|
142308
|
+
senderId,
|
|
142309
|
+
text: "",
|
|
142310
|
+
resources: message.mediaId ? [
|
|
142311
|
+
{
|
|
142312
|
+
key: message.mediaId,
|
|
142313
|
+
name: "image.jpg",
|
|
142314
|
+
mimeType: "image/jpeg",
|
|
142315
|
+
kind: "image"
|
|
142316
|
+
}
|
|
142317
|
+
] : [],
|
|
142318
|
+
...message.mediaId ? {} : { unsupportedType: "image" }
|
|
142319
|
+
});
|
|
142320
|
+
return;
|
|
142321
|
+
}
|
|
142322
|
+
handlers.onMessage({
|
|
142323
|
+
messageId: message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`,
|
|
142324
|
+
chatId,
|
|
142325
|
+
senderId,
|
|
142326
|
+
text: "",
|
|
142327
|
+
resources: [],
|
|
142328
|
+
unsupportedType: message.msgtype || "unknown"
|
|
142329
|
+
});
|
|
142330
|
+
};
|
|
142331
|
+
return {
|
|
142332
|
+
platform: "wecom-app",
|
|
142333
|
+
icon: "wecom",
|
|
142334
|
+
label: deps.translate("platform.wecom-app"),
|
|
142335
|
+
columns: COLUMNS5,
|
|
142336
|
+
isConfigured: (bot) => {
|
|
142337
|
+
const c = wecomAppCredentials(bot);
|
|
142338
|
+
return c.corpId !== "" && c.agentId !== "" && c.corpSecret !== "" && c.token !== "" && isValidEncodingAESKey(c.encodingAESKey);
|
|
142339
|
+
},
|
|
142340
|
+
/**
|
|
142341
|
+
* 自建应用是回调式渠道:入站由企微推到我们的 listener,没有可探的长连接;能探的是出站
|
|
142342
|
+
* 凭据——和真实发送同一条路,向 gettoken 换一次 access_token。
|
|
142343
|
+
*/
|
|
142344
|
+
checkConnection: async (bot) => {
|
|
142345
|
+
const c = wecomAppCredentials(bot);
|
|
142346
|
+
if (c.corpId === "" || c.agentId === "" || c.corpSecret === "") {
|
|
142347
|
+
return {
|
|
142348
|
+
stage: "credentials",
|
|
142349
|
+
ok: false,
|
|
142350
|
+
detail: "missing corp id, agent id or secret in saved config"
|
|
142351
|
+
};
|
|
142352
|
+
}
|
|
142353
|
+
try {
|
|
142354
|
+
await newApi(c).getAccessToken();
|
|
142355
|
+
return {
|
|
142356
|
+
stage: "gettoken",
|
|
142357
|
+
ok: true,
|
|
142358
|
+
detail: "access_token ok (callback reachability is not probed)"
|
|
142359
|
+
};
|
|
142360
|
+
} catch (error) {
|
|
142361
|
+
return { stage: "gettoken", ok: false, detail: describeError(error) };
|
|
142362
|
+
}
|
|
142363
|
+
},
|
|
142364
|
+
connect: async (bot, handlers) => {
|
|
142365
|
+
const c = wecomAppCredentials(bot);
|
|
142366
|
+
const port = parseListenPort(c.listenPortRaw);
|
|
142367
|
+
const lease = await callbackHub.acquire({ port });
|
|
142368
|
+
const server = lease.server;
|
|
142369
|
+
const api = newApi(c);
|
|
142370
|
+
apis.set(bot.rowId, api);
|
|
142371
|
+
let path;
|
|
142372
|
+
try {
|
|
142373
|
+
path = server.register({
|
|
142374
|
+
botRowId: bot.rowId,
|
|
142375
|
+
token: c.token,
|
|
142376
|
+
encodingAESKey: c.encodingAESKey,
|
|
142377
|
+
corpId: c.corpId,
|
|
142378
|
+
log: deps.log,
|
|
142379
|
+
onInbound: (message) => toInbound(message, handlers)
|
|
142380
|
+
});
|
|
142381
|
+
} catch (error) {
|
|
142382
|
+
await lease.release();
|
|
142383
|
+
throw error;
|
|
142384
|
+
}
|
|
142385
|
+
const fullUrl = c.callbackBaseUrl !== "" ? `${c.callbackBaseUrl}${path}` : `${server.origin}${path}`;
|
|
142386
|
+
deps.log.info("wecom-app connected", {
|
|
142387
|
+
bot: bot.displayName,
|
|
142388
|
+
callbackPath: path,
|
|
142389
|
+
callbackUrl: fullUrl,
|
|
142390
|
+
listen: `${server.host}:${String(server.port)}`
|
|
142391
|
+
});
|
|
142392
|
+
return {
|
|
142393
|
+
close: () => {
|
|
142394
|
+
server.unregister(bot.rowId);
|
|
142395
|
+
apis.delete(bot.rowId);
|
|
142396
|
+
void lease.release().catch((error) => {
|
|
142397
|
+
deps.log.warn("wecom-app callback listener did not close cleanly", {
|
|
142398
|
+
bot: bot.displayName,
|
|
142399
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142400
|
+
});
|
|
142401
|
+
});
|
|
142402
|
+
}
|
|
142403
|
+
};
|
|
142404
|
+
},
|
|
142405
|
+
sendText: async (binding, text3, options) => {
|
|
142406
|
+
if (options?.targetKind === "group") {
|
|
142407
|
+
throw new Error("wecom-app can only deliver to a user; group targets are not supported.");
|
|
142408
|
+
}
|
|
142409
|
+
const api = apis.get(binding.rowId);
|
|
142410
|
+
if (api === void 0) {
|
|
142411
|
+
throw new ImNotConnectedError(`wecom-app bot '${binding.displayName}' is not connected.`);
|
|
142412
|
+
}
|
|
142413
|
+
await api.sendText({ userId: binding.chatId, content: text3 });
|
|
142414
|
+
},
|
|
142415
|
+
askApproval: async (binding, request) => {
|
|
142416
|
+
const api = apis.get(binding.rowId);
|
|
142417
|
+
if (api === void 0) return null;
|
|
142418
|
+
await api.sendText({ userId: binding.chatId, content: numberedApprovalText(request) });
|
|
142419
|
+
return null;
|
|
142420
|
+
},
|
|
142421
|
+
answerByText: (_binding, text3, options) => answerByNumber(text3, options)
|
|
142422
|
+
};
|
|
142423
|
+
};
|
|
142424
|
+
|
|
140295
142425
|
// src/index.ts
|
|
140296
142426
|
var imPlugin = definePlugin({
|
|
140297
142427
|
id: "im",
|
|
@@ -140331,10 +142461,28 @@ var imPlugin = definePlugin({
|
|
|
140331
142461
|
info: (message, fields) => ctx.log.info(message, fields),
|
|
140332
142462
|
warn: (message, fields) => ctx.log.warn(message, fields)
|
|
140333
142463
|
}
|
|
142464
|
+
}),
|
|
142465
|
+
makeWecomAppAdapter({
|
|
142466
|
+
// 自建应用:HTTP 回调收消息 + cgi-bin 回文字(YCO-235)。
|
|
142467
|
+
fetch: ctx.http,
|
|
142468
|
+
translate: t,
|
|
142469
|
+
log: {
|
|
142470
|
+
info: (message, fields) => ctx.log.info(message, fields),
|
|
142471
|
+
warn: (message, fields) => ctx.log.warn(message, fields)
|
|
142472
|
+
}
|
|
142473
|
+
}),
|
|
142474
|
+
// 微信个微:配置面先挂上;真 iLink 登录等 0069 安全评审合入门槛通过后再实装。
|
|
142475
|
+
makeWechatAdapter({
|
|
142476
|
+
translate: t,
|
|
142477
|
+
log: {
|
|
142478
|
+
info: (message, fields) => ctx.log.info(message, fields),
|
|
142479
|
+
warn: (message, fields) => ctx.log.warn(message, fields)
|
|
142480
|
+
}
|
|
140334
142481
|
})
|
|
140335
142482
|
];
|
|
140336
142483
|
ctx.settingsCard({ ...buildSettingsCard(adapters), actions: [LARK_SETUP_CARD_ACTION] });
|
|
140337
142484
|
registerLarkSetup(ctx);
|
|
142485
|
+
registerSettingsRowActions(ctx, adapters);
|
|
140338
142486
|
const core = makeImCore({
|
|
140339
142487
|
host: {
|
|
140340
142488
|
log: ctx.log,
|
|
@@ -140360,13 +142508,15 @@ var imPlugin = definePlugin({
|
|
|
140360
142508
|
clearWorking: acknowledgements.clearWorking,
|
|
140361
142509
|
replyCards
|
|
140362
142510
|
});
|
|
142511
|
+
const webSync = makeWebSync(core);
|
|
140363
142512
|
const approvals = makeApprovals(core);
|
|
140364
142513
|
const interactions = makeInteractions(core, approvals);
|
|
140365
142514
|
const inboundAttachments = makeInboundAttachments(core);
|
|
140366
142515
|
const turns = makeTurns(core, {
|
|
140367
142516
|
attachments: inboundAttachments,
|
|
140368
142517
|
queueFlow: interactions.queueFlow,
|
|
140369
|
-
rememberThreadProject: notifications.rememberThreadProject
|
|
142518
|
+
rememberThreadProject: notifications.rememberThreadProject,
|
|
142519
|
+
noteInboundUserMessage: webSync.noteInboundUserMessage
|
|
140370
142520
|
});
|
|
140371
142521
|
const sessions = makeSessions(core);
|
|
140372
142522
|
const modelPlan = makeModelPlan(core, turns);
|
|
@@ -140419,6 +142569,34 @@ var imPlugin = definePlugin({
|
|
|
140419
142569
|
]
|
|
140420
142570
|
});
|
|
140421
142571
|
const { bots, connecting } = startConnections(core, inbound.inboundHandlersFor);
|
|
142572
|
+
if (readDeliveryHttpEnabled(ctx.config)) {
|
|
142573
|
+
const port = readDeliveryHttpPort(ctx.config);
|
|
142574
|
+
const token = readDeliveryHttpToken(ctx.config);
|
|
142575
|
+
const send = makeDeliverySender(core, ctx.config);
|
|
142576
|
+
try {
|
|
142577
|
+
const server = await startDeliveryHttpServer({
|
|
142578
|
+
port,
|
|
142579
|
+
token,
|
|
142580
|
+
send,
|
|
142581
|
+
log: {
|
|
142582
|
+
info: (message, fields) => ctx.log.info(message, fields),
|
|
142583
|
+
warn: (message, fields) => ctx.log.warn(message, fields)
|
|
142584
|
+
}
|
|
142585
|
+
});
|
|
142586
|
+
ctx.onDispose(() => {
|
|
142587
|
+
void server.close().catch((error) => {
|
|
142588
|
+
ctx.log.warn("delivery http could not close", {
|
|
142589
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142590
|
+
});
|
|
142591
|
+
});
|
|
142592
|
+
});
|
|
142593
|
+
} catch (error) {
|
|
142594
|
+
ctx.log.warn("delivery http failed to listen", {
|
|
142595
|
+
port,
|
|
142596
|
+
error: error instanceof Error ? error.message : String(error)
|
|
142597
|
+
});
|
|
142598
|
+
}
|
|
142599
|
+
}
|
|
140422
142600
|
ctx.log.info("chat plugin active", { bots, connecting });
|
|
140423
142601
|
}
|
|
140424
142602
|
});
|