@abloatai/ablo 0.3.0 → 0.4.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 +43 -5
- package/NOTICE +2 -2
- package/README.md +30 -28
- package/dist/agent/Agent.d.ts +1 -1
- package/dist/agent/Agent.js +1 -1
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +6 -6
- package/dist/agent/types.d.ts +1 -1
- package/dist/ai-sdk/index.d.ts +3 -3
- package/dist/ai-sdk/index.js +3 -3
- package/dist/ai-sdk/intent-broadcast.d.ts +1 -1
- package/dist/ai-sdk/intent-broadcast.js +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/client/Ablo.d.ts +8 -14
- package/dist/client/Ablo.js +32 -1
- package/dist/client/auth.d.ts +3 -3
- package/dist/client/auth.js +5 -5
- package/dist/client/createModelProxy.d.ts +110 -32
- package/dist/client/createModelProxy.js +77 -38
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +9 -9
- package/dist/interfaces/headless.d.ts +1 -1
- package/dist/interfaces/headless.js +2 -2
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +2 -2
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +1 -1
- package/dist/react/ClientSideSuspense.d.ts +1 -1
- package/dist/react/SyncGroupProvider.js +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutate.d.ts +1 -1
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react/useReader.d.ts +1 -1
- package/dist/schema/field.d.ts +1 -1
- package/dist/schema/field.js +1 -1
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +2 -2
- package/dist/schema/model.d.ts +2 -2
- package/dist/schema/model.js +2 -2
- package/dist/schema/queries.d.ts +1 -1
- package/dist/schema/queries.js +1 -1
- package/dist/schema/relation.d.ts +1 -1
- package/dist/schema/relation.js +1 -1
- package/dist/schema/schema.d.ts +1 -1
- package/dist/schema/schema.js +1 -1
- package/dist/source/index.d.ts +22 -28
- package/dist/source/index.js +23 -20
- package/dist/source/pushQueue.d.ts +1 -1
- package/dist/source/pushQueue.js +2 -2
- package/dist/sync/SyncWebSocket.d.ts +14 -0
- package/dist/sync/createIntentStream.js +7 -0
- package/dist/testing/fixtures/models.d.ts +1 -1
- package/dist/testing/fixtures/models.js +1 -1
- package/dist/testing/helpers/react-wrapper.d.ts +2 -2
- package/dist/testing/helpers/react-wrapper.js +2 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/types/streams.d.ts +39 -0
- package/docs/api-keys.md +2 -2
- package/docs/api.md +81 -23
- package/docs/capabilities.md +1 -1
- package/docs/client-behavior.md +7 -7
- package/docs/data-sources.md +52 -18
- package/docs/examples/agent-human.md +3 -3
- package/docs/examples/ai-sdk-tool.md +16 -33
- package/docs/examples/existing-python-backend.md +10 -10
- package/docs/examples/nextjs.md +1 -1
- package/docs/examples/server-agent.md +2 -2
- package/docs/index.md +1 -1
- package/docs/integration-guide.md +15 -14
- package/docs/interaction-model.md +16 -4
- package/docs/mcp.md +1 -1
- package/docs/quickstart.md +23 -21
- package/docs/react.md +3 -3
- package/docs/roadmap.md +1 -1
- package/examples/README.md +3 -3
- package/examples/data-source/README.md +1 -1
- package/examples/data-source/ablo-driver.ts +5 -5
- package/examples/data-source/customer-server.ts +10 -10
- package/examples/data-source/run.ts +9 -11
- package/examples/data-source/schema.ts +1 -1
- package/examples/quickstart.ts +2 -2
- package/llms.txt +8 -8
- package/package.json +1 -1
package/docs/quickstart.md
CHANGED
|
@@ -9,7 +9,7 @@ Ablo-managed state versus a Data Source that calls your existing API service.
|
|
|
9
9
|
## 1. Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install @ablo
|
|
12
|
+
npm install @abloatai/ablo
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## 2. Set a Sandbox Key
|
|
@@ -26,8 +26,8 @@ provider with a scoped capability/session route, not a bundled API key.
|
|
|
26
26
|
## 3. Declare a Schema
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
|
-
import Ablo from '@ablo
|
|
30
|
-
import { defineSchema, model, z } from '@ablo/
|
|
29
|
+
import Ablo from '@abloatai/ablo';
|
|
30
|
+
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
31
31
|
|
|
32
32
|
const schema = defineSchema({
|
|
33
33
|
weatherReports: model({
|
|
@@ -43,8 +43,9 @@ export const ablo = Ablo({
|
|
|
43
43
|
});
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Customer apps should always pass `schema`. Treat it like Prisma's schema file:
|
|
47
|
+
it is the source of truth for typed model resources, realtime subscriptions,
|
|
48
|
+
agent writes, and Data Source requests.
|
|
48
49
|
|
|
49
50
|
## 4. Create and Update
|
|
50
51
|
|
|
@@ -79,33 +80,34 @@ ABLO_API_KEY=sk_test_... npx tsx quickstart.ts
|
|
|
79
80
|
|
|
80
81
|
## 6. AI Activity on Existing State
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
When AI or background work will touch an existing row for more than a quick
|
|
84
|
+
write, coordinate through `ablo.<model>.intent(id)`. It returns a handle
|
|
85
|
+
synchronously — read `.current` to see who's working on the row, `acquire()` to
|
|
86
|
+
claim it, `update()` to write under the claim (which auto-releases).
|
|
86
87
|
|
|
87
88
|
```ts
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
const report = ablo.weatherReports.intent('weather_stockholm');
|
|
90
|
+
|
|
91
|
+
// If another participant holds it, wait for them to finish.
|
|
92
|
+
if (report.current) await report.settled();
|
|
93
|
+
|
|
94
|
+
// Claim it so other participants yield while we work.
|
|
95
|
+
await report.acquire({ action: 'checking_weather', field: 'forecast', ttl: '2m' });
|
|
93
96
|
|
|
94
97
|
// Your existing weather tool or agent call. While this runs, other clients see
|
|
95
98
|
// that weather_stockholm is being checked.
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
+
const row = ablo.weatherReports.retrieve('weather_stockholm');
|
|
100
|
+
const weather = await weatherAgent.getWeather(row.location);
|
|
99
101
|
|
|
100
|
-
await
|
|
102
|
+
await report.update({
|
|
101
103
|
status: 'ready',
|
|
102
104
|
forecast: weather.summary,
|
|
103
105
|
});
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
Ablo does not fetch the weather. It keeps the activity visible
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
Ablo does not fetch the weather. It keeps the activity visible while the work
|
|
109
|
+
runs, rejects `report.update(...)` with `AbloStaleContextError` if the row
|
|
110
|
+
changed under you, and releases the intent automatically once the write lands.
|
|
109
111
|
|
|
110
112
|
## 7. Multiplayer and Busy Work
|
|
111
113
|
|
package/docs/react.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# React
|
|
2
2
|
|
|
3
|
-
The React bindings for `@ablo
|
|
3
|
+
The React bindings for `@abloatai/ablo`. Use them when you want live
|
|
4
4
|
data on the client without writing fetch + WebSocket plumbing yourself.
|
|
5
5
|
|
|
6
6
|
For the full app structure, including server loads, existing backends, and
|
|
@@ -11,7 +11,7 @@ agents, start with [Integration Guide](/docs/integration-guide).
|
|
|
11
11
|
The React bindings ship with the main package — no extra install.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import { useAblo } from '@ablo/
|
|
14
|
+
import { useAblo } from '@abloatai/ablo/react';
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## useAblo — model resource
|
|
@@ -19,7 +19,7 @@ import { useAblo } from '@ablo/sync-engine/react';
|
|
|
19
19
|
```tsx
|
|
20
20
|
'use client';
|
|
21
21
|
|
|
22
|
-
import { useAblo } from '@ablo/
|
|
22
|
+
import { useAblo } from '@abloatai/ablo/react';
|
|
23
23
|
|
|
24
24
|
export function TaskView({ task: serverTask }: { task: { id: string; title: string } }) {
|
|
25
25
|
const task = useAblo((ablo) => ablo.tasks.retrieve(serverTask.id)) ?? serverTask;
|
package/docs/roadmap.md
CHANGED
|
@@ -8,7 +8,7 @@ What is shipped, what is next, and what we will not build.
|
|
|
8
8
|
- **Capability tokens** — Biscuit-signed, scoped, attenuable, hot-revocable.
|
|
9
9
|
- **Audit log** — hash-chained per principal, with `delegationChainRoot`.
|
|
10
10
|
- **MCP transport** — HTTP server at `/api/mcp`.
|
|
11
|
-
- **TypeScript SDK** — `@ablo
|
|
11
|
+
- **TypeScript SDK** — `@abloatai/ablo`, with React bindings.
|
|
12
12
|
- **Dashboard** — keys, audit, metrics, allowed origins.
|
|
13
13
|
|
|
14
14
|
## In flight
|
package/examples/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# @ablo
|
|
1
|
+
# @abloatai/ablo Examples
|
|
2
2
|
|
|
3
3
|
The examples teach the same path as the README and docs: declare a schema,
|
|
4
4
|
create or load typed models, and write through `ablo.<model>`.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
|
-
import Ablo from '@ablo
|
|
8
|
-
import { defineSchema, model, z } from '@ablo/
|
|
7
|
+
import Ablo from '@abloatai/ablo';
|
|
8
|
+
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
9
9
|
|
|
10
10
|
const schema = defineSchema({
|
|
11
11
|
weatherReports: model({
|
|
@@ -22,7 +22,7 @@ npx tsx data-source/run.ts
|
|
|
22
22
|
|
|
23
23
|
No network port, no env vars, no cloud credentials. The orchestrator
|
|
24
24
|
calls the handler in-process. Signer and verifier still exchange
|
|
25
|
-
signed bytes — flip the
|
|
25
|
+
signed bytes — flip the API key and you'll see a 401.
|
|
26
26
|
|
|
27
27
|
## What it proves
|
|
28
28
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* locally without standing up the cloud.
|
|
8
8
|
*
|
|
9
9
|
* In production:
|
|
10
|
-
* - Ablo Cloud holds the
|
|
10
|
+
* - Ablo Cloud holds the API key in its config
|
|
11
11
|
* - It signs each outbound POST with `signAbloSourceRequest`
|
|
12
12
|
* - The customer's `dataSource(...)` handler verifies the signature
|
|
13
13
|
* - The response feeds back into Ablo Cloud's hosted realtime layer
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import {
|
|
20
20
|
signAbloSourceRequest,
|
|
21
21
|
type Ablo,
|
|
22
|
-
} from '@ablo
|
|
22
|
+
} from '@abloatai/ablo';
|
|
23
23
|
|
|
24
24
|
export interface AbloDriverOptions {
|
|
25
25
|
/**
|
|
@@ -27,8 +27,8 @@ export interface AbloDriverOptions {
|
|
|
27
27
|
* the handler directly so there's no http port to manage.
|
|
28
28
|
*/
|
|
29
29
|
readonly handler: (request: Request) => Promise<Response>;
|
|
30
|
-
/** Same
|
|
31
|
-
readonly
|
|
30
|
+
/** Same API key the customer's `dataSource(...)` is configured with. */
|
|
31
|
+
readonly apiKey: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export class AbloDriver {
|
|
@@ -68,7 +68,7 @@ export class AbloDriver {
|
|
|
68
68
|
const body = JSON.stringify(payload);
|
|
69
69
|
const messageId = `msg_${Date.now()}_${this.messageCounter}`;
|
|
70
70
|
const signed = await signAbloSourceRequest({
|
|
71
|
-
|
|
71
|
+
apiKey: this.options.apiKey,
|
|
72
72
|
body,
|
|
73
73
|
messageId,
|
|
74
74
|
});
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* inside a transaction. The shape of the handlers stays identical.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import Ablo, { dataSource } from '@ablo
|
|
18
|
+
import Ablo, { dataSource } from '@abloatai/ablo';
|
|
19
19
|
import { schema } from './schema';
|
|
20
20
|
|
|
21
21
|
type TaskRow = {
|
|
@@ -68,20 +68,20 @@ taskStore.set('task_seed', {
|
|
|
68
68
|
export const handleAbloSource = dataSource({
|
|
69
69
|
schema,
|
|
70
70
|
|
|
71
|
-
// The
|
|
72
|
-
// Wrong
|
|
73
|
-
// function (instead of the env value directly) re-reads the
|
|
74
|
-
// on every request
|
|
71
|
+
// The API key pairs with what Ablo Cloud is configured with.
|
|
72
|
+
// Wrong key -> 401 with `source_signature_invalid`. Passing a
|
|
73
|
+
// function (instead of the env value directly) re-reads the key
|
|
74
|
+
// on every request and is required by the
|
|
75
75
|
// example because `run.ts` configures the env after this module is
|
|
76
76
|
// imported.
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
77
|
+
apiKey: () => {
|
|
78
|
+
const apiKey = process.env.ABLO_API_KEY;
|
|
79
|
+
if (!apiKey) {
|
|
80
80
|
throw new Error(
|
|
81
|
-
'
|
|
81
|
+
'ABLO_API_KEY is not set — refusing to accept unsigned requests',
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
|
-
return
|
|
84
|
+
return apiKey;
|
|
85
85
|
},
|
|
86
86
|
|
|
87
87
|
// `authorize` runs before any handler. Use it to map the signed
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* What this proves:
|
|
10
10
|
*
|
|
11
11
|
* 1. Ablo Cloud's signer + the customer's verifier interop. A wrong
|
|
12
|
-
*
|
|
12
|
+
* API key produces `source_signature_invalid`.
|
|
13
13
|
* 2. `load`, `list`, `commit`, and `events` all flow through the
|
|
14
14
|
* same Fetch-API handler.
|
|
15
15
|
* 3. The customer's "database" (here a Map) holds canonical rows.
|
|
@@ -21,19 +21,17 @@
|
|
|
21
21
|
import { handleAbloSource, _inspectStore } from './customer-server';
|
|
22
22
|
import { AbloDriver } from './ablo-driver';
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
process.env.
|
|
24
|
+
const API_KEY =
|
|
25
|
+
process.env.ABLO_API_KEY ?? 'sk_test_example_key_do_not_use_in_prod';
|
|
26
26
|
|
|
27
|
-
// `dataSource()` reads `options.
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
// and the customer's environment.
|
|
31
|
-
process.env.ABLO_DATA_SOURCE_SIGNING_SECRET = SIGNING_SECRET;
|
|
27
|
+
// `dataSource()` reads `options.apiKey` at request time; we re-export
|
|
28
|
+
// the same value to the driver so signer and verifier agree.
|
|
29
|
+
process.env.ABLO_API_KEY = API_KEY;
|
|
32
30
|
|
|
33
31
|
async function main() {
|
|
34
32
|
const driver = new AbloDriver({
|
|
35
33
|
handler: handleAbloSource,
|
|
36
|
-
|
|
34
|
+
apiKey: API_KEY,
|
|
37
35
|
});
|
|
38
36
|
|
|
39
37
|
log('--- 1. load (existing seeded row) ---');
|
|
@@ -68,10 +66,10 @@ async function main() {
|
|
|
68
66
|
const events = await driver.events();
|
|
69
67
|
log('events:', events);
|
|
70
68
|
|
|
71
|
-
log('\n--- 5. signature failure (wrong
|
|
69
|
+
log('\n--- 5. signature failure (wrong API key) ---');
|
|
72
70
|
const badDriver = new AbloDriver({
|
|
73
71
|
handler: handleAbloSource,
|
|
74
|
-
|
|
72
|
+
apiKey: 'sk_test_wrong_key',
|
|
75
73
|
});
|
|
76
74
|
try {
|
|
77
75
|
await badDriver.load('tasks', 'task_seed');
|
package/examples/quickstart.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* ABLO_API_KEY=sk_test_... npx tsx quickstart.ts
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import Ablo from '@ablo
|
|
10
|
-
import { defineSchema, model, z } from '@ablo/
|
|
9
|
+
import Ablo from '@abloatai/ablo';
|
|
10
|
+
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
11
11
|
|
|
12
12
|
const schema = defineSchema({
|
|
13
13
|
weatherReports: model({
|
package/llms.txt
CHANGED
|
@@ -7,8 +7,8 @@ Use AI SDK for the agent loop. Use Ablo when agent reads and writes must persist
|
|
|
7
7
|
## Use this API
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import Ablo from '@ablo
|
|
11
|
-
import { defineSchema, model, z } from '@ablo/
|
|
10
|
+
import Ablo from '@abloatai/ablo';
|
|
11
|
+
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
12
12
|
|
|
13
13
|
const schema = defineSchema({
|
|
14
14
|
tasks: model({
|
|
@@ -103,12 +103,12 @@ Teach schema as model fields and relations first. Advanced schema helpers such a
|
|
|
103
103
|
|
|
104
104
|
Do not add `databaseURL` to `Ablo(...)`. Application and agent code use `ABLO_API_KEY`.
|
|
105
105
|
|
|
106
|
-
Every schema model has a backing store. By default, Ablo stores rows for declared models, so `ablo.<model>.create/update/delete` write to Ablo-managed state. If the customer database is canonical,
|
|
106
|
+
Every schema model has a backing store. By default, Ablo stores rows for declared models, so `ablo.<model>.create/update/delete` write to Ablo-managed state. If the customer database is canonical, expose a Data Source endpoint and pass `apiKey: process.env.ABLO_API_KEY` to `dataSource({ schema, apiKey, load, list, commit, events })`. Customer-owned app database credentials stay private.
|
|
107
107
|
|
|
108
108
|
Use `dataSource` from the root import:
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
|
-
import { dataSource } from '@ablo
|
|
111
|
+
import { dataSource } from '@abloatai/ablo';
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
## Sandboxes
|
|
@@ -133,10 +133,10 @@ two-writer stale/intent smoke test.
|
|
|
133
133
|
|
|
134
134
|
Import from these public paths only:
|
|
135
135
|
|
|
136
|
-
- `@ablo
|
|
137
|
-
- `@ablo/
|
|
138
|
-
- `@ablo/
|
|
139
|
-
- `@ablo/
|
|
136
|
+
- `@abloatai/ablo` — `Ablo`, errors, typed model resources, intents, `dataSource`, and advanced protocol resources.
|
|
137
|
+
- `@abloatai/ablo/schema` — schema DSL.
|
|
138
|
+
- `@abloatai/ablo/react` — React provider and hooks.
|
|
139
|
+
- `@abloatai/ablo/testing` — test harnesses and mocks.
|
|
140
140
|
|
|
141
141
|
Do not teach `/api`, `/agent`, `/ai-sdk`, `/core`, `/realtime`, `/source`, or internal subpaths.
|
|
142
142
|
|