host-execution-mcp 1.0.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/CHANGELOG.md +330 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/SECURITY.md +72 -0
- package/dist/access/docker-runtime.d.ts +38 -0
- package/dist/access/docker-runtime.js +61 -0
- package/dist/access/docker-runtime.js.map +1 -0
- package/dist/access/index.d.ts +110 -0
- package/dist/access/index.js +174 -0
- package/dist/access/index.js.map +1 -0
- package/dist/access/paradi-auth.d.ts +33 -0
- package/dist/access/paradi-auth.js +89 -0
- package/dist/access/paradi-auth.js.map +1 -0
- package/dist/access/paradi-transport.d.ts +10 -0
- package/dist/access/paradi-transport.js +39 -0
- package/dist/access/paradi-transport.js.map +1 -0
- package/dist/access/paradi-username.d.ts +5 -0
- package/dist/access/paradi-username.js +14 -0
- package/dist/access/paradi-username.js.map +1 -0
- package/dist/access/probe.d.ts +14 -0
- package/dist/access/probe.js +63 -0
- package/dist/access/probe.js.map +1 -0
- package/dist/access/session-pool.d.ts +25 -0
- package/dist/access/session-pool.js +46 -0
- package/dist/access/session-pool.js.map +1 -0
- package/dist/access/ssh-transport.d.ts +16 -0
- package/dist/access/ssh-transport.js +74 -0
- package/dist/access/ssh-transport.js.map +1 -0
- package/dist/access/types.d.ts +43 -0
- package/dist/access/types.js +21 -0
- package/dist/access/types.js.map +1 -0
- package/dist/config.d.ts +52 -0
- package/dist/config.js +179 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +4 -0
- package/dist/instructions.js +93 -0
- package/dist/instructions.js.map +1 -0
- package/dist/kubectl/runner.d.ts +32 -0
- package/dist/kubectl/runner.js +79 -0
- package/dist/kubectl/runner.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +24 -0
- package/dist/server.js.map +1 -0
- package/dist/ssh/access-client.d.ts +62 -0
- package/dist/ssh/access-client.js +470 -0
- package/dist/ssh/access-client.js.map +1 -0
- package/dist/ssh/access-errors.d.ts +53 -0
- package/dist/ssh/access-errors.js +180 -0
- package/dist/ssh/access-errors.js.map +1 -0
- package/dist/ssh/access-sftp.d.ts +14 -0
- package/dist/ssh/access-sftp.js +456 -0
- package/dist/ssh/access-sftp.js.map +1 -0
- package/dist/ssh/bastion-client.d.ts +60 -0
- package/dist/ssh/bastion-client.js +423 -0
- package/dist/ssh/bastion-client.js.map +1 -0
- package/dist/ssh/bastion-errors.d.ts +62 -0
- package/dist/ssh/bastion-errors.js +190 -0
- package/dist/ssh/bastion-errors.js.map +1 -0
- package/dist/ssh/hop1-sftp.d.ts +14 -0
- package/dist/ssh/hop1-sftp.js +437 -0
- package/dist/ssh/hop1-sftp.js.map +1 -0
- package/dist/tools/copy-job-schemas.d.ts +709 -0
- package/dist/tools/copy-job-schemas.js +48 -0
- package/dist/tools/copy-job-schemas.js.map +1 -0
- package/dist/tools/register.d.ts +14 -0
- package/dist/tools/register.js +619 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/remote-schemas.d.ts +879 -0
- package/dist/tools/remote-schemas.js +214 -0
- package/dist/tools/remote-schemas.js.map +1 -0
- package/dist/utils/copy-executor.d.ts +41 -0
- package/dist/utils/copy-executor.js +231 -0
- package/dist/utils/copy-executor.js.map +1 -0
- package/dist/utils/copy-job-manager.d.ts +34 -0
- package/dist/utils/copy-job-manager.js +400 -0
- package/dist/utils/copy-job-manager.js.map +1 -0
- package/dist/utils/copy-job-store.d.ts +18 -0
- package/dist/utils/copy-job-store.js +104 -0
- package/dist/utils/copy-job-store.js.map +1 -0
- package/dist/utils/copy-job-types.d.ts +133 -0
- package/dist/utils/copy-job-types.js +37 -0
- package/dist/utils/copy-job-types.js.map +1 -0
- package/dist/utils/copy-transfer.d.ts +59 -0
- package/dist/utils/copy-transfer.js +86 -0
- package/dist/utils/copy-transfer.js.map +1 -0
- package/dist/utils/remote-copy.d.ts +14 -0
- package/dist/utils/remote-copy.js +115 -0
- package/dist/utils/remote-copy.js.map +1 -0
- package/dist/utils/remote-executor.d.ts +15 -0
- package/dist/utils/remote-executor.js +34 -0
- package/dist/utils/remote-executor.js.map +1 -0
- package/dist/utils/remote-target.d.ts +22 -0
- package/dist/utils/remote-target.js +75 -0
- package/dist/utils/remote-target.js.map +1 -0
- package/dist/utils/result.d.ts +87 -0
- package/dist/utils/result.js +81 -0
- package/dist/utils/result.js.map +1 -0
- package/dist/utils/shell-quote.d.ts +22 -0
- package/dist/utils/shell-quote.js +55 -0
- package/dist/utils/shell-quote.js.map +1 -0
- package/dist/utils/timeout.d.ts +1 -0
- package/dist/utils/timeout.js +18 -0
- package/dist/utils/timeout.js.map +1 -0
- package/dist/utils/truncate.d.ts +5 -0
- package/dist/utils/truncate.js +12 -0
- package/dist/utils/truncate.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/docs/AGENT_GUIDE.md +161 -0
- package/docs/CONFIGURATION.md +130 -0
- package/docs/COPY_SPECIALIZED_TEST_CHECKLIST.md +9 -0
- package/docs/HOP1_E2E_CHECKLIST.md +128 -0
- package/docs/HOP1_E2E_RESULTS.json +647 -0
- package/docs/PARADI_ACCESS_PLAN.md +272 -0
- package/docs/REMOTE_COPY_JOB_API.md +92 -0
- package/docs/TODO.md +66 -0
- package/docs/USER_GUIDE.zh-CN.md +93 -0
- package/docs/clients/claude-desktop.json.example +12 -0
- package/docs/clients/cursor-npx.json.example +12 -0
- package/docs/clients/cursor.json.example +12 -0
- package/docs/clients/vscode.json.example +12 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2026-07-27
|
|
11
|
+
|
|
12
|
+
First public release of **`host-execution-mcp`** (new npm package name; continues the Access → HostExecutionContext → Runtime line formerly developed as `bastion-k8s-mcp`).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Access shapes: `ACCESS_KIND=ssh` (landing / jump+`TARGET_*`) and `ACCESS_KIND=paradi-gateway`
|
|
17
|
+
- Paradi `ACCESS_PARADI_AUTH=auto|slash|compound` (default `auto`): try slash then compound on AUTH_FAILED; pin winner for the MCP process lifetime
|
|
18
|
+
- `access_check` may include `paradiAuth` after a successful Paradi connect
|
|
19
|
+
- Tools: `access_check`, `shell_exec`, `context_derive` / `context_probe`, `k8s_*`, `docker_*`, `remote_*` / `remote_copy_*`
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
|
|
23
|
+
- Install: `npx -y host-execution-mcp@1.0.0` (or `@latest`)
|
|
24
|
+
- Env: `ACCESS_*` / `TARGET_*` only (no `BASTION_*` / `REMOTE_SSH_*` aliases)
|
|
25
|
+
|
|
26
|
+
## [6.0.0] - 2026-07-23
|
|
27
|
+
|
|
28
|
+
### Breaking
|
|
29
|
+
|
|
30
|
+
- Package / MCP server rename: `bastion-k8s-mcp` → `host-execution-mcp`
|
|
31
|
+
- Source / index paths: `packages/mcp/bastion-k8s` → `packages/mcp/host-execution`, `mcp/bastion-k8s` → `mcp/host-execution`
|
|
32
|
+
- Internal rename: `BastionClient` → `AccessClient` (`access-client.ts` / `access-errors.ts` / `access-sftp.ts`)
|
|
33
|
+
- Removed CLI bin alias `bastion-k8s-mcp` (bin: `host-execution-mcp` only)
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Paradi Zod fail-fast: `remote_*` / `remote_copy_*` schemas omit `target=context`; shell/docker omit `contextKey` under `ACCESS_KIND=paradi-gateway`
|
|
38
|
+
- Copy guard rejects Paradi `target=context` (and `host`) before materialize
|
|
39
|
+
- Agent-facing tool descriptions drop Hop1/Hop2 jargon; docs aligned to ACCESS/Context naming + 5.1 backlog
|
|
40
|
+
|
|
41
|
+
## [5.0.0] - 2026-07-23
|
|
42
|
+
|
|
43
|
+
### Breaking
|
|
44
|
+
|
|
45
|
+
- Tool renames: `bastion_check` → `access_check`, `bastion_exec` → `shell_exec` (optional `contextKey`)
|
|
46
|
+
- New tool: `context_derive` (returns `contextKey`; unavailable under `ACCESS_KIND=paradi-gateway`)
|
|
47
|
+
- Error rename: `BASTION_UNAVAILABLE` → `ACCESS_UNAVAILABLE`; `BastionUnavailableError` → `AccessUnavailableError`
|
|
48
|
+
- Remote schemas: `target=bastion` → `target=landing`; removed agent-facing `target=host`; added `target=context` with `contextKey`
|
|
49
|
+
- `docker_*` / `context_probe`: `host`/`useTarget` removed — use `contextKey` only (omit for landing)
|
|
50
|
+
- `ssh_exec` response includes `contextKey` + command result fields
|
|
51
|
+
- Removed env alias `BASTION_COPY_JOB_ROOT` — use `ACCESS_COPY_JOB_ROOT` only
|
|
52
|
+
- Removed deprecated `shellOnAccess` alias on `AccessRuntime`
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Copy engine internal target `bastion` → `landing`; `target=context` materializes in copy handlers before execute
|
|
57
|
+
- Instructions / AGENT_GUIDE / README tool table aligned to 5.0.0 naming (no deferred renames)
|
|
58
|
+
- Instructions: ssh_exec = reuse-only derive+shell shortcut; shell_exec never creates Context; omit contextKey = AccessTransport landing; remote target=context defined; Paradi derive soft-worded; k8s_* notes contextKey not exposed yet; drop redundant Prefer-ssh_exec / Host-level duplication
|
|
59
|
+
- Instructions (MMR consensus): ssh_exec = plain derive+shell shortcut; Paradi derive = unsupported by transport; k8s_* landing-only by design; slim system prompt (cookbook → AGENT_GUIDE)
|
|
60
|
+
|
|
61
|
+
## [4.0.0] - 2026-07-22
|
|
62
|
+
|
|
63
|
+
### Breaking
|
|
64
|
+
|
|
65
|
+
- Removed env aliases: `BASTION_HOST`/`USER`/`PORT`/`PASSWORD`/`PRIVATE_KEY_PATH`, `REMOTE_SSH_USER`/`PASSWORD`. Use `ACCESS_*` / `TARGET_*` only; leftover old names throw with rename hints.
|
|
66
|
+
- Policy: prefer `ACCESS_POLICY_ALLOWED_NAMESPACES` (`ALLOWED_NAMESPACES` still accepted as policy-only short name).
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- Docs / README / SECURITY / `.env.example` aligned to ACCESS_* only
|
|
71
|
+
- Phase 4 cleanup complete per PARADI_ACCESS_PLAN
|
|
72
|
+
- Post-review hardening: Paradi-aware MCP instructions & tool descriptions; forbid `remote_copy` `target=host` under Paradi (sync + async start); derived `cwd` uses bash single-quote; derive context key hashes password; `ACCESS_COPY_JOB_ROOT`; probe exitCode handling; tool-description UTF-8 arrows; `remote_read`/`remote_ps`/`remote_copy_start` Paradi notes; SECURITY.md 4.0
|
|
73
|
+
- Instructions converge on Access/Runtime/derive (drop Hop1/Hop2); `current Context` wording; honest `docker_*(host/useTarget)` = derive convenience; hide Paradi username encoding; rename to `buildDerivedHostAuthGuidance` (no Hop2 alias); remove static `SERVER_INSTRUCTIONS`
|
|
74
|
+
- Remove unused internals: `AccessRuntime.deriveToHost` / `shellOnContext`, `remoteHop2FilePathEndpointSchema`, unused `remoteCopy*ResultSchema`
|
|
75
|
+
- Instruction v3 (text): Access-layer selectors vs Runtime; host-entry split; no Legacy wording; K8s probe on landing Context; Never reuse ACCESS_USER; Paradi “not available under this ACCESS_KIND” (not forever-disabled); capabilities not shared across Contexts
|
|
76
|
+
|
|
77
|
+
## [3.3.0] - 2026-07-22
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- `ACCESS_KIND=paradi-gateway`: ParadiAccessTransport → landing Context on TARGET; Hop1 SSH username `ACCESS_USER/TARGET_HOST/TARGET_USER`
|
|
82
|
+
- Declared capabilities: `sftp: "degraded"`, `channelOpenSensitive: true` (preserved across probe)
|
|
83
|
+
- `ssh_exec` / derive / `useTarget` rejected under paradi (already on TARGET)
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- `AccessClient` / Hop1 SFTP use `resolveAccessSshIdentity` (paradi-aware username)
|
|
88
|
+
- CONFIGURATION documents Paradi shape; `TARGET_PASSWORD` ignored for paradi auth
|
|
89
|
+
|
|
90
|
+
## [3.2.0] - 2026-07-22
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
|
|
94
|
+
- `context_probe`: probe Context capabilities (`docker` / `kubectl` CLI); tri-state true|false|unknown
|
|
95
|
+
- `docker_ps` / `docker_exec` / `docker_logs`: Docker Runtime via SessionPool (ACCESS default; `host` / `useTarget` to derive)
|
|
96
|
+
- Auto-probe `capabilities.docker` before docker_* when still unknown; fail if false
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- Server instructions: unknown ≠ true; docker_* vs k8s_exec selection
|
|
101
|
+
|
|
102
|
+
## [3.1.0] - 2026-07-22
|
|
103
|
+
|
|
104
|
+
### Breaking
|
|
105
|
+
|
|
106
|
+
- Prefer `ACCESS_*` / `TARGET_*` env names (see CONFIGURATION.md). Legacy `BASTION_*` / `REMOTE_SSH_*` still accepted as temporary aliases.
|
|
107
|
+
- `TARGET_HOST` requires `TARGET_USER` (no fallback to `ACCESS_USER`).
|
|
108
|
+
- `ACCESS_KIND=paradi-gateway` rejected until Phase 3.
|
|
109
|
+
|
|
110
|
+
### Added
|
|
111
|
+
|
|
112
|
+
- Access layer: `HostExecutionContext` (no session) + `SessionPool` + `SshAccessTransport`
|
|
113
|
+
- `ssh_exec` ≡ `deriveContext` + shell via `AccessRuntime` (optional host uses `TARGET_HOST`)
|
|
114
|
+
- Capabilities: `exec` + `docker`/`kubectl` = `"unknown"` until probe (Phase 2)
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
|
|
118
|
+
- Config / instructions / client examples document three Access shapes; Runtime not in mcp.json
|
|
119
|
+
|
|
120
|
+
## [3.0.4] - 2026-07-22
|
|
121
|
+
|
|
122
|
+
### Fixed
|
|
123
|
+
|
|
124
|
+
- Hop2 auth guidance: password-only (`REMOTE_SSH_PASSWORD` without `REMOTE_SSH_USER`) now says omit password but **still pass user** (was incorrectly "omit user/password")
|
|
125
|
+
|
|
126
|
+
### Changed
|
|
127
|
+
|
|
128
|
+
- Slim Hop2 `REMOTE_SSH_*` agent guidance; drop redundant `bastion_check.remoteHostAuth` and `ssh_exec` Auth prose (server instructions + short schema remain)
|
|
129
|
+
- First-session handshake: `kubectl cluster-info` only for Kubernetes ops
|
|
130
|
+
- Clarify `k8s_logs` as stdout+stderr log stream; copy `skipIfComplete`/`verify`/`totalTimeoutMs` as all-hop copy rules; `bastion_check` hints in JSON message (other tools' `BASTION_UNAVAILABLE` hints in stderr)
|
|
131
|
+
|
|
132
|
+
## [3.0.3] - 2026-07-22
|
|
133
|
+
|
|
134
|
+
### Fixed
|
|
135
|
+
|
|
136
|
+
- Agent guidance: when `REMOTE_SSH_USER` / `REMOTE_SSH_PASSWORD` are set in MCP env, server instructions and `ssh_exec` descriptions now say defaults are **active** — omit credentials and **do not ask the human** (runtime already applied env; agents were still prompted by old "provide user" wording)
|
|
137
|
+
|
|
138
|
+
### Changed
|
|
139
|
+
|
|
140
|
+
- `buildServerInstructions(config)` injects Hop2 auth status at startup (exposes default username, not password)
|
|
141
|
+
- `bastion_check` returns `remoteHostAuth` (`userConfigured`, `user`, `passwordConfigured`, `hint`)
|
|
142
|
+
- `hostFields` / `ssh_exec` parameter describes: omit when env defaults exist
|
|
143
|
+
|
|
144
|
+
## [3.0.2] - 2026-07-21
|
|
145
|
+
|
|
146
|
+
### Fixed
|
|
147
|
+
|
|
148
|
+
- `buildSshRemoteCommand`: quote the entire `bash -lc '<cmd>'` as one ssh remote argv so OpenSSH space-join no longer truncates multi-word commands (`ssh_exec`, `remote_*` target=host)
|
|
149
|
+
- `remote-copy` host post-copy `stat`: use `buildSshRemoteCommand` and pass the same password as scp (password-only hosts no longer fall back to BatchMode ssh)
|
|
150
|
+
|
|
151
|
+
### Changed
|
|
152
|
+
|
|
153
|
+
- Shared Hop2 host transport prefix (`buildHostTransportPrefix` / `hostSshBaseOpts`) for ssh and scp so `-o` / sshpass opts do not fork
|
|
154
|
+
- Agent instructions + `AGENT_GUIDE`: shell metacharacters, non-TTY docker empty stdout, CRLF, `DOCKER_CONFIG`, `success=false` reads stdout; deferred `registry_check` noted in `TODO.md`
|
|
155
|
+
|
|
156
|
+
### Tests
|
|
157
|
+
|
|
158
|
+
- `shell-quote`: multi-word command, URL `&`, sshpass, double-wrap (`buildBashLcCommand(buildSshRemoteCommand(...))`)
|
|
159
|
+
|
|
160
|
+
## [3.0.1] - 2026-07-17
|
|
161
|
+
|
|
162
|
+
### Added
|
|
163
|
+
|
|
164
|
+
- `BastionUnavailableError` (`code=BASTION_UNAVAILABLE`) with Chinese hints for VPN/network/auth failures
|
|
165
|
+
- `bastion_check` tool: short-timeout Local→Bastion SSH probe (≤10s), structured `{ reachable, latencyMs, ... }`
|
|
166
|
+
- `ensureAccessReady()` with positive/negative TTL on `exec` and Hop1 `uploadPath`/`downloadPath`
|
|
167
|
+
|
|
168
|
+
### Changed
|
|
169
|
+
|
|
170
|
+
- Network/auth **connect-phase** failures fail-fast (no 3× reconnect); stream-phase `ECONNRESET`/`EPIPE` remain briefly retryable
|
|
171
|
+
- Probe/`bastion_check` use ephemeral SSH (do not sever in-flight exec/copy); transfer errors are not reclassified as bastion-down
|
|
172
|
+
- Auth classification tightened to ssh2 auth phrases (file `Permission denied` excluded)
|
|
173
|
+
- Server instructions / tool-selection prompt / descriptions: prefer `bastion_check` first; stop retrying on `BASTION_UNAVAILABLE`
|
|
174
|
+
|
|
175
|
+
## [3.0.0] - 2026-07-16
|
|
176
|
+
|
|
177
|
+
### Added
|
|
178
|
+
|
|
179
|
+
- Hop1 directory copy (local↔bastion): walk + per-file SFTP via `ssh2-sftp-client` (independent TCP from exec)
|
|
180
|
+
- `uploadPath` / `downloadPath` on AccessClient (file or directory)
|
|
181
|
+
- `skipIfComplete` + result fields `skippedBytes` / `skipped` / `skipReason` (`size-match` | `hash-match`)
|
|
182
|
+
- `resolveEffectiveVerify`: rejects `skipIfComplete` + `verify=none`
|
|
183
|
+
- Job status `timeout`; `connectionTimeoutMs` for Hop1 SFTP connect
|
|
184
|
+
|
|
185
|
+
### Changed
|
|
186
|
+
|
|
187
|
+
- **BREAKING**: Removed Hop1 self-built stream engine and `.copy-meta.json` byte-offset resume
|
|
188
|
+
- **BREAKING**: Removed `resume` (use `skipIfComplete`); removed `idleTimeoutMs`; renamed `resumedFrom` → `skippedBytes`
|
|
189
|
+
- `remote_copy_file` remains sync-only; never auto-creates async jobs
|
|
190
|
+
- Skip gate strength is explicitly `verify` (no auto-upgrade size→sha256)
|
|
191
|
+
|
|
192
|
+
### Fixed
|
|
193
|
+
|
|
194
|
+
- Hop1 upload `skipIfComplete`: verify fail after size match falls through to rewrite (aligned with download)
|
|
195
|
+
- Directory skip accumulates `skippedBytes`; directory supports per-file `verify=size+sha256`
|
|
196
|
+
|
|
197
|
+
### Removed
|
|
198
|
+
|
|
199
|
+
- Stream upload/download, copy-meta helpers, `uploadFile`/`downloadFile` aliases, `resume` / `idleTimeoutMs` API surface
|
|
200
|
+
|
|
201
|
+
## [2.2.1] - 2026-07-16
|
|
202
|
+
|
|
203
|
+
### Fixed
|
|
204
|
+
|
|
205
|
+
- Job terminal-state fence: cancel cannot overwrite success/failed; success cannot overwrite cancelled
|
|
206
|
+
- Orphan reconcile on MCP start: leftover `queued|running` jobs → `failed` ("MCP restarted…")
|
|
207
|
+
- Hop1 stream abort no longer `disconnect()`s the shared SSH client (avoids killing concurrent tools/jobs)
|
|
208
|
+
- `exec` accepts `AbortSignal` and closes the channel (no 3× retry on abort/timeout)
|
|
209
|
+
- Pipeline copies share one wall-clock `totalTimeoutMs` budget across hops; abort checked between hops
|
|
210
|
+
- Upload fastPut / size-shortcut now run real verify; `verified: true` is no longer claimed without checks
|
|
211
|
+
- Resume: same-size local without matching `.copy-meta.json` forces restart (no silent skip)
|
|
212
|
+
- Stream idle timeout based on byte progress (+ write drain), not only read `data` events
|
|
213
|
+
- Pre-aborted signal no longer hits `totalTimer` TDZ `ReferenceError`
|
|
214
|
+
- Schema: `remote_copy_status` only accepts async `remote_copy_start` transferIds
|
|
215
|
+
|
|
216
|
+
### Changed
|
|
217
|
+
|
|
218
|
+
- Cancel messaging documents Hop2 best-effort abort
|
|
219
|
+
- **Upload resume simplified**: no bastion-side meta / byte-offset append; `resume` only skips rewrite when remote size already matches + verify
|
|
220
|
+
- Unified `withSftp` + single `verifyTransfer` for Hop1; removed unused `detach` and numeric timeout overload on upload/download
|
|
221
|
+
- `throwIfAborted` lives in `copy-transfer` (shared)
|
|
222
|
+
|
|
223
|
+
## [2.2.0] - 2026-07-16
|
|
224
|
+
|
|
225
|
+
### Added
|
|
226
|
+
|
|
227
|
+
- **Async copy jobs (M2)**: `remote_copy_start` / `remote_copy_status` / `remote_copy_cancel`
|
|
228
|
+
- Job persistence under `~/.agent-compose/remote-copy/jobs/<transferId>.json` (override with `BASTION_COPY_JOB_ROOT`)
|
|
229
|
+
- In-process worker runs the same Copy Engine as sync `remote_copy_file`; cancel via `AbortSignal` (Hop1 streams aborted; shared SSH kept)
|
|
230
|
+
- Policy helpers: `shouldPreferAsyncCopy` (size>256MB or ETA>2min)
|
|
231
|
+
- Docs: `docs/REMOTE_COPY_JOB_API.md` marked implemented
|
|
232
|
+
|
|
233
|
+
### Changed
|
|
234
|
+
|
|
235
|
+
- Server instructions / prompts recommend async tools for large or long-ETA copies
|
|
236
|
+
- Hop1 stream upload/download honor `signal` for cooperative abort
|
|
237
|
+
|
|
238
|
+
## [2.1.0] - 2026-07-16
|
|
239
|
+
|
|
240
|
+
### Added
|
|
241
|
+
|
|
242
|
+
- **`remote_copy_file` Hop1 SFTP resume**: optional `resume` with sibling `dest.copy-meta.json` (remote path/size/mtime) to prevent silent corruption
|
|
243
|
+
- **Idle + total timeouts**: `idleTimeoutMs` (default 60s, resets on data) and `totalTimeoutMs` (wall clock); legacy `timeoutMs` maps to `totalTimeoutMs`
|
|
244
|
+
- **Throttled progress**: `progressIntervalMs` (default 5s) + MCP `notifications/progress` when client provides `progressToken`
|
|
245
|
+
- **Result fields**: `transferId`, `totalBytes`, `resumedFrom`, `verified`, `usedFastPath` (ready for future `remote_copy_status`)
|
|
246
|
+
- **Verify modes**: `verify=none|size|size+sha256` (default `size`) for Hop1 transfers
|
|
247
|
+
- Small files still use `fastGet`/`fastPut`; large files or `resume`/progress use streamed copy
|
|
248
|
+
|
|
249
|
+
### Changed
|
|
250
|
+
|
|
251
|
+
- `executeRemoteCopy` now takes an options object (breaking for direct JS callers; MCP schema remains backward compatible via optional fields)
|
|
252
|
+
|
|
253
|
+
## [2.0.0] - 2026-07-06
|
|
254
|
+
|
|
255
|
+
### Removed
|
|
256
|
+
|
|
257
|
+
- **`remote_tail_file` tool** — use `remote_read_file` with `mode=tail` instead (breaking change)
|
|
258
|
+
|
|
259
|
+
### Changed
|
|
260
|
+
|
|
261
|
+
- Tool count reduced from 9 to 8; all docs/prompts/instructions updated
|
|
262
|
+
|
|
263
|
+
## [1.4.0] - 2026-07-06
|
|
264
|
+
|
|
265
|
+
### Added
|
|
266
|
+
|
|
267
|
+
- `remote_read_file` unified file reading: `mode=head|tail`, shared `lines` parameter
|
|
268
|
+
- Tail reads now include `totalLines` / `totalBytes` metadata (same as head)
|
|
269
|
+
- `hop2PathUnion()` shared schema builder for bastion/pod/host targets (P0 refactor)
|
|
270
|
+
- Tests for `resolveReadOptions`, `buildRemoteReadCommand`, deprecated tail alias
|
|
271
|
+
|
|
272
|
+
### Changed
|
|
273
|
+
|
|
274
|
+
- **`remote_tail_file` deprecated** — delegates to `remote_read_file` with `mode=tail`; kept for backward compatibility
|
|
275
|
+
- `maxLines` on `remote_read_file` remains as deprecated alias for `lines` when `mode=head`
|
|
276
|
+
|
|
277
|
+
## [1.2.0] - 2026-07-06
|
|
278
|
+
|
|
279
|
+
### Added
|
|
280
|
+
|
|
281
|
+
- **`remote_copy_file` `local` endpoint**: SFTP upload/download between MCP server machine and bastion
|
|
282
|
+
- Chained routes: `local→pod`, `local→host`, `pod→local`, `host→local` (via bastion staging)
|
|
283
|
+
|
|
284
|
+
## [1.1.0] - 2026-07-06
|
|
285
|
+
|
|
286
|
+
### Added
|
|
287
|
+
|
|
288
|
+
- **`remote_copy_file` tool**: copy files/directories between bastion, pod, and host; returns `{ success, bytes, message }` without file content
|
|
289
|
+
- **`remote_read_file` metadata**: response includes `totalLines`, `totalBytes`, `truncatedByLines`, `truncatedByChars` for clearer truncation diagnosis
|
|
290
|
+
- Unit tests for copy command builders and read-file metadata parsing
|
|
291
|
+
|
|
292
|
+
### Changed
|
|
293
|
+
|
|
294
|
+
- `remote_read_file` tool description documents default `maxLines=500` and `MAX_OUTPUT_CHARS` (65536) limits
|
|
295
|
+
- Server instructions mention `remote_copy_file` and updated truncation guidance
|
|
296
|
+
|
|
297
|
+
## [1.0.1] - 2026-07-03
|
|
298
|
+
|
|
299
|
+
### Fixed
|
|
300
|
+
|
|
301
|
+
- Bastion SSH auth: pass all configured methods (key, agent, password) to ssh2 instead of picking only the first — fixes empty SSH agent blocking `BASTION_PASSWORD` in IDE-launched MCP clients
|
|
302
|
+
|
|
303
|
+
### Added
|
|
304
|
+
|
|
305
|
+
- Unit tests: `truncateOutput`, `buildReadFileCommand` path escaping, `resolveSshAuthOptions` auth priority (injectable deps)
|
|
306
|
+
- ESLint + Prettier, `CONTRIBUTING.md`
|
|
307
|
+
- Expanded `SECURITY.md` (two-hop model, sshpass visibility, accept-new, ALLOWED_NAMESPACES scope)
|
|
308
|
+
|
|
309
|
+
### Changed
|
|
310
|
+
|
|
311
|
+
- Removed `parseRemoteTarget`; `remote_*` targets unified via `remote-schemas.ts` (`remoteInputToTarget` + `RemoteTarget` type)
|
|
312
|
+
- `resolveSshAuthOptions` accepts optional deps for testability
|
|
313
|
+
|
|
314
|
+
## [1.0.0] - 2026-07-02
|
|
315
|
+
|
|
316
|
+
### Added
|
|
317
|
+
|
|
318
|
+
- MCP server with 8 tools: `bastion_exec`, `k8s_list_pods`, `k8s_exec`, `k8s_logs`, `ssh_exec`, `remote_read_file`, `remote_tail_file`, `remote_ps`
|
|
319
|
+
- Two-hop architecture: MCP client → SSH → bastion → kubectl / SSH / shell
|
|
320
|
+
- Flexible bastion auth: SSH agent, default keys, `BASTION_PRIVATE_KEY_PATH`, or `BASTION_PASSWORD`
|
|
321
|
+
- Remote host password support via `REMOTE_SSH_PASSWORD` / tool `password` (sshpass on bastion)
|
|
322
|
+
- MCP prompts: `tool-selection`, `remote-debug-workflow`
|
|
323
|
+
- Agent documentation (`docs/AGENT_GUIDE.md`) and configuration guide
|
|
324
|
+
- Manual integration scripts under `scripts/manual/` (env-driven, not published to npm)
|
|
325
|
+
- GitHub Actions CI (build + unit tests)
|
|
326
|
+
|
|
327
|
+
### Security
|
|
328
|
+
|
|
329
|
+
- Removed hardcoded credentials from manual scripts; use `scripts/manual/.env.local` instead
|
|
330
|
+
- Added `SECURITY.md` with credential and sshpass guidance
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 host-execution-mcp contributors
|
|
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.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# host-execution-mcp
|
|
2
|
+
|
|
3
|
+
Generic [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for Access → HostExecutionContext → Runtime (shell / docker / kubectl via SSH jump or Paradi gateway).
|
|
4
|
+
|
|
5
|
+
**Any MCP client** can connect (Cursor, Claude Desktop, VS Code, custom SDK) using stdio transport.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MCP Client → host-execution-mcp (local) → SSH → ACCESS landing Context → kubectl / docker / shell / optional derive → pods & hosts
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Composable tools:
|
|
14
|
+
|
|
15
|
+
| Tool | Purpose |
|
|
16
|
+
|------|---------|
|
|
17
|
+
| `access_check` | Short ACCESS SSH probe (`reachable` / `latencyMs`; not kubectl) |
|
|
18
|
+
| `shell_exec` | Shell on landing Context (optional `contextKey`) |
|
|
19
|
+
| `context_derive` | Derive child Context → `contextKey` (`ACCESS_KIND=ssh` only) |
|
|
20
|
+
| `context_probe` | Probe docker/kubectl capabilities (unknown ≠ true) |
|
|
21
|
+
| `docker_ps` / `docker_exec` / `docker_logs` | Docker Runtime on landing or derived Context (`contextKey`) |
|
|
22
|
+
| `k8s_list_pods` | List pods (`kubectl get pods`) |
|
|
23
|
+
| `k8s_exec` | Run command in pod container |
|
|
24
|
+
| `k8s_logs` | Pod stdout/stderr (`kubectl logs`) |
|
|
25
|
+
| `ssh_exec` | Derive + shell on remote host (`ACCESS_KIND=ssh`) |
|
|
26
|
+
| `remote_read_file` | Read file head/tail (`mode=head\|tail`) on landing/pod/context |
|
|
27
|
+
| `remote_copy_file` | Sync copy between local/landing/pod/context (SFTP for local↔landing; `skipIfComplete` / `totalTimeoutMs` / progress) |
|
|
28
|
+
| `remote_copy_start` | Async copy job — returns `transferId` immediately (prefer for >256MB or ETA>2min) |
|
|
29
|
+
| `remote_copy_status` | Poll async copy job progress |
|
|
30
|
+
| `remote_copy_cancel` | Cancel async copy job |
|
|
31
|
+
| `remote_ps` | List processes on landing/pod/context |
|
|
32
|
+
|
|
33
|
+
Async job state: `~/.agent-compose/remote-copy/jobs/<transferId>.json` — see [docs/REMOTE_COPY_JOB_API.md](docs/REMOTE_COPY_JOB_API.md).
|
|
34
|
+
|
|
35
|
+
No business logic, no hardcoded namespaces or paths.
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
### Option A — npx(推荐,无需 clone/build)
|
|
40
|
+
|
|
41
|
+
Requires Node.js >= 18 only.
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"host-execution": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": ["-y", "host-execution-mcp@latest"],
|
|
49
|
+
"env": {
|
|
50
|
+
"ACCESS_HOST": "10.0.0.1",
|
|
51
|
+
"ACCESS_USER": "your-user"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Pin a version for stability: `"args": ["-y", "host-execution-mcp@6.0.0"]`
|
|
59
|
+
|
|
60
|
+
### Option B — 本地开发
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd packages/mcp/host-execution
|
|
64
|
+
npm install
|
|
65
|
+
npm run build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
MCP config uses local path:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
"command": "node",
|
|
72
|
+
"args": ["<ABS_PATH>/packages/mcp/host-execution/dist/index.js"]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Minimal env
|
|
76
|
+
|
|
77
|
+
Only `ACCESS_HOST` and `ACCESS_USER` are required. Configure any SSH auth you use (key path, default `~/.ssh` keys, SSH agent, and/or `ACCESS_PASSWORD`); the client tries each until one works. If your IDE exposes an empty SSH agent, set `ACCESS_PASSWORD` and it will be used after agent/key attempts fail. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md).
|
|
78
|
+
|
|
79
|
+
## Publishing (maintainers)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm run build && npm test
|
|
83
|
+
npm publish # or --registry for private npm
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Documentation
|
|
87
|
+
|
|
88
|
+
- [使用教程(中文)](docs/USER_GUIDE.zh-CN.md) — Node.js 安装、MCP 配置、Agent 实用话术
|
|
89
|
+
- [Agent usage guide](docs/AGENT_GUIDE.md) — for LLM agents after MCP handshake
|
|
90
|
+
- [Configuration](docs/CONFIGURATION.md) — env vars, auth, client setup
|
|
91
|
+
- [SECURITY.md](SECURITY.md) — trust boundaries, credentials, sshpass notes
|
|
92
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---------|-----------|
|
|
7
|
+
| 6.0.x | Yes |
|
|
8
|
+
| 5.0.x | Best effort |
|
|
9
|
+
| 4.0.x | Best effort |
|
|
10
|
+
| < 4.0 | Best effort |
|
|
11
|
+
|
|
12
|
+
## Reporting a vulnerability
|
|
13
|
+
|
|
14
|
+
If you discover a security issue, please report it privately to the maintainers (do not open a public issue with exploit details).
|
|
15
|
+
|
|
16
|
+
## Architecture and trust boundaries
|
|
17
|
+
|
|
18
|
+
Operations follow **Access → HostExecutionContext → Runtime** (`access_check`, `shell_exec`, `context_*`, `k8s_*`, `docker_*`, `remote_*`):
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
MCP client → SSH → ACCESS_HOST (landing Context)
|
|
22
|
+
→ kubectl / docker / shell / optional derive(ssh) → Pod or host
|
|
23
|
+
|
|
24
|
+
ACCESS_KIND=paradi-gateway: SSH username encodes TARGET; landing Context = TARGET (not the gateway OS).
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
There is **no direct path** from the MCP client machine to cluster pods or worker nodes. Treat compromise of the ACCESS landing (or Paradi gateway) as compromise of every target it can reach.
|
|
28
|
+
|
|
29
|
+
## Credential storage
|
|
30
|
+
|
|
31
|
+
| Credential | Typical location | Notes |
|
|
32
|
+
|------------|------------------|-------|
|
|
33
|
+
| ACCESS SSH key / agent | Local `~/.ssh`, `SSH_AUTH_SOCK`, or `ACCESS_PRIVATE_KEY_PATH` | Preferred over password; agent and password may both be configured — ssh2 tries each until one succeeds |
|
|
34
|
+
| ACCESS password | MCP client env (`ACCESS_PASSWORD`) or `.env` | Dev only; never commit |
|
|
35
|
+
| Target host password | MCP env (`TARGET_PASSWORD`) or per-tool `password` | Uses `sshpass` on ACCESS for derive (`ssh` kind) |
|
|
36
|
+
| MCP client config | `.cursor/mcp.json`, Claude Desktop config, etc. | Often gitignored; treat as secret |
|
|
37
|
+
|
|
38
|
+
**Never commit** `.env`, `scripts/manual/.env.local`, MCP configs with passwords, or private keys. Rotate credentials if they were ever committed or shared in chat logs.
|
|
39
|
+
|
|
40
|
+
## sshpass and process visibility
|
|
41
|
+
|
|
42
|
+
When `ssh_exec` / `context_derive` use a password (or `TARGET_PASSWORD`), the ACCESS landing runs:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
sshpass -p '<password>' ssh ...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The password may appear in **`ps` / process listings on the landing** while the command runs. **Prefer SSH keys** for ACCESS→host derive. If passwords are unavoidable, restrict landing shell access and audit who can run MCP.
|
|
49
|
+
|
|
50
|
+
## StrictHostKeyChecking=accept-new
|
|
51
|
+
|
|
52
|
+
Remote host SSH uses `-o StrictHostKeyChecking=accept-new`. On first connect, unknown host keys are accepted automatically. This is intended for **controlled internal networks** where MITM risk is managed by network policy. For internet-facing hosts, pre-populate `known_hosts` on the bastion and consider key-based auth only.
|
|
53
|
+
|
|
54
|
+
## ALLOWED_NAMESPACES scope
|
|
55
|
+
|
|
56
|
+
`ALLOWED_NAMESPACES` restricts **kubectl tools only** (`k8s_list_pods`, `k8s_exec`, `k8s_logs`, and `remote_*` with `target=pod`). It does **not** restrict:
|
|
57
|
+
|
|
58
|
+
- `shell_exec` — arbitrary shell on the ACCESS landing Context, including `kubectl` against any namespace
|
|
59
|
+
- `ssh_exec` — SSH derive from ACCESS to any reachable host
|
|
60
|
+
- `remote_*` with `target=landing` or materialized `target=context`
|
|
61
|
+
|
|
62
|
+
For defense in depth, combine namespace whitelist with bastion RBAC, kubeconfig permissions, and limiting who can run the MCP server.
|
|
63
|
+
|
|
64
|
+
## Operational risks
|
|
65
|
+
|
|
66
|
+
- This server executes arbitrary shell commands on the bastion, in pods, and on remote hosts. There is no built-in command allowlist in v1.x.
|
|
67
|
+
- Treat MCP access as equivalent to shell access on the bastion plus kubectl access in allowed namespaces.
|
|
68
|
+
- Output is truncated at `MAX_OUTPUT_CHARS` (default 65536) to limit accidental large dumps.
|
|
69
|
+
|
|
70
|
+
## Dependencies
|
|
71
|
+
|
|
72
|
+
Run `npm audit` periodically and keep `@modelcontextprotocol/sdk`, `ssh2`, and `zod` updated.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ExecOutput } from "../ssh/access-client.js";
|
|
2
|
+
import type { SessionPool } from "./session-pool.js";
|
|
3
|
+
import type { HostExecutionContext } from "./types.js";
|
|
4
|
+
/** Docker Runtime — only consumes Context + SessionPool (Invariant B). */
|
|
5
|
+
export declare function buildDockerPsCommand(options: {
|
|
6
|
+
all?: boolean;
|
|
7
|
+
quiet?: boolean;
|
|
8
|
+
filter?: string;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function buildDockerExecCommand(container: string, command: string, options?: {
|
|
11
|
+
interactive?: boolean;
|
|
12
|
+
workdir?: string;
|
|
13
|
+
}): string;
|
|
14
|
+
export declare function buildDockerLogsCommand(container: string, options?: {
|
|
15
|
+
tail?: number;
|
|
16
|
+
since?: string;
|
|
17
|
+
timestamps?: boolean;
|
|
18
|
+
}): string;
|
|
19
|
+
export declare function ensureDocker(pool: SessionPool, ctx: HostExecutionContext, timeoutMs?: number): Promise<void>;
|
|
20
|
+
export declare function dockerPs(pool: SessionPool, ctx: HostExecutionContext, options?: {
|
|
21
|
+
all?: boolean;
|
|
22
|
+
quiet?: boolean;
|
|
23
|
+
filter?: string;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}): Promise<ExecOutput>;
|
|
26
|
+
export declare function dockerExec(pool: SessionPool, ctx: HostExecutionContext, args: {
|
|
27
|
+
container: string;
|
|
28
|
+
command: string;
|
|
29
|
+
workdir?: string;
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}): Promise<ExecOutput>;
|
|
32
|
+
export declare function dockerLogs(pool: SessionPool, ctx: HostExecutionContext, args: {
|
|
33
|
+
container: string;
|
|
34
|
+
tail?: number;
|
|
35
|
+
since?: string;
|
|
36
|
+
timestamps?: boolean;
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}): Promise<ExecOutput>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { bashSingleQuote } from "../utils/shell-quote.js";
|
|
2
|
+
import { assertCapability, probeCapabilities } from "./probe.js";
|
|
3
|
+
/** Docker Runtime — only consumes Context + SessionPool (Invariant B). */
|
|
4
|
+
export function buildDockerPsCommand(options) {
|
|
5
|
+
const parts = ["docker", "ps"];
|
|
6
|
+
if (options.all) {
|
|
7
|
+
parts.push("-a");
|
|
8
|
+
}
|
|
9
|
+
if (options.quiet) {
|
|
10
|
+
parts.push("-q");
|
|
11
|
+
}
|
|
12
|
+
if (options.filter?.trim()) {
|
|
13
|
+
parts.push("--filter", bashSingleQuote(options.filter.trim()));
|
|
14
|
+
}
|
|
15
|
+
// Prefer stable columns for agents (skip format when quiet)
|
|
16
|
+
if (!options.quiet) {
|
|
17
|
+
parts.push("--format", bashSingleQuote("table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}"));
|
|
18
|
+
}
|
|
19
|
+
return parts.join(" ");
|
|
20
|
+
}
|
|
21
|
+
export function buildDockerExecCommand(container, command, options = {}) {
|
|
22
|
+
const parts = ["docker", "exec"];
|
|
23
|
+
if (options.workdir?.trim()) {
|
|
24
|
+
parts.push("-w", bashSingleQuote(options.workdir.trim()));
|
|
25
|
+
}
|
|
26
|
+
parts.push(bashSingleQuote(container.trim()));
|
|
27
|
+
// Run via bash -lc so pipelines/redirection work like other Runtimes
|
|
28
|
+
parts.push("bash", "-lc", bashSingleQuote(command));
|
|
29
|
+
return parts.join(" ");
|
|
30
|
+
}
|
|
31
|
+
export function buildDockerLogsCommand(container, options = {}) {
|
|
32
|
+
const parts = ["docker", "logs"];
|
|
33
|
+
if (options.timestamps) {
|
|
34
|
+
parts.push("-t");
|
|
35
|
+
}
|
|
36
|
+
if (options.since?.trim()) {
|
|
37
|
+
parts.push("--since", bashSingleQuote(options.since.trim()));
|
|
38
|
+
}
|
|
39
|
+
parts.push("--tail", String(options.tail ?? 200));
|
|
40
|
+
parts.push(bashSingleQuote(container.trim()));
|
|
41
|
+
return parts.join(" ");
|
|
42
|
+
}
|
|
43
|
+
export async function ensureDocker(pool, ctx, timeoutMs) {
|
|
44
|
+
if (ctx.capabilities.docker === "unknown") {
|
|
45
|
+
await probeCapabilities(pool, ctx, timeoutMs);
|
|
46
|
+
}
|
|
47
|
+
assertCapability(ctx, "docker");
|
|
48
|
+
}
|
|
49
|
+
export async function dockerPs(pool, ctx, options = {}) {
|
|
50
|
+
await ensureDocker(pool, ctx, options.timeoutMs);
|
|
51
|
+
return pool.exec(ctx, buildDockerPsCommand(options), options.timeoutMs);
|
|
52
|
+
}
|
|
53
|
+
export async function dockerExec(pool, ctx, args) {
|
|
54
|
+
await ensureDocker(pool, ctx, args.timeoutMs);
|
|
55
|
+
return pool.exec(ctx, buildDockerExecCommand(args.container, args.command, { workdir: args.workdir }), args.timeoutMs);
|
|
56
|
+
}
|
|
57
|
+
export async function dockerLogs(pool, ctx, args) {
|
|
58
|
+
await ensureDocker(pool, ctx, args.timeoutMs);
|
|
59
|
+
return pool.exec(ctx, buildDockerLogsCommand(args.container, args), args.timeoutMs);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=docker-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-runtime.js","sourceRoot":"","sources":["../../src/access/docker-runtime.ts"],"names":[],"mappings":"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"}
|