clivly 0.9.2 → 0.9.3

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 CHANGED
@@ -60,7 +60,7 @@ database tables and does not modify your ORM schema.
60
60
  | [`clivly login`](#clivly-login) | Pair this machine and write credentials to your env file |
61
61
  | [`clivly status`](#clivly-status) | Check your setup, step by step. Read-only |
62
62
  | [`clivly add widget`](#clivly-add-widget) | Scaffold the chat-session route |
63
- | [`clivly push-schema`](#clivly-push-schema) | Report your schema to Clivly without deploying |
63
+ | [`clivly push-schema`](#clivly-push-schema) | Push your schema, custom objects and mappings to Clivly without deploying |
64
64
  | [`clivly dev`](#clivly-dev) | Tunnel localhost so remote sync can reach you |
65
65
  | `clivly --version` | Print the installed `clivly` version |
66
66
  | [`clivly ping` / `connect` / `doctor`](#deprecated-aliases) | Deprecated aliases running parts of `status` |
@@ -257,8 +257,36 @@ Walks your setup one step at a time and stops at the first thing that isn't
257
257
  ready, telling you what to do about it. **This is the command to reach for when
258
258
  something isn't working.** It is read-only and writes nothing.
259
259
 
260
- The ladder: credential → Clivly reachable → `clivly.config` loads →
261
- `clivly.config` is edge-safe → setup checks → sync trigger URL.
260
+ It answers two questions under two headings:
261
+
262
+ - **Workspace** — how far setup has got *on Clivly's side*. The server computes
263
+ this ladder and the CLI prints its verdict, so the terminal and the dashboard
264
+ can never disagree about where you are. Shown whenever reachability is in
265
+ scope; `clivly doctor` is local by definition and omits it.
266
+ - **This project** — the local ladder, run here on your machine: credential →
267
+ Clivly reachable → `clivly.config` loads → `clivly.config` is edge-safe →
268
+ setup checks → sync trigger URL.
269
+
270
+ ```text
271
+ Clivly status
272
+
273
+ Workspace
274
+ ✅ Create a credential
275
+ ✅ Install Clivly in your app heartbeat 2m ago
276
+ ✅ Check what the SDK reported found 12 tables
277
+ ⏳ Choose the table holding your people
278
+ 🔒 Let Clivly start a sync
279
+
280
+ This project
281
+ ✅ Step 1 of 6 · API key present
282
+ ✅ Step 2 of 6 · Clivly reachable — Acme
283
+ ...
284
+ ```
285
+
286
+ If the Workspace section reads
287
+ `⚠️ This Clivly server does not report setup progress`, or names a rejected key
288
+ or an unreachable host, that means the question could not be asked — not that
289
+ your workspace is empty.
262
290
 
263
291
  | Flag | Effect |
264
292
  | --- | --- |
@@ -303,9 +331,35 @@ for the full flow.
303
331
  npx clivly push-schema [--config <path>]
304
332
  ```
305
333
 
306
- Sends your discovered tables and columns to Clivly so the dashboard can offer
307
- them for mapping — without deploying your app. Reads your config; writes nothing
308
- locally. **Table and column names only; never row data.**
334
+ Pushes everything your config declares to Clivly, without deploying your app:
335
+
336
+ 1. **Discovery** — your tables and columns, so the dashboard can offer them for
337
+ mapping. **Names only; never row data.**
338
+ 2. **Custom object types** — anything you declared with `objectType`, so you
339
+ never create one in the dashboard and paste its id back into config.
340
+ 3. **Mappings** — the rows that make those objects actually receive data. Until
341
+ a mapping exists, a sync is accepted and stores nothing.
342
+
343
+ Reads your config; writes nothing locally. Safe to re-run — it reports what it
344
+ created, updated, or left alone.
345
+
346
+ ```text
347
+ Clivly push-schema
348
+ ✅ Schema reported to Clivly.
349
+ ✅ Created 1 custom object: client_accounts.
350
+ ✅ Mapped 2 entities: clients, advisors.
351
+ Open Integrations → Mapping to review them.
352
+ ```
353
+
354
+ **Exits `1` if Clivly refuses your config** — most often because an entity would
355
+ overwrite a mapping someone edited in the dashboard. Config owns the mappings it
356
+ declares; a dashboard-edited mapping is never silently overwritten. That makes
357
+ this command safe to run in CI as a schema gate.
358
+
359
+ > Two older-version paths, both non-fatal. If the **SDK in your app** predates
360
+ > this feature, only steps 1 and 2 run. If **Clivly** does not have the route
361
+ > yet, the command warns that it could not declare them and still exits `0` —
362
+ > your schema did land, so re-running once Clivly is upgraded finishes the job.
309
363
 
310
364
  ---
311
365
 
package/core.d.cts CHANGED
@@ -6,5 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-RQLGx8ub.cjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-CmEdAMwj.cjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-BG2O1an2.cjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.cjs";
9
10
  import { a as validateProjection, c as draftErrors, d as suggestJoinConditions, f as fanOutJoins, i as primaryKeyColumns, l as draftToProjection, n as coversUniqueKey, o as ProjectionDraft, p as joinFansOut, r as hasRichMeta, s as describeCardinality, t as columnMetaFor, u as emptyProjectionDraft } from "./internal-Da_dlcQ_.cjs";
10
- export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
11
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/core.d.mts CHANGED
@@ -6,5 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-D9dsonoi.mjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-Bo7pqXW3.mjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-DgQbrJON.mjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.mjs";
9
10
  import { a as validateProjection, c as draftErrors, d as suggestJoinConditions, f as fanOutJoins, i as primaryKeyColumns, l as draftToProjection, n as coversUniqueKey, o as ProjectionDraft, p as joinFansOut, r as hasRichMeta, s as describeCardinality, t as columnMetaFor, u as emptyProjectionDraft } from "./internal-CDY8g9Ic.mjs";
10
- export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
11
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/drizzle.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-DEtRrqtc.cjs";
2
- import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-C6aHjedO.cjs";
2
+ import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-EYyxWZnf.cjs";
3
3
  import { Table } from "drizzle-orm";
4
4
  //#region ../sdk/src/drizzle.d.ts
5
5
  interface DrizzleDb {
package/drizzle.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-CiK8MMEZ.mjs";
2
- import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-Dj-0Q3Hb.mjs";
2
+ import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-BmdGnYMn.mjs";
3
3
  import { Table } from "drizzle-orm";
4
4
  //#region ../sdk/src/drizzle.d.ts
5
5
  interface DrizzleDb {
package/index.cjs CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_entity_config = require("./entity-config-DtBsl2MQ.cjs");
4
4
  require("./mapping-score-CaB3F4Uu.cjs");
5
5
  const require_internal_entity_heuristics = require("./internal-entity-heuristics.cjs");
6
- const require_sdk = require("./sdk-D4Wp_uxb.cjs");
6
+ const require_sdk = require("./sdk-COLUHhJq.cjs");
7
7
  let node_child_process = require("node:child_process");
8
8
  let node_fs = require("node:fs");
9
9
  let node_module = require("node:module");
@@ -1002,7 +1002,7 @@ Usage:
1002
1002
  clivly init [options] Scaffold Clivly (config, routes, env file) for your app
1003
1003
  clivly add widget [options] Scaffold the chat-session route
1004
1004
  clivly status [options] Check your setup, step by step
1005
- clivly push-schema [options] Report your schema to Clivly without deploying
1005
+ clivly push-schema [options] Push schema, custom objects and mappings to Clivly
1006
1006
  clivly dev [options] Tunnel localhost so remote sync can reach you
1007
1007
  clivly login [options] Pair this machine and write credentials to your env file
1008
1008
  clivly --version Print the installed clivly version
@@ -1058,7 +1058,9 @@ Options:
1058
1058
  Usage:
1059
1059
  clivly status [--verbose] [--config <path>] [--env-file <path>]
1060
1060
 
1061
- Runs the setup ladder: credential, reachability, config load, edge-safety,
1061
+ Prints two sections. "Workspace" is how far setup has got on Clivly's side,
1062
+ computed by the server so the terminal and the dashboard agree. "This project"
1063
+ runs the local ladder: credential, reachability, config load, edge-safety,
1062
1064
  setup checks, sync trigger. Read-only — writes nothing.
1063
1065
 
1064
1066
  Options:
@@ -1066,13 +1068,23 @@ Options:
1066
1068
  --config <path> Path to clivly.config.ts
1067
1069
  --env-file <path> Extra env file to load
1068
1070
  `,
1069
- "push-schema": `clivly push-schema — report your schema without deploying
1071
+ "push-schema": `clivly push-schema — push everything your config declares
1070
1072
 
1071
1073
  Usage:
1072
1074
  clivly push-schema [--config <path>]
1073
1075
 
1074
- Sends your discovered tables to Clivly so the dashboard can offer them for
1075
- mapping. Reads your config; writes nothing locally.
1076
+ Pushes three things to Clivly, without deploying your app:
1077
+ 1. Your table and column NAMES, so the dashboard can offer them for mapping.
1078
+ Never row data — discovery is names only.
1079
+ 2. The custom object types you declared with \`objectType\`.
1080
+ 3. The mappings that make those objects actually receive rows.
1081
+
1082
+ Reads your config; writes nothing locally. Safe to re-run — it reports what it
1083
+ created, updated, or left alone.
1084
+
1085
+ Exits 1 if Clivly refuses your config, most often because an entity would
1086
+ overwrite a mapping edited in the dashboard. That makes it usable in CI as a
1087
+ schema gate.
1076
1088
  `,
1077
1089
  dev: `clivly dev — tunnel localhost so remote sync can reach you
1078
1090
 
@@ -2861,6 +2873,17 @@ function defaultReadConfigSource(path) {
2861
2873
  return null;
2862
2874
  }
2863
2875
  }
2876
+ /**
2877
+ * Ping Clivly at most once per command, memoised on the context.
2878
+ *
2879
+ * `clivly status` asks two questions of one response — "did this call reach
2880
+ * Clivly" and "how far has this workspace got" — and they must come from the
2881
+ * same answer, not two calls that could disagree.
2882
+ */
2883
+ function pingOnce(ctx) {
2884
+ ctx.pingResult ??= ctx.connect(ctx.apiKey, ctx.apiUrl);
2885
+ return ctx.pingResult;
2886
+ }
2864
2887
  const REASON_HINT = {
2865
2888
  ok: "",
2866
2889
  invalid_api_key: "The key was rejected (401). Check CLIVLY_SECRET_KEY (or legacy CLIVLY_API_KEY).",
@@ -2872,7 +2895,7 @@ const REASON_HINT = {
2872
2895
  network_error: "Could not reach Clivly. Check your network or --api-url."
2873
2896
  };
2874
2897
  const apiKeyStage = {
2875
- id: "credential",
2898
+ id: "api-key",
2876
2899
  label: "API key present",
2877
2900
  run(ctx) {
2878
2901
  if (ctx.apiKey) return Promise.resolve({
@@ -2887,11 +2910,11 @@ const apiKeyStage = {
2887
2910
  }
2888
2911
  };
2889
2912
  const reachabilityStage = {
2890
- id: "application",
2913
+ id: "reachable",
2891
2914
  label: "Clivly reachable",
2892
2915
  needs: [apiKeyStage.id],
2893
2916
  async run(ctx) {
2894
- const result = await ctx.connect(ctx.apiKey, ctx.apiUrl);
2917
+ const result = await pingOnce(ctx);
2895
2918
  if (result.ok) {
2896
2919
  const name = result.org?.name ?? "(unnamed org)";
2897
2920
  const id = result.org?.id ? ` (${result.org.id})` : "";
@@ -3025,19 +3048,57 @@ function defaultLoadSdk(path) {
3025
3048
  return loadConfigSdk(path, "doctor");
3026
3049
  }
3027
3050
  //#endregion
3051
+ //#region src/workspace-section.ts
3052
+ const MARK$1 = {
3053
+ active: "⏳",
3054
+ blocked: "🔒",
3055
+ done: "✅",
3056
+ failed: "❌",
3057
+ skipped: "⏭️ "
3058
+ };
3059
+ /**
3060
+ * The workspace half of `clivly status`, rendered from what the server sent.
3061
+ *
3062
+ * This module decides nothing about a stage's state — it prints the verdict the
3063
+ * server computed. A CLI that computed its own would be a second ladder, which
3064
+ * is the drift this whole change removes.
3065
+ *
3066
+ * "Unavailable" and "nothing is set up" are deliberately different outcomes. An
3067
+ * old server, a rejected key and a dead network all mean we could not ask; none
3068
+ * of them means the workspace is empty.
3069
+ */
3070
+ function workspaceSection(result) {
3071
+ if (!result) return {
3072
+ kind: "unavailable",
3073
+ reason: "Could not reach Clivly."
3074
+ };
3075
+ if (!result.ok) return {
3076
+ kind: "unavailable",
3077
+ reason: result.status === null ? "Could not reach Clivly (no response)." : `Clivly rejected the request — ${result.reason} (HTTP ${result.status}).`
3078
+ };
3079
+ if (!result.workspace) return {
3080
+ kind: "unavailable",
3081
+ reason: "This Clivly server does not report setup progress. Upgrade the server to see it here."
3082
+ };
3083
+ return {
3084
+ kind: "ladder",
3085
+ lines: result.workspace.steps.map((step) => ` ${MARK$1[step.state]} ${step.label}${step.evidence ? ` ${step.evidence}` : ""}`)
3086
+ };
3087
+ }
3088
+ //#endregion
3028
3089
  //#region src/status.ts
3029
3090
  /** Stage ids each legacy command maps onto. */
3030
3091
  const ALIAS_STAGE_IDS = {
3031
3092
  status: STATUS_STAGES.map((stage) => stage.id),
3032
- ping: ["credential", "application"],
3093
+ ping: ["api-key", "reachable"],
3033
3094
  doctor: [
3034
3095
  "config",
3035
3096
  "runtime",
3036
3097
  "setup-checks"
3037
3098
  ],
3038
3099
  connect: [
3039
- "credential",
3040
- "application",
3100
+ "api-key",
3101
+ "reachable",
3041
3102
  "config",
3042
3103
  "trigger-url"
3043
3104
  ]
@@ -3093,6 +3154,13 @@ async function runStatus(options) {
3093
3154
  };
3094
3155
  out("\nClivly status\n");
3095
3156
  const results = await runLadder(stages, ctx);
3157
+ if (selected.has("reachable")) {
3158
+ const section = workspaceSection(ctx.pingResult ? await ctx.pingResult : null);
3159
+ out("\nWorkspace\n");
3160
+ if (section.kind === "ladder") for (const line of section.lines) out(`${line}\n`);
3161
+ else out(` ⚠️ ${section.reason}\n`);
3162
+ }
3163
+ out("\nThis project\n");
3096
3164
  for (const result of results) renderStage(result, options.verbose ?? false, out);
3097
3165
  const code = ladderExitCode(results);
3098
3166
  if (code !== 0 && results.some((result) => result.state === "pending")) out("\nSetup is incomplete — finish the steps above and re-run.\n");
@@ -3122,6 +3190,7 @@ const KNOWN_COMMANDS = [
3122
3190
  "init",
3123
3191
  "status",
3124
3192
  "push-schema",
3193
+ "add",
3125
3194
  "dev",
3126
3195
  "login",
3127
3196
  "help",
@@ -3889,6 +3958,7 @@ if (isEntrypoint(process.argv[1], require("url").pathToFileURL(__filename).href)
3889
3958
  process.exitCode = code;
3890
3959
  });
3891
3960
  //#endregion
3961
+ exports.KNOWN_COMMANDS = KNOWN_COMMANDS;
3892
3962
  exports.credentialsMissing = credentialsMissing;
3893
3963
  exports.main = main;
3894
3964
  exports.nearestCommand = nearestCommand;
package/index.d.cts CHANGED
@@ -58,6 +58,7 @@ interface ProbeHit {
58
58
  }
59
59
  //#endregion
60
60
  //#region src/index.d.ts
61
+ declare const KNOWN_COMMANDS: readonly ["init", "status", "push-schema", "add", "dev", "login", "help", "ping", "doctor", "connect"];
61
62
  declare function nearestCommand(input: string): string | undefined;
62
63
  interface AddDeps {
63
64
  fileExists: (path: string) => boolean;
@@ -117,4 +118,4 @@ declare function runInit(options: RunInitOptions): Promise<number>;
117
118
  declare function runInitFromArgv(argv: string[], cwd: string): Promise<number>;
118
119
  declare function main(argv: string[]): Promise<number>;
119
120
  //#endregion
120
- export { AddDeps, RunInitOptions, credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
121
+ export { AddDeps, KNOWN_COMMANDS, RunInitOptions, credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
package/index.d.mts CHANGED
@@ -58,6 +58,7 @@ interface ProbeHit {
58
58
  }
59
59
  //#endregion
60
60
  //#region src/index.d.ts
61
+ declare const KNOWN_COMMANDS: readonly ["init", "status", "push-schema", "add", "dev", "login", "help", "ping", "doctor", "connect"];
61
62
  declare function nearestCommand(input: string): string | undefined;
62
63
  interface AddDeps {
63
64
  fileExists: (path: string) => boolean;
@@ -117,4 +118,4 @@ declare function runInit(options: RunInitOptions): Promise<number>;
117
118
  declare function runInitFromArgv(argv: string[], cwd: string): Promise<number>;
118
119
  declare function main(argv: string[]): Promise<number>;
119
120
  //#endregion
120
- export { AddDeps, RunInitOptions, credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
121
+ export { AddDeps, KNOWN_COMMANDS, RunInitOptions, credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
package/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { t as CANONICAL_FIELDS } from "./entity-config-kUHri9yA.mjs";
3
3
  import "./mapping-score-C68uAqGB.mjs";
4
4
  import { matchesConcept, requiredFieldsFor, scoreFieldMap, scoreRelationships, suggestCursorField, toFieldMap } from "./internal-entity-heuristics.mjs";
5
- import { t as createClivlySDK } from "./sdk-DXV8M_Io.mjs";
5
+ import { t as createClivlySDK } from "./sdk-CZNXBsgA.mjs";
6
6
  import { createRequire } from "node:module";
7
7
  import { spawn } from "node:child_process";
8
8
  import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, watch, writeFileSync } from "node:fs";
@@ -1001,7 +1001,7 @@ Usage:
1001
1001
  clivly init [options] Scaffold Clivly (config, routes, env file) for your app
1002
1002
  clivly add widget [options] Scaffold the chat-session route
1003
1003
  clivly status [options] Check your setup, step by step
1004
- clivly push-schema [options] Report your schema to Clivly without deploying
1004
+ clivly push-schema [options] Push schema, custom objects and mappings to Clivly
1005
1005
  clivly dev [options] Tunnel localhost so remote sync can reach you
1006
1006
  clivly login [options] Pair this machine and write credentials to your env file
1007
1007
  clivly --version Print the installed clivly version
@@ -1057,7 +1057,9 @@ Options:
1057
1057
  Usage:
1058
1058
  clivly status [--verbose] [--config <path>] [--env-file <path>]
1059
1059
 
1060
- Runs the setup ladder: credential, reachability, config load, edge-safety,
1060
+ Prints two sections. "Workspace" is how far setup has got on Clivly's side,
1061
+ computed by the server so the terminal and the dashboard agree. "This project"
1062
+ runs the local ladder: credential, reachability, config load, edge-safety,
1061
1063
  setup checks, sync trigger. Read-only — writes nothing.
1062
1064
 
1063
1065
  Options:
@@ -1065,13 +1067,23 @@ Options:
1065
1067
  --config <path> Path to clivly.config.ts
1066
1068
  --env-file <path> Extra env file to load
1067
1069
  `,
1068
- "push-schema": `clivly push-schema — report your schema without deploying
1070
+ "push-schema": `clivly push-schema — push everything your config declares
1069
1071
 
1070
1072
  Usage:
1071
1073
  clivly push-schema [--config <path>]
1072
1074
 
1073
- Sends your discovered tables to Clivly so the dashboard can offer them for
1074
- mapping. Reads your config; writes nothing locally.
1075
+ Pushes three things to Clivly, without deploying your app:
1076
+ 1. Your table and column NAMES, so the dashboard can offer them for mapping.
1077
+ Never row data — discovery is names only.
1078
+ 2. The custom object types you declared with \`objectType\`.
1079
+ 3. The mappings that make those objects actually receive rows.
1080
+
1081
+ Reads your config; writes nothing locally. Safe to re-run — it reports what it
1082
+ created, updated, or left alone.
1083
+
1084
+ Exits 1 if Clivly refuses your config, most often because an entity would
1085
+ overwrite a mapping edited in the dashboard. That makes it usable in CI as a
1086
+ schema gate.
1075
1087
  `,
1076
1088
  dev: `clivly dev — tunnel localhost so remote sync can reach you
1077
1089
 
@@ -2860,6 +2872,17 @@ function defaultReadConfigSource(path) {
2860
2872
  return null;
2861
2873
  }
2862
2874
  }
2875
+ /**
2876
+ * Ping Clivly at most once per command, memoised on the context.
2877
+ *
2878
+ * `clivly status` asks two questions of one response — "did this call reach
2879
+ * Clivly" and "how far has this workspace got" — and they must come from the
2880
+ * same answer, not two calls that could disagree.
2881
+ */
2882
+ function pingOnce(ctx) {
2883
+ ctx.pingResult ??= ctx.connect(ctx.apiKey, ctx.apiUrl);
2884
+ return ctx.pingResult;
2885
+ }
2863
2886
  const REASON_HINT = {
2864
2887
  ok: "",
2865
2888
  invalid_api_key: "The key was rejected (401). Check CLIVLY_SECRET_KEY (or legacy CLIVLY_API_KEY).",
@@ -2871,7 +2894,7 @@ const REASON_HINT = {
2871
2894
  network_error: "Could not reach Clivly. Check your network or --api-url."
2872
2895
  };
2873
2896
  const apiKeyStage = {
2874
- id: "credential",
2897
+ id: "api-key",
2875
2898
  label: "API key present",
2876
2899
  run(ctx) {
2877
2900
  if (ctx.apiKey) return Promise.resolve({
@@ -2886,11 +2909,11 @@ const apiKeyStage = {
2886
2909
  }
2887
2910
  };
2888
2911
  const reachabilityStage = {
2889
- id: "application",
2912
+ id: "reachable",
2890
2913
  label: "Clivly reachable",
2891
2914
  needs: [apiKeyStage.id],
2892
2915
  async run(ctx) {
2893
- const result = await ctx.connect(ctx.apiKey, ctx.apiUrl);
2916
+ const result = await pingOnce(ctx);
2894
2917
  if (result.ok) {
2895
2918
  const name = result.org?.name ?? "(unnamed org)";
2896
2919
  const id = result.org?.id ? ` (${result.org.id})` : "";
@@ -3024,19 +3047,57 @@ function defaultLoadSdk(path) {
3024
3047
  return loadConfigSdk(path, "doctor");
3025
3048
  }
3026
3049
  //#endregion
3050
+ //#region src/workspace-section.ts
3051
+ const MARK$1 = {
3052
+ active: "⏳",
3053
+ blocked: "🔒",
3054
+ done: "✅",
3055
+ failed: "❌",
3056
+ skipped: "⏭️ "
3057
+ };
3058
+ /**
3059
+ * The workspace half of `clivly status`, rendered from what the server sent.
3060
+ *
3061
+ * This module decides nothing about a stage's state — it prints the verdict the
3062
+ * server computed. A CLI that computed its own would be a second ladder, which
3063
+ * is the drift this whole change removes.
3064
+ *
3065
+ * "Unavailable" and "nothing is set up" are deliberately different outcomes. An
3066
+ * old server, a rejected key and a dead network all mean we could not ask; none
3067
+ * of them means the workspace is empty.
3068
+ */
3069
+ function workspaceSection(result) {
3070
+ if (!result) return {
3071
+ kind: "unavailable",
3072
+ reason: "Could not reach Clivly."
3073
+ };
3074
+ if (!result.ok) return {
3075
+ kind: "unavailable",
3076
+ reason: result.status === null ? "Could not reach Clivly (no response)." : `Clivly rejected the request — ${result.reason} (HTTP ${result.status}).`
3077
+ };
3078
+ if (!result.workspace) return {
3079
+ kind: "unavailable",
3080
+ reason: "This Clivly server does not report setup progress. Upgrade the server to see it here."
3081
+ };
3082
+ return {
3083
+ kind: "ladder",
3084
+ lines: result.workspace.steps.map((step) => ` ${MARK$1[step.state]} ${step.label}${step.evidence ? ` ${step.evidence}` : ""}`)
3085
+ };
3086
+ }
3087
+ //#endregion
3027
3088
  //#region src/status.ts
3028
3089
  /** Stage ids each legacy command maps onto. */
3029
3090
  const ALIAS_STAGE_IDS = {
3030
3091
  status: STATUS_STAGES.map((stage) => stage.id),
3031
- ping: ["credential", "application"],
3092
+ ping: ["api-key", "reachable"],
3032
3093
  doctor: [
3033
3094
  "config",
3034
3095
  "runtime",
3035
3096
  "setup-checks"
3036
3097
  ],
3037
3098
  connect: [
3038
- "credential",
3039
- "application",
3099
+ "api-key",
3100
+ "reachable",
3040
3101
  "config",
3041
3102
  "trigger-url"
3042
3103
  ]
@@ -3092,6 +3153,13 @@ async function runStatus(options) {
3092
3153
  };
3093
3154
  out("\nClivly status\n");
3094
3155
  const results = await runLadder(stages, ctx);
3156
+ if (selected.has("reachable")) {
3157
+ const section = workspaceSection(ctx.pingResult ? await ctx.pingResult : null);
3158
+ out("\nWorkspace\n");
3159
+ if (section.kind === "ladder") for (const line of section.lines) out(`${line}\n`);
3160
+ else out(` ⚠️ ${section.reason}\n`);
3161
+ }
3162
+ out("\nThis project\n");
3095
3163
  for (const result of results) renderStage(result, options.verbose ?? false, out);
3096
3164
  const code = ladderExitCode(results);
3097
3165
  if (code !== 0 && results.some((result) => result.state === "pending")) out("\nSetup is incomplete — finish the steps above and re-run.\n");
@@ -3121,6 +3189,7 @@ const KNOWN_COMMANDS = [
3121
3189
  "init",
3122
3190
  "status",
3123
3191
  "push-schema",
3192
+ "add",
3124
3193
  "dev",
3125
3194
  "login",
3126
3195
  "help",
@@ -3888,4 +3957,4 @@ if (isEntrypoint(process.argv[1], import.meta.url)) main(process.argv.slice(2)).
3888
3957
  process.exitCode = code;
3889
3958
  });
3890
3959
  //#endregion
3891
- export { credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
3960
+ export { KNOWN_COMMANDS, credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
@@ -0,0 +1,130 @@
1
+ //#region ../core/src/integration-state.d.ts
2
+ declare const INTEGRATION_STEPS: readonly ["credential", "application", "schema", "mapping", "trigger", "sync", "widget"];
3
+ type StepId = (typeof INTEGRATION_STEPS)[number];
4
+ /** `blocked` means "prerequisite unmet", never "broken" — see C-19. */
5
+ type StepState = "done" | "active" | "blocked" | "failed" | "skipped";
6
+ type IntegrationStatus = "not_configured" | "sdk_installed" | "awaiting_connection" | "connected" | "schema_discovered" | "mapping_required" | "trigger_required" | "ready_to_sync" | "syncing" | "healthy" | "warning" | "error" |
7
+ /**
8
+ * @deprecated No longer derived. Liveness is its own dimension — read
9
+ * `ConnectionHealth` from `deriveLiveness`. Setup progress does not move
10
+ * backwards just because an app stopped running.
11
+ */
12
+ "disconnected";
13
+ interface RunFact {
14
+ at: number;
15
+ message?: string;
16
+ ok: boolean;
17
+ }
18
+ /**
19
+ * Where the reporting app is running, as the app itself understands it.
20
+ *
21
+ * `unknown` is a real value, not an absence: `detectHostEnvironment` refuses to
22
+ * guess when nothing places the app, and a bare `node server.js` with no
23
+ * NODE_ENV must still be able to connect and be shown as itself. Defaulting
24
+ * such a host to `local_dev` would file a production box's faults under local
25
+ * development, where nobody would look for them.
26
+ */
27
+ type HostEnvironment = "local_dev" | "preview" | "production" | "unknown";
28
+ interface IntegrationFacts {
29
+ discoveredTableCount: number;
30
+ /** The environment the app reported on its last heartbeat. */
31
+ environment?: HostEnvironment | null;
32
+ /**
33
+ * Has this app ever checked in? Distinguishes "never installed" from "was
34
+ * working, now silent" — the same absent heartbeat means different things,
35
+ * and only one of them is diagnosed rather than set up.
36
+ */
37
+ everConnected: boolean;
38
+ hasCredential: boolean;
39
+ /**
40
+ * The cadence the app said it heartbeats at. Liveness windows are multiples
41
+ * of this rather than a fixed constant, so an app that deliberately reports
42
+ * slowly is not called dead for obeying its own configuration.
43
+ */
44
+ heartbeatIntervalMs?: number | null;
45
+ lastHeartbeatAt: number | null;
46
+ lastRun: RunFact | null;
47
+ mappedEntityCount: number;
48
+ now: number;
49
+ /**
50
+ * Deployment presence, when this deployment has one. Absent or `http`
51
+ * transport keeps the cadence-derived behaviour every SDK in the field uses.
52
+ */
53
+ presence?: DeploymentPresence | null;
54
+ /**
55
+ * Faults the running app diagnosed about itself on its last heartbeat — an
56
+ * unreadable database, a secret missing from the process environment. These
57
+ * are what make a reachable app `live_with_issues`; unfinished setup is not.
58
+ */
59
+ reportedIssues?: StepProblem[];
60
+ syncInFlight: boolean;
61
+ /** A trigger URL and secret both exist, so Clivly can ask the app to sync. */
62
+ triggerConfigured: boolean;
63
+ warnings: string[];
64
+ widgetInstalled: boolean;
65
+ }
66
+ interface StepProblem {
67
+ code: string;
68
+ fix: string;
69
+ message: string;
70
+ }
71
+ interface IntegrationStep {
72
+ blockedBy: StepId | null;
73
+ evidence: string | null;
74
+ id: StepId;
75
+ label: string;
76
+ problem: StepProblem | null;
77
+ state: StepState;
78
+ }
79
+ type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
80
+ /** Which algorithm decides whether this deployment is up. */
81
+ type PresenceTransport = "websocket" | "http";
82
+ type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
83
+ /**
84
+ * A deployment's connection state as its `PresenceRoom` last persisted it.
85
+ *
86
+ * This exists because a socket has an explicit state and a timestamp does not.
87
+ * Deriving WebSocket liveness from heartbeat age would leave a disconnected app
88
+ * shown as live for 150 seconds and offline only after ten minutes, which is
89
+ * the whole reason this transport exists.
90
+ */
91
+ interface DeploymentPresence {
92
+ /** Sockets currently accepted in the room. */
93
+ activeInstanceCount: number;
94
+ connectionState: PresenceConnectionState;
95
+ transport: PresenceTransport;
96
+ }
97
+ interface ConnectionLiveness {
98
+ /** `"8s ago"`, or `"No heartbeat yet"` when the app has never reported. */
99
+ ageLabel: string;
100
+ ageMs: number | null;
101
+ environment: HostEnvironment | null;
102
+ health: ConnectionHealth;
103
+ /**
104
+ * Faults on a reachable app. Empty unless `health` is `live_with_issues`.
105
+ */
106
+ issues: StepProblem[];
107
+ /** Present when a deployment-presence record supplied this liveness result. */
108
+ presence?: Pick<DeploymentPresence, "connectionState" | "transport">;
109
+ /** Is a heartbeat recent enough to speak for the app as it is right now? */
110
+ reachable: boolean;
111
+ }
112
+ interface NextAction {
113
+ command: string | null;
114
+ envFile: string | null;
115
+ label: string;
116
+ }
117
+ interface IntegrationState {
118
+ /**
119
+ * Is the app running right now. Deliberately alongside `status` rather than
120
+ * folded into it: a surface must be able to say "setup reached mapping" and
121
+ * "the app is offline" in the same breath without either overwriting the
122
+ * other.
123
+ */
124
+ liveness: ConnectionLiveness;
125
+ next: NextAction | null;
126
+ status: IntegrationStatus;
127
+ steps: IntegrationStep[];
128
+ }
129
+ //#endregion
130
+ export { IntegrationStep as a, StepProblem as c, IntegrationStatus as i, StepState as l, IntegrationFacts as n, NextAction as o, IntegrationState as r, StepId as s, ConnectionLiveness as t };
@@ -0,0 +1,130 @@
1
+ //#region ../core/src/integration-state.d.ts
2
+ declare const INTEGRATION_STEPS: readonly ["credential", "application", "schema", "mapping", "trigger", "sync", "widget"];
3
+ type StepId = (typeof INTEGRATION_STEPS)[number];
4
+ /** `blocked` means "prerequisite unmet", never "broken" — see C-19. */
5
+ type StepState = "done" | "active" | "blocked" | "failed" | "skipped";
6
+ type IntegrationStatus = "not_configured" | "sdk_installed" | "awaiting_connection" | "connected" | "schema_discovered" | "mapping_required" | "trigger_required" | "ready_to_sync" | "syncing" | "healthy" | "warning" | "error" |
7
+ /**
8
+ * @deprecated No longer derived. Liveness is its own dimension — read
9
+ * `ConnectionHealth` from `deriveLiveness`. Setup progress does not move
10
+ * backwards just because an app stopped running.
11
+ */
12
+ "disconnected";
13
+ interface RunFact {
14
+ at: number;
15
+ message?: string;
16
+ ok: boolean;
17
+ }
18
+ /**
19
+ * Where the reporting app is running, as the app itself understands it.
20
+ *
21
+ * `unknown` is a real value, not an absence: `detectHostEnvironment` refuses to
22
+ * guess when nothing places the app, and a bare `node server.js` with no
23
+ * NODE_ENV must still be able to connect and be shown as itself. Defaulting
24
+ * such a host to `local_dev` would file a production box's faults under local
25
+ * development, where nobody would look for them.
26
+ */
27
+ type HostEnvironment = "local_dev" | "preview" | "production" | "unknown";
28
+ interface IntegrationFacts {
29
+ discoveredTableCount: number;
30
+ /** The environment the app reported on its last heartbeat. */
31
+ environment?: HostEnvironment | null;
32
+ /**
33
+ * Has this app ever checked in? Distinguishes "never installed" from "was
34
+ * working, now silent" — the same absent heartbeat means different things,
35
+ * and only one of them is diagnosed rather than set up.
36
+ */
37
+ everConnected: boolean;
38
+ hasCredential: boolean;
39
+ /**
40
+ * The cadence the app said it heartbeats at. Liveness windows are multiples
41
+ * of this rather than a fixed constant, so an app that deliberately reports
42
+ * slowly is not called dead for obeying its own configuration.
43
+ */
44
+ heartbeatIntervalMs?: number | null;
45
+ lastHeartbeatAt: number | null;
46
+ lastRun: RunFact | null;
47
+ mappedEntityCount: number;
48
+ now: number;
49
+ /**
50
+ * Deployment presence, when this deployment has one. Absent or `http`
51
+ * transport keeps the cadence-derived behaviour every SDK in the field uses.
52
+ */
53
+ presence?: DeploymentPresence | null;
54
+ /**
55
+ * Faults the running app diagnosed about itself on its last heartbeat — an
56
+ * unreadable database, a secret missing from the process environment. These
57
+ * are what make a reachable app `live_with_issues`; unfinished setup is not.
58
+ */
59
+ reportedIssues?: StepProblem[];
60
+ syncInFlight: boolean;
61
+ /** A trigger URL and secret both exist, so Clivly can ask the app to sync. */
62
+ triggerConfigured: boolean;
63
+ warnings: string[];
64
+ widgetInstalled: boolean;
65
+ }
66
+ interface StepProblem {
67
+ code: string;
68
+ fix: string;
69
+ message: string;
70
+ }
71
+ interface IntegrationStep {
72
+ blockedBy: StepId | null;
73
+ evidence: string | null;
74
+ id: StepId;
75
+ label: string;
76
+ problem: StepProblem | null;
77
+ state: StepState;
78
+ }
79
+ type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
80
+ /** Which algorithm decides whether this deployment is up. */
81
+ type PresenceTransport = "websocket" | "http";
82
+ type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
83
+ /**
84
+ * A deployment's connection state as its `PresenceRoom` last persisted it.
85
+ *
86
+ * This exists because a socket has an explicit state and a timestamp does not.
87
+ * Deriving WebSocket liveness from heartbeat age would leave a disconnected app
88
+ * shown as live for 150 seconds and offline only after ten minutes, which is
89
+ * the whole reason this transport exists.
90
+ */
91
+ interface DeploymentPresence {
92
+ /** Sockets currently accepted in the room. */
93
+ activeInstanceCount: number;
94
+ connectionState: PresenceConnectionState;
95
+ transport: PresenceTransport;
96
+ }
97
+ interface ConnectionLiveness {
98
+ /** `"8s ago"`, or `"No heartbeat yet"` when the app has never reported. */
99
+ ageLabel: string;
100
+ ageMs: number | null;
101
+ environment: HostEnvironment | null;
102
+ health: ConnectionHealth;
103
+ /**
104
+ * Faults on a reachable app. Empty unless `health` is `live_with_issues`.
105
+ */
106
+ issues: StepProblem[];
107
+ /** Present when a deployment-presence record supplied this liveness result. */
108
+ presence?: Pick<DeploymentPresence, "connectionState" | "transport">;
109
+ /** Is a heartbeat recent enough to speak for the app as it is right now? */
110
+ reachable: boolean;
111
+ }
112
+ interface NextAction {
113
+ command: string | null;
114
+ envFile: string | null;
115
+ label: string;
116
+ }
117
+ interface IntegrationState {
118
+ /**
119
+ * Is the app running right now. Deliberately alongside `status` rather than
120
+ * folded into it: a surface must be able to say "setup reached mapping" and
121
+ * "the app is offline" in the same breath without either overwriting the
122
+ * other.
123
+ */
124
+ liveness: ConnectionLiveness;
125
+ next: NextAction | null;
126
+ status: IntegrationStatus;
127
+ steps: IntegrationStep[];
128
+ }
129
+ //#endregion
130
+ export { IntegrationStep as a, StepProblem as c, IntegrationStatus as i, StepState as l, IntegrationFacts as n, NextAction as o, IntegrationState as r, StepId as s, ConnectionLiveness as t };
package/internal.d.cts CHANGED
@@ -6,5 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-RQLGx8ub.cjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-CmEdAMwj.cjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-BG2O1an2.cjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.cjs";
9
10
  import { a as validateProjection, c as draftErrors, d as suggestJoinConditions, f as fanOutJoins, i as primaryKeyColumns, l as draftToProjection, n as coversUniqueKey, o as ProjectionDraft, p as joinFansOut, r as hasRichMeta, s as describeCardinality, t as columnMetaFor, u as emptyProjectionDraft } from "./internal-Da_dlcQ_.cjs";
10
- export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
11
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/internal.d.mts CHANGED
@@ -6,5 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-D9dsonoi.mjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-Bo7pqXW3.mjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-DgQbrJON.mjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.mjs";
9
10
  import { a as validateProjection, c as draftErrors, d as suggestJoinConditions, f as fanOutJoins, i as primaryKeyColumns, l as draftToProjection, n as coversUniqueKey, o as ProjectionDraft, p as joinFansOut, r as hasRichMeta, s as describeCardinality, t as columnMetaFor, u as emptyProjectionDraft } from "./internal-CDY8g9Ic.mjs";
10
- export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
11
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
@@ -1,4 +1,5 @@
1
1
  import { a as ClivlyEntitiesConfig } from "./entity-config-B2RA53nw.mjs";
2
+ import { r as IntegrationState } from "./integration-state-BK-riZnm.mjs";
2
3
  //#region ../sdk/src/drizzle-meta.d.ts
3
4
  declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
4
5
  interface DrizzleSourceMeta {
@@ -301,6 +302,12 @@ interface ClivlyConnectResult {
301
302
  retryable: boolean;
302
303
  /** HTTP status, or null when the request never completed (network error). */
303
304
  status: number | null;
305
+ /**
306
+ * The workspace's setup ladder, computed by the server. Present only on
307
+ * `ping()`, and null when the server is older than this field or could not
308
+ * read it — which the CLI renders as "unavailable", never as "not set up".
309
+ */
310
+ workspace?: IntegrationState | null;
304
311
  }
305
312
  interface SyncCursor {
306
313
  /** The row's id, breaking ties when timestamps collide. */
@@ -1,4 +1,5 @@
1
1
  import { a as ClivlyEntitiesConfig } from "./entity-config-B2RA53nw.cjs";
2
+ import { r as IntegrationState } from "./integration-state-BK-riZnm.cjs";
2
3
  //#region ../sdk/src/drizzle-meta.d.ts
3
4
  declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
4
5
  interface DrizzleSourceMeta {
@@ -301,6 +302,12 @@ interface ClivlyConnectResult {
301
302
  retryable: boolean;
302
303
  /** HTTP status, or null when the request never completed (network error). */
303
304
  status: number | null;
305
+ /**
306
+ * The workspace's setup ladder, computed by the server. Present only on
307
+ * `ping()`, and null when the server is older than this field or could not
308
+ * read it — which the CLI renders as "unavailable", never as "not set up".
309
+ */
310
+ workspace?: IntegrationState | null;
304
311
  }
305
312
  interface SyncCursor {
306
313
  /** The row's id, breaking ties when timestamps collide. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clivly",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Clivly CLI — scaffold a CRM into your app (clivly init).",
@@ -1789,6 +1789,7 @@ function createClivlySDK(config) {
1789
1789
  reason: "ok",
1790
1790
  retryable: false,
1791
1791
  org: body?.org,
1792
+ workspace: body?.workspace ?? null,
1792
1793
  ..."appLastSeenAt" in body ? { appLastSeenAt: body.appLastSeenAt ?? null } : {}
1793
1794
  };
1794
1795
  }
@@ -1789,6 +1789,7 @@ function createClivlySDK(config) {
1789
1789
  reason: "ok",
1790
1790
  retryable: false,
1791
1791
  org: body?.org,
1792
+ workspace: body?.workspace ?? null,
1792
1793
  ..."appLastSeenAt" in body ? { appLastSeenAt: body.appLastSeenAt ?? null } : {}
1793
1794
  };
1794
1795
  }
package/sdk.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_entity_config = require("./entity-config-DtBsl2MQ.cjs");
3
- const require_sdk = require("./sdk-D4Wp_uxb.cjs");
3
+ const require_sdk = require("./sdk-COLUHhJq.cjs");
4
4
  exports.ClivlyAuthError = require_sdk.ClivlyAuthError;
5
5
  exports.ClivlyError = require_sdk.ClivlyError;
6
6
  exports.ClivlyNetworkError = require_sdk.ClivlyNetworkError;
package/sdk.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as defineClivlyConfig, S as customObjectDeclarations, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult } from "./entity-config-B2RA53nw.cjs";
2
- import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-C6aHjedO.cjs";
2
+ import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-EYyxWZnf.cjs";
3
3
  //#region ../sdk/src/chat-session.d.ts
4
4
  declare function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl, fetchImpl, resolveUser }: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
5
5
  //#endregion
package/sdk.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as defineClivlyConfig, S as customObjectDeclarations, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult } from "./entity-config-B2RA53nw.mjs";
2
- import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-Dj-0Q3Hb.mjs";
2
+ import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-BmdGnYMn.mjs";
3
3
  //#region ../sdk/src/chat-session.d.ts
4
4
  declare function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl, fetchImpl, resolveUser }: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
5
5
  //#endregion
package/sdk.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { o as customObjectDeclarations, p as validateEntitiesConfig, s as defineClivlyConfig } from "./entity-config-kUHri9yA.mjs";
2
- import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, t as createClivlySDK } from "./sdk-DXV8M_Io.mjs";
2
+ import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, t as createClivlySDK } from "./sdk-CZNXBsgA.mjs";
3
3
  export { ClivlyAuthError, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, validateEntitiesConfig };
package/vite.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ClivlySDK } from "./namespace-probe-C6aHjedO.cjs";
1
+ import { o as ClivlySDK } from "./namespace-probe-EYyxWZnf.cjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region ../sdk/src/vite.d.ts
4
4
  interface ClivlyViteOptions {
package/vite.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ClivlySDK } from "./namespace-probe-Dj-0Q3Hb.mjs";
1
+ import { o as ClivlySDK } from "./namespace-probe-BmdGnYMn.mjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region ../sdk/src/vite.d.ts
4
4
  interface ClivlyViteOptions {