sema-cli 0.1.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/README.md +78 -0
- package/experiments/spike-shell-stream/bin/analytics.js +209 -0
- package/experiments/spike-shell-stream/bin/sema.js +322 -0
- package/experiments/spike-shell-stream/bin/start.js +387 -0
- package/experiments/spike-shell-stream/mac-agent/agent.js +450 -0
- package/experiments/spike-shell-stream/mac-agent/analyzer.js +189 -0
- package/experiments/spike-shell-stream/mac-agent/analyzer.test.js +307 -0
- package/experiments/spike-shell-stream/mac-agent/session.js +38 -0
- package/experiments/spike-shell-stream/mobile-web/inbox.html +431 -0
- package/experiments/spike-shell-stream/mobile-web/index.html +1093 -0
- package/experiments/spike-shell-stream/mobile-web/landing.html +586 -0
- package/experiments/spike-shell-stream/mobile-web/pair.html +304 -0
- package/experiments/spike-shell-stream/relay-server/server.js +1085 -0
- package/experiments/spike-shell-stream/shared/crypto.js +138 -0
- package/experiments/spike-shell-stream/shared/crypto.test.js +350 -0
- package/package.json +52 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Sema — the signal between you and your agents</title>
|
|
7
|
+
<meta name="description" content="Mobile collaboration layer for AI coding agents. Get notified on your phone when your agent needs you, decide in 10 seconds, walk away." />
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
color-scheme: dark;
|
|
11
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
12
|
+
background: #0a0d10;
|
|
13
|
+
color: #e8e5dc;
|
|
14
|
+
--accent: #4ec9b0;
|
|
15
|
+
--accent-dim: #2a7a66;
|
|
16
|
+
--surface: #14181d;
|
|
17
|
+
--border: #2b3337;
|
|
18
|
+
--muted: #8a9099;
|
|
19
|
+
--max-w: 720px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
min-height: 100vh;
|
|
26
|
+
background: #0a0d10;
|
|
27
|
+
color: #e8e5dc;
|
|
28
|
+
line-height: 1.6;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
a { color: var(--accent); text-decoration: none; }
|
|
32
|
+
a:hover { text-decoration: underline; }
|
|
33
|
+
|
|
34
|
+
.container {
|
|
35
|
+
max-width: var(--max-w);
|
|
36
|
+
margin: 0 auto;
|
|
37
|
+
padding: 0 20px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Hero */
|
|
41
|
+
.hero {
|
|
42
|
+
text-align: center;
|
|
43
|
+
padding: 80px 20px 60px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.hero-tagline {
|
|
47
|
+
font-size: clamp(28px, 5vw, 44px);
|
|
48
|
+
font-weight: 800;
|
|
49
|
+
line-height: 1.2;
|
|
50
|
+
letter-spacing: -0.02em;
|
|
51
|
+
margin-bottom: 16px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.hero-tagline em {
|
|
55
|
+
font-style: normal;
|
|
56
|
+
color: var(--accent);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.hero-sub {
|
|
60
|
+
font-size: clamp(16px, 2.5vw, 20px);
|
|
61
|
+
color: var(--muted);
|
|
62
|
+
max-width: 540px;
|
|
63
|
+
margin: 0 auto 32px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.hero-cta {
|
|
67
|
+
display: inline-block;
|
|
68
|
+
background: var(--accent);
|
|
69
|
+
color: #0a0d10;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
font-size: 16px;
|
|
72
|
+
padding: 12px 32px;
|
|
73
|
+
border-radius: 8px;
|
|
74
|
+
transition: opacity 0.2s;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.hero-cta:hover {
|
|
78
|
+
opacity: 0.85;
|
|
79
|
+
text-decoration: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.hero-cta-secondary {
|
|
83
|
+
background: transparent;
|
|
84
|
+
border: 1px solid var(--border);
|
|
85
|
+
color: var(--muted);
|
|
86
|
+
font-size: 14px;
|
|
87
|
+
padding: 8px 20px;
|
|
88
|
+
margin-top: 12px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.hero-install {
|
|
92
|
+
margin-bottom: 12px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.install-cmd {
|
|
96
|
+
display: inline-block;
|
|
97
|
+
background: var(--surface);
|
|
98
|
+
border: 1px solid var(--border);
|
|
99
|
+
border-radius: 8px;
|
|
100
|
+
padding: 12px 24px;
|
|
101
|
+
font-family: "SF Mono", SFMono-Regular, ui-monospace, monospace;
|
|
102
|
+
font-size: clamp(14px, 2.5vw, 18px);
|
|
103
|
+
color: var(--accent);
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
transition: border-color 0.2s;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.install-cmd:hover {
|
|
109
|
+
border-color: var(--accent-dim);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.install-cmd::before {
|
|
113
|
+
content: "$ ";
|
|
114
|
+
color: var(--muted);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.hero-require {
|
|
118
|
+
color: var(--muted);
|
|
119
|
+
font-size: 13px;
|
|
120
|
+
margin-bottom: 8px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Section */
|
|
124
|
+
section {
|
|
125
|
+
padding: 60px 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.section-title {
|
|
129
|
+
font-size: 24px;
|
|
130
|
+
font-weight: 700;
|
|
131
|
+
text-align: center;
|
|
132
|
+
margin-bottom: 40px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* How it works */
|
|
136
|
+
.steps {
|
|
137
|
+
display: grid;
|
|
138
|
+
gap: 32px;
|
|
139
|
+
counter-reset: step;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.step {
|
|
143
|
+
display: flex;
|
|
144
|
+
gap: 20px;
|
|
145
|
+
align-items: flex-start;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.step-num {
|
|
149
|
+
flex-shrink: 0;
|
|
150
|
+
width: 40px;
|
|
151
|
+
height: 40px;
|
|
152
|
+
border-radius: 50%;
|
|
153
|
+
background: var(--accent-dim);
|
|
154
|
+
color: var(--accent);
|
|
155
|
+
font-weight: 700;
|
|
156
|
+
font-size: 18px;
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.step-title {
|
|
163
|
+
font-size: 18px;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
margin-bottom: 4px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.step-desc {
|
|
169
|
+
color: var(--muted);
|
|
170
|
+
font-size: 15px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Features */
|
|
174
|
+
.features {
|
|
175
|
+
display: grid;
|
|
176
|
+
grid-template-columns: 1fr 1fr;
|
|
177
|
+
gap: 20px;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@media (max-width: 520px) {
|
|
181
|
+
.features { grid-template-columns: 1fr; }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.feature-card {
|
|
185
|
+
background: var(--surface);
|
|
186
|
+
border: 1px solid var(--border);
|
|
187
|
+
border-radius: 12px;
|
|
188
|
+
padding: 24px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.feature-icon {
|
|
192
|
+
font-size: 28px;
|
|
193
|
+
margin-bottom: 12px;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.feature-title {
|
|
197
|
+
font-size: 16px;
|
|
198
|
+
font-weight: 600;
|
|
199
|
+
margin-bottom: 6px;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.feature-desc {
|
|
203
|
+
font-size: 14px;
|
|
204
|
+
color: var(--muted);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Signup */
|
|
208
|
+
.signup {
|
|
209
|
+
background: var(--surface);
|
|
210
|
+
border: 1px solid var(--border);
|
|
211
|
+
border-radius: 16px;
|
|
212
|
+
padding: 40px 32px;
|
|
213
|
+
text-align: center;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.signup-title {
|
|
217
|
+
font-size: 22px;
|
|
218
|
+
font-weight: 700;
|
|
219
|
+
margin-bottom: 8px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.signup-sub {
|
|
223
|
+
color: var(--muted);
|
|
224
|
+
margin-bottom: 28px;
|
|
225
|
+
font-size: 15px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.pricing-group {
|
|
229
|
+
margin-bottom: 24px;
|
|
230
|
+
text-align: left;
|
|
231
|
+
max-width: 360px;
|
|
232
|
+
margin-left: auto;
|
|
233
|
+
margin-right: auto;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.pricing-label {
|
|
237
|
+
font-size: 14px;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
margin-bottom: 10px;
|
|
240
|
+
display: block;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.pricing-options {
|
|
244
|
+
display: grid;
|
|
245
|
+
grid-template-columns: 1fr 1fr;
|
|
246
|
+
gap: 8px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.pricing-option {
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
gap: 8px;
|
|
253
|
+
padding: 10px 14px;
|
|
254
|
+
border: 1px solid var(--border);
|
|
255
|
+
border-radius: 8px;
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
font-size: 14px;
|
|
258
|
+
transition: border-color 0.2s;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.pricing-option:hover {
|
|
262
|
+
border-color: var(--accent-dim);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.pricing-option input {
|
|
266
|
+
accent-color: var(--accent);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.pricing-option.selected {
|
|
270
|
+
border-color: var(--accent);
|
|
271
|
+
background: rgba(78, 201, 176, 0.08);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.email-form {
|
|
275
|
+
display: flex;
|
|
276
|
+
gap: 10px;
|
|
277
|
+
max-width: 420px;
|
|
278
|
+
margin: 0 auto;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@media (max-width: 420px) {
|
|
282
|
+
.email-form { flex-direction: column; }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.email-input {
|
|
286
|
+
flex: 1;
|
|
287
|
+
padding: 12px 16px;
|
|
288
|
+
border: 1px solid var(--border);
|
|
289
|
+
border-radius: 8px;
|
|
290
|
+
background: #0a0d10;
|
|
291
|
+
color: #e8e5dc;
|
|
292
|
+
font-size: 15px;
|
|
293
|
+
outline: none;
|
|
294
|
+
transition: border-color 0.2s;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.email-input:focus {
|
|
298
|
+
border-color: var(--accent);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.email-input::placeholder {
|
|
302
|
+
color: var(--muted);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.submit-btn {
|
|
306
|
+
background: var(--accent);
|
|
307
|
+
color: #0a0d10;
|
|
308
|
+
font-weight: 600;
|
|
309
|
+
font-size: 15px;
|
|
310
|
+
padding: 12px 24px;
|
|
311
|
+
border: none;
|
|
312
|
+
border-radius: 8px;
|
|
313
|
+
cursor: pointer;
|
|
314
|
+
transition: opacity 0.2s;
|
|
315
|
+
white-space: nowrap;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.submit-btn:hover {
|
|
319
|
+
opacity: 0.85;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.submit-btn:disabled {
|
|
323
|
+
opacity: 0.5;
|
|
324
|
+
cursor: not-allowed;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.form-status {
|
|
328
|
+
margin-top: 16px;
|
|
329
|
+
font-size: 14px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.form-status.success { color: var(--accent); }
|
|
333
|
+
.form-status.error { color: #f97583; }
|
|
334
|
+
|
|
335
|
+
/* Footer */
|
|
336
|
+
footer {
|
|
337
|
+
text-align: center;
|
|
338
|
+
padding: 40px 20px;
|
|
339
|
+
color: var(--muted);
|
|
340
|
+
font-size: 13px;
|
|
341
|
+
border-top: 1px solid var(--border);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
footer a {
|
|
345
|
+
color: var(--muted);
|
|
346
|
+
margin: 0 12px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Divider */
|
|
350
|
+
.divider {
|
|
351
|
+
border: none;
|
|
352
|
+
border-top: 1px solid var(--border);
|
|
353
|
+
max-width: var(--max-w);
|
|
354
|
+
margin: 0 auto;
|
|
355
|
+
}
|
|
356
|
+
</style>
|
|
357
|
+
</head>
|
|
358
|
+
<body>
|
|
359
|
+
|
|
360
|
+
<!-- Hero -->
|
|
361
|
+
<div class="hero">
|
|
362
|
+
<div class="container">
|
|
363
|
+
<h1 class="hero-tagline">Your AI agents<br/>know how to <em>reach you</em>.</h1>
|
|
364
|
+
<p class="hero-sub">
|
|
365
|
+
Mobile collaboration layer for AI coding agents.
|
|
366
|
+
Get notified when your agent needs you — decide in 10 seconds, walk away.
|
|
367
|
+
</p>
|
|
368
|
+
<div class="hero-install">
|
|
369
|
+
<code class="install-cmd">npx sema-cli claude</code>
|
|
370
|
+
</div>
|
|
371
|
+
<p class="hero-require">Requires macOS + Node 22+ + tmux</p>
|
|
372
|
+
<a href="#signup" class="hero-cta hero-cta-secondary">Join Waitlist</a>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<hr class="divider" />
|
|
377
|
+
|
|
378
|
+
<!-- How it works -->
|
|
379
|
+
<section>
|
|
380
|
+
<div class="container">
|
|
381
|
+
<h2 class="section-title">How it works</h2>
|
|
382
|
+
<div class="steps">
|
|
383
|
+
<div class="step">
|
|
384
|
+
<div class="step-num">1</div>
|
|
385
|
+
<div>
|
|
386
|
+
<div class="step-title">Start your agent on your Mac</div>
|
|
387
|
+
<div class="step-desc">
|
|
388
|
+
Launch Claude Code, Codex, or Gemini CLI as usual. Sema runs alongside — no changes to your workflow.
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
<div class="step">
|
|
393
|
+
<div class="step-num">2</div>
|
|
394
|
+
<div>
|
|
395
|
+
<div class="step-title">Your agent reaches your phone</div>
|
|
396
|
+
<div class="step-desc">
|
|
397
|
+
When the agent needs your input, you get a smart notification — not a raw terminal dump, but a structured decision you can understand at a glance.
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
<div class="step">
|
|
402
|
+
<div class="step-num">3</div>
|
|
403
|
+
<div>
|
|
404
|
+
<div class="step-title">Decide in 10 seconds</div>
|
|
405
|
+
<div class="step-desc">
|
|
406
|
+
Approve, reject, or choose from options — right on your phone. The agent continues. You keep walking.
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
</section>
|
|
413
|
+
|
|
414
|
+
<hr class="divider" />
|
|
415
|
+
|
|
416
|
+
<!-- Features -->
|
|
417
|
+
<section>
|
|
418
|
+
<div class="container">
|
|
419
|
+
<h2 class="section-title">Built for async-first development</h2>
|
|
420
|
+
<div class="features">
|
|
421
|
+
<div class="feature-card">
|
|
422
|
+
<div class="feature-icon">🧠</div>
|
|
423
|
+
<div class="feature-title">Smart Notifications</div>
|
|
424
|
+
<div class="feature-desc">
|
|
425
|
+
Semantic analysis turns raw agent output into structured decisions. Yes/no prompts, numbered choices, error states — presented as tappable cards, not terminal text.
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
<div class="feature-card">
|
|
429
|
+
<div class="feature-icon">🔐</div>
|
|
430
|
+
<div class="feature-title">End-to-End Encrypted</div>
|
|
431
|
+
<div class="feature-desc">
|
|
432
|
+
X25519 key exchange + AES-256-GCM. The relay server sees only ciphertext. Your code and decisions stay private.
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
<div class="feature-card">
|
|
436
|
+
<div class="feature-icon">🤖</div>
|
|
437
|
+
<div class="feature-title">Multi-Agent Inbox</div>
|
|
438
|
+
<div class="feature-desc">
|
|
439
|
+
Run Claude, Codex, and Gemini in parallel. One unified inbox on your phone to manage all active agent sessions.
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
<div class="feature-card">
|
|
443
|
+
<div class="feature-icon">📱</div>
|
|
444
|
+
<div class="feature-title">Session Persistence</div>
|
|
445
|
+
<div class="feature-desc">
|
|
446
|
+
Phone disconnect, subway tunnel, Mac sleep — your agent session survives it all via tmux. Reconnect and pick up where you left off.
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
</section>
|
|
452
|
+
|
|
453
|
+
<hr class="divider" />
|
|
454
|
+
|
|
455
|
+
<!-- Signup -->
|
|
456
|
+
<section id="signup">
|
|
457
|
+
<div class="container">
|
|
458
|
+
<div class="signup">
|
|
459
|
+
<h2 class="signup-title">Get early access</h2>
|
|
460
|
+
<p class="signup-sub">
|
|
461
|
+
We're looking for developers who use AI coding agents daily.
|
|
462
|
+
<br/>Join the waitlist — we'll send you an invite when it's ready.
|
|
463
|
+
</p>
|
|
464
|
+
|
|
465
|
+
<div class="pricing-group">
|
|
466
|
+
<label class="pricing-label">How much would you pay per month?</label>
|
|
467
|
+
<div class="pricing-options">
|
|
468
|
+
<label class="pricing-option" onclick="selectPricing(this)">
|
|
469
|
+
<input type="radio" name="pricing" value="free" /> Free (with limits)
|
|
470
|
+
</label>
|
|
471
|
+
<label class="pricing-option" onclick="selectPricing(this)">
|
|
472
|
+
<input type="radio" name="pricing" value="$5/mo" /> $5 / month
|
|
473
|
+
</label>
|
|
474
|
+
<label class="pricing-option" onclick="selectPricing(this)">
|
|
475
|
+
<input type="radio" name="pricing" value="$10/mo" /> $10 / month
|
|
476
|
+
</label>
|
|
477
|
+
<label class="pricing-option" onclick="selectPricing(this)">
|
|
478
|
+
<input type="radio" name="pricing" value="$20/mo" /> $20 / month
|
|
479
|
+
</label>
|
|
480
|
+
<label class="pricing-option" onclick="selectPricing(this)">
|
|
481
|
+
<input type="radio" name="pricing" value="$20+/mo" /> More than $20
|
|
482
|
+
</label>
|
|
483
|
+
<label class="pricing-option" onclick="selectPricing(this)">
|
|
484
|
+
<input type="radio" name="pricing" value="undecided" /> Not sure yet
|
|
485
|
+
</label>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<form class="email-form" id="waitlist-form" onsubmit="return submitForm(event)">
|
|
490
|
+
<input
|
|
491
|
+
type="email"
|
|
492
|
+
class="email-input"
|
|
493
|
+
id="email-input"
|
|
494
|
+
placeholder="you@example.com"
|
|
495
|
+
required
|
|
496
|
+
autocomplete="email"
|
|
497
|
+
/>
|
|
498
|
+
<button type="submit" class="submit-btn" id="submit-btn">Join Waitlist</button>
|
|
499
|
+
</form>
|
|
500
|
+
|
|
501
|
+
<div class="form-status" id="form-status"></div>
|
|
502
|
+
</div>
|
|
503
|
+
</div>
|
|
504
|
+
</section>
|
|
505
|
+
|
|
506
|
+
<!-- Footer -->
|
|
507
|
+
<footer>
|
|
508
|
+
<a href="/app">Open App</a>
|
|
509
|
+
<a href="https://www.npmjs.com/package/sema-cli" target="_blank" rel="noopener">npm</a>
|
|
510
|
+
<br /><br />
|
|
511
|
+
Sema · Built for developers who delegate to AI and walk away.
|
|
512
|
+
</footer>
|
|
513
|
+
|
|
514
|
+
<script>
|
|
515
|
+
let selectedPricing = "";
|
|
516
|
+
|
|
517
|
+
// Click-to-copy install command
|
|
518
|
+
document.querySelector(".install-cmd")?.addEventListener("click", function() {
|
|
519
|
+
navigator.clipboard.writeText("npx sema-cli claude").then(() => {
|
|
520
|
+
const orig = this.textContent;
|
|
521
|
+
this.textContent = "Copied!";
|
|
522
|
+
setTimeout(() => { this.textContent = orig; }, 2000);
|
|
523
|
+
}).catch(() => {});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
function selectPricing(el) {
|
|
527
|
+
document.querySelectorAll(".pricing-option").forEach(opt => opt.classList.remove("selected"));
|
|
528
|
+
el.classList.add("selected");
|
|
529
|
+
const radio = el.querySelector("input[type=radio]");
|
|
530
|
+
if (radio) {
|
|
531
|
+
radio.checked = true;
|
|
532
|
+
selectedPricing = radio.value;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function submitForm(event) {
|
|
537
|
+
event.preventDefault();
|
|
538
|
+
|
|
539
|
+
const email = document.getElementById("email-input").value.trim();
|
|
540
|
+
const btn = document.getElementById("submit-btn");
|
|
541
|
+
const status = document.getElementById("form-status");
|
|
542
|
+
|
|
543
|
+
if (!email) return false;
|
|
544
|
+
|
|
545
|
+
btn.disabled = true;
|
|
546
|
+
btn.textContent = "Submitting...";
|
|
547
|
+
status.textContent = "";
|
|
548
|
+
status.className = "form-status";
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
const res = await fetch("/api/waitlist", {
|
|
552
|
+
method: "POST",
|
|
553
|
+
headers: { "content-type": "application/json" },
|
|
554
|
+
body: JSON.stringify({ email, pricing: selectedPricing }),
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
if (res.ok) {
|
|
558
|
+
status.textContent = "✓ You're on the list! We'll reach out when it's ready.";
|
|
559
|
+
status.className = "form-status success";
|
|
560
|
+
btn.textContent = "Joined ✓";
|
|
561
|
+
document.getElementById("email-input").disabled = true;
|
|
562
|
+
} else if (res.status === 429) {
|
|
563
|
+
status.textContent = "Too many submissions. Please wait a moment.";
|
|
564
|
+
status.className = "form-status error";
|
|
565
|
+
btn.disabled = false;
|
|
566
|
+
btn.textContent = "Join Waitlist";
|
|
567
|
+
} else {
|
|
568
|
+
const data = await res.json().catch(() => ({}));
|
|
569
|
+
status.textContent = data.error || "Something went wrong. Try again.";
|
|
570
|
+
status.className = "form-status error";
|
|
571
|
+
btn.disabled = false;
|
|
572
|
+
btn.textContent = "Join Waitlist";
|
|
573
|
+
}
|
|
574
|
+
} catch {
|
|
575
|
+
status.textContent = "Network error. Please try again.";
|
|
576
|
+
status.className = "form-status error";
|
|
577
|
+
btn.disabled = false;
|
|
578
|
+
btn.textContent = "Join Waitlist";
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
</script>
|
|
584
|
+
|
|
585
|
+
</body>
|
|
586
|
+
</html>
|