create-githat-app 1.7.0 → 1.8.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.
Files changed (63) hide show
  1. package/dist/cli.js +3 -3
  2. package/package.json +6 -2
  3. package/templates/agent/app/(auth)/forgot-password/page.tsx.hbs +11 -0
  4. package/templates/agent/app/(auth)/reset-password/page.tsx.hbs +39 -0
  5. package/templates/agent/app/(auth)/verify-email/page.tsx.hbs +41 -0
  6. package/templates/agent/app/admin/agent/page.tsx.hbs +159 -62
  7. package/templates/agent/app/admin/layout.tsx.hbs +82 -0
  8. package/templates/agent/app/admin/mcp/page.tsx.hbs +156 -0
  9. package/templates/agent/app/dashboard/agents/page.tsx.hbs +9 -0
  10. package/templates/agent/app/dashboard/layout.tsx.hbs +9 -0
  11. package/templates/agent/app/dashboard/mcp/page.tsx.hbs +9 -0
  12. package/templates/agent/app/dashboard/page.tsx.hbs +128 -0
  13. package/templates/agent/app/globals.css.hbs +14 -9
  14. package/templates/agent/app/layout.tsx.hbs +7 -2
  15. package/templates/agent/app/page.tsx.hbs +127 -70
  16. package/templates/agent/app/verify/agent/page.tsx.hbs +124 -0
  17. package/templates/agent/next.config.ts.hbs +4 -5
  18. package/templates/agent/public/HERO_IMAGE.md +23 -0
  19. package/templates/base/githat/api/agents.ts.hbs +6 -6
  20. package/templates/base/githat/config.ts.hbs +7 -9
  21. package/templates/base/githat/dashboard/overview.tsx.hbs +106 -16
  22. package/templates/classroom/app/layout.tsx.hbs +6 -1
  23. package/templates/classroom/next.config.ts.hbs +4 -5
  24. package/templates/content/app/layout.tsx.hbs +6 -1
  25. package/templates/content/next.config.ts.hbs +4 -5
  26. package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +2 -2
  27. package/templates/dashboard/app/layout.tsx.hbs +6 -1
  28. package/templates/dashboard/next.config.ts.hbs +4 -5
  29. package/templates/fullstack/apps-web-nextjs/app/layout.tsx.hbs +6 -1
  30. package/templates/fullstack/apps-web-nextjs/next.config.ts.hbs +5 -5
  31. package/templates/marketplace/app/layout.tsx.hbs +6 -1
  32. package/templates/marketplace/next.config.ts.hbs +4 -5
  33. package/templates/nextjs/app/(auth)/forgot-password/page.tsx.hbs +2 -54
  34. package/templates/nextjs/app/(auth)/reset-password/page.tsx.hbs +8 -75
  35. package/templates/nextjs/app/layout.tsx.hbs +6 -1
  36. package/templates/nextjs/next.config.ts.hbs +4 -5
  37. package/templates/plain/app/layout.tsx.hbs +6 -1
  38. package/templates/plain/next.config.ts.hbs +4 -5
  39. package/templates/portfolio/app/layout.tsx.hbs +6 -1
  40. package/templates/portfolio/next.config.ts.hbs +4 -5
  41. package/templates/saas/app/layout.tsx.hbs +6 -1
  42. package/templates/saas/next.config.ts.hbs +4 -5
  43. package/templates/agent/app/api/githat/[...path]/route.ts.hbs +0 -21
  44. package/templates/agent/proxy.ts.hbs +0 -10
  45. package/templates/classroom/app/api/githat/[...path]/route.ts.hbs +0 -21
  46. package/templates/classroom/proxy.ts.hbs +0 -10
  47. package/templates/content/app/api/githat/[...path]/route.ts.hbs +0 -21
  48. package/templates/content/proxy.ts.hbs +0 -10
  49. package/templates/dashboard/app/api/githat/[...path]/route.ts.hbs +0 -21
  50. package/templates/dashboard/proxy.ts.hbs +0 -10
  51. package/templates/fullstack/apps-web-nextjs/app/api/githat/[...path]/route.ts.hbs +0 -21
  52. package/templates/marketplace/app/(shop)/[slug]/p/[productId]/page.tsx.hbs +0 -99
  53. package/templates/marketplace/app/(shop)/[slug]/page.tsx.hbs +0 -90
  54. package/templates/marketplace/app/api/githat/[...path]/route.ts.hbs +0 -21
  55. package/templates/marketplace/proxy.ts.hbs +0 -10
  56. package/templates/nextjs/app/api/githat/[...path]/route.ts.hbs +0 -21
  57. package/templates/nextjs/proxy.ts.hbs +0 -10
  58. package/templates/plain/app/api/githat/[...path]/route.ts.hbs +0 -21
  59. package/templates/plain/proxy.ts.hbs +0 -10
  60. package/templates/portfolio/app/api/githat/[...path]/route.ts.hbs +0 -21
  61. package/templates/portfolio/proxy.ts.hbs +0 -10
  62. package/templates/saas/app/api/githat/[...path]/route.ts.hbs +0 -21
  63. package/templates/saas/proxy.ts.hbs +0 -10
@@ -0,0 +1,9 @@
1
+ {{#if includeAgentModule}}
2
+ {{#if includeGithatFolder}}
3
+ import { DashboardAgents } from '../../../githat/dashboard/agents{{#unless typescript}}.jsx{{/unless}}';
4
+
5
+ export default function AgentsPage() {
6
+ return <DashboardAgents />;
7
+ }
8
+ {{/if}}
9
+ {{/if}}
@@ -0,0 +1,9 @@
1
+ {{#if includeDashboard}}
2
+ {{#if includeGithatFolder}}
3
+ import { DashboardLayout } from '../../githat/dashboard/layout{{#unless typescript}}.jsx{{/unless}}';
4
+
5
+ export default function Layout({ children }{{#if typescript}}: { children: React.ReactNode }{{/if}}) {
6
+ return <DashboardLayout>{children}</DashboardLayout>;
7
+ }
8
+ {{/if}}
9
+ {{/if}}
@@ -0,0 +1,9 @@
1
+ {{#if includeMcpModule}}
2
+ {{#if includeGithatFolder}}
3
+ import { DashboardMcpServers } from '../../../githat/dashboard/mcp-servers{{#unless typescript}}.jsx{{/unless}}';
4
+
5
+ export default function McpPage() {
6
+ return <DashboardMcpServers />;
7
+ }
8
+ {{/if}}
9
+ {{/if}}
@@ -0,0 +1,128 @@
1
+ {{#if includeDashboard}}
2
+ 'use client';
3
+
4
+ import { useEffect, useState } from 'react';
5
+ import Link from 'next/link';
6
+ import { useAuth } from '@githat/nextjs';
7
+ import { githatApi } from '../../../githat/api/client{{#unless typescript}}.js{{/unless}}';
8
+ {{#if typescript}}
9
+ interface AgentSummary {
10
+ total: number;
11
+ active: number;
12
+ actionsLast24h: number;
13
+ mcpServers: number;
14
+ }
15
+ {{/if}}
16
+
17
+ export default function DashboardPage() {
18
+ const { user, org } = useAuth();
19
+ const [summary, setSummary] = useState{{#if typescript}}<AgentSummary>{{/if}}({
20
+ total: 0,
21
+ active: 0,
22
+ actionsLast24h: 0,
23
+ mcpServers: 0,
24
+ });
25
+
26
+ useEffect(() => {
27
+ githatApi.get{{#if typescript}}<AgentSummary>{{/if}}('/agents/summary')
28
+ .then((data) => setSummary(data))
29
+ .catch(() => {});
30
+ }, []);
31
+
32
+ return (
33
+ <div>
34
+ <div style=\{{ marginBottom: '2rem' }}>
35
+ <h1 style=\{{ fontSize: '1.5rem', fontWeight: 700, color: '#fafafa', marginBottom: '0.375rem' }}>
36
+ Welcome back{user?.name ? `, ${user.name}` : ''}
37
+ </h1>
38
+ {org && (
39
+ <p style=\{{ color: '#71717a', fontSize: '0.875rem' }}>
40
+ {org.name} · <span style=\{{ color: '#a1a1aa' }}>{org.role}</span>
41
+ </p>
42
+ )}
43
+ </div>
44
+
45
+ {/* Stats row */}
46
+ <div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(14rem, 1fr))', gap: '1rem', marginBottom: '2.5rem' }}>
47
+ {[
48
+ { label: 'Total agents', value: summary.total, sub: 'Registered' },
49
+ { label: 'Active agents', value: summary.active, sub: 'Running now' },
50
+ { label: 'Actions (24h)', value: summary.actionsLast24h, sub: 'Across all agents' },
51
+ { label: 'MCP servers', value: summary.mcpServers, sub: `Plan: ${org?.tier ?? 'Free'}` },
52
+ ].map(({ label, value, sub }) => (
53
+ <div key={label} style=\{{
54
+ background: '#111113',
55
+ border: '1px solid #1e1e2e',
56
+ borderRadius: '0.75rem',
57
+ padding: '1.5rem',
58
+ }}>
59
+ <p style=\{{ fontSize: '0.8125rem', color: '#71717a', marginBottom: '0.375rem' }}>{label}</p>
60
+ <p style=\{{ fontSize: '1.5rem', fontWeight: 700, color: '#fafafa', marginBottom: '0.25rem' }}>{value}</p>
61
+ <p style=\{{ fontSize: '0.75rem', color: '#52525b' }}>{sub}</p>
62
+ </div>
63
+ ))}
64
+ </div>
65
+
66
+ {/* Quick actions */}
67
+ <div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr))', gap: '1rem' }}>
68
+ {[
69
+ {
70
+ title: 'Register an AI agent',
71
+ desc: 'Assign a wallet-bound identity to a new autonomous agent. Capability-scoped, kill-switchable, fully audited.',
72
+ cta: 'Register agent',
73
+ href: '/dashboard/agents',
74
+ accent: '#6366f1',
75
+ },
76
+ {
77
+ title: 'Connect an MCP server',
78
+ desc: 'Grant your agents access to new tools by connecting a Model Context Protocol server.',
79
+ cta: 'Connect server',
80
+ href: '/dashboard/mcp',
81
+ accent: '#0ea5e9',
82
+ },
83
+ {
84
+ title: 'Invite team members',
85
+ desc: 'Add collaborators who can monitor agents, adjust capabilities, or trigger the kill switch.',
86
+ cta: 'Invite',
87
+ href: '/dashboard/members',
88
+ accent: '#10b981',
89
+ },
90
+ {
91
+ title: 'Agent operator console',
92
+ desc: 'View live status, adjust capabilities, and hit the kill switch for a specific agent.',
93
+ cta: 'Open console',
94
+ href: '/admin/agent',
95
+ accent: '#f59e0b',
96
+ },
97
+ ].map(({ title, desc, cta, href, accent }) => (
98
+ <div key={title} style=\{{
99
+ background: '#111113',
100
+ border: '1px solid #1e1e2e',
101
+ borderRadius: '0.75rem',
102
+ padding: '1.5rem',
103
+ display: 'flex',
104
+ flexDirection: 'column',
105
+ gap: '0.75rem',
106
+ }}>
107
+ <h3 style=\{{ fontSize: '0.9375rem', fontWeight: 600, color: '#fafafa' }}>{title}</h3>
108
+ <p style=\{{ fontSize: '0.875rem', color: '#71717a', lineHeight: 1.6, flex: 1 }}>{desc}</p>
109
+ <Link href={href} style=\{{
110
+ display: 'inline-block',
111
+ background: accent,
112
+ color: '#fff',
113
+ textDecoration: 'none',
114
+ padding: '0.5rem 1rem',
115
+ borderRadius: '0.375rem',
116
+ fontSize: '0.875rem',
117
+ fontWeight: 600,
118
+ alignSelf: 'flex-start',
119
+ }}>
120
+ {cta} →
121
+ </Link>
122
+ </div>
123
+ ))}
124
+ </div>
125
+ </div>
126
+ );
127
+ }
128
+ {{/if}}
@@ -39,21 +39,26 @@
39
39
  --danger: #dc2626;
40
40
 
41
41
  /* Spacing — used by @githat/nextjs/styles */
42
- --space-1: 0.25rem;
43
- --space-2: 0.5rem;
44
- --space-3: 0.75rem;
45
- --space-4: 1rem;
46
- --space-6: 1.5rem;
47
- --space-8: 2rem;
42
+ --space-1: 0.25rem;
43
+ --space-2: 0.5rem;
44
+ --space-3: 0.75rem;
45
+ --space-4: 1rem;
46
+ --space-5: 1.25rem;
47
+ --space-6: 1.5rem;
48
+ --space-8: 2rem;
49
+ --space-10: 2.5rem;
50
+ --space-12: 3rem;
48
51
 
49
52
  /* Radius */
50
- --radius: 0.5rem;
51
- --radius-md: 0.5rem;
52
- --radius-lg: 0.75rem;
53
+ --radius: 0.5rem;
54
+ --radius-md: 0.5rem;
55
+ --radius-lg: 0.75rem;
56
+ --radius-full: 9999px;
53
57
 
54
58
  /* Fonts */
55
59
  --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
56
60
  --font-wordmark: 'Instrument Serif', Georgia, serif;
61
+ --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
57
62
  }
58
63
 
59
64
  @media (prefers-color-scheme: dark) {
@@ -19,10 +19,15 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
19
19
  */}
20
20
  <GitHatProvider config=\{{
21
21
  publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
22
- apiUrl: '/api/githat',
22
+ apiUrl: 'https://api.githat.io',
23
+ {{#if typescript}}
24
+ tokenStorage: 'localStorage' as const,
25
+ {{else}}
26
+ tokenStorage: 'localStorage',
27
+ {{/if}}
23
28
  signInUrl: '/sign-in',
24
29
  signUpUrl: '/sign-up',
25
- afterSignInUrl: '/',
30
+ afterSignInUrl: '/dashboard',
26
31
  afterSignOutUrl: '/',
27
32
  }}>
28
33
  <header style=\{{
@@ -4,90 +4,52 @@ import Link from 'next/link';
4
4
  import { useAuth } from '@githat/nextjs';
5
5
 
6
6
  /**
7
- * AI Agent template Web4 identity, MCP, public verification.
7
+ * Landing pagesplit-hero layout.
8
8
  *
9
- * The pitch: every other auth platform stops at humans. GitHat
10
- * extends identity to autonomous agents wallet-bound (Ethereum
11
- * sigs), capability-scoped, kill-switchable, audit-logged. Anyone
12
- * can verify an agent at /verify/agent/<wallet>.
9
+ * Left: pitch + CTA
10
+ * Right: your hero image or logo (drop a file in public/ and swap
11
+ * <HeroImagePlaceholder /> for <Image src="/your-image.png" ... />).
13
12
  *
14
- * This template is the agent operator's dashboard, plus the public
15
- * /verify endpoint anyone can hit to check an agent's status.
13
+ * The <Image> tag from next/image is already imported so the swap is
14
+ * one line. See public/HERO_IMAGE.md for full instructions.
16
15
  */
17
- export default function Home() {
18
- const { isSignedIn } = useAuth();
19
16
 
17
+ function HeroImagePlaceholder() {
20
18
  return (
21
- <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
22
- <section style=\{{
23
- padding: 'var(--space-12) var(--space-4)',
24
- textAlign: 'center',
25
- maxWidth: '40rem',
26
- margin: '0 auto',
27
- }}>
28
- <h1 style=\{{
29
- fontFamily: 'var(--font-wordmark, Georgia, serif)',
30
- fontSize: 'clamp(2rem, 5vw, 3rem)',
31
- lineHeight: 1.1,
32
- marginBottom: 'var(--space-3)',
33
- }}>
34
- {{businessName}}
35
- </h1>
36
- <p style=\{{ color: 'var(--fg-muted)', fontSize: '1.125rem', marginBottom: 'var(--space-6)' }}>
37
- Wallet-bound identity for autonomous AI agents. Capability
38
- scoping, kill switch, audit trail — and a public /verify
39
- endpoint anyone can hit.
40
- </p>
41
- {!isSignedIn ? (
42
- <Link href="/sign-up" style=\{{
43
- display: 'inline-block',
44
- padding: 'var(--space-3) var(--space-6)',
45
- borderRadius: 'var(--radius-md, 0.5rem)',
46
- background: 'var(--primary)',
47
- color: 'var(--bg)',
48
- fontWeight: 600,
49
- textDecoration: 'none',
50
- }}>
51
- Register an agent →
52
- </Link>
53
- ) : (
54
- <Link href="/admin/agent" style=\{{
55
- display: 'inline-block',
56
- padding: 'var(--space-3) var(--space-6)',
57
- borderRadius: 'var(--radius-md, 0.5rem)',
58
- background: 'var(--primary)',
59
- color: 'var(--bg)',
60
- fontWeight: 600,
61
- textDecoration: 'none',
62
- }}>
63
- Open agent dashboard →
64
- </Link>
65
- )}
66
- </section>
67
-
68
- <section style=\{{ padding: 'var(--space-8) var(--space-4)', background: 'var(--surface-sub)' }}>
69
- <div style=\{{ maxWidth: '48rem', margin: '0 auto' }}>
70
- <h2 style=\{{ fontSize: '1.5rem', marginBottom: 'var(--space-4)' }}>What you get</h2>
71
- <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
72
- <Item emoji="🪪" title="Wallet-bound identity" body="Ethereum-style key pair. The agent signs requests; clients verify the signature. No shared secrets to leak." />
73
- <Item emoji="🎚" title="Capability scopes" body="Each agent can only call the tools you explicitly granted. Add or revoke at any time." />
74
- <Item emoji="🛑" title="Kill switch" body="One click and the agent is revoked everywhere. Existing tokens stop working immediately." />
75
- <Item emoji="📜" title="Audit log" body="Every action is logged with timestamp, capability used, and signature. Prove what happened." />
76
- <Item emoji="🔍" title="Public verification" body="Anyone can hit githat.io/verify/agent/<wallet> and see the agent's status, capabilities, and recent actions." />
77
- </ul>
78
- </div>
79
- </section>
19
+ <div style=\{{
20
+ position: 'relative',
21
+ width: '100%',
22
+ aspectRatio: '4 / 3',
23
+ borderRadius: '1rem',
24
+ border: '2px dashed var(--border)',
25
+ background: 'var(--surface)',
26
+ display: 'flex',
27
+ flexDirection: 'column',
28
+ alignItems: 'center',
29
+ justifyContent: 'center',
30
+ gap: '0.75rem',
31
+ color: 'var(--fg-muted)',
32
+ }}>
33
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden>
34
+ <rect x="3" y="3" width="18" height="18" rx="2" />
35
+ <circle cx="8.5" cy="8.5" r="1.5" />
36
+ <path d="m21 15-5-5L5 21" />
37
+ </svg>
38
+ <p style=\{{ fontSize: '0.8125rem', textAlign: 'center', lineHeight: 1.5, maxWidth: '16rem' }}>
39
+ Drop your hero image in <code style=\{{ fontFamily: 'var(--font-mono)' }}>public/</code>
40
+ <br />See <code style=\{{ fontFamily: 'var(--font-mono)' }}>public/HERO_IMAGE.md</code>
41
+ </p>
80
42
  </div>
81
43
  );
82
44
  }
83
45
 
84
- function Item({ emoji, title, body }: { emoji: string; title: string; body: string }) {
46
+ function FeatureItem({ emoji, title, body }{{#if typescript}}: { emoji: string; title: string; body: string }{{/if}}) {
85
47
  return (
86
48
  <li style=\{{
87
49
  display: 'flex',
88
50
  gap: 'var(--space-4)',
89
51
  padding: 'var(--space-4)',
90
- borderRadius: 'var(--radius-md, 0.5rem)',
52
+ borderRadius: 'var(--radius-md)',
91
53
  background: 'var(--surface)',
92
54
  }}>
93
55
  <span style=\{{ fontSize: '1.5rem' }} aria-hidden>{emoji}</span>
@@ -98,3 +60,98 @@ function Item({ emoji, title, body }: { emoji: string; title: string; body: stri
98
60
  </li>
99
61
  );
100
62
  }
63
+
64
+ export default function Home() {
65
+ const { isSignedIn } = useAuth();
66
+
67
+ return (
68
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
69
+
70
+ {/* Hero — split layout */}
71
+ <section style=\{{
72
+ display: 'grid',
73
+ gridTemplateColumns: 'repeat(auto-fit, minmax(18rem, 1fr))',
74
+ gap: 'var(--space-12)',
75
+ alignItems: 'center',
76
+ padding: 'var(--space-12) var(--space-6)',
77
+ maxWidth: '72rem',
78
+ margin: '0 auto',
79
+ }}>
80
+ {/* Left — pitch */}
81
+ <div>
82
+ <h1 style=\{{
83
+ fontFamily: 'var(--font-wordmark)',
84
+ fontSize: 'clamp(2rem, 5vw, 3.25rem)',
85
+ lineHeight: 1.1,
86
+ marginBottom: 'var(--space-4)',
87
+ }}>
88
+ {{businessName}}
89
+ </h1>
90
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '1.125rem', lineHeight: 1.6, marginBottom: 'var(--space-6)', maxWidth: '32rem' }}>
91
+ Wallet-bound identity for autonomous AI agents. Capability
92
+ scoping, kill switch, audit trail — and a public{' '}
93
+ <code style=\{{ fontSize: '0.9em', fontFamily: 'var(--font-mono)' }}>/verify</code>{' '}
94
+ endpoint anyone can hit.
95
+ </p>
96
+ {!isSignedIn ? (
97
+ <div style=\{{ display: 'flex', gap: 'var(--space-3)', flexWrap: 'wrap' }}>
98
+ <Link href="/sign-up" style=\{{
99
+ display: 'inline-block',
100
+ padding: 'var(--space-3) var(--space-6)',
101
+ borderRadius: 'var(--radius-md)',
102
+ background: 'var(--primary)',
103
+ color: '#fff',
104
+ fontWeight: 600,
105
+ textDecoration: 'none',
106
+ }}>
107
+ Register an agent →
108
+ </Link>
109
+ <Link href="/sign-in" style=\{{
110
+ display: 'inline-block',
111
+ padding: 'var(--space-3) var(--space-6)',
112
+ borderRadius: 'var(--radius-md)',
113
+ border: '1px solid var(--border)',
114
+ color: 'var(--fg)',
115
+ fontWeight: 600,
116
+ textDecoration: 'none',
117
+ }}>
118
+ Sign in
119
+ </Link>
120
+ </div>
121
+ ) : (
122
+ <Link href="/dashboard" style=\{{
123
+ display: 'inline-block',
124
+ padding: 'var(--space-3) var(--space-6)',
125
+ borderRadius: 'var(--radius-md)',
126
+ background: 'var(--primary)',
127
+ color: '#fff',
128
+ fontWeight: 600,
129
+ textDecoration: 'none',
130
+ }}>
131
+ Open dashboard →
132
+ </Link>
133
+ )}
134
+ </div>
135
+
136
+ {/* Right — hero image */}
137
+ <div>
138
+ <HeroImagePlaceholder />
139
+ </div>
140
+ </section>
141
+
142
+ {/* Features */}
143
+ <section style=\{{ padding: 'var(--space-12) var(--space-6)', background: 'var(--surface-sub)' }}>
144
+ <div style=\{{ maxWidth: '52rem', margin: '0 auto' }}>
145
+ <h2 style=\{{ fontSize: '1.5rem', marginBottom: 'var(--space-6)' }}>What you get</h2>
146
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
147
+ <FeatureItem emoji="🪪" title="Wallet-bound identity" body="Ethereum-style key pair. The agent signs requests; clients verify the signature. No shared secrets." />
148
+ <FeatureItem emoji="🎚" title="Capability scopes" body="Each agent can only call the tools you explicitly granted. Add or revoke at any time." />
149
+ <FeatureItem emoji="🛑" title="Kill switch" body="One click and the agent is revoked everywhere. Existing tokens stop working immediately." />
150
+ <FeatureItem emoji="📜" title="Audit log" body="Every action is logged with timestamp, capability used, and signature." />
151
+ <FeatureItem emoji="🔍" title="Public verification" body="Anyone can hit /verify/agent/<wallet> and see the agent's current status and capabilities." />
152
+ </ul>
153
+ </div>
154
+ </section>
155
+ </div>
156
+ );
157
+ }
@@ -0,0 +1,124 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Public agent verification page.
5
+ * Anyone can visit /verify/agent?wallet=<wallet> to confirm an agent's
6
+ * identity, check its current status, and review its capabilities.
7
+ *
8
+ * Wallet is a query param (not a path) so this is a single static page
9
+ * that handles any wallet — output: 'export' can't pre-render an unbounded
10
+ * dynamic-route param space.
11
+ */
12
+ import { Suspense, useEffect, useState } from 'react';
13
+ import { useSearchParams } from 'next/navigation';
14
+
15
+ const GITHAT_API = 'https://api.githat.io';
16
+
17
+ {{#if typescript}}
18
+ interface AgentVerify {
19
+ verified: boolean;
20
+ name: string;
21
+ wallet: string;
22
+ chainId: number;
23
+ status: 'active' | 'paused' | 'revoked' | 'pending';
24
+ verifiedSince: string;
25
+ lastActivity: string;
26
+ capabilities: string[];
27
+ actionsLast24h: number;
28
+ verificationUrl: string;
29
+ }
30
+ {{/if}}
31
+
32
+ function VerifyAgentContent() {
33
+ const searchParams = useSearchParams();
34
+ const wallet = searchParams.get('wallet') ?? '';
35
+ const [agent, setAgent] = useState{{#if typescript}}<AgentVerify | null>{{/if}}(null);
36
+ const [loading, setLoading] = useState(true);
37
+
38
+ useEffect(() => {
39
+ if (!wallet) { setLoading(false); return; }
40
+ let cancelled = false;
41
+ fetch(`${GITHAT_API}/verify/agent/${wallet}`)
42
+ .then((r) => (r.ok ? r.json() : null))
43
+ .then((data) => { if (!cancelled) setAgent(data); })
44
+ .catch(() => { if (!cancelled) setAgent(null); })
45
+ .finally(() => { if (!cancelled) setLoading(false); });
46
+ return () => { cancelled = true; };
47
+ }, [wallet]);
48
+
49
+ if (loading) {
50
+ return (
51
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#a1a1aa', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
52
+ <p style=\{{ fontSize: '0.875rem' }}>Loading…</p>
53
+ </main>
54
+ );
55
+ }
56
+
57
+ if (!agent?.verified) {
58
+ return (
59
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#fafafa', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '4rem 1rem' }}>
60
+ <div style=\{{ textAlign: 'center' }}>
61
+ <p style=\{{ fontSize: '1.125rem', color: '#a1a1aa', marginBottom: '0.5rem' }}>Agent not found</p>
62
+ <p style=\{{ fontSize: '0.875rem', color: '#52525b', fontFamily: "'JetBrains Mono', monospace", wordBreak: 'break-all' }}>{wallet || '(no wallet provided)'}</p>
63
+ </div>
64
+ </main>
65
+ );
66
+ }
67
+
68
+ const statusColor = ({
69
+ active: '#16a34a',
70
+ paused: '#d97706',
71
+ revoked: '#dc2626',
72
+ pending: '#6366f1',
73
+ }{{#if typescript}} as Record<string, string>{{/if}})[agent.status] ?? '#71717a';
74
+
75
+ return (
76
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#fafafa', display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '4rem 1rem' }}>
77
+ <div style=\{{ width: '100%', maxWidth: '36rem' }}>
78
+ <div style=\{{ marginBottom: '2rem', textAlign: 'center' }}>
79
+ <div style=\{{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem', padding: '0.375rem 0.875rem', borderRadius: '9999px', background: statusColor + '22', border: `1px solid ${statusColor}44`, marginBottom: '1.5rem' }}>
80
+ <span style=\{{ width: '0.5rem', height: '0.5rem', borderRadius: '9999px', background: statusColor, display: 'inline-block' }} />
81
+ <span style=\{{ fontSize: '0.75rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: statusColor }}>{agent.status}</span>
82
+ </div>
83
+ <h1 style=\{{ fontSize: '1.75rem', fontWeight: 700, marginBottom: '0.5rem' }}>{agent.name}</h1>
84
+ <p style=\{{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem', color: '#71717a', wordBreak: 'break-all' }}>{agent.wallet}</p>
85
+ </div>
86
+
87
+ <div style=\{{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginBottom: '1.5rem' }}>
88
+ {[
89
+ { label: 'Actions (24h)', value: agent.actionsLast24h },
90
+ { label: 'Verified since', value: new Date(agent.verifiedSince).toLocaleDateString() },
91
+ ].map(({ label, value }) => (
92
+ <div key={label} style=\{{ padding: '1.25rem', borderRadius: '0.75rem', border: '1px solid #27272a', background: '#18181b' }}>
93
+ <div style=\{{ fontSize: '0.75rem', color: '#71717a', marginBottom: '0.375rem' }}>{label}</div>
94
+ <div style=\{{ fontSize: '1.125rem', fontWeight: 600 }}>{value}</div>
95
+ </div>
96
+ ))}
97
+ </div>
98
+
99
+ {agent.capabilities.length > 0 && (
100
+ <div style=\{{ padding: '1.25rem', borderRadius: '0.75rem', border: '1px solid #27272a', background: '#18181b', marginBottom: '1.5rem' }}>
101
+ <h2 style=\{{ fontSize: '0.875rem', fontWeight: 600, color: '#a1a1aa', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '1rem' }}>Capabilities</h2>
102
+ <div style=\{{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
103
+ {agent.capabilities.map((cap{{#if typescript}}: string{{/if}}) => (
104
+ <span key={cap} style=\{{ padding: '0.375rem 0.75rem', borderRadius: '0.375rem', border: '1px solid #3f3f46', fontSize: '0.8125rem', fontFamily: "'JetBrains Mono', monospace", color: '#a1a1aa' }}>{cap}</span>
105
+ ))}
106
+ </div>
107
+ </div>
108
+ )}
109
+ </div>
110
+ </main>
111
+ );
112
+ }
113
+
114
+ export default function VerifyAgentPage() {
115
+ return (
116
+ <Suspense fallback={
117
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#a1a1aa', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
118
+ <p style=\{{ fontSize: '0.875rem' }}>Loading…</p>
119
+ </main>
120
+ }>
121
+ <VerifyAgentContent />
122
+ </Suspense>
123
+ );
124
+ }
@@ -1,9 +1,8 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} output: 'standalone',
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
7
6
  };
8
7
 
9
8
  export default withGitHat(nextConfig);
@@ -0,0 +1,23 @@
1
+ # Hero Image
2
+
3
+ Drop your logo or hero image (`.png`, `.jpg`, `.webp`) in this folder.
4
+
5
+ Then in `app/page.tsx`, replace `<HeroImagePlaceholder />` with:
6
+
7
+ ```tsx
8
+ <Image
9
+ src="/your-image.png"
10
+ alt="{{businessName}}"
11
+ fill
12
+ style={{ objectFit: 'cover', borderRadius: '1rem' }}
13
+ priority
14
+ />
15
+ ```
16
+
17
+ Wrap it in a `position: 'relative'` container with the same `aspectRatio: '4 / 3'` as the placeholder.
18
+
19
+ The `Image` component from `next/image` is not yet imported — add it at the top of `page.tsx`:
20
+
21
+ ```tsx
22
+ import Image from 'next/image';
23
+ ```
@@ -5,13 +5,13 @@ import type { AiAgent } from './types';
5
5
  {{/if}}
6
6
 
7
7
  export const agentsApi = {
8
- register: (wallet{{#if typescript}}: string{{/if}}, chainId{{#if typescript}}: number{{/if}}, name{{#if typescript}}: string{{/if}}) =>
9
- githatApi.post{{#if typescript}}<{ agent: AiAgent }>{{/if}}('/agent/register', { wallet, chainId, name }),
8
+ register: (walletAddress{{#if typescript}}: string{{/if}}, chainId{{#if typescript}}: number{{/if}}, name{{#if typescript}}: string{{/if}}) =>
9
+ githatApi.post{{#if typescript}}<{ agent: AiAgent }>{{/if}}('/agent/register', { walletAddress, chainId, name }),
10
10
 
11
- challenge: (wallet{{#if typescript}}: string{{/if}}) =>
12
- githatApi.post{{#if typescript}}<{ nonce: string }>{{/if}}('/agent/challenge', { wallet }),
11
+ challenge: (walletAddress{{#if typescript}}: string{{/if}}) =>
12
+ githatApi.post{{#if typescript}}<{ nonce: string }>{{/if}}('/agent/challenge', { walletAddress }),
13
13
 
14
- getToken: (wallet{{#if typescript}}: string{{/if}}, signature{{#if typescript}}: string{{/if}}, nonce{{#if typescript}}: string{{/if}}) =>
15
- githatApi.post{{#if typescript}}<{ accessToken: string }>{{/if}}('/agent/token', { wallet, signature, nonce }),
14
+ getToken: (walletAddress{{#if typescript}}: string{{/if}}, signature{{#if typescript}}: string{{/if}}, nonce{{#if typescript}}: string{{/if}}) =>
15
+ githatApi.post{{#if typescript}}<{ accessToken: string }>{{/if}}('/agent/token', { walletAddress, signature, nonce }),
16
16
  };
17
17
  {{/if}}
@@ -1,15 +1,13 @@
1
1
  {{#if includeGithatFolder}}
2
2
  export const githatConfig = {
3
3
  appName: '{{businessName}}',
4
- publishableKey: {{#ifEquals framework "nextjs"}}process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY{{else}}import.meta.env.VITE_GITHAT_PUBLISHABLE_KEY{{/ifEquals}} || '',
5
- // For nextjs we point at the same-origin proxy mounted at
6
- // src/app/api/githat/[...path]/route.ts — that proxy forwards to the
7
- // real api.githat.io upstream and re-emits Set-Cookie on this app's
8
- // domain so cookies are visible to proxy.ts and getAuth(). For
9
- // react-vite (no Next.js API routes) we hit api.githat.io directly,
10
- // which only works for browser-token auth flows that don't depend on
11
- // cookies.
12
- apiUrl: {{#ifEquals framework "nextjs"}}process.env.NEXT_PUBLIC_GITHAT_API_URL || '/api/githat'{{else}}import.meta.env.VITE_GITHAT_API_URL || '{{apiUrl}}'{{/ifEquals}},
4
+ publishableKey: {{#ifNext framework}}process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY{{else}}import.meta.env.VITE_GITHAT_PUBLISHABLE_KEY{{/ifNext}} || '',
5
+ // For Next.js-shaped frameworks we point at the same-origin proxy
6
+ // mounted at app/api/githat/[...path]/route.ts — that proxy forwards
7
+ // to api.githat.io and re-emits Set-Cookie on this app's domain so
8
+ // cookies are visible to proxy.ts and getAuth(). For react-vite we
9
+ // hit api.githat.io directly (browser-token flows only).
10
+ apiUrl: {{#ifNext framework}}process.env.NEXT_PUBLIC_GITHAT_API_URL || '/api/githat'{{else}}import.meta.env.VITE_GITHAT_API_URL || '{{apiUrl}}'{{/ifNext}},
13
11
  signInUrl: '/sign-in',
14
12
  signUpUrl: '/sign-up',
15
13
  afterSignInUrl: '/dashboard',