@modesto_ma/dsh-channels 0.4.0 → 0.5.0-beta.1
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 +73 -55
- package/docs/access-control-design.md +7 -5
- package/docs/attachment-pipeline-v2.md +33 -41
- package/docs/feishu-onboarding.md +33 -33
- package/docs/persistence.md +6 -4
- package/docs/refactor-plan.md +1 -1
- package/docs/workspace-isolation-design.md +7 -8
- package/lib/client.js +173 -287
- package/lib/client.js.map +1 -1
- package/lib/index.js +3001 -2627
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/access/cards.ts +9 -9
- package/src/access/controller.ts +10 -10
- package/src/access/index.ts +1 -0
- package/src/access/policy.ts +10 -10
- package/src/access/requests.ts +32 -31
- package/src/access/store.ts +12 -12
- package/src/access/targets.ts +56 -0
- package/src/cards/approval.ts +96 -26
- package/src/cards/index.ts +1 -0
- package/src/cards/onboarding.ts +1 -0
- package/src/cards/picker.ts +115 -0
- package/src/cards/streaming.ts +56 -7
- package/src/client/api.ts +5 -25
- package/src/client/panel/access-card.tsx +24 -15
- package/src/client/panel/access-tab.tsx +3 -3
- package/src/client/panel/connection-tab.tsx +70 -1
- package/src/client/panel/index.tsx +11 -62
- package/src/client/panel/shared.tsx +0 -23
- package/src/client/panel/workspace-tab.tsx +2 -18
- package/src/client/styles.ts +2 -0
- package/src/commands/registry.ts +2 -2
- package/src/config.ts +25 -56
- package/src/domain.ts +28 -125
- package/src/feishu/inbound-attachments.ts +1 -1
- package/src/feishu/manifest.ts +8 -31
- package/src/feishu/message-read.ts +19 -22
- package/src/feishu/scopes.ts +171 -0
- package/src/feishu/send-file.ts +543 -0
- package/src/feishu/slash-commands.ts +134 -0
- package/src/feishu/tools.ts +72 -112
- package/src/feishu-channel.ts +270 -9
- package/src/index.ts +5 -4
- package/src/interactions/approvals.ts +187 -7
- package/src/interactions/pending.ts +3 -3
- package/src/routing/inbound.ts +73 -106
- package/src/service.ts +463 -438
- package/src/session/manager.ts +1 -1
- package/src/session/routes.ts +2 -3
- package/src/settings/contract.ts +68 -85
- package/src/settings/routes.ts +3 -17
- package/src/settings/surface.ts +37 -88
- package/src/streamer.ts +94 -5
- package/src/workspaces.ts +21 -29
- package/src/cleanup.ts +0 -546
- package/src/client/panel/cleanup-card.tsx +0 -106
- package/src/feishu/pending-attachments.ts +0 -562
package/src/session/manager.ts
CHANGED
|
@@ -293,7 +293,7 @@ export class SessionManager {
|
|
|
293
293
|
const workspacePath = record.workspacePath
|
|
294
294
|
const workspace = await this.deps.ctx.workspaceRegistry.create(
|
|
295
295
|
workspacePath,
|
|
296
|
-
workspaceTitleFor(record.chatType, record.chatId, record.
|
|
296
|
+
workspaceTitleFor(record.chatType, record.chatId, record.senderUserId),
|
|
297
297
|
)
|
|
298
298
|
|
|
299
299
|
let newHandle: AgentHandle | undefined
|
package/src/session/routes.ts
CHANGED
|
@@ -135,8 +135,7 @@ export class RouteStore {
|
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Retire one active thread route into a persistent tombstone regardless of
|
|
138
|
-
* the configured TTL.
|
|
139
|
-
* close topics earlier than the route TTL.
|
|
138
|
+
* the configured TTL.
|
|
140
139
|
*/
|
|
141
140
|
async retire(key: string, now = Date.now()): Promise<ExpiredRouteRecord | undefined> {
|
|
142
141
|
const record = this.get(key)
|
|
@@ -145,7 +144,7 @@ export class RouteStore {
|
|
|
145
144
|
key,
|
|
146
145
|
chatId: record.chatId,
|
|
147
146
|
chatType: record.chatType,
|
|
148
|
-
|
|
147
|
+
senderUserId: record.senderUserId,
|
|
149
148
|
expiredAt: now,
|
|
150
149
|
workspacePath: record.workspacePath,
|
|
151
150
|
...(record.sessionDir === undefined ? {} : { sessionDir: record.sessionDir }),
|
package/src/settings/contract.ts
CHANGED
|
@@ -20,13 +20,11 @@ export const CREDENTIALS_ROUTE = '/dsh-channels/credentials'
|
|
|
20
20
|
export const RECONNECT_ROUTE = '/dsh-channels/reconnect'
|
|
21
21
|
/** 模型/推理档位/预设选择器的宿主目录(provider → model → effort 级联刷新)。 */
|
|
22
22
|
export const CATALOG_ROUTE = '/dsh-channels/catalog'
|
|
23
|
-
/** 手动执行一次过期目录清理(POST,无请求体)。 */
|
|
24
|
-
export const CLEANUP_ROUTE = '/dsh-channels/cleanup'
|
|
25
23
|
/** 访问与授权只读快照(GET/HEAD):准入模式、管理员、群/用户白名单、待处理申请。 */
|
|
26
24
|
export const ACCESS_ROUTE = '/dsh-channels/access'
|
|
27
25
|
/** 群白名单增删(POST/DELETE,body `{ chatId, name? }` / `{ chatId }`)。 */
|
|
28
26
|
export const ACCESS_GROUPS_ROUTE = '/dsh-channels/access/groups'
|
|
29
|
-
/** 用户白名单增删(POST/DELETE,body `{
|
|
27
|
+
/** 用户白名单增删(POST/DELETE,body `{ userId, name? }` / `{ userId }`)。 */
|
|
30
28
|
export const ACCESS_USERS_ROUTE = '/dsh-channels/access/users'
|
|
31
29
|
/** 结算一条待处理授权申请(POST,body `{ token, decision }`)。 */
|
|
32
30
|
export const ACCESS_REQUESTS_ROUTE = '/dsh-channels/access/requests'
|
|
@@ -56,16 +54,15 @@ export const PICK_DIRECTORY_ROUTE = '/dsh-channels/pick-directory'
|
|
|
56
54
|
export const EDITABLE_KEYS = [
|
|
57
55
|
// 工作区
|
|
58
56
|
'baseDir',
|
|
59
|
-
'workspaceRetention',
|
|
60
|
-
'workspaceRetentionAuto',
|
|
61
57
|
// 行为
|
|
62
58
|
'dmMode',
|
|
63
59
|
'requireMention',
|
|
64
60
|
'threadFallback',
|
|
65
61
|
'welcomeOnboarding',
|
|
62
|
+
'slashCommandPanel',
|
|
66
63
|
'allowPermissionSwitch',
|
|
67
64
|
// 准入与授权(管理员/准入模式/申请有效期;白名单由「访问与授权」卡片管理)
|
|
68
|
-
'
|
|
65
|
+
'adminUserId',
|
|
69
66
|
'accessMode',
|
|
70
67
|
'authRequestTtlDays',
|
|
71
68
|
// 内容与附件(v2 管线;`feishuTools.*` 是嵌套配置,覆盖层仍写成嵌套对象)
|
|
@@ -76,8 +73,6 @@ export const EDITABLE_KEYS = [
|
|
|
76
73
|
'prefetchImageMaxBytes',
|
|
77
74
|
'prefetchTotalMaxBytes',
|
|
78
75
|
'historyBootstrapLimit',
|
|
79
|
-
'pendingAttachmentTtlMs',
|
|
80
|
-
'pendingAttachmentLimit',
|
|
81
76
|
// 模型工具(feishuTools 子项)
|
|
82
77
|
'feishuTools.attachmentFetch',
|
|
83
78
|
'feishuTools.attachmentFetchMaxResources',
|
|
@@ -85,6 +80,7 @@ export const EDITABLE_KEYS = [
|
|
|
85
80
|
'feishuTools.attachmentFetchTimeoutMs',
|
|
86
81
|
'feishuTools.topicMessages',
|
|
87
82
|
'feishuTools.topicMessagesTimeoutMs',
|
|
83
|
+
'feishuTools.sendFile',
|
|
88
84
|
'promptContextTimeoutMs',
|
|
89
85
|
// 模型
|
|
90
86
|
'provider',
|
|
@@ -106,6 +102,7 @@ export type EditableKey = (typeof EDITABLE_KEYS)[number]
|
|
|
106
102
|
export const RESTART_KEYS = [
|
|
107
103
|
'dmMode',
|
|
108
104
|
'requireMention',
|
|
105
|
+
'slashCommandPanel',
|
|
109
106
|
'refreshMs',
|
|
110
107
|
'statusRefreshMs',
|
|
111
108
|
'loggerLevel',
|
|
@@ -266,8 +263,40 @@ export interface ChannelStatusWire {
|
|
|
266
263
|
overlayPath: string
|
|
267
264
|
/** 正在连接或重连时为 true,此时面板的写操作按钮禁用。 */
|
|
268
265
|
busy: boolean
|
|
266
|
+
/** 连接后的权限自检结果;未连接或自检尚未完成时缺省。 */
|
|
267
|
+
scopeCheck?: ScopeCheckWire
|
|
268
|
+
/** 连接后飞书原生斜杠指令面板的同步结果;未连接或同步尚未完成时缺省。 */
|
|
269
|
+
slashCommandSync?: SlashCommandSyncWire
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
/** One plugin need whose scopes are all missing. */
|
|
273
|
+
export interface MissingScopeWire {
|
|
274
|
+
/** Scope names, preferred first; granting any one satisfies the need. */
|
|
275
|
+
scopes: string[]
|
|
276
|
+
/** `required` breaks a core path; `optional` only degrades a feature. */
|
|
277
|
+
level: 'required' | 'optional'
|
|
278
|
+
/** What the plugin loses without it. */
|
|
279
|
+
purpose: string
|
|
280
|
+
/** Developer-console permission page filtered to the preferred scope. */
|
|
281
|
+
consoleUrl: string
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** Post-connect scope self-check outcome. */
|
|
285
|
+
export type ScopeCheckWire =
|
|
286
|
+
/** Every applicable need is granted. */
|
|
287
|
+
| { state: 'ok'; checkedAt: number }
|
|
288
|
+
/** Some needs are missing; `consoleUrl` opens the console filtered to all of them. */
|
|
289
|
+
| { state: 'missing'; checkedAt: number; missing: MissingScopeWire[]; consoleUrl: string }
|
|
290
|
+
/** The grant list could not be read; `error` carries the Feishu reason. */
|
|
291
|
+
| { state: 'error'; checkedAt: number; error: string }
|
|
292
|
+
|
|
293
|
+
/** Post-connect native slash-command panel sync outcome. */
|
|
294
|
+
export type SlashCommandSyncWire =
|
|
295
|
+
/** The panel lists exactly the registry commands; counts are this sync's writes. */
|
|
296
|
+
| { state: 'ok'; checkedAt: number; created: number; updated: number; removed: number; total: number }
|
|
297
|
+
/** A Feishu call failed; `error` carries the Feishu reason. */
|
|
298
|
+
| { state: 'error'; checkedAt: number; error: string }
|
|
299
|
+
|
|
271
300
|
/** The whole editable configuration surface, with provenance. */
|
|
272
301
|
export interface PanelConfigWire {
|
|
273
302
|
/** profile `cordis.patch.yml` 提供的值(覆盖层之下)。 */
|
|
@@ -298,9 +327,9 @@ export interface AccessGroupWire {
|
|
|
298
327
|
addedAt: number
|
|
299
328
|
}
|
|
300
329
|
|
|
301
|
-
/** One authorized user `
|
|
330
|
+
/** One authorized user `user_id` (global across every authorized group). */
|
|
302
331
|
export interface AccessUserWire {
|
|
303
|
-
|
|
332
|
+
userId: string
|
|
304
333
|
/** Display name captured at authorization time. */
|
|
305
334
|
name?: string
|
|
306
335
|
/** Which authorization entry produced the grant. */
|
|
@@ -314,7 +343,7 @@ export interface AccessRequestWire {
|
|
|
314
343
|
/** Opaque token; never derived from the target id. */
|
|
315
344
|
token: string
|
|
316
345
|
kind: 'group' | 'user'
|
|
317
|
-
/** `chat_id` for a group request, `
|
|
346
|
+
/** `chat_id` for a group request, `user_id` for a user request. */
|
|
318
347
|
targetId: string
|
|
319
348
|
/** Display name of the requested target (group name / user name), when known. */
|
|
320
349
|
name?: string
|
|
@@ -337,9 +366,14 @@ export interface AccessRequestWire {
|
|
|
337
366
|
export interface AccessWire {
|
|
338
367
|
mode: 'open' | 'guarded'
|
|
339
368
|
admin: {
|
|
340
|
-
|
|
369
|
+
/** The effective administrator: configured `adminUserId`, else the app owner. */
|
|
370
|
+
userId?: string
|
|
371
|
+
/** Where `userId` came from: explicit config or the app-owner fallback. */
|
|
372
|
+
source?: 'config' | 'owner'
|
|
341
373
|
/** false while guarded mode would refuse every conversation (fail closed). */
|
|
342
374
|
configured: boolean
|
|
375
|
+
/** Why the app-owner fallback is unavailable, when its lookup failed. */
|
|
376
|
+
ownerLookupError?: string
|
|
343
377
|
}
|
|
344
378
|
/**
|
|
345
379
|
* Whether the persisted whitelists were loaded. While the channel holds no
|
|
@@ -361,51 +395,10 @@ export type AccessMutationResponse =
|
|
|
361
395
|
export interface PanelWire {
|
|
362
396
|
status: ChannelStatusWire
|
|
363
397
|
config: PanelConfigWire
|
|
364
|
-
cleanup: CleanupPreviewWire
|
|
365
398
|
/** 访问与授权卡片的数据(准入模式、管理员、白名单、待处理申请)。 */
|
|
366
399
|
access: AccessWire
|
|
367
400
|
}
|
|
368
401
|
|
|
369
|
-
/**
|
|
370
|
-
* What the workspace-retention cleanup would delete right now. Counts are
|
|
371
|
-
* zero and `error` is set when the Host cannot compute the preview (for
|
|
372
|
-
* example the cleanup seam is unavailable); the panel stays usable.
|
|
373
|
-
*/
|
|
374
|
-
export interface CleanupPreviewWire {
|
|
375
|
-
/** Effective `workspaceRetention` value (`off` / `7d` / `30d` / `90d` / `180d`). */
|
|
376
|
-
retention: string
|
|
377
|
-
/** Retention threshold in milliseconds; absent when `retention` is `off`. */
|
|
378
|
-
thresholdMs?: number
|
|
379
|
-
/** Periodic cleanup runs at startup and once per day. */
|
|
380
|
-
auto: boolean
|
|
381
|
-
/** Plugin-created workspaces under the current baseDir (user projects). */
|
|
382
|
-
workspaces: number
|
|
383
|
-
/** Directories older than the threshold (legacy topic workspaces + per-topic dirs). */
|
|
384
|
-
candidates: number
|
|
385
|
-
/** Total bytes currently occupied by the candidates. */
|
|
386
|
-
candidateBytes: number
|
|
387
|
-
/** Why the preview could not be computed (counts are zero then). */
|
|
388
|
-
error?: string
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/** Outcome of one manual cleanup run (partial failures do not abort the run). */
|
|
392
|
-
export interface CleanupRunWire {
|
|
393
|
-
ok: boolean
|
|
394
|
-
deletedWorkspaces: number
|
|
395
|
-
/** Per-topic working directories removed (current layout). */
|
|
396
|
-
deletedSessionDirs: number
|
|
397
|
-
retiredRoutes: number
|
|
398
|
-
freedBytes: number
|
|
399
|
-
/** One `path: message` line per directory that could not be deleted. */
|
|
400
|
-
failures: string[]
|
|
401
|
-
error?: string
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/** `POST /dsh-channels/cleanup` response. */
|
|
405
|
-
export type CleanupResponse =
|
|
406
|
-
| { ok: true; panel: PanelWire; result: CleanupRunWire }
|
|
407
|
-
| { ok: false; error: string }
|
|
408
|
-
|
|
409
402
|
/**
|
|
410
403
|
* `POST /dsh-channels/pick-directory` response. `canceled` is a normal
|
|
411
404
|
* outcome (the operator dismissed the OS dialog), not an error; `path` is an
|
|
@@ -540,6 +533,21 @@ export const PANEL_COPY = {
|
|
|
540
533
|
statusSummaryConnecting: '正在连接飞书长连接…',
|
|
541
534
|
statusSummaryError: '最近一次连接飞书失败,请检查凭据与网络后重试。',
|
|
542
535
|
lastError: '最近一次连接失败',
|
|
536
|
+
scopeCheckTitle: '权限自检',
|
|
537
|
+
scopeCheckOk: '已开通插件需要的全部飞书权限。',
|
|
538
|
+
scopeCheckMissing: '以下飞书权限未开通,开通后需在开发者后台发布新版本:',
|
|
539
|
+
scopeCheckOpenAll: '在开发者后台开通全部缺失权限',
|
|
540
|
+
scopeCheckOpenOne: '去开通',
|
|
541
|
+
scopeCheckOptional: '可选',
|
|
542
|
+
scopeCheckRequired: '必需',
|
|
543
|
+
scopeCheckError: '无法读取应用的权限授予状态:',
|
|
544
|
+
slashCommandSyncTitle: '斜杠指令面板',
|
|
545
|
+
slashCommandSyncOk: '插件命令已同步到飞书原生斜杠指令面板,在聊天框输入 / 即可选择(飞书客户端约 5 分钟后刷新)。',
|
|
546
|
+
slashCommandSyncTotal: '命令数',
|
|
547
|
+
slashCommandSyncCreated: '本次新增',
|
|
548
|
+
slashCommandSyncUpdated: '本次更新',
|
|
549
|
+
slashCommandSyncRemoved: '本次删除',
|
|
550
|
+
slashCommandSyncError: '同步飞书原生斜杠指令面板失败(不影响直接输入命令):',
|
|
543
551
|
missingConfig: '缺少配置',
|
|
544
552
|
statusHintUnconfigured: '配置完成后插件会自动连接;也可以稍后在本页手动连接。',
|
|
545
553
|
connectionInfo: '连接信息',
|
|
@@ -588,9 +596,9 @@ export const PANEL_COPY = {
|
|
|
588
596
|
credentialsPendingHint: '填写后点「保存凭据」;两项齐全时插件会自动连接飞书。',
|
|
589
597
|
// ---- tabs / forms -------------------------------------------------------
|
|
590
598
|
connectionTabHelp: '连接状态与飞书应用凭据。凭据不完整时不会连接飞书。',
|
|
591
|
-
sessionsTabHelp: '
|
|
599
|
+
sessionsTabHelp: '私聊、群聊触发方式与入站内容:附件存储/预取、模型工具开关与读取预算;「需重连」项在下次连接时读取。',
|
|
592
600
|
modelTabHelp: '飞书会话使用的模型服务商、模型、推理档位与 Agent 预设;下拉选项来自宿主机实时目录,保存后对新会话生效。',
|
|
593
|
-
workspaceTabHelp: '每个用户一个工作区(模型/工具产生文件的落点),每个话题在其中拥有独立工作目录;只影响之后新建的话题。入站附件默认存于 DSH
|
|
601
|
+
workspaceTabHelp: '每个用户一个工作区(模型/工具产生文件的落点),每个话题在其中拥有独立工作目录;只影响之后新建的话题。入站附件默认存于 DSH 原生附件库,不占用话题目录。插件不会自动删除工作区或话题目录;过期目录可手动删除,话题下次对话时会重建空目录。',
|
|
594
602
|
accessTabHelp: '准入采用 fail-closed:管理员不受白名单限制,非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。',
|
|
595
603
|
advancedTabHelp: '流式刷新等低层参数、Web 覆盖文件与只读信息。',
|
|
596
604
|
save: '保存更改',
|
|
@@ -637,37 +645,12 @@ export const PANEL_COPY = {
|
|
|
637
645
|
advancedHelp: '这些值来自 profile 配置,不能在面板里编辑。',
|
|
638
646
|
readOnlyCard: '部署级参数',
|
|
639
647
|
// ---- model --------------------------------------------------------------
|
|
640
|
-
// ---- cleanup ------------------------------------------------------------
|
|
641
|
-
cleanupCard: '目录清理',
|
|
642
|
-
cleanupCardHelp: '只清理工作区与 Agent 产物:删除超过保留期且不再活跃的飞书话题目录、旧版本按话题建立的工作区目录,以及这些目录内 Agent 产生的文件;不影响 Session 附件——入站图片/文件默认存于 DSH 原生附件库,不随目录清理删除。用户工作区本身与 DSH 会话日志也不会删除。删除不可恢复:相关旧话题会失效并提示已过期。',
|
|
643
|
-
cleanupRetention: '保留期',
|
|
644
|
-
cleanupAutoLabel: '自动清理',
|
|
645
|
-
cleanupCandidates: '可清理目录',
|
|
646
|
-
cleanupWorkspaces: '插件工作区',
|
|
647
|
-
cleanupCandidateBytes: '预计释放空间',
|
|
648
|
-
cleanupRetentionOff: '关闭(不删除)',
|
|
649
|
-
cleanupAutoOn: '已开启(启动时 + 每天一次)',
|
|
650
|
-
cleanupAutoOff: '已关闭(仅手动执行)',
|
|
651
|
-
cleanupAutoInactive: '未执行(保留期为关闭)',
|
|
652
|
-
cleanupNow: '立即清理',
|
|
653
|
-
cleanupRunning: '清理中…',
|
|
654
|
-
cleanupConfirm: '确定清理过期目录吗?只清理工作区与 Agent 产物,不影响 Session 附件;但这些目录内未提交的文件会被删除,且操作不可恢复。',
|
|
655
|
-
cleanupConfirmHint: '删除前会再次确认;保留期为「关闭」或没有过期目录时按钮不可用。',
|
|
656
|
-
cleanupDisabledOff: '保留策略为「关闭」:请先在上方「过期目录保留期」中选择保留天数。',
|
|
657
|
-
cleanupDisabledEmpty: '没有超过保留期的过期目录。',
|
|
658
|
-
cleanupPreviewFailed: '无法读取清理预览',
|
|
659
|
-
cleanupDone: '清理完成',
|
|
660
|
-
cleanupFailed: '清理失败',
|
|
661
|
-
cleanupDeleted: '已删除工作区',
|
|
662
|
-
cleanupDeletedDirs: '话题目录',
|
|
663
|
-
cleanupRetired: '已清理话题路由',
|
|
664
|
-
cleanupFreed: '释放空间',
|
|
665
|
-
cleanupFailures: '以下目录未能删除:',
|
|
666
648
|
// ---- access -------------------------------------------------------------
|
|
667
649
|
accessCard: '访问与授权',
|
|
668
|
-
accessCardHelp: '准入采用 fail-closed:管理员不受白名单限制(私聊、任意群聊都可直接对话);非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。管理员与准入模式在上方「准入与授权」中编辑;私聊机器人发送 /userid 获取自己的
|
|
650
|
+
accessCardHelp: '准入采用 fail-closed:管理员不受白名单限制(私聊、任意群聊都可直接对话);非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。管理员与准入模式在上方「准入与授权」中编辑;私聊机器人发送 /userid 获取自己的 user_id。',
|
|
669
651
|
accessModeOpenWarning: '当前准入模式为开放(open):应用可用范围内的任何人都能开启对话。该模式不安全,仅建议调试时使用。',
|
|
670
|
-
accessAdminMissingWarning: '当前准入=guarded
|
|
652
|
+
accessAdminMissingWarning: '当前准入=guarded 但未配置管理员,也无法自动使用应用所有者,所有对话都会被拒绝。请在上方「准入与授权」填写管理员 user_id(私聊机器人发送 /userid 获取;私聊模式关闭时 /userid 不可用,可在飞书管理后台的成员详情里查看用户 ID)。',
|
|
653
|
+
accessAdminOwnerNotice: '未配置管理员,已自动使用应用所有者作为管理员:',
|
|
671
654
|
accessGroupsTitle: '已授权群',
|
|
672
655
|
accessGroupsEmpty: '尚未授权任何群。机器人被拉进群后,管理员会收到带 chat_id 的群信息卡;也可在群内发送 /groupid 获取。',
|
|
673
656
|
accessUsersTitle: '已授权用户',
|
|
@@ -676,7 +659,7 @@ export const PANEL_COPY = {
|
|
|
676
659
|
accessRequestsEmpty: '没有待处理的授权申请。',
|
|
677
660
|
accessGroupChatIdPlaceholder: '群 chat_id(oc_…)',
|
|
678
661
|
accessGroupNamePlaceholder: '群名(可选)',
|
|
679
|
-
|
|
662
|
+
accessUserIdPlaceholder: '用户 user_id',
|
|
680
663
|
accessUserNamePlaceholder: '昵称(可选)',
|
|
681
664
|
accessAddGroup: '添加群',
|
|
682
665
|
accessAddUser: '添加用户',
|
|
@@ -698,5 +681,5 @@ export const PANEL_COPY = {
|
|
|
698
681
|
accessRequestAllowed: '已授权',
|
|
699
682
|
accessRequestDenied: '已忽略',
|
|
700
683
|
accessHints: '三种授权入口:群内 /auth 命令、入群与用户授权卡、本面板。撤销立即生效;白名单只约束非管理员(管理员在任何私聊/群聊都直接放行),非管理员需要群和用户两层同时命中。',
|
|
701
|
-
accessChatIdHint: 'chat_id 可从入群群信息卡或群内 /groupid 获取;
|
|
684
|
+
accessChatIdHint: 'chat_id 可从入群群信息卡或群内 /groupid 获取;user_id 可私聊机器人发送 /userid 获取。',
|
|
702
685
|
} as const
|
package/src/settings/routes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Web 设置面板的 HTTP 路由:status / panel / catalog / config(PUT,DELETE) /
|
|
3
|
-
* credentials(PUT,DELETE) / reconnect /
|
|
3
|
+
* credentials(PUT,DELETE) / reconnect / access。
|
|
4
4
|
*
|
|
5
5
|
* 路由注册进宿主可选的 `webServer` 服务(headless profile 没有它时面板自然
|
|
6
6
|
* 不存在,飞书通道不受影响)。写操作额外做同源校验 —— 本机非回环绑定下
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
ACCESS_ROUTE,
|
|
21
21
|
ACCESS_USERS_ROUTE,
|
|
22
22
|
CATALOG_ROUTE,
|
|
23
|
-
CLEANUP_ROUTE,
|
|
24
23
|
CONFIG_ROUTE,
|
|
25
24
|
CREDENTIALS_ROUTE,
|
|
26
25
|
PANEL_ROUTE,
|
|
@@ -310,19 +309,6 @@ export function registerSettingsRoutes(server: HostWebServer, surface: SettingsS
|
|
|
310
309
|
sendJson(res, result.ok ? 200 : (result.unconfigured === true ? 400 : 500), result)
|
|
311
310
|
}),
|
|
312
311
|
}),
|
|
313
|
-
server.register({
|
|
314
|
-
kind: 'exact',
|
|
315
|
-
path: CLEANUP_ROUTE,
|
|
316
|
-
handler: guarded(async (req, res) => {
|
|
317
|
-
if (!allowOnly(req, res, ['POST'])) return
|
|
318
|
-
if (!isSameOriginRequest(req)) {
|
|
319
|
-
forbiddenWrite(res)
|
|
320
|
-
return
|
|
321
|
-
}
|
|
322
|
-
const result = await surface.runCleanup()
|
|
323
|
-
sendJson(res, result.ok ? 200 : 500, result)
|
|
324
|
-
}),
|
|
325
|
-
}),
|
|
326
312
|
server.register({
|
|
327
313
|
kind: 'exact',
|
|
328
314
|
path: PICK_DIRECTORY_ROUTE,
|
|
@@ -404,8 +390,8 @@ export function registerSettingsRoutes(server: HostWebServer, surface: SettingsS
|
|
|
404
390
|
const payload = await readAccessBody(req, res)
|
|
405
391
|
if (payload === undefined) return
|
|
406
392
|
const result = methodOf(req) === 'DELETE'
|
|
407
|
-
? await surface.removeAccessUser(payload.
|
|
408
|
-
: await surface.saveAccessUser(payload.
|
|
393
|
+
? await surface.removeAccessUser(payload.userId)
|
|
394
|
+
: await surface.saveAccessUser(payload.userId, payload.name)
|
|
409
395
|
sendJson(res, result.ok ? 200 : 400, result)
|
|
410
396
|
}),
|
|
411
397
|
}),
|