@positronic/spec 0.0.37 → 0.0.39
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/api.d.ts +47 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +470 -1
- package/dist/api.js.map +1 -1
- package/dist/src/api.js +975 -1
- package/package.json +1 -1
package/dist/src/api.js
CHANGED
|
@@ -122,7 +122,7 @@ function _ts_generator(thisArg, body) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
import { STATUS } from '@positronic/core';
|
|
125
|
+
import { STATUS, BRAIN_EVENTS } from '@positronic/core';
|
|
126
126
|
export function testStatus(fetch) {
|
|
127
127
|
return _async_to_generator(function() {
|
|
128
128
|
var request, response, data, error;
|
|
@@ -1392,6 +1392,116 @@ export var brains = {
|
|
|
1392
1392
|
});
|
|
1393
1393
|
})();
|
|
1394
1394
|
},
|
|
1395
|
+
search: /**
|
|
1396
|
+
* Test GET /brains?q=<query> - Search brains by query string
|
|
1397
|
+
* Returns brains matching the query (by title, filename, or description).
|
|
1398
|
+
* The matching algorithm is implementation-defined; the spec only verifies
|
|
1399
|
+
* the response structure and that results are relevant to the query.
|
|
1400
|
+
*/ function search(fetch, query) {
|
|
1401
|
+
return _async_to_generator(function() {
|
|
1402
|
+
var url, request, response, data, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, brain, error;
|
|
1403
|
+
return _ts_generator(this, function(_state) {
|
|
1404
|
+
switch(_state.label){
|
|
1405
|
+
case 0:
|
|
1406
|
+
_state.trys.push([
|
|
1407
|
+
0,
|
|
1408
|
+
3,
|
|
1409
|
+
,
|
|
1410
|
+
4
|
|
1411
|
+
]);
|
|
1412
|
+
url = new URL('http://example.com/brains');
|
|
1413
|
+
url.searchParams.set('q', query);
|
|
1414
|
+
request = new Request(url.toString(), {
|
|
1415
|
+
method: 'GET'
|
|
1416
|
+
});
|
|
1417
|
+
return [
|
|
1418
|
+
4,
|
|
1419
|
+
fetch(request)
|
|
1420
|
+
];
|
|
1421
|
+
case 1:
|
|
1422
|
+
response = _state.sent();
|
|
1423
|
+
if (!response.ok) {
|
|
1424
|
+
console.error("GET /brains?q=".concat(query, " returned ").concat(response.status));
|
|
1425
|
+
return [
|
|
1426
|
+
2,
|
|
1427
|
+
null
|
|
1428
|
+
];
|
|
1429
|
+
}
|
|
1430
|
+
return [
|
|
1431
|
+
4,
|
|
1432
|
+
response.json()
|
|
1433
|
+
];
|
|
1434
|
+
case 2:
|
|
1435
|
+
data = _state.sent();
|
|
1436
|
+
// Validate response structure
|
|
1437
|
+
if (!Array.isArray(data.brains)) {
|
|
1438
|
+
console.error("Expected brains to be an array, got ".concat(_type_of(data.brains)));
|
|
1439
|
+
return [
|
|
1440
|
+
2,
|
|
1441
|
+
null
|
|
1442
|
+
];
|
|
1443
|
+
}
|
|
1444
|
+
if (typeof data.count !== 'number') {
|
|
1445
|
+
console.error("Expected count to be number, got ".concat(_type_of(data.count)));
|
|
1446
|
+
return [
|
|
1447
|
+
2,
|
|
1448
|
+
null
|
|
1449
|
+
];
|
|
1450
|
+
}
|
|
1451
|
+
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
1452
|
+
try {
|
|
1453
|
+
// Validate each brain has required fields
|
|
1454
|
+
for(_iterator = data.brains[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
1455
|
+
brain = _step.value;
|
|
1456
|
+
if (!brain.title || typeof brain.title !== 'string' || !brain.description || typeof brain.description !== 'string') {
|
|
1457
|
+
console.error("Brain missing required fields or has invalid types: ".concat(JSON.stringify(brain)));
|
|
1458
|
+
return [
|
|
1459
|
+
2,
|
|
1460
|
+
null
|
|
1461
|
+
];
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
} catch (err) {
|
|
1465
|
+
_didIteratorError = true;
|
|
1466
|
+
_iteratorError = err;
|
|
1467
|
+
} finally{
|
|
1468
|
+
try {
|
|
1469
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
1470
|
+
_iterator.return();
|
|
1471
|
+
}
|
|
1472
|
+
} finally{
|
|
1473
|
+
if (_didIteratorError) {
|
|
1474
|
+
throw _iteratorError;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
// Count should match array length
|
|
1479
|
+
if (data.count !== data.brains.length) {
|
|
1480
|
+
console.error("Count (".concat(data.count, ") does not match brains array length (").concat(data.brains.length, ")"));
|
|
1481
|
+
return [
|
|
1482
|
+
2,
|
|
1483
|
+
null
|
|
1484
|
+
];
|
|
1485
|
+
}
|
|
1486
|
+
return [
|
|
1487
|
+
2,
|
|
1488
|
+
data
|
|
1489
|
+
];
|
|
1490
|
+
case 3:
|
|
1491
|
+
error = _state.sent();
|
|
1492
|
+
console.error("Failed to test GET /brains?q=".concat(query, ":"), error);
|
|
1493
|
+
return [
|
|
1494
|
+
2,
|
|
1495
|
+
null
|
|
1496
|
+
];
|
|
1497
|
+
case 4:
|
|
1498
|
+
return [
|
|
1499
|
+
2
|
|
1500
|
+
];
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
})();
|
|
1504
|
+
},
|
|
1395
1505
|
getBrainInfo: /**
|
|
1396
1506
|
* Test GET /brains/:identifier - Get brain structure/definition
|
|
1397
1507
|
* (For future brain exploration/info command)
|
|
@@ -2066,6 +2176,870 @@ export var brains = {
|
|
|
2066
2176
|
}
|
|
2067
2177
|
});
|
|
2068
2178
|
})();
|
|
2179
|
+
},
|
|
2180
|
+
killSuspended: /**
|
|
2181
|
+
* Test DELETE /brains/runs/:runId for a brain suspended on a webhook.
|
|
2182
|
+
* This tests that killing a webhook-suspended brain:
|
|
2183
|
+
* 1. Returns 204 (not 409)
|
|
2184
|
+
* 2. Updates status to CANCELLED
|
|
2185
|
+
* 3. Clears webhook registrations (webhook no longer resumes the brain)
|
|
2186
|
+
*
|
|
2187
|
+
* Requires a brain with a loop step that will pause on a webhook.
|
|
2188
|
+
*/ function killSuspended(fetch, loopBrainIdentifier, webhookSlug, webhookPayload) {
|
|
2189
|
+
return _async_to_generator(function() {
|
|
2190
|
+
var runRequest, runResponse, brainRunId, watchRequest, watchResponse, foundWebhookEvent, reader, decoder, buffer, _ref, value, done, eventEndIndex, message, event, killRequest, killResponse, getRunRequest, getRunResponse, runData, webhookRequest, webhookResponse, webhookResult, finalCheckRequest, finalCheckResponse, finalRunData, error;
|
|
2191
|
+
return _ts_generator(this, function(_state) {
|
|
2192
|
+
switch(_state.label){
|
|
2193
|
+
case 0:
|
|
2194
|
+
_state.trys.push([
|
|
2195
|
+
0,
|
|
2196
|
+
18,
|
|
2197
|
+
,
|
|
2198
|
+
19
|
|
2199
|
+
]);
|
|
2200
|
+
// Step 1: Start the loop brain
|
|
2201
|
+
runRequest = new Request('http://example.com/brains/runs', {
|
|
2202
|
+
method: 'POST',
|
|
2203
|
+
headers: {
|
|
2204
|
+
'Content-Type': 'application/json'
|
|
2205
|
+
},
|
|
2206
|
+
body: JSON.stringify({
|
|
2207
|
+
identifier: loopBrainIdentifier
|
|
2208
|
+
})
|
|
2209
|
+
});
|
|
2210
|
+
return [
|
|
2211
|
+
4,
|
|
2212
|
+
fetch(runRequest)
|
|
2213
|
+
];
|
|
2214
|
+
case 1:
|
|
2215
|
+
runResponse = _state.sent();
|
|
2216
|
+
if (runResponse.status !== 201) {
|
|
2217
|
+
console.error("POST /brains/runs returned ".concat(runResponse.status, ", expected 201"));
|
|
2218
|
+
return [
|
|
2219
|
+
2,
|
|
2220
|
+
false
|
|
2221
|
+
];
|
|
2222
|
+
}
|
|
2223
|
+
return [
|
|
2224
|
+
4,
|
|
2225
|
+
runResponse.json()
|
|
2226
|
+
];
|
|
2227
|
+
case 2:
|
|
2228
|
+
brainRunId = _state.sent().brainRunId;
|
|
2229
|
+
// Step 2: Watch until WEBHOOK event (brain pauses)
|
|
2230
|
+
watchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
|
|
2231
|
+
method: 'GET'
|
|
2232
|
+
});
|
|
2233
|
+
return [
|
|
2234
|
+
4,
|
|
2235
|
+
fetch(watchRequest)
|
|
2236
|
+
];
|
|
2237
|
+
case 3:
|
|
2238
|
+
watchResponse = _state.sent();
|
|
2239
|
+
if (!watchResponse.ok) {
|
|
2240
|
+
console.error("GET /brains/runs/".concat(brainRunId, "/watch returned ").concat(watchResponse.status));
|
|
2241
|
+
return [
|
|
2242
|
+
2,
|
|
2243
|
+
false
|
|
2244
|
+
];
|
|
2245
|
+
}
|
|
2246
|
+
foundWebhookEvent = false;
|
|
2247
|
+
if (!watchResponse.body) return [
|
|
2248
|
+
3,
|
|
2249
|
+
10
|
|
2250
|
+
];
|
|
2251
|
+
reader = watchResponse.body.getReader();
|
|
2252
|
+
decoder = new TextDecoder();
|
|
2253
|
+
buffer = '';
|
|
2254
|
+
_state.label = 4;
|
|
2255
|
+
case 4:
|
|
2256
|
+
_state.trys.push([
|
|
2257
|
+
4,
|
|
2258
|
+
,
|
|
2259
|
+
8,
|
|
2260
|
+
10
|
|
2261
|
+
]);
|
|
2262
|
+
_state.label = 5;
|
|
2263
|
+
case 5:
|
|
2264
|
+
if (!!foundWebhookEvent) return [
|
|
2265
|
+
3,
|
|
2266
|
+
7
|
|
2267
|
+
];
|
|
2268
|
+
return [
|
|
2269
|
+
4,
|
|
2270
|
+
reader.read()
|
|
2271
|
+
];
|
|
2272
|
+
case 6:
|
|
2273
|
+
_ref = _state.sent(), value = _ref.value, done = _ref.done;
|
|
2274
|
+
if (done) return [
|
|
2275
|
+
3,
|
|
2276
|
+
7
|
|
2277
|
+
];
|
|
2278
|
+
buffer += decoder.decode(value, {
|
|
2279
|
+
stream: true
|
|
2280
|
+
});
|
|
2281
|
+
eventEndIndex = void 0;
|
|
2282
|
+
while((eventEndIndex = buffer.indexOf('\n\n')) !== -1){
|
|
2283
|
+
message = buffer.substring(0, eventEndIndex);
|
|
2284
|
+
buffer = buffer.substring(eventEndIndex + 2);
|
|
2285
|
+
if (message.startsWith('data: ')) {
|
|
2286
|
+
try {
|
|
2287
|
+
event = JSON.parse(message.substring(6));
|
|
2288
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK) {
|
|
2289
|
+
foundWebhookEvent = true;
|
|
2290
|
+
break;
|
|
2291
|
+
}
|
|
2292
|
+
if (event.type === BRAIN_EVENTS.COMPLETE || event.type === BRAIN_EVENTS.ERROR) {
|
|
2293
|
+
console.error("Brain completed/errored before WEBHOOK event: ".concat(event.type));
|
|
2294
|
+
return [
|
|
2295
|
+
2,
|
|
2296
|
+
false
|
|
2297
|
+
];
|
|
2298
|
+
}
|
|
2299
|
+
} catch (e) {
|
|
2300
|
+
// Ignore parse errors
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return [
|
|
2305
|
+
3,
|
|
2306
|
+
5
|
|
2307
|
+
];
|
|
2308
|
+
case 7:
|
|
2309
|
+
return [
|
|
2310
|
+
3,
|
|
2311
|
+
10
|
|
2312
|
+
];
|
|
2313
|
+
case 8:
|
|
2314
|
+
return [
|
|
2315
|
+
4,
|
|
2316
|
+
reader.cancel()
|
|
2317
|
+
];
|
|
2318
|
+
case 9:
|
|
2319
|
+
_state.sent();
|
|
2320
|
+
return [
|
|
2321
|
+
7
|
|
2322
|
+
];
|
|
2323
|
+
case 10:
|
|
2324
|
+
if (!foundWebhookEvent) {
|
|
2325
|
+
console.error('Brain did not emit WEBHOOK event');
|
|
2326
|
+
return [
|
|
2327
|
+
2,
|
|
2328
|
+
false
|
|
2329
|
+
];
|
|
2330
|
+
}
|
|
2331
|
+
// Step 3: Kill the suspended brain
|
|
2332
|
+
killRequest = new Request("http://example.com/brains/runs/".concat(brainRunId), {
|
|
2333
|
+
method: 'DELETE'
|
|
2334
|
+
});
|
|
2335
|
+
return [
|
|
2336
|
+
4,
|
|
2337
|
+
fetch(killRequest)
|
|
2338
|
+
];
|
|
2339
|
+
case 11:
|
|
2340
|
+
killResponse = _state.sent();
|
|
2341
|
+
if (killResponse.status !== 204) {
|
|
2342
|
+
console.error("DELETE /brains/runs/".concat(brainRunId, " returned ").concat(killResponse.status, ", expected 204"));
|
|
2343
|
+
return [
|
|
2344
|
+
2,
|
|
2345
|
+
false
|
|
2346
|
+
];
|
|
2347
|
+
}
|
|
2348
|
+
// Step 4: Verify status is CANCELLED via getRun
|
|
2349
|
+
getRunRequest = new Request("http://example.com/brains/runs/".concat(brainRunId), {
|
|
2350
|
+
method: 'GET'
|
|
2351
|
+
});
|
|
2352
|
+
return [
|
|
2353
|
+
4,
|
|
2354
|
+
fetch(getRunRequest)
|
|
2355
|
+
];
|
|
2356
|
+
case 12:
|
|
2357
|
+
getRunResponse = _state.sent();
|
|
2358
|
+
if (!getRunResponse.ok) {
|
|
2359
|
+
console.error("GET /brains/runs/".concat(brainRunId, " returned ").concat(getRunResponse.status));
|
|
2360
|
+
return [
|
|
2361
|
+
2,
|
|
2362
|
+
false
|
|
2363
|
+
];
|
|
2364
|
+
}
|
|
2365
|
+
return [
|
|
2366
|
+
4,
|
|
2367
|
+
getRunResponse.json()
|
|
2368
|
+
];
|
|
2369
|
+
case 13:
|
|
2370
|
+
runData = _state.sent();
|
|
2371
|
+
if (runData.status !== STATUS.CANCELLED) {
|
|
2372
|
+
console.error("Expected status to be '".concat(STATUS.CANCELLED, "', got '").concat(runData.status, "'"));
|
|
2373
|
+
return [
|
|
2374
|
+
2,
|
|
2375
|
+
false
|
|
2376
|
+
];
|
|
2377
|
+
}
|
|
2378
|
+
// Step 5: Verify webhook no longer resumes the brain
|
|
2379
|
+
// Send a webhook - it should return 'no-match' since registrations were cleared
|
|
2380
|
+
webhookRequest = new Request("http://example.com/webhooks/".concat(encodeURIComponent(webhookSlug)), {
|
|
2381
|
+
method: 'POST',
|
|
2382
|
+
headers: {
|
|
2383
|
+
'Content-Type': 'application/json'
|
|
2384
|
+
},
|
|
2385
|
+
body: JSON.stringify(webhookPayload)
|
|
2386
|
+
});
|
|
2387
|
+
return [
|
|
2388
|
+
4,
|
|
2389
|
+
fetch(webhookRequest)
|
|
2390
|
+
];
|
|
2391
|
+
case 14:
|
|
2392
|
+
webhookResponse = _state.sent();
|
|
2393
|
+
// Accept 200/202 - the important thing is it doesn't resume the brain
|
|
2394
|
+
if (!webhookResponse.ok) {
|
|
2395
|
+
console.error("POST /webhooks/".concat(webhookSlug, " returned ").concat(webhookResponse.status));
|
|
2396
|
+
return [
|
|
2397
|
+
2,
|
|
2398
|
+
false
|
|
2399
|
+
];
|
|
2400
|
+
}
|
|
2401
|
+
return [
|
|
2402
|
+
4,
|
|
2403
|
+
webhookResponse.json()
|
|
2404
|
+
];
|
|
2405
|
+
case 15:
|
|
2406
|
+
webhookResult = _state.sent();
|
|
2407
|
+
// The action should be 'no-match' since webhook registrations were cleared
|
|
2408
|
+
if (webhookResult.action === 'resumed') {
|
|
2409
|
+
console.error('Webhook resumed the brain after it was killed - webhook registrations were not cleared');
|
|
2410
|
+
return [
|
|
2411
|
+
2,
|
|
2412
|
+
false
|
|
2413
|
+
];
|
|
2414
|
+
}
|
|
2415
|
+
// Verify the brain is still CANCELLED (didn't restart)
|
|
2416
|
+
finalCheckRequest = new Request("http://example.com/brains/runs/".concat(brainRunId), {
|
|
2417
|
+
method: 'GET'
|
|
2418
|
+
});
|
|
2419
|
+
return [
|
|
2420
|
+
4,
|
|
2421
|
+
fetch(finalCheckRequest)
|
|
2422
|
+
];
|
|
2423
|
+
case 16:
|
|
2424
|
+
finalCheckResponse = _state.sent();
|
|
2425
|
+
if (!finalCheckResponse.ok) {
|
|
2426
|
+
console.error("Final GET /brains/runs/".concat(brainRunId, " returned ").concat(finalCheckResponse.status));
|
|
2427
|
+
return [
|
|
2428
|
+
2,
|
|
2429
|
+
false
|
|
2430
|
+
];
|
|
2431
|
+
}
|
|
2432
|
+
return [
|
|
2433
|
+
4,
|
|
2434
|
+
finalCheckResponse.json()
|
|
2435
|
+
];
|
|
2436
|
+
case 17:
|
|
2437
|
+
finalRunData = _state.sent();
|
|
2438
|
+
if (finalRunData.status !== STATUS.CANCELLED) {
|
|
2439
|
+
console.error("Final status check: expected '".concat(STATUS.CANCELLED, "', got '").concat(finalRunData.status, "'"));
|
|
2440
|
+
return [
|
|
2441
|
+
2,
|
|
2442
|
+
false
|
|
2443
|
+
];
|
|
2444
|
+
}
|
|
2445
|
+
return [
|
|
2446
|
+
2,
|
|
2447
|
+
true
|
|
2448
|
+
];
|
|
2449
|
+
case 18:
|
|
2450
|
+
error = _state.sent();
|
|
2451
|
+
console.error("Failed to test kill suspended brain for ".concat(loopBrainIdentifier, ":"), error);
|
|
2452
|
+
return [
|
|
2453
|
+
2,
|
|
2454
|
+
false
|
|
2455
|
+
];
|
|
2456
|
+
case 19:
|
|
2457
|
+
return [
|
|
2458
|
+
2
|
|
2459
|
+
];
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
})();
|
|
2463
|
+
},
|
|
2464
|
+
watchLoopEvents: /**
|
|
2465
|
+
* Test that loop steps emit proper LOOP_* events in the SSE stream.
|
|
2466
|
+
* Requires a brain with a loop step that will pause on a webhook.
|
|
2467
|
+
*
|
|
2468
|
+
* Expected events before webhook pause:
|
|
2469
|
+
* - LOOP_START (with prompt and optional system)
|
|
2470
|
+
* - LOOP_ITERATION
|
|
2471
|
+
* - LOOP_TOOL_CALL
|
|
2472
|
+
* - LOOP_WEBHOOK (before WEBHOOK event)
|
|
2473
|
+
* - WEBHOOK
|
|
2474
|
+
*/ function watchLoopEvents(fetch, loopBrainIdentifier) {
|
|
2475
|
+
return _async_to_generator(function() {
|
|
2476
|
+
var runRequest, runResponse, brainRunId, watchRequest, watchResponse, events, reader, decoder, buffer, done, _ref, value, streamDone, eventEndIndex, message, event, hasLoopStart, loopStartEvent, hasLoopIteration, hasLoopToolCall, webhookIndex, loopWebhookIndex, loopWebhookEvent, error;
|
|
2477
|
+
return _ts_generator(this, function(_state) {
|
|
2478
|
+
switch(_state.label){
|
|
2479
|
+
case 0:
|
|
2480
|
+
_state.trys.push([
|
|
2481
|
+
0,
|
|
2482
|
+
11,
|
|
2483
|
+
,
|
|
2484
|
+
12
|
|
2485
|
+
]);
|
|
2486
|
+
// Start the loop brain
|
|
2487
|
+
runRequest = new Request('http://example.com/brains/runs', {
|
|
2488
|
+
method: 'POST',
|
|
2489
|
+
headers: {
|
|
2490
|
+
'Content-Type': 'application/json'
|
|
2491
|
+
},
|
|
2492
|
+
body: JSON.stringify({
|
|
2493
|
+
identifier: loopBrainIdentifier
|
|
2494
|
+
})
|
|
2495
|
+
});
|
|
2496
|
+
return [
|
|
2497
|
+
4,
|
|
2498
|
+
fetch(runRequest)
|
|
2499
|
+
];
|
|
2500
|
+
case 1:
|
|
2501
|
+
runResponse = _state.sent();
|
|
2502
|
+
if (runResponse.status !== 201) {
|
|
2503
|
+
console.error("POST /brains/runs returned ".concat(runResponse.status, ", expected 201"));
|
|
2504
|
+
return [
|
|
2505
|
+
2,
|
|
2506
|
+
false
|
|
2507
|
+
];
|
|
2508
|
+
}
|
|
2509
|
+
return [
|
|
2510
|
+
4,
|
|
2511
|
+
runResponse.json()
|
|
2512
|
+
];
|
|
2513
|
+
case 2:
|
|
2514
|
+
brainRunId = _state.sent().brainRunId;
|
|
2515
|
+
// Watch the brain run
|
|
2516
|
+
watchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
|
|
2517
|
+
method: 'GET'
|
|
2518
|
+
});
|
|
2519
|
+
return [
|
|
2520
|
+
4,
|
|
2521
|
+
fetch(watchRequest)
|
|
2522
|
+
];
|
|
2523
|
+
case 3:
|
|
2524
|
+
watchResponse = _state.sent();
|
|
2525
|
+
if (!watchResponse.ok) {
|
|
2526
|
+
console.error("GET /brains/runs/".concat(brainRunId, "/watch returned ").concat(watchResponse.status));
|
|
2527
|
+
return [
|
|
2528
|
+
2,
|
|
2529
|
+
false
|
|
2530
|
+
];
|
|
2531
|
+
}
|
|
2532
|
+
// Read SSE events until we get WEBHOOK or COMPLETE/ERROR
|
|
2533
|
+
events = [];
|
|
2534
|
+
if (!watchResponse.body) return [
|
|
2535
|
+
3,
|
|
2536
|
+
10
|
|
2537
|
+
];
|
|
2538
|
+
reader = watchResponse.body.getReader();
|
|
2539
|
+
decoder = new TextDecoder();
|
|
2540
|
+
buffer = '';
|
|
2541
|
+
done = false;
|
|
2542
|
+
_state.label = 4;
|
|
2543
|
+
case 4:
|
|
2544
|
+
_state.trys.push([
|
|
2545
|
+
4,
|
|
2546
|
+
,
|
|
2547
|
+
8,
|
|
2548
|
+
10
|
|
2549
|
+
]);
|
|
2550
|
+
_state.label = 5;
|
|
2551
|
+
case 5:
|
|
2552
|
+
if (!!done) return [
|
|
2553
|
+
3,
|
|
2554
|
+
7
|
|
2555
|
+
];
|
|
2556
|
+
return [
|
|
2557
|
+
4,
|
|
2558
|
+
reader.read()
|
|
2559
|
+
];
|
|
2560
|
+
case 6:
|
|
2561
|
+
_ref = _state.sent(), value = _ref.value, streamDone = _ref.done;
|
|
2562
|
+
if (streamDone) return [
|
|
2563
|
+
3,
|
|
2564
|
+
7
|
|
2565
|
+
];
|
|
2566
|
+
buffer += decoder.decode(value, {
|
|
2567
|
+
stream: true
|
|
2568
|
+
});
|
|
2569
|
+
// Process complete SSE messages
|
|
2570
|
+
eventEndIndex = void 0;
|
|
2571
|
+
while((eventEndIndex = buffer.indexOf('\n\n')) !== -1){
|
|
2572
|
+
message = buffer.substring(0, eventEndIndex);
|
|
2573
|
+
buffer = buffer.substring(eventEndIndex + 2);
|
|
2574
|
+
if (message.startsWith('data: ')) {
|
|
2575
|
+
try {
|
|
2576
|
+
event = JSON.parse(message.substring(6));
|
|
2577
|
+
events.push(event);
|
|
2578
|
+
// Stop on terminal events
|
|
2579
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK || event.type === BRAIN_EVENTS.COMPLETE || event.type === BRAIN_EVENTS.ERROR) {
|
|
2580
|
+
done = true;
|
|
2581
|
+
break;
|
|
2582
|
+
}
|
|
2583
|
+
} catch (e) {
|
|
2584
|
+
// Ignore parse errors
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
return [
|
|
2589
|
+
3,
|
|
2590
|
+
5
|
|
2591
|
+
];
|
|
2592
|
+
case 7:
|
|
2593
|
+
return [
|
|
2594
|
+
3,
|
|
2595
|
+
10
|
|
2596
|
+
];
|
|
2597
|
+
case 8:
|
|
2598
|
+
return [
|
|
2599
|
+
4,
|
|
2600
|
+
reader.cancel()
|
|
2601
|
+
];
|
|
2602
|
+
case 9:
|
|
2603
|
+
_state.sent();
|
|
2604
|
+
return [
|
|
2605
|
+
7
|
|
2606
|
+
];
|
|
2607
|
+
case 10:
|
|
2608
|
+
// Verify required loop events are present
|
|
2609
|
+
hasLoopStart = events.some(function(e) {
|
|
2610
|
+
return e.type === BRAIN_EVENTS.LOOP_START;
|
|
2611
|
+
});
|
|
2612
|
+
if (!hasLoopStart) {
|
|
2613
|
+
console.error('Missing LOOP_START event in SSE stream');
|
|
2614
|
+
return [
|
|
2615
|
+
2,
|
|
2616
|
+
false
|
|
2617
|
+
];
|
|
2618
|
+
}
|
|
2619
|
+
// Verify LOOP_START has prompt field
|
|
2620
|
+
loopStartEvent = events.find(function(e) {
|
|
2621
|
+
return e.type === BRAIN_EVENTS.LOOP_START;
|
|
2622
|
+
});
|
|
2623
|
+
if (!loopStartEvent.prompt || typeof loopStartEvent.prompt !== 'string') {
|
|
2624
|
+
console.error('LOOP_START event missing prompt field');
|
|
2625
|
+
return [
|
|
2626
|
+
2,
|
|
2627
|
+
false
|
|
2628
|
+
];
|
|
2629
|
+
}
|
|
2630
|
+
hasLoopIteration = events.some(function(e) {
|
|
2631
|
+
return e.type === BRAIN_EVENTS.LOOP_ITERATION;
|
|
2632
|
+
});
|
|
2633
|
+
if (!hasLoopIteration) {
|
|
2634
|
+
console.error('Missing LOOP_ITERATION event in SSE stream');
|
|
2635
|
+
return [
|
|
2636
|
+
2,
|
|
2637
|
+
false
|
|
2638
|
+
];
|
|
2639
|
+
}
|
|
2640
|
+
hasLoopToolCall = events.some(function(e) {
|
|
2641
|
+
return e.type === BRAIN_EVENTS.LOOP_TOOL_CALL;
|
|
2642
|
+
});
|
|
2643
|
+
if (!hasLoopToolCall) {
|
|
2644
|
+
console.error('Missing LOOP_TOOL_CALL event in SSE stream');
|
|
2645
|
+
return [
|
|
2646
|
+
2,
|
|
2647
|
+
false
|
|
2648
|
+
];
|
|
2649
|
+
}
|
|
2650
|
+
// If we got a WEBHOOK event, verify LOOP_WEBHOOK came before it
|
|
2651
|
+
webhookIndex = events.findIndex(function(e) {
|
|
2652
|
+
return e.type === BRAIN_EVENTS.WEBHOOK;
|
|
2653
|
+
});
|
|
2654
|
+
if (webhookIndex !== -1) {
|
|
2655
|
+
loopWebhookIndex = events.findIndex(function(e) {
|
|
2656
|
+
return e.type === BRAIN_EVENTS.LOOP_WEBHOOK;
|
|
2657
|
+
});
|
|
2658
|
+
if (loopWebhookIndex === -1) {
|
|
2659
|
+
console.error('Missing LOOP_WEBHOOK event before WEBHOOK event');
|
|
2660
|
+
return [
|
|
2661
|
+
2,
|
|
2662
|
+
false
|
|
2663
|
+
];
|
|
2664
|
+
}
|
|
2665
|
+
if (loopWebhookIndex >= webhookIndex) {
|
|
2666
|
+
console.error('LOOP_WEBHOOK event must come before WEBHOOK event');
|
|
2667
|
+
return [
|
|
2668
|
+
2,
|
|
2669
|
+
false
|
|
2670
|
+
];
|
|
2671
|
+
}
|
|
2672
|
+
// Verify LOOP_WEBHOOK has required fields
|
|
2673
|
+
loopWebhookEvent = events[loopWebhookIndex];
|
|
2674
|
+
if (!loopWebhookEvent.toolCallId || !loopWebhookEvent.toolName) {
|
|
2675
|
+
console.error('LOOP_WEBHOOK event missing toolCallId or toolName fields');
|
|
2676
|
+
return [
|
|
2677
|
+
2,
|
|
2678
|
+
false
|
|
2679
|
+
];
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
return [
|
|
2683
|
+
2,
|
|
2684
|
+
true
|
|
2685
|
+
];
|
|
2686
|
+
case 11:
|
|
2687
|
+
error = _state.sent();
|
|
2688
|
+
console.error("Failed to test loop events for ".concat(loopBrainIdentifier, ":"), error);
|
|
2689
|
+
return [
|
|
2690
|
+
2,
|
|
2691
|
+
false
|
|
2692
|
+
];
|
|
2693
|
+
case 12:
|
|
2694
|
+
return [
|
|
2695
|
+
2
|
|
2696
|
+
];
|
|
2697
|
+
}
|
|
2698
|
+
});
|
|
2699
|
+
})();
|
|
2700
|
+
},
|
|
2701
|
+
loopWebhookResume: /**
|
|
2702
|
+
* Test full loop webhook resumption flow:
|
|
2703
|
+
* 1. Start a loop brain that will pause on a webhook
|
|
2704
|
+
* 2. Verify it pauses with WEBHOOK event
|
|
2705
|
+
* 3. Trigger the webhook with a response
|
|
2706
|
+
* 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and LOOP_TOOL_RESULT
|
|
2707
|
+
*
|
|
2708
|
+
* Requires:
|
|
2709
|
+
* - A brain with a loop step that calls a tool returning { waitFor: webhook(...) }
|
|
2710
|
+
* - The webhook slug and identifier to trigger
|
|
2711
|
+
*/ function loopWebhookResume(fetch, loopBrainIdentifier, webhookSlug, webhookPayload) {
|
|
2712
|
+
return _async_to_generator(function() {
|
|
2713
|
+
var runRequest, runResponse, brainRunId, watchRequest, watchResponse, foundWebhookEvent, reader, decoder, buffer, _ref, value, done, eventEndIndex, message, event, webhookRequest, webhookResponse, webhookResult, resumeWatchRequest, resumeWatchResponse, resumeEvents, reader1, decoder1, buffer1, done1, _ref1, value1, streamDone, eventEndIndex1, message1, event1, hasWebhookResponse, hasLoopToolResult, completeEvent, error;
|
|
2714
|
+
return _ts_generator(this, function(_state) {
|
|
2715
|
+
switch(_state.label){
|
|
2716
|
+
case 0:
|
|
2717
|
+
_state.trys.push([
|
|
2718
|
+
0,
|
|
2719
|
+
21,
|
|
2720
|
+
,
|
|
2721
|
+
22
|
|
2722
|
+
]);
|
|
2723
|
+
// Step 1: Start the loop brain
|
|
2724
|
+
runRequest = new Request('http://example.com/brains/runs', {
|
|
2725
|
+
method: 'POST',
|
|
2726
|
+
headers: {
|
|
2727
|
+
'Content-Type': 'application/json'
|
|
2728
|
+
},
|
|
2729
|
+
body: JSON.stringify({
|
|
2730
|
+
identifier: loopBrainIdentifier
|
|
2731
|
+
})
|
|
2732
|
+
});
|
|
2733
|
+
return [
|
|
2734
|
+
4,
|
|
2735
|
+
fetch(runRequest)
|
|
2736
|
+
];
|
|
2737
|
+
case 1:
|
|
2738
|
+
runResponse = _state.sent();
|
|
2739
|
+
if (runResponse.status !== 201) {
|
|
2740
|
+
console.error("POST /brains/runs returned ".concat(runResponse.status, ", expected 201"));
|
|
2741
|
+
return [
|
|
2742
|
+
2,
|
|
2743
|
+
false
|
|
2744
|
+
];
|
|
2745
|
+
}
|
|
2746
|
+
return [
|
|
2747
|
+
4,
|
|
2748
|
+
runResponse.json()
|
|
2749
|
+
];
|
|
2750
|
+
case 2:
|
|
2751
|
+
brainRunId = _state.sent().brainRunId;
|
|
2752
|
+
// Step 2: Watch until WEBHOOK event (brain pauses)
|
|
2753
|
+
watchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
|
|
2754
|
+
method: 'GET'
|
|
2755
|
+
});
|
|
2756
|
+
return [
|
|
2757
|
+
4,
|
|
2758
|
+
fetch(watchRequest)
|
|
2759
|
+
];
|
|
2760
|
+
case 3:
|
|
2761
|
+
watchResponse = _state.sent();
|
|
2762
|
+
if (!watchResponse.ok) {
|
|
2763
|
+
console.error("GET /brains/runs/".concat(brainRunId, "/watch returned ").concat(watchResponse.status));
|
|
2764
|
+
return [
|
|
2765
|
+
2,
|
|
2766
|
+
false
|
|
2767
|
+
];
|
|
2768
|
+
}
|
|
2769
|
+
foundWebhookEvent = false;
|
|
2770
|
+
if (!watchResponse.body) return [
|
|
2771
|
+
3,
|
|
2772
|
+
10
|
|
2773
|
+
];
|
|
2774
|
+
reader = watchResponse.body.getReader();
|
|
2775
|
+
decoder = new TextDecoder();
|
|
2776
|
+
buffer = '';
|
|
2777
|
+
_state.label = 4;
|
|
2778
|
+
case 4:
|
|
2779
|
+
_state.trys.push([
|
|
2780
|
+
4,
|
|
2781
|
+
,
|
|
2782
|
+
8,
|
|
2783
|
+
10
|
|
2784
|
+
]);
|
|
2785
|
+
_state.label = 5;
|
|
2786
|
+
case 5:
|
|
2787
|
+
if (!!foundWebhookEvent) return [
|
|
2788
|
+
3,
|
|
2789
|
+
7
|
|
2790
|
+
];
|
|
2791
|
+
return [
|
|
2792
|
+
4,
|
|
2793
|
+
reader.read()
|
|
2794
|
+
];
|
|
2795
|
+
case 6:
|
|
2796
|
+
_ref = _state.sent(), value = _ref.value, done = _ref.done;
|
|
2797
|
+
if (done) return [
|
|
2798
|
+
3,
|
|
2799
|
+
7
|
|
2800
|
+
];
|
|
2801
|
+
buffer += decoder.decode(value, {
|
|
2802
|
+
stream: true
|
|
2803
|
+
});
|
|
2804
|
+
eventEndIndex = void 0;
|
|
2805
|
+
while((eventEndIndex = buffer.indexOf('\n\n')) !== -1){
|
|
2806
|
+
message = buffer.substring(0, eventEndIndex);
|
|
2807
|
+
buffer = buffer.substring(eventEndIndex + 2);
|
|
2808
|
+
if (message.startsWith('data: ')) {
|
|
2809
|
+
try {
|
|
2810
|
+
event = JSON.parse(message.substring(6));
|
|
2811
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK) {
|
|
2812
|
+
foundWebhookEvent = true;
|
|
2813
|
+
break;
|
|
2814
|
+
}
|
|
2815
|
+
if (event.type === BRAIN_EVENTS.COMPLETE || event.type === BRAIN_EVENTS.ERROR) {
|
|
2816
|
+
console.error("Brain completed/errored before WEBHOOK event: ".concat(event.type));
|
|
2817
|
+
return [
|
|
2818
|
+
2,
|
|
2819
|
+
false
|
|
2820
|
+
];
|
|
2821
|
+
}
|
|
2822
|
+
} catch (e) {
|
|
2823
|
+
// Ignore parse errors
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
return [
|
|
2828
|
+
3,
|
|
2829
|
+
5
|
|
2830
|
+
];
|
|
2831
|
+
case 7:
|
|
2832
|
+
return [
|
|
2833
|
+
3,
|
|
2834
|
+
10
|
|
2835
|
+
];
|
|
2836
|
+
case 8:
|
|
2837
|
+
return [
|
|
2838
|
+
4,
|
|
2839
|
+
reader.cancel()
|
|
2840
|
+
];
|
|
2841
|
+
case 9:
|
|
2842
|
+
_state.sent();
|
|
2843
|
+
return [
|
|
2844
|
+
7
|
|
2845
|
+
];
|
|
2846
|
+
case 10:
|
|
2847
|
+
if (!foundWebhookEvent) {
|
|
2848
|
+
console.error('Brain did not emit WEBHOOK event');
|
|
2849
|
+
return [
|
|
2850
|
+
2,
|
|
2851
|
+
false
|
|
2852
|
+
];
|
|
2853
|
+
}
|
|
2854
|
+
// Step 3: Trigger the webhook
|
|
2855
|
+
webhookRequest = new Request("http://example.com/webhooks/".concat(encodeURIComponent(webhookSlug)), {
|
|
2856
|
+
method: 'POST',
|
|
2857
|
+
headers: {
|
|
2858
|
+
'Content-Type': 'application/json'
|
|
2859
|
+
},
|
|
2860
|
+
body: JSON.stringify(webhookPayload)
|
|
2861
|
+
});
|
|
2862
|
+
return [
|
|
2863
|
+
4,
|
|
2864
|
+
fetch(webhookRequest)
|
|
2865
|
+
];
|
|
2866
|
+
case 11:
|
|
2867
|
+
webhookResponse = _state.sent();
|
|
2868
|
+
if (!webhookResponse.ok) {
|
|
2869
|
+
console.error("POST /webhooks/".concat(webhookSlug, " returned ").concat(webhookResponse.status));
|
|
2870
|
+
return [
|
|
2871
|
+
2,
|
|
2872
|
+
false
|
|
2873
|
+
];
|
|
2874
|
+
}
|
|
2875
|
+
return [
|
|
2876
|
+
4,
|
|
2877
|
+
webhookResponse.json()
|
|
2878
|
+
];
|
|
2879
|
+
case 12:
|
|
2880
|
+
webhookResult = _state.sent();
|
|
2881
|
+
if (!webhookResult.received) {
|
|
2882
|
+
console.error('Webhook was not received');
|
|
2883
|
+
return [
|
|
2884
|
+
2,
|
|
2885
|
+
false
|
|
2886
|
+
];
|
|
2887
|
+
}
|
|
2888
|
+
if (webhookResult.action !== 'resumed') {
|
|
2889
|
+
console.error("Expected webhook action 'resumed', got '".concat(webhookResult.action, "'"));
|
|
2890
|
+
return [
|
|
2891
|
+
2,
|
|
2892
|
+
false
|
|
2893
|
+
];
|
|
2894
|
+
}
|
|
2895
|
+
// Step 4: Watch again for resumed events
|
|
2896
|
+
resumeWatchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
|
|
2897
|
+
method: 'GET'
|
|
2898
|
+
});
|
|
2899
|
+
return [
|
|
2900
|
+
4,
|
|
2901
|
+
fetch(resumeWatchRequest)
|
|
2902
|
+
];
|
|
2903
|
+
case 13:
|
|
2904
|
+
resumeWatchResponse = _state.sent();
|
|
2905
|
+
if (!resumeWatchResponse.ok) {
|
|
2906
|
+
console.error("GET /brains/runs/".concat(brainRunId, "/watch (resume) returned ").concat(resumeWatchResponse.status));
|
|
2907
|
+
return [
|
|
2908
|
+
2,
|
|
2909
|
+
false
|
|
2910
|
+
];
|
|
2911
|
+
}
|
|
2912
|
+
resumeEvents = [];
|
|
2913
|
+
if (!resumeWatchResponse.body) return [
|
|
2914
|
+
3,
|
|
2915
|
+
20
|
|
2916
|
+
];
|
|
2917
|
+
reader1 = resumeWatchResponse.body.getReader();
|
|
2918
|
+
decoder1 = new TextDecoder();
|
|
2919
|
+
buffer1 = '';
|
|
2920
|
+
done1 = false;
|
|
2921
|
+
_state.label = 14;
|
|
2922
|
+
case 14:
|
|
2923
|
+
_state.trys.push([
|
|
2924
|
+
14,
|
|
2925
|
+
,
|
|
2926
|
+
18,
|
|
2927
|
+
20
|
|
2928
|
+
]);
|
|
2929
|
+
_state.label = 15;
|
|
2930
|
+
case 15:
|
|
2931
|
+
if (!!done1) return [
|
|
2932
|
+
3,
|
|
2933
|
+
17
|
|
2934
|
+
];
|
|
2935
|
+
return [
|
|
2936
|
+
4,
|
|
2937
|
+
reader1.read()
|
|
2938
|
+
];
|
|
2939
|
+
case 16:
|
|
2940
|
+
_ref1 = _state.sent(), value1 = _ref1.value, streamDone = _ref1.done;
|
|
2941
|
+
if (streamDone) return [
|
|
2942
|
+
3,
|
|
2943
|
+
17
|
|
2944
|
+
];
|
|
2945
|
+
buffer1 += decoder1.decode(value1, {
|
|
2946
|
+
stream: true
|
|
2947
|
+
});
|
|
2948
|
+
eventEndIndex1 = void 0;
|
|
2949
|
+
while((eventEndIndex1 = buffer1.indexOf('\n\n')) !== -1){
|
|
2950
|
+
message1 = buffer1.substring(0, eventEndIndex1);
|
|
2951
|
+
buffer1 = buffer1.substring(eventEndIndex1 + 2);
|
|
2952
|
+
if (message1.startsWith('data: ')) {
|
|
2953
|
+
try {
|
|
2954
|
+
event1 = JSON.parse(message1.substring(6));
|
|
2955
|
+
resumeEvents.push(event1);
|
|
2956
|
+
if (event1.type === BRAIN_EVENTS.COMPLETE || event1.type === BRAIN_EVENTS.ERROR) {
|
|
2957
|
+
done1 = true;
|
|
2958
|
+
break;
|
|
2959
|
+
}
|
|
2960
|
+
} catch (e) {
|
|
2961
|
+
// Ignore parse errors
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
return [
|
|
2966
|
+
3,
|
|
2967
|
+
15
|
|
2968
|
+
];
|
|
2969
|
+
case 17:
|
|
2970
|
+
return [
|
|
2971
|
+
3,
|
|
2972
|
+
20
|
|
2973
|
+
];
|
|
2974
|
+
case 18:
|
|
2975
|
+
return [
|
|
2976
|
+
4,
|
|
2977
|
+
reader1.cancel()
|
|
2978
|
+
];
|
|
2979
|
+
case 19:
|
|
2980
|
+
_state.sent();
|
|
2981
|
+
return [
|
|
2982
|
+
7
|
|
2983
|
+
];
|
|
2984
|
+
case 20:
|
|
2985
|
+
// Verify WEBHOOK_RESPONSE event is present
|
|
2986
|
+
hasWebhookResponse = resumeEvents.some(function(e) {
|
|
2987
|
+
return e.type === BRAIN_EVENTS.WEBHOOK_RESPONSE;
|
|
2988
|
+
});
|
|
2989
|
+
if (!hasWebhookResponse) {
|
|
2990
|
+
console.error('Missing WEBHOOK_RESPONSE event after resume');
|
|
2991
|
+
return [
|
|
2992
|
+
2,
|
|
2993
|
+
false
|
|
2994
|
+
];
|
|
2995
|
+
}
|
|
2996
|
+
// Verify LOOP_TOOL_RESULT event is present (with the webhook response as result)
|
|
2997
|
+
hasLoopToolResult = resumeEvents.some(function(e) {
|
|
2998
|
+
return e.type === BRAIN_EVENTS.LOOP_TOOL_RESULT;
|
|
2999
|
+
});
|
|
3000
|
+
if (!hasLoopToolResult) {
|
|
3001
|
+
console.error('Missing LOOP_TOOL_RESULT event after resume');
|
|
3002
|
+
return [
|
|
3003
|
+
2,
|
|
3004
|
+
false
|
|
3005
|
+
];
|
|
3006
|
+
}
|
|
3007
|
+
// Verify brain completed successfully
|
|
3008
|
+
completeEvent = resumeEvents.find(function(e) {
|
|
3009
|
+
return e.type === BRAIN_EVENTS.COMPLETE;
|
|
3010
|
+
});
|
|
3011
|
+
if (!completeEvent) {
|
|
3012
|
+
console.error('Brain did not complete after resume');
|
|
3013
|
+
return [
|
|
3014
|
+
2,
|
|
3015
|
+
false
|
|
3016
|
+
];
|
|
3017
|
+
}
|
|
3018
|
+
if (completeEvent.status !== STATUS.COMPLETE) {
|
|
3019
|
+
console.error("Expected COMPLETE status, got ".concat(completeEvent.status));
|
|
3020
|
+
return [
|
|
3021
|
+
2,
|
|
3022
|
+
false
|
|
3023
|
+
];
|
|
3024
|
+
}
|
|
3025
|
+
return [
|
|
3026
|
+
2,
|
|
3027
|
+
true
|
|
3028
|
+
];
|
|
3029
|
+
case 21:
|
|
3030
|
+
error = _state.sent();
|
|
3031
|
+
console.error("Failed to test loop webhook resume for ".concat(loopBrainIdentifier, ":"), error);
|
|
3032
|
+
return [
|
|
3033
|
+
2,
|
|
3034
|
+
false
|
|
3035
|
+
];
|
|
3036
|
+
case 22:
|
|
3037
|
+
return [
|
|
3038
|
+
2
|
|
3039
|
+
];
|
|
3040
|
+
}
|
|
3041
|
+
});
|
|
3042
|
+
})();
|
|
2069
3043
|
}
|
|
2070
3044
|
};
|
|
2071
3045
|
export var schedules = {
|