v0.8.18: readable terminal output (vertical entity lists + verbatim-fenced aligned tables) + cmd_push direct-mode branch + _direct_ssh_opts dedup
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d55e222341
commit
65807308d8
58
CHANGELOG.md
58
CHANGELOG.md
@ -4,6 +4,64 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here.
|
|||||||
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
||||||
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
||||||
|
|
||||||
|
## v0.8.18 — 2026-05-28
|
||||||
|
|
||||||
|
Readable terminal output + two DIRECT-mode follow-ups from Vera's v0.8.17 gate
|
||||||
|
(Clover). larry runs in a plain monospace terminal that does NOT render
|
||||||
|
markdown — this release makes tabular results actually line up and site lists
|
||||||
|
read vertically, and closes the deferred `ssh_push` direct-mode gap plus the
|
||||||
|
duplicated direct-ssh options.
|
||||||
|
|
||||||
|
**1. Readable output in a monospace terminal (Bryan's primary ask).**
|
||||||
|
The on-server LLM was re-rendering tool results as markdown tables
|
||||||
|
(`| col | col |`, `|---|`) — which print raw and never align — and collapsing
|
||||||
|
the site list into a comma-joined inline sentence. The tools already emit clean
|
||||||
|
data (nc-find/nc-inbound pre-align columns with `%-*s`; `list_sites` prints one
|
||||||
|
site per line with a `sites: N (excluded: …)` headline). The fix steers the
|
||||||
|
model to PRESERVE that, and reinforces it at the tool boundary so it can't drift:
|
||||||
|
|
||||||
|
- `agents/larry.md` — new **TERMINAL OUTPUT CONTRACT** section (5 rules): never
|
||||||
|
emit a markdown table; reproduce a tool's pre-aligned/fenced table VERBATIM in
|
||||||
|
a ```text fence; never hand-align columns (the tools do it deterministically);
|
||||||
|
render entity lists ONE PER LINE vertically (never comma-joined inline), and
|
||||||
|
keep `list_sites`'s count headline + one-per-line list as returned.
|
||||||
|
- `larry.sh` — new `_fence_aligned_table` helper wraps an already-aligned tool
|
||||||
|
table in a ```text fence with an explicit "reproduce VERBATIM; do NOT convert
|
||||||
|
to a markdown table" marker. `tool_nc_find` and `tool_nc_find_inbound` route
|
||||||
|
their `--format table` output through it (tsv/jsonl data formats pass through
|
||||||
|
unfenced). Empty / error / usage output passes through UNCHANGED (the operator
|
||||||
|
must still see errors plainly); the table bytes are never altered — only the
|
||||||
|
fence + marker lines are added.
|
||||||
|
|
||||||
|
**2. `cmd_push` DIRECT-mode branch (closes Vera's deferred MAJOR).**
|
||||||
|
`cmd_push` called `_resolve_open_master` unconditionally, so a DIRECT-mode alias
|
||||||
|
(no ControlMaster) died "no open master" — breaking `ssh_push` (exposed tool;
|
||||||
|
used by `nc_regression` phase 4 to push cross-env input bundles, central to the
|
||||||
|
`epic_adt_in` cross-env goal). Added the symmetric direct branch mirroring
|
||||||
|
`cmd_pull`: `_direct_scp <alias> <local> <addr>:<remote>` for the transfer, then
|
||||||
|
post-transfer size verification via `_dispatch_remote` (fresh per-command
|
||||||
|
`_run_direct`). `lib/ssh-helper.sh`.
|
||||||
|
|
||||||
|
**3. De-duplicated the direct-ssh options (closes Vera's MINOR).**
|
||||||
|
The shared direct-mode `-o` flags (the five security-critical ones —
|
||||||
|
`PreferredAuthentications=password`, `PubkeyAuthentication=no`,
|
||||||
|
`StrictHostKeyChecking=accept-new`, `ControlMaster=no`, `ControlPath=none` —
|
||||||
|
plus `NumberOfPasswordPrompts=1` and `ConnectTimeout`) were copied in three
|
||||||
|
places (`cmd_setup` probe, `_run_direct`, `_direct_scp`). Extracted into one
|
||||||
|
`_direct_ssh_opts` helper so a security-option change can't drift across copies;
|
||||||
|
all three sites now splat `$(_direct_ssh_opts)`. ssh `-o` ordering is immaterial
|
||||||
|
(no conflicting duplicate keys) so this is byte-equivalent in BEHAVIOR — verified
|
||||||
|
the reconstructed argv matches the prior inline copies token-for-token.
|
||||||
|
`lib/ssh-helper.sh`.
|
||||||
|
|
||||||
|
**No traffic bypass (unchanged, absolute).** DIRECT mode is legitimate
|
||||||
|
forced-password auth only — no proxy, no tunnel, no masking, and host-key
|
||||||
|
checking STAYS ON (`StrictHostKeyChecking=accept-new`). The dedup helper keeps
|
||||||
|
that posture in a single source of truth.
|
||||||
|
|
||||||
|
`bash -n` clean on every changed file. `/sites` slash path drives clean under
|
||||||
|
`set -u`. VERSION + larry.sh:81 bumped to 0.8.18; MANIFEST regenerated.
|
||||||
|
|
||||||
## v0.8.17 — 2026-05-28
|
## v0.8.17 — 2026-05-28
|
||||||
|
|
||||||
Per-alias DIRECT (no-multiplex) SSH mode (Clover). The real unblock for
|
Per-alias DIRECT (no-multiplex) SSH mode (Clover). The real unblock for
|
||||||
|
|||||||
10
MANIFEST
10
MANIFEST
@ -23,19 +23,19 @@
|
|||||||
# scripts/make-manifest.sh and bump VERSION.
|
# scripts/make-manifest.sh and bump VERSION.
|
||||||
|
|
||||||
# Top-level scripts
|
# Top-level scripts
|
||||||
larry.sh 7bdbe0743d7aec58ccedadfebd766e8bbfd828d47e33e51b2bac75a4d0706f5b
|
larry.sh 087cc26634aa330049d46940ff6370dad2b84b267a8d4ce87b528eb8bd333d5d
|
||||||
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
||||||
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
||||||
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
||||||
install-larry.sh e97da4e12a0d8863ca18d79b12f6c4294c72fa6d4b11dffeab66504236bb4eb1
|
install-larry.sh e97da4e12a0d8863ca18d79b12f6c4294c72fa6d4b11dffeab66504236bb4eb1
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
VERSION a8c64c5df539331e33b8b4b5c1534d12f6238dbbbd313e7cebbf1cff1df0fe87
|
VERSION 1d14fd69d4f2d2b8118fa821e3c9a3d88f0a45cb6b262645ff643b4ae101d2b2
|
||||||
MANUAL.md 666128a086b59ff3c31a574aec0c5dd681666d66319da9f078451bf9013ca5e1
|
MANUAL.md 666128a086b59ff3c31a574aec0c5dd681666d66319da9f078451bf9013ca5e1
|
||||||
CHANGELOG.md a329fda8be2e5caa33f1dfec7a5c68adc7d7d19b8449032cbfc9542a766292b6
|
CHANGELOG.md 41763bdd066ed12d25a0f212378102fac4b5cfd91895a330f34e0859ae697d91
|
||||||
|
|
||||||
# Agent personas (system-prompt overlays)
|
# Agent personas (system-prompt overlays)
|
||||||
agents/larry.md 11ea905fa7cac6fa7baeb11b2d62af07b15a666ce90cfe36491bcbc555244397
|
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||||
agents/clover.md d1bbfd6cc4642c2bff6e15dcbdf051d71b063b3fe29e0be97d17b3180d3c7ac5
|
agents/clover.md d1bbfd6cc4642c2bff6e15dcbdf051d71b063b3fe29e0be97d17b3180d3c7ac5
|
||||||
agents/cloverleaf-cheatsheet.md c0a2aab91f1ddf092bce312def02cc6f3f62a1f653ca5af67a9430c3fcef4c3f
|
agents/cloverleaf-cheatsheet.md c0a2aab91f1ddf092bce312def02cc6f3f62a1f653ca5af67a9430c3fcef4c3f
|
||||||
agents/regress.md bb05ed1439b1e35d6e9799e32d683bfab166472c72115c1f02757e227c74e42f
|
agents/regress.md bb05ed1439b1e35d6e9799e32d683bfab166472c72115c1f02757e227c74e42f
|
||||||
@ -52,7 +52,7 @@ lib/fetch-safe.sh abecf0045b9856f63ffa346119443c11de56547344be32bddaed9fbae6b021
|
|||||||
lib/oauth.sh 04a93376f88fe53cc1c86a5dbe577735c60375dadd4f2fda55b921ef3cddf22b
|
lib/oauth.sh 04a93376f88fe53cc1c86a5dbe577735c60375dadd4f2fda55b921ef3cddf22b
|
||||||
|
|
||||||
# Secure SSH with ControlMaster (password hidden from Larry-the-LLM)
|
# Secure SSH with ControlMaster (password hidden from Larry-the-LLM)
|
||||||
lib/ssh-helper.sh e9e2f33bb893d951e668d81dfe88057d235013b60cdba0e3441d1400d877a6d4
|
lib/ssh-helper.sh bd205aa87bc9e53821cac45888faa9434c1e182bee2bf16d6d838dcb79bfac3e
|
||||||
|
|
||||||
# v0.8.6: work-box → Mac headers.log sync (tsk-2026-05-27-023). Incremental,
|
# v0.8.6: work-box → Mac headers.log sync (tsk-2026-05-27-023). Incremental,
|
||||||
# offset-tracked push of $LARRY_HOME/log/headers.log to a daemon-watched path
|
# offset-tracked push of $LARRY_HOME/log/headers.log to a daemon-watched path
|
||||||
|
|||||||
@ -40,6 +40,18 @@ You have access to a small but sharp tool set:
|
|||||||
|
|
||||||
You do **not** have subagent dispatch in portable mode. You are Larry + Clover (and any other specialist you need to channel) in one head. Be honest about that limitation when it matters.
|
You do **not** have subagent dispatch in portable mode. You are Larry + Clover (and any other specialist you need to channel) in one head. Be honest about that limitation when it matters.
|
||||||
|
|
||||||
|
## TERMINAL OUTPUT CONTRACT (mandatory — you render to a plain monospace terminal)
|
||||||
|
|
||||||
|
Your output is read in a **plain monospace terminal that does NOT render markdown.** Markdown tables, bold, and headings print as raw literal characters. Follow these rules every time:
|
||||||
|
|
||||||
|
1. **NEVER emit a markdown table.** Do not write `| col | col |` rows or `|---|---|` separator lines — they print raw and the columns do NOT line up. If a tool already returned an aligned, fenced table block (the table tools do — see below), reproduce that block VERBATIM. Do not re-flow it, re-pad it, re-sort it, or convert it back into a markdown table.
|
||||||
|
2. **Tool output that is already a table → present it verbatim in a fenced code block.** Tools like `nc_find`, `nc_find_inbound`, and the table tools return columns that are ALREADY aligned with spaces by the tool, wrapped in a ```text … ``` fence. Echo that fenced block exactly as received. The alignment is deterministic and done by the tool — you must not "improve" it. Adding or removing a single space breaks the alignment in the terminal.
|
||||||
|
3. **Do not align columns yourself.** You are bad at counting monospace width; the tools do it deterministically. Your job is to pass the pre-aligned block through unchanged, then add a one-line takeaway ABOVE or BELOW the block (never inside it).
|
||||||
|
4. **Entity lists render ONE PER LINE, vertically — never comma-joined inline.** When you list sites, threads, files, processes, or any set of named things, put each on its own line (e.g. ` - ancout`). Do NOT write `ancout, ancout2, ancout3, …` on one line. The `list_sites` tool already prints sites one per line and prints a `sites: N (excluded: …)` headline — keep the count headline and the one-per-line list exactly as returned; do not collapse the list into a sentence.
|
||||||
|
5. **Keep prose minimal around data.** Lead with the answer (the count, the match), show the verbatim block, then stop. No restating every row in prose.
|
||||||
|
|
||||||
|
These rules override any instinct to "format nicely with markdown." In this environment, plain pre-aligned text IS the nice format.
|
||||||
|
|
||||||
## Working style
|
## Working style
|
||||||
|
|
||||||
- **Read before you write.** When pointed at a Cloverleaf root, start with `list_dir` and a targeted `grep_files` to map the lay of the land before proposing changes.
|
- **Read before you write.** When pointed at a Cloverleaf root, start with `list_dir` and a targeted `grep_files` to map the lay of the land before proposing changes.
|
||||||
|
|||||||
43
larry.sh
43
larry.sh
@ -78,7 +78,7 @@ set -o pipefail
|
|||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Config
|
# Config
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
LARRY_VERSION="0.8.17"
|
LARRY_VERSION="0.8.18"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@ -1604,6 +1604,31 @@ _lib_err_if_missing() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# v0.8.18: _fence_aligned_table — wrap an already-space-aligned tool table in a
|
||||||
|
# ```text fence so the on-server LLM reproduces it VERBATIM in the monospace
|
||||||
|
# terminal instead of re-rendering it as a (mis-aligned) markdown table. Reads
|
||||||
|
# the tool's stdout/stderr on STDIN; the table tools (nc-find, nc-inbound) emit
|
||||||
|
# columns padded with %-*s, so the bytes are ALREADY aligned — we only need to
|
||||||
|
# fence them and tell the model not to touch them.
|
||||||
|
#
|
||||||
|
# Pass-through guarantee: if the captured text is empty, or looks like an error /
|
||||||
|
# usage line (so there is no real table to protect), we emit it UNCHANGED — the
|
||||||
|
# model must still see error text plainly. We never alter the table bytes; the
|
||||||
|
# fence and the two marker lines are the only additions.
|
||||||
|
_fence_aligned_table() {
|
||||||
|
local body; body=$(cat)
|
||||||
|
# Empty or obvious error/diagnostic → pass through untouched.
|
||||||
|
if [ -z "$body" ] || printf '%s' "$body" \
|
||||||
|
| grep -qiE '^(ERROR|nc-[a-z]+:|usage:|\[)' ; then
|
||||||
|
printf '%s\n' "$body"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf '%s\n' "TABLE (monospace, pre-aligned by the tool — reproduce VERBATIM in a code block; do NOT convert to a markdown table):"
|
||||||
|
printf '%s\n' '```text'
|
||||||
|
printf '%s\n' "$body"
|
||||||
|
printf '%s\n' '```'
|
||||||
|
}
|
||||||
|
|
||||||
tool_nc_list_protocols() {
|
tool_nc_list_protocols() {
|
||||||
local nc="$1"
|
local nc="$1"
|
||||||
_lib_err_if_missing || return
|
_lib_err_if_missing || return
|
||||||
@ -1651,7 +1676,13 @@ tool_nc_xlate_refs() {
|
|||||||
tool_nc_find_inbound() {
|
tool_nc_find_inbound() {
|
||||||
local nc="$1" mode="${2:-all}" fmt="${3:-tsv}"
|
local nc="$1" mode="${2:-all}" fmt="${3:-tsv}"
|
||||||
_lib_err_if_missing || return
|
_lib_err_if_missing || return
|
||||||
"$LARRY_LIB_DIR/nc-inbound.sh" "$nc" --mode "$mode" --format "$fmt" 2>&1
|
# v0.8.18: fence the table format so the model reproduces it verbatim in the
|
||||||
|
# monospace terminal. tsv/jsonl are data formats — passed through unfenced.
|
||||||
|
if [ "$fmt" = "table" ]; then
|
||||||
|
"$LARRY_LIB_DIR/nc-inbound.sh" "$nc" --mode "$mode" --format "$fmt" 2>&1 | _fence_aligned_table
|
||||||
|
else
|
||||||
|
"$LARRY_LIB_DIR/nc-inbound.sh" "$nc" --mode "$mode" --format "$fmt" 2>&1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
tool_nc_make_jump() {
|
tool_nc_make_jump() {
|
||||||
local nc="$1" inbound="$2" new_host="$3" jump_port="$4"
|
local nc="$1" inbound="$2" new_host="$3" jump_port="$4"
|
||||||
@ -1708,7 +1739,13 @@ tool_nc_find() {
|
|||||||
name|port|host|process|where|xlate|tclproc) args+=(--"$mode" "$query") ;;
|
name|port|host|process|where|xlate|tclproc) args+=(--"$mode" "$query") ;;
|
||||||
*) echo "ERROR: unknown nc_find mode: $mode"; return 1 ;;
|
*) echo "ERROR: unknown nc_find mode: $mode"; return 1 ;;
|
||||||
esac
|
esac
|
||||||
"$LARRY_LIB_DIR/nc-find.sh" "${args[@]}" 2>&1
|
# v0.8.18: fence the table format so the model reproduces it verbatim in the
|
||||||
|
# monospace terminal. tsv/jsonl are data formats — passed through unfenced.
|
||||||
|
if [ "$format" = "table" ]; then
|
||||||
|
"$LARRY_LIB_DIR/nc-find.sh" "${args[@]}" 2>&1 | _fence_aligned_table
|
||||||
|
else
|
||||||
|
"$LARRY_LIB_DIR/nc-find.sh" "${args[@]}" 2>&1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_nc_insert_protocol() {
|
tool_nc_insert_protocol() {
|
||||||
|
|||||||
@ -331,14 +331,9 @@ cmd_setup() {
|
|||||||
local errfile; errfile=$(mktemp 2>/dev/null || echo "/tmp/larry-ssh-direct-setup.err.$$")
|
local errfile; errfile=$(mktemp 2>/dev/null || echo "/tmp/larry-ssh-direct-setup.err.$$")
|
||||||
# A trivial, side-effect-free probe. Forced password auth, host-key checked,
|
# A trivial, side-effect-free probe. Forced password auth, host-key checked,
|
||||||
# no master. STDERR (banner/sudo) is captured for failure diagnosis only.
|
# no master. STDERR (banner/sudo) is captured for failure diagnosis only.
|
||||||
|
# v0.8.18: shared DIRECT options via _direct_ssh_opts (was an inline copy).
|
||||||
sshpass -f "$credfile" ssh \
|
sshpass -f "$credfile" ssh \
|
||||||
-o "PreferredAuthentications=password" \
|
$(_direct_ssh_opts) \
|
||||||
-o "PubkeyAuthentication=no" \
|
|
||||||
-o "NumberOfPasswordPrompts=1" \
|
|
||||||
-o "StrictHostKeyChecking=accept-new" \
|
|
||||||
-o "ControlMaster=no" \
|
|
||||||
-o "ControlPath=none" \
|
|
||||||
-o "ConnectTimeout=$_DIRECT_CONNECT_TIMEOUT" \
|
|
||||||
-p "$port" \
|
-p "$port" \
|
||||||
"$addr" 'true' 2>"$errfile"
|
"$addr" 'true' 2>"$errfile"
|
||||||
local vrc=$?
|
local vrc=$?
|
||||||
@ -580,6 +575,37 @@ _remote_cmd_for() {
|
|||||||
# _DIRECT_CONNECT_TIMEOUT — seconds for ssh ConnectTimeout (env-overridable).
|
# _DIRECT_CONNECT_TIMEOUT — seconds for ssh ConnectTimeout (env-overridable).
|
||||||
_DIRECT_CONNECT_TIMEOUT="${LARRY_SSH_DIRECT_TIMEOUT:-10}"
|
_DIRECT_CONNECT_TIMEOUT="${LARRY_SSH_DIRECT_TIMEOUT:-10}"
|
||||||
|
|
||||||
|
# _direct_ssh_opts → emit the shared ssh/scp `-o` option tokens for every DIRECT
|
||||||
|
# (no-ControlMaster) transport, one token per word, on STDOUT. v0.8.18: extracted
|
||||||
|
# so the security posture lives in ONE place and a change can't drift across the
|
||||||
|
# three call sites (cmd_setup probe, _run_direct, _direct_scp). The five
|
||||||
|
# security-critical flags are:
|
||||||
|
# PreferredAuthentications=password — force the password method so sshpass
|
||||||
|
# feeds the password cleanly past a banner
|
||||||
|
# PubkeyAuthentication=no — never silently fall back to a key
|
||||||
|
# StrictHostKeyChecking=accept-new — host-key checking STAYS ON (TOFU). This
|
||||||
|
# is the no-traffic-bypass guarantee: we
|
||||||
|
# never disable host verification.
|
||||||
|
# ControlMaster=no / ControlPath=none — never multiplex (the box rejects it)
|
||||||
|
# Plus two shared connection knobs identical across all three callers:
|
||||||
|
# NumberOfPasswordPrompts=1 — a stale password fails fast (one prompt)
|
||||||
|
# ConnectTimeout=$_DIRECT_CONNECT_TIMEOUT
|
||||||
|
# ssh `-o` ordering is immaterial (no conflicting duplicate keys), so emitting
|
||||||
|
# these as one contiguous block is byte-equivalent in BEHAVIOR to the prior
|
||||||
|
# inline copies. Callers splat the words unquoted: `ssh $(_direct_ssh_opts) ...`.
|
||||||
|
# Every token here is a single shell word (no spaces inside any -o value), so the
|
||||||
|
# unquoted expansion is safe.
|
||||||
|
_direct_ssh_opts() {
|
||||||
|
printf '%s\n' \
|
||||||
|
-o "PreferredAuthentications=password" \
|
||||||
|
-o "PubkeyAuthentication=no" \
|
||||||
|
-o "NumberOfPasswordPrompts=1" \
|
||||||
|
-o "StrictHostKeyChecking=accept-new" \
|
||||||
|
-o "ControlMaster=no" \
|
||||||
|
-o "ControlPath=none" \
|
||||||
|
-o "ConnectTimeout=$_DIRECT_CONNECT_TIMEOUT"
|
||||||
|
}
|
||||||
|
|
||||||
# _direct_creds ALIAS → echoes the credfile path (the file /ssh-pass writes),
|
# _direct_creds ALIAS → echoes the credfile path (the file /ssh-pass writes),
|
||||||
# or empty (and warns) if absent. Same file the ControlMaster path uses.
|
# or empty (and warns) if absent. Same file the ControlMaster path uses.
|
||||||
_direct_creds() {
|
_direct_creds() {
|
||||||
@ -629,14 +655,9 @@ _run_direct() {
|
|||||||
# path's v0.8.15 PreferredAuthentications=password hardening). BatchMode is
|
# path's v0.8.15 PreferredAuthentications=password hardening). BatchMode is
|
||||||
# NOT set — sshpass supplies the password non-interactively via the askpass
|
# NOT set — sshpass supplies the password non-interactively via the askpass
|
||||||
# file descriptor; BatchMode would suppress that path on some builds.
|
# file descriptor; BatchMode would suppress that path on some builds.
|
||||||
|
# v0.8.18: shared DIRECT options via _direct_ssh_opts (was an inline copy).
|
||||||
sshpass -f "$credfile" ssh \
|
sshpass -f "$credfile" ssh \
|
||||||
-o "PreferredAuthentications=password" \
|
$(_direct_ssh_opts) \
|
||||||
-o "PubkeyAuthentication=no" \
|
|
||||||
-o "NumberOfPasswordPrompts=1" \
|
|
||||||
-o "StrictHostKeyChecking=accept-new" \
|
|
||||||
-o "ControlMaster=no" \
|
|
||||||
-o "ControlPath=none" \
|
|
||||||
-o "ConnectTimeout=$_DIRECT_CONNECT_TIMEOUT" \
|
|
||||||
-p "$port" \
|
-p "$port" \
|
||||||
"$addr" "$remote_cmd" 2>"$errfile"
|
"$addr" "$remote_cmd" 2>"$errfile"
|
||||||
local rc=$?
|
local rc=$?
|
||||||
@ -671,14 +692,10 @@ _direct_scp() {
|
|||||||
local credfile; credfile=$(_direct_creds "$alias") \
|
local credfile; credfile=$(_direct_creds "$alias") \
|
||||||
|| die "no password set for $alias — run 'pass $alias' first"
|
|| die "no password set for $alias — run 'pass $alias' first"
|
||||||
local errfile; errfile=$(mktemp 2>/dev/null || echo "/tmp/larry-scp-direct.err.$$")
|
local errfile; errfile=$(mktemp 2>/dev/null || echo "/tmp/larry-scp-direct.err.$$")
|
||||||
|
# v0.8.18: shared DIRECT options via _direct_ssh_opts (was an inline copy).
|
||||||
|
# scp reads the same ssh-style -o options; only the port flag differs (-P).
|
||||||
sshpass -f "$credfile" scp -q \
|
sshpass -f "$credfile" scp -q \
|
||||||
-o "PreferredAuthentications=password" \
|
$(_direct_ssh_opts) \
|
||||||
-o "PubkeyAuthentication=no" \
|
|
||||||
-o "NumberOfPasswordPrompts=1" \
|
|
||||||
-o "StrictHostKeyChecking=accept-new" \
|
|
||||||
-o "ControlMaster=no" \
|
|
||||||
-o "ControlPath=none" \
|
|
||||||
-o "ConnectTimeout=$_DIRECT_CONNECT_TIMEOUT" \
|
|
||||||
-P "$port" \
|
-P "$port" \
|
||||||
"$src" "$dst" 2>"$errfile"
|
"$src" "$dst" 2>"$errfile"
|
||||||
local rc=$?
|
local rc=$?
|
||||||
@ -959,6 +976,30 @@ cmd_push() {
|
|||||||
[ -n "$alias" ] && [ -n "$local_path" ] && [ -n "$remote" ] \
|
[ -n "$alias" ] && [ -n "$local_path" ] && [ -n "$remote" ] \
|
||||||
|| die "usage: push <alias> <local_path> <remote_path>"
|
|| die "usage: push <alias> <local_path> <remote_path>"
|
||||||
[ -f "$local_path" ] || die "local file not found: $local_path"
|
[ -f "$local_path" ] || die "local file not found: $local_path"
|
||||||
|
|
||||||
|
# v0.8.18: DIRECT mode — symmetric with cmd_pull's direct branch. No
|
||||||
|
# ControlMaster (the host rejects multiplexing); the transfer uses _direct_scp
|
||||||
|
# (fresh per-command sshpass), and the post-transfer size verification rides a
|
||||||
|
# fresh per-command connection via _dispatch_remote → _run_direct. Without this
|
||||||
|
# branch, ssh_push (an exposed tool, used by nc_regression phase 4 to push
|
||||||
|
# cross-env input bundles) died "no open master" for any DIRECT-mode alias.
|
||||||
|
if _alias_is_direct "$alias"; then
|
||||||
|
local addr_port; addr_port=$(read_host_addr "$alias")
|
||||||
|
[ -n "$addr_port" ] || die "no such alias: $alias"
|
||||||
|
local _d_addr; _d_addr=$(printf '%s' "$addr_port" | cut -f1)
|
||||||
|
local local_size; local_size=$(coerce_int "$(wc -c < "$local_path" 2>/dev/null)" 0)
|
||||||
|
if _direct_scp "$alias" "$local_path" "$_d_addr:$remote"; then
|
||||||
|
local got
|
||||||
|
got=$(coerce_int "$(_dispatch_remote "$alias" "wc -c < $(printf '%q' "$remote") 2>/dev/null" 2>/dev/null)" 0)
|
||||||
|
if [ "$got" != "$local_size" ]; then
|
||||||
|
die "partial transfer: local=$local_size bytes, remote=$got bytes ($alias:$remote)"
|
||||||
|
fi
|
||||||
|
ok "pushed $local_path → $alias:$remote ($got bytes, direct)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
_resolve_open_master "$alias"
|
_resolve_open_master "$alias"
|
||||||
|
|
||||||
# v0.7.5: coerce_int on wc output — Cygwin wc.exe CR-taint defense.
|
# v0.7.5: coerce_int on wc output — Cygwin wc.exe CR-taint defense.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user