chore: update all plugins to Nushell 0.111.0
Some checks failed
Build and Test / Validate Setup (push) Has been cancelled
Build and Test / Build (darwin-amd64) (push) Has been cancelled
Build and Test / Build (darwin-arm64) (push) Has been cancelled
Build and Test / Build (linux-amd64) (push) Has been cancelled
Build and Test / Build (windows-amd64) (push) Has been cancelled
Build and Test / Build (linux-arm64) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Package Results (push) Has been cancelled
Build and Test / Quality Gate (push) Has been cancelled
Nightly Build / Check for Changes (push) Has been cancelled
Nightly Build / Validate Setup (push) Has been cancelled
Nightly Build / Nightly Build (darwin-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (darwin-arm64) (push) Has been cancelled
Nightly Build / Nightly Build (linux-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (windows-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (linux-arm64) (push) Has been cancelled
Nightly Build / Create Nightly Pre-release (push) Has been cancelled
Nightly Build / Notify Build Status (push) Has been cancelled
Nightly Build / Nightly Maintenance (push) Has been cancelled

- Bump all 18 plugins from 0.110.0 to 0.111.0
  - Update rust-toolchain.toml channel to 1.93.1 (nu 0.111.0 requires ≥1.91.1)

  Fixes:
  - interprocess pin =2.2.x → ^2.3.1 in nu_plugin_mcp, nu_plugin_nats, nu_plugin_typedialog
    (required by nu-plugin-core 0.111.0)
  - nu_plugin_typedialog: BackendType::Web initializer — add open_browser: false field
  - nu_plugin_auth: implement missing user_info_to_value helper referenced in tests

  Scripts:
  - update_all_plugins.nu: fix [package].version update on minor bumps; add [dev-dependencies]
    pass; add nu-plugin-test-support to managed crates
  - download_nushell.nu: rustup override unset before rm -rf on nushell dir replace;
    fix unclosed ) in string interpolation
This commit is contained in:
Jesús Pérez 2026-03-11 03:22:42 +00:00
parent b6eeaee4da
commit d9ef2f0d5b
201 changed files with 29374 additions and 15475 deletions

View File

@ -1,10 +1,99 @@
# Changelog # Changelog
## [0.111.0] - 2026-03-11 (NUSHELL 0.111.0 COMPATIBILITY)
### 🔧 Version Update
- Nushell 0.111.0 compatibility — all 18 plugins updated
- `rust-toolchain.toml` channel updated to `1.93.1` (nushell 0.111.0 requires rustc ≥1.91.1)
- Removed stale `rustup override` for `nushell/` directory from update toolchain
### 🐛 Fixes
- `interprocess` pin updated `=2.2.x``^2.3.1` in `nu_plugin_mcp`, `nu_plugin_nats`,
`nu_plugin_typedialog` — required by `nu-plugin-core 0.111.0`
- `nu_plugin_typedialog`: `BackendType::Web` initializer updated with new `open_browser: false`
field added in `typedialog-core`
- `nu_plugin_auth`: implemented missing `user_info_to_value` helper referenced in tests
- `download_nushell.nu`: fixed unclosed interpolation delimiter in version mismatch warning
- `update_all_plugins.nu`: fixed version update to also cover `[package].version` on minor bumps
and `[dev-dependencies]` section (`nu-plugin-test-support`); added `nu-plugin-test-support`
to managed crates list
- `download_nushell.nu`: `rustup override unset` before `rm -rf` on nushell directory replace —
prevents stale toolchain overrides surviving source replacement
---
## [0.110.0] - 2026-02-10 (TYPEDIALOG + MCP CLIENT PLUGINS)
### ✨ New Plugins
#### nu_plugin_typedialog
- Replaces `provisioning/core/shlib/` bash TTY wrappers with a native Nushell plugin
- `typedialog form <path> [--backend cli|web] [--port] [--initial <record>]` — execute
interactive form from TOML definition, returns record of results
- `typedialog nickel-roundtrip <ncl> <toml> <ncl> [--no-validate]` — read Nickel → form
→ write back, returns `{output_nickel, validation_passed, form_results, changed}`
- `typedialog text/confirm/select/multi-select/password` — direct prompt primitives
- ESC/cancel returns `null` (Value::nothing), never an error
- Backed by `typedialog-core` crate with CLI and Web backends
- Fixed `interprocess` API break via `=2.2.2` pin (nushell 0.110.0 compat)
#### nu_plugin_mcp
- MCP client plugin — spawns `provisioning-mcp-server` as child process
- Session state held in `Arc<Mutex<Option<McpSession>>>` on plugin struct (persists across
command calls within plugin process lifetime)
- `mcp connect <binary> [--provisioning-path]` — MCP handshake (initialize + initialized)
- `mcp tools list` — returns table `{name, description, required_args}`
- `mcp tool call <name> [--payload <record>]` — dispatches tool, maps content envelope
to Nu values (JSON-parsed text or `{error: true, message: ...}` record)
- `mcp disconnect` — kills child process, clears session
### 🔧 Infrastructure
- `provisioning-mcp-server/src/simple_main.rs` — raw JSON-RPC 2.0 stdio transport
replaces broken `rust-mcp-sdk` dependency; 37 tools restored from `main.rs.disabled`
- `plugin_registry.toml` — added entries for `nu_plugin_typedialog` and `nu_plugin_mcp`
---
## [0.109.0] - 2025-12-15 (KCL & NICKEL PLUGINS WITH CACHING)
### ✨ New Features
#### Configuration Loading Plugins with Caching
- **nu_plugin_kcl** - Enhanced with automatic caching support
- Added `kcl-eval` command for primary config loading with cache
- Added `kcl-cache-status` command for cache diagnostics
- SHA256-based cache keys: `~/.cache/provisioning/config-cache/`
- Expected performance: 20-50x improvement (cache hit ~5ms vs CLI ~300ms)
- **nu_plugin_nickel** - New independent plugin for Nickel configs
- Complete implementation with identical caching architecture
- Added `nickel-eval` command for primary config loading with cache
- Added `nickel-cache-status` command for cache diagnostics
- Shared cache directory with KCL plugin
- Commands: `nickel-eval`, `nickel-export`, `nickel-format`, `nickel-validate`, `nickel-cache-status`
### 🔧 Plugin Registry Enhancements
- Restructured registry format: `[plugins.*]` nested structure
- Added `local_path` field for all plugins
- Added `upstream_branch` field for upstream tracking
- Added `[settings]` section for managed dependencies
- Updated nu_plugin_nickel registry entry with complete metadata
---
## [0.109.0] - 2025-12-11 (COMPREHENSIVE DOCUMENTATION & COMMIT PREPARATION) ## [0.109.0] - 2025-12-11 (COMPREHENSIVE DOCUMENTATION & COMMIT PREPARATION)
### 📚 Documentation Updates ### 📚 Documentation Updates
#### Repository Documentation #### Repository Documentation
- **CHANGES.md** (provisioning/core): Complete summary of core system updates - **CHANGES.md** (provisioning/core): Complete summary of core system updates
- CLI, libraries, plugins, and utilities changes - CLI, libraries, plugins, and utilities changes
- File-by-file breakdown organized by directory - File-by-file breakdown organized by directory
@ -16,6 +105,7 @@
- Ready for: `git commit -F provisioning/core/COMMIT_MESSAGE.md` - Ready for: `git commit -F provisioning/core/COMMIT_MESSAGE.md`
#### Repository Documentation (provisioning/) #### Repository Documentation (provisioning/)
- **CHANGES.md**: Summary of configuration and documentation updates - **CHANGES.md**: Summary of configuration and documentation updates
- Configuration files (config/, kcl/, core/, extensions/, platform/) - Configuration files (config/, kcl/, core/, extensions/, platform/)
- Documentation updates across all modules - Documentation updates across all modules
@ -41,6 +131,7 @@ All repositories now have:
### 🎯 Plugin Exclusion System (2025-12-03) ### 🎯 Plugin Exclusion System (2025-12-03)
#### Architecture Implementation #### Architecture Implementation
- **Configuration-Driven Plugin Exclusion**: - **Configuration-Driven Plugin Exclusion**:
- Central registry in `etc/plugin_registry.toml` for managing exclusions - Central registry in `etc/plugin_registry.toml` for managing exclusions
- Single source of truth for which plugins excluded from distributions - Single source of truth for which plugins excluded from distributions
@ -48,23 +139,27 @@ All repositories now have:
- Future-proof design supporting profiles and conditional exclusions - Future-proof design supporting profiles and conditional exclusions
#### Collection System Enhancement (`scripts/collect_full_binaries.nu`) #### Collection System Enhancement (`scripts/collect_full_binaries.nu`)
- Added `get_excluded_plugins()` helper function to load exclusion list - Added `get_excluded_plugins()` helper function to load exclusion list
- Updated `get_workspace_plugins_info()` to filter excluded workspace plugins - Updated `get_workspace_plugins_info()` to filter excluded workspace plugins
- Updated `get_custom_plugins_info()` to filter excluded custom plugins - Updated `get_custom_plugins_info()` to filter excluded custom plugins
- Distribution collections now exclude specified plugins automatically - Distribution collections now exclude specified plugins automatically
#### Packaging System Enhancement (`scripts/create_distribution_packages.nu`) #### Packaging System Enhancement (`scripts/create_distribution_packages.nu`)
- Added `get_excluded_plugins_dist()` helper function - Added `get_excluded_plugins_dist()` helper function
- Updated `get_plugin_components()` to filter both custom and workspace excluded plugins - Updated `get_plugin_components()` to filter both custom and workspace excluded plugins
- Distribution packages now exclude specified plugins automatically - Distribution packages now exclude specified plugins automatically
- Consistent filtering with collection system - Consistent filtering with collection system
#### Installation Configuration (`scripts/templates/default_config.nu`) #### Installation Configuration (`scripts/templates/default_config.nu`)
- Removed excluded plugins from auto-load plugin list - Removed excluded plugins from auto-load plugin list
- Added documentation explaining why plugins are excluded - Added documentation explaining why plugins are excluded
- Users won't see missing plugin errors in fresh installations - Users won't see missing plugin errors in fresh installations
#### Documentation - Complete Coverage #### Documentation - Complete Coverage
- **User Guide**: `docs/PLUGIN_EXCLUSION_GUIDE.md` (400+ lines) - **User Guide**: `docs/PLUGIN_EXCLUSION_GUIDE.md` (400+ lines)
- Quick start for users, developers, release managers - Quick start for users, developers, release managers
- Common tasks with step-by-step instructions - Common tasks with step-by-step instructions
@ -107,6 +202,7 @@ All repositories now have:
- Testing validation - Testing validation
#### Configuration Updates #### Configuration Updates
- **Registry**: `etc/plugin_registry.toml` - **Registry**: `etc/plugin_registry.toml`
- Added `[distribution]` section with `excluded_plugins` list - Added `[distribution]` section with `excluded_plugins` list
- Marked `nu_plugin_example` as excluded with reason documentation - Marked `nu_plugin_example` as excluded with reason documentation
@ -114,12 +210,14 @@ All repositories now have:
#### Files Modified (9 files total) #### Files Modified (9 files total)
**Implementation** (4 files): **Implementation** (4 files):
- `etc/plugin_registry.toml` - Config: Added `[distribution]` section - `etc/plugin_registry.toml` - Config: Added `[distribution]` section
- `scripts/collect_full_binaries.nu` - Feature: Added filtering functions - `scripts/collect_full_binaries.nu` - Feature: Added filtering functions
- `scripts/create_distribution_packages.nu` - Feature: Added filtering functions - `scripts/create_distribution_packages.nu` - Feature: Added filtering functions
- `scripts/templates/default_config.nu` - Config: Removed excluded from auto-load - `scripts/templates/default_config.nu` - Config: Removed excluded from auto-load
**Documentation** (5 files): **Documentation** (5 files):
- `docs/PLUGIN_EXCLUSION_GUIDE.md` - NEW: User guide - `docs/PLUGIN_EXCLUSION_GUIDE.md` - NEW: User guide
- `docs/architecture/README.md` - NEW: Navigation index - `docs/architecture/README.md` - NEW: Navigation index
- `docs/architecture/PLUGIN_EXCLUSION_SYSTEM.md` - NEW: Technical spec - `docs/architecture/PLUGIN_EXCLUSION_SYSTEM.md` - NEW: Technical spec
@ -128,6 +226,7 @@ All repositories now have:
- `docs/PROVISIONING_PLUGINS_SUMMARY.md` - UPDATED: Added links - `docs/PROVISIONING_PLUGINS_SUMMARY.md` - UPDATED: Added links
#### Impact & Behavior Changes #### Impact & Behavior Changes
- ✅ Build system UNCHANGED - all plugins still built - ✅ Build system UNCHANGED - all plugins still built
- ✅ Test system UNCHANGED - all plugins still tested - ✅ Test system UNCHANGED - all plugins still tested
- ✅ Dev workflows UNCHANGED - developers can use excluded plugins - ✅ Dev workflows UNCHANGED - developers can use excluded plugins
@ -136,6 +235,7 @@ All repositories now have:
- ❌ Auto-load NOW excludes specified plugins from user configs - ❌ Auto-load NOW excludes specified plugins from user configs
#### Example: nu_plugin_example #### Example: nu_plugin_example
- ✅ Still built with `just build` - ✅ Still built with `just build`
- ✅ Still tested with `just test` - ✅ Still tested with `just test`
- ✅ Still available in build output for reference - ✅ Still available in build output for reference
@ -144,6 +244,7 @@ All repositories now have:
- ❌ NOT auto-loaded in user installations - ❌ NOT auto-loaded in user installations
#### Testing & Verification #### Testing & Verification
- ✅ Registry parses correctly - ✅ Registry parses correctly
- ✅ Collection system excludes plugins - ✅ Collection system excludes plugins
- ✅ Packaging system excludes plugins - ✅ Packaging system excludes plugins
@ -154,6 +255,7 @@ All repositories now have:
- ✅ All 1,400+ lines of documentation complete - ✅ All 1,400+ lines of documentation complete
#### Quality Metrics #### Quality Metrics
- **Code Changes**: 40 lines added, 1 line removed (net +39) - **Code Changes**: 40 lines added, 1 line removed (net +39)
- **Documentation**: 1,400+ lines of comprehensive coverage - **Documentation**: 1,400+ lines of comprehensive coverage
- **Error Handling**: 100% graceful degradation - **Error Handling**: 100% graceful degradation
@ -168,6 +270,7 @@ All repositories now have:
### 🚀 Bootstrap Installer & Distribution Improvements (2025-10-19) ### 🚀 Bootstrap Installer & Distribution Improvements (2025-10-19)
#### Install Script Architecture (DRY Design) #### Install Script Architecture (DRY Design)
- **Implemented Symlink-Based DRY Architecture**: - **Implemented Symlink-Based DRY Architecture**:
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines) - Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks: `./install.sh``installers/bootstrap/install.sh` - Symlinks: `./install.sh``installers/bootstrap/install.sh`
@ -176,6 +279,7 @@ All repositories now have:
- No code duplication across installation paths - No code duplication across installation paths
#### Archive Extraction Fixes (Critical) #### Archive Extraction Fixes (Critical)
- **Fixed Archive Binary Detection (Version-Agnostic)**: - **Fixed Archive Binary Detection (Version-Agnostic)**:
- Root cause: `find` command returning parent directory itself in results - Root cause: `find` command returning parent directory itself in results
- Solution: Added `-not -path "$extract_dir"` to exclude starting directory - Solution: Added `-not -path "$extract_dir"` to exclude starting directory
@ -191,6 +295,7 @@ All repositories now have:
- Validates binaries exist before using them - Validates binaries exist before using them
#### Plugin Registration Error Handling #### Plugin Registration Error Handling
- **Improved Plugin Registration with Version Mismatch Detection**: - **Improved Plugin Registration with Version Mismatch Detection**:
- Captures both stdout and stderr from plugin add commands - Captures both stdout and stderr from plugin add commands
- Detects version incompatibility errors: "is not compatible with version" - Detects version incompatibility errors: "is not compatible with version"
@ -202,6 +307,7 @@ All repositories now have:
- Installation continues successfully even with version mismatches - Installation continues successfully even with version mismatches
#### Shell Configuration PATH Update Messaging #### Shell Configuration PATH Update Messaging
- **Fixed Confusing PATH Update Messages**: - **Fixed Confusing PATH Update Messages**:
- Root cause: Script conflated "PATH found" with "PATH needs updating" - Root cause: Script conflated "PATH found" with "PATH needs updating"
- Solution: Track two separate states: - Solution: Track two separate states:
@ -213,6 +319,7 @@ All repositories now have:
- ⚠️ "Could not find..." (when file doesn't exist) - ⚠️ "Could not find..." (when file doesn't exist)
#### Installation Features #### Installation Features
- **`--source-path` Option for Local Installation**: - **`--source-path` Option for Local Installation**:
- Install from local archive: `--source-path archive.tar.gz` - Install from local archive: `--source-path archive.tar.gz`
- Install from local directory: `--source-path /path/to/binaries` - Install from local directory: `--source-path /path/to/binaries`
@ -226,6 +333,7 @@ All repositories now have:
- Preserves user choice (keep or remove config) - Preserves user choice (keep or remove config)
#### Documentation Updates #### Documentation Updates
- **Updated CLAUDE.md** with: - **Updated CLAUDE.md** with:
- Install Script Architecture (DRY Design) section - Install Script Architecture (DRY Design) section
- Source of truth location and symlink structure - Source of truth location and symlink structure
@ -241,6 +349,7 @@ All repositories now have:
- How DRY Works (3-step explanation) - How DRY Works (3-step explanation)
#### Files Modified #### Files Modified
- `installers/bootstrap/install.sh` - All fixes (1,247 lines, +3 lines) - `installers/bootstrap/install.sh` - All fixes (1,247 lines, +3 lines)
- `./install.sh` - Auto-updated via symlink - `./install.sh` - Auto-updated via symlink
- `./scripts/templates/install.sh` - Auto-updated via symlink - `./scripts/templates/install.sh` - Auto-updated via symlink
@ -248,6 +357,7 @@ All repositories now have:
- `README.md` - Added install script section - `README.md` - Added install script section
#### Testing & Verification #### Testing & Verification
- ✅ Archive extraction works with version-agnostic detection - ✅ Archive extraction works with version-agnostic detection
- ✅ Installation to `~/.local` successful (16 binaries) - ✅ Installation to `~/.local` successful (16 binaries)
- ✅ Installation to `~/.local/bin` successful (21 plugins loaded) - ✅ Installation to `~/.local/bin` successful (21 plugins loaded)
@ -256,6 +366,7 @@ All repositories now have:
- ✅ Clean uninstall followed by fresh reinstall works perfectly - ✅ Clean uninstall followed by fresh reinstall works perfectly
#### Impact #### Impact
- ✅ Users can install from any version of nushell-full archive - ✅ Users can install from any version of nushell-full archive
- ✅ Clear error messages if binaries not found in archive - ✅ Clear error messages if binaries not found in archive
- ✅ Version mismatch plugins skipped without breaking installation - ✅ Version mismatch plugins skipped without breaking installation
@ -270,6 +381,7 @@ All repositories now have:
### 🎯 Help System & Build Process Fixes (2025-10-19) ### 🎯 Help System & Build Process Fixes (2025-10-19)
#### Help System Improvements #### Help System Improvements
- **Added Version Update Module to Help System**: - **Added Version Update Module to Help System**:
- Version-update module now discoverable via `just help modules` - Version-update module now discoverable via `just help modules`
- Added to `just help` main output with key commands - Added to `just help` main output with key commands
@ -277,11 +389,13 @@ All repositories now have:
- Full integration into help system navigation - Full integration into help system navigation
#### New Help Commands #### New Help Commands
- **`just commands`**: New recipe showing all commands organized by group (alias for `just --list`) - **`just commands`**: New recipe showing all commands organized by group (alias for `just --list`)
- Shows [version-update] group with all 30+ update recipes - Shows [version-update] group with all 30+ update recipes
- Replaces need to manually run `just --list` - Replaces need to manually run `just --list`
#### Build Process Fixes #### Build Process Fixes
- **Fixed Plugin Archive Creation Bug**: - **Fixed Plugin Archive Creation Bug**:
- Phase 3 "No plugins found" warning fixed - Phase 3 "No plugins found" warning fixed
- Root cause: `each` command returns null, breaking count logic - Root cause: `each` command returns null, breaking count logic
@ -301,6 +415,7 @@ All repositories now have:
- Properly calculates archive file size in MB - Properly calculates archive file size in MB
#### Plugin Dependency Update Optimization #### Plugin Dependency Update Optimization
- **Fixed Unnecessary Plugin Rebuilds**: - **Fixed Unnecessary Plugin Rebuilds**:
- Root cause: `update_all_plugins.nu` always touched all Cargo.toml files - Root cause: `update_all_plugins.nu` always touched all Cargo.toml files
- Solution: Only save if content actually changed - Solution: Only save if content actually changed
@ -309,6 +424,7 @@ All repositories now have:
- Result: Only plugins with real changes trigger rebuilds (hashes, highlight) - Result: Only plugins with real changes trigger rebuilds (hashes, highlight)
#### Files Modified #### Files Modified
- `justfiles/help.just` - Added version-update module to help system - `justfiles/help.just` - Added version-update module to help system
- `scripts/create_full_distribution.nu` - Fixed plugin collection filtering (exclude .d files) - `scripts/create_full_distribution.nu` - Fixed plugin collection filtering (exclude .d files)
- `scripts/create_distribution_packages.nu` - Fixed get_plugin_components to look in correct directories - `scripts/create_distribution_packages.nu` - Fixed get_plugin_components to look in correct directories
@ -316,6 +432,7 @@ All repositories now have:
- `CHANGELOG.md` - Documented all fixes - `CHANGELOG.md` - Documented all fixes
#### Archive Content & Structure Fixes (Critical Fix) #### Archive Content & Structure Fixes (Critical Fix)
- **Fixed nushell-full archive missing plugins**: - **Fixed nushell-full archive missing plugins**:
- Root cause: `get_plugin_components` looked in distribution directory (staging output) - Root cause: `get_plugin_components` looked in distribution directory (staging output)
- Solution: Look in `nu_plugin_*/target/release/` (actual binaries) - Solution: Look in `nu_plugin_*/target/release/` (actual binaries)
@ -343,6 +460,7 @@ All repositories now have:
- Reduces archive size and keeps distribution focused - Reduces archive size and keeps distribution focused
#### Impact #### Impact
- ✅ Version-update commands now fully discoverable - ✅ Version-update commands now fully discoverable
- ✅ Phase 3 bin archive creation works correctly - ✅ Phase 3 bin archive creation works correctly
- ✅ nushell-full archive contains nu + all plugins - ✅ nushell-full archive contains nu + all plugins
@ -357,23 +475,27 @@ All repositories now have:
### 🎯 Nushell Core Update: 0.107.1 → 0.108.0 ### 🎯 Nushell Core Update: 0.107.1 → 0.108.0
#### Major Changes #### Major Changes
- **Updated Nushell to 0.108.0** with MCP (Model Context Protocol) support - **Updated Nushell to 0.108.0** with MCP (Model Context Protocol) support
- **Fixed critical documentation bugs** in `best_nushell_code.md` affecting all code generation - **Fixed critical documentation bugs** in `best_nushell_code.md` affecting all code generation
- **Created comprehensive automation framework** for future version updates - **Created comprehensive automation framework** for future version updates
- **Built complete distribution** with nushell 0.108.0 + all plugins - **Built complete distribution** with nushell 0.108.0 + all plugins
#### Critical Bug Fixes #### Critical Bug Fixes
- **Rule 16 (Function Signatures)**: Fixed incorrect syntax `]: type {``]: nothing -> type {` - **Rule 16 (Function Signatures)**: Fixed incorrect syntax `]: type {``]: nothing -> type {`
- **Rule 17 (String Interpolation)**: Fixed non-working syntax `[$var]``($var)` - **Rule 17 (String Interpolation)**: Fixed non-working syntax `[$var]``($var)`
- Both bugs were in documentation and caused all generated code to fail parsing - Both bugs were in documentation and caused all generated code to fail parsing
#### New Features #### New Features
- ✅ **MCP Support**: Model Context Protocol for AI agent integration - ✅ **MCP Support**: Model Context Protocol for AI agent integration
- ✅ **Enhanced SQLite**: Improved database operations - ✅ **Enhanced SQLite**: Improved database operations
- ✅ **System Clipboard**: Native clipboard integration - ✅ **System Clipboard**: Native clipboard integration
- ✅ **Trash Support**: Safe file deletion to trash - ✅ **Trash Support**: Safe file deletion to trash
#### Breaking Changes #### Breaking Changes
- **`into value``detect type`**: Command deprecated (still works with warning) - **`into value``detect type`**: Command deprecated (still works with warning)
- Shows helpful migration message - Shows helpful migration message
- Recommends `update cells {detect type}` instead - Recommends `update cells {detect type}` instead
@ -382,6 +504,7 @@ All repositories now have:
- Requires explicit error handling with `try`/`catch` - Requires explicit error handling with `try`/`catch`
#### New Automation Scripts (8 scripts created) #### New Automation Scripts (8 scripts created)
1. **`download_nushell.nu`** (285 lines) - Download from GitHub tags 1. **`download_nushell.nu`** (285 lines) - Download from GitHub tags
2. **`analyze_nushell_features.nu`** (350 lines) - Parse and validate features 2. **`analyze_nushell_features.nu`** (350 lines) - Parse and validate features
3. **`audit_crate_dependencies.nu`** (390 lines) - Audit plugin dependencies 3. **`audit_crate_dependencies.nu`** (390 lines) - Audit plugin dependencies
@ -392,18 +515,21 @@ All repositories now have:
8. **`complete_update.nu`** (NEW) - All-in-one update script 8. **`complete_update.nu`** (NEW) - All-in-one update script
#### Documentation Created #### Documentation Created
- **`updates/108/NUSHELL_0.108_UPDATE_SUMMARY.md`** - Complete update summary - **`updates/108/NUSHELL_0.108_UPDATE_SUMMARY.md`** - Complete update summary
- **`updates/108/MIGRATION_0.108.0.md`** - Step-by-step migration guide - **`updates/108/MIGRATION_0.108.0.md`** - Step-by-step migration guide
- **`updates/108/NUSHELL_UPDATE_AUTOMATION.md`** - Automation documentation - **`updates/108/NUSHELL_UPDATE_AUTOMATION.md`** - Automation documentation
- **`guides/COMPLETE_VERSION_UPDATE_GUIDE.md`** - Comprehensive update guide - **`guides/COMPLETE_VERSION_UPDATE_GUIDE.md`** - Comprehensive update guide
#### Build System Improvements #### Build System Improvements
- **Build Time**: Optimized to 2m 55s (from 15+ minutes) - **Build Time**: Optimized to 2m 55s (from 15+ minutes)
- **Features**: All desired features validated and included - **Features**: All desired features validated and included
- **Workspace**: Proper `--workspace` flag for system plugins - **Workspace**: Proper `--workspace` flag for system plugins
- **Artifacts**: Complete binary collection system - **Artifacts**: Complete binary collection system
#### Validation & Testing #### Validation & Testing
- ✅ All syntax patterns tested against actual 0.108.0 binary - ✅ All syntax patterns tested against actual 0.108.0 binary
- ✅ Function signatures validated - ✅ Function signatures validated
- ✅ String interpolation validated - ✅ String interpolation validated
@ -412,12 +538,14 @@ All repositories now have:
- ✅ Breaking changes verified - ✅ Breaking changes verified
#### Impact #### Impact
- **Developer Experience**: 80% reduction in update time with automation - **Developer Experience**: 80% reduction in update time with automation
- **Code Quality**: All future code will use correct syntax - **Code Quality**: All future code will use correct syntax
- **Maintainability**: Semi-automated updates with 3 manual checkpoints - **Maintainability**: Semi-automated updates with 3 manual checkpoints
- **Documentation**: Comprehensive guides for all future updates - **Documentation**: Comprehensive guides for all future updates
#### Files Modified #### Files Modified
- `best_nushell_code.md` - Fixed Rules 16 & 17, Quick Reference, Summary - `best_nushell_code.md` - Fixed Rules 16 & 17, Quick Reference, Summary
- `nushell/` - Updated to 0.108.0 - `nushell/` - Updated to 0.108.0
- `nu_plugin_*/Cargo.toml` - Dependency versions updated - `nu_plugin_*/Cargo.toml` - Dependency versions updated
@ -426,6 +554,7 @@ All repositories now have:
- `guides/` - New comprehensive guide - `guides/` - New comprehensive guide
#### Migration Notes #### Migration Notes
- Old `into value` usage still works but shows deprecation warning - Old `into value` usage still works but shows deprecation warning
- Update to `detect type` or `update cells {detect type}` to remove warnings - Update to `detect type` or `update cells {detect type}` to remove warnings
- Stream collection operations may need `try`/`catch` for error handling - Stream collection operations may need `try`/`catch` for error handling
@ -438,6 +567,7 @@ All repositories now have:
### 🚀 Major Feature: Complete Nushell Distribution System ### 🚀 Major Feature: Complete Nushell Distribution System
#### Full Distribution Infrastructure #### Full Distribution Infrastructure
- **Complete Nushell binary distribution**: Added capability to build, package, and distribute Nushell itself alongside plugins - **Complete Nushell binary distribution**: Added capability to build, package, and distribute Nushell itself alongside plugins
- **Zero-prerequisite installation**: Users can install complete Nushell environment without having Rust, Cargo, or Nushell pre-installed - **Zero-prerequisite installation**: Users can install complete Nushell environment without having Rust, Cargo, or Nushell pre-installed
- **Cross-platform bootstrap installers**: Universal POSIX shell installer (`install.sh`) and Windows PowerShell installer (`install.ps1`) - **Cross-platform bootstrap installers**: Universal POSIX shell installer (`install.sh`) and Windows PowerShell installer (`install.ps1`)
@ -445,6 +575,7 @@ All repositories now have:
- **Self-contained distribution packages**: Complete packages including binaries, configuration, documentation, and installers - **Self-contained distribution packages**: Complete packages including binaries, configuration, documentation, and installers
#### New Build System #### New Build System
- **`scripts/build_nushell.nu`**: Comprehensive script to build nushell with all workspace plugins - **`scripts/build_nushell.nu`**: Comprehensive script to build nushell with all workspace plugins
- **`scripts/collect_full_binaries.nu`**: Advanced binary collection system for nushell + all plugins - **`scripts/collect_full_binaries.nu`**: Advanced binary collection system for nushell + all plugins
- **`scripts/create_distribution_packages.nu`**: Multi-platform package creator with manifest generation - **`scripts/create_distribution_packages.nu`**: Multi-platform package creator with manifest generation
@ -452,6 +583,7 @@ All repositories now have:
- **Enhanced justfile**: Added 40+ new recipes in `justfiles/full_distro.just` for complete distribution workflows - **Enhanced justfile**: Added 40+ new recipes in `justfiles/full_distro.just` for complete distribution workflows
#### Installation and Configuration System #### Installation and Configuration System
- **`scripts/install_full_nushell.nu`**: Advanced nu-based installer with plugin selection and configuration options - **`scripts/install_full_nushell.nu`**: Advanced nu-based installer with plugin selection and configuration options
- **`scripts/verify_installation.nu`**: Comprehensive installation verification with detailed reporting - **`scripts/verify_installation.nu`**: Comprehensive installation verification with detailed reporting
- **`scripts/templates/default_config.nu`**: Complete 500+ line Nushell configuration with optimizations - **`scripts/templates/default_config.nu`**: Complete 500+ line Nushell configuration with optimizations
@ -459,6 +591,7 @@ All repositories now have:
- **`etc/distribution_config.toml`**: Central distribution configuration management - **`etc/distribution_config.toml`**: Central distribution configuration management
#### Bootstrap Installers (Zero Prerequisites) #### Bootstrap Installers (Zero Prerequisites)
- **`installers/bootstrap/install.sh`**: 900+ line universal POSIX installer for Linux/macOS - **`installers/bootstrap/install.sh`**: 900+ line universal POSIX installer for Linux/macOS
- Automatic platform detection and binary download - Automatic platform detection and binary download
- Build from source fallback capability - Build from source fallback capability
@ -468,12 +601,15 @@ All repositories now have:
- **Complete documentation**: Installation guides, troubleshooting, and security considerations - **Complete documentation**: Installation guides, troubleshooting, and security considerations
#### Uninstall System #### Uninstall System
- **`scripts/templates/uninstall.sh`** and **`uninstall.ps1`**: Clean removal scripts for all platforms - **`scripts/templates/uninstall.sh`** and **`uninstall.ps1`**: Clean removal scripts for all platforms
- **Complete cleanup**: Removes binaries, configurations, PATH entries with optional backup - **Complete cleanup**: Removes binaries, configurations, PATH entries with optional backup
- **Plugin unregistration**: Clean removal from nushell registry - **Plugin unregistration**: Clean removal from nushell registry
#### Key Distribution Workflows #### Key Distribution Workflows
```bash
```nushell
bash
# Build complete distribution # Build complete distribution
just build-full # Build nushell + all plugins just build-full # Build nushell + all plugins
@ -489,6 +625,7 @@ just release-full-cross # Full cross-platform release
``` ```
#### Installation Experience #### Installation Experience
- **One-liner installation**: `curl -sSf https://your-url/install.sh | sh` - **One-liner installation**: `curl -sSf https://your-url/install.sh | sh`
- **Multiple installation modes**: User (~/.local/bin), system (/usr/local/bin), portable - **Multiple installation modes**: User (~/.local/bin), system (/usr/local/bin), portable
- **Automatic plugin registration**: All plugins registered and verified with `nu -c "plugin list"` - **Automatic plugin registration**: All plugins registered and verified with `nu -c "plugin list"`
@ -497,6 +634,7 @@ just release-full-cross # Full cross-platform release
### 🎯 Major Updates ### 🎯 Major Updates
#### Documentation and Repository Structure #### Documentation and Repository Structure
- **Enhanced README.md**: Significantly expanded documentation with 682 new lines covering: - **Enhanced README.md**: Significantly expanded documentation with 682 new lines covering:
- Comprehensive plugin collection overview - Comprehensive plugin collection overview
- Detailed development workflows and automation - Detailed development workflows and automation
@ -505,12 +643,14 @@ just release-full-cross # Full cross-platform release
- Complete command reference and usage examples - Complete command reference and usage examples
#### Script and Automation Cleanup #### Script and Automation Cleanup
- **Removed legacy scripts**: Cleaned up old bash scripts (build-all.sh, collect-install.sh, make_plugin.sh) - **Removed legacy scripts**: Cleaned up old bash scripts (build-all.sh, collect-install.sh, make_plugin.sh)
- **Streamlined automation**: Consolidated script system in favor of unified approach via justfile and nushell scripts - **Streamlined automation**: Consolidated script system in favor of unified approach via justfile and nushell scripts
### 🔧 Plugin Updates and Dependency Management ### 🔧 Plugin Updates and Dependency Management
#### Nushell Core Updates #### Nushell Core Updates
- **Updated nushell submodule**: Comprehensive update to latest nushell version (0.107.1) - **Updated nushell submodule**: Comprehensive update to latest nushell version (0.107.1)
- **Synchronized dependencies**: Updated all nu-* dependencies across all plugins for version consistency - **Synchronized dependencies**: Updated all nu-* dependencies across all plugins for version consistency
- **Updated Cargo.lock files**: Refreshed dependency lock files for all plugins - **Updated Cargo.lock files**: Refreshed dependency lock files for all plugins
@ -518,25 +658,30 @@ just release-full-cross # Full cross-platform release
#### Plugin-Specific Changes #### Plugin-Specific Changes
##### nu_plugin_clipboard ##### nu_plugin_clipboard
- Updated Cargo.toml with new dependency versions - Updated Cargo.toml with new dependency versions
- Refreshed Cargo.lock with 253 dependency changes - Refreshed Cargo.lock with 253 dependency changes
##### nu_plugin_desktop_notifications ##### nu_plugin_desktop_notifications
- Updated Cargo.toml for nushell 0.107.1 compatibility - Updated Cargo.toml for nushell 0.107.1 compatibility
- Refreshed Cargo.lock with 218 dependency updates - Refreshed Cargo.lock with 218 dependency updates
##### nu_plugin_hashes ##### nu_plugin_hashes
- **Enhanced functionality**: Updated hasher.rs implementation - **Enhanced functionality**: Updated hasher.rs implementation
- **Build system improvements**: Modified build.rs configuration - **Build system improvements**: Modified build.rs configuration
- Updated Cargo.toml with 24 configuration changes - Updated Cargo.toml with 24 configuration changes
- Refreshed Cargo.lock with 283 dependency updates - Refreshed Cargo.lock with 283 dependency updates
##### nu_plugin_highlight ##### nu_plugin_highlight
- **Code improvements**: Enhanced highlight.rs and plugin.rs implementations - **Code improvements**: Enhanced highlight.rs and plugin.rs implementations
- Updated for new nushell plugin API compatibility - Updated for new nushell plugin API compatibility
- Refreshed Cargo.lock with 476 dependency updates - Refreshed Cargo.lock with 476 dependency updates
##### nu_plugin_image ##### nu_plugin_image
- **Major code refactoring**: Comprehensive updates to image processing modules - **Major code refactoring**: Comprehensive updates to image processing modules
- **Removed deprecated code**: Deleted ansi_to_image.rs (replaced with modular approach) - **Removed deprecated code**: Deleted ansi_to_image.rs (replaced with modular approach)
- **Enhanced modules**: - **Enhanced modules**:
@ -548,21 +693,25 @@ just release-full-cross # Full cross-platform release
- Refreshed Cargo.lock with 494 dependency updates - Refreshed Cargo.lock with 494 dependency updates
##### nu_plugin_kcl and nu_plugin_tera ##### nu_plugin_kcl and nu_plugin_tera
- Updated submodule references - Updated submodule references
- Synchronized with latest upstream changes - Synchronized with latest upstream changes
##### nu_plugin_port_extension and nu_plugin_qr_maker ##### nu_plugin_port_extension and nu_plugin_qr_maker
- Updated Cargo.toml for version consistency - Updated Cargo.toml for version consistency
- Refreshed Cargo.lock files - Refreshed Cargo.lock files
#### API KCL Plugin #### API KCL Plugin
- Updated Cargo.lock with 266 dependency changes - Updated Cargo.lock with 266 dependency changes
### 🏗️ Repository Infrastructure Updates ### 🏗️ Repository Infrastructure Updates
#### Git Tracking Cleanup #### Git Tracking Cleanup
- **Removed nushell directory from tracking**: The nushell submodule directory is now properly ignored - **Removed nushell directory from tracking**: The nushell submodule directory is now properly ignored
- **Updated .gitignore**: Added patterns for nushell directory, nushell-* files, and *.tar.gz archives - **Updated .gitignore**: Added patterns for nushell directory, nushell-*files, and*.tar.gz archives
### 📊 Statistics Summary ### 📊 Statistics Summary

View File

@ -1,20 +1,19 @@
# 🚀 Nushell Plugins Repository # 🚀 Nushell Plugins Repository
**Current Nushell Version**: 0.108.0 | **Last Updated**: 2025-10-18 **Current Nushell Version**: 0.111.0 | **Last Updated**: 2026-03-11
A comprehensive collection of nushell plugins with automated upstream tracking, dependency management, development workflows, **complete Nushell distribution system**, and **semi-automated version update framework**. A comprehensive collection of nushell plugins with automated upstream tracking, dependency management, development workflows, **complete Nushell distribution system**, and **semi-automated version update framework**.
## 🎯 Latest Update: Nushell 0.108.0 ## 🎯 Latest Update: Nushell 0.111.0
**Major highlights of the 0.108.0 update:** **Highlights of the 0.111.0 update:**
- ✅ **MCP Support**: Model Context Protocol for AI agent integration
- ✅ **Critical Bug Fixes**: Fixed documentation syntax errors affecting all code generation
- ✅ **Automation Framework**: 8 new scripts for semi-automated version updates
- ✅ **Complete Documentation**: Migration guides, automation docs, and validation reports
- ✅ **80% Faster Updates**: Automated workflows with strategic manual checkpoints
**→ [START HERE: UPDATE.md](UPDATE.md)** for version update instructions - ✅ **18 plugins updated** to Nushell 0.111.0
See [`CHANGELOG.md`](CHANGELOG.md) for complete details | Read [`updates/108/`](updates/108/) for full documentation - ✅ **Rust toolchain** bumped to 1.93.1 (nushell 0.111.0 requires ≥1.91.1)
- ✅ **interprocess ^2.3.1** — resolved conflict with nu-plugin-core 0.111.0 across mcp/nats/typedialog
- ✅ **Update script fixes**`[package].version` and `[dev-dependencies]` now correctly updated on minor version bumps
See [`CHANGELOG.md`](CHANGELOG.md) for complete details.
## 🆕 NEW: Full Nushell Distribution System ## 🆕 NEW: Full Nushell Distribution System
@ -23,6 +22,7 @@ See [`CHANGELOG.md`](CHANGELOG.md) for complete details | Read [`updates/108/`](
This repository provides **complete Nushell distributions** that include Nushell itself plus all plugins, offering zero-prerequisite installation for end users: This repository provides **complete Nushell distributions** that include Nushell itself plus all plugins, offering zero-prerequisite installation for end users:
### 🎯 End User Installation (Zero Prerequisites) ### 🎯 End User Installation (Zero Prerequisites)
```bash ```bash
# One-liner installation (Linux/macOS) # One-liner installation (Linux/macOS)
curl -sSf https://your-url/install.sh | sh curl -sSf https://your-url/install.sh | sh
@ -36,6 +36,7 @@ tar -xzf nushell-full-*.tar.gz && cd nushell-full-* && ./install.sh
``` ```
### 🚀 Developer Distribution Commands ### 🚀 Developer Distribution Commands
```bash ```bash
just build-full # Build nushell + all plugins just build-full # Build nushell + all plugins
just pack-full # Create distribution package just pack-full # Create distribution package
@ -45,6 +46,7 @@ just release-full-cross # Complete release workflow
``` ```
### ✨ Key Features ### ✨ Key Features
- **Zero Prerequisites**: No Rust, Cargo, or Nu installation required - **Zero Prerequisites**: No Rust, Cargo, or Nu installation required
- **Cross-Platform**: Linux, macOS, Windows support - **Cross-Platform**: Linux, macOS, Windows support
- **Complete Environment**: Nushell + all plugins + configuration - **Complete Environment**: Nushell + all plugins + configuration
@ -83,6 +85,7 @@ tar -xzf nushell-full-*.tar.gz && cd nushell-full-* && ./install.sh
### For Developers ### For Developers
#### Prerequisites #### Prerequisites
```bash ```bash
# Install required tools # Install required tools
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Rust
@ -91,6 +94,7 @@ cargo install just # Just (optional
``` ```
#### Development Workflow #### Development Workflow
```bash ```bash
# Clone the repository # Clone the repository
git clone <repository-url> git clone <repository-url>
@ -130,6 +134,7 @@ The Full Distribution System transforms this repository from a **development-foc
### End User Experience ### End User Experience
**Before**: Complex development setup required **Before**: Complex development setup required
```bash ```bash
# Users needed to: # Users needed to:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install Rust
@ -139,6 +144,7 @@ just validate-nushell && just build # Build everythin
``` ```
**After**: Zero-prerequisite installation **After**: Zero-prerequisite installation
```bash ```bash
# Users now only need: # Users now only need:
curl -sSf https://your-url/install.sh | sh # Done! curl -sSf https://your-url/install.sh | sh # Done!
@ -149,6 +155,7 @@ curl -sSf https://your-url/install.sh | sh # Done!
#### Building Complete Distributions #### Building Complete Distributions
**Step 1: Build Nushell + System Plugins** **Step 1: Build Nushell + System Plugins**
```bash ```bash
# Build Nushell with built-in system plugins (uses cargo build --workspace) # Build Nushell with built-in system plugins (uses cargo build --workspace)
just build-nushell # Builds: nu + nu_plugin_formats, nu_plugin_inc, nu_plugin_gstat, just build-nushell # Builds: nu + nu_plugin_formats, nu_plugin_inc, nu_plugin_gstat,
@ -159,6 +166,7 @@ just build-nushell-target linux-arm64 # Cross-compile system plugins
``` ```
**Step 2: Build Everything (System + Custom Plugins)** **Step 2: Build Everything (System + Custom Plugins)**
```bash ```bash
# Build nushell + system plugins + custom plugins from this repo # Build nushell + system plugins + custom plugins from this repo
just build-full # Native build (calls build-nushell + build custom plugins) just build-full # Native build (calls build-nushell + build custom plugins)
@ -167,6 +175,7 @@ just build-full-all # All platforms
``` ```
**Step 3: Collect Built Binaries** **Step 3: Collect Built Binaries**
```bash ```bash
# Collect nushell binary + all plugins for distribution # Collect nushell binary + all plugins for distribution
just collect # Current platform (darwin-arm64) just collect # Current platform (darwin-arm64)
@ -175,6 +184,7 @@ just collect-platform PLATFORM # Specific platform
``` ```
**Step 4: Create Distribution Packages** **Step 4: Create Distribution Packages**
```bash ```bash
# Create distribution packages # Create distribution packages
just pack-full # Current platform just pack-full # Current platform
@ -187,6 +197,7 @@ just test-install-full # Test complete installation process
``` ```
#### Cross-Platform Release #### Cross-Platform Release
```bash ```bash
# Complete release workflow # Complete release workflow
just release-full-cross # Build → Pack → Verify for all platforms just release-full-cross # Build → Pack → Verify for all platforms
@ -200,6 +211,7 @@ just release-full-windows # Windows distributions
#### Installation Modes #### Installation Modes
**System Installation** (recommended for end users): **System Installation** (recommended for end users):
```bash ```bash
# Install to system paths with proper integration # Install to system paths with proper integration
./install.sh ./install.sh
@ -212,6 +224,7 @@ just release-full-windows # Windows distributions
``` ```
**Local Installation** (development/testing): **Local Installation** (development/testing):
```bash ```bash
# Install to user directory without system integration # Install to user directory without system integration
./install.sh --local ./install.sh --local
@ -223,6 +236,7 @@ just release-full-windows # Windows distributions
### Distribution Architecture ### Distribution Architecture
#### Components Included #### Components Included
- **Nushell Binary**: Complete nushell installation - **Nushell Binary**: Complete nushell installation
- **All Plugins**: Every plugin in the repository - **All Plugins**: Every plugin in the repository
- **Configuration**: Optimized default configuration - **Configuration**: Optimized default configuration
@ -231,6 +245,7 @@ just release-full-windows # Windows distributions
- **Verification**: Installation integrity checks - **Verification**: Installation integrity checks
#### Platform Support #### Platform Support
| Platform | Architecture | Status | Installation Method | | Platform | Architecture | Status | Installation Method |
|----------|-------------|--------|-------------------| |----------|-------------|--------|-------------------|
| **Linux** | x86_64 | ✅ Full | `curl` installer + manual | | **Linux** | x86_64 | ✅ Full | `curl` installer + manual |
@ -240,6 +255,7 @@ just release-full-windows # Windows distributions
| **Windows** | x86_64 | ✅ Full | PowerShell installer + manual | | **Windows** | x86_64 | ✅ Full | PowerShell installer + manual |
#### Bootstrap Installers #### Bootstrap Installers
- **Smart Detection**: Automatically detects platform and architecture - **Smart Detection**: Automatically detects platform and architecture
- **Conflict Resolution**: Handles existing Nushell installations - **Conflict Resolution**: Handles existing Nushell installations
- **Path Management**: Configures PATH and shell integration - **Path Management**: Configures PATH and shell integration
@ -249,18 +265,21 @@ just release-full-windows # Windows distributions
### Use Cases ### Use Cases
#### For End Users #### For End Users
- **Simple Installation**: Get Nushell + all plugins with one command - **Simple Installation**: Get Nushell + all plugins with one command
- **No Prerequisites**: No need for Rust, Git, or development tools - **No Prerequisites**: No need for Rust, Git, or development tools
- **Professional Experience**: Clean installation/uninstallation - **Professional Experience**: Clean installation/uninstallation
- **Immediate Productivity**: Pre-configured environment with all plugins - **Immediate Productivity**: Pre-configured environment with all plugins
#### For System Administrators #### For System Administrators
- **Bulk Deployment**: Deploy to multiple systems easily - **Bulk Deployment**: Deploy to multiple systems easily
- **Consistent Environment**: Identical setup across all machines - **Consistent Environment**: Identical setup across all machines
- **Offline Installation**: Manual packages work without internet - **Offline Installation**: Manual packages work without internet
- **Integration Ready**: System-wide installation with proper paths - **Integration Ready**: System-wide installation with proper paths
#### for Distributors/Packagers #### for Distributors/Packagers
- **Ready Binaries**: All binaries built and tested - **Ready Binaries**: All binaries built and tested
- **Cross-Platform**: Support for all major platforms - **Cross-Platform**: Support for all major platforms
- **Checksums Included**: Integrity verification built-in - **Checksums Included**: Integrity verification built-in
@ -302,33 +321,34 @@ This repository provides:
| **nu_plugin_fluent** | ✅ Tracked | Upstream | Fluent localization framework | | **nu_plugin_fluent** | ✅ Tracked | Upstream | Fluent localization framework |
| **nu_plugin_tera** | ✅ Tracked | Private | Tera templating engine (private repo) | | **nu_plugin_tera** | ✅ Tracked | Private | Tera templating engine (private repo) |
| **nu_plugin_kcl** | ✅ Tracked | Private | KCL configuration language (private repo) | | **nu_plugin_kcl** | ✅ Tracked | Private | KCL configuration language (private repo) |
| **nu_plugin_nickel** | 🏠 Local | Local | Nickel configuration language (eval, export, format, validate) |
### Provisioning Platform Plugins (NEW) ### Provisioning Platform Plugins
High-performance native plugins for the provisioning platform with **10x performance improvement** over HTTP APIs: High-performance native plugins for the provisioning platform:
| Plugin | Type | Description | Performance Gain | | Plugin | Type | Description |
|--------|------|-------------|------------------| |--------|------|-------------|
| **nu_plugin_auth** | 🔐 Security | JWT authentication, MFA (TOTP/WebAuthn), session management | 20% faster | | **nu_plugin_auth** | 🔐 Security | JWT authentication, MFA (TOTP/WebAuthn), session management |
| **nu_plugin_kms** | 🔑 Security | Multi-backend KMS (RustyVault, Age, Cosmian, AWS, Vault) | **10x faster** | | **nu_plugin_kms** | 🔑 Security | Multi-backend KMS (RustyVault, Age, Cosmian, AWS, Vault) |
| **nu_plugin_orchestrator** | 🎯 Operations | Orchestrator status, workflow validation, task management | **10x faster** | | **nu_plugin_orchestrator** | 🎯 Operations | Orchestrator status, workflow validation, task management |
| **nu_plugin_typedialog** | 💬 UX | Interactive forms/prompts via TypeDialog — replaces shlib TTY wrappers |
**Key Features**: | **nu_plugin_mcp** | 🤖 AI | MCP client — spawn and interact with `provisioning-mcp-server` |
- **Native Performance**: Direct Rust integration eliminates HTTP overhead
- **Pipeline Integration**: Full Nushell pipeline support
- **Multi-Backend KMS**: RustyVault, Age, Cosmian, AWS KMS, HashiCorp Vault
- **Security First**: JWT, MFA (TOTP/WebAuthn), keyring storage
- **Production Ready**: Comprehensive tests, error handling, documentation
**Quick Start**: **Quick Start**:
```bash ```bash
# Build and register provisioning plugins # Build and register provisioning plugins
just build-plugin nu_plugin_auth just build-plugin nu_plugin_auth
just build-plugin nu_plugin_kms just build-plugin nu_plugin_kms
just build-plugin nu_plugin_orchestrator just build-plugin nu_plugin_orchestrator
just build-plugin nu_plugin_typedialog
just build-plugin nu_plugin_mcp
just install-plugin nu_plugin_auth just install-plugin nu_plugin_auth
just install-plugin nu_plugin_kms just install-plugin nu_plugin_kms
just install-plugin nu_plugin_orchestrator just install-plugin nu_plugin_orchestrator
just install-plugin nu_plugin_typedialog
just install-plugin nu_plugin_mcp
# Verify installation # Verify installation
nu -c "plugin list | where name =~ provisioning" nu -c "plugin list | where name =~ provisioning"
@ -337,6 +357,7 @@ nu -c "plugin list | where name =~ provisioning"
**See Full Guide**: `docs/user/NUSHELL_PLUGINS_GUIDE.md` **See Full Guide**: `docs/user/NUSHELL_PLUGINS_GUIDE.md`
### Legend ### Legend
- ✅ **Tracked**: Has upstream repository with automated tracking - ✅ **Tracked**: Has upstream repository with automated tracking
- 🏠 **Local**: Developed locally without upstream - 🏠 **Local**: Developed locally without upstream
- 🔒 **Private**: Private repository with tracking (requires authentication) - 🔒 **Private**: Private repository with tracking (requires authentication)
@ -412,6 +433,7 @@ For detailed instructions, platform-specific setup, troubleshooting, and advance
**➡️ [Complete Building and Cross-Compilation Guide](docs/BUILDING.md)** **➡️ [Complete Building and Cross-Compilation Guide](docs/BUILDING.md)**
The guide covers: The guide covers:
- Platform-specific setup and dependencies - Platform-specific setup and dependencies
- Native vs Docker cross-compilation - Native vs Docker cross-compilation
- Configuration and customization - Configuration and customization
@ -608,7 +630,7 @@ The repository now uses a **unified script system** with automatic nushell detec
### Directory Structure ### Directory Structure
``` ```plaintext
scripts/ scripts/
├── run.sh # Universal script runner with version checking ├── run.sh # Universal script runner with version checking
├── check_version.nu # Version consistency validator ├── check_version.nu # Version consistency validator
@ -682,7 +704,7 @@ Every script automatically validates that your system nushell version matches th
## 📁 File Structure ## 📁 File Structure
``` ```plaintext
nushell-plugins/ nushell-plugins/
├── README.md # This file ├── README.md # This file
├── justfile # Just task runner recipes ├── justfile # Just task runner recipes
@ -731,6 +753,7 @@ nu --version && nu -c "plugin list"
``` ```
**What you get:** **What you get:**
- Complete Nushell installation - Complete Nushell installation
- All plugins from this repository - All plugins from this repository
- Optimized configuration - Optimized configuration
@ -742,6 +765,7 @@ nu --version && nu -c "plugin list"
The installation system uses a **single source of truth** with symlinks to eliminate code duplication: The installation system uses a **single source of truth** with symlinks to eliminate code duplication:
**Key Files:** **Key Files:**
- **Source of Truth**: `installers/bootstrap/install.sh` (1,176 lines) - **Source of Truth**: `installers/bootstrap/install.sh` (1,176 lines)
- Complete bootstrap installer for Nushell + plugins - Complete bootstrap installer for Nushell + plugins
- Repository: `https://github.com/jesusperezlorenzo/nushell-plugins` - Repository: `https://github.com/jesusperezlorenzo/nushell-plugins`
@ -775,6 +799,7 @@ Install from local archives or directories without downloading:
``` ```
**Version-Agnostic Archive Detection**: **Version-Agnostic Archive Detection**:
- Automatically detects any `nushell-*` version directory - Automatically detects any `nushell-*` version directory
- Works with: 0.107, 0.108, 0.109, or any future version - Works with: 0.107, 0.108, 0.109, or any future version
- Searches for binaries in: `bin/nu` (preferred) → root `nu` (fallback) - Searches for binaries in: `bin/nu` (preferred) → root `nu` (fallback)
@ -782,6 +807,7 @@ Install from local archives or directories without downloading:
- Smart extraction and path detection - Smart extraction and path detection
**Installation Options**: **Installation Options**:
```bash ```bash
./install.sh # Interactive mode ./install.sh # Interactive mode
./install.sh --install-dir ~/.local # Custom path, non-interactive ./install.sh --install-dir ~/.local # Custom path, non-interactive
@ -792,6 +818,7 @@ Install from local archives or directories without downloading:
``` ```
**How DRY Works**: **How DRY Works**:
1. **Single Source**: Only `installers/bootstrap/install.sh` is edited 1. **Single Source**: Only `installers/bootstrap/install.sh` is edited
2. **Symlinks Propagate**: All changes automatically available at `./install.sh` and `./scripts/templates/install.sh` 2. **Symlinks Propagate**: All changes automatically available at `./install.sh` and `./scripts/templates/install.sh`
3. **Verified**: All symlinked paths contain identical content 3. **Verified**: All symlinked paths contain identical content
@ -939,6 +966,7 @@ just validate # Validate setup and dependencies
**A:** This system requires your installed nushell version to match the submodule version for consistency. All operations automatically check version consistency and will fail if versions don't match. **A:** This system requires your installed nushell version to match the submodule version for consistency. All operations automatically check version consistency and will fail if versions don't match.
**Solutions:** **Solutions:**
- **Auto-fix**: `just fix-nushell` or `./scripts/run.sh --fix --check-only` - **Auto-fix**: `just fix-nushell` or `./scripts/run.sh --fix --check-only`
- **Manual check**: `just validate-nushell` - **Manual check**: `just validate-nushell`
- **Skip (not recommended)**: `./scripts/run.sh --no-version-check script.nu` - **Skip (not recommended)**: `./scripts/run.sh --no-version-check script.nu`
@ -946,6 +974,7 @@ just validate # Validate setup and dependencies
### Q: What happened to the bash wrapper scripts? ### Q: What happened to the bash wrapper scripts?
**A:** The repository has been consolidated to eliminate script duplication. The old bash wrappers in `scripts/sh/` have been removed in favor of: **A:** The repository has been consolidated to eliminate script duplication. The old bash wrappers in `scripts/sh/` have been removed in favor of:
- **Universal wrapper**: `./scripts/run.sh` with automatic version checking - **Universal wrapper**: `./scripts/run.sh` with automatic version checking
- **Direct nu scripts**: All scripts moved from `scripts/nu/` to `scripts/` - **Direct nu scripts**: All scripts moved from `scripts/nu/` to `scripts/`
- **Just recipes**: Updated to use the new system - **Just recipes**: Updated to use the new system
@ -953,6 +982,7 @@ just validate # Validate setup and dependencies
### Q: How does the new script system work? ### Q: How does the new script system work?
**A:** The new system provides: **A:** The new system provides:
1. **Universal wrapper** (`scripts/run.sh`) with automatic nushell detection and version validation 1. **Universal wrapper** (`scripts/run.sh`) with automatic nushell detection and version validation
2. **Consolidated scripts** - all nu scripts in `scripts/` directory 2. **Consolidated scripts** - all nu scripts in `scripts/` directory
3. **Mandatory version checking** - every operation validates version consistency 3. **Mandatory version checking** - every operation validates version consistency
@ -965,6 +995,7 @@ just validate # Validate setup and dependencies
### Q: What happens to my local changes during upstream merges? ### Q: What happens to my local changes during upstream merges?
**A:** Local changes are preserved. The system: **A:** Local changes are preserved. The system:
1. Creates backup branches before any merge 1. Creates backup branches before any merge
2. Applies upstream changes in a temporary branch 2. Applies upstream changes in a temporary branch
3. Restores your local nu_* dependency versions 3. Restores your local nu_* dependency versions
@ -978,6 +1009,7 @@ just validate # Validate setup and dependencies
### Q: How do I add a new plugin to the repository? ### Q: How do I add a new plugin to the repository?
**A:** Use the template generator: **A:** Use the template generator:
```bash ```bash
just make-plugin nu_plugin_myfeature just make-plugin nu_plugin_myfeature
# or # or
@ -987,6 +1019,7 @@ nu scripts/nu/make_plugin.nu nu_plugin_myfeature
### Q: What if upstream tracking fails? ### Q: What if upstream tracking fails?
**A:** Check the plugin status with `just status`. Failed plugins show error details. Common issues: **A:** Check the plugin status with `just status`. Failed plugins show error details. Common issues:
- Network connectivity to upstream repository - Network connectivity to upstream repository
- Authentication for private repositories - Authentication for private repositories
- Merge conflicts requiring manual resolution - Merge conflicts requiring manual resolution
@ -994,6 +1027,7 @@ nu scripts/nu/make_plugin.nu nu_plugin_myfeature
### Q: How do I update nushell dependency versions? ### Q: How do I update nushell dependency versions?
**A:** Use the version updater: **A:** Use the version updater:
```bash ```bash
just update-nu-versions # Update all plugins just update-nu-versions # Update all plugins
just list-nu-versions # Show current versions just list-nu-versions # Show current versions
@ -1006,6 +1040,7 @@ just list-nu-versions # Show current versions
### Q: How do I distribute plugins to other systems? ### Q: How do I distribute plugins to other systems?
**A:** Use the distribution pipeline: **A:** Use the distribution pipeline:
```bash ```bash
just build # Build all plugins just build # Build all plugins
just collect # Collect binaries just collect # Collect binaries
@ -1017,6 +1052,7 @@ The resulting archive contains all binaries and installation scripts.
### Q: What's the difference between shell scripts and nushell scripts? ### Q: What's the difference between shell scripts and nushell scripts?
**A:** **A:**
- **Nushell scripts** (`scripts/nu/`): Primary implementation with full features - **Nushell scripts** (`scripts/nu/`): Primary implementation with full features
- **Shell scripts** (`scripts/sh/`): Wrappers for compatibility with non-nushell environments - **Shell scripts** (`scripts/sh/`): Wrappers for compatibility with non-nushell environments
- Both provide the same functionality, use whichever fits your environment - Both provide the same functionality, use whichever fits your environment
@ -1024,6 +1060,7 @@ The resulting archive contains all binaries and installation scripts.
### Q: How do I contribute a new plugin? ### Q: How do I contribute a new plugin?
**A:** **A:**
1. Create the plugin: `just make-plugin nu_plugin_yourname` 1. Create the plugin: `just make-plugin nu_plugin_yourname`
2. Implement your functionality 2. Implement your functionality
3. Add upstream URL to `etc/plugin_registry.toml` if it has one 3. Add upstream URL to `etc/plugin_registry.toml` if it has one
@ -1033,12 +1070,14 @@ The resulting archive contains all binaries and installation scripts.
### Q: How do I exclude plugins from upstream tracking? ### Q: How do I exclude plugins from upstream tracking?
**A:** Edit `etc/upstream_exclude.toml` to exclude plugins from specific operations: **A:** Edit `etc/upstream_exclude.toml` to exclude plugins from specific operations:
- Add to `[exclude]` section to exclude from all operations - Add to `[exclude]` section to exclude from all operations
- Add to `[exclude.check]` to skip automatic checks - Add to `[exclude.check]` to skip automatic checks
- Add to `[exclude.merge]` to prevent automatic merging - Add to `[exclude.merge]` to prevent automatic merging
- Use patterns like `nu_plugin_test_*` to exclude multiple plugins - Use patterns like `nu_plugin_test_*` to exclude multiple plugins
You can also use Just commands: You can also use Just commands:
```bash ```bash
just exclude nu_plugin_test add # Add to exclusion just exclude nu_plugin_test add # Add to exclusion
just exclude nu_plugin_test remove # Remove from exclusion just exclude nu_plugin_test remove # Remove from exclusion

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -182,12 +182,6 @@ additional_dirs = [
[packaging] [packaging]
# Output directory for packages # Output directory for packages
output_dir = "bin_archives" output_dir = "bin_archives"
# Package formats per platform
formats = {
linux = ["tar.gz"],
macos = ["tar.gz"],
windows = ["zip"]
}
# Compression level (0-9) # Compression level (0-9)
compression_level = 6 compression_level = 6
# Whether to generate checksums # Whether to generate checksums
@ -195,6 +189,12 @@ generate_checksums = true
# Checksum algorithms # Checksum algorithms
checksum_algorithms = ["sha256", "sha512"] checksum_algorithms = ["sha256", "sha512"]
# Package formats per platform
[packaging.formats]
linux = ["tar.gz"]
macos = ["tar.gz"]
windows = ["zip"]
# Manifest settings # Manifest settings
[packaging.manifest] [packaging.manifest]
# Include detailed manifest in packages # Include detailed manifest in packages
@ -210,11 +210,6 @@ include_plugin_info = true
# Installation settings # Installation settings
[installation] [installation]
# Default configuration templates
config_templates = {
env = "scripts/templates/default_env.nu",
config = "scripts/templates/default_config.nu"
}
# Whether to create config directory # Whether to create config directory
create_config_dir = true create_config_dir = true
# Whether to update PATH # Whether to update PATH
@ -227,6 +222,11 @@ shell_scripts = [
"installers/bootstrap/install.ps1" "installers/bootstrap/install.ps1"
] ]
# Default configuration templates
[installation.config_templates]
env = "scripts/templates/default_env.nu"
config = "scripts/templates/default_config.nu"
# Verification settings # Verification settings
[verification] [verification]
# Enable installation verification # Enable installation verification
@ -274,11 +274,10 @@ generate_release_notes = true
# Pre-release validation # Pre-release validation
validate_before_release = true validate_before_release = true
# Release channels # Release channels
channels = { [release.channels]
stable = "main", stable = "main"
beta = "develop", beta = "develop"
nightly = "nightly" nightly = "nightly"
}
# Documentation settings # Documentation settings
[documentation] [documentation]

View File

@ -1,10 +1,9 @@
# Nushell Plugin Registry for Provisioning Platform [plugins.nu_plugin_auth]
# This file tracks available Nushell plugins with metadata
[nu_plugin_auth]
upstream_url = "local" upstream_url = "local"
upstream_branch = "main"
status = "ok" status = "ok"
auto_merge = false auto_merge = false
local_path = "nu_plugin_auth"
description = "Authentication plugin (JWT, MFA) for provisioning platform" description = "Authentication plugin (JWT, MFA) for provisioning platform"
version = "0.1.0" version = "0.1.0"
category = "provisioning" category = "provisioning"
@ -14,59 +13,71 @@ commands = [
"auth verify", "auth verify",
"auth sessions", "auth sessions",
"auth mfa enroll", "auth mfa enroll",
"auth mfa verify" "auth mfa verify",
] ]
dependencies = [ dependencies = [
"jsonwebtoken", "jsonwebtoken",
"reqwest", "reqwest",
"keyring", "keyring",
"rpassword", "rpassword",
"qrcode" "qrcode",
] ]
[nu_plugin_kms] [plugins.nu_plugin_kms]
upstream_url = "local" upstream_url = "local"
upstream_branch = "main"
status = "ok" status = "ok"
auto_merge = false auto_merge = false
local_path = "nu_plugin_kms"
description = "KMS plugin (RustyVault, Age, Cosmian) for provisioning platform" description = "KMS plugin (RustyVault, Age, Cosmian) for provisioning platform"
version = "0.1.0" version = "0.1.0"
category = "provisioning" category = "provisioning"
backends = ["rustyvault", "age", "cosmian", "aws", "vault"] backends = [
"rustyvault",
"age",
"cosmian",
"aws",
"vault",
]
commands = [ commands = [
"kms encrypt", "kms encrypt",
"kms decrypt", "kms decrypt",
"kms generate-key", "kms generate-key",
"kms status" "kms status",
] ]
dependencies = [ dependencies = [
"reqwest", "reqwest",
"age", "age",
"base64", "base64",
"serde" "serde",
] ]
[nu_plugin_orchestrator] [plugins.nu_plugin_orchestrator]
upstream_url = "local" upstream_url = "local"
upstream_branch = "main"
status = "ok" status = "ok"
auto_merge = false auto_merge = false
local_path = "nu_plugin_orchestrator"
description = "Orchestrator operations plugin (status, validate, tasks)" description = "Orchestrator operations plugin (status, validate, tasks)"
version = "0.1.0" version = "0.1.0"
category = "provisioning" category = "provisioning"
commands = [ commands = [
"orch status", "orch status",
"orch validate", "orch validate",
"orch tasks" "orch tasks",
] ]
dependencies = [ dependencies = [
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"walkdir" "walkdir",
] ]
[nu_plugin_inquire] [plugins.nu_plugin_inquire]
upstream_url = "https://github.com/jesusperezlorenzo/nu_plugin_inquire" upstream_url = "https://github.com/jesusperezlorenzo/nu_plugin_inquire"
upstream_branch = "main"
status = "ok" status = "ok"
auto_merge = false auto_merge = false
local_path = "nu_plugin_inquire"
description = "Interactive forms and prompts plugin using inquire crate - solves TTY buffering issues" description = "Interactive forms and prompts plugin using inquire crate - solves TTY buffering issues"
version = "0.1.0" version = "0.1.0"
category = "utility" category = "utility"
@ -79,7 +90,7 @@ commands = [
"inquire custom", "inquire custom",
"inquire editor", "inquire editor",
"inquire date", "inquire date",
"inquire form" "inquire form",
] ]
dependencies = [ dependencies = [
"inquire", "inquire",
@ -87,13 +98,15 @@ dependencies = [
"serde_json", "serde_json",
"toml", "toml",
"chrono", "chrono",
"dialoguer" "dialoguer",
] ]
[forminquire] [plugins.forminquire]
upstream_url = "local" upstream_url = "local"
upstream_branch = "main"
status = "ok" status = "ok"
auto_merge = false auto_merge = false
local_path = "forminquire"
description = "Standalone interactive forms and prompts library + CLI tool - no Nushell dependency required" description = "Standalone interactive forms and prompts library + CLI tool - no Nushell dependency required"
version = "0.1.0" version = "0.1.0"
category = "utility" category = "utility"
@ -108,16 +121,20 @@ commands = [
"forminquire custom", "forminquire custom",
"forminquire editor", "forminquire editor",
"forminquire date", "forminquire date",
"forminquire form" "forminquire form",
] ]
library_support = true library_support = true
output_formats = ["text", "json", "yaml"] output_formats = [
"text",
"json",
"yaml",
]
features = [ features = [
"8 interactive prompt types", "8 interactive prompt types",
"TOML-based form definitions", "TOML-based form definitions",
"Automatic stdin fallback", "Automatic stdin fallback",
"Multiple output formats", "Multiple output formats",
"Both library and CLI usage" "Both library and CLI usage",
] ]
dependencies = [ dependencies = [
"clap", "clap",
@ -129,18 +146,343 @@ dependencies = [
"chrono", "chrono",
"dialoguer", "dialoguer",
"anyhow", "anyhow",
"thiserror" "thiserror",
] ]
# Distribution Configuration [plugins.nu_plugin_nickel]
[distribution] upstream_url = "local"
excluded_plugins = [ upstream_branch = "main"
"nu_plugin_example" status = "ok"
auto_merge = false
local_path = "nu_plugin_nickel"
description = "Nushell plugin for Nickel configuration language with eval, export, format, and validation"
version = "0.1.0"
category = "provisioning"
commands = [
"nickel-eval",
"nickel-export",
"nickel-format",
"nickel-validate",
"nickel-cache-status",
] ]
dependencies = [
"nu-plugin",
"nu-protocol",
"anyhow",
"tempfile",
"sha2",
"serde",
"serde_json",
"dirs",
"chrono",
]
features = [
"Config file evaluation with caching",
"Export Nickel to JSON/YAML",
"Format Nickel files",
"Validate Nickel projects",
"Cache status management",
]
[plugins.nu_plugin_nats]
upstream_url = "local"
upstream_branch = "main"
status = "ok"
auto_merge = false
local_path = "nu_plugin_nats"
description = "NATS JetStream plugin for provisioning platform event bus"
version = "0.110.0"
category = "provisioning"
commands = [
"nats stream setup",
"nats consumer setup",
"nats notify",
"nats pub",
"nats status",
]
dependencies = [
"async-nats",
"tokio",
"bytes",
"futures",
"serde_json",
]
features = [
"Idempotent stream + consumer provisioning",
"Pull drain of cli-notifications consumer",
"JetStream publish with stream+sequence ack",
"Live stream state for all 6 platform streams",
"NATS_SERVER env var or nats://127.0.0.1:4222 default",
]
[distribution]
excluded_plugins = ["nu_plugin_example"]
reason = "Reference/documentation plugin - excluded from distributions, installations, and collections. Still included in build and test for validation." reason = "Reference/documentation plugin - excluded from distributions, installations, and collections. Still included in build and test for validation."
# Metadata [settings]
nu_managed_dependencies = [
"nu-plugin",
"nu-protocol",
"nu-plugin-core",
"nu-plugin-protocol",
"nu-engine",
"nu-system",
"nu-path",
]
[registry] [registry]
version = "1.0.0" version = "1.0.0"
updated = "2025-10-09" updated = "2025-10-09"
format = "toml" format = "toml"
["plugins.nu_plugin_auth"]
upstream_url = "local"
upstream_branch = "main"
status = "error"
auto_merge = false
local_path = "nu_plugin_auth"
description = "Authentication plugin (JWT, MFA) for provisioning platform"
version = "0.1.0"
category = "provisioning"
commands = [
"auth login",
"auth logout",
"auth verify",
"auth sessions",
"auth mfa enroll",
"auth mfa verify",
]
dependencies = [
"jsonwebtoken",
"reqwest",
"keyring",
"rpassword",
"qrcode",
]
["plugins.nu_plugin_kms"]
upstream_url = "local"
upstream_branch = "main"
status = "error"
auto_merge = false
local_path = "nu_plugin_kms"
description = "KMS plugin (RustyVault, Age, Cosmian) for provisioning platform"
version = "0.1.0"
category = "provisioning"
backends = [
"rustyvault",
"age",
"cosmian",
"aws",
"vault",
]
commands = [
"kms encrypt",
"kms decrypt",
"kms generate-key",
"kms status",
]
dependencies = [
"reqwest",
"age",
"base64",
"serde",
]
["plugins.nu_plugin_orchestrator"]
upstream_url = "local"
upstream_branch = "main"
status = "error"
auto_merge = false
local_path = "nu_plugin_orchestrator"
description = "Orchestrator operations plugin (status, validate, tasks)"
version = "0.1.0"
category = "provisioning"
commands = [
"orch status",
"orch validate",
"orch tasks",
]
dependencies = [
"serde_json",
"serde_yaml",
"walkdir",
]
["plugins.nu_plugin_inquire"]
upstream_url = "https://github.com/jesusperezlorenzo/nu_plugin_inquire"
upstream_branch = "main"
status = "error"
auto_merge = false
local_path = "nu_plugin_inquire"
description = "Interactive forms and prompts plugin using inquire crate - solves TTY buffering issues"
version = "0.1.0"
category = "utility"
commands = [
"inquire text",
"inquire confirm",
"inquire select",
"inquire multi-select",
"inquire password",
"inquire custom",
"inquire editor",
"inquire date",
"inquire form",
]
dependencies = [
"inquire",
"serde",
"serde_json",
"toml",
"chrono",
"dialoguer",
]
["plugins.forminquire"]
upstream_url = "local"
upstream_branch = "main"
status = "error"
auto_merge = false
local_path = "forminquire"
description = "Standalone interactive forms and prompts library + CLI tool - no Nushell dependency required"
version = "0.1.0"
category = "utility"
type = "binary"
dual_mode = true
commands = [
"forminquire text",
"forminquire confirm",
"forminquire select",
"forminquire multi-select",
"forminquire password",
"forminquire custom",
"forminquire editor",
"forminquire date",
"forminquire form",
]
library_support = true
output_formats = [
"text",
"json",
"yaml",
]
features = [
"8 interactive prompt types",
"TOML-based form definitions",
"Automatic stdin fallback",
"Multiple output formats",
"Both library and CLI usage",
]
dependencies = [
"clap",
"inquire",
"serde",
"serde_json",
"serde_yaml",
"toml",
"chrono",
"dialoguer",
"anyhow",
"thiserror",
]
["plugins.nu_plugin_nickel"]
upstream_url = "local"
upstream_branch = "main"
status = "error"
auto_merge = false
local_path = "nu_plugin_nickel"
description = "Nushell plugin for Nickel configuration language with eval, export, format, and validation"
version = "0.1.0"
category = "provisioning"
commands = [
"nickel-eval",
"nickel-export",
"nickel-format",
"nickel-validate",
"nickel-cache-status",
]
dependencies = [
"nu-plugin",
"nu-protocol",
"anyhow",
"tempfile",
"sha2",
"serde",
"serde_json",
"dirs",
"chrono",
]
features = [
"Config file evaluation with caching",
"Export Nickel to JSON/YAML",
"Format Nickel files",
"Validate Nickel projects",
"Cache status management",
]
["plugins.nu_plugin_typedialog"]
upstream_url = "local"
upstream_branch = "main"
status = "ok"
auto_merge = false
local_path = "nu_plugin_typedialog"
description = "TypeDialog interactive forms and prompts plugin — replaces shlib TTY wrappers"
version = "0.110.0"
category = "provisioning"
commands = [
"typedialog form",
"typedialog nickel-roundtrip",
"typedialog text",
"typedialog confirm",
"typedialog select",
"typedialog multi-select",
"typedialog password",
]
dependencies = [
"nu-plugin",
"nu-protocol",
"typedialog-core",
"tokio",
"serde_json",
"thiserror",
"interprocess",
]
features = [
"CLI and web backends (--backend cli|web)",
"Nickel roundtrip with typecheck validation",
"Direct prompts: text, confirm, select, multi-select, password",
"ESC/cancel returns null (Value::nothing), not error",
"Initial values seeding via --initial <record>",
]
["plugins.nu_plugin_mcp"]
upstream_url = "local"
upstream_branch = "main"
status = "ok"
auto_merge = false
local_path = "nu_plugin_mcp"
description = "MCP client plugin — spawn and interact with provisioning-mcp-server via JSON-RPC 2.0"
version = "0.110.0"
category = "provisioning"
commands = [
"mcp connect",
"mcp tools list",
"mcp tool call",
"mcp disconnect",
]
dependencies = [
"nu-plugin",
"nu-protocol",
"serde_json",
"thiserror",
"interprocess",
]
features = [
"Spawn MCP server binary as child process",
"MCP protocol handshake (initialize + initialized)",
"Tool discovery via tools/list",
"Tool dispatch with record payload",
"Session state persisted in Arc<Mutex<Option<McpSession>>>",
"isError responses mapped to {error: true, message: ...} records",
]

File diff suppressed because one or more lines are too long

View File

@ -3,9 +3,11 @@
## Fundamental Rules for AI-Friendly Nushell Code ## Fundamental Rules for AI-Friendly Nushell Code
### Rule 1: One Command, One Purpose ### Rule 1: One Command, One Purpose
Every command must do exactly one thing. AI agents struggle with multi-purpose functions. Every command must do exactly one thing. AI agents struggle with multi-purpose functions.
```nushell ```nushell
nushell
# ✅ GOOD - Single purpose # ✅ GOOD - Single purpose
def extract-errors [log_file: path] -> table { def extract-errors [log_file: path] -> table {
open $log_file | lines | parse "{time} [{level}] {msg}" | where level == "ERROR" open $log_file | lines | parse "{time} [{level}] {msg}" | where level == "ERROR"
@ -18,9 +20,11 @@ def process-logs [log_file: path, --extract-errors, --count-warnings, --save-out
``` ```
### Rule 2: Explicit Type Signatures Always ### Rule 2: Explicit Type Signatures Always
AI agents need clear contracts. Never omit types. AI agents need clear contracts. Never omit types.
```nushell ```nushell
nushell
# ✅ GOOD - Complete type information # ✅ GOOD - Complete type information
def calculate-average [numbers: list<float>] -> float { def calculate-average [numbers: list<float>] -> float {
$numbers | math avg $numbers | math avg
@ -33,9 +37,11 @@ def calculate-average [numbers] {
``` ```
### Rule 3: Return Early, Fail Fast ### Rule 3: Return Early, Fail Fast
Check preconditions immediately. Don't nest error handling. Check preconditions immediately. Don't nest error handling.
```nushell ```nushell
nushell
# ✅ GOOD - Early returns # ✅ GOOD - Early returns
def process-file [path: path] -> table { def process-file [path: path] -> table {
if not ($path | path exists) { if not ($path | path exists) {
@ -66,9 +72,11 @@ def process-file [path: path] -> table {
## Essential Patterns for AI Code Generation ## Essential Patterns for AI Code Generation
### Pattern 1: Command Template Pattern ### Pattern 1: Command Template Pattern
Use this structure for EVERY command: Use this structure for EVERY command:
```nushell ```nushell
nushell
# [PURPOSE]: Single-line description of what this does # [PURPOSE]: Single-line description of what this does
# [INPUT]: Expected input format # [INPUT]: Expected input format
# [OUTPUT]: Output format # [OUTPUT]: Output format
@ -90,9 +98,11 @@ def command-name [
``` ```
### Pattern 2: Pipeline Stage Pattern ### Pattern 2: Pipeline Stage Pattern
Each pipeline stage must be self-contained and testable: Each pipeline stage must be self-contained and testable:
```nushell ```nushell
nushell
# ✅ GOOD - Clear pipeline stages # ✅ GOOD - Clear pipeline stages
def analyze-data [input: path] -> table { def analyze-data [input: path] -> table {
load-data $input load-data $input
@ -111,11 +121,13 @@ def format-output [data: table] -> table { $data | select relevant_columns }
``` ```
### Pattern 3: Error Context Pattern ### Pattern 3: Error Context Pattern
Always provide context for errors that AI can use to fix issues: Always provide context for errors that AI can use to fix issues:
in Nushell 0.108, try-catch with error parameter might not be supported when assigning to variables. in Nushell 0.108, try-catch with error parameter might not be supported when assigning to variables.
```nushell ```nushell
nushell
# ✅ GOOD - Detailed error context # ✅ GOOD - Detailed error context
def parse-config [config_path: path] -> record { def parse-config [config_path: path] -> record {
try { try {
@ -134,9 +146,11 @@ def parse-config [config_path: path] -> record {
``` ```
### Pattern 4: Data Validation Pattern ### Pattern 4: Data Validation Pattern
Validate data structure at boundaries: Validate data structure at boundaries:
```nushell ```nushell
nushell
# ✅ GOOD - Explicit validation # ✅ GOOD - Explicit validation
def process-user-data [data: table] -> table { def process-user-data [data: table] -> table {
# Define expected schema # Define expected schema
@ -163,9 +177,11 @@ def process-user-data [data: table] -> table {
## Critical Rules for AI Tool Integration ## Critical Rules for AI Tool Integration
### Rule 4: No Side Effects in Functions ### Rule 4: No Side Effects in Functions
Functions must be pure unless explicitly named as mutations: Functions must be pure unless explicitly named as mutations:
```nushell ```nushell
nushell
# ✅ GOOD - Pure function # ✅ GOOD - Pure function
def calculate-tax [amount: float, rate: float] -> float { def calculate-tax [amount: float, rate: float] -> float {
$amount * $rate $amount * $rate
@ -186,9 +202,11 @@ def calculate-tax [amount: float, rate: float] -> float {
``` ```
### Rule 5: Atomic Operations ### Rule 5: Atomic Operations
Every operation must be atomic - it either completely succeeds or completely fails: Every operation must be atomic - it either completely succeeds or completely fails:
```nushell ```nushell
nushell
# ✅ GOOD - Atomic operation # ✅ GOOD - Atomic operation
def update-json-file [path: path, updates: record] -> nothing { def update-json-file [path: path, updates: record] -> nothing {
# Read, modify, write as single operation # Read, modify, write as single operation
@ -208,9 +226,11 @@ def update-json-file [path: path, updates: record] -> nothing {
``` ```
### Rule 6: Explicit Dependencies ### Rule 6: Explicit Dependencies
Never rely on global state or environment without declaring it: Never rely on global state or environment without declaring it:
```nushell ```nushell
nushell
# ✅ GOOD - Explicit dependencies # ✅ GOOD - Explicit dependencies
def api-request [ def api-request [
endpoint: string endpoint: string
@ -232,9 +252,11 @@ def api-request [endpoint: string] -> any {
## Structured Data Patterns ## Structured Data Patterns
### Pattern 5: Table Transformation Pattern ### Pattern 5: Table Transformation Pattern
Always use Nushell's table operations instead of loops: Always use Nushell's table operations instead of loops:
```nushell ```nushell
nushell
# ✅ GOOD - Table operations # ✅ GOOD - Table operations
def transform-sales [sales: table] -> table { def transform-sales [sales: table] -> table {
$sales $sales
@ -258,9 +280,11 @@ def transform-sales [sales: table] -> table {
``` ```
### Pattern 6: Schema Definition Pattern ### Pattern 6: Schema Definition Pattern
Define data schemas explicitly for AI understanding: Define data schemas explicitly for AI understanding:
```nushell ```nushell
nushell
# Schema definitions at module level # Schema definitions at module level
const USER_SCHEMA = { const USER_SCHEMA = {
id: "int" id: "int"
@ -291,9 +315,11 @@ def validate-user [user: record] -> bool {
## AI-Specific Patterns ## AI-Specific Patterns
### Pattern 7: Self-Documenting Code Pattern ### Pattern 7: Self-Documenting Code Pattern
Include inline documentation that AI can parse: Include inline documentation that AI can parse:
```nushell ```nushell
nushell
def complex-calculation [ def complex-calculation [
data: table # @format: [{x: float, y: float, weight: float}] data: table # @format: [{x: float, y: float, weight: float}]
] -> record { # @returns: {mean: float, std: float, correlation: float} ] -> record { # @returns: {mean: float, std: float, correlation: float}
@ -318,9 +344,11 @@ def complex-calculation [
``` ```
### Pattern 8: Testable Unit Pattern ### Pattern 8: Testable Unit Pattern
Every function must include test examples: Every function must include test examples:
```nushell ```nushell
nushell
# Function with embedded test cases # Function with embedded test cases
def parse-version [version: string] -> record { def parse-version [version: string] -> record {
# @test: "1.2.3" -> {major: 1, minor: 2, patch: 3} # @test: "1.2.3" -> {major: 1, minor: 2, patch: 3}
@ -342,9 +370,11 @@ def test-parse-version [] {
``` ```
### Pattern 9: Incremental Computation Pattern ### Pattern 9: Incremental Computation Pattern
Break complex computations into verifiable steps: Break complex computations into verifiable steps:
```nushell ```nushell
nushell
# ✅ GOOD - Each step is verifiable # ✅ GOOD - Each step is verifiable
def analyze-dataset [data: path] -> record { def analyze-dataset [data: path] -> record {
# Load and get shape # Load and get shape
@ -373,9 +403,11 @@ def analyze-dataset [data: path] -> record {
## Module Organization Rules ## Module Organization Rules
### Rule 7: Single Responsibility Modules ### Rule 7: Single Responsibility Modules
Each module handles one domain: Each module handles one domain:
```nushell ```nushell
nushell
# file: data_validation.nu # file: data_validation.nu
module data_validation { module data_validation {
export def validate-email [email: string] -> bool { } export def validate-email [email: string] -> bool { }
@ -392,9 +424,11 @@ module data_transformation {
``` ```
### Rule 8: Explicit Exports ### Rule 8: Explicit Exports
Only export what's needed: Only export what's needed:
```nushell ```nushell
nushell
module api_client { module api_client {
# Public API # Public API
export def get [endpoint: string] -> any { export def get [endpoint: string] -> any {
@ -415,9 +449,11 @@ module api_client {
## Performance Rules for AI Tools ## Performance Rules for AI Tools
### Rule 9: Lazy Evaluation ### Rule 9: Lazy Evaluation
Don't compute until necessary: Don't compute until necessary:
```nushell ```nushell
nushell
# ✅ GOOD - Lazy evaluation # ✅ GOOD - Lazy evaluation
def process-conditionally [ def process-conditionally [
data: table data: table
@ -436,9 +472,11 @@ def process-conditionally [
``` ```
### Rule 10: Stream Large Data ### Rule 10: Stream Large Data
Never load entire large files into memory: Never load entire large files into memory:
```nushell ```nushell
nushell
# ✅ GOOD - Streaming # ✅ GOOD - Streaming
def process-large-file [path: path] -> nothing { def process-large-file [path: path] -> nothing {
open --raw $path open --raw $path
@ -460,9 +498,11 @@ def process-large-file [path: path] -> table {
## Error Handling Rules ## Error Handling Rules
### Rule 11: Never Swallow Errors ### Rule 11: Never Swallow Errors
Always propagate or handle errors explicitly: Always propagate or handle errors explicitly:
```nushell ```nushell
nushell
# ✅ GOOD - Explicit error handling # ✅ GOOD - Explicit error handling
def safe-divide [a: float, b: float] -> float { def safe-divide [a: float, b: float] -> float {
if $b == 0 { if $b == 0 {
@ -478,9 +518,11 @@ def safe-divide [a: float, b: float] -> float {
``` ```
### Rule 12: Structured Error Returns ### Rule 12: Structured Error Returns
Use consistent error structures: Use consistent error structures:
```nushell ```nushell
nushell
# Define error type # Define error type
const ERROR_SCHEMA = { const ERROR_SCHEMA = {
success: "bool" success: "bool"
@ -512,9 +554,11 @@ def api-call [url: string] -> record {
## Code Generation Rules for AI ## Code Generation Rules for AI
### Rule 13: Predictable Naming ### Rule 13: Predictable Naming
Use consistent, predictable names: Use consistent, predictable names:
```nushell ```nushell
nushell
# Naming conventions AI can predict: # Naming conventions AI can predict:
# - get-* : Returns data without modification # - get-* : Returns data without modification
# - set-* : Updates data # - set-* : Updates data
@ -536,9 +580,11 @@ def parse-json [text: string] -> any { }
``` ```
### Rule 14: Consistent Parameter Order ### Rule 14: Consistent Parameter Order
Always use this parameter order: Always use this parameter order:
```nushell ```nushell
nushell
# Order: required positional, optional positional, flags # Order: required positional, optional positional, flags
def command [ def command [
required1: type # Required parameters first required1: type # Required parameters first
@ -550,9 +596,11 @@ def command [
``` ```
### Rule 15: Return Type Consistency ### Rule 15: Return Type Consistency
Use consistent return types for similar operations: Use consistent return types for similar operations:
```nushell ```nushell
nushell
# All getters return record or null # All getters return record or null
def get-config [] -> record? { def get-config [] -> record? {
try { open config.json } catch { null } try { open config.json } catch { null }
@ -571,9 +619,11 @@ def transform-table [input: table] -> table {
``` ```
### Rule 16: Function Signature Syntax with Pipeline Types (Nushell 0.107.1+) ### Rule 16: Function Signature Syntax with Pipeline Types (Nushell 0.107.1+)
**CRITICAL**: When using return type annotations, use the pipeline signature: `[params]: input_type -> return_type` **CRITICAL**: When using return type annotations, use the pipeline signature: `[params]: input_type -> return_type`
```nushell ```nushell
nushell
# ✅ GOOD - Complete pipeline signature # ✅ GOOD - Complete pipeline signature
def process-data [input: string]: nothing -> table { def process-data [input: string]: nothing -> table {
$input | from json $input | from json
@ -601,9 +651,11 @@ def process-data [input: string] {
**Note**: The syntax is `[parameters]: input_type -> return_type`. Both the colon AND arrow are required when specifying return types. This has been the syntax since Nushell 0.107.1. **Note**: The syntax is `[parameters]: input_type -> return_type`. Both the colon AND arrow are required when specifying return types. This has been the syntax since Nushell 0.107.1.
### Rule 17: String Interpolation - Always Use Parentheses ### Rule 17: String Interpolation - Always Use Parentheses
**CRITICAL**: In string interpolations, ALWAYS use parentheses `($var)` or `($expr)` for ALL interpolations. **CRITICAL**: In string interpolations, ALWAYS use parentheses `($var)` or `($expr)` for ALL interpolations.
```nushell ```nushell
nushell
# ✅ GOOD - Parentheses for variables # ✅ GOOD - Parentheses for variables
print $"Processing file ($filename) at ($timestamp)" print $"Processing file ($filename) at ($timestamp)"
print $"Server ($hostname) running on port ($port)" print $"Server ($hostname) running on port ($port)"
@ -624,12 +676,14 @@ print $"Server $hostname on port $port"
``` ```
**Why**: **Why**:
- Parentheses `($var)` or `($expr)` are the ONLY way to interpolate in Nushell strings - Parentheses `($var)` or `($expr)` are the ONLY way to interpolate in Nushell strings
- Square brackets `[...]` are treated as literal characters (no interpolation) - Square brackets `[...]` are treated as literal characters (no interpolation)
- Both variables and expressions use the same syntax: `($something)` - Both variables and expressions use the same syntax: `($something)`
- Consistent syntax reduces errors and improves maintainability - Consistent syntax reduces errors and improves maintainability
**Rule of thumb**: **Rule of thumb**:
- Variable? Use `($var)` - Variable? Use `($var)`
- Expression? Use `($expr)` - Expression? Use `($expr)`
- Function call? Use `($fn arg)` - Function call? Use `($fn arg)`
@ -640,24 +694,31 @@ print $"Server $hostname on port $port"
**CRITICAL**: In `$"..."` strings, parentheses that are NOT variable interpolation MUST be escaped. **CRITICAL**: In `$"..."` strings, parentheses that are NOT variable interpolation MUST be escaped.
**WRONG**: **WRONG**:
```nushell ```nushell
nushell
let msg = $"Update? (yes/no): " # ERROR: (yes/no) treated as command let msg = $"Update? (yes/no): " # ERROR: (yes/no) treated as command
let text = $"Use format (JSON/YAML)" # ERROR: tries to execute command let text = $"Use format (JSON/YAML)" # ERROR: tries to execute command
``` ```
**CORRECT**: **CORRECT**:
```nushell ```nushell
nushell
let msg = $"Update? \(yes/no\): " # Escaped literal parentheses let msg = $"Update? \(yes/no\): " # Escaped literal parentheses
let text = $"Use format \(JSON/YAML\)" # Escaped literal parentheses let text = $"Use format \(JSON/YAML\)" # Escaped literal parentheses
let msg = $"Value: ($var)" # Unescaped for variable interpolation let msg = $"Value: ($var)" # Unescaped for variable interpolation
``` ```
**Simple rule**: **Simple rule**:
- If `()` contains a variable name → keep as is: `($variable)` - If `()` contains a variable name → keep as is: `($variable)`
- If `()` is literal text → escape with backslash: `\(text\)` - If `()` is literal text → escape with backslash: `\(text\)`
**Common mistake patterns:** **Common mistake patterns:**
```nushell ```nushell
nushell
# BAD - will try to execute 'yes/no' command # BAD - will try to execute 'yes/no' command
input $"Continue? (yes/no): " input $"Continue? (yes/no): "
@ -676,6 +737,7 @@ log_info $"Format \(example\): data"
## Quick Reference Card for AI Agents ## Quick Reference Card for AI Agents
```nushell ```nushell
nushell
# TEMPLATE: Copy-paste this for new commands (Nushell 0.107.1+) # TEMPLATE: Copy-paste this for new commands (Nushell 0.107.1+)
# [PURPOSE]: # [PURPOSE]:
# [INPUT]: # [INPUT]:
@ -716,6 +778,7 @@ def command-name [
## Summary Checklist for AI-Compatible Nushell ## Summary Checklist for AI-Compatible Nushell
✅ **Every function has:** ✅ **Every function has:**
- Explicit type signatures with pipeline syntax `[param: type]: input_type -> return_type {` - Explicit type signatures with pipeline syntax `[param: type]: input_type -> return_type {`
- Single responsibility - Single responsibility
- Early validation - Early validation
@ -723,6 +786,7 @@ def command-name [
- Test examples - Test examples
✅ **Never use:** ✅ **Never use:**
- Global state without declaration - Global state without declaration
- Hidden side effects - Hidden side effects
- Nested conditionals (prefer early returns) - Nested conditionals (prefer early returns)
@ -733,6 +797,7 @@ def command-name [
- Function signatures without both colon AND arrow when specifying return types - Function signatures without both colon AND arrow when specifying return types
✅ **Always prefer:** ✅ **Always prefer:**
- Pipeline operations over loops - Pipeline operations over loops
- Pure functions over stateful - Pure functions over stateful
- Explicit over implicit - Explicit over implicit
@ -741,6 +806,7 @@ def command-name [
- Parentheses `($var)` for ALL string interpolations (variables and expressions) - Parentheses `($var)` for ALL string interpolations (variables and expressions)
✅ **For AI tools specifically:** ✅ **For AI tools specifically:**
- Use predictable naming patterns - Use predictable naming patterns
- Include operation markers (! for mutations) - Include operation markers (! for mutations)
- Document schemas inline - Document schemas inline
@ -752,37 +818,36 @@ Following these rules and patterns ensures that AI agents like Claude Code can e
1. Try-Catch Block Pattern (10 files) 1. Try-Catch Block Pattern (10 files)
- Issue: Nushell 0.108 has stricter parsing for try-catch blocks - Issue: Nushell 0.108 has stricter parsing for try-catch blocks
- Solution: Replace try {...} catch {...} with complete-based error handling: - Solution: Replace try {...} catch {...} with complete-based error handling:
let result = (do { ... } | complete) let result = (do { ... } | complete)
if $result.exit_code == 0 { $result.stdout } else { error_value } if $result.exit_code == 0 { $result.stdout } else { error_value }
- Files fixed: - Files fixed:
- workspace/version.nu - workspace/version.nu
- workspace/migration.nu (4 blocks) - workspace/migration.nu (4 blocks)
- user/config.nu - user/config.nu
- config/loader.nu - config/loader.nu
- oci/client.nu (8 blocks - OCI currently disabled) - oci/client.nu (8 blocks - OCI currently disabled)
2. Function Signature Syntax (2 instances) 1. Function Signature Syntax (2 instances)
- Issue: Missing input type in signatures - Issue: Missing input type in signatures
- Old: def foo [x: string] -> bool - Old: def foo [x: string] -> bool
- New: def foo [x: string]: nothing -> bool - New: def foo [x: string]: nothing -> bool
- Files fixed: workspace/helpers.nu - Files fixed: workspace/helpers.nu
3. Boolean Flag Syntax (1 instance) 1. Boolean Flag Syntax (1 instance)
- Issue: Type annotations not allowed on boolean flags - Issue: Type annotations not allowed on boolean flags
- Old: --flag: bool = true - Old: --flag: bool = true
- New: --flag = true - New: --flag = true
- Files fixed: main_provisioning/contexts.nu - Files fixed: main_provisioning/contexts.nu
4. Variable Type Initialization (1 instance) 1. Variable Type Initialization (1 instance)
- Issue: Can't assign record to null variable in 0.108
- Old: mut var = null; $var = {record}
- New: mut var = {success: false}; $var = {record}
- Issue: Can't assign record to null variable in 0.108
- Old: mut var = null; $var = {record}
- New: mut var = {success: false}; $var = {record}
#" Before (fails in Nushell 0.107.1) #" Before (fails in Nushell 0.107.1)
try { try {
@ -793,7 +858,8 @@ Following these rules and patterns ensures that AI agents like Claude Code can e
default_value default_value
} }
# After (works in Nushell 0.107.1) # After (works in Nushell 0.107.1)
let result = (do { let result = (do {
# operations # operations
result result

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,65 +1,64 @@
# Tools Module - Development Tools and Utilities # Tools Module - Development Tools and Utilities
# Commands for development utilities, plugin management, and system tools # Commands for development utilities, plugin management, and system tools
# 🔍 VERSION AND VALIDATION COMMANDS # 🔍 VERSION AND VALIDATION COMMANDS
# Check nushell version consistency # Check nushell version consistency
[no-cd] [no-cd]
validate-nushell: validate-nushell:
@echo "🔍 Validating nushell version consistency..." @echo "🔍 Validating nushell version consistency..."
@{{justfile_directory()}}/scripts/run.sh --check-only @{{ justfile_directory() }}/scripts/run.sh --check-only
# Fix nushell version mismatches # Fix nushell version mismatches
[no-cd] [no-cd]
fix-nushell: fix-nushell:
@echo "🔧 Fixing nushell version mismatches..." @echo "🔧 Fixing nushell version mismatches..."
@{{justfile_directory()}}/scripts/run.sh --fix --check-only @{{ justfile_directory() }}/scripts/run.sh --fix --check-only
# Update nu dependency versions # Update nu dependency versions
[no-cd] [no-cd]
update-nu-versions: update-nu-versions:
@echo "🔄 Updating nu dependency versions..." @echo "🔄 Updating nu dependency versions..."
@{{justfile_directory()}}/scripts/run.sh update_nu_versions.nu @{{ justfile_directory() }}/scripts/run.sh update_nu_versions.nu
# List current nu dependency versions # List current nu dependency versions
[no-cd] [no-cd]
list-nu-versions: list-nu-versions:
@echo "📋 Current nu dependency versions:" @echo "📋 Current nu dependency versions:"
@{{justfile_directory()}}/scripts/run.sh update_nu_versions.nu list @{{ justfile_directory() }}/scripts/run.sh update_nu_versions.nu list
# Update nushell submodule # Update nushell submodule
[no-cd] [no-cd]
update-nushell: update-nushell:
@echo "🔄 Updating nushell submodule..." @echo "🔄 Updating nushell submodule..."
@bash {{justfile_directory()}}/scripts/sh/update_nushell.sh update @bash {{ justfile_directory() }}/scripts/sh/update_nushell.sh update
# 🆕 PLUGIN MANAGEMENT COMMANDS # 🆕 PLUGIN MANAGEMENT COMMANDS
# Create a new plugin from template # Create a new plugin from template
[no-cd] [no-cd]
make-plugin PLUGIN_NAME: make-plugin PLUGIN_NAME:
@echo "🆕 Creating new plugin: {{PLUGIN_NAME}}" @echo "🆕 Creating new plugin: {{ PLUGIN_NAME }}"
@{{justfile_directory()}}/scripts/run.sh make_plugin.nu {{PLUGIN_NAME}} @{{ justfile_directory() }}/scripts/run.sh make_plugin.nu {{ PLUGIN_NAME }}
# Install specific plugin locally (build + register) # Install specific plugin locally (build + register)
[no-cd] [no-cd]
install-plugin PLUGIN: install-plugin PLUGIN:
@{{justfile_directory()}}/scripts/run.sh install_plugin.nu {{PLUGIN}} --verify @{{ justfile_directory() }}/scripts/run.sh install_plugin.nu {{ PLUGIN }} --verify
# Register specific plugin with nushell # Register specific plugin with nushell
[no-cd] [no-cd]
register-plugin PLUGIN: register-plugin PLUGIN:
@{{justfile_directory()}}/scripts/run.sh register_plugins.nu register ./{{PLUGIN}}/target/release/{{PLUGIN}} --verify @{{ justfile_directory() }}/scripts/run.sh register_plugins.nu register ./{{ PLUGIN }}/target/release/{{ PLUGIN }} --verify
# Register all built plugins with nushell # Register all built plugins with nushell
[no-cd] [no-cd]
register-all-plugins: register-all-plugins:
@{{justfile_directory()}}/scripts/run.sh register_plugins.nu register-all --verify @{{ justfile_directory() }}/scripts/run.sh register_plugins.nu register-all --verify
# Verify plugin registration status # Verify plugin registration status
[no-cd] [no-cd]
verify-plugins: verify-plugins:
@{{justfile_directory()}}/scripts/run.sh register_plugins.nu verify @{{ justfile_directory() }}/scripts/run.sh register_plugins.nu verify
# Install plugins locally from distribution # Install plugins locally from distribution
[no-cd] [no-cd]
@ -83,7 +82,7 @@ install-local:
PLATFORM_DIR=$(find distribution -maxdepth 1 -type d -name "*-*" | head -n1) PLATFORM_DIR=$(find distribution -maxdepth 1 -type d -name "*-*" | head -n1)
fi fi
if [ -z "$PLATFORM_DIR" ] || [ ! -f "$PLATFORM_DIR/install.nu" ]; then if [ -z "$PLATFORM_DIR" ] || [ ! -f "$PLATFORM_DIR/install.sh" ]; then
echo "❌ Installation script not found in distribution" echo "❌ Installation script not found in distribution"
echo "💡 Run 'just collect' to regenerate distribution" echo "💡 Run 'just collect' to regenerate distribution"
exit 1 exit 1
@ -92,7 +91,7 @@ install-local:
echo "📦 Installing plugins locally from distribution..." echo "📦 Installing plugins locally from distribution..."
echo "💡 Using register-only mode (no sudo required)" echo "💡 Using register-only mode (no sudo required)"
echo "💡 Using distribution: $PLATFORM_DIR" echo "💡 Using distribution: $PLATFORM_DIR"
cd "$PLATFORM_DIR" && nu install.nu --verify cd "$PLATFORM_DIR" && bash install.sh --local
echo "" echo ""
echo "✅ Local installation completed!" echo "✅ Local installation completed!"
@ -120,7 +119,7 @@ install-system:
PLATFORM_DIR=$(find distribution -maxdepth 1 -type d -name "*-*" | head -n1) PLATFORM_DIR=$(find distribution -maxdepth 1 -type d -name "*-*" | head -n1)
fi fi
if [ -z "$PLATFORM_DIR" ] || [ ! -f "$PLATFORM_DIR/install.nu" ]; then if [ -z "$PLATFORM_DIR" ] || [ ! -f "$PLATFORM_DIR/install.sh" ]; then
echo "❌ Installation script not found in distribution" echo "❌ Installation script not found in distribution"
echo "💡 Run 'just collect' to regenerate distribution" echo "💡 Run 'just collect' to regenerate distribution"
exit 1 exit 1
@ -136,6 +135,7 @@ install-system:
# Copy nushell binary to user location (safer than system) # Copy nushell binary to user location (safer than system)
echo " Installing nushell binary to ~/.local/bin..." echo " Installing nushell binary to ~/.local/bin..."
rm -f ~/.local/bin/nu # Remove old version if it exists
cp "$PLATFORM_DIR/nu" ~/.local/bin/nu cp "$PLATFORM_DIR/nu" ~/.local/bin/nu
chmod +x ~/.local/bin/nu chmod +x ~/.local/bin/nu
@ -169,7 +169,7 @@ install-system:
# Verify installation # Verify installation
echo " Verifying installation..." echo " Verifying installation..."
~/.local/bin/nu -c "plugin list" | head -20 ~/.local/bin/nu -c "plugin list" # | head -20
# Update PATH suggestion # Update PATH suggestion
echo "" echo ""
@ -186,7 +186,7 @@ install-from-archive ARCHIVE="":
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
ARCHIVE="{{ARCHIVE}}" ARCHIVE="{{ ARCHIVE }}"
# If no archive specified, find the latest one # If no archive specified, find the latest one
if [ -z "$ARCHIVE" ]; then if [ -z "$ARCHIVE" ]; then
@ -206,7 +206,7 @@ install-from-archive ARCHIVE="":
fi fi
echo "📦 Installing from archive: $ARCHIVE" echo "📦 Installing from archive: $ARCHIVE"
./install.sh --source-path "$ARCHIVE" --install-dir ~/.local --verify ./install.sh --source-path "$ARCHIVE" --install-dir ~/.local/bin --verify
# Install from bin_archives/ (fastest - everything already built) # Install from bin_archives/ (fastest - everything already built)
[no-cd] [no-cd]
@ -230,7 +230,7 @@ install-fast:
echo "📍 Installing to: ~/.local/bin" echo "📍 Installing to: ~/.local/bin"
echo "" echo ""
./install.sh --source-path "$ARCHIVE" --install-dir ~/.local --verify ./install.sh --source-path "$ARCHIVE" --install-dir ~/.local/bin --verify
echo "" echo ""
echo "✅ Installation complete!" echo "✅ Installation complete!"
@ -240,18 +240,18 @@ install-fast:
# Remove plugin from workspace # Remove plugin from workspace
[no-cd] [no-cd]
remove-plugin PLUGIN: remove-plugin PLUGIN:
@echo "🗑️ Removing plugin {{PLUGIN}}..." @echo "🗑️ Removing plugin {{ PLUGIN }}..."
@if [ -d "{{PLUGIN}}" ]; then \ @if [ -d "{{ PLUGIN }}" ]; then \
echo "Are you sure you want to remove {{PLUGIN}}? (y/N)"; \ echo "Are you sure you want to remove {{ PLUGIN }}? (y/N)"; \
read -r confirm; \ read -r confirm; \
if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \
rm -rf "{{PLUGIN}}"; \ rm -rf "{{ PLUGIN }}"; \
echo "✅ Removed {{PLUGIN}}"; \ echo "✅ Removed {{ PLUGIN }}"; \
else \ else \
echo "❌ Cancelled"; \ echo "❌ Cancelled"; \
fi; \ fi; \
else \ else \
echo "Plugin {{PLUGIN}} not found"; \ echo "Plugin {{ PLUGIN }} not found"; \
fi fi
# List all plugins # List all plugins
@ -267,40 +267,40 @@ list-plugins:
# Show plugin information # Show plugin information
[no-cd] [no-cd]
plugin-info PLUGIN: plugin-info PLUGIN:
@echo " Plugin Information: {{PLUGIN}}" @echo " Plugin Information: {{ PLUGIN }}"
@if [ -d "{{PLUGIN}}" ]; then \ @if [ -d "{{ PLUGIN }}" ]; then \
echo "Name: {{PLUGIN}}"; \ echo "Name: {{ PLUGIN }}"; \
echo "Path: $(pwd)/{{PLUGIN}}"; \ echo "Path: $(pwd)/{{ PLUGIN }}"; \
if [ -f "{{PLUGIN}}/Cargo.toml" ]; then \ if [ -f "{{ PLUGIN }}/Cargo.toml" ]; then \
echo "Version: $(grep '^version' {{PLUGIN}}/Cargo.toml | cut -d'"' -f2)"; \ echo "Version: $(grep '^version' {{ PLUGIN }}/Cargo.toml | cut -d'"' -f2)"; \
echo "Description: $(grep '^description' {{PLUGIN}}/Cargo.toml | cut -d'"' -f2 || echo 'Not available')"; \ echo "Description: $(grep '^description' {{ PLUGIN }}/Cargo.toml | cut -d'"' -f2 || echo 'Not available')"; \
fi; \ fi; \
echo "Binary exists: $([ -f {{PLUGIN}}/target/release/{{PLUGIN}} ] && echo 'Yes' || echo 'No')"; \ echo "Binary exists: $([ -f {{ PLUGIN }}/target/release/{{ PLUGIN }} ] && echo 'Yes' || echo 'No')"; \
echo "Last modified: $(stat -c %y {{PLUGIN}} 2>/dev/null || stat -f %m {{PLUGIN}} 2>/dev/null || echo 'Unknown')"; \ echo "Last modified: $(stat -c %y {{ PLUGIN }} 2>/dev/null || stat -f %m {{ PLUGIN }} 2>/dev/null || echo 'Unknown')"; \
else \ else \
echo "Plugin {{PLUGIN}} not found"; \ echo "Plugin {{ PLUGIN }} not found"; \
fi fi
# 🛠️ DEVELOPMENT TOOLS # 🛠️ DEVELOPMENT TOOLS
# Run command on specific plugin # Run command on specific plugin
plugin PLUGIN CMD: plugin PLUGIN CMD:
@echo "🔧 Running '{{CMD}}' on {{PLUGIN}}" @echo "🔧 Running '{{ CMD }}' on {{ PLUGIN }}"
@cd {{PLUGIN}} && {{CMD}} @cd {{ PLUGIN }} && {{ CMD }}
# Check specific plugin # Check specific plugin
[no-cd] [no-cd]
check-plugin PLUGIN: check-plugin PLUGIN:
@echo "🔍 Checking {{PLUGIN}}..." @echo "🔍 Checking {{ PLUGIN }}..."
@cd {{PLUGIN}} && cargo check @cd {{ PLUGIN }} && cargo check
# Watch for changes and rebuild (requires entr) # Watch for changes and rebuild (requires entr)
[no-cd] [no-cd]
watch PLUGIN: watch PLUGIN:
#!/usr/bin/env bash #!/usr/bin/env bash
if command -v entr >/dev/null 2>&1; then if command -v entr >/dev/null 2>&1; then
echo "👀 Watching {{PLUGIN}} for changes..." echo "👀 Watching {{ PLUGIN }} for changes..."
find {{PLUGIN}}/src -name "*.rs" | entr just build-plugin {{PLUGIN}} find {{ PLUGIN }}/src -name "*.rs" | entr just build-plugin {{ PLUGIN }}
else else
echo "❌ entr not installed. Install with: brew install entr (macOS) or apt install entr (Ubuntu)" echo "❌ entr not installed. Install with: brew install entr (macOS) or apt install entr (Ubuntu)"
fi fi
@ -420,12 +420,12 @@ clean:
# Clean specific plugin # Clean specific plugin
[no-cd] [no-cd]
clean-plugin PLUGIN: clean-plugin PLUGIN:
@echo "🧹 Cleaning {{PLUGIN}}..." @echo "🧹 Cleaning {{ PLUGIN }}..."
@if [ -d "{{PLUGIN}}/target" ]; then \ @if [ -d "{{ PLUGIN }}/target" ]; then \
echo "Removing {{PLUGIN}}/target..."; \ echo "Removing {{ PLUGIN }}/target..."; \
rm -rf "{{PLUGIN}}/target"; \ rm -rf "{{ PLUGIN }}/target"; \
else \ else \
echo "No target directory found for {{PLUGIN}}"; \ echo "No target directory found for {{ PLUGIN }}"; \
fi fi
# Clean distribution files # Clean distribution files
@ -498,8 +498,8 @@ config-list:
# Edit configuration file # Edit configuration file
[no-cd] [no-cd]
config-edit FILE: config-edit FILE:
@echo "✏️ Editing configuration: {{FILE}}" @echo "✏️ Editing configuration: {{ FILE }}"
@${EDITOR:-nano} {{FILE}} @${EDITOR:-nano} {{ FILE }}
# Backup configuration # Backup configuration
[no-cd] [no-cd]

View File

@ -262,6 +262,20 @@ list-versions:
@echo "📋 Plugin Version List" @echo "📋 Plugin Version List"
@nu {{justfile_directory()}}/scripts/update_all_plugins.nu --list @nu {{justfile_directory()}}/scripts/update_all_plugins.nu --list
# Replace [package] version from old to new (only exact matches)
[no-cd]
[group('version-update')]
replace-package-version FROM TO:
@echo "🔄 Replacing [package] version from {{FROM}} to {{TO}}"
@nu {{justfile_directory()}}/scripts/update_all_plugins.nu replace-version {{FROM}} {{TO}}
# Preview [package] version replacement (dry-run)
[no-cd]
[group('version-update')]
preview-package-version FROM TO:
@echo "🔍 Preview: Replacing [package] version from {{FROM}} to {{TO}}"
@nu {{justfile_directory()}}/scripts/update_all_plugins.nu replace-version {{FROM}} {{TO}} --dry-run
# 📦 DISTRIBUTION CREATION # 📦 DISTRIBUTION CREATION
# Create complete distribution packages (nushell + all plugins) # Create complete distribution packages (nushell + all plugins)
@ -480,6 +494,10 @@ update-help:
@echo " just dist-status - Show distribution status" @echo " just dist-status - Show distribution status"
@echo " just list-versions - List plugin versions" @echo " just list-versions - List plugin versions"
@echo "" @echo ""
@echo "Package Version Management:"
@echo " just replace-package-version 0.109.1 0.110.0 - Replace [package] version"
@echo " just preview-package-version 0.109.1 0.110.0 - Preview replacement (dry-run)"
@echo ""
@echo "Documentation:" @echo "Documentation:"
@echo " just update-docs - Show documentation paths" @echo " just update-docs - Show documentation paths"
@echo " cat guides/COMPLETE_VERSION_UPDATE_GUIDE.md" @echo " cat guides/COMPLETE_VERSION_UPDATE_GUIDE.md"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu_plugin_auth" name = "nu_plugin_auth"
version = "0.109.1" version = "0.111.0"
authors = ["Jesus Perez <jesus@librecloud.online>"] authors = ["Jesus Perez <jesus@librecloud.online>"]
edition = "2021" edition = "2021"
description = "Nushell plugin for provisioning authentication (JWT, MFA)" description = "Nushell plugin for provisioning authentication (JWT, MFA)"
@ -8,8 +8,8 @@ repository = "https://github.com/provisioning/nu_plugin_auth"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
jsonwebtoken = "=9.3" jsonwebtoken = "=9.3"
serde_json = "1.0" serde_json = "1.0"
keyring = "3.6" keyring = "3.6"
@ -39,6 +39,5 @@ features = ["full"]
version = "5.7" version = "5.7"
features = ["qr"] features = ["qr"]
[dev-dependencies.nu-plugin-test-support] [dev-dependencies]
version = "0.109.1" nu-plugin-test-support = "0.111.0"
path = "../nushell/crates/nu-plugin-test-support"

View File

@ -1,44 +0,0 @@
[package]
name = "nu_plugin_auth"
version = "0.109.0"
authors = ["Jesus Perez <jesus@librecloud.online>"]
edition = "2021"
description = "Nushell plugin for provisioning authentication (JWT, MFA)"
repository = "https://github.com/provisioning/nu_plugin_auth"
license = "MIT"
[dependencies]
nu-plugin = "0.109.1"
nu-protocol = "0.109.1"
jsonwebtoken = "=9.3"
serde_json = "1.0"
keyring = "3.6"
rpassword = "7.4"
base64 = "0.22"
qrcode = "0.14"
chrono = "0.4"
[dependencies.reqwest]
version = "0.12"
features = [
"json",
"rustls-tls",
"blocking",
]
default-features = false
[dependencies.serde]
version = "1.0"
features = ["derive"]
[dependencies.tokio]
version = "1.48"
features = ["full"]
[dependencies.totp-rs]
version = "5.7"
features = ["qr"]
[dev-dependencies.nu-plugin-test-support]
version = "0.109.0"
path = "../nushell/crates/nu-plugin-test-support"

View File

@ -21,12 +21,16 @@ This plugin provides native Nushell commands for authenticating with the provisi
Login to provisioning platform with JWT authentication. Login to provisioning platform with JWT authentication.
**Syntax:** **Syntax:**
```nushell ```nushell
nushell
auth login <username> [password] [--url <control-center-url>] [--save] auth login <username> [password] [--url <control-center-url>] [--save]
``` ```
**Examples:** **Examples:**
```nushell ```nushell
nushell
# Login with password prompt (secure) # Login with password prompt (secure)
auth login admin auth login admin
@ -45,12 +49,16 @@ auth login admin --save
Logout from provisioning platform (revoke tokens). Logout from provisioning platform (revoke tokens).
**Syntax:** **Syntax:**
```nushell ```nushell
nushell
auth logout [--all] auth logout [--all]
``` ```
**Examples:** **Examples:**
```nushell ```nushell
nushell
# Logout from current session # Logout from current session
auth logout auth logout
@ -63,12 +71,16 @@ auth logout --all
Verify current authentication token. Verify current authentication token.
**Syntax:** **Syntax:**
```nushell ```nushell
nushell
auth verify [--token <jwt-token>] auth verify [--token <jwt-token>]
``` ```
**Examples:** **Examples:**
```nushell ```nushell
nushell
# Verify stored authentication token # Verify stored authentication token
auth verify auth verify
@ -81,12 +93,16 @@ auth verify --token eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
List active authentication sessions. List active authentication sessions.
**Syntax:** **Syntax:**
```nushell ```nushell
nushell
auth sessions [--active] auth sessions [--active]
``` ```
**Examples:** **Examples:**
```nushell ```nushell
nushell
# List all sessions # List all sessions
auth sessions auth sessions
@ -98,7 +114,8 @@ auth sessions --active
### Build from source ### Build from source
```bash ```nushell
bash
cd provisioning/core/plugins/nushell-plugins/nu_plugin_auth cd provisioning/core/plugins/nushell-plugins/nu_plugin_auth
cargo build --release cargo build --release
``` ```
@ -106,13 +123,15 @@ cargo build --release
### Register with Nushell ### Register with Nushell
```nushell ```nushell
nushell
plugin add target/release/nu_plugin_auth plugin add target/release/nu_plugin_auth
plugin use auth plugin use auth
``` ```
### Using justfile (recommended) ### Using justfile (recommended)
```bash ```nushell
bash
# From nushell-plugins directory # From nushell-plugins directory
just install-plugin nu_plugin_auth just install-plugin nu_plugin_auth
@ -131,6 +150,7 @@ The plugin uses the following defaults:
Override defaults using command flags: Override defaults using command flags:
```nushell ```nushell
nushell
# Use custom control center URL # Use custom control center URL
auth login admin --url https://control.production.example.com auth login admin --url https://control.production.example.com
``` ```
@ -167,6 +187,7 @@ See control center API documentation for details: `provisioning/platform/control
**Version**: 0.1.0 (Initial structure) **Version**: 0.1.0 (Initial structure)
**Implementation Progress**: **Implementation Progress**:
- ✅ Plugin structure created (Agente 1) - ✅ Plugin structure created (Agente 1)
- ⏳ Login command implementation (Agente 2) - ⏳ Login command implementation (Agente 2)
- ⏳ Logout command implementation (Agente 3) - ⏳ Logout command implementation (Agente 3)

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@
## ✅ Completed Components ## ✅ Completed Components
### 1. Login Command (`auth login`) ### 1. Login Command (`auth login`)
- [x] Username/password authentication - [x] Username/password authentication
- [x] Secure password prompt (no echo) - [x] Secure password prompt (no echo)
- [x] HTTP POST to `/auth/login` - [x] HTTP POST to `/auth/login`
@ -20,6 +21,7 @@
- [x] Error handling (HTTP errors, keyring errors) - [x] Error handling (HTTP errors, keyring errors)
### 2. Logout Command (`auth logout`) ### 2. Logout Command (`auth logout`)
- [x] Token retrieval from keyring - [x] Token retrieval from keyring
- [x] HTTP POST to `/auth/logout` - [x] HTTP POST to `/auth/logout`
- [x] Token revocation on server - [x] Token revocation on server
@ -29,6 +31,7 @@
- [x] Error handling (no session, HTTP errors) - [x] Error handling (no session, HTTP errors)
### 3. Helper Functions (`src/helpers.rs`) ### 3. Helper Functions (`src/helpers.rs`)
- [x] `store_tokens_in_keyring()` - Save JWT tokens securely - [x] `store_tokens_in_keyring()` - Save JWT tokens securely
- [x] `get_access_token()` - Retrieve access token - [x] `get_access_token()` - Retrieve access token
- [x] `get_tokens_from_keyring()` - Retrieve both tokens - [x] `get_tokens_from_keyring()` - Retrieve both tokens
@ -40,6 +43,7 @@
- [x] `list_sessions()` - HTTP sessions API (ready for future use) - [x] `list_sessions()` - HTTP sessions API (ready for future use)
### 4. MFA Support (BONUS) ### 4. MFA Support (BONUS)
- [x] `send_mfa_enroll_request()` - TOTP/WebAuthn enrollment - [x] `send_mfa_enroll_request()` - TOTP/WebAuthn enrollment
- [x] `send_mfa_verify_request()` - TOTP code verification - [x] `send_mfa_verify_request()` - TOTP code verification
- [x] `generate_qr_code()` - QR code generation for TOTP - [x] `generate_qr_code()` - QR code generation for TOTP
@ -48,6 +52,7 @@
- [x] `auth mfa verify` command - [x] `auth mfa verify` command
### 5. Security Features ### 5. Security Features
- [x] OS keyring integration (macOS Keychain, Linux libsecret, Windows Credential Manager) - [x] OS keyring integration (macOS Keychain, Linux libsecret, Windows Credential Manager)
- [x] Secure password input (rpassword crate) - [x] Secure password input (rpassword crate)
- [x] HTTPS with rustls-tls - [x] HTTPS with rustls-tls
@ -56,6 +61,7 @@
- [x] Server-side token revocation - [x] Server-side token revocation
### 6. Documentation ### 6. Documentation
- [x] `LOGIN_LOGOUT_IMPLEMENTATION.md` - Complete implementation details - [x] `LOGIN_LOGOUT_IMPLEMENTATION.md` - Complete implementation details
- [x] `QUICK_REFERENCE.md` - Command reference card - [x] `QUICK_REFERENCE.md` - Command reference card
- [x] `IMPLEMENTATION_STATUS.md` - This status file - [x] `IMPLEMENTATION_STATUS.md` - This status file
@ -67,7 +73,9 @@
## 🔧 Build Status ## 🔧 Build Status
### Compilation ### Compilation
```bash
```nushell
bash
$ cargo check $ cargo check
Checking nu_plugin_auth v0.1.0 Checking nu_plugin_auth v0.1.0
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.89s Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.89s
@ -81,6 +89,7 @@ $ cargo build --release
**Warnings**: 6 unused code warnings (for future commands) **Warnings**: 6 unused code warnings (for future commands)
### Dependencies ### Dependencies
- ✅ `reqwest` with `blocking` feature - ✅ `reqwest` with `blocking` feature
- ✅ `keyring = "3.2"` for OS credential storage - ✅ `keyring = "3.2"` for OS credential storage
- ✅ `rpassword = "7.4"` for secure input - ✅ `rpassword = "7.4"` for secure input
@ -93,13 +102,17 @@ $ cargo build --release
## 📝 Test Instructions ## 📝 Test Instructions
### 1. Register Plugin ### 1. Register Plugin
```nushell ```nushell
nushell
plugin add target/release/nu_plugin_auth plugin add target/release/nu_plugin_auth
plugin use nu_plugin_auth plugin use nu_plugin_auth
``` ```
### 2. Test Login ### 2. Test Login
```nushell ```nushell
nushell
# Interactive password prompt # Interactive password prompt
auth login admin auth login admin
@ -111,7 +124,9 @@ auth login admin --url http://control.example.com:8081
``` ```
### 3. Test Logout ### 3. Test Logout
```nushell ```nushell
nushell
# Logout current user # Logout current user
auth logout auth logout
@ -125,7 +140,9 @@ auth logout --all
### 4. Expected Output ### 4. Expected Output
**Login Success:** **Login Success:**
```nushell ```nushell
nushell
{ {
success: true, success: true,
user: { user: {
@ -140,7 +157,9 @@ auth logout --all
``` ```
**Logout Success:** **Logout Success:**
```nushell ```nushell
nushell
{ {
success: true, success: true,
message: "Logged out successfully", message: "Logged out successfully",
@ -153,6 +172,7 @@ auth logout --all
## 🚀 Integration Points ## 🚀 Integration Points
### Control Center API ### Control Center API
- **Base URL**: `http://localhost:8081` (default) - **Base URL**: `http://localhost:8081` (default)
- **Endpoints**: - **Endpoints**:
- `POST /auth/login` - Authentication - `POST /auth/login` - Authentication
@ -163,6 +183,7 @@ auth logout --all
- `POST /mfa/verify` - MFA verification - `POST /mfa/verify` - MFA verification
### Security System ### Security System
- **JWT Auth**: RS256-signed tokens (15min access, 7d refresh) - **JWT Auth**: RS256-signed tokens (15min access, 7d refresh)
- **MFA**: TOTP (RFC 6238) + WebAuthn/FIDO2 - **MFA**: TOTP (RFC 6238) + WebAuthn/FIDO2
- **Audit**: All auth events logged - **Audit**: All auth events logged
@ -173,12 +194,14 @@ auth logout --all
## ⏭️ Future Work (Not Implemented) ## ⏭️ Future Work (Not Implemented)
### Commands to Implement ### Commands to Implement
- [ ] `auth verify` - Verify current token validity - [ ] `auth verify` - Verify current token validity
- [ ] `auth sessions` - List all active sessions - [ ] `auth sessions` - List all active sessions
- [ ] `auth whoami` - Show current user from token - [ ] `auth whoami` - Show current user from token
- [ ] `auth refresh` - Refresh expired access token - [ ] `auth refresh` - Refresh expired access token
### Enhancements ### Enhancements
- [ ] Auto-refresh tokens before expiration - [ ] Auto-refresh tokens before expiration
- [ ] Background token refresh daemon - [ ] Background token refresh daemon
- [ ] Session management (revoke specific session) - [ ] Session management (revoke specific session)
@ -292,6 +315,7 @@ Beyond the basic requirements:
**Status**: ✅ PRODUCTION READY **Status**: ✅ PRODUCTION READY
**Ready for**: **Ready for**:
- Manual testing with Control Center - Manual testing with Control Center
- Integration testing - Integration testing
- User acceptance testing - User acceptance testing

View File

@ -20,27 +20,32 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
**Implemented Functions:** **Implemented Functions:**
#### Token Storage (Keyring Integration) #### Token Storage (Keyring Integration)
- `store_tokens_in_keyring()` - Store JWT tokens in OS keyring - `store_tokens_in_keyring()` - Store JWT tokens in OS keyring
- `get_access_token()` - Retrieve access token from keyring - `get_access_token()` - Retrieve access token from keyring
- `get_tokens_from_keyring()` - Retrieve both tokens from keyring - `get_tokens_from_keyring()` - Retrieve both tokens from keyring
- `remove_tokens_from_keyring()` - Delete tokens from keyring - `remove_tokens_from_keyring()` - Delete tokens from keyring
#### Password Input #### Password Input
- `prompt_password()` - Secure password input (no echo) - `prompt_password()` - Secure password input (no echo)
#### HTTP API Calls #### HTTP API Calls
- `send_login_request()` - POST `/auth/login` with credentials - `send_login_request()` - POST `/auth/login` with credentials
- `send_logout_request()` - POST `/auth/logout` to revoke token - `send_logout_request()` - POST `/auth/logout` to revoke token
- `verify_token()` - GET `/auth/verify` to check token validity - `verify_token()` - GET `/auth/verify` to check token validity
- `list_sessions()` - GET `/auth/sessions` to list active sessions - `list_sessions()` - GET `/auth/sessions` to list active sessions
#### MFA Support (Bonus) #### MFA Support (Bonus)
- `send_mfa_enroll_request()` - POST `/mfa/enroll/{type}` for TOTP/WebAuthn - `send_mfa_enroll_request()` - POST `/mfa/enroll/{type}` for TOTP/WebAuthn
- `send_mfa_verify_request()` - POST `/mfa/verify` to verify TOTP code - `send_mfa_verify_request()` - POST `/mfa/verify` to verify TOTP code
- `generate_qr_code()` - Generate QR code for TOTP secret - `generate_qr_code()` - Generate QR code for TOTP secret
- `display_qr_code()` - Display QR code in terminal with instructions - `display_qr_code()` - Display QR code in terminal with instructions
**Data Structures:** **Data Structures:**
- `LoginRequest` - Login payload - `LoginRequest` - Login payload
- `TokenResponse` - Login response with JWT tokens and user info - `TokenResponse` - Login response with JWT tokens and user info
- `UserInfo` - User details (id, username, email, roles) - `UserInfo` - User details (id, username, email, roles)
@ -59,13 +64,16 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
**Implemented Commands:** **Implemented Commands:**
#### `auth login` Command (Lines 92-149) #### `auth login` Command (Lines 92-149)
**Signature:** **Signature:**
- Required: `username: String` - Required: `username: String`
- Optional: `password: String` (prompts if omitted) - Optional: `password: String` (prompts if omitted)
- Flag: `--url <string>` (default: `http://localhost:8081`) - Flag: `--url <string>` (default: `http://localhost:8081`)
- Switch: `--save` (save tokens to keyring) - Switch: `--save` (save tokens to keyring)
**Behavior:** **Behavior:**
1. Get username from first argument 1. Get username from first argument
2. Get password from second argument OR prompt securely 2. Get password from second argument OR prompt securely
3. Send login request to Control Center 3. Send login request to Control Center
@ -73,7 +81,9 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
5. Return success response with user info and token metadata 5. Return success response with user info and token metadata
**Example Output:** **Example Output:**
```nushell ```nushell
nushell
{ {
success: true, success: true,
user: { user: {
@ -88,12 +98,15 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
``` ```
#### `auth logout` Command (Lines 193-234) #### `auth logout` Command (Lines 193-234)
**Signature:** **Signature:**
- Flag: `--user <string>` (default: current system user) - Flag: `--user <string>` (default: current system user)
- Flag: `--url <string>` (default: `http://localhost:8081`) - Flag: `--url <string>` (default: `http://localhost:8081`)
- Switch: `--all` (logout all sessions) - Switch: `--all` (logout all sessions)
**Behavior:** **Behavior:**
1. Get username from flag or environment variable `$USER` 1. Get username from flag or environment variable `$USER`
2. Retrieve access token from keyring 2. Retrieve access token from keyring
3. Send logout request to Control Center 3. Send logout request to Control Center
@ -101,7 +114,9 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
5. Return success confirmation 5. Return success confirmation
**Example Output:** **Example Output:**
```nushell ```nushell
nushell
{ {
success: true, success: true,
message: "Logged out successfully", message: "Logged out successfully",
@ -114,6 +129,7 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
### 3. `Cargo.toml` ### 3. `Cargo.toml`
**Dependencies Added:** **Dependencies Added:**
- `reqwest` with `blocking` feature enabled for synchronous HTTP - `reqwest` with `blocking` feature enabled for synchronous HTTP
- `keyring = "3.2"` for OS-level credential storage - `keyring = "3.2"` for OS-level credential storage
- `rpassword = "7.4"` for secure password input - `rpassword = "7.4"` for secure password input
@ -149,6 +165,7 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
**Base URL**: Configurable via `--url` flag (default: `http://localhost:8081`) **Base URL**: Configurable via `--url` flag (default: `http://localhost:8081`)
**Endpoints Used:** **Endpoints Used:**
- `POST /auth/login` - Authenticate and receive JWT tokens - `POST /auth/login` - Authenticate and receive JWT tokens
- `POST /auth/logout` - Revoke access token - `POST /auth/logout` - Revoke access token
- `GET /auth/verify` - Verify token validity - `GET /auth/verify` - Verify token validity
@ -157,7 +174,9 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
- `POST /mfa/verify` - Verify MFA code - `POST /mfa/verify` - Verify MFA code
**Request Format:** **Request Format:**
```json
```nushell
json
// Login // Login
{ {
"username": "admin", "username": "admin",
@ -171,7 +190,9 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
``` ```
**Response Format:** **Response Format:**
```json
```nushell
json
// Login success // Login success
{ {
"access_token": "eyJhbGc...", "access_token": "eyJhbGc...",
@ -189,13 +210,16 @@ Complete implementation of Login and Logout commands for the `nu_plugin_auth` Nu
### Error Handling ### Error Handling
**Comprehensive error messages:** **Comprehensive error messages:**
- HTTP request failures with status codes - HTTP request failures with status codes
- Keyring errors (access denied, not found) - Keyring errors (access denied, not found)
- Password input errors - Password input errors
- API response parsing errors - API response parsing errors
**Example Error Flow:** **Example Error Flow:**
```nushell ```nushell
nushell
# No active session # No active session
auth logout auth logout
# Error: No active session: No token found # Error: No active session: No token found
@ -215,7 +239,8 @@ auth login admin --url http://invalid:8081
✅ **Successful Compilation** ✅ **Successful Compilation**
```bash ```nushell
bash
$ cargo check $ cargo check
Checking nu_plugin_auth v0.1.0 Checking nu_plugin_auth v0.1.0
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.89s Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.89s
@ -236,53 +261,70 @@ $ cargo build --release
### Manual Testing ### Manual Testing
#### 1. Register Plugin #### 1. Register Plugin
```nushell ```nushell
nushell
plugin add target/release/nu_plugin_auth plugin add target/release/nu_plugin_auth
plugin use nu_plugin_auth plugin use nu_plugin_auth
``` ```
#### 2. Test Login (Password Prompt) #### 2. Test Login (Password Prompt)
```nushell ```nushell
nushell
auth login admin auth login admin
# Password: ****** # Password: ******
# Returns user info and token metadata # Returns user info and token metadata
``` ```
#### 3. Test Login (Password in Command) #### 3. Test Login (Password in Command)
```nushell ```nushell
nushell
auth login admin mypassword --save auth login admin mypassword --save
# Saves tokens to keyring # Saves tokens to keyring
``` ```
#### 4. Test Login (Custom URL) #### 4. Test Login (Custom URL)
```nushell ```nushell
nushell
auth login admin --url http://control.example.com:8081 auth login admin --url http://control.example.com:8081
``` ```
#### 5. Test Logout #### 5. Test Logout
```nushell ```nushell
nushell
auth logout auth logout
# Logs out current user # Logs out current user
``` ```
#### 6. Test Logout (Specific User) #### 6. Test Logout (Specific User)
```nushell ```nushell
nushell
auth logout --user admin auth logout --user admin
``` ```
#### 7. Test Logout (All Sessions) #### 7. Test Logout (All Sessions)
```nushell ```nushell
nushell
auth logout --all auth logout --all
``` ```
### Integration Testing ### Integration Testing
**Prerequisites:** **Prerequisites:**
- Control Center running at `http://localhost:8081` - Control Center running at `http://localhost:8081`
- Valid user account (username + password) - Valid user account (username + password)
**Test Workflow:** **Test Workflow:**
```nushell ```nushell
nushell
# 1. Login # 1. Login
let login_result = auth login testuser testpass --save let login_result = auth login testuser testpass --save
@ -324,7 +366,9 @@ assert ($logout_result.message == "Logged out successfully")
### MFA Support (Bonus Implementation) ### MFA Support (Bonus Implementation)
**TOTP Enrollment:** **TOTP Enrollment:**
```nushell ```nushell
nushell
# Enroll in TOTP (Google Authenticator, Authy) # Enroll in TOTP (Google Authenticator, Authy)
auth mfa enroll totp --user admin auth mfa enroll totp --user admin
@ -333,13 +377,17 @@ auth mfa enroll totp --user admin
``` ```
**TOTP Verification:** **TOTP Verification:**
```nushell ```nushell
nushell
# Verify 6-digit TOTP code # Verify 6-digit TOTP code
auth mfa verify --code 123456 --user admin auth mfa verify --code 123456 --user admin
``` ```
**WebAuthn Enrollment:** **WebAuthn Enrollment:**
```nushell ```nushell
nushell
# Enroll WebAuthn (YubiKey, Touch ID, Windows Hello) # Enroll WebAuthn (YubiKey, Touch ID, Windows Hello)
auth mfa enroll webauthn --user admin auth mfa enroll webauthn --user admin
``` ```
@ -349,18 +397,22 @@ auth mfa enroll webauthn --user admin
## Future Enhancements (Not Implemented) ## Future Enhancements (Not Implemented)
### Token Refresh ### Token Refresh
- Auto-refresh expired access tokens using refresh token - Auto-refresh expired access tokens using refresh token
- Background refresh before expiration - Background refresh before expiration
### Session Management ### Session Management
- `auth sessions` - List all active sessions - `auth sessions` - List all active sessions
- `auth sessions --revoke <id>` - Revoke specific session - `auth sessions --revoke <id>` - Revoke specific session
### Token Verification ### Token Verification
- `auth verify` - Check if current token is valid - `auth verify` - Check if current token is valid
- `auth whoami` - Show current user info from token - `auth whoami` - Show current user info from token
### Certificate Pinning ### Certificate Pinning
- Pin Control Center TLS certificate - Pin Control Center TLS certificate
- Prevent MITM attacks - Prevent MITM attacks
@ -369,6 +421,7 @@ auth mfa enroll webauthn --user admin
## Dependencies ## Dependencies
### Runtime Dependencies ### Runtime Dependencies
- `keyring = "3.2"` - OS credential storage - `keyring = "3.2"` - OS credential storage
- `rpassword = "7.4"` - Secure password input - `rpassword = "7.4"` - Secure password input
- `reqwest = "0.12"` - HTTP client (blocking mode) - `reqwest = "0.12"` - HTTP client (blocking mode)
@ -377,10 +430,12 @@ auth mfa enroll webauthn --user admin
- `qrcode = "0.14"` - QR code generation - `qrcode = "0.14"` - QR code generation
### Build Dependencies ### Build Dependencies
- Rust 1.70+ (stable) - Rust 1.70+ (stable)
- Nushell 0.107.1 (via path dependency) - Nushell 0.107.1 (via path dependency)
### Platform Requirements ### Platform Requirements
- macOS: Keychain access - macOS: Keychain access
- Linux: libsecret/gnome-keyring - Linux: libsecret/gnome-keyring
- Windows: Credential Manager - Windows: Credential Manager
@ -392,7 +447,9 @@ auth mfa enroll webauthn --user admin
### Command Help ### Command Help
#### Login Command #### Login Command
```nushell ```nushell
nushell
help auth login help auth login
# Usage: # Usage:
@ -417,7 +474,9 @@ help auth login
``` ```
#### Logout Command #### Logout Command
```nushell ```nushell
nushell
help auth logout help auth logout
# Usage: # Usage:

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,7 @@
## Installation ## Installation
```nushell ```nushell
nushell
# Build plugin # Build plugin
cargo build --release -p nu_plugin_auth cargo build --release -p nu_plugin_auth
@ -21,7 +22,9 @@ plugin use nu_plugin_auth
## Login Command ## Login Command
### Basic Usage ### Basic Usage
```nushell ```nushell
nushell
# Interactive login (password prompt) # Interactive login (password prompt)
auth login admin auth login admin
@ -36,13 +39,16 @@ auth login admin --url http://control.example.com:8081
``` ```
### Flags ### Flags
| Flag | Short | Type | Description | Default | | Flag | Short | Type | Description | Default |
|------|-------|------|-------------|---------| |------|-------|------|-------------|---------|
| `--url` | - | String | Control Center URL | `http://localhost:8081` | | `--url` | - | String | Control Center URL | `http://localhost:8081` |
| `--save` | - | Switch | Save tokens to keyring | `false` | | `--save` | - | Switch | Save tokens to keyring | `false` |
### Output ### Output
```nushell ```nushell
nushell
{ {
success: true, success: true,
user: { user: {
@ -61,7 +67,9 @@ auth login admin --url http://control.example.com:8081
## Logout Command ## Logout Command
### Basic Usage ### Basic Usage
```nushell ```nushell
nushell
# Logout current user # Logout current user
auth logout auth logout
@ -73,6 +81,7 @@ auth logout --all
``` ```
### Flags ### Flags
| Flag | Short | Type | Description | Default | | Flag | Short | Type | Description | Default |
|------|-------|------|-------------|---------| |------|-------|------|-------------|---------|
| `--user` | `-u` | String | Username | Current system user | | `--user` | `-u` | String | Username | Current system user |
@ -80,7 +89,9 @@ auth logout --all
| `--all` | `-a` | Switch | Logout all sessions | `false` | | `--all` | `-a` | Switch | Logout all sessions | `false` |
### Output ### Output
```nushell ```nushell
nushell
{ {
success: true, success: true,
message: "Logged out successfully", message: "Logged out successfully",
@ -93,7 +104,9 @@ auth logout --all
## MFA Commands (Bonus) ## MFA Commands (Bonus)
### TOTP Enrollment ### TOTP Enrollment
```nushell ```nushell
nushell
# Enroll in TOTP # Enroll in TOTP
auth mfa enroll totp auth mfa enroll totp
@ -104,7 +117,9 @@ auth mfa enroll totp --user alice
**Output**: QR code in terminal + secret + backup codes **Output**: QR code in terminal + secret + backup codes
### TOTP Verification ### TOTP Verification
```nushell ```nushell
nushell
# Verify TOTP code # Verify TOTP code
auth mfa verify --code 123456 auth mfa verify --code 123456
@ -113,7 +128,9 @@ auth mfa verify --code 123456 --user alice
``` ```
### WebAuthn Enrollment ### WebAuthn Enrollment
```nushell ```nushell
nushell
# Enroll WebAuthn (YubiKey, Touch ID) # Enroll WebAuthn (YubiKey, Touch ID)
auth mfa enroll webauthn auth mfa enroll webauthn
``` ```
@ -133,6 +150,7 @@ auth mfa enroll webauthn
## Error Handling ## Error Handling
```nushell ```nushell
nushell
# No active session # No active session
auth logout auth logout
# Error: No active session: No token found # Error: No active session: No token found
@ -174,7 +192,9 @@ auth login admin --url http://invalid:8081
## Workflow Examples ## Workflow Examples
### Standard Login/Logout ### Standard Login/Logout
```nushell ```nushell
nushell
# Login # Login
auth login admin --save auth login admin --save
@ -185,7 +205,9 @@ auth logout
``` ```
### Multiple Users ### Multiple Users
```nushell ```nushell
nushell
# Login as different users # Login as different users
auth login alice --save auth login alice --save
auth login bob --save auth login bob --save
@ -195,7 +217,9 @@ auth logout --user alice
``` ```
### CI/CD Integration ### CI/CD Integration
```nushell ```nushell
nushell
# Non-interactive login # Non-interactive login
let token = auth login $env.CI_USER $env.CI_PASS | get user.id let token = auth login $env.CI_USER $env.CI_PASS | get user.id
@ -210,18 +234,22 @@ auth logout --user $env.CI_USER
## Troubleshooting ## Troubleshooting
### "No token found" error ### "No token found" error
**Cause**: No active session or keyring not accessible **Cause**: No active session or keyring not accessible
**Fix**: Login again with `--save` flag **Fix**: Login again with `--save` flag
### "HTTP request failed" ### "HTTP request failed"
**Cause**: Control Center not running or wrong URL **Cause**: Control Center not running or wrong URL
**Fix**: Check Control Center status and `--url` flag **Fix**: Check Control Center status and `--url` flag
### "Login failed: HTTP 401" ### "Login failed: HTTP 401"
**Cause**: Invalid credentials **Cause**: Invalid credentials
**Fix**: Verify username and password **Fix**: Verify username and password
### Keyring access denied ### Keyring access denied
**Cause**: OS permission issue **Cause**: OS permission issue
**Fix**: Grant keychain/keyring access to plugin binary **Fix**: Grant keychain/keyring access to plugin binary
@ -230,7 +258,9 @@ auth logout --user $env.CI_USER
## Development ## Development
### Build Commands ### Build Commands
```bash
```nushell
bash
# Check code # Check code
cargo check -p nu_plugin_auth cargo check -p nu_plugin_auth
@ -245,6 +275,7 @@ cargo test -p nu_plugin_auth
``` ```
### Plugin Location ### Plugin Location
- Source: `provisioning/core/plugins/nushell-plugins/nu_plugin_auth/` - Source: `provisioning/core/plugins/nushell-plugins/nu_plugin_auth/`
- Binary: `target/release/nu_plugin_auth` - Binary: `target/release/nu_plugin_auth`

View File

@ -2,7 +2,8 @@
## Installation ## Installation
```bash ```nushell
bash
# Build plugin # Build plugin
cargo build --release cargo build --release
@ -15,7 +16,8 @@ plugin use auth
### TOTP Enrollment ### TOTP Enrollment
```bash ```nushell
bash
# Enroll with QR code # Enroll with QR code
auth mfa enroll totp auth mfa enroll totp
@ -30,7 +32,8 @@ auth mfa enroll totp --url http://control.example.com:8081
### TOTP Verification ### TOTP Verification
```bash ```nushell
bash
# Verify code # Verify code
auth mfa verify --code 123456 auth mfa verify --code 123456
@ -42,7 +45,8 @@ auth mfa verify --code 123456 --user alice
## Complete Workflow ## Complete Workflow
```bash ```nushell
bash
# 1. Login # 1. Login
auth login admin --save auth login admin --save

View File

@ -93,7 +93,9 @@ impl VerificationResult {
pub fn decode_claims_unverified(token: &str) -> Result<Claims, AuthError> { pub fn decode_claims_unverified(token: &str) -> Result<Claims, AuthError> {
let parts: Vec<&str> = token.split('.').collect(); let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 { if parts.len() != 3 {
return Err(AuthError::invalid_token("Token must have 3 parts separated by '.'")); return Err(AuthError::invalid_token(
"Token must have 3 parts separated by '.'",
));
} }
let payload = parts[1]; let payload = parts[1];
@ -118,7 +120,10 @@ pub fn decode_claims_unverified(token: &str) -> Result<Claims, AuthError> {
/// ///
/// Returns a VerificationResult indicating whether the token is valid /// Returns a VerificationResult indicating whether the token is valid
/// and containing the claims if verification succeeded. /// and containing the claims if verification succeeded.
pub fn verify_token_rs256(token: &str, public_key_pem: &str) -> Result<VerificationResult, AuthError> { pub fn verify_token_rs256(
token: &str,
public_key_pem: &str,
) -> Result<VerificationResult, AuthError> {
// Verify the token header uses RS256 // Verify the token header uses RS256
let header = decode_header(token) let header = decode_header(token)
.map_err(|e| AuthError::invalid_token(format!("Failed to decode header: {}", e)))?; .map_err(|e| AuthError::invalid_token(format!("Failed to decode header: {}", e)))?;

View File

@ -206,9 +206,15 @@ mod tests {
#[test] #[test]
fn test_error_kind_display() { fn test_error_kind_display() {
assert_eq!(AuthErrorKind::InvalidCredentials.to_string(), "invalid credentials"); assert_eq!(
AuthErrorKind::InvalidCredentials.to_string(),
"invalid credentials"
);
assert_eq!(AuthErrorKind::TokenExpired.to_string(), "token expired"); assert_eq!(AuthErrorKind::TokenExpired.to_string(), "token expired");
assert_eq!(AuthErrorKind::KeyringError.to_string(), "keyring operation failed"); assert_eq!(
AuthErrorKind::KeyringError.to_string(),
"keyring operation failed"
);
} }
#[test] #[test]

View File

@ -248,7 +248,7 @@ pub fn verify_token(url: &str, token: &str) -> Result<VerifyResponse, AuthError>
pub fn list_sessions( pub fn list_sessions(
url: &str, url: &str,
token: &str, token: &str,
active_only: bool active_only: bool,
) -> Result<Vec<SessionInfo>, AuthError> { ) -> Result<Vec<SessionInfo>, AuthError> {
let client = create_client()?; let client = create_client()?;
@ -365,8 +365,12 @@ pub fn prompt_password(prompt: &str) -> Result<String, AuthError> {
.flush() .flush()
.map_err(|e| AuthError::new(AuthErrorKind::InternalError, format!("Flush error: {}", e)))?; .map_err(|e| AuthError::new(AuthErrorKind::InternalError, format!("Flush error: {}", e)))?;
rpassword::read_password() rpassword::read_password().map_err(|e| {
.map_err(|e| AuthError::new(AuthErrorKind::InternalError, format!("Password read error: {}", e))) AuthError::new(
AuthErrorKind::InternalError,
format!("Password read error: {}", e),
)
})
} }
// ============================================================================= // =============================================================================
@ -386,8 +390,12 @@ pub fn generate_qr_code(uri: &str) -> Result<String, AuthError> {
use qrcode::render::unicode; use qrcode::render::unicode;
use qrcode::QrCode; use qrcode::QrCode;
let code = QrCode::new(uri) let code = QrCode::new(uri).map_err(|e| {
.map_err(|e| AuthError::new(AuthErrorKind::InternalError, format!("QR code generation failed: {}", e)))?; AuthError::new(
AuthErrorKind::InternalError,
format!("QR code generation failed: {}", e),
)
})?;
let qr_string = code let qr_string = code
.render::<unicode::Dense1x2>() .render::<unicode::Dense1x2>()
@ -425,10 +433,12 @@ fn extract_secret(uri: &str) -> Result<String, AuthError> {
.nth(1) .nth(1)
.and_then(|s| s.split('&').next()) .and_then(|s| s.split('&').next())
.map(|s| s.to_string()) .map(|s| s.to_string())
.ok_or_else(|| AuthError::new( .ok_or_else(|| {
AuthError::new(
AuthErrorKind::InternalError, AuthErrorKind::InternalError,
"Failed to extract secret from URI", "Failed to extract secret from URI",
)) )
})
} }
// ============================================================================= // =============================================================================
@ -437,6 +447,21 @@ fn extract_secret(uri: &str) -> Result<String, AuthError> {
use nu_protocol::{record, Span, Value}; use nu_protocol::{record, Span, Value};
/// Converts a UserInfo to a Nushell Value record.
pub fn user_info_to_value(user: &UserInfo, span: Span) -> Value {
Value::record(
record! {
"id" => Value::string(&user.id, span),
"username" => Value::string(&user.username, span),
"email" => Value::string(&user.email, span),
"roles" => Value::list(
user.roles.iter().map(|r| Value::string(r, span)).collect(),
span,
),
},
span,
)
}
/// Converts a SessionInfo to a Nushell Value record. /// Converts a SessionInfo to a Nushell Value record.
pub fn session_info_to_value(session: &SessionInfo, span: Span) -> Value { pub fn session_info_to_value(session: &SessionInfo, span: Span) -> Value {

View File

@ -78,12 +78,12 @@ pub fn get_access_token(username: &str) -> Result<String, AuthError> {
let entry = Entry::new(SERVICE_NAME_ACCESS, username) let entry = Entry::new(SERVICE_NAME_ACCESS, username)
.map_err(|e| AuthError::keyring_error(format!("Failed to access keyring: {}", e)))?; .map_err(|e| AuthError::keyring_error(format!("Failed to access keyring: {}", e)))?;
entry entry.get_password().map_err(|e| {
.get_password() AuthError::new(
.map_err(|e| AuthError::new(
AuthErrorKind::SessionNotFound, AuthErrorKind::SessionNotFound,
format!("No access token found for user '{}': {}", username, e), format!("No access token found for user '{}': {}", username, e),
)) )
})
} }
/// Retrieves the refresh token from the system keyring. /// Retrieves the refresh token from the system keyring.
@ -100,12 +100,12 @@ pub fn get_refresh_token(username: &str) -> Result<String, AuthError> {
let entry = Entry::new(SERVICE_NAME_REFRESH, username) let entry = Entry::new(SERVICE_NAME_REFRESH, username)
.map_err(|e| AuthError::keyring_error(format!("Failed to access keyring: {}", e)))?; .map_err(|e| AuthError::keyring_error(format!("Failed to access keyring: {}", e)))?;
entry entry.get_password().map_err(|e| {
.get_password() AuthError::new(
.map_err(|e| AuthError::new(
AuthErrorKind::SessionNotFound, AuthErrorKind::SessionNotFound,
format!("No refresh token found for user '{}': {}", username, e), format!("No refresh token found for user '{}': {}", username, e),
)) )
})
} }
/// Retrieves both access and refresh tokens from the system keyring. /// Retrieves both access and refresh tokens from the system keyring.
@ -175,9 +175,9 @@ pub fn get_public_key(key_id: &str) -> Result<String, AuthError> {
let entry = Entry::new(SERVICE_NAME_PUBLIC_KEY, key_id) let entry = Entry::new(SERVICE_NAME_PUBLIC_KEY, key_id)
.map_err(|e| AuthError::keyring_error(format!("Failed to access keyring: {}", e)))?; .map_err(|e| AuthError::keyring_error(format!("Failed to access keyring: {}", e)))?;
entry entry.get_password().map_err(|e| {
.get_password() AuthError::configuration_error(format!("Public key '{}' not found: {}", key_id, e))
.map_err(|e| AuthError::configuration_error(format!("Public key '{}' not found: {}", key_id, e))) })
} }
/// Checks if tokens exist for a user. /// Checks if tokens exist for a user.

View File

@ -128,8 +128,7 @@ impl SimplePluginCommand for Login {
let password = if let Some(pwd) = password_arg { let password = if let Some(pwd) = password_arg {
pwd pwd
} else { } else {
helpers::prompt_password("Password: ") helpers::prompt_password("Password: ").map_err(|e| LabeledError::new(e.to_string()))?
.map_err(|e| LabeledError::new(e.to_string()))?
}; };
// Send login request // Send login request
@ -234,16 +233,15 @@ impl SimplePluginCommand for Logout {
let username = username_arg.unwrap_or_else(keyring::get_current_username); let username = username_arg.unwrap_or_else(keyring::get_current_username);
// Get access token // Get access token
let access_token = keyring::get_access_token(&username) let access_token =
.map_err(|e| LabeledError::new(e.to_string()))?; keyring::get_access_token(&username).map_err(|e| LabeledError::new(e.to_string()))?;
// Send logout request // Send logout request
helpers::send_logout_request(&url, &access_token) helpers::send_logout_request(&url, &access_token)
.map_err(|e| LabeledError::new(e.to_string()))?; .map_err(|e| LabeledError::new(e.to_string()))?;
// Remove tokens from keyring // Remove tokens from keyring
keyring::remove_tokens(&username) keyring::remove_tokens(&username).map_err(|e| LabeledError::new(e.to_string()))?;
.map_err(|e| LabeledError::new(e.to_string()))?;
Ok(Value::record( Ok(Value::record(
record! { record! {
@ -292,7 +290,11 @@ impl SimplePluginCommand for Verify {
"Control Center URL for remote verification", "Control Center URL for remote verification",
None, None,
) )
.switch("local", "Verify locally without contacting server", Some('l')) .switch(
"local",
"Verify locally without contacting server",
Some('l'),
)
.category(Category::Custom("provisioning".into())) .category(Category::Custom("provisioning".into()))
} }
@ -337,14 +339,13 @@ impl SimplePluginCommand for Verify {
t t
} else { } else {
let username = username_arg.unwrap_or_else(keyring::get_current_username); let username = username_arg.unwrap_or_else(keyring::get_current_username);
keyring::get_access_token(&username) keyring::get_access_token(&username).map_err(|e| LabeledError::new(e.to_string()))?
.map_err(|e| LabeledError::new(e.to_string()))?
}; };
if local_only { if local_only {
// Local verification (no network) // Local verification (no network)
let result = auth::verify_token_local(&token) let result =
.map_err(|e| LabeledError::new(e.to_string()))?; auth::verify_token_local(&token).map_err(|e| LabeledError::new(e.to_string()))?;
Ok(Value::record( Ok(Value::record(
record! { record! {
@ -448,8 +449,8 @@ impl SimplePluginCommand for Sessions {
// Get username and access token // Get username and access token
let username = username_arg.unwrap_or_else(keyring::get_current_username); let username = username_arg.unwrap_or_else(keyring::get_current_username);
let access_token = keyring::get_access_token(&username) let access_token =
.map_err(|e| LabeledError::new(e.to_string()))?; keyring::get_access_token(&username).map_err(|e| LabeledError::new(e.to_string()))?;
// List sessions from server // List sessions from server
let sessions = helpers::list_sessions(&url, &access_token, active_only) let sessions = helpers::list_sessions(&url, &access_token, active_only)
@ -536,8 +537,8 @@ impl SimplePluginCommand for MfaEnroll {
} }
// Get access token // Get access token
let access_token = keyring::get_access_token(&username) let access_token =
.map_err(|e| LabeledError::new(e.to_string()))?; keyring::get_access_token(&username).map_err(|e| LabeledError::new(e.to_string()))?;
// Send enrollment request // Send enrollment request
let response = helpers::send_mfa_enroll_request(&url, &access_token, &mfa_type) let response = helpers::send_mfa_enroll_request(&url, &access_token, &mfa_type)
@ -622,9 +623,7 @@ impl SimplePluginCommand for MfaVerify {
// Validate code format // Validate code format
if code.len() != 6 || !code.chars().all(|c| c.is_ascii_digit()) { if code.len() != 6 || !code.chars().all(|c| c.is_ascii_digit()) {
return Err(LabeledError::new( return Err(LabeledError::new("Code must be a 6-digit number"));
"Code must be a 6-digit number",
));
} }
let username = call let username = call
@ -635,8 +634,8 @@ impl SimplePluginCommand for MfaVerify {
.unwrap_or_else(|| DEFAULT_CONTROL_CENTER_URL.to_string()); .unwrap_or_else(|| DEFAULT_CONTROL_CENTER_URL.to_string());
// Get access token // Get access token
let access_token = keyring::get_access_token(&username) let access_token =
.map_err(|e| LabeledError::new(e.to_string()))?; keyring::get_access_token(&username).map_err(|e| LabeledError::new(e.to_string()))?;
// Verify code // Verify code
let valid = helpers::send_mfa_verify_request(&url, &access_token, &code) let valid = helpers::send_mfa_verify_request(&url, &access_token, &code)

View File

@ -117,10 +117,19 @@ fn test_auth_error_kind_display() {
"invalid credentials" "invalid credentials"
); );
assert_eq!(AuthErrorKind::TokenExpired.to_string(), "token expired"); assert_eq!(AuthErrorKind::TokenExpired.to_string(), "token expired");
assert_eq!(AuthErrorKind::InvalidToken.to_string(), "invalid token format"); assert_eq!(
assert_eq!(AuthErrorKind::KeyringError.to_string(), "keyring operation failed"); AuthErrorKind::InvalidToken.to_string(),
"invalid token format"
);
assert_eq!(
AuthErrorKind::KeyringError.to_string(),
"keyring operation failed"
);
assert_eq!(AuthErrorKind::NetworkError.to_string(), "network error"); assert_eq!(AuthErrorKind::NetworkError.to_string(), "network error");
assert_eq!(AuthErrorKind::MfaFailed.to_string(), "MFA verification failed"); assert_eq!(
AuthErrorKind::MfaFailed.to_string(),
"MFA verification failed"
);
} }
#[test] #[test]

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -10,14 +10,14 @@ keywords = [
homepage = "https://github.com/FMotalleb/nu_plugin_clipboard" homepage = "https://github.com/FMotalleb/nu_plugin_clipboard"
repository = "https://github.com/FMotalleb/nu_plugin_clipboard" repository = "https://github.com/FMotalleb/nu_plugin_clipboard"
description = "A nushell plugin to copy text into clipboard or get text from it." description = "A nushell plugin to copy text into clipboard or get text from it."
version = "0.109.1" version = "0.111.0"
edition = "2024" edition = "2024"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
nu-json = "0.109.1" nu-json = "0.111.0"
[dependencies.arboard] [dependencies.arboard]
version = "3.6.1" version = "3.6.1"

View File

@ -25,13 +25,15 @@ Try disabling the daemon mode, as mentioned in [#20](https://github.com/FMotalle
### Copying a string (supports only strings for now) ### Copying a string (supports only strings for now)
```bash ```nushell
bash
echo "test value" | clipboard copy echo "test value" | clipboard copy
``` ```
### Using clipboard content ### Using clipboard content
```bash ```nushell
bash
clipboard paste | echo $in clipboard paste | echo $in
``` ```
@ -41,7 +43,8 @@ clipboard paste | echo $in
- When pasting, `clipboard paste` tries to parse JSON into a table or object. - When pasting, `clipboard paste` tries to parse JSON into a table or object.
- If parsing fails, the content is returned as a string. - If parsing fails, the content is returned as a string.
```bash ```nushell
bash
$env | clipboard copy $env | clipboard copy
clipboard paste clipboard paste
@ -55,7 +58,8 @@ clipboard paste
This method automatically handles dependencies and features: This method automatically handles dependencies and features:
```bash ```nushell
bash
git clone https://github.com/FMotalleb/nu_plugin_clipboard.git git clone https://github.com/FMotalleb/nu_plugin_clipboard.git
nupm install --path nu_plugin_clipboard -f nupm install --path nu_plugin_clipboard -f
``` ```
@ -71,7 +75,8 @@ nupm install --path nu_plugin_clipboard -f
### 🛠️ Manual Compilation ### 🛠️ Manual Compilation
```bash ```nushell
bash
git clone https://github.com/FMotalleb/nu_plugin_clipboard.git git clone https://github.com/FMotalleb/nu_plugin_clipboard.git
cd nu_plugin_clipboard cd nu_plugin_clipboard
cargo build -r cargo build -r
@ -80,7 +85,8 @@ plugin add target/release/nu_plugin_clipboard
### 📦 Install via Cargo (using git) ### 📦 Install via Cargo (using git)
```bash ```rust
bash
cargo install --git https://github.com/FMotalleb/nu_plugin_clipboard.git cargo install --git https://github.com/FMotalleb/nu_plugin_clipboard.git
plugin add ~/.cargo/bin/nu_plugin_clipboard plugin add ~/.cargo/bin/nu_plugin_clipboard
``` ```
@ -89,7 +95,8 @@ plugin add ~/.cargo/bin/nu_plugin_clipboard
- Since I live in Iran and crates.io won't let me update my packages like a normal person, most of the time crates.io is outdated. - Since I live in Iran and crates.io won't let me update my packages like a normal person, most of the time crates.io is outdated.
```bash ```nushell
bash
cargo install nu_plugin_clipboard cargo install nu_plugin_clipboard
plugin add ~/.cargo/bin/nu_plugin_clipboard plugin add ~/.cargo/bin/nu_plugin_clipboard
``` ```

View File

@ -1,3 +1,3 @@
pub(crate) fn map_arboard_err_to_label(err: arboard::Error) -> nu_protocol::LabeledError { pub(crate) fn map_arboard_err_to_label(err: arboard::Error) -> nu_protocol::LabeledError {
nu_protocol::LabeledError::new(format!("Clipboard Error: {}", err.to_string())) nu_protocol::LabeledError::new(format!("Clipboard Error: {}", err))
} }

View File

@ -1,6 +1,6 @@
use std::{ use std::{
env, env,
io::{stderr, stdout, Read, Write}, io::{Read, Write, stderr, stdout},
process::{Command, Stdio}, process::{Command, Stdio},
}; };

View File

@ -1,5 +1,5 @@
use crate::clipboard::clipboard::Clipboard;
use crate::ClipboardPlugins; use crate::ClipboardPlugins;
use crate::clipboard::clipboard::Clipboard;
use crate::{clipboard::clipboard::create_clipboard, utils::json}; use crate::{clipboard::clipboard::create_clipboard, utils::json};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{Category, IntoPipelineData, LabeledError, PipelineData, Signature, Type, Value}; use nu_protocol::{Category, IntoPipelineData, LabeledError, PipelineData, Signature, Type, Value};
@ -12,7 +12,7 @@ impl ClipboardCopy {
} }
fn format_json(input: &Value) -> Result<String, LabeledError> { fn format_json(input: &Value) -> Result<String, LabeledError> {
let json_value = let json_value =
json::value_to_json_value(&input).map(|v| nu_json::to_string_with_indent(&v, 4)); json::value_to_json_value(input).map(|v| nu_json::to_string_with_indent(&v, 4));
match json_value { match json_value {
Ok(Ok(text)) => Ok(text.to_owned()), // Return the owned String Ok(Ok(text)) => Ok(text.to_owned()), // Return the owned String
@ -62,9 +62,7 @@ impl PluginCommand for ClipboardCopy {
let value = input.into_value(call.head); let value = input.into_value(call.head);
match value { match value {
Ok(value) => { Ok(value) => {
if let Err(err) = Self::copy(engine, &value) { Self::copy(engine, &value)?;
return Err(err);
}
Ok(value.into_pipeline_data()) Ok(value.into_pipeline_data())
} }
Err(err) => Err(LabeledError::new(err.to_string())), Err(err) => Err(LabeledError::new(err.to_string())),

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
clipboard::clipboard::{create_clipboard, Clipboard},
utils::json::json_to_value,
ClipboardPlugins, ClipboardPlugins,
clipboard::clipboard::{Clipboard, create_clipboard},
utils::json::json_to_value,
}; };
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{Category, IntoPipelineData, LabeledError, PipelineData, Type, Value}; use nu_protocol::{Category, IntoPipelineData, LabeledError, PipelineData, Type, Value};

View File

@ -4,13 +4,13 @@ pub mod utils;
use std::io; use std::io;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use std::{ use std::{
io::{stderr, stdout, Write}, io::{Write, stderr, stdout},
process::exit, process::exit,
}; };
use crate::command::copy::ClipboardCopy; use crate::command::copy::ClipboardCopy;
use crate::command::paste::ClipboardPaste; use crate::command::paste::ClipboardPaste;
use clipboard::clipboard::{create_clipboard, CheckResult, Clipboard}; use clipboard::clipboard::{CheckResult, Clipboard, create_clipboard};
use nu_plugin::PluginCommand; use nu_plugin::PluginCommand;
pub struct ClipboardPlugins; pub struct ClipboardPlugins;
@ -30,10 +30,13 @@ impl nu_plugin::Plugin for ClipboardPlugins {
fn main() -> Result<(), io::Error> { fn main() -> Result<(), io::Error> {
match create_clipboard(None).pre_execute_check() { match create_clipboard(None).pre_execute_check() {
CheckResult::Continue => Ok(nu_plugin::serve_plugin( CheckResult::Continue => {
&mut ClipboardPlugins {}, nu_plugin::serve_plugin(
&ClipboardPlugins {},
nu_plugin::MsgPackSerializer {}, nu_plugin::MsgPackSerializer {},
)), );
Ok(())
},
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
CheckResult::Exit(message, code) => { CheckResult::Exit(message, code) => {
if code != 0 { if code != 0 {

View File

@ -38,12 +38,6 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -53,6 +47,56 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.6" version = "0.7.6"
@ -227,7 +271,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
"rustc-hash", "rustc-hash 1.1.0",
"shlex", "shlex",
"syn", "syn",
] ]
@ -370,16 +414,15 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.39" version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone", "iana-time-zone",
"num-traits", "num-traits",
"pure-rust-locales", "pure-rust-locales",
"serde", "serde",
"windows-targets 0.52.6", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@ -402,6 +445,40 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "clap"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"terminal_size",
]
[[package]]
name = "clap_lex"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "2.5.0" version = "2.5.0"
@ -540,6 +617,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "doctest-file" name = "doctest-file"
version = "1.0.0" version = "1.0.0"
@ -637,9 +725,9 @@ dependencies = [
[[package]] [[package]]
name = "fancy-regex" name = "fancy-regex"
version = "0.16.2" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f" checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"regex-automata", "regex-automata",
@ -663,10 +751,55 @@ dependencies = [
] ]
[[package]] [[package]]
name = "foldhash" name = "fluent"
version = "0.1.4" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477"
dependencies = [
"fluent-bundle",
"unic-langid",
]
[[package]]
name = "fluent-bundle"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash 2.1.1",
"self_cell",
"smallvec",
"unic-langid",
]
[[package]]
name = "fluent-langneg"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0"
dependencies = [
"unic-langid",
]
[[package]]
name = "fluent-syntax"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [
"memchr",
"thiserror 2.0.18",
]
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]] [[package]]
name = "futures-core" name = "futures-core"
@ -724,9 +857,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@ -776,9 +909,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.11.4" version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -786,9 +919,9 @@ dependencies = [
[[package]] [[package]]
name = "interprocess" name = "interprocess"
version = "2.2.2" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69"
dependencies = [ dependencies = [
"doctest-file", "doctest-file",
"libc", "libc",
@ -797,6 +930,25 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "intl-memoizer"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
dependencies = [
"type-map",
"unic-langid",
]
[[package]]
name = "intl_pluralrules"
version = "7.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
dependencies = [
"unic-langid",
]
[[package]] [[package]]
name = "inventory" name = "inventory"
version = "0.3.19" version = "0.3.19"
@ -812,6 +964,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@ -860,9 +1018,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.175" version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -931,9 +1089,9 @@ checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.12.5" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
dependencies = [ dependencies = [
"hashbrown", "hashbrown",
] ]
@ -963,12 +1121,9 @@ dependencies = [
[[package]] [[package]]
name = "mach2" name = "mach2"
version = "0.4.2" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "malloc_buf" name = "malloc_buf"
@ -981,9 +1136,9 @@ dependencies = [
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]] [[package]]
name = "memoffset" name = "memoffset"
@ -1008,7 +1163,7 @@ dependencies = [
"supports-unicode", "supports-unicode",
"terminal_size", "terminal_size",
"textwrap", "textwrap",
"unicode-width", "unicode-width 0.1.14",
] ]
[[package]] [[package]]
@ -1118,9 +1273,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-derive-value" name = "nu-derive-value"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465d2d3ada6004cb6689f269a08c70ba81056231e2b5392d1e0ccf5825f81cb" checksum = "d71958b54c367bda033f7dcc4a73b61972fb52323f71a1e3533e290fa67148d1"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error2", "proc-macro-error2",
@ -1131,9 +1286,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3b777faf7c5180fe5d7f67d83c44fd14138d91f2938a36494ed6ac66b7160f3" checksum = "d41b3e3e2d25c30741a0761856258e22624c0d60064e4f0e12f86202a451d492"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"log", "log",
@ -1146,25 +1301,25 @@ dependencies = [
[[package]] [[package]]
name = "nu-experimental" name = "nu-experimental"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73dd212a1afdad646a38c00579a0988264880aeb97fee820b349a28cdcc04df2" checksum = "f328fa0531bdf49c2dc0312b40cb780e3d74e0d3dbb15d508469a5ae4cfd8d8f"
dependencies = [ dependencies = [
"itertools 0.14.0", "itertools 0.14.0",
"thiserror 2.0.12", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "nu-glob" name = "nu-glob"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15aa2c17078926f14e393b4b708e69f228cb6fd4c81136839bde82772bdde1b5" checksum = "01ee787f61353c9c90581ddf4c0602a07b991cdd06c97dac8b6d323a1a52c43a"
[[package]] [[package]]
name = "nu-path" name = "nu-path"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde9d8ba26f62c07176c0237a36f38ce964ab3a0dcfb6aab1feea7515d1c6594" checksum = "c01d110cb931acf56237ce572e5b156e8e1134227c90deeffb92eedda9482c23"
dependencies = [ dependencies = [
"dirs", "dirs",
"omnipath", "omnipath",
@ -1174,9 +1329,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea1fbfd41b2f5c967675fc948831e03be67d91c6b8e18a60f3445113fe6548c" checksum = "c322531b1a7d6338c5ead1f454294f46babf8c99cd4716311cab1e88ba52b154"
dependencies = [ dependencies = [
"log", "log",
"nix 0.30.1", "nix 0.30.1",
@ -1185,14 +1340,14 @@ dependencies = [
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
"thiserror 2.0.12", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2410648c2c38cf9359595ffcf281d9d60a81c0580ff07f7c7d42bed414f3a1" checksum = "38ee792aeb0d37e0ed55ca4304e434eece497914e27ae42616a8bb973f5d2720"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"log", "log",
@ -1206,9 +1361,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27de26da922261dff8103a811879228c55749a1b7b0e573b639c609a0651a01e" checksum = "7725f341428db16dbef4392970de32705abc77ee80a902572c8da811dade3564"
dependencies = [ dependencies = [
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
@ -1220,9 +1375,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038943300ca9de0924fef1c795a7dd16ffc67105629477cf163e8ee6bad95ea6" checksum = "f1c0e58cbeb46cbfd40156e6f4b9f90e4a77e774ca863fa158867a4726aab1d1"
dependencies = [ dependencies = [
"brotli", "brotli",
"bytes", "bytes",
@ -1251,7 +1406,7 @@ dependencies = [
"serde_json", "serde_json",
"strum", "strum",
"strum_macros", "strum_macros",
"thiserror 2.0.12", "thiserror 2.0.18",
"typetag", "typetag",
"web-time", "web-time",
"windows 0.62.2", "windows 0.62.2",
@ -1260,9 +1415,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-system" name = "nu-system"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46be734cc9b19e09a9665769e14360e13e6978490056ba5c8bfad7dd0537ea83" checksum = "62fe7847b65edbe362a0fcb67dedfab9fd7370e89c0313f7cb7d0a7ab8f9834b"
dependencies = [ dependencies = [
"chrono", "chrono",
"itertools 0.14.0", "itertools 0.14.0",
@ -1274,15 +1429,16 @@ dependencies = [
"ntapi", "ntapi",
"procfs", "procfs",
"sysinfo", "sysinfo",
"uucore",
"web-time", "web-time",
"windows 0.62.2", "windows 0.62.2",
] ]
[[package]] [[package]]
name = "nu-utils" name = "nu-utils"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f8eb43c29cc5bce85f87defdadc2cca964fa434d808af37036a7cb78f3c68e9" checksum = "df85a8a4bb28c84d5f7c096c02c859ac454dfac59fd0296ab5eb6ed86619219e"
dependencies = [ dependencies = [
"byteyarn", "byteyarn",
"crossterm", "crossterm",
@ -1303,7 +1459,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_desktop_notifications" name = "nu_plugin_desktop_notifications"
version = "0.109.1" version = "0.111.0"
dependencies = [ dependencies = [
"notify-rust", "notify-rust",
"nu-plugin", "nu-plugin",
@ -1357,18 +1513,18 @@ dependencies = [
[[package]] [[package]]
name = "objc2-core-foundation" name = "objc2-core-foundation"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "objc2-io-kit" name = "objc2-io-kit"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
dependencies = [ dependencies = [
"libc", "libc",
"objc2-core-foundation", "objc2-core-foundation",
@ -1395,6 +1551,12 @@ version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
@ -1411,6 +1573,15 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "os_display"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5fd71b79026fb918650dde6d125000a233764f1c2f1659a1c71118e33ea08f"
dependencies = [
"unicode-width 0.2.2",
]
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.2.1" version = "1.2.1"
@ -1542,23 +1713,22 @@ dependencies = [
[[package]] [[package]]
name = "procfs" name = "procfs"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
"flate2", "flate2",
"hex",
"procfs-core", "procfs-core",
"rustix 0.38.44", "rustix 1.1.2",
] ]
[[package]] [[package]]
name = "procfs-core" name = "procfs-core"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
@ -1567,9 +1737,9 @@ dependencies = [
[[package]] [[package]]
name = "pure-rust-locales" name = "pure-rust-locales"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d"
[[package]] [[package]]
name = "pwd" name = "pwd"
@ -1633,23 +1803,23 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [ dependencies = [
"getrandom 0.2.15", "getrandom 0.2.15",
"libredox", "libredox",
"thiserror 2.0.12", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "ref-cast" name = "ref-cast"
version = "1.0.23" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
dependencies = [ dependencies = [
"ref-cast-impl", "ref-cast-impl",
] ]
[[package]] [[package]]
name = "ref-cast-impl" name = "ref-cast-impl"
version = "1.0.23" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1698,11 +1868,10 @@ dependencies = [
[[package]] [[package]]
name = "rmp-serde" name = "rmp-serde"
version = "1.3.0" version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
dependencies = [ dependencies = [
"byteorder",
"rmp", "rmp",
"serde", "serde",
] ]
@ -1713,6 +1882,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.44" version = "0.38.44"
@ -1757,6 +1932,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "self_cell"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.25" version = "1.0.25"
@ -1765,18 +1946,28 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.217" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.217" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1785,14 +1976,15 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.138" version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu",
"serde", "serde",
"serde_core",
"zmij",
] ]
[[package]] [[package]]
@ -1873,10 +2065,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "strum" name = "strsim"
version = "0.26.3" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
@ -1933,16 +2131,16 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.37.2" version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
"ntapi", "ntapi",
"objc2-core-foundation", "objc2-core-foundation",
"objc2-io-kit", "objc2-io-kit",
"windows 0.61.1", "windows 0.62.2",
] ]
[[package]] [[package]]
@ -1952,7 +2150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9"
dependencies = [ dependencies = [
"quick-xml", "quick-xml",
"thiserror 2.0.12", "thiserror 2.0.18",
"windows 0.61.1", "windows 0.61.1",
"windows-version", "windows-version",
] ]
@ -1988,7 +2186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
dependencies = [ dependencies = [
"unicode-linebreak", "unicode-linebreak",
"unicode-width", "unicode-width 0.1.14",
] ]
[[package]] [[package]]
@ -2002,11 +2200,11 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.12" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [ dependencies = [
"thiserror-impl 2.0.12", "thiserror-impl 2.0.18",
] ]
[[package]] [[package]]
@ -2022,9 +2220,9 @@ dependencies = [
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.12" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2050,6 +2248,17 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "tinystr"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"serde_core",
"zerovec",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.8" version = "0.6.8"
@ -2098,6 +2307,15 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash 2.1.1",
]
[[package]] [[package]]
name = "typeid" name = "typeid"
version = "1.0.2" version = "1.0.2"
@ -2140,10 +2358,28 @@ dependencies = [
] ]
[[package]] [[package]]
name = "unicase" name = "unic-langid"
version = "2.8.1" version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
dependencies = [
"tinystr",
]
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -2169,6 +2405,47 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uucore"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b157ba598d7f7ed06f6dbc62999edb9d730b4d3fb58e503d8ad6d5fbe1e04391"
dependencies = [
"clap",
"fluent",
"fluent-bundle",
"fluent-syntax",
"libc",
"nix 0.30.1",
"os_display",
"thiserror 2.0.18",
"unic-langid",
"uucore_procs",
"wild",
]
[[package]]
name = "uucore_procs"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa291a52608ac5a2f8539e119666e021baa6b8c01f22f02ed201bbae54cbbc0"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "vte" name = "vte"
version = "0.14.1" version = "0.14.1"
@ -2267,6 +2544,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]]
name = "wild"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3131afc8c575281e1e80f36ed6a092aa502c08b18ed7524e86fbbb12bb410e1"
dependencies = [
"glob",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -2763,6 +3049,28 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
[[package]]
name = "zerovec"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"serde",
"zerofrom",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]] [[package]]
name = "zvariant" name = "zvariant"
version = "5.4.0" version = "5.4.0"

View File

@ -1,6 +1,6 @@
[dependencies] [dependencies]
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
[dependencies.notify-rust] [dependencies.notify-rust]
version = "4.11.7" version = "4.11.7"
@ -20,4 +20,4 @@ license = "MIT"
name = "nu_plugin_desktop_notifications" name = "nu_plugin_desktop_notifications"
readme = "README.md" readme = "README.md"
repository = "https://github.com/FMotalleb/nu_plugin_desktop_notifications" repository = "https://github.com/FMotalleb/nu_plugin_desktop_notifications"
version = "0.109.1" version = "0.111.0"

View File

@ -17,7 +17,8 @@ A [Nushell](https://www.nushell.sh/) plugin for sending desktop notifications us
### **Sending a Notification** ### **Sending a Notification**
```bash ```nushell
bash
notify -t "Test notification body" --summary "Test title" notify -t "Test notification body" --summary "Test title"
``` ```
@ -40,7 +41,8 @@ Send a notification after a task completes, displaying the elapsed time:
![image](https://github.com/FMotalleb/nu_plugin_desktop_notifications/assets/30149519/a4fbc2a9-6537-4d18-8d98-e55ebcd6b0bd) ![image](https://github.com/FMotalleb/nu_plugin_desktop_notifications/assets/30149519/a4fbc2a9-6537-4d18-8d98-e55ebcd6b0bd)
```bash ```nushell
bash
def "notify on done" [ def "notify on done" [
task: closure task: closure
] { ] {
@ -62,14 +64,16 @@ notify on done { port scan 8.8.8.8 53 }
### 🚀 Recommended: Using [nupm](https://github.com/nushell/nupm) ### 🚀 Recommended: Using [nupm](https://github.com/nushell/nupm)
```bash ```nushell
bash
git clone https://github.com/FMotalleb/nu_plugin_desktop_notifications.git git clone https://github.com/FMotalleb/nu_plugin_desktop_notifications.git
nupm install --path nu_plugin_desktop_notifications -f nupm install --path nu_plugin_desktop_notifications -f
``` ```
### 🛠️ Manual Compilation ### 🛠️ Manual Compilation
```bash ```nushell
bash
git clone https://github.com/FMotalleb/nu_plugin_desktop_notifications.git git clone https://github.com/FMotalleb/nu_plugin_desktop_notifications.git
cd nu_plugin_desktop_notifications cd nu_plugin_desktop_notifications
cargo build -r cargo build -r
@ -78,7 +82,8 @@ register target/release/nu_plugin_desktop_notifications
### 📦 Install via Cargo (using git) ### 📦 Install via Cargo (using git)
```bash ```rust
bash
cargo install --git https://github.com/FMotalleb/nu_plugin_desktop_notifications.git cargo install --git https://github.com/FMotalleb/nu_plugin_desktop_notifications.git
register ~/.cargo/bin/nu_plugin_desktop_notifications register ~/.cargo/bin/nu_plugin_desktop_notifications
``` ```
@ -87,7 +92,8 @@ register ~/.cargo/bin/nu_plugin_desktop_notifications
> >
> _Since I live in Iran and crates.io often restricts package updates, the version there might be outdated._ > _Since I live in Iran and crates.io often restricts package updates, the version there might be outdated._
```bash ```nushell
bash
cargo install nu_plugin_desktop_notifications cargo install nu_plugin_desktop_notifications
register ~/.cargo/bin/nu_plugin_desktop_notifications register ~/.cargo/bin/nu_plugin_desktop_notifications
``` ```

View File

@ -1,4 +1,4 @@
use nu_plugin::{serve_plugin, Plugin}; use nu_plugin::{Plugin, serve_plugin};
use crate::notify::NotifyCommand; use crate::notify::NotifyCommand;
mod notify; mod notify;
@ -15,5 +15,5 @@ impl Plugin for NotifyPlugin {
} }
fn main() { fn main() {
serve_plugin(&mut NotifyPlugin {}, nu_plugin::MsgPackSerializer {}) serve_plugin(&NotifyPlugin {}, nu_plugin::MsgPackSerializer {})
} }

View File

@ -107,15 +107,12 @@ impl SimplePluginCommand for NotifyCommand {
} }
if let Some(duration_value) = call.get_flag_value("timeout") { if let Some(duration_value) = call.get_flag_value("timeout") {
match duration_value.as_duration() { if let Ok(timeout) = duration_value.as_duration() {
Ok(timeout) => {
if let Ok(nanos) = timeout.try_into() { if let Ok(nanos) = timeout.try_into() {
let duration = Timeout::from(Duration::from_nanos(nanos)); let duration = Timeout::from(Duration::from_nanos(nanos));
notification.timeout(duration); notification.timeout(duration);
} }
} }
Err(_) => {}
}
} }
match notification.show() { match notification.show() {

View File

@ -47,6 +47,56 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.0",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.6" version = "0.7.6"
@ -197,9 +247,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.42" version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"num-traits", "num-traits",
@ -228,6 +278,40 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "clap"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"terminal_size",
]
[[package]]
name = "clap_lex"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.7.1" version = "0.7.1"
@ -264,7 +348,7 @@ dependencies = [
"document-features", "document-features",
"mio", "mio",
"parking_lot", "parking_lot",
"rustix 1.1.2", "rustix",
"signal-hook", "signal-hook",
"signal-hook-mio", "signal-hook-mio",
"winapi", "winapi",
@ -382,9 +466,9 @@ dependencies = [
[[package]] [[package]]
name = "fancy-regex" name = "fancy-regex"
version = "0.16.2" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f" checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"regex-automata", "regex-automata",
@ -455,14 +539,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [ dependencies = [
"memchr", "memchr",
"thiserror 2.0.16", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
@ -495,21 +579,15 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.5" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
"foldhash", "foldhash",
] ]
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -534,7 +612,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"wasm-bindgen", "wasm-bindgen",
"windows-core 0.62.2", "windows-core",
] ]
[[package]] [[package]]
@ -548,19 +626,19 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.12.0" version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.16.0", "hashbrown",
] ]
[[package]] [[package]]
name = "interprocess" name = "interprocess"
version = "2.2.3" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69"
dependencies = [ dependencies = [
"doctest-file", "doctest-file",
"libc", "libc",
@ -603,6 +681,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@ -651,9 +735,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.175" version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -686,12 +770,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.11.0" version = "0.11.0"
@ -722,11 +800,11 @@ checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.12.5" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
dependencies = [ dependencies = [
"hashbrown 0.15.5", "hashbrown",
] ]
[[package]] [[package]]
@ -741,18 +819,15 @@ dependencies = [
[[package]] [[package]]
name = "mach2" name = "mach2"
version = "0.4.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.5" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]] [[package]]
name = "miette" name = "miette"
@ -851,9 +926,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-derive-value" name = "nu-derive-value"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465d2d3ada6004cb6689f269a08c70ba81056231e2b5392d1e0ccf5825f81cb" checksum = "d71958b54c367bda033f7dcc4a73b61972fb52323f71a1e3533e290fa67148d1"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error2", "proc-macro-error2",
@ -864,9 +939,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3b777faf7c5180fe5d7f67d83c44fd14138d91f2938a36494ed6ac66b7160f3" checksum = "d41b3e3e2d25c30741a0761856258e22624c0d60064e4f0e12f86202a451d492"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"log", "log",
@ -879,25 +954,25 @@ dependencies = [
[[package]] [[package]]
name = "nu-experimental" name = "nu-experimental"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73dd212a1afdad646a38c00579a0988264880aeb97fee820b349a28cdcc04df2" checksum = "f328fa0531bdf49c2dc0312b40cb780e3d74e0d3dbb15d508469a5ae4cfd8d8f"
dependencies = [ dependencies = [
"itertools 0.14.0", "itertools 0.14.0",
"thiserror 2.0.16", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "nu-glob" name = "nu-glob"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15aa2c17078926f14e393b4b708e69f228cb6fd4c81136839bde82772bdde1b5" checksum = "01ee787f61353c9c90581ddf4c0602a07b991cdd06c97dac8b6d323a1a52c43a"
[[package]] [[package]]
name = "nu-path" name = "nu-path"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde9d8ba26f62c07176c0237a36f38ce964ab3a0dcfb6aab1feea7515d1c6594" checksum = "c01d110cb931acf56237ce572e5b156e8e1134227c90deeffb92eedda9482c23"
dependencies = [ dependencies = [
"dirs", "dirs",
"omnipath", "omnipath",
@ -907,9 +982,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea1fbfd41b2f5c967675fc948831e03be67d91c6b8e18a60f3445113fe6548c" checksum = "c322531b1a7d6338c5ead1f454294f46babf8c99cd4716311cab1e88ba52b154"
dependencies = [ dependencies = [
"log", "log",
"nix", "nix",
@ -918,14 +993,14 @@ dependencies = [
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
"thiserror 2.0.16", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2410648c2c38cf9359595ffcf281d9d60a81c0580ff07f7c7d42bed414f3a1" checksum = "38ee792aeb0d37e0ed55ca4304e434eece497914e27ae42616a8bb973f5d2720"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"log", "log",
@ -934,14 +1009,14 @@ dependencies = [
"rmp-serde", "rmp-serde",
"serde", "serde",
"serde_json", "serde_json",
"windows 0.62.2", "windows",
] ]
[[package]] [[package]]
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27de26da922261dff8103a811879228c55749a1b7b0e573b639c609a0651a01e" checksum = "7725f341428db16dbef4392970de32705abc77ee80a902572c8da811dade3564"
dependencies = [ dependencies = [
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
@ -953,9 +1028,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038943300ca9de0924fef1c795a7dd16ffc67105629477cf163e8ee6bad95ea6" checksum = "f1c0e58cbeb46cbfd40156e6f4b9f90e4a77e774ca863fa158867a4726aab1d1"
dependencies = [ dependencies = [
"brotli", "brotli",
"bytes", "bytes",
@ -984,18 +1059,18 @@ dependencies = [
"serde_json", "serde_json",
"strum", "strum",
"strum_macros", "strum_macros",
"thiserror 2.0.16", "thiserror 2.0.18",
"typetag", "typetag",
"web-time", "web-time",
"windows 0.62.2", "windows",
"windows-sys 0.61.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
name = "nu-system" name = "nu-system"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46be734cc9b19e09a9665769e14360e13e6978490056ba5c8bfad7dd0537ea83" checksum = "62fe7847b65edbe362a0fcb67dedfab9fd7370e89c0313f7cb7d0a7ab8f9834b"
dependencies = [ dependencies = [
"chrono", "chrono",
"itertools 0.14.0", "itertools 0.14.0",
@ -1007,15 +1082,16 @@ dependencies = [
"ntapi", "ntapi",
"procfs", "procfs",
"sysinfo", "sysinfo",
"uucore",
"web-time", "web-time",
"windows 0.62.2", "windows",
] ]
[[package]] [[package]]
name = "nu-utils" name = "nu-utils"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f8eb43c29cc5bce85f87defdadc2cca964fa434d808af37036a7cb78f3c68e9" checksum = "df85a8a4bb28c84d5f7c096c02c859ac454dfac59fd0296ab5eb6ed86619219e"
dependencies = [ dependencies = [
"byteyarn", "byteyarn",
"crossterm", "crossterm",
@ -1036,7 +1112,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_fluent" name = "nu_plugin_fluent"
version = "0.109.1" version = "0.111.0"
dependencies = [ dependencies = [
"fluent", "fluent",
"fluent-bundle", "fluent-bundle",
@ -1047,7 +1123,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tempfile", "tempfile",
"thiserror 2.0.16", "thiserror 2.0.18",
"unic-langid", "unic-langid",
] ]
@ -1072,18 +1148,18 @@ dependencies = [
[[package]] [[package]]
name = "objc2-core-foundation" name = "objc2-core-foundation"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "objc2-io-kit" name = "objc2-io-kit"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
dependencies = [ dependencies = [
"libc", "libc",
"objc2-core-foundation", "objc2-core-foundation",
@ -1101,12 +1177,27 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_display"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5fd71b79026fb918650dde6d125000a233764f1c2f1659a1c71118e33ea08f"
dependencies = [
"unicode-width 0.2.1",
]
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.2.2" version = "1.2.2"
@ -1185,23 +1276,22 @@ dependencies = [
[[package]] [[package]]
name = "procfs" name = "procfs"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
"flate2", "flate2",
"hex",
"procfs-core", "procfs-core",
"rustix 0.38.44", "rustix",
] ]
[[package]] [[package]]
name = "procfs-core" name = "procfs-core"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
@ -1210,9 +1300,9 @@ dependencies = [
[[package]] [[package]]
name = "pure-rust-locales" name = "pure-rust-locales"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d"
[[package]] [[package]]
name = "pwd" name = "pwd"
@ -1262,23 +1352,23 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [ dependencies = [
"getrandom 0.2.16", "getrandom 0.2.16",
"libredox", "libredox",
"thiserror 2.0.16", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "ref-cast" name = "ref-cast"
version = "1.0.24" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
dependencies = [ dependencies = [
"ref-cast-impl", "ref-cast-impl",
] ]
[[package]] [[package]]
name = "ref-cast-impl" name = "ref-cast-impl"
version = "1.0.24" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1327,11 +1417,10 @@ dependencies = [
[[package]] [[package]]
name = "rmp-serde" name = "rmp-serde"
version = "1.3.0" version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
dependencies = [ dependencies = [
"byteorder",
"rmp", "rmp",
"serde", "serde",
] ]
@ -1348,19 +1437,6 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.1.2" version = "1.1.2"
@ -1370,7 +1446,7 @@ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.11.0", "linux-raw-sys",
"windows-sys 0.61.0", "windows-sys 0.61.0",
] ]
@ -1406,9 +1482,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.225" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive", "serde_derive",
@ -1416,18 +1492,18 @@ dependencies = [
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.225" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.225" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1436,15 +1512,15 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.145" version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu",
"serde", "serde",
"serde_core", "serde_core",
"zmij",
] ]
[[package]] [[package]]
@ -1499,10 +1575,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "strum" name = "strsim"
version = "0.26.3" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
@ -1559,16 +1641,16 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.37.2" version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
"ntapi", "ntapi",
"objc2-core-foundation", "objc2-core-foundation",
"objc2-io-kit", "objc2-io-kit",
"windows 0.61.3", "windows",
] ]
[[package]] [[package]]
@ -1580,7 +1662,7 @@ dependencies = [
"fastrand", "fastrand",
"getrandom 0.3.3", "getrandom 0.3.3",
"once_cell", "once_cell",
"rustix 1.1.2", "rustix",
"windows-sys 0.61.0", "windows-sys 0.61.0",
] ]
@ -1590,7 +1672,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
dependencies = [ dependencies = [
"rustix 1.1.2", "rustix",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
@ -1615,11 +1697,11 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.16" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [ dependencies = [
"thiserror-impl 2.0.16", "thiserror-impl 2.0.18",
] ]
[[package]] [[package]]
@ -1635,9 +1717,9 @@ dependencies = [
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.16" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1713,9 +1795,9 @@ dependencies = [
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.8.1" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -1747,6 +1829,41 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uucore"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b157ba598d7f7ed06f6dbc62999edb9d730b4d3fb58e503d8ad6d5fbe1e04391"
dependencies = [
"clap",
"fluent",
"fluent-bundle",
"fluent-syntax",
"libc",
"nix",
"os_display",
"thiserror 2.0.18",
"unic-langid",
"uucore_procs",
"wild",
]
[[package]]
name = "uucore_procs"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa291a52608ac5a2f8539e119666e021baa6b8c01f22f02ed201bbae54cbbc0"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "vte" name = "vte"
version = "0.14.1" version = "0.14.1"
@ -1855,6 +1972,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
[[package]]
name = "wild"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3131afc8c575281e1e80f36ed6a092aa502c08b18ed7524e86fbbb12bb410e1"
dependencies = [
"glob",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1877,38 +2003,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections 0.2.0",
"windows-core 0.61.2",
"windows-future 0.2.1",
"windows-link 0.1.3",
"windows-numerics 0.2.0",
]
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.62.2" version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [ dependencies = [
"windows-collections 0.3.2", "windows-collections",
"windows-core 0.62.2", "windows-core",
"windows-future 0.3.2", "windows-future",
"windows-numerics 0.3.1", "windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.2",
] ]
[[package]] [[package]]
@ -1917,20 +2021,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [ dependencies = [
"windows-core 0.62.2", "windows-core",
]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link 0.1.3",
"windows-result 0.3.4",
"windows-strings 0.4.2",
] ]
[[package]] [[package]]
@ -1942,19 +2033,8 @@ dependencies = [
"windows-implement", "windows-implement",
"windows-interface", "windows-interface",
"windows-link 0.2.1", "windows-link 0.2.1",
"windows-result 0.4.1", "windows-result",
"windows-strings 0.5.1", "windows-strings",
]
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
"windows-threading 0.1.0",
] ]
[[package]] [[package]]
@ -1963,9 +2043,9 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [ dependencies = [
"windows-core 0.62.2", "windows-core",
"windows-link 0.2.1", "windows-link 0.2.1",
"windows-threading 0.2.1", "windows-threading",
] ]
[[package]] [[package]]
@ -2002,35 +2082,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-numerics" name = "windows-numerics"
version = "0.3.1" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [ dependencies = [
"windows-core 0.62.2", "windows-core",
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.4.1" version = "0.4.1"
@ -2040,15 +2101,6 @@ dependencies = [
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-strings" name = "windows-strings"
version = "0.5.1" version = "0.5.1"
@ -2127,15 +2179,6 @@ dependencies = [
"windows_x86_64_msvc 0.53.0", "windows_x86_64_msvc 0.53.0",
] ]
[[package]]
name = "windows-threading"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-threading" name = "windows-threading"
version = "0.2.1" version = "0.2.1"
@ -2282,3 +2325,9 @@ checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
dependencies = [ dependencies = [
"zerofrom", "zerofrom",
] ]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu_plugin_fluent" name = "nu_plugin_fluent"
version = "0.109.1" version = "0.111.0"
edition = "2021" edition = "2021"
description = "Nushell plugin for Fluent i18n integration" description = "Nushell plugin for Fluent i18n integration"
authors = ["Jesús Pérex <jpl@jesusperez.com>"] authors = ["Jesús Pérex <jpl@jesusperez.com>"]
@ -19,8 +19,8 @@ categories = [
] ]
[dependencies] [dependencies]
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
serde_json = "1.0" serde_json = "1.0"
fluent = "0.17" fluent = "0.17"
fluent-bundle = "0.16" fluent-bundle = "0.16"

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,8 @@
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{
Category, LabeledError, Signature, Span, SyntaxShape, Type, Value, record,
};
use fluent::{FluentBundle, FluentResource};
use unic_langid::LanguageIdentifier;
use crate::FluentPlugin; use crate::FluentPlugin;
use fluent::{FluentBundle, FluentResource};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{record, Category, LabeledError, Signature, Span, SyntaxShape, Type, Value};
use unic_langid::LanguageIdentifier;
pub struct CreateBundle; pub struct CreateBundle;
@ -37,7 +35,11 @@ impl SimplePluginCommand for CreateBundle {
"Fallback locales in order", "Fallback locales in order",
Some('f'), Some('f'),
) )
.switch("override", "Allow page files to override global messages", Some('o')) .switch(
"override",
"Allow page files to override global messages",
Some('o'),
)
.category(Category::Strings) .category(Category::Strings)
} }
@ -70,16 +72,24 @@ impl SimplePluginCommand for CreateBundle {
let locale_code: String = call.req(0)?; let locale_code: String = call.req(0)?;
// Parse locale // Parse locale
let locale: LanguageIdentifier = locale_code.parse() let locale: LanguageIdentifier = locale_code.parse().map_err(|e| {
.map_err(|e| LabeledError::new("Invalid locale").with_label(format!("Invalid locale '{}': {}", locale_code, e), call.head))?; LabeledError::new("Invalid locale").with_label(
format!("Invalid locale '{}': {}", locale_code, e),
call.head,
)
})?;
// Create fallback locales // Create fallback locales
let mut locales = vec![locale.clone()]; let mut locales = vec![locale.clone()];
if let Some(fallback_value) = call.get_flag("fallback")? { if let Some(fallback_value) = call.get_flag("fallback")? {
let fallback_codes = extract_string_list(fallback_value)?; let fallback_codes = extract_string_list(fallback_value)?;
for code in fallback_codes { for code in fallback_codes {
let fallback_locale: LanguageIdentifier = code.parse() let fallback_locale: LanguageIdentifier = code.parse().map_err(|e| {
.map_err(|e| LabeledError::new("Invalid fallback locale").with_label(format!("Invalid fallback locale '{}': {}", code, e), call.head))?; LabeledError::new("Invalid fallback locale").with_label(
format!("Invalid fallback locale '{}': {}", code, e),
call.head,
)
})?;
locales.push(fallback_locale); locales.push(fallback_locale);
} }
} }
@ -115,12 +125,12 @@ impl SimplePluginCommand for CreateBundle {
fn extract_string_list(value: Value) -> Result<Vec<String>, LabeledError> { fn extract_string_list(value: Value) -> Result<Vec<String>, LabeledError> {
match value { match value {
Value::List { vals, .. } => { Value::List { vals, .. } => vals
vals.iter() .iter()
.map(|v| value_to_string(v)) .map(|v| value_to_string(v))
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>(),
} _ => Err(LabeledError::new("Invalid list")
_ => Err(LabeledError::new("Invalid list").with_label("Must be a list of strings", nu_protocol::Span::unknown())), .with_label("Must be a list of strings", nu_protocol::Span::unknown())),
} }
} }
@ -134,23 +144,31 @@ fn load_files_to_bundle(
source: &str, source: &str,
) -> Result<(), LabeledError> { ) -> Result<(), LabeledError> {
for file_path in files { for file_path in files {
let content = std::fs::read_to_string(file_path) let content = std::fs::read_to_string(file_path).map_err(|e| {
.map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read '{}': {}", file_path, e), nu_protocol::Span::unknown()))?; LabeledError::new("Read error").with_label(
format!("Failed to read '{}': {}", file_path, e),
nu_protocol::Span::unknown(),
)
})?;
let resource = FluentResource::try_new(content) let resource = FluentResource::try_new(content).map_err(|e| {
.map_err(|e| LabeledError::new("Invalid FTL").with_label(format!("Invalid FTL in '{}': {:?}", file_path, e), nu_protocol::Span::unknown()))?; LabeledError::new("Invalid FTL").with_label(
format!("Invalid FTL in '{}': {:?}", file_path, e),
nu_protocol::Span::unknown(),
)
})?;
bundle.add_resource(resource) bundle.add_resource(resource).map_err(|errors| {
.map_err(|errors| { let error_msgs: Vec<String> = errors.iter().map(|e| format!("{:?}", e)).collect();
let error_msgs: Vec<String> = errors.iter() LabeledError::new("Load error").with_label(
.map(|e| format!("{:?}", e)) format!(
.collect();
LabeledError::new("Load error").with_label(format!(
"Failed to load {} file '{}': {}", "Failed to load {} file '{}': {}",
source, source,
file_path, file_path,
error_msgs.join(", ") error_msgs.join(", ")
), nu_protocol::Span::unknown()) ),
nu_protocol::Span::unknown(),
)
})?; })?;
} }
Ok(()) Ok(())
@ -197,7 +215,7 @@ fn value_to_string(value: &Value) -> Result<String, LabeledError> {
Value::Bool { val, .. } => Ok(val.to_string()), Value::Bool { val, .. } => Ok(val.to_string()),
_ => Err(LabeledError::new("Type conversion error").with_label( _ => Err(LabeledError::new("Type conversion error").with_label(
format!("Cannot convert {:?} to string", value.get_type()), format!("Cannot convert {:?} to string", value.get_type()),
nu_protocol::Span::unknown() nu_protocol::Span::unknown(),
)), )),
} }
} }

View File

@ -1,6 +1,6 @@
use crate::FluentPlugin;
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand}; use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value}; use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value};
use crate::FluentPlugin;
pub struct ExtractMessages; pub struct ExtractMessages;
@ -14,7 +14,11 @@ impl SimplePluginCommand for ExtractMessages {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build(PluginCommand::name(self)) Signature::build(PluginCommand::name(self))
.input_output_type(Type::Any, Type::List(Box::new(Type::String))) .input_output_type(Type::Any, Type::List(Box::new(Type::String)))
.required("file", SyntaxShape::Filepath, "FTL file to extract messages from") .required(
"file",
SyntaxShape::Filepath,
"FTL file to extract messages from",
)
.category(Category::Strings) .category(Category::Strings)
} }
@ -23,13 +27,11 @@ impl SimplePluginCommand for ExtractMessages {
} }
fn examples(&self) -> Vec<nu_protocol::Example<'_>> { fn examples(&self) -> Vec<nu_protocol::Example<'_>> {
vec![ vec![nu_protocol::Example {
nu_protocol::Example {
description: "Extract message IDs from an FTL file", description: "Extract message IDs from an FTL file",
example: "fluent-extract locales/en-US/main.ftl", example: "fluent-extract locales/en-US/main.ftl",
result: None, result: None,
}, }]
]
} }
fn run( fn run(

View File

@ -1,7 +1,7 @@
use crate::FluentPlugin;
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand}; use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value}; use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value};
use std::fs; use std::fs;
use crate::FluentPlugin;
pub struct ListLocales; pub struct ListLocales;
@ -15,7 +15,11 @@ impl SimplePluginCommand for ListLocales {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build(PluginCommand::name(self)) Signature::build(PluginCommand::name(self))
.input_output_type(Type::Any, Type::List(Box::new(Type::String))) .input_output_type(Type::Any, Type::List(Box::new(Type::String)))
.required("directory", SyntaxShape::Directory, "Directory containing locale folders") .required(
"directory",
SyntaxShape::Directory,
"Directory containing locale folders",
)
.category(Category::Strings) .category(Category::Strings)
} }
@ -24,13 +28,11 @@ impl SimplePluginCommand for ListLocales {
} }
fn examples(&self) -> Vec<nu_protocol::Example<'_>> { fn examples(&self) -> Vec<nu_protocol::Example<'_>> {
vec![ vec![nu_protocol::Example {
nu_protocol::Example {
description: "List available locales", description: "List available locales",
example: "fluent-list-locales ./locales", example: "fluent-list-locales ./locales",
result: None, result: None,
}, }]
]
} }
fn run( fn run(
@ -42,17 +44,27 @@ impl SimplePluginCommand for ListLocales {
) -> Result<Value, LabeledError> { ) -> Result<Value, LabeledError> {
let directory: String = call.req(0)?; let directory: String = call.req(0)?;
let entries = fs::read_dir(&directory) let entries = fs::read_dir(&directory).map_err(|e| {
.map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read directory '{}': {}", directory, e), call.head))?; LabeledError::new("Read error").with_label(
format!("Failed to read directory '{}': {}", directory, e),
call.head,
)
})?;
let mut locales = Vec::new(); let mut locales = Vec::new();
for entry in entries { for entry in entries {
let entry = entry let entry = entry.map_err(|e| {
.map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read directory entry: {}", e), call.head))?; LabeledError::new("Read error")
.with_label(format!("Failed to read directory entry: {}", e), call.head)
})?;
if entry.file_type() if entry
.map_err(|e| LabeledError::new("File type error").with_label(format!("Failed to get file type: {}", e), call.head))? .file_type()
.map_err(|e| {
LabeledError::new("File type error")
.with_label(format!("Failed to get file type: {}", e), call.head)
})?
.is_dir() .is_dir()
{ {
if let Some(name) = entry.file_name().to_str() { if let Some(name) = entry.file_name().to_str() {

View File

@ -1,10 +1,8 @@
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{
Category, LabeledError, Signature, SyntaxShape, Type, Value
};
use fluent::{FluentBundle, FluentResource, FluentArgs, FluentValue};
use unic_langid::LanguageIdentifier;
use crate::FluentPlugin; use crate::FluentPlugin;
use fluent::{FluentArgs, FluentBundle, FluentResource, FluentValue};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value};
use unic_langid::LanguageIdentifier;
pub struct Localize; pub struct Localize;
@ -38,7 +36,11 @@ impl SimplePluginCommand for Localize {
"FTL files to load", "FTL files to load",
Some('f'), Some('f'),
) )
.switch("fallback", "Return message ID if translation not found", Some('F')) .switch(
"fallback",
"Return message ID if translation not found",
Some('F'),
)
.category(Category::Strings) .category(Category::Strings)
} }
@ -77,8 +79,12 @@ impl SimplePluginCommand for Localize {
let locale_code: String = call.req(1)?; let locale_code: String = call.req(1)?;
// Parse locale // Parse locale
let locale: LanguageIdentifier = locale_code.parse() let locale: LanguageIdentifier = locale_code.parse().map_err(|e| {
.map_err(|e| LabeledError::new("Invalid locale").with_label(format!("Invalid locale '{}': {}", locale_code, e), call.head))?; LabeledError::new("Invalid locale").with_label(
format!("Invalid locale '{}': {}", locale_code, e),
call.head,
)
})?;
// Create FluentBundle // Create FluentBundle
let mut bundle = FluentBundle::new(vec![locale.clone()]); let mut bundle = FluentBundle::new(vec![locale.clone()]);
@ -92,7 +98,8 @@ impl SimplePluginCommand for Localize {
let files = extract_file_list(files_value)?; let files = extract_file_list(files_value)?;
load_from_files(&mut bundle, &files)?; load_from_files(&mut bundle, &files)?;
} else { } else {
return Err(LabeledError::new("Missing argument").with_label("Must provide either --bundle or --files", call.head)); return Err(LabeledError::new("Missing argument")
.with_label("Must provide either --bundle or --files", call.head));
} }
// Prepare arguments for interpolation // Prepare arguments for interpolation
@ -110,32 +117,37 @@ impl SimplePluginCommand for Localize {
// Return message ID as fallback // Return message ID as fallback
return Ok(Value::string(format!("[[{}]]", message_id), call.head)); return Ok(Value::string(format!("[[{}]]", message_id), call.head));
} else { } else {
return Err(LabeledError::new("Message not found").with_label(format!("Message '{}' not found in locale '{}'", message_id, locale_code), call.head)); return Err(LabeledError::new("Message not found").with_label(
format!(
"Message '{}' not found in locale '{}'",
message_id, locale_code
),
call.head,
));
} }
} }
}; };
// Format the message // Format the message
let pattern = msg.value() let pattern = msg.value().ok_or_else(|| {
.ok_or_else(|| LabeledError::new("Message has no value").with_label(format!("Message '{}' has no value", message_id), call.head))?; LabeledError::new("Message has no value")
.with_label(format!("Message '{}' has no value", message_id), call.head)
})?;
let mut errors = vec![]; let mut errors = vec![];
let formatted = bundle.format_pattern( let formatted = bundle.format_pattern(pattern, fluent_args.as_ref(), &mut errors);
pattern,
fluent_args.as_ref(),
&mut errors
);
// Handle formatting errors // Handle formatting errors
if !errors.is_empty() { if !errors.is_empty() {
let error_msgs: Vec<String> = errors.iter() let error_msgs: Vec<String> = errors.iter().map(|e| format!("{:?}", e)).collect();
.map(|e| format!("{:?}", e)) return Err(LabeledError::new("Formatting error").with_label(
.collect(); format!(
return Err(LabeledError::new("Formatting error").with_label(format!(
"Formatting errors for message '{}': {}", "Formatting errors for message '{}': {}",
message_id, message_id,
error_msgs.join(", ") error_msgs.join(", ")
), call.head)); ),
call.head,
));
} }
Ok(Value::string(formatted.to_string(), call.head)) Ok(Value::string(formatted.to_string(), call.head))
@ -153,7 +165,10 @@ fn load_from_bundle_value(
load_messages_from_value(bundle, messages_value)?; load_messages_from_value(bundle, messages_value)?;
} }
} }
_ => return Err(LabeledError::new("Invalid bundle").with_label("Bundle must be a record", nu_protocol::Span::unknown())), _ => {
return Err(LabeledError::new("Invalid bundle")
.with_label("Bundle must be a record", nu_protocol::Span::unknown()))
}
} }
Ok(()) Ok(())
} }
@ -172,16 +187,27 @@ fn load_messages_from_value(
// Create a minimal FTL resource from the message // Create a minimal FTL resource from the message
let ftl_content = format!("{} = {}", id, text); let ftl_content = format!("{} = {}", id, text);
let resource = FluentResource::try_new(ftl_content) let resource = FluentResource::try_new(ftl_content).map_err(|_| {
.map_err(|_| LabeledError::new("Invalid FTL").with_label(format!("Invalid FTL for message '{}'", id), nu_protocol::Span::unknown()))?; LabeledError::new("Invalid FTL").with_label(
format!("Invalid FTL for message '{}'", id),
nu_protocol::Span::unknown(),
)
})?;
bundle.add_resource(resource) bundle.add_resource(resource).map_err(|_| {
.map_err(|_| LabeledError::new("Failed to add message").with_label(format!("Failed to add message '{}'", id), nu_protocol::Span::unknown()))?; LabeledError::new("Failed to add message").with_label(
format!("Failed to add message '{}'", id),
nu_protocol::Span::unknown(),
)
})?;
} }
} }
} }
} }
_ => return Err(LabeledError::new("Invalid messages").with_label("Messages must be a list", nu_protocol::Span::unknown())), _ => {
return Err(LabeledError::new("Invalid messages")
.with_label("Messages must be a list", nu_protocol::Span::unknown()))
}
} }
Ok(()) Ok(())
} }
@ -191,26 +217,40 @@ fn load_from_files(
files: &[String], files: &[String],
) -> Result<(), LabeledError> { ) -> Result<(), LabeledError> {
for file_path in files { for file_path in files {
let content = std::fs::read_to_string(file_path) let content = std::fs::read_to_string(file_path).map_err(|e| {
.map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read '{}': {}", file_path, e), nu_protocol::Span::unknown()))?; LabeledError::new("Read error").with_label(
format!("Failed to read '{}': {}", file_path, e),
nu_protocol::Span::unknown(),
)
})?;
let resource = FluentResource::try_new(content) let resource = FluentResource::try_new(content).map_err(|e| {
.map_err(|e| LabeledError::new("Invalid FTL").with_label(format!("Invalid FTL in '{}': {:?}", file_path, e), nu_protocol::Span::unknown()))?; LabeledError::new("Invalid FTL").with_label(
format!("Invalid FTL in '{}': {:?}", file_path, e),
nu_protocol::Span::unknown(),
)
})?;
bundle.add_resource(resource) bundle.add_resource(resource).map_err(|e| {
.map_err(|e| LabeledError::new("Load error").with_label(format!("Failed to load '{}': {:?}", file_path, e), nu_protocol::Span::unknown()))?; LabeledError::new("Load error").with_label(
format!("Failed to load '{}': {:?}", file_path, e),
nu_protocol::Span::unknown(),
)
})?;
} }
Ok(()) Ok(())
} }
fn extract_file_list(files_value: Value) -> Result<Vec<String>, LabeledError> { fn extract_file_list(files_value: Value) -> Result<Vec<String>, LabeledError> {
match files_value { match files_value {
Value::List { vals, .. } => { Value::List { vals, .. } => vals
vals.iter() .iter()
.map(|v| value_to_string(v)) .map(|v| value_to_string(v))
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>(),
} _ => Err(LabeledError::new("Invalid files").with_label(
_ => Err(LabeledError::new("Invalid files").with_label("Files must be a list of strings", nu_protocol::Span::unknown())), "Files must be a list of strings",
nu_protocol::Span::unknown(),
)),
} }
} }
@ -224,16 +264,24 @@ fn convert_to_fluent_args(args_value: Value) -> Result<FluentArgs<'static>, Labe
Value::String { val, .. } => FluentValue::from(val.clone()), Value::String { val, .. } => FluentValue::from(val.clone()),
Value::Int { val, .. } => FluentValue::from(*val as f64), Value::Int { val, .. } => FluentValue::from(*val as f64),
Value::Float { val, .. } => FluentValue::from(*val), Value::Float { val, .. } => FluentValue::from(*val),
_ => return Err(LabeledError::new("Unsupported argument type").with_label(format!( _ => {
return Err(LabeledError::new("Unsupported argument type").with_label(
format!(
"Unsupported argument type for '{}': {:?}", "Unsupported argument type for '{}': {:?}",
key, key,
value.get_type() value.get_type()
), nu_protocol::Span::unknown())), ),
nu_protocol::Span::unknown(),
))
}
}; };
fluent_args.set(key.clone(), fluent_value); fluent_args.set(key.clone(), fluent_value);
} }
} }
_ => return Err(LabeledError::new("Invalid arguments").with_label("Arguments must be a record", nu_protocol::Span::unknown())), _ => {
return Err(LabeledError::new("Invalid arguments")
.with_label("Arguments must be a record", nu_protocol::Span::unknown()))
}
} }
Ok(fluent_args) Ok(fluent_args)
@ -247,7 +295,7 @@ fn value_to_string(value: &Value) -> Result<String, LabeledError> {
Value::Bool { val, .. } => Ok(val.to_string()), Value::Bool { val, .. } => Ok(val.to_string()),
_ => Err(LabeledError::new("Type conversion error").with_label( _ => Err(LabeledError::new("Type conversion error").with_label(
format!("Cannot convert {:?} to string", value.get_type()), format!("Cannot convert {:?} to string", value.get_type()),
nu_protocol::Span::unknown() nu_protocol::Span::unknown(),
)), )),
} }
} }

View File

@ -1,14 +1,13 @@
mod parse_ftl; mod create_bundle;
mod localize;
mod validate_ftl;
mod extract_messages; mod extract_messages;
mod list_locales; mod list_locales;
mod create_bundle; mod localize;
mod parse_ftl;
mod validate_ftl;
pub use parse_ftl::ParseFtl; pub use create_bundle::CreateBundle;
pub use localize::Localize;
pub use validate_ftl::ValidateFtl;
pub use extract_messages::ExtractMessages; pub use extract_messages::ExtractMessages;
pub use list_locales::ListLocales; pub use list_locales::ListLocales;
pub use create_bundle::CreateBundle; pub use localize::Localize;
pub use parse_ftl::ParseFtl;
pub use validate_ftl::ValidateFtl;

View File

@ -1,11 +1,9 @@
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{
Category, LabeledError, Signature, Span, SyntaxShape, Type, Value, record
};
use fluent_syntax::parser::parse;
use fluent_syntax::ast::{Entry, Message, Pattern, PatternElement};
use std::fs;
use crate::FluentPlugin; use crate::FluentPlugin;
use fluent_syntax::ast::{Entry, Message, Pattern, PatternElement};
use fluent_syntax::parser::parse;
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{record, Category, LabeledError, Signature, Span, SyntaxShape, Type, Value};
use std::fs;
pub struct ParseFtl; pub struct ParseFtl;
@ -28,13 +26,11 @@ impl SimplePluginCommand for ParseFtl {
} }
fn examples(&self) -> Vec<nu_protocol::Example<'_>> { fn examples(&self) -> Vec<nu_protocol::Example<'_>> {
vec![ vec![nu_protocol::Example {
nu_protocol::Example {
description: "Parse an FTL file", description: "Parse an FTL file",
example: "fluent-parse locales/en-US/main.ftl", example: "fluent-parse locales/en-US/main.ftl",
result: None, result: None,
}, }]
]
} }
fn run( fn run(
@ -47,14 +43,21 @@ impl SimplePluginCommand for ParseFtl {
let file_path: String = call.req(0)?; let file_path: String = call.req(0)?;
// Read FTL file // Read FTL file
let ftl_content = fs::read_to_string(&file_path) let ftl_content = fs::read_to_string(&file_path).map_err(|e| {
.map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read file '{}': {}", file_path, e), call.head))?; LabeledError::new("Read error").with_label(
format!("Failed to read file '{}': {}", file_path, e),
call.head,
)
})?;
// Parse FTL content // Parse FTL content
let resource = match parse(ftl_content) { let resource = match parse(ftl_content) {
Ok(res) => res, Ok(res) => res,
Err((_, errors)) => { Err((_, errors)) => {
return Err(LabeledError::new("Parse error").with_label(format!("Failed to parse FTL content: {:?}", errors), call.head)); return Err(LabeledError::new("Parse error").with_label(
format!("Failed to parse FTL content: {:?}", errors),
call.head,
));
} }
}; };
@ -76,7 +79,10 @@ impl SimplePluginCommand for ParseFtl {
} }
} }
fn extract_messages_from_resource(resource: &fluent_syntax::ast::Resource<String>, span: Span) -> Result<Vec<Value>, String> { fn extract_messages_from_resource(
resource: &fluent_syntax::ast::Resource<String>,
span: Span,
) -> Result<Vec<Value>, String> {
let mut messages = Vec::new(); let mut messages = Vec::new();
for entry in &resource.body { for entry in &resource.body {
@ -113,8 +119,16 @@ fn extract_message_info(message: &Message<String>, span: Span) -> Result<Value,
} }
// Extract comment if present // Extract comment if present
let comment = message.comment.as_ref() let comment = message
.map(|c| c.content.iter().map(|line| line.trim()).collect::<Vec<_>>().join("\n")) .comment
.as_ref()
.map(|c| {
c.content
.iter()
.map(|line| line.trim())
.collect::<Vec<_>>()
.join("\n")
})
.unwrap_or_default(); .unwrap_or_default();
Ok(Value::record( Ok(Value::record(
@ -129,7 +143,8 @@ fn extract_message_info(message: &Message<String>, span: Span) -> Result<Value,
} }
fn extract_pattern_text(pattern: &Pattern<String>) -> String { fn extract_pattern_text(pattern: &Pattern<String>) -> String {
pattern.elements pattern
.elements
.iter() .iter()
.map(|element| match element { .map(|element| match element {
PatternElement::TextElement { value } => value.to_string(), PatternElement::TextElement { value } => value.to_string(),

View File

@ -1,7 +1,7 @@
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value, record};
use fluent_syntax::parser::parse;
use crate::FluentPlugin; use crate::FluentPlugin;
use fluent_syntax::parser::parse;
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{record, Category, LabeledError, Signature, SyntaxShape, Type, Value};
pub struct ValidateFtl; pub struct ValidateFtl;
@ -24,13 +24,11 @@ impl SimplePluginCommand for ValidateFtl {
} }
fn examples(&self) -> Vec<nu_protocol::Example<'_>> { fn examples(&self) -> Vec<nu_protocol::Example<'_>> {
vec![ vec![nu_protocol::Example {
nu_protocol::Example {
description: "Validate an FTL file", description: "Validate an FTL file",
example: "fluent-validate locales/en-US/main.ftl", example: "fluent-validate locales/en-US/main.ftl",
result: None, result: None,
}, }]
]
} }
fn run( fn run(
@ -42,8 +40,10 @@ impl SimplePluginCommand for ValidateFtl {
) -> Result<Value, LabeledError> { ) -> Result<Value, LabeledError> {
let file_path: String = call.req(0)?; let file_path: String = call.req(0)?;
let content = std::fs::read_to_string(&file_path) let content = std::fs::read_to_string(&file_path).map_err(|e| {
.map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read '{}': {}", file_path, e), call.head))?; LabeledError::new("Read error")
.with_label(format!("Failed to read '{}': {}", file_path, e), call.head)
})?;
let parse_result = parse(content); let parse_result = parse(content);
let result = match parse_result { let result = match parse_result {
@ -56,7 +56,8 @@ impl SimplePluginCommand for ValidateFtl {
call.head, call.head,
), ),
Err((_resource, errors)) => { Err((_resource, errors)) => {
let error_list: Vec<Value> = errors.iter() let error_list: Vec<Value> = errors
.iter()
.map(|err| Value::string(format!("{:?}", err), call.head)) .map(|err| Value::string(format!("{:?}", err), call.head))
.collect(); .collect();

View File

@ -22,4 +22,3 @@ impl Plugin for FluentPlugin {
] ]
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ keywords = [
categories = ["algorithms"] categories = ["algorithms"]
repository = "https://github.com/ArmoredPony/nu_plugin_hashes" repository = "https://github.com/ArmoredPony/nu_plugin_hashes"
license = "MIT" license = "MIT"
version = "0.109.1" version = "0.111.0"
edition = "2024" edition = "2024"
[features] [features]
@ -37,9 +37,9 @@ default = [
] ]
[dependencies] [dependencies]
nu-cmd-base = "0.109.1" nu-cmd-base = "0.111.0"
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
digest = "0.10.7" digest = "0.10.7"
[dependencies.ascon-hash] [dependencies.ascon-hash]
@ -215,9 +215,8 @@ optional = true
version = "0.10.4" version = "0.10.4"
optional = true optional = true
[dev-dependencies.nu-plugin-test-support] [dev-dependencies]
version = "0.109.1" nu-plugin-test-support = "0.111.0"
path = "../nushell/crates/nu-plugin-test-support"
[profile.release] [profile.release]
strip = true strip = true

View File

@ -1,225 +0,0 @@
[package]
name = "nu_plugin_hashes"
description = "A Nushell plugin that adds 63 cryptographic hash functions from Hashes project"
keywords = [
"nu",
"plugin",
"hash",
]
categories = ["algorithms"]
repository = "https://github.com/ArmoredPony/nu_plugin_hashes"
license = "MIT"
version = "0.109.0"
edition = "2024"
[features]
default = [
"ascon-hash",
"belt-hash",
"blake2",
"blake3",
"fsb",
"gost94",
"groestl",
"jh",
"md2",
"md4",
"ripemd",
"sha1",
"sha2",
"sha3",
"shabal",
"skein",
"sm3",
"streebog",
"tiger",
"whirlpool",
]
[dependencies]
nu-cmd-base = "0.109.1"
nu-plugin = "0.109.1"
nu-protocol = "0.109.1"
digest = "0.10.7"
[dependencies.ascon-hash]
version = "0.3.1"
optional = true
[dependencies.belt-hash]
version = "0.1.1"
optional = true
[dependencies.blake2]
version = "0.10.6"
optional = true
[dependencies.blake3]
version = "1.8.2"
optional = true
default-features = false
features = [
"std",
"traits-preview",
]
[dependencies.fsb]
version = "0.1.3"
optional = true
[dependencies.gost94]
version = "0.10.4"
optional = true
[dependencies.groestl]
version = "0.10.1"
optional = true
[dependencies.jh]
version = "0.1.0"
optional = true
[dependencies.md2]
version = "0.10.2"
optional = true
[dependencies.md4]
version = "0.10.2"
optional = true
[dependencies.ripemd]
version = "0.1.3"
optional = true
[dependencies.sha1]
version = "0.10.6"
optional = true
[dependencies.sha2]
version = "0.10.9"
optional = true
[dependencies.sha3]
version = "0.10.8"
optional = true
[dependencies.shabal]
version = "0.4.1"
optional = true
[dependencies.skein]
version = "0.1.1"
optional = true
[dependencies.sm3]
version = "0.4.2"
optional = true
[dependencies.streebog]
version = "0.10.2"
optional = true
[dependencies.tiger]
version = "0.2.1"
optional = true
[dependencies.whirlpool]
version = "0.10.4"
optional = true
[build-dependencies]
digest = "0.10.7"
[build-dependencies.ascon-hash]
version = "0.3.1"
optional = true
[build-dependencies.belt-hash]
version = "0.1.1"
optional = true
[build-dependencies.blake2]
version = "0.10.6"
optional = true
[build-dependencies.blake3]
version = "1.8.2"
optional = true
default-features = false
features = [
"std",
"traits-preview",
]
[build-dependencies.fsb]
version = "0.1.3"
optional = true
[build-dependencies.gost94]
version = "0.10.4"
optional = true
[build-dependencies.groestl]
version = "0.10.1"
optional = true
[build-dependencies.jh]
version = "0.1.0"
optional = true
[build-dependencies.md2]
version = "0.10.2"
optional = true
[build-dependencies.md4]
version = "0.10.2"
optional = true
[build-dependencies.ripemd]
version = "0.1.3"
optional = true
[build-dependencies.sha1]
version = "0.10.6"
optional = true
[build-dependencies.sha2]
version = "0.10.9"
optional = true
[build-dependencies.sha3]
version = "0.10.8"
optional = true
[build-dependencies.shabal]
version = "0.4.1"
optional = true
[build-dependencies.skein]
version = "0.1.1"
optional = true
[build-dependencies.sm3]
version = "0.4.2"
optional = true
[build-dependencies.streebog]
version = "0.10.2"
optional = true
[build-dependencies.tiger]
version = "0.2.1"
optional = true
[build-dependencies.whirlpool]
version = "0.10.4"
optional = true
[dev-dependencies.nu-plugin-test-support]
version = "0.109.0"
path = "../nushell/crates/nu-plugin-test-support"
[profile.release]
strip = true
lto = true
codegen-units = 1

View File

@ -13,20 +13,25 @@ crate.
## Installation ## Installation
To install this plugin with all algorithms available run To install this plugin with all algorithms available run
```nu
```nushell
nu
cargo install nu_plugin_hashes cargo install nu_plugin_hashes
plugin add ($env.CARGO_HOME ++ /bin/nu_plugin_hashes) plugin add ($env.CARGO_HOME ++ /bin/nu_plugin_hashes)
``` ```
or on Windows or on Windows
```nu
```nushell
nu
cargo install nu_plugin_hashes cargo install nu_plugin_hashes
plugin add ($env.CARGO_HOME ++ /bin/nu_plugin_hashes.exe) plugin add ($env.CARGO_HOME ++ /bin/nu_plugin_hashes.exe)
``` ```
After loading the plugin, execute `help hash` to list newly added commands After loading the plugin, execute `help hash` to list newly added commands
```nu ```nushell
nu
~> help hash ~> help hash
Apply hash function. Apply hash function.
@ -47,12 +52,16 @@ Subcommands:
If you only need some algorithms, disable default features and select only If you only need some algorithms, disable default features and select only
those you need those you need
```nu
```nushell
nu
cargo install nu_plugin_hashes --no-default-features --features sha2,streebog cargo install nu_plugin_hashes --no-default-features --features sha2,streebog
``` ```
Then check what's installed Then check what's installed
```nu
```nushell
nu
~> help hash ~> help hash
Apply hash function. Apply hash function.

View File

@ -467,12 +467,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let hashers_generated_path = Path::new(&out_dir).join("hashers_generated.rs"); let hashers_generated_path = Path::new(&out_dir).join("hashers_generated.rs");
let commands_generated_path = let commands_generated_path = Path::new(&out_dir).join("commands_generated.rs");
Path::new(&out_dir).join("commands_generated.rs"); let mut hashers_generated_file = std::fs::File::create(hashers_generated_path).unwrap();
let mut hashers_generated_file = let mut commands_generated_file = std::fs::File::create(commands_generated_path).unwrap();
std::fs::File::create(hashers_generated_path).unwrap();
let mut commands_generated_file =
std::fs::File::create(commands_generated_path).unwrap();
write!( write!(
hashers_generated_file, hashers_generated_file,
@ -494,13 +491,10 @@ pub fn commands() -> Vec<Box<dyn PluginCommand<Plugin = HashesPlugin>>> {{
)?; )?;
for mut hasher_impl_meta in hasher_impls { for mut hasher_impl_meta in hasher_impls {
let feature_name = let feature_name = hasher_impl_meta.crate_name.replace("-", "_").to_uppercase();
hasher_impl_meta.crate_name.replace("-", "_").to_uppercase();
if std::env::var(format!("CARGO_FEATURE_{feature_name}")).is_ok() { if std::env::var(format!("CARGO_FEATURE_{feature_name}")).is_ok() {
hashers_generated_file hashers_generated_file.write_all(build_impl_str(&mut hasher_impl_meta).as_bytes())?;
.write_all(build_impl_str(&mut hasher_impl_meta).as_bytes())?; hashers_generated_file.write_all(build_test_str(&hasher_impl_meta).as_bytes())?;
hashers_generated_file
.write_all(build_test_str(&hasher_impl_meta).as_bytes())?;
let crate_name = hasher_impl_meta.crate_name; let crate_name = hasher_impl_meta.crate_name;
let hasher_type_name = hasher_impl_meta.hasher_type_name; let hasher_type_name = hasher_impl_meta.hasher_type_name;
@ -586,13 +580,11 @@ impl Hasher for {crate_name}::{hasher_type_name} {{
}} }}
}} }}
", ",
hash hash.iter()
.iter()
.map(|b| format!("{b:02x?}")) .map(|b| format!("{b:02x?}"))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(""), .join(""),
hash hash.iter()
.iter()
.map(|b| format!("0x{b:02x?}")) .map(|b| format!("0x{b:02x?}"))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(",") .join(",")

View File

@ -7,21 +7,11 @@
use std::{io::Write, marker::PhantomData, ops::Not}; use std::{io::Write, marker::PhantomData, ops::Not};
use digest::{Digest, Output}; use digest::{Digest, Output};
use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_cmd_base::input_handler::{CmdArgument, operate};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{ use nu_protocol::{
ast::CellPath, Category, Example, IntoPipelineData, LabeledError, PipelineData, ShellError, Signature, Span,
Category, SyntaxShape, Type, Value, ast::CellPath,
Example,
IntoPipelineData,
LabeledError,
PipelineData,
ShellError,
Signature,
Span,
SyntaxShape,
Type,
Value,
}; };
use crate::HashesPlugin; use crate::HashesPlugin;
@ -42,10 +32,7 @@ impl<H: Hasher> Default for GenericHasher<H> {
fn default() -> Self { fn default() -> Self {
Self { Self {
name: format!("hash {}", H::name()), name: format!("hash {}", H::name()),
description: format!( description: format!("Hash a value using the {} hash algorithm.", H::name()),
"Hash a value using the {} hash algorithm.",
H::name()
),
_hasher: PhantomData, _hasher: PhantomData,
} }
} }

View File

@ -1,4 +1,4 @@
use nu_plugin::{serve_plugin, MsgPackSerializer}; use nu_plugin::{MsgPackSerializer, serve_plugin};
use nu_plugin_hashes::HashesPlugin; use nu_plugin_hashes::HashesPlugin;
fn main() { fn main() {

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu_plugin_highlight" name = "nu_plugin_highlight"
version = "1.4.7+0.105.2" version = "0.111.0"
authors = ["Tim 'Piepmatz' Hesse"] authors = ["Tim 'Piepmatz' Hesse"]
edition = "2024" edition = "2024"
repository = "https://github.com/cptpiepmatz/nu-plugin-highlight" repository = "https://github.com/cptpiepmatz/nu-plugin-highlight"
@ -26,9 +26,9 @@ version = "0.26"
default-features = false default-features = false
[dependencies] [dependencies]
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
nu-path = "0.109.1" nu-path = "0.111.0"
nu-ansi-term = "0.50" nu-ansi-term = "0.50"
ansi_colours = "1" ansi_colours = "1"
mime_guess = "2" mime_guess = "2"

View File

@ -16,8 +16,8 @@
</div> </div>
## About ## About
`nu-plugin-highlight` is a plugin for [Nushell](https://www.nushell.sh) that `nu-plugin-highlight` is a plugin for [Nushell](https://www.nushell.sh) that
provides syntax highlighting for source code. provides syntax highlighting for source code.
It uses the [`syntect`](https://crates.io/crates/syntect) library for syntax It uses the [`syntect`](https://crates.io/crates/syntect) library for syntax
@ -26,9 +26,12 @@ access to its ready-to-use assets.
Custom themes can be loaded too. Custom themes can be loaded too.
## Usage ## Usage
The `highlight` command can be used for syntax highlighting source code. The `highlight` command can be used for syntax highlighting source code.
Here are a few examples: Here are a few examples:
```nushell ```nushell
nushell
# Highlight a Markdown file by guessing the type from the pipeline metadata # Highlight a Markdown file by guessing the type from the pipeline metadata
open README.md | highlight open README.md | highlight
@ -49,11 +52,13 @@ highlight --list-themes
``` ```
### Parameters ### Parameters
- `language <string>`: - `language <string>`:
This is an optional parameter that can be used to specify the language or file This is an optional parameter that can be used to specify the language or file
extension to aid language detection. extension to aid language detection.
### Flags ### Flags
- `-h, --help`: - `-h, --help`:
Display the help message for the highlight command. Display the help message for the highlight command.
@ -64,18 +69,23 @@ highlight --list-themes
List all possible themes. List all possible themes.
## Configuration ## Configuration
The plugin can be configured using the The plugin can be configured using the
[`$env.config.plugins.highlight`](https://github.com/nushell/nushell/pull/10955) [`$env.config.plugins.highlight`](https://github.com/nushell/nushell/pull/10955)
variable. variable.
### `true_colors` ### `true_colors`
Enable or disable true colors (24-bit). Enable or disable true colors (24-bit).
By default, this is enabled. By default, this is enabled.
```nushell ```nushell
nushell
$env.config.plugins.highlight.true_colors = true $env.config.plugins.highlight.true_colors = true
``` ```
### `theme` ### `theme`
Set a theme to use. Set a theme to use.
The default theme depends on the operating system. The default theme depends on the operating system.
Use `highlight --list-themes | where default == true` to see your default theme. Use `highlight --list-themes | where default == true` to see your default theme.
@ -83,24 +93,31 @@ Setting this environment variable should allow
`highlight --list-themes | where id == $env.config.plugins.highlight.theme` to `highlight --list-themes | where id == $env.config.plugins.highlight.theme` to
result in a single row with your selected theme. result in a single row with your selected theme.
If you get no results, you have set an invalid theme. If you get no results, you have set an invalid theme.
```nushell ```nushell
nushell
$env.config.plugins.highlight.theme = ansi $env.config.plugins.highlight.theme = ansi
``` ```
### `custom_themes` ### `custom_themes`
Set a directory to load custom themes from. Set a directory to load custom themes from.
Using `synctect`s theme loader, you can load custom themes in the `.tmtheme` Using `synctect`s theme loader, you can load custom themes in the `.tmtheme`
format from a directory that is passed as this configuration value. format from a directory that is passed as this configuration value.
```nushell
```toml
nushell
$env.config.plugins.highlight.custom_themes = ~/.nu/highlight/themes $env.config.plugins.highlight.custom_themes = ~/.nu/highlight/themes
``` ```
## Plugin Installation ## Plugin Installation
Installing and registering the `nu-plugin-highlight` is a straightforward Installing and registering the `nu-plugin-highlight` is a straightforward
process. process.
Follow these steps: Follow these steps:
1. Install the plugin from crates.io using cargo: 1. Install the plugin from crates.io using cargo:
```nushell ```nushell
cargo install nu_plugin_highlight cargo install nu_plugin_highlight
``` ```
@ -108,6 +125,7 @@ Follow these steps:
2. Restart your terminal session to ensure the newly installed plugin is recognized. 2. Restart your terminal session to ensure the newly installed plugin is recognized.
3. Find path of your installation: 3. Find path of your installation:
```nushell ```nushell
which nu_plugin_highlight which nu_plugin_highlight
``` ```
@ -115,6 +133,7 @@ Follow these steps:
4. Register the plugin with Nushell: 4. Register the plugin with Nushell:
If you are using a version **lower** than **0.93.0**, use `register` instead of `plugin add`. If you are using a version **lower** than **0.93.0**, use `register` instead of `plugin add`.
```nushell ```nushell
plugin add path/to/the/plugin/binary plugin add path/to/the/plugin/binary
``` ```
@ -124,6 +143,7 @@ Follow these steps:
Tip: You can simply restart the shell or terminal. When nushell starts, it loads all plugins. Tip: You can simply restart the shell or terminal. When nushell starts, it loads all plugins.
If you are using a version **lower** than **0.93.0**, you do **not need** to do this. If you are using a version **lower** than **0.93.0**, you do **not need** to do this.
```nushell ```nushell
plugin use highlight plugin use highlight
``` ```
@ -131,10 +151,12 @@ Follow these steps:
After registering, the plugin is available as part of your set of commands: After registering, the plugin is available as part of your set of commands:
```nushell ```nushell
nushell
help commands | where command_type == "plugin" help commands | where command_type == "plugin"
``` ```
## Version Numbering ## Version Numbering
Starting with version `v1.1.0`, the version number of `nu-plugin-highlight` Starting with version `v1.1.0`, the version number of `nu-plugin-highlight`
incorporates the version number of its dependency, `nu-plugin`. incorporates the version number of its dependency, `nu-plugin`.
This is denoted in the format `v1.1.0+0.90.1`, where `v1.1.0` refers to the This is denoted in the format `v1.1.0+0.90.1`, where `v1.1.0` refers to the
@ -142,5 +164,6 @@ version of `nu-plugin-highlight` and `0.90.1` refers to the version of the
`nu-plugin` dependency. `nu-plugin` dependency.
## License ## License
`nu_plugin_highlight` is licensed under the MIT License. `nu_plugin_highlight` is licensed under the MIT License.
See [LICENSE](LICENSE) for more information. See [LICENSE](LICENSE) for more information.

View File

@ -2,11 +2,11 @@ use std::ops::Deref;
use std::path::Path; use std::path::Path;
use bat::assets::HighlightingAssets; use bat::assets::HighlightingAssets;
use bat::theme::{default_theme, ColorScheme}; use bat::theme::{ColorScheme, default_theme};
use syntect::LoadingError;
use syntect::easy::HighlightLines; use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet; use syntect::highlighting::ThemeSet;
use syntect::parsing::{SyntaxReference, SyntaxSet}; use syntect::parsing::{SyntaxReference, SyntaxSet};
use syntect::LoadingError;
use crate::terminal; use crate::terminal;
use crate::theme::{ListThemes, ThemeDescription}; use crate::theme::{ListThemes, ThemeDescription};
@ -17,7 +17,7 @@ const SYNTAX_SET: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/syntax_set.b
pub struct Highlighter { pub struct Highlighter {
syntax_set: SyntaxSet, syntax_set: SyntaxSet,
highlighting_assets: HighlightingAssets, highlighting_assets: HighlightingAssets,
custom_themes: Option<ThemeSet> custom_themes: Option<ThemeSet>,
} }
impl Highlighter { impl Highlighter {
@ -27,13 +27,13 @@ impl Highlighter {
syntax_set: syntect::dumps::from_uncompressed_data(SYNTAX_SET) syntax_set: syntect::dumps::from_uncompressed_data(SYNTAX_SET)
.expect("Failed to load syntax set"), .expect("Failed to load syntax set"),
highlighting_assets: HighlightingAssets::from_binary(), highlighting_assets: HighlightingAssets::from_binary(),
custom_themes: None custom_themes: None,
} }
} }
pub fn custom_themes_from_folder( pub fn custom_themes_from_folder(
&mut self, &mut self,
path: impl AsRef<Path> path: impl AsRef<Path>,
) -> Result<(), LoadingError> { ) -> Result<(), LoadingError> {
let path = nu_path::expand_to_real_path(path); let path = nu_path::expand_to_real_path(path);
self.custom_themes = Some(ThemeSet::load_from_folder(path)?); self.custom_themes = Some(ThemeSet::load_from_folder(path)?);
@ -53,7 +53,7 @@ impl Highlighter {
id: t_id.to_owned(), id: t_id.to_owned(),
name: theme.name.clone(), name: theme.name.clone(),
author: theme.author.clone(), author: theme.author.clone(),
default: default_theme_id == t_id default: default_theme_id == t_id,
} }
}) })
.collect(); .collect();
@ -64,7 +64,7 @@ impl Highlighter {
id: id.to_owned(), id: id.to_owned(),
name: theme.name.clone(), name: theme.name.clone(),
author: theme.author.clone(), author: theme.author.clone(),
default: default_theme_id == id default: default_theme_id == id,
}); });
} }
} }
@ -91,7 +91,7 @@ impl Highlighter {
input: &str, input: &str,
language: Option<&str>, language: Option<&str>,
theme: Option<&str>, theme: Option<&str>,
true_colors: bool true_colors: bool,
) -> String { ) -> String {
let syntax_set = &self.syntax_set; let syntax_set = &self.syntax_set;
let syntax_ref: Option<&SyntaxReference> = match language { let syntax_ref: Option<&SyntaxReference> = match language {
@ -118,7 +118,7 @@ impl Highlighter {
.or_else(|| syntax_set.find_syntax_by_extension(&language_lowercase)) .or_else(|| syntax_set.find_syntax_by_extension(&language_lowercase))
.or_else(|| syntax_set.find_syntax_by_extension(&language_capitalized)) .or_else(|| syntax_set.find_syntax_by_extension(&language_capitalized))
} }
_ => None _ => None,
}; };
let syntax_ref = syntax_ref let syntax_ref = syntax_ref
.or(syntax_set.find_syntax_by_first_line(input)) .or(syntax_set.find_syntax_by_first_line(input))
@ -126,7 +126,7 @@ impl Highlighter {
let theme_id = match theme { let theme_id = match theme {
None => default_theme(ColorScheme::Dark), None => default_theme(ColorScheme::Dark),
Some(theme) => theme Some(theme) => theme,
}; };
let theme = self let theme = self
.custom_themes .custom_themes
@ -143,10 +143,11 @@ impl Highlighter {
// insert a newline in between lines, this is necessary for bats syntax set // insert a newline in between lines, this is necessary for bats syntax set
let l = match i == line_count - 1 { let l = match i == line_count - 1 {
false => format!("{}\n", l.trim_end()), false => format!("{}\n", l.trim_end()),
true => l.trim_end().to_owned() true => l.trim_end().to_owned(),
}; };
let styled_lines = highlighter.highlight_line(&l, syntax_set) let styled_lines = highlighter
.highlight_line(&l, syntax_set)
.expect("Failed to highlight line"); .expect("Failed to highlight line");
styled_lines styled_lines
.iter() .iter()

View File

@ -1,4 +1,4 @@
use nu_plugin::{serve_plugin, MsgPackSerializer}; use nu_plugin::{MsgPackSerializer, serve_plugin};
use plugin::HighlightPlugin; use plugin::HighlightPlugin;
mod highlight; mod highlight;

View File

@ -6,7 +6,7 @@ use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand};
use nu_protocol::shell_error::io::IoError; use nu_protocol::shell_error::io::IoError;
use nu_protocol::{ use nu_protocol::{
Category, DataSource, ErrorLabel, Example, FromValue, IntoValue, LabeledError, PipelineData, Category, DataSource, ErrorLabel, Example, FromValue, IntoValue, LabeledError, PipelineData,
PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
}; };
use syntect::LoadingError; use syntect::LoadingError;
@ -29,7 +29,7 @@ impl Plugin for HighlightPlugin {
struct Config { struct Config {
pub theme: Option<Spanned<String>>, pub theme: Option<Spanned<String>>,
pub true_colors: Option<bool>, pub true_colors: Option<bool>,
pub custom_themes: Option<Spanned<PathBuf>> pub custom_themes: Option<Spanned<PathBuf>>,
} }
struct Highlight; struct Highlight;
@ -46,13 +46,13 @@ impl PluginCommand for Highlight {
.optional( .optional(
"language", "language",
SyntaxShape::String, SyntaxShape::String,
"language or file extension to help language detection" "language or file extension to help language detection",
) )
.named( .named(
"theme", "theme",
SyntaxShape::String, SyntaxShape::String,
"them used for highlighting", "them used for highlighting",
Some('t') Some('t'),
) )
.switch("list-themes", "list all possible themes", None) .switch("list-themes", "list all possible themes", None)
.category(Category::Strings) .category(Category::Strings)
@ -66,8 +66,8 @@ impl PluginCommand for Highlight {
(String::from("author"), Type::String), (String::from("author"), Type::String),
(String::from("default"), Type::Bool), (String::from("default"), Type::Bool),
] ]
.into() .into(),
) ),
) )
} }
@ -80,7 +80,7 @@ impl PluginCommand for Highlight {
_plugin: &Self::Plugin, _plugin: &Self::Plugin,
engine: &EngineInterface, engine: &EngineInterface,
call: &EvaluatedCall, call: &EvaluatedCall,
input: PipelineData input: PipelineData,
) -> Result<PipelineData, LabeledError> { ) -> Result<PipelineData, LabeledError> {
let mut highlighter = Highlighter::new(); let mut highlighter = Highlighter::new();
@ -96,17 +96,17 @@ impl PluginCommand for Highlight {
err, err,
custom_themes_path.span, custom_themes_path.span,
custom_themes_path.item, custom_themes_path.item,
"Error while loading custom themes" "Error while loading custom themes",
) ),
))) )));
} }
Err(err) => { Err(err) => {
return Err(labeled_error( return Err(labeled_error(
err, err,
"Error while loading custom themes", "Error while loading custom themes",
custom_themes_path.span, custom_themes_path.span,
None None,
)) ));
} }
} }
} }
@ -117,12 +117,13 @@ impl PluginCommand for Highlight {
.transpose()? .transpose()?
.or(config.theme); .or(config.theme);
if let Some(theme) = &theme if let Some(theme) = &theme
&& !highlighter.is_valid_theme(&theme.item) { && !highlighter.is_valid_theme(&theme.item)
{
return Err(labeled_error( return Err(labeled_error(
"use `highlight --list-themes` to list all themes", "use `highlight --list-themes` to list all themes",
format!("Unknown passed theme {:?}", &theme.item), format!("Unknown passed theme {:?}", &theme.item),
theme.span, theme.span,
None None,
)); ));
} }
let theme = theme.map(|spanned| spanned.item); let theme = theme.map(|spanned| spanned.item);
@ -152,35 +153,35 @@ impl PluginCommand for Highlight {
fn examples(&self) -> Vec<Example<'_>> { fn examples(&self) -> Vec<Example<'_>> {
const fn example<'e>(description: &'e str, example: &'e str) -> Example<'e> const fn example<'e>(description: &'e str, example: &'e str) -> Example<'e>
where where
'e: 'static 'e: 'static,
{ {
Example { Example {
example, example,
description, description,
result: None result: None,
} }
} }
vec![ vec![
example( example(
"Highlight a Markdown file by guessing the type from the pipeline metadata", "Highlight a Markdown file by guessing the type from the pipeline metadata",
"open README.md | highlight" "open README.md | highlight",
), ),
example( example(
"Highlight a toml file by its file extension", "Highlight a toml file by its file extension",
"open Cargo.toml -r | echo $in | highlight toml" "open Cargo.toml -r | echo $in | highlight toml",
), ),
example( example(
"Highlight a rust file by programming language", "Highlight a rust file by programming language",
"open src/main.rs | echo $in | highlight Rust" "open src/main.rs | echo $in | highlight Rust",
), ),
example( example(
"Highlight a bash script by inferring the language (needs shebang)", "Highlight a bash script by inferring the language (needs shebang)",
"open example.sh | echo $in | highlight" "open example.sh | echo $in | highlight",
), ),
example( example(
"Highlight a toml file with another theme", "Highlight a toml file with another theme",
"open Cargo.toml -r | highlight -t ansi" "open Cargo.toml -r | highlight -t ansi",
), ),
example("List all available themes", "highlight --list-themes"), example("List all available themes", "highlight --list-themes"),
] ]
@ -189,7 +190,7 @@ impl PluginCommand for Highlight {
fn language_hint( fn language_hint(
call: &EvaluatedCall, call: &EvaluatedCall,
metadata: Option<&PipelineMetadata> metadata: Option<&PipelineMetadata>,
) -> Result<Option<String>, ShellError> { ) -> Result<Option<String>, ShellError> {
// first use passed argument // first use passed argument
let arg = call.opt(0)?.map(String::from_value).transpose()?; let arg = call.opt(0)?.map(String::from_value).transpose()?;
@ -206,15 +207,14 @@ fn language_hint(
"x-toml" => Some("toml".to_string()), "x-toml" => Some("toml".to_string()),
"x-nuscript" | "x-nushell" | "x-nuon" => Some("nushell".to_string()), "x-nuscript" | "x-nushell" | "x-nuon" => Some("nushell".to_string()),
s if s.starts_with("x-") => None, // we cannot be sure about this type, s if s.starts_with("x-") => None, // we cannot be sure about this type,
_ => Some(sub_type) _ => Some(sub_type),
} }
}; };
// as last resort, try to use the extension of data source // as last resort, try to use the extension of data source
let data_source = || -> Option<String> { let data_source = || -> Option<String> {
let data_source = &metadata?.data_source; let data_source = &metadata?.data_source;
let DataSource::FilePath(path) = data_source let DataSource::FilePath(path) = data_source else {
else {
return None; return None;
}; };
let extension = path.extension()?.to_string_lossy(); let extension = path.extension()?.to_string_lossy();
@ -229,20 +229,20 @@ fn labeled_error(
msg: impl ToString, msg: impl ToString,
label: impl ToString, label: impl ToString,
span: Span, span: Span,
inner: impl Into<Option<ShellError>> inner: impl Into<Option<ShellError>>,
) -> LabeledError { ) -> LabeledError {
LabeledError { LabeledError {
msg: msg.to_string(), msg: msg.to_string(),
labels: Box::new(vec![ErrorLabel { labels: Box::new(vec![ErrorLabel {
text: label.to_string(), text: label.to_string(),
span span,
}]), }]),
code: None, code: None,
url: None, url: None,
help: None, help: None,
inner: match inner.into() { inner: match inner.into() {
Some(inner) => Box::new(vec![inner.into()]), Some(inner) => Box::new(vec![inner.into()]),
None => Box::new(vec![]) None => Box::new(vec![]),
} },
} }
} }

View File

@ -6,7 +6,7 @@ pub struct ThemeDescription {
pub id: String, pub id: String,
pub name: Option<String>, pub name: Option<String>,
pub author: Option<String>, pub author: Option<String>,
pub default: bool pub default: bool,
} }
/// List of theme descriptions. /// List of theme descriptions.

View File

@ -1,11 +1,15 @@
# Nushell syntax highlight for sublime text # Nushell syntax highlight for sublime text
- Just copied matlab syntax file and modified it for nushell - Just copied matlab syntax file and modified it for nushell
- Use nushell lsp language server (need lsp sublime package) - Use nushell lsp language server (need lsp sublime package)
## Installation ## Installation
1. In sublime text install the LSP package. 1. In sublime text install the LSP package.
2. After cloning and cd-ing into the directory, in nushell run (tested in Ubuntu 20.04): 2. After cloning and cd-ing into the directory, in nushell run (tested in Ubuntu 20.04):
```nu
```nushell
nu
ls ls
| find -v README & export & color | find -v README & export & color
| get name | get name
@ -14,11 +18,14 @@ ls
cp -f $file (~/.config/sublime-text/Packages/User | path expand) cp -f $file (~/.config/sublime-text/Packages/User | path expand)
} }
``` ```
3. In sublime, open `Preferences > Customize Color Scheme` and add the contents of `sublime-color-scheme`. Modify to your liking. 1. In sublime, open `Preferences > Customize Color Scheme` and add the contents of `sublime-color-scheme`. Modify to your liking.
## Update commands ## Update commands
If you need to update nushell functions or add your custom commands and aliases, run: If you need to update nushell functions or add your custom commands and aliases, run:
```nu
```nushell
nu
chmod +x export.nu chmod +x export.nu
./export.nu ./export.nu
``` ```

View File

@ -90,12 +90,6 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -232,7 +226,7 @@ dependencies = [
"num-traits", "num-traits",
"pastey", "pastey",
"rayon", "rayon",
"thiserror 2.0.12", "thiserror 2.0.18",
"v_frame", "v_frame",
"y4m", "y4m",
] ]
@ -273,7 +267,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
"rustc-hash", "rustc-hash 1.1.0",
"shlex", "shlex",
"syn 2.0.104", "syn 2.0.104",
] ]
@ -432,18 +426,17 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.41" version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"pure-rust-locales", "pure-rust-locales",
"serde", "serde",
"wasm-bindgen", "wasm-bindgen",
"windows-link 0.1.3", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@ -486,6 +479,7 @@ dependencies = [
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim", "strsim",
"terminal_size",
] ]
[[package]] [[package]]
@ -597,7 +591,7 @@ dependencies = [
"document-features", "document-features",
"mio", "mio",
"parking_lot", "parking_lot",
"rustix 1.0.7", "rustix",
"signal-hook", "signal-hook",
"signal-hook-mio", "signal-hook-mio",
"winapi", "winapi",
@ -675,6 +669,17 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]] [[package]]
name = "doctest-file" name = "doctest-file"
version = "1.0.0" version = "1.0.0"
@ -768,9 +773,9 @@ dependencies = [
[[package]] [[package]]
name = "fancy-regex" name = "fancy-regex"
version = "0.16.2" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f" checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"regex-automata", "regex-automata",
@ -817,10 +822,55 @@ dependencies = [
] ]
[[package]] [[package]]
name = "foldhash" name = "fluent"
version = "0.1.5" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477"
dependencies = [
"fluent-bundle",
"unic-langid",
]
[[package]]
name = "fluent-bundle"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash 2.1.1",
"self_cell",
"smallvec",
"unic-langid",
]
[[package]]
name = "fluent-langneg"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0"
dependencies = [
"unic-langid",
]
[[package]]
name = "fluent-syntax"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [
"memchr",
"thiserror 2.0.18",
]
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
@ -885,9 +935,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.4" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@ -1033,12 +1083,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.11.4" version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.4", "hashbrown 0.16.1",
] ]
[[package]] [[package]]
@ -1054,9 +1104,9 @@ dependencies = [
[[package]] [[package]]
name = "interprocess" name = "interprocess"
version = "2.2.3" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69"
dependencies = [ dependencies = [
"doctest-file", "doctest-file",
"libc", "libc",
@ -1065,6 +1115,25 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "intl-memoizer"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
dependencies = [
"type-map",
"unic-langid",
]
[[package]]
name = "intl_pluralrules"
version = "7.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
dependencies = [
"unic-langid",
]
[[package]] [[package]]
name = "inventory" name = "inventory"
version = "0.3.20" version = "0.3.20"
@ -1176,9 +1245,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]] [[package]]
name = "libflate" name = "libflate"
@ -1251,12 +1320,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.9.4" version = "0.9.4"
@ -1296,11 +1359,11 @@ dependencies = [
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.12.5" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
dependencies = [ dependencies = [
"hashbrown 0.15.4", "hashbrown 0.16.1",
] ]
[[package]] [[package]]
@ -1315,12 +1378,9 @@ dependencies = [
[[package]] [[package]]
name = "mach2" name = "mach2"
version = "0.4.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "matrixmultiply" name = "matrixmultiply"
@ -1344,9 +1404,9 @@ dependencies = [
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.5" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]] [[package]]
name = "miette" name = "miette"
@ -1483,9 +1543,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-derive-value" name = "nu-derive-value"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465d2d3ada6004cb6689f269a08c70ba81056231e2b5392d1e0ccf5825f81cb" checksum = "d71958b54c367bda033f7dcc4a73b61972fb52323f71a1e3533e290fa67148d1"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error2", "proc-macro-error2",
@ -1496,9 +1556,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3b777faf7c5180fe5d7f67d83c44fd14138d91f2938a36494ed6ac66b7160f3" checksum = "d41b3e3e2d25c30741a0761856258e22624c0d60064e4f0e12f86202a451d492"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"log", "log",
@ -1511,25 +1571,25 @@ dependencies = [
[[package]] [[package]]
name = "nu-experimental" name = "nu-experimental"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73dd212a1afdad646a38c00579a0988264880aeb97fee820b349a28cdcc04df2" checksum = "f328fa0531bdf49c2dc0312b40cb780e3d74e0d3dbb15d508469a5ae4cfd8d8f"
dependencies = [ dependencies = [
"itertools 0.14.0", "itertools 0.14.0",
"thiserror 2.0.12", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "nu-glob" name = "nu-glob"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15aa2c17078926f14e393b4b708e69f228cb6fd4c81136839bde82772bdde1b5" checksum = "01ee787f61353c9c90581ddf4c0602a07b991cdd06c97dac8b6d323a1a52c43a"
[[package]] [[package]]
name = "nu-path" name = "nu-path"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde9d8ba26f62c07176c0237a36f38ce964ab3a0dcfb6aab1feea7515d1c6594" checksum = "c01d110cb931acf56237ce572e5b156e8e1134227c90deeffb92eedda9482c23"
dependencies = [ dependencies = [
"dirs", "dirs",
"omnipath", "omnipath",
@ -1539,9 +1599,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea1fbfd41b2f5c967675fc948831e03be67d91c6b8e18a60f3445113fe6548c" checksum = "c322531b1a7d6338c5ead1f454294f46babf8c99cd4716311cab1e88ba52b154"
dependencies = [ dependencies = [
"log", "log",
"nix", "nix",
@ -1550,14 +1610,14 @@ dependencies = [
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
"thiserror 2.0.12", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2410648c2c38cf9359595ffcf281d9d60a81c0580ff07f7c7d42bed414f3a1" checksum = "38ee792aeb0d37e0ed55ca4304e434eece497914e27ae42616a8bb973f5d2720"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"log", "log",
@ -1566,14 +1626,14 @@ dependencies = [
"rmp-serde", "rmp-serde",
"serde", "serde",
"serde_json", "serde_json",
"windows 0.62.2", "windows",
] ]
[[package]] [[package]]
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27de26da922261dff8103a811879228c55749a1b7b0e573b639c609a0651a01e" checksum = "7725f341428db16dbef4392970de32705abc77ee80a902572c8da811dade3564"
dependencies = [ dependencies = [
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
@ -1585,9 +1645,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038943300ca9de0924fef1c795a7dd16ffc67105629477cf163e8ee6bad95ea6" checksum = "f1c0e58cbeb46cbfd40156e6f4b9f90e4a77e774ca863fa158867a4726aab1d1"
dependencies = [ dependencies = [
"brotli", "brotli",
"bytes", "bytes",
@ -1616,18 +1676,18 @@ dependencies = [
"serde_json", "serde_json",
"strum", "strum",
"strum_macros", "strum_macros",
"thiserror 2.0.12", "thiserror 2.0.18",
"typetag", "typetag",
"web-time", "web-time",
"windows 0.62.2", "windows",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "nu-system" name = "nu-system"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46be734cc9b19e09a9665769e14360e13e6978490056ba5c8bfad7dd0537ea83" checksum = "62fe7847b65edbe362a0fcb67dedfab9fd7370e89c0313f7cb7d0a7ab8f9834b"
dependencies = [ dependencies = [
"chrono", "chrono",
"itertools 0.14.0", "itertools 0.14.0",
@ -1639,15 +1699,16 @@ dependencies = [
"ntapi", "ntapi",
"procfs", "procfs",
"sysinfo", "sysinfo",
"uucore",
"web-time", "web-time",
"windows 0.62.2", "windows",
] ]
[[package]] [[package]]
name = "nu-utils" name = "nu-utils"
version = "0.109.1" version = "0.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f8eb43c29cc5bce85f87defdadc2cca964fa434d808af37036a7cb78f3c68e9" checksum = "df85a8a4bb28c84d5f7c096c02c859ac454dfac59fd0296ab5eb6ed86619219e"
dependencies = [ dependencies = [
"byteyarn", "byteyarn",
"crossterm", "crossterm",
@ -1668,7 +1729,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_image" name = "nu_plugin_image"
version = "0.109.1" version = "0.111.0"
dependencies = [ dependencies = [
"ab_glyph", "ab_glyph",
"ansi_colours", "ansi_colours",
@ -1790,18 +1851,18 @@ dependencies = [
[[package]] [[package]]
name = "objc2-core-foundation" name = "objc2-core-foundation"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "objc2-io-kit" name = "objc2-io-kit"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
dependencies = [ dependencies = [
"libc", "libc",
"objc2-core-foundation", "objc2-core-foundation",
@ -1831,6 +1892,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_display"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5fd71b79026fb918650dde6d125000a233764f1c2f1659a1c71118e33ea08f"
dependencies = [
"unicode-width 0.2.1",
]
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.2.2" version = "1.2.2"
@ -1982,23 +2052,22 @@ dependencies = [
[[package]] [[package]]
name = "procfs" name = "procfs"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
"flate2", "flate2",
"hex",
"procfs-core", "procfs-core",
"rustix 0.38.44", "rustix",
] ]
[[package]] [[package]]
name = "procfs-core" name = "procfs-core"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
@ -2026,9 +2095,9 @@ dependencies = [
[[package]] [[package]]
name = "pure-rust-locales" name = "pure-rust-locales"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d"
[[package]] [[package]]
name = "pwd" name = "pwd"
@ -2178,7 +2247,7 @@ dependencies = [
"rand 0.9.2", "rand 0.9.2",
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"simd_helpers", "simd_helpers",
"thiserror 2.0.12", "thiserror 2.0.18",
"v_frame", "v_frame",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -2247,23 +2316,23 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [ dependencies = [
"getrandom 0.2.16", "getrandom 0.2.16",
"libredox", "libredox",
"thiserror 2.0.12", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "ref-cast" name = "ref-cast"
version = "1.0.24" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
dependencies = [ dependencies = [
"ref-cast-impl", "ref-cast-impl",
] ]
[[package]] [[package]]
name = "ref-cast-impl" name = "ref-cast-impl"
version = "1.0.24" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2327,11 +2396,10 @@ dependencies = [
[[package]] [[package]]
name = "rmp-serde" name = "rmp-serde"
version = "1.3.0" version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
dependencies = [ dependencies = [
"byteorder",
"rmp", "rmp",
"serde", "serde",
] ]
@ -2343,17 +2411,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustix" name = "rustc-hash"
version = "0.38.44" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
@ -2364,7 +2425,7 @@ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.9.4", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@ -2395,6 +2456,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "self_cell"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.26" version = "1.0.26"
@ -2433,14 +2500,15 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.140" version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu",
"serde", "serde",
"serde_core",
"zmij",
] ]
[[package]] [[package]]
@ -2574,9 +2642,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.26.3" version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
@ -2643,16 +2711,16 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.37.2" version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
"ntapi", "ntapi",
"objc2-core-foundation", "objc2-core-foundation",
"objc2-io-kit", "objc2-io-kit",
"windows 0.61.3", "windows",
] ]
[[package]] [[package]]
@ -2685,7 +2753,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
dependencies = [ dependencies = [
"rustix 1.0.7", "rustix",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@ -2710,11 +2778,11 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.12" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [ dependencies = [
"thiserror-impl 2.0.12", "thiserror-impl 2.0.18",
] ]
[[package]] [[package]]
@ -2730,9 +2798,9 @@ dependencies = [
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.12" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2793,12 +2861,32 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tinystr"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"serde_core",
"zerovec",
]
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.25.1" version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash 2.1.1",
]
[[package]] [[package]]
name = "typeid" name = "typeid"
version = "1.0.3" version = "1.0.3"
@ -2836,10 +2924,28 @@ dependencies = [
] ]
[[package]] [[package]]
name = "unicase" name = "unic-langid"
version = "2.8.1" version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
dependencies = [
"tinystr",
]
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -2877,6 +2983,35 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uucore"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b157ba598d7f7ed06f6dbc62999edb9d730b4d3fb58e503d8ad6d5fbe1e04391"
dependencies = [
"clap",
"fluent",
"fluent-bundle",
"fluent-syntax",
"libc",
"nix",
"os_display",
"thiserror 2.0.18",
"unic-langid",
"uucore_procs",
"wild",
]
[[package]]
name = "uucore_procs"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa291a52608ac5a2f8539e119666e021baa6b8c01f22f02ed201bbae54cbbc0"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "v_frame" name = "v_frame"
version = "0.3.9" version = "0.3.9"
@ -3018,6 +3153,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
[[package]]
name = "wild"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3131afc8c575281e1e80f36ed6a092aa502c08b18ed7524e86fbbb12bb410e1"
dependencies = [
"glob",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -3049,38 +3193,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections 0.2.0",
"windows-core 0.61.2",
"windows-future 0.2.1",
"windows-link 0.1.3",
"windows-numerics 0.2.0",
]
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.62.2" version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [ dependencies = [
"windows-collections 0.3.2", "windows-collections",
"windows-core 0.62.2", "windows-core 0.62.2",
"windows-future 0.3.2", "windows-future",
"windows-numerics 0.3.1", "windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.2",
] ]
[[package]] [[package]]
@ -3118,17 +3240,6 @@ dependencies = [
"windows-strings 0.5.1", "windows-strings 0.5.1",
] ]
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
"windows-threading 0.1.0",
]
[[package]] [[package]]
name = "windows-future" name = "windows-future"
version = "0.3.2" version = "0.3.2"
@ -3137,7 +3248,7 @@ checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [ dependencies = [
"windows-core 0.62.2", "windows-core 0.62.2",
"windows-link 0.2.1", "windows-link 0.2.1",
"windows-threading 0.2.1", "windows-threading",
] ]
[[package]] [[package]]
@ -3174,16 +3285,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-numerics" name = "windows-numerics"
version = "0.3.1" version = "0.3.1"
@ -3298,15 +3399,6 @@ dependencies = [
"windows_x86_64_msvc 0.53.0", "windows_x86_64_msvc 0.53.0",
] ]
[[package]]
name = "windows-threading"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-threading" name = "windows-threading"
version = "0.2.1" version = "0.2.1"
@ -3468,6 +3560,28 @@ dependencies = [
"syn 2.0.104", "syn 2.0.104",
] ]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
[[package]]
name = "zerovec"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"serde",
"zerofrom",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.13.3" version = "0.13.3"

View File

@ -11,8 +11,8 @@ vte = "0.15.0"
lazy_static = "1.5.0" lazy_static = "1.5.0"
slog-term = "2.9.2" slog-term = "2.9.2"
slog-async = "2.8.0" slog-async = "2.8.0"
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
[dependencies.clap] [dependencies.clap]
features = ["derive"] features = ["derive"]
@ -52,4 +52,4 @@ license = "MIT"
name = "nu_plugin_image" name = "nu_plugin_image"
readme = "README.md" readme = "README.md"
repository = "https://github.com/FMotalleb/nu_plugin_image" repository = "https://github.com/FMotalleb/nu_plugin_image"
version = "0.109.1" version = "0.111.0"

View File

@ -19,7 +19,8 @@ The `to png` command converts an ANSI string into a PNG image. Customizable font
#### 📌 Usage #### 📌 Usage
```bash ```nushell
bash
> to png {flags} (output-path) > to png {flags} (output-path)
``` ```
@ -55,7 +56,8 @@ The `to png` command converts an ANSI string into a PNG image. Customizable font
#### 📊 Example: Convert ANSI String to PNG with Custom Theme #### 📊 Example: Convert ANSI String to PNG with Custom Theme
```bash ```nushell
bash
> to png --theme "xterm" --custom-theme-fg "#FF00FF" --custom-theme-bg "#00000000" output.png > to png --theme "xterm" --custom-theme-fg "#FF00FF" --custom-theme-bg "#00000000" output.png
``` ```
@ -67,7 +69,8 @@ The `from png` command converts an image into its corresponding ANSI text repres
#### 📌 Usage #### 📌 Usage
```bash ```nushell
bash
> from png {flags} > from png {flags}
``` ```
@ -80,7 +83,8 @@ The `from png` command converts an image into its corresponding ANSI text repres
#### 📊 Example: Convert PNG Image to ANSI Text #### 📊 Example: Convert PNG Image to ANSI Text
```bash ```nushell
bash
> from png --width 80 --height 20 image.png > from png --width 80 --height 20 image.png
``` ```
@ -92,14 +96,16 @@ The `from png` command converts an image into its corresponding ANSI text repres
This method automatically handles dependencies and features. This method automatically handles dependencies and features.
```bash ```nushell
bash
git clone https://github.com/FMotalleb/nu_plugin_image.git git clone https://github.com/FMotalleb/nu_plugin_image.git
nupm install --path nu_plugin_image -f nupm install --path nu_plugin_image -f
``` ```
### 🛠️ Manual Compilation ### 🛠️ Manual Compilation
```bash ```nushell
bash
git clone https://github.com/FMotalleb/nu_plugin_image.git git clone https://github.com/FMotalleb/nu_plugin_image.git
cd nu_plugin_image cd nu_plugin_image
cargo build -r cargo build -r
@ -108,8 +114,8 @@ plugin add target/release/nu_plugin_image
### 📦 Install via Cargo (using git) ### 📦 Install via Cargo (using git)
```bash ```rust
bash
cargo install --git https://github.com/FMotalleb/nu_plugin_image.git cargo install --git https://github.com/FMotalleb/nu_plugin_image.git
plugin add ~/.cargo/bin/nu_plugin_image plugin add ~/.cargo/bin/nu_plugin_image
``` ```

View File

@ -73,7 +73,9 @@ fn load_u32(call: &EvaluatedCall, flag_name: &str) -> Result<u32, LabeledError>
.map_err(|e| make_params_err(e.to_string(), call.head))?; .map_err(|e| make_params_err(e.to_string(), call.head))?;
int_val int_val
.try_into() .try_into()
.map_err(|err: std::num::TryFromIntError| make_params_err(err.to_string(), call.head)) .map_err(|err: std::num::TryFromIntError| {
make_params_err(err.to_string(), call.head)
})
} }
_ => Err(make_params_err( _ => Err(make_params_err(
format!("value of `{flag_name}` is not an integer"), format!("value of `{flag_name}` is not an integer"),

@ -1 +1 @@
Subproject commit 11216da7174f88a7d5b3ce04fea3cd14bcde8d8c Subproject commit 97e7365a5a917cca6559ea8e66063a4242dc2605

697
nu_plugin_kms/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu_plugin_kms" name = "nu_plugin_kms"
version = "0.1.0" version = "0.111.0"
authors = ["Jesus Perez <jesus@librecloud.online>"] authors = ["Jesus Perez <jesus@librecloud.online>"]
edition = "2021" edition = "2021"
description = "Nushell plugin for KMS operations (RustyVault, Age, Cosmian)" description = "Nushell plugin for KMS operations (RustyVault, Age, Cosmian)"
@ -8,8 +8,8 @@ repository = "https://github.com/provisioning/nu_plugin_kms"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
nu-plugin = "0.109.1" nu-plugin = "0.111.0"
nu-protocol = "0.109.1" nu-protocol = "0.111.0"
rusty_vault = "0.2.1" rusty_vault = "0.2.1"
age = "0.11" age = "0.11"
base64 = "0.22" base64 = "0.22"
@ -32,6 +32,5 @@ default-features = false
version = "1.48" version = "1.48"
features = ["full"] features = ["full"]
[dev-dependencies.nu-plugin-test-support] [dev-dependencies]
version = "0.109.1" nu-plugin-test-support = "0.111.0"
path = "../nushell/crates/nu-plugin-test-support"

View File

@ -1,37 +0,0 @@
[package]
name = "nu_plugin_kms"
version = "0.1.0"
authors = ["Jesus Perez <jesus@librecloud.online>"]
edition = "2021"
description = "Nushell plugin for KMS operations (RustyVault, Age, Cosmian)"
repository = "https://github.com/provisioning/nu_plugin_kms"
license = "MIT"
[dependencies]
nu-plugin = "0.109.1"
nu-protocol = "0.109.1"
rusty_vault = "0.2.1"
age = "0.11"
base64 = "0.22"
serde_json = "1.0"
tempfile = "3.23"
[dependencies.serde]
version = "1.0"
features = ["derive"]
[dependencies.reqwest]
version = "0.12"
features = [
"json",
"rustls-tls",
]
default-features = false
[dependencies.tokio]
version = "1.48"
features = ["full"]
[dev-dependencies.nu-plugin-test-support]
version = "0.109.0"
path = "../nushell/crates/nu-plugin-test-support"

View File

@ -18,7 +18,8 @@ Nushell plugin for KMS operations with multiple backends.
## Installation ## Installation
```bash ```nushell
bash
cargo build --release cargo build --release
plugin add target/release/nu_plugin_kms plugin add target/release/nu_plugin_kms
``` ```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -182,7 +182,8 @@ mod tests {
#[test] #[test]
fn test_kms_error_with_source() { fn test_kms_error_with_source() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "key file not found"); let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "key file not found");
let error = KmsError::with_source(KmsErrorKind::KeyNotFound, "failed to load key", io_error); let error =
KmsError::with_source(KmsErrorKind::KeyNotFound, "failed to load key", io_error);
assert!(error.to_string().contains("caused by")); assert!(error.to_string().contains("caused by"));
} }

View File

@ -209,7 +209,8 @@ pub fn encrypt_age(data: &[u8], recipient_str: &str) -> Result<String, String> {
.parse::<age::x25519::Recipient>() .parse::<age::x25519::Recipient>()
.map_err(|e| format!("Invalid Age recipient: {}", e))?; .map_err(|e| format!("Invalid Age recipient: {}", e))?;
let encryptor = age::Encryptor::with_recipients(std::iter::once(&recipient as &dyn age::Recipient)) let encryptor =
age::Encryptor::with_recipients(std::iter::once(&recipient as &dyn age::Recipient))
.map_err(|_| "Failed to create Age encryptor".to_string())?; .map_err(|_| "Failed to create Age encryptor".to_string())?;
let mut encrypted = vec![]; let mut encrypted = vec![];
@ -513,9 +514,7 @@ pub async fn decrypt_aws_kms(_key_id: &str, ciphertext: &str) -> Result<Vec<u8>,
.await .await
.map_err(|e| format!("JSON parse error: {}", e))?; .map_err(|e| format!("JSON parse error: {}", e))?;
let plaintext_b64 = result["Plaintext"] let plaintext_b64 = result["Plaintext"].as_str().ok_or("Missing Plaintext")?;
.as_str()
.ok_or("Missing Plaintext")?;
general_purpose::STANDARD general_purpose::STANDARD
.decode(plaintext_b64) .decode(plaintext_b64)
@ -523,7 +522,10 @@ pub async fn decrypt_aws_kms(_key_id: &str, ciphertext: &str) -> Result<Vec<u8>,
} }
/// Generate data key using AWS KMS /// Generate data key using AWS KMS
pub async fn generate_data_key_aws(key_id: &str, key_spec: &str) -> Result<(String, String), String> { pub async fn generate_data_key_aws(
key_id: &str,
key_spec: &str,
) -> Result<(String, String), String> {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let kms_url = std::env::var("AWS_KMS_ENDPOINT") let kms_url = std::env::var("AWS_KMS_ENDPOINT")
@ -649,7 +651,10 @@ pub async fn generate_data_key_vault(
}; };
let response = client let response = client
.post(format!("{}/v1/transit/datakey/plaintext/{}", addr, key_name)) .post(format!(
"{}/v1/transit/datakey/plaintext/{}",
addr, key_name
))
.header("X-Vault-Token", token) .header("X-Vault-Token", token)
.json(&serde_json::json!({ .json(&serde_json::json!({
"bits": bits, "bits": bits,
@ -685,16 +690,12 @@ pub fn check_backend_available(backend_name: &str) -> bool {
"rustyvault" => { "rustyvault" => {
std::env::var("RUSTYVAULT_ADDR").is_ok() && std::env::var("RUSTYVAULT_TOKEN").is_ok() std::env::var("RUSTYVAULT_ADDR").is_ok() && std::env::var("RUSTYVAULT_TOKEN").is_ok()
} }
"age" => { "age" => std::env::var("AGE_RECIPIENT").is_ok() || std::env::var("AGE_IDENTITY").is_ok(),
std::env::var("AGE_RECIPIENT").is_ok() || std::env::var("AGE_IDENTITY").is_ok()
}
"aws" => { "aws" => {
std::env::var("AWS_ACCESS_KEY_ID").is_ok() std::env::var("AWS_ACCESS_KEY_ID").is_ok()
&& std::env::var("AWS_SECRET_ACCESS_KEY").is_ok() && std::env::var("AWS_SECRET_ACCESS_KEY").is_ok()
} }
"vault" => { "vault" => std::env::var("VAULT_ADDR").is_ok() && std::env::var("VAULT_TOKEN").is_ok(),
std::env::var("VAULT_ADDR").is_ok() && std::env::var("VAULT_TOKEN").is_ok()
}
"cosmian" => std::env::var("KMS_HTTP_URL").is_ok(), "cosmian" => std::env::var("KMS_HTTP_URL").is_ok(),
_ => false, _ => false,
} }

View File

@ -169,14 +169,15 @@ impl SimplePluginCommand for KmsEncrypt {
helpers::encrypt_age(data.as_bytes(), recipient) helpers::encrypt_age(data.as_bytes(), recipient)
.map_err(|e| LabeledError::new(e))? .map_err(|e| LabeledError::new(e))?
} }
helpers::Backend::AwsKms { ref key_id } => { helpers::Backend::AwsKms { ref key_id } => runtime.block_on(async {
runtime.block_on(async {
helpers::encrypt_aws_kms(key_id, data.as_bytes()) helpers::encrypt_aws_kms(key_id, data.as_bytes())
.await .await
.map_err(|e| LabeledError::new(e)) .map_err(|e| LabeledError::new(e))
})? })?,
} helpers::Backend::Vault {
helpers::Backend::Vault { ref addr, ref token } => { ref addr,
ref token,
} => {
let key_name = key.unwrap_or_else(|| "provisioning-main".to_string()); let key_name = key.unwrap_or_else(|| "provisioning-main".to_string());
runtime.block_on(async { runtime.block_on(async {
helpers::encrypt_vault(addr, token, &key_name, data.as_bytes()) helpers::encrypt_vault(addr, token, &key_name, data.as_bytes())
@ -321,14 +322,15 @@ impl SimplePluginCommand for KmsDecrypt {
})?; })?;
helpers::decrypt_age(&encrypted, identity_path).map_err(|e| LabeledError::new(e))? helpers::decrypt_age(&encrypted, identity_path).map_err(|e| LabeledError::new(e))?
} }
helpers::Backend::AwsKms { ref key_id } => { helpers::Backend::AwsKms { ref key_id } => runtime.block_on(async {
runtime.block_on(async {
helpers::decrypt_aws_kms(key_id, &encrypted) helpers::decrypt_aws_kms(key_id, &encrypted)
.await .await
.map_err(|e| LabeledError::new(e)) .map_err(|e| LabeledError::new(e))
})? })?,
} helpers::Backend::Vault {
helpers::Backend::Vault { ref addr, ref token } => { ref addr,
ref token,
} => {
let key_name = key.unwrap_or_else(|| "provisioning-main".to_string()); let key_name = key.unwrap_or_else(|| "provisioning-main".to_string());
runtime.block_on(async { runtime.block_on(async {
helpers::decrypt_vault(addr, token, &key_name, &encrypted) helpers::decrypt_vault(addr, token, &key_name, &encrypted)
@ -463,20 +465,19 @@ impl SimplePluginCommand for KmsGenerateKey {
.map(|(secret, public)| (secret, public)) .map(|(secret, public)| (secret, public))
.map_err(|e| LabeledError::new(e))? .map_err(|e| LabeledError::new(e))?
} }
helpers::Backend::AwsKms { ref key_id } => { helpers::Backend::AwsKms { ref key_id } => runtime.block_on(async {
runtime.block_on(async {
helpers::generate_data_key_aws(key_id, &key_spec) helpers::generate_data_key_aws(key_id, &key_spec)
.await .await
.map_err(|e| LabeledError::new(e)) .map_err(|e| LabeledError::new(e))
})? })?,
} helpers::Backend::Vault {
helpers::Backend::Vault { ref addr, ref token } => { ref addr,
runtime.block_on(async { ref token,
} => runtime.block_on(async {
helpers::generate_data_key_vault(addr, token, "provisioning-main", &key_spec) helpers::generate_data_key_vault(addr, token, "provisioning-main", &key_spec)
.await .await
.map_err(|e| LabeledError::new(e)) .map_err(|e| LabeledError::new(e))
})? })?,
}
helpers::Backend::HttpFallback { helpers::Backend::HttpFallback {
ref backend_name, ref backend_name,
ref url, ref url,
@ -560,12 +561,8 @@ impl SimplePluginCommand for KmsStatus {
format!("recipient: {}, identity: {}", recipient, identity_status), format!("recipient: {}, identity: {}", recipient, identity_status),
) )
} }
helpers::Backend::AwsKms { ref key_id } => { helpers::Backend::AwsKms { ref key_id } => ("aws", true, format!("key_id: {}", key_id)),
("aws", true, format!("key_id: {}", key_id)) helpers::Backend::Vault { ref addr, .. } => ("vault", true, format!("addr: {}", addr)),
}
helpers::Backend::Vault { ref addr, .. } => {
("vault", true, format!("addr: {}", addr))
}
helpers::Backend::HttpFallback { helpers::Backend::HttpFallback {
ref backend_name, ref backend_name,
ref url, ref url,
@ -624,11 +621,31 @@ impl SimplePluginCommand for KmsListBackends {
_input: &Value, _input: &Value,
) -> Result<Value, LabeledError> { ) -> Result<Value, LabeledError> {
let backends = vec![ let backends = vec![
("rustyvault", "RustyVault Transit backend", "RUSTYVAULT_ADDR, RUSTYVAULT_TOKEN"), (
("age", "Age file-based encryption", "AGE_RECIPIENT, AGE_IDENTITY"), "rustyvault",
("aws", "AWS Key Management Service", "AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION"), "RustyVault Transit backend",
("vault", "HashiCorp Vault Transit", "VAULT_ADDR, VAULT_TOKEN"), "RUSTYVAULT_ADDR, RUSTYVAULT_TOKEN",
("cosmian", "Cosmian privacy-preserving encryption", "KMS_HTTP_URL"), ),
(
"age",
"Age file-based encryption",
"AGE_RECIPIENT, AGE_IDENTITY",
),
(
"aws",
"AWS Key Management Service",
"AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION",
),
(
"vault",
"HashiCorp Vault Transit",
"VAULT_ADDR, VAULT_TOKEN",
),
(
"cosmian",
"Cosmian privacy-preserving encryption",
"KMS_HTTP_URL",
),
]; ];
let backend_values: Vec<Value> = backends let backend_values: Vec<Value> = backends

View File

@ -24,11 +24,26 @@ fn test_kms_error_with_source() {
#[test] #[test]
fn test_kms_error_kind_display() { fn test_kms_error_kind_display() {
assert_eq!(KmsErrorKind::BackendNotAvailable.to_string(), "backend not available"); assert_eq!(
assert_eq!(KmsErrorKind::EncryptionFailed.to_string(), "encryption failed"); KmsErrorKind::BackendNotAvailable.to_string(),
assert_eq!(KmsErrorKind::DecryptionFailed.to_string(), "decryption failed"); "backend not available"
assert_eq!(KmsErrorKind::KeyGenerationFailed.to_string(), "key generation failed"); );
assert_eq!(KmsErrorKind::InvalidKeySpec.to_string(), "invalid key specification"); assert_eq!(
KmsErrorKind::EncryptionFailed.to_string(),
"encryption failed"
);
assert_eq!(
KmsErrorKind::DecryptionFailed.to_string(),
"decryption failed"
);
assert_eq!(
KmsErrorKind::KeyGenerationFailed.to_string(),
"key generation failed"
);
assert_eq!(
KmsErrorKind::InvalidKeySpec.to_string(),
"invalid key specification"
);
assert_eq!(KmsErrorKind::NetworkError.to_string(), "network error"); assert_eq!(KmsErrorKind::NetworkError.to_string(), "network error");
} }
@ -71,10 +86,7 @@ fn test_backend_new_age_invalid() {
fn test_backend_http_fallback() { fn test_backend_http_fallback() {
let backend = helpers::Backend::new_http_fallback("cosmian", "http://localhost:8081"); let backend = helpers::Backend::new_http_fallback("cosmian", "http://localhost:8081");
match backend { match backend {
helpers::Backend::HttpFallback { helpers::Backend::HttpFallback { backend_name, url } => {
backend_name,
url,
} => {
assert_eq!(backend_name, "cosmian"); assert_eq!(backend_name, "cosmian");
assert_eq!(url, "http://localhost:8081"); assert_eq!(url, "http://localhost:8081");
} }

View File

@ -7,7 +7,8 @@
### 1. Verify Binary Exists ### 1. Verify Binary Exists
```bash ```nushell
bash
cd /Users/Akasha/project-provisioning/provisioning/core/plugins/nushell-plugins/nu_plugin_kms cd /Users/Akasha/project-provisioning/provisioning/core/plugins/nushell-plugins/nu_plugin_kms
# Check binary exists # Check binary exists
@ -19,7 +20,8 @@ ls -lh target/release/nu_plugin_kms
### 2. Register Plugin with Nushell ### 2. Register Plugin with Nushell
```bash ```nushell
bash
# Register plugin # Register plugin
nu -c "plugin add target/release/nu_plugin_kms" nu -c "plugin add target/release/nu_plugin_kms"
@ -35,7 +37,8 @@ nu -c "plugin list | where name =~ kms"
### 3. Test Plugin Help ### 3. Test Plugin Help
```bash ```nushell
bash
# Check command help # Check command help
nu -c "kms encrypt --help" nu -c "kms encrypt --help"
nu -c "kms decrypt --help" nu -c "kms decrypt --help"
@ -49,7 +52,8 @@ nu -c "kms status --help"
#### Setup #### Setup
```bash ```nushell
bash
# Install Age (if not already installed) # Install Age (if not already installed)
brew install age # macOS brew install age # macOS
# or # or
@ -68,7 +72,8 @@ echo "Age Identity: $AGE_IDENTITY"
#### Test Encryption #### Test Encryption
```bash ```nushell
bash
# Test 1: Encrypt with Age # Test 1: Encrypt with Age
nu -c "kms encrypt 'Hello, Age!' --backend age --key $AGE_RECIPIENT" > /tmp/encrypted.txt nu -c "kms encrypt 'Hello, Age!' --backend age --key $AGE_RECIPIENT" > /tmp/encrypted.txt
@ -80,7 +85,8 @@ cat /tmp/encrypted.txt
#### Test Decryption #### Test Decryption
```bash ```nushell
bash
# Test 2: Decrypt with Age # Test 2: Decrypt with Age
nu -c "kms decrypt '$(cat /tmp/encrypted.txt)' --backend age --key $AGE_IDENTITY" nu -c "kms decrypt '$(cat /tmp/encrypted.txt)' --backend age --key $AGE_IDENTITY"
@ -89,7 +95,8 @@ nu -c "kms decrypt '$(cat /tmp/encrypted.txt)' --backend age --key $AGE_IDENTITY
#### Test Key Generation #### Test Key Generation
```bash ```nushell
bash
# Test 3: Generate new Age key pair # Test 3: Generate new Age key pair
nu -c "kms generate-key --backend age" nu -c "kms generate-key --backend age"
@ -102,7 +109,8 @@ nu -c "kms generate-key --backend age"
#### Test Auto-Detection #### Test Auto-Detection
```bash ```nushell
bash
# Test 4: Auto-detect Age backend # Test 4: Auto-detect Age backend
nu -c "kms status" nu -c "kms status"
@ -118,7 +126,8 @@ nu -c "kms status"
#### Setup #### Setup
```bash ```nushell
bash
# Start RustyVault in Docker # Start RustyVault in Docker
docker run -d --name rustyvault \ docker run -d --name rustyvault \
-p 8200:8200 \ -p 8200:8200 \
@ -143,7 +152,8 @@ docker exec rustyvault \
#### Test Encryption #### Test Encryption
```bash ```nushell
bash
# Test 1: Encrypt with RustyVault # Test 1: Encrypt with RustyVault
nu -c "kms encrypt 'Secret data!' --backend rustyvault --key provisioning-main" nu -c "kms encrypt 'Secret data!' --backend rustyvault --key provisioning-main"
@ -152,7 +162,8 @@ nu -c "kms encrypt 'Secret data!' --backend rustyvault --key provisioning-main"
#### Test Decryption #### Test Decryption
```bash ```nushell
bash
# Test 2: Encrypt and then decrypt # Test 2: Encrypt and then decrypt
ENCRYPTED=$(nu -c "kms encrypt 'RustyVault test' --backend rustyvault --key provisioning-main") ENCRYPTED=$(nu -c "kms encrypt 'RustyVault test' --backend rustyvault --key provisioning-main")
@ -163,7 +174,8 @@ nu -c "kms decrypt '$ENCRYPTED' --backend rustyvault --key provisioning-main"
#### Test Key Generation #### Test Key Generation
```bash ```nushell
bash
# Test 3: Generate AES256 data key # Test 3: Generate AES256 data key
nu -c "kms generate-key --backend rustyvault --spec AES256" nu -c "kms generate-key --backend rustyvault --spec AES256"
@ -176,7 +188,8 @@ nu -c "kms generate-key --backend rustyvault --spec AES256"
#### Test Status #### Test Status
```bash ```nushell
bash
# Test 4: Check RustyVault status # Test 4: Check RustyVault status
nu -c "kms status" nu -c "kms status"
@ -190,7 +203,8 @@ nu -c "kms status"
#### Cleanup #### Cleanup
```bash ```nushell
bash
# Stop and remove RustyVault container # Stop and remove RustyVault container
docker stop rustyvault docker stop rustyvault
docker rm rustyvault docker rm rustyvault
@ -200,7 +214,8 @@ docker rm rustyvault
#### Setup (Mock Server) #### Setup (Mock Server)
```bash ```nushell
bash
# Create simple mock KMS server with Python # Create simple mock KMS server with Python
cat > /tmp/mock_kms_server.py << 'EOF' cat > /tmp/mock_kms_server.py << 'EOF'
#!/usr/bin/env python3 #!/usr/bin/env python3
@ -262,7 +277,8 @@ export KMS_HTTP_BACKEND="mock"
#### Test HTTP Backend #### Test HTTP Backend
```bash ```nushell
bash
# Test 1: Encrypt # Test 1: Encrypt
nu -c "kms encrypt 'HTTP test data' --backend mock" nu -c "kms encrypt 'HTTP test data' --backend mock"
@ -279,7 +295,8 @@ nu -c "kms status"
#### Cleanup #### Cleanup
```bash ```nushell
bash
# Stop mock server # Stop mock server
kill $MOCK_SERVER_PID kill $MOCK_SERVER_PID
``` ```
@ -288,7 +305,8 @@ kill $MOCK_SERVER_PID
### Test Auto-Detection Priority ### Test Auto-Detection Priority
```bash ```nushell
bash
# Test 1: RustyVault has priority # Test 1: RustyVault has priority
export RUSTYVAULT_ADDR="http://localhost:8200" export RUSTYVAULT_ADDR="http://localhost:8200"
export RUSTYVAULT_TOKEN="test-token" export RUSTYVAULT_TOKEN="test-token"
@ -313,7 +331,8 @@ nu -c "kms status"
### Test Error Handling ### Test Error Handling
```bash ```nushell
bash
# Test 1: Missing required flags # Test 1: Missing required flags
nu -c "kms encrypt 'data' --backend age" nu -c "kms encrypt 'data' --backend age"
# Expected: Error about missing --key recipient # Expected: Error about missing --key recipient
@ -334,7 +353,8 @@ nu -c "kms generate-key --backend rustyvault --spec INVALID"
### Test Binary Data ### Test Binary Data
```bash ```nushell
bash
# Test handling of non-UTF8 data # Test handling of non-UTF8 data
dd if=/dev/urandom bs=1024 count=1 | base64 > /tmp/random.b64 dd if=/dev/urandom bs=1024 count=1 | base64 > /tmp/random.b64
@ -356,7 +376,8 @@ fi
### Age Performance ### Age Performance
```bash ```nushell
bash
# Benchmark encryption (1000 iterations) # Benchmark encryption (1000 iterations)
time for i in {1..1000}; do time for i in {1..1000}; do
nu -c "kms encrypt 'test data' --backend age --key $AGE_RECIPIENT" > /dev/null nu -c "kms encrypt 'test data' --backend age --key $AGE_RECIPIENT" > /dev/null
@ -367,7 +388,8 @@ done
### RustyVault Performance ### RustyVault Performance
```bash ```rust
bash
# Benchmark encryption (1000 iterations) # Benchmark encryption (1000 iterations)
time for i in {1..1000}; do time for i in {1..1000}; do
nu -c "kms encrypt 'test data' --backend rustyvault --key provisioning-main" > /dev/null nu -c "kms encrypt 'test data' --backend rustyvault --key provisioning-main" > /dev/null
@ -378,7 +400,8 @@ done
### Memory Usage ### Memory Usage
```bash ```nushell
bash
# Monitor memory during operations # Monitor memory during operations
while true; do while true; do
nu -c "kms encrypt 'test data' --backend age --key $AGE_RECIPIENT" > /dev/null nu -c "kms encrypt 'test data' --backend age --key $AGE_RECIPIENT" > /dev/null
@ -392,17 +415,20 @@ done
## Verification Checklist ## Verification Checklist
### Compilation ### Compilation
- [x] `cargo check` passes - [x] `cargo check` passes
- [x] `cargo build --release` succeeds - [x] `cargo build --release` succeeds
- [x] Binary created in `target/release/nu_plugin_kms` - [x] Binary created in `target/release/nu_plugin_kms`
- [x] File size reasonable (< 50MB) - [x] File size reasonable (< 50MB)
### Plugin Registration ### Plugin Registration
- [ ] Plugin registers with Nushell - [ ] Plugin registers with Nushell
- [ ] All 4 commands visible in `plugin list` - [ ] All 4 commands visible in `plugin list`
- [ ] Help text accessible for each command - [ ] Help text accessible for each command
### Age Backend ### Age Backend
- [ ] Encryption works with recipient - [ ] Encryption works with recipient
- [ ] Decryption works with identity file - [ ] Decryption works with identity file
- [ ] Key generation produces valid key pair - [ ] Key generation produces valid key pair
@ -410,6 +436,7 @@ done
- [ ] Auto-detection works when env vars set - [ ] Auto-detection works when env vars set
### RustyVault Backend ### RustyVault Backend
- [ ] Encryption works with Transit engine - [ ] Encryption works with Transit engine
- [ ] Decryption works correctly - [ ] Decryption works correctly
- [ ] Data key generation works - [ ] Data key generation works
@ -417,6 +444,7 @@ done
- [ ] Auto-detection works when env vars set - [ ] Auto-detection works when env vars set
### HTTP Fallback ### HTTP Fallback
- [ ] Encryption works with HTTP service - [ ] Encryption works with HTTP service
- [ ] Decryption works correctly - [ ] Decryption works correctly
- [ ] Data key generation works - [ ] Data key generation works
@ -424,12 +452,14 @@ done
- [ ] Auto-detection works as fallback - [ ] Auto-detection works as fallback
### Error Handling ### Error Handling
- [ ] Missing flags produce clear errors - [ ] Missing flags produce clear errors
- [ ] Invalid inputs rejected gracefully - [ ] Invalid inputs rejected gracefully
- [ ] Network errors handled properly - [ ] Network errors handled properly
- [ ] Missing env vars reported clearly - [ ] Missing env vars reported clearly
### Integration ### Integration
- [ ] Auto-detection priority correct - [ ] Auto-detection priority correct
- [ ] Multiple backends can coexist - [ ] Multiple backends can coexist
- [ ] Environment switching works - [ ] Environment switching works
@ -438,21 +468,25 @@ done
## Success Criteria ## Success Criteria
✅ **Basic Functionality** ✅ **Basic Functionality**
- All backends encrypt and decrypt successfully - All backends encrypt and decrypt successfully
- Key generation works for all backends - Key generation works for all backends
- Status command reports correctly - Status command reports correctly
✅ **Robustness** ✅ **Robustness**
- Error messages are clear and actionable - Error messages are clear and actionable
- No panics or crashes - No panics or crashes
- Memory usage is stable - Memory usage is stable
✅ **Performance** ✅ **Performance**
- Operations complete in reasonable time - Operations complete in reasonable time
- No memory leaks - No memory leaks
- Concurrent operations work - Concurrent operations work
✅ **Usability** ✅ **Usability**
- Auto-detection works as expected - Auto-detection works as expected
- Environment configuration is straightforward - Environment configuration is straightforward
- Help text is clear - Help text is clear
@ -461,7 +495,8 @@ done
### Plugin Not Loading ### Plugin Not Loading
```bash ```nushell
bash
# Check plugin is registered # Check plugin is registered
nu -c "plugin list" | grep kms nu -c "plugin list" | grep kms
@ -474,7 +509,8 @@ nu -c "plugin list --version"
### Environment Variables Not Working ### Environment Variables Not Working
```bash ```nushell
bash
# Check env vars are set # Check env vars are set
env | grep -E '(RUSTYVAULT|AGE|KMS)' env | grep -E '(RUSTYVAULT|AGE|KMS)'
@ -484,7 +520,8 @@ bash -c 'export AGE_RECIPIENT=...; nu -c "kms status"'
### Compilation Errors ### Compilation Errors
```bash ```nushell
bash
# Clean build # Clean build
cargo clean cargo clean

2242
nu_plugin_mcp/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

27
nu_plugin_mcp/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[[bin]]
name = "nu_plugin_mcp"
path = "src/main.rs"
[lib]
name = "nu_plugin_mcp"
path = "src/lib.rs"
[package]
name = "nu_plugin_mcp"
version = "0.111.0"
edition = "2021"
description = "Nushell plugin for MCP server interaction — connect, discover, and call provisioning tools"
authors = ["Provisioning Team"]
license = "MIT"
[dependencies]
serde_json = "1.0"
thiserror = "2.0"
interprocess = "^2.3.1"
nu-plugin = "0.111.0"
nu-protocol = "0.111.0"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1

View File

@ -0,0 +1,12 @@
#!/usr/bin/env nu
# Validate infrastructure config via MCP and display results
mcp connect provisioning-mcp-server --provisioning-path /opt/provisioning
let result = mcp tool call guidance_validate_config_file --payload {
config_path: "provisioning/config/workspace.ncl"
}
print $result
mcp disconnect

View File

@ -0,0 +1,12 @@
#!/usr/bin/env nu
# Search provisioning documentation via RAG
mcp connect provisioning-mcp-server --provisioning-path /opt/provisioning
let docs = mcp tool call guidance_find_docs --payload {
query: "nickel validation type-safe configuration"
}
print $docs
mcp disconnect

View File

@ -0,0 +1,8 @@
#!/usr/bin/env nu
# Discover all available MCP tools and display as a table
mcp connect provisioning-mcp-server --provisioning-path /opt/provisioning
mcp tools list | select name description | sort-by name | table
mcp disconnect

View File

@ -0,0 +1,192 @@
use crate::McpPlugin;
use nu_plugin::{EvaluatedCall, SimplePluginCommand};
use nu_protocol::{
Category, Example, LabeledError, Record, Signature, Span, SyntaxShape, Type, Value,
};
use serde_json::Value as Json;
#[derive(Debug)]
pub struct McpToolCall;
impl SimplePluginCommand for McpToolCall {
type Plugin = McpPlugin;
fn name(&self) -> &str {
"mcp tool call"
}
fn description(&self) -> &str {
"Call a tool on the connected MCP server"
}
fn signature(&self) -> Signature {
Signature::build("mcp tool call")
.input_output_type(Type::Nothing, Type::Any)
.required(
"name",
SyntaxShape::String,
"Tool name (e.g. provision_status)",
)
.named(
"payload",
SyntaxShape::Record(vec![]),
"Arguments to pass to the tool as a record",
Some('a'),
)
.category(Category::Custom("provisioning".into()))
}
fn examples(&self) -> Vec<Example<'_>> {
vec![
Example {
example: "mcp tool call provision_status",
description: "Get provisioning infrastructure status",
result: None,
},
Example {
example: "mcp tool call provision_query --payload {query: \"list all servers\"}",
description: "Query infrastructure using natural language",
result: None,
},
Example {
example:
"mcp tool call guidance_find_docs --payload {query: \"nickel validation\"}",
description: "Find relevant documentation",
result: None,
},
]
}
fn run(
&self,
plugin: &McpPlugin,
_engine: &nu_plugin::EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
let tool_name: String = call.req(0)?;
let payload_val: Option<Value> = call.get_flag("payload")?;
let span = call.head;
let arguments = match payload_val {
Some(v) => nu_record_to_json(&v)
.map_err(|e| LabeledError::new(format!("payload conversion: {e}")))?,
None => Json::Object(Default::default()),
};
let mut guard = plugin
.session
.lock()
.map_err(|_| LabeledError::new("session mutex poisoned"))?;
let session = guard
.as_mut()
.ok_or_else(|| LabeledError::new("not connected — run `mcp connect <binary>` first"))?;
let result = session
.tool_call(&tool_name, arguments)
.map_err(|e| LabeledError::new(e.to_string()))?;
Ok(mcp_result_to_nu(result, span))
}
}
/// Convert MCP tool result to a Nu value.
/// MCP spec: result = { content: [{type: "text", text: "..."}, ...], isError?: bool }
fn mcp_result_to_nu(result: Json, span: Span) -> Value {
let is_error = result
.get("isError")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let text = result
.get("content")
.and_then(|c| c.as_array())
.and_then(|arr| arr.first())
.and_then(|item| item.get("text"))
.and_then(|t| t.as_str())
.unwrap_or("")
.to_owned();
if is_error {
let mut rec = Record::new();
rec.push("error", Value::bool(true, span));
rec.push("message", Value::string(text, span));
Value::record(rec, span)
} else {
// Try to parse text as JSON for richer output; fall back to plain string
if let Ok(json_val) = serde_json::from_str::<Json>(&text) {
json_to_nu_value(json_val, span)
} else {
Value::string(text, span)
}
}
}
fn json_to_nu_value(v: Json, span: Span) -> Value {
match v {
Json::Null => Value::nothing(span),
Json::Bool(b) => Value::bool(b, span),
Json::Number(n) => {
if let Some(i) = n.as_i64() {
Value::int(i, span)
} else {
Value::float(n.as_f64().unwrap_or(0.0), span)
}
}
Json::String(s) => Value::string(s, span),
Json::Array(arr) => {
let vals = arr.into_iter().map(|v| json_to_nu_value(v, span)).collect();
Value::list(vals, span)
}
Json::Object(map) => {
let mut rec = Record::new();
for (k, v) in map {
rec.push(k, json_to_nu_value(v, span));
}
Value::record(rec, span)
}
}
}
fn nu_record_to_json(val: &Value) -> Result<Json, String> {
match val {
Value::Nothing { .. } => Ok(Json::Object(Default::default())),
Value::Record { val, .. } => {
let mut map = serde_json::Map::new();
for (k, v) in val.iter() {
map.insert(k.to_owned(), nu_value_to_json(v)?);
}
Ok(Json::Object(map))
}
_ => Err(format!(
"payload must be a record, got {:?}",
val.get_type()
)),
}
}
fn nu_value_to_json(val: &Value) -> Result<Json, String> {
match val {
Value::Nothing { .. } => Ok(Json::Null),
Value::Bool { val, .. } => Ok(Json::Bool(*val)),
Value::Int { val, .. } => Ok(Json::Number((*val).into())),
Value::Float { val, .. } => serde_json::Number::from_f64(*val)
.map(Json::Number)
.ok_or_else(|| format!("non-finite float: {val}")),
Value::String { val, .. } => Ok(Json::String(val.clone())),
Value::List { vals, .. } => {
let arr: Result<Vec<_>, _> = vals.iter().map(nu_value_to_json).collect();
Ok(Json::Array(arr?))
}
Value::Record { val, .. } => {
let mut map = serde_json::Map::new();
for (k, v) in val.iter() {
map.insert(k.to_owned(), nu_value_to_json(v)?);
}
Ok(Json::Object(map))
}
other => Ok(Json::String(
other.to_expanded_string(", ", &Default::default()),
)),
}
}

View File

@ -0,0 +1,91 @@
use crate::session::McpSession;
use crate::McpPlugin;
use nu_plugin::{EvaluatedCall, SimplePluginCommand};
use nu_protocol::{
Category, Example, LabeledError, Record, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Debug)]
pub struct McpConnect;
impl SimplePluginCommand for McpConnect {
type Plugin = McpPlugin;
fn name(&self) -> &str {
"mcp connect"
}
fn description(&self) -> &str {
"Connect to an MCP server by spawning a binary as a child process"
}
fn signature(&self) -> Signature {
Signature::build("mcp connect")
.input_output_type(Type::Nothing, Type::Record(vec![].into()))
.required(
"binary",
SyntaxShape::String,
"Path to the MCP server binary or command name on PATH",
)
.named(
"provisioning-path",
SyntaxShape::Filepath,
"Set PROVISIONING_PATH env var for the spawned server",
Some('p'),
)
.category(Category::Custom("provisioning".into()))
}
fn examples(&self) -> Vec<Example<'_>> {
vec![
Example {
example: "mcp connect provisioning-mcp-server",
description: "Connect to the provisioning MCP server on PATH",
result: None,
},
Example {
example: "mcp connect ./target/release/provisioning-mcp-server --provisioning-path /opt/provisioning",
description: "Connect with explicit provisioning path",
result: None,
},
]
}
fn run(
&self,
plugin: &McpPlugin,
_engine: &nu_plugin::EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
let binary: String = call.req(0)?;
let provisioning_path: Option<String> = call.get_flag("provisioning-path")?;
let span = call.head;
let mut guard = plugin
.session
.lock()
.map_err(|_| LabeledError::new("session mutex poisoned"))?;
if guard.is_some() {
return Err(LabeledError::new(
"already connected — run `mcp disconnect` before reconnecting",
));
}
let session = McpSession::connect(&binary, provisioning_path.as_deref())
.map_err(|e| LabeledError::new(e.to_string()))?;
*guard = Some(session);
drop(guard);
Ok(connected_record(&binary, span))
}
}
fn connected_record(binary: &str, span: Span) -> Value {
let mut rec = Record::new();
rec.push("status", Value::string("connected", span));
rec.push("binary", Value::string(binary, span));
Value::record(rec, span)
}

Some files were not shown because too many files have changed in this diff Show More