@positronic/cloudflare 0.0.68 → 0.0.69

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.
@@ -126,6 +126,31 @@ function _ts_generator(thisArg, body) {
126
126
  }
127
127
  }
128
128
  import { jwtVerify, decodeJwt, importJWK } from 'jose';
129
+ /**
130
+ * Middleware that restricts access to root users only.
131
+ * Returns 403 if the authenticated user is not root.
132
+ */ export function requireRoot() {
133
+ return function(c, next) {
134
+ return _async_to_generator(function() {
135
+ var auth;
136
+ return _ts_generator(this, function(_state) {
137
+ auth = c.get('auth');
138
+ if (!(auth === null || auth === void 0 ? void 0 : auth.isRoot)) {
139
+ return [
140
+ 2,
141
+ c.json({
142
+ error: 'Root access required'
143
+ }, 403)
144
+ ];
145
+ }
146
+ return [
147
+ 2,
148
+ next()
149
+ ];
150
+ });
151
+ })();
152
+ };
153
+ }
129
154
  /**
130
155
  * Get the JWT algorithm based on JWK key type
131
156
  */ function getAlgorithmForJwk(jwk) {
@@ -160,9 +160,18 @@ import Fuse from 'fuse.js';
160
160
  import { isSignalValid, brainMachineDefinition } from '@positronic/core';
161
161
  import { getManifest } from '../brain-runner-do.js';
162
162
  var brains = new Hono();
163
+ /**
164
+ * Get the userId for ownership filtering from the auth context.
165
+ * Root users get null (no filter — sees everything).
166
+ * Non-root users get their userId (sees only their own).
167
+ */ function scopeUserId(context) {
168
+ var auth = context.get('auth');
169
+ var _auth_userId;
170
+ return (auth === null || auth === void 0 ? void 0 : auth.isRoot) ? null : (_auth_userId = auth === null || auth === void 0 ? void 0 : auth.userId) !== null && _auth_userId !== void 0 ? _auth_userId : null;
171
+ }
163
172
  brains.post('/runs', function(context) {
164
173
  return _async_to_generator(function() {
165
- var requestBody, options, identifier, manifest, resolution, brain, brainRunId, namespace, doId, stub, initialData, brainTitle, response;
174
+ var requestBody, options, identifier, manifest, resolution, brain, brainRunId, namespace, doId, stub, auth, currentUser, initialData, brainTitle, response;
166
175
  return _ts_generator(this, function(_state) {
167
176
  switch(_state.label){
168
177
  case 0:
@@ -218,6 +227,19 @@ brains.post('/runs', function(context) {
218
227
  namespace = context.env.BRAIN_RUNNER_DO;
219
228
  doId = namespace.idFromName(brainRunId);
220
229
  stub = namespace.get(doId);
230
+ // Read auth context for currentUser (every brain run must have an owner)
231
+ auth = context.get('auth');
232
+ if (!(auth === null || auth === void 0 ? void 0 : auth.userId) && !(auth === null || auth === void 0 ? void 0 : auth.isRoot)) {
233
+ return [
234
+ 2,
235
+ context.json({
236
+ error: 'Authentication required to run a brain'
237
+ }, 401)
238
+ ];
239
+ }
240
+ currentUser = {
241
+ id: auth.userId || 'root'
242
+ };
221
243
  // Pass options to the brain runner if provided
222
244
  initialData = options ? {
223
245
  options: options
@@ -226,7 +248,7 @@ brains.post('/runs', function(context) {
226
248
  brainTitle = brain.title || identifier;
227
249
  return [
228
250
  4,
229
- stub.start(brainTitle, brainRunId, initialData)
251
+ stub.start(brainTitle, brainRunId, currentUser, initialData)
230
252
  ];
231
253
  case 2:
232
254
  _state.sent();
@@ -243,7 +265,7 @@ brains.post('/runs', function(context) {
243
265
  });
244
266
  brains.post('/runs/rerun', function(context) {
245
267
  return _async_to_generator(function() {
246
- var requestBody, runId, startsAt, stopsAfter, identifier, manifest, resolution, brain, monitorId, monitorStub, existingRun, newBrainRunId, namespace, doId, stub, rerunOptions, brainTitle, response;
268
+ var requestBody, runId, startsAt, stopsAfter, identifier, manifest, resolution, brain, monitorId, monitorStub, existingRun, auth, currentUser, newBrainRunId, namespace, doId, stub, rerunOptions, brainTitle, response;
247
269
  return _ts_generator(this, function(_state) {
248
270
  switch(_state.label){
249
271
  case 0:
@@ -317,6 +339,19 @@ brains.post('/runs/rerun', function(context) {
317
339
  }
318
340
  _state.label = 3;
319
341
  case 3:
342
+ // Read auth context for currentUser (every brain run must have an owner)
343
+ auth = context.get('auth');
344
+ if (!(auth === null || auth === void 0 ? void 0 : auth.userId) && !(auth === null || auth === void 0 ? void 0 : auth.isRoot)) {
345
+ return [
346
+ 2,
347
+ context.json({
348
+ error: 'Authentication required to run a brain'
349
+ }, 401)
350
+ ];
351
+ }
352
+ currentUser = {
353
+ id: auth.userId || 'root'
354
+ };
320
355
  // Create a new brain run with rerun parameters
321
356
  newBrainRunId = uuidv4();
322
357
  namespace = context.env.BRAIN_RUNNER_DO;
@@ -334,7 +369,7 @@ brains.post('/runs/rerun', function(context) {
334
369
  brainTitle = brain.title || identifier;
335
370
  return [
336
371
  4,
337
- stub.start(brainTitle, newBrainRunId, rerunOptions)
372
+ stub.start(brainTitle, newBrainRunId, currentUser, rerunOptions)
338
373
  ];
339
374
  case 4:
340
375
  _state.sent();
@@ -375,16 +410,17 @@ brains.get('/runs/:runId/watch', function(context) {
375
410
  });
376
411
  brains.get('/runs/:runId', function(context) {
377
412
  return _async_to_generator(function() {
378
- var runId, monitorId, monitorStub, run;
413
+ var runId, userId, monitorId, monitorStub, run;
379
414
  return _ts_generator(this, function(_state) {
380
415
  switch(_state.label){
381
416
  case 0:
382
417
  runId = context.req.param('runId');
418
+ userId = scopeUserId(context);
383
419
  monitorId = context.env.MONITOR_DO.idFromName('singleton');
384
420
  monitorStub = context.env.MONITOR_DO.get(monitorId);
385
421
  return [
386
422
  4,
387
- monitorStub.getRun(runId)
423
+ monitorStub.getRun(runId, userId)
388
424
  ];
389
425
  case 1:
390
426
  run = _state.sent();
@@ -640,7 +676,7 @@ brains.post('/runs/:runId/resume', function(context) {
640
676
  });
641
677
  brains.get('/:identifier/history', function(context) {
642
678
  return _async_to_generator(function() {
643
- var identifier, limit, manifest, resolution, brain, brainTitle, monitorId, monitorStub, runs;
679
+ var identifier, limit, manifest, resolution, brain, brainTitle, monitorId, monitorStub, userId, runs;
644
680
  return _ts_generator(this, function(_state) {
645
681
  switch(_state.label){
646
682
  case 0:
@@ -681,9 +717,10 @@ brains.get('/:identifier/history', function(context) {
681
717
  // Get the monitor singleton instance
682
718
  monitorId = context.env.MONITOR_DO.idFromName('singleton');
683
719
  monitorStub = context.env.MONITOR_DO.get(monitorId);
720
+ userId = scopeUserId(context);
684
721
  return [
685
722
  4,
686
- monitorStub.history(brainTitle, limit)
723
+ monitorStub.history(brainTitle, limit, userId)
687
724
  ];
688
725
  case 1:
689
726
  runs = _state.sent();
@@ -699,7 +736,7 @@ brains.get('/:identifier/history', function(context) {
699
736
  });
700
737
  brains.get('/:identifier/active-runs', function(context) {
701
738
  return _async_to_generator(function() {
702
- var identifier, manifest, resolution, brain, brainTitle, monitorId, monitorStub, runs;
739
+ var identifier, manifest, resolution, brain, brainTitle, monitorId, monitorStub, userId, runs;
703
740
  return _ts_generator(this, function(_state) {
704
741
  switch(_state.label){
705
742
  case 0:
@@ -739,9 +776,10 @@ brains.get('/:identifier/active-runs', function(context) {
739
776
  // Get the monitor singleton instance
740
777
  monitorId = context.env.MONITOR_DO.idFromName('singleton');
741
778
  monitorStub = context.env.MONITOR_DO.get(monitorId);
779
+ userId = scopeUserId(context);
742
780
  return [
743
781
  4,
744
- monitorStub.activeRuns(brainTitle)
782
+ monitorStub.activeRuns(brainTitle, userId)
745
783
  ];
746
784
  case 1:
747
785
  runs = _state.sent();
@@ -922,7 +960,7 @@ brains.get('/', function(context) {
922
960
  // Create a new schedule
923
961
  brains.post('/schedules', function(context) {
924
962
  return _async_to_generator(function() {
925
- var body, cronExpression, identifier, manifest, resolution, brain, brainTitle, scheduleDoId, scheduleStub, timezone, schedule, error, errorMessage;
963
+ var body, cronExpression, identifier, manifest, resolution, brain, brainTitle, scheduleDoId, scheduleStub, auth, runAsUserId, timezone, schedule, error, errorMessage;
926
964
  return _ts_generator(this, function(_state) {
927
965
  switch(_state.label){
928
966
  case 0:
@@ -1003,6 +1041,17 @@ brains.post('/schedules', function(context) {
1003
1041
  // Get the schedule singleton instance
1004
1042
  scheduleDoId = context.env.SCHEDULE_DO.idFromName('singleton');
1005
1043
  scheduleStub = context.env.SCHEDULE_DO.get(scheduleDoId);
1044
+ // Require authentication — scheduled runs will execute as this user
1045
+ auth = context.get('auth');
1046
+ if (!(auth === null || auth === void 0 ? void 0 : auth.userId) && !(auth === null || auth === void 0 ? void 0 : auth.isRoot)) {
1047
+ return [
1048
+ 2,
1049
+ context.json({
1050
+ error: 'Authentication required to create a schedule'
1051
+ }, 401)
1052
+ ];
1053
+ }
1054
+ runAsUserId = auth.userId || 'root';
1006
1055
  // Determine timezone: use provided value, fall back to project timezone
1007
1056
  timezone = body.timezone;
1008
1057
  if (!!timezone) return [
@@ -1032,7 +1081,7 @@ brains.post('/schedules', function(context) {
1032
1081
  }
1033
1082
  return [
1034
1083
  4,
1035
- scheduleStub.createSchedule(brainTitle, cronExpression, timezone)
1084
+ scheduleStub.createSchedule(brainTitle, cronExpression, timezone, runAsUserId)
1036
1085
  ];
1037
1086
  case 4:
1038
1087
  schedule = _state.sent();
@@ -1060,15 +1109,16 @@ brains.post('/schedules', function(context) {
1060
1109
  // List all schedules
1061
1110
  brains.get('/schedules', function(context) {
1062
1111
  return _async_to_generator(function() {
1063
- var scheduleId, scheduleStub, result;
1112
+ var scheduleId, scheduleStub, userId, result;
1064
1113
  return _ts_generator(this, function(_state) {
1065
1114
  switch(_state.label){
1066
1115
  case 0:
1067
1116
  scheduleId = context.env.SCHEDULE_DO.idFromName('singleton');
1068
1117
  scheduleStub = context.env.SCHEDULE_DO.get(scheduleId);
1118
+ userId = scopeUserId(context);
1069
1119
  return [
1070
1120
  4,
1071
- scheduleStub.listSchedules()
1121
+ scheduleStub.listSchedules(userId)
1072
1122
  ];
1073
1123
  case 1:
1074
1124
  result = _state.sent();
@@ -1083,7 +1133,7 @@ brains.get('/schedules', function(context) {
1083
1133
  // Get scheduled run history - MUST be before :scheduleId route
1084
1134
  brains.get('/schedules/runs', function(context) {
1085
1135
  return _async_to_generator(function() {
1086
- var scheduleIdParam, limit, scheduleDoId, scheduleStub, result;
1136
+ var scheduleIdParam, limit, scheduleDoId, scheduleStub, userId, result;
1087
1137
  return _ts_generator(this, function(_state) {
1088
1138
  switch(_state.label){
1089
1139
  case 0:
@@ -1091,9 +1141,10 @@ brains.get('/schedules/runs', function(context) {
1091
1141
  limit = Number(context.req.query('limit') || '100');
1092
1142
  scheduleDoId = context.env.SCHEDULE_DO.idFromName('singleton');
1093
1143
  scheduleStub = context.env.SCHEDULE_DO.get(scheduleDoId);
1144
+ userId = scopeUserId(context);
1094
1145
  return [
1095
1146
  4,
1096
- scheduleStub.getAllRuns(scheduleIdParam, limit)
1147
+ scheduleStub.getAllRuns(scheduleIdParam, limit, userId)
1097
1148
  ];
1098
1149
  case 1:
1099
1150
  result = _state.sent();
@@ -178,6 +178,7 @@ function _ts_generator(thisArg, body) {
178
178
  }
179
179
  }
180
180
  import { Hono } from 'hono';
181
+ import { requireRoot } from './auth-middleware.js';
181
182
  /**
182
183
  * Helper to check if Cloudflare API credentials are configured
183
184
  */ function getSecretsApiConfig(env) {
@@ -214,6 +215,8 @@ import { Hono } from 'hono';
214
215
  }).apply(this, arguments);
215
216
  }
216
217
  var secrets = new Hono();
218
+ // Only root users can manage secrets
219
+ secrets.use('*', requireRoot());
217
220
  // Protected secret name that cannot be managed via the API
218
221
  var PROTECTED_SECRET = 'ROOT_PUBLIC_KEY';
219
222
  // List all secrets (names only, not values)
@@ -300,6 +300,7 @@ import { EventLoader } from './event-loader.js';
300
300
  import { createPagesService } from './pages-service.js';
301
301
  import { setGovernorBinding, rateGoverned } from './governor-client-wrapper.js';
302
302
  import { CloudflareR2Loader } from './r2-loader.js';
303
+ import { createR2Backend } from './create-r2-store.js';
303
304
  import { createResources } from '@positronic/core';
304
305
  var manifest = null;
305
306
  export function setManifest(generatedManifest) {
@@ -504,6 +505,8 @@ var ScheduleAdapter = /*#__PURE__*/ function() {
504
505
  ]);
505
506
  return BatchChunkAdapter;
506
507
  }();
508
+ // SQL to initialize the run owner table (stores who started this brain run)
509
+ var runOwnerTableSQL = "\nCREATE TABLE IF NOT EXISTS run_owner (\n user_id TEXT NOT NULL\n);\n";
507
510
  // SQL to initialize the signals table
508
511
  var signalsTableSQL = "\nCREATE TABLE IF NOT EXISTS brain_signals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n signal_type TEXT NOT NULL,\n content TEXT,\n queued_at INTEGER NOT NULL\n);\n";
509
512
  // SQL to initialize the wait timeout table
@@ -517,13 +520,37 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
517
520
  _this = _call_super(this, BrainRunnerDO, [
518
521
  state,
519
522
  env
520
- ]), _define_property(_this, "sql", void 0), _define_property(_this, "brainRunId", void 0), _define_property(_this, "eventStreamAdapter", new EventStreamAdapter()), _define_property(_this, "abortController", null), _define_property(_this, "pageAdapter", null), _define_property(_this, "signalsTableInitialized", false), _define_property(_this, "waitTimeoutTableInitialized", false);
523
+ ]), _define_property(_this, "sql", void 0), _define_property(_this, "brainRunId", void 0), _define_property(_this, "eventStreamAdapter", new EventStreamAdapter()), _define_property(_this, "abortController", null), _define_property(_this, "pageAdapter", null), _define_property(_this, "runOwnerTableInitialized", false), _define_property(_this, "signalsTableInitialized", false), _define_property(_this, "waitTimeoutTableInitialized", false);
521
524
  _this.sql = state.storage.sql;
522
525
  _this.brainRunId = state.id.toString();
523
526
  _this.env = env;
524
527
  return _this;
525
528
  }
526
529
  _create_class(BrainRunnerDO, [
530
+ {
531
+ key: "initializeRunOwnerTable",
532
+ value: function initializeRunOwnerTable() {
533
+ if (!this.runOwnerTableInitialized) {
534
+ this.sql.exec(runOwnerTableSQL);
535
+ this.runOwnerTableInitialized = true;
536
+ }
537
+ }
538
+ },
539
+ {
540
+ key: "storeRunOwner",
541
+ value: function storeRunOwner(userId) {
542
+ this.initializeRunOwnerTable();
543
+ this.sql.exec("INSERT INTO run_owner (user_id) VALUES (?)", userId);
544
+ }
545
+ },
546
+ {
547
+ key: "getRunOwner",
548
+ value: function getRunOwner() {
549
+ this.initializeRunOwnerTable();
550
+ var results = this.sql.exec("SELECT user_id FROM run_owner LIMIT 1").toArray();
551
+ return results.length > 0 ? results[0].user_id : null;
552
+ }
553
+ },
527
554
  {
528
555
  key: "initializeSignalsTable",
529
556
  value: function initializeSignalsTable() {
@@ -1019,7 +1046,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1019
1046
  },
1020
1047
  {
1021
1048
  key: "start",
1022
- value: function start(brainTitle, brainRunId, initialData) {
1049
+ value: function start(brainTitle, brainRunId, currentUser, initialData) {
1023
1050
  return _async_to_generator(function() {
1024
1051
  var _this, sql, resolution, brainToRun, sqliteAdapter, eventStreamAdapter, monitorDOStub, monitorAdapter, scheduleAdapter, webhookAdapter, env, pagesService, r2Resources, runnerWithResources, signalProvider, options, initialState, batchChunkAdapter, timeoutAdapter;
1025
1052
  return _ts_generator(this, function(_state) {
@@ -1080,10 +1107,12 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1080
1107
  });
1081
1108
  runnerWithResources = runnerWithResources.withSignalProvider(signalProvider).withGovernor(function(c) {
1082
1109
  return rateGoverned(c);
1083
- });
1110
+ }).withStoreProvider(createR2Backend(this.env.RESOURCES_BUCKET));
1084
1111
  // Extract options from initialData if present
1085
1112
  options = initialData === null || initialData === void 0 ? void 0 : initialData.options;
1086
1113
  initialState = initialData && !initialData.options ? initialData : {};
1114
+ // Persist run owner durably (immutable, not derived from events)
1115
+ this.storeRunOwner(currentUser.id);
1087
1116
  // Create abort controller for this run
1088
1117
  this.abortController = new AbortController();
1089
1118
  batchChunkAdapter = new BatchChunkAdapter(function(signal) {
@@ -1106,6 +1135,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1106
1135
  batchChunkAdapter,
1107
1136
  timeoutAdapter
1108
1137
  ]).run(brainToRun, _object_spread_props(_object_spread({
1138
+ currentUser: currentUser,
1109
1139
  initialState: initialState,
1110
1140
  brainRunId: brainRunId
1111
1141
  }, options && {
@@ -1135,7 +1165,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1135
1165
  * This method reconstructs state and calls BrainRunner.resume().
1136
1166
  */ function wakeUp(brainRunId) {
1137
1167
  return _async_to_generator(function() {
1138
- var _this, sql, pendingTimeout, eventLoader, startEvent, brainTitle, initialState, originalBrainRunId, resolution, brainToRun, allEvents, machine, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, event, sqliteAdapter, eventStreamAdapter, monitorDOStub, monitorAdapter, scheduleAdapter, webhookAdapter, env, pagesService, r2Resources, runnerWithResources, signalProvider, batchChunkAdapter, timeoutAdapter;
1168
+ var _this, sql, pendingTimeout, eventLoader, startEvent, brainTitle, initialState, ownerId, currentUser, originalBrainRunId, resolution, brainToRun, allEvents, machine, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, event, sqliteAdapter, eventStreamAdapter, monitorDOStub, monitorAdapter, scheduleAdapter, webhookAdapter, env, pagesService, r2Resources, runnerWithResources, signalProvider, batchChunkAdapter, timeoutAdapter;
1139
1169
  return _ts_generator(this, function(_state) {
1140
1170
  switch(_state.label){
1141
1171
  case 0:
@@ -1174,6 +1204,14 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1174
1204
  }
1175
1205
  brainTitle = startEvent.brainTitle;
1176
1206
  initialState = startEvent.initialState || {};
1207
+ // Read run owner from durable storage (set once in start(), not from events)
1208
+ ownerId = this.getRunOwner();
1209
+ if (!ownerId) {
1210
+ throw new Error("No run owner found for brain run ".concat(brainRunId));
1211
+ }
1212
+ currentUser = {
1213
+ id: ownerId
1214
+ };
1177
1215
  // Use the brainRunId from the START event, not the parameter.
1178
1216
  // alarm() passes state.id.toString() (the DO hex ID), but the brain was
1179
1217
  // originally started with a UUID. Events must use the original UUID so
@@ -1260,7 +1298,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1260
1298
  });
1261
1299
  runnerWithResources = runnerWithResources.withSignalProvider(signalProvider).withGovernor(function(c) {
1262
1300
  return rateGoverned(c);
1263
- });
1301
+ }).withStoreProvider(createR2Backend(this.env.RESOURCES_BUCKET));
1264
1302
  // Create abort controller for this run
1265
1303
  this.abortController = new AbortController();
1266
1304
  batchChunkAdapter = new BatchChunkAdapter(function(signal) {
@@ -1283,6 +1321,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1283
1321
  batchChunkAdapter,
1284
1322
  timeoutAdapter
1285
1323
  ]).resume(brainToRun, {
1324
+ currentUser: currentUser,
1286
1325
  machine: machine,
1287
1326
  brainRunId: originalBrainRunId,
1288
1327
  signal: this.abortController.signal
@@ -0,0 +1,319 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_with_holes(arr) {
7
+ if (Array.isArray(arr)) return arr;
8
+ }
9
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
10
+ try {
11
+ var info = gen[key](arg);
12
+ var value = info.value;
13
+ } catch (error) {
14
+ reject(error);
15
+ return;
16
+ }
17
+ if (info.done) {
18
+ resolve(value);
19
+ } else {
20
+ Promise.resolve(value).then(_next, _throw);
21
+ }
22
+ }
23
+ function _async_to_generator(fn) {
24
+ return function() {
25
+ var self = this, args = arguments;
26
+ return new Promise(function(resolve, reject) {
27
+ var gen = fn.apply(self, args);
28
+ function _next(value) {
29
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
30
+ }
31
+ function _throw(err) {
32
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
33
+ }
34
+ _next(undefined);
35
+ });
36
+ };
37
+ }
38
+ function _iterable_to_array_limit(arr, i) {
39
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
40
+ if (_i == null) return;
41
+ var _arr = [];
42
+ var _n = true;
43
+ var _d = false;
44
+ var _s, _e;
45
+ try {
46
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
47
+ _arr.push(_s.value);
48
+ if (i && _arr.length === i) break;
49
+ }
50
+ } catch (err) {
51
+ _d = true;
52
+ _e = err;
53
+ } finally{
54
+ try {
55
+ if (!_n && _i["return"] != null) _i["return"]();
56
+ } finally{
57
+ if (_d) throw _e;
58
+ }
59
+ }
60
+ return _arr;
61
+ }
62
+ function _non_iterable_rest() {
63
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
64
+ }
65
+ function _sliced_to_array(arr, i) {
66
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
67
+ }
68
+ function _type_of(obj) {
69
+ "@swc/helpers - typeof";
70
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
71
+ }
72
+ function _unsupported_iterable_to_array(o, minLen) {
73
+ if (!o) return;
74
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
75
+ var n = Object.prototype.toString.call(o).slice(8, -1);
76
+ if (n === "Object" && o.constructor) n = o.constructor.name;
77
+ if (n === "Map" || n === "Set") return Array.from(n);
78
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
79
+ }
80
+ function _ts_generator(thisArg, body) {
81
+ var f, y, t, _ = {
82
+ label: 0,
83
+ sent: function() {
84
+ if (t[0] & 1) throw t[1];
85
+ return t[1];
86
+ },
87
+ trys: [],
88
+ ops: []
89
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
90
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
91
+ return this;
92
+ }), g;
93
+ function verb(n) {
94
+ return function(v) {
95
+ return step([
96
+ n,
97
+ v
98
+ ]);
99
+ };
100
+ }
101
+ function step(op) {
102
+ if (f) throw new TypeError("Generator is already executing.");
103
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
104
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
105
+ if (y = 0, t) op = [
106
+ op[0] & 2,
107
+ t.value
108
+ ];
109
+ switch(op[0]){
110
+ case 0:
111
+ case 1:
112
+ t = op;
113
+ break;
114
+ case 4:
115
+ _.label++;
116
+ return {
117
+ value: op[1],
118
+ done: false
119
+ };
120
+ case 5:
121
+ _.label++;
122
+ y = op[1];
123
+ op = [
124
+ 0
125
+ ];
126
+ continue;
127
+ case 7:
128
+ op = _.ops.pop();
129
+ _.trys.pop();
130
+ continue;
131
+ default:
132
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
133
+ _ = 0;
134
+ continue;
135
+ }
136
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
137
+ _.label = op[1];
138
+ break;
139
+ }
140
+ if (op[0] === 6 && _.label < t[1]) {
141
+ _.label = t[1];
142
+ t = op;
143
+ break;
144
+ }
145
+ if (t && _.label < t[2]) {
146
+ _.label = t[2];
147
+ _.ops.push(op);
148
+ break;
149
+ }
150
+ if (t[2]) _.ops.pop();
151
+ _.trys.pop();
152
+ continue;
153
+ }
154
+ op = body.call(thisArg, _);
155
+ } catch (e) {
156
+ op = [
157
+ 6,
158
+ e
159
+ ];
160
+ y = 0;
161
+ } finally{
162
+ f = t = 0;
163
+ }
164
+ if (op[0] & 5) throw op[1];
165
+ return {
166
+ value: op[0] ? op[1] : void 0,
167
+ done: true
168
+ };
169
+ }
170
+ }
171
+ /**
172
+ * Type guard for per-user field definitions in a store schema.
173
+ */ function isPerUserField(value) {
174
+ return value !== null && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object' && 'perUser' in value && value.perUser === true && 'type' in value;
175
+ }
176
+ /**
177
+ * Create a StoreProvider backed by Cloudflare R2.
178
+ *
179
+ * Key resolution:
180
+ * shared: store/{brainTitle}/{key}.json
181
+ * per-user: store/{brainTitle}/user/{userId}/{key}.json
182
+ *
183
+ * The factory receives the store schema, brain title, and currentUser,
184
+ * and returns a typed Store<any> with full key resolution built in.
185
+ */ export function createR2Backend(bucket) {
186
+ return function(param) {
187
+ var schema = param.schema, brainTitle = param.brainTitle, currentUser = param.currentUser;
188
+ var resolveKey = function resolveKey(key) {
189
+ if (perUserKeys.has(key)) {
190
+ if (!currentUser) {
191
+ throw new Error('Store key "'.concat(key, '" is per-user but no currentUser was provided. ') + "Per-user store keys require a currentUser in run params.");
192
+ }
193
+ return "store/".concat(brainTitle, "/user/").concat(currentUser.id, "/").concat(key);
194
+ }
195
+ return "store/".concat(brainTitle, "/").concat(key);
196
+ };
197
+ // Parse which keys are per-user from the schema
198
+ var perUserKeys = new Set();
199
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
200
+ try {
201
+ for(var _iterator = Object.entries(schema)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
202
+ var _step_value = _sliced_to_array(_step.value, 2), key = _step_value[0], value = _step_value[1];
203
+ if (isPerUserField(value)) {
204
+ perUserKeys.add(key);
205
+ }
206
+ }
207
+ } catch (err) {
208
+ _didIteratorError = true;
209
+ _iteratorError = err;
210
+ } finally{
211
+ try {
212
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
213
+ _iterator.return();
214
+ }
215
+ } finally{
216
+ if (_didIteratorError) {
217
+ throw _iteratorError;
218
+ }
219
+ }
220
+ }
221
+ var store = {
222
+ get: function get(key) {
223
+ return _async_to_generator(function() {
224
+ var resolved, object, _;
225
+ return _ts_generator(this, function(_state) {
226
+ switch(_state.label){
227
+ case 0:
228
+ resolved = resolveKey(key);
229
+ return [
230
+ 4,
231
+ bucket.get("".concat(resolved, ".json"))
232
+ ];
233
+ case 1:
234
+ object = _state.sent();
235
+ if (object === null) return [
236
+ 2,
237
+ undefined
238
+ ];
239
+ _ = JSON.parse;
240
+ return [
241
+ 4,
242
+ object.text()
243
+ ];
244
+ case 2:
245
+ return [
246
+ 2,
247
+ _.apply(JSON, [
248
+ _state.sent()
249
+ ])
250
+ ];
251
+ }
252
+ });
253
+ })();
254
+ },
255
+ set: function set(key, value) {
256
+ return _async_to_generator(function() {
257
+ var resolved;
258
+ return _ts_generator(this, function(_state) {
259
+ switch(_state.label){
260
+ case 0:
261
+ resolved = resolveKey(key);
262
+ return [
263
+ 4,
264
+ bucket.put("".concat(resolved, ".json"), JSON.stringify(value))
265
+ ];
266
+ case 1:
267
+ _state.sent();
268
+ return [
269
+ 2
270
+ ];
271
+ }
272
+ });
273
+ })();
274
+ },
275
+ delete: function _delete(key) {
276
+ return _async_to_generator(function() {
277
+ var resolved;
278
+ return _ts_generator(this, function(_state) {
279
+ switch(_state.label){
280
+ case 0:
281
+ resolved = resolveKey(key);
282
+ return [
283
+ 4,
284
+ bucket.delete("".concat(resolved, ".json"))
285
+ ];
286
+ case 1:
287
+ _state.sent();
288
+ return [
289
+ 2
290
+ ];
291
+ }
292
+ });
293
+ })();
294
+ },
295
+ has: function has(key) {
296
+ return _async_to_generator(function() {
297
+ var resolved, object;
298
+ return _ts_generator(this, function(_state) {
299
+ switch(_state.label){
300
+ case 0:
301
+ resolved = resolveKey(key);
302
+ return [
303
+ 4,
304
+ bucket.head("".concat(resolved, ".json"))
305
+ ];
306
+ case 1:
307
+ object = _state.sent();
308
+ return [
309
+ 2,
310
+ object !== null
311
+ ];
312
+ }
313
+ });
314
+ })();
315
+ }
316
+ };
317
+ return store;
318
+ };
319
+ }
package/dist/src/index.js CHANGED
@@ -4,5 +4,6 @@ export { ScheduleDO } from './schedule-do.js';
4
4
  export { AuthDO } from './auth-do.js';
5
5
  export { GovernorDO } from './governor-do.js';
6
6
  export { rateGoverned, setGovernorBinding } from './governor-client-wrapper.js';
7
+ export { createR2Backend } from './create-r2-store.js';
7
8
  export { PositronicManifest } from './manifest.js';
8
9
  export { default as api } from './api/index.js';
@@ -265,6 +265,12 @@ export var MonitorDO = /*#__PURE__*/ function(DurableObject) {
265
265
  } catch (e) {
266
266
  // Column already exists
267
267
  }
268
+ // Migration: add user_id column to brain_runs for ownership tracking
269
+ try {
270
+ _this.storage.exec("ALTER TABLE brain_runs ADD COLUMN user_id TEXT");
271
+ } catch (e) {
272
+ // Column already exists
273
+ }
268
274
  return _this;
269
275
  }
270
276
  _create_class(MonitorDO, [
@@ -310,7 +316,9 @@ export var MonitorDO = /*#__PURE__*/ function(DurableObject) {
310
316
  } else {
311
317
  // All other events have brainTitle/brainDescription (BrainBaseEvent types)
312
318
  var brainEvent = event;
313
- this.storage.exec("\n INSERT INTO brain_runs (\n run_id, brain_title, brain_description, type, status,\n options, error, created_at, started_at, completed_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(run_id) DO UPDATE SET\n type = excluded.type,\n status = excluded.status,\n error = excluded.error,\n completed_at = excluded.completed_at\n ", brainRunId, brainEvent.brainTitle, brainEvent.brainDescription || null, event.type, status, JSON.stringify(event.options || {}), error, currentTime, startTime, completeTime);
319
+ // Extract user_id from START event (set once, never updated)
320
+ var userId = event.type === BRAIN_EVENTS.START ? event.currentUser.id : null;
321
+ this.storage.exec("\n INSERT INTO brain_runs (\n run_id, brain_title, brain_description, type, status,\n options, error, created_at, started_at, completed_at, user_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(run_id) DO UPDATE SET\n type = excluded.type,\n status = excluded.status,\n error = excluded.error,\n completed_at = excluded.completed_at\n ", brainRunId, brainEvent.brainTitle, brainEvent.brainDescription || null, event.type, status, JSON.stringify(event.options || {}), error, currentTime, startTime, completeTime, userId);
314
322
  }
315
323
  // Clean up registrations and in-memory state when brain terminates
316
324
  if (isTerminalStatus) {
@@ -445,10 +453,12 @@ export var MonitorDO = /*#__PURE__*/ function(DurableObject) {
445
453
  {
446
454
  /**
447
455
  * Get detailed information about a specific brain run
448
- * Returns null if run not found
456
+ * Returns null if run not found or not owned by userId
457
+ * Pass null for userId to skip ownership check (root access)
449
458
  */ key: "getRun",
450
459
  value: function getRun(brainRunId) {
451
- var results = this.storage.exec("\n SELECT\n run_id as brainRunId,\n brain_title as brainTitle,\n brain_description as brainDescription,\n type,\n status,\n options,\n error,\n created_at as createdAt,\n started_at as startedAt,\n completed_at as completedAt\n FROM brain_runs\n WHERE run_id = ?\n ", brainRunId).toArray();
460
+ var userId = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : null;
461
+ var results = this.storage.exec("\n SELECT\n run_id as brainRunId,\n brain_title as brainTitle,\n brain_description as brainDescription,\n type,\n status,\n options,\n error,\n created_at as createdAt,\n started_at as startedAt,\n completed_at as completedAt,\n user_id as userId\n FROM brain_runs\n WHERE run_id = ?\n AND (? IS NULL OR user_id = ?)\n ", brainRunId, userId, userId).toArray();
452
462
  if (results.length === 0) {
453
463
  return null;
454
464
  }
@@ -461,20 +471,23 @@ export var MonitorDO = /*#__PURE__*/ function(DurableObject) {
461
471
  }
462
472
  },
463
473
  {
464
- // Update history method parameter and query
465
- key: "history",
474
+ /**
475
+ * Get run history for a brain.
476
+ * Pass null for userId to skip ownership filter (root access).
477
+ */ key: "history",
466
478
  value: function history(brainTitle) {
467
- var limit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 10;
468
- // Renamed parameter
469
- // Update select query with aliases and filter by brain_title
470
- return this.storage.exec("\n SELECT\n run_id as brainRunId,\n brain_title as brainTitle,\n brain_description as brainDescription,\n type,\n status,\n options,\n error,\n created_at as createdAt,\n started_at as startedAt,\n completed_at as completedAt\n FROM brain_runs\n WHERE brain_title = ? -- Filter by new column name\n ORDER BY created_at DESC\n LIMIT ?\n ", brainTitle, limit).toArray(); // Use renamed parameter
479
+ var limit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 10, userId = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : null;
480
+ return this.storage.exec("\n SELECT\n run_id as brainRunId,\n brain_title as brainTitle,\n brain_description as brainDescription,\n type,\n status,\n options,\n error,\n created_at as createdAt,\n started_at as startedAt,\n completed_at as completedAt,\n user_id as userId\n FROM brain_runs\n WHERE brain_title = ?\n AND (? IS NULL OR user_id = ?)\n ORDER BY created_at DESC\n LIMIT ?\n ", brainTitle, userId, userId, limit).toArray();
471
481
  }
472
482
  },
473
483
  {
474
- // Get active brain runs for a specific brain (running, paused, or waiting)
475
- key: "activeRuns",
484
+ /**
485
+ * Get active brain runs for a specific brain (running, paused, or waiting).
486
+ * Pass null for userId to skip ownership filter (root access).
487
+ */ key: "activeRuns",
476
488
  value: function activeRuns(brainTitle) {
477
- return this.storage.exec("\n SELECT\n run_id as brainRunId,\n brain_title as brainTitle,\n brain_description as brainDescription,\n type,\n status,\n options,\n error,\n created_at as createdAt,\n started_at as startedAt,\n completed_at as completedAt\n FROM brain_runs\n WHERE brain_title = ? AND status IN (?, ?, ?)\n ORDER BY created_at DESC\n ", brainTitle, STATUS.RUNNING, STATUS.PAUSED, STATUS.WAITING).toArray();
489
+ var userId = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : null;
490
+ return this.storage.exec("\n SELECT\n run_id as brainRunId,\n brain_title as brainTitle,\n brain_description as brainDescription,\n type,\n status,\n options,\n error,\n created_at as createdAt,\n started_at as startedAt,\n completed_at as completedAt,\n user_id as userId\n FROM brain_runs\n WHERE brain_title = ? AND status IN (?, ?, ?)\n AND (? IS NULL OR user_id = ?)\n ORDER BY created_at DESC\n ", brainTitle, STATUS.RUNNING, STATUS.PAUSED, STATUS.WAITING, userId, userId).toArray();
478
491
  }
479
492
  },
480
493
  {
@@ -253,7 +253,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
253
253
  ]), _define_property(_this, "storage", void 0);
254
254
  _this.storage = state.storage.sql;
255
255
  // Initialize database schema
256
- _this.storage.exec("\n CREATE TABLE IF NOT EXISTS schedules (\n id TEXT PRIMARY KEY,\n brain_title TEXT NOT NULL,\n cron_expression TEXT NOT NULL,\n enabled INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n next_run_at INTEGER\n );\n\n CREATE INDEX IF NOT EXISTS idx_schedules_brain\n ON schedules(brain_title);\n\n CREATE INDEX IF NOT EXISTS idx_schedules_enabled\n ON schedules(enabled);\n\n CREATE TABLE IF NOT EXISTS scheduled_runs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n schedule_id TEXT NOT NULL,\n brain_run_id TEXT UNIQUE,\n status TEXT NOT NULL CHECK(status IN ('triggered', 'failed', 'complete')),\n ran_at INTEGER NOT NULL,\n completed_at INTEGER,\n error TEXT,\n FOREIGN KEY (schedule_id) REFERENCES schedules(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_runs_schedule\n ON scheduled_runs(schedule_id, ran_at DESC);\n ");
256
+ _this.storage.exec("\n CREATE TABLE IF NOT EXISTS schedules (\n id TEXT PRIMARY KEY,\n brain_title TEXT NOT NULL,\n cron_expression TEXT NOT NULL,\n enabled INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n next_run_at INTEGER,\n run_as_user_id TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_schedules_brain\n ON schedules(brain_title);\n\n CREATE INDEX IF NOT EXISTS idx_schedules_enabled\n ON schedules(enabled);\n\n CREATE TABLE IF NOT EXISTS scheduled_runs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n schedule_id TEXT NOT NULL,\n brain_run_id TEXT UNIQUE,\n status TEXT NOT NULL CHECK(status IN ('triggered', 'failed', 'complete')),\n ran_at INTEGER NOT NULL,\n completed_at INTEGER,\n error TEXT,\n FOREIGN KEY (schedule_id) REFERENCES schedules(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_runs_schedule\n ON scheduled_runs(schedule_id, ran_at DESC);\n ");
257
257
  // Migration: add timezone column for existing DOs
258
258
  try {
259
259
  _this.storage.exec("ALTER TABLE schedules ADD COLUMN timezone TEXT NOT NULL DEFAULT 'UTC'");
@@ -266,7 +266,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
266
266
  {
267
267
  key: "createSchedule",
268
268
  value: function createSchedule(brainTitle, cronExpression) {
269
- var timezone = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 'UTC';
269
+ var timezone = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 'UTC', runAsUserId = arguments.length > 3 ? arguments[3] : void 0;
270
270
  return _async_to_generator(function() {
271
271
  var id, createdAt, alarm, nextRunAt;
272
272
  return _ts_generator(this, function(_state) {
@@ -299,7 +299,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
299
299
  // Note: Cron expression is validated at the API level before calling this method
300
300
  // Calculate next run time
301
301
  nextRunAt = this.calculateNextRunTime(cronExpression, createdAt, timezone);
302
- this.storage.exec("INSERT INTO schedules (id, brain_title, cron_expression, timezone, enabled, created_at, next_run_at)\n VALUES (?, ?, ?, ?, 1, ?, ?)", id, brainTitle, cronExpression, timezone, createdAt, nextRunAt);
302
+ this.storage.exec("INSERT INTO schedules (id, brain_title, cron_expression, timezone, enabled, created_at, next_run_at, run_as_user_id)\n VALUES (?, ?, ?, ?, 1, ?, ?, ?)", id, brainTitle, cronExpression, timezone, createdAt, nextRunAt, runAsUserId);
303
303
  return [
304
304
  2,
305
305
  {
@@ -309,7 +309,8 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
309
309
  timezone: timezone,
310
310
  enabled: true,
311
311
  createdAt: createdAt,
312
- nextRunAt: nextRunAt
312
+ nextRunAt: nextRunAt,
313
+ runAsUserId: runAsUserId
313
314
  }
314
315
  ];
315
316
  }
@@ -323,7 +324,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
323
324
  return _async_to_generator(function() {
324
325
  var results, result;
325
326
  return _ts_generator(this, function(_state) {
326
- results = this.storage.exec("SELECT id, brain_title, cron_expression, timezone, enabled, created_at, next_run_at\n FROM schedules WHERE id = ?", scheduleId).toArray();
327
+ results = this.storage.exec("SELECT id, brain_title, cron_expression, timezone, enabled, created_at, next_run_at, run_as_user_id\n FROM schedules WHERE id = ?", scheduleId).toArray();
327
328
  if (results.length === 0) {
328
329
  return [
329
330
  2,
@@ -340,7 +341,8 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
340
341
  timezone: result.timezone || 'UTC',
341
342
  enabled: result.enabled === 1,
342
343
  createdAt: result.created_at,
343
- nextRunAt: result.next_run_at
344
+ nextRunAt: result.next_run_at,
345
+ runAsUserId: result.run_as_user_id
344
346
  }
345
347
  ];
346
348
  });
@@ -379,7 +381,10 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
379
381
  },
380
382
  {
381
383
  key: "listSchedules",
382
- value: function listSchedules() {
384
+ value: /**
385
+ * List all schedules. Pass null for userId to skip ownership filter (root access).
386
+ */ function listSchedules() {
387
+ var userId = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : null;
383
388
  return _async_to_generator(function() {
384
389
  var alarm, schedules;
385
390
  return _ts_generator(this, function(_state) {
@@ -416,7 +421,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
416
421
  _state.sent();
417
422
  _state.label = 4;
418
423
  case 4:
419
- schedules = this.storage.exec("SELECT id, brain_title, cron_expression, timezone, enabled, created_at, next_run_at\n FROM schedules\n ORDER BY created_at DESC").toArray().map(function(row) {
424
+ schedules = this.storage.exec("SELECT id, brain_title, cron_expression, timezone, enabled, created_at, next_run_at, run_as_user_id\n FROM schedules\n WHERE (? IS NULL OR run_as_user_id = ?)\n ORDER BY created_at DESC", userId, userId).toArray().map(function(row) {
420
425
  return {
421
426
  id: row.id,
422
427
  brainTitle: row.brain_title,
@@ -424,7 +429,8 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
424
429
  timezone: row.timezone || 'UTC',
425
430
  enabled: row.enabled === 1,
426
431
  createdAt: row.created_at,
427
- nextRunAt: row.next_run_at
432
+ nextRunAt: row.next_run_at,
433
+ runAsUserId: row.run_as_user_id
428
434
  };
429
435
  });
430
436
  return [
@@ -441,18 +447,23 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
441
447
  },
442
448
  {
443
449
  key: "getAllRuns",
444
- value: function getAllRuns(scheduleId) {
445
- var limit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 100;
450
+ value: /**
451
+ * Get all scheduled runs. Pass null for userId to skip ownership filter (root access).
452
+ * When userId is set, only returns runs for schedules owned by that user.
453
+ */ function getAllRuns(scheduleId) {
454
+ var limit = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 100, userId = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : null;
446
455
  return _async_to_generator(function() {
447
456
  var _this_storage, _this_storage1, query, params, runs, countQuery, countParams, countResult, count;
448
457
  return _ts_generator(this, function(_state) {
449
- query = "\n SELECT id, schedule_id, brain_run_id, status, ran_at, completed_at, error\n FROM scheduled_runs\n ";
458
+ query = "\n SELECT sr.id, sr.schedule_id, sr.brain_run_id, sr.status, sr.ran_at, sr.completed_at, sr.error\n FROM scheduled_runs sr\n JOIN schedules s ON sr.schedule_id = s.id\n WHERE 1=1\n ";
450
459
  params = [];
451
460
  if (scheduleId) {
452
- query += " WHERE schedule_id = ?";
461
+ query += " AND sr.schedule_id = ?";
453
462
  params.push(scheduleId);
454
463
  }
455
- query += " ORDER BY ran_at DESC LIMIT ?";
464
+ query += " AND (? IS NULL OR s.run_as_user_id = ?)";
465
+ params.push(userId, userId);
466
+ query += " ORDER BY sr.ran_at DESC LIMIT ?";
456
467
  params.push(limit);
457
468
  runs = (_this_storage = this.storage).exec.apply(_this_storage, [
458
469
  query
@@ -468,12 +479,14 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
468
479
  };
469
480
  });
470
481
  // Get total count
471
- countQuery = "SELECT COUNT(*) as count FROM scheduled_runs";
482
+ countQuery = "\n SELECT COUNT(*) as count\n FROM scheduled_runs sr\n JOIN schedules s ON sr.schedule_id = s.id\n WHERE 1=1\n ";
472
483
  countParams = [];
473
484
  if (scheduleId) {
474
- countQuery += " WHERE schedule_id = ?";
485
+ countQuery += " AND sr.schedule_id = ?";
475
486
  countParams.push(scheduleId);
476
487
  }
488
+ countQuery += " AND (? IS NULL OR s.run_as_user_id = ?)";
489
+ countParams.push(userId, userId);
477
490
  countResult = (_this_storage1 = this.storage).exec.apply(_this_storage1, [
478
491
  countQuery
479
492
  ].concat(_to_consumable_array(countParams))).one();
@@ -494,7 +507,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
494
507
  value: // Handle the alarm trigger - runs every minute in a perpetual cycle
495
508
  function alarm() {
496
509
  return _async_to_generator(function() {
497
- var now, dueSchedules, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, schedule, scheduleId, brainTitle, cronExpression, brainRunId, error, errorMessage, timezone, nextRunAt, err;
510
+ var now, dueSchedules, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, schedule, scheduleId, brainTitle, cronExpression, runAsUserId, brainRunId, error, errorMessage, timezone, nextRunAt, err;
498
511
  return _ts_generator(this, function(_state) {
499
512
  switch(_state.label){
500
513
  case 0:
@@ -509,7 +522,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
509
522
  // checking every minute ensures we never miss a scheduled run.
510
523
  // Get all enabled schedules that are due
511
524
  now = Date.now();
512
- dueSchedules = this.storage.exec("SELECT id, brain_title, cron_expression, timezone\n FROM schedules\n WHERE enabled = 1 AND next_run_at <= ?", now).toArray();
525
+ dueSchedules = this.storage.exec("SELECT id, brain_title, cron_expression, timezone, run_as_user_id\n FROM schedules\n WHERE enabled = 1 AND next_run_at <= ?", now).toArray();
513
526
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
514
527
  _state.label = 1;
515
528
  case 1:
@@ -530,6 +543,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
530
543
  scheduleId = schedule.id;
531
544
  brainTitle = schedule.brain_title;
532
545
  cronExpression = schedule.cron_expression;
546
+ runAsUserId = schedule.run_as_user_id;
533
547
  _state.label = 3;
534
548
  case 3:
535
549
  _state.trys.push([
@@ -540,7 +554,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
540
554
  ]);
541
555
  return [
542
556
  4,
543
- this.triggerBrainRun(brainTitle)
557
+ this.triggerBrainRun(brainTitle, runAsUserId)
544
558
  ];
545
559
  case 4:
546
560
  brainRunId = _state.sent();
@@ -630,7 +644,7 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
630
644
  },
631
645
  {
632
646
  key: "triggerBrainRun",
633
- value: function triggerBrainRun(brainTitle) {
647
+ value: function triggerBrainRun(brainTitle, runAsUserId) {
634
648
  return _async_to_generator(function() {
635
649
  var brainRunId, namespace, doId, stub;
636
650
  return _ts_generator(this, function(_state) {
@@ -640,10 +654,12 @@ export var ScheduleDO = /*#__PURE__*/ function(DurableObject) {
640
654
  namespace = this.env.BRAIN_RUNNER_DO;
641
655
  doId = namespace.idFromName(brainRunId);
642
656
  stub = namespace.get(doId);
643
- console.log("[ScheduleDO] Triggering brain run ".concat(brainTitle, " with id ").concat(brainRunId));
657
+ console.log("[ScheduleDO] Triggering brain run ".concat(brainTitle, " with id ").concat(brainRunId, " as user ").concat(runAsUserId));
644
658
  return [
645
659
  4,
646
- stub.start(brainTitle, brainRunId)
660
+ stub.start(brainTitle, brainRunId, {
661
+ id: runAsUserId
662
+ })
647
663
  ];
648
664
  case 1:
649
665
  _state.sent();
@@ -4,6 +4,11 @@ export interface AuthContext {
4
4
  userId: string | null;
5
5
  isRoot: boolean;
6
6
  }
7
+ /**
8
+ * Middleware that restricts access to root users only.
9
+ * Returns 403 if the authenticated user is not root.
10
+ */
11
+ export declare function requireRoot(): MiddlewareHandler;
7
12
  declare module 'hono' {
8
13
  interface ContextVariableMap {
9
14
  auth: AuthContext;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-middleware.d.ts","sourceRoot":"","sources":["../../../src/api/auth-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAGD,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,IAAI,EAAE,WAAW,CAAC;KACnB;CACF;AAwBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAsF1E"}
1
+ {"version":3,"file":"auth-middleware.d.ts","sourceRoot":"","sources":["../../../src/api/auth-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CAQ/C;AAGD,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,IAAI,EAAE,WAAW,CAAC;KACnB;CACF;AAwBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAsF1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"brains.d.ts","sourceRoot":"","sources":["../../../src/api/brains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAC;AAM1C,OAAO,KAAK,EAAE,QAAQ,EAAiD,MAAM,YAAY,CAAC;AAE1F,QAAA,MAAM,MAAM;cAAwB,QAAQ;yCAAK,CAAC;AA+lBlD,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"brains.d.ts","sourceRoot":"","sources":["../../../src/api/brains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAC;AAM1C,OAAO,KAAK,EAAE,QAAQ,EAAiD,MAAM,YAAY,CAAC;AAE1F,QAAA,MAAM,MAAM;cAAwB,QAAQ;yCAAK,CAAC;AAooBlD,eAAe,MAAM,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../../src/api/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiD3C,QAAA,MAAM,OAAO;cAAwB,QAAQ;yCAAK,CAAC;AA0SnD,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../../src/api/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAkD3C,QAAA,MAAM,OAAO;cAAwB,QAAQ;yCAAK,CAAC;AA6SnD,eAAe,OAAO,CAAC"}
@@ -24,9 +24,13 @@ export declare class BrainRunnerDO extends DurableObject<Env> {
24
24
  private eventStreamAdapter;
25
25
  private abortController;
26
26
  private pageAdapter;
27
+ private runOwnerTableInitialized;
27
28
  private signalsTableInitialized;
28
29
  private waitTimeoutTableInitialized;
29
30
  constructor(state: DurableObjectState, env: Env);
31
+ private initializeRunOwnerTable;
32
+ private storeRunOwner;
33
+ private getRunOwner;
30
34
  private initializeSignalsTable;
31
35
  private initializeWaitTimeoutTable;
32
36
  storeWaitTimeout(brainRunId: string, timeoutAt: number): void;
@@ -74,7 +78,9 @@ export declare class BrainRunnerDO extends DurableObject<Env> {
74
78
  message: string;
75
79
  }>;
76
80
  alarm(): Promise<void>;
77
- start(brainTitle: string, brainRunId: string, initialData?: Record<string, any>): Promise<void>;
81
+ start(brainTitle: string, brainRunId: string, currentUser: {
82
+ id: string;
83
+ }, initialData?: Record<string, any>): Promise<void>;
78
84
  /**
79
85
  * Wake up (resume) a brain from a previous execution point.
80
86
  * Webhook response data comes from signals, not as a parameter.
@@ -1 +1 @@
1
- {"version":3,"file":"brain-runner-do.d.ts","sourceRoot":"","sources":["../../src/brain-runner-do.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"brain-runner-do.d.ts","sourceRoot":"","sources":["../../src/brain-runner-do.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { R2Bucket } from '@cloudflare/workers-types';
2
+ import type { StoreProvider } from '@positronic/core';
3
+ /**
4
+ * Create a StoreProvider backed by Cloudflare R2.
5
+ *
6
+ * Key resolution:
7
+ * shared: store/{brainTitle}/{key}.json
8
+ * per-user: store/{brainTitle}/user/{userId}/{key}.json
9
+ *
10
+ * The factory receives the store schema, brain title, and currentUser,
11
+ * and returns a typed Store<any> with full key resolution built in.
12
+ */
13
+ export declare function createR2Backend(bucket: R2Bucket): StoreProvider;
14
+ //# sourceMappingURL=create-r2-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-r2-store.d.ts","sourceRoot":"","sources":["../../src/create-r2-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAS,MAAM,kBAAkB,CAAC;AAe7D;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,aAAa,CA+C/D"}
@@ -4,6 +4,7 @@ export { ScheduleDO } from './schedule-do.js';
4
4
  export { AuthDO } from './auth-do.js';
5
5
  export { GovernorDO } from './governor-do.js';
6
6
  export { rateGoverned, setGovernorBinding } from './governor-client-wrapper.js';
7
+ export { createR2Backend } from './create-r2-store.js';
7
8
  export { PositronicManifest, type BrainMetadata } from './manifest.js';
8
9
  export { default as api } from './api/index.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EACd,WAAW,EACX,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EACd,WAAW,EACX,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,gBAAgB,CAAC"}
@@ -13,11 +13,20 @@ export declare class MonitorDO extends DurableObject<Env> {
13
13
  getLastEvent(brainRunId: string): Record<string, SqlStorageValue> | null;
14
14
  /**
15
15
  * Get detailed information about a specific brain run
16
- * Returns null if run not found
16
+ * Returns null if run not found or not owned by userId
17
+ * Pass null for userId to skip ownership check (root access)
17
18
  */
18
- getRun(brainRunId: string): any;
19
- history(brainTitle: string, limit?: number): Record<string, SqlStorageValue>[];
20
- activeRuns(brainTitle: string): Record<string, SqlStorageValue>[];
19
+ getRun(brainRunId: string, userId?: string | null): any;
20
+ /**
21
+ * Get run history for a brain.
22
+ * Pass null for userId to skip ownership filter (root access).
23
+ */
24
+ history(brainTitle: string, limit?: number, userId?: string | null): Record<string, SqlStorageValue>[];
25
+ /**
26
+ * Get active brain runs for a specific brain (running, paused, or waiting).
27
+ * Pass null for userId to skip ownership filter (root access).
28
+ */
29
+ activeRuns(brainTitle: string, userId?: string | null): Record<string, SqlStorageValue>[];
21
30
  /**
22
31
  * Register a webhook to wait for
23
32
  * Called when a brain emits a WEBHOOK event
@@ -1 +1 @@
1
- {"version":3,"file":"monitor-do.d.ts","sourceRoot":"","sources":["../../src/monitor-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAQnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,GAAG;CAEnB;AAED,qBAAa,SAAU,SAAQ,aAAa,CAAC,GAAG,CAAC;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,kBAAkB,CAA4B;IAEtD,OAAO,CAAC,QAAQ,CAAwC;gBAE5C,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG;IAyE/C,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC;YAmHzB,sBAAsB;IA4B9B,KAAK,CAAC,OAAO,EAAE,OAAO;IAmE5B,YAAY,CAAC,UAAU,EAAE,MAAM;IAa/B;;;OAGG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM;IAqCzB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW;IA6B9C,UAAU,CAAC,UAAU,EAAE,MAAM;IA2B7B;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAcpF;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAmBvG;;;OAGG;IACH,yBAAyB,CAAC,UAAU,EAAE,MAAM;IAU5C;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAgB/D;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM;IAU3B;;;OAGG;IACH,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAezD;;;OAGG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM;CAU1C"}
1
+ {"version":3,"file":"monitor-do.d.ts","sourceRoot":"","sources":["../../src/monitor-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAQnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,GAAG;CAEnB;AAED,qBAAa,SAAU,SAAQ,aAAa,CAAC,GAAG,CAAC;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,kBAAkB,CAA4B;IAEtD,OAAO,CAAC,QAAQ,CAAwC;gBAE5C,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG;IAgF/C,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC;YA0HzB,sBAAsB;IA4B9B,KAAK,CAAC,OAAO,EAAE,OAAO;IAmE5B,YAAY,CAAC,UAAU,EAAE,MAAM;IAa/B;;;;OAIG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW;IAwCvD;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW;IA8B5E;;;OAGG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW;IA+B3D;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAcpF;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAmBvG;;;OAGG;IACH,yBAAyB,CAAC,UAAU,EAAE,MAAM;IAU5C;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAgB/D;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM;IAU3B;;;OAGG;IACH,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAezD;;;OAGG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM;CAU1C"}
@@ -14,6 +14,7 @@ interface Schedule {
14
14
  enabled: boolean;
15
15
  createdAt: number;
16
16
  nextRunAt?: number;
17
+ runAsUserId: string;
17
18
  }
18
19
  interface ScheduledRun {
19
20
  id: number;
@@ -27,14 +28,21 @@ interface ScheduledRun {
27
28
  export declare class ScheduleDO extends DurableObject<Env> {
28
29
  private readonly storage;
29
30
  constructor(state: DurableObjectState, env: Env);
30
- createSchedule(brainTitle: string, cronExpression: string, timezone?: string): Promise<Schedule>;
31
+ createSchedule(brainTitle: string, cronExpression: string, timezone: string | undefined, runAsUserId: string): Promise<Schedule>;
31
32
  getSchedule(scheduleId: string): Promise<Schedule | null>;
32
33
  deleteSchedule(scheduleId: string): Promise<boolean>;
33
- listSchedules(): Promise<{
34
+ /**
35
+ * List all schedules. Pass null for userId to skip ownership filter (root access).
36
+ */
37
+ listSchedules(userId?: string | null): Promise<{
34
38
  schedules: Schedule[];
35
39
  count: number;
36
40
  }>;
37
- getAllRuns(scheduleId?: string, limit?: number): Promise<{
41
+ /**
42
+ * Get all scheduled runs. Pass null for userId to skip ownership filter (root access).
43
+ * When userId is set, only returns runs for schedules owned by that user.
44
+ */
45
+ getAllRuns(scheduleId?: string, limit?: number, userId?: string | null): Promise<{
38
46
  runs: ScheduledRun[];
39
47
  count: number;
40
48
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"schedule-do.d.ts","sourceRoot":"","sources":["../../src/schedule-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,WAAW,GAAG;IAClB,eAAe,EAAE,sBAAsB,CAAC,aAAa,CAAC,CAAC;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,UAAW,SAAQ,aAAa,CAAC,GAAG,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;gBAEzB,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG;IA4CzC,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,QAAQ,GAAE,MAAc,GACvB,OAAO,CAAC,QAAQ,CAAC;IAmCd,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA0BzD,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYpD,aAAa,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAmClE,UAAU,CACd,UAAU,CAAC,EAAE,MAAM,EACnB,KAAK,GAAE,MAAY,GAClB,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA4C7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA2Ed,eAAe;IAcvB,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C7D,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,oBAAoB;IAMtB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;CAG5C"}
1
+ {"version":3,"file":"schedule-do.d.ts","sourceRoot":"","sources":["../../src/schedule-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,WAAW,GAAG;IAClB,eAAe,EAAE,sBAAsB,CAAC,aAAa,CAAC,CAAC;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,UAAW,SAAQ,aAAa,CAAC,GAAG,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;gBAEzB,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG;IA8CzC,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,YAAQ,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC;IAqCd,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA2BzD,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAY1D;;OAEG;IACG,aAAa,CAAC,MAAM,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAuCpG;;;OAGG;IACG,UAAU,CACd,UAAU,CAAC,EAAE,MAAM,EACnB,KAAK,GAAE,MAAY,EACnB,MAAM,GAAE,MAAM,GAAG,IAAW,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAyD7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA4Ed,eAAe;IAcvB,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C7D,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,oBAAoB;IAMtB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;CAG5C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@positronic/cloudflare",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -31,9 +31,9 @@
31
31
  "clean": "rm -rf tsconfig.tsbuildinfo dist"
32
32
  },
33
33
  "dependencies": {
34
- "@positronic/core": "^0.0.68",
35
- "@positronic/spec": "^0.0.68",
36
- "@positronic/template-new-project": "^0.0.68",
34
+ "@positronic/core": "^0.0.69",
35
+ "@positronic/spec": "^0.0.69",
36
+ "@positronic/template-new-project": "^0.0.69",
37
37
  "aws4fetch": "^1.0.18",
38
38
  "caz": "^2.0.0",
39
39
  "croner": "^10.0.1",