dsh-ops 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +189 -0
- package/LICENSE +30 -0
- package/NOTICE +106 -0
- package/PROVENANCE.md +417 -0
- package/README.en.md +121 -0
- package/README.md +113 -2
- package/README.zh.md +114 -0
- package/bin/dsh-ops.mjs +1216 -0
- package/cordis.patch.yml +160 -0
- package/docs/manual-validation.md +53 -0
- package/docs/schema-baseline.json +64 -0
- package/docs/schema-current.json +84 -0
- package/docs/schema-measurement.md +17 -0
- package/dsh-plugin.json +88 -0
- package/icon.svg +12 -0
- package/lib/binary.js +409 -0
- package/lib/config.js +198 -0
- package/lib/handshake.js +252 -0
- package/lib/index.js +108 -0
- package/lib/jobs.js +42 -0
- package/lib/policy.js +64 -0
- package/lib/presentation.js +63 -0
- package/lib/profile-install.js +61 -0
- package/lib/rust.js +194 -0
- package/lib/session-shells.js +78 -0
- package/lib/shells.js +993 -0
- package/lib/tools.js +657 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +114 -4
- package/vendor/fastctx/Cargo.lock +3210 -0
- package/vendor/fastctx/Cargo.toml +94 -0
- package/vendor/fastctx/FORK.md +119 -0
- package/vendor/fastctx/LICENSE-APACHE +201 -0
- package/vendor/fastctx/NOTICE +40 -0
- package/vendor/fastctx/README.md +439 -0
- package/vendor/fastctx/THIRD_PARTY_LICENSES.md +17 -0
- package/vendor/fastctx/THIRD_PARTY_LICENSES_RUST.md +7914 -0
- package/vendor/fastctx/UPSTREAM.md +49 -0
- package/vendor/fastctx/build.rs +413 -0
- package/vendor/fastctx/src/background_status.rs +403 -0
- package/vendor/fastctx/src/binary.rs +75 -0
- package/vendor/fastctx/src/bounded_sort.rs +500 -0
- package/vendor/fastctx/src/budget.rs +781 -0
- package/vendor/fastctx/src/cli/mod.rs +110 -0
- package/vendor/fastctx/src/context_guard.rs +289 -0
- package/vendor/fastctx/src/control/mod.rs +6 -0
- package/vendor/fastctx/src/control/paths.rs +49 -0
- package/vendor/fastctx/src/control/settings.rs +753 -0
- package/vendor/fastctx/src/control/transaction.rs +531 -0
- package/vendor/fastctx/src/edit/document.rs +535 -0
- package/vendor/fastctx/src/edit/locks.rs +371 -0
- package/vendor/fastctx/src/edit/mod.rs +213 -0
- package/vendor/fastctx/src/edit/private_storage/unix.rs +315 -0
- package/vendor/fastctx/src/edit/private_storage/windows.rs +793 -0
- package/vendor/fastctx/src/edit/private_storage.rs +234 -0
- package/vendor/fastctx/src/edit/replace.rs +1030 -0
- package/vendor/fastctx/src/edit_server.rs +53 -0
- package/vendor/fastctx/src/encoding/reference_v011.rs +587 -0
- package/vendor/fastctx/src/encoding/snapshot_pipeline.rs +1678 -0
- package/vendor/fastctx/src/encoding.rs +1118 -0
- package/vendor/fastctx/src/file_executor.rs +1151 -0
- package/vendor/fastctx/src/file_snapshot.rs +1491 -0
- package/vendor/fastctx/src/glob_filter.rs +98 -0
- package/vendor/fastctx/src/glob_tool.rs +653 -0
- package/vendor/fastctx/src/grep_sink.rs +1162 -0
- package/vendor/fastctx/src/grep_tool.rs +2449 -0
- package/vendor/fastctx/src/lib.rs +45 -0
- package/vendor/fastctx/src/main.rs +15 -0
- package/vendor/fastctx/src/model.rs +51 -0
- package/vendor/fastctx/src/model_guidance.rs +62 -0
- package/vendor/fastctx/src/operation.rs +356 -0
- package/vendor/fastctx/src/ordered_window.rs +1235 -0
- package/vendor/fastctx/src/os_environment.rs +414 -0
- package/vendor/fastctx/src/path_codec.rs +850 -0
- package/vendor/fastctx/src/paths.rs +244 -0
- package/vendor/fastctx/src/process_identity.rs +763 -0
- package/vendor/fastctx/src/process_policy.rs +74 -0
- package/vendor/fastctx/src/read_tool/batch.rs +496 -0
- package/vendor/fastctx/src/read_tool/hex_file.rs +141 -0
- package/vendor/fastctx/src/read_tool/image_file.rs +88 -0
- package/vendor/fastctx/src/read_tool/mod.rs +245 -0
- package/vendor/fastctx/src/read_tool/pdf.rs +470 -0
- package/vendor/fastctx/src/read_tool/pdf_disabled.rs +47 -0
- package/vendor/fastctx/src/read_tool/pdf_engine.rs +664 -0
- package/vendor/fastctx/src/read_tool/text_file.rs +351 -0
- package/vendor/fastctx/src/render_plan.rs +468 -0
- package/vendor/fastctx/src/runtime/activity.rs +159 -0
- package/vendor/fastctx/src/runtime/hosts.rs +99 -0
- package/vendor/fastctx/src/runtime/journal.rs +556 -0
- package/vendor/fastctx/src/runtime/local_ipc.rs +186 -0
- package/vendor/fastctx/src/runtime/mod.rs +746 -0
- package/vendor/fastctx/src/runtime/protocol.rs +296 -0
- package/vendor/fastctx/src/runtime/session.rs +536 -0
- package/vendor/fastctx/src/runtime/windows_process.rs +66 -0
- package/vendor/fastctx/src/search_parallelism.rs +106 -0
- package/vendor/fastctx/src/search_text.rs +227 -0
- package/vendor/fastctx/src/server.rs +359 -0
- package/vendor/fastctx/src/server_manifest.rs +468 -0
- package/vendor/fastctx/src/server_support.rs +826 -0
- package/vendor/fastctx/src/session.rs +629 -0
- package/vendor/fastctx/src/shell/apply_patch_hint.rs +41 -0
- package/vendor/fastctx/src/shell/bash.rs +263 -0
- package/vendor/fastctx/src/shell/buffer.rs +108 -0
- package/vendor/fastctx/src/shell/encoding.rs +403 -0
- package/vendor/fastctx/src/shell/foreground.rs +115 -0
- package/vendor/fastctx/src/shell/jobs/admission.rs +91 -0
- package/vendor/fastctx/src/shell/jobs/background.rs +146 -0
- package/vendor/fastctx/src/shell/jobs/host.rs +830 -0
- package/vendor/fastctx/src/shell/jobs/identity.rs +29 -0
- package/vendor/fastctx/src/shell/jobs/mod.rs +1513 -0
- package/vendor/fastctx/src/shell/jobs/model.rs +244 -0
- package/vendor/fastctx/src/shell/jobs/output_log.rs +1148 -0
- package/vendor/fastctx/src/shell/jobs/store.rs +1300 -0
- package/vendor/fastctx/src/shell/mod.rs +345 -0
- package/vendor/fastctx/src/shell/normalize.rs +389 -0
- package/vendor/fastctx/src/shell/output.rs +406 -0
- package/vendor/fastctx/src/shell/process.rs +493 -0
- package/vendor/fastctx/src/shell_server.rs +156 -0
- package/vendor/fastctx/src/skip_report.rs +83 -0
- package/vendor/fastctx/src/stdio_transport.rs +177 -0
- package/vendor/fastctx/src/tool_schema.rs +204 -0
- package/vendor/fastctx/src/traversal.rs +846 -0
- package/vendor/fastctx/third-party/pdfium-7763/LICENSE +9 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/abseil.txt +202 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/agg23.txt +14 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/fast_float.txt +27 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/freetype.txt +169 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/icu.txt +542 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/lcms.txt +27 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/libjpeg_turbo.ijg +260 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/libjpeg_turbo.md +135 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/libopenjpeg.txt +32 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/libpng.txt +134 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/libtiff.txt +21 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/llvm-libc.txt +278 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/pdfium.txt +230 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/simdutf.txt +18 -0
- package/vendor/fastctx/third-party/pdfium-7763/licenses/zlib.txt +29 -0
package/PROVENANCE.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# Provenance
|
|
2
|
+
|
|
3
|
+
What this repository is, where its parts came from, what was verified, and what
|
|
4
|
+
was not.
|
|
5
|
+
|
|
6
|
+
## 0.2.1 Windows payload packaging
|
|
7
|
+
|
|
8
|
+
The assembler now builds all three Windows x64 payload packages. Bash carries
|
|
9
|
+
complete, unmodified PortableGit 2.56.0.2 (GNU bash 5.3.15); pwsh carries complete
|
|
10
|
+
PowerShell 7.6.6 under bin/. Original licenses/notices are preserved. Archive
|
|
11
|
+
and executable hashes are pinned in lib/shells.js. All three runtime packages
|
|
12
|
+
are injected into the main publish artifact; no install lifecycle downloads.
|
|
13
|
+
|
|
14
|
+
The maintainer authorized publication after the source-delivery risk was reported.
|
|
15
|
+
The first attempt used an old local npm credential and received E403; explicitly
|
|
16
|
+
selecting the maintainer-provided bypass token succeeded. This was a credential
|
|
17
|
+
selection error, not proof the provided token lacked bypass permission. PortableGit's
|
|
18
|
+
etc/package-versions.txt includes many GPL/LGPL components; complete matching
|
|
19
|
+
source closure/delivery has not been collected or reviewed. Retaining upstream
|
|
20
|
+
licenses does not attest that those obligations have been fulfilled. No source
|
|
21
|
+
offer is invented. Historical 0.2.0 pin-package descriptions below do not describe
|
|
22
|
+
this new payload layout.
|
|
23
|
+
|
|
24
|
+
The profile installer delegates to the official DSH CLI, retaining its locks,
|
|
25
|
+
compatibility checks and rollback rather than cloning them. Live installation
|
|
26
|
+
on each application version has not been attested. Default README is Chinese;
|
|
27
|
+
README.en.md is the English counterpart.
|
|
28
|
+
|
|
29
|
+
## Current slimming-pass evidence (supersedes historical ladder claims below)
|
|
30
|
+
|
|
31
|
+
This pass changes only plugin presentation, registration, and result projection; no vendored functionality changed. Historical tables below describe the earlier implementation and its earlier tests, not acceptance of the current revision.
|
|
32
|
+
|
|
33
|
+
- Restored three-layer intent: tools → ops_bash → PowerShell 7. `lib/session-shells.js` publishes bash through plugin-owned agent child fibers only with full-access authority and the subprocess service; it is independent of FastCtx availability and enableShellTools. Prompt routing prefers bash for general commands, not PowerShell fallback.
|
|
34
|
+
- Permission authority: host `packages/sandbox/sandbox-policy/src/index.ts:157-186`, `sandboxPolicy.resolve({session})`; durable mode event at `src/session-mode.ts:25-56`.
|
|
35
|
+
- Session mode feed: `ctx.on('session/event', (session, event))`, not client-wire projection change notifications. The plugin reconciles plugin-owned agent child fibers on `sandbox/mode`.
|
|
36
|
+
- Own registration scopes: `packages/core/tools/src/index.ts:1058-1088`; inherited restrictions and own-layer exemption at `:1090-1124,1163-1206`; prompt callbacks use assembly agent plus `ctx.get('tools').schemas(agent)`.
|
|
37
|
+
- Concurrency API: `packages/core/tools/src/index.ts:267-280,1303-1309` requires a pure function, not a boolean. FastCtx's read/search handlers use shared bounded permits and blocking executors (`vendor/fastctx/src/server.rs:198-280`); MCP request IDs isolate pending replies. Only read-only tools opt into sibling overlap.
|
|
38
|
+
- Host timeout metadata requires work quiescence; the current MCP transport removes pending waits but cannot prove server cancellation. No host `timeoutMs` promise is added.
|
|
39
|
+
- Job launch ID is parsed only from FastCtx's successful terminal marker (`vendor/fastctx/src/shell/jobs/mod.rs:232-278`); global durable lists at `:1053-1250` are projected to session-owned IDs. Footer grammar/placement comes from `vendor/fastctx/src/background_status.rs`.
|
|
40
|
+
- Baseline at pre-change plugin HEAD `dee3c57`, runtime FastCtx 0.2.6, is recorded in `docs/schema-baseline.json`. `scripts/measure-schemas.mjs` measures compact name/description/parameters JSON, not use frequency or actual billing.
|
|
41
|
+
- Per operator instruction: no regression tests or CI added, no existing suite updated or run. Syntax checks and schema-list measurements are not runtime acceptance. See `docs/manual-validation.md`.
|
|
42
|
+
- Residual boundaries: file tools remain outside the host filesystem sandbox; shell-mode gating does not confine `ops_replace`. Approval and sandbox state are separate; no new approval escalation is implemented. Reconnect loses job ownership and does not prove durable jobs terminated.
|
|
43
|
+
|
|
44
|
+
## Two bodies of work
|
|
45
|
+
|
|
46
|
+
| | Author | License | Where |
|
|
47
|
+
| --- | --- | --- | --- |
|
|
48
|
+
| The dsh-ops plugin | this repository | MIT | `lib/`, `bin/`, `scripts/`, `test/`, `cordis.patch.yml`, `dsh-plugin.json`, docs |
|
|
49
|
+
| FastCtx, vendored | [yc-duan](https://github.com/yc-duan) | Apache-2.0 | `vendor/fastctx/` |
|
|
50
|
+
|
|
51
|
+
`vendor/fastctx/UPSTREAM.md` records the exact upstream revision;
|
|
52
|
+
`vendor/fastctx/FORK.md` records every difference from it — the distribution
|
|
53
|
+
machinery removed, the standalone-only code removed, and every source file this
|
|
54
|
+
fork touches (each carries its own Apache-2.0 §4(b) notice at the top). The
|
|
55
|
+
composite licensing statement is in `NOTICE`.
|
|
56
|
+
|
|
57
|
+
## Distribution and binary sources
|
|
58
|
+
|
|
59
|
+
The plugin reaches a profile as a dependency of that profile — `dsh plugin
|
|
60
|
+
--profile <p> add dsh-ops`, or `plugin_manager { action: "install_bundle" }`. What
|
|
61
|
+
that install brings is this distribution's own work: one payload we build, and two
|
|
62
|
+
pins. The channels and the commands are described in the README under "What an
|
|
63
|
+
install brings".
|
|
64
|
+
|
|
65
|
+
### What this distribution ships
|
|
66
|
+
|
|
67
|
+
| Artifact | Contents |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `dsh-ops@0.2.0` | the plugin itself: JavaScript, the manifest, the bundle patch, and the vendored FastCtx source. 497,013 B packed, 2,339,108 B unpacked, 130 files |
|
|
70
|
+
| `@dsh-ops/fastctx-win32-x64@0.2.0` | **a payload we build**: this fork's FastCtx, `bin/fastctx.exe` |
|
|
71
|
+
| `@dsh-ops/bash-win32-x64@0.2.0` | **a pin only**: upstream URL, release, version, byte count, and SHA-256. No binaries |
|
|
72
|
+
| `@dsh-ops/pwsh-win32-x64@0.2.0` | **a pin only**: the same for PowerShell 7. No binaries |
|
|
73
|
+
| `SHA256SUMS` (release asset) | the digests of those four tarballs, attached to the tag's GitHub Release beside them |
|
|
74
|
+
|
|
75
|
+
### What this distribution does not ship
|
|
76
|
+
|
|
77
|
+
The Git for Windows and PowerShell archives are **not** redistributed here: no
|
|
78
|
+
tarball, no release asset, and no cache in this repository holds them.
|
|
79
|
+
`dsh-ops provision-shells` reads the pin, downloads the archive from the upstream
|
|
80
|
+
URL on the user's own machine, verifies it against the pinned SHA-256, and unpacks
|
|
81
|
+
it under `<DSH_HOME>/dsh-ops/shells/<name>/<version>/`. Those bytes travel from
|
|
82
|
+
upstream to that machine directly, and the licences below are the ones the user
|
|
83
|
+
obtains them under.
|
|
84
|
+
|
|
85
|
+
### The pins
|
|
86
|
+
|
|
87
|
+
| Package | Upstream source | Release | Version | Artifact | Bytes | SHA-256 | Licence |
|
|
88
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
89
|
+
| `@dsh-ops/fastctx-win32-x64` | this fork's build of `vendor/fastctx/`, upstream `yc-duan/fastctx` at `ccaa157790d02328a60786eb94ee5ad698995a5f` | `v0.2.6` | `0.2.6` | `bin/fastctx.exe`, built here | 49,492,480 | `7f4b494627cf3c0b298dd0b6d36624fd09d35bd1add655c5d55e0372e9e20987` | Apache-2.0 |
|
|
90
|
+
| `@dsh-ops/bash-win32-x64` | [Git for Windows](https://github.com/git-for-windows/git) | `v2.56.0.windows.2` | `2.56.0.2` | `PortableGit-2.56.0.2-64-bit.7z.exe` — fetched by the user, not redistributed here | 60,027,568 | `075e158ef8e1f0ab80b347e245405d3eca735c2dc88fd8e032e137d0ca61f61b` | GPL-2.0-only |
|
|
91
|
+
| `@dsh-ops/pwsh-win32-x64` | [PowerShell](https://github.com/PowerShell/PowerShell) | `v7.6.6` | `7.6.6` | `PowerShell-7.6.6-win-x64.zip` — fetched by the user, not redistributed here | 106,328,873 | `02fe458be20493fbdf43f61ea20610b811ee6c738ab1676c61b9cfcd1a33c860` | MIT |
|
|
92
|
+
|
|
93
|
+
The pin values live in one table — `SHELL_UPSTREAM_PINS` in `lib/shells.js`, which
|
|
94
|
+
`dsh-ops provision-shells` downloads from and the release build imports when it
|
|
95
|
+
publishes the pinned facts beside the packages it assembles — and the release's
|
|
96
|
+
`provenance.json` records what was built from them. The URLs are upstream's own
|
|
97
|
+
release URLs verbatim, so a pin can be checked against the upstream release page
|
|
98
|
+
rather than trusted from here. Each pin also records the digest of the executable
|
|
99
|
+
inside its archive (`bin/bash.exe`, `bin/pwsh.exe`), which the provisioner re-checks
|
|
100
|
+
after unpacking, so an archive that yields something else is refused. Unpacked, the
|
|
101
|
+
bash pin is about 390 MiB and the PowerShell pin about 245 MiB on disk — which is
|
|
102
|
+
why neither is part of an install.
|
|
103
|
+
|
|
104
|
+
Every `@dsh-ops/*` platform package is an optional dependency of the published
|
|
105
|
+
main package, and each one carries its own `LICENSE`, `NOTICE`, and metadata beside
|
|
106
|
+
that payload or pin.
|
|
107
|
+
|
|
108
|
+
### The licence each pin points at
|
|
109
|
+
|
|
110
|
+
The plugin's composite statement is `MIT AND Apache-2.0`, and it covers the plugin
|
|
111
|
+
alone. The two shell pins point at someone else's distribution:
|
|
112
|
+
|
|
113
|
+
- **`fastctx` — this fork's Apache-2.0, plus upstream's notice.** The payload is
|
|
114
|
+
built here from `vendor/fastctx/`, so it ships `vendor/fastctx/LICENSE-APACHE` and
|
|
115
|
+
`vendor/fastctx/NOTICE`, and every source file this fork touched already carries
|
|
116
|
+
its own §4(b) change notice (`vendor/fastctx/FORK.md` is the enumeration).
|
|
117
|
+
FastCtx's required attribution is reproduced verbatim in `NOTICE` and in both
|
|
118
|
+
READMEs; that text is reproduced, never edited.
|
|
119
|
+
- **`bash` — Git for Windows' GPL-2.0.** The pin names the upstream archive and its
|
|
120
|
+
digest; the download, the extraction, and the resulting copy on disk are the
|
|
121
|
+
user's, and the GPL-2.0 terms (and the corresponding source, published by the Git
|
|
122
|
+
for Windows and Git projects) attach to those bytes. No GPL-2.0-covered component
|
|
123
|
+
is part of this distribution, which is why nothing here carries a GPL text or
|
|
124
|
+
makes a source offer for code it does not ship.
|
|
125
|
+
- **`pwsh` — Microsoft's MIT.** The same shape: the pin points at Microsoft's own
|
|
126
|
+
release asset, the download happens on the user's machine, and the licence text
|
|
127
|
+
and copyright are Microsoft's. Nothing in the plugin's MIT grant covers it.
|
|
128
|
+
|
|
129
|
+
## Host targeting
|
|
130
|
+
|
|
131
|
+
The plugin targets DSH `0.2.0-rc.2`, the version of the desktop installation this
|
|
132
|
+
was developed against.
|
|
133
|
+
|
|
134
|
+
The source of that exact version is readable on this machine: `dsh-core/v0.2.0-rc.2/`
|
|
135
|
+
is a checkout of tag `dsh-v0.2.0-rc.2` (`639ed015`, commit date 2026-09-29),
|
|
136
|
+
14,104 files. The installed host itself ships inside `app.asar` and stays
|
|
137
|
+
unreadable from a shell, so the readable copy is what "the host" means below.
|
|
138
|
+
|
|
139
|
+
Each depended-on API is therefore established twice: **read** from that source,
|
|
140
|
+
with the file and line recorded here, and **run** by the suite named in the same
|
|
141
|
+
row. The line numbers are of `dsh-core/v0.2.0-rc.2/` and will move with upstream;
|
|
142
|
+
the file is the durable part.
|
|
143
|
+
|
|
144
|
+
| Depended on | Read from | Run by |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| `new Context()`, `ctx.plugin`, `ctx.inject`, `ctx.effect`, `ctx.on`, `ctx.waterfall`, `ctx.get` | Cordis source, vendored: `vendor/cordis/src/` | the mount suite boots a real context and disposes it |
|
|
147
|
+
| `tools.register(definition)` — one object argument, returns the unregister disposer | `packages/core/tools/src/index.ts:1057-1088` | the mount suite reads back the nine registered schemas, and the shells suite asserts the `ops_bash` definition and that its disposer removes it |
|
|
148
|
+
| `tools.restrict(filter)` — `{allow?, deny?}` over the global tools of one agent scope; requires a scoped context, rejects unknown names, intersects, lifts on dispose | `packages/core/tools/src/index.ts:1090-1124`, type at `:700-705` | **used by this plugin**: on `agent/created`, and only while a rung of its own is live, it masks the configured host shell tools out of that agent's own view (`lib/index.js` `hideHostShellTools`), and the disposer it returns is handed to the plugin's effect too. The mount suite asserts the masked names leave `schemas(agent)`, stop resolving through `get(name, agent)`, are still refused by the fence, stay registered globally, and come back when the plugin unloads; a name the agent registered itself makes `restrict()` throw, which the plugin reports once and leaves visible |
|
|
149
|
+
| `tools.guard(guard)` — monotonic denial registered after the pre-execute waterfall; the guard returns a denial reason or `undefined` | `packages/core/tools/src/index.ts:1126-1142`, type at `:723-731` | not used by this plugin yet: the fence at `tools/pre-execute` — with its model-readable reason and its `next()` delegation — plus the per-agent mask already implement `deny-host-shell` |
|
|
150
|
+
| `tools.get(name, scope?)` — the definition one scope resolves, or undefined; the lookup half of "visibility, lookup, and execution agree" | `packages/core/tools/src/index.ts:1230` | the mount suite only: a masked host shell name must not resolve for that agent while it stays registered globally |
|
|
151
|
+
| `ctx.on('agent/created')` and the agent's own scoped context (`agent.ctx`) — the event fires once per agent, after its scope is published and before its queued work is released and its prompt first assembled; the payload is `{agent, source, signal}` | `packages/core/agent/src/runtime-types.ts:261` (the typed event), sequenced at `packages/core/agent/src/index.ts:172-181` and emitted serially at `:550` | the mount suite dispatches the real event (`ctx.serial('agent/created', …)`) into real `@deepseek-ai/dsh-scope` scopes and asserts all three outcomes: hidden while a rung of the plugin's own is live, reported-and-visible when no such rung is live, and reported-and-visible when `restrict()` cannot name the tool |
|
|
152
|
+
| `tools/pre-execute` waterfall, `(exec, next) => PreToolDecision`, `@mode waterfall` | `packages/core/tools/src/index.ts:142-153` | the fence suite dispatches the real waterfall and asserts `{kind:'deny'}` |
|
|
153
|
+
| `systemPrompt.section({name, order, text, interpolate})`, duplicate names throw, returns an effect disposer | `packages/core/system-prompt/src/index.ts:446-463`, `PromptSection` at `:52-76` | the mount suite renders both sections from a real assembly |
|
|
154
|
+
| `systemPrompt.assemble({})` resolves sections in ascending order, equal orders by name | `packages/core/system-prompt/src/index.ts:548-558`, sort at `:237-238` | the mount suite asserts both sections appear in one assembly |
|
|
155
|
+
| `ctx.get('tools').schemas()` — one deep-cloned schema per visible tool, the global view when no scope is passed; the plugin's only answer to "are the `ops_` tools live right now" (asked at every prompt assembly) and to "which host shell names does this agent actually see" (asked when it hides them) | `packages/core/tools/src/index.ts:1252-1262` | the mount suite renders the tooling section from a real assembly and asserts it names exactly the live tools; it also reads `schemas(agent)` before and after a mask |
|
|
156
|
+
| `ctx.get('subprocess')` — the abstract `SubprocessRuntime`: `spawn(spec)` with a fully explicit `argv`, `cwd`, per-stream stdio, `graceMs`, cancellation, and `env` overrides merged after the service's own credential scrub; `argv` is never shell-interpreted | `packages/subprocess/subprocess/README.md:28` (mount and consume), `:44-46` (the call shape), `docs/subsystems/subprocess.md:91-104` (the fully-explicit spec), `:267-269` (`ctx.subprocess` registration) | the shells suite, through a recording subprocess service: `ops_bash` spawns `argv: [<resolved bash>, '-c', <command>]`, forwards exactly the terminal overrides, adds no credential-shaped name of its own, and reports a missing service instead of throwing something opaque. The mount suite is where the service is real, and where the registration is shown to be gated on it: with no `ctx.subprocess`, a resolved bash publishes no rung and installs no mask |
|
|
157
|
+
| the credential rule both spawn paths keep: a child never inherits credential-shaped names or ambient `DSH_*` facts, and a transport that owns its own spawn (the SDK client, MCP) stays outside the service so the policy has one source | `packages/subprocess/subprocess/README.md:80` (the scrub and its merges), `:84` (route around the service when the transport owns the spawn), `:104` (`scrubbedParentEnv`), `packages/subprocess/subprocess-local/README.md:156` (the scrub is a name heuristic, not a guarantee) | the mount suite's `childEnv` check, for the MCP child. That child's rule is restated in `lib/tools.js` `childEnv()` rather than imported, because importing `scrubbedParentEnv` would be the `@deepseek-ai/*` value import this host half forbids; the `ops_bash` child gets the service's own scrub instead (`ctx.get('subprocess')` above) |
|
|
158
|
+
| this plugin's own `ops_` namespace is its conflict domain: a name already registered in the same scope throws, so the whole generation is rolled back rather than half-published | `packages/core/tools/src/index.ts:746-748` (both duplicate branches) | not covered against the real host — no suite publishes a colliding name; the fixture's registry raises on a duplicate (`test/mount.test.mjs`) and `#publish` in `lib/tools.js` is where the rollback lives |
|
|
159
|
+
| a bundle is a package declaring `dsh.bundle.patch`, and a layer replaces a row's whole `config`: | `docs/user/develop/basic/publish.md:11-16`, `:56-64`, `:129-132` | `scripts/validate-manifest.mjs` parses the shipped patch and runs the row's config through the plugin's own validator |
|
|
160
|
+
| Cordis answers every service property read with a fresh traceable wrapper | `vendor/cordis/src/utils.ts:116-125` (`getTraceable`) and `:165-218` (`createTraceable`, a new `Proxy` per read) | the incident's regression assertions in the mount suite: the registry carries no own `register` after mount, and the same foreign name registered once in each of two scopes stays out of this plugin's global view |
|
|
161
|
+
| `ctx.pluginManager` (`installBundle`, `removeBundle`, `setBundleEnabled`, `setPluginEnabled`, `inspect`, `registries`, `waitForInstall`, `cancelInstall`, version exemptions) | `docs/subsystems/boot.md:85-180` | not run here; the install path is exercised by hand, not by a suite |
|
|
162
|
+
|
|
163
|
+
One row left this table when the plugin began speaking MCP itself:
|
|
164
|
+
`@deepseek-ai/dsh-mcp-client` and its `mcp__<serverName>__<rawName>` naming
|
|
165
|
+
contract are not part of this plugin's dependency surface — nothing in `lib/`
|
|
166
|
+
imports it, mounts it, or names anything after the names it would have produced.
|
|
167
|
+
The `0.2.0` changelog entry records its removal from `package.json`, together with
|
|
168
|
+
the declaration and the manifest check that went with it.
|
|
169
|
+
|
|
170
|
+
The package versions under test are pinned in `devDependencies`, so the gate is
|
|
171
|
+
reproducible. Because those are npm releases of the same packages rather than the
|
|
172
|
+
copies inside `app.asar`, a host-side API change between `0.2.0-rc.2` and what
|
|
173
|
+
this repository installs surfaces as a red mount suite.
|
|
174
|
+
|
|
175
|
+
Two things the read established that the earlier execution-only record could not:
|
|
176
|
+
|
|
177
|
+
- **`tools.register` takes one object, not `(name, definition)`.** The earlier
|
|
178
|
+
record said "the ToolRuntime `register`", which left the argument form to the
|
|
179
|
+
suite. The signature is `register(definition: ToolDefinition): () => void`
|
|
180
|
+
(`packages/core/tools/src/index.ts:1063`), and it validates `output.schema`,
|
|
181
|
+
`output.render`, and `timeoutMs` before registering.
|
|
182
|
+
- **`restrict` and `guard` exist, with exactly the monotonic semantics this
|
|
183
|
+
plugin's design assumes.** `restrict` narrows what one agent inherits
|
|
184
|
+
(`:1097`), `guard` denies after the waterfall and cannot be turned back into
|
|
185
|
+
permission by a later listener (`:1136`, `:723-731`). The plugin uses `restrict`
|
|
186
|
+
for the visibility half of `deny-host-shell` and is runtime-verified for it; it
|
|
187
|
+
registers no `guard` yet, so that half is read-only here.
|
|
188
|
+
|
|
189
|
+
### The boundary the 2026-10-08 incident established
|
|
190
|
+
|
|
191
|
+
**What happened.** While the plugin still mounted the harness MCP bridge, it
|
|
192
|
+
renamed that bridge's tools by installing an interceptor on the shared tool
|
|
193
|
+
registry — `interceptBridgeRegistrations` wrote an own `register` property on the
|
|
194
|
+
service object. A Cordis service property read returns a wrapper **bound to the
|
|
195
|
+
reading context** (`vendor/cordis/src/utils.ts:165-218`), so the interceptor's
|
|
196
|
+
`original.call(…)` attributed every later registration — including foreign ones —
|
|
197
|
+
to *this* plugin's host plane. The host's second registration of a name that
|
|
198
|
+
another plugin installs per agent (`subagent`) then collided and **every new
|
|
199
|
+
session failed**: `tool "subagent" is already registered (for a per-agent variant,
|
|
200
|
+
register through that agent's `agent.ctx` instead)`
|
|
201
|
+
(`packages/core/tools/src/index.ts:746-747`, the `scope === undefined` branch,
|
|
202
|
+
i.e. the global layer). A fresh `DSH_HOME` without this plugin did not fail, which
|
|
203
|
+
is what located the fault here rather than in the composition.
|
|
204
|
+
|
|
205
|
+
**What stands now.** The plugin publishes its own tools through its own
|
|
206
|
+
`ctx.tools.register` (`lib/tools.js` for the hosted nine, `lib/shells.js` for
|
|
207
|
+
`ops_bash`) and rewrites nothing: no own property on the shared registry, no
|
|
208
|
+
replaced service method, no name derived from another component's naming. The one
|
|
209
|
+
place it acts on another context's tools is `agent.ctx.tools.restrict({ deny })` —
|
|
210
|
+
the documented per-agent visibility call, made on that agent's own scoped registry
|
|
211
|
+
to hide the host shell tools this deployment asked to deny. The symptoms, the
|
|
212
|
+
mechanism, and the regression contract are stated in the plugin's `AGENTS.md`,
|
|
213
|
+
"上游兼容 (upstream compatibility)"; that section is the authority for the
|
|
214
|
+
boundary, and `test/mount.test.mjs` is the executable form of it.
|
|
215
|
+
|
|
216
|
+
Rules that follow, and that this repository must not regress:
|
|
217
|
+
|
|
218
|
+
- **Do not replace a shared service method to observe or reshape it.** Reading a
|
|
219
|
+
service method is not identity-stable, and the wrapper carries the caller's
|
|
220
|
+
context, so re-entering through a saved `original` changes whose registration it
|
|
221
|
+
is.
|
|
222
|
+
- **The public namespace is this plugin's own registrations.** `ops_<name>` comes
|
|
223
|
+
from `publicToolName()` in `lib/policy.js` and is registered into this plugin's
|
|
224
|
+
scope, so no other component's naming can change or claim it.
|
|
225
|
+
- **Release only through the disposers the surface collected.**
|
|
226
|
+
`ctx.get('tools').register` is not identity-stable across two reads
|
|
227
|
+
(`ctx.get('tools').register !== ctx.get('tools').register`), so a registration
|
|
228
|
+
cannot be recognized by function identity; one generation is published and
|
|
229
|
+
released as one unit.
|
|
230
|
+
- **Foreign registrations are none of this plugin's business.** A name in another
|
|
231
|
+
server's namespace, a lookalike of this plugin's own, or the same name in two
|
|
232
|
+
scopes must survive a mount, an unload, and each other — `test/mount.test.mjs`
|
|
233
|
+
is what keeps that true.
|
|
234
|
+
- **Act on another context's tools only through its documented API, on that
|
|
235
|
+
context.** The per-agent mask is `agent.ctx.tools.restrict({ deny })`, called on
|
|
236
|
+
the agent's own scoped registry and released through the plugin's effect as well;
|
|
237
|
+
the plugin never reaches into a registry instance and never names a tool that
|
|
238
|
+
agent registered itself.
|
|
239
|
+
|
|
240
|
+
### Not verified here
|
|
241
|
+
|
|
242
|
+
- **A real `dsh plugin add` into a live profile.** The profile that booted the
|
|
243
|
+
session which produced this record is the one that installed the plugin by
|
|
244
|
+
`link:`, so the install did happen — but through a manual profile edit, not
|
|
245
|
+
through `dsh plugin`. The *installation path* (`dsh plugin --profile … add`, the
|
|
246
|
+
profile's `dsh.profile.bundles` ordering, the host compatibility gate reading
|
|
247
|
+
`peerDependencies`) is documented from the harness sources above, not exercised
|
|
248
|
+
by a suite in this repository.
|
|
249
|
+
- **Hot reload of a replaced package.** `ctx.hmr` is read
|
|
250
|
+
(`docs/subsystems/boot.md:56-83`) and the upstream rule that replacing an
|
|
251
|
+
installed package needs a restart is quoted in the README
|
|
252
|
+
(`packages/preset/agent-preset/skills/cordis-plugin-development/references/host-plugin.md:60`),
|
|
253
|
+
but no suite here replaces a mounted package and observes the new module
|
|
254
|
+
generation.
|
|
255
|
+
- **A conflicting registration against the real host.** `lib/tools.js` publishes
|
|
256
|
+
one whole generation and rolls a partial one back when a name is already taken,
|
|
257
|
+
but no suite publishes a name that collides with this plugin's `ops_` namespace
|
|
258
|
+
against the real registry: only the fixture's own registry in
|
|
259
|
+
`test/mount.test.mjs` raises on a duplicate.
|
|
260
|
+
- **The platform packages and the pins, against a real tag.** The release step
|
|
261
|
+
declares `@dsh-ops/fastctx-<platform>-<arch>` and the two pins, and the shell
|
|
262
|
+
layouts they can resolve to are exercised against a temporary install in
|
|
263
|
+
`test/shells.test.mjs` — including the L3 expression agreeing with the plugin's own
|
|
264
|
+
probe on every layout. What has never run is the release pipeline itself: no tag has
|
|
265
|
+
been pushed and nothing has been published, so no tarball and no Release asset has
|
|
266
|
+
been fetched from a release, and no deployment has yet resolved a runtime from one.
|
|
267
|
+
The first real tag is what has to prove that path end to end.
|
|
268
|
+
- **A `provision-shells` download against upstream's live assets.** The pin values
|
|
269
|
+
are read from `SHELL_UPSTREAM_PINS` in `lib/shells.js`, and the digests there were
|
|
270
|
+
checked against the archives this machine downloaded while the release build was
|
|
271
|
+
assembled — but that was a build-time fetch here, not `dsh-ops provision-shells`
|
|
272
|
+
fetching into `<DSH_HOME>/dsh-ops/shells/` on a user's machine, and not a
|
|
273
|
+
re-download of the pinned bytes after upstream had served them again. Whether
|
|
274
|
+
upstream still serves the pinned URL, and whether the extracted tree is the one the
|
|
275
|
+
plugin's probe accepts, is what the first run of that command has to establish.
|
|
276
|
+
- **A real DSH boot running the L3 override.** The patch's `!!js` expression is
|
|
277
|
+
parsed from `cordis.patch.yml` and evaluated the way the vendored loader does it
|
|
278
|
+
(`with (ctx) { return eval(expr) }`, `vendor/loader/src/config/utils.ts:5-9`) —
|
|
279
|
+
inert without a bundled pwsh, resolving one with it — but no DSH boot has mounted
|
|
280
|
+
the patched `pwsh-sandbox` row.
|
|
281
|
+
|
|
282
|
+
## Design decisions worth recording
|
|
283
|
+
|
|
284
|
+
### Two spawn paths, one credential policy
|
|
285
|
+
|
|
286
|
+
The plugin reaches every host service through `ctx.inject([...])` / `ctx.get()` and
|
|
287
|
+
imports no `@deepseek-ai/*` value at any point. That leaves two ways to start a
|
|
288
|
+
process, and the credential rule is kept on both.
|
|
289
|
+
|
|
290
|
+
**The FastCtx server is the plugin's own spawn** (`lib/handshake.js`,
|
|
291
|
+
`node:child_process.spawn`), because it is the plugin's transport, and the harness's
|
|
292
|
+
own guidance for that case is to stay outside the subprocess service — "when a
|
|
293
|
+
transport owns its own spawn (the SDK client, MCP), route around the service and
|
|
294
|
+
import `scrubbedParentEnv` directly so environment policy stays single-sourced"
|
|
295
|
+
(`packages/subprocess/subprocess/README.md:84`). Importing `scrubbedParentEnv` is
|
|
296
|
+
exactly the value import this host half forbids, so the rule is restated in
|
|
297
|
+
`lib/tools.js` `childEnv()`: credential-shaped names (`*KEY*` / `*PASSWORD*` /
|
|
298
|
+
`*SECRET*` / `*TOKEN*`) and ambient `DSH_*` facts are dropped, tool configuration
|
|
299
|
+
locations such as `GH_CONFIG_DIR` survive, and the mount suite pins that behaviour.
|
|
300
|
+
The heuristic has the limits upstream documents
|
|
301
|
+
(`packages/subprocess/subprocess-local/README.md:156`): a differently named secret
|
|
302
|
+
passes through.
|
|
303
|
+
|
|
304
|
+
**`ops_bash` goes through the service** (`lib/shells.js`). It has no reason to own a
|
|
305
|
+
spawn, so it takes the harness's real credential scrub, output spilling, and
|
|
306
|
+
process-range termination, and supplies only the argv, the directory, the budgets,
|
|
307
|
+
the terminal overrides, and its cancellation — read with `ctx.get('subprocess')`
|
|
308
|
+
rather than imported. Nothing in the plugin writes `process.env`, `PATH`, or the
|
|
309
|
+
working directory.
|
|
310
|
+
|
|
311
|
+
### One question decides the ladder and the mask
|
|
312
|
+
|
|
313
|
+
`ladderLevels({published, pwsh})` (`lib/policy.js`, a pure function) turns the
|
|
314
|
+
published `ops_` names plus one resolution fact into the three rungs: rung 1 is live
|
|
315
|
+
when any published name is not one of this plugin's own in-process tools — so the
|
|
316
|
+
shell tools cannot stand in for a server that is not there — rung 2 when `ops_bash`
|
|
317
|
+
is among them, and rung 3 when this plugin has a pwsh of its own on this platform.
|
|
318
|
+
`hostShellEnforcement({shellPolicy, levels})` then asks the same object whether
|
|
319
|
+
`deny-host-shell` may hide the host shell tools, and it is asked again as each agent
|
|
320
|
+
appears. Both answers therefore follow "what the model can reach"; `resolveShells()`
|
|
321
|
+
supplies the detail and the report and never the decision. The `tools/pre-execute`
|
|
322
|
+
fence is installed by the mode alone, so the guarantee does not depend on either
|
|
323
|
+
answer.
|
|
324
|
+
|
|
325
|
+
### Rung 3 is a bundle patch; rung 2 is a tool
|
|
326
|
+
|
|
327
|
+
The two bundled rungs are implemented differently because the host leaves two
|
|
328
|
+
different seams open, and both were read from the host source rather than assumed:
|
|
329
|
+
|
|
330
|
+
- **Rung 2 must be a tool.** `bash-local` has no executable field — its config is
|
|
331
|
+
`cwd`/`timeoutMs`/`maxTimeoutMs`/`maxOutputBytes`/`maxSpillBytes`/`graceMs`
|
|
332
|
+
(`packages/shell/bash-local/src/index.ts`) — so the only zero-internal-dependency
|
|
333
|
+
way to run the plugin's own bash is to publish `ops_bash` and execute through
|
|
334
|
+
`ctx.subprocess`. The rung is therefore the published tool, not the resolved
|
|
335
|
+
executable: a bash that resolved without that service is a rung the model cannot
|
|
336
|
+
reach, so `lib/shells.js` publishes nothing, names the resolved bash in its report,
|
|
337
|
+
and the ladder has no rung 2.
|
|
338
|
+
- **Rung 3 must be a patch.** `pwsh-sandbox` inherits `pwshPath` verbatim from
|
|
339
|
+
`pwsh-local` (`packages/shell/pwsh-sandbox/src/index.ts:40`, `type Config =
|
|
340
|
+
LocalConfig`), so pointing that row at the plugin's executable puts the bundled
|
|
341
|
+
pwsh 7 under the host's sandbox, credential scrub, and output governance with no
|
|
342
|
+
custom executor and no patched internals. A bundle patch is evaluated before any
|
|
343
|
+
`dsh-ops` row is mounted, which is also why there is no config key for it: a key
|
|
344
|
+
could never reach the expression, and a key that pretended to would make the
|
|
345
|
+
ladder claim a rung the host row never runs. A patch replaces the target row's
|
|
346
|
+
entire `config`, so every non-default field must be restated — the target row
|
|
347
|
+
declares none (`packages/bundle/base/cordis.patch.yml:241-243`), and the shells
|
|
348
|
+
suite asserts that the shipped patch writes `pwshPath` and nothing else.
|
|
349
|
+
**The expression and the probe are single-sourced by an order, not by an import**
|
|
350
|
+
(a bundle patch is plain YAML and cannot import `lib/shells.js`): both accept
|
|
351
|
+
`PWSH_LAYOUT_ORDER` — the provisioned copy under
|
|
352
|
+
`<DSH_HOME>/dsh-ops/shells/pwsh/<version>/` first, then an installed
|
|
353
|
+
`@dsh-ops/pwsh-<platform>-<arch>` package's `bin/pwsh.exe`, then
|
|
354
|
+
`vendor/pwsh/<platform>-<arch>/pwsh.exe` inside this package — apply the same
|
|
355
|
+
`lstat`-based usability test, and the shells suite asserts that agreement on every
|
|
356
|
+
layout, so the ladder's rung 3 and the host row are one fact rather than two
|
|
357
|
+
guesses.
|
|
358
|
+
|
|
359
|
+
### No Schemastery `Config`
|
|
360
|
+
|
|
361
|
+
The plugin exports no `Config` schema, so the row's config is validated by
|
|
362
|
+
`lib/config.js` instead. A `Config` export means importing
|
|
363
|
+
`@deepseek-ai/schemastery` — a value import whose resolution the profile loader
|
|
364
|
+
owns — and this host half deliberately imports no `@deepseek-ai/*` value at load
|
|
365
|
+
time. There is no exception now: every host service this plugin needs arrives as
|
|
366
|
+
a service, never as an import.
|
|
367
|
+
|
|
368
|
+
### Resolution and probing are synchronous
|
|
369
|
+
|
|
370
|
+
`apply` resolves the executable and probes it with `--version` before returning.
|
|
371
|
+
Both are synchronous, so they belong in `apply` rather than behind a service
|
|
372
|
+
injection: `required: true` must reject *activation*, which is only observable if
|
|
373
|
+
the failure happens while the loader is still loading the plugin.
|
|
374
|
+
|
|
375
|
+
### Prompt section orders 1500 and 3250
|
|
376
|
+
|
|
377
|
+
The harness allocates named section orders centrally, and an external
|
|
378
|
+
contribution supplies its own numbers. Reading the table
|
|
379
|
+
(`packages/core/system-prompt/src/index.ts:125-159`):
|
|
380
|
+
|
|
381
|
+
- **1500 is already claimed** — it is `TOOL_GREP`. The host-shell rule therefore
|
|
382
|
+
shares an order with a first-party section instead of sitting in a gap. Equal
|
|
383
|
+
orders are broken by name, code-unit order (`:231-238`), so
|
|
384
|
+
`dsh-ops:host-shell-policy` sorts before `tool-grep`; the rule still lands after
|
|
385
|
+
`TOOL_BASH` (1000) and before `MCP_SERVERS` (3100), which is the position the
|
|
386
|
+
policy text is written for. `lib/policy.js` states the same allocation in place,
|
|
387
|
+
and `scripts/validate-manifest.mjs` compares both numbers against the manifest.
|
|
388
|
+
- **3250 sits in a real gap** — between `MCP_SERVERS` (3100) and `TOOLS_SDK`
|
|
389
|
+
(5000), which is where the tooling section is meant to sit.
|
|
390
|
+
|
|
391
|
+
### `ctx.get('tools')`, never `ctx.tools`, from the prompt scope
|
|
392
|
+
|
|
393
|
+
The tooling section's text function asks the registry whether the FastCtx tools
|
|
394
|
+
exist, and `lib/shells.js` asks it where to register `ops_bash`. Both run from
|
|
395
|
+
injection scopes that do not declare `tools`; the `ctx.tools` property proxy is
|
|
396
|
+
topology-sensitive and answered `undefined` there. The mount suite caught this — the
|
|
397
|
+
section rendered empty next to nine registered tools.
|
|
398
|
+
|
|
399
|
+
## Verification record
|
|
400
|
+
|
|
401
|
+
On Windows x64 with Node 24.18.0, at the commit that carries this record. The
|
|
402
|
+
totals are the output of that run; what has to keep holding is what the suites
|
|
403
|
+
assert, not the count:
|
|
404
|
+
|
|
405
|
+
```console
|
|
406
|
+
node scripts/validate-manifest.mjs # all 47 checks passed
|
|
407
|
+
node test/run.mjs # 7 suites, 122 checks, passed in 9s
|
|
408
|
+
node bin/dsh-ops.mjs status # fastctx 0.2.6 from the managed runtime, 9 tools
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
The vendored source build:
|
|
412
|
+
|
|
413
|
+
```console
|
|
414
|
+
cargo 1.97.1 (c980f4866 2026-06-30)
|
|
415
|
+
cargo build --release --locked # Finished `release` profile in 7m 05s
|
|
416
|
+
vendor/fastctx/target/release/fastctx.exe --version # fastctx 0.2.6
|
|
417
|
+
```
|
package/README.en.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# dsh-ops
|
|
2
|
+
|
|
3
|
+
Reduce tokens, time and model attention wasted on repeated PowerShell errors in
|
|
4
|
+
Windows DSH. The bundle supplies independent **bash 5.3.15**, **PowerShell 7.6.6**,
|
|
5
|
+
and high-performance Rust repository tools powered by FastCtx.
|
|
6
|
+
|
|
7
|
+
Routing: **file tools → bash → PowerShell 7**. Prefer bash for general commands;
|
|
8
|
+
use pwsh only for necessary Windows-native operations. Fix bash errors in bash.
|
|
9
|
+
Windows x64 only; target DSH `0.2.0-rc.2`. [中文](README.md)
|
|
10
|
+
|
|
11
|
+
## Install, update and remove
|
|
12
|
+
|
|
13
|
+
In the official marketplace, enter **`dsh-ops`**, install, then enable. This installs
|
|
14
|
+
into the running application's profile. All three Windows runtime packages are
|
|
15
|
+
installed as dependencies: no postinstall downloads and no system PATH changes.
|
|
16
|
+
|
|
17
|
+
```console
|
|
18
|
+
npx --yes dsh-ops@latest install --profile desktop
|
|
19
|
+
npx --yes dsh-ops@latest install --profile web
|
|
20
|
+
npx --yes dsh-ops@latest install --profile tui
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
An explicit profile is required. `tui` maps to the dsh-TUI product's **dsh-tui**
|
|
24
|
+
profile, not the old tui directory. Initialize the target application first.
|
|
25
|
+
The wrapper delegates locking, compatibility and rollback to the official DSH CLI.
|
|
26
|
+
|
|
27
|
+
Desktop uses its own `resources/runtime/cli/bin/dsh.cmd`, auto-detected under
|
|
28
|
+
`%LOCALAPPDATA%/Programs/DeepSeek Harness`. For another installation, pass
|
|
29
|
+
`--dsh-cli "<installation>/resources/runtime/cli/bin/dsh.cmd"`. Ordinary DSH CLI
|
|
30
|
+
cannot manage the reserved desktop profile. Web/TUI require an available DSH CLI;
|
|
31
|
+
`--dsh-cli` can also specify that executable explicitly.
|
|
32
|
+
|
|
33
|
+
Equivalent official commands:
|
|
34
|
+
|
|
35
|
+
```console
|
|
36
|
+
dsh plugin --profile web add dsh-ops
|
|
37
|
+
dsh plugin --profile dsh-tui add dsh-ops
|
|
38
|
+
"<desktop installation>/resources/runtime/cli/bin/dsh.cmd" plugin --profile desktop add dsh-ops
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Update by repeating the npx install command, or official `add dsh-ops@latest`.
|
|
42
|
+
Reload/restart as the application requests; replacing loaded code may require restart.
|
|
43
|
+
|
|
44
|
+
```console
|
|
45
|
+
npx --yes dsh-ops@latest status --profile desktop
|
|
46
|
+
npx --yes dsh-ops@latest uninstall --profile desktop
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Use web/tui as appropriate. Marketplace uninstall and official `remove dsh-ops`
|
|
50
|
+
also remove profile dependency references. Runtime files are package dependencies,
|
|
51
|
+
not a new shared provisioned directory. System shells, other profiles and package
|
|
52
|
+
manager caches are never manually deleted. Clean legacy provisioned files separately
|
|
53
|
+
with `npx --yes dsh-ops@latest uninstall --yes`; `~/.fastctx/` is kept unless
|
|
54
|
+
`--purge-fastctx` is explicit. Unload/downgrade does not prove durable jobs stopped.
|
|
55
|
+
|
|
56
|
+
## Configuration and tools
|
|
57
|
+
|
|
58
|
+
Edit the dsh-ops row's `config` in the DSH configuration editor. Unknown keys fail.
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
config:
|
|
62
|
+
enableShellTools: true
|
|
63
|
+
publishBashTool: true
|
|
64
|
+
promptPolicy: true
|
|
65
|
+
toolCallTimeoutMs: 300000
|
|
66
|
+
required: false
|
|
67
|
+
shellPolicy: advise
|
|
68
|
+
allowSystemShellFallback: true
|
|
69
|
+
# binaryPath: 'C:\tools\fastctx.exe'
|
|
70
|
+
# bashPath: 'C:\tools\bash.exe'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`enableShellTools` controls the FastCtx command/job group; `publishBashTool` controls
|
|
74
|
+
bash. Both additionally require authoritative session `danger-full-access`; bash
|
|
75
|
+
also requires the host subprocess service. `promptPolicy` controls compact routing;
|
|
76
|
+
`extraGuidance` appends text. RPC wait timeout does not prove server termination.
|
|
77
|
+
`required` fails activation on unavailable runtime. Explicit binary/bash paths are
|
|
78
|
+
authoritative. `shellPolicy: deny-host-shell` blocks `deniedHostTools` (default
|
|
79
|
+
`[pwsh, bash, pwsh_persistent]`), including the third-layer pwsh; use cautiously.
|
|
80
|
+
|
|
81
|
+
| Tools | Purpose | Full access |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| ops_inspect_local_file | Batch text ranges, encoding, PDF text and hex | No command gate¹ |
|
|
84
|
+
| ops_grep | Rust regex, file filters, counts/summaries | No command gate¹ |
|
|
85
|
+
| ops_glob | Multiple path patterns and exclusions | No command gate¹ |
|
|
86
|
+
| ops_replace | Mechanical cross-file replacement; host edit for precise edits | No command gate¹ |
|
|
87
|
+
| ops_bash | Preferred general bash executor | Required |
|
|
88
|
+
| ops_run | Bounded bash result | Required |
|
|
89
|
+
| ops_run_background | Start background jobs | Required |
|
|
90
|
+
| ops_job_output / ops_job_list / ops_job_kill | Operate on this session's owned jobs | Required |
|
|
91
|
+
| Host pwsh | Bundled PowerShell 7 Windows-native operations | Host policy |
|
|
92
|
+
|
|
93
|
+
¹ File tools are **not host filesystem-confined**, including ops_replace. Do not
|
|
94
|
+
use this bundle as a security boundary for untrusted restricted deployments.
|
|
95
|
+
Images belong to host read_image. Permissions are reconciled dynamically and
|
|
96
|
+
rechecked on execution; job ownership is per session and connection, lost on reconnect.
|
|
97
|
+
See [manual validation](docs/manual-validation.md) and [schema measurements](docs/schema-measurement.md).
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
Plugin source: **MIT AND Apache-2.0**. Outside vendor/fastctx: MIT; vendored FastCtx:
|
|
102
|
+
Apache-2.0. See [NOTICE](NOTICE), vendor/fastctx/LICENSE-APACHE and vendor/fastctx/NOTICE.
|
|
103
|
+
The independent Windows payload packages retain upstream component licenses:
|
|
104
|
+
Git for Windows includes GPL components; PowerShell includes MIT and third-party
|
|
105
|
+
components. The plugin's MIT does not relicense them. See [PROVENANCE](PROVENANCE.md).
|
|
106
|
+
|
|
107
|
+
## Acknowledgements
|
|
108
|
+
|
|
109
|
+
The Rust tools use source from yc-duan's FastCtx Codex plugin. Distribution changes
|
|
110
|
+
are recorded in vendor/fastctx/FORK.md and vendor/fastctx/UPSTREAM.md. Required notice:
|
|
111
|
+
|
|
112
|
+
> This product includes FastCtx
|
|
113
|
+
> (https://github.com/yc-duan/fastctx), Copyright (c) 2026 yc-duan,
|
|
114
|
+
> used under the Apache License 2.0.
|
|
115
|
+
>
|
|
116
|
+
> FastCtx is redistributed and/or modified here by the maintainer of
|
|
117
|
+
> this distribution. Any such change is that maintainer's own work
|
|
118
|
+
> and their sole responsibility. It is not endorsed by, not
|
|
119
|
+
> supported by, and not attributable to the author of FastCtx, who
|
|
120
|
+
> accepts no liability of any kind arising from this distribution or
|
|
121
|
+
> from anything built on top of it.
|