create-egregore 0.19.12 → 0.19.13
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 +168 -4
- package/bin/cli.js +18 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// egregore-launcher-version: 0.
|
|
2
|
+
// egregore-launcher-version: 0.19.13
|
|
3
3
|
//
|
|
4
4
|
// egregore — launch and browse Egregore instances from your terminal.
|
|
5
5
|
// Installed by create-egregore. Reads ~/.egregore/instances.json.
|
|
@@ -33,10 +33,17 @@ const crypto = require("node:crypto");
|
|
|
33
33
|
const { spawnSync, spawn } = require("node:child_process");
|
|
34
34
|
const readline = require("node:readline");
|
|
35
35
|
|
|
36
|
-
const VERSION = "0.
|
|
36
|
+
const VERSION = "0.19.13";
|
|
37
37
|
const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
|
|
38
38
|
const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
|
|
39
|
+
const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
|
|
39
40
|
const PUBLIC_NPX_PACKAGE = "create-egregore";
|
|
41
|
+
const UPDATE_REGISTRY_URL =
|
|
42
|
+
process.env.EGREGORE_UPDATE_REGISTRY_URL ||
|
|
43
|
+
`https://registry.npmjs.org/${PUBLIC_NPX_PACKAGE}/latest`;
|
|
44
|
+
const UPDATE_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
45
|
+
const UPDATE_FAILURE_TTL_MS = 30 * 60 * 1000;
|
|
46
|
+
const UPDATE_CHECK_TIMEOUT_MS = 1500;
|
|
40
47
|
const CONNECT_API_URL = (
|
|
41
48
|
process.env.EGREGORE_CONNECT_API_URL ||
|
|
42
49
|
"https://egregore-production-55f2.up.railway.app"
|
|
@@ -976,11 +983,145 @@ function requestJson(options, body) {
|
|
|
976
983
|
});
|
|
977
984
|
});
|
|
978
985
|
req.on("error", (err) => resolve({ status: 0, data: { message: err.message } }));
|
|
986
|
+
if (options.unref) {
|
|
987
|
+
req.on("socket", (socket) => socket.unref());
|
|
988
|
+
}
|
|
989
|
+
if (options.timeoutMs) {
|
|
990
|
+
req.setTimeout(options.timeoutMs, () => req.destroy(new Error("request timed out")));
|
|
991
|
+
}
|
|
979
992
|
if (payload) req.write(payload);
|
|
980
993
|
req.end();
|
|
981
994
|
});
|
|
982
995
|
}
|
|
983
996
|
|
|
997
|
+
function parseReleaseVersion(value) {
|
|
998
|
+
const match = String(value || "").trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
|
|
999
|
+
if (!match) return null;
|
|
1000
|
+
return {
|
|
1001
|
+
parts: match.slice(1, 4).map(Number),
|
|
1002
|
+
prerelease: match[4] || "",
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function compareReleaseVersions(a, b) {
|
|
1007
|
+
const left = parseReleaseVersion(a);
|
|
1008
|
+
const right = parseReleaseVersion(b);
|
|
1009
|
+
if (!left || !right) return 0;
|
|
1010
|
+
for (let i = 0; i < 3; i++) {
|
|
1011
|
+
if (left.parts[i] !== right.parts[i]) return left.parts[i] > right.parts[i] ? 1 : -1;
|
|
1012
|
+
}
|
|
1013
|
+
if (left.prerelease === right.prerelease) return 0;
|
|
1014
|
+
if (!left.prerelease) return 1;
|
|
1015
|
+
if (!right.prerelease) return -1;
|
|
1016
|
+
return left.prerelease.localeCompare(right.prerelease);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function cliUpdateState(latest) {
|
|
1020
|
+
if (!parseReleaseVersion(latest)) return null;
|
|
1021
|
+
return {
|
|
1022
|
+
current: VERSION,
|
|
1023
|
+
latest,
|
|
1024
|
+
available: compareReleaseVersions(latest, VERSION) > 0,
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function readUpdateCache() {
|
|
1029
|
+
try {
|
|
1030
|
+
const cache = JSON.parse(fs.readFileSync(UPDATE_CACHE_FILE, "utf-8"));
|
|
1031
|
+
if (!cache || !cache.checked_at) return null;
|
|
1032
|
+
const age = Date.now() - new Date(cache.checked_at).getTime();
|
|
1033
|
+
const ttl = cache.latest ? UPDATE_CACHE_TTL_MS : UPDATE_FAILURE_TTL_MS;
|
|
1034
|
+
if (!Number.isFinite(age) || age < 0 || age > ttl) return null;
|
|
1035
|
+
if (!cache.latest) return { current: VERSION, latest: null, available: false };
|
|
1036
|
+
return cliUpdateState(cache.latest);
|
|
1037
|
+
} catch {
|
|
1038
|
+
return null;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function writeUpdateCache(latest) {
|
|
1043
|
+
try {
|
|
1044
|
+
fs.mkdirSync(path.dirname(UPDATE_CACHE_FILE), { recursive: true });
|
|
1045
|
+
atomicWrite(
|
|
1046
|
+
UPDATE_CACHE_FILE,
|
|
1047
|
+
JSON.stringify({ checked_at: new Date().toISOString(), latest: latest || null }, null, 2) + "\n",
|
|
1048
|
+
0o600,
|
|
1049
|
+
);
|
|
1050
|
+
} catch {}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
async function checkForCliUpdate() {
|
|
1054
|
+
if (process.env.EGREGORE_DISABLE_UPDATE_CHECK === "1") return null;
|
|
1055
|
+
if (process.env.EGREGORE_UPDATE_LATEST_VERSION) {
|
|
1056
|
+
return cliUpdateState(process.env.EGREGORE_UPDATE_LATEST_VERSION);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const cached = readUpdateCache();
|
|
1060
|
+
if (cached) return cached;
|
|
1061
|
+
|
|
1062
|
+
try {
|
|
1063
|
+
const url = new URL(UPDATE_REGISTRY_URL);
|
|
1064
|
+
const result = await requestJson({
|
|
1065
|
+
hostname: url.hostname,
|
|
1066
|
+
port: url.port || undefined,
|
|
1067
|
+
path: `${url.pathname}${url.search}`,
|
|
1068
|
+
method: "GET",
|
|
1069
|
+
protocol: url.protocol,
|
|
1070
|
+
timeoutMs: UPDATE_CHECK_TIMEOUT_MS,
|
|
1071
|
+
unref: true,
|
|
1072
|
+
headers: { Accept: "application/json" },
|
|
1073
|
+
});
|
|
1074
|
+
const latest = result.status === 200 && result.data && result.data.version;
|
|
1075
|
+
const update = cliUpdateState(latest);
|
|
1076
|
+
writeUpdateCache(update?.latest || null);
|
|
1077
|
+
return update;
|
|
1078
|
+
} catch {
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function cmdCliUpdate(latest) {
|
|
1084
|
+
if (!parseReleaseVersion(latest)) {
|
|
1085
|
+
console.error(`\n ${YELLOW}!${RESET} Could not verify the CLI release version.\n`);
|
|
1086
|
+
process.exitCode = 1;
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
console.log("");
|
|
1091
|
+
console.log(` ${BOLD}${CYAN}↑ CLI UPDATE${RESET}`);
|
|
1092
|
+
console.log(` ${DIM}${VERSION} → ${latest}${RESET}`);
|
|
1093
|
+
console.log("");
|
|
1094
|
+
process.stdout.write(` [1/2] Installing ${PUBLIC_NPX_PACKAGE}@${latest}…`);
|
|
1095
|
+
const result = spawnSync(
|
|
1096
|
+
"npx",
|
|
1097
|
+
["-y", `${PUBLIC_NPX_PACKAGE}@${latest}`, "--refresh-launcher"],
|
|
1098
|
+
{ encoding: "utf-8", stdio: ["inherit", "pipe", "pipe"], env: { ...process.env } },
|
|
1099
|
+
);
|
|
1100
|
+
if (result.status !== 0) {
|
|
1101
|
+
console.log(`\r [1/2] ${YELLOW}!${RESET} Install failed`);
|
|
1102
|
+
const detail = String(result.stderr || result.stdout || "").trim().split("\n").filter(Boolean).pop();
|
|
1103
|
+
if (detail) console.error(` ${DIM}${detail}${RESET}`);
|
|
1104
|
+
console.error(` Retry: npx -y ${PUBLIC_NPX_PACKAGE}@${latest} --refresh-launcher\n`);
|
|
1105
|
+
process.exitCode = 1;
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
console.log(`\r [1/2] ${GREEN}✓${RESET} CLI ${latest} installed`);
|
|
1109
|
+
|
|
1110
|
+
if (process.env.EGREGORE_UPDATE_NO_RELAUNCH === "1") {
|
|
1111
|
+
console.log(` [2/2] ${GREEN}✓${RESET} Ready for the next launch\n`);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
console.log(` [2/2] ${GREEN}✓${RESET} Reopening Egregore\n`);
|
|
1116
|
+
const relaunched = spawnSync(process.execPath, [process.argv[1], "browse"], {
|
|
1117
|
+
stdio: "inherit",
|
|
1118
|
+
env: { ...process.env },
|
|
1119
|
+
});
|
|
1120
|
+
if (relaunched.error) {
|
|
1121
|
+
console.error(` ${YELLOW}!${RESET} Updated successfully. Run ${BOLD}egregore${RESET} to reopen.\n`);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
984
1125
|
function githubRequest(token, method, apiPath, body) {
|
|
985
1126
|
return requestJson({
|
|
986
1127
|
hostname: "api.github.com",
|
|
@@ -1689,7 +1830,7 @@ function selectedWorkflowLine(pv, width) {
|
|
|
1689
1830
|
return joinShort(available.map(skillToken), Math.max(12, width));
|
|
1690
1831
|
}
|
|
1691
1832
|
|
|
1692
|
-
function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, filterMode }) {
|
|
1833
|
+
function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, filterMode, cliUpdate }) {
|
|
1693
1834
|
const outerWidth = Math.min(Math.max(50, width - 2), 126);
|
|
1694
1835
|
const innerWidth = Math.max(48, outerWidth - 2);
|
|
1695
1836
|
const bodyWidth = Math.max(8, innerWidth - 2);
|
|
@@ -1741,7 +1882,17 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1741
1882
|
lines.push(mid);
|
|
1742
1883
|
lines.push(boxLine(`${BOLD}${title}${RESET} ${DIM}· ${org} · ${runtime} · ${countSuffix} · ${filterText}${RESET}`, innerWidth, margin));
|
|
1743
1884
|
lines.push(boxLine(`${DIM}skills${RESET} ${skillStatus}`, innerWidth, margin));
|
|
1744
|
-
|
|
1885
|
+
if (cliUpdate?.available) {
|
|
1886
|
+
lines.push(boxLine(
|
|
1887
|
+
`${YELLOW}→ CLI update ${cliUpdate.latest} available${RESET} ${DIM}· installed ${cliUpdate.current} · ${RESET}${BOLD}U${RESET}${DIM} install${RESET}`,
|
|
1888
|
+
innerWidth,
|
|
1889
|
+
margin,
|
|
1890
|
+
));
|
|
1891
|
+
} else {
|
|
1892
|
+
// Reserve the status row so the list never jumps when the async version
|
|
1893
|
+
// check resolves and reveals an available release.
|
|
1894
|
+
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1895
|
+
}
|
|
1745
1896
|
|
|
1746
1897
|
const sigilWidth = Math.max(...EGREGORE_SIGIL.map((line) => line.length));
|
|
1747
1898
|
const twoColumn = bodyWidth >= 68;
|
|
@@ -1938,6 +2089,7 @@ async function browser(args = {}) {
|
|
|
1938
2089
|
message: null, // ephemeral info line (e.g. "default set to X")
|
|
1939
2090
|
messageKind: "info", // "info" | "success" | "warn"
|
|
1940
2091
|
messageUntil: 0,
|
|
2092
|
+
cliUpdate: null,
|
|
1941
2093
|
};
|
|
1942
2094
|
|
|
1943
2095
|
const newAction = {
|
|
@@ -2041,6 +2193,7 @@ async function browser(args = {}) {
|
|
|
2041
2193
|
countSuffix,
|
|
2042
2194
|
filter: state.filter,
|
|
2043
2195
|
filterMode: state.filterMode,
|
|
2196
|
+
cliUpdate: state.cliUpdate,
|
|
2044
2197
|
});
|
|
2045
2198
|
for (const line of heroLines) out.push(line + ANSI.clearLine + "\n");
|
|
2046
2199
|
out.push(ANSI.clearLine + "\n");
|
|
@@ -2571,6 +2724,12 @@ async function browser(args = {}) {
|
|
|
2571
2724
|
resolve(cmdConnect([pick.slug]));
|
|
2572
2725
|
return;
|
|
2573
2726
|
}
|
|
2727
|
+
if (chunk === "U" && state.cliUpdate?.available) {
|
|
2728
|
+
const latest = state.cliUpdate.latest;
|
|
2729
|
+
cleanup();
|
|
2730
|
+
resolve(cmdCliUpdate(latest));
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2574
2733
|
if (chunk === "i") {
|
|
2575
2734
|
const pick = visible()[state.cursor]?.e;
|
|
2576
2735
|
if (!pick) return;
|
|
@@ -2657,6 +2816,11 @@ async function browser(args = {}) {
|
|
|
2657
2816
|
});
|
|
2658
2817
|
|
|
2659
2818
|
render();
|
|
2819
|
+
checkForCliUpdate().then((update) => {
|
|
2820
|
+
if (done || !update) return;
|
|
2821
|
+
state.cliUpdate = update;
|
|
2822
|
+
if (update.available) render();
|
|
2823
|
+
});
|
|
2660
2824
|
});
|
|
2661
2825
|
}
|
|
2662
2826
|
|
package/bin/cli.js
CHANGED
|
@@ -223,6 +223,8 @@ function parseArgs(argv) {
|
|
|
223
223
|
// already served as the entry screen — a second alt-screen welcome
|
|
224
224
|
// would otherwise demand a redundant Enter and churn the alt buffer.
|
|
225
225
|
args.noWelcome = true;
|
|
226
|
+
} else if (argv[i] === "--refresh-launcher") {
|
|
227
|
+
args.refreshLauncher = true;
|
|
226
228
|
} else if (argv[i] === "--codex") {
|
|
227
229
|
args.runtime = "codex";
|
|
228
230
|
} else if (argv[i] === "--pi") {
|
|
@@ -395,6 +397,7 @@ function showHelp() {
|
|
|
395
397
|
ui.info(" --force Skip the in-repo detection check");
|
|
396
398
|
ui.info(" --no-autolaunch Don't launch the harness at the end (for CI)");
|
|
397
399
|
ui.info(" --no-screens Skip the full-screen welcome/ready bookends");
|
|
400
|
+
ui.info(" --refresh-launcher Refresh only the Egregore CLI, then exit");
|
|
398
401
|
ui.info(" -h, --help Show this help");
|
|
399
402
|
ui.info("");
|
|
400
403
|
}
|
|
@@ -443,6 +446,21 @@ async function main() {
|
|
|
443
446
|
}
|
|
444
447
|
}
|
|
445
448
|
|
|
449
|
+
if (args.refreshLauncher) {
|
|
450
|
+
const instances = readLocalInstances();
|
|
451
|
+
if (instances.length === 0) {
|
|
452
|
+
ui.error("No installed Egregore instance was found. Run create-egregore without --refresh-launcher.");
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
const alias = await installLauncherBinary(instances[0].path, ui);
|
|
456
|
+
if (!alias || !alias.binaryPath) {
|
|
457
|
+
ui.error("Could not refresh the Egregore CLI.");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
ui.success(`Egregore CLI is current at ${alias.binaryPath}`);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
446
464
|
// Local-first product surface: if the user already has egregores installed
|
|
447
465
|
// and didn't pass an explicit mode flag, refresh the launcher and open the
|
|
448
466
|
// same TUI as bare `egregore`. With no installed instances, make bare
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-egregore",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.13",
|
|
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": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build:runtime": "node scripts/build-runtime-bundle.mjs && node scripts/build-pi-runtime-bundle.mjs",
|
|
14
14
|
"build:pi-runtime": "node scripts/build-pi-runtime-bundle.mjs",
|
|
15
|
-
"test": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/context.test.js test/parse-join-target.test.js test/skip-repos.test.js test/onboarding-shortcircuit.test.js test/welcome-screen.test.js test/runtime-selection.test.js test/command-generation.test.js test/auth-gh.test.js test/gauntlet-fixes.test.js test/codex-runtime.test.js test/pi-runtime.test.js test/pi-product-workflows.test.js test/installer-runtime-source.test.js test/scroll-repro.test.js test/launcher-refocus-redraw.test.js test/multiselect-remote-search.test.js test/back-navigation.test.js test/manifest-form.test.js test/launcher-delete.test.js test/launcher-connect.test.js",
|
|
15
|
+
"test": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/context.test.js test/parse-join-target.test.js test/skip-repos.test.js test/onboarding-shortcircuit.test.js test/welcome-screen.test.js test/runtime-selection.test.js test/command-generation.test.js test/auth-gh.test.js test/gauntlet-fixes.test.js test/codex-runtime.test.js test/pi-runtime.test.js test/pi-product-workflows.test.js test/installer-runtime-source.test.js test/scroll-repro.test.js test/launcher-refocus-redraw.test.js test/multiselect-remote-search.test.js test/back-navigation.test.js test/manifest-form.test.js test/launcher-delete.test.js test/launcher-connect.test.js test/launcher-update.test.js",
|
|
16
16
|
"test:e2e": "node --test test/e2e-local.test.js",
|
|
17
17
|
"test:all": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/"
|
|
18
18
|
},
|