@hogsend/cli 0.17.1 → 0.19.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/dist/bin.js +20 -0
- package/dist/bin.js.map +1 -1
- package/package.json +4 -4
- package/skills/hogsend-authoring-journeys/references/journey-context.md +4 -1
- package/skills/hogsend-client-sdk/SKILL.md +7 -3
- package/skills/hogsend-deploy/references/env-and-secrets.md +16 -1
- package/src/commands/doctor.ts +30 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hogsend/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"tsup": "^8.5.1",
|
|
35
35
|
"tsx": "^4.22.4",
|
|
36
36
|
"vitest": "^4.1.7",
|
|
37
|
-
"@hogsend/studio": "^0.
|
|
37
|
+
"@hogsend/studio": "^0.19.0",
|
|
38
38
|
"@repo/typescript-config": "0.0.0"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@clack/prompts": "^1.5.0",
|
|
45
45
|
"better-auth": "^1.6.11",
|
|
46
46
|
"picocolors": "^1.1.1",
|
|
47
|
-
"@hogsend/db": "^0.
|
|
48
|
-
"@hogsend/engine": "^0.
|
|
47
|
+
"@hogsend/db": "^0.19.0",
|
|
48
|
+
"@hogsend/engine": "^0.19.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"prebuild": "node scripts/bundle-studio.mjs",
|
|
@@ -149,7 +149,10 @@ orchestration:
|
|
|
149
149
|
- **`getPostHog()`** — `import { getPostHog } from "@hogsend/engine"` for the raw
|
|
150
150
|
PostHog service (a fire-and-forget escape hatch). For fanning lifecycle data out
|
|
151
151
|
to product/data tools, prefer an outbound DESTINATION (see above) — it delivers
|
|
152
|
-
durably and is vendor-neutral.
|
|
152
|
+
durably and is vendor-neutral. Note: capture and `$set` person WRITES use the
|
|
153
|
+
`phc_` project key; person READS (`getPersonProperties`) additionally need
|
|
154
|
+
`POSTHOG_PERSONAL_API_KEY` (the project key is write-only by PostHog's design)
|
|
155
|
+
and soft-fail to `{}` without it.
|
|
153
156
|
- **SMS / push / Slack** — plain functions you import, never on `ctx`.
|
|
154
157
|
- There is **no `ctx.db`, no `ctx.sendEmail`, no `ctx.hatchet`** surfaced to
|
|
155
158
|
consumer journeys. If you reach for one of those, you are modelling it wrong —
|
|
@@ -89,7 +89,7 @@ await hs.events.send({ // POST /v1/events
|
|
|
89
89
|
eventProperties: { source: "landing" }, // stored ON the event
|
|
90
90
|
contactProperties: { country: "GB" }, // merged onto the CONTACT
|
|
91
91
|
idempotencyKey: "evt_abc",
|
|
92
|
-
}); // -> { stored, exits, listsError? }
|
|
92
|
+
}); // -> { stored, exits, contactKey?, listsError? }
|
|
93
93
|
hs.events.track(/* … */); // alias of events.send
|
|
94
94
|
|
|
95
95
|
// Emails ------------------------------------------------------------------
|
|
@@ -185,13 +185,17 @@ same split is exposed by the CLI as `--prop` (event) vs `--contact-prop`
|
|
|
185
185
|
## The 202 + `listsError` warning
|
|
186
186
|
|
|
187
187
|
`POST /v1/events` returns **202 Accepted** (the event is durably stored and
|
|
188
|
-
queued for routing), NOT 200. The result is `{ stored, exits }`
|
|
189
|
-
`listsError`:
|
|
188
|
+
queued for routing), NOT 200. The result is `{ stored, exits, contactKey }`
|
|
189
|
+
plus an optional `listsError`:
|
|
190
190
|
|
|
191
191
|
- `stored` — `true` once the event row is written (`false` only on a dedup via
|
|
192
192
|
`idempotencyKey`).
|
|
193
193
|
- `exits` — the `{ journeyId, stateId, exited }[]` from evaluating active
|
|
194
194
|
journeys' `exitOn` rules for this user.
|
|
195
|
+
- `contactKey` — the contact's canonical key (engine ≥0.18): the same key
|
|
196
|
+
outbound destinations emit as `userId` and `hs_t` identity tokens resolve
|
|
197
|
+
to. Hand it to your analytics `identify()` to join the session to the
|
|
198
|
+
contact's person — it carries no PII.
|
|
195
199
|
- **`listsError?`** — present ONLY when the event was ingested fine but the
|
|
196
200
|
(non-atomic, post-ingest) `lists` membership write failed. **The event itself
|
|
197
201
|
is durably stored** — this is a soft warning surfaced on the 202, not a 400.
|
|
@@ -73,10 +73,19 @@ dashboard at `http://localhost:8888`, login `admin@example.com` / `Admin123!!`.)
|
|
|
73
73
|
All commented-out in `.env.example` — add only what you use:
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
# PostHog
|
|
76
|
+
# PostHog event capture + person property WRITES (no-op if unset).
|
|
77
77
|
POSTHOG_API_KEY=phc_...
|
|
78
78
|
POSTHOG_HOST=https://us.i.posthog.com
|
|
79
79
|
|
|
80
|
+
# PostHog person property READS (timezone resolution, property conditions).
|
|
81
|
+
# The phc_ key is write-only by PostHog's design — reads need a PERSONAL API
|
|
82
|
+
# key scoped person:read. Without it reads soft-fail to contact properties.
|
|
83
|
+
POSTHOG_PERSONAL_API_KEY=...
|
|
84
|
+
# Project id for the private API — discovered automatically when unset.
|
|
85
|
+
# POSTHOG_PROJECT_ID=12345
|
|
86
|
+
# Private API host — derived (eu.i.posthog.com → eu.posthog.com) when unset.
|
|
87
|
+
# POSTHOG_PRIVATE_HOST=https://us.posthog.com
|
|
88
|
+
|
|
80
89
|
# Verify incoming PostHog webhooks (POST /v1/webhooks/posthog).
|
|
81
90
|
POSTHOG_WEBHOOK_SECRET=...
|
|
82
91
|
|
|
@@ -92,6 +101,12 @@ Notes:
|
|
|
92
101
|
|
|
93
102
|
- **PostHog is fully optional.** Without `POSTHOG_API_KEY`, person-property
|
|
94
103
|
fetches and event captures are no-ops — journeys still run.
|
|
104
|
+
- **Two PostHog credentials, by PostHog's design.** The `phc_` project key is
|
|
105
|
+
public (it ships in browser bundles) so PostHog makes it WRITE-only: capture
|
|
106
|
+
and `$set` person writes work, reads never will. Person READS (per-user
|
|
107
|
+
timezone resolution) need `POSTHOG_PERSONAL_API_KEY` — a personal API key
|
|
108
|
+
scoped `person:read`, created in PostHog → Settings → Personal API keys.
|
|
109
|
+
`hogsend doctor` warns when capture is configured without it.
|
|
95
110
|
- **Webhook secrets are per-source.** Only set the secret for a webhook source
|
|
96
111
|
you've actually registered (see the consumer's `src/webhook-sources`).
|
|
97
112
|
- **`ADMIN_API_KEY` gates `/v1/admin/*`.** Set it in prod if you want to drive
|
package/src/commands/doctor.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
|
+
import { loadDotEnv } from "../lib/config.js";
|
|
2
3
|
import { isHttpError } from "../lib/http.js";
|
|
3
4
|
import { color } from "../lib/output.js";
|
|
4
5
|
import { skillsStaleness } from "../lib/skills.js";
|
|
@@ -22,6 +23,34 @@ function skillsNudge(ctx: CommandContext): void {
|
|
|
22
23
|
);
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Best-effort nudge: PostHog capture configured (`POSTHOG_API_KEY` in the
|
|
28
|
+
* cwd's `.env` or process env) without `POSTHOG_PERSONAL_API_KEY` means
|
|
29
|
+
* person READS are silently disabled — the phc_ project key is write-only by
|
|
30
|
+
* PostHog's design, so timezone resolution falls back to contact properties.
|
|
31
|
+
* Warn-not-fail: capture and person writes still work.
|
|
32
|
+
*/
|
|
33
|
+
function analyticsNudge(ctx: CommandContext): void {
|
|
34
|
+
if (ctx.json) return;
|
|
35
|
+
const dotenv = loadDotEnv(process.cwd());
|
|
36
|
+
const captureKey = process.env.POSTHOG_API_KEY ?? dotenv.POSTHOG_API_KEY;
|
|
37
|
+
const personalKey =
|
|
38
|
+
process.env.POSTHOG_PERSONAL_API_KEY ?? dotenv.POSTHOG_PERSONAL_API_KEY;
|
|
39
|
+
if (!captureKey || personalKey) return;
|
|
40
|
+
ctx.out.note(
|
|
41
|
+
[
|
|
42
|
+
"POSTHOG_API_KEY is set without POSTHOG_PERSONAL_API_KEY — person",
|
|
43
|
+
"property READS are disabled (the phc_ project key is write-only by",
|
|
44
|
+
"PostHog's design), so per-user timezone resolution falls back to",
|
|
45
|
+
"contact properties. Capture and person WRITES are unaffected.",
|
|
46
|
+
"",
|
|
47
|
+
`Fix: create a personal API key scoped ${color.cyan("person:read")} and set ${color.cyan("POSTHOG_PERSONAL_API_KEY")}.`,
|
|
48
|
+
`Docs: ${color.cyan("https://hogsend.com/docs/guides/analytics-access")}`,
|
|
49
|
+
].join("\n"),
|
|
50
|
+
"PostHog person reads disabled",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
25
54
|
const usage = `hogsend doctor [--url <baseUrl>] [--admin-key <key>] [--json]
|
|
26
55
|
|
|
27
56
|
Probe a running Hogsend instance via GET /v1/health and report its health:
|
|
@@ -221,6 +250,7 @@ async function run(ctx: CommandContext): Promise<void> {
|
|
|
221
250
|
ctx.out.note(lines.join("\n"), "Doctor");
|
|
222
251
|
|
|
223
252
|
skillsNudge(ctx);
|
|
253
|
+
analyticsNudge(ctx);
|
|
224
254
|
|
|
225
255
|
if (ok) {
|
|
226
256
|
ctx.out.outro(color.green("doctor: ok"));
|