Zynk CLI
Ready-to-use scripts
Two ready-to-use Bash wrappers that send and accept transfers, read the JSON stream, and report a clear outcome.
Send script
Save this as json-send.sh. It submits a transfer, reads the whole NDJSON stream, and reports whether the request was accepted.
Save as json-send.sh
#!/usr/bin/env bashset -uif ! command -v jq >/dev/null 2>&1; then printf 'json-send: jq is required\n' >&2 exit 69fizynk_bin=${ZYNK_BIN:-zynk}global_args=()while (($#)); do if [[ $1 == -- ]]; then shift break fi global_args+=("$1") shiftdoneif (($# < 1)); then printf 'usage: %s [ZYNK_GLOBAL_OPTION...] -- DESTINATION [PATH...]\n' "$0" >&2 exit 64fiif (($# < 2)) && [[ -t 0 ]]; then printf '%s: no paths given; list them after the destination or pipe them on stdin\n' "$0" >&2 exit 64fistream=$(mktemp "${TMPDIR:-/tmp}/zynk-json-send.XXXXXX") || exit 74trap 'rm -f "$stream"' EXITif "$zynk_bin" "${global_args[@]}" --json-output send "$@" >"$stream"; then command_status=0else command_status=$?ficat "$stream"# Read every complete record before judging the stream. A killed or truncated# run still has to report the transfer ID it already dispatched, because# without it you cannot tell a safe retry from a duplicate send.queued_id=$(jq -r 'select(.type == "send_queued") | .data.transfer_id // empty' "$stream" 2>/dev/null | tail -n 1)result=$(jq -c 'select(.type == "send_result")' "$stream" 2>/dev/null | tail -n 1)if [[ -z $result ]]; then result='{}'fioutcome=$(jq -r '.data.outcome // empty' <<<"$result")result_id=$(jq -r '.data.transfer_id // empty' <<<"$result")error_code=$(jq -r 'select(.type == "error") | .code // empty' "$stream" 2>/dev/null | tail -n 1)if [[ ! -s $stream ]]; then printf 'json-send: no output; Zynk exited with status %s and dispatched nothing\n' \ "$command_status" >&2 exit "$((command_status == 0 ? 65 : command_status))"fiif ! jq -e . "$stream" >/dev/null 2>&1; then if [[ -n $queued_id ]]; then printf 'json-send: truncated output after dispatch transfer_id=%s; check state before retrying\n' \ "$queued_id" >&2 else printf 'json-send: truncated output with no dispatch receipt\n' >&2 fi exit "$((command_status == 0 ? 65 : command_status))"fiif [[ -n $queued_id && -n $result_id && $queued_id != "$result_id" ]]; then printf 'json-send: protocol error: queued/result transfer IDs differ\n' >&2 exit 65ficase "$command_status:$outcome" in 0:submitted) printf 'json-send: submitted transfer_id=%s (recipient delivery is still pending)\n' \ "$result_id" >&2 ;; *:rejected) printf 'json-send: rejected transfer_id=%s code=%s\n' \ "$result_id" "${error_code:-submission_rejected}" >&2 ;; 0:*) printf 'json-send: protocol error: successful command had no submitted result\n' >&2 exit 65 ;; *:*) if [[ -n $queued_id ]]; then printf 'json-send: outcome unknown transfer_id=%s code=%s; inspect state before retrying\n' \ "$queued_id" "${error_code:-unknown}" >&2 else printf 'json-send: failed before a dispatch receipt code=%s\n' \ "${error_code:-unknown}" >&2 fi ;;esacexit "$command_status"This reads the complete result and reports submitted, rejected, or outcome unknown. A submitted result confirms the request, not delivery to the recipient.
Run the send script
$ bash json-send.sh -- Alice ./report.pdfReplace Alice and ./report.pdf with your recipient and file. The -- separator goes before the recipient and paths.
Set a response timeout and save the JSON
$ bash json-send.sh --wait-timeout 10s -- Alice ./report.pdf > send.jsonlGeneral Zynk options go before --. JSON is saved to send.jsonl; the short explanation still appears in the terminal.
Accept script
Save this as json-accept.sh. It accepts pending transfers and tracks requested IDs separately from completed downloads.
Save as json-accept.sh
#!/usr/bin/env bashset -uif ! command -v jq >/dev/null 2>&1; then printf 'json-accept: jq is required\n' >&2 exit 69fizynk_bin=${ZYNK_BIN:-zynk}global_args=()while (($#)); do if [[ $1 == -- ]]; then shift break fi global_args+=("$1") shiftdonestream=$(mktemp "${TMPDIR:-/tmp}/zynk-json-accept.XXXXXX") || exit 74trap 'rm -f "$stream"' EXITif "$zynk_bin" "${global_args[@]}" --json-output accept "$@" >"$stream"; then command_status=0else command_status=$?ficat "$stream"# Read every complete record before judging the stream, so a killed or# truncated run still reports the transfer IDs it already dispatched.dispatched_ids=$(jq -r 'select(.type == "accept") | .data.accepted[]?.id' "$stream" 2>/dev/null | paste -sd, -)completed_ids=$(jq -r ' select(.type == "transfer_complete" and .data.upload == false) | .data.transfer_id // empty' "$stream" 2>/dev/null | paste -sd, -)failed_count=$(jq -s '[.[] | select(.type == "accept") | .data.failed[]?] | length' "$stream" 2>/dev/null)dispatched_count=$(jq -s '[.[] | select(.type == "accept") | .data.accepted[]?] | length' "$stream" 2>/dev/null)completed_count=$(jq -s '[.[] | select(.type == "transfer_complete" and .data.upload == false)] | length' "$stream" 2>/dev/null)error_code=$(jq -r 'select(.type == "error") | .code // empty' "$stream" 2>/dev/null | tail -n 1)unresolved_count=$(jq -s ' ([.[] | select(.type == "accept") | .data.accepted[]?.id] | unique) as $dispatched | ([.[] | select(.type == "transfer_complete" and .data.upload == false) | .data.transfer_id] | unique) as $completed | ($dispatched - $completed) | length' "$stream" 2>/dev/null)if [[ ! -s $stream ]]; then printf 'json-accept: no output; Zynk exited with status %s and dispatched nothing\n' \ "$command_status" >&2 exit "$((command_status == 0 ? 65 : command_status))"fiif ! jq -e . "$stream" >/dev/null 2>&1; then if [[ -n $dispatched_ids ]]; then printf 'json-accept: truncated output after dispatching %s; inspect those IDs before retrying\n' \ "$dispatched_ids" >&2 else printf 'json-accept: truncated output with no accept dispatch\n' >&2 fi exit "$((command_status == 0 ? 65 : command_status))"fiif [[ -n $dispatched_ids ]]; then printf 'json-accept: retained dispatched transfer IDs: %s\n' "$dispatched_ids" >&2fiif [[ -n $completed_ids ]]; then printf 'json-accept: completed transfer IDs: %s\n' "$completed_ids" >&2fiif ((command_status == 0)); then printf 'json-accept: complete dispatched=%s completed=%s failed=%s\n' \ "$dispatched_count" "$completed_count" "$failed_count" >&2elif ((unresolved_count > 0)); then printf 'json-accept: outcome unknown code=%s; inspect retained IDs before retrying\n' \ "${error_code:-unknown}" >&2elif [[ -n $completed_ids ]]; then printf 'json-accept: partial success; do not retry completed IDs (failed=%s)\n' \ "$failed_count" >&2else printf 'json-accept: failed before any accept dispatch\n' >&2fiexit "$command_status"This tracks requested IDs separately from completed downloads, including when only part of a batch succeeds.
Run the accept script
$ bash json-accept.sh -- --all --save-dir ./DownloadsThis accepts every transfer that is still waiting into ./Downloads, so check pending --all first when you only want some of them. Accept options go after the -- separator.
Output and exit status
These scripts collect output and report after Zynk finishes. They preserve Zynk’s exit status when they can read the output. Missing tools or invalid output cause the scripts themselves to fail. Set ZYNK_BIN to use a different Zynk binary.