|
41 | 41 | | select(.key | split(" ")[0:2] | join(" ") == $key) |
42 | 42 | | .value[] ] |
43 | 43 | | unique_by(.namespace) |
44 | | - | map("\(.name)=\(.namespace)") | join(",")') |
| 44 | + | map("\(.name)=\(.namespace)=\(.identity // "")") | join(",")') |
45 | 45 | fi |
46 | 46 |
|
47 | 47 | # Output extra environment= directive for authorized_keys line |
|
71 | 71 | fi |
72 | 72 | } |
73 | 73 |
|
74 | | - # Parse SEED_REPOS into parallel arrays: REPO_NAMES and REPO_NS |
| 74 | + # Parse SEED_REPOS into parallel arrays: REPO_NAMES, REPO_NS, REPO_IDENTITY |
| 75 | + # Format: name=namespace=identity (3-part), or name=namespace (2-part legacy) |
75 | 76 | declare -a REPO_NAMES=() |
76 | 77 | declare -a REPO_NS=() |
| 78 | + declare -a REPO_IDENTITY=() |
77 | 79 | if [ -n "$REPOS_RAW" ]; then |
78 | 80 | IFS=',' read -ra PAIRS <<< "$REPOS_RAW" |
79 | 81 | for pair in "''${PAIRS[@]}"; do |
80 | | - REPO_NAMES+=("''${pair%%=*}") |
81 | | - REPO_NS+=("''${pair#*=}") |
| 82 | + # Split on '=' — name=namespace=identity |
| 83 | + IFS='=' read -r _name _ns _id <<< "$pair" |
| 84 | + REPO_NAMES+=("$_name") |
| 85 | + REPO_NS+=("$_ns") |
| 86 | + REPO_IDENTITY+=("''${_id:-}") |
82 | 87 | done |
83 | 88 | fi |
84 | 89 |
|
@@ -151,34 +156,42 @@ let |
151 | 156 | FOLLOW=false |
152 | 157 | LINES="" |
153 | 158 |
|
| 159 | + ARG3="" |
154 | 160 | while [ $# -gt 0 ]; do |
155 | 161 | case "$1" in |
156 | 162 | --json) JSON_OUT=true ;; |
157 | 163 | --follow) FOLLOW=true ;; |
158 | 164 | -f) FOLLOW=true ;; |
159 | 165 | --lines) shift; LINES="''${1:-}" ;; |
160 | | - *) if [ -z "$ARG" ]; then ARG="$1"; elif [ -z "$ARG2" ]; then ARG2="$1"; fi ;; |
| 166 | + *) if [ -z "$ARG" ]; then ARG="$1"; elif [ -z "$ARG2" ]; then ARG2="$1"; elif [ -z "$ARG3" ]; then ARG3="$1"; fi ;; |
161 | 167 | esac |
162 | 168 | shift |
163 | 169 | done |
164 | 170 |
|
165 | 171 | case "$ACTION" in |
166 | 172 | plant) |
167 | 173 | if [ -z "$ARG" ] || [ -z "$ARG2" ]; then |
168 | | - echo "usage: plant <flake-uri> <invite-code>" >&2 |
| 174 | + echo "usage: plant <flake-uri> <invite-code> [<signature>]" >&2 |
169 | 175 | echo "" >&2 |
170 | 176 | echo "examples:" >&2 |
171 | 177 | echo " plant github:me/my-app a3f8c2e1" >&2 |
172 | 178 | echo " plant silo:my-app a3f8c2e1" >&2 |
| 179 | + echo " plant silo:my-app a3f8c2e1 <base64-ssh-signature>" >&2 |
173 | 180 | exit 1 |
174 | 181 | fi |
175 | 182 | if [ -z "$KEY_BLOB" ]; then |
176 | 183 | echo "error: key identity not available" >&2 |
177 | 184 | exit 1 |
178 | 185 | fi |
| 186 | + # Build JSON payload — include signature if provided |
| 187 | + PLANT_JSON="{\"flakeUri\":\"$ARG\",\"inviteCode\":\"$ARG2\",\"keyBlob\":\"$KEY_BLOB\"" |
| 188 | + if [ -n "$ARG3" ]; then |
| 189 | + PLANT_JSON="$PLANT_JSON,\"signature\":\"$ARG3\"" |
| 190 | + fi |
| 191 | + PLANT_JSON="$PLANT_JSON}" |
179 | 192 | RESULT=$($CURL -sf -X POST "$API/plant" \ |
180 | 193 | -H "Content-Type: application/json" \ |
181 | | - -d "{\"flakeUri\":\"$ARG\",\"inviteCode\":\"$ARG2\",\"keyBlob\":\"$KEY_BLOB\"}") || { |
| 194 | + -d "$PLANT_JSON") || { |
182 | 195 | echo "error: plant failed" >&2 |
183 | 196 | exit 1 |
184 | 197 | } |
|
187 | 200 | echo "error: $ERROR" >&2 |
188 | 201 | exit 1 |
189 | 202 | fi |
190 | | - echo "$RESULT" | $JQ -r '"planted \(.flakeUri)\n name: \(.name)\n namespace: \(.namespace)"' |
| 203 | + echo "$RESULT" | $JQ -r '"planted \(.flakeUri)\n name: \(.name)\n namespace: \(.namespace)" + (if .identity != "" then "\n identity: \(.identity)" else "" end)' |
| 204 | + ;; |
| 205 | +
|
| 206 | + replant) |
| 207 | + if [ -z "$ARG" ] || [ -z "$ARG2" ]; then |
| 208 | + echo "usage: replant <identity-cid> <new-flake-uri>" >&2 |
| 209 | + echo "" >&2 |
| 210 | + echo "change the source URI for an identity-based repo." >&2 |
| 211 | + echo "your SSH key must be in .authorized_keys of the new repo," >&2 |
| 212 | + echo "and the new repo must have the same .seed-identity file." >&2 |
| 213 | + echo "" >&2 |
| 214 | + echo "examples:" >&2 |
| 215 | + echo " replant k51qzi5... silo:my-app" >&2 |
| 216 | + echo " replant k51qzi5... github:me/my-app" >&2 |
| 217 | + exit 1 |
| 218 | + fi |
| 219 | + if [ -z "$KEY_BLOB" ]; then |
| 220 | + echo "error: key identity not available" >&2 |
| 221 | + exit 1 |
| 222 | + fi |
| 223 | + RESULT=$($CURL -sf -X POST "$API/replant" \ |
| 224 | + -H "Content-Type: application/json" \ |
| 225 | + -d "{\"identity\":\"$ARG\",\"newFlakeUri\":\"$ARG2\",\"keyBlob\":\"$KEY_BLOB\"}") || { |
| 226 | + echo "error: replant failed" >&2 |
| 227 | + exit 1 |
| 228 | + } |
| 229 | + ERROR=$(echo "$RESULT" | $JQ -r '.error // empty') |
| 230 | + if [ -n "$ERROR" ]; then |
| 231 | + echo "error: $ERROR" >&2 |
| 232 | + exit 1 |
| 233 | + fi |
| 234 | + echo "$RESULT" | $JQ -r '"replanted → \(.flakeUri)\n name: \(.name)\n namespace: \(.namespace)\n identity: \(.identity)"' |
191 | 235 | ;; |
192 | 236 |
|
193 | 237 | status) |
|
228 | 272 | if [ "$JSON_OUT" = true ]; then |
229 | 273 | echo "$ALL_JSON" | $JQ . |
230 | 274 | else |
| 275 | + # Build identity lookup for display |
| 276 | + declare -A REPO_ID_MAP |
| 277 | + for i in "''${!REPO_NAMES[@]}"; do |
| 278 | + REPO_ID_MAP["''${REPO_NAMES[$i]}"]="''${REPO_IDENTITY[$i]:-}" |
| 279 | + done |
| 280 | +
|
231 | 281 | echo "$ALL_JSON" | $JQ -r '.[] | |
232 | 282 | .data.namespace as $ns | |
233 | 283 | "\u001b[1;4m\(.repo)\u001b[0m \u001b[2m\($ns)\u001b[0m", |
|
239 | 289 | " restarts=\(.value.restarts)" + |
240 | 290 | " age=\(.value.age)" |
241 | 291 | ), ""' |
| 292 | +
|
| 293 | + # Show identity CIDs if present |
| 294 | + for i in "''${!REPO_NAMES[@]}"; do |
| 295 | + local id="''${REPO_IDENTITY[$i]:-}" |
| 296 | + if [ -n "$id" ]; then |
| 297 | + printf ' \033[2midentity: %s\033[0m\n' "$id" |
| 298 | + fi |
| 299 | + done |
242 | 300 | fi |
243 | 301 | fi |
244 | 302 | ;; |
@@ -340,22 +398,24 @@ let |
340 | 398 | echo "seed shell — manage your seed instances" |
341 | 399 | echo "" |
342 | 400 | echo "commands:" |
343 | | - echo " plant <flake-uri> <code> register a repo with an invite code" |
344 | | - echo " status [repo] show instance status (default: all repos)" |
345 | | - echo " logs <[repo/]instance> show recent logs (default: 100 lines)" |
346 | | - echo " restart <[repo/]instance> restart an instance" |
347 | | - echo " keys <[repo/]instance> show age public key (for sops encryption)" |
348 | | - echo " help show this help" |
| 401 | + echo " plant <flake-uri> <code> [sig] register a repo with an invite code" |
| 402 | + echo " replant <identity> <new-uri> change source URI (identity preserved)" |
| 403 | + echo " status [repo] show instance status (default: all repos)" |
| 404 | + echo " logs <[repo/]instance> show recent logs (default: 100 lines)" |
| 405 | + echo " restart <[repo/]instance> restart an instance" |
| 406 | + echo " keys <[repo/]instance> show age public key (for sops encryption)" |
| 407 | + echo " help show this help" |
349 | 408 | echo "" |
350 | 409 | echo "examples:" |
351 | | - echo " plant github:me/app a3f8 register with invite code" |
352 | | - echo " plant silo:my-app a3f8 register a silo repo" |
353 | | - echo " status status of all repos" |
354 | | - echo " status seed status of the 'seed' repo" |
355 | | - echo " logs web logs for 'web' (auto-resolves repo)" |
356 | | - echo " logs seed/web -f follow logs for 'web' in 'seed' repo" |
| 410 | + echo " plant github:me/app a3f8 register with invite code" |
| 411 | + echo " plant silo:my-app a3f8 <sig> register with identity signature" |
| 412 | + echo " replant k51qzi5... silo:my-app change source URI" |
| 413 | + echo " status status of all repos" |
| 414 | + echo " status seed status of the 'seed' repo" |
| 415 | + echo " logs web logs for 'web' (auto-resolves repo)" |
| 416 | + echo " logs seed/web -f follow logs for 'web' in 'seed' repo" |
357 | 417 | echo " restart shoot-demo/shoot-demo" |
358 | | - echo " keys web age public key for sops encryption" |
| 418 | + echo " keys web age public key for sops encryption" |
359 | 419 | echo "" |
360 | 420 | echo "flags:" |
361 | 421 | echo " --json output raw JSON (for scripting)" |
|
0 commit comments