ultravisor 1.0.24 → 1.0.26
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/docs/_sidebar.md +1 -0
- package/docs/features/persistence-via-databeacon.md +1211 -0
- package/package.json +6 -6
- package/source/cli/Ultravisor-CLIProgram.cjs +80 -0
- package/source/config/Ultravisor-Default-Command-Configuration.cjs +9 -1
- package/source/datamodel/Ultravisor-Fleet.json +66 -0
- package/source/persistence/UltravisorPersistenceSchema.json +240 -0
- package/source/services/Ultravisor-AuthBeaconBridge.cjs +271 -0
- package/source/services/Ultravisor-Beacon-Coordinator.cjs +339 -151
- package/source/services/Ultravisor-Beacon-Scheduler.cjs +65 -29
- package/source/services/Ultravisor-DirectoryDistributor.cjs +280 -0
- package/source/services/Ultravisor-ExecutionManifest.cjs +99 -4
- package/source/services/Ultravisor-FleetManager.cjs +871 -0
- package/source/services/Ultravisor-ManifestStoreBridge.cjs +1134 -0
- package/source/services/Ultravisor-QueuePersistenceBridge.cjs +1336 -0
- package/source/services/persistence/Ultravisor-Beacon-FleetStore.cjs +570 -0
- package/source/web_server/Ultravisor-API-Server.cjs +1185 -90
- package/test/fleetstore-smoke.js +152 -0
- package/webinterface/package.json +1 -0
- package/webinterface/source/Pict-Application-Ultravisor.js +59 -2
- package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +12 -0
- package/webinterface/source/views/PictView-Ultravisor-Fleet.js +489 -0
- package/webinterface/source/views/PictView-Ultravisor-Login.js +74 -0
- package/webinterface/source/views/PictView-Ultravisor-TopBar.js +26 -0
- package/webinterface/source/views/PictView-Ultravisor-UserManagement.js +159 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultravisor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Cyclic process execution with ai integration.",
|
|
5
5
|
"main": "source/Ultravisor.cjs",
|
|
6
6
|
"bin": {
|
|
@@ -29,20 +29,20 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"better-sqlite3": "^11.10.0",
|
|
31
31
|
"cron": "^4.4.0",
|
|
32
|
-
"orator": "^6.0
|
|
33
|
-
"orator-authentication": "^1.0.
|
|
32
|
+
"orator": "^6.1.0",
|
|
33
|
+
"orator-authentication": "^1.0.1",
|
|
34
34
|
"orator-serviceserver-restify": "^2.0.10",
|
|
35
|
-
"pict": "^1.0.
|
|
35
|
+
"pict": "^1.0.365",
|
|
36
36
|
"pict-service-commandlineutility": "^1.0.19",
|
|
37
37
|
"pict-serviceproviderbase": "^1.0.4",
|
|
38
|
-
"ultravisor-beacon": "^0.0.
|
|
38
|
+
"ultravisor-beacon": "^0.0.13",
|
|
39
39
|
"ultravisor-file-stream": "^0.0.1",
|
|
40
40
|
"ws": "^8.20.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"pict-docuserve": "^0.1.5",
|
|
44
44
|
"puppeteer": "^24.40.0",
|
|
45
|
-
"quackage": "^1.1.
|
|
45
|
+
"quackage": "^1.1.2"
|
|
46
46
|
},
|
|
47
47
|
"mocha": {
|
|
48
48
|
"diff": true,
|
|
@@ -18,6 +18,14 @@ const libServiceExecutionManifest = require('../services/Ultravisor-ExecutionMan
|
|
|
18
18
|
const libServiceBeaconCoordinator = require('../services/Ultravisor-Beacon-Coordinator.cjs');
|
|
19
19
|
const libServiceBeaconReachability = require('../services/Ultravisor-Beacon-Reachability.cjs');
|
|
20
20
|
const libServiceBeaconQueueJournal = require('../services/persistence/Ultravisor-Beacon-QueueJournal.cjs');
|
|
21
|
+
const libServiceBeaconQueueStore = require('../services/persistence/Ultravisor-Beacon-QueueStore.cjs');
|
|
22
|
+
const libServiceBeaconScheduler = require('../services/Ultravisor-Beacon-Scheduler.cjs');
|
|
23
|
+
const libServiceBeaconFleetStore = require('../services/persistence/Ultravisor-Beacon-FleetStore.cjs');
|
|
24
|
+
const libServiceAuthBeaconBridge = require('../services/Ultravisor-AuthBeaconBridge.cjs');
|
|
25
|
+
const libServiceQueuePersistenceBridge = require('../services/Ultravisor-QueuePersistenceBridge.cjs');
|
|
26
|
+
const libServiceManifestStoreBridge = require('../services/Ultravisor-ManifestStoreBridge.cjs');
|
|
27
|
+
const libServiceDirectoryDistributor = require('../services/Ultravisor-DirectoryDistributor.cjs');
|
|
28
|
+
const libServiceFleetManager = require('../services/Ultravisor-FleetManager.cjs');
|
|
21
29
|
|
|
22
30
|
// TODO: Remove this when Restify is fixed.
|
|
23
31
|
process.removeAllListeners('warning')
|
|
@@ -207,6 +215,19 @@ if (tmpQueueJournal)
|
|
|
207
215
|
tmpQueueJournal.initialize(_Ultravisor_Pict.fable.settings.UltravisorFileStorePath);
|
|
208
216
|
}
|
|
209
217
|
|
|
218
|
+
// --- Beacon queue store (SQLite-backed history + per-item events) ---
|
|
219
|
+
// Distinct from the journal above: the journal is a write-ahead log for
|
|
220
|
+
// crash recovery of the in-flight queue; the store is a long-lived
|
|
221
|
+
// SQLite database that backs the /queue UI's history list and the
|
|
222
|
+
// /Beacon/Work/:hash/Events endpoint. Both can coexist — they were
|
|
223
|
+
// added in separate generations of the queue work.
|
|
224
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists('UltravisorBeaconQueueStore', libServiceBeaconQueueStore);
|
|
225
|
+
let tmpQueueStore = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconQueueStore'])[0];
|
|
226
|
+
if (tmpQueueStore)
|
|
227
|
+
{
|
|
228
|
+
tmpQueueStore.initialize(_Ultravisor_Pict.fable.settings.UltravisorFileStorePath);
|
|
229
|
+
}
|
|
230
|
+
|
|
210
231
|
// Restore persisted work queue from journal (if any)
|
|
211
232
|
let tmpCoordinator = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconCoordinator'])[0];
|
|
212
233
|
if (tmpCoordinator)
|
|
@@ -215,8 +236,67 @@ if (tmpCoordinator)
|
|
|
215
236
|
tmpCoordinator.loadActionCatalog();
|
|
216
237
|
}
|
|
217
238
|
|
|
239
|
+
// --- Fleet management (per-(beacon, model) install/enable state) ---
|
|
240
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
241
|
+
'UltravisorBeaconFleetStore', libServiceBeaconFleetStore);
|
|
242
|
+
let tmpFleetStore = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconFleetStore'])[0];
|
|
243
|
+
if (tmpFleetStore)
|
|
244
|
+
{
|
|
245
|
+
tmpFleetStore.initialize(_Ultravisor_Pict.fable.settings.UltravisorFileStorePath);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
249
|
+
'UltravisorDirectoryDistributor', libServiceDirectoryDistributor);
|
|
250
|
+
|
|
251
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
252
|
+
'UltravisorFleetManager', libServiceFleetManager);
|
|
253
|
+
|
|
254
|
+
// --- Beacon scheduler (queue.* topic broadcasts + dispatch tick) ---
|
|
255
|
+
// The scheduler must exist before the APIServer wires its broadcast
|
|
256
|
+
// handler — see Ultravisor-API-Server._initializeWebSocket where it
|
|
257
|
+
// calls scheduler.setBroadcastHandler(...). After APIServer is up,
|
|
258
|
+
// we kick off the dispatch/health/summary timers via .start().
|
|
259
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
260
|
+
'UltravisorBeaconScheduler', libServiceBeaconScheduler);
|
|
261
|
+
|
|
262
|
+
// --- Auth beacon bridge (consults the optional Authentication-capable
|
|
263
|
+
// beacon for session validation + non-promiscuous mode admission). The
|
|
264
|
+
// bridge is always installed; it just resolves to "not available" when
|
|
265
|
+
// no auth beacon is connected, so the rest of the hub keeps working
|
|
266
|
+
// without it. See source/services/Ultravisor-AuthBeaconBridge.cjs and
|
|
267
|
+
// the matching ultravisor-auth-beacon module under modules/apps/.
|
|
268
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
269
|
+
'UltravisorAuthBeaconBridge', libServiceAuthBeaconBridge);
|
|
270
|
+
|
|
271
|
+
// --- Queue persistence bridge (consults the optional QueuePersistence
|
|
272
|
+
// beacon for durable queue + event log storage). Like the auth bridge,
|
|
273
|
+
// it's always installed and falls back to the in-process
|
|
274
|
+
// UltravisorBeaconQueueStore when no beacon is connected. The
|
|
275
|
+
// coordinator + scheduler call into the bridge for every persistence
|
|
276
|
+
// op; switching to a beacon-backed backend is a runtime decision.
|
|
277
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
278
|
+
'UltravisorQueuePersistenceBridge', libServiceQueuePersistenceBridge);
|
|
279
|
+
|
|
280
|
+
// --- Manifest store bridge (consults the optional ManifestStore
|
|
281
|
+
// beacon for durable execution-manifest storage). Same shape as the
|
|
282
|
+
// queue bridge: always installed, falls back to the in-process
|
|
283
|
+
// UltravisorExecutionManifest service when no beacon is connected.
|
|
284
|
+
// Persistence calls (finalizeExecution, abandonRun) go through this
|
|
285
|
+
// bridge instead of directly writing JSON files.
|
|
286
|
+
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
|
|
287
|
+
'UltravisorManifestStoreBridge', libServiceManifestStoreBridge);
|
|
288
|
+
|
|
218
289
|
_Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists('UltravisorAPIServer', libWebServerAPIServer);
|
|
219
290
|
|
|
291
|
+
// Kick the scheduler timers AFTER the API server has wired
|
|
292
|
+
// setBroadcastHandler — otherwise the first summary tick has nowhere
|
|
293
|
+
// to fan out to and is silently dropped.
|
|
294
|
+
let tmpScheduler = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconScheduler'])[0];
|
|
295
|
+
if (tmpScheduler && typeof tmpScheduler.start === 'function')
|
|
296
|
+
{
|
|
297
|
+
tmpScheduler.start();
|
|
298
|
+
}
|
|
299
|
+
|
|
220
300
|
// ── Service name aliases ────────────────────────────────
|
|
221
301
|
// Some CLI commands access services by hyphenated names via this.fable['Name'].
|
|
222
302
|
// Bridge the camelCase registration to hyphenated access.
|
|
@@ -16,5 +16,13 @@ module.exports = (
|
|
|
16
16
|
"UltravisorBeaconWorkItemTimeoutMs": 300000,
|
|
17
17
|
"UltravisorBeaconAffinityTTLMs": 3600000,
|
|
18
18
|
"UltravisorBeaconPollIntervalMs": 5000,
|
|
19
|
-
"UltravisorBeaconJournalCompactThreshold": 500
|
|
19
|
+
"UltravisorBeaconJournalCompactThreshold": 500,
|
|
20
|
+
|
|
21
|
+
// Optional non-promiscuous mode. When true, every BeaconRegister
|
|
22
|
+
// must present a JoinSecret that either (a) matches the bootstrap
|
|
23
|
+
// secret below, for the auth beacon's own admission, or (b) is
|
|
24
|
+
// validated by the auth beacon's AUTH_ValidateBeaconJoin action.
|
|
25
|
+
// Default false → behavior identical to pre-auth-beacon ultravisor.
|
|
26
|
+
"UltravisorNonPromiscuous": false,
|
|
27
|
+
"UltravisorBootstrapAuthSecret": ""
|
|
20
28
|
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Tables":
|
|
3
|
+
{
|
|
4
|
+
"BeaconModelInstallation":
|
|
5
|
+
{
|
|
6
|
+
"TableName": "BeaconModelInstallation",
|
|
7
|
+
"Domain": "Default",
|
|
8
|
+
"Description": "Per-(beacon, model) installation state. Authoritative source of truth for which beacons may be dispatched a given model. Status tracks install lifecycle; EnabledForDispatch is a separate operator toggle.",
|
|
9
|
+
"Columns":
|
|
10
|
+
[
|
|
11
|
+
{ "Column": "IDBeaconModelInstallation", "DataType": "ID" },
|
|
12
|
+
{ "Column": "GUIDBeaconModelInstallation", "DataType": "GUID", "Size": "36" },
|
|
13
|
+
{ "Column": "CreateDate", "DataType": "DateTime" },
|
|
14
|
+
{ "Column": "CreatingIDUser", "DataType": "Numeric", "Size": "int" },
|
|
15
|
+
{ "Column": "UpdateDate", "DataType": "DateTime" },
|
|
16
|
+
{ "Column": "UpdatingIDUser", "DataType": "Numeric", "Size": "int" },
|
|
17
|
+
{ "Column": "Deleted", "DataType": "Boolean" },
|
|
18
|
+
{ "Column": "DeleteDate", "DataType": "DateTime" },
|
|
19
|
+
{ "Column": "DeletingIDUser", "DataType": "Numeric", "Size": "int" },
|
|
20
|
+
{ "Column": "BeaconID", "DataType": "String", "Size": "128" },
|
|
21
|
+
{ "Column": "BeaconName", "DataType": "String", "Size": "256" },
|
|
22
|
+
{ "Column": "ModelKey", "DataType": "String", "Size": "256" },
|
|
23
|
+
{ "Column": "ModelName", "DataType": "String", "Size": "256" },
|
|
24
|
+
{ "Column": "ModelSourceDir", "DataType": "String", "Size": "1024" },
|
|
25
|
+
{ "Column": "ExpectedTreeHash", "DataType": "String", "Size": "128" },
|
|
26
|
+
{ "Column": "InstalledTreeHash", "DataType": "String", "Size": "128" },
|
|
27
|
+
{ "Column": "InstalledBytes", "DataType": "Numeric", "Size": "bigint" },
|
|
28
|
+
{ "Column": "Status", "DataType": "String", "Size": "32" },
|
|
29
|
+
{ "Column": "EnabledForDispatch", "DataType": "Boolean" },
|
|
30
|
+
{ "Column": "PushProgressBytes", "DataType": "Numeric", "Size": "bigint" },
|
|
31
|
+
{ "Column": "PushTotalBytes", "DataType": "Numeric", "Size": "bigint" },
|
|
32
|
+
{ "Column": "LastError", "DataType": "String", "Size": "4096" },
|
|
33
|
+
{ "Column": "InstalledAt", "DataType": "DateTime" },
|
|
34
|
+
{ "Column": "LastUpdatedAt", "DataType": "DateTime" },
|
|
35
|
+
{ "Column": "Source", "DataType": "String", "Size": "32" }
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"BeaconRuntimeInstallation":
|
|
39
|
+
{
|
|
40
|
+
"TableName": "BeaconRuntimeInstallation",
|
|
41
|
+
"Domain": "Default",
|
|
42
|
+
"Description": "Per-(beacon, runtime) state. A runtime is a named directory of pipeline-worker code (e.g. retold-labs' examples/pipeline-workers). Auto-pushed on beacon connect. Tracks last-known runtime hash so the hub can short-circuit pushes when the worker is already up to date.",
|
|
43
|
+
"Columns":
|
|
44
|
+
[
|
|
45
|
+
{ "Column": "IDBeaconRuntimeInstallation", "DataType": "ID" },
|
|
46
|
+
{ "Column": "GUIDBeaconRuntimeInstallation", "DataType": "GUID", "Size": "36" },
|
|
47
|
+
{ "Column": "CreateDate", "DataType": "DateTime" },
|
|
48
|
+
{ "Column": "CreatingIDUser", "DataType": "Numeric", "Size": "int" },
|
|
49
|
+
{ "Column": "UpdateDate", "DataType": "DateTime" },
|
|
50
|
+
{ "Column": "UpdatingIDUser", "DataType": "Numeric", "Size": "int" },
|
|
51
|
+
{ "Column": "Deleted", "DataType": "Boolean" },
|
|
52
|
+
{ "Column": "DeleteDate", "DataType": "DateTime" },
|
|
53
|
+
{ "Column": "DeletingIDUser", "DataType": "Numeric", "Size": "int" },
|
|
54
|
+
{ "Column": "BeaconID", "DataType": "String", "Size": "128" },
|
|
55
|
+
{ "Column": "BeaconName", "DataType": "String", "Size": "256" },
|
|
56
|
+
{ "Column": "RuntimeName", "DataType": "String", "Size": "128" },
|
|
57
|
+
{ "Column": "ExpectedRuntimeHash", "DataType": "String", "Size": "128" },
|
|
58
|
+
{ "Column": "InstalledRuntimeHash", "DataType": "String", "Size": "128" },
|
|
59
|
+
{ "Column": "Status", "DataType": "String", "Size": "32" },
|
|
60
|
+
{ "Column": "LastError", "DataType": "String", "Size": "4096" },
|
|
61
|
+
{ "Column": "InstalledAt", "DataType": "DateTime" },
|
|
62
|
+
{ "Column": "LastUpdatedAt", "DataType": "DateTime" }
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Ultravisor persistence schema — source of truth for the four tables used when ultravisor's QueuePersistenceBridge / ManifestStoreBridge route through retold-databeacon's MeadowProxy. See modules/apps/ultravisor/docs/features/persistence-via-databeacon.md for the architectural context.",
|
|
3
|
+
|
|
4
|
+
"SchemaName": "ultravisor",
|
|
5
|
+
"Version": 1,
|
|
6
|
+
|
|
7
|
+
"Tables":
|
|
8
|
+
[
|
|
9
|
+
{
|
|
10
|
+
"Scope": "UVQueueWorkItem",
|
|
11
|
+
"DefaultIdentifier": "IDUVQueueWorkItem",
|
|
12
|
+
"Domain": "Ultravisor",
|
|
13
|
+
"Schema":
|
|
14
|
+
[
|
|
15
|
+
{ "Column": "IDUVQueueWorkItem", "Type": "AutoIdentity", "Size": "Default" },
|
|
16
|
+
{ "Column": "WorkItemHash", "Type": "String", "Size": "128" },
|
|
17
|
+
{ "Column": "RunID", "Type": "String", "Size": "128" },
|
|
18
|
+
{ "Column": "RunHash", "Type": "String", "Size": "128" },
|
|
19
|
+
{ "Column": "NodeHash", "Type": "String", "Size": "128" },
|
|
20
|
+
{ "Column": "OperationHash", "Type": "String", "Size": "128" },
|
|
21
|
+
{ "Column": "Capability", "Type": "String", "Size": "128" },
|
|
22
|
+
{ "Column": "Action", "Type": "String", "Size": "128" },
|
|
23
|
+
{ "Column": "Settings", "Type": "String", "Size": "Default" },
|
|
24
|
+
{ "Column": "AffinityKey", "Type": "String", "Size": "128" },
|
|
25
|
+
{ "Column": "AssignedBeaconID", "Type": "String", "Size": "128" },
|
|
26
|
+
{ "Column": "Status", "Type": "String", "Size": "32" },
|
|
27
|
+
{ "Column": "Priority", "Type": "Integer", "Size": "int" },
|
|
28
|
+
{ "Column": "EnqueuedAt", "Type": "String", "Size": "50" },
|
|
29
|
+
{ "Column": "DispatchedAt", "Type": "String", "Size": "50" },
|
|
30
|
+
{ "Column": "StartedAt", "Type": "String", "Size": "50" },
|
|
31
|
+
{ "Column": "ClaimedAt", "Type": "String", "Size": "50" },
|
|
32
|
+
{ "Column": "CompletedAt", "Type": "String", "Size": "50" },
|
|
33
|
+
{ "Column": "CanceledAt", "Type": "String", "Size": "50" },
|
|
34
|
+
{ "Column": "AssignedAt", "Type": "String", "Size": "50" },
|
|
35
|
+
{ "Column": "LastEventAt", "Type": "String", "Size": "50" },
|
|
36
|
+
{ "Column": "QueueWaitMs", "Type": "Integer", "Size": "int" },
|
|
37
|
+
{ "Column": "TimeoutMs", "Type": "Integer", "Size": "int" },
|
|
38
|
+
{ "Column": "Health", "Type": "Float", "Size": "Default" },
|
|
39
|
+
{ "Column": "HealthLabel", "Type": "String", "Size": "32" },
|
|
40
|
+
{ "Column": "HealthReason", "Type": "String", "Size": "512" },
|
|
41
|
+
{ "Column": "HealthComputedAt", "Type": "String", "Size": "50" },
|
|
42
|
+
{ "Column": "AttemptNumber", "Type": "Integer", "Size": "int" },
|
|
43
|
+
{ "Column": "MaxAttempts", "Type": "Integer", "Size": "int" },
|
|
44
|
+
{ "Column": "RetryBackoffMs", "Type": "Integer", "Size": "int" },
|
|
45
|
+
{ "Column": "RetryAfter", "Type": "String", "Size": "50" },
|
|
46
|
+
{ "Column": "LastError", "Type": "String", "Size": "Default" },
|
|
47
|
+
{ "Column": "Result", "Type": "String", "Size": "Default" },
|
|
48
|
+
{ "Column": "CancelRequested", "Type": "Boolean", "Size": "Default" },
|
|
49
|
+
{ "Column": "CancelReason", "Type": "String", "Size": "512" },
|
|
50
|
+
{ "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
|
|
51
|
+
{ "Column": "UpdateDate", "Type": "UpdateDate", "Size": "Default" },
|
|
52
|
+
{ "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
|
|
53
|
+
],
|
|
54
|
+
"DefaultObject":
|
|
55
|
+
{
|
|
56
|
+
"IDUVQueueWorkItem": 0,
|
|
57
|
+
"WorkItemHash": "",
|
|
58
|
+
"RunID": "",
|
|
59
|
+
"RunHash": "",
|
|
60
|
+
"NodeHash": "",
|
|
61
|
+
"OperationHash": "",
|
|
62
|
+
"Capability": "",
|
|
63
|
+
"Action": "",
|
|
64
|
+
"Settings": "{}",
|
|
65
|
+
"AffinityKey": "",
|
|
66
|
+
"AssignedBeaconID": "",
|
|
67
|
+
"Status": "Pending",
|
|
68
|
+
"Priority": 0,
|
|
69
|
+
"EnqueuedAt": "",
|
|
70
|
+
"DispatchedAt": "",
|
|
71
|
+
"StartedAt": "",
|
|
72
|
+
"ClaimedAt": "",
|
|
73
|
+
"CompletedAt": "",
|
|
74
|
+
"CanceledAt": "",
|
|
75
|
+
"AssignedAt": "",
|
|
76
|
+
"LastEventAt": "",
|
|
77
|
+
"QueueWaitMs": 0,
|
|
78
|
+
"TimeoutMs": 0,
|
|
79
|
+
"Health": 0,
|
|
80
|
+
"HealthLabel": "Unknown",
|
|
81
|
+
"HealthReason": "",
|
|
82
|
+
"HealthComputedAt": "",
|
|
83
|
+
"AttemptNumber": 0,
|
|
84
|
+
"MaxAttempts": 1,
|
|
85
|
+
"RetryBackoffMs": 0,
|
|
86
|
+
"RetryAfter": "",
|
|
87
|
+
"LastError": "",
|
|
88
|
+
"Result": "",
|
|
89
|
+
"CancelRequested": false,
|
|
90
|
+
"CancelReason": "",
|
|
91
|
+
"Deleted": false
|
|
92
|
+
},
|
|
93
|
+
"Indexes":
|
|
94
|
+
[
|
|
95
|
+
{ "Name": "IX_UVQueueWorkItem_Hash", "Columns": ["WorkItemHash"], "Unique": true },
|
|
96
|
+
{ "Name": "IX_UVQueueWorkItem_Status", "Columns": ["Status"] },
|
|
97
|
+
{ "Name": "IX_UVQueueWorkItem_RunID", "Columns": ["RunID"] },
|
|
98
|
+
{ "Name": "IX_UVQueueWorkItem_Assigned", "Columns": ["AssignedBeaconID", "Status"] },
|
|
99
|
+
{ "Name": "IX_UVQueueWorkItem_Dispatch", "Columns": ["Status", "Priority", "EnqueuedAt"] }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
{
|
|
104
|
+
"Scope": "UVQueueWorkItemEvent",
|
|
105
|
+
"DefaultIdentifier": "IDUVQueueWorkItemEvent",
|
|
106
|
+
"Domain": "Ultravisor",
|
|
107
|
+
"_comment": "Append-only event log per work item. EventGUID is unique for idempotency on bootstrap-flush replay.",
|
|
108
|
+
"Schema":
|
|
109
|
+
[
|
|
110
|
+
{ "Column": "IDUVQueueWorkItemEvent", "Type": "AutoIdentity", "Size": "Default" },
|
|
111
|
+
{ "Column": "EventGUID", "Type": "String", "Size": "36" },
|
|
112
|
+
{ "Column": "WorkItemHash", "Type": "String", "Size": "128" },
|
|
113
|
+
{ "Column": "EventType", "Type": "String", "Size": "64" },
|
|
114
|
+
{ "Column": "Payload", "Type": "String", "Size": "Default" },
|
|
115
|
+
{ "Column": "EmittedAt", "Type": "String", "Size": "50" },
|
|
116
|
+
{ "Column": "Seq", "Type": "Integer", "Size": "int" },
|
|
117
|
+
{ "Column": "FromStatus", "Type": "String", "Size": "32" },
|
|
118
|
+
{ "Column": "ToStatus", "Type": "String", "Size": "32" },
|
|
119
|
+
{ "Column": "BeaconID", "Type": "String", "Size": "128" },
|
|
120
|
+
{ "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
|
|
121
|
+
{ "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
|
|
122
|
+
],
|
|
123
|
+
"DefaultObject":
|
|
124
|
+
{
|
|
125
|
+
"IDUVQueueWorkItemEvent": 0,
|
|
126
|
+
"EventGUID": "",
|
|
127
|
+
"WorkItemHash": "",
|
|
128
|
+
"EventType": "",
|
|
129
|
+
"Payload": "{}",
|
|
130
|
+
"EmittedAt": "",
|
|
131
|
+
"Seq": 0,
|
|
132
|
+
"FromStatus": "",
|
|
133
|
+
"ToStatus": "",
|
|
134
|
+
"BeaconID": "",
|
|
135
|
+
"Deleted": false
|
|
136
|
+
},
|
|
137
|
+
"Indexes":
|
|
138
|
+
[
|
|
139
|
+
{ "Name": "IX_UVQueueEvent_GUID", "Columns": ["EventGUID"], "Unique": true },
|
|
140
|
+
{ "Name": "IX_UVQueueEvent_Hash", "Columns": ["WorkItemHash"] }
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
"Scope": "UVQueueWorkItemAttempt",
|
|
146
|
+
"DefaultIdentifier": "IDUVQueueWorkItemAttempt",
|
|
147
|
+
"Domain": "Ultravisor",
|
|
148
|
+
"_comment": "One row per dispatch attempt. (WorkItemHash, AttemptNumber) is unique.",
|
|
149
|
+
"Schema":
|
|
150
|
+
[
|
|
151
|
+
{ "Column": "IDUVQueueWorkItemAttempt", "Type": "AutoIdentity", "Size": "Default" },
|
|
152
|
+
{ "Column": "WorkItemHash", "Type": "String", "Size": "128" },
|
|
153
|
+
{ "Column": "AttemptNumber", "Type": "Integer", "Size": "int" },
|
|
154
|
+
{ "Column": "BeaconID", "Type": "String", "Size": "128" },
|
|
155
|
+
{ "Column": "StartedAt", "Type": "String", "Size": "50" },
|
|
156
|
+
{ "Column": "EndedAt", "Type": "String", "Size": "50" },
|
|
157
|
+
{ "Column": "Outcome", "Type": "String", "Size": "32" },
|
|
158
|
+
{ "Column": "Error", "Type": "String", "Size": "Default" },
|
|
159
|
+
{ "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
|
|
160
|
+
{ "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
|
|
161
|
+
],
|
|
162
|
+
"DefaultObject":
|
|
163
|
+
{
|
|
164
|
+
"IDUVQueueWorkItemAttempt": 0,
|
|
165
|
+
"WorkItemHash": "",
|
|
166
|
+
"AttemptNumber": 0,
|
|
167
|
+
"BeaconID": "",
|
|
168
|
+
"StartedAt": "",
|
|
169
|
+
"EndedAt": "",
|
|
170
|
+
"Outcome": "",
|
|
171
|
+
"Error": "",
|
|
172
|
+
"Deleted": false
|
|
173
|
+
},
|
|
174
|
+
"Indexes":
|
|
175
|
+
[
|
|
176
|
+
{ "Name": "IX_UVQueueAttempt_HashNum", "Columns": ["WorkItemHash", "AttemptNumber"], "Unique": true }
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
"Scope": "UVManifest",
|
|
182
|
+
"DefaultIdentifier": "IDUVManifest",
|
|
183
|
+
"Domain": "Ultravisor",
|
|
184
|
+
"_comment": "Execution manifests, one row per run. ManifestJSON is the full wire-safe blob produced by _cleanManifestForWire.",
|
|
185
|
+
"Schema":
|
|
186
|
+
[
|
|
187
|
+
{ "Column": "IDUVManifest", "Type": "AutoIdentity", "Size": "Default" },
|
|
188
|
+
{ "Column": "Hash", "Type": "String", "Size": "128" },
|
|
189
|
+
{ "Column": "OperationHash", "Type": "String", "Size": "128" },
|
|
190
|
+
{ "Column": "OperationName", "Type": "String", "Size": "500" },
|
|
191
|
+
{ "Column": "Status", "Type": "String", "Size": "32" },
|
|
192
|
+
{ "Column": "RunMode", "Type": "String", "Size": "32" },
|
|
193
|
+
{ "Column": "Live", "Type": "Boolean", "Size": "Default" },
|
|
194
|
+
{ "Column": "StartTime", "Type": "String", "Size": "50" },
|
|
195
|
+
{ "Column": "StopTime", "Type": "String", "Size": "50" },
|
|
196
|
+
{ "Column": "ElapsedMs", "Type": "Integer", "Size": "int" },
|
|
197
|
+
{ "Column": "ManifestJSON", "Type": "String", "Size": "Default" },
|
|
198
|
+
{ "Column": "StagingPath", "Type": "String", "Size": "1024" },
|
|
199
|
+
{ "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
|
|
200
|
+
{ "Column": "UpdateDate", "Type": "UpdateDate", "Size": "Default" },
|
|
201
|
+
{ "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
|
|
202
|
+
],
|
|
203
|
+
"DefaultObject":
|
|
204
|
+
{
|
|
205
|
+
"IDUVManifest": 0,
|
|
206
|
+
"Hash": "",
|
|
207
|
+
"OperationHash": "",
|
|
208
|
+
"OperationName": "",
|
|
209
|
+
"Status": "",
|
|
210
|
+
"RunMode": "",
|
|
211
|
+
"Live": false,
|
|
212
|
+
"StartTime": "",
|
|
213
|
+
"StopTime": "",
|
|
214
|
+
"ElapsedMs": 0,
|
|
215
|
+
"ManifestJSON": "{}",
|
|
216
|
+
"StagingPath": "",
|
|
217
|
+
"Deleted": false
|
|
218
|
+
},
|
|
219
|
+
"Indexes":
|
|
220
|
+
[
|
|
221
|
+
{ "Name": "IX_UVManifest_Hash", "Columns": ["Hash"], "Unique": true },
|
|
222
|
+
{ "Name": "IX_UVManifest_StatusStop","Columns": ["Status", "StopTime"] },
|
|
223
|
+
{ "Name": "IX_UVManifest_OpHash", "Columns": ["OperationHash"] }
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
|
|
228
|
+
"_notes":
|
|
229
|
+
{
|
|
230
|
+
"meadowConventions": "Type values follow meadow column conventions. AutoIdentity → engine PK; CreateDate / UpdateDate / Deleted → meadow's auto-managed audit columns. The per-engine Meadow-Schema-<engine>.js files translate these to engine-specific DDL.",
|
|
231
|
+
|
|
232
|
+
"indexes": "Indexes here are NOT part of meadow's standard table descriptor. retold-databeacon's EnsureSchema action will issue them as CREATE INDEX IF NOT EXISTS statements after the meadow table create. Per-engine syntax differences (e.g. SQL Server's CREATE INDEX vs Postgres's CREATE INDEX IF NOT EXISTS) handled in the engine-specific path.",
|
|
233
|
+
|
|
234
|
+
"jsonColumns": "Settings (work item), Payload (event), Result, LastError, ManifestJSON are stored as opaque strings (JSON-encoded by callers). We don't use a native JSON column type because not all meadow connectors support it uniformly (mssql JSON support varies; sqlite has none). Callers are responsible for parse/stringify on the boundary.",
|
|
235
|
+
|
|
236
|
+
"floatColumns": "Health is stored as Float (0..1 score). Connector translation: REAL on sqlite, DOUBLE on mysql/postgres, FLOAT on mssql.",
|
|
237
|
+
|
|
238
|
+
"softDelete": "Deleted column matches meadow's standard soft-delete convention. The bridges' `removeManifest` etc. flip Deleted=1 rather than DROP. Hard-delete deferred to a separate retention sweep."
|
|
239
|
+
}
|
|
240
|
+
}
|