opencode-feishu 1.0.2 → 1.2.0
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/dist/index.js +552 -422
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11587,7 +11587,7 @@ var require_object_inspect = __commonJS({
|
|
|
11587
11587
|
"double": /(["\\])/g,
|
|
11588
11588
|
single: /(['\\])/g
|
|
11589
11589
|
};
|
|
11590
|
-
module2.exports = function inspect_(obj, options, depth,
|
|
11590
|
+
module2.exports = function inspect_(obj, options, depth, seen) {
|
|
11591
11591
|
var opts = options || {};
|
|
11592
11592
|
if (has(opts, "quoteStyle") && !has(quotes, opts.quoteStyle)) {
|
|
11593
11593
|
throw new TypeError('option "quoteStyle" must be "single" or "double"');
|
|
@@ -11637,15 +11637,15 @@ var require_object_inspect = __commonJS({
|
|
|
11637
11637
|
return isArray2(obj) ? "[Array]" : "[Object]";
|
|
11638
11638
|
}
|
|
11639
11639
|
var indent = getIndent(opts, depth);
|
|
11640
|
-
if (typeof
|
|
11641
|
-
|
|
11642
|
-
} else if (indexOf(
|
|
11640
|
+
if (typeof seen === "undefined") {
|
|
11641
|
+
seen = [];
|
|
11642
|
+
} else if (indexOf(seen, obj) >= 0) {
|
|
11643
11643
|
return "[Circular]";
|
|
11644
11644
|
}
|
|
11645
11645
|
function inspect(value, from, noIndent) {
|
|
11646
11646
|
if (from) {
|
|
11647
|
-
|
|
11648
|
-
|
|
11647
|
+
seen = $arrSlice.call(seen);
|
|
11648
|
+
seen.push(from);
|
|
11649
11649
|
}
|
|
11650
11650
|
if (noIndent) {
|
|
11651
11651
|
var newOpts = {
|
|
@@ -11654,9 +11654,9 @@ var require_object_inspect = __commonJS({
|
|
|
11654
11654
|
if (has(opts, "quoteStyle")) {
|
|
11655
11655
|
newOpts.quoteStyle = opts.quoteStyle;
|
|
11656
11656
|
}
|
|
11657
|
-
return inspect_(value, newOpts, depth + 1,
|
|
11657
|
+
return inspect_(value, newOpts, depth + 1, seen);
|
|
11658
11658
|
}
|
|
11659
|
-
return inspect_(value, opts, depth + 1,
|
|
11659
|
+
return inspect_(value, opts, depth + 1, seen);
|
|
11660
11660
|
}
|
|
11661
11661
|
if (typeof obj === "function" && !isRegExp2(obj)) {
|
|
11662
11662
|
var name = nameOf(obj);
|
|
@@ -13718,7 +13718,7 @@ var require_lodash2 = __commonJS({
|
|
|
13718
13718
|
if (stacked && stack.get(other)) {
|
|
13719
13719
|
return stacked == other;
|
|
13720
13720
|
}
|
|
13721
|
-
var index = -1, result = true,
|
|
13721
|
+
var index = -1, result = true, seen = bitmask & UNORDERED_COMPARE_FLAG ? new SetCache() : void 0;
|
|
13722
13722
|
stack.set(array2, other);
|
|
13723
13723
|
stack.set(other, array2);
|
|
13724
13724
|
while (++index < arrLength) {
|
|
@@ -13733,10 +13733,10 @@ var require_lodash2 = __commonJS({
|
|
|
13733
13733
|
result = false;
|
|
13734
13734
|
break;
|
|
13735
13735
|
}
|
|
13736
|
-
if (
|
|
13736
|
+
if (seen) {
|
|
13737
13737
|
if (!arraySome(other, function(othValue2, othIndex) {
|
|
13738
|
-
if (!
|
|
13739
|
-
return
|
|
13738
|
+
if (!seen.has(othIndex) && (arrValue === othValue2 || equalFunc(arrValue, othValue2, customizer, bitmask, stack))) {
|
|
13739
|
+
return seen.add(othIndex);
|
|
13740
13740
|
}
|
|
13741
13741
|
})) {
|
|
13742
13742
|
result = false;
|
|
@@ -109648,14 +109648,14 @@ function initializeContext(params) {
|
|
|
109648
109648
|
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
109649
109649
|
var _a2;
|
|
109650
109650
|
const def = schema._zod.def;
|
|
109651
|
-
const
|
|
109652
|
-
if (
|
|
109653
|
-
|
|
109651
|
+
const seen = ctx.seen.get(schema);
|
|
109652
|
+
if (seen) {
|
|
109653
|
+
seen.count++;
|
|
109654
109654
|
const isCycle = _params.schemaPath.includes(schema);
|
|
109655
109655
|
if (isCycle) {
|
|
109656
|
-
|
|
109656
|
+
seen.cycle = _params.path;
|
|
109657
109657
|
}
|
|
109658
|
-
return
|
|
109658
|
+
return seen.schema;
|
|
109659
109659
|
}
|
|
109660
109660
|
const result = { schema: {}, count: 1, cycle: void 0, path: _params.path };
|
|
109661
109661
|
ctx.seen.set(schema, result);
|
|
@@ -109738,12 +109738,12 @@ function extractDefs(ctx, schema) {
|
|
|
109738
109738
|
if (entry[1].schema.$ref) {
|
|
109739
109739
|
return;
|
|
109740
109740
|
}
|
|
109741
|
-
const
|
|
109741
|
+
const seen = entry[1];
|
|
109742
109742
|
const { ref, defId } = makeURI(entry);
|
|
109743
|
-
|
|
109743
|
+
seen.def = { ...seen.schema };
|
|
109744
109744
|
if (defId)
|
|
109745
|
-
|
|
109746
|
-
const schema2 =
|
|
109745
|
+
seen.defId = defId;
|
|
109746
|
+
const schema2 = seen.schema;
|
|
109747
109747
|
for (const key in schema2) {
|
|
109748
109748
|
delete schema2[key];
|
|
109749
109749
|
}
|
|
@@ -109751,16 +109751,16 @@ function extractDefs(ctx, schema) {
|
|
|
109751
109751
|
};
|
|
109752
109752
|
if (ctx.cycles === "throw") {
|
|
109753
109753
|
for (const entry of ctx.seen.entries()) {
|
|
109754
|
-
const
|
|
109755
|
-
if (
|
|
109756
|
-
throw new Error(`Cycle detected: #/${
|
|
109754
|
+
const seen = entry[1];
|
|
109755
|
+
if (seen.cycle) {
|
|
109756
|
+
throw new Error(`Cycle detected: #/${seen.cycle?.join("/")}/<root>
|
|
109757
109757
|
|
|
109758
109758
|
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);
|
|
109759
109759
|
}
|
|
109760
109760
|
}
|
|
109761
109761
|
}
|
|
109762
109762
|
for (const entry of ctx.seen.entries()) {
|
|
109763
|
-
const
|
|
109763
|
+
const seen = entry[1];
|
|
109764
109764
|
if (schema === entry[0]) {
|
|
109765
109765
|
extractToDef(entry);
|
|
109766
109766
|
continue;
|
|
@@ -109777,11 +109777,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
109777
109777
|
extractToDef(entry);
|
|
109778
109778
|
continue;
|
|
109779
109779
|
}
|
|
109780
|
-
if (
|
|
109780
|
+
if (seen.cycle) {
|
|
109781
109781
|
extractToDef(entry);
|
|
109782
109782
|
continue;
|
|
109783
109783
|
}
|
|
109784
|
-
if (
|
|
109784
|
+
if (seen.count > 1) {
|
|
109785
109785
|
if (ctx.reused === "ref") {
|
|
109786
109786
|
extractToDef(entry);
|
|
109787
109787
|
continue;
|
|
@@ -109794,13 +109794,13 @@ function finalize(ctx, schema) {
|
|
|
109794
109794
|
if (!root)
|
|
109795
109795
|
throw new Error("Unprocessed schema. This is a bug in Zod.");
|
|
109796
109796
|
const flattenRef = (zodSchema) => {
|
|
109797
|
-
const
|
|
109798
|
-
if (
|
|
109797
|
+
const seen = ctx.seen.get(zodSchema);
|
|
109798
|
+
if (seen.ref === null)
|
|
109799
109799
|
return;
|
|
109800
|
-
const schema2 =
|
|
109800
|
+
const schema2 = seen.def ?? seen.schema;
|
|
109801
109801
|
const _cached = { ...schema2 };
|
|
109802
|
-
const ref =
|
|
109803
|
-
|
|
109802
|
+
const ref = seen.ref;
|
|
109803
|
+
seen.ref = null;
|
|
109804
109804
|
if (ref) {
|
|
109805
109805
|
flattenRef(ref);
|
|
109806
109806
|
const refSeen = ctx.seen.get(ref);
|
|
@@ -109852,7 +109852,7 @@ function finalize(ctx, schema) {
|
|
|
109852
109852
|
ctx.override({
|
|
109853
109853
|
zodSchema,
|
|
109854
109854
|
jsonSchema: schema2,
|
|
109855
|
-
path:
|
|
109855
|
+
path: seen.path ?? []
|
|
109856
109856
|
});
|
|
109857
109857
|
};
|
|
109858
109858
|
for (const entry of [...ctx.seen.entries()].reverse()) {
|
|
@@ -109875,9 +109875,9 @@ function finalize(ctx, schema) {
|
|
|
109875
109875
|
Object.assign(result, root.def ?? root.schema);
|
|
109876
109876
|
const defs = ctx.external?.defs ?? {};
|
|
109877
109877
|
for (const entry of ctx.seen.entries()) {
|
|
109878
|
-
const
|
|
109879
|
-
if (
|
|
109880
|
-
defs[
|
|
109878
|
+
const seen = entry[1];
|
|
109879
|
+
if (seen.def && seen.defId) {
|
|
109880
|
+
defs[seen.defId] = seen.def;
|
|
109881
109881
|
}
|
|
109882
109882
|
}
|
|
109883
109883
|
if (ctx.external) ; else {
|
|
@@ -110371,9 +110371,9 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
110371
110371
|
var nullableProcessor = (schema, ctx, json2, params) => {
|
|
110372
110372
|
const def = schema._zod.def;
|
|
110373
110373
|
const inner = process2(def.innerType, ctx, params);
|
|
110374
|
-
const
|
|
110374
|
+
const seen = ctx.seen.get(schema);
|
|
110375
110375
|
if (ctx.target === "openapi-3.0") {
|
|
110376
|
-
|
|
110376
|
+
seen.ref = def.innerType;
|
|
110377
110377
|
json2.nullable = true;
|
|
110378
110378
|
} else {
|
|
110379
110379
|
json2.anyOf = [inner, { type: "null" }];
|
|
@@ -110382,29 +110382,29 @@ var nullableProcessor = (schema, ctx, json2, params) => {
|
|
|
110382
110382
|
var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
110383
110383
|
const def = schema._zod.def;
|
|
110384
110384
|
process2(def.innerType, ctx, params);
|
|
110385
|
-
const
|
|
110386
|
-
|
|
110385
|
+
const seen = ctx.seen.get(schema);
|
|
110386
|
+
seen.ref = def.innerType;
|
|
110387
110387
|
};
|
|
110388
110388
|
var defaultProcessor = (schema, ctx, json2, params) => {
|
|
110389
110389
|
const def = schema._zod.def;
|
|
110390
110390
|
process2(def.innerType, ctx, params);
|
|
110391
|
-
const
|
|
110392
|
-
|
|
110391
|
+
const seen = ctx.seen.get(schema);
|
|
110392
|
+
seen.ref = def.innerType;
|
|
110393
110393
|
json2.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
110394
110394
|
};
|
|
110395
110395
|
var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
110396
110396
|
const def = schema._zod.def;
|
|
110397
110397
|
process2(def.innerType, ctx, params);
|
|
110398
|
-
const
|
|
110399
|
-
|
|
110398
|
+
const seen = ctx.seen.get(schema);
|
|
110399
|
+
seen.ref = def.innerType;
|
|
110400
110400
|
if (ctx.io === "input")
|
|
110401
110401
|
json2._prefault = JSON.parse(JSON.stringify(def.defaultValue));
|
|
110402
110402
|
};
|
|
110403
110403
|
var catchProcessor = (schema, ctx, json2, params) => {
|
|
110404
110404
|
const def = schema._zod.def;
|
|
110405
110405
|
process2(def.innerType, ctx, params);
|
|
110406
|
-
const
|
|
110407
|
-
|
|
110406
|
+
const seen = ctx.seen.get(schema);
|
|
110407
|
+
seen.ref = def.innerType;
|
|
110408
110408
|
let catchValue;
|
|
110409
110409
|
try {
|
|
110410
110410
|
catchValue = def.catchValue(void 0);
|
|
@@ -110417,33 +110417,33 @@ var pipeProcessor = (schema, ctx, _json, params) => {
|
|
|
110417
110417
|
const def = schema._zod.def;
|
|
110418
110418
|
const innerType = ctx.io === "input" ? def.in._zod.def.type === "transform" ? def.out : def.in : def.out;
|
|
110419
110419
|
process2(innerType, ctx, params);
|
|
110420
|
-
const
|
|
110421
|
-
|
|
110420
|
+
const seen = ctx.seen.get(schema);
|
|
110421
|
+
seen.ref = innerType;
|
|
110422
110422
|
};
|
|
110423
110423
|
var readonlyProcessor = (schema, ctx, json2, params) => {
|
|
110424
110424
|
const def = schema._zod.def;
|
|
110425
110425
|
process2(def.innerType, ctx, params);
|
|
110426
|
-
const
|
|
110427
|
-
|
|
110426
|
+
const seen = ctx.seen.get(schema);
|
|
110427
|
+
seen.ref = def.innerType;
|
|
110428
110428
|
json2.readOnly = true;
|
|
110429
110429
|
};
|
|
110430
110430
|
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
110431
110431
|
const def = schema._zod.def;
|
|
110432
110432
|
process2(def.innerType, ctx, params);
|
|
110433
|
-
const
|
|
110434
|
-
|
|
110433
|
+
const seen = ctx.seen.get(schema);
|
|
110434
|
+
seen.ref = def.innerType;
|
|
110435
110435
|
};
|
|
110436
110436
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
110437
110437
|
const def = schema._zod.def;
|
|
110438
110438
|
process2(def.innerType, ctx, params);
|
|
110439
|
-
const
|
|
110440
|
-
|
|
110439
|
+
const seen = ctx.seen.get(schema);
|
|
110440
|
+
seen.ref = def.innerType;
|
|
110441
110441
|
};
|
|
110442
110442
|
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
110443
110443
|
const innerType = schema._zod.innerType;
|
|
110444
110444
|
process2(innerType, ctx, params);
|
|
110445
|
-
const
|
|
110446
|
-
|
|
110445
|
+
const seen = ctx.seen.get(schema);
|
|
110446
|
+
seen.ref = innerType;
|
|
110447
110447
|
};
|
|
110448
110448
|
var allProcessors = {
|
|
110449
110449
|
string: stringProcessor,
|
|
@@ -112507,17 +112507,19 @@ config(en_default());
|
|
|
112507
112507
|
// src/types.ts
|
|
112508
112508
|
var AutoPromptSchema = external_exports.object({
|
|
112509
112509
|
enabled: external_exports.boolean().default(false),
|
|
112510
|
-
intervalSeconds: external_exports.number().int().positive().default(30),
|
|
112511
|
-
maxIterations: external_exports.number().int().positive().default(10),
|
|
112512
|
-
message: external_exports.string().min(1).default("\u8BF7\u540C\u6B65\u5F53\u524D\u8FDB\u5EA6\uFF0C\u5982\u9700\u5E2E\u52A9\u8BF7\u8BF4\u660E")
|
|
112510
|
+
intervalSeconds: external_exports.number().int().positive().max(300).default(30),
|
|
112511
|
+
maxIterations: external_exports.number().int().positive().max(100).default(10),
|
|
112512
|
+
message: external_exports.string().min(1).default("\u8BF7\u540C\u6B65\u5F53\u524D\u8FDB\u5EA6\uFF0C\u5982\u9700\u5E2E\u52A9\u8BF7\u8BF4\u660E"),
|
|
112513
|
+
idleThreshold: external_exports.number().int().min(1).default(2),
|
|
112514
|
+
idleMaxLength: external_exports.number().int().min(10).default(50)
|
|
112513
112515
|
});
|
|
112514
112516
|
var FeishuConfigSchema = external_exports.object({
|
|
112515
112517
|
appId: external_exports.string().min(1, "appId \u4E0D\u80FD\u4E3A\u7A7A"),
|
|
112516
112518
|
appSecret: external_exports.string().min(1, "appSecret \u4E0D\u80FD\u4E3A\u7A7A"),
|
|
112517
|
-
timeout: external_exports.number().int().positive().default(12e4),
|
|
112519
|
+
timeout: external_exports.number().int().positive().max(6e5).default(12e4),
|
|
112518
112520
|
thinkingDelay: external_exports.number().int().nonnegative().default(2500),
|
|
112519
112521
|
logLevel: external_exports.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
|
|
112520
|
-
maxHistoryMessages: external_exports.number().int().positive().default(200),
|
|
112522
|
+
maxHistoryMessages: external_exports.number().int().positive().max(500).default(200),
|
|
112521
112523
|
pollInterval: external_exports.number().int().positive().default(1e3),
|
|
112522
112524
|
stablePolls: external_exports.number().int().positive().default(3),
|
|
112523
112525
|
dedupTtl: external_exports.number().int().positive().default(10 * 60 * 1e3),
|
|
@@ -112712,37 +112714,40 @@ function buildQuestionCard(request) {
|
|
|
112712
112714
|
}
|
|
112713
112715
|
|
|
112714
112716
|
// src/feishu/sender.ts
|
|
112717
|
+
async function wrapSendCall(fn, idExtractor = (res) => res?.data?.message_id ?? "") {
|
|
112718
|
+
try {
|
|
112719
|
+
const res = await fn();
|
|
112720
|
+
return { ok: true, messageId: idExtractor(res) };
|
|
112721
|
+
} catch (err) {
|
|
112722
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112723
|
+
}
|
|
112724
|
+
}
|
|
112715
112725
|
async function sendTextMessage(client, chatId, text) {
|
|
112716
112726
|
if (!chatId?.trim()) {
|
|
112717
112727
|
return { ok: false, error: "No chat_id provided" };
|
|
112718
112728
|
}
|
|
112719
|
-
|
|
112720
|
-
|
|
112729
|
+
return wrapSendCall(
|
|
112730
|
+
() => client.im.message.create({
|
|
112721
112731
|
params: { receive_id_type: "chat_id" },
|
|
112722
112732
|
data: {
|
|
112723
112733
|
receive_id: chatId.trim(),
|
|
112724
112734
|
msg_type: "text",
|
|
112725
112735
|
content: JSON.stringify({ text })
|
|
112726
112736
|
}
|
|
112727
|
-
})
|
|
112728
|
-
|
|
112729
|
-
} catch (err) {
|
|
112730
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112731
|
-
}
|
|
112737
|
+
})
|
|
112738
|
+
);
|
|
112732
112739
|
}
|
|
112733
112740
|
async function updateMessage(client, messageId, text) {
|
|
112734
|
-
|
|
112735
|
-
|
|
112741
|
+
return wrapSendCall(
|
|
112742
|
+
() => client.im.message.update({
|
|
112736
112743
|
path: { message_id: messageId },
|
|
112737
112744
|
data: {
|
|
112738
112745
|
msg_type: "text",
|
|
112739
112746
|
content: JSON.stringify({ text })
|
|
112740
112747
|
}
|
|
112741
|
-
})
|
|
112742
|
-
|
|
112743
|
-
|
|
112744
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112745
|
-
}
|
|
112748
|
+
}),
|
|
112749
|
+
() => messageId
|
|
112750
|
+
);
|
|
112746
112751
|
}
|
|
112747
112752
|
async function deleteMessage(client, messageId) {
|
|
112748
112753
|
try {
|
|
@@ -112754,49 +112759,71 @@ async function sendInteractiveCard(client, chatId, card) {
|
|
|
112754
112759
|
if (!chatId?.trim()) {
|
|
112755
112760
|
return { ok: false, error: "No chat_id provided" };
|
|
112756
112761
|
}
|
|
112757
|
-
|
|
112758
|
-
|
|
112762
|
+
return wrapSendCall(
|
|
112763
|
+
() => client.im.message.create({
|
|
112759
112764
|
params: { receive_id_type: "chat_id" },
|
|
112760
112765
|
data: {
|
|
112761
112766
|
receive_id: chatId.trim(),
|
|
112762
112767
|
msg_type: "interactive",
|
|
112763
112768
|
content: JSON.stringify(card)
|
|
112764
112769
|
}
|
|
112765
|
-
})
|
|
112766
|
-
|
|
112767
|
-
} catch (err) {
|
|
112768
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112769
|
-
}
|
|
112770
|
+
})
|
|
112771
|
+
);
|
|
112770
112772
|
}
|
|
112771
112773
|
async function sendCardMessage(client, chatId, cardId) {
|
|
112772
112774
|
if (!chatId?.trim()) {
|
|
112773
112775
|
return { ok: false, error: "No chat_id provided" };
|
|
112774
112776
|
}
|
|
112775
|
-
|
|
112776
|
-
|
|
112777
|
+
return wrapSendCall(
|
|
112778
|
+
() => client.im.message.create({
|
|
112777
112779
|
params: { receive_id_type: "chat_id" },
|
|
112778
112780
|
data: {
|
|
112779
112781
|
receive_id: chatId.trim(),
|
|
112780
112782
|
msg_type: "interactive",
|
|
112781
112783
|
content: JSON.stringify({ type: "card_kit", data: { card_id: cardId } })
|
|
112782
112784
|
}
|
|
112783
|
-
})
|
|
112784
|
-
|
|
112785
|
-
} catch (err) {
|
|
112786
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112787
|
-
}
|
|
112785
|
+
})
|
|
112786
|
+
);
|
|
112788
112787
|
}
|
|
112789
112788
|
|
|
112789
|
+
// src/utils/ttl-map.ts
|
|
112790
|
+
var TtlMap = class {
|
|
112791
|
+
constructor(defaultTtlMs) {
|
|
112792
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
112793
|
+
}
|
|
112794
|
+
data = /* @__PURE__ */ new Map();
|
|
112795
|
+
timers = /* @__PURE__ */ new Map();
|
|
112796
|
+
get(key) {
|
|
112797
|
+
return this.data.get(key);
|
|
112798
|
+
}
|
|
112799
|
+
has(key) {
|
|
112800
|
+
return this.data.has(key);
|
|
112801
|
+
}
|
|
112802
|
+
set(key, value, ttlMs) {
|
|
112803
|
+
this.delete(key);
|
|
112804
|
+
this.data.set(key, value);
|
|
112805
|
+
const timer = setTimeout(() => {
|
|
112806
|
+
this.data.delete(key);
|
|
112807
|
+
this.timers.delete(key);
|
|
112808
|
+
}, ttlMs ?? this.defaultTtlMs);
|
|
112809
|
+
timer.unref();
|
|
112810
|
+
this.timers.set(key, timer);
|
|
112811
|
+
}
|
|
112812
|
+
delete(key) {
|
|
112813
|
+
const timer = this.timers.get(key);
|
|
112814
|
+
if (timer) {
|
|
112815
|
+
clearTimeout(timer);
|
|
112816
|
+
this.timers.delete(key);
|
|
112817
|
+
}
|
|
112818
|
+
this.data.delete(key);
|
|
112819
|
+
}
|
|
112820
|
+
};
|
|
112821
|
+
|
|
112790
112822
|
// src/handler/interactive.ts
|
|
112791
|
-
var
|
|
112792
|
-
var seenRequestIds = /* @__PURE__ */ new Map();
|
|
112823
|
+
var seenIds = new TtlMap(10 * 60 * 1e3);
|
|
112793
112824
|
function markSeen(requestId) {
|
|
112794
|
-
|
|
112795
|
-
|
|
112796
|
-
if (now - ts > SEEN_TTL_MS) seenRequestIds.delete(id);
|
|
112797
|
-
}
|
|
112798
|
-
if (seenRequestIds.has(requestId)) return false;
|
|
112799
|
-
seenRequestIds.set(requestId, now);
|
|
112825
|
+
if (seenIds.has(requestId)) return false;
|
|
112826
|
+
seenIds.set(requestId, true);
|
|
112800
112827
|
return true;
|
|
112801
112828
|
}
|
|
112802
112829
|
function handlePermissionRequested(request, chatId, deps) {
|
|
@@ -112864,19 +112891,14 @@ function buildCallbackResponse(action) {
|
|
|
112864
112891
|
}
|
|
112865
112892
|
|
|
112866
112893
|
// src/feishu/dedup.ts
|
|
112867
|
-
var
|
|
112868
|
-
var seen = /* @__PURE__ */ new Map();
|
|
112894
|
+
var dedup = new TtlMap(10 * 60 * 1e3);
|
|
112869
112895
|
function initDedup(ttl) {
|
|
112870
|
-
|
|
112896
|
+
dedup = new TtlMap(ttl);
|
|
112871
112897
|
}
|
|
112872
112898
|
function isDuplicate(messageId) {
|
|
112873
|
-
const now = Date.now();
|
|
112874
|
-
for (const [k, ts] of seen) {
|
|
112875
|
-
if (now - ts > seenTtlMs) seen.delete(k);
|
|
112876
|
-
}
|
|
112877
112899
|
if (!messageId) return false;
|
|
112878
|
-
if (
|
|
112879
|
-
|
|
112900
|
+
if (dedup.has(messageId)) return true;
|
|
112901
|
+
dedup.set(messageId, true);
|
|
112880
112902
|
return false;
|
|
112881
112903
|
}
|
|
112882
112904
|
|
|
@@ -113314,57 +113336,24 @@ function emit(sessionId, action) {
|
|
|
113314
113336
|
|
|
113315
113337
|
// src/handler/event.ts
|
|
113316
113338
|
var pendingBySession = /* @__PURE__ */ new Map();
|
|
113317
|
-
var sessionErrors =
|
|
113318
|
-
var
|
|
113319
|
-
var SESSION_ERROR_TTL_MS = 3e4;
|
|
113320
|
-
var retryAttempts = /* @__PURE__ */ new Map();
|
|
113321
|
-
var retryAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
113339
|
+
var sessionErrors = new TtlMap(3e4);
|
|
113340
|
+
var retryAttempts = new TtlMap(36e5);
|
|
113322
113341
|
var MAX_RETRY_ATTEMPTS = 2;
|
|
113323
|
-
var RETRY_ATTEMPTS_TTL_MS = 36e5;
|
|
113324
113342
|
function clearRetryAttempts(sessionKey) {
|
|
113325
113343
|
retryAttempts.delete(sessionKey);
|
|
113326
|
-
const timer = retryAttemptTimeouts.get(sessionKey);
|
|
113327
|
-
if (timer) {
|
|
113328
|
-
clearTimeout(timer);
|
|
113329
|
-
retryAttemptTimeouts.delete(sessionKey);
|
|
113330
|
-
}
|
|
113331
113344
|
}
|
|
113332
113345
|
function getRetryAttempts(sessionKey) {
|
|
113333
113346
|
return retryAttempts.get(sessionKey) ?? 0;
|
|
113334
113347
|
}
|
|
113335
113348
|
function setRetryAttempts(sessionKey, count) {
|
|
113336
113349
|
retryAttempts.set(sessionKey, count);
|
|
113337
|
-
const existing = retryAttemptTimeouts.get(sessionKey);
|
|
113338
|
-
if (existing) clearTimeout(existing);
|
|
113339
|
-
const timeoutId = setTimeout(() => {
|
|
113340
|
-
retryAttempts.delete(sessionKey);
|
|
113341
|
-
retryAttemptTimeouts.delete(sessionKey);
|
|
113342
|
-
}, RETRY_ATTEMPTS_TTL_MS);
|
|
113343
|
-
retryAttemptTimeouts.set(sessionKey, timeoutId);
|
|
113344
113350
|
}
|
|
113345
113351
|
function getSessionError(sessionId) {
|
|
113346
113352
|
return sessionErrors.get(sessionId);
|
|
113347
113353
|
}
|
|
113348
113354
|
function clearSessionError(sessionId) {
|
|
113349
|
-
const timer = sessionErrorTimeouts.get(sessionId);
|
|
113350
|
-
if (timer) {
|
|
113351
|
-
clearTimeout(timer);
|
|
113352
|
-
sessionErrorTimeouts.delete(sessionId);
|
|
113353
|
-
}
|
|
113354
113355
|
sessionErrors.delete(sessionId);
|
|
113355
113356
|
}
|
|
113356
|
-
function setSessionError(sessionId, message, fields) {
|
|
113357
|
-
const existing = sessionErrorTimeouts.get(sessionId);
|
|
113358
|
-
if (existing) {
|
|
113359
|
-
clearTimeout(existing);
|
|
113360
|
-
}
|
|
113361
|
-
sessionErrors.set(sessionId, { message, fields });
|
|
113362
|
-
const timeoutId = setTimeout(() => {
|
|
113363
|
-
sessionErrors.delete(sessionId);
|
|
113364
|
-
sessionErrorTimeouts.delete(sessionId);
|
|
113365
|
-
}, SESSION_ERROR_TTL_MS);
|
|
113366
|
-
sessionErrorTimeouts.set(sessionId, timeoutId);
|
|
113367
|
-
}
|
|
113368
113357
|
function registerPending(sessionId, payload) {
|
|
113369
113358
|
pendingBySession.set(sessionId, { ...payload, textBuffer: "", expectedMessageId: void 0 });
|
|
113370
113359
|
}
|
|
@@ -113415,111 +113404,115 @@ async function handleEvent(event, deps) {
|
|
|
113415
113404
|
switch (event.type) {
|
|
113416
113405
|
case "message.part.updated": {
|
|
113417
113406
|
const part = event.properties.part;
|
|
113418
|
-
if (!part) break;
|
|
113419
|
-
const
|
|
113420
|
-
if (!sessionId) break;
|
|
113421
|
-
const payload = pendingBySession.get(sessionId);
|
|
113407
|
+
if (!part?.sessionID) break;
|
|
113408
|
+
const payload = pendingBySession.get(part.sessionID);
|
|
113422
113409
|
if (!payload) break;
|
|
113423
|
-
|
|
113424
|
-
if (messageId) {
|
|
113425
|
-
if (!payload.expectedMessageId) {
|
|
113426
|
-
payload.expectedMessageId = messageId;
|
|
113427
|
-
} else if (payload.expectedMessageId !== messageId) {
|
|
113428
|
-
break;
|
|
113429
|
-
}
|
|
113430
|
-
} else if (payload.expectedMessageId) {
|
|
113431
|
-
break;
|
|
113432
|
-
}
|
|
113433
|
-
const partSessionId = part.sessionID;
|
|
113434
|
-
if (part.type === "tool") {
|
|
113435
|
-
const p = part;
|
|
113436
|
-
const toolName = String(p.toolName ?? p.name ?? "unknown");
|
|
113437
|
-
const callID = String(p.toolCallID ?? p.id ?? "");
|
|
113438
|
-
const hasError = p.error !== void 0 && p.error !== null;
|
|
113439
|
-
const rawState = p.state != null ? String(p.state) : hasError ? "error" : "running";
|
|
113440
|
-
const toolState = rawState === "completed" || rawState === "error" ? rawState : "running";
|
|
113441
|
-
if (partSessionId) {
|
|
113442
|
-
emit(partSessionId, {
|
|
113443
|
-
type: "tool-state-changed",
|
|
113444
|
-
sessionId: partSessionId,
|
|
113445
|
-
callID,
|
|
113446
|
-
tool: toolName,
|
|
113447
|
-
state: toolState
|
|
113448
|
-
});
|
|
113449
|
-
}
|
|
113450
|
-
break;
|
|
113451
|
-
}
|
|
113452
|
-
const delta = event.properties.delta;
|
|
113453
|
-
if (delta) {
|
|
113454
|
-
payload.textBuffer += delta;
|
|
113455
|
-
} else {
|
|
113456
|
-
const fullText = extractPartText(part);
|
|
113457
|
-
if (fullText) {
|
|
113458
|
-
payload.textBuffer = fullText;
|
|
113459
|
-
}
|
|
113460
|
-
}
|
|
113461
|
-
if (payload.textBuffer) {
|
|
113462
|
-
const res = await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
113463
|
-
if (!res.ok) ;
|
|
113464
|
-
}
|
|
113465
|
-
if (partSessionId) {
|
|
113466
|
-
emit(partSessionId, {
|
|
113467
|
-
type: "text-updated",
|
|
113468
|
-
sessionId: partSessionId,
|
|
113469
|
-
messageId: part.messageID,
|
|
113470
|
-
delta: delta ?? void 0,
|
|
113471
|
-
fullText: payload.textBuffer
|
|
113472
|
-
});
|
|
113473
|
-
}
|
|
113410
|
+
await handleMessagePartUpdated(event, part, payload);
|
|
113474
113411
|
break;
|
|
113475
113412
|
}
|
|
113476
|
-
case "session.error":
|
|
113477
|
-
|
|
113478
|
-
const sessionId = props.sessionID;
|
|
113479
|
-
if (!sessionId) break;
|
|
113480
|
-
const error48 = props.error;
|
|
113481
|
-
let errMsg;
|
|
113482
|
-
if (typeof error48 === "string") {
|
|
113483
|
-
errMsg = error48;
|
|
113484
|
-
} else if (error48 && typeof error48 === "object") {
|
|
113485
|
-
const e = error48;
|
|
113486
|
-
const rawDataMsg = e.data && typeof e.data === "object" && "message" in e.data ? e.data.message : void 0;
|
|
113487
|
-
const dataMsg = rawDataMsg != null ? String(rawDataMsg) : void 0;
|
|
113488
|
-
errMsg = String(e.message ?? dataMsg ?? e.type ?? e.name ?? "An unexpected error occurred");
|
|
113489
|
-
} else {
|
|
113490
|
-
errMsg = String(error48);
|
|
113491
|
-
}
|
|
113492
|
-
const fields = extractErrorFields(error48);
|
|
113493
|
-
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", { sessionId, errMsg });
|
|
113494
|
-
setSessionError(sessionId, errMsg, fields);
|
|
113413
|
+
case "session.error":
|
|
113414
|
+
handleSessionErrorEvent(event, deps);
|
|
113495
113415
|
break;
|
|
113496
|
-
|
|
113497
|
-
|
|
113498
|
-
const evtType = event.type;
|
|
113499
|
-
const evtProps = event.properties ?? {};
|
|
113500
|
-
const evtSessionId = evtProps.sessionID;
|
|
113501
|
-
if (evtType === "permission.asked" && evtSessionId) {
|
|
113502
|
-
emit(evtSessionId, {
|
|
113503
|
-
type: "permission-requested",
|
|
113504
|
-
sessionId: evtSessionId,
|
|
113505
|
-
request: evtProps
|
|
113506
|
-
});
|
|
113507
|
-
deps.log("info", "permission.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113508
|
-
} else if (evtType === "question.asked" && evtSessionId) {
|
|
113509
|
-
emit(evtSessionId, {
|
|
113510
|
-
type: "question-requested",
|
|
113511
|
-
sessionId: evtSessionId,
|
|
113512
|
-
request: evtProps
|
|
113513
|
-
});
|
|
113514
|
-
deps.log("info", "question.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113515
|
-
} else if (evtType === "session.idle" && evtSessionId) {
|
|
113516
|
-
emit(evtSessionId, {
|
|
113517
|
-
type: "session-idle",
|
|
113518
|
-
sessionId: evtSessionId
|
|
113519
|
-
});
|
|
113520
|
-
}
|
|
113416
|
+
default:
|
|
113417
|
+
handleV2Event(event, deps);
|
|
113521
113418
|
break;
|
|
113419
|
+
}
|
|
113420
|
+
}
|
|
113421
|
+
async function handleMessagePartUpdated(event, part, payload) {
|
|
113422
|
+
const messageId = part.messageID;
|
|
113423
|
+
if (messageId) {
|
|
113424
|
+
if (!payload.expectedMessageId) {
|
|
113425
|
+
payload.expectedMessageId = messageId;
|
|
113426
|
+
} else if (payload.expectedMessageId !== messageId) {
|
|
113427
|
+
return;
|
|
113428
|
+
}
|
|
113429
|
+
} else if (payload.expectedMessageId) {
|
|
113430
|
+
return;
|
|
113431
|
+
}
|
|
113432
|
+
const partSessionId = part.sessionID;
|
|
113433
|
+
if (part.type === "tool") {
|
|
113434
|
+
const p = part;
|
|
113435
|
+
const toolName = String(p.toolName ?? p.name ?? "unknown");
|
|
113436
|
+
const callID = String(p.toolCallID ?? p.id ?? "");
|
|
113437
|
+
const hasError = p.error !== void 0 && p.error !== null;
|
|
113438
|
+
const rawState = p.state != null ? String(p.state) : hasError ? "error" : "running";
|
|
113439
|
+
const toolState = rawState === "completed" || rawState === "error" ? rawState : "running";
|
|
113440
|
+
if (partSessionId) {
|
|
113441
|
+
emit(partSessionId, {
|
|
113442
|
+
type: "tool-state-changed",
|
|
113443
|
+
sessionId: partSessionId,
|
|
113444
|
+
callID,
|
|
113445
|
+
tool: toolName,
|
|
113446
|
+
state: toolState
|
|
113447
|
+
});
|
|
113522
113448
|
}
|
|
113449
|
+
return;
|
|
113450
|
+
}
|
|
113451
|
+
const delta = event.properties.delta;
|
|
113452
|
+
if (delta) {
|
|
113453
|
+
payload.textBuffer += delta;
|
|
113454
|
+
} else {
|
|
113455
|
+
const fullText = extractPartText(part);
|
|
113456
|
+
if (fullText) {
|
|
113457
|
+
payload.textBuffer = fullText;
|
|
113458
|
+
}
|
|
113459
|
+
}
|
|
113460
|
+
if (payload.textBuffer) {
|
|
113461
|
+
await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
113462
|
+
}
|
|
113463
|
+
if (partSessionId) {
|
|
113464
|
+
emit(partSessionId, {
|
|
113465
|
+
type: "text-updated",
|
|
113466
|
+
sessionId: partSessionId,
|
|
113467
|
+
messageId: part.messageID,
|
|
113468
|
+
delta: delta ?? void 0,
|
|
113469
|
+
fullText: payload.textBuffer
|
|
113470
|
+
});
|
|
113471
|
+
}
|
|
113472
|
+
}
|
|
113473
|
+
function handleSessionErrorEvent(event, deps) {
|
|
113474
|
+
const props = event.properties;
|
|
113475
|
+
const sessionId = props.sessionID;
|
|
113476
|
+
if (!sessionId) return;
|
|
113477
|
+
const error48 = props.error;
|
|
113478
|
+
let errMsg;
|
|
113479
|
+
if (typeof error48 === "string") {
|
|
113480
|
+
errMsg = error48;
|
|
113481
|
+
} else if (error48 && typeof error48 === "object") {
|
|
113482
|
+
const e = error48;
|
|
113483
|
+
const asStr = (v) => typeof v === "string" && v.trim().length > 0 ? v : void 0;
|
|
113484
|
+
const rawDataMsg = e.data && typeof e.data === "object" && "message" in e.data ? e.data.message : void 0;
|
|
113485
|
+
errMsg = asStr(e.message) ?? asStr(rawDataMsg) ?? asStr(e.type) ?? asStr(e.name) ?? "An unexpected error occurred";
|
|
113486
|
+
} else {
|
|
113487
|
+
errMsg = String(error48);
|
|
113488
|
+
}
|
|
113489
|
+
const fields = extractErrorFields(error48);
|
|
113490
|
+
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", { sessionId, errMsg });
|
|
113491
|
+
sessionErrors.set(sessionId, { message: errMsg, fields });
|
|
113492
|
+
}
|
|
113493
|
+
function handleV2Event(event, deps) {
|
|
113494
|
+
const evtType = event.type;
|
|
113495
|
+
const evtProps = event.properties ?? {};
|
|
113496
|
+
const evtSessionId = evtProps.sessionID;
|
|
113497
|
+
if (evtType === "permission.asked" && evtSessionId) {
|
|
113498
|
+
emit(evtSessionId, {
|
|
113499
|
+
type: "permission-requested",
|
|
113500
|
+
sessionId: evtSessionId,
|
|
113501
|
+
request: evtProps
|
|
113502
|
+
});
|
|
113503
|
+
deps.log("info", "permission.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113504
|
+
} else if (evtType === "question.asked" && evtSessionId) {
|
|
113505
|
+
emit(evtSessionId, {
|
|
113506
|
+
type: "question-requested",
|
|
113507
|
+
sessionId: evtSessionId,
|
|
113508
|
+
request: evtProps
|
|
113509
|
+
});
|
|
113510
|
+
deps.log("info", "question.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113511
|
+
} else if (evtType === "session.idle" && evtSessionId) {
|
|
113512
|
+
emit(evtSessionId, {
|
|
113513
|
+
type: "session-idle",
|
|
113514
|
+
sessionId: evtSessionId
|
|
113515
|
+
});
|
|
113523
113516
|
}
|
|
113524
113517
|
}
|
|
113525
113518
|
function extractPartText(part) {
|
|
@@ -113530,6 +113523,128 @@ function extractPartText(part) {
|
|
|
113530
113523
|
return "";
|
|
113531
113524
|
}
|
|
113532
113525
|
|
|
113526
|
+
// src/handler/error-recovery.ts
|
|
113527
|
+
var SessionErrorDetected = class extends Error {
|
|
113528
|
+
constructor(sessionError) {
|
|
113529
|
+
super(sessionError.message);
|
|
113530
|
+
this.sessionError = sessionError;
|
|
113531
|
+
this.name = "SessionErrorDetected";
|
|
113532
|
+
}
|
|
113533
|
+
};
|
|
113534
|
+
async function getGlobalDefaultModel(client, directory) {
|
|
113535
|
+
const query = directory ? { directory } : void 0;
|
|
113536
|
+
const { data: config2 } = await client.config.get({ query });
|
|
113537
|
+
const model = config2?.model;
|
|
113538
|
+
if (!model || !model.includes("/")) return void 0;
|
|
113539
|
+
const slash = model.indexOf("/");
|
|
113540
|
+
const providerID = model.slice(0, slash).trim();
|
|
113541
|
+
const modelID = model.slice(slash + 1).trim();
|
|
113542
|
+
if (!providerID || !modelID) return void 0;
|
|
113543
|
+
return { providerID, modelID };
|
|
113544
|
+
}
|
|
113545
|
+
function extractSessionError(err, sessionId) {
|
|
113546
|
+
const result = err instanceof SessionErrorDetected ? err.sessionError : getSessionError(sessionId);
|
|
113547
|
+
clearSessionError(sessionId);
|
|
113548
|
+
return result;
|
|
113549
|
+
}
|
|
113550
|
+
async function tryModelRecovery(params) {
|
|
113551
|
+
const {
|
|
113552
|
+
sessionError,
|
|
113553
|
+
sessionId,
|
|
113554
|
+
sessionKey,
|
|
113555
|
+
client,
|
|
113556
|
+
directory,
|
|
113557
|
+
parts,
|
|
113558
|
+
timeout,
|
|
113559
|
+
pollInterval,
|
|
113560
|
+
stablePolls,
|
|
113561
|
+
query,
|
|
113562
|
+
signal,
|
|
113563
|
+
log,
|
|
113564
|
+
poll
|
|
113565
|
+
} = params;
|
|
113566
|
+
log("info", "\u9519\u8BEF\u5B57\u6BB5\u68C0\u67E5", {
|
|
113567
|
+
sessionKey,
|
|
113568
|
+
fields: sessionError.fields,
|
|
113569
|
+
isModel: isModelError(sessionError.fields)
|
|
113570
|
+
});
|
|
113571
|
+
if (!isModelError(sessionError.fields)) {
|
|
113572
|
+
return { recovered: false, sessionError };
|
|
113573
|
+
}
|
|
113574
|
+
const attempts = getRetryAttempts(sessionKey);
|
|
113575
|
+
if (attempts >= MAX_RETRY_ATTEMPTS) {
|
|
113576
|
+
log("warn", "\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey, attempts });
|
|
113577
|
+
return { recovered: false, sessionError };
|
|
113578
|
+
}
|
|
113579
|
+
try {
|
|
113580
|
+
let modelOverride;
|
|
113581
|
+
try {
|
|
113582
|
+
modelOverride = await getGlobalDefaultModel(client, directory);
|
|
113583
|
+
} catch (configErr) {
|
|
113584
|
+
log("warn", "\u8BFB\u53D6\u5168\u5C40\u6A21\u578B\u914D\u7F6E\u5931\u8D25", {
|
|
113585
|
+
sessionKey,
|
|
113586
|
+
error: configErr instanceof Error ? configErr.message : String(configErr)
|
|
113587
|
+
});
|
|
113588
|
+
}
|
|
113589
|
+
if (!modelOverride) {
|
|
113590
|
+
log("warn", "\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey });
|
|
113591
|
+
return { recovered: false, sessionError };
|
|
113592
|
+
}
|
|
113593
|
+
setRetryAttempts(sessionKey, attempts + 1);
|
|
113594
|
+
log("info", "\u4F7F\u7528\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u6062\u590D", {
|
|
113595
|
+
sessionKey,
|
|
113596
|
+
providerID: modelOverride.providerID,
|
|
113597
|
+
modelID: modelOverride.modelID
|
|
113598
|
+
});
|
|
113599
|
+
clearSessionError(sessionId);
|
|
113600
|
+
await client.session.promptAsync({
|
|
113601
|
+
path: { id: sessionId },
|
|
113602
|
+
query,
|
|
113603
|
+
body: { parts: [...parts], model: modelOverride }
|
|
113604
|
+
});
|
|
113605
|
+
const finalText = await poll(client, sessionId, {
|
|
113606
|
+
timeout,
|
|
113607
|
+
pollInterval,
|
|
113608
|
+
stablePolls,
|
|
113609
|
+
query,
|
|
113610
|
+
signal
|
|
113611
|
+
});
|
|
113612
|
+
log("info", "\u6A21\u578B\u6062\u590D\u540E\u54CD\u5E94\u5B8C\u6210", {
|
|
113613
|
+
sessionKey,
|
|
113614
|
+
sessionId,
|
|
113615
|
+
output: finalText || "(empty)"
|
|
113616
|
+
});
|
|
113617
|
+
clearRetryAttempts(sessionKey);
|
|
113618
|
+
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
113619
|
+
sessionId,
|
|
113620
|
+
sessionKey,
|
|
113621
|
+
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
113622
|
+
attempt: attempts + 1
|
|
113623
|
+
});
|
|
113624
|
+
return { recovered: true, text: finalText };
|
|
113625
|
+
} catch (recoveryErr) {
|
|
113626
|
+
if (recoveryErr instanceof Error && recoveryErr.name === "AbortError") {
|
|
113627
|
+
throw recoveryErr;
|
|
113628
|
+
}
|
|
113629
|
+
const errMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
113630
|
+
let updatedError;
|
|
113631
|
+
if (recoveryErr instanceof SessionErrorDetected) {
|
|
113632
|
+
updatedError = recoveryErr.sessionError;
|
|
113633
|
+
clearSessionError(sessionId);
|
|
113634
|
+
} else {
|
|
113635
|
+
const sseError = getSessionError(sessionId);
|
|
113636
|
+
if (sseError) {
|
|
113637
|
+
updatedError = sseError;
|
|
113638
|
+
clearSessionError(sessionId);
|
|
113639
|
+
} else {
|
|
113640
|
+
updatedError = { message: errMsg, fields: [] };
|
|
113641
|
+
}
|
|
113642
|
+
}
|
|
113643
|
+
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", { sessionId, sessionKey, error: errMsg });
|
|
113644
|
+
return { recovered: false, sessionError: updatedError };
|
|
113645
|
+
}
|
|
113646
|
+
}
|
|
113647
|
+
|
|
113533
113648
|
// src/session.ts
|
|
113534
113649
|
var SESSION_KEY_PREFIX = "feishu";
|
|
113535
113650
|
var TITLE_PREFIX = "Feishu";
|
|
@@ -113762,7 +113877,6 @@ var StreamingCard = class {
|
|
|
113762
113877
|
};
|
|
113763
113878
|
|
|
113764
113879
|
// src/handler/chat.ts
|
|
113765
|
-
var activeAutoPrompts = /* @__PURE__ */ new Map();
|
|
113766
113880
|
async function finalizeReply(streamingCard, feishuClient, chatId, placeholderId, text) {
|
|
113767
113881
|
if (streamingCard) {
|
|
113768
113882
|
await streamingCard.close(text);
|
|
@@ -113780,19 +113894,13 @@ async function abortCleanup(streamingCard, feishuClient, placeholderId) {
|
|
|
113780
113894
|
}
|
|
113781
113895
|
async function handleChat(ctx, deps, signal) {
|
|
113782
113896
|
const { content, chatId, chatType, senderId, shouldReply, messageType, rawContent, messageId } = ctx;
|
|
113783
|
-
if (!content.trim() && messageType === "text") return;
|
|
113897
|
+
if (!content.trim() && messageType === "text") return void 0;
|
|
113784
113898
|
const { config: config2, client, feishuClient, log, directory } = deps;
|
|
113785
113899
|
const query = directory ? { directory } : void 0;
|
|
113786
113900
|
const sessionKey = buildSessionKey(chatType, chatType === "p2p" ? senderId : chatId);
|
|
113787
|
-
const existing = activeAutoPrompts.get(sessionKey);
|
|
113788
|
-
if (existing) {
|
|
113789
|
-
existing.abort();
|
|
113790
|
-
activeAutoPrompts.delete(sessionKey);
|
|
113791
|
-
log("info", "\u7528\u6237\u4ECB\u5165\uFF0C\u81EA\u52A8\u63D0\u793A\u5DF2\u4E2D\u65AD", { sessionKey });
|
|
113792
|
-
}
|
|
113793
113901
|
const session = await getOrCreateSession(client, sessionKey, directory);
|
|
113794
113902
|
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log);
|
|
113795
|
-
if (!parts.length) return;
|
|
113903
|
+
if (!parts.length) return void 0;
|
|
113796
113904
|
log("info", "\u6536\u5230\u7528\u6237\u6D88\u606F", {
|
|
113797
113905
|
sessionKey,
|
|
113798
113906
|
sessionId: session.id,
|
|
@@ -113816,7 +113924,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113816
113924
|
error: err instanceof Error ? err.message : String(err)
|
|
113817
113925
|
});
|
|
113818
113926
|
}
|
|
113819
|
-
return;
|
|
113927
|
+
return void 0;
|
|
113820
113928
|
}
|
|
113821
113929
|
const timeout = config2.timeout;
|
|
113822
113930
|
const thinkingDelay = config2.thinkingDelay;
|
|
@@ -113857,46 +113965,6 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113857
113965
|
});
|
|
113858
113966
|
}
|
|
113859
113967
|
}, thinkingDelay) : null;
|
|
113860
|
-
async function runAutoPromptLoop(activeId) {
|
|
113861
|
-
const { autoPrompt } = config2;
|
|
113862
|
-
if (!autoPrompt.enabled || !shouldReply) return;
|
|
113863
|
-
const ac = new AbortController();
|
|
113864
|
-
activeAutoPrompts.set(sessionKey, ac);
|
|
113865
|
-
log("info", "\u542F\u52A8\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF", { sessionKey, maxIterations: autoPrompt.maxIterations });
|
|
113866
|
-
try {
|
|
113867
|
-
for (let i = 0; i < autoPrompt.maxIterations; i++) {
|
|
113868
|
-
await abortableSleep(autoPrompt.intervalSeconds * 1e3, ac.signal);
|
|
113869
|
-
log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey, iteration: i + 1 });
|
|
113870
|
-
clearSessionError(activeId);
|
|
113871
|
-
await client.session.prompt({
|
|
113872
|
-
path: { id: activeId },
|
|
113873
|
-
query,
|
|
113874
|
-
body: { parts: [{ type: "text", text: autoPrompt.message }] }
|
|
113875
|
-
});
|
|
113876
|
-
const text = await pollForResponse(client, activeId, { timeout, pollInterval, stablePolls, query, signal: ac.signal });
|
|
113877
|
-
if (text) {
|
|
113878
|
-
log("info", "\u81EA\u52A8\u63D0\u793A\u54CD\u5E94", {
|
|
113879
|
-
sessionKey,
|
|
113880
|
-
iteration: i + 1,
|
|
113881
|
-
output: text
|
|
113882
|
-
});
|
|
113883
|
-
await sendTextMessage(feishuClient, chatId, text);
|
|
113884
|
-
}
|
|
113885
|
-
}
|
|
113886
|
-
log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", { sessionKey });
|
|
113887
|
-
} catch (loopErr) {
|
|
113888
|
-
if (loopErr.name === "AbortError") {
|
|
113889
|
-
log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u88AB\u4E2D\u65AD", { sessionKey });
|
|
113890
|
-
} else {
|
|
113891
|
-
log("error", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u5F02\u5E38", {
|
|
113892
|
-
sessionKey,
|
|
113893
|
-
error: loopErr instanceof Error ? loopErr.message : String(loopErr)
|
|
113894
|
-
});
|
|
113895
|
-
}
|
|
113896
|
-
} finally {
|
|
113897
|
-
activeAutoPrompts.delete(sessionKey);
|
|
113898
|
-
}
|
|
113899
|
-
}
|
|
113900
113968
|
let cardUnsub;
|
|
113901
113969
|
{
|
|
113902
113970
|
const card = streamingCard;
|
|
@@ -113942,113 +114010,60 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113942
114010
|
});
|
|
113943
114011
|
clearRetryAttempts(sessionKey);
|
|
113944
114012
|
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
113945
|
-
|
|
114013
|
+
if (config2.autoPrompt.enabled && shouldReply) {
|
|
114014
|
+
return { sessionId: session.id, sessionKey, chatId, deps };
|
|
114015
|
+
}
|
|
114016
|
+
return void 0;
|
|
113946
114017
|
} catch (err) {
|
|
113947
114018
|
if (err instanceof Error && err.name === "AbortError") {
|
|
113948
114019
|
log("info", "\u5904\u7406\u88AB\u4E2D\u65AD", { sessionKey, sessionId: session.id });
|
|
113949
114020
|
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
113950
|
-
return;
|
|
113951
|
-
}
|
|
113952
|
-
let sessionError;
|
|
113953
|
-
if (err instanceof SessionErrorDetected) {
|
|
113954
|
-
sessionError = err.sessionError;
|
|
113955
|
-
clearSessionError(session.id);
|
|
113956
|
-
} else {
|
|
113957
|
-
sessionError = getSessionError(session.id);
|
|
113958
|
-
clearSessionError(session.id);
|
|
114021
|
+
return void 0;
|
|
113959
114022
|
}
|
|
114023
|
+
const sessionError = extractSessionError(err, session.id);
|
|
114024
|
+
let displayError = sessionError;
|
|
113960
114025
|
if (sessionError) {
|
|
113961
|
-
|
|
113962
|
-
|
|
113963
|
-
|
|
113964
|
-
|
|
113965
|
-
});
|
|
113966
|
-
}
|
|
113967
|
-
if (sessionError && isModelError(sessionError.fields)) {
|
|
113968
|
-
const attempts = getRetryAttempts(sessionKey);
|
|
113969
|
-
if (attempts < MAX_RETRY_ATTEMPTS) {
|
|
113970
|
-
try {
|
|
113971
|
-
let modelOverride;
|
|
113972
|
-
try {
|
|
113973
|
-
modelOverride = await getGlobalDefaultModel(client, directory);
|
|
113974
|
-
} catch (configErr) {
|
|
113975
|
-
log("warn", "\u8BFB\u53D6\u5168\u5C40\u6A21\u578B\u914D\u7F6E\u5931\u8D25", {
|
|
113976
|
-
sessionKey,
|
|
113977
|
-
error: configErr instanceof Error ? configErr.message : String(configErr)
|
|
113978
|
-
});
|
|
113979
|
-
}
|
|
113980
|
-
if (!modelOverride) {
|
|
113981
|
-
log("warn", "\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey });
|
|
113982
|
-
} else {
|
|
113983
|
-
setRetryAttempts(sessionKey, attempts + 1);
|
|
113984
|
-
log("info", "\u4F7F\u7528\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u6062\u590D", {
|
|
113985
|
-
sessionKey,
|
|
113986
|
-
providerID: modelOverride.providerID,
|
|
113987
|
-
modelID: modelOverride.modelID
|
|
113988
|
-
});
|
|
113989
|
-
clearSessionError(session.id);
|
|
113990
|
-
await client.session.promptAsync({
|
|
113991
|
-
path: { id: session.id },
|
|
113992
|
-
query,
|
|
113993
|
-
body: { ...baseBody, model: modelOverride }
|
|
113994
|
-
});
|
|
113995
|
-
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query, signal });
|
|
113996
|
-
log("info", "\u6A21\u578B\u6062\u590D\u540E\u54CD\u5E94\u5B8C\u6210", {
|
|
113997
|
-
sessionKey,
|
|
113998
|
-
sessionId: session.id,
|
|
113999
|
-
output: finalText || "(empty)"
|
|
114000
|
-
});
|
|
114001
|
-
clearRetryAttempts(sessionKey);
|
|
114002
|
-
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
114003
|
-
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
114004
|
-
sessionId: session.id,
|
|
114005
|
-
sessionKey,
|
|
114006
|
-
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
114007
|
-
attempt: attempts + 1
|
|
114008
|
-
});
|
|
114009
|
-
await runAutoPromptLoop(session.id);
|
|
114010
|
-
return;
|
|
114011
|
-
}
|
|
114012
|
-
} catch (recoveryErr) {
|
|
114013
|
-
if (recoveryErr instanceof Error && recoveryErr.name === "AbortError") {
|
|
114014
|
-
log("info", "\u6A21\u578B\u6062\u590D\u88AB\u4E2D\u65AD", { sessionKey });
|
|
114015
|
-
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
114016
|
-
return;
|
|
114017
|
-
}
|
|
114018
|
-
const errMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
114019
|
-
if (recoveryErr instanceof SessionErrorDetected) {
|
|
114020
|
-
sessionError = recoveryErr.sessionError;
|
|
114021
|
-
clearSessionError(session.id);
|
|
114022
|
-
} else {
|
|
114023
|
-
const sseError = getSessionError(session.id);
|
|
114024
|
-
if (sseError) {
|
|
114025
|
-
sessionError = sseError;
|
|
114026
|
-
clearSessionError(session.id);
|
|
114027
|
-
} else {
|
|
114028
|
-
sessionError = { message: errMsg, fields: [] };
|
|
114029
|
-
}
|
|
114030
|
-
}
|
|
114031
|
-
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", {
|
|
114032
|
-
sessionId: session.id,
|
|
114033
|
-
sessionKey,
|
|
114034
|
-
error: errMsg
|
|
114035
|
-
});
|
|
114036
|
-
}
|
|
114037
|
-
} else {
|
|
114038
|
-
log("warn", "\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", {
|
|
114026
|
+
try {
|
|
114027
|
+
const recovery = await tryModelRecovery({
|
|
114028
|
+
sessionError,
|
|
114029
|
+
sessionId: session.id,
|
|
114039
114030
|
sessionKey,
|
|
114040
|
-
|
|
114031
|
+
client,
|
|
114032
|
+
directory,
|
|
114033
|
+
parts,
|
|
114034
|
+
timeout,
|
|
114035
|
+
pollInterval,
|
|
114036
|
+
stablePolls,
|
|
114037
|
+
query,
|
|
114038
|
+
signal,
|
|
114039
|
+
log,
|
|
114040
|
+
poll: pollForResponse
|
|
114041
114041
|
});
|
|
114042
|
+
if (recovery.recovered) {
|
|
114043
|
+
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, recovery.text || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
114044
|
+
if (config2.autoPrompt.enabled && shouldReply) {
|
|
114045
|
+
return { sessionId: session.id, sessionKey, chatId, deps };
|
|
114046
|
+
}
|
|
114047
|
+
return void 0;
|
|
114048
|
+
}
|
|
114049
|
+
displayError = recovery.sessionError;
|
|
114050
|
+
} catch (abortErr) {
|
|
114051
|
+
if (abortErr instanceof Error && abortErr.name === "AbortError") {
|
|
114052
|
+
log("info", "\u6A21\u578B\u6062\u590D\u88AB\u4E2D\u65AD", { sessionKey });
|
|
114053
|
+
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
114054
|
+
return void 0;
|
|
114055
|
+
}
|
|
114056
|
+
throw abortErr;
|
|
114042
114057
|
}
|
|
114043
114058
|
}
|
|
114044
114059
|
const thrownError = err instanceof Error ? err.message : String(err);
|
|
114045
|
-
const errorMessage =
|
|
114060
|
+
const errorMessage = displayError?.message || thrownError;
|
|
114046
114061
|
log("error", "\u5BF9\u8BDD\u5904\u7406\u5931\u8D25", {
|
|
114047
114062
|
sessionId: session.id,
|
|
114048
114063
|
sessionKey,
|
|
114049
114064
|
chatType,
|
|
114050
114065
|
error: thrownError,
|
|
114051
|
-
...
|
|
114066
|
+
...displayError ? { sessionError: displayError.message } : {}
|
|
114052
114067
|
});
|
|
114053
114068
|
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, "\u274C " + errorMessage);
|
|
114054
114069
|
} finally {
|
|
@@ -114058,17 +114073,6 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114058
114073
|
unregisterPending(activeSessionId);
|
|
114059
114074
|
}
|
|
114060
114075
|
}
|
|
114061
|
-
async function getGlobalDefaultModel(client, directory) {
|
|
114062
|
-
const query = directory ? { directory } : void 0;
|
|
114063
|
-
const { data: config2 } = await client.config.get({ query });
|
|
114064
|
-
const model = config2?.model;
|
|
114065
|
-
if (!model || !model.includes("/")) return void 0;
|
|
114066
|
-
const slash = model.indexOf("/");
|
|
114067
|
-
const providerID = model.slice(0, slash).trim();
|
|
114068
|
-
const modelID = model.slice(slash + 1).trim();
|
|
114069
|
-
if (!providerID || !modelID) return void 0;
|
|
114070
|
-
return { providerID, modelID };
|
|
114071
|
-
}
|
|
114072
114076
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
114073
114077
|
if (messageType === "text") {
|
|
114074
114078
|
let promptText = textContent;
|
|
@@ -114083,13 +114087,6 @@ async function buildPromptParts(feishuClient, messageId, messageType, rawContent
|
|
|
114083
114087
|
}
|
|
114084
114088
|
return parts;
|
|
114085
114089
|
}
|
|
114086
|
-
var SessionErrorDetected = class extends Error {
|
|
114087
|
-
constructor(sessionError) {
|
|
114088
|
-
super(sessionError.message);
|
|
114089
|
-
this.sessionError = sessionError;
|
|
114090
|
-
this.name = "SessionErrorDetected";
|
|
114091
|
-
}
|
|
114092
|
-
};
|
|
114093
114090
|
async function pollForResponse(client, sessionId, opts) {
|
|
114094
114091
|
const { timeout, pollInterval, stablePolls, query, signal } = opts;
|
|
114095
114092
|
const start = Date.now();
|
|
@@ -114145,6 +114142,43 @@ async function replyOrUpdate(feishuClient, chatId, placeholderId, text) {
|
|
|
114145
114142
|
await sendTextMessage(feishuClient, chatId, text);
|
|
114146
114143
|
}
|
|
114147
114144
|
}
|
|
114145
|
+
var idlePatterns = [
|
|
114146
|
+
/^(无|没有)(任务|变化|进行中)/,
|
|
114147
|
+
/空闲|闲置|等待(指令|中|新|你)/,
|
|
114148
|
+
/随时可(开始|开始新)/,
|
|
114149
|
+
/等你指令/
|
|
114150
|
+
];
|
|
114151
|
+
function isIdleResponse(text, maxLength = 50) {
|
|
114152
|
+
if (text.length >= maxLength) return false;
|
|
114153
|
+
return idlePatterns.some((p) => p.test(text));
|
|
114154
|
+
}
|
|
114155
|
+
async function runOneAutoPromptIteration(apCtx, iteration, signal) {
|
|
114156
|
+
const { sessionId, chatId, deps } = apCtx;
|
|
114157
|
+
const { config: config2, client, feishuClient, log, directory } = deps;
|
|
114158
|
+
const query = directory ? { directory } : void 0;
|
|
114159
|
+
const { autoPrompt, timeout, pollInterval, stablePolls } = config2;
|
|
114160
|
+
log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey: apCtx.sessionKey, iteration });
|
|
114161
|
+
clearSessionError(sessionId);
|
|
114162
|
+
await client.session.promptAsync({
|
|
114163
|
+
path: { id: sessionId },
|
|
114164
|
+
query,
|
|
114165
|
+
body: { parts: [{ type: "text", text: autoPrompt.message }] }
|
|
114166
|
+
});
|
|
114167
|
+
const text = await pollForResponse(client, sessionId, {
|
|
114168
|
+
timeout,
|
|
114169
|
+
pollInterval,
|
|
114170
|
+
stablePolls,
|
|
114171
|
+
query,
|
|
114172
|
+
signal
|
|
114173
|
+
});
|
|
114174
|
+
if (!text) return { text: null, isIdle: false };
|
|
114175
|
+
const idle = isIdleResponse(text, autoPrompt.idleMaxLength);
|
|
114176
|
+
if (!idle) {
|
|
114177
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u54CD\u5E94", { sessionKey: apCtx.sessionKey, iteration, output: text });
|
|
114178
|
+
await sendTextMessage(feishuClient, chatId, text);
|
|
114179
|
+
}
|
|
114180
|
+
return { text, isIdle: idle };
|
|
114181
|
+
}
|
|
114148
114182
|
function abortableSleep(ms, signal) {
|
|
114149
114183
|
return new Promise((resolve, reject) => {
|
|
114150
114184
|
if (signal.aborted) {
|
|
@@ -114173,6 +114207,7 @@ function extractLastAssistantText(messages) {
|
|
|
114173
114207
|
}
|
|
114174
114208
|
|
|
114175
114209
|
// src/handler/session-queue.ts
|
|
114210
|
+
var QUEUE_MONITOR_INTERVAL_MS = 200;
|
|
114176
114211
|
var states = /* @__PURE__ */ new Map();
|
|
114177
114212
|
function getOrCreateState(sessionKey) {
|
|
114178
114213
|
const existing = states.get(sessionKey);
|
|
@@ -114191,6 +114226,9 @@ function cleanupStateIfIdle(sessionKey, state) {
|
|
|
114191
114226
|
states.delete(sessionKey);
|
|
114192
114227
|
}
|
|
114193
114228
|
}
|
|
114229
|
+
function sleep(ms) {
|
|
114230
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
114231
|
+
}
|
|
114194
114232
|
async function enqueueMessage(ctx, deps) {
|
|
114195
114233
|
if (!ctx.shouldReply) {
|
|
114196
114234
|
await handleChat(ctx, deps);
|
|
@@ -114221,7 +114259,18 @@ async function handleP2PMessage(sessionKey, ctx, deps) {
|
|
|
114221
114259
|
const controller = new AbortController();
|
|
114222
114260
|
state.controller = controller;
|
|
114223
114261
|
state.processing = true;
|
|
114224
|
-
const task =
|
|
114262
|
+
const task = (async () => {
|
|
114263
|
+
const apCtx = await processMessage(ctx, deps, controller.signal);
|
|
114264
|
+
if (apCtx) {
|
|
114265
|
+
await runP2PAutoPrompt(apCtx, controller.signal);
|
|
114266
|
+
}
|
|
114267
|
+
})().catch((err) => {
|
|
114268
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
114269
|
+
deps.log("error", "P2P \u6D88\u606F\u6216\u81EA\u52A8\u63D0\u793A\u5904\u7406\u5931\u8D25", {
|
|
114270
|
+
sessionKey,
|
|
114271
|
+
error: err instanceof Error ? err.message : String(err)
|
|
114272
|
+
});
|
|
114273
|
+
}).finally(() => {
|
|
114225
114274
|
state.processing = false;
|
|
114226
114275
|
state.controller = null;
|
|
114227
114276
|
state.currentTask = null;
|
|
@@ -114230,6 +114279,31 @@ async function handleP2PMessage(sessionKey, ctx, deps) {
|
|
|
114230
114279
|
state.currentTask = task;
|
|
114231
114280
|
await task;
|
|
114232
114281
|
}
|
|
114282
|
+
async function runP2PAutoPrompt(apCtx, signal) {
|
|
114283
|
+
const { autoPrompt } = apCtx.deps.config;
|
|
114284
|
+
if (!autoPrompt.enabled) return;
|
|
114285
|
+
let idleCount = 0;
|
|
114286
|
+
for (let i = 0; i < autoPrompt.maxIterations; i++) {
|
|
114287
|
+
await abortableSleep(autoPrompt.intervalSeconds * 1e3, signal);
|
|
114288
|
+
const result = await runOneAutoPromptIteration(apCtx, i + 1, signal);
|
|
114289
|
+
if (result.isIdle) {
|
|
114290
|
+
idleCount++;
|
|
114291
|
+
if (idleCount >= autoPrompt.idleThreshold) {
|
|
114292
|
+
apCtx.deps.log("info", "P2P \u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u68C0\u6D4B\u5230\u7A7A\u95F2\uFF09", {
|
|
114293
|
+
sessionKey: apCtx.sessionKey,
|
|
114294
|
+
iteration: i + 1,
|
|
114295
|
+
idleCount
|
|
114296
|
+
});
|
|
114297
|
+
return;
|
|
114298
|
+
}
|
|
114299
|
+
} else {
|
|
114300
|
+
idleCount = 0;
|
|
114301
|
+
}
|
|
114302
|
+
}
|
|
114303
|
+
apCtx.deps.log("info", "P2P \u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", {
|
|
114304
|
+
sessionKey: apCtx.sessionKey
|
|
114305
|
+
});
|
|
114306
|
+
}
|
|
114233
114307
|
async function handleGroupMessage(sessionKey, ctx, deps) {
|
|
114234
114308
|
const state = getOrCreateState(sessionKey);
|
|
114235
114309
|
state.queue.push({ ctx, deps });
|
|
@@ -114238,21 +114312,81 @@ async function handleGroupMessage(sessionKey, ctx, deps) {
|
|
|
114238
114312
|
}
|
|
114239
114313
|
async function drainLoop(sessionKey, state) {
|
|
114240
114314
|
state.processing = true;
|
|
114315
|
+
let autoPromptCtx;
|
|
114316
|
+
let idleCount = 0;
|
|
114317
|
+
let autoPromptIteration = 0;
|
|
114241
114318
|
try {
|
|
114242
|
-
while (
|
|
114243
|
-
|
|
114244
|
-
|
|
114245
|
-
|
|
114246
|
-
|
|
114319
|
+
while (true) {
|
|
114320
|
+
if (state.queue.length > 0) {
|
|
114321
|
+
const item = state.queue.shift();
|
|
114322
|
+
const controller = new AbortController();
|
|
114323
|
+
state.controller = controller;
|
|
114324
|
+
try {
|
|
114325
|
+
autoPromptCtx = await processMessage(item.ctx, item.deps, controller.signal);
|
|
114326
|
+
idleCount = 0;
|
|
114327
|
+
autoPromptIteration = 0;
|
|
114328
|
+
} catch (err) {
|
|
114329
|
+
item.deps.log("error", "\u7FA4\u804A\u961F\u5217\u6D88\u606F\u5904\u7406\u5931\u8D25", {
|
|
114330
|
+
sessionKey,
|
|
114331
|
+
error: err instanceof Error ? err.message : String(err)
|
|
114332
|
+
});
|
|
114333
|
+
} finally {
|
|
114334
|
+
state.controller = null;
|
|
114335
|
+
}
|
|
114336
|
+
continue;
|
|
114337
|
+
}
|
|
114338
|
+
if (!autoPromptCtx) break;
|
|
114339
|
+
const { autoPrompt } = autoPromptCtx.deps.config;
|
|
114340
|
+
if (!autoPrompt.enabled) break;
|
|
114341
|
+
if (autoPromptIteration >= autoPrompt.maxIterations) {
|
|
114342
|
+
autoPromptCtx.deps.log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", { sessionKey });
|
|
114343
|
+
break;
|
|
114344
|
+
}
|
|
114345
|
+
const intervalSeconds = autoPrompt.intervalSeconds;
|
|
114346
|
+
let interrupted = false;
|
|
114347
|
+
for (let s = 0; s < intervalSeconds; s++) {
|
|
114348
|
+
await sleep(1e3);
|
|
114349
|
+
if (state.queue.length > 0) {
|
|
114350
|
+
interrupted = true;
|
|
114351
|
+
break;
|
|
114352
|
+
}
|
|
114353
|
+
}
|
|
114354
|
+
if (interrupted) continue;
|
|
114355
|
+
const autoPromptController = new AbortController();
|
|
114356
|
+
const monitor = setInterval(() => {
|
|
114357
|
+
if (state.queue.length > 0) autoPromptController.abort();
|
|
114358
|
+
}, QUEUE_MONITOR_INTERVAL_MS);
|
|
114247
114359
|
try {
|
|
114248
|
-
await
|
|
114360
|
+
const result = await runOneAutoPromptIteration(
|
|
114361
|
+
autoPromptCtx,
|
|
114362
|
+
autoPromptIteration + 1,
|
|
114363
|
+
autoPromptController.signal
|
|
114364
|
+
);
|
|
114365
|
+
autoPromptIteration++;
|
|
114366
|
+
if (result.isIdle) {
|
|
114367
|
+
idleCount++;
|
|
114368
|
+
if (idleCount >= autoPrompt.idleThreshold) {
|
|
114369
|
+
autoPromptCtx.deps.log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u68C0\u6D4B\u5230\u7A7A\u95F2\uFF09", {
|
|
114370
|
+
sessionKey,
|
|
114371
|
+
iteration: autoPromptIteration,
|
|
114372
|
+
idleCount
|
|
114373
|
+
});
|
|
114374
|
+
break;
|
|
114375
|
+
}
|
|
114376
|
+
} else {
|
|
114377
|
+
idleCount = 0;
|
|
114378
|
+
}
|
|
114249
114379
|
} catch (err) {
|
|
114250
|
-
|
|
114380
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
114381
|
+
continue;
|
|
114382
|
+
}
|
|
114383
|
+
autoPromptCtx.deps.log("error", "\u81EA\u52A8\u63D0\u793A\u8FED\u4EE3\u5F02\u5E38", {
|
|
114251
114384
|
sessionKey,
|
|
114252
114385
|
error: err instanceof Error ? err.message : String(err)
|
|
114253
114386
|
});
|
|
114387
|
+
break;
|
|
114254
114388
|
} finally {
|
|
114255
|
-
|
|
114389
|
+
clearInterval(monitor);
|
|
114256
114390
|
}
|
|
114257
114391
|
}
|
|
114258
114392
|
} finally {
|
|
@@ -114261,7 +114395,7 @@ async function drainLoop(sessionKey, state) {
|
|
|
114261
114395
|
}
|
|
114262
114396
|
}
|
|
114263
114397
|
async function processMessage(ctx, deps, signal) {
|
|
114264
|
-
|
|
114398
|
+
return handleChat(ctx, deps, signal);
|
|
114265
114399
|
}
|
|
114266
114400
|
async function abortServerSession(sessionKey, deps) {
|
|
114267
114401
|
const { client, log, directory } = deps;
|
|
@@ -114384,21 +114518,9 @@ var FeishuPlugin = async (ctx) => {
|
|
|
114384
114518
|
});
|
|
114385
114519
|
};
|
|
114386
114520
|
const configPath = join(homedir(), ".config", "opencode", "plugins", "feishu.json");
|
|
114387
|
-
if (!existsSync(configPath)) {
|
|
114388
|
-
throw new Error(
|
|
114389
|
-
`\u7F3A\u5C11\u98DE\u4E66\u914D\u7F6E\u6587\u4EF6\uFF1A\u8BF7\u521B\u5EFA ${configPath}\uFF0C\u5185\u5BB9\u4E3A {"appId":"cli_xxx","appSecret":"xxx"}`
|
|
114390
|
-
);
|
|
114391
|
-
}
|
|
114392
114521
|
let resolvedConfig;
|
|
114393
114522
|
try {
|
|
114394
|
-
|
|
114395
|
-
JSON.parse(readFileSync(configPath, "utf-8"))
|
|
114396
|
-
);
|
|
114397
|
-
const parsed = FeishuConfigSchema.parse(raw);
|
|
114398
|
-
resolvedConfig = {
|
|
114399
|
-
...parsed,
|
|
114400
|
-
directory: expandDirectoryPath(parsed.directory ?? ctx.directory ?? "")
|
|
114401
|
-
};
|
|
114523
|
+
resolvedConfig = loadAndValidateConfig(configPath, ctx.directory ?? "");
|
|
114402
114524
|
} catch (e) {
|
|
114403
114525
|
if (e instanceof external_exports.ZodError) {
|
|
114404
114526
|
const details = e.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
@@ -114477,6 +114599,14 @@ ${details}`);
|
|
|
114477
114599
|
};
|
|
114478
114600
|
return hooks;
|
|
114479
114601
|
};
|
|
114602
|
+
function loadAndValidateConfig(configPath, ctxDirectory) {
|
|
114603
|
+
if (!existsSync(configPath)) {
|
|
114604
|
+
throw new Error(`\u7F3A\u5C11\u98DE\u4E66\u914D\u7F6E\u6587\u4EF6\uFF1A\u8BF7\u521B\u5EFA ${configPath}\uFF0C\u5185\u5BB9\u4E3A {"appId":"cli_xxx","appSecret":"xxx"}`);
|
|
114605
|
+
}
|
|
114606
|
+
const raw = resolveEnvPlaceholders(JSON.parse(readFileSync(configPath, "utf-8")));
|
|
114607
|
+
const parsed = FeishuConfigSchema.parse(raw);
|
|
114608
|
+
return { ...parsed, directory: expandDirectoryPath(parsed.directory ?? ctxDirectory ?? "") };
|
|
114609
|
+
}
|
|
114480
114610
|
function expandDirectoryPath(dir) {
|
|
114481
114611
|
if (!dir) return dir;
|
|
114482
114612
|
if (dir.startsWith("~")) {
|