@headlessly/sdk 0.0.1
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 +543 -0
- package/cli.js +48 -0
- package/index.js +19 -0
- package/package.json +61 -0
- package/types.d.ts +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
# headless.ly
|
|
2
|
+
|
|
3
|
+
The operating system for agent-first startups.
|
|
4
|
+
|
|
5
|
+
Create an org. Get everything. CRM, project management, billing, analytics, content, support, marketing, experimentation — as a single unified system that AI agents can operate autonomously.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import Headlessly from 'headless.ly'
|
|
9
|
+
|
|
10
|
+
const org = Headlessly({ tenant: 'my-startup' })
|
|
11
|
+
|
|
12
|
+
// Everything exists. One line.
|
|
13
|
+
await org.Contact.create({ name: 'Alice', email: 'alice@vc.com', stage: 'Lead' })
|
|
14
|
+
await org.Deal.create({ name: 'Seed Round', contact: 'contact_1', value: 500_000 })
|
|
15
|
+
await org.Issue.create({ title: 'Build MVP', project: 'project_1', status: 'InProgress' })
|
|
16
|
+
await org.Subscription.create({ price: 'price_pro', customer: 'customer_1' })
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install headless.ly
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Why headless.ly?
|
|
26
|
+
|
|
27
|
+
**You're building a startup, not configuring software.**
|
|
28
|
+
|
|
29
|
+
Most business tools make you choose modules, design schemas, wire integrations, and learn 15 different APIs. headless.ly gives you one typed graph of everything — contacts, deals, tasks, content, tickets, billing, analytics, experiments — connected and ready for agents to operate from day zero.
|
|
30
|
+
|
|
31
|
+
- **One graph, not 15 SaaS tools** — every entity lives in the same system
|
|
32
|
+
- **Agent-first** — TypeScript SDK is the primary contract, not a bolted-on API
|
|
33
|
+
- **Immutable event log** — nothing is ever deleted, every state is reconstructable
|
|
34
|
+
- **Zero configuration** — create an org and everything works
|
|
35
|
+
|
|
36
|
+
## 32 Core Entities
|
|
37
|
+
|
|
38
|
+
Every entity exists because headless.ly needs it to run itself as an autonomous business. If headless.ly doesn't need it, it doesn't ship. Every entity carries `$type`, `$id`, `$context` (tenant namespace), `$version`, `$createdAt`, `$createdBy`, `$updatedAt`.
|
|
39
|
+
|
|
40
|
+
### Identity — Authentication & Tenancy (WorkOS-backed)
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Users and orgs managed via WorkOS
|
|
44
|
+
await org.User.invite({ name: 'Alice', email: 'alice@acme.com', role: 'Member' })
|
|
45
|
+
await org.ApiKey.create({ name: 'CI/CD', scopes: ['read', 'write'] })
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Entity | Purpose |
|
|
49
|
+
|--------|---------|
|
|
50
|
+
| **User** | Authenticated humans — founders, team members |
|
|
51
|
+
| **Organization** | Tenants — each org gets all 32 entities |
|
|
52
|
+
| **ApiKey** | Programmatic access — SDK, CI/CD, agents |
|
|
53
|
+
|
|
54
|
+
### CRM — People & Relationships
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
await org.Contact.create({ name: 'Alice', email: 'alice@vc.com', stage: 'Lead' })
|
|
58
|
+
await org.Contact.qualify('contact_1')
|
|
59
|
+
await org.Company.create({ name: 'Acme Corp', domain: 'acme.com' })
|
|
60
|
+
await org.Deal.create({ name: 'Enterprise', value: 50_000, stage: 'Proposal' })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Entity | Purpose |
|
|
64
|
+
|--------|---------|
|
|
65
|
+
| **Contact** | People — developers, founders, VCs, partners |
|
|
66
|
+
| **Company** | Organizations — startups, enterprises, partners |
|
|
67
|
+
| **Deal** | Sales opportunities with value and pipeline stage |
|
|
68
|
+
|
|
69
|
+
### Projects — Work (GitHub + Beads superset)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
await org.Project.create({ name: 'v1 Launch', status: 'Active' })
|
|
73
|
+
await org.Issue.create({ title: 'Build auth flow', project: 'project_1', priority: 'High', type: 'Feature' })
|
|
74
|
+
await org.Issue.assign('issue_1', { assignee: 'user_1' })
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Entity | Purpose |
|
|
78
|
+
|--------|---------|
|
|
79
|
+
| **Project** | Container for organized work, maps to GitHub Projects |
|
|
80
|
+
| **Issue** | Unit of work with deps/hierarchy, syncs with GitHub Issues |
|
|
81
|
+
| **Comment** | Polymorphic discussion on Issues, Tickets, or Content |
|
|
82
|
+
|
|
83
|
+
### Content — Publishing (mdxui-typed)
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
await org.Content.create({ title: 'Pricing', slug: 'pricing', type: 'Page', status: 'Published' })
|
|
87
|
+
await org.Content.create({ title: 'Why Agent-First', type: 'Post', status: 'Draft' })
|
|
88
|
+
await org.Content.publish('content_1')
|
|
89
|
+
await org.Asset.create({ name: 'hero.png', url: 'https://...', type: 'Image' })
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Entity | Purpose |
|
|
93
|
+
|--------|---------|
|
|
94
|
+
| **Content** | Typed MDX — Page, Post, Doc, LandingPage, LeanCanvas, StoryBrand, etc. |
|
|
95
|
+
| **Asset** | Media files — images, videos, documents |
|
|
96
|
+
| **Site** | mdxui site config — Marketing, Docs, Blog, Directory, etc. |
|
|
97
|
+
|
|
98
|
+
### Billing — Revenue (Stripe-backed)
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
await org.Product.create({ name: 'headless.ly', type: 'Software' })
|
|
102
|
+
await org.Price.create({ name: 'Pro', product: 'product_1', amount: 49, interval: 'Monthly' })
|
|
103
|
+
await org.Customer.create({ contact: 'contact_1' })
|
|
104
|
+
await org.Subscription.create({ price: 'price_1', customer: 'customer_1' })
|
|
105
|
+
await org.Subscription.upgrade('sub_1', { price: 'price_enterprise' })
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
| Entity | Purpose |
|
|
109
|
+
|--------|---------|
|
|
110
|
+
| **Customer** | Stripe Customer — links Contact to billing identity |
|
|
111
|
+
| **Product** | What you sell |
|
|
112
|
+
| **Price** | Pricing config — Monthly, Annual, OneTime, Usage |
|
|
113
|
+
| **Subscription** | Active paying relationships |
|
|
114
|
+
| **Invoice** | Bills — monthly/annual |
|
|
115
|
+
| **Payment** | Money movement — charges, refunds, credits |
|
|
116
|
+
|
|
117
|
+
### Support — Help
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
await org.Ticket.create({ subject: 'API returns 500', priority: 'High', contact: 'contact_1' })
|
|
121
|
+
await org.Ticket.assign('ticket_1', { assignee: 'user_1' })
|
|
122
|
+
await org.Message.create({ ticket: 'ticket_1', body: 'Looking into this now.', channel: 'Web', direction: 'Outbound' })
|
|
123
|
+
await org.Ticket.resolve('ticket_1')
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Entity | Purpose |
|
|
127
|
+
|--------|---------|
|
|
128
|
+
| **Ticket** | Support requests — bugs, questions, feature requests |
|
|
129
|
+
|
|
130
|
+
### Analytics — Insights
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Events captured via browser SDK → forwarded to GA/Sentry/PostHog + stored in lakehouse
|
|
134
|
+
await org.Event.track({ name: 'signup', userId: 'user_1', properties: { plan: 'free' } })
|
|
135
|
+
|
|
136
|
+
// Financial metrics derived from real Stripe data (headless baremetrics)
|
|
137
|
+
const mrr = await org.Metric.get('mrr') // Real MRR from Stripe
|
|
138
|
+
const churn = await org.Metric.get('churn_rate') // Real churn rate
|
|
139
|
+
|
|
140
|
+
// Funnels track the full revenue pipeline
|
|
141
|
+
await org.Funnel.analyze('visitor-to-paid') // visitor → signup → activated → paid
|
|
142
|
+
await org.Goal.check('goal_mrr_100k')
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Entity | Purpose |
|
|
146
|
+
|--------|---------|
|
|
147
|
+
| **Event** | Every tracked action — pageviews, API calls, sign-ups (forwarded to GA/PostHog + stored) |
|
|
148
|
+
| **Metric** | Real values — MRR, churn, NRR, LTV from Stripe; DAU, NPS from events |
|
|
149
|
+
| **Funnel** | Conversion flows — visitor → signup → activate → pay |
|
|
150
|
+
| **Goal** | Business objectives — revenue targets, user targets |
|
|
151
|
+
|
|
152
|
+
### Marketing — Growth
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
await org.Campaign.create({ name: 'Product Hunt Launch', type: 'Event', status: 'Active' })
|
|
156
|
+
await org.Segment.create({ name: 'Power Users', definition: { events: { $gte: 100 } } })
|
|
157
|
+
await org.Form.create({ name: 'Waitlist', fields: ['name', 'email', 'company'] })
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
| Entity | Purpose |
|
|
161
|
+
|--------|---------|
|
|
162
|
+
| **Campaign** | Marketing initiatives — launches, drips, content pushes |
|
|
163
|
+
| **Segment** | Audience groups — by ICP, usage, stage |
|
|
164
|
+
| **Form** | Data collection — sign-up, waitlist, contact us |
|
|
165
|
+
|
|
166
|
+
### Experimentation — Testing
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
await org.Experiment.create({ name: 'Pricing Test', type: 'ABTest', variants: ['$29', '$49'] })
|
|
170
|
+
await org.FeatureFlag.create({ key: 'new-onboarding', rollout: 25 })
|
|
171
|
+
await org.FeatureFlag.rollout('new-onboarding', { percentage: 100 })
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
| Entity | Purpose |
|
|
175
|
+
|--------|---------|
|
|
176
|
+
| **Experiment** | A/B tests on pricing, onboarding, features |
|
|
177
|
+
| **FeatureFlag** | Progressive rollouts, beta access |
|
|
178
|
+
|
|
179
|
+
### Communication
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// Cross-channel messaging — email, SMS, Slack, Discord, Teams, web, in-app
|
|
183
|
+
await org.Message.create({ body: 'Welcome!', channel: 'Email', direction: 'Outbound', to: 'contact_1' })
|
|
184
|
+
await org.Message.create({ body: 'Following up', channel: 'Slack', direction: 'Outbound', deal: 'deal_1' })
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
| Entity | Purpose |
|
|
188
|
+
|--------|---------|
|
|
189
|
+
| **Message** | Cross-channel comms — Email, SMS, Slack, Discord, Teams, Web, InApp, API |
|
|
190
|
+
|
|
191
|
+
### Platform — Infrastructure
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Automation: when a deal closes, create a subscription
|
|
195
|
+
org.Deal.closed(deal => {
|
|
196
|
+
org.Subscription.create({ price: 'price_pro', customer: deal.contact })
|
|
197
|
+
org.Contact.update(deal.contact, { stage: 'Customer' })
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
// AI agent with full autonomous interface
|
|
201
|
+
await org.Agent.create({ name: 'Support Bot', mode: 'Autonomous', role: 'Support', model: 'claude-sonnet' })
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
| Entity | Purpose |
|
|
205
|
+
|--------|---------|
|
|
206
|
+
| **Workflow** | Automation rules — deal.closed → subscription.created |
|
|
207
|
+
| **Integration** | External connections — Stripe, GitHub, Slack, WorkOS |
|
|
208
|
+
| **Agent** | AI agents with do/ask/decide/approve/notify/delegate/escalate/learn |
|
|
209
|
+
|
|
210
|
+
## Digital Objects
|
|
211
|
+
|
|
212
|
+
Entities are defined using the `Noun()` function from `digital-objects` — zero dependencies, zero codegen, full TypeScript inference:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { Noun } from 'digital-objects'
|
|
216
|
+
|
|
217
|
+
export const Contact = Noun('Contact', {
|
|
218
|
+
name: 'string!',
|
|
219
|
+
email: 'string?#',
|
|
220
|
+
phone: 'string?',
|
|
221
|
+
title: 'string?',
|
|
222
|
+
stage: 'Lead | Qualified | Customer | Churned | Partner',
|
|
223
|
+
source: 'string?',
|
|
224
|
+
company: '-> Company.contacts',
|
|
225
|
+
deals: '<- Deal.contact[]',
|
|
226
|
+
messages: '<- Message.contact[]',
|
|
227
|
+
|
|
228
|
+
// Custom verbs (CRUD is automatic)
|
|
229
|
+
qualify: 'Qualified',
|
|
230
|
+
capture: 'Captured',
|
|
231
|
+
assign: 'Assigned',
|
|
232
|
+
merge: 'Merged',
|
|
233
|
+
enrich: 'Enriched',
|
|
234
|
+
})
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Every property value tells the parser what it is:
|
|
238
|
+
|
|
239
|
+
| Value Pattern | Meaning | Example |
|
|
240
|
+
|--------------|---------|---------|
|
|
241
|
+
| Type string | Data property | `'string!'`, `'number?'`, `'datetime!'` |
|
|
242
|
+
| Arrow syntax | Relationship | `'-> Company.contacts'`, `'<- Deal.contact[]'` |
|
|
243
|
+
| Pipe-separated PascalCase | Enum | `'Lead \| Qualified \| Customer'` |
|
|
244
|
+
| Single PascalCase word | Verb → Event | `'Qualified'`, `'Captured'` |
|
|
245
|
+
| `null` | Opt out of inherited verb | `update: null` (makes entity immutable) |
|
|
246
|
+
|
|
247
|
+
## Verbs & Event Handlers
|
|
248
|
+
|
|
249
|
+
Every verb has a full conjugation that maps to the execution lifecycle:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
qualify
|
|
253
|
+
├── qualify() → execute the action
|
|
254
|
+
├── qualifying() → BEFORE hook (validate, transform, reject)
|
|
255
|
+
├── qualified() → AFTER hook (react, trigger side effects)
|
|
256
|
+
└── qualifiedBy → who performed this action
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Register handlers directly on the entity:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// BEFORE hook — validate or reject
|
|
263
|
+
org.Contact.qualifying(contact => {
|
|
264
|
+
if (!contact.email) throw new Error('Cannot qualify without email')
|
|
265
|
+
return contact
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// AFTER hook — react to what happened
|
|
269
|
+
org.Contact.qualified(contact => {
|
|
270
|
+
org.Activity.create({
|
|
271
|
+
type: 'Task',
|
|
272
|
+
subject: `Follow up with ${contact.name}`,
|
|
273
|
+
contact: contact.$id,
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// Execute
|
|
278
|
+
await org.Contact.qualify('contact_123')
|
|
279
|
+
// → runs .qualifying() → sets stage → persists event → runs .qualified()
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Promise Pipelining
|
|
283
|
+
|
|
284
|
+
The SDK uses `rpc.do` with capnweb promise pipelining — chain dependent calls without awaiting, and the system batches them into a single round trip:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// One round trip, not three
|
|
288
|
+
const deals = await org.Contact
|
|
289
|
+
.find({ stage: 'Qualified' })
|
|
290
|
+
.map(contact => contact.deals)
|
|
291
|
+
.filter(deal => deal.status === 'Open')
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Time Travel
|
|
295
|
+
|
|
296
|
+
Every mutation is an event appended to an immutable log. Any point in time can be reconstructed:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
const contacts = await org.Contact.find(
|
|
300
|
+
{ stage: 'Lead' },
|
|
301
|
+
{ asOf: '2026-01-15T10:00:00Z' }
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
await org.Contact.rollback('contact_123', { asOf: '2026-02-06T15:00:00Z' })
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
This gives founders confidence to let agents operate freely — anything can be undone.
|
|
308
|
+
|
|
309
|
+
## Foundational Integrations
|
|
310
|
+
|
|
311
|
+
Stripe and GitHub aren't "integrations to add later" — they're truth sources from day 1:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// Day 0: connect Stripe and GitHub
|
|
315
|
+
await org.Integration.connect('stripe', { apiKey: process.env.STRIPE_KEY })
|
|
316
|
+
await org.Integration.connect('github', { token: process.env.GITHUB_TOKEN })
|
|
317
|
+
|
|
318
|
+
// Billing entities are now Stripe-backed
|
|
319
|
+
// Creating a Subscription creates a Stripe subscription
|
|
320
|
+
await org.Subscription.create({ plan: 'pro', contact: 'contact_1' })
|
|
321
|
+
|
|
322
|
+
// Project entities sync with GitHub
|
|
323
|
+
// Creating an Issue creates a GitHub issue
|
|
324
|
+
await org.Issue.create({ title: 'Build auth', project: 'project_1' })
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Real Financial Metrics (Headless Baremetrics)
|
|
328
|
+
|
|
329
|
+
Financial metrics are derived from real Stripe data — not self-reported, not placeholders:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
const mrr = await org.Metric.get('mrr') // Real MRR from Stripe subscriptions
|
|
333
|
+
const churn = await org.Metric.get('churn_rate') // Real churn from Stripe events
|
|
334
|
+
const nrr = await org.Metric.get('nrr') // Net revenue retention
|
|
335
|
+
const ltv = await org.Metric.get('ltv') // Lifetime value
|
|
336
|
+
const arpu = await org.Metric.get('arpu') // Average revenue per user
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Event Forwarding & Data Lakehouse
|
|
340
|
+
|
|
341
|
+
The browser SDK captures all events and forwards to your existing tools while building your own data lake:
|
|
342
|
+
|
|
343
|
+
```html
|
|
344
|
+
<!-- One script tag — events flow everywhere -->
|
|
345
|
+
<script src="https://js.headless.ly/v1" data-tenant="my-startup" />
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
Browser → headless.ly → Google Analytics (web analytics)
|
|
350
|
+
→ Sentry (error tracking)
|
|
351
|
+
→ PostHog (product analytics)
|
|
352
|
+
→ Iceberg R2 Lakehouse (your data, forever)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Day 1: external tools handle analytics. Over time, your lakehouse enables more. The revenue pipeline — visitor → signup → paid → $$ — is tracked end-to-end.
|
|
356
|
+
|
|
357
|
+
### Dashboards — Your Entire Business at a Glance
|
|
358
|
+
|
|
359
|
+
headless.ly is headless — no built-in UI dashboards. But because everything is one system, a single dashboard connection shows your *entire business*. Most founders wire up 5-10 SaaS tools to get one view. With headless.ly, one connection covers it all.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// One API, every metric
|
|
363
|
+
const metrics = await org.Metric.dashboard()
|
|
364
|
+
// {
|
|
365
|
+
// revenue: { mrr: 12_500, arr: 150_000, churn: 2.1, nrr: 108 },
|
|
366
|
+
// pipeline: { leads: 47, qualified: 12, deals_open: 8, deal_value: 340_000 },
|
|
367
|
+
// product: { tasks_open: 23, milestones_due: 2 },
|
|
368
|
+
// support: { tickets_open: 5, avg_response: '2h', csat: 94 },
|
|
369
|
+
// marketing: { campaigns_active: 3, signups_7d: 89 },
|
|
370
|
+
// engagement: { dau: 230, mau: 1_200, events_24h: 15_400 },
|
|
371
|
+
// }
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Plug into any visualization tool: **Numerics** (iOS/Mac widgets), **Grafana** (time-series), **Retool** (internal tools), **Google Sheets** (live connection), or any BI tool via OpenAPI.
|
|
375
|
+
|
|
376
|
+
### All Integrations
|
|
377
|
+
|
|
378
|
+
| Connect... | ...and this lights up | When |
|
|
379
|
+
|------------|----------------------|------|
|
|
380
|
+
| **Stripe** | Products, subscriptions, invoices, MRR, churn → Billing + Metrics | **Day 1** |
|
|
381
|
+
| **GitHub** | Repos, issues, PRs, milestones → Projects | **Day 1** |
|
|
382
|
+
| **Numerics / Dashboards** | Real-time KPIs → entire business at a glance | **Day 1** |
|
|
383
|
+
| **Google Analytics** | Pageviews, sessions → Events (forwarded + stored) | Day 1 (proxy) |
|
|
384
|
+
| **Sentry** | Errors → Events (forwarded + stored) | Day 1 (proxy) |
|
|
385
|
+
| **PostHog** | Product events → Events (forwarded + stored) | Day 1 (proxy) |
|
|
386
|
+
| **Google Apps** | Email → Messages, Calendar → Events | Phase 2 |
|
|
387
|
+
| **Slack/Discord** | Messages → Messages, Channels → Segments | Phase 2 |
|
|
388
|
+
|
|
389
|
+
## ICP Templates
|
|
390
|
+
|
|
391
|
+
At org creation, select an ICP template that configures defaults without changing the schema:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
const org = Headlessly({
|
|
395
|
+
tenant: 'my-startup',
|
|
396
|
+
template: 'b2b-saas',
|
|
397
|
+
})
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
| Template | Pipeline Stages | Key Metrics |
|
|
401
|
+
|----------|----------------|-------------|
|
|
402
|
+
| **B2B SaaS** | Lead → Demo → Trial → Closed → Active → Churned | MRR, CAC, LTV |
|
|
403
|
+
| **B2C / Consumer** | Signup → Activated → Engaged → Monetized → Churned | DAU/MAU, ARPU |
|
|
404
|
+
| **B2D / Developer Tools** | Discovered → Integrated → Active → Scaled | Time-to-first-call, API usage |
|
|
405
|
+
| **B2A / Agent Services** | Connected → Configured → Autonomous → Scaled | Tool invocations, success rate |
|
|
406
|
+
|
|
407
|
+
Same 32 entities. Same schema. Different default labels and metric calculations. Segment includes ICP sentence pattern (`as/at/are/using/to`) for structured audience targeting.
|
|
408
|
+
|
|
409
|
+
## MCP: Search, Fetch, Do
|
|
410
|
+
|
|
411
|
+
Three primitives for AI agents — not hundreds of tools:
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
search({ type: 'Contact', filter: { stage: 'Lead' } })
|
|
415
|
+
fetch({ type: 'Contact', id: 'abc123' })
|
|
416
|
+
do({ method: 'Contact.qualify', args: ['abc123'] })
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## Multi-Tenancy
|
|
420
|
+
|
|
421
|
+
Each tenant gets their own Cloudflare Durable Object — complete data isolation:
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
POST headless.ly/~my-startup/Contact → create
|
|
425
|
+
GET headless.ly/~my-startup/Contact?stage=Lead → find
|
|
426
|
+
GET headless.ly/~my-startup/Contact/abc123 → get
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Journey Subdomains
|
|
430
|
+
|
|
431
|
+
Different stages of building a startup emphasize different capabilities. Journey subdomains provide contextual focus — all 32 entities are always available, but the subdomain surfaces what matters most:
|
|
432
|
+
|
|
433
|
+
| Subdomain | Stage | Focus |
|
|
434
|
+
|-----------|-------|-------|
|
|
435
|
+
| `build.headless.ly` | Day 0-30 | Projects, Issues, Content |
|
|
436
|
+
| `launch.headless.ly` | Day 30 | Campaigns, Content, Forms |
|
|
437
|
+
| `experiment.headless.ly` | Day 30-60 | Experiments, FeatureFlags, Funnels |
|
|
438
|
+
| `grow.headless.ly` | Day 30-90 | Contacts, Deals, Campaigns, Goals |
|
|
439
|
+
| `automate.headless.ly` | Day 60+ | Workflows, Agents, Integrations |
|
|
440
|
+
| `scale.headless.ly` | Day 90+ | Metrics, Subscriptions, Analytics |
|
|
441
|
+
|
|
442
|
+
## The Startup Journey
|
|
443
|
+
|
|
444
|
+
### Day 0 — "I have an idea" → build.headless.ly
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import Headlessly from 'headless.ly'
|
|
448
|
+
const org = Headlessly({ tenant: 'my-startup' })
|
|
449
|
+
// Everything exists. CRM, PM, Content, Billing, Analytics, Support — all ready.
|
|
450
|
+
|
|
451
|
+
// Connect Stripe and GitHub immediately — real data from day 1
|
|
452
|
+
await org.Integration.connect('stripe', { apiKey: process.env.STRIPE_KEY })
|
|
453
|
+
await org.Integration.connect('github', { token: process.env.GITHUB_TOKEN })
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Day 1-30 — "I'm building" → build.headless.ly
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
await org.Contact.create({ name: 'Alice', email: 'alice@vc.com', stage: 'Lead' })
|
|
460
|
+
await org.Issue.create({ title: 'Build MVP', project: 'project_1', status: 'InProgress' })
|
|
461
|
+
await org.Content.create({ title: 'Introducing Our Product', type: 'Post' })
|
|
462
|
+
|
|
463
|
+
// Browser SDK captures everything → forwards to GA/Sentry/PostHog + lakehouse
|
|
464
|
+
// <script src="https://js.headless.ly/v1" data-tenant="my-startup" />
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Day 30-90 — "I have users" → grow.headless.ly
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
// Stripe webhooks are already flowing → billing entities are real
|
|
471
|
+
// GitHub events are already syncing → project entities are real
|
|
472
|
+
// Financial metrics are computing from Stripe → MRR, churn, NRR are real numbers
|
|
473
|
+
|
|
474
|
+
org.Contact.qualified(contact => {
|
|
475
|
+
org.Event.create({ name: 'demo_scheduled', source: 'crm', contact: contact.$id })
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
org.Deal.closed(deal => {
|
|
479
|
+
org.Subscription.create({ price: 'price_pro', customer: deal.contact })
|
|
480
|
+
org.Contact.update(deal.contact, { stage: 'Customer' })
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
const mrr = await org.Metric.get('mrr') // Real number from Stripe
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Day 90+ — "I'm scaling" → scale.headless.ly
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// Full revenue pipeline: visitor → signup → activated → paid → $$ (MRR/NRR/Churn)
|
|
490
|
+
await org.Funnel.analyze('visitor-to-paid')
|
|
491
|
+
|
|
492
|
+
await org.Campaign.create({ name: 'Series A Outreach', type: 'Email' })
|
|
493
|
+
await org.Experiment.create({ name: 'Pricing v2', type: 'ABTest', variants: ['$29', '$49'] })
|
|
494
|
+
await org.Agent.create({ name: 'Support Bot', mode: 'Autonomous', role: 'Support' })
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
The same system scales from 1 founder to a seed-stage team. No migration. No new tools. The graph grows, agents get smarter, the lakehouse gets deeper, and the immutable event log means nothing is ever lost.
|
|
498
|
+
|
|
499
|
+
## Architecture
|
|
500
|
+
|
|
501
|
+
```
|
|
502
|
+
┌─────────────────────────────────────────────────┐
|
|
503
|
+
│ headless.ly │
|
|
504
|
+
│ Tenant management, ICP templates, SDK │
|
|
505
|
+
│ RPC client with capnweb pipelining │
|
|
506
|
+
├─────────────────────────────────────────────────┤
|
|
507
|
+
│ objects.do │
|
|
508
|
+
│ Managed Digital Object service │
|
|
509
|
+
│ Verb conjugation, event subscriptions │
|
|
510
|
+
├──────────────────┬──────────────────────────────┤
|
|
511
|
+
│ digital-objects │ .do services │
|
|
512
|
+
│ (zero-dep schemas)│ payments.do (Stripe) │
|
|
513
|
+
│ │ oauth.do (auth) │
|
|
514
|
+
│ Noun() definitions│ events.do (CDC) │
|
|
515
|
+
│ 32 core entities │ database.do (ParqueDB) │
|
|
516
|
+
│ Type inference │ functions.do (execution) │
|
|
517
|
+
├──────────────────┴──────────────────────────────┤
|
|
518
|
+
│ @dotdo/do │
|
|
519
|
+
│ THE Durable Object for Digital Objects │
|
|
520
|
+
│ StorageHandler · EventsStore · WebSocket │
|
|
521
|
+
├─────────────────────────────────────────────────┤
|
|
522
|
+
│ @dotdo/db (ParqueDB) │
|
|
523
|
+
│ Hybrid Relational-Document-Graph DB │
|
|
524
|
+
│ Parquet · Iceberg · time travel · inference │
|
|
525
|
+
├─────────────────────────────────────────────────┤
|
|
526
|
+
│ Cloudflare Infrastructure │
|
|
527
|
+
│ Workers · Durable Objects · R2 · KV · AI │
|
|
528
|
+
└─────────────────────────────────────────────────┘
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
## Package Ecosystem
|
|
532
|
+
|
|
533
|
+
| Package | What it adds |
|
|
534
|
+
|---------|-------------|
|
|
535
|
+
| `digital-objects` | Pure schemas, zero deps |
|
|
536
|
+
| `business-as-code` | + business definition primitives |
|
|
537
|
+
| `business.org.ai` | + ontology data (NAICS, O*NET, APQC) |
|
|
538
|
+
| `startups.org.ai` | + ICP templates, startup metrics |
|
|
539
|
+
| `headless.ly` | + tenant composition, RPC client, managed service |
|
|
540
|
+
|
|
541
|
+
## License
|
|
542
|
+
|
|
543
|
+
MIT
|
package/cli.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* headless.ly CLI
|
|
4
|
+
* @generated
|
|
5
|
+
*
|
|
6
|
+
* Proxies to cli.do, mcp.do, and oauth.do for functionality
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { cli } from 'cli.do'
|
|
10
|
+
|
|
11
|
+
// Auth commands -> oauth.do
|
|
12
|
+
cli.command('login', 'Authenticate with headless.ly', async () => {
|
|
13
|
+
const { login } = await import('oauth.do/node')
|
|
14
|
+
await login({ service: 'headless.ly' })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
cli.command('logout', 'Log out from headless.ly', async () => {
|
|
18
|
+
const { logout } = await import('oauth.do/node')
|
|
19
|
+
await logout({ service: 'headless.ly' })
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
cli.command('whoami', 'Show current user', async () => {
|
|
23
|
+
const { whoami } = await import('oauth.do/node')
|
|
24
|
+
const user = await whoami({ service: 'headless.ly' })
|
|
25
|
+
console.log(user ? `Logged in as ${user.email}` : 'Not logged in')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// MCP server -> mcp.do
|
|
29
|
+
cli.command('mcp', 'Start MCP server for AI agents', async () => {
|
|
30
|
+
const { serve } = await import('mcp.do')
|
|
31
|
+
await serve({
|
|
32
|
+
name: 'headless.ly',
|
|
33
|
+
tools: await import('./mcp-tools.js')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// RPC commands -> rpc.do
|
|
38
|
+
cli.command('call <service> <method> [params]', 'Make RPC call', async (service, method, params) => {
|
|
39
|
+
const { ensureLoggedIn } = await import('oauth.do/node')
|
|
40
|
+
const { apiKey } = await ensureLoggedIn({ service: 'headless.ly' })
|
|
41
|
+
const { rpc } = await import('rpc.do')
|
|
42
|
+
|
|
43
|
+
const client = rpc(service, { apiKey })
|
|
44
|
+
const result = await client.call(method, params ? JSON.parse(params) : {})
|
|
45
|
+
console.log(JSON.stringify(result, null, 2))
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
cli.run()
|
package/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* headless.ly - Headless SaaS Platform for AI Agents
|
|
3
|
+
* @generated
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// RPC client - all functionality is RPC against the backend
|
|
7
|
+
export { rpc, $ } from 'rpc.do'
|
|
8
|
+
|
|
9
|
+
// Convenience exports for common services
|
|
10
|
+
export const crm = (opts) => rpc('crm', opts)
|
|
11
|
+
export const sell = (opts) => rpc('sell', opts)
|
|
12
|
+
export const market = (opts) => rpc('market', opts)
|
|
13
|
+
export const erp = (opts) => rpc('erp', opts)
|
|
14
|
+
export const support = (opts) => rpc('support', opts)
|
|
15
|
+
export const analytics = (opts) => rpc('analytics', opts)
|
|
16
|
+
export const db = (opts) => rpc('db', opts)
|
|
17
|
+
|
|
18
|
+
// Re-export from rpc.do
|
|
19
|
+
import { rpc } from 'rpc.do'
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@headlessly/sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Headless SaaS Platform for AI Agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"types": "types.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"headlessly": "./cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./index.js",
|
|
14
|
+
"require": "./index.js",
|
|
15
|
+
"types": "./types.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./nouns/*": {
|
|
18
|
+
"import": "./nouns/*/index.js",
|
|
19
|
+
"types": "./nouns/*/types.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js",
|
|
24
|
+
"types.d.ts",
|
|
25
|
+
"cli.js",
|
|
26
|
+
"nouns/",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"typecheck": "vitest --typecheck"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"rpc.do": "^0.1.0",
|
|
36
|
+
"oauth.do": "^0.1.0",
|
|
37
|
+
"cli.do": "^0.1.0",
|
|
38
|
+
"mcp.do": "^0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"vitest": "^3.0.0",
|
|
42
|
+
"typescript": "^5.7.0"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"headless",
|
|
46
|
+
"saas",
|
|
47
|
+
"ai",
|
|
48
|
+
"agents",
|
|
49
|
+
"rpc"
|
|
50
|
+
],
|
|
51
|
+
"author": "headless.ly",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/headlessly/headless.ly"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://headless.ly",
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
}
|
|
61
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* headless.ly - Headless SaaS Platform for AI Agents
|
|
3
|
+
* @generated from .db/ schemas
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RPCClient } from 'rpc.do'
|
|
7
|
+
|
|
8
|
+
// RPC exports
|
|
9
|
+
export { rpc, $ } from 'rpc.do'
|
|
10
|
+
|
|
11
|
+
// Service clients
|
|
12
|
+
export declare function crm(options?: { apiKey?: string }): RPCClient
|
|
13
|
+
export declare function sell(options?: { apiKey?: string }): RPCClient
|
|
14
|
+
export declare function market(options?: { apiKey?: string }): RPCClient
|
|
15
|
+
export declare function erp(options?: { apiKey?: string }): RPCClient
|
|
16
|
+
export declare function support(options?: { apiKey?: string }): RPCClient
|
|
17
|
+
export declare function analytics(options?: { apiKey?: string }): RPCClient
|
|
18
|
+
export declare function db(options?: { apiKey?: string }): RPCClient
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Entity Types (generated from .db/schema/)
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
// TODO: Generate from .db/schema/*.ts
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// RPC Method Types
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
// TODO: Generate from RPC definitions
|