@positronic/cloudflare 0.0.66 → 0.0.68

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.
@@ -155,7 +155,7 @@ function _ts_generator(thisArg, body) {
155
155
  }
156
156
  import { Hono } from 'hono';
157
157
  import { v4 as uuidv4 } from 'uuid';
158
- import { parseCronExpression } from 'cron-schedule';
158
+ import { Cron } from 'croner';
159
159
  import Fuse from 'fuse.js';
160
160
  import { isSignalValid, brainMachineDefinition } from '@positronic/core';
161
161
  import { getManifest } from '../brain-runner-do.js';
@@ -922,15 +922,15 @@ brains.get('/', function(context) {
922
922
  // Create a new schedule
923
923
  brains.post('/schedules', function(context) {
924
924
  return _async_to_generator(function() {
925
- var body, cronExpression, identifier, manifest, resolution, brain, brainTitle, scheduleId, scheduleStub, schedule, error, errorMessage;
925
+ var body, cronExpression, identifier, manifest, resolution, brain, brainTitle, scheduleDoId, scheduleStub, timezone, schedule, error, errorMessage;
926
926
  return _ts_generator(this, function(_state) {
927
927
  switch(_state.label){
928
928
  case 0:
929
929
  _state.trys.push([
930
930
  0,
931
- 3,
931
+ 5,
932
932
  ,
933
- 4
933
+ 6
934
934
  ]);
935
935
  return [
936
936
  4,
@@ -959,7 +959,7 @@ brains.post('/schedules', function(context) {
959
959
  }
960
960
  // Validate cron expression before calling DO
961
961
  try {
962
- parseCronExpression(cronExpression);
962
+ new Cron(cronExpression);
963
963
  } catch (e) {
964
964
  return [
965
965
  2,
@@ -1001,19 +1001,46 @@ brains.post('/schedules', function(context) {
1001
1001
  brain = resolution.brain;
1002
1002
  brainTitle = brain.title || identifier;
1003
1003
  // Get the schedule singleton instance
1004
- scheduleId = context.env.SCHEDULE_DO.idFromName('singleton');
1005
- scheduleStub = context.env.SCHEDULE_DO.get(scheduleId);
1004
+ scheduleDoId = context.env.SCHEDULE_DO.idFromName('singleton');
1005
+ scheduleStub = context.env.SCHEDULE_DO.get(scheduleDoId);
1006
+ // Determine timezone: use provided value, fall back to project timezone
1007
+ timezone = body.timezone;
1008
+ if (!!timezone) return [
1009
+ 3,
1010
+ 3
1011
+ ];
1006
1012
  return [
1007
1013
  4,
1008
- scheduleStub.createSchedule(brainTitle, cronExpression)
1014
+ scheduleStub.getProjectTimezone()
1009
1015
  ];
1010
1016
  case 2:
1017
+ timezone = _state.sent();
1018
+ _state.label = 3;
1019
+ case 3:
1020
+ // Validate timezone
1021
+ try {
1022
+ Intl.DateTimeFormat(undefined, {
1023
+ timeZone: timezone
1024
+ });
1025
+ } catch (e) {
1026
+ return [
1027
+ 2,
1028
+ context.json({
1029
+ error: "Invalid timezone: ".concat(timezone)
1030
+ }, 400)
1031
+ ];
1032
+ }
1033
+ return [
1034
+ 4,
1035
+ scheduleStub.createSchedule(brainTitle, cronExpression, timezone)
1036
+ ];
1037
+ case 4:
1011
1038
  schedule = _state.sent();
1012
1039
  return [
1013
1040
  2,
1014
1041
  context.json(schedule, 201)
1015
1042
  ];
1016
- case 3:
1043
+ case 5:
1017
1044
  error = _state.sent();
1018
1045
  errorMessage = _instanceof(error, Error) ? error.message : 'Failed to create schedule';
1019
1046
  return [
@@ -1022,7 +1049,7 @@ brains.post('/schedules', function(context) {
1022
1049
  error: errorMessage
1023
1050
  }, 400)
1024
1051
  ];
1025
- case 4:
1052
+ case 6:
1026
1053
  return [
1027
1054
  2
1028
1055
  ];
@@ -1078,6 +1105,84 @@ brains.get('/schedules/runs', function(context) {
1078
1105
  });
1079
1106
  })();
1080
1107
  });
1108
+ // Get project timezone
1109
+ brains.get('/schedules/timezone', function(context) {
1110
+ return _async_to_generator(function() {
1111
+ var scheduleDoId, scheduleStub, timezone;
1112
+ return _ts_generator(this, function(_state) {
1113
+ switch(_state.label){
1114
+ case 0:
1115
+ scheduleDoId = context.env.SCHEDULE_DO.idFromName('singleton');
1116
+ scheduleStub = context.env.SCHEDULE_DO.get(scheduleDoId);
1117
+ return [
1118
+ 4,
1119
+ scheduleStub.getProjectTimezone()
1120
+ ];
1121
+ case 1:
1122
+ timezone = _state.sent();
1123
+ return [
1124
+ 2,
1125
+ context.json({
1126
+ timezone: timezone
1127
+ })
1128
+ ];
1129
+ }
1130
+ });
1131
+ })();
1132
+ });
1133
+ // Set project timezone
1134
+ brains.put('/schedules/timezone', function(context) {
1135
+ return _async_to_generator(function() {
1136
+ var body, timezone, scheduleDoId, scheduleStub;
1137
+ return _ts_generator(this, function(_state) {
1138
+ switch(_state.label){
1139
+ case 0:
1140
+ return [
1141
+ 4,
1142
+ context.req.json()
1143
+ ];
1144
+ case 1:
1145
+ body = _state.sent();
1146
+ timezone = body.timezone;
1147
+ if (!timezone) {
1148
+ return [
1149
+ 2,
1150
+ context.json({
1151
+ error: 'Missing required field "timezone"'
1152
+ }, 400)
1153
+ ];
1154
+ }
1155
+ // Validate timezone
1156
+ try {
1157
+ Intl.DateTimeFormat(undefined, {
1158
+ timeZone: timezone
1159
+ });
1160
+ } catch (e) {
1161
+ return [
1162
+ 2,
1163
+ context.json({
1164
+ error: "Invalid timezone: ".concat(timezone)
1165
+ }, 400)
1166
+ ];
1167
+ }
1168
+ scheduleDoId = context.env.SCHEDULE_DO.idFromName('singleton');
1169
+ scheduleStub = context.env.SCHEDULE_DO.get(scheduleDoId);
1170
+ return [
1171
+ 4,
1172
+ scheduleStub.setProjectTimezone(timezone)
1173
+ ];
1174
+ case 2:
1175
+ _state.sent();
1176
+ return [
1177
+ 2,
1178
+ context.json({
1179
+ timezone: timezone
1180
+ })
1181
+ ];
1182
+ }
1183
+ });
1184
+ })();
1185
+ });
1081
1186
  // Delete a schedule
1082
1187
  brains.delete('/schedules/:scheduleId', function(context) {
1083
1188
  return _async_to_generator(function() {
@@ -298,6 +298,7 @@ import { TimeoutAdapter } from './timeout-adapter.js';
298
298
  import { PageAdapter } from './page-adapter.js';
299
299
  import { EventLoader } from './event-loader.js';
300
300
  import { createPagesService } from './pages-service.js';
301
+ import { setGovernorBinding, rateGoverned } from './governor-client-wrapper.js';
301
302
  import { CloudflareR2Loader } from './r2-loader.js';
302
303
  import { createResources } from '@positronic/core';
303
304
  var manifest = null;
@@ -1055,6 +1056,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1055
1056
  env = this.buildRuntimeEnv();
1056
1057
  // Create pages service for brain to use
1057
1058
  pagesService = createPagesService(brainRunId, this.env.RESOURCES_BUCKET, monitorDOStub, env);
1059
+ setGovernorBinding(this.env.GOVERNOR_DO);
1058
1060
  if (!brainRunner) {
1059
1061
  throw new Error('BrainRunner not initialized');
1060
1062
  }
@@ -1076,7 +1078,9 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1076
1078
  signalProvider = new CloudflareSignalProvider(function(filter) {
1077
1079
  return _this.getAndConsumeSignals(filter);
1078
1080
  });
1079
- runnerWithResources = runnerWithResources.withSignalProvider(signalProvider);
1081
+ runnerWithResources = runnerWithResources.withSignalProvider(signalProvider).withGovernor(function(c) {
1082
+ return rateGoverned(c);
1083
+ });
1080
1084
  // Extract options from initialData if present
1081
1085
  options = initialData === null || initialData === void 0 ? void 0 : initialData.options;
1082
1086
  initialState = initialData && !initialData.options ? initialData : {};
@@ -1233,6 +1237,7 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1233
1237
  env = this.buildRuntimeEnv();
1234
1238
  // Create pages service for brain to use
1235
1239
  pagesService = createPagesService(brainRunId, this.env.RESOURCES_BUCKET, monitorDOStub, env);
1240
+ setGovernorBinding(this.env.GOVERNOR_DO);
1236
1241
  if (!brainRunner) {
1237
1242
  throw new Error('BrainRunner not initialized');
1238
1243
  }
@@ -1253,7 +1258,9 @@ export var BrainRunnerDO = /*#__PURE__*/ function(DurableObject) {
1253
1258
  signalProvider = new CloudflareSignalProvider(function(filter) {
1254
1259
  return _this.getAndConsumeSignals(filter);
1255
1260
  });
1256
- runnerWithResources = runnerWithResources.withSignalProvider(signalProvider);
1261
+ runnerWithResources = runnerWithResources.withSignalProvider(signalProvider).withGovernor(function(c) {
1262
+ return rateGoverned(c);
1263
+ });
1257
1264
  // Create abort controller for this run
1258
1265
  this.abortController = new AbortController();
1259
1266
  batchChunkAdapter = new BatchChunkAdapter(function(signal) {
@@ -416,7 +416,7 @@ import { createRequire } from 'module';
416
416
  }();
417
417
  function generateProject(projectName, projectDir, onSuccess) {
418
418
  return _async_to_generator(function() {
419
- var devPath, newProjectTemplatePath, cazOptions, templateSourcePath, require, templatePackageJsonPath, copiedNewProjectPkg;
419
+ var devPath, newProjectTemplatePath, cazOptions, templateSourcePath, inferredDevPath, require, templatePackageJsonPath, realPath, potentialRoot, copiedNewProjectPkg;
420
420
  return _ts_generator(this, function(_state) {
421
421
  switch(_state.label){
422
422
  case 0:
@@ -425,6 +425,7 @@ function generateProject(projectName, projectDir, onSuccess) {
425
425
  cazOptions = {
426
426
  name: projectName
427
427
  };
428
+ inferredDevPath = false;
428
429
  _state.label = 1;
429
430
  case 1:
430
431
  _state.trys.push([
@@ -448,6 +449,19 @@ function generateProject(projectName, projectDir, onSuccess) {
448
449
  require = createRequire(import.meta.url);
449
450
  templatePackageJsonPath = require.resolve('@positronic/template-new-project/package.json');
450
451
  templateSourcePath = path.dirname(templatePackageJsonPath);
452
+ // Auto-detect local workspace: if the template resolves to a real path
453
+ // outside node_modules, we're running from a local monorepo (e.g. the user's
454
+ // project uses file: references to @positronic/cloudflare). In that case,
455
+ // set POSITRONIC_LOCAL_PATH so the template generates file: references too.
456
+ realPath = fs.realpathSync(templateSourcePath);
457
+ if (!realPath.includes("".concat(path.sep, "node_modules").concat(path.sep))) {
458
+ potentialRoot = path.resolve(realPath, '..', '..');
459
+ if (fs.existsSync(path.join(potentialRoot, 'packages', 'core')) && fs.existsSync(path.join(potentialRoot, 'packages', 'cloudflare'))) {
460
+ devPath = potentialRoot;
461
+ process.env.POSITRONIC_LOCAL_PATH = potentialRoot;
462
+ inferredDevPath = true;
463
+ }
464
+ }
451
465
  }
452
466
  // Always copy to a temporary directory to avoid CAZ modifying our source
453
467
  // CAZ only supports local paths, GitHub repos, or ZIP URLs - not npm package names
@@ -485,6 +499,10 @@ function generateProject(projectName, projectDir, onSuccess) {
485
499
  5
486
500
  ];
487
501
  case 4:
502
+ // Clean up the temporarily set env var
503
+ if (inferredDevPath) {
504
+ delete process.env.POSITRONIC_LOCAL_PATH;
505
+ }
488
506
  // Clean up the temporary copied new project package
489
507
  fs.rmSync(newProjectTemplatePath, {
490
508
  recursive: true,
@@ -0,0 +1,364 @@
1
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
2
+ try {
3
+ var info = gen[key](arg);
4
+ var value = info.value;
5
+ } catch (error) {
6
+ reject(error);
7
+ return;
8
+ }
9
+ if (info.done) {
10
+ resolve(value);
11
+ } else {
12
+ Promise.resolve(value).then(_next, _throw);
13
+ }
14
+ }
15
+ function _async_to_generator(fn) {
16
+ return function() {
17
+ var self = this, args = arguments;
18
+ return new Promise(function(resolve, reject) {
19
+ var gen = fn.apply(self, args);
20
+ function _next(value) {
21
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22
+ }
23
+ function _throw(err) {
24
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
25
+ }
26
+ _next(undefined);
27
+ });
28
+ };
29
+ }
30
+ function _ts_generator(thisArg, body) {
31
+ var f, y, t, _ = {
32
+ label: 0,
33
+ sent: function() {
34
+ if (t[0] & 1) throw t[1];
35
+ return t[1];
36
+ },
37
+ trys: [],
38
+ ops: []
39
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
40
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
41
+ return this;
42
+ }), g;
43
+ function verb(n) {
44
+ return function(v) {
45
+ return step([
46
+ n,
47
+ v
48
+ ]);
49
+ };
50
+ }
51
+ function step(op) {
52
+ if (f) throw new TypeError("Generator is already executing.");
53
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
54
+ 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;
55
+ if (y = 0, t) op = [
56
+ op[0] & 2,
57
+ t.value
58
+ ];
59
+ switch(op[0]){
60
+ case 0:
61
+ case 1:
62
+ t = op;
63
+ break;
64
+ case 4:
65
+ _.label++;
66
+ return {
67
+ value: op[1],
68
+ done: false
69
+ };
70
+ case 5:
71
+ _.label++;
72
+ y = op[1];
73
+ op = [
74
+ 0
75
+ ];
76
+ continue;
77
+ case 7:
78
+ op = _.ops.pop();
79
+ _.trys.pop();
80
+ continue;
81
+ default:
82
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
83
+ _ = 0;
84
+ continue;
85
+ }
86
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
87
+ _.label = op[1];
88
+ break;
89
+ }
90
+ if (op[0] === 6 && _.label < t[1]) {
91
+ _.label = t[1];
92
+ t = op;
93
+ break;
94
+ }
95
+ if (t && _.label < t[2]) {
96
+ _.label = t[2];
97
+ _.ops.push(op);
98
+ break;
99
+ }
100
+ if (t[2]) _.ops.pop();
101
+ _.trys.pop();
102
+ continue;
103
+ }
104
+ op = body.call(thisArg, _);
105
+ } catch (e) {
106
+ op = [
107
+ 6,
108
+ e
109
+ ];
110
+ y = 0;
111
+ } finally{
112
+ f = t = 0;
113
+ }
114
+ if (op[0] & 5) throw op[1];
115
+ return {
116
+ value: op[0] ? op[1] : void 0,
117
+ done: true
118
+ };
119
+ }
120
+ }
121
+ import { estimateRequestTokens } from './token-estimator.js';
122
+ var governorNamespace = null;
123
+ export function setGovernorBinding(ns) {
124
+ governorNamespace = ns;
125
+ }
126
+ function getGovernorStub(identity) {
127
+ if (!governorNamespace) return null;
128
+ return governorNamespace.get(governorNamespace.idFromName(identity));
129
+ }
130
+ function computeIdentity(modelId, apiKey) {
131
+ return _async_to_generator(function() {
132
+ var data, hashBuffer, hashArray;
133
+ return _ts_generator(this, function(_state) {
134
+ switch(_state.label){
135
+ case 0:
136
+ data = new TextEncoder().encode("".concat(modelId, ":").concat(apiKey));
137
+ return [
138
+ 4,
139
+ crypto.subtle.digest('SHA-256', data)
140
+ ];
141
+ case 1:
142
+ hashBuffer = _state.sent();
143
+ hashArray = new Uint8Array(hashBuffer);
144
+ return [
145
+ 2,
146
+ Array.from(hashArray).map(function(b) {
147
+ return b.toString(16).padStart(2, '0');
148
+ }).join('')
149
+ ];
150
+ }
151
+ });
152
+ })();
153
+ }
154
+ function governedCall(_0) {
155
+ return _async_to_generator(function(param) {
156
+ var governorStub, modelId, estimatedTokens, call, extractHeaders, error, result, headers;
157
+ return _ts_generator(this, function(_state) {
158
+ switch(_state.label){
159
+ case 0:
160
+ governorStub = param.governorStub, modelId = param.modelId, estimatedTokens = param.estimatedTokens, call = param.call, extractHeaders = param.extractHeaders;
161
+ if (!governorStub) {
162
+ return [
163
+ 2,
164
+ call()
165
+ ];
166
+ }
167
+ _state.label = 1;
168
+ case 1:
169
+ _state.trys.push([
170
+ 1,
171
+ 3,
172
+ ,
173
+ 4
174
+ ]);
175
+ return [
176
+ 4,
177
+ governorStub.waitForCapacity(modelId, estimatedTokens)
178
+ ];
179
+ case 2:
180
+ _state.sent();
181
+ return [
182
+ 3,
183
+ 4
184
+ ];
185
+ case 3:
186
+ error = _state.sent();
187
+ console.warn('Governor waitForCapacity failed, proceeding without rate limiting:', error);
188
+ return [
189
+ 3,
190
+ 4
191
+ ];
192
+ case 4:
193
+ return [
194
+ 4,
195
+ call()
196
+ ];
197
+ case 5:
198
+ result = _state.sent();
199
+ headers = extractHeaders(result);
200
+ if (headers) {
201
+ governorStub.reportHeaders(headers);
202
+ }
203
+ return [
204
+ 2,
205
+ result
206
+ ];
207
+ }
208
+ });
209
+ }).apply(this, arguments);
210
+ }
211
+ export function rateGoverned(client) {
212
+ var _client_apiKey;
213
+ var apiKey = (_client_apiKey = client.apiKey) !== null && _client_apiKey !== void 0 ? _client_apiKey : '';
214
+ var cachedIdentity = null;
215
+ function getIdentity() {
216
+ return _async_to_generator(function() {
217
+ var _client_modelId, modelId;
218
+ return _ts_generator(this, function(_state) {
219
+ switch(_state.label){
220
+ case 0:
221
+ if (cachedIdentity) return [
222
+ 2,
223
+ cachedIdentity
224
+ ];
225
+ modelId = (_client_modelId = client.modelId) !== null && _client_modelId !== void 0 ? _client_modelId : 'unknown';
226
+ return [
227
+ 4,
228
+ computeIdentity(modelId, apiKey)
229
+ ];
230
+ case 1:
231
+ cachedIdentity = _state.sent();
232
+ return [
233
+ 2,
234
+ cachedIdentity
235
+ ];
236
+ }
237
+ });
238
+ })();
239
+ }
240
+ var wrapper = {
241
+ get identity () {
242
+ return client.identity;
243
+ },
244
+ get modelId () {
245
+ return client.modelId;
246
+ },
247
+ get apiKey () {
248
+ return client.apiKey;
249
+ },
250
+ generateObject: function generateObject(params) {
251
+ return _async_to_generator(function() {
252
+ var identity, governorStub, estimated, _client_modelId;
253
+ return _ts_generator(this, function(_state) {
254
+ switch(_state.label){
255
+ case 0:
256
+ return [
257
+ 4,
258
+ getIdentity()
259
+ ];
260
+ case 1:
261
+ identity = _state.sent();
262
+ governorStub = getGovernorStub(identity);
263
+ estimated = estimateRequestTokens({
264
+ prompt: params.prompt,
265
+ messages: params.messages,
266
+ system: params.system
267
+ });
268
+ return [
269
+ 2,
270
+ governedCall({
271
+ governorStub: governorStub,
272
+ modelId: (_client_modelId = client.modelId) !== null && _client_modelId !== void 0 ? _client_modelId : 'unknown',
273
+ estimatedTokens: estimated,
274
+ call: function() {
275
+ return client.generateObject(params);
276
+ },
277
+ extractHeaders: function(result) {
278
+ return result.responseHeaders;
279
+ }
280
+ })
281
+ ];
282
+ }
283
+ });
284
+ })();
285
+ },
286
+ createToolResultMessage: client.createToolResultMessage ? function(toolCallId, toolName, result) {
287
+ return client.createToolResultMessage(toolCallId, toolName, result);
288
+ } : undefined,
289
+ streamText: function streamText(params) {
290
+ return _async_to_generator(function() {
291
+ var identity, governorStub, estimated, _client_modelId;
292
+ return _ts_generator(this, function(_state) {
293
+ switch(_state.label){
294
+ case 0:
295
+ return [
296
+ 4,
297
+ getIdentity()
298
+ ];
299
+ case 1:
300
+ identity = _state.sent();
301
+ governorStub = getGovernorStub(identity);
302
+ estimated = estimateRequestTokens({
303
+ prompt: params.prompt,
304
+ messages: params.messages,
305
+ system: params.system
306
+ });
307
+ return [
308
+ 2,
309
+ governedCall({
310
+ governorStub: governorStub,
311
+ modelId: (_client_modelId = client.modelId) !== null && _client_modelId !== void 0 ? _client_modelId : 'unknown',
312
+ estimatedTokens: estimated,
313
+ call: function() {
314
+ return client.streamText(params);
315
+ },
316
+ extractHeaders: function(result) {
317
+ return result.responseHeaders;
318
+ }
319
+ })
320
+ ];
321
+ }
322
+ });
323
+ })();
324
+ }
325
+ };
326
+ if (client.generateText) {
327
+ wrapper.generateText = function(params) {
328
+ return _async_to_generator(function() {
329
+ var identity, governorStub, estimated, _client_modelId;
330
+ return _ts_generator(this, function(_state) {
331
+ switch(_state.label){
332
+ case 0:
333
+ return [
334
+ 4,
335
+ getIdentity()
336
+ ];
337
+ case 1:
338
+ identity = _state.sent();
339
+ governorStub = getGovernorStub(identity);
340
+ estimated = estimateRequestTokens({
341
+ messages: params.messages,
342
+ system: params.system
343
+ });
344
+ return [
345
+ 2,
346
+ governedCall({
347
+ governorStub: governorStub,
348
+ modelId: (_client_modelId = client.modelId) !== null && _client_modelId !== void 0 ? _client_modelId : 'unknown',
349
+ estimatedTokens: estimated,
350
+ call: function() {
351
+ return client.generateText(params);
352
+ },
353
+ extractHeaders: function(result) {
354
+ return result.responseHeaders;
355
+ }
356
+ })
357
+ ];
358
+ }
359
+ });
360
+ })();
361
+ };
362
+ }
363
+ return wrapper;
364
+ }