create-egregore 0.21.0-runtime-mvp.8 → 0.21.0-runtime-mvp.9
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 +126 -14
- package/package.json +1 -1
- package/runtime/claude/bin/settings.sh +35 -1
- package/runtime/claude/bin/telemetry.sh +24 -1
- package/runtime/codex/bin/settings.sh +35 -1
- package/runtime/codex/bin/telemetry.sh +24 -1
- package/runtime/pi/bin/settings.sh +35 -1
- package/runtime/pi/bin/telemetry.sh +24 -1
- package/runtime/prime/bin/settings.sh +35 -1
- package/runtime/prime/bin/telemetry.sh +24 -1
|
@@ -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.9
|
|
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.9";
|
|
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");
|
|
@@ -2787,13 +2787,13 @@ async function cmdSettingsRuntime(inst, args) {
|
|
|
2787
2787
|
async function cmdSettings(args) {
|
|
2788
2788
|
const registry = readRegistry();
|
|
2789
2789
|
if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
|
|
2790
|
-
const DOMAINS = new Set(["hosting", "workflow", "repo", "admin", "people", "dump", "runtime", "privacy", "telemetry", "boundary"]);
|
|
2790
|
+
const DOMAINS = new Set(["hosting", "workflow", "repo", "admin", "people", "dump", "runtime", "privacy", "telemetry", "boundary", "posture"]);
|
|
2791
2791
|
let rest = args.slice();
|
|
2792
2792
|
let slug = "";
|
|
2793
2793
|
if (rest.length && !DOMAINS.has(rest[0])) { slug = rest[0]; rest = rest.slice(1); }
|
|
2794
2794
|
const inst = resolveInviteInstance(registry, slug);
|
|
2795
2795
|
if (!inst) {
|
|
2796
|
-
die(`multiple egregores installed; choose one:\n egregore settings <slug> [runtime|hosting|workflow|repo|admin|people|privacy|telemetry]\n run: egregore ls`);
|
|
2796
|
+
die(`multiple egregores installed; choose one:\n egregore settings <slug> [runtime|hosting|workflow|repo|admin|people|privacy|posture|telemetry]\n run: egregore ls`);
|
|
2797
2797
|
}
|
|
2798
2798
|
// Runtime update is Runtime-owned state, not org config — it routes to the
|
|
2799
2799
|
// upgrade bridge instead of bin/settings.sh.
|
|
@@ -3208,14 +3208,58 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
|
|
|
3208
3208
|
lines.push(boxLine(`${BOLD}${view === "privacy" ? "Privacy & access" : "Telemetry"}${RESET}`, innerWidth, margin));
|
|
3209
3209
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3210
3210
|
if (mode === "shareinfo") {
|
|
3211
|
-
const
|
|
3211
|
+
const proposal = data && data.shareProposal;
|
|
3212
3212
|
lines.push(boxLine(`${BOLD}Share proposal${RESET} ${DIM}(nothing has been sent)${RESET}`, innerWidth, margin));
|
|
3213
3213
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3214
|
-
|
|
3215
|
-
lines.push(boxLine(`${
|
|
3214
|
+
if (proposal && proposal.token) {
|
|
3215
|
+
lines.push(boxLine(`${DIM}Target: ${proposal.target}${RESET}`, innerWidth, margin));
|
|
3216
|
+
lines.push(boxLine(`${DIM}Events: ${proposal.events} · Bytes: ${proposal.bytes}${RESET}`, innerWidth, margin));
|
|
3217
|
+
lines.push(boxLine(`${DIM}Dataset SHA-256: ${proposal.dataset_sha256}${RESET}`, innerWidth, margin));
|
|
3218
|
+
lines.push(boxLine(`${DIM}Metric fields: ${proposal.metric_fields}${RESET}`, innerWidth, margin));
|
|
3219
|
+
lines.push(boxLine(`${DIM}Excluded by validation: ${proposal.excluded}${RESET}`, innerWidth, margin));
|
|
3220
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3221
|
+
if (input) {
|
|
3222
|
+
lines.push(boxLine(`${YELLOW}${input}${RESET}`, innerWidth, margin));
|
|
3223
|
+
} else {
|
|
3224
|
+
lines.push(boxLine(`${DIM}Nothing is sent until you confirm here. ${RESET}${BOLD}s${RESET}${DIM} send now · esc back${RESET}`, innerWidth, margin));
|
|
3225
|
+
}
|
|
3226
|
+
} else {
|
|
3227
|
+
// Older adapter without a machine-readable proposal: show its prose
|
|
3228
|
+
// and fall back to the terminal confirm instruction it printed.
|
|
3229
|
+
const shareLines = (data && data.shareLines) || ["share proposal unavailable"];
|
|
3230
|
+
for (const line of shareLines.slice(0, 16)) {
|
|
3231
|
+
lines.push(boxLine(`${line.includes("--confirm") ? "" : DIM}${line}${RESET}`, innerWidth, margin));
|
|
3232
|
+
}
|
|
3233
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3234
|
+
lines.push(boxLine(`${DIM}Sharing completes only by running the confirm command above in a terminal. ← back${RESET}`, innerWidth, margin));
|
|
3216
3235
|
}
|
|
3217
3236
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3218
|
-
lines.push(
|
|
3237
|
+
lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
|
|
3238
|
+
return lines;
|
|
3239
|
+
}
|
|
3240
|
+
if (mode === "posture") {
|
|
3241
|
+
const current = (data && data.privacySnapshot && data.privacySnapshot.boundary && data.privacySnapshot.boundary.posture) || "standard";
|
|
3242
|
+
lines.push(boxLine(`${BOLD}Boundary posture${RESET} ${DIM}— how far reads may reach outside this egregore${RESET}`, innerWidth, margin));
|
|
3243
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3244
|
+
const options = [
|
|
3245
|
+
["strict", "reads outside the project, memory, and managed repos always ask first"],
|
|
3246
|
+
["standard", "inbox folders (Downloads, Desktop) readable without asking; everything else asks first"],
|
|
3247
|
+
["open", "reads outside the boundary allowed without asking; writes outside still ask"],
|
|
3248
|
+
];
|
|
3249
|
+
options.forEach(([name, why], i) => {
|
|
3250
|
+
const sel = i === (listCursor || 0);
|
|
3251
|
+
const mark = name === current ? `${GREEN}● ${RESET}` : " ";
|
|
3252
|
+
lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${mark}${sel ? BOLD : ""}${name}${RESET}`, innerWidth, margin));
|
|
3253
|
+
lines.push(boxLine(` ${DIM}${why}${RESET}`, innerWidth, margin));
|
|
3254
|
+
});
|
|
3255
|
+
lines.push(boxLine(` ${DIM}other Egregore instances stay isolated under every posture${RESET}`, innerWidth, margin));
|
|
3256
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3257
|
+
if (input) {
|
|
3258
|
+
lines.push(boxLine(`${YELLOW}${input}${RESET}`, innerWidth, margin));
|
|
3259
|
+
} else {
|
|
3260
|
+
if (notice) lines.push(boxLine(notice, innerWidth, margin));
|
|
3261
|
+
lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}enter${RESET}${DIM} select · esc cancel${RESET}`, innerWidth, margin));
|
|
3262
|
+
}
|
|
3219
3263
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
3220
3264
|
lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
|
|
3221
3265
|
return lines;
|
|
@@ -3300,7 +3344,7 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
|
|
|
3300
3344
|
lines.push(boxLine(
|
|
3301
3345
|
locked
|
|
3302
3346
|
? `${DIM}Boundary policy is managed by your organization. ← back · q quit${RESET}`
|
|
3303
|
-
: `${DIM}${RESET}${BOLD}a${RESET}${DIM} add personal read root · ${RESET}${BOLD}r${RESET}${DIM} remove · ← back · q quit${RESET}`,
|
|
3347
|
+
: `${DIM}${RESET}${BOLD}a${RESET}${DIM} add personal read root · ${RESET}${BOLD}r${RESET}${DIM} remove · ${RESET}${BOLD}p${RESET}${DIM} boundary posture · ← back · q quit${RESET}`,
|
|
3304
3348
|
innerWidth, margin,
|
|
3305
3349
|
));
|
|
3306
3350
|
} else {
|
|
@@ -3966,16 +4010,49 @@ async function browser(args = {}) {
|
|
|
3966
4010
|
render(); return;
|
|
3967
4011
|
}
|
|
3968
4012
|
if (chunk === "s") {
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
4013
|
+
// Prefer the machine-readable proposal so the send can complete
|
|
4014
|
+
// here; fall back to the adapter's prose for older runtimes.
|
|
4015
|
+
let proposal = null;
|
|
4016
|
+
const rj = runTelemetry(S.settingsInst, ["share", "--json"]);
|
|
4017
|
+
if (rj && rj.status === 0 && rj.stdout) {
|
|
4018
|
+
try { proposal = JSON.parse(rj.stdout); } catch { proposal = null; }
|
|
4019
|
+
}
|
|
4020
|
+
if (proposal && proposal.token) {
|
|
4021
|
+
S.settingsData = { ...(S.settingsData || {}), shareProposal: proposal, shareLines: null };
|
|
4022
|
+
} else {
|
|
4023
|
+
const r = runTelemetry(S.settingsInst, ["share"]);
|
|
4024
|
+
const shareLines = ((r && (r.stdout || r.stderr)) || "share preview unavailable").split("\n").filter(Boolean);
|
|
4025
|
+
S.settingsData = { ...(S.settingsData || {}), shareProposal: null, shareLines };
|
|
4026
|
+
}
|
|
4027
|
+
S.settingsMode = "shareinfo"; S.settingsNotice = ""; S.settingsInput = ""; S.settingsAction = "";
|
|
3973
4028
|
render(); return;
|
|
3974
4029
|
}
|
|
3975
4030
|
return;
|
|
3976
4031
|
}
|
|
3977
4032
|
if (S.settingsMode === "shareinfo") {
|
|
3978
|
-
|
|
4033
|
+
const proposal = S.settingsData && S.settingsData.shareProposal;
|
|
4034
|
+
if (S.settingsAction === "tele-share" && S.settingsInput) {
|
|
4035
|
+
if (chunk === "y" || chunk === "Y") {
|
|
4036
|
+
const r = runTelemetry(S.settingsInst, ["share", "--confirm", proposal.token]);
|
|
4037
|
+
S.settingsNotice = r && r.status === 0
|
|
4038
|
+
? `${GREEN}${((r.stdout || "shared").split("\n")[0])}${RESET}`
|
|
4039
|
+
: `${YELLOW}${(((r && (r.stderr || r.stdout)) || "share failed").split("\n")[0])}${RESET}`;
|
|
4040
|
+
S.settingsAction = ""; S.settingsInput = "";
|
|
4041
|
+
S.settingsData = { ...(S.settingsData || {}), shareProposal: null, telemetryEvents: telemetryEvents(S.settingsInst, 100) };
|
|
4042
|
+
S.settingsMode = "events"; S.settingsListCursor = 0;
|
|
4043
|
+
refreshPanel(); render(); return;
|
|
4044
|
+
}
|
|
4045
|
+
S.settingsAction = ""; S.settingsInput = ""; render(); return;
|
|
4046
|
+
}
|
|
4047
|
+
if (chunk === "s" && proposal && proposal.token) {
|
|
4048
|
+
S.settingsAction = "tele-share";
|
|
4049
|
+
S.settingsInput = `Send ${proposal.events} event(s) to ${proposal.target} now? Local events are retained. [y/N]`;
|
|
4050
|
+
render(); return;
|
|
4051
|
+
}
|
|
4052
|
+
if (key.name === "escape" || key.name === "left") {
|
|
4053
|
+
S.settingsAction = ""; S.settingsInput = "";
|
|
4054
|
+
S.settingsMode = "events"; render(); return;
|
|
4055
|
+
}
|
|
3979
4056
|
return;
|
|
3980
4057
|
}
|
|
3981
4058
|
if (S.settingsMode === "pickdir") {
|
|
@@ -4003,6 +4080,34 @@ async function browser(args = {}) {
|
|
|
4003
4080
|
}
|
|
4004
4081
|
return;
|
|
4005
4082
|
}
|
|
4083
|
+
if (S.settingsMode === "posture") {
|
|
4084
|
+
const POSTURES = ["strict", "standard", "open"];
|
|
4085
|
+
const current = (S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.posture) || "standard";
|
|
4086
|
+
if (S.settingsAction === "posture" && S.settingsInput) {
|
|
4087
|
+
if (chunk === "y" || chunk === "Y") {
|
|
4088
|
+
const r = runSettings(S.settingsInst, ["posture", S.settingsPendingDir]);
|
|
4089
|
+
S.settingsNotice = r && r.status === 0
|
|
4090
|
+
? `${GREEN}${((r.stdout || "done").split("\n")[0])}${RESET}`
|
|
4091
|
+
: `${YELLOW}${(((r && (r.stderr || r.stdout)) || "posture change failed").split("\n")[0])}${RESET}`;
|
|
4092
|
+
S.settingsAction = ""; S.settingsInput = ""; S.settingsPendingDir = "";
|
|
4093
|
+
S.settingsMode = "browse";
|
|
4094
|
+
refreshPanel(); render(); return;
|
|
4095
|
+
}
|
|
4096
|
+
S.settingsAction = ""; S.settingsInput = ""; S.settingsPendingDir = ""; render(); return;
|
|
4097
|
+
}
|
|
4098
|
+
if (key.name === "escape" || key.name === "left") { S.settingsMode = "browse"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return; }
|
|
4099
|
+
if (key.name === "up" || key.name === "k") { S.settingsListCursor = ((S.settingsListCursor || 0) + 2) % 3; render(); return; }
|
|
4100
|
+
if (key.name === "down" || key.name === "j") { S.settingsListCursor = ((S.settingsListCursor || 0) + 1) % 3; render(); return; }
|
|
4101
|
+
if (key.name === "return") {
|
|
4102
|
+
const chosen = POSTURES[Math.min(S.settingsListCursor || 0, 2)];
|
|
4103
|
+
if (chosen === current) { S.settingsNotice = `${DIM}already on ${chosen}${RESET}`; render(); return; }
|
|
4104
|
+
S.settingsAction = "posture";
|
|
4105
|
+
S.settingsPendingDir = chosen;
|
|
4106
|
+
S.settingsInput = `Switch boundary posture to ${chosen.toUpperCase()} for the whole organization? This edits the org's committed egregore.json for everyone. [y/N]`;
|
|
4107
|
+
render(); return;
|
|
4108
|
+
}
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
4006
4111
|
if (S.settingsMode === "pickroot") {
|
|
4007
4112
|
const roots = (S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.personal_read_roots) || [];
|
|
4008
4113
|
if (key.name === "escape" || key.name === "left") { S.settingsMode = "browse"; S.settingsListCursor = 0; render(); return; }
|
|
@@ -4050,6 +4155,13 @@ async function browser(args = {}) {
|
|
|
4050
4155
|
if (!locked && chunk === "r") {
|
|
4051
4156
|
S.settingsMode = "pickroot"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return;
|
|
4052
4157
|
}
|
|
4158
|
+
if (!locked && chunk === "p") {
|
|
4159
|
+
const current = (S.settingsData && S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.posture) || "standard";
|
|
4160
|
+
S.settingsMode = "posture";
|
|
4161
|
+
S.settingsListCursor = Math.max(0, ["strict", "standard", "open"].indexOf(current));
|
|
4162
|
+
S.settingsNotice = ""; S.settingsInput = ""; S.settingsAction = "";
|
|
4163
|
+
render(); return;
|
|
4164
|
+
}
|
|
4053
4165
|
return;
|
|
4054
4166
|
}
|
|
4055
4167
|
// telemetry view
|
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.9",
|
|
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": {
|
|
@@ -59,6 +59,7 @@ settings.sh — Egregore instance settings
|
|
|
59
59
|
settings.sh repo list | add <name> [description] | remove <name>
|
|
60
60
|
settings.sh admin list | add <github-handle> | remove <github-handle>
|
|
61
61
|
settings.sh people list | add <github-handle> | remove <github-handle>
|
|
62
|
+
settings.sh posture status|strict|standard|open org-wide boundary posture
|
|
62
63
|
settings.sh dump full snapshot (JSON)
|
|
63
64
|
|
|
64
65
|
--json machine-readable output for status/list
|
|
@@ -441,6 +442,38 @@ cmd_privacy() {
|
|
|
441
442
|
}'
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
# ── boundary posture (org-wide) ──────────────────────────────────────────
|
|
446
|
+
# Edits committed egregore.json for the whole organization. `locked: true`
|
|
447
|
+
# removes the change path entirely — posture is then org-policy managed.
|
|
448
|
+
|
|
449
|
+
cmd_posture() {
|
|
450
|
+
local action="${1:-status}" current
|
|
451
|
+
current="$(jq -r '.boundary.posture // "standard"' "$CONFIG" 2>/dev/null || echo standard)"
|
|
452
|
+
case "$current" in strict|standard|open) ;; *) current="standard" ;; esac
|
|
453
|
+
case "$action" in
|
|
454
|
+
status)
|
|
455
|
+
if [ "$JSON" = 1 ]; then
|
|
456
|
+
jq -n --arg posture "$current" \
|
|
457
|
+
--argjson locked "$(_boundary_locked && echo true || echo false)" \
|
|
458
|
+
'{domain:"posture", posture:$posture, locked:$locked}'
|
|
459
|
+
else
|
|
460
|
+
echo "Boundary posture: $current"
|
|
461
|
+
_boundary_locked && echo "(organization boundary policy is locked — posture cannot change)"
|
|
462
|
+
fi
|
|
463
|
+
;;
|
|
464
|
+
strict|standard|open)
|
|
465
|
+
if _boundary_locked; then
|
|
466
|
+
_die "boundary policy is managed by your organization and locked; posture cannot change" 1
|
|
467
|
+
fi
|
|
468
|
+
if [ "$action" != "$current" ]; then
|
|
469
|
+
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
|
+
fi
|
|
471
|
+
echo "Boundary posture: $action"
|
|
472
|
+
;;
|
|
473
|
+
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
|
+
esac
|
|
475
|
+
}
|
|
476
|
+
|
|
444
477
|
# ── personal boundary read roots ─────────────────────────────────────────
|
|
445
478
|
# Personal-only: edits .egregore-boundary.local.json for the current member.
|
|
446
479
|
# Respects org `locked: true` (no personal expansion), and never accepts a
|
|
@@ -525,8 +558,9 @@ case "$DOMAIN" in
|
|
|
525
558
|
admin) cmd_admin "$@" ;;
|
|
526
559
|
people) cmd_people "$@" ;;
|
|
527
560
|
privacy) cmd_privacy ;;
|
|
561
|
+
posture) cmd_posture "$@" ;;
|
|
528
562
|
boundary) cmd_boundary "$@" ;;
|
|
529
563
|
dump) cmd_dump ;;
|
|
530
564
|
""|-h|--help|help) usage 0 ;;
|
|
531
|
-
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, boundary, dump)" 1 ;;
|
|
565
|
+
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, posture, boundary, dump)" 1 ;;
|
|
532
566
|
esac
|
|
@@ -484,7 +484,7 @@ _resolve_share_target() {
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
cmd_share() {
|
|
487
|
-
local confirmation="" requested_endpoint="" allow_legacy_relay="false"
|
|
487
|
+
local confirmation="" requested_endpoint="" allow_legacy_relay="false" json_output="false"
|
|
488
488
|
while [ "$#" -gt 0 ]; do
|
|
489
489
|
case "$1" in
|
|
490
490
|
--confirm)
|
|
@@ -499,6 +499,10 @@ cmd_share() {
|
|
|
499
499
|
allow_legacy_relay="true"
|
|
500
500
|
shift
|
|
501
501
|
;;
|
|
502
|
+
--json)
|
|
503
|
+
json_output="true"
|
|
504
|
+
shift
|
|
505
|
+
;;
|
|
502
506
|
*)
|
|
503
507
|
echo "Unknown share option: $1" >&2
|
|
504
508
|
return 2
|
|
@@ -561,6 +565,25 @@ cmd_share() {
|
|
|
561
565
|
> "$SHARE_CONSENT_FILE"
|
|
562
566
|
chmod 600 "$SHARE_CONSENT_FILE"
|
|
563
567
|
|
|
568
|
+
if [ "$json_output" = "true" ]; then
|
|
569
|
+
# Machine-readable proposal for thin surfaces (the launcher). Same
|
|
570
|
+
# consent protocol: the token here is the only place it exists, and a
|
|
571
|
+
# send still requires a separate explicit `share --confirm`.
|
|
572
|
+
jq -n \
|
|
573
|
+
--arg target "$endpoint" \
|
|
574
|
+
--argjson events "${count:-0}" \
|
|
575
|
+
--argjson bytes "${size:-0}" \
|
|
576
|
+
--arg dataset_sha256 "$dataset_sha256" \
|
|
577
|
+
--arg metric_fields "$fields" \
|
|
578
|
+
--arg token "$token" \
|
|
579
|
+
--argjson expires_at "$expires_at" \
|
|
580
|
+
'{proposal:true, sent:false, target:$target, events:$events, bytes:$bytes,
|
|
581
|
+
dataset_sha256:$dataset_sha256, metric_fields:$metric_fields,
|
|
582
|
+
excluded:"prompts, content, passages, file paths, arguments, secrets, env values",
|
|
583
|
+
token:$token, expires_at:$expires_at}'
|
|
584
|
+
return 0
|
|
585
|
+
fi
|
|
586
|
+
|
|
564
587
|
echo "Telemetry share proposal (nothing sent):"
|
|
565
588
|
echo ""
|
|
566
589
|
echo " Target: $endpoint"
|
|
@@ -59,6 +59,7 @@ settings.sh — Egregore instance settings
|
|
|
59
59
|
settings.sh repo list | add <name> [description] | remove <name>
|
|
60
60
|
settings.sh admin list | add <github-handle> | remove <github-handle>
|
|
61
61
|
settings.sh people list | add <github-handle> | remove <github-handle>
|
|
62
|
+
settings.sh posture status|strict|standard|open org-wide boundary posture
|
|
62
63
|
settings.sh dump full snapshot (JSON)
|
|
63
64
|
|
|
64
65
|
--json machine-readable output for status/list
|
|
@@ -441,6 +442,38 @@ cmd_privacy() {
|
|
|
441
442
|
}'
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
# ── boundary posture (org-wide) ──────────────────────────────────────────
|
|
446
|
+
# Edits committed egregore.json for the whole organization. `locked: true`
|
|
447
|
+
# removes the change path entirely — posture is then org-policy managed.
|
|
448
|
+
|
|
449
|
+
cmd_posture() {
|
|
450
|
+
local action="${1:-status}" current
|
|
451
|
+
current="$(jq -r '.boundary.posture // "standard"' "$CONFIG" 2>/dev/null || echo standard)"
|
|
452
|
+
case "$current" in strict|standard|open) ;; *) current="standard" ;; esac
|
|
453
|
+
case "$action" in
|
|
454
|
+
status)
|
|
455
|
+
if [ "$JSON" = 1 ]; then
|
|
456
|
+
jq -n --arg posture "$current" \
|
|
457
|
+
--argjson locked "$(_boundary_locked && echo true || echo false)" \
|
|
458
|
+
'{domain:"posture", posture:$posture, locked:$locked}'
|
|
459
|
+
else
|
|
460
|
+
echo "Boundary posture: $current"
|
|
461
|
+
_boundary_locked && echo "(organization boundary policy is locked — posture cannot change)"
|
|
462
|
+
fi
|
|
463
|
+
;;
|
|
464
|
+
strict|standard|open)
|
|
465
|
+
if _boundary_locked; then
|
|
466
|
+
_die "boundary policy is managed by your organization and locked; posture cannot change" 1
|
|
467
|
+
fi
|
|
468
|
+
if [ "$action" != "$current" ]; then
|
|
469
|
+
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
|
+
fi
|
|
471
|
+
echo "Boundary posture: $action"
|
|
472
|
+
;;
|
|
473
|
+
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
|
+
esac
|
|
475
|
+
}
|
|
476
|
+
|
|
444
477
|
# ── personal boundary read roots ─────────────────────────────────────────
|
|
445
478
|
# Personal-only: edits .egregore-boundary.local.json for the current member.
|
|
446
479
|
# Respects org `locked: true` (no personal expansion), and never accepts a
|
|
@@ -525,8 +558,9 @@ case "$DOMAIN" in
|
|
|
525
558
|
admin) cmd_admin "$@" ;;
|
|
526
559
|
people) cmd_people "$@" ;;
|
|
527
560
|
privacy) cmd_privacy ;;
|
|
561
|
+
posture) cmd_posture "$@" ;;
|
|
528
562
|
boundary) cmd_boundary "$@" ;;
|
|
529
563
|
dump) cmd_dump ;;
|
|
530
564
|
""|-h|--help|help) usage 0 ;;
|
|
531
|
-
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, boundary, dump)" 1 ;;
|
|
565
|
+
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, posture, boundary, dump)" 1 ;;
|
|
532
566
|
esac
|
|
@@ -484,7 +484,7 @@ _resolve_share_target() {
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
cmd_share() {
|
|
487
|
-
local confirmation="" requested_endpoint="" allow_legacy_relay="false"
|
|
487
|
+
local confirmation="" requested_endpoint="" allow_legacy_relay="false" json_output="false"
|
|
488
488
|
while [ "$#" -gt 0 ]; do
|
|
489
489
|
case "$1" in
|
|
490
490
|
--confirm)
|
|
@@ -499,6 +499,10 @@ cmd_share() {
|
|
|
499
499
|
allow_legacy_relay="true"
|
|
500
500
|
shift
|
|
501
501
|
;;
|
|
502
|
+
--json)
|
|
503
|
+
json_output="true"
|
|
504
|
+
shift
|
|
505
|
+
;;
|
|
502
506
|
*)
|
|
503
507
|
echo "Unknown share option: $1" >&2
|
|
504
508
|
return 2
|
|
@@ -561,6 +565,25 @@ cmd_share() {
|
|
|
561
565
|
> "$SHARE_CONSENT_FILE"
|
|
562
566
|
chmod 600 "$SHARE_CONSENT_FILE"
|
|
563
567
|
|
|
568
|
+
if [ "$json_output" = "true" ]; then
|
|
569
|
+
# Machine-readable proposal for thin surfaces (the launcher). Same
|
|
570
|
+
# consent protocol: the token here is the only place it exists, and a
|
|
571
|
+
# send still requires a separate explicit `share --confirm`.
|
|
572
|
+
jq -n \
|
|
573
|
+
--arg target "$endpoint" \
|
|
574
|
+
--argjson events "${count:-0}" \
|
|
575
|
+
--argjson bytes "${size:-0}" \
|
|
576
|
+
--arg dataset_sha256 "$dataset_sha256" \
|
|
577
|
+
--arg metric_fields "$fields" \
|
|
578
|
+
--arg token "$token" \
|
|
579
|
+
--argjson expires_at "$expires_at" \
|
|
580
|
+
'{proposal:true, sent:false, target:$target, events:$events, bytes:$bytes,
|
|
581
|
+
dataset_sha256:$dataset_sha256, metric_fields:$metric_fields,
|
|
582
|
+
excluded:"prompts, content, passages, file paths, arguments, secrets, env values",
|
|
583
|
+
token:$token, expires_at:$expires_at}'
|
|
584
|
+
return 0
|
|
585
|
+
fi
|
|
586
|
+
|
|
564
587
|
echo "Telemetry share proposal (nothing sent):"
|
|
565
588
|
echo ""
|
|
566
589
|
echo " Target: $endpoint"
|
|
@@ -59,6 +59,7 @@ settings.sh — Egregore instance settings
|
|
|
59
59
|
settings.sh repo list | add <name> [description] | remove <name>
|
|
60
60
|
settings.sh admin list | add <github-handle> | remove <github-handle>
|
|
61
61
|
settings.sh people list | add <github-handle> | remove <github-handle>
|
|
62
|
+
settings.sh posture status|strict|standard|open org-wide boundary posture
|
|
62
63
|
settings.sh dump full snapshot (JSON)
|
|
63
64
|
|
|
64
65
|
--json machine-readable output for status/list
|
|
@@ -441,6 +442,38 @@ cmd_privacy() {
|
|
|
441
442
|
}'
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
# ── boundary posture (org-wide) ──────────────────────────────────────────
|
|
446
|
+
# Edits committed egregore.json for the whole organization. `locked: true`
|
|
447
|
+
# removes the change path entirely — posture is then org-policy managed.
|
|
448
|
+
|
|
449
|
+
cmd_posture() {
|
|
450
|
+
local action="${1:-status}" current
|
|
451
|
+
current="$(jq -r '.boundary.posture // "standard"' "$CONFIG" 2>/dev/null || echo standard)"
|
|
452
|
+
case "$current" in strict|standard|open) ;; *) current="standard" ;; esac
|
|
453
|
+
case "$action" in
|
|
454
|
+
status)
|
|
455
|
+
if [ "$JSON" = 1 ]; then
|
|
456
|
+
jq -n --arg posture "$current" \
|
|
457
|
+
--argjson locked "$(_boundary_locked && echo true || echo false)" \
|
|
458
|
+
'{domain:"posture", posture:$posture, locked:$locked}'
|
|
459
|
+
else
|
|
460
|
+
echo "Boundary posture: $current"
|
|
461
|
+
_boundary_locked && echo "(organization boundary policy is locked — posture cannot change)"
|
|
462
|
+
fi
|
|
463
|
+
;;
|
|
464
|
+
strict|standard|open)
|
|
465
|
+
if _boundary_locked; then
|
|
466
|
+
_die "boundary policy is managed by your organization and locked; posture cannot change" 1
|
|
467
|
+
fi
|
|
468
|
+
if [ "$action" != "$current" ]; then
|
|
469
|
+
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
|
+
fi
|
|
471
|
+
echo "Boundary posture: $action"
|
|
472
|
+
;;
|
|
473
|
+
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
|
+
esac
|
|
475
|
+
}
|
|
476
|
+
|
|
444
477
|
# ── personal boundary read roots ─────────────────────────────────────────
|
|
445
478
|
# Personal-only: edits .egregore-boundary.local.json for the current member.
|
|
446
479
|
# Respects org `locked: true` (no personal expansion), and never accepts a
|
|
@@ -525,8 +558,9 @@ case "$DOMAIN" in
|
|
|
525
558
|
admin) cmd_admin "$@" ;;
|
|
526
559
|
people) cmd_people "$@" ;;
|
|
527
560
|
privacy) cmd_privacy ;;
|
|
561
|
+
posture) cmd_posture "$@" ;;
|
|
528
562
|
boundary) cmd_boundary "$@" ;;
|
|
529
563
|
dump) cmd_dump ;;
|
|
530
564
|
""|-h|--help|help) usage 0 ;;
|
|
531
|
-
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, boundary, dump)" 1 ;;
|
|
565
|
+
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, posture, boundary, dump)" 1 ;;
|
|
532
566
|
esac
|
|
@@ -484,7 +484,7 @@ _resolve_share_target() {
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
cmd_share() {
|
|
487
|
-
local confirmation="" requested_endpoint="" allow_legacy_relay="false"
|
|
487
|
+
local confirmation="" requested_endpoint="" allow_legacy_relay="false" json_output="false"
|
|
488
488
|
while [ "$#" -gt 0 ]; do
|
|
489
489
|
case "$1" in
|
|
490
490
|
--confirm)
|
|
@@ -499,6 +499,10 @@ cmd_share() {
|
|
|
499
499
|
allow_legacy_relay="true"
|
|
500
500
|
shift
|
|
501
501
|
;;
|
|
502
|
+
--json)
|
|
503
|
+
json_output="true"
|
|
504
|
+
shift
|
|
505
|
+
;;
|
|
502
506
|
*)
|
|
503
507
|
echo "Unknown share option: $1" >&2
|
|
504
508
|
return 2
|
|
@@ -561,6 +565,25 @@ cmd_share() {
|
|
|
561
565
|
> "$SHARE_CONSENT_FILE"
|
|
562
566
|
chmod 600 "$SHARE_CONSENT_FILE"
|
|
563
567
|
|
|
568
|
+
if [ "$json_output" = "true" ]; then
|
|
569
|
+
# Machine-readable proposal for thin surfaces (the launcher). Same
|
|
570
|
+
# consent protocol: the token here is the only place it exists, and a
|
|
571
|
+
# send still requires a separate explicit `share --confirm`.
|
|
572
|
+
jq -n \
|
|
573
|
+
--arg target "$endpoint" \
|
|
574
|
+
--argjson events "${count:-0}" \
|
|
575
|
+
--argjson bytes "${size:-0}" \
|
|
576
|
+
--arg dataset_sha256 "$dataset_sha256" \
|
|
577
|
+
--arg metric_fields "$fields" \
|
|
578
|
+
--arg token "$token" \
|
|
579
|
+
--argjson expires_at "$expires_at" \
|
|
580
|
+
'{proposal:true, sent:false, target:$target, events:$events, bytes:$bytes,
|
|
581
|
+
dataset_sha256:$dataset_sha256, metric_fields:$metric_fields,
|
|
582
|
+
excluded:"prompts, content, passages, file paths, arguments, secrets, env values",
|
|
583
|
+
token:$token, expires_at:$expires_at}'
|
|
584
|
+
return 0
|
|
585
|
+
fi
|
|
586
|
+
|
|
564
587
|
echo "Telemetry share proposal (nothing sent):"
|
|
565
588
|
echo ""
|
|
566
589
|
echo " Target: $endpoint"
|
|
@@ -59,6 +59,7 @@ settings.sh — Egregore instance settings
|
|
|
59
59
|
settings.sh repo list | add <name> [description] | remove <name>
|
|
60
60
|
settings.sh admin list | add <github-handle> | remove <github-handle>
|
|
61
61
|
settings.sh people list | add <github-handle> | remove <github-handle>
|
|
62
|
+
settings.sh posture status|strict|standard|open org-wide boundary posture
|
|
62
63
|
settings.sh dump full snapshot (JSON)
|
|
63
64
|
|
|
64
65
|
--json machine-readable output for status/list
|
|
@@ -441,6 +442,38 @@ cmd_privacy() {
|
|
|
441
442
|
}'
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
# ── boundary posture (org-wide) ──────────────────────────────────────────
|
|
446
|
+
# Edits committed egregore.json for the whole organization. `locked: true`
|
|
447
|
+
# removes the change path entirely — posture is then org-policy managed.
|
|
448
|
+
|
|
449
|
+
cmd_posture() {
|
|
450
|
+
local action="${1:-status}" current
|
|
451
|
+
current="$(jq -r '.boundary.posture // "standard"' "$CONFIG" 2>/dev/null || echo standard)"
|
|
452
|
+
case "$current" in strict|standard|open) ;; *) current="standard" ;; esac
|
|
453
|
+
case "$action" in
|
|
454
|
+
status)
|
|
455
|
+
if [ "$JSON" = 1 ]; then
|
|
456
|
+
jq -n --arg posture "$current" \
|
|
457
|
+
--argjson locked "$(_boundary_locked && echo true || echo false)" \
|
|
458
|
+
'{domain:"posture", posture:$posture, locked:$locked}'
|
|
459
|
+
else
|
|
460
|
+
echo "Boundary posture: $current"
|
|
461
|
+
_boundary_locked && echo "(organization boundary policy is locked — posture cannot change)"
|
|
462
|
+
fi
|
|
463
|
+
;;
|
|
464
|
+
strict|standard|open)
|
|
465
|
+
if _boundary_locked; then
|
|
466
|
+
_die "boundary policy is managed by your organization and locked; posture cannot change" 1
|
|
467
|
+
fi
|
|
468
|
+
if [ "$action" != "$current" ]; then
|
|
469
|
+
_save '.boundary = ((.boundary // {}) + {posture: $p})' --arg p "$action"
|
|
470
|
+
fi
|
|
471
|
+
echo "Boundary posture: $action"
|
|
472
|
+
;;
|
|
473
|
+
*) _die "usage: settings.sh posture status|strict|standard|open" 1 ;;
|
|
474
|
+
esac
|
|
475
|
+
}
|
|
476
|
+
|
|
444
477
|
# ── personal boundary read roots ─────────────────────────────────────────
|
|
445
478
|
# Personal-only: edits .egregore-boundary.local.json for the current member.
|
|
446
479
|
# Respects org `locked: true` (no personal expansion), and never accepts a
|
|
@@ -525,8 +558,9 @@ case "$DOMAIN" in
|
|
|
525
558
|
admin) cmd_admin "$@" ;;
|
|
526
559
|
people) cmd_people "$@" ;;
|
|
527
560
|
privacy) cmd_privacy ;;
|
|
561
|
+
posture) cmd_posture "$@" ;;
|
|
528
562
|
boundary) cmd_boundary "$@" ;;
|
|
529
563
|
dump) cmd_dump ;;
|
|
530
564
|
""|-h|--help|help) usage 0 ;;
|
|
531
|
-
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, boundary, dump)" 1 ;;
|
|
565
|
+
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, privacy, posture, boundary, dump)" 1 ;;
|
|
532
566
|
esac
|
|
@@ -484,7 +484,7 @@ _resolve_share_target() {
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
cmd_share() {
|
|
487
|
-
local confirmation="" requested_endpoint="" allow_legacy_relay="false"
|
|
487
|
+
local confirmation="" requested_endpoint="" allow_legacy_relay="false" json_output="false"
|
|
488
488
|
while [ "$#" -gt 0 ]; do
|
|
489
489
|
case "$1" in
|
|
490
490
|
--confirm)
|
|
@@ -499,6 +499,10 @@ cmd_share() {
|
|
|
499
499
|
allow_legacy_relay="true"
|
|
500
500
|
shift
|
|
501
501
|
;;
|
|
502
|
+
--json)
|
|
503
|
+
json_output="true"
|
|
504
|
+
shift
|
|
505
|
+
;;
|
|
502
506
|
*)
|
|
503
507
|
echo "Unknown share option: $1" >&2
|
|
504
508
|
return 2
|
|
@@ -561,6 +565,25 @@ cmd_share() {
|
|
|
561
565
|
> "$SHARE_CONSENT_FILE"
|
|
562
566
|
chmod 600 "$SHARE_CONSENT_FILE"
|
|
563
567
|
|
|
568
|
+
if [ "$json_output" = "true" ]; then
|
|
569
|
+
# Machine-readable proposal for thin surfaces (the launcher). Same
|
|
570
|
+
# consent protocol: the token here is the only place it exists, and a
|
|
571
|
+
# send still requires a separate explicit `share --confirm`.
|
|
572
|
+
jq -n \
|
|
573
|
+
--arg target "$endpoint" \
|
|
574
|
+
--argjson events "${count:-0}" \
|
|
575
|
+
--argjson bytes "${size:-0}" \
|
|
576
|
+
--arg dataset_sha256 "$dataset_sha256" \
|
|
577
|
+
--arg metric_fields "$fields" \
|
|
578
|
+
--arg token "$token" \
|
|
579
|
+
--argjson expires_at "$expires_at" \
|
|
580
|
+
'{proposal:true, sent:false, target:$target, events:$events, bytes:$bytes,
|
|
581
|
+
dataset_sha256:$dataset_sha256, metric_fields:$metric_fields,
|
|
582
|
+
excluded:"prompts, content, passages, file paths, arguments, secrets, env values",
|
|
583
|
+
token:$token, expires_at:$expires_at}'
|
|
584
|
+
return 0
|
|
585
|
+
fi
|
|
586
|
+
|
|
564
587
|
echo "Telemetry share proposal (nothing sent):"
|
|
565
588
|
echo ""
|
|
566
589
|
echo " Target: $endpoint"
|