make-api-episode18-demo.sh bash
73 lines 3.0 KB
Raw
sha256:2395bda966b2f4b80995fa7ec495b8e2fedf0d97f183203ee46b9593f525b01d Add Episode 18 script (MuseHub API) and raw curl+MSign demo… Sonnet 5 patch 16 hours ago
1 #!/usr/bin/env bash
2 # Drives the raw MuseHub REST API for Episode 18 ("MuseHub API") -- no
3 # `muse hub` subcommands, no MCP, just curl and a hand-signed MSign header.
4 # Requires the local MuseHub dev stack running.
5 set -euo pipefail
6
7 HUB="https://localhost:1337"
8
9 echo "=== Part 1: the API describes itself ==="
10 curl -sk "$HUB/api/openapi.json" | python3 -c "
11 import json, sys
12 d = json.load(sys.stdin)
13 print('title:', d['info']['title'])
14 print('version:', d['info']['version'])
15 print('paths:', len(d['paths']))
16 "
17
18 echo
19 echo "=== Part 2: a raw GET, signed by hand, no muse hub involved ==="
20 echo "--- mistake: forgot to sign the query string ---"
21 HEADER=$(muse sign header --method GET --path "/api/repos" --hub "$HUB" --json | python3 -c "import json,sys; print(json.load(sys.stdin)['header_value'])")
22 curl -sk -X GET "$HUB/api/repos?limit=3" -H "Authorization: $HEADER" | python3 -m json.tool
23
24 echo "--- fix: sign the full path including the query string ---"
25 HEADER=$(muse sign header --method GET --path "/api/repos?limit=3" --hub "$HUB" --json | python3 -c "import json,sys; print(json.load(sys.stdin)['header_value'])")
26 curl -sk -X GET "$HUB/api/repos?limit=3" -H "Authorization: $HEADER" | python3 -c "
27 import json, sys
28 d = json.load(sys.stdin)
29 for r in d['repos']:
30 print(' -', r['owner'] + '/' + r['slug'])
31 "
32
33 echo
34 echo "=== Part 3: finding the real write endpoint from the schema, not guesswork ==="
35 curl -sk "$HUB/api/openapi.json" | python3 -c "
36 import json, sys
37 d = json.load(sys.stdin)
38 for path, methods in d['paths'].items():
39 if path == '/api/repos/{repo_id}/issues':
40 print(list(methods.keys()), path)
41 "
42
43 REPO_ID=$(curl -sk "$HUB/api/repos?limit=100" \
44 -H "Authorization: $(muse sign header --method GET --path '/api/repos?limit=100' --hub "$HUB" --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["header_value"])')" \
45 | python3 -c "
46 import json, sys
47 d = json.load(sys.stdin)
48 for r in d['repos']:
49 if r['slug'] == 'wire-episode17':
50 print(r['repoId'])
51 break
52 ")
53 echo "resolved repo_id: $REPO_ID"
54
55 echo
56 echo "=== Part 4: a raw signed POST -- creating a real issue ==="
57 ENCODED=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$REPO_ID")
58 BODY='{"title":"Real issue created via raw curl + MSign","body":"No muse CLI, no MCP -- just curl and a hand-signed header."}'
59 printf '%s' "$BODY" > /tmp/ep18-issue-body.json
60 PATH_ONLY="/api/repos/$ENCODED/issues"
61 HEADER=$(muse sign header --method POST --path "$PATH_ONLY" --hub "$HUB" --body-file /tmp/ep18-issue-body.json --json | python3 -c "import json,sys; print(json.load(sys.stdin)['header_value'])")
62 curl -sk -X POST "$HUB$PATH_ONLY" \
63 -H "Authorization: $HEADER" \
64 -H "Content-Type: application/json" \
65 --data-binary @/tmp/ep18-issue-body.json | python3 -m json.tool
66 rm -f /tmp/ep18-issue-body.json
67
68 echo
69 echo "=== Part 5: muse sign curl -- the convenience wrapper ==="
70 muse sign curl --method GET --url "$HUB/api/repos?limit=2" --hub "$HUB"
71
72 echo
73 echo "Demo complete."
File History 1 commit
sha256:2395bda966b2f4b80995fa7ec495b8e2fedf0d97f183203ee46b9593f525b01d Add Episode 18 script (MuseHub API) and raw curl+MSign demo… Sonnet 5 patch 16 hours ago