clivly 0.2.0 → 0.2.2

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 (117) hide show
  1. package/README.md +54 -3
  2. package/adapter-Cd6D_3JD.d.mts +46 -0
  3. package/adapter-Cy4q2CLs.d.cts +46 -0
  4. package/auth-adapter-CBOKDhlc.cjs +11 -0
  5. package/auth-adapter-CRBW4elj.d.mts +30 -0
  6. package/auth-adapter-CdLODn2p.d.cts +30 -0
  7. package/auth-adapter-wqnnEjo3.mjs +6 -0
  8. package/auth-better-auth.cjs +28 -0
  9. package/auth-better-auth.d.cts +33 -0
  10. package/auth-better-auth.d.mts +33 -0
  11. package/auth-better-auth.mjs +26 -0
  12. package/auth-clerk.cjs +34 -0
  13. package/auth-clerk.d.cts +32 -0
  14. package/auth-clerk.d.mts +32 -0
  15. package/auth-clerk.mjs +32 -0
  16. package/auth-workos.cjs +47 -0
  17. package/auth-workos.d.cts +30 -0
  18. package/auth-workos.d.mts +30 -0
  19. package/auth-workos.mjs +45 -0
  20. package/core-adapter.cjs +0 -0
  21. package/core-adapter.d.cts +2 -0
  22. package/core-adapter.d.mts +2 -0
  23. package/core-adapter.mjs +1 -0
  24. package/core-auth-adapter.cjs +3 -0
  25. package/core-auth-adapter.d.cts +2 -0
  26. package/core-auth-adapter.d.mts +2 -0
  27. package/core-auth-adapter.mjs +2 -0
  28. package/core-drizzle.cjs +32 -0
  29. package/core-drizzle.d.cts +21 -0
  30. package/core-drizzle.d.mts +21 -0
  31. package/core-drizzle.mjs +31 -0
  32. package/core-entity-config.cjs +13 -0
  33. package/core-entity-config.d.cts +2 -0
  34. package/core-entity-config.d.mts +2 -0
  35. package/core-entity-config.mjs +2 -0
  36. package/core-entity-heuristics.cjs +442 -0
  37. package/core-entity-heuristics.d.cts +68 -0
  38. package/core-entity-heuristics.d.mts +68 -0
  39. package/core-entity-heuristics.mjs +431 -0
  40. package/core-mapping-form.cjs +5 -0
  41. package/core-mapping-form.d.cts +2 -0
  42. package/core-mapping-form.d.mts +2 -0
  43. package/core-mapping-form.mjs +2 -0
  44. package/core-mapping-score.cjs +12 -0
  45. package/core-mapping-score.d.cts +2 -0
  46. package/core-mapping-score.d.mts +2 -0
  47. package/core-mapping-score.mjs +2 -0
  48. package/core-mappings-config.cjs +3 -0
  49. package/core-mappings-config.d.cts +2 -0
  50. package/core-mappings-config.d.mts +2 -0
  51. package/core-mappings-config.mjs +2 -0
  52. package/core-sync-engine.cjs +6 -0
  53. package/core-sync-engine.d.cts +2 -0
  54. package/core-sync-engine.d.mts +2 -0
  55. package/core-sync-engine.mjs +2 -0
  56. package/core-types.cjs +0 -0
  57. package/core-types.d.cts +2 -0
  58. package/core-types.d.mts +2 -0
  59. package/core-types.mjs +1 -0
  60. package/core-view-compiler.cjs +5 -0
  61. package/core-view-compiler.d.cts +2 -0
  62. package/core-view-compiler.d.mts +2 -0
  63. package/core-view-compiler.mjs +2 -0
  64. package/core.cjs +27 -0
  65. package/core.d.cts +9 -0
  66. package/core.d.mts +9 -0
  67. package/core.mjs +7 -0
  68. package/drizzle-meta-BGn0BqGb.cjs +9 -0
  69. package/drizzle-meta-CnC336L-.mjs +4 -0
  70. package/drizzle.cjs +107 -0
  71. package/drizzle.d.cts +28 -0
  72. package/drizzle.d.mts +28 -0
  73. package/drizzle.mjs +105 -0
  74. package/entity-config-Cvjo1cpv.cjs +4598 -0
  75. package/entity-config-D1DFyeUp.d.mts +2403 -0
  76. package/entity-config-DoR0khce.mjs +4533 -0
  77. package/entity-config-GczjD3y8.d.cts +2403 -0
  78. package/index.cjs +961 -0
  79. package/index.d.cts +57 -0
  80. package/index.d.mts +57 -0
  81. package/index.mjs +960 -0
  82. package/mapping-form-BHtPexzI.mjs +47 -0
  83. package/mapping-form-CUlFVfG8.d.cts +35 -0
  84. package/mapping-form-DdmDebC3.cjs +64 -0
  85. package/mapping-form-kjSiJaCX.d.mts +35 -0
  86. package/mapping-score-A2lRnlpk.d.mts +49 -0
  87. package/mapping-score-BJNe0lFn.mjs +87 -0
  88. package/mapping-score-BcjC0ZQs.d.cts +49 -0
  89. package/mapping-score-D9gfhtRu.cjs +146 -0
  90. package/mappings-config-CYPrzB3K.mjs +99 -0
  91. package/mappings-config-DTu_2cyx.cjs +104 -0
  92. package/mappings-config-DjRLCb2Q.d.cts +42 -0
  93. package/mappings-config-GxiYsSBQ.d.mts +42 -0
  94. package/namespace-probe-BtS0nXcr.d.cts +299 -0
  95. package/namespace-probe-CYMie-BI.d.mts +299 -0
  96. package/package.json +244 -21
  97. package/sdk-BZUn6wzs.mjs +764 -0
  98. package/sdk-CP_mYZa5.cjs +793 -0
  99. package/sdk.cjs +7 -0
  100. package/sdk.d.cts +33 -0
  101. package/sdk.d.mts +33 -0
  102. package/sdk.mjs +2 -0
  103. package/sync-engine-DUc0qWgo.cjs +230 -0
  104. package/sync-engine-DWnQPjV3.d.mts +183 -0
  105. package/sync-engine-DedXngop.mjs +207 -0
  106. package/sync-engine-uhfzKM-e.d.cts +183 -0
  107. package/types-BpsKVccL.d.cts +159 -0
  108. package/types-_qbJXMt_.d.mts +159 -0
  109. package/view-compiler-B4UYTvRM.d.cts +71 -0
  110. package/view-compiler-BHzbI9b7.cjs +202 -0
  111. package/view-compiler-CosFGar8.mjs +185 -0
  112. package/view-compiler-i_xFf-L0.d.mts +71 -0
  113. package/vite.cjs +38 -0
  114. package/vite.d.cts +23 -0
  115. package/vite.d.mts +23 -0
  116. package/vite.mjs +37 -0
  117. package/dist/index.mjs +0 -542
package/index.cjs ADDED
@@ -0,0 +1,961 @@
1
+ #!/usr/bin/env node
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_sdk = require("./sdk-CP_mYZa5.cjs");
4
+ const require_entity_config = require("./entity-config-Cvjo1cpv.cjs");
5
+ require("./mapping-score-D9gfhtRu.cjs");
6
+ const require_core_entity_heuristics = require("./core-entity-heuristics.cjs");
7
+ let node_fs = require("node:fs");
8
+ let node_module = require("node:module");
9
+ let node_path = require("node:path");
10
+ let jiti = require("jiti");
11
+ let node_url = require("node:url");
12
+ let node_child_process = require("node:child_process");
13
+ //#region src/render.ts
14
+ function checkLine(ok, label, detail) {
15
+ return ` ${ok ? "✅" : "❌"} ${label}${detail ? ` — ${detail}` : ""}\n`;
16
+ }
17
+ //#endregion
18
+ //#region src/connect.ts
19
+ const REASON_HINT$1 = {
20
+ ok: "",
21
+ invalid_api_key: "The key was rejected (401). Check CLIVLY_API_KEY.",
22
+ forbidden: "The key lacks access (403).",
23
+ rate_limited: "Rate limited (429) — retry shortly.",
24
+ server_error: "Clivly returned a 5xx — retry shortly.",
25
+ client_error: "The request was rejected (4xx).",
26
+ network_error: "No response — check the network / apiUrl."
27
+ };
28
+ const defaultConnect$1 = (apiKey, apiUrl) => require_sdk.createClivlySDK({
29
+ apiKey,
30
+ apiUrl,
31
+ retry: { maxAttempts: 1 }
32
+ }).connect();
33
+ const defaultLoadConfig = async (path) => {
34
+ const mod = await (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href).import(path);
35
+ return mod.default ?? mod;
36
+ };
37
+ /**
38
+ * `clivly connect` — a read-only pass/fail report: API key present → heartbeat
39
+ * (real SDK connect, proving the cloud saw it) → config schema loadable →
40
+ * trigger-URL note. Exit 0 iff the first three pass; a missing trigger URL warns
41
+ * but does not fail (push-only setups are valid). Deps injectable for tests.
42
+ */
43
+ async function runConnect(options) {
44
+ const out = options.out ?? ((line) => process.stdout.write(line));
45
+ out("\nClivly connect\n");
46
+ if (!options.apiKey) {
47
+ out(checkLine(false, "CLIVLY_API_KEY is not set."));
48
+ out(" Set it to your sk_live_… key from Settings → API keys.\n");
49
+ return 1;
50
+ }
51
+ const result = await (options.connectFn ?? defaultConnect$1)(options.apiKey, options.apiUrl);
52
+ if (result.ok) out(checkLine(true, `Heartbeat reached Clivly — org: ${result.org?.name ?? "(unnamed org)"}${result.org?.id ? ` (${result.org.id})` : ""}`));
53
+ else {
54
+ const status = result.status === null ? "no response" : `HTTP ${result.status}`;
55
+ out(checkLine(false, `Heartbeat failed (${result.reason}, ${status})`));
56
+ out(` ${REASON_HINT$1[result.reason]}\n`);
57
+ return 1;
58
+ }
59
+ const path = (0, node_path.resolve)(options.cwd, options.configPath ?? "clivly.config.ts");
60
+ const loadConfig = options.loadConfigFn ?? defaultLoadConfig;
61
+ let config;
62
+ try {
63
+ config = await loadConfig(path);
64
+ } catch (error) {
65
+ out(checkLine(false, "Load clivly.config", error.message));
66
+ return 1;
67
+ }
68
+ const tables = Object.values(config.entities?.entities ?? {}).map((e) => e.source).filter((s) => Boolean(s));
69
+ if (tables.length === 0) {
70
+ out(checkLine(false, "No entities configured in clivly.config"));
71
+ return 1;
72
+ }
73
+ out(checkLine(true, `Schema: ${tables.length} entity table(s) — ${tables.join(", ")}`));
74
+ const triggerUrl = config.syncTrigger?.url;
75
+ if (triggerUrl) out(checkLine(true, `Trigger route configured — ${triggerUrl}`));
76
+ else {
77
+ out(checkLine(false, "No trigger URL — dashboard-triggered sync is not wired"));
78
+ out(" Set syncTrigger.url (CLIVLY_SYNC_TRIGGER_URL) to your public tick route.\n");
79
+ }
80
+ return 0;
81
+ }
82
+ //#endregion
83
+ //#region src/detect.ts
84
+ const FRAMEWORK_MARKERS = [
85
+ {
86
+ framework: "nextjs",
87
+ deps: ["next"]
88
+ },
89
+ {
90
+ framework: "tanstack-start",
91
+ deps: ["@tanstack/react-start"]
92
+ },
93
+ {
94
+ framework: "sveltekit",
95
+ deps: ["@sveltejs/kit"]
96
+ },
97
+ {
98
+ framework: "remix",
99
+ deps: ["@remix-run/react", "@remix-run/node"]
100
+ },
101
+ {
102
+ framework: "nuxt",
103
+ deps: ["nuxt"]
104
+ }
105
+ ];
106
+ const ORM_MARKERS = [
107
+ {
108
+ orm: "drizzle",
109
+ deps: ["drizzle-orm"]
110
+ },
111
+ {
112
+ orm: "prisma",
113
+ deps: ["@prisma/client", "prisma"]
114
+ },
115
+ {
116
+ orm: "kysely",
117
+ deps: ["kysely"]
118
+ }
119
+ ];
120
+ const FRAMEWORK_LABELS = {
121
+ nextjs: "Next.js",
122
+ "tanstack-start": "TanStack Start",
123
+ sveltekit: "SvelteKit",
124
+ remix: "Remix",
125
+ nuxt: "Nuxt",
126
+ unknown: "Unknown"
127
+ };
128
+ function detectFramework(deps) {
129
+ for (const { framework, deps: markers } of FRAMEWORK_MARKERS) if (markers.some((dep) => dep in deps)) return framework;
130
+ return "unknown";
131
+ }
132
+ function detectOrm(deps) {
133
+ for (const { orm, deps: markers } of ORM_MARKERS) if (markers.some((dep) => dep in deps)) return orm;
134
+ return "unknown";
135
+ }
136
+ const LOCKFILES = [
137
+ {
138
+ file: "pnpm-lock.yaml",
139
+ pm: "pnpm"
140
+ },
141
+ {
142
+ file: "bun.lockb",
143
+ pm: "bun"
144
+ },
145
+ {
146
+ file: "yarn.lock",
147
+ pm: "yarn"
148
+ },
149
+ {
150
+ file: "package-lock.json",
151
+ pm: "npm"
152
+ }
153
+ ];
154
+ function detectPackageManager(cwd, fileExists = node_fs.existsSync) {
155
+ for (const { file, pm } of LOCKFILES) if (fileExists((0, node_path.join)(cwd, file))) return pm;
156
+ return "npm";
157
+ }
158
+ function addCommand(pm, packages) {
159
+ return {
160
+ command: pm,
161
+ args: [pm === "npm" ? "install" : "add", ...packages]
162
+ };
163
+ }
164
+ //#endregion
165
+ //#region src/discover-schema.ts
166
+ const WINDOWS_SEP = /\\/g;
167
+ const MODULE_EXT = /\.(ts|js|mjs|cts|mts)$/;
168
+ function toImportPath(schemaPath, cwd) {
169
+ const rel = (0, node_path.relative)(cwd, schemaPath).replace(WINDOWS_SEP, "/").replace(MODULE_EXT, "");
170
+ return rel.startsWith(".") ? rel : `./${rel}`;
171
+ }
172
+ function isDirectory(path) {
173
+ try {
174
+ return (0, node_fs.statSync)(path).isDirectory();
175
+ } catch {
176
+ return false;
177
+ }
178
+ }
179
+ function loadSchemaModule(schemaPath, load) {
180
+ return load(isDirectory(schemaPath) ? (0, node_path.join)(schemaPath, "index.ts") : schemaPath);
181
+ }
182
+ function defaultLoader(cwd) {
183
+ const jiti$1 = (0, jiti.createJiti)((0, node_url.pathToFileURL)((0, node_path.join)(cwd, "clivly.config.ts")).href);
184
+ return (specifier) => jiti$1.import(specifier);
185
+ }
186
+ async function loadPgCoreRuntime(load) {
187
+ try {
188
+ return await load("drizzle-orm/pg-core");
189
+ } catch {
190
+ return null;
191
+ }
192
+ }
193
+ function buildFkByColumn(pgCore, drizzle, table) {
194
+ const fkByColumn = /* @__PURE__ */ new Map();
195
+ if (!pgCore) return fkByColumn;
196
+ try {
197
+ for (const fk of pgCore.getTableConfig(table).foreignKeys) {
198
+ const ref = fk.reference();
199
+ const foreignTable = drizzle.getTableName(ref.foreignTable);
200
+ ref.columns.forEach((col, i) => {
201
+ const foreignCol = ref.foreignColumns[i];
202
+ if (foreignCol) fkByColumn.set(col.name, {
203
+ table: foreignTable,
204
+ column: foreignCol.name
205
+ });
206
+ });
207
+ }
208
+ } catch {}
209
+ return fkByColumn;
210
+ }
211
+ function toDiscoveredColumn(key, col, fkByColumn) {
212
+ let type;
213
+ try {
214
+ type = col.getSQLType?.();
215
+ } catch {
216
+ type = void 0;
217
+ }
218
+ const references = fkByColumn.get(col.name);
219
+ return {
220
+ key,
221
+ dbName: col.name,
222
+ isPrimary: Boolean(col.primary),
223
+ type,
224
+ nullable: !col.notNull,
225
+ ...references ? { references } : {}
226
+ };
227
+ }
228
+ async function discoverSchema(opts) {
229
+ const load = opts.load ?? defaultLoader(opts.cwd);
230
+ const mod = await loadSchemaModule(opts.schemaPath, load);
231
+ const drizzle = await load("drizzle-orm");
232
+ const pgCore = await loadPgCoreRuntime(load);
233
+ const importPath = toImportPath(opts.schemaPath, opts.cwd);
234
+ const tables = [];
235
+ let db = null;
236
+ for (const [moduleKey, value] of Object.entries(mod)) {
237
+ if (drizzle.is(value, drizzle.Table)) {
238
+ const fkByColumn = buildFkByColumn(pgCore, drizzle, value);
239
+ const columns = Object.entries(drizzle.getTableColumns(value)).map(([key, col]) => toDiscoveredColumn(key, col, fkByColumn));
240
+ tables.push({
241
+ moduleKey,
242
+ tableName: drizzle.getTableName(value),
243
+ columns
244
+ });
245
+ continue;
246
+ }
247
+ if (db === null && value && typeof value.select === "function") db = {
248
+ moduleKey,
249
+ importPath
250
+ };
251
+ }
252
+ return {
253
+ tables,
254
+ importPath,
255
+ db
256
+ };
257
+ }
258
+ //#endregion
259
+ //#region src/doctor.ts
260
+ async function loadSdkFromConfig(path) {
261
+ const mod = await (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href).import(path);
262
+ const sdk = mod.default ?? mod;
263
+ if (typeof sdk.doctor !== "function") throw new Error("default export is not a Clivly SDK instance");
264
+ return sdk;
265
+ }
266
+ /**
267
+ * `clivly doctor` — load the app's clivly.config, run the SDK's read-only setup
268
+ * checks, and print a ✅/❌ checklist. Exit 0 when every check passes. `loadSdk`
269
+ * and `out` are injectable for tests.
270
+ */
271
+ async function runDoctor(options) {
272
+ const out = options.out ?? ((line) => process.stdout.write(line));
273
+ const path = (0, node_path.resolve)(options.cwd, options.configPath ?? "clivly.config.ts");
274
+ const loadSdk = options.loadSdk ?? loadSdkFromConfig;
275
+ out("\nClivly doctor\n");
276
+ let sdk;
277
+ try {
278
+ sdk = await loadSdk(path);
279
+ } catch (error) {
280
+ out(checkLine(false, "Load clivly.config", error.message));
281
+ out(" Expected clivly.config.ts at your app root with `export default createClivlySDK({...})`.\n");
282
+ return 1;
283
+ }
284
+ const report = await sdk.doctor();
285
+ for (const check of report.checks) {
286
+ if (check.skipped) {
287
+ out(` ⚠️ ${check.name} — ${check.detail}\n`);
288
+ continue;
289
+ }
290
+ out(checkLine(check.ok, check.name, check.detail));
291
+ }
292
+ return report.ok ? 0 : 1;
293
+ }
294
+ //#endregion
295
+ //#region src/env-file.ts
296
+ function hasKey(content, key) {
297
+ return new RegExp(`^\\s*${key}=`, "m").test(content);
298
+ }
299
+ /**
300
+ * Append only the `entries` whose key is absent, each preceded by its comment.
301
+ * Existing keys/values are left exactly as-is. Returns the full new content.
302
+ */
303
+ function appendEnvKeys(existing, entries) {
304
+ const missing = entries.filter((e) => !hasKey(existing, e.key));
305
+ if (missing.length === 0) return existing;
306
+ const block = missing.map((e) => `${e.comment ? `# ${e.comment}\n` : ""}${e.key}=`).join("\n");
307
+ return `${existing}${existing.length > 0 && !existing.endsWith("\n") ? "\n" : ""}${existing.length > 0 ? "\n" : ""}# Clivly\n${block}\n`;
308
+ }
309
+ //#endregion
310
+ //#region src/install.ts
311
+ const defaultRun = (command, args, cwd) => new Promise((resolvePromise, reject) => {
312
+ const child = (0, node_child_process.spawn)(command, args, {
313
+ cwd,
314
+ stdio: "inherit"
315
+ });
316
+ child.on("error", reject);
317
+ child.on("close", (code) => resolvePromise(code ?? 0));
318
+ });
319
+ const QUIET_FLAGS = {
320
+ npm: [
321
+ "--no-audit",
322
+ "--no-fund",
323
+ "--loglevel=error"
324
+ ],
325
+ pnpm: ["--reporter=silent"],
326
+ yarn: [],
327
+ bun: []
328
+ };
329
+ /**
330
+ * Best-effort install: run `<pm> add <packages>`. Never throws — returns
331
+ * `{ ok, command }` so the caller can print the exact command on failure and
332
+ * continue scaffolding.
333
+ */
334
+ async function installPackages(options) {
335
+ const { command, args } = addCommand(options.pm, options.packages);
336
+ const fullArgs = [...args, ...QUIET_FLAGS[options.pm]];
337
+ const printable = `${command} ${fullArgs.join(" ")}`;
338
+ const run = options.run ?? defaultRun;
339
+ try {
340
+ return {
341
+ ok: await run(command, fullArgs, options.cwd) === 0,
342
+ command: printable
343
+ };
344
+ } catch {
345
+ return {
346
+ ok: false,
347
+ command: printable
348
+ };
349
+ }
350
+ }
351
+ //#endregion
352
+ //#region src/is-entry.ts
353
+ function isEntrypoint(argv1, moduleUrl, realpath = node_fs.realpathSync) {
354
+ if (!argv1) return false;
355
+ const modulePath = (0, node_url.fileURLToPath)(moduleUrl);
356
+ const resolve = (path) => {
357
+ try {
358
+ return realpath(path);
359
+ } catch {
360
+ return path;
361
+ }
362
+ };
363
+ return resolve(argv1) === resolve(modulePath);
364
+ }
365
+ //#endregion
366
+ //#region src/ping.ts
367
+ const REASON_HINT = {
368
+ ok: "",
369
+ invalid_api_key: "The key was rejected (401). Check CLIVLY_API_KEY.",
370
+ forbidden: "The key is valid but lacks access to this org (403).",
371
+ rate_limited: "Rate limited (429). Wait a moment and retry.",
372
+ server_error: "Clivly returned a server error (5xx). Retry shortly.",
373
+ client_error: "The request was rejected (4xx). Check your configuration.",
374
+ network_error: "Could not reach Clivly. Check your network or --api-url."
375
+ };
376
+ const defaultConnect = (apiKey, apiUrl) => require_sdk.createClivlySDK({
377
+ apiKey,
378
+ apiUrl,
379
+ retry: { maxAttempts: 1 }
380
+ }).connect();
381
+ function resultLines(result) {
382
+ if (result.ok) {
383
+ const lines = [checkLine(true, "Connected to Clivly")];
384
+ if (result.org) {
385
+ const name = result.org.name ?? "(unnamed org)";
386
+ const id = result.org.id ? ` (${result.org.id})` : "";
387
+ lines.push(` Org: ${name}${id}\n`);
388
+ }
389
+ return lines;
390
+ }
391
+ const status = result.status === null ? "no response" : `HTTP ${result.status}`;
392
+ return [checkLine(false, `Could not connect (${result.reason}, ${status})`), ` ${REASON_HINT[result.reason]}\n`];
393
+ }
394
+ /**
395
+ * `clivly ping` — send one heartbeat with the configured key and print a clear
396
+ * ✅/❌ with the resolved org or the failure reason. Verifies key + reachability
397
+ * from the terminal with zero code. `connect`/`out` are injectable for tests.
398
+ */
399
+ async function runPing(options) {
400
+ const out = options.out ?? ((line) => process.stdout.write(line));
401
+ out("\nClivly ping\n");
402
+ if (!options.apiKey) {
403
+ out(checkLine(false, "CLIVLY_API_KEY is not set."));
404
+ out(" Set it to your sk_live_… key from Settings → API keys.\n");
405
+ return 1;
406
+ }
407
+ const result = await (options.connect ?? defaultConnect)(options.apiKey, options.apiUrl);
408
+ for (const line of resultLines(result)) out(line);
409
+ return result.ok ? 0 : 1;
410
+ }
411
+ //#endregion
412
+ //#region src/plan-init.ts
413
+ function toScoredTable(table) {
414
+ return {
415
+ name: table.tableName,
416
+ columns: table.columns.map((c) => c.dbName),
417
+ columnsMeta: table.columns.map((c) => ({
418
+ name: c.dbName,
419
+ type: c.type,
420
+ nullable: c.nullable,
421
+ isPrimaryKey: c.isPrimary,
422
+ isForeignKey: c.references !== void 0,
423
+ ...c.references ? { references: c.references } : {}
424
+ }))
425
+ };
426
+ }
427
+ function rankCandidates(tables, concept) {
428
+ return tables.map((t) => ({
429
+ t,
430
+ m: require_core_entity_heuristics.matchesConcept(t.tableName, concept)
431
+ })).filter((c) => Boolean(c.m)).sort((a, b) => Number(b.m.exact) - Number(a.m.exact)).map((c) => c.t);
432
+ }
433
+ function selectEntities(tables) {
434
+ const contactCands = rankCandidates(tables, "contact");
435
+ const companyCands = rankCandidates(tables, "company");
436
+ const contactNames = new Set(contactCands.map((t) => t.tableName));
437
+ const companyNames = new Set(companyCands.map((t) => t.tableName));
438
+ const contactExclusive = contactCands.filter((t) => !companyNames.has(t.tableName));
439
+ const companyExclusive = companyCands.filter((t) => !contactNames.has(t.tableName));
440
+ const contact = contactExclusive[0] ?? contactCands[0] ?? null;
441
+ const companyPreferred = companyExclusive[0] ?? companyCands[0] ?? null;
442
+ return {
443
+ contact,
444
+ company: companyPreferred && companyPreferred.tableName !== contact?.tableName ? companyPreferred : companyCands.find((t) => t.tableName !== contact?.tableName) ?? null
445
+ };
446
+ }
447
+ function chooseSyncFields(table, todos) {
448
+ const columnKeys = table.columns.map((c) => c.key);
449
+ let cursorField = require_core_entity_heuristics.suggestCursorField(columnKeys);
450
+ if (!cursorField) {
451
+ cursorField = table.columns.find((c) => c.isPrimary)?.key ?? columnKeys[0] ?? "id";
452
+ todos.push(`No updated_at-like column on "${table.tableName}" — using "${cursorField}" as the sync cursor. Confirm it is monotonic.`);
453
+ }
454
+ const pkCols = table.columns.filter((c) => c.isPrimary);
455
+ if (pkCols.length === 1) return {
456
+ cursorField,
457
+ idField: null
458
+ };
459
+ const idField = pkCols[0]?.key ?? columnKeys[0] ?? "id";
460
+ todos.push(`Table "${table.tableName}" has ${pkCols.length === 0 ? "no" : "a composite"} primary key — set idField (guessed "${idField}").`);
461
+ return {
462
+ cursorField,
463
+ idField
464
+ };
465
+ }
466
+ function buildEntity(table, concept, todos) {
467
+ const canonical = require_entity_config.CANONICAL_FIELDS[concept];
468
+ const suggested = require_core_entity_heuristics.toFieldMap(require_core_entity_heuristics.scoreFieldMap(concept, toScoredTable(table)));
469
+ const fields = {};
470
+ for (const [key, column] of Object.entries(suggested)) if (canonical.includes(key)) fields[key] = column;
471
+ for (const required of require_core_entity_heuristics.requiredFieldsFor(concept)) if (!fields[required]) todos.push(`Could not map required ${concept} field "${required}" on "${table.tableName}" — add it manually.`);
472
+ const { cursorField, idField } = chooseSyncFields(table, todos);
473
+ return {
474
+ moduleKey: table.moduleKey,
475
+ tableName: table.tableName,
476
+ concept,
477
+ fields,
478
+ cursorField,
479
+ idField
480
+ };
481
+ }
482
+ function placeholderContact(todos) {
483
+ todos.push("No contact table detected. Filled in a placeholder — set `source`, `fields`, and the fromDrizzle table below, then run `clivly doctor`.");
484
+ return {
485
+ moduleKey: "contacts",
486
+ tableName: "contacts",
487
+ concept: "contact",
488
+ fields: {
489
+ name: "name",
490
+ email: "email"
491
+ },
492
+ cursorField: "updatedAt",
493
+ idField: null
494
+ };
495
+ }
496
+ function planInit(schema) {
497
+ const todos = [];
498
+ const { contact: contactTable, company: companyTable } = selectEntities(schema.tables);
499
+ const contactDetected = contactTable !== null;
500
+ const contact = contactTable ? buildEntity(contactTable, "contact", todos) : placeholderContact(todos);
501
+ let company = null;
502
+ if (companyTable) {
503
+ company = buildEntity(companyTable, "company", todos);
504
+ if (require_core_entity_heuristics.matchesConcept(companyTable.tableName, "contact")) todos.push(`"${companyTable.tableName}" matches both contact and company naming — assigned to company. Swap it to your contact entity if that's wrong.`);
505
+ }
506
+ if (contactTable) {
507
+ const scored = require_core_entity_heuristics.scoreRelationships(toScoredTable(contactTable), schema.tables.map(toScoredTable));
508
+ const top = scored[0];
509
+ const runnerUp = scored[1];
510
+ const isClearWinner = top !== void 0 && (runnerUp === void 0 || top.score - runnerUp.score >= 15);
511
+ if (top && isClearWinner) contact.relationship = {
512
+ name: top.value.name,
513
+ via: top.value.via
514
+ };
515
+ }
516
+ return {
517
+ importPath: schema.importPath,
518
+ db: schema.db,
519
+ contact,
520
+ company,
521
+ todos,
522
+ contactDetected
523
+ };
524
+ }
525
+ //#endregion
526
+ //#region src/render-config.ts
527
+ const CLIVLY_ENV_KEYS = [
528
+ {
529
+ key: "CLIVLY_API_KEY",
530
+ comment: "Org API key — Settings → API keys"
531
+ },
532
+ {
533
+ key: "CLIVLY_SYNC_TRIGGER_URL",
534
+ comment: "Public URL of your mounted tick route (e.g. https://app.example.com/api/clivly/tick)"
535
+ },
536
+ {
537
+ key: "CLIVLY_SYNC_TRIGGER_SECRET",
538
+ comment: "Signing secret — Integrations → Remote sync"
539
+ },
540
+ {
541
+ key: "CLIVLY_VERIFY_TOKEN",
542
+ comment: "Bearer token Clivly presents to /clivly/auth/verify"
543
+ }
544
+ ];
545
+ function renderFields(fields) {
546
+ return `{ ${Object.entries(fields).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ")} }`;
547
+ }
548
+ function renderRelationships(entity) {
549
+ if (!entity.relationship) return "";
550
+ const { name, via } = entity.relationship;
551
+ return `,\n relationships: { ${name}: { entity: "${name}", via: ${JSON.stringify(via)} } }`;
552
+ }
553
+ function renderEntity(key, entity) {
554
+ return ` ${key}: {
555
+ concept: "${entity.concept}",
556
+ source: "${entity.tableName}",
557
+ fields: ${renderFields(entity.fields)}${renderRelationships(entity)},
558
+ }`;
559
+ }
560
+ function renderSource(role, entity, dbExpr) {
561
+ const opts = entity.idField === null ? `{ entity: "${entity.tableName}", cursorField: "${entity.cursorField}" }` : `{ entity: "${entity.tableName}", cursorField: "${entity.cursorField}", idField: "${entity.idField}" }`;
562
+ return ` ${role}: fromDrizzle(${dbExpr}, schema.${entity.moduleKey}, ${opts})`;
563
+ }
564
+ function fallbackDbImportPath(schemaImportPath) {
565
+ const slash = schemaImportPath.lastIndexOf("/");
566
+ return `${slash >= 0 ? schemaImportPath.slice(0, slash) : "."}/db`;
567
+ }
568
+ function renderDbExpr(plan) {
569
+ return plan.db ? `schema.${plan.db.moduleKey}` : "db";
570
+ }
571
+ function renderImports(plan) {
572
+ const lines = [
573
+ "import { createClivlySDK } from \"clivly/sdk\";",
574
+ "import { fromDrizzle } from \"clivly/drizzle\";",
575
+ "import { defineClivlyConfig } from \"clivly/core\";",
576
+ "import { discoverFromDrizzle } from \"clivly/core/drizzle\";",
577
+ `import * as schema from "${plan.importPath}";`
578
+ ];
579
+ if (!plan.db) lines.push(`import { db } from "${fallbackDbImportPath(plan.importPath)}"; // TODO: point at your Drizzle instance`);
580
+ return lines.join("\n");
581
+ }
582
+ function renderTodoHeader(todos) {
583
+ if (todos.length === 0) return "";
584
+ return `// TODO(clivly): review before running \`clivly doctor\`:\n${todos.map((t) => `// - ${t}`).join("\n")}\n\n`;
585
+ }
586
+ function renderInitConfig(plan) {
587
+ const dbExpr = renderDbExpr(plan);
588
+ const entities = [renderEntity(plan.contact.moduleKey, plan.contact)];
589
+ if (plan.company) entities.push(renderEntity(plan.company.moduleKey, plan.company));
590
+ const sources = [renderSource("contacts", plan.contact, dbExpr)];
591
+ if (plan.company) sources.push(renderSource("companies", plan.company, dbExpr));
592
+ return `${renderTodoHeader(plan.todos)}${renderImports(plan)}
593
+
594
+ const entities = defineClivlyConfig({
595
+ entities: {
596
+ ${entities.join(",\n")},
597
+ },
598
+ });
599
+
600
+ export default createClivlySDK({
601
+ apiKey: process.env.CLIVLY_API_KEY!,
602
+ entities,
603
+ // Every table in your Drizzle schema, so the Clivly dashboard can offer them
604
+ // all when you add a mapping — not just the ones wired up as sources below.
605
+ schema: discoverFromDrizzle(schema),
606
+ source: {
607
+ ${sources.join(",\n")},
608
+ },
609
+ syncTrigger: {
610
+ // The public URL Clivly Cloud calls to trigger a sync — your mounted tick route.
611
+ url: process.env.CLIVLY_SYNC_TRIGGER_URL,
612
+ secret: process.env.CLIVLY_SYNC_TRIGGER_SECRET,
613
+ },
614
+ verifyToken: process.env.CLIVLY_VERIFY_TOKEN,
615
+ });
616
+ `;
617
+ }
618
+ //#endregion
619
+ //#region src/resolve-schema-path.ts
620
+ const COMMON_PATHS = [
621
+ "src/db/schema.ts",
622
+ "src/db/schema",
623
+ "db/schema.ts",
624
+ "db/schema",
625
+ "src/schema.ts",
626
+ "src/schema",
627
+ "drizzle/schema.ts",
628
+ "drizzle/schema"
629
+ ];
630
+ const DRIZZLE_CONFIG_NAMES = [
631
+ "drizzle.config.ts",
632
+ "drizzle.config.js",
633
+ "drizzle.config.mjs"
634
+ ];
635
+ function defaultReadDrizzleConfigSchema(cwd) {
636
+ for (const name of DRIZZLE_CONFIG_NAMES) {
637
+ const path = (0, node_path.resolve)(cwd, name);
638
+ if (!(0, node_fs.existsSync)(path)) continue;
639
+ try {
640
+ const schema = (0, jiti.createJiti)((0, node_url.pathToFileURL)(path).href)(path).default?.schema;
641
+ const first = Array.isArray(schema) ? schema[0] : schema;
642
+ if (typeof first === "string") return first;
643
+ } catch {}
644
+ }
645
+ return null;
646
+ }
647
+ function resolveSchemaPath(opts) {
648
+ const exists = opts.deps?.exists ?? node_fs.existsSync;
649
+ const readSchema = opts.deps?.readDrizzleConfigSchema ?? defaultReadDrizzleConfigSchema;
650
+ const toAbs = (p) => (0, node_path.isAbsolute)(p) ? p : (0, node_path.resolve)(opts.cwd, p);
651
+ if (opts.flag) return toAbs(opts.flag);
652
+ const fromConfig = readSchema(opts.cwd);
653
+ if (fromConfig) return toAbs(fromConfig);
654
+ for (const candidate of COMMON_PATHS) {
655
+ const abs = toAbs(candidate);
656
+ if (exists(abs)) return abs;
657
+ }
658
+ return null;
659
+ }
660
+ //#endregion
661
+ //#region src/scaffold.ts
662
+ const TANSTACK_ROUTE_PREFIX = /^src\/routes\//;
663
+ const TS_EXTENSION = /\.ts$/;
664
+ const ROUTE_PATHS = {
665
+ nextjs: {
666
+ tick: "app/api/clivly/tick/route.ts",
667
+ verify: "app/api/clivly/auth/verify/route.ts"
668
+ },
669
+ "tanstack-start": {
670
+ tick: "src/routes/api/clivly/tick.ts",
671
+ verify: "src/routes/api/clivly/auth/verify.ts"
672
+ },
673
+ sveltekit: {
674
+ tick: "src/routes/api/clivly/tick/+server.ts",
675
+ verify: "src/routes/api/clivly/auth/verify/+server.ts"
676
+ },
677
+ remix: {
678
+ tick: "app/routes/api.clivly.tick.ts",
679
+ verify: "app/routes/api.clivly.auth.verify.ts"
680
+ },
681
+ nuxt: {
682
+ tick: "server/api/clivly/tick.ts",
683
+ verify: "server/api/clivly/auth/verify.ts"
684
+ },
685
+ unknown: null
686
+ };
687
+ function routePath(framework, kind) {
688
+ const paths = ROUTE_PATHS[framework];
689
+ if (!paths) throw new Error(`No route path for framework: ${framework}`);
690
+ return paths[kind];
691
+ }
692
+ function configImport(routeFilePath) {
693
+ const rel = (0, node_path.relative)((0, node_path.dirname)(routeFilePath), "clivly.config").replace(/\\/g, "/");
694
+ return rel.startsWith(".") ? rel : `./${rel}`;
695
+ }
696
+ function renderBody(framework, importPath, verify) {
697
+ const preamble = verify ? `import clivly from "${importPath}";
698
+
699
+ // TODO(clivly): resolve the signed-in host user from the request (your auth stack).
700
+ const resolveUser = (_req: Request) => null;
701
+ const handler = clivly.createAuthVerifyHandler(resolveUser);
702
+ ` : `import clivly from "${importPath}";
703
+
704
+ const handler = clivly.createClivlyHandler();
705
+ `;
706
+ switch (framework) {
707
+ case "nextjs": return `${preamble}
708
+ export const POST = (request: Request) => handler(request);
709
+ `;
710
+ case "tanstack-start": return `import { createAPIFileRoute } from "@tanstack/react-start/api";
711
+ ${preamble}
712
+ export const APIRoute = createAPIFileRoute("__ROUTE__")({
713
+ POST: ({ request }) => handler(request),
714
+ });
715
+ `;
716
+ case "sveltekit": return `import type { RequestHandler } from "./$types";
717
+ ${preamble}
718
+ export const POST: RequestHandler = ({ request }) => handler(request);
719
+ `;
720
+ case "remix": return `import type { ActionFunctionArgs } from "@remix-run/node";
721
+ ${preamble}
722
+ export const action = ({ request }: ActionFunctionArgs) => handler(request);
723
+ `;
724
+ case "nuxt": return `import { toWebRequest } from "h3";
725
+ ${preamble}
726
+ export default defineEventHandler((event) => handler(toWebRequest(event)));
727
+ `;
728
+ default: throw new Error(`No route template for framework: ${framework}`);
729
+ }
730
+ }
731
+ function renderRouteFile(framework, kind) {
732
+ const path = routePath(framework, kind);
733
+ let body = renderBody(framework, configImport(path), kind === "verify");
734
+ if (framework === "tanstack-start") {
735
+ const url = `/${path.replace(TANSTACK_ROUTE_PREFIX, "").replace(TS_EXTENSION, "")}`;
736
+ body = body.replace("__ROUTE__", url);
737
+ }
738
+ return body;
739
+ }
740
+ const COMMON_FILES = [{
741
+ path: "clivly.config.ts",
742
+ description: "Your Clivly configuration",
743
+ owned: false
744
+ }];
745
+ function planScaffold(framework) {
746
+ const paths = ROUTE_PATHS[framework];
747
+ if (!paths) return [];
748
+ return [
749
+ {
750
+ path: paths.tick,
751
+ description: "Clivly sync-trigger route (createClivlyHandler)",
752
+ owned: true
753
+ },
754
+ {
755
+ path: paths.verify,
756
+ description: "Clivly auth-verify route (createAuthVerifyHandler) — finish resolveUser",
757
+ owned: true
758
+ },
759
+ ...COMMON_FILES
760
+ ];
761
+ }
762
+ //#endregion
763
+ //#region src/index.ts
764
+ function flagValue(argv, flag) {
765
+ const index = argv.indexOf(flag);
766
+ return index >= 0 ? argv[index + 1] : void 0;
767
+ }
768
+ function readDeps(cwd) {
769
+ const pkgPath = (0, node_path.join)(cwd, "package.json");
770
+ if (!(0, node_fs.existsSync)(pkgPath)) return {};
771
+ try {
772
+ const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf8"));
773
+ return {
774
+ ...pkg.dependencies,
775
+ ...pkg.devDependencies
776
+ };
777
+ } catch {
778
+ return {};
779
+ }
780
+ }
781
+ function readOwnVersion() {
782
+ const req = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
783
+ for (const rel of ["./package.json", "../package.json"]) try {
784
+ const pkg = req(rel);
785
+ if (pkg.name === "clivly" && pkg.version) return pkg.version;
786
+ } catch {}
787
+ }
788
+ function defaultReadEnv(cwd) {
789
+ const path = (0, node_path.join)(cwd, ".env");
790
+ return (0, node_fs.existsSync)(path) ? (0, node_fs.readFileSync)(path, "utf8") : "";
791
+ }
792
+ function defaultWriteEnv(cwd, contents) {
793
+ (0, node_fs.writeFileSync)((0, node_path.join)(cwd, ".env"), contents, "utf8");
794
+ }
795
+ function resolveInitDeps(options) {
796
+ return {
797
+ out: options.out ?? ((line) => process.stdout.write(line)),
798
+ readDepsFn: options.readDeps ?? readDeps,
799
+ resolvePath: options.resolveSchemaPath ?? ((cwd) => resolveSchemaPath({
800
+ cwd,
801
+ flag: options.schemaFlag
802
+ })),
803
+ discover: options.discoverSchema ?? ((schemaPath, cwd) => discoverSchema({
804
+ schemaPath,
805
+ cwd
806
+ })),
807
+ configExists: options.configExists ?? node_fs.existsSync,
808
+ writeConfig: options.writeConfig ?? ((path, contents) => {
809
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(path), { recursive: true });
810
+ (0, node_fs.writeFileSync)(path, contents, "utf8");
811
+ }),
812
+ detectPackageManager: options.detectPackageManager ?? detectPackageManager,
813
+ install: options.installPackages ?? installPackages,
814
+ readEnv: options.readEnv ?? defaultReadEnv,
815
+ readOwnVersion: options.readOwnVersion ?? readOwnVersion,
816
+ writeEnv: options.writeEnv ?? defaultWriteEnv,
817
+ framework: "unknown"
818
+ };
819
+ }
820
+ async function acquireSchema(options, deps) {
821
+ const { out } = deps;
822
+ const appDeps = deps.readDepsFn(options.cwd);
823
+ if (Object.keys(appDeps).length === 0) {
824
+ out("No package.json found here. Run `clivly init` from your app root.\n");
825
+ return { code: 1 };
826
+ }
827
+ const framework = detectFramework(appDeps);
828
+ const orm = detectOrm(appDeps);
829
+ deps.framework = framework;
830
+ out("\nClivly init\n");
831
+ out(` Framework: ${FRAMEWORK_LABELS[framework]}\n`);
832
+ out(` ORM: ${orm}\n\n`);
833
+ if (orm !== "drizzle") {
834
+ out(`clivly init currently generates config for Drizzle only. Detected ORM: ${orm}\n`);
835
+ return { code: 1 };
836
+ }
837
+ if (framework === "unknown") {
838
+ out("clivly init doesn't recognise your framework yet (looked for Next.js, TanStack Start, SvelteKit, Remix, Nuxt). Route file generation needs one of these.\n");
839
+ return { code: 1 };
840
+ }
841
+ const schemaPath = deps.resolvePath(options.cwd);
842
+ if (!schemaPath) {
843
+ out("Could not locate your Drizzle schema. Pass --schema <path>, or add a `schema` field to drizzle.config.ts.\n");
844
+ return { code: 1 };
845
+ }
846
+ out(` Schema: ${schemaPath}\n\n`);
847
+ try {
848
+ const schema = await deps.discover(schemaPath, options.cwd);
849
+ if (schema.tables.length === 0) {
850
+ out(`No Drizzle tables found in ${schemaPath}. Is this the right path?\n`);
851
+ return { code: 1 };
852
+ }
853
+ return schema;
854
+ } catch (error) {
855
+ out(`Failed to load the schema module: ${error.message}\n`);
856
+ out("The schema must import cleanly without a live DB connection.\n");
857
+ return { code: 1 };
858
+ }
859
+ }
860
+ function printPlan(out, plan) {
861
+ out("Detected:\n");
862
+ out(` contact ← ${plan.contact.tableName} fields ${JSON.stringify(plan.contact.fields)}\n`);
863
+ if (plan.company) out(` company ← ${plan.company.tableName} fields ${JSON.stringify(plan.company.fields)}\n`);
864
+ out("\n");
865
+ if (plan.todos.length > 0) {
866
+ out("Review after generation:\n");
867
+ for (const todo of plan.todos) out(` • ${todo}\n`);
868
+ out("\n");
869
+ }
870
+ }
871
+ function writeOwnedFile(options, deps, path, contents) {
872
+ const full = (0, node_path.join)(options.cwd, path);
873
+ if (deps.configExists(full) && !options.force) {
874
+ deps.out(` • ${path} exists — skipped (pass --force to overwrite)\n`);
875
+ return;
876
+ }
877
+ deps.writeConfig(full, contents);
878
+ deps.out(` ✓ ${path}\n`);
879
+ }
880
+ function writeScaffoldFiles(options, deps, plan) {
881
+ writeOwnedFile(options, deps, "clivly.config.ts", renderInitConfig(plan));
882
+ for (const file of planScaffold(deps.framework)) {
883
+ if (file.path === "clivly.config.ts") continue;
884
+ const kind = file.path.includes("verify") ? "verify" : "tick";
885
+ writeOwnedFile(options, deps, file.path, renderRouteFile(deps.framework, kind));
886
+ }
887
+ }
888
+ function writeEnvFile(options, deps) {
889
+ const nextEnv = appendEnvKeys(deps.readEnv(options.cwd), CLIVLY_ENV_KEYS);
890
+ deps.writeEnv(options.cwd, nextEnv);
891
+ deps.out(" ✓ .env placeholders\n");
892
+ }
893
+ function printNextSteps(out) {
894
+ out("\nNext steps:\n");
895
+ out(" 1. Set the CLIVLY_* values in .env\n");
896
+ out(" 2. Set CLIVLY_SYNC_TRIGGER_URL to your public tick route\n");
897
+ out(" 3. Finish resolveUser in the auth/verify route\n");
898
+ out(" 4. Run `clivly connect` to verify\n");
899
+ }
900
+ async function scaffold(options, deps, plan) {
901
+ const { out } = deps;
902
+ const pm = deps.detectPackageManager(options.cwd);
903
+ const version = deps.readOwnVersion();
904
+ const spec = version ? `clivly@${version}` : "clivly";
905
+ const result = await deps.install({
906
+ cwd: options.cwd,
907
+ pm,
908
+ packages: [spec]
909
+ });
910
+ if (result.ok) out(`Installed clivly (${pm})\n`);
911
+ else out(`Could not install automatically — run:\n ${result.command}\n`);
912
+ writeScaffoldFiles(options, deps, plan);
913
+ writeEnvFile(options, deps);
914
+ printNextSteps(out);
915
+ return plan.contactDetected ? 0 : 1;
916
+ }
917
+ async function runInit(options) {
918
+ const deps = resolveInitDeps(options);
919
+ const acquired = await acquireSchema(options, deps);
920
+ if ("code" in acquired) return acquired.code;
921
+ const plan = planInit(acquired);
922
+ printPlan(deps.out, plan);
923
+ return scaffold(options, deps, plan);
924
+ }
925
+ async function main(argv) {
926
+ const [command, ...rest] = argv;
927
+ switch (command) {
928
+ case "init": return await runInit({
929
+ cwd: process.cwd(),
930
+ force: rest.includes("--force"),
931
+ schemaFlag: flagValue(rest, "--schema")
932
+ });
933
+ case "ping": return await runPing({
934
+ apiKey: process.env.CLIVLY_API_KEY,
935
+ apiUrl: flagValue(rest, "--api-url") ?? process.env.CLIVLY_API_URL
936
+ });
937
+ case "doctor": return await runDoctor({
938
+ cwd: process.cwd(),
939
+ configPath: flagValue(rest, "--config")
940
+ });
941
+ case "connect": return await runConnect({
942
+ cwd: process.cwd(),
943
+ apiKey: process.env.CLIVLY_API_KEY,
944
+ apiUrl: flagValue(rest, "--api-url") ?? process.env.CLIVLY_API_URL,
945
+ configPath: flagValue(rest, "--config")
946
+ });
947
+ case void 0:
948
+ case "help":
949
+ case "--help":
950
+ process.stdout.write("clivly — embed a CRM in your app\n\nUsage:\n clivly init [--force] [--schema <path>] Scaffold Clivly (config, routes, .env) for your app\n clivly ping [--api-url <url>] Verify CLIVLY_API_KEY + reachability\n clivly doctor [--config <path>] Verify your clivly.config setup end-to-end\n clivly connect [--config <path>] One pass/fail check that setup is wired\n");
951
+ return 0;
952
+ default:
953
+ process.stdout.write(`Unknown command: ${command}\n`);
954
+ return 1;
955
+ }
956
+ }
957
+ if (isEntrypoint(process.argv[1], require("url").pathToFileURL(__filename).href)) main(process.argv.slice(2)).then((code) => {
958
+ process.exitCode = code;
959
+ });
960
+ //#endregion
961
+ exports.runInit = runInit;