opencode-feishu 0.7.8 → 0.7.11
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 +118 -163
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99181,30 +99181,30 @@ var pendingBySession = /* @__PURE__ */ new Map();
|
|
|
99181
99181
|
var sessionErrors = /* @__PURE__ */ new Map();
|
|
99182
99182
|
var sessionErrorTimeouts = /* @__PURE__ */ new Map();
|
|
99183
99183
|
var SESSION_ERROR_TTL_MS = 3e4;
|
|
99184
|
-
var
|
|
99185
|
-
var
|
|
99186
|
-
var
|
|
99187
|
-
var
|
|
99188
|
-
function
|
|
99189
|
-
|
|
99190
|
-
const timer =
|
|
99184
|
+
var retryAttempts = /* @__PURE__ */ new Map();
|
|
99185
|
+
var retryAttemptTimeouts = /* @__PURE__ */ new Map();
|
|
99186
|
+
var MAX_RETRY_ATTEMPTS = 2;
|
|
99187
|
+
var RETRY_ATTEMPTS_TTL_MS = 36e5;
|
|
99188
|
+
function clearRetryAttempts(sessionKey) {
|
|
99189
|
+
retryAttempts.delete(sessionKey);
|
|
99190
|
+
const timer = retryAttemptTimeouts.get(sessionKey);
|
|
99191
99191
|
if (timer) {
|
|
99192
99192
|
clearTimeout(timer);
|
|
99193
|
-
|
|
99193
|
+
retryAttemptTimeouts.delete(sessionKey);
|
|
99194
99194
|
}
|
|
99195
99195
|
}
|
|
99196
|
-
function
|
|
99197
|
-
return
|
|
99196
|
+
function getRetryAttempts(sessionKey) {
|
|
99197
|
+
return retryAttempts.get(sessionKey) ?? 0;
|
|
99198
99198
|
}
|
|
99199
|
-
function
|
|
99200
|
-
|
|
99201
|
-
const existing =
|
|
99199
|
+
function setRetryAttempts(sessionKey, count) {
|
|
99200
|
+
retryAttempts.set(sessionKey, count);
|
|
99201
|
+
const existing = retryAttemptTimeouts.get(sessionKey);
|
|
99202
99202
|
if (existing) clearTimeout(existing);
|
|
99203
99203
|
const timeoutId = setTimeout(() => {
|
|
99204
|
-
|
|
99205
|
-
|
|
99206
|
-
},
|
|
99207
|
-
|
|
99204
|
+
retryAttempts.delete(sessionKey);
|
|
99205
|
+
retryAttemptTimeouts.delete(sessionKey);
|
|
99206
|
+
}, RETRY_ATTEMPTS_TTL_MS);
|
|
99207
|
+
retryAttemptTimeouts.set(sessionKey, timeoutId);
|
|
99208
99208
|
}
|
|
99209
99209
|
function getSessionError(sessionId) {
|
|
99210
99210
|
return sessionErrors.get(sessionId);
|
|
@@ -99249,11 +99249,11 @@ function extractErrorFields(error) {
|
|
|
99249
99249
|
return [String(error)];
|
|
99250
99250
|
}
|
|
99251
99251
|
function isModelError(fields) {
|
|
99252
|
-
const
|
|
99253
|
-
|
|
99254
|
-
|
|
99255
|
-
|
|
99256
|
-
|
|
99252
|
+
const patterns = ["model not found", "modelnotfound", "model not supported", "model_not_supported"];
|
|
99253
|
+
return fields.some((f) => {
|
|
99254
|
+
const l = f.toLowerCase();
|
|
99255
|
+
return patterns.some((p) => l.includes(p));
|
|
99256
|
+
});
|
|
99257
99257
|
}
|
|
99258
99258
|
async function handleEvent(event, deps) {
|
|
99259
99259
|
switch (event.type) {
|
|
@@ -99314,22 +99314,8 @@ function extractPartText(part) {
|
|
|
99314
99314
|
var SESSION_KEY_PREFIX = "feishu";
|
|
99315
99315
|
var TITLE_PREFIX = "Feishu";
|
|
99316
99316
|
var sessionCache = /* @__PURE__ */ new Map();
|
|
99317
|
-
var sessionIdToKeyCache = /* @__PURE__ */ new Map();
|
|
99318
99317
|
function setCachedSession(sessionKey, session) {
|
|
99319
|
-
const oldSession = sessionCache.get(sessionKey);
|
|
99320
|
-
if (oldSession && oldSession.id !== session.id) {
|
|
99321
|
-
sessionIdToKeyCache.delete(oldSession.id);
|
|
99322
|
-
}
|
|
99323
99318
|
sessionCache.set(sessionKey, session);
|
|
99324
|
-
sessionIdToKeyCache.set(session.id, sessionKey);
|
|
99325
|
-
}
|
|
99326
|
-
function invalidateCachedSession(sessionId) {
|
|
99327
|
-
const sessionKey = sessionIdToKeyCache.get(sessionId);
|
|
99328
|
-
if (sessionKey) {
|
|
99329
|
-
sessionCache.delete(sessionKey);
|
|
99330
|
-
sessionIdToKeyCache.delete(sessionId);
|
|
99331
|
-
}
|
|
99332
|
-
return sessionKey;
|
|
99333
99319
|
}
|
|
99334
99320
|
function buildSessionKey(chatType, id) {
|
|
99335
99321
|
return `${SESSION_KEY_PREFIX}-${chatType}-${id}`;
|
|
@@ -99373,57 +99359,6 @@ async function getOrCreateSession(client, sessionKey, directory) {
|
|
|
99373
99359
|
setCachedSession(sessionKey, session);
|
|
99374
99360
|
return session;
|
|
99375
99361
|
}
|
|
99376
|
-
async function forkSession(client, oldSessionId, sessionKey, directory) {
|
|
99377
|
-
const query = directory ? { directory } : void 0;
|
|
99378
|
-
const resp = await client.session.fork({
|
|
99379
|
-
path: { id: oldSessionId },
|
|
99380
|
-
query,
|
|
99381
|
-
body: {}
|
|
99382
|
-
});
|
|
99383
|
-
if (!resp?.data?.id) {
|
|
99384
|
-
const err = resp?.error;
|
|
99385
|
-
throw new Error(
|
|
99386
|
-
`Fork \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99387
|
-
);
|
|
99388
|
-
}
|
|
99389
|
-
const title = generateSessionTitle(sessionKey);
|
|
99390
|
-
const updateResp = await client.session.update({
|
|
99391
|
-
path: { id: resp.data.id },
|
|
99392
|
-
query,
|
|
99393
|
-
body: { title }
|
|
99394
|
-
});
|
|
99395
|
-
if (!updateResp?.data?.id) {
|
|
99396
|
-
const err = updateResp?.error;
|
|
99397
|
-
throw new Error(
|
|
99398
|
-
`\u66F4\u65B0 forked session \u6807\u9898\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99399
|
-
);
|
|
99400
|
-
}
|
|
99401
|
-
return { id: resp.data.id, title };
|
|
99402
|
-
}
|
|
99403
|
-
async function createFreshSession(client, sessionKey, directory) {
|
|
99404
|
-
const query = directory ? { directory } : void 0;
|
|
99405
|
-
const title = generateSessionTitle(sessionKey);
|
|
99406
|
-
const resp = await client.session.create({ query, body: { title } });
|
|
99407
|
-
if (!resp?.data?.id) {
|
|
99408
|
-
const err = resp?.error;
|
|
99409
|
-
throw new Error(
|
|
99410
|
-
`\u521B\u5EFA\u65B0\u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99411
|
-
);
|
|
99412
|
-
}
|
|
99413
|
-
return { id: resp.data.id, title: resp.data.title };
|
|
99414
|
-
}
|
|
99415
|
-
async function forkOrCreateSession(client, oldSessionId, sessionKey, directory, log) {
|
|
99416
|
-
try {
|
|
99417
|
-
return await forkSession(client, oldSessionId, sessionKey, directory);
|
|
99418
|
-
} catch (forkErr) {
|
|
99419
|
-
log?.("warn", "Fork \u5931\u8D25\uFF0C\u56DE\u9000\u5230\u521B\u5EFA\u65B0\u4F1A\u8BDD", {
|
|
99420
|
-
oldSessionId,
|
|
99421
|
-
sessionKey,
|
|
99422
|
-
error: forkErr instanceof Error ? forkErr.message : String(forkErr)
|
|
99423
|
-
});
|
|
99424
|
-
return await createFreshSession(client, sessionKey, directory);
|
|
99425
|
-
}
|
|
99426
|
-
}
|
|
99427
99362
|
|
|
99428
99363
|
// src/handler/chat.ts
|
|
99429
99364
|
var SSE_RACE_WAIT_MS = 100;
|
|
@@ -99501,6 +99436,7 @@ async function handleChat(ctx, deps) {
|
|
|
99501
99436
|
for (let i = 0; i < autoPrompt.maxIterations; i++) {
|
|
99502
99437
|
await abortableSleep(autoPrompt.intervalSeconds * 1e3, ac.signal);
|
|
99503
99438
|
log("info", "\u53D1\u9001\u81EA\u52A8\u63D0\u793A", { sessionKey, iteration: i + 1 });
|
|
99439
|
+
clearSessionError(activeId);
|
|
99504
99440
|
await client.session.prompt({
|
|
99505
99441
|
path: { id: activeId },
|
|
99506
99442
|
query,
|
|
@@ -99531,6 +99467,7 @@ async function handleChat(ctx, deps) {
|
|
|
99531
99467
|
}
|
|
99532
99468
|
}
|
|
99533
99469
|
try {
|
|
99470
|
+
clearSessionError(session.id);
|
|
99534
99471
|
await client.session.prompt({
|
|
99535
99472
|
path: { id: session.id },
|
|
99536
99473
|
query,
|
|
@@ -99542,84 +99479,95 @@ async function handleChat(ctx, deps) {
|
|
|
99542
99479
|
sessionId: session.id,
|
|
99543
99480
|
output: finalText || "(empty)"
|
|
99544
99481
|
});
|
|
99545
|
-
|
|
99482
|
+
clearRetryAttempts(sessionKey);
|
|
99546
99483
|
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99547
99484
|
await runAutoPromptLoop(session.id);
|
|
99548
99485
|
} catch (err) {
|
|
99549
|
-
|
|
99550
|
-
|
|
99551
|
-
|
|
99552
|
-
|
|
99553
|
-
|
|
99554
|
-
|
|
99555
|
-
|
|
99556
|
-
|
|
99486
|
+
let sessionError;
|
|
99487
|
+
if (err instanceof SessionErrorDetected) {
|
|
99488
|
+
sessionError = err.sessionError;
|
|
99489
|
+
clearSessionError(session.id);
|
|
99490
|
+
} else {
|
|
99491
|
+
await new Promise((r) => setTimeout(r, SSE_RACE_WAIT_MS));
|
|
99492
|
+
sessionError = getSessionError(session.id);
|
|
99493
|
+
clearSessionError(session.id);
|
|
99494
|
+
if (!sessionError) {
|
|
99495
|
+
const thrownFields = extractErrorFields(err);
|
|
99496
|
+
if (isModelError(thrownFields)) {
|
|
99497
|
+
const thrownMsg = err instanceof Error ? err.message : String(err);
|
|
99498
|
+
sessionError = { message: thrownMsg, fields: thrownFields };
|
|
99499
|
+
}
|
|
99557
99500
|
}
|
|
99558
99501
|
}
|
|
99559
99502
|
if (sessionError && isModelError(sessionError.fields)) {
|
|
99560
|
-
const attempts =
|
|
99561
|
-
if (attempts <
|
|
99562
|
-
setForkAttempts(sessionKey, attempts + 1);
|
|
99503
|
+
const attempts = getRetryAttempts(sessionKey);
|
|
99504
|
+
if (attempts < MAX_RETRY_ATTEMPTS) {
|
|
99563
99505
|
try {
|
|
99564
|
-
invalidateCachedSession(session.id);
|
|
99565
|
-
const newSession = await forkOrCreateSession(client, session.id, sessionKey, directory, log);
|
|
99566
|
-
setCachedSession(sessionKey, newSession);
|
|
99567
|
-
activeSessionId = newSession.id;
|
|
99568
99506
|
let modelOverride;
|
|
99569
99507
|
try {
|
|
99570
|
-
modelOverride = await
|
|
99571
|
-
|
|
99572
|
-
|
|
99573
|
-
sessionKey,
|
|
99574
|
-
providerID: modelOverride.providerID,
|
|
99575
|
-
modelID: modelOverride.modelID
|
|
99576
|
-
});
|
|
99577
|
-
}
|
|
99578
|
-
} catch (modelErr) {
|
|
99579
|
-
log("warn", "\u89E3\u6790\u964D\u7EA7\u6A21\u578B\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B", {
|
|
99508
|
+
modelOverride = await getGlobalDefaultModel(client, directory);
|
|
99509
|
+
} catch (configErr) {
|
|
99510
|
+
log("warn", "\u8BFB\u53D6\u5168\u5C40\u6A21\u578B\u914D\u7F6E\u5931\u8D25", {
|
|
99580
99511
|
sessionKey,
|
|
99581
|
-
error:
|
|
99512
|
+
error: configErr instanceof Error ? configErr.message : String(configErr)
|
|
99582
99513
|
});
|
|
99583
99514
|
}
|
|
99584
|
-
|
|
99585
|
-
|
|
99586
|
-
|
|
99515
|
+
if (!modelOverride) {
|
|
99516
|
+
log("warn", "\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u672A\u914D\u7F6E\uFF0C\u653E\u5F03\u6062\u590D", { sessionKey });
|
|
99517
|
+
} else {
|
|
99518
|
+
setRetryAttempts(sessionKey, attempts + 1);
|
|
99519
|
+
log("info", "\u4F7F\u7528\u5168\u5C40\u9ED8\u8BA4\u6A21\u578B\u6062\u590D", {
|
|
99520
|
+
sessionKey,
|
|
99521
|
+
providerID: modelOverride.providerID,
|
|
99522
|
+
modelID: modelOverride.modelID
|
|
99523
|
+
});
|
|
99524
|
+
clearSessionError(session.id);
|
|
99525
|
+
await client.session.prompt({
|
|
99526
|
+
path: { id: session.id },
|
|
99527
|
+
query,
|
|
99528
|
+
body: { ...baseBody, model: modelOverride }
|
|
99529
|
+
});
|
|
99530
|
+
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query });
|
|
99531
|
+
log("info", "\u6A21\u578B\u6062\u590D\u540E\u54CD\u5E94\u5B8C\u6210", {
|
|
99532
|
+
sessionKey,
|
|
99533
|
+
sessionId: session.id,
|
|
99534
|
+
output: finalText || "(empty)"
|
|
99535
|
+
});
|
|
99536
|
+
clearRetryAttempts(sessionKey);
|
|
99537
|
+
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99538
|
+
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
99539
|
+
sessionId: session.id,
|
|
99540
|
+
sessionKey,
|
|
99541
|
+
model: `${modelOverride.providerID}/${modelOverride.modelID}`,
|
|
99542
|
+
attempt: attempts + 1
|
|
99543
|
+
});
|
|
99544
|
+
await runAutoPromptLoop(session.id);
|
|
99545
|
+
return;
|
|
99587
99546
|
}
|
|
99588
|
-
const retryBody = { ...baseBody, ...modelOverride ? { model: modelOverride } : {} };
|
|
99589
|
-
await client.session.prompt({
|
|
99590
|
-
path: { id: newSession.id },
|
|
99591
|
-
query,
|
|
99592
|
-
body: retryBody
|
|
99593
|
-
});
|
|
99594
|
-
const finalText = await pollForResponse(client, newSession.id, { timeout, pollInterval, stablePolls, query });
|
|
99595
|
-
log("info", "\u6062\u590D\u540E\u6A21\u578B\u54CD\u5E94\u5B8C\u6210", {
|
|
99596
|
-
sessionKey,
|
|
99597
|
-
newSessionId: newSession.id,
|
|
99598
|
-
output: finalText || "(empty)"
|
|
99599
|
-
});
|
|
99600
|
-
clearForkAttempts(sessionKey);
|
|
99601
|
-
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99602
|
-
log("info", "\u6A21\u578B\u4E0D\u517C\u5BB9\u6062\u590D\u6210\u529F", {
|
|
99603
|
-
oldSessionId: session.id,
|
|
99604
|
-
newSessionId: newSession.id,
|
|
99605
|
-
sessionKey,
|
|
99606
|
-
forkAttempt: attempts + 1
|
|
99607
|
-
});
|
|
99608
|
-
await runAutoPromptLoop(newSession.id);
|
|
99609
|
-
return;
|
|
99610
99547
|
} catch (recoveryErr) {
|
|
99611
|
-
const
|
|
99612
|
-
|
|
99613
|
-
|
|
99614
|
-
|
|
99548
|
+
const errMsg = recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr);
|
|
99549
|
+
if (recoveryErr instanceof SessionErrorDetected) {
|
|
99550
|
+
sessionError = recoveryErr.sessionError;
|
|
99551
|
+
clearSessionError(session.id);
|
|
99552
|
+
} else {
|
|
99553
|
+
await new Promise((r) => setTimeout(r, SSE_RACE_WAIT_MS));
|
|
99554
|
+
const sseError = getSessionError(session.id);
|
|
99555
|
+
if (sseError) {
|
|
99556
|
+
sessionError = sseError;
|
|
99557
|
+
clearSessionError(session.id);
|
|
99558
|
+
} else {
|
|
99559
|
+
const thrownFields = extractErrorFields(recoveryErr);
|
|
99560
|
+
sessionError = { message: errMsg, fields: thrownFields };
|
|
99561
|
+
}
|
|
99562
|
+
}
|
|
99615
99563
|
log("error", "\u6A21\u578B\u6062\u590D\u5931\u8D25", {
|
|
99616
99564
|
sessionId: session.id,
|
|
99617
99565
|
sessionKey,
|
|
99618
|
-
error:
|
|
99566
|
+
error: errMsg
|
|
99619
99567
|
});
|
|
99620
99568
|
}
|
|
99621
99569
|
} else {
|
|
99622
|
-
log("warn", "\u5DF2\u8FBE
|
|
99570
|
+
log("warn", "\u5DF2\u8FBE\u91CD\u8BD5\u4E0A\u9650\uFF0C\u653E\u5F03\u6062\u590D", {
|
|
99623
99571
|
sessionKey,
|
|
99624
99572
|
attempts
|
|
99625
99573
|
});
|
|
@@ -99642,24 +99590,16 @@ async function handleChat(ctx, deps) {
|
|
|
99642
99590
|
unregisterPending(activeSessionId);
|
|
99643
99591
|
}
|
|
99644
99592
|
}
|
|
99645
|
-
async function
|
|
99646
|
-
const pattern = /model not found:?\s*(\w[\w-]*)\//i;
|
|
99647
|
-
const rawProviderID = errorFields.map((f) => pattern.exec(f)?.[1]).find(Boolean);
|
|
99648
|
-
if (!rawProviderID) return void 0;
|
|
99649
|
-
const providerID = rawProviderID.toLowerCase();
|
|
99593
|
+
async function getGlobalDefaultModel(client, directory) {
|
|
99650
99594
|
const query = directory ? { directory } : void 0;
|
|
99651
|
-
const { data } = await client.
|
|
99652
|
-
|
|
99653
|
-
|
|
99654
|
-
|
|
99655
|
-
|
|
99656
|
-
|
|
99657
|
-
|
|
99658
|
-
|
|
99659
|
-
const sortedModels = Object.values(provider.models).filter((m) => m.status !== "deprecated").sort((a, b) => b.release_date.localeCompare(a.release_date));
|
|
99660
|
-
if (sortedModels.length === 0) return void 0;
|
|
99661
|
-
const best = sortedModels.find((m) => m.tool_call) ?? sortedModels[0];
|
|
99662
|
-
return { providerID, modelID: best.id };
|
|
99595
|
+
const { data: config } = await client.config.get({ query });
|
|
99596
|
+
const model = config?.model;
|
|
99597
|
+
if (!model || !model.includes("/")) return void 0;
|
|
99598
|
+
const slash = model.indexOf("/");
|
|
99599
|
+
const providerID = model.slice(0, slash).trim();
|
|
99600
|
+
const modelID = model.slice(slash + 1).trim();
|
|
99601
|
+
if (!providerID || !modelID) return void 0;
|
|
99602
|
+
return { providerID, modelID };
|
|
99663
99603
|
}
|
|
99664
99604
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
99665
99605
|
if (messageType === "text") {
|
|
@@ -99675,6 +99615,13 @@ async function buildPromptParts(feishuClient, messageId, messageType, rawContent
|
|
|
99675
99615
|
}
|
|
99676
99616
|
return parts;
|
|
99677
99617
|
}
|
|
99618
|
+
var SessionErrorDetected = class extends Error {
|
|
99619
|
+
constructor(sessionError) {
|
|
99620
|
+
super(sessionError.message);
|
|
99621
|
+
this.sessionError = sessionError;
|
|
99622
|
+
this.name = "SessionErrorDetected";
|
|
99623
|
+
}
|
|
99624
|
+
};
|
|
99678
99625
|
async function pollForResponse(client, sessionId, opts) {
|
|
99679
99626
|
const { timeout, pollInterval, stablePolls, query, signal } = opts;
|
|
99680
99627
|
const start = Date.now();
|
|
@@ -99686,6 +99633,10 @@ async function pollForResponse(client, sessionId, opts) {
|
|
|
99686
99633
|
} else {
|
|
99687
99634
|
await new Promise((r) => setTimeout(r, pollInterval));
|
|
99688
99635
|
}
|
|
99636
|
+
const sseError = getSessionError(sessionId);
|
|
99637
|
+
if (sseError) {
|
|
99638
|
+
throw new SessionErrorDetected(sseError);
|
|
99639
|
+
}
|
|
99689
99640
|
const { data: messages } = await client.session.messages({ path: { id: sessionId }, query });
|
|
99690
99641
|
const text2 = extractLastAssistantText(messages ?? []);
|
|
99691
99642
|
if (text2 && text2 !== lastText) {
|
|
@@ -99696,6 +99647,10 @@ async function pollForResponse(client, sessionId, opts) {
|
|
|
99696
99647
|
if (sameCount >= stablePolls) break;
|
|
99697
99648
|
}
|
|
99698
99649
|
}
|
|
99650
|
+
const finalSseError = getSessionError(sessionId);
|
|
99651
|
+
if (finalSseError) {
|
|
99652
|
+
throw new SessionErrorDetected(finalSseError);
|
|
99653
|
+
}
|
|
99699
99654
|
const { data: finalMessages } = await client.session.messages({ path: { id: sessionId }, query });
|
|
99700
99655
|
return extractLastAssistantText(finalMessages ?? []) || lastText;
|
|
99701
99656
|
}
|
|
@@ -99932,7 +99887,7 @@ var FeishuPlugin = async (ctx) => {
|
|
|
99932
99887
|
const hooks = {
|
|
99933
99888
|
event: async ({ event }) => {
|
|
99934
99889
|
if (!gateway) return;
|
|
99935
|
-
await handleEvent(event, {
|
|
99890
|
+
await handleEvent(event, { log, directory: resolvedConfig.directory });
|
|
99936
99891
|
}
|
|
99937
99892
|
};
|
|
99938
99893
|
return hooks;
|