create-egregore 0.12.0 → 0.12.2
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/lib/auth.js +1 -1
- package/lib/local.js +67 -33
- package/package.json +1 -1
package/lib/auth.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const https = require("node:https");
|
|
7
7
|
|
|
8
8
|
const CLIENT_ID = "Iv23li2obNsAjakoK2RE";
|
|
9
|
-
const SCOPE = "";
|
|
9
|
+
const SCOPE = ""; // GitHub App — permissions managed via installation, not OAuth scope
|
|
10
10
|
|
|
11
11
|
function post(url, body) {
|
|
12
12
|
return new Promise((resolve, reject) => {
|
package/lib/local.js
CHANGED
|
@@ -22,9 +22,6 @@ const {
|
|
|
22
22
|
const TEMPLATE_OWNER = "egregore-labs";
|
|
23
23
|
const TEMPLATE_REPO = "egregore";
|
|
24
24
|
|
|
25
|
-
// GitHub App installation URL
|
|
26
|
-
const APP_INSTALL_URL = "https://github.com/apps/egregore-labs/installations/new";
|
|
27
|
-
|
|
28
25
|
// ── GitHub API helpers ──────────────────────────────────────────────
|
|
29
26
|
|
|
30
27
|
async function getUser(token) {
|
|
@@ -83,6 +80,17 @@ async function waitForRepo(token, owner, repo, timeoutMs = 30000) {
|
|
|
83
80
|
return false;
|
|
84
81
|
}
|
|
85
82
|
|
|
83
|
+
async function findEgregoreRepo(token, orgLogin, explicitRepo) {
|
|
84
|
+
if (explicitRepo) {
|
|
85
|
+
if (await repoExists(token, orgLogin, explicitRepo)) return explicitRepo;
|
|
86
|
+
} else {
|
|
87
|
+
for (const candidate of ["egregore", "egregore-core"]) {
|
|
88
|
+
if (await repoExists(token, orgLogin, candidate)) return candidate;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
86
94
|
async function getFileContent(token, owner, repo, filePath) {
|
|
87
95
|
const { status, data } = await ghApi("GET", `/repos/${owner}/${repo}/contents/${filePath}`, token);
|
|
88
96
|
if (status !== 200 || !data.content) return null;
|
|
@@ -182,15 +190,8 @@ function createSymlink(egregoreDir, memoryDir, ui) {
|
|
|
182
190
|
// ── Flow 1: Founder (local mode) ───────────────────────────────────
|
|
183
191
|
|
|
184
192
|
async function localFounderFlow(ui) {
|
|
185
|
-
// 1.
|
|
186
|
-
ui.info("Let's set up Egregore.\n");
|
|
187
|
-
ui.info("First, install the Egregore app on your GitHub account.");
|
|
188
|
-
ui.info("You'll choose exactly which repos to share.\n");
|
|
189
|
-
openBrowser(APP_INSTALL_URL);
|
|
190
|
-
await ui.prompt("Press Enter when done...");
|
|
191
|
-
|
|
192
|
-
// 2. GitHub auth (auto-approved — user just authorized during App install)
|
|
193
|
-
ui.info("");
|
|
193
|
+
// 1. GitHub auth
|
|
194
|
+
ui.info("Let's set up Egregore. First, sign in with GitHub.\n");
|
|
194
195
|
let githubToken;
|
|
195
196
|
try {
|
|
196
197
|
githubToken = await deviceFlow(ui);
|
|
@@ -571,40 +572,43 @@ async function localJoinFlow(orgArg, ui) {
|
|
|
571
572
|
// 2. Get user info
|
|
572
573
|
const user = await getUser(githubToken);
|
|
573
574
|
|
|
574
|
-
// 3. Accept pending invitations
|
|
575
|
+
// 3. Accept pending invitations (may silently fail with GitHub App tokens)
|
|
575
576
|
console.log("");
|
|
576
577
|
const s1 = ui.spinner("Checking for invitations...");
|
|
577
578
|
await acceptPendingInvitations(githubToken, orgLogin, ui);
|
|
578
579
|
s1.stop("Checked invitations");
|
|
579
580
|
|
|
580
581
|
// 4. Find egregore repo
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
582
|
+
let repoName = await findEgregoreRepo(githubToken, orgLogin, explicitRepo);
|
|
583
|
+
|
|
584
|
+
// 4b. If repo not found, the user likely has a pending invitation that couldn't
|
|
585
|
+
// be accepted automatically. Open the repo page so they can accept with one click.
|
|
586
|
+
if (!repoName && explicitRepo) {
|
|
587
|
+
console.log("");
|
|
588
|
+
ui.info("Accept the repo invitation in your browser.");
|
|
589
|
+
openBrowser(`https://github.com/${orgLogin}/${explicitRepo}`);
|
|
590
|
+
await ui.prompt("Press Enter after accepting...");
|
|
591
|
+
|
|
592
|
+
const s2 = ui.spinner("Retrying...");
|
|
593
|
+
repoName = await findEgregoreRepo(githubToken, orgLogin, explicitRepo);
|
|
594
|
+
if (!repoName) {
|
|
595
|
+
s2.fail(`Still can't access ${orgLogin}/${explicitRepo}`);
|
|
596
|
+
ui.error("Make sure the org admin has set up Egregore and invited you.");
|
|
597
|
+
process.exit(1);
|
|
595
598
|
}
|
|
596
|
-
|
|
597
|
-
if (!repoName) {
|
|
598
|
-
|
|
599
|
+
s2.stop("Access confirmed");
|
|
600
|
+
} else if (!repoName) {
|
|
601
|
+
ui.error(`No egregore repo found in ${orgLogin}`);
|
|
599
602
|
ui.error("Make sure the org admin has set up Egregore and invited you.");
|
|
600
603
|
ui.info("If the repo has a custom name, use: npx create-egregore join <org>/<repo>");
|
|
601
604
|
process.exit(1);
|
|
602
605
|
}
|
|
603
606
|
|
|
604
607
|
// 5. Read egregore.json from remote
|
|
608
|
+
const s3 = ui.spinner(`Reading config from ${orgLogin}/${repoName}...`);
|
|
605
609
|
const configRaw = await getFileContent(githubToken, orgLogin, repoName, "egregore.json");
|
|
606
610
|
if (!configRaw) {
|
|
607
|
-
|
|
611
|
+
s3.fail("Could not read egregore.json");
|
|
608
612
|
ui.error("The egregore repo exists but has no egregore.json. Ask the admin to set up first.");
|
|
609
613
|
process.exit(1);
|
|
610
614
|
}
|
|
@@ -612,10 +616,10 @@ async function localJoinFlow(orgArg, ui) {
|
|
|
612
616
|
try {
|
|
613
617
|
config = JSON.parse(configRaw);
|
|
614
618
|
} catch {
|
|
615
|
-
|
|
619
|
+
s3.fail("Invalid egregore.json");
|
|
616
620
|
process.exit(1);
|
|
617
621
|
}
|
|
618
|
-
|
|
622
|
+
s3.stop(`Found ${ui.bold(config.org_name || orgLogin)}`);
|
|
619
623
|
|
|
620
624
|
const orgName = config.org_name || orgLogin;
|
|
621
625
|
const memoryRepoName = config.memory_repo
|
|
@@ -623,6 +627,36 @@ async function localJoinFlow(orgArg, ui) {
|
|
|
623
627
|
: `${orgLogin}-memory`;
|
|
624
628
|
const managedRepos = config.repos || [];
|
|
625
629
|
|
|
630
|
+
// 5b. Check access to memory repo + managed repos (may need invitation acceptance)
|
|
631
|
+
const additionalRepos = [memoryRepoName, ...managedRepos];
|
|
632
|
+
const inaccessible = [];
|
|
633
|
+
for (const repo of additionalRepos) {
|
|
634
|
+
if (!(await repoExists(githubToken, orgLogin, repo))) {
|
|
635
|
+
inaccessible.push(repo);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (inaccessible.length > 0) {
|
|
639
|
+
console.log("");
|
|
640
|
+
ui.info(`Accept ${inaccessible.length === 1 ? "the invitation" : "all invitations"} in your browser.`);
|
|
641
|
+
for (const repo of inaccessible) {
|
|
642
|
+
openBrowser(`https://github.com/${orgLogin}/${repo}`);
|
|
643
|
+
}
|
|
644
|
+
await ui.prompt("Press Enter after accepting...");
|
|
645
|
+
|
|
646
|
+
const stillBlocked = [];
|
|
647
|
+
for (const repo of inaccessible) {
|
|
648
|
+
if (!(await repoExists(githubToken, orgLogin, repo))) {
|
|
649
|
+
stillBlocked.push(repo);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (stillBlocked.length > 0) {
|
|
653
|
+
ui.error(`Still can't access: ${stillBlocked.join(", ")}`);
|
|
654
|
+
ui.error("Make sure you've been invited to all repos.");
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
ui.success("All repos accessible");
|
|
658
|
+
}
|
|
659
|
+
|
|
626
660
|
// 6. Clone locally
|
|
627
661
|
const base = process.cwd();
|
|
628
662
|
const forkUrl = `https://github.com/${orgLogin}/${repoName}.git`;
|