mdkg 0.1.10 → 0.2.0
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 +26 -0
- package/README.md +14 -11
- package/dist/cli.js +147 -6
- package/dist/commands/db.js +185 -1
- package/dist/core/project_db_migrations.js +24 -0
- package/dist/core/project_db_queue.js +186 -0
- package/dist/core/project_db_snapshot.js +28 -3
- package/dist/init/AGENT_START.md +10 -8
- package/dist/init/CLI_COMMAND_MATRIX.md +10 -5
- package/dist/init/README.md +11 -9
- package/dist/init/init-manifest.json +60 -5
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +8 -7
- package/dist/init/templates/skills/base.SKILL.md +66 -0
- package/dist/init/templates/specs/agent.SPEC.md +39 -0
- package/dist/init/templates/specs/api.SPEC.md +32 -0
- package/dist/init/templates/specs/base.SPEC.md +87 -0
- package/dist/init/templates/specs/capability.SPEC.md +32 -0
- package/dist/init/templates/specs/integration.SPEC.md +24 -0
- package/dist/init/templates/specs/model.SPEC.md +20 -0
- package/dist/init/templates/specs/omniruntime-agent.SPEC.md +39 -0
- package/dist/init/templates/specs/project.SPEC.md +26 -0
- package/dist/init/templates/specs/runtime-image.SPEC.md +20 -0
- package/dist/init/templates/specs/tool.SPEC.md +24 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ This project follows a pragmatic changelog style inspired by Keep a Changelog. V
|
|
|
6
6
|
|
|
7
7
|
mdkg is pre-v1 public alpha software. Command, graph, cache, bundle, and DAL contracts may change quickly while the project converges on a stable v1 surface.
|
|
8
8
|
|
|
9
|
+
## 0.2.0 - Unreleased
|
|
10
|
+
|
|
11
|
+
Release numbering note: future project DB materializer/profile release planning
|
|
12
|
+
should follow `0.1.9 -> 0.2.0` rather than continuing the line as `0.1.10`
|
|
13
|
+
when the release represents a capability-track boundary.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added public `mdkg db queue ...` commands for local project DB queue
|
|
18
|
+
create/pause/resume/enqueue/claim/ack/fail/dead-letter/release-expired/stats/list/show.
|
|
19
|
+
- Added `mdkg.project_db.queue_control.v1` / `005_mdkg_project_db_queue_control.sql`
|
|
20
|
+
for first-class queue active/paused state and migration backfill from existing
|
|
21
|
+
queue messages.
|
|
22
|
+
- Added queue-aware snapshot sealing policies: default `--queue-policy drain`
|
|
23
|
+
blocks ready/leased messages, while `--queue-policy paused` allows ready
|
|
24
|
+
messages only in paused queues and always blocks leased messages.
|
|
25
|
+
- Added packed CLI-only `smoke:db-queue-cli` coverage that exercises public
|
|
26
|
+
queue commands and pause/drain snapshot behavior from an installed tarball.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Source release line now targets `0.2.0` for the next project DB
|
|
31
|
+
materializer/profile capability track.
|
|
32
|
+
- Project DB queue support is now public under `mdkg db queue`; event, reducer,
|
|
33
|
+
writer lease, and materializer command surfaces remain internal-only.
|
|
34
|
+
|
|
9
35
|
## 0.1.10 - 2026-06-05
|
|
10
36
|
|
|
11
37
|
### Added
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ mdkg stays deliberately boring:
|
|
|
14
14
|
- first-class rebuildable SQLite cache through built-in `node:sqlite`
|
|
15
15
|
- no daemon, hosted index, or vector DB
|
|
16
16
|
|
|
17
|
-
Current package version in source: `0.
|
|
17
|
+
Current package version in source: `0.2.0`
|
|
18
18
|
|
|
19
19
|
mdkg is still pre-v1 public alpha software. The public package is usable, but graph, cache, bundle, and DAL contracts may continue to change quickly while the project converges on a stable v1 surface.
|
|
20
20
|
|
|
@@ -349,16 +349,16 @@ Runtime DB files, WAL, SHM, journal, lock, and temp files are ignored by
|
|
|
349
349
|
default. `mdkg db init` does not create an active runtime SQLite database.
|
|
350
350
|
Run `mdkg db migrate` after init to create or update the active runtime
|
|
351
351
|
SQLite database at the configured `db.runtime_path`; built-in migrations write
|
|
352
|
-
mdkg-owned generic foundation tables,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
Queue state is durable local delivery infrastructure, not canonical
|
|
356
|
-
history.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
public `mdkg db
|
|
352
|
+
mdkg-owned generic foundation tables, public local node:sqlite queue delivery
|
|
353
|
+
tables, internal local event/receipt/reducer tables, writer lease/CAS tables,
|
|
354
|
+
and queue control state, then record migration order, checksums, and applied
|
|
355
|
+
timestamps. Queue state is durable local delivery infrastructure, not canonical
|
|
356
|
+
event history. Use `mdkg db queue create|pause|resume|enqueue|claim|ack|fail|dead-letter|release-expired|stats|list|show`
|
|
357
|
+
to operate local project queues. Paused queues reject enqueue/claim while still
|
|
358
|
+
allowing ack/fail/dead-letter/release-expired so leased work can settle. Event
|
|
359
|
+
rows are durable local project DB history; receipts, reducers, writer leases,
|
|
360
|
+
and materializers remain internal helper surfaces in this release, with no
|
|
361
|
+
public `mdkg db event`, `mdkg db reducer`, `mdkg db lease`, or
|
|
362
362
|
`mdkg db materializer` CLI yet.
|
|
363
363
|
Use `mdkg db verify` for non-mutating health checks over config, layout,
|
|
364
364
|
runtime SQLite integrity, migration metadata, and transient runtime files. Use
|
|
@@ -366,6 +366,9 @@ runtime SQLite integrity, migration metadata, and transient runtime files. Use
|
|
|
366
366
|
receipt-file count, and state snapshot presence.
|
|
367
367
|
Use `mdkg db snapshot seal` to create an explicit sealed checkpoint at
|
|
368
368
|
`.mdkg/db/state/project.sqlite` with `.mdkg/db/state/project.manifest.json`.
|
|
369
|
+
The default queue policy is `--queue-policy drain`, which requires no ready or
|
|
370
|
+
leased queue messages. Use `--queue-policy paused` only when ready messages are
|
|
371
|
+
intentionally preserved in paused queues; leased messages always block sealing.
|
|
369
372
|
Use `mdkg db snapshot verify` and `mdkg db snapshot status` for checkpoint
|
|
370
373
|
health, and use `mdkg db snapshot dump` / `mdkg db snapshot diff` as
|
|
371
374
|
deterministic review aids for SQLite snapshots instead of comparing raw binary
|
package/dist/cli.js
CHANGED
|
@@ -194,18 +194,41 @@ function printDbHelp(log, subcommand) {
|
|
|
194
194
|
return;
|
|
195
195
|
case "snapshot":
|
|
196
196
|
log("Usage:");
|
|
197
|
-
log(" mdkg db snapshot seal [--json]");
|
|
197
|
+
log(" mdkg db snapshot seal [--queue-policy drain|paused] [--json]");
|
|
198
198
|
log(" mdkg db snapshot verify [--json]");
|
|
199
199
|
log(" mdkg db snapshot status [--json]");
|
|
200
200
|
log(" mdkg db snapshot dump [--snapshot <path>] [--output <path>] [--json]");
|
|
201
201
|
log(" mdkg db snapshot diff <left-snapshot> <right-snapshot> [--json]");
|
|
202
202
|
log("\nBoundaries:");
|
|
203
203
|
log(" - snapshot seal writes a clean opt-in sealed project DB checkpoint");
|
|
204
|
+
log(" - default queue policy is drain: no ready or leased queue messages");
|
|
205
|
+
log(" - paused queue policy allows ready messages only in paused queues and never leased messages");
|
|
204
206
|
log(" - snapshot verify/status read `.mdkg/db/state/project.sqlite` and its manifest");
|
|
205
207
|
log(" - snapshot dump/diff are deterministic review aids, not source of truth");
|
|
206
208
|
log(" - active runtime/WAL files remain ignored by default");
|
|
207
209
|
printGlobalOptions(log);
|
|
208
210
|
return;
|
|
211
|
+
case "queue":
|
|
212
|
+
log("Usage:");
|
|
213
|
+
log(" mdkg db queue create <queue> [--paused] [--reason <text>] [--json]");
|
|
214
|
+
log(" mdkg db queue pause <queue> [--reason <text>] [--json]");
|
|
215
|
+
log(" mdkg db queue resume <queue> [--json]");
|
|
216
|
+
log(" mdkg db queue enqueue <queue> <message-id> --payload-json <json>|--payload-file <path> [--dedupe-key <key>] [--available-at-ms <ms>] [--max-attempts <n>] [--json]");
|
|
217
|
+
log(" mdkg db queue claim <queue> --lease-owner <owner> --lease-ms <ms> [--json]");
|
|
218
|
+
log(" mdkg db queue ack <queue> <message-id> --lease-owner <owner> [--json]");
|
|
219
|
+
log(" mdkg db queue fail <queue> <message-id> --lease-owner <owner> --error <text> [--retry-after-ms <ms>] [--json]");
|
|
220
|
+
log(" mdkg db queue dead-letter <queue> <message-id> --lease-owner <owner> --error <text> [--json]");
|
|
221
|
+
log(" mdkg db queue release-expired [queue] [--json]");
|
|
222
|
+
log(" mdkg db queue stats [queue] [--json]");
|
|
223
|
+
log(" mdkg db queue list <queue> [--status ready|leased|acked|dead_letter|all] [--limit <n>] [--json]");
|
|
224
|
+
log(" mdkg db queue show <queue> <message-id> [--json]");
|
|
225
|
+
log("\nSemantics:");
|
|
226
|
+
log(" - queues are durable local delivery state, not canonical event history");
|
|
227
|
+
log(" - paused queues reject enqueue and claim");
|
|
228
|
+
log(" - ack, fail, dead-letter, and release-expired are allowed while paused so leased work can settle");
|
|
229
|
+
log(" - no raw SQL or hosted queue dependency is exposed");
|
|
230
|
+
printGlobalOptions(log);
|
|
231
|
+
return;
|
|
209
232
|
default:
|
|
210
233
|
log("Usage:");
|
|
211
234
|
log(" mdkg db index rebuild [--tolerant] [--json]");
|
|
@@ -215,7 +238,13 @@ function printDbHelp(log, subcommand) {
|
|
|
215
238
|
log(" mdkg db migrate [--json]");
|
|
216
239
|
log(" mdkg db verify [--json]");
|
|
217
240
|
log(" mdkg db stats [--json]");
|
|
218
|
-
log(" mdkg db
|
|
241
|
+
log(" mdkg db queue create <queue> [--paused] [--reason <text>] [--json]");
|
|
242
|
+
log(" mdkg db queue enqueue <queue> <message-id> --payload-json <json>|--payload-file <path> [--json]");
|
|
243
|
+
log(" mdkg db queue claim <queue> --lease-owner <owner> --lease-ms <ms> [--json]");
|
|
244
|
+
log(" mdkg db queue ack|fail|dead-letter <queue> <message-id> --lease-owner <owner> [--json]");
|
|
245
|
+
log(" mdkg db queue pause|resume <queue> [--json]");
|
|
246
|
+
log(" mdkg db queue stats|list|show ... [--json]");
|
|
247
|
+
log(" mdkg db snapshot seal [--queue-policy drain|paused] [--json]");
|
|
219
248
|
log(" mdkg db snapshot verify [--json]");
|
|
220
249
|
log(" mdkg db snapshot status [--json]");
|
|
221
250
|
log(" mdkg db snapshot dump [--snapshot <path>] [--output <path>] [--json]");
|
|
@@ -227,14 +256,15 @@ function printDbHelp(log, subcommand) {
|
|
|
227
256
|
log(" - `mdkg db init` does not create an active runtime SQLite database");
|
|
228
257
|
log(" - `mdkg db migrate` creates/updates the active runtime SQLite database");
|
|
229
258
|
log(" - `mdkg db migrate` applies mdkg-owned foundation plus internal queue, event, receipt, reducer, and lease migrations");
|
|
230
|
-
log(" - queue
|
|
259
|
+
log(" - `mdkg db queue ...` exposes local durable queue delivery operations");
|
|
260
|
+
log(" - paused queues reject enqueue/claim and can be sealed with explicit paused snapshot policy");
|
|
231
261
|
log(" - event rows are durable local history; receipts, reducers, and writer leases remain internal helper surfaces");
|
|
232
262
|
log(" - no public `mdkg db event`, `mdkg db reducer`, or `mdkg db lease` CLI is exposed");
|
|
233
263
|
log(" - `mdkg db verify` checks config, layout, SQLite integrity, migrations, and transient files");
|
|
234
264
|
log(" - `mdkg db stats` reports table counts, migration state, DB size, and receipt counts");
|
|
235
265
|
log(" - `mdkg db snapshot ...` manages opt-in sealed checkpoints and review dumps");
|
|
236
266
|
log(" - active `.mdkg/db/runtime` and transient DB files are ignored by default");
|
|
237
|
-
log(" - no raw SQL, hosted queue/event store, profile, public
|
|
267
|
+
log(" - no raw SQL, hosted queue/event store, profile, public event/reducer/lease command, or publish behavior is exposed here");
|
|
238
268
|
printGlobalOptions(log);
|
|
239
269
|
}
|
|
240
270
|
}
|
|
@@ -922,6 +952,16 @@ function parseNumberFlag(flag, value) {
|
|
|
922
952
|
}
|
|
923
953
|
return parsed;
|
|
924
954
|
}
|
|
955
|
+
function parseQueuePolicyFlag(value) {
|
|
956
|
+
const raw = requireFlagValue("--queue-policy", value);
|
|
957
|
+
if (raw === undefined) {
|
|
958
|
+
return undefined;
|
|
959
|
+
}
|
|
960
|
+
if (raw === "drain" || raw === "paused") {
|
|
961
|
+
return raw;
|
|
962
|
+
}
|
|
963
|
+
throw new errors_1.UsageError("--queue-policy must be drain or paused");
|
|
964
|
+
}
|
|
925
965
|
function parseEdgesFlag(value) {
|
|
926
966
|
if (value === undefined) {
|
|
927
967
|
return undefined;
|
|
@@ -1109,6 +1149,107 @@ function runDbSubcommand(parsed, root) {
|
|
|
1109
1149
|
(0, db_1.runDbStatsCommand)({ root, json: parseBooleanFlag("--json", parsed.flags["--json"]) });
|
|
1110
1150
|
return 0;
|
|
1111
1151
|
}
|
|
1152
|
+
case "queue": {
|
|
1153
|
+
const action = (parsed.positionals[2] ?? "").toLowerCase();
|
|
1154
|
+
const json = parseBooleanFlag("--json", parsed.flags["--json"]);
|
|
1155
|
+
const queueName = parsed.positionals[3];
|
|
1156
|
+
const messageId = parsed.positionals[4];
|
|
1157
|
+
const common = {
|
|
1158
|
+
root,
|
|
1159
|
+
json,
|
|
1160
|
+
queueName,
|
|
1161
|
+
messageId,
|
|
1162
|
+
leaseOwner: requireFlagValue("--lease-owner", parsed.flags["--lease-owner"]),
|
|
1163
|
+
leaseMs: parseNumberFlag("--lease-ms", parsed.flags["--lease-ms"]),
|
|
1164
|
+
payloadJson: requireFlagValue("--payload-json", parsed.flags["--payload-json"]),
|
|
1165
|
+
payloadFile: requireFlagValue("--payload-file", parsed.flags["--payload-file"]),
|
|
1166
|
+
dedupeKey: requireFlagValue("--dedupe-key", parsed.flags["--dedupe-key"]),
|
|
1167
|
+
availableAtMs: parseNumberFlag("--available-at-ms", parsed.flags["--available-at-ms"]),
|
|
1168
|
+
maxAttempts: parseNumberFlag("--max-attempts", parsed.flags["--max-attempts"]),
|
|
1169
|
+
retryAfterMs: parseNumberFlag("--retry-after-ms", parsed.flags["--retry-after-ms"]),
|
|
1170
|
+
error: requireFlagValue("--error", parsed.flags["--error"]),
|
|
1171
|
+
paused: parseBooleanFlag("--paused", parsed.flags["--paused"]),
|
|
1172
|
+
reason: requireFlagValue("--reason", parsed.flags["--reason"]),
|
|
1173
|
+
status: requireFlagValue("--status", parsed.flags["--status"]),
|
|
1174
|
+
limit: parseNumberFlag("--limit", parsed.flags["--limit"]),
|
|
1175
|
+
};
|
|
1176
|
+
switch (action) {
|
|
1177
|
+
case "create":
|
|
1178
|
+
if (!queueName || parsed.positionals.length > 4) {
|
|
1179
|
+
throw new errors_1.UsageError("mdkg db queue create requires <queue>");
|
|
1180
|
+
}
|
|
1181
|
+
(0, db_1.runDbQueueCreateCommand)(common);
|
|
1182
|
+
return 0;
|
|
1183
|
+
case "pause":
|
|
1184
|
+
if (!queueName || parsed.positionals.length > 4) {
|
|
1185
|
+
throw new errors_1.UsageError("mdkg db queue pause requires <queue>");
|
|
1186
|
+
}
|
|
1187
|
+
(0, db_1.runDbQueuePauseCommand)(common);
|
|
1188
|
+
return 0;
|
|
1189
|
+
case "resume":
|
|
1190
|
+
if (!queueName || parsed.positionals.length > 4) {
|
|
1191
|
+
throw new errors_1.UsageError("mdkg db queue resume requires <queue>");
|
|
1192
|
+
}
|
|
1193
|
+
(0, db_1.runDbQueueResumeCommand)(common);
|
|
1194
|
+
return 0;
|
|
1195
|
+
case "enqueue":
|
|
1196
|
+
if (!queueName || !messageId || parsed.positionals.length > 5) {
|
|
1197
|
+
throw new errors_1.UsageError("mdkg db queue enqueue requires <queue> <message-id>");
|
|
1198
|
+
}
|
|
1199
|
+
(0, db_1.runDbQueueEnqueueCommand)(common);
|
|
1200
|
+
return 0;
|
|
1201
|
+
case "claim":
|
|
1202
|
+
if (!queueName || parsed.positionals.length > 4) {
|
|
1203
|
+
throw new errors_1.UsageError("mdkg db queue claim requires <queue>");
|
|
1204
|
+
}
|
|
1205
|
+
(0, db_1.runDbQueueClaimCommand)(common);
|
|
1206
|
+
return 0;
|
|
1207
|
+
case "ack":
|
|
1208
|
+
if (!queueName || !messageId || parsed.positionals.length > 5) {
|
|
1209
|
+
throw new errors_1.UsageError("mdkg db queue ack requires <queue> <message-id>");
|
|
1210
|
+
}
|
|
1211
|
+
(0, db_1.runDbQueueAckCommand)(common);
|
|
1212
|
+
return 0;
|
|
1213
|
+
case "fail":
|
|
1214
|
+
if (!queueName || !messageId || parsed.positionals.length > 5) {
|
|
1215
|
+
throw new errors_1.UsageError("mdkg db queue fail requires <queue> <message-id>");
|
|
1216
|
+
}
|
|
1217
|
+
(0, db_1.runDbQueueFailCommand)(common);
|
|
1218
|
+
return 0;
|
|
1219
|
+
case "dead-letter":
|
|
1220
|
+
if (!queueName || !messageId || parsed.positionals.length > 5) {
|
|
1221
|
+
throw new errors_1.UsageError("mdkg db queue dead-letter requires <queue> <message-id>");
|
|
1222
|
+
}
|
|
1223
|
+
(0, db_1.runDbQueueDeadLetterCommand)(common);
|
|
1224
|
+
return 0;
|
|
1225
|
+
case "release-expired":
|
|
1226
|
+
if (parsed.positionals.length > 4) {
|
|
1227
|
+
throw new errors_1.UsageError("mdkg db queue release-expired accepts at most one queue");
|
|
1228
|
+
}
|
|
1229
|
+
(0, db_1.runDbQueueReleaseExpiredCommand)(common);
|
|
1230
|
+
return 0;
|
|
1231
|
+
case "stats":
|
|
1232
|
+
if (parsed.positionals.length > 4) {
|
|
1233
|
+
throw new errors_1.UsageError("mdkg db queue stats accepts at most one queue");
|
|
1234
|
+
}
|
|
1235
|
+
(0, db_1.runDbQueueStatsCommand)(common);
|
|
1236
|
+
return 0;
|
|
1237
|
+
case "list":
|
|
1238
|
+
if (!queueName || parsed.positionals.length > 4) {
|
|
1239
|
+
throw new errors_1.UsageError("mdkg db queue list requires <queue>");
|
|
1240
|
+
}
|
|
1241
|
+
(0, db_1.runDbQueueListCommand)(common);
|
|
1242
|
+
return 0;
|
|
1243
|
+
case "show":
|
|
1244
|
+
if (!queueName || !messageId || parsed.positionals.length > 5) {
|
|
1245
|
+
throw new errors_1.UsageError("mdkg db queue show requires <queue> <message-id>");
|
|
1246
|
+
}
|
|
1247
|
+
(0, db_1.runDbQueueShowCommand)(common);
|
|
1248
|
+
return 0;
|
|
1249
|
+
default:
|
|
1250
|
+
throw new errors_1.UsageError("mdkg db queue requires create/pause/resume/enqueue/claim/ack/fail/dead-letter/release-expired/stats/list/show");
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1112
1253
|
case "snapshot": {
|
|
1113
1254
|
const action = (parsed.positionals[2] ?? "").toLowerCase();
|
|
1114
1255
|
const json = parseBooleanFlag("--json", parsed.flags["--json"]);
|
|
@@ -1117,7 +1258,7 @@ function runDbSubcommand(parsed, root) {
|
|
|
1117
1258
|
if (parsed.positionals.length > 3) {
|
|
1118
1259
|
throw new errors_1.UsageError("mdkg db snapshot seal does not accept positional arguments");
|
|
1119
1260
|
}
|
|
1120
|
-
(0, db_1.runDbSnapshotSealCommand)({ root, json });
|
|
1261
|
+
(0, db_1.runDbSnapshotSealCommand)({ root, json, queuePolicy: parseQueuePolicyFlag(parsed.flags["--queue-policy"]) });
|
|
1121
1262
|
return 0;
|
|
1122
1263
|
case "verify":
|
|
1123
1264
|
if (parsed.positionals.length > 3) {
|
|
@@ -1154,7 +1295,7 @@ function runDbSubcommand(parsed, root) {
|
|
|
1154
1295
|
}
|
|
1155
1296
|
}
|
|
1156
1297
|
default:
|
|
1157
|
-
throw new errors_1.UsageError("mdkg db requires index/init/migrate/verify/stats/snapshot");
|
|
1298
|
+
throw new errors_1.UsageError("mdkg db requires index/init/migrate/verify/stats/queue/snapshot");
|
|
1158
1299
|
}
|
|
1159
1300
|
}
|
|
1160
1301
|
function runCapabilitySubcommand(parsed, root) {
|
package/dist/commands/db.js
CHANGED
|
@@ -8,6 +8,18 @@ exports.runDbInitCommand = runDbInitCommand;
|
|
|
8
8
|
exports.runDbMigrateCommand = runDbMigrateCommand;
|
|
9
9
|
exports.runDbVerifyCommand = runDbVerifyCommand;
|
|
10
10
|
exports.runDbStatsCommand = runDbStatsCommand;
|
|
11
|
+
exports.runDbQueueCreateCommand = runDbQueueCreateCommand;
|
|
12
|
+
exports.runDbQueuePauseCommand = runDbQueuePauseCommand;
|
|
13
|
+
exports.runDbQueueResumeCommand = runDbQueueResumeCommand;
|
|
14
|
+
exports.runDbQueueEnqueueCommand = runDbQueueEnqueueCommand;
|
|
15
|
+
exports.runDbQueueClaimCommand = runDbQueueClaimCommand;
|
|
16
|
+
exports.runDbQueueAckCommand = runDbQueueAckCommand;
|
|
17
|
+
exports.runDbQueueFailCommand = runDbQueueFailCommand;
|
|
18
|
+
exports.runDbQueueDeadLetterCommand = runDbQueueDeadLetterCommand;
|
|
19
|
+
exports.runDbQueueReleaseExpiredCommand = runDbQueueReleaseExpiredCommand;
|
|
20
|
+
exports.runDbQueueStatsCommand = runDbQueueStatsCommand;
|
|
21
|
+
exports.runDbQueueListCommand = runDbQueueListCommand;
|
|
22
|
+
exports.runDbQueueShowCommand = runDbQueueShowCommand;
|
|
11
23
|
exports.runDbSnapshotSealCommand = runDbSnapshotSealCommand;
|
|
12
24
|
exports.runDbSnapshotVerifyCommand = runDbSnapshotVerifyCommand;
|
|
13
25
|
exports.runDbSnapshotStatusCommand = runDbSnapshotStatusCommand;
|
|
@@ -23,6 +35,7 @@ const paths_1 = require("../core/paths");
|
|
|
23
35
|
const project_db_1 = require("../core/project_db");
|
|
24
36
|
const project_db_migrations_1 = require("../core/project_db_migrations");
|
|
25
37
|
const project_db_snapshot_1 = require("../core/project_db_snapshot");
|
|
38
|
+
const project_db_queue_1 = require("../core/project_db_queue");
|
|
26
39
|
const version_1 = require("../core/version");
|
|
27
40
|
const index_1 = require("./index");
|
|
28
41
|
const capabilities_indexer_1 = require("../graph/capabilities_indexer");
|
|
@@ -449,6 +462,177 @@ function runDbStatsCommand(options) {
|
|
|
449
462
|
}
|
|
450
463
|
}
|
|
451
464
|
}
|
|
465
|
+
function requireQueueName(options) {
|
|
466
|
+
if (!options.queueName) {
|
|
467
|
+
throw new errors_1.UsageError("queue name is required");
|
|
468
|
+
}
|
|
469
|
+
return options.queueName;
|
|
470
|
+
}
|
|
471
|
+
function requireMessageId(options) {
|
|
472
|
+
if (!options.messageId) {
|
|
473
|
+
throw new errors_1.UsageError("message id is required");
|
|
474
|
+
}
|
|
475
|
+
return options.messageId;
|
|
476
|
+
}
|
|
477
|
+
function requireLeaseOwner(options) {
|
|
478
|
+
if (!options.leaseOwner) {
|
|
479
|
+
throw new errors_1.UsageError("--lease-owner is required");
|
|
480
|
+
}
|
|
481
|
+
return options.leaseOwner;
|
|
482
|
+
}
|
|
483
|
+
function requireLeaseMs(options) {
|
|
484
|
+
if (options.leaseMs === undefined) {
|
|
485
|
+
throw new errors_1.UsageError("--lease-ms is required");
|
|
486
|
+
}
|
|
487
|
+
return options.leaseMs;
|
|
488
|
+
}
|
|
489
|
+
function loadQueueDatabasePath(root) {
|
|
490
|
+
const config = (0, config_1.loadConfig)(root);
|
|
491
|
+
const verification = (0, project_db_migrations_1.verifyProjectDb)(root, config);
|
|
492
|
+
if (!verification.ok) {
|
|
493
|
+
throw new errors_1.ValidationError(`db queue requires a valid project DB; run mdkg db verify`);
|
|
494
|
+
}
|
|
495
|
+
return (0, project_db_1.resolveConfiguredProjectDbLayout)(root, config.db).runtimeFile;
|
|
496
|
+
}
|
|
497
|
+
function parseQueuePayload(options) {
|
|
498
|
+
const hasPayloadJson = options.payloadJson !== undefined;
|
|
499
|
+
const hasPayloadFile = options.payloadFile !== undefined;
|
|
500
|
+
if (hasPayloadJson === hasPayloadFile) {
|
|
501
|
+
throw new errors_1.UsageError("mdkg db queue enqueue requires exactly one of --payload-json or --payload-file");
|
|
502
|
+
}
|
|
503
|
+
const raw = hasPayloadJson
|
|
504
|
+
? options.payloadJson
|
|
505
|
+
: fs_1.default.readFileSync(path_1.default.resolve(options.root, String(options.payloadFile)), "utf8");
|
|
506
|
+
try {
|
|
507
|
+
return JSON.parse(String(raw));
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
throw new errors_1.UsageError(`queue payload must be valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function writeQueueJsonOrText(action, payload, json) {
|
|
514
|
+
if (json) {
|
|
515
|
+
console.log(JSON.stringify({ action, ok: true, ...payload }, null, 2));
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
console.log(action);
|
|
519
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
520
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
521
|
+
console.log(`${key}: ${value}`);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
console.log(`${key}: ${JSON.stringify(value)}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function runQueueMutation(options, fn, action) {
|
|
529
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
530
|
+
(0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => {
|
|
531
|
+
const databasePath = loadQueueDatabasePath(options.root);
|
|
532
|
+
writeQueueJsonOrText(action, fn(databasePath), options.json);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
function runDbQueueCreateCommand(options) {
|
|
536
|
+
runQueueMutation(options, (databasePath) => (0, project_db_queue_1.createProjectQueue)(databasePath, {
|
|
537
|
+
queue_name: requireQueueName(options),
|
|
538
|
+
paused: options.paused,
|
|
539
|
+
reason: options.reason,
|
|
540
|
+
}), "db-queue-create");
|
|
541
|
+
}
|
|
542
|
+
function runDbQueuePauseCommand(options) {
|
|
543
|
+
runQueueMutation(options, (databasePath) => ({ queue: (0, project_db_queue_1.pauseProjectQueue)(databasePath, { queue_name: requireQueueName(options), reason: options.reason }) }), "db-queue-pause");
|
|
544
|
+
}
|
|
545
|
+
function runDbQueueResumeCommand(options) {
|
|
546
|
+
runQueueMutation(options, (databasePath) => ({ queue: (0, project_db_queue_1.resumeProjectQueue)(databasePath, { queue_name: requireQueueName(options) }) }), "db-queue-resume");
|
|
547
|
+
}
|
|
548
|
+
function runDbQueueEnqueueCommand(options) {
|
|
549
|
+
runQueueMutation(options, (databasePath) => (0, project_db_queue_1.enqueueProjectQueueMessage)(databasePath, {
|
|
550
|
+
queue_name: requireQueueName(options),
|
|
551
|
+
message_id: requireMessageId(options),
|
|
552
|
+
dedupe_key: options.dedupeKey,
|
|
553
|
+
payload: parseQueuePayload(options),
|
|
554
|
+
available_at_ms: options.availableAtMs,
|
|
555
|
+
max_attempts: options.maxAttempts,
|
|
556
|
+
}), "db-queue-enqueue");
|
|
557
|
+
}
|
|
558
|
+
function runDbQueueClaimCommand(options) {
|
|
559
|
+
runQueueMutation(options, (databasePath) => ({
|
|
560
|
+
message: (0, project_db_queue_1.claimProjectQueueMessage)(databasePath, {
|
|
561
|
+
queue_name: requireQueueName(options),
|
|
562
|
+
lease_owner: requireLeaseOwner(options),
|
|
563
|
+
lease_ms: requireLeaseMs(options),
|
|
564
|
+
}),
|
|
565
|
+
}), "db-queue-claim");
|
|
566
|
+
}
|
|
567
|
+
function runDbQueueAckCommand(options) {
|
|
568
|
+
runQueueMutation(options, (databasePath) => ({
|
|
569
|
+
message: (0, project_db_queue_1.ackProjectQueueMessage)(databasePath, {
|
|
570
|
+
queue_name: requireQueueName(options),
|
|
571
|
+
message_id: requireMessageId(options),
|
|
572
|
+
lease_owner: requireLeaseOwner(options),
|
|
573
|
+
}),
|
|
574
|
+
}), "db-queue-ack");
|
|
575
|
+
}
|
|
576
|
+
function runDbQueueFailCommand(options) {
|
|
577
|
+
if (!options.error) {
|
|
578
|
+
throw new errors_1.UsageError("--error is required");
|
|
579
|
+
}
|
|
580
|
+
runQueueMutation(options, (databasePath) => ({
|
|
581
|
+
message: (0, project_db_queue_1.failProjectQueueMessage)(databasePath, {
|
|
582
|
+
queue_name: requireQueueName(options),
|
|
583
|
+
message_id: requireMessageId(options),
|
|
584
|
+
lease_owner: requireLeaseOwner(options),
|
|
585
|
+
error: String(options.error),
|
|
586
|
+
retry_after_ms: options.retryAfterMs,
|
|
587
|
+
}),
|
|
588
|
+
}), "db-queue-fail");
|
|
589
|
+
}
|
|
590
|
+
function runDbQueueDeadLetterCommand(options) {
|
|
591
|
+
if (!options.error) {
|
|
592
|
+
throw new errors_1.UsageError("--error is required");
|
|
593
|
+
}
|
|
594
|
+
runQueueMutation(options, (databasePath) => ({
|
|
595
|
+
message: (0, project_db_queue_1.deadLetterProjectQueueMessage)(databasePath, {
|
|
596
|
+
queue_name: requireQueueName(options),
|
|
597
|
+
message_id: requireMessageId(options),
|
|
598
|
+
lease_owner: requireLeaseOwner(options),
|
|
599
|
+
error: String(options.error),
|
|
600
|
+
}),
|
|
601
|
+
}), "db-queue-dead-letter");
|
|
602
|
+
}
|
|
603
|
+
function runDbQueueReleaseExpiredCommand(options) {
|
|
604
|
+
runQueueMutation(options, (databasePath) => (0, project_db_queue_1.releaseExpiredProjectQueueLeases)(databasePath, { queue_name: options.queueName }), "db-queue-release-expired");
|
|
605
|
+
}
|
|
606
|
+
function runDbQueueStatsCommand(options) {
|
|
607
|
+
const databasePath = loadQueueDatabasePath(options.root);
|
|
608
|
+
const stats = (0, project_db_queue_1.readProjectQueueStats)(databasePath, { queue_name: options.queueName });
|
|
609
|
+
const queue = options.queueName ? (0, project_db_queue_1.readProjectQueue)(databasePath, options.queueName) : null;
|
|
610
|
+
writeQueueJsonOrText("db-queue-stats", {
|
|
611
|
+
queue,
|
|
612
|
+
stats,
|
|
613
|
+
queues: options.queueName ? undefined : (0, project_db_queue_1.listProjectQueues)(databasePath),
|
|
614
|
+
snapshot_summary: (0, project_db_queue_1.readProjectQueueSnapshotSummary)(databasePath),
|
|
615
|
+
}, options.json);
|
|
616
|
+
}
|
|
617
|
+
function runDbQueueListCommand(options) {
|
|
618
|
+
const databasePath = loadQueueDatabasePath(options.root);
|
|
619
|
+
const messages = (0, project_db_queue_1.listProjectQueueMessages)(databasePath, {
|
|
620
|
+
queue_name: requireQueueName(options),
|
|
621
|
+
status: (options.status ?? "all"),
|
|
622
|
+
limit: options.limit,
|
|
623
|
+
});
|
|
624
|
+
writeQueueJsonOrText("db-queue-list", { queue_name: requireQueueName(options), count: messages.length, messages }, options.json);
|
|
625
|
+
}
|
|
626
|
+
function runDbQueueShowCommand(options) {
|
|
627
|
+
const databasePath = loadQueueDatabasePath(options.root);
|
|
628
|
+
const queueName = requireQueueName(options);
|
|
629
|
+
const messageId = requireMessageId(options);
|
|
630
|
+
const message = (0, project_db_queue_1.readProjectQueueMessage)(databasePath, queueName, messageId);
|
|
631
|
+
if (!message) {
|
|
632
|
+
throw new errors_1.NotFoundError(`queue message not found: ${queueName}/${messageId}`);
|
|
633
|
+
}
|
|
634
|
+
writeQueueJsonOrText("db-queue-show", { message }, options.json);
|
|
635
|
+
}
|
|
452
636
|
function printSnapshotChecks(payload) {
|
|
453
637
|
for (const check of payload.checks) {
|
|
454
638
|
const location = check.path ? ` (${path_1.default.isAbsolute(check.path) ? rel(process.cwd(), check.path) : check.path})` : "";
|
|
@@ -457,7 +641,7 @@ function printSnapshotChecks(payload) {
|
|
|
457
641
|
}
|
|
458
642
|
function runDbSnapshotSealCommandLocked(options) {
|
|
459
643
|
const config = (0, config_1.loadConfig)(options.root);
|
|
460
|
-
const payload = (0, project_db_snapshot_1.sealProjectDbSnapshot)(options.root, config);
|
|
644
|
+
const payload = (0, project_db_snapshot_1.sealProjectDbSnapshot)(options.root, config, options.queuePolicy ?? "drain");
|
|
461
645
|
if (options.json) {
|
|
462
646
|
console.log(JSON.stringify(payload, null, 2));
|
|
463
647
|
return;
|
|
@@ -53,6 +53,24 @@ ON project_queue_message(queue_name, status, available_at_ms, created_at_ms, mes
|
|
|
53
53
|
CREATE INDEX IF NOT EXISTS project_queue_message_lease_idx
|
|
54
54
|
ON project_queue_message(queue_name, status, lease_deadline_ms, created_at_ms, message_id);
|
|
55
55
|
`;
|
|
56
|
+
const QUEUE_CONTROL_MIGRATION_SQL = `
|
|
57
|
+
CREATE TABLE IF NOT EXISTS project_queue (
|
|
58
|
+
queue_name TEXT PRIMARY KEY,
|
|
59
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'paused')),
|
|
60
|
+
paused_reason TEXT,
|
|
61
|
+
created_at_ms INTEGER NOT NULL,
|
|
62
|
+
updated_at_ms INTEGER NOT NULL
|
|
63
|
+
) STRICT;
|
|
64
|
+
|
|
65
|
+
INSERT OR IGNORE INTO project_queue (queue_name, status, paused_reason, created_at_ms, updated_at_ms)
|
|
66
|
+
SELECT DISTINCT
|
|
67
|
+
queue_name,
|
|
68
|
+
'active',
|
|
69
|
+
NULL,
|
|
70
|
+
CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER),
|
|
71
|
+
CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)
|
|
72
|
+
FROM project_queue_message;
|
|
73
|
+
`;
|
|
56
74
|
const EVENTS_RECEIPTS_MIGRATION_SQL = `
|
|
57
75
|
CREATE TABLE IF NOT EXISTS project_event (
|
|
58
76
|
event_id TEXT PRIMARY KEY,
|
|
@@ -162,6 +180,12 @@ const BUILTIN_MIGRATIONS = [
|
|
|
162
180
|
filename: "004_mdkg_project_db_writer_leases.sql",
|
|
163
181
|
sql: WRITER_LEASES_MIGRATION_SQL.trim(),
|
|
164
182
|
},
|
|
183
|
+
{
|
|
184
|
+
ordinal: 5,
|
|
185
|
+
key: "mdkg.project_db.queue_control.v1",
|
|
186
|
+
filename: "005_mdkg_project_db_queue_control.sql",
|
|
187
|
+
sql: QUEUE_CONTROL_MIGRATION_SQL.trim(),
|
|
188
|
+
},
|
|
165
189
|
];
|
|
166
190
|
function loadDatabaseCtor() {
|
|
167
191
|
try {
|