dsh-lark-agent 0.3.2 → 0.3.3
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.md +0 -2
- package/dist/bot-menu.d.ts +12 -0
- package/dist/bot-menu.js +14 -0
- package/dist/bridge.js +4 -2
- package/dist/client.cjs +23 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.js +2 -2
- package/dist/live-reply.js +8 -5
- package/dist/onboarding/controller.d.ts +2 -0
- package/dist/onboarding/controller.js +7 -1
- package/dist/onboarding/lark-platform.d.ts +2 -0
- package/dist/onboarding/lark-platform.js +5 -0
- package/dist/onboarding/protocol.d.ts +9 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,8 +30,6 @@ Before you start:
|
|
|
30
30
|
|
|
31
31
|
In the sidebar, open **插件 → 添加插件** (Plugins → Add plugin), enter `dsh-lark-agent`, click **安装** (Install), then **立即启用** (Enable now). Restart if the app asks.
|
|
32
32
|
|
|
33
|
-
<img src="https://raw.githubusercontent.com/Nirvana-Jie/dsh-lark-agent/main/assets/readme/desktop-install-02.jpg" width="720" alt="The Add plugin dialog in the DeepSeek Harness desktop app">
|
|
34
|
-
|
|
35
33
|
### Install with the `dsh` CLI
|
|
36
34
|
|
|
37
35
|
[`dsh plugin`](https://www.npmjs.com/package/@deepseek-ai/dsh) runs pnpm, so pnpm must be on your PATH:
|
package/dist/bot-menu.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export declare const BOT_MENU_COMMANDS: Readonly<{
|
|
|
10
10
|
}>;
|
|
11
11
|
export type BotMenuCommand = (typeof BOT_MENU_COMMANDS)[keyof typeof BOT_MENU_COMMANDS];
|
|
12
12
|
export declare function botMenuCommand(eventKey: unknown): BotMenuCommand | undefined;
|
|
13
|
+
/** A menu entry as the owner adds it in the developer console. */
|
|
14
|
+
export interface BotMenuEntry {
|
|
15
|
+
label: string;
|
|
16
|
+
eventKey: string;
|
|
17
|
+
parent?: string;
|
|
18
|
+
}
|
|
13
19
|
export interface BotMenuNode {
|
|
14
20
|
menu_id: string;
|
|
15
21
|
parent_menu_id?: string;
|
|
@@ -32,6 +38,12 @@ export declare function buildBotMenuAbility(): {
|
|
|
32
38
|
bot_menus: BotMenuNode[];
|
|
33
39
|
};
|
|
34
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* The entries a published menu lacks, by label. A menu configured by hand
|
|
43
|
+
* before a release added an entry stays without it; none is reported when
|
|
44
|
+
* no dsh entry was configured at all.
|
|
45
|
+
*/
|
|
46
|
+
export declare function missingBotMenuEntries(eventKeys: readonly string[]): BotMenuEntry[];
|
|
35
47
|
/** Pure review/export plan: this module never invokes an API or changes an app. */
|
|
36
48
|
export declare function buildBotMenuPlan(appId: string): {
|
|
37
49
|
scope: string;
|
package/dist/bot-menu.js
CHANGED
|
@@ -29,6 +29,20 @@ export function buildBotMenuAbility() {
|
|
|
29
29
|
{ menu_id: 'dsh_permission', parent_menu_id: 'dsh_settings', sort: 2, default_name: '权限模式', menu_content_type: 2, event_key: 'dsh.permission' },
|
|
30
30
|
] } };
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* The entries a published menu lacks, by label. A menu configured by hand
|
|
34
|
+
* before a release added an entry stays without it; none is reported when
|
|
35
|
+
* no dsh entry was configured at all.
|
|
36
|
+
*/
|
|
37
|
+
export function missingBotMenuEntries(eventKeys) {
|
|
38
|
+
const nodes = buildBotMenuAbility().bot.bot_menus, entries = nodes.filter(node => node.event_key);
|
|
39
|
+
if (!entries.some(node => eventKeys.includes(node.event_key)))
|
|
40
|
+
return [];
|
|
41
|
+
return entries.filter(node => !eventKeys.includes(node.event_key)).map(node => {
|
|
42
|
+
const parent = nodes.find(candidate => candidate.menu_id === node.parent_menu_id)?.default_name;
|
|
43
|
+
return { label: node.default_name, eventKey: node.event_key, ...(parent ? { parent } : {}) };
|
|
44
|
+
});
|
|
45
|
+
}
|
|
32
46
|
/** Pure review/export plan: this module never invokes an API or changes an app. */
|
|
33
47
|
export function buildBotMenuPlan(appId) {
|
|
34
48
|
if (typeof appId !== 'string' || !/^cli_[A-Za-z0-9_-]{1,128}$/.test(appId))
|
package/dist/bridge.js
CHANGED
|
@@ -433,9 +433,11 @@ export class BotBridge {
|
|
|
433
433
|
return;
|
|
434
434
|
const controller = new AbortController();
|
|
435
435
|
lane.controller = controller;
|
|
436
|
-
let timedOut = false;
|
|
437
|
-
const timer = setTimeout(() => { timedOut = true; controller.abort(); }, this.config.turnTimeoutMs);
|
|
438
436
|
const harness = message.chatType === 'p2p' && lane.user.directMode === 'harness';
|
|
437
|
+
let timedOut = false;
|
|
438
|
+
// Like the App, a turn runs until it ends or someone sends /stop. Only a configured cap bounds a restricted turn.
|
|
439
|
+
const deadline = harness ? undefined : this.config.turnTimeoutMs;
|
|
440
|
+
const timer = deadline === undefined ? undefined : setTimeout(() => { timedOut = true; controller.abort(); }, deadline);
|
|
439
441
|
let live;
|
|
440
442
|
// Once a live card exists it carries this turn's whole outcome; nothing else is sent as text.
|
|
441
443
|
const conclude = async (final) => {
|
package/dist/client.cjs
CHANGED
|
@@ -111,6 +111,15 @@ function parseSnapshot(value) {
|
|
|
111
111
|
snapshot.documentSetup.qr = { url: officialUrl(qr.url), image, expiresAt: timestamp(qr.expiresAt) };
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
if (data.menu !== void 0) {
|
|
115
|
+
const menu = object(data.menu);
|
|
116
|
+
if (!Array.isArray(menu.missing) || !menu.missing.length || menu.missing.length > 10) throw new Error("invalid menu");
|
|
117
|
+
snapshot.menu = { url: officialUrl(menu.url), missing: menu.missing.map((value2) => {
|
|
118
|
+
const entry = object(value2);
|
|
119
|
+
if (typeof entry.eventKey !== "string" || !/^dsh\.[a-z]{1,30}$/.test(entry.eventKey)) throw new Error("invalid menu");
|
|
120
|
+
return { label: text(entry.label, 60), eventKey: entry.eventKey, ...entry.parent === void 0 ? {} : { parent: text(entry.parent, 60) } };
|
|
121
|
+
}) };
|
|
122
|
+
}
|
|
114
123
|
if (data.update !== void 0) {
|
|
115
124
|
const update = object(data.update);
|
|
116
125
|
const version = (value2) => {
|
|
@@ -330,6 +339,18 @@ function Steps({ active }) {
|
|
|
330
339
|
label
|
|
331
340
|
] }, label)) });
|
|
332
341
|
}
|
|
342
|
+
function MenuNotice({ menu, phase }) {
|
|
343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-update", role: "status", children: [
|
|
344
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "\u98DE\u4E66\u5E95\u90E8\u83DC\u5355\u7F3A\u5C11\u5165\u53E3" }),
|
|
345
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: menu.missing.map((entry) => `${entry.parent ? `${entry.parent} \u2192 ` : ""}${entry.label}\uFF08\u4E8B\u4EF6\u6807\u8BC6 ${entry.eventKey}\uFF09`).join("\u3001") }),
|
|
346
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ol", { className: "lark-instructions", children: [
|
|
347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u5728\u98DE\u4E66\u5F00\u53D1\u8005\u540E\u53F0\u6253\u5F00\u300C\u673A\u5668\u4EBA\u81EA\u5B9A\u4E49\u83DC\u5355\u300D\u8865\u4E0A\uFF0C\u54CD\u5E94\u52A8\u4F5C\u9009\u300C\u63A8\u9001\u4E8B\u4EF6\u300D\uFF0C\u4E8B\u4EF6\u6807\u8BC6\u9010\u5B57\u586B\u5199\u3002" }),
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u521B\u5EFA\u7248\u672C\u5E76\u53D1\u5E03\uFF0C\u5BA2\u6237\u7AEF\u7EA6 5 \u5206\u949F\u540E\u66F4\u65B0\u3002" }),
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: phase === "paused" ? "\u56DE\u5230\u8FD9\u91CC\u70B9\u300C\u6062\u590D\u8FDE\u63A5\u300D\u3002" : "\u56DE\u5230\u8FD9\u91CC\u70B9\u300C\u6682\u505C\u8FDE\u63A5\u300D\uFF0C\u518D\u70B9\u300C\u6062\u590D\u8FDE\u63A5\u300D\u3002" })
|
|
350
|
+
] }),
|
|
351
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lark-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(External, { url: menu.url, children: "\u6253\u5F00\u98DE\u4E66\u540E\u53F0" }) })
|
|
352
|
+
] });
|
|
353
|
+
}
|
|
333
354
|
function UpdateNotice({ update, host, phase }) {
|
|
334
355
|
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
335
356
|
const [status, setStatus] = (0, import_react2.useState)("");
|
|
@@ -594,6 +615,7 @@ function ConnectionCard({ api, host }) {
|
|
|
594
615
|
] }),
|
|
595
616
|
!snapshot.capabilities.documents && !snapshot.documentSetup && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", disabled, onClick: () => void mutate("enable-documents"), children: "\u8865\u5145\u9ED8\u8BA4\u6743\u9650" })
|
|
596
617
|
] }),
|
|
618
|
+
snapshot.menu && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuNotice, { menu: snapshot.menu, phase }),
|
|
597
619
|
snapshot.documentSetup && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-notice", role: "status", children: [
|
|
598
620
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: snapshot.documentSetup.message }),
|
|
599
621
|
snapshot.documentSetup.phase === "preparing" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "lark-spinner", "aria-label": "\u6B63\u5728\u51C6\u5907\u6388\u6743" }),
|
|
@@ -671,7 +693,7 @@ function ConnectionCard({ api, host }) {
|
|
|
671
693
|
// src/client/styles.ts
|
|
672
694
|
var css = `
|
|
673
695
|
.dsh-lark-settings{--lark-ink:#263247;--lark-muted:#738096;--lark-line:#e4eaf2;--lark-blue:#4268df;--lark-surface:#fff;--lark-soft:#f5f8fd;font:14px/1.65 -apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;color:var(--lark-ink);max-width:720px;margin:0 auto;padding:8px 0 20px;box-sizing:border-box}
|
|
674
|
-
.dsh-lark-settings *{box-sizing:border-box}.dsh-lark-settings button,.dsh-lark-settings input{font:inherit}.dsh-lark-settings button,.dsh-lark-settings summary{cursor:pointer}.dsh-lark-settings button:disabled{opacity:.5;cursor:not-allowed}.dsh-lark-settings button:focus-visible,.dsh-lark-settings a:focus-visible,.dsh-lark-settings input:focus-visible,.dsh-lark-settings summary:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.dsh-lark-settings a{overflow-wrap:anywhere}.lark-heading{margin:0 0 22px}.lark-heading h2{font-size:23px;font-weight:650;margin:0 0 5px;letter-spacing:-.4px}.lark-heading p,.lark-card-header p{color:var(--lark-muted);font-size:13px;margin:0}.lark-card{border:1px solid var(--lark-line);border-radius:15px;background:var(--lark-surface);overflow:hidden;box-shadow:0 2px 5px #27385504}.lark-card-header{display:flex;align-items:center;gap:12px;padding:19px 23px;border-bottom:1px solid var(--lark-line)}.lark-plugin-icon{width:43px;height:43px;object-fit:contain;flex-shrink:0}.lark-card-header h2{font-size:15px;font-weight:650;line-height:1.6;margin:0}.lark-card-header p{font-size:12px}.lark-status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--lark-muted);white-space:nowrap}.lark-status i{width:6px;height:6px;border-radius:50%;background:#9da9bc}.lark-status.online{color:#248470}.lark-status.online i{background:#248470}.lark-body{padding:28px;position:relative}.lark-body h3{font-size:20px;font-weight:650;letter-spacing:-.25px;margin:0 0 9px;line-height:1.5}.lark-copy{color:var(--lark-muted);font-size:13px;margin:0 0 20px}.lark-centered{text-align:center;line-height:1.9}.lark-body .lark-intro-title{text-align:center;font-size:21px}.lark-brands{display:flex;justify-content:center;align-items:center;gap:16px;margin:5px 0 25px}.lark-brand{width:53px;height:53px;display:grid;place-items:center;border-radius:14px;background:var(--lark-soft);border:1px solid var(--lark-line)}.lark-brand img{width:37px;height:37px;object-fit:contain}.lark-brand-line{width:78px;height:1px;background:#dbe3ee;position:relative}.lark-brand-line:after{content:"";position:absolute;left:calc(50% - 3px);top:-3px;width:7px;height:7px;border-radius:50%;background:#a9bbdc;border:2px solid var(--lark-surface)}.lark-brands-connected .lark-brand-line{background:#99d1c1}.lark-brands-connected .lark-brand-line:after{background:#248470}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:38px;padding:8px 16px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:550;border:1px solid transparent;line-height:1.5;transition:background .14s}.lark-primary{background:var(--lark-blue);color:#fff}.lark-primary:hover{background:#3559ca}.lark-secondary{color:#485b79;background:var(--lark-surface);border-color:#dce3ed}.lark-secondary:hover{background:var(--lark-soft)}.lark-quiet{background:transparent;color:var(--lark-muted);font-weight:450}.lark-quiet:hover{background:var(--lark-soft)}.lark-danger{color:#ad4949;border-color:#e5bcbc;background:var(--lark-surface)}.lark-text-button{display:inline-flex;align-items:center;gap:7px;background:transparent;border:0;padding:5px 0;color:#5875a5;font-size:12px}.lark-entry{display:flex;flex-direction:column;align-items:center;gap:10px}.lark-entry .lark-primary{min-width:200px}.lark-actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:23px}.lark-spread{justify-content:space-between}.lark-center{justify-content:center}.lark-footnote{color:var(--lark-muted);font-size:11px;text-align:center;margin:18px 0 0}.lark-left{text-align:left}.lark-bottom-note{text-align:center;color:var(--lark-muted);font-size:11px;margin:15px 0 0}.lark-steps{display:flex;list-style:none;gap:12px;margin:0 0 25px;padding:0;color:#8b98ad;font-size:11px}.lark-steps li{display:flex;gap:6px;align-items:center;white-space:nowrap}.lark-steps li+li:before{content:"";width:23px;height:1px;background:var(--lark-line);margin-right:5px}.lark-steps li span{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:var(--lark-soft);font-size:10px}.lark-steps .active{color:var(--lark-blue);font-weight:600}.lark-steps .active span{background:#edf2ff}.lark-steps .done{color:#248470}.lark-steps .done span{background:#edf8f4}.lark-qr-layout{display:flex;align-items:center;gap:25px;margin:23px 0}.lark-qr{position:relative;width:166px;height:166px;padding:10px;background:white;border:1px solid var(--lark-line);border-radius:12px;flex-shrink:0}.lark-qr img{width:100%;height:100%;object-fit:contain}.lark-qr.expired img{opacity:.12}.lark-qr>span{position:absolute;inset:0;display:grid;place-items:center;color:#64758e;font-weight:600}.lark-qr-layout strong{font-weight:600;font-size:13px}.lark-qr-layout .lark-copy{margin:7px 0 10px}.lark-time{color:var(--lark-muted);font-size:11px;font-variant-numeric:tabular-nums}.lark-link-details{border-top:1px solid var(--lark-line);padding:13px 0 0;font-size:12px;color:var(--lark-muted)}.lark-link-details summary{margin-bottom:12px}.lark-raw-url{font-size:10px;line-height:1.65;overflow-wrap:anywhere;margin-top:10px;color:var(--lark-muted);user-select:all}.lark-notice{padding:13px 15px;background:#eef3fe;border-radius:9px;color:#526c95;font-size:12px;line-height:1.8}.lark-notice.warning{background:#fff7e9;color:#8b671e}.lark-state-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#edf8f4;color:#248470;font-size:20px;margin:2px 0 18px}.lark-state-icon.warning{background:#fff7e9;color:#9b7530}.lark-checks{padding:0;list-style:none;margin:20px 0;font-size:13px;color:var(--lark-muted)}.lark-checks li{padding:10px 0;border-bottom:1px solid var(--lark-line)}.lark-checks li:before{content:"\xB7";margin-right:12px}.lark-pairing{background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:11px;padding:17px 18px;display:flex;align-items:center;justify-content:space-between;gap:13px}.lark-pair-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:21px;letter-spacing:.02em;color:#3d5a91;margin:4px 0 2px;overflow-wrap:anywhere;user-select:all}.lark-pairing.expired .lark-pair-code{opacity:.5}.lark-caption{font-size:11px;color:var(--lark-muted)}.lark-copy-status{min-height:18px;font-size:11px;color:#248470;margin:5px 0}.lark-instructions{padding-left:20px;color:#536784;font-size:13px}.lark-instructions li{padding:3px 0}.lark-owner{padding:17px 18px;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:10px;display:grid;gap:6px}.lark-owner strong{font-size:15px;font-weight:600;overflow-wrap:anywhere}.lark-connected-row{border-top:1px solid var(--lark-line);border-bottom:1px solid var(--lark-line);display:flex;justify-content:space-between;align-items:center;gap:16px;padding:16px 0;margin:26px 0 0}.lark-connected-row strong{font-size:12px;font-weight:600}.lark-connected-row p{font-size:11px;color:var(--lark-muted);margin:3px 0 0}.lark-advanced{font-size:12px;color:var(--lark-muted);padding-top:14px}.lark-advanced p{margin:9px 0 0;line-height:1.9}.lark-disconnect{border:1px solid #eacbcb;padding:14px;border-radius:10px;margin-top:16px;font-size:12px}.lark-disconnect p{margin:0}.lark-disconnect .lark-actions{margin-top:12px}.lark-profile-list{border:0;margin:20px 0 0;padding:0;display:grid;gap:10px;min-width:0}.lark-profile{display:flex;align-items:center;gap:12px;border:1px solid var(--lark-line);border-radius:10px;padding:15px;background:var(--lark-soft);cursor:pointer;overflow-wrap:anywhere}.lark-profile input{accent-color:var(--lark-blue)}.lark-error{display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff1f1;border:1px solid #f0d5d5;color:#9b4e4e;border-radius:9px;padding:11px 13px;font-size:12px;margin-bottom:21px}.lark-error .lark-text-button{flex-shrink:0;color:#9b4e4e}.lark-loading{padding:27px 0;text-align:center}.lark-loading .lark-spinner{margin:0 auto 17px;display:block}.lark-loading p{font-size:13px;color:var(--lark-muted);margin:0}.lark-loading-row{display:flex;align-items:center;gap:12px;color:var(--lark-muted);font-size:13px;margin:4px 0 15px}.lark-loading-row h3{color:var(--lark-ink);margin:0}.lark-spinner{display:inline-block;width:18px;height:18px;border:2px solid #dfe6f4;border-top-color:var(--lark-blue);border-radius:50%;animation:lark-spin .85s linear infinite;flex-shrink:0}.lark-pending{display:flex;align-items:center;gap:9px;margin-top:19px;font-size:11px;color:var(--lark-muted)}.lark-pending .lark-spinner{width:13px;height:13px}.lark-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.lark-update{border:1px solid #cfdcf8;background:#f4f7ff;border-radius:11px;padding:14px 16px;margin:0 0 22px}.lark-update-row{display:flex;align-items:center;justify-content:space-between;gap:14px}.lark-update strong{font-size:13px;font-weight:600}.lark-update p{font-size:12px;color:var(--lark-muted);margin:3px 0 0}.lark-update .lark-instructions{margin:12px 0 0}.lark-update .lark-actions{margin-top:12px}.lark-update .lark-copy-status{margin:6px 0 0}.lark-command{display:block;margin-top:6px;padding:8px 10px;border-radius:7px;background:var(--lark-surface);border:1px solid var(--lark-line);font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere;user-select:all}
|
|
696
|
+
.dsh-lark-settings *{box-sizing:border-box}.dsh-lark-settings button,.dsh-lark-settings input{font:inherit}.dsh-lark-settings button,.dsh-lark-settings summary{cursor:pointer}.dsh-lark-settings button:disabled{opacity:.5;cursor:not-allowed}.dsh-lark-settings button:focus-visible,.dsh-lark-settings a:focus-visible,.dsh-lark-settings input:focus-visible,.dsh-lark-settings summary:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.dsh-lark-settings a{overflow-wrap:anywhere}.lark-heading{margin:0 0 22px}.lark-heading h2{font-size:23px;font-weight:650;margin:0 0 5px;letter-spacing:-.4px}.lark-heading p,.lark-card-header p{color:var(--lark-muted);font-size:13px;margin:0}.lark-card{border:1px solid var(--lark-line);border-radius:15px;background:var(--lark-surface);overflow:hidden;box-shadow:0 2px 5px #27385504}.lark-card-header{display:flex;align-items:center;gap:12px;padding:19px 23px;border-bottom:1px solid var(--lark-line)}.lark-plugin-icon{width:43px;height:43px;object-fit:contain;flex-shrink:0}.lark-card-header h2{font-size:15px;font-weight:650;line-height:1.6;margin:0}.lark-card-header p{font-size:12px}.lark-status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--lark-muted);white-space:nowrap}.lark-status i{width:6px;height:6px;border-radius:50%;background:#9da9bc}.lark-status.online{color:#248470}.lark-status.online i{background:#248470}.lark-body{padding:28px;position:relative}.lark-body h3{font-size:20px;font-weight:650;letter-spacing:-.25px;margin:0 0 9px;line-height:1.5}.lark-copy{color:var(--lark-muted);font-size:13px;margin:0 0 20px}.lark-centered{text-align:center;line-height:1.9}.lark-body .lark-intro-title{text-align:center;font-size:21px}.lark-brands{display:flex;justify-content:center;align-items:center;gap:16px;margin:5px 0 25px}.lark-brand{width:53px;height:53px;display:grid;place-items:center;border-radius:14px;background:var(--lark-soft);border:1px solid var(--lark-line)}.lark-brand img{width:37px;height:37px;object-fit:contain}.lark-brand-line{width:78px;height:1px;background:#dbe3ee;position:relative}.lark-brand-line:after{content:"";position:absolute;left:calc(50% - 3px);top:-3px;width:7px;height:7px;border-radius:50%;background:#a9bbdc;border:2px solid var(--lark-surface)}.lark-brands-connected .lark-brand-line{background:#99d1c1}.lark-brands-connected .lark-brand-line:after{background:#248470}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:38px;padding:8px 16px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:550;border:1px solid transparent;line-height:1.5;transition:background .14s}.lark-primary{background:var(--lark-blue);color:#fff}.lark-primary:hover{background:#3559ca}.lark-secondary{color:#485b79;background:var(--lark-surface);border-color:#dce3ed}.lark-secondary:hover{background:var(--lark-soft)}.lark-quiet{background:transparent;color:var(--lark-muted);font-weight:450}.lark-quiet:hover{background:var(--lark-soft)}.lark-danger{color:#ad4949;border-color:#e5bcbc;background:var(--lark-surface)}.lark-text-button{display:inline-flex;align-items:center;gap:7px;background:transparent;border:0;padding:5px 0;color:#5875a5;font-size:12px}.lark-entry{display:flex;flex-direction:column;align-items:center;gap:10px}.lark-entry .lark-primary{min-width:200px}.lark-actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:23px}.lark-spread{justify-content:space-between}.lark-center{justify-content:center}.lark-footnote{color:var(--lark-muted);font-size:11px;text-align:center;margin:18px 0 0}.lark-left{text-align:left}.lark-bottom-note{text-align:center;color:var(--lark-muted);font-size:11px;margin:15px 0 0}.lark-steps{display:flex;list-style:none;gap:12px;margin:0 0 25px;padding:0;color:#8b98ad;font-size:11px}.lark-steps li{display:flex;gap:6px;align-items:center;white-space:nowrap}.lark-steps li+li:before{content:"";width:23px;height:1px;background:var(--lark-line);margin-right:5px}.lark-steps li span{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:var(--lark-soft);font-size:10px}.lark-steps .active{color:var(--lark-blue);font-weight:600}.lark-steps .active span{background:#edf2ff}.lark-steps .done{color:#248470}.lark-steps .done span{background:#edf8f4}.lark-qr-layout{display:flex;align-items:center;gap:25px;margin:23px 0}.lark-qr{position:relative;width:166px;height:166px;padding:10px;background:white;border:1px solid var(--lark-line);border-radius:12px;flex-shrink:0}.lark-qr img{width:100%;height:100%;object-fit:contain}.lark-qr.expired img{opacity:.12}.lark-qr>span{position:absolute;inset:0;display:grid;place-items:center;color:#64758e;font-weight:600}.lark-qr-layout strong{font-weight:600;font-size:13px}.lark-qr-layout .lark-copy{margin:7px 0 10px}.lark-time{color:var(--lark-muted);font-size:11px;font-variant-numeric:tabular-nums}.lark-link-details{border-top:1px solid var(--lark-line);padding:13px 0 0;font-size:12px;color:var(--lark-muted)}.lark-link-details summary{margin-bottom:12px}.lark-raw-url{font-size:10px;line-height:1.65;overflow-wrap:anywhere;margin-top:10px;color:var(--lark-muted);user-select:all}.lark-notice{padding:13px 15px;background:#eef3fe;border-radius:9px;color:#526c95;font-size:12px;line-height:1.8}.lark-notice.warning{background:#fff7e9;color:#8b671e}.lark-state-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#edf8f4;color:#248470;font-size:20px;margin:2px 0 18px}.lark-state-icon.warning{background:#fff7e9;color:#9b7530}.lark-checks{padding:0;list-style:none;margin:20px 0;font-size:13px;color:var(--lark-muted)}.lark-checks li{padding:10px 0;border-bottom:1px solid var(--lark-line)}.lark-checks li:before{content:"\xB7";margin-right:12px}.lark-pairing{background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:11px;padding:17px 18px;display:flex;align-items:center;justify-content:space-between;gap:13px}.lark-pair-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:21px;letter-spacing:.02em;color:#3d5a91;margin:4px 0 2px;overflow-wrap:anywhere;user-select:all}.lark-pairing.expired .lark-pair-code{opacity:.5}.lark-caption{font-size:11px;color:var(--lark-muted)}.lark-copy-status{min-height:18px;font-size:11px;color:#248470;margin:5px 0}.lark-instructions{padding-left:20px;color:#536784;font-size:13px}.lark-instructions li{padding:3px 0}.lark-owner{padding:17px 18px;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:10px;display:grid;gap:6px}.lark-owner strong{font-size:15px;font-weight:600;overflow-wrap:anywhere}.lark-connected-row{border-top:1px solid var(--lark-line);border-bottom:1px solid var(--lark-line);display:flex;justify-content:space-between;align-items:center;gap:16px;padding:16px 0;margin:26px 0 0}.lark-connected-row strong{font-size:12px;font-weight:600}.lark-connected-row p{font-size:11px;color:var(--lark-muted);margin:3px 0 0}.lark-advanced{font-size:12px;color:var(--lark-muted);padding-top:14px}.lark-advanced p{margin:9px 0 0;line-height:1.9}.lark-disconnect{border:1px solid #eacbcb;padding:14px;border-radius:10px;margin-top:16px;font-size:12px}.lark-disconnect p{margin:0}.lark-disconnect .lark-actions{margin-top:12px}.lark-profile-list{border:0;margin:20px 0 0;padding:0;display:grid;gap:10px;min-width:0}.lark-profile{display:flex;align-items:center;gap:12px;border:1px solid var(--lark-line);border-radius:10px;padding:15px;background:var(--lark-soft);cursor:pointer;overflow-wrap:anywhere}.lark-profile input{accent-color:var(--lark-blue)}.lark-error{display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff1f1;border:1px solid #f0d5d5;color:#9b4e4e;border-radius:9px;padding:11px 13px;font-size:12px;margin-bottom:21px}.lark-error .lark-text-button{flex-shrink:0;color:#9b4e4e}.lark-loading{padding:27px 0;text-align:center}.lark-loading .lark-spinner{margin:0 auto 17px;display:block}.lark-loading p{font-size:13px;color:var(--lark-muted);margin:0}.lark-loading-row{display:flex;align-items:center;gap:12px;color:var(--lark-muted);font-size:13px;margin:4px 0 15px}.lark-loading-row h3{color:var(--lark-ink);margin:0}.lark-spinner{display:inline-block;width:18px;height:18px;border:2px solid #dfe6f4;border-top-color:var(--lark-blue);border-radius:50%;animation:lark-spin .85s linear infinite;flex-shrink:0}.lark-pending{display:flex;align-items:center;gap:9px;margin-top:19px;font-size:11px;color:var(--lark-muted)}.lark-pending .lark-spinner{width:13px;height:13px}.lark-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.lark-update{border:1px solid #cfdcf8;background:#f4f7ff;border-radius:11px;padding:14px 16px;margin:0 0 22px}.lark-connected-row+.lark-update{margin:16px 0 0}.lark-update-row{display:flex;align-items:center;justify-content:space-between;gap:14px}.lark-update strong{font-size:13px;font-weight:600}.lark-update p{font-size:12px;color:var(--lark-muted);margin:3px 0 0}.lark-update .lark-instructions{margin:12px 0 0}.lark-update .lark-actions{margin-top:12px}.lark-update .lark-copy-status{margin:6px 0 0}.lark-command{display:block;margin-top:6px;padding:8px 10px;border-radius:7px;background:var(--lark-surface);border:1px solid var(--lark-line);font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere;user-select:all}
|
|
675
697
|
@keyframes lark-spin{to{transform:rotate(360deg)}}
|
|
676
698
|
@media(prefers-color-scheme:dark){.dsh-lark-settings{--lark-ink:#e0e7f1;--lark-muted:#96a6bd;--lark-line:#364357;--lark-surface:#202a39;--lark-soft:#283448;--lark-blue:#5077ec}.lark-secondary{color:#c6d5ed;border-color:#46536b}.lark-pair-code{color:#b9ceff}.lark-instructions{color:#b0c2de}.lark-notice{background:#273d5a;color:#c2d3ec}.lark-notice.warning,.lark-state-icon.warning{background:#44391f;color:#e4c886}.lark-error{background:#492e32;border-color:#684047;color:#edbec2}.lark-error .lark-text-button{color:#edbec2}.lark-steps .active span{background:#293e6b}.lark-steps .done span,.lark-state-icon{background:#23483f}.lark-status.online,.lark-steps .done{color:#78c5b0}.lark-update{background:#24324a;border-color:#3a4d70}}
|
|
677
699
|
@media(max-width:520px){.lark-heading{margin-bottom:17px}.lark-card-header{padding:16px;gap:10px}.lark-card-header p{display:none}.lark-plugin-icon{width:37px;height:37px}.lark-body{padding:21px 18px}.lark-steps{gap:7px}.lark-steps li+li:before{width:12px;margin-right:2px}.lark-qr-layout{gap:17px;align-items:flex-start}.lark-qr{width:138px;height:138px}.lark-qr-layout strong{font-size:12px}.lark-qr-layout .lark-copy{font-size:12px}.lark-pairing{align-items:flex-start;flex-direction:column}.lark-pair-code{font-size:19px}.lark-body h3{font-size:19px}.lark-connected-row{gap:10px}.lark-actions{gap:8px}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{padding:8px 12px}.lark-copy{font-size:12px}.lark-update-row{flex-direction:column;align-items:flex-start}}
|
package/dist/config.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export interface BotConfig {
|
|
|
19
19
|
provider?: string;
|
|
20
20
|
model?: string;
|
|
21
21
|
users: UserPolicy[];
|
|
22
|
-
|
|
22
|
+
/** Optional cap on a restricted turn; unset, a turn runs until done or /stop. */
|
|
23
|
+
turnTimeoutMs?: number;
|
|
23
24
|
idleTimeoutMs: number;
|
|
24
25
|
maxSessions: number;
|
|
25
26
|
maxSessionsPerUser: number;
|
package/dist/config.js
CHANGED
|
@@ -73,8 +73,8 @@ export function parseConfig(value, baseDir = process.cwd()) {
|
|
|
73
73
|
provider: data.provider === undefined ? undefined : string(data.provider, 'provider'),
|
|
74
74
|
model: data.model === undefined ? undefined : string(data.model, 'model'),
|
|
75
75
|
users,
|
|
76
|
-
//
|
|
77
|
-
|
|
76
|
+
// An agent's work can take an hour. Only an administrator's explicit cap bounds restricted turns.
|
|
77
|
+
...(data.turnTimeoutMs === undefined ? {} : { turnTimeoutMs: integer(data.turnTimeoutMs, 0, 1000, 1_800_000, 'turnTimeoutMs') }),
|
|
78
78
|
idleTimeoutMs: integer(data.idleTimeoutMs, 1_800_000, 1000, 86_400_000, 'idleTimeoutMs'),
|
|
79
79
|
maxSessions: integer(data.maxSessions, 100, 1, 1000, 'maxSessions'),
|
|
80
80
|
maxSessionsPerUser: integer(data.maxSessionsPerUser, 10, 1, 100, 'maxSessionsPerUser'),
|
package/dist/live-reply.js
CHANGED
|
@@ -14,6 +14,9 @@ const TRUNCATED = '回复超过了飞书卡片的长度上限,后面的内容
|
|
|
14
14
|
const grey = (markdown) => `<font color='grey'>${markdown}</font>`;
|
|
15
15
|
const bytes = (value) => Buffer.byteLength(typeof value === 'string' ? value : JSON.stringify(value), 'utf8');
|
|
16
16
|
const code = (value) => '`' + value.replace(/`/g, 'ˋ') + '`';
|
|
17
|
+
// A harness turn may run for an hour; past a minute, seconds stop being readable.
|
|
18
|
+
const elapsed = (seconds) => seconds < 60 ? `${seconds} 秒`
|
|
19
|
+
: seconds < 3600 ? `${Math.floor(seconds / 60)} 分 ${seconds % 60} 秒` : `${Math.floor(seconds / 3600)} 小时 ${Math.floor(seconds % 3600 / 60)} 分`;
|
|
17
20
|
/** The App's step labels and a matching Feishu icon, by tool name. */
|
|
18
21
|
function activity(name) {
|
|
19
22
|
const is = (...names) => names.includes(name);
|
|
@@ -404,7 +407,7 @@ export class LiveReply {
|
|
|
404
407
|
}
|
|
405
408
|
panelTitle() {
|
|
406
409
|
const tools = this.tools ? ` · ${this.tools} 次工具调用` : '';
|
|
407
|
-
const seconds = (end) => Math.max(1, Math.round(((end ?? this.now()) - this.startedAt) / 1000));
|
|
410
|
+
const seconds = (end) => elapsed(Math.max(1, Math.round(((end ?? this.now()) - this.startedAt) / 1000)));
|
|
408
411
|
switch (this.phase) {
|
|
409
412
|
case 'thinking': {
|
|
410
413
|
const running = [...this.steps].reverse().find(step => step.kind === 'tool' && step.state === 'running');
|
|
@@ -413,10 +416,10 @@ export class LiveReply {
|
|
|
413
416
|
const detail = running.detail ? plainLabel(running.detail, 40) : '';
|
|
414
417
|
return `${activity(running.name).doing}${detail ? ` · ${detail}` : '…'}`;
|
|
415
418
|
}
|
|
416
|
-
case 'answering': return `已思考 ${seconds(this.answeredAt)}
|
|
417
|
-
case 'done': return `已完成,用时 ${seconds(this.finishedAt)}
|
|
418
|
-
case 'stopped': return `已停止,用时 ${seconds(this.finishedAt)}
|
|
419
|
-
case 'failed': return `处理失败,用时 ${seconds(this.finishedAt)}
|
|
419
|
+
case 'answering': return `已思考 ${seconds(this.answeredAt)}${tools}`;
|
|
420
|
+
case 'done': return `已完成,用时 ${seconds(this.finishedAt)}${tools}`;
|
|
421
|
+
case 'stopped': return `已停止,用时 ${seconds(this.finishedAt)}${tools}`;
|
|
422
|
+
case 'failed': return `处理失败,用时 ${seconds(this.finishedAt)}${tools}`;
|
|
420
423
|
}
|
|
421
424
|
}
|
|
422
425
|
/** The folded process panel. Readers open it when they want the thinking and tool steps. */
|
|
@@ -39,6 +39,8 @@ export declare class ConnectionController {
|
|
|
39
39
|
private unsafeStop;
|
|
40
40
|
private menuReady;
|
|
41
41
|
private documentsReady;
|
|
42
|
+
/** Menu entries the published app lacks; the owner adds them in the developer console. */
|
|
43
|
+
private menuMissing;
|
|
42
44
|
private documentSetup?;
|
|
43
45
|
private documentJob?;
|
|
44
46
|
private documentAborter?;
|
|
@@ -9,6 +9,7 @@ import { LockHeldError } from '../lock.js';
|
|
|
9
9
|
import { BOT_SCOPES, CARD_SCOPE, LarkPlatformError } from './lark-platform.js';
|
|
10
10
|
import { createChallenge, PairingError, waitForPairing } from './pairing.js';
|
|
11
11
|
import { defaultScopesReady } from '../rich-scopes.js';
|
|
12
|
+
import { missingBotMenuEntries } from '../bot-menu.js';
|
|
12
13
|
const restoreDelay = (attempt) => Math.min(10 * 60_000, 5_000 * 2 ** Math.min(attempt, 10));
|
|
13
14
|
const messages = {
|
|
14
15
|
idle: '把助手带到飞书,先用飞书确认创建。',
|
|
@@ -75,6 +76,8 @@ export class ConnectionController {
|
|
|
75
76
|
unsafeStop = false;
|
|
76
77
|
menuReady = false;
|
|
77
78
|
documentsReady = false;
|
|
79
|
+
/** Menu entries the published app lacks; the owner adds them in the developer console. */
|
|
80
|
+
menuMissing = [];
|
|
78
81
|
documentSetup;
|
|
79
82
|
documentJob;
|
|
80
83
|
documentAborter;
|
|
@@ -87,7 +90,8 @@ export class ConnectionController {
|
|
|
87
90
|
}
|
|
88
91
|
snapshot() {
|
|
89
92
|
return structuredClone({ ...this.state,
|
|
90
|
-
capabilities: { ...this.state.capabilities, documents: this.documentsReady }, ...(this.documentSetup ? { documentSetup: this.documentSetup } : {})
|
|
93
|
+
capabilities: { ...this.state.capabilities, documents: this.documentsReady }, ...(this.documentSetup ? { documentSetup: this.documentSetup } : {}),
|
|
94
|
+
...(this.record && this.menuMissing.length ? { menu: { missing: this.menuMissing, url: this.consoleUrl() } } : {}) });
|
|
91
95
|
}
|
|
92
96
|
phase(phase, message = messages[phase]) {
|
|
93
97
|
this.state = { phase, message, busy: ['creating', 'configuring', 'starting'].includes(phase),
|
|
@@ -168,6 +172,7 @@ export class ConnectionController {
|
|
|
168
172
|
if (!aborter.signal.aborted && !this.closed && this.record?.appId === record.appId) {
|
|
169
173
|
if (inspection.appId === record.appId && (!record.botOpenId || inspection.botOpenId === record.botOpenId)) {
|
|
170
174
|
this.documentsReady = inspection.scopeStatus === 'verified' && defaultScopesReady(inspection.grantedScopes);
|
|
175
|
+
this.menuMissing = missingBotMenuEntries(inspection.menuEventKeys ?? []);
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
178
|
}).catch(() => { })
|
|
@@ -353,6 +358,7 @@ export class ConnectionController {
|
|
|
353
358
|
let record = this.record;
|
|
354
359
|
let inspection = await this.deps.platform.inspect(record.profile, signal);
|
|
355
360
|
this.documentsReady = inspection.scopeStatus === 'verified' && defaultScopesReady(inspection.grantedScopes);
|
|
361
|
+
this.menuMissing = missingBotMenuEntries(inspection.menuEventKeys ?? []);
|
|
356
362
|
this.assertCurrent(signal);
|
|
357
363
|
if (inspection.appId !== record.appId || (record.botOpenId && inspection.botOpenId && record.botOpenId !== inspection.botOpenId)) {
|
|
358
364
|
throw new SetupError('机器人身份发生变化,请断开后重新绑定。', false);
|
|
@@ -37,6 +37,8 @@ export interface Inspection {
|
|
|
37
37
|
pendingVersionId?: string;
|
|
38
38
|
publicationStatus: 'approved' | 'pending' | 'rejected' | 'draft' | 'unknown';
|
|
39
39
|
subscribedEvents?: string[];
|
|
40
|
+
/** Event keys of the shown bot menu in that version; empty when its menu is off. */
|
|
41
|
+
menuEventKeys?: string[];
|
|
40
42
|
/** Read-only CLI preflight evidence. Actual connection readiness is checked by the service. */
|
|
41
43
|
cardReady?: boolean;
|
|
42
44
|
menuReady?: boolean;
|
|
@@ -429,6 +429,11 @@ export class LarkPlatform {
|
|
|
429
429
|
if (Array.isArray(detail.events) && detail.events.every(v => typeof v === 'string' && /^[a-zA-Z0-9_.:-]{1,200}$/.test(v))) {
|
|
430
430
|
inspection.subscribedEvents = detail.events;
|
|
431
431
|
}
|
|
432
|
+
const bot = object(object(detail.ability)?.bot);
|
|
433
|
+
if (bot && Array.isArray(bot.bot_menus) && bot.bot_menus.length <= 64) {
|
|
434
|
+
inspection.menuEventKeys = bot.bot_menu_enable !== true ? [] : bot.bot_menus.map(node => object(node)?.event_key)
|
|
435
|
+
.filter((key) => typeof key === 'string' && /^[a-zA-Z0-9_.:-]{1,64}$/.test(key));
|
|
436
|
+
}
|
|
432
437
|
});
|
|
433
438
|
checkSignal(signal);
|
|
434
439
|
return inspection;
|
|
@@ -40,6 +40,15 @@ export interface ConnectionSnapshot {
|
|
|
40
40
|
expiresAt: number;
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
+
/** Bottom-menu entries the published app lacks, and where its owner adds them. */
|
|
44
|
+
menu?: {
|
|
45
|
+
missing: Array<{
|
|
46
|
+
label: string;
|
|
47
|
+
eventKey: string;
|
|
48
|
+
parent?: string;
|
|
49
|
+
}>;
|
|
50
|
+
url: string;
|
|
51
|
+
};
|
|
43
52
|
/** A newer published release of this plugin, with the installed version. */
|
|
44
53
|
update?: {
|
|
45
54
|
current: string;
|