opencode-feishu 1.0.2 → 1.1.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 +162 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -112509,7 +112509,9 @@ var AutoPromptSchema = external_exports.object({
|
|
|
112509
112509
|
enabled: external_exports.boolean().default(false),
|
|
112510
112510
|
intervalSeconds: external_exports.number().int().positive().default(30),
|
|
112511
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")
|
|
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"),
|
|
@@ -113762,7 +113764,6 @@ var StreamingCard = class {
|
|
|
113762
113764
|
};
|
|
113763
113765
|
|
|
113764
113766
|
// src/handler/chat.ts
|
|
113765
|
-
var activeAutoPrompts = /* @__PURE__ */ new Map();
|
|
113766
113767
|
async function finalizeReply(streamingCard, feishuClient, chatId, placeholderId, text) {
|
|
113767
113768
|
if (streamingCard) {
|
|
113768
113769
|
await streamingCard.close(text);
|
|
@@ -113780,19 +113781,13 @@ async function abortCleanup(streamingCard, feishuClient, placeholderId) {
|
|
|
113780
113781
|
}
|
|
113781
113782
|
async function handleChat(ctx, deps, signal) {
|
|
113782
113783
|
const { content, chatId, chatType, senderId, shouldReply, messageType, rawContent, messageId } = ctx;
|
|
113783
|
-
if (!content.trim() && messageType === "text") return;
|
|
113784
|
+
if (!content.trim() && messageType === "text") return void 0;
|
|
113784
113785
|
const { config: config2, client, feishuClient, log, directory } = deps;
|
|
113785
113786
|
const query = directory ? { directory } : void 0;
|
|
113786
113787
|
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
113788
|
const session = await getOrCreateSession(client, sessionKey, directory);
|
|
113794
113789
|
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log);
|
|
113795
|
-
if (!parts.length) return;
|
|
113790
|
+
if (!parts.length) return void 0;
|
|
113796
113791
|
log("info", "\u6536\u5230\u7528\u6237\u6D88\u606F", {
|
|
113797
113792
|
sessionKey,
|
|
113798
113793
|
sessionId: session.id,
|
|
@@ -113816,7 +113811,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113816
113811
|
error: err instanceof Error ? err.message : String(err)
|
|
113817
113812
|
});
|
|
113818
113813
|
}
|
|
113819
|
-
return;
|
|
113814
|
+
return void 0;
|
|
113820
113815
|
}
|
|
113821
113816
|
const timeout = config2.timeout;
|
|
113822
113817
|
const thinkingDelay = config2.thinkingDelay;
|
|
@@ -113857,46 +113852,6 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113857
113852
|
});
|
|
113858
113853
|
}
|
|
113859
113854
|
}, 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
113855
|
let cardUnsub;
|
|
113901
113856
|
{
|
|
113902
113857
|
const card = streamingCard;
|
|
@@ -113942,12 +113897,15 @@ async function handleChat(ctx, deps, signal) {
|
|
|
113942
113897
|
});
|
|
113943
113898
|
clearRetryAttempts(sessionKey);
|
|
113944
113899
|
await finalizeReply(streamingCard, feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
113945
|
-
|
|
113900
|
+
if (config2.autoPrompt.enabled && shouldReply) {
|
|
113901
|
+
return { sessionId: session.id, sessionKey, chatId, deps };
|
|
113902
|
+
}
|
|
113903
|
+
return void 0;
|
|
113946
113904
|
} catch (err) {
|
|
113947
113905
|
if (err instanceof Error && err.name === "AbortError") {
|
|
113948
113906
|
log("info", "\u5904\u7406\u88AB\u4E2D\u65AD", { sessionKey, sessionId: session.id });
|
|
113949
113907
|
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
113950
|
-
return;
|
|
113908
|
+
return void 0;
|
|
113951
113909
|
}
|
|
113952
113910
|
let sessionError;
|
|
113953
113911
|
if (err instanceof SessionErrorDetected) {
|
|
@@ -114006,14 +113964,16 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114006
113964
|
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
114007
113965
|
attempt: attempts + 1
|
|
114008
113966
|
});
|
|
114009
|
-
|
|
114010
|
-
|
|
113967
|
+
if (config2.autoPrompt.enabled && shouldReply) {
|
|
113968
|
+
return { sessionId: session.id, sessionKey, chatId, deps };
|
|
113969
|
+
}
|
|
113970
|
+
return void 0;
|
|
114011
113971
|
}
|
|
114012
113972
|
} catch (recoveryErr) {
|
|
114013
113973
|
if (recoveryErr instanceof Error && recoveryErr.name === "AbortError") {
|
|
114014
113974
|
log("info", "\u6A21\u578B\u6062\u590D\u88AB\u4E2D\u65AD", { sessionKey });
|
|
114015
113975
|
await abortCleanup(streamingCard, feishuClient, placeholderId);
|
|
114016
|
-
return;
|
|
113976
|
+
return void 0;
|
|
114017
113977
|
}
|
|
114018
113978
|
const errMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
114019
113979
|
if (recoveryErr instanceof SessionErrorDetected) {
|
|
@@ -114145,6 +114105,43 @@ async function replyOrUpdate(feishuClient, chatId, placeholderId, text) {
|
|
|
114145
114105
|
await sendTextMessage(feishuClient, chatId, text);
|
|
114146
114106
|
}
|
|
114147
114107
|
}
|
|
114108
|
+
var idlePatterns = [
|
|
114109
|
+
/^(无|没有)(任务|变化|进行中)/,
|
|
114110
|
+
/空闲|闲置|等待(指令|中|新|你)/,
|
|
114111
|
+
/随时可(开始|开始新)/,
|
|
114112
|
+
/等你指令/
|
|
114113
|
+
];
|
|
114114
|
+
function isIdleResponse(text, maxLength = 50) {
|
|
114115
|
+
if (text.length >= maxLength) return false;
|
|
114116
|
+
return idlePatterns.some((p) => p.test(text));
|
|
114117
|
+
}
|
|
114118
|
+
async function runOneAutoPromptIteration(apCtx, iteration, signal) {
|
|
114119
|
+
const { sessionId, chatId, deps } = apCtx;
|
|
114120
|
+
const { config: config2, client, feishuClient, log, directory } = deps;
|
|
114121
|
+
const query = directory ? { directory } : void 0;
|
|
114122
|
+
const { autoPrompt, timeout, pollInterval, stablePolls } = config2;
|
|
114123
|
+
log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey: apCtx.sessionKey, iteration });
|
|
114124
|
+
clearSessionError(sessionId);
|
|
114125
|
+
await client.session.promptAsync({
|
|
114126
|
+
path: { id: sessionId },
|
|
114127
|
+
query,
|
|
114128
|
+
body: { parts: [{ type: "text", text: autoPrompt.message }] }
|
|
114129
|
+
});
|
|
114130
|
+
const text = await pollForResponse(client, sessionId, {
|
|
114131
|
+
timeout,
|
|
114132
|
+
pollInterval,
|
|
114133
|
+
stablePolls,
|
|
114134
|
+
query,
|
|
114135
|
+
signal
|
|
114136
|
+
});
|
|
114137
|
+
if (!text) return { text: null, isIdle: false };
|
|
114138
|
+
const idle = isIdleResponse(text, autoPrompt.idleMaxLength);
|
|
114139
|
+
if (!idle) {
|
|
114140
|
+
log("info", "\u81EA\u52A8\u63D0\u793A\u54CD\u5E94", { sessionKey: apCtx.sessionKey, iteration, output: text });
|
|
114141
|
+
await sendTextMessage(feishuClient, chatId, text);
|
|
114142
|
+
}
|
|
114143
|
+
return { text, isIdle: idle };
|
|
114144
|
+
}
|
|
114148
114145
|
function abortableSleep(ms, signal) {
|
|
114149
114146
|
return new Promise((resolve, reject) => {
|
|
114150
114147
|
if (signal.aborted) {
|
|
@@ -114191,6 +114188,9 @@ function cleanupStateIfIdle(sessionKey, state) {
|
|
|
114191
114188
|
states.delete(sessionKey);
|
|
114192
114189
|
}
|
|
114193
114190
|
}
|
|
114191
|
+
function sleep(ms) {
|
|
114192
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
114193
|
+
}
|
|
114194
114194
|
async function enqueueMessage(ctx, deps) {
|
|
114195
114195
|
if (!ctx.shouldReply) {
|
|
114196
114196
|
await handleChat(ctx, deps);
|
|
@@ -114221,7 +114221,18 @@ async function handleP2PMessage(sessionKey, ctx, deps) {
|
|
|
114221
114221
|
const controller = new AbortController();
|
|
114222
114222
|
state.controller = controller;
|
|
114223
114223
|
state.processing = true;
|
|
114224
|
-
const task =
|
|
114224
|
+
const task = (async () => {
|
|
114225
|
+
const apCtx = await processMessage(ctx, deps, controller.signal);
|
|
114226
|
+
if (apCtx) {
|
|
114227
|
+
await runP2PAutoPrompt(apCtx, controller.signal);
|
|
114228
|
+
}
|
|
114229
|
+
})().catch((err) => {
|
|
114230
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
114231
|
+
deps.log("error", "P2P \u6D88\u606F\u6216\u81EA\u52A8\u63D0\u793A\u5904\u7406\u5931\u8D25", {
|
|
114232
|
+
sessionKey,
|
|
114233
|
+
error: err instanceof Error ? err.message : String(err)
|
|
114234
|
+
});
|
|
114235
|
+
}).finally(() => {
|
|
114225
114236
|
state.processing = false;
|
|
114226
114237
|
state.controller = null;
|
|
114227
114238
|
state.currentTask = null;
|
|
@@ -114230,6 +114241,31 @@ async function handleP2PMessage(sessionKey, ctx, deps) {
|
|
|
114230
114241
|
state.currentTask = task;
|
|
114231
114242
|
await task;
|
|
114232
114243
|
}
|
|
114244
|
+
async function runP2PAutoPrompt(apCtx, signal) {
|
|
114245
|
+
const { autoPrompt } = apCtx.deps.config;
|
|
114246
|
+
if (!autoPrompt.enabled) return;
|
|
114247
|
+
let idleCount = 0;
|
|
114248
|
+
for (let i = 0; i < autoPrompt.maxIterations; i++) {
|
|
114249
|
+
await abortableSleep(autoPrompt.intervalSeconds * 1e3, signal);
|
|
114250
|
+
const result = await runOneAutoPromptIteration(apCtx, i + 1, signal);
|
|
114251
|
+
if (result.isIdle) {
|
|
114252
|
+
idleCount++;
|
|
114253
|
+
if (idleCount >= autoPrompt.idleThreshold) {
|
|
114254
|
+
apCtx.deps.log("info", "P2P \u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u68C0\u6D4B\u5230\u7A7A\u95F2\uFF09", {
|
|
114255
|
+
sessionKey: apCtx.sessionKey,
|
|
114256
|
+
iteration: i + 1,
|
|
114257
|
+
idleCount
|
|
114258
|
+
});
|
|
114259
|
+
return;
|
|
114260
|
+
}
|
|
114261
|
+
} else {
|
|
114262
|
+
idleCount = 0;
|
|
114263
|
+
}
|
|
114264
|
+
}
|
|
114265
|
+
apCtx.deps.log("info", "P2P \u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", {
|
|
114266
|
+
sessionKey: apCtx.sessionKey
|
|
114267
|
+
});
|
|
114268
|
+
}
|
|
114233
114269
|
async function handleGroupMessage(sessionKey, ctx, deps) {
|
|
114234
114270
|
const state = getOrCreateState(sessionKey);
|
|
114235
114271
|
state.queue.push({ ctx, deps });
|
|
@@ -114238,21 +114274,81 @@ async function handleGroupMessage(sessionKey, ctx, deps) {
|
|
|
114238
114274
|
}
|
|
114239
114275
|
async function drainLoop(sessionKey, state) {
|
|
114240
114276
|
state.processing = true;
|
|
114277
|
+
let autoPromptCtx;
|
|
114278
|
+
let idleCount = 0;
|
|
114279
|
+
let autoPromptIteration = 0;
|
|
114241
114280
|
try {
|
|
114242
|
-
while (
|
|
114243
|
-
|
|
114244
|
-
|
|
114245
|
-
|
|
114246
|
-
|
|
114281
|
+
while (true) {
|
|
114282
|
+
if (state.queue.length > 0) {
|
|
114283
|
+
const item = state.queue.shift();
|
|
114284
|
+
const controller = new AbortController();
|
|
114285
|
+
state.controller = controller;
|
|
114286
|
+
try {
|
|
114287
|
+
autoPromptCtx = await processMessage(item.ctx, item.deps, controller.signal);
|
|
114288
|
+
idleCount = 0;
|
|
114289
|
+
autoPromptIteration = 0;
|
|
114290
|
+
} catch (err) {
|
|
114291
|
+
item.deps.log("error", "\u7FA4\u804A\u961F\u5217\u6D88\u606F\u5904\u7406\u5931\u8D25", {
|
|
114292
|
+
sessionKey,
|
|
114293
|
+
error: err instanceof Error ? err.message : String(err)
|
|
114294
|
+
});
|
|
114295
|
+
} finally {
|
|
114296
|
+
state.controller = null;
|
|
114297
|
+
}
|
|
114298
|
+
continue;
|
|
114299
|
+
}
|
|
114300
|
+
if (!autoPromptCtx) break;
|
|
114301
|
+
const { autoPrompt } = autoPromptCtx.deps.config;
|
|
114302
|
+
if (!autoPrompt.enabled) break;
|
|
114303
|
+
if (autoPromptIteration >= autoPrompt.maxIterations) {
|
|
114304
|
+
autoPromptCtx.deps.log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u8FBE\u5230\u6700\u5927\u6B21\u6570\uFF09", { sessionKey });
|
|
114305
|
+
break;
|
|
114306
|
+
}
|
|
114307
|
+
const intervalSeconds = autoPrompt.intervalSeconds;
|
|
114308
|
+
let interrupted = false;
|
|
114309
|
+
for (let s = 0; s < intervalSeconds; s++) {
|
|
114310
|
+
await sleep(1e3);
|
|
114311
|
+
if (state.queue.length > 0) {
|
|
114312
|
+
interrupted = true;
|
|
114313
|
+
break;
|
|
114314
|
+
}
|
|
114315
|
+
}
|
|
114316
|
+
if (interrupted) continue;
|
|
114317
|
+
const autoPromptController = new AbortController();
|
|
114318
|
+
const monitor = setInterval(() => {
|
|
114319
|
+
if (state.queue.length > 0) autoPromptController.abort();
|
|
114320
|
+
}, 200);
|
|
114247
114321
|
try {
|
|
114248
|
-
await
|
|
114322
|
+
const result = await runOneAutoPromptIteration(
|
|
114323
|
+
autoPromptCtx,
|
|
114324
|
+
autoPromptIteration + 1,
|
|
114325
|
+
autoPromptController.signal
|
|
114326
|
+
);
|
|
114327
|
+
autoPromptIteration++;
|
|
114328
|
+
if (result.isIdle) {
|
|
114329
|
+
idleCount++;
|
|
114330
|
+
if (idleCount >= autoPrompt.idleThreshold) {
|
|
114331
|
+
autoPromptCtx.deps.log("info", "\u81EA\u52A8\u63D0\u793A\u5FAA\u73AF\u7ED3\u675F\uFF08\u68C0\u6D4B\u5230\u7A7A\u95F2\uFF09", {
|
|
114332
|
+
sessionKey,
|
|
114333
|
+
iteration: autoPromptIteration,
|
|
114334
|
+
idleCount
|
|
114335
|
+
});
|
|
114336
|
+
break;
|
|
114337
|
+
}
|
|
114338
|
+
} else {
|
|
114339
|
+
idleCount = 0;
|
|
114340
|
+
}
|
|
114249
114341
|
} catch (err) {
|
|
114250
|
-
|
|
114342
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
114343
|
+
continue;
|
|
114344
|
+
}
|
|
114345
|
+
autoPromptCtx.deps.log("error", "\u81EA\u52A8\u63D0\u793A\u8FED\u4EE3\u5F02\u5E38", {
|
|
114251
114346
|
sessionKey,
|
|
114252
114347
|
error: err instanceof Error ? err.message : String(err)
|
|
114253
114348
|
});
|
|
114349
|
+
break;
|
|
114254
114350
|
} finally {
|
|
114255
|
-
|
|
114351
|
+
clearInterval(monitor);
|
|
114256
114352
|
}
|
|
114257
114353
|
}
|
|
114258
114354
|
} finally {
|
|
@@ -114261,7 +114357,7 @@ async function drainLoop(sessionKey, state) {
|
|
|
114261
114357
|
}
|
|
114262
114358
|
}
|
|
114263
114359
|
async function processMessage(ctx, deps, signal) {
|
|
114264
|
-
|
|
114360
|
+
return handleChat(ctx, deps, signal);
|
|
114265
114361
|
}
|
|
114266
114362
|
async function abortServerSession(sessionKey, deps) {
|
|
114267
114363
|
const { client, log, directory } = deps;
|