@mmmbuto/masix 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +15 -13
  2. package/install.js +89 -26
  3. package/package.json +4 -3
  4. package/packages/plugin-base/codex-backend/0.1.4/SHA256SUMS +3 -0
  5. package/packages/plugin-base/codex-backend/0.1.4/codex-backend-android-aarch64-termux.pkg +0 -0
  6. package/packages/plugin-base/codex-backend/0.1.4/codex-backend-linux-x86_64.pkg +0 -0
  7. package/packages/plugin-base/codex-backend/0.1.4/codex-backend-macos-aarch64.pkg +0 -0
  8. package/packages/plugin-base/codex-backend/0.1.4/manifest.json +33 -0
  9. package/packages/plugin-base/codex-backend/CHANGELOG.md +17 -0
  10. package/packages/plugin-base/codex-backend/README.md +33 -0
  11. package/packages/plugin-base/codex-backend/source/Cargo.toml +25 -0
  12. package/packages/plugin-base/codex-backend/source/README-PACKAGE.txt +54 -0
  13. package/packages/plugin-base/codex-backend/source/plugin.manifest.json +103 -0
  14. package/packages/plugin-base/codex-backend/source/src/error.rs +60 -0
  15. package/packages/plugin-base/codex-backend/source/src/exec.rs +436 -0
  16. package/packages/plugin-base/codex-backend/source/src/http_backend.rs +1198 -0
  17. package/packages/plugin-base/codex-backend/source/src/lib.rs +328 -0
  18. package/packages/plugin-base/codex-backend/source/src/patch.rs +767 -0
  19. package/packages/plugin-base/codex-backend/source/src/policy.rs +297 -0
  20. package/packages/plugin-base/codex-backend/source/src/tools.rs +72 -0
  21. package/packages/plugin-base/codex-backend/source/src/workspace.rs +433 -0
  22. package/packages/plugin-base/codex-tools/0.1.3/SHA256SUMS +3 -0
  23. package/packages/plugin-base/codex-tools/0.1.3/codex-tools-android-aarch64-termux.pkg +0 -0
  24. package/packages/plugin-base/codex-tools/0.1.3/codex-tools-linux-x86_64.pkg +0 -0
  25. package/packages/plugin-base/codex-tools/0.1.3/codex-tools-macos-aarch64.pkg +0 -0
  26. package/packages/plugin-base/codex-tools/0.1.3/manifest.json +33 -0
  27. package/packages/plugin-base/codex-tools/CHANGELOG.md +17 -0
  28. package/packages/plugin-base/codex-tools/README.md +33 -0
  29. package/packages/plugin-base/codex-tools/source/Cargo.toml +23 -0
  30. package/packages/plugin-base/codex-tools/source/plugin.manifest.json +124 -0
  31. package/packages/plugin-base/codex-tools/source/src/main.rs +995 -0
  32. package/packages/plugin-base/discovery/0.2.4/SHA256SUMS +3 -0
  33. package/packages/plugin-base/discovery/0.2.4/discovery-android-aarch64-termux.pkg +0 -0
  34. package/packages/plugin-base/discovery/0.2.4/discovery-linux-x86_64.pkg +0 -0
  35. package/packages/plugin-base/discovery/0.2.4/discovery-macos-aarch64.pkg +0 -0
  36. package/packages/plugin-base/discovery/0.2.4/manifest.json +31 -0
  37. package/packages/plugin-base/discovery/CHANGELOG.md +17 -0
  38. package/packages/plugin-base/discovery/README.md +48 -0
  39. package/packages/plugin-base/discovery/source/Cargo.toml +14 -0
  40. package/packages/plugin-base/discovery/source/plugin.manifest.json +30 -0
  41. package/packages/plugin-base/discovery/source/src/main.rs +2570 -0
  42. package/prebuilt/masix +0 -0
@@ -0,0 +1,433 @@
1
+ //! Workspace resolution with fallback chain
2
+ //!
3
+ //! Provides robust path resolution that falls back through:
4
+ //! 1. Explicit repo_path if valid and existing
5
+ //! 2. Runtime-provided workdir
6
+ //! 3. Module-configured default root
7
+ //!
8
+ //! All paths are canonicalized and validated against the active execution profile.
9
+
10
+ use crate::CodingError;
11
+ use std::fmt;
12
+ use std::path::{Path, PathBuf};
13
+
14
+ /// Default workspace root for Termux environments
15
+ #[cfg(target_os = "android")]
16
+ pub fn default_workspace_root() -> PathBuf {
17
+ PathBuf::from("/data/data/com.termux/files/home")
18
+ }
19
+
20
+ /// Default workspace root for non-Termux environments
21
+ #[cfg(not(target_os = "android"))]
22
+ pub fn default_workspace_root() -> PathBuf {
23
+ std::env::var("HOME")
24
+ .map(PathBuf::from)
25
+ .or_else(|_| std::env::var("USERPROFILE").map(PathBuf::from))
26
+ .unwrap_or_else(|_| PathBuf::from("/tmp"))
27
+ }
28
+
29
+ /// Execution profile controlling path access scope
30
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31
+ pub enum ExecutionProfile {
32
+ /// Workspace locked - only paths under resolved workspace root
33
+ #[default]
34
+ WorkspaceLocked,
35
+ /// Workspace plus configured roots - workspace + allowlisted paths
36
+ WorkspacePlusRoots,
37
+ /// System unlocked - admin-only, full system access with logging
38
+ SystemUnlocked,
39
+ }
40
+
41
+ impl ExecutionProfile {
42
+ pub fn as_str(&self) -> &'static str {
43
+ match self {
44
+ ExecutionProfile::WorkspaceLocked => "workspace_locked",
45
+ ExecutionProfile::WorkspacePlusRoots => "workspace_plus_roots",
46
+ ExecutionProfile::SystemUnlocked => "system_unlocked",
47
+ }
48
+ }
49
+
50
+ pub fn from_str(s: &str) -> Option<Self> {
51
+ match s.to_lowercase().as_str() {
52
+ "workspace_locked" | "workspacelocked" => Some(ExecutionProfile::WorkspaceLocked),
53
+ "workspace_plus_roots" | "workspaceplusroots" => {
54
+ Some(ExecutionProfile::WorkspacePlusRoots)
55
+ }
56
+ "system_unlocked" | "systemunlocked" => Some(ExecutionProfile::SystemUnlocked),
57
+ _ => None,
58
+ }
59
+ }
60
+ }
61
+
62
+ impl fmt::Display for ExecutionProfile {
63
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64
+ write!(f, "{}", self.as_str())
65
+ }
66
+ }
67
+
68
+ /// Configuration for workspace resolution
69
+ #[derive(Debug, Clone)]
70
+ pub struct WorkspaceConfig {
71
+ /// Explicit repo path from tool input
72
+ pub repo_path: Option<PathBuf>,
73
+ /// Runtime-provided working directory
74
+ pub runtime_workdir: Option<PathBuf>,
75
+ /// Module-configured default root
76
+ pub default_root: Option<PathBuf>,
77
+ /// Additional allowed roots (for WorkspacePlusRoots profile)
78
+ pub allowed_roots: Vec<PathBuf>,
79
+ /// Execution profile
80
+ pub profile: ExecutionProfile,
81
+ }
82
+
83
+ impl Default for WorkspaceConfig {
84
+ fn default() -> Self {
85
+ Self {
86
+ repo_path: None,
87
+ runtime_workdir: None,
88
+ default_root: Some(default_workspace_root()),
89
+ allowed_roots: Vec::new(),
90
+ profile: ExecutionProfile::WorkspaceLocked,
91
+ }
92
+ }
93
+ }
94
+
95
+ impl WorkspaceConfig {
96
+ /// Create a new workspace config with explicit repo path
97
+ pub fn with_repo_path(mut self, path: impl Into<PathBuf>) -> Self {
98
+ let path: PathBuf = path.into();
99
+ if path.as_os_str().is_empty() {
100
+ self.repo_path = None;
101
+ } else {
102
+ self.repo_path = Some(path);
103
+ }
104
+ self
105
+ }
106
+
107
+ /// Set runtime workdir
108
+ pub fn with_runtime_workdir(mut self, path: impl Into<PathBuf>) -> Self {
109
+ self.runtime_workdir = Some(path.into());
110
+ self
111
+ }
112
+
113
+ /// Set default root
114
+ pub fn with_default_root(mut self, path: impl Into<PathBuf>) -> Self {
115
+ self.default_root = Some(path.into());
116
+ self
117
+ }
118
+
119
+ /// Add an allowed root
120
+ pub fn with_allowed_root(mut self, path: impl Into<PathBuf>) -> Self {
121
+ self.allowed_roots.push(path.into());
122
+ self
123
+ }
124
+
125
+ /// Set execution profile
126
+ pub fn with_profile(mut self, profile: ExecutionProfile) -> Self {
127
+ self.profile = profile;
128
+ self
129
+ }
130
+ }
131
+
132
+ /// Resolved workspace with validated root
133
+ #[derive(Debug, Clone)]
134
+ pub struct ResolvedWorkspace {
135
+ /// Canonical workspace root
136
+ pub root: PathBuf,
137
+ /// Source of the resolution
138
+ pub source: WorkspaceSource,
139
+ /// Execution profile in effect
140
+ pub profile: ExecutionProfile,
141
+ /// Canonical allowed roots for WorkspacePlusRoots profile
142
+ pub allowed_roots: Vec<PathBuf>,
143
+ }
144
+
145
+ /// Source of workspace resolution
146
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
147
+ pub enum WorkspaceSource {
148
+ /// Explicit repo_path was used
149
+ ExplicitRepoPath,
150
+ /// Runtime workdir was used
151
+ RuntimeWorkdir,
152
+ /// Default root was used
153
+ DefaultRoot,
154
+ }
155
+
156
+ impl fmt::Display for WorkspaceSource {
157
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158
+ match self {
159
+ WorkspaceSource::ExplicitRepoPath => write!(f, "explicit_repo_path"),
160
+ WorkspaceSource::RuntimeWorkdir => write!(f, "runtime_workdir"),
161
+ WorkspaceSource::DefaultRoot => write!(f, "default_root"),
162
+ }
163
+ }
164
+ }
165
+
166
+ impl ResolvedWorkspace {
167
+ /// Check if a path is within the workspace (or allowed roots)
168
+ pub fn is_path_allowed(&self, path: &Path) -> Result<PathBuf, CodingError> {
169
+ // Canonicalize the path (or its parent if it doesn't exist yet)
170
+ let canonical_path = if path.exists() {
171
+ path.canonicalize().map_err(|e| {
172
+ CodingError::PathSecurityViolation(format!("Cannot canonicalize path: {}", e))
173
+ })?
174
+ } else {
175
+ // For non-existent paths, canonicalize parent and join
176
+ let mut check_path = path.to_path_buf();
177
+ while !check_path.exists() {
178
+ if !check_path.pop() {
179
+ break;
180
+ }
181
+ }
182
+ let canonical_parent = check_path.canonicalize().map_err(|e| {
183
+ CodingError::PathSecurityViolation(format!(
184
+ "Cannot canonicalize parent path: {}",
185
+ e
186
+ ))
187
+ })?;
188
+ canonical_parent.join(path.strip_prefix(&check_path).unwrap_or(path))
189
+ };
190
+
191
+ // Canonicalize workspace root as well to avoid false negatives on platforms
192
+ // where temp/system paths have alias forms (e.g. /var vs /private/var on macOS).
193
+ let canonical_root = self
194
+ .root
195
+ .canonicalize()
196
+ .unwrap_or_else(|_| self.root.clone());
197
+
198
+ // SystemUnlocked allows anything (admin-only)
199
+ if self.profile == ExecutionProfile::SystemUnlocked {
200
+ return Ok(canonical_path);
201
+ }
202
+
203
+ // Check against workspace root
204
+ if canonical_path.starts_with(&canonical_root) {
205
+ return Ok(canonical_path);
206
+ }
207
+
208
+ // Check against allowed roots (for WorkspacePlusRoots profile)
209
+ if self.profile == ExecutionProfile::WorkspacePlusRoots {
210
+ for allowed in &self.allowed_roots {
211
+ let canonical_allowed = allowed.canonicalize().unwrap_or_else(|_| allowed.clone());
212
+ if canonical_path.starts_with(&canonical_allowed) {
213
+ return Ok(canonical_path);
214
+ }
215
+ }
216
+ }
217
+
218
+ Err(CodingError::PathSecurityViolation(format!(
219
+ "Path '{}' is outside workspace root '{}'",
220
+ path.display(),
221
+ canonical_root.display()
222
+ )))
223
+ }
224
+ }
225
+
226
+ /// Resolve workspace using fallback chain
227
+ pub fn resolve_workspace(config: &WorkspaceConfig) -> Result<ResolvedWorkspace, CodingError> {
228
+ let canonical_allowed_roots: Vec<PathBuf> = config
229
+ .allowed_roots
230
+ .iter()
231
+ .filter(|p| p.exists())
232
+ .filter_map(|p| p.canonicalize().ok())
233
+ .collect();
234
+
235
+ let build = |root: PathBuf, source: WorkspaceSource| ResolvedWorkspace {
236
+ root,
237
+ source,
238
+ profile: config.profile,
239
+ allowed_roots: canonical_allowed_roots.clone(),
240
+ };
241
+
242
+ // Try explicit repo_path first
243
+ if let Some(ref repo_path) = config.repo_path {
244
+ if repo_path.exists() {
245
+ let canonical = repo_path.canonicalize().map_err(|e| {
246
+ CodingError::IoError(format!(
247
+ "Cannot canonicalize repo_path '{}': {}",
248
+ repo_path.display(),
249
+ e
250
+ ))
251
+ })?;
252
+ return Ok(build(canonical, WorkspaceSource::ExplicitRepoPath));
253
+ }
254
+ // repo_path was provided but doesn't exist - this is no longer a hard error
255
+ // We fall through to fallbacks
256
+ }
257
+
258
+ // Try runtime workdir
259
+ if let Some(ref workdir) = config.runtime_workdir {
260
+ if workdir.exists() {
261
+ let canonical = workdir.canonicalize().map_err(|e| {
262
+ CodingError::IoError(format!(
263
+ "Cannot canonicalize runtime_workdir '{}': {}",
264
+ workdir.display(),
265
+ e
266
+ ))
267
+ })?;
268
+ return Ok(build(canonical, WorkspaceSource::RuntimeWorkdir));
269
+ }
270
+ }
271
+
272
+ // Fall back to default root
273
+ if let Some(ref default) = config.default_root {
274
+ if default.exists() {
275
+ let canonical = default.canonicalize().map_err(|e| {
276
+ CodingError::IoError(format!(
277
+ "Cannot canonicalize default_root '{}': {}",
278
+ default.display(),
279
+ e
280
+ ))
281
+ })?;
282
+ return Ok(build(canonical, WorkspaceSource::DefaultRoot));
283
+ }
284
+ }
285
+
286
+ // Last resort: use current directory
287
+ let cwd = std::env::current_dir()
288
+ .map_err(|e| CodingError::IoError(format!("Cannot get current directory: {}", e)))?;
289
+ let canonical = cwd
290
+ .canonicalize()
291
+ .map_err(|e| CodingError::IoError(format!("Cannot canonicalize cwd: {}", e)))?;
292
+
293
+ Ok(build(canonical, WorkspaceSource::DefaultRoot))
294
+ }
295
+
296
+ /// Resolve a relative path within a workspace
297
+ pub fn resolve_relative_path(
298
+ relative: &str,
299
+ workspace: &ResolvedWorkspace,
300
+ ) -> Result<PathBuf, CodingError> {
301
+ // Security checks
302
+ let normalized = relative.replace('\\', "/");
303
+
304
+ // Reject absolute paths
305
+ if normalized.starts_with('/') || normalized.starts_with('~') {
306
+ return Err(CodingError::PathSecurityViolation(
307
+ "Absolute/home paths not allowed".into(),
308
+ ));
309
+ }
310
+
311
+ // Reject path traversal
312
+ for component in normalized.split('/') {
313
+ if component == ".." {
314
+ return Err(CodingError::PathSecurityViolation(
315
+ "Path traversal not allowed".into(),
316
+ ));
317
+ }
318
+ }
319
+
320
+ let resolved = workspace.root.join(&normalized);
321
+
322
+ // Verify the path is within workspace
323
+ workspace.is_path_allowed(&resolved)
324
+ }
325
+
326
+ #[cfg(test)]
327
+ mod tests {
328
+ use super::*;
329
+ use std::fs;
330
+
331
+ #[test]
332
+ fn test_execution_profile_from_str() {
333
+ assert_eq!(
334
+ ExecutionProfile::from_str("workspace_locked"),
335
+ Some(ExecutionProfile::WorkspaceLocked)
336
+ );
337
+ assert_eq!(
338
+ ExecutionProfile::from_str("system_unlocked"),
339
+ Some(ExecutionProfile::SystemUnlocked)
340
+ );
341
+ assert_eq!(ExecutionProfile::from_str("invalid"), None);
342
+ }
343
+
344
+ #[test]
345
+ fn test_workspace_config_builder() {
346
+ let config = WorkspaceConfig::default()
347
+ .with_repo_path("/tmp/test")
348
+ .with_profile(ExecutionProfile::WorkspaceLocked);
349
+
350
+ assert_eq!(config.repo_path, Some(PathBuf::from("/tmp/test")));
351
+ assert_eq!(config.profile, ExecutionProfile::WorkspaceLocked);
352
+ }
353
+
354
+ #[test]
355
+ fn test_resolve_workspace_with_existing_path() {
356
+ let tmp_dir = std::env::temp_dir();
357
+ let test_path = tmp_dir.join("masix_test_workspace");
358
+ fs::create_dir_all(&test_path).ok();
359
+
360
+ let config = WorkspaceConfig::default().with_repo_path(&test_path);
361
+ let result = resolve_workspace(&config);
362
+
363
+ assert!(result.is_ok());
364
+ let workspace = result.unwrap();
365
+ assert_eq!(workspace.source, WorkspaceSource::ExplicitRepoPath);
366
+
367
+ fs::remove_dir(&test_path).ok();
368
+ }
369
+
370
+ #[test]
371
+ fn test_resolve_workspace_fallback_to_default() {
372
+ let config = WorkspaceConfig {
373
+ repo_path: Some(PathBuf::from("/nonexistent/path/12345")),
374
+ runtime_workdir: None,
375
+ default_root: Some(std::env::temp_dir()),
376
+ allowed_roots: Vec::new(),
377
+ profile: ExecutionProfile::WorkspaceLocked,
378
+ };
379
+
380
+ let result = resolve_workspace(&config);
381
+ assert!(result.is_ok());
382
+ let workspace = result.unwrap();
383
+ assert_eq!(workspace.source, WorkspaceSource::DefaultRoot);
384
+ }
385
+
386
+ #[test]
387
+ fn test_resolve_relative_path_rejects_absolute() {
388
+ let workspace = ResolvedWorkspace {
389
+ root: PathBuf::from("/tmp"),
390
+ source: WorkspaceSource::DefaultRoot,
391
+ profile: ExecutionProfile::WorkspaceLocked,
392
+ allowed_roots: Vec::new(),
393
+ };
394
+
395
+ let result = resolve_relative_path("/etc/passwd", &workspace);
396
+ assert!(matches!(result, Err(CodingError::PathSecurityViolation(_))));
397
+ }
398
+
399
+ #[test]
400
+ fn test_resolve_relative_path_rejects_traversal() {
401
+ let workspace = ResolvedWorkspace {
402
+ root: PathBuf::from("/tmp"),
403
+ source: WorkspaceSource::DefaultRoot,
404
+ profile: ExecutionProfile::WorkspaceLocked,
405
+ allowed_roots: Vec::new(),
406
+ };
407
+
408
+ let result = resolve_relative_path("../../../etc/passwd", &workspace);
409
+ assert!(matches!(result, Err(CodingError::PathSecurityViolation(_))));
410
+ }
411
+
412
+ #[test]
413
+ fn test_workspace_plus_roots_allows_extra_root() {
414
+ let tmp_dir = std::env::temp_dir();
415
+ let workspace_root = tmp_dir.join("masix_workspace_root");
416
+ let allowed_root = tmp_dir.join("masix_allowed_root");
417
+ fs::create_dir_all(&workspace_root).ok();
418
+ fs::create_dir_all(&allowed_root).ok();
419
+
420
+ let config = WorkspaceConfig::default()
421
+ .with_repo_path(&workspace_root)
422
+ .with_profile(ExecutionProfile::WorkspacePlusRoots)
423
+ .with_allowed_root(&allowed_root);
424
+ let resolved = resolve_workspace(&config).unwrap();
425
+
426
+ let target = allowed_root.join("nested").join("file.txt");
427
+ let allowed = resolved.is_path_allowed(&target);
428
+ assert!(allowed.is_ok());
429
+
430
+ fs::remove_dir_all(&workspace_root).ok();
431
+ fs::remove_dir_all(&allowed_root).ok();
432
+ }
433
+ }
@@ -0,0 +1,3 @@
1
+ b3daf8cfe3531fdf675ddf4578a65c3f5186ad9e490d02ea2a9b90d1653b4920 codex-tools-android-aarch64-termux.pkg
2
+ d1b39e3c8c2c6ca4234a0f4a19f20fb1eb63faf6ce0bde3447b6dcbff08a18b3 codex-tools-linux-x86_64.pkg
3
+ d52ae0ccd97060dce260cb542ac820b7eb0bd664c82820c126c2a7f032c9b383 codex-tools-macos-aarch64.pkg
@@ -0,0 +1,33 @@
1
+ {
2
+ "plugin_id": "codex-tools",
3
+ "version": "0.1.3",
4
+ "visibility": "plugin-base",
5
+ "license": "MIT",
6
+ "admin_only": true,
7
+ "package_type": "mcp_binary",
8
+ "platforms": [
9
+ {
10
+ "id": "android-aarch64-termux",
11
+ "file": "codex-tools-android-aarch64-termux.pkg",
12
+ "sha256": "b3daf8cfe3531fdf675ddf4578a65c3f5186ad9e490d02ea2a9b90d1653b4920"
13
+ },
14
+ {
15
+ "id": "linux-x86_64",
16
+ "file": "codex-tools-linux-x86_64.pkg",
17
+ "sha256": "d1b39e3c8c2c6ca4234a0f4a19f20fb1eb63faf6ce0bde3447b6dcbff08a18b3"
18
+ },
19
+ {
20
+ "id": "macos-aarch64",
21
+ "file": "codex-tools-macos-aarch64.pkg",
22
+ "sha256": "d52ae0ccd97060dce260cb542ac820b7eb0bd664c82820c126c2a7f032c9b383"
23
+ }
24
+ ],
25
+ "install": {
26
+ "command": "masix plugin install-file --file <path-to-pkg> --plugin codex-tools --version 0.1.3 --package-type mcp_binary --admin-only"
27
+ },
28
+ "notes": [
29
+ "Admin-only module. Non-admin users cannot call codex tools.",
30
+ "Package usable without plugin server.",
31
+ "Requires codex-backend enabled for full functionality."
32
+ ]
33
+ }
@@ -0,0 +1,17 @@
1
+ # codex-tools Package Changelog
2
+
3
+ ## 0.1.3 - 2026-03-05
4
+
5
+ - Refreshed package artifacts for android-aarch64-termux, linux-x86_64, macos-aarch64.
6
+ - Added `packages/plugin-base/codex-tools/0.1.3/` with updated `manifest.json` and `SHA256SUMS`.
7
+
8
+
9
+ ## 0.1.2 - 2026-03-02
10
+
11
+ - Added MIT package publication layout under `packages/plugin-base/codex-tools/0.1.2`.
12
+ - Included platform artifacts:
13
+ - `codex-tools-android-aarch64-termux.pkg`
14
+ - `codex-tools-linux-x86_64.pkg`
15
+ - `codex-tools-macos-aarch64.pkg`
16
+ - Added `manifest.json` with platform metadata and checksums.
17
+ - Added `SHA256SUMS` for manual integrity verification.
@@ -0,0 +1,33 @@
1
+ # codex-tools (`plugin-base`)
2
+
3
+ `codex-tools` exposes MCP tools for coding workflows and patch operations.
4
+
5
+ ## What It Is
6
+
7
+ - Package type: `mcp_binary`
8
+ - Visibility: `plugin-base`
9
+ - Admin-only: `true`
10
+ - Distribution: local `.pkg` install supported (`install-file`)
11
+
12
+ ## Install Example
13
+
14
+ ```bash
15
+ masix plugin install-file \
16
+ --file packages/plugin-base/codex-tools/0.1.3/codex-tools-linux-x86_64.pkg \
17
+ --plugin codex-tools \
18
+ --version 0.1.3 \
19
+ --package-type mcp_binary \
20
+ --admin-only
21
+ ```
22
+
23
+ Then enable and restart:
24
+
25
+ ```bash
26
+ masix plugin enable codex-tools
27
+ masix restart
28
+ ```
29
+
30
+ ## Notes
31
+
32
+ - For full coding flow, install `codex-backend` and `codex-tools` together.
33
+ - Tools are filtered by admin policy at runtime.
@@ -0,0 +1,23 @@
1
+ [package]
2
+ name = "masix-plugin-codex-tools"
3
+ version = "0.1.3"
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ description = "MCP binary plugin exposing codex-backend tools for MasiX runtime"
7
+
8
+ [[bin]]
9
+ name = "masix-plugin-codex-tools"
10
+ path = "src/main.rs"
11
+
12
+ [dependencies]
13
+ anyhow.workspace = true
14
+ clap.workspace = true
15
+ masix-plugin-codex-backend = { path = "../codex-backend" }
16
+ serde.workspace = true
17
+ serde_json.workspace = true
18
+ tokio.workspace = true
19
+ tracing.workspace = true
20
+ tracing-subscriber.workspace = true
21
+
22
+ [dev-dependencies]
23
+ # none yet
@@ -0,0 +1,124 @@
1
+ {
2
+ "id": "codex-tools",
3
+ "name": "MasiX Codex Tools",
4
+ "version": "0.1.3",
5
+ "visibility": "plugin-base",
6
+ "package_type": "mcp_binary",
7
+ "entrypoint": "masix-plugin-codex-tools",
8
+ "admin_only": true,
9
+ "permissions": {
10
+ "required": [
11
+ "admin"
12
+ ],
13
+ "description": "Codex tools require admin privileges for file operations and API access"
14
+ },
15
+ "platforms_supported": [
16
+ "linux-x86_64",
17
+ "android-aarch64-termux",
18
+ "macos-aarch64"
19
+ ],
20
+ "platforms_tested": [
21
+ "linux-x86_64",
22
+ "android-aarch64-termux",
23
+ "macos-aarch64"
24
+ ],
25
+ "platforms_planned": [
26
+ "linux-aarch64"
27
+ ],
28
+ "dependencies": {
29
+ "codex-backend": ">=0.1.0"
30
+ },
31
+ "config": {
32
+ "properties": {
33
+ "provider": {
34
+ "type": "string",
35
+ "enum": [
36
+ "openai",
37
+ "anthropic",
38
+ "openai-compatible"
39
+ ],
40
+ "default": "openai"
41
+ },
42
+ "api_key_env": {
43
+ "type": "string",
44
+ "default": "CODEX_API_KEY",
45
+ "description": "Environment variable name for API key"
46
+ },
47
+ "base_url": {
48
+ "type": "string"
49
+ },
50
+ "model": {
51
+ "type": "string"
52
+ },
53
+ "timeout_secs": {
54
+ "type": "integer",
55
+ "default": 300,
56
+ "minimum": 10,
57
+ "maximum": 3600
58
+ }
59
+ }
60
+ },
61
+ "tools": [
62
+ {
63
+ "name": "codex_run",
64
+ "description": "Execute a coding task via HTTP backend",
65
+ "requires_admin": true
66
+ },
67
+ {
68
+ "name": "codex_dry_run",
69
+ "description": "Preview execution settings without mutation",
70
+ "requires_admin": false
71
+ },
72
+ {
73
+ "name": "codex_status",
74
+ "description": "Get module version and health info",
75
+ "requires_admin": false
76
+ },
77
+ {
78
+ "name": "codex_capabilities",
79
+ "description": "Get machine-readable metadata for AI workers",
80
+ "requires_admin": false
81
+ },
82
+ {
83
+ "name": "codex_exec_command",
84
+ "description": "Execute a command in a bounded, secure environment",
85
+ "requires_admin": true
86
+ },
87
+ {
88
+ "name": "codex_patch_preview",
89
+ "description": "Preview a diff patch without applying it",
90
+ "requires_admin": true
91
+ },
92
+ {
93
+ "name": "codex_apply_patch",
94
+ "description": "Apply a diff patch with automatic backup",
95
+ "requires_admin": true
96
+ },
97
+ {
98
+ "name": "codex_rollback_patch",
99
+ "description": "Rollback a previously applied patch using backup ID",
100
+ "requires_admin": true
101
+ }
102
+ ],
103
+ "min_masix_version": "0.3.0",
104
+ "max_masix_version": "0.4.1",
105
+ "capabilities": [
106
+ "http_client",
107
+ "file_read",
108
+ "file_write",
109
+ "command_exec"
110
+ ],
111
+ "constraints": {
112
+ "max_concurrent_tasks": 1,
113
+ "requires_isolation": false,
114
+ "timeout_enforcement": "strict"
115
+ },
116
+ "tool_access": {
117
+ "default_required_role": "admin",
118
+ "per_tool_required_role": {
119
+ "codex_dry_run": "user",
120
+ "codex_status": "user",
121
+ "codex_capabilities": "user"
122
+ }
123
+ }
124
+ }