opencode-feishu-plugin 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -0
- package/README.md +2 -1
- package/dist/index.js +105 -21
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -389,6 +389,7 @@ Without this relay, any clarifying question would stall the Feishu session forev
|
|
|
389
389
|
| `logLevel` | `debug`\|`info`\|`warn`\|`error` | `info` | Log level (secrets are never logged, only their presence) |
|
|
390
390
|
| `logFile` | string \| boolean | — | `true` writes `<configDir>/plugins/feishu.log`. **Plugin stderr is discarded in service mode — enable this when debugging** |
|
|
391
391
|
| `gatewayLocation` | string | — | Only start the gateway in this location **or any of its subdirectories**. `~` is expanded and relative paths / trailing slashes are normalized. **Set this to your usual working directory** to avoid multiple long connections; leave empty to run in every location |
|
|
392
|
+
| `gatewayMatchGraceMs` | number | `3000` | Grace window for **exact-match priority**: a subdirectory candidate waits this long and yields if a location equal to `gatewayLocation` shows up (`0` = no wait, subdirectory takes over immediately) |
|
|
392
393
|
| `approvalTtlMs` | number | `600000` | Approval token / card TTL |
|
|
393
394
|
| `staleExecutionMs` | number | `300000` | Watchdog threshold: an execution with no event for this long is treated as stuck and auto-interrupted; a queue stuck this long without `execution.started` also triggers a notice. Clamped to 1–60 minutes |
|
|
394
395
|
| `maxResourcesShown` | number | `8` | Max resource lines shown on an approval card |
|
package/README.md
CHANGED
|
@@ -249,7 +249,8 @@ agent 调 `question` 等 form 类交互时,插件把它转成飞书卡片:
|
|
|
249
249
|
| `recentDirsLimit` / `recentModelsLimit` | number | `5` | 表单「最近使用」条数(1–20) |
|
|
250
250
|
| `logLevel` | `debug`\|`info`\|`warn`\|`error` | `info` | 日志级别 |
|
|
251
251
|
| `logFile` | string \| boolean | — | `true` = 写 `<configDir>/plugins/feishu.log`;服务模式建议开启 |
|
|
252
|
-
| `gatewayLocation` | string | — | 只在该 location
|
|
252
|
+
| `gatewayLocation` | string | — | 只在该 location(或其**子目录**兜底)启动网关;`~` 自动展开、相对路径/尾斜杠会归一化。留空 = 任意 location 生效 |
|
|
253
|
+
| `gatewayMatchGraceMs` | number | `3000` | **精确匹配优先**的宽限窗口:子目录候选先等这么久,出现 `here === gatewayLocation` 就让位(0 = 不等待,子目录立即兜底) |
|
|
253
254
|
| `approvalTtlMs` | number | `600000` | 审批 token / 卡片有效期 |
|
|
254
255
|
| `staleExecutionMs` | number | `300000` | 看门狗阈值(夹取 1–60 分钟) |
|
|
255
256
|
| `maxResourcesShown` | number | `8` | 审批卡最多展示的资源行数 |
|
package/dist/index.js
CHANGED
|
@@ -144033,6 +144033,7 @@ function resolveConfig2(raw, env = process.env, deps = {}) {
|
|
|
144033
144033
|
const domain = merged.domain === "lark" ? "lark" : "feishu";
|
|
144034
144034
|
const logFile = resolveLogFile(merged.logFile, env, deps);
|
|
144035
144035
|
const gatewayLocation = normalizeGatewayLocation(asString(merged.gatewayLocation));
|
|
144036
|
+
const gatewayMatchGraceMs = clamp4(asNumber(merged.gatewayMatchGraceMs, 3e3), 0, 1e4);
|
|
144036
144037
|
const signSecretRaw = expandEnv(asString(merged.signSecret), env);
|
|
144037
144038
|
const signSecret = signSecretRaw && signSecretRaw.length > 0 ? signSecretRaw : deriveSignSecret(appSecret);
|
|
144038
144039
|
let enabled = true;
|
|
@@ -144062,6 +144063,7 @@ function resolveConfig2(raw, env = process.env, deps = {}) {
|
|
|
144062
144063
|
logLevel,
|
|
144063
144064
|
logFile,
|
|
144064
144065
|
gatewayLocation,
|
|
144066
|
+
gatewayMatchGraceMs,
|
|
144065
144067
|
approvalTtlMs,
|
|
144066
144068
|
staleExecutionMs,
|
|
144067
144069
|
signSecret,
|
|
@@ -144276,6 +144278,45 @@ function releaseProcessGuard() {
|
|
|
144276
144278
|
const g = globalThis;
|
|
144277
144279
|
const slot = g[GUARD_SLOT];
|
|
144278
144280
|
if (slot) slot.active = false;
|
|
144281
|
+
resetExactGateway();
|
|
144282
|
+
}
|
|
144283
|
+
var GATEWAY_EXACT_SLOT = /* @__PURE__ */ Symbol.for("opencode-feishu-v2/gateway-exact");
|
|
144284
|
+
function electionSlot() {
|
|
144285
|
+
const g = globalThis;
|
|
144286
|
+
let slot = g[GATEWAY_EXACT_SLOT];
|
|
144287
|
+
if (!slot) {
|
|
144288
|
+
slot = { exact: false, resolvers: [] };
|
|
144289
|
+
g[GATEWAY_EXACT_SLOT] = slot;
|
|
144290
|
+
}
|
|
144291
|
+
return slot;
|
|
144292
|
+
}
|
|
144293
|
+
function markExactGateway() {
|
|
144294
|
+
const slot = electionSlot();
|
|
144295
|
+
slot.exact = true;
|
|
144296
|
+
if (slot.timer) {
|
|
144297
|
+
clearTimeout(slot.timer);
|
|
144298
|
+
slot.timer = void 0;
|
|
144299
|
+
}
|
|
144300
|
+
for (const resolve4 of slot.resolvers.splice(0)) resolve4(true);
|
|
144301
|
+
}
|
|
144302
|
+
function resetExactGateway() {
|
|
144303
|
+
const g = globalThis;
|
|
144304
|
+
const slot = g[GATEWAY_EXACT_SLOT];
|
|
144305
|
+
if (slot) slot.exact = false;
|
|
144306
|
+
}
|
|
144307
|
+
function waitForExactGateway(graceMs) {
|
|
144308
|
+
const slot = electionSlot();
|
|
144309
|
+
if (slot.exact) return Promise.resolve(true);
|
|
144310
|
+
if (!(graceMs > 0)) return Promise.resolve(false);
|
|
144311
|
+
return new Promise((resolve4) => {
|
|
144312
|
+
slot.resolvers.push(resolve4);
|
|
144313
|
+
if (!slot.timer) {
|
|
144314
|
+
slot.timer = setTimeout(() => {
|
|
144315
|
+
slot.timer = void 0;
|
|
144316
|
+
for (const r of slot.resolvers.splice(0)) r(false);
|
|
144317
|
+
}, graceMs);
|
|
144318
|
+
}
|
|
144319
|
+
});
|
|
144279
144320
|
}
|
|
144280
144321
|
var GATEWAY_SEEN_SLOT = /* @__PURE__ */ Symbol.for("opencode-feishu-v2/gateway-seen");
|
|
144281
144322
|
function trackGatewayLocationSeen(input) {
|
|
@@ -147161,7 +147202,11 @@ async function touchLocationOverHttp(directory, deps) {
|
|
|
147161
147202
|
var WATCHDOG_SLOT = /* @__PURE__ */ Symbol.for("opencode-feishu-v2/gateway-watchdog");
|
|
147162
147203
|
function ensureGatewayWatchdog(input) {
|
|
147163
147204
|
const g = globalThis;
|
|
147164
|
-
|
|
147205
|
+
let existing = g[WATCHDOG_SLOT];
|
|
147206
|
+
if (existing && !existing.state) {
|
|
147207
|
+
clearWatchdogSlot(existing);
|
|
147208
|
+
existing = void 0;
|
|
147209
|
+
}
|
|
147165
147210
|
if (existing) {
|
|
147166
147211
|
if (input.authoritative && existing.target !== input.directory) {
|
|
147167
147212
|
existing.target = input.directory;
|
|
@@ -147201,6 +147246,13 @@ function ensureGatewayWatchdog(input) {
|
|
|
147201
147246
|
});
|
|
147202
147247
|
return true;
|
|
147203
147248
|
}
|
|
147249
|
+
function clearWatchdogSlot(slot) {
|
|
147250
|
+
const s = slot ?? {};
|
|
147251
|
+
if (s.timer) clearInterval(s.timer);
|
|
147252
|
+
if (s.initial) clearTimeout(s.initial);
|
|
147253
|
+
const g = globalThis;
|
|
147254
|
+
if (g[WATCHDOG_SLOT] === slot) delete g[WATCHDOG_SLOT];
|
|
147255
|
+
}
|
|
147204
147256
|
function startKeepalive(deps) {
|
|
147205
147257
|
const setIntervalImpl = deps.setIntervalImpl ?? setInterval;
|
|
147206
147258
|
const clearIntervalImpl = deps.clearIntervalImpl ?? clearInterval;
|
|
@@ -150973,29 +151025,57 @@ var index_default = plugin_exports.define({
|
|
|
150973
151025
|
if (config.keepalive) {
|
|
150974
151026
|
const watchdogTarget = config.gatewayLocation ?? here;
|
|
150975
151027
|
if (watchdogTarget) {
|
|
150976
|
-
|
|
150977
|
-
|
|
150978
|
-
|
|
150979
|
-
|
|
150980
|
-
|
|
151028
|
+
try {
|
|
151029
|
+
startedWatchdog = ensureGatewayWatchdog({
|
|
151030
|
+
log: log2,
|
|
151031
|
+
directory: watchdogTarget,
|
|
151032
|
+
intervalMs: config.keepaliveIntervalMs
|
|
151033
|
+
});
|
|
151034
|
+
} catch (err2) {
|
|
151035
|
+
log2.warn("\u7F51\u5173\u770B\u95E8\u72D7\u542F\u52A8\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09", { error: errorMessage(err2) });
|
|
151036
|
+
}
|
|
150981
151037
|
}
|
|
150982
151038
|
}
|
|
150983
151039
|
if (config.gatewayLocation) {
|
|
150984
|
-
const
|
|
151040
|
+
const exact = Boolean(here) && here === config.gatewayLocation;
|
|
151041
|
+
const sub = Boolean(here) && !exact && isUnder(here, config.gatewayLocation);
|
|
151042
|
+
const matched = exact || sub;
|
|
150985
151043
|
const keepSink = matched || startedWatchdog;
|
|
150986
|
-
|
|
150987
|
-
|
|
150988
|
-
|
|
150989
|
-
|
|
150990
|
-
|
|
150991
|
-
|
|
150992
|
-
|
|
151044
|
+
try {
|
|
151045
|
+
trackGatewayLocationSeen({
|
|
151046
|
+
here: here ?? "(unknown)",
|
|
151047
|
+
expected: config.gatewayLocation,
|
|
151048
|
+
matched,
|
|
151049
|
+
warn: (message) => log2.warn(message),
|
|
151050
|
+
...keepSink ? {} : { onSettled: () => logSink?.close() }
|
|
151051
|
+
});
|
|
151052
|
+
} catch (err2) {
|
|
151053
|
+
log2.warn("\u7F51\u5173\u4F4D\u7F6E\u8DDF\u8E2A\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09", { error: errorMessage(err2) });
|
|
151054
|
+
}
|
|
150993
151055
|
if (!matched) {
|
|
150994
151056
|
log2.debug("\u8DF3\u8FC7\u975E\u7F51\u5173 location", { here, expected: config.gatewayLocation });
|
|
150995
151057
|
if (!keepSink) logSink?.close();
|
|
150996
151058
|
return async () => {
|
|
150997
151059
|
};
|
|
150998
151060
|
}
|
|
151061
|
+
if (exact) {
|
|
151062
|
+
markExactGateway();
|
|
151063
|
+
} else {
|
|
151064
|
+
const exactSeen = await waitForExactGateway(config.gatewayMatchGraceMs);
|
|
151065
|
+
if (exactSeen) {
|
|
151066
|
+
log2.debug("\u7CBE\u786E\u5339\u914D location \u5DF2\u5C31\u7EEA\uFF0C\u5B50\u76EE\u5F55\u5019\u9009\u8BA9\u4F4D", {
|
|
151067
|
+
here,
|
|
151068
|
+
expected: config.gatewayLocation
|
|
151069
|
+
});
|
|
151070
|
+
if (!keepSink) logSink?.close();
|
|
151071
|
+
return async () => {
|
|
151072
|
+
};
|
|
151073
|
+
}
|
|
151074
|
+
log2.info("\u7CBE\u786E\u5339\u914D location \u672A\u51FA\u73B0\uFF0C\u5B50\u76EE\u5F55\u5019\u9009\u63A5\u7BA1\u7F51\u5173", {
|
|
151075
|
+
here,
|
|
151076
|
+
expected: config.gatewayLocation
|
|
151077
|
+
});
|
|
151078
|
+
}
|
|
150999
151079
|
}
|
|
151000
151080
|
if (!acquireProcessGuard()) {
|
|
151001
151081
|
log2.debug("\u68C0\u6D4B\u5230\u540C\u8FDB\u7A0B\u91CD\u590D setup\uFF0C\u8DF3\u8FC7\u542F\u52A8\uFF08\u4EC5\u9996\u4E2A\u5B9E\u4F8B\u751F\u6548\uFF09");
|
|
@@ -151553,13 +151633,17 @@ async function start(ctx, config, log2, logSink) {
|
|
|
151553
151633
|
const here = ctx.location?.directory;
|
|
151554
151634
|
const keepaliveDirectory = here ?? config.gatewayLocation;
|
|
151555
151635
|
if (config.keepalive && keepaliveDirectory) {
|
|
151556
|
-
|
|
151557
|
-
|
|
151558
|
-
|
|
151559
|
-
|
|
151560
|
-
|
|
151561
|
-
|
|
151562
|
-
|
|
151636
|
+
try {
|
|
151637
|
+
ensureGatewayWatchdog({
|
|
151638
|
+
log: log2,
|
|
151639
|
+
directory: keepaliveDirectory,
|
|
151640
|
+
intervalMs: config.keepaliveIntervalMs,
|
|
151641
|
+
authoritative: true,
|
|
151642
|
+
immediateDelayMs: 0
|
|
151643
|
+
});
|
|
151644
|
+
} catch (err2) {
|
|
151645
|
+
log2.warn("\u7F51\u5173\u770B\u95E8\u72D7\u767B\u8BB0\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09", { error: errorMessage(err2) });
|
|
151646
|
+
}
|
|
151563
151647
|
}
|
|
151564
151648
|
const stopKeepalive = config.keepalive && keepaliveDirectory ? startKeepalive({
|
|
151565
151649
|
log: log2,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-feishu-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Feishu/Lark plugin for OpenCode V2 — single-user topics as sessions, in-card permission approvals, streaming cards. Minimal permissions, no public endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|