Process and size an image for a Sky Map celestial info card. Prompts for an image (URL or local path), optional crop, and output filename, then converts to 480×800 WebP and saves to the correct assets directory. Trigger on "process celestial image", "add image for info card", "convert image for Sky Map", etc. ARGUMENTS: "[source_url_or_path] [category/output_name] [crop x1,y1,x2,y2]"
Convert and size an image for use in a Sky Map celestial info card.
app/src/main/assets/celestial_images/<category>/<name>.webpobject_info.json as "imageKey": "<category>/<name>.webp"$ARGUMENTS may contain (all optional — prompt for missing ones):
celestial_images/ — e.g. messier/hubble_m1 or stars/eso_sirius
(.webp extension added automatically)x1,y1,x2,y2 in source-image pixelsIf $ARGUMENTS is empty, ask the user:
<category>/<filename> without extension.
Valid categories: constellations, stars, messier, planets.
Suggest a name based on the subject (e.g. messier/hubble_m42).x1,y1,x2,y2 to extract a sub-region before resizing.
Skip if the subject is already well-centred and the image is in portrait or square orientation.Validate before proceeding — reject and ask again if any check fails:
constellations, stars, messier, planets.[a-z0-9_]+ only.https://. Reject http://, file://, and all other schemes.
Also reject any URL whose hostname resolves to a private/loopback range — this is enforced
again at download time (Step 2), but reject obviously bad hostnames early (e.g. localhost,
127.*, 10.*, 192.168.*, 169.254.*, [::1]).[a-zA-Z0-9/_.\- ]+ — reject any path containing shell
metacharacters (", ', `, $, !, &, |, ;, (, ), <, >, \n, etc.).
If the user provides such a path, ask them to move or rename the file.Never interpolate user-provided values into shell command strings. Instead, use the Write tool to write a JSON config file. Python will read from it — the values never pass through the shell.
Use the Write tool to create /tmp/celestial_cfg.json with the following structure
(substituting the actual validated values you collected):
{
"source": "/validated/local/path/or/https://validated.url/image.jpg",
"category": "messier",
"name": "hubble_m42",
"crop": [x1, y1, x2, y2]
}
Set "crop" to null if no crop was requested.
If the source is a URL, download it using the script below. All inputs are read from the config file — no user data ever appears in a shell command line.
python3 - <<'EOF'
import json, os, socket, tempfile, urllib.parse, urllib.request
import ipaddress
with open('/tmp/celestial_cfg.json') as f:
cfg = json.load(f)
url = cfg['source']
# Validate scheme (defence-in-depth; Step 1 already checked this)
parsed = urllib.parse.urlparse(url)
if parsed.scheme != 'https':
raise SystemExit(f'rejected scheme: {parsed.scheme!r}')
# Resolve hostname and block private/loopback ranges (SSRF guard)
host = parsed.hostname