@littlefriend/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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +566 -0
  3. package/dist/index.js +5158 -0
  4. package/package.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZVN DEV
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,566 @@
1
+ # @littlefriend/cli
2
+
3
+ Set up Little Friend from the terminal: create a project, print the install snippet for your framework, write secret keys straight into an env file, and check that events arrive. Every command takes `--json`, so a coding agent can do the whole setup and read the results.
4
+
5
+ No dependencies. Node 22 or later.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npx @littlefriend/cli login
11
+ ```
12
+
13
+ Or install it once and use `littlefriend`:
14
+
15
+ ```sh
16
+ npm install -g @littlefriend/cli
17
+ littlefriend login
18
+ ```
19
+
20
+ ## Set up a site
21
+
22
+ ```sh
23
+ littlefriend login
24
+ littlefriend projects create --name "Acme" --domain acme.com --mode journey
25
+ littlefriend snippet --framework next
26
+ littlefriend keys create --kind server --env-file .env.local
27
+ littlefriend verify
28
+ ```
29
+
30
+ `snippet` detects the framework in the current directory when you leave out `--framework`, and falls back to the plain script tag. `verify` waits up to 60 seconds for the first event, then shows what arrived and anything the collector refused, with the reason.
31
+
32
+ `littlefriend guide` prints the full setup guide for coding agents.
33
+
34
+ ## Signing in
35
+
36
+ `littlefriend login` opens your browser to approve the CLI. It listens on `127.0.0.1` on a random port for the answer, checks it, and stores the sign-in in `~/.littlefriend/credentials.json`, readable only by you (mode 0600). Pass `--no-browser` to open the printed link yourself. The CLI waits 5 minutes for the approval.
37
+
38
+ The sign-in acts as you, with your role in each workspace: it can create and change projects, goals, funnels, keys and settings, and only read where you are a viewer. It never reaches billing, your password or invites.
39
+
40
+ The access token renews on its own. `littlefriend logout` revokes the sign-in and deletes it from the machine. You can also revoke it in the dashboard under Account, Connected apps.
41
+
42
+ For CI, set `LITTLEFRIEND_TOKEN` to an access token. It is used as is, never stored or renewed.
43
+
44
+ ## Choosing a project
45
+
46
+ Commands that act on a project take `--project`, which accepts a project id (`prj_...`), a site key (`lf_...`), a name or a domain. Leave it out when you can see a single project. `--workspace` (id, slug or name) narrows the search when you belong to several workspaces.
47
+
48
+ Set `LITTLEFRIEND_PROJECT` and `LITTLEFRIEND_WORKSPACE` to skip the flags.
49
+
50
+ ## Output
51
+
52
+ Human output goes to stdout. Progress notes and warnings go to stderr.
53
+
54
+ With `--json`, stdout carries one JSON document. On an error it is:
55
+
56
+ ```json
57
+ { "error": { "code": "NOT_SIGNED_IN", "message": "Not signed in to https://api.littlefriend.io. Run: littlefriend login" } }
58
+ ```
59
+
60
+ and stderr repeats the message. Messages from the API are repeated word for word.
61
+
62
+ | Exit code | Meaning |
63
+ |---|---|
64
+ | 0 | Done |
65
+ | 1 | Failed, or `verify` saw no events |
66
+ | 2 | Wrong flags or arguments, or a confirmation (`--yes`) is missing |
67
+ | 3 | Sign in needed: not signed in, the sign-in expired, or the token was refused |
68
+
69
+ ## Environment
70
+
71
+ | Variable | |
72
+ |---|---|
73
+ | `LITTLEFRIEND_API` | API origin. Default `https://api.littlefriend.io`. Same as `--api`. |
74
+ | `LITTLEFRIEND_WORKSPACE` | Default workspace. Same as `--workspace`. |
75
+ | `LITTLEFRIEND_PROJECT` | Default project. Same as `--project`. |
76
+ | `LITTLEFRIEND_TOKEN` | Access token to use instead of the stored sign-in. |
77
+ | `LITTLEFRIEND_HOME` | Directory for `credentials.json`. Default `~/.littlefriend`. |
78
+
79
+ ## Commands
80
+
81
+ The JSON shapes below list the top-level keys. Where a command returns an API response unchanged, the type is named from `@littlefriend/contract`.
82
+
83
+ A project in JSON is:
84
+
85
+ ```ts
86
+ {
87
+ id: string; workspaceId: string; name: string; domain: string;
88
+ publicKey: string; // the site key, lf_...
89
+ privacyMode: 'aggregate' | 'journey';
90
+ timezone: string;
91
+ allowedOrigins: string[]; // empty: every origin
92
+ campaignAllowTerm: boolean;
93
+ retentionEventsDays: number; retentionAggregatesDays: number;
94
+ installVerifiedAt: string | null; createdAt: string;
95
+ }
96
+ ```
97
+
98
+ ### Sign in
99
+
100
+ #### `login`
101
+
102
+ Signs in with your browser.
103
+
104
+ ```sh
105
+ littlefriend login
106
+ ```
107
+
108
+ ```ts
109
+ { api: string; email: string | null; scope: string | null; expiresAt: string }
110
+ ```
111
+
112
+ #### `logout`
113
+
114
+ Revokes this machine's sign-in and deletes it. Running it when not signed in does nothing and exits 0.
115
+
116
+ ```sh
117
+ littlefriend logout
118
+ ```
119
+
120
+ ```ts
121
+ { api: string; signedOut: boolean; revoked: boolean }
122
+ ```
123
+
124
+ #### `whoami`
125
+
126
+ Shows who the CLI is signed in as and your workspaces.
127
+
128
+ ```sh
129
+ littlefriend whoami
130
+ ```
131
+
132
+ ```ts
133
+ {
134
+ api: string; email: string | null;
135
+ source: 'stored' | 'env'; // env: LITTLEFRIEND_TOKEN
136
+ expiresAt: string | null;
137
+ workspaces: { id: string; name: string; slug: string; role: 'owner' | 'editor' | 'viewer' }[];
138
+ }
139
+ ```
140
+
141
+ ### Workspaces and projects
142
+
143
+ #### `workspaces list`
144
+
145
+ ```sh
146
+ littlefriend workspaces list
147
+ ```
148
+
149
+ ```ts
150
+ { workspaces: { id: string; name: string; slug: string; role: string }[] }
151
+ ```
152
+
153
+ #### `projects list`
154
+
155
+ Lists projects in every workspace you belong to, or in the one `--workspace` names.
156
+
157
+ ```sh
158
+ littlefriend projects list --workspace acme
159
+ ```
160
+
161
+ ```ts
162
+ { projects: Project[] }
163
+ ```
164
+
165
+ #### `projects create`
166
+
167
+ Creates a project. If a project in the workspace already has the domain, it prints that one instead and exits 0, so running it twice is safe.
168
+
169
+ Allowed origins default to `https://<domain>` and `https://www.<domain>`, so events from local and preview builds are refused and your numbers stay clean. `--origin` (repeatable) replaces that list. `--any-origin` accepts every origin. Other flags: `--mode aggregate|journey` (default aggregate) and `--timezone` (default this machine's time zone, as in the dashboard).
170
+
171
+ ```sh
172
+ littlefriend projects create --name "Acme" --domain acme.com --mode journey
173
+ ```
174
+
175
+ ```ts
176
+ { created: boolean; project: Project } // created: false when the domain already had a project
177
+ ```
178
+
179
+ #### `projects get`
180
+
181
+ ```sh
182
+ littlefriend projects get --project acme.com
183
+ ```
184
+
185
+ ```ts
186
+ { project: Project; role: 'owner' | 'editor' | 'viewer' }
187
+ ```
188
+
189
+ #### `projects update`
190
+
191
+ Changes a project's settings: `--name`, `--domain`, `--mode`, `--timezone`, `--events-days <1-7>`, `--aggregates-days <30-395>`, `--campaign-term` or `--no-campaign-term`. For allowed origins, `--origin` replaces the list, `--add-origin` and `--remove-origin` edit it, and `--any-origin` empties it.
192
+
193
+ ```sh
194
+ littlefriend projects update --add-origin https://staging.acme.com
195
+ ```
196
+
197
+ ```ts
198
+ { project: Project }
199
+ ```
200
+
201
+ #### `projects delete`
202
+
203
+ Deletes the project. Owners only. Needs `--yes`.
204
+
205
+ ```sh
206
+ littlefriend projects delete --project prj_... --yes
207
+ ```
208
+
209
+ ```ts
210
+ { deleted: true; project: { id: string; name: string } }
211
+ ```
212
+
213
+ #### `projects check`
214
+
215
+ The project, whether events arrive, and why any were refused. It is `verify` without waiting, and returns the same JSON with the full project.
216
+
217
+ ```sh
218
+ littlefriend projects check --project acme.com
219
+ ```
220
+
221
+ ### Install
222
+
223
+ #### `snippet`
224
+
225
+ Prints what to add to the site, with the site key filled in: the script tag, or the npm package and the file to put `init` in, plus the Content Security Policy lines. Frameworks: `html`, `next`, `nuxt`, `sveltekit`, `astro`, `remix`, `react`, `vue`, `angular`, `wordpress`, `npm`.
226
+
227
+ The mode comes from the project unless you pass `--mode`. `--consent required` waits for your consent banner (journey mode). `--replay` adds session replay. `--site-key lf_...` prints a snippet without signing in.
228
+
229
+ For a staging site, add `data-test` to the tag (or `test: true` to `init`): its events show in `verify` and never in reports.
230
+
231
+ ```sh
232
+ littlefriend snippet --framework next --mode journey --consent required
233
+ ```
234
+
235
+ ```ts
236
+ {
237
+ project?: { id: string; name: string };
238
+ framework: string; route: 'tag' | 'npm'; siteKey: string;
239
+ mode: 'aggregate' | 'journey'; consent: 'required' | 'none'; replay: boolean;
240
+ steps: { title: string; file?: string; language: string; code: string }[];
241
+ csp: string[]; // directives to add if the site sets a CSP
242
+ notes: string[];
243
+ }
244
+ ```
245
+
246
+ #### `keys list`
247
+
248
+ Lists the project's secret keys by prefix. Secrets are never shown again after creation.
249
+
250
+ ```sh
251
+ littlefriend keys list
252
+ ```
253
+
254
+ ```ts
255
+ { keys: ApiKey[] }
256
+
257
+ // ApiKey, never the secret
258
+ { id: string; kind: 'server' | 'edge' | 'read'; label: string | null; prefix: string;
259
+ createdAt: string; lastUsedAt: string | null; revokedAt: string | null;
260
+ createdBy: { id: string; name: string | null; email: string } | null }
261
+ ```
262
+
263
+ #### `keys create`
264
+
265
+ Creates a secret key: `server` for `@littlefriend/node`, `edge` for `@littlefriend/edge` and the agent door, `read` for reading reports.
266
+
267
+ With `--env-file`, the secret goes straight into the file as `NAME=secret` and is never printed. The name defaults to `LF_SERVER_KEY`, `LF_EDGE_KEY` or `LF_READ_KEY`; change it with `--env-name`. If the file already sets that name, no key is created. The CLI warns when git does not ignore the file.
268
+
269
+ ```sh
270
+ littlefriend keys create --kind server --env-file .env.local
271
+ ```
272
+
273
+ ```ts
274
+ // with --env-file
275
+ { created: true; key: ApiKey; envFile: string; envName: string }
276
+ { created: false; envFile: string; envName: string; reason: 'already_set' }
277
+ // without --env-file: the secret appears once, here
278
+ { key: ApiKey; secret: string; notice: string }
279
+ ```
280
+
281
+ #### `keys revoke`
282
+
283
+ Needs `--yes`. Requests that use the key are refused from then on.
284
+
285
+ ```sh
286
+ littlefriend keys revoke key_... --yes
287
+ ```
288
+
289
+ ```ts
290
+ { key: ApiKey }
291
+ ```
292
+
293
+ #### `verify`
294
+
295
+ Waits for the first event from the site and says what it was: time, name, route and source. Then it shows the project's health: whether the install is verified, when the last event arrived, which sources send, and requests refused in the last 24 hours with what each reason means. `origin_not_allowed`, for example, means a page on a host missing from the allowed origins sent it.
296
+
297
+ `--wait <seconds>` (default 60, 0 checks once) and `--since <minutes>` (default 60) set how long to wait and how far back an event counts. Exits 1 when nothing arrived.
298
+
299
+ ```sh
300
+ littlefriend verify --wait 120
301
+ ```
302
+
303
+ ```ts
304
+ // an event arrived, exit 0
305
+ { seen: true; project: { id: string; name: string; publicKey: string };
306
+ event: LiveEvent; events: number; sinceMinutes: number; health: DataHealth }
307
+ // nothing arrived, exit 1
308
+ { seen: false; project: { id: string; name: string; publicKey: string };
309
+ waitedSeconds: number; sinceMinutes: number;
310
+ rejected: { reason: string; count: number }[]; // refusals seen while waiting
311
+ health: DataHealth; error: { code: 'NO_EVENTS'; message: string } }
312
+ ```
313
+
314
+ `DataHealth.rejected24h` lists the refusals of the last 24 hours as `{ reason, count }`.
315
+
316
+ ### Measure
317
+
318
+ #### `goals list`
319
+
320
+ `--archived` includes archived goals.
321
+
322
+ ```sh
323
+ littlefriend goals list
324
+ ```
325
+
326
+ ```ts
327
+ { goals: GoalDefinition[] }
328
+ ```
329
+
330
+ #### `goals create`
331
+
332
+ A goal counts a page view (`--route /thanks`) or an event (`--event order.completed`, with `--source browser|server` to count one source only). `--server-confirmed` counts only outcomes confirmed by your server.
333
+
334
+ ```sh
335
+ littlefriend goals create --name "Paid" --event order.completed --source server --server-confirmed
336
+ ```
337
+
338
+ ```ts
339
+ { goal: GoalDefinition }
340
+ ```
341
+
342
+ #### `goals update`
343
+
344
+ Takes the same flags as `create`, plus `--no-server-confirmed` and `--restore` for an archived goal.
345
+
346
+ ```sh
347
+ littlefriend goals update goal_... --name "Checkout completed"
348
+ ```
349
+
350
+ ```ts
351
+ { goal: GoalDefinition }
352
+ ```
353
+
354
+ #### `goals delete`
355
+
356
+ Archives the goal, so past completions stay explainable. Needs `--yes`.
357
+
358
+ ```sh
359
+ littlefriend goals delete goal_... --yes
360
+ ```
361
+
362
+ ```ts
363
+ { archived: true; goalId: string }
364
+ ```
365
+
366
+ #### `funnels list`
367
+
368
+ ```sh
369
+ littlefriend funnels list
370
+ ```
371
+
372
+ ```ts
373
+ { funnels: { id: string; projectId: string; name: string; steps: string[];
374
+ createdAt: string; updatedAt: string }[] }
375
+ ```
376
+
377
+ #### `funnels create`
378
+
379
+ Steps run in order, up to 8 of them: `route:/path` (a page view), `event:<name>` or `goal:<goalId>`.
380
+
381
+ ```sh
382
+ littlefriend funnels create --name Signup --step route:/pricing --step event:signup.start --step goal:goal_...
383
+ ```
384
+
385
+ ```ts
386
+ { funnel: SavedFunnel }
387
+ ```
388
+
389
+ #### `funnels update`
390
+
391
+ `--step` replaces all the steps.
392
+
393
+ ```sh
394
+ littlefriend funnels update fnl_... --name "Trial signup"
395
+ ```
396
+
397
+ ```ts
398
+ { funnel: SavedFunnel }
399
+ ```
400
+
401
+ #### `funnels delete`
402
+
403
+ Needs `--yes`.
404
+
405
+ ```sh
406
+ littlefriend funnels delete fnl_... --yes
407
+ ```
408
+
409
+ ```ts
410
+ { deleted: true; funnelId: string }
411
+ ```
412
+
413
+ #### `funnels report`
414
+
415
+ Runs a saved funnel, or steps given with `--step`, over `--from` and `--to` (default the last 7 days in the project's time zone). `--within 30m|1h|session` sets how long a session has to finish. Funnels need journey mode.
416
+
417
+ ```sh
418
+ littlefriend funnels report fnl_... --from 2026-09-01 --to 2026-09-28
419
+ ```
420
+
421
+ The JSON is the API's `FunnelReport`: `{ meta, available, unavailableReason?, within, steps, sessionsInScope, completed, conversionRate, coveredFrom, denominator }`.
422
+
423
+ #### `report`
424
+
425
+ Quick numbers to check the data looks right: `overview` (the default), `pages`, `sources`, `goals` or `countries`, over `--from` and `--to` (default the last 7 days in the project's time zone).
426
+
427
+ ```sh
428
+ littlefriend report sources --from 2026-09-01 --to 2026-09-28
429
+ ```
430
+
431
+ The JSON is the API's report response, with `meta.range` holding the dates used.
432
+
433
+ ### Session replay, the agent door and log drains
434
+
435
+ #### `replay get`, `replay enable`, `replay disable`
436
+
437
+ Replay records sessions with every word masked. It needs journey mode. `enable` takes `--rate <1-100>`, the percent of sessions to record.
438
+
439
+ ```sh
440
+ littlefriend replay enable --rate 25
441
+ ```
442
+
443
+ ```ts
444
+ // ReplaySettingsResponse
445
+ { settings: { enabled: boolean; sampleRate: number; unmaskSelectors: string[]; blockSelectors: string[];
446
+ excludeRoutes: string[]; minActiveMs: number; version: number; updatedAt: string | null };
447
+ available: boolean; unavailableReason?: string; installed: boolean }
448
+ ```
449
+
450
+ #### `replay set`
451
+
452
+ `--unmask`, `--block` and `--exclude` replace a list; `--add-*`, `--remove-*` and `--clear-*` edit it. `--unmask-preset` adds the recommended set: navigation, headers, footers, headings, buttons and labels. Also `--rate` and `--min-active-ms <0-60000>`.
453
+
454
+ ```sh
455
+ littlefriend replay set --unmask-preset --exclude /account --exclude /checkout --block .support-chat
456
+ ```
457
+
458
+ Returns `ReplaySettingsResponse`.
459
+
460
+ #### `door get`
461
+
462
+ Shows the agent door policy: dry run or live, and its rules.
463
+
464
+ ```sh
465
+ littlefriend door get
466
+ ```
467
+
468
+ ```ts
469
+ // DoorSettingsResponse
470
+ { policy: DoorPolicy; presets: Record<string, DoorRule[]>; canEdit: boolean;
471
+ updatedAt: string | null; updatedBy: string | null; installed: boolean }
472
+ ```
473
+
474
+ #### `door set`
475
+
476
+ Applies a preset (`open`, `no_training`, `verified_only`, `commerce`), switches `--mode dry_run|live`, or both. Replacing custom rules with a preset needs `--yes`.
477
+
478
+ ```sh
479
+ littlefriend door set --preset no_training --mode dry_run
480
+ ```
481
+
482
+ Returns `DoorSettingsResponse`.
483
+
484
+ #### `door simulate`
485
+
486
+ With `--ua`, shows what the door decides for one request. The user agent is classified on your machine with the same rules the collector uses. `--path` and `--method` describe the request, `--verified` or `--spoofed` stand in for the address check, and `--preset` tries a preset instead of the saved policy.
487
+
488
+ Without `--ua`, it tries the policy against the last 7 days of agent traffic.
489
+
490
+ ```sh
491
+ littlefriend door simulate --ua "GPTBot/1.1" --path /blog
492
+ ```
493
+
494
+ ```ts
495
+ // with --ua
496
+ { classification: object; facts: DoorFacts;
497
+ policy: { source: 'saved' | 'preset'; preset?: string; mode: 'dry_run' | 'live'; version: number };
498
+ decision: { action: string; ruleId: string | null } }
499
+ // without --ua: DoorSimulation
500
+ { from: string; to: string; requests: number; rules: object[]; unmatched: number }
501
+ ```
502
+
503
+ #### `drains list`
504
+
505
+ ```sh
506
+ littlefriend drains list
507
+ ```
508
+
509
+ ```ts
510
+ { endpoint: string; drains: DrainInfo[] } // never the secrets
511
+ ```
512
+
513
+ #### `drains create`
514
+
515
+ Creates a Vercel log drain and prints its URL, the header to set, the signature secret and the steps to finish in Vercel. The secret is shown once.
516
+
517
+ ```sh
518
+ littlefriend drains create --provider vercel --label "Vercel production"
519
+ ```
520
+
521
+ ```ts
522
+ // DrainCredentials
523
+ { drain: DrainInfo; endpoint: string; headers: Record<string, string>; secret: string; notice: string }
524
+ ```
525
+
526
+ #### `drains rotate`
527
+
528
+ Replaces the drain's key and secret and prints the new ones once. The old ones keep working for `--grace-hours` (default 24, up to 168), or stop at once with `--expire-now`.
529
+
530
+ ```sh
531
+ littlefriend drains rotate drn_...
532
+ ```
533
+
534
+ Returns `DrainCredentials`.
535
+
536
+ #### `drains delete`
537
+
538
+ Needs `--yes`.
539
+
540
+ ```sh
541
+ littlefriend drains delete drn_... --yes
542
+ ```
543
+
544
+ ```ts
545
+ { drain: DrainInfo }
546
+ ```
547
+
548
+ ### Help
549
+
550
+ #### `guide`
551
+
552
+ Prints the agent setup guide that ships with this version of the CLI.
553
+
554
+ ```sh
555
+ littlefriend guide
556
+ ```
557
+
558
+ ```ts
559
+ { guide: string; bundled: boolean }
560
+ ```
561
+
562
+ `littlefriend help <command>` or `--help` on any command shows its flags and an example.
563
+
564
+ ## License
565
+
566
+ MIT