@abloatai/ablo 0.10.1 → 0.11.1

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 (105) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +63 -23
  3. package/dist/BaseSyncedStore.d.ts +75 -0
  4. package/dist/BaseSyncedStore.js +193 -8
  5. package/dist/Database.d.ts +10 -2
  6. package/dist/Database.js +15 -1
  7. package/dist/SyncClient.d.ts +12 -1
  8. package/dist/SyncClient.js +110 -26
  9. package/dist/agent/Agent.d.ts +9 -9
  10. package/dist/agent/Agent.js +16 -16
  11. package/dist/agent/index.d.ts +1 -1
  12. package/dist/agent/index.js +2 -2
  13. package/dist/agent/types.d.ts +1 -1
  14. package/dist/agent/types.js +1 -1
  15. package/dist/ai-sdk/{intent-broadcast.d.ts → claim-broadcast.d.ts} +10 -10
  16. package/dist/ai-sdk/{intent-broadcast.js → claim-broadcast.js} +6 -6
  17. package/dist/ai-sdk/coordination-context.d.ts +9 -9
  18. package/dist/ai-sdk/coordination-context.js +8 -8
  19. package/dist/ai-sdk/index.d.ts +1 -1
  20. package/dist/ai-sdk/index.js +1 -1
  21. package/dist/ai-sdk/wrap.d.ts +4 -4
  22. package/dist/ai-sdk/wrap.js +4 -4
  23. package/dist/api/index.d.ts +2 -2
  24. package/dist/cli.cjs +369 -67
  25. package/dist/client/Ablo.d.ts +30 -63
  26. package/dist/client/Ablo.js +124 -103
  27. package/dist/client/ApiClient.d.ts +6 -5
  28. package/dist/client/ApiClient.js +86 -62
  29. package/dist/client/auth.d.ts +9 -4
  30. package/dist/client/auth.js +40 -5
  31. package/dist/client/createModelProxy.d.ts +41 -54
  32. package/dist/client/createModelProxy.js +123 -20
  33. package/dist/client/httpClient.d.ts +2 -0
  34. package/dist/client/httpClient.js +1 -1
  35. package/dist/client/index.d.ts +3 -3
  36. package/dist/client/writeOptionsSchema.d.ts +4 -4
  37. package/dist/client/writeOptionsSchema.js +4 -4
  38. package/dist/coordination/schema.d.ts +249 -38
  39. package/dist/coordination/schema.js +172 -39
  40. package/dist/core/index.d.ts +2 -2
  41. package/dist/core/index.js +4 -4
  42. package/dist/errorCodes.d.ts +9 -9
  43. package/dist/errorCodes.js +16 -16
  44. package/dist/errors.d.ts +51 -2
  45. package/dist/errors.js +94 -5
  46. package/dist/interfaces/index.d.ts +8 -4
  47. package/dist/policy/index.d.ts +1 -1
  48. package/dist/policy/types.d.ts +13 -13
  49. package/dist/policy/types.js +8 -8
  50. package/dist/react/AbloProvider.d.ts +51 -4
  51. package/dist/react/AbloProvider.js +95 -11
  52. package/dist/react/context.d.ts +26 -9
  53. package/dist/react/context.js +2 -2
  54. package/dist/react/index.d.ts +4 -4
  55. package/dist/react/index.js +4 -4
  56. package/dist/react/useAblo.js +5 -5
  57. package/dist/react/{useIntent.d.ts → useClaim.d.ts} +9 -9
  58. package/dist/react/useClaim.js +42 -0
  59. package/dist/schema/index.js +1 -1
  60. package/dist/schema/schema.d.ts +3 -3
  61. package/dist/schema/sugar.d.ts +3 -3
  62. package/dist/schema/sugar.js +3 -3
  63. package/dist/schema/sync-delta-wire.d.ts +8 -8
  64. package/dist/server/commit.d.ts +2 -2
  65. package/dist/sync/AreaOfInterestManager.d.ts +162 -0
  66. package/dist/sync/AreaOfInterestManager.js +233 -0
  67. package/dist/sync/BootstrapHelper.d.ts +9 -1
  68. package/dist/sync/BootstrapHelper.js +15 -5
  69. package/dist/sync/NetworkProbe.d.ts +1 -1
  70. package/dist/sync/NetworkProbe.js +1 -1
  71. package/dist/sync/SyncWebSocket.d.ts +59 -25
  72. package/dist/sync/SyncWebSocket.js +123 -26
  73. package/dist/sync/awaitClaimGrant.d.ts +40 -0
  74. package/dist/sync/awaitClaimGrant.js +86 -0
  75. package/dist/sync/createClaimStream.d.ts +34 -0
  76. package/dist/sync/{createIntentStream.js → createClaimStream.js} +92 -81
  77. package/dist/sync/createPresenceStream.js +3 -2
  78. package/dist/sync/participants.d.ts +10 -10
  79. package/dist/sync/participants.js +17 -10
  80. package/dist/sync/schemas.d.ts +8 -8
  81. package/dist/transactions/TransactionQueue.d.ts +23 -0
  82. package/dist/transactions/TransactionQueue.js +186 -12
  83. package/dist/types/global.d.ts +18 -13
  84. package/dist/types/global.js +11 -6
  85. package/dist/types/index.d.ts +9 -7
  86. package/dist/types/index.js +2 -2
  87. package/dist/types/streams.d.ts +114 -98
  88. package/dist/types/streams.js +1 -1
  89. package/dist/utils/asyncIterator.d.ts +1 -1
  90. package/dist/utils/asyncIterator.js +1 -1
  91. package/dist/wire/frames.d.ts +2 -2
  92. package/docs/api.md +3 -3
  93. package/docs/client-behavior.md +6 -3
  94. package/docs/coordination.md +13 -3
  95. package/docs/data-sources.md +29 -9
  96. package/docs/migration.md +40 -0
  97. package/docs/quickstart.md +61 -33
  98. package/docs/react.md +46 -0
  99. package/llms-full.txt +25 -8
  100. package/llms.txt +11 -9
  101. package/package.json +3 -2
  102. package/dist/react/useIntent.js +0 -42
  103. package/dist/sync/awaitIntentGrant.d.ts +0 -40
  104. package/dist/sync/awaitIntentGrant.js +0 -62
  105. package/dist/sync/createIntentStream.d.ts +0 -34
package/dist/cli.cjs CHANGED
@@ -212180,11 +212180,11 @@ var require_commonjs2 = __commonJS({
212180
212180
  if (pad) {
212181
212181
  const need = width - c.length;
212182
212182
  if (need > 0) {
212183
- const z4 = new Array(need + 1).join("0");
212183
+ const z6 = new Array(need + 1).join("0");
212184
212184
  if (i < 0) {
212185
- c = "-" + z4 + c.slice(1);
212185
+ c = "-" + z6 + c.slice(1);
212186
212186
  } else {
212187
- c = z4 + c;
212187
+ c = z6 + c;
212188
212188
  }
212189
212189
  }
212190
212190
  }
@@ -276682,8 +276682,8 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
276682
276682
  if (i) process.stdout.write(`${I2} ${l2}...`);
276683
276683
  else if (t === "timer") process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
276684
276684
  else {
276685
- const z4 = ".".repeat(Math.floor(w2)).slice(0, 3);
276686
- process.stdout.write(`${I2} ${l2}${z4}`);
276685
+ const z6 = ".".repeat(Math.floor(w2)).slice(0, 3);
276686
+ process.stdout.write(`${I2} ${l2}${z6}`);
276687
276687
  }
276688
276688
  h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
276689
276689
  }, r2);
@@ -276710,7 +276710,7 @@ init_cjs_shims();
276710
276710
 
276711
276711
  // src/errors.ts
276712
276712
  init_cjs_shims();
276713
- var import_zod2 = require("zod");
276713
+ var import_zod4 = require("zod");
276714
276714
 
276715
276715
  // src/errorCodes.ts
276716
276716
  init_cjs_shims();
@@ -276786,7 +276786,7 @@ var ERROR_CODES = {
276786
276786
  byo_role_unreadable: wire("permission", 403, false, "The direct Postgres connector role could not be introspected."),
276787
276787
  byo_tenant_tables_unforced_rls: wire("permission", 403, false, "Tenant tables do not have RLS forced under the direct Postgres connector role."),
276788
276788
  byo_host_not_allowed: wire("permission", 403, false, "The direct Postgres connector host resolves to a private, loopback, or link-local address and cannot be used."),
276789
- // ── claim / intent conflict (409) ──────────────────────────────────
276789
+ // ── claim / claim conflict (409) ──────────────────────────────────
276790
276790
  // Held-claim rejections are NOT queue-retryable (gRPC FAILED_PRECONDITION /
276791
276791
  // ABORTED semantics; Replicache/Zero SETTLE a rejected mutation — reject the
276792
276792
  // caller, roll back the optimistic effect — instead of resending it).
@@ -276799,17 +276799,17 @@ var ERROR_CODES = {
276799
276799
  claim_conflict: wire("claim", 409, false, "The target entity is claimed by another participant."),
276800
276800
  claim_lost: wire("claim", 409, false, "A previously held claim was lost before the write applied."),
276801
276801
  entity_claimed: wire("claim", 409, false, "The target entity is currently claimed; write was blocked."),
276802
- intent_conflict: wire("claim", 409, false, "An intent on the target conflicts with an active intent (server-internal alias of claim_conflict)."),
276803
276802
  malformed_claim: wire("claim", 400, false, "The claim payload was malformed."),
276803
+ malformed_subscription: wire("validation", 400, false, "The update_subscription payload was malformed; expected { syncGroups: string[] }."),
276804
276804
  model_claimed: wire("claim", 409, false, "The model instance is claimed by another participant."),
276805
276805
  model_claimed_timeout: wire("claim", 409, false, "Timed out waiting for a model claim to clear."),
276806
- model_claim_not_configured: client("claim", "Claiming was requested on a model that has no claim configuration."),
276806
+ model_claim_not_configured: client("claim", "Claiming requires the collaboration runtime, which the standard Ablo({ schema, apiKey }) client wires up for every model automatically \u2014 there is no per-model claim configuration to add. This appears only when a model proxy is constructed directly without that runtime (an internal/advanced path)."),
276807
276807
  // ── stale context / idempotency (409) ──────────────────────────────
276808
276808
  stale_context: wire("conflict", 409, true, "The write carried a readAt watermark that is now stale; re-read and retry."),
276809
276809
  idempotency_conflict: wire("conflict", 409, false, "The same Idempotency-Key was reused with a different request body."),
276810
276810
  idempotency_key_too_long: wire("validation", 400, false, "The supplied Idempotency-Key exceeds the maximum length."),
276811
276811
  // ── validation (400 / 422) ─────────────────────────────────────────
276812
- write_options_invalid: client("validation", "The write options (`idempotencyKey` / `label` / `wait` / `readAt` / `onStale` / `intent`) failed validation against the write-options schema."),
276812
+ write_options_invalid: client("validation", "The write options (`idempotencyKey` / `label` / `wait` / `readAt` / `onStale` / `claim`) failed validation against the write-options schema."),
276813
276813
  source_operation_id_required: client("validation", "A data-source operation arrived without the entity `id` it targets."),
276814
276814
  source_adapter_misconfigured: client("validation", "The data-source ORM adapter could not map a schema model onto the backing client (missing delegate or model)."),
276815
276815
  source_event_invalid: client("validation", "A data-source outbox event could not be built \u2014 the operation carries no entity id and none was supplied."),
@@ -276867,15 +276867,15 @@ var ERROR_CODES = {
276867
276867
  required_field_added: client("schema", "Migration adds a new required field."),
276868
276868
  enum_value_removed: client("schema", "Migration removes an enum value (destructive classification)."),
276869
276869
  risky_cast: client("schema", "Migration would perform a risky column type cast."),
276870
- // ── intent / lease (409 / transport) ───────────────────────────────
276871
- intent_lease_unavailable: wire("intent", 503, true, "The intent-lease coordination subsystem is unavailable; retry."),
276872
- intent_not_wired: client("intent", "Intent support was used but is not wired in this runtime."),
276873
- intent_queued: wire("intent", 409, true, "The intent was queued behind an active lease holder."),
276874
- intent_wait_aborted: wire("intent", 409, true, "Waiting for the intent lease was aborted."),
276875
- intent_wait_poll_interval_required: client("intent", "A poll interval is required when waiting on an intent."),
276876
- grant_timeout: wire("intent", 504, true, "Timed out waiting for a capability grant."),
276877
- slide_intent_missing_deck_id: wire("intent", 400, false, "A slide intent was missing its deck id."),
276878
- slide_intent_unknown_sibling: wire("intent", 400, false, "A slide intent referenced an unknown sibling slide."),
276870
+ // ── claim / lease (409 / transport) ───────────────────────────────
276871
+ claim_lease_unavailable: wire("claim", 503, true, "The claim-lease coordination subsystem is unavailable; retry."),
276872
+ claim_not_wired: client("claim", "Claim support was used but is not wired in this runtime."),
276873
+ claim_queued: wire("claim", 409, true, "The claim was queued behind an active lease holder."),
276874
+ claim_wait_aborted: wire("claim", 409, true, "Waiting for the claim lease was aborted."),
276875
+ claim_wait_poll_interval_required: client("claim", "A poll interval is required when waiting on an claim."),
276876
+ grant_timeout: wire("claim", 504, true, "Timed out waiting for a capability grant."),
276877
+ slide_intent_missing_deck_id: wire("claim", 400, false, "A slide claim was missing its deck id."),
276878
+ slide_intent_unknown_sibling: wire("claim", 400, false, "A slide claim referenced an unknown sibling slide."),
276879
276879
  // ── bootstrap (transport) ──────────────────────────────────────────
276880
276880
  bootstrap_fetch_timeout: wire("bootstrap", 504, true, "The bootstrap fetch timed out."),
276881
276881
  bootstrap_offline: wire("bootstrap", 503, true, "Bootstrap could not run because the client is offline."),
@@ -276978,7 +276978,7 @@ var ERROR_CODES = {
276978
276978
  upload_items_required: wire("validation", 400, false, "The request must include a non-empty items array."),
276979
276979
  presigned_url_failed: wire("server", 500, true, "Failed to generate a presigned upload URL."),
276980
276980
  task_id_required: wire("validation", 400, false, "A task id is required for this request."),
276981
- intent_id_required: wire("validation", 400, false, "An intent id is required for this request."),
276981
+ claim_id_required: wire("validation", 400, false, "An claim id is required for this request."),
276982
276982
  commit_operation_action_required: wire("validation", 400, false, "A commit operation is missing its `action`."),
276983
276983
  commit_operation_unsupported: wire("validation", 400, false, "A commit operation used an unsupported `action`."),
276984
276984
  usage_invalid: wire("validation", 400, false, "The usage request was invalid."),
@@ -276993,12 +276993,210 @@ var ERROR_CODES = {
276993
276993
  parent_turn_foreign_agent: wire("permission", 403, false, "The parent turn belongs to a different agent."),
276994
276994
  turn_not_found: wire("not_found", 404, false, "The referenced turn does not exist."),
276995
276995
  turn_foreign_agent: wire("permission", 403, false, "The turn belongs to a different agent."),
276996
- invalid_intent: wire("validation", 400, false, "The intent request was invalid."),
276996
+ invalid_intent: wire("validation", 400, false, "The claim request was invalid."),
276997
276997
  schema_too_large: wire("validation", 413, false, "The submitted schema exceeds the maximum size."),
276998
276998
  invalid_schema: wire("validation", 400, false, "The submitted schema could not be parsed."),
276999
276999
  incompatible_change: wire("conflict", 409, false, "The schema change is incompatible with the current schema.")
277000
277000
  };
277001
277001
 
277002
+ // src/coordination/schema.ts
277003
+ init_cjs_shims();
277004
+ var import_zod3 = require("zod");
277005
+
277006
+ // src/schema/roles.ts
277007
+ init_cjs_shims();
277008
+ var import_zod2 = require("zod");
277009
+ var syncGroupSchema = import_zod2.z.templateLiteral([import_zod2.z.string().regex(/^[a-z][a-z0-9_]*$/), ":", import_zod2.z.string().min(1)]).brand();
277010
+ var syncGroupInputSchema = import_zod2.z.union([import_zod2.z.literal("default"), syncGroupSchema]);
277011
+ var roleSourceSchema = import_zod2.z.object({
277012
+ /** The context field to read, e.g. `'organizationId'`, `'id'`, `'deckId'`. */
277013
+ field: import_zod2.z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "source must be a valid identifier"),
277014
+ /**
277015
+ * When `true`, `field` holds an array; every non-empty string element yields
277016
+ * one group. When `false` (default), `field` is a scalar; truthy → one group.
277017
+ */
277018
+ multi: import_zod2.z.boolean()
277019
+ });
277020
+ var roleSchema = import_zod2.z.object({
277021
+ kind: import_zod2.z.string().regex(/^[a-z][a-z0-9_]*$/, 'kind must be a lowercase identifier, e.g. "deck"'),
277022
+ source: roleSourceSchema
277023
+ });
277024
+ var scopeSchema = import_zod2.z.union([
277025
+ import_zod2.z.boolean(),
277026
+ import_zod2.z.string().regex(/^[a-z][a-z0-9_]*$/, 'scope kind must be a lowercase identifier, e.g. "dataroom"')
277027
+ ]);
277028
+ var grantsRefSchema = import_zod2.z.object({
277029
+ subject: import_zod2.z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "grants.subject must name a relation"),
277030
+ scope: import_zod2.z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "grants.scope must name a relation")
277031
+ });
277032
+
277033
+ // src/coordination/schema.ts
277034
+ var targetRangeSchema = import_zod3.z.object({
277035
+ startLine: import_zod3.z.number(),
277036
+ endLine: import_zod3.z.number(),
277037
+ startColumn: import_zod3.z.number().optional(),
277038
+ endColumn: import_zod3.z.number().optional()
277039
+ });
277040
+ var participantKindSchema = import_zod3.z.enum(["user", "agent", "system"]);
277041
+ var wireParticipantKindSchema = import_zod3.z.preprocess(
277042
+ (value) => value === "human" ? "user" : value,
277043
+ participantKindSchema
277044
+ );
277045
+ var targetRefSchema = import_zod3.z.object({
277046
+ entityType: import_zod3.z.string(),
277047
+ entityId: import_zod3.z.string(),
277048
+ path: import_zod3.z.string().optional(),
277049
+ range: targetRangeSchema.optional(),
277050
+ field: import_zod3.z.string().optional(),
277051
+ meta: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).optional()
277052
+ });
277053
+ var onStaleModeSchema = import_zod3.z.enum(["reject", "force", "flag", "merge"]);
277054
+ var writeGuardSchema = import_zod3.z.object({
277055
+ readAt: import_zod3.z.number().nullish(),
277056
+ onStale: onStaleModeSchema.nullish(),
277057
+ bypass: import_zod3.z.boolean().optional()
277058
+ });
277059
+ var claimStatusSchema = import_zod3.z.enum([
277060
+ "active",
277061
+ "committed",
277062
+ "expired",
277063
+ "canceled"
277064
+ ]);
277065
+ var wireClaimBaseSchema = targetRefSchema.extend({
277066
+ claimId: import_zod3.z.string(),
277067
+ /** Verb the agent expects: 'update' | 'create' | 'editing' | 'reviewing' … */
277068
+ action: import_zod3.z.string(),
277069
+ /** Server-stamped declaration time (epoch ms). */
277070
+ declaredAt: import_zod3.z.number(),
277071
+ /** Server-computed TTL deadline (epoch ms). Readers treat as advisory. */
277072
+ expiresAt: import_zod3.z.number(),
277073
+ status: claimStatusSchema.optional()
277074
+ });
277075
+ var wireClaimSummarySchema = wireClaimBaseSchema.pick({
277076
+ claimId: true,
277077
+ action: true,
277078
+ declaredAt: true,
277079
+ expiresAt: true,
277080
+ entityType: true,
277081
+ entityId: true,
277082
+ field: true,
277083
+ meta: true
277084
+ });
277085
+ var claimErrorSchema = import_zod3.z.object({
277086
+ code: import_zod3.z.string(),
277087
+ message: import_zod3.z.string().optional(),
277088
+ /** Participant already holding the target (conflict rejections). */
277089
+ heldBy: import_zod3.z.string().optional(),
277090
+ heldByClaimId: import_zod3.z.string().optional(),
277091
+ heldByExpiresAt: import_zod3.z.number().optional(),
277092
+ /** Rich holder context for conflict rejections. Additive: older frames omit it. */
277093
+ heldByClaim: wireClaimSummarySchema.optional(),
277094
+ /** Optional conflict-policy explanation. Additive: older frames omit it. */
277095
+ policyReason: import_zod3.z.string().optional()
277096
+ });
277097
+ var wireClaimSchema = wireClaimBaseSchema.extend({
277098
+ error: claimErrorSchema.optional()
277099
+ });
277100
+ var claimRejectionSchema = import_zod3.z.object({
277101
+ claimId: import_zod3.z.string(),
277102
+ reason: import_zod3.z.string(),
277103
+ target: targetRefSchema.optional(),
277104
+ heldBy: import_zod3.z.string().optional(),
277105
+ heldByClaimId: import_zod3.z.string().optional(),
277106
+ heldByExpiresAt: import_zod3.z.number().optional(),
277107
+ heldByClaim: wireClaimSummarySchema.optional(),
277108
+ policyReason: import_zod3.z.string().optional()
277109
+ });
277110
+ var modelTargetSchema = import_zod3.z.object({
277111
+ model: import_zod3.z.string(),
277112
+ id: import_zod3.z.string(),
277113
+ path: import_zod3.z.string().optional(),
277114
+ range: targetRangeSchema.optional(),
277115
+ field: import_zod3.z.string().optional(),
277116
+ meta: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).optional()
277117
+ }).readonly();
277118
+ var modelClaimSchema = import_zod3.z.object({
277119
+ id: import_zod3.z.string(),
277120
+ actor: import_zod3.z.string(),
277121
+ participantKind: wireParticipantKindSchema,
277122
+ action: import_zod3.z.string(),
277123
+ description: import_zod3.z.string().optional(),
277124
+ field: import_zod3.z.string().optional(),
277125
+ status: import_zod3.z.enum(["active", "queued"]).optional(),
277126
+ position: import_zod3.z.number().optional(),
277127
+ expiresAt: import_zod3.z.number(),
277128
+ target: modelTargetSchema
277129
+ }).readonly();
277130
+ var claimBeginPayloadSchema = targetRefSchema.extend({
277131
+ claimId: import_zod3.z.string(),
277132
+ action: import_zod3.z.string(),
277133
+ /** Hint for `expiresAt`; the server caps it. */
277134
+ estimatedMs: import_zod3.z.number().optional(),
277135
+ /**
277136
+ * Opt into the fair wait queue: when the target is already held, the server
277137
+ * enqueues this claim (FIFO) and replies `claim_queued` → later
277138
+ * `claim_granted`, instead of `claim_rejected`. Clients that set this MUST
277139
+ * handle the grant.
277140
+ */
277141
+ queue: import_zod3.z.boolean().optional()
277142
+ });
277143
+ var claimAbandonPayloadSchema = import_zod3.z.object({
277144
+ claimId: import_zod3.z.string(),
277145
+ entityType: import_zod3.z.string().optional(),
277146
+ entityId: import_zod3.z.string().optional()
277147
+ });
277148
+ var claimReorderPayloadSchema = import_zod3.z.object({
277149
+ entityType: import_zod3.z.string(),
277150
+ entityId: import_zod3.z.string(),
277151
+ order: import_zod3.z.array(import_zod3.z.object({ heldBy: import_zod3.z.string(), claimId: import_zod3.z.string() }))
277152
+ });
277153
+ var updateSubscriptionPayloadSchema = import_zod3.z.object({
277154
+ syncGroups: import_zod3.z.array(syncGroupInputSchema)
277155
+ });
277156
+ var subscriptionAckPayloadSchema = import_zod3.z.object({
277157
+ success: import_zod3.z.boolean(),
277158
+ syncGroups: import_zod3.z.array(import_zod3.z.string()),
277159
+ error: import_zod3.z.object({ code: import_zod3.z.string(), message: import_zod3.z.string() }).optional()
277160
+ });
277161
+ var commitOperationTypeSchema = import_zod3.z.enum([
277162
+ "CREATE",
277163
+ "UPDATE",
277164
+ "DELETE",
277165
+ "ARCHIVE",
277166
+ "UNARCHIVE"
277167
+ ]);
277168
+ var commitOperationSchema = writeGuardSchema.extend({
277169
+ type: commitOperationTypeSchema,
277170
+ model: import_zod3.z.string(),
277171
+ id: import_zod3.z.string().nullish(),
277172
+ input: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).nullish(),
277173
+ /** Per-op client tx id, echoed on the broadcast delta. */
277174
+ transactionId: import_zod3.z.string().nullish()
277175
+ });
277176
+ var presenceKindSchema = import_zod3.z.enum(["enter", "update", "leave"]);
277177
+ var presenceActivitySchema = targetRefSchema.extend({
277178
+ action: import_zod3.z.string(),
277179
+ detail: import_zod3.z.string().optional()
277180
+ });
277181
+ var presenceUpdateFrameSchema = import_zod3.z.object({
277182
+ kind: presenceKindSchema,
277183
+ userId: import_zod3.z.string().optional(),
277184
+ syncGroups: import_zod3.z.array(import_zod3.z.string()).optional(),
277185
+ timestamp: import_zod3.z.number().optional(),
277186
+ status: import_zod3.z.string(),
277187
+ timezone: import_zod3.z.string().optional(),
277188
+ customStatus: import_zod3.z.string().optional(),
277189
+ activity: presenceActivitySchema.optional(),
277190
+ isAgent: import_zod3.z.boolean().optional(),
277191
+ /**
277192
+ * Server-stamped canonical kind. Additive — older servers omit it and
277193
+ * readers fall back to `isAgent` (see {@link participantKindFromWire}).
277194
+ */
277195
+ participantKind: wireParticipantKindSchema.optional(),
277196
+ activeClaims: import_zod3.z.array(wireClaimSchema).optional(),
277197
+ delegatedFrom: import_zod3.z.string().nullish()
277198
+ });
277199
+
277002
277200
  // src/errors.ts
277003
277201
  var AbloError = class extends Error {
277004
277202
  /** Discriminator string — matches the class name. Lets consumers
@@ -277068,35 +277266,43 @@ function docUrlForCode(code) {
277068
277266
  var AbloValidationError = class extends AbloError {
277069
277267
  type = "AbloValidationError";
277070
277268
  };
277071
- var OptionalWireStringSchema = import_zod2.z.preprocess(
277269
+ var OptionalWireStringSchema = import_zod4.z.preprocess(
277072
277270
  (value) => typeof value === "string" ? value : void 0,
277073
- import_zod2.z.string().optional()
277271
+ import_zod4.z.string().optional()
277074
277272
  );
277075
- var RequiredCapabilityWireSchema = import_zod2.z.object({
277076
- scope: import_zod2.z.string(),
277077
- constraints: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.union([import_zod2.z.array(import_zod2.z.string()), import_zod2.z.string()])).optional(),
277273
+ var RequiredCapabilityWireSchema = import_zod4.z.object({
277274
+ scope: import_zod4.z.string(),
277275
+ constraints: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.union([import_zod4.z.array(import_zod4.z.string()), import_zod4.z.string()])).optional(),
277078
277276
  issuer: OptionalWireStringSchema,
277079
- ttlSeconds: import_zod2.z.preprocess((value) => typeof value === "number" ? value : void 0, import_zod2.z.number().optional()),
277277
+ ttlSeconds: import_zod4.z.preprocess((value) => typeof value === "number" ? value : void 0, import_zod4.z.number().optional()),
277080
277278
  nonce: OptionalWireStringSchema
277081
277279
  }).passthrough();
277082
- var NestedErrorShapeSchema = import_zod2.z.object({
277280
+ var NestedErrorShapeSchema = import_zod4.z.object({
277083
277281
  code: OptionalWireStringSchema,
277084
277282
  message: OptionalWireStringSchema,
277085
277283
  field: OptionalWireStringSchema,
277086
- requiredCapability: RequiredCapabilityWireSchema.optional().catch(void 0)
277284
+ requiredCapability: RequiredCapabilityWireSchema.optional().catch(void 0),
277285
+ heldBy: OptionalWireStringSchema,
277286
+ policyReason: OptionalWireStringSchema,
277287
+ heldByClaim: wireClaimSummarySchema.optional().catch(void 0),
277288
+ claims: import_zod4.z.array(wireClaimSummarySchema).optional().catch(void 0)
277087
277289
  }).passthrough();
277088
- var ErrorFieldSchema = import_zod2.z.preprocess(
277290
+ var ErrorFieldSchema = import_zod4.z.preprocess(
277089
277291
  (value) => typeof value === "string" || typeof value === "object" && value !== null ? value : void 0,
277090
- import_zod2.z.union([import_zod2.z.string(), NestedErrorShapeSchema]).optional()
277292
+ import_zod4.z.union([import_zod4.z.string(), NestedErrorShapeSchema]).optional()
277091
277293
  ).catch(void 0);
277092
- var ErrorBodyShapeSchema = import_zod2.z.object({
277294
+ var ErrorBodyShapeSchema = import_zod4.z.object({
277093
277295
  /** Legacy: `error` was a flat code string on older endpoints. Newer
277094
277296
  * endpoints (CommitReceipt) carry `error` as a nested object. */
277095
277297
  error: ErrorFieldSchema,
277096
277298
  code: OptionalWireStringSchema,
277097
277299
  reason: OptionalWireStringSchema,
277098
277300
  message: OptionalWireStringSchema,
277099
- requiredCapability: RequiredCapabilityWireSchema.optional().catch(void 0)
277301
+ requiredCapability: RequiredCapabilityWireSchema.optional().catch(void 0),
277302
+ heldBy: OptionalWireStringSchema,
277303
+ policyReason: OptionalWireStringSchema,
277304
+ heldByClaim: wireClaimSummarySchema.optional().catch(void 0),
277305
+ claims: import_zod4.z.array(wireClaimSummarySchema).optional().catch(void 0)
277100
277306
  }).passthrough();
277101
277307
 
277102
277308
  // src/cli/migrate.ts
@@ -279404,7 +279610,7 @@ function readProjectDatabaseUrl(cwd = process.cwd()) {
279404
279610
  }
279405
279611
 
279406
279612
  // src/cli/migrate.ts
279407
- var import_schema2 = require("@abloatai/ablo/schema");
279613
+ var import_schema3 = require("@abloatai/ablo/schema");
279408
279614
  var import_source = require("@abloatai/ablo/source");
279409
279615
 
279410
279616
  // src/cli/push.ts
@@ -279412,7 +279618,7 @@ init_cjs_shims();
279412
279618
  var import_picocolors3 = __toESM(require_picocolors(), 1);
279413
279619
  var import_fs4 = require("fs");
279414
279620
  var import_path3 = require("path");
279415
- var import_schema = require("@abloatai/ablo/schema");
279621
+ var import_schema2 = require("@abloatai/ablo/schema");
279416
279622
 
279417
279623
  // src/cli/config.ts
279418
279624
  init_cjs_shims();
@@ -279577,7 +279783,7 @@ function fmtSignal(s) {
279577
279783
  return ` \u2022 ${import_picocolors3.default.bold(where ?? "?")} \u2014 ${sig.detail ?? ""}`;
279578
279784
  }
279579
279785
  async function pushSchema(schema, args) {
279580
- const schemaJson = JSON.parse((0, import_schema.serializeSchema)(schema));
279786
+ const schemaJson = JSON.parse((0, import_schema2.serializeSchema)(schema));
279581
279787
  const res = await fetch(`${args.url}/api/schema`, {
279582
279788
  method: "POST",
279583
279789
  headers: {
@@ -279690,7 +279896,7 @@ async function push(argv) {
279690
279896
  process.exit(1);
279691
279897
  }
279692
279898
  const schema = await loadSchema(args.schemaPath, args.exportName);
279693
- const hash = (0, import_schema.schemaHash)(schema);
279899
+ const hash = (0, import_schema2.schemaHash)(schema);
279694
279900
  console.log(
279695
279901
  ` Pushing ${import_picocolors3.default.bold(args.schemaPath)} ${import_picocolors3.default.dim(`(${Object.keys(schema.models).length} models, hash ${hash})`)} \u2192 ${import_picocolors3.default.dim(args.url)}`
279696
279902
  );
@@ -279736,6 +279942,14 @@ async function push(argv) {
279736
279942
  }
279737
279943
 
279738
279944
  // src/cli/migrate.ts
279945
+ var MIGRATE_USAGE = ` ablo migrate \u2014 provision your schema's tables in your own Postgres (DATABASE_URL)
279946
+
279947
+ Usage:
279948
+ npx ablo migrate Create the synced-model tables (with row-level security)
279949
+ npx ablo migrate --dry-run Print the SQL without executing it
279950
+ npx ablo migrate --output schema.sql Write the SQL to a file instead of applying
279951
+ npx ablo migrate --schema <path> Use a schema file other than ablo/schema.ts
279952
+ npx ablo migrate --export <name> Use a named export other than \`schema\``;
279739
279953
  var DEFAULT_SCHEMA_PATH2 = "ablo/schema.ts";
279740
279954
  var DEFAULT_EXPORT2 = "schema";
279741
279955
  function parseMigrateArgs(argv) {
@@ -279769,8 +279983,8 @@ function parseMigrateArgs(argv) {
279769
279983
  return { schemaPath, exportName, targetSchema, dryRun, outputFile };
279770
279984
  }
279771
279985
  function planFor(schema, targetSchema = "public") {
279772
- const schemaJson = JSON.parse((0, import_schema2.serializeSchema)(schema));
279773
- const plan = (0, import_schema2.generateProvisionPlan)(schemaJson, targetSchema, {
279986
+ const schemaJson = JSON.parse((0, import_schema3.serializeSchema)(schema));
279987
+ const plan = (0, import_schema3.generateProvisionPlan)(schemaJson, targetSchema, {
279774
279988
  foreignKeys: targetSchema === "public"
279775
279989
  });
279776
279990
  const adapterTables = (0, import_source.adapterTableMigrations)().map((m2) => m2.up);
@@ -279895,7 +280109,7 @@ init_cjs_shims();
279895
280109
  var import_fs6 = require("fs");
279896
280110
  var import_path4 = require("path");
279897
280111
  var import_picocolors5 = __toESM(require_picocolors(), 1);
279898
- var import_schema3 = require("@abloatai/ablo/schema");
280112
+ var import_schema4 = require("@abloatai/ablo/schema");
279899
280113
  var DEFAULT_SCHEMA_PATH3 = "ablo/schema.ts";
279900
280114
  var DEFAULT_EXPORT3 = "schema";
279901
280115
  var DEFAULT_OUT = "ablo/generated.ts";
@@ -279932,8 +280146,8 @@ async function generate(argv) {
279932
280146
  let source;
279933
280147
  try {
279934
280148
  const schema = await loadSchema(args.schemaPath, args.exportName);
279935
- const schemaJson = JSON.parse((0, import_schema3.serializeSchema)(schema));
279936
- source = (0, import_schema3.generateTypes)(schemaJson);
280149
+ const schemaJson = JSON.parse((0, import_schema4.serializeSchema)(schema));
280150
+ source = (0, import_schema4.generateTypes)(schemaJson);
279937
280151
  } catch (err) {
279938
280152
  console.error(import_picocolors5.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279939
280153
  process.exit(1);
@@ -279949,7 +280163,7 @@ init_cjs_shims();
279949
280163
  var import_picocolors6 = __toESM(require_picocolors(), 1);
279950
280164
  var import_fs7 = require("fs");
279951
280165
  var import_path5 = require("path");
279952
- var import_schema4 = require("@abloatai/ablo/schema");
280166
+ var import_schema5 = require("@abloatai/ablo/schema");
279953
280167
 
279954
280168
  // src/cli/theme.ts
279955
280169
  init_cjs_shims();
@@ -280115,7 +280329,7 @@ async function dev(argv) {
280115
280329
  const schema = await loadSchema(args.schemaPath, args.exportName);
280116
280330
  const modelCount = Object.keys(schema.models).length;
280117
280331
  console.log(
280118
- ` ${import_picocolors6.default.dim("schema")} ${import_picocolors6.default.bold(args.schemaPath)} ${import_picocolors6.default.dim(`(${modelCount} models, hash ${(0, import_schema4.schemaHash)(schema)})`)}`
280332
+ ` ${import_picocolors6.default.dim("schema")} ${import_picocolors6.default.bold(args.schemaPath)} ${import_picocolors6.default.dim(`(${modelCount} models, hash ${(0, import_schema5.schemaHash)(schema)})`)}`
280119
280333
  );
280120
280334
  console.log(` ${import_picocolors6.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
280121
280335
  console.log(` ${import_picocolors6.default.dim("api")} ${args.url}
@@ -280272,7 +280486,10 @@ ${import_picocolors7.default.dim(url)}`, "Approve in your browser");
280272
280486
  s.message("Provisioning a sandbox key\u2026");
280273
280487
  const provRes = await fetch(`${AUTH_URL}/api/cli/provision-key`, {
280274
280488
  method: "POST",
280275
- headers: { authorization: `Bearer ${accessToken}` }
280489
+ headers: { authorization: `Bearer ${accessToken}`, "content-type": "application/json" },
280490
+ // Pass the device_code so the server can scope the minted keys to the
280491
+ // project the user picked at /cli (login project picker). Harmless if none.
280492
+ body: JSON.stringify({ device_code: code.device_code })
280276
280493
  }).catch(() => null);
280277
280494
  if (!provRes || !provRes.ok) {
280278
280495
  s.stop("Could not provision a key.");
@@ -280500,6 +280717,39 @@ async function projects(argv) {
280500
280717
  );
280501
280718
  return;
280502
280719
  }
280720
+ if (sub === "rename") {
280721
+ const ref = argv[1];
280722
+ const name = argv.slice(2).join(" ").trim();
280723
+ if (!ref || ref.startsWith("-") || !name) {
280724
+ console.error(import_picocolors9.default.red(" usage: ablo projects rename <slug|id> <new name>"));
280725
+ process.exit(1);
280726
+ }
280727
+ const all = await fetchProjects();
280728
+ const target = all.find((p2) => p2.slug === ref || p2.id === ref);
280729
+ if (!target) {
280730
+ console.error(import_picocolors9.default.red(` No project "${ref}".`) + import_picocolors9.default.dim(" Run ablo projects list."));
280731
+ process.exit(1);
280732
+ }
280733
+ if (target.default) {
280734
+ console.error(import_picocolors9.default.red(" The default project cannot be renamed."));
280735
+ process.exit(1);
280736
+ }
280737
+ const { status: status2, body } = await request(`/api/v1/projects/${target.id}`, requireKey(), {
280738
+ method: "PATCH",
280739
+ body: { name }
280740
+ });
280741
+ if (status2 !== 200) {
280742
+ console.error(
280743
+ import_picocolors9.default.red(` Rename failed (${status2}): ${String(body.message ?? body.code ?? "")}`)
280744
+ );
280745
+ process.exit(1);
280746
+ }
280747
+ const updated = body;
280748
+ console.log(
280749
+ ` ${import_picocolors9.default.green("\u2713")} Renamed ${import_picocolors9.default.bold(updated.slug)} \u2192 ${import_picocolors9.default.bold(updated.name ?? updated.slug)} ${import_picocolors9.default.dim(`(${updated.id})`)}`
280750
+ );
280751
+ return;
280752
+ }
280503
280753
  if (sub === "use") {
280504
280754
  const ref = argv[1];
280505
280755
  if (!ref) {
@@ -280527,7 +280777,9 @@ async function projects(argv) {
280527
280777
  return;
280528
280778
  }
280529
280779
  console.error(
280530
- import_picocolors9.default.red(` unknown subcommand: ${sub}`) + import_picocolors9.default.dim(` (expected ${import_picocolors9.default.bold("list")}, ${import_picocolors9.default.bold("create")}, or ${import_picocolors9.default.bold("use")})`)
280780
+ import_picocolors9.default.red(` unknown subcommand: ${sub}`) + import_picocolors9.default.dim(
280781
+ ` (expected ${import_picocolors9.default.bold("list")}, ${import_picocolors9.default.bold("create")}, ${import_picocolors9.default.bold("rename")}, or ${import_picocolors9.default.bold("use")})`
280782
+ )
280531
280783
  );
280532
280784
  process.exit(1);
280533
280785
  }
@@ -280937,7 +281189,7 @@ async function webhooks(argv) {
280937
281189
  // src/cli/check.ts
280938
281190
  init_cjs_shims();
280939
281191
  var import_picocolors13 = __toESM(require_picocolors(), 1);
280940
- var import_schema5 = require("@abloatai/ablo/schema");
281192
+ var import_schema6 = require("@abloatai/ablo/schema");
280941
281193
  var DEFAULT_SCHEMA_PATH4 = "ablo/schema.ts";
280942
281194
  var DEFAULT_EXPORT4 = "schema";
280943
281195
  var BASE_COLUMNS = /* @__PURE__ */ new Set(["id", "organization_id", "created_by", "created_at", "updated_at"]);
@@ -280982,7 +281234,7 @@ async function check(argv) {
280982
281234
  process.exit(1);
280983
281235
  }
280984
281236
  const schema = await loadSchema(args.schemaPath, args.exportName);
280985
- const schemaJson = JSON.parse((0, import_schema5.serializeSchema)(schema));
281237
+ const schemaJson = JSON.parse((0, import_schema6.serializeSchema)(schema));
280986
281238
  const sql = src_default(dbUrl, { max: 1, prepare: false, onnotice: () => {
280987
281239
  } });
280988
281240
  let rows;
@@ -281024,7 +281276,7 @@ async function check(argv) {
281024
281276
  const problems = [];
281025
281277
  const warns = [];
281026
281278
  if (!present.has("id")) problems.push('missing primary key "id"');
281027
- const orgCol = (0, import_schema5.tenancyColumn)((0, import_schema5.resolveTenancy)(model));
281279
+ const orgCol = (0, import_schema6.tenancyColumn)((0, import_schema6.resolveTenancy)(model));
281028
281280
  if (orgCol && !present.has(orgCol)) {
281029
281281
  problems.push(
281030
281282
  `missing "${orgCol}" \u2014 Ablo isolates tenants (RLS) and routes realtime by it, so a table without it has no safe boundary. Add the column, or use a Data Source endpoint.`
@@ -281202,8 +281454,8 @@ async function upgrade(argv) {
281202
281454
  if (t === "drizzleDataSource" && call.getArguments().length === 2 && !import_ts_morph.Node.isIdentifier(call.getArguments()[1])) {
281203
281455
  flag2(call, "drizzleDataSource(db, tables)", "now `drizzleDataSource(db, schema)` \u2014 pass your Ablo schema, drop the tables map.");
281204
281456
  }
281205
- if (/\.intents\b/.test(t) && /^(ablo|sync)\b/.test(t)) {
281206
- flag2(call, "ablo.intents.*", "use `ablo.<model>.claim` (claim.state / claim.queue / `await using claim = await \u2026claim({ id })`).");
281457
+ if (/\.claims\b/.test(t) && /^(ablo|sync)\b/.test(t)) {
281458
+ flag2(call, "ablo.claims.*", "use `ablo.<model>.claim` (claim.state / claim.queue / `await using claim = await \u2026claim({ id })`).");
281207
281459
  }
281208
281460
  }
281209
281461
  for (const jsx of sf.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxOpeningElement)) {
@@ -281924,8 +282176,18 @@ async function drizzlePull(argv) {
281924
282176
  var LOGO = `
281925
282177
  ${brand("ablo")} ${import_picocolors18.default.dim("sync engine")}
281926
282178
  `;
282179
+ var SUBCOMMAND_USAGE = {
282180
+ migrate: MIGRATE_USAGE
282181
+ };
281927
282182
  async function main() {
281928
- const command = process.argv[2];
282183
+ let command = process.argv[2];
282184
+ if (command && process.argv.slice(3).some((a) => a === "--help" || a === "-h")) {
282185
+ if (SUBCOMMAND_USAGE[command]) {
282186
+ console.log(SUBCOMMAND_USAGE[command]);
282187
+ return;
282188
+ }
282189
+ command = void 0;
282190
+ }
281929
282191
  if (command === "init") {
281930
282192
  await init(process.argv.slice(3));
281931
282193
  } else if (command === "login") {
@@ -281943,8 +282205,14 @@ async function main() {
281943
282205
  } else if (command === "webhooks") {
281944
282206
  await webhooks(process.argv.slice(3));
281945
282207
  } else if (command === "dev") {
281946
- console.log(import_picocolors18.default.dim(" `ablo dev` is now `ablo push --watch` \u2014 running that."));
281947
- await dev([...process.argv.slice(3), "--watch"]);
282208
+ const devArgs = process.argv.slice(3);
282209
+ const oneShot = devArgs.includes("--no-watch");
282210
+ console.log(
282211
+ import_picocolors18.default.dim(
282212
+ oneShot ? " `ablo dev --no-watch` is `ablo push` (push once, no watcher) \u2014 running that." : " `ablo dev` is now `ablo push --watch` \u2014 running that."
282213
+ )
282214
+ );
282215
+ await dev(oneShot ? devArgs : [...devArgs, "--watch"]);
281948
282216
  } else if (command === "check") {
281949
282217
  await check(process.argv.slice(3));
281950
282218
  } else if (command === "pull") {
@@ -282002,6 +282270,8 @@ async function main() {
282002
282270
  console.log(` npx ablo pull prisma [path] Generate schema.ts from a Prisma schema (keeps enums + relations)`);
282003
282271
  console.log(` npx ablo pull drizzle <module> Generate schema.ts from a Drizzle schema (keeps enums + relations)`);
282004
282272
  console.log(` npx ablo check Check your existing database fits the schema (read-only, creates no tables)`);
282273
+ console.log(` npx ablo migrate Provision your synced-model tables in your own Postgres (DATABASE_URL)`);
282274
+ console.log(` npx ablo migrate --dry-run Print the SQL without executing (preview)`);
282005
282275
  console.log(` npx ablo push Upload your schema definition to Ablo (metadata only \u2014 rows stay in your DB)`);
282006
282276
  console.log(` npx ablo push --force Allow destructive/unexecutable changes`);
282007
282277
  console.log(` npx ablo push --rename a:b Treat model "a" as renamed to "b"`);
@@ -282079,6 +282349,10 @@ function detectOrm(override) {
282079
282349
  }
282080
282350
  return "none";
282081
282351
  }
282352
+ function detectNextLayout() {
282353
+ const useSrc = (0, import_fs12.existsSync)((0, import_path7.join)("src", "app")) || !(0, import_fs12.existsSync)("app") && (0, import_fs12.existsSync)("src");
282354
+ return useSrc ? { appBase: (0, import_path7.join)("src", "app"), aliasBase: "src" } : { appBase: "app", aliasBase: "." };
282355
+ }
282082
282356
  async function chooseOption(name, flagValue, fallback, allowed, interactive, prompt) {
282083
282357
  if (flagValue !== void 0) {
282084
282358
  if (!allowed.includes(flagValue)) {
@@ -282178,7 +282452,8 @@ async function init(args = []) {
282178
282452
  "Non-interactive (no TTY / --yes)"
282179
282453
  );
282180
282454
  }
282181
- const abloDir = "ablo";
282455
+ const layout = framework === "nextjs" ? detectNextLayout() : { appBase: "app", aliasBase: "." };
282456
+ const abloDir = (0, import_path7.join)(layout.aliasBase, "ablo");
282182
282457
  (0, import_fs12.mkdirSync)(abloDir, { recursive: true });
282183
282458
  const created = [];
282184
282459
  let schemaSource = generateSchema();
@@ -282209,41 +282484,51 @@ async function init(args = []) {
282209
282484
  created.push(`${abloDir}/schema.ts${schemaNote}`);
282210
282485
  (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "index.ts"), generateSyncConfig(auth, storage));
282211
282486
  created.push(`${abloDir}/index.ts`);
282487
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "register.ts"), generateRegister());
282488
+ created.push(`${abloDir}/register.ts`);
282212
282489
  const orm = detectOrm(opts.orm);
282213
282490
  if (storage === "endpoint") {
282214
282491
  (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "data-source.ts"), generateDataSource(orm));
282215
282492
  created.push(`${abloDir}/data-source.ts${orm === "drizzle" ? " (Drizzle)" : " (Prisma)"}`);
282216
282493
  }
282217
282494
  const envFile = framework === "nextjs" ? ".env.local" : ".env";
282495
+ const resolvedKey = process.env.ABLO_API_KEY ? void 0 : resolveApiKey("sandbox");
282496
+ const wireRealKey = envFile === ".env.local" && Boolean(resolvedKey);
282497
+ const envBody = generateEnv(storage, { includeApiKey: !wireRealKey });
282218
282498
  if (!(0, import_fs12.existsSync)(envFile)) {
282219
- (0, import_fs12.writeFileSync)(envFile, generateEnv(storage));
282499
+ (0, import_fs12.writeFileSync)(envFile, envBody);
282220
282500
  created.push(envFile);
282221
282501
  } else {
282222
282502
  const existing = (0, import_fs12.readFileSync)(envFile, "utf-8");
282223
282503
  if (!existing.includes("ABLO_")) {
282224
- (0, import_fs12.writeFileSync)(envFile, existing + "\n" + generateEnv(storage));
282504
+ (0, import_fs12.writeFileSync)(envFile, existing + "\n" + envBody);
282225
282505
  created.push(`${envFile} ${import_picocolors18.default.dim("(appended)")}`);
282226
282506
  } else {
282227
282507
  created.push(`${envFile} ${import_picocolors18.default.dim("(already configured)")}`);
282228
282508
  }
282229
282509
  }
282510
+ if (wireRealKey && resolvedKey) {
282511
+ wireEnvLocal(resolvedKey);
282512
+ created.push(`.env.local ${import_picocolors18.default.dim("(ABLO_API_KEY set from your login)")}`);
282513
+ }
282230
282514
  if (agent) {
282231
282515
  (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
282232
282516
  created.push(`${abloDir}/agent.ts`);
282233
282517
  }
282234
282518
  if (framework === "nextjs") {
282235
282519
  if (storage === "endpoint") {
282236
- const webhookDir = (0, import_path7.join)("app", "api", "ablo", "webhooks");
282520
+ const webhookDir = (0, import_path7.join)(layout.appBase, "api", "ablo", "webhooks");
282237
282521
  (0, import_fs12.mkdirSync)(webhookDir, { recursive: true });
282238
282522
  (0, import_fs12.writeFileSync)((0, import_path7.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
282239
282523
  created.push(`${webhookDir}/route.ts${orm === "prisma" ? " (Prisma mirror)" : " (add your database write)"}`);
282240
282524
  }
282241
- (0, import_fs12.writeFileSync)((0, import_path7.join)("app", "providers.tsx"), generateProviders());
282242
- created.push(`app/providers.tsx ${import_picocolors18.default.dim("(wrap app/layout.tsx in <Providers>)")}`);
282243
- const sessionDir = (0, import_path7.join)("app", "api", "ablo-session");
282525
+ const providersPath = (0, import_path7.join)(layout.appBase, "providers.tsx");
282526
+ (0, import_fs12.writeFileSync)(providersPath, generateProviders());
282527
+ created.push(`${providersPath} ${import_picocolors18.default.dim(`(wrap ${(0, import_path7.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
282528
+ const sessionDir = (0, import_path7.join)(layout.appBase, "api", "ablo-session");
282244
282529
  (0, import_fs12.mkdirSync)(sessionDir, { recursive: true });
282245
282530
  (0, import_fs12.writeFileSync)((0, import_path7.join)(sessionDir, "route.ts"), generateSessionRoute());
282246
- created.push(`app/api/ablo-session/route.ts ${import_picocolors18.default.dim("(wire your auth)")}`);
282531
+ created.push(`${(0, import_path7.join)(sessionDir, "route.ts")} ${import_picocolors18.default.dim("(wire your auth)")}`);
282247
282532
  }
282248
282533
  if (framework !== "vanilla") {
282249
282534
  (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
@@ -282272,7 +282557,7 @@ async function init(args = []) {
282272
282557
  `Provision your DB: ${import_picocolors18.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors18.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors18.default.bold("/api/ablo/source")}`
282273
282558
  ],
282274
282559
  ...framework === "nextjs" ? [
282275
- `Wrap ${import_picocolors18.default.bold("app/layout.tsx")} in ${import_picocolors18.default.bold("<Providers>")} (app/providers.tsx) and add your auth to ${import_picocolors18.default.bold("app/api/ablo-session/route.ts")}`
282560
+ `Wrap ${import_picocolors18.default.bold((0, import_path7.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors18.default.bold("<Providers>")} (${(0, import_path7.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors18.default.bold((0, import_path7.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
282276
282561
  ] : [],
282277
282562
  `Run ${import_picocolors18.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
282278
282563
  ...agent ? [
@@ -282351,14 +282636,31 @@ export const sync = Ablo({
282351
282636
  apiKey: process.env.ABLO_API_KEY,${databaseLine}${authLine}
282352
282637
  schema,
282353
282638
  });
282639
+
282640
+ // Name the client's type off the constructed value \u2014 the overload resolves at
282641
+ // this call site, so this carries the full typed surface. (Like tRPC's
282642
+ // \`typeof appRouter\`, Drizzle's \`typeof db\`.) Prefer this over \`ReturnType<typeof Ablo>\`.
282643
+ export type Sync = typeof sync;
282644
+ `;
282645
+ }
282646
+ function generateRegister() {
282647
+ return `import type { schema } from './schema';
282648
+
282649
+ declare module '@abloatai/ablo' {
282650
+ interface Register {
282651
+ Schema: typeof schema;
282652
+ }
282653
+ }
282654
+
282655
+ export {};
282354
282656
  `;
282355
282657
  }
282356
- function generateEnv(storage) {
282658
+ function generateEnv(storage, opts = {}) {
282659
+ const { includeApiKey = true } = opts;
282357
282660
  const databaseBlock = storage === "direct" ? "# Your Postgres \u2014 the system of record. The client registers this connection\n# (sent once over TLS, stored sealed) and every row lives HERE, never with Ablo.\n# Use a dedicated non-superuser role; the browser never sees this value.\nDATABASE_URL=postgres://user:password@host:5432/db\n" : "# Used by ablo/data-source.ts (your DB endpoint) + `ablo migrate` \u2014 NOT the client.\n# Ablo never sees it; the browser never sees it. Your DB stays in your app.\nDATABASE_URL=postgres://user:password@host:5432/db\n";
282358
282661
  const webhookBlock = storage === "endpoint" ? "# Signing secret for the webhook receiver (app/api/ablo/webhooks/route.ts).\n# Ablo mints this when you register the endpoint's URL (POST /v1/webhook_endpoints\n# or the dashboard) and returns it once \u2014 paste it here.\nABLO_WEBHOOK_SECRET=whsec_your_endpoint_secret_here\n" : "";
282359
- return `# Ablo Sync Engine \u2014 use a sk_test_ key for local dev (\`npx ablo dev\`)
282360
- ABLO_API_KEY=sk_test_your_key_here
282361
- ${webhookBlock}${databaseBlock}`;
282662
+ const apiKeyBlock = includeApiKey ? "# Ablo Sync Engine \u2014 use a sk_test_ key for local dev (`npx ablo push`)\nABLO_API_KEY=sk_test_your_key_here\n" : "";
282663
+ return `${apiKeyBlock}${webhookBlock}${databaseBlock}`;
282362
282664
  }
282363
282665
  function generateDataSource(orm) {
282364
282666
  return orm === "drizzle" ? drizzleDataSourceScaffold() : prismaDataSourceScaffold();