create-hq 10.9.1 → 10.11.0
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/commands/{team-sync.md → sync-team.md} +167 -6
- package/dist/__tests__/auth.test.js +176 -186
- package/dist/__tests__/auth.test.js.map +1 -1
- package/dist/__tests__/scaffold.test.js +5 -3
- package/dist/__tests__/scaffold.test.js.map +1 -1
- package/dist/auth.d.ts +52 -131
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +73 -369
- package/dist/auth.js.map +1 -1
- package/dist/fetch-template.d.ts +7 -2
- package/dist/fetch-template.d.ts.map +1 -1
- package/dist/fetch-template.js +27 -15
- package/dist/fetch-template.js.map +1 -1
- package/dist/index.js +7 -17
- package/dist/index.js.map +1 -1
- package/dist/packages.d.ts.map +1 -1
- package/dist/packages.js +1 -1
- package/dist/packages.js.map +1 -1
- package/dist/recommended-packages.d.ts +93 -0
- package/dist/recommended-packages.d.ts.map +1 -0
- package/dist/recommended-packages.js +221 -0
- package/dist/recommended-packages.js.map +1 -0
- package/dist/scaffold.d.ts +4 -2
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +139 -98
- package/dist/scaffold.js.map +1 -1
- package/dist/ui.d.ts +0 -17
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +0 -45
- package/dist/ui.js.map +1 -1
- package/package.json +2 -1
- package/dist/admin-onboarding.d.ts +0 -44
- package/dist/admin-onboarding.d.ts.map +0 -1
- package/dist/admin-onboarding.js +0 -530
- package/dist/admin-onboarding.js.map +0 -1
- package/dist/company-template.d.ts +0 -34
- package/dist/company-template.d.ts.map +0 -1
- package/dist/company-template.js +0 -142
- package/dist/company-template.js.map +0 -1
- package/dist/invite-command.d.ts +0 -10
- package/dist/invite-command.d.ts.map +0 -1
- package/dist/invite-command.js +0 -110
- package/dist/invite-command.js.map +0 -1
- package/dist/invite.d.ts +0 -91
- package/dist/invite.d.ts.map +0 -1
- package/dist/invite.js +0 -230
- package/dist/invite.js.map +0 -1
- package/dist/join-flow.d.ts +0 -32
- package/dist/join-flow.d.ts.map +0 -1
- package/dist/join-flow.js +0 -205
- package/dist/join-flow.js.map +0 -1
- package/dist/team-setup.d.ts +0 -83
- package/dist/team-setup.d.ts.map +0 -1
- package/dist/team-setup.js +0 -353
- package/dist/team-setup.js.map +0 -1
- package/dist/teams-flow.d.ts +0 -41
- package/dist/teams-flow.d.ts.map +0 -1
- package/dist/teams-flow.js +0 -173
- package/dist/teams-flow.js.map +0 -1
package/dist/company-template.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Company / team-repo template generator.
|
|
3
|
-
*
|
|
4
|
-
* When an admin creates a new team, we need to seed the new {org}/hq private
|
|
5
|
-
* repo with a baseline directory structure that mirrors what /newcompany
|
|
6
|
-
* produces. This module renders that structure into a target directory
|
|
7
|
-
* (which is then committed and pushed to the new GitHub repo).
|
|
8
|
-
*
|
|
9
|
-
* No filesystem dependencies on the core HQ template — these files are
|
|
10
|
-
* synthesized in code so the seed is version-locked with the create-hq
|
|
11
|
-
* release the user just installed.
|
|
12
|
-
*/
|
|
13
|
-
import * as fs from "fs";
|
|
14
|
-
import * as path from "path";
|
|
15
|
-
const COMPANY_SUBDIRS = [
|
|
16
|
-
"knowledge",
|
|
17
|
-
"settings",
|
|
18
|
-
"data",
|
|
19
|
-
"workers",
|
|
20
|
-
"repos",
|
|
21
|
-
"projects",
|
|
22
|
-
"policies",
|
|
23
|
-
];
|
|
24
|
-
const GITIGNORE = `# Local secrets and caches — never commit
|
|
25
|
-
settings/*.local.*
|
|
26
|
-
settings/.env*
|
|
27
|
-
data/cache/
|
|
28
|
-
data/tmp/
|
|
29
|
-
*.log
|
|
30
|
-
.DS_Store
|
|
31
|
-
Thumbs.db
|
|
32
|
-
`;
|
|
33
|
-
function readme(meta) {
|
|
34
|
-
return `# ${meta.team_name}
|
|
35
|
-
|
|
36
|
-
HQ Teams workspace for **${meta.org_login}**.
|
|
37
|
-
|
|
38
|
-
## What is this?
|
|
39
|
-
|
|
40
|
-
This repository is the shared workspace for the **${meta.team_name}** HQ team.
|
|
41
|
-
Members of the \`${meta.org_login}\` GitHub organization with access to this
|
|
42
|
-
repo will see its contents inside their local HQ at:
|
|
43
|
-
|
|
44
|
-
\`\`\`
|
|
45
|
-
companies/${meta.team_slug}/
|
|
46
|
-
\`\`\`
|
|
47
|
-
|
|
48
|
-
## Structure
|
|
49
|
-
|
|
50
|
-
| Directory | Purpose |
|
|
51
|
-
|-----------|---------|
|
|
52
|
-
| \`knowledge/\` | Shared docs, references, and qmd-indexed content |
|
|
53
|
-
| \`settings/\` | Team-scoped credentials and config (gitignore secrets) |
|
|
54
|
-
| \`data/\` | Exports, datasets, and reports |
|
|
55
|
-
| \`workers/\` | Custom worker definitions for this team |
|
|
56
|
-
| \`repos/\` | References to repos the team owns |
|
|
57
|
-
| \`projects/\` | Active projects with PRDs and stories |
|
|
58
|
-
| \`policies/\` | Team policies (auto-loaded by Claude Code) |
|
|
59
|
-
|
|
60
|
-
## Adding members
|
|
61
|
-
|
|
62
|
-
Anyone with access to this repository on GitHub will automatically pick up
|
|
63
|
-
this workspace when they run \`npx create-hq\`. To add a teammate, grant them
|
|
64
|
-
access through the \`${meta.org_login}\` GitHub organization or directly to
|
|
65
|
-
this repository.
|
|
66
|
-
|
|
67
|
-
## Created
|
|
68
|
-
|
|
69
|
-
- Created by: @${meta.created_by}
|
|
70
|
-
- Created at: ${meta.created_at}
|
|
71
|
-
- Seeded from: create-hq / HQ template ${meta.hq_version}
|
|
72
|
-
`;
|
|
73
|
-
}
|
|
74
|
-
function knowledgeReadme(meta) {
|
|
75
|
-
return `# ${meta.team_name} Knowledge
|
|
76
|
-
|
|
77
|
-
This is the shared knowledge base for the ${meta.team_name} HQ team.
|
|
78
|
-
|
|
79
|
-
Drop markdown files here to share docs, references, and conventions across
|
|
80
|
-
the team. Indexed by qmd for semantic + full-text search.
|
|
81
|
-
`;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Render the company template into targetDir. Creates the directory if it
|
|
85
|
-
* does not exist. Will not overwrite existing files (idempotent on re-runs).
|
|
86
|
-
*/
|
|
87
|
-
export function writeCompanyTemplate(targetDir, meta) {
|
|
88
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
89
|
-
// Subdirectories with .gitkeep
|
|
90
|
-
for (const sub of COMPANY_SUBDIRS) {
|
|
91
|
-
const dir = path.join(targetDir, sub);
|
|
92
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
93
|
-
const gitkeep = path.join(dir, ".gitkeep");
|
|
94
|
-
if (!fs.existsSync(gitkeep)) {
|
|
95
|
-
fs.writeFileSync(gitkeep, "", "utf-8");
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// README.md
|
|
99
|
-
const readmePath = path.join(targetDir, "README.md");
|
|
100
|
-
if (!fs.existsSync(readmePath)) {
|
|
101
|
-
fs.writeFileSync(readmePath, readme(meta), "utf-8");
|
|
102
|
-
}
|
|
103
|
-
// team.json
|
|
104
|
-
const teamJsonPath = path.join(targetDir, "team.json");
|
|
105
|
-
if (!fs.existsSync(teamJsonPath)) {
|
|
106
|
-
fs.writeFileSync(teamJsonPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
107
|
-
}
|
|
108
|
-
// knowledge/README.md (replaces .gitkeep there)
|
|
109
|
-
const knowledgeReadmePath = path.join(targetDir, "knowledge", "README.md");
|
|
110
|
-
if (!fs.existsSync(knowledgeReadmePath)) {
|
|
111
|
-
fs.writeFileSync(knowledgeReadmePath, knowledgeReadme(meta), "utf-8");
|
|
112
|
-
// Remove the redundant .gitkeep now that there's a real file
|
|
113
|
-
const gitkeep = path.join(targetDir, "knowledge", ".gitkeep");
|
|
114
|
-
if (fs.existsSync(gitkeep)) {
|
|
115
|
-
try {
|
|
116
|
-
fs.unlinkSync(gitkeep);
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
// ignore
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// .gitignore
|
|
124
|
-
const gitignorePath = path.join(targetDir, ".gitignore");
|
|
125
|
-
if (!fs.existsSync(gitignorePath)) {
|
|
126
|
-
fs.writeFileSync(gitignorePath, GITIGNORE, "utf-8");
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Ensure all standard company subdirectories exist inside targetDir
|
|
131
|
-
* without overwriting any existing files. Used after cloning a team repo
|
|
132
|
-
* to fill in any directories the upstream repo doesn't have yet.
|
|
133
|
-
*/
|
|
134
|
-
export function ensureCompanyStructure(targetDir) {
|
|
135
|
-
for (const sub of COMPANY_SUBDIRS) {
|
|
136
|
-
const dir = path.join(targetDir, sub);
|
|
137
|
-
if (!fs.existsSync(dir)) {
|
|
138
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
//# sourceMappingURL=company-template.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"company-template.js","sourceRoot":"","sources":["../src/company-template.ts"],"names":[],"mappings":"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"}
|
package/dist/invite-command.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Standalone invite command for admins.
|
|
3
|
-
*
|
|
4
|
-
* Usage: create-hq invite
|
|
5
|
-
*
|
|
6
|
-
* Finds team metadata from the current HQ directory, authenticates
|
|
7
|
-
* the admin, and generates an invite code plus optional org invite.
|
|
8
|
-
*/
|
|
9
|
-
export declare function runInviteCommand(): Promise<void>;
|
|
10
|
-
//# sourceMappingURL=invite-command.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invite-command.d.ts","sourceRoot":"","sources":["../src/invite-command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6EH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CA8CtD"}
|
package/dist/invite-command.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Standalone invite command for admins.
|
|
3
|
-
*
|
|
4
|
-
* Usage: create-hq invite
|
|
5
|
-
*
|
|
6
|
-
* Finds team metadata from the current HQ directory, authenticates
|
|
7
|
-
* the admin, and generates an invite code plus optional org invite.
|
|
8
|
-
*/
|
|
9
|
-
import * as fs from "fs";
|
|
10
|
-
import * as path from "path";
|
|
11
|
-
import { execSync } from "child_process";
|
|
12
|
-
import { createInterface } from "readline";
|
|
13
|
-
import chalk from "chalk";
|
|
14
|
-
import { authenticate } from "./teams-flow.js";
|
|
15
|
-
import { inviteLoop } from "./admin-onboarding.js";
|
|
16
|
-
import { warn, info } from "./ui.js";
|
|
17
|
-
function prompt(question, defaultVal) {
|
|
18
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
19
|
-
const suffix = defaultVal ? ` (${defaultVal})` : "";
|
|
20
|
-
return new Promise((resolve) => {
|
|
21
|
-
rl.question(` ? ${question}${suffix} `, (answer) => {
|
|
22
|
-
rl.close();
|
|
23
|
-
resolve(answer.trim() || defaultVal || "");
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Find team.json files in companies/ walking up from cwd.
|
|
29
|
-
*/
|
|
30
|
-
function findTeams() {
|
|
31
|
-
let dir = process.cwd();
|
|
32
|
-
const root = path.parse(dir).root;
|
|
33
|
-
const teams = [];
|
|
34
|
-
while (dir !== root) {
|
|
35
|
-
const companiesDir = path.join(dir, "companies");
|
|
36
|
-
if (fs.existsSync(companiesDir) && fs.statSync(companiesDir).isDirectory()) {
|
|
37
|
-
for (const entry of fs.readdirSync(companiesDir)) {
|
|
38
|
-
const teamJsonPath = path.join(companiesDir, entry, "team.json");
|
|
39
|
-
if (fs.existsSync(teamJsonPath)) {
|
|
40
|
-
try {
|
|
41
|
-
const meta = JSON.parse(fs.readFileSync(teamJsonPath, "utf-8"));
|
|
42
|
-
let cloneUrl = `https://github.com/${meta.org_login}/hq-${meta.team_slug}.git`;
|
|
43
|
-
try {
|
|
44
|
-
const remote = execSync("git remote get-url origin", {
|
|
45
|
-
cwd: path.join(companiesDir, entry),
|
|
46
|
-
stdio: "pipe",
|
|
47
|
-
})
|
|
48
|
-
.toString()
|
|
49
|
-
.trim();
|
|
50
|
-
if (remote)
|
|
51
|
-
cloneUrl = remote;
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Use constructed URL
|
|
55
|
-
}
|
|
56
|
-
teams.push({
|
|
57
|
-
meta,
|
|
58
|
-
cloneUrl,
|
|
59
|
-
companyDir: path.join(companiesDir, entry),
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
// Skip malformed team.json
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
dir = path.dirname(dir);
|
|
70
|
-
}
|
|
71
|
-
return teams;
|
|
72
|
-
}
|
|
73
|
-
export async function runInviteCommand() {
|
|
74
|
-
console.log();
|
|
75
|
-
console.log(chalk.bold(" Generate a team invite"));
|
|
76
|
-
console.log();
|
|
77
|
-
const teams = findTeams();
|
|
78
|
-
if (teams.length === 0) {
|
|
79
|
-
warn("No teams found. Run this from your HQ directory (must have companies/*/team.json).");
|
|
80
|
-
info("If you haven't created a team yet, run: npx create-hq");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
let selected;
|
|
84
|
-
if (teams.length === 1) {
|
|
85
|
-
selected = teams[0];
|
|
86
|
-
info(`Team: ${chalk.cyan(selected.meta.team_name)} (${selected.meta.org_login})`);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
console.log(" Which team?");
|
|
90
|
-
for (let i = 0; i < teams.length; i++) {
|
|
91
|
-
console.log(chalk.cyan(` [${i + 1}] `) +
|
|
92
|
-
chalk.white(teams[i].meta.team_name) +
|
|
93
|
-
chalk.dim(` (${teams[i].meta.org_login})`));
|
|
94
|
-
}
|
|
95
|
-
const choice = await prompt(`Select (1-${teams.length})`, "1");
|
|
96
|
-
const idx = parseInt(choice, 10) - 1;
|
|
97
|
-
if (idx < 0 || idx >= teams.length) {
|
|
98
|
-
warn("Invalid selection.");
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
selected = teams[idx];
|
|
102
|
-
}
|
|
103
|
-
const auth = await authenticate();
|
|
104
|
-
if (!auth) {
|
|
105
|
-
warn("Authentication required to generate invites.");
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
await inviteLoop(auth, selected.meta, selected.cloneUrl);
|
|
109
|
-
}
|
|
110
|
-
//# sourceMappingURL=invite-command.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invite-command.js","sourceRoot":"","sources":["../src/invite-command.ts"],"names":[],"mappings":"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"}
|
package/dist/invite.d.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Invite system for HQ Teams.
|
|
3
|
-
*
|
|
4
|
-
* Tokens are self-contained (no server) — they encode the team coordinates
|
|
5
|
-
* so a new member can join without discovery. Security comes from GitHub:
|
|
6
|
-
* the member still needs org access to clone the private repo.
|
|
7
|
-
*
|
|
8
|
-
* Token format: "hq_" + base64url({ org, repo, slug, teamName, cloneUrl, invitedBy })
|
|
9
|
-
*
|
|
10
|
-
* Admin flow:
|
|
11
|
-
* 1. Generate token from team metadata
|
|
12
|
-
* 2. Optionally send GitHub org invite by email
|
|
13
|
-
* 3. Share token + instructions with member
|
|
14
|
-
*
|
|
15
|
-
* Member flow:
|
|
16
|
-
* 1. Decode token → get team coordinates
|
|
17
|
-
* 2. Auth via GitHub device flow
|
|
18
|
-
* 3. Verify org membership (wait for invite acceptance if needed)
|
|
19
|
-
* 4. Clone repo into companies/{slug}/
|
|
20
|
-
*/
|
|
21
|
-
import { type GitHubAuth } from "./auth.js";
|
|
22
|
-
export interface InvitePayload {
|
|
23
|
-
/** GitHub org login (e.g. "indigoai-us") */
|
|
24
|
-
org: string;
|
|
25
|
-
/** Repo name (e.g. "hq-indigo") */
|
|
26
|
-
repo: string;
|
|
27
|
-
/** Team slug (e.g. "indigo") — maps to companies/{slug}/ */
|
|
28
|
-
slug: string;
|
|
29
|
-
/** Human-readable team name */
|
|
30
|
-
teamName: string;
|
|
31
|
-
/** HTTPS clone URL for the repo */
|
|
32
|
-
cloneUrl: string;
|
|
33
|
-
/** GitHub login of the admin who generated the invite */
|
|
34
|
-
invitedBy: string;
|
|
35
|
-
}
|
|
36
|
-
export declare function encodeInviteToken(payload: InvitePayload): string;
|
|
37
|
-
export declare function decodeInviteToken(token: string): InvitePayload | null;
|
|
38
|
-
interface OrgInviteResponse {
|
|
39
|
-
id: number;
|
|
40
|
-
login?: string;
|
|
41
|
-
email?: string;
|
|
42
|
-
role: string;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Send a GitHub org invitation by email.
|
|
46
|
-
*
|
|
47
|
-
* Requires the authenticated user to be an org admin, and the GitHub App
|
|
48
|
-
* to have "Organization > Members: Write" permission.
|
|
49
|
-
*
|
|
50
|
-
* Returns the invitation on success, or an error message string on failure.
|
|
51
|
-
*/
|
|
52
|
-
export declare function sendOrgInviteByEmail(auth: GitHubAuth, orgLogin: string, email: string, role?: "direct_member" | "admin"): Promise<{
|
|
53
|
-
ok: true;
|
|
54
|
-
invite: OrgInviteResponse;
|
|
55
|
-
} | {
|
|
56
|
-
ok: false;
|
|
57
|
-
error: string;
|
|
58
|
-
}>;
|
|
59
|
-
/**
|
|
60
|
-
* Check whether a GitHub user is a member of an org.
|
|
61
|
-
*/
|
|
62
|
-
export declare function checkOrgMembership(auth: GitHubAuth, orgLogin: string, username: string): Promise<"active" | "pending" | "none">;
|
|
63
|
-
/**
|
|
64
|
-
* Check whether the authenticated user can access a specific repo.
|
|
65
|
-
*/
|
|
66
|
-
export declare function checkRepoAccess(auth: GitHubAuth, orgLogin: string, repoName: string): Promise<boolean>;
|
|
67
|
-
/**
|
|
68
|
-
* Generate a ready-to-share invite message for the admin to send to the
|
|
69
|
-
* new member. Designed to be copy-pasted into Slack, email, or text.
|
|
70
|
-
*/
|
|
71
|
-
export declare function formatInviteMessage(payload: InvitePayload, token: string, memberEmail?: string): string;
|
|
72
|
-
/**
|
|
73
|
-
* Print the invite details to the console for the admin.
|
|
74
|
-
*/
|
|
75
|
-
export declare function printInviteSummary(payload: InvitePayload, token: string, _emailSent?: boolean, memberEmail?: string): void;
|
|
76
|
-
/**
|
|
77
|
-
* Build a mailto: URL pre-populated with the invite message.
|
|
78
|
-
* Opens in the admin's default email client — they just hit Send.
|
|
79
|
-
*/
|
|
80
|
-
export declare function buildMailtoUrl(payload: InvitePayload, token: string, recipientEmail: string): string;
|
|
81
|
-
/**
|
|
82
|
-
* Open the admin's email client with a pre-populated invite email.
|
|
83
|
-
*/
|
|
84
|
-
export declare function openInviteEmail(payload: InvitePayload, token: string, recipientEmail: string): void;
|
|
85
|
-
/**
|
|
86
|
-
* Copy text to the system clipboard. Cross-platform: pbcopy (macOS),
|
|
87
|
-
* clip (Windows), xclip/xsel (Linux). Returns true on success.
|
|
88
|
-
*/
|
|
89
|
-
export declare function copyToClipboard(text: string): boolean;
|
|
90
|
-
export {};
|
|
91
|
-
//# sourceMappingURL=invite.d.ts.map
|
package/dist/invite.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invite.d.ts","sourceRoot":"","sources":["../src/invite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,KAAK,UAAU,EAA0B,MAAM,WAAW,CAAC;AAIpE,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB;AAmBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAEhE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwBrE;AAID,UAAU,iBAAiB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,eAAe,GAAG,OAAyB,GAChD,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiCjF;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAwBxC;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAOlB;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAoDR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,OAAO,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAsBN;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,GACrB,MAAM,CAIR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,GACrB,IAAI,CAGN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmBrD"}
|
package/dist/invite.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Invite system for HQ Teams.
|
|
3
|
-
*
|
|
4
|
-
* Tokens are self-contained (no server) — they encode the team coordinates
|
|
5
|
-
* so a new member can join without discovery. Security comes from GitHub:
|
|
6
|
-
* the member still needs org access to clone the private repo.
|
|
7
|
-
*
|
|
8
|
-
* Token format: "hq_" + base64url({ org, repo, slug, teamName, cloneUrl, invitedBy })
|
|
9
|
-
*
|
|
10
|
-
* Admin flow:
|
|
11
|
-
* 1. Generate token from team metadata
|
|
12
|
-
* 2. Optionally send GitHub org invite by email
|
|
13
|
-
* 3. Share token + instructions with member
|
|
14
|
-
*
|
|
15
|
-
* Member flow:
|
|
16
|
-
* 1. Decode token → get team coordinates
|
|
17
|
-
* 2. Auth via GitHub device flow
|
|
18
|
-
* 3. Verify org membership (wait for invite acceptance if needed)
|
|
19
|
-
* 4. Clone repo into companies/{slug}/
|
|
20
|
-
*/
|
|
21
|
-
import chalk from "chalk";
|
|
22
|
-
import { execSync } from "child_process";
|
|
23
|
-
import { githubApi, openBrowser } from "./auth.js";
|
|
24
|
-
// ─── Token encode / decode ──────────────────────────────────────────────────
|
|
25
|
-
const TOKEN_PREFIX = "hq_";
|
|
26
|
-
function toBase64Url(str) {
|
|
27
|
-
return Buffer.from(str, "utf-8")
|
|
28
|
-
.toString("base64")
|
|
29
|
-
.replace(/\+/g, "-")
|
|
30
|
-
.replace(/\//g, "_")
|
|
31
|
-
.replace(/=+$/, "");
|
|
32
|
-
}
|
|
33
|
-
function fromBase64Url(b64) {
|
|
34
|
-
const padded = b64.replace(/-/g, "+").replace(/_/g, "/");
|
|
35
|
-
return Buffer.from(padded, "base64").toString("utf-8");
|
|
36
|
-
}
|
|
37
|
-
export function encodeInviteToken(payload) {
|
|
38
|
-
return TOKEN_PREFIX + toBase64Url(JSON.stringify(payload));
|
|
39
|
-
}
|
|
40
|
-
export function decodeInviteToken(token) {
|
|
41
|
-
try {
|
|
42
|
-
const raw = token.startsWith(TOKEN_PREFIX)
|
|
43
|
-
? token.slice(TOKEN_PREFIX.length)
|
|
44
|
-
: token;
|
|
45
|
-
const json = fromBase64Url(raw);
|
|
46
|
-
const parsed = JSON.parse(json);
|
|
47
|
-
// Validate required fields
|
|
48
|
-
if (typeof parsed.org !== "string" ||
|
|
49
|
-
typeof parsed.repo !== "string" ||
|
|
50
|
-
typeof parsed.slug !== "string" ||
|
|
51
|
-
typeof parsed.teamName !== "string" ||
|
|
52
|
-
typeof parsed.cloneUrl !== "string" ||
|
|
53
|
-
typeof parsed.invitedBy !== "string") {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
return parsed;
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Send a GitHub org invitation by email.
|
|
64
|
-
*
|
|
65
|
-
* Requires the authenticated user to be an org admin, and the GitHub App
|
|
66
|
-
* to have "Organization > Members: Write" permission.
|
|
67
|
-
*
|
|
68
|
-
* Returns the invitation on success, or an error message string on failure.
|
|
69
|
-
*/
|
|
70
|
-
export async function sendOrgInviteByEmail(auth, orgLogin, email, role = "direct_member") {
|
|
71
|
-
try {
|
|
72
|
-
const invite = await githubApi(`/orgs/${orgLogin}/invitations`, auth, {
|
|
73
|
-
method: "POST",
|
|
74
|
-
body: JSON.stringify({
|
|
75
|
-
email,
|
|
76
|
-
role,
|
|
77
|
-
}),
|
|
78
|
-
});
|
|
79
|
-
return { ok: true, invite };
|
|
80
|
-
}
|
|
81
|
-
catch (err) {
|
|
82
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
-
if (message.includes("404") || message.includes("403")) {
|
|
84
|
-
return {
|
|
85
|
-
ok: false,
|
|
86
|
-
error: "The HQ App doesn't have permission to send org invites. " +
|
|
87
|
-
`Please invite the member manually at: https://github.com/orgs/${orgLogin}/people`,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
if (message.includes("422") && /already/i.test(message)) {
|
|
91
|
-
return {
|
|
92
|
-
ok: false,
|
|
93
|
-
error: "This email has already been invited or is already a member.",
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
return { ok: false, error: message };
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Check whether a GitHub user is a member of an org.
|
|
101
|
-
*/
|
|
102
|
-
export async function checkOrgMembership(auth, orgLogin, username) {
|
|
103
|
-
// Check active membership first
|
|
104
|
-
try {
|
|
105
|
-
await githubApi(`/orgs/${orgLogin}/members/${username}`, auth);
|
|
106
|
-
return "active";
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
// Not an active member — check pending invitations
|
|
110
|
-
}
|
|
111
|
-
// Check if there's a pending invitation for this user
|
|
112
|
-
try {
|
|
113
|
-
const memberships = await githubApi("/user/memberships/orgs?state=pending&per_page=100", auth);
|
|
114
|
-
const pending = memberships.find((m) => m.organization.login.toLowerCase() === orgLogin.toLowerCase());
|
|
115
|
-
if (pending)
|
|
116
|
-
return "pending";
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
// Can't check pending — fall through
|
|
120
|
-
}
|
|
121
|
-
return "none";
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Check whether the authenticated user can access a specific repo.
|
|
125
|
-
*/
|
|
126
|
-
export async function checkRepoAccess(auth, orgLogin, repoName) {
|
|
127
|
-
try {
|
|
128
|
-
await githubApi(`/repos/${orgLogin}/${repoName}`, auth);
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// ─── Invite message template ────────────────────────────────────────────────
|
|
136
|
-
/**
|
|
137
|
-
* Generate a ready-to-share invite message for the admin to send to the
|
|
138
|
-
* new member. Designed to be copy-pasted into Slack, email, or text.
|
|
139
|
-
*/
|
|
140
|
-
export function formatInviteMessage(payload, token, memberEmail) {
|
|
141
|
-
const orgInviteUrl = `https://github.com/orgs/${payload.org}/invitation`;
|
|
142
|
-
const lines = [
|
|
143
|
-
`You've been invited to join ${payload.teamName} on HQ!`,
|
|
144
|
-
"",
|
|
145
|
-
];
|
|
146
|
-
// Step 1: Accept org invite (which handles GitHub account creation too)
|
|
147
|
-
if (memberEmail) {
|
|
148
|
-
lines.push(`Step 1: Accept the GitHub invitation sent to ${memberEmail}`, ` (if you don't have a GitHub account yet, you'll be guided to create one)`, ` Direct link: ${orgInviteUrl}`, "");
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
lines.push(`Step 1: Accept your GitHub organization invite`, ` Direct link: ${orgInviteUrl}`, ` (if you haven't received one, ask @${payload.invitedBy} to send it)`, "");
|
|
152
|
-
}
|
|
153
|
-
// Step 2: Install Node.js (platform-specific instructions)
|
|
154
|
-
lines.push(`Step 2: Install Node.js (if you don't have it already)`, "", ` Windows (PowerShell, run as Administrator):`, ` winget install OpenJS.NodeJS.LTS`, "", ` Mac (Terminal):`, ` brew install node`, ` (if you don't have brew: https://brew.sh)`, "", ` Or download from: https://nodejs.org`, "");
|
|
155
|
-
// Step 3: Single command with token
|
|
156
|
-
lines.push(`Step 3: Open your terminal and run this command:`, "", ` npx create-hq --invite ${token}`, "", ` Then follow the prompts to sign in with GitHub and complete setup.`, "", `---`, `Questions? Ask @${payload.invitedBy} or visit https://getindigo.ai/hq`);
|
|
157
|
-
return lines.join("\n");
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Print the invite details to the console for the admin.
|
|
161
|
-
*/
|
|
162
|
-
export function printInviteSummary(payload, token, _emailSent, memberEmail) {
|
|
163
|
-
console.log();
|
|
164
|
-
console.log(chalk.bold(" Invite ready!"));
|
|
165
|
-
console.log();
|
|
166
|
-
console.log(` ${chalk.dim("Team:")} ${chalk.cyan(payload.teamName)}`);
|
|
167
|
-
console.log(` ${chalk.dim("Org:")} ${payload.org}`);
|
|
168
|
-
if (memberEmail) {
|
|
169
|
-
console.log(` ${chalk.dim("Email:")} ${memberEmail}`);
|
|
170
|
-
}
|
|
171
|
-
console.log();
|
|
172
|
-
console.log(chalk.bold(" Invite code (share with the new member):"));
|
|
173
|
-
console.log();
|
|
174
|
-
console.log(` ${chalk.cyan(token)}`);
|
|
175
|
-
console.log();
|
|
176
|
-
console.log(chalk.dim(" Full invite message (copy-paste ready):"));
|
|
177
|
-
console.log(chalk.dim(" ─".repeat(30)));
|
|
178
|
-
const msg = formatInviteMessage(payload, token, memberEmail);
|
|
179
|
-
for (const line of msg.split("\n")) {
|
|
180
|
-
console.log(chalk.dim(" ") + line);
|
|
181
|
-
}
|
|
182
|
-
console.log(chalk.dim(" ─".repeat(30)));
|
|
183
|
-
console.log();
|
|
184
|
-
}
|
|
185
|
-
// ─── Mailto helper ──────────────────────────────────────────────────────────
|
|
186
|
-
/**
|
|
187
|
-
* Build a mailto: URL pre-populated with the invite message.
|
|
188
|
-
* Opens in the admin's default email client — they just hit Send.
|
|
189
|
-
*/
|
|
190
|
-
export function buildMailtoUrl(payload, token, recipientEmail) {
|
|
191
|
-
const subject = `You're invited to join ${payload.teamName} on HQ`;
|
|
192
|
-
const body = formatInviteMessage(payload, token, recipientEmail);
|
|
193
|
-
return `mailto:${recipientEmail}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Open the admin's email client with a pre-populated invite email.
|
|
197
|
-
*/
|
|
198
|
-
export function openInviteEmail(payload, token, recipientEmail) {
|
|
199
|
-
const url = buildMailtoUrl(payload, token, recipientEmail);
|
|
200
|
-
openBrowser(url);
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Copy text to the system clipboard. Cross-platform: pbcopy (macOS),
|
|
204
|
-
* clip (Windows), xclip/xsel (Linux). Returns true on success.
|
|
205
|
-
*/
|
|
206
|
-
export function copyToClipboard(text) {
|
|
207
|
-
try {
|
|
208
|
-
const platform = process.platform;
|
|
209
|
-
if (platform === "darwin") {
|
|
210
|
-
execSync("pbcopy", { input: text });
|
|
211
|
-
}
|
|
212
|
-
else if (platform === "win32") {
|
|
213
|
-
execSync("clip", { input: text });
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
// Try xclip first, fall back to xsel
|
|
217
|
-
try {
|
|
218
|
-
execSync("xclip -selection clipboard", { input: text });
|
|
219
|
-
}
|
|
220
|
-
catch {
|
|
221
|
-
execSync("xsel --clipboard --input", { input: text });
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
//# sourceMappingURL=invite.js.map
|
package/dist/invite.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invite.js","sourceRoot":"","sources":["../src/invite.ts"],"names":[],"mappings":"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"}
|
package/dist/join-flow.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Join-via-invite flow for HQ Teams members.
|
|
3
|
-
*
|
|
4
|
-
* When a member has an invite token (from an admin), this flow:
|
|
5
|
-
* 1. Decodes the token to get team coordinates
|
|
6
|
-
* 2. Authenticates via GitHub device flow
|
|
7
|
-
* 3. Checks org membership / repo access (waits if invite is pending)
|
|
8
|
-
* 4. Clones the team repo into companies/{slug}/
|
|
9
|
-
*
|
|
10
|
-
* This is the primary path for non-technical users who received an invite.
|
|
11
|
-
*/
|
|
12
|
-
import { type GitHubAuth } from "./auth.js";
|
|
13
|
-
export interface JoinByInviteResult {
|
|
14
|
-
/** Team slug (companies/{slug}/ directory). */
|
|
15
|
-
slug: string;
|
|
16
|
-
/** Human-readable team name. */
|
|
17
|
-
teamName: string;
|
|
18
|
-
/** Local path where the team repo was cloned. */
|
|
19
|
-
companyDir: string;
|
|
20
|
-
/** GitHub HTML URL for the team repo. */
|
|
21
|
-
repoUrl: string;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Run the join-via-invite flow.
|
|
25
|
-
*
|
|
26
|
-
* @param auth - Authenticated GitHub user (already signed in)
|
|
27
|
-
* @param hqRoot - Local HQ root directory (where companies/ lives)
|
|
28
|
-
* @param token - The invite token string (hq_...) or raw base64
|
|
29
|
-
* @returns - Result on success, null on failure/abort
|
|
30
|
-
*/
|
|
31
|
-
export declare function runJoinByInvite(auth: GitHubAuth, hqRoot: string, token: string): Promise<JoinByInviteResult | null>;
|
|
32
|
-
//# sourceMappingURL=join-flow.d.ts.map
|
package/dist/join-flow.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"join-flow.d.ts","sourceRoot":"","sources":["../src/join-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAY5C,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAwED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAwIpC"}
|