Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e54629b
fix(send-file): add required --tag/--category/--project flags and iso…
May 14, 2026
ed9f0b7
fix(send-file): address review feedback - guard missing values, suppo…
May 14, 2026
6353325
fix(send-file): remove exec to allow trap cleanup, add empty-value gu…
May 14, 2026
1e73263
fix(send-file): add empty-value guard for --server= flag
May 14, 2026
b86f0fc
fix(send-file): address review feedback - directory check and exec co…
May 14, 2026
056972a
refactor(send-file): use realpath for cleaner path resolution
May 14, 2026
9cce285
fix(send-file): use saved filename in realpath error message
May 14, 2026
a2597ac
fix(send-file): address review feedback - unify errors, cross-platfor…
May 14, 2026
e89c2f9
fix(send-file): address review feedback - idempotent cleanup, cp --, …
May 14, 2026
1fd2b69
fix: initialize tmpdir and use defensive access in cleanup
May 14, 2026
37c81f2
fix(send-file): add readlink -f fallback and simplify tmpdir init
May 14, 2026
16d635d
fix(send-file): address review feedback - URL validation, trim whites…
May 14, 2026
ca29f29
fix(send-file): trim params before required validation, add HTML exte…
May 14, 2026
70f26e3
fix(send-file): address review - remove duplicate assignment, case-in…
May 14, 2026
15e8d26
fix(send-file): address review - Bash 3.2 compat, move trim(), safer …
May 14, 2026
9ffd1bc
fix(send-file): address review - reject flag-like option values, add …
May 14, 2026
0af4461
refactor(send-file): address review - extract require_value helper, s…
May 14, 2026
02185dd
refactor(send-file): address review - inline trim during parsing, add…
May 14, 2026
b0e7a94
refactor(send-file): address review - multi-line case branches, --hel…
May 14, 2026
84de2ac
fix(send-file): address review - document trim(), extract allowed_ext…
May 14, 2026
eecbdeb
fix(send-file): address review - case-based ext check, test readlink …
May 14, 2026
3a41045
fix(send-file.sh): address review feedback
May 14, 2026
58cc252
fix(send-file.sh): support POSIX end-of-options separator (--)
May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions skills/sth/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ Defaults:
3. Register an HTML file:

```bash
bash scripts/send-file.sh /absolute/or/relative/file.html [server_url]
bash scripts/send-file.sh /absolute/or/relative/file.html --tag TAG --category CAT --project PROJ [--server URL]
```

The command uploads the HTML file and sibling assets from the same directory, then prints a session URL like `http://192.168.2.14:3939/s/<id>/`.
The command copies the HTML file to an isolated temporary directory (avoiding packaging unrelated files), uploads it, and prints a session URL like `http://192.168.2.14:3939/s/<id>/`.

If you need to include sibling resources (CSS, JS, images) alongside the HTML file, use `sth send` directly with the directory containing those resources instead of `send-file.sh`:

```bash
sth send ./my-project-dir --tag demo --category preview --project myproj [--server URL]
```

When calling `sth send` directly, `--tag` accepts multiple comma-separated values:

Expand Down
152 changes: 148 additions & 4 deletions skills/sth/scripts/send-file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,159 @@

set -euo pipefail

usage() {
cat >&2 <<EOF
Usage: send-file.sh <file.html> [options]

Options:
--tag TAG Tag(s) for the session (required)
--category CAT Category for the session (required)
--project PROJ Project for the session (required)
--server URL Server URL (default: \$STATIC_HTML_SERVER_URL or http://127.0.0.1:3939)

Note: Only the specified HTML file is uploaded. If you need to include sibling
resources (CSS/JS/images), use 'sth send' directly with the appropriate directory.
EOF
exit "${1:-1}"
}

# Strips leading/trailing whitespace and collapses internal newlines to spaces.
trim() {
local v="$1"
v="${v#"${v%%[![:space:]]*}"}"
v="${v%"${v##*[![:space:]]}"}"
v="${v//$'\n'/ }"
printf '%s' "$v"
}

if [ "$#" -lt 1 ]; then
echo "Usage: send-file.sh <file.html> [server_url]" >&2
usage 1
fi
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
usage 0
fi

target_file=""
server_url="${STATIC_HTML_SERVER_URL:-http://127.0.0.1:3939}"
tag=""
category=""
project=""

while [ "$#" -gt 0 ]; do
case "$1" in
--help|-h)
usage 0 ;;
--tag)
if [ -z "${2:-}" ]; then echo "Error: --tag requires a value" >&2; usage; fi
tag="$(trim "$2")"; shift 2 ;;
--tag=*)
tag="$(trim "${1#--tag=}")"; [ -z "$tag" ] && { echo "Error: --tag requires a value" >&2; usage; }; shift ;;
--category)
if [ -z "${2:-}" ]; then echo "Error: --category requires a value" >&2; usage; fi
category="$(trim "$2")"; shift 2 ;;
--category=*)
category="$(trim "${1#--category=}")"; [ -z "$category" ] && { echo "Error: --category requires a value" >&2; usage; }; shift ;;
--project)
if [ -z "${2:-}" ]; then echo "Error: --project requires a value" >&2; usage; fi
project="$(trim "$2")"; shift 2 ;;
--project=*)
project="$(trim "${1#--project=}")"; [ -z "$project" ] && { echo "Error: --project requires a value" >&2; usage; }; shift ;;
--server)
if [ -z "${2:-}" ]; then echo "Error: --server requires a value" >&2; usage; fi
server_url="$(trim "$2")"; shift 2 ;;
--server=*)
server_url="$(trim "${1#--server=}")"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;;
--)
shift; break ;;
-*)
echo "Unknown option: $1" >&2; usage ;;
*)
if [ -z "$target_file" ]; then
target_file="$1"; shift
else
echo "Unexpected argument: $1" >&2; usage
fi ;;
esac
done

if [ -z "$target_file" ]; then
echo "Error: <file.html> is required" >&2
usage
fi

if [[ ! "$server_url" =~ ^https?:// ]]; then
echo "Error: server URL must start with http:// or https://" >&2
exit 1
fi

if [ -z "$tag" ]; then
echo "Error: --tag is required" >&2
usage
fi
if [ -z "$category" ]; then
echo "Error: --category is required" >&2
usage
fi
if [ -z "$project" ]; then
echo "Error: --project is required" >&2
usage
fi

original_file="$target_file"
if command -v realpath >/dev/null 2>&1; then
target_file="$(realpath -- "$target_file" 2>/dev/null)" || {
echo "Error: file not found or inaccessible: $original_file" >&2
exit 1
}
elif command -v readlink >/dev/null 2>&1 && readlink -f / >/dev/null 2>&1; then
target_file="$(readlink -f -- "$target_file" 2>/dev/null)" || {
echo "Error: file not found or inaccessible: $original_file" >&2
exit 1
}
else
# Best-effort path resolution via cd+pwd; correctness is guaranteed by the -f check below.
# Limitations compared to realpath/readlink: symlinks are not resolved, and a dangling
# symlink will use the link name as basename rather than the target name.
_base="$(basename "$target_file")"
_dir="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)" || {
echo "Error: cannot resolve path: $original_file" >&2
exit 1
}
target_file="$_dir/$_base"
if [ ! -e "$target_file" ]; then
echo "Error: file not found or inaccessible: $original_file" >&2
exit 1
fi
fi
if [ ! -f "$target_file" ]; then
echo "Error: file not found: $target_file" >&2
exit 1
fi
filename="$(basename "$target_file")"
lowername="$(printf '%s' "$filename" | tr '[:upper:]' '[:lower:]')"
case "$lowername" in
*.html|*.htm) ;;
*) echo "Error: file must have .html or .htm extension: $filename" >&2; exit 1 ;;
esac

tmpdir="$(mktemp -d)"
chmod 700 "$tmpdir"
cleanup() { [ -d "${tmpdir:-}" ] && rm -rf "$tmpdir"; }
trap cleanup EXIT

cp -- "$target_file" "$tmpdir/"

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_dir="$("$script_dir/bootstrap-repo.sh")"
target_file="$1"
server_url="${2:-${STATIC_HTML_SERVER_URL:-http://127.0.0.1:3939}}"
if [ ! -d "$repo_dir" ]; then
echo "Error: bootstrap-repo.sh did not return a valid directory: $repo_dir" >&2
exit 1
fi

cd "$repo_dir"
exec "$repo_dir/dist/sth" send "$target_file" --server "$server_url"
# Do NOT use exec here; the shell must remain alive so the EXIT trap can clean up $tmpdir.
"$repo_dir/dist/sth" send "$tmpdir/$filename" \
--server "$server_url" \
--tag "$tag" \
--category "$category" \
--project "$project"
Loading