mdkg 0.4.2 → 0.5.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 +71 -15
- package/CLI_COMMAND_MATRIX.md +59 -2
- package/README.md +20 -2
- package/dist/cli.js +124 -2
- package/dist/command-contract.json +756 -2
- package/dist/commands/archive.js +31 -14
- package/dist/commands/bundle.js +180 -44
- package/dist/commands/capability.js +1 -0
- package/dist/commands/checkpoint.js +6 -5
- package/dist/commands/event_support.js +16 -7
- package/dist/commands/fix.js +11 -8
- package/dist/commands/git.js +41 -10
- package/dist/commands/goal.js +23 -23
- package/dist/commands/graph.js +64 -10
- package/dist/commands/handoff.js +4 -2
- package/dist/commands/init.js +28 -23
- package/dist/commands/loop.js +1668 -0
- package/dist/commands/loop_descriptors.js +219 -0
- package/dist/commands/mcp.js +101 -11
- package/dist/commands/new.js +49 -3
- package/dist/commands/pack.js +14 -7
- package/dist/commands/search.js +10 -1
- package/dist/commands/skill.js +12 -9
- package/dist/commands/skill_mirror.js +39 -31
- package/dist/commands/subgraph.js +59 -26
- package/dist/commands/task.js +77 -4
- package/dist/commands/upgrade.js +13 -15
- package/dist/commands/validate.js +20 -7
- package/dist/commands/work.js +44 -26
- package/dist/commands/workspace.js +10 -4
- package/dist/core/config.js +45 -17
- package/dist/core/filesystem_authority.js +281 -0
- package/dist/core/project_db_events.js +4 -0
- package/dist/core/project_db_materializer.js +2 -0
- package/dist/core/project_db_migrations.js +238 -181
- package/dist/core/project_db_snapshot.js +64 -14
- package/dist/core/workspace_path.js +7 -0
- package/dist/graph/archive_integrity.js +1 -1
- package/dist/graph/capabilities_index_cache.js +4 -1
- package/dist/graph/frontmatter.js +38 -0
- package/dist/graph/goal_scope.js +4 -1
- package/dist/graph/index_cache.js +37 -7
- package/dist/graph/loop_bindings.js +39 -0
- package/dist/graph/node.js +209 -1
- package/dist/graph/node_body.js +52 -6
- package/dist/graph/skills_index_cache.js +41 -6
- package/dist/graph/skills_indexer.js +5 -2
- package/dist/graph/sqlite_index.js +118 -105
- package/dist/graph/subgraphs.js +142 -5
- package/dist/graph/validate_graph.js +109 -13
- package/dist/graph/workspace_files.js +39 -9
- package/dist/init/AGENT_START.md +16 -0
- package/dist/init/CLI_COMMAND_MATRIX.md +14 -0
- package/dist/init/config.json +7 -1
- package/dist/init/init-manifest.json +49 -4
- package/dist/init/skills/default/pursue-mdkg-loop/SKILL.md +150 -0
- package/dist/init/templates/default/loop.md +160 -0
- package/dist/init/templates/loops/backend-api-cli-bloat-audit.loop.md +117 -0
- package/dist/init/templates/loops/design-frontend-ux-audit.loop.md +117 -0
- package/dist/init/templates/loops/duplicate-code-and-linting-audit.loop.md +114 -0
- package/dist/init/templates/loops/security-audit.loop.md +140 -0
- package/dist/init/templates/loops/tech-stack-best-practices-audit.loop.md +116 -0
- package/dist/init/templates/loops/test-ci-skill-infrastructure-audit.loop.md +116 -0
- package/dist/init/templates/loops/user-story-audit-and-recommendations.loop.md +116 -0
- package/dist/pack/order.js +3 -1
- package/dist/pack/pack.js +139 -6
- package/dist/templates/loader.js +9 -3
- package/dist/util/lock.js +10 -9
- package/dist/util/zip.js +163 -9
- package/package.json +8 -4
|
@@ -9,9 +9,9 @@ exports.projectDbStats = projectDbStats;
|
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const filesystem_authority_1 = require("./filesystem_authority");
|
|
12
13
|
const project_db_1 = require("./project_db");
|
|
13
14
|
const version_1 = require("./version");
|
|
14
|
-
const atomic_1 = require("../util/atomic");
|
|
15
15
|
const errors_1 = require("../util/errors");
|
|
16
16
|
const FOUNDATION_MIGRATION_SQL = `
|
|
17
17
|
CREATE TABLE IF NOT EXISTS project_meta (
|
|
@@ -219,46 +219,71 @@ function checksumContent(content) {
|
|
|
219
219
|
}
|
|
220
220
|
function assertDirectory(root, dirPath, label) {
|
|
221
221
|
const relative = rel(root, dirPath);
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
try {
|
|
223
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: relative, operation: "read" }, ({ absolutePath }) => {
|
|
224
|
+
if (!fs_1.default.existsSync(absolutePath)) {
|
|
225
|
+
throw new errors_1.ValidationError(`${label} missing at ${relative}; run mdkg db init`);
|
|
226
|
+
}
|
|
227
|
+
if (!fs_1.default.lstatSync(absolutePath).isDirectory()) {
|
|
228
|
+
throw new errors_1.ValidationError(`${relative} exists and is not a directory`);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
224
231
|
}
|
|
225
|
-
|
|
226
|
-
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (error instanceof errors_1.ValidationError) {
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
throw new errors_1.ValidationError(`${relative} is not a safe contained directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
227
237
|
}
|
|
228
238
|
}
|
|
229
239
|
function directoryCheck(root, dirPath, name) {
|
|
230
240
|
const relative = rel(root, dirPath);
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
try {
|
|
242
|
+
return (0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: relative, operation: "read" }, ({ absolutePath }) => {
|
|
243
|
+
if (!fs_1.default.existsSync(absolutePath)) {
|
|
244
|
+
return {
|
|
245
|
+
name,
|
|
246
|
+
ok: false,
|
|
247
|
+
level: "fail",
|
|
248
|
+
path: relative,
|
|
249
|
+
detail: `${name} missing`,
|
|
250
|
+
errors: [`${relative} missing; run mdkg db init`],
|
|
251
|
+
warnings: [],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (!fs_1.default.lstatSync(absolutePath).isDirectory()) {
|
|
255
|
+
return {
|
|
256
|
+
name,
|
|
257
|
+
ok: false,
|
|
258
|
+
level: "fail",
|
|
259
|
+
path: relative,
|
|
260
|
+
detail: `${name} is not a directory`,
|
|
261
|
+
errors: [`${relative} exists and is not a directory`],
|
|
262
|
+
warnings: [],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
name,
|
|
267
|
+
ok: true,
|
|
268
|
+
level: "ok",
|
|
269
|
+
path: relative,
|
|
270
|
+
detail: `${name} exists`,
|
|
271
|
+
errors: [],
|
|
272
|
+
warnings: [],
|
|
273
|
+
};
|
|
274
|
+
});
|
|
241
275
|
}
|
|
242
|
-
|
|
276
|
+
catch (error) {
|
|
243
277
|
return {
|
|
244
278
|
name,
|
|
245
279
|
ok: false,
|
|
246
280
|
level: "fail",
|
|
247
281
|
path: relative,
|
|
248
|
-
detail: `${name} is not
|
|
249
|
-
errors: [`${relative}
|
|
282
|
+
detail: `${name} is not safely contained`,
|
|
283
|
+
errors: [`${relative} is not safely contained: ${error instanceof Error ? error.message : String(error)}`],
|
|
250
284
|
warnings: [],
|
|
251
285
|
};
|
|
252
286
|
}
|
|
253
|
-
return {
|
|
254
|
-
name,
|
|
255
|
-
ok: true,
|
|
256
|
-
level: "ok",
|
|
257
|
-
path: relative,
|
|
258
|
-
detail: `${name} exists`,
|
|
259
|
-
errors: [],
|
|
260
|
-
warnings: [],
|
|
261
|
-
};
|
|
262
287
|
}
|
|
263
288
|
function assertProjectDbReady(root, config) {
|
|
264
289
|
if (!config.db.enabled) {
|
|
@@ -274,31 +299,42 @@ function assertProjectDbReady(root, config) {
|
|
|
274
299
|
assertDirectory(root, layout.runtimeDir, "project db runtime directory");
|
|
275
300
|
assertDirectory(root, layout.stateDir, "project db state directory");
|
|
276
301
|
assertDirectory(root, layout.receipts, "project db receipts directory");
|
|
277
|
-
|
|
278
|
-
|
|
302
|
+
try {
|
|
303
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: config.db.runtime_path, operation: "replace" }, ({ absolutePath }) => {
|
|
304
|
+
if (fs_1.default.existsSync(absolutePath) && fs_1.default.lstatSync(absolutePath).isDirectory()) {
|
|
305
|
+
throw new errors_1.ValidationError(`${rel(root, absolutePath)} exists and is not a file`);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (error instanceof errors_1.ValidationError) {
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
throw new errors_1.ValidationError(`project db runtime path is not safely contained: ${error instanceof Error ? error.message : String(error)}`);
|
|
279
314
|
}
|
|
280
315
|
}
|
|
281
316
|
function ensureMigrationFiles(root, config) {
|
|
282
317
|
const layout = (0, project_db_1.resolveConfiguredProjectDbLayout)(root, config.db);
|
|
283
318
|
const created = [];
|
|
284
319
|
const unchanged = [];
|
|
320
|
+
const missing = [];
|
|
285
321
|
for (const migration of BUILTIN_MIGRATIONS) {
|
|
286
322
|
const filePath = path_1.default.join(layout.migrations, migration.filename);
|
|
287
323
|
const expectedContent = `${migration.sql.trim()}\n`;
|
|
288
324
|
const relative = rel(root, filePath);
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
throw new errors_1.ValidationError(`${relative} exists and is not a file`);
|
|
292
|
-
}
|
|
293
|
-
const current = fs_1.default.readFileSync(filePath, "utf8");
|
|
325
|
+
if ((0, filesystem_authority_1.containedPathExists)({ root, relativePath: relative })) {
|
|
326
|
+
const current = (0, filesystem_authority_1.readContainedFile)({ root, relativePath: relative });
|
|
294
327
|
if (checksumContent(current) !== checksumContent(expectedContent)) {
|
|
295
328
|
throw new errors_1.ValidationError(`migration file checksum drift at ${relative}`);
|
|
296
329
|
}
|
|
297
330
|
unchanged.push(relative);
|
|
298
331
|
continue;
|
|
299
332
|
}
|
|
300
|
-
(
|
|
301
|
-
|
|
333
|
+
missing.push({ relative, expectedContent });
|
|
334
|
+
}
|
|
335
|
+
for (const item of missing) {
|
|
336
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root, relativePath: item.relative }, item.expectedContent);
|
|
337
|
+
created.push(item.relative);
|
|
302
338
|
}
|
|
303
339
|
return { created, unchanged };
|
|
304
340
|
}
|
|
@@ -309,17 +345,18 @@ function checkMigrationFiles(root, config) {
|
|
|
309
345
|
const filePath = path_1.default.join(layout.migrations, migration.filename);
|
|
310
346
|
const relative = rel(root, filePath);
|
|
311
347
|
const expectedContent = `${migration.sql.trim()}\n`;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
348
|
+
try {
|
|
349
|
+
if (!(0, filesystem_authority_1.containedPathExists)({ root, relativePath: relative })) {
|
|
350
|
+
errors.push(`${relative} missing; run mdkg db migrate`);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const current = (0, filesystem_authority_1.readContainedFile)({ root, relativePath: relative });
|
|
354
|
+
if (checksumContent(current) !== checksumContent(expectedContent)) {
|
|
355
|
+
errors.push(`migration file checksum drift at ${relative}`);
|
|
356
|
+
}
|
|
319
357
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
errors.push(`migration file checksum drift at ${relative}`);
|
|
358
|
+
catch (error) {
|
|
359
|
+
errors.push(`${relative} is not safely contained: ${error instanceof Error ? error.message : String(error)}`);
|
|
323
360
|
}
|
|
324
361
|
}
|
|
325
362
|
return {
|
|
@@ -509,83 +546,84 @@ function runProjectDbMigrations(root, config) {
|
|
|
509
546
|
assertProjectDbReady(root, config);
|
|
510
547
|
const migrationFiles = ensureMigrationFiles(root, config);
|
|
511
548
|
const layout = (0, project_db_1.resolveConfiguredProjectDbLayout)(root, config.db);
|
|
512
|
-
fs_1.default.mkdirSync(path_1.default.dirname(layout.runtimeFile), { recursive: true });
|
|
513
549
|
const DatabaseSync = loadDatabaseCtor();
|
|
514
|
-
const db = new DatabaseSync(layout.runtimeFile);
|
|
515
550
|
const statuses = [];
|
|
516
551
|
let appliedCount = 0;
|
|
517
|
-
|
|
518
|
-
db
|
|
519
|
-
db.exec("PRAGMA synchronous = FULL;");
|
|
520
|
-
assertIntegrity(db);
|
|
521
|
-
db.exec(createMigrationTableSql(config.db.migration_table));
|
|
522
|
-
db.exec("BEGIN IMMEDIATE");
|
|
552
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: config.db.runtime_path, operation: "replace", createParents: true }, ({ absolutePath }) => {
|
|
553
|
+
const db = new DatabaseSync(absolutePath);
|
|
523
554
|
try {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (row) {
|
|
536
|
-
if (row.ordinal !== migration.ordinal) {
|
|
537
|
-
throw new errors_1.ValidationError(`migration order drift for ${migration.key}`);
|
|
555
|
+
db.exec("PRAGMA foreign_keys = ON;");
|
|
556
|
+
db.exec("PRAGMA synchronous = FULL;");
|
|
557
|
+
assertIntegrity(db);
|
|
558
|
+
db.exec(createMigrationTableSql(config.db.migration_table));
|
|
559
|
+
db.exec("BEGIN IMMEDIATE");
|
|
560
|
+
try {
|
|
561
|
+
const applied = readAppliedMigrations(db, config.db.migration_table);
|
|
562
|
+
const knownKeys = new Set(BUILTIN_MIGRATIONS.map((migration) => migration.key));
|
|
563
|
+
for (const appliedKey of applied.keys()) {
|
|
564
|
+
if (!knownKeys.has(appliedKey)) {
|
|
565
|
+
throw new errors_1.ValidationError(`project DB contains unsupported migration ${appliedKey}`);
|
|
538
566
|
}
|
|
539
|
-
|
|
540
|
-
|
|
567
|
+
}
|
|
568
|
+
const now = Date.now();
|
|
569
|
+
for (const migration of BUILTIN_MIGRATIONS) {
|
|
570
|
+
const checksum = checksumMigration(migration);
|
|
571
|
+
const row = applied.get(migration.key);
|
|
572
|
+
if (row) {
|
|
573
|
+
if (row.ordinal !== migration.ordinal) {
|
|
574
|
+
throw new errors_1.ValidationError(`migration order drift for ${migration.key}`);
|
|
575
|
+
}
|
|
576
|
+
if (row.checksum !== checksum) {
|
|
577
|
+
throw new errors_1.ValidationError(`migration checksum drift for ${migration.key}`);
|
|
578
|
+
}
|
|
579
|
+
statuses.push({
|
|
580
|
+
key: migration.key,
|
|
581
|
+
ordinal: migration.ordinal,
|
|
582
|
+
checksum,
|
|
583
|
+
status: "already_applied",
|
|
584
|
+
applied_at_ms: row.applied_at_ms,
|
|
585
|
+
});
|
|
586
|
+
continue;
|
|
541
587
|
}
|
|
588
|
+
db.exec(migration.sql);
|
|
589
|
+
const appliedAt = now + appliedCount;
|
|
590
|
+
db
|
|
591
|
+
.prepare(`INSERT INTO ${config.db.migration_table} (migration_key, ordinal, checksum, applied_at_ms, mdkg_version) VALUES (?, ?, ?, ?, ?)`)
|
|
592
|
+
.run(migration.key, migration.ordinal, checksum, appliedAt, (0, version_1.readPackageVersion)());
|
|
542
593
|
statuses.push({
|
|
543
594
|
key: migration.key,
|
|
544
595
|
ordinal: migration.ordinal,
|
|
545
596
|
checksum,
|
|
546
|
-
status: "
|
|
547
|
-
applied_at_ms:
|
|
597
|
+
status: "applied",
|
|
598
|
+
applied_at_ms: appliedAt,
|
|
548
599
|
});
|
|
549
|
-
|
|
600
|
+
appliedCount += 1;
|
|
550
601
|
}
|
|
551
|
-
db
|
|
552
|
-
|
|
553
|
-
db
|
|
554
|
-
.prepare(`INSERT INTO ${config.db.migration_table} (migration_key, ordinal, checksum, applied_at_ms, mdkg_version) VALUES (?, ?, ?, ?, ?)`)
|
|
555
|
-
.run(migration.key, migration.ordinal, checksum, appliedAt, (0, version_1.readPackageVersion)());
|
|
556
|
-
statuses.push({
|
|
557
|
-
key: migration.key,
|
|
558
|
-
ordinal: migration.ordinal,
|
|
559
|
-
checksum,
|
|
560
|
-
status: "applied",
|
|
561
|
-
applied_at_ms: appliedAt,
|
|
562
|
-
});
|
|
563
|
-
appliedCount += 1;
|
|
602
|
+
syncProjectMeta(db, config);
|
|
603
|
+
db.exec("COMMIT");
|
|
564
604
|
}
|
|
565
|
-
|
|
566
|
-
|
|
605
|
+
catch (err) {
|
|
606
|
+
try {
|
|
607
|
+
db.exec("ROLLBACK");
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
// ignore rollback failures when no transaction is active
|
|
611
|
+
}
|
|
612
|
+
throw err;
|
|
613
|
+
}
|
|
614
|
+
assertIntegrity(db);
|
|
567
615
|
}
|
|
568
616
|
catch (err) {
|
|
569
|
-
|
|
570
|
-
|
|
617
|
+
if (err instanceof errors_1.ValidationError) {
|
|
618
|
+
throw err;
|
|
571
619
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
throw err;
|
|
620
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
621
|
+
throw new errors_1.ValidationError(`project DB migration failed: ${message}`);
|
|
576
622
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
catch (err) {
|
|
580
|
-
if (err instanceof errors_1.ValidationError) {
|
|
581
|
-
throw err;
|
|
623
|
+
finally {
|
|
624
|
+
db.close();
|
|
582
625
|
}
|
|
583
|
-
|
|
584
|
-
throw new errors_1.ValidationError(`project DB migration failed: ${message}`);
|
|
585
|
-
}
|
|
586
|
-
finally {
|
|
587
|
-
db.close();
|
|
588
|
-
}
|
|
626
|
+
});
|
|
589
627
|
return {
|
|
590
628
|
action: "db-migrate",
|
|
591
629
|
ok: true,
|
|
@@ -628,36 +666,51 @@ function verifyProjectDb(root, config) {
|
|
|
628
666
|
checks.push(directoryCheck(root, layout.stateDir, "project db state directory"));
|
|
629
667
|
checks.push(directoryCheck(root, layout.receipts, "project db receipts directory"));
|
|
630
668
|
const databasePath = rel(root, layout.runtimeFile);
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
669
|
+
try {
|
|
670
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: config.db.runtime_path, operation: "read" }, ({ absolutePath }) => {
|
|
671
|
+
if (!fs_1.default.existsSync(absolutePath)) {
|
|
672
|
+
checks.push({
|
|
673
|
+
name: "runtime-database",
|
|
674
|
+
ok: false,
|
|
675
|
+
level: "fail",
|
|
676
|
+
path: databasePath,
|
|
677
|
+
detail: "runtime database missing",
|
|
678
|
+
errors: [`${databasePath} missing; run mdkg db migrate`],
|
|
679
|
+
warnings: [],
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
else if (fs_1.default.lstatSync(absolutePath).isDirectory()) {
|
|
683
|
+
checks.push({
|
|
684
|
+
name: "runtime-database",
|
|
685
|
+
ok: false,
|
|
686
|
+
level: "fail",
|
|
687
|
+
path: databasePath,
|
|
688
|
+
detail: "runtime database path is not a file",
|
|
689
|
+
errors: [`${databasePath} exists and is not a file`],
|
|
690
|
+
warnings: [],
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
checks.push({
|
|
695
|
+
name: "runtime-database",
|
|
696
|
+
ok: true,
|
|
697
|
+
level: "ok",
|
|
698
|
+
path: databasePath,
|
|
699
|
+
detail: "runtime database exists",
|
|
700
|
+
errors: [],
|
|
701
|
+
warnings: [],
|
|
702
|
+
});
|
|
703
|
+
}
|
|
640
704
|
});
|
|
641
705
|
}
|
|
642
|
-
|
|
706
|
+
catch (error) {
|
|
643
707
|
checks.push({
|
|
644
708
|
name: "runtime-database",
|
|
645
709
|
ok: false,
|
|
646
710
|
level: "fail",
|
|
647
711
|
path: databasePath,
|
|
648
|
-
detail: "runtime database
|
|
649
|
-
errors: [`${databasePath}
|
|
650
|
-
warnings: [],
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
else {
|
|
654
|
-
checks.push({
|
|
655
|
-
name: "runtime-database",
|
|
656
|
-
ok: true,
|
|
657
|
-
level: "ok",
|
|
658
|
-
path: databasePath,
|
|
659
|
-
detail: "runtime database exists",
|
|
660
|
-
errors: [],
|
|
712
|
+
detail: "runtime database is not safely contained",
|
|
713
|
+
errors: [`${databasePath} is not safely contained: ${error instanceof Error ? error.message : String(error)}`],
|
|
661
714
|
warnings: [],
|
|
662
715
|
});
|
|
663
716
|
}
|
|
@@ -667,14 +720,16 @@ function verifyProjectDb(root, config) {
|
|
|
667
720
|
checks.push(checkMigrationFiles(root, config));
|
|
668
721
|
const DatabaseSync = loadDatabaseCtor();
|
|
669
722
|
try {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
723
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: config.db.runtime_path, operation: "read" }, ({ absolutePath }) => {
|
|
724
|
+
const db = new DatabaseSync(absolutePath);
|
|
725
|
+
try {
|
|
726
|
+
checks.push(integrityCheck(db));
|
|
727
|
+
checks.push(migrationTableCheck(db, config));
|
|
728
|
+
}
|
|
729
|
+
finally {
|
|
730
|
+
db.close();
|
|
731
|
+
}
|
|
732
|
+
});
|
|
678
733
|
}
|
|
679
734
|
catch (err) {
|
|
680
735
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -712,49 +767,51 @@ function projectDbStats(root, config) {
|
|
|
712
767
|
}
|
|
713
768
|
const layout = (0, project_db_1.resolveConfiguredProjectDbLayout)(root, config.db);
|
|
714
769
|
const DatabaseSync = loadDatabaseCtor();
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
770
|
+
return (0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: config.db.runtime_path, operation: "read" }, ({ absolutePath }) => {
|
|
771
|
+
const db = new DatabaseSync(absolutePath);
|
|
772
|
+
try {
|
|
773
|
+
const applied = readAppliedMigrations(db, config.db.migration_table);
|
|
774
|
+
const migrationStatuses = BUILTIN_MIGRATIONS
|
|
775
|
+
.map((migration) => {
|
|
776
|
+
const row = applied.get(migration.key);
|
|
777
|
+
return row
|
|
778
|
+
? {
|
|
779
|
+
key: migration.key,
|
|
780
|
+
ordinal: migration.ordinal,
|
|
781
|
+
checksum: row.checksum,
|
|
782
|
+
status: "already_applied",
|
|
783
|
+
applied_at_ms: row.applied_at_ms,
|
|
784
|
+
}
|
|
785
|
+
: undefined;
|
|
786
|
+
})
|
|
787
|
+
.filter((item) => item !== undefined);
|
|
788
|
+
const latestMigration = migrationStatuses[migrationStatuses.length - 1] ?? null;
|
|
789
|
+
const receiptFiles = walkFiles(layout.receipts);
|
|
790
|
+
return {
|
|
791
|
+
action: "db-stats",
|
|
792
|
+
ok: true,
|
|
793
|
+
enabled: config.db.enabled,
|
|
794
|
+
database: rel(root, absolutePath),
|
|
795
|
+
schema_version: config.db.schema_version,
|
|
796
|
+
migration_table: config.db.migration_table,
|
|
797
|
+
db_size: fs_1.default.statSync(absolutePath).size,
|
|
798
|
+
transient_files: transientFiles(root, absolutePath),
|
|
799
|
+
migration_count: migrationStatuses.length,
|
|
800
|
+
latest_migration: latestMigration,
|
|
801
|
+
tables: tableCounts(db),
|
|
802
|
+
state_snapshot: {
|
|
803
|
+
path: rel(root, layout.stateFile),
|
|
804
|
+
exists: fs_1.default.existsSync(layout.stateFile),
|
|
805
|
+
size: fs_1.default.existsSync(layout.stateFile) ? fs_1.default.statSync(layout.stateFile).size : 0,
|
|
806
|
+
},
|
|
807
|
+
receipt_files: {
|
|
808
|
+
path: rel(root, layout.receipts),
|
|
809
|
+
count: receiptFiles.length,
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
finally {
|
|
814
|
+
db.close();
|
|
815
|
+
}
|
|
816
|
+
});
|
|
760
817
|
}
|