Zynk CLI

Automation

Use Zynk CLI one-shot commands, stdin path input, JSON output, dry-run previews, daemon mode, and unattended script habits.

One-shot commands

Automation starts from the one-shot command surface. It is the clearest contract: pass flags and arguments, receive output, and exit.

One-shot shape

$ zynk contacts$ zynk pending$ zynk send Bob report.pdf

Command lifecycle

  • One-shot commands run a single action and exit.
  • They can route through the daemon when the daemon is running.
  • If daemon routing is unavailable, they still run the command directly.

Automation fit

  • Use one-shot commands for shell scripts, CI helpers, local wrappers, and scheduled jobs.
  • Use interactive shell commands when a human needs completions, progress, or repeated exploration.
  • Use daemon mode when scripts run many Zynk commands over time.

Script contract

Treat each command as a small process boundary. Give it complete arguments, keep prompts out of unattended paths, and parse JSON only from commands that document JSON output.

Surface
Exit behavior
Contract
A successful one-shot command exits after its command-level work is complete. Invalid arguments, bad config, missing paths, and failed startup exit nonzero.
Surface
stdout
Contract
Human terminal output is the default. Supported successful --json-output commands print one JSON object with a top-level type and data payload.
Surface
stderr
Contract
Validation and startup errors can appear on stderr. Capture stderr separately in wrappers instead of treating it as machine data.
Surface
Prompts
Contract
Complete one-shot commands such as pending, send <target> <path>, and daemon status do not need interactive answers. Avoid install and uninstall in unattended scripts unless the flags answer the prompts you expect.
Surface
Stdin paths
Contract
send reads newline-separated paths from stdin only when no path arguments are present.
Surface
Daemon sends
Contract
A daemon-routed send returns after the request is accepted or queued by the background process; it does not wait for every byte to finish transferring.
Surface
Unsupported JSON
Contract
If a command is not documented as JSON-stable, do not assume --json-output makes its terminal output parseable.

Stdin paths

The most useful automation path is piping a file list into zynk send. This lets existing shell tools choose files while Zynk handles transfer.

Send from files or stdin

$ zynk send Bob report.pdf ./assets$ fd '*.jpg' | zynk send my:laptop$ rg --files | rg 'invoice' | zynk send Alice

Path input

  • send accepts explicit files and folders.
  • When no path arguments are provided, send reads paths from stdin.
  • Use newline-separated path streams from tools such as fd, find, or rg --files.

Scripting habits

  • Quote paths with spaces when passing them directly.
  • Prefer stdin when another command already has the file list.
  • Keep the destination explicit in scripts.

JSON output

Use JSON output when a wrapper or script needs structured status instead of terminal text.

Machine output

$ zynk pending --json-output{"type":"pending","data":{"count":0,"transfers":[]}}$ zynk pending --include-messages --json-output{"type":"pending","data":{"count":1,"transfers":[{"index":0,"id":"<transfer-id>","name":"report.pdf","state":"pending"}],"messages":[]}}$ zynk daemon status --json-output{"type":"daemon_status","data":{"running":true,"pid":"12345","user":"<os-user>","uptime_secs":3600,"socket":"/tmp/zynk.sock","socket_exists":true,"lock_file":"<lock-file>"}}$ zynk daemon status --json-output{"type":"daemon_status","data":{"running":false,"pid":null,"user":null,"uptime_secs":null,"socket":"/tmp/zynk.sock","socket_exists":false,"lock_file":"<lock-file>"}}
Command
zynk pending --json-output
Top-level type
pending
Stable fields
data.count counts transfer rows for the selected window. Each stable transfer row includes index, id when available, name, and state. data.messages appears only with --include-messages.
Command
zynk daemon status --json-output
Top-level type
daemon_status
Stable fields
data.running, nullable string data.pid, nullable OS username data.user, nullable data.uptime_secs, data.socket, data.socket_exists, and data.lock_file.
Command
zynk config path --json-output
Top-level type
config_paths
Stable fields
data.config_dir, data.user_config, and data.system_config.
Command
zynk show-state-dir --json-output
Top-level type
state_dir
Stable fields
data.path for the default or flag-selected state directory used by that run.

JSON boundary

  • --json-output is global, but not every command emits a stable JSON payload.
  • Start with commands that are useful in wrappers: pending, daemon status, config path, and show-state-dir.
  • Treat each command's payload as its own shape instead of assuming all JSON output is interchangeable.

Useful payloads

  • pending --json-output gives scripts a machine-readable incoming-transfer inbox.
  • count: 0 is a successful no-work result for the command's current time window.
  • If a script is looking beyond the default window, use --all or --since <duration> deliberately.
  • daemon status --json-output gives scripts a machine-readable health check.
  • Add --include-messages only when the wrapper needs recent incoming messages next to transfers.

JSON errors

JSON output gives wrappers a machine-readable success or error object when the command reaches the JSON-emitting path.

Error shape

$ zynk pending --json-output{"type":"error","code":"<error-code>","message":"<message>"}# stderr may also contain non-JSON diagnostics for startup or argument failures

Wrapper handling

  • Read the top-level type before reading data.
  • Only parse data when type is the success shape the wrapper expected.
  • Error objects use top-level code and message; they do not have the success data envelope.
  • Treat a nonzero process exit as failure even when stdout contains JSON.

stderr is separate

  • Startup, validation, and environment failures can still write diagnostic text to stderr.
  • Not every failure reaches the JSON-emitting path.
  • Capture stderr separately from stdout in scripts.
  • Do not try to parse stderr as JSON.

Wrapper example

A small wrapper can let shell tools choose files, preview the action, then run the transfer only after the preview looks right.

Dry-run then send matching paths

$ paths="$(mktemp)"$ trap 'rm -f "$paths"' EXIT$ rg --files "$HOME/Invoices" | rg 'invoice' > "$paths"$ zynk --dry-run send Alice < "$paths"$ zynk send Alice < "$paths"

The file list is captured once so the dry run and real send use the same paths.

Dry runs

Dry runs are the bridge between automation and human approval. They let a wrapper inspect the planned action before it executes.

Preview actions

$ zynk --dry-run send Bob report.pdf$ zynk --dry-run accept 0 ~/Downloads

Dry-run output is preview JSON, for example send_preview with destination, device, and files, or accept_preview with selector and destination. It is not proof that a real transfer happened.

Dry-run rules

  • --dry-run previews what an action would do without executing it.
  • --dry-run always bypasses daemon routing.
  • Use dry runs before wrappers ask a human to confirm a transfer.

Review before execution

  • Compare dry-run output against the transfer or accept action your wrapper plans to run.
  • A dry run does not replace user permission when a script will transmit real files.
  • Keep preview output distinct from JSON status output.

Unattended scripts

Unattended scripts need the account, state directory, executable path, and log handling to be explicit.

Cron or server wrapper

#!/usr/bin/env bashset -euo pipefailexport PATH="$HOME/.local/bin:$PATH"status_json="$(zynk --config ./server.zynk.conf --persistence /var/lib/zynk daemon status --json-output 2>zynk.err)"if ! jq -e '.type == "daemon_status" and .data.running == true' <<<"$status_json" >/dev/null; then  echo "Zynk daemon is not running" >&2  cat zynk.err >&2  exit 1fizynk --no-color --json-output pending

Use any JSON parser your environment provides; the important gate is .type == "daemon_status" and .data.running == true before continuing.

Environment

  • Set PATH explicitly instead of relying on an interactive shell profile.
  • Pass --config and --persistence when the script must use a specific account or state directory.
  • Use daemon status --json-output as the first health check before a batch of routed commands.

Output handling

  • Use --json-output only on commands with documented JSON shapes.
  • Use --no-color for plain fallback terminal output.
  • Write stderr and daemon logs somewhere you can inspect after an unattended run.

Daemon scripts

Daemon mode makes repeated scripts cheaper while keeping the background process explicit and observable.

Warm daemon flow

$ zynk --run-mode server$ zynk daemon status --json-output$ zynk pending --json-output

Daemon automation

  • Start the daemon explicitly with zynk --run-mode server.
  • Check health with zynk daemon status --json-output.
  • Stop it with zynk daemon kill when rotating accounts, changing state, or investigating stale behavior.

Server discipline

  • Persist persistence on servers so identity survives restarts.
  • Avoid running multiple daemons against the same state directory.
  • Use logs for daemon lifecycle diagnosis.

Shell completions

Shell completions are an interactive convenience. Keep them separate from automation contracts and unattended script checks.

Generate completions

$ zynk completions zsh > ~/.zsh/completions/_zynk$ zynk completions bash > /etc/bash_completion.d/zynk$ zynk completions fish > ~/.config/fish/completions/zynk.fish

Supported shells

  • zynk completions zsh generates zsh completions.
  • zynk completions bash generates bash completions.
  • zynk completions fish generates fish completions.
  • zynk completions powershell generates PowerShell completions.
  • zynk completions elvish generates Elvish completions.

Install notes

  • Completions are generated and redirected by the user.
  • Completion generation does not route through the daemon.
  • The installer can print completion-generation hints after PATH setup.
  • They are useful for humans typing commands, not for machine contracts.