@modesto_ma/dsh-guanxiang 0.0.1-beta.1 → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/lib/client.js +2 -2
- package/lib/client.js.map +1 -1
- package/lib/index.js +283 -99
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/page.tsx +2 -2
- package/src/config.ts +8 -2
- package/src/dispatch/tool.ts +1 -1
- package/src/repos/manifest.ts +32 -9
- package/src/repos/registry.ts +206 -79
- package/src/service.ts +22 -1
- package/src/settings/contract.ts +6 -6
- package/src/skill/entry.ts +9 -4
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
用户:/guanxiang 订单服务昨晚 21 点下单报 500,日志见附件
|
|
49
49
|
│
|
|
50
50
|
▼
|
|
51
|
-
入口 SKILL(意图识别)──
|
|
51
|
+
入口 SKILL(意图识别)── 先 fetch 各仓库,应用目录由远端最新提交的 .dsh/guanxiang.yaml 生成
|
|
52
52
|
│ guanxiang_dispatch({ tasks: [{ app, question, logs }] })
|
|
53
53
|
▼
|
|
54
54
|
派发工具:校验应用 id → 下载 URL 日志 → 并行启动子 Agent,等待全部结束
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
- **一个入口。** 插件只提供用户斜杠入口 `/guanxiang`(模型不会自行加载)。dsh 在 Web、TUI、ACP 以及经 dsh_channels 的飞书消息中都能识别它。
|
|
61
61
|
- **派发由代码完成。** 模型只给出应用 id、问题和日志位置;仓库目录、应用 SKILL、并发上限都由插件决定。应用 id 必须与注册表一一对应,否则整次派发被拒绝。
|
|
62
62
|
- **日志一律以绝对路径交给子 Agent。** 附件和本机文件直接传路径;消息里的对象存储下载链接由派发工具下载(只允许白名单域名,不带鉴权,限制大小与耗时)。日志包不解压,由应用的日志分析 SKILL 在运行目录里解压。
|
|
63
|
+
- **应用目录随时最新。** 每次加载 `/guanxiang` 都先 `git fetch` 各仓库(间隔由 `fetchIntervalMs` 控制,默认每次),再从远端分支最新提交的 git 对象读取应用描述生成应用目录。fetch 不改动任何检出,所以正在进行的分析不会挡住更新。最多等待 `refreshWaitMs`,超时就先用已有的目录,fetch 在后台继续。
|
|
64
|
+
- **代码快照不可变。** 子 Agent 在 `<reposDir>/.snapshots/<仓库名>/` 下按提交创建的 `git worktree` 快照里工作。快照创建后不再改动,同一提交的派发共用一份;没有分析在用、且已不是最新提交的快照会被删除。
|
|
63
65
|
- **运行目录。** 每次派发在父会话目录下创建 `.guanxiang/runs/<runId>/<序号>-<应用 id>/`,下载的日志放在这里,子 Agent 也只在这里写文件;应用仓库对子 Agent 只读。
|
|
64
66
|
- **等待式派发。** 工具等待所有子 Agent 结束后一次返回,一条消息对应一张完整回复;超过派发超时或调用被取消时停止仍在运行的子 Agent。子会话中不提供派发工具。
|
|
65
67
|
|
|
@@ -91,7 +93,7 @@ npm 发行包已包含构建产物;本地 `link:` 安装前必须先执行 `pn
|
|
|
91
93
|
- url: git@github.com:example/order-service.git
|
|
92
94
|
- url: https://github.com/example/pay-service.git
|
|
93
95
|
branch: release # 可选,默认跟随远端默认分支
|
|
94
|
-
fetchIntervalMs:
|
|
96
|
+
fetchIntervalMs: 0 # 加载 /guanxiang 时距上次拉取超过该时间的仓库先 fetch;0 = 每次
|
|
95
97
|
maxAppsPerRequest: 4 # 单次派发的任务上限
|
|
96
98
|
dispatchTimeoutMs: 1800000 # 等待全部子 Agent 的上限
|
|
97
99
|
download:
|
|
@@ -100,12 +102,15 @@ npm 发行包已包含构建产物;本地 `link:` 安装前必须先执行 `pn
|
|
|
100
102
|
timeoutMs: 600000
|
|
101
103
|
subagentProvider: spawn # 子 Agent 使用的 ctx.subagents provider
|
|
102
104
|
gitTimeoutMs: 600000 # 单条 git 命令的超时
|
|
105
|
+
refreshWaitMs: 15000 # 加载 /guanxiang 时等待 fetch 的上限;0 = 不等待
|
|
103
106
|
```
|
|
104
107
|
|
|
105
108
|
配置在加载时校验,不合法时插件加载失败。git 地址支持 https/http/ssh/git/file URL、`user@host:path` 和绝对路径,拉取时使用本机已有的 git 凭据(SSH key 或 credential helper),插件不保存密码或 token,也不会弹出交互式输入。
|
|
106
109
|
|
|
107
110
|
子 Agent 的命令在 dsh 的文件沙箱中运行:仓库只读,只有父会话的项目目录可写,运行目录就在其中。Linux 的 bubblewrap 沙箱会把 `/tmp` 换成空的临时目录,所以 `reposDir` 不要放在 `/tmp` 下,否则子 Agent 的命令看不到仓库。
|
|
108
111
|
|
|
112
|
+
拉取目录 `<reposDir>/<仓库名>` 只用来 fetch,新克隆不检出文件(旧版本留下的检出文件不再使用)。多个同时运行的 dsh 进程不要共用同一个 `reposDir`:进程启动后首次同步时会删除快照目录里的遗留快照。
|
|
113
|
+
|
|
109
114
|
### 设置面板
|
|
110
115
|
|
|
111
116
|
DSH Web 左侧栏的「观象」入口打开整页设置面板:
|
|
@@ -135,7 +140,7 @@ skill: log-analysis # 对应 .dsh/skills/log-analysis/SKILL.md
|
|
|
135
140
|
|
|
136
141
|
`.dsh/skills/<skill>/SKILL.md`:这个应用的日志分析流程(如何解压日志包、日志格式、代码结构、常见问题)。它不能设置 `user-invocable: false`,因为派发时用 `/<skill>` 加载。
|
|
137
142
|
|
|
138
|
-
插件拉取仓库后检查这两个文件;缺失、不合法,或多个仓库声明同一个 id
|
|
143
|
+
插件拉取仓库后检查这两个文件;缺失、不合法,或多个仓库声明同一个 id 时,相关应用不可用,原因显示在设置面板。应用描述和 SKILL 必须是普通文件,不能是符号链接。删除地址不会删除本地克隆,它的快照在没有分析使用后删除。
|
|
139
144
|
|
|
140
145
|
## 通过飞书使用
|
|
141
146
|
|
|
@@ -150,14 +155,14 @@ pnpm check # 类型检查(Host 与 Client)+ 单元测试 + 构建 +
|
|
|
150
155
|
|
|
151
156
|
## 发布
|
|
152
157
|
|
|
153
|
-
版本号由 semantic-release 按 Conventional Commits
|
|
158
|
+
版本号由 semantic-release 按 Conventional Commits 计算。推送到 `dev` 后,检查通过就自动发布 beta;正式版**只在手动触发时发布**,推送到 `main` 只跑检查。只有 `feat`、`fix` 或破坏性变更会产生新版本,其余类型的提交不发布。PR 标题需符合 `<type>(<scope>): <summary>`,type 取 `feat | fix | docs | test | refactor | chore | ci`。
|
|
154
159
|
|
|
155
|
-
| 分支 | 版本 | npm dist-tag |
|
|
156
|
-
| --- | --- | --- |
|
|
157
|
-
| `main` | 正式版,如 `0.1.0` | `latest` |
|
|
158
|
-
| `dev` | 预发布版,如 `0.1.0-beta.1` | `beta` |
|
|
160
|
+
| 分支 | 触发 | 版本 | npm dist-tag |
|
|
161
|
+
| --- | --- | --- | --- |
|
|
162
|
+
| `main` | 手动 | 正式版,如 `0.1.0` | `latest` |
|
|
163
|
+
| `dev` | 推送自动 | 预发布版,如 `0.1.0-beta.1` | `beta` |
|
|
159
164
|
|
|
160
|
-
|
|
165
|
+
发布正式版:在 GitHub Actions 的 `pipeline` workflow 点击 **Run workflow** 并选择 `main`。`dry_run` 默认勾选,只预览下一个版本号;取消勾选才真正发布 npm 包、打 tag 并创建 GitHub Release。在 `dev` 上手动触发同样可用,用来预览或补发 beta。安装 beta 版用 `@modesto_ma/dsh-guanxiang@beta`。
|
|
161
166
|
|
|
162
167
|
| 路径 | 内容 |
|
|
163
168
|
|---|---|
|
package/lib/client.js
CHANGED
|
@@ -559,7 +559,7 @@ window.__ModuleLoader__.load({
|
|
|
559
559
|
children: [saved ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
560
560
|
type: "button",
|
|
561
561
|
className: "gx__btn",
|
|
562
|
-
disabled: busy || current.phase === "syncing"
|
|
562
|
+
disabled: busy || current.phase === "syncing",
|
|
563
563
|
onClick: () => {
|
|
564
564
|
onSync(repo.url);
|
|
565
565
|
},
|
|
@@ -784,7 +784,7 @@ window.__ModuleLoader__.load({
|
|
|
784
784
|
})]
|
|
785
785
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
|
|
786
786
|
label: "更新间隔",
|
|
787
|
-
summary: "
|
|
787
|
+
summary: "输入 /guanxiang 时,距上次拉取超过该时间的仓库会先更新;0 表示每次都更新。",
|
|
788
788
|
unit: "分钟",
|
|
789
789
|
value: draft.fetchIntervalMin,
|
|
790
790
|
overridden: overridden.includes("fetchIntervalMs"),
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../src/client/icon.tsx","../src/settings/contract.ts","../src/client/api.ts","../src/client/page.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 左侧栏「观象」行的装饰图标;行本身负责可访问名称(列表注册的 label)。\n *\n * @module dsh-guanxiang/client/icon\n */\n\n/** Owner share of `sidebar.panellist`: requested square edge and selection state. */\nexport interface GuanxiangIconProps {\n size: number\n active: boolean\n}\n\n/**\n * Eye-over-waveform glyph drawn in `currentColor`.\n * @param props - the sidebar's icon share.\n * @returns decorative icon.\n */\nexport function GuanxiangIcon({ size }: GuanxiangIconProps): JSX.Element {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.6} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M2.5 10c2.4-3.6 5.6-5.4 9.5-5.4s7.1 1.8 9.5 5.4c-2.4 3.6-5.6 5.4-9.5 5.4S4.9 13.6 2.5 10Z\" />\n <circle cx=\"12\" cy=\"10\" r=\"2.4\" />\n <path d=\"M3 19.5h3l2-2.5 2.5 4 3-6 2.5 4.5 1.5-2h3.5\" />\n </svg>\n )\n}\n","/**\n * 设置面板与 Host 路由共用的路由常量和 JSON 结构。浏览器端 bundle 也会\n * 编译本文件,因此这里只放类型和常量,不 import 任何 Node 模块。\n *\n * @module dsh-guanxiang/settings/contract\n */\n\n/** GET:面板所需的全部状态(生效设置、默认值、仓库与应用)。 */\nexport const STATE_ROUTE = '/guanxiang/state'\n/** PUT:保存可编辑设置;DELETE:清除面板覆盖、恢复 cordis.yml 默认值。 */\nexport const SETTINGS_ROUTE = '/guanxiang/settings'\n/** POST:立即重新拉取全部仓库,或 body `{ url }` 指定的一个仓库。 */\nexport const SYNC_ROUTE = '/guanxiang/sync'\n\n/** One configured application repository. */\nexport interface RepoSpec {\n /** Git remote address, cloned with the host machine's existing git credentials. */\n url: string\n /** Remote branch to follow; omitted follows the remote's default branch. */\n branch?: string\n}\n\n/** Log URL download policy. */\nexport interface DownloadSettings {\n /** Allowed host names; `example.com` also admits its subdomains. Empty disables URL downloads. */\n allowedHosts: string[]\n /** Per-file byte ceiling. */\n maxBytes: number\n /** Per-file download timeout in milliseconds. */\n timeoutMs: number\n}\n\n/** The settings the Web panel can override; every field is required on the wire. */\nexport interface EditableSettings {\n /** Absolute directory holding the cloned repositories (`~` expands to the home directory). */\n reposDir: string\n /** Application repositories to clone and keep updated. */\n repos: RepoSpec[]\n /** Minimum age of a checkout before a dispatch updates it first, in milliseconds. */\n fetchIntervalMs: number\n /** Maximum number of child analyses one dispatch may start. */\n maxAppsPerRequest: number\n /** Whole-dispatch wait budget in milliseconds; children still running then are stopped. */\n dispatchTimeoutMs: number\n /** URL download policy. */\n download: DownloadSettings\n}\n\n/** Keys of {@link EditableSettings}, in panel order. */\nexport const EDITABLE_KEYS = ['reposDir', 'repos', 'fetchIntervalMs', 'maxAppsPerRequest', 'dispatchTimeoutMs', 'download'] as const satisfies readonly (keyof EditableSettings)[]\n\n/** One editable key. */\nexport type EditableKey = (typeof EDITABLE_KEYS)[number]\n\n/** Application description read from `<repo>/.dsh/guanxiang.yaml`. */\nexport interface AppManifest {\n /** Registry id the dispatch tool and analysis requests address. */\n id: string\n /** Display name, also the child agent's label. */\n name: string\n /** What the application does, used for intent recognition. */\n description: string\n /** Other names users call the application by. */\n aliases: string[]\n /** Strings that identify this application's log lines (tags, process names, class names). */\n logHints: string[]\n /** Project skill name under `.dsh/skills/<skill>/SKILL.md` that analyzes this application's logs. */\n skill: string\n}\n\n/** Lifecycle of one repository checkout. */\nexport type RepoPhase = 'pending' | 'syncing' | 'ready' | 'failed'\n\n/** One repository as the panel shows it. */\nexport interface RepoStatusWire {\n url: string\n branch?: string\n /** Absolute checkout directory. */\n dir: string\n phase: RepoPhase\n /** Checked-out commit, once a checkout succeeded. */\n commit?: string\n /** Committer date of {@link commit}, ISO 8601. */\n commitTime?: string\n /** Last completed sync attempt, epoch milliseconds. */\n syncedAt?: number\n /** Application description of a usable checkout. */\n app?: AppManifest\n /** Why the repository is unusable (`failed`), or why the last update failed while an older checkout stays usable (`ready`). */\n error?: string\n /** Whether the application is offered to intent recognition and dispatch. */\n available: boolean\n /** Child analyses currently running in this checkout. */\n activeRuns: number\n}\n\n/** `GET /guanxiang/state` response. */\nexport interface StateWire {\n /** Effective settings: cordis.yml values with panel overrides applied. */\n settings: EditableSettings\n /** cordis.yml values before panel overrides. */\n defaults: EditableSettings\n /** Keys the panel currently overrides. */\n overridden: EditableKey[]\n /** Why the overlay file was ignored (unreadable or invalid); panel saves replace it. */\n overlayError?: string\n repos: RepoStatusWire[]\n}\n\n/** Successful mutation response. */\nexport interface MutationOk {\n ok: true\n state: StateWire\n}\n\n/** Failed request response. */\nexport interface MutationError {\n ok: false\n error: string\n}\n","/**\n * 设置面板的同源 HTTP 客户端,直接请求 Host 半侧注册的 `/guanxiang/*` 路由。\n *\n * @module dsh-guanxiang/client/api\n */\n\nimport { SETTINGS_ROUTE, STATE_ROUTE, SYNC_ROUTE } from '../settings/contract.ts'\nimport type { EditableSettings, MutationError, MutationOk, StateWire } from '../settings/contract.ts'\n\n/**\n * Error text of a value thrown by a request.\n * @param error - thrown value.\n * @returns message.\n */\nexport function messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * Fetch one Host route. Routes are absolute pathnames, but the browser\n * addresses them relative to the DSH document so a reverse proxy that mounts\n * DSH under a prefix still routes them with its scoped login cookie.\n */\nasync function request<T>(route: string, init?: RequestInit): Promise<T> {\n let response: Response\n try {\n response = await fetch(route.slice(1), {\n cache: 'no-store',\n credentials: 'same-origin',\n ...init,\n headers: { accept: 'application/json', ...init?.body === undefined ? {} : { 'content-type': 'application/json' } },\n })\n } catch (error) {\n throw new Error(`无法连接设置接口:${messageOf(error)}`)\n }\n const text = await response.text()\n let body: unknown\n try {\n body = JSON.parse(text)\n } catch {\n // Non-JSON bodies (proxy errors) are reported by HTTP status below.\n throw new Error(`HTTP ${response.status}`)\n }\n if (!response.ok) {\n const error = (body as Partial<MutationError>).error\n throw new Error(typeof error === 'string' && error !== '' ? error : `HTTP ${response.status}`)\n }\n return body as T\n}\n\n/** @returns the panel state. */\nexport function fetchState(): Promise<StateWire> {\n return request<StateWire>(STATE_ROUTE)\n}\n\n/**\n * Save the complete editable settings.\n * @param settings - settings to persist.\n * @returns the new state.\n */\nexport async function saveSettings(settings: EditableSettings): Promise<StateWire> {\n return (await request<MutationOk>(SETTINGS_ROUTE, { method: 'PUT', body: JSON.stringify(settings) })).state\n}\n\n/** @returns the state after removing every panel override. */\nexport async function resetSettings(): Promise<StateWire> {\n return (await request<MutationOk>(SETTINGS_ROUTE, { method: 'DELETE' })).state\n}\n\n/**\n * Re-sync one repository or all of them.\n * @param url - repository address; omitted syncs all.\n * @returns the state right after the sync started.\n */\nexport async function syncRepos(url?: string): Promise<StateWire> {\n return (await request<MutationOk>(SYNC_ROUTE, { method: 'POST', body: JSON.stringify(url === undefined ? {} : { url }) })).state\n}\n","/**\n * 左侧栏「观象」主区页面:应用仓库、日志下载、高级三个分页,共用一份草稿和\n * 底部保存条。仓库拉取中时每 2 秒刷新状态。\n *\n * @module dsh-guanxiang/client/page\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { EditableKey, EditableSettings, RepoSpec, RepoStatusWire, StateWire } from '../settings/contract.ts'\nimport { fetchState, messageOf, resetSettings, saveSettings, syncRepos } from './api.ts'\n\n/** Sidebar label and page title. */\nexport const PANEL_TITLE = '观象'\n\nconst MB = 1024 * 1024\nconst MINUTE = 60 * 1000\n\ntype TabId = 'repos' | 'download' | 'advanced'\n\nconst TABS: readonly { id: TabId; label: string }[] = [\n { id: 'repos', label: '应用仓库' },\n { id: 'download', label: '日志下载' },\n { id: 'advanced', label: '高级' },\n]\n\n/** Editable text form of the settings. */\ninterface Draft {\n reposDir: string\n repos: RepoSpec[]\n fetchIntervalMin: string\n maxAppsPerRequest: string\n dispatchTimeoutMin: string\n allowedHosts: string[]\n maxMb: string\n timeoutSec: string\n}\n\nfunction trimNumber(value: number): string {\n return String(Math.round(value * 100) / 100)\n}\n\nfunction toDraft(settings: EditableSettings): Draft {\n return {\n reposDir: settings.reposDir,\n repos: settings.repos.map(repo => ({ ...repo })),\n fetchIntervalMin: trimNumber(settings.fetchIntervalMs / MINUTE),\n maxAppsPerRequest: String(settings.maxAppsPerRequest),\n dispatchTimeoutMin: trimNumber(settings.dispatchTimeoutMs / MINUTE),\n allowedHosts: [...settings.download.allowedHosts],\n maxMb: trimNumber(settings.download.maxBytes / MB),\n timeoutSec: trimNumber(settings.download.timeoutMs / 1000),\n }\n}\n\nfunction positive(text: string, label: string, allowZero = false): number {\n const value = Number(text.trim())\n if (text.trim() === '' || !Number.isFinite(value) || value < 0 || (!allowZero && value === 0)) throw new Error(`${label}必须是${allowZero ? '非负' : '正'}数`)\n return value\n}\n\nfunction fromDraft(draft: Draft): EditableSettings {\n const maxApps = positive(draft.maxAppsPerRequest, '单次应用数上限')\n if (!Number.isInteger(maxApps)) throw new Error('单次应用数上限必须是整数')\n return {\n reposDir: draft.reposDir.trim(),\n repos: draft.repos.map(repo => repo.branch === undefined || repo.branch.trim() === '' ? { url: repo.url.trim() } : { url: repo.url.trim(), branch: repo.branch.trim() }),\n fetchIntervalMs: Math.round(positive(draft.fetchIntervalMin, '更新间隔', true) * MINUTE),\n maxAppsPerRequest: maxApps,\n dispatchTimeoutMs: Math.round(positive(draft.dispatchTimeoutMin, '派发超时') * MINUTE),\n download: {\n allowedHosts: draft.allowedHosts.map(host => host.trim()).filter(host => host !== ''),\n maxBytes: Math.round(positive(draft.maxMb, '单文件大小上限') * MB),\n timeoutMs: Math.round(positive(draft.timeoutSec, '下载超时') * 1000),\n },\n }\n}\n\nfunction sameSettings(draft: Draft, settings: EditableSettings): boolean {\n try {\n return JSON.stringify(fromDraft(draft)) === JSON.stringify(settings)\n } catch {\n // An unparsable draft differs from any saved settings.\n return false\n }\n}\n\nfunction formatTime(iso: string | number | undefined): string {\n if (iso === undefined) return ''\n const date = new Date(iso)\n return Number.isNaN(date.getTime()) ? '' : date.toLocaleString()\n}\n\nfunction phasePill(repo: RepoStatusWire | undefined): JSX.Element {\n if (repo === undefined) return <span className=\"gx__pill\"><span className=\"gx__dot\" />保存后拉取</span>\n if (repo.phase === 'syncing') return <span className=\"gx__pill gx__pill--warn\"><span className=\"gx__dot\" />拉取中</span>\n if (repo.available) return <span className=\"gx__pill gx__pill--ok\"><span className=\"gx__dot\" />就绪</span>\n if (repo.phase === 'ready' || repo.phase === 'failed') return <span className=\"gx__pill gx__pill--bad\"><span className=\"gx__dot\" />不可用</span>\n return <span className=\"gx__pill\"><span className=\"gx__dot\" />待拉取</span>\n}\n\nfunction Overridden({ keys, name }: { keys: readonly EditableKey[]; name: EditableKey }): JSX.Element | null {\n return keys.includes(name) ? <span className=\"gx__tag gx__tag--accent\">面板设置</span> : null\n}\n\n/**\n * Main-column page for the `main` slot.\n * @returns the page.\n */\nexport function GuanxiangPage(): JSX.Element {\n return (\n <section className=\"gx-page\" aria-label={PANEL_TITLE}>\n <div className=\"gx-page__content\">\n <GuanxiangPanel />\n </div>\n </section>\n )\n}\n\ntype Notice = { kind: 'ok' | 'error'; text: string } | undefined\n\n/**\n * Panel body: loads state, edits a draft, saves.\n * @returns the panel.\n */\nexport function GuanxiangPanel(): JSX.Element {\n const [state, setState] = useState<StateWire | undefined>()\n const [loadError, setLoadError] = useState<string | undefined>()\n const [draft, setDraft] = useState<Draft | undefined>()\n const [tab, setTab] = useState<TabId>('repos')\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<Notice>()\n\n const adopt = useCallback((next: StateWire, resetDraft: boolean) => {\n setState(next)\n if (resetDraft) setDraft(toDraft(next.settings))\n }, [])\n\n const reload = useCallback(async (resetDraft: boolean) => {\n try {\n adopt(await fetchState(), resetDraft)\n setLoadError(undefined)\n } catch (error) {\n setLoadError(messageOf(error))\n }\n }, [adopt])\n\n useEffect(() => { void reload(true) }, [reload])\n\n const syncing = state?.repos.some(repo => repo.phase === 'syncing') === true\n useEffect(() => {\n if (!syncing) return\n const timer = setInterval(() => { void reload(false) }, 2000)\n return () => { clearInterval(timer) }\n }, [syncing, reload])\n\n const dirty = state !== undefined && draft !== undefined && !sameSettings(draft, state.settings)\n\n const run = async (action: () => Promise<StateWire>, success: string, resetDraft: boolean): Promise<void> => {\n setBusy(true)\n setNotice(undefined)\n try {\n adopt(await action(), resetDraft)\n setNotice({ kind: 'ok', text: success })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(false)\n }\n }\n\n const save = (): void => {\n if (draft === undefined) return\n let settings: EditableSettings\n try {\n settings = fromDraft(draft)\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n return\n }\n void run(() => saveSettings(settings), '已保存,新的仓库会在后台拉取', true)\n }\n\n if (state === undefined || draft === undefined) {\n return (\n <div className=\"gx\">\n {loadError === undefined\n ? <div className=\"gx__loading\">加载中…</div>\n : <div className=\"gx__notice gx__notice--error\">{loadError} <button type=\"button\" className=\"gx__btn gx__btn--ghost\" onClick={() => { void reload(true) }}>重试</button></div>}\n </div>\n )\n }\n\n const available = state.repos.filter(repo => repo.available).length\n return (\n <div className=\"gx\">\n <header className=\"gx__head\">\n <div className=\"gx__headRow\">\n <h1 className=\"gx__title\">{PANEL_TITLE}</h1>\n <span className={`gx__pill ${available > 0 ? 'gx__pill--ok' : 'gx__pill--warn'}`}><span className=\"gx__dot\" />{available} 个应用可用</span>\n {syncing ? <span className=\"gx__pill gx__pill--warn\"><span className=\"gx__dot\" />拉取中</span> : null}\n </div>\n <div className=\"gx__meta\">观其象,知其因。在对话中输入 /guanxiang 描述问题并附上日志,观象会按应用派发分析。</div>\n <div className=\"gx__tabs\" role=\"tablist\">\n {TABS.map(item => (\n <button key={item.id} type=\"button\" role=\"tab\" className=\"gx__tab\" aria-selected={tab === item.id} onClick={() => { setTab(item.id) }}>{item.label}</button>\n ))}\n </div>\n </header>\n {state.overlayError === undefined ? null : <div className=\"gx__notice gx__notice--error\">面板设置文件未生效:{state.overlayError}。保存一次会用当前内容覆盖它。</div>}\n <div className=\"gx__body\" role=\"tabpanel\">\n {tab === 'repos' ? <ReposTab draft={draft} setDraft={setDraft} state={state} busy={busy || dirty} onSync={(url) => { void run(() => syncRepos(url), url === undefined ? '已开始重新拉取全部仓库' : '已开始重新拉取', false) }} /> : null}\n {tab === 'download' ? <DownloadTab draft={draft} setDraft={setDraft} overridden={state.overridden} /> : null}\n {tab === 'advanced' ? <AdvancedTab draft={draft} setDraft={setDraft} overridden={state.overridden} busy={busy} onReset={() => { void run(resetSettings, '已恢复为 cordis.yml 中的配置', true) }} /> : null}\n </div>\n {notice === undefined ? null : <div className={`gx__notice gx__notice--${notice.kind}`}>{notice.text}</div>}\n <div className=\"gx__saveBar\">\n <span className=\"gx__saveMsg\">{dirty ? '有未保存的修改' : '设置已保存'}</span>\n <button type=\"button\" className=\"gx__btn gx__btn--ghost\" disabled={!dirty || busy} onClick={() => { setDraft(toDraft(state.settings)); setNotice(undefined) }}>放弃修改</button>\n <button type=\"button\" className=\"gx__btn gx__btn--primary\" disabled={!dirty || busy} onClick={save}>保存</button>\n </div>\n </div>\n )\n}\n\ninterface TabProps {\n draft: Draft\n setDraft: (next: Draft) => void\n}\n\nfunction ReposTab({ draft, setDraft, state, busy, onSync }: TabProps & { state: StateWire; busy: boolean; onSync: (url?: string) => void }): JSX.Element {\n const [url, setUrl] = useState('')\n const [branch, setBranch] = useState('')\n const status = useMemo(() => new Map(state.repos.map(repo => [repo.url, repo])), [state.repos])\n const add = (): void => {\n const trimmed = url.trim()\n if (trimmed === '' || draft.repos.some(repo => repo.url === trimmed)) return\n setDraft({ ...draft, repos: [...draft.repos, branch.trim() === '' ? { url: trimmed } : { url: trimmed, branch: branch.trim() }] })\n setUrl('')\n setBranch('')\n }\n return (\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">应用仓库 <Overridden keys={state.overridden} name=\"repos\" /></h2>\n <div className=\"gx__list\">\n {draft.repos.length === 0 ? <div className=\"gx__emptyRow\">还没有应用仓库。添加 git 地址后保存,观象会把仓库拉取到本机。</div> : null}\n {draft.repos.map((repo, index) => {\n const current = status.get(repo.url)\n const saved = current !== undefined && current.branch === repo.branch\n return (\n <div key={repo.url} className=\"gx__row gx__row--top\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">\n <span className=\"gx__mono\">{repo.url}</span>\n {repo.branch === undefined ? null : <span className=\"gx__tag\">{repo.branch}</span>}\n {phasePill(saved ? current : undefined)}\n </div>\n {saved && current.app !== undefined\n ? <div className=\"gx__rowSummary\">{current.app.name}(<span className=\"gx__mono\">{current.app.id}</span>)· SKILL <span className=\"gx__mono\">/{current.app.skill}</span>{current.activeRuns > 0 ? ` · ${current.activeRuns} 个分析进行中` : ''}</div>\n : null}\n {saved && current.app !== undefined ? <div className=\"gx__rowSummary\">{current.app.description}</div> : null}\n {saved && current.commit !== undefined\n ? <div className=\"gx__rowSummary\">提交 <span className=\"gx__mono\">{current.commit.slice(0, 8)}</span>({formatTime(current.commitTime)})· 拉取于 {formatTime(current.syncedAt)} · <span className=\"gx__mono\">{current.dir}</span></div>\n : null}\n {saved && current.error !== undefined ? <div className=\"gx__rowError\">{current.error}</div> : null}\n </div>\n <div className=\"gx__rowCtl\">\n {saved ? <button type=\"button\" className=\"gx__btn\" disabled={busy || current.phase === 'syncing' || current.activeRuns > 0} onClick={() => { onSync(repo.url) }}>重新拉取</button> : null}\n <button type=\"button\" className=\"gx__btn gx__btn--danger\" onClick={() => { setDraft({ ...draft, repos: draft.repos.filter((_, position) => position !== index) }) }}>删除</button>\n </div>\n </div>\n )\n })}\n <div className=\"gx__addRow\">\n <input className=\"gx__input\" placeholder=\"git 地址,例如 git@github.com:example/order-service.git\" value={url} onChange={(event) => { setUrl(event.target.value) }} onKeyDown={(event) => { if (event.key === 'Enter') add() }} />\n <input className=\"gx__input gx__input--branch\" placeholder=\"分支(可选)\" value={branch} onChange={(event) => { setBranch(event.target.value) }} onKeyDown={(event) => { if (event.key === 'Enter') add() }} />\n <button type=\"button\" className=\"gx__btn\" disabled={url.trim() === ''} onClick={add}>添加</button>\n </div>\n </div>\n <p className=\"gx__secFoot\">使用本机已有的 git 凭据(SSH key 或 credential helper)拉取,不留分支时跟随远端默认分支。每个仓库需包含 <span className=\"gx__mono\">.dsh/guanxiang.yaml</span> 与其中声明的日志分析 SKILL。删除地址不会删除本地目录。</p>\n {state.repos.length > 0 ? <div><button type=\"button\" className=\"gx__btn\" disabled={busy} onClick={() => { onSync() }}>重新拉取全部</button></div> : null}\n </section>\n )\n}\n\nfunction DownloadTab({ draft, setDraft, overridden }: TabProps & { overridden: readonly EditableKey[] }): JSX.Element {\n const [host, setHost] = useState('')\n const add = (): void => {\n const trimmed = host.trim().toLowerCase()\n if (trimmed === '' || draft.allowedHosts.includes(trimmed)) return\n setDraft({ ...draft, allowedHosts: [...draft.allowedHosts, trimmed] })\n setHost('')\n }\n return (\n <>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">允许下载的域名 <Overridden keys={overridden} name=\"download\" /></h2>\n <div className=\"gx__list\">\n {draft.allowedHosts.length === 0 ? <div className=\"gx__emptyRow\">未配置域名,消息中的日志下载链接不会被下载。</div> : null}\n {draft.allowedHosts.map(item => (\n <div key={item} className=\"gx__row\">\n <div className=\"gx__rowText\"><span className=\"gx__mono\">{item}</span></div>\n <div className=\"gx__rowCtl\"><button type=\"button\" className=\"gx__btn gx__btn--danger\" onClick={() => { setDraft({ ...draft, allowedHosts: draft.allowedHosts.filter(entry => entry !== item) }) }}>删除</button></div>\n </div>\n ))}\n <div className=\"gx__addRow\">\n <input className=\"gx__input\" placeholder=\"主机名,例如 oss.example.com\" value={host} onChange={(event) => { setHost(event.target.value) }} onKeyDown={(event) => { if (event.key === 'Enter') add() }} />\n <button type=\"button\" className=\"gx__btn\" disabled={host.trim() === ''} onClick={add}>添加</button>\n </div>\n </div>\n <p className=\"gx__secFoot\">只填主机名,同时允许它的子域名。下载不携带任何鉴权信息,每次重定向都会重新校验域名。</p>\n </section>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">限制</h2>\n <div className=\"gx__list\">\n <NumberRow label=\"单文件大小上限\" unit=\"MB\" value={draft.maxMb} onChange={(value) => { setDraft({ ...draft, maxMb: value }) }} />\n <NumberRow label=\"单文件下载超时\" unit=\"秒\" value={draft.timeoutSec} onChange={(value) => { setDraft({ ...draft, timeoutSec: value }) }} />\n </div>\n </section>\n </>\n )\n}\n\nfunction AdvancedTab({ draft, setDraft, overridden, busy, onReset }: TabProps & { overridden: readonly EditableKey[]; busy: boolean; onReset: () => void }): JSX.Element {\n return (\n <>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">仓库</h2>\n <div className=\"gx__list\">\n <div className=\"gx__row\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">拉取目录 <Overridden keys={overridden} name=\"reposDir\" /></div>\n <div className=\"gx__rowSummary\">每个仓库拉取到该目录下的同名子目录,观象只读使用。</div>\n </div>\n <div className=\"gx__rowCtl\"><input className=\"gx__input gx__input--wide gx__mono\" value={draft.reposDir} onChange={(event) => { setDraft({ ...draft, reposDir: event.target.value }) }} /></div>\n </div>\n <NumberRow label=\"更新间隔\" summary=\"派发前,距上次拉取超过该时间的仓库会先更新;0 表示每次都更新。\" unit=\"分钟\" value={draft.fetchIntervalMin} overridden={overridden.includes('fetchIntervalMs')} onChange={(value) => { setDraft({ ...draft, fetchIntervalMin: value }) }} />\n </div>\n </section>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">派发</h2>\n <div className=\"gx__list\">\n <NumberRow label=\"单次应用数上限\" summary=\"一次派发最多同时分析的应用数,不应超过 DSH 的活动子 Agent 上限(默认 8)。\" unit=\"个\" value={draft.maxAppsPerRequest} overridden={overridden.includes('maxAppsPerRequest')} onChange={(value) => { setDraft({ ...draft, maxAppsPerRequest: value }) }} />\n <NumberRow label=\"派发超时\" summary=\"等待全部子 Agent 的最长时间,超时后停止仍在运行的子 Agent。\" unit=\"分钟\" value={draft.dispatchTimeoutMin} overridden={overridden.includes('dispatchTimeoutMs')} onChange={(value) => { setDraft({ ...draft, dispatchTimeoutMin: value }) }} />\n </div>\n </section>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">面板设置</h2>\n <div className=\"gx__list\">\n <div className=\"gx__row\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">恢复 cordis.yml 配置</div>\n <div className=\"gx__rowSummary\">删除面板保存的全部设置($DSH_HOME/guanxiang/settings.json 中的覆盖值)。</div>\n </div>\n <div className=\"gx__rowCtl\"><button type=\"button\" className=\"gx__btn gx__btn--danger\" disabled={busy || overridden.length === 0} onClick={onReset}>恢复</button></div>\n </div>\n </div>\n </section>\n </>\n )\n}\n\nfunction NumberRow({ label, summary, unit, value, overridden = false, onChange }: { label: string; summary?: string; unit: string; value: string; overridden?: boolean; onChange: (value: string) => void }): JSX.Element {\n return (\n <div className=\"gx__row\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">{label}{overridden ? <span className=\"gx__tag gx__tag--accent\">面板设置</span> : null}</div>\n {summary === undefined ? null : <div className=\"gx__rowSummary\">{summary}</div>}\n </div>\n <div className=\"gx__rowCtl\">\n <input className=\"gx__input gx__input--num\" inputMode=\"decimal\" value={value} onChange={(event) => { onChange(event.target.value) }} />\n <span className=\"gx__unit\">{unit}</span>\n </div>\n </div>\n )\n}\n","/**\n * 「观象」页面样式。使用 DSH 语义 token(`--dsw-alias-*`)并带中性回退,\n * 页面随当前主题变化。\n *\n * @module dsh-guanxiang/client/styles\n */\n\nconst STYLE_ATTRIBUTE = 'data-dsh-guanxiang-css'\n\nconst CSS = `\n.gx-page{width:100%;height:100%;min-width:0;min-height:0;overflow:auto;scrollbar-gutter:stable;background:var(--dsw-alias-bg-base,#fff)}\n.gx-page__content{box-sizing:border-box;max-width:816px;margin:0 auto;padding:18px clamp(24px,4vw,48px) 48px}\n[data-platform='darwin'] .gx-page__content{padding-top:calc(18px + var(--dsh-frame-top-clearance,0px))}\n.gx{--gx-fg:var(--dsw-alias-label-primary,#1f2328);--gx-fg2:var(--dsw-alias-label-secondary,#5c6169);--gx-fg3:var(--dsw-alias-label-tertiary,#8a8f98);--gx-dim:var(--dsw-alias-label-dimmed,#b0b4ba);--gx-bg:var(--dsw-alias-bg-base,#fff);--gx-line:var(--dsw-alias-border-l2,rgba(0,0,0,.12));--gx-hover:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05));--gx-accent:var(--dsw-alias-state-business-primary,#0a67d3);--gx-ok:var(--dsw-alias-state-success-primary,#0a7d33);--gx-warn:var(--dsw-alias-state-warn-primary,#a15c00);--gx-bad:var(--dsw-alias-state-error-primary,#c62828);--gx-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:flex;flex-direction:column;gap:14px;width:100%;max-width:720px;color:var(--gx-fg);font-size:13px;line-height:20px}\n.gx__head{position:sticky;top:0;z-index:2;display:flex;flex-direction:column;gap:4px;padding:10px 0 6px;background:var(--gx-bg)}\n.gx__headRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.gx__title{margin:0 2px 0 0;font-size:20px;font-weight:500;line-height:28px}\n.gx__meta{color:var(--gx-fg3);font-size:12px}\n.gx__mono{font-family:var(--gx-mono);font-size:12px}\n.gx__pill{display:inline-flex;align-items:center;gap:5px;padding:0 8px;border-radius:999px;font-size:12px;line-height:20px;white-space:nowrap;color:var(--gx-fg3);background:color-mix(in srgb,currentColor 10%,transparent)}\n.gx__pill--ok{color:var(--gx-ok)}\n.gx__pill--warn{color:var(--gx-warn)}\n.gx__pill--bad{color:var(--gx-bad)}\n.gx__dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}\n.gx__tabs{display:flex;align-items:center;gap:18px;border-bottom:1px solid var(--gx-line);overflow-x:auto;scrollbar-width:none}\n.gx__tab{display:inline-flex;align-items:center;min-height:34px;padding:0;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--gx-fg2);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.gx__tab:hover{color:var(--gx-fg)}\n.gx__tab[aria-selected=\"true\"]{color:var(--gx-fg);border-bottom-color:currentColor;font-weight:600}\n.gx__body{display:flex;flex-direction:column;gap:22px;padding:4px 0 8px}\n.gx__sec{display:flex;flex-direction:column;gap:6px;margin:0}\n.gx__secTitle{margin:0;font-size:12px;font-weight:600;line-height:18px;color:var(--gx-fg3);letter-spacing:.02em}\n.gx__secFoot{margin:0;color:var(--gx-fg3);font-size:12px;line-height:18px}\n.gx__list{display:flex;flex-direction:column;border:1px solid var(--gx-line);border-radius:10px}\n.gx__list>*+*{border-top:1px solid var(--gx-line)}\n.gx__row{display:flex;align-items:center;gap:16px;padding:11px 14px;min-height:28px}\n.gx__row--top{align-items:flex-start}\n.gx__rowText{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}\n.gx__rowLabel{display:flex;align-items:center;gap:6px;flex-wrap:wrap;overflow-wrap:anywhere}\n.gx__rowSummary{color:var(--gx-fg3);font-size:12px;line-height:18px;overflow-wrap:anywhere}\n.gx__rowError{color:var(--gx-bad);font-size:12px;line-height:18px;overflow-wrap:anywhere;white-space:pre-wrap}\n.gx__rowCtl{flex:none;display:flex;align-items:center;gap:6px;justify-content:flex-end}\n.gx__tag{display:inline-flex;align-items:center;padding:0 5px;border-radius:4px;font-size:11px;line-height:16px;white-space:nowrap;color:var(--gx-fg2);background:var(--gx-hover)}\n.gx__tag--accent{color:var(--gx-accent);background:color-mix(in srgb,var(--gx-accent) 10%,transparent)}\n.gx__input{box-sizing:border-box;width:100%;min-width:0;height:30px;padding:0 10px;border:1px solid var(--gx-line);border-radius:7px;background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;font:inherit;font-size:13px;outline:none}\n.gx__input:focus{border-color:var(--gx-accent)}\n.gx__input--num{width:120px;text-align:right;font-variant-numeric:tabular-nums}\n.gx__input--wide{width:320px}\n.gx__unit{min-width:36px;color:var(--gx-fg3);font-size:12px}\n.gx__addRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 14px}\n.gx__addRow .gx__input{flex:1 1 160px;width:auto}\n.gx__addRow .gx__input--branch{flex:0 1 140px}\n.gx__emptyRow{padding:11px 14px;color:var(--gx-fg3);font-size:12px}\n.gx__btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:28px;padding:0 12px;border:1px solid var(--gx-line);border-radius:7px;background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.gx__btn:hover:not(:disabled){background:var(--gx-hover)}\n.gx__btn:disabled{color:var(--gx-dim);cursor:default}\n.gx__btn--primary{background:var(--dsw-alias-button-primary-fill,var(--gx-accent));border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}\n.gx__btn--primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#0b5cba)}\n.gx__btn--primary:disabled{opacity:.45;color:var(--dsw-alias-label-primary-foreground,#fff)}\n.gx__btn--ghost{border-color:transparent;color:var(--gx-fg2)}\n.gx__btn--danger{color:var(--gx-bad)}\n.gx__btn:focus-visible,.gx__tab:focus-visible{outline:2px solid var(--gx-accent);outline-offset:2px}\n.gx__notice{padding:8px 12px;border-radius:8px;font-size:12px;line-height:18px;overflow-wrap:anywhere}\n.gx__notice--ok{color:var(--gx-ok);background:color-mix(in srgb,var(--gx-ok) 9%,transparent)}\n.gx__notice--error{color:var(--gx-bad);background:color-mix(in srgb,var(--gx-bad) 8%,transparent)}\n.gx__saveBar{position:sticky;bottom:0;z-index:2;display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 0;border-top:1px solid var(--gx-line);background:var(--gx-bg)}\n.gx__saveMsg{flex:1;min-width:0;color:var(--gx-fg2);font-size:12px}\n.gx__loading{padding:16px 0;color:var(--gx-fg3);font-size:12px}\n`\n\n/**\n * Install the page stylesheet once.\n * @returns disposer removing it.\n */\nexport function installStyles(): () => void {\n const existing = document.querySelector(`style[${STYLE_ATTRIBUTE}]`)\n if (existing !== null) return () => {}\n const element = document.createElement('style')\n element.setAttribute(STYLE_ATTRIBUTE, '')\n element.textContent = CSS\n document.head.append(element)\n return () => { element.remove() }\n}\n","/**\n * 观象 client half:左侧栏全局入口「观象」+ 主区整页设置面板。\n *\n * 只依赖平台核心的 `slots` 服务与 react(bundle 里 react 走平台模块表,\n * 其余全部内联);页面数据来自 Host 半侧的 `/guanxiang/*` 路由。\n *\n * @module dsh-guanxiang/client\n */\n\nimport { GuanxiangIcon } from './icon.tsx'\nimport { GuanxiangPage, PANEL_TITLE } from './page.tsx'\nimport { installStyles } from './styles.ts'\n\n/** Slots runtime surface this bundle uses: list slots by `id`, keyed slots by `key`. */\nexport interface ClientSlots {\n inject(slot: string, contribution: () => unknown): void\n register(\n declaration:\n | { name: string; id: string; order?: number; label?: () => string }\n | { name: string; key: string },\n component: (props: never) => unknown,\n ): () => void\n}\n\n/** Client plugin context. */\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-guanxiang'\n\n/** Required client services: the Slots registry is part of the web shell core. */\nexport const inject = ['slots']\n\n/**\n * Main panel id: the `sidebar.panellist` row id and the matching `main` slot\n * key. Must not change once released: DSH keys navigation state by it.\n */\nexport const PANEL_ID = 'guanxiang'\n/** Left-sidebar row order (dsh-channels uses 40). */\nexport const PANEL_ORDER = 45\n\n/**\n * Mount the left-sidebar entry, its main-column page, and the stylesheet.\n * @param ctx - client plugin context carrying the slots registry.\n */\nexport function apply(ctx: ClientContext): void {\n if (typeof ctx.effect === 'function') ctx.effect(() => installStyles(), 'dsh-guanxiang: panel styles')\n else installStyles()\n ctx.slots.inject('main', () => ctx.slots.register({ name: 'main', key: PANEL_ID }, GuanxiangPage))\n ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({\n name: 'sidebar.panellist',\n id: PANEL_ID,\n order: PANEL_ORDER,\n label: () => PANEL_TITLE,\n }, GuanxiangIcon))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../src/client/icon.tsx","../src/settings/contract.ts","../src/client/api.ts","../src/client/page.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 左侧栏「观象」行的装饰图标;行本身负责可访问名称(列表注册的 label)。\n *\n * @module dsh-guanxiang/client/icon\n */\n\n/** Owner share of `sidebar.panellist`: requested square edge and selection state. */\nexport interface GuanxiangIconProps {\n size: number\n active: boolean\n}\n\n/**\n * Eye-over-waveform glyph drawn in `currentColor`.\n * @param props - the sidebar's icon share.\n * @returns decorative icon.\n */\nexport function GuanxiangIcon({ size }: GuanxiangIconProps): JSX.Element {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.6} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M2.5 10c2.4-3.6 5.6-5.4 9.5-5.4s7.1 1.8 9.5 5.4c-2.4 3.6-5.6 5.4-9.5 5.4S4.9 13.6 2.5 10Z\" />\n <circle cx=\"12\" cy=\"10\" r=\"2.4\" />\n <path d=\"M3 19.5h3l2-2.5 2.5 4 3-6 2.5 4.5 1.5-2h3.5\" />\n </svg>\n )\n}\n","/**\n * 设置面板与 Host 路由共用的路由常量和 JSON 结构。浏览器端 bundle 也会\n * 编译本文件,因此这里只放类型和常量,不 import 任何 Node 模块。\n *\n * @module dsh-guanxiang/settings/contract\n */\n\n/** GET:面板所需的全部状态(生效设置、默认值、仓库与应用)。 */\nexport const STATE_ROUTE = '/guanxiang/state'\n/** PUT:保存可编辑设置;DELETE:清除面板覆盖、恢复 cordis.yml 默认值。 */\nexport const SETTINGS_ROUTE = '/guanxiang/settings'\n/** POST:立即重新拉取全部仓库,或 body `{ url }` 指定的一个仓库。 */\nexport const SYNC_ROUTE = '/guanxiang/sync'\n\n/** One configured application repository. */\nexport interface RepoSpec {\n /** Git remote address, cloned with the host machine's existing git credentials. */\n url: string\n /** Remote branch to follow; omitted follows the remote's default branch. */\n branch?: string\n}\n\n/** Log URL download policy. */\nexport interface DownloadSettings {\n /** Allowed host names; `example.com` also admits its subdomains. Empty disables URL downloads. */\n allowedHosts: string[]\n /** Per-file byte ceiling. */\n maxBytes: number\n /** Per-file download timeout in milliseconds. */\n timeoutMs: number\n}\n\n/** The settings the Web panel can override; every field is required on the wire. */\nexport interface EditableSettings {\n /** Absolute directory holding the cloned repositories (`~` expands to the home directory). */\n reposDir: string\n /** Application repositories to clone and keep updated. */\n repos: RepoSpec[]\n /** Minimum age of a repository's last sync before loading `/guanxiang` syncs it again, in milliseconds; 0 syncs on every load. */\n fetchIntervalMs: number\n /** Maximum number of child analyses one dispatch may start. */\n maxAppsPerRequest: number\n /** Whole-dispatch wait budget in milliseconds; children still running then are stopped. */\n dispatchTimeoutMs: number\n /** URL download policy. */\n download: DownloadSettings\n}\n\n/** Keys of {@link EditableSettings}, in panel order. */\nexport const EDITABLE_KEYS = ['reposDir', 'repos', 'fetchIntervalMs', 'maxAppsPerRequest', 'dispatchTimeoutMs', 'download'] as const satisfies readonly (keyof EditableSettings)[]\n\n/** One editable key. */\nexport type EditableKey = (typeof EDITABLE_KEYS)[number]\n\n/** Application description read from `<repo>/.dsh/guanxiang.yaml`. */\nexport interface AppManifest {\n /** Registry id the dispatch tool and analysis requests address. */\n id: string\n /** Display name, also the child agent's label. */\n name: string\n /** What the application does, used for intent recognition. */\n description: string\n /** Other names users call the application by. */\n aliases: string[]\n /** Strings that identify this application's log lines (tags, process names, class names). */\n logHints: string[]\n /** Project skill name under `.dsh/skills/<skill>/SKILL.md` that analyzes this application's logs. */\n skill: string\n}\n\n/** Lifecycle of one repository checkout. */\nexport type RepoPhase = 'pending' | 'syncing' | 'ready' | 'failed'\n\n/** One repository as the panel shows it. */\nexport interface RepoStatusWire {\n url: string\n branch?: string\n /** Absolute directory of the fetch-only clone; children work in commit snapshots under `<reposDir>/.snapshots/`. */\n dir: string\n phase: RepoPhase\n /** Latest synced commit, the one the catalog describes and new dispatches use. */\n commit?: string\n /** Committer date of {@link commit}, ISO 8601. */\n commitTime?: string\n /** Last completed sync attempt, epoch milliseconds. */\n syncedAt?: number\n /** Application description read from {@link commit}. */\n app?: AppManifest\n /** Why the repository is unusable (`failed`), or why the last update failed while an older commit stays usable (`ready`). */\n error?: string\n /** Whether the application is offered to intent recognition and dispatch. */\n available: boolean\n /** Child analyses currently running in snapshots of this repository. */\n activeRuns: number\n}\n\n/** `GET /guanxiang/state` response. */\nexport interface StateWire {\n /** Effective settings: cordis.yml values with panel overrides applied. */\n settings: EditableSettings\n /** cordis.yml values before panel overrides. */\n defaults: EditableSettings\n /** Keys the panel currently overrides. */\n overridden: EditableKey[]\n /** Why the overlay file was ignored (unreadable or invalid); panel saves replace it. */\n overlayError?: string\n repos: RepoStatusWire[]\n}\n\n/** Successful mutation response. */\nexport interface MutationOk {\n ok: true\n state: StateWire\n}\n\n/** Failed request response. */\nexport interface MutationError {\n ok: false\n error: string\n}\n","/**\n * 设置面板的同源 HTTP 客户端,直接请求 Host 半侧注册的 `/guanxiang/*` 路由。\n *\n * @module dsh-guanxiang/client/api\n */\n\nimport { SETTINGS_ROUTE, STATE_ROUTE, SYNC_ROUTE } from '../settings/contract.ts'\nimport type { EditableSettings, MutationError, MutationOk, StateWire } from '../settings/contract.ts'\n\n/**\n * Error text of a value thrown by a request.\n * @param error - thrown value.\n * @returns message.\n */\nexport function messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * Fetch one Host route. Routes are absolute pathnames, but the browser\n * addresses them relative to the DSH document so a reverse proxy that mounts\n * DSH under a prefix still routes them with its scoped login cookie.\n */\nasync function request<T>(route: string, init?: RequestInit): Promise<T> {\n let response: Response\n try {\n response = await fetch(route.slice(1), {\n cache: 'no-store',\n credentials: 'same-origin',\n ...init,\n headers: { accept: 'application/json', ...init?.body === undefined ? {} : { 'content-type': 'application/json' } },\n })\n } catch (error) {\n throw new Error(`无法连接设置接口:${messageOf(error)}`)\n }\n const text = await response.text()\n let body: unknown\n try {\n body = JSON.parse(text)\n } catch {\n // Non-JSON bodies (proxy errors) are reported by HTTP status below.\n throw new Error(`HTTP ${response.status}`)\n }\n if (!response.ok) {\n const error = (body as Partial<MutationError>).error\n throw new Error(typeof error === 'string' && error !== '' ? error : `HTTP ${response.status}`)\n }\n return body as T\n}\n\n/** @returns the panel state. */\nexport function fetchState(): Promise<StateWire> {\n return request<StateWire>(STATE_ROUTE)\n}\n\n/**\n * Save the complete editable settings.\n * @param settings - settings to persist.\n * @returns the new state.\n */\nexport async function saveSettings(settings: EditableSettings): Promise<StateWire> {\n return (await request<MutationOk>(SETTINGS_ROUTE, { method: 'PUT', body: JSON.stringify(settings) })).state\n}\n\n/** @returns the state after removing every panel override. */\nexport async function resetSettings(): Promise<StateWire> {\n return (await request<MutationOk>(SETTINGS_ROUTE, { method: 'DELETE' })).state\n}\n\n/**\n * Re-sync one repository or all of them.\n * @param url - repository address; omitted syncs all.\n * @returns the state right after the sync started.\n */\nexport async function syncRepos(url?: string): Promise<StateWire> {\n return (await request<MutationOk>(SYNC_ROUTE, { method: 'POST', body: JSON.stringify(url === undefined ? {} : { url }) })).state\n}\n","/**\n * 左侧栏「观象」主区页面:应用仓库、日志下载、高级三个分页,共用一份草稿和\n * 底部保存条。仓库拉取中时每 2 秒刷新状态。\n *\n * @module dsh-guanxiang/client/page\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { EditableKey, EditableSettings, RepoSpec, RepoStatusWire, StateWire } from '../settings/contract.ts'\nimport { fetchState, messageOf, resetSettings, saveSettings, syncRepos } from './api.ts'\n\n/** Sidebar label and page title. */\nexport const PANEL_TITLE = '观象'\n\nconst MB = 1024 * 1024\nconst MINUTE = 60 * 1000\n\ntype TabId = 'repos' | 'download' | 'advanced'\n\nconst TABS: readonly { id: TabId; label: string }[] = [\n { id: 'repos', label: '应用仓库' },\n { id: 'download', label: '日志下载' },\n { id: 'advanced', label: '高级' },\n]\n\n/** Editable text form of the settings. */\ninterface Draft {\n reposDir: string\n repos: RepoSpec[]\n fetchIntervalMin: string\n maxAppsPerRequest: string\n dispatchTimeoutMin: string\n allowedHosts: string[]\n maxMb: string\n timeoutSec: string\n}\n\nfunction trimNumber(value: number): string {\n return String(Math.round(value * 100) / 100)\n}\n\nfunction toDraft(settings: EditableSettings): Draft {\n return {\n reposDir: settings.reposDir,\n repos: settings.repos.map(repo => ({ ...repo })),\n fetchIntervalMin: trimNumber(settings.fetchIntervalMs / MINUTE),\n maxAppsPerRequest: String(settings.maxAppsPerRequest),\n dispatchTimeoutMin: trimNumber(settings.dispatchTimeoutMs / MINUTE),\n allowedHosts: [...settings.download.allowedHosts],\n maxMb: trimNumber(settings.download.maxBytes / MB),\n timeoutSec: trimNumber(settings.download.timeoutMs / 1000),\n }\n}\n\nfunction positive(text: string, label: string, allowZero = false): number {\n const value = Number(text.trim())\n if (text.trim() === '' || !Number.isFinite(value) || value < 0 || (!allowZero && value === 0)) throw new Error(`${label}必须是${allowZero ? '非负' : '正'}数`)\n return value\n}\n\nfunction fromDraft(draft: Draft): EditableSettings {\n const maxApps = positive(draft.maxAppsPerRequest, '单次应用数上限')\n if (!Number.isInteger(maxApps)) throw new Error('单次应用数上限必须是整数')\n return {\n reposDir: draft.reposDir.trim(),\n repos: draft.repos.map(repo => repo.branch === undefined || repo.branch.trim() === '' ? { url: repo.url.trim() } : { url: repo.url.trim(), branch: repo.branch.trim() }),\n fetchIntervalMs: Math.round(positive(draft.fetchIntervalMin, '更新间隔', true) * MINUTE),\n maxAppsPerRequest: maxApps,\n dispatchTimeoutMs: Math.round(positive(draft.dispatchTimeoutMin, '派发超时') * MINUTE),\n download: {\n allowedHosts: draft.allowedHosts.map(host => host.trim()).filter(host => host !== ''),\n maxBytes: Math.round(positive(draft.maxMb, '单文件大小上限') * MB),\n timeoutMs: Math.round(positive(draft.timeoutSec, '下载超时') * 1000),\n },\n }\n}\n\nfunction sameSettings(draft: Draft, settings: EditableSettings): boolean {\n try {\n return JSON.stringify(fromDraft(draft)) === JSON.stringify(settings)\n } catch {\n // An unparsable draft differs from any saved settings.\n return false\n }\n}\n\nfunction formatTime(iso: string | number | undefined): string {\n if (iso === undefined) return ''\n const date = new Date(iso)\n return Number.isNaN(date.getTime()) ? '' : date.toLocaleString()\n}\n\nfunction phasePill(repo: RepoStatusWire | undefined): JSX.Element {\n if (repo === undefined) return <span className=\"gx__pill\"><span className=\"gx__dot\" />保存后拉取</span>\n if (repo.phase === 'syncing') return <span className=\"gx__pill gx__pill--warn\"><span className=\"gx__dot\" />拉取中</span>\n if (repo.available) return <span className=\"gx__pill gx__pill--ok\"><span className=\"gx__dot\" />就绪</span>\n if (repo.phase === 'ready' || repo.phase === 'failed') return <span className=\"gx__pill gx__pill--bad\"><span className=\"gx__dot\" />不可用</span>\n return <span className=\"gx__pill\"><span className=\"gx__dot\" />待拉取</span>\n}\n\nfunction Overridden({ keys, name }: { keys: readonly EditableKey[]; name: EditableKey }): JSX.Element | null {\n return keys.includes(name) ? <span className=\"gx__tag gx__tag--accent\">面板设置</span> : null\n}\n\n/**\n * Main-column page for the `main` slot.\n * @returns the page.\n */\nexport function GuanxiangPage(): JSX.Element {\n return (\n <section className=\"gx-page\" aria-label={PANEL_TITLE}>\n <div className=\"gx-page__content\">\n <GuanxiangPanel />\n </div>\n </section>\n )\n}\n\ntype Notice = { kind: 'ok' | 'error'; text: string } | undefined\n\n/**\n * Panel body: loads state, edits a draft, saves.\n * @returns the panel.\n */\nexport function GuanxiangPanel(): JSX.Element {\n const [state, setState] = useState<StateWire | undefined>()\n const [loadError, setLoadError] = useState<string | undefined>()\n const [draft, setDraft] = useState<Draft | undefined>()\n const [tab, setTab] = useState<TabId>('repos')\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<Notice>()\n\n const adopt = useCallback((next: StateWire, resetDraft: boolean) => {\n setState(next)\n if (resetDraft) setDraft(toDraft(next.settings))\n }, [])\n\n const reload = useCallback(async (resetDraft: boolean) => {\n try {\n adopt(await fetchState(), resetDraft)\n setLoadError(undefined)\n } catch (error) {\n setLoadError(messageOf(error))\n }\n }, [adopt])\n\n useEffect(() => { void reload(true) }, [reload])\n\n const syncing = state?.repos.some(repo => repo.phase === 'syncing') === true\n useEffect(() => {\n if (!syncing) return\n const timer = setInterval(() => { void reload(false) }, 2000)\n return () => { clearInterval(timer) }\n }, [syncing, reload])\n\n const dirty = state !== undefined && draft !== undefined && !sameSettings(draft, state.settings)\n\n const run = async (action: () => Promise<StateWire>, success: string, resetDraft: boolean): Promise<void> => {\n setBusy(true)\n setNotice(undefined)\n try {\n adopt(await action(), resetDraft)\n setNotice({ kind: 'ok', text: success })\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n } finally {\n setBusy(false)\n }\n }\n\n const save = (): void => {\n if (draft === undefined) return\n let settings: EditableSettings\n try {\n settings = fromDraft(draft)\n } catch (error) {\n setNotice({ kind: 'error', text: messageOf(error) })\n return\n }\n void run(() => saveSettings(settings), '已保存,新的仓库会在后台拉取', true)\n }\n\n if (state === undefined || draft === undefined) {\n return (\n <div className=\"gx\">\n {loadError === undefined\n ? <div className=\"gx__loading\">加载中…</div>\n : <div className=\"gx__notice gx__notice--error\">{loadError} <button type=\"button\" className=\"gx__btn gx__btn--ghost\" onClick={() => { void reload(true) }}>重试</button></div>}\n </div>\n )\n }\n\n const available = state.repos.filter(repo => repo.available).length\n return (\n <div className=\"gx\">\n <header className=\"gx__head\">\n <div className=\"gx__headRow\">\n <h1 className=\"gx__title\">{PANEL_TITLE}</h1>\n <span className={`gx__pill ${available > 0 ? 'gx__pill--ok' : 'gx__pill--warn'}`}><span className=\"gx__dot\" />{available} 个应用可用</span>\n {syncing ? <span className=\"gx__pill gx__pill--warn\"><span className=\"gx__dot\" />拉取中</span> : null}\n </div>\n <div className=\"gx__meta\">观其象,知其因。在对话中输入 /guanxiang 描述问题并附上日志,观象会按应用派发分析。</div>\n <div className=\"gx__tabs\" role=\"tablist\">\n {TABS.map(item => (\n <button key={item.id} type=\"button\" role=\"tab\" className=\"gx__tab\" aria-selected={tab === item.id} onClick={() => { setTab(item.id) }}>{item.label}</button>\n ))}\n </div>\n </header>\n {state.overlayError === undefined ? null : <div className=\"gx__notice gx__notice--error\">面板设置文件未生效:{state.overlayError}。保存一次会用当前内容覆盖它。</div>}\n <div className=\"gx__body\" role=\"tabpanel\">\n {tab === 'repos' ? <ReposTab draft={draft} setDraft={setDraft} state={state} busy={busy || dirty} onSync={(url) => { void run(() => syncRepos(url), url === undefined ? '已开始重新拉取全部仓库' : '已开始重新拉取', false) }} /> : null}\n {tab === 'download' ? <DownloadTab draft={draft} setDraft={setDraft} overridden={state.overridden} /> : null}\n {tab === 'advanced' ? <AdvancedTab draft={draft} setDraft={setDraft} overridden={state.overridden} busy={busy} onReset={() => { void run(resetSettings, '已恢复为 cordis.yml 中的配置', true) }} /> : null}\n </div>\n {notice === undefined ? null : <div className={`gx__notice gx__notice--${notice.kind}`}>{notice.text}</div>}\n <div className=\"gx__saveBar\">\n <span className=\"gx__saveMsg\">{dirty ? '有未保存的修改' : '设置已保存'}</span>\n <button type=\"button\" className=\"gx__btn gx__btn--ghost\" disabled={!dirty || busy} onClick={() => { setDraft(toDraft(state.settings)); setNotice(undefined) }}>放弃修改</button>\n <button type=\"button\" className=\"gx__btn gx__btn--primary\" disabled={!dirty || busy} onClick={save}>保存</button>\n </div>\n </div>\n )\n}\n\ninterface TabProps {\n draft: Draft\n setDraft: (next: Draft) => void\n}\n\nfunction ReposTab({ draft, setDraft, state, busy, onSync }: TabProps & { state: StateWire; busy: boolean; onSync: (url?: string) => void }): JSX.Element {\n const [url, setUrl] = useState('')\n const [branch, setBranch] = useState('')\n const status = useMemo(() => new Map(state.repos.map(repo => [repo.url, repo])), [state.repos])\n const add = (): void => {\n const trimmed = url.trim()\n if (trimmed === '' || draft.repos.some(repo => repo.url === trimmed)) return\n setDraft({ ...draft, repos: [...draft.repos, branch.trim() === '' ? { url: trimmed } : { url: trimmed, branch: branch.trim() }] })\n setUrl('')\n setBranch('')\n }\n return (\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">应用仓库 <Overridden keys={state.overridden} name=\"repos\" /></h2>\n <div className=\"gx__list\">\n {draft.repos.length === 0 ? <div className=\"gx__emptyRow\">还没有应用仓库。添加 git 地址后保存,观象会把仓库拉取到本机。</div> : null}\n {draft.repos.map((repo, index) => {\n const current = status.get(repo.url)\n const saved = current !== undefined && current.branch === repo.branch\n return (\n <div key={repo.url} className=\"gx__row gx__row--top\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">\n <span className=\"gx__mono\">{repo.url}</span>\n {repo.branch === undefined ? null : <span className=\"gx__tag\">{repo.branch}</span>}\n {phasePill(saved ? current : undefined)}\n </div>\n {saved && current.app !== undefined\n ? <div className=\"gx__rowSummary\">{current.app.name}(<span className=\"gx__mono\">{current.app.id}</span>)· SKILL <span className=\"gx__mono\">/{current.app.skill}</span>{current.activeRuns > 0 ? ` · ${current.activeRuns} 个分析进行中` : ''}</div>\n : null}\n {saved && current.app !== undefined ? <div className=\"gx__rowSummary\">{current.app.description}</div> : null}\n {saved && current.commit !== undefined\n ? <div className=\"gx__rowSummary\">提交 <span className=\"gx__mono\">{current.commit.slice(0, 8)}</span>({formatTime(current.commitTime)})· 拉取于 {formatTime(current.syncedAt)} · <span className=\"gx__mono\">{current.dir}</span></div>\n : null}\n {saved && current.error !== undefined ? <div className=\"gx__rowError\">{current.error}</div> : null}\n </div>\n <div className=\"gx__rowCtl\">\n {saved ? <button type=\"button\" className=\"gx__btn\" disabled={busy || current.phase === 'syncing'} onClick={() => { onSync(repo.url) }}>重新拉取</button> : null}\n <button type=\"button\" className=\"gx__btn gx__btn--danger\" onClick={() => { setDraft({ ...draft, repos: draft.repos.filter((_, position) => position !== index) }) }}>删除</button>\n </div>\n </div>\n )\n })}\n <div className=\"gx__addRow\">\n <input className=\"gx__input\" placeholder=\"git 地址,例如 git@github.com:example/order-service.git\" value={url} onChange={(event) => { setUrl(event.target.value) }} onKeyDown={(event) => { if (event.key === 'Enter') add() }} />\n <input className=\"gx__input gx__input--branch\" placeholder=\"分支(可选)\" value={branch} onChange={(event) => { setBranch(event.target.value) }} onKeyDown={(event) => { if (event.key === 'Enter') add() }} />\n <button type=\"button\" className=\"gx__btn\" disabled={url.trim() === ''} onClick={add}>添加</button>\n </div>\n </div>\n <p className=\"gx__secFoot\">使用本机已有的 git 凭据(SSH key 或 credential helper)拉取,不留分支时跟随远端默认分支。每个仓库需包含 <span className=\"gx__mono\">.dsh/guanxiang.yaml</span> 与其中声明的日志分析 SKILL。删除地址不会删除本地目录。</p>\n {state.repos.length > 0 ? <div><button type=\"button\" className=\"gx__btn\" disabled={busy} onClick={() => { onSync() }}>重新拉取全部</button></div> : null}\n </section>\n )\n}\n\nfunction DownloadTab({ draft, setDraft, overridden }: TabProps & { overridden: readonly EditableKey[] }): JSX.Element {\n const [host, setHost] = useState('')\n const add = (): void => {\n const trimmed = host.trim().toLowerCase()\n if (trimmed === '' || draft.allowedHosts.includes(trimmed)) return\n setDraft({ ...draft, allowedHosts: [...draft.allowedHosts, trimmed] })\n setHost('')\n }\n return (\n <>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">允许下载的域名 <Overridden keys={overridden} name=\"download\" /></h2>\n <div className=\"gx__list\">\n {draft.allowedHosts.length === 0 ? <div className=\"gx__emptyRow\">未配置域名,消息中的日志下载链接不会被下载。</div> : null}\n {draft.allowedHosts.map(item => (\n <div key={item} className=\"gx__row\">\n <div className=\"gx__rowText\"><span className=\"gx__mono\">{item}</span></div>\n <div className=\"gx__rowCtl\"><button type=\"button\" className=\"gx__btn gx__btn--danger\" onClick={() => { setDraft({ ...draft, allowedHosts: draft.allowedHosts.filter(entry => entry !== item) }) }}>删除</button></div>\n </div>\n ))}\n <div className=\"gx__addRow\">\n <input className=\"gx__input\" placeholder=\"主机名,例如 oss.example.com\" value={host} onChange={(event) => { setHost(event.target.value) }} onKeyDown={(event) => { if (event.key === 'Enter') add() }} />\n <button type=\"button\" className=\"gx__btn\" disabled={host.trim() === ''} onClick={add}>添加</button>\n </div>\n </div>\n <p className=\"gx__secFoot\">只填主机名,同时允许它的子域名。下载不携带任何鉴权信息,每次重定向都会重新校验域名。</p>\n </section>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">限制</h2>\n <div className=\"gx__list\">\n <NumberRow label=\"单文件大小上限\" unit=\"MB\" value={draft.maxMb} onChange={(value) => { setDraft({ ...draft, maxMb: value }) }} />\n <NumberRow label=\"单文件下载超时\" unit=\"秒\" value={draft.timeoutSec} onChange={(value) => { setDraft({ ...draft, timeoutSec: value }) }} />\n </div>\n </section>\n </>\n )\n}\n\nfunction AdvancedTab({ draft, setDraft, overridden, busy, onReset }: TabProps & { overridden: readonly EditableKey[]; busy: boolean; onReset: () => void }): JSX.Element {\n return (\n <>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">仓库</h2>\n <div className=\"gx__list\">\n <div className=\"gx__row\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">拉取目录 <Overridden keys={overridden} name=\"reposDir\" /></div>\n <div className=\"gx__rowSummary\">每个仓库拉取到该目录下的同名子目录,观象只读使用。</div>\n </div>\n <div className=\"gx__rowCtl\"><input className=\"gx__input gx__input--wide gx__mono\" value={draft.reposDir} onChange={(event) => { setDraft({ ...draft, reposDir: event.target.value }) }} /></div>\n </div>\n <NumberRow label=\"更新间隔\" summary=\"输入 /guanxiang 时,距上次拉取超过该时间的仓库会先更新;0 表示每次都更新。\" unit=\"分钟\" value={draft.fetchIntervalMin} overridden={overridden.includes('fetchIntervalMs')} onChange={(value) => { setDraft({ ...draft, fetchIntervalMin: value }) }} />\n </div>\n </section>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">派发</h2>\n <div className=\"gx__list\">\n <NumberRow label=\"单次应用数上限\" summary=\"一次派发最多同时分析的应用数,不应超过 DSH 的活动子 Agent 上限(默认 8)。\" unit=\"个\" value={draft.maxAppsPerRequest} overridden={overridden.includes('maxAppsPerRequest')} onChange={(value) => { setDraft({ ...draft, maxAppsPerRequest: value }) }} />\n <NumberRow label=\"派发超时\" summary=\"等待全部子 Agent 的最长时间,超时后停止仍在运行的子 Agent。\" unit=\"分钟\" value={draft.dispatchTimeoutMin} overridden={overridden.includes('dispatchTimeoutMs')} onChange={(value) => { setDraft({ ...draft, dispatchTimeoutMin: value }) }} />\n </div>\n </section>\n <section className=\"gx__sec\">\n <h2 className=\"gx__secTitle\">面板设置</h2>\n <div className=\"gx__list\">\n <div className=\"gx__row\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">恢复 cordis.yml 配置</div>\n <div className=\"gx__rowSummary\">删除面板保存的全部设置($DSH_HOME/guanxiang/settings.json 中的覆盖值)。</div>\n </div>\n <div className=\"gx__rowCtl\"><button type=\"button\" className=\"gx__btn gx__btn--danger\" disabled={busy || overridden.length === 0} onClick={onReset}>恢复</button></div>\n </div>\n </div>\n </section>\n </>\n )\n}\n\nfunction NumberRow({ label, summary, unit, value, overridden = false, onChange }: { label: string; summary?: string; unit: string; value: string; overridden?: boolean; onChange: (value: string) => void }): JSX.Element {\n return (\n <div className=\"gx__row\">\n <div className=\"gx__rowText\">\n <div className=\"gx__rowLabel\">{label}{overridden ? <span className=\"gx__tag gx__tag--accent\">面板设置</span> : null}</div>\n {summary === undefined ? null : <div className=\"gx__rowSummary\">{summary}</div>}\n </div>\n <div className=\"gx__rowCtl\">\n <input className=\"gx__input gx__input--num\" inputMode=\"decimal\" value={value} onChange={(event) => { onChange(event.target.value) }} />\n <span className=\"gx__unit\">{unit}</span>\n </div>\n </div>\n )\n}\n","/**\n * 「观象」页面样式。使用 DSH 语义 token(`--dsw-alias-*`)并带中性回退,\n * 页面随当前主题变化。\n *\n * @module dsh-guanxiang/client/styles\n */\n\nconst STYLE_ATTRIBUTE = 'data-dsh-guanxiang-css'\n\nconst CSS = `\n.gx-page{width:100%;height:100%;min-width:0;min-height:0;overflow:auto;scrollbar-gutter:stable;background:var(--dsw-alias-bg-base,#fff)}\n.gx-page__content{box-sizing:border-box;max-width:816px;margin:0 auto;padding:18px clamp(24px,4vw,48px) 48px}\n[data-platform='darwin'] .gx-page__content{padding-top:calc(18px + var(--dsh-frame-top-clearance,0px))}\n.gx{--gx-fg:var(--dsw-alias-label-primary,#1f2328);--gx-fg2:var(--dsw-alias-label-secondary,#5c6169);--gx-fg3:var(--dsw-alias-label-tertiary,#8a8f98);--gx-dim:var(--dsw-alias-label-dimmed,#b0b4ba);--gx-bg:var(--dsw-alias-bg-base,#fff);--gx-line:var(--dsw-alias-border-l2,rgba(0,0,0,.12));--gx-hover:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05));--gx-accent:var(--dsw-alias-state-business-primary,#0a67d3);--gx-ok:var(--dsw-alias-state-success-primary,#0a7d33);--gx-warn:var(--dsw-alias-state-warn-primary,#a15c00);--gx-bad:var(--dsw-alias-state-error-primary,#c62828);--gx-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:flex;flex-direction:column;gap:14px;width:100%;max-width:720px;color:var(--gx-fg);font-size:13px;line-height:20px}\n.gx__head{position:sticky;top:0;z-index:2;display:flex;flex-direction:column;gap:4px;padding:10px 0 6px;background:var(--gx-bg)}\n.gx__headRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.gx__title{margin:0 2px 0 0;font-size:20px;font-weight:500;line-height:28px}\n.gx__meta{color:var(--gx-fg3);font-size:12px}\n.gx__mono{font-family:var(--gx-mono);font-size:12px}\n.gx__pill{display:inline-flex;align-items:center;gap:5px;padding:0 8px;border-radius:999px;font-size:12px;line-height:20px;white-space:nowrap;color:var(--gx-fg3);background:color-mix(in srgb,currentColor 10%,transparent)}\n.gx__pill--ok{color:var(--gx-ok)}\n.gx__pill--warn{color:var(--gx-warn)}\n.gx__pill--bad{color:var(--gx-bad)}\n.gx__dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}\n.gx__tabs{display:flex;align-items:center;gap:18px;border-bottom:1px solid var(--gx-line);overflow-x:auto;scrollbar-width:none}\n.gx__tab{display:inline-flex;align-items:center;min-height:34px;padding:0;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--gx-fg2);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.gx__tab:hover{color:var(--gx-fg)}\n.gx__tab[aria-selected=\"true\"]{color:var(--gx-fg);border-bottom-color:currentColor;font-weight:600}\n.gx__body{display:flex;flex-direction:column;gap:22px;padding:4px 0 8px}\n.gx__sec{display:flex;flex-direction:column;gap:6px;margin:0}\n.gx__secTitle{margin:0;font-size:12px;font-weight:600;line-height:18px;color:var(--gx-fg3);letter-spacing:.02em}\n.gx__secFoot{margin:0;color:var(--gx-fg3);font-size:12px;line-height:18px}\n.gx__list{display:flex;flex-direction:column;border:1px solid var(--gx-line);border-radius:10px}\n.gx__list>*+*{border-top:1px solid var(--gx-line)}\n.gx__row{display:flex;align-items:center;gap:16px;padding:11px 14px;min-height:28px}\n.gx__row--top{align-items:flex-start}\n.gx__rowText{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}\n.gx__rowLabel{display:flex;align-items:center;gap:6px;flex-wrap:wrap;overflow-wrap:anywhere}\n.gx__rowSummary{color:var(--gx-fg3);font-size:12px;line-height:18px;overflow-wrap:anywhere}\n.gx__rowError{color:var(--gx-bad);font-size:12px;line-height:18px;overflow-wrap:anywhere;white-space:pre-wrap}\n.gx__rowCtl{flex:none;display:flex;align-items:center;gap:6px;justify-content:flex-end}\n.gx__tag{display:inline-flex;align-items:center;padding:0 5px;border-radius:4px;font-size:11px;line-height:16px;white-space:nowrap;color:var(--gx-fg2);background:var(--gx-hover)}\n.gx__tag--accent{color:var(--gx-accent);background:color-mix(in srgb,var(--gx-accent) 10%,transparent)}\n.gx__input{box-sizing:border-box;width:100%;min-width:0;height:30px;padding:0 10px;border:1px solid var(--gx-line);border-radius:7px;background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;font:inherit;font-size:13px;outline:none}\n.gx__input:focus{border-color:var(--gx-accent)}\n.gx__input--num{width:120px;text-align:right;font-variant-numeric:tabular-nums}\n.gx__input--wide{width:320px}\n.gx__unit{min-width:36px;color:var(--gx-fg3);font-size:12px}\n.gx__addRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 14px}\n.gx__addRow .gx__input{flex:1 1 160px;width:auto}\n.gx__addRow .gx__input--branch{flex:0 1 140px}\n.gx__emptyRow{padding:11px 14px;color:var(--gx-fg3);font-size:12px}\n.gx__btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:28px;padding:0 12px;border:1px solid var(--gx-line);border-radius:7px;background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}\n.gx__btn:hover:not(:disabled){background:var(--gx-hover)}\n.gx__btn:disabled{color:var(--gx-dim);cursor:default}\n.gx__btn--primary{background:var(--dsw-alias-button-primary-fill,var(--gx-accent));border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}\n.gx__btn--primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#0b5cba)}\n.gx__btn--primary:disabled{opacity:.45;color:var(--dsw-alias-label-primary-foreground,#fff)}\n.gx__btn--ghost{border-color:transparent;color:var(--gx-fg2)}\n.gx__btn--danger{color:var(--gx-bad)}\n.gx__btn:focus-visible,.gx__tab:focus-visible{outline:2px solid var(--gx-accent);outline-offset:2px}\n.gx__notice{padding:8px 12px;border-radius:8px;font-size:12px;line-height:18px;overflow-wrap:anywhere}\n.gx__notice--ok{color:var(--gx-ok);background:color-mix(in srgb,var(--gx-ok) 9%,transparent)}\n.gx__notice--error{color:var(--gx-bad);background:color-mix(in srgb,var(--gx-bad) 8%,transparent)}\n.gx__saveBar{position:sticky;bottom:0;z-index:2;display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 0;border-top:1px solid var(--gx-line);background:var(--gx-bg)}\n.gx__saveMsg{flex:1;min-width:0;color:var(--gx-fg2);font-size:12px}\n.gx__loading{padding:16px 0;color:var(--gx-fg3);font-size:12px}\n`\n\n/**\n * Install the page stylesheet once.\n * @returns disposer removing it.\n */\nexport function installStyles(): () => void {\n const existing = document.querySelector(`style[${STYLE_ATTRIBUTE}]`)\n if (existing !== null) return () => {}\n const element = document.createElement('style')\n element.setAttribute(STYLE_ATTRIBUTE, '')\n element.textContent = CSS\n document.head.append(element)\n return () => { element.remove() }\n}\n","/**\n * 观象 client half:左侧栏全局入口「观象」+ 主区整页设置面板。\n *\n * 只依赖平台核心的 `slots` 服务与 react(bundle 里 react 走平台模块表,\n * 其余全部内联);页面数据来自 Host 半侧的 `/guanxiang/*` 路由。\n *\n * @module dsh-guanxiang/client\n */\n\nimport { GuanxiangIcon } from './icon.tsx'\nimport { GuanxiangPage, PANEL_TITLE } from './page.tsx'\nimport { installStyles } from './styles.ts'\n\n/** Slots runtime surface this bundle uses: list slots by `id`, keyed slots by `key`. */\nexport interface ClientSlots {\n inject(slot: string, contribution: () => unknown): void\n register(\n declaration:\n | { name: string; id: string; order?: number; label?: () => string }\n | { name: string; key: string },\n component: (props: never) => unknown,\n ): () => void\n}\n\n/** Client plugin context. */\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-guanxiang'\n\n/** Required client services: the Slots registry is part of the web shell core. */\nexport const inject = ['slots']\n\n/**\n * Main panel id: the `sidebar.panellist` row id and the matching `main` slot\n * key. Must not change once released: DSH keys navigation state by it.\n */\nexport const PANEL_ID = 'guanxiang'\n/** Left-sidebar row order (dsh-channels uses 40). */\nexport const PANEL_ORDER = 45\n\n/**\n * Mount the left-sidebar entry, its main-column page, and the stylesheet.\n * @param ctx - client plugin context carrying the slots registry.\n */\nexport function apply(ctx: ClientContext): void {\n if (typeof ctx.effect === 'function') ctx.effect(() => installStyles(), 'dsh-guanxiang: panel styles')\n else installStyles()\n ctx.slots.inject('main', () => ctx.slots.register({ name: 'main', key: PANEL_ID }, GuanxiangPage))\n ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({\n name: 'sidebar.panellist',\n id: PANEL_ID,\n order: PANEL_ORDER,\n label: () => PANEL_TITLE,\n }, GuanxiangIcon))\n}\n"],"mappings":"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"}
|