This commit is contained in:
2026-05-16 15:26:08 -07:00
parent 5048729b0c
commit 02d84f423e
10 changed files with 488 additions and 470 deletions
+31 -53
View File
@@ -1,6 +1,6 @@
# FTP Plugin Walkthrough
This walkthrough adds a real bundled `ftp` plugin so users can:
This walkthrough covers the bundled `ftp` plugin. It lets users:
- run `search-file -plugin ftp -instance <name> ...`
- browse remote folders as result tables
@@ -10,9 +10,10 @@ This walkthrough adds a real bundled `ftp` plugin so users can:
The implementation lives in [plugins/ftp/__init__.py](plugins/ftp/__init__.py).
## What The Plugin Does
## What the plugin does
The FTP plugin demonstrates the main provider hooks that matter for a storage-style integration:
The FTP plugin demonstrates the main plugin hooks that matter for a
storage-style integration:
- `config_schema()` exposes host, credentials, base path, TLS, and search depth.
- `extract_query_arguments()` supports inline query fields like `path:` and `depth:`.
@@ -22,9 +23,10 @@ The FTP plugin demonstrates the main provider hooks that matter for a storage-st
- `resolve_pipe_result_download()` lets `@N | add-file -instance ...` materialize a remote FTP file first.
- `upload()` lets `add-file -plugin ftp -instance <name> -path ...` push a local file to the configured FTP server.
## Example Config
## Example config
Add one or more named FTP provider instances to your config:
Add one or more named FTP plugin instances to your config. The current stored
key path remains `provider.ftp.<instance>` for legacy compatibility:
```toml
[provider.ftp.work]
@@ -48,121 +50,97 @@ tls = true
```
Notes:
- `work` and `archive` are instance names; use them with `-instance work` or `-instance archive`.
- `work` and `archive` are instance names.
- `host` is the only required field for each instance to validate.
- `username` defaults to `anonymous` and `password` defaults to `anonymous@`.
- `base_path` is both the default search root and the upload target directory.
- `search_depth` controls how many folder levels `search-file -plugin ftp` scans by default.
- You can browse configured instances from `.config plugins` in the CLI.
## Search Flow
Basic listing from the configured base path:
## Search flow
```powershell
search-file -plugin ftp -instance work "*"
```
Search by filename fragment:
```powershell
search-file -plugin ftp -instance work "invoice"
```
Search a different subtree and recurse deeper:
```powershell
search-file -plugin ftp -instance work "path:/pub depth:2 invoice"
```
Filter to folders only:
```powershell
search-file -plugin ftp -instance work "path:/pub type:folder *"
```
The plugin returns rows with explicit columns for name, type, directory, size, and modification time.
The plugin returns rows with explicit columns for name, type, directory, size,
and modification time.
## Selection Flow
## Selection flow
Folder rows are navigation rows. If the selected row is a directory, plain `@N` opens a new FTP table for that directory:
Folder rows are navigation rows:
```powershell
search-file -plugin ftp -instance work "*"
@2
```
File rows carry an explicit row action:
File rows carry an explicit row action equivalent to:
```powershell
download-file -plugin ftp -instance work -url ftp://ftp.example.com/incoming/report.pdf
```
That means plain `@N` on a file row downloads it immediately:
So plain `@N` on a file row downloads it immediately:
```powershell
search-file -plugin ftp -instance work "report"
@1
```
## Download And Add-File Flow
If you want the downloaded file in a specific local directory:
## Download and add-file flow
```powershell
search-file -plugin ftp -instance work "report"
@1 | download-file -path C:\Downloads
```
If you want to ingest the selected FTP file into a configured instance backend:
```powershell
search-file -plugin ftp -instance work "report"
@1 | add-file -instance tutorial
```
Why this works:
- the file row advertises a `download-file` row action
- the pipeline auto-inserts that download before `add-file`
- the FTP plugin also implements `resolve_pipe_result_download()` so provider-owned FTP rows can be materialized for ingestion
- file rows also carry the chosen `instance`, so selection replay and `@N | add-file ...` keep the same FTP target
- the FTP plugin also implements `resolve_pipe_result_download()` so plugin-owned FTP rows can be materialized for ingestion
- file rows carry the chosen `instance`, so selection replay and `@N | add-file ...` keep the same FTP target
## Upload Flow
## Upload flow
Uploading uses the same provider name, but through `add-file -plugin ftp -instance <name>`:
Uploading uses the same plugin, through `add-file -plugin ftp -instance <name>`:
```powershell
add-file -plugin ftp -instance archive -path C:\Media\report.pdf
```
That sends the file to the selected instance's FTP `base_path` and returns the FTP URL as the uploaded result.
That sends the file to the selected instance's FTP `base_path` and returns the
FTP URL as the uploaded result.
## Why The Row Metadata Matters
## Why the row metadata matters
The critical part of this plugin is the file-row metadata:
- file rows emit `_selection_args` as `['-instance', '<name>', '-url', '<ftp-url>']`
- file rows emit `_selection_action` as `['download-file', '-plugin', 'ftp', '-instance', '<name>', '-url', '<ftp-url>']`
- folder rows do not emit a download action, so `selector()` can own drill-in behavior instead
That split is what keeps these two user experiences compatible:
- folder rows do not emit a download action, so `selector()` owns drill-in behavior instead
That keeps these flows compatible:
- `@N` on a folder opens a new table
- `@N` on a file downloads the file
- `@N | add-file -instance ...` first downloads, then ingests
## Implementation Notes
## Implementation notes
The plugin prefers `MLSD` for directory listings and falls back to `NLST` plus directory probes when the server does not support machine-readable listings.
The code is intentionally small and uses only Python stdlib pieces:
The plugin prefers `MLSD` for directory listings and falls back to `NLST` plus
directory probes when the server does not support machine-readable listings.
The code intentionally stays small and uses only Python stdlib pieces:
- `ftplib` for FTP and FTPS
- `fnmatch` for wildcard-style search tokens
- `tempfile` for `add-file` handoff downloads
## Recommended Commands To Demo The Walkthrough
## Recommended demo commands
```powershell
search-file -plugin ftp -instance work "*"