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.
Files changed (140) hide show
  1. package/CHANGELOG.md +202 -0
  2. package/LICENSE +30 -0
  3. package/NOTICE +106 -0
  4. package/PROVENANCE.md +435 -0
  5. package/README.en.md +126 -0
  6. package/README.md +115 -2
  7. package/README.zh.md +116 -0
  8. package/bin/dsh-ops.mjs +1216 -0
  9. package/cordis.patch.yml +160 -0
  10. package/docs/manual-validation.md +53 -0
  11. package/docs/release-0.2.1.md +72 -0
  12. package/docs/schema-baseline.json +64 -0
  13. package/docs/schema-current.json +84 -0
  14. package/docs/schema-measurement.md +17 -0
  15. package/dsh-plugin.json +88 -0
  16. package/icon.svg +12 -0
  17. package/lib/binary.js +409 -0
  18. package/lib/config.js +198 -0
  19. package/lib/handshake.js +252 -0
  20. package/lib/index.js +108 -0
  21. package/lib/jobs.js +42 -0
  22. package/lib/policy.js +64 -0
  23. package/lib/presentation.js +63 -0
  24. package/lib/profile-install.js +61 -0
  25. package/lib/rust.js +194 -0
  26. package/lib/session-shells.js +78 -0
  27. package/lib/shells.js +998 -0
  28. package/lib/tools.js +657 -0
  29. package/locale/en.json +6 -0
  30. package/locale/zh.json +6 -0
  31. package/package.json +114 -4
  32. package/vendor/fastctx/Cargo.lock +3210 -0
  33. package/vendor/fastctx/Cargo.toml +94 -0
  34. package/vendor/fastctx/FORK.md +119 -0
  35. package/vendor/fastctx/LICENSE-APACHE +201 -0
  36. package/vendor/fastctx/NOTICE +40 -0
  37. package/vendor/fastctx/README.md +439 -0
  38. package/vendor/fastctx/THIRD_PARTY_LICENSES.md +17 -0
  39. package/vendor/fastctx/THIRD_PARTY_LICENSES_RUST.md +7914 -0
  40. package/vendor/fastctx/UPSTREAM.md +49 -0
  41. package/vendor/fastctx/build.rs +413 -0
  42. package/vendor/fastctx/src/background_status.rs +403 -0
  43. package/vendor/fastctx/src/binary.rs +75 -0
  44. package/vendor/fastctx/src/bounded_sort.rs +500 -0
  45. package/vendor/fastctx/src/budget.rs +781 -0
  46. package/vendor/fastctx/src/cli/mod.rs +110 -0
  47. package/vendor/fastctx/src/context_guard.rs +289 -0
  48. package/vendor/fastctx/src/control/mod.rs +6 -0
  49. package/vendor/fastctx/src/control/paths.rs +49 -0
  50. package/vendor/fastctx/src/control/settings.rs +753 -0
  51. package/vendor/fastctx/src/control/transaction.rs +531 -0
  52. package/vendor/fastctx/src/edit/document.rs +535 -0
  53. package/vendor/fastctx/src/edit/locks.rs +371 -0
  54. package/vendor/fastctx/src/edit/mod.rs +213 -0
  55. package/vendor/fastctx/src/edit/private_storage/unix.rs +315 -0
  56. package/vendor/fastctx/src/edit/private_storage/windows.rs +793 -0
  57. package/vendor/fastctx/src/edit/private_storage.rs +234 -0
  58. package/vendor/fastctx/src/edit/replace.rs +1030 -0
  59. package/vendor/fastctx/src/edit_server.rs +53 -0
  60. package/vendor/fastctx/src/encoding/reference_v011.rs +587 -0
  61. package/vendor/fastctx/src/encoding/snapshot_pipeline.rs +1678 -0
  62. package/vendor/fastctx/src/encoding.rs +1118 -0
  63. package/vendor/fastctx/src/file_executor.rs +1151 -0
  64. package/vendor/fastctx/src/file_snapshot.rs +1491 -0
  65. package/vendor/fastctx/src/glob_filter.rs +98 -0
  66. package/vendor/fastctx/src/glob_tool.rs +653 -0
  67. package/vendor/fastctx/src/grep_sink.rs +1162 -0
  68. package/vendor/fastctx/src/grep_tool.rs +2449 -0
  69. package/vendor/fastctx/src/lib.rs +45 -0
  70. package/vendor/fastctx/src/main.rs +15 -0
  71. package/vendor/fastctx/src/model.rs +51 -0
  72. package/vendor/fastctx/src/model_guidance.rs +62 -0
  73. package/vendor/fastctx/src/operation.rs +356 -0
  74. package/vendor/fastctx/src/ordered_window.rs +1235 -0
  75. package/vendor/fastctx/src/os_environment.rs +414 -0
  76. package/vendor/fastctx/src/path_codec.rs +850 -0
  77. package/vendor/fastctx/src/paths.rs +244 -0
  78. package/vendor/fastctx/src/process_identity.rs +763 -0
  79. package/vendor/fastctx/src/process_policy.rs +74 -0
  80. package/vendor/fastctx/src/read_tool/batch.rs +496 -0
  81. package/vendor/fastctx/src/read_tool/hex_file.rs +141 -0
  82. package/vendor/fastctx/src/read_tool/image_file.rs +88 -0
  83. package/vendor/fastctx/src/read_tool/mod.rs +245 -0
  84. package/vendor/fastctx/src/read_tool/pdf.rs +470 -0
  85. package/vendor/fastctx/src/read_tool/pdf_disabled.rs +47 -0
  86. package/vendor/fastctx/src/read_tool/pdf_engine.rs +664 -0
  87. package/vendor/fastctx/src/read_tool/text_file.rs +351 -0
  88. package/vendor/fastctx/src/render_plan.rs +468 -0
  89. package/vendor/fastctx/src/runtime/activity.rs +159 -0
  90. package/vendor/fastctx/src/runtime/hosts.rs +99 -0
  91. package/vendor/fastctx/src/runtime/journal.rs +556 -0
  92. package/vendor/fastctx/src/runtime/local_ipc.rs +186 -0
  93. package/vendor/fastctx/src/runtime/mod.rs +746 -0
  94. package/vendor/fastctx/src/runtime/protocol.rs +296 -0
  95. package/vendor/fastctx/src/runtime/session.rs +536 -0
  96. package/vendor/fastctx/src/runtime/windows_process.rs +66 -0
  97. package/vendor/fastctx/src/search_parallelism.rs +106 -0
  98. package/vendor/fastctx/src/search_text.rs +227 -0
  99. package/vendor/fastctx/src/server.rs +359 -0
  100. package/vendor/fastctx/src/server_manifest.rs +468 -0
  101. package/vendor/fastctx/src/server_support.rs +826 -0
  102. package/vendor/fastctx/src/session.rs +629 -0
  103. package/vendor/fastctx/src/shell/apply_patch_hint.rs +41 -0
  104. package/vendor/fastctx/src/shell/bash.rs +263 -0
  105. package/vendor/fastctx/src/shell/buffer.rs +108 -0
  106. package/vendor/fastctx/src/shell/encoding.rs +403 -0
  107. package/vendor/fastctx/src/shell/foreground.rs +115 -0
  108. package/vendor/fastctx/src/shell/jobs/admission.rs +91 -0
  109. package/vendor/fastctx/src/shell/jobs/background.rs +146 -0
  110. package/vendor/fastctx/src/shell/jobs/host.rs +830 -0
  111. package/vendor/fastctx/src/shell/jobs/identity.rs +29 -0
  112. package/vendor/fastctx/src/shell/jobs/mod.rs +1513 -0
  113. package/vendor/fastctx/src/shell/jobs/model.rs +244 -0
  114. package/vendor/fastctx/src/shell/jobs/output_log.rs +1148 -0
  115. package/vendor/fastctx/src/shell/jobs/store.rs +1300 -0
  116. package/vendor/fastctx/src/shell/mod.rs +345 -0
  117. package/vendor/fastctx/src/shell/normalize.rs +389 -0
  118. package/vendor/fastctx/src/shell/output.rs +406 -0
  119. package/vendor/fastctx/src/shell/process.rs +493 -0
  120. package/vendor/fastctx/src/shell_server.rs +156 -0
  121. package/vendor/fastctx/src/skip_report.rs +83 -0
  122. package/vendor/fastctx/src/stdio_transport.rs +177 -0
  123. package/vendor/fastctx/src/tool_schema.rs +204 -0
  124. package/vendor/fastctx/src/traversal.rs +846 -0
  125. package/vendor/fastctx/third-party/pdfium-7763/LICENSE +9 -0
  126. package/vendor/fastctx/third-party/pdfium-7763/licenses/abseil.txt +202 -0
  127. package/vendor/fastctx/third-party/pdfium-7763/licenses/agg23.txt +14 -0
  128. package/vendor/fastctx/third-party/pdfium-7763/licenses/fast_float.txt +27 -0
  129. package/vendor/fastctx/third-party/pdfium-7763/licenses/freetype.txt +169 -0
  130. package/vendor/fastctx/third-party/pdfium-7763/licenses/icu.txt +542 -0
  131. package/vendor/fastctx/third-party/pdfium-7763/licenses/lcms.txt +27 -0
  132. package/vendor/fastctx/third-party/pdfium-7763/licenses/libjpeg_turbo.ijg +260 -0
  133. package/vendor/fastctx/third-party/pdfium-7763/licenses/libjpeg_turbo.md +135 -0
  134. package/vendor/fastctx/third-party/pdfium-7763/licenses/libopenjpeg.txt +32 -0
  135. package/vendor/fastctx/third-party/pdfium-7763/licenses/libpng.txt +134 -0
  136. package/vendor/fastctx/third-party/pdfium-7763/licenses/libtiff.txt +21 -0
  137. package/vendor/fastctx/third-party/pdfium-7763/licenses/llvm-libc.txt +278 -0
  138. package/vendor/fastctx/third-party/pdfium-7763/licenses/pdfium.txt +230 -0
  139. package/vendor/fastctx/third-party/pdfium-7763/licenses/simdutf.txt +18 -0
  140. 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
+ }