@positronic/cloudflare 0.0.2 → 0.0.4
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/dist/src/api.js +1270 -0
- package/dist/src/brain-runner-do.js +654 -0
- package/dist/src/dev-server.js +1357 -0
- package/{src/index.ts → dist/src/index.js} +1 -6
- package/dist/src/manifest.js +278 -0
- package/dist/src/monitor-do.js +408 -0
- package/{src/node-index.ts → dist/src/node-index.js} +3 -7
- package/dist/src/r2-loader.js +207 -0
- package/dist/src/schedule-do.js +705 -0
- package/dist/src/sqlite-adapter.js +69 -0
- package/dist/types/api.d.ts +21 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/brain-runner-do.d.ts +25 -0
- package/dist/types/brain-runner-do.d.ts.map +1 -0
- package/dist/types/dev-server.d.ts +45 -0
- package/dist/types/dev-server.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/manifest.d.ts +11 -0
- package/dist/types/manifest.d.ts.map +1 -0
- package/dist/types/monitor-do.d.ts +16 -0
- package/dist/types/monitor-do.d.ts.map +1 -0
- package/dist/types/node-index.d.ts +10 -0
- package/dist/types/node-index.d.ts.map +1 -0
- package/dist/types/r2-loader.d.ts +10 -0
- package/dist/types/r2-loader.d.ts.map +1 -0
- package/dist/types/schedule-do.d.ts +47 -0
- package/dist/types/schedule-do.d.ts.map +1 -0
- package/dist/types/sqlite-adapter.d.ts +10 -0
- package/dist/types/sqlite-adapter.d.ts.map +1 -0
- package/package.json +8 -4
- package/src/api.ts +0 -579
- package/src/brain-runner-do.ts +0 -309
- package/src/dev-server.ts +0 -776
- package/src/manifest.ts +0 -69
- package/src/monitor-do.ts +0 -268
- package/src/r2-loader.ts +0 -27
- package/src/schedule-do.ts +0 -377
- package/src/sqlite-adapter.ts +0 -50
- package/test-project/package-lock.json +0 -3010
- package/test-project/package.json +0 -21
- package/test-project/src/index.ts +0 -70
- package/test-project/src/runner.ts +0 -24
- package/test-project/tests/api.test.ts +0 -1005
- package/test-project/tests/r2loader.test.ts +0 -73
- package/test-project/tests/resources-api.test.ts +0 -671
- package/test-project/tests/spec.test.ts +0 -135
- package/test-project/tests/tsconfig.json +0 -7
- package/test-project/tsconfig.json +0 -20
- package/test-project/vitest.config.ts +0 -12
- package/test-project/wrangler.jsonc +0 -53
- package/tsconfig.json +0 -11
package/src/manifest.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { Brain } from '@positronic/core';
|
|
2
|
-
|
|
3
|
-
interface BrainImportStrategy {
|
|
4
|
-
import(name: string): Promise<Brain | undefined>;
|
|
5
|
-
list(): string[];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
class StaticManifestStrategy implements BrainImportStrategy {
|
|
9
|
-
constructor(private manifest: Record<string, Brain>) {}
|
|
10
|
-
|
|
11
|
-
async import(name: string): Promise<Brain | undefined> {
|
|
12
|
-
return this.manifest[name];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
list(): string[] {
|
|
16
|
-
return Object.keys(this.manifest);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
class DynamicImportStrategy implements BrainImportStrategy {
|
|
21
|
-
constructor(private brainsDir: string) {}
|
|
22
|
-
|
|
23
|
-
async import(name: string): Promise<Brain | undefined> {
|
|
24
|
-
try {
|
|
25
|
-
const module = await import(`${this.brainsDir}/${name}.ts`);
|
|
26
|
-
return module.default;
|
|
27
|
-
} catch (e) {
|
|
28
|
-
console.error(`Failed to import brain ${name}:`, e);
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
list(): string[] {
|
|
34
|
-
// For dynamic imports, we can't easily list files at runtime in a worker environment
|
|
35
|
-
// This would need to be handled differently, perhaps by generating a list at build time
|
|
36
|
-
console.warn('DynamicImportStrategy.list() is not implemented - returning empty array');
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export class PositronicManifest {
|
|
42
|
-
private importStrategy: BrainImportStrategy;
|
|
43
|
-
|
|
44
|
-
constructor(options: {
|
|
45
|
-
staticManifest?: Record<string, Brain>;
|
|
46
|
-
brainsDir?: string;
|
|
47
|
-
}) {
|
|
48
|
-
if (options.staticManifest && options.brainsDir) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
'Cannot provide both staticManifest and brainsDir - choose one import strategy'
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
if (!options.staticManifest && !options.brainsDir) {
|
|
54
|
-
throw new Error('Must provide either staticManifest or brainsDir');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
this.importStrategy = options.staticManifest
|
|
58
|
-
? new StaticManifestStrategy(options.staticManifest)
|
|
59
|
-
: new DynamicImportStrategy(options.brainsDir!);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async import(name: string): Promise<Brain | undefined> {
|
|
63
|
-
return this.importStrategy.import(name);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
list(): string[] {
|
|
67
|
-
return this.importStrategy.list();
|
|
68
|
-
}
|
|
69
|
-
}
|
package/src/monitor-do.ts
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { DurableObject } from 'cloudflare:workers';
|
|
2
|
-
import { BRAIN_EVENTS, STATUS } from '@positronic/core';
|
|
3
|
-
import type { BrainEvent } from '@positronic/core';
|
|
4
|
-
|
|
5
|
-
export interface Env {
|
|
6
|
-
// Add any environment bindings here as needed
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class MonitorDO extends DurableObject<Env> {
|
|
10
|
-
private readonly storage: SqlStorage;
|
|
11
|
-
private eventStreamHandler = new EventStreamHandler();
|
|
12
|
-
|
|
13
|
-
constructor(state: DurableObjectState, env: Env) {
|
|
14
|
-
super(state, env);
|
|
15
|
-
this.storage = state.storage.sql;
|
|
16
|
-
|
|
17
|
-
// Update table schema and indexes
|
|
18
|
-
this.storage.exec(`
|
|
19
|
-
CREATE TABLE IF NOT EXISTS brain_runs (
|
|
20
|
-
run_id TEXT PRIMARY KEY,
|
|
21
|
-
brain_title TEXT NOT NULL, -- Renamed column
|
|
22
|
-
brain_description TEXT, -- Renamed column
|
|
23
|
-
type TEXT NOT NULL,
|
|
24
|
-
status TEXT NOT NULL,
|
|
25
|
-
options TEXT,
|
|
26
|
-
error TEXT,
|
|
27
|
-
created_at INTEGER NOT NULL,
|
|
28
|
-
started_at INTEGER,
|
|
29
|
-
completed_at INTEGER
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
CREATE INDEX IF NOT EXISTS idx_brain_status -- Renamed index
|
|
33
|
-
ON brain_runs(brain_title, status);
|
|
34
|
-
|
|
35
|
-
CREATE INDEX IF NOT EXISTS idx_brain_time -- Renamed index
|
|
36
|
-
ON brain_runs(created_at DESC);
|
|
37
|
-
`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
handleBrainEvent(event: BrainEvent<any>) {
|
|
41
|
-
if (
|
|
42
|
-
event.type === BRAIN_EVENTS.START ||
|
|
43
|
-
event.type === BRAIN_EVENTS.RESTART ||
|
|
44
|
-
event.type === BRAIN_EVENTS.COMPLETE ||
|
|
45
|
-
event.type === BRAIN_EVENTS.ERROR
|
|
46
|
-
) {
|
|
47
|
-
const currentTime = Date.now();
|
|
48
|
-
const startTime =
|
|
49
|
-
event.type === BRAIN_EVENTS.START || event.type === BRAIN_EVENTS.RESTART
|
|
50
|
-
? currentTime
|
|
51
|
-
: null;
|
|
52
|
-
const completeTime =
|
|
53
|
-
event.type === BRAIN_EVENTS.COMPLETE ||
|
|
54
|
-
event.type === BRAIN_EVENTS.ERROR
|
|
55
|
-
? currentTime
|
|
56
|
-
: null;
|
|
57
|
-
const error =
|
|
58
|
-
event.type === BRAIN_EVENTS.ERROR ? JSON.stringify(event.error) : null;
|
|
59
|
-
|
|
60
|
-
// Update SQL insert/update with new column names, read from existing event fields
|
|
61
|
-
this.storage.exec(
|
|
62
|
-
`
|
|
63
|
-
INSERT INTO brain_runs (
|
|
64
|
-
run_id, brain_title, brain_description, type, status,
|
|
65
|
-
options, error, created_at, started_at, completed_at
|
|
66
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
67
|
-
ON CONFLICT(run_id) DO UPDATE SET
|
|
68
|
-
type = excluded.type,
|
|
69
|
-
status = excluded.status,
|
|
70
|
-
error = excluded.error,
|
|
71
|
-
completed_at = excluded.completed_at
|
|
72
|
-
`,
|
|
73
|
-
event.brainRunId, // Use brainRunId for run_id
|
|
74
|
-
event.brainTitle, // Read from event field, store in brain_title
|
|
75
|
-
event.brainDescription || null, // Read from event field, store in brain_description
|
|
76
|
-
event.type,
|
|
77
|
-
event.status,
|
|
78
|
-
JSON.stringify(event.options || {}),
|
|
79
|
-
error,
|
|
80
|
-
currentTime,
|
|
81
|
-
startTime,
|
|
82
|
-
completeTime
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
this.broadcastRunningBrains();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private async broadcastRunningBrains() {
|
|
90
|
-
const runningBrains = await this.storage
|
|
91
|
-
.exec(
|
|
92
|
-
`
|
|
93
|
-
SELECT
|
|
94
|
-
run_id as brainRunId,
|
|
95
|
-
brain_title as brainTitle,
|
|
96
|
-
brain_description as brainDescription,
|
|
97
|
-
type,
|
|
98
|
-
status,
|
|
99
|
-
options,
|
|
100
|
-
error,
|
|
101
|
-
created_at as createdAt,
|
|
102
|
-
started_at as startedAt,
|
|
103
|
-
completed_at as completedAt
|
|
104
|
-
FROM brain_runs
|
|
105
|
-
WHERE status = ?
|
|
106
|
-
ORDER BY created_at DESC
|
|
107
|
-
`,
|
|
108
|
-
STATUS.RUNNING
|
|
109
|
-
)
|
|
110
|
-
.toArray();
|
|
111
|
-
|
|
112
|
-
this.eventStreamHandler.broadcast({ runningBrains });
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async fetch(request: Request) {
|
|
116
|
-
const url = new URL(request.url);
|
|
117
|
-
const encoder = new TextEncoder();
|
|
118
|
-
|
|
119
|
-
if (url.pathname === '/watch') {
|
|
120
|
-
const stream = new ReadableStream({
|
|
121
|
-
start: async (controller) => {
|
|
122
|
-
try {
|
|
123
|
-
const runningBrains = await this.storage
|
|
124
|
-
.exec(
|
|
125
|
-
`
|
|
126
|
-
SELECT
|
|
127
|
-
run_id as brainRunId,
|
|
128
|
-
brain_title as brainTitle,
|
|
129
|
-
brain_description as brainDescription,
|
|
130
|
-
type,
|
|
131
|
-
status,
|
|
132
|
-
options,
|
|
133
|
-
error,
|
|
134
|
-
created_at as createdAt,
|
|
135
|
-
started_at as startedAt,
|
|
136
|
-
completed_at as completedAt
|
|
137
|
-
FROM brain_runs
|
|
138
|
-
WHERE status = ?
|
|
139
|
-
ORDER BY created_at DESC
|
|
140
|
-
`,
|
|
141
|
-
STATUS.RUNNING
|
|
142
|
-
)
|
|
143
|
-
.toArray();
|
|
144
|
-
|
|
145
|
-
controller.enqueue(
|
|
146
|
-
encoder.encode(`data: ${JSON.stringify({ runningBrains })}\n\n`)
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
this.eventStreamHandler.subscribe(controller);
|
|
150
|
-
} catch (err) {
|
|
151
|
-
console.error('[MONITOR_DO] Error during stream start:', err);
|
|
152
|
-
controller.close();
|
|
153
|
-
this.eventStreamHandler.unsubscribe(controller);
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
cancel: (controller) => {
|
|
157
|
-
this.eventStreamHandler.unsubscribe(controller);
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
return new Response(stream, {
|
|
162
|
-
headers: {
|
|
163
|
-
'Content-Type': 'text/event-stream',
|
|
164
|
-
'Cache-Control': 'no-cache',
|
|
165
|
-
Connection: 'keep-alive',
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return new Response('Not found', { status: 404 });
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// No changes needed for getLastEvent, uses run_id
|
|
174
|
-
getLastEvent(brainRunId: string) {
|
|
175
|
-
return this.storage
|
|
176
|
-
.exec(
|
|
177
|
-
`
|
|
178
|
-
SELECT * FROM brain_runs WHERE run_id = ?
|
|
179
|
-
`,
|
|
180
|
-
brainRunId
|
|
181
|
-
)
|
|
182
|
-
.one();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Update history method parameter and query
|
|
186
|
-
history(brainTitle: string, limit: number = 10) {
|
|
187
|
-
// Renamed parameter
|
|
188
|
-
// Update select query with aliases and filter by brain_title
|
|
189
|
-
return this.storage
|
|
190
|
-
.exec(
|
|
191
|
-
`
|
|
192
|
-
SELECT
|
|
193
|
-
run_id as brainRunId,
|
|
194
|
-
brain_title as brainTitle,
|
|
195
|
-
brain_description as brainDescription,
|
|
196
|
-
type,
|
|
197
|
-
status,
|
|
198
|
-
options,
|
|
199
|
-
error,
|
|
200
|
-
created_at as createdAt,
|
|
201
|
-
started_at as startedAt,
|
|
202
|
-
completed_at as completedAt
|
|
203
|
-
FROM brain_runs
|
|
204
|
-
WHERE brain_title = ? -- Filter by new column name
|
|
205
|
-
ORDER BY created_at DESC
|
|
206
|
-
LIMIT ?
|
|
207
|
-
`,
|
|
208
|
-
brainTitle,
|
|
209
|
-
limit
|
|
210
|
-
)
|
|
211
|
-
.toArray(); // Use renamed parameter
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Get active/running brain runs for a specific brain
|
|
215
|
-
activeRuns(brainTitle: string) {
|
|
216
|
-
return this.storage
|
|
217
|
-
.exec(
|
|
218
|
-
`
|
|
219
|
-
SELECT
|
|
220
|
-
run_id as brainRunId,
|
|
221
|
-
brain_title as brainTitle,
|
|
222
|
-
brain_description as brainDescription,
|
|
223
|
-
type,
|
|
224
|
-
status,
|
|
225
|
-
options,
|
|
226
|
-
error,
|
|
227
|
-
created_at as createdAt,
|
|
228
|
-
started_at as startedAt,
|
|
229
|
-
completed_at as completedAt
|
|
230
|
-
FROM brain_runs
|
|
231
|
-
WHERE brain_title = ? AND status = ?
|
|
232
|
-
ORDER BY created_at DESC
|
|
233
|
-
`,
|
|
234
|
-
brainTitle,
|
|
235
|
-
STATUS.RUNNING
|
|
236
|
-
)
|
|
237
|
-
.toArray();
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
class EventStreamHandler {
|
|
242
|
-
private subscribers: Set<ReadableStreamDefaultController> = new Set();
|
|
243
|
-
private encoder = new TextEncoder();
|
|
244
|
-
|
|
245
|
-
subscribe(controller: ReadableStreamDefaultController) {
|
|
246
|
-
this.subscribers.add(controller);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
unsubscribe(controller: ReadableStreamDefaultController) {
|
|
250
|
-
this.subscribers.delete(controller);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
broadcast(data: any) {
|
|
254
|
-
const message = `data: ${JSON.stringify(data)}\n\n`;
|
|
255
|
-
const encodedMessage = this.encoder.encode(message);
|
|
256
|
-
this.subscribers.forEach((controller) => {
|
|
257
|
-
try {
|
|
258
|
-
controller.enqueue(encodedMessage);
|
|
259
|
-
} catch (e) {
|
|
260
|
-
console.error(
|
|
261
|
-
'[MONITOR_DO_SSE] Failed to send message to subscriber, removing.',
|
|
262
|
-
e
|
|
263
|
-
);
|
|
264
|
-
this.unsubscribe(controller);
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
package/src/r2-loader.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { R2Bucket } from '@cloudflare/workers-types';
|
|
2
|
-
import type { ResourceLoader } from '@positronic/core';
|
|
3
|
-
import { Buffer } from 'buffer';
|
|
4
|
-
|
|
5
|
-
export class CloudflareR2Loader implements ResourceLoader {
|
|
6
|
-
constructor(private bucket: R2Bucket) {}
|
|
7
|
-
|
|
8
|
-
async load(resourceName: string, type: 'text'): Promise<string>;
|
|
9
|
-
async load(resourceName: string, type: 'binary'): Promise<Buffer>;
|
|
10
|
-
async load(
|
|
11
|
-
resourceName: string,
|
|
12
|
-
type: 'text' | 'binary' = 'text'
|
|
13
|
-
): Promise<string | Buffer> {
|
|
14
|
-
const object = await this.bucket.get(resourceName);
|
|
15
|
-
|
|
16
|
-
if (object === null) {
|
|
17
|
-
throw new Error(`Resource "${resourceName}" not found in R2 bucket.`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (type === 'binary') {
|
|
21
|
-
const arrayBuffer = await object.arrayBuffer();
|
|
22
|
-
return Buffer.from(arrayBuffer);
|
|
23
|
-
}
|
|
24
|
-
// Defaults to text
|
|
25
|
-
return object.text();
|
|
26
|
-
}
|
|
27
|
-
}
|