@modesto_ma/dsh-channels 0.2.0 → 0.3.0
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 +53 -29
- package/docs/access-control-design.md +1 -1
- package/docs/feasibility-and-design.md +54 -26
- package/docs/feishu-onboarding.md +15 -13
- package/docs/persistence.md +4 -4
- package/docs/refactor-plan.md +6 -5
- package/docs/workspace-isolation-design.md +78 -0
- package/lib/client.js +23 -13
- package/lib/client.js.map +1 -1
- package/lib/index.js +1110 -603
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cards/index.ts +1 -0
- package/src/cards/onboarding.ts +23 -0
- package/src/cards/primitives.ts +20 -0
- package/src/cards/streaming.ts +310 -291
- package/src/cleanup.ts +256 -119
- package/src/client/panel/cleanup-card.tsx +4 -2
- package/src/client/panel/workspace-tab.tsx +1 -1
- package/src/config.ts +27 -8
- package/src/domain.ts +34 -3
- package/src/feishu/agent-context.ts +9 -5
- package/src/index.ts +8 -6
- package/src/routing/inbound.ts +88 -43
- package/src/service.ts +119 -41
- package/src/session/isolation.ts +277 -0
- package/src/session/manager.ts +63 -20
- package/src/session/repair.ts +2 -3
- package/src/session/routes.ts +2 -0
- package/src/session-commands.ts +41 -14
- package/src/settings/contract.ts +18 -14
- package/src/settings/surface.ts +12 -4
- package/src/streamer.ts +70 -30
- package/src/workspaces.ts +50 -28
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["messageOf","requestDirectoryPicker"],"sources":["../src/settings/contract.ts","../src/client/api.ts","../src/client/catalog.ts","../src/client/panel/shared.tsx","../src/client/panel/access-card.tsx","../src/client/panel/access-tab.tsx","../src/client/panel/advanced-tab.tsx","../src/client/panel/credentials-card.tsx","../src/client/panel/connection-tab.tsx","../src/client/panel/model-tab.tsx","../src/client/panel/sessions-tab.tsx","../src/client/panel/cleanup-card.tsx","../src/client/panel/workspace-tab.tsx","../src/client/panel/index.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Web 设置面板的线上契约:Host 半侧的路由/面板数据与 Browser 半侧的渲染共用。\n *\n * 本模块**没有运行时依赖**:Host 与 Client bundle 都直接内联它。因此这里只放\n * 常量、字段清单与可序列化的 wire 类型,不 import zod / node / DSH 内部包。\n *\n * @module dsh-channels/settings/contract\n */\n\n/** Same-origin routes the settings panel talks to. */\nexport const STATUS_ROUTE = '/dsh-channels/status'\nexport const PANEL_ROUTE = '/dsh-channels/panel'\nexport const CONFIG_ROUTE = '/dsh-channels/config'\nexport const CREDENTIALS_ROUTE = '/dsh-channels/credentials'\n/**\n * Connection lifecycle action (POST). While the channel runs it restarts the\n * long connection; while it is stopped it opens one — so the panel has exactly\n * one \"connect / reconnect\" button. Credentials are resolved again either way.\n */\nexport const RECONNECT_ROUTE = '/dsh-channels/reconnect'\n/** 模型/推理档位/预设选择器的宿主目录(provider → model → effort 级联刷新)。 */\nexport const CATALOG_ROUTE = '/dsh-channels/catalog'\n/** 手动执行一次过期工作区清理(POST,无请求体)。 */\nexport const CLEANUP_ROUTE = '/dsh-channels/cleanup'\n/** 访问与授权只读快照(GET/HEAD):准入模式、管理员、群/用户白名单、待处理申请。 */\nexport const ACCESS_ROUTE = '/dsh-channels/access'\n/** 群白名单增删(POST/DELETE,body `{ chatId, name? }` / `{ chatId }`)。 */\nexport const ACCESS_GROUPS_ROUTE = '/dsh-channels/access/groups'\n/** 用户白名单增删(POST/DELETE,body `{ openId, name? }` / `{ openId }`)。 */\nexport const ACCESS_USERS_ROUTE = '/dsh-channels/access/users'\n/** 结算一条待处理授权申请(POST,body `{ token, decision }`)。 */\nexport const ACCESS_REQUESTS_ROUTE = '/dsh-channels/access/requests'\n/**\n * 打开宿主机的系统目录选择框(POST,无请求体),返回绝对路径。\n *\n * 只有宿主挂载了 `directoryPicker` 的 native 后端(桌面/本地 GUI 会话)才\n * 会注册实际选择逻辑;远程/无头会话的 browse 后端不可用,面板不下发\n * [`EditableFieldWire.browse`],保留手写输入。\n */\nexport const PICK_DIRECTORY_ROUTE = '/dsh-channels/pick-directory'\n\n/**\n * 可由 Web 面板覆盖的配置项。全部是 `src/config.ts` 里已存在的字段;\n * 覆盖值写进 `$DSH_HOME/dsh-channels/settings.json`,优先级高于 profile\n * 的 `cordis.patch.yml`(见 README「Web 设置面板」)。\n *\n * 刻意不包含 `appId` / `appSecret`:它们是**凭据引用名**,面板改为直接\n * 编辑引用背后的值(`PUT /dsh-channels/credentials`,经 DSH 凭据服务写入),\n * 因此不会落进覆盖层文件。\n *\n * `baseDir`(工作区根目录 = 模型/工具产生文件的落点)可覆盖,但面板只接受\n * **绝对路径**(`~` 会展开);已有 Session / 工作区不迁移,只影响之后新建的\n * 会话。校验与保存时建目录见 `src/workspaces.ts`。\n */\nexport const EDITABLE_KEYS = [\n // 工作区\n 'baseDir',\n 'workspaceRetention',\n 'workspaceRetentionAuto',\n // 行为\n 'dmMode',\n 'requireMention',\n 'threadFallback',\n 'welcomeOnboarding',\n 'allowPermissionSwitch',\n // 准入与授权(管理员/准入模式/申请有效期;白名单由「访问与授权」卡片管理)\n 'adminOpenId',\n 'accessMode',\n 'authRequestTtlDays',\n // 内容\n 'acceptAttachments',\n // 模型\n 'provider',\n 'model',\n 'reasoningEffort',\n 'agentPreset',\n // 流式卡片\n 'refreshMs',\n 'statusRefreshMs',\n 'loggerLevel',\n] as const\n\nexport type EditableKey = (typeof EDITABLE_KEYS)[number]\n\n/**\n * 只有重连飞书长连接后才生效的字段:它们在 `FeishuService.start()` 建连时\n * 被交给官方 SDK 或流式器,运行中修改不会影响已建立的连接。\n */\nexport const RESTART_KEYS = [\n 'dmMode',\n 'requireMention',\n 'refreshMs',\n 'statusRefreshMs',\n 'loggerLevel',\n] as const satisfies readonly EditableKey[]\n\nexport type RestartKey = (typeof RESTART_KEYS)[number]\n\n/** Render kinds the generic panel form understands. */\nexport type FieldKind = 'boolean' | 'number' | 'text' | 'select'\n\n/**\n * Field groups partition the editable form. Each group is rendered inside one\n * panel tab (see {@link TAB_FIELD_GROUPS}); the Host is the source of truth for\n * which key belongs to which group.\n */\nexport type FieldGroup = 'workspace' | 'behavior' | 'content' | 'model' | 'streaming' | 'access'\n\n/** One selectable value rendered by the generic form. */\nexport interface FieldOptionWire {\n value: string\n label: string\n}\n\n/** One editable row, fully described by the Host so the client stays schema-free. */\nexport interface EditableFieldWire {\n key: EditableKey\n label: string\n kind: FieldKind\n group: FieldGroup\n help?: string\n options?: readonly FieldOptionWire[]\n /** Selector is not editable yet (e.g. pick the provider before the model). */\n disabled?: boolean\n min?: number\n max?: number\n step?: number\n placeholder?: string\n /** Render across the whole form width (long paths). */\n wide?: boolean\n /**\n * `directory`:给这个 text 字段加一个「选择目录…」按钮,点击后由 Host\n * 打开宿主机系统目录选择框并把绝对路径写回草稿。仅当宿主提供 native\n * 目录选择器时下发;缺省(含 browse 后端)保持纯手写输入。\n */\n browse?: 'directory'\n /** 保存后需重连飞书通道才生效。 */\n restartRequired: boolean\n}\n\n/**\n * One selector group resolved from live host services. `selectable: false`\n * means the host could not enumerate values (service absent / lookup failed),\n * so the field falls back to the free-text control with the same key.\n */\nexport interface SelectCatalogWire {\n selectable: boolean\n /** Rendered in order; the leading `''` entry is the inherit/clear choice. */\n options: FieldOptionWire[]\n disabled?: boolean\n /** Host-side explanation shown instead of the static field help. */\n help?: string\n}\n\n/** `GET /dsh-channels/catalog` payload: the model-facing selector catalog. */\nexport interface CatalogWire {\n provider: SelectCatalogWire\n model: SelectCatalogWire\n reasoningEffort: SelectCatalogWire\n agentPreset: SelectCatalogWire\n /** Deployment default selection, when the host exposes `agentDefaultModel`. */\n deploymentDefault?: {\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n /** Roster default preset id, when the host exposes the preset roster. */\n defaultPresetId?: string\n}\n\n/** Values the panel may submit. Empty strings for optional text fields clear the override. */\nexport type ConfigValue = string | number | boolean\nexport type EditableValues = Partial<Record<EditableKey, ConfigValue>>\n\n/** Read-only facts shown in the advanced card. */\nexport interface ReadOnlyFieldWire {\n key: string\n label: string\n value: string\n help?: string\n}\n\n/** One credential reference plus its Host-side state (values never cross the wire). */\nexport interface CredentialRefWire {\n ref: string\n configured: boolean\n /** false when the mounted credentials provider cannot store values. */\n writable: boolean\n /** Where the configured value comes from (e.g. `file`), when the provider reports it. */\n source?: string\n}\n\n/** The two credential slots the panel may write. */\nexport type CredentialField = 'appId' | 'appSecret'\n\n/** Official-SDK WebSocket lifecycle snapshot (structural copy, no SDK import on the wire). */\nexport interface ConnectionWire {\n state: string\n lastConnectTime?: number\n nextConnectTime?: number\n reconnectAttempts: number\n}\n\n/**\n * The panel-facing lifecycle phase, derived by the Host from configuration\n * readiness and runtime state. Enabling the plugin starts in `unconfigured`\n * (or `idle` when credentials already resolve) and never forces a connection\n * before the Feishu application credentials are complete.\n */\nexport type ChannelPhaseWire =\n /** Credentials are incomplete: the plugin is loaded but deliberately not connected. */\n | 'unconfigured'\n /** Credentials are complete, no connection is open yet. */\n | 'idle'\n /** A connection attempt / reconnect is in flight. */\n | 'connecting'\n /** The Feishu long connection is open. */\n | 'connected'\n /** The last connection attempt failed; the reason is in `lastStartError`. */\n | 'error'\n\n/** Runtime connection/liveness facts. */\nexport interface ChannelStatusWire {\n running: boolean\n /**\n * Whether the Feishu application credentials are complete. When false the\n * plugin stays loaded (settings panel usable) but opens no long connection.\n */\n configured: boolean\n /** Display phase derived from `configured` / `running` / `lastStartError`. */\n phase: ChannelPhaseWire\n /** Human-readable config items that are still missing (names only, never values). */\n missingConfig: string[]\n level: 'healthy' | 'degraded' | 'unhealthy'\n startedAt?: number\n uptimeMs?: number\n connection?: ConnectionWire\n botOpenIdTail?: string\n routeCount: number\n liveAgentCount: number\n inflightTurnCount: number\n /** 最近一次连接失败的原因(成功后清空)。 */\n lastStartError?: string\n /** 健康判定的原因说明(空数组 = 健康);由 Host 的 health 折叠给出。 */\n reasons: string[]\n /** 面板显示的所有键值对来源版本。 */\n version: string\n /** Web 覆盖文件绝对路径(用户可直接编辑)。 */\n overlayPath: string\n /** 正在连接或重连时为 true,此时面板的写操作按钮禁用。 */\n busy: boolean\n}\n\n/** The whole editable configuration surface, with provenance. */\nexport interface PanelConfigWire {\n /** profile `cordis.patch.yml` 提供的值(覆盖层之下)。 */\n base: Record<string, ConfigValue | undefined>\n /** 合并后的生效值。 */\n effective: Record<string, ConfigValue | undefined>\n /** 已写入 Web 覆盖层的值。 */\n overlay: EditableValues\n overriddenKeys: EditableKey[]\n /** 与当前运行中连接不一致、需重连才生效的字段。 */\n restartPendingKeys: EditableKey[]\n fields: EditableFieldWire[]\n readOnly: ReadOnlyFieldWire[]\n credentials: {\n appId: CredentialRefWire\n appSecret: CredentialRefWire\n }\n}\n\n/** One authorized group (the first of the two admission gates). */\nexport interface AccessGroupWire {\n chatId: string\n /** Human-readable group name captured at authorization time. */\n name?: string\n /** Which authorization entry produced the grant. */\n source: 'command' | 'card' | 'panel'\n /** Epoch ms when it was authorized. */\n addedAt: number\n}\n\n/** One authorized user `open_id` (global across every authorized group). */\nexport interface AccessUserWire {\n openId: string\n /** Display name captured at authorization time. */\n name?: string\n /** Which authorization entry produced the grant. */\n source: 'command' | 'card' | 'panel'\n /** Epoch ms when it was authorized. */\n addedAt: number\n}\n\n/** One pending authorization request the panel may settle (same path as the DM card). */\nexport interface AccessRequestWire {\n /** Opaque token; never derived from the target id. */\n token: string\n kind: 'group' | 'user'\n /** `chat_id` for a group request, `open_id` for a user request. */\n targetId: string\n /** Display name of the requested target (group name / user name), when known. */\n name?: string\n /** Chat the request originated from, when applicable. */\n chatId?: string\n /** Group name captured for the card. */\n groupName?: string\n /** Open_id of the user who triggered a user request. */\n requester?: string\n /** Epoch ms when the request was created. */\n createdAt: number\n /** Epoch ms after which the request can no longer be decided. */\n expiresAt: number\n}\n\n/**\n * `GET /dsh-channels/access` payload: admission mode, the administrator\n * reference, both whitelists, and the still-pending authorization requests.\n */\nexport interface AccessWire {\n mode: 'open' | 'guarded'\n admin: {\n openId?: string\n /** false while guarded mode would refuse every conversation (fail closed). */\n configured: boolean\n }\n /**\n * Whether the persisted whitelists were loaded. While the channel holds no\n * connection the plugin storage domain is closed, so the lists are unknown\n * (not empty) and the panel must not offer mutations.\n */\n loaded: boolean\n groups: AccessGroupWire[]\n users: AccessUserWire[]\n requests: AccessRequestWire[]\n}\n\n/** Write responses for every `/dsh-channels/access*` mutation. */\nexport type AccessMutationResponse =\n | { ok: true; access: AccessWire }\n | { ok: false; error: string }\n\n/** `GET /dsh-channels/panel` payload. */\nexport interface PanelWire {\n status: ChannelStatusWire\n config: PanelConfigWire\n cleanup: CleanupPreviewWire\n /** 访问与授权卡片的数据(准入模式、管理员、白名单、待处理申请)。 */\n access: AccessWire\n}\n\n/**\n * What the workspace-retention cleanup would delete right now. Counts are\n * zero and `error` is set when the Host cannot compute the preview (for\n * example the cleanup seam is unavailable); the panel stays usable.\n */\nexport interface CleanupPreviewWire {\n /** Effective `workspaceRetention` value (`off` / `7d` / `30d` / `90d` / `180d`). */\n retention: string\n /** Retention threshold in milliseconds; absent when `retention` is `off`. */\n thresholdMs?: number\n /** Periodic cleanup runs at startup and once per day. */\n auto: boolean\n /** Plugin-created workspaces under the current baseDir. */\n workspaces: number\n /** Workspaces older than the threshold (the deletable set). */\n candidates: number\n /** Total bytes currently occupied by the candidates. */\n candidateBytes: number\n /** Why the preview could not be computed (counts are zero then). */\n error?: string\n}\n\n/** Outcome of one manual cleanup run (partial failures do not abort the run). */\nexport interface CleanupRunWire {\n ok: boolean\n deletedWorkspaces: number\n retiredRoutes: number\n freedBytes: number\n /** One `path: message` line per workspace that could not be deleted. */\n failures: string[]\n error?: string\n}\n\n/** `POST /dsh-channels/cleanup` response. */\nexport type CleanupResponse =\n | { ok: true; panel: PanelWire; result: CleanupRunWire }\n | { ok: false; error: string }\n\n/**\n * `POST /dsh-channels/pick-directory` response. `canceled` is a normal\n * outcome (the operator dismissed the OS dialog), not an error; `path` is an\n * absolute directory the Host's native chooser returned.\n */\nexport type PickDirectoryResponse =\n | { ok: true; path: string }\n | { ok: true; canceled: true }\n | { ok: false; error: string }\n\n/** `PUT /dsh-channels/config` body. */\nexport interface SaveConfigRequest {\n values: EditableValues\n}\n\n/** Per-field validation failure. */\nexport interface FieldIssueWire {\n key: string\n message: string\n}\n\n/** `PUT /dsh-channels/config` response. */\nexport type SaveConfigResponse =\n | { ok: true; panel: PanelWire; clearedKeys: EditableKey[] }\n | { ok: false; error: string; issues?: FieldIssueWire[] }\n\n/** `POST /dsh-channels/reconnect` response. */\nexport type ReconnectResponse =\n | { ok: true; panel: PanelWire }\n /** `unconfigured` tells the panel to route the user to the credentials form. */\n | { ok: false; error: string; unconfigured?: boolean }\n\n/** `DELETE /dsh-channels/config` response. */\nexport type ClearConfigResponse =\n | { ok: true; panel: PanelWire }\n | { ok: false; error: string }\n\n/** `PUT /dsh-channels/credentials` body: only provided, non-empty values are written. */\nexport interface SaveCredentialsRequest {\n appId?: string\n appSecret?: string\n}\n\n/** `DELETE /dsh-channels/credentials` body. */\nexport interface ClearCredentialRequest {\n field: CredentialField\n}\n\n/**\n * How a saved credential reached the running channel.\n *\n * - `auto`: the channel was stopped and connected immediately after the save.\n * - `pending`: the channel is running; reconnect for the value to take effect.\n * - `not-needed`: removed while stopped, nothing to reconnect.\n * - `unconfigured`: saved, but the other credential of the pair is still\n * missing — the plugin keeps waiting (no connection attempt) until it is\n * configured.\n * - `failed`: the pair is complete and a connection was attempted, but it\n * failed; the value is stored and `autoReconnectError` explains why.\n */\nexport type CredentialReconnectState = 'auto' | 'pending' | 'not-needed' | 'unconfigured' | 'failed'\n\nexport interface CredentialsSavedPayload {\n panel: PanelWire\n reconnect: CredentialReconnectState\n /** Set when the automatic reconnect after a save failed (the value is still saved). */\n autoReconnectError?: string\n}\n\n/** Credential write/remove responses (values never appear in either direction). */\nexport type SaveCredentialsResponse =\n | ({ ok: true } & CredentialsSavedPayload)\n | { ok: false; error: string; issues?: FieldIssueWire[] }\n\nexport type ClearCredentialResponse =\n | ({ ok: true } & CredentialsSavedPayload)\n | { ok: false; error: string; issues?: FieldIssueWire[] }\n\n/**\n * Top-level panel tabs, in render order. The panel shows the connection state\n * header above every tab, so switching tabs never hides whether the bridge is\n * connected.\n */\nexport const PANEL_TABS = [\n { id: 'connection', label: '连接' },\n { id: 'sessions', label: '会话与行为' },\n { id: 'model', label: '模型' },\n { id: 'workspace', label: '工作区' },\n { id: 'access', label: '访问授权' },\n { id: 'advanced', label: '高级' },\n] as const\n\nexport type PanelTabId = (typeof PANEL_TABS)[number]['id']\n\n/** Which editable field groups each tab renders (the tabs own one or two). */\nexport const TAB_FIELD_GROUPS: Readonly<Record<PanelTabId, readonly FieldGroup[]>> = {\n connection: [],\n sessions: ['behavior', 'content'],\n model: ['model'],\n workspace: ['workspace'],\n access: ['access'],\n advanced: ['streaming'],\n}\n\n/** Section heading for one field group inside its tab. */\nexport const FIELD_GROUP_LABELS: Readonly<Record<FieldGroup, string>> = {\n workspace: '工作区与文件',\n behavior: '行为',\n content: '内容与附件',\n model: '模型与推理',\n streaming: '流式卡片与日志',\n access: '准入与授权',\n}\n\n/** Display labels for the panel header/cards (kept on the wire so the client owns no copy). */\nexport const PANEL_COPY = {\n title: '飞书渠道',\n description: '飞书长连接、应用凭据与渠道配置。凭据只写入 DSH 凭据库且不回显;标注「需重连」的项在连接建立时读取。',\n // ---- lifecycle / status -------------------------------------------------\n statusCard: '连接状态',\n phaseUnconfigured: '待配置',\n phaseIdle: '未连接',\n phaseConnecting: '连接中',\n phaseConnected: '已连接',\n phaseError: '连接失败',\n configuredBadge: '凭据已配置',\n unconfiguredBadge: '凭据未配置',\n statusSummaryConnected: '飞书长连接已建立,机器人可以收发消息。',\n statusSummaryUnconfigured: '尚未配置飞书应用凭据:插件已加载,但不会连接飞书。填写凭据并保存后会自动连接。',\n statusSummaryIdle: '飞书应用凭据已就绪,但当前没有建立长连接。',\n statusSummaryConnecting: '正在连接飞书长连接…',\n statusSummaryError: '最近一次连接飞书失败,请检查凭据与网络后重试。',\n lastError: '最近一次连接失败',\n missingConfig: '缺少配置',\n statusHintUnconfigured: '配置完成后插件会自动连接;也可以稍后在本页手动连接。',\n connectionInfo: '连接信息',\n connectionStateLabel: '飞书长连接',\n botOpenId: 'Bot open_id',\n uptime: '运行时长',\n routeCount: '话题路由',\n liveAgentCount: '活动会话',\n inflightTurnCount: '在途回复',\n healthReasons: '健康判定',\n unknownValue: '—',\n connect: '连接飞书',\n connecting: '连接中…',\n // ---- onboarding ---------------------------------------------------------\n onboardingTitle: '开始使用飞书渠道',\n onboardingIntro: '只需三步:在飞书开放平台创建自建应用,把 App ID / App Secret 填入本页,保存后自动连接。',\n onboardingStep1Title: '创建飞书自建应用',\n onboardingStep1Text: '在飞书开放平台创建企业自建应用,开启机器人能力;事件订阅选择「长连接」,订阅 im.message.receive_v1(接收消息)。',\n onboardingStep2Title: '填写应用凭据',\n onboardingStep2Text: '把应用的 App ID(cli_ 开头)与 App Secret 填入下方「飞书应用凭据」,值会写入 DSH 凭据库。',\n onboardingStep3Title: '保存并等待连接',\n onboardingStep3Text: '两项凭据都保存后插件会自动连接;状态变为「已连接」即可在飞书里私聊机器人。',\n onboardingDocsHint: '开放平台地址:open.feishu.cn/app;应用需要开通 im:message、im:message:send_as_bot 等消息权限。',\n // ---- credentials --------------------------------------------------------\n credentialsCard: '飞书应用凭据',\n credentialsCardHelp: '凭据值经 DSH 凭据服务写入本机凭据库;面板只显示「已配置 / 未配置」,永不回显已存值。两项都配置后才会连接飞书。',\n appIdLabel: '飞书 App ID',\n appSecretLabel: '飞书 App Secret',\n credentialsRef: '凭据引用',\n saveCredentials: '保存凭据',\n savingCredentials: '保存中…',\n credentialSaved: '凭据已保存',\n credentialSavedReconnected: '凭据已保存,飞书已连接',\n credentialSavedPending: '凭据已保存,重启飞书连接后生效',\n credentialSavedUnconfigured: '凭据已保存;还缺少另一项凭据,补齐后会自动连接',\n credentialAutoConnectFailed: '凭据已保存,但自动连接失败',\n credentialRemoved: '已移除存储的凭据',\n credentialRemovedPending: '已移除存储的凭据,重启飞书连接后生效',\n removeCredential: '移除已存值',\n removingCredential: '移除中…',\n credentialConfigured: '已配置',\n credentialMissing: '未配置',\n credentialReadOnly: '当前凭据存储不可写',\n appIdPlaceholder: '输入 App ID(cli_…)',\n appSecretPlaceholder: '输入 App Secret',\n credentialsPendingHint: '填写后点「保存凭据」;两项齐全时插件会自动连接飞书。',\n // ---- tabs / forms -------------------------------------------------------\n connectionTabHelp: '连接状态与飞书应用凭据。凭据不完整时不会连接飞书。',\n sessionsTabHelp: '私聊、群聊触发方式与入站内容;「需重连」项在下次连接时读取。',\n modelTabHelp: '飞书会话使用的模型服务商、模型、推理档位与 Agent 预设;下拉选项来自宿主机实时目录,保存后对新会话生效。',\n workspaceTabHelp: '每个飞书话题一个独立工作区(模型/工具产生文件的落点);只影响之后新建的话题。',\n accessTabHelp: '准入采用 fail-closed:管理员不受白名单限制,非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。',\n advancedTabHelp: '流式刷新等低层参数、Web 覆盖文件与只读信息。',\n save: '保存更改',\n saving: '保存中…',\n saveHintDirty: '只保存改动过的字段;标注「需重连」的项在下次连接时生效。',\n discardTab: '放弃本页更改',\n saveHintClean: '没有未保存的更改。',\n dirtyCount: '未保存项',\n browseDirectory: '选择目录…',\n browsingDirectory: '选择中…',\n directoryPicked: '已选择目录',\n directoryPickFailed: '无法打开系统目录选择器',\n saved: '已保存',\n cleared: '已清除 Web 覆盖,恢复为 profile 配置',\n clear: '清除 Web 覆盖',\n clearing: '清除中…',\n noOverrides: '当前没有 Web 覆盖',\n overridden: 'Web 覆盖',\n inherited: '继承 profile',\n restartTag: '需重连',\n restartBanner: '以下配置需重启飞书连接后生效:',\n restartWarning: '重启会断开当前飞书长连接并中断进行中的回复,请确认空闲时操作。',\n reconnect: '重启飞书连接',\n reconnecting: '重连中…',\n refresh: '刷新状态',\n retry: '重试',\n pollFailed: '状态刷新失败',\n connectDone: '已连接飞书',\n reconnectDone: '已重启飞书连接',\n saveInvalid: '请先修正标红的配置项',\n enabled: '已启用',\n disabled: '已关闭',\n credentialRemoveConfirm: '确定移除已存储的凭据值?移除后不会回显,需要时须重新填写。',\n accessNotLoaded: '飞书连接建立后加载授权列表',\n loading: '加载中…',\n loadFailed: '设置面板加载失败',\n catalogFailed: '模型目录加载失败,已退回手填。',\n catalogLoading: '正在读取宿主机选项…',\n catalogUnavailable: '宿主机未提供可选项,请手填。',\n version: '插件版本',\n overlayPath: '覆盖文件',\n overlayPathHelp: '可以直接编辑该 JSON 文件;面板保存会整体重写它,「清除 Web 覆盖」会删除它并回落到 profile 配置。',\n advancedCard: '只读与高级信息',\n advancedHelp: '这些值来自 profile 配置,不能在面板里编辑。',\n readOnlyCard: '部署级参数',\n // ---- model --------------------------------------------------------------\n // ---- cleanup ------------------------------------------------------------\n cleanupCard: '工作区清理',\n cleanupCardHelp: '删除超过保留期且不再活跃的飞书工作区目录;只会删除本插件在「工作区根目录」下创建的目录,DSH 会话日志不删除。删除不可恢复:相关旧话题会失效并提示已过期,工作区列表记录同步移除。',\n cleanupRetention: '保留期',\n cleanupAutoLabel: '自动清理',\n cleanupCandidates: '可清理工作区',\n cleanupCandidateBytes: '预计释放空间',\n cleanupRetentionOff: '关闭(不删除)',\n cleanupAutoOn: '已开启(启动时 + 每天一次)',\n cleanupAutoOff: '已关闭(仅手动执行)',\n cleanupAutoInactive: '未执行(保留期为关闭)',\n cleanupNow: '立即清理',\n cleanupRunning: '清理中…',\n cleanupConfirm: '确定清理过期工作区吗?该操作不可恢复,会删除这些工作区内的全部文件(包括 attachments/ 附件)。',\n cleanupConfirmHint: '删除前会再次确认;保留期为「关闭」或没有过期工作区时按钮不可用。',\n cleanupDisabledOff: '保留策略为「关闭」:请先在上方「过期工作区保留期」中选择保留天数。',\n cleanupDisabledEmpty: '没有超过保留期的过期工作区。',\n cleanupPreviewFailed: '无法读取清理预览',\n cleanupDone: '清理完成',\n cleanupFailed: '清理失败',\n cleanupDeleted: '已删除工作区',\n cleanupRetired: '已清理话题路由',\n cleanupFreed: '释放空间',\n cleanupFailures: '以下工作区未能删除:',\n // ---- access -------------------------------------------------------------\n accessCard: '访问与授权',\n accessCardHelp: '准入采用 fail-closed:管理员不受白名单限制(私聊、任意群聊都可直接对话);非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。管理员与准入模式在上方「准入与授权」中编辑;私聊机器人发送 /userid 获取自己的 open_id。',\n accessModeOpenWarning: '当前准入模式为开放(open):应用可用范围内的任何人都能开启对话。该模式不安全,仅建议调试时使用。',\n accessAdminMissingWarning: '当前准入=guarded 但未配置管理员,所有对话都会被拒绝。请在上方「准入与授权」填写管理员 open_id。',\n accessGroupsTitle: '已授权群',\n accessGroupsEmpty: '尚未授权任何群。机器人被拉进群后,管理员会收到带 chat_id 的群信息卡;也可在群内发送 /groupid 获取。',\n accessUsersTitle: '已授权用户',\n accessUsersEmpty: '尚未授权任何用户。已授权用户可在所有已授权群里与机器人对话(私聊仍只开给管理员)。',\n accessRequestsTitle: '待处理申请',\n accessRequestsEmpty: '没有待处理的授权申请。',\n accessGroupChatIdPlaceholder: '群 chat_id(oc_…)',\n accessGroupNamePlaceholder: '群名(可选)',\n accessUserOpenIdPlaceholder: '用户 open_id(ou_…)',\n accessUserNamePlaceholder: '昵称(可选)',\n accessAddGroup: '添加群',\n accessAddUser: '添加用户',\n accessRemove: '移除',\n accessAllow: '授权',\n accessDeny: '忽略',\n accessWorking: '处理中…',\n accessSourceCommand: '对话命令',\n accessSourceCard: '授权卡',\n accessSourcePanel: '设置面板',\n accessRequestGroup: '群申请',\n accessRequestUser: '用户申请',\n accessRequestRequester: '申请人',\n accessRequestExpires: '有效期至',\n accessGroupAdded: '已添加群授权',\n accessGroupRemoved: '已移除群授权',\n accessUserAdded: '已添加用户授权',\n accessUserRemoved: '已移除用户授权',\n accessRequestAllowed: '已授权',\n accessRequestDenied: '已忽略',\n accessHints: '三种授权入口:群内 /auth 命令、入群与用户授权卡、本面板。撤销立即生效;白名单只约束非管理员(管理员在任何私聊/群聊都直接放行),非管理员需要群和用户两层同时命中。',\n accessChatIdHint: 'chat_id 可从入群群信息卡或群内 /groupid 获取;open_id 可私聊机器人发送 /userid 获取。',\n} as const\n","/**\n * Same-origin HTTP client for the settings panel. No typert contract: the\n * Host half registers plain exact routes and this module fetches them.\n *\n * @module dsh-channels/client/api\n */\n\nimport {\n ACCESS_GROUPS_ROUTE,\n ACCESS_REQUESTS_ROUTE,\n ACCESS_ROUTE,\n ACCESS_USERS_ROUTE,\n CATALOG_ROUTE,\n CLEANUP_ROUTE,\n CONFIG_ROUTE,\n CREDENTIALS_ROUTE,\n PANEL_ROUTE,\n PICK_DIRECTORY_ROUTE,\n RECONNECT_ROUTE,\n STATUS_ROUTE,\n} from '../settings/contract.ts'\nimport type {\n AccessMutationResponse,\n AccessWire,\n CatalogWire,\n ChannelStatusWire,\n CleanupResponse,\n ClearConfigResponse,\n ClearCredentialResponse,\n CredentialField,\n EditableValues,\n PanelWire,\n PickDirectoryResponse,\n ReconnectResponse,\n SaveConfigResponse,\n SaveCredentialsRequest,\n SaveCredentialsResponse,\n} from '../settings/contract.ts'\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Pull the JSON `error` field out of a failed response body. */\nfunction errorFromBody(status: number, text: string): string {\n try {\n const parsed = JSON.parse(text) as { error?: unknown }\n if (typeof parsed.error === 'string' && parsed.error !== '') return parsed.error\n } catch {\n // Not JSON — fall through to the HTTP status.\n }\n return `HTTP ${status}`\n}\n\nasync function request(path: string, init?: RequestInit): Promise<Response> {\n try {\n return await fetch(path, {\n cache: 'no-store',\n credentials: 'same-origin',\n ...init,\n headers: { accept: 'application/json', ...(init?.headers ?? {}) },\n })\n } catch (error) {\n throw new Error(`无法连接设置接口:${messageOf(error)}`)\n }\n}\n\nasync function readJson<T>(response: Response): Promise<T> {\n const text = await response.text()\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as T\n}\n\n/** Full panel payload (status + editable config + read-only facts). */\nexport async function fetchPanel(): Promise<PanelWire> {\n return readJson<PanelWire>(await request(PANEL_ROUTE))\n}\n\n/** Status-only payload polled while the page is open. */\nexport async function fetchStatus(): Promise<ChannelStatusWire> {\n return readJson<ChannelStatusWire>(await request(STATUS_ROUTE))\n}\n\n/**\n * Model-selector catalog for one route: providers, the provider's models, the\n * model's reasoning efforts, and the preset roster. The client re-queries it\n * whenever the draft provider/model changes.\n */\nexport async function fetchCatalog(provider?: string, model?: string): Promise<CatalogWire> {\n const params = new URLSearchParams()\n if (provider !== undefined && provider !== '') params.set('provider', provider)\n if (model !== undefined && model !== '') params.set('model', model)\n const query = params.toString()\n return readJson<CatalogWire>(await request(query === '' ? CATALOG_ROUTE : `${CATALOG_ROUTE}?${query}`))\n}\n\n/** Persist a partial edit. Validation failures come back as `{ok:false}`. */\nexport async function saveConfig(values: EditableValues): Promise<SaveConfigResponse> {\n const response = await request(CONFIG_ROUTE, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ values }),\n })\n const text = await response.text()\n if (response.status === 400) {\n // Validation/IO refusal: the body is already the SaveConfigResponse.\n try {\n return JSON.parse(text) as SaveConfigResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as SaveConfigResponse\n}\n\n/** Drop every Web override and return the refreshed panel. */\nexport async function clearConfig(): Promise<ClearConfigResponse> {\n const response = await request(CONFIG_ROUTE, { method: 'DELETE' })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as ClearConfigResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as ClearConfigResponse\n}\n\n/** Reopen the Feishu long connection (interrupts in-flight replies). */\nexport async function reconnect(): Promise<ReconnectResponse> {\n const response = await request(RECONNECT_ROUTE, { method: 'POST' })\n const text = await response.text()\n if (!response.ok) {\n try {\n return JSON.parse(text) as ReconnectResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n return JSON.parse(text) as ReconnectResponse\n}\n\n/**\n * Run one workspace cleanup. No request body: the Host derives the candidate\n * set from the effective retention config. Irreversible, so the caller asks\n * for confirmation first; partial failures come back inside `result.failures`.\n */\nexport async function runCleanup(): Promise<CleanupResponse> {\n const response = await request(CLEANUP_ROUTE, { method: 'POST' })\n const text = await response.text()\n if (!response.ok) {\n try {\n return JSON.parse(text) as CleanupResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n return JSON.parse(text) as CleanupResponse\n}\n\n/**\n * Ask the Host to open its native directory chooser (the panel's「选择目录…」\n * button). The request stays open while the OS dialog does; a cancel comes back\n * as `{ ok: true, canceled: true }`, not as an error.\n */\nexport async function pickDirectory(): Promise<PickDirectoryResponse> {\n const response = await request(PICK_DIRECTORY_ROUTE, { method: 'POST' })\n const text = await response.text()\n if (!response.ok) {\n try {\n return JSON.parse(text) as PickDirectoryResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n return JSON.parse(text) as PickDirectoryResponse\n}\n\n/** Store provided credential values (the request/response never carry stored values). */\nexport async function saveCredentials(values: SaveCredentialsRequest): Promise<SaveCredentialsResponse> {\n const response = await request(CREDENTIALS_ROUTE, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(values),\n })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as SaveCredentialsResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as SaveCredentialsResponse\n}\n\n/** Remove the stored value behind one credential reference. */\nexport async function clearCredential(field: CredentialField): Promise<ClearCredentialResponse> {\n const response = await request(CREDENTIALS_ROUTE, {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ field }),\n })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as ClearCredentialResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as ClearCredentialResponse\n}\n\n/** Admission snapshot: mode, administrator, whitelists, pending requests. */\nexport async function fetchAccess(): Promise<AccessWire> {\n return readJson<AccessWire>(await request(ACCESS_ROUTE))\n}\n\n/** One access write: a 400 body already is the wire union; others throw. */\nasync function mutateAccess(\n path: string,\n method: 'POST' | 'DELETE',\n body: unknown,\n): Promise<AccessMutationResponse> {\n const response = await request(path, {\n method,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as AccessMutationResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as AccessMutationResponse\n}\n\n/** Authorize one group; resolves with the refreshed access snapshot. */\nexport async function addAccessGroup(chatId: string, name?: string): Promise<AccessMutationResponse> {\n return mutateAccess(\n ACCESS_GROUPS_ROUTE,\n 'POST',\n name === undefined || name === '' ? { chatId } : { chatId, name },\n )\n}\n\n/** Revoke one group authorization (takes effect on the next message). */\nexport async function removeAccessGroup(chatId: string): Promise<AccessMutationResponse> {\n return mutateAccess(ACCESS_GROUPS_ROUTE, 'DELETE', { chatId })\n}\n\n/** Authorize one user open_id (global across authorized groups). */\nexport async function addAccessUser(openId: string, name?: string): Promise<AccessMutationResponse> {\n return mutateAccess(\n ACCESS_USERS_ROUTE,\n 'POST',\n name === undefined || name === '' ? { openId } : { openId, name },\n )\n}\n\n/** Revoke one user authorization (takes effect on the next message). */\nexport async function removeAccessUser(openId: string): Promise<AccessMutationResponse> {\n return mutateAccess(ACCESS_USERS_ROUTE, 'DELETE', { openId })\n}\n\n/** Settle one pending authorization request (same resolver as the Feishu card). */\nexport async function resolveAccessRequest(\n token: string,\n decision: 'allow' | 'deny',\n): Promise<AccessMutationResponse> {\n return mutateAccess(ACCESS_REQUESTS_ROUTE, 'POST', { token, decision })\n}\n","/**\n * 浏览器半区的设置面板选择器目录折叠:把 Host 下发的字段描述与实时目录\n * (`/dsh-channels/catalog`)合成通用表单需要的 select 描述。\n *\n * 拆成独立模块(不含 JSX/React)以便单测;`panel/index.tsx` 只负责串起状态与渲染。\n *\n * @module dsh-channels/client/catalog\n */\n\nimport { PANEL_COPY } from '../settings/contract.ts'\nimport type {\n CatalogWire,\n EditableFieldWire,\n EditableKey,\n FieldOptionWire,\n SelectCatalogWire,\n} from '../settings/contract.ts'\n\n/** Keys whose options come from the Host selector catalog. */\nexport const CATALOG_KEYS = ['provider', 'model', 'reasoningEffort', 'agentPreset'] as const\nexport type CatalogKey = (typeof CATALOG_KEYS)[number]\n\nexport function isCatalogKey(key: EditableKey): key is CatalogKey {\n return (CATALOG_KEYS as readonly string[]).includes(key)\n}\n\n/** Read one selector group back out of the Host-resolved panel fields. */\nexport function selectorOf(field: EditableFieldWire | undefined): SelectCatalogWire {\n if (field?.kind !== 'select') return { selectable: false, options: [] }\n return {\n selectable: true,\n options: field.options === undefined ? [] : [...field.options],\n ...(field.disabled === true ? { disabled: true } : {}),\n ...(field.help === undefined ? {} : { help: field.help }),\n }\n}\n\n/** Seed the client catalog from the options the Host already resolved for the panel. */\nexport function catalogFromFields(fields: readonly EditableFieldWire[]): CatalogWire | null {\n const byKey = new Map(fields.map(field => [field.key, field]))\n const catalog: CatalogWire = {\n provider: selectorOf(byKey.get('provider')),\n model: selectorOf(byKey.get('model')),\n reasoningEffort: selectorOf(byKey.get('reasoningEffort')),\n agentPreset: selectorOf(byKey.get('agentPreset')),\n }\n return CATALOG_KEYS.every(key => !catalog[key].selectable) ? null : catalog\n}\n\n/** Current-value option kept when a saved value is missing from the live list. */\nfunction withCurrentOption(options: readonly FieldOptionWire[], value: string): FieldOptionWire[] {\n const [inherit, ...rest] = options\n const extra: FieldOptionWire = { value, label: `${value}(当前配置,不在宿主机列表中)` }\n return inherit === undefined ? [extra] : [inherit, extra, ...rest]\n}\n\n/**\n * Layer the live catalog over the Host panel fields. A group the Host cannot\n * enumerate falls back to the free-text control; the rest render as selects.\n *\n * `disabled` must be cleared explicitly: the Host's initial panel descriptor\n * carries `disabled: true` for dependent selectors (model without a provider,\n * effort without a model), and a fetched catalog that resolved the dependency\n * has to re-enable them. Forgetting this is exactly what made the model and\n * reasoning selects permanently unusable after choosing a provider/model.\n */\nexport function applyCatalogFields(\n fields: readonly EditableFieldWire[],\n catalog: CatalogWire | null,\n current: Partial<Record<CatalogKey, string | undefined>> = {},\n): EditableFieldWire[] {\n if (catalog === null) return [...fields]\n return fields.map((field) => {\n if (!isCatalogKey(field.key)) return field\n const selector = catalog[field.key]\n if (!selector.selectable) {\n return field.kind === 'select'\n ? { ...field, kind: 'text', options: undefined, help: PANEL_COPY.catalogUnavailable, disabled: undefined }\n : field\n }\n const value = current[field.key] ?? ''\n const options = value !== '' && !selector.options.some(option => option.value === value)\n ? withCurrentOption(selector.options, value)\n : [...selector.options]\n const next: EditableFieldWire = { ...field, kind: 'select', options }\n if (selector.disabled === true) next.disabled = true\n else delete next.disabled\n if (selector.help === undefined) delete next.help\n else next.help = selector.help\n return next\n })\n}","/**\n * 设置面板共享层:页面状态类型、Host 字段渲染原语(FieldRow / FieldsCard /\n * SaveBar)、生命周期徽标与纯格式化工具。\n *\n * 这一层不发请求、不持有页面状态,只把 Host 下发的 wire 数据渲染成控件;\n * 页面外壳在 `index.tsx`,各 tab 只组合这些原语。\n *\n * @module dsh-channels/client/panel/shared\n */\n\nimport { useState } from 'react'\nimport { FIELD_GROUP_LABELS, PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type {\n AccessWire,\n ChannelPhaseWire,\n CleanupPreviewWire,\n ConfigValue,\n CredentialField,\n EditableFieldWire,\n EditableKey,\n EditableValues,\n FieldGroup,\n FieldIssueWire,\n PanelTabId,\n PanelWire,\n} from '../../settings/contract.ts'\n\n/** Initial panel load: one request, retried from the error card. */\nexport type LoadState =\n | { phase: 'loading' }\n | { phase: 'error'; message: string }\n | { phase: 'ready'; panel: PanelWire }\n\n/** One transient banner above the tab body (save/credentials/reconnect/catalog). */\nexport interface Notice {\n kind: 'ok' | 'error'\n text: string\n /** Per-field validation failures rendered inline by FieldRow. */\n issues?: FieldIssueWire[]\n}\n\n/** One in-flight panel mutation (only one action runs at a time). */\nexport type BusyAction =\n | 'save'\n | 'clear'\n | 'reconnect'\n | 'credentials'\n | 'credentials-clear'\n | 'cleanup'\n | 'access'\n\n/** Tone of a small badge; maps onto `dsh-channels-cfg__badge--*` classes. */\nexport type BadgeTone = 'ok' | 'warn' | 'bad' | 'muted'\n\nexport function messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Human-readable byte size for the cleanup preview/result (base-2 units). */\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n let value = bytes\n let unit = 0\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit += 1\n }\n const digits = unit === 0 || value >= 10 ? 0 : 1\n return `${value.toFixed(digits)} ${units[unit] ?? 'B'}`\n}\n\n/** Compact duration for the connection card. */\nexport function formatUptime(ms: number | undefined): string {\n if (ms === undefined || !Number.isFinite(ms)) return PANEL_COPY.unknownValue\n const totalSeconds = Math.max(0, Math.floor(ms / 1_000))\n const days = Math.floor(totalSeconds / 86_400)\n const hours = Math.floor((totalSeconds % 86_400) / 3_600)\n const minutes = Math.floor((totalSeconds % 3_600) / 60)\n if (days > 0) return `${days} 天 ${hours} 小时`\n if (hours > 0) return `${hours} 小时 ${minutes} 分`\n if (minutes > 0) return `${minutes} 分 ${totalSeconds % 60} 秒`\n return `${totalSeconds} 秒`\n}\n\n/** Effective value for one key (falling back to the profile base). */\nexport function effectiveValue(panel: PanelWire, key: EditableKey): ConfigValue | undefined {\n return panel.config.effective[key] ?? panel.config.base[key]\n}\n\n/** Host label for one editable key; the raw key is the technical fallback. */\nexport function fieldLabel(fields: readonly EditableFieldWire[], key: EditableKey): string {\n return fields.find(field => field.key === key)?.label ?? key\n}\n\n/** Effective access-mode label, reusing the Host field options for consistency. */\nexport function accessModeLabel(panel: PanelWire, mode: AccessWire['mode']): string {\n const field = panel.config.fields.find(item => item.key === 'accessMode')\n return field?.options?.find(option => option.value === mode)?.label ?? mode\n}\n\n/** Numeric draft kept as the raw input string; parsed on save. */\nfunction numericDraft(field: EditableFieldWire, raw: ConfigValue | undefined): number | undefined {\n if (typeof raw === 'number') return raw\n if (typeof raw !== 'string' || raw.trim() === '') return undefined\n const parsed = Number(raw)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\n/**\n * Turn the dirty draft into the wire payload, keeping the Host-descriptor\n * bounds check client-side. Field messages stay technical (the Host re-validates\n * every value on save).\n */\nexport function validateDraft(\n panel: PanelWire,\n draft: EditableValues,\n): { values: EditableValues; issues: FieldIssueWire[] } {\n const values: EditableValues = {}\n const issues: FieldIssueWire[] = []\n for (const field of panel.config.fields) {\n if (!(field.key in draft)) continue\n const raw = draft[field.key]\n if (field.kind === 'number') {\n const parsed = numericDraft(field, raw)\n if (parsed === undefined) {\n issues.push({ key: field.key, message: '请输入数字' })\n continue\n }\n if (field.min !== undefined && parsed < field.min) {\n issues.push({ key: field.key, message: `不得小于 ${field.min}` })\n continue\n }\n if (field.max !== undefined && parsed > field.max) {\n issues.push({ key: field.key, message: `不得大于 ${field.max}` })\n continue\n }\n values[field.key] = parsed\n continue\n }\n if (field.kind === 'boolean') {\n values[field.key] = raw === true\n continue\n }\n values[field.key] = String(raw ?? '')\n }\n return { values, issues }\n}\n\n/** Header badge for the Host-derived lifecycle phase. */\nexport function phaseBadge(phase: ChannelPhaseWire): { text: string; tone: BadgeTone } {\n switch (phase) {\n case 'connected': return { text: PANEL_COPY.phaseConnected, tone: 'ok' }\n case 'connecting': return { text: PANEL_COPY.phaseConnecting, tone: 'warn' }\n case 'error': return { text: PANEL_COPY.phaseError, tone: 'bad' }\n case 'idle': return { text: PANEL_COPY.phaseIdle, tone: 'muted' }\n default: return { text: PANEL_COPY.phaseUnconfigured, tone: 'muted' }\n }\n}\n\n/** One-line explanation shown under the page title for the current phase. */\nexport function phaseSummary(phase: ChannelPhaseWire): string {\n switch (phase) {\n case 'connected': return PANEL_COPY.statusSummaryConnected\n case 'connecting': return PANEL_COPY.statusSummaryConnecting\n case 'error': return PANEL_COPY.statusSummaryError\n case 'idle': return PANEL_COPY.statusSummaryIdle\n default: return PANEL_COPY.statusSummaryUnconfigured\n }\n}\n\n/** Credential editor data owned by the page shell, rendered by the connection tab. */\nexport interface CredentialsView {\n appId: string\n appSecret: string\n issues: FieldIssueWire[]\n busy: BusyAction | null\n /** Host-side lifecycle busy (connecting/restarting): block writes. */\n locked: boolean\n onAppIdChange: (value: string) => void\n onAppSecretChange: (value: string) => void\n onSave: () => void\n onClear: (field: CredentialField) => void\n}\n\n/** Everything a field-bearing tab needs from the page shell's single draft. */\nexport interface FormView {\n panel: PanelWire\n /** Host descriptors after the live selector catalog has been layered on. */\n fields: readonly EditableFieldWire[]\n draft: EditableValues\n issues: FieldIssueWire[]\n busy: BusyAction | null\n /** Host-side lifecycle busy (connecting/restarting): block writes. */\n locked: boolean\n dirtyCount: number\n onChange: (key: EditableKey, value: ConfigValue) => void\n onSave: () => void\n /** Drop only the draft keys owned by one tab. */\n onDiscardTab: (tab: PanelTabId) => void\n /**\n * Open the Host's native directory chooser and write the picked absolute\n * path into the draft (not saved until「保存更改」). Absent when the Host\n * cannot offer a native chooser.\n */\n onPickDirectory?: (key: EditableKey) => Promise<void>\n}\n\n/**\n * Text input paired with the host-native「选择目录…」button. The button owns\n * its own busy state: the OS dialog can stay open for a long time, so the\n * rest of the form stays usable while the operator browses.\n */\nfunction DirectoryTextInput(props: {\n field: EditableFieldWire\n value: ConfigValue | undefined\n inputClass: string\n disabled: boolean\n onChange: (key: EditableKey, value: ConfigValue) => void\n onPick: () => Promise<void>\n}): JSX.Element {\n const { field, value, inputClass, disabled, onChange, onPick } = props\n const [picking, setPicking] = useState(false)\n const pick = async (): Promise<void> => {\n setPicking(true)\n try {\n await onPick()\n } finally {\n setPicking(false)\n }\n }\n return (\n <div className=\"dsh-channels-cfg__inputRow\">\n <input\n type=\"text\"\n className={inputClass}\n value={String(value ?? '')}\n placeholder={field.placeholder}\n aria-label={field.label}\n disabled={disabled || picking}\n onChange={event => onChange(field.key, event.target.value)}\n />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--browse\"\n disabled={disabled || picking}\n onClick={() => { void pick() }}\n >\n {picking ? PANEL_COPY.browsingDirectory : PANEL_COPY.browseDirectory}\n </button>\n </div>\n )\n}\n\n/** One generic form control driven by the Host field descriptor. */\nexport function FieldRow(props: {\n field: EditableFieldWire\n value: ConfigValue | undefined\n issues: FieldIssueWire[]\n disabled?: boolean\n onChange: (key: EditableKey, value: ConfigValue) => void\n /** Present only for `field.browse === 'directory'` rows the page can serve. */\n onPickDirectory?: () => Promise<void>\n}): JSX.Element {\n const { field, value, issues, disabled = false, onChange, onPickDirectory } = props\n const issue = issues.find(item => item.key === field.key)\n const inputClass = `dsh-channels-cfg__input${issue === undefined ? '' : ' dsh-channels-cfg__input--invalid'}`\n const fieldClass = `dsh-channels-cfg__field${field.wide === true ? ' dsh-channels-cfg__field--wide' : ''}`\n return (\n <div className={fieldClass}>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{field.label}</span>\n {field.restartRequired\n ? <span className=\"dsh-channels-cfg__tag dsh-channels-cfg__tag--restart\">{PANEL_COPY.restartTag}</span>\n : null}\n </div>\n {field.kind === 'boolean'\n ? (\n <label className=\"dsh-channels-cfg__check\">\n <input\n type=\"checkbox\"\n className=\"dsh-channels-cfg__checkbox\"\n checked={value === true}\n aria-label={field.label}\n disabled={disabled}\n onChange={event => onChange(field.key, event.target.checked)}\n />\n <span>{value === true ? PANEL_COPY.enabled : PANEL_COPY.disabled}</span>\n </label>\n )\n : null}\n {field.kind === 'select'\n ? (\n <select\n className={inputClass}\n value={String(value ?? '')}\n disabled={disabled || field.disabled === true}\n aria-label={field.label}\n onChange={event => onChange(field.key, event.target.value)}\n >\n {(field.options ?? []).map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n )\n : null}\n {field.kind === 'number'\n ? (\n <input\n type=\"number\"\n className={inputClass}\n value={value === undefined || value === '' ? '' : String(value)}\n min={field.min}\n max={field.max}\n step={field.step}\n aria-label={field.label}\n disabled={disabled}\n onChange={event => onChange(field.key, event.target.value)}\n />\n )\n : null}\n {field.kind === 'text'\n ? (field.browse === 'directory' && onPickDirectory !== undefined\n ? (\n <DirectoryTextInput\n field={field}\n value={value}\n inputClass={inputClass}\n disabled={disabled}\n onChange={onChange}\n onPick={onPickDirectory}\n />\n )\n : (\n <input\n type=\"text\"\n className={inputClass}\n value={String(value ?? '')}\n placeholder={field.placeholder}\n aria-label={field.label}\n disabled={disabled}\n onChange={event => onChange(field.key, event.target.value)}\n />\n ))\n : null}\n {field.help === undefined ? null : <span className=\"dsh-channels-cfg__help\">{field.help}</span>}\n {issue === undefined ? null : <span className=\"dsh-channels-cfg__fieldError\">{issue.message}</span>}\n </div>\n )\n}\n\n/**\n * One tab's editable fields, partitioned by the Host field groups that the tab\n * owns (`TAB_FIELD_GROUPS`). Empty groups render nothing, so a Host that moves\n * every key out of a group does not leave a stray heading behind.\n */\nexport function FieldsCard(props: {\n form: FormView\n groups: readonly FieldGroup[]\n}): JSX.Element | null {\n const { form, groups } = props\n const sections = groups\n .map(group => ({\n group,\n label: FIELD_GROUP_LABELS[group],\n fields: form.fields.filter(field => field.group === group),\n }))\n .filter(section => section.fields.length > 0)\n if (sections.length === 0) return null\n return (\n <section className=\"dsh-channels-cfg__card\">\n {sections.map(section => (\n <div className=\"dsh-channels-cfg__group\" key={section.group}>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__cardTitle\">{section.label}</span>\n </div>\n <div className=\"dsh-channels-cfg__grid\">\n {section.fields.map((field) => {\n const onPick = form.onPickDirectory\n return (\n <FieldRow\n key={field.key}\n field={field}\n value={form.draft[field.key] ?? effectiveValue(form.panel, field.key)}\n issues={form.issues}\n disabled={form.locked}\n onChange={form.onChange}\n onPickDirectory={onPick === undefined ? undefined : () => onPick(field.key)}\n />\n )\n })}\n </div>\n </div>\n ))}\n </section>\n )\n}\n\n/** The unified per-tab action bar: save every dirty field or drop the draft. */\nexport function SaveBar(props: { form: FormView; tab: PanelTabId }): JSX.Element {\n const { form, tab } = props\n const groups = new Set<string>(TAB_FIELD_GROUPS[tab])\n const pageDirty = form.fields.filter(field => groups.has(field.group) && field.key in form.draft).length\n // Saving submits the whole draft, so any dirty key (any tab) enables it;\n // discarding only clears this tab, so it follows this tab's own count.\n const dirty = form.dirtyCount > 0\n const disabled = !dirty || form.busy !== null || form.locked\n return (\n <div className=\"dsh-channels-cfg__footer\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={form.onSave}\n disabled={disabled}\n >\n {form.busy === 'save' ? PANEL_COPY.saving : PANEL_COPY.save}\n </button>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n onClick={() => form.onDiscardTab(tab)}\n disabled={pageDirty === 0 || form.busy !== null || form.locked}\n >\n {PANEL_COPY.discardTab}\n </button>\n <span className=\"dsh-channels-cfg__spacer\" />\n {pageDirty > 0\n ? (\n <span className=\"dsh-channels-cfg__tag dsh-channels-cfg__tag--dirty\">\n {PANEL_COPY.dirtyCount} {pageDirty}\n </span>\n )\n : dirty\n ? (\n <span className=\"dsh-channels-cfg__tag dsh-channels-cfg__tag--dirty\">\n {PANEL_COPY.dirtyCount} {form.dirtyCount}(其他标签页)\n </span>\n )\n : null}\n <span className=\"dsh-channels-cfg__help\">\n {dirty ? PANEL_COPY.saveHintDirty : PANEL_COPY.saveHintClean}\n </span>\n </div>\n )\n}\n\n/** Muted help paragraph introducing one tab. */\nexport function TabHelp(props: { text: string }): JSX.Element {\n return <p className=\"dsh-channels-cfg__tabHelp\">{props.text}</p>\n}\n\n/** Transient page-level banner (save / credentials / reconnect / catalog). */\nexport function NoticeBar(props: { notice: Notice }): JSX.Element {\n const { notice } = props\n return (\n <div\n className={`dsh-channels-cfg__notice dsh-channels-cfg__notice--${notice.kind === 'ok' ? 'ok' : 'error'}`}\n role=\"status\"\n aria-live=\"polite\"\n >\n <span>{notice.text}</span>\n </div>\n )\n}\n\n/** Muted empty/blocked state (e.g. access lists before the channel connects). */\nexport function EmptyState(props: { text: string }): JSX.Element {\n return <div className=\"dsh-channels-cfg__emptyState\">{props.text}</div>\n}\n\n/** Human label for the official SDK's WebSocket lifecycle state. */\nexport function connectionStateLabel(state: string | undefined): { text: string; tone: BadgeTone } {\n switch (state) {\n case 'connected': return { text: PANEL_COPY.phaseConnected, tone: 'ok' }\n case 'connecting': return { text: PANEL_COPY.phaseConnecting, tone: 'warn' }\n case 'reconnecting': return { text: PANEL_COPY.phaseConnecting, tone: 'warn' }\n case 'failed': return { text: PANEL_COPY.phaseError, tone: 'bad' }\n case 'idle': return { text: PANEL_COPY.phaseIdle, tone: 'muted' }\n default: return { text: state ?? PANEL_COPY.unknownValue, tone: 'muted' }\n }\n}\n\n/** Retention label, reusing the Host field options so the copy stays on the Host. */\nexport function retentionLabel(panel: PanelWire, cleanup: CleanupPreviewWire): string {\n const field = panel.config.fields.find(item => item.key === 'workspaceRetention')\n return field?.options?.find(option => option.value === cleanup.retention)?.label\n ?? (cleanup.retention === 'off' ? PANEL_COPY.cleanupRetentionOff : cleanup.retention)\n}\n","/**\n * 访问与授权卡:fail-closed 警告、群/用户白名单增删、待处理申请处理。\n *\n * `access.loaded === false`(飞书未连接、插件 storage 域未打开)时白名单与\n * 申请列表是「未知」而不是「空」,因此只渲染连接提示并禁用全部写操作。\n * 管理员与准入模式由同 tab 上方「准入与授权」字段表单编辑,这里不再重复。\n *\n * @module dsh-channels/client/panel/access-card\n */\n\nimport { useState } from 'react'\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type {\n AccessGroupWire,\n AccessRequestWire,\n AccessUserWire,\n PanelWire,\n} from '../../settings/contract.ts'\nimport { accessModeLabel, EmptyState } from './shared.tsx'\nimport type { BusyAction } from './shared.tsx'\n\n/** Local `YYYY-MM-DD HH:mm` rendering for authorization timestamps. */\nfunction formatAccessDate(ms: number): string {\n if (!Number.isFinite(ms) || ms <= 0) return PANEL_COPY.unknownValue\n const date = new Date(ms)\n const pad = (value: number): string => String(value).padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/** Which authorization entry produced one grant. */\nfunction accessSourceLabel(source: AccessGroupWire['source']): string {\n switch (source) {\n case 'command': return PANEL_COPY.accessSourceCommand\n case 'card': return PANEL_COPY.accessSourceCard\n default: return PANEL_COPY.accessSourcePanel\n }\n}\n\nexport interface AccessCardProps {\n panel: PanelWire\n busy: BusyAction | null\n locked: boolean\n notice: string | null\n error: string | null\n onAddGroup: (chatId: string, name: string) => Promise<boolean>\n onRemoveGroup: (chatId: string) => void\n onAddUser: (openId: string, name: string) => Promise<boolean>\n onRemoveUser: (openId: string) => void\n onResolveRequest: (token: string, decision: 'allow' | 'deny') => void\n}\n\nexport function AccessCard(props: AccessCardProps): JSX.Element {\n const { panel, busy, locked, notice, error, onAddGroup, onRemoveGroup, onAddUser, onRemoveUser, onResolveRequest } = props\n const { access } = panel\n const [groupChatId, setGroupChatId] = useState('')\n const [groupName, setGroupName] = useState('')\n const [userOpenId, setUserOpenId] = useState('')\n const [userName, setUserName] = useState('')\n const groups: readonly AccessGroupWire[] = access.groups\n const users: readonly AccessUserWire[] = access.users\n const requests: readonly AccessRequestWire[] = access.requests\n const working = busy === 'access'\n const disabled = busy !== null || locked || !access.loaded\n\n const submitGroup = async (): Promise<void> => {\n if (groupChatId.trim() === '') return\n if (await onAddGroup(groupChatId, groupName)) {\n setGroupChatId('')\n setGroupName('')\n }\n }\n\n const submitUser = async (): Promise<void> => {\n if (userOpenId.trim() === '') return\n if (await onAddUser(userOpenId, userName)) {\n setUserOpenId('')\n setUserName('')\n }\n }\n\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.accessCard}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{accessModeLabel(panel, access.mode)}</span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessCardHelp}</span>\n\n {access.mode === 'open'\n ? <div className=\"dsh-channels-cfg__banner\">{PANEL_COPY.accessModeOpenWarning}</div>\n : null}\n {access.loaded && access.mode === 'guarded' && !access.admin.configured\n ? (\n <div className=\"dsh-channels-cfg__notice dsh-channels-cfg__notice--error\">\n <strong>{PANEL_COPY.accessAdminMissingWarning}</strong>\n </div>\n )\n : null}\n\n {!access.loaded\n ? <EmptyState text={PANEL_COPY.accessNotLoaded} />\n : (\n <>\n <div className=\"dsh-channels-cfg__accessSection\">\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{PANEL_COPY.accessGroupsTitle}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{groups.length} 个</span>\n </div>\n {groups.length === 0\n ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessGroupsEmpty}</span>\n : (\n <ul className=\"dsh-channels-cfg__accessList\">\n {groups.map(group => (\n <li className=\"dsh-channels-cfg__accessRow\" key={group.chatId}>\n <span className=\"dsh-channels-cfg__accessMain\">\n <span>{group.name ?? group.chatId}</span>\n <span className=\"dsh-channels-cfg__accessMeta\">\n <span className=\"dsh-channels-cfg__mono\">{group.chatId}</span>\n {` · ${accessSourceLabel(group.source)} · ${formatAccessDate(group.addedAt)}`}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onRemoveGroup(group.chatId)}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessRemove}\n </button>\n </li>\n ))}\n </ul>\n )}\n <div className=\"dsh-channels-cfg__accessAdd\">\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={groupChatId}\n placeholder={PANEL_COPY.accessGroupChatIdPlaceholder}\n aria-label={PANEL_COPY.accessGroupChatIdPlaceholder}\n disabled={disabled}\n onChange={event => setGroupChatId(event.target.value)}\n />\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={groupName}\n placeholder={PANEL_COPY.accessGroupNamePlaceholder}\n aria-label={PANEL_COPY.accessGroupNamePlaceholder}\n disabled={disabled}\n onChange={event => setGroupName(event.target.value)}\n />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n disabled={disabled || groupChatId.trim() === ''}\n onClick={() => { void submitGroup() }}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessAddGroup}\n </button>\n </div>\n </div>\n\n <div className=\"dsh-channels-cfg__accessSection\">\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{PANEL_COPY.accessUsersTitle}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{users.length} 个</span>\n </div>\n {users.length === 0\n ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessUsersEmpty}</span>\n : (\n <ul className=\"dsh-channels-cfg__accessList\">\n {users.map(user => (\n <li className=\"dsh-channels-cfg__accessRow\" key={user.openId}>\n <span className=\"dsh-channels-cfg__accessMain\">\n <span>{user.name ?? user.openId}</span>\n <span className=\"dsh-channels-cfg__accessMeta\">\n <span className=\"dsh-channels-cfg__mono\">{user.openId}</span>\n {` · ${accessSourceLabel(user.source)} · ${formatAccessDate(user.addedAt)}`}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onRemoveUser(user.openId)}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessRemove}\n </button>\n </li>\n ))}\n </ul>\n )}\n <div className=\"dsh-channels-cfg__accessAdd\">\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={userOpenId}\n placeholder={PANEL_COPY.accessUserOpenIdPlaceholder}\n aria-label={PANEL_COPY.accessUserOpenIdPlaceholder}\n disabled={disabled}\n onChange={event => setUserOpenId(event.target.value)}\n />\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={userName}\n placeholder={PANEL_COPY.accessUserNamePlaceholder}\n aria-label={PANEL_COPY.accessUserNamePlaceholder}\n disabled={disabled}\n onChange={event => setUserName(event.target.value)}\n />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n disabled={disabled || userOpenId.trim() === ''}\n onClick={() => { void submitUser() }}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessAddUser}\n </button>\n </div>\n </div>\n\n <div className=\"dsh-channels-cfg__accessSection\">\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{PANEL_COPY.accessRequestsTitle}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{requests.length} 条</span>\n </div>\n {requests.length === 0\n ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessRequestsEmpty}</span>\n : (\n <ul className=\"dsh-channels-cfg__accessList\">\n {requests.map(request => (\n <li className=\"dsh-channels-cfg__accessRow\" key={request.token}>\n <span className=\"dsh-channels-cfg__accessMain\">\n <span>\n {request.kind === 'group' ? PANEL_COPY.accessRequestGroup : PANEL_COPY.accessRequestUser}\n {':'}\n {request.name ?? request.targetId}\n </span>\n <span className=\"dsh-channels-cfg__accessMeta\">\n <span className=\"dsh-channels-cfg__mono\">{request.targetId}</span>\n {` · ${formatAccessDate(request.createdAt)} · ${PANEL_COPY.accessRequestExpires} ${formatAccessDate(request.expiresAt)}`}\n {request.kind === 'user' && request.requester !== undefined\n ? ` · ${PANEL_COPY.accessRequestRequester} ${request.requester}`\n : ''}\n {request.kind === 'user' && request.groupName !== undefined\n ? ` · ${request.groupName}`\n : request.kind === 'user' && request.chatId !== undefined\n ? ` · ${request.chatId}`\n : ''}\n </span>\n </span>\n <span className=\"dsh-channels-cfg__accessActions\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n disabled={disabled}\n onClick={() => onResolveRequest(request.token, 'allow')}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessAllow}\n </button>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onResolveRequest(request.token, 'deny')}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessDeny}\n </button>\n </span>\n </li>\n ))}\n </ul>\n )}\n </div>\n </>\n )}\n\n {notice === null\n ? null\n : (\n <div className=\"dsh-channels-cfg__notice dsh-channels-cfg__notice--ok\" role=\"status\" aria-live=\"polite\">\n <span>{notice}</span>\n </div>\n )}\n {error === null\n ? null\n : <span className=\"dsh-channels-cfg__fieldError\" role=\"alert\">{error}</span>}\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessHints}</span>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessChatIdHint}</span>\n </section>\n )\n}\n","/**\n * 「访问授权」tab:准入与授权字段表单(管理员 open_id / 准入模式 /\n * 申请有效期)+ 白名单与待处理申请卡。表单保存走全局 draft,白名单\n * 增删是独立即时 mutation。\n *\n * @module dsh-channels/client/panel/access-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport { AccessCard } from './access-card.tsx'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport interface AccessTabProps {\n form: FormView\n accessNotice: string | null\n accessError: string | null\n onAddGroup: (chatId: string, name: string) => Promise<boolean>\n onRemoveGroup: (chatId: string) => void\n onAddUser: (openId: string, name: string) => Promise<boolean>\n onRemoveUser: (openId: string) => void\n onResolveRequest: (token: string, decision: 'allow' | 'deny') => void\n}\n\nexport function AccessTab(props: AccessTabProps): JSX.Element {\n const { form, accessNotice, accessError, onAddGroup, onRemoveGroup, onAddUser, onRemoveUser, onResolveRequest } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.accessTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.access} />\n <SaveBar form={form} tab=\"access\" />\n <AccessCard\n panel={form.panel}\n busy={form.busy}\n locked={form.locked}\n notice={accessNotice}\n error={accessError}\n onAddGroup={onAddGroup}\n onRemoveGroup={onRemoveGroup}\n onAddUser={onAddUser}\n onRemoveUser={onRemoveUser}\n onResolveRequest={onResolveRequest}\n />\n </div>\n )\n}\n","/**\n * 「高级」tab:流式刷新等低层字段 + 只读部署信息(overlayPath / version /\n * 部署级参数)+ 「清除 Web 覆盖」。清除覆盖是整体动作,不走 draft。\n *\n * @module dsh-channels/client/panel/advanced-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type { PanelWire } from '../../settings/contract.ts'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { BusyAction, FormView } from './shared.tsx'\n\nexport function AdvancedTab(props: { form: FormView; onClear: () => void }): JSX.Element {\n const { form, onClear } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.advancedTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.advanced} />\n <SaveBar form={form} tab=\"advanced\" />\n <AdvancedCard panel={form.panel} busy={form.busy} locked={form.locked} onClear={onClear} />\n </div>\n )\n}\n\n/** Read-only deployment facts; the only write action here drops the overlay file. */\nfunction AdvancedCard(props: {\n panel: PanelWire\n busy: BusyAction | null\n locked: boolean\n onClear: () => void\n}): JSX.Element {\n const { panel, busy, locked, onClear } = props\n const { readOnly } = panel.config\n const overridden = panel.config.overriddenKeys.length\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.advancedCard}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">\n {overridden === 0 ? PANEL_COPY.inherited : `${PANEL_COPY.overridden} ${overridden} 项`}\n </span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.advancedHelp}</span>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.readOnlyCard}</span>\n </div>\n <div className=\"dsh-channels-cfg__readOnly\">\n {readOnly.map(field => (\n <div className=\"dsh-channels-cfg__kv\" key={field.key}>\n <span className=\"dsh-channels-cfg__kvKey\">{field.label}</span>\n <span className=\"dsh-channels-cfg__kvValue\">{field.value}</span>\n {field.help === undefined ? null : <span className=\"dsh-channels-cfg__help\">{field.help}</span>}\n </div>\n ))}\n <div className=\"dsh-channels-cfg__kv\">\n <span className=\"dsh-channels-cfg__kvKey\">{PANEL_COPY.overlayPath}</span>\n <span className=\"dsh-channels-cfg__kvValue dsh-channels-cfg__mono\">{panel.status.overlayPath}</span>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.overlayPathHelp}</span>\n </div>\n <div className=\"dsh-channels-cfg__kv\">\n <span className=\"dsh-channels-cfg__kvKey\">{PANEL_COPY.version}</span>\n <span className=\"dsh-channels-cfg__kvValue dsh-channels-cfg__mono\">{panel.status.version}</span>\n </div>\n </div>\n <div className=\"dsh-channels-cfg__actions\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n onClick={onClear}\n disabled={overridden === 0 || busy !== null || locked}\n >\n {busy === 'clear' ? PANEL_COPY.clearing : PANEL_COPY.clear}\n </button>\n <span className=\"dsh-channels-cfg__help\">\n {overridden === 0 ? PANEL_COPY.noOverrides : `${PANEL_COPY.overridden} ${overridden} 项`}\n </span>\n </div>\n </section>\n )\n}\n","/**\n * 飞书应用凭据编辑器:值经 Host 的 DSH 凭据服务写入本机凭据库,面板只显示\n * 「已配置 / 未配置」,两个方向都不回显已存值。保存动作由页面外壳持有\n * (凭据保存独立于配置 draft)。\n *\n * @module dsh-channels/client/panel/credentials-card\n */\n\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type { CredentialField, CredentialRefWire, PanelWire } from '../../settings/contract.ts'\nimport type { CredentialsView } from './shared.tsx'\n\nexport function CredentialsCard(props: {\n panel: PanelWire\n credentials: CredentialsView\n}): JSX.Element {\n const { panel, credentials } = props\n const { appId, appSecret, issues, busy, locked, onAppIdChange, onAppSecretChange, onSave, onClear } = credentials\n const appIdState = panel.config.credentials.appId\n const appSecretState = panel.config.credentials.appSecret\n const configured = appIdState.configured && appSecretState.configured\n const writable = appIdState.writable || appSecretState.writable\n const disabled = busy !== null || locked || !writable\n const pending = appId.trim() !== '' || appSecret.trim() !== ''\n\n const row = (\n field: CredentialField,\n label: string,\n state: CredentialRefWire,\n value: string,\n type: 'text' | 'password',\n placeholder: string,\n onChange: (next: string) => void,\n ): JSX.Element => {\n const issue = issues.find(item => item.key === field)\n const inputClass = `dsh-channels-cfg__input${issue === undefined ? '' : ' dsh-channels-cfg__input--invalid'}`\n return (\n <div className=\"dsh-channels-cfg__field\" key={field}>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{label}</span>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${state.configured ? 'ok' : 'bad'}`}>\n {state.configured ? PANEL_COPY.credentialConfigured : PANEL_COPY.credentialMissing}\n </span>\n </div>\n <div className=\"dsh-channels-cfg__cred\">\n <input\n type={type}\n className={inputClass}\n value={value}\n placeholder={placeholder}\n autoComplete=\"new-password\"\n aria-label={label}\n disabled={disabled}\n onChange={event => onChange(event.target.value)}\n />\n {state.configured\n ? (\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onClear(field)}\n >\n {busy === 'credentials-clear' ? PANEL_COPY.removingCredential : PANEL_COPY.removeCredential}\n </button>\n )\n : null}\n </div>\n <span className=\"dsh-channels-cfg__help\">\n {PANEL_COPY.credentialsRef}:<span className=\"dsh-channels-cfg__mono\">{state.ref}</span>\n {state.source === undefined ? '' : ` · ${state.source}`}\n </span>\n {issue === undefined ? null : <span className=\"dsh-channels-cfg__fieldError\">{issue.message}</span>}\n </div>\n )\n }\n\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.credentialsCard}</span>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${configured ? 'ok' : 'muted'}`}>\n {configured ? PANEL_COPY.configuredBadge : PANEL_COPY.unconfiguredBadge}\n </span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.credentialsCardHelp}</span>\n {row('appId', PANEL_COPY.appIdLabel, appIdState, appId, 'text', PANEL_COPY.appIdPlaceholder, onAppIdChange)}\n {row('appSecret', PANEL_COPY.appSecretLabel, appSecretState, appSecret, 'password', PANEL_COPY.appSecretPlaceholder, onAppSecretChange)}\n {writable ? null : <span className=\"dsh-channels-cfg__fieldError\">{PANEL_COPY.credentialReadOnly}</span>}\n <div className=\"dsh-channels-cfg__footer\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={onSave}\n disabled={disabled || !pending}\n >\n {busy === 'credentials' ? PANEL_COPY.savingCredentials : PANEL_COPY.saveCredentials}\n </button>\n <span className=\"dsh-channels-cfg__spacer\" />\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.credentialsPendingHint}</span>\n </div>\n </section>\n )\n}\n","/**\n * 「连接」tab:按 Host 派生的生命周期阶段渲染连接引导。\n *\n * - unconfigured:onboarding 三步 + 凭据卡(不渲染可用的「连接」按钮)\n * - idle:连接信息卡 + 「连接飞书」\n * - error:连接信息卡 + 最近一次连接失败 + 「重试」\n * - connected:连接信息卡 + 「重启飞书连接」+ 凭据卡\n * - connecting:禁用按钮显示「连接中…」\n *\n * @module dsh-channels/client/panel/connection-tab\n */\n\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type { ChannelStatusWire, PanelWire } from '../../settings/contract.ts'\nimport { CredentialsCard } from './credentials-card.tsx'\nimport { connectionStateLabel, fieldLabel, formatUptime, phaseBadge, TabHelp } from './shared.tsx'\nimport type { BusyAction, CredentialsView } from './shared.tsx'\n\nexport interface ConnectionTabProps {\n panel: PanelWire\n status: ChannelStatusWire\n busy: BusyAction | null\n credentials: CredentialsView\n onReconnect: () => void\n}\n\nexport function ConnectionTab(props: ConnectionTabProps): JSX.Element {\n const { panel, status, busy, credentials, onReconnect } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.connectionTabHelp} />\n {panel.config.restartPendingKeys.length === 0\n ? null\n : <RestartBanner panel={panel} busy={busy} locked={status.busy} onReconnect={onReconnect} />}\n {status.phase === 'unconfigured'\n ? <OnboardingCard status={status} />\n : <ConnectionInfoCard status={status} busy={busy} onReconnect={onReconnect} />}\n <CredentialsCard panel={panel} credentials={credentials} />\n </div>\n )\n}\n\n/** First-run guide: create the app, fill the credentials, save and wait. */\nfunction OnboardingCard(props: { status: ChannelStatusWire }): JSX.Element {\n const { status } = props\n const steps = [\n { title: PANEL_COPY.onboardingStep1Title, text: PANEL_COPY.onboardingStep1Text },\n { title: PANEL_COPY.onboardingStep2Title, text: PANEL_COPY.onboardingStep2Text },\n { title: PANEL_COPY.onboardingStep3Title, text: PANEL_COPY.onboardingStep3Text },\n ]\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.onboardingTitle}</span>\n <span className=\"dsh-channels-cfg__badge dsh-channels-cfg__badge--muted\">\n {PANEL_COPY.phaseUnconfigured}\n </span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.onboardingIntro}</span>\n <ol className=\"dsh-channels-cfg__steps\">\n {steps.map((step, index) => (\n <li className=\"dsh-channels-cfg__step\" key={step.title}>\n <span className=\"dsh-channels-cfg__stepIndex\">{index + 1}</span>\n <span className=\"dsh-channels-cfg__stepBody\">\n <span className=\"dsh-channels-cfg__stepTitle\">{step.title}</span>\n <span className=\"dsh-channels-cfg__help\">{step.text}</span>\n </span>\n </li>\n ))}\n </ol>\n {status.missingConfig.length === 0\n ? null\n : (\n <div className=\"dsh-channels-cfg__banner\">\n <strong>{PANEL_COPY.missingConfig}:</strong>\n <span>{status.missingConfig.join('、')}</span>\n </div>\n )}\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.statusHintUnconfigured}</span>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.onboardingDocsHint}</span>\n </section>\n )\n}\n\n/** Runtime facts plus the one lifecycle action available in the current phase. */\nfunction ConnectionInfoCard(props: {\n status: ChannelStatusWire\n busy: BusyAction | null\n onReconnect: () => void\n}): JSX.Element {\n const { status, busy, onReconnect } = props\n const badge = phaseBadge(status.phase)\n // `status.busy` covers a connection attempt started outside this panel; the\n // local busy flag covers the panel's own action. Either way the button waits.\n const actionBusy = busy !== null || status.busy\n const connection = connectionStateLabel(status.connection?.state)\n const facts = [\n { label: PANEL_COPY.statusCard, value: badge.text },\n { label: PANEL_COPY.connectionStateLabel, value: connection.text },\n { label: PANEL_COPY.botOpenId, value: status.botOpenIdTail ?? PANEL_COPY.unknownValue },\n { label: PANEL_COPY.uptime, value: formatUptime(status.uptimeMs) },\n { label: PANEL_COPY.routeCount, value: String(status.routeCount) },\n { label: PANEL_COPY.liveAgentCount, value: String(status.liveAgentCount) },\n { label: PANEL_COPY.inflightTurnCount, value: String(status.inflightTurnCount) },\n ]\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.connectionInfo}</span>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${status.configured ? 'ok' : 'bad'}`}>\n {status.configured ? PANEL_COPY.configuredBadge : PANEL_COPY.unconfiguredBadge}\n </span>\n </div>\n {status.lastStartError === undefined\n ? null\n : (\n <div className=\"dsh-channels-cfg__notice dsh-channels-cfg__notice--error\">\n <strong>{PANEL_COPY.lastError}</strong>\n <span className=\"dsh-channels-cfg__mono\">{status.lastStartError}</span>\n </div>\n )}\n <div className=\"dsh-channels-cfg__facts\">\n {facts.map(fact => (\n <div className=\"dsh-channels-cfg__fact\" key={fact.label}>\n <span className=\"dsh-channels-cfg__factLabel\">{fact.label}</span>\n <span className=\"dsh-channels-cfg__factValue\">{fact.value}</span>\n </div>\n ))}\n </div>\n {status.reasons.length === 0\n ? null\n : (\n <span className={`dsh-channels-cfg__help${status.level === 'unhealthy' ? ' dsh-channels-cfg__reasonBad' : ''}`}>\n {PANEL_COPY.healthReasons}:{status.reasons.join(';')}\n </span>\n )}\n <div className=\"dsh-channels-cfg__actions\">\n {status.phase === 'connecting'\n ? (\n <button type=\"button\" className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\" disabled>\n {PANEL_COPY.connecting}\n </button>\n )\n : null}\n {status.phase === 'idle'\n ? (\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={onReconnect}\n disabled={actionBusy}\n >\n {busy === 'reconnect' ? PANEL_COPY.connecting : PANEL_COPY.connect}\n </button>\n )\n : null}\n {status.phase === 'error'\n ? (\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={onReconnect}\n disabled={actionBusy}\n >\n {busy === 'reconnect' ? PANEL_COPY.connecting : PANEL_COPY.retry}\n </button>\n )\n : null}\n {status.phase === 'connected'\n ? (\n <>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--danger\"\n onClick={onReconnect}\n disabled={actionBusy}\n >\n {busy === 'reconnect' ? PANEL_COPY.reconnecting : PANEL_COPY.reconnect}\n </button>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.restartWarning}</span>\n </>\n )\n : null}\n </div>\n </section>\n )\n}\n\n/** Config saved but only applied on the next connect: offer the one-click restart. */\nfunction RestartBanner(props: {\n panel: PanelWire\n busy: BusyAction | null\n locked: boolean\n onReconnect: () => void\n}): JSX.Element {\n const { panel, busy, locked, onReconnect } = props\n const keys = panel.config.restartPendingKeys\n .map(key => fieldLabel(panel.config.fields, key))\n .join('、')\n return (\n <div className=\"dsh-channels-cfg__banner\">\n <span>{PANEL_COPY.restartBanner}{keys}</span>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--danger\"\n onClick={onReconnect}\n disabled={busy !== null || locked}\n >\n {busy === 'reconnect' ? PANEL_COPY.reconnecting : PANEL_COPY.reconnect}\n </button>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.restartWarning}</span>\n </div>\n )\n}\n","/**\n * 「模型」tab:模型服务商 / 模型 id / 推理档位 / Agent 预设四个选择器。\n * 选项来自 Host 实时目录(页面外壳负责 provider→model→effort 级联刷新),\n * 目录读取中时在表单上方提示。\n *\n * @module dsh-channels/client/panel/model-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport function ModelTab(props: { form: FormView; catalogBusy: boolean }): JSX.Element {\n const { form, catalogBusy } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.modelTabHelp} />\n {catalogBusy ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.catalogLoading}</span> : null}\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.model} />\n <SaveBar form={form} tab=\"model\" />\n </div>\n )\n}\n","/**\n * 「会话与行为」tab:行为 + 内容与附件两组字段(私聊/群聊触发方式、\n * 首次引导、权限切换、附件接收)。表单字段完全由 Host 描述驱动。\n *\n * @module dsh-channels/client/panel/sessions-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport function SessionsTab(props: { form: FormView }): JSX.Element {\n const { form } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.sessionsTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.sessions} />\n <SaveBar form={form} tab=\"sessions\" />\n </div>\n )\n}\n","/**\n * 工作区清理卡:展示清理预览(保留期 / 自动清理 / 候选数量与空间),\n * 执行前二次确认,结果与部分失败逐条展示。`cleanup.error` 非空时\n * (例如未连接、无法扫描工作区)按钮天然禁用并显示原因。\n *\n * @module dsh-channels/client/panel/cleanup-card\n */\n\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type { CleanupPreviewWire, CleanupRunWire, PanelWire } from '../../settings/contract.ts'\nimport { formatBytes, retentionLabel } from './shared.tsx'\nimport type { BusyAction } from './shared.tsx'\n\nexport function CleanupCard(props: {\n panel: PanelWire\n cleanup: CleanupPreviewWire\n busy: BusyAction | null\n locked: boolean\n result: CleanupRunWire | null\n error: string | null\n onRun: () => void\n}): JSX.Element {\n const { panel, cleanup, busy, locked, result, error, onRun } = props\n const autoText = cleanup.retention === 'off'\n ? PANEL_COPY.cleanupAutoInactive\n : cleanup.auto ? PANEL_COPY.cleanupAutoOn : PANEL_COPY.cleanupAutoOff\n const blocked = cleanup.error !== undefined\n ? `${PANEL_COPY.cleanupPreviewFailed}:${cleanup.error}`\n : cleanup.retention === 'off'\n ? PANEL_COPY.cleanupDisabledOff\n : cleanup.candidates === 0\n ? PANEL_COPY.cleanupDisabledEmpty\n : null\n const facts = [\n { label: PANEL_COPY.cleanupRetention, value: retentionLabel(panel, cleanup) },\n { label: PANEL_COPY.cleanupAutoLabel, value: autoText },\n { label: PANEL_COPY.cleanupCandidates, value: `${cleanup.candidates} / ${cleanup.workspaces} 个` },\n { label: PANEL_COPY.cleanupCandidateBytes, value: formatBytes(cleanup.candidateBytes) },\n ]\n const running = busy === 'cleanup'\n const failed = result !== null && (!result.ok || error !== null)\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.cleanupCard}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{retentionLabel(panel, cleanup)}</span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.cleanupCardHelp}</span>\n <div className=\"dsh-channels-cfg__facts\">\n {facts.map(fact => (\n <div className=\"dsh-channels-cfg__fact\" key={fact.label}>\n <span className=\"dsh-channels-cfg__factLabel\">{fact.label}</span>\n <span className=\"dsh-channels-cfg__factValue\">{fact.value}</span>\n </div>\n ))}\n </div>\n {blocked === null ? null : <span className=\"dsh-channels-cfg__help\">{blocked}</span>}\n {result === null\n ? null\n : (\n <div className={`dsh-channels-cfg__notice dsh-channels-cfg__notice--${failed ? 'error' : 'ok'}`}>\n {result.ok\n ? (\n <span>\n {PANEL_COPY.cleanupDone}:{PANEL_COPY.cleanupDeleted} {result.deletedWorkspaces} 个,\n {PANEL_COPY.cleanupRetired} {result.retiredRoutes} 条,\n {PANEL_COPY.cleanupFreed} {formatBytes(result.freedBytes)}。\n </span>\n )\n : (\n <span>\n {PANEL_COPY.cleanupFailed}\n {result.error === undefined ? '' : `:${result.error}`}\n </span>\n )}\n {result.failures.length === 0\n ? null\n : (\n <>\n <span>{PANEL_COPY.cleanupFailures}</span>\n <ul className=\"dsh-channels-cfg__issues\">\n {result.failures.map((failure, index) => (\n <li key={`${index}-${failure}`} className=\"dsh-channels-cfg__mono\">{failure}</li>\n ))}\n </ul>\n </>\n )}\n </div>\n )}\n {error === null ? null : <span className=\"dsh-channels-cfg__fieldError\">{error}</span>}\n <div className=\"dsh-channels-cfg__actions\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--danger\"\n onClick={onRun}\n disabled={running || busy !== null || locked || blocked !== null}\n >\n {running ? PANEL_COPY.cleanupRunning : PANEL_COPY.cleanupNow}\n </button>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.cleanupConfirmHint}</span>\n </div>\n </section>\n )\n}\n","/**\n * 「工作区」tab:工作区根目录 / 过期保留策略 / 自动清理字段 + 工作区清理卡。\n * 表单保存栏在字段卡正下方,清理卡(独立动作,不走 draft)在其后。\n *\n * @module dsh-channels/client/panel/workspace-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type { CleanupPreviewWire, CleanupRunWire } from '../../settings/contract.ts'\nimport { CleanupCard } from './cleanup-card.tsx'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport interface WorkspaceTabProps {\n form: FormView\n cleanup: CleanupPreviewWire\n cleanupResult: CleanupRunWire | null\n cleanupError: string | null\n onRunCleanup: () => void\n}\n\nexport function WorkspaceTab(props: WorkspaceTabProps): JSX.Element {\n const { form, cleanup, cleanupResult, cleanupError, onRunCleanup } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.workspaceTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.workspace} />\n <SaveBar form={form} tab=\"workspace\" />\n <CleanupCard\n panel={form.panel}\n cleanup={cleanup}\n busy={form.busy}\n locked={form.locked}\n result={cleanupResult}\n error={cleanupError}\n onRun={onRunCleanup}\n />\n </div>\n )\n}\n","/**\n * 设置 →「飞书渠道」页面:数据加载、5 秒状态轮询、全局 draft 与顶部状态头 /\n * tab 导航的外壳。\n *\n * 所有 Host 交互(panel / status / catalog / config / credentials / cleanup /\n * access / pick-directory)都集中在这里;tab 组件只接收渲染所需的数据与\n * 回调。页面不 import 任何 DSH 客户端包,只依赖 react 与 `settings.section`\n * 槽位。\n *\n * @module dsh-channels/client/panel\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { KeyboardEvent } from 'react'\nimport { PANEL_COPY, PANEL_TABS, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type {\n AccessWire,\n CatalogWire,\n ChannelStatusWire,\n CleanupRunWire,\n ConfigValue,\n CredentialField,\n EditableKey,\n EditableValues,\n FieldIssueWire,\n PanelTabId,\n PanelWire,\n} from '../../settings/contract.ts'\nimport {\n addAccessGroup,\n addAccessUser,\n clearConfig,\n clearCredential,\n fetchCatalog,\n fetchPanel,\n fetchStatus,\n pickDirectory as requestDirectoryPicker,\n reconnect,\n removeAccessGroup,\n removeAccessUser,\n resolveAccessRequest,\n runCleanup,\n saveConfig,\n saveCredentials,\n} from '../api.ts'\nimport { applyCatalogFields, catalogFromFields } from '../catalog.ts'\nimport { AccessTab } from './access-tab.tsx'\nimport { AdvancedTab } from './advanced-tab.tsx'\nimport { ConnectionTab } from './connection-tab.tsx'\nimport { ModelTab } from './model-tab.tsx'\nimport { SessionsTab } from './sessions-tab.tsx'\nimport {\n fieldLabel,\n formatBytes,\n messageOf,\n NoticeBar,\n phaseBadge,\n phaseSummary,\n validateDraft,\n} from './shared.tsx'\nimport type {\n BusyAction,\n CredentialsView,\n FormView,\n LoadState,\n Notice,\n} from './shared.tsx'\nimport { WorkspaceTab } from './workspace-tab.tsx'\n\n/** Owner props handed to a `settings.section` contribution (unused beyond parity). */\nexport interface FeishuChannelsSectionProps {\n close?: () => void\n}\n\n/** Page shell: one panel payload, one status poll, one global config draft. */\nexport function FeishuChannelsSection(_props: FeishuChannelsSectionProps): JSX.Element | null {\n const [load, setLoad] = useState<LoadState>({ phase: 'loading' })\n const [activeTab, setActiveTab] = useState<PanelTabId>('connection')\n const [draft, setDraft] = useState<EditableValues>({})\n const [notice, setNotice] = useState<Notice | null>(null)\n const [busy, setBusy] = useState<BusyAction | null>(null)\n const [status, setStatus] = useState<ChannelStatusWire | null>(null)\n const [pollError, setPollError] = useState<string | null>(null)\n const [appIdInput, setAppIdInput] = useState('')\n const [appSecretInput, setAppSecretInput] = useState('')\n const [catalog, setCatalog] = useState<CatalogWire | null>(null)\n const [catalogBusy, setCatalogBusy] = useState(false)\n const [cleanupResult, setCleanupResult] = useState<CleanupRunWire | null>(null)\n const [cleanupError, setCleanupError] = useState<string | null>(null)\n const [accessNotice, setAccessNotice] = useState<string | null>(null)\n const [accessError, setAccessError] = useState<string | null>(null)\n const catalogTicket = useRef(0)\n /** Latest ready panel, read from the poll loop without restarting it. */\n const panelRef = useRef<PanelWire | null>(null)\n const panel = load.phase === 'ready' ? load.panel : null\n /** Host-side lifecycle busy: connecting/restarting blocks every panel write. */\n const hostBusy = (status ?? panel?.status)?.busy === true\n useEffect(() => {\n panelRef.current = panel\n }, [panel])\n\n const loadPanel = useCallback(async (): Promise<void> => {\n setLoad({ phase: 'loading' })\n setNotice(null)\n setCleanupResult(null)\n setCleanupError(null)\n setAccessNotice(null)\n setAccessError(null)\n try {\n const next = await fetchPanel()\n setLoad({ phase: 'ready', panel: next })\n setStatus(next.status)\n setDraft({})\n setAppIdInput('')\n setAppSecretInput('')\n } catch (error) {\n setLoad({ phase: 'error', message: messageOf(error) })\n }\n }, [])\n\n useEffect(() => {\n void loadPanel()\n }, [loadPanel])\n\n // The Host panel payload already carries options resolved for the effective\n // route; re-seed the local catalog whenever a fresh panel arrives (load/save).\n useEffect(() => {\n if (panel === null) {\n setCatalog(null)\n return\n }\n setCatalog(catalogFromFields(panel.config.fields))\n setCatalogBusy(false)\n }, [panel])\n\n useEffect(() => {\n if (load.phase !== 'ready') return undefined\n let cancelled = false\n const tick = async (): Promise<void> => {\n try {\n const previousPhase = panelRef.current?.status.phase\n const next = await fetchStatus()\n if (cancelled) return\n setStatus(next)\n setPollError(null)\n // A phase transition changes what the panel payload can offer: the\n // storage domain opens/closes with the connection, so the access\n // whitelists and the cleanup preview go stale. Re-read the panel but\n // never touch the dirty draft.\n if (previousPhase !== undefined && previousPhase !== next.phase) {\n try {\n const refreshed = await fetchPanel()\n if (!cancelled) {\n setLoad({ phase: 'ready', panel: refreshed })\n setStatus(refreshed.status)\n }\n } catch {\n // Keep the status-only update; the next manual refresh retries.\n }\n }\n } catch (error) {\n if (!cancelled) setPollError(messageOf(error))\n }\n }\n const timer = window.setInterval(() => { void tick() }, 5_000)\n return () => {\n cancelled = true\n window.clearInterval(timer)\n }\n }, [load.phase])\n\n /** Fetch the selector catalog for a draft route (latest request wins). */\n const refreshCatalog = useCallback(async (provider: string, model: string): Promise<void> => {\n catalogTicket.current += 1\n const ticket = catalogTicket.current\n setCatalogBusy(true)\n try {\n const next = await fetchCatalog(provider, model)\n if (catalogTicket.current === ticket) setCatalog(next)\n } catch {\n if (catalogTicket.current !== ticket) return\n // Never keep another route's options: degrade the dependent fields to the\n // free-text controls instead of offering stale values.\n setCatalog(previous => previous === null ? null : {\n provider: previous.provider,\n model: { selectable: false, options: [] },\n reasoningEffort: { selectable: false, options: [] },\n agentPreset: previous.agentPreset,\n })\n setNotice({ kind: 'error', text: PANEL_COPY.catalogFailed })\n } finally {\n if (catalogTicket.current === ticket) setCatalogBusy(false)\n }\n }, [])\n\n /** Current form values, used to keep unlisted configured values visible. */\n const currentValues = useMemo(() => {\n if (panel === null) return {}\n const valueOf = (key: EditableKey): string | undefined => {\n const raw = draft[key] ?? panel.config.effective[key] ?? panel.config.base[key]\n return typeof raw === 'string' && raw !== '' ? raw : undefined\n }\n return {\n provider: valueOf('provider'),\n model: valueOf('model'),\n reasoningEffort: valueOf('reasoningEffort'),\n agentPreset: valueOf('agentPreset'),\n }\n }, [draft, panel])\n\n const renderedFields = useMemo(\n () => applyCatalogFields(panel?.config.fields ?? [], catalog, currentValues),\n [panel, catalog, currentValues],\n )\n\n /** Whether one selector currently renders as a dropdown for this form. */\n const isSelectField = useCallback(\n (key: EditableKey): boolean => renderedFields.some(field => field.key === key && field.kind === 'select'),\n [renderedFields],\n )\n\n /** Draft one field, cascading provider → model → effort like the Host catalog. */\n const updateDraft = useCallback((key: EditableKey, value: ConfigValue) => {\n if (panel === null) return\n const providerNow = String(\n draft.provider ?? panel.config.effective.provider ?? panel.config.base.provider ?? '',\n )\n setNotice(null)\n if (key === 'provider') {\n // Changing the provider invalidates the model/effort below it. When the\n // selected provider is the configured one, keep showing its configured\n // model; otherwise fall back to the inherit choice.\n const nextProvider = String(value ?? '')\n const configuredProvider = String(\n panel.config.effective.provider ?? panel.config.base.provider ?? '',\n )\n const keepModel = nextProvider === configuredProvider\n ? String(panel.config.effective.model ?? panel.config.base.model ?? '')\n : ''\n setDraft(previous => ({ ...previous, provider: value, model: '', reasoningEffort: '' }))\n if (catalog !== null && isSelectField('provider')) void refreshCatalog(nextProvider, keepModel)\n return\n }\n if (key === 'model') {\n setDraft(previous => ({ ...previous, model: value, reasoningEffort: '' }))\n if (catalog !== null && isSelectField('model')) void refreshCatalog(providerNow, String(value ?? ''))\n return\n }\n setDraft(previous => ({ ...previous, [key]: value }))\n }, [catalog, draft.provider, isSelectField, panel, refreshCatalog])\n\n const refreshStatus = useCallback(async (): Promise<void> => {\n try {\n const next = await fetchStatus()\n setStatus(next)\n setPollError(null)\n } catch (error) {\n setPollError(messageOf(error))\n }\n }, [])\n\n /**\n * Manual refresh: reload the whole panel payload so access lists, the cleanup\n * preview and `restartPendingKeys` catch up with the Host. The dirty draft is\n * deliberately preserved — refreshing never discards user input.\n */\n const onRefresh = useCallback(async (): Promise<void> => {\n try {\n const next = await fetchPanel()\n setLoad({ phase: 'ready', panel: next })\n setStatus(next.status)\n setPollError(null)\n setAccessNotice(null)\n setAccessError(null)\n } catch (error) {\n setPollError(messageOf(error))\n }\n }, [])\n\n /** Submit every dirty field at once; the Host persists and re-validates. */\n const onSave = useCallback(async (): Promise<void> => {\n if (panel === null) return\n const { values, issues } = validateDraft(panel, draft)\n if (issues.length > 0) {\n // Name the offending fields: with one draft shared by every tab, the\n // user may be looking at a different tab than the invalid field.\n const detail = issues\n .map(issue => `${fieldLabel(panel.config.fields, issue.key as EditableKey)} ${issue.message}`)\n .join(';')\n setNotice({ kind: 'error', text: `${PANEL_COPY.saveInvalid}:${detail}`, issues })\n return\n }\n if (Object.keys(values).length === 0) return\n setBusy('save')\n setNotice(null)\n try {\n const result = await saveConfig(values)\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error, issues: result.issues })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setCleanupResult(null)\n setCleanupError(null)\n // Clear only the submitted keys: edits typed while the request was in\n // flight stay in the draft instead of being silently discarded.\n setDraft(previous => {\n const next = { ...previous }\n for (const key of Object.keys(values) as EditableKey[]) delete next[key]\n return next\n })\n setNotice({ kind: 'ok', text: PANEL_COPY.saved })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [draft, panel])\n\n /** Drop the draft keys owned by one tab (nothing is written). */\n const discardTab = useCallback((tab: PanelTabId): void => {\n if (panel === null) return\n const groups = new Set<string>(TAB_FIELD_GROUPS[tab])\n const keys = new Set(\n panel.config.fields.filter(field => groups.has(field.group)).map(field => field.key),\n )\n setDraft((previous) => {\n const next = { ...previous }\n for (const key of keys) delete next[key]\n return next\n })\n setNotice(null)\n if (tab === 'model') {\n catalogTicket.current += 1\n setCatalog(catalogFromFields(panel.config.fields))\n setCatalogBusy(false)\n }\n }, [panel])\n\n /**\n * Open the host's native directory chooser (workspace root「选择目录…」) and\n * stage the picked absolute path as a draft value. Nothing is persisted until\n *「保存更改」; a canceled dialog is a no-op, not an error.\n */\n const onPickDirectory = useCallback(async (key: EditableKey): Promise<void> => {\n setNotice(null)\n try {\n const result = await requestDirectoryPicker()\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error })\n return\n }\n if ('canceled' in result) return\n setDraft(previous => ({ ...previous, [key]: result.path }))\n setNotice({ kind: 'ok', text: `${PANEL_COPY.directoryPicked}:${result.path}` })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n }\n }, [])\n\n /** Drop every Web override and return the refreshed panel. */\n const onClear = useCallback(async (): Promise<void> => {\n setBusy('clear')\n setNotice(null)\n try {\n const result = await clearConfig()\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setDraft({})\n setCleanupResult(null)\n setCleanupError(null)\n setNotice({ kind: 'ok', text: PANEL_COPY.cleared })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [])\n\n const running = (status ?? panel?.status)?.running === true\n\n /**\n * The single connect/reconnect action: open a stopped channel or restart a\n * running one. Only a restart interrupts live replies, so only it confirms.\n */\n const onReconnect = useCallback(async (): Promise<void> => {\n if (running) {\n const confirmed = typeof window.confirm === 'function'\n ? window.confirm(PANEL_COPY.restartWarning)\n : true\n if (!confirmed) return\n }\n setBusy('reconnect')\n setNotice(null)\n try {\n const result = await reconnect()\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error })\n // Incomplete credentials: send the user back to the onboarding form.\n if (result.unconfigured === true) setActiveTab('connection')\n await refreshStatus()\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setNotice({ kind: 'ok', text: running ? PANEL_COPY.reconnectDone : PANEL_COPY.connectDone })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n await refreshStatus()\n } finally {\n setBusy(null)\n }\n }, [refreshStatus, running])\n\n /**\n * Delete stale workspaces after an explicit confirmation. The Host refreshes\n * the panel in the same response, so the preview counts update immediately;\n * partial failures stay visible in the card instead of being swallowed.\n */\n const onRunCleanup = useCallback(async (): Promise<void> => {\n if (panel === null) return\n const { cleanup } = panel\n const confirmed = typeof window.confirm === 'function'\n ? window.confirm(\n `${PANEL_COPY.cleanupConfirm}\\n${PANEL_COPY.cleanupCandidates}:${cleanup.candidates} · ${PANEL_COPY.cleanupCandidateBytes}:${formatBytes(cleanup.candidateBytes)}`,\n )\n : true\n if (!confirmed) return\n setBusy('cleanup')\n setNotice(null)\n setCleanupResult(null)\n setCleanupError(null)\n try {\n const response = await runCleanup()\n if (!response.ok) {\n setCleanupError(response.error)\n return\n }\n setLoad({ phase: 'ready', panel: response.panel })\n setStatus(response.panel.status)\n setCleanupResult(response.result)\n } catch (error) {\n setCleanupError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [panel])\n\n /** Replace the embedded access snapshot after a whitelist mutation. */\n const applyAccess = useCallback((access: AccessWire): void => {\n setLoad(previous => previous.phase === 'ready'\n ? { phase: 'ready', panel: { ...previous.panel, access } }\n : previous)\n }, [])\n\n const onAddAccessGroup = useCallback(async (chatId: string, name: string): Promise<boolean> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await addAccessGroup(chatId, name)\n if (!result.ok) {\n setAccessError(result.error)\n return false\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessGroupAdded)\n return true\n } catch (error) {\n setAccessError(messageOf(error))\n return false\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onRemoveAccessGroup = useCallback(async (chatId: string): Promise<void> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await removeAccessGroup(chatId)\n if (!result.ok) {\n setAccessError(result.error)\n return\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessGroupRemoved)\n } catch (error) {\n setAccessError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onAddAccessUser = useCallback(async (openId: string, name: string): Promise<boolean> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await addAccessUser(openId, name)\n if (!result.ok) {\n setAccessError(result.error)\n return false\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessUserAdded)\n return true\n } catch (error) {\n setAccessError(messageOf(error))\n return false\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onRemoveAccessUser = useCallback(async (openId: string): Promise<void> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await removeAccessUser(openId)\n if (!result.ok) {\n setAccessError(result.error)\n return\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessUserRemoved)\n } catch (error) {\n setAccessError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onResolveAccessRequest = useCallback(async (token: string, decision: 'allow' | 'deny'): Promise<void> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await resolveAccessRequest(token, decision)\n if (!result.ok) {\n setAccessError(result.error)\n return\n }\n applyAccess(result.access)\n setAccessNotice(decision === 'allow' ? PANEL_COPY.accessRequestAllowed : PANEL_COPY.accessRequestDenied)\n } catch (error) {\n setAccessError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n /** Save provided credential values; the Host reconnects automatically when ready. */\n const onSaveCredentials = useCallback(async (): Promise<void> => {\n const values: { appId?: string; appSecret?: string } = {}\n if (appIdInput.trim() !== '') values.appId = appIdInput\n if (appSecretInput.trim() !== '') values.appSecret = appSecretInput\n if (values.appId === undefined && values.appSecret === undefined) return\n setBusy('credentials')\n setNotice(null)\n try {\n const result = await saveCredentials(values)\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error, issues: result.issues })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setAppIdInput('')\n setAppSecretInput('')\n const base = result.reconnect === 'pending'\n ? PANEL_COPY.credentialSavedPending\n : result.reconnect === 'auto'\n ? PANEL_COPY.credentialSavedReconnected\n : result.reconnect === 'unconfigured'\n ? PANEL_COPY.credentialSavedUnconfigured\n : result.reconnect === 'failed'\n ? PANEL_COPY.credentialAutoConnectFailed\n : PANEL_COPY.credentialSaved\n setNotice({\n kind: result.reconnect === 'failed' || result.autoReconnectError !== undefined ? 'error' : 'ok',\n text: result.autoReconnectError === undefined\n ? base\n : `${base}:${result.autoReconnectError}`,\n })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [appIdInput, appSecretInput])\n\n const onClearCredential = useCallback(async (field: CredentialField): Promise<void> => {\n const confirmed = typeof window.confirm === 'function'\n ? window.confirm(PANEL_COPY.credentialRemoveConfirm)\n : true\n if (!confirmed) return\n setBusy('credentials-clear')\n setNotice(null)\n try {\n const result = await clearCredential(field)\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error, issues: result.issues })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setNotice({\n kind: 'ok',\n text: result.reconnect === 'pending' ? PANEL_COPY.credentialRemovedPending : PANEL_COPY.credentialRemoved,\n })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [])\n\n const issues = useMemo<FieldIssueWire[]>(\n () => (notice?.kind === 'error' ? notice.issues ?? [] : []),\n [notice],\n )\n const dirtyCount = Object.keys(draft).length\n\n /** Which tabs currently own a dirty key (the dot is per tab, not global). */\n const dirtyTabs = useMemo<ReadonlySet<PanelTabId>>(() => {\n const tabs = new Set<PanelTabId>()\n if (panel === null || dirtyCount === 0) return tabs\n const groupOf = new Map(panel.config.fields.map(field => [field.key, field.group]))\n for (const key of Object.keys(draft)) {\n const group = groupOf.get(key as EditableKey)\n if (group === undefined) continue\n for (const tab of PANEL_TABS) {\n if (TAB_FIELD_GROUPS[tab.id].includes(group)) tabs.add(tab.id)\n }\n }\n return tabs\n }, [dirtyCount, draft, panel])\n\n const form = useMemo<FormView | null>(() => {\n if (panel === null) return null\n return {\n panel,\n fields: renderedFields,\n draft,\n issues,\n busy,\n locked: hostBusy,\n dirtyCount,\n onChange: updateDraft,\n onSave: () => { void onSave() },\n onDiscardTab: discardTab,\n onPickDirectory,\n }\n }, [busy, discardTab, dirtyCount, draft, hostBusy, issues, onPickDirectory, onSave, panel, renderedFields, updateDraft])\n\n const credentials = useMemo<CredentialsView>(() => ({\n appId: appIdInput,\n appSecret: appSecretInput,\n issues,\n busy,\n locked: hostBusy,\n onAppIdChange: setAppIdInput,\n onAppSecretChange: setAppSecretInput,\n onSave: () => { void onSaveCredentials() },\n onClear: (field: CredentialField) => { void onClearCredential(field) },\n }), [appIdInput, appSecretInput, busy, hostBusy, issues, onClearCredential, onSaveCredentials])\n\n if (load.phase === 'loading') {\n return (\n <div className=\"dsh-channels-cfg\">\n <div className=\"dsh-channels-cfg__loading\">{PANEL_COPY.loading}</div>\n </div>\n )\n }\n if (load.phase === 'error') {\n return (\n <div className=\"dsh-channels-cfg\">\n <div className=\"dsh-channels-cfg__error\">\n <strong>{PANEL_COPY.loadFailed}</strong>\n <span>{load.message}</span>\n <div>\n <button type=\"button\" className=\"dsh-channels-cfg__btn\" onClick={() => { void loadPanel() }}>\n {PANEL_COPY.retry}\n </button>\n </div>\n </div>\n </div>\n )\n }\n if (form === null) return null\n const current = status ?? load.panel.status\n\n return (\n <div className=\"dsh-channels-cfg\">\n <PanelHeader\n status={current}\n pollError={pollError}\n busy={busy}\n locked={hostBusy}\n onRefresh={() => { void onRefresh() }}\n />\n <TabNav activeTab={activeTab} dirtyTabs={dirtyTabs} onChange={setActiveTab} />\n {notice === null ? null : <NoticeBar notice={notice} />}\n <div\n role=\"tabpanel\"\n id={`dsh-channels-tabpanel-${activeTab}`}\n aria-labelledby={`dsh-channels-tab-${activeTab}`}\n >\n {activeTab === 'connection'\n ? (\n <ConnectionTab\n panel={form.panel}\n status={current}\n busy={busy}\n credentials={credentials}\n onReconnect={() => { void onReconnect() }}\n />\n )\n : null}\n {activeTab === 'sessions' ? <SessionsTab form={form} /> : null}\n {activeTab === 'model' ? <ModelTab form={form} catalogBusy={catalogBusy} /> : null}\n {activeTab === 'workspace'\n ? (\n <WorkspaceTab\n form={form}\n cleanup={form.panel.cleanup}\n cleanupResult={cleanupResult}\n cleanupError={cleanupError}\n onRunCleanup={() => { void onRunCleanup() }}\n />\n )\n : null}\n {activeTab === 'access'\n ? (\n <AccessTab\n form={form}\n accessNotice={accessNotice}\n accessError={accessError}\n onAddGroup={onAddAccessGroup}\n onRemoveGroup={chatId => { void onRemoveAccessGroup(chatId) }}\n onAddUser={onAddAccessUser}\n onRemoveUser={openId => { void onRemoveAccessUser(openId) }}\n onResolveRequest={(token, decision) => { void onResolveAccessRequest(token, decision) }}\n />\n )\n : null}\n {activeTab === 'advanced'\n ? <AdvancedTab form={form} onClear={() => { void onClear() }} />\n : null}\n </div>\n </div>\n )\n}\n\n/** Page header: title + phase badge + refresh, visible on every tab. */\nfunction PanelHeader(props: {\n status: ChannelStatusWire\n pollError: string | null\n busy: BusyAction | null\n locked: boolean\n onRefresh: () => void\n}): JSX.Element {\n const { status, pollError, busy, locked, onRefresh } = props\n const badge = phaseBadge(status.phase)\n return (\n <header className=\"dsh-channels-cfg__panelHead\" title={PANEL_COPY.description}>\n <div className=\"dsh-channels-cfg__headRow\">\n <h2 className=\"dsh-channels-cfg__title\">{PANEL_COPY.title}</h2>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${badge.tone}`}>\n <span className=\"dsh-channels-cfg__dot\" />\n {badge.text}\n </span>\n <span className=\"dsh-channels-cfg__spacer\" />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n onClick={onRefresh}\n disabled={busy !== null || locked}\n >\n {PANEL_COPY.refresh}\n </button>\n </div>\n <p className=\"dsh-channels-cfg__summary\">{phaseSummary(status.phase)}</p>\n {pollError === null\n ? null\n : (\n <span className=\"dsh-channels-cfg__fieldError\" role=\"status\" aria-live=\"polite\">\n {PANEL_COPY.pollFailed}:{pollError}\n </span>\n )}\n </header>\n )\n}\n\n/** Top-level tab navigation; the dirty dot keeps unsaved work visible per tab. */\nfunction TabNav(props: {\n activeTab: PanelTabId\n dirtyTabs: ReadonlySet<PanelTabId>\n onChange: (tab: PanelTabId) => void\n}): JSX.Element {\n const { activeTab, dirtyTabs, onChange } = props\n const refs = useRef<Array<HTMLButtonElement | null>>([])\n\n /** Roving-focus keyboard navigation between tabs (WAI-ARIA tabs pattern). */\n const onKeyDown = (index: number, event: KeyboardEvent<HTMLButtonElement>): void => {\n const last = PANEL_TABS.length - 1\n let next: number\n switch (event.key) {\n case 'ArrowRight': next = index === last ? 0 : index + 1; break\n case 'ArrowLeft': next = index === 0 ? last : index - 1; break\n case 'Home': next = 0; break\n case 'End': next = last; break\n default: return\n }\n event.preventDefault()\n onChange(PANEL_TABS[next]!.id)\n refs.current[next]?.focus()\n }\n\n return (\n <nav className=\"dsh-channels-cfg__tabs\" role=\"tablist\" aria-label={PANEL_COPY.title}>\n {PANEL_TABS.map((tab, index) => {\n const selected = tab.id === activeTab\n const dirty = dirtyTabs.has(tab.id)\n return (\n <button\n key={tab.id}\n ref={(node) => { refs.current[index] = node }}\n type=\"button\"\n role=\"tab\"\n id={`dsh-channels-tab-${tab.id}`}\n aria-selected={selected}\n aria-controls={selected ? `dsh-channels-tabpanel-${tab.id}` : undefined}\n tabIndex={selected ? 0 : -1}\n className=\"dsh-channels-cfg__tab\"\n onClick={() => onChange(tab.id)}\n onKeyDown={event => onKeyDown(index, event)}\n >\n {tab.label}\n {dirty\n ? (\n <span\n className=\"dsh-channels-cfg__tabDot\"\n title={PANEL_COPY.dirtyCount}\n aria-label={PANEL_COPY.dirtyCount}\n />\n )\n : null}\n </button>\n )\n })}\n </nav>\n )\n}\n","/**\n * Settings panel styling. DSH semantic tokens (`--dsw-alias-*`) with neutral\n * fallbacks so the page follows the active theme; only pseudo-classes and\n * responsive rules live here (everything else is inline in the component).\n *\n * @module dsh-channels/client/styles\n */\n\nconst STYLE_ATTRIBUTE = 'data-dsh-channels-css'\n\nconst CSS = `\n.dsh-channels-cfg{container-type:inline-size;display:flex;flex-direction:column;gap:12px;width:100%;max-width:720px;color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;line-height:20px}\n.dsh-channels-cfg__panelHead{position:sticky;top:0;z-index:2;display:flex;flex-direction:column;gap:4px;padding:10px 0 8px;background:var(--dsw-alias-bg-base,#fff);border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dsh-channels-cfg__headRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__title{margin:0;font-size:16px;font-weight:500;line-height:24px}\n.dsh-channels-cfg__summary{margin:0;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:18px}\n.dsh-channels-cfg__tabs{display:flex;align-items:center;gap:2px;flex-wrap:wrap;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dsh-channels-cfg__tab{display:inline-flex;align-items:center;gap:5px;min-height:32px;padding:0 10px;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#5c6169);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.dsh-channels-cfg__tab:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dsh-channels-cfg__tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0a67d3);outline-offset:-2px}\n.dsh-channels-cfg__tab[aria-selected=\"true\"]{color:var(--dsw-alias-state-business-primary,#0a67d3);border-bottom-color:currentColor;font-weight:500}\n.dsh-channels-cfg__tabDot{width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#a15c00);flex:none}\n.dsh-channels-cfg__tabBody{display:flex;flex-direction:column;gap:12px}\n.dsh-channels-cfg__tabHelp{margin:0;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:18px}\n.dsh-channels-cfg__card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:12px}\n.dsh-channels-cfg__cardHead{display:flex;align-items:center;gap:8px;min-height:20px;flex-wrap:wrap}\n.dsh-channels-cfg__cardTitle{font-size:13px;font-weight:500}\n.dsh-channels-cfg__cardMeta{margin-left:auto;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px}\n.dsh-channels-cfg__group{display:flex;flex-direction:column;gap:8px}\n.dsh-channels-cfg__badge{display:inline-flex;align-items:center;gap:5px;padding:1px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));font-size:12px;line-height:18px;white-space:nowrap}\n.dsh-channels-cfg__dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}\n.dsh-channels-cfg__badge--ok{color:var(--dsw-alias-state-business-primary,#0a7d33);border-color:currentColor}\n.dsh-channels-cfg__badge--warn{color:var(--dsw-alias-state-warning-primary,#a15c00);border-color:currentColor}\n.dsh-channels-cfg__badge--bad{color:var(--dsw-alias-state-danger-primary,#c62828);border-color:currentColor}\n.dsh-channels-cfg__badge--muted{color:var(--dsw-alias-label-tertiary,#8a8f98)}\n.dsh-channels-cfg__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px 16px}\n.dsh-channels-cfg__fact{display:flex;flex-direction:column;gap:2px;min-width:0}\n.dsh-channels-cfg__factLabel{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:16px}\n.dsh-channels-cfg__factValue{font-variant-numeric:tabular-nums;overflow-wrap:anywhere}\n.dsh-channels-cfg__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-height:28px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.dsh-channels-cfg__btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05))}\n.dsh-channels-cfg__btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0a67d3);outline-offset:2px}\n.dsh-channels-cfg__btn:disabled{color:var(--dsw-alias-label-dimmed,#b0b4ba);cursor:default}\n.dsh-channels-cfg__btn--primary{background:var(--dsw-alias-button-primary-fill,#0a67d3);border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}\n.dsh-channels-cfg__btn--primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#0b5cba)}\n.dsh-channels-cfg__btn--danger{color:var(--dsw-alias-state-danger-primary,#c62828);border-color:currentColor}\n.dsh-channels-cfg__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 20px;align-items:start}\n.dsh-channels-cfg__field{display:flex;flex-direction:column;gap:4px;min-width:0}\n.dsh-channels-cfg__field--wide{grid-column:1/-1}\n.dsh-channels-cfg__labelRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}\n.dsh-channels-cfg__label{font-size:12px;color:var(--dsw-alias-label-secondary,#5c6169)}\n.dsh-channels-cfg__tag{display:inline-flex;align-items:center;padding:0 5px;border-radius:4px;border:1px dashed currentColor;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#8a8f98);white-space:nowrap}\n.dsh-channels-cfg__tag--restart{color:var(--dsw-alias-state-warning-primary,#a15c00)}\n.dsh-channels-cfg__tag--dirty{color:var(--dsw-alias-state-warning-primary,#a15c00);border-style:solid}\n.dsh-channels-cfg__help{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:11px;line-height:16px}\n.dsh-channels-cfg__reasonBad{color:var(--dsw-alias-state-danger-primary,#c62828)}\n.dsh-channels-cfg__input{box-sizing:border-box;width:100%;min-height:28px;padding:3px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;font:inherit;font-size:13px;outline:none}\n.dsh-channels-cfg__inputRow{display:flex;align-items:center;gap:8px;min-width:0}\n.dsh-channels-cfg__inputRow .dsh-channels-cfg__input{flex:1 1 auto;width:auto;min-width:0}\n.dsh-channels-cfg__btn--browse{flex:none}\n.dsh-channels-cfg__input:focus{border-color:var(--dsw-alias-state-business-primary,#0a67d3)}\n.dsh-channels-cfg__input--invalid{border-color:var(--dsw-alias-state-danger-primary,#c62828)}\nselect.dsh-channels-cfg__input{appearance:auto;cursor:pointer}\n.dsh-channels-cfg__check{display:inline-flex;align-items:center;gap:6px;min-height:28px;cursor:pointer;font-size:13px}\n.dsh-channels-cfg__checkbox{width:15px;height:15px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0a67d3);cursor:pointer}\n.dsh-channels-cfg__fieldError{color:var(--dsw-alias-state-danger-primary,#c62828);font-size:11px;line-height:16px}\n.dsh-channels-cfg__notice{display:flex;flex-direction:column;gap:2px;padding:8px 10px;border-radius:8px;font-size:12px;line-height:18px;overflow-wrap:anywhere}\n.dsh-channels-cfg__notice--ok{color:var(--dsw-alias-state-business-primary,#0a7d33);background:color-mix(in srgb,currentColor 8%,transparent)}\n.dsh-channels-cfg__notice--error{color:var(--dsw-alias-state-danger-primary,#c62828);background:color-mix(in srgb,currentColor 8%,transparent)}\n.dsh-channels-cfg__banner{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 10px;border-radius:8px;border:1px dashed var(--dsw-alias-state-warning-primary,#a15c00);color:var(--dsw-alias-state-warning-primary,#a15c00);font-size:12px;line-height:18px}\n.dsh-channels-cfg__footer{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:2px}\n.dsh-channels-cfg__spacer{flex:1}\n.dsh-channels-cfg__steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}\n.dsh-channels-cfg__step{display:flex;align-items:flex-start;gap:10px}\n.dsh-channels-cfg__stepIndex{display:inline-flex;align-items:center;justify-content:center;flex:none;width:20px;height:20px;border-radius:50%;border:1px solid currentColor;color:var(--dsw-alias-state-business-primary,#0a67d3);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}\n.dsh-channels-cfg__stepBody{display:flex;flex-direction:column;gap:2px;min-width:0}\n.dsh-channels-cfg__stepTitle{font-size:13px;font-weight:500}\n.dsh-channels-cfg__emptyState{display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px dashed var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:18px}\n.dsh-channels-cfg__readOnly{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 20px}\n.dsh-channels-cfg__kv{display:flex;flex-direction:column;gap:2px;min-width:0}\n.dsh-channels-cfg__kvKey{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:16px}\n.dsh-channels-cfg__kvValue{overflow-wrap:anywhere}\n.dsh-channels-cfg__mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}\n.dsh-channels-cfg__cred{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__cred .dsh-channels-cfg__input{flex:1 1 240px;min-width:0;width:auto}\n.dsh-channels-cfg__accessSection{display:flex;flex-direction:column;gap:6px}\n.dsh-channels-cfg__accessList{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}\n.dsh-channels-cfg__accessRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px}\n.dsh-channels-cfg__accessMain{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1 1 200px;overflow-wrap:anywhere}\n.dsh-channels-cfg__accessMeta{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:11px;line-height:16px;overflow-wrap:anywhere}\n.dsh-channels-cfg__accessActions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.dsh-channels-cfg__accessAdd{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__accessInput{flex:1 1 140px;min-width:0;width:auto}\n.dsh-channels-cfg__error{padding:10px 12px;border:1px solid var(--dsw-alias-state-danger-primary,#c62828);border-radius:10px;color:var(--dsw-alias-state-danger-primary,#c62828);font-size:12px;line-height:18px;display:flex;flex-direction:column;gap:8px}\n.dsh-channels-cfg__loading{padding:16px 0;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px}\n.dsh-channels-cfg__issues{margin:0;padding-left:18px}\n@container (max-width:560px){\n.dsh-channels-cfg__facts{grid-template-columns:repeat(2,minmax(0,1fr))}\n.dsh-channels-cfg__grid,.dsh-channels-cfg__readOnly{grid-template-columns:minmax(0,1fr)}\n.dsh-channels-cfg__tabs{flex-wrap:nowrap;overflow-x:auto}\n}\n`\n\n/** Append the panel stylesheet once (idempotent across HMR / remounts). */\nexport function installStyles(): () => void {\n const existing = document.querySelector(`style[${STYLE_ATTRIBUTE}]`)\n if (existing !== null) return () => {}\n const style = document.createElement('style')\n style.setAttribute(STYLE_ATTRIBUTE, '')\n style.textContent = CSS\n document.head.append(style)\n return () => {\n style.remove()\n }\n}\n","/**\n * dsh-channels client half: 设置 →「飞书渠道」分区。\n *\n * 只依赖平台核心的 `slots` 服务与 react(bundle 里 react 走平台模块表,\n * 其余全部内联)。页面数据来自 Host 半侧的 `/dsh-channels/*` 路由,因此\n * 不需要 typert 契约,也不 import 任何 DSH 客户端包。\n *\n * @module dsh-channels/client\n */\n\nimport { PANEL_COPY } from '../settings/contract.ts'\nimport { FeishuChannelsSection } from './panel/index.tsx'\nimport { installStyles } from './styles.ts'\n\n// 平台 client runtime 的最小结构声明:只声明本 bundle 用到的两个能力。\n// 与安装的 DSH Web 版本(0.2.x)的 slots 运行时 API 一致。\nexport interface ClientSlots {\n inject(slot: string, contribution: () => unknown): void\n register(\n declaration: {\n name: string\n id: string\n order?: number\n label: () => string\n },\n component: (props: { close?: () => void }) => unknown,\n ): () => void\n}\n\nexport interface ClientContext {\n slots: ClientSlots\n effect?(callback: () => void | (() => void), label?: string): void\n}\n\n/** Cordis client plugin name. */\nexport const name = 'dsh-channels'\n\n/** Required client services: the Slots registry is part of the web shell core. */\nexport const inject = ['slots']\n\n/** Section id and nav order (built-ins: account -10, general 0, models 10, plugins 15, agent-presets 20). */\nexport const SETTINGS_SECTION_ID = 'feishu-channels'\nexport const SETTINGS_SECTION_ORDER = 40\n\n/**\n * Mount the settings section and its stylesheet.\n * @param ctx - client plugin context carrying the slots registry.\n */\nexport function apply(ctx: ClientContext): void {\n if (typeof ctx.effect === 'function') {\n ctx.effect(() => installStyles(), 'dsh-channels: settings styles')\n } else {\n installStyles()\n }\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: SETTINGS_SECTION_ID,\n order: SETTINGS_SECTION_ORDER,\n label: () => PANEL_COPY.title,\n }, FeishuChannelsSection))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["messageOf","requestDirectoryPicker"],"sources":["../src/settings/contract.ts","../src/client/api.ts","../src/client/catalog.ts","../src/client/panel/shared.tsx","../src/client/panel/access-card.tsx","../src/client/panel/access-tab.tsx","../src/client/panel/advanced-tab.tsx","../src/client/panel/credentials-card.tsx","../src/client/panel/connection-tab.tsx","../src/client/panel/model-tab.tsx","../src/client/panel/sessions-tab.tsx","../src/client/panel/cleanup-card.tsx","../src/client/panel/workspace-tab.tsx","../src/client/panel/index.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Web 设置面板的线上契约:Host 半侧的路由/面板数据与 Browser 半侧的渲染共用。\n *\n * 本模块**没有运行时依赖**:Host 与 Client bundle 都直接内联它。因此这里只放\n * 常量、字段清单与可序列化的 wire 类型,不 import zod / node / DSH 内部包。\n *\n * @module dsh-channels/settings/contract\n */\n\n/** Same-origin routes the settings panel talks to. */\nexport const STATUS_ROUTE = '/dsh-channels/status'\nexport const PANEL_ROUTE = '/dsh-channels/panel'\nexport const CONFIG_ROUTE = '/dsh-channels/config'\nexport const CREDENTIALS_ROUTE = '/dsh-channels/credentials'\n/**\n * Connection lifecycle action (POST). While the channel runs it restarts the\n * long connection; while it is stopped it opens one — so the panel has exactly\n * one \"connect / reconnect\" button. Credentials are resolved again either way.\n */\nexport const RECONNECT_ROUTE = '/dsh-channels/reconnect'\n/** 模型/推理档位/预设选择器的宿主目录(provider → model → effort 级联刷新)。 */\nexport const CATALOG_ROUTE = '/dsh-channels/catalog'\n/** 手动执行一次过期目录清理(POST,无请求体)。 */\nexport const CLEANUP_ROUTE = '/dsh-channels/cleanup'\n/** 访问与授权只读快照(GET/HEAD):准入模式、管理员、群/用户白名单、待处理申请。 */\nexport const ACCESS_ROUTE = '/dsh-channels/access'\n/** 群白名单增删(POST/DELETE,body `{ chatId, name? }` / `{ chatId }`)。 */\nexport const ACCESS_GROUPS_ROUTE = '/dsh-channels/access/groups'\n/** 用户白名单增删(POST/DELETE,body `{ openId, name? }` / `{ openId }`)。 */\nexport const ACCESS_USERS_ROUTE = '/dsh-channels/access/users'\n/** 结算一条待处理授权申请(POST,body `{ token, decision }`)。 */\nexport const ACCESS_REQUESTS_ROUTE = '/dsh-channels/access/requests'\n/**\n * 打开宿主机的系统目录选择框(POST,无请求体),返回绝对路径。\n *\n * 只有宿主挂载了 `directoryPicker` 的 native 后端(桌面/本地 GUI 会话)才\n * 会注册实际选择逻辑;远程/无头会话的 browse 后端不可用,面板不下发\n * [`EditableFieldWire.browse`],保留手写输入。\n */\nexport const PICK_DIRECTORY_ROUTE = '/dsh-channels/pick-directory'\n\n/**\n * 可由 Web 面板覆盖的配置项。全部是 `src/config.ts` 里已存在的字段;\n * 覆盖值写进 `$DSH_HOME/dsh-channels/settings.json`,优先级高于 profile\n * 的 `cordis.patch.yml`(见 README「Web 设置面板」)。\n *\n * 刻意不包含 `appId` / `appSecret`:它们是**凭据引用名**,面板改为直接\n * 编辑引用背后的值(`PUT /dsh-channels/credentials`,经 DSH 凭据服务写入),\n * 因此不会落进覆盖层文件。\n *\n * `baseDir`(工作区根目录 = 模型/工具产生文件的落点)可覆盖,但面板只接受\n * **绝对路径**(`~` 会展开);已有 Session / 目录不迁移,只影响之后新建的\n * 会话。校验与保存时建目录见 `src/workspaces.ts`。\n */\nexport const EDITABLE_KEYS = [\n // 工作区\n 'baseDir',\n 'workspaceRetention',\n 'workspaceRetentionAuto',\n // 行为\n 'dmMode',\n 'requireMention',\n 'threadFallback',\n 'welcomeOnboarding',\n 'allowPermissionSwitch',\n // 准入与授权(管理员/准入模式/申请有效期;白名单由「访问与授权」卡片管理)\n 'adminOpenId',\n 'accessMode',\n 'authRequestTtlDays',\n // 内容\n 'acceptAttachments',\n // 模型\n 'provider',\n 'model',\n 'reasoningEffort',\n 'agentPreset',\n // 流式卡片\n 'refreshMs',\n 'statusRefreshMs',\n 'loggerLevel',\n] as const\n\nexport type EditableKey = (typeof EDITABLE_KEYS)[number]\n\n/**\n * 只有重连飞书长连接后才生效的字段:它们在 `FeishuService.start()` 建连时\n * 被交给官方 SDK 或流式器,运行中修改不会影响已建立的连接。\n */\nexport const RESTART_KEYS = [\n 'dmMode',\n 'requireMention',\n 'refreshMs',\n 'statusRefreshMs',\n 'loggerLevel',\n] as const satisfies readonly EditableKey[]\n\nexport type RestartKey = (typeof RESTART_KEYS)[number]\n\n/** Render kinds the generic panel form understands. */\nexport type FieldKind = 'boolean' | 'number' | 'text' | 'select'\n\n/**\n * Field groups partition the editable form. Each group is rendered inside one\n * panel tab (see {@link TAB_FIELD_GROUPS}); the Host is the source of truth for\n * which key belongs to which group.\n */\nexport type FieldGroup = 'workspace' | 'behavior' | 'content' | 'model' | 'streaming' | 'access'\n\n/** One selectable value rendered by the generic form. */\nexport interface FieldOptionWire {\n value: string\n label: string\n}\n\n/** One editable row, fully described by the Host so the client stays schema-free. */\nexport interface EditableFieldWire {\n key: EditableKey\n label: string\n kind: FieldKind\n group: FieldGroup\n help?: string\n options?: readonly FieldOptionWire[]\n /** Selector is not editable yet (e.g. pick the provider before the model). */\n disabled?: boolean\n min?: number\n max?: number\n step?: number\n placeholder?: string\n /** Render across the whole form width (long paths). */\n wide?: boolean\n /**\n * `directory`:给这个 text 字段加一个「选择目录…」按钮,点击后由 Host\n * 打开宿主机系统目录选择框并把绝对路径写回草稿。仅当宿主提供 native\n * 目录选择器时下发;缺省(含 browse 后端)保持纯手写输入。\n */\n browse?: 'directory'\n /** 保存后需重连飞书通道才生效。 */\n restartRequired: boolean\n}\n\n/**\n * One selector group resolved from live host services. `selectable: false`\n * means the host could not enumerate values (service absent / lookup failed),\n * so the field falls back to the free-text control with the same key.\n */\nexport interface SelectCatalogWire {\n selectable: boolean\n /** Rendered in order; the leading `''` entry is the inherit/clear choice. */\n options: FieldOptionWire[]\n disabled?: boolean\n /** Host-side explanation shown instead of the static field help. */\n help?: string\n}\n\n/** `GET /dsh-channels/catalog` payload: the model-facing selector catalog. */\nexport interface CatalogWire {\n provider: SelectCatalogWire\n model: SelectCatalogWire\n reasoningEffort: SelectCatalogWire\n agentPreset: SelectCatalogWire\n /** Deployment default selection, when the host exposes `agentDefaultModel`. */\n deploymentDefault?: {\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n /** Roster default preset id, when the host exposes the preset roster. */\n defaultPresetId?: string\n}\n\n/** Values the panel may submit. Empty strings for optional text fields clear the override. */\nexport type ConfigValue = string | number | boolean\nexport type EditableValues = Partial<Record<EditableKey, ConfigValue>>\n\n/** Read-only facts shown in the advanced card. */\nexport interface ReadOnlyFieldWire {\n key: string\n label: string\n value: string\n help?: string\n}\n\n/** One credential reference plus its Host-side state (values never cross the wire). */\nexport interface CredentialRefWire {\n ref: string\n configured: boolean\n /** false when the mounted credentials provider cannot store values. */\n writable: boolean\n /** Where the configured value comes from (e.g. `file`), when the provider reports it. */\n source?: string\n}\n\n/** The two credential slots the panel may write. */\nexport type CredentialField = 'appId' | 'appSecret'\n\n/** Official-SDK WebSocket lifecycle snapshot (structural copy, no SDK import on the wire). */\nexport interface ConnectionWire {\n state: string\n lastConnectTime?: number\n nextConnectTime?: number\n reconnectAttempts: number\n}\n\n/**\n * The panel-facing lifecycle phase, derived by the Host from configuration\n * readiness and runtime state. Enabling the plugin starts in `unconfigured`\n * (or `idle` when credentials already resolve) and never forces a connection\n * before the Feishu application credentials are complete.\n */\nexport type ChannelPhaseWire =\n /** Credentials are incomplete: the plugin is loaded but deliberately not connected. */\n | 'unconfigured'\n /** Credentials are complete, no connection is open yet. */\n | 'idle'\n /** A connection attempt / reconnect is in flight. */\n | 'connecting'\n /** The Feishu long connection is open. */\n | 'connected'\n /** The last connection attempt failed; the reason is in `lastStartError`. */\n | 'error'\n\n/** Runtime connection/liveness facts. */\nexport interface ChannelStatusWire {\n running: boolean\n /**\n * Whether the Feishu application credentials are complete. When false the\n * plugin stays loaded (settings panel usable) but opens no long connection.\n */\n configured: boolean\n /** Display phase derived from `configured` / `running` / `lastStartError`. */\n phase: ChannelPhaseWire\n /** Human-readable config items that are still missing (names only, never values). */\n missingConfig: string[]\n level: 'healthy' | 'degraded' | 'unhealthy'\n startedAt?: number\n uptimeMs?: number\n connection?: ConnectionWire\n botOpenIdTail?: string\n routeCount: number\n liveAgentCount: number\n inflightTurnCount: number\n /** 最近一次连接失败的原因(成功后清空)。 */\n lastStartError?: string\n /** 健康判定的原因说明(空数组 = 健康);由 Host 的 health 折叠给出。 */\n reasons: string[]\n /** 面板显示的所有键值对来源版本。 */\n version: string\n /** Web 覆盖文件绝对路径(用户可直接编辑)。 */\n overlayPath: string\n /** 正在连接或重连时为 true,此时面板的写操作按钮禁用。 */\n busy: boolean\n}\n\n/** The whole editable configuration surface, with provenance. */\nexport interface PanelConfigWire {\n /** profile `cordis.patch.yml` 提供的值(覆盖层之下)。 */\n base: Record<string, ConfigValue | undefined>\n /** 合并后的生效值。 */\n effective: Record<string, ConfigValue | undefined>\n /** 已写入 Web 覆盖层的值。 */\n overlay: EditableValues\n overriddenKeys: EditableKey[]\n /** 与当前运行中连接不一致、需重连才生效的字段。 */\n restartPendingKeys: EditableKey[]\n fields: EditableFieldWire[]\n readOnly: ReadOnlyFieldWire[]\n credentials: {\n appId: CredentialRefWire\n appSecret: CredentialRefWire\n }\n}\n\n/** One authorized group (the first of the two admission gates). */\nexport interface AccessGroupWire {\n chatId: string\n /** Human-readable group name captured at authorization time. */\n name?: string\n /** Which authorization entry produced the grant. */\n source: 'command' | 'card' | 'panel'\n /** Epoch ms when it was authorized. */\n addedAt: number\n}\n\n/** One authorized user `open_id` (global across every authorized group). */\nexport interface AccessUserWire {\n openId: string\n /** Display name captured at authorization time. */\n name?: string\n /** Which authorization entry produced the grant. */\n source: 'command' | 'card' | 'panel'\n /** Epoch ms when it was authorized. */\n addedAt: number\n}\n\n/** One pending authorization request the panel may settle (same path as the DM card). */\nexport interface AccessRequestWire {\n /** Opaque token; never derived from the target id. */\n token: string\n kind: 'group' | 'user'\n /** `chat_id` for a group request, `open_id` for a user request. */\n targetId: string\n /** Display name of the requested target (group name / user name), when known. */\n name?: string\n /** Chat the request originated from, when applicable. */\n chatId?: string\n /** Group name captured for the card. */\n groupName?: string\n /** Open_id of the user who triggered a user request. */\n requester?: string\n /** Epoch ms when the request was created. */\n createdAt: number\n /** Epoch ms after which the request can no longer be decided. */\n expiresAt: number\n}\n\n/**\n * `GET /dsh-channels/access` payload: admission mode, the administrator\n * reference, both whitelists, and the still-pending authorization requests.\n */\nexport interface AccessWire {\n mode: 'open' | 'guarded'\n admin: {\n openId?: string\n /** false while guarded mode would refuse every conversation (fail closed). */\n configured: boolean\n }\n /**\n * Whether the persisted whitelists were loaded. While the channel holds no\n * connection the plugin storage domain is closed, so the lists are unknown\n * (not empty) and the panel must not offer mutations.\n */\n loaded: boolean\n groups: AccessGroupWire[]\n users: AccessUserWire[]\n requests: AccessRequestWire[]\n}\n\n/** Write responses for every `/dsh-channels/access*` mutation. */\nexport type AccessMutationResponse =\n | { ok: true; access: AccessWire }\n | { ok: false; error: string }\n\n/** `GET /dsh-channels/panel` payload. */\nexport interface PanelWire {\n status: ChannelStatusWire\n config: PanelConfigWire\n cleanup: CleanupPreviewWire\n /** 访问与授权卡片的数据(准入模式、管理员、白名单、待处理申请)。 */\n access: AccessWire\n}\n\n/**\n * What the workspace-retention cleanup would delete right now. Counts are\n * zero and `error` is set when the Host cannot compute the preview (for\n * example the cleanup seam is unavailable); the panel stays usable.\n */\nexport interface CleanupPreviewWire {\n /** Effective `workspaceRetention` value (`off` / `7d` / `30d` / `90d` / `180d`). */\n retention: string\n /** Retention threshold in milliseconds; absent when `retention` is `off`. */\n thresholdMs?: number\n /** Periodic cleanup runs at startup and once per day. */\n auto: boolean\n /** Plugin-created workspaces under the current baseDir (user projects). */\n workspaces: number\n /** Directories older than the threshold (legacy topic workspaces + per-topic dirs). */\n candidates: number\n /** Total bytes currently occupied by the candidates. */\n candidateBytes: number\n /** Why the preview could not be computed (counts are zero then). */\n error?: string\n}\n\n/** Outcome of one manual cleanup run (partial failures do not abort the run). */\nexport interface CleanupRunWire {\n ok: boolean\n deletedWorkspaces: number\n /** Per-topic working directories removed (current layout). */\n deletedSessionDirs: number\n retiredRoutes: number\n freedBytes: number\n /** One `path: message` line per directory that could not be deleted. */\n failures: string[]\n error?: string\n}\n\n/** `POST /dsh-channels/cleanup` response. */\nexport type CleanupResponse =\n | { ok: true; panel: PanelWire; result: CleanupRunWire }\n | { ok: false; error: string }\n\n/**\n * `POST /dsh-channels/pick-directory` response. `canceled` is a normal\n * outcome (the operator dismissed the OS dialog), not an error; `path` is an\n * absolute directory the Host's native chooser returned.\n */\nexport type PickDirectoryResponse =\n | { ok: true; path: string }\n | { ok: true; canceled: true }\n | { ok: false; error: string }\n\n/** `PUT /dsh-channels/config` body. */\nexport interface SaveConfigRequest {\n values: EditableValues\n}\n\n/** Per-field validation failure. */\nexport interface FieldIssueWire {\n key: string\n message: string\n}\n\n/** `PUT /dsh-channels/config` response. */\nexport type SaveConfigResponse =\n | { ok: true; panel: PanelWire; clearedKeys: EditableKey[] }\n | { ok: false; error: string; issues?: FieldIssueWire[] }\n\n/** `POST /dsh-channels/reconnect` response. */\nexport type ReconnectResponse =\n | { ok: true; panel: PanelWire }\n /** `unconfigured` tells the panel to route the user to the credentials form. */\n | { ok: false; error: string; unconfigured?: boolean }\n\n/** `DELETE /dsh-channels/config` response. */\nexport type ClearConfigResponse =\n | { ok: true; panel: PanelWire }\n | { ok: false; error: string }\n\n/** `PUT /dsh-channels/credentials` body: only provided, non-empty values are written. */\nexport interface SaveCredentialsRequest {\n appId?: string\n appSecret?: string\n}\n\n/** `DELETE /dsh-channels/credentials` body. */\nexport interface ClearCredentialRequest {\n field: CredentialField\n}\n\n/**\n * How a saved credential reached the running channel.\n *\n * - `auto`: the channel was stopped and connected immediately after the save.\n * - `pending`: the channel is running; reconnect for the value to take effect.\n * - `not-needed`: removed while stopped, nothing to reconnect.\n * - `unconfigured`: saved, but the other credential of the pair is still\n * missing — the plugin keeps waiting (no connection attempt) until it is\n * configured.\n * - `failed`: the pair is complete and a connection was attempted, but it\n * failed; the value is stored and `autoReconnectError` explains why.\n */\nexport type CredentialReconnectState = 'auto' | 'pending' | 'not-needed' | 'unconfigured' | 'failed'\n\nexport interface CredentialsSavedPayload {\n panel: PanelWire\n reconnect: CredentialReconnectState\n /** Set when the automatic reconnect after a save failed (the value is still saved). */\n autoReconnectError?: string\n}\n\n/** Credential write/remove responses (values never appear in either direction). */\nexport type SaveCredentialsResponse =\n | ({ ok: true } & CredentialsSavedPayload)\n | { ok: false; error: string; issues?: FieldIssueWire[] }\n\nexport type ClearCredentialResponse =\n | ({ ok: true } & CredentialsSavedPayload)\n | { ok: false; error: string; issues?: FieldIssueWire[] }\n\n/**\n * Top-level panel tabs, in render order. The panel shows the connection state\n * header above every tab, so switching tabs never hides whether the bridge is\n * connected.\n */\nexport const PANEL_TABS = [\n { id: 'connection', label: '连接' },\n { id: 'sessions', label: '会话与行为' },\n { id: 'model', label: '模型' },\n { id: 'workspace', label: '工作区' },\n { id: 'access', label: '访问授权' },\n { id: 'advanced', label: '高级' },\n] as const\n\nexport type PanelTabId = (typeof PANEL_TABS)[number]['id']\n\n/** Which editable field groups each tab renders (the tabs own one or two). */\nexport const TAB_FIELD_GROUPS: Readonly<Record<PanelTabId, readonly FieldGroup[]>> = {\n connection: [],\n sessions: ['behavior', 'content'],\n model: ['model'],\n workspace: ['workspace'],\n access: ['access'],\n advanced: ['streaming'],\n}\n\n/** Section heading for one field group inside its tab. */\nexport const FIELD_GROUP_LABELS: Readonly<Record<FieldGroup, string>> = {\n workspace: '工作区与文件',\n behavior: '行为',\n content: '内容与附件',\n model: '模型与推理',\n streaming: '流式卡片与日志',\n access: '准入与授权',\n}\n\n/** Display labels for the panel header/cards (kept on the wire so the client owns no copy). */\nexport const PANEL_COPY = {\n title: '飞书渠道',\n description: '飞书长连接、应用凭据与渠道配置。凭据只写入 DSH 凭据库且不回显;标注「需重连」的项在连接建立时读取。',\n // ---- lifecycle / status -------------------------------------------------\n statusCard: '连接状态',\n phaseUnconfigured: '待配置',\n phaseIdle: '未连接',\n phaseConnecting: '连接中',\n phaseConnected: '已连接',\n phaseError: '连接失败',\n configuredBadge: '凭据已配置',\n unconfiguredBadge: '凭据未配置',\n statusSummaryConnected: '飞书长连接已建立,机器人可以收发消息。',\n statusSummaryUnconfigured: '尚未配置飞书应用凭据:插件已加载,但不会连接飞书。填写凭据并保存后会自动连接。',\n statusSummaryIdle: '飞书应用凭据已就绪,但当前没有建立长连接。',\n statusSummaryConnecting: '正在连接飞书长连接…',\n statusSummaryError: '最近一次连接飞书失败,请检查凭据与网络后重试。',\n lastError: '最近一次连接失败',\n missingConfig: '缺少配置',\n statusHintUnconfigured: '配置完成后插件会自动连接;也可以稍后在本页手动连接。',\n connectionInfo: '连接信息',\n connectionStateLabel: '飞书长连接',\n botOpenId: 'Bot open_id',\n uptime: '运行时长',\n routeCount: '话题路由',\n liveAgentCount: '活动会话',\n inflightTurnCount: '在途回复',\n healthReasons: '健康判定',\n unknownValue: '—',\n connect: '连接飞书',\n connecting: '连接中…',\n // ---- onboarding ---------------------------------------------------------\n onboardingTitle: '开始使用飞书渠道',\n onboardingIntro: '只需三步:在飞书开放平台创建自建应用,把 App ID / App Secret 填入本页,保存后自动连接。',\n onboardingStep1Title: '创建飞书自建应用',\n onboardingStep1Text: '在飞书开放平台创建企业自建应用,开启机器人能力;事件订阅选择「长连接」,订阅 im.message.receive_v1(接收消息)。',\n onboardingStep2Title: '填写应用凭据',\n onboardingStep2Text: '把应用的 App ID(cli_ 开头)与 App Secret 填入下方「飞书应用凭据」,值会写入 DSH 凭据库。',\n onboardingStep3Title: '保存并等待连接',\n onboardingStep3Text: '两项凭据都保存后插件会自动连接;状态变为「已连接」即可在飞书里私聊机器人。',\n onboardingDocsHint: '开放平台地址:open.feishu.cn/app;应用需要开通 im:message、im:message:send_as_bot 等消息权限。',\n // ---- credentials --------------------------------------------------------\n credentialsCard: '飞书应用凭据',\n credentialsCardHelp: '凭据值经 DSH 凭据服务写入本机凭据库;面板只显示「已配置 / 未配置」,永不回显已存值。两项都配置后才会连接飞书。',\n appIdLabel: '飞书 App ID',\n appSecretLabel: '飞书 App Secret',\n credentialsRef: '凭据引用',\n saveCredentials: '保存凭据',\n savingCredentials: '保存中…',\n credentialSaved: '凭据已保存',\n credentialSavedReconnected: '凭据已保存,飞书已连接',\n credentialSavedPending: '凭据已保存,重启飞书连接后生效',\n credentialSavedUnconfigured: '凭据已保存;还缺少另一项凭据,补齐后会自动连接',\n credentialAutoConnectFailed: '凭据已保存,但自动连接失败',\n credentialRemoved: '已移除存储的凭据',\n credentialRemovedPending: '已移除存储的凭据,重启飞书连接后生效',\n removeCredential: '移除已存值',\n removingCredential: '移除中…',\n credentialConfigured: '已配置',\n credentialMissing: '未配置',\n credentialReadOnly: '当前凭据存储不可写',\n appIdPlaceholder: '输入 App ID(cli_…)',\n appSecretPlaceholder: '输入 App Secret',\n credentialsPendingHint: '填写后点「保存凭据」;两项齐全时插件会自动连接飞书。',\n // ---- tabs / forms -------------------------------------------------------\n connectionTabHelp: '连接状态与飞书应用凭据。凭据不完整时不会连接飞书。',\n sessionsTabHelp: '私聊、群聊触发方式与入站内容;「需重连」项在下次连接时读取。',\n modelTabHelp: '飞书会话使用的模型服务商、模型、推理档位与 Agent 预设;下拉选项来自宿主机实时目录,保存后对新会话生效。',\n workspaceTabHelp: '每个用户一个工作区(模型/工具产生文件的落点),每个话题在其中拥有独立工作目录;只影响之后新建的话题。',\n accessTabHelp: '准入采用 fail-closed:管理员不受白名单限制,非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。',\n advancedTabHelp: '流式刷新等低层参数、Web 覆盖文件与只读信息。',\n save: '保存更改',\n saving: '保存中…',\n saveHintDirty: '只保存改动过的字段;标注「需重连」的项在下次连接时生效。',\n discardTab: '放弃本页更改',\n saveHintClean: '没有未保存的更改。',\n dirtyCount: '未保存项',\n browseDirectory: '选择目录…',\n browsingDirectory: '选择中…',\n directoryPicked: '已选择目录',\n directoryPickFailed: '无法打开系统目录选择器',\n saved: '已保存',\n cleared: '已清除 Web 覆盖,恢复为 profile 配置',\n clear: '清除 Web 覆盖',\n clearing: '清除中…',\n noOverrides: '当前没有 Web 覆盖',\n overridden: 'Web 覆盖',\n inherited: '继承 profile',\n restartTag: '需重连',\n restartBanner: '以下配置需重启飞书连接后生效:',\n restartWarning: '重启会断开当前飞书长连接并中断进行中的回复,请确认空闲时操作。',\n reconnect: '重启飞书连接',\n reconnecting: '重连中…',\n refresh: '刷新状态',\n retry: '重试',\n pollFailed: '状态刷新失败',\n connectDone: '已连接飞书',\n reconnectDone: '已重启飞书连接',\n saveInvalid: '请先修正标红的配置项',\n enabled: '已启用',\n disabled: '已关闭',\n credentialRemoveConfirm: '确定移除已存储的凭据值?移除后不会回显,需要时须重新填写。',\n accessNotLoaded: '飞书连接建立后加载授权列表',\n loading: '加载中…',\n loadFailed: '设置面板加载失败',\n catalogFailed: '模型目录加载失败,已退回手填。',\n catalogLoading: '正在读取宿主机选项…',\n catalogUnavailable: '宿主机未提供可选项,请手填。',\n version: '插件版本',\n overlayPath: '覆盖文件',\n overlayPathHelp: '可以直接编辑该 JSON 文件;面板保存会整体重写它,「清除 Web 覆盖」会删除它并回落到 profile 配置。',\n advancedCard: '只读与高级信息',\n advancedHelp: '这些值来自 profile 配置,不能在面板里编辑。',\n readOnlyCard: '部署级参数',\n // ---- model --------------------------------------------------------------\n // ---- cleanup ------------------------------------------------------------\n cleanupCard: '目录清理',\n cleanupCardHelp: '删除超过保留期且不再活跃的飞书话题目录(以及旧版本按话题建立的工作区目录);用户工作区本身、DSH 会话日志都不会删除。删除不可恢复:相关旧话题会失效并提示已过期。',\n cleanupRetention: '保留期',\n cleanupAutoLabel: '自动清理',\n cleanupCandidates: '可清理目录',\n cleanupWorkspaces: '插件工作区',\n cleanupCandidateBytes: '预计释放空间',\n cleanupRetentionOff: '关闭(不删除)',\n cleanupAutoOn: '已开启(启动时 + 每天一次)',\n cleanupAutoOff: '已关闭(仅手动执行)',\n cleanupAutoInactive: '未执行(保留期为关闭)',\n cleanupNow: '立即清理',\n cleanupRunning: '清理中…',\n cleanupConfirm: '确定清理过期目录吗?该操作不可恢复,会删除这些目录内的全部文件(包括 attachments/ 附件)。',\n cleanupConfirmHint: '删除前会再次确认;保留期为「关闭」或没有过期目录时按钮不可用。',\n cleanupDisabledOff: '保留策略为「关闭」:请先在上方「过期目录保留期」中选择保留天数。',\n cleanupDisabledEmpty: '没有超过保留期的过期目录。',\n cleanupPreviewFailed: '无法读取清理预览',\n cleanupDone: '清理完成',\n cleanupFailed: '清理失败',\n cleanupDeleted: '已删除工作区',\n cleanupDeletedDirs: '话题目录',\n cleanupRetired: '已清理话题路由',\n cleanupFreed: '释放空间',\n cleanupFailures: '以下目录未能删除:',\n // ---- access -------------------------------------------------------------\n accessCard: '访问与授权',\n accessCardHelp: '准入采用 fail-closed:管理员不受白名单限制(私聊、任意群聊都可直接对话);非管理员私聊会被拒绝,群聊需要「群已授权 + 用户在白名单」两层同时命中。管理员与准入模式在上方「准入与授权」中编辑;私聊机器人发送 /userid 获取自己的 open_id。',\n accessModeOpenWarning: '当前准入模式为开放(open):应用可用范围内的任何人都能开启对话。该模式不安全,仅建议调试时使用。',\n accessAdminMissingWarning: '当前准入=guarded 但未配置管理员,所有对话都会被拒绝。请在上方「准入与授权」填写管理员 open_id。',\n accessGroupsTitle: '已授权群',\n accessGroupsEmpty: '尚未授权任何群。机器人被拉进群后,管理员会收到带 chat_id 的群信息卡;也可在群内发送 /groupid 获取。',\n accessUsersTitle: '已授权用户',\n accessUsersEmpty: '尚未授权任何用户。已授权用户可在所有已授权群里与机器人对话(私聊仍只开给管理员)。',\n accessRequestsTitle: '待处理申请',\n accessRequestsEmpty: '没有待处理的授权申请。',\n accessGroupChatIdPlaceholder: '群 chat_id(oc_…)',\n accessGroupNamePlaceholder: '群名(可选)',\n accessUserOpenIdPlaceholder: '用户 open_id(ou_…)',\n accessUserNamePlaceholder: '昵称(可选)',\n accessAddGroup: '添加群',\n accessAddUser: '添加用户',\n accessRemove: '移除',\n accessAllow: '授权',\n accessDeny: '忽略',\n accessWorking: '处理中…',\n accessSourceCommand: '对话命令',\n accessSourceCard: '授权卡',\n accessSourcePanel: '设置面板',\n accessRequestGroup: '群申请',\n accessRequestUser: '用户申请',\n accessRequestRequester: '申请人',\n accessRequestExpires: '有效期至',\n accessGroupAdded: '已添加群授权',\n accessGroupRemoved: '已移除群授权',\n accessUserAdded: '已添加用户授权',\n accessUserRemoved: '已移除用户授权',\n accessRequestAllowed: '已授权',\n accessRequestDenied: '已忽略',\n accessHints: '三种授权入口:群内 /auth 命令、入群与用户授权卡、本面板。撤销立即生效;白名单只约束非管理员(管理员在任何私聊/群聊都直接放行),非管理员需要群和用户两层同时命中。',\n accessChatIdHint: 'chat_id 可从入群群信息卡或群内 /groupid 获取;open_id 可私聊机器人发送 /userid 获取。',\n} as const\n","/**\n * Same-origin HTTP client for the settings panel. No typert contract: the\n * Host half registers plain exact routes and this module fetches them.\n *\n * @module dsh-channels/client/api\n */\n\nimport {\n ACCESS_GROUPS_ROUTE,\n ACCESS_REQUESTS_ROUTE,\n ACCESS_ROUTE,\n ACCESS_USERS_ROUTE,\n CATALOG_ROUTE,\n CLEANUP_ROUTE,\n CONFIG_ROUTE,\n CREDENTIALS_ROUTE,\n PANEL_ROUTE,\n PICK_DIRECTORY_ROUTE,\n RECONNECT_ROUTE,\n STATUS_ROUTE,\n} from '../settings/contract.ts'\nimport type {\n AccessMutationResponse,\n AccessWire,\n CatalogWire,\n ChannelStatusWire,\n CleanupResponse,\n ClearConfigResponse,\n ClearCredentialResponse,\n CredentialField,\n EditableValues,\n PanelWire,\n PickDirectoryResponse,\n ReconnectResponse,\n SaveConfigResponse,\n SaveCredentialsRequest,\n SaveCredentialsResponse,\n} from '../settings/contract.ts'\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Pull the JSON `error` field out of a failed response body. */\nfunction errorFromBody(status: number, text: string): string {\n try {\n const parsed = JSON.parse(text) as { error?: unknown }\n if (typeof parsed.error === 'string' && parsed.error !== '') return parsed.error\n } catch {\n // Not JSON — fall through to the HTTP status.\n }\n return `HTTP ${status}`\n}\n\nasync function request(path: string, init?: RequestInit): Promise<Response> {\n try {\n return await fetch(path, {\n cache: 'no-store',\n credentials: 'same-origin',\n ...init,\n headers: { accept: 'application/json', ...(init?.headers ?? {}) },\n })\n } catch (error) {\n throw new Error(`无法连接设置接口:${messageOf(error)}`)\n }\n}\n\nasync function readJson<T>(response: Response): Promise<T> {\n const text = await response.text()\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as T\n}\n\n/** Full panel payload (status + editable config + read-only facts). */\nexport async function fetchPanel(): Promise<PanelWire> {\n return readJson<PanelWire>(await request(PANEL_ROUTE))\n}\n\n/** Status-only payload polled while the page is open. */\nexport async function fetchStatus(): Promise<ChannelStatusWire> {\n return readJson<ChannelStatusWire>(await request(STATUS_ROUTE))\n}\n\n/**\n * Model-selector catalog for one route: providers, the provider's models, the\n * model's reasoning efforts, and the preset roster. The client re-queries it\n * whenever the draft provider/model changes.\n */\nexport async function fetchCatalog(provider?: string, model?: string): Promise<CatalogWire> {\n const params = new URLSearchParams()\n if (provider !== undefined && provider !== '') params.set('provider', provider)\n if (model !== undefined && model !== '') params.set('model', model)\n const query = params.toString()\n return readJson<CatalogWire>(await request(query === '' ? CATALOG_ROUTE : `${CATALOG_ROUTE}?${query}`))\n}\n\n/** Persist a partial edit. Validation failures come back as `{ok:false}`. */\nexport async function saveConfig(values: EditableValues): Promise<SaveConfigResponse> {\n const response = await request(CONFIG_ROUTE, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ values }),\n })\n const text = await response.text()\n if (response.status === 400) {\n // Validation/IO refusal: the body is already the SaveConfigResponse.\n try {\n return JSON.parse(text) as SaveConfigResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as SaveConfigResponse\n}\n\n/** Drop every Web override and return the refreshed panel. */\nexport async function clearConfig(): Promise<ClearConfigResponse> {\n const response = await request(CONFIG_ROUTE, { method: 'DELETE' })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as ClearConfigResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as ClearConfigResponse\n}\n\n/** Reopen the Feishu long connection (interrupts in-flight replies). */\nexport async function reconnect(): Promise<ReconnectResponse> {\n const response = await request(RECONNECT_ROUTE, { method: 'POST' })\n const text = await response.text()\n if (!response.ok) {\n try {\n return JSON.parse(text) as ReconnectResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n return JSON.parse(text) as ReconnectResponse\n}\n\n/**\n * Run one workspace cleanup. No request body: the Host derives the candidate\n * set from the effective retention config. Irreversible, so the caller asks\n * for confirmation first; partial failures come back inside `result.failures`.\n */\nexport async function runCleanup(): Promise<CleanupResponse> {\n const response = await request(CLEANUP_ROUTE, { method: 'POST' })\n const text = await response.text()\n if (!response.ok) {\n try {\n return JSON.parse(text) as CleanupResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n return JSON.parse(text) as CleanupResponse\n}\n\n/**\n * Ask the Host to open its native directory chooser (the panel's「选择目录…」\n * button). The request stays open while the OS dialog does; a cancel comes back\n * as `{ ok: true, canceled: true }`, not as an error.\n */\nexport async function pickDirectory(): Promise<PickDirectoryResponse> {\n const response = await request(PICK_DIRECTORY_ROUTE, { method: 'POST' })\n const text = await response.text()\n if (!response.ok) {\n try {\n return JSON.parse(text) as PickDirectoryResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n return JSON.parse(text) as PickDirectoryResponse\n}\n\n/** Store provided credential values (the request/response never carry stored values). */\nexport async function saveCredentials(values: SaveCredentialsRequest): Promise<SaveCredentialsResponse> {\n const response = await request(CREDENTIALS_ROUTE, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(values),\n })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as SaveCredentialsResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as SaveCredentialsResponse\n}\n\n/** Remove the stored value behind one credential reference. */\nexport async function clearCredential(field: CredentialField): Promise<ClearCredentialResponse> {\n const response = await request(CREDENTIALS_ROUTE, {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ field }),\n })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as ClearCredentialResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as ClearCredentialResponse\n}\n\n/** Admission snapshot: mode, administrator, whitelists, pending requests. */\nexport async function fetchAccess(): Promise<AccessWire> {\n return readJson<AccessWire>(await request(ACCESS_ROUTE))\n}\n\n/** One access write: a 400 body already is the wire union; others throw. */\nasync function mutateAccess(\n path: string,\n method: 'POST' | 'DELETE',\n body: unknown,\n): Promise<AccessMutationResponse> {\n const response = await request(path, {\n method,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n const text = await response.text()\n if (response.status === 400) {\n try {\n return JSON.parse(text) as AccessMutationResponse\n } catch {\n return { ok: false, error: errorFromBody(response.status, text) }\n }\n }\n if (!response.ok) throw new Error(errorFromBody(response.status, text))\n return JSON.parse(text) as AccessMutationResponse\n}\n\n/** Authorize one group; resolves with the refreshed access snapshot. */\nexport async function addAccessGroup(chatId: string, name?: string): Promise<AccessMutationResponse> {\n return mutateAccess(\n ACCESS_GROUPS_ROUTE,\n 'POST',\n name === undefined || name === '' ? { chatId } : { chatId, name },\n )\n}\n\n/** Revoke one group authorization (takes effect on the next message). */\nexport async function removeAccessGroup(chatId: string): Promise<AccessMutationResponse> {\n return mutateAccess(ACCESS_GROUPS_ROUTE, 'DELETE', { chatId })\n}\n\n/** Authorize one user open_id (global across authorized groups). */\nexport async function addAccessUser(openId: string, name?: string): Promise<AccessMutationResponse> {\n return mutateAccess(\n ACCESS_USERS_ROUTE,\n 'POST',\n name === undefined || name === '' ? { openId } : { openId, name },\n )\n}\n\n/** Revoke one user authorization (takes effect on the next message). */\nexport async function removeAccessUser(openId: string): Promise<AccessMutationResponse> {\n return mutateAccess(ACCESS_USERS_ROUTE, 'DELETE', { openId })\n}\n\n/** Settle one pending authorization request (same resolver as the Feishu card). */\nexport async function resolveAccessRequest(\n token: string,\n decision: 'allow' | 'deny',\n): Promise<AccessMutationResponse> {\n return mutateAccess(ACCESS_REQUESTS_ROUTE, 'POST', { token, decision })\n}\n","/**\n * 浏览器半区的设置面板选择器目录折叠:把 Host 下发的字段描述与实时目录\n * (`/dsh-channels/catalog`)合成通用表单需要的 select 描述。\n *\n * 拆成独立模块(不含 JSX/React)以便单测;`panel/index.tsx` 只负责串起状态与渲染。\n *\n * @module dsh-channels/client/catalog\n */\n\nimport { PANEL_COPY } from '../settings/contract.ts'\nimport type {\n CatalogWire,\n EditableFieldWire,\n EditableKey,\n FieldOptionWire,\n SelectCatalogWire,\n} from '../settings/contract.ts'\n\n/** Keys whose options come from the Host selector catalog. */\nexport const CATALOG_KEYS = ['provider', 'model', 'reasoningEffort', 'agentPreset'] as const\nexport type CatalogKey = (typeof CATALOG_KEYS)[number]\n\nexport function isCatalogKey(key: EditableKey): key is CatalogKey {\n return (CATALOG_KEYS as readonly string[]).includes(key)\n}\n\n/** Read one selector group back out of the Host-resolved panel fields. */\nexport function selectorOf(field: EditableFieldWire | undefined): SelectCatalogWire {\n if (field?.kind !== 'select') return { selectable: false, options: [] }\n return {\n selectable: true,\n options: field.options === undefined ? [] : [...field.options],\n ...(field.disabled === true ? { disabled: true } : {}),\n ...(field.help === undefined ? {} : { help: field.help }),\n }\n}\n\n/** Seed the client catalog from the options the Host already resolved for the panel. */\nexport function catalogFromFields(fields: readonly EditableFieldWire[]): CatalogWire | null {\n const byKey = new Map(fields.map(field => [field.key, field]))\n const catalog: CatalogWire = {\n provider: selectorOf(byKey.get('provider')),\n model: selectorOf(byKey.get('model')),\n reasoningEffort: selectorOf(byKey.get('reasoningEffort')),\n agentPreset: selectorOf(byKey.get('agentPreset')),\n }\n return CATALOG_KEYS.every(key => !catalog[key].selectable) ? null : catalog\n}\n\n/** Current-value option kept when a saved value is missing from the live list. */\nfunction withCurrentOption(options: readonly FieldOptionWire[], value: string): FieldOptionWire[] {\n const [inherit, ...rest] = options\n const extra: FieldOptionWire = { value, label: `${value}(当前配置,不在宿主机列表中)` }\n return inherit === undefined ? [extra] : [inherit, extra, ...rest]\n}\n\n/**\n * Layer the live catalog over the Host panel fields. A group the Host cannot\n * enumerate falls back to the free-text control; the rest render as selects.\n *\n * `disabled` must be cleared explicitly: the Host's initial panel descriptor\n * carries `disabled: true` for dependent selectors (model without a provider,\n * effort without a model), and a fetched catalog that resolved the dependency\n * has to re-enable them. Forgetting this is exactly what made the model and\n * reasoning selects permanently unusable after choosing a provider/model.\n */\nexport function applyCatalogFields(\n fields: readonly EditableFieldWire[],\n catalog: CatalogWire | null,\n current: Partial<Record<CatalogKey, string | undefined>> = {},\n): EditableFieldWire[] {\n if (catalog === null) return [...fields]\n return fields.map((field) => {\n if (!isCatalogKey(field.key)) return field\n const selector = catalog[field.key]\n if (!selector.selectable) {\n return field.kind === 'select'\n ? { ...field, kind: 'text', options: undefined, help: PANEL_COPY.catalogUnavailable, disabled: undefined }\n : field\n }\n const value = current[field.key] ?? ''\n const options = value !== '' && !selector.options.some(option => option.value === value)\n ? withCurrentOption(selector.options, value)\n : [...selector.options]\n const next: EditableFieldWire = { ...field, kind: 'select', options }\n if (selector.disabled === true) next.disabled = true\n else delete next.disabled\n if (selector.help === undefined) delete next.help\n else next.help = selector.help\n return next\n })\n}","/**\n * 设置面板共享层:页面状态类型、Host 字段渲染原语(FieldRow / FieldsCard /\n * SaveBar)、生命周期徽标与纯格式化工具。\n *\n * 这一层不发请求、不持有页面状态,只把 Host 下发的 wire 数据渲染成控件;\n * 页面外壳在 `index.tsx`,各 tab 只组合这些原语。\n *\n * @module dsh-channels/client/panel/shared\n */\n\nimport { useState } from 'react'\nimport { FIELD_GROUP_LABELS, PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type {\n AccessWire,\n ChannelPhaseWire,\n CleanupPreviewWire,\n ConfigValue,\n CredentialField,\n EditableFieldWire,\n EditableKey,\n EditableValues,\n FieldGroup,\n FieldIssueWire,\n PanelTabId,\n PanelWire,\n} from '../../settings/contract.ts'\n\n/** Initial panel load: one request, retried from the error card. */\nexport type LoadState =\n | { phase: 'loading' }\n | { phase: 'error'; message: string }\n | { phase: 'ready'; panel: PanelWire }\n\n/** One transient banner above the tab body (save/credentials/reconnect/catalog). */\nexport interface Notice {\n kind: 'ok' | 'error'\n text: string\n /** Per-field validation failures rendered inline by FieldRow. */\n issues?: FieldIssueWire[]\n}\n\n/** One in-flight panel mutation (only one action runs at a time). */\nexport type BusyAction =\n | 'save'\n | 'clear'\n | 'reconnect'\n | 'credentials'\n | 'credentials-clear'\n | 'cleanup'\n | 'access'\n\n/** Tone of a small badge; maps onto `dsh-channels-cfg__badge--*` classes. */\nexport type BadgeTone = 'ok' | 'warn' | 'bad' | 'muted'\n\nexport function messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Human-readable byte size for the cleanup preview/result (base-2 units). */\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n let value = bytes\n let unit = 0\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit += 1\n }\n const digits = unit === 0 || value >= 10 ? 0 : 1\n return `${value.toFixed(digits)} ${units[unit] ?? 'B'}`\n}\n\n/** Compact duration for the connection card. */\nexport function formatUptime(ms: number | undefined): string {\n if (ms === undefined || !Number.isFinite(ms)) return PANEL_COPY.unknownValue\n const totalSeconds = Math.max(0, Math.floor(ms / 1_000))\n const days = Math.floor(totalSeconds / 86_400)\n const hours = Math.floor((totalSeconds % 86_400) / 3_600)\n const minutes = Math.floor((totalSeconds % 3_600) / 60)\n if (days > 0) return `${days} 天 ${hours} 小时`\n if (hours > 0) return `${hours} 小时 ${minutes} 分`\n if (minutes > 0) return `${minutes} 分 ${totalSeconds % 60} 秒`\n return `${totalSeconds} 秒`\n}\n\n/** Effective value for one key (falling back to the profile base). */\nexport function effectiveValue(panel: PanelWire, key: EditableKey): ConfigValue | undefined {\n return panel.config.effective[key] ?? panel.config.base[key]\n}\n\n/** Host label for one editable key; the raw key is the technical fallback. */\nexport function fieldLabel(fields: readonly EditableFieldWire[], key: EditableKey): string {\n return fields.find(field => field.key === key)?.label ?? key\n}\n\n/** Effective access-mode label, reusing the Host field options for consistency. */\nexport function accessModeLabel(panel: PanelWire, mode: AccessWire['mode']): string {\n const field = panel.config.fields.find(item => item.key === 'accessMode')\n return field?.options?.find(option => option.value === mode)?.label ?? mode\n}\n\n/** Numeric draft kept as the raw input string; parsed on save. */\nfunction numericDraft(field: EditableFieldWire, raw: ConfigValue | undefined): number | undefined {\n if (typeof raw === 'number') return raw\n if (typeof raw !== 'string' || raw.trim() === '') return undefined\n const parsed = Number(raw)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\n/**\n * Turn the dirty draft into the wire payload, keeping the Host-descriptor\n * bounds check client-side. Field messages stay technical (the Host re-validates\n * every value on save).\n */\nexport function validateDraft(\n panel: PanelWire,\n draft: EditableValues,\n): { values: EditableValues; issues: FieldIssueWire[] } {\n const values: EditableValues = {}\n const issues: FieldIssueWire[] = []\n for (const field of panel.config.fields) {\n if (!(field.key in draft)) continue\n const raw = draft[field.key]\n if (field.kind === 'number') {\n const parsed = numericDraft(field, raw)\n if (parsed === undefined) {\n issues.push({ key: field.key, message: '请输入数字' })\n continue\n }\n if (field.min !== undefined && parsed < field.min) {\n issues.push({ key: field.key, message: `不得小于 ${field.min}` })\n continue\n }\n if (field.max !== undefined && parsed > field.max) {\n issues.push({ key: field.key, message: `不得大于 ${field.max}` })\n continue\n }\n values[field.key] = parsed\n continue\n }\n if (field.kind === 'boolean') {\n values[field.key] = raw === true\n continue\n }\n values[field.key] = String(raw ?? '')\n }\n return { values, issues }\n}\n\n/** Header badge for the Host-derived lifecycle phase. */\nexport function phaseBadge(phase: ChannelPhaseWire): { text: string; tone: BadgeTone } {\n switch (phase) {\n case 'connected': return { text: PANEL_COPY.phaseConnected, tone: 'ok' }\n case 'connecting': return { text: PANEL_COPY.phaseConnecting, tone: 'warn' }\n case 'error': return { text: PANEL_COPY.phaseError, tone: 'bad' }\n case 'idle': return { text: PANEL_COPY.phaseIdle, tone: 'muted' }\n default: return { text: PANEL_COPY.phaseUnconfigured, tone: 'muted' }\n }\n}\n\n/** One-line explanation shown under the page title for the current phase. */\nexport function phaseSummary(phase: ChannelPhaseWire): string {\n switch (phase) {\n case 'connected': return PANEL_COPY.statusSummaryConnected\n case 'connecting': return PANEL_COPY.statusSummaryConnecting\n case 'error': return PANEL_COPY.statusSummaryError\n case 'idle': return PANEL_COPY.statusSummaryIdle\n default: return PANEL_COPY.statusSummaryUnconfigured\n }\n}\n\n/** Credential editor data owned by the page shell, rendered by the connection tab. */\nexport interface CredentialsView {\n appId: string\n appSecret: string\n issues: FieldIssueWire[]\n busy: BusyAction | null\n /** Host-side lifecycle busy (connecting/restarting): block writes. */\n locked: boolean\n onAppIdChange: (value: string) => void\n onAppSecretChange: (value: string) => void\n onSave: () => void\n onClear: (field: CredentialField) => void\n}\n\n/** Everything a field-bearing tab needs from the page shell's single draft. */\nexport interface FormView {\n panel: PanelWire\n /** Host descriptors after the live selector catalog has been layered on. */\n fields: readonly EditableFieldWire[]\n draft: EditableValues\n issues: FieldIssueWire[]\n busy: BusyAction | null\n /** Host-side lifecycle busy (connecting/restarting): block writes. */\n locked: boolean\n dirtyCount: number\n onChange: (key: EditableKey, value: ConfigValue) => void\n onSave: () => void\n /** Drop only the draft keys owned by one tab. */\n onDiscardTab: (tab: PanelTabId) => void\n /**\n * Open the Host's native directory chooser and write the picked absolute\n * path into the draft (not saved until「保存更改」). Absent when the Host\n * cannot offer a native chooser.\n */\n onPickDirectory?: (key: EditableKey) => Promise<void>\n}\n\n/**\n * Text input paired with the host-native「选择目录…」button. The button owns\n * its own busy state: the OS dialog can stay open for a long time, so the\n * rest of the form stays usable while the operator browses.\n */\nfunction DirectoryTextInput(props: {\n field: EditableFieldWire\n value: ConfigValue | undefined\n inputClass: string\n disabled: boolean\n onChange: (key: EditableKey, value: ConfigValue) => void\n onPick: () => Promise<void>\n}): JSX.Element {\n const { field, value, inputClass, disabled, onChange, onPick } = props\n const [picking, setPicking] = useState(false)\n const pick = async (): Promise<void> => {\n setPicking(true)\n try {\n await onPick()\n } finally {\n setPicking(false)\n }\n }\n return (\n <div className=\"dsh-channels-cfg__inputRow\">\n <input\n type=\"text\"\n className={inputClass}\n value={String(value ?? '')}\n placeholder={field.placeholder}\n aria-label={field.label}\n disabled={disabled || picking}\n onChange={event => onChange(field.key, event.target.value)}\n />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--browse\"\n disabled={disabled || picking}\n onClick={() => { void pick() }}\n >\n {picking ? PANEL_COPY.browsingDirectory : PANEL_COPY.browseDirectory}\n </button>\n </div>\n )\n}\n\n/** One generic form control driven by the Host field descriptor. */\nexport function FieldRow(props: {\n field: EditableFieldWire\n value: ConfigValue | undefined\n issues: FieldIssueWire[]\n disabled?: boolean\n onChange: (key: EditableKey, value: ConfigValue) => void\n /** Present only for `field.browse === 'directory'` rows the page can serve. */\n onPickDirectory?: () => Promise<void>\n}): JSX.Element {\n const { field, value, issues, disabled = false, onChange, onPickDirectory } = props\n const issue = issues.find(item => item.key === field.key)\n const inputClass = `dsh-channels-cfg__input${issue === undefined ? '' : ' dsh-channels-cfg__input--invalid'}`\n const fieldClass = `dsh-channels-cfg__field${field.wide === true ? ' dsh-channels-cfg__field--wide' : ''}`\n return (\n <div className={fieldClass}>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{field.label}</span>\n {field.restartRequired\n ? <span className=\"dsh-channels-cfg__tag dsh-channels-cfg__tag--restart\">{PANEL_COPY.restartTag}</span>\n : null}\n </div>\n {field.kind === 'boolean'\n ? (\n <label className=\"dsh-channels-cfg__check\">\n <input\n type=\"checkbox\"\n className=\"dsh-channels-cfg__checkbox\"\n checked={value === true}\n aria-label={field.label}\n disabled={disabled}\n onChange={event => onChange(field.key, event.target.checked)}\n />\n <span>{value === true ? PANEL_COPY.enabled : PANEL_COPY.disabled}</span>\n </label>\n )\n : null}\n {field.kind === 'select'\n ? (\n <select\n className={inputClass}\n value={String(value ?? '')}\n disabled={disabled || field.disabled === true}\n aria-label={field.label}\n onChange={event => onChange(field.key, event.target.value)}\n >\n {(field.options ?? []).map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n )\n : null}\n {field.kind === 'number'\n ? (\n <input\n type=\"number\"\n className={inputClass}\n value={value === undefined || value === '' ? '' : String(value)}\n min={field.min}\n max={field.max}\n step={field.step}\n aria-label={field.label}\n disabled={disabled}\n onChange={event => onChange(field.key, event.target.value)}\n />\n )\n : null}\n {field.kind === 'text'\n ? (field.browse === 'directory' && onPickDirectory !== undefined\n ? (\n <DirectoryTextInput\n field={field}\n value={value}\n inputClass={inputClass}\n disabled={disabled}\n onChange={onChange}\n onPick={onPickDirectory}\n />\n )\n : (\n <input\n type=\"text\"\n className={inputClass}\n value={String(value ?? '')}\n placeholder={field.placeholder}\n aria-label={field.label}\n disabled={disabled}\n onChange={event => onChange(field.key, event.target.value)}\n />\n ))\n : null}\n {field.help === undefined ? null : <span className=\"dsh-channels-cfg__help\">{field.help}</span>}\n {issue === undefined ? null : <span className=\"dsh-channels-cfg__fieldError\">{issue.message}</span>}\n </div>\n )\n}\n\n/**\n * One tab's editable fields, partitioned by the Host field groups that the tab\n * owns (`TAB_FIELD_GROUPS`). Empty groups render nothing, so a Host that moves\n * every key out of a group does not leave a stray heading behind.\n */\nexport function FieldsCard(props: {\n form: FormView\n groups: readonly FieldGroup[]\n}): JSX.Element | null {\n const { form, groups } = props\n const sections = groups\n .map(group => ({\n group,\n label: FIELD_GROUP_LABELS[group],\n fields: form.fields.filter(field => field.group === group),\n }))\n .filter(section => section.fields.length > 0)\n if (sections.length === 0) return null\n return (\n <section className=\"dsh-channels-cfg__card\">\n {sections.map(section => (\n <div className=\"dsh-channels-cfg__group\" key={section.group}>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__cardTitle\">{section.label}</span>\n </div>\n <div className=\"dsh-channels-cfg__grid\">\n {section.fields.map((field) => {\n const onPick = form.onPickDirectory\n return (\n <FieldRow\n key={field.key}\n field={field}\n value={form.draft[field.key] ?? effectiveValue(form.panel, field.key)}\n issues={form.issues}\n disabled={form.locked}\n onChange={form.onChange}\n onPickDirectory={onPick === undefined ? undefined : () => onPick(field.key)}\n />\n )\n })}\n </div>\n </div>\n ))}\n </section>\n )\n}\n\n/** The unified per-tab action bar: save every dirty field or drop the draft. */\nexport function SaveBar(props: { form: FormView; tab: PanelTabId }): JSX.Element {\n const { form, tab } = props\n const groups = new Set<string>(TAB_FIELD_GROUPS[tab])\n const pageDirty = form.fields.filter(field => groups.has(field.group) && field.key in form.draft).length\n // Saving submits the whole draft, so any dirty key (any tab) enables it;\n // discarding only clears this tab, so it follows this tab's own count.\n const dirty = form.dirtyCount > 0\n const disabled = !dirty || form.busy !== null || form.locked\n return (\n <div className=\"dsh-channels-cfg__footer\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={form.onSave}\n disabled={disabled}\n >\n {form.busy === 'save' ? PANEL_COPY.saving : PANEL_COPY.save}\n </button>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n onClick={() => form.onDiscardTab(tab)}\n disabled={pageDirty === 0 || form.busy !== null || form.locked}\n >\n {PANEL_COPY.discardTab}\n </button>\n <span className=\"dsh-channels-cfg__spacer\" />\n {pageDirty > 0\n ? (\n <span className=\"dsh-channels-cfg__tag dsh-channels-cfg__tag--dirty\">\n {PANEL_COPY.dirtyCount} {pageDirty}\n </span>\n )\n : dirty\n ? (\n <span className=\"dsh-channels-cfg__tag dsh-channels-cfg__tag--dirty\">\n {PANEL_COPY.dirtyCount} {form.dirtyCount}(其他标签页)\n </span>\n )\n : null}\n <span className=\"dsh-channels-cfg__help\">\n {dirty ? PANEL_COPY.saveHintDirty : PANEL_COPY.saveHintClean}\n </span>\n </div>\n )\n}\n\n/** Muted help paragraph introducing one tab. */\nexport function TabHelp(props: { text: string }): JSX.Element {\n return <p className=\"dsh-channels-cfg__tabHelp\">{props.text}</p>\n}\n\n/** Transient page-level banner (save / credentials / reconnect / catalog). */\nexport function NoticeBar(props: { notice: Notice }): JSX.Element {\n const { notice } = props\n return (\n <div\n className={`dsh-channels-cfg__notice dsh-channels-cfg__notice--${notice.kind === 'ok' ? 'ok' : 'error'}`}\n role=\"status\"\n aria-live=\"polite\"\n >\n <span>{notice.text}</span>\n </div>\n )\n}\n\n/** Muted empty/blocked state (e.g. access lists before the channel connects). */\nexport function EmptyState(props: { text: string }): JSX.Element {\n return <div className=\"dsh-channels-cfg__emptyState\">{props.text}</div>\n}\n\n/** Human label for the official SDK's WebSocket lifecycle state. */\nexport function connectionStateLabel(state: string | undefined): { text: string; tone: BadgeTone } {\n switch (state) {\n case 'connected': return { text: PANEL_COPY.phaseConnected, tone: 'ok' }\n case 'connecting': return { text: PANEL_COPY.phaseConnecting, tone: 'warn' }\n case 'reconnecting': return { text: PANEL_COPY.phaseConnecting, tone: 'warn' }\n case 'failed': return { text: PANEL_COPY.phaseError, tone: 'bad' }\n case 'idle': return { text: PANEL_COPY.phaseIdle, tone: 'muted' }\n default: return { text: state ?? PANEL_COPY.unknownValue, tone: 'muted' }\n }\n}\n\n/** Retention label, reusing the Host field options so the copy stays on the Host. */\nexport function retentionLabel(panel: PanelWire, cleanup: CleanupPreviewWire): string {\n const field = panel.config.fields.find(item => item.key === 'workspaceRetention')\n return field?.options?.find(option => option.value === cleanup.retention)?.label\n ?? (cleanup.retention === 'off' ? PANEL_COPY.cleanupRetentionOff : cleanup.retention)\n}\n","/**\n * 访问与授权卡:fail-closed 警告、群/用户白名单增删、待处理申请处理。\n *\n * `access.loaded === false`(飞书未连接、插件 storage 域未打开)时白名单与\n * 申请列表是「未知」而不是「空」,因此只渲染连接提示并禁用全部写操作。\n * 管理员与准入模式由同 tab 上方「准入与授权」字段表单编辑,这里不再重复。\n *\n * @module dsh-channels/client/panel/access-card\n */\n\nimport { useState } from 'react'\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type {\n AccessGroupWire,\n AccessRequestWire,\n AccessUserWire,\n PanelWire,\n} from '../../settings/contract.ts'\nimport { accessModeLabel, EmptyState } from './shared.tsx'\nimport type { BusyAction } from './shared.tsx'\n\n/** Local `YYYY-MM-DD HH:mm` rendering for authorization timestamps. */\nfunction formatAccessDate(ms: number): string {\n if (!Number.isFinite(ms) || ms <= 0) return PANEL_COPY.unknownValue\n const date = new Date(ms)\n const pad = (value: number): string => String(value).padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/** Which authorization entry produced one grant. */\nfunction accessSourceLabel(source: AccessGroupWire['source']): string {\n switch (source) {\n case 'command': return PANEL_COPY.accessSourceCommand\n case 'card': return PANEL_COPY.accessSourceCard\n default: return PANEL_COPY.accessSourcePanel\n }\n}\n\nexport interface AccessCardProps {\n panel: PanelWire\n busy: BusyAction | null\n locked: boolean\n notice: string | null\n error: string | null\n onAddGroup: (chatId: string, name: string) => Promise<boolean>\n onRemoveGroup: (chatId: string) => void\n onAddUser: (openId: string, name: string) => Promise<boolean>\n onRemoveUser: (openId: string) => void\n onResolveRequest: (token: string, decision: 'allow' | 'deny') => void\n}\n\nexport function AccessCard(props: AccessCardProps): JSX.Element {\n const { panel, busy, locked, notice, error, onAddGroup, onRemoveGroup, onAddUser, onRemoveUser, onResolveRequest } = props\n const { access } = panel\n const [groupChatId, setGroupChatId] = useState('')\n const [groupName, setGroupName] = useState('')\n const [userOpenId, setUserOpenId] = useState('')\n const [userName, setUserName] = useState('')\n const groups: readonly AccessGroupWire[] = access.groups\n const users: readonly AccessUserWire[] = access.users\n const requests: readonly AccessRequestWire[] = access.requests\n const working = busy === 'access'\n const disabled = busy !== null || locked || !access.loaded\n\n const submitGroup = async (): Promise<void> => {\n if (groupChatId.trim() === '') return\n if (await onAddGroup(groupChatId, groupName)) {\n setGroupChatId('')\n setGroupName('')\n }\n }\n\n const submitUser = async (): Promise<void> => {\n if (userOpenId.trim() === '') return\n if (await onAddUser(userOpenId, userName)) {\n setUserOpenId('')\n setUserName('')\n }\n }\n\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.accessCard}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{accessModeLabel(panel, access.mode)}</span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessCardHelp}</span>\n\n {access.mode === 'open'\n ? <div className=\"dsh-channels-cfg__banner\">{PANEL_COPY.accessModeOpenWarning}</div>\n : null}\n {access.loaded && access.mode === 'guarded' && !access.admin.configured\n ? (\n <div className=\"dsh-channels-cfg__notice dsh-channels-cfg__notice--error\">\n <strong>{PANEL_COPY.accessAdminMissingWarning}</strong>\n </div>\n )\n : null}\n\n {!access.loaded\n ? <EmptyState text={PANEL_COPY.accessNotLoaded} />\n : (\n <>\n <div className=\"dsh-channels-cfg__accessSection\">\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{PANEL_COPY.accessGroupsTitle}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{groups.length} 个</span>\n </div>\n {groups.length === 0\n ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessGroupsEmpty}</span>\n : (\n <ul className=\"dsh-channels-cfg__accessList\">\n {groups.map(group => (\n <li className=\"dsh-channels-cfg__accessRow\" key={group.chatId}>\n <span className=\"dsh-channels-cfg__accessMain\">\n <span>{group.name ?? group.chatId}</span>\n <span className=\"dsh-channels-cfg__accessMeta\">\n <span className=\"dsh-channels-cfg__mono\">{group.chatId}</span>\n {` · ${accessSourceLabel(group.source)} · ${formatAccessDate(group.addedAt)}`}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onRemoveGroup(group.chatId)}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessRemove}\n </button>\n </li>\n ))}\n </ul>\n )}\n <div className=\"dsh-channels-cfg__accessAdd\">\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={groupChatId}\n placeholder={PANEL_COPY.accessGroupChatIdPlaceholder}\n aria-label={PANEL_COPY.accessGroupChatIdPlaceholder}\n disabled={disabled}\n onChange={event => setGroupChatId(event.target.value)}\n />\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={groupName}\n placeholder={PANEL_COPY.accessGroupNamePlaceholder}\n aria-label={PANEL_COPY.accessGroupNamePlaceholder}\n disabled={disabled}\n onChange={event => setGroupName(event.target.value)}\n />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n disabled={disabled || groupChatId.trim() === ''}\n onClick={() => { void submitGroup() }}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessAddGroup}\n </button>\n </div>\n </div>\n\n <div className=\"dsh-channels-cfg__accessSection\">\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{PANEL_COPY.accessUsersTitle}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{users.length} 个</span>\n </div>\n {users.length === 0\n ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessUsersEmpty}</span>\n : (\n <ul className=\"dsh-channels-cfg__accessList\">\n {users.map(user => (\n <li className=\"dsh-channels-cfg__accessRow\" key={user.openId}>\n <span className=\"dsh-channels-cfg__accessMain\">\n <span>{user.name ?? user.openId}</span>\n <span className=\"dsh-channels-cfg__accessMeta\">\n <span className=\"dsh-channels-cfg__mono\">{user.openId}</span>\n {` · ${accessSourceLabel(user.source)} · ${formatAccessDate(user.addedAt)}`}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onRemoveUser(user.openId)}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessRemove}\n </button>\n </li>\n ))}\n </ul>\n )}\n <div className=\"dsh-channels-cfg__accessAdd\">\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={userOpenId}\n placeholder={PANEL_COPY.accessUserOpenIdPlaceholder}\n aria-label={PANEL_COPY.accessUserOpenIdPlaceholder}\n disabled={disabled}\n onChange={event => setUserOpenId(event.target.value)}\n />\n <input\n type=\"text\"\n className=\"dsh-channels-cfg__input dsh-channels-cfg__accessInput\"\n value={userName}\n placeholder={PANEL_COPY.accessUserNamePlaceholder}\n aria-label={PANEL_COPY.accessUserNamePlaceholder}\n disabled={disabled}\n onChange={event => setUserName(event.target.value)}\n />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n disabled={disabled || userOpenId.trim() === ''}\n onClick={() => { void submitUser() }}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessAddUser}\n </button>\n </div>\n </div>\n\n <div className=\"dsh-channels-cfg__accessSection\">\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{PANEL_COPY.accessRequestsTitle}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{requests.length} 条</span>\n </div>\n {requests.length === 0\n ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessRequestsEmpty}</span>\n : (\n <ul className=\"dsh-channels-cfg__accessList\">\n {requests.map(request => (\n <li className=\"dsh-channels-cfg__accessRow\" key={request.token}>\n <span className=\"dsh-channels-cfg__accessMain\">\n <span>\n {request.kind === 'group' ? PANEL_COPY.accessRequestGroup : PANEL_COPY.accessRequestUser}\n {':'}\n {request.name ?? request.targetId}\n </span>\n <span className=\"dsh-channels-cfg__accessMeta\">\n <span className=\"dsh-channels-cfg__mono\">{request.targetId}</span>\n {` · ${formatAccessDate(request.createdAt)} · ${PANEL_COPY.accessRequestExpires} ${formatAccessDate(request.expiresAt)}`}\n {request.kind === 'user' && request.requester !== undefined\n ? ` · ${PANEL_COPY.accessRequestRequester} ${request.requester}`\n : ''}\n {request.kind === 'user' && request.groupName !== undefined\n ? ` · ${request.groupName}`\n : request.kind === 'user' && request.chatId !== undefined\n ? ` · ${request.chatId}`\n : ''}\n </span>\n </span>\n <span className=\"dsh-channels-cfg__accessActions\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n disabled={disabled}\n onClick={() => onResolveRequest(request.token, 'allow')}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessAllow}\n </button>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onResolveRequest(request.token, 'deny')}\n >\n {working ? PANEL_COPY.accessWorking : PANEL_COPY.accessDeny}\n </button>\n </span>\n </li>\n ))}\n </ul>\n )}\n </div>\n </>\n )}\n\n {notice === null\n ? null\n : (\n <div className=\"dsh-channels-cfg__notice dsh-channels-cfg__notice--ok\" role=\"status\" aria-live=\"polite\">\n <span>{notice}</span>\n </div>\n )}\n {error === null\n ? null\n : <span className=\"dsh-channels-cfg__fieldError\" role=\"alert\">{error}</span>}\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessHints}</span>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.accessChatIdHint}</span>\n </section>\n )\n}\n","/**\n * 「访问授权」tab:准入与授权字段表单(管理员 open_id / 准入模式 /\n * 申请有效期)+ 白名单与待处理申请卡。表单保存走全局 draft,白名单\n * 增删是独立即时 mutation。\n *\n * @module dsh-channels/client/panel/access-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport { AccessCard } from './access-card.tsx'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport interface AccessTabProps {\n form: FormView\n accessNotice: string | null\n accessError: string | null\n onAddGroup: (chatId: string, name: string) => Promise<boolean>\n onRemoveGroup: (chatId: string) => void\n onAddUser: (openId: string, name: string) => Promise<boolean>\n onRemoveUser: (openId: string) => void\n onResolveRequest: (token: string, decision: 'allow' | 'deny') => void\n}\n\nexport function AccessTab(props: AccessTabProps): JSX.Element {\n const { form, accessNotice, accessError, onAddGroup, onRemoveGroup, onAddUser, onRemoveUser, onResolveRequest } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.accessTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.access} />\n <SaveBar form={form} tab=\"access\" />\n <AccessCard\n panel={form.panel}\n busy={form.busy}\n locked={form.locked}\n notice={accessNotice}\n error={accessError}\n onAddGroup={onAddGroup}\n onRemoveGroup={onRemoveGroup}\n onAddUser={onAddUser}\n onRemoveUser={onRemoveUser}\n onResolveRequest={onResolveRequest}\n />\n </div>\n )\n}\n","/**\n * 「高级」tab:流式刷新等低层字段 + 只读部署信息(overlayPath / version /\n * 部署级参数)+ 「清除 Web 覆盖」。清除覆盖是整体动作,不走 draft。\n *\n * @module dsh-channels/client/panel/advanced-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type { PanelWire } from '../../settings/contract.ts'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { BusyAction, FormView } from './shared.tsx'\n\nexport function AdvancedTab(props: { form: FormView; onClear: () => void }): JSX.Element {\n const { form, onClear } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.advancedTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.advanced} />\n <SaveBar form={form} tab=\"advanced\" />\n <AdvancedCard panel={form.panel} busy={form.busy} locked={form.locked} onClear={onClear} />\n </div>\n )\n}\n\n/** Read-only deployment facts; the only write action here drops the overlay file. */\nfunction AdvancedCard(props: {\n panel: PanelWire\n busy: BusyAction | null\n locked: boolean\n onClear: () => void\n}): JSX.Element {\n const { panel, busy, locked, onClear } = props\n const { readOnly } = panel.config\n const overridden = panel.config.overriddenKeys.length\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.advancedCard}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">\n {overridden === 0 ? PANEL_COPY.inherited : `${PANEL_COPY.overridden} ${overridden} 项`}\n </span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.advancedHelp}</span>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.readOnlyCard}</span>\n </div>\n <div className=\"dsh-channels-cfg__readOnly\">\n {readOnly.map(field => (\n <div className=\"dsh-channels-cfg__kv\" key={field.key}>\n <span className=\"dsh-channels-cfg__kvKey\">{field.label}</span>\n <span className=\"dsh-channels-cfg__kvValue\">{field.value}</span>\n {field.help === undefined ? null : <span className=\"dsh-channels-cfg__help\">{field.help}</span>}\n </div>\n ))}\n <div className=\"dsh-channels-cfg__kv\">\n <span className=\"dsh-channels-cfg__kvKey\">{PANEL_COPY.overlayPath}</span>\n <span className=\"dsh-channels-cfg__kvValue dsh-channels-cfg__mono\">{panel.status.overlayPath}</span>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.overlayPathHelp}</span>\n </div>\n <div className=\"dsh-channels-cfg__kv\">\n <span className=\"dsh-channels-cfg__kvKey\">{PANEL_COPY.version}</span>\n <span className=\"dsh-channels-cfg__kvValue dsh-channels-cfg__mono\">{panel.status.version}</span>\n </div>\n </div>\n <div className=\"dsh-channels-cfg__actions\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n onClick={onClear}\n disabled={overridden === 0 || busy !== null || locked}\n >\n {busy === 'clear' ? PANEL_COPY.clearing : PANEL_COPY.clear}\n </button>\n <span className=\"dsh-channels-cfg__help\">\n {overridden === 0 ? PANEL_COPY.noOverrides : `${PANEL_COPY.overridden} ${overridden} 项`}\n </span>\n </div>\n </section>\n )\n}\n","/**\n * 飞书应用凭据编辑器:值经 Host 的 DSH 凭据服务写入本机凭据库,面板只显示\n * 「已配置 / 未配置」,两个方向都不回显已存值。保存动作由页面外壳持有\n * (凭据保存独立于配置 draft)。\n *\n * @module dsh-channels/client/panel/credentials-card\n */\n\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type { CredentialField, CredentialRefWire, PanelWire } from '../../settings/contract.ts'\nimport type { CredentialsView } from './shared.tsx'\n\nexport function CredentialsCard(props: {\n panel: PanelWire\n credentials: CredentialsView\n}): JSX.Element {\n const { panel, credentials } = props\n const { appId, appSecret, issues, busy, locked, onAppIdChange, onAppSecretChange, onSave, onClear } = credentials\n const appIdState = panel.config.credentials.appId\n const appSecretState = panel.config.credentials.appSecret\n const configured = appIdState.configured && appSecretState.configured\n const writable = appIdState.writable || appSecretState.writable\n const disabled = busy !== null || locked || !writable\n const pending = appId.trim() !== '' || appSecret.trim() !== ''\n\n const row = (\n field: CredentialField,\n label: string,\n state: CredentialRefWire,\n value: string,\n type: 'text' | 'password',\n placeholder: string,\n onChange: (next: string) => void,\n ): JSX.Element => {\n const issue = issues.find(item => item.key === field)\n const inputClass = `dsh-channels-cfg__input${issue === undefined ? '' : ' dsh-channels-cfg__input--invalid'}`\n return (\n <div className=\"dsh-channels-cfg__field\" key={field}>\n <div className=\"dsh-channels-cfg__labelRow\">\n <span className=\"dsh-channels-cfg__label\">{label}</span>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${state.configured ? 'ok' : 'bad'}`}>\n {state.configured ? PANEL_COPY.credentialConfigured : PANEL_COPY.credentialMissing}\n </span>\n </div>\n <div className=\"dsh-channels-cfg__cred\">\n <input\n type={type}\n className={inputClass}\n value={value}\n placeholder={placeholder}\n autoComplete=\"new-password\"\n aria-label={label}\n disabled={disabled}\n onChange={event => onChange(event.target.value)}\n />\n {state.configured\n ? (\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n disabled={disabled}\n onClick={() => onClear(field)}\n >\n {busy === 'credentials-clear' ? PANEL_COPY.removingCredential : PANEL_COPY.removeCredential}\n </button>\n )\n : null}\n </div>\n <span className=\"dsh-channels-cfg__help\">\n {PANEL_COPY.credentialsRef}:<span className=\"dsh-channels-cfg__mono\">{state.ref}</span>\n {state.source === undefined ? '' : ` · ${state.source}`}\n </span>\n {issue === undefined ? null : <span className=\"dsh-channels-cfg__fieldError\">{issue.message}</span>}\n </div>\n )\n }\n\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.credentialsCard}</span>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${configured ? 'ok' : 'muted'}`}>\n {configured ? PANEL_COPY.configuredBadge : PANEL_COPY.unconfiguredBadge}\n </span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.credentialsCardHelp}</span>\n {row('appId', PANEL_COPY.appIdLabel, appIdState, appId, 'text', PANEL_COPY.appIdPlaceholder, onAppIdChange)}\n {row('appSecret', PANEL_COPY.appSecretLabel, appSecretState, appSecret, 'password', PANEL_COPY.appSecretPlaceholder, onAppSecretChange)}\n {writable ? null : <span className=\"dsh-channels-cfg__fieldError\">{PANEL_COPY.credentialReadOnly}</span>}\n <div className=\"dsh-channels-cfg__footer\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={onSave}\n disabled={disabled || !pending}\n >\n {busy === 'credentials' ? PANEL_COPY.savingCredentials : PANEL_COPY.saveCredentials}\n </button>\n <span className=\"dsh-channels-cfg__spacer\" />\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.credentialsPendingHint}</span>\n </div>\n </section>\n )\n}\n","/**\n * 「连接」tab:按 Host 派生的生命周期阶段渲染连接引导。\n *\n * - unconfigured:onboarding 三步 + 凭据卡(不渲染可用的「连接」按钮)\n * - idle:连接信息卡 + 「连接飞书」\n * - error:连接信息卡 + 最近一次连接失败 + 「重试」\n * - connected:连接信息卡 + 「重启飞书连接」+ 凭据卡\n * - connecting:禁用按钮显示「连接中…」\n *\n * @module dsh-channels/client/panel/connection-tab\n */\n\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type { ChannelStatusWire, PanelWire } from '../../settings/contract.ts'\nimport { CredentialsCard } from './credentials-card.tsx'\nimport { connectionStateLabel, fieldLabel, formatUptime, phaseBadge, TabHelp } from './shared.tsx'\nimport type { BusyAction, CredentialsView } from './shared.tsx'\n\nexport interface ConnectionTabProps {\n panel: PanelWire\n status: ChannelStatusWire\n busy: BusyAction | null\n credentials: CredentialsView\n onReconnect: () => void\n}\n\nexport function ConnectionTab(props: ConnectionTabProps): JSX.Element {\n const { panel, status, busy, credentials, onReconnect } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.connectionTabHelp} />\n {panel.config.restartPendingKeys.length === 0\n ? null\n : <RestartBanner panel={panel} busy={busy} locked={status.busy} onReconnect={onReconnect} />}\n {status.phase === 'unconfigured'\n ? <OnboardingCard status={status} />\n : <ConnectionInfoCard status={status} busy={busy} onReconnect={onReconnect} />}\n <CredentialsCard panel={panel} credentials={credentials} />\n </div>\n )\n}\n\n/** First-run guide: create the app, fill the credentials, save and wait. */\nfunction OnboardingCard(props: { status: ChannelStatusWire }): JSX.Element {\n const { status } = props\n const steps = [\n { title: PANEL_COPY.onboardingStep1Title, text: PANEL_COPY.onboardingStep1Text },\n { title: PANEL_COPY.onboardingStep2Title, text: PANEL_COPY.onboardingStep2Text },\n { title: PANEL_COPY.onboardingStep3Title, text: PANEL_COPY.onboardingStep3Text },\n ]\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.onboardingTitle}</span>\n <span className=\"dsh-channels-cfg__badge dsh-channels-cfg__badge--muted\">\n {PANEL_COPY.phaseUnconfigured}\n </span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.onboardingIntro}</span>\n <ol className=\"dsh-channels-cfg__steps\">\n {steps.map((step, index) => (\n <li className=\"dsh-channels-cfg__step\" key={step.title}>\n <span className=\"dsh-channels-cfg__stepIndex\">{index + 1}</span>\n <span className=\"dsh-channels-cfg__stepBody\">\n <span className=\"dsh-channels-cfg__stepTitle\">{step.title}</span>\n <span className=\"dsh-channels-cfg__help\">{step.text}</span>\n </span>\n </li>\n ))}\n </ol>\n {status.missingConfig.length === 0\n ? null\n : (\n <div className=\"dsh-channels-cfg__banner\">\n <strong>{PANEL_COPY.missingConfig}:</strong>\n <span>{status.missingConfig.join('、')}</span>\n </div>\n )}\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.statusHintUnconfigured}</span>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.onboardingDocsHint}</span>\n </section>\n )\n}\n\n/** Runtime facts plus the one lifecycle action available in the current phase. */\nfunction ConnectionInfoCard(props: {\n status: ChannelStatusWire\n busy: BusyAction | null\n onReconnect: () => void\n}): JSX.Element {\n const { status, busy, onReconnect } = props\n const badge = phaseBadge(status.phase)\n // `status.busy` covers a connection attempt started outside this panel; the\n // local busy flag covers the panel's own action. Either way the button waits.\n const actionBusy = busy !== null || status.busy\n const connection = connectionStateLabel(status.connection?.state)\n const facts = [\n { label: PANEL_COPY.statusCard, value: badge.text },\n { label: PANEL_COPY.connectionStateLabel, value: connection.text },\n { label: PANEL_COPY.botOpenId, value: status.botOpenIdTail ?? PANEL_COPY.unknownValue },\n { label: PANEL_COPY.uptime, value: formatUptime(status.uptimeMs) },\n { label: PANEL_COPY.routeCount, value: String(status.routeCount) },\n { label: PANEL_COPY.liveAgentCount, value: String(status.liveAgentCount) },\n { label: PANEL_COPY.inflightTurnCount, value: String(status.inflightTurnCount) },\n ]\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.connectionInfo}</span>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${status.configured ? 'ok' : 'bad'}`}>\n {status.configured ? PANEL_COPY.configuredBadge : PANEL_COPY.unconfiguredBadge}\n </span>\n </div>\n {status.lastStartError === undefined\n ? null\n : (\n <div className=\"dsh-channels-cfg__notice dsh-channels-cfg__notice--error\">\n <strong>{PANEL_COPY.lastError}</strong>\n <span className=\"dsh-channels-cfg__mono\">{status.lastStartError}</span>\n </div>\n )}\n <div className=\"dsh-channels-cfg__facts\">\n {facts.map(fact => (\n <div className=\"dsh-channels-cfg__fact\" key={fact.label}>\n <span className=\"dsh-channels-cfg__factLabel\">{fact.label}</span>\n <span className=\"dsh-channels-cfg__factValue\">{fact.value}</span>\n </div>\n ))}\n </div>\n {status.reasons.length === 0\n ? null\n : (\n <span className={`dsh-channels-cfg__help${status.level === 'unhealthy' ? ' dsh-channels-cfg__reasonBad' : ''}`}>\n {PANEL_COPY.healthReasons}:{status.reasons.join(';')}\n </span>\n )}\n <div className=\"dsh-channels-cfg__actions\">\n {status.phase === 'connecting'\n ? (\n <button type=\"button\" className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\" disabled>\n {PANEL_COPY.connecting}\n </button>\n )\n : null}\n {status.phase === 'idle'\n ? (\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={onReconnect}\n disabled={actionBusy}\n >\n {busy === 'reconnect' ? PANEL_COPY.connecting : PANEL_COPY.connect}\n </button>\n )\n : null}\n {status.phase === 'error'\n ? (\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--primary\"\n onClick={onReconnect}\n disabled={actionBusy}\n >\n {busy === 'reconnect' ? PANEL_COPY.connecting : PANEL_COPY.retry}\n </button>\n )\n : null}\n {status.phase === 'connected'\n ? (\n <>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--danger\"\n onClick={onReconnect}\n disabled={actionBusy}\n >\n {busy === 'reconnect' ? PANEL_COPY.reconnecting : PANEL_COPY.reconnect}\n </button>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.restartWarning}</span>\n </>\n )\n : null}\n </div>\n </section>\n )\n}\n\n/** Config saved but only applied on the next connect: offer the one-click restart. */\nfunction RestartBanner(props: {\n panel: PanelWire\n busy: BusyAction | null\n locked: boolean\n onReconnect: () => void\n}): JSX.Element {\n const { panel, busy, locked, onReconnect } = props\n const keys = panel.config.restartPendingKeys\n .map(key => fieldLabel(panel.config.fields, key))\n .join('、')\n return (\n <div className=\"dsh-channels-cfg__banner\">\n <span>{PANEL_COPY.restartBanner}{keys}</span>\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--danger\"\n onClick={onReconnect}\n disabled={busy !== null || locked}\n >\n {busy === 'reconnect' ? PANEL_COPY.reconnecting : PANEL_COPY.reconnect}\n </button>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.restartWarning}</span>\n </div>\n )\n}\n","/**\n * 「模型」tab:模型服务商 / 模型 id / 推理档位 / Agent 预设四个选择器。\n * 选项来自 Host 实时目录(页面外壳负责 provider→model→effort 级联刷新),\n * 目录读取中时在表单上方提示。\n *\n * @module dsh-channels/client/panel/model-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport function ModelTab(props: { form: FormView; catalogBusy: boolean }): JSX.Element {\n const { form, catalogBusy } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.modelTabHelp} />\n {catalogBusy ? <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.catalogLoading}</span> : null}\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.model} />\n <SaveBar form={form} tab=\"model\" />\n </div>\n )\n}\n","/**\n * 「会话与行为」tab:行为 + 内容与附件两组字段(私聊/群聊触发方式、\n * 首次引导、权限切换、附件接收)。表单字段完全由 Host 描述驱动。\n *\n * @module dsh-channels/client/panel/sessions-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport function SessionsTab(props: { form: FormView }): JSX.Element {\n const { form } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.sessionsTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.sessions} />\n <SaveBar form={form} tab=\"sessions\" />\n </div>\n )\n}\n","/**\n * 目录清理卡:展示清理预览(保留期 / 自动清理 / 候选数量与空间),\n * 执行前二次确认,结果与部分失败逐条展示。`cleanup.error` 非空时\n * (例如未连接、无法扫描工作区)按钮天然禁用并显示原因。\n *\n * @module dsh-channels/client/panel/cleanup-card\n */\n\nimport { PANEL_COPY } from '../../settings/contract.ts'\nimport type { CleanupPreviewWire, CleanupRunWire, PanelWire } from '../../settings/contract.ts'\nimport { formatBytes, retentionLabel } from './shared.tsx'\nimport type { BusyAction } from './shared.tsx'\n\nexport function CleanupCard(props: {\n panel: PanelWire\n cleanup: CleanupPreviewWire\n busy: BusyAction | null\n locked: boolean\n result: CleanupRunWire | null\n error: string | null\n onRun: () => void\n}): JSX.Element {\n const { panel, cleanup, busy, locked, result, error, onRun } = props\n const autoText = cleanup.retention === 'off'\n ? PANEL_COPY.cleanupAutoInactive\n : cleanup.auto ? PANEL_COPY.cleanupAutoOn : PANEL_COPY.cleanupAutoOff\n const blocked = cleanup.error !== undefined\n ? `${PANEL_COPY.cleanupPreviewFailed}:${cleanup.error}`\n : cleanup.retention === 'off'\n ? PANEL_COPY.cleanupDisabledOff\n : cleanup.candidates === 0\n ? PANEL_COPY.cleanupDisabledEmpty\n : null\n const facts = [\n { label: PANEL_COPY.cleanupRetention, value: retentionLabel(panel, cleanup) },\n { label: PANEL_COPY.cleanupAutoLabel, value: autoText },\n { label: PANEL_COPY.cleanupCandidates, value: `${cleanup.candidates} 个` },\n { label: PANEL_COPY.cleanupWorkspaces, value: `${cleanup.workspaces} 个` },\n { label: PANEL_COPY.cleanupCandidateBytes, value: formatBytes(cleanup.candidateBytes) },\n ]\n const running = busy === 'cleanup'\n const failed = result !== null && (!result.ok || error !== null)\n return (\n <section className=\"dsh-channels-cfg__card\">\n <div className=\"dsh-channels-cfg__cardHead\">\n <span className=\"dsh-channels-cfg__cardTitle\">{PANEL_COPY.cleanupCard}</span>\n <span className=\"dsh-channels-cfg__cardMeta\">{retentionLabel(panel, cleanup)}</span>\n </div>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.cleanupCardHelp}</span>\n <div className=\"dsh-channels-cfg__facts\">\n {facts.map(fact => (\n <div className=\"dsh-channels-cfg__fact\" key={fact.label}>\n <span className=\"dsh-channels-cfg__factLabel\">{fact.label}</span>\n <span className=\"dsh-channels-cfg__factValue\">{fact.value}</span>\n </div>\n ))}\n </div>\n {blocked === null ? null : <span className=\"dsh-channels-cfg__help\">{blocked}</span>}\n {result === null\n ? null\n : (\n <div className={`dsh-channels-cfg__notice dsh-channels-cfg__notice--${failed ? 'error' : 'ok'}`}>\n {result.ok\n ? (\n <span>\n {PANEL_COPY.cleanupDone}:{PANEL_COPY.cleanupDeleted} {result.deletedWorkspaces} 个,\n {PANEL_COPY.cleanupDeletedDirs} {result.deletedSessionDirs} 个,\n {PANEL_COPY.cleanupRetired} {result.retiredRoutes} 条,\n {PANEL_COPY.cleanupFreed} {formatBytes(result.freedBytes)}。\n </span>\n )\n : (\n <span>\n {PANEL_COPY.cleanupFailed}\n {result.error === undefined ? '' : `:${result.error}`}\n </span>\n )}\n {result.failures.length === 0\n ? null\n : (\n <>\n <span>{PANEL_COPY.cleanupFailures}</span>\n <ul className=\"dsh-channels-cfg__issues\">\n {result.failures.map((failure, index) => (\n <li key={`${index}-${failure}`} className=\"dsh-channels-cfg__mono\">{failure}</li>\n ))}\n </ul>\n </>\n )}\n </div>\n )}\n {error === null ? null : <span className=\"dsh-channels-cfg__fieldError\">{error}</span>}\n <div className=\"dsh-channels-cfg__actions\">\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn dsh-channels-cfg__btn--danger\"\n onClick={onRun}\n disabled={running || busy !== null || locked || blocked !== null}\n >\n {running ? PANEL_COPY.cleanupRunning : PANEL_COPY.cleanupNow}\n </button>\n <span className=\"dsh-channels-cfg__help\">{PANEL_COPY.cleanupConfirmHint}</span>\n </div>\n </section>\n )\n}\n","/**\n * 「工作区」tab:工作区根目录 / 过期保留策略 / 自动清理字段 + 目录清理卡。\n * 表单保存栏在字段卡正下方,清理卡(独立动作,不走 draft)在其后。\n *\n * @module dsh-channels/client/panel/workspace-tab\n */\n\nimport { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type { CleanupPreviewWire, CleanupRunWire } from '../../settings/contract.ts'\nimport { CleanupCard } from './cleanup-card.tsx'\nimport { FieldsCard, SaveBar, TabHelp } from './shared.tsx'\nimport type { FormView } from './shared.tsx'\n\nexport interface WorkspaceTabProps {\n form: FormView\n cleanup: CleanupPreviewWire\n cleanupResult: CleanupRunWire | null\n cleanupError: string | null\n onRunCleanup: () => void\n}\n\nexport function WorkspaceTab(props: WorkspaceTabProps): JSX.Element {\n const { form, cleanup, cleanupResult, cleanupError, onRunCleanup } = props\n return (\n <div className=\"dsh-channels-cfg__tabBody\">\n <TabHelp text={PANEL_COPY.workspaceTabHelp} />\n <FieldsCard form={form} groups={TAB_FIELD_GROUPS.workspace} />\n <SaveBar form={form} tab=\"workspace\" />\n <CleanupCard\n panel={form.panel}\n cleanup={cleanup}\n busy={form.busy}\n locked={form.locked}\n result={cleanupResult}\n error={cleanupError}\n onRun={onRunCleanup}\n />\n </div>\n )\n}\n","/**\n * 设置 →「飞书渠道」页面:数据加载、5 秒状态轮询、全局 draft 与顶部状态头 /\n * tab 导航的外壳。\n *\n * 所有 Host 交互(panel / status / catalog / config / credentials / cleanup /\n * access / pick-directory)都集中在这里;tab 组件只接收渲染所需的数据与\n * 回调。页面不 import 任何 DSH 客户端包,只依赖 react 与 `settings.section`\n * 槽位。\n *\n * @module dsh-channels/client/panel\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { KeyboardEvent } from 'react'\nimport { PANEL_COPY, PANEL_TABS, TAB_FIELD_GROUPS } from '../../settings/contract.ts'\nimport type {\n AccessWire,\n CatalogWire,\n ChannelStatusWire,\n CleanupRunWire,\n ConfigValue,\n CredentialField,\n EditableKey,\n EditableValues,\n FieldIssueWire,\n PanelTabId,\n PanelWire,\n} from '../../settings/contract.ts'\nimport {\n addAccessGroup,\n addAccessUser,\n clearConfig,\n clearCredential,\n fetchCatalog,\n fetchPanel,\n fetchStatus,\n pickDirectory as requestDirectoryPicker,\n reconnect,\n removeAccessGroup,\n removeAccessUser,\n resolveAccessRequest,\n runCleanup,\n saveConfig,\n saveCredentials,\n} from '../api.ts'\nimport { applyCatalogFields, catalogFromFields } from '../catalog.ts'\nimport { AccessTab } from './access-tab.tsx'\nimport { AdvancedTab } from './advanced-tab.tsx'\nimport { ConnectionTab } from './connection-tab.tsx'\nimport { ModelTab } from './model-tab.tsx'\nimport { SessionsTab } from './sessions-tab.tsx'\nimport {\n fieldLabel,\n formatBytes,\n messageOf,\n NoticeBar,\n phaseBadge,\n phaseSummary,\n validateDraft,\n} from './shared.tsx'\nimport type {\n BusyAction,\n CredentialsView,\n FormView,\n LoadState,\n Notice,\n} from './shared.tsx'\nimport { WorkspaceTab } from './workspace-tab.tsx'\n\n/** Owner props handed to a `settings.section` contribution (unused beyond parity). */\nexport interface FeishuChannelsSectionProps {\n close?: () => void\n}\n\n/** Page shell: one panel payload, one status poll, one global config draft. */\nexport function FeishuChannelsSection(_props: FeishuChannelsSectionProps): JSX.Element | null {\n const [load, setLoad] = useState<LoadState>({ phase: 'loading' })\n const [activeTab, setActiveTab] = useState<PanelTabId>('connection')\n const [draft, setDraft] = useState<EditableValues>({})\n const [notice, setNotice] = useState<Notice | null>(null)\n const [busy, setBusy] = useState<BusyAction | null>(null)\n const [status, setStatus] = useState<ChannelStatusWire | null>(null)\n const [pollError, setPollError] = useState<string | null>(null)\n const [appIdInput, setAppIdInput] = useState('')\n const [appSecretInput, setAppSecretInput] = useState('')\n const [catalog, setCatalog] = useState<CatalogWire | null>(null)\n const [catalogBusy, setCatalogBusy] = useState(false)\n const [cleanupResult, setCleanupResult] = useState<CleanupRunWire | null>(null)\n const [cleanupError, setCleanupError] = useState<string | null>(null)\n const [accessNotice, setAccessNotice] = useState<string | null>(null)\n const [accessError, setAccessError] = useState<string | null>(null)\n const catalogTicket = useRef(0)\n /** Latest ready panel, read from the poll loop without restarting it. */\n const panelRef = useRef<PanelWire | null>(null)\n const panel = load.phase === 'ready' ? load.panel : null\n /** Host-side lifecycle busy: connecting/restarting blocks every panel write. */\n const hostBusy = (status ?? panel?.status)?.busy === true\n useEffect(() => {\n panelRef.current = panel\n }, [panel])\n\n const loadPanel = useCallback(async (): Promise<void> => {\n setLoad({ phase: 'loading' })\n setNotice(null)\n setCleanupResult(null)\n setCleanupError(null)\n setAccessNotice(null)\n setAccessError(null)\n try {\n const next = await fetchPanel()\n setLoad({ phase: 'ready', panel: next })\n setStatus(next.status)\n setDraft({})\n setAppIdInput('')\n setAppSecretInput('')\n } catch (error) {\n setLoad({ phase: 'error', message: messageOf(error) })\n }\n }, [])\n\n useEffect(() => {\n void loadPanel()\n }, [loadPanel])\n\n // The Host panel payload already carries options resolved for the effective\n // route; re-seed the local catalog whenever a fresh panel arrives (load/save).\n useEffect(() => {\n if (panel === null) {\n setCatalog(null)\n return\n }\n setCatalog(catalogFromFields(panel.config.fields))\n setCatalogBusy(false)\n }, [panel])\n\n useEffect(() => {\n if (load.phase !== 'ready') return undefined\n let cancelled = false\n const tick = async (): Promise<void> => {\n try {\n const previousPhase = panelRef.current?.status.phase\n const next = await fetchStatus()\n if (cancelled) return\n setStatus(next)\n setPollError(null)\n // A phase transition changes what the panel payload can offer: the\n // storage domain opens/closes with the connection, so the access\n // whitelists and the cleanup preview go stale. Re-read the panel but\n // never touch the dirty draft.\n if (previousPhase !== undefined && previousPhase !== next.phase) {\n try {\n const refreshed = await fetchPanel()\n if (!cancelled) {\n setLoad({ phase: 'ready', panel: refreshed })\n setStatus(refreshed.status)\n }\n } catch {\n // Keep the status-only update; the next manual refresh retries.\n }\n }\n } catch (error) {\n if (!cancelled) setPollError(messageOf(error))\n }\n }\n const timer = window.setInterval(() => { void tick() }, 5_000)\n return () => {\n cancelled = true\n window.clearInterval(timer)\n }\n }, [load.phase])\n\n /** Fetch the selector catalog for a draft route (latest request wins). */\n const refreshCatalog = useCallback(async (provider: string, model: string): Promise<void> => {\n catalogTicket.current += 1\n const ticket = catalogTicket.current\n setCatalogBusy(true)\n try {\n const next = await fetchCatalog(provider, model)\n if (catalogTicket.current === ticket) setCatalog(next)\n } catch {\n if (catalogTicket.current !== ticket) return\n // Never keep another route's options: degrade the dependent fields to the\n // free-text controls instead of offering stale values.\n setCatalog(previous => previous === null ? null : {\n provider: previous.provider,\n model: { selectable: false, options: [] },\n reasoningEffort: { selectable: false, options: [] },\n agentPreset: previous.agentPreset,\n })\n setNotice({ kind: 'error', text: PANEL_COPY.catalogFailed })\n } finally {\n if (catalogTicket.current === ticket) setCatalogBusy(false)\n }\n }, [])\n\n /** Current form values, used to keep unlisted configured values visible. */\n const currentValues = useMemo(() => {\n if (panel === null) return {}\n const valueOf = (key: EditableKey): string | undefined => {\n const raw = draft[key] ?? panel.config.effective[key] ?? panel.config.base[key]\n return typeof raw === 'string' && raw !== '' ? raw : undefined\n }\n return {\n provider: valueOf('provider'),\n model: valueOf('model'),\n reasoningEffort: valueOf('reasoningEffort'),\n agentPreset: valueOf('agentPreset'),\n }\n }, [draft, panel])\n\n const renderedFields = useMemo(\n () => applyCatalogFields(panel?.config.fields ?? [], catalog, currentValues),\n [panel, catalog, currentValues],\n )\n\n /** Whether one selector currently renders as a dropdown for this form. */\n const isSelectField = useCallback(\n (key: EditableKey): boolean => renderedFields.some(field => field.key === key && field.kind === 'select'),\n [renderedFields],\n )\n\n /** Draft one field, cascading provider → model → effort like the Host catalog. */\n const updateDraft = useCallback((key: EditableKey, value: ConfigValue) => {\n if (panel === null) return\n const providerNow = String(\n draft.provider ?? panel.config.effective.provider ?? panel.config.base.provider ?? '',\n )\n setNotice(null)\n if (key === 'provider') {\n // Changing the provider invalidates the model/effort below it. When the\n // selected provider is the configured one, keep showing its configured\n // model; otherwise fall back to the inherit choice.\n const nextProvider = String(value ?? '')\n const configuredProvider = String(\n panel.config.effective.provider ?? panel.config.base.provider ?? '',\n )\n const keepModel = nextProvider === configuredProvider\n ? String(panel.config.effective.model ?? panel.config.base.model ?? '')\n : ''\n setDraft(previous => ({ ...previous, provider: value, model: '', reasoningEffort: '' }))\n if (catalog !== null && isSelectField('provider')) void refreshCatalog(nextProvider, keepModel)\n return\n }\n if (key === 'model') {\n setDraft(previous => ({ ...previous, model: value, reasoningEffort: '' }))\n if (catalog !== null && isSelectField('model')) void refreshCatalog(providerNow, String(value ?? ''))\n return\n }\n setDraft(previous => ({ ...previous, [key]: value }))\n }, [catalog, draft.provider, isSelectField, panel, refreshCatalog])\n\n const refreshStatus = useCallback(async (): Promise<void> => {\n try {\n const next = await fetchStatus()\n setStatus(next)\n setPollError(null)\n } catch (error) {\n setPollError(messageOf(error))\n }\n }, [])\n\n /**\n * Manual refresh: reload the whole panel payload so access lists, the cleanup\n * preview and `restartPendingKeys` catch up with the Host. The dirty draft is\n * deliberately preserved — refreshing never discards user input.\n */\n const onRefresh = useCallback(async (): Promise<void> => {\n try {\n const next = await fetchPanel()\n setLoad({ phase: 'ready', panel: next })\n setStatus(next.status)\n setPollError(null)\n setAccessNotice(null)\n setAccessError(null)\n } catch (error) {\n setPollError(messageOf(error))\n }\n }, [])\n\n /** Submit every dirty field at once; the Host persists and re-validates. */\n const onSave = useCallback(async (): Promise<void> => {\n if (panel === null) return\n const { values, issues } = validateDraft(panel, draft)\n if (issues.length > 0) {\n // Name the offending fields: with one draft shared by every tab, the\n // user may be looking at a different tab than the invalid field.\n const detail = issues\n .map(issue => `${fieldLabel(panel.config.fields, issue.key as EditableKey)} ${issue.message}`)\n .join(';')\n setNotice({ kind: 'error', text: `${PANEL_COPY.saveInvalid}:${detail}`, issues })\n return\n }\n if (Object.keys(values).length === 0) return\n setBusy('save')\n setNotice(null)\n try {\n const result = await saveConfig(values)\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error, issues: result.issues })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setCleanupResult(null)\n setCleanupError(null)\n // Clear only the submitted keys: edits typed while the request was in\n // flight stay in the draft instead of being silently discarded.\n setDraft(previous => {\n const next = { ...previous }\n for (const key of Object.keys(values) as EditableKey[]) delete next[key]\n return next\n })\n setNotice({ kind: 'ok', text: PANEL_COPY.saved })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [draft, panel])\n\n /** Drop the draft keys owned by one tab (nothing is written). */\n const discardTab = useCallback((tab: PanelTabId): void => {\n if (panel === null) return\n const groups = new Set<string>(TAB_FIELD_GROUPS[tab])\n const keys = new Set(\n panel.config.fields.filter(field => groups.has(field.group)).map(field => field.key),\n )\n setDraft((previous) => {\n const next = { ...previous }\n for (const key of keys) delete next[key]\n return next\n })\n setNotice(null)\n if (tab === 'model') {\n catalogTicket.current += 1\n setCatalog(catalogFromFields(panel.config.fields))\n setCatalogBusy(false)\n }\n }, [panel])\n\n /**\n * Open the host's native directory chooser (workspace root「选择目录…」) and\n * stage the picked absolute path as a draft value. Nothing is persisted until\n *「保存更改」; a canceled dialog is a no-op, not an error.\n */\n const onPickDirectory = useCallback(async (key: EditableKey): Promise<void> => {\n setNotice(null)\n try {\n const result = await requestDirectoryPicker()\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error })\n return\n }\n if ('canceled' in result) return\n setDraft(previous => ({ ...previous, [key]: result.path }))\n setNotice({ kind: 'ok', text: `${PANEL_COPY.directoryPicked}:${result.path}` })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n }\n }, [])\n\n /** Drop every Web override and return the refreshed panel. */\n const onClear = useCallback(async (): Promise<void> => {\n setBusy('clear')\n setNotice(null)\n try {\n const result = await clearConfig()\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setDraft({})\n setCleanupResult(null)\n setCleanupError(null)\n setNotice({ kind: 'ok', text: PANEL_COPY.cleared })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [])\n\n const running = (status ?? panel?.status)?.running === true\n\n /**\n * The single connect/reconnect action: open a stopped channel or restart a\n * running one. Only a restart interrupts live replies, so only it confirms.\n */\n const onReconnect = useCallback(async (): Promise<void> => {\n if (running) {\n const confirmed = typeof window.confirm === 'function'\n ? window.confirm(PANEL_COPY.restartWarning)\n : true\n if (!confirmed) return\n }\n setBusy('reconnect')\n setNotice(null)\n try {\n const result = await reconnect()\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error })\n // Incomplete credentials: send the user back to the onboarding form.\n if (result.unconfigured === true) setActiveTab('connection')\n await refreshStatus()\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setNotice({ kind: 'ok', text: running ? PANEL_COPY.reconnectDone : PANEL_COPY.connectDone })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n await refreshStatus()\n } finally {\n setBusy(null)\n }\n }, [refreshStatus, running])\n\n /**\n * Delete stale workspaces after an explicit confirmation. The Host refreshes\n * the panel in the same response, so the preview counts update immediately;\n * partial failures stay visible in the card instead of being swallowed.\n */\n const onRunCleanup = useCallback(async (): Promise<void> => {\n if (panel === null) return\n const { cleanup } = panel\n const confirmed = typeof window.confirm === 'function'\n ? window.confirm(\n `${PANEL_COPY.cleanupConfirm}\\n${PANEL_COPY.cleanupCandidates}:${cleanup.candidates} · ${PANEL_COPY.cleanupCandidateBytes}:${formatBytes(cleanup.candidateBytes)}`,\n )\n : true\n if (!confirmed) return\n setBusy('cleanup')\n setNotice(null)\n setCleanupResult(null)\n setCleanupError(null)\n try {\n const response = await runCleanup()\n if (!response.ok) {\n setCleanupError(response.error)\n return\n }\n setLoad({ phase: 'ready', panel: response.panel })\n setStatus(response.panel.status)\n setCleanupResult(response.result)\n } catch (error) {\n setCleanupError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [panel])\n\n /** Replace the embedded access snapshot after a whitelist mutation. */\n const applyAccess = useCallback((access: AccessWire): void => {\n setLoad(previous => previous.phase === 'ready'\n ? { phase: 'ready', panel: { ...previous.panel, access } }\n : previous)\n }, [])\n\n const onAddAccessGroup = useCallback(async (chatId: string, name: string): Promise<boolean> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await addAccessGroup(chatId, name)\n if (!result.ok) {\n setAccessError(result.error)\n return false\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessGroupAdded)\n return true\n } catch (error) {\n setAccessError(messageOf(error))\n return false\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onRemoveAccessGroup = useCallback(async (chatId: string): Promise<void> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await removeAccessGroup(chatId)\n if (!result.ok) {\n setAccessError(result.error)\n return\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessGroupRemoved)\n } catch (error) {\n setAccessError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onAddAccessUser = useCallback(async (openId: string, name: string): Promise<boolean> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await addAccessUser(openId, name)\n if (!result.ok) {\n setAccessError(result.error)\n return false\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessUserAdded)\n return true\n } catch (error) {\n setAccessError(messageOf(error))\n return false\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onRemoveAccessUser = useCallback(async (openId: string): Promise<void> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await removeAccessUser(openId)\n if (!result.ok) {\n setAccessError(result.error)\n return\n }\n applyAccess(result.access)\n setAccessNotice(PANEL_COPY.accessUserRemoved)\n } catch (error) {\n setAccessError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n const onResolveAccessRequest = useCallback(async (token: string, decision: 'allow' | 'deny'): Promise<void> => {\n setBusy('access')\n setAccessError(null)\n setAccessNotice(null)\n try {\n const result = await resolveAccessRequest(token, decision)\n if (!result.ok) {\n setAccessError(result.error)\n return\n }\n applyAccess(result.access)\n setAccessNotice(decision === 'allow' ? PANEL_COPY.accessRequestAllowed : PANEL_COPY.accessRequestDenied)\n } catch (error) {\n setAccessError(messageOf(error))\n } finally {\n setBusy(null)\n }\n }, [applyAccess])\n\n /** Save provided credential values; the Host reconnects automatically when ready. */\n const onSaveCredentials = useCallback(async (): Promise<void> => {\n const values: { appId?: string; appSecret?: string } = {}\n if (appIdInput.trim() !== '') values.appId = appIdInput\n if (appSecretInput.trim() !== '') values.appSecret = appSecretInput\n if (values.appId === undefined && values.appSecret === undefined) return\n setBusy('credentials')\n setNotice(null)\n try {\n const result = await saveCredentials(values)\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error, issues: result.issues })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setAppIdInput('')\n setAppSecretInput('')\n const base = result.reconnect === 'pending'\n ? PANEL_COPY.credentialSavedPending\n : result.reconnect === 'auto'\n ? PANEL_COPY.credentialSavedReconnected\n : result.reconnect === 'unconfigured'\n ? PANEL_COPY.credentialSavedUnconfigured\n : result.reconnect === 'failed'\n ? PANEL_COPY.credentialAutoConnectFailed\n : PANEL_COPY.credentialSaved\n setNotice({\n kind: result.reconnect === 'failed' || result.autoReconnectError !== undefined ? 'error' : 'ok',\n text: result.autoReconnectError === undefined\n ? base\n : `${base}:${result.autoReconnectError}`,\n })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [appIdInput, appSecretInput])\n\n const onClearCredential = useCallback(async (field: CredentialField): Promise<void> => {\n const confirmed = typeof window.confirm === 'function'\n ? window.confirm(PANEL_COPY.credentialRemoveConfirm)\n : true\n if (!confirmed) return\n setBusy('credentials-clear')\n setNotice(null)\n try {\n const result = await clearCredential(field)\n if (!result.ok) {\n setNotice({ kind: 'error', text: result.error, issues: result.issues })\n return\n }\n setLoad({ phase: 'ready', panel: result.panel })\n setStatus(result.panel.status)\n setNotice({\n kind: 'ok',\n text: result.reconnect === 'pending' ? PANEL_COPY.credentialRemovedPending : PANEL_COPY.credentialRemoved,\n })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(null)\n }\n }, [])\n\n const issues = useMemo<FieldIssueWire[]>(\n () => (notice?.kind === 'error' ? notice.issues ?? [] : []),\n [notice],\n )\n const dirtyCount = Object.keys(draft).length\n\n /** Which tabs currently own a dirty key (the dot is per tab, not global). */\n const dirtyTabs = useMemo<ReadonlySet<PanelTabId>>(() => {\n const tabs = new Set<PanelTabId>()\n if (panel === null || dirtyCount === 0) return tabs\n const groupOf = new Map(panel.config.fields.map(field => [field.key, field.group]))\n for (const key of Object.keys(draft)) {\n const group = groupOf.get(key as EditableKey)\n if (group === undefined) continue\n for (const tab of PANEL_TABS) {\n if (TAB_FIELD_GROUPS[tab.id].includes(group)) tabs.add(tab.id)\n }\n }\n return tabs\n }, [dirtyCount, draft, panel])\n\n const form = useMemo<FormView | null>(() => {\n if (panel === null) return null\n return {\n panel,\n fields: renderedFields,\n draft,\n issues,\n busy,\n locked: hostBusy,\n dirtyCount,\n onChange: updateDraft,\n onSave: () => { void onSave() },\n onDiscardTab: discardTab,\n onPickDirectory,\n }\n }, [busy, discardTab, dirtyCount, draft, hostBusy, issues, onPickDirectory, onSave, panel, renderedFields, updateDraft])\n\n const credentials = useMemo<CredentialsView>(() => ({\n appId: appIdInput,\n appSecret: appSecretInput,\n issues,\n busy,\n locked: hostBusy,\n onAppIdChange: setAppIdInput,\n onAppSecretChange: setAppSecretInput,\n onSave: () => { void onSaveCredentials() },\n onClear: (field: CredentialField) => { void onClearCredential(field) },\n }), [appIdInput, appSecretInput, busy, hostBusy, issues, onClearCredential, onSaveCredentials])\n\n if (load.phase === 'loading') {\n return (\n <div className=\"dsh-channels-cfg\">\n <div className=\"dsh-channels-cfg__loading\">{PANEL_COPY.loading}</div>\n </div>\n )\n }\n if (load.phase === 'error') {\n return (\n <div className=\"dsh-channels-cfg\">\n <div className=\"dsh-channels-cfg__error\">\n <strong>{PANEL_COPY.loadFailed}</strong>\n <span>{load.message}</span>\n <div>\n <button type=\"button\" className=\"dsh-channels-cfg__btn\" onClick={() => { void loadPanel() }}>\n {PANEL_COPY.retry}\n </button>\n </div>\n </div>\n </div>\n )\n }\n if (form === null) return null\n const current = status ?? load.panel.status\n\n return (\n <div className=\"dsh-channels-cfg\">\n <PanelHeader\n status={current}\n pollError={pollError}\n busy={busy}\n locked={hostBusy}\n onRefresh={() => { void onRefresh() }}\n />\n <TabNav activeTab={activeTab} dirtyTabs={dirtyTabs} onChange={setActiveTab} />\n {notice === null ? null : <NoticeBar notice={notice} />}\n <div\n role=\"tabpanel\"\n id={`dsh-channels-tabpanel-${activeTab}`}\n aria-labelledby={`dsh-channels-tab-${activeTab}`}\n >\n {activeTab === 'connection'\n ? (\n <ConnectionTab\n panel={form.panel}\n status={current}\n busy={busy}\n credentials={credentials}\n onReconnect={() => { void onReconnect() }}\n />\n )\n : null}\n {activeTab === 'sessions' ? <SessionsTab form={form} /> : null}\n {activeTab === 'model' ? <ModelTab form={form} catalogBusy={catalogBusy} /> : null}\n {activeTab === 'workspace'\n ? (\n <WorkspaceTab\n form={form}\n cleanup={form.panel.cleanup}\n cleanupResult={cleanupResult}\n cleanupError={cleanupError}\n onRunCleanup={() => { void onRunCleanup() }}\n />\n )\n : null}\n {activeTab === 'access'\n ? (\n <AccessTab\n form={form}\n accessNotice={accessNotice}\n accessError={accessError}\n onAddGroup={onAddAccessGroup}\n onRemoveGroup={chatId => { void onRemoveAccessGroup(chatId) }}\n onAddUser={onAddAccessUser}\n onRemoveUser={openId => { void onRemoveAccessUser(openId) }}\n onResolveRequest={(token, decision) => { void onResolveAccessRequest(token, decision) }}\n />\n )\n : null}\n {activeTab === 'advanced'\n ? <AdvancedTab form={form} onClear={() => { void onClear() }} />\n : null}\n </div>\n </div>\n )\n}\n\n/** Page header: title + phase badge + refresh, visible on every tab. */\nfunction PanelHeader(props: {\n status: ChannelStatusWire\n pollError: string | null\n busy: BusyAction | null\n locked: boolean\n onRefresh: () => void\n}): JSX.Element {\n const { status, pollError, busy, locked, onRefresh } = props\n const badge = phaseBadge(status.phase)\n return (\n <header className=\"dsh-channels-cfg__panelHead\" title={PANEL_COPY.description}>\n <div className=\"dsh-channels-cfg__headRow\">\n <h2 className=\"dsh-channels-cfg__title\">{PANEL_COPY.title}</h2>\n <span className={`dsh-channels-cfg__badge dsh-channels-cfg__badge--${badge.tone}`}>\n <span className=\"dsh-channels-cfg__dot\" />\n {badge.text}\n </span>\n <span className=\"dsh-channels-cfg__spacer\" />\n <button\n type=\"button\"\n className=\"dsh-channels-cfg__btn\"\n onClick={onRefresh}\n disabled={busy !== null || locked}\n >\n {PANEL_COPY.refresh}\n </button>\n </div>\n <p className=\"dsh-channels-cfg__summary\">{phaseSummary(status.phase)}</p>\n {pollError === null\n ? null\n : (\n <span className=\"dsh-channels-cfg__fieldError\" role=\"status\" aria-live=\"polite\">\n {PANEL_COPY.pollFailed}:{pollError}\n </span>\n )}\n </header>\n )\n}\n\n/** Top-level tab navigation; the dirty dot keeps unsaved work visible per tab. */\nfunction TabNav(props: {\n activeTab: PanelTabId\n dirtyTabs: ReadonlySet<PanelTabId>\n onChange: (tab: PanelTabId) => void\n}): JSX.Element {\n const { activeTab, dirtyTabs, onChange } = props\n const refs = useRef<Array<HTMLButtonElement | null>>([])\n\n /** Roving-focus keyboard navigation between tabs (WAI-ARIA tabs pattern). */\n const onKeyDown = (index: number, event: KeyboardEvent<HTMLButtonElement>): void => {\n const last = PANEL_TABS.length - 1\n let next: number\n switch (event.key) {\n case 'ArrowRight': next = index === last ? 0 : index + 1; break\n case 'ArrowLeft': next = index === 0 ? last : index - 1; break\n case 'Home': next = 0; break\n case 'End': next = last; break\n default: return\n }\n event.preventDefault()\n onChange(PANEL_TABS[next]!.id)\n refs.current[next]?.focus()\n }\n\n return (\n <nav className=\"dsh-channels-cfg__tabs\" role=\"tablist\" aria-label={PANEL_COPY.title}>\n {PANEL_TABS.map((tab, index) => {\n const selected = tab.id === activeTab\n const dirty = dirtyTabs.has(tab.id)\n return (\n <button\n key={tab.id}\n ref={(node) => { refs.current[index] = node }}\n type=\"button\"\n role=\"tab\"\n id={`dsh-channels-tab-${tab.id}`}\n aria-selected={selected}\n aria-controls={selected ? `dsh-channels-tabpanel-${tab.id}` : undefined}\n tabIndex={selected ? 0 : -1}\n className=\"dsh-channels-cfg__tab\"\n onClick={() => onChange(tab.id)}\n onKeyDown={event => onKeyDown(index, event)}\n >\n {tab.label}\n {dirty\n ? (\n <span\n className=\"dsh-channels-cfg__tabDot\"\n title={PANEL_COPY.dirtyCount}\n aria-label={PANEL_COPY.dirtyCount}\n />\n )\n : null}\n </button>\n )\n })}\n </nav>\n )\n}\n","/**\n * Settings panel styling. DSH semantic tokens (`--dsw-alias-*`) with neutral\n * fallbacks so the page follows the active theme; only pseudo-classes and\n * responsive rules live here (everything else is inline in the component).\n *\n * @module dsh-channels/client/styles\n */\n\nconst STYLE_ATTRIBUTE = 'data-dsh-channels-css'\n\nconst CSS = `\n.dsh-channels-cfg{container-type:inline-size;display:flex;flex-direction:column;gap:12px;width:100%;max-width:720px;color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;line-height:20px}\n.dsh-channels-cfg__panelHead{position:sticky;top:0;z-index:2;display:flex;flex-direction:column;gap:4px;padding:10px 0 8px;background:var(--dsw-alias-bg-base,#fff);border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dsh-channels-cfg__headRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__title{margin:0;font-size:16px;font-weight:500;line-height:24px}\n.dsh-channels-cfg__summary{margin:0;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:18px}\n.dsh-channels-cfg__tabs{display:flex;align-items:center;gap:2px;flex-wrap:wrap;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dsh-channels-cfg__tab{display:inline-flex;align-items:center;gap:5px;min-height:32px;padding:0 10px;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#5c6169);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.dsh-channels-cfg__tab:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dsh-channels-cfg__tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0a67d3);outline-offset:-2px}\n.dsh-channels-cfg__tab[aria-selected=\"true\"]{color:var(--dsw-alias-state-business-primary,#0a67d3);border-bottom-color:currentColor;font-weight:500}\n.dsh-channels-cfg__tabDot{width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#a15c00);flex:none}\n.dsh-channels-cfg__tabBody{display:flex;flex-direction:column;gap:12px}\n.dsh-channels-cfg__tabHelp{margin:0;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:18px}\n.dsh-channels-cfg__card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:12px}\n.dsh-channels-cfg__cardHead{display:flex;align-items:center;gap:8px;min-height:20px;flex-wrap:wrap}\n.dsh-channels-cfg__cardTitle{font-size:13px;font-weight:500}\n.dsh-channels-cfg__cardMeta{margin-left:auto;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px}\n.dsh-channels-cfg__group{display:flex;flex-direction:column;gap:8px}\n.dsh-channels-cfg__badge{display:inline-flex;align-items:center;gap:5px;padding:1px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));font-size:12px;line-height:18px;white-space:nowrap}\n.dsh-channels-cfg__dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}\n.dsh-channels-cfg__badge--ok{color:var(--dsw-alias-state-business-primary,#0a7d33);border-color:currentColor}\n.dsh-channels-cfg__badge--warn{color:var(--dsw-alias-state-warning-primary,#a15c00);border-color:currentColor}\n.dsh-channels-cfg__badge--bad{color:var(--dsw-alias-state-danger-primary,#c62828);border-color:currentColor}\n.dsh-channels-cfg__badge--muted{color:var(--dsw-alias-label-tertiary,#8a8f98)}\n.dsh-channels-cfg__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px 16px}\n.dsh-channels-cfg__fact{display:flex;flex-direction:column;gap:2px;min-width:0}\n.dsh-channels-cfg__factLabel{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:16px}\n.dsh-channels-cfg__factValue{font-variant-numeric:tabular-nums;overflow-wrap:anywhere}\n.dsh-channels-cfg__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-height:28px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.dsh-channels-cfg__btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05))}\n.dsh-channels-cfg__btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0a67d3);outline-offset:2px}\n.dsh-channels-cfg__btn:disabled{color:var(--dsw-alias-label-dimmed,#b0b4ba);cursor:default}\n.dsh-channels-cfg__btn--primary{background:var(--dsw-alias-button-primary-fill,#0a67d3);border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}\n.dsh-channels-cfg__btn--primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#0b5cba)}\n.dsh-channels-cfg__btn--danger{color:var(--dsw-alias-state-danger-primary,#c62828);border-color:currentColor}\n.dsh-channels-cfg__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 20px;align-items:start}\n.dsh-channels-cfg__field{display:flex;flex-direction:column;gap:4px;min-width:0}\n.dsh-channels-cfg__field--wide{grid-column:1/-1}\n.dsh-channels-cfg__labelRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}\n.dsh-channels-cfg__label{font-size:12px;color:var(--dsw-alias-label-secondary,#5c6169)}\n.dsh-channels-cfg__tag{display:inline-flex;align-items:center;padding:0 5px;border-radius:4px;border:1px dashed currentColor;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#8a8f98);white-space:nowrap}\n.dsh-channels-cfg__tag--restart{color:var(--dsw-alias-state-warning-primary,#a15c00)}\n.dsh-channels-cfg__tag--dirty{color:var(--dsw-alias-state-warning-primary,#a15c00);border-style:solid}\n.dsh-channels-cfg__help{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:11px;line-height:16px}\n.dsh-channels-cfg__reasonBad{color:var(--dsw-alias-state-danger-primary,#c62828)}\n.dsh-channels-cfg__input{box-sizing:border-box;width:100%;min-height:28px;padding:3px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;font:inherit;font-size:13px;outline:none}\n.dsh-channels-cfg__inputRow{display:flex;align-items:center;gap:8px;min-width:0}\n.dsh-channels-cfg__inputRow .dsh-channels-cfg__input{flex:1 1 auto;width:auto;min-width:0}\n.dsh-channels-cfg__btn--browse{flex:none}\n.dsh-channels-cfg__input:focus{border-color:var(--dsw-alias-state-business-primary,#0a67d3)}\n.dsh-channels-cfg__input--invalid{border-color:var(--dsw-alias-state-danger-primary,#c62828)}\nselect.dsh-channels-cfg__input{appearance:auto;cursor:pointer}\n.dsh-channels-cfg__check{display:inline-flex;align-items:center;gap:6px;min-height:28px;cursor:pointer;font-size:13px}\n.dsh-channels-cfg__checkbox{width:15px;height:15px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0a67d3);cursor:pointer}\n.dsh-channels-cfg__fieldError{color:var(--dsw-alias-state-danger-primary,#c62828);font-size:11px;line-height:16px}\n.dsh-channels-cfg__notice{display:flex;flex-direction:column;gap:2px;padding:8px 10px;border-radius:8px;font-size:12px;line-height:18px;overflow-wrap:anywhere}\n.dsh-channels-cfg__notice--ok{color:var(--dsw-alias-state-business-primary,#0a7d33);background:color-mix(in srgb,currentColor 8%,transparent)}\n.dsh-channels-cfg__notice--error{color:var(--dsw-alias-state-danger-primary,#c62828);background:color-mix(in srgb,currentColor 8%,transparent)}\n.dsh-channels-cfg__banner{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 10px;border-radius:8px;border:1px dashed var(--dsw-alias-state-warning-primary,#a15c00);color:var(--dsw-alias-state-warning-primary,#a15c00);font-size:12px;line-height:18px}\n.dsh-channels-cfg__footer{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:2px}\n.dsh-channels-cfg__spacer{flex:1}\n.dsh-channels-cfg__steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}\n.dsh-channels-cfg__step{display:flex;align-items:flex-start;gap:10px}\n.dsh-channels-cfg__stepIndex{display:inline-flex;align-items:center;justify-content:center;flex:none;width:20px;height:20px;border-radius:50%;border:1px solid currentColor;color:var(--dsw-alias-state-business-primary,#0a67d3);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}\n.dsh-channels-cfg__stepBody{display:flex;flex-direction:column;gap:2px;min-width:0}\n.dsh-channels-cfg__stepTitle{font-size:13px;font-weight:500}\n.dsh-channels-cfg__emptyState{display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px dashed var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:18px}\n.dsh-channels-cfg__readOnly{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 20px}\n.dsh-channels-cfg__kv{display:flex;flex-direction:column;gap:2px;min-width:0}\n.dsh-channels-cfg__kvKey{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px;line-height:16px}\n.dsh-channels-cfg__kvValue{overflow-wrap:anywhere}\n.dsh-channels-cfg__mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}\n.dsh-channels-cfg__cred{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__cred .dsh-channels-cfg__input{flex:1 1 240px;min-width:0;width:auto}\n.dsh-channels-cfg__accessSection{display:flex;flex-direction:column;gap:6px}\n.dsh-channels-cfg__accessList{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}\n.dsh-channels-cfg__accessRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px}\n.dsh-channels-cfg__accessMain{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1 1 200px;overflow-wrap:anywhere}\n.dsh-channels-cfg__accessMeta{color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:11px;line-height:16px;overflow-wrap:anywhere}\n.dsh-channels-cfg__accessActions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.dsh-channels-cfg__accessAdd{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-channels-cfg__accessInput{flex:1 1 140px;min-width:0;width:auto}\n.dsh-channels-cfg__error{padding:10px 12px;border:1px solid var(--dsw-alias-state-danger-primary,#c62828);border-radius:10px;color:var(--dsw-alias-state-danger-primary,#c62828);font-size:12px;line-height:18px;display:flex;flex-direction:column;gap:8px}\n.dsh-channels-cfg__loading{padding:16px 0;color:var(--dsw-alias-label-tertiary,#8a8f98);font-size:12px}\n.dsh-channels-cfg__issues{margin:0;padding-left:18px}\n@container (max-width:560px){\n.dsh-channels-cfg__facts{grid-template-columns:repeat(2,minmax(0,1fr))}\n.dsh-channels-cfg__grid,.dsh-channels-cfg__readOnly{grid-template-columns:minmax(0,1fr)}\n.dsh-channels-cfg__tabs{flex-wrap:nowrap;overflow-x:auto}\n}\n`\n\n/** Append the panel stylesheet once (idempotent across HMR / remounts). */\nexport function installStyles(): () => void {\n const existing = document.querySelector(`style[${STYLE_ATTRIBUTE}]`)\n if (existing !== null) return () => {}\n const style = document.createElement('style')\n style.setAttribute(STYLE_ATTRIBUTE, '')\n style.textContent = CSS\n document.head.append(style)\n return () => {\n style.remove()\n }\n}\n","/**\n * dsh-channels client half: 设置 →「飞书渠道」分区。\n *\n * 只依赖平台核心的 `slots` 服务与 react(bundle 里 react 走平台模块表,\n * 其余全部内联)。页面数据来自 Host 半侧的 `/dsh-channels/*` 路由,因此\n * 不需要 typert 契约,也不 import 任何 DSH 客户端包。\n *\n * @module dsh-channels/client\n */\n\nimport { PANEL_COPY } from '../settings/contract.ts'\nimport { FeishuChannelsSection } from './panel/index.tsx'\nimport { installStyles } from './styles.ts'\n\n// 平台 client runtime 的最小结构声明:只声明本 bundle 用到的两个能力。\n// 与安装的 DSH Web 版本(0.2.x)的 slots 运行时 API 一致。\nexport interface ClientSlots {\n inject(slot: string, contribution: () => unknown): void\n register(\n declaration: {\n name: string\n id: string\n order?: number\n label: () => string\n },\n component: (props: { close?: () => void }) => unknown,\n ): () => void\n}\n\nexport interface ClientContext {\n slots: ClientSlots\n effect?(callback: () => void | (() => void), label?: string): void\n}\n\n/** Cordis client plugin name. */\nexport const name = 'dsh-channels'\n\n/** Required client services: the Slots registry is part of the web shell core. */\nexport const inject = ['slots']\n\n/** Section id and nav order (built-ins: account -10, general 0, models 10, plugins 15, agent-presets 20). */\nexport const SETTINGS_SECTION_ID = 'feishu-channels'\nexport const SETTINGS_SECTION_ORDER = 40\n\n/**\n * Mount the settings section and its stylesheet.\n * @param ctx - client plugin context carrying the slots registry.\n */\nexport function apply(ctx: ClientContext): void {\n if (typeof ctx.effect === 'function') {\n ctx.effect(() => installStyles(), 'dsh-channels: settings styles')\n } else {\n installStyles()\n }\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: SETTINGS_SECTION_ID,\n order: SETTINGS_SECTION_ORDER,\n label: () => PANEL_COPY.title,\n }, FeishuChannelsSection))\n}\n"],"mappings":"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"}
|