@itookit/dsht 0.3.0 → 0.3.3
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.i18n.yaml +2 -2
- package/README.md +39 -23
- package/README.zh.md +40 -24
- package/dist/catalog/controller.d.ts +32 -0
- package/dist/catalog/controller.js +88 -0
- package/dist/catalog/index.d.ts +2 -0
- package/dist/catalog/index.js +2 -0
- package/dist/{cli.js → cli/index.js} +42 -29
- package/dist/controller/connection.d.ts +80 -0
- package/dist/controller/connection.js +190 -0
- package/dist/controller/controller.d.ts +269 -0
- package/dist/controller/controller.js +372 -0
- package/dist/controller/index.d.ts +5 -0
- package/dist/controller/index.js +3 -0
- package/dist/controller/memory-log.d.ts +35 -0
- package/dist/controller/memory-log.js +95 -0
- package/dist/cost/config.d.ts +17 -0
- package/dist/cost/config.js +68 -0
- package/dist/cost/controller.d.ts +41 -0
- package/dist/cost/controller.js +115 -0
- package/dist/cost/index.d.ts +10 -0
- package/dist/cost/index.js +8 -0
- package/dist/cost/ledger-files.d.ts +16 -0
- package/dist/cost/ledger-files.js +103 -0
- package/dist/cost/ledger.d.ts +78 -0
- package/dist/cost/ledger.js +146 -0
- package/dist/cost/pricing.d.ts +86 -0
- package/dist/cost/pricing.js +223 -0
- package/dist/cost/records.d.ts +17 -0
- package/dist/cost/records.js +79 -0
- package/dist/cost/scanner.d.ts +22 -0
- package/dist/cost/scanner.js +95 -0
- package/dist/cost/types.d.ts +85 -0
- package/dist/cost/types.js +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/session/connection-view.d.ts +18 -0
- package/dist/session/connection-view.js +1 -0
- package/dist/{controller.d.ts → session/controller.d.ts} +102 -136
- package/dist/session/controller.js +616 -0
- package/dist/session/export-html.d.ts +9 -0
- package/dist/session/export-html.js +39 -0
- package/dist/{export.d.ts → session/export.d.ts} +1 -1
- package/dist/{export.js → session/export.js} +6 -20
- package/dist/{history.d.ts → session/history.d.ts} +17 -0
- package/dist/{history.js → session/history.js} +163 -2
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +10 -0
- package/dist/session/markdown.d.ts +39 -0
- package/dist/session/markdown.js +255 -0
- package/dist/session/math.d.ts +11 -0
- package/dist/session/math.js +82 -0
- package/dist/session/navigation.d.ts +37 -0
- package/dist/session/navigation.js +84 -0
- package/dist/{references.js → session/references.js} +1 -1
- package/dist/{telemetry.d.ts → session/telemetry.d.ts} +1 -1
- package/dist/{telemetry.js → session/telemetry.js} +1 -1
- package/dist/{transcript.d.ts → session/transcript.d.ts} +65 -1
- package/dist/{transcript.js → session/transcript.js} +141 -20
- package/dist/session/types.d.ts +18 -0
- package/dist/session/types.js +2 -0
- package/dist/state.d.ts +41 -0
- package/dist/state.js +9 -0
- package/dist/storage/directories.d.ts +14 -0
- package/dist/storage/directories.js +24 -0
- package/dist/storage/files.d.ts +51 -0
- package/dist/storage/files.js +152 -0
- package/dist/storage/heap-snapshot.d.ts +19 -0
- package/dist/storage/heap-snapshot.js +29 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +4 -0
- package/dist/transport/auth.js +65 -0
- package/dist/transport/host.d.ts +13 -0
- package/dist/transport/host.js +1 -0
- package/dist/ui/app.d.ts +11 -0
- package/dist/ui/app.js +790 -0
- package/dist/ui/chat/header.d.ts +11 -0
- package/dist/ui/chat/header.js +14 -0
- package/dist/ui/chat/history-view.d.ts +12 -0
- package/dist/ui/chat/history-view.js +17 -0
- package/dist/ui/chat/status.d.ts +91 -0
- package/dist/ui/chat/status.js +386 -0
- package/dist/ui/chat/viewport.d.ts +17 -0
- package/dist/ui/chat/viewport.js +14 -0
- package/dist/ui/commands/parse.d.ts +99 -0
- package/dist/ui/commands/parse.js +126 -0
- package/dist/ui/commands/registry.d.ts +33 -0
- package/dist/ui/commands/registry.js +73 -0
- package/dist/ui/copy-mode.d.ts +4 -0
- package/dist/ui/copy-mode.js +6 -0
- package/dist/{cost-view.d.ts → ui/dialogs/cost.d.ts} +1 -1
- package/dist/{cost-view.js → ui/dialogs/cost.js} +6 -6
- package/dist/ui/dialogs/index.d.ts +120 -0
- package/dist/ui/dialogs/index.js +113 -0
- package/dist/ui/dialogs/picker.d.ts +18 -0
- package/dist/ui/dialogs/picker.js +38 -0
- package/dist/ui/frozen.d.ts +8 -0
- package/dist/ui/frozen.js +7 -0
- package/dist/ui/input/references.d.ts +12 -0
- package/dist/ui/input/references.js +15 -0
- package/dist/ui/mount.d.ts +6 -0
- package/dist/ui/mount.js +11 -0
- package/dist/{theme.d.ts → ui/theme/index.d.ts} +1 -1
- package/package.json +19 -13
- package/dist/app.d.ts +0 -21
- package/dist/app.js +0 -805
- package/dist/auth.js +0 -108
- package/dist/controller.js +0 -961
- package/dist/cost.d.ts +0 -119
- package/dist/cost.js +0 -313
- package/dist/history-view.d.ts +0 -8
- package/dist/history-view.js +0 -12
- package/dist/navigation.d.ts +0 -11
- package/dist/navigation.js +0 -36
- package/dist/status.d.ts +0 -28
- package/dist/status.js +0 -157
- /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
- /package/dist/{memory.d.ts → session/memory.d.ts} +0 -0
- /package/dist/{memory.js → session/memory.js} +0 -0
- /package/dist/{references.d.ts → session/references.d.ts} +0 -0
- /package/dist/{auth.d.ts → transport/auth.d.ts} +0 -0
- /package/dist/{client.d.ts → transport/client.d.ts} +0 -0
- /package/dist/{client.js → transport/client.js} +0 -0
- /package/dist/{endpoint.d.ts → transport/endpoint.d.ts} +0 -0
- /package/dist/{endpoint.js → transport/endpoint.js} +0 -0
- /package/dist/{wire.d.ts → transport/wire.d.ts} +0 -0
- /package/dist/{wire.js → transport/wire.js} +0 -0
- /package/dist/{input-history.d.ts → ui/input/history.d.ts} +0 -0
- /package/dist/{input-history.js → ui/input/history.js} +0 -0
- /package/dist/{input.d.ts → ui/input/input.d.ts} +0 -0
- /package/dist/{input.js → ui/input/input.js} +0 -0
- /package/dist/{mouse.d.ts → ui/input/mouse.d.ts} +0 -0
- /package/dist/{mouse.js → ui/input/mouse.js} +0 -0
- /package/dist/{theme.js → ui/theme/index.js} +0 -0
package/README.i18n.yaml
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# Git blob hashes of the reviewed bilingual pair.
|
|
2
|
-
README.md:
|
|
3
|
-
README.zh.md:
|
|
2
|
+
README.md: bf33ca79c59936198c6faabacc5da8475137257a
|
|
3
|
+
README.zh.md: befb6be4b60db5a19f0f98084f147de2536ed1c3
|
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Main features:
|
|
|
47
47
|
- Queued prompts, steering, turn cancellation, approvals, and free-text question answers.
|
|
48
48
|
- Cookie persistence per host, automatic reconnect, and snapshot replacement, so control survives a dropped connection.
|
|
49
49
|
- JSON or tab-separated workspace/session lists for scripts, plus a reusable HTTP client.
|
|
50
|
-
- **Cost-aware**: session
|
|
50
|
+
- **Cost-aware**: session and today CNY estimates with versioned peak/off-peak prices and `/cost` summaries.
|
|
51
51
|
|
|
52
52
|
## Why use dsht?
|
|
53
53
|
|
|
@@ -118,7 +118,7 @@ today
|
|
|
118
118
|
today + the previous two calendar days
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
The status bar also keeps showing session
|
|
121
|
+
The status bar also keeps showing this session's cost and today's total (`S¥1.23 · D¥5.00`), so cost changes surface while a task runs instead of only after the invoice arrives.
|
|
122
122
|
|
|
123
123
|
That gives `dsht` two roles at once:
|
|
124
124
|
|
|
@@ -166,7 +166,7 @@ npm start
|
|
|
166
166
|
|
|
167
167
|
Both paths read the same `DSH_URL` and `DSH_TOKEN` variables.
|
|
168
168
|
|
|
169
|
-
Select a workspace with ↑/↓ and Enter, then select a session or **New session**. **All sessions** also exposes sessions outside registered workspaces. **Add workspace**
|
|
169
|
+
Select a workspace with ↑/↓ and Enter, then select a session or **New session**. **All sessions** also exposes sessions outside registered workspaces. **Add workspace (this directory)** registers the directory `dsht` itself runs in, and appears only while the host does not already have it; **Add workspace (host directory)** takes an existing absolute directory on the host, which may differ from your local filesystem, and Esc leaves that prompt for the picker again. Creating a session requires a selected workspace.
|
|
170
170
|
|
|
171
171
|
On first login, authentication exchanges the token at `GET /` and saves the cookie per HTTP origin. Later starts, including list commands, reuse that cookie without a token. The store uses `$XDG_STATE_HOME/dsht/auth`, or `~/.local/state/dsht/auth` when unset; `--auth-dir` or `DSHT_AUTH_DIR` overrides it. POSIX directories use 0700 and cookie files use 0600; Windows uses the account directory's inherited access controls. Launch tokens are never saved.
|
|
172
172
|
|
|
@@ -279,8 +279,8 @@ From a source checkout, run the same commands through npm or through the source
|
|
|
279
279
|
```sh
|
|
280
280
|
npm start -- list workspaces --json
|
|
281
281
|
npm start -- list sessions --json
|
|
282
|
-
node --import tsx src/cli.tsx list workspaces --json
|
|
283
|
-
node --import tsx src/cli.tsx list sessions --json
|
|
282
|
+
node --import tsx src/cli/index.tsx list workspaces --json
|
|
283
|
+
node --import tsx src/cli/index.tsx list sessions --json
|
|
284
284
|
```
|
|
285
285
|
|
|
286
286
|
JSON output is `{ "items": [...] }`; omit `--json` for tab-separated output. Workspace filtering uses the host's `sessionIds` membership. The workspace list consumes and cancels the first `workspace/follow` baseline; it does not call a nonexistent `workspace/list` endpoint.
|
|
@@ -289,7 +289,7 @@ JSON output is `{ "items": [...] }`; omit `--json` for tab-separated output. Wor
|
|
|
289
289
|
|
|
290
290
|
Enter submits a prompt: while the agent is Working it becomes steering for the next step; while idle it starts a new turn. Steering waits for the current step, including its tools, to finish and does not interrupt a running tool. Ctrl+C clears a non-empty draft first; otherwise it requests cancellation while the selected session is running and exits only when it is idle; repeated keys share an in-flight cancellation. Cancellation waits for a pending prompt admission, and failures keep the client open. Esc sends an explicit cancellation from the conversation even when the cached running flag is idle; open menus also cancel a known running agent while closing. Active history/search/cost loads, host commands, and exports are cancelled first. Page Up/Down scroll the retained transcript; `/older` loads an earlier page. Every exit path, including `/quit` and SIGTERM, stops the selected turn before the connection closes, so quitting does not leave the agent running; an idle session is left untouched. Cancellation leaves pending queue items intact.
|
|
291
291
|
|
|
292
|
-
`/copy`, Ctrl+S, or an unmodified left click in the normal chat view freezes the display and disables mouse reporting for native terminal selection. Esc, Ctrl+S, or Ctrl+C leaves copy mode and catches up with the latest output; leaving copy mode does not cancel the agent. Dialogs and pickers pause automatic background title
|
|
292
|
+
`/copy`, Ctrl+S, or an unmodified left click in the normal chat view freezes the display and disables mouse reporting for native terminal selection. Esc, Ctrl+S, or Ctrl+C leaves copy mode and catches up with the latest output; leaving copy mode does not cancel the agent. Dialogs and pickers pause automatic background title and conversation updates; chat dialogs also pause status updates. Workspace selection, session selection, and host-path entry keep connection notices and the status bar live unless copy mode is active. The conversation remains above the composer; mouse wheel and PgUp/PgDn scroll its history without moving the selected option. In the help panel PgUp/PgDn changes help pages. Dialog clicks do not enter copy mode; Ctrl+S freezes the whole display and releases mouse capture for native selection. The Working clock also pauses while reading older history. Help/status/cost panels no longer expire on a timer. Background reception and memory reclamation continue; window resizing can redraw the screen.
|
|
293
293
|
|
|
294
294
|
The mouse wheel and Page Up/Down scroll conversation history; scrolling to the top automatically requests an older page. New output preserves a scrolled reading position; scrolling back down resumes following the newest output. Mouse reporting is enabled while the TUI is mounted and disabled on exit; the terminal must support SGR mouse reports. Esc or Ctrl+C cancels a history load or search before interrupting the remote agent.
|
|
295
295
|
|
|
@@ -297,7 +297,7 @@ In `/ws` and `/resume` pickers, select a workspace or session and press `d` or D
|
|
|
297
297
|
|
|
298
298
|
`/search` matches literal text case-insensitively in conversation messages, including older pages; tool-only rows are excluded. Search scans up to 80 messages per request, discards each temporary page, and retains at most 200 short matches, including folded reasoning. A truncated result asks you to refine the query. Opening a match loads a separate page around its sequence; `/latest` releases that window. Esc or Ctrl+C cancels the search. A rare or absent term still requires scanning the full history over HTTP; there is no server-side full-text index for this command. `/history` lists your own prompts from the loaded pages. Record sequences are the numbers shown by these pickers. `/ssearch` and `/wsearch` call `session/search`, which searches current user/assistant message content and returns at most 20 sessions, snippets, and a truncation flag; it exposes neither a result cursor nor matching record sequences. Workspace filtering happens after that global limit, so a truncated workspace result can omit matches. The UI warns when results are incomplete; refine the query. Selecting a session loads its history and offers matching messages for the jump. These operations use HTTP and never scan the host configuration directory.
|
|
299
299
|
|
|
300
|
-
↑/↓ or Ctrl+P/N recalls previously submitted prompts and slash commands without sending them; Enter submits the recalled text. Moving past the newest entry restores the unsent draft. Editing recalled text starts a new draft. Recall keeps up to 200 entries and approximately 256 KiB of text for the selected session. Opening or restoring a session seeds recall from its already-loaded User messages; switching sessions releases the previous recall buffer. It does not fetch older pages or write a separate history file. Consecutive duplicates are merged, oversized entries are skipped, and question or approval answers are excluded. Question options and completion menus keep arrow navigation; workspace/session lists use arrows when the composer is empty, with Ctrl+P/N available for recall.
|
|
300
|
+
↑/↓ or Ctrl+P/N recalls previously submitted prompts and slash commands without sending them; Enter submits the recalled text. A reading panel that fits the screen leaves the arrows with this history, a panel that has to scroll takes them for itself, and Ctrl+P/N reach the history from any panel. Moving past the newest entry restores the unsent draft. Editing recalled text starts a new draft. Recall keeps up to 200 entries and approximately 256 KiB of text for the selected session. Opening or restoring a session seeds recall from its already-loaded User messages; switching sessions releases the previous recall buffer. It does not fetch older pages or write a separate history file. Consecutive duplicates are merged, oversized entries are skipped, and question or approval answers are excluded. Question options and completion menus keep arrow navigation; workspace/session lists use arrows when the composer is empty, with Ctrl+P/N available for recall.
|
|
301
301
|
|
|
302
302
|
Within each User group, only the first assistant prose or reasoning message shows an Assistant heading. Later messages and live output reuse that heading across tool results and Context messages. A newly loaded history window starts its own visible group; message sequences, tool status, search, and reasoning expansion remain independent.
|
|
303
303
|
|
|
@@ -319,7 +319,7 @@ Tab completes the leading slash command, extending an ambiguous draft to the sha
|
|
|
319
319
|
|
|
320
320
|
| Command | Action |
|
|
321
321
|
| --- | --- |
|
|
322
|
-
| `/ws` | Show all workspaces; choosing one opens its session list |
|
|
322
|
+
| `/ws` | Show all workspaces and both add-workspace entries; choosing one opens its session list |
|
|
323
323
|
| `/ws TARGET` | Select a workspace by ID, exact name/path, or unique ID prefix |
|
|
324
324
|
| `/resume` | Show sessions in the current workspace; choose a workspace first if none is selected |
|
|
325
325
|
| `/resume TARGET` | Open a session by ID, exact title, or unique ID prefix across workspaces |
|
|
@@ -334,6 +334,8 @@ Tab completes the leading slash command, extending an ambiguous draft to the sha
|
|
|
334
334
|
| `/permission [preset]` | View or switch the host sandbox/approval preset |
|
|
335
335
|
| `/feedback TEXT` | Record feedback about the current session |
|
|
336
336
|
| `/export [local.zip]` | Download the session log ZIP to a new local file |
|
|
337
|
+
| `/export-html [local.html]` | Save the loaded conversation as offline HTML with diagrams and math |
|
|
338
|
+
| `/coredump [tag]` | Write a V8 heap snapshot to the working directory for memory diagnosis |
|
|
337
339
|
| `/older` | Load older history |
|
|
338
340
|
| `/history [text]` | List your own prompts, optionally filtered; Enter jumps to the selected record |
|
|
339
341
|
| `/search <text>` | Search history page by page; choose a match to open its location |
|
|
@@ -342,13 +344,13 @@ Tab completes the leading slash command, extending an ambiguous draft to the sha
|
|
|
342
344
|
| `/ssearch <text>` | Search host results within the selected workspace |
|
|
343
345
|
| `/wsearch <text>` | Search sessions across all workspaces visible to the host |
|
|
344
346
|
| `/allow`, `/deny` | Answer the displayed approval; allow applies once |
|
|
345
|
-
| `/status` | Expand or collapse full footer details |
|
|
346
|
-
| `/cost` | Toggle session
|
|
347
|
+
| `/status` | Expand or collapse full footer details; ↑/↓ scroll it, PgUp/PgDn page |
|
|
348
|
+
| `/cost` | Toggle session and today estimates and refresh usage |
|
|
347
349
|
| `/think` | List reasoning with user prompt summaries; ↑/↓ and Enter jump to and expand a thought |
|
|
348
350
|
| `/think SEQ` | Toggle one loaded thought; `live` toggles the active attempt |
|
|
349
351
|
| `/help`, `/quit` | List every command with its description, or exit |
|
|
350
352
|
|
|
351
|
-
Slash commands work in both pickers and the conversation composer. Typing `/` displays matching commands, and `/help` lists commands with their one-line descriptions; PgUp/PgDn changes pages. The `/help`, `/cost`, and `/status` panels remain open until the next command or Esc; Esc leaves the draft in place. `/workspace` and `/workspaces` alias `/ws`; `/session` and `/sessions` alias `/resume`. Names may contain spaces; quotes around the complete target are optional. The unquoted target `all` is reserved for `/resume all`; use `/resume "all"` or an ID to open a session titled `all`. Ambiguous targets require a full ID. Switching a workspace opens its sessions and detaches the old transcript; switching sessions updates the workspace label. Neither operation cancels a remote agent.
|
|
353
|
+
Slash commands work in both pickers and the conversation composer. Typing `/` displays matching commands, and `/help` lists commands with their one-line descriptions; PgUp/PgDn changes pages. The `/help`, `/cost`, and `/status` panels remain open until the next command or Esc; `/history` also expires after ten seconds, so a forgotten lookup releases the composer. Esc leaves the draft in place. `/workspace` and `/workspaces` alias `/ws`; `/session` and `/sessions` alias `/resume`. Names may contain spaces; quotes around the complete target are optional. The unquoted target `all` is reserved for `/resume all`; use `/resume "all"` or an ID to open a session titled `all`. Ambiguous targets require a full ID. Switching a workspace opens its sessions and detaches the old transcript; switching sessions updates the workspace label. Neither operation cancels a remote agent.
|
|
352
354
|
|
|
353
355
|
Type `@` at the end of the draft to search files and directories in the selected session's working directory **on the host**. Use ↑/↓ to select and Tab or Enter to insert; selecting a directory continues completion inside it. Paths with spaces use `@"path with spaces"`. Escape closes the menu and requests cancellation when the agent is running; after closing it, Enter sends the literal draft, including an unmatched path. Lookup failures remain visible and do not submit the draft. Completion operates on the trailing reference, not the cursor position inside existing text.
|
|
354
356
|
|
|
@@ -362,25 +364,37 @@ Pending ordinary messages appear inside the composer, with up to two previews. `
|
|
|
362
364
|
|
|
363
365
|
Below 62 terminal columns, active streaming reasoning defaults to one folded row. Wider terminals expand active reasoning and fold it on completion. `/think live` toggles the current reasoning; a new response restores the default.
|
|
364
366
|
|
|
367
|
+
Approvals offer 1. Allow once, 2. Deny, and 3. Stop turn. With an empty composer, use 1–3 or ↑/↓ to select, then Enter to confirm; no action is selected initially, Escape clears the highlight, and a replayed request starts unselected again. Existing drafts keep normal typing, and `/allow`, `/deny`, and `/cancel` remain available.
|
|
368
|
+
|
|
365
369
|
User questions show progress, numbered options, and descriptions. Selection lists, pending questions, approvals, and file completions share the composer border with the text input. Pending questions and approvals retain recent conversation history above the composer. The history viewport fits the remaining height, removes extra vertical margins while a dialog is open, and refreshes on explicit scrolling or viewport resizing. The visible option window adapts to terminal height and follows the highlighted choice. With an empty composer, ↑/↓ or 1–9 selects an option and Enter confirms; numbers only select and do not submit. For multi-select questions, Space or 1–9 toggles checkboxes, and Enter confirms the selection. Options beyond nine remain reachable with arrows. Choose Other answer to type numeric free text; ordinary text answers remain supported. Existing drafts keep normal typing, and Escape returns from Other to the options without cancelling the question. All questions are submitted together as structured selected labels and optional custom text; a failed submission preserves the answers for retry. Recognized question and approval events are retained by event ID for the connection, including replay before session selection; only the selected session displays them. Switching pickers does not decline those requests. Unrecognized waterfalls still delegate with `next`. The live host replays pending events after client reconnection; client restart does not preserve unsubmitted answer drafts. Normal TUI shutdown cancels a running turn. A cancelled/failed tool call or a host restart cannot restore the original wait from local UI state; send a new prompt requesting the questions again. Failed submissions retain their input; an interrupted HTTP response can leave delivery uncertain, so check the transcript before manually resending. The client never retries a mutation automatically.
|
|
366
370
|
|
|
367
371
|
The header stays above the scrollable conversation while the composer and status stay below it. The always-visible keyboard legend is removed; `/help` contains the full shortcuts and pickers retain their local navigation hints. The single-line header prioritizes the latest session title (falling back to the ID), with the workspace name after it on wider terminals; host and connection labels are omitted. Without a selected session, it shows the workspace name or All workspaces. A divider sits below the title; `/status` retains the complete session ID. The cancellation acknowledgement stays visible through incoming history until the host reports idle; acceptance does not mean a tool process has already exited.
|
|
368
372
|
|
|
373
|
+
## Markdown, diagrams and math
|
|
374
|
+
|
|
375
|
+
Message text renders GitHub-flavored Markdown: headings, emphasis, strikethrough, links, lists, tasks, quotes, code and tables. Tables align by terminal display width, wrap cell contents, and stack records vertically when columns would be too narrow. Code preserves indentation; links retain their destinations. Reasoning and tool summaries keep their existing plain-text presentation. Search retains the original Markdown source.
|
|
376
|
+
|
|
377
|
+
Closed `mermaid` fences render as Unicode diagrams for supported flowcharts, state, sequence, class and ER diagrams. Diagrams that exceed the available width, unsupported syntax and unfinished fences show source code. `$...$`, `$$...$$`, `\(...\)`, `\[...\]` and `math` / `latex` / `tex` / `mathjax` fences use MathJax's base and AMS TeX packages. The terminal shows Unicode symbols, grouped fractions, scripts and matrices; unsupported notation or invalid TeX retains its source. Terminal formulas approximate typeset mathematics.
|
|
378
|
+
|
|
379
|
+
`/export-html [local.html]` saves the currently loaded conversation and live tail with Mermaid SVG images and MathJax-generated MathML. Open the file in a browser for full mathematical layout; no network or scripts are required. Older or evicted messages are excluded, and tool rows remain summaries. The default filename is timestamped in the working directory; quoted paths are accepted, existing files are never overwritten, and cancellation removes incomplete output. `/export` remains the complete host-log ZIP download.
|
|
380
|
+
|
|
369
381
|
## Live status
|
|
370
382
|
|
|
371
|
-
The footer groups `◐ Working · 8s · Ctrl+C Stop` or `● Ready`, model and reasoning effort, session
|
|
383
|
+
The footer groups `◐ Working · 8s · Ctrl+C Stop` or `● Ready`, model and reasoning effort, the session cost beside today's spend with the all-time total, a ten-cell context bar and percentage, and session turns / total tokens / cache-hit share. Wide terminals reserve the activity column so model and metrics stay aligned when a run completes. Narrow terminals reclaim padding, remove the bar, shorten the model, and then omit lower-priority metrics while retaining the stop hint. `/status` shows the host URL, operation status, workspace path, full provider/model, pending model, usage buckets, turns, queues, jobs, and four-decimal costs. `!` flags a metrics or model catalog error, or incomplete cost coverage; details explain the cause. Running sessions use the last-used model; ready sessions use the next selection, with the host catalog default for new sessions. Model catalog changes refresh on host settings, credential, and adapter notifications.
|
|
384
|
+
|
|
385
|
+
Working time uses the retained `turn/start` timestamp. If that timestamp is unavailable, `(observed)` in the details panel means time since this client observed the run; reconnecting can reset this fallback. The clock stops when the host reports idle. The status includes model generation, tool execution, and approval waits, not just streamed text. Offline status is explicitly marked as last known. The expanded panel merges related values onto one row and prints counts in the compact form the single-row bar already uses (`Context ~40% (400.6K/1M) · 229.7M tok`), so a 24-row terminal shows every detail on one screen; ↑/↓ scroll it one line at a time, PgUp/PgDn one screen, and the footer names the visible range and the total.
|
|
372
386
|
|
|
373
|
-
|
|
387
|
+
The single-row bar keeps its groups by value rather than by column: `◐ 6:18 · bash 1:08 · ^C │ v4-flash · high · ctx 30% · S¥2.49* · ¥: 5.00 (12.34) · 2 turns · 34.5M tok · hit 92%`. The live phase is reported as a fact — `think 28s` while reasoning, the tool name with its age while a tool runs, and `write 12s` while the answer streams — and never inferred from silence, because a long reasoning step and a quiet tool are not stalls. It names the newest event and keeps its age until another stripe of work starts or the turn closes, so the quiet stretch after a command is still time the turn spent working, and `● Ready` drops the phase because only the host knows the turn ended. The two cost groups never substitute for each other: `S¥2.49*` is this session, and reports `S?` while the ledger has not priced it, while `¥: 5.00 (12.34)` is today's spend with the all-time total. When the width runs out the least valuable group is dropped first (the cache-hit share, tokens, turns, effort, model, today's spend, then context); the session cost is never dropped but moves to a second row, and the state cluster gives up the phase and the stop hint only below about twenty columns. A paused clock names its reason (`⏸ copy`, `⏸ dialog`, `⏸ history`) instead of freezing silently, and `! Offline` or `⚠ Error` replaces the state token entirely.
|
|
374
388
|
|
|
375
|
-
Turns come from the complete session's `sessionStats.turns` projection. Context occupancy is marked `~`: Harness combines provider usage with estimated surface changes and the latest route capacity. Token totals come from the complete session's `tokenUsage` projection, with separate uncached input, output, cache-read, and cache-write buckets; reasoning is already included in output. Totals update when the host publishes usage, not on every streamed character. Missing measurements display `unknown` or `?`. Control-stream baselines replace state on reconnect, and per-key watermarks prevent an older follow snapshot from overwriting newer metrics.
|
|
389
|
+
Turns come from the complete session's `sessionStats.turns` projection. Context occupancy is marked `~`: Harness combines provider usage with estimated surface changes and the latest route capacity. Token totals come from the complete session's `tokenUsage` projection, with separate uncached input, output, cache-read, and cache-write buckets; reasoning is already included in output. The cache-hit share is the cache-read bucket over all three billed prompt buckets, and it gains decimal places rather than reporting a partial hit as `100%`. Session and day costs come from the ledger's per-session slices, so a session with no slice yet is unknown, not the day's spend. Totals update when the host publishes usage, not on every streamed character. Missing measurements display `unknown` or `?`. Control-stream baselines replace state on reconnect, and per-key watermarks prevent an older follow snapshot from overwriting newer metrics.
|
|
376
390
|
|
|
377
|
-
The default [Catppuccin Mocha](https://catppuccin.com/palette/) theme distinguishes `❯ User` (blue), `✦ Assistant` (green), reasoning (mauve), tools (sky), success (green), and errors (red). The compact status bar uses green for Ready, yellow for Working, red for offline, mauve for model/effort, sky for costs, and subdued gray for usage. Context occupancy changes from green to yellow at 80% and red at 95%; these are visual thresholds, not host compaction triggers. Groups are fitted before ANSI styling,
|
|
391
|
+
The default [Catppuccin Mocha](https://catppuccin.com/palette/) theme distinguishes `❯ User` (blue), `✦ Assistant` (green), reasoning (mauve), tools (sky), success (green), and errors (red). The compact status bar uses green for Ready, yellow for Working, red for offline, subdued gray for a paused reason and the separators, mauve for model/effort, sky for costs, and subdued gray for usage. Context occupancy changes from green to yellow at 80% and red at 95%; these are visual thresholds, not host compaction triggers. Groups are fitted by priority before ANSI styling, so a plain terminal shows the same text. Semantic colors live in `src/ui/theme/index.ts`; the application accepts a theme independently of stored messages. Ink adapts ANSI output to terminal capabilities, and plain terminals retain the role markers. Tool calls show their name and description plus a `$` preview of the command's first line when it differs from the description. Without a description, the first command line, path, or query supplies the summary. Each line truncates by terminal display width; completion updates the original call from ⚙ to ✓ or ✗ by call ID, without a second result entry. Command previews retain two-space indentation. Results whose calls are outside the loaded window remain visible until their call page is loaded; nested result bodies stay hidden.
|
|
378
392
|
|
|
379
393
|
Reasoning streams in full while being generated, then folds when its block closes or answer/tool output begins. `/think` opens a newest-first list of reasoning summaries with the preceding loaded user prompt and an entry for the active attempt. Select with ↑/↓ and Enter to jump to the original message and expand it; `/think SEQ` folds or expands that message, and `/think live` controls the active attempt. The list stays open until selection, Esc, or another command. It initially uses loaded history; `Load older reasoning` fetches one earlier page on demand. If a prompt precedes the loaded window, the list says so until that page is loaded. Full reasoning remains available for search.
|
|
380
394
|
|
|
381
395
|
`/model` reads `session/modelCatalog` and offers the provider/model routes and reasoning efforts advertised by the host. Selection calls `session/selectModel` with `{ request: { sessionId, provider, model, reasoningEffort? } }`; omitting effort uses the adapter default. The host applies it to subsequent requests, logs the selection, and also attempts to save it as the deployment default. It does not replace an in-flight request. `modelSelection.next` and `lastUsed` remain the authority for the displayed model; failures retain the previous selection. Provider catalog failures are shown without hiding healthy providers. The header follows the web agent-preset label: `agentPreset` supplies the current ID and `agentPresets/list` supplies names and trust metadata. Built-in system presets display Standard mode, PTC mode, Minimal mode, or Creator mode. Custom presets retain their names; missing roster entries fall back to the ID. The optional roster loads only when needed and is reused for the connection. Plan is a separate feature and does not determine this mode label. Below 62 terminal columns, mode remains available in `/status` to leave room for the session title.
|
|
382
396
|
|
|
383
|
-
History separates semantic message blocks, prompt/reasoning summaries, view-only fold state, and a row index. Stream frames reuse committed offsets and materialize only the viewport. A per-session LRU holds at most 2,048 committed terminal rows; evicted rows are recreated when revisited. Finished legacy chunks and unused tool-result bodies are released, while the host retains the original log. The host log is the durable tier; the client is a reloadable memory tier. The live tail defaults to soft budgets of 2,000 records or 16 MiB of estimated semantic payload (`--history-records`, `--history-mb`). Eviction targets 75% of the budgets and releases old text, summaries, and layout caches. Switching sessions disposes the previous transcript. Scrolled reading and reasoning navigation protect the loaded window; `/latest` returns to live output and resumes reclamation. Offline history, unfinished streams, and a minimum recent tail are protected, so these limits are not a process RSS cap. First layout, width changes, and an expanded very large block still require wrapping that content. `npm run bench:history` measures local stream/layout cost at 500, 2,000, and 10,000 messages without model or network time.
|
|
397
|
+
History separates semantic message blocks, prompt/reasoning summaries, view-only fold state, and a row index. Stream frames reuse committed offsets and materialize only the viewport. A per-session LRU holds at most 2,048 committed terminal rows; evicted rows are recreated when revisited. Finished legacy chunks and unused tool-result bodies are released, while the host retains the original log. The host log is the durable tier; the client is a reloadable memory tier. The live tail defaults to soft budgets of 2,000 records or 16 MiB of estimated semantic payload (`--history-records`, `--history-mb`). Eviction targets 75% of the budgets and releases old text, summaries, and layout caches. Switching sessions disposes the previous transcript. Scrolled reading and reasoning navigation protect the loaded window; `/latest` returns to live output and resumes reclamation. Offline history, unfinished streams, and a minimum recent tail are protected, so these limits are not a process RSS cap. A bounded runtime memory log is enabled by default at `<state>/memory.log`: one JSON line every 30 seconds with the process counters, the retained record and byte counts, the pin state, the ledger size, the layout row cache, the bounded math and diagram cache, and the sessions, pages and events the last cost scan re-read, so growth can be told apart from V8's high-water mark. On a runtime that exposes a collection the sample also records the heap after a forced one; `npm run start:profile` supplies that runtime together with a heap-snapshot signal, where `kill -USR2 <pid>` writes a snapshot. `/coredump [tag]` writes the same `.heapsnapshot` artifact into the client's working directory without a signal or a profiling runtime; the tag labels the file (default `snapshot`) and V8 serializes the heap synchronously, so the client pauses until the file exists. Open the snapshot in Chrome DevTools rather than the terminal. `--memory-log <path>` or `DSHT_MEMORY_LOG` changes the path, and `--no-memory-log` or `DSHT_MEMORY_LOG=off` disables it. First layout, width changes, and an expanded very large block still require wrapping that content. `npm run bench:history` measures local stream/layout cost at 500, 2,000, and 10,000 messages without model or network time.
|
|
384
398
|
|
|
385
399
|
## Cost estimates
|
|
386
400
|
|
|
@@ -388,21 +402,21 @@ History separates semantic message blocks, prompt/reasoning summaries, view-only
|
|
|
388
402
|
|
|
389
403
|
> These figures are a high-precision estimate from Harness-visible usage and local price configuration, for cost monitoring and control. They are not a provider account bill, and the provider invoice remains authoritative.
|
|
390
404
|
|
|
391
|
-
`/cost` shows the selected session
|
|
405
|
+
`/cost` shows the selected session and today. Dates use Asia/Shanghai. The status bar shows this session's cost with today's total in parentheses (`¥: 1.23(113.00)`); the pair is not a budget. `*` marks a subtotal that is not exact, because a request carries no timestamp, no price covers it, or the scan has not covered every session yet; `/cost` names the reason. Each host origin has a separate ledger. Totals cover HTTP-visible sessions and previously cached sessions; they are not account-wide provider bills.
|
|
392
406
|
|
|
393
|
-
The client reads complete histories in the background on connection, every 60 seconds, at turn completion, and when opening `/cost`.
|
|
407
|
+
The client reads complete histories in the background on connection, every 60 seconds, at turn completion, and when opening `/cost`. Every new connection re-reads every session once, because the host keeps running while this client is disconnected and a recorded update time cannot show what happened during the gap; within one connection, idle sessions whose host update timestamp has not moved are skipped. No model requests are made by billing. Esc or Ctrl+C cancels an explicit refresh. The ledger counts disjoint uncached input, cache read/write and output buckets; reasoning is already part of output. Retries count separately, replacement samples update their attempt, and fork-inherited history is excluded. A request that reports no usable usage, no settlement timestamp, or a cache-write bucket the official table does not price is counted as unpriced instead of being guessed at, and a model no entry covers stays unpriced; an unlisted provider is never billed from the official table. Every listed session is read independently, so one unreachable or rejected session is reported as a failure count instead of stopping the scan, and a subagent child is read under its durable parent address. Failed scans retain labelled partial cached totals.
|
|
394
408
|
|
|
395
409
|
The bundled CNY rates were checked against the [official pricing page](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) on 2026-09-10. Beijing weekday peak windows are 09:00–12:00 and 14:00–18:00; other times use half-price rates. Flash peak cache-miss-input/cache-hit-input/output rates are ¥2/¥0.04/¥8 per million tokens and Pro rates are ¥9/¥0.30/¥27; the current model name is `deepseek-flash`, and older Flash names keep those same rates. The provider has announced that from 2026-09-14T12:00+08:00 it serves `deepseek-v4-pro` from Flash and bills it at Flash rates, which the bundled entry records so that date does not overstate Pro usage. Separate cache writes use the uncached-input rate. An exact configured model price takes priority; otherwise `deepseek-official` names containing `pro` (case-insensitive) use Pro and all other names use Flash, including temporary model aliases. Other providers require explicit entries.
|
|
396
410
|
|
|
397
|
-
The default price validity starts at Beijing midnight on the verification date; this is a local estimate policy, not a claim about the official effective date. Earlier usage needs historical price entries. The recorded assistant settlement timestamp selects the rate; requests spanning a tariff boundary may differ from the invoice because the official page does not specify their attribution. Images use provider-reported tokens.
|
|
411
|
+
The default price validity starts at Beijing midnight on the verification date; this is a local estimate policy, not a claim about the official effective date. Earlier usage needs historical price entries. The recorded assistant settlement timestamp selects the rate; requests spanning a tariff boundary may differ from the invoice because the official page does not specify their attribution. Images use provider-reported tokens. A scan folds the history again with the table it holds, so a stored total is a projection of the log and the table: editing `prices.json` re-prices the requests it covers on the next scan, and a request no entry covered is priced as soon as one does.
|
|
398
412
|
|
|
399
413
|
On first interactive launch, the client creates `~/.config/dsht/prices.json` (or `$XDG_CONFIG_HOME/dsht/prices.json`). `DSHT_CONFIG_DIR` overrides that directory. The JSON array contains price versions with `id`, `provider`, `model`, `currency: "CNY"`, `source`, inclusive `from`, optional exclusive `until`, `timezone`, weekday numbers (`0` Sunday), minute-of-day `windows`, and `peak`/`offPeak` rates named `input`, `cacheRead`, `cacheWrite`, `output`, per million tokens. To update prices, close the old interval with `until` and append a new version with a unique ID and matching `from`; overlapping intervals are rejected. Restart to load configuration changes. Price discovery is manual; the TUI does not scrape prices during startup.
|
|
400
414
|
|
|
401
|
-
Usage files live under `~/.local/state/dsht/cost/<origin-hash>/` (respecting `XDG_STATE_HOME`, or `DSHT_STATE_DIR` for the application state root).
|
|
415
|
+
Usage files live under `~/.local/state/dsht/cost/<origin-hash>/` (respecting `XDG_STATE_HOME`, or `DSHT_STATE_DIR` for the application state root). Each holds one session's folded totals: the session amount with its request and unpriced counts, the day bucket its last scan ran on, the decision-rules revision and a digest of the price table that produced them, and the reasons a total is inexact. They exclude prompts, tool bodies, credentials, cookies, and every per-request fact. The price file is configuration and these usage files are state, so only the former belongs in a settings backup. Writes use private temporary files and atomic replacement; each session keeps one fixed file whose recorded cut and rules revision are compared before writing, so an older scan cannot displace a newer one. The cache survives restart and does not need access to the host configuration directory. A file of another generation is ignored and rebuilt by the next scan.
|
|
402
416
|
|
|
403
417
|
## Client API
|
|
404
418
|
|
|
405
|
-
Installed packages export `Client` from `@itookit/dsht` and `login`/`CookieStore` from `@itookit/dsht/auth`, with TypeScript declarations. Source consumers can import from `src/client.ts` with a TypeScript loader, or from `dist/
|
|
419
|
+
Installed packages export `Client` from `@itookit/dsht` and `login`/`CookieStore` from `@itookit/dsht/auth`, with TypeScript declarations. Source consumers can import from `src/transport/client.ts` with a TypeScript loader, or from `dist/index.js` after building. `authenticate(token)` exchanges credentials; `connect()` opens one multiplexed socket; `listWorkspaces()` and `listSessions(workspaceId?)` return promises of server rows. `call(endpoint, args, signal?)` preserves host errors as `RemoteError` with `code` and `details`. Always await `close()` in `finally`. Library consumers opt into persistence with `login(client, token, new CookieStore())` from `src/transport/auth.ts`; `Client.authenticate()` itself only retains credentials in memory.
|
|
406
420
|
|
|
407
421
|
Session and workspace command methods use `{ request: { ... } }` inside `args`; session listing uses `{ _request: {} }`. `$events/result` uses its named arguments directly. Follow snapshots replace retained state after reconnect; durable messages and transient assistant text remain separate. The reader accepts both `event` records and older `chunks` wrappers containing `chunkrow/text-chunks`, `chunkrow/reasoning-chunks`, or `chunkrow/tool-call-chunks`. Hosts without `assistantStream` expose live text through logged chunks; the TUI reconstructs only the unfinished attempt and preserves each packed record's starting sequence for pagination.
|
|
408
422
|
|
|
@@ -412,7 +426,7 @@ This repository publishes one public package, `@itookit/dsht`, from the `mushuan
|
|
|
412
426
|
|
|
413
427
|
| Field | Value |
|
|
414
428
|
| --- | --- |
|
|
415
|
-
| Name and version | `@itookit/dsht` `0.3.
|
|
429
|
+
| Name and version | `@itookit/dsht` `0.3.3` |
|
|
416
430
|
| Executable | `dsht`, or `npx @itookit/dsht` without installing |
|
|
417
431
|
| Library entries | `@itookit/dsht` and `@itookit/dsht/auth` |
|
|
418
432
|
| Author | lizlok@gmail.com |
|
|
@@ -445,11 +459,13 @@ npm test
|
|
|
445
459
|
npm run test:terminal
|
|
446
460
|
npm run build
|
|
447
461
|
npm run bench:input
|
|
448
|
-
node dist/cli.js --help
|
|
462
|
+
node dist/cli/index.js --help
|
|
449
463
|
```
|
|
450
464
|
|
|
451
465
|
Tests use isolated HTTP/WebSocket hosts, drive the real Ink picker and composer, run the CLI in subprocesses, and project copied Harness v2 workspace-edit and v0 packed-chunk recordings. The repository needs no model credentials for these checks. The recording and expected transcript live under `tests/`; they do not depend on a parent checkout. Live model-provider behavior is not covered by these tests.
|
|
452
466
|
|
|
467
|
+
Source is organised by business domain under `src/`: `transport/` owns the host wire protocol and authentication, `session/` the transcript, history and interactions, `cost/` the folded billing ledger, `catalog/` models and presets, `controller/` the application facade, `ui/` everything React and Ink, `storage/` every filesystem operation, and `cli/` the composition root. Cross-domain imports go through each module's `index.ts`; `tests/architecture/dependencies.test.ts` rejects a forbidden direction.
|
|
468
|
+
|
|
453
469
|
`npm test` renders frames without styling, because the assertions and the recorded expectations in `tests/expected/` describe text. A test runner started from a terminal exports `FORCE_COLOR=1` to each test file, which makes Ink interleave SGR escapes between a prompt and its text; `npm run test:terminal` reproduces that environment on any host, and `prepublishOnly` runs it so a publish from a terminal validates what a terminal actually renders. Theme tests render separate truecolor and plain subprocesses with terminal and CI color detection isolated from the parent environment.
|
|
454
470
|
|
|
455
471
|
Typing reuses history projection and wrapping until the transcript revision or terminal width changes; host updates and older pages invalidate that reuse. `bench:input` measures local input-to-render work with 20 and 500 synthetic messages, 30 measured keystrokes after warmup, and history projection read counts. It excludes network/model time and is a diagnostic, not a machine-independent latency threshold.
|
package/README.zh.md
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
`dsht` **本身不是 SSH 客户端**。它运行在普通终端中,因此可以直接工作在 SSH、嵌套 SSH、ProxyJump/跳板机、tmux 等远程终端环境里。只要你的终端能够到达运行 `dsht` 的主机,就可以继续控制同一套 DeepSeek Harness 会话。
|
|
38
38
|
|
|
39
|
-
除了远程控制,`dsht` 还内置了面向成本控制的用量统计:它按请求记录 token
|
|
39
|
+
除了远程控制,`dsht` 还内置了面向成本控制的用量统计:它按请求记录 token 用量,区分未缓存输入、缓存读取、缓存写入和输出,并结合模型、时间、高峰/空闲价格及版本化价格表,计算当前会话与今日的人民币费用估算。
|
|
40
40
|
|
|
41
41
|
主要特点:
|
|
42
42
|
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
- 排队消息、转向输入、轮次取消、审批和自由文本问题回答。
|
|
48
48
|
- 按服务端保存 cookie、自动重连和快照替换,方便断线后恢复控制。
|
|
49
49
|
- 面向脚本的 JSON/制表符工作区与会话列表,以及可复用的 HTTP 客户端。
|
|
50
|
-
-
|
|
50
|
+
- **成本感知**:会话与今日人民币费用估算,支持版本化高峰/空闲价格和 `/cost` 汇总。
|
|
51
51
|
|
|
52
52
|
## 为什么使用 dsht?
|
|
53
53
|
|
|
@@ -118,7 +118,7 @@ DeepSeek Harness 往往运行在性能更强、环境更完整的开发工作站
|
|
|
118
118
|
今日 + 前两个自然日
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
状态栏还可以持续显示本会话费用与括号内的今日合计(`¥: 1.23(5.00)`),便于在任务执行过程中及时发现成本变化,而不是等到账单出现后才知道消耗了多少。
|
|
122
122
|
|
|
123
123
|
这让 `dsht` 同时承担两个角色:
|
|
124
124
|
|
|
@@ -166,7 +166,7 @@ npm start
|
|
|
166
166
|
|
|
167
167
|
两种方式读取相同的 `DSH_URL` 和 `DSH_TOKEN` 变量。
|
|
168
168
|
|
|
169
|
-
使用 ↑/↓ 和 Enter 选择工作区,然后选择已有会话或 **New session**。**All sessions** 同时显示未归属注册工作区的会话。**Add workspace**
|
|
169
|
+
使用 ↑/↓ 和 Enter 选择工作区,然后选择已有会话或 **New session**。**All sessions** 同时显示未归属注册工作区的会话。**Add workspace (this directory)** 直接注册 `dsht` 自身所在的目录,只在服务端尚未注册它时出现;**Add workspace (host directory)** 接收服务端已有目录的绝对路径,该路径可能与本机文件系统不同,按 Esc 可以退回选择器。新建会话前必须选择工作区。
|
|
170
170
|
|
|
171
171
|
首次登录通过 `GET /` 兑换 token,并按 HTTP origin 保存 cookie。后续启动和列表命令自动复用 cookie,无需再次提供 token。默认目录为 `$XDG_STATE_HOME/dsht/auth`,未设置时使用 `~/.local/state/dsht/auth`;可通过 `--auth-dir` 或 `DSHT_AUTH_DIR` 覆盖。POSIX 下目录权限为 0700、cookie 文件为 0600;Windows 使用账户目录继承的访问控制。启动 token 永不保存。
|
|
172
172
|
|
|
@@ -279,8 +279,8 @@ npx @itookit/dsht list sessions --workspace WORKSPACE_ID --json
|
|
|
279
279
|
```sh
|
|
280
280
|
npm start -- list workspaces --json
|
|
281
281
|
npm start -- list sessions --json
|
|
282
|
-
node --import tsx src/cli.tsx list workspaces --json
|
|
283
|
-
node --import tsx src/cli.tsx list sessions --json
|
|
282
|
+
node --import tsx src/cli/index.tsx list workspaces --json
|
|
283
|
+
node --import tsx src/cli/index.tsx list sessions --json
|
|
284
284
|
```
|
|
285
285
|
|
|
286
286
|
JSON 输出格式为 `{ "items": [...] }`;省略 `--json` 则输出制表符分隔的列表。工作区筛选使用服务端 `sessionIds` 成员关系。工作区列表读取 `workspace/follow` 的首个 baseline 后取消订阅,不会调用不存在的 `workspace/list` 端点。
|
|
@@ -289,7 +289,7 @@ JSON 输出格式为 `{ "items": [...] }`;省略 `--json` 则输出制表符
|
|
|
289
289
|
|
|
290
290
|
Enter 提交消息:代理处于 Working 时自动作为下一个 step 的转向输入;空闲时开启新一轮。转向输入等待当前步骤及其工具执行完成,不会中断正在运行的工具。输入框非空时,Ctrl+C 先清空输入;否则所选会话运行中时请求取消,只有空闲时才退出,连续按键会复用尚未完成的取消请求。取消会等待正在提交的消息完成接收,失败时保留客户端。聊天界面中 Esc 会发送取消请求,不受本地空闲状态判断限制。任务运行中时,Esc 关闭文件或历史/搜索菜单的同时请求取消;空闲菜单仅关闭。正在执行的历史/搜索/费用加载、服务端命令和导出请求优先被取消。Page Up/Down 滚动当前对话;`/older` 加载更早记录。所有退出路径(包括 `/quit` 和 SIGTERM)都会在关闭连接前停止所选任务,因此退出不会留下仍在运行的代理;会话空闲时不发送取消。取消当前任务会保留排队消息。
|
|
291
291
|
|
|
292
|
-
`/copy`、Ctrl+S 或普通聊天界面的鼠标左键单击冻结画面并关闭鼠标事件捕获,便于使用终端原生选择复制。Esc、Ctrl+S 或 Ctrl+C
|
|
292
|
+
`/copy`、Ctrl+S 或普通聊天界面的鼠标左键单击冻结画面并关闭鼠标事件捕获,便于使用终端原生选择复制。Esc、Ctrl+S 或 Ctrl+C 退出复制模式并显示最新输出,退出复制模式不会取消代理。对话框和选择器暂停背景标题和对话的自动更新,聊天对话框也暂停状态更新。工作区选择、会话选择和主机路径输入界面的连接提示及状态栏持续刷新,复制模式除外。对话历史保留在输入框上方,鼠标滚轮及 PgUp/PgDn 可滚动历史,不会移动当前选项;帮助面板中的 PgUp/PgDn 用于帮助翻页。对话框中的鼠标单击不会进入复制模式,Ctrl+S 可冻结整个画面并释放鼠标捕获以进行原生选择。回看旧历史时 Working 计时显示也暂停。帮助/状态/费用面板不再定时消失。后台接收与内存回收继续运行,调整窗口大小仍可能重绘。
|
|
293
293
|
|
|
294
294
|
鼠标滚轮和 Page Up/Down 滚动对话;滚到顶部自动加载更早的一页。查看旧记录时,新输出保留阅读位置;向下滚动即可恢复跟随最新输出。TUI 挂载时启用鼠标报告,退出时关闭,需要终端支持 SGR 鼠标报告。加载历史或搜索期间,Esc 或 Ctrl+C 优先取消本地操作,不中断远程任务。
|
|
295
295
|
|
|
@@ -297,7 +297,7 @@ Enter 提交消息:代理处于 Working 时自动作为下一个 step 的转
|
|
|
297
297
|
|
|
298
298
|
`/search` 对对话消息进行不区分大小写的字面文本匹配,包含旧页,排除纯工具行。搜索每次请求最多 80 条消息,扫描后释放临时页,只保留最多 200 条简短命中摘要,包含折叠的思考;结果截断时提示缩小查询范围。选择命中项只加载其序号附近的独立页面,`/latest` 释放该窗口。Esc 或 Ctrl+C 可取消搜索。稀有词或无匹配查询仍需通过 HTTP 扫描全部历史,此命令尚无服务端全文索引。`/history` 只列出已加载页面中自己的提示词,选择器显示的数字就是记录序号。`/ssearch` 与 `/wsearch` 调用 `session/search`,服务端搜索当前用户/助手消息内容,最多返回 20 个会话、摘要和截断标记,没有结果分页游标或命中记录序号。工作区筛选在全局数量限制之后进行,因此截断时可能漏掉工作区内的匹配会话;界面会提示结果不完整,可缩小查询范围。选择会话后加载其历史,再选择匹配消息跳转。所有操作均通过 HTTP 完成,不扫描服务端配置目录。
|
|
299
299
|
|
|
300
|
-
↑/↓ 或 Ctrl+P/N 回填之前提交的提示词和 slash 命令,按 Enter
|
|
300
|
+
↑/↓ 或 Ctrl+P/N 回填之前提交的提示词和 slash 命令,按 Enter 才提交。一屏放得下的阅读面板会把方向键留给该历史,需要滚动的面板才接管方向键,而 Ctrl+P/N 在任何面板打开时都能回填。向下越过最新记录时恢复未发送草稿;编辑回填内容后开始新的草稿。历史按当前会话保留,最多 200 条、约 256 KiB 文本。打开或恢复会话时,从已加载的 User 消息初始化回填;切换会话释放旧缓存。不额外拉取历史页,也不写入独立历史文件。连续重复输入合并,超大输入跳过,提问和审批回答不记入历史。提问选项与补全菜单优先使用箭头;工作区/会话列表在输入框为空时使用箭头选择,可用 Ctrl+P/N 调出输入历史。
|
|
301
301
|
|
|
302
302
|
每条 User 消息之后,只在第一段助手正文或思考前显示 Assistant 标题;后续消息及流式输出沿用分组,工具结果和 Context 消息不重置分组。当前加载的历史窗口从自身起点建立可见分组,消息序号、工具状态、搜索和思考展开仍各自保留。
|
|
303
303
|
|
|
@@ -319,7 +319,7 @@ Tab 补全开头的 slash 命令,多个候选时补到公共前缀。单行输
|
|
|
319
319
|
|
|
320
320
|
| 命令 | 操作 |
|
|
321
321
|
| --- | --- |
|
|
322
|
-
| `/ws` |
|
|
322
|
+
| `/ws` | 显示所有工作区与两条添加工作区入口,选中后打开其会话列表 |
|
|
323
323
|
| `/ws TARGET` | 按 ID、完整名称/路径或唯一 ID 前缀选择工作区 |
|
|
324
324
|
| `/resume` | 显示当前工作区的会话;未选择工作区时先引导选择 |
|
|
325
325
|
| `/resume TARGET` | 按 ID、完整标题或唯一 ID 前缀跨工作区打开会话 |
|
|
@@ -334,6 +334,8 @@ Tab 补全开头的 slash 命令,多个候选时补到公共前缀。单行输
|
|
|
334
334
|
| `/permission [preset]` | 查看或切换服务端沙箱与审批预设 |
|
|
335
335
|
| `/feedback TEXT` | 记录当前会话反馈 |
|
|
336
336
|
| `/export [local.zip]` | 下载会话日志 ZIP 到新的本地文件 |
|
|
337
|
+
| `/export-html [local.html]` | 将已加载会话保存为包含图表和公式的离线 HTML |
|
|
338
|
+
| `/coredump [tag]` | 在当前工作目录写出 V8 堆快照,用于内存诊断 |
|
|
337
339
|
| `/older` | 加载更早的历史 |
|
|
338
340
|
| `/history [text]` | 列出自己的提示词并可选筛选;Enter 跳到所选记录 |
|
|
339
341
|
| `/search <text>` | 逐页搜索历史,选择命中项后打开其位置 |
|
|
@@ -342,13 +344,13 @@ Tab 补全开头的 slash 命令,多个候选时补到公共前缀。单行输
|
|
|
342
344
|
| `/ssearch <text>` | 在服务端搜索结果中筛选当前工作区的会话 |
|
|
343
345
|
| `/wsearch <text>` | 搜索服务端可见的所有工作区会话 |
|
|
344
346
|
| `/allow`, `/deny` | 回复当前审批;批准仅限一次 |
|
|
345
|
-
| `/status` |
|
|
346
|
-
| `/cost` |
|
|
347
|
+
| `/status` | 展开或收起底部完整状态信息;`↑`/`↓` 滚动、`PgUp`/`PgDn` 翻屏 |
|
|
348
|
+
| `/cost` | 展开/收起会话与今日费用,并刷新用量 |
|
|
347
349
|
| `/think` | 显示思考及用户 prompt 摘要列表;↑/↓ 选择、Enter 跳转并展开 |
|
|
348
350
|
| `/think SEQ` | 切换一条已加载思考的展开状态;`live` 表示当前尝试 |
|
|
349
351
|
| `/help`, `/quit` | 列出每条命令及其说明,或退出 |
|
|
350
352
|
|
|
351
|
-
Slash 命令在选择器和对话输入框中均可使用。输入 `/` 会显示匹配命令,`/help` 分页列出命令及其单行说明,PgUp/PgDn 翻页。`/help`、`/cost`、`/status` 面板保持打开,直到下一条命令或 Esc
|
|
353
|
+
Slash 命令在选择器和对话输入框中均可使用。输入 `/` 会显示匹配命令,`/help` 分页列出命令及其单行说明,PgUp/PgDn 翻页。`/help`、`/cost`、`/status` 面板保持打开,直到下一条命令或 Esc;`/history` 还会在十秒后自动关闭,避免遗留列表一直占用输入框。Esc 保留输入内容。`/workspace`、`/workspaces` 是 `/ws` 的别名;`/session`、`/sessions` 是 `/resume` 的别名。名称可以包含空格,完整目标两侧的引号可选。不带引号的目标 `all` 保留给 `/resume all`;打开标题为 `all` 的会话时,使用 `/resume "all"` 或其 ID。目标有歧义时必须提供完整 ID。切换工作区会打开其会话列表并解除旧对话订阅;切换会话会同步工作区标签。两种操作均不会取消远程代理。
|
|
352
354
|
|
|
353
355
|
在输入末尾键入 `@`,可搜索所选会话**在服务端**工作目录中的文件和目录。使用 ↑/↓ 选择,Tab 或 Enter 插入;选择目录后继续补全其内部路径。带空格的路径使用 `@"path with spaces"`。Esc 关闭菜单,任务运行中时同时请求取消;关闭后 Enter 发送原样输入,包括未匹配到的路径。搜索失败时显示错误,不提交输入。补全针对输入末尾的引用,不跟踪已有文本内部的光标位置。
|
|
354
356
|
|
|
@@ -362,25 +364,37 @@ Slash 命令在选择器和对话输入框中均可使用。输入 `/` 会显示
|
|
|
362
364
|
|
|
363
365
|
终端少于 62 列时,最新流式思考也默认折叠为一行;宽屏保持流式展开、完成后折叠。`/think live` 可手动展开或折叠当前思考,新回复恢复默认行为。
|
|
364
366
|
|
|
367
|
+
审批提供 1. 允许一次、2. 拒绝、3. 停止当前轮次。输入框为空时,按 1–3 或 ↑/↓ 选择,再按 Enter 确认;初始不选中任何操作,Esc 清除高亮,请求重放时重新回到未选中。已有草稿时仍按正常文字输入处理,也保留 `/allow`、`/deny` 和 `/cancel` 命令。
|
|
368
|
+
|
|
365
369
|
用户问题显示题目进度、编号选项和说明。选择列表、待答问题、审批和文件补全与文本输入共用一个输入框边框。待答问题和审批会在输入框上方保留最近对话历史,历史视口使用剩余高度;对话框打开时减少上下留白,并在手动滚动或视口高度变化时刷新;可见选项数量按终端高度调整,并跟随当前高亮项滚动。输入框为空时,↑/↓ 或 1–9 定位选项,Enter 确认;数字键只选择、不提交。多选题用空格或 1–9 勾选/取消勾选,Enter 确认,超过九个选项仍可通过方向键访问。选择 Other answer 后可输入纯数字自由文本,也保留普通文本回答。已有草稿时按正常文字输入处理,Esc 从 Other 返回选项而不取消提问。全部题目回答完成后,一次提交结构化选项标签及可选自定义文本;失败时保留答案以便重试。已识别的提问和审批事件在本次连接中按事件 ID 保留,包括早于会话选择到达的重放事件;只展示当前会话对应的请求。切换列表不会退回这些请求,不识别的 waterfall 仍通过 `next` 委托后续处理。存活服务端在客户端重连后重发待答事件;客户端重启不保留尚未提交的回答草稿。正常退出 TUI 会取消正在运行的轮次。调用已取消/失败或服务端已重启时,无法靠本地 UI 状态恢复原等待,需要发送新提示词要求重新提问。提交失败时保留输入;HTTP 响应中断可能导致投递状态不确定,手动重发前应检查会话记录。客户端不会自动重试修改请求。
|
|
366
370
|
|
|
367
371
|
标题固定在可滚动对话区域上方,输入框和状态栏保留在下方。底部不再常驻快捷键说明,完整快捷键放在 `/help`,选择器只显示当前需要的导航提示。顶部单行优先显示最新会话标题(无标题时回退到 ID),宽屏时在其后附上工作区名称,不再显示主机地址和连接状态。未选择会话时显示工作区名称或 All workspaces,下方为分隔线;`/status` 保留完整会话 ID。取消回执在后续历史消息到达时保持可见,直到服务端报告空闲;接受取消不表示工具进程已经退出。
|
|
368
372
|
|
|
373
|
+
## Markdown、图表与公式
|
|
374
|
+
|
|
375
|
+
消息正文支持 GitHub 风格 Markdown:标题、强调、删除线、链接、列表、任务项、引用、代码和表格。表格按终端显示宽度对齐,单元格内换行;列宽过窄时改为逐条纵向展示。代码保留缩进,链接保留目标地址。思考和工具摘要继续使用原有纯文本展示,搜索保留原始 Markdown 源码。
|
|
376
|
+
|
|
377
|
+
闭合的 `mermaid` 代码块将支持的流程图、状态图、时序图、类图和 ER 图渲染为 Unicode 文本图。图形超出可用宽度、不支持的语法或未闭合的代码块显示源码。`$...$`、`$$...$$`、`\(...\)`、`\[...\]` 及 `math` / `latex` / `tex` / `mathjax` 代码块使用 MathJax 的基础与 AMS TeX 包解析。终端显示 Unicode 符号、带括号的分式、上下标和矩阵;不支持的记法或无效 TeX 保留源码。终端公式是数学排版的近似表达。
|
|
378
|
+
|
|
379
|
+
`/export-html [local.html]` 将当前已加载会话和实时尾部保存为包含 Mermaid SVG 图片及 MathJax 生成的 MathML 的页面。用浏览器打开文件可查看完整数学排版,不需要网络或脚本。尚未加载或已回收的消息不包含在内,工具行仍为摘要。默认文件名带时间戳并保存在当前工作目录,支持带引号的路径,不覆盖已有文件,取消时删除未完成输出。`/export` 仍下载服务端完整日志 ZIP。
|
|
380
|
+
|
|
369
381
|
## 实时状态
|
|
370
382
|
|
|
371
|
-
底栏分组显示 `◐ Working · 8s · Ctrl+C Stop` 或 `● Ready
|
|
383
|
+
底栏分组显示 `◐ Working · 8s · Ctrl+C Stop` 或 `● Ready`、模型与思考强度、本会话费用与「今天花费(历史总计)」、十格上下文进度条及百分比、会话轮次与累计 token 及缓存命中率。宽终端为运行状态预留固定宽度,完成后模型和指标保持对齐。窄终端依次回收留白、隐藏进度条、缩短模型名、省略次要指标,优先保留停止提示。`/status` 显示主机 URL、操作状态、工作区完整路径、完整供应商/模型、下次模型、各项用量、轮次、队列、后台任务和四位小数费用。`!` 表示有指标或模型目录错误,或计费覆盖不完整;详情中显示原因。运行中使用最近实际使用的模型,空闲时使用下次模型,新会话使用服务端模型目录默认值。服务端设置、凭据和适配器变更通知会刷新模型目录。
|
|
384
|
+
|
|
385
|
+
工作计时使用已加载日志的 `turn/start` 时间戳。缺少该时间戳时,详情面板中的 `(observed)` 表示从客户端观察到运行开始计时;重连可能重置此备用计时。服务端报告空闲后停止计时。运行状态涵盖模型生成、工具执行及审批等待,不仅是文本输出。断线时明确标注为最后已知状态。展开面板把相关值合并到一行,并用单行状态栏已有的紧凑计数(`Context ~40% (400.6K/1M) · 229.7M tok`),因此 24 行终端可以一屏看到全部详情;`↑`/`↓` 逐行滚动,`PgUp`/`PgDn` 翻屏,页脚标出可见区间与总数。
|
|
372
386
|
|
|
373
|
-
|
|
387
|
+
单行状态栏按价值而不是按列来保留分组:`◐ 6:18 · bash 1:08 · ^C │ v4-flash · high · ctx 30% · S¥2.49* · ¥: 5.00 (12.34) · 2 turns · 34.5M tok · hit 92%`。当前阶段只报事实——推理时是 `think 28s`,工具运行时是工具名加已等待时长,回答流式输出时是 `write 12s`——绝不从静默推断异常,因为长时间推理与安静运行的工具都不是卡住。它显示的是**当前事件**的名字与年龄,并在下一段工作开始或回合关闭前保持不变:命令返回之后、下一次增量到达之前的静默期仍被算作这个回合的工作时间;`● Ready` 不显示阶段,因为只有宿主知道回合已经结束。两个费用分组互不替代:`S¥2.49*` 只报本会话,账本还没为它定价时如实写 `S?`;`¥: 5.00 (12.34)` 只报今天花费与历史总计。宽度不足时先丢价值最低的分组(缓存命中率、token、回合、effort、模型、当天花费,然后 ctx);本会话费用永不丢弃,只会挪到第二行;状态簇只在约二十列以下才让出阶段与停止提示。暂停的时钟会说明原因(`⏸ copy`、`⏸ dialog`、`⏸ history`)而不是无声冻结;`! Offline` 或 `⚠ Error` 会整体替换状态标记。
|
|
374
388
|
|
|
375
|
-
轮次数来自完整会话的 `sessionStats.turns` 投影。上下文占用标为 `~`:Harness 将供应商用量与对话变化估算值、最新模型容量结合。Token 总量来自完整会话的 `tokenUsage` 投影,分别显示非缓存输入、输出、缓存读取和缓存写入;思考 token
|
|
389
|
+
轮次数来自完整会话的 `sessionStats.turns` 投影。上下文占用标为 `~`:Harness 将供应商用量与对话变化估算值、最新模型容量结合。Token 总量来自完整会话的 `tokenUsage` 投影,分别显示非缓存输入、输出、缓存读取和缓存写入;思考 token 已包含在输出中。缓存命中率是缓存读取占三个互斥提示侧桶(非缓存输入、缓存读取、缓存写入)之和的比例,遇到部分命中时增加小数位而不是报成 `100%`。本会话与当天费用来自账本按会话保存的切片,因此还没有切片的会话显示为未知,而不是当天的花费。总量随服务端用量投影更新,不按流式字符计数。缺失数据显示 `unknown` 或 `?`。重连时控制流基线整体替换状态,每个投影键的序号防止旧 follow 快照覆盖较新的指标。
|
|
376
390
|
|
|
377
|
-
默认使用 [Catppuccin Mocha](https://catppuccin.com/palette/) 主题:`❯ User` 为蓝色,`✦ Assistant` 为绿色,思考为淡紫色,工具为天蓝色,成功为绿色,错误为红色。紧凑状态栏中 Ready 为绿色、Working
|
|
391
|
+
默认使用 [Catppuccin Mocha](https://catppuccin.com/palette/) 主题:`❯ User` 为蓝色,`✦ Assistant` 为绿色,思考为淡紫色,工具为天蓝色,成功为绿色,错误为红色。紧凑状态栏中 Ready 为绿色、Working 为黄色、离线为红色、暂停原因与分隔符为柔和灰色,模型/强度为淡紫色、费用为天蓝色、用量为柔和灰色。上下文占用达到 80% 时从绿色变黄,95% 时变红;这只是视觉阈值,不代表服务端压缩触发条件。各组在 ANSI 着色之前按优先级装填,因此无色终端显示同样的文字。语义配色独立放在 `src/ui/theme/index.ts`,应用可单独接收主题,消息不保存 ANSI 样式。Ink 根据终端能力输出颜色,无色终端仍保留角色标记。工具调用显示名称和描述;命令与描述不同时,下一行以 `$` 显示命令第一行。没有描述时使用命令第一行、路径或查询作为摘要。各行按终端显示宽度截断;结果按调用 ID 在原条目上将 ⚙ 更新为 ✓ 或 ✗,不再重复新增结果条目,命令预览保留两格缩进。调用尚未加载时单独显示结果摘要,加载调用页后合并;嵌套结果正文保持隐藏。
|
|
378
392
|
|
|
379
393
|
思考生成时完整流式显示,思考块结束或正文/工具输出开始后自动折叠。`/think` 按从新到旧列出思考摘要、前一条已加载的用户 prompt,并包含当前尝试。↑/↓ 选择、Enter 跳到原消息并展开;`/think SEQ` 可切换该消息的折叠状态,`/think live` 控制当前尝试。列表在选择、Esc 或其他命令时关闭,不自动超时。打开列表只使用已加载记录,选择 `Load older reasoning` 才读取一页更早历史;prompt 在已加载窗口之前时明确提示,加载对应页面后补全。完整思考仍可被搜索。
|
|
380
394
|
|
|
381
395
|
`/model` 读取 `session/modelCatalog`,展示服务端公布的 provider/model 路由及思考强度。选择通过 `session/selectModel` 提交 `{ request: { sessionId, provider, model, reasoningEffort? } }`,省略强度时使用适配器默认值。服务端将选择用于后续请求、记录选择事件,并尝试保存为部署默认值;不会替换正在执行的请求。展示模型继续以 `modelSelection.next` 和 `lastUsed` 为准,调用失败保留原选择。部分 provider 目录失败会单独提示,不隐藏正常 provider。标题与网页的 Agent preset 标签一致:`agentPreset` 提供当前 ID,`agentPresets/list` 提供名称和信任来源。内置系统预设显示 Standard mode、PTC mode、Minimal mode、Creator mode;自定义预设保留其名称,目录缺失时回退显示 ID。可选目录按需读取并在本次连接中复用。Plan 是独立功能,不决定这里的模式名称。终端少于 62 列时,mode 可在 `/status` 查看,为会话标题留出空间。
|
|
382
396
|
|
|
383
|
-
历史分为语义消息块、prompt/思考摘要、独立折叠状态和行数索引。流式更新复用已提交历史的位置索引,只生成当前可见区域。每个会话的 LRU 最多保留 2,048 行已提交终端内容,移出缓存的行在回看时重建。已结束的旧版流式分片和不用展示的工具结果正文会释放,原始日志由服务端保存。服务端日志作为持久层,客户端作为可重载的内存层。实时历史默认以 2,000 条记录或 16 MiB 语义数据估算量为软限制(`--history-records`、`--history-mb`),触发回收后以限制的 75% 为目标,释放旧正文、摘要和排版缓存。切换会话会释放上一会话的 transcript。回看和思考导航期间保护已加载窗口;`/latest` 返回实时输出并恢复回收。离线历史、未结束流和最小近期尾部受保护,因此这些参数不是进程 RSS
|
|
397
|
+
历史分为语义消息块、prompt/思考摘要、独立折叠状态和行数索引。流式更新复用已提交历史的位置索引,只生成当前可见区域。每个会话的 LRU 最多保留 2,048 行已提交终端内容,移出缓存的行在回看时重建。已结束的旧版流式分片和不用展示的工具结果正文会释放,原始日志由服务端保存。服务端日志作为持久层,客户端作为可重载的内存层。实时历史默认以 2,000 条记录或 16 MiB 语义数据估算量为软限制(`--history-records`、`--history-mb`),触发回收后以限制的 75% 为目标,释放旧正文、摘要和排版缓存。切换会话会释放上一会话的 transcript。回看和思考导航期间保护已加载窗口;`/latest` 返回实时输出并恢复回收。离线历史、未结束流和最小近期尾部受保护,因此这些参数不是进程 RSS 硬上限。默认启用体积受限的运行时内存日志,位于 `<state>/memory.log`:每 30 秒一行 JSON,记录进程计数、保留记录数与字节数、是否处于回看保护状态、账本规模、布局行缓存、有界的数学与图表缓存,以及最近一次成本扫描重读的会话数、页数与事件数,用于区分真实增长与 V8 高水位。若运行时提供强制回收能力,样本还会记录回收后的堆;`npm run start:profile` 会以该能力加上堆快照信号启动,此时 `kill -USR2 <pid>` 即可写出快照。`/coredump [tag]` 无需信号或 profiling 运行时,即可把同样的 `.heapsnapshot` 产物写入客户端当前工作目录;`tag` 用于命名快照(默认 `snapshot`),V8 会同步序列化整个堆,因此客户端在文件写完前会暂停。快照请用 Chrome DevTools 打开分析,而不是终端。`--memory-log <路径>` 或 `DSHT_MEMORY_LOG` 可改路径,`--no-memory-log` 或 `DSHT_MEMORY_LOG=off` 可关闭。首次排版、改变终端宽度及展开特别大的单个内容块,仍需要处理对应全文。`npm run bench:history` 测量 500、2,000、10,000 条消息下的本地流式排版耗时,不含网络和模型时间。
|
|
384
398
|
|
|
385
399
|
## 费用估算
|
|
386
400
|
|
|
@@ -388,21 +402,21 @@ Slash 命令在选择器和对话输入框中均可使用。输入 `/` 会显示
|
|
|
388
402
|
|
|
389
403
|
> 这里的费用是基于 Harness 可见用量和本地价格配置得到的高精度估算,用于成本监控和控制;它不是供应商账户级账单,最终费用仍以供应商账单为准。
|
|
390
404
|
|
|
391
|
-
`/cost`
|
|
405
|
+
`/cost` 显示当前会话与今日的费用。日期使用 Asia/Shanghai。状态栏以两位小数显示本会话费用,括号内是今日合计(`¥: 1.23(113.00)`);这两个数不表示预算。`*` 表示该小计并不精确:请求缺少时间戳、没有价格覆盖,或扫描尚未覆盖全部会话;`/cost` 会说明原因。每个服务端 origin 使用独立账本;总额覆盖 HTTP 可见会话及之前缓存的会话,不是供应商账户级账单。
|
|
392
406
|
|
|
393
|
-
|
|
407
|
+
客户端每次连接后、每 60 秒、任务结束及打开 `/cost` 时在后台通过 HTTP 读取完整历史;每个新连接都会把全部会话完整重读一遍,因为客户端不在时服务端仍在工作,已记录的更新时间无法说明这段空档里发生了什么;同一次连接内,服务端更新时间未变的空闲会话跳过扫描。计费不会发起模型请求。显式刷新时可按 Esc 或 Ctrl+C 取消。账本分别统计未缓存输入、缓存读/写和输出,思考 token 已包含在输出中。重试单独计费,同一次尝试的替换用量更新原记录,fork 继承历史不重复计费。没有可用用量、没有结算时间戳,或带官方价目表并不定价的缓存写入桶的请求只计入未计价而不猜测;没有条目覆盖的模型保持未计价,未列出的供应商不会套用官方价目。每个会话独立读取:某个会话不可达或被拒绝时只计为失败数量并继续扫描,不会中止整轮;子代理会话按其持久父级地址读取。扫描失败保留并标明部分缓存结果。
|
|
394
408
|
|
|
395
409
|
内置人民币价格于 2026-09-10 根据[官方价格页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/)核对。北京时间工作日 09:00–12:00、14:00–18:00 为高峰,其余时段半价。Flash 高峰未命中输入/缓存命中输入/输出为每百万 token ¥2/¥0.04/¥8,Pro 为 ¥9/¥0.30/¥27;当前模型名为 `deepseek-flash`,旧 Flash 名称沿用同一费率。供应方已公告自北京时间 2026-09-14 12:00 起将 `deepseek-v4-pro` 交由 Flash 服务并按 Flash 价格计费,内置条目已记录该变更,避免此后高估 Pro 用量。单列的缓存写入按未命中输入价计算。配置中的精确模型价格优先;否则 `deepseek-official` 模型名包含 `pro`(不区分大小写)时按 Pro 计价,其余名称包括临时别名均按 Flash 计价。其他供应商需要显式配置。
|
|
396
410
|
|
|
397
|
-
默认价格有效期从核对日期的北京时间零点开始,这是本地估算规则,不代表官方价格生效日期。更早用量需要补充历史价格版本。程序按助手请求结算记录的时间选择单价;官方未说明跨时段请求的归属,因此边界附近的估算可能与账单不同。图片使用供应商报告的 token
|
|
411
|
+
默认价格有效期从核对日期的北京时间零点开始,这是本地估算规则,不代表官方价格生效日期。更早用量需要补充历史价格版本。程序按助手请求结算记录的时间选择单价;官方未说明跨时段请求的归属,因此边界附近的估算可能与账单不同。图片使用供应商报告的 token 数。每次扫描都按当前加载的价格表重新折叠历史,因此落盘的总额是日志与价格表的投影:修改 `prices.json` 会在下一次扫描时重新计价它覆盖的请求,此前没有条目覆盖的请求则在出现覆盖后立即计价。
|
|
398
412
|
|
|
399
413
|
首次交互启动会创建 `~/.config/dsht/prices.json`(或 `$XDG_CONFIG_HOME/dsht/prices.json`),可用 `DSHT_CONFIG_DIR` 覆盖目录。JSON 数组中的价格版本包含 `id`、`provider`、`model`、`currency: "CNY"`、`source`、包含起点的 `from`、可选且不含终点的 `until`、`timezone`、星期数字 `weekdays`(`0` 为周日)、日内分钟区间 `windows`,以及 `peak`/`offPeak` 下每百万 token 的 `input`、`cacheRead`、`cacheWrite`、`output` 单价。调价时用 `until` 结束旧区间,再添加唯一 ID 且 `from` 衔接的新版本;程序拒绝重叠区间。重启后读取配置修改;价格由用户维护,启动时不抓取网页价格。
|
|
400
414
|
|
|
401
|
-
用量文件位于 `~/.local/state/dsht/cost/<origin-hash>/`,遵循 `XDG_STATE_HOME`,也可通过 `DSHT_STATE_DIR`
|
|
415
|
+
用量文件位于 `~/.local/state/dsht/cost/<origin-hash>/`,遵循 `XDG_STATE_HOME`,也可通过 `DSHT_STATE_DIR` 指定应用状态根目录。每个文件保存一个会话折叠后的总额:会话金额及其请求数与未计价数、最近一次扫描所在自然日的当天分桶、决策规则版本与该次折叠所用价格表的摘要,以及总额不精确的原因。文件不含提示词、工具正文、凭据、cookie,也不含任何逐请求信息。价格文件属于配置,这些用量文件属于状态,因此只有前者需要纳入设置备份。写入使用私有临时文件及原子替换;每个会话一个固定文件,写入前比较文件中记录的 cut 与规则版本,因此旧扫描无法覆盖较新的一次。缓存跨重启保留,不需要访问服务端配置目录。其他代数的文件会被忽略,并由下一次扫描重建。
|
|
402
416
|
|
|
403
417
|
## 客户端接口
|
|
404
418
|
|
|
405
|
-
安装后的包通过 `@itookit/dsht` 导出 `Client`,通过 `@itookit/dsht/auth` 导出 `login`/`CookieStore`,并提供 TypeScript 声明。源码调用方可通过 TypeScript loader 从 `src/client.ts` 导入,或构建后从 `dist/
|
|
419
|
+
安装后的包通过 `@itookit/dsht` 导出 `Client`,通过 `@itookit/dsht/auth` 导出 `login`/`CookieStore`,并提供 TypeScript 声明。源码调用方可通过 TypeScript loader 从 `src/transport/client.ts` 导入,或构建后从 `dist/index.js` 导入。`authenticate(token)` 兑换凭据;`connect()` 打开一条多路复用连接;`listWorkspaces()` 和 `listSessions(workspaceId?)` 返回服务端列表的 Promise。`call(endpoint, args, signal?)` 将服务端错误保留为带有 `code` 和 `details` 的 `RemoteError`。务必在 `finally` 中等待 `close()`。库调用方可使用 `src/transport/auth.ts` 的 `login(client, token, new CookieStore())` 启用持久化;`Client.authenticate()` 本身仅在内存中保留凭据。
|
|
406
420
|
|
|
407
421
|
会话和工作区命令在 `args` 内使用 `{ request: { ... } }`;会话列表使用 `{ _request: {} }`。`$events/result` 直接使用具名参数。重连后的 follow 快照整体替换保留状态;持久消息与临时助手文本分别保存。读取器同时支持 `event` 记录和旧版 `chunks` 包装;后者包含 `chunkrow/text-chunks`、`chunkrow/reasoning-chunks` 或 `chunkrow/tool-call-chunks`。不提供 `assistantStream` 的服务端通过日志 chunk 传递实时文本;TUI 只重建尚未完成的尝试,并保留每条压缩记录的起始序号用于翻页。
|
|
408
422
|
|
|
@@ -412,7 +426,7 @@ Slash 命令在选择器和对话输入框中均可使用。输入 `/` 会显示
|
|
|
412
426
|
|
|
413
427
|
| 字段 | 值 |
|
|
414
428
|
| --- | --- |
|
|
415
|
-
| 名称与版本 | `@itookit/dsht` `0.3.
|
|
429
|
+
| 名称与版本 | `@itookit/dsht` `0.3.3` |
|
|
416
430
|
| 可执行命令 | `dsht`,不安装时用 `npx @itookit/dsht` |
|
|
417
431
|
| 库入口 | `@itookit/dsht` 和 `@itookit/dsht/auth` |
|
|
418
432
|
| 作者 | lizlok\@gmail.com |
|
|
@@ -445,11 +459,13 @@ npm test
|
|
|
445
459
|
npm run test:terminal
|
|
446
460
|
npm run build
|
|
447
461
|
npm run bench:input
|
|
448
|
-
node dist/cli.js --help
|
|
462
|
+
node dist/cli/index.js --help
|
|
449
463
|
```
|
|
450
464
|
|
|
451
465
|
测试使用隔离的 HTTP/WebSocket 服务,驱动实际 Ink 选择器和输入框,在子进程中运行 CLI,并投影复制的 Harness v2 工作区编辑记录和 v0 压缩 chunk 记录。这些检查不需要模型凭据。记录和预期对话输出位于 `tests/`,不依赖父仓库。测试不覆盖真实模型供应商行为。
|
|
452
466
|
|
|
467
|
+
源码在 `src/` 下按业务域组织:`transport/` 负责服务端 wire 协议与认证,`session/` 负责对话、历史与交互,`cost/` 负责折叠计费账本,`catalog/` 负责模型与 preset,`controller/` 是应用门面,`ui/` 承载全部 React 与 Ink,`storage/` 负责全部文件系统操作,`cli/` 是组装入口。跨模块导入统一走各模块的 `index.ts`;`tests/architecture/dependencies.test.ts` 会拒绝禁止的依赖方向。
|
|
468
|
+
|
|
453
469
|
`npm test` 渲染不带样式的帧,因为断言和 `tests/expected/` 中的预期输出描述的是文本。从终端启动的测试运行器会向每个测试文件导出 `FORCE_COLOR=1`,使 Ink 在提示符与文本之间插入 SGR 转义序列;`npm run test:terminal` 在任何主机上复现该环境,`prepublishOnly` 也会运行它,因此从终端发布时验证的就是终端实际渲染的结果。 主题测试在独立子进程中分别渲染真彩色和纯文本,并隔离父进程中影响终端和 CI 颜色检测的环境设置。
|
|
454
470
|
|
|
455
471
|
输入期间复用历史投影和换行结果,直到对话版本或终端宽度变化;服务端更新和历史翻页会使缓存失效。`bench:input` 使用 20 条和 500 条合成消息,在预热后测量 30 次按键的本地输入至渲染耗时及历史投影读取次数。它排除网络/模型耗时,仅供诊断,不作为跨机器的延迟阈值。
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HostAccess } from '../transport/host.ts';
|
|
2
|
+
import { type ObjectValue } from '../transport/wire.ts';
|
|
3
|
+
import type { ControllerStore } from '../state.ts';
|
|
4
|
+
/** Owns model-catalog and preset loads for the selected session. */
|
|
5
|
+
export declare class CatalogController {
|
|
6
|
+
private readonly store;
|
|
7
|
+
private readonly host;
|
|
8
|
+
private presetClient?;
|
|
9
|
+
private revision;
|
|
10
|
+
private tasks;
|
|
11
|
+
constructor(store: ControllerStore, host: HostAccess);
|
|
12
|
+
/** Drop generation-scoped catalog state at the start of a connection generation. */
|
|
13
|
+
reset(): void;
|
|
14
|
+
/** Wait for every in-flight catalog task, so shutdown leaves no pending request. */
|
|
15
|
+
settle(): Promise<void>;
|
|
16
|
+
/** Load the optional preset roster once per connection, only when a session names a preset. */
|
|
17
|
+
loadPresetNames(): void;
|
|
18
|
+
/** Fetch current model routes and adapter-owned reasoning choices for the selected session.
|
|
19
|
+
* @returns Host catalog; provider failures remain available to the selector.
|
|
20
|
+
*/
|
|
21
|
+
modelCatalog(): Promise<ObjectValue>;
|
|
22
|
+
/** Select the next request's model; the host also attempts to save its deployment default.
|
|
23
|
+
* @param provider - Host provider route ID.
|
|
24
|
+
* @param model - Exact model ID.
|
|
25
|
+
* @param reasoningEffort - Optional adapter-owned effort ID; omission uses its default.
|
|
26
|
+
*/
|
|
27
|
+
selectModel(provider: string, model: string, reasoningEffort?: string): Promise<void>;
|
|
28
|
+
/** Reload the default route and provider failures without touching session state. */
|
|
29
|
+
refresh(): void;
|
|
30
|
+
/** @returns The selected session identity, or a `Select a session first` failure. */
|
|
31
|
+
private get sessionId();
|
|
32
|
+
}
|