create-githat-app 1.0.5 → 1.0.7
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/README.md +35 -0
- package/dist/cli.js +102 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# create-githat-app
|
|
2
4
|
|
|
5
|
+
**The CLI for [GitHat](https://githat.io) — Auth + Hosting that replaces Clerk + Vercel**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/create-githat-app)
|
|
8
|
+
[](https://www.npmjs.com/package/create-githat-app)
|
|
9
|
+
[](https://www.npmjs.com/package/@githat/nextjs)
|
|
10
|
+
[](https://githat.io)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
3
17
|
Scaffold a production-ready app with a fully-managed backend — auth, teams, orgs, API keys, MCP verification, and AI agent identity. **No backend to deploy.**
|
|
4
18
|
|
|
5
19
|
[GitHat](https://githat.io) is your backend. When you run `create-githat-app`, your generated project connects to GitHat's hosted platform at `api.githat.io`. User accounts, organizations, teams, API keys, MCP servers, and AI agents are all stored and managed by GitHat. You write frontend code only.
|
|
@@ -263,6 +277,19 @@ VITE_GITHAT_PUBLISHABLE_KEY=pk_live_...
|
|
|
263
277
|
VITE_GITHAT_API_URL=https://api.githat.io
|
|
264
278
|
```
|
|
265
279
|
|
|
280
|
+
## Documentation
|
|
281
|
+
|
|
282
|
+
- [GitHat Quick Start](https://githat.io/docs/quickstart) — Get running in 5 minutes
|
|
283
|
+
- [SDK Reference](https://githat.io/docs/sdk) — `@githat/nextjs` hooks, components, and server utils
|
|
284
|
+
- [API Reference](https://githat.io/docs/api) — All 86 REST endpoints
|
|
285
|
+
- [CLI Reference](https://githat.io/docs/cli) — Flags, prompts, and templates
|
|
286
|
+
- [Next.js Guide](https://githat.io/docs/nextjs) — Auth in Next.js 14-16+
|
|
287
|
+
- [React Guide](https://githat.io/docs/react) — Auth in any React app
|
|
288
|
+
- [Bring Your Own Database](https://githat.io/docs/byod) — Keep your PostgreSQL, MySQL, or MongoDB
|
|
289
|
+
- [MCP Servers](https://githat.io/docs/mcp) — Domain verification for AI tool servers
|
|
290
|
+
- [AI Agents](https://githat.io/docs/agents) — Wallet-based identity for autonomous agents
|
|
291
|
+
- [Skills Marketplace](https://githat.io/docs/skills) — Install templates, integrations, and workflows
|
|
292
|
+
|
|
266
293
|
## Contributing
|
|
267
294
|
|
|
268
295
|
```bash
|
|
@@ -273,6 +300,14 @@ npm run build
|
|
|
273
300
|
node bin/index.js test-app
|
|
274
301
|
```
|
|
275
302
|
|
|
303
|
+
## Related
|
|
304
|
+
|
|
305
|
+
- [@githat/nextjs](https://www.npmjs.com/package/@githat/nextjs) — React/Next.js SDK
|
|
306
|
+
- [GitHat Platform](https://githat.io) — Auth + Hosting replacing Clerk + Vercel
|
|
307
|
+
- [GitHat Pricing](https://githat.io/pricing) — Free, Pro, and Enterprise tiers
|
|
308
|
+
- [GitHat vs Clerk](https://githat.io/compare/githat-vs-clerk) — Feature comparison
|
|
309
|
+
- [GitHat vs Auth0](https://githat.io/compare/githat-vs-auth0) — Why developers switch
|
|
310
|
+
|
|
276
311
|
## License
|
|
277
312
|
|
|
278
313
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import gradient from "gradient-string";
|
|
|
11
11
|
import chalk from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/constants.ts
|
|
14
|
-
var VERSION = "1.0.
|
|
14
|
+
var VERSION = "1.0.6";
|
|
15
15
|
var DEFAULT_API_URL = "https://api.githat.io";
|
|
16
16
|
var DASHBOARD_URL = "https://githat.io/dashboard/apps";
|
|
17
17
|
var BRAND_COLORS = ["#7c3aed", "#6366f1", "#8b5cf6"];
|
|
@@ -281,12 +281,12 @@ async function promptProjectType() {
|
|
|
281
281
|
{
|
|
282
282
|
value: "frontend",
|
|
283
283
|
label: "Frontend only",
|
|
284
|
-
hint: "Next.js
|
|
284
|
+
hint: "Next.js or React+Vite"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
value: "fullstack",
|
|
288
288
|
label: "Fullstack",
|
|
289
|
-
hint: "Next.js +
|
|
289
|
+
hint: "Next.js + API (Turborepo)"
|
|
290
290
|
}
|
|
291
291
|
]
|
|
292
292
|
})
|
|
@@ -325,16 +325,17 @@ function getInstallCommand(pm) {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
// src/prompts/framework.ts
|
|
328
|
-
async function promptFramework(typescriptOverride) {
|
|
328
|
+
async function promptFramework(typescriptOverride, isFullstack) {
|
|
329
329
|
const packageManager = detectPackageManager();
|
|
330
|
+
const frameworkOptions = isFullstack ? [{ value: "nextjs", label: "Next.js 16", hint: "App Router \xB7 SSR \xB7 middleware auth" }] : [
|
|
331
|
+
{ value: "nextjs", label: "Next.js 16", hint: "App Router \xB7 SSR \xB7 middleware auth" },
|
|
332
|
+
{ value: "react-vite", label: "React 19 + Vite 7", hint: "SPA \xB7 client-side routing" }
|
|
333
|
+
];
|
|
330
334
|
const answers = await p3.group(
|
|
331
335
|
{
|
|
332
336
|
framework: () => p3.select({
|
|
333
337
|
message: "Framework",
|
|
334
|
-
options:
|
|
335
|
-
{ value: "nextjs", label: "Next.js 16", hint: "App Router \xB7 SSR \xB7 middleware auth" },
|
|
336
|
-
{ value: "react-vite", label: "React 19 + Vite 7", hint: "SPA \xB7 client-side routing" }
|
|
337
|
-
]
|
|
338
|
+
options: frameworkOptions
|
|
338
339
|
}),
|
|
339
340
|
typescript: () => typescriptOverride !== void 0 ? Promise.resolve(typescriptOverride) : p3.select({
|
|
340
341
|
message: "Language",
|
|
@@ -397,7 +398,77 @@ async function promptBackend() {
|
|
|
397
398
|
}
|
|
398
399
|
|
|
399
400
|
// src/prompts/githat.ts
|
|
401
|
+
import { execSync } from "child_process";
|
|
400
402
|
import * as p5 from "@clack/prompts";
|
|
403
|
+
function openBrowser(url) {
|
|
404
|
+
try {
|
|
405
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
406
|
+
execSync(`${cmd} "${url}"`, { stdio: "ignore" });
|
|
407
|
+
} catch {
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function sleep(ms) {
|
|
411
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
412
|
+
}
|
|
413
|
+
async function deviceAuthFlow() {
|
|
414
|
+
const spinner2 = p5.spinner();
|
|
415
|
+
try {
|
|
416
|
+
spinner2.start("Requesting device code...");
|
|
417
|
+
const codeRes = await fetch(`${DEFAULT_API_URL}/auth/device/code`, {
|
|
418
|
+
method: "POST",
|
|
419
|
+
headers: { "Content-Type": "application/json" },
|
|
420
|
+
body: JSON.stringify({ client_name: "create-githat-app" })
|
|
421
|
+
});
|
|
422
|
+
if (!codeRes.ok) {
|
|
423
|
+
spinner2.stop("Failed to get device code");
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
const codeData = await codeRes.json();
|
|
427
|
+
spinner2.stop("Device code generated");
|
|
428
|
+
p5.note(
|
|
429
|
+
`Code: ${codeData.user_code}
|
|
430
|
+
|
|
431
|
+
Opening browser to complete sign-in...
|
|
432
|
+
If it doesn't open, visit: ${codeData.verification_uri_complete}`,
|
|
433
|
+
"Authorize Device"
|
|
434
|
+
);
|
|
435
|
+
openBrowser(codeData.verification_uri_complete);
|
|
436
|
+
spinner2.start("Waiting for browser authorization...");
|
|
437
|
+
const expiresAt = Date.now() + codeData.expires_in * 1e3;
|
|
438
|
+
const pollInterval = (codeData.interval || 5) * 1e3;
|
|
439
|
+
while (Date.now() < expiresAt) {
|
|
440
|
+
await sleep(pollInterval);
|
|
441
|
+
const tokenRes = await fetch(`${DEFAULT_API_URL}/auth/device/token`, {
|
|
442
|
+
method: "POST",
|
|
443
|
+
headers: { "Content-Type": "application/json" },
|
|
444
|
+
body: JSON.stringify({ device_code: codeData.device_code })
|
|
445
|
+
});
|
|
446
|
+
const tokenData = await tokenRes.json();
|
|
447
|
+
if (tokenData.error === "authorization_pending") {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (tokenData.error === "expired_token") {
|
|
451
|
+
spinner2.stop("Device code expired");
|
|
452
|
+
p5.log.error("Authorization timed out. Please try again.");
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
if (tokenData.publishable_key) {
|
|
456
|
+
spinner2.stop("Authorized!");
|
|
457
|
+
p5.log.success(`Connected to ${tokenData.app_name || "your app"} (${tokenData.org_name || "your org"})`);
|
|
458
|
+
return tokenData.publishable_key;
|
|
459
|
+
}
|
|
460
|
+
spinner2.stop("Authorization failed");
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
spinner2.stop("Timed out");
|
|
464
|
+
p5.log.error("Authorization timed out. Please try again.");
|
|
465
|
+
return null;
|
|
466
|
+
} catch (err) {
|
|
467
|
+
spinner2.stop("Connection error");
|
|
468
|
+
p5.log.error("Failed to connect to GitHat API. Check your internet connection.");
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
401
472
|
async function promptGitHat(existingKey) {
|
|
402
473
|
let publishableKey = existingKey || "";
|
|
403
474
|
if (!publishableKey) {
|
|
@@ -405,6 +476,7 @@ async function promptGitHat(existingKey) {
|
|
|
405
476
|
message: "Connect to GitHat",
|
|
406
477
|
options: [
|
|
407
478
|
{ value: "skip", label: "Skip for now", hint: "auth works on localhost \u2014 add key later" },
|
|
479
|
+
{ value: "browser", label: "Sign in with browser", hint: "opens githat.io to authorize" },
|
|
408
480
|
{ value: "paste", label: "I have a key", hint: "paste your pk_live_... key" }
|
|
409
481
|
]
|
|
410
482
|
});
|
|
@@ -412,7 +484,14 @@ async function promptGitHat(existingKey) {
|
|
|
412
484
|
p5.cancel("Setup cancelled.");
|
|
413
485
|
process.exit(0);
|
|
414
486
|
}
|
|
415
|
-
if (connectChoice === "
|
|
487
|
+
if (connectChoice === "browser") {
|
|
488
|
+
const key = await deviceAuthFlow();
|
|
489
|
+
if (key) {
|
|
490
|
+
publishableKey = key;
|
|
491
|
+
} else {
|
|
492
|
+
p5.log.warn("Authorization failed. Continuing without key...");
|
|
493
|
+
}
|
|
494
|
+
} else if (connectChoice === "paste") {
|
|
416
495
|
const pastedKey = await p5.text({
|
|
417
496
|
message: "Publishable key",
|
|
418
497
|
placeholder: `pk_live_... (get one at ${DASHBOARD_URL})`,
|
|
@@ -535,9 +614,10 @@ async function runPrompts(args) {
|
|
|
535
614
|
const project = await promptProject(args.initialName);
|
|
536
615
|
const projectType = await promptProjectType();
|
|
537
616
|
sectionHeader("Stack");
|
|
538
|
-
const
|
|
617
|
+
const isFullstack = projectType.projectType === "fullstack";
|
|
618
|
+
const framework = await promptFramework(args.typescript, isFullstack);
|
|
539
619
|
let backend = {};
|
|
540
|
-
if (
|
|
620
|
+
if (isFullstack) {
|
|
541
621
|
backend = await promptBackend();
|
|
542
622
|
}
|
|
543
623
|
sectionHeader("Connect");
|
|
@@ -579,7 +659,7 @@ function answersToContext(answers) {
|
|
|
579
659
|
// src/scaffold/index.ts
|
|
580
660
|
import fs3 from "fs-extra";
|
|
581
661
|
import path3 from "path";
|
|
582
|
-
import { execSync as
|
|
662
|
+
import { execSync as execSync3 } from "child_process";
|
|
583
663
|
import * as p9 from "@clack/prompts";
|
|
584
664
|
import chalk2 from "chalk";
|
|
585
665
|
|
|
@@ -701,25 +781,25 @@ function createSpinner(text4) {
|
|
|
701
781
|
return ora({ text: text4, color: "magenta" });
|
|
702
782
|
}
|
|
703
783
|
async function withSpinner(text4, fn, successText) {
|
|
704
|
-
const
|
|
705
|
-
|
|
784
|
+
const spinner2 = createSpinner(text4);
|
|
785
|
+
spinner2.start();
|
|
706
786
|
try {
|
|
707
787
|
const result = await fn();
|
|
708
|
-
|
|
788
|
+
spinner2.succeed(successText || text4);
|
|
709
789
|
return result;
|
|
710
790
|
} catch (err) {
|
|
711
|
-
|
|
791
|
+
spinner2.fail(text4);
|
|
712
792
|
throw err;
|
|
713
793
|
}
|
|
714
794
|
}
|
|
715
795
|
|
|
716
796
|
// src/utils/git.ts
|
|
717
|
-
import { execSync } from "child_process";
|
|
797
|
+
import { execSync as execSync2 } from "child_process";
|
|
718
798
|
function initGit(cwd) {
|
|
719
799
|
try {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
800
|
+
execSync2("git init", { cwd, stdio: "ignore" });
|
|
801
|
+
execSync2("git add -A", { cwd, stdio: "ignore" });
|
|
802
|
+
execSync2('git commit -m "Initial commit from create-githat-app"', {
|
|
723
803
|
cwd,
|
|
724
804
|
stdio: "ignore"
|
|
725
805
|
});
|
|
@@ -776,7 +856,7 @@ async function scaffold(context, options) {
|
|
|
776
856
|
`Installing dependencies with ${context.packageManager}...`,
|
|
777
857
|
async () => {
|
|
778
858
|
try {
|
|
779
|
-
|
|
859
|
+
execSync3(installCmd, { cwd: root, stdio: "ignore", timeout: 12e4 });
|
|
780
860
|
} catch (err) {
|
|
781
861
|
const msg = err.message || "";
|
|
782
862
|
if (msg.includes("TIMEOUT")) {
|
|
@@ -799,7 +879,7 @@ async function scaffold(context, options) {
|
|
|
799
879
|
if (!p9.isCancel(starPrompt) && starPrompt) {
|
|
800
880
|
try {
|
|
801
881
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
802
|
-
|
|
882
|
+
execSync3(`${cmd} "https://github.com/GitHat-IO/githat"`, { stdio: "ignore" });
|
|
803
883
|
} catch {
|
|
804
884
|
p9.log.info("Visit https://github.com/GitHat-IO/githat to star us!");
|
|
805
885
|
}
|