@treeseed/sdk 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +565 -0
- package/dist/cli-tools.js +44 -0
- package/dist/content-store.js +237 -0
- package/dist/d1-store.js +549 -0
- package/dist/frontmatter.js +33 -0
- package/dist/git-runtime.js +67 -0
- package/dist/index.js +12 -0
- package/dist/model-registry.js +164 -0
- package/dist/runtime.js +36 -0
- package/dist/scripts/.ts-run-1775616845195-odh4xzphk3l.js +22 -0
- package/dist/scripts/.ts-run-1775616848931-9386s6kwrl.js +126 -0
- package/dist/scripts/assert-release-tag-version.d.ts +1 -0
- package/dist/scripts/assert-release-tag-version.js +23 -0
- package/dist/scripts/build-dist.d.ts +1 -0
- package/dist/scripts/build-dist.js +114 -0
- package/dist/scripts/package-tools.d.ts +15 -0
- package/dist/scripts/package-tools.js +76 -0
- package/dist/scripts/publish-package.d.ts +1 -0
- package/dist/scripts/publish-package.js +20 -0
- package/dist/scripts/release-verify.d.ts +1 -0
- package/dist/scripts/release-verify.js +49 -0
- package/dist/scripts/run-ts.js +45 -0
- package/dist/scripts/test-smoke.d.ts +1 -0
- package/dist/scripts/test-smoke.js +77 -0
- package/dist/sdk-filters.js +77 -0
- package/dist/sdk-types.js +24 -0
- package/dist/sdk.js +232 -0
- package/dist/src/cli-tools.d.ts +3 -0
- package/dist/src/content-store.d.ts +24 -0
- package/dist/src/d1-store.d.ts +108 -0
- package/dist/src/frontmatter.d.ts +6 -0
- package/dist/src/git-runtime.d.ts +16 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/model-registry.d.ts +4 -0
- package/dist/src/runtime.d.ts +1 -0
- package/dist/src/sdk-filters.d.ts +4 -0
- package/dist/src/sdk-types.d.ts +285 -0
- package/dist/src/sdk.d.ts +109 -0
- package/dist/src/stores/cursor-store.d.ts +10 -0
- package/dist/src/stores/envelopes.d.ts +116 -0
- package/dist/src/stores/helpers.d.ts +12 -0
- package/dist/src/stores/lease-store.d.ts +18 -0
- package/dist/src/stores/message-store.d.ts +12 -0
- package/dist/src/stores/run-store.d.ts +10 -0
- package/dist/src/stores/subscription-store.d.ts +9 -0
- package/dist/src/types/agents.d.ts +100 -0
- package/dist/src/types/cloudflare.d.ts +32 -0
- package/dist/src/wrangler-d1.d.ts +25 -0
- package/dist/stores/cursor-store.js +158 -0
- package/dist/stores/envelopes.js +219 -0
- package/dist/stores/helpers.js +42 -0
- package/dist/stores/lease-store.js +183 -0
- package/dist/stores/message-store.js +249 -0
- package/dist/stores/run-store.js +166 -0
- package/dist/stores/subscription-store.js +171 -0
- package/dist/test/test-fixture.d.ts +1 -0
- package/dist/test/utils/envelopes.test.d.ts +1 -0
- package/dist/test/utils/sdk.test.d.ts +1 -0
- package/dist/types/agents.js +40 -0
- package/dist/types/cloudflare.js +0 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/wrangler-d1.js +84 -0
- package/package.json +130 -0
package/dist/d1-store.js
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { applyFilters, applySort } from "./sdk-filters.js";
|
|
3
|
+
import { CursorStore } from "./stores/cursor-store.js";
|
|
4
|
+
import { LeaseStore } from "./stores/lease-store.js";
|
|
5
|
+
import { MessageStore } from "./stores/message-store.js";
|
|
6
|
+
import { RunStore } from "./stores/run-store.js";
|
|
7
|
+
import { SubscriptionStore } from "./stores/subscription-store.js";
|
|
8
|
+
function nowIso() {
|
|
9
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
10
|
+
}
|
|
11
|
+
function nextLeaseToken() {
|
|
12
|
+
return crypto.randomUUID();
|
|
13
|
+
}
|
|
14
|
+
function filterSinceField(model) {
|
|
15
|
+
switch (model) {
|
|
16
|
+
case "message":
|
|
17
|
+
case "subscription":
|
|
18
|
+
return "updated_at";
|
|
19
|
+
case "agent_run":
|
|
20
|
+
return "startedAt";
|
|
21
|
+
case "agent_cursor":
|
|
22
|
+
return "updatedAt";
|
|
23
|
+
case "content_lease":
|
|
24
|
+
return "leaseExpiresAt";
|
|
25
|
+
default:
|
|
26
|
+
return "updatedAt";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
class MemoryAgentDatabase {
|
|
30
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
31
|
+
messages = /* @__PURE__ */ new Map();
|
|
32
|
+
runs = /* @__PURE__ */ new Map();
|
|
33
|
+
contentLeases = /* @__PURE__ */ new Map();
|
|
34
|
+
cursors = /* @__PURE__ */ new Map();
|
|
35
|
+
messageId = 0;
|
|
36
|
+
constructor(seed) {
|
|
37
|
+
for (const item of seed?.subscriptions ?? []) {
|
|
38
|
+
this.subscriptions.set(String(item.id ?? item.email), item);
|
|
39
|
+
}
|
|
40
|
+
for (const message of seed?.messages ?? []) {
|
|
41
|
+
this.messages.set(message.id, message);
|
|
42
|
+
this.messageId = Math.max(this.messageId, message.id);
|
|
43
|
+
}
|
|
44
|
+
for (const run of seed?.runs ?? []) {
|
|
45
|
+
this.runs.set(run.runId, run);
|
|
46
|
+
}
|
|
47
|
+
for (const cursor of seed?.cursors ?? []) {
|
|
48
|
+
this.cursors.set(`${cursor.agentSlug}:${cursor.cursorKey}`, cursor.cursorValue);
|
|
49
|
+
}
|
|
50
|
+
for (const lease of seed?.leases ?? []) {
|
|
51
|
+
this.contentLeases.set(`${lease.model}:${lease.itemKey}`, lease);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
rowsForModel(model) {
|
|
55
|
+
if (model === "subscription") {
|
|
56
|
+
return [...this.subscriptions.values()];
|
|
57
|
+
}
|
|
58
|
+
if (model === "message") {
|
|
59
|
+
return [...this.messages.values()];
|
|
60
|
+
}
|
|
61
|
+
if (model === "agent_run") {
|
|
62
|
+
return [...this.runs.values()];
|
|
63
|
+
}
|
|
64
|
+
if (model === "agent_cursor") {
|
|
65
|
+
return [...this.cursors.entries()].map(([key, value]) => {
|
|
66
|
+
const [agentSlug, cursorKey] = key.split(":", 2);
|
|
67
|
+
return {
|
|
68
|
+
agentSlug,
|
|
69
|
+
cursorKey,
|
|
70
|
+
cursorValue: value,
|
|
71
|
+
updatedAt: null
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (model === "content_lease") {
|
|
76
|
+
return [...this.contentLeases.values()].map((lease) => ({
|
|
77
|
+
model: lease.model,
|
|
78
|
+
itemKey: lease.itemKey,
|
|
79
|
+
claimedBy: lease.claimedBy,
|
|
80
|
+
claimedAt: lease.claimedAt,
|
|
81
|
+
leaseExpiresAt: lease.leaseExpiresAt,
|
|
82
|
+
token: lease.token
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
throw new Error(`Unsupported D1 model "${model}".`);
|
|
86
|
+
}
|
|
87
|
+
async get(request) {
|
|
88
|
+
const key = String(request.id ?? request.slug ?? request.key ?? "");
|
|
89
|
+
if (request.model === "agent_cursor") {
|
|
90
|
+
if (!key) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const [agentSlug, cursorKey] = key.split(":", 2);
|
|
94
|
+
const value = this.cursors.get(`${agentSlug}:${cursorKey}`);
|
|
95
|
+
return value ? {
|
|
96
|
+
agentSlug,
|
|
97
|
+
cursorKey,
|
|
98
|
+
cursorValue: value,
|
|
99
|
+
updatedAt: null
|
|
100
|
+
} : null;
|
|
101
|
+
}
|
|
102
|
+
if (request.model === "content_lease") {
|
|
103
|
+
const lease = this.contentLeases.get(key);
|
|
104
|
+
return lease ? {
|
|
105
|
+
model: lease.model,
|
|
106
|
+
itemKey: lease.itemKey,
|
|
107
|
+
claimedBy: lease.claimedBy,
|
|
108
|
+
claimedAt: lease.claimedAt,
|
|
109
|
+
leaseExpiresAt: lease.leaseExpiresAt,
|
|
110
|
+
token: lease.token
|
|
111
|
+
} : null;
|
|
112
|
+
}
|
|
113
|
+
return this.rowsForModel(request.model).find(
|
|
114
|
+
(row) => [row.id, row.email, row.runId].map((value) => String(value ?? "")).includes(key)
|
|
115
|
+
) ?? null;
|
|
116
|
+
}
|
|
117
|
+
async search(request) {
|
|
118
|
+
const filtered = applyFilters(this.rowsForModel(request.model), request.filters);
|
|
119
|
+
const sorted = applySort(filtered, request.sort);
|
|
120
|
+
return sorted.slice(0, request.limit ?? sorted.length);
|
|
121
|
+
}
|
|
122
|
+
async follow(request) {
|
|
123
|
+
const filters = [
|
|
124
|
+
...request.filters ?? [],
|
|
125
|
+
{
|
|
126
|
+
field: filterSinceField(request.model),
|
|
127
|
+
op: "updated_since",
|
|
128
|
+
value: request.since
|
|
129
|
+
}
|
|
130
|
+
];
|
|
131
|
+
return {
|
|
132
|
+
items: await this.search({
|
|
133
|
+
model: request.model,
|
|
134
|
+
filters
|
|
135
|
+
}),
|
|
136
|
+
since: request.since
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async pick(request) {
|
|
140
|
+
if (request.model === "message") {
|
|
141
|
+
const item = await this.claimMessage({
|
|
142
|
+
workerId: request.workerId,
|
|
143
|
+
messageTypes: request.filters?.filter((filter) => filter.field === "type" && filter.op === "in").flatMap((filter) => Array.isArray(filter.value) ? filter.value.map(String) : []),
|
|
144
|
+
leaseSeconds: request.leaseSeconds
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
item,
|
|
148
|
+
leaseToken: item ? nextLeaseToken() : null
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (request.model === "content_lease") {
|
|
152
|
+
const item = (await this.search({
|
|
153
|
+
model: request.model,
|
|
154
|
+
filters: request.filters,
|
|
155
|
+
sort: [{ field: "leaseExpiresAt", direction: "desc" }],
|
|
156
|
+
limit: 1
|
|
157
|
+
}))[0];
|
|
158
|
+
return {
|
|
159
|
+
item: item ?? null,
|
|
160
|
+
leaseToken: item ? String(item.token) : null
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const items = await this.search({
|
|
164
|
+
model: request.model,
|
|
165
|
+
filters: request.filters,
|
|
166
|
+
sort: [{ field: filterSinceField(request.model), direction: "desc" }]
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
item: items[0] ?? null,
|
|
170
|
+
leaseToken: null
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async create(request) {
|
|
174
|
+
switch (request.model) {
|
|
175
|
+
case "message":
|
|
176
|
+
return await this.createMessage({
|
|
177
|
+
type: String(request.data.type ?? "message.created"),
|
|
178
|
+
payload: request.data.payload ?? request.data,
|
|
179
|
+
relatedModel: typeof request.data.relatedModel === "string" ? request.data.relatedModel : null,
|
|
180
|
+
relatedId: typeof request.data.relatedId === "string" ? request.data.relatedId : null,
|
|
181
|
+
priority: Number(request.data.priority ?? 0),
|
|
182
|
+
maxAttempts: Number(request.data.maxAttempts ?? 3),
|
|
183
|
+
actor: request.actor
|
|
184
|
+
});
|
|
185
|
+
case "subscription": {
|
|
186
|
+
const record = {
|
|
187
|
+
id: this.subscriptions.size + 1,
|
|
188
|
+
email: String(request.data.email ?? ""),
|
|
189
|
+
name: request.data.name ? String(request.data.name) : null,
|
|
190
|
+
status: String(request.data.status ?? "active"),
|
|
191
|
+
source: String(request.data.source ?? "sdk"),
|
|
192
|
+
consent_at: String(request.data.consent_at ?? nowIso()),
|
|
193
|
+
created_at: String(request.data.created_at ?? nowIso()),
|
|
194
|
+
updated_at: String(request.data.updated_at ?? nowIso()),
|
|
195
|
+
ip_hash: String(request.data.ip_hash ?? "")
|
|
196
|
+
};
|
|
197
|
+
this.subscriptions.set(String(record.id), record);
|
|
198
|
+
return record;
|
|
199
|
+
}
|
|
200
|
+
case "agent_run":
|
|
201
|
+
return this.recordRun({ run: request.data });
|
|
202
|
+
case "agent_cursor": {
|
|
203
|
+
const agentSlug = String(request.data.agentSlug ?? "");
|
|
204
|
+
const cursorKey = String(request.data.cursorKey ?? "");
|
|
205
|
+
const cursorValue = String(request.data.cursorValue ?? "");
|
|
206
|
+
this.cursors.set(`${agentSlug}:${cursorKey}`, cursorValue);
|
|
207
|
+
return {
|
|
208
|
+
agentSlug,
|
|
209
|
+
cursorKey,
|
|
210
|
+
cursorValue,
|
|
211
|
+
updatedAt: nowIso()
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
case "content_lease": {
|
|
215
|
+
const token = await this.tryClaimContentLease({
|
|
216
|
+
model: String(request.data.model ?? ""),
|
|
217
|
+
itemKey: String(request.data.itemKey ?? ""),
|
|
218
|
+
claimedBy: String(request.data.claimedBy ?? request.actor),
|
|
219
|
+
leaseSeconds: Number(request.data.leaseSeconds ?? 300)
|
|
220
|
+
});
|
|
221
|
+
const lease = this.contentLeases.get(`${request.data.model}:${request.data.itemKey}`);
|
|
222
|
+
return {
|
|
223
|
+
model: String(request.data.model ?? ""),
|
|
224
|
+
itemKey: String(request.data.itemKey ?? ""),
|
|
225
|
+
claimedBy: String(request.data.claimedBy ?? request.actor),
|
|
226
|
+
claimedAt: String(lease?.claimedAt ?? nowIso()),
|
|
227
|
+
leaseExpiresAt: String(lease?.leaseExpiresAt ?? nowIso()),
|
|
228
|
+
token: String(token ?? lease?.token ?? "")
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
default:
|
|
232
|
+
throw new Error(`Unsupported D1 create model "${request.model}".`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async update(request) {
|
|
236
|
+
switch (request.model) {
|
|
237
|
+
case "message": {
|
|
238
|
+
const current = this.messages.get(Number(request.id ?? request.key ?? request.data.id ?? 0));
|
|
239
|
+
if (!current) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
const next = {
|
|
243
|
+
...current,
|
|
244
|
+
...request.data,
|
|
245
|
+
updatedAt: nowIso()
|
|
246
|
+
};
|
|
247
|
+
this.messages.set(next.id, next);
|
|
248
|
+
return next;
|
|
249
|
+
}
|
|
250
|
+
case "subscription": {
|
|
251
|
+
const key = String(request.id ?? request.key ?? request.data.email ?? "");
|
|
252
|
+
const current = await this.get({ model: "subscription", key });
|
|
253
|
+
if (!current) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
const next = {
|
|
257
|
+
...current,
|
|
258
|
+
...request.data,
|
|
259
|
+
updated_at: nowIso()
|
|
260
|
+
};
|
|
261
|
+
this.subscriptions.set(String(next.id ?? next.email), next);
|
|
262
|
+
return next;
|
|
263
|
+
}
|
|
264
|
+
case "agent_run":
|
|
265
|
+
return this.recordRun({ run: { ...request.data, runId: request.id ?? request.key ?? request.data.runId } });
|
|
266
|
+
case "agent_cursor":
|
|
267
|
+
return this.create({
|
|
268
|
+
model: "agent_cursor",
|
|
269
|
+
data: request.data,
|
|
270
|
+
actor: request.actor
|
|
271
|
+
});
|
|
272
|
+
case "content_lease":
|
|
273
|
+
return this.create({
|
|
274
|
+
model: "content_lease",
|
|
275
|
+
data: request.data,
|
|
276
|
+
actor: request.actor
|
|
277
|
+
});
|
|
278
|
+
default:
|
|
279
|
+
throw new Error(`Unsupported D1 update model "${request.model}".`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async claimMessage(request) {
|
|
283
|
+
const pending = [...this.messages.values()].filter(
|
|
284
|
+
(message) => (message.status === "pending" || message.status === "failed") && new Date(message.availableAt).valueOf() <= Date.now() && (!request.messageTypes?.length || request.messageTypes.includes(message.type))
|
|
285
|
+
).sort((left, right) => right.priority - left.priority || left.availableAt.localeCompare(right.availableAt))[0];
|
|
286
|
+
if (!pending) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const claimedAt = nowIso();
|
|
290
|
+
const next = {
|
|
291
|
+
...pending,
|
|
292
|
+
status: "claimed",
|
|
293
|
+
claimedBy: request.workerId,
|
|
294
|
+
claimedAt,
|
|
295
|
+
leaseExpiresAt: new Date(Date.now() + request.leaseSeconds * 1e3).toISOString(),
|
|
296
|
+
attempts: pending.attempts + 1,
|
|
297
|
+
updatedAt: claimedAt
|
|
298
|
+
};
|
|
299
|
+
this.messages.set(next.id, next);
|
|
300
|
+
return next;
|
|
301
|
+
}
|
|
302
|
+
async ackMessage(request) {
|
|
303
|
+
const current = this.messages.get(request.id);
|
|
304
|
+
if (!current) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
this.messages.set(request.id, {
|
|
308
|
+
...current,
|
|
309
|
+
status: request.status,
|
|
310
|
+
updatedAt: nowIso()
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async createMessage(request) {
|
|
314
|
+
this.messageId += 1;
|
|
315
|
+
const record = {
|
|
316
|
+
id: this.messageId,
|
|
317
|
+
type: request.type,
|
|
318
|
+
status: "pending",
|
|
319
|
+
payloadJson: JSON.stringify(request.payload),
|
|
320
|
+
relatedModel: request.relatedModel ?? null,
|
|
321
|
+
relatedId: request.relatedId ?? null,
|
|
322
|
+
priority: request.priority ?? 0,
|
|
323
|
+
availableAt: nowIso(),
|
|
324
|
+
claimedBy: null,
|
|
325
|
+
claimedAt: null,
|
|
326
|
+
leaseExpiresAt: null,
|
|
327
|
+
attempts: 0,
|
|
328
|
+
maxAttempts: request.maxAttempts ?? 3,
|
|
329
|
+
createdAt: nowIso(),
|
|
330
|
+
updatedAt: nowIso()
|
|
331
|
+
};
|
|
332
|
+
this.messages.set(record.id, record);
|
|
333
|
+
return record;
|
|
334
|
+
}
|
|
335
|
+
async recordRun(request) {
|
|
336
|
+
const run = request.run;
|
|
337
|
+
this.runs.set(String(run.runId), run);
|
|
338
|
+
return run;
|
|
339
|
+
}
|
|
340
|
+
async getCursor(request) {
|
|
341
|
+
return this.cursors.get(`${request.agentSlug}:${request.cursorKey}`) ?? null;
|
|
342
|
+
}
|
|
343
|
+
async upsertCursor(request) {
|
|
344
|
+
this.cursors.set(`${request.agentSlug}:${request.cursorKey}`, request.cursorValue);
|
|
345
|
+
}
|
|
346
|
+
async releaseLease(request) {
|
|
347
|
+
this.contentLeases.delete(`${request.model}:${request.itemKey}`);
|
|
348
|
+
}
|
|
349
|
+
async tryClaimContentLease(input) {
|
|
350
|
+
const key = `${input.model}:${input.itemKey}`;
|
|
351
|
+
const existing = this.contentLeases.get(key);
|
|
352
|
+
if (existing && new Date(existing.leaseExpiresAt).valueOf() > Date.now()) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
const token = nextLeaseToken();
|
|
356
|
+
this.contentLeases.set(key, {
|
|
357
|
+
model: input.model,
|
|
358
|
+
itemKey: input.itemKey,
|
|
359
|
+
claimedBy: input.claimedBy,
|
|
360
|
+
claimedAt: nowIso(),
|
|
361
|
+
leaseExpiresAt: new Date(Date.now() + input.leaseSeconds * 1e3).toISOString(),
|
|
362
|
+
token
|
|
363
|
+
});
|
|
364
|
+
return token;
|
|
365
|
+
}
|
|
366
|
+
async releaseAllLeases() {
|
|
367
|
+
const count = this.contentLeases.size;
|
|
368
|
+
this.contentLeases.clear();
|
|
369
|
+
return count;
|
|
370
|
+
}
|
|
371
|
+
inspectRuns() {
|
|
372
|
+
return [...this.runs.values()];
|
|
373
|
+
}
|
|
374
|
+
inspectLeases() {
|
|
375
|
+
return [...this.contentLeases.values()];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
class CloudflareD1AgentDatabase {
|
|
379
|
+
constructor(db) {
|
|
380
|
+
this.db = db;
|
|
381
|
+
this.subscriptions = new SubscriptionStore(db);
|
|
382
|
+
this.messages = new MessageStore(db);
|
|
383
|
+
this.runs = new RunStore(db);
|
|
384
|
+
this.cursors = new CursorStore(db);
|
|
385
|
+
this.leases = new LeaseStore(db);
|
|
386
|
+
}
|
|
387
|
+
db;
|
|
388
|
+
subscriptions;
|
|
389
|
+
messages;
|
|
390
|
+
runs;
|
|
391
|
+
cursors;
|
|
392
|
+
leases;
|
|
393
|
+
async get(request) {
|
|
394
|
+
if (request.model === "subscription") {
|
|
395
|
+
return this.subscriptions.getByKey(String(request.id ?? request.slug ?? request.key ?? ""));
|
|
396
|
+
}
|
|
397
|
+
if (request.model === "message") {
|
|
398
|
+
return this.messages.getById(Number(request.id ?? request.slug ?? request.key ?? 0));
|
|
399
|
+
}
|
|
400
|
+
if (request.model === "agent_run") {
|
|
401
|
+
return this.runs.getByKey(String(request.id ?? request.key ?? request.slug ?? ""));
|
|
402
|
+
}
|
|
403
|
+
if (request.model === "agent_cursor") {
|
|
404
|
+
return this.cursors.getByKey(String(request.id ?? request.key ?? request.slug ?? ""));
|
|
405
|
+
}
|
|
406
|
+
if (request.model === "content_lease") {
|
|
407
|
+
return this.leases.getByKey(String(request.id ?? request.key ?? request.slug ?? ""));
|
|
408
|
+
}
|
|
409
|
+
throw new Error(`Unsupported D1 get model "${request.model}".`);
|
|
410
|
+
}
|
|
411
|
+
async search(request) {
|
|
412
|
+
if (request.model === "subscription") {
|
|
413
|
+
return this.subscriptions.search(request);
|
|
414
|
+
}
|
|
415
|
+
if (request.model === "message") {
|
|
416
|
+
return this.messages.search(request);
|
|
417
|
+
}
|
|
418
|
+
if (request.model === "agent_run") {
|
|
419
|
+
return this.runs.search(request);
|
|
420
|
+
}
|
|
421
|
+
if (request.model === "agent_cursor") {
|
|
422
|
+
return this.cursors.search(request);
|
|
423
|
+
}
|
|
424
|
+
if (request.model === "content_lease") {
|
|
425
|
+
return this.leases.search(request);
|
|
426
|
+
}
|
|
427
|
+
throw new Error(`Unsupported D1 search model "${request.model}".`);
|
|
428
|
+
}
|
|
429
|
+
async follow(request) {
|
|
430
|
+
const field = request.model === "subscription" || request.model === "message" ? "updated_at" : request.model === "agent_run" ? "started_at" : request.model === "agent_cursor" ? "updated_at" : "lease_expires_at";
|
|
431
|
+
return this.search({
|
|
432
|
+
model: request.model,
|
|
433
|
+
filters: [
|
|
434
|
+
...request.filters ?? [],
|
|
435
|
+
{ field, op: "updated_since", value: request.since }
|
|
436
|
+
]
|
|
437
|
+
}).then((items) => ({ items, since: request.since }));
|
|
438
|
+
}
|
|
439
|
+
async pick(request) {
|
|
440
|
+
if (request.model === "message") {
|
|
441
|
+
const claimed = await this.claimMessage({
|
|
442
|
+
workerId: request.workerId,
|
|
443
|
+
messageTypes: request.filters?.filter((filter) => filter.field === "type" && filter.op === "in").flatMap((filter) => Array.isArray(filter.value) ? filter.value.map(String) : []),
|
|
444
|
+
leaseSeconds: request.leaseSeconds
|
|
445
|
+
});
|
|
446
|
+
return {
|
|
447
|
+
item: claimed,
|
|
448
|
+
leaseToken: claimed ? nextLeaseToken() : null
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
if (request.model === "content_lease") {
|
|
452
|
+
const items = await this.leases.search({
|
|
453
|
+
model: "content_lease",
|
|
454
|
+
filters: request.filters,
|
|
455
|
+
sort: [{ field: "lease_expires_at", direction: "desc" }],
|
|
456
|
+
limit: 1
|
|
457
|
+
});
|
|
458
|
+
return {
|
|
459
|
+
item: items[0] ?? null,
|
|
460
|
+
leaseToken: items[0]?.token ?? null
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
item: null,
|
|
465
|
+
leaseToken: null
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
async create(request) {
|
|
469
|
+
if (request.model === "message") {
|
|
470
|
+
return await this.createMessage({
|
|
471
|
+
type: String(request.data.type ?? "message.created"),
|
|
472
|
+
payload: request.data.payload ?? request.data,
|
|
473
|
+
relatedModel: typeof request.data.relatedModel === "string" ? request.data.relatedModel : null,
|
|
474
|
+
relatedId: typeof request.data.relatedId === "string" ? request.data.relatedId : null,
|
|
475
|
+
priority: Number(request.data.priority ?? 0),
|
|
476
|
+
maxAttempts: Number(request.data.maxAttempts ?? 3),
|
|
477
|
+
actor: request.actor
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (request.model === "subscription") {
|
|
481
|
+
return await this.subscriptions.create(request);
|
|
482
|
+
}
|
|
483
|
+
if (request.model === "agent_run") {
|
|
484
|
+
return await this.runs.record({ run: request.data });
|
|
485
|
+
}
|
|
486
|
+
if (request.model === "agent_cursor") {
|
|
487
|
+
return await this.cursors.update({
|
|
488
|
+
...request,
|
|
489
|
+
model: "agent_cursor"
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
if (request.model === "content_lease") {
|
|
493
|
+
return await this.leases.update({
|
|
494
|
+
...request,
|
|
495
|
+
model: "content_lease"
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
throw new Error(`Unsupported D1 create model "${request.model}".`);
|
|
499
|
+
}
|
|
500
|
+
async update(request) {
|
|
501
|
+
if (request.model === "message") {
|
|
502
|
+
return this.messages.update(request);
|
|
503
|
+
}
|
|
504
|
+
if (request.model === "subscription") {
|
|
505
|
+
return this.subscriptions.update(request);
|
|
506
|
+
}
|
|
507
|
+
if (request.model === "agent_run") {
|
|
508
|
+
return this.runs.update(request);
|
|
509
|
+
}
|
|
510
|
+
if (request.model === "agent_cursor") {
|
|
511
|
+
return this.cursors.update(request);
|
|
512
|
+
}
|
|
513
|
+
if (request.model === "content_lease") {
|
|
514
|
+
return this.leases.update(request);
|
|
515
|
+
}
|
|
516
|
+
throw new Error(`Unsupported D1 update model "${request.model}".`);
|
|
517
|
+
}
|
|
518
|
+
claimMessage(request) {
|
|
519
|
+
return this.messages.claim(request);
|
|
520
|
+
}
|
|
521
|
+
ackMessage(request) {
|
|
522
|
+
return this.messages.ack(request);
|
|
523
|
+
}
|
|
524
|
+
createMessage(request) {
|
|
525
|
+
return this.messages.create(request);
|
|
526
|
+
}
|
|
527
|
+
recordRun(request) {
|
|
528
|
+
return this.runs.record(request);
|
|
529
|
+
}
|
|
530
|
+
getCursor(request) {
|
|
531
|
+
return this.cursors.get(request);
|
|
532
|
+
}
|
|
533
|
+
upsertCursor(request) {
|
|
534
|
+
return this.cursors.upsert(request);
|
|
535
|
+
}
|
|
536
|
+
releaseLease(request) {
|
|
537
|
+
return this.leases.release(request);
|
|
538
|
+
}
|
|
539
|
+
tryClaimContentLease(input) {
|
|
540
|
+
return this.leases.tryClaim(input);
|
|
541
|
+
}
|
|
542
|
+
releaseAllLeases() {
|
|
543
|
+
return this.leases.releaseAll();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
export {
|
|
547
|
+
CloudflareD1AgentDatabase,
|
|
548
|
+
MemoryAgentDatabase
|
|
549
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
2
|
+
function parseFrontmatterDocument(source) {
|
|
3
|
+
if (!source.startsWith("---\n")) {
|
|
4
|
+
return {
|
|
5
|
+
frontmatter: {},
|
|
6
|
+
body: source
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
const delimiterIndex = source.indexOf("\n---\n", 4);
|
|
10
|
+
if (delimiterIndex < 0) {
|
|
11
|
+
return {
|
|
12
|
+
frontmatter: {},
|
|
13
|
+
body: source
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const yamlSource = source.slice(4, delimiterIndex);
|
|
17
|
+
const body = source.slice(delimiterIndex + 5);
|
|
18
|
+
const parsed = parseYaml(yamlSource);
|
|
19
|
+
return {
|
|
20
|
+
frontmatter: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {},
|
|
21
|
+
body
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function serializeFrontmatterDocument(frontmatter, body) {
|
|
25
|
+
return `---
|
|
26
|
+
${stringifyYaml(frontmatter).trimEnd()}
|
|
27
|
+
---
|
|
28
|
+
${body.replace(/^\n*/, "")}`;
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
parseFrontmatterDocument,
|
|
32
|
+
serializeFrontmatterDocument
|
|
33
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
class GitRuntime {
|
|
6
|
+
constructor(repoRoot, disabled = false) {
|
|
7
|
+
this.repoRoot = repoRoot;
|
|
8
|
+
this.disabled = disabled;
|
|
9
|
+
}
|
|
10
|
+
repoRoot;
|
|
11
|
+
disabled;
|
|
12
|
+
async currentBranch() {
|
|
13
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
14
|
+
cwd: this.repoRoot
|
|
15
|
+
});
|
|
16
|
+
return stdout.trim();
|
|
17
|
+
}
|
|
18
|
+
async ensureWorktree(branchName) {
|
|
19
|
+
const worktreePath = path.join(this.repoRoot, ".agent-worktrees", branchName);
|
|
20
|
+
if (this.disabled) {
|
|
21
|
+
return worktreePath;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await execFileAsync("git", ["worktree", "list", "--porcelain"], {
|
|
25
|
+
cwd: this.repoRoot
|
|
26
|
+
}).then(async ({ stdout }) => {
|
|
27
|
+
if (stdout.includes(`worktree ${worktreePath}`)) {
|
|
28
|
+
await execFileAsync("git", ["switch", branchName], { cwd: worktreePath });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
await execFileAsync("git", ["worktree", "add", "-B", branchName, worktreePath, "HEAD"], {
|
|
32
|
+
cwd: this.repoRoot
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
} catch {
|
|
36
|
+
await execFileAsync("git", ["worktree", "add", "-B", branchName, worktreePath, "HEAD"], {
|
|
37
|
+
cwd: this.repoRoot
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return worktreePath;
|
|
41
|
+
}
|
|
42
|
+
async commitFileChange(filePath, branchName, commitMessage) {
|
|
43
|
+
return this.commitFileChanges([filePath], branchName, commitMessage);
|
|
44
|
+
}
|
|
45
|
+
async commitFileChanges(filePaths, branchName, commitMessage) {
|
|
46
|
+
const worktreePath = await this.ensureWorktree(branchName);
|
|
47
|
+
if (this.disabled) {
|
|
48
|
+
return { branchName, commitMessage, worktreePath, commitSha: null, changedPaths: filePaths };
|
|
49
|
+
}
|
|
50
|
+
const relativeFilePaths = filePaths.map((filePath) => path.relative(worktreePath, filePath));
|
|
51
|
+
await execFileAsync("git", ["add", ...relativeFilePaths], { cwd: worktreePath });
|
|
52
|
+
try {
|
|
53
|
+
await execFileAsync("git", ["commit", "-m", commitMessage], { cwd: worktreePath });
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const message = error && typeof error === "object" && "stderr" in error ? String(error.stderr ?? "") : "";
|
|
56
|
+
if (!message.includes("nothing to commit")) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: worktreePath });
|
|
61
|
+
const commitSha = stdout.trim();
|
|
62
|
+
return { branchName, commitMessage, worktreePath, commitSha, changedPaths: filePaths };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
GitRuntime
|
|
67
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AgentSdk, ScopedAgentSdk } from "./sdk.js";
|
|
2
|
+
import { MODEL_REGISTRY, buildModelRegistry, resolveModelDefinition } from "./model-registry.js";
|
|
3
|
+
import { normalizeAgentCliOptions, buildCopilotAllowToolArgs } from "./cli-tools.js";
|
|
4
|
+
export {
|
|
5
|
+
AgentSdk,
|
|
6
|
+
MODEL_REGISTRY,
|
|
7
|
+
ScopedAgentSdk,
|
|
8
|
+
buildCopilotAllowToolArgs,
|
|
9
|
+
buildModelRegistry,
|
|
10
|
+
normalizeAgentCliOptions,
|
|
11
|
+
resolveModelDefinition
|
|
12
|
+
};
|