@hasna/loops 0.4.27 → 0.4.28
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/CHANGELOG.md +121 -0
- package/README.md +6 -0
- package/dist/api/index.js +689 -20
- package/dist/cli/index.js +509 -85
- package/dist/daemon/index.js +118 -11
- package/dist/index.js +525 -64
- package/dist/lib/executor.d.ts +9 -0
- package/dist/lib/migration.d.ts +66 -0
- package/dist/lib/mode.js +3 -3
- package/dist/lib/route/fields.d.ts +8 -0
- package/dist/lib/storage/index.js +197 -7
- package/dist/lib/storage/postgres-loop-storage.d.ts +2 -2
- package/dist/lib/storage/postgres-schema.js +3 -0
- package/dist/lib/storage/postgres.js +3 -0
- package/dist/lib/storage/sqlite.js +83 -3
- package/dist/lib/store/index.d.ts +3 -0
- package/dist/lib/store.d.ts +77 -0
- package/dist/lib/store.js +89 -4
- package/dist/mcp/index.js +118 -11
- package/dist/runner/index.js +35 -8
- package/dist/sdk/http.d.ts +153 -1
- package/dist/sdk/http.js +78 -1
- package/dist/sdk/index.js +411 -60
- package/dist/serve/index.js +919 -60
- package/dist/types.d.ts +11 -0
- package/docs/DEPLOYMENT_MODES.md +6 -0
- package/docs/USAGE.md +11 -8
- package/package.json +3 -3
package/dist/api/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@hasna/loops",
|
|
6
|
-
version: "0.4.
|
|
6
|
+
version: "0.4.28",
|
|
7
7
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
8
8
|
type: "module",
|
|
9
9
|
main: "dist/index.js",
|
|
@@ -113,6 +113,7 @@ var package_default = {
|
|
|
113
113
|
bun: ">=1.0.0"
|
|
114
114
|
},
|
|
115
115
|
dependencies: {
|
|
116
|
+
"@hasna/contracts": "^0.5.1",
|
|
116
117
|
"@hasna/events": "^0.1.9",
|
|
117
118
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
118
119
|
"@openrouter/ai-sdk-provider": "2.9.1",
|
|
@@ -122,8 +123,7 @@ var package_default = {
|
|
|
122
123
|
zod: "4.4.3"
|
|
123
124
|
},
|
|
124
125
|
optionalDependencies: {
|
|
125
|
-
"@hasna/machines": "0.0.49"
|
|
126
|
-
"@hasna/contracts": "^0.4.2"
|
|
126
|
+
"@hasna/machines": "0.0.49"
|
|
127
127
|
},
|
|
128
128
|
devDependencies: {
|
|
129
129
|
"@types/bun": "latest",
|
|
@@ -935,6 +935,14 @@ var loops_default = {
|
|
|
935
935
|
type: "integer"
|
|
936
936
|
}
|
|
937
937
|
},
|
|
938
|
+
{
|
|
939
|
+
name: "offset",
|
|
940
|
+
in: "query",
|
|
941
|
+
required: false,
|
|
942
|
+
schema: {
|
|
943
|
+
type: "integer"
|
|
944
|
+
}
|
|
945
|
+
},
|
|
938
946
|
{
|
|
939
947
|
name: "includeArchived",
|
|
940
948
|
in: "query",
|
|
@@ -1210,6 +1218,22 @@ var loops_default = {
|
|
|
1210
1218
|
schema: {
|
|
1211
1219
|
type: "integer"
|
|
1212
1220
|
}
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
name: "offset",
|
|
1224
|
+
in: "query",
|
|
1225
|
+
required: false,
|
|
1226
|
+
schema: {
|
|
1227
|
+
type: "integer"
|
|
1228
|
+
}
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
name: "showOutput",
|
|
1232
|
+
in: "query",
|
|
1233
|
+
required: false,
|
|
1234
|
+
schema: {
|
|
1235
|
+
type: "boolean"
|
|
1236
|
+
}
|
|
1213
1237
|
}
|
|
1214
1238
|
],
|
|
1215
1239
|
responses: {
|
|
@@ -1266,6 +1290,14 @@ var loops_default = {
|
|
|
1266
1290
|
schema: {
|
|
1267
1291
|
type: "string"
|
|
1268
1292
|
}
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
name: "showOutput",
|
|
1296
|
+
in: "query",
|
|
1297
|
+
required: false,
|
|
1298
|
+
schema: {
|
|
1299
|
+
type: "boolean"
|
|
1300
|
+
}
|
|
1269
1301
|
}
|
|
1270
1302
|
],
|
|
1271
1303
|
responses: {
|
|
@@ -1286,7 +1318,7 @@ var loops_default = {
|
|
|
1286
1318
|
post: {
|
|
1287
1319
|
operationId: "importRows",
|
|
1288
1320
|
summary: "Bulk id-preserving import (self-hosted backfill)",
|
|
1289
|
-
description: "Upsert full workflow/loop/run rows by id (idempotent; ON CONFLICT(id) DO UPDATE). Preserves
|
|
1321
|
+
description: "Upsert full workflow/loop/run rows by id (idempotent; ON CONFLICT(id) DO UPDATE). Preserves ids, definitions, and timestamps while applying safe defaults: imported workflows are archived, imported loops are paused, and nextRunAt/retryScheduledFor are cleared by default so backfills never schedule immediately. Set preserveWorkflowActivation=true or preserveLoopScheduling=true only for intentional activation. Applied FK-safe: workflows, then loops, then runs. Volatile running runs are skipped and reported.",
|
|
1290
1322
|
requestBody: {
|
|
1291
1323
|
required: true,
|
|
1292
1324
|
content: {
|
|
@@ -1311,6 +1343,260 @@ var loops_default = {
|
|
|
1311
1343
|
}
|
|
1312
1344
|
}
|
|
1313
1345
|
},
|
|
1346
|
+
"/v1/workflows": {
|
|
1347
|
+
get: {
|
|
1348
|
+
operationId: "listWorkflows",
|
|
1349
|
+
summary: "List workflow specs",
|
|
1350
|
+
parameters: [
|
|
1351
|
+
{
|
|
1352
|
+
name: "status",
|
|
1353
|
+
in: "query",
|
|
1354
|
+
required: false,
|
|
1355
|
+
schema: {
|
|
1356
|
+
type: "string",
|
|
1357
|
+
enum: [
|
|
1358
|
+
"active",
|
|
1359
|
+
"archived"
|
|
1360
|
+
]
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
name: "limit",
|
|
1365
|
+
in: "query",
|
|
1366
|
+
required: false,
|
|
1367
|
+
schema: {
|
|
1368
|
+
type: "integer"
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
name: "offset",
|
|
1373
|
+
in: "query",
|
|
1374
|
+
required: false,
|
|
1375
|
+
schema: {
|
|
1376
|
+
type: "integer"
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
],
|
|
1380
|
+
responses: {
|
|
1381
|
+
"200": {
|
|
1382
|
+
description: "workflow specs",
|
|
1383
|
+
content: {
|
|
1384
|
+
"application/json": {
|
|
1385
|
+
schema: {
|
|
1386
|
+
$ref: "#/components/schemas/WorkflowListResponse"
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
post: {
|
|
1394
|
+
operationId: "createWorkflow",
|
|
1395
|
+
summary: "Create a workflow spec",
|
|
1396
|
+
requestBody: {
|
|
1397
|
+
required: true,
|
|
1398
|
+
content: {
|
|
1399
|
+
"application/json": {
|
|
1400
|
+
schema: {
|
|
1401
|
+
$ref: "#/components/schemas/CreateWorkflowInput"
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
},
|
|
1406
|
+
responses: {
|
|
1407
|
+
"201": {
|
|
1408
|
+
description: "workflow",
|
|
1409
|
+
content: {
|
|
1410
|
+
"application/json": {
|
|
1411
|
+
schema: {
|
|
1412
|
+
$ref: "#/components/schemas/WorkflowResponse"
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
},
|
|
1420
|
+
"/v1/workflows/count": {
|
|
1421
|
+
get: {
|
|
1422
|
+
operationId: "countWorkflows",
|
|
1423
|
+
summary: "Count workflow specs",
|
|
1424
|
+
parameters: [
|
|
1425
|
+
{
|
|
1426
|
+
name: "status",
|
|
1427
|
+
in: "query",
|
|
1428
|
+
required: false,
|
|
1429
|
+
schema: {
|
|
1430
|
+
type: "string"
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
],
|
|
1434
|
+
responses: {
|
|
1435
|
+
"200": {
|
|
1436
|
+
description: "count",
|
|
1437
|
+
content: {
|
|
1438
|
+
"application/json": {
|
|
1439
|
+
schema: {
|
|
1440
|
+
$ref: "#/components/schemas/CountResponse"
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
"/v1/workflows/{id}": {
|
|
1449
|
+
get: {
|
|
1450
|
+
operationId: "getWorkflow",
|
|
1451
|
+
summary: "Get a workflow spec by id",
|
|
1452
|
+
parameters: [
|
|
1453
|
+
{
|
|
1454
|
+
name: "id",
|
|
1455
|
+
in: "path",
|
|
1456
|
+
required: true,
|
|
1457
|
+
schema: {
|
|
1458
|
+
type: "string"
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
],
|
|
1462
|
+
responses: {
|
|
1463
|
+
"200": {
|
|
1464
|
+
description: "workflow",
|
|
1465
|
+
content: {
|
|
1466
|
+
"application/json": {
|
|
1467
|
+
schema: {
|
|
1468
|
+
$ref: "#/components/schemas/WorkflowResponse"
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
},
|
|
1476
|
+
"/v1/invocations": {
|
|
1477
|
+
get: {
|
|
1478
|
+
operationId: "listWorkflowInvocations",
|
|
1479
|
+
summary: "List workflow route invocations",
|
|
1480
|
+
parameters: [
|
|
1481
|
+
{
|
|
1482
|
+
name: "limit",
|
|
1483
|
+
in: "query",
|
|
1484
|
+
required: false,
|
|
1485
|
+
schema: {
|
|
1486
|
+
type: "integer"
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
],
|
|
1490
|
+
responses: {
|
|
1491
|
+
"200": {
|
|
1492
|
+
description: "workflow invocations",
|
|
1493
|
+
content: {
|
|
1494
|
+
"application/json": {
|
|
1495
|
+
schema: {
|
|
1496
|
+
$ref: "#/components/schemas/WorkflowInvocationListResponse"
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
},
|
|
1503
|
+
post: {
|
|
1504
|
+
operationId: "createWorkflowInvocation",
|
|
1505
|
+
summary: "Create an id-preserving workflow route invocation",
|
|
1506
|
+
requestBody: {
|
|
1507
|
+
required: true,
|
|
1508
|
+
content: {
|
|
1509
|
+
"application/json": {
|
|
1510
|
+
schema: {
|
|
1511
|
+
$ref: "#/components/schemas/CreateWorkflowInvocationInput"
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
},
|
|
1516
|
+
responses: {
|
|
1517
|
+
"201": {
|
|
1518
|
+
description: "workflow invocation",
|
|
1519
|
+
content: {
|
|
1520
|
+
"application/json": {
|
|
1521
|
+
schema: {
|
|
1522
|
+
$ref: "#/components/schemas/WorkflowInvocationResponse"
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
},
|
|
1530
|
+
"/v1/work-items": {
|
|
1531
|
+
get: {
|
|
1532
|
+
operationId: "listWorkflowWorkItems",
|
|
1533
|
+
summary: "List workflow route work items",
|
|
1534
|
+
parameters: [
|
|
1535
|
+
{
|
|
1536
|
+
name: "status",
|
|
1537
|
+
in: "query",
|
|
1538
|
+
required: false,
|
|
1539
|
+
schema: {
|
|
1540
|
+
type: "string"
|
|
1541
|
+
}
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
name: "routeKey",
|
|
1545
|
+
in: "query",
|
|
1546
|
+
required: false,
|
|
1547
|
+
schema: {
|
|
1548
|
+
type: "string"
|
|
1549
|
+
}
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: "limit",
|
|
1553
|
+
in: "query",
|
|
1554
|
+
required: false,
|
|
1555
|
+
schema: {
|
|
1556
|
+
type: "integer"
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
],
|
|
1560
|
+
responses: {
|
|
1561
|
+
"200": {
|
|
1562
|
+
description: "workflow work items",
|
|
1563
|
+
content: {
|
|
1564
|
+
"application/json": {
|
|
1565
|
+
schema: {
|
|
1566
|
+
$ref: "#/components/schemas/WorkflowWorkItemListResponse"
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1573
|
+
post: {
|
|
1574
|
+
operationId: "upsertWorkflowWorkItem",
|
|
1575
|
+
summary: "Upsert an id-preserving workflow route work item",
|
|
1576
|
+
requestBody: {
|
|
1577
|
+
required: true,
|
|
1578
|
+
content: {
|
|
1579
|
+
"application/json": {
|
|
1580
|
+
schema: {
|
|
1581
|
+
$ref: "#/components/schemas/UpsertWorkflowWorkItemInput"
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
},
|
|
1586
|
+
responses: {
|
|
1587
|
+
"201": {
|
|
1588
|
+
description: "workflow work item",
|
|
1589
|
+
content: {
|
|
1590
|
+
"application/json": {
|
|
1591
|
+
schema: {
|
|
1592
|
+
$ref: "#/components/schemas/WorkflowWorkItemResponse"
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
},
|
|
1314
1600
|
"/v1/receipts": {
|
|
1315
1601
|
get: {
|
|
1316
1602
|
operationId: "listRunReceipts",
|
|
@@ -1913,22 +2199,368 @@ var loops_default = {
|
|
|
1913
2199
|
"receipts"
|
|
1914
2200
|
]
|
|
1915
2201
|
},
|
|
1916
|
-
|
|
2202
|
+
Workflow: {
|
|
1917
2203
|
type: "object",
|
|
1918
|
-
description: "Batches of full rows to upsert by id. Any array may be omitted.",
|
|
1919
2204
|
properties: {
|
|
1920
|
-
|
|
1921
|
-
type: "
|
|
1922
|
-
items: {
|
|
1923
|
-
type: "object",
|
|
1924
|
-
additionalProperties: true
|
|
1925
|
-
}
|
|
2205
|
+
id: {
|
|
2206
|
+
type: "string"
|
|
1926
2207
|
},
|
|
1927
|
-
|
|
1928
|
-
type: "
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2208
|
+
name: {
|
|
2209
|
+
type: "string"
|
|
2210
|
+
},
|
|
2211
|
+
description: {
|
|
2212
|
+
type: "string",
|
|
2213
|
+
nullable: true
|
|
2214
|
+
},
|
|
2215
|
+
version: {
|
|
2216
|
+
type: "integer"
|
|
2217
|
+
},
|
|
2218
|
+
status: {
|
|
2219
|
+
type: "string"
|
|
2220
|
+
},
|
|
2221
|
+
steps: {
|
|
2222
|
+
type: "array",
|
|
2223
|
+
items: {
|
|
2224
|
+
type: "object",
|
|
2225
|
+
additionalProperties: true
|
|
2226
|
+
}
|
|
2227
|
+
},
|
|
2228
|
+
goal: {
|
|
2229
|
+
type: "object",
|
|
2230
|
+
additionalProperties: true
|
|
2231
|
+
},
|
|
2232
|
+
createdAt: {
|
|
2233
|
+
type: "string"
|
|
2234
|
+
},
|
|
2235
|
+
updatedAt: {
|
|
2236
|
+
type: "string"
|
|
2237
|
+
}
|
|
2238
|
+
},
|
|
2239
|
+
required: [
|
|
2240
|
+
"id",
|
|
2241
|
+
"name",
|
|
2242
|
+
"version",
|
|
2243
|
+
"status",
|
|
2244
|
+
"steps"
|
|
2245
|
+
]
|
|
2246
|
+
},
|
|
2247
|
+
CreateWorkflowInput: {
|
|
2248
|
+
type: "object",
|
|
2249
|
+
properties: {
|
|
2250
|
+
name: {
|
|
2251
|
+
type: "string"
|
|
2252
|
+
},
|
|
2253
|
+
description: {
|
|
2254
|
+
type: "string"
|
|
2255
|
+
},
|
|
2256
|
+
steps: {
|
|
2257
|
+
type: "array",
|
|
2258
|
+
items: {
|
|
2259
|
+
type: "object",
|
|
2260
|
+
additionalProperties: true
|
|
2261
|
+
}
|
|
2262
|
+
},
|
|
2263
|
+
goal: {
|
|
2264
|
+
type: "object",
|
|
2265
|
+
additionalProperties: true
|
|
2266
|
+
}
|
|
2267
|
+
},
|
|
2268
|
+
required: [
|
|
2269
|
+
"name",
|
|
2270
|
+
"steps"
|
|
2271
|
+
]
|
|
2272
|
+
},
|
|
2273
|
+
WorkflowResponse: {
|
|
2274
|
+
type: "object",
|
|
2275
|
+
properties: {
|
|
2276
|
+
ok: {
|
|
2277
|
+
type: "boolean"
|
|
2278
|
+
},
|
|
2279
|
+
workflow: {
|
|
2280
|
+
$ref: "#/components/schemas/Workflow"
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
required: [
|
|
2284
|
+
"ok",
|
|
2285
|
+
"workflow"
|
|
2286
|
+
]
|
|
2287
|
+
},
|
|
2288
|
+
WorkflowListResponse: {
|
|
2289
|
+
type: "object",
|
|
2290
|
+
properties: {
|
|
2291
|
+
ok: {
|
|
2292
|
+
type: "boolean"
|
|
2293
|
+
},
|
|
2294
|
+
workflows: {
|
|
2295
|
+
type: "array",
|
|
2296
|
+
items: {
|
|
2297
|
+
$ref: "#/components/schemas/Workflow"
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
},
|
|
2301
|
+
required: [
|
|
2302
|
+
"ok",
|
|
2303
|
+
"workflows"
|
|
2304
|
+
]
|
|
2305
|
+
},
|
|
2306
|
+
CreateWorkflowInvocationInput: {
|
|
2307
|
+
type: "object",
|
|
2308
|
+
properties: {
|
|
2309
|
+
id: {
|
|
2310
|
+
type: "string"
|
|
2311
|
+
},
|
|
2312
|
+
workflowId: {
|
|
2313
|
+
type: "string"
|
|
2314
|
+
},
|
|
2315
|
+
templateId: {
|
|
2316
|
+
type: "string"
|
|
2317
|
+
},
|
|
2318
|
+
sourceRef: {
|
|
2319
|
+
type: "object",
|
|
2320
|
+
additionalProperties: true
|
|
2321
|
+
},
|
|
2322
|
+
subjectRef: {
|
|
2323
|
+
type: "object",
|
|
2324
|
+
additionalProperties: true
|
|
2325
|
+
},
|
|
2326
|
+
intent: {
|
|
2327
|
+
type: "string"
|
|
2328
|
+
},
|
|
2329
|
+
scope: {
|
|
2330
|
+
type: "object",
|
|
2331
|
+
additionalProperties: true
|
|
2332
|
+
},
|
|
2333
|
+
outputPolicy: {
|
|
2334
|
+
type: "object",
|
|
2335
|
+
additionalProperties: true
|
|
2336
|
+
}
|
|
2337
|
+
},
|
|
2338
|
+
required: [
|
|
2339
|
+
"sourceRef",
|
|
2340
|
+
"subjectRef",
|
|
2341
|
+
"intent"
|
|
2342
|
+
]
|
|
2343
|
+
},
|
|
2344
|
+
WorkflowInvocation: {
|
|
2345
|
+
allOf: [
|
|
2346
|
+
{
|
|
2347
|
+
$ref: "#/components/schemas/CreateWorkflowInvocationInput"
|
|
2348
|
+
},
|
|
2349
|
+
{
|
|
2350
|
+
type: "object",
|
|
2351
|
+
properties: {
|
|
2352
|
+
id: {
|
|
2353
|
+
type: "string"
|
|
2354
|
+
},
|
|
2355
|
+
createdAt: {
|
|
2356
|
+
type: "string"
|
|
2357
|
+
},
|
|
2358
|
+
updatedAt: {
|
|
2359
|
+
type: "string"
|
|
2360
|
+
}
|
|
2361
|
+
},
|
|
2362
|
+
required: [
|
|
2363
|
+
"id",
|
|
2364
|
+
"createdAt",
|
|
2365
|
+
"updatedAt"
|
|
2366
|
+
]
|
|
2367
|
+
}
|
|
2368
|
+
]
|
|
2369
|
+
},
|
|
2370
|
+
WorkflowInvocationResponse: {
|
|
2371
|
+
type: "object",
|
|
2372
|
+
properties: {
|
|
2373
|
+
ok: {
|
|
2374
|
+
type: "boolean"
|
|
2375
|
+
},
|
|
2376
|
+
invocation: {
|
|
2377
|
+
$ref: "#/components/schemas/WorkflowInvocation"
|
|
2378
|
+
}
|
|
2379
|
+
},
|
|
2380
|
+
required: [
|
|
2381
|
+
"ok",
|
|
2382
|
+
"invocation"
|
|
2383
|
+
]
|
|
2384
|
+
},
|
|
2385
|
+
WorkflowInvocationListResponse: {
|
|
2386
|
+
type: "object",
|
|
2387
|
+
properties: {
|
|
2388
|
+
ok: {
|
|
2389
|
+
type: "boolean"
|
|
2390
|
+
},
|
|
2391
|
+
invocations: {
|
|
2392
|
+
type: "array",
|
|
2393
|
+
items: {
|
|
2394
|
+
$ref: "#/components/schemas/WorkflowInvocation"
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
},
|
|
2398
|
+
required: [
|
|
2399
|
+
"ok",
|
|
2400
|
+
"invocations"
|
|
2401
|
+
]
|
|
2402
|
+
},
|
|
2403
|
+
WorkflowWorkItem: {
|
|
2404
|
+
type: "object",
|
|
2405
|
+
additionalProperties: true,
|
|
2406
|
+
properties: {
|
|
2407
|
+
id: {
|
|
2408
|
+
type: "string"
|
|
2409
|
+
},
|
|
2410
|
+
routeKey: {
|
|
2411
|
+
type: "string"
|
|
2412
|
+
},
|
|
2413
|
+
idempotencyKey: {
|
|
2414
|
+
type: "string"
|
|
2415
|
+
},
|
|
2416
|
+
invocationId: {
|
|
2417
|
+
type: "string"
|
|
2418
|
+
},
|
|
2419
|
+
sourceType: {
|
|
2420
|
+
type: "string"
|
|
2421
|
+
},
|
|
2422
|
+
sourceRef: {
|
|
2423
|
+
type: "string"
|
|
2424
|
+
},
|
|
2425
|
+
subjectRef: {
|
|
2426
|
+
type: "string"
|
|
2427
|
+
},
|
|
2428
|
+
status: {
|
|
2429
|
+
type: "string"
|
|
2430
|
+
},
|
|
2431
|
+
priority: {
|
|
2432
|
+
type: "integer"
|
|
2433
|
+
},
|
|
2434
|
+
createdAt: {
|
|
2435
|
+
type: "string"
|
|
2436
|
+
},
|
|
2437
|
+
updatedAt: {
|
|
2438
|
+
type: "string"
|
|
2439
|
+
}
|
|
2440
|
+
},
|
|
2441
|
+
required: [
|
|
2442
|
+
"id",
|
|
2443
|
+
"routeKey",
|
|
2444
|
+
"idempotencyKey",
|
|
2445
|
+
"invocationId",
|
|
2446
|
+
"sourceType",
|
|
2447
|
+
"sourceRef",
|
|
2448
|
+
"subjectRef",
|
|
2449
|
+
"status",
|
|
2450
|
+
"priority"
|
|
2451
|
+
]
|
|
2452
|
+
},
|
|
2453
|
+
UpsertWorkflowWorkItemInput: {
|
|
2454
|
+
type: "object",
|
|
2455
|
+
properties: {
|
|
2456
|
+
id: {
|
|
2457
|
+
type: "string"
|
|
2458
|
+
},
|
|
2459
|
+
routeKey: {
|
|
2460
|
+
type: "string"
|
|
2461
|
+
},
|
|
2462
|
+
idempotencyKey: {
|
|
2463
|
+
type: "string"
|
|
2464
|
+
},
|
|
2465
|
+
invocationId: {
|
|
2466
|
+
type: "string"
|
|
2467
|
+
},
|
|
2468
|
+
sourceType: {
|
|
2469
|
+
type: "string"
|
|
2470
|
+
},
|
|
2471
|
+
sourceRef: {
|
|
2472
|
+
type: "string"
|
|
2473
|
+
},
|
|
2474
|
+
subjectRef: {
|
|
2475
|
+
type: "string"
|
|
2476
|
+
},
|
|
2477
|
+
projectKey: {
|
|
2478
|
+
type: "string"
|
|
2479
|
+
},
|
|
2480
|
+
projectGroup: {
|
|
2481
|
+
type: "string"
|
|
2482
|
+
},
|
|
2483
|
+
machineId: {
|
|
2484
|
+
type: "string"
|
|
2485
|
+
},
|
|
2486
|
+
routeScope: {
|
|
2487
|
+
type: "string"
|
|
2488
|
+
},
|
|
2489
|
+
priority: {
|
|
2490
|
+
type: "integer"
|
|
2491
|
+
},
|
|
2492
|
+
status: {
|
|
2493
|
+
type: "string",
|
|
2494
|
+
enum: [
|
|
2495
|
+
"queued",
|
|
2496
|
+
"deferred"
|
|
2497
|
+
]
|
|
2498
|
+
},
|
|
2499
|
+
nextAttemptAt: {
|
|
2500
|
+
type: "string"
|
|
2501
|
+
},
|
|
2502
|
+
lastReason: {
|
|
2503
|
+
type: "string"
|
|
2504
|
+
}
|
|
2505
|
+
},
|
|
2506
|
+
required: [
|
|
2507
|
+
"routeKey",
|
|
2508
|
+
"idempotencyKey",
|
|
2509
|
+
"invocationId",
|
|
2510
|
+
"sourceType",
|
|
2511
|
+
"sourceRef",
|
|
2512
|
+
"subjectRef"
|
|
2513
|
+
]
|
|
2514
|
+
},
|
|
2515
|
+
WorkflowWorkItemResponse: {
|
|
2516
|
+
type: "object",
|
|
2517
|
+
properties: {
|
|
2518
|
+
ok: {
|
|
2519
|
+
type: "boolean"
|
|
2520
|
+
},
|
|
2521
|
+
workItem: {
|
|
2522
|
+
$ref: "#/components/schemas/WorkflowWorkItem"
|
|
2523
|
+
}
|
|
2524
|
+
},
|
|
2525
|
+
required: [
|
|
2526
|
+
"ok",
|
|
2527
|
+
"workItem"
|
|
2528
|
+
]
|
|
2529
|
+
},
|
|
2530
|
+
WorkflowWorkItemListResponse: {
|
|
2531
|
+
type: "object",
|
|
2532
|
+
properties: {
|
|
2533
|
+
ok: {
|
|
2534
|
+
type: "boolean"
|
|
2535
|
+
},
|
|
2536
|
+
workItems: {
|
|
2537
|
+
type: "array",
|
|
2538
|
+
items: {
|
|
2539
|
+
$ref: "#/components/schemas/WorkflowWorkItem"
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
},
|
|
2543
|
+
required: [
|
|
2544
|
+
"ok",
|
|
2545
|
+
"workItems"
|
|
2546
|
+
]
|
|
2547
|
+
},
|
|
2548
|
+
ImportInput: {
|
|
2549
|
+
type: "object",
|
|
2550
|
+
description: "Batches of full rows to upsert by id. Any array may be omitted.",
|
|
2551
|
+
properties: {
|
|
2552
|
+
workflows: {
|
|
2553
|
+
type: "array",
|
|
2554
|
+
items: {
|
|
2555
|
+
type: "object",
|
|
2556
|
+
additionalProperties: true
|
|
2557
|
+
}
|
|
2558
|
+
},
|
|
2559
|
+
loops: {
|
|
2560
|
+
type: "array",
|
|
2561
|
+
items: {
|
|
2562
|
+
type: "object",
|
|
2563
|
+
additionalProperties: true
|
|
1932
2564
|
}
|
|
1933
2565
|
},
|
|
1934
2566
|
runs: {
|
|
@@ -1940,7 +2572,15 @@ var loops_default = {
|
|
|
1940
2572
|
},
|
|
1941
2573
|
replace: {
|
|
1942
2574
|
type: "boolean",
|
|
1943
|
-
description: "Update existing rows whose id matches
|
|
2575
|
+
description: "Update existing rows whose id matches. Default safe imports may still archive/pause same-id workflows/loops when preserve flags are false."
|
|
2576
|
+
},
|
|
2577
|
+
preserveLoopScheduling: {
|
|
2578
|
+
type: "boolean",
|
|
2579
|
+
description: "Default false. When false, imported loops are written paused with nextRunAt and retryScheduledFor cleared so backfills never schedule immediately."
|
|
2580
|
+
},
|
|
2581
|
+
preserveWorkflowActivation: {
|
|
2582
|
+
type: "boolean",
|
|
2583
|
+
description: "Default false. When false, imported workflows are written archived, including same-id active rows, so backfills cannot activate workflow execution unless explicitly requested."
|
|
1944
2584
|
}
|
|
1945
2585
|
}
|
|
1946
2586
|
},
|
|
@@ -2175,6 +2815,21 @@ async function handleV1Request(ctx) {
|
|
|
2175
2815
|
return errorResponse(error);
|
|
2176
2816
|
}
|
|
2177
2817
|
}
|
|
2818
|
+
function safeImportedWorkflow(workflow, opts) {
|
|
2819
|
+
if (opts.preserveWorkflowActivation)
|
|
2820
|
+
return workflow;
|
|
2821
|
+
return { ...workflow, status: "archived" };
|
|
2822
|
+
}
|
|
2823
|
+
function safeImportedLoop(loop, opts) {
|
|
2824
|
+
if (opts.preserveLoopScheduling)
|
|
2825
|
+
return loop;
|
|
2826
|
+
return {
|
|
2827
|
+
...loop,
|
|
2828
|
+
status: "paused",
|
|
2829
|
+
nextRunAt: undefined,
|
|
2830
|
+
retryScheduledFor: undefined
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2178
2833
|
async function handleImportRequest(ctx, segments) {
|
|
2179
2834
|
if (segments.length !== 0 || ctx.request.method !== "POST")
|
|
2180
2835
|
return fail("not_found", 404);
|
|
@@ -2184,14 +2839,20 @@ async function handleImportRequest(ctx, segments) {
|
|
|
2184
2839
|
const workflows = Array.isArray(body.workflows) ? body.workflows : [];
|
|
2185
2840
|
const loops = Array.isArray(body.loops) ? body.loops : [];
|
|
2186
2841
|
const runs = Array.isArray(body.runs) ? body.runs : [];
|
|
2842
|
+
const preserveWorkflowActivation = body.preserveWorkflowActivation === true;
|
|
2843
|
+
const preserveLoopScheduling = body.preserveLoopScheduling === true;
|
|
2187
2844
|
const imported = { workflows: 0, loops: 0, runs: 0 };
|
|
2188
2845
|
let skippedRunning = 0;
|
|
2189
2846
|
for (const workflow of workflows) {
|
|
2190
|
-
await storage.upsertMigrationWorkflow(workflow, {
|
|
2847
|
+
await storage.upsertMigrationWorkflow(safeImportedWorkflow(workflow, { preserveWorkflowActivation }), {
|
|
2848
|
+
replace: replace || !preserveWorkflowActivation
|
|
2849
|
+
});
|
|
2191
2850
|
imported.workflows += 1;
|
|
2192
2851
|
}
|
|
2193
2852
|
for (const loop of loops) {
|
|
2194
|
-
await storage.upsertMigrationLoop(loop, {
|
|
2853
|
+
await storage.upsertMigrationLoop(safeImportedLoop(loop, { preserveLoopScheduling }), {
|
|
2854
|
+
replace: replace || !preserveLoopScheduling
|
|
2855
|
+
});
|
|
2195
2856
|
imported.loops += 1;
|
|
2196
2857
|
}
|
|
2197
2858
|
for (const run of runs) {
|
|
@@ -2350,6 +3011,10 @@ async function handleWorkItemsRequest(ctx, segments) {
|
|
|
2350
3011
|
});
|
|
2351
3012
|
return ok({ workItems: items.map(publicWorkflowWorkItem) });
|
|
2352
3013
|
}
|
|
3014
|
+
if (segments.length === 0 && ctx.request.method === "POST") {
|
|
3015
|
+
const body = await readJsonBody(ctx.request, ctx.bodyLimitBytes);
|
|
3016
|
+
return ok({ workItem: publicWorkflowWorkItem(await storage.upsertWorkflowWorkItem(body)) }, { status: 201 });
|
|
3017
|
+
}
|
|
2353
3018
|
const id = segments[0];
|
|
2354
3019
|
if (!id)
|
|
2355
3020
|
return fail("not_found", 404);
|
|
@@ -2367,6 +3032,10 @@ async function handleInvocationsRequest(ctx, segments) {
|
|
|
2367
3032
|
const invocations = await storage.listWorkflowInvocations({ limit: optionalLimit(ctx.url.searchParams.get("limit")) });
|
|
2368
3033
|
return ok({ invocations: invocations.map(publicWorkflowInvocation) });
|
|
2369
3034
|
}
|
|
3035
|
+
if (segments.length === 0 && ctx.request.method === "POST") {
|
|
3036
|
+
const body = await readJsonBody(ctx.request, ctx.bodyLimitBytes);
|
|
3037
|
+
return ok({ invocation: publicWorkflowInvocation(await storage.createWorkflowInvocation(body)) }, { status: 201 });
|
|
3038
|
+
}
|
|
2370
3039
|
const id = segments[0];
|
|
2371
3040
|
if (!id)
|
|
2372
3041
|
return fail("not_found", 404);
|