create-egregore 0.19.7 → 0.19.11
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 +188 -295
- package/lib/local.js +11 -1
- package/lib/setup.js +33 -0
- package/package.json +1 -1
- package/runtime/codex/.codex/hooks/branch-guard.js +242 -34
- package/runtime/codex/.codex/skills/activity/SKILL.md +18 -6
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +90 -55
- package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/handoff/SKILL.md +6 -2
- package/runtime/codex/.codex/skills/save/SKILL.md +21 -3
- package/runtime/codex/.codex/skills/scroll/SKILL.md +54 -3
- package/runtime/codex/.codex/skills/task/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/tui-design/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/wrap/SKILL.md +4 -1
- package/runtime/codex/.codex/spec-manifest.json +15 -3
- package/runtime/codex/AGENTS.md +45 -9
- package/runtime/codex/bin/activity-data.sh +1 -1
- package/runtime/codex/bin/agent.sh +66 -23
- package/runtime/codex/bin/attendant.sh +525 -0
- package/runtime/codex/bin/capture-reconcile.sh +67 -0
- package/runtime/codex/bin/capture-run.sh +341 -0
- package/runtime/codex/bin/codex-render-spec.mjs +15 -4
- package/runtime/codex/bin/codex-session-start.sh +45 -3
- package/runtime/codex/bin/codex-skill-render.mjs +23 -4
- package/runtime/codex/bin/graph-op.sh +719 -49
- package/runtime/codex/bin/graph-wal.sh +3 -2
- package/runtime/codex/bin/handoff-run.sh +72 -0
- package/runtime/codex/bin/handoff-save-egregore.sh +51 -11
- package/runtime/codex/bin/index-handoff.sh +26 -17
- package/runtime/codex/bin/ingest-graph.sh +80 -0
- package/runtime/codex/bin/ingest-surface.html +429 -0
- package/runtime/codex/bin/ingest.py +614 -0
- package/runtime/codex/bin/ingest.sh +231 -0
- package/runtime/codex/bin/ingest_graph.py +776 -0
- package/runtime/codex/bin/ingest_surface.py +464 -0
- package/runtime/codex/bin/lib/git-sync.sh +2 -6
- package/runtime/codex/bin/lib/greeting.sh +157 -22
- package/runtime/codex/bin/lib/identity.sh +50 -29
- package/runtime/codex/bin/lib/noncode.sh +31 -0
- package/runtime/codex/bin/person.py +610 -0
- package/runtime/codex/bin/person.sh +448 -0
- package/runtime/codex/bin/session-autosave.sh +184 -0
- package/runtime/codex/bin/session-log.sh +199 -0
- package/runtime/codex/bin/settings.sh +1 -1
- package/runtime/codex/manifest.json +19 -1
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -34
- package/runtime/pi/.codex/skills/activity/SKILL.md +18 -6
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +90 -55
- package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +6 -2
- package/runtime/pi/.codex/skills/save/SKILL.md +21 -3
- package/runtime/pi/.codex/skills/scroll/SKILL.md +54 -3
- package/runtime/pi/.codex/skills/task/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/wrap/SKILL.md +4 -1
- package/runtime/pi/.codex/spec-manifest.json +15 -3
- package/runtime/pi/.pi/APPEND_SYSTEM.md +41 -5
- package/runtime/pi/.pi/extensions/egregore.ts +98 -8
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +45 -9
- package/runtime/pi/bin/activity-data.sh +1 -1
- package/runtime/pi/bin/agent.sh +66 -23
- package/runtime/pi/bin/attendant.sh +525 -0
- package/runtime/pi/bin/capture-reconcile.sh +67 -0
- package/runtime/pi/bin/capture-run.sh +341 -0
- package/runtime/pi/bin/codex-render-spec.mjs +15 -4
- package/runtime/pi/bin/codex-session-start.sh +45 -3
- package/runtime/pi/bin/codex-skill-render.mjs +23 -4
- package/runtime/pi/bin/graph-op.sh +719 -49
- package/runtime/pi/bin/graph-wal.sh +3 -2
- package/runtime/pi/bin/handoff-run.sh +72 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +51 -11
- package/runtime/pi/bin/index-handoff.sh +26 -17
- package/runtime/pi/bin/ingest-graph.sh +80 -0
- package/runtime/pi/bin/ingest-surface.html +429 -0
- package/runtime/pi/bin/ingest.py +614 -0
- package/runtime/pi/bin/ingest.sh +231 -0
- package/runtime/pi/bin/ingest_graph.py +776 -0
- package/runtime/pi/bin/ingest_surface.py +464 -0
- package/runtime/pi/bin/lib/git-sync.sh +2 -6
- package/runtime/pi/bin/lib/greeting.sh +157 -22
- package/runtime/pi/bin/lib/identity.sh +50 -29
- package/runtime/pi/bin/lib/noncode.sh +31 -0
- package/runtime/pi/bin/person.py +610 -0
- package/runtime/pi/bin/person.sh +448 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +164 -4
- package/runtime/pi/bin/pi-product-workflows.mjs +31 -2
- package/runtime/pi/bin/publish-artifact.sh +14 -0
- package/runtime/pi/bin/session-autosave.sh +184 -0
- package/runtime/pi/bin/session-log.sh +199 -0
- package/runtime/pi/bin/settings.sh +274 -0
- package/runtime/pi/manifest.json +20 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// egregore-launcher-version: 0.7.
|
|
2
|
+
// egregore-launcher-version: 0.7.3
|
|
3
3
|
//
|
|
4
4
|
// egregore — launch and browse Egregore instances from your terminal.
|
|
5
5
|
// Installed by create-egregore. Reads ~/.egregore/instances.json.
|
|
@@ -31,7 +31,7 @@ const https = require("node:https");
|
|
|
31
31
|
const { spawnSync, spawn } = require("node:child_process");
|
|
32
32
|
const readline = require("node:readline");
|
|
33
33
|
|
|
34
|
-
const VERSION = "0.7.
|
|
34
|
+
const VERSION = "0.7.3";
|
|
35
35
|
const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
|
|
36
36
|
const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
|
|
37
37
|
const PUBLIC_NPX_PACKAGE = "create-egregore";
|
|
@@ -1321,292 +1321,6 @@ function settingsDump(inst) {
|
|
|
1321
1321
|
try { return JSON.parse(r.stdout); } catch { return null; }
|
|
1322
1322
|
}
|
|
1323
1323
|
|
|
1324
|
-
// Nested raw-mode list editor for a domain (repo | admin | people): hover an
|
|
1325
|
-
// item and press `d` to remove it, `a` to open an inline add field, esc to go
|
|
1326
|
-
// back. Mirrors the main browser's affordances. Its own raw-mode loop; resolves
|
|
1327
|
-
// when the user backs out. Each mutation is ONE deterministic settings.sh verb;
|
|
1328
|
-
// validation, the last-admin guard, and GitHub-access notes surface as notices.
|
|
1329
|
-
function listEditor(inst, domain, label) {
|
|
1330
|
-
const fieldFor = (d) => {
|
|
1331
|
-
if (domain === "repo") return (d.repos || []).map((r) => r.name || r);
|
|
1332
|
-
if (domain === "admin") return d.admins || [];
|
|
1333
|
-
return d.people || [];
|
|
1334
|
-
};
|
|
1335
|
-
const noun = domain === "repo" ? "repo name" : "github handle";
|
|
1336
|
-
|
|
1337
|
-
return new Promise((resolve) => {
|
|
1338
|
-
let items = fieldFor(settingsDump(inst) || {});
|
|
1339
|
-
let cursor = 0;
|
|
1340
|
-
let mode = "browse"; // browse | add | confirm
|
|
1341
|
-
let input = "";
|
|
1342
|
-
let notice = "";
|
|
1343
|
-
let rendered = 0;
|
|
1344
|
-
let done = false;
|
|
1345
|
-
const previousRaw = !!process.stdin.isRaw;
|
|
1346
|
-
const isMouseEvent = makeMouseFilter();
|
|
1347
|
-
|
|
1348
|
-
const refresh = () => {
|
|
1349
|
-
items = fieldFor(settingsDump(inst) || {});
|
|
1350
|
-
if (cursor >= items.length) cursor = Math.max(0, items.length - 1);
|
|
1351
|
-
};
|
|
1352
|
-
|
|
1353
|
-
const erase = () => {
|
|
1354
|
-
if (!rendered) return;
|
|
1355
|
-
process.stdout.write(`\x1b[${rendered}A` + ANSI.clearDown);
|
|
1356
|
-
rendered = 0;
|
|
1357
|
-
};
|
|
1358
|
-
|
|
1359
|
-
const buildLines = () => {
|
|
1360
|
-
const out = [];
|
|
1361
|
-
out.push(``);
|
|
1362
|
-
out.push(` ${BOLD}${label}${RESET}`);
|
|
1363
|
-
out.push(` ${DIM}────────────────────────────────────────${RESET}`);
|
|
1364
|
-
if (items.length === 0) {
|
|
1365
|
-
out.push(` ${DIM}(none)${RESET}`);
|
|
1366
|
-
} else {
|
|
1367
|
-
items.forEach((it, i) => {
|
|
1368
|
-
const sel = i === cursor;
|
|
1369
|
-
out.push(` ${sel ? CYAN + "❯" + RESET : " "} ${sel ? BOLD + it + RESET : it}`);
|
|
1370
|
-
});
|
|
1371
|
-
}
|
|
1372
|
-
out.push(``);
|
|
1373
|
-
if (notice) out.push(` ${notice}`);
|
|
1374
|
-
if (mode === "add") {
|
|
1375
|
-
out.push(` ${CYAN}add ${noun}:${RESET} ${input}${CYAN}▌${RESET} ${DIM}enter save · esc cancel${RESET}`);
|
|
1376
|
-
} else if (mode === "confirm") {
|
|
1377
|
-
out.push(` ${YELLOW}remove ${items[cursor]}?${RESET} ${DIM}y / N${RESET}`);
|
|
1378
|
-
} else {
|
|
1379
|
-
out.push(` ${DIM}↑↓ nav · ${RESET}${BOLD}a${RESET}${DIM} add · ${RESET}${BOLD}d${RESET}${DIM} delete · esc back${RESET}`);
|
|
1380
|
-
}
|
|
1381
|
-
return out;
|
|
1382
|
-
};
|
|
1383
|
-
|
|
1384
|
-
const render = () => {
|
|
1385
|
-
erase();
|
|
1386
|
-
const lines = buildLines();
|
|
1387
|
-
rendered = lines.length;
|
|
1388
|
-
process.stdout.write(lines.map((l) => l + ANSI.clearLine).join("\n") + "\n");
|
|
1389
|
-
};
|
|
1390
|
-
|
|
1391
|
-
const cleanup = () => {
|
|
1392
|
-
if (done) return;
|
|
1393
|
-
done = true;
|
|
1394
|
-
process.stdin.removeListener("keypress", onKey);
|
|
1395
|
-
process.stdout.write(ANSI.mouseOff);
|
|
1396
|
-
if (process.stdin.setRawMode) process.stdin.setRawMode(previousRaw);
|
|
1397
|
-
if (!previousRaw) process.stdin.pause();
|
|
1398
|
-
erase();
|
|
1399
|
-
};
|
|
1400
|
-
|
|
1401
|
-
// Run a verb (spawnSync blocks briefly — people ops hit GitHub). Surface the
|
|
1402
|
-
// last stdout/stderr line as a notice; refresh the list from disk after.
|
|
1403
|
-
const runVerb = (args) => {
|
|
1404
|
-
const r = runSettings(inst, args);
|
|
1405
|
-
const msg = ((r.stdout || "") + " " + (r.stderr || "")).trim().split("\n").map((s) => s.trim()).filter(Boolean).pop() || "";
|
|
1406
|
-
notice = r && r.status === 0 ? msg : `${YELLOW}${msg}${RESET}`;
|
|
1407
|
-
refresh();
|
|
1408
|
-
};
|
|
1409
|
-
|
|
1410
|
-
function onKey(chunk, key = {}) {
|
|
1411
|
-
if (isMouseEvent(chunk, key)) return;
|
|
1412
|
-
if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
|
|
1413
|
-
|
|
1414
|
-
if (mode === "add") {
|
|
1415
|
-
if (key.name === "escape") { mode = "browse"; input = ""; render(); return; }
|
|
1416
|
-
if (key.name === "return") {
|
|
1417
|
-
const v = input.trim().split(/\s+/)[0];
|
|
1418
|
-
mode = "browse"; input = "";
|
|
1419
|
-
if (v) runVerb([domain, "add", v]);
|
|
1420
|
-
render();
|
|
1421
|
-
return;
|
|
1422
|
-
}
|
|
1423
|
-
if (key.name === "backspace") { input = input.slice(0, -1); render(); return; }
|
|
1424
|
-
if (chunk && chunk.length === 1 && chunk >= " " && !key.ctrl && !key.meta) { input += chunk; render(); return; }
|
|
1425
|
-
return;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
if (mode === "confirm") {
|
|
1429
|
-
if (chunk === "y" || chunk === "Y") {
|
|
1430
|
-
const victim = items[cursor];
|
|
1431
|
-
mode = "browse";
|
|
1432
|
-
if (victim) runVerb([domain, "remove", victim]);
|
|
1433
|
-
} else { mode = "browse"; }
|
|
1434
|
-
render();
|
|
1435
|
-
return;
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
// browse
|
|
1439
|
-
if (key.name === "escape" || key.name === "left") { cleanup(); resolve(); return; }
|
|
1440
|
-
if (key.name === "up" || key.name === "k") { if (items.length) cursor = (cursor - 1 + items.length) % items.length; render(); return; }
|
|
1441
|
-
if (key.name === "down" || key.name === "j") { if (items.length) cursor = (cursor + 1) % items.length; render(); return; }
|
|
1442
|
-
if (chunk === "a") { mode = "add"; input = ""; notice = ""; render(); return; }
|
|
1443
|
-
if (chunk === "d") { if (items.length) { mode = "confirm"; render(); } return; }
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
readline.emitKeypressEvents(process.stdin);
|
|
1447
|
-
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
1448
|
-
process.stdin.resume();
|
|
1449
|
-
process.stdout.write(ANSI.mouseOff);
|
|
1450
|
-
process.stdin.on("keypress", onKey);
|
|
1451
|
-
render();
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
// The `s`-key settings screen: a raw-mode menu (hosting toggle + repo/admin
|
|
1456
|
-
// editors) over bin/settings.sh. Modeled on pickRuntimeInteractive.
|
|
1457
|
-
function settingsScreen(inst) {
|
|
1458
|
-
const title = inst.slug || inst.name || path.basename(inst.path);
|
|
1459
|
-
|
|
1460
|
-
if (!fs.existsSync(settingsScriptPath(inst))) {
|
|
1461
|
-
console.log(`\n ${YELLOW}Settings unavailable${RESET} — ${BOLD}${title}${RESET} has no ${DIM}bin/settings.sh${RESET}.`);
|
|
1462
|
-
console.log(` Update it: open the egregore and run ${CYAN}/pull${RESET}.\n`);
|
|
1463
|
-
return Promise.resolve();
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
let data = settingsDump(inst) || { hosting: true, repos: [], admins: [], people: [] };
|
|
1467
|
-
|
|
1468
|
-
const rows = [
|
|
1469
|
-
{
|
|
1470
|
-
label: () => `Hosted Artifacts`,
|
|
1471
|
-
value: () => (data.hosting ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}`),
|
|
1472
|
-
detailLines: () => [data.hosting
|
|
1473
|
-
? ` ${DIM}Enter toggles. When OFF, artifacts never upload to egregore.xyz.${RESET}`
|
|
1474
|
-
: ` ${DIM}Artifacts stay local. /view still renders them on localhost.${RESET}`],
|
|
1475
|
-
},
|
|
1476
|
-
{
|
|
1477
|
-
label: () => `Managed repos`,
|
|
1478
|
-
value: () => `${DIM}${data.repos.length}${RESET}`,
|
|
1479
|
-
detailLines: () => data.repos.length
|
|
1480
|
-
? data.repos.map((r) => ` ${DIM}·${RESET} ${r.name || r}`)
|
|
1481
|
-
: [` ${DIM}(none)${RESET}`],
|
|
1482
|
-
},
|
|
1483
|
-
{
|
|
1484
|
-
label: () => `Admins`,
|
|
1485
|
-
value: () => `${DIM}${data.admins.length}${RESET}`,
|
|
1486
|
-
detailLines: () => data.admins.length
|
|
1487
|
-
? [` ${DIM}${data.admins.join(", ")}${RESET}`]
|
|
1488
|
-
: [` ${DIM}(none)${RESET}`],
|
|
1489
|
-
},
|
|
1490
|
-
{
|
|
1491
|
-
label: () => `People`,
|
|
1492
|
-
value: () => `${DIM}${(data.people || []).length}${RESET}`,
|
|
1493
|
-
detailLines: () => (data.people && data.people.length)
|
|
1494
|
-
? [` ${DIM}${data.people.join(", ")}${RESET}`]
|
|
1495
|
-
: [` ${DIM}(none)${RESET}`],
|
|
1496
|
-
},
|
|
1497
|
-
];
|
|
1498
|
-
|
|
1499
|
-
return new Promise((resolve) => {
|
|
1500
|
-
let cursor = 0;
|
|
1501
|
-
let rendered = 0;
|
|
1502
|
-
let done = false;
|
|
1503
|
-
let notice = "";
|
|
1504
|
-
const previousRaw = !!process.stdin.isRaw;
|
|
1505
|
-
const isMouseEvent = makeMouseFilter();
|
|
1506
|
-
|
|
1507
|
-
const erase = () => {
|
|
1508
|
-
if (!rendered) return;
|
|
1509
|
-
// Move to the TOP of the last-rendered block, then wipe everything below
|
|
1510
|
-
// in one shot. Clearing line-by-line leaves the cursor at the BOTTOM, so
|
|
1511
|
-
// the next redraw starts a row lower each time — that's the drift that
|
|
1512
|
-
// pushes blank "margins" on top. Up-N + clearDown returns to the top and
|
|
1513
|
-
// clears cleanly, so every render repaints in place.
|
|
1514
|
-
process.stdout.write(`\x1b[${rendered}A` + ANSI.clearDown);
|
|
1515
|
-
rendered = 0;
|
|
1516
|
-
};
|
|
1517
|
-
|
|
1518
|
-
const buildLines = () => {
|
|
1519
|
-
const out = [];
|
|
1520
|
-
out.push(``);
|
|
1521
|
-
out.push(` ${BOLD}Settings${RESET} ${DIM}·${RESET} ${BOLD}${title}${RESET}`);
|
|
1522
|
-
out.push(` ${DIM}────────────────────────────────────────${RESET}`);
|
|
1523
|
-
rows.forEach((row, i) => {
|
|
1524
|
-
const sel = i === cursor;
|
|
1525
|
-
const marker = sel ? `${CYAN}❯${RESET}` : ` `;
|
|
1526
|
-
const name = sel ? `${BOLD}${row.label()}${RESET}` : row.label();
|
|
1527
|
-
out.push(` ${marker} ${name} ${row.value()}`);
|
|
1528
|
-
if (sel) for (const dl of row.detailLines()) out.push(dl);
|
|
1529
|
-
});
|
|
1530
|
-
out.push(``);
|
|
1531
|
-
if (notice) out.push(` ${notice}`);
|
|
1532
|
-
out.push(` ${DIM}↑↓ nav · enter ${cursor === 0 ? "toggle" : "edit"} · esc back · q quit${RESET}`);
|
|
1533
|
-
return out;
|
|
1534
|
-
};
|
|
1535
|
-
|
|
1536
|
-
const render = () => {
|
|
1537
|
-
erase();
|
|
1538
|
-
const lines = buildLines();
|
|
1539
|
-
rendered = lines.length;
|
|
1540
|
-
process.stdout.write(lines.map((l) => l + ANSI.clearLine).join("\n") + "\n");
|
|
1541
|
-
};
|
|
1542
|
-
|
|
1543
|
-
const teardownRaw = () => {
|
|
1544
|
-
process.stdin.removeListener("keypress", onKey);
|
|
1545
|
-
process.stdout.write(ANSI.mouseOff);
|
|
1546
|
-
if (process.stdin.setRawMode) process.stdin.setRawMode(previousRaw);
|
|
1547
|
-
if (!previousRaw) process.stdin.pause();
|
|
1548
|
-
};
|
|
1549
|
-
|
|
1550
|
-
const setupRaw = () => {
|
|
1551
|
-
readline.emitKeypressEvents(process.stdin);
|
|
1552
|
-
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
1553
|
-
process.stdin.resume();
|
|
1554
|
-
process.stdout.write(ANSI.mouseOff);
|
|
1555
|
-
process.stdin.on("keypress", onKey);
|
|
1556
|
-
};
|
|
1557
|
-
|
|
1558
|
-
const cleanup = () => {
|
|
1559
|
-
if (done) return;
|
|
1560
|
-
done = true;
|
|
1561
|
-
teardownRaw();
|
|
1562
|
-
erase();
|
|
1563
|
-
};
|
|
1564
|
-
|
|
1565
|
-
const toggleHosting = () => {
|
|
1566
|
-
const r = runSettings(inst, ["hosting", data.hosting ? "off" : "on"]);
|
|
1567
|
-
if (!r || r.status !== 0) {
|
|
1568
|
-
notice = `${YELLOW}${((r && r.stderr) || "could not change hosting").trim().split("\n")[0]}${RESET}`;
|
|
1569
|
-
} else {
|
|
1570
|
-
data = settingsDump(inst) || data;
|
|
1571
|
-
notice = `hosting ${data.hosting ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}`}`;
|
|
1572
|
-
}
|
|
1573
|
-
render();
|
|
1574
|
-
};
|
|
1575
|
-
|
|
1576
|
-
// Hand off to the nested raw-mode list editor, then repaint this screen with
|
|
1577
|
-
// refreshed counts when the user backs out.
|
|
1578
|
-
const openList = (domain, label) => {
|
|
1579
|
-
erase();
|
|
1580
|
-
teardownRaw();
|
|
1581
|
-
listEditor(inst, domain, label).then(() => {
|
|
1582
|
-
data = settingsDump(inst) || data;
|
|
1583
|
-
notice = "";
|
|
1584
|
-
setupRaw();
|
|
1585
|
-
render();
|
|
1586
|
-
});
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
|
-
function onKey(chunk, key = {}) {
|
|
1590
|
-
if (isMouseEvent(chunk, key)) return;
|
|
1591
|
-
if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
|
|
1592
|
-
if (key.name === "q") { cleanup(); resolve(); return; }
|
|
1593
|
-
// Esc / ← = back (esc never quits).
|
|
1594
|
-
if (key.name === "escape" || key.name === "left") { cleanup(); resolve(); return; }
|
|
1595
|
-
if (key.name === "up" || key.name === "k") { cursor = (cursor - 1 + rows.length) % rows.length; render(); return; }
|
|
1596
|
-
if (key.name === "down" || key.name === "j") { cursor = (cursor + 1) % rows.length; render(); return; }
|
|
1597
|
-
if (key.name === "return" || chunk === " ") {
|
|
1598
|
-
if (cursor === 0) toggleHosting();
|
|
1599
|
-
else if (cursor === 1) openList("repo", "Managed repos");
|
|
1600
|
-
else if (cursor === 2) openList("admin", "Admins");
|
|
1601
|
-
else if (cursor === 3) openList("people", "People");
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
setupRaw();
|
|
1606
|
-
render();
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
1324
|
async function cmdSettings(args) {
|
|
1611
1325
|
const registry = readRegistry();
|
|
1612
1326
|
if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
|
|
@@ -1618,11 +1332,9 @@ async function cmdSettings(args) {
|
|
|
1618
1332
|
if (!inst) {
|
|
1619
1333
|
die(`multiple egregores installed; choose one:\n egregore settings <slug> [hosting|repo|admin]\n run: egregore ls`);
|
|
1620
1334
|
}
|
|
1621
|
-
//
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
rest = ["dump"];
|
|
1625
|
-
}
|
|
1335
|
+
// Interactive editing lives in the launcher (press `s`). With no verb, print
|
|
1336
|
+
// the snapshot; otherwise forward straight to the deterministic verb.
|
|
1337
|
+
if (rest.length === 0) rest = ["dump"];
|
|
1626
1338
|
const r = runSettings(inst, rest);
|
|
1627
1339
|
if (r.stdout) process.stdout.write(r.stdout.endsWith("\n") ? r.stdout : r.stdout + "\n");
|
|
1628
1340
|
if (r.stderr) process.stderr.write(r.stderr);
|
|
@@ -1849,6 +1561,78 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
|
|
|
1849
1561
|
return lines;
|
|
1850
1562
|
}
|
|
1851
1563
|
|
|
1564
|
+
// ── settings panel (rendered in-place inside the browser) ───────────
|
|
1565
|
+
|
|
1566
|
+
function settingsItemsFor(data, domain) {
|
|
1567
|
+
if (!data) return [];
|
|
1568
|
+
if (domain === "repo") return (data.repos || []).map((r) => r.name || r);
|
|
1569
|
+
if (domain === "admin") return data.admins || [];
|
|
1570
|
+
return data.people || [];
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
const SETTINGS_DOMAIN_LABEL = { repo: "Managed repos", admin: "Admins", people: "People" };
|
|
1574
|
+
const SETTINGS_DOMAIN_NOUN = { repo: "repo name", admin: "github handle", people: "github handle" };
|
|
1575
|
+
|
|
1576
|
+
// Same box geometry as renderRuntimePanel so pressing `s` keeps the frame put.
|
|
1577
|
+
function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCursor, mode, input, notice, width }) {
|
|
1578
|
+
const title = inst.slug || inst.name || path.basename(inst.path);
|
|
1579
|
+
const outerWidth = Math.min(Math.max(50, width - 2), 126);
|
|
1580
|
+
const innerWidth = Math.max(48, outerWidth - 2);
|
|
1581
|
+
const margin = " ".repeat(Math.max(0, Math.floor((width - outerWidth) / 2)));
|
|
1582
|
+
const label = " settings ";
|
|
1583
|
+
const d = data || {};
|
|
1584
|
+
const lines = [
|
|
1585
|
+
`${margin}${DIM}┌${"─".repeat(2)}${label}${"─".repeat(Math.max(0, innerWidth - label.length - 2))}┐${RESET}`,
|
|
1586
|
+
emptyBoxLine(innerWidth, margin),
|
|
1587
|
+
boxLine(`${BOLD}${CYAN}${title}${RESET}`, innerWidth, margin),
|
|
1588
|
+
boxLine(`${DIM}${inst.path}${RESET}`, innerWidth, margin),
|
|
1589
|
+
emptyBoxLine(innerWidth, margin),
|
|
1590
|
+
];
|
|
1591
|
+
|
|
1592
|
+
if (view === "list") {
|
|
1593
|
+
lines.push(boxLine(`${BOLD}${SETTINGS_DOMAIN_LABEL[domain] || domain}${RESET}`, innerWidth, margin));
|
|
1594
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1595
|
+
if (!items || !items.length) {
|
|
1596
|
+
lines.push(boxLine(`${DIM}(none)${RESET}`, innerWidth, margin));
|
|
1597
|
+
} else {
|
|
1598
|
+
items.forEach((it, i) => {
|
|
1599
|
+
const sel = i === listCursor;
|
|
1600
|
+
const marker = sel ? `${CYAN}❯${RESET}` : " ";
|
|
1601
|
+
lines.push(boxLine(`${marker} ${sel ? `${BOLD}${it}${RESET}` : it}`, innerWidth, margin));
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1605
|
+
if (notice) lines.push(boxLine(notice, innerWidth, margin));
|
|
1606
|
+
if (mode === "add") {
|
|
1607
|
+
lines.push(boxLine(`${CYAN}add ${SETTINGS_DOMAIN_NOUN[domain]}:${RESET} ${input}${CYAN}▌${RESET} ${DIM}enter save · esc cancel${RESET}`, innerWidth, margin));
|
|
1608
|
+
} else if (mode === "confirm") {
|
|
1609
|
+
lines.push(boxLine(`${YELLOW}remove ${items[listCursor]}?${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
|
|
1610
|
+
} else {
|
|
1611
|
+
lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}a${RESET}${DIM} add · ${RESET}${BOLD}d${RESET}${DIM} delete · ← back · q quit${RESET}`, innerWidth, margin));
|
|
1612
|
+
}
|
|
1613
|
+
} else {
|
|
1614
|
+
const rows = [
|
|
1615
|
+
{ name: "Hosted Artifacts", value: (d.hosting !== false) ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}` },
|
|
1616
|
+
{ name: "Managed repos", value: `${DIM}${(d.repos || []).length}${RESET}` },
|
|
1617
|
+
{ name: "Admins", value: `${DIM}${(d.admins || []).length}${RESET}` },
|
|
1618
|
+
{ name: "People", value: `${DIM}${(d.people || []).length}${RESET}` },
|
|
1619
|
+
];
|
|
1620
|
+
rows.forEach((row, i) => {
|
|
1621
|
+
const sel = i === cursor;
|
|
1622
|
+
const marker = sel ? `${CYAN}❯${RESET}` : " ";
|
|
1623
|
+
const name = sel ? `${BOLD}${row.name}${RESET}` : row.name;
|
|
1624
|
+
lines.push(boxLine(`${marker} ${pad(name, 20)} ${row.value}`, innerWidth, margin));
|
|
1625
|
+
});
|
|
1626
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1627
|
+
if (notice) lines.push(boxLine(notice, innerWidth, margin));
|
|
1628
|
+
lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}enter${RESET}${DIM} ${cursor === 0 ? "toggle" : "open"} · ← back · q quit${RESET}`, innerWidth, margin));
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1632
|
+
lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
|
|
1633
|
+
return lines;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1852
1636
|
// ── browser (full-screen TUI) ───────────────────────────────────────
|
|
1853
1637
|
|
|
1854
1638
|
async function browser(args = {}) {
|
|
@@ -1881,6 +1665,18 @@ async function browser(args = {}) {
|
|
|
1881
1665
|
inviteInst: null,
|
|
1882
1666
|
runtimeInst: null,
|
|
1883
1667
|
runtimeCursor: 0,
|
|
1668
|
+
// Settings panel — rendered in-place like the harness picker (renderSettingsPanel),
|
|
1669
|
+
// never a separate screen. Active while settingsInst is set.
|
|
1670
|
+
settingsInst: null,
|
|
1671
|
+
settingsData: null,
|
|
1672
|
+
settingsCursor: 0,
|
|
1673
|
+
settingsView: "menu", // "menu" | "list"
|
|
1674
|
+
settingsDomain: null, // "repo" | "admin" | "people"
|
|
1675
|
+
settingsItems: [],
|
|
1676
|
+
settingsListCursor: 0,
|
|
1677
|
+
settingsMode: "browse", // "browse" | "add" | "confirm"
|
|
1678
|
+
settingsInput: "",
|
|
1679
|
+
settingsNotice: "",
|
|
1884
1680
|
cursor: initialCursorForCwd(registry),
|
|
1885
1681
|
showHelp: false,
|
|
1886
1682
|
modal: null, // { type: "remove" | "sweep", onYes, onNo }
|
|
@@ -1953,6 +1749,27 @@ async function browser(args = {}) {
|
|
|
1953
1749
|
return;
|
|
1954
1750
|
}
|
|
1955
1751
|
|
|
1752
|
+
if (state.settingsInst) {
|
|
1753
|
+
const settingsLines = renderSettingsPanel({
|
|
1754
|
+
inst: state.settingsInst,
|
|
1755
|
+
data: state.settingsData,
|
|
1756
|
+
cursor: state.settingsCursor,
|
|
1757
|
+
view: state.settingsView,
|
|
1758
|
+
domain: state.settingsDomain,
|
|
1759
|
+
items: state.settingsItems,
|
|
1760
|
+
listCursor: state.settingsListCursor,
|
|
1761
|
+
mode: state.settingsMode,
|
|
1762
|
+
input: state.settingsInput,
|
|
1763
|
+
notice: state.settingsNotice,
|
|
1764
|
+
width: w,
|
|
1765
|
+
});
|
|
1766
|
+
for (const line of settingsLines) out.push(line + ANSI.clearLine + "\n");
|
|
1767
|
+
const remainingRows = Math.max(0, h - settingsLines.length - 1);
|
|
1768
|
+
for (let i = 0; i < remainingRows; i++) out.push(ANSI.clearLine + "\n");
|
|
1769
|
+
process.stdout.write(out.join(""));
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1956
1773
|
// Header / hero panel
|
|
1957
1774
|
const def = readDefault();
|
|
1958
1775
|
const v = visible();
|
|
@@ -2206,10 +2023,78 @@ async function browser(args = {}) {
|
|
|
2206
2023
|
};
|
|
2207
2024
|
|
|
2208
2025
|
return new Promise((resolve) => {
|
|
2026
|
+
// Run a deterministic settings verb, refresh the panel's data + list in place.
|
|
2027
|
+
const settingsRunVerb = (verbArgs) => {
|
|
2028
|
+
const r = runSettings(state.settingsInst, verbArgs);
|
|
2029
|
+
const msg = ((r.stdout || "") + " " + (r.stderr || "")).trim().split("\n").map((s) => s.trim()).filter(Boolean).pop() || "";
|
|
2030
|
+
state.settingsNotice = r && r.status === 0 ? msg : `${YELLOW}${msg}${RESET}`;
|
|
2031
|
+
state.settingsData = settingsDump(state.settingsInst) || state.settingsData;
|
|
2032
|
+
if (state.settingsView === "list" && state.settingsDomain) {
|
|
2033
|
+
state.settingsItems = settingsItemsFor(state.settingsData, state.settingsDomain);
|
|
2034
|
+
if (state.settingsListCursor >= state.settingsItems.length) state.settingsListCursor = Math.max(0, state.settingsItems.length - 1);
|
|
2035
|
+
}
|
|
2036
|
+
};
|
|
2037
|
+
|
|
2209
2038
|
process.stdin.on("keypress", (chunk, key) => {
|
|
2210
2039
|
if (!key) return;
|
|
2211
2040
|
if (isMouseEvent(chunk, key)) return; // drop scroll-wheel / mouse escape sequences
|
|
2212
2041
|
|
|
2042
|
+
if (state.settingsInst) {
|
|
2043
|
+
const S = state;
|
|
2044
|
+
if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
|
|
2045
|
+
if (key.name === "q") { cleanup(); resolve(); return; }
|
|
2046
|
+
|
|
2047
|
+
if (S.settingsView === "list") {
|
|
2048
|
+
if (S.settingsMode === "add") {
|
|
2049
|
+
if (key.name === "escape") { S.settingsMode = "browse"; S.settingsInput = ""; render(); return; }
|
|
2050
|
+
if (key.name === "return") {
|
|
2051
|
+
const v = S.settingsInput.trim().split(/\s+/)[0];
|
|
2052
|
+
S.settingsMode = "browse"; S.settingsInput = "";
|
|
2053
|
+
if (v) settingsRunVerb([S.settingsDomain, "add", v]);
|
|
2054
|
+
render(); return;
|
|
2055
|
+
}
|
|
2056
|
+
if (key.name === "backspace") { S.settingsInput = S.settingsInput.slice(0, -1); render(); return; }
|
|
2057
|
+
if (chunk && chunk.length === 1 && chunk >= " " && !key.ctrl && !key.meta) { S.settingsInput += chunk; render(); return; }
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
if (S.settingsMode === "confirm") {
|
|
2061
|
+
if (chunk === "y" || chunk === "Y") {
|
|
2062
|
+
const victim = S.settingsItems[S.settingsListCursor];
|
|
2063
|
+
S.settingsMode = "browse";
|
|
2064
|
+
if (victim) settingsRunVerb([S.settingsDomain, "remove", victim]);
|
|
2065
|
+
} else { S.settingsMode = "browse"; }
|
|
2066
|
+
render(); return;
|
|
2067
|
+
}
|
|
2068
|
+
// list browse
|
|
2069
|
+
if (key.name === "escape" || key.name === "left") { S.settingsView = "menu"; S.settingsNotice = ""; render(); return; }
|
|
2070
|
+
if ((key.name === "up" || key.name === "k") && S.settingsItems.length) { S.settingsListCursor = (S.settingsListCursor - 1 + S.settingsItems.length) % S.settingsItems.length; render(); return; }
|
|
2071
|
+
if ((key.name === "down" || key.name === "j") && S.settingsItems.length) { S.settingsListCursor = (S.settingsListCursor + 1) % S.settingsItems.length; render(); return; }
|
|
2072
|
+
if (chunk === "a") { S.settingsMode = "add"; S.settingsInput = ""; S.settingsNotice = ""; render(); return; }
|
|
2073
|
+
if (chunk === "d" && S.settingsItems.length) { S.settingsMode = "confirm"; render(); return; }
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// menu view
|
|
2078
|
+
if (key.name === "escape" || key.name === "left") { S.settingsInst = null; S.settingsNotice = ""; render(); return; }
|
|
2079
|
+
if (key.name === "up" || key.name === "k") { S.settingsCursor = (S.settingsCursor - 1 + 4) % 4; render(); return; }
|
|
2080
|
+
if (key.name === "down" || key.name === "j") { S.settingsCursor = (S.settingsCursor + 1) % 4; render(); return; }
|
|
2081
|
+
if (key.name === "return" || chunk === " ") {
|
|
2082
|
+
if (S.settingsCursor === 0) {
|
|
2083
|
+
const on = (S.settingsData && S.settingsData.hosting) !== false;
|
|
2084
|
+
settingsRunVerb(["hosting", on ? "off" : "on"]);
|
|
2085
|
+
render();
|
|
2086
|
+
} else {
|
|
2087
|
+
const domain = S.settingsCursor === 1 ? "repo" : S.settingsCursor === 2 ? "admin" : "people";
|
|
2088
|
+
S.settingsView = "list"; S.settingsDomain = domain;
|
|
2089
|
+
S.settingsItems = settingsItemsFor(S.settingsData, domain);
|
|
2090
|
+
S.settingsListCursor = 0; S.settingsMode = "browse"; S.settingsNotice = "";
|
|
2091
|
+
render();
|
|
2092
|
+
}
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2213
2098
|
if (state.runtimeInst) {
|
|
2214
2099
|
const runtimes = runtimeChoicesFor(state.runtimeInst, args.runtimeOverride);
|
|
2215
2100
|
if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
|
|
@@ -2437,8 +2322,16 @@ async function browser(args = {}) {
|
|
|
2437
2322
|
if (chunk === "s") {
|
|
2438
2323
|
const pick = visible()[state.cursor]?.e;
|
|
2439
2324
|
if (!pick) return;
|
|
2440
|
-
|
|
2441
|
-
|
|
2325
|
+
if (!fs.existsSync(settingsScriptPath(pick))) {
|
|
2326
|
+
flashMessage(`${YELLOW}settings need bin/settings.sh — run /pull in this egregore${RESET}`, "warn");
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
state.settingsInst = pick;
|
|
2330
|
+
state.settingsData = settingsDump(pick) || { hosting: true, repos: [], admins: [], people: [] };
|
|
2331
|
+
state.settingsCursor = 0;
|
|
2332
|
+
state.settingsView = "menu";
|
|
2333
|
+
state.settingsNotice = "";
|
|
2334
|
+
render();
|
|
2442
2335
|
return;
|
|
2443
2336
|
}
|
|
2444
2337
|
if (chunk === "u") {
|
package/lib/local.js
CHANGED
|
@@ -47,7 +47,12 @@ const TEMPLATE_REPO = "egregore";
|
|
|
47
47
|
async function getUser(token) {
|
|
48
48
|
const { status, data } = await ghApi("GET", "/user", token);
|
|
49
49
|
if (status !== 200) throw new Error("GitHub auth failed — could not fetch user");
|
|
50
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
login: data.login,
|
|
52
|
+
id: data.id || null,
|
|
53
|
+
name: data.name || data.login,
|
|
54
|
+
email: data.email || null,
|
|
55
|
+
};
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
async function listOrgs(token) {
|
|
@@ -1286,11 +1291,14 @@ async function localFounderFlow(ui, ctx = {}) {
|
|
|
1286
1291
|
const slugifiedName = (displayName || user.login).toLowerCase().replace(/\s+/g, "-");
|
|
1287
1292
|
const state = {
|
|
1288
1293
|
github_username: user.login,
|
|
1294
|
+
github_id: user.id,
|
|
1295
|
+
person_id: user.id ? `github:${user.id}` : `github-login:${user.login.toLowerCase()}`,
|
|
1289
1296
|
github_name: user.name,
|
|
1290
1297
|
display_name: displayName || user.login,
|
|
1291
1298
|
name: slugifiedName,
|
|
1292
1299
|
member_role: memberRole,
|
|
1293
1300
|
email: user.email,
|
|
1301
|
+
emails: user.email ? [user.email.toLowerCase()] : [],
|
|
1294
1302
|
onboarding_complete: false,
|
|
1295
1303
|
usage_type: "founder_group",
|
|
1296
1304
|
org_setup: true,
|
|
@@ -1430,6 +1438,7 @@ async function localFounderFlow(ui, ctx = {}) {
|
|
|
1430
1438
|
const invPersonContent = [
|
|
1431
1439
|
"---",
|
|
1432
1440
|
`name: ${invUsername}`,
|
|
1441
|
+
`person_id: github-login:${invUsername.toLowerCase()}`,
|
|
1433
1442
|
`github: ${invUsername}`,
|
|
1434
1443
|
`invited_by: ${user.login}`,
|
|
1435
1444
|
`joined: ${today}`,
|
|
@@ -1755,6 +1764,7 @@ async function localJoinFlow(orgArg, ui, ctx = {}) {
|
|
|
1755
1764
|
org_name: orgName,
|
|
1756
1765
|
github_org: orgLogin,
|
|
1757
1766
|
github_username: user.login,
|
|
1767
|
+
github_id: user.id,
|
|
1758
1768
|
github_name: user.name,
|
|
1759
1769
|
github_email: user.email,
|
|
1760
1770
|
slug: config.slug || orgLogin,
|
package/lib/setup.js
CHANGED
|
@@ -174,12 +174,24 @@ function mergeProfileFields(existing, additions = []) {
|
|
|
174
174
|
function personProfileContent(state = {}, opts = {}) {
|
|
175
175
|
const now = opts.onboardedAt || new Date().toISOString();
|
|
176
176
|
const username = state.github_username || opts.githubUsername || "unknown";
|
|
177
|
+
const githubId = state.github_id || opts.githubId || "";
|
|
178
|
+
const personId = state.person_id || (githubId ? `github:${githubId}` : `github-login:${username.toLowerCase()}`);
|
|
177
179
|
const displayName = state.display_name || state.github_name || username;
|
|
178
180
|
const role = state.member_role || state.onboarding?.member_role || opts.memberRole || "Member";
|
|
181
|
+
const emails = [...new Set(
|
|
182
|
+
[state.email, ...(Array.isArray(state.emails) ? state.emails : [])]
|
|
183
|
+
.filter(Boolean)
|
|
184
|
+
.map((value) => String(value).trim().toLowerCase()),
|
|
185
|
+
)];
|
|
179
186
|
return [
|
|
180
187
|
`# ${displayName}`,
|
|
181
188
|
"",
|
|
189
|
+
`Person-ID: ${personId}`,
|
|
182
190
|
`GitHub: ${username}`,
|
|
191
|
+
`GitHub-ID: ${githubId}`,
|
|
192
|
+
"GitHub-Aliases: ",
|
|
193
|
+
`Email: ${emails[0] || ""}`,
|
|
194
|
+
`Emails: ${emails.join(", ")}`,
|
|
183
195
|
`Role: ${role}`,
|
|
184
196
|
`Joined: ${now.split("T")[0]}`,
|
|
185
197
|
`Onboarded: ${now}`,
|
|
@@ -234,6 +246,7 @@ function buildOnboardingState(existing = {}, data = {}, opts = {}) {
|
|
|
234
246
|
const state = { ...existing };
|
|
235
247
|
const preferredRuntime = normalizeRuntime(opts.runtime);
|
|
236
248
|
const githubUsername = data.github_username || state.github_username || "";
|
|
249
|
+
const githubId = data.github_id || state.github_id || null;
|
|
237
250
|
const githubName = data.github_name || state.github_name || githubUsername;
|
|
238
251
|
const displayName = data.display_name || state.display_name || githubName || githubUsername;
|
|
239
252
|
const usageType = normalizeUsageType(data);
|
|
@@ -248,6 +261,12 @@ function buildOnboardingState(existing = {}, data = {}, opts = {}) {
|
|
|
248
261
|
const shouldComplete = alreadyComplete || (requestedComplete && !!memberRole);
|
|
249
262
|
|
|
250
263
|
if (githubUsername) state.github_username = githubUsername;
|
|
264
|
+
if (githubId) {
|
|
265
|
+
state.github_id = githubId;
|
|
266
|
+
state.person_id = `github:${githubId}`;
|
|
267
|
+
} else if (githubUsername && !state.person_id) {
|
|
268
|
+
state.person_id = `github-login:${githubUsername.toLowerCase()}`;
|
|
269
|
+
}
|
|
251
270
|
if (githubName) state.github_name = githubName;
|
|
252
271
|
if (displayName) {
|
|
253
272
|
state.display_name = displayName;
|
|
@@ -457,6 +476,20 @@ async function install(data, ui, targetDir, opts = {}) {
|
|
|
457
476
|
reportPiRuntimeSummary(piRuntime, ui);
|
|
458
477
|
}
|
|
459
478
|
|
|
479
|
+
// Reconcile the installer-captured identity through the same portable writer
|
|
480
|
+
// used by /me and onboarding. Failure is recoverable: the state + person
|
|
481
|
+
// profile already exist and the next session retries `bin/person.sh sync`.
|
|
482
|
+
const personScript = path.join(egregoreDir, "bin", "person.sh");
|
|
483
|
+
if (fs.existsSync(personScript)) {
|
|
484
|
+
try {
|
|
485
|
+
spawnSync("bash", [personScript, "onboard"], {
|
|
486
|
+
cwd: egregoreDir,
|
|
487
|
+
stdio: "ignore",
|
|
488
|
+
timeout: 15000,
|
|
489
|
+
});
|
|
490
|
+
} catch {}
|
|
491
|
+
}
|
|
492
|
+
|
|
460
493
|
// 5. Register instance + install launcher binary
|
|
461
494
|
ui.step(5, totalSteps, "Registering instance...");
|
|
462
495
|
registerInstance(forkDirName, org_name, egregoreDir, preferredRuntime);
|
package/package.json
CHANGED