@arcforgelabs/flow 2026.11.2 → 2026.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env bash
2
+ # Flow command for an AI assistant (docs/AGENT_ACCESS.md, "AI assistant key").
3
+ # flow.sh setup <https://flow url> save this lab's Flow address, make the key
4
+ # once, print only the key's SHA-256
5
+ # flow.sh get <path> GET <FLOW_URL>/api/<path>
6
+ # flow.sh post <path> [json] POST with an optional JSON body
7
+ # flow.sh put <path> <json> PUT with a JSON body
8
+ # flow.sh delete <path> DELETE
9
+ # flow.sh health GET <FLOW_URL>/health (public, no key sent)
10
+ # flow.sh update replace this skill with the copy the
11
+ # saved Flow serves (its own release)
12
+ # The key stays in FLOW_AGENT_KEY_FILE (mode 600). It never goes on a command
13
+ # line or into the output.
14
+ set -euo pipefail
15
+
16
+ KEY_FILE="${FLOW_AGENT_KEY_FILE:-$HOME/.openclaw/credentials/flow-agent-key}"
17
+ URL_FILE="${KEY_FILE}.url"
18
+
19
+ die() { echo "flow.sh: $*" >&2; exit 2; }
20
+
21
+ # The key goes only to the Flow address saved at setup (or FLOW_URL). There is
22
+ # no default address, so a key never goes to another lab's Flow.
23
+ flow_url() {
24
+ local url="${FLOW_URL:-}"
25
+ [[ -n "$url" || ! -s "$URL_FILE" ]] || url="$(tr -d '\n' < "$URL_FILE")"
26
+ [[ "$url" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$ ]] || die "no Flow address: run flow.sh setup https://<your Flow>"
27
+ printf '%s' "${url%/}"
28
+ }
29
+
30
+ setup() {
31
+ local url="${1:-}"
32
+ if [[ -n "$url" ]]; then
33
+ [[ "$url" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$ ]] || die "the Flow address must be https://<host>"
34
+ mkdir -p "$(dirname "$URL_FILE")"
35
+ printf '%s\n' "${url%/}" > "$URL_FILE"
36
+ fi
37
+ echo "Flow address: $(flow_url)"
38
+ if [[ ! -s "$KEY_FILE" ]]; then
39
+ mkdir -p "$(dirname "$KEY_FILE")"
40
+ (umask 077 && openssl rand -hex 32 > "$KEY_FILE")
41
+ fi
42
+ chmod 600 "$KEY_FILE"
43
+ echo "FLOW_AGENT_KEY_SHA256=$(tr -d '\n' < "$KEY_FILE" | sha256sum | cut -d' ' -f1)"
44
+ }
45
+
46
+ call() {
47
+ local method="$1" path="${2:-}" body="${3:-}"
48
+ [[ -n "$path" ]] || die "give a path, for example: get invoicing/invoices?status=ready"
49
+ [[ -s "$KEY_FILE" ]] || die "no key yet: run flow.sh setup https://<your Flow> first"
50
+ local args=(-sS -X "$method" -K - -H 'Accept: application/json' -w '\nHTTP %{http_code}\n')
51
+ if [[ -n "$body" ]]; then args+=(-H 'Content-Type: application/json' --data-binary "$body"); fi
52
+ printf 'header = "Authorization: Bearer %s"\n' "$(tr -d '\n' < "$KEY_FILE")" \
53
+ | curl "${args[@]}" "$(flow_url)/api/${path#/}"
54
+ }
55
+
56
+ # Fetch SKILL.md and flow.sh from the saved Flow, so the skill always matches
57
+ # the release that Flow runs. Both files arrive first, then both replace the
58
+ # old ones; a bad answer changes nothing.
59
+ update() {
60
+ [[ -s "$KEY_FILE" ]] || die "no key yet: run flow.sh setup https://<your Flow> first"
61
+ local dir tmp file code
62
+ dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
63
+ tmp="$(mktemp -d "$dir/.update.XXXXXX")"
64
+ trap "rm -rf '$tmp'" EXIT
65
+ for file in SKILL.md flow.sh; do
66
+ code="$(printf 'header = "Authorization: Bearer %s"\n' "$(tr -d '\n' < "$KEY_FILE")" \
67
+ | curl -sS -K - -o "$tmp/$file" -w '%{http_code}' "$(flow_url)/api/agent-skill/flow/$file")" \
68
+ || die "could not reach $(flow_url)"
69
+ [[ "$code" == 200 && -s "$tmp/$file" ]] || die "$file: HTTP $code from $(flow_url); nothing changed"
70
+ done
71
+ head -n 1 "$tmp/flow.sh" | grep -q '^#!/usr/bin/env bash' && bash -n "$tmp/flow.sh" \
72
+ || die "flow.sh from $(flow_url) is not a valid script; nothing changed"
73
+ grep -q '^name: flow$' "$tmp/SKILL.md" || die "SKILL.md from $(flow_url) is not the flow skill; nothing changed"
74
+ chmod 755 "$tmp/flow.sh"
75
+ chmod 644 "$tmp/SKILL.md"
76
+ mv -f "$tmp/SKILL.md" "$dir/SKILL.md"
77
+ mv -f "$tmp/flow.sh" "$dir/flow.sh"
78
+ echo "Skill updated from $(flow_url)"
79
+ }
80
+
81
+ case "${1:-}" in
82
+ setup) setup "${2:-}" ;;
83
+ get) call GET "${2:-}" ;;
84
+ post) call POST "${2:-}" "${3:-}" ;;
85
+ put) [[ -n "${3:-}" ]] || die "put needs a JSON body"; call PUT "${2:-}" "$3" ;;
86
+ delete) call DELETE "${2:-}" ;;
87
+ health) curl -sS -H 'Accept: application/json' -w '\nHTTP %{http_code}\n' "$(flow_url)/health" ;;
88
+ update) update ;;
89
+ *) sed -n '2,12p' "$0"; exit 2 ;;
90
+ esac