@abloatai/ablo 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -3
- package/README.md +1 -1
- package/dist/BaseSyncedStore.js +8 -9
- package/dist/Database.d.ts +14 -1
- package/dist/Database.js +228 -28
- package/dist/Model.d.ts +17 -0
- package/dist/Model.js +32 -1
- package/dist/SyncClient.d.ts +10 -6
- package/dist/SyncClient.js +390 -75
- package/dist/adapters/inMemoryStorage.d.ts +1 -0
- package/dist/adapters/inMemoryStorage.js +29 -13
- package/dist/ai-sdk/coordinatedTool.d.ts +15 -2
- package/dist/auth/schemas.d.ts +6 -0
- package/dist/auth/schemas.js +7 -0
- package/dist/cli.cjs +821 -402
- package/dist/client/Ablo.d.ts +53 -24
- package/dist/client/Ablo.js +12 -199
- package/dist/client/claimHeartbeatLoop.d.ts +1 -1
- package/dist/client/claimHeartbeatLoop.js +1 -1
- package/dist/client/createInternalComponents.d.ts +4 -0
- package/dist/client/createInternalComponents.js +3 -1
- package/dist/client/durableWrites.d.ts +21 -0
- package/dist/client/durableWrites.js +46 -0
- package/dist/client/httpClient.d.ts +21 -45
- package/dist/client/httpClient.js +104 -26
- package/dist/client/httpTransport.d.ts +8 -0
- package/dist/client/{ApiClient.js → httpTransport.js} +361 -308
- package/dist/client/modelRegistration.js +11 -0
- package/dist/client/options.d.ts +54 -7
- package/dist/client/options.js +3 -2
- package/dist/client/resourceTypes.d.ts +9 -85
- package/dist/client/resourceTypes.js +1 -1
- package/dist/client/sessionMint.d.ts +5 -6
- package/dist/client/sessionMint.js +4 -5
- package/dist/client/validateAbloOptions.js +3 -3
- package/dist/client/wsMutationExecutor.d.ts +3 -2
- package/dist/client/wsMutationExecutor.js +3 -2
- package/dist/coordination/schema.d.ts +30 -0
- package/dist/coordination/schema.js +14 -0
- package/dist/core/StoreManager.d.ts +2 -0
- package/dist/core/StoreManager.js +12 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/errorCodes.js +6 -2
- package/dist/errors.d.ts +4 -40
- package/dist/errors.js +5 -5
- package/dist/index.d.ts +20 -8
- package/dist/index.js +9 -5
- package/dist/interfaces/index.d.ts +5 -2
- package/dist/mutators/UndoManager.d.ts +2 -0
- package/dist/mutators/UndoManager.js +32 -0
- package/dist/mutators/defineMutators.d.ts +18 -0
- package/dist/mutators/defineMutators.js +4 -8
- package/dist/react/index.d.ts +1 -1
- package/dist/react/useAblo.d.ts +6 -4
- package/dist/react/useAblo.js +25 -3
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +1 -1
- package/dist/schema/schema.d.ts +31 -1
- package/dist/schema/select.d.ts +15 -0
- package/dist/schema/select.js +27 -3
- package/dist/source/connector.d.ts +3 -3
- package/dist/source/factory.d.ts +4 -6
- package/dist/source/factory.js +4 -7
- package/dist/source/index.d.ts +4 -4
- package/dist/source/index.js +2 -2
- package/dist/source/signing.d.ts +0 -3
- package/dist/source/types.d.ts +0 -26
- package/dist/stores/ObjectStore.d.ts +14 -1
- package/dist/stores/ObjectStore.js +33 -10
- package/dist/stores/ObjectStoreContract.d.ts +2 -0
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +3 -0
- package/dist/sync/BootstrapFetcher.d.ts +10 -0
- package/dist/sync/BootstrapFetcher.js +27 -4
- package/dist/sync/SyncWebSocket.d.ts +2 -1
- package/dist/sync/createClaimStream.d.ts +11 -2
- package/dist/sync/createClaimStream.js +4 -3
- package/dist/sync/persistedPrefix.d.ts +12 -0
- package/dist/sync/persistedPrefix.js +22 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/mocks/FakeDatabase.d.ts +18 -0
- package/dist/testing/mocks/FakeDatabase.js +10 -0
- package/dist/testing/mocks/MockWebSocket.d.ts +2 -1
- package/dist/transactions/TransactionQueue.d.ts +66 -8
- package/dist/transactions/TransactionQueue.js +607 -89
- package/dist/transactions/commitEnvelope.d.ts +132 -0
- package/dist/transactions/commitEnvelope.js +139 -0
- package/dist/transactions/commitOutboxStore.d.ts +32 -0
- package/dist/transactions/commitOutboxStore.js +26 -0
- package/dist/transactions/commitPayload.d.ts +15 -0
- package/dist/transactions/commitPayload.js +6 -0
- package/dist/transactions/durableWriteStore.d.ts +123 -0
- package/dist/transactions/durableWriteStore.js +30 -0
- package/dist/transactions/httpCommitEnvelope.d.ts +44 -0
- package/dist/transactions/httpCommitEnvelope.js +181 -0
- package/dist/transactions/idempotencyKey.d.ts +10 -0
- package/dist/transactions/idempotencyKey.js +9 -0
- package/dist/transactions/replayValidation.d.ts +83 -0
- package/dist/transactions/replayValidation.js +46 -1
- package/dist/types/global.d.ts +10 -29
- package/dist/types/global.js +4 -7
- package/dist/types/streams.d.ts +2 -28
- package/dist/wire/bootstrapReason.d.ts +9 -0
- package/dist/wire/bootstrapReason.js +8 -0
- package/dist/wire/frames.d.ts +6 -21
- package/dist/wire/frames.js +4 -4
- package/dist/wire/index.d.ts +6 -3
- package/dist/wire/index.js +3 -2
- package/dist/wire/protocolVersion.d.ts +30 -17
- package/dist/wire/protocolVersion.js +34 -18
- package/docs/agents.md +8 -3
- package/docs/api.md +16 -14
- package/docs/client-behavior.md +6 -3
- package/docs/coordination.md +4 -5
- package/docs/data-sources.md +5 -8
- package/docs/guarantees.md +21 -0
- package/docs/integration-guide.md +1 -0
- package/docs/mcp.md +1 -1
- package/docs/migration.md +21 -1
- package/docs/quickstart.md +11 -0
- package/docs/react.md +0 -46
- package/examples/README.md +6 -5
- package/llms.txt +15 -15
- package/package.json +3 -3
- package/dist/client/ApiClient.d.ts +0 -177
|
@@ -2,31 +2,30 @@
|
|
|
2
2
|
* The sync protocol version — a single integer, increasing over time, that
|
|
3
3
|
* covers everything the client and server must agree on to talk to each other:
|
|
4
4
|
* the WebSocket frame shapes, the HTTP request and response envelopes, and the
|
|
5
|
-
* delta encodings a client replays. It is separate from the
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* delta encodings a client replays. It is separate from the advisory
|
|
6
|
+
* app-schema hash, which diagnoses data-model drift; this version selects a
|
|
7
|
+
* concrete transport codec.
|
|
8
8
|
*
|
|
9
9
|
* Deploy ordering: the server is deployed first. It accepts every version in
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* server
|
|
13
|
-
*
|
|
14
|
-
* mismatch is visible rather than undefined.
|
|
10
|
+
* {@link SUPPORTED_PROTOCOL_VERSIONS}, and a client is never expected to connect
|
|
11
|
+
* to a server older than itself. If that does happen — for example a partial
|
|
12
|
+
* server rollback — {@link protocolVersionProblem} reports it as `too_new` so
|
|
13
|
+
* the mismatch is visible rather than undefined.
|
|
15
14
|
*
|
|
16
15
|
* To change the protocol:
|
|
17
16
|
* 1. Make the change backward-tolerant where you can. The server ignores
|
|
18
17
|
* unknown payload keys, so an additive field usually needs no version bump.
|
|
19
|
-
* 2. For a breaking change, bump {@link PROTOCOL_VERSION}, add
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* window.
|
|
23
|
-
*
|
|
24
|
-
* same change, deliberately.
|
|
18
|
+
* 2. For a breaking change, bump {@link PROTOCOL_VERSION}, add its codec to
|
|
19
|
+
* {@link SUPPORTED_PROTOCOL_VERSIONS}, and add a changelog entry below.
|
|
20
|
+
* 3. Keep {@link MIN_SUPPORTED_PROTOCOL_VERSION} low enough to cover every
|
|
21
|
+
* client still in use; raise it only after a deprecation window.
|
|
22
|
+
* 4. The protocol contract tests fail on a version without a codec and on any
|
|
23
|
+
* unreviewed bump — update them in the same change, deliberately.
|
|
25
24
|
*
|
|
26
25
|
* Changelog
|
|
27
26
|
* v1 (2026-07-03) — the protocol as of this field's introduction: the
|
|
28
27
|
* sync-request frame (`cursor`, `lastSyncId`, `capabilities`, optional
|
|
29
|
-
* `protocolVersion`), the commit,
|
|
28
|
+
* `protocolVersion`), the commit, claim, release, ack, and
|
|
30
29
|
* presence-update frames, the bootstrap and delta batches, and the HTTP
|
|
31
30
|
* error and list envelopes. A client that predates this field sends no
|
|
32
31
|
* `protocolVersion` and is treated as v1, since introducing the field
|
|
@@ -39,10 +38,22 @@ export declare const PROTOCOL_VERSION = 1;
|
|
|
39
38
|
* and with a changelog entry.
|
|
40
39
|
*/
|
|
41
40
|
export declare const MIN_SUPPORTED_PROTOCOL_VERSION = 1;
|
|
41
|
+
/** The version used by clients from before the version field existed. */
|
|
42
|
+
export declare const DEFAULT_PROTOCOL_VERSION = 1;
|
|
43
|
+
/**
|
|
44
|
+
* Every wire version this build can actually decode. This is intentionally an
|
|
45
|
+
* explicit manifest rather than a computed numeric range: a version is not
|
|
46
|
+
* supported merely because its number falls between two constants. Server
|
|
47
|
+
* codec registries use {@link SupportedProtocolVersion} as their key type, so
|
|
48
|
+
* adding a version here makes a missing decoder a compile error.
|
|
49
|
+
*/
|
|
50
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly [1];
|
|
51
|
+
export type SupportedProtocolVersion = (typeof SUPPORTED_PROTOCOL_VERSIONS)[number];
|
|
52
|
+
export type ProtocolVersionProblem = 'too_old' | 'too_new' | 'codec_missing';
|
|
42
53
|
/**
|
|
43
54
|
* The WebSocket close code the server sends to reject a protocol-version
|
|
44
55
|
* mismatch. It sits alongside the other application close codes (4001 for a
|
|
45
|
-
* credential problem
|
|
56
|
+
* credential problem), and its reason string is the
|
|
46
57
|
* error code `protocol_version_unsupported`. A client should treat this close
|
|
47
58
|
* as terminal: reconnecting cannot heal a version mismatch, but upgrading the
|
|
48
59
|
* client or rolling the server forward can.
|
|
@@ -55,6 +66,8 @@ export declare const WS_CLOSE_PROTOCOL_VERSION = 4010;
|
|
|
55
66
|
* — counts as v1. A non-integer value is treated as `'too_old'` so the check
|
|
56
67
|
* fails closed and visibly.
|
|
57
68
|
*/
|
|
58
|
-
export declare function protocolVersionProblem(announced: number | undefined):
|
|
69
|
+
export declare function protocolVersionProblem(announced: number | undefined): ProtocolVersionProblem | null;
|
|
70
|
+
/** Resolve an announced value to a version that has a concrete codec. */
|
|
71
|
+
export declare function resolveProtocolVersion(announced: number | undefined): SupportedProtocolVersion | null;
|
|
59
72
|
/** The HTTP request header a client uses to announce its protocol version. */
|
|
60
73
|
export declare const PROTOCOL_VERSION_HEADER = "Ablo-Protocol-Version";
|
|
@@ -2,31 +2,30 @@
|
|
|
2
2
|
* The sync protocol version — a single integer, increasing over time, that
|
|
3
3
|
* covers everything the client and server must agree on to talk to each other:
|
|
4
4
|
* the WebSocket frame shapes, the HTTP request and response envelopes, and the
|
|
5
|
-
* delta encodings a client replays. It is separate from the
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* delta encodings a client replays. It is separate from the advisory
|
|
6
|
+
* app-schema hash, which diagnoses data-model drift; this version selects a
|
|
7
|
+
* concrete transport codec.
|
|
8
8
|
*
|
|
9
9
|
* Deploy ordering: the server is deployed first. It accepts every version in
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* server
|
|
13
|
-
*
|
|
14
|
-
* mismatch is visible rather than undefined.
|
|
10
|
+
* {@link SUPPORTED_PROTOCOL_VERSIONS}, and a client is never expected to connect
|
|
11
|
+
* to a server older than itself. If that does happen — for example a partial
|
|
12
|
+
* server rollback — {@link protocolVersionProblem} reports it as `too_new` so
|
|
13
|
+
* the mismatch is visible rather than undefined.
|
|
15
14
|
*
|
|
16
15
|
* To change the protocol:
|
|
17
16
|
* 1. Make the change backward-tolerant where you can. The server ignores
|
|
18
17
|
* unknown payload keys, so an additive field usually needs no version bump.
|
|
19
|
-
* 2. For a breaking change, bump {@link PROTOCOL_VERSION}, add
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* window.
|
|
23
|
-
*
|
|
24
|
-
* same change, deliberately.
|
|
18
|
+
* 2. For a breaking change, bump {@link PROTOCOL_VERSION}, add its codec to
|
|
19
|
+
* {@link SUPPORTED_PROTOCOL_VERSIONS}, and add a changelog entry below.
|
|
20
|
+
* 3. Keep {@link MIN_SUPPORTED_PROTOCOL_VERSION} low enough to cover every
|
|
21
|
+
* client still in use; raise it only after a deprecation window.
|
|
22
|
+
* 4. The protocol contract tests fail on a version without a codec and on any
|
|
23
|
+
* unreviewed bump — update them in the same change, deliberately.
|
|
25
24
|
*
|
|
26
25
|
* Changelog
|
|
27
26
|
* v1 (2026-07-03) — the protocol as of this field's introduction: the
|
|
28
27
|
* sync-request frame (`cursor`, `lastSyncId`, `capabilities`, optional
|
|
29
|
-
* `protocolVersion`), the commit,
|
|
28
|
+
* `protocolVersion`), the commit, claim, release, ack, and
|
|
30
29
|
* presence-update frames, the bootstrap and delta batches, and the HTTP
|
|
31
30
|
* error and list envelopes. A client that predates this field sends no
|
|
32
31
|
* `protocolVersion` and is treated as v1, since introducing the field
|
|
@@ -39,10 +38,20 @@ export const PROTOCOL_VERSION = 1;
|
|
|
39
38
|
* and with a changelog entry.
|
|
40
39
|
*/
|
|
41
40
|
export const MIN_SUPPORTED_PROTOCOL_VERSION = 1;
|
|
41
|
+
/** The version used by clients from before the version field existed. */
|
|
42
|
+
export const DEFAULT_PROTOCOL_VERSION = 1;
|
|
43
|
+
/**
|
|
44
|
+
* Every wire version this build can actually decode. This is intentionally an
|
|
45
|
+
* explicit manifest rather than a computed numeric range: a version is not
|
|
46
|
+
* supported merely because its number falls between two constants. Server
|
|
47
|
+
* codec registries use {@link SupportedProtocolVersion} as their key type, so
|
|
48
|
+
* adding a version here makes a missing decoder a compile error.
|
|
49
|
+
*/
|
|
50
|
+
export const SUPPORTED_PROTOCOL_VERSIONS = [1];
|
|
42
51
|
/**
|
|
43
52
|
* The WebSocket close code the server sends to reject a protocol-version
|
|
44
53
|
* mismatch. It sits alongside the other application close codes (4001 for a
|
|
45
|
-
* credential problem
|
|
54
|
+
* credential problem), and its reason string is the
|
|
46
55
|
* error code `protocol_version_unsupported`. A client should treat this close
|
|
47
56
|
* as terminal: reconnecting cannot heal a version mismatch, but upgrading the
|
|
48
57
|
* client or rolling the server forward can.
|
|
@@ -56,12 +65,19 @@ export const WS_CLOSE_PROTOCOL_VERSION = 4010;
|
|
|
56
65
|
* fails closed and visibly.
|
|
57
66
|
*/
|
|
58
67
|
export function protocolVersionProblem(announced) {
|
|
59
|
-
const v = announced ??
|
|
68
|
+
const v = announced ?? DEFAULT_PROTOCOL_VERSION;
|
|
60
69
|
if (!Number.isInteger(v) || v < MIN_SUPPORTED_PROTOCOL_VERSION)
|
|
61
70
|
return 'too_old';
|
|
62
71
|
if (v > PROTOCOL_VERSION)
|
|
63
72
|
return 'too_new';
|
|
64
|
-
return
|
|
73
|
+
return SUPPORTED_PROTOCOL_VERSIONS.some((candidate) => candidate === v)
|
|
74
|
+
? null
|
|
75
|
+
: 'codec_missing';
|
|
76
|
+
}
|
|
77
|
+
/** Resolve an announced value to a version that has a concrete codec. */
|
|
78
|
+
export function resolveProtocolVersion(announced) {
|
|
79
|
+
const version = announced ?? DEFAULT_PROTOCOL_VERSION;
|
|
80
|
+
return SUPPORTED_PROTOCOL_VERSIONS.find((candidate) => candidate === version) ?? null;
|
|
65
81
|
}
|
|
66
82
|
/** The HTTP request header a client uses to announce its protocol version. */
|
|
67
83
|
export const PROTOCOL_VERSION_HEADER = 'Ablo-Protocol-Version';
|
package/docs/agents.md
CHANGED
|
@@ -27,10 +27,15 @@ import { schema } from "./schema";
|
|
|
27
27
|
|
|
28
28
|
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, transport: "http" });
|
|
29
29
|
|
|
30
|
-
// Reads + writes, fully typed off your schema
|
|
30
|
+
// Reads + writes, fully typed off your schema.
|
|
31
|
+
// `retrieve` resolves to the row, or `undefined` when none matches.
|
|
31
32
|
const open = await ablo.tasks.list({ where: { status: "todo" } });
|
|
32
|
-
|
|
33
|
-
await ablo.tasks.
|
|
33
|
+
|
|
34
|
+
const task = await ablo.tasks.retrieve({ id: open[0].id });
|
|
35
|
+
if (!task) throw new Error("task not found");
|
|
36
|
+
|
|
37
|
+
console.log(task.title);
|
|
38
|
+
await ablo.tasks.update({ id: task.id, data: { status: "done" } });
|
|
34
39
|
```
|
|
35
40
|
|
|
36
41
|
It exposes `retrieve` / `list` / `create` / `update` / `delete`, plus `commits`
|
package/docs/api.md
CHANGED
|
@@ -206,27 +206,29 @@ The SDK is a convenience wrapper over a model-scoped HTTP surface — the same
|
|
|
206
206
|
noun (`model`) and verbs as `ablo.<model>.…`. Non-JS callers (or curl) use it
|
|
207
207
|
directly. The table below shows the shape with `{model}` as a placeholder; the
|
|
208
208
|
[OpenAPI spec](./openapi.json) expands it into one **typed** path per model
|
|
209
|
-
(`/v1/models/task`, `/v1/models/deck`, …, generated from your schema) so each
|
|
209
|
+
(`/api/v1/models/task`, `/api/v1/models/deck`, …, generated from your schema) so each
|
|
210
210
|
endpoint documents that model's real field contract instead of a generic blob.
|
|
211
211
|
|
|
212
212
|
| SDK call | HTTP |
|
|
213
213
|
|---|---|
|
|
214
|
-
| `ablo.<model>.create({ data })` | `POST /v1/models/{model}` |
|
|
215
|
-
| `ablo.<model>.list({ where })` | `GET /v1/models/{model}` |
|
|
216
|
-
| `ablo.<model>.retrieve({ id })` | `GET /v1/models/{model}/{id}` |
|
|
217
|
-
| `ablo.<model>.update({ id, data })` | `PATCH /v1/models/{model}/{id}` |
|
|
218
|
-
| `ablo.<model>.delete({ id })` | `DELETE /v1/models/{model}/{id}` |
|
|
219
|
-
| `ablo.<model>.claim({ id })` | `POST /v1/models/{model}/{id}/claim` |
|
|
220
|
-
| (release a claim) | `DELETE /v1/models/{model}/{id}/claim` |
|
|
214
|
+
| `ablo.<model>.create({ data })` | `POST /api/v1/models/{model}` |
|
|
215
|
+
| `ablo.<model>.list({ where })` | `GET /api/v1/models/{model}` |
|
|
216
|
+
| `ablo.<model>.retrieve({ id })` | `GET /api/v1/models/{model}/{id}` |
|
|
217
|
+
| `ablo.<model>.update({ id, data })` | `PATCH /api/v1/models/{model}/{id}` |
|
|
218
|
+
| `ablo.<model>.delete({ id })` | `DELETE /api/v1/models/{model}/{id}` |
|
|
219
|
+
| `ablo.<model>.claim({ id })` | `POST /api/v1/models/{model}/{id}/claim` |
|
|
220
|
+
| (release a claim) | `DELETE /api/v1/models/{model}/{id}/claim` |
|
|
221
221
|
|
|
222
222
|
Auth is a bearer API key: `Authorization: Bearer sk_…`. Mutations take an
|
|
223
223
|
`Idempotency-Key` header — derive it from the business event, not a random
|
|
224
|
-
value, so a retry never double-writes.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
`
|
|
224
|
+
value, so a retry never double-writes. Direct HTTP writes return a protocol
|
|
225
|
+
receipt; the typed SDK turns single-model writes into their application result
|
|
226
|
+
(the created or updated row, or nothing for delete). A rejected write carries an
|
|
227
|
+
error `code` (e.g. `stale_context`, `intent_conflict`) to act on.
|
|
228
|
+
`GET /api/v1/models/{model}` is cursor-paginated (`limit`, `order`, `order_by`,
|
|
229
|
+
`starting_after`) and returns `{ data, has_more, next_cursor }`.
|
|
230
|
+
|
|
231
|
+
`POST /api/v1/commits` remains the path for **atomic multi-op** writes (several
|
|
230
232
|
operations across rows/models that must commit together) — the per-model routes
|
|
231
233
|
above are the one-record path. Both run the identical guarded-write engine.
|
|
232
234
|
|
package/docs/client-behavior.md
CHANGED
|
@@ -32,6 +32,7 @@ Common options:
|
|
|
32
32
|
| `databaseUrl` | Optional, server-only. Registers your Postgres directly (the connection-string path). Pass it explicitly — it is **not** auto-read from the environment. Omit it for a signed Data Source endpoint or the hosted sandbox. The SDK throws if it sees this in a browser. |
|
|
33
33
|
| `baseURL` | Override the hosted sync endpoint for staging or private deployments. |
|
|
34
34
|
| `persistence` | `memory` by default. Use `indexeddb` for a durable browser cache that survives reloads. |
|
|
35
|
+
| `durableWrites` | Optional crash recovery for unacknowledged agent/worker writes. Independent of the default memory cache; accepts `{ store, namespace? }`. |
|
|
35
36
|
| `transport` | `'websocket'` (default) is the live, stateful client — a persistent socket, a local synced pool, and `onChange` subscriptions. `'http'` returns the **stateless** client for server-side actors (agents, workers, serverless): the same `ablo.<model>` read/write/claim surface, but each call is one HTTP round-trip with no socket. Under `'http'` the return type narrows to `AbloHttpClient`, so stateful-only methods (`get`/`getAll`, `onChange`, `watch`) are compile errors rather than runtime gaps. |
|
|
36
37
|
| `fetch` | Custom fetch implementation for tests or non-standard runtimes. |
|
|
37
38
|
| `defaultHeaders` | Extra headers attached to every HTTP request. |
|
|
@@ -109,7 +110,6 @@ await ablo.weatherReports.update({
|
|
|
109
110
|
readAt: snap.stamp,
|
|
110
111
|
onStale: 'reject',
|
|
111
112
|
idempotencyKey: 'report_stockholm:mark-ready:v1',
|
|
112
|
-
timeout: 20_000,
|
|
113
113
|
});
|
|
114
114
|
```
|
|
115
115
|
|
|
@@ -119,7 +119,6 @@ await ablo.weatherReports.update({
|
|
|
119
119
|
| `readAt` | State cursor the write was based on. |
|
|
120
120
|
| `onStale` | Policy when the target changed after `readAt`. Prefer `reject`. |
|
|
121
121
|
| `idempotencyKey` | Stable key for retry-safe writes. The SDK generates one when omitted. |
|
|
122
|
-
| `timeout` | Maximum time for the write call. |
|
|
123
122
|
|
|
124
123
|
## Claimed Behavior
|
|
125
124
|
|
|
@@ -136,7 +135,11 @@ if (active) {
|
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
const handle = await ablo.weatherReports.claim({ id: 'report_stockholm' });
|
|
139
|
-
await ablo.weatherReports.update({
|
|
138
|
+
await ablo.weatherReports.update({
|
|
139
|
+
id: handle.data.id,
|
|
140
|
+
data: { status: 'ready' },
|
|
141
|
+
claim: handle,
|
|
142
|
+
});
|
|
140
143
|
await handle.release();
|
|
141
144
|
```
|
|
142
145
|
|
package/docs/coordination.md
CHANGED
|
@@ -465,13 +465,13 @@ Each beat's answer carries two more things:
|
|
|
465
465
|
|
|
466
466
|
Works identically on both transports: the realtime client sends a
|
|
467
467
|
`claim_heartbeat` frame; the HTTP client posts
|
|
468
|
-
`POST /v1/models/{model}/{id}/claim/heartbeat` (`{ ttl?, claimId?, details? }`).
|
|
468
|
+
`POST /api/v1/models/{model}/{id}/claim/heartbeat` (`{ ttl?, claimId?, details? }`).
|
|
469
469
|
Over HTTP, a **queued** claim can heartbeat too — it refreshes the waiter's
|
|
470
470
|
slot in the line (a queued slot is TTL'd like a lease) and reports
|
|
471
471
|
`{ status: 'queued', position }`.
|
|
472
472
|
|
|
473
473
|
A stateless worker holding **many** rows beats them all in one round trip:
|
|
474
|
-
`ablo.claims.heartbeatAll({ ttl: '5m' })` → `POST /v1/claims/heartbeat`, one
|
|
474
|
+
`ablo.claims.heartbeatAll({ ttl: '5m' })` → `POST /api/v1/claims/heartbeat`, one
|
|
475
475
|
entry per extended lease. This is the socketless twin of the realtime
|
|
476
476
|
keepalive, which already renews every held lease on each ping.
|
|
477
477
|
|
|
@@ -631,9 +631,8 @@ guarantee can't drift): read the freshest row + its watermark → run your updat
|
|
|
631
631
|
comes from the watermark, **not** from participant identity — so it's immune to
|
|
632
632
|
the shared-credential clobber footgun and needs no `claim` and no per-agent `rk_`.
|
|
633
633
|
|
|
634
|
-
Nothing about claims, identity, or conflict codes surfaces.
|
|
635
|
-
returns the reconciled row
|
|
636
|
-
WebSocket client) or, at the extreme, throws **one** error:
|
|
634
|
+
Nothing about claims, identity, or conflict codes surfaces. On both transports,
|
|
635
|
+
the call returns the reconciled row or, at the extreme, throws **one** error:
|
|
637
636
|
|
|
638
637
|
```ts
|
|
639
638
|
import { AbloContentionError } from '@abloatai/ablo';
|
package/docs/data-sources.md
CHANGED
|
@@ -180,14 +180,11 @@ database, schema — the connection string itself is sealed and never echoed bac
|
|
|
180
180
|
the replication slot position, and the commit log that drives sync. Never your
|
|
181
181
|
rows.
|
|
182
182
|
|
|
183
|
-
> **
|
|
184
|
-
>
|
|
185
|
-
>
|
|
186
|
-
>
|
|
187
|
-
>
|
|
188
|
-
> removed. Maintainers: see
|
|
189
|
-
> [internal/byo-wal-consumer.md](./internal/byo-wal-consumer.md) for the
|
|
190
|
-
> architecture and remaining slices.
|
|
183
|
+
> **Postgres replication status: Preview.** Registration, readiness checks, and
|
|
184
|
+
> the server replication fleet are implemented and boot-wired. Preview describes
|
|
185
|
+
> product rollout and support, not an inactive code path. Maintainers: see
|
|
186
|
+
> [internal/postgres-replication.md](./internal/postgres-replication.md) for the
|
|
187
|
+
> architecture and operational invariants.
|
|
191
188
|
|
|
192
189
|
## Next steps
|
|
193
190
|
|
package/docs/guarantees.md
CHANGED
|
@@ -125,6 +125,27 @@ const ablo = Ablo({
|
|
|
125
125
|
|
|
126
126
|
Node, SSR, tests, and agents use in-memory persistence ('memory') automatically.
|
|
127
127
|
|
|
128
|
+
Cache persistence and outbound-write recovery are separate concerns. Most
|
|
129
|
+
clients need only the default memory cache: once the server confirms a write,
|
|
130
|
+
the server is durable and the idempotency key makes a retry safe. A long-running
|
|
131
|
+
worker that must also recover an unacknowledged write after its own process dies
|
|
132
|
+
can opt into a durable write journal:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const ablo = Ablo({
|
|
136
|
+
schema,
|
|
137
|
+
apiKey: process.env.ABLO_API_KEY,
|
|
138
|
+
durableWrites: {
|
|
139
|
+
store: workerWriteStore,
|
|
140
|
+
namespace: 'report-worker',
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The store can be backed by the worker's workflow state, SQLite, or another
|
|
146
|
+
durable system. Actor identity is derived from authentication; `namespace` only
|
|
147
|
+
separates workflow or deployment lanes sharing the same store.
|
|
148
|
+
|
|
128
149
|
## Storage Boundary
|
|
129
150
|
|
|
130
151
|
Ablo does not need a customer database URL. When your own database is canonical,
|
|
@@ -492,6 +492,7 @@ Keep agent writes on the same schema client surface as the app.
|
|
|
492
492
|
| `/testing` | Test harnesses and deterministic mocks. |
|
|
493
493
|
| `Data Source` | Keep your app database canonical. |
|
|
494
494
|
| `persistence: 'indexeddb'` | Durable browser cache that survives reloads, for apps that need it. |
|
|
495
|
+
| `durableWrites: { store, namespace? }` | Recover unacknowledged worker writes after a process restart. |
|
|
495
496
|
| `claim` / `claim.state` / `claim.queue` | Show active work and coordinate before a write. |
|
|
496
497
|
| `snapshot` + `readAt` | Reject writes based on stale state. |
|
|
497
498
|
| `mutable`, `readOnly`, `field`, `indexed` | Advanced schema and read tuning. |
|
package/docs/mcp.md
CHANGED
|
@@ -16,7 +16,7 @@ edits rows → coordination; teaching your IDE assistant the SDK → helper.
|
|
|
16
16
|
## Coordination server (`@ablo/mcp`)
|
|
17
17
|
|
|
18
18
|
The coordination server is the MCP projection of the model-scoped API
|
|
19
|
-
(`/v1/models/...`) — the same surface as `ablo.<model>.create/update/claim` and
|
|
19
|
+
(`/api/v1/models/...`) — the same surface as `ablo.<model>.create/update/claim` and
|
|
20
20
|
the REST routes, rendered as tools. An agent connects with your API key and
|
|
21
21
|
gets one safe loop: **claim → read → commit → release.**
|
|
22
22
|
|
package/docs/migration.md
CHANGED
|
@@ -11,7 +11,8 @@ change when you upgrade.
|
|
|
11
11
|
|
|
12
12
|
| Version | What changed | What to do |
|
|
13
13
|
|---|---|---|
|
|
14
|
-
| **0.
|
|
14
|
+
| **0.28.0** | Removed React placeholders that had no working runtime | `usePresence` → `usePeers` or `useWatch`; `useClaim` → `ablo.<model>.claim`; `SyncGroupProvider` / `useSyncGroup` → `useWatch({ scope })` |
|
|
15
|
+
| **0.11.0** | Historical `intent` → `claim` rename | The hook renamed in that release was later removed in 0.28.0. Current code uses `ablo.<model>.claim` or `useWatch` |
|
|
15
16
|
| **0.10.0** | Environment enum renamed `test`/`live` → `sandbox`/`production` | Update code that branches on the environment (e.g. source `mode`): `'test'`→`'sandbox'`, `'live'`→`'production'`. Key prefixes `sk_test_`/`sk_live_` are unchanged |
|
|
16
17
|
| **0.9.2** | `turn` primitive + agent-work `tasks` resource removed | Coordinate with `claim`; mint a scoped session instead of `agent().run()` |
|
|
17
18
|
| **0.9.2** | `intents` deprecated in favor of `claim` | Use `ablo.<model>.claim`; `ablo.intents` is now `@internal` |
|
|
@@ -25,8 +26,27 @@ change when you upgrade.
|
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
29
|
+
## 0.28.0 — dead React multiplayer placeholders removed
|
|
30
|
+
|
|
31
|
+
Four React exports looked usable but had no live implementation:
|
|
32
|
+
|
|
33
|
+
- `usePresence` returned no provider-backed presence value. Use `usePeers` for
|
|
34
|
+
read-only presence or `useWatch` to join a scoped participant.
|
|
35
|
+
- `useClaim` depended on a callback the provider never supplied and always
|
|
36
|
+
threw. Use `ablo.<model>.claim({ id, ... })` for row claims or
|
|
37
|
+
`useWatch({ scope, claim: true })` for a scoped participant claim.
|
|
38
|
+
- `SyncGroupProvider` and `useSyncGroup` had no repository consumers. Pass the
|
|
39
|
+
scope directly to `useWatch({ scope })`.
|
|
40
|
+
|
|
41
|
+
There is no compatibility alias: the replacement APIs were already the only
|
|
42
|
+
working paths.
|
|
43
|
+
|
|
28
44
|
## 0.11.0 — `intent` → `claim` rename completed
|
|
29
45
|
|
|
46
|
+
> **Historical note:** this section documents the 0.11.0 transition.
|
|
47
|
+
> `useClaim` was subsequently removed in 0.28.0 because its provider callback
|
|
48
|
+
> was never wired. Do not migrate new code to it; use the replacements above.
|
|
49
|
+
|
|
30
50
|
The coordination primitive has been `claim` since 0.9.2, but a few `intent`-named
|
|
31
51
|
surfaces lingered. 0.11.0 finishes the rename. There are three edits, all
|
|
32
52
|
mechanical:
|
package/docs/quickstart.md
CHANGED
|
@@ -191,6 +191,17 @@ const updated = await ablo.weatherReports.update({
|
|
|
191
191
|
console.log({ id: updated.id, status: updated.status }); // { id: '...', status: 'ready' }
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
+
Read a single row back with `retrieve({ id })`. It resolves to the row, or to
|
|
195
|
+
`undefined` when no row has that id — so narrow it once, then the fields are
|
|
196
|
+
fully typed:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
const report = await ablo.weatherReports.retrieve({ id: created.id });
|
|
200
|
+
if (!report) throw new Error(`weatherReports ${created.id} not found`);
|
|
201
|
+
|
|
202
|
+
console.log(report.status); // 'ready'
|
|
203
|
+
```
|
|
204
|
+
|
|
194
205
|
## Add coordination for slow work
|
|
195
206
|
|
|
196
207
|
When AI or background work will touch an existing row for more than a quick
|
package/docs/react.md
CHANGED
|
@@ -178,52 +178,6 @@ imperative work after an event or effect.
|
|
|
178
178
|
|
|
179
179
|
See [API reference](/docs/api) for the full options surface.
|
|
180
180
|
|
|
181
|
-
## useClaim — named-claim dispatcher
|
|
182
|
-
|
|
183
|
-
`useClaim` (renamed from `useIntent` in 0.11.0) is typed sugar for invoking a
|
|
184
|
-
*named* claim from your own coordination vocabulary — distinct from the
|
|
185
|
-
row-level `ablo.<model>.claim({ id })` resource claim. Use it when you want to
|
|
186
|
-
broadcast a semantic claim like "I'm editing this layer" or "the agent is
|
|
187
|
-
generating here" and let your transport turn it into a network effect.
|
|
188
|
-
|
|
189
|
-
Declare the vocabulary once via module augmentation on the `Register` interface
|
|
190
|
-
(the `Claims` key — previously `Intents`):
|
|
191
|
-
|
|
192
|
-
```ts
|
|
193
|
-
declare module '@abloatai/ablo' {
|
|
194
|
-
interface Register {
|
|
195
|
-
Claims: {
|
|
196
|
-
editLayer: { slideId: string; layerId: string };
|
|
197
|
-
generateWithAI: { entityId: string; tool: string };
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Then `useClaim('editLayer')` returns a function whose sole argument is the
|
|
204
|
-
`editLayer` shape — purely compile-time narrowing, no runtime checks:
|
|
205
|
-
|
|
206
|
-
```tsx
|
|
207
|
-
'use client';
|
|
208
|
-
|
|
209
|
-
import { useClaim } from '@abloatai/ablo/react';
|
|
210
|
-
|
|
211
|
-
export function LayerToolbar({ slideId, layerId }: { slideId: string; layerId: string }) {
|
|
212
|
-
const claimEditLayer = useClaim('editLayer');
|
|
213
|
-
|
|
214
|
-
return (
|
|
215
|
-
<button onClick={() => claimEditLayer({ slideId, layerId })}>
|
|
216
|
-
Edit layer
|
|
217
|
-
</button>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
The hook is pure sugar: the actual network effect lives in the `beginClaim`
|
|
223
|
-
function wired into the provider (bound to your transport). If no `beginClaim`
|
|
224
|
-
is wired, the returned invoker throws `AbloValidationError` with code
|
|
225
|
-
`claim_not_wired`.
|
|
226
|
-
|
|
227
181
|
## useWatch — scoped presence + read interest
|
|
228
182
|
|
|
229
183
|
`useWatch` is the React form of `ablo.<model>.watch`. It joins multiplayer for a
|
package/examples/README.md
CHANGED
|
@@ -21,13 +21,14 @@ const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
|
21
21
|
Then:
|
|
22
22
|
|
|
23
23
|
- create with `ablo.weatherReports.create`
|
|
24
|
-
- read with `ablo.weatherReports.
|
|
25
|
-
-
|
|
24
|
+
- read with `ablo.weatherReports.retrieve` or `ablo.weatherReports.list`
|
|
25
|
+
- coordinate long-running work with `ablo.weatherReports.claim`
|
|
26
26
|
- write with `ablo.weatherReports.update`
|
|
27
|
-
-
|
|
27
|
+
- dispose the client when the worker finishes
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
Import the same schema in every runtime. Use `commits.create` only when several
|
|
30
|
+
typed row operations must land atomically; ordinary writes stay on
|
|
31
|
+
`ablo.<model>.create/update/delete`.
|
|
31
32
|
|
|
32
33
|
## Running
|
|
33
34
|
|
package/llms.txt
CHANGED
|
@@ -8,7 +8,7 @@ Use AI SDK for the agent loop. Use Ablo when agent reads and writes must persist
|
|
|
8
8
|
|
|
9
9
|
## Start here
|
|
10
10
|
|
|
11
|
-
First action when integrating into an app: run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`. Agents have no TTY — `--yes` is REQUIRED or it HANGS.
|
|
11
|
+
First action when integrating into an app: run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`. Agents have no TTY — `--yes` is REQUIRED or it HANGS. It scaffolds `ablo/schema.ts`, the `Ablo({ schema, apiKey })` client, and (for Next.js) the browser provider + session route. All on the current API. Edit the generated files rather than hand-writing from this doc. Connecting a database is a SEPARATE step with one path — logical replication via `npx ablo connect` (see Storage Boundary); the signed Data Source endpoint is the fallback when database credentials must stay inside the app.
|
|
12
12
|
|
|
13
13
|
Second: make sure a key exists — WITHOUT printing it. The key is a secret; it must never appear in your output, your reasoning, or a file you echo (it would live in the conversation history forever). Check PRESENCE only: `[ -n "$ABLO_API_KEY" ] && echo set` and `grep -cq '^ABLO_API_KEY=' .env.local && echo wired` — never `cat .env.local`, never `echo $ABLO_API_KEY`. If neither check passes, ask the HUMAN to run `npx ablo login` once — it opens a browser and saves a `sk_test_` key locally; an agent must NOT run it. You never copy the key by hand: the next step writes it into `.env.local` (and gitignores it) for you.
|
|
14
14
|
|
|
@@ -69,14 +69,14 @@ await. The query reads accept `where`, `filter`, `orderBy`, `limit`, `offset`,
|
|
|
69
69
|
and `state`; state defaults to `'live'`, with `'archived'` and `'all'` to include
|
|
70
70
|
retired rows.
|
|
71
71
|
|
|
72
|
-
Workers
|
|
73
|
-
`
|
|
72
|
+
Workers import the same app schema and select `transport: 'http'`. The transport
|
|
73
|
+
changes; the typed `ablo.<model>` contract does not. There is no public
|
|
74
|
+
schema-less or string-keyed model client.
|
|
74
75
|
|
|
75
76
|
React reads should use selector `useAblo`: `useAblo((ablo) => ablo.weatherReports.get(id))` (synchronous local read, reactive in render).
|
|
76
77
|
Use zero-argument `useAblo()` only when a component needs the client for an
|
|
77
|
-
event handler or effect.
|
|
78
|
-
`useMutate`
|
|
79
|
-
first integration path.
|
|
78
|
+
event handler or effect. The older string-keyed hooks (`useQuery`, `useOne`,
|
|
79
|
+
`useReader`, and `useMutate`) are removed; do not import or recommend them.
|
|
80
80
|
|
|
81
81
|
## Multiplayer
|
|
82
82
|
|
|
@@ -98,9 +98,10 @@ coordination until the app reports it through Data Source events.
|
|
|
98
98
|
## Claimed Behavior
|
|
99
99
|
|
|
100
100
|
Reads never silently block. Schema reads stay open while a row is claimed.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
Typed reads through `ablo.<model>.retrieve({ id })` return the row by default,
|
|
102
|
+
even while it is claimed. Pass `ifClaimed: 'fail'` to throw
|
|
103
|
+
`AbloClaimedError`, and inspect active coordination separately through
|
|
104
|
+
`ablo.<model>.claim.state({ id })`.
|
|
104
105
|
|
|
105
106
|
Reads never block on a claim. To wait for a row to free up, `claim({ id })` it —
|
|
106
107
|
the claim queues fairly behind the current holder and is granted when the row
|
|
@@ -126,9 +127,7 @@ THE ONE PATH IS LOGICAL REPLICATION. In production every schema model is backed
|
|
|
126
127
|
|
|
127
128
|
FALLBACK (only when the database can't grant a `REPLICATION` role, e.g. a locked-down managed DB): a signed Data Source endpoint that hands Ablo an ORM `adapter` (Drizzle default; Prisma and Kysely supported) — it owns the transaction, exactly-once idempotency, and outbox in ONE pass; your credentials never leave your infrastructure.
|
|
128
129
|
|
|
129
|
-
DEPRECATED — being removed, do NOT recommend or scaffold: the `databaseUrl` constructor option (the dial-in, where Ablo held a read/write connection and committed directly to your DB). The end-state constructor is `Ablo({ schema, apiKey })`; the database is connected out of band via `ablo connect`, not through `databaseUrl`.
|
|
130
|
-
|
|
131
|
-
GOTCHA the user WILL hit: `DATABASE_URL` must use a NON-superuser, NON-BYPASSRLS role (Ablo enforces row-level security; owner roles are rejected with `database_role_cannot_enforce_rls`). Neon's and Supabase's default dashboard connection strings use the database OWNER (e.g. `neondb_owner`) and are rejected. EASIEST: `npx ablo migrate` detects the unsafe role and creates the scoped one automatically from the user's machine (owner credential never reaches Ablo; new DATABASE_URL written to the env file). Manual alternative — create a scoped role first: `CREATE ROLE ablo_app LOGIN PASSWORD '...' NOSUPERUSER NOBYPASSRLS; GRANT CREATE, CONNECT ON DATABASE <db> TO ablo_app; GRANT CREATE, USAGE ON SCHEMA public TO ablo_app;` — then swap user/password into the same host/db string.
|
|
130
|
+
DEPRECATED — being removed, do NOT recommend or scaffold: the `databaseUrl` constructor option (the dial-in, where Ablo held a read/write connection and committed directly to your DB). The end-state constructor is `Ablo({ schema, apiKey })`; the database is connected out of band via `ablo connect`, not through `databaseUrl`. The role rule you may have seen belongs ONLY to this dial-in: a `databaseUrl` had to name a NON-superuser, NON-BYPASSRLS role, or Ablo rejected it with `database_role_cannot_enforce_rls` (it enforced row-level security through the connection it held, so an owner/BYPASSRLS role — e.g. Neon's `neondb_owner` — would have silently skipped RLS). The WAL path retires that rule entirely: Ablo never holds a connection to your rows, so there is no `DATABASE_URL` to scope — `ablo connect` needs a `REPLICATION` role instead. `ablo init` still scaffolds `--storage direct`/`databaseUrl` for existing dial-in projects and the CLI now warns on it; prefer `ablo connect` regardless of what `init` scaffolds.
|
|
132
131
|
|
|
133
132
|
```ts
|
|
134
133
|
// app/api/ablo/source/route.ts
|
|
@@ -146,7 +145,7 @@ export const { POST } = dataSourceNext({
|
|
|
146
145
|
});
|
|
147
146
|
```
|
|
148
147
|
|
|
149
|
-
`npx ablo
|
|
148
|
+
Connect a database with `npx ablo connect` (logical replication — the one path above). The signed Data Source endpoint (code above) is the fallback when app database credentials must stay private; scaffold it with `npx ablo init --storage endpoint` — Ablo only calls the endpoint. (`--storage direct` still exists for existing dial-in projects but is deprecated; the CLI warns on it.)
|
|
150
149
|
|
|
151
150
|
## Sandboxes
|
|
152
151
|
|
|
@@ -172,7 +171,7 @@ two-writer stale/claim smoke test.
|
|
|
172
171
|
|
|
173
172
|
Import from these public paths only:
|
|
174
173
|
|
|
175
|
-
- `@abloatai/ablo` — `Ablo`, errors, typed model clients, claims, `dataSource
|
|
174
|
+
- `@abloatai/ablo` — `Ablo`, errors, typed model clients, claims, and `dataSource`.
|
|
176
175
|
- `@abloatai/ablo/schema` — schema DSL.
|
|
177
176
|
- `@abloatai/ablo/react` — React provider and hooks.
|
|
178
177
|
- `@abloatai/ablo/testing` — test harnesses and mocks.
|
|
@@ -188,7 +187,8 @@ Do not teach `/api`, `/agent`, `/ai-sdk`, `/core`, `/realtime`, or internal subp
|
|
|
188
187
|
|
|
189
188
|
`ablo init` and other prompts need a TTY; an agent/CI run has none and will HANG. Always:
|
|
190
189
|
|
|
191
|
-
- `npx ablo init --yes` (flags: `--framework`, `--auth`, `--storage direct|endpoint
|
|
190
|
+
- `npx ablo init --yes` (flags: `--framework`, `--auth`, `--storage direct|endpoint`, `--no-agent`, `--no-pull`, `--no-install`, `--no-login`). Generates `ablo/schema.ts` + the `Ablo({ schema, apiKey })` client. `--storage endpoint` also scaffolds the `ablo/data-source.ts` fallback endpoint; `--storage direct` (deprecated, CLI warns) wires the legacy `databaseUrl`.
|
|
191
|
+
- `npx ablo connect` connects your database via logical replication — the read path (prints the `wal_level=logical` + publication + `REPLICATION`-role SQL); `npx ablo connect --register` registers it, `npx ablo connect --check` validates.
|
|
192
192
|
- Key: see "Start here" — env → `.env.local` → ask the human to `npx ablo login`; never run `login` yourself, never copy keys by hand (`ablo push` writes `.env.local`).
|
|
193
193
|
- Adopt an existing DB: `npx ablo pull prisma [path]` / `npx ablo pull drizzle <module>`.
|
|
194
194
|
- `npx ablo push` pushes the schema (sandbox) AND writes `ABLO_API_KEY` to `.env.local`; `npx ablo dev --no-watch` is the push-once form of the watcher; `npx ablo logs --no-follow` exits instead of tailing forever; `npx ablo mode sandbox|production` always needs the argument. `npx ablo push`/`status`/`pull`/`check`/`generate` are one-shot.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "The Collaboration Layer For AI Agents",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"test:e2e": "E2E_TEST=true jest --config jest.e2e.config.ts",
|
|
159
159
|
"test:e2e:up": "docker compose -f docker-compose.test.yml up -d --wait",
|
|
160
160
|
"test:e2e:down": "docker compose -f docker-compose.test.yml down -v",
|
|
161
|
-
"test:e2e:run": "
|
|
161
|
+
"test:e2e:run": "node scripts/run-e2e.mjs"
|
|
162
162
|
},
|
|
163
163
|
"publishConfig": {
|
|
164
164
|
"access": "public",
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
"mobx": "^6.13.7",
|
|
208
208
|
"mobx-react-lite": "^4.0.0",
|
|
209
209
|
"uuid": "^11.1.0",
|
|
210
|
-
"zod": "^4.
|
|
210
|
+
"zod": "^4.1.11"
|
|
211
211
|
},
|
|
212
212
|
"devDependencies": {
|
|
213
213
|
"@ai-sdk/provider": "^3.0.0",
|