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
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
//! fastshell core: bash-backed foreground commands and bounded background jobs.
|
|
2
|
+
|
|
3
|
+
mod apply_patch_hint;
|
|
4
|
+
pub(crate) mod bash;
|
|
5
|
+
mod buffer;
|
|
6
|
+
mod encoding;
|
|
7
|
+
mod foreground;
|
|
8
|
+
pub(crate) mod jobs;
|
|
9
|
+
mod normalize;
|
|
10
|
+
mod output;
|
|
11
|
+
mod process;
|
|
12
|
+
|
|
13
|
+
use crate::model::ToolResponse;
|
|
14
|
+
use crate::paths::{canonical_existing, display_path, parse_input_path};
|
|
15
|
+
use bash::BashLocator;
|
|
16
|
+
use jobs::JobManager;
|
|
17
|
+
use schemars::JsonSchema;
|
|
18
|
+
use serde::Deserialize;
|
|
19
|
+
use std::path::PathBuf;
|
|
20
|
+
use std::sync::Arc;
|
|
21
|
+
use std::time::SystemTime;
|
|
22
|
+
|
|
23
|
+
const DEFAULT_TIMEOUT_MS: u64 = 120_000;
|
|
24
|
+
pub(crate) const MAX_BLOCKING_CALL_MS: u64 = 240_000;
|
|
25
|
+
const DEFAULT_WAIT_MS: u64 = 30_000;
|
|
26
|
+
|
|
27
|
+
fn default_login_shell() -> bool {
|
|
28
|
+
true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fn default_wait_ms() -> Option<u64> {
|
|
32
|
+
Some(DEFAULT_WAIT_MS)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Parameters for a foreground bash command.
|
|
36
|
+
#[derive(Clone, Debug, Deserialize, JsonSchema)]
|
|
37
|
+
#[serde(deny_unknown_fields)]
|
|
38
|
+
pub struct RunRequest {
|
|
39
|
+
/// The bash command line to run (passed to bash).
|
|
40
|
+
pub command: String,
|
|
41
|
+
/// Absolute path of the working directory. Omit for the session working directory.
|
|
42
|
+
pub cwd: Option<String>,
|
|
43
|
+
/// Kill the command (whole process tree) after this many milliseconds.
|
|
44
|
+
#[schemars(range(min = 1, max = 240_000))]
|
|
45
|
+
pub timeout_ms: Option<u64>,
|
|
46
|
+
/// Run with a login shell (bash -lc) so your profile is loaded (PATH for nvm/cargo/pyenv).
|
|
47
|
+
/// Set false for a clean non-login shell (--noprofile --norc) that skips profile side effects.
|
|
48
|
+
#[serde(default = "default_login_shell")]
|
|
49
|
+
#[schemars(default = "default_login_shell")]
|
|
50
|
+
pub login_shell: bool,
|
|
51
|
+
/// Known source encoding of the command's output, as a WHATWG label like "gbk" or
|
|
52
|
+
/// "shift_jis". The response is always UTF-8. Omit for automatic detection.
|
|
53
|
+
pub encoding: Option<String>,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Parameters for starting a background bash command.
|
|
57
|
+
#[derive(Clone, Debug, Deserialize, JsonSchema)]
|
|
58
|
+
#[serde(deny_unknown_fields)]
|
|
59
|
+
pub struct RunBackgroundRequest {
|
|
60
|
+
/// The bash command line to run (passed to bash).
|
|
61
|
+
pub command: String,
|
|
62
|
+
/// Absolute path of the working directory. Omit for the session working directory.
|
|
63
|
+
pub cwd: Option<String>,
|
|
64
|
+
/// Same as run: login shell (bash -lc) by default; false for a clean non-login shell.
|
|
65
|
+
#[serde(default = "default_login_shell")]
|
|
66
|
+
#[schemars(default = "default_login_shell")]
|
|
67
|
+
pub login_shell: bool,
|
|
68
|
+
/// Default source encoding for this job's output when shown by job_output (WHATWG label
|
|
69
|
+
/// like "gbk"). Each job_output call may override it.
|
|
70
|
+
pub encoding: Option<String>,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Parameters for querying a background job.
|
|
74
|
+
#[derive(Clone, Debug, Deserialize, JsonSchema)]
|
|
75
|
+
#[serde(deny_unknown_fields)]
|
|
76
|
+
pub struct JobOutputRequest {
|
|
77
|
+
/// The job id returned by run_background.
|
|
78
|
+
pub job_id: String,
|
|
79
|
+
/// How long this query may take, in milliseconds. It returns earlier only when the job ends.
|
|
80
|
+
/// Use 0 for an immediate snapshot.
|
|
81
|
+
#[schemars(default = "default_wait_ms", range(min = 0, max = 240_000))]
|
|
82
|
+
pub wait_ms: Option<u64>,
|
|
83
|
+
/// Return output after this line number of the job's log. Omit to continue where your last
|
|
84
|
+
/// call left off; pass it to re-read a stretch you already saw, for example with a different
|
|
85
|
+
/// encoding.
|
|
86
|
+
#[schemars(range(min = 0))]
|
|
87
|
+
pub after_seq: Option<u64>,
|
|
88
|
+
/// Decode this job's stored output with this source encoding for this call (WHATWG label
|
|
89
|
+
/// like "gbk"). Overrides the job's default. The response is always UTF-8.
|
|
90
|
+
pub encoding: Option<String>,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Parameters for terminating a background job tree.
|
|
94
|
+
#[derive(Clone, Debug, Deserialize, JsonSchema)]
|
|
95
|
+
#[serde(deny_unknown_fields)]
|
|
96
|
+
pub struct JobKillRequest {
|
|
97
|
+
/// The job id returned by run_background.
|
|
98
|
+
pub job_id: String,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Parameters for listing persistent background jobs.
|
|
102
|
+
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
|
|
103
|
+
#[serde(deny_unknown_fields)]
|
|
104
|
+
pub struct JobListRequest {
|
|
105
|
+
/// Lifecycle subset to list: "running" (default, still-alive process trees), "finished" (retained exited and interrupted records), or "all". Omit for currently running jobs.
|
|
106
|
+
#[serde(default)]
|
|
107
|
+
#[schemars(default)]
|
|
108
|
+
pub status: JobListStatus,
|
|
109
|
+
/// Maximum records in this page. Omit to use `fastshell.job_list_limit` (default 20).
|
|
110
|
+
#[schemars(range(min = 1, max = 100))]
|
|
111
|
+
pub limit: Option<i64>,
|
|
112
|
+
/// Skip this many entries of the sorted list (from a prior Partial note's offset).
|
|
113
|
+
#[schemars(range(min = 0))]
|
|
114
|
+
pub offset: Option<i64>,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Lifecycle subset exposed by `job_list`.
|
|
118
|
+
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq)]
|
|
119
|
+
#[serde(rename_all = "lowercase")]
|
|
120
|
+
pub enum JobListStatus {
|
|
121
|
+
/// Jobs whose process trees are still alive.
|
|
122
|
+
#[default]
|
|
123
|
+
Running,
|
|
124
|
+
/// Retained exited and interrupted records.
|
|
125
|
+
Finished,
|
|
126
|
+
/// Running jobs followed by retained terminal records.
|
|
127
|
+
All,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Stateful shell service shared by all five tools in one MCP connection.
|
|
131
|
+
#[derive(Clone, Debug)]
|
|
132
|
+
pub struct FastShell {
|
|
133
|
+
bash: Arc<BashLocator>,
|
|
134
|
+
jobs: JobManager,
|
|
135
|
+
session: Arc<crate::session::SessionContext>,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
impl FastShell {
|
|
139
|
+
/// Creates a shell service whose background-job registry is shared across server restarts.
|
|
140
|
+
pub fn new() -> Self {
|
|
141
|
+
Self::with_session(crate::session::SessionContext::library_default())
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Creates a shell service bound to one immutable MCP connection.
|
|
145
|
+
pub(crate) fn with_session(session: Arc<crate::session::SessionContext>) -> Self {
|
|
146
|
+
Self {
|
|
147
|
+
bash: Arc::new(BashLocator::default()),
|
|
148
|
+
jobs: JobManager::with_session(Arc::clone(&session)),
|
|
149
|
+
session,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Executes a bounded foreground command.
|
|
154
|
+
pub fn run(&self, request: RunRequest) -> ToolResponse {
|
|
155
|
+
self.run_until_cancelled(request, || false)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
pub(crate) fn run_until_cancelled(
|
|
159
|
+
&self,
|
|
160
|
+
request: RunRequest,
|
|
161
|
+
cancelled: impl Fn() -> bool,
|
|
162
|
+
) -> ToolResponse {
|
|
163
|
+
if request.command.trim().is_empty() {
|
|
164
|
+
return ToolResponse::error("Invalid command: it must be a non-empty string.");
|
|
165
|
+
}
|
|
166
|
+
let timeout_ms = request.timeout_ms.unwrap_or(DEFAULT_TIMEOUT_MS);
|
|
167
|
+
if !(1..=MAX_BLOCKING_CALL_MS).contains(&timeout_ms) {
|
|
168
|
+
return invalid_timeout(timeout_ms);
|
|
169
|
+
}
|
|
170
|
+
if let Err(error) = output::validate_run_budget(timeout_ms) {
|
|
171
|
+
return ToolResponse::error(error);
|
|
172
|
+
}
|
|
173
|
+
let encoding = match request
|
|
174
|
+
.encoding
|
|
175
|
+
.as_deref()
|
|
176
|
+
.map(encoding::validate_output_encoding)
|
|
177
|
+
.transpose()
|
|
178
|
+
{
|
|
179
|
+
Ok(encoding) => encoding,
|
|
180
|
+
Err(error) => return ToolResponse::error(error),
|
|
181
|
+
};
|
|
182
|
+
let cwd = match resolve_cwd(request.cwd.as_deref(), self.session.environment.cwd()) {
|
|
183
|
+
Ok(cwd) => cwd,
|
|
184
|
+
Err(error) => return ToolResponse::error(error),
|
|
185
|
+
};
|
|
186
|
+
let environment = &self.session.command_environment;
|
|
187
|
+
let bash = match self.bash.resolve(environment) {
|
|
188
|
+
Ok(bash) => bash,
|
|
189
|
+
Err(error) => return ToolResponse::error(error),
|
|
190
|
+
};
|
|
191
|
+
foreground::run(
|
|
192
|
+
foreground::ForegroundCommand {
|
|
193
|
+
bash: &bash,
|
|
194
|
+
command: &request.command,
|
|
195
|
+
cwd: &cwd,
|
|
196
|
+
timeout_ms,
|
|
197
|
+
login_shell: request.login_shell,
|
|
198
|
+
encoding,
|
|
199
|
+
environment,
|
|
200
|
+
utf8_locale: self.bash.utf8_locale(environment, &bash),
|
|
201
|
+
},
|
|
202
|
+
cancelled,
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// Starts a background command and returns immediately.
|
|
207
|
+
pub fn run_background(&self, request: RunBackgroundRequest) -> ToolResponse {
|
|
208
|
+
if request.command.trim().is_empty() {
|
|
209
|
+
return ToolResponse::error("Invalid command: it must be a non-empty string.");
|
|
210
|
+
}
|
|
211
|
+
let encoding = match request
|
|
212
|
+
.encoding
|
|
213
|
+
.as_deref()
|
|
214
|
+
.map(encoding::validate_output_encoding)
|
|
215
|
+
.transpose()
|
|
216
|
+
{
|
|
217
|
+
Ok(encoding) => encoding,
|
|
218
|
+
Err(error) => return ToolResponse::error(error),
|
|
219
|
+
};
|
|
220
|
+
let cwd = match resolve_cwd(request.cwd.as_deref(), self.session.environment.cwd()) {
|
|
221
|
+
Ok(cwd) => cwd,
|
|
222
|
+
Err(error) => return ToolResponse::error(error),
|
|
223
|
+
};
|
|
224
|
+
let environment = &self.session.command_environment;
|
|
225
|
+
let bash = match self.bash.resolve(environment) {
|
|
226
|
+
Ok(bash) => bash,
|
|
227
|
+
Err(error) => return ToolResponse::error(error),
|
|
228
|
+
};
|
|
229
|
+
self.jobs.start(jobs::BackgroundLaunch {
|
|
230
|
+
bash: &bash,
|
|
231
|
+
command: &request.command,
|
|
232
|
+
cwd: &cwd,
|
|
233
|
+
login_shell: request.login_shell,
|
|
234
|
+
encoding,
|
|
235
|
+
environment,
|
|
236
|
+
utf8_locale: self.bash.utf8_locale(environment, &bash),
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/// Returns output after an explicit sequence anchor or the server-side cursor.
|
|
241
|
+
pub fn job_output(&self, request: JobOutputRequest) -> ToolResponse {
|
|
242
|
+
self.job_output_until_cancelled(request, || false)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
pub(crate) fn job_output_until_cancelled(
|
|
246
|
+
&self,
|
|
247
|
+
request: JobOutputRequest,
|
|
248
|
+
cancelled: impl Fn() -> bool,
|
|
249
|
+
) -> ToolResponse {
|
|
250
|
+
let wait_ms = request.wait_ms.unwrap_or(DEFAULT_WAIT_MS);
|
|
251
|
+
if wait_ms > MAX_BLOCKING_CALL_MS {
|
|
252
|
+
return ToolResponse::error(format!(
|
|
253
|
+
"Invalid wait_ms value: {wait_ms}. Expected an integer from 0 to 240000."
|
|
254
|
+
));
|
|
255
|
+
}
|
|
256
|
+
let encoding = match request
|
|
257
|
+
.encoding
|
|
258
|
+
.as_deref()
|
|
259
|
+
.map(encoding::validate_output_encoding)
|
|
260
|
+
.transpose()
|
|
261
|
+
{
|
|
262
|
+
Ok(encoding) => encoding,
|
|
263
|
+
Err(error) => return ToolResponse::error(error),
|
|
264
|
+
};
|
|
265
|
+
self.jobs.output_until_cancelled(
|
|
266
|
+
&request.job_id,
|
|
267
|
+
wait_ms,
|
|
268
|
+
request.after_seq,
|
|
269
|
+
encoding,
|
|
270
|
+
cancelled,
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// Terminates a job's whole process tree, or reports its prior exit.
|
|
275
|
+
pub fn job_kill(&self, request: JobKillRequest) -> ToolResponse {
|
|
276
|
+
self.jobs.kill(&request.job_id)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/// Lists a bounded lifecycle subset across every server session.
|
|
280
|
+
pub fn job_list(&self, request: JobListRequest) -> ToolResponse {
|
|
281
|
+
let offset = request.offset.unwrap_or(0);
|
|
282
|
+
if offset < 0 {
|
|
283
|
+
return ToolResponse::error(format!(
|
|
284
|
+
"Invalid offset value: {offset}. Expected a non-negative integer."
|
|
285
|
+
));
|
|
286
|
+
}
|
|
287
|
+
if let Some(limit) = request.limit
|
|
288
|
+
&& !(1..=crate::control::settings::MAX_JOB_LIST_LIMIT as i64).contains(&limit)
|
|
289
|
+
{
|
|
290
|
+
return ToolResponse::error(format!(
|
|
291
|
+
"Invalid limit value: {limit}. Expected an integer from 1 to 100."
|
|
292
|
+
));
|
|
293
|
+
}
|
|
294
|
+
self.jobs.list(
|
|
295
|
+
request.status,
|
|
296
|
+
offset as u64,
|
|
297
|
+
request.limit.map(|limit| limit as u64),
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
pub(crate) fn background_status(
|
|
302
|
+
&self,
|
|
303
|
+
exclude: Option<&str>,
|
|
304
|
+
) -> Option<crate::background_status::BackgroundStatus> {
|
|
305
|
+
self.jobs.background_status_at(exclude, SystemTime::now())
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
impl Default for FastShell {
|
|
310
|
+
fn default() -> Self {
|
|
311
|
+
Self::new()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
fn invalid_timeout(timeout_ms: u64) -> ToolResponse {
|
|
316
|
+
ToolResponse::error(format!(
|
|
317
|
+
"Invalid timeout_ms value: {timeout_ms}. Expected an integer from 1 to 240000."
|
|
318
|
+
))
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
fn resolve_cwd(input: Option<&str>, session_cwd: &std::path::Path) -> Result<PathBuf, String> {
|
|
322
|
+
let path = match input {
|
|
323
|
+
Some(input) => {
|
|
324
|
+
let path = parse_input_path(input);
|
|
325
|
+
if !path.is_absolute() {
|
|
326
|
+
return Err("The cwd parameter must be an absolute path.".to_string());
|
|
327
|
+
}
|
|
328
|
+
if !path.exists() {
|
|
329
|
+
return Err(format!(
|
|
330
|
+
"Working directory does not exist: {}",
|
|
331
|
+
display_path(&path)
|
|
332
|
+
));
|
|
333
|
+
}
|
|
334
|
+
if !path.is_dir() {
|
|
335
|
+
return Err(format!(
|
|
336
|
+
"Working directory is not a directory: {}",
|
|
337
|
+
display_path(&path)
|
|
338
|
+
));
|
|
339
|
+
}
|
|
340
|
+
path
|
|
341
|
+
}
|
|
342
|
+
None => session_cwd.to_path_buf(),
|
|
343
|
+
};
|
|
344
|
+
Ok(canonical_existing(&path).unwrap_or(path))
|
|
345
|
+
}
|