@positronic/cloudflare 0.0.56 → 0.0.58
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/auth-middleware.js +475 -0
- package/dist/src/api/brains.js +160 -0
- package/dist/src/api/index.js +39 -1
- package/dist/src/api/secrets.js +31 -1
- package/dist/src/api/users.js +582 -0
- package/dist/src/api/webhooks/coordination.js +43 -8
- package/dist/src/api/webhooks/index.js +2 -2
- package/dist/src/api/webhooks/system.js +2 -2
- package/dist/src/auth-do.js +455 -0
- package/dist/src/brain-runner-do.js +255 -97
- package/dist/src/event-loader.js +301 -0
- package/dist/src/index.js +1 -0
- package/dist/src/monitor-do.js +39 -19
- package/dist/src/signal-provider.js +179 -0
- package/dist/src/sqlite-adapter.js +200 -13
- package/dist/types/api/auth-middleware.d.ts +19 -0
- package/dist/types/api/auth-middleware.d.ts.map +1 -0
- package/dist/types/api/brains.d.ts.map +1 -1
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/secrets.d.ts.map +1 -1
- package/dist/types/api/types.d.ts +3 -0
- package/dist/types/api/types.d.ts.map +1 -1
- package/dist/types/api/users.d.ts +7 -0
- package/dist/types/api/users.d.ts.map +1 -0
- package/dist/types/api/webhooks/coordination.d.ts +7 -3
- package/dist/types/api/webhooks/coordination.d.ts.map +1 -1
- package/dist/types/auth-do.d.ts +37 -0
- package/dist/types/auth-do.d.ts.map +1 -0
- package/dist/types/brain-runner-do.d.ts +29 -2
- package/dist/types/brain-runner-do.d.ts.map +1 -1
- package/dist/types/event-loader.d.ts +25 -0
- package/dist/types/event-loader.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/monitor-do.d.ts +1 -0
- package/dist/types/monitor-do.d.ts.map +1 -1
- package/dist/types/signal-provider.d.ts +11 -0
- package/dist/types/signal-provider.d.ts.map +1 -0
- package/dist/types/sqlite-adapter.d.ts +6 -3
- package/dist/types/sqlite-adapter.d.ts.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,475 @@
|
|
|
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 _instanceof(left, right) {
|
|
39
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
40
|
+
return !!right[Symbol.hasInstance](left);
|
|
41
|
+
} else {
|
|
42
|
+
return left instanceof right;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function _iterable_to_array_limit(arr, i) {
|
|
46
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
47
|
+
if (_i == null) return;
|
|
48
|
+
var _arr = [];
|
|
49
|
+
var _n = true;
|
|
50
|
+
var _d = false;
|
|
51
|
+
var _s, _e;
|
|
52
|
+
try {
|
|
53
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
54
|
+
_arr.push(_s.value);
|
|
55
|
+
if (i && _arr.length === i) break;
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
_d = true;
|
|
59
|
+
_e = err;
|
|
60
|
+
} finally{
|
|
61
|
+
try {
|
|
62
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
63
|
+
} finally{
|
|
64
|
+
if (_d) throw _e;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return _arr;
|
|
68
|
+
}
|
|
69
|
+
function _non_iterable_rest() {
|
|
70
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
71
|
+
}
|
|
72
|
+
function _sliced_to_array(arr, i) {
|
|
73
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
74
|
+
}
|
|
75
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
76
|
+
if (!o) return;
|
|
77
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
78
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
79
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
80
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
81
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
82
|
+
}
|
|
83
|
+
function _ts_generator(thisArg, body) {
|
|
84
|
+
var f, y, t, _ = {
|
|
85
|
+
label: 0,
|
|
86
|
+
sent: function() {
|
|
87
|
+
if (t[0] & 1) throw t[1];
|
|
88
|
+
return t[1];
|
|
89
|
+
},
|
|
90
|
+
trys: [],
|
|
91
|
+
ops: []
|
|
92
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
93
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
94
|
+
return this;
|
|
95
|
+
}), g;
|
|
96
|
+
function verb(n) {
|
|
97
|
+
return function(v) {
|
|
98
|
+
return step([
|
|
99
|
+
n,
|
|
100
|
+
v
|
|
101
|
+
]);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function step(op) {
|
|
105
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
106
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
107
|
+
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;
|
|
108
|
+
if (y = 0, t) op = [
|
|
109
|
+
op[0] & 2,
|
|
110
|
+
t.value
|
|
111
|
+
];
|
|
112
|
+
switch(op[0]){
|
|
113
|
+
case 0:
|
|
114
|
+
case 1:
|
|
115
|
+
t = op;
|
|
116
|
+
break;
|
|
117
|
+
case 4:
|
|
118
|
+
_.label++;
|
|
119
|
+
return {
|
|
120
|
+
value: op[1],
|
|
121
|
+
done: false
|
|
122
|
+
};
|
|
123
|
+
case 5:
|
|
124
|
+
_.label++;
|
|
125
|
+
y = op[1];
|
|
126
|
+
op = [
|
|
127
|
+
0
|
|
128
|
+
];
|
|
129
|
+
continue;
|
|
130
|
+
case 7:
|
|
131
|
+
op = _.ops.pop();
|
|
132
|
+
_.trys.pop();
|
|
133
|
+
continue;
|
|
134
|
+
default:
|
|
135
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
136
|
+
_ = 0;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
140
|
+
_.label = op[1];
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
144
|
+
_.label = t[1];
|
|
145
|
+
t = op;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
if (t && _.label < t[2]) {
|
|
149
|
+
_.label = t[2];
|
|
150
|
+
_.ops.push(op);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
if (t[2]) _.ops.pop();
|
|
154
|
+
_.trys.pop();
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
op = body.call(thisArg, _);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
op = [
|
|
160
|
+
6,
|
|
161
|
+
e
|
|
162
|
+
];
|
|
163
|
+
y = 0;
|
|
164
|
+
} finally{
|
|
165
|
+
f = t = 0;
|
|
166
|
+
}
|
|
167
|
+
if (op[0] & 5) throw op[1];
|
|
168
|
+
return {
|
|
169
|
+
value: op[0] ? op[1] : void 0,
|
|
170
|
+
done: true
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
import { parseRequestSignature } from '@misskey-dev/node-http-message-signatures';
|
|
175
|
+
/**
|
|
176
|
+
* Get the algorithm parameters for SubtleCrypto based on JWK key type
|
|
177
|
+
*/ function getAlgorithmForJwk(jwk) {
|
|
178
|
+
if (jwk.kty === 'RSA') {
|
|
179
|
+
return {
|
|
180
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
181
|
+
hash: 'SHA-256'
|
|
182
|
+
};
|
|
183
|
+
} else if (jwk.kty === 'EC') {
|
|
184
|
+
return {
|
|
185
|
+
name: 'ECDSA',
|
|
186
|
+
namedCurve: jwk.crv || 'P-256'
|
|
187
|
+
};
|
|
188
|
+
} else if (jwk.kty === 'OKP' && jwk.crv === 'Ed25519') {
|
|
189
|
+
return {
|
|
190
|
+
name: 'Ed25519'
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
throw new Error("Unsupported key type: ".concat(jwk.kty));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get the algorithm parameters for signature verification
|
|
197
|
+
*/ function getVerifyAlgorithm(jwk) {
|
|
198
|
+
if (jwk.kty === 'RSA') {
|
|
199
|
+
return {
|
|
200
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
201
|
+
hash: 'SHA-256'
|
|
202
|
+
};
|
|
203
|
+
} else if (jwk.kty === 'EC') {
|
|
204
|
+
return {
|
|
205
|
+
name: 'ECDSA',
|
|
206
|
+
hash: 'SHA-256'
|
|
207
|
+
};
|
|
208
|
+
} else if (jwk.kty === 'OKP' && jwk.crv === 'Ed25519') {
|
|
209
|
+
return {
|
|
210
|
+
name: 'Ed25519'
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
throw new Error("Unsupported key type: ".concat(jwk.kty));
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Convert a JWK to a CryptoKey for signature verification
|
|
217
|
+
*/ function jwkToCryptoKey(jwkString) {
|
|
218
|
+
return _async_to_generator(function() {
|
|
219
|
+
var jwk, algorithm;
|
|
220
|
+
return _ts_generator(this, function(_state) {
|
|
221
|
+
jwk = JSON.parse(jwkString);
|
|
222
|
+
algorithm = getAlgorithmForJwk(jwk);
|
|
223
|
+
return [
|
|
224
|
+
2,
|
|
225
|
+
crypto.subtle.importKey('jwk', jwk, algorithm, true, [
|
|
226
|
+
'verify'
|
|
227
|
+
])
|
|
228
|
+
];
|
|
229
|
+
});
|
|
230
|
+
})();
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Verify a signature using Web Crypto API
|
|
234
|
+
*/ function verifySignatureWithKey(signatureBase, signatureB64, cryptoKey, jwk) {
|
|
235
|
+
return _async_to_generator(function() {
|
|
236
|
+
var encoder, data, signatureBytes, algorithm;
|
|
237
|
+
return _ts_generator(this, function(_state) {
|
|
238
|
+
encoder = new TextEncoder();
|
|
239
|
+
data = encoder.encode(signatureBase);
|
|
240
|
+
signatureBytes = Uint8Array.from(atob(signatureB64), function(c) {
|
|
241
|
+
return c.charCodeAt(0);
|
|
242
|
+
});
|
|
243
|
+
algorithm = getVerifyAlgorithm(jwk);
|
|
244
|
+
return [
|
|
245
|
+
2,
|
|
246
|
+
crypto.subtle.verify(algorithm, cryptoKey, signatureBytes, data)
|
|
247
|
+
];
|
|
248
|
+
});
|
|
249
|
+
})();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Extract keyId and signature info from parsed signature
|
|
253
|
+
* Handles both draft and RFC9421 formats
|
|
254
|
+
*/ function extractSignatureInfo(parsed) {
|
|
255
|
+
if (parsed.version === 'draft') {
|
|
256
|
+
return {
|
|
257
|
+
keyId: parsed.value.keyId,
|
|
258
|
+
signature: parsed.value.params.signature,
|
|
259
|
+
base: parsed.value.signingString
|
|
260
|
+
};
|
|
261
|
+
} else if (parsed.version === 'rfc9421') {
|
|
262
|
+
// RFC9421 returns an array of [label, value] tuples
|
|
263
|
+
var signatures = parsed.value;
|
|
264
|
+
if (signatures.length === 0) return null;
|
|
265
|
+
// Use the first signature (usually 'sig1')
|
|
266
|
+
var _signatures_ = _sliced_to_array(signatures[0], 2), sigValue = _signatures_[1];
|
|
267
|
+
return {
|
|
268
|
+
keyId: sigValue.keyid,
|
|
269
|
+
signature: sigValue.signature,
|
|
270
|
+
base: sigValue.base
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Authentication middleware for the Positronic API
|
|
277
|
+
* Verifies HTTP message signatures (RFC 9421) using @misskey-dev/node-http-message-signatures for parsing
|
|
278
|
+
*/ export function authMiddleware() {
|
|
279
|
+
return function(c, next) {
|
|
280
|
+
return _async_to_generator(function() {
|
|
281
|
+
var signatureHeader, signatureInputHeader, parsedSignature, requestForParsing, errorMessage, sigInfo, keyId, signature, base, authDoId, authDo, userKey, jwk, cryptoKey, isValid, error, errorMessage1, jwk1, cryptoKey1, isValid1, error1, errorMessage2;
|
|
282
|
+
return _ts_generator(this, function(_state) {
|
|
283
|
+
switch(_state.label){
|
|
284
|
+
case 0:
|
|
285
|
+
// Skip auth in development mode
|
|
286
|
+
if (c.env.NODE_ENV === 'development') {
|
|
287
|
+
c.set('auth', {
|
|
288
|
+
userId: null,
|
|
289
|
+
isRoot: true
|
|
290
|
+
});
|
|
291
|
+
return [
|
|
292
|
+
2,
|
|
293
|
+
next()
|
|
294
|
+
];
|
|
295
|
+
}
|
|
296
|
+
// Get signature headers
|
|
297
|
+
signatureHeader = c.req.header('Signature');
|
|
298
|
+
signatureInputHeader = c.req.header('Signature-Input');
|
|
299
|
+
// If no signature headers, return 401
|
|
300
|
+
if (!signatureHeader || !signatureInputHeader) {
|
|
301
|
+
return [
|
|
302
|
+
2,
|
|
303
|
+
c.json({
|
|
304
|
+
error: 'Authentication required'
|
|
305
|
+
}, 401)
|
|
306
|
+
];
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
// Build a request-like object for the library
|
|
310
|
+
requestForParsing = {
|
|
311
|
+
method: c.req.method,
|
|
312
|
+
url: c.req.url,
|
|
313
|
+
headers: Object.fromEntries(c.req.raw.headers.entries())
|
|
314
|
+
};
|
|
315
|
+
parsedSignature = parseRequestSignature(requestForParsing, {
|
|
316
|
+
clockSkew: {
|
|
317
|
+
now: new Date(),
|
|
318
|
+
forward: 300000,
|
|
319
|
+
delay: 300000
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
} catch (error) {
|
|
323
|
+
// Log error type only - avoid logging request details that could contain sensitive data
|
|
324
|
+
errorMessage = _instanceof(error, Error) ? error.message : 'Unknown error';
|
|
325
|
+
console.error('Failed to parse signature:', errorMessage);
|
|
326
|
+
return [
|
|
327
|
+
2,
|
|
328
|
+
c.json({
|
|
329
|
+
error: 'Invalid signature format'
|
|
330
|
+
}, 401)
|
|
331
|
+
];
|
|
332
|
+
}
|
|
333
|
+
// Extract signature info from parsed result
|
|
334
|
+
sigInfo = extractSignatureInfo(parsedSignature);
|
|
335
|
+
if (!sigInfo) {
|
|
336
|
+
return [
|
|
337
|
+
2,
|
|
338
|
+
c.json({
|
|
339
|
+
error: 'No valid signature found'
|
|
340
|
+
}, 401)
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
keyId = sigInfo.keyId, signature = sigInfo.signature, base = sigInfo.base;
|
|
344
|
+
// Try to find the key in the auth database
|
|
345
|
+
authDoId = c.env.AUTH_DO.idFromName('auth');
|
|
346
|
+
authDo = c.env.AUTH_DO.get(authDoId);
|
|
347
|
+
return [
|
|
348
|
+
4,
|
|
349
|
+
authDo.getKeyByFingerprint(keyId)
|
|
350
|
+
];
|
|
351
|
+
case 1:
|
|
352
|
+
userKey = _state.sent();
|
|
353
|
+
if (!userKey) return [
|
|
354
|
+
3,
|
|
355
|
+
6
|
|
356
|
+
];
|
|
357
|
+
_state.label = 2;
|
|
358
|
+
case 2:
|
|
359
|
+
_state.trys.push([
|
|
360
|
+
2,
|
|
361
|
+
5,
|
|
362
|
+
,
|
|
363
|
+
6
|
|
364
|
+
]);
|
|
365
|
+
jwk = JSON.parse(userKey.jwk);
|
|
366
|
+
return [
|
|
367
|
+
4,
|
|
368
|
+
jwkToCryptoKey(userKey.jwk)
|
|
369
|
+
];
|
|
370
|
+
case 3:
|
|
371
|
+
cryptoKey = _state.sent();
|
|
372
|
+
return [
|
|
373
|
+
4,
|
|
374
|
+
verifySignatureWithKey(base, signature, cryptoKey, jwk)
|
|
375
|
+
];
|
|
376
|
+
case 4:
|
|
377
|
+
isValid = _state.sent();
|
|
378
|
+
if (!isValid) {
|
|
379
|
+
return [
|
|
380
|
+
2,
|
|
381
|
+
c.json({
|
|
382
|
+
error: 'Invalid signature'
|
|
383
|
+
}, 401)
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
c.set('auth', {
|
|
387
|
+
userId: userKey.userId,
|
|
388
|
+
isRoot: false
|
|
389
|
+
});
|
|
390
|
+
return [
|
|
391
|
+
2,
|
|
392
|
+
next()
|
|
393
|
+
];
|
|
394
|
+
case 5:
|
|
395
|
+
error = _state.sent();
|
|
396
|
+
// Log error type only - avoid logging key material or signature data
|
|
397
|
+
errorMessage1 = _instanceof(error, Error) ? error.message : 'Unknown error';
|
|
398
|
+
console.error('Signature verification failed:', errorMessage1);
|
|
399
|
+
return [
|
|
400
|
+
2,
|
|
401
|
+
c.json({
|
|
402
|
+
error: 'Signature verification failed'
|
|
403
|
+
}, 401)
|
|
404
|
+
];
|
|
405
|
+
case 6:
|
|
406
|
+
if (!c.env.ROOT_PUBLIC_KEY) return [
|
|
407
|
+
3,
|
|
408
|
+
11
|
|
409
|
+
];
|
|
410
|
+
_state.label = 7;
|
|
411
|
+
case 7:
|
|
412
|
+
_state.trys.push([
|
|
413
|
+
7,
|
|
414
|
+
10,
|
|
415
|
+
,
|
|
416
|
+
11
|
|
417
|
+
]);
|
|
418
|
+
jwk1 = JSON.parse(c.env.ROOT_PUBLIC_KEY);
|
|
419
|
+
return [
|
|
420
|
+
4,
|
|
421
|
+
jwkToCryptoKey(c.env.ROOT_PUBLIC_KEY)
|
|
422
|
+
];
|
|
423
|
+
case 8:
|
|
424
|
+
cryptoKey1 = _state.sent();
|
|
425
|
+
return [
|
|
426
|
+
4,
|
|
427
|
+
verifySignatureWithKey(base, signature, cryptoKey1, jwk1)
|
|
428
|
+
];
|
|
429
|
+
case 9:
|
|
430
|
+
isValid1 = _state.sent();
|
|
431
|
+
if (isValid1) {
|
|
432
|
+
c.set('auth', {
|
|
433
|
+
userId: null,
|
|
434
|
+
isRoot: true
|
|
435
|
+
});
|
|
436
|
+
return [
|
|
437
|
+
2,
|
|
438
|
+
next()
|
|
439
|
+
];
|
|
440
|
+
}
|
|
441
|
+
return [
|
|
442
|
+
3,
|
|
443
|
+
11
|
|
444
|
+
];
|
|
445
|
+
case 10:
|
|
446
|
+
error1 = _state.sent();
|
|
447
|
+
// Log error type only - avoid logging key material
|
|
448
|
+
errorMessage2 = _instanceof(error1, Error) ? error1.message : 'Unknown error';
|
|
449
|
+
console.error('Root key verification failed:', errorMessage2);
|
|
450
|
+
return [
|
|
451
|
+
3,
|
|
452
|
+
11
|
|
453
|
+
];
|
|
454
|
+
case 11:
|
|
455
|
+
// No matching key found
|
|
456
|
+
// Check if ROOT_PUBLIC_KEY is configured - if not, return specific error
|
|
457
|
+
if (!c.env.ROOT_PUBLIC_KEY) {
|
|
458
|
+
return [
|
|
459
|
+
2,
|
|
460
|
+
c.json({
|
|
461
|
+
error: 'ROOT_KEY_NOT_CONFIGURED'
|
|
462
|
+
}, 401)
|
|
463
|
+
];
|
|
464
|
+
}
|
|
465
|
+
return [
|
|
466
|
+
2,
|
|
467
|
+
c.json({
|
|
468
|
+
error: 'Unknown key'
|
|
469
|
+
}, 401)
|
|
470
|
+
];
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
})();
|
|
474
|
+
};
|
|
475
|
+
}
|
package/dist/src/api/brains.js
CHANGED
|
@@ -157,6 +157,7 @@ import { Hono } from 'hono';
|
|
|
157
157
|
import { v4 as uuidv4 } from 'uuid';
|
|
158
158
|
import { parseCronExpression } from 'cron-schedule';
|
|
159
159
|
import Fuse from 'fuse.js';
|
|
160
|
+
import { isSignalValid, brainMachineDefinition } from '@positronic/core';
|
|
160
161
|
import { getManifest } from '../brain-runner-do.js';
|
|
161
162
|
var brains = new Hono();
|
|
162
163
|
brains.post('/runs', function(context) {
|
|
@@ -478,6 +479,165 @@ brains.delete('/runs/:runId', function(context) {
|
|
|
478
479
|
});
|
|
479
480
|
})();
|
|
480
481
|
});
|
|
482
|
+
// Signal endpoint - queue KILL, PAUSE, USER_MESSAGE, RESUME, or WEBHOOK_RESPONSE signals
|
|
483
|
+
brains.post('/runs/:runId/signals', function(context) {
|
|
484
|
+
return _async_to_generator(function() {
|
|
485
|
+
var runId, body, monitorId, monitorStub, run, validation, namespace, doId, stub, signal;
|
|
486
|
+
return _ts_generator(this, function(_state) {
|
|
487
|
+
switch(_state.label){
|
|
488
|
+
case 0:
|
|
489
|
+
runId = context.req.param('runId');
|
|
490
|
+
return [
|
|
491
|
+
4,
|
|
492
|
+
context.req.json()
|
|
493
|
+
];
|
|
494
|
+
case 1:
|
|
495
|
+
body = _state.sent();
|
|
496
|
+
// Validate signal type
|
|
497
|
+
if (![
|
|
498
|
+
'KILL',
|
|
499
|
+
'PAUSE',
|
|
500
|
+
'USER_MESSAGE',
|
|
501
|
+
'RESUME',
|
|
502
|
+
'WEBHOOK_RESPONSE'
|
|
503
|
+
].includes(body.type)) {
|
|
504
|
+
return [
|
|
505
|
+
2,
|
|
506
|
+
context.json({
|
|
507
|
+
error: 'Invalid signal type'
|
|
508
|
+
}, 400)
|
|
509
|
+
];
|
|
510
|
+
}
|
|
511
|
+
// Check if the run exists in MonitorDO
|
|
512
|
+
monitorId = context.env.MONITOR_DO.idFromName('singleton');
|
|
513
|
+
monitorStub = context.env.MONITOR_DO.get(monitorId);
|
|
514
|
+
return [
|
|
515
|
+
4,
|
|
516
|
+
monitorStub.getRun(runId)
|
|
517
|
+
];
|
|
518
|
+
case 2:
|
|
519
|
+
run = _state.sent();
|
|
520
|
+
if (!run) {
|
|
521
|
+
return [
|
|
522
|
+
2,
|
|
523
|
+
context.json({
|
|
524
|
+
error: 'Brain run not found'
|
|
525
|
+
}, 404)
|
|
526
|
+
];
|
|
527
|
+
}
|
|
528
|
+
// Validate control signals against current brain state using state machine definition
|
|
529
|
+
// USER_MESSAGE is a data signal that gets queued and processed during agent execution,
|
|
530
|
+
// so it doesn't need state validation - it can always be queued
|
|
531
|
+
if (body.type !== 'USER_MESSAGE') {
|
|
532
|
+
validation = isSignalValid(brainMachineDefinition, run.status, body.type);
|
|
533
|
+
if (!validation.valid) {
|
|
534
|
+
return [
|
|
535
|
+
2,
|
|
536
|
+
context.json({
|
|
537
|
+
error: validation.reason
|
|
538
|
+
}, 409)
|
|
539
|
+
];
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// Get BrainRunnerDO stub and queue the signal
|
|
543
|
+
namespace = context.env.BRAIN_RUNNER_DO;
|
|
544
|
+
doId = namespace.idFromName(runId);
|
|
545
|
+
stub = namespace.get(doId);
|
|
546
|
+
return [
|
|
547
|
+
4,
|
|
548
|
+
stub.queueSignal(body)
|
|
549
|
+
];
|
|
550
|
+
case 3:
|
|
551
|
+
signal = _state.sent();
|
|
552
|
+
if (!(body.type === 'RESUME')) return [
|
|
553
|
+
3,
|
|
554
|
+
5
|
|
555
|
+
];
|
|
556
|
+
return [
|
|
557
|
+
4,
|
|
558
|
+
stub.wakeUp(runId)
|
|
559
|
+
];
|
|
560
|
+
case 4:
|
|
561
|
+
_state.sent();
|
|
562
|
+
_state.label = 5;
|
|
563
|
+
case 5:
|
|
564
|
+
return [
|
|
565
|
+
2,
|
|
566
|
+
context.json({
|
|
567
|
+
success: true,
|
|
568
|
+
signal: {
|
|
569
|
+
type: signal.type,
|
|
570
|
+
queuedAt: signal.queuedAt
|
|
571
|
+
}
|
|
572
|
+
}, 202)
|
|
573
|
+
];
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
})();
|
|
577
|
+
});
|
|
578
|
+
// Resume endpoint - resume a paused brain using signal-based approach
|
|
579
|
+
brains.post('/runs/:runId/resume', function(context) {
|
|
580
|
+
return _async_to_generator(function() {
|
|
581
|
+
var runId, monitorId, monitorStub, run, namespace, doId, stub;
|
|
582
|
+
return _ts_generator(this, function(_state) {
|
|
583
|
+
switch(_state.label){
|
|
584
|
+
case 0:
|
|
585
|
+
runId = context.req.param('runId');
|
|
586
|
+
// Check if the run exists and is paused via MonitorDO
|
|
587
|
+
monitorId = context.env.MONITOR_DO.idFromName('singleton');
|
|
588
|
+
monitorStub = context.env.MONITOR_DO.get(monitorId);
|
|
589
|
+
return [
|
|
590
|
+
4,
|
|
591
|
+
monitorStub.getRun(runId)
|
|
592
|
+
];
|
|
593
|
+
case 1:
|
|
594
|
+
run = _state.sent();
|
|
595
|
+
if (!run) {
|
|
596
|
+
return [
|
|
597
|
+
2,
|
|
598
|
+
context.json({
|
|
599
|
+
error: 'Brain run not found'
|
|
600
|
+
}, 404)
|
|
601
|
+
];
|
|
602
|
+
}
|
|
603
|
+
if (run.status !== 'paused') {
|
|
604
|
+
return [
|
|
605
|
+
2,
|
|
606
|
+
context.json({
|
|
607
|
+
error: "Cannot resume brain in '".concat(run.status, "' state. Only paused brains can be resumed.")
|
|
608
|
+
}, 409)
|
|
609
|
+
];
|
|
610
|
+
}
|
|
611
|
+
// Queue RESUME signal and wake up the brain
|
|
612
|
+
namespace = context.env.BRAIN_RUNNER_DO;
|
|
613
|
+
doId = namespace.idFromName(runId);
|
|
614
|
+
stub = namespace.get(doId);
|
|
615
|
+
// Queue the RESUME signal first, then wake up the brain
|
|
616
|
+
return [
|
|
617
|
+
4,
|
|
618
|
+
stub.queueSignal({
|
|
619
|
+
type: 'RESUME'
|
|
620
|
+
})
|
|
621
|
+
];
|
|
622
|
+
case 2:
|
|
623
|
+
_state.sent();
|
|
624
|
+
return [
|
|
625
|
+
4,
|
|
626
|
+
stub.wakeUp(runId)
|
|
627
|
+
];
|
|
628
|
+
case 3:
|
|
629
|
+
_state.sent();
|
|
630
|
+
return [
|
|
631
|
+
2,
|
|
632
|
+
context.json({
|
|
633
|
+
success: true,
|
|
634
|
+
action: 'resumed'
|
|
635
|
+
}, 202)
|
|
636
|
+
];
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
})();
|
|
640
|
+
});
|
|
481
641
|
brains.get('/:identifier/history', function(context) {
|
|
482
642
|
return _async_to_generator(function() {
|
|
483
643
|
var identifier, limit, manifest, resolution, brain, brainTitle, monitorId, monitorStub, runs;
|