dsh-ops 0.0.0-stage → 0.2.2
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 +202 -0
- package/LICENSE +30 -0
- package/NOTICE +106 -0
- package/PROVENANCE.md +435 -0
- package/README.en.md +126 -0
- package/README.md +115 -2
- package/README.zh.md +116 -0
- package/bin/dsh-ops.mjs +1216 -0
- package/cordis.patch.yml +160 -0
- package/docs/manual-validation.md +53 -0
- package/docs/release-0.2.1.md +72 -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 +998 -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/lib/rust.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locating a Rust toolchain and building the vendored FastCtx runtime.
|
|
3
|
+
*
|
|
4
|
+
* The plugin's runtime is a native executable, so a source checkout needs one
|
|
5
|
+
* explicit, reproducible build step. This module owns it: find `cargo` without
|
|
6
|
+
* guessing, run the release build in the vendored tree, and report the exact
|
|
7
|
+
* toolchain that produced the binary.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-ops/rust
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
13
|
+
import fs from 'node:fs'
|
|
14
|
+
import os from 'node:os'
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
import { PACKAGE_ROOT, executableName } from './binary.js'
|
|
17
|
+
|
|
18
|
+
/** Environment variable that names the `cargo` executable to use. */
|
|
19
|
+
export const CARGO_ENV = 'DSH_OPS_CARGO'
|
|
20
|
+
|
|
21
|
+
/** The vendored FastCtx source tree, relative to the plugin package root. */
|
|
22
|
+
export const VENDOR_DIR = path.join(PACKAGE_ROOT, 'vendor', 'fastctx')
|
|
23
|
+
|
|
24
|
+
/** Error raised when no usable Rust toolchain is found. */
|
|
25
|
+
export class CargoNotFoundError extends Error {
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} message - the failure summary.
|
|
28
|
+
* @param {string[]} tried - every candidate considered.
|
|
29
|
+
*/
|
|
30
|
+
constructor(message, tried) {
|
|
31
|
+
super(message)
|
|
32
|
+
this.name = 'CargoNotFoundError'
|
|
33
|
+
this.tried = tried
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* One process run, with its output collected.
|
|
39
|
+
* @param {string} command - the executable.
|
|
40
|
+
* @param {string[]} args - its arguments.
|
|
41
|
+
* @param {object} options - run inputs.
|
|
42
|
+
* @param {string} [options.cwd] - working directory.
|
|
43
|
+
* @param {NodeJS.ProcessEnv} [options.env] - environment.
|
|
44
|
+
* @returns {{status: number|null, stdout: string, stderr: string}} the outcome.
|
|
45
|
+
*/
|
|
46
|
+
function run(command, args, { cwd, env }) {
|
|
47
|
+
const result = spawnSync(command, args, { cwd, env, encoding: 'utf8', windowsHide: true })
|
|
48
|
+
return {
|
|
49
|
+
status: result.status,
|
|
50
|
+
stdout: result.stdout ?? '',
|
|
51
|
+
stderr: result.stderr ?? result.error?.message ?? '',
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Candidate `cargo` executables, most explicit first.
|
|
57
|
+
* @param {object} [options] - discovery inputs.
|
|
58
|
+
* @param {NodeJS.ProcessEnv} [options.env] - the environment to read.
|
|
59
|
+
* @param {string} [options.platform] - `process.platform`.
|
|
60
|
+
* @returns {{file: string, source: string}[]} the candidates.
|
|
61
|
+
*/
|
|
62
|
+
function cargoCandidates({ env = process.env, platform = process.platform } = {}) {
|
|
63
|
+
const name = platform === 'win32' ? 'cargo.exe' : 'cargo'
|
|
64
|
+
/** @type {{file: string, source: string}[]} */
|
|
65
|
+
const candidates = []
|
|
66
|
+
if (env[CARGO_ENV] !== undefined && env[CARGO_ENV].trim() !== '') {
|
|
67
|
+
candidates.push({ file: path.resolve(env[CARGO_ENV]), source: CARGO_ENV })
|
|
68
|
+
}
|
|
69
|
+
const cargoHome = env.CARGO_HOME
|
|
70
|
+
if (cargoHome !== undefined && cargoHome.trim() !== '') {
|
|
71
|
+
candidates.push({ file: path.join(cargoHome, 'bin', name), source: 'CARGO_HOME' })
|
|
72
|
+
}
|
|
73
|
+
candidates.push({ file: path.join(os.homedir(), '.cargo', 'bin', name), source: 'user cargo home' })
|
|
74
|
+
// A relocated toolchain (a shared development drive, a CI image) is reached
|
|
75
|
+
// through DSH_OPS_RUST_HOME, which names the directory holding `.cargo`.
|
|
76
|
+
const rustHome = env.DSH_OPS_RUST_HOME
|
|
77
|
+
if (rustHome !== undefined && rustHome.trim() !== '') {
|
|
78
|
+
candidates.push({ file: path.join(rustHome, '.cargo', 'bin', name), source: 'DSH_OPS_RUST_HOME' })
|
|
79
|
+
}
|
|
80
|
+
candidates.push({ file: name, source: 'PATH' })
|
|
81
|
+
return candidates
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Find a working `cargo`.
|
|
86
|
+
*
|
|
87
|
+
* `DSH_OPS_CARGO` is authoritative: when it names something unusable the search
|
|
88
|
+
* fails instead of silently building with a different toolchain.
|
|
89
|
+
* @param {object} [options] - discovery inputs.
|
|
90
|
+
* @param {NodeJS.ProcessEnv} [options.env] - the environment to read.
|
|
91
|
+
* @param {string} [options.platform] - `process.platform`.
|
|
92
|
+
* @returns {{file: string, source: string, version: string, env: NodeJS.ProcessEnv}} the toolchain.
|
|
93
|
+
* @throws {CargoNotFoundError} when no usable `cargo` is found.
|
|
94
|
+
*/
|
|
95
|
+
export function findCargo({ env = process.env, platform = process.platform } = {}) {
|
|
96
|
+
/** @type {string[]} */
|
|
97
|
+
const tried = []
|
|
98
|
+
for (const candidate of cargoCandidates({ env, platform })) {
|
|
99
|
+
if (candidate.file !== 'cargo' && candidate.file !== 'cargo.exe' && !fs.existsSync(candidate.file)) {
|
|
100
|
+
tried.push(`${candidate.source}: ${candidate.file} (missing)`)
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
const probe = run(candidate.file, ['--version'], { env })
|
|
104
|
+
if (probe.status === 0) {
|
|
105
|
+
return { ...candidate, version: probe.stdout.trim(), env }
|
|
106
|
+
}
|
|
107
|
+
tried.push(`${candidate.source}: ${candidate.file} (${probe.stderr.trim().split('\n')[0] || `exited ${probe.status}`})`)
|
|
108
|
+
if (candidate.source === CARGO_ENV) {
|
|
109
|
+
throw new CargoNotFoundError(`${CARGO_ENV} does not name a working cargo`, tried)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw new CargoNotFoundError(
|
|
113
|
+
'no working Rust toolchain found; install Rust, or set DSH_OPS_CARGO to the cargo executable, '
|
|
114
|
+
+ 'or set CARGO_HOME to the toolchain home',
|
|
115
|
+
tried,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Probe an existing release build of the vendored source.
|
|
121
|
+
* @param {object} [options] - probe inputs.
|
|
122
|
+
* @param {string} [options.sourceDir] - the vendored FastCtx tree.
|
|
123
|
+
* @param {string} [options.platform] - `process.platform`.
|
|
124
|
+
* @returns {{file: string, mtimeMs: number}|undefined} the binary, when it exists.
|
|
125
|
+
*/
|
|
126
|
+
export function builtBinary({ sourceDir = VENDOR_DIR, platform = process.platform } = {}) {
|
|
127
|
+
const file = path.join(sourceDir, 'target', 'release', executableName(platform))
|
|
128
|
+
try {
|
|
129
|
+
const stat = fs.statSync(file)
|
|
130
|
+
return stat.isFile() ? { file, mtimeMs: stat.mtimeMs } : undefined
|
|
131
|
+
} catch {
|
|
132
|
+
return undefined
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Whether the vendored source is newer than an existing release build, so a
|
|
138
|
+
* rebuild is required before that binary may be trusted.
|
|
139
|
+
* @param {object} [options] - the comparison inputs.
|
|
140
|
+
* @param {string} [options.sourceDir] - the vendored FastCtx tree.
|
|
141
|
+
* @param {string} [options.platform] - `process.platform`.
|
|
142
|
+
* @returns {{stale: boolean, newestSourceMs: number, builtMs: number}|undefined} the verdict, absent when there is no build.
|
|
143
|
+
*/
|
|
144
|
+
export function buildFreshness({ sourceDir = VENDOR_DIR, platform = process.platform } = {}) {
|
|
145
|
+
const built = builtBinary({ sourceDir, platform })
|
|
146
|
+
if (built === undefined) return undefined
|
|
147
|
+
let newest = 0
|
|
148
|
+
const roots = [path.join(sourceDir, 'src'), path.join(sourceDir, 'build.rs'), path.join(sourceDir, 'Cargo.toml')]
|
|
149
|
+
for (const root of roots) {
|
|
150
|
+
if (!fs.existsSync(root)) continue
|
|
151
|
+
const stat = fs.statSync(root)
|
|
152
|
+
if (stat.isFile()) {
|
|
153
|
+
newest = Math.max(newest, stat.mtimeMs)
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
for (const entry of fs.readdirSync(root, { recursive: true, withFileTypes: true })) {
|
|
157
|
+
if (!entry.isFile()) continue
|
|
158
|
+
newest = Math.max(newest, fs.statSync(path.join(entry.parentPath, entry.name)).mtimeMs)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { stale: newest > built.mtimeMs, newestSourceMs: newest, builtMs: built.mtimeMs }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Run `cargo build --release` over the vendored FastCtx source, streaming its
|
|
166
|
+
* output to the caller's terminal.
|
|
167
|
+
* @param {object} [options] - build inputs.
|
|
168
|
+
* @param {object} [options.cargo] - the toolchain from {@link findCargo}.
|
|
169
|
+
* @param {string} [options.sourceDir] - the vendored FastCtx tree.
|
|
170
|
+
* @param {string[]} [options.extraArgs] - additional cargo arguments.
|
|
171
|
+
* @param {(line: string) => void} [options.onLine] - receives each output line.
|
|
172
|
+
* @returns {Promise<{code: number, output: string}>} the exit code and everything cargo wrote.
|
|
173
|
+
*/
|
|
174
|
+
export function cargoBuild({ cargo, sourceDir = VENDOR_DIR, extraArgs = [], onLine } = {}) {
|
|
175
|
+
const args = ['build', '--release', '--locked', ...extraArgs]
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
const child = spawn(cargo.file, args, {
|
|
178
|
+
cwd: sourceDir,
|
|
179
|
+
env: cargo.env,
|
|
180
|
+
windowsHide: true,
|
|
181
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
182
|
+
})
|
|
183
|
+
let output = ''
|
|
184
|
+
const forward = (chunk) => {
|
|
185
|
+
const text = chunk.toString('utf8')
|
|
186
|
+
output += text
|
|
187
|
+
if (onLine !== undefined) for (const line of text.split('\n')) if (line !== '') onLine(line)
|
|
188
|
+
}
|
|
189
|
+
child.stdout.on('data', forward)
|
|
190
|
+
child.stderr.on('data', forward)
|
|
191
|
+
child.once('error', reject)
|
|
192
|
+
child.once('close', (code) => resolve({ code: code ?? 1, output }))
|
|
193
|
+
})
|
|
194
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Plugin-owned bash layers; permission changes never mutate the shared registry. */
|
|
2
|
+
import { BASH_TOOL } from './policy.js'
|
|
3
|
+
import { bashToolDefinition } from './shells.js'
|
|
4
|
+
|
|
5
|
+
export function mountSessionBash(ctx, config, shells) {
|
|
6
|
+
if (!config.publishBashTool) return
|
|
7
|
+
const bash = shells.bash
|
|
8
|
+
if (!bash.available) {
|
|
9
|
+
ctx.logger?.warn?.(`dsh-ops: bash unavailable: ${bash.detail}`)
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
const entries = new Map()
|
|
13
|
+
let ready = false
|
|
14
|
+
let stopped = false
|
|
15
|
+
const allowed = agent => {
|
|
16
|
+
if (!ready || stopped || !agent?.session) return false
|
|
17
|
+
try { return ctx.get('sandboxPolicy')?.resolve({ session: agent.session })?.mode === 'danger-full-access' }
|
|
18
|
+
catch { return false }
|
|
19
|
+
}
|
|
20
|
+
const clear = entry => {
|
|
21
|
+
entry.dispose?.()
|
|
22
|
+
entry.dispose = undefined
|
|
23
|
+
}
|
|
24
|
+
const reconcile = () => {
|
|
25
|
+
for (const entry of entries.values()) clear(entry)
|
|
26
|
+
// Publish authorized ancestors before masking inheritance in restricted children.
|
|
27
|
+
for (const [agent, entry] of entries) {
|
|
28
|
+
if (!entry.ctx || !allowed(agent)) continue
|
|
29
|
+
const definition = bashToolDefinition({ file: bash.file, source: bash.source, subprocess: ctx.get('subprocess') })
|
|
30
|
+
const execute = definition.execute
|
|
31
|
+
definition.execute = (args, exec) => {
|
|
32
|
+
if (!allowed(exec?.agent)) throw new Error('Bash requires authoritative danger-full-access for this session.')
|
|
33
|
+
return execute({ ...args, workdir: args?.workdir ?? exec.agent.session.header?.cwd }, exec)
|
|
34
|
+
}
|
|
35
|
+
try { entry.dispose = entry.ctx.tools.register(definition) }
|
|
36
|
+
catch (error) { ctx.logger?.warn?.(`dsh-ops: bash publication failed: ${error.message}`) }
|
|
37
|
+
}
|
|
38
|
+
for (const [agent, entry] of entries) {
|
|
39
|
+
if (!entry.ctx || allowed(agent) || stopped) continue
|
|
40
|
+
if ((ctx.get('tools').schemas(agent) ?? []).some(tool => tool.name === BASH_TOOL)) {
|
|
41
|
+
try { entry.dispose = entry.ctx.tools.restrict({ deny: [BASH_TOOL] }) }
|
|
42
|
+
catch (error) { ctx.logger?.warn?.(`dsh-ops: inherited bash restriction failed: ${error.message}`) }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const attach = agent => {
|
|
47
|
+
if (stopped || !agent?.ctx || entries.has(agent)) return
|
|
48
|
+
const entry = { ctx: undefined, fiber: undefined, dispose: undefined }
|
|
49
|
+
entries.set(agent, entry)
|
|
50
|
+
entry.fiber = agent.ctx.plugin(child => {
|
|
51
|
+
child.inject(['tools'], toolsCtx => {
|
|
52
|
+
entry.ctx = toolsCtx
|
|
53
|
+
reconcile()
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
const detach = agent => {
|
|
58
|
+
const entry = entries.get(agent)
|
|
59
|
+
if (!entry) return
|
|
60
|
+
entries.delete(agent)
|
|
61
|
+
clear(entry)
|
|
62
|
+
reconcile()
|
|
63
|
+
return entry.fiber?.dispose()
|
|
64
|
+
}
|
|
65
|
+
ctx.effect(() => async () => {
|
|
66
|
+
stopped = true
|
|
67
|
+
await Promise.all([...entries.keys()].map(detach))
|
|
68
|
+
}, 'dsh-ops: session bash layers')
|
|
69
|
+
ctx.on('agent/created', ({ agent }) => attach(agent))
|
|
70
|
+
ctx.on('agent/disposed', ({ agent }) => detach(agent))
|
|
71
|
+
ctx.on('session/event', (_session, event) => { if (event.type === 'sandbox/mode') reconcile() })
|
|
72
|
+
ctx.inject(['sandboxPolicy', 'subprocess'], authorityCtx => {
|
|
73
|
+
ready = true
|
|
74
|
+
reconcile()
|
|
75
|
+
authorityCtx.effect(() => () => { ready = false; reconcile() }, 'dsh-ops: bash authority and executor')
|
|
76
|
+
})
|
|
77
|
+
for (const agent of ctx.get('agents')?.list?.() ?? []) attach(agent)
|
|
78
|
+
}
|