create-egregore 0.21.0-runtime-mvp.9 → 0.21.0-runtime-mvp.10
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 +3 -3
- package/package.json +1 -1
- package/runtime/claude/bin/search.sh +15 -0
- package/runtime/claude/bin/settings.sh +3 -3
- package/runtime/claude/bin/telemetry.sh +36 -23
- package/runtime/codex/bin/search.sh +15 -0
- package/runtime/codex/bin/settings.sh +3 -3
- package/runtime/codex/bin/telemetry.sh +36 -23
- package/runtime/pi/bin/search.sh +15 -0
- package/runtime/pi/bin/settings.sh +3 -3
- package/runtime/pi/bin/telemetry.sh +36 -23
- package/runtime/prime/bin/search.sh +15 -0
- package/runtime/prime/bin/settings.sh +3 -3
- package/runtime/prime/bin/telemetry.sh +36 -23
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// egregore-launcher-version: 0.21.0-runtime-mvp.
|
|
2
|
+
// egregore-launcher-version: 0.21.0-runtime-mvp.10
|
|
3
3
|
//
|
|
4
4
|
// egregore — launch and browse Egregore instances from your terminal.
|
|
5
5
|
// Installed by create-egregore. Reads ~/.egregore/instances.json.
|
|
@@ -35,7 +35,7 @@ const crypto = require("node:crypto");
|
|
|
35
35
|
const { spawnSync, spawn } = require("node:child_process");
|
|
36
36
|
const readline = require("node:readline");
|
|
37
37
|
|
|
38
|
-
const VERSION = "0.21.0-runtime-mvp.
|
|
38
|
+
const VERSION = "0.21.0-runtime-mvp.10";
|
|
39
39
|
const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
|
|
40
40
|
const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
|
|
41
41
|
const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
|
|
@@ -4046,7 +4046,7 @@ async function browser(args = {}) {
|
|
|
4046
4046
|
}
|
|
4047
4047
|
if (chunk === "s" && proposal && proposal.token) {
|
|
4048
4048
|
S.settingsAction = "tele-share";
|
|
4049
|
-
S.settingsInput = `Send ${proposal.events} event(s)
|
|
4049
|
+
S.settingsInput = `Send ${proposal.events} event(s) now? Local events are retained. [y/N]`;
|
|
4050
4050
|
render(); return;
|
|
4051
4051
|
}
|
|
4052
4052
|
if (key.name === "escape" || key.name === "left") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-egregore",
|
|
3
|
-
"version": "0.21.0-runtime-mvp.
|
|
3
|
+
"version": "0.21.0-runtime-mvp.10",
|
|
4
4
|
"description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code, Codex, or Pi.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -235,6 +235,21 @@ cmd_query() {
|
|
|
235
235
|
} >&2
|
|
236
236
|
return "$query_rc"
|
|
237
237
|
fi
|
|
238
|
+
# Content-free query telemetry: mode, duration, result count. Never the
|
|
239
|
+
# query text. This is what makes Runtime/QMD retrieval visible in the
|
|
240
|
+
# telemetry event list instead of only legacy graph_query rows.
|
|
241
|
+
local _tele_count _tele_ms
|
|
242
|
+
_tele_ms=$(( ($(date +%s) - t0) * 1000 ))
|
|
243
|
+
_tele_count=$(printf '%s' "$out" | jq 'if type == "array" then length elif type == "object" then (.results // [] | length) else empty end' 2>/dev/null || true)
|
|
244
|
+
if [[ "$_tele_count" =~ ^[0-9]+$ ]]; then
|
|
245
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
246
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" --argjson c "$_tele_count" \
|
|
247
|
+
'{mode:$mode, duration_ms:$d, count:$c}')" 2>/dev/null &
|
|
248
|
+
else
|
|
249
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
250
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" '{mode:$mode, duration_ms:$d}')" 2>/dev/null &
|
|
251
|
+
fi
|
|
252
|
+
|
|
238
253
|
[ "$enrich" = 1 ] && emit="_enrich"
|
|
239
254
|
if [ "$context_packet" = 1 ]; then
|
|
240
255
|
local packet
|
|
@@ -146,7 +146,7 @@ cmd_workflow() {
|
|
|
146
146
|
|| _die "cannot switch workflow: origin/main does not exist or could not be reached" 1
|
|
147
147
|
_save '.base_branch = "main"'
|
|
148
148
|
fi
|
|
149
|
-
echo "Git workflow: SIMPLE — pull requests now target main"
|
|
149
|
+
echo "Git workflow: SIMPLE — pull requests now target main · /save shares it with the org"
|
|
150
150
|
;;
|
|
151
151
|
staged)
|
|
152
152
|
if [ "$base" != "develop" ]; then
|
|
@@ -160,7 +160,7 @@ cmd_workflow() {
|
|
|
160
160
|
elif ! jq -e 'has("base_branch")' "$CONFIG" >/dev/null 2>&1; then
|
|
161
161
|
_save '.base_branch = "develop"'
|
|
162
162
|
fi
|
|
163
|
-
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main"
|
|
163
|
+
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main · /save shares it with the org"
|
|
164
164
|
;;
|
|
165
165
|
*) _die "usage: settings.sh workflow status|simple|staged" 1 ;;
|
|
166
166
|
esac
|
|
@@ -468,7 +468,7 @@ cmd_posture() {
|
|
|
468
468
|
if [ "$action" != "$current" ]; then
|
|
469
469
|
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
470
|
fi
|
|
471
|
-
echo "Boundary posture: $action"
|
|
471
|
+
echo "Boundary posture: $action · applies to new sessions · /save shares it with the org"
|
|
472
472
|
;;
|
|
473
473
|
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
474
|
esac
|
|
@@ -27,6 +27,10 @@ fi
|
|
|
27
27
|
BUFFER_FILE="$BUFFER_DIR/telemetry.jsonl"
|
|
28
28
|
LOCK_DIR="$BUFFER_DIR/telemetry.lock"
|
|
29
29
|
SHARE_CONSENT_FILE="$BUFFER_DIR/telemetry-share-consent.json"
|
|
30
|
+
# Exact dataset pinned at proposal time. The confirm step sends this snapshot,
|
|
31
|
+
# so events emitted between proposal and confirm (attendant, other sessions)
|
|
32
|
+
# can never invalidate — or silently join — a disclosed share.
|
|
33
|
+
SHARE_STAGED_FILE="$BUFFER_DIR/telemetry.share-staged.jsonl"
|
|
30
34
|
STATE_FILE="${EGREGORE_STATE_FILE:-$SCRIPT_DIR/.egregore-state.json}"
|
|
31
35
|
CONFIG="${EGREGORE_CONFIG_FILE:-$SCRIPT_DIR/egregore.json}"
|
|
32
36
|
ENV_FILE="${EGREGORE_ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
@@ -230,7 +234,8 @@ _validate_metrics() {
|
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
_validate_buffer() {
|
|
233
|
-
|
|
237
|
+
local target="${1:-$BUFFER_FILE}"
|
|
238
|
+
[ -s "$target" ] || return 0
|
|
234
239
|
jq -e --arg schema "$SCHEMA_VERSION" --argjson allowed "$ALLOWED_METRICS_JSON" '
|
|
235
240
|
def safe_id:
|
|
236
241
|
type == "string" and length > 0 and length <= 256 and
|
|
@@ -264,7 +269,7 @@ _validate_buffer() {
|
|
|
264
269
|
(.data | safe_metrics) and (.ts | type == "string") and
|
|
265
270
|
(.type | safe_id) and (.sid | safe_id) and (.org | safe_id) and (.user | safe_id)
|
|
266
271
|
end
|
|
267
|
-
' "$
|
|
272
|
+
' "$target" >/dev/null 2>&1
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
cmd_emit() {
|
|
@@ -422,7 +427,7 @@ cmd_clear() {
|
|
|
422
427
|
echo "Could not acquire the telemetry lock; nothing was cleared." >&2
|
|
423
428
|
return 1
|
|
424
429
|
fi
|
|
425
|
-
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE"
|
|
430
|
+
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
426
431
|
_unlock
|
|
427
432
|
echo "Local telemetry buffer cleared."
|
|
428
433
|
}
|
|
@@ -525,31 +530,35 @@ cmd_share() {
|
|
|
525
530
|
https://*|http://127.0.0.1*|http://localhost*|http://\[::1\]*) ;;
|
|
526
531
|
*) echo "Share endpoint must use https (or loopback http)." >&2; return 2 ;;
|
|
527
532
|
esac
|
|
528
|
-
|
|
533
|
+
# Proposal validates the live buffer it is about to stage; confirm validates
|
|
534
|
+
# the staged snapshot it is about to send — never the moving buffer.
|
|
535
|
+
if [ -z "$confirmation" ] && ! _validate_buffer; then
|
|
529
536
|
echo "Share blocked: the local buffer contains an invalid or content-bearing event." >&2
|
|
530
537
|
return 2
|
|
531
538
|
fi
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
count=$(grep -c '^{' "$BUFFER_FILE" 2>/dev/null || true)
|
|
536
|
-
size=$(wc -c < "$BUFFER_FILE" 2>/dev/null | tr -d ' ')
|
|
537
|
-
dataset_sha256=$(_sha256_file "$BUFFER_FILE")
|
|
538
|
-
else
|
|
539
|
-
local empty_file="$BUFFER_DIR/telemetry.empty.$$"
|
|
540
|
-
mkdir -p "$BUFFER_DIR"
|
|
541
|
-
: > "$empty_file"
|
|
542
|
-
dataset_sha256=$(_sha256_file "$empty_file")
|
|
543
|
-
rm -f "$empty_file"
|
|
539
|
+
if [ -n "$confirmation" ] && ! _validate_buffer "$SHARE_STAGED_FILE"; then
|
|
540
|
+
echo "Share blocked: the staged dataset contains an invalid or content-bearing event." >&2
|
|
541
|
+
return 2
|
|
544
542
|
fi
|
|
545
543
|
|
|
544
|
+
local count=0 size=0 dataset_sha256=""
|
|
546
545
|
if [ -z "$confirmation" ]; then
|
|
547
|
-
|
|
546
|
+
mkdir -p "$BUFFER_DIR"
|
|
548
547
|
if [ -s "$BUFFER_FILE" ]; then
|
|
548
|
+
cp "$BUFFER_FILE" "$SHARE_STAGED_FILE"
|
|
549
|
+
else
|
|
550
|
+
: > "$SHARE_STAGED_FILE"
|
|
551
|
+
fi
|
|
552
|
+
chmod 600 "$SHARE_STAGED_FILE"
|
|
553
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
554
|
+
size=$(wc -c < "$SHARE_STAGED_FILE" 2>/dev/null | tr -d ' ')
|
|
555
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
556
|
+
local fields="none"
|
|
557
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
549
558
|
fields=$(jq -rs '
|
|
550
559
|
[.[].metrics // .[].data // {} | keys[]] | unique |
|
|
551
560
|
if length == 0 then "none" else map("metrics." + .) | join(", ") end
|
|
552
|
-
' "$
|
|
561
|
+
' "$SHARE_STAGED_FILE" 2>/dev/null || echo "unable to inspect")
|
|
553
562
|
fi
|
|
554
563
|
local token token_sha256 expires_at
|
|
555
564
|
token=$(_random_token)
|
|
@@ -600,10 +609,14 @@ cmd_share() {
|
|
|
600
609
|
return 0
|
|
601
610
|
fi
|
|
602
611
|
|
|
603
|
-
if [ ! -f "$SHARE_CONSENT_FILE" ]; then
|
|
612
|
+
if [ ! -f "$SHARE_CONSENT_FILE" ] || [ ! -f "$SHARE_STAGED_FILE" ]; then
|
|
604
613
|
echo "Share blocked: run share without --confirm to inspect and prepare this dataset first." >&2
|
|
605
614
|
return 2
|
|
606
615
|
fi
|
|
616
|
+
# The staged snapshot is the disclosed dataset: exactly what the proposal
|
|
617
|
+
# named is what ships, and later buffer growth cannot invalidate it.
|
|
618
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
619
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
607
620
|
local expected_target expected_dataset expected_token expires_at actual_token
|
|
608
621
|
expected_target=$(jq -r '.target // empty' "$SHARE_CONSENT_FILE")
|
|
609
622
|
expected_dataset=$(jq -r '.dataset_sha256 // empty' "$SHARE_CONSENT_FILE")
|
|
@@ -611,7 +624,7 @@ cmd_share() {
|
|
|
611
624
|
expires_at=$(jq -r '.expires_at // 0' "$SHARE_CONSENT_FILE")
|
|
612
625
|
actual_token=$(_sha256_text "$confirmation")
|
|
613
626
|
if [ "$(date +%s)" -gt "$expires_at" ] 2>/dev/null; then
|
|
614
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
627
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
615
628
|
echo "Share blocked: confirmation expired; prepare a new share." >&2
|
|
616
629
|
return 2
|
|
617
630
|
fi
|
|
@@ -621,7 +634,7 @@ cmd_share() {
|
|
|
621
634
|
fi
|
|
622
635
|
|
|
623
636
|
local share_file="$BUFFER_DIR/telemetry.share.$$"
|
|
624
|
-
if [ -s "$
|
|
637
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
625
638
|
jq -c --arg schema "$SCHEMA_VERSION" '
|
|
626
639
|
if .schema_version == $schema then
|
|
627
640
|
{ts:.occurred_at,type:.event_type,sid:.session_id,org:.org_id,user:.actor_id,
|
|
@@ -629,7 +642,7 @@ cmd_share() {
|
|
|
629
642
|
artifact_ids:.artifact_ids,task_id:.task_id,org_revision:.org_revision,
|
|
630
643
|
index_spec_version:.index_spec_version,shared:true})}
|
|
631
644
|
else . end
|
|
632
|
-
' "$
|
|
645
|
+
' "$SHARE_STAGED_FILE" > "$share_file"
|
|
633
646
|
else
|
|
634
647
|
: > "$share_file"
|
|
635
648
|
fi
|
|
@@ -646,7 +659,7 @@ cmd_share() {
|
|
|
646
659
|
http_code=$(curl "${curl_args[@]}" 2>/dev/null || echo "000")
|
|
647
660
|
rm -f "$share_file"
|
|
648
661
|
if [ "$http_code" -ge 200 ] 2>/dev/null && [ "$http_code" -lt 300 ] 2>/dev/null; then
|
|
649
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
662
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
650
663
|
echo "Shared $count telemetry events with $endpoint. Local events were retained."
|
|
651
664
|
else
|
|
652
665
|
echo "Share failed (HTTP $http_code). Nothing was removed locally." >&2
|
|
@@ -235,6 +235,21 @@ cmd_query() {
|
|
|
235
235
|
} >&2
|
|
236
236
|
return "$query_rc"
|
|
237
237
|
fi
|
|
238
|
+
# Content-free query telemetry: mode, duration, result count. Never the
|
|
239
|
+
# query text. This is what makes Runtime/QMD retrieval visible in the
|
|
240
|
+
# telemetry event list instead of only legacy graph_query rows.
|
|
241
|
+
local _tele_count _tele_ms
|
|
242
|
+
_tele_ms=$(( ($(date +%s) - t0) * 1000 ))
|
|
243
|
+
_tele_count=$(printf '%s' "$out" | jq 'if type == "array" then length elif type == "object" then (.results // [] | length) else empty end' 2>/dev/null || true)
|
|
244
|
+
if [[ "$_tele_count" =~ ^[0-9]+$ ]]; then
|
|
245
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
246
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" --argjson c "$_tele_count" \
|
|
247
|
+
'{mode:$mode, duration_ms:$d, count:$c}')" 2>/dev/null &
|
|
248
|
+
else
|
|
249
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
250
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" '{mode:$mode, duration_ms:$d}')" 2>/dev/null &
|
|
251
|
+
fi
|
|
252
|
+
|
|
238
253
|
[ "$enrich" = 1 ] && emit="_enrich"
|
|
239
254
|
if [ "$context_packet" = 1 ]; then
|
|
240
255
|
local packet
|
|
@@ -146,7 +146,7 @@ cmd_workflow() {
|
|
|
146
146
|
|| _die "cannot switch workflow: origin/main does not exist or could not be reached" 1
|
|
147
147
|
_save '.base_branch = "main"'
|
|
148
148
|
fi
|
|
149
|
-
echo "Git workflow: SIMPLE — pull requests now target main"
|
|
149
|
+
echo "Git workflow: SIMPLE — pull requests now target main · /save shares it with the org"
|
|
150
150
|
;;
|
|
151
151
|
staged)
|
|
152
152
|
if [ "$base" != "develop" ]; then
|
|
@@ -160,7 +160,7 @@ cmd_workflow() {
|
|
|
160
160
|
elif ! jq -e 'has("base_branch")' "$CONFIG" >/dev/null 2>&1; then
|
|
161
161
|
_save '.base_branch = "develop"'
|
|
162
162
|
fi
|
|
163
|
-
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main"
|
|
163
|
+
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main · /save shares it with the org"
|
|
164
164
|
;;
|
|
165
165
|
*) _die "usage: settings.sh workflow status|simple|staged" 1 ;;
|
|
166
166
|
esac
|
|
@@ -468,7 +468,7 @@ cmd_posture() {
|
|
|
468
468
|
if [ "$action" != "$current" ]; then
|
|
469
469
|
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
470
|
fi
|
|
471
|
-
echo "Boundary posture: $action"
|
|
471
|
+
echo "Boundary posture: $action · applies to new sessions · /save shares it with the org"
|
|
472
472
|
;;
|
|
473
473
|
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
474
|
esac
|
|
@@ -27,6 +27,10 @@ fi
|
|
|
27
27
|
BUFFER_FILE="$BUFFER_DIR/telemetry.jsonl"
|
|
28
28
|
LOCK_DIR="$BUFFER_DIR/telemetry.lock"
|
|
29
29
|
SHARE_CONSENT_FILE="$BUFFER_DIR/telemetry-share-consent.json"
|
|
30
|
+
# Exact dataset pinned at proposal time. The confirm step sends this snapshot,
|
|
31
|
+
# so events emitted between proposal and confirm (attendant, other sessions)
|
|
32
|
+
# can never invalidate — or silently join — a disclosed share.
|
|
33
|
+
SHARE_STAGED_FILE="$BUFFER_DIR/telemetry.share-staged.jsonl"
|
|
30
34
|
STATE_FILE="${EGREGORE_STATE_FILE:-$SCRIPT_DIR/.egregore-state.json}"
|
|
31
35
|
CONFIG="${EGREGORE_CONFIG_FILE:-$SCRIPT_DIR/egregore.json}"
|
|
32
36
|
ENV_FILE="${EGREGORE_ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
@@ -230,7 +234,8 @@ _validate_metrics() {
|
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
_validate_buffer() {
|
|
233
|
-
|
|
237
|
+
local target="${1:-$BUFFER_FILE}"
|
|
238
|
+
[ -s "$target" ] || return 0
|
|
234
239
|
jq -e --arg schema "$SCHEMA_VERSION" --argjson allowed "$ALLOWED_METRICS_JSON" '
|
|
235
240
|
def safe_id:
|
|
236
241
|
type == "string" and length > 0 and length <= 256 and
|
|
@@ -264,7 +269,7 @@ _validate_buffer() {
|
|
|
264
269
|
(.data | safe_metrics) and (.ts | type == "string") and
|
|
265
270
|
(.type | safe_id) and (.sid | safe_id) and (.org | safe_id) and (.user | safe_id)
|
|
266
271
|
end
|
|
267
|
-
' "$
|
|
272
|
+
' "$target" >/dev/null 2>&1
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
cmd_emit() {
|
|
@@ -422,7 +427,7 @@ cmd_clear() {
|
|
|
422
427
|
echo "Could not acquire the telemetry lock; nothing was cleared." >&2
|
|
423
428
|
return 1
|
|
424
429
|
fi
|
|
425
|
-
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE"
|
|
430
|
+
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
426
431
|
_unlock
|
|
427
432
|
echo "Local telemetry buffer cleared."
|
|
428
433
|
}
|
|
@@ -525,31 +530,35 @@ cmd_share() {
|
|
|
525
530
|
https://*|http://127.0.0.1*|http://localhost*|http://\[::1\]*) ;;
|
|
526
531
|
*) echo "Share endpoint must use https (or loopback http)." >&2; return 2 ;;
|
|
527
532
|
esac
|
|
528
|
-
|
|
533
|
+
# Proposal validates the live buffer it is about to stage; confirm validates
|
|
534
|
+
# the staged snapshot it is about to send — never the moving buffer.
|
|
535
|
+
if [ -z "$confirmation" ] && ! _validate_buffer; then
|
|
529
536
|
echo "Share blocked: the local buffer contains an invalid or content-bearing event." >&2
|
|
530
537
|
return 2
|
|
531
538
|
fi
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
count=$(grep -c '^{' "$BUFFER_FILE" 2>/dev/null || true)
|
|
536
|
-
size=$(wc -c < "$BUFFER_FILE" 2>/dev/null | tr -d ' ')
|
|
537
|
-
dataset_sha256=$(_sha256_file "$BUFFER_FILE")
|
|
538
|
-
else
|
|
539
|
-
local empty_file="$BUFFER_DIR/telemetry.empty.$$"
|
|
540
|
-
mkdir -p "$BUFFER_DIR"
|
|
541
|
-
: > "$empty_file"
|
|
542
|
-
dataset_sha256=$(_sha256_file "$empty_file")
|
|
543
|
-
rm -f "$empty_file"
|
|
539
|
+
if [ -n "$confirmation" ] && ! _validate_buffer "$SHARE_STAGED_FILE"; then
|
|
540
|
+
echo "Share blocked: the staged dataset contains an invalid or content-bearing event." >&2
|
|
541
|
+
return 2
|
|
544
542
|
fi
|
|
545
543
|
|
|
544
|
+
local count=0 size=0 dataset_sha256=""
|
|
546
545
|
if [ -z "$confirmation" ]; then
|
|
547
|
-
|
|
546
|
+
mkdir -p "$BUFFER_DIR"
|
|
548
547
|
if [ -s "$BUFFER_FILE" ]; then
|
|
548
|
+
cp "$BUFFER_FILE" "$SHARE_STAGED_FILE"
|
|
549
|
+
else
|
|
550
|
+
: > "$SHARE_STAGED_FILE"
|
|
551
|
+
fi
|
|
552
|
+
chmod 600 "$SHARE_STAGED_FILE"
|
|
553
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
554
|
+
size=$(wc -c < "$SHARE_STAGED_FILE" 2>/dev/null | tr -d ' ')
|
|
555
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
556
|
+
local fields="none"
|
|
557
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
549
558
|
fields=$(jq -rs '
|
|
550
559
|
[.[].metrics // .[].data // {} | keys[]] | unique |
|
|
551
560
|
if length == 0 then "none" else map("metrics." + .) | join(", ") end
|
|
552
|
-
' "$
|
|
561
|
+
' "$SHARE_STAGED_FILE" 2>/dev/null || echo "unable to inspect")
|
|
553
562
|
fi
|
|
554
563
|
local token token_sha256 expires_at
|
|
555
564
|
token=$(_random_token)
|
|
@@ -600,10 +609,14 @@ cmd_share() {
|
|
|
600
609
|
return 0
|
|
601
610
|
fi
|
|
602
611
|
|
|
603
|
-
if [ ! -f "$SHARE_CONSENT_FILE" ]; then
|
|
612
|
+
if [ ! -f "$SHARE_CONSENT_FILE" ] || [ ! -f "$SHARE_STAGED_FILE" ]; then
|
|
604
613
|
echo "Share blocked: run share without --confirm to inspect and prepare this dataset first." >&2
|
|
605
614
|
return 2
|
|
606
615
|
fi
|
|
616
|
+
# The staged snapshot is the disclosed dataset: exactly what the proposal
|
|
617
|
+
# named is what ships, and later buffer growth cannot invalidate it.
|
|
618
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
619
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
607
620
|
local expected_target expected_dataset expected_token expires_at actual_token
|
|
608
621
|
expected_target=$(jq -r '.target // empty' "$SHARE_CONSENT_FILE")
|
|
609
622
|
expected_dataset=$(jq -r '.dataset_sha256 // empty' "$SHARE_CONSENT_FILE")
|
|
@@ -611,7 +624,7 @@ cmd_share() {
|
|
|
611
624
|
expires_at=$(jq -r '.expires_at // 0' "$SHARE_CONSENT_FILE")
|
|
612
625
|
actual_token=$(_sha256_text "$confirmation")
|
|
613
626
|
if [ "$(date +%s)" -gt "$expires_at" ] 2>/dev/null; then
|
|
614
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
627
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
615
628
|
echo "Share blocked: confirmation expired; prepare a new share." >&2
|
|
616
629
|
return 2
|
|
617
630
|
fi
|
|
@@ -621,7 +634,7 @@ cmd_share() {
|
|
|
621
634
|
fi
|
|
622
635
|
|
|
623
636
|
local share_file="$BUFFER_DIR/telemetry.share.$$"
|
|
624
|
-
if [ -s "$
|
|
637
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
625
638
|
jq -c --arg schema "$SCHEMA_VERSION" '
|
|
626
639
|
if .schema_version == $schema then
|
|
627
640
|
{ts:.occurred_at,type:.event_type,sid:.session_id,org:.org_id,user:.actor_id,
|
|
@@ -629,7 +642,7 @@ cmd_share() {
|
|
|
629
642
|
artifact_ids:.artifact_ids,task_id:.task_id,org_revision:.org_revision,
|
|
630
643
|
index_spec_version:.index_spec_version,shared:true})}
|
|
631
644
|
else . end
|
|
632
|
-
' "$
|
|
645
|
+
' "$SHARE_STAGED_FILE" > "$share_file"
|
|
633
646
|
else
|
|
634
647
|
: > "$share_file"
|
|
635
648
|
fi
|
|
@@ -646,7 +659,7 @@ cmd_share() {
|
|
|
646
659
|
http_code=$(curl "${curl_args[@]}" 2>/dev/null || echo "000")
|
|
647
660
|
rm -f "$share_file"
|
|
648
661
|
if [ "$http_code" -ge 200 ] 2>/dev/null && [ "$http_code" -lt 300 ] 2>/dev/null; then
|
|
649
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
662
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
650
663
|
echo "Shared $count telemetry events with $endpoint. Local events were retained."
|
|
651
664
|
else
|
|
652
665
|
echo "Share failed (HTTP $http_code). Nothing was removed locally." >&2
|
package/runtime/pi/bin/search.sh
CHANGED
|
@@ -235,6 +235,21 @@ cmd_query() {
|
|
|
235
235
|
} >&2
|
|
236
236
|
return "$query_rc"
|
|
237
237
|
fi
|
|
238
|
+
# Content-free query telemetry: mode, duration, result count. Never the
|
|
239
|
+
# query text. This is what makes Runtime/QMD retrieval visible in the
|
|
240
|
+
# telemetry event list instead of only legacy graph_query rows.
|
|
241
|
+
local _tele_count _tele_ms
|
|
242
|
+
_tele_ms=$(( ($(date +%s) - t0) * 1000 ))
|
|
243
|
+
_tele_count=$(printf '%s' "$out" | jq 'if type == "array" then length elif type == "object" then (.results // [] | length) else empty end' 2>/dev/null || true)
|
|
244
|
+
if [[ "$_tele_count" =~ ^[0-9]+$ ]]; then
|
|
245
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
246
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" --argjson c "$_tele_count" \
|
|
247
|
+
'{mode:$mode, duration_ms:$d, count:$c}')" 2>/dev/null &
|
|
248
|
+
else
|
|
249
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
250
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" '{mode:$mode, duration_ms:$d}')" 2>/dev/null &
|
|
251
|
+
fi
|
|
252
|
+
|
|
238
253
|
[ "$enrich" = 1 ] && emit="_enrich"
|
|
239
254
|
if [ "$context_packet" = 1 ]; then
|
|
240
255
|
local packet
|
|
@@ -146,7 +146,7 @@ cmd_workflow() {
|
|
|
146
146
|
|| _die "cannot switch workflow: origin/main does not exist or could not be reached" 1
|
|
147
147
|
_save '.base_branch = "main"'
|
|
148
148
|
fi
|
|
149
|
-
echo "Git workflow: SIMPLE — pull requests now target main"
|
|
149
|
+
echo "Git workflow: SIMPLE — pull requests now target main · /save shares it with the org"
|
|
150
150
|
;;
|
|
151
151
|
staged)
|
|
152
152
|
if [ "$base" != "develop" ]; then
|
|
@@ -160,7 +160,7 @@ cmd_workflow() {
|
|
|
160
160
|
elif ! jq -e 'has("base_branch")' "$CONFIG" >/dev/null 2>&1; then
|
|
161
161
|
_save '.base_branch = "develop"'
|
|
162
162
|
fi
|
|
163
|
-
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main"
|
|
163
|
+
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main · /save shares it with the org"
|
|
164
164
|
;;
|
|
165
165
|
*) _die "usage: settings.sh workflow status|simple|staged" 1 ;;
|
|
166
166
|
esac
|
|
@@ -468,7 +468,7 @@ cmd_posture() {
|
|
|
468
468
|
if [ "$action" != "$current" ]; then
|
|
469
469
|
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
470
|
fi
|
|
471
|
-
echo "Boundary posture: $action"
|
|
471
|
+
echo "Boundary posture: $action · applies to new sessions · /save shares it with the org"
|
|
472
472
|
;;
|
|
473
473
|
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
474
|
esac
|
|
@@ -27,6 +27,10 @@ fi
|
|
|
27
27
|
BUFFER_FILE="$BUFFER_DIR/telemetry.jsonl"
|
|
28
28
|
LOCK_DIR="$BUFFER_DIR/telemetry.lock"
|
|
29
29
|
SHARE_CONSENT_FILE="$BUFFER_DIR/telemetry-share-consent.json"
|
|
30
|
+
# Exact dataset pinned at proposal time. The confirm step sends this snapshot,
|
|
31
|
+
# so events emitted between proposal and confirm (attendant, other sessions)
|
|
32
|
+
# can never invalidate — or silently join — a disclosed share.
|
|
33
|
+
SHARE_STAGED_FILE="$BUFFER_DIR/telemetry.share-staged.jsonl"
|
|
30
34
|
STATE_FILE="${EGREGORE_STATE_FILE:-$SCRIPT_DIR/.egregore-state.json}"
|
|
31
35
|
CONFIG="${EGREGORE_CONFIG_FILE:-$SCRIPT_DIR/egregore.json}"
|
|
32
36
|
ENV_FILE="${EGREGORE_ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
@@ -230,7 +234,8 @@ _validate_metrics() {
|
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
_validate_buffer() {
|
|
233
|
-
|
|
237
|
+
local target="${1:-$BUFFER_FILE}"
|
|
238
|
+
[ -s "$target" ] || return 0
|
|
234
239
|
jq -e --arg schema "$SCHEMA_VERSION" --argjson allowed "$ALLOWED_METRICS_JSON" '
|
|
235
240
|
def safe_id:
|
|
236
241
|
type == "string" and length > 0 and length <= 256 and
|
|
@@ -264,7 +269,7 @@ _validate_buffer() {
|
|
|
264
269
|
(.data | safe_metrics) and (.ts | type == "string") and
|
|
265
270
|
(.type | safe_id) and (.sid | safe_id) and (.org | safe_id) and (.user | safe_id)
|
|
266
271
|
end
|
|
267
|
-
' "$
|
|
272
|
+
' "$target" >/dev/null 2>&1
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
cmd_emit() {
|
|
@@ -422,7 +427,7 @@ cmd_clear() {
|
|
|
422
427
|
echo "Could not acquire the telemetry lock; nothing was cleared." >&2
|
|
423
428
|
return 1
|
|
424
429
|
fi
|
|
425
|
-
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE"
|
|
430
|
+
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
426
431
|
_unlock
|
|
427
432
|
echo "Local telemetry buffer cleared."
|
|
428
433
|
}
|
|
@@ -525,31 +530,35 @@ cmd_share() {
|
|
|
525
530
|
https://*|http://127.0.0.1*|http://localhost*|http://\[::1\]*) ;;
|
|
526
531
|
*) echo "Share endpoint must use https (or loopback http)." >&2; return 2 ;;
|
|
527
532
|
esac
|
|
528
|
-
|
|
533
|
+
# Proposal validates the live buffer it is about to stage; confirm validates
|
|
534
|
+
# the staged snapshot it is about to send — never the moving buffer.
|
|
535
|
+
if [ -z "$confirmation" ] && ! _validate_buffer; then
|
|
529
536
|
echo "Share blocked: the local buffer contains an invalid or content-bearing event." >&2
|
|
530
537
|
return 2
|
|
531
538
|
fi
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
count=$(grep -c '^{' "$BUFFER_FILE" 2>/dev/null || true)
|
|
536
|
-
size=$(wc -c < "$BUFFER_FILE" 2>/dev/null | tr -d ' ')
|
|
537
|
-
dataset_sha256=$(_sha256_file "$BUFFER_FILE")
|
|
538
|
-
else
|
|
539
|
-
local empty_file="$BUFFER_DIR/telemetry.empty.$$"
|
|
540
|
-
mkdir -p "$BUFFER_DIR"
|
|
541
|
-
: > "$empty_file"
|
|
542
|
-
dataset_sha256=$(_sha256_file "$empty_file")
|
|
543
|
-
rm -f "$empty_file"
|
|
539
|
+
if [ -n "$confirmation" ] && ! _validate_buffer "$SHARE_STAGED_FILE"; then
|
|
540
|
+
echo "Share blocked: the staged dataset contains an invalid or content-bearing event." >&2
|
|
541
|
+
return 2
|
|
544
542
|
fi
|
|
545
543
|
|
|
544
|
+
local count=0 size=0 dataset_sha256=""
|
|
546
545
|
if [ -z "$confirmation" ]; then
|
|
547
|
-
|
|
546
|
+
mkdir -p "$BUFFER_DIR"
|
|
548
547
|
if [ -s "$BUFFER_FILE" ]; then
|
|
548
|
+
cp "$BUFFER_FILE" "$SHARE_STAGED_FILE"
|
|
549
|
+
else
|
|
550
|
+
: > "$SHARE_STAGED_FILE"
|
|
551
|
+
fi
|
|
552
|
+
chmod 600 "$SHARE_STAGED_FILE"
|
|
553
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
554
|
+
size=$(wc -c < "$SHARE_STAGED_FILE" 2>/dev/null | tr -d ' ')
|
|
555
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
556
|
+
local fields="none"
|
|
557
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
549
558
|
fields=$(jq -rs '
|
|
550
559
|
[.[].metrics // .[].data // {} | keys[]] | unique |
|
|
551
560
|
if length == 0 then "none" else map("metrics." + .) | join(", ") end
|
|
552
|
-
' "$
|
|
561
|
+
' "$SHARE_STAGED_FILE" 2>/dev/null || echo "unable to inspect")
|
|
553
562
|
fi
|
|
554
563
|
local token token_sha256 expires_at
|
|
555
564
|
token=$(_random_token)
|
|
@@ -600,10 +609,14 @@ cmd_share() {
|
|
|
600
609
|
return 0
|
|
601
610
|
fi
|
|
602
611
|
|
|
603
|
-
if [ ! -f "$SHARE_CONSENT_FILE" ]; then
|
|
612
|
+
if [ ! -f "$SHARE_CONSENT_FILE" ] || [ ! -f "$SHARE_STAGED_FILE" ]; then
|
|
604
613
|
echo "Share blocked: run share without --confirm to inspect and prepare this dataset first." >&2
|
|
605
614
|
return 2
|
|
606
615
|
fi
|
|
616
|
+
# The staged snapshot is the disclosed dataset: exactly what the proposal
|
|
617
|
+
# named is what ships, and later buffer growth cannot invalidate it.
|
|
618
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
619
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
607
620
|
local expected_target expected_dataset expected_token expires_at actual_token
|
|
608
621
|
expected_target=$(jq -r '.target // empty' "$SHARE_CONSENT_FILE")
|
|
609
622
|
expected_dataset=$(jq -r '.dataset_sha256 // empty' "$SHARE_CONSENT_FILE")
|
|
@@ -611,7 +624,7 @@ cmd_share() {
|
|
|
611
624
|
expires_at=$(jq -r '.expires_at // 0' "$SHARE_CONSENT_FILE")
|
|
612
625
|
actual_token=$(_sha256_text "$confirmation")
|
|
613
626
|
if [ "$(date +%s)" -gt "$expires_at" ] 2>/dev/null; then
|
|
614
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
627
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
615
628
|
echo "Share blocked: confirmation expired; prepare a new share." >&2
|
|
616
629
|
return 2
|
|
617
630
|
fi
|
|
@@ -621,7 +634,7 @@ cmd_share() {
|
|
|
621
634
|
fi
|
|
622
635
|
|
|
623
636
|
local share_file="$BUFFER_DIR/telemetry.share.$$"
|
|
624
|
-
if [ -s "$
|
|
637
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
625
638
|
jq -c --arg schema "$SCHEMA_VERSION" '
|
|
626
639
|
if .schema_version == $schema then
|
|
627
640
|
{ts:.occurred_at,type:.event_type,sid:.session_id,org:.org_id,user:.actor_id,
|
|
@@ -629,7 +642,7 @@ cmd_share() {
|
|
|
629
642
|
artifact_ids:.artifact_ids,task_id:.task_id,org_revision:.org_revision,
|
|
630
643
|
index_spec_version:.index_spec_version,shared:true})}
|
|
631
644
|
else . end
|
|
632
|
-
' "$
|
|
645
|
+
' "$SHARE_STAGED_FILE" > "$share_file"
|
|
633
646
|
else
|
|
634
647
|
: > "$share_file"
|
|
635
648
|
fi
|
|
@@ -646,7 +659,7 @@ cmd_share() {
|
|
|
646
659
|
http_code=$(curl "${curl_args[@]}" 2>/dev/null || echo "000")
|
|
647
660
|
rm -f "$share_file"
|
|
648
661
|
if [ "$http_code" -ge 200 ] 2>/dev/null && [ "$http_code" -lt 300 ] 2>/dev/null; then
|
|
649
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
662
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
650
663
|
echo "Shared $count telemetry events with $endpoint. Local events were retained."
|
|
651
664
|
else
|
|
652
665
|
echo "Share failed (HTTP $http_code). Nothing was removed locally." >&2
|
|
@@ -235,6 +235,21 @@ cmd_query() {
|
|
|
235
235
|
} >&2
|
|
236
236
|
return "$query_rc"
|
|
237
237
|
fi
|
|
238
|
+
# Content-free query telemetry: mode, duration, result count. Never the
|
|
239
|
+
# query text. This is what makes Runtime/QMD retrieval visible in the
|
|
240
|
+
# telemetry event list instead of only legacy graph_query rows.
|
|
241
|
+
local _tele_count _tele_ms
|
|
242
|
+
_tele_ms=$(( ($(date +%s) - t0) * 1000 ))
|
|
243
|
+
_tele_count=$(printf '%s' "$out" | jq 'if type == "array" then length elif type == "object" then (.results // [] | length) else empty end' 2>/dev/null || true)
|
|
244
|
+
if [[ "$_tele_count" =~ ^[0-9]+$ ]]; then
|
|
245
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
246
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" --argjson c "$_tele_count" \
|
|
247
|
+
'{mode:$mode, duration_ms:$d, count:$c}')" 2>/dev/null &
|
|
248
|
+
else
|
|
249
|
+
bash "$SCRIPT_DIR/bin/telemetry.sh" emit "query" \
|
|
250
|
+
"$(jq -n --arg mode "$label" --argjson d "$_tele_ms" '{mode:$mode, duration_ms:$d}')" 2>/dev/null &
|
|
251
|
+
fi
|
|
252
|
+
|
|
238
253
|
[ "$enrich" = 1 ] && emit="_enrich"
|
|
239
254
|
if [ "$context_packet" = 1 ]; then
|
|
240
255
|
local packet
|
|
@@ -146,7 +146,7 @@ cmd_workflow() {
|
|
|
146
146
|
|| _die "cannot switch workflow: origin/main does not exist or could not be reached" 1
|
|
147
147
|
_save '.base_branch = "main"'
|
|
148
148
|
fi
|
|
149
|
-
echo "Git workflow: SIMPLE — pull requests now target main"
|
|
149
|
+
echo "Git workflow: SIMPLE — pull requests now target main · /save shares it with the org"
|
|
150
150
|
;;
|
|
151
151
|
staged)
|
|
152
152
|
if [ "$base" != "develop" ]; then
|
|
@@ -160,7 +160,7 @@ cmd_workflow() {
|
|
|
160
160
|
elif ! jq -e 'has("base_branch")' "$CONFIG" >/dev/null 2>&1; then
|
|
161
161
|
_save '.base_branch = "develop"'
|
|
162
162
|
fi
|
|
163
|
-
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main"
|
|
163
|
+
echo "Git workflow: STAGED — pull requests now target develop; releases promote to main · /save shares it with the org"
|
|
164
164
|
;;
|
|
165
165
|
*) _die "usage: settings.sh workflow status|simple|staged" 1 ;;
|
|
166
166
|
esac
|
|
@@ -468,7 +468,7 @@ cmd_posture() {
|
|
|
468
468
|
if [ "$action" != "$current" ]; then
|
|
469
469
|
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
470
|
fi
|
|
471
|
-
echo "Boundary posture: $action"
|
|
471
|
+
echo "Boundary posture: $action · applies to new sessions · /save shares it with the org"
|
|
472
472
|
;;
|
|
473
473
|
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
474
|
esac
|
|
@@ -27,6 +27,10 @@ fi
|
|
|
27
27
|
BUFFER_FILE="$BUFFER_DIR/telemetry.jsonl"
|
|
28
28
|
LOCK_DIR="$BUFFER_DIR/telemetry.lock"
|
|
29
29
|
SHARE_CONSENT_FILE="$BUFFER_DIR/telemetry-share-consent.json"
|
|
30
|
+
# Exact dataset pinned at proposal time. The confirm step sends this snapshot,
|
|
31
|
+
# so events emitted between proposal and confirm (attendant, other sessions)
|
|
32
|
+
# can never invalidate — or silently join — a disclosed share.
|
|
33
|
+
SHARE_STAGED_FILE="$BUFFER_DIR/telemetry.share-staged.jsonl"
|
|
30
34
|
STATE_FILE="${EGREGORE_STATE_FILE:-$SCRIPT_DIR/.egregore-state.json}"
|
|
31
35
|
CONFIG="${EGREGORE_CONFIG_FILE:-$SCRIPT_DIR/egregore.json}"
|
|
32
36
|
ENV_FILE="${EGREGORE_ENV_FILE:-$SCRIPT_DIR/.env}"
|
|
@@ -230,7 +234,8 @@ _validate_metrics() {
|
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
_validate_buffer() {
|
|
233
|
-
|
|
237
|
+
local target="${1:-$BUFFER_FILE}"
|
|
238
|
+
[ -s "$target" ] || return 0
|
|
234
239
|
jq -e --arg schema "$SCHEMA_VERSION" --argjson allowed "$ALLOWED_METRICS_JSON" '
|
|
235
240
|
def safe_id:
|
|
236
241
|
type == "string" and length > 0 and length <= 256 and
|
|
@@ -264,7 +269,7 @@ _validate_buffer() {
|
|
|
264
269
|
(.data | safe_metrics) and (.ts | type == "string") and
|
|
265
270
|
(.type | safe_id) and (.sid | safe_id) and (.org | safe_id) and (.user | safe_id)
|
|
266
271
|
end
|
|
267
|
-
' "$
|
|
272
|
+
' "$target" >/dev/null 2>&1
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
cmd_emit() {
|
|
@@ -422,7 +427,7 @@ cmd_clear() {
|
|
|
422
427
|
echo "Could not acquire the telemetry lock; nothing was cleared." >&2
|
|
423
428
|
return 1
|
|
424
429
|
fi
|
|
425
|
-
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE"
|
|
430
|
+
rm -f "$BUFFER_FILE" "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
426
431
|
_unlock
|
|
427
432
|
echo "Local telemetry buffer cleared."
|
|
428
433
|
}
|
|
@@ -525,31 +530,35 @@ cmd_share() {
|
|
|
525
530
|
https://*|http://127.0.0.1*|http://localhost*|http://\[::1\]*) ;;
|
|
526
531
|
*) echo "Share endpoint must use https (or loopback http)." >&2; return 2 ;;
|
|
527
532
|
esac
|
|
528
|
-
|
|
533
|
+
# Proposal validates the live buffer it is about to stage; confirm validates
|
|
534
|
+
# the staged snapshot it is about to send — never the moving buffer.
|
|
535
|
+
if [ -z "$confirmation" ] && ! _validate_buffer; then
|
|
529
536
|
echo "Share blocked: the local buffer contains an invalid or content-bearing event." >&2
|
|
530
537
|
return 2
|
|
531
538
|
fi
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
count=$(grep -c '^{' "$BUFFER_FILE" 2>/dev/null || true)
|
|
536
|
-
size=$(wc -c < "$BUFFER_FILE" 2>/dev/null | tr -d ' ')
|
|
537
|
-
dataset_sha256=$(_sha256_file "$BUFFER_FILE")
|
|
538
|
-
else
|
|
539
|
-
local empty_file="$BUFFER_DIR/telemetry.empty.$$"
|
|
540
|
-
mkdir -p "$BUFFER_DIR"
|
|
541
|
-
: > "$empty_file"
|
|
542
|
-
dataset_sha256=$(_sha256_file "$empty_file")
|
|
543
|
-
rm -f "$empty_file"
|
|
539
|
+
if [ -n "$confirmation" ] && ! _validate_buffer "$SHARE_STAGED_FILE"; then
|
|
540
|
+
echo "Share blocked: the staged dataset contains an invalid or content-bearing event." >&2
|
|
541
|
+
return 2
|
|
544
542
|
fi
|
|
545
543
|
|
|
544
|
+
local count=0 size=0 dataset_sha256=""
|
|
546
545
|
if [ -z "$confirmation" ]; then
|
|
547
|
-
|
|
546
|
+
mkdir -p "$BUFFER_DIR"
|
|
548
547
|
if [ -s "$BUFFER_FILE" ]; then
|
|
548
|
+
cp "$BUFFER_FILE" "$SHARE_STAGED_FILE"
|
|
549
|
+
else
|
|
550
|
+
: > "$SHARE_STAGED_FILE"
|
|
551
|
+
fi
|
|
552
|
+
chmod 600 "$SHARE_STAGED_FILE"
|
|
553
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
554
|
+
size=$(wc -c < "$SHARE_STAGED_FILE" 2>/dev/null | tr -d ' ')
|
|
555
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
556
|
+
local fields="none"
|
|
557
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
549
558
|
fields=$(jq -rs '
|
|
550
559
|
[.[].metrics // .[].data // {} | keys[]] | unique |
|
|
551
560
|
if length == 0 then "none" else map("metrics." + .) | join(", ") end
|
|
552
|
-
' "$
|
|
561
|
+
' "$SHARE_STAGED_FILE" 2>/dev/null || echo "unable to inspect")
|
|
553
562
|
fi
|
|
554
563
|
local token token_sha256 expires_at
|
|
555
564
|
token=$(_random_token)
|
|
@@ -600,10 +609,14 @@ cmd_share() {
|
|
|
600
609
|
return 0
|
|
601
610
|
fi
|
|
602
611
|
|
|
603
|
-
if [ ! -f "$SHARE_CONSENT_FILE" ]; then
|
|
612
|
+
if [ ! -f "$SHARE_CONSENT_FILE" ] || [ ! -f "$SHARE_STAGED_FILE" ]; then
|
|
604
613
|
echo "Share blocked: run share without --confirm to inspect and prepare this dataset first." >&2
|
|
605
614
|
return 2
|
|
606
615
|
fi
|
|
616
|
+
# The staged snapshot is the disclosed dataset: exactly what the proposal
|
|
617
|
+
# named is what ships, and later buffer growth cannot invalidate it.
|
|
618
|
+
count=$(grep -c '^{' "$SHARE_STAGED_FILE" 2>/dev/null || true)
|
|
619
|
+
dataset_sha256=$(_sha256_file "$SHARE_STAGED_FILE")
|
|
607
620
|
local expected_target expected_dataset expected_token expires_at actual_token
|
|
608
621
|
expected_target=$(jq -r '.target // empty' "$SHARE_CONSENT_FILE")
|
|
609
622
|
expected_dataset=$(jq -r '.dataset_sha256 // empty' "$SHARE_CONSENT_FILE")
|
|
@@ -611,7 +624,7 @@ cmd_share() {
|
|
|
611
624
|
expires_at=$(jq -r '.expires_at // 0' "$SHARE_CONSENT_FILE")
|
|
612
625
|
actual_token=$(_sha256_text "$confirmation")
|
|
613
626
|
if [ "$(date +%s)" -gt "$expires_at" ] 2>/dev/null; then
|
|
614
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
627
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
615
628
|
echo "Share blocked: confirmation expired; prepare a new share." >&2
|
|
616
629
|
return 2
|
|
617
630
|
fi
|
|
@@ -621,7 +634,7 @@ cmd_share() {
|
|
|
621
634
|
fi
|
|
622
635
|
|
|
623
636
|
local share_file="$BUFFER_DIR/telemetry.share.$$"
|
|
624
|
-
if [ -s "$
|
|
637
|
+
if [ -s "$SHARE_STAGED_FILE" ]; then
|
|
625
638
|
jq -c --arg schema "$SCHEMA_VERSION" '
|
|
626
639
|
if .schema_version == $schema then
|
|
627
640
|
{ts:.occurred_at,type:.event_type,sid:.session_id,org:.org_id,user:.actor_id,
|
|
@@ -629,7 +642,7 @@ cmd_share() {
|
|
|
629
642
|
artifact_ids:.artifact_ids,task_id:.task_id,org_revision:.org_revision,
|
|
630
643
|
index_spec_version:.index_spec_version,shared:true})}
|
|
631
644
|
else . end
|
|
632
|
-
' "$
|
|
645
|
+
' "$SHARE_STAGED_FILE" > "$share_file"
|
|
633
646
|
else
|
|
634
647
|
: > "$share_file"
|
|
635
648
|
fi
|
|
@@ -646,7 +659,7 @@ cmd_share() {
|
|
|
646
659
|
http_code=$(curl "${curl_args[@]}" 2>/dev/null || echo "000")
|
|
647
660
|
rm -f "$share_file"
|
|
648
661
|
if [ "$http_code" -ge 200 ] 2>/dev/null && [ "$http_code" -lt 300 ] 2>/dev/null; then
|
|
649
|
-
rm -f "$SHARE_CONSENT_FILE"
|
|
662
|
+
rm -f "$SHARE_CONSENT_FILE" "$SHARE_STAGED_FILE"
|
|
650
663
|
echo "Shared $count telemetry events with $endpoint. Local events were retained."
|
|
651
664
|
else
|
|
652
665
|
echo "Share failed (HTTP $http_code). Nothing was removed locally." >&2
|