opencode-feishu 1.1.0 → 1.3.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 +701 -510
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/skills/CLAUDE.md +3 -0
- package/skills/feishu-card-interaction.md +149 -0
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs, { existsSync, readFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
+
import url, { fileURLToPath } from 'url';
|
|
3
4
|
import { homedir } from 'os';
|
|
4
|
-
import crypto2 from 'crypto';
|
|
5
|
-
import url from 'url';
|
|
5
|
+
import crypto2, { randomUUID } from 'crypto';
|
|
6
6
|
import http from 'http';
|
|
7
7
|
import https from 'https';
|
|
8
8
|
import http2 from 'http2';
|
|
@@ -13,6 +13,8 @@ import { EventEmitter } from 'events';
|
|
|
13
13
|
import qs from 'querystring';
|
|
14
14
|
import WebSocket from 'ws';
|
|
15
15
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
16
|
+
import { tool } from '@opencode-ai/plugin';
|
|
17
|
+
import { createOpencodeClient } from '@opencode-ai/sdk/v2/client';
|
|
16
18
|
|
|
17
19
|
var __create = Object.create;
|
|
18
20
|
var __defProp = Object.defineProperty;
|
|
@@ -11587,7 +11589,7 @@ var require_object_inspect = __commonJS({
|
|
|
11587
11589
|
"double": /(["\\])/g,
|
|
11588
11590
|
single: /(['\\])/g
|
|
11589
11591
|
};
|
|
11590
|
-
module2.exports = function inspect_(obj, options, depth,
|
|
11592
|
+
module2.exports = function inspect_(obj, options, depth, seen) {
|
|
11591
11593
|
var opts = options || {};
|
|
11592
11594
|
if (has(opts, "quoteStyle") && !has(quotes, opts.quoteStyle)) {
|
|
11593
11595
|
throw new TypeError('option "quoteStyle" must be "single" or "double"');
|
|
@@ -11637,15 +11639,15 @@ var require_object_inspect = __commonJS({
|
|
|
11637
11639
|
return isArray2(obj) ? "[Array]" : "[Object]";
|
|
11638
11640
|
}
|
|
11639
11641
|
var indent = getIndent(opts, depth);
|
|
11640
|
-
if (typeof
|
|
11641
|
-
|
|
11642
|
-
} else if (indexOf(
|
|
11642
|
+
if (typeof seen === "undefined") {
|
|
11643
|
+
seen = [];
|
|
11644
|
+
} else if (indexOf(seen, obj) >= 0) {
|
|
11643
11645
|
return "[Circular]";
|
|
11644
11646
|
}
|
|
11645
11647
|
function inspect(value, from, noIndent) {
|
|
11646
11648
|
if (from) {
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
+
seen = $arrSlice.call(seen);
|
|
11650
|
+
seen.push(from);
|
|
11649
11651
|
}
|
|
11650
11652
|
if (noIndent) {
|
|
11651
11653
|
var newOpts = {
|
|
@@ -11654,9 +11656,9 @@ var require_object_inspect = __commonJS({
|
|
|
11654
11656
|
if (has(opts, "quoteStyle")) {
|
|
11655
11657
|
newOpts.quoteStyle = opts.quoteStyle;
|
|
11656
11658
|
}
|
|
11657
|
-
return inspect_(value, newOpts, depth + 1,
|
|
11659
|
+
return inspect_(value, newOpts, depth + 1, seen);
|
|
11658
11660
|
}
|
|
11659
|
-
return inspect_(value, opts, depth + 1,
|
|
11661
|
+
return inspect_(value, opts, depth + 1, seen);
|
|
11660
11662
|
}
|
|
11661
11663
|
if (typeof obj === "function" && !isRegExp2(obj)) {
|
|
11662
11664
|
var name = nameOf(obj);
|
|
@@ -13718,7 +13720,7 @@ var require_lodash2 = __commonJS({
|
|
|
13718
13720
|
if (stacked && stack.get(other)) {
|
|
13719
13721
|
return stacked == other;
|
|
13720
13722
|
}
|
|
13721
|
-
var index = -1, result = true,
|
|
13723
|
+
var index = -1, result = true, seen = bitmask & UNORDERED_COMPARE_FLAG ? new SetCache() : void 0;
|
|
13722
13724
|
stack.set(array2, other);
|
|
13723
13725
|
stack.set(other, array2);
|
|
13724
13726
|
while (++index < arrLength) {
|
|
@@ -13733,10 +13735,10 @@ var require_lodash2 = __commonJS({
|
|
|
13733
13735
|
result = false;
|
|
13734
13736
|
break;
|
|
13735
13737
|
}
|
|
13736
|
-
if (
|
|
13738
|
+
if (seen) {
|
|
13737
13739
|
if (!arraySome(other, function(othValue2, othIndex) {
|
|
13738
|
-
if (!
|
|
13739
|
-
return
|
|
13740
|
+
if (!seen.has(othIndex) && (arrValue === othValue2 || equalFunc(arrValue, othValue2, customizer, bitmask, stack))) {
|
|
13741
|
+
return seen.add(othIndex);
|
|
13740
13742
|
}
|
|
13741
13743
|
})) {
|
|
13742
13744
|
result = false;
|
|
@@ -109648,14 +109650,14 @@ function initializeContext(params) {
|
|
|
109648
109650
|
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
109649
109651
|
var _a2;
|
|
109650
109652
|
const def = schema._zod.def;
|
|
109651
|
-
const
|
|
109652
|
-
if (
|
|
109653
|
-
|
|
109653
|
+
const seen = ctx.seen.get(schema);
|
|
109654
|
+
if (seen) {
|
|
109655
|
+
seen.count++;
|
|
109654
109656
|
const isCycle = _params.schemaPath.includes(schema);
|
|
109655
109657
|
if (isCycle) {
|
|
109656
|
-
|
|
109658
|
+
seen.cycle = _params.path;
|
|
109657
109659
|
}
|
|
109658
|
-
return
|
|
109660
|
+
return seen.schema;
|
|
109659
109661
|
}
|
|
109660
109662
|
const result = { schema: {}, count: 1, cycle: void 0, path: _params.path };
|
|
109661
109663
|
ctx.seen.set(schema, result);
|
|
@@ -109738,12 +109740,12 @@ function extractDefs(ctx, schema) {
|
|
|
109738
109740
|
if (entry[1].schema.$ref) {
|
|
109739
109741
|
return;
|
|
109740
109742
|
}
|
|
109741
|
-
const
|
|
109743
|
+
const seen = entry[1];
|
|
109742
109744
|
const { ref, defId } = makeURI(entry);
|
|
109743
|
-
|
|
109745
|
+
seen.def = { ...seen.schema };
|
|
109744
109746
|
if (defId)
|
|
109745
|
-
|
|
109746
|
-
const schema2 =
|
|
109747
|
+
seen.defId = defId;
|
|
109748
|
+
const schema2 = seen.schema;
|
|
109747
109749
|
for (const key in schema2) {
|
|
109748
109750
|
delete schema2[key];
|
|
109749
109751
|
}
|
|
@@ -109751,16 +109753,16 @@ function extractDefs(ctx, schema) {
|
|
|
109751
109753
|
};
|
|
109752
109754
|
if (ctx.cycles === "throw") {
|
|
109753
109755
|
for (const entry of ctx.seen.entries()) {
|
|
109754
|
-
const
|
|
109755
|
-
if (
|
|
109756
|
-
throw new Error(`Cycle detected: #/${
|
|
109756
|
+
const seen = entry[1];
|
|
109757
|
+
if (seen.cycle) {
|
|
109758
|
+
throw new Error(`Cycle detected: #/${seen.cycle?.join("/")}/<root>
|
|
109757
109759
|
|
|
109758
109760
|
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);
|
|
109759
109761
|
}
|
|
109760
109762
|
}
|
|
109761
109763
|
}
|
|
109762
109764
|
for (const entry of ctx.seen.entries()) {
|
|
109763
|
-
const
|
|
109765
|
+
const seen = entry[1];
|
|
109764
109766
|
if (schema === entry[0]) {
|
|
109765
109767
|
extractToDef(entry);
|
|
109766
109768
|
continue;
|
|
@@ -109777,11 +109779,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
109777
109779
|
extractToDef(entry);
|
|
109778
109780
|
continue;
|
|
109779
109781
|
}
|
|
109780
|
-
if (
|
|
109782
|
+
if (seen.cycle) {
|
|
109781
109783
|
extractToDef(entry);
|
|
109782
109784
|
continue;
|
|
109783
109785
|
}
|
|
109784
|
-
if (
|
|
109786
|
+
if (seen.count > 1) {
|
|
109785
109787
|
if (ctx.reused === "ref") {
|
|
109786
109788
|
extractToDef(entry);
|
|
109787
109789
|
continue;
|
|
@@ -109794,13 +109796,13 @@ function finalize(ctx, schema) {
|
|
|
109794
109796
|
if (!root)
|
|
109795
109797
|
throw new Error("Unprocessed schema. This is a bug in Zod.");
|
|
109796
109798
|
const flattenRef = (zodSchema) => {
|
|
109797
|
-
const
|
|
109798
|
-
if (
|
|
109799
|
+
const seen = ctx.seen.get(zodSchema);
|
|
109800
|
+
if (seen.ref === null)
|
|
109799
109801
|
return;
|
|
109800
|
-
const schema2 =
|
|
109802
|
+
const schema2 = seen.def ?? seen.schema;
|
|
109801
109803
|
const _cached = { ...schema2 };
|
|
109802
|
-
const ref =
|
|
109803
|
-
|
|
109804
|
+
const ref = seen.ref;
|
|
109805
|
+
seen.ref = null;
|
|
109804
109806
|
if (ref) {
|
|
109805
109807
|
flattenRef(ref);
|
|
109806
109808
|
const refSeen = ctx.seen.get(ref);
|
|
@@ -109852,7 +109854,7 @@ function finalize(ctx, schema) {
|
|
|
109852
109854
|
ctx.override({
|
|
109853
109855
|
zodSchema,
|
|
109854
109856
|
jsonSchema: schema2,
|
|
109855
|
-
path:
|
|
109857
|
+
path: seen.path ?? []
|
|
109856
109858
|
});
|
|
109857
109859
|
};
|
|
109858
109860
|
for (const entry of [...ctx.seen.entries()].reverse()) {
|
|
@@ -109875,9 +109877,9 @@ function finalize(ctx, schema) {
|
|
|
109875
109877
|
Object.assign(result, root.def ?? root.schema);
|
|
109876
109878
|
const defs = ctx.external?.defs ?? {};
|
|
109877
109879
|
for (const entry of ctx.seen.entries()) {
|
|
109878
|
-
const
|
|
109879
|
-
if (
|
|
109880
|
-
defs[
|
|
109880
|
+
const seen = entry[1];
|
|
109881
|
+
if (seen.def && seen.defId) {
|
|
109882
|
+
defs[seen.defId] = seen.def;
|
|
109881
109883
|
}
|
|
109882
109884
|
}
|
|
109883
109885
|
if (ctx.external) ; else {
|
|
@@ -110371,9 +110373,9 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
110371
110373
|
var nullableProcessor = (schema, ctx, json2, params) => {
|
|
110372
110374
|
const def = schema._zod.def;
|
|
110373
110375
|
const inner = process2(def.innerType, ctx, params);
|
|
110374
|
-
const
|
|
110376
|
+
const seen = ctx.seen.get(schema);
|
|
110375
110377
|
if (ctx.target === "openapi-3.0") {
|
|
110376
|
-
|
|
110378
|
+
seen.ref = def.innerType;
|
|
110377
110379
|
json2.nullable = true;
|
|
110378
110380
|
} else {
|
|
110379
110381
|
json2.anyOf = [inner, { type: "null" }];
|
|
@@ -110382,29 +110384,29 @@ var nullableProcessor = (schema, ctx, json2, params) => {
|
|
|
110382
110384
|
var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
110383
110385
|
const def = schema._zod.def;
|
|
110384
110386
|
process2(def.innerType, ctx, params);
|
|
110385
|
-
const
|
|
110386
|
-
|
|
110387
|
+
const seen = ctx.seen.get(schema);
|
|
110388
|
+
seen.ref = def.innerType;
|
|
110387
110389
|
};
|
|
110388
110390
|
var defaultProcessor = (schema, ctx, json2, params) => {
|
|
110389
110391
|
const def = schema._zod.def;
|
|
110390
110392
|
process2(def.innerType, ctx, params);
|
|
110391
|
-
const
|
|
110392
|
-
|
|
110393
|
+
const seen = ctx.seen.get(schema);
|
|
110394
|
+
seen.ref = def.innerType;
|
|
110393
110395
|
json2.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
110394
110396
|
};
|
|
110395
110397
|
var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
110396
110398
|
const def = schema._zod.def;
|
|
110397
110399
|
process2(def.innerType, ctx, params);
|
|
110398
|
-
const
|
|
110399
|
-
|
|
110400
|
+
const seen = ctx.seen.get(schema);
|
|
110401
|
+
seen.ref = def.innerType;
|
|
110400
110402
|
if (ctx.io === "input")
|
|
110401
110403
|
json2._prefault = JSON.parse(JSON.stringify(def.defaultValue));
|
|
110402
110404
|
};
|
|
110403
110405
|
var catchProcessor = (schema, ctx, json2, params) => {
|
|
110404
110406
|
const def = schema._zod.def;
|
|
110405
110407
|
process2(def.innerType, ctx, params);
|
|
110406
|
-
const
|
|
110407
|
-
|
|
110408
|
+
const seen = ctx.seen.get(schema);
|
|
110409
|
+
seen.ref = def.innerType;
|
|
110408
110410
|
let catchValue;
|
|
110409
110411
|
try {
|
|
110410
110412
|
catchValue = def.catchValue(void 0);
|
|
@@ -110417,33 +110419,33 @@ var pipeProcessor = (schema, ctx, _json, params) => {
|
|
|
110417
110419
|
const def = schema._zod.def;
|
|
110418
110420
|
const innerType = ctx.io === "input" ? def.in._zod.def.type === "transform" ? def.out : def.in : def.out;
|
|
110419
110421
|
process2(innerType, ctx, params);
|
|
110420
|
-
const
|
|
110421
|
-
|
|
110422
|
+
const seen = ctx.seen.get(schema);
|
|
110423
|
+
seen.ref = innerType;
|
|
110422
110424
|
};
|
|
110423
110425
|
var readonlyProcessor = (schema, ctx, json2, params) => {
|
|
110424
110426
|
const def = schema._zod.def;
|
|
110425
110427
|
process2(def.innerType, ctx, params);
|
|
110426
|
-
const
|
|
110427
|
-
|
|
110428
|
+
const seen = ctx.seen.get(schema);
|
|
110429
|
+
seen.ref = def.innerType;
|
|
110428
110430
|
json2.readOnly = true;
|
|
110429
110431
|
};
|
|
110430
110432
|
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
110431
110433
|
const def = schema._zod.def;
|
|
110432
110434
|
process2(def.innerType, ctx, params);
|
|
110433
|
-
const
|
|
110434
|
-
|
|
110435
|
+
const seen = ctx.seen.get(schema);
|
|
110436
|
+
seen.ref = def.innerType;
|
|
110435
110437
|
};
|
|
110436
110438
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
110437
110439
|
const def = schema._zod.def;
|
|
110438
110440
|
process2(def.innerType, ctx, params);
|
|
110439
|
-
const
|
|
110440
|
-
|
|
110441
|
+
const seen = ctx.seen.get(schema);
|
|
110442
|
+
seen.ref = def.innerType;
|
|
110441
110443
|
};
|
|
110442
110444
|
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
110443
110445
|
const innerType = schema._zod.innerType;
|
|
110444
110446
|
process2(innerType, ctx, params);
|
|
110445
|
-
const
|
|
110446
|
-
|
|
110447
|
+
const seen = ctx.seen.get(schema);
|
|
110448
|
+
seen.ref = innerType;
|
|
110447
110449
|
};
|
|
110448
110450
|
var allProcessors = {
|
|
110449
110451
|
string: stringProcessor,
|
|
@@ -112507,8 +112509,8 @@ config(en_default());
|
|
|
112507
112509
|
// src/types.ts
|
|
112508
112510
|
var AutoPromptSchema = external_exports.object({
|
|
112509
112511
|
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
|
+
intervalSeconds: external_exports.number().int().positive().max(300).default(30),
|
|
112513
|
+
maxIterations: external_exports.number().int().positive().max(100).default(10),
|
|
112512
112514
|
message: external_exports.string().min(1).default("\u8BF7\u540C\u6B65\u5F53\u524D\u8FDB\u5EA6\uFF0C\u5982\u9700\u5E2E\u52A9\u8BF7\u8BF4\u660E"),
|
|
112513
112515
|
idleThreshold: external_exports.number().int().min(1).default(2),
|
|
112514
112516
|
idleMaxLength: external_exports.number().int().min(10).default(50)
|
|
@@ -112516,10 +112518,10 @@ var AutoPromptSchema = external_exports.object({
|
|
|
112516
112518
|
var FeishuConfigSchema = external_exports.object({
|
|
112517
112519
|
appId: external_exports.string().min(1, "appId \u4E0D\u80FD\u4E3A\u7A7A"),
|
|
112518
112520
|
appSecret: external_exports.string().min(1, "appSecret \u4E0D\u80FD\u4E3A\u7A7A"),
|
|
112519
|
-
timeout: external_exports.number().int().positive().default(12e4),
|
|
112521
|
+
timeout: external_exports.number().int().positive().max(6e5).default(12e4),
|
|
112520
112522
|
thinkingDelay: external_exports.number().int().nonnegative().default(2500),
|
|
112521
112523
|
logLevel: external_exports.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
|
|
112522
|
-
maxHistoryMessages: external_exports.number().int().positive().default(200),
|
|
112524
|
+
maxHistoryMessages: external_exports.number().int().positive().max(500).default(200),
|
|
112523
112525
|
pollInterval: external_exports.number().int().positive().default(1e3),
|
|
112524
112526
|
stablePolls: external_exports.number().int().positive().default(3),
|
|
112525
112527
|
dedupTtl: external_exports.number().int().positive().default(10 * 60 * 1e3),
|
|
@@ -112623,128 +112625,86 @@ var CardKitClient = class {
|
|
|
112623
112625
|
}
|
|
112624
112626
|
};
|
|
112625
112627
|
|
|
112626
|
-
// src/
|
|
112627
|
-
|
|
112628
|
-
|
|
112629
|
-
|
|
112630
|
-
|
|
112631
|
-
|
|
112632
|
-
|
|
112633
|
-
|
|
112634
|
-
data
|
|
112635
|
-
|
|
112636
|
-
|
|
112637
|
-
|
|
112638
|
-
|
|
112639
|
-
|
|
112640
|
-
|
|
112641
|
-
|
|
112642
|
-
|
|
112643
|
-
|
|
112644
|
-
|
|
112645
|
-
|
|
112646
|
-
|
|
112647
|
-
|
|
112648
|
-
|
|
112649
|
-
|
|
112650
|
-
|
|
112651
|
-
|
|
112652
|
-
|
|
112653
|
-
|
|
112654
|
-
text: { tag: "plain_text", content: "\u2705 \u5141\u8BB8\u4E00\u6B21" },
|
|
112655
|
-
type: "primary",
|
|
112656
|
-
value: JSON.stringify({ action: "permission_reply", requestId, reply: "once" })
|
|
112657
|
-
},
|
|
112658
|
-
{
|
|
112659
|
-
tag: "button",
|
|
112660
|
-
text: { tag: "plain_text", content: "\u{1F513} \u59CB\u7EC8\u5141\u8BB8" },
|
|
112661
|
-
type: "default",
|
|
112662
|
-
value: JSON.stringify({ action: "permission_reply", requestId, reply: "always" })
|
|
112663
|
-
},
|
|
112664
|
-
{
|
|
112665
|
-
tag: "button",
|
|
112666
|
-
text: { tag: "plain_text", content: "\u274C \u62D2\u7EDD" },
|
|
112667
|
-
type: "danger",
|
|
112668
|
-
value: JSON.stringify({ action: "permission_reply", requestId, reply: "reject" })
|
|
112669
|
-
}
|
|
112670
|
-
]
|
|
112671
|
-
}
|
|
112672
|
-
]
|
|
112673
|
-
}
|
|
112628
|
+
// src/utils/ttl-map.ts
|
|
112629
|
+
var TtlMap = class {
|
|
112630
|
+
constructor(defaultTtlMs) {
|
|
112631
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
112632
|
+
}
|
|
112633
|
+
data = /* @__PURE__ */ new Map();
|
|
112634
|
+
timers = /* @__PURE__ */ new Map();
|
|
112635
|
+
get(key) {
|
|
112636
|
+
return this.data.get(key);
|
|
112637
|
+
}
|
|
112638
|
+
has(key) {
|
|
112639
|
+
return this.data.has(key);
|
|
112640
|
+
}
|
|
112641
|
+
set(key, value, ttlMs) {
|
|
112642
|
+
this.delete(key);
|
|
112643
|
+
this.data.set(key, value);
|
|
112644
|
+
const timer = setTimeout(() => {
|
|
112645
|
+
this.data.delete(key);
|
|
112646
|
+
this.timers.delete(key);
|
|
112647
|
+
}, ttlMs ?? this.defaultTtlMs);
|
|
112648
|
+
timer.unref();
|
|
112649
|
+
this.timers.set(key, timer);
|
|
112650
|
+
}
|
|
112651
|
+
delete(key) {
|
|
112652
|
+
const timer = this.timers.get(key);
|
|
112653
|
+
if (timer) {
|
|
112654
|
+
clearTimeout(timer);
|
|
112655
|
+
this.timers.delete(key);
|
|
112674
112656
|
}
|
|
112675
|
-
|
|
112657
|
+
this.data.delete(key);
|
|
112658
|
+
}
|
|
112659
|
+
};
|
|
112660
|
+
|
|
112661
|
+
// src/feishu/session-chat-map.ts
|
|
112662
|
+
var sessionToChat = new TtlMap(24 * 60 * 60 * 1e3);
|
|
112663
|
+
function registerSessionChat(sessionId, chatId, chatType) {
|
|
112664
|
+
sessionToChat.set(sessionId, { chatId, chatType });
|
|
112676
112665
|
}
|
|
112677
|
-
function
|
|
112678
|
-
|
|
112679
|
-
|
|
112680
|
-
|
|
112681
|
-
|
|
112682
|
-
const questionText = String(q?.question ?? "\u8BF7\u9009\u62E9");
|
|
112683
|
-
const options = Array.isArray(q?.options) ? q.options : [];
|
|
112684
|
-
const buttons = options.map((opt, idx) => ({
|
|
112685
|
-
tag: "button",
|
|
112686
|
-
text: { tag: "plain_text", content: String(opt.label ?? opt.value ?? `\u9009\u9879 ${idx + 1}`) },
|
|
112687
|
-
type: idx === 0 ? "primary" : "default",
|
|
112688
|
-
value: JSON.stringify({
|
|
112689
|
-
action: "question_reply",
|
|
112690
|
-
requestId,
|
|
112691
|
-
answers: [[String(opt.value ?? opt.label ?? "")]]
|
|
112692
|
-
})
|
|
112693
|
-
}));
|
|
112694
|
-
return {
|
|
112695
|
-
type: "card_kit",
|
|
112696
|
-
data: {
|
|
112697
|
-
schema: "2.0",
|
|
112698
|
-
config: { wide_screen_mode: true },
|
|
112699
|
-
header: {
|
|
112700
|
-
title: { tag: "plain_text", content: header },
|
|
112701
|
-
template: "blue"
|
|
112702
|
-
},
|
|
112703
|
-
body: {
|
|
112704
|
-
elements: [
|
|
112705
|
-
{
|
|
112706
|
-
tag: "markdown",
|
|
112707
|
-
content: questionText
|
|
112708
|
-
},
|
|
112709
|
-
...buttons.length > 0 ? [{ tag: "action", actions: buttons }] : []
|
|
112710
|
-
]
|
|
112711
|
-
}
|
|
112712
|
-
}
|
|
112713
|
-
};
|
|
112666
|
+
function getChatIdBySession(sessionId) {
|
|
112667
|
+
return sessionToChat.get(sessionId)?.chatId;
|
|
112668
|
+
}
|
|
112669
|
+
function getChatInfoBySession(sessionId) {
|
|
112670
|
+
return sessionToChat.get(sessionId);
|
|
112714
112671
|
}
|
|
112715
112672
|
|
|
112716
112673
|
// src/feishu/sender.ts
|
|
112674
|
+
async function wrapSendCall(fn, idExtractor = (res) => res?.data?.message_id ?? "") {
|
|
112675
|
+
try {
|
|
112676
|
+
const res = await fn();
|
|
112677
|
+
return { ok: true, messageId: idExtractor(res) };
|
|
112678
|
+
} catch (err) {
|
|
112679
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112680
|
+
}
|
|
112681
|
+
}
|
|
112717
112682
|
async function sendTextMessage(client, chatId, text) {
|
|
112718
112683
|
if (!chatId?.trim()) {
|
|
112719
112684
|
return { ok: false, error: "No chat_id provided" };
|
|
112720
112685
|
}
|
|
112721
|
-
|
|
112722
|
-
|
|
112686
|
+
return wrapSendCall(
|
|
112687
|
+
() => client.im.message.create({
|
|
112723
112688
|
params: { receive_id_type: "chat_id" },
|
|
112724
112689
|
data: {
|
|
112725
112690
|
receive_id: chatId.trim(),
|
|
112726
112691
|
msg_type: "text",
|
|
112727
112692
|
content: JSON.stringify({ text })
|
|
112728
112693
|
}
|
|
112729
|
-
})
|
|
112730
|
-
|
|
112731
|
-
} catch (err) {
|
|
112732
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112733
|
-
}
|
|
112694
|
+
})
|
|
112695
|
+
);
|
|
112734
112696
|
}
|
|
112735
112697
|
async function updateMessage(client, messageId, text) {
|
|
112736
|
-
|
|
112737
|
-
|
|
112698
|
+
return wrapSendCall(
|
|
112699
|
+
() => client.im.message.update({
|
|
112738
112700
|
path: { message_id: messageId },
|
|
112739
112701
|
data: {
|
|
112740
112702
|
msg_type: "text",
|
|
112741
112703
|
content: JSON.stringify({ text })
|
|
112742
112704
|
}
|
|
112743
|
-
})
|
|
112744
|
-
|
|
112745
|
-
|
|
112746
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112747
|
-
}
|
|
112705
|
+
}),
|
|
112706
|
+
() => messageId
|
|
112707
|
+
);
|
|
112748
112708
|
}
|
|
112749
112709
|
async function deleteMessage(client, messageId) {
|
|
112750
112710
|
try {
|
|
@@ -112756,59 +112716,193 @@ async function sendInteractiveCard(client, chatId, card) {
|
|
|
112756
112716
|
if (!chatId?.trim()) {
|
|
112757
112717
|
return { ok: false, error: "No chat_id provided" };
|
|
112758
112718
|
}
|
|
112759
|
-
|
|
112760
|
-
|
|
112719
|
+
return wrapSendCall(
|
|
112720
|
+
() => client.im.message.create({
|
|
112761
112721
|
params: { receive_id_type: "chat_id" },
|
|
112762
112722
|
data: {
|
|
112763
112723
|
receive_id: chatId.trim(),
|
|
112764
112724
|
msg_type: "interactive",
|
|
112765
112725
|
content: JSON.stringify(card)
|
|
112766
112726
|
}
|
|
112767
|
-
})
|
|
112768
|
-
|
|
112769
|
-
} catch (err) {
|
|
112770
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
112771
|
-
}
|
|
112727
|
+
})
|
|
112728
|
+
);
|
|
112772
112729
|
}
|
|
112773
112730
|
async function sendCardMessage(client, chatId, cardId) {
|
|
112774
112731
|
if (!chatId?.trim()) {
|
|
112775
112732
|
return { ok: false, error: "No chat_id provided" };
|
|
112776
112733
|
}
|
|
112777
|
-
|
|
112778
|
-
|
|
112734
|
+
return wrapSendCall(
|
|
112735
|
+
() => client.im.message.create({
|
|
112779
112736
|
params: { receive_id_type: "chat_id" },
|
|
112780
112737
|
data: {
|
|
112781
112738
|
receive_id: chatId.trim(),
|
|
112782
112739
|
msg_type: "interactive",
|
|
112783
112740
|
content: JSON.stringify({ type: "card_kit", data: { card_id: cardId } })
|
|
112784
112741
|
}
|
|
112785
|
-
})
|
|
112786
|
-
|
|
112787
|
-
|
|
112788
|
-
|
|
112742
|
+
})
|
|
112743
|
+
);
|
|
112744
|
+
}
|
|
112745
|
+
|
|
112746
|
+
// src/feishu/markdown.ts
|
|
112747
|
+
var MAX_CARD_BYTES = 28 * 1024;
|
|
112748
|
+
var TRUNCATION_SUFFIX = "\n\n*\u5185\u5BB9\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD*";
|
|
112749
|
+
var TRUNCATION_SUFFIX_BYTES = new TextEncoder().encode(TRUNCATION_SUFFIX).length;
|
|
112750
|
+
var CODE_FENCE_BYTES = 4;
|
|
112751
|
+
var HTML_TAG_RE = /<\/?\w+(?:\s[^>]*)?\/?>/g;
|
|
112752
|
+
function cleanMarkdown(text) {
|
|
112753
|
+
let result = text.replace(/<br\s*\/?>/gi, "\n");
|
|
112754
|
+
const { segments, codeBlocks } = extractCodeBlocks(result);
|
|
112755
|
+
result = segments.map((seg) => seg.replace(HTML_TAG_RE, "")).join("\0");
|
|
112756
|
+
let idx = 0;
|
|
112757
|
+
result = result.replace(/\0/g, () => codeBlocks[idx++] ?? "");
|
|
112758
|
+
result = closeCodeBlocks(result);
|
|
112759
|
+
return result;
|
|
112760
|
+
}
|
|
112761
|
+
function truncateMarkdown(text, limit = MAX_CARD_BYTES) {
|
|
112762
|
+
const bytes = new TextEncoder().encode(text);
|
|
112763
|
+
if (bytes.length <= limit) return text;
|
|
112764
|
+
const effectiveLimit = limit - TRUNCATION_SUFFIX_BYTES - CODE_FENCE_BYTES;
|
|
112765
|
+
if (effectiveLimit <= 0) return TRUNCATION_SUFFIX;
|
|
112766
|
+
const truncated = new TextDecoder().decode(bytes.slice(0, effectiveLimit));
|
|
112767
|
+
const lastNewline = truncated.lastIndexOf("\n");
|
|
112768
|
+
const cutPoint = lastNewline > effectiveLimit * 0.8 ? lastNewline : truncated.length;
|
|
112769
|
+
let result = truncated.slice(0, cutPoint);
|
|
112770
|
+
result = closeCodeBlocks(result);
|
|
112771
|
+
return result + TRUNCATION_SUFFIX;
|
|
112772
|
+
}
|
|
112773
|
+
function extractCodeBlocks(text) {
|
|
112774
|
+
const segments = [];
|
|
112775
|
+
const codeBlocks = [];
|
|
112776
|
+
const re = /```[\s\S]*?```/g;
|
|
112777
|
+
let lastIndex = 0;
|
|
112778
|
+
let match;
|
|
112779
|
+
while ((match = re.exec(text)) !== null) {
|
|
112780
|
+
segments.push(text.slice(lastIndex, match.index));
|
|
112781
|
+
codeBlocks.push(match[0]);
|
|
112782
|
+
lastIndex = match.index + match[0].length;
|
|
112789
112783
|
}
|
|
112784
|
+
segments.push(text.slice(lastIndex));
|
|
112785
|
+
return { segments, codeBlocks };
|
|
112786
|
+
}
|
|
112787
|
+
function closeCodeBlocks(text) {
|
|
112788
|
+
const matches = text.match(/```/g);
|
|
112789
|
+
if (matches && matches.length % 2 !== 0) {
|
|
112790
|
+
return text + "\n```";
|
|
112791
|
+
}
|
|
112792
|
+
return text;
|
|
112793
|
+
}
|
|
112794
|
+
|
|
112795
|
+
// src/tools/send-card.ts
|
|
112796
|
+
var z2 = tool.schema;
|
|
112797
|
+
var TEMPLATE_COLORS = ["blue", "green", "orange", "red", "purple", "grey"];
|
|
112798
|
+
function createSendCardTool(deps) {
|
|
112799
|
+
return tool({
|
|
112800
|
+
description: "\u53D1\u9001\u683C\u5F0F\u5316\u5361\u7247\u6D88\u606F\u5230\u5F53\u524D\u98DE\u4E66\u4F1A\u8BDD\u3002\u652F\u6301 markdown \u6B63\u6587\u3001\u5206\u5272\u7EBF\u3001\u5907\u6CE8\u548C\u4EA4\u4E92\u6309\u94AE\u3002\u6309\u94AE\u70B9\u51FB\u7B49\u540C\u7528\u6237\u53D1\u9001\u6D88\u606F\u3002\u5361\u7247\u4F5C\u4E3A\u72EC\u7ACB\u6D88\u606F\u53D1\u9001\uFF0C\u4E0D\u5F71\u54CD\u6D41\u5F0F\u56DE\u590D\u3002",
|
|
112801
|
+
args: {
|
|
112802
|
+
title: z2.string().describe("\u5361\u7247\u6807\u9898"),
|
|
112803
|
+
template: z2.enum(TEMPLATE_COLORS).default("blue").describe("\u6807\u9898\u989C\u8272\u4E3B\u9898"),
|
|
112804
|
+
sections: z2.array(
|
|
112805
|
+
z2.object({
|
|
112806
|
+
type: z2.enum(["markdown", "divider", "note", "actions"]).default("markdown").describe("\u533A\u5757\u7C7B\u578B\uFF1Amarkdown\uFF08\u6B63\u6587\uFF09\u3001divider\uFF08\u5206\u5272\u7EBF\uFF09\u3001note\uFF08\u5907\u6CE8\uFF09\u3001actions\uFF08\u6309\u94AE\u7EC4\uFF09"),
|
|
112807
|
+
content: z2.string().optional().describe("\u533A\u5757\u5185\u5BB9\uFF08markdown \u683C\u5F0F\uFF0Cdivider/actions \u7C7B\u578B\u65E0\u9700\u6B64\u5B57\u6BB5\uFF09"),
|
|
112808
|
+
buttons: z2.array(
|
|
112809
|
+
z2.object({
|
|
112810
|
+
text: z2.string().describe("\u6309\u94AE\u663E\u793A\u6587\u672C\uFF082-6\u5B57\uFF09"),
|
|
112811
|
+
value: z2.string().describe("\u70B9\u51FB\u540E\u4F5C\u4E3A\u7528\u6237\u6D88\u606F\u53D1\u9001\u7684\u5185\u5BB9"),
|
|
112812
|
+
style: z2.enum(["primary", "default", "danger"]).default("default").describe("\u6309\u94AE\u6837\u5F0F")
|
|
112813
|
+
})
|
|
112814
|
+
).optional().describe("\u6309\u94AE\u5217\u8868\uFF08\u4EC5 actions \u7C7B\u578B\u4F7F\u7528\uFF09")
|
|
112815
|
+
})
|
|
112816
|
+
).min(1).describe("\u5361\u7247\u6B63\u6587\u533A\u5757\u5217\u8868")
|
|
112817
|
+
},
|
|
112818
|
+
async execute(args, context) {
|
|
112819
|
+
const chatId = getChatIdBySession(context.sessionID);
|
|
112820
|
+
if (!chatId) {
|
|
112821
|
+
return "\u9519\u8BEF\uFF1A\u5F53\u524D\u4F1A\u8BDD\u4E0D\u5173\u8054\u98DE\u4E66\u804A\u5929\uFF0C\u65E0\u6CD5\u53D1\u9001\u5361\u7247";
|
|
112822
|
+
}
|
|
112823
|
+
const chatInfo = getChatInfoBySession(context.sessionID);
|
|
112824
|
+
const card = buildCardFromDSL(args, chatId, chatInfo?.chatType ?? "p2p");
|
|
112825
|
+
const result = await sendInteractiveCard(deps.feishuClient, chatId, card);
|
|
112826
|
+
if (result.ok) {
|
|
112827
|
+
deps.log("info", "Agent \u5361\u7247\u5DF2\u53D1\u9001", {
|
|
112828
|
+
sessionId: context.sessionID,
|
|
112829
|
+
chatId,
|
|
112830
|
+
title: args.title,
|
|
112831
|
+
messageId: result.messageId
|
|
112832
|
+
});
|
|
112833
|
+
return `\u5361\u7247\u5DF2\u53D1\u9001\uFF1A\u300C${args.title}\u300D`;
|
|
112834
|
+
}
|
|
112835
|
+
deps.log("warn", "Agent \u5361\u7247\u53D1\u9001\u5931\u8D25", {
|
|
112836
|
+
sessionId: context.sessionID,
|
|
112837
|
+
chatId,
|
|
112838
|
+
title: args.title,
|
|
112839
|
+
error: result.error
|
|
112840
|
+
});
|
|
112841
|
+
return `\u5361\u7247\u53D1\u9001\u5931\u8D25\uFF1A${result.error}`;
|
|
112842
|
+
}
|
|
112843
|
+
});
|
|
112844
|
+
}
|
|
112845
|
+
function buildCardFromDSL(args, chatId, chatType) {
|
|
112846
|
+
return {
|
|
112847
|
+
schema: "2.0",
|
|
112848
|
+
config: { wide_screen_mode: true },
|
|
112849
|
+
header: {
|
|
112850
|
+
title: { tag: "plain_text", content: args.title },
|
|
112851
|
+
template: args.template
|
|
112852
|
+
},
|
|
112853
|
+
body: {
|
|
112854
|
+
elements: args.sections.map((s) => {
|
|
112855
|
+
switch (s.type) {
|
|
112856
|
+
case "divider":
|
|
112857
|
+
return { tag: "hr" };
|
|
112858
|
+
case "note":
|
|
112859
|
+
return {
|
|
112860
|
+
tag: "note",
|
|
112861
|
+
elements: [{ tag: "plain_text", content: s.content ?? "" }]
|
|
112862
|
+
};
|
|
112863
|
+
case "actions":
|
|
112864
|
+
if (!s.buttons?.length) return null;
|
|
112865
|
+
return {
|
|
112866
|
+
tag: "action",
|
|
112867
|
+
actions: s.buttons.map((btn) => ({
|
|
112868
|
+
tag: "button",
|
|
112869
|
+
text: { tag: "plain_text", content: btn.text },
|
|
112870
|
+
type: btn.style,
|
|
112871
|
+
value: JSON.stringify(btn.actionPayload ?? {
|
|
112872
|
+
action: "send_message",
|
|
112873
|
+
chatId,
|
|
112874
|
+
chatType,
|
|
112875
|
+
text: btn.value
|
|
112876
|
+
})
|
|
112877
|
+
}))
|
|
112878
|
+
};
|
|
112879
|
+
case "markdown":
|
|
112880
|
+
default:
|
|
112881
|
+
return {
|
|
112882
|
+
tag: "markdown",
|
|
112883
|
+
content: truncateMarkdown(s.content ?? "", 28e3)
|
|
112884
|
+
};
|
|
112885
|
+
}
|
|
112886
|
+
}).filter(Boolean)
|
|
112887
|
+
}
|
|
112888
|
+
};
|
|
112790
112889
|
}
|
|
112791
112890
|
|
|
112792
112891
|
// src/handler/interactive.ts
|
|
112793
|
-
var
|
|
112794
|
-
var seenRequestIds = /* @__PURE__ */ new Map();
|
|
112892
|
+
var seenIds = new TtlMap(10 * 60 * 1e3);
|
|
112795
112893
|
function markSeen(requestId) {
|
|
112796
|
-
|
|
112797
|
-
|
|
112798
|
-
if (now - ts > SEEN_TTL_MS) seenRequestIds.delete(id);
|
|
112799
|
-
}
|
|
112800
|
-
if (seenRequestIds.has(requestId)) return false;
|
|
112801
|
-
seenRequestIds.set(requestId, now);
|
|
112894
|
+
if (seenIds.has(requestId)) return false;
|
|
112895
|
+
seenIds.set(requestId, true);
|
|
112802
112896
|
return true;
|
|
112803
112897
|
}
|
|
112804
|
-
function handlePermissionRequested(request, chatId, deps) {
|
|
112898
|
+
function handlePermissionRequested(request, chatId, deps, chatType = "p2p") {
|
|
112805
112899
|
if (!deps.v2Client) {
|
|
112806
112900
|
deps.log("warn", "v2Client \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u6743\u9650\u5361\u7247\u53D1\u9001", { requestId: String(request.id ?? "") });
|
|
112807
112901
|
return;
|
|
112808
112902
|
}
|
|
112809
112903
|
const requestId = String(request.id ?? "");
|
|
112810
112904
|
if (!requestId || !markSeen(requestId)) return;
|
|
112811
|
-
const card =
|
|
112905
|
+
const card = buildPermissionCardDSL(request, chatId, chatType);
|
|
112812
112906
|
sendInteractiveCard(deps.feishuClient, chatId, card).catch((err) => {
|
|
112813
112907
|
deps.log("warn", "\u53D1\u9001\u6743\u9650\u5361\u7247\u5931\u8D25", {
|
|
112814
112908
|
requestId,
|
|
@@ -112816,14 +112910,14 @@ function handlePermissionRequested(request, chatId, deps) {
|
|
|
112816
112910
|
});
|
|
112817
112911
|
});
|
|
112818
112912
|
}
|
|
112819
|
-
function handleQuestionRequested(request, chatId, deps) {
|
|
112913
|
+
function handleQuestionRequested(request, chatId, deps, chatType = "p2p") {
|
|
112820
112914
|
if (!deps.v2Client) {
|
|
112821
112915
|
deps.log("warn", "v2Client \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u95EE\u7B54\u5361\u7247\u53D1\u9001", { requestId: String(request.id ?? "") });
|
|
112822
112916
|
return;
|
|
112823
112917
|
}
|
|
112824
112918
|
const requestId = String(request.id ?? "");
|
|
112825
112919
|
if (!requestId || !markSeen(requestId)) return;
|
|
112826
|
-
const card =
|
|
112920
|
+
const card = buildQuestionCardDSL(request, chatId, chatType);
|
|
112827
112921
|
sendInteractiveCard(deps.feishuClient, chatId, card).catch((err) => {
|
|
112828
112922
|
deps.log("warn", "\u53D1\u9001\u95EE\u7B54\u5361\u7247\u5931\u8D25", {
|
|
112829
112923
|
requestId,
|
|
@@ -112833,12 +112927,39 @@ function handleQuestionRequested(request, chatId, deps) {
|
|
|
112833
112927
|
}
|
|
112834
112928
|
async function handleCardAction(action, deps) {
|
|
112835
112929
|
if (!action.actionValue) return;
|
|
112836
|
-
{
|
|
112930
|
+
if (!deps.v2Client) {
|
|
112837
112931
|
deps.log("warn", "v2Client \u672A\u914D\u7F6E\uFF0C\u4EA4\u4E92\u56DE\u8C03\u88AB\u5FFD\u7565\uFF08\u6309\u94AE\u70B9\u51FB\u4E0D\u4F1A\u8F6C\u53D1\u5230 OpenCode\uFF09", {
|
|
112838
112932
|
actionValue: action.actionValue
|
|
112839
112933
|
});
|
|
112840
112934
|
return;
|
|
112841
112935
|
}
|
|
112936
|
+
let value;
|
|
112937
|
+
try {
|
|
112938
|
+
value = JSON.parse(action.actionValue);
|
|
112939
|
+
} catch {
|
|
112940
|
+
return;
|
|
112941
|
+
}
|
|
112942
|
+
const requestId = value.requestId;
|
|
112943
|
+
if (!requestId) return;
|
|
112944
|
+
try {
|
|
112945
|
+
if (value.action === "permission_reply" && "reply" in value) {
|
|
112946
|
+
await deps.v2Client.permission.reply({
|
|
112947
|
+
requestID: requestId,
|
|
112948
|
+
reply: value.reply
|
|
112949
|
+
});
|
|
112950
|
+
} else if (value.action === "question_reply" && "answers" in value) {
|
|
112951
|
+
await deps.v2Client.question.reply({
|
|
112952
|
+
requestID: requestId,
|
|
112953
|
+
answers: value.answers
|
|
112954
|
+
});
|
|
112955
|
+
}
|
|
112956
|
+
} catch (err) {
|
|
112957
|
+
deps.log("error", "\u4EA4\u4E92\u56DE\u8C03\u5904\u7406\u5931\u8D25", {
|
|
112958
|
+
action: value.action,
|
|
112959
|
+
requestId,
|
|
112960
|
+
error: err instanceof Error ? err.message : String(err)
|
|
112961
|
+
});
|
|
112962
|
+
}
|
|
112842
112963
|
}
|
|
112843
112964
|
function buildCallbackResponse(action) {
|
|
112844
112965
|
if (!action.actionValue) return {};
|
|
@@ -112862,23 +112983,81 @@ function buildCallbackResponse(action) {
|
|
|
112862
112983
|
toast: { type: "success", content: "\u2705 \u5DF2\u56DE\u7B54" }
|
|
112863
112984
|
};
|
|
112864
112985
|
}
|
|
112986
|
+
if (value.action === "send_message") {
|
|
112987
|
+
return {
|
|
112988
|
+
toast: { type: "info", content: "\u{1F4E8} \u5DF2\u53D1\u9001" }
|
|
112989
|
+
};
|
|
112990
|
+
}
|
|
112865
112991
|
return {};
|
|
112866
112992
|
}
|
|
112993
|
+
function buildPermissionCardDSL(request, chatId, chatType) {
|
|
112994
|
+
const permission = String(request.permission ?? "unknown");
|
|
112995
|
+
const patterns = Array.isArray(request.patterns) ? request.patterns.map(String) : [];
|
|
112996
|
+
const requestId = String(request.id ?? "");
|
|
112997
|
+
const patternsText = patterns.length > 0 ? patterns.map((p) => `- \`${p}\``).join("\n") : "\uFF08\u65E0\u5177\u4F53\u8DEF\u5F84\uFF09";
|
|
112998
|
+
const buttons = [
|
|
112999
|
+
{
|
|
113000
|
+
text: "\u2705 \u5141\u8BB8\u4E00\u6B21",
|
|
113001
|
+
value: "",
|
|
113002
|
+
style: "primary",
|
|
113003
|
+
actionPayload: { action: "permission_reply", requestId, reply: "once" }
|
|
113004
|
+
},
|
|
113005
|
+
{
|
|
113006
|
+
text: "\u{1F513} \u59CB\u7EC8\u5141\u8BB8",
|
|
113007
|
+
value: "",
|
|
113008
|
+
style: "default",
|
|
113009
|
+
actionPayload: { action: "permission_reply", requestId, reply: "always" }
|
|
113010
|
+
},
|
|
113011
|
+
{
|
|
113012
|
+
text: "\u274C \u62D2\u7EDD",
|
|
113013
|
+
value: "",
|
|
113014
|
+
style: "danger",
|
|
113015
|
+
actionPayload: { action: "permission_reply", requestId, reply: "reject" }
|
|
113016
|
+
}
|
|
113017
|
+
];
|
|
113018
|
+
const sections = [
|
|
113019
|
+
{ type: "markdown", content: `AI \u8BF7\u6C42\u4EE5\u4E0B\u6743\u9650:
|
|
113020
|
+
|
|
113021
|
+
${patternsText}` },
|
|
113022
|
+
{ type: "actions", buttons }
|
|
113023
|
+
];
|
|
113024
|
+
const dsl = { title: `\u{1F510} \u6743\u9650\u8BF7\u6C42: ${permission}`, template: "orange", sections };
|
|
113025
|
+
return { type: "card_kit", data: buildCardFromDSL(dsl, chatId, chatType) };
|
|
113026
|
+
}
|
|
113027
|
+
function buildQuestionCardDSL(request, chatId, chatType) {
|
|
113028
|
+
const questions = request.questions ?? [];
|
|
113029
|
+
const requestId = String(request.id ?? "");
|
|
113030
|
+
const q = questions[0];
|
|
113031
|
+
const header = String(q?.header ?? "AI \u63D0\u95EE");
|
|
113032
|
+
const questionText = String(q?.question ?? "\u8BF7\u9009\u62E9");
|
|
113033
|
+
const options = Array.isArray(q?.options) ? q.options : [];
|
|
113034
|
+
const buttons = options.map((opt, idx) => ({
|
|
113035
|
+
text: String(opt.label ?? opt.value ?? `\u9009\u9879 ${idx + 1}`),
|
|
113036
|
+
value: "",
|
|
113037
|
+
style: idx === 0 ? "primary" : "default",
|
|
113038
|
+
actionPayload: {
|
|
113039
|
+
action: "question_reply",
|
|
113040
|
+
requestId,
|
|
113041
|
+
answers: [[String(opt.value ?? opt.label ?? "")]]
|
|
113042
|
+
}
|
|
113043
|
+
}));
|
|
113044
|
+
const sections = [
|
|
113045
|
+
{ type: "markdown", content: questionText },
|
|
113046
|
+
...buttons.length > 0 ? [{ type: "actions", buttons }] : []
|
|
113047
|
+
];
|
|
113048
|
+
const dsl = { title: header, template: "blue", sections };
|
|
113049
|
+
return { type: "card_kit", data: buildCardFromDSL(dsl, chatId, chatType) };
|
|
113050
|
+
}
|
|
112867
113051
|
|
|
112868
113052
|
// src/feishu/dedup.ts
|
|
112869
|
-
var
|
|
112870
|
-
var seen = /* @__PURE__ */ new Map();
|
|
113053
|
+
var dedup = new TtlMap(10 * 60 * 1e3);
|
|
112871
113054
|
function initDedup(ttl) {
|
|
112872
|
-
|
|
113055
|
+
dedup = new TtlMap(ttl);
|
|
112873
113056
|
}
|
|
112874
113057
|
function isDuplicate(messageId) {
|
|
112875
|
-
const now = Date.now();
|
|
112876
|
-
for (const [k, ts] of seen) {
|
|
112877
|
-
if (now - ts > seenTtlMs) seen.delete(k);
|
|
112878
|
-
}
|
|
112879
113058
|
if (!messageId) return false;
|
|
112880
|
-
if (
|
|
112881
|
-
|
|
113059
|
+
if (dedup.has(messageId)) return true;
|
|
113060
|
+
dedup.set(messageId, true);
|
|
112882
113061
|
return false;
|
|
112883
113062
|
}
|
|
112884
113063
|
|
|
@@ -113235,6 +113414,25 @@ function startFeishuGateway(options) {
|
|
|
113235
113414
|
chatId: String(evt.context?.open_chat_id ?? evt.open_chat_id ?? ""),
|
|
113236
113415
|
operatorId: String(evt.operator?.open_id ?? "")
|
|
113237
113416
|
};
|
|
113417
|
+
const sendMsg = parseSendMessageAction(action);
|
|
113418
|
+
if (sendMsg) {
|
|
113419
|
+
const syntheticCtx = {
|
|
113420
|
+
chatId: sendMsg.chatId,
|
|
113421
|
+
messageId: `btn-${randomUUID()}`,
|
|
113422
|
+
messageType: "text",
|
|
113423
|
+
content: sendMsg.text,
|
|
113424
|
+
rawContent: JSON.stringify({ text: sendMsg.text }),
|
|
113425
|
+
chatType: sendMsg.chatType,
|
|
113426
|
+
senderId: action.operatorId ?? "",
|
|
113427
|
+
shouldReply: true
|
|
113428
|
+
};
|
|
113429
|
+
void Promise.resolve(onMessage(syntheticCtx)).catch((err) => {
|
|
113430
|
+
log("error", "send_message \u6309\u94AE\u5904\u7406\u5931\u8D25", {
|
|
113431
|
+
error: err instanceof Error ? err.message : String(err)
|
|
113432
|
+
});
|
|
113433
|
+
});
|
|
113434
|
+
return buildCallbackResponse(action);
|
|
113435
|
+
}
|
|
113238
113436
|
if (onCardAction) {
|
|
113239
113437
|
void onCardAction(action).catch((err) => {
|
|
113240
113438
|
log("error", "card action \u5904\u7406\u5931\u8D25", {
|
|
@@ -113282,6 +113480,20 @@ function startFeishuGateway(options) {
|
|
|
113282
113480
|
};
|
|
113283
113481
|
return { client: larkClient, stop };
|
|
113284
113482
|
}
|
|
113483
|
+
function parseSendMessageAction(action) {
|
|
113484
|
+
if (!action.actionValue) return void 0;
|
|
113485
|
+
try {
|
|
113486
|
+
const value = JSON.parse(action.actionValue);
|
|
113487
|
+
if (value.action !== "send_message") return void 0;
|
|
113488
|
+
const text = typeof value.text === "string" ? value.text : "";
|
|
113489
|
+
const chatId = typeof value.chatId === "string" ? value.chatId : "";
|
|
113490
|
+
if (!text || !chatId) return void 0;
|
|
113491
|
+
const chatType = value.chatType === "group" ? "group" : "p2p";
|
|
113492
|
+
return { chatId, chatType, text };
|
|
113493
|
+
} catch {
|
|
113494
|
+
return void 0;
|
|
113495
|
+
}
|
|
113496
|
+
}
|
|
113285
113497
|
|
|
113286
113498
|
// src/handler/action-bus.ts
|
|
113287
113499
|
var subscribers = /* @__PURE__ */ new Map();
|
|
@@ -113316,57 +113528,24 @@ function emit(sessionId, action) {
|
|
|
113316
113528
|
|
|
113317
113529
|
// src/handler/event.ts
|
|
113318
113530
|
var pendingBySession = /* @__PURE__ */ new Map();
|
|
113319
|
-
var sessionErrors =
|
|
113320
|
-
var
|
|
113321
|
-
var SESSION_ERROR_TTL_MS = 3e4;
|
|
113322
|
-
var retryAttempts = /* @__PURE__ */ new Map();
|
|
113323
|
-
var retryAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
113531
|
+
var sessionErrors = new TtlMap(3e4);
|
|
113532
|
+
var retryAttempts = new TtlMap(36e5);
|
|
113324
113533
|
var MAX_RETRY_ATTEMPTS = 2;
|
|
113325
|
-
var RETRY_ATTEMPTS_TTL_MS = 36e5;
|
|
113326
113534
|
function clearRetryAttempts(sessionKey) {
|
|
113327
113535
|
retryAttempts.delete(sessionKey);
|
|
113328
|
-
const timer = retryAttemptTimeouts.get(sessionKey);
|
|
113329
|
-
if (timer) {
|
|
113330
|
-
clearTimeout(timer);
|
|
113331
|
-
retryAttemptTimeouts.delete(sessionKey);
|
|
113332
|
-
}
|
|
113333
113536
|
}
|
|
113334
113537
|
function getRetryAttempts(sessionKey) {
|
|
113335
113538
|
return retryAttempts.get(sessionKey) ?? 0;
|
|
113336
113539
|
}
|
|
113337
113540
|
function setRetryAttempts(sessionKey, count) {
|
|
113338
113541
|
retryAttempts.set(sessionKey, count);
|
|
113339
|
-
const existing = retryAttemptTimeouts.get(sessionKey);
|
|
113340
|
-
if (existing) clearTimeout(existing);
|
|
113341
|
-
const timeoutId = setTimeout(() => {
|
|
113342
|
-
retryAttempts.delete(sessionKey);
|
|
113343
|
-
retryAttemptTimeouts.delete(sessionKey);
|
|
113344
|
-
}, RETRY_ATTEMPTS_TTL_MS);
|
|
113345
|
-
retryAttemptTimeouts.set(sessionKey, timeoutId);
|
|
113346
113542
|
}
|
|
113347
113543
|
function getSessionError(sessionId) {
|
|
113348
113544
|
return sessionErrors.get(sessionId);
|
|
113349
113545
|
}
|
|
113350
113546
|
function clearSessionError(sessionId) {
|
|
113351
|
-
const timer = sessionErrorTimeouts.get(sessionId);
|
|
113352
|
-
if (timer) {
|
|
113353
|
-
clearTimeout(timer);
|
|
113354
|
-
sessionErrorTimeouts.delete(sessionId);
|
|
113355
|
-
}
|
|
113356
113547
|
sessionErrors.delete(sessionId);
|
|
113357
113548
|
}
|
|
113358
|
-
function setSessionError(sessionId, message, fields) {
|
|
113359
|
-
const existing = sessionErrorTimeouts.get(sessionId);
|
|
113360
|
-
if (existing) {
|
|
113361
|
-
clearTimeout(existing);
|
|
113362
|
-
}
|
|
113363
|
-
sessionErrors.set(sessionId, { message, fields });
|
|
113364
|
-
const timeoutId = setTimeout(() => {
|
|
113365
|
-
sessionErrors.delete(sessionId);
|
|
113366
|
-
sessionErrorTimeouts.delete(sessionId);
|
|
113367
|
-
}, SESSION_ERROR_TTL_MS);
|
|
113368
|
-
sessionErrorTimeouts.set(sessionId, timeoutId);
|
|
113369
|
-
}
|
|
113370
113549
|
function registerPending(sessionId, payload) {
|
|
113371
113550
|
pendingBySession.set(sessionId, { ...payload, textBuffer: "", expectedMessageId: void 0 });
|
|
113372
113551
|
}
|
|
@@ -113417,112 +113596,116 @@ async function handleEvent(event, deps) {
|
|
|
113417
113596
|
switch (event.type) {
|
|
113418
113597
|
case "message.part.updated": {
|
|
113419
113598
|
const part = event.properties.part;
|
|
113420
|
-
if (!part) break;
|
|
113421
|
-
const
|
|
113422
|
-
if (!sessionId) break;
|
|
113423
|
-
const payload = pendingBySession.get(sessionId);
|
|
113599
|
+
if (!part?.sessionID) break;
|
|
113600
|
+
const payload = pendingBySession.get(part.sessionID);
|
|
113424
113601
|
if (!payload) break;
|
|
113425
|
-
|
|
113426
|
-
if (messageId) {
|
|
113427
|
-
if (!payload.expectedMessageId) {
|
|
113428
|
-
payload.expectedMessageId = messageId;
|
|
113429
|
-
} else if (payload.expectedMessageId !== messageId) {
|
|
113430
|
-
break;
|
|
113431
|
-
}
|
|
113432
|
-
} else if (payload.expectedMessageId) {
|
|
113433
|
-
break;
|
|
113434
|
-
}
|
|
113435
|
-
const partSessionId = part.sessionID;
|
|
113436
|
-
if (part.type === "tool") {
|
|
113437
|
-
const p = part;
|
|
113438
|
-
const toolName = String(p.toolName ?? p.name ?? "unknown");
|
|
113439
|
-
const callID = String(p.toolCallID ?? p.id ?? "");
|
|
113440
|
-
const hasError = p.error !== void 0 && p.error !== null;
|
|
113441
|
-
const rawState = p.state != null ? String(p.state) : hasError ? "error" : "running";
|
|
113442
|
-
const toolState = rawState === "completed" || rawState === "error" ? rawState : "running";
|
|
113443
|
-
if (partSessionId) {
|
|
113444
|
-
emit(partSessionId, {
|
|
113445
|
-
type: "tool-state-changed",
|
|
113446
|
-
sessionId: partSessionId,
|
|
113447
|
-
callID,
|
|
113448
|
-
tool: toolName,
|
|
113449
|
-
state: toolState
|
|
113450
|
-
});
|
|
113451
|
-
}
|
|
113452
|
-
break;
|
|
113453
|
-
}
|
|
113454
|
-
const delta = event.properties.delta;
|
|
113455
|
-
if (delta) {
|
|
113456
|
-
payload.textBuffer += delta;
|
|
113457
|
-
} else {
|
|
113458
|
-
const fullText = extractPartText(part);
|
|
113459
|
-
if (fullText) {
|
|
113460
|
-
payload.textBuffer = fullText;
|
|
113461
|
-
}
|
|
113462
|
-
}
|
|
113463
|
-
if (payload.textBuffer) {
|
|
113464
|
-
const res = await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
113465
|
-
if (!res.ok) ;
|
|
113466
|
-
}
|
|
113467
|
-
if (partSessionId) {
|
|
113468
|
-
emit(partSessionId, {
|
|
113469
|
-
type: "text-updated",
|
|
113470
|
-
sessionId: partSessionId,
|
|
113471
|
-
messageId: part.messageID,
|
|
113472
|
-
delta: delta ?? void 0,
|
|
113473
|
-
fullText: payload.textBuffer
|
|
113474
|
-
});
|
|
113475
|
-
}
|
|
113602
|
+
await handleMessagePartUpdated(event, part, payload);
|
|
113476
113603
|
break;
|
|
113477
113604
|
}
|
|
113478
|
-
case "session.error":
|
|
113479
|
-
|
|
113480
|
-
const sessionId = props.sessionID;
|
|
113481
|
-
if (!sessionId) break;
|
|
113482
|
-
const error48 = props.error;
|
|
113483
|
-
let errMsg;
|
|
113484
|
-
if (typeof error48 === "string") {
|
|
113485
|
-
errMsg = error48;
|
|
113486
|
-
} else if (error48 && typeof error48 === "object") {
|
|
113487
|
-
const e = error48;
|
|
113488
|
-
const rawDataMsg = e.data && typeof e.data === "object" && "message" in e.data ? e.data.message : void 0;
|
|
113489
|
-
const dataMsg = rawDataMsg != null ? String(rawDataMsg) : void 0;
|
|
113490
|
-
errMsg = String(e.message ?? dataMsg ?? e.type ?? e.name ?? "An unexpected error occurred");
|
|
113491
|
-
} else {
|
|
113492
|
-
errMsg = String(error48);
|
|
113493
|
-
}
|
|
113494
|
-
const fields = extractErrorFields(error48);
|
|
113495
|
-
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", { sessionId, errMsg });
|
|
113496
|
-
setSessionError(sessionId, errMsg, fields);
|
|
113605
|
+
case "session.error":
|
|
113606
|
+
handleSessionErrorEvent(event, deps);
|
|
113497
113607
|
break;
|
|
113498
|
-
|
|
113499
|
-
|
|
113500
|
-
const evtType = event.type;
|
|
113501
|
-
const evtProps = event.properties ?? {};
|
|
113502
|
-
const evtSessionId = evtProps.sessionID;
|
|
113503
|
-
if (evtType === "permission.asked" && evtSessionId) {
|
|
113504
|
-
emit(evtSessionId, {
|
|
113505
|
-
type: "permission-requested",
|
|
113506
|
-
sessionId: evtSessionId,
|
|
113507
|
-
request: evtProps
|
|
113508
|
-
});
|
|
113509
|
-
deps.log("info", "permission.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113510
|
-
} else if (evtType === "question.asked" && evtSessionId) {
|
|
113511
|
-
emit(evtSessionId, {
|
|
113512
|
-
type: "question-requested",
|
|
113513
|
-
sessionId: evtSessionId,
|
|
113514
|
-
request: evtProps
|
|
113515
|
-
});
|
|
113516
|
-
deps.log("info", "question.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113517
|
-
} else if (evtType === "session.idle" && evtSessionId) {
|
|
113518
|
-
emit(evtSessionId, {
|
|
113519
|
-
type: "session-idle",
|
|
113520
|
-
sessionId: evtSessionId
|
|
113521
|
-
});
|
|
113522
|
-
}
|
|
113608
|
+
default:
|
|
113609
|
+
handleV2Event(event, deps);
|
|
113523
113610
|
break;
|
|
113611
|
+
}
|
|
113612
|
+
}
|
|
113613
|
+
async function handleMessagePartUpdated(event, part, payload) {
|
|
113614
|
+
const messageId = part.messageID;
|
|
113615
|
+
if (messageId) {
|
|
113616
|
+
if (!payload.expectedMessageId) {
|
|
113617
|
+
payload.expectedMessageId = messageId;
|
|
113618
|
+
} else if (payload.expectedMessageId !== messageId) {
|
|
113619
|
+
return;
|
|
113620
|
+
}
|
|
113621
|
+
} else if (payload.expectedMessageId) {
|
|
113622
|
+
return;
|
|
113623
|
+
}
|
|
113624
|
+
const partSessionId = part.sessionID;
|
|
113625
|
+
if (part.type === "tool") {
|
|
113626
|
+
const p = part;
|
|
113627
|
+
const toolName = String(p.toolName ?? p.name ?? "unknown");
|
|
113628
|
+
const callID = String(p.toolCallID ?? p.id ?? "");
|
|
113629
|
+
const hasError = p.error !== void 0 && p.error !== null;
|
|
113630
|
+
const rawState = p.state != null ? String(p.state) : hasError ? "error" : "running";
|
|
113631
|
+
const toolState = rawState === "completed" || rawState === "error" ? rawState : "running";
|
|
113632
|
+
if (partSessionId) {
|
|
113633
|
+
emit(partSessionId, {
|
|
113634
|
+
type: "tool-state-changed",
|
|
113635
|
+
sessionId: partSessionId,
|
|
113636
|
+
callID,
|
|
113637
|
+
tool: toolName,
|
|
113638
|
+
state: toolState
|
|
113639
|
+
});
|
|
113640
|
+
}
|
|
113641
|
+
return;
|
|
113642
|
+
}
|
|
113643
|
+
const delta = event.properties.delta;
|
|
113644
|
+
if (delta) {
|
|
113645
|
+
payload.textBuffer += delta;
|
|
113646
|
+
} else {
|
|
113647
|
+
const fullText = extractPartText(part);
|
|
113648
|
+
if (fullText) {
|
|
113649
|
+
payload.textBuffer = fullText;
|
|
113524
113650
|
}
|
|
113525
113651
|
}
|
|
113652
|
+
if (payload.textBuffer) {
|
|
113653
|
+
await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
113654
|
+
}
|
|
113655
|
+
if (partSessionId) {
|
|
113656
|
+
emit(partSessionId, {
|
|
113657
|
+
type: "text-updated",
|
|
113658
|
+
sessionId: partSessionId,
|
|
113659
|
+
messageId: part.messageID,
|
|
113660
|
+
delta: delta ?? void 0,
|
|
113661
|
+
fullText: payload.textBuffer
|
|
113662
|
+
});
|
|
113663
|
+
}
|
|
113664
|
+
}
|
|
113665
|
+
function handleSessionErrorEvent(event, deps) {
|
|
113666
|
+
const props = event.properties;
|
|
113667
|
+
const sessionId = props.sessionID;
|
|
113668
|
+
if (!sessionId) return;
|
|
113669
|
+
const error48 = props.error;
|
|
113670
|
+
let errMsg;
|
|
113671
|
+
if (typeof error48 === "string") {
|
|
113672
|
+
errMsg = error48;
|
|
113673
|
+
} else if (error48 && typeof error48 === "object") {
|
|
113674
|
+
const e = error48;
|
|
113675
|
+
const asStr = (v) => typeof v === "string" && v.trim().length > 0 ? v : void 0;
|
|
113676
|
+
const rawDataMsg = e.data && typeof e.data === "object" && "message" in e.data ? e.data.message : void 0;
|
|
113677
|
+
errMsg = asStr(e.message) ?? asStr(rawDataMsg) ?? asStr(e.type) ?? asStr(e.name) ?? "An unexpected error occurred";
|
|
113678
|
+
} else {
|
|
113679
|
+
errMsg = String(error48);
|
|
113680
|
+
}
|
|
113681
|
+
const fields = extractErrorFields(error48);
|
|
113682
|
+
deps.log("warn", "\u6536\u5230 session.error \u4E8B\u4EF6", { sessionId, errMsg });
|
|
113683
|
+
sessionErrors.set(sessionId, { message: errMsg, fields });
|
|
113684
|
+
}
|
|
113685
|
+
function handleV2Event(event, deps) {
|
|
113686
|
+
const evtType = event.type;
|
|
113687
|
+
const evtProps = event.properties ?? {};
|
|
113688
|
+
const evtSessionId = evtProps.sessionID;
|
|
113689
|
+
if (evtType === "permission.asked" && evtSessionId) {
|
|
113690
|
+
emit(evtSessionId, {
|
|
113691
|
+
type: "permission-requested",
|
|
113692
|
+
sessionId: evtSessionId,
|
|
113693
|
+
request: evtProps
|
|
113694
|
+
});
|
|
113695
|
+
deps.log("info", "permission.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113696
|
+
} else if (evtType === "question.asked" && evtSessionId) {
|
|
113697
|
+
emit(evtSessionId, {
|
|
113698
|
+
type: "question-requested",
|
|
113699
|
+
sessionId: evtSessionId,
|
|
113700
|
+
request: evtProps
|
|
113701
|
+
});
|
|
113702
|
+
deps.log("info", "question.asked \u4E8B\u4EF6\u5DF2\u5206\u53D1", { sessionId: evtSessionId });
|
|
113703
|
+
} else if (evtType === "session.idle" && evtSessionId) {
|
|
113704
|
+
emit(evtSessionId, {
|
|
113705
|
+
type: "session-idle",
|
|
113706
|
+
sessionId: evtSessionId
|
|
113707
|
+
});
|
|
113708
|
+
}
|
|
113526
113709
|
}
|
|
113527
113710
|
function extractPartText(part) {
|
|
113528
113711
|
if (part.type === "text") return part.text ?? "";
|
|
@@ -113532,6 +113715,128 @@ function extractPartText(part) {
|
|
|
113532
113715
|
return "";
|
|
113533
113716
|
}
|
|
113534
113717
|
|
|
113718
|
+
// src/handler/error-recovery.ts
|
|
113719
|
+
var SessionErrorDetected = class extends Error {
|
|
113720
|
+
constructor(sessionError) {
|
|
113721
|
+
super(sessionError.message);
|
|
113722
|
+
this.sessionError = sessionError;
|
|
113723
|
+
this.name = "SessionErrorDetected";
|
|
113724
|
+
}
|
|
113725
|
+
};
|
|
113726
|
+
async function getGlobalDefaultModel(client, directory) {
|
|
113727
|
+
const query = directory ? { directory } : void 0;
|
|
113728
|
+
const { data: config2 } = await client.config.get({ query });
|
|
113729
|
+
const model = config2?.model;
|
|
113730
|
+
if (!model || !model.includes("/")) return void 0;
|
|
113731
|
+
const slash = model.indexOf("/");
|
|
113732
|
+
const providerID = model.slice(0, slash).trim();
|
|
113733
|
+
const modelID = model.slice(slash + 1).trim();
|
|
113734
|
+
if (!providerID || !modelID) return void 0;
|
|
113735
|
+
return { providerID, modelID };
|
|
113736
|
+
}
|
|
113737
|
+
function extractSessionError(err, sessionId) {
|
|
113738
|
+
const result = err instanceof SessionErrorDetected ? err.sessionError : getSessionError(sessionId);
|
|
113739
|
+
clearSessionError(sessionId);
|
|
113740
|
+
return result;
|
|
113741
|
+
}
|
|
113742
|
+
async function tryModelRecovery(params) {
|
|
113743
|
+
const {
|
|
113744
|
+
sessionError,
|
|
113745
|
+
sessionId,
|
|
113746
|
+
sessionKey,
|
|
113747
|
+
client,
|
|
113748
|
+
directory,
|
|
113749
|
+
parts,
|
|
113750
|
+
timeout,
|
|
113751
|
+
pollInterval,
|
|
113752
|
+
stablePolls,
|
|
113753
|
+
query,
|
|
113754
|
+
signal,
|
|
113755
|
+
log,
|
|
113756
|
+
poll
|
|
113757
|
+
} = params;
|
|
113758
|
+
log("info", "\u9519\u8BEF\u5B57\u6BB5\u68C0\u67E5", {
|
|
113759
|
+
sessionKey,
|
|
113760
|
+
fields: sessionError.fields,
|
|
113761
|
+
isModel: isModelError(sessionError.fields)
|
|
113762
|
+
});
|
|
113763
|
+
if (!isModelError(sessionError.fields)) {
|
|
113764
|
+
return { recovered: false, sessionError };
|
|
113765
|
+
}
|
|
113766
|
+
const attempts = getRetryAttempts(sessionKey);
|
|
113767
|
+
if (attempts >= MAX_RETRY_ATTEMPTS) {
|
|
113768
|
+
log("warn", "\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey, attempts });
|
|
113769
|
+
return { recovered: false, sessionError };
|
|
113770
|
+
}
|
|
113771
|
+
try {
|
|
113772
|
+
let modelOverride;
|
|
113773
|
+
try {
|
|
113774
|
+
modelOverride = await getGlobalDefaultModel(client, directory);
|
|
113775
|
+
} catch (configErr) {
|
|
113776
|
+
log("warn", "\u8BFB\u53D6\u5168\u5C40\u6A21\u578B\u914D\u7F6E\u5931\u8D25", {
|
|
113777
|
+
sessionKey,
|
|
113778
|
+
error: configErr instanceof Error ? configErr.message : String(configErr)
|
|
113779
|
+
});
|
|
113780
|
+
}
|
|
113781
|
+
if (!modelOverride) {
|
|
113782
|
+
log("warn", "\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey });
|
|
113783
|
+
return { recovered: false, sessionError };
|
|
113784
|
+
}
|
|
113785
|
+
setRetryAttempts(sessionKey, attempts + 1);
|
|
113786
|
+
log("info", "\u4F7F\u7528\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u6062\u590D", {
|
|
113787
|
+
sessionKey,
|
|
113788
|
+
providerID: modelOverride.providerID,
|
|
113789
|
+
modelID: modelOverride.modelID
|
|
113790
|
+
});
|
|
113791
|
+
clearSessionError(sessionId);
|
|
113792
|
+
await client.session.promptAsync({
|
|
113793
|
+
path: { id: sessionId },
|
|
113794
|
+
query,
|
|
113795
|
+
body: { parts: [...parts], model: modelOverride }
|
|
113796
|
+
});
|
|
113797
|
+
const finalText = await poll(client, sessionId, {
|
|
113798
|
+
timeout,
|
|
113799
|
+
pollInterval,
|
|
113800
|
+
stablePolls,
|
|
113801
|
+
query,
|
|
113802
|
+
signal
|
|
113803
|
+
});
|
|
113804
|
+
log("info", "\u6A21\u578B\u6062\u590D\u540E\u54CD\u5E94\u5B8C\u6210", {
|
|
113805
|
+
sessionKey,
|
|
113806
|
+
sessionId,
|
|
113807
|
+
output: finalText || "(empty)"
|
|
113808
|
+
});
|
|
113809
|
+
clearRetryAttempts(sessionKey);
|
|
113810
|
+
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
113811
|
+
sessionId,
|
|
113812
|
+
sessionKey,
|
|
113813
|
+
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
113814
|
+
attempt: attempts + 1
|
|
113815
|
+
});
|
|
113816
|
+
return { recovered: true, text: finalText };
|
|
113817
|
+
} catch (recoveryErr) {
|
|
113818
|
+
if (recoveryErr instanceof Error && recoveryErr.name === "AbortError") {
|
|
113819
|
+
throw recoveryErr;
|
|
113820
|
+
}
|
|
113821
|
+
const errMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
113822
|
+
let updatedError;
|
|
113823
|
+
if (recoveryErr instanceof SessionErrorDetected) {
|
|
113824
|
+
updatedError = recoveryErr.sessionError;
|
|
113825
|
+
clearSessionError(sessionId);
|
|
113826
|
+
} else {
|
|
113827
|
+
const sseError = getSessionError(sessionId);
|
|
113828
|
+
if (sseError) {
|
|
113829
|
+
updatedError = sseError;
|
|
113830
|
+
clearSessionError(sessionId);
|
|
113831
|
+
} else {
|
|
113832
|
+
updatedError = { message: errMsg, fields: [] };
|
|
113833
|
+
}
|
|
113834
|
+
}
|
|
113835
|
+
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", { sessionId, sessionKey, error: errMsg });
|
|
113836
|
+
return { recovered: false, sessionError: updatedError };
|
|
113837
|
+
}
|
|
113838
|
+
}
|
|
113839
|
+
|
|
113535
113840
|
// src/session.ts
|
|
113536
113841
|
var SESSION_KEY_PREFIX = "feishu";
|
|
113537
113842
|
var TITLE_PREFIX = "Feishu";
|
|
@@ -113585,55 +113890,6 @@ async function getOrCreateSession(client, sessionKey, directory) {
|
|
|
113585
113890
|
return session;
|
|
113586
113891
|
}
|
|
113587
113892
|
|
|
113588
|
-
// src/feishu/markdown.ts
|
|
113589
|
-
var MAX_CARD_BYTES = 28 * 1024;
|
|
113590
|
-
var TRUNCATION_SUFFIX = "\n\n*\u5185\u5BB9\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD*";
|
|
113591
|
-
var TRUNCATION_SUFFIX_BYTES = new TextEncoder().encode(TRUNCATION_SUFFIX).length;
|
|
113592
|
-
var CODE_FENCE_BYTES = 4;
|
|
113593
|
-
var HTML_TAG_RE = /<\/?\w+(?:\s[^>]*)?\/?>/g;
|
|
113594
|
-
function cleanMarkdown(text) {
|
|
113595
|
-
let result = text.replace(/<br\s*\/?>/gi, "\n");
|
|
113596
|
-
const { segments, codeBlocks } = extractCodeBlocks(result);
|
|
113597
|
-
result = segments.map((seg) => seg.replace(HTML_TAG_RE, "")).join("\0");
|
|
113598
|
-
let idx = 0;
|
|
113599
|
-
result = result.replace(/\0/g, () => codeBlocks[idx++] ?? "");
|
|
113600
|
-
result = closeCodeBlocks(result);
|
|
113601
|
-
return result;
|
|
113602
|
-
}
|
|
113603
|
-
function truncateMarkdown(text, limit = MAX_CARD_BYTES) {
|
|
113604
|
-
const bytes = new TextEncoder().encode(text);
|
|
113605
|
-
if (bytes.length <= limit) return text;
|
|
113606
|
-
const effectiveLimit = limit - TRUNCATION_SUFFIX_BYTES - CODE_FENCE_BYTES;
|
|
113607
|
-
if (effectiveLimit <= 0) return TRUNCATION_SUFFIX;
|
|
113608
|
-
const truncated = new TextDecoder().decode(bytes.slice(0, effectiveLimit));
|
|
113609
|
-
const lastNewline = truncated.lastIndexOf("\n");
|
|
113610
|
-
const cutPoint = lastNewline > effectiveLimit * 0.8 ? lastNewline : truncated.length;
|
|
113611
|
-
let result = truncated.slice(0, cutPoint);
|
|
113612
|
-
result = closeCodeBlocks(result);
|
|
113613
|
-
return result + TRUNCATION_SUFFIX;
|
|
113614
|
-
}
|
|
113615
|
-
function extractCodeBlocks(text) {
|
|
113616
|
-
const segments = [];
|
|
113617
|
-
const codeBlocks = [];
|
|
113618
|
-
const re = /```[\s\S]*?```/g;
|
|
113619
|
-
let lastIndex = 0;
|
|
113620
|
-
let match;
|
|
113621
|
-
while ((match = re.exec(text)) !== null) {
|
|
113622
|
-
segments.push(text.slice(lastIndex, match.index));
|
|
113623
|
-
codeBlocks.push(match[0]);
|
|
113624
|
-
lastIndex = match.index + match[0].length;
|
|
113625
|
-
}
|
|
113626
|
-
segments.push(text.slice(lastIndex));
|
|
113627
|
-
return { segments, codeBlocks };
|
|
113628
|
-
}
|
|
113629
|
-
function closeCodeBlocks(text) {
|
|
113630
|
-
const matches = text.match(/```/g);
|
|
113631
|
-
if (matches && matches.length % 2 !== 0) {
|
|
113632
|
-
return text + "\n```";
|
|
113633
|
-
}
|
|
113634
|
-
return text;
|
|
113635
|
-
}
|
|
113636
|
-
|
|
113637
113893
|
// src/feishu/streaming-card.ts
|
|
113638
113894
|
var StreamingCard = class {
|
|
113639
113895
|
constructor(cardkit, feishuClient, chatId, log) {
|
|
@@ -113696,9 +113952,9 @@ var StreamingCard = class {
|
|
|
113696
113952
|
/**
|
|
113697
113953
|
* 更新工具状态到 tools 元素
|
|
113698
113954
|
*/
|
|
113699
|
-
async setToolStatus(callID,
|
|
113955
|
+
async setToolStatus(callID, tool2, state) {
|
|
113700
113956
|
if (this.closed || !this.cardId) return;
|
|
113701
|
-
this.toolStates.set(callID, { tool, state });
|
|
113957
|
+
this.toolStates.set(callID, { tool: tool2, state });
|
|
113702
113958
|
this.enqueue(() => this.doUpdateTools());
|
|
113703
113959
|
}
|
|
113704
113960
|
/**
|
|
@@ -113786,6 +114042,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113786
114042
|
const query = directory ? { directory } : void 0;
|
|
113787
114043
|
const sessionKey = buildSessionKey(chatType, chatType === "p2p" ? senderId : chatId);
|
|
113788
114044
|
const session = await getOrCreateSession(client, sessionKey, directory);
|
|
114045
|
+
registerSessionChat(session.id, chatId, chatType);
|
|
113789
114046
|
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log);
|
|
113790
114047
|
if (!parts.length) return void 0;
|
|
113791
114048
|
log("info", "\u6536\u5230\u7528\u6237\u6D88\u606F", {
|
|
@@ -113871,12 +114128,12 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113871
114128
|
break;
|
|
113872
114129
|
case "permission-requested":
|
|
113873
114130
|
if (deps.interactiveDeps) {
|
|
113874
|
-
handlePermissionRequested(action.request, chatId, deps.interactiveDeps);
|
|
114131
|
+
handlePermissionRequested(action.request, chatId, deps.interactiveDeps, chatType);
|
|
113875
114132
|
}
|
|
113876
114133
|
break;
|
|
113877
114134
|
case "question-requested":
|
|
113878
114135
|
if (deps.interactiveDeps) {
|
|
113879
|
-
handleQuestionRequested(action.request, chatId, deps.interactiveDeps);
|
|
114136
|
+
handleQuestionRequested(action.request, chatId, deps.interactiveDeps, chatType);
|
|
113880
114137
|
}
|
|
113881
114138
|
break;
|
|
113882
114139
|
}
|
|
@@ -113907,108 +114164,50 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113907
114164
|
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
113908
114165
|
return void 0;
|
|
113909
114166
|
}
|
|
113910
|
-
|
|
113911
|
-
|
|
113912
|
-
sessionError = err.sessionError;
|
|
113913
|
-
clearSessionError(session.id);
|
|
113914
|
-
} else {
|
|
113915
|
-
sessionError = getSessionError(session.id);
|
|
113916
|
-
clearSessionError(session.id);
|
|
113917
|
-
}
|
|
114167
|
+
const sessionError = extractSessionError(err, session.id);
|
|
114168
|
+
let displayError = sessionError;
|
|
113918
114169
|
if (sessionError) {
|
|
113919
|
-
|
|
113920
|
-
|
|
113921
|
-
|
|
113922
|
-
|
|
113923
|
-
});
|
|
113924
|
-
}
|
|
113925
|
-
if (sessionError && isModelError(sessionError.fields)) {
|
|
113926
|
-
const attempts = getRetryAttempts(sessionKey);
|
|
113927
|
-
if (attempts < MAX_RETRY_ATTEMPTS) {
|
|
113928
|
-
try {
|
|
113929
|
-
let modelOverride;
|
|
113930
|
-
try {
|
|
113931
|
-
modelOverride = await getGlobalDefaultModel(client, directory);
|
|
113932
|
-
} catch (configErr) {
|
|
113933
|
-
log("warn", "\u8BFB\u53D6\u5168\u5C40\u6A21\u578B\u914D\u7F6E\u5931\u8D25", {
|
|
113934
|
-
sessionKey,
|
|
113935
|
-
error: configErr instanceof Error ? configErr.message : String(configErr)
|
|
113936
|
-
});
|
|
113937
|
-
}
|
|
113938
|
-
if (!modelOverride) {
|
|
113939
|
-
log("warn", "\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey });
|
|
113940
|
-
} else {
|
|
113941
|
-
setRetryAttempts(sessionKey, attempts + 1);
|
|
113942
|
-
log("info", "\u4F7F\u7528\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u6062\u590D", {
|
|
113943
|
-
sessionKey,
|
|
113944
|
-
providerID: modelOverride.providerID,
|
|
113945
|
-
modelID: modelOverride.modelID
|
|
113946
|
-
});
|
|
113947
|
-
clearSessionError(session.id);
|
|
113948
|
-
await client.session.promptAsync({
|
|
113949
|
-
path: { id: session.id },
|
|
113950
|
-
query,
|
|
113951
|
-
body: { ...baseBody, model: modelOverride }
|
|
113952
|
-
});
|
|
113953
|
-
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query, signal });
|
|
113954
|
-
log("info", "\u6A21\u578B\u6062\u590D\u540E\u54CD\u5E94\u5B8C\u6210", {
|
|
113955
|
-
sessionKey,
|
|
113956
|
-
sessionId: session.id,
|
|
113957
|
-
output: finalText || "(empty)"
|
|
113958
|
-
});
|
|
113959
|
-
clearRetryAttempts(sessionKey);
|
|
113960
|
-
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
113961
|
-
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
113962
|
-
sessionId: session.id,
|
|
113963
|
-
sessionKey,
|
|
113964
|
-
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
113965
|
-
attempt: attempts + 1
|
|
113966
|
-
});
|
|
113967
|
-
if (config2.autoPrompt.enabled && shouldReply) {
|
|
113968
|
-
return { sessionId: session.id, sessionKey, chatId, deps };
|
|
113969
|
-
}
|
|
113970
|
-
return void 0;
|
|
113971
|
-
}
|
|
113972
|
-
} catch (recoveryErr) {
|
|
113973
|
-
if (recoveryErr instanceof Error && recoveryErr.name === "AbortError") {
|
|
113974
|
-
log("info", "\u6A21\u578B\u6062\u590D\u88AB\u4E2D\u65AD", { sessionKey });
|
|
113975
|
-
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
113976
|
-
return void 0;
|
|
113977
|
-
}
|
|
113978
|
-
const errMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
113979
|
-
if (recoveryErr instanceof SessionErrorDetected) {
|
|
113980
|
-
sessionError = recoveryErr.sessionError;
|
|
113981
|
-
clearSessionError(session.id);
|
|
113982
|
-
} else {
|
|
113983
|
-
const sseError = getSessionError(session.id);
|
|
113984
|
-
if (sseError) {
|
|
113985
|
-
sessionError = sseError;
|
|
113986
|
-
clearSessionError(session.id);
|
|
113987
|
-
} else {
|
|
113988
|
-
sessionError = { message: errMsg, fields: [] };
|
|
113989
|
-
}
|
|
113990
|
-
}
|
|
113991
|
-
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", {
|
|
113992
|
-
sessionId: session.id,
|
|
113993
|
-
sessionKey,
|
|
113994
|
-
error: errMsg
|
|
113995
|
-
});
|
|
113996
|
-
}
|
|
113997
|
-
} else {
|
|
113998
|
-
log("warn", "\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", {
|
|
114170
|
+
try {
|
|
114171
|
+
const recovery = await tryModelRecovery({
|
|
114172
|
+
sessionError,
|
|
114173
|
+
sessionId: session.id,
|
|
113999
114174
|
sessionKey,
|
|
114000
|
-
|
|
114175
|
+
client,
|
|
114176
|
+
directory,
|
|
114177
|
+
parts,
|
|
114178
|
+
timeout,
|
|
114179
|
+
pollInterval,
|
|
114180
|
+
stablePolls,
|
|
114181
|
+
query,
|
|
114182
|
+
signal,
|
|
114183
|
+
log,
|
|
114184
|
+
poll: pollForResponse
|
|
114001
114185
|
});
|
|
114186
|
+
if (recovery.recovered) {
|
|
114187
|
+
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, recovery.text || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
114188
|
+
if (config2.autoPrompt.enabled && shouldReply) {
|
|
114189
|
+
return { sessionId: session.id, sessionKey, chatId, deps };
|
|
114190
|
+
}
|
|
114191
|
+
return void 0;
|
|
114192
|
+
}
|
|
114193
|
+
displayError = recovery.sessionError;
|
|
114194
|
+
} catch (abortErr) {
|
|
114195
|
+
if (abortErr instanceof Error && abortErr.name === "AbortError") {
|
|
114196
|
+
log("info", "\u6A21\u578B\u6062\u590D\u88AB\u4E2D\u65AD", { sessionKey });
|
|
114197
|
+
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
114198
|
+
return void 0;
|
|
114199
|
+
}
|
|
114200
|
+
throw abortErr;
|
|
114002
114201
|
}
|
|
114003
114202
|
}
|
|
114004
114203
|
const thrownError = err instanceof Error ? err.message : String(err);
|
|
114005
|
-
const errorMessage =
|
|
114204
|
+
const errorMessage = displayError?.message || thrownError;
|
|
114006
114205
|
log("error", "\u5BF9\u8BDD\u5904\u7406\u5931\u8D25", {
|
|
114007
114206
|
sessionId: session.id,
|
|
114008
114207
|
sessionKey,
|
|
114009
114208
|
chatType,
|
|
114010
114209
|
error: thrownError,
|
|
114011
|
-
...
|
|
114210
|
+
...displayError ? { sessionError: displayError.message } : {}
|
|
114012
114211
|
});
|
|
114013
114212
|
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, "\u274C " + errorMessage);
|
|
114014
114213
|
} finally {
|
|
@@ -114018,17 +114217,6 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114018
114217
|
unregisterPending(activeSessionId);
|
|
114019
114218
|
}
|
|
114020
114219
|
}
|
|
114021
|
-
async function getGlobalDefaultModel(client, directory) {
|
|
114022
|
-
const query = directory ? { directory } : void 0;
|
|
114023
|
-
const { data: config2 } = await client.config.get({ query });
|
|
114024
|
-
const model = config2?.model;
|
|
114025
|
-
if (!model || !model.includes("/")) return void 0;
|
|
114026
|
-
const slash = model.indexOf("/");
|
|
114027
|
-
const providerID = model.slice(0, slash).trim();
|
|
114028
|
-
const modelID = model.slice(slash + 1).trim();
|
|
114029
|
-
if (!providerID || !modelID) return void 0;
|
|
114030
|
-
return { providerID, modelID };
|
|
114031
|
-
}
|
|
114032
114220
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
114033
114221
|
if (messageType === "text") {
|
|
114034
114222
|
let promptText = textContent;
|
|
@@ -114043,13 +114231,6 @@ async function buildPromptParts(feishuClient, messageId, messageType, rawContent
|
|
|
114043
114231
|
}
|
|
114044
114232
|
return parts;
|
|
114045
114233
|
}
|
|
114046
|
-
var SessionErrorDetected = class extends Error {
|
|
114047
|
-
constructor(sessionError) {
|
|
114048
|
-
super(sessionError.message);
|
|
114049
|
-
this.sessionError = sessionError;
|
|
114050
|
-
this.name = "SessionErrorDetected";
|
|
114051
|
-
}
|
|
114052
|
-
};
|
|
114053
114234
|
async function pollForResponse(client, sessionId, opts) {
|
|
114054
114235
|
const { timeout, pollInterval, stablePolls, query, signal } = opts;
|
|
114055
114236
|
const start = Date.now();
|
|
@@ -114170,6 +114351,7 @@ function extractLastAssistantText(messages) {
|
|
|
114170
114351
|
}
|
|
114171
114352
|
|
|
114172
114353
|
// src/handler/session-queue.ts
|
|
114354
|
+
var QUEUE_MONITOR_INTERVAL_MS = 200;
|
|
114173
114355
|
var states = /* @__PURE__ */ new Map();
|
|
114174
114356
|
function getOrCreateState(sessionKey) {
|
|
114175
114357
|
const existing = states.get(sessionKey);
|
|
@@ -114317,7 +114499,7 @@ async function drainLoop(sessionKey, state) {
|
|
|
114317
114499
|
const autoPromptController = new AbortController();
|
|
114318
114500
|
const monitor = setInterval(() => {
|
|
114319
114501
|
if (state.queue.length > 0) autoPromptController.abort();
|
|
114320
|
-
},
|
|
114502
|
+
}, QUEUE_MONITOR_INTERVAL_MS);
|
|
114321
114503
|
try {
|
|
114322
114504
|
const result = await runOneAutoPromptIteration(
|
|
114323
114505
|
autoPromptCtx,
|
|
@@ -114456,11 +114638,17 @@ function formatHistoryAsContext(messages) {
|
|
|
114456
114638
|
return `${header}
|
|
114457
114639
|
${body}`;
|
|
114458
114640
|
}
|
|
114459
|
-
|
|
114460
|
-
// src/index.ts
|
|
114461
114641
|
var SERVICE_NAME = "opencode-feishu";
|
|
114462
114642
|
var LOG_PREFIX = "[feishu]";
|
|
114463
114643
|
var isDebug = !!process.env.FEISHU_DEBUG;
|
|
114644
|
+
function loadFeishuSkill() {
|
|
114645
|
+
const skillPath = join(fileURLToPath(import.meta.url), "../../skills/feishu-card-interaction.md");
|
|
114646
|
+
if (existsSync(skillPath)) {
|
|
114647
|
+
return readFileSync(skillPath, "utf-8");
|
|
114648
|
+
}
|
|
114649
|
+
return "\u5F53\u524D\u7528\u6237\u901A\u8FC7\u98DE\u4E66\uFF08Feishu/Lark\uFF09\u4E0E\u4F60\u5BF9\u8BDD\u3002\u4F60\u53EF\u4EE5\u4F7F\u7528 feishu_send_card \u5DE5\u5177\u53D1\u9001\u683C\u5F0F\u5316\u5361\u7247\u6D88\u606F\uFF08\u652F\u6301\u6309\u94AE\u4EA4\u4E92\uFF09\u3002";
|
|
114650
|
+
}
|
|
114651
|
+
var feishuSystemPrompt = loadFeishuSkill();
|
|
114464
114652
|
var FeishuPlugin = async (ctx) => {
|
|
114465
114653
|
const { client } = ctx;
|
|
114466
114654
|
let gateway = null;
|
|
@@ -114480,21 +114668,9 @@ var FeishuPlugin = async (ctx) => {
|
|
|
114480
114668
|
});
|
|
114481
114669
|
};
|
|
114482
114670
|
const configPath = join(homedir(), ".config", "opencode", "plugins", "feishu.json");
|
|
114483
|
-
if (!existsSync(configPath)) {
|
|
114484
|
-
throw new Error(
|
|
114485
|
-
`\u7F3A\u5C11\u98DE\u4E66\u914D\u7F6E\u6587\u4EF6\uFF1A\u8BF7\u521B\u5EFA ${configPath}\uFF0C\u5185\u5BB9\u4E3A {"appId":"cli_xxx","appSecret":"xxx"}`
|
|
114486
|
-
);
|
|
114487
|
-
}
|
|
114488
114671
|
let resolvedConfig;
|
|
114489
114672
|
try {
|
|
114490
|
-
|
|
114491
|
-
JSON.parse(readFileSync(configPath, "utf-8"))
|
|
114492
|
-
);
|
|
114493
|
-
const parsed = FeishuConfigSchema.parse(raw);
|
|
114494
|
-
resolvedConfig = {
|
|
114495
|
-
...parsed,
|
|
114496
|
-
directory: expandDirectoryPath(parsed.directory ?? ctx.directory ?? "")
|
|
114497
|
-
};
|
|
114673
|
+
resolvedConfig = loadAndValidateConfig(configPath, ctx.directory ?? "");
|
|
114498
114674
|
} catch (e) {
|
|
114499
114675
|
if (e instanceof external_exports.ZodError) {
|
|
114500
114676
|
const details = e.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
@@ -114515,7 +114691,7 @@ ${details}`);
|
|
|
114515
114691
|
});
|
|
114516
114692
|
const cardkit = new CardKitClient(larkClient, log);
|
|
114517
114693
|
const botOpenId = await fetchBotOpenId(larkClient, log);
|
|
114518
|
-
const v2Client = void 0;
|
|
114694
|
+
const v2Client = createOpencodeClient({ directory: resolvedConfig.directory || void 0 });
|
|
114519
114695
|
gateway = startFeishuGateway({
|
|
114520
114696
|
config: resolvedConfig,
|
|
114521
114697
|
larkClient,
|
|
@@ -114569,10 +114745,25 @@ ${details}`);
|
|
|
114569
114745
|
event: async ({ event }) => {
|
|
114570
114746
|
if (!gateway) return;
|
|
114571
114747
|
await handleEvent(event, { log, directory: resolvedConfig.directory });
|
|
114748
|
+
},
|
|
114749
|
+
tool: {
|
|
114750
|
+
feishu_send_card: createSendCardTool({ feishuClient: larkClient, log })
|
|
114751
|
+
},
|
|
114752
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
114753
|
+
if (!input.sessionID || !getChatIdBySession(input.sessionID)) return;
|
|
114754
|
+
output.system.push(feishuSystemPrompt);
|
|
114572
114755
|
}
|
|
114573
114756
|
};
|
|
114574
114757
|
return hooks;
|
|
114575
114758
|
};
|
|
114759
|
+
function loadAndValidateConfig(configPath, ctxDirectory) {
|
|
114760
|
+
if (!existsSync(configPath)) {
|
|
114761
|
+
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"}`);
|
|
114762
|
+
}
|
|
114763
|
+
const raw = resolveEnvPlaceholders(JSON.parse(readFileSync(configPath, "utf-8")));
|
|
114764
|
+
const parsed = FeishuConfigSchema.parse(raw);
|
|
114765
|
+
return { ...parsed, directory: expandDirectoryPath(parsed.directory ?? ctxDirectory ?? "") };
|
|
114766
|
+
}
|
|
114576
114767
|
function expandDirectoryPath(dir) {
|
|
114577
114768
|
if (!dir) return dir;
|
|
114578
114769
|
if (dir.startsWith("~")) {
|