@itookit/dsht 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +3 -0
- package/README.md +214 -0
- package/README.zh.md +214 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.js +353 -0
- package/dist/auth.d.ts +16 -0
- package/dist/auth.js +108 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +107 -0
- package/dist/client.d.ts +61 -0
- package/dist/client.js +230 -0
- package/dist/controller.d.ts +140 -0
- package/dist/controller.js +640 -0
- package/dist/cost-view.d.ts +8 -0
- package/dist/cost-view.js +20 -0
- package/dist/cost.d.ts +97 -0
- package/dist/cost.js +267 -0
- package/dist/endpoint.d.ts +16 -0
- package/dist/endpoint.js +16 -0
- package/dist/history.d.ts +17 -0
- package/dist/history.js +32 -0
- package/dist/input.d.ts +23 -0
- package/dist/input.js +103 -0
- package/dist/mouse.d.ts +14 -0
- package/dist/mouse.js +41 -0
- package/dist/navigation.d.ts +11 -0
- package/dist/navigation.js +36 -0
- package/dist/references.d.ts +25 -0
- package/dist/references.js +40 -0
- package/dist/status.d.ts +26 -0
- package/dist/status.js +135 -0
- package/dist/telemetry.d.ts +28 -0
- package/dist/telemetry.js +97 -0
- package/dist/transcript.d.ts +64 -0
- package/dist/transcript.js +279 -0
- package/dist/wire.d.ts +17 -0
- package/dist/wire.js +27 -0
- package/dsht.png +0 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lizlok@gmail.com
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# DeepSeek Harness Terminal
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
Choose a workspace and session, chat with a running DeepSeek Harness host, and inspect session history from your terminal. This is an independent Node.js repository: it has its own Git history, dependencies, and tests, and imports no Harness packages.
|
|
10
|
+
|
|
11
|
+
Main features:
|
|
12
|
+
|
|
13
|
+
- Workspace and session pickers, direct switching with `/ws` and `/s`, and explicit session creation.
|
|
14
|
+
- Streaming replies, reasoning, compact tool names and success/failure status, and paged conversation history.
|
|
15
|
+
- Queued prompts, steering, turn cancellation, approvals, and free-text question answers.
|
|
16
|
+
- Cookie persistence per host, automatic reconnect, and snapshot replacement.
|
|
17
|
+
- JSON or tab-separated workspace/session lists for scripts, plus a reusable HTTP client.
|
|
18
|
+
- Session and daily CNY cost estimates, versioned peak/off-peak prices, and `/cost` summaries.
|
|
19
|
+
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [Start](#start)
|
|
23
|
+
- [List workspaces and sessions](#list-workspaces-and-sessions)
|
|
24
|
+
- [Conversation controls](#conversation-controls)
|
|
25
|
+
- [Live status](#live-status)
|
|
26
|
+
- [Cost estimates](#cost-estimates)
|
|
27
|
+
- [Client API](#client-api)
|
|
28
|
+
- [Publishing to npm](#publishing-to-npm)
|
|
29
|
+
- [Development and limitations](#development-and-limitations)
|
|
30
|
+
|
|
31
|
+
## Start
|
|
32
|
+
|
|
33
|
+
Use Node.js 22.19 or newer and an existing `dsh web` server; the server is a separate prerequisite and is not launched by this client. The default host is the local `http://127.0.0.1:3080`:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npx @itookit/dsht
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A token is needed only on the first run and after the saved cookie expires. Export it on its own, or export the complete URL printed by `dsh web` and let the client split off its `?token=` parameter:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
export DSH_TOKEN=<token> && npx @itookit/dsht
|
|
43
|
+
export DSH_URL='http://127.0.0.1:3080/?token=<token>' && npx @itookit/dsht
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`DSH_TOKEN` takes precedence when both are set, and `--url` overrides `DSH_URL` for one run. The token is never written to disk; only the resulting cookie is. Both exports above land in your shell history, so use `read -rs -p 'Host token: ' DSH_TOKEN` where that matters. Another host needs its own origin in `DSH_URL`.
|
|
47
|
+
|
|
48
|
+
`npx @itookit/dsht list workspaces --json` and `npx @itookit/dsht list sessions --json` print workspace and session lists for scripts, and `npm install -g @itookit/dsht` installs the `dsht` command. These registry commands require the package to be published.
|
|
49
|
+
|
|
50
|
+
From a source checkout, install the dependencies and run the TypeScript entry:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm ci --ignore-scripts
|
|
54
|
+
npm start
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Both paths read the same `DSH_URL` and `DSH_TOKEN` variables.
|
|
58
|
+
|
|
59
|
+
Select a workspace with ↑/↓ and Enter, then select a session or **New session**. **All sessions** also exposes sessions outside registered workspaces. **Add workspace** accepts an existing absolute directory on the host, which may differ from your local filesystem. Creating a session requires a selected workspace.
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
The host determines cookie expiration. An expired or rejected cookie requires a token again; a supplied token refreshes authentication automatically after HTTP 401. Network failures and HTTP 403 do not trigger token exchange. Corrupt or insecure cookie files fail explicitly. The base URL must be an origin without a path or extra query parameters, and the host must allow its hostname.
|
|
64
|
+
|
|
65
|
+
## List workspaces and sessions
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npx @itookit/dsht list workspaces --json
|
|
69
|
+
npx @itookit/dsht list sessions --json
|
|
70
|
+
npx @itookit/dsht list sessions --workspace WORKSPACE_ID --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
From a source checkout, run the same commands through npm or through the source entry; the direct entry avoids npm's script banners:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
npm start -- list workspaces --json
|
|
77
|
+
npm start -- list sessions --json
|
|
78
|
+
node --import tsx src/cli.tsx list workspaces --json
|
|
79
|
+
node --import tsx src/cli.tsx list sessions --json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
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.
|
|
83
|
+
|
|
84
|
+
## Conversation controls
|
|
85
|
+
|
|
86
|
+
Enter submits a prompt. Ctrl+C 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 local history/search/cost loads are cancelled first. Page Up/Down scroll the retained transcript; `/older` loads an earlier page. `/quit` exits directly without cancelling remote work. Cancellation leaves pending queue items intact.
|
|
87
|
+
|
|
88
|
+
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. `/jump last` 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.
|
|
89
|
+
|
|
90
|
+
`/search` matches literal text case-insensitively in displayed messages, including older pages; hidden tool bodies are excluded. `/history` only lists loaded records. 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.
|
|
91
|
+
|
|
92
|
+
The single-line composer supports Readline-style editing. Words are whitespace-delimited; cursor movement and character deletion preserve composed Unicode characters. Multiline pasted text becomes one line with spaces. Ctrl+D on empty input does not exit; Ctrl+C keeps its stop/exit behavior. Other unhandled modifier shortcuts do not insert their control characters. Both BS and DEL terminal backspace encodings delete backward; the dedicated Delete key (CSI 3~) deletes forward.
|
|
93
|
+
|
|
94
|
+
| Key | Edit |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| Ctrl+A / Ctrl+E, Home / End | Move to start / end |
|
|
97
|
+
| Ctrl+B / Ctrl+F, ← / → | Move one character |
|
|
98
|
+
| Alt+B / Alt+F, Ctrl+← / Ctrl+→ | Move one word |
|
|
99
|
+
| Ctrl+K / Ctrl+U | Delete from cursor to end / from start to cursor |
|
|
100
|
+
| Ctrl+W, Alt+Backspace | Delete the preceding word |
|
|
101
|
+
| Alt+D | Delete the following word |
|
|
102
|
+
| Ctrl+Y | Restore the most recently killed text at the cursor |
|
|
103
|
+
| Ctrl+H / Backspace, Ctrl+D / Delete | Delete the preceding / following character |
|
|
104
|
+
|
|
105
|
+
| Command | Action |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `/ws` | Show all workspaces; choosing one opens its session list |
|
|
108
|
+
| `/ws TARGET` | Select a workspace by ID, exact name/path, or unique ID prefix |
|
|
109
|
+
| `/s` | Show sessions in the current workspace; choose a workspace first if none is selected |
|
|
110
|
+
| `/s TARGET` | Open a session by ID, exact title, or unique ID prefix across workspaces |
|
|
111
|
+
| `/s all` | Show sessions from every workspace |
|
|
112
|
+
| `/new` | Create a session in the selected workspace |
|
|
113
|
+
| `/cancel` | Cancel the active turn; leave pending queue items intact |
|
|
114
|
+
| `/steer TEXT` | Submit steering input |
|
|
115
|
+
| `/older` | Load older history |
|
|
116
|
+
| `/history [text]` | List loaded records, optionally filtered; Enter jumps to the selected record |
|
|
117
|
+
| `/jump <seq\|first\|last>` | Jump to a visible record sequence, oldest history, or latest output |
|
|
118
|
+
| `/search <text>` | Load and search the current session history; choose a matching message to jump |
|
|
119
|
+
| `/ssearch <text>` | Search host results within the selected workspace |
|
|
120
|
+
| `/wsearch <text>` | Search sessions across all workspaces visible to the host |
|
|
121
|
+
| `/allow`, `/deny` | Answer the displayed approval; allow applies once |
|
|
122
|
+
| `/status` | Expand or collapse full footer details |
|
|
123
|
+
| `/cost` | Toggle session/today/three-day estimates and refresh usage |
|
|
124
|
+
| `/help`, `/quit` | Show command hints or exit |
|
|
125
|
+
|
|
126
|
+
Slash commands work in both pickers and the conversation composer. Typing `/` displays matching commands. The long forms `/workspace`, `/workspaces`, `/session`, and `/sessions` remain aliases. Names may contain spaces; quotes around the complete target are optional. The unquoted target `all` is reserved for `/s all`; use `/s "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.
|
|
127
|
+
|
|
128
|
+
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.
|
|
129
|
+
|
|
130
|
+
A file reference sends only `@path` in a text block. Harness instructs the model to read the referenced file or list the directory when needed; the TUI does not read local files, upload bytes, or expand contents into the prompt. Referencing an image path does not attach image data. Local attachments, image uploads/previews, and `@` session references are not implemented.
|
|
131
|
+
|
|
132
|
+
User questions accept typed free-text answers one question at a time. Other sessions' interactions and unrecognized waterfalls delegate with `next`. 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.
|
|
133
|
+
|
|
134
|
+
The conversation header shows the latest session title, falling back to the ID; `/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.
|
|
135
|
+
|
|
136
|
+
## Live status
|
|
137
|
+
|
|
138
|
+
The footer defaults to one borderless line showing activity, model, workspace, context occupancy, and total tokens; wider terminals also show input/output, cache, queue, and job counts. Long names shorten by terminal display width, and narrow terminals omit lower-priority fields first. `/status` toggles full multiline details with the complete path, provider/model, reasoning effort, and usage buckets. `!` flags a metrics or model catalog error whose reason appears in the details. During a run it distinguishes the last-used model from a different next-request selection; a fresh session uses the host catalog default. Model catalog changes refresh on host settings, credential, and adapter notifications.
|
|
139
|
+
|
|
140
|
+
Working time uses the retained `turn/start` timestamp. If that timestamp is unavailable, `(observed)` 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.
|
|
141
|
+
|
|
142
|
+
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.
|
|
143
|
+
|
|
144
|
+
Tool-only rows omit the separate role heading: `⚙` identifies a call, `✓` a successful result, and `✗` a failed result. Once complete arguments are available, each row shows the tool name and operation description, falling back to its command, path, or query. Results reuse the matching call summary when retained history contains it. Each operation occupies at most one terminal row, with whitespace flattened and long text ellipsized by display width. Other arguments, nested results, and tool output remain hidden. Assistant prose and explicit approval requests remain visible so the user can understand the response and decide whether to approve an action.
|
|
145
|
+
|
|
146
|
+
## Cost estimates
|
|
147
|
+
|
|
148
|
+
`/cost` shows the selected session, today, and today plus the preceding two calendar days. Dates use Asia/Shanghai; the three-day view is not a rolling 72-hour window. The status bar reserves `S:` for session cost and `D:` for today. `~` marks an estimate, and `*` marks unpriced requests or incomplete coverage. Each host origin has a separate ledger. Totals cover HTTP-visible sessions and previously cached sessions; they are not account-wide provider bills.
|
|
149
|
+
|
|
150
|
+
The client reads complete histories in the background on connection, every 60 seconds, at turn completion, and when opening `/cost`. Idle sessions with unchanged host update timestamps 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. Missing timestamps, inconsistent usage and unknown prices remain unpriced. Failed scans retain labelled partial cached totals.
|
|
151
|
+
|
|
152
|
+
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 input/cache-hit/output rates are ¥3/¥0.10/¥9 per million tokens; Pro rates are ¥9/¥0.30/¥27. 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.
|
|
153
|
+
|
|
154
|
+
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. Cached priced requests retain their price version when configuration changes; previously unpriced requests can be priced on a later scan.
|
|
155
|
+
|
|
156
|
+
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.
|
|
157
|
+
|
|
158
|
+
Usage files live under `~/.local/state/dsht/cost/<origin-hash>/` (respecting `XDG_STATE_HOME`, or `DSHT_STATE_DIR` for the application state root). They contain only session IDs, timestamps, model identities, token counts, selected price versions and estimates. They exclude prompts, tool bodies, credentials and cookies. Writes use private temporary files and atomic replacement; opening-cut filenames prevent older concurrent scans from displacing a newer cached cut. The cache survives restart and does not need access to the host configuration directory.
|
|
159
|
+
|
|
160
|
+
## Client API
|
|
161
|
+
|
|
162
|
+
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/client.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/auth.ts`; `Client.authenticate()` itself only retains credentials in memory.
|
|
163
|
+
|
|
164
|
+
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.
|
|
165
|
+
|
|
166
|
+
## Publishing to npm
|
|
167
|
+
|
|
168
|
+
This repository publishes one public package, `@itookit/dsht`, from the `mushuanli/dsht` repository. The scope is required because npm rejects the unscoped `dsht` as too similar to existing short names such as `dot` and `st`. `package.json` is the authority for the fields below.
|
|
169
|
+
|
|
170
|
+
| Field | Value |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| Name and version | `@itookit/dsht` `0.1.0` |
|
|
173
|
+
| Executable | `dsht`, or `npx @itookit/dsht` without installing |
|
|
174
|
+
| Library entries | `@itookit/dsht` and `@itookit/dsht/auth` |
|
|
175
|
+
| Author | lizlok@gmail.com |
|
|
176
|
+
| License | MIT, with the license text in `LICENSE` |
|
|
177
|
+
| Repository and issues | [mushuanli/dsht](https://github.com/mushuanli/dsht) |
|
|
178
|
+
| Node.js | 22.19 or newer |
|
|
179
|
+
| Registry access | public, under the `@itookit` scope |
|
|
180
|
+
| Published files | `dist/`, both READMEs, their pairing record, the screenshot, and the license |
|
|
181
|
+
|
|
182
|
+
Descriptions, keywords, and dependencies live in `package.json`. The following commands are maintainer actions; creating a local package does not publish it.
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
npm run test:package
|
|
186
|
+
npm login
|
|
187
|
+
npm publish --access public
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`test:package` builds a tarball and runs its CLI through an isolated, offline npm-exec installation using the dependency cache populated by installation, and rejects any packed path outside the published set above. `prepublishOnly` checks types and tests; `prepack` compiles JavaScript and declarations. Source tests, recordings, and local authentication files are excluded.
|
|
191
|
+
|
|
192
|
+
`publishConfig.access` is `public`, which a scoped package needs to be installable without a paid plan; the flag is therefore part of the package rather than of the publish command. An account with two-factor authentication publishes with a live code, `npm publish --otp=<code>`; the code is checked at the final request, after the typecheck, suite, and build have already run.
|
|
193
|
+
|
|
194
|
+
Later releases run in `.github/workflows/publish.yml`, which publishes from a version tag with [trusted publishing](https://docs.npmjs.com/trusted-publishers) (OIDC) and provenance, so no publish token is stored. Configure it once at `npmjs.com` → `@itookit/dsht` → Settings → Trusted Publisher → GitHub Actions with organization or user `mushuanli`, repository `dsht`, workflow filename `publish.yml`, and allowed action `npm publish`. Trusted publishing cannot create a package, so version `0.1.0` is published by hand; after that, `npm version 0.1.1 && git push --follow-tags` releases.
|
|
195
|
+
|
|
196
|
+
The workflow packs without publishing when started manually, and refuses a tag that disagrees with `package.json`. See the official [scoped publishing guide](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/) and [npx documentation](https://docs.npmjs.com/cli/npm-exec/). Registry publication is not part of the local validation performed for this repository.
|
|
197
|
+
|
|
198
|
+
## Development and limitations
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
npm test
|
|
202
|
+
npm run test:terminal
|
|
203
|
+
npm run build
|
|
204
|
+
npm run bench:input
|
|
205
|
+
node dist/cli.js --help
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
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.
|
|
209
|
+
|
|
210
|
+
`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.
|
|
211
|
+
|
|
212
|
+
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.
|
|
213
|
+
|
|
214
|
+
The interface presents plain text, reasoning, tool calls, and tool results. Rich plugin cards, file upload, subagent navigation, model selection, and queue editing are not implemented. Reconnect uses bounded exponential backoff with jitter and replaces snapshots; list commands fail directly instead of retrying. Updating pre-stable host APIs requires updating the local wire adapter and tests.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# DeepSeek Harness Terminal
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## 摘要
|
|
8
|
+
|
|
9
|
+
在终端中选择工作区和会话,与运行中的 DeepSeek Harness 服务对话,并查看会话历史。这是独立的 Node.js 仓库,拥有自己的 Git 历史、依赖和测试,不导入 Harness 内部包。
|
|
10
|
+
|
|
11
|
+
主要功能:
|
|
12
|
+
|
|
13
|
+
- 工作区和会话选择器,通过 `/ws`、`/s` 直接切换,并显式创建会话。
|
|
14
|
+
- 流式回复、思考内容、精简工具名称及成功/失败状态和分页对话历史。
|
|
15
|
+
- 排队消息、转向输入、轮次取消、审批和自由文本问题回答。
|
|
16
|
+
- 按服务端保存 cookie、自动重连和快照替换。
|
|
17
|
+
- 面向脚本的 JSON/制表符工作区与会话列表,以及可复用的 HTTP 客户端。
|
|
18
|
+
- 会话与今日人民币费用估算、按版本保存的高峰/空闲价格,以及 `/cost` 汇总。
|
|
19
|
+
|
|
20
|
+
## 目录
|
|
21
|
+
|
|
22
|
+
- [启动](#启动)
|
|
23
|
+
- [列出工作区和会话](#列出工作区和会话)
|
|
24
|
+
- [对话操作](#对话操作)
|
|
25
|
+
- [实时状态](#实时状态)
|
|
26
|
+
- [费用估算](#费用估算)
|
|
27
|
+
- [客户端接口](#客户端接口)
|
|
28
|
+
- [发布到 npm](#发布到-npm)
|
|
29
|
+
- [开发与限制](#开发与限制)
|
|
30
|
+
|
|
31
|
+
## 启动
|
|
32
|
+
|
|
33
|
+
需要 Node.js 22.19 或更新版本,以及已经运行的 `dsh web` 服务;服务是单独的前置条件,本客户端不会启动它。默认连接本机 `http://127.0.0.1:3080`:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npx @itookit/dsht
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
仅在首次运行以及已保存的 cookie 过期后需要 token。可以单独导出它,也可以直接导出 `dsh web` 打印的完整地址,由客户端拆出其中的 `?token=` 参数:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
export DSH_TOKEN=<token> && npx @itookit/dsht
|
|
43
|
+
export DSH_URL='http://127.0.0.1:3080/?token=<token>' && npx @itookit/dsht
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
两者同时提供时 `DSH_TOKEN` 优先,`--url` 可覆盖单次运行的 `DSH_URL`。token 不会写入磁盘,只保存兑换得到的 cookie。上面两种 export 都会留在 shell 历史中,在意时改用 `read -rs -p 'Host token: ' DSH_TOKEN`。连接其他服务端需在 `DSH_URL` 中给出其 origin。
|
|
47
|
+
|
|
48
|
+
`npx @itookit/dsht list workspaces --json` 和 `npx @itookit/dsht list sessions --json` 供脚本获取工作区和会话列表,`npm install -g @itookit/dsht` 会安装 `dsht` 命令。这些 registry 命令要求包已发布。
|
|
49
|
+
|
|
50
|
+
若使用源码仓库,先安装依赖,再运行 TypeScript 入口:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm ci --ignore-scripts
|
|
54
|
+
npm start
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
两种方式读取相同的 `DSH_URL` 和 `DSH_TOKEN` 变量。
|
|
58
|
+
|
|
59
|
+
使用 ↑/↓ 和 Enter 选择工作区,然后选择已有会话或 **New session**。**All sessions** 同时显示未归属注册工作区的会话。**Add workspace** 接收服务端已有目录的绝对路径,该路径可能与本机文件系统不同。新建会话前必须选择工作区。
|
|
60
|
+
|
|
61
|
+
首次登录通过 `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 永不保存。
|
|
62
|
+
|
|
63
|
+
Cookie 有效期由服务端决定。过期或被拒绝后,需要再次提供 token;已提供 token 时,HTTP 401 会自动触发重新认证。网络故障和 HTTP 403 不触发 token 兑换。损坏或权限不安全的 cookie 文件会明确报错。服务地址必须是不带路径、且除 `token` 外无其他查询参数的 origin,主机名须受服务端信任。
|
|
64
|
+
|
|
65
|
+
## 列出工作区和会话
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npx @itookit/dsht list workspaces --json
|
|
69
|
+
npx @itookit/dsht list sessions --json
|
|
70
|
+
npx @itookit/dsht list sessions --workspace WORKSPACE_ID --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
在源码仓库中,可以通过 npm 或源码入口执行同样的命令;直接调用入口可以避免 npm 的脚本提示混入输出:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
npm start -- list workspaces --json
|
|
77
|
+
npm start -- list sessions --json
|
|
78
|
+
node --import tsx src/cli.tsx list workspaces --json
|
|
79
|
+
node --import tsx src/cli.tsx list sessions --json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
JSON 输出格式为 `{ "items": [...] }`;省略 `--json` 则输出制表符分隔的列表。工作区筛选使用服务端 `sessionIds` 成员关系。工作区列表读取 `workspace/follow` 的首个 baseline 后取消订阅,不会调用不存在的 `workspace/list` 端点。
|
|
83
|
+
|
|
84
|
+
## 对话操作
|
|
85
|
+
|
|
86
|
+
Enter 提交消息。所选会话运行中时,Ctrl+C 请求取消;只有空闲时才退出,连续按键会复用尚未完成的取消请求。取消会等待正在提交的消息完成接收,失败时保留客户端。聊天界面中 Esc 会发送取消请求,不受本地空闲状态判断限制。任务运行中时,Esc 关闭文件或历史/搜索菜单的同时请求取消;空闲菜单仅关闭。正在执行的本地历史/搜索/费用加载优先被取消。Page Up/Down 滚动当前对话;`/older` 加载更早记录。`/quit` 直接退出,不取消远程任务。取消当前任务会保留排队消息。
|
|
87
|
+
|
|
88
|
+
鼠标滚轮和 Page Up/Down 滚动对话;滚到顶部自动加载更早的一页。查看旧记录时,新输出保留阅读位置;`/jump last` 恢复跟随最新输出。TUI 挂载时启用鼠标报告,退出时关闭,需要终端支持 SGR 鼠标报告。加载历史或搜索期间,Esc 或 Ctrl+C 优先取消本地操作,不中断远程任务。
|
|
89
|
+
|
|
90
|
+
`/search` 对显示的消息进行不区分大小写的字面文本匹配,包含旧页,不搜索隐藏的工具正文。`/history` 仅列出已加载记录,选择器显示的数字就是记录序号。`/ssearch` 与 `/wsearch` 调用 `session/search`,服务端搜索当前用户/助手消息内容,最多返回 20 个会话、摘要和截断标记,没有结果分页游标或命中记录序号。工作区筛选在全局数量限制之后进行,因此截断时可能漏掉工作区内的匹配会话;界面会提示结果不完整,可缩小查询范围。选择会话后加载其历史,再选择匹配消息跳转。所有操作均通过 HTTP 完成,不扫描服务端配置目录。
|
|
91
|
+
|
|
92
|
+
单行输入框支持 Readline 风格编辑。单词以空白分隔;光标移动和逐字符删除保持完整的 Unicode 组合字符。粘贴的多行文本会以空格连接成一行。空输入时 Ctrl+D 不退出;Ctrl+C 保持停止/退出行为。未处理的修饰键快捷键不会将控制字符插入消息。终端退格键的 BS 和 DEL 编码均向后删除;独立 Delete 键(CSI 3~)向前删除。
|
|
93
|
+
|
|
94
|
+
| 按键 | 编辑操作 |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| Ctrl+A / Ctrl+E、Home / End | 移到开头/末尾 |
|
|
97
|
+
| Ctrl+B / Ctrl+F、← / → | 移动一个字符 |
|
|
98
|
+
| Alt+B / Alt+F、Ctrl+← / Ctrl+→ | 移动一个词 |
|
|
99
|
+
| Ctrl+K / Ctrl+U | 删除光标至末尾/开头至光标 |
|
|
100
|
+
| Ctrl+W、Alt+Backspace | 删除前一个词 |
|
|
101
|
+
| Alt+D | 删除后一个词 |
|
|
102
|
+
| Ctrl+Y | 在光标处恢复最近剪除的文本 |
|
|
103
|
+
| Ctrl+H / Backspace、Ctrl+D / Delete | 删除前一个/后一个字符 |
|
|
104
|
+
|
|
105
|
+
| 命令 | 操作 |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `/ws` | 显示所有工作区,选中后打开其会话列表 |
|
|
108
|
+
| `/ws TARGET` | 按 ID、完整名称/路径或唯一 ID 前缀选择工作区 |
|
|
109
|
+
| `/s` | 显示当前工作区的会话;未选择工作区时先引导选择 |
|
|
110
|
+
| `/s TARGET` | 按 ID、完整标题或唯一 ID 前缀跨工作区打开会话 |
|
|
111
|
+
| `/s all` | 显示所有工作区的会话 |
|
|
112
|
+
| `/new` | 在所选工作区创建会话 |
|
|
113
|
+
| `/cancel` | 取消当前轮次,保留待处理队列 |
|
|
114
|
+
| `/steer TEXT` | 提交转向输入 |
|
|
115
|
+
| `/older` | 加载更早的历史 |
|
|
116
|
+
| `/history [text]` | 列出并可选筛选已加载记录;Enter 跳到所选记录 |
|
|
117
|
+
| `/jump <seq\|first\|last>` | 跳到可见记录序号、最早历史或最新输出 |
|
|
118
|
+
| `/search <text>` | 补齐并搜索当前会话历史,选择匹配消息后跳转 |
|
|
119
|
+
| `/ssearch <text>` | 在服务端搜索结果中筛选当前工作区的会话 |
|
|
120
|
+
| `/wsearch <text>` | 搜索服务端可见的所有工作区会话 |
|
|
121
|
+
| `/allow`, `/deny` | 回复当前审批;批准仅限一次 |
|
|
122
|
+
| `/status` | 展开或收起底部完整状态信息 |
|
|
123
|
+
| `/cost` | 展开/收起会话、今日、三日费用,并刷新用量 |
|
|
124
|
+
| `/help`, `/quit` | 显示命令提示或退出 |
|
|
125
|
+
|
|
126
|
+
Slash 命令在选择器和对话输入框中均可使用。输入 `/` 会显示匹配命令。长命令 `/workspace`、`/workspaces`、`/session`、`/sessions` 保留为别名。名称可以包含空格,完整目标两侧的引号可选。不带引号的目标 `all` 保留给 `/s all`;打开标题为 `all` 的会话时,使用 `/s "all"` 或其 ID。目标有歧义时必须提供完整 ID。切换工作区会打开其会话列表并解除旧对话订阅;切换会话会同步工作区标签。两种操作均不会取消远程代理。
|
|
127
|
+
|
|
128
|
+
在输入末尾键入 `@`,可搜索所选会话**在服务端**工作目录中的文件和目录。使用 ↑/↓ 选择,Tab 或 Enter 插入;选择目录后继续补全其内部路径。带空格的路径使用 `@"path with spaces"`。Esc 关闭菜单,任务运行中时同时请求取消;关闭后 Enter 发送原样输入,包括未匹配到的路径。搜索失败时显示错误,不提交输入。补全针对输入末尾的引用,不跟踪已有文本内部的光标位置。
|
|
129
|
+
|
|
130
|
+
文件引用仅在文本块中发送 `@path`。Harness 提示模型按需读取文件或列出目录;TUI 不读取本地文件、不上传字节,也不将文件内容展开进提示词。引用图片路径不会附带图片数据。尚未实现本地附件、图片上传/预览及 `@` 会话引用。
|
|
131
|
+
|
|
132
|
+
用户问题逐题接收自由文本回答。其他会话的交互以及不识别的 waterfall 通过 `next` 委托后续处理。提交失败时保留输入;HTTP 响应中断可能导致投递状态不确定,手动重发前应检查会话记录。客户端不会自动重试修改请求。
|
|
133
|
+
|
|
134
|
+
对话顶部显示最新会话标题,无标题时回退到 ID;`/status` 保留完整会话 ID。取消回执在后续历史消息到达时保持可见,直到服务端报告空闲;接受取消不表示工具进程已经退出。
|
|
135
|
+
|
|
136
|
+
## 实时状态
|
|
137
|
+
|
|
138
|
+
底栏默认无边框单行显示运行状态、模型、工作区、上下文占用和 token 总量;宽度足够时补充输入/输出、缓存、队列和后台任务数。长名称按终端显示宽度缩短,窄终端优先省略次要信息。`/status` 切换完整多行详情,显示完整路径、供应商/模型、思考强度及各项用量。`!` 表示有指标或模型目录错误,详情中显示原因。运行中时区分最近实际使用的模型和不同的下次请求模型;新会话使用服务端模型目录的默认值。服务端设置、凭据和适配器变更通知会刷新模型目录。
|
|
139
|
+
|
|
140
|
+
工作计时使用已加载日志的 `turn/start` 时间戳。缺少该时间戳时,`(observed)` 表示从客户端观察到运行开始计时;重连可能重置此备用计时。服务端报告空闲后停止计时。运行状态涵盖模型生成、工具执行及审批等待,不仅是文本输出。断线时明确标注为最后已知状态。
|
|
141
|
+
|
|
142
|
+
上下文占用标为 `~`:Harness 将供应商用量与对话变化估算值、最新模型容量结合。Token 总量来自完整会话的 `tokenUsage` 投影,分别显示非缓存输入、输出、缓存读取和缓存写入;思考 token 已包含在输出中。总量随服务端用量投影更新,不按流式字符计数。缺失数据显示 `unknown` 或 `?`。重连时控制流基线整体替换状态,每个投影键的序号防止旧 follow 快照覆盖较新的指标。
|
|
143
|
+
|
|
144
|
+
纯工具行省略独立角色标题:`⚙` 表示调用,`✓` 表示成功结果,`✗` 表示失败结果。完整参数到达后,每行显示工具名和操作描述,缺少描述时使用命令、路径或查询摘要。已加载历史包含对应调用时,结果复用该调用摘要。每个操作最多占一行,合并空白,并按终端显示宽度用省略号截断。其他参数、嵌套结果和工具正文仍然隐藏。助手正文与显式审批请求保持可见,以便用户理解回答并判断是否批准操作。
|
|
145
|
+
|
|
146
|
+
## 费用估算
|
|
147
|
+
|
|
148
|
+
`/cost` 显示当前会话、今日及今日加前两个自然日的费用。日期使用 Asia/Shanghai,三日统计不是滚动 72 小时。状态栏中 `S:` 表示会话费用,`D:` 表示今日费用。`~` 表示估算,`*` 表示存在未计价请求或统计尚不完整。每个服务端 origin 使用独立账本;总额覆盖 HTTP 可见会话及之前缓存的会话,不是供应商账户级账单。
|
|
149
|
+
|
|
150
|
+
客户端连接后、每 60 秒、任务结束及打开 `/cost` 时在后台通过 HTTP 读取完整历史;服务端更新时间未变的空闲会话跳过扫描。计费不会发起模型请求。显式刷新时可按 Esc 或 Ctrl+C 取消。账本分别统计未缓存输入、缓存读/写和输出,思考 token 已包含在输出中。重试单独计费,同一次尝试的替换用量更新原记录,fork 继承历史不重复计费。缺少时间戳、用量矛盾或缺少价格时标为未计价;扫描失败保留并标明部分缓存结果。
|
|
151
|
+
|
|
152
|
+
内置人民币价格于 2026-09-10 根据[官方价格页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/)核对。北京时间工作日 09:00–12:00、14:00–18:00 为高峰,其余时段半价。Flash 高峰未命中输入/缓存命中/输出为每百万 token ¥3/¥0.10/¥9,Pro 为 ¥9/¥0.30/¥27。单列的缓存写入按未命中输入价计算。配置中的精确模型价格优先;否则 `deepseek-official` 模型名包含 `pro`(不区分大小写)时按 Pro 计价,其余名称包括临时别名均按 Flash 计价。其他供应商需要显式配置。
|
|
153
|
+
|
|
154
|
+
默认价格有效期从核对日期的北京时间零点开始,这是本地估算规则,不代表官方价格生效日期。更早用量需要补充历史价格版本。程序按助手请求结算记录的时间选择单价;官方未说明跨时段请求的归属,因此边界附近的估算可能与账单不同。图片使用供应商报告的 token 数。已计价请求保留原价格版本,不随配置修改重新套价;未计价请求可以在后续扫描时补算。
|
|
155
|
+
|
|
156
|
+
首次交互启动会创建 `~/.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` 衔接的新版本;程序拒绝重叠区间。重启后读取配置修改;价格由用户维护,启动时不抓取网页价格。
|
|
157
|
+
|
|
158
|
+
用量文件位于 `~/.local/state/dsht/cost/<origin-hash>/`,遵循 `XDG_STATE_HOME`,也可通过 `DSHT_STATE_DIR` 指定应用状态根目录。文件只含会话 ID、时间戳、模型身份、token 数、所选价格版本和估算值,不包含提示词、工具正文、凭据或 cookie。写入使用私有临时文件及原子替换,按历史截点命名的文件避免旧扫描覆盖更新的缓存截点。缓存跨重启保留,不需要访问服务端配置目录。
|
|
159
|
+
|
|
160
|
+
## 客户端接口
|
|
161
|
+
|
|
162
|
+
安装后的包通过 `@itookit/dsht` 导出 `Client`,通过 `@itookit/dsht/auth` 导出 `login`/`CookieStore`,并提供 TypeScript 声明。源码调用方可通过 TypeScript loader 从 `src/client.ts` 导入,或构建后从 `dist/client.js` 导入。`authenticate(token)` 兑换凭据;`connect()` 打开一条多路复用连接;`listWorkspaces()` 和 `listSessions(workspaceId?)` 返回服务端列表的 Promise。`call(endpoint, args, signal?)` 将服务端错误保留为带有 `code` 和 `details` 的 `RemoteError`。务必在 `finally` 中等待 `close()`。库调用方可使用 `src/auth.ts` 的 `login(client, token, new CookieStore())` 启用持久化;`Client.authenticate()` 本身仅在内存中保留凭据。
|
|
163
|
+
|
|
164
|
+
会话和工作区命令在 `args` 内使用 `{ request: { ... } }`;会话列表使用 `{ _request: {} }`。`$events/result` 直接使用具名参数。重连后的 follow 快照整体替换保留状态;持久消息与临时助手文本分别保存。读取器同时支持 `event` 记录和旧版 `chunks` 包装;后者包含 `chunkrow/text-chunks`、`chunkrow/reasoning-chunks` 或 `chunkrow/tool-call-chunks`。不提供 `assistantStream` 的服务端通过日志 chunk 传递实时文本;TUI 只重建尚未完成的尝试,并保留每条压缩记录的起始序号用于翻页。
|
|
165
|
+
|
|
166
|
+
## 发布到 npm
|
|
167
|
+
|
|
168
|
+
本仓库从 `mushuanli/dsht` 仓库发布一个公开包 `@itookit/dsht`。必须使用 scope,因为 npm 会以「与 `dot`、`st` 等现有短名过于相似」为由拒绝非 scope 的 `dsht`。下表中 `package.json` 是各字段的依据。
|
|
169
|
+
|
|
170
|
+
| 字段 | 值 |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| 名称与版本 | `@itookit/dsht` `0.1.0` |
|
|
173
|
+
| 可执行命令 | `dsht`,不安装时用 `npx @itookit/dsht` |
|
|
174
|
+
| 库入口 | `@itookit/dsht` 和 `@itookit/dsht/auth` |
|
|
175
|
+
| 作者 | lizlok@gmail.com |
|
|
176
|
+
| 许可证 | MIT,许可证正文位于 `LICENSE` |
|
|
177
|
+
| 仓库与问题反馈 | [mushuanli/dsht](https://github.com/mushuanli/dsht) |
|
|
178
|
+
| Node.js | 22.19 或更新版本 |
|
|
179
|
+
| Registry 访问 | public,使用 `@itookit` scope |
|
|
180
|
+
| 发布内容 | `dist/`、两份 README、它们的配对记录、截图和许可证 |
|
|
181
|
+
|
|
182
|
+
描述、关键词和依赖位于 `package.json`。以下命令属于维护者操作;创建本地安装包不会自动发布。
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
npm run test:package
|
|
186
|
+
npm login
|
|
187
|
+
npm publish --access public
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`test:package` 构建 tarball,然后使用安装依赖时填充的缓存,在隔离的离线 npm-exec 安装中运行 CLI,并拒绝上述发布集合之外的打包路径。`prepublishOnly` 执行类型检查和测试;`prepack` 编译 JavaScript 与类型声明。包内不包含源码测试、录制数据和本地认证文件。
|
|
191
|
+
|
|
192
|
+
`publishConfig.access` 为 `public`;scoped 包需要它才能被公开安装,因此该设置放在包里而不是每次发布命令上。启用两步验证的账号需用即时验证码发布:`npm publish --otp=<验证码>`;验证码在最后一次请求时校验,此时类型检查、测试和构建均已执行完毕。
|
|
193
|
+
|
|
194
|
+
后续版本由 `.github/workflows/publish.yml` 发布:它以版本 tag 触发,使用 [trusted publishing](https://docs.npmjs.com/trusted-publishers)(OIDC)并生成 provenance,不保存任何发布 token。需在 `npmjs.com` → `@itookit/dsht` → Settings → Trusted Publisher → GitHub Actions 一次性配置:组织或用户 `mushuanli`、仓库 `dsht`、工作流文件名 `publish.yml`、允许动作 `npm publish`。Trusted publishing 无法创建包,因此 `0.1.0` 需手工发布;之后执行 `npm version 0.1.1 && git push --follow-tags` 即可发布。
|
|
195
|
+
|
|
196
|
+
手动触发时该工作流只打包不发布,并拒绝与 `package.json` 不一致的 tag。参见官方 [scoped 发布指南](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/)和 [npx 文档](https://docs.npmjs.com/cli/npm-exec/)。Registry 发布不属于本仓库已执行的本地验证。
|
|
197
|
+
|
|
198
|
+
## 开发与限制
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
npm test
|
|
202
|
+
npm run test:terminal
|
|
203
|
+
npm run build
|
|
204
|
+
npm run bench:input
|
|
205
|
+
node dist/cli.js --help
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
测试使用隔离的 HTTP/WebSocket 服务,驱动实际 Ink 选择器和输入框,在子进程中运行 CLI,并投影复制的 Harness v2 工作区编辑记录和 v0 压缩 chunk 记录。这些检查不需要模型凭据。记录和预期对话输出位于 `tests/`,不依赖父仓库。测试不覆盖真实模型供应商行为。
|
|
209
|
+
|
|
210
|
+
`npm test` 渲染不带样式的帧,因为断言和 `tests/expected/` 中的预期输出描述的是文本。从终端启动的测试运行器会向每个测试文件导出 `FORCE_COLOR=1`,使 Ink 在提示符与文本之间插入 SGR 转义序列;`npm run test:terminal` 在任何主机上复现该环境,`prepublishOnly` 也会运行它,因此从终端发布时验证的就是终端实际渲染的结果。
|
|
211
|
+
|
|
212
|
+
输入期间复用历史投影和换行结果,直到对话版本或终端宽度变化;服务端更新和历史翻页会使缓存失效。`bench:input` 使用 20 条和 500 条合成消息,在预热后测量 30 次按键的本地输入至渲染耗时及历史投影读取次数。它排除网络/模型耗时,仅供诊断,不作为跨机器的延迟阈值。
|
|
213
|
+
|
|
214
|
+
界面显示纯文本、思考内容、工具调用和工具结果。尚未实现富插件卡片、文件上传、子代理导航、模型选择和队列编辑。重连采用有上限的指数退避及抖动,并替换快照;列表命令直接报告失败而不重试。服务端的非稳定 API 更新后,需要同步本地报文适配和测试。
|
package/dist/app.d.ts
ADDED