@modesto_ma/dsh-channels 0.5.0-beta.4 → 0.5.0-beta.6
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 +49 -28
- package/docs/attachment-pipeline-v2.md +1 -1
- package/docs/feishu-onboarding.md +6 -5
- package/lib/client.js +1874 -1510
- package/lib/client.js.map +1 -1
- package/lib/index.js +736 -353
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/api.ts +45 -2
- package/src/client/avatar.tsx +36 -0
- package/src/client/icon.tsx +25 -0
- package/src/client/index.ts +78 -23
- package/src/client/number-field.ts +97 -0
- package/src/client/origin-cache.ts +45 -0
- package/src/client/origin.tsx +201 -0
- package/src/client/panel/access-tab.tsx +90 -21
- package/src/client/panel/advanced-tab.tsx +59 -68
- package/src/client/panel/connection-tab.tsx +147 -164
- package/src/client/panel/credentials-card.tsx +124 -87
- package/src/client/panel/fields-tab.tsx +27 -0
- package/src/client/panel/index.tsx +77 -54
- package/src/client/panel/profiles.ts +73 -0
- package/src/client/panel/shared.tsx +358 -253
- package/src/client/panel/whitelist.tsx +203 -0
- package/src/client/panel/wiki-tab.tsx +33 -39
- package/src/client/styles.ts +132 -92
- package/src/domain.ts +42 -0
- package/src/feishu/scopes.ts +2 -1
- package/src/feishu-channel.ts +87 -0
- package/src/index.ts +1 -1
- package/src/routing/inbound.ts +13 -5
- package/src/service.ts +146 -3
- package/src/session/routes.ts +45 -4
- package/src/settings/contract.ts +265 -152
- package/src/settings/routes.ts +47 -0
- package/src/settings/surface.ts +201 -197
- package/src/client/panel/access-card.tsx +0 -329
- package/src/client/panel/analyze-token-card.tsx +0 -70
- package/src/client/panel/model-tab.tsx +0 -23
- package/src/client/panel/sessions-tab.tsx +0 -24
- package/src/client/panel/workspace-tab.tsx +0 -24
package/lib/index.js
CHANGED
|
@@ -1337,6 +1337,31 @@ const analyzeRequestRecordSchema = z.object({
|
|
|
1337
1337
|
])
|
|
1338
1338
|
});
|
|
1339
1339
|
/**
|
|
1340
|
+
* Where one Feishu-routed Session came from, keyed by its DSH session id.
|
|
1341
|
+
* Written the first time a route record names the Session (new topic,
|
|
1342
|
+
* `/renew`, analyze API) and kept after the route retires, so the Web
|
|
1343
|
+
* client can still show the origin of an old Session. Names are resolved
|
|
1344
|
+
* live when read, never stored.
|
|
1345
|
+
*/
|
|
1346
|
+
const sessionOriginRecordSchema = z.object({
|
|
1347
|
+
/** DSH session id (also the table key). */
|
|
1348
|
+
key: z.string().min(1),
|
|
1349
|
+
/** Route key the Session was bound under (thread_id or chat:<chatId>). */
|
|
1350
|
+
routeKey: z.string().min(1),
|
|
1351
|
+
/** Topic-scoped (`thread`) or chat-scoped (`chat`) route. */
|
|
1352
|
+
kind: z.enum(["thread", "chat"]),
|
|
1353
|
+
/** Feishu chat the Session was started from. */
|
|
1354
|
+
chatId: z.string().min(1),
|
|
1355
|
+
/** `p2p` or `group`. */
|
|
1356
|
+
chatType: z.enum(["p2p", "group"]),
|
|
1357
|
+
/** `user_id` of the user who started the Session (the requester for analyze topics). */
|
|
1358
|
+
senderUserId: z.string().min(1),
|
|
1359
|
+
/** Epoch ms when the Session was bound to the route. */
|
|
1360
|
+
createdAt: z.number().int().nonnegative(),
|
|
1361
|
+
/** Group-shared route (log-platform analysis topic). */
|
|
1362
|
+
shared: z.literal(true).optional()
|
|
1363
|
+
});
|
|
1364
|
+
/**
|
|
1340
1365
|
* Domain declaration for the host's storage facility. Plain data plus zod
|
|
1341
1366
|
* schemas — deliberately not built with `defineDomain` so this module has no
|
|
1342
1367
|
* runtime import of `@deepseek-ai/dsh-storage-domain`.
|
|
@@ -1364,7 +1389,8 @@ const feishuDomain = {
|
|
|
1364
1389
|
access_users: { valueSchema: accessUserRecordSchema },
|
|
1365
1390
|
access_requests: { valueSchema: accessRequestRecordSchema },
|
|
1366
1391
|
access_wiki_users: { valueSchema: accessUserRecordSchema },
|
|
1367
|
-
analyze_requests: { valueSchema: analyzeRequestRecordSchema }
|
|
1392
|
+
analyze_requests: { valueSchema: analyzeRequestRecordSchema },
|
|
1393
|
+
session_origins: { valueSchema: sessionOriginRecordSchema }
|
|
1368
1394
|
}
|
|
1369
1395
|
};
|
|
1370
1396
|
/**
|
|
@@ -3367,6 +3393,7 @@ var RouteStore = class {
|
|
|
3367
3393
|
this.expired.set(key, record);
|
|
3368
3394
|
this.active.delete(key);
|
|
3369
3395
|
}
|
|
3396
|
+
for (const record of this.active.values()) this.recordOrigins(record);
|
|
3370
3397
|
}
|
|
3371
3398
|
/** One active route by key (cache-first, normalized). */
|
|
3372
3399
|
get(key) {
|
|
@@ -3390,7 +3417,13 @@ var RouteStore = class {
|
|
|
3390
3417
|
/** Persist one active route and refresh the cache. */
|
|
3391
3418
|
async put(key, record) {
|
|
3392
3419
|
await this.deps.domain()?.routes.put(key, record);
|
|
3393
|
-
|
|
3420
|
+
const normalized = normalizeRouteRecord(record);
|
|
3421
|
+
this.active.set(key, normalized);
|
|
3422
|
+
await this.recordOrigins(normalized);
|
|
3423
|
+
}
|
|
3424
|
+
/** Feishu origin of one Session any route ever named, retired routes included. */
|
|
3425
|
+
origin(sessionId) {
|
|
3426
|
+
return this.deps.domain()?.sessionOrigins.get(sessionId);
|
|
3394
3427
|
}
|
|
3395
3428
|
/** Delete one active route from the medium and the cache. */
|
|
3396
3429
|
async remove(key) {
|
|
@@ -3418,6 +3451,33 @@ var RouteStore = class {
|
|
|
3418
3451
|
this.active.clear();
|
|
3419
3452
|
this.expired.clear();
|
|
3420
3453
|
}
|
|
3454
|
+
/**
|
|
3455
|
+
* Write an origin for every Session in the route's history that has none.
|
|
3456
|
+
* Existing origins are never rewritten: the first route naming a Session
|
|
3457
|
+
* is where it started. Failures are logged and never fail the route write.
|
|
3458
|
+
*/
|
|
3459
|
+
async recordOrigins(record) {
|
|
3460
|
+
const table = this.deps.domain()?.sessionOrigins;
|
|
3461
|
+
if (table === void 0) return;
|
|
3462
|
+
for (const entry of normalizeSessionHistory(record)) {
|
|
3463
|
+
if (table.get(entry.sessionId) !== void 0) continue;
|
|
3464
|
+
const origin = {
|
|
3465
|
+
key: entry.sessionId,
|
|
3466
|
+
routeKey: record.key,
|
|
3467
|
+
kind: record.kind,
|
|
3468
|
+
chatId: record.chatId,
|
|
3469
|
+
chatType: record.chatType,
|
|
3470
|
+
senderUserId: record.senderUserId,
|
|
3471
|
+
createdAt: entry.createdAt,
|
|
3472
|
+
...record.shared === true ? { shared: true } : {}
|
|
3473
|
+
};
|
|
3474
|
+
try {
|
|
3475
|
+
await table.put(entry.sessionId, origin);
|
|
3476
|
+
} catch (error) {
|
|
3477
|
+
this.deps.warn("dsh-channels: recording the origin of session %s failed: %s", entry.sessionId, String(error));
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3421
3481
|
/** Whether one active route has crossed the configured inactivity deadline. */
|
|
3422
3482
|
hasExpired(record, now) {
|
|
3423
3483
|
const ttlMs = this.deps.ttlMs();
|
|
@@ -3504,7 +3564,12 @@ const BASE_REQUIREMENTS = [
|
|
|
3504
3564
|
{
|
|
3505
3565
|
anyOf: ["im:chat:readonly", "im:chat"],
|
|
3506
3566
|
level: "optional",
|
|
3507
|
-
purpose: "
|
|
3567
|
+
purpose: "机器人入群后读取群信息、发送群信息卡;Web 页面显示群名和群头像"
|
|
3568
|
+
},
|
|
3569
|
+
{
|
|
3570
|
+
anyOf: ["contact:user.base:readonly"],
|
|
3571
|
+
level: "optional",
|
|
3572
|
+
purpose: "Web 页面显示用户真实姓名和头像"
|
|
3508
3573
|
}
|
|
3509
3574
|
];
|
|
3510
3575
|
/** Panel sync needs, applicable while `slashCommandPanel` is on. */
|
|
@@ -4279,6 +4344,19 @@ function thrownErrorFields(error) {
|
|
|
4279
4344
|
function assertFeishuOk(response) {
|
|
4280
4345
|
if (response.code !== void 0 && response.code !== 0) throw new FeishuApiError(`code=${response.code}${response.msg === void 0 ? "" : ` ${response.msg}`}`, response.code);
|
|
4281
4346
|
}
|
|
4347
|
+
/** Member-list pages `memberName()` scans before giving up (100 members each). */
|
|
4348
|
+
const MEMBER_NAME_PAGES = 5;
|
|
4349
|
+
/** `user_ids` per `contact.v3.user.batch` call (the API maximum). */
|
|
4350
|
+
const USER_BATCH_SIZE = 50;
|
|
4351
|
+
/** Keep a non-blank name and an `https:` avatar URL from one Feishu response. */
|
|
4352
|
+
function feishuProfile(name, avatar) {
|
|
4353
|
+
const trimmed = name?.trim();
|
|
4354
|
+
const avatarUrl = avatar !== void 0 && /^https:\/\/[^\s"'<>]+$/u.test(avatar) ? avatar : void 0;
|
|
4355
|
+
return {
|
|
4356
|
+
...trimmed === void 0 || trimmed === "" ? {} : { name: trimmed },
|
|
4357
|
+
...avatarUrl === void 0 ? {} : { avatarUrl }
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4282
4360
|
/** Run one raw request, mapping thrown and non-zero-code failures to {@link FeishuApiError}. */
|
|
4283
4361
|
async function feishuCall(call) {
|
|
4284
4362
|
let response;
|
|
@@ -4415,6 +4493,49 @@ var LarkChannelAdapter = class {
|
|
|
4415
4493
|
async getChatInfo(chatId) {
|
|
4416
4494
|
return this.channel.getChatInfo(chatId);
|
|
4417
4495
|
}
|
|
4496
|
+
async userProfiles(userIds) {
|
|
4497
|
+
const client = this.channel.rawClient;
|
|
4498
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
4499
|
+
for (let offset = 0; offset < userIds.length; offset += USER_BATCH_SIZE) {
|
|
4500
|
+
const response = await feishuCall(() => client.contact.v3.user.batch({ params: {
|
|
4501
|
+
user_ids: userIds.slice(offset, offset + USER_BATCH_SIZE),
|
|
4502
|
+
user_id_type: "user_id"
|
|
4503
|
+
} }));
|
|
4504
|
+
for (const item of response.data?.items ?? []) {
|
|
4505
|
+
if (item.user_id === void 0) continue;
|
|
4506
|
+
profiles.set(item.user_id, feishuProfile(item.name, item.avatar?.avatar_72));
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
return profiles;
|
|
4510
|
+
}
|
|
4511
|
+
async chatProfile(chatId) {
|
|
4512
|
+
const client = this.channel.rawClient;
|
|
4513
|
+
const response = await feishuCall(() => client.im.v1.chat.get({ path: { chat_id: chatId } }));
|
|
4514
|
+
return feishuProfile(response.data?.name, response.data?.avatar);
|
|
4515
|
+
}
|
|
4516
|
+
async memberName(userId, chatId) {
|
|
4517
|
+
const client = this.channel.rawClient;
|
|
4518
|
+
let pageToken;
|
|
4519
|
+
for (let page = 0; page < MEMBER_NAME_PAGES; page += 1) {
|
|
4520
|
+
let response;
|
|
4521
|
+
try {
|
|
4522
|
+
response = await feishuCall(() => client.im.v1.chatMembers.get({
|
|
4523
|
+
path: { chat_id: chatId },
|
|
4524
|
+
params: {
|
|
4525
|
+
member_id_type: "user_id",
|
|
4526
|
+
page_size: 100,
|
|
4527
|
+
...pageToken === void 0 ? {} : { page_token: pageToken }
|
|
4528
|
+
}
|
|
4529
|
+
}));
|
|
4530
|
+
} catch (_membersUnavailable) {
|
|
4531
|
+
return;
|
|
4532
|
+
}
|
|
4533
|
+
const hit = response.data?.items?.find((item) => item.member_id === userId)?.name?.trim();
|
|
4534
|
+
if (hit !== void 0 && hit !== "") return hit;
|
|
4535
|
+
pageToken = response.data?.page_token;
|
|
4536
|
+
if (response.data?.has_more !== true || pageToken === void 0) return void 0;
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4418
4539
|
botOpenId() {
|
|
4419
4540
|
return this.channel.botIdentity?.openId;
|
|
4420
4541
|
}
|
|
@@ -6727,7 +6848,7 @@ var InboundRouter = class {
|
|
|
6727
6848
|
} catch (error) {
|
|
6728
6849
|
this.ctx.logger?.warn?.("dsh-channels: recording processed message %s failed: %s", msg.messageId, String(error));
|
|
6729
6850
|
}
|
|
6730
|
-
if (content.startsWith("/")) {
|
|
6851
|
+
if (content.startsWith("/") && this.deps.commands.owns(content)) {
|
|
6731
6852
|
await this.deps.commands.handleSlash(msg, content);
|
|
6732
6853
|
return;
|
|
6733
6854
|
}
|
|
@@ -16221,7 +16342,7 @@ function resolveConfig(raw) {
|
|
|
16221
16342
|
* Web 设置面板的线上契约:Host 半侧的路由/面板数据与 Browser 半侧的渲染共用。
|
|
16222
16343
|
*
|
|
16223
16344
|
* 本模块**没有运行时依赖**:Host 与 Client bundle 都直接内联它。因此这里只放
|
|
16224
|
-
*
|
|
16345
|
+
* 常量、字段清单、可序列化的 wire 类型与无依赖的纯文案函数,不 import zod / node / DSH 内部包。
|
|
16225
16346
|
*
|
|
16226
16347
|
* @module dsh-channels/settings/contract
|
|
16227
16348
|
*/
|
|
@@ -16256,6 +16377,14 @@ const ACCESS_REQUESTS_ROUTE = "/dsh-channels/access/requests";
|
|
|
16256
16377
|
* [`EditableFieldWire.browse`],保留手写输入。
|
|
16257
16378
|
*/
|
|
16258
16379
|
const PICK_DIRECTORY_ROUTE = "/dsh-channels/pick-directory";
|
|
16380
|
+
/** 会话的飞书来源(GET/HEAD,`?sessionId=`):对话右侧栏「飞书来源」标签与会话头部入口使用。 */
|
|
16381
|
+
const SESSION_ORIGIN_ROUTE = "/dsh-channels/session-origin";
|
|
16382
|
+
/**
|
|
16383
|
+
* `GET PROFILES_ROUTE?users=<user_id,...>&chats=<chat_id,...>`: live Feishu
|
|
16384
|
+
* names and avatars for the users and chats the panel shows. At most
|
|
16385
|
+
* {@link PROFILES_MAX_IDS} ids per list.
|
|
16386
|
+
*/
|
|
16387
|
+
const PROFILES_ROUTE = "/dsh-channels/profiles";
|
|
16259
16388
|
/**
|
|
16260
16389
|
* 可由 Web 面板覆盖的配置项。顶层键对应 `src/config.ts` 的字段;`feishuTools.*`
|
|
16261
16390
|
* 是嵌套字段(覆盖层里仍写成 `{ "feishuTools": { ... } }` 对象)。覆盖值写进
|
|
@@ -16269,43 +16398,50 @@ const PICK_DIRECTORY_ROUTE = "/dsh-channels/pick-directory";
|
|
|
16269
16398
|
* `baseDir`(工作区根目录 = 模型/工具产生文件的落点)可覆盖,但面板只接受
|
|
16270
16399
|
* **绝对路径**(`~` 会展开);已有 Session / 目录不迁移,只影响之后新建的
|
|
16271
16400
|
* 会话。校验与保存时建目录见 `src/workspaces.ts`。
|
|
16401
|
+
*
|
|
16402
|
+
* 数组顺序就是面板里同一分组内字段的显示顺序。
|
|
16272
16403
|
*/
|
|
16273
16404
|
const EDITABLE_KEYS = [
|
|
16274
|
-
"baseDir",
|
|
16275
16405
|
"dmMode",
|
|
16276
16406
|
"requireMention",
|
|
16277
16407
|
"threadFallback",
|
|
16408
|
+
"baseDir",
|
|
16409
|
+
"topicIsolation",
|
|
16410
|
+
"routeTtlMs",
|
|
16411
|
+
"approvalTimeoutMs",
|
|
16412
|
+
"questionTimeoutMs",
|
|
16413
|
+
"refreshMs",
|
|
16414
|
+
"statusRefreshMs",
|
|
16278
16415
|
"welcomeOnboarding",
|
|
16279
16416
|
"slashCommandPanel",
|
|
16280
16417
|
"allowPermissionSwitch",
|
|
16281
|
-
"
|
|
16282
|
-
"
|
|
16283
|
-
"
|
|
16284
|
-
"
|
|
16418
|
+
"provider",
|
|
16419
|
+
"model",
|
|
16420
|
+
"reasoningEffort",
|
|
16421
|
+
"maxTokens",
|
|
16422
|
+
"agentPreset",
|
|
16285
16423
|
"acceptAttachments",
|
|
16286
16424
|
"attachmentStore",
|
|
16287
16425
|
"prefetchImages",
|
|
16426
|
+
"historyBootstrapLimit",
|
|
16427
|
+
"feishuTools.attachmentFetch",
|
|
16428
|
+
"feishuTools.topicMessages",
|
|
16429
|
+
"feishuTools.sendFile",
|
|
16288
16430
|
"prefetchTimeoutMs",
|
|
16289
16431
|
"prefetchImageMaxBytes",
|
|
16290
16432
|
"prefetchTotalMaxBytes",
|
|
16291
|
-
"historyBootstrapLimit",
|
|
16292
|
-
"feishuTools.attachmentFetch",
|
|
16293
16433
|
"feishuTools.attachmentFetchMaxResources",
|
|
16294
16434
|
"feishuTools.attachmentFetchMaxBytes",
|
|
16295
16435
|
"feishuTools.attachmentFetchTimeoutMs",
|
|
16296
|
-
"feishuTools.topicMessages",
|
|
16297
16436
|
"feishuTools.topicMessagesTimeoutMs",
|
|
16298
|
-
"
|
|
16437
|
+
"promptContextTimeoutMs",
|
|
16438
|
+
"accessMode",
|
|
16439
|
+
"adminUserId",
|
|
16440
|
+
"authRequestTtlDays",
|
|
16299
16441
|
"feishuTools.wikiRead",
|
|
16300
16442
|
"feishuTools.wikiWrite",
|
|
16301
16443
|
"wikiRoots",
|
|
16302
|
-
"
|
|
16303
|
-
"provider",
|
|
16304
|
-
"model",
|
|
16305
|
-
"reasoningEffort",
|
|
16306
|
-
"agentPreset",
|
|
16307
|
-
"refreshMs",
|
|
16308
|
-
"statusRefreshMs",
|
|
16444
|
+
"analyzeListen",
|
|
16309
16445
|
"loggerLevel"
|
|
16310
16446
|
];
|
|
16311
16447
|
/**
|
|
@@ -16321,66 +16457,57 @@ const RESTART_KEYS = [
|
|
|
16321
16457
|
"statusRefreshMs",
|
|
16322
16458
|
"loggerLevel"
|
|
16323
16459
|
];
|
|
16460
|
+
/** `unit` of byte-count fields; the panel adds a MiB reading next to them. */
|
|
16461
|
+
const BYTES_UNIT = "字节";
|
|
16324
16462
|
/** Display labels for the panel header/cards (kept on the wire so the client owns no copy). */
|
|
16325
16463
|
const PANEL_COPY = {
|
|
16326
16464
|
title: "飞书渠道",
|
|
16327
|
-
description: "飞书长连接、应用凭据与渠道配置。凭据只写入 DSH 凭据库且不回显;标注「需重连」的项在连接建立时读取。",
|
|
16328
|
-
statusCard: "连接状态",
|
|
16329
16465
|
phaseUnconfigured: "待配置",
|
|
16330
16466
|
phaseIdle: "未连接",
|
|
16331
16467
|
phaseConnecting: "连接中",
|
|
16332
16468
|
phaseConnected: "已连接",
|
|
16333
16469
|
phaseError: "连接失败",
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
statusSummaryError: "最近一次连接飞书失败,请检查凭据与网络后重试。",
|
|
16470
|
+
statusSummaryConnected: "机器人在线,可以收发消息",
|
|
16471
|
+
statusSummaryUnconfigured: "还没有填写飞书应用凭据,插件不会连接飞书",
|
|
16472
|
+
statusSummaryIdle: "凭据已就绪,还没有建立连接",
|
|
16473
|
+
statusSummaryConnecting: "正在连接飞书…",
|
|
16474
|
+
statusSummaryError: "连接飞书失败,请检查凭据与网络后重试",
|
|
16475
|
+
uptimePrefix: "运行",
|
|
16341
16476
|
lastError: "最近一次连接失败",
|
|
16342
|
-
|
|
16343
|
-
scopeCheckOk: "
|
|
16344
|
-
scopeCheckMissing: "
|
|
16345
|
-
|
|
16477
|
+
checksTitle: "自检",
|
|
16478
|
+
scopeCheckOk: "插件需要的飞书权限都已开通",
|
|
16479
|
+
scopeCheckMissing: "{n} 项权限未开通",
|
|
16480
|
+
scopeCheckMissingRequired: "{n} 项必需权限未开通,相关功能无法使用",
|
|
16481
|
+
scopeCheckMissingOptional: "不影响收发消息,只有相关功能不可用。开通后需在开发者后台发布新版本。",
|
|
16482
|
+
scopeCheckPublish: "开通后需在开发者后台发布新版本。",
|
|
16483
|
+
scopeCheckOpenAll: "全部开通",
|
|
16346
16484
|
scopeCheckOpenOne: "去开通",
|
|
16347
|
-
scopeCheckOptional: "可选",
|
|
16348
16485
|
scopeCheckRequired: "必需",
|
|
16349
|
-
scopeCheckError: "
|
|
16486
|
+
scopeCheckError: "无法读取权限开通情况",
|
|
16350
16487
|
slashCommandSyncTitle: "斜杠指令面板",
|
|
16351
|
-
slashCommandSyncOk: "
|
|
16352
|
-
|
|
16353
|
-
slashCommandSyncCreated: "本次新增",
|
|
16354
|
-
slashCommandSyncUpdated: "本次更新",
|
|
16355
|
-
slashCommandSyncRemoved: "本次删除",
|
|
16356
|
-
slashCommandSyncError: "同步飞书原生斜杠指令面板失败(不影响直接输入命令):",
|
|
16488
|
+
slashCommandSyncOk: "已同步 {total} 条命令,飞书客户端约 5 分钟后刷新",
|
|
16489
|
+
slashCommandSyncError: "同步失败,直接输入命令不受影响",
|
|
16357
16490
|
missingConfig: "缺少配置",
|
|
16358
|
-
statusHintUnconfigured: "配置完成后插件会自动连接;也可以稍后在本页手动连接。",
|
|
16359
|
-
connectionInfo: "连接信息",
|
|
16360
|
-
connectionStateLabel: "飞书长连接",
|
|
16361
|
-
botOpenId: "Bot open_id",
|
|
16362
|
-
uptime: "运行时长",
|
|
16363
16491
|
routeCount: "话题路由",
|
|
16364
16492
|
liveAgentCount: "活动会话",
|
|
16365
16493
|
inflightTurnCount: "在途回复",
|
|
16494
|
+
botOpenId: "Bot open_id",
|
|
16366
16495
|
healthReasons: "健康判定",
|
|
16367
16496
|
unknownValue: "—",
|
|
16368
16497
|
connect: "连接飞书",
|
|
16369
16498
|
connecting: "连接中…",
|
|
16370
16499
|
onboardingTitle: "开始使用飞书渠道",
|
|
16371
|
-
onboardingIntro: "只需三步:在飞书开放平台创建自建应用,把 App ID / App Secret 填入本页,保存后自动连接。",
|
|
16372
16500
|
onboardingStep1Title: "创建飞书自建应用",
|
|
16373
|
-
onboardingStep1Text: "
|
|
16501
|
+
onboardingStep1Text: "在 open.feishu.cn/app 创建企业自建应用,开启机器人能力;事件订阅选「长连接」,订阅 im.message.receive_v1。",
|
|
16374
16502
|
onboardingStep2Title: "填写应用凭据",
|
|
16375
|
-
onboardingStep2Text: "
|
|
16376
|
-
onboardingStep3Title: "
|
|
16377
|
-
onboardingStep3Text: "
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
|
|
16382
|
-
|
|
16383
|
-
credentialsRef: "凭据引用",
|
|
16503
|
+
onboardingStep2Text: "把 App ID(cli_ 开头)和 App Secret 填到下方「应用凭据」。",
|
|
16504
|
+
onboardingStep3Title: "保存后自动连接",
|
|
16505
|
+
onboardingStep3Text: "两项都保存后插件会自动连接,状态变成「已连接」就能在飞书里私聊机器人。",
|
|
16506
|
+
credentialsTitle: "应用凭据",
|
|
16507
|
+
credentialsHint: "值只存在本机凭据库,面板不回显。",
|
|
16508
|
+
appIdLabel: "App ID",
|
|
16509
|
+
appSecretLabel: "App Secret",
|
|
16510
|
+
credentialStore: "凭据库",
|
|
16384
16511
|
saveCredentials: "保存凭据",
|
|
16385
16512
|
savingCredentials: "保存中…",
|
|
16386
16513
|
credentialSaved: "凭据已保存",
|
|
@@ -16390,27 +16517,29 @@ const PANEL_COPY = {
|
|
|
16390
16517
|
credentialAutoConnectFailed: "凭据已保存,但自动连接失败",
|
|
16391
16518
|
credentialRemoved: "已移除存储的凭据",
|
|
16392
16519
|
credentialRemovedPending: "已移除存储的凭据,重启飞书连接后生效",
|
|
16393
|
-
|
|
16520
|
+
replaceCredential: "更换",
|
|
16521
|
+
setCredential: "设置",
|
|
16522
|
+
cancel: "取消",
|
|
16523
|
+
removeCredential: "移除",
|
|
16394
16524
|
removingCredential: "移除中…",
|
|
16395
16525
|
credentialConfigured: "已配置",
|
|
16396
16526
|
credentialMissing: "未配置",
|
|
16397
16527
|
credentialReadOnly: "当前凭据存储不可写",
|
|
16398
16528
|
appIdPlaceholder: "输入 App ID(cli_…)",
|
|
16399
16529
|
appSecretPlaceholder: "输入 App Secret",
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
|
-
sessionsTabHelp: "私聊、群聊触发方式与入站内容:附件存储/预取、模型工具开关与读取预算;「需重连」项在下次连接时读取。",
|
|
16403
|
-
modelTabHelp: "飞书会话使用的模型服务商、模型、推理档位与 Agent 预设;下拉选项来自宿主机实时目录,保存后对新会话生效。",
|
|
16404
|
-
workspaceTabHelp: "每个用户一个工作区(模型/工具产生文件的落点),每个话题在其中拥有独立工作目录;只影响之后新建的话题。入站附件默认存于 DSH 原生附件库,不占用话题目录。插件不会自动删除工作区或话题目录;过期目录可手动删除,话题下次对话时会重建空目录。",
|
|
16405
|
-
accessTabHelp: "准入采用 fail-closed:管理员不受白名单限制,非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。",
|
|
16406
|
-
wikiTabHelp: "知识库工具以应用身份访问「知识库范围」内的文档、电子表格和多维表格。只有管理员或知识库白名单里的用户发起的话题能用;每次打开、新建、修改仍需话题发起人在授权卡上确认。",
|
|
16407
|
-
advancedTabHelp: "流式刷新等低层参数、Web 覆盖文件与只读信息。",
|
|
16408
|
-
save: "保存更改",
|
|
16530
|
+
credentialRemoveConfirm: "确定移除已存储的凭据值?移除后不会回显,需要时须重新填写。",
|
|
16531
|
+
save: "保存",
|
|
16409
16532
|
saving: "保存中…",
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
|
|
16533
|
+
discard: "放弃",
|
|
16534
|
+
dirtyCount: "{n} 项未保存",
|
|
16535
|
+
dirtyRestart: ",其中 {n} 项需要重连后生效",
|
|
16536
|
+
details: "详情",
|
|
16537
|
+
expand: "展开",
|
|
16538
|
+
collapse: "收起",
|
|
16539
|
+
collapsedCount: "{n} 项",
|
|
16540
|
+
collapsedClean: "一般不用改",
|
|
16541
|
+
collapsedChanged: "{n} 项已在面板修改",
|
|
16542
|
+
restartTag: "需重连",
|
|
16414
16543
|
listAdd: "添加",
|
|
16415
16544
|
listRemove: "移除",
|
|
16416
16545
|
listEntry: "第 {n} 项",
|
|
@@ -16422,62 +16551,58 @@ const PANEL_COPY = {
|
|
|
16422
16551
|
directoryPicked: "已选择目录",
|
|
16423
16552
|
directoryPickFailed: "无法打开系统目录选择器",
|
|
16424
16553
|
saved: "已保存",
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16554
|
+
saveInvalid: "请先修正标红的配置项",
|
|
16555
|
+
numberInvalid: "请输入数字",
|
|
16556
|
+
numberMin: "不得小于 {n}",
|
|
16557
|
+
numberMax: "不得大于 {n}",
|
|
16558
|
+
modelFootnote: "对之后新建的会话生效。",
|
|
16559
|
+
catalogFailed: "模型目录加载失败,已退回手填。",
|
|
16560
|
+
catalogLoading: "正在读取宿主机选项…",
|
|
16561
|
+
catalogUnavailable: "宿主机未提供可选项,请手填。",
|
|
16562
|
+
restartBanner: "以下配置要重启飞书连接后生效:",
|
|
16563
|
+
restartWarning: "重启会断开当前飞书连接并中断进行中的回复,确定现在重启?",
|
|
16564
|
+
reconnect: "重启连接",
|
|
16435
16565
|
reconnecting: "重连中…",
|
|
16436
|
-
refresh: "
|
|
16566
|
+
refresh: "刷新",
|
|
16437
16567
|
retry: "重试",
|
|
16438
16568
|
pollFailed: "状态刷新失败",
|
|
16439
16569
|
connectDone: "已连接飞书",
|
|
16440
16570
|
reconnectDone: "已重启飞书连接",
|
|
16441
|
-
|
|
16442
|
-
|
|
16443
|
-
disabled: "已关闭",
|
|
16444
|
-
credentialRemoveConfirm: "确定移除已存储的凭据值?移除后不会回显,需要时须重新填写。",
|
|
16445
|
-
analyzeTokenCard: "日志分析接口令牌",
|
|
16446
|
-
analyzeTokenCardHelp: "日志平台调用分析接口时 Authorization: Bearer 后面的值。经 DSH 凭据服务写入本机凭据库,面板永不回显;保存或移除后下一次请求即生效,无需重连。未配置时接口对所有请求返回 503。",
|
|
16571
|
+
loading: "加载中…",
|
|
16572
|
+
loadFailed: "设置面板加载失败",
|
|
16447
16573
|
analyzeTokenLabel: "访问令牌",
|
|
16574
|
+
analyzeTokenSummary: "请求头 Authorization: Bearer 的值,保存后立即生效;未配置时接口拒绝所有请求",
|
|
16448
16575
|
analyzeTokenPlaceholder: "填写新令牌(建议 32 位以上随机串)",
|
|
16449
|
-
saveAnalyzeToken: "保存令牌",
|
|
16450
16576
|
analyzeTokenSaved: "令牌已保存,下一次请求生效",
|
|
16451
16577
|
analyzeTokenRemoved: "令牌已移除,接口将拒绝所有请求",
|
|
16452
|
-
|
|
16453
|
-
loading: "加载中…",
|
|
16454
|
-
loadFailed: "设置面板加载失败",
|
|
16455
|
-
catalogFailed: "模型目录加载失败,已退回手填。",
|
|
16456
|
-
catalogLoading: "正在读取宿主机选项…",
|
|
16457
|
-
catalogUnavailable: "宿主机未提供可选项,请手填。",
|
|
16458
|
-
version: "插件版本",
|
|
16578
|
+
overlayTitle: "Web 覆盖",
|
|
16459
16579
|
overlayPath: "覆盖文件",
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16580
|
+
overlayCount: "{n} 项配置由面板覆盖",
|
|
16581
|
+
noOverrides: "当前没有面板覆盖的配置",
|
|
16582
|
+
overlayPathHelp: "面板保存的配置写在这个 JSON 文件里,优先级高于 profile。可以直接编辑;面板保存会整体重写它。",
|
|
16583
|
+
clear: "恢复 profile 配置",
|
|
16584
|
+
clearing: "恢复中…",
|
|
16585
|
+
clearConfirm: "确定删除所有面板覆盖,恢复为 profile 配置?",
|
|
16586
|
+
cleared: "已恢复为 profile 配置",
|
|
16587
|
+
debugModeWarning: "调试模式已打开:应用可用范围内的任何人都能和机器人对话。用完请在「访问授权」关闭。",
|
|
16588
|
+
accessAdminMissingWarning: "没有可用的管理员,所有对话都会被拒绝。请在下方填写管理员 user_id(私聊机器人发送 /userid 获取)。",
|
|
16589
|
+
accessAdminCurrent: "当前管理员",
|
|
16590
|
+
accessAdminSourceConfig: "已配置",
|
|
16591
|
+
accessAdminSourceOwner: "应用所有者",
|
|
16592
|
+
profilesWarning: "部分飞书名字和头像未能读取:{reason}",
|
|
16593
|
+
accessNotLoaded: "飞书连接建立后加载授权列表",
|
|
16594
|
+
accessRequestsTitle: "待处理申请",
|
|
16469
16595
|
accessGroupsTitle: "已授权群",
|
|
16470
|
-
accessGroupsEmpty: "尚未授权任何群。机器人被拉进群后,管理员会收到带 chat_id 的群信息卡;也可在群内发送 /groupid 获取。",
|
|
16471
16596
|
accessUsersTitle: "已授权用户",
|
|
16472
|
-
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
accessGroupChatIdPlaceholder: "群 chat_id(oc_…)",
|
|
16597
|
+
accessGroupsEmpty: "还没有授权任何群",
|
|
16598
|
+
accessUsersEmpty: "还没有授权任何用户",
|
|
16599
|
+
accessGroupChatIdPlaceholder: "chat_id(oc_…,群里发 /groupid 获取)",
|
|
16476
16600
|
accessGroupNamePlaceholder: "群名(可选)",
|
|
16477
|
-
accessUserIdPlaceholder: "
|
|
16601
|
+
accessUserIdPlaceholder: "user_id(私聊机器人发 /userid 获取)",
|
|
16478
16602
|
accessUserNamePlaceholder: "昵称(可选)",
|
|
16479
16603
|
accessAddGroup: "添加群",
|
|
16480
16604
|
accessAddUser: "添加用户",
|
|
16605
|
+
accessAdd: "添加",
|
|
16481
16606
|
accessRemove: "移除",
|
|
16482
16607
|
accessAllow: "授权",
|
|
16483
16608
|
accessDeny: "忽略",
|
|
@@ -16485,23 +16610,22 @@ const PANEL_COPY = {
|
|
|
16485
16610
|
accessSourceCommand: "对话命令",
|
|
16486
16611
|
accessSourceCard: "授权卡",
|
|
16487
16612
|
accessSourcePanel: "设置面板",
|
|
16488
|
-
accessRequestGroup: "
|
|
16489
|
-
accessRequestUser: "
|
|
16490
|
-
|
|
16491
|
-
accessRequestExpires: "
|
|
16613
|
+
accessRequestGroup: "申请授权群",
|
|
16614
|
+
accessRequestUser: "申请使用",
|
|
16615
|
+
accessRequestIn: "在「{group}」",
|
|
16616
|
+
accessRequestExpires: "{date} 前有效",
|
|
16492
16617
|
accessGroupAdded: "已添加群授权",
|
|
16493
16618
|
accessGroupRemoved: "已移除群授权",
|
|
16494
16619
|
accessUserAdded: "已添加用户授权",
|
|
16495
16620
|
accessUserRemoved: "已移除用户授权",
|
|
16496
16621
|
accessRequestAllowed: "已授权",
|
|
16497
16622
|
accessRequestDenied: "已忽略",
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
|
-
|
|
16501
|
-
|
|
16623
|
+
wikiUsersTitle: "可使用的人",
|
|
16624
|
+
wikiUsersAdminPlus: "管理员 + {n}",
|
|
16625
|
+
wikiUsersEmpty: "目前只有管理员发起的话题能用知识库工具",
|
|
16626
|
+
wikiUsersHint: "也可以在飞书发 /auth wiki add @用户",
|
|
16502
16627
|
wikiUserAdded: "已加入知识库白名单",
|
|
16503
|
-
wikiUserRemoved: "已移出知识库白名单"
|
|
16504
|
-
accessChatIdHint: "chat_id 可从入群群信息卡或群内 /groupid 获取;user_id 可私聊机器人发送 /userid 获取。"
|
|
16628
|
+
wikiUserRemoved: "已移出知识库白名单"
|
|
16505
16629
|
};
|
|
16506
16630
|
//#endregion
|
|
16507
16631
|
//#region src/settings/overlay.ts
|
|
@@ -16774,6 +16898,18 @@ const APP_OWNER_LOOKUP_TIMEOUT_MS = 1e4;
|
|
|
16774
16898
|
const SCOPE_CHECK_TIMEOUT_MS = 1e4;
|
|
16775
16899
|
/** Upper bound on the whole post-connect slash-command panel sync (one list plus per-command writes). */
|
|
16776
16900
|
const SLASH_COMMAND_SYNC_TIMEOUT_MS = 3e4;
|
|
16901
|
+
/**
|
|
16902
|
+
* How long one resolved user or chat profile is reused. Short enough that
|
|
16903
|
+
* every page entry shows current Feishu data; long enough that the session
|
|
16904
|
+
* header and its sidebar tab opening together cost one lookup.
|
|
16905
|
+
*/
|
|
16906
|
+
const PROFILE_TTL_MS = 30 * 1e3;
|
|
16907
|
+
/** The Feishu `code=<n> <msg>` of a failed API call, else the error text. */
|
|
16908
|
+
function feishuReason(error) {
|
|
16909
|
+
return error instanceof FeishuApiError ? error.detail : messageOf$3(error);
|
|
16910
|
+
}
|
|
16911
|
+
/** Feishu `chat_id`s the profile lookup accepts. */
|
|
16912
|
+
const FEISHU_CHAT_ID_PATTERN = /^oc_[A-Za-z0-9_-]+$/u;
|
|
16777
16913
|
/** Wrap the host's storage facility open behind a stable local type. */
|
|
16778
16914
|
async function openFeishuDomain(ctx) {
|
|
16779
16915
|
const domain = await ctx.storageDomain.open(feishuDomain);
|
|
@@ -16786,6 +16922,7 @@ async function openFeishuDomain(ctx) {
|
|
|
16786
16922
|
const accessRequestsRaw = domain.table("access_requests");
|
|
16787
16923
|
const accessWikiUsersRaw = domain.table("access_wiki_users");
|
|
16788
16924
|
const analyzeRequestsRaw = domain.table("analyze_requests");
|
|
16925
|
+
const sessionOriginsRaw = domain.table("session_origins");
|
|
16789
16926
|
return {
|
|
16790
16927
|
routes: {
|
|
16791
16928
|
get: (key) => table.get(key),
|
|
@@ -16840,6 +16977,10 @@ async function openFeishuDomain(ctx) {
|
|
|
16840
16977
|
put: (key, value) => analyzeRequestsRaw.put(key, value),
|
|
16841
16978
|
entries: () => analyzeRequestsRaw.entries()
|
|
16842
16979
|
},
|
|
16980
|
+
sessionOrigins: {
|
|
16981
|
+
get: (key) => sessionOriginsRaw.get(key),
|
|
16982
|
+
put: (key, value) => sessionOriginsRaw.put(key, value)
|
|
16983
|
+
},
|
|
16843
16984
|
getGlobal: () => domain.global.get() ?? {},
|
|
16844
16985
|
setGlobal: (value) => domain.global.set(value),
|
|
16845
16986
|
close: () => domain.close()
|
|
@@ -16986,6 +17127,8 @@ var FeishuService = class {
|
|
|
16986
17127
|
* is unset. Cleared on shutdown so a reconnect looks it up again.
|
|
16987
17128
|
*/
|
|
16988
17129
|
appOwner;
|
|
17130
|
+
/** Live-resolved user and chat profiles, by `user:`/`chat:` key; misses are kept as `{}`. */
|
|
17131
|
+
profileCache = /* @__PURE__ */ new Map();
|
|
16989
17132
|
/** Post-connect scope self-check; cleared on shutdown. */
|
|
16990
17133
|
scopeCheckState;
|
|
16991
17134
|
/** Post-connect slash-command panel sync; cleared on shutdown. */
|
|
@@ -17164,7 +17307,13 @@ var FeishuService = class {
|
|
|
17164
17307
|
streamer: () => this.streamer,
|
|
17165
17308
|
questionAnswerer: () => this.questionAnswerer,
|
|
17166
17309
|
approvalAnswerer: () => this.approvalAnswerer,
|
|
17167
|
-
commands: {
|
|
17310
|
+
commands: {
|
|
17311
|
+
owns: (line) => {
|
|
17312
|
+
const parsed = parseSlashCommand(line);
|
|
17313
|
+
return parsed !== void 0 && commandSpec(parsed.name) !== void 0;
|
|
17314
|
+
},
|
|
17315
|
+
handleSlash: (msg, line) => this.handleSlashMessage(msg, line)
|
|
17316
|
+
}
|
|
17168
17317
|
});
|
|
17169
17318
|
this.analyzeLauncher = new AnalyzeLauncher({
|
|
17170
17319
|
ctx: this.ctx,
|
|
@@ -17653,6 +17802,118 @@ var FeishuService = class {
|
|
|
17653
17802
|
};
|
|
17654
17803
|
}
|
|
17655
17804
|
}
|
|
17805
|
+
/** Panel: Feishu origin of one Session, or null when Feishu never routed it. */
|
|
17806
|
+
async sessionOrigin(sessionId) {
|
|
17807
|
+
const origin = this.routes.origin(sessionId);
|
|
17808
|
+
if (origin === void 0) return null;
|
|
17809
|
+
const route = this.routes.findBySession(sessionId);
|
|
17810
|
+
const bound = route !== void 0;
|
|
17811
|
+
const group = origin.chatType === "group";
|
|
17812
|
+
const profiles = await this.feishuProfiles([origin.senderUserId], group ? [origin.chatId] : []);
|
|
17813
|
+
const chat = profiles.chats[origin.chatId] ?? {};
|
|
17814
|
+
const sender = profiles.users[origin.senderUserId] ?? {};
|
|
17815
|
+
const senderName = sender.name ?? (group ? await this.memberName(origin.senderUserId, origin.chatId) : void 0);
|
|
17816
|
+
return {
|
|
17817
|
+
sessionId,
|
|
17818
|
+
chatType: origin.chatType,
|
|
17819
|
+
kind: origin.kind,
|
|
17820
|
+
chatId: origin.chatId,
|
|
17821
|
+
...chat.name === void 0 ? {} : { chatName: chat.name },
|
|
17822
|
+
...chat.avatarUrl === void 0 ? {} : { chatAvatarUrl: chat.avatarUrl },
|
|
17823
|
+
senderUserId: origin.senderUserId,
|
|
17824
|
+
...senderName === void 0 ? {} : { senderName },
|
|
17825
|
+
...sender.avatarUrl === void 0 ? {} : { senderAvatarUrl: sender.avatarUrl },
|
|
17826
|
+
shared: origin.shared === true,
|
|
17827
|
+
createdAt: origin.createdAt,
|
|
17828
|
+
bound,
|
|
17829
|
+
...bound ? { lastActivityAt: route.lastActivityAt } : {},
|
|
17830
|
+
chatLink: `https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(origin.chatId)}`
|
|
17831
|
+
};
|
|
17832
|
+
}
|
|
17833
|
+
/**
|
|
17834
|
+
* Live Feishu names and avatars for the panel and the session-origin view,
|
|
17835
|
+
* through a {@link PROFILE_TTL_MS} cache. Ids that are not Feishu
|
|
17836
|
+
* `user_id`s / `chat_id`s are skipped; a disconnected channel and failed
|
|
17837
|
+
* lookups leave ids absent, and failures are reported in `warning` and not
|
|
17838
|
+
* cached.
|
|
17839
|
+
* @param userIds - user `user_id`s to resolve.
|
|
17840
|
+
* @param chatIds - chat `chat_id`s to resolve.
|
|
17841
|
+
* @returns the resolved profiles by id.
|
|
17842
|
+
*/
|
|
17843
|
+
async feishuProfiles(userIds, chatIds) {
|
|
17844
|
+
const result = {
|
|
17845
|
+
users: {},
|
|
17846
|
+
chats: {}
|
|
17847
|
+
};
|
|
17848
|
+
const warnings = [];
|
|
17849
|
+
const now = Date.now();
|
|
17850
|
+
for (const [key, cached] of this.profileCache) if (now - cached.at >= PROFILE_TTL_MS) this.profileCache.delete(key);
|
|
17851
|
+
const fresh = (key) => this.profileCache.get(key)?.profile;
|
|
17852
|
+
const keep = (target, id, profile) => {
|
|
17853
|
+
if (profile.name !== void 0 || profile.avatarUrl !== void 0) target[id] = profile;
|
|
17854
|
+
};
|
|
17855
|
+
const users = [...new Set(userIds)].filter((id) => FEISHU_USER_ID_PATTERN.test(id));
|
|
17856
|
+
const chats = [...new Set(chatIds)].filter((id) => FEISHU_CHAT_ID_PATTERN.test(id));
|
|
17857
|
+
const missingUsers = [];
|
|
17858
|
+
for (const id of users) {
|
|
17859
|
+
const cached = fresh(`user:${id}`);
|
|
17860
|
+
if (cached === void 0) missingUsers.push(id);
|
|
17861
|
+
else keep(result.users, id, cached);
|
|
17862
|
+
}
|
|
17863
|
+
const missingChats = [];
|
|
17864
|
+
for (const id of chats) {
|
|
17865
|
+
const cached = fresh(`chat:${id}`);
|
|
17866
|
+
if (cached === void 0) missingChats.push(id);
|
|
17867
|
+
else keep(result.chats, id, cached);
|
|
17868
|
+
}
|
|
17869
|
+
const channel = this.channel;
|
|
17870
|
+
if (channel === void 0) return result;
|
|
17871
|
+
const lookups = [];
|
|
17872
|
+
if (missingUsers.length > 0 && channel.userProfiles !== void 0) lookups.push(channel.userProfiles(missingUsers).then((found) => {
|
|
17873
|
+
for (const id of missingUsers) {
|
|
17874
|
+
const profile = found.get(id) ?? {};
|
|
17875
|
+
this.profileCache.set(`user:${id}`, {
|
|
17876
|
+
profile,
|
|
17877
|
+
at: now
|
|
17878
|
+
});
|
|
17879
|
+
keep(result.users, id, profile);
|
|
17880
|
+
}
|
|
17881
|
+
}, (error) => {
|
|
17882
|
+
warnings.push(`读取用户资料失败:${feishuReason(error)}`);
|
|
17883
|
+
}));
|
|
17884
|
+
const chatProfile = channel.chatProfile?.bind(channel);
|
|
17885
|
+
if (chatProfile !== void 0) {
|
|
17886
|
+
let chatError;
|
|
17887
|
+
for (const id of missingChats) lookups.push(chatProfile(id).then((profile) => {
|
|
17888
|
+
this.profileCache.set(`chat:${id}`, {
|
|
17889
|
+
profile,
|
|
17890
|
+
at: now
|
|
17891
|
+
});
|
|
17892
|
+
keep(result.chats, id, profile);
|
|
17893
|
+
}, (error) => {
|
|
17894
|
+
if (chatError !== void 0) return;
|
|
17895
|
+
chatError = `读取群信息失败:${feishuReason(error)}`;
|
|
17896
|
+
warnings.push(chatError);
|
|
17897
|
+
}));
|
|
17898
|
+
}
|
|
17899
|
+
await Promise.all(lookups);
|
|
17900
|
+
if (warnings.length > 0) {
|
|
17901
|
+
result.warning = warnings.join(";");
|
|
17902
|
+
this.ctx.logger?.debug?.("dsh-channels: Feishu profile lookup: %s", result.warning);
|
|
17903
|
+
}
|
|
17904
|
+
return result;
|
|
17905
|
+
}
|
|
17906
|
+
/** Chat-member-list name for a user the contact profile cannot name. */
|
|
17907
|
+
async memberName(userId, chatId) {
|
|
17908
|
+
const cached = this.profileCache.get(`member:${chatId}:${userId}`);
|
|
17909
|
+
if (cached !== void 0 && Date.now() - cached.at < PROFILE_TTL_MS) return cached.profile.name;
|
|
17910
|
+
const name = await this.channel?.memberName?.(userId, chatId);
|
|
17911
|
+
this.profileCache.set(`member:${chatId}:${userId}`, {
|
|
17912
|
+
profile: name === void 0 ? {} : { name },
|
|
17913
|
+
at: Date.now()
|
|
17914
|
+
});
|
|
17915
|
+
return name;
|
|
17916
|
+
}
|
|
17656
17917
|
/** Panel: decide one pending authorization request (same path as the card). */
|
|
17657
17918
|
async resolveAccessRequest(token, decision) {
|
|
17658
17919
|
const requests = this.accessRequests;
|
|
@@ -19372,6 +19633,11 @@ async function readJsonBody(req) {
|
|
|
19372
19633
|
throw new HttpError(400, "请求体不是合法 JSON");
|
|
19373
19634
|
}
|
|
19374
19635
|
}
|
|
19636
|
+
/** Distinct non-empty entries of one comma-separated id query parameter. */
|
|
19637
|
+
function idList(raw) {
|
|
19638
|
+
if (raw === null) return [];
|
|
19639
|
+
return [...new Set(raw.split(",").map((id) => id.trim()).filter((id) => id !== ""))];
|
|
19640
|
+
}
|
|
19375
19641
|
/**
|
|
19376
19642
|
* Parse one JSON-object body for an access write. HttpErrors are answered
|
|
19377
19643
|
* here (415/413/400); `undefined` means "already responded, stop".
|
|
@@ -19477,6 +19743,50 @@ function registerSettingsRoutes(server, gate, surface) {
|
|
|
19477
19743
|
sendJson(res, 200, await surface.catalogFor(url.searchParams.get("provider") ?? void 0, url.searchParams.get("model") ?? void 0));
|
|
19478
19744
|
})
|
|
19479
19745
|
}),
|
|
19746
|
+
server.register({
|
|
19747
|
+
kind: "exact",
|
|
19748
|
+
path: SESSION_ORIGIN_ROUTE,
|
|
19749
|
+
handler: guarded(async (req, res) => {
|
|
19750
|
+
if (!allowOnly(req, res, ["GET", "HEAD"])) return;
|
|
19751
|
+
if (methodOf(req) === "HEAD") {
|
|
19752
|
+
res.writeHead(200, { "cache-control": "no-store" });
|
|
19753
|
+
res.end();
|
|
19754
|
+
return;
|
|
19755
|
+
}
|
|
19756
|
+
const sessionId = new URL(req.url ?? "/", "http://dsh.local").searchParams.get("sessionId")?.trim();
|
|
19757
|
+
if (sessionId === void 0 || sessionId === "") {
|
|
19758
|
+
sendJson(res, 400, {
|
|
19759
|
+
ok: false,
|
|
19760
|
+
error: "缺少 sessionId 参数"
|
|
19761
|
+
});
|
|
19762
|
+
return;
|
|
19763
|
+
}
|
|
19764
|
+
sendJson(res, 200, await surface.sessionOrigin(sessionId));
|
|
19765
|
+
})
|
|
19766
|
+
}),
|
|
19767
|
+
server.register({
|
|
19768
|
+
kind: "exact",
|
|
19769
|
+
path: PROFILES_ROUTE,
|
|
19770
|
+
handler: guarded(async (req, res) => {
|
|
19771
|
+
if (!allowOnly(req, res, ["GET", "HEAD"])) return;
|
|
19772
|
+
if (methodOf(req) === "HEAD") {
|
|
19773
|
+
res.writeHead(200, { "cache-control": "no-store" });
|
|
19774
|
+
res.end();
|
|
19775
|
+
return;
|
|
19776
|
+
}
|
|
19777
|
+
const params = new URL(req.url ?? "/", "http://dsh.local").searchParams;
|
|
19778
|
+
const userIds = idList(params.get("users"));
|
|
19779
|
+
const chatIds = idList(params.get("chats"));
|
|
19780
|
+
if (userIds.length > 200 || chatIds.length > 200) {
|
|
19781
|
+
sendJson(res, 400, {
|
|
19782
|
+
ok: false,
|
|
19783
|
+
error: `每次最多查询 200 个用户和 200 个群`
|
|
19784
|
+
});
|
|
19785
|
+
return;
|
|
19786
|
+
}
|
|
19787
|
+
sendJson(res, 200, await surface.feishuProfiles(userIds, chatIds));
|
|
19788
|
+
})
|
|
19789
|
+
}),
|
|
19480
19790
|
server.register({
|
|
19481
19791
|
kind: "exact",
|
|
19482
19792
|
path: CONFIG_ROUTE,
|
|
@@ -19735,12 +20045,13 @@ const CREDENTIAL_FIELDS = [
|
|
|
19735
20045
|
"appSecret",
|
|
19736
20046
|
"analyzeToken"
|
|
19737
20047
|
];
|
|
19738
|
-
/** Optional
|
|
20048
|
+
/** Optional fields: an empty string in the panel clears the override. */
|
|
19739
20049
|
const OPTIONAL_TEXT_KEYS = /* @__PURE__ */ new Set([
|
|
19740
20050
|
"provider",
|
|
19741
20051
|
"model",
|
|
19742
20052
|
"reasoningEffort",
|
|
19743
20053
|
"agentPreset",
|
|
20054
|
+
"maxTokens",
|
|
19744
20055
|
"adminUserId",
|
|
19745
20056
|
"analyzeListen"
|
|
19746
20057
|
]);
|
|
@@ -19829,26 +20140,47 @@ function normalizeStringList(key, raw, spec) {
|
|
|
19829
20140
|
};
|
|
19830
20141
|
}
|
|
19831
20142
|
const SELECT_OPTIONS = {
|
|
19832
|
-
attachmentStore: [{
|
|
19833
|
-
value: "native",
|
|
19834
|
-
label: "DSH 原生附件库(默认)"
|
|
19835
|
-
}, {
|
|
19836
|
-
value: "archive",
|
|
19837
|
-
label: "工作区 attachments/(旧版)"
|
|
19838
|
-
}],
|
|
19839
20143
|
dmMode: [{
|
|
19840
20144
|
value: "open",
|
|
19841
|
-
label: "
|
|
20145
|
+
label: "开启"
|
|
19842
20146
|
}, {
|
|
19843
20147
|
value: "disabled",
|
|
19844
|
-
label: "
|
|
20148
|
+
label: "关闭"
|
|
19845
20149
|
}],
|
|
19846
20150
|
threadFallback: [{
|
|
19847
20151
|
value: "degrade",
|
|
19848
|
-
label: "
|
|
20152
|
+
label: "降级为会话级"
|
|
19849
20153
|
}, {
|
|
19850
20154
|
value: "strict",
|
|
19851
|
-
label: "
|
|
20155
|
+
label: "直接失败"
|
|
20156
|
+
}],
|
|
20157
|
+
topicIsolation: [
|
|
20158
|
+
{
|
|
20159
|
+
value: "auto",
|
|
20160
|
+
label: "自动(推荐)"
|
|
20161
|
+
},
|
|
20162
|
+
{
|
|
20163
|
+
value: "subdir",
|
|
20164
|
+
label: "普通子目录"
|
|
20165
|
+
},
|
|
20166
|
+
{
|
|
20167
|
+
value: "worktree",
|
|
20168
|
+
label: "Git worktree"
|
|
20169
|
+
}
|
|
20170
|
+
],
|
|
20171
|
+
attachmentStore: [{
|
|
20172
|
+
value: "native",
|
|
20173
|
+
label: "DSH 原生附件库"
|
|
20174
|
+
}, {
|
|
20175
|
+
value: "archive",
|
|
20176
|
+
label: "话题目录 attachments/"
|
|
20177
|
+
}],
|
|
20178
|
+
accessMode: [{
|
|
20179
|
+
value: "guarded",
|
|
20180
|
+
label: "关闭(管理员与白名单)"
|
|
20181
|
+
}, {
|
|
20182
|
+
value: "open",
|
|
20183
|
+
label: "打开(所有人可聊)"
|
|
19852
20184
|
}],
|
|
19853
20185
|
loggerLevel: [
|
|
19854
20186
|
{
|
|
@@ -19867,260 +20199,341 @@ const SELECT_OPTIONS = {
|
|
|
19867
20199
|
value: "error",
|
|
19868
20200
|
label: "error"
|
|
19869
20201
|
}
|
|
19870
|
-
]
|
|
19871
|
-
accessMode: [{
|
|
19872
|
-
value: "guarded",
|
|
19873
|
-
label: "守卫模式(仅管理员与白名单)"
|
|
19874
|
-
}, {
|
|
19875
|
-
value: "open",
|
|
19876
|
-
label: "开放(不安全,仅调试)"
|
|
19877
|
-
}]
|
|
20202
|
+
]
|
|
19878
20203
|
};
|
|
19879
|
-
/**
|
|
20204
|
+
/** Two-value selects rendered as switches: the stored value while on / off. */
|
|
20205
|
+
const TOGGLES = {
|
|
20206
|
+
dmMode: {
|
|
20207
|
+
on: "open",
|
|
20208
|
+
off: "disabled"
|
|
20209
|
+
},
|
|
20210
|
+
threadFallback: {
|
|
20211
|
+
on: "degrade",
|
|
20212
|
+
off: "strict"
|
|
20213
|
+
},
|
|
20214
|
+
attachmentStore: {
|
|
20215
|
+
on: "native",
|
|
20216
|
+
off: "archive"
|
|
20217
|
+
},
|
|
20218
|
+
accessMode: {
|
|
20219
|
+
on: "open",
|
|
20220
|
+
off: "guarded"
|
|
20221
|
+
}
|
|
20222
|
+
};
|
|
20223
|
+
const DAY_MS = 864e5;
|
|
19880
20224
|
const FIELD_META = {
|
|
20225
|
+
dmMode: {
|
|
20226
|
+
label: "私聊",
|
|
20227
|
+
group: "trigger",
|
|
20228
|
+
summary: "关闭后私聊不再触发会话",
|
|
20229
|
+
help: "群聊和已绑定的话题不受影响。"
|
|
20230
|
+
},
|
|
20231
|
+
requireMention: {
|
|
20232
|
+
label: "普通群需要 @ 机器人",
|
|
20233
|
+
group: "trigger",
|
|
20234
|
+
summary: "话题内始终不需要 @",
|
|
20235
|
+
help: "关闭后普通群里的每条消息都会触发会话。"
|
|
20236
|
+
},
|
|
20237
|
+
threadFallback: {
|
|
20238
|
+
label: "话题被拒时降级为会话级",
|
|
20239
|
+
group: "trigger",
|
|
20240
|
+
summary: "飞书不允许建话题时改在会话里回复,关闭则直接报错"
|
|
20241
|
+
},
|
|
19881
20242
|
baseDir: {
|
|
19882
|
-
label: "
|
|
19883
|
-
|
|
20243
|
+
label: "工作区根目录",
|
|
20244
|
+
group: "workspace",
|
|
20245
|
+
summary: "模型和工具生成文件的位置",
|
|
20246
|
+
help: "每个用户一个工作区:私聊 <根目录>/p2p/<user_id>/,群聊 <根目录>/groups/<chat_id>/<user_id>/,每个话题在里面有独立目录。必须是绝对路径(~ 按家目录展开),保存时会创建该目录。只影响之后新建的话题,旧文件不迁移。",
|
|
19884
20247
|
placeholder: "/Users/you/.dsh/dsh-channels/workspaces",
|
|
19885
20248
|
wide: true
|
|
19886
20249
|
},
|
|
19887
|
-
|
|
19888
|
-
label: "
|
|
19889
|
-
|
|
20250
|
+
topicIsolation: {
|
|
20251
|
+
label: "话题工作目录",
|
|
20252
|
+
group: "workspace",
|
|
20253
|
+
summary: "每个话题在工作区里怎么隔离",
|
|
20254
|
+
help: "自动:工作区是 Git 仓库时给每个话题建一个 worktree,否则用普通子目录。选 Git worktree 但宿主不支持时退回普通子目录。只影响之后新建的话题,已有话题保持原来的目录。"
|
|
19890
20255
|
},
|
|
19891
|
-
|
|
19892
|
-
label: "
|
|
19893
|
-
|
|
20256
|
+
routeTtlMs: {
|
|
20257
|
+
label: "话题过期",
|
|
20258
|
+
group: "cards",
|
|
20259
|
+
summary: "闲置超过这么久的话题不再续聊,0 为永不过期",
|
|
20260
|
+
help: "过期话题里的新消息会被明确拒绝并提示新开话题,而不是悄悄开一个没有上下文的会话;Session 和工作区文件都保留。调小后,下一次清理(启动时和定期)就会让更多旧话题过期。",
|
|
20261
|
+
min: 0,
|
|
20262
|
+
step: DAY_MS,
|
|
20263
|
+
unit: "天",
|
|
20264
|
+
scale: DAY_MS
|
|
19894
20265
|
},
|
|
19895
|
-
|
|
19896
|
-
label: "
|
|
19897
|
-
|
|
20266
|
+
approvalTimeoutMs: {
|
|
20267
|
+
label: "审批卡片等待",
|
|
20268
|
+
group: "cards",
|
|
20269
|
+
summary: "超时自动拒绝,等待期间回复暂停",
|
|
20270
|
+
min: 1e4,
|
|
20271
|
+
max: 36e5,
|
|
20272
|
+
step: 1e3,
|
|
20273
|
+
unit: "秒",
|
|
20274
|
+
scale: 1e3
|
|
20275
|
+
},
|
|
20276
|
+
questionTimeoutMs: {
|
|
20277
|
+
label: "提问卡片等待",
|
|
20278
|
+
group: "cards",
|
|
20279
|
+
summary: "模型向用户提问时最多等多久",
|
|
20280
|
+
min: 1e4,
|
|
20281
|
+
max: 36e5,
|
|
20282
|
+
step: 1e3,
|
|
20283
|
+
unit: "秒",
|
|
20284
|
+
scale: 1e3
|
|
20285
|
+
},
|
|
20286
|
+
refreshMs: {
|
|
20287
|
+
label: "流式刷新间隔",
|
|
20288
|
+
group: "cards",
|
|
20289
|
+
summary: "回复卡片多久更新一次",
|
|
20290
|
+
min: 200,
|
|
20291
|
+
max: 1e4,
|
|
20292
|
+
step: 100,
|
|
20293
|
+
unit: "ms"
|
|
20294
|
+
},
|
|
20295
|
+
statusRefreshMs: {
|
|
20296
|
+
label: "状态行保活间隔",
|
|
20297
|
+
group: "cards",
|
|
20298
|
+
summary: "没有文字输出时,计时多久刷新一次",
|
|
20299
|
+
min: 1e3,
|
|
20300
|
+
max: 6e4,
|
|
20301
|
+
step: 500,
|
|
20302
|
+
unit: "ms"
|
|
19898
20303
|
},
|
|
19899
20304
|
welcomeOnboarding: {
|
|
19900
|
-
label: "
|
|
19901
|
-
|
|
20305
|
+
label: "首次私聊发引导卡",
|
|
20306
|
+
group: "features",
|
|
20307
|
+
summary: "每个私聊只发一次"
|
|
19902
20308
|
},
|
|
19903
20309
|
slashCommandPanel: {
|
|
19904
|
-
label: "
|
|
19905
|
-
|
|
20310
|
+
label: "同步斜杠指令面板",
|
|
20311
|
+
group: "features",
|
|
20312
|
+
summary: "在飞书输入 / 时列出插件命令",
|
|
20313
|
+
help: "需要 application:app_slash_command:read 和 application:app_slash_command:write 权限。部分企业不允许应用添加斜杠指令;关闭时不读写面板,直接输入命令不受影响。"
|
|
19906
20314
|
},
|
|
19907
20315
|
allowPermissionSwitch: {
|
|
19908
|
-
label: "
|
|
19909
|
-
|
|
20316
|
+
label: "允许在飞书里切换权限预设",
|
|
20317
|
+
group: "features",
|
|
20318
|
+
summary: "话题所有者可以用 /permissions 改沙箱和审批"
|
|
19910
20319
|
},
|
|
19911
|
-
|
|
19912
|
-
label: "
|
|
19913
|
-
|
|
19914
|
-
|
|
20320
|
+
provider: {
|
|
20321
|
+
label: "服务商",
|
|
20322
|
+
group: "model",
|
|
20323
|
+
summary: "留空使用部署默认",
|
|
20324
|
+
placeholder: "留空 = 部署默认"
|
|
19915
20325
|
},
|
|
19916
|
-
|
|
19917
|
-
label: "
|
|
19918
|
-
|
|
19919
|
-
|
|
20326
|
+
model: {
|
|
20327
|
+
label: "模型",
|
|
20328
|
+
group: "model",
|
|
20329
|
+
summary: "留空使用部署默认",
|
|
20330
|
+
placeholder: "留空 = 部署默认"
|
|
19920
20331
|
},
|
|
19921
|
-
|
|
19922
|
-
label: "
|
|
19923
|
-
|
|
20332
|
+
reasoningEffort: {
|
|
20333
|
+
label: "推理档位",
|
|
20334
|
+
group: "model",
|
|
20335
|
+
summary: "模型声明的档位 id,留空使用适配器默认",
|
|
20336
|
+
placeholder: "留空 = 适配器默认"
|
|
19924
20337
|
},
|
|
19925
|
-
|
|
19926
|
-
label: "
|
|
19927
|
-
|
|
20338
|
+
maxTokens: {
|
|
20339
|
+
label: "单次输出上限",
|
|
20340
|
+
group: "model",
|
|
20341
|
+
summary: "留空使用模型默认",
|
|
19928
20342
|
min: 1,
|
|
19929
|
-
|
|
19930
|
-
|
|
20343
|
+
step: 1,
|
|
20344
|
+
unit: "tokens",
|
|
20345
|
+
optional: true,
|
|
20346
|
+
placeholder: "模型默认"
|
|
20347
|
+
},
|
|
20348
|
+
agentPreset: {
|
|
20349
|
+
label: "Agent 预设",
|
|
20350
|
+
group: "model",
|
|
20351
|
+
summary: "留空使用 roster 默认",
|
|
20352
|
+
placeholder: "留空 = roster 默认"
|
|
19931
20353
|
},
|
|
19932
20354
|
acceptAttachments: {
|
|
19933
|
-
label: "
|
|
19934
|
-
|
|
20355
|
+
label: "接收图片和文件",
|
|
20356
|
+
group: "attachments",
|
|
20357
|
+
summary: "关闭后附件消息不保存、不触发模型",
|
|
20358
|
+
help: "纯附件消息不回复;需要时引用该消息再提问,多个文件可以合并转发后引用。也接收合并转发的聊天记录。"
|
|
19935
20359
|
},
|
|
19936
20360
|
attachmentStore: {
|
|
19937
|
-
label: "
|
|
19938
|
-
|
|
20361
|
+
label: "使用 DSH 原生附件库",
|
|
20362
|
+
group: "attachments",
|
|
20363
|
+
summary: "推荐开启;关闭则下载到话题目录 attachments/",
|
|
20364
|
+
help: "原生附件库:不可变、内容寻址去重、随 Session 回放,不自动清理。话题目录:旧版行为,可以手动清理,但下载会阻塞会话。宿主没有附件服务时自动退回话题目录。"
|
|
19939
20365
|
},
|
|
19940
20366
|
prefetchImages: {
|
|
19941
|
-
label: "
|
|
19942
|
-
|
|
20367
|
+
label: "预取触发消息里的图片",
|
|
20368
|
+
group: "attachments",
|
|
20369
|
+
summary: "首轮直接带图,失败的留给模型按需取"
|
|
20370
|
+
},
|
|
20371
|
+
historyBootstrapLimit: {
|
|
20372
|
+
label: "首轮带上话题历史",
|
|
20373
|
+
group: "attachments",
|
|
20374
|
+
summary: "新会话自动附带最近几条消息,0 为关闭",
|
|
20375
|
+
help: "更早的消息由模型用 feishu_topic_messages 工具翻页读取。缺权限或读取失败时静默省略。",
|
|
20376
|
+
min: 0,
|
|
20377
|
+
max: 20,
|
|
20378
|
+
step: 1,
|
|
20379
|
+
unit: "条"
|
|
20380
|
+
},
|
|
20381
|
+
"feishuTools.attachmentFetch": {
|
|
20382
|
+
label: "读取附件",
|
|
20383
|
+
group: "tools",
|
|
20384
|
+
summary: "按需下载已授权消息里的图片和文件",
|
|
20385
|
+
help: "工具名 feishu_attachment_fetch。关闭后文件无法按需读取,入站文件直接下载到话题目录。对新会话生效。"
|
|
20386
|
+
},
|
|
20387
|
+
"feishuTools.topicMessages": {
|
|
20388
|
+
label: "读取话题消息",
|
|
20389
|
+
group: "tools",
|
|
20390
|
+
summary: "翻看引用消息和话题里更早的内容",
|
|
20391
|
+
help: "工具名 feishu_topic_messages。只影响模型主动读取;引用摘录和首轮历史不受影响。"
|
|
20392
|
+
},
|
|
20393
|
+
"feishuTools.sendFile": {
|
|
20394
|
+
label: "发送文件",
|
|
20395
|
+
group: "tools",
|
|
20396
|
+
summary: "把生成的图片或文件发回会话,需 im:resource 权限",
|
|
20397
|
+
help: "工具名 feishu_send_file。图片不超过 10 MB,文件不超过 30 MB。对新会话生效。"
|
|
19943
20398
|
},
|
|
19944
20399
|
prefetchTimeoutMs: {
|
|
19945
|
-
label: "
|
|
19946
|
-
|
|
20400
|
+
label: "图片预取超时",
|
|
20401
|
+
group: "limits",
|
|
19947
20402
|
min: 100,
|
|
19948
20403
|
max: 12e4,
|
|
19949
|
-
step: 100
|
|
20404
|
+
step: 100,
|
|
20405
|
+
unit: "ms"
|
|
19950
20406
|
},
|
|
19951
20407
|
prefetchImageMaxBytes: {
|
|
19952
|
-
label: "
|
|
19953
|
-
|
|
20408
|
+
label: "单张预取图片上限",
|
|
20409
|
+
group: "limits",
|
|
19954
20410
|
min: 1,
|
|
19955
20411
|
max: 200 * 1024 * 1024,
|
|
19956
|
-
step: 1
|
|
20412
|
+
step: 1,
|
|
20413
|
+
unit: BYTES_UNIT
|
|
19957
20414
|
},
|
|
19958
20415
|
prefetchTotalMaxBytes: {
|
|
19959
|
-
label: "
|
|
19960
|
-
|
|
20416
|
+
label: "单次预取总量",
|
|
20417
|
+
group: "limits",
|
|
19961
20418
|
min: 1,
|
|
19962
20419
|
max: 1024 * 1024 * 1024,
|
|
19963
|
-
step: 1
|
|
19964
|
-
|
|
19965
|
-
historyBootstrapLimit: {
|
|
19966
|
-
label: "首轮话题历史条数",
|
|
19967
|
-
help: "新 Session 首轮自动附带本话题最近 N 条消息摘要(0 = 关闭,上限 20);被动读取使用短预算,缺权限/失败时静默省略。更早的消息用 feishu_topic_messages 工具翻页。",
|
|
19968
|
-
min: 0,
|
|
19969
|
-
max: 20,
|
|
19970
|
-
step: 1
|
|
19971
|
-
},
|
|
19972
|
-
promptContextTimeoutMs: {
|
|
19973
|
-
label: "被动读取预算(ms)",
|
|
19974
|
-
help: "引用摘录与首轮话题历史这类被动 prompt 增强的整段预算(默认 3000 ms,100–10000);超时即省略对应块、回合照跑。模型主动调用工具的读取超时另由 feishu_tools 超时控制。",
|
|
19975
|
-
min: 100,
|
|
19976
|
-
max: 1e4,
|
|
19977
|
-
step: 100
|
|
19978
|
-
},
|
|
19979
|
-
"feishuTools.attachmentFetch": {
|
|
19980
|
-
label: "启用 feishu_attachment_fetch 工具",
|
|
19981
|
-
help: "让模型按 message_id/file_key 懒取已授权消息的图片/文件。关闭后文件无法懒取,插件整体回退 archive 后端(入站文件直接下载到工作区)。对新会话生效。"
|
|
20420
|
+
step: 1,
|
|
20421
|
+
unit: BYTES_UNIT
|
|
19982
20422
|
},
|
|
19983
20423
|
"feishuTools.attachmentFetchMaxResources": {
|
|
19984
|
-
label: "
|
|
19985
|
-
|
|
20424
|
+
label: "读取附件:数量上限",
|
|
20425
|
+
group: "limits",
|
|
19986
20426
|
min: 1,
|
|
19987
20427
|
max: 50,
|
|
19988
|
-
step: 1
|
|
20428
|
+
step: 1,
|
|
20429
|
+
unit: "个"
|
|
19989
20430
|
},
|
|
19990
20431
|
"feishuTools.attachmentFetchMaxBytes": {
|
|
19991
|
-
label: "
|
|
19992
|
-
|
|
20432
|
+
label: "读取附件:总字节",
|
|
20433
|
+
group: "limits",
|
|
19993
20434
|
min: 1,
|
|
19994
|
-
step: 1
|
|
20435
|
+
step: 1,
|
|
20436
|
+
unit: BYTES_UNIT
|
|
19995
20437
|
},
|
|
19996
20438
|
"feishuTools.attachmentFetchTimeoutMs": {
|
|
19997
|
-
label: "
|
|
19998
|
-
|
|
20439
|
+
label: "读取附件:超时",
|
|
20440
|
+
group: "limits",
|
|
19999
20441
|
min: 1e3,
|
|
20000
20442
|
max: MAX_TIMER_DELAY_MS,
|
|
20001
|
-
step: 1e3
|
|
20002
|
-
|
|
20003
|
-
"feishuTools.topicMessages": {
|
|
20004
|
-
label: "启用 feishu_topic_messages 工具",
|
|
20005
|
-
help: "让模型按需读取引用消息与话题内更早的消息/附件。关闭只影响模型工具;被动的引用摘录与首轮历史仍由 historyBootstrapLimit 控制。"
|
|
20443
|
+
step: 1e3,
|
|
20444
|
+
unit: "ms"
|
|
20006
20445
|
},
|
|
20007
20446
|
"feishuTools.topicMessagesTimeoutMs": {
|
|
20008
|
-
label: "
|
|
20009
|
-
|
|
20447
|
+
label: "读取话题消息:超时",
|
|
20448
|
+
group: "limits",
|
|
20010
20449
|
min: 1e3,
|
|
20011
20450
|
max: 12e4,
|
|
20012
|
-
step: 1e3
|
|
20451
|
+
step: 1e3,
|
|
20452
|
+
unit: "ms"
|
|
20013
20453
|
},
|
|
20014
|
-
|
|
20015
|
-
label: "
|
|
20016
|
-
|
|
20454
|
+
promptContextTimeoutMs: {
|
|
20455
|
+
label: "引用摘录与历史:超时",
|
|
20456
|
+
group: "limits",
|
|
20457
|
+
min: 100,
|
|
20458
|
+
max: 1e4,
|
|
20459
|
+
step: 100,
|
|
20460
|
+
unit: "ms"
|
|
20461
|
+
},
|
|
20462
|
+
accessMode: {
|
|
20463
|
+
label: "调试模式",
|
|
20464
|
+
group: "access",
|
|
20465
|
+
summary: "打开后所有人都能聊,不安全,只在调试时用",
|
|
20466
|
+
help: "关闭时(默认):管理员随时可用;非管理员私聊会被拒绝;群聊要同时满足群已授权、发言人在用户白名单里。授权入口有三个:群里 /auth 命令、授权卡、本页。撤销立即生效。"
|
|
20467
|
+
},
|
|
20468
|
+
adminUserId: {
|
|
20469
|
+
label: "管理员 user_id",
|
|
20470
|
+
group: "access",
|
|
20471
|
+
summary: "私聊机器人发送 /userid 获取;留空使用应用所有者",
|
|
20472
|
+
help: "不是 ou_ 开头的 open_id,也不是邮箱。私聊关闭时 /userid 不可用,可以在飞书管理后台的成员详情里查看。自动使用应用所有者需要可选的「获取应用信息」权限。",
|
|
20473
|
+
placeholder: "user_id"
|
|
20474
|
+
},
|
|
20475
|
+
authRequestTtlDays: {
|
|
20476
|
+
label: "申请有效期",
|
|
20477
|
+
group: "access",
|
|
20478
|
+
summary: "授权申请卡多久内可以处理",
|
|
20479
|
+
min: 1,
|
|
20480
|
+
max: 30,
|
|
20481
|
+
step: 1,
|
|
20482
|
+
unit: "天"
|
|
20017
20483
|
},
|
|
20018
20484
|
"feishuTools.wikiRead": {
|
|
20019
|
-
label: "
|
|
20020
|
-
|
|
20485
|
+
label: "读取知识库",
|
|
20486
|
+
group: "wiki",
|
|
20487
|
+
summary: "浏览节点,读取文档、电子表格、多维表格",
|
|
20488
|
+
help: "以应用身份访问,工具 feishu_wiki_list / feishu_wiki_read / feishu_wiki_sheet_read / feishu_wiki_base_read。打开文档前在授权卡上征得话题发起人同意,每个话题一次。只有管理员或下方「可使用的人」发起的话题能用(/auth wiki add @成员)。需要权限 wiki:wiki、docx:document(或只读)、sheets:spreadsheet:read、base:table:read、base:field:read、base:record:read;旧版 sheets:spreadsheet、bitable:app 不生效。对新会话生效。"
|
|
20021
20489
|
},
|
|
20022
20490
|
"feishuTools.wikiWrite": {
|
|
20023
|
-
label: "
|
|
20024
|
-
|
|
20491
|
+
label: "编辑知识库",
|
|
20492
|
+
group: "wiki",
|
|
20493
|
+
summary: "新建和修改文档、表格,每次都需审批",
|
|
20494
|
+
help: "工具 feishu_wiki_create / feishu_wiki_edit / feishu_wiki_sheet_edit / feishu_wiki_base_edit。只能编辑本应用拥有的文档和表格;其他多维表格只能新增记录。需要读取工具的权限,以及 sheets:spreadsheet:write_only、base:record:create、base:record:update、base:record:delete。对新会话生效。"
|
|
20025
20495
|
},
|
|
20026
20496
|
wikiRoots: {
|
|
20027
|
-
label: "
|
|
20028
|
-
|
|
20497
|
+
label: "可访问范围",
|
|
20498
|
+
group: "wiki",
|
|
20499
|
+
summary: "每行一个知识空间或节点链接,为空时不挂载知识库工具",
|
|
20500
|
+
help: "整个知识空间 https://<租户>.feishu.cn/wiki/space/<space_id>,或节点 https://<租户>.feishu.cn/wiki/<node_token>(含其下所有节点)。最多 20 项,空行保存时忽略。对新会话生效。",
|
|
20029
20501
|
placeholder: "https://<租户>.feishu.cn/wiki/space/<space_id>",
|
|
20030
20502
|
wide: true
|
|
20031
20503
|
},
|
|
20032
|
-
|
|
20033
|
-
label: "
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
label: "模型 id",
|
|
20039
|
-
help: "留空 = 使用部署默认;例如 deepseek-v4-flash。",
|
|
20040
|
-
placeholder: "留空 = 部署默认"
|
|
20041
|
-
},
|
|
20042
|
-
reasoningEffort: {
|
|
20043
|
-
label: "推理档位",
|
|
20044
|
-
help: "模型声明的精确档位 id(如 high / max);留空 = 适配器默认。",
|
|
20045
|
-
placeholder: "留空 = 适配器默认"
|
|
20046
|
-
},
|
|
20047
|
-
agentPreset: {
|
|
20048
|
-
label: "Agent 预设",
|
|
20049
|
-
help: "新会话使用的预设 id(如 standard);留空 = roster 默认。",
|
|
20050
|
-
placeholder: "留空 = roster 默认"
|
|
20051
|
-
},
|
|
20052
|
-
refreshMs: {
|
|
20053
|
-
label: "流式刷新间隔(ms)",
|
|
20054
|
-
help: "流式卡片的 patch 节流间隔,200–10000;需重连生效。",
|
|
20055
|
-
min: 200,
|
|
20056
|
-
max: 1e4,
|
|
20057
|
-
step: 100
|
|
20058
|
-
},
|
|
20059
|
-
statusRefreshMs: {
|
|
20060
|
-
label: "状态行保活间隔(ms)",
|
|
20061
|
-
help: "无文本输出时活动状态行的计时刷新间隔,1000–60000;需重连生效。",
|
|
20062
|
-
min: 1e3,
|
|
20063
|
-
max: 6e4,
|
|
20064
|
-
step: 500
|
|
20504
|
+
analyzeListen: {
|
|
20505
|
+
label: "监听地址",
|
|
20506
|
+
group: "analyze",
|
|
20507
|
+
summary: "日志平台调用分析接口的 host:port,留空关闭",
|
|
20508
|
+
help: "接口路径 POST /dsh-channels/analyze,请求需带 Authorization: Bearer <访问令牌>。",
|
|
20509
|
+
placeholder: "例如 0.0.0.0:3090"
|
|
20065
20510
|
},
|
|
20066
20511
|
loggerLevel: {
|
|
20067
20512
|
label: "SDK 日志级别",
|
|
20068
|
-
|
|
20513
|
+
group: "logging"
|
|
20069
20514
|
}
|
|
20070
20515
|
};
|
|
20071
|
-
const GROUP_OF = {
|
|
20072
|
-
baseDir: "workspace",
|
|
20073
|
-
dmMode: "behavior",
|
|
20074
|
-
requireMention: "behavior",
|
|
20075
|
-
threadFallback: "behavior",
|
|
20076
|
-
welcomeOnboarding: "behavior",
|
|
20077
|
-
slashCommandPanel: "behavior",
|
|
20078
|
-
allowPermissionSwitch: "behavior",
|
|
20079
|
-
analyzeListen: "behavior",
|
|
20080
|
-
adminUserId: "access",
|
|
20081
|
-
accessMode: "access",
|
|
20082
|
-
authRequestTtlDays: "access",
|
|
20083
|
-
acceptAttachments: "content",
|
|
20084
|
-
attachmentStore: "content",
|
|
20085
|
-
prefetchImages: "content",
|
|
20086
|
-
prefetchTimeoutMs: "content",
|
|
20087
|
-
prefetchImageMaxBytes: "content",
|
|
20088
|
-
prefetchTotalMaxBytes: "content",
|
|
20089
|
-
historyBootstrapLimit: "content",
|
|
20090
|
-
promptContextTimeoutMs: "tools",
|
|
20091
|
-
"feishuTools.attachmentFetch": "tools",
|
|
20092
|
-
"feishuTools.attachmentFetchMaxResources": "tools",
|
|
20093
|
-
"feishuTools.attachmentFetchMaxBytes": "tools",
|
|
20094
|
-
"feishuTools.attachmentFetchTimeoutMs": "tools",
|
|
20095
|
-
"feishuTools.topicMessages": "tools",
|
|
20096
|
-
"feishuTools.topicMessagesTimeoutMs": "tools",
|
|
20097
|
-
"feishuTools.sendFile": "tools",
|
|
20098
|
-
"feishuTools.wikiRead": "wiki",
|
|
20099
|
-
"feishuTools.wikiWrite": "wiki",
|
|
20100
|
-
wikiRoots: "wiki",
|
|
20101
|
-
provider: "model",
|
|
20102
|
-
model: "model",
|
|
20103
|
-
reasoningEffort: "model",
|
|
20104
|
-
agentPreset: "model",
|
|
20105
|
-
refreshMs: "streaming",
|
|
20106
|
-
statusRefreshMs: "streaming",
|
|
20107
|
-
loggerLevel: "streaming"
|
|
20108
|
-
};
|
|
20109
20516
|
/** One descriptor per editable key, in `EDITABLE_KEYS` order (Host-driven form). */
|
|
20110
20517
|
const FIELD_DESCRIPTORS = EDITABLE_KEYS.map((key) => {
|
|
20111
20518
|
const meta = FIELD_META[key];
|
|
20112
20519
|
const options = SELECT_OPTIONS[key];
|
|
20520
|
+
const toggle = TOGGLES[key];
|
|
20113
20521
|
const list = STRING_LIST_FIELDS[key];
|
|
20114
20522
|
return {
|
|
20115
20523
|
key,
|
|
20116
20524
|
label: meta.label,
|
|
20117
20525
|
kind: list !== void 0 ? "string-list" : BOOLEAN_KEYS.has(key) ? "boolean" : options !== void 0 ? "select" : meta.min !== void 0 ? "number" : "text",
|
|
20118
|
-
group:
|
|
20119
|
-
|
|
20526
|
+
group: meta.group,
|
|
20527
|
+
...meta.summary === void 0 ? {} : { summary: meta.summary },
|
|
20528
|
+
...meta.help === void 0 ? {} : { help: meta.help },
|
|
20120
20529
|
...options === void 0 ? {} : { options },
|
|
20530
|
+
...toggle === void 0 ? {} : { toggle },
|
|
20121
20531
|
...meta.min === void 0 ? {} : { min: meta.min },
|
|
20122
20532
|
...meta.max === void 0 ? {} : { max: meta.max },
|
|
20123
20533
|
...meta.step === void 0 ? {} : { step: meta.step },
|
|
20534
|
+
...meta.unit === void 0 ? {} : { unit: meta.unit },
|
|
20535
|
+
...meta.scale === void 0 ? {} : { scale: meta.scale },
|
|
20536
|
+
...meta.optional === void 0 ? {} : { optional: meta.optional },
|
|
20124
20537
|
...meta.placeholder === void 0 ? {} : { placeholder: meta.placeholder },
|
|
20125
20538
|
...list === void 0 ? {} : { maxItems: list.maxItems },
|
|
20126
20539
|
...list?.itemFormat === void 0 ? {} : { itemFormat: list.itemFormat },
|
|
@@ -20214,15 +20627,6 @@ function withDirectoryBrowse(fields, available) {
|
|
|
20214
20627
|
function messageOf$1(error) {
|
|
20215
20628
|
return error instanceof Error ? error.message : String(error);
|
|
20216
20629
|
}
|
|
20217
|
-
function formatDays(ms) {
|
|
20218
|
-
return ms === 0 ? "关闭(不过期)" : `${Math.round(ms / 864e5)} 天`;
|
|
20219
|
-
}
|
|
20220
|
-
function formatSeconds(ms) {
|
|
20221
|
-
return `${Math.round(ms / 1e3)} 秒`;
|
|
20222
|
-
}
|
|
20223
|
-
function formatOptional(value, suffix = "") {
|
|
20224
|
-
return value === void 0 ? "未设置" : `${value}${suffix}`;
|
|
20225
|
-
}
|
|
20226
20630
|
/** Credential values are opaque strings; trim, cap length, reject control chars. */
|
|
20227
20631
|
const MAX_CREDENTIAL_LENGTH = 512;
|
|
20228
20632
|
function normalizeCredentialValue(raw) {
|
|
@@ -20308,39 +20712,6 @@ function normalizeAccessName(raw) {
|
|
|
20308
20712
|
value
|
|
20309
20713
|
};
|
|
20310
20714
|
}
|
|
20311
|
-
/** Build the read-only advanced card from the effective config. */
|
|
20312
|
-
function readOnlyFields(config) {
|
|
20313
|
-
return [
|
|
20314
|
-
{
|
|
20315
|
-
key: "topicIsolation",
|
|
20316
|
-
label: "话题目录隔离",
|
|
20317
|
-
value: config.topicIsolation === "auto" ? "自动(Git 仓库用 worktree,否则普通子目录)" : config.topicIsolation === "worktree" ? "Git worktree(失败回退子目录)" : "普通子目录",
|
|
20318
|
-
help: "每个话题的独立工作目录形式;在 profile 配置里用 topicIsolation 设置(auto / subdir / worktree)。"
|
|
20319
|
-
},
|
|
20320
|
-
{
|
|
20321
|
-
key: "routeTtlMs",
|
|
20322
|
-
label: "话题路由过期",
|
|
20323
|
-
value: formatDays(config.routeTtlMs),
|
|
20324
|
-
help: "超期话题的后续消息会被明确拒绝,而不是新建 Context-free Session。"
|
|
20325
|
-
},
|
|
20326
|
-
{
|
|
20327
|
-
key: "approvalTimeoutMs",
|
|
20328
|
-
label: "审批卡片等待",
|
|
20329
|
-
value: formatSeconds(config.approvalTimeoutMs)
|
|
20330
|
-
},
|
|
20331
|
-
{
|
|
20332
|
-
key: "questionTimeoutMs",
|
|
20333
|
-
label: "提问卡片等待",
|
|
20334
|
-
value: formatSeconds(config.questionTimeoutMs)
|
|
20335
|
-
},
|
|
20336
|
-
{
|
|
20337
|
-
key: "maxTokens",
|
|
20338
|
-
label: "单次输出上限",
|
|
20339
|
-
value: formatOptional(config.maxTokens, " tokens"),
|
|
20340
|
-
help: "留空/未设置时使用模型适配器默认值。"
|
|
20341
|
-
}
|
|
20342
|
-
];
|
|
20343
|
-
}
|
|
20344
20715
|
/**
|
|
20345
20716
|
* Derive the panel phase from readiness + runtime facts. The order matters:
|
|
20346
20717
|
* an incomplete configuration always wins (no connection is attempted), and a
|
|
@@ -20441,6 +20812,19 @@ var SettingsSurface = class {
|
|
|
20441
20812
|
* the draft provider/model changes). A broken lookup degrades to the
|
|
20442
20813
|
* free-text form instead of failing the panel.
|
|
20443
20814
|
*/
|
|
20815
|
+
/** Feishu origin of one Session for the conversation sidebar. */
|
|
20816
|
+
async sessionOrigin(sessionId) {
|
|
20817
|
+
return { origin: await this.target.sessionOrigin(sessionId) };
|
|
20818
|
+
}
|
|
20819
|
+
/**
|
|
20820
|
+
* Live Feishu names and avatars for the panel's users and chats.
|
|
20821
|
+
* @param userIds - user `user_id`s.
|
|
20822
|
+
* @param chatIds - chat `chat_id`s.
|
|
20823
|
+
* @returns the profiles Feishu resolved.
|
|
20824
|
+
*/
|
|
20825
|
+
async feishuProfiles(userIds, chatIds) {
|
|
20826
|
+
return this.target.feishuProfiles(userIds, chatIds);
|
|
20827
|
+
}
|
|
20444
20828
|
async catalogFor(provider, model) {
|
|
20445
20829
|
if (this.catalog === void 0) return emptyCatalog();
|
|
20446
20830
|
try {
|
|
@@ -20520,7 +20904,6 @@ var SettingsSurface = class {
|
|
|
20520
20904
|
overriddenKeys,
|
|
20521
20905
|
restartPendingKeys,
|
|
20522
20906
|
fields,
|
|
20523
|
-
readOnly: readOnlyFields(effective),
|
|
20524
20907
|
credentials
|
|
20525
20908
|
},
|
|
20526
20909
|
access
|
|
@@ -21234,6 +21617,6 @@ function apply(ctx, config) {
|
|
|
21234
21617
|
}, "dsh-channels");
|
|
21235
21618
|
}
|
|
21236
21619
|
//#endregion
|
|
21237
|
-
export { AUTH_USER_TARGET_ERRORS, AccessController, AccessRequests, AccessStore, ApprovalAnswerer, CARD_MAX_BYTES, CARD_TARGET_BYTES, CATALOG_ROUTE, CHANNEL_COMMAND_NAMES, CONFIG_ROUTE, CREDENTIALS_ROUTE, Config, DM_DENY_TEXT, DM_UNCONFIGURED_TEXT, DSH_CHANNELS_VERSION, EDITABLE_KEYS, EMPTY_TOTALS, FIELD_DESCRIPTORS, FeishuService, HEALTH_DEPENDENCY_NAMES, INTERRUPTION_NOTICE, LOGICAL_EXPORT_FORMAT, LOGICAL_EXPORT_FORMAT_VERSION, MAX_QUESTION_OPTIONS, MAX_TABLES_PER_CARD, OverlayStore, PANEL_CHEVRON_TOKEN, PANEL_COPY, PANEL_ROUTE, PANEL_TITLES, PICK_DIRECTORY_ROUTE, QuestionAnswerer, REASONING_MAX_BYTES, RECONNECT_ROUTE, RESTART_KEYS, RouteOperationQueue, SESSION_HISTORY_DISPLAY_LIMIT, STATUS_ROUTE, SUPPORTED_COMMAND_NAMES, SettingsSurface, TRUNCATION_SUFFIX, TurnStreamer, accessGroupRecordSchema, accessRequestRecordSchema, accessUserRecordSchema, addUsage, appendSessionHistory, apply, applyCatalog, applyProjectionViews, armModelCommand, averageDecodeTokensPerSecond, averageTtftMs, buildApprovalCard, buildApprovalSettledCard, buildCard, buildCommandResultCard, buildCompactComparisonCard, buildGroupInfoCard, buildHealthCard, buildInterruptionCard, buildQuestionCard, buildQuestionSettledCard, buildRenewCard, buildRequestSettledCard, buildSessionHistoryCard, buildSessionStatusCard, buildUserRequestCard, buildWorkDirCard, cacheHitPct, cardJsonBytes, chatRouteKey, commandResultCardKind, compactFailureContext, countFiberStates, createHostCatalog, createLogicalSessionExport, createSettingsDirectoryPicker, decideAccess, defaultOverlayPath, emptyCatalog, ensureWorkspace, expiredRouteRecordSchema, feishuDomain, foldAll, foldEvent, foldHealthSnapshot, foldSessionStats, inflightSnapshotSchema, inject, isAdminUserId, isSameOriginRequest, isSupportedCommand, maskOpenIdTail, measureSessionContext, mergeOverlay, mountSettingsRoutes, name, normalizeCredentialValue, normalizeOverlay, normalizeRouteRecord, normalizeSessionHistory, parseOverlay, parseSlashCommand, pickEditable, processedRecordSchema, querySessionHistory,
|
|
21620
|
+
export { AUTH_USER_TARGET_ERRORS, AccessController, AccessRequests, AccessStore, ApprovalAnswerer, CARD_MAX_BYTES, CARD_TARGET_BYTES, CATALOG_ROUTE, CHANNEL_COMMAND_NAMES, CONFIG_ROUTE, CREDENTIALS_ROUTE, Config, DM_DENY_TEXT, DM_UNCONFIGURED_TEXT, DSH_CHANNELS_VERSION, EDITABLE_KEYS, EMPTY_TOTALS, FIELD_DESCRIPTORS, FeishuService, HEALTH_DEPENDENCY_NAMES, INTERRUPTION_NOTICE, LOGICAL_EXPORT_FORMAT, LOGICAL_EXPORT_FORMAT_VERSION, MAX_QUESTION_OPTIONS, MAX_TABLES_PER_CARD, OverlayStore, PANEL_CHEVRON_TOKEN, PANEL_COPY, PANEL_ROUTE, PANEL_TITLES, PICK_DIRECTORY_ROUTE, QuestionAnswerer, REASONING_MAX_BYTES, RECONNECT_ROUTE, RESTART_KEYS, RouteOperationQueue, SESSION_HISTORY_DISPLAY_LIMIT, STATUS_ROUTE, SUPPORTED_COMMAND_NAMES, SettingsSurface, TRUNCATION_SUFFIX, TurnStreamer, accessGroupRecordSchema, accessRequestRecordSchema, accessUserRecordSchema, addUsage, appendSessionHistory, apply, applyCatalog, applyProjectionViews, armModelCommand, averageDecodeTokensPerSecond, averageTtftMs, buildApprovalCard, buildApprovalSettledCard, buildCard, buildCommandResultCard, buildCompactComparisonCard, buildGroupInfoCard, buildHealthCard, buildInterruptionCard, buildQuestionCard, buildQuestionSettledCard, buildRenewCard, buildRequestSettledCard, buildSessionHistoryCard, buildSessionStatusCard, buildUserRequestCard, buildWorkDirCard, cacheHitPct, cardJsonBytes, chatRouteKey, commandResultCardKind, compactFailureContext, countFiberStates, createHostCatalog, createLogicalSessionExport, createSettingsDirectoryPicker, decideAccess, defaultOverlayPath, emptyCatalog, ensureWorkspace, expiredRouteRecordSchema, feishuDomain, foldAll, foldEvent, foldHealthSnapshot, foldSessionStats, inflightSnapshotSchema, inject, isAdminUserId, isSameOriginRequest, isSupportedCommand, maskOpenIdTail, measureSessionContext, mergeOverlay, mountSettingsRoutes, name, normalizeCredentialValue, normalizeOverlay, normalizeRouteRecord, normalizeSessionHistory, parseOverlay, parseSlashCommand, pickEditable, processedRecordSchema, querySessionHistory, readProjectionViews, registerChannelCommands, registerModelCommand, registerSettingsRoutes, resolveAuthUserTarget, resolveDshHome, resolveWorkDir, routeRecordSchema, segmentCardContent, serializeLogicalSession, sessionDirOf, sessionHistoryEntrySchema, splitBodyForCard, stagingPathFor, stagingRoot, stripTables, supportedCommandHelp, supportedCommandList, tableBlocks, topicDirFor, topicTokenFor, usageFromBuckets, withDirectoryBrowse, workspacePathFor, workspaceTitleFor, worktreeBranchFor };
|
|
21238
21621
|
|
|
21239
21622
|
//# sourceMappingURL=index.js.map
|