@repogo/plugins 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +330 -29
- package/dist/buckets.d.ts +88 -0
- package/dist/collections.d.ts +4 -7
- package/dist/context.d.ts +64 -1
- package/dist/database.d.ts +11 -0
- package/dist/define.d.ts +28 -4
- package/dist/duration.d.ts +3 -0
- package/dist/index.d.ts +6 -2
- package/dist/kv.d.ts +32 -0
- package/dist/store.d.ts +32 -0
- package/dist/streams.d.ts +79 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
# @repogo/plugins
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Plugins are small apps that run on your environment, the machine the RepoGo
|
|
4
|
+
host runs on. A plugin keeps its own data, does work with your tools and APIs,
|
|
5
|
+
and is reached two ways: agents call its methods as tools, and cards on your
|
|
6
|
+
phone draw its data live and run its methods when you tap.
|
|
7
|
+
|
|
8
|
+
- [Quick start](#quick-start)
|
|
9
|
+
- [Where plugins live](#where-plugins-live)
|
|
10
|
+
- [Methods](#methods): queries, mutations, streams
|
|
11
|
+
- [Data](#data): namespaces, collections, kv, streams, buckets
|
|
12
|
+
- [Crons](#crons)
|
|
13
|
+
- [The context](#the-context)
|
|
14
|
+
- [Events](#events)
|
|
15
|
+
- [How cards and agents reach a plugin](#how-cards-and-agents-reach-a-plugin)
|
|
16
|
+
- [Errors and confirmations](#errors-and-confirmations)
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
6
19
|
|
|
7
20
|
```ts
|
|
8
21
|
// .repogo/plugins/notes/app.ts
|
|
@@ -10,21 +23,75 @@ import { definePlugin, method, z } from "@repogo/plugins";
|
|
|
10
23
|
|
|
11
24
|
export default definePlugin({
|
|
12
25
|
description: "Notes for this project",
|
|
13
|
-
|
|
14
|
-
collections: { notes: z.object({ text: z.string() }) },
|
|
26
|
+
namespace: "myproject.notes",
|
|
27
|
+
collections: { notes: z.object({ text: z.string(), done: z.boolean().default(false) }) },
|
|
15
28
|
methods: {
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
open: method(z.object({}), async (_, ctx) => ctx.collections.notes.list({ where: { done: false } }), {
|
|
30
|
+
kind: "query",
|
|
31
|
+
description: "Notes not done yet, newest first",
|
|
32
|
+
}),
|
|
18
33
|
clear: method(z.object({}), async (_, ctx) => {
|
|
19
|
-
for (const note of await ctx.
|
|
34
|
+
for (const note of await ctx.collections.notes.list({ limit: 500 })) await ctx.collections.notes.delete(note.id);
|
|
35
|
+
return null;
|
|
20
36
|
}, { confirm: "Delete every note?" }),
|
|
21
37
|
},
|
|
22
38
|
});
|
|
23
39
|
```
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
That's a whole plugin. The host loads it on its next look at the folder (within
|
|
42
|
+
a minute; at once when something calls it), and it gets:
|
|
43
|
+
|
|
44
|
+
- `notes.open` and `notes.clear`, the methods it wrote;
|
|
45
|
+
- `notes.notes.list`, `.get`, `.create`, `.update` and `.delete`, generated for its collection;
|
|
46
|
+
- a `data.db` in `~/.repogo/plugin-data/myproject.notes/`.
|
|
47
|
+
|
|
48
|
+
Add the package to `.repogo/plugins/package.json` (`bun add @repogo/plugins`) for
|
|
49
|
+
types and editor help. The host runs plugins with its own copy of this package,
|
|
50
|
+
the version it shipped with, so a plugin always matches its host. Plugins run on
|
|
51
|
+
[Bun](https://bun.sh); install it on the environment.
|
|
52
|
+
|
|
53
|
+
## Where plugins live
|
|
54
|
+
|
|
55
|
+
| Folder | For | `ctx.root` |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `<project>/.repogo/plugins/<name>/app.ts` | One project | The project's checkout |
|
|
58
|
+
| `~/.repogo/plugins/<name>/app.ts` | Every project (Vercel, Gmail, builds) | The project that called it |
|
|
59
|
+
|
|
60
|
+
A plugin's name is its folder's: lowercase letters, digits and hyphens. A
|
|
61
|
+
project plugin hides an environment one of the same name. Each folder's plugins
|
|
62
|
+
run together in one Worker, so a plugin's blocking code stalls the others in its
|
|
63
|
+
folder: use async APIs.
|
|
64
|
+
|
|
65
|
+
Pages beside an environment plugin (`pages/*.md`) open from any project as
|
|
66
|
+
`~/.repogo/plugins/<name>/pages/<page>.md`, and `~/.repogo/plugins/tab.json`
|
|
67
|
+
names the tab of a project without its own.
|
|
68
|
+
|
|
69
|
+
## Methods
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
method(input, run, { kind?, description?, output?, confirm? })
|
|
73
|
+
stream(input, async function* run, { description? })
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`input` is a zod schema; the call's input is checked against it and defaults
|
|
77
|
+
filled in before `run` sees it. `run(input, ctx)` returns the result: anything
|
|
78
|
+
JSON can carry.
|
|
79
|
+
|
|
80
|
+
| Kind | Does | Who runs it |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `query` | Only reads; writing throws | Cards on their own, and again whenever data it read changes |
|
|
83
|
+
| `mutation` (the default) | Reads and writes | Only when someone acts: a tap, an agent, a cron, other code |
|
|
84
|
+
| `stream` | Only reads; yields items as they happen | A card while it shows it |
|
|
85
|
+
|
|
86
|
+
Mark every method that only reads as `kind: "query"`: that's what lets a card
|
|
87
|
+
show it and keep it current. Give each a `description`; agents read it to choose
|
|
88
|
+
a method. `output`, a zod schema of the result, tells agents and cards its
|
|
89
|
+
field names.
|
|
90
|
+
|
|
91
|
+
A **stream** sends each `yield` to the card as it happens, for as long as the
|
|
92
|
+
card shows it. `ctx.signal` aborts when the card goes, so hand it to what you
|
|
93
|
+
start. Give items an `id`. An agent calling a stream gets what arrives within
|
|
94
|
+
5 seconds, at most 200 items.
|
|
28
95
|
|
|
29
96
|
```ts
|
|
30
97
|
tail: stream(z.object({ file: z.string() }), async function* ({ file }, ctx) {
|
|
@@ -38,24 +105,258 @@ tail: stream(z.object({ file: z.string() }), async function* ({ file }, ctx) {
|
|
|
38
105
|
}),
|
|
39
106
|
```
|
|
40
107
|
|
|
41
|
-
|
|
42
|
-
on the environment; left out, it is the plugin's folder name. Name a project's own
|
|
43
|
-
data `<project>.<name>`. A bare name (`todos`) is shared by every plugin that
|
|
44
|
-
names it, in any project, so they must agree on their collections' schemas.
|
|
45
|
-
Data stays in place when the project moves. A plugin without collections has no file.
|
|
108
|
+
## Data
|
|
46
109
|
|
|
47
|
-
A plugin
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
110
|
+
A plugin declares its data in four kinds, each with a schema, and gets typed
|
|
111
|
+
access in `ctx` and generated methods for agents and cards:
|
|
112
|
+
|
|
113
|
+
| Kind | Holds | `ctx` | On disk |
|
|
114
|
+
| --- | --- | --- | --- |
|
|
115
|
+
| `collections` | Records: tasks, notes, settings rows | `ctx.collections.<name>` | Rows in `data.db` |
|
|
116
|
+
| `kv` | Keys to values, which may expire | `ctx.kv.<name>` | Rows in `data.db` |
|
|
117
|
+
| `streams` | Append-only lines per key: a run's log, a job's output | `ctx.streams.<name>` | `streams/<name>/<key>/*.jsonl` |
|
|
118
|
+
| `buckets` | Files, each with its meta | `ctx.buckets.<name>` | `buckets/<name>/<id>/` |
|
|
119
|
+
|
|
120
|
+
Every write is checked against its schema, and every read and write is tracked,
|
|
121
|
+
so a card showing a query refreshes when a write touches what it read: in this
|
|
122
|
+
plugin, another plugin, or another project.
|
|
123
|
+
|
|
124
|
+
### Namespaces
|
|
125
|
+
|
|
126
|
+
`namespace` names where a plugin's data lives: the folder
|
|
127
|
+
`~/.repogo/plugin-data/<namespace>/`, holding `data.db` and its streams' and
|
|
128
|
+
buckets' files. Left out, it's the plugin's name. Plugins naming the same
|
|
129
|
+
namespace share its data, in any project, so they must declare the same schemas.
|
|
130
|
+
|
|
131
|
+
By convention a project's own data is `<project>.<name>` (`myproject.notes`),
|
|
132
|
+
and data meant for every project a bare name (`todos`). That's only a
|
|
133
|
+
convention: the same namespace is the same data. Data stays put when a project
|
|
134
|
+
moves. A plugin with no data has no folder.
|
|
135
|
+
|
|
136
|
+
### Collections
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
collections: { tasks: z.object({ title: z.string(), status: z.enum(["open", "done"]).default("open") }) }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const task = await ctx.collections.tasks.create({ title: "Ship it" }); // { id, title, status, createdAt, updatedAt }
|
|
144
|
+
await ctx.collections.tasks.get(task.id); // or null
|
|
145
|
+
await ctx.collections.tasks.update(task.id, { status: "done" }); // only the fields given change
|
|
146
|
+
await ctx.collections.tasks.list({ where: { status: "open" }, orderBy: "updatedAt", order: "desc", limit: 50 });
|
|
147
|
+
await ctx.collections.tasks.delete(task.id);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`list` is newest first by default, at most 100 unless `limit` says (up to 500).
|
|
151
|
+
`where` matches top-level fields exactly; `null` matches a missing one.
|
|
152
|
+
|
|
153
|
+
### Kv
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
kv: { state: z.object({ step: z.string() }) }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
await ctx.kv.state.set("run-42", { step: "build" }, { ttl: "1h" }); // ttl: "30s", "5m", "1h", "7d" or ms
|
|
161
|
+
await ctx.kv.state.get("run-42"); // { step: "build" }, or null
|
|
162
|
+
await ctx.kv.state.entry("run-42"); // { key, value, expiresAt, createdAt, updatedAt }
|
|
163
|
+
await ctx.kv.state.list({ prefix: "run-", limit: 100 }); // entries in key order
|
|
164
|
+
await ctx.kv.state.delete("run-42");
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The value's schema may be any zod type, not only an object. A key past its
|
|
168
|
+
`ttl` reads as missing at once; a sweep every few seconds deletes it and
|
|
169
|
+
refreshes cards. Setting a key again without a `ttl` keeps it until deleted.
|
|
170
|
+
|
|
171
|
+
### Streams
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
streams: {
|
|
175
|
+
runs: z.object({ level: z.string(), text: z.string() }),
|
|
176
|
+
audit: { item: z.object({ text: z.string() }), retain: { age: "90d", bytes: 500_000_000 } },
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A stream is append-only JSON lines, split by key: one key per workflow run, job
|
|
181
|
+
or day. Each line on disk is `{ offset, at, data }`, in
|
|
182
|
+
`streams/<name>/<key>/<first offset>.jsonl`, a new file each megabyte, so you
|
|
183
|
+
can `tail` and grep them.
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
await ctx.streams.runs.append("run-42", { level: "info", text: "built" }); // { id: "run-42:0", key, offset, at, data }
|
|
187
|
+
await ctx.streams.runs.read("run-42", { from: 0, limit: 100 });
|
|
188
|
+
for await (const line of ctx.streams.runs.follow("run-42", { signal: ctx.signal })) { /* … */ }
|
|
189
|
+
await ctx.streams.runs.end("run-42"); // followers stop; appends fail
|
|
190
|
+
await ctx.streams.runs.keys(); // [{ key, lines, bytes, endedAt, updatedAt }], newest first
|
|
191
|
+
await ctx.streams.runs.delete("run-42");
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`follow` replays the last 100 lines (or from `from`, or the last `last`), then
|
|
195
|
+
yields each new one, until the key ends or the signal aborts. Keys are letters,
|
|
196
|
+
digits, dots, dashes and underscores.
|
|
197
|
+
|
|
198
|
+
A key is kept 7 days after its last line or its end, and past 50 MB its oldest
|
|
199
|
+
files go. `retain` sets `{ age?, bytes? }` per stream, or `"forever"`. Offsets
|
|
200
|
+
are never reused: after a crash between writing a line and recording it, the
|
|
201
|
+
line is cut off before the next append.
|
|
202
|
+
|
|
203
|
+
### Buckets
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
buckets: { photos: z.object({ caption: z.string().optional(), tags: z.array(z.string()).optional() }) }
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
A bucket keeps files, one folder per file named by its id, holding the file
|
|
210
|
+
under its own name and its `meta.json`:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
buckets/photos/
|
|
214
|
+
01a14cd8-ad7a-7000-8773-aa8bff079ece/
|
|
215
|
+
beach.jpg
|
|
216
|
+
meta.json {"caption": "Sunset", "folder": "2026"}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The folders are the truth. Anyone may add a file by making a folder with the
|
|
220
|
+
file in it, and change its meta by writing `meta.json`: an agent with its file
|
|
221
|
+
tools, a script, Finder. The host indexes the bucket for `list` and `search` when
|
|
222
|
+
the folder changes, and rescans it each minute. Meta may be missing or `{}`;
|
|
223
|
+
its one reserved field, `folder`, places the file in a folder of the bucket's
|
|
224
|
+
own. Each file reads with a `status`:
|
|
225
|
+
|
|
226
|
+
| `status` | Meta | `problem` |
|
|
227
|
+
| --- | --- | --- |
|
|
228
|
+
| `ok` | Matches the schema | null |
|
|
229
|
+
| `empty` | None, or `{}` | null |
|
|
230
|
+
| `invalid` | Fails the schema; kept as written | Why |
|
|
231
|
+
| `malformed` | Not a JSON object, or the folder doesn't hold exactly one file | Why |
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
const file = await ctx.buckets.photos.put({ name: "beach.jpg", body: bytes, folder: "2026", meta: { caption: "Sunset" } });
|
|
235
|
+
// { id, name, folder, path, size, type, meta, status, problem, revision, createdAt, updatedAt }
|
|
236
|
+
await ctx.buckets.photos.get(file.id);
|
|
237
|
+
await ctx.buckets.photos.list({ folder: "2026", status: "ok", limit: 100 }); // newest first
|
|
238
|
+
await ctx.buckets.photos.search("sunset trip"); // name, folder and meta; words as prefixes
|
|
239
|
+
await ctx.buckets.photos.update(file.id, { tags: ["trip"], caption: null }, { revision: file.revision });
|
|
240
|
+
await ctx.buckets.photos.move(file.id, { name: "sunset.jpg", folder: "best" });
|
|
241
|
+
const text = await (await ctx.buckets.photos.file(file.id)).text(); // a BunFile
|
|
242
|
+
await ctx.buckets.photos.delete(file.id);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`body` is a string, bytes or a `Blob` (`Bun.file(path)` copies a file). `path`
|
|
246
|
+
is the file on disk. `update` merges into the meta, `null` removing a field, and
|
|
247
|
+
checks it against the schema; with `revision`, it fails if the file changed
|
|
248
|
+
since you read it. Writes through `ctx` are checked; files written by hand are
|
|
249
|
+
indexed with their `status` instead.
|
|
250
|
+
|
|
251
|
+
### Generated methods
|
|
252
|
+
|
|
253
|
+
Every collection, kv, stream and bucket gets methods named
|
|
254
|
+
`<plugin>.<name>.<method>`, so a plugin that only declares data can be read and
|
|
255
|
+
changed by agents and cards with no code of its own:
|
|
256
|
+
|
|
257
|
+
| Kind | Queries | Mutations | Stream |
|
|
258
|
+
| --- | --- | --- | --- |
|
|
259
|
+
| Collection | `list`, `get` | `create`, `update`, `delete` (asks) | |
|
|
260
|
+
| Kv | `get`, `list` | `set`, `delete` (asks) | |
|
|
261
|
+
| Stream | `read`, `keys` | `append`, `end`, `delete` (asks) | `tail` |
|
|
262
|
+
| Bucket | `list`, `search`, `get` | `put`, `update`, `move`, `delete` (asks) | |
|
|
263
|
+
|
|
264
|
+
A bucket's `put` takes `text`, or `from`, an absolute path on the environment to
|
|
265
|
+
copy: bytes never travel in a call. A method of your own with the same name as a
|
|
266
|
+
generated one fails the plugin's load.
|
|
267
|
+
|
|
268
|
+
## Crons
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
export default definePlugin({
|
|
272
|
+
methods: { watch: method(z.object({}), async (_, ctx) => { /* … */ }) },
|
|
273
|
+
crons: [
|
|
274
|
+
{ method: "watch", schedule: "*/5 * * * *" },
|
|
275
|
+
{ method: "digest", schedule: "0 9 * * 1-5", timezone: "America/New_York" },
|
|
276
|
+
],
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The host calls the method with `{}` when it comes due, as a tap would, whether
|
|
281
|
+
or not anyone is looking. `schedule` is five fields (minute hour day-of-month
|
|
282
|
+
month day-of-week), or six with seconds first (`*/5 * * * * *`); each is `*`, a
|
|
283
|
+
number, a range, a list or a step. `timezone` defaults to the environment's.
|
|
284
|
+
|
|
285
|
+
A job still running isn't started again, a time the environment slept through
|
|
286
|
+
is skipped, and nothing is retried. A cron naming a missing method, a stream, a
|
|
287
|
+
method that needs input or asks first, or a schedule never due fails the
|
|
288
|
+
plugin's load. The manifest shows each cron's `nextRunAt`, `lastRunAt`,
|
|
289
|
+
`lastError` and `runs`.
|
|
290
|
+
|
|
291
|
+
## The context
|
|
292
|
+
|
|
293
|
+
Every method, handler and cron gets `ctx`:
|
|
294
|
+
|
|
295
|
+
| Field | What |
|
|
296
|
+
| --- | --- |
|
|
297
|
+
| `plugin` | The plugin's name |
|
|
298
|
+
| `root` | The calling project's checkout |
|
|
299
|
+
| `collections`, `kv`, `streams`, `buckets` | The plugin's data |
|
|
300
|
+
| `call(method, input)` | Runs a method of a plugin in the same folder; `"open"` is this plugin's own |
|
|
301
|
+
| `plugins()` | The plugins in the same folder, with their methods |
|
|
302
|
+
| `host(method, params)` | Runs one of the host's methods that agents and cards may call (`git.status`, `chats.list`, `actions.run`, …); others fail with `FORBIDDEN` |
|
|
303
|
+
| `notify({ title, body, page?, collapse? })` | Alerts every paired phone; a tap opens `page`, and alerts sharing `collapse` replace each other |
|
|
304
|
+
| `emit(event, payload)` | Sends `<plugin>.<event>` to handlers |
|
|
305
|
+
| `signal` | Aborts when a stream's card goes |
|
|
306
|
+
| `log(...)` | Writes to the host's plugin log |
|
|
307
|
+
|
|
308
|
+
A query doesn't re-run when what a `ctx.host` method read changes.
|
|
309
|
+
|
|
310
|
+
## Events
|
|
311
|
+
|
|
312
|
+
Handlers in `on` run after the events they name:
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
on: {
|
|
316
|
+
"photos.created": async (file, ctx) => { /* caption it */ },
|
|
317
|
+
"tasks.updated": async ({ before, after }, ctx) => { /* … */ },
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
| Source | Events |
|
|
322
|
+
| --- | --- |
|
|
323
|
+
| Collection | `<name>.created` (the record), `<name>.updated` (`{ before, after }`), `<name>.deleted` (the record) |
|
|
324
|
+
| Kv | `<name>.created`, `<name>.updated`, `<name>.deleted` (the entry) |
|
|
325
|
+
| Bucket | `<name>.created`, `<name>.changed` (the file's bytes), `<name>.metaChanged`, `<name>.deleted` (the file) |
|
|
326
|
+
| Any method | `<method>` (`{ input, result }`) |
|
|
327
|
+
|
|
328
|
+
Changes to a bucket's folder made by hand emit too, once the host sees them.
|
|
329
|
+
Events run in memory: none is retried, and a handler that throws is logged.
|
|
330
|
+
|
|
331
|
+
## How cards and agents reach a plugin
|
|
332
|
+
|
|
333
|
+
**Agents** use two tools: `plugins` lists every plugin with its methods, input
|
|
334
|
+
schemas, crons and `storage` (each bucket's and stream's folder), and
|
|
335
|
+
`plugin_call` runs one. Call a new method once before a card names it.
|
|
336
|
+
|
|
337
|
+
**Cards** name methods as `plugin:<plugin>.<method>`. A `Data` element runs a
|
|
338
|
+
query, or follows a stream, and redraws when its data changes; a `Button` or
|
|
339
|
+
`Item` action runs a mutation:
|
|
340
|
+
|
|
341
|
+
```xml
|
|
342
|
+
<Data name="photos" method="plugin:gallery.photos.search">
|
|
343
|
+
<Param name="query" value="{{q}}"/>
|
|
344
|
+
<Each in="photos" as="p"><Item label="{{p.name}}" subtitle="{{p.meta.caption}}"/></Each>
|
|
345
|
+
</Data>
|
|
346
|
+
<Button label="Mark done" action="plugin:notes.notes.update">
|
|
347
|
+
<Param name="id" value="{{note.id}}"/>
|
|
348
|
+
<Param name="done" value="true" type="boolean"/>
|
|
349
|
+
</Button>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Errors and confirmations
|
|
53
353
|
|
|
54
|
-
|
|
55
|
-
`
|
|
56
|
-
`
|
|
354
|
+
Throw `new MethodError(code, message)` for a failure the caller should read:
|
|
355
|
+
`VALIDATION`, `NOT_FOUND`, `FORBIDDEN` or `INTERNAL`. Input that fails its
|
|
356
|
+
schema answers `VALIDATION` with a message per field. Anything else thrown is
|
|
357
|
+
`INTERNAL` with its message.
|
|
57
358
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
359
|
+
A method's `confirm` is asked before a card's tap runs it. An agent's
|
|
360
|
+
`plugin_call` gets it back as a `CONFIRM` error until it calls again with
|
|
361
|
+
`confirmed: true`, once the user agrees. Only `ctx.call` from plugin code skips
|
|
362
|
+
it. Give one to anything that can't be undone.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { Store, type StoreOptions } from "./store";
|
|
3
|
+
/**
|
|
4
|
+
* How a file's meta reads: `ok` matches the bucket's schema, `empty` has none,
|
|
5
|
+
* `invalid` fails the schema, and `malformed` isn't JSON or its folder doesn't
|
|
6
|
+
* hold exactly one file. `problem` says why for the last two.
|
|
7
|
+
*/
|
|
8
|
+
export type FileStatus = "ok" | "empty" | "invalid" | "malformed";
|
|
9
|
+
/** One file in a bucket. `path` is the file on disk; `meta` is its `meta.json` without `folder`. */
|
|
10
|
+
export type BucketFile<T> = {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
folder: string;
|
|
14
|
+
path: string;
|
|
15
|
+
size: number;
|
|
16
|
+
type: string;
|
|
17
|
+
meta: Partial<T>;
|
|
18
|
+
status: FileStatus;
|
|
19
|
+
problem: string | null;
|
|
20
|
+
revision: number;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
updatedAt: string;
|
|
23
|
+
};
|
|
24
|
+
/** What `put` writes: the file's name, its bytes, and optionally where it sits and its meta. */
|
|
25
|
+
export type PutFile<T> = {
|
|
26
|
+
name: string;
|
|
27
|
+
body: string | Uint8Array | ArrayBuffer | Blob;
|
|
28
|
+
folder?: string;
|
|
29
|
+
meta?: T;
|
|
30
|
+
};
|
|
31
|
+
export declare class Bucket<S extends z.ZodObject> extends Store {
|
|
32
|
+
private schema;
|
|
33
|
+
private table;
|
|
34
|
+
private searchTable;
|
|
35
|
+
private folder;
|
|
36
|
+
private watcher;
|
|
37
|
+
private settling;
|
|
38
|
+
private scanning;
|
|
39
|
+
private lastScan;
|
|
40
|
+
constructor(options: StoreOptions, name: string, schema: S);
|
|
41
|
+
/** The bucket's folder, for agents to add and read files. */
|
|
42
|
+
get path(): string;
|
|
43
|
+
setup(): Promise<void>;
|
|
44
|
+
put(file: PutFile<z.input<S>>): Promise<BucketFile<z.infer<S>>>;
|
|
45
|
+
get(id: string): Promise<BucketFile<z.infer<S>> | null>;
|
|
46
|
+
/** The file's bytes, to read as text, JSON, a stream or an ArrayBuffer. */
|
|
47
|
+
file(id: string): Promise<Bun.BunFile>;
|
|
48
|
+
/** Newest first; `folder` keeps one folder's files, `status` one status's. */
|
|
49
|
+
list(options?: {
|
|
50
|
+
folder?: string;
|
|
51
|
+
status?: FileStatus;
|
|
52
|
+
limit?: number;
|
|
53
|
+
}): Promise<BucketFile<z.infer<S>>[]>;
|
|
54
|
+
/** Files whose name, folder or meta text has every word of `query`, each as a prefix, best first. */
|
|
55
|
+
search(query: string, options?: {
|
|
56
|
+
limit?: number;
|
|
57
|
+
}): Promise<BucketFile<z.infer<S>>[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Merges `meta` into the file's meta.json (`null` removes a field), checked
|
|
60
|
+
* against the schema. With `revision`, fails if the file changed since it was read.
|
|
61
|
+
*/
|
|
62
|
+
update(id: string, meta: Partial<{
|
|
63
|
+
[K in keyof z.input<S>]: z.input<S>[K] | null;
|
|
64
|
+
}>, options?: {
|
|
65
|
+
revision?: number;
|
|
66
|
+
}): Promise<BucketFile<z.core.output<S>>>;
|
|
67
|
+
/** Renames the file, or moves it to another folder; its id stays. */
|
|
68
|
+
move(id: string, to: {
|
|
69
|
+
name?: string;
|
|
70
|
+
folder?: string;
|
|
71
|
+
}, options?: {
|
|
72
|
+
revision?: number;
|
|
73
|
+
}): Promise<BucketFile<z.core.output<S>>>;
|
|
74
|
+
delete(id: string): Promise<void>;
|
|
75
|
+
tick(now: number): Promise<void>;
|
|
76
|
+
close(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Brings the table in line with the folders, one scan at a time. A folder
|
|
79
|
+
* whose prints match its row is skipped; rows go only once the listing worked.
|
|
80
|
+
*/
|
|
81
|
+
scan(): Promise<void>;
|
|
82
|
+
private scanNow;
|
|
83
|
+
private reindex;
|
|
84
|
+
/** One file's folder as a row would hold it, or null when it's gone. */
|
|
85
|
+
private read;
|
|
86
|
+
private rewrite;
|
|
87
|
+
private toFile;
|
|
88
|
+
}
|
package/dist/collections.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SQL } from "bun";
|
|
2
1
|
import type { z } from "zod";
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import type { EventBus } from "./events";
|
|
4
4
|
export type Record_<T> = T & {
|
|
5
5
|
id: string;
|
|
@@ -13,19 +13,16 @@ export type ListOptions = {
|
|
|
13
13
|
limit?: number;
|
|
14
14
|
};
|
|
15
15
|
export declare class Collection<S extends z.ZodObject> {
|
|
16
|
-
private
|
|
16
|
+
private database;
|
|
17
17
|
private events;
|
|
18
18
|
private plugin;
|
|
19
19
|
private name;
|
|
20
20
|
private schema;
|
|
21
21
|
private table;
|
|
22
22
|
readonly tag: string;
|
|
23
|
-
|
|
24
|
-
private writes;
|
|
25
|
-
constructor(sql: SQL, events: EventBus, plugin: string, db: string, name: string, schema: S);
|
|
23
|
+
constructor(database: Database, events: EventBus, plugin: string, namespace: string, name: string, schema: S);
|
|
26
24
|
setup(): Promise<void>;
|
|
27
|
-
private
|
|
28
|
-
private transaction;
|
|
25
|
+
private get sql();
|
|
29
26
|
create(input: z.input<S>): Promise<Record_<z.infer<S>>>;
|
|
30
27
|
get(id: string): Promise<Record_<z.infer<S>> | null>;
|
|
31
28
|
update(id: string, patch: Partial<z.input<S>>): Promise<Record_<z.infer<S>>>;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { Bucket } from "./buckets";
|
|
1
2
|
import type { Collection } from "./collections";
|
|
3
|
+
import type { Kv } from "./kv";
|
|
4
|
+
import type { Stream } from "./streams";
|
|
5
|
+
import type { MethodKind } from "./define";
|
|
2
6
|
/** An alert for the user's phones: what it says, and the page a tap opens. */
|
|
3
7
|
export type Notice = {
|
|
4
8
|
title: string;
|
|
@@ -8,13 +12,72 @@ export type Notice = {
|
|
|
8
12
|
/** Alerts sharing a collapse id replace one another; defaults to the plugin and title. */
|
|
9
13
|
collapse?: string;
|
|
10
14
|
};
|
|
15
|
+
/** One method as `ctx.plugins()` and the agent's `plugins` tool list it. */
|
|
16
|
+
export type ManifestMethod = {
|
|
17
|
+
/** `<plugin>.<method>`, the name `ctx.call` takes. */
|
|
18
|
+
name: string;
|
|
19
|
+
kind: MethodKind;
|
|
20
|
+
description: string | null;
|
|
21
|
+
/** What a card asks before running it; null runs on tap. */
|
|
22
|
+
confirm: string | null;
|
|
23
|
+
/** JSON Schema of the input, and of the output when the method declares one. */
|
|
24
|
+
input: unknown;
|
|
25
|
+
output: unknown;
|
|
26
|
+
};
|
|
27
|
+
/** One cron and how it's going: times in ms, and what its last run threw. */
|
|
28
|
+
export type ManifestCron = {
|
|
29
|
+
method: string;
|
|
30
|
+
schedule: string;
|
|
31
|
+
timezone: string;
|
|
32
|
+
nextRunAt: number | null;
|
|
33
|
+
lastRunAt: number | null;
|
|
34
|
+
lastError: string | null;
|
|
35
|
+
running: boolean;
|
|
36
|
+
runs: number;
|
|
37
|
+
};
|
|
38
|
+
/** Where a plugin's buckets and streams sit on disk, for an agent's own file tools, and its kv names. */
|
|
39
|
+
export type ManifestStorage = {
|
|
40
|
+
buckets: {
|
|
41
|
+
name: string;
|
|
42
|
+
path: string;
|
|
43
|
+
}[];
|
|
44
|
+
kv: {
|
|
45
|
+
name: string;
|
|
46
|
+
}[];
|
|
47
|
+
streams: {
|
|
48
|
+
name: string;
|
|
49
|
+
path: string;
|
|
50
|
+
}[];
|
|
51
|
+
};
|
|
52
|
+
export type Manifest = {
|
|
53
|
+
plugins: {
|
|
54
|
+
name: string;
|
|
55
|
+
description: string | null;
|
|
56
|
+
methods: ManifestMethod[];
|
|
57
|
+
crons: ManifestCron[];
|
|
58
|
+
storage: ManifestStorage;
|
|
59
|
+
}[];
|
|
60
|
+
};
|
|
11
61
|
export type Context = {
|
|
12
62
|
plugin: string;
|
|
13
63
|
/** The project's checkout, for a plugin that reads the project's files. */
|
|
14
64
|
root: string;
|
|
15
|
-
|
|
65
|
+
collections: Record<string, Collection<any>>;
|
|
66
|
+
buckets: Record<string, Bucket<any>>;
|
|
67
|
+
kv: Record<string, Kv<any>>;
|
|
68
|
+
streams: Record<string, Stream<any>>;
|
|
16
69
|
call: (method: string, input?: unknown) => Promise<unknown>;
|
|
17
70
|
emit: (name: string, payload: unknown) => Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Runs one of the host's methods, the ones agents and cards can call:
|
|
73
|
+
* `ctx.host("git.status", { path: ctx.root })`. Its answer doesn't re-run a query.
|
|
74
|
+
*/
|
|
75
|
+
host: (method: string, params?: unknown) => Promise<unknown>;
|
|
76
|
+
/**
|
|
77
|
+
* The plugins beside this one, in the same folder (the project's, or the
|
|
78
|
+
* environment's), with their methods: what `call` can reach.
|
|
79
|
+
*/
|
|
80
|
+
plugins: () => Manifest["plugins"];
|
|
18
81
|
/** Aborts when a stream's last watcher leaves; a query or mutation's never does. */
|
|
19
82
|
signal: AbortSignal;
|
|
20
83
|
/** Sends an alert to every phone paired with this environment. */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SQL } from "bun";
|
|
2
|
+
export declare class Database {
|
|
3
|
+
readonly sql: SQL;
|
|
4
|
+
private writes;
|
|
5
|
+
private constructor();
|
|
6
|
+
static open(path: string): Promise<Database>;
|
|
7
|
+
/** Runs `work` alone in a transaction; it commits when `work` returns and rolls back when it throws. */
|
|
8
|
+
transaction<T>(work: () => Promise<T>): Promise<T>;
|
|
9
|
+
close(): void;
|
|
10
|
+
private run;
|
|
11
|
+
}
|
package/dist/define.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import type { Context } from "./context";
|
|
3
|
+
import type { Retain } from "./streams";
|
|
3
4
|
export type Collections = Record<string, z.ZodObject>;
|
|
5
|
+
/** A stream's lines' schema, or that and how long it keeps them (7 days or 50 MB a key unless it says). */
|
|
6
|
+
export type StreamDefinition = z.ZodType | {
|
|
7
|
+
item: z.ZodType;
|
|
8
|
+
retain: Retain;
|
|
9
|
+
};
|
|
4
10
|
/**
|
|
5
11
|
* A query only reads, so cards may run it on their own and again when its data
|
|
6
12
|
* changes. A mutation writes, and runs only when the user acts. A stream only
|
|
@@ -22,18 +28,36 @@ export type Method<I extends z.ZodType = z.ZodType, O extends z.ZodType = z.ZodT
|
|
|
22
28
|
run: (input: z.infer<I>, ctx: Context) => unknown | Promise<unknown>;
|
|
23
29
|
};
|
|
24
30
|
export type Handler = (payload: any, ctx: Context) => unknown | Promise<unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Runs one of the plugin's methods on a schedule, called with `{}` as a tap
|
|
33
|
+
* would: `{ method: "watch", schedule: "*\/5 * * * *" }`. `schedule` is a cron,
|
|
34
|
+
* five fields or six with seconds first; `timezone` defaults to the environment's.
|
|
35
|
+
*/
|
|
36
|
+
export type Cron = {
|
|
37
|
+
method: string;
|
|
38
|
+
schedule: string;
|
|
39
|
+
timezone?: string;
|
|
40
|
+
};
|
|
25
41
|
export type PluginDefinition = {
|
|
26
42
|
/** One line, shown to the agent in the index of plugins. */
|
|
27
43
|
description?: string;
|
|
28
44
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
45
|
+
* Where the plugin's data lives: `<namespace>/` in the data dir, holding `data.db`
|
|
46
|
+
* and its buckets' and streams' files. Plugins naming the same namespace share its
|
|
47
|
+
* data. The plugin's folder name when left out; by convention a project's own is
|
|
48
|
+
* `<project>.<name>` (`repogo.plans`) and data for every project a bare name (`todos`).
|
|
32
49
|
*/
|
|
33
|
-
|
|
50
|
+
namespace?: string;
|
|
34
51
|
collections?: Collections;
|
|
52
|
+
/** Files with a `meta.json` each, in `<namespace>/buckets/<name>/`; the schema is the meta's. */
|
|
53
|
+
buckets?: Record<string, z.ZodObject>;
|
|
54
|
+
/** Keys to values, in the namespace's `data.db`; a key may expire. */
|
|
55
|
+
kv?: Record<string, z.ZodType>;
|
|
56
|
+
/** Append-only JSONL per key, in `<namespace>/streams/<name>/`, kept as `retain` says. */
|
|
57
|
+
streams?: Record<string, StreamDefinition>;
|
|
35
58
|
methods?: Record<string, Method<any, any>>;
|
|
36
59
|
on?: Record<string, Handler>;
|
|
60
|
+
crons?: Cron[];
|
|
37
61
|
};
|
|
38
62
|
export declare function definePlugin<T extends PluginDefinition>(plugin: T): T;
|
|
39
63
|
export declare function method<I extends z.ZodType, O extends z.ZodType = z.ZodType>(input: I, run: (input: z.infer<I>, ctx: Context) => unknown | Promise<unknown>, options?: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { definePlugin, method, stream } from "./define";
|
|
2
|
-
export type { PluginDefinition, Method, MethodKind } from "./define";
|
|
3
|
-
export type { Context } from "./context";
|
|
2
|
+
export type { PluginDefinition, Method, MethodKind, Cron, StreamDefinition } from "./define";
|
|
3
|
+
export type { Context, Manifest, ManifestCron, ManifestMethod, ManifestStorage } from "./context";
|
|
4
|
+
export type { BucketFile, FileStatus, PutFile } from "./buckets";
|
|
5
|
+
export type { KvEntry, SetOptions } from "./kv";
|
|
6
|
+
export type { Retain, StreamEntry, StreamKey } from "./streams";
|
|
7
|
+
export type { Duration } from "./duration";
|
|
4
8
|
export { MethodError, toCallError, type CallError } from "./errors";
|
|
5
9
|
export { z } from "zod";
|
package/dist/kv.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { type Duration } from "./duration";
|
|
3
|
+
import { Store, type StoreOptions } from "./store";
|
|
4
|
+
/** One key and its value, as `get` sees it and the generated methods send it. */
|
|
5
|
+
export type KvEntry<T> = {
|
|
6
|
+
key: string;
|
|
7
|
+
value: T;
|
|
8
|
+
expiresAt: string | null;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
};
|
|
12
|
+
/** `ttl`: how long the key lives; setting a key without one keeps it until deleted. */
|
|
13
|
+
export type SetOptions = {
|
|
14
|
+
ttl?: Duration;
|
|
15
|
+
};
|
|
16
|
+
export declare class Kv<S extends z.ZodType> extends Store {
|
|
17
|
+
private schema;
|
|
18
|
+
private table;
|
|
19
|
+
constructor(options: StoreOptions, name: string, schema: S);
|
|
20
|
+
setup(): Promise<void>;
|
|
21
|
+
get(key: string): Promise<z.infer<S> | null>;
|
|
22
|
+
entry(key: string): Promise<KvEntry<z.infer<S>> | null>;
|
|
23
|
+
set(key: string, value: z.input<S>, options?: SetOptions): Promise<KvEntry<z.infer<S>>>;
|
|
24
|
+
delete(key: string): Promise<void>;
|
|
25
|
+
/** Live keys in key order, those starting with `prefix` when it's given. */
|
|
26
|
+
list(options?: {
|
|
27
|
+
prefix?: string;
|
|
28
|
+
limit?: number;
|
|
29
|
+
}): Promise<KvEntry<z.infer<S>>[]>;
|
|
30
|
+
tick(now: number): Promise<void>;
|
|
31
|
+
private live;
|
|
32
|
+
}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Database } from "./database";
|
|
2
|
+
import type { EventBus } from "./events";
|
|
3
|
+
/** What every kv, stream and bucket is built with: its namespace's file and folder, and the runtime's bus. */
|
|
4
|
+
export type StoreOptions = {
|
|
5
|
+
database: Database;
|
|
6
|
+
events: EventBus;
|
|
7
|
+
namespace: string;
|
|
8
|
+
/** `<dataDir>/<namespace>`, the folder for what isn't rows. */
|
|
9
|
+
dir: string;
|
|
10
|
+
/** Tells cards a tag changed, for writes no call is tracking (the sweep, a scan). */
|
|
11
|
+
announce: (tags: string[]) => void;
|
|
12
|
+
};
|
|
13
|
+
export type StoreKind = "kv" | "streams" | "buckets";
|
|
14
|
+
export declare abstract class Store {
|
|
15
|
+
protected options: StoreOptions;
|
|
16
|
+
readonly kind: StoreKind;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly tag: string;
|
|
19
|
+
readonly plugins: Set<string>;
|
|
20
|
+
constructor(options: StoreOptions, kind: StoreKind, name: string);
|
|
21
|
+
/** Brings the store's files and table up; called once, when the first plugin declares it. */
|
|
22
|
+
abstract setup(): Promise<void>;
|
|
23
|
+
/** Called every few seconds by the Worker: expiry, retention, reconciling with the disk. */
|
|
24
|
+
abstract tick(now: number): Promise<void>;
|
|
25
|
+
close(): void;
|
|
26
|
+
protected get sql(): Bun.SQL;
|
|
27
|
+
protected emit(event: string, payload: unknown): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/** A table name for a store: its kind and name, letters, digits and underscores only. */
|
|
30
|
+
export declare function tableName(kind: StoreKind, name: string): string;
|
|
31
|
+
/** A key or id that is also a file name: no slashes, no leading dot, at most 128 characters. */
|
|
32
|
+
export declare const safeName: RegExp;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { type Duration } from "./duration";
|
|
3
|
+
import { Store, type StoreOptions } from "./store";
|
|
4
|
+
/** One line of a stream. `id` is `<key>:<offset>`, for cards; on disk a line is `{ offset, at, data }`. */
|
|
5
|
+
export type StreamEntry<T> = {
|
|
6
|
+
id: string;
|
|
7
|
+
key: string;
|
|
8
|
+
offset: number;
|
|
9
|
+
at: string;
|
|
10
|
+
data: T;
|
|
11
|
+
};
|
|
12
|
+
/** One key of a stream: a run, a job, a day. */
|
|
13
|
+
export type StreamKey = {
|
|
14
|
+
key: string;
|
|
15
|
+
lines: number;
|
|
16
|
+
bytes: number;
|
|
17
|
+
endedAt: string | null;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* How long a stream keeps what it was sent: a key untouched for `age` goes
|
|
22
|
+
* whole, and past `bytes` its oldest segments go. `"forever"` keeps everything.
|
|
23
|
+
*/
|
|
24
|
+
export type Retain = {
|
|
25
|
+
age?: Duration;
|
|
26
|
+
bytes?: number;
|
|
27
|
+
} | "forever";
|
|
28
|
+
export declare const defaultRetain: {
|
|
29
|
+
readonly age: "7d";
|
|
30
|
+
readonly bytes: 50000000;
|
|
31
|
+
};
|
|
32
|
+
export declare class Stream<S extends z.ZodType> extends Store {
|
|
33
|
+
private schema;
|
|
34
|
+
private table;
|
|
35
|
+
private folder;
|
|
36
|
+
private ageMs;
|
|
37
|
+
private maxBytes;
|
|
38
|
+
private lastRetain;
|
|
39
|
+
/** Followers in this Worker waiting on a key's next line. */
|
|
40
|
+
private waiting;
|
|
41
|
+
constructor(options: StoreOptions, name: string, schema: S, retain?: Retain);
|
|
42
|
+
/** The stream's folder, for agents to `tail`. */
|
|
43
|
+
get path(): string;
|
|
44
|
+
setup(): Promise<void>;
|
|
45
|
+
append(key: string, data: z.input<S>): Promise<StreamEntry<z.infer<S>>>;
|
|
46
|
+
/** Lines from `from` on (the oldest kept when it's older), at most `limit`. */
|
|
47
|
+
read(key: string, options?: {
|
|
48
|
+
from?: number;
|
|
49
|
+
limit?: number;
|
|
50
|
+
}): Promise<StreamEntry<z.infer<S>>[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Replays from `from`, then yields each line as it's appended, until the key
|
|
53
|
+
* ends or `signal` aborts. Defaults to the last `last` lines (100).
|
|
54
|
+
*/
|
|
55
|
+
follow(key: string, options: {
|
|
56
|
+
from?: number;
|
|
57
|
+
last?: number;
|
|
58
|
+
signal: AbortSignal;
|
|
59
|
+
}): AsyncGenerator<StreamEntry<z.core.output<S>>, void, unknown>;
|
|
60
|
+
/** Marks a key finished: followers stop, appends fail, and it goes once it's older than the retention age. */
|
|
61
|
+
end(key: string): Promise<void>;
|
|
62
|
+
keys(options?: {
|
|
63
|
+
limit?: number;
|
|
64
|
+
}): Promise<StreamKey[]>;
|
|
65
|
+
delete(key: string): Promise<void>;
|
|
66
|
+
tick(now: number): Promise<void>;
|
|
67
|
+
close(): void;
|
|
68
|
+
private retain;
|
|
69
|
+
private lines;
|
|
70
|
+
private cutUncommitted;
|
|
71
|
+
private start;
|
|
72
|
+
private row;
|
|
73
|
+
/** A key's segment files by first offset, oldest first. */
|
|
74
|
+
private segments;
|
|
75
|
+
private segmentPath;
|
|
76
|
+
/** Settles on this Worker's next append to `key`, after a poll interval for other Workers', or on abort. */
|
|
77
|
+
private nextAppend;
|
|
78
|
+
private wake;
|
|
79
|
+
}
|