docs: refresh top-level README + add dsh-arch-diagram skill
Replaces the 20-line stub with a bilingual README pair covering:
- getting started (`pnpm run demo:repl` + Node ^22.19 || ≥24 + pnpm 11.7)
- editor integration (Zed / VS Code / Cursor over ACP)
- programmatic embedding via `@deepseek-ai/dsh-app-boot` (with the
two failure paths of `boot()` vs `installFailLoud` spelled out)
- writing a plugin (`echo-tool` grounded in `examples/echo-agent`, with
the runnable command `pnpm run demo:echo`)
- Packages table grouped by family, including `session-query`,
`context`, and Support (with `invariants` called out as a runtime
diagnostic mounted by `dsh-agent-core`, not test-only)
- deep-dive links, community, license
Bilingual pairing: English and Chinese sides share byte-identical code
blocks and mirroring link ordinals; language-asymmetric community
channels (Discord/X in EN, WeCom in ZH) sit inside HTML `<a href>`
tags that the pairing gate's structural signature excludes by design.
ZH side follows `docs/i18n/terminology.md` — first `agent` occurrence
annotated as `agent(智能体)`, plain `agent` thereafter.
Ships an in-repo skill at `.agents/skills/dsh-arch-diagram/` that owns
the two architecture PNGs (`assets/arch-{en,zh}.png`). HTML templates
+ shared CSS + a `render.sh` that renders via Chrome headless. The
renderer is portable (auto-detects Chrome/Chromium on macOS + Linux,
uses `--no-sandbox` under root for container envs, discovers the local
http.server port from Python's own startup line so `lsof` isn't
required). The 12-card capability row uses `flex: 0 0 auto` so
adjacent card backgrounds cannot clip descriptions; layout tuned to
1560px page / 96% row-width / 1820×580 render viewport.
Consolidates the four review-response commits from the initial round
of ds-review-bot feedback; each review round is documented in a
top-level PR comment for traceability.
This commit is contained in:
158
.agents/skills/dsh-arch-diagram/SKILL.md
Normal file
158
.agents/skills/dsh-arch-diagram/SKILL.md
Normal file
@@ -0,0 +1,158 @@
|
||||
---
|
||||
name: dsh-arch-diagram
|
||||
description: Regenerate the DeepSeek Harness "System overview" architecture PNGs (English + Chinese) that the top-level README embeds. Reflects the current state of packages/* and docs/architecture.md. Use when the codebase adds/removes/renames services and the README diagram must be refreshed.
|
||||
---
|
||||
|
||||
# dsh-arch-diagram
|
||||
|
||||
Produces `assets/arch-en.png` and `assets/arch-zh.png` in this repo — the two hero images embedded at the top of `README.md` and `README.zh.md`.
|
||||
|
||||
The pipeline is HTML + Chrome headless, so text quality, layout, and connector geometry are **deterministic** — every rerun produces the same output regardless of model.
|
||||
|
||||
Aesthetic is locked to the DeepSeek brand: light blue gradient background, brand-blue Cordis bar, sans-serif everywhere, no serif / no italic / no hand-drawn feel.
|
||||
|
||||
---
|
||||
|
||||
## When to use
|
||||
|
||||
- User asks to "regenerate" or "refresh" the Harness architecture diagram
|
||||
- User points out `packages/` has changed (new services, renames) and the README diagram is stale
|
||||
- User just added a new `packages/<family>/` or a new `ctx.*` service and wants the diagram to reflect it
|
||||
|
||||
## When NOT to use
|
||||
|
||||
- User wants a different diagram (Code Mode, Workflow fanout, agent lifecycle) — those belong in `docs/` and need their own skill
|
||||
- User wants a different visual aesthetic (dark mode, hand-drawn, editorial-serif) — this skill is locked to DeepSeek brand
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Enumerate current services from the repo
|
||||
|
||||
The diagram is grounded in two lists that live in the docs:
|
||||
|
||||
- **Default Services** — from `packages/core/*` and `docs/architecture.md`'s "Default Services" table. As of last refresh: `ctx.sessions`, `ctx.systemPrompt`, `ctx.tools`, `ctx.agents`, `ctx.agentLoop`.
|
||||
- **Capability Services** — from the non-core capability packages and `docs/architecture.md`'s "Capability Services" table. As of last refresh: `ctx.llm`, `ctx.bash`, `ctx.sandbox`, `ctx.codeRuntime`, `ctx.fs`, `ctx.skills`, `ctx.web`, `ctx.compact`, `ctx.subagents`, `ctx.workflows`, `ctx.sessionPersistence`, `ctx.sessionQuery`.
|
||||
|
||||
**Do this**:
|
||||
|
||||
1. Read `docs/architecture.md`
|
||||
2. Extract the two `ctx.*` tables verbatim
|
||||
3. Cross-check against `packages/`: services live under `packages/core/*` (defaults) or under `packages/<family>/<package>/` (capabilities). The inner package dir mirrors the npm name without the `@deepseek-ai/dsh-` prefix — e.g. `packages/session-query/session-query/` publishes `@deepseek-ai/dsh-session-query`. Never assume the inner dir carries a `dsh-` prefix.
|
||||
|
||||
### 2. Diff against the current HTML templates
|
||||
|
||||
Read `harness-arch-en.html` and `harness-arch-zh.html` in this skill directory. Pull out the currently-rendered `ctx.*` names. Report the diff:
|
||||
|
||||
```
|
||||
Default services: no change
|
||||
Capability services: + ctx.<new>, − ctx.<removed>, ↻ ctx.<renamed>
|
||||
```
|
||||
|
||||
### 3. Update the templates
|
||||
|
||||
If there are changes, edit both HTML files in place. Each card is one line:
|
||||
|
||||
```html
|
||||
<div class="card"><span class="name">ctx.something</span><span class="desc">short description</span></div>
|
||||
```
|
||||
|
||||
For a **new** service:
|
||||
- Description is **short** — 2–4 words (EN), 3–6 chars (ZH)
|
||||
- Ground the description in what the package's README or its `ctx.<name>.register()` **actually** does. Do NOT paraphrase from an abstract or invent capabilities the code doesn't have.
|
||||
- Add the card in **both** languages, in docs order.
|
||||
|
||||
For a **removed** service: delete the card in both files.
|
||||
|
||||
For a **renamed** service: update the `.name` span in both files.
|
||||
|
||||
**Do not touch the layout**. Rows are:
|
||||
- Top row (`row top`): exactly 5 default-service cards
|
||||
- Bottom row (`row bottom`): 12 capability-service cards
|
||||
|
||||
If the capability count grows past ~13, the row gets visually tight. Stop and ask the user before shrinking fonts or wrapping to two rows.
|
||||
|
||||
### 4. Render to PNG
|
||||
|
||||
```bash
|
||||
bash .agents/skills/dsh-arch-diagram/render.sh
|
||||
```
|
||||
|
||||
Default output: `<repo>/assets/arch-en.png` and `<repo>/assets/arch-zh.png` — the paths that `README.md` and `README.zh.md` already reference.
|
||||
|
||||
Pass an alternate directory as `$1` to write elsewhere. `CHROME` and `PORT` env vars override the browser path and http.server port.
|
||||
|
||||
The script:
|
||||
- Spins up a temporary `python3 -m http.server` on `127.0.0.1` (needed so Google Fonts CDN loads reliably in Chrome headless)
|
||||
- Runs Chrome headless twice at `--force-device-scale-factor=2 --window-size=1536,580`
|
||||
- Writes two 3072×1160 PNGs (~600 KB each)
|
||||
- Tears down the server
|
||||
|
||||
### 5. Verify by eye — mandatory, do not skip
|
||||
|
||||
After rendering, **open each PNG and visually check every item** in this list. Do not report the diagram as done without going through it.
|
||||
|
||||
- [ ] Top-row card count matches the Default Services list you extracted in §1, every name rendered in full (no truncation on the longest, e.g. `ctx.systemPrompt` / `ctx.agentLoop`)
|
||||
- [ ] Bottom-row card count matches the Capability Services list you extracted in §1, every name in full (especially the longest, e.g. `ctx.sessionPersistence`)
|
||||
- [ ] Every card has a description underneath, and the description matches what the code does (not made up)
|
||||
- [ ] Bar reads `Cordis · microkernel (vendored)` (EN) / `Cordis · 微内核 (vendored)` (ZH)
|
||||
- [ ] Number of vertical connector lines equals (top-row cards + bottom-row cards) from §1. All straight vertical, all parallel, each reaching the horizontal center of its card
|
||||
- [ ] `cordis.yml` sidebar visible on the right; dashed arrow points left into the bar
|
||||
- [ ] `deployment leaf` / `部署清单` and `picks which plugins load` / `决定哪些插件加载` are readable and NOT crossed through by any connector line
|
||||
- [ ] Chinese descriptions render as CJK glyphs, not `□` tofu boxes (means fonts loaded)
|
||||
- [ ] No large empty gradient area at the bottom of the image — if there is, tune `--window-size` in `render.sh`
|
||||
|
||||
If any check fails: fix the CSS or template, re-run `render.sh`, re-check. Do not report "done" on first render without visually confirming.
|
||||
|
||||
---
|
||||
|
||||
## Design system (locked — do not modify without explicit user request)
|
||||
|
||||
Colors:
|
||||
- Background: `linear-gradient(135deg, #ffffff 0%, #e8effc 100%)`
|
||||
- Brand blue (Cordis bar + connectors): `#4a6ef5`
|
||||
- Ink text: `#1a1a1a`
|
||||
- Muted text: `#8a8f9c`
|
||||
- Card border: `#dde3ef`
|
||||
- Card fill: `#ffffff`
|
||||
|
||||
Typography (**sans-serif only** — never serif, never italic):
|
||||
- English title & body: **Inter** (Google Fonts)
|
||||
- Chinese title & body: **Noto Sans SC** (Google Fonts) with `PingFang SC` local fallback
|
||||
- Code labels (`ctx.*`, `cordis.yml`, `vendored`): **JetBrains Mono** (Google Fonts)
|
||||
|
||||
Layout:
|
||||
- `.page` max-width 1560 px, centered
|
||||
- `.bus-row`, `.row.top`, `.row.bottom` all 96% width, centered
|
||||
- Top row: `flex: 1` on each card (equal widths)
|
||||
- Bottom row: `justify-content: space-between` + `flex: 0 0 auto` on each card — cards keep their intrinsic (nowrap) width and adjacent card backgrounds cannot clip a description
|
||||
- Bus: `#4a6ef5` filled, 60 px tall, 8 px radius
|
||||
- `cordis.yml` sidebar `position: absolute` at `left: calc(100% + 20px)` of `.bus-row`, width 130 px
|
||||
- All connectors: CSS `::before` / `::after` pseudo-elements, 2 px wide, 40 px tall, `#4a6ef5`, centered via `left: 50%; transform: translateX(-50%)` — this is what guarantees pixel-perfect vertical parallel lines regardless of card content
|
||||
|
||||
Rendering:
|
||||
- Chrome headless `--force-device-scale-factor=2`
|
||||
- `--window-size=1820,580` → output is 3640×1160 (aspect ~3.14:1, wider than square-ish so a 12-card capability row fits without truncation)
|
||||
- Local http.server on `127.0.0.1` so CDN fonts load cleanly
|
||||
|
||||
---
|
||||
|
||||
## Files in this skill
|
||||
|
||||
- `SKILL.md` — this file
|
||||
- `harness-arch.css` — shared design system (colors, fonts, layout, connectors)
|
||||
- `harness-arch-en.html` — English template
|
||||
- `harness-arch-zh.html` — Chinese template
|
||||
- `render.sh` — Chrome headless renderer
|
||||
|
||||
Templates are self-contained. Fonts load from Google Fonts CDN; no local font files.
|
||||
|
||||
---
|
||||
|
||||
## Rules I keep breaking (and shouldn't)
|
||||
|
||||
1. **Verify the output by looking at it.** Don't report "done" and move on — actually inspect each PNG and check every item in §5.
|
||||
2. **Ground service descriptions in the code, not the docs' abstract, not the marketing copy.** If unsure what a package does, read its `src/` or the `ctx.<name>.register()` block.
|
||||
3. **Don't touch layout unless asked.** 96% row/bar width, 40 px connector height, and 60 px bar height are all tuned. Card counts grow one at a time; once the bottom row starts to visibly overflow again (adjacent card backgrounds clipping a description, or the `cordis.yml` sidebar falling outside the render viewport), stop and ask before shrinking fonts, widening the viewport further, or wrapping to two rows.
|
||||
4. **Never introduce a serif font or italic.** Not for titles, not for annotations, not "to make it feel editorial." This diagram is DeepSeek brand: sans-serif, upright, only.
|
||||
66
.agents/skills/dsh-arch-diagram/harness-arch-en.html
Normal file
66
.agents/skills/dsh-arch-diagram/harness-arch-en.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1536, initial-scale=1.0">
|
||||
<title>DeepSeek Harness · Architecture</title>
|
||||
<link rel="stylesheet" href="harness-arch.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<p class="eyebrow">ARCHITECTURE</p>
|
||||
<h1>System overview</h1>
|
||||
|
||||
<div class="diagram">
|
||||
<p class="section-label top">DEFAULT SERVICES</p>
|
||||
|
||||
<div class="row top">
|
||||
<div class="card"><span class="name">ctx.sessions</span><span class="desc">event-sourced log</span></div>
|
||||
<div class="card"><span class="name">ctx.systemPrompt</span><span class="desc">prompt assembly</span></div>
|
||||
<div class="card"><span class="name">ctx.tools</span><span class="desc">tool registry & pipeline</span></div>
|
||||
<div class="card"><span class="name">ctx.agents</span><span class="desc">agent handles & events</span></div>
|
||||
<div class="card"><span class="name">ctx.agentLoop</span><span class="desc">ReAct control loop</span></div>
|
||||
</div>
|
||||
|
||||
<div class="bus-row">
|
||||
<div class="bus">
|
||||
<span class="core">Cordis</span>
|
||||
<span class="dot">·</span>
|
||||
<span class="core">microkernel</span>
|
||||
<span class="vendored">(vendored)</span>
|
||||
</div>
|
||||
<div class="yml-side">
|
||||
<div class="yml-arrow">
|
||||
<svg viewBox="0 0 30 12" width="30" height="12" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="30" y1="6" x2="8" y2="6" stroke="#8a8f9c" stroke-width="1.2" stroke-dasharray="3 3"/>
|
||||
<polyline points="8,3 2,6 8,9" fill="none" stroke="#8a8f9c" stroke-width="1.2" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="yml-box">
|
||||
<div class="file">cordis.yml</div>
|
||||
<div class="sub">deployment leaf</div>
|
||||
</div>
|
||||
<div class="yml-note">picks which plugins load</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row bottom">
|
||||
<div class="card"><span class="name">ctx.llm</span><span class="desc">LLM provider adapter</span></div>
|
||||
<div class="card"><span class="name">ctx.bash</span><span class="desc">shell execution</span></div>
|
||||
<div class="card"><span class="name">ctx.sandbox</span><span class="desc">process confinement</span></div>
|
||||
<div class="card"><span class="name">ctx.codeRuntime</span><span class="desc">JS worker runtime</span></div>
|
||||
<div class="card"><span class="name">ctx.fs</span><span class="desc">filesystem + policy</span></div>
|
||||
<div class="card"><span class="name">ctx.skills</span><span class="desc">reusable skills</span></div>
|
||||
<div class="card"><span class="name">ctx.web</span><span class="desc">search & fetch</span></div>
|
||||
<div class="card"><span class="name">ctx.compact</span><span class="desc">context compression</span></div>
|
||||
<div class="card"><span class="name">ctx.subagents</span><span class="desc">spawn / fork / ACP</span></div>
|
||||
<div class="card"><span class="name">ctx.workflows</span><span class="desc">dynamic orchestration</span></div>
|
||||
<div class="card"><span class="name">ctx.sessionPersistence</span><span class="desc">JSONL / SQLite</span></div>
|
||||
<div class="card"><span class="name">ctx.sessionQuery</span><span class="desc">unified log reads</span></div>
|
||||
</div>
|
||||
|
||||
<p class="section-label bottom">CAPABILITY SERVICES</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
66
.agents/skills/dsh-arch-diagram/harness-arch-zh.html
Normal file
66
.agents/skills/dsh-arch-diagram/harness-arch-zh.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1536, initial-scale=1.0">
|
||||
<title>DeepSeek Harness · 架构</title>
|
||||
<link rel="stylesheet" href="harness-arch.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<p class="eyebrow cn">架 构</p>
|
||||
<h1 class="cn">系统一览</h1>
|
||||
|
||||
<div class="diagram">
|
||||
<p class="section-label top cn">默 认 服 务</p>
|
||||
|
||||
<div class="row top">
|
||||
<div class="card"><span class="name">ctx.sessions</span><span class="desc cn">事件溯源日志</span></div>
|
||||
<div class="card"><span class="name">ctx.systemPrompt</span><span class="desc cn">系统提示词组装</span></div>
|
||||
<div class="card"><span class="name">ctx.tools</span><span class="desc cn">工具注册与执行</span></div>
|
||||
<div class="card"><span class="name">ctx.agents</span><span class="desc cn">Agent 实例与事件</span></div>
|
||||
<div class="card"><span class="name">ctx.agentLoop</span><span class="desc cn">ReAct 控制循环</span></div>
|
||||
</div>
|
||||
|
||||
<div class="bus-row">
|
||||
<div class="bus cn">
|
||||
<span class="core">Cordis</span>
|
||||
<span class="dot">·</span>
|
||||
<span class="core">微内核</span>
|
||||
<span class="vendored">(vendored)</span>
|
||||
</div>
|
||||
<div class="yml-side">
|
||||
<div class="yml-arrow">
|
||||
<svg viewBox="0 0 30 12" width="30" height="12" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="30" y1="6" x2="8" y2="6" stroke="#8a8f9c" stroke-width="1.2" stroke-dasharray="3 3"/>
|
||||
<polyline points="8,3 2,6 8,9" fill="none" stroke="#8a8f9c" stroke-width="1.2" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="yml-box">
|
||||
<div class="file">cordis.yml</div>
|
||||
<div class="sub cn">部署清单</div>
|
||||
</div>
|
||||
<div class="yml-note cn">决定哪些插件加载</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row bottom">
|
||||
<div class="card"><span class="name">ctx.llm</span><span class="desc cn">模型适配层</span></div>
|
||||
<div class="card"><span class="name">ctx.bash</span><span class="desc cn">命令行执行</span></div>
|
||||
<div class="card"><span class="name">ctx.sandbox</span><span class="desc cn">进程隔离</span></div>
|
||||
<div class="card"><span class="name">ctx.codeRuntime</span><span class="desc cn">JS 运行时</span></div>
|
||||
<div class="card"><span class="name">ctx.fs</span><span class="desc cn">文件读写</span></div>
|
||||
<div class="card"><span class="name">ctx.skills</span><span class="desc cn">可复用技能</span></div>
|
||||
<div class="card"><span class="name">ctx.web</span><span class="desc cn">网页搜索抓取</span></div>
|
||||
<div class="card"><span class="name">ctx.compact</span><span class="desc cn">上下文压缩</span></div>
|
||||
<div class="card"><span class="name">ctx.subagents</span><span class="desc cn">子智能体</span></div>
|
||||
<div class="card"><span class="name">ctx.workflows</span><span class="desc cn">动态编排</span></div>
|
||||
<div class="card"><span class="name">ctx.sessionPersistence</span><span class="desc cn">会话持久化</span></div>
|
||||
<div class="card"><span class="name">ctx.sessionQuery</span><span class="desc cn">会话日志查询</span></div>
|
||||
</div>
|
||||
|
||||
<p class="section-label bottom cn">能 力 服 务</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
220
.agents/skills/dsh-arch-diagram/harness-arch.css
Normal file
220
.agents/skills/dsh-arch-diagram/harness-arch.css
Normal file
@@ -0,0 +1,220 @@
|
||||
/* DeepSeek brand — bone-white + brand-blue, all sans-serif, no italic, no serif. */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
|
||||
:root {
|
||||
--bg-top: #ffffff;
|
||||
--bg-bot: #e8effc;
|
||||
--ink: #1a1a1a;
|
||||
--muted: #8a8f9c;
|
||||
--card-bd: #dde3ef;
|
||||
--card-bg: #ffffff;
|
||||
--brand: #4a6ef5;
|
||||
--brand-soft: #dbe4fe;
|
||||
|
||||
--sans: 'Inter', 'Noto Sans SC', 'PingFang SC', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
||||
--cn: 'Noto Sans SC', 'PingFang SC', 'Inter', -apple-system, sans-serif;
|
||||
--mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: var(--sans);
|
||||
color: var(--ink);
|
||||
background: linear-gradient(135deg, var(--bg-top) 0%, var(--bg-bot) 100%);
|
||||
padding: 40px 64px 32px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-feature-settings: "kern" 1, "liga" 1, "cv02" 1, "cv11" 1;
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: 1560px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* ── header ─────────────────────────────────────────── */
|
||||
.eyebrow {
|
||||
font-family: var(--sans);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.22em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.eyebrow.cn { font-family: var(--cn); letter-spacing: 0.4em; text-transform: none; }
|
||||
|
||||
h1 {
|
||||
font-family: var(--sans);
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.15;
|
||||
color: var(--ink);
|
||||
margin-bottom: 88px;
|
||||
}
|
||||
h1.cn { font-family: var(--cn); letter-spacing: 0.02em; font-weight: 600; }
|
||||
|
||||
/* ── diagram frame ──────────────────────────────────── */
|
||||
.diagram { position: relative; }
|
||||
|
||||
.section-label {
|
||||
font-family: var(--sans);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.22em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.section-label.cn { font-family: var(--cn); letter-spacing: 0.4em; text-transform: none; font-size: 12px; }
|
||||
.section-label.bottom { margin: 20px 0 0; }
|
||||
|
||||
/* ── card rows ──────────────────────────────────────── */
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
width: 96%;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
.row.top .card { flex: 1; }
|
||||
.row.bottom {
|
||||
gap: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
/* flex: 0 0 auto so cards keep their intrinsic (nowrap) width and
|
||||
neighbouring card backgrounds cannot clip a description. */
|
||||
.row.bottom .card { flex: 0 0 auto; }
|
||||
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-bd);
|
||||
border-radius: 6px;
|
||||
padding: 14px 10px;
|
||||
text-align: center;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
}
|
||||
.card .name {
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--ink);
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
.card .desc {
|
||||
font-family: var(--sans);
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: var(--muted);
|
||||
margin-top: 4px;
|
||||
line-height: 1.3;
|
||||
min-height: 28px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.card .desc.cn { font-family: var(--cn); }
|
||||
.row.bottom .card { padding: 10px 6px; }
|
||||
.row.bottom .card .name { font-size: 10px; }
|
||||
.row.bottom .card .desc { font-size: 9px; min-height: 22px; }
|
||||
.row.bottom .card .desc.cn { font-size: 10px; }
|
||||
|
||||
/* connectors — pure CSS, guaranteed vertical & parallel */
|
||||
.row.top .card::after,
|
||||
.row.bottom .card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 2px;
|
||||
background: var(--brand);
|
||||
}
|
||||
.row.top .card::after { top: 100%; height: 40px; }
|
||||
.row.bottom .card::before { bottom: 100%; height: 40px; }
|
||||
|
||||
/* ── bus row (Cordis) ───────────────────────────────── */
|
||||
.bus-row {
|
||||
position: relative;
|
||||
width: 96%;
|
||||
margin: 40px auto;
|
||||
}
|
||||
.bus {
|
||||
height: 60px;
|
||||
background: var(--brand);
|
||||
color: #ffffff;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--sans);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
.bus .dot { padding: 0 12px; opacity: 0.75; }
|
||||
.bus .vendored {
|
||||
font-family: var(--mono);
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
opacity: 0.8;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.bus.cn .core { font-family: var(--cn); }
|
||||
|
||||
/* ── cordis.yml side box (positioned outside bus width) ─ */
|
||||
.yml-side {
|
||||
position: absolute;
|
||||
left: calc(100% + 20px);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 130px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.yml-arrow {
|
||||
position: absolute;
|
||||
left: -30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 30px;
|
||||
height: 12px;
|
||||
}
|
||||
.yml-arrow svg { display: block; }
|
||||
.yml-box {
|
||||
width: 100%;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-bd);
|
||||
border-radius: 6px;
|
||||
padding: 12px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.yml-box .file {
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--ink);
|
||||
}
|
||||
.yml-box .sub {
|
||||
font-family: var(--sans);
|
||||
font-size: 10px;
|
||||
color: var(--muted);
|
||||
margin-top: 4px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.yml-box .sub.cn { font-family: var(--cn); letter-spacing: 0.05em; }
|
||||
.yml-note {
|
||||
font-family: var(--sans);
|
||||
font-size: 10px;
|
||||
color: var(--muted);
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.yml-note.cn { font-family: var(--cn); letter-spacing: 0.05em; }
|
||||
97
.agents/skills/dsh-arch-diagram/render.sh
Executable file
97
.agents/skills/dsh-arch-diagram/render.sh
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
# render.sh — Chrome headless renderer for the DeepSeek Harness architecture diagram.
|
||||
#
|
||||
# Writes arch-en.png and arch-zh.png (3072×1160, ~600 KB each) to the given output
|
||||
# directory. Default is <repo>/assets/, which is where the README image references
|
||||
# resolve.
|
||||
#
|
||||
# Usage:
|
||||
# bash render.sh # writes to <repo>/assets/
|
||||
# bash render.sh /some/other/dir # writes there instead
|
||||
#
|
||||
# Env overrides:
|
||||
# CHROME=/path/to/Chrome # non-standard Chrome location
|
||||
# PORT=<n> # override the local http.server port
|
||||
|
||||
set -e
|
||||
|
||||
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SKILL_DIR/../../.." && pwd)"
|
||||
OUT_DIR="${1:-$REPO_ROOT/assets}"
|
||||
BIND_PORT="${PORT:-0}" # 0 → kernel picks a free port
|
||||
SERVER_LOG="$(mktemp -t dsh-arch-diagram-server.XXXXXX.log)"
|
||||
|
||||
# Locate a Chrome / Chromium binary. Honour $CHROME if set; otherwise probe
|
||||
# common per-platform paths so `bash render.sh` works on macOS + Linux out
|
||||
# of the box.
|
||||
if [ -z "$CHROME" ]; then
|
||||
for candidate in \
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
|
||||
"/Applications/Chromium.app/Contents/MacOS/Chromium" \
|
||||
"$(command -v google-chrome 2>/dev/null || true)" \
|
||||
"$(command -v google-chrome-stable 2>/dev/null || true)" \
|
||||
"$(command -v chromium 2>/dev/null || true)" \
|
||||
"$(command -v chromium-browser 2>/dev/null || true)"; do
|
||||
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
|
||||
CHROME="$candidate"; break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "$CHROME" ] || [ ! -x "$CHROME" ]; then
|
||||
echo "No Chrome / Chromium binary found." >&2
|
||||
echo "Tried: macOS default, google-chrome, google-chrome-stable, chromium, chromium-browser." >&2
|
||||
echo "Set CHROME=/path/to/binary and rerun." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
python3 -u -m http.server "$BIND_PORT" --bind 127.0.0.1 --directory "$SKILL_DIR" \
|
||||
>"$SERVER_LOG" 2>&1 &
|
||||
SERVER_PID=$!
|
||||
cleanup() { kill "$SERVER_PID" 2>/dev/null; rm -f "$SERVER_LOG"; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# Discover the actual bound port from Python's own startup line
|
||||
# ("Serving HTTP on 127.0.0.1 port <N> ..."). Works whether the kernel
|
||||
# picked it or PORT was set explicitly; no external `lsof` needed.
|
||||
PORT=""
|
||||
for _ in $(seq 1 40); do
|
||||
PORT=$(awk '/Serving HTTP on/ {print $6; exit}' "$SERVER_LOG" 2>/dev/null || true)
|
||||
[ -n "$PORT" ] && break
|
||||
# Bail early if the server died (e.g. explicit PORT already in use).
|
||||
kill -0 "$SERVER_PID" 2>/dev/null || break
|
||||
sleep 0.05
|
||||
done
|
||||
if [ -z "$PORT" ]; then
|
||||
echo "Failed to detect local server port. Server log:" >&2
|
||||
cat "$SERVER_LOG" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Chrome/Chromium refuses to run as root without --no-sandbox. Rendering
|
||||
# a local static HTML doc under a headless browser doesn't need Chrome's
|
||||
# own sandbox, so passing the flag when EUID is 0 makes the script work
|
||||
# in Codex/CI container environments that default to root.
|
||||
CHROME_EXTRA_ARGS=()
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
CHROME_EXTRA_ARGS+=(--no-sandbox)
|
||||
fi
|
||||
|
||||
for lang in en zh; do
|
||||
out="$OUT_DIR/arch-$lang.png"
|
||||
"$CHROME" \
|
||||
"${CHROME_EXTRA_ARGS[@]}" \
|
||||
--headless=new \
|
||||
--disable-gpu \
|
||||
--hide-scrollbars \
|
||||
--force-device-scale-factor=2 \
|
||||
--window-size=1820,580 \
|
||||
--virtual-time-budget=8000 \
|
||||
--screenshot="$out" \
|
||||
"http://127.0.0.1:$PORT/harness-arch-$lang.html" \
|
||||
2>/dev/null
|
||||
size=$(du -h "$out" | awk '{print $1}')
|
||||
echo "[$lang] wrote $out ($size)"
|
||||
done
|
||||
Reference in New Issue
Block a user