5.3 KiB
FTP Plugin Walkthrough
This walkthrough adds a real bundled ftp plugin so users can:
- run
search-file -plugin ftp -instance <name> ... - browse remote folders as result tables
- select file rows to
download-file - pipe selected file rows into
add-file - upload local files with
add-file -plugin ftp -instance <name>
The implementation lives in plugins/ftp/__init__.py.
What The Plugin Does
The FTP plugin demonstrates the main provider 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 likepath:anddepth:.search()walks an FTP directory tree and returnsSearchResultrows.selector()turns folder rows into a follow-up table when the user runs@N.download()anddownload_url()fetch FTP files intodownload-fileoutput paths.resolve_pipe_result_download()lets@N | add-file -instance ...materialize a remote FTP file first.upload()letsadd-file -plugin ftp -instance <name> -path ...push a local file to the configured FTP server.
Example Config
Add one or more named FTP provider instances to your config:
[provider.ftp.work]
host = "ftp.example.com"
port = 21
username = "demo"
password = "secret"
base_path = "/incoming"
tls = false
passive = true
timeout = 20
search_depth = 1
[provider.ftp.archive]
host = "archive.example.com"
port = 2121
username = "archive-bot"
password = "secret"
base_path = "/dropbox"
tls = true
Notes:
workandarchiveare instance names; use them with-instance workor-instance archive.hostis the only required field for each instance to validate.usernamedefaults toanonymousandpassworddefaults toanonymous@.base_pathis both the default search root and the upload target directory.search_depthcontrols how many folder levelssearch-file -plugin ftpscans by default.
Search Flow
Basic listing from the configured base path:
search-file -plugin ftp -instance work "*"
Search by filename fragment:
search-file -plugin ftp -instance work "invoice"
Search a different subtree and recurse deeper:
search-file -plugin ftp -instance work "path:/pub depth:2 invoice"
Filter to folders only:
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.
Selection Flow
Folder rows are navigation rows. If the selected row is a directory, plain @N opens a new FTP table for that directory:
search-file -plugin ftp -instance work "*"
@2
File rows carry an explicit row action:
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:
search-file -plugin ftp -instance work "report"
@1
Download And Add-File Flow
If you want the downloaded file in a specific local directory:
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:
search-file -plugin ftp -instance work "report"
@1 | add-file -instance tutorial
Why this works:
- the file row advertises a
download-filerow 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
Upload Flow
Uploading uses the same provider name, but through add-file -plugin ftp -instance <name>:
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.
Why The Row Metadata Matters
The critical part of this plugin is the file-row metadata:
- file rows emit
_selection_argsas['-instance', '<name>', '-url', '<ftp-url>'] - file rows emit
_selection_actionas['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:
@Non a folder opens a new table@Non a file downloads the file@N | add-file -instance ...first downloads, then ingests
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:
ftplibfor FTP and FTPSfnmatchfor wildcard-style search tokenstempfileforadd-filehandoff downloads
Recommended Commands To Demo The Walkthrough
search-file -plugin ftp -instance work "*"
search-file -plugin ftp -instance work "path:/incoming depth:2 *.pdf"
@1
@1 | download-file -path C:\Downloads
@1 | add-file -instance tutorial
add-file -plugin ftp -instance archive -path C:\Media\report.pdf