create-egregore 0.21.4 → 0.21.6
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.
- package/assets/egregore-launcher.js +88 -14
- package/bin/cli.js +6 -3
- package/lib/api.js +26 -22
- package/lib/connect-runtime.js +13 -8
- package/lib/hosted-api.js +70 -0
- package/lib/local.js +3 -17
- package/lib/setup.js +6 -1
- package/package.json +2 -2
- package/runtime/claude/.claude/context/telemetry.md +8 -18
- package/runtime/claude/.claude/skills/emissary/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/telemetry/SKILL.md +3 -3
- package/runtime/claude/.claude/skills/update/SKILL.md +46 -32
- package/runtime/claude/bin/handoff-run.sh +1 -1
- package/runtime/claude/bin/hosting-ops.sh +5 -243
- package/runtime/claude/bin/lib/framework-update.sh +3 -1
- package/runtime/claude/bin/lib/git-sync.sh +2 -2
- package/runtime/claude/bin/notify.sh +1 -1
- package/runtime/claude/bin/post-update.sh +13 -0
- package/runtime/claude/bin/publish-artifact.sh +1 -1
- package/runtime/claude/bin/telemetry.sh +7 -10
- package/runtime/claude/egregore_runtime/contracts.py +38 -0
- package/runtime/claude/egregore_runtime/member_authority.py +405 -0
- package/runtime/claude/egregore_runtime/upgrade.py +41 -2
- package/runtime/claude/manifest.json +1 -1
- package/runtime/codex/bin/handoff-run.sh +1 -1
- package/runtime/codex/bin/hosting-ops.sh +5 -243
- package/runtime/codex/bin/lib/framework-update.sh +3 -1
- package/runtime/codex/bin/lib/git-sync.sh +2 -2
- package/runtime/codex/bin/notify.sh +1 -1
- package/runtime/codex/bin/post-update.sh +13 -0
- package/runtime/codex/bin/publish-artifact.sh +1 -1
- package/runtime/codex/bin/telemetry.sh +7 -10
- package/runtime/codex/egregore_runtime/contracts.py +38 -0
- package/runtime/codex/egregore_runtime/member_authority.py +405 -0
- package/runtime/codex/egregore_runtime/upgrade.py +41 -2
- package/runtime/codex/manifest.json +1 -1
- package/runtime/pi/bin/handoff-run.sh +1 -1
- package/runtime/pi/bin/hosting-ops.sh +5 -243
- package/runtime/pi/bin/lib/framework-update.sh +3 -1
- package/runtime/pi/bin/lib/git-sync.sh +2 -2
- package/runtime/pi/bin/notify.sh +1 -1
- package/runtime/pi/bin/post-update.sh +13 -0
- package/runtime/pi/bin/publish-artifact.sh +1 -1
- package/runtime/pi/bin/telemetry.sh +7 -10
- package/runtime/pi/egregore_runtime/contracts.py +38 -0
- package/runtime/pi/egregore_runtime/member_authority.py +405 -0
- package/runtime/pi/egregore_runtime/upgrade.py +41 -2
- package/runtime/pi/manifest.json +1 -1
- package/runtime/prime/bin/handoff-run.sh +1 -1
- package/runtime/prime/bin/hosting-ops.sh +5 -243
- package/runtime/prime/bin/lib/framework-update.sh +3 -1
- package/runtime/prime/bin/lib/git-sync.sh +2 -2
- package/runtime/prime/bin/notify.sh +1 -1
- package/runtime/prime/bin/post-update.sh +13 -0
- package/runtime/prime/bin/publish-artifact.sh +1 -1
- package/runtime/prime/bin/telemetry.sh +7 -10
- package/runtime/prime/egregore_runtime/contracts.py +38 -0
- package/runtime/prime/egregore_runtime/member_authority.py +405 -0
- package/runtime/prime/egregore_runtime/upgrade.py +41 -2
- package/runtime/prime/manifest.json +1 -1
- package/runtime/claude/bin/build-sealed-dataroom.mjs +0 -722
- package/runtime/codex/bin/build-sealed-dataroom.mjs +0 -722
- package/runtime/pi/bin/build-sealed-dataroom.mjs +0 -722
- package/runtime/prime/bin/build-sealed-dataroom.mjs +0 -722
|
@@ -1,248 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# Retained as an inert compatibility entry point for older installations.
|
|
2
3
|
set -euo pipefail
|
|
3
4
|
set +vx
|
|
4
|
-
umask 077
|
|
5
|
-
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
source "$SCRIPT_DIR/bin/lib/config.sh"
|
|
11
|
-
|
|
12
|
-
usage() {
|
|
13
|
-
echo 'Usage: bash bin/hosting-ops.sh status|enable|wait-ready|users <slug>' >&2
|
|
14
|
-
echo ' bash bin/hosting-ops.sh ssh <slug> [--] <remote command…>' >&2
|
|
15
|
-
echo ' bash bin/hosting-ops.sh scp <slug> <local file> <remote path>' >&2
|
|
16
|
-
echo ' bash bin/hosting-ops.sh deploy-template <slug> [--name <version>]' >&2
|
|
17
|
-
echo ' bash bin/hosting-ops.sh restart-workspace <slug> <workspace id>' >&2
|
|
18
|
-
echo ' bash bin/hosting-ops.sh user <slug> <github username>' >&2
|
|
19
|
-
echo ' bash bin/hosting-ops.sh list' >&2
|
|
20
|
-
exit 2
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
die() { printf 'hosting-ops: %s\n' "$*" >&2; exit 1; }
|
|
24
|
-
|
|
25
|
-
[ "$#" -ge 1 ] || usage
|
|
26
|
-
operation="$1"
|
|
27
|
-
shift
|
|
28
|
-
case "$operation" in
|
|
29
|
-
list) [ "$#" -eq 0 ] || usage ;;
|
|
30
|
-
status|enable|wait-ready|users) [ "$#" -eq 1 ] || usage ;;
|
|
31
|
-
user|restart-workspace) [ "$#" -eq 2 ] && [ -n "$2" ] || usage ;;
|
|
32
|
-
scp) [ "$#" -eq 3 ] && [ -n "$2" ] && [ -n "$3" ] || usage ;;
|
|
33
|
-
ssh) [ "$#" -ge 2 ] || usage ;;
|
|
34
|
-
deploy-template)
|
|
35
|
-
[ "$#" -eq 1 ] || { [ "$#" -eq 3 ] && [ "$2" = --name ] && [ -n "$3" ]; } || usage
|
|
36
|
-
;;
|
|
37
|
-
*) usage ;;
|
|
38
|
-
esac
|
|
39
|
-
slug="${1-}"
|
|
40
|
-
if [ "$operation" != list ]; then
|
|
41
|
-
# Slugs are URL path/query components, never shell source.
|
|
42
|
-
case "$slug" in ''|*[!a-zA-Z0-9_-]*) usage ;; esac
|
|
43
|
-
shift
|
|
44
|
-
fi
|
|
45
|
-
if [ "$operation" = ssh ]; then
|
|
46
|
-
if [ "${1-}" = -- ]; then shift; fi
|
|
47
|
-
[ "$#" -ge 1 ] && [ -n "$1" ] || usage
|
|
48
|
-
# A quoted command is unchanged; separate words use SSH's usual space join.
|
|
49
|
-
remote_command="$*"
|
|
50
|
-
fi
|
|
51
|
-
if [ "$operation" = restart-workspace ]; then
|
|
52
|
-
case "$1" in *[!a-zA-Z0-9_-]*) usage ;; esac
|
|
53
|
-
fi
|
|
54
|
-
case "$operation" in
|
|
55
|
-
ssh|scp|deploy-template|restart-workspace)
|
|
56
|
-
command -v sshpass >/dev/null 2>&1 || die 'sshpass is required; install via brew install hudochenkov/sshpass/sshpass'
|
|
57
|
-
;;
|
|
58
|
-
esac
|
|
59
|
-
|
|
60
|
-
scratch=$(mktemp -d "${TMPDIR:-/tmp}/hosting-ops.XXXXXX")
|
|
61
|
-
trap 'rm -rf -- "$scratch"' EXIT
|
|
62
|
-
trap 'exit 1' HUP INT TERM
|
|
63
|
-
|
|
64
|
-
gateway() {
|
|
65
|
-
bash "$SCRIPT_DIR/bin/api-call.sh" "$@" --auth github || die 'API gateway request failed'
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
body_error() {
|
|
69
|
-
jq -er 'if type == "object" then (.error // .detail // (if .status == "error" then "request failed" else empty end)) else empty end | select(. != false and . != "") | if type == "string" then . else tojson end' "$1" 2>/dev/null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
check_body() {
|
|
73
|
-
local detail
|
|
74
|
-
jq -e 'type == "object"' "$1" >/dev/null 2>&1 || die 'invalid response body'
|
|
75
|
-
if detail=$(body_error "$1"); then die "$detail"; fi
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
credentials() {
|
|
79
|
-
local failure="no hosted VPS or missing credentials for $slug"
|
|
80
|
-
bash "$SCRIPT_DIR/bin/api-call.sh" GET "/api/hosting/credentials/$slug" --auth github --out "$scratch/credentials.json" 2>/dev/null \
|
|
81
|
-
|| die "$failure"
|
|
82
|
-
ip=$(jq -r '.ip | select(type == "string")' "$scratch/credentials.json" 2>/dev/null) || die "$failure"
|
|
83
|
-
vps_password=$(jq -r '.password | select(type == "string")' "$scratch/credentials.json" 2>/dev/null) || die "$failure"
|
|
84
|
-
[ -n "$ip" ] && [ -n "$vps_password" ] || die "$failure"
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
vps_ssh() {
|
|
88
|
-
SSHPASS="$vps_password" sshpass -e ssh -o StrictHostKeyChecking=no "root@$ip" "$@"
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
vps_scp() {
|
|
92
|
-
SSHPASS="$vps_password" sshpass -e scp -o StrictHostKeyChecking=no "$1" "root@$ip:$2"
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
coder_login() {
|
|
96
|
-
# jq reads the password from stdin, never a --arg value.
|
|
97
|
-
printf '%s' "$vps_password" | jq -Rs '{email:"admin@egregore.xyz", password:.}' > "$scratch/login.json"
|
|
98
|
-
vps_ssh "curl -q -sS -H 'Content-Type: application/json' --data-binary @- http://localhost/api/v2/users/login" \
|
|
99
|
-
< "$scratch/login.json" > "$scratch/login-response.json" 2>/dev/null || die 'Coder login failed'
|
|
100
|
-
coder_token=$(jq -er '.session_token | select(type == "string" and length > 0)' "$scratch/login-response.json" 2>/dev/null) \
|
|
101
|
-
|| die 'Coder login failed'
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
curl_config_value() {
|
|
105
|
-
# Curl config quoting, using builtins so secret values stay off exec argv.
|
|
106
|
-
local value="$2"
|
|
107
|
-
value="${value//\\/\\\\}"
|
|
108
|
-
value="${value//\"/\\\"}"
|
|
109
|
-
value="${value//$'\n'/\\n}"
|
|
110
|
-
value="${value//$'\r'/\\r}"
|
|
111
|
-
value="${value//$'\t'/\\t}"
|
|
112
|
-
printf '%s = "%s"\n' "$1" "$value"
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
coder_request() {
|
|
116
|
-
local code
|
|
117
|
-
{
|
|
118
|
-
curl_config_value header "Coder-Session-Token: $coder_token"
|
|
119
|
-
curl_config_value url "http://localhost/api/v2$2"
|
|
120
|
-
curl_config_value request "$1"
|
|
121
|
-
if [ -n "${3-}" ]; then
|
|
122
|
-
curl_config_value header 'Content-Type: application/json'
|
|
123
|
-
curl_config_value data "$(cat "$3")"
|
|
124
|
-
fi
|
|
125
|
-
} > "$scratch/coder-curl.conf"
|
|
126
|
-
vps_ssh "curl -q -sS -K - --write-out '\n%{http_code}'" < "$scratch/coder-curl.conf" \
|
|
127
|
-
> "$scratch/coder-response" 2>/dev/null || die 'Coder request failed'
|
|
128
|
-
code=$(tail -n 1 "$scratch/coder-response")
|
|
129
|
-
case "$code" in 2[0-9][0-9]) ;; *) die 'Coder request failed' ;; esac
|
|
130
|
-
sed '$d' "$scratch/coder-response" > "$scratch/coder.json"
|
|
131
|
-
jq -e 'type == "object"' "$scratch/coder.json" >/dev/null 2>&1 || die 'Coder request failed'
|
|
132
|
-
if body_error "$scratch/coder.json" >/dev/null; then die 'Coder request failed'; fi
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
deploy_template() {
|
|
136
|
-
local version="$1" api_url
|
|
137
|
-
vps_ssh 'mkdir -p /tmp/egregore-template' > /dev/null || die 'template directory creation failed'
|
|
138
|
-
vps_scp "$SCRIPT_DIR/docker/egregore-template/main.tf" /tmp/egregore-template/main.tf > /dev/null || die 'template copy failed'
|
|
139
|
-
coder_login
|
|
140
|
-
vps_ssh 'cat /opt/egregore/org-config.json' > "$scratch/org-config.json" || die 'cannot read VPS org config'
|
|
141
|
-
vps_ssh 'cat /opt/egregore/github-token 2>/dev/null' > "$scratch/github-token" || die 'cannot read VPS GitHub token'
|
|
142
|
-
api_url=$(bash "$SCRIPT_DIR/bin/config-get.sh" api_url) || die 'cannot read api_url'
|
|
143
|
-
[ -n "$api_url" ] || die 'api_url is not set'
|
|
144
|
-
# A JSON object is also a YAML mapping, as accepted by --variables-file.
|
|
145
|
-
# Do not use secret-valued --var flags: they leak on the remote argv too.
|
|
146
|
-
jq -e --arg api_url "$api_url" --rawfile github_token "$scratch/github-token" '
|
|
147
|
-
{egregore_api_key, api_url:$api_url, memory_url, fork_url,
|
|
148
|
-
ghcr_token:"not-needed", github_token:($github_token | sub("\\n+$"; ""))}
|
|
149
|
-
| if all(.[]; type == "string") then . else error("missing template variable") end
|
|
150
|
-
' "$scratch/org-config.json" > "$scratch/variables.json" 2>/dev/null || die 'VPS template variables are missing or invalid'
|
|
151
|
-
{
|
|
152
|
-
printf 'set -euo pipefail\nset +vx\numask 077\nunset CODER_SESSION_TOKEN\nexport CODER_URL=http://localhost\n'
|
|
153
|
-
# Coder reads its session file; no secret is exported to child processes.
|
|
154
|
-
printf 'coder_config=$(mktemp -d /tmp/egregore-coder-config.XXXXXX)\n'
|
|
155
|
-
printf 'trap '\''rm -rf -- "$coder_config"'\'' EXIT\ntrap '\''exit 1'\'' HUP INT TERM\n'
|
|
156
|
-
printf 'printf "%%s" %q > "$coder_config/session"\n' "$coder_token"
|
|
157
|
-
printf 'vars_file=$(mktemp /tmp/egregore-template-vars.XXXXXX)\n'
|
|
158
|
-
printf 'trap '\''rm -f -- "$vars_file"; rm -rf -- "$coder_config"'\'' EXIT\n'
|
|
159
|
-
# printf is a Bash builtin, so these values never reach an exec argv.
|
|
160
|
-
printf 'printf "%%s" %q > "$vars_file"\n' "$(cat "$scratch/variables.json")"
|
|
161
|
-
printf 'coder --global-config "$coder_config" templates push Egregore --directory /tmp/egregore-template --name %q --variables-file "$vars_file" --yes\n' "$version"
|
|
162
|
-
} > "$scratch/push.sh"
|
|
163
|
-
vps_ssh bash -s < "$scratch/push.sh" || die 'template push failed'
|
|
164
|
-
printf 'Template "Egregore" deployed: %s\n' "$version"
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
restart_workspace() {
|
|
168
|
-
local workspace_id="$1" template_id active_version attempt stopped=false
|
|
169
|
-
coder_login
|
|
170
|
-
coder_request GET "/workspaces/$workspace_id"
|
|
171
|
-
template_id=$(jq -er '.template_id | select(type == "string" and length > 0)' "$scratch/coder.json") \
|
|
172
|
-
|| die 'workspace has no template_id'
|
|
173
|
-
printf '%s\n' '{"transition":"stop"}' > "$scratch/build.json"
|
|
174
|
-
coder_request POST "/workspaces/$workspace_id/builds" "$scratch/build.json"
|
|
175
|
-
for ((attempt=1; attempt<=24; attempt++)); do
|
|
176
|
-
sleep 5
|
|
177
|
-
coder_request GET "/workspaces/$workspace_id"
|
|
178
|
-
if jq -e '.latest_build.status == "stopped"' "$scratch/coder.json" >/dev/null; then stopped=true; break; fi
|
|
179
|
-
done
|
|
180
|
-
[ "$stopped" = true ] || die 'workspace did not stop after 24 checks'
|
|
181
|
-
coder_request GET "/templates/$template_id"
|
|
182
|
-
active_version=$(jq -er '.active_version_id | select(type == "string" and length > 0)' "$scratch/coder.json") \
|
|
183
|
-
|| die 'template has no active_version_id'
|
|
184
|
-
jq -n --arg version "$active_version" '{transition:"start", template_version_id:$version}' > "$scratch/build.json"
|
|
185
|
-
coder_request POST "/workspaces/$workspace_id/builds" "$scratch/build.json"
|
|
186
|
-
cat "$scratch/coder.json"
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
create_users() {
|
|
190
|
-
local requested="${1-}" username detail response created=0 failed=0
|
|
191
|
-
gateway GET "/api/admin/org/$slug" > "$scratch/org.json"
|
|
192
|
-
check_body "$scratch/org.json"
|
|
193
|
-
jq -er '.members | type == "array"' "$scratch/org.json" >/dev/null || die 'org response has no members'
|
|
194
|
-
jq -r --arg requested "$requested" '.members[]
|
|
195
|
-
| select(if $requested == "" then .status == "active" else .github_username == $requested end)
|
|
196
|
-
| .github_username | select(type == "string" and length > 0)' "$scratch/org.json" > "$scratch/members"
|
|
197
|
-
while IFS= read -r username; do
|
|
198
|
-
jq -n --arg name "$username" '{username:$name}' > "$scratch/member.json"
|
|
199
|
-
detail=''
|
|
200
|
-
response="$scratch/member-response.json"
|
|
201
|
-
if ! bash "$SCRIPT_DIR/bin/api-call.sh" POST "/api/hosting/user/$slug" --auth egregore --json-file "$scratch/member.json" \
|
|
202
|
-
--error-out "$scratch/member-error.json" > "$response" 2>/dev/null; then
|
|
203
|
-
response="$scratch/member-error.json"
|
|
204
|
-
detail='API gateway request failed'
|
|
205
|
-
fi
|
|
206
|
-
if ! jq -e 'type == "object"' "$response" >/dev/null 2>&1; then
|
|
207
|
-
detail="${detail:-invalid response body}"
|
|
208
|
-
else
|
|
209
|
-
# Only the bounded, operator-facing detail is published, never raw JSON.
|
|
210
|
-
detail=$(jq -r --arg fallback "$detail" '(.detail | select(. != "")) // $fallback | if type == "string" then . else tojson end | .[:200]' "$response")
|
|
211
|
-
fi
|
|
212
|
-
if [ -n "$detail" ]; then
|
|
213
|
-
printf '✗ %s — error: %s\n' "$username" "$detail"
|
|
214
|
-
failed=$((failed + 1))
|
|
215
|
-
else
|
|
216
|
-
printf '✓ %s — Coder user created (workspace is created on first open)\n' "$username"
|
|
217
|
-
created=$((created + 1))
|
|
218
|
-
fi
|
|
219
|
-
done < "$scratch/members"
|
|
220
|
-
printf 'Done: %s created, %s failed\n' "$created" "$failed"
|
|
221
|
-
[ "$failed" -eq 0 ]
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
case "$operation" in
|
|
225
|
-
status) gateway GET "/api/hosting/status/$slug" ;;
|
|
226
|
-
enable) gateway POST "/api/hosting/enable/$slug" ;;
|
|
227
|
-
list) gateway GET /api/admin/health ;;
|
|
228
|
-
wait-ready)
|
|
229
|
-
poll_seconds="${HOSTING_OPS_POLL_SECONDS:-30}"
|
|
230
|
-
case "$poll_seconds" in ''|*[!0-9]*) usage ;; esac
|
|
231
|
-
for ((attempt=1; attempt<=10; attempt++)); do
|
|
232
|
-
# Emit a progress line even when this attempt returns an error body.
|
|
233
|
-
gateway GET "/api/hosting/status/$slug" > "$scratch/status.json"
|
|
234
|
-
progress=$(jq -r '"ip: \(.ip // "unknown") — token_stored: \(if has("token_stored") then .token_stored else "unknown" end)"' "$scratch/status.json" 2>/dev/null) || die 'invalid status response'
|
|
235
|
-
printf 'check %s/10 — %s\n' "$attempt" "$progress"
|
|
236
|
-
check_body "$scratch/status.json"
|
|
237
|
-
if jq -e '.coder_ready == true' "$scratch/status.json" >/dev/null; then exit 0; fi
|
|
238
|
-
if [ "$attempt" -lt 10 ]; then sleep "$poll_seconds"; fi
|
|
239
|
-
done
|
|
240
|
-
die 'Coder did not become ready after 10 checks'
|
|
241
|
-
;;
|
|
242
|
-
ssh) credentials; vps_ssh "$remote_command" || die 'SSH command failed' ;;
|
|
243
|
-
scp) credentials; vps_scp "$1" "$2" || die 'SCP failed' ;;
|
|
244
|
-
deploy-template) credentials; deploy_template "${2:-$slug-initial}" ;;
|
|
245
|
-
restart-workspace) credentials; restart_workspace "$1" ;;
|
|
246
|
-
users) create_users ;;
|
|
247
|
-
user) create_users "$1" ;;
|
|
6
|
+
printf '%s\n' 'Hosted Coder workspaces have been retired. Use Egregore from your local workspace.' >&2
|
|
7
|
+
case "${1-}" in
|
|
8
|
+
--help|-h) exit 0 ;;
|
|
248
9
|
esac
|
|
10
|
+
exit 1
|
|
@@ -33,7 +33,9 @@ _checkout_framework_paths() {
|
|
|
33
33
|
# A path checkout does not merge, rebase, move HEAD, or consult the user's
|
|
34
34
|
# pull/merge/rebase strategy. Tolerate each missing or failed path separately,
|
|
35
35
|
# preserving the historical overlay behavior while reporting each skip once.
|
|
36
|
-
|
|
36
|
+
# The Runtime adapters in bin/ import egregore_runtime/, so the core travels
|
|
37
|
+
# with them; one without the other leaves recall unable to start.
|
|
38
|
+
for framework_path in bin/ egregore_runtime/ .claude/commands/ .claude/skills/ .claude/hooks/ .claude/context/ .claude/agents/ .pi/ .prime/ loom/ CLAUDE.md skills/; do
|
|
37
39
|
if ! git -C "$target_dir" checkout upstream/main -- "$framework_path" 2>/dev/null; then
|
|
38
40
|
printf 'update-framework: skipped %s (absent from upstream/main or checkout failed)\n' "$framework_path" >&2
|
|
39
41
|
fi
|
|
@@ -231,13 +231,13 @@ _apply_framework_update() {
|
|
|
231
231
|
# committing — an overwrite of local edits is the one outcome the user has
|
|
232
232
|
# to be able to see afterwards, and the commit is the only other record.
|
|
233
233
|
local _changed
|
|
234
|
-
_changed=$(git status --porcelain bin/ .claude/ .pi/ .prime/ loom/ CLAUDE.md skills/ 2>/dev/null)
|
|
234
|
+
_changed=$(git status --porcelain bin/ egregore_runtime/ .claude/ .pi/ .prime/ loom/ CLAUDE.md skills/ 2>/dev/null)
|
|
235
235
|
if [ -n "$_changed" ]; then
|
|
236
236
|
FRAMEWORK_UPDATED_COUNT=$(printf '%s\n' "$_changed" | grep -c . || echo 0)
|
|
237
237
|
FRAMEWORK_UPDATED_FILES=$(printf '%s\n' "$_changed" | awk '{print $NF}' | head -3 | tr '\n' ' ')
|
|
238
238
|
git add bin/ .claude/ loom/ CLAUDE.md skills/ 2>/dev/null
|
|
239
239
|
local _runtime_path
|
|
240
|
-
for _runtime_path in .pi .prime; do
|
|
240
|
+
for _runtime_path in egregore_runtime .pi .prime; do
|
|
241
241
|
[ ! -e "$_runtime_path" ] || git add -- "$_runtime_path" 2>/dev/null
|
|
242
242
|
done
|
|
243
243
|
EGREGORE_FRAMEWORK_UPDATE=1 git commit -m "chore(sync): update framework from upstream" --quiet 2>/dev/null || true
|
|
@@ -32,7 +32,7 @@ SESSION_ID="$(sed -n '1p' "$SCRIPT_DIR/.egregore-session-id" 2>/dev/null || true
|
|
|
32
32
|
SESSION_ID="${SESSION_ID:-no-session}"
|
|
33
33
|
PROJECT_HASH="$(printf '%s' "$SCRIPT_DIR" | cksum | awk '{print $1}')"
|
|
34
34
|
STATE_DIR="${EGREGORE_NOTIFY_STATE_DIR:-$HOME/.egregore/notifications/$PROJECT_HASH}"
|
|
35
|
-
RELAY_URL="${EGREGORE_NOTIFY_RELAY_URL:-https://egregore
|
|
35
|
+
RELAY_URL="${EGREGORE_NOTIFY_RELAY_URL:-https://api.egregore.xyz}"
|
|
36
36
|
mkdir -p "$STATE_DIR"
|
|
37
37
|
chmod 700 "$STATE_DIR" 2>/dev/null || true
|
|
38
38
|
|
|
@@ -30,6 +30,19 @@ if [ -d ".claude/skills" ] && [ -d ".claude/commands" ]; then
|
|
|
30
30
|
fi
|
|
31
31
|
fi
|
|
32
32
|
|
|
33
|
+
# --- Migration: Runtime core travels with its adapters ---
|
|
34
|
+
# Earlier overlays copied bin/ but not egregore_runtime/, leaving the Runtime
|
|
35
|
+
# adapters without the module they import. The overlay that ran this update
|
|
36
|
+
# was loaded before bin/ changed, so copy the core here: one /update is
|
|
37
|
+
# enough. A Runtime-managed installation owns these files and is left alone.
|
|
38
|
+
if [ ! -e "$SCRIPT_DIR/egregore_runtime" ] && \
|
|
39
|
+
git -C "$SCRIPT_DIR" cat-file -e 'upstream/main:egregore_runtime' 2>/dev/null && \
|
|
40
|
+
! (source "$SCRIPT_DIR/bin/lib/runtime-owned.sh"; runtime_owns_framework); then
|
|
41
|
+
git -C "$SCRIPT_DIR" checkout upstream/main -- egregore_runtime/
|
|
42
|
+
echo " ✓ Added the Runtime core (egregore_runtime/)"
|
|
43
|
+
MIGRATED=1
|
|
44
|
+
fi
|
|
45
|
+
|
|
33
46
|
# --- Future migrations go here ---
|
|
34
47
|
# Each migration should:
|
|
35
48
|
# 1. Check if it needs to run (idempotent guard)
|
|
@@ -132,7 +132,7 @@ fi
|
|
|
132
132
|
|
|
133
133
|
# Public relay for OSS share endpoint. Unauthenticated: anything posted here is
|
|
134
134
|
# readable by anyone holding the URL, for 7 days, with nothing in front of it.
|
|
135
|
-
RELAY_URL="https://egregore
|
|
135
|
+
RELAY_URL="https://api.egregore.xyz"
|
|
136
136
|
|
|
137
137
|
# --- Artifact hosting off-switch (compliance / privacy) ---
|
|
138
138
|
# An instance can disable ALL egregore.xyz publishing via `bin/settings.sh hosting off`
|
|
@@ -38,7 +38,6 @@ MAX_BUFFER_BYTES=1048576
|
|
|
38
38
|
MAX_EVENTS_AFTER_TRUNCATE=500
|
|
39
39
|
SHARE_CONSENT_SECONDS=600
|
|
40
40
|
SCHEMA_VERSION="egregore.telemetry/v1"
|
|
41
|
-
LEGACY_RELAY_URL="https://egregore-production-55f2.up.railway.app/api/telemetry/relay"
|
|
42
41
|
|
|
43
42
|
ALLOWED_METRICS_JSON='[
|
|
44
43
|
"action","branch","class","command","connector","cost_usd","count",
|
|
@@ -516,7 +515,6 @@ cmd_export() {
|
|
|
516
515
|
|
|
517
516
|
_resolve_share_target() {
|
|
518
517
|
local requested_endpoint="$1"
|
|
519
|
-
local allow_legacy_relay="$2"
|
|
520
518
|
if [ -n "$requested_endpoint" ]; then
|
|
521
519
|
printf '%s\n' "$requested_endpoint"
|
|
522
520
|
return
|
|
@@ -530,13 +528,11 @@ _resolve_share_target() {
|
|
|
530
528
|
fi
|
|
531
529
|
if [ -n "$api_url" ] && [ -n "$api_key" ]; then
|
|
532
530
|
printf '%s/api/telemetry/ingest\n' "${api_url%/}"
|
|
533
|
-
elif [ "$allow_legacy_relay" = "true" ]; then
|
|
534
|
-
printf '%s\n' "$LEGACY_RELAY_URL"
|
|
535
531
|
fi
|
|
536
532
|
}
|
|
537
533
|
|
|
538
534
|
cmd_share() {
|
|
539
|
-
local confirmation="" requested_endpoint=""
|
|
535
|
+
local confirmation="" requested_endpoint="" json_output="false"
|
|
540
536
|
while [ "$#" -gt 0 ]; do
|
|
541
537
|
case "$1" in
|
|
542
538
|
--confirm)
|
|
@@ -548,8 +544,10 @@ cmd_share() {
|
|
|
548
544
|
shift 2
|
|
549
545
|
;;
|
|
550
546
|
--legacy-public-relay)
|
|
551
|
-
|
|
552
|
-
|
|
547
|
+
# The former public relay ran on a retired host; nothing is sent there.
|
|
548
|
+
echo "The former public relay is retired; nothing was sent." >&2
|
|
549
|
+
echo "Export locally, or name an explicit --endpoint." >&2
|
|
550
|
+
return 2
|
|
553
551
|
;;
|
|
554
552
|
--json)
|
|
555
553
|
json_output="true"
|
|
@@ -567,10 +565,9 @@ cmd_share() {
|
|
|
567
565
|
fi
|
|
568
566
|
|
|
569
567
|
local endpoint=""
|
|
570
|
-
endpoint=$(_resolve_share_target "$requested_endpoint"
|
|
568
|
+
endpoint=$(_resolve_share_target "$requested_endpoint")
|
|
571
569
|
if [ -z "$endpoint" ]; then
|
|
572
570
|
echo "No shared telemetry sink is configured. Export locally, or name an explicit --endpoint." >&2
|
|
573
|
-
echo "The former public relay remains available only with --legacy-public-relay." >&2
|
|
574
571
|
return 2
|
|
575
572
|
fi
|
|
576
573
|
case "$endpoint" in
|
|
@@ -697,7 +694,7 @@ cmd_share() {
|
|
|
697
694
|
local curl_args=(-s -o /dev/null -w '%{http_code}' -X POST "$endpoint"
|
|
698
695
|
-H "Content-Type: application/x-ndjson" --data-binary "@$share_file" --max-time 15)
|
|
699
696
|
local api_key="" configured_endpoint=""
|
|
700
|
-
configured_endpoint=$(_resolve_share_target ""
|
|
697
|
+
configured_endpoint=$(_resolve_share_target "")
|
|
701
698
|
if [ -n "$configured_endpoint" ] && [ "$endpoint" = "$configured_endpoint" ] && [ -f "$ENV_FILE" ]; then
|
|
702
699
|
api_key=$(grep '^EGREGORE_API_KEY=' "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true)
|
|
703
700
|
fi
|
|
@@ -152,6 +152,30 @@ class Entitlements(Serializable):
|
|
|
152
152
|
return capability in self.capabilities
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
@dataclass(frozen=True, slots=True)
|
|
156
|
+
class ExecutionAuthority(Serializable):
|
|
157
|
+
"""Non-secret attribution for a verified member mandate.
|
|
158
|
+
|
|
159
|
+
This records the original verified admission, not the latest authorization
|
|
160
|
+
decision. It is not a credential or permission. An authenticated resolver
|
|
161
|
+
must revalidate current authority before each protected operation. Spirit
|
|
162
|
+
ownership, funding and parent_actor_id confer none of this authority.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
executor_actor_id: str
|
|
166
|
+
executor_kind: ActorKind
|
|
167
|
+
worker_id: str
|
|
168
|
+
member_account_id: str
|
|
169
|
+
member_actor_id: str
|
|
170
|
+
membership_id: str
|
|
171
|
+
org_id: str
|
|
172
|
+
grant_id: str
|
|
173
|
+
binding_id: str
|
|
174
|
+
audience: str
|
|
175
|
+
revision: str
|
|
176
|
+
expires_at: datetime
|
|
177
|
+
|
|
178
|
+
|
|
155
179
|
@dataclass(frozen=True, slots=True)
|
|
156
180
|
class ActorContext(Serializable):
|
|
157
181
|
account: AccountIdentity | None
|
|
@@ -161,6 +185,13 @@ class ActorContext(Serializable):
|
|
|
161
185
|
entitlements: Entitlements
|
|
162
186
|
session_id: str
|
|
163
187
|
harness: str
|
|
188
|
+
execution: ExecutionAuthority | None = None
|
|
189
|
+
|
|
190
|
+
def to_dict(self) -> dict[str, Any]:
|
|
191
|
+
result = Serializable.to_dict(self)
|
|
192
|
+
if self.execution is None:
|
|
193
|
+
result.pop("execution")
|
|
194
|
+
return result
|
|
164
195
|
|
|
165
196
|
|
|
166
197
|
@dataclass(frozen=True, slots=True)
|
|
@@ -184,6 +215,13 @@ class SourceProvenance(Serializable):
|
|
|
184
215
|
observed_at: datetime
|
|
185
216
|
source_uri: str | None = None
|
|
186
217
|
imported_by: str | None = None
|
|
218
|
+
execution: ExecutionAuthority | None = None
|
|
219
|
+
|
|
220
|
+
def to_dict(self) -> dict[str, Any]:
|
|
221
|
+
result = Serializable.to_dict(self)
|
|
222
|
+
if self.execution is None:
|
|
223
|
+
result.pop("execution")
|
|
224
|
+
return result
|
|
187
225
|
|
|
188
226
|
|
|
189
227
|
@dataclass(frozen=True, slots=True)
|