clivly 0.9.4 → 0.9.5
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 +23 -6
- package/index.cjs +120 -3
- package/index.mjs +120 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,7 +87,10 @@ Detects your framework, ORM, auth adapter and app layout, then scaffolds:
|
|
|
87
87
|
| The auth-verify route for your framework | Yes — `--force` to overwrite |
|
|
88
88
|
|
|
89
89
|
Plus `CLIVLY_SECRET_KEY`, `CLIVLY_SYNC_TRIGGER_URL` and
|
|
90
|
-
`CLIVLY_SYNC_TRIGGER_SECRET`
|
|
90
|
+
`CLIVLY_SYNC_TRIGGER_SECRET` in your env file. The two secrets are written
|
|
91
|
+
empty, for you or `clivly login` to fill; the trigger URL gets a visible
|
|
92
|
+
`https://<yourDomain.com>/api/clivly/tick` template, because there the shape is
|
|
93
|
+
the documentation.
|
|
91
94
|
|
|
92
95
|
| Flag | Purpose |
|
|
93
96
|
| --- | --- |
|
|
@@ -233,7 +236,7 @@ the CLI requires pairing.
|
|
|
233
236
|
| --- | --- | --- |
|
|
234
237
|
| `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 |
|
|
235
238
|
| `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 |
|
|
236
|
-
| `CLIVLY_SYNC_TRIGGER_URL` | Your own deployment | The public URL of your mounted tick route
|
|
239
|
+
| `CLIVLY_SYNC_TRIGGER_URL` | Your own deployment | The public URL of your mounted tick route. `init` and `login` both scaffold it as `https://<yourDomain.com>/api/clivly/tick` — the angle brackets mark it unedited, and the SDK ignores it until you replace them, so leaving it alone costs nothing. Optional until you deploy — push-only setups can leave it as the template |
|
|
237
240
|
| `CLIVLY_PUBLISHABLE_KEY` | Same place as the secret key | Only needed for the chat widget. Safe to expose in client code |
|
|
238
241
|
|
|
239
242
|
Write them into the env file of the package that imports `clivly`, then re-run
|
|
@@ -265,7 +268,7 @@ It answers two questions under two headings:
|
|
|
265
268
|
scope; `clivly doctor` is local by definition and omits it.
|
|
266
269
|
- **This project** — the local ladder, run here on your machine: credential →
|
|
267
270
|
Clivly reachable → `clivly.config` loads → `clivly.config` is edge-safe →
|
|
268
|
-
setup checks → sync trigger URL.
|
|
271
|
+
boot hook wired → setup checks → sync trigger URL.
|
|
269
272
|
|
|
270
273
|
```text
|
|
271
274
|
Clivly status
|
|
@@ -278,11 +281,23 @@ Workspace
|
|
|
278
281
|
🔒 Let Clivly start a sync
|
|
279
282
|
|
|
280
283
|
This project
|
|
281
|
-
✅ Step 1 of
|
|
282
|
-
✅ Step 2 of
|
|
284
|
+
✅ Step 1 of 7 · API key present
|
|
285
|
+
✅ Step 2 of 7 · Clivly reachable — Acme
|
|
283
286
|
...
|
|
284
287
|
```
|
|
285
288
|
|
|
289
|
+
**Boot hook wired** is the rung to know about. It finds the boot site for your
|
|
290
|
+
framework and confirms something there actually starts Clivly — and on Next.js
|
|
291
|
+
below 15, that `experimental.instrumentationHook` is set in `next.config`,
|
|
292
|
+
without which `instrumentation.ts` is ignored outright and your app never checks
|
|
293
|
+
in. That failure is otherwise completely silent: the file is present, the config
|
|
294
|
+
loads, the credentials work, and the dashboard stays empty.
|
|
295
|
+
|
|
296
|
+
It is the one rung with no prerequisites, so it still reports when the config
|
|
297
|
+
above it fails to load. A framework the CLI does not recognise shows it as
|
|
298
|
+
skipped rather than passed — there is nowhere known to look, and a green step
|
|
299
|
+
for an unasked question is what made this worth adding.
|
|
300
|
+
|
|
286
301
|
If the Workspace section reads
|
|
287
302
|
`⚠️ This Clivly server does not report setup progress`, or names a rejected key
|
|
288
303
|
or an unreachable host, that means the question could not be asked — not that
|
|
@@ -406,7 +421,7 @@ work, and are kept because output and docs have recommended them — but prefer
|
|
|
406
421
|
| --- | --- |
|
|
407
422
|
| `clivly ping` | Credential + reachability |
|
|
408
423
|
| `clivly connect` | The above, plus config and trigger URL |
|
|
409
|
-
| `clivly doctor` | Config,
|
|
424
|
+
| `clivly doctor` | Config, edge safety, boot hook and setup checks |
|
|
410
425
|
|
|
411
426
|
Each prints a deprecation notice, then the steps it covers, renumbered to its own
|
|
412
427
|
scope:
|
|
@@ -478,6 +493,8 @@ socket reconnect. The CLI does not enable this setting on your behalf.
|
|
|
478
493
|
| `init` cannot find your schema | Pass `--schema <path\|package>` |
|
|
479
494
|
| `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` |
|
|
480
495
|
| `clivly login` exits non-zero, pairing unavailable | The backend has `CLIVLY_CLI_PAIRING_ENABLED` off. Use the manual credential path above |
|
|
496
|
+
| App runs, no errors, dashboard stays silent | Run `clivly status` — the **Boot hook wired** step names the cause. On Next.js below 15 it is almost always the missing `experimental: { instrumentationHook: true }` in `next.config` |
|
|
497
|
+
| `Clivly reachable` fails with `invalid_api_key` (401) | The key is not one this org accepts. Re-run `npx clivly login` to re-pair, and check the key belongs to the org whose dashboard you are watching |
|
|
481
498
|
| `clivly dev` cannot start | Install `cloudflared`, or set `CLIVLY_SYNC_TRIGGER_URL` to a tunnel you run yourself |
|
|
482
499
|
| Credentials written but the app never loads them | Wrong env file for your framework — see the table under `clivly login` |
|
|
483
500
|
|
package/index.cjs
CHANGED
|
@@ -974,7 +974,7 @@ function isKeySet(contents, key) {
|
|
|
974
974
|
function appendEnvKeys(existing, entries) {
|
|
975
975
|
const missing = entries.filter((e) => !hasKey$1(existing, e.key));
|
|
976
976
|
if (missing.length === 0) return existing;
|
|
977
|
-
const block = missing.map((e) => `${e.comment ? `# ${e.comment}\n` : ""}${e.key}
|
|
977
|
+
const block = missing.map((e) => `${e.comment ? `# ${e.comment}\n` : ""}${e.key}=${e.value ?? ""}`).join("\n");
|
|
978
978
|
return `${existing}${existing.length > 0 && !existing.endsWith("\n") ? "\n" : ""}${existing.length > 0 ? "\n" : ""}# Clivly\n${block}\n`;
|
|
979
979
|
}
|
|
980
980
|
/**
|
|
@@ -1810,7 +1810,8 @@ const CLIVLY_ENV_KEYS = [
|
|
|
1810
1810
|
},
|
|
1811
1811
|
{
|
|
1812
1812
|
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1813
|
-
|
|
1813
|
+
value: TRIGGER_URL_PLACEHOLDER,
|
|
1814
|
+
comment: "Public URL of your tick route — replace <yourDomain.com> once deployed. Local dev: run `clivly dev` instead."
|
|
1814
1815
|
},
|
|
1815
1816
|
{
|
|
1816
1817
|
key: "CLIVLY_SYNC_TRIGGER_SECRET",
|
|
@@ -2741,6 +2742,103 @@ function ladderExitCode(results) {
|
|
|
2741
2742
|
return results.every((result) => result.state !== "fail" && result.state !== "pending") ? 0 : 1;
|
|
2742
2743
|
}
|
|
2743
2744
|
//#endregion
|
|
2745
|
+
//#region src/boot-hook-check.ts
|
|
2746
|
+
const REMIX_ENTRY_CANDIDATES = [
|
|
2747
|
+
"app/entry.server.tsx",
|
|
2748
|
+
"app/entry.server.ts",
|
|
2749
|
+
"app/entry.server.jsx",
|
|
2750
|
+
"app/entry.server.js"
|
|
2751
|
+
];
|
|
2752
|
+
const NEXT_CONFIG_CANDIDATES = [
|
|
2753
|
+
"next.config.ts",
|
|
2754
|
+
"next.config.mjs",
|
|
2755
|
+
"next.config.js",
|
|
2756
|
+
"next.config.cjs",
|
|
2757
|
+
"next.config.mts"
|
|
2758
|
+
];
|
|
2759
|
+
const CLIVLY_MENTION = /clivly/i;
|
|
2760
|
+
const INSTRUMENTATION_HOOK_ENABLED = /instrumentationHook\s*:\s*true/;
|
|
2761
|
+
/**
|
|
2762
|
+
* Where this framework's boot hook lives, most likely location first.
|
|
2763
|
+
*
|
|
2764
|
+
* The file-owning frameworks are asked of `boot-hook.ts` rather than restated,
|
|
2765
|
+
* so a path change there cannot leave this check hunting for a file `init` no
|
|
2766
|
+
* longer writes. The other two are patched into a file the app already owns.
|
|
2767
|
+
*/
|
|
2768
|
+
function bootSiteCandidates(framework) {
|
|
2769
|
+
const owned = [bootHookPath(framework, "app"), bootHookPath(framework, "src/app")].filter((path) => path !== null);
|
|
2770
|
+
if (owned.length > 0) return [...new Set(owned)];
|
|
2771
|
+
if (framework === "tanstack-start") return [...VITE_CONFIG_CANDIDATES];
|
|
2772
|
+
if (framework === "remix") return REMIX_ENTRY_CANDIDATES;
|
|
2773
|
+
return [];
|
|
2774
|
+
}
|
|
2775
|
+
function declaredDeps(readFile) {
|
|
2776
|
+
const raw = readFile("package.json");
|
|
2777
|
+
if (raw === null) return null;
|
|
2778
|
+
try {
|
|
2779
|
+
const pkg = JSON.parse(raw);
|
|
2780
|
+
return {
|
|
2781
|
+
...pkg.dependencies,
|
|
2782
|
+
...pkg.devDependencies
|
|
2783
|
+
};
|
|
2784
|
+
} catch {
|
|
2785
|
+
return null;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
function findBootSite(readFile, framework) {
|
|
2789
|
+
for (const candidate of bootSiteCandidates(framework)) {
|
|
2790
|
+
const source = readFile(candidate);
|
|
2791
|
+
if (source !== null && CLIVLY_MENTION.test(source)) return candidate;
|
|
2792
|
+
}
|
|
2793
|
+
return null;
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* On Next.js below 15 the hook file is ignored unless the flag is on, so a
|
|
2797
|
+
* present-and-correct instrumentation.ts is not evidence of anything. A missing
|
|
2798
|
+
* next.config is the same failure as one without the flag: either way nothing
|
|
2799
|
+
* switches the hook on.
|
|
2800
|
+
*/
|
|
2801
|
+
function nextFlagVerdict(readFile, version, site) {
|
|
2802
|
+
if (!nextNeedsInstrumentationFlag(version)) return null;
|
|
2803
|
+
for (const candidate of NEXT_CONFIG_CANDIDATES) {
|
|
2804
|
+
const source = readFile(candidate);
|
|
2805
|
+
if (source !== null && INSTRUMENTATION_HOOK_ENABLED.test(source)) return null;
|
|
2806
|
+
}
|
|
2807
|
+
return {
|
|
2808
|
+
state: "broken",
|
|
2809
|
+
detail: `${site} exists, but Next.js ${version} ignores it — experimental.instrumentationHook is not enabled in next.config.`,
|
|
2810
|
+
hint: NEXT_INSTRUMENTATION_FLAG_STEP
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
function checkBootHook(readFile) {
|
|
2814
|
+
const deps = declaredDeps(readFile);
|
|
2815
|
+
if (deps === null) return {
|
|
2816
|
+
state: "unknown",
|
|
2817
|
+
detail: "package.json not readable — cannot tell how this app boots."
|
|
2818
|
+
};
|
|
2819
|
+
const framework = detectFramework(deps);
|
|
2820
|
+
if (framework === "unknown") return {
|
|
2821
|
+
state: "unknown",
|
|
2822
|
+
detail: "No supported framework detected, so there is no known place to check."
|
|
2823
|
+
};
|
|
2824
|
+
const label = FRAMEWORK_LABELS[framework];
|
|
2825
|
+
const site = findBootSite(readFile, framework);
|
|
2826
|
+
if (site === null) return {
|
|
2827
|
+
state: "broken",
|
|
2828
|
+
detail: `No Clivly boot hook found in this ${label} app.`,
|
|
2829
|
+
hint: `Nothing starts the SDK, so your app never tells Clivly it is running — the dashboard stays silent with no error anywhere. Run \`clivly init\` to scaffold it, or call \`clivly.start()\` where your server boots (${bootSiteCandidates(framework)[0]}).`
|
|
2830
|
+
};
|
|
2831
|
+
const nextVersion = deps.next;
|
|
2832
|
+
if (framework === "nextjs" && nextVersion) {
|
|
2833
|
+
const broken = nextFlagVerdict(readFile, nextVersion, site);
|
|
2834
|
+
if (broken) return broken;
|
|
2835
|
+
}
|
|
2836
|
+
return {
|
|
2837
|
+
state: "ok",
|
|
2838
|
+
detail: site
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2841
|
+
//#endregion
|
|
2744
2842
|
//#region src/edge-safety.ts
|
|
2745
2843
|
const NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
2746
2844
|
"assert",
|
|
@@ -2927,7 +3025,7 @@ function pingOnce(ctx) {
|
|
|
2927
3025
|
}
|
|
2928
3026
|
const REASON_HINT = {
|
|
2929
3027
|
ok: "",
|
|
2930
|
-
invalid_api_key: "The key was rejected (401).
|
|
3028
|
+
invalid_api_key: "The key was rejected (401). Run `clivly login` to re-pair and rewrite the credentials, and check CLIVLY_SECRET_KEY (or legacy CLIVLY_API_KEY) belongs to the org whose dashboard you are watching.",
|
|
2931
3029
|
forbidden: "The key is valid but lacks access to this org (403).",
|
|
2932
3030
|
rate_limited: "Rate limited (429). Wait a moment and retry.",
|
|
2933
3031
|
server_error: "Clivly returned a server error (5xx). Retry shortly.",
|
|
@@ -3083,6 +3181,24 @@ const STATUS_STAGES = [
|
|
|
3083
3181
|
});
|
|
3084
3182
|
}
|
|
3085
3183
|
},
|
|
3184
|
+
{
|
|
3185
|
+
id: "boot-hook",
|
|
3186
|
+
label: "Boot hook wired",
|
|
3187
|
+
run(ctx) {
|
|
3188
|
+
const read = ctx.readConfigSource ?? defaultReadConfigSource;
|
|
3189
|
+
const root = (0, node_path.dirname)(ctx.configPath);
|
|
3190
|
+
const verdict = checkBootHook((relative) => read((0, node_path.resolve)(root, relative)));
|
|
3191
|
+
if (verdict.state === "ok") return Promise.resolve({
|
|
3192
|
+
state: "pass",
|
|
3193
|
+
detail: `starts Clivly from ${verdict.detail}`
|
|
3194
|
+
});
|
|
3195
|
+
return Promise.resolve({
|
|
3196
|
+
state: verdict.state === "unknown" ? "skip" : "fail",
|
|
3197
|
+
detail: verdict.detail,
|
|
3198
|
+
hint: verdict.hint
|
|
3199
|
+
});
|
|
3200
|
+
}
|
|
3201
|
+
},
|
|
3086
3202
|
sdkChecksStage,
|
|
3087
3203
|
triggerUrlStage
|
|
3088
3204
|
];
|
|
@@ -3136,6 +3252,7 @@ const ALIAS_STAGE_IDS = {
|
|
|
3136
3252
|
doctor: [
|
|
3137
3253
|
"config",
|
|
3138
3254
|
"runtime",
|
|
3255
|
+
"boot-hook",
|
|
3139
3256
|
"setup-checks"
|
|
3140
3257
|
],
|
|
3141
3258
|
connect: [
|
package/index.mjs
CHANGED
|
@@ -973,7 +973,7 @@ function isKeySet(contents, key) {
|
|
|
973
973
|
function appendEnvKeys(existing, entries) {
|
|
974
974
|
const missing = entries.filter((e) => !hasKey$1(existing, e.key));
|
|
975
975
|
if (missing.length === 0) return existing;
|
|
976
|
-
const block = missing.map((e) => `${e.comment ? `# ${e.comment}\n` : ""}${e.key}
|
|
976
|
+
const block = missing.map((e) => `${e.comment ? `# ${e.comment}\n` : ""}${e.key}=${e.value ?? ""}`).join("\n");
|
|
977
977
|
return `${existing}${existing.length > 0 && !existing.endsWith("\n") ? "\n" : ""}${existing.length > 0 ? "\n" : ""}# Clivly\n${block}\n`;
|
|
978
978
|
}
|
|
979
979
|
/**
|
|
@@ -1809,7 +1809,8 @@ const CLIVLY_ENV_KEYS = [
|
|
|
1809
1809
|
},
|
|
1810
1810
|
{
|
|
1811
1811
|
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1812
|
-
|
|
1812
|
+
value: TRIGGER_URL_PLACEHOLDER,
|
|
1813
|
+
comment: "Public URL of your tick route — replace <yourDomain.com> once deployed. Local dev: run `clivly dev` instead."
|
|
1813
1814
|
},
|
|
1814
1815
|
{
|
|
1815
1816
|
key: "CLIVLY_SYNC_TRIGGER_SECRET",
|
|
@@ -2740,6 +2741,103 @@ function ladderExitCode(results) {
|
|
|
2740
2741
|
return results.every((result) => result.state !== "fail" && result.state !== "pending") ? 0 : 1;
|
|
2741
2742
|
}
|
|
2742
2743
|
//#endregion
|
|
2744
|
+
//#region src/boot-hook-check.ts
|
|
2745
|
+
const REMIX_ENTRY_CANDIDATES = [
|
|
2746
|
+
"app/entry.server.tsx",
|
|
2747
|
+
"app/entry.server.ts",
|
|
2748
|
+
"app/entry.server.jsx",
|
|
2749
|
+
"app/entry.server.js"
|
|
2750
|
+
];
|
|
2751
|
+
const NEXT_CONFIG_CANDIDATES = [
|
|
2752
|
+
"next.config.ts",
|
|
2753
|
+
"next.config.mjs",
|
|
2754
|
+
"next.config.js",
|
|
2755
|
+
"next.config.cjs",
|
|
2756
|
+
"next.config.mts"
|
|
2757
|
+
];
|
|
2758
|
+
const CLIVLY_MENTION = /clivly/i;
|
|
2759
|
+
const INSTRUMENTATION_HOOK_ENABLED = /instrumentationHook\s*:\s*true/;
|
|
2760
|
+
/**
|
|
2761
|
+
* Where this framework's boot hook lives, most likely location first.
|
|
2762
|
+
*
|
|
2763
|
+
* The file-owning frameworks are asked of `boot-hook.ts` rather than restated,
|
|
2764
|
+
* so a path change there cannot leave this check hunting for a file `init` no
|
|
2765
|
+
* longer writes. The other two are patched into a file the app already owns.
|
|
2766
|
+
*/
|
|
2767
|
+
function bootSiteCandidates(framework) {
|
|
2768
|
+
const owned = [bootHookPath(framework, "app"), bootHookPath(framework, "src/app")].filter((path) => path !== null);
|
|
2769
|
+
if (owned.length > 0) return [...new Set(owned)];
|
|
2770
|
+
if (framework === "tanstack-start") return [...VITE_CONFIG_CANDIDATES];
|
|
2771
|
+
if (framework === "remix") return REMIX_ENTRY_CANDIDATES;
|
|
2772
|
+
return [];
|
|
2773
|
+
}
|
|
2774
|
+
function declaredDeps(readFile) {
|
|
2775
|
+
const raw = readFile("package.json");
|
|
2776
|
+
if (raw === null) return null;
|
|
2777
|
+
try {
|
|
2778
|
+
const pkg = JSON.parse(raw);
|
|
2779
|
+
return {
|
|
2780
|
+
...pkg.dependencies,
|
|
2781
|
+
...pkg.devDependencies
|
|
2782
|
+
};
|
|
2783
|
+
} catch {
|
|
2784
|
+
return null;
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
function findBootSite(readFile, framework) {
|
|
2788
|
+
for (const candidate of bootSiteCandidates(framework)) {
|
|
2789
|
+
const source = readFile(candidate);
|
|
2790
|
+
if (source !== null && CLIVLY_MENTION.test(source)) return candidate;
|
|
2791
|
+
}
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2794
|
+
/**
|
|
2795
|
+
* On Next.js below 15 the hook file is ignored unless the flag is on, so a
|
|
2796
|
+
* present-and-correct instrumentation.ts is not evidence of anything. A missing
|
|
2797
|
+
* next.config is the same failure as one without the flag: either way nothing
|
|
2798
|
+
* switches the hook on.
|
|
2799
|
+
*/
|
|
2800
|
+
function nextFlagVerdict(readFile, version, site) {
|
|
2801
|
+
if (!nextNeedsInstrumentationFlag(version)) return null;
|
|
2802
|
+
for (const candidate of NEXT_CONFIG_CANDIDATES) {
|
|
2803
|
+
const source = readFile(candidate);
|
|
2804
|
+
if (source !== null && INSTRUMENTATION_HOOK_ENABLED.test(source)) return null;
|
|
2805
|
+
}
|
|
2806
|
+
return {
|
|
2807
|
+
state: "broken",
|
|
2808
|
+
detail: `${site} exists, but Next.js ${version} ignores it — experimental.instrumentationHook is not enabled in next.config.`,
|
|
2809
|
+
hint: NEXT_INSTRUMENTATION_FLAG_STEP
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
function checkBootHook(readFile) {
|
|
2813
|
+
const deps = declaredDeps(readFile);
|
|
2814
|
+
if (deps === null) return {
|
|
2815
|
+
state: "unknown",
|
|
2816
|
+
detail: "package.json not readable — cannot tell how this app boots."
|
|
2817
|
+
};
|
|
2818
|
+
const framework = detectFramework(deps);
|
|
2819
|
+
if (framework === "unknown") return {
|
|
2820
|
+
state: "unknown",
|
|
2821
|
+
detail: "No supported framework detected, so there is no known place to check."
|
|
2822
|
+
};
|
|
2823
|
+
const label = FRAMEWORK_LABELS[framework];
|
|
2824
|
+
const site = findBootSite(readFile, framework);
|
|
2825
|
+
if (site === null) return {
|
|
2826
|
+
state: "broken",
|
|
2827
|
+
detail: `No Clivly boot hook found in this ${label} app.`,
|
|
2828
|
+
hint: `Nothing starts the SDK, so your app never tells Clivly it is running — the dashboard stays silent with no error anywhere. Run \`clivly init\` to scaffold it, or call \`clivly.start()\` where your server boots (${bootSiteCandidates(framework)[0]}).`
|
|
2829
|
+
};
|
|
2830
|
+
const nextVersion = deps.next;
|
|
2831
|
+
if (framework === "nextjs" && nextVersion) {
|
|
2832
|
+
const broken = nextFlagVerdict(readFile, nextVersion, site);
|
|
2833
|
+
if (broken) return broken;
|
|
2834
|
+
}
|
|
2835
|
+
return {
|
|
2836
|
+
state: "ok",
|
|
2837
|
+
detail: site
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
//#endregion
|
|
2743
2841
|
//#region src/edge-safety.ts
|
|
2744
2842
|
const NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
2745
2843
|
"assert",
|
|
@@ -2926,7 +3024,7 @@ function pingOnce(ctx) {
|
|
|
2926
3024
|
}
|
|
2927
3025
|
const REASON_HINT = {
|
|
2928
3026
|
ok: "",
|
|
2929
|
-
invalid_api_key: "The key was rejected (401).
|
|
3027
|
+
invalid_api_key: "The key was rejected (401). Run `clivly login` to re-pair and rewrite the credentials, and check CLIVLY_SECRET_KEY (or legacy CLIVLY_API_KEY) belongs to the org whose dashboard you are watching.",
|
|
2930
3028
|
forbidden: "The key is valid but lacks access to this org (403).",
|
|
2931
3029
|
rate_limited: "Rate limited (429). Wait a moment and retry.",
|
|
2932
3030
|
server_error: "Clivly returned a server error (5xx). Retry shortly.",
|
|
@@ -3082,6 +3180,24 @@ const STATUS_STAGES = [
|
|
|
3082
3180
|
});
|
|
3083
3181
|
}
|
|
3084
3182
|
},
|
|
3183
|
+
{
|
|
3184
|
+
id: "boot-hook",
|
|
3185
|
+
label: "Boot hook wired",
|
|
3186
|
+
run(ctx) {
|
|
3187
|
+
const read = ctx.readConfigSource ?? defaultReadConfigSource;
|
|
3188
|
+
const root = dirname(ctx.configPath);
|
|
3189
|
+
const verdict = checkBootHook((relative) => read(resolve(root, relative)));
|
|
3190
|
+
if (verdict.state === "ok") return Promise.resolve({
|
|
3191
|
+
state: "pass",
|
|
3192
|
+
detail: `starts Clivly from ${verdict.detail}`
|
|
3193
|
+
});
|
|
3194
|
+
return Promise.resolve({
|
|
3195
|
+
state: verdict.state === "unknown" ? "skip" : "fail",
|
|
3196
|
+
detail: verdict.detail,
|
|
3197
|
+
hint: verdict.hint
|
|
3198
|
+
});
|
|
3199
|
+
}
|
|
3200
|
+
},
|
|
3085
3201
|
sdkChecksStage,
|
|
3086
3202
|
triggerUrlStage
|
|
3087
3203
|
];
|
|
@@ -3135,6 +3251,7 @@ const ALIAS_STAGE_IDS = {
|
|
|
3135
3251
|
doctor: [
|
|
3136
3252
|
"config",
|
|
3137
3253
|
"runtime",
|
|
3254
|
+
"boot-hook",
|
|
3138
3255
|
"setup-checks"
|
|
3139
3256
|
],
|
|
3140
3257
|
connect: [
|