clivly 0.7.2 → 0.7.4
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 +362 -110
- package/drizzle.cjs +7 -3
- package/drizzle.d.cts +1 -9
- package/drizzle.d.mts +1 -9
- package/drizzle.mjs +7 -3
- package/index.cjs +736 -118
- package/index.d.cts +6 -0
- package/index.d.mts +6 -0
- package/index.mjs +737 -119
- package/internal-entity-heuristics.cjs +27 -7
- package/internal-entity-heuristics.mjs +27 -7
- package/namespace-probe-B4WE45TH.d.mts +372 -0
- package/namespace-probe-DRzOtaRT.d.cts +372 -0
- package/package.json +1 -1
- package/{sdk-iqOXeWIp.cjs → sdk-Bj-ZDf4u.cjs} +126 -28
- package/{sdk-C6WANW8M.mjs → sdk-BvRD2jAp.mjs} +126 -28
- package/sdk.cjs +1 -1
- package/sdk.d.cts +36 -1
- package/sdk.d.mts +36 -1
- package/sdk.mjs +1 -1
- package/vite.d.cts +1 -1
- package/vite.d.mts +1 -1
- package/types-CsL0rFYh.d.cts +0 -155
- package/types-CsL0rFYh.d.mts +0 -155
package/README.md
CHANGED
|
@@ -1,189 +1,441 @@
|
|
|
1
1
|
# clivly
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The consolidated public package for [Clivly](https://clivly.com) — the CRM that
|
|
4
|
+
lives inside your app. It is two things in one install:
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
- **A CLI** (`npx clivly …`) that scaffolds your integration, pairs your machine
|
|
7
|
+
with a workspace, and diagnoses the setup.
|
|
8
|
+
- **A runtime**, imported from typed subpaths (`clivly/sdk`, `clivly/drizzle`,
|
|
9
|
+
`clivly/vite`, `clivly/auth/*`).
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
> **The canonical end-to-end integration guide is
|
|
12
|
+
> [Connecting to Clivly Cloud](https://github.com/amani-joseph/clivly.com/blob/master/docs/guides/connecting-to-clivly-cloud.md).**
|
|
13
|
+
> This README is the command-level reference.
|
|
9
14
|
|
|
10
|
-
##
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
| Requirement | Version / note |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| Node.js | **≥ 22** |
|
|
20
|
+
| An existing app | The CLI scaffolds into a project you already have |
|
|
21
|
+
| Drizzle ORM | Required for `clivly init` to generate a config. Prisma/Kysely/raw-SQL projects hand-write a `fetchPage` source instead |
|
|
22
|
+
| `cloudflared` on `PATH` | Only for `clivly dev` |
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
11
25
|
|
|
12
26
|
```bash
|
|
13
27
|
npm install clivly
|
|
14
28
|
```
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
This package requires Node `>=22`.
|
|
30
|
+
Published on npm's `latest` tag. Once installed, `npx clivly …` uses your local
|
|
31
|
+
copy rather than re-fetching. Use `@next` only when deliberately testing a
|
|
32
|
+
prerelease cut.
|
|
20
33
|
|
|
21
|
-
##
|
|
34
|
+
## Five-minute quick start
|
|
22
35
|
|
|
23
36
|
```bash
|
|
24
|
-
|
|
25
|
-
npx clivly
|
|
26
|
-
npx clivly
|
|
27
|
-
npx clivly status # check your setup, step by step
|
|
28
|
-
npx clivly dev # tunnel localhost so remote sync can reach you
|
|
29
|
-
npx clivly push-schema # report your schema to Clivly without deploying
|
|
37
|
+
npm install clivly
|
|
38
|
+
npx clivly init # scaffold config + routes + .env, then pair this machine
|
|
39
|
+
npx clivly status # check every setup step until they all pass
|
|
30
40
|
```
|
|
31
41
|
|
|
32
|
-
`
|
|
33
|
-
`
|
|
42
|
+
`clivly init` writes exactly three files (plus `.env` keys) — a
|
|
43
|
+
`clivly.config.ts` you own, and two route files Clivly owns. It creates no
|
|
44
|
+
database tables and does not modify your ORM schema.
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
---
|
|
36
47
|
|
|
37
|
-
|
|
48
|
+
## Commands
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
| Command | What it does |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| [`clivly init`](#clivly-init) | Scaffold config, routes and env placeholders for your app |
|
|
53
|
+
| [`clivly login`](#clivly-login) | Pair this machine and write credentials to your env file |
|
|
54
|
+
| [`clivly status`](#clivly-status) | Check your setup, step by step. Read-only |
|
|
55
|
+
| [`clivly add widget`](#clivly-add-widget) | Scaffold the chat-session route |
|
|
56
|
+
| [`clivly push-schema`](#clivly-push-schema) | Report your schema to Clivly without deploying |
|
|
57
|
+
| [`clivly dev`](#clivly-dev) | Tunnel localhost so remote sync can reach you |
|
|
58
|
+
| `clivly --version` | Print the installed `clivly` version |
|
|
59
|
+
| [`clivly ping` / `connect` / `doctor`](#deprecated-aliases) | Deprecated aliases running parts of `status` |
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
entry is the CLI.
|
|
61
|
+
Every command accepts `--help` (or `-h`), which **describes without acting**.
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
what your app actually uses, such as `drizzle-orm`, `vite`, `@clerk/backend`,
|
|
53
|
-
`@workos-inc/node`, or `better-auth`.
|
|
63
|
+
---
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
### `clivly init`
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
```bash
|
|
68
|
+
npx clivly init [--force] [--dry-run] [--diff] [--yes] [--no-login] [--no-browser]
|
|
69
|
+
[--schema <path|package>] [--db-import <specifier>]
|
|
70
|
+
[--auth-import <specifier>] [--contact-table <table>]
|
|
71
|
+
[--company-table <table>]
|
|
72
|
+
```
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
- the supported Clivly route files for your framework
|
|
62
|
-
- missing `.env` placeholders
|
|
74
|
+
Detects your framework, ORM, auth adapter and app layout, then scaffolds:
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
| File | Owned by Clivly |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `clivly.config.ts` | No — yours to edit |
|
|
79
|
+
| The tick route for your framework | Yes — `--force` to overwrite |
|
|
80
|
+
| The auth-verify route for your framework | Yes — `--force` to overwrite |
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
Plus `CLIVLY_SECRET_KEY`, `CLIVLY_SYNC_TRIGGER_URL` and
|
|
83
|
+
`CLIVLY_SYNC_TRIGGER_SECRET` placeholders in your env file.
|
|
69
84
|
|
|
70
85
|
| Flag | Purpose |
|
|
71
86
|
| --- | --- |
|
|
72
|
-
| `--
|
|
73
|
-
| `--
|
|
74
|
-
| `--
|
|
75
|
-
| `--
|
|
76
|
-
| `--
|
|
77
|
-
| `--
|
|
78
|
-
| `--
|
|
79
|
-
| `--
|
|
87
|
+
| `--dry-run` | Show what would be written; write nothing |
|
|
88
|
+
| `--diff` | Show a diff for files that already exist |
|
|
89
|
+
| `--force` | Overwrite the files Clivly owns |
|
|
90
|
+
| `--yes` | Skip prompts |
|
|
91
|
+
| `--no-login` | Skip browser pairing |
|
|
92
|
+
| `--no-browser` | Pair, but print the URL instead of opening it |
|
|
93
|
+
| `--schema <path\|package>` | Where your Drizzle tables live |
|
|
94
|
+
| `--db-import <specifier>` | Where the Drizzle instance lives, when it is not beside the schema. Accepts `@acme/database#database` to name the export |
|
|
95
|
+
| `--auth-import <specifier>` | Where your better-auth instance lives. Same `#exportName` form. Overrides on-disk probing, which cannot see across a package boundary |
|
|
96
|
+
| `--contact-table <name>` | Use this table as the contact entity, skipping the naming heuristics |
|
|
97
|
+
| `--company-table <name>` | Same for companies |
|
|
98
|
+
|
|
99
|
+
`--contact-table` and `--company-table` accept either the SQL table name or the
|
|
100
|
+
name it is exported under in your schema module — whichever you have in front of
|
|
101
|
+
you.
|
|
102
|
+
|
|
103
|
+
When `init` finishes and `CLIVLY_SECRET_KEY` is still empty, it hands you
|
|
104
|
+
straight into `clivly login` so you don't end up staring at a blank env file.
|
|
105
|
+
That step is interactive, so it is skipped automatically when there is no TTY or
|
|
106
|
+
when `CI` is set.
|
|
107
|
+
|
|
108
|
+
#### Framework support
|
|
109
|
+
|
|
110
|
+
Route scaffolding exists for these five. Anywhere else, mount the handler
|
|
111
|
+
yourself — it is a plain `(Request) => Promise<Response>`.
|
|
112
|
+
|
|
113
|
+
| Framework | Tick route written |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| Next.js | `app/api/clivly/tick/route.ts` (or `src/app/…`) |
|
|
116
|
+
| TanStack Start | `src/routes/api/clivly/tick.ts` |
|
|
117
|
+
| SvelteKit | `src/routes/api/clivly/tick/+server.ts` |
|
|
118
|
+
| Remix | `app/routes/api.clivly.tick.ts` |
|
|
119
|
+
| Nuxt | `server/api/clivly/tick.ts` |
|
|
120
|
+
|
|
121
|
+
Plain Vue apps can still integrate Clivly, but they need a manual server layer
|
|
122
|
+
for the tick route unless they are on Nuxt.
|
|
80
123
|
|
|
81
|
-
|
|
82
|
-
generating a broken config.
|
|
124
|
+
#### ORM support
|
|
83
125
|
|
|
84
|
-
|
|
126
|
+
`init` generates a config for **Drizzle only**. Prisma and Kysely projects
|
|
127
|
+
integrate through a hand-written `fetchPage` source — see the
|
|
128
|
+
[Non-Drizzle sources](https://github.com/amani-joseph/clivly.com/blob/master/docs/guides/connecting-to-clivly-cloud.md#non-drizzle-sources)
|
|
129
|
+
section of the guide.
|
|
85
130
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- SvelteKit
|
|
89
|
-
- Remix
|
|
90
|
-
- Nuxt
|
|
131
|
+
Use `--schema` when your Drizzle schema is not in a common location or in
|
|
132
|
+
`drizzle.config.ts`.
|
|
91
133
|
|
|
92
|
-
|
|
134
|
+
#### Monorepos
|
|
135
|
+
|
|
136
|
+
In a workspace, `init` reads your `pnpm-workspace.yaml` (or `workspaces` array)
|
|
137
|
+
and imports anything owned by a *sibling* package by **package name** rather
|
|
138
|
+
than by a relative path that climbs out of your app:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import * as schema from "@acme/database"; // not "../../packages/database/src/index"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`--schema` accepts a package specifier as well as a path:
|
|
93
145
|
|
|
94
146
|
```bash
|
|
95
|
-
npx clivly
|
|
147
|
+
npx clivly init --schema @acme/database # the package entry
|
|
148
|
+
npx clivly init --schema @acme/database/src/schema.ts # a subpath inside it
|
|
149
|
+
npx clivly init --schema ./src/db/schema.ts # a plain path
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
A real file always wins over a same-named package, so existing relative paths
|
|
153
|
+
keep their meaning.
|
|
154
|
+
|
|
155
|
+
#### When no contact entity can be found
|
|
156
|
+
|
|
157
|
+
`init` **writes nothing** rather than scaffolding a config that cannot compile.
|
|
158
|
+
Clivly needs one entity before a config will load, and it will not invent one:
|
|
159
|
+
|
|
96
160
|
```
|
|
161
|
+
No usable contact entity
|
|
97
162
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
163
|
+
Tables in @acme/database:
|
|
164
|
+
widgets
|
|
165
|
+
invoices
|
|
166
|
+
|
|
167
|
+
Pick the one that holds your people and re-run:
|
|
168
|
+
clivly init --schema @acme/database --contact-table widgets
|
|
169
|
+
|
|
170
|
+
Nothing was written — Clivly needs one entity before a config can load.
|
|
171
|
+
```
|
|
101
172
|
|
|
102
|
-
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### `clivly login`
|
|
103
176
|
|
|
104
177
|
```bash
|
|
105
178
|
npx clivly login [--force] [--rotate] [--api-url <url>] [--no-browser]
|
|
106
179
|
```
|
|
107
180
|
|
|
108
|
-
Pairs this machine with a Clivly workspace and writes
|
|
181
|
+
Pairs this machine with a Clivly workspace and writes `CLIVLY_SECRET_KEY` and
|
|
182
|
+
`CLIVLY_SYNC_TRIGGER_SECRET` into your project's env file. It creates a pairing
|
|
183
|
+
session, prints (and by default opens) an approval link, and waits while you
|
|
184
|
+
approve it in a signed-in browser. The approval link is always printed, so an
|
|
185
|
+
SSH or headless session can be approved by opening the link elsewhere.
|
|
186
|
+
|
|
187
|
+
| Flag | Effect |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| `--no-browser` | Print the approval URL without opening a browser |
|
|
190
|
+
| `--force` | Overwrite env values that already have a real value. Without it, existing values are kept and reported as skipped; an empty placeholder (`CLIVLY_SECRET_KEY=`) is always filled in |
|
|
191
|
+
| `--rotate` | Rotate the workspace's remote-sync trigger secret instead of revealing the current one. **Your already-deployed app stops receiving sync triggers until you redeploy it with the new secret** |
|
|
192
|
+
| `--api-url <url>` | Point at a non-default backend |
|
|
193
|
+
|
|
194
|
+
Before anything is written, the CLI prints which workspace approved the pairing
|
|
195
|
+
and asks you to confirm. Declining writes nothing and exits non-zero. In a
|
|
196
|
+
non-interactive run (no TTY, or `CI` set) the workspace is still printed but you
|
|
197
|
+
are not prompted, so scripted pairing cannot hang.
|
|
198
|
+
|
|
199
|
+
#### Which env file gets written
|
|
109
200
|
|
|
110
|
-
|
|
111
|
-
|
|
201
|
+
The CLI follows your framework's convention. This is not cosmetic on Workers:
|
|
202
|
+
wrangler reads `.dev.vars` and nothing else, so credentials written to `.env`
|
|
203
|
+
would never load.
|
|
112
204
|
|
|
113
|
-
|
|
205
|
+
| Framework | Env file |
|
|
206
|
+
| --- | --- |
|
|
207
|
+
| Next.js | `.env.local` |
|
|
208
|
+
| TanStack Start, SvelteKit, Remix, Nuxt | `.env` |
|
|
209
|
+
| Hono on Cloudflare Workers | `.dev.vars` |
|
|
210
|
+
| Anything else | `.env` |
|
|
114
211
|
|
|
115
|
-
|
|
116
|
-
approved in another browser.
|
|
212
|
+
#### Pairing availability
|
|
117
213
|
|
|
118
|
-
|
|
214
|
+
The pairing endpoints are feature-flagged server-side
|
|
215
|
+
(`CLIVLY_CLI_PAIRING_ENABLED`). **Clivly Cloud has pairing enabled**, so
|
|
216
|
+
`clivly login` is the normal path there. The flag defaults to `false`, so a
|
|
217
|
+
self-hosted backend must opt in — otherwise `clivly login` says so and exits
|
|
218
|
+
non-zero.
|
|
119
219
|
|
|
120
|
-
|
|
121
|
-
- `--rotate` rotates the remote-sync trigger secret instead of revealing the
|
|
122
|
-
current one
|
|
123
|
-
- pairing is feature-flagged server-side with `CLIVLY_CLI_PAIRING_ENABLED`
|
|
220
|
+
#### Manual credential path
|
|
124
221
|
|
|
125
|
-
If pairing is unavailable,
|
|
126
|
-
|
|
222
|
+
If pairing is unavailable, every credential can be set by hand. Nothing about
|
|
223
|
+
the CLI requires pairing.
|
|
127
224
|
|
|
128
|
-
|
|
225
|
+
| Value | Where to get it | Notes |
|
|
226
|
+
| --- | --- | --- |
|
|
227
|
+
| `CLIVLY_SECRET_KEY` | Dashboard → **Connect app**, or **Settings → API keys** | **Shown once.** Copy it immediately; you cannot retrieve it later, only regenerate. Regenerating **revokes the previous key**, so redeploy after rotating. The legacy name `CLIVLY_API_KEY` is still accepted as a fallback |
|
|
228
|
+
| `CLIVLY_SYNC_TRIGGER_SECRET` | Dashboard → **Integrations → Remote sync** | Must match the value in your app's env exactly. A mismatch surfaces as a `401` on the signed trigger |
|
|
229
|
+
| `CLIVLY_SYNC_TRIGGER_URL` | Your own deployment | The public URL of your mounted tick route, e.g. `https://app.example.com/api/clivly/tick`. Optional until you deploy — push-only setups can leave it unset |
|
|
230
|
+
| `CLIVLY_PUBLISHABLE_KEY` | Same place as the secret key | Only needed for the chat widget. Safe to expose in client code |
|
|
231
|
+
|
|
232
|
+
Write them into the env file of the package that imports `clivly`, then re-run
|
|
233
|
+
`clivly status` to confirm each step goes green.
|
|
234
|
+
|
|
235
|
+
> **Running your own Clivly backend?** `CLIVLY_TRIGGER_SECRET_WRAPPING_KEY` (a
|
|
236
|
+
> 32-byte hex key) is a **hard production prerequisite** for pairing. The
|
|
237
|
+
> trigger secret is stored encrypted with it, so without the key set on the
|
|
238
|
+
> server, `clivly login` fails with a 500 at the moment it would hand over
|
|
239
|
+
> credentials. Set it before enabling `CLIVLY_CLI_PAIRING_ENABLED`.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### `clivly status`
|
|
129
244
|
|
|
130
245
|
```bash
|
|
131
246
|
npx clivly status [--verbose] [--config <path>] [--env-file <path>]
|
|
132
247
|
```
|
|
133
248
|
|
|
134
|
-
Walks
|
|
135
|
-
|
|
249
|
+
Walks your setup one step at a time and stops at the first thing that isn't
|
|
250
|
+
ready, telling you what to do about it. **This is the command to reach for when
|
|
251
|
+
something isn't working.** It is read-only and writes nothing.
|
|
252
|
+
|
|
253
|
+
The ladder: credential → Clivly reachable → `clivly.config` loads →
|
|
254
|
+
`clivly.config` is edge-safe → setup checks → sync trigger URL.
|
|
255
|
+
|
|
256
|
+
| Flag | Effect |
|
|
257
|
+
| --- | --- |
|
|
258
|
+
| `--verbose` | Show every check, including those that passed |
|
|
259
|
+
| `--config <path>` | Path to `clivly.config.ts` (needed in a monorepo) |
|
|
260
|
+
| `--env-file <path>` | Extra env file to load. Repeatable; later files win |
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npx clivly status --config ./apps/web/clivly.config.ts
|
|
264
|
+
npx clivly status --env-file ../server/.env
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Env precedence, strongest first: **real environment → `--env-file` / the
|
|
268
|
+
`envFiles` array in `clivly.cli.ts` → `.env.local` → `.env`**.
|
|
269
|
+
|
|
270
|
+
---
|
|
136
271
|
|
|
137
|
-
|
|
272
|
+
### `clivly add widget`
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx clivly add widget [--force] [--dry-run]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Writes the route that mints chat-widget sessions, for your detected framework
|
|
279
|
+
(e.g. `app/api/clivly/widget/session/route.ts` on Next.js). Idempotent.
|
|
280
|
+
|
|
281
|
+
It creates **no widget record** — that lives in the Clivly dashboard under
|
|
282
|
+
**Settings → Chat widget**. See
|
|
283
|
+
[Adding the chat widget](https://github.com/amani-joseph/clivly.com/blob/master/docs/guides/adding-the-chat-widget.md)
|
|
284
|
+
for the full flow.
|
|
285
|
+
|
|
286
|
+
| Flag | Effect |
|
|
287
|
+
| --- | --- |
|
|
288
|
+
| `--dry-run` | Show what would be written; write nothing |
|
|
289
|
+
| `--force` | Overwrite the route if it already exists |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
### `clivly push-schema`
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npx clivly push-schema [--config <path>]
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Sends your discovered tables and columns to Clivly so the dashboard can offer
|
|
300
|
+
them for mapping — without deploying your app. Reads your config; writes nothing
|
|
301
|
+
locally. **Table and column names only; never row data.**
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
### `clivly dev`
|
|
138
306
|
|
|
139
307
|
```bash
|
|
140
308
|
npx clivly dev [--port <n>] [--api-url <url>] [--route-path <path>] [-- <cmd>]
|
|
141
309
|
```
|
|
142
310
|
|
|
143
311
|
Opens a `cloudflared` tunnel to your local app and registers it with Clivly for
|
|
144
|
-
the
|
|
145
|
-
|
|
312
|
+
the length of the session, so remote sync can reach `localhost` while you
|
|
313
|
+
develop. Your dev server is detected from `package.json` scripts, or pass it
|
|
314
|
+
explicitly after `--`. Needs `cloudflared` on `PATH` and `CLIVLY_SECRET_KEY`
|
|
315
|
+
set. The override is cleared when the process exits; remote sync is otherwise
|
|
316
|
+
deploy-only.
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
npx clivly dev --port 3000 -- npm run dev
|
|
320
|
+
```
|
|
146
321
|
|
|
147
|
-
|
|
322
|
+
It also watches `clivly.config.ts` and tells you when it changes. It cannot
|
|
323
|
+
reload it for you — the config belongs to your app's process, not to the CLI.
|
|
148
324
|
|
|
149
|
-
|
|
150
|
-
|
|
325
|
+
If registration fails, the warning names the cause: an invalid key, a backend
|
|
326
|
+
without the endpoint enabled, an HTTP status Clivly returned, or a genuine
|
|
327
|
+
network failure. Your app keeps running either way, and outbound sync (your app
|
|
328
|
+
→ Clivly) is unaffected — only dashboard-triggered sync needs the tunnel.
|
|
151
329
|
|
|
152
|
-
|
|
330
|
+
| Flag | Effect |
|
|
331
|
+
| --- | --- |
|
|
332
|
+
| `--port <n>` | Local port your app listens on |
|
|
333
|
+
| `--route-path <path>` | Path to your mounted tick route |
|
|
334
|
+
| `--api-url <url>` | Point at a non-default backend |
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### Deprecated aliases
|
|
339
|
+
|
|
340
|
+
`ping`, `connect` and `doctor` each run part of the `status` ladder. They still
|
|
341
|
+
work, and are kept because output and docs have recommended them — but prefer
|
|
342
|
+
`clivly status`, which runs the whole ladder and tells you which step you are on.
|
|
343
|
+
|
|
344
|
+
| Alias | Equivalent scope |
|
|
345
|
+
| --- | --- |
|
|
346
|
+
| `clivly ping` | Credential + reachability |
|
|
347
|
+
| `clivly connect` | The above, plus config and trigger URL |
|
|
348
|
+
| `clivly doctor` | Config, setup checks and edge safety |
|
|
349
|
+
|
|
350
|
+
Each prints a deprecation notice, then the steps it covers, renumbered to its own
|
|
351
|
+
scope:
|
|
153
352
|
|
|
154
353
|
```bash
|
|
155
|
-
npx clivly
|
|
354
|
+
$ npx clivly ping
|
|
355
|
+
`clivly ping` is a deprecated alias that runs part of `clivly status`. Prefer `clivly status`.
|
|
356
|
+
|
|
357
|
+
Clivly status
|
|
358
|
+
✅ Step 1 of 2 · API key present — Secret key is set.
|
|
359
|
+
✅ Step 2 of 2 · Clivly reachable — Reached org: acme (org_1)
|
|
156
360
|
```
|
|
157
361
|
|
|
158
|
-
|
|
159
|
-
|
|
362
|
+
`clivly doctor` runs the same read-only checks as `await clivly.doctor()` in
|
|
363
|
+
code: API key present, entities configured, sources configured, concept
|
|
364
|
+
coverage, Drizzle cursor/id columns, sample fetch, mapping dry-run,
|
|
365
|
+
custom-object slugs, heartbeat, and namespace reachability.
|
|
160
366
|
|
|
161
|
-
|
|
367
|
+
---
|
|
162
368
|
|
|
163
|
-
|
|
369
|
+
## Runtime imports
|
|
164
370
|
|
|
165
|
-
`clivly
|
|
166
|
-
|
|
371
|
+
`clivly` is the consolidated public package. Application code imports from typed
|
|
372
|
+
subpaths:
|
|
167
373
|
|
|
168
|
-
|
|
374
|
+
```ts
|
|
375
|
+
import { createClivlySDK, defineClivlyConfig } from "clivly/sdk";
|
|
376
|
+
import { fromDrizzle, discoverFromDrizzle } from "clivly/drizzle";
|
|
377
|
+
import { clivlyVite } from "clivly/vite";
|
|
378
|
+
import { createClerkAuthAdapter } from "clivly/auth/clerk";
|
|
379
|
+
import { createWorkOSAuthAdapter } from "clivly/auth/workos";
|
|
380
|
+
import { createBetterAuthAdapter } from "clivly/auth/better-auth";
|
|
381
|
+
```
|
|
169
382
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
383
|
+
> **Never `import` from the bare `clivly` specifier.** The package root is the
|
|
384
|
+
> CLI binary — it imports `node:fs` and `node:child_process` at module scope, so
|
|
385
|
+
> importing it from app code drags the whole CLI into your bundle and throws at
|
|
386
|
+
> import time on Workers and other edge runtimes.
|
|
387
|
+
|
|
388
|
+
Provider and framework integrations use **optional peer dependencies**. Install
|
|
389
|
+
only what your app actually uses:
|
|
390
|
+
|
|
391
|
+
| Subpath | Peer dependency |
|
|
392
|
+
| --- | --- |
|
|
393
|
+
| `clivly/drizzle` | `drizzle-orm` |
|
|
394
|
+
| `clivly/vite` | `vite` |
|
|
395
|
+
| `clivly/auth/clerk` | `@clerk/backend` |
|
|
396
|
+
| `clivly/auth/workos` | `@workos-inc/node` |
|
|
397
|
+
| `clivly/auth/better-auth` | none — you pass your own `betterAuth({...})` instance |
|
|
398
|
+
|
|
399
|
+
`clivly/core/*` is deprecated in favour of `clivly/internal/*`. It still
|
|
400
|
+
resolves, and emits a one-time `process.emitWarning` naming the replacement (so
|
|
401
|
+
it stays off stdout and cannot corrupt machine-readable CLI output). See
|
|
402
|
+
[STABILITY.md](https://github.com/amani-joseph/clivly.com/blob/master/STABILITY.md).
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Common errors
|
|
407
|
+
|
|
408
|
+
| Symptom | Fix |
|
|
409
|
+
| --- | --- |
|
|
410
|
+
| Generated code doesn't match these docs | `npx clivly --version` — an older installed version is the usual cause |
|
|
411
|
+
| `init` refuses to write, "No usable contact entity" | Pass `--contact-table <name>` |
|
|
412
|
+
| `init` cannot find your schema | Pass `--schema <path\|package>` |
|
|
413
|
+
| `init` scaffolded a `db` import that doesn't resolve | The probe cannot evaluate a db module that throws without `DATABASE_URL`. Fix the `TODO(clivly)` import path it left, or pass `--db-import` |
|
|
414
|
+
| `clivly login` exits non-zero, pairing unavailable | The backend has `CLIVLY_CLI_PAIRING_ENABLED` off. Use the manual credential path above |
|
|
415
|
+
| `clivly dev` cannot start | Install `cloudflared`, or set `CLIVLY_SYNC_TRIGGER_URL` to a tunnel you run yourself |
|
|
416
|
+
| Credentials written but the app never loads them | Wrong env file for your framework — see the table under `clivly login` |
|
|
417
|
+
|
|
418
|
+
The full troubleshooting table is in the
|
|
419
|
+
[integration guide](https://github.com/amani-joseph/clivly.com/blob/master/docs/guides/connecting-to-clivly-cloud.md#8-common-errors-and-troubleshooting).
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Telemetry
|
|
173
424
|
|
|
174
|
-
|
|
175
|
-
|
|
425
|
+
**Clivly collects no telemetry.** The CLI makes network requests only to the
|
|
426
|
+
Clivly API you point it at, and only to do what you asked: pairing, heartbeats,
|
|
427
|
+
schema pushes, and status checks. No usage analytics, no crash reporting, no
|
|
428
|
+
phone-home. See
|
|
429
|
+
[SECURITY.md](https://github.com/amani-joseph/clivly.com/blob/master/SECURITY.md#telemetry).
|
|
176
430
|
|
|
177
|
-
##
|
|
431
|
+
## Related documentation
|
|
178
432
|
|
|
179
|
-
- Cloud
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
-
|
|
184
|
-
|
|
185
|
-
- Release notes:
|
|
186
|
-
[CHANGELOG.md](https://github.com/amani-joseph/clivly.com/blob/master/CHANGELOG.md)
|
|
433
|
+
- **[Connecting to Clivly Cloud](https://github.com/amani-joseph/clivly.com/blob/master/docs/guides/connecting-to-clivly-cloud.md)** — the canonical end-to-end guide
|
|
434
|
+
- [Adding the chat widget](https://github.com/amani-joseph/clivly.com/blob/master/docs/guides/adding-the-chat-widget.md)
|
|
435
|
+
- [`@clivly/sdk` README](https://github.com/amani-joseph/clivly.com/blob/master/packages/sdk/README.md) — the runtime API surface
|
|
436
|
+
- [STABILITY.md](https://github.com/amani-joseph/clivly.com/blob/master/STABILITY.md) — import tiers
|
|
437
|
+
- [SECURITY.md](https://github.com/amani-joseph/clivly.com/blob/master/SECURITY.md)
|
|
438
|
+
- [CHANGELOG.md](https://github.com/amani-joseph/clivly.com/blob/master/CHANGELOG.md)
|
|
187
439
|
|
|
188
440
|
## License
|
|
189
441
|
|
package/drizzle.cjs
CHANGED
|
@@ -65,11 +65,15 @@ function fromDrizzle(db, table, opts) {
|
|
|
65
65
|
const idColumn = columns[idField];
|
|
66
66
|
if (!cursorColumn) throw new Error(`fromDrizzle: cursorField "${opts.cursorField}" is not a column on "${(0, drizzle_orm.getTableName)(table)}".`);
|
|
67
67
|
if (!idColumn) throw new Error(`fromDrizzle: idField "${idField}" is not a column on "${(0, drizzle_orm.getTableName)(table)}".`);
|
|
68
|
+
const projection = {};
|
|
69
|
+
for (const column of Object.values(columns)) projection[column.name] = column;
|
|
70
|
+
const cursorColumnName = cursorColumn.name;
|
|
71
|
+
const idColumnName = idColumn.name;
|
|
68
72
|
const source = {
|
|
69
73
|
entity: opts.entity,
|
|
70
|
-
cursorField:
|
|
71
|
-
idField,
|
|
72
|
-
fetchPage: ({ since, limit }) => drizzleDb.select().from(table).where(since ? drizzle_orm.sql`(${cursorColumn}, ${idColumn}) > (${since.time}, ${since.id})` : void 0).orderBy((0, drizzle_orm.asc)(cursorColumn), (0, drizzle_orm.asc)(idColumn)).limit(limit)
|
|
74
|
+
cursorField: cursorColumnName,
|
|
75
|
+
idField: idColumnName,
|
|
76
|
+
fetchPage: ({ since, limit }) => drizzleDb.select(projection).from(table).where(since ? drizzle_orm.sql`(${cursorColumn}, ${idColumn}) > (${since.time}, ${since.id})` : void 0).orderBy((0, drizzle_orm.asc)(cursorColumn), (0, drizzle_orm.asc)(idColumn)).limit(limit)
|
|
73
77
|
};
|
|
74
78
|
const meta = {
|
|
75
79
|
columnKeys: Object.keys(columns),
|
package/drizzle.d.cts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { t as discoverFromDrizzle } from "./drizzle-DmeKSqeF.cjs";
|
|
2
|
-
import {
|
|
2
|
+
import { m as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-DRzOtaRT.cjs";
|
|
3
3
|
import { Table } from "drizzle-orm";
|
|
4
|
-
//#region ../sdk/src/namespace-probe.d.ts
|
|
5
|
-
interface NamespaceIntrospector {
|
|
6
|
-
/** All base table names in the reachable schema. */
|
|
7
|
-
listTables(): Promise<string[]>;
|
|
8
|
-
/** Prove a table is genuinely selectable (SELECT ... LIMIT 1). Throws if unreachable/denied. */
|
|
9
|
-
sampleSelect(table: string): Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
4
|
//#region ../sdk/src/drizzle.d.ts
|
|
13
5
|
interface DrizzleDb {
|
|
14
6
|
select: (...args: any[]) => any;
|
package/drizzle.d.mts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { t as discoverFromDrizzle } from "./drizzle-CFOr9dU9.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { m as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-B4WE45TH.mjs";
|
|
3
3
|
import { Table } from "drizzle-orm";
|
|
4
|
-
//#region ../sdk/src/namespace-probe.d.ts
|
|
5
|
-
interface NamespaceIntrospector {
|
|
6
|
-
/** All base table names in the reachable schema. */
|
|
7
|
-
listTables(): Promise<string[]>;
|
|
8
|
-
/** Prove a table is genuinely selectable (SELECT ... LIMIT 1). Throws if unreachable/denied. */
|
|
9
|
-
sampleSelect(table: string): Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
4
|
//#region ../sdk/src/drizzle.d.ts
|
|
13
5
|
interface DrizzleDb {
|
|
14
6
|
select: (...args: any[]) => any;
|
package/drizzle.mjs
CHANGED
|
@@ -64,11 +64,15 @@ function fromDrizzle(db, table, opts) {
|
|
|
64
64
|
const idColumn = columns[idField];
|
|
65
65
|
if (!cursorColumn) throw new Error(`fromDrizzle: cursorField "${opts.cursorField}" is not a column on "${getTableName(table)}".`);
|
|
66
66
|
if (!idColumn) throw new Error(`fromDrizzle: idField "${idField}" is not a column on "${getTableName(table)}".`);
|
|
67
|
+
const projection = {};
|
|
68
|
+
for (const column of Object.values(columns)) projection[column.name] = column;
|
|
69
|
+
const cursorColumnName = cursorColumn.name;
|
|
70
|
+
const idColumnName = idColumn.name;
|
|
67
71
|
const source = {
|
|
68
72
|
entity: opts.entity,
|
|
69
|
-
cursorField:
|
|
70
|
-
idField,
|
|
71
|
-
fetchPage: ({ since, limit }) => drizzleDb.select().from(table).where(since ? sql`(${cursorColumn}, ${idColumn}) > (${since.time}, ${since.id})` : void 0).orderBy(asc(cursorColumn), asc(idColumn)).limit(limit)
|
|
73
|
+
cursorField: cursorColumnName,
|
|
74
|
+
idField: idColumnName,
|
|
75
|
+
fetchPage: ({ since, limit }) => drizzleDb.select(projection).from(table).where(since ? sql`(${cursorColumn}, ${idColumn}) > (${since.time}, ${since.id})` : void 0).orderBy(asc(cursorColumn), asc(idColumn)).limit(limit)
|
|
72
76
|
};
|
|
73
77
|
const meta = {
|
|
74
78
|
columnKeys: Object.keys(columns),
|