mdkg 0.1.2 → 0.1.3

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.
@@ -11,12 +11,15 @@ const path_1 = __importDefault(require("path"));
11
11
  const config_1 = require("../core/config");
12
12
  const indexer_1 = require("../graph/indexer");
13
13
  const index_cache_1 = require("../graph/index_cache");
14
+ const reindex_1 = require("../graph/reindex");
14
15
  const frontmatter_1 = require("../graph/frontmatter");
15
16
  const skills_indexer_1 = require("../graph/skills_indexer");
16
17
  const date_1 = require("../util/date");
17
18
  const errors_1 = require("../util/errors");
18
19
  const qid_1 = require("../util/qid");
19
20
  const id_1 = require("../util/id");
21
+ const atomic_1 = require("../util/atomic");
22
+ const lock_1 = require("../util/lock");
20
23
  const event_support_1 = require("./event_support");
21
24
  const checkpoint_1 = require("./checkpoint");
22
25
  const MUTABLE_TASK_TYPES = new Set(["task", "bug", "test"]);
@@ -93,8 +96,7 @@ function maybeReindex(root, config) {
93
96
  if (!config.index.auto_reindex) {
94
97
  return;
95
98
  }
96
- const outputPath = path_1.default.resolve(root, config.index.global_index_path);
97
- (0, index_cache_1.writeIndex)(outputPath, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
99
+ (0, reindex_1.writeDerivedIndexes)(root, config, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
98
100
  }
99
101
  function loadMutableTaskNode(root, idOrQid, wsHint) {
100
102
  const config = (0, config_1.loadConfig)(root);
@@ -132,7 +134,7 @@ function writeNodeFile(root, filePath, frontmatter, body) {
132
134
  const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
133
135
  const frontmatterBlock = ["---", ...lines, "---"].join("\n");
134
136
  const content = body.length > 0 ? `${frontmatterBlock}\n${body}` : frontmatterBlock;
135
- fs_1.default.writeFileSync(filePath, content, "utf8");
137
+ (0, atomic_1.atomicWriteFile)(filePath, content);
136
138
  }
137
139
  function ensureStatusAllowed(config, statusRaw, flag = "--status") {
138
140
  const normalized = statusRaw.trim().toLowerCase();
@@ -202,7 +204,7 @@ function printTaskMutationReceipt(action, root, loaded, json, checkpoint) {
202
204
  }
203
205
  console.log(`task ${action}: ${loaded.qid}`);
204
206
  }
205
- function runTaskStartCommand(options) {
207
+ function runTaskStartCommandLocked(options) {
206
208
  const loaded = loadMutableTaskNode(options.root, options.id, options.ws);
207
209
  const now = options.now ?? new Date();
208
210
  loaded.frontmatter.status = ensureStatusAllowed(loaded.config, "progress");
@@ -222,7 +224,7 @@ function runTaskStartCommand(options) {
222
224
  maybeWarnEventsDisabled(options.root, loaded.config, loaded.ws);
223
225
  printTaskMutationReceipt("started", options.root, loaded, options.json);
224
226
  }
225
- function runTaskUpdateCommand(options) {
227
+ function runTaskUpdateCommandLocked(options) {
226
228
  const loaded = loadMutableTaskNode(options.root, options.id, options.ws);
227
229
  const now = options.now ?? new Date();
228
230
  if (options.status) {
@@ -264,7 +266,7 @@ function runTaskUpdateCommand(options) {
264
266
  });
265
267
  printTaskMutationReceipt("updated", options.root, loaded, options.json);
266
268
  }
267
- function runTaskDoneCommand(options) {
269
+ function runTaskDoneCommandLocked(options) {
268
270
  const loaded = loadMutableTaskNode(options.root, options.id, options.ws);
269
271
  const now = options.now ?? new Date();
270
272
  const nextLinks = appendUnique(toStringList(loaded.frontmatter.links), parseCsvList(options.addLinks));
@@ -316,3 +318,15 @@ function runTaskDoneCommand(options) {
316
318
  maybeWarnEventsDisabled(options.root, loaded.config, loaded.ws);
317
319
  printTaskMutationReceipt("done", options.root, loaded, options.json, checkpoint);
318
320
  }
321
+ function runTaskStartCommand(options) {
322
+ const config = (0, config_1.loadConfig)(options.root);
323
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runTaskStartCommandLocked(options));
324
+ }
325
+ function runTaskUpdateCommand(options) {
326
+ const config = (0, config_1.loadConfig)(options.root);
327
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runTaskUpdateCommandLocked(options));
328
+ }
329
+ function runTaskDoneCommand(options) {
330
+ const config = (0, config_1.loadConfig)(options.root);
331
+ return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runTaskDoneCommandLocked(options));
332
+ }
@@ -14,6 +14,7 @@ const workspace_files_1 = require("../graph/workspace_files");
14
14
  const validate_graph_1 = require("../graph/validate_graph");
15
15
  const bundle_imports_1 = require("../graph/bundle_imports");
16
16
  const visibility_1 = require("../graph/visibility");
17
+ const sqlite_index_1 = require("../graph/sqlite_index");
17
18
  const errors_1 = require("../util/errors");
18
19
  const skill_mirror_1 = require("./skill_mirror");
19
20
  const RECOMMENDED_HEADINGS = {
@@ -207,6 +208,11 @@ function runValidateCommand(options) {
207
208
  const filesByAlias = (0, workspace_files_1.listWorkspaceDocFilesByAlias)(options.root, config);
208
209
  const errors = [];
209
210
  const warnings = [];
211
+ if ((0, sqlite_index_1.isSqliteBackend)(config)) {
212
+ const health = (0, sqlite_index_1.sqliteHealth)(options.root, config);
213
+ warnings.push(...health.warnings);
214
+ errors.push(...health.errors);
215
+ }
210
216
  if (templateSchemaInfo.fallbackTypes.length > 0) {
211
217
  warnings.push(`using bundled template schema fallback for missing local type(s): ${templateSchemaInfo.fallbackTypes.join(", ")}; run \`mdkg upgrade --apply\` to vendor built-in templates`);
212
218
  }
@@ -15,12 +15,15 @@ const config_1 = require("../core/config");
15
15
  const frontmatter_1 = require("../graph/frontmatter");
16
16
  const indexer_1 = require("../graph/indexer");
17
17
  const index_cache_1 = require("../graph/index_cache");
18
+ const reindex_1 = require("../graph/reindex");
18
19
  const agent_file_types_1 = require("../graph/agent_file_types");
19
20
  const loader_1 = require("../templates/loader");
20
21
  const date_1 = require("../util/date");
21
22
  const errors_1 = require("../util/errors");
22
23
  const id_1 = require("../util/id");
23
24
  const qid_1 = require("../util/qid");
25
+ const atomic_1 = require("../util/atomic");
26
+ const lock_1 = require("../util/lock");
24
27
  const event_support_1 = require("./event_support");
25
28
  const archive_1 = require("./archive");
26
29
  const PRICING_MODELS = new Set(["free", "included", "quoted", "fixed", "metered", "subscription"]);
@@ -90,8 +93,7 @@ function maybeReindex(root, config) {
90
93
  if (!config.index.auto_reindex) {
91
94
  return;
92
95
  }
93
- const outputPath = path_1.default.resolve(root, config.index.global_index_path);
94
- (0, index_cache_1.writeIndex)(outputPath, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
96
+ (0, reindex_1.writeDerivedIndexes)(root, config, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
95
97
  }
96
98
  function findNodeById(index, ws, id, type) {
97
99
  const node = index.nodes[`${ws}:${id}`];
@@ -123,7 +125,7 @@ function nodeReceipt(root, node) {
123
125
  function writeFrontmatterFile(filePath, frontmatter, body) {
124
126
  const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
125
127
  const content = ["---", ...lines, "---", body.trimStart()].join("\n");
126
- fs_1.default.writeFileSync(filePath, content.endsWith("\n") ? content : `${content}\n`, "utf8");
128
+ (0, atomic_1.atomicWriteFile)(filePath, content.endsWith("\n") ? content : `${content}\n`);
127
129
  }
128
130
  function createAgentWorkflowNode(options) {
129
131
  const config = (0, config_1.loadConfig)(options.root);
@@ -149,8 +151,16 @@ function createAgentWorkflowNode(options) {
149
151
  updated: today,
150
152
  ...options.overrides,
151
153
  });
152
- fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
153
- fs_1.default.writeFileSync(filePath, content, "utf8");
154
+ try {
155
+ (0, atomic_1.writeFileExclusive)(filePath, content);
156
+ }
157
+ catch (err) {
158
+ const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : "";
159
+ if (code === "EEXIST") {
160
+ throw new errors_1.UsageError(`node already exists: ${path_1.default.relative(options.root, filePath)}`);
161
+ }
162
+ throw err;
163
+ }
154
164
  maybeReindex(options.root, config);
155
165
  (0, event_support_1.appendAutomaticEvent)({
156
166
  root: options.root,
@@ -200,7 +210,7 @@ function printReceipt(action, receipt, json) {
200
210
  }
201
211
  console.log(`work ${action}: ${receipt.qid} (${receipt.path})`);
202
212
  }
203
- function runWorkContractNewCommand(options) {
213
+ function runWorkContractNewCommandLocked(options) {
204
214
  const config = (0, config_1.loadConfig)(options.root);
205
215
  const ws = normalizeWorkspace(options.ws);
206
216
  const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
@@ -226,7 +236,7 @@ function runWorkContractNewCommand(options) {
226
236
  });
227
237
  printReceipt("contract created", receipt, options.json);
228
238
  }
229
- function runWorkOrderNewCommand(options) {
239
+ function runWorkOrderNewCommandLocked(options) {
230
240
  const config = (0, config_1.loadConfig)(options.root);
231
241
  const ws = normalizeWorkspace(options.ws);
232
242
  const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
@@ -255,7 +265,7 @@ function runWorkOrderNewCommand(options) {
255
265
  });
256
266
  printReceipt("order created", receipt, options.json);
257
267
  }
258
- function runWorkOrderUpdateCommand(options) {
268
+ function runWorkOrderUpdateCommandLocked(options) {
259
269
  const loaded = loadMutableAgentNode(options.root, options.id, options.ws, "work_order");
260
270
  if (options.status) {
261
271
  loaded.frontmatter.order_status = normalizeEnum(options.status, "--status", ORDER_STATUSES);
@@ -267,7 +277,7 @@ function runWorkOrderUpdateCommand(options) {
267
277
  maybeReindex(options.root, loaded.config);
268
278
  printReceipt("order updated", nodeReceipt(options.root, loaded.node), options.json);
269
279
  }
270
- function runWorkReceiptNewCommand(options) {
280
+ function runWorkReceiptNewCommandLocked(options) {
271
281
  const config = (0, config_1.loadConfig)(options.root);
272
282
  const ws = normalizeWorkspace(options.ws);
273
283
  const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
@@ -295,7 +305,7 @@ function runWorkReceiptNewCommand(options) {
295
305
  });
296
306
  printReceipt("receipt created", receipt, options.json);
297
307
  }
298
- function runWorkReceiptUpdateCommand(options) {
308
+ function runWorkReceiptUpdateCommandLocked(options) {
299
309
  const loaded = loadMutableAgentNode(options.root, options.id, options.ws, "receipt");
300
310
  if (options.receiptStatus) {
301
311
  loaded.frontmatter.receipt_status = normalizeEnum(options.receiptStatus, "--receipt-status", RECEIPT_STATUSES);
@@ -308,7 +318,7 @@ function runWorkReceiptUpdateCommand(options) {
308
318
  maybeReindex(options.root, loaded.config);
309
319
  printReceipt("receipt updated", nodeReceipt(options.root, loaded.node), options.json);
310
320
  }
311
- function runWorkArtifactAddCommand(options) {
321
+ function runWorkArtifactAddCommandLocked(options) {
312
322
  const config = (0, config_1.loadConfig)(options.root);
313
323
  const ws = normalizeWorkspace(options.ws);
314
324
  const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
@@ -363,3 +373,25 @@ function runWorkArtifactAddCommand(options) {
363
373
  }
364
374
  console.log(`work artifact registered: ${target.qid} -> ${archiveUri}`);
365
375
  }
376
+ function withWorkLock(root, fn) {
377
+ const config = (0, config_1.loadConfig)(root);
378
+ return (0, lock_1.withMutationLock)(root, config.index.lock_timeout_ms, fn);
379
+ }
380
+ function runWorkContractNewCommand(options) {
381
+ return withWorkLock(options.root, () => runWorkContractNewCommandLocked(options));
382
+ }
383
+ function runWorkOrderNewCommand(options) {
384
+ return withWorkLock(options.root, () => runWorkOrderNewCommandLocked(options));
385
+ }
386
+ function runWorkOrderUpdateCommand(options) {
387
+ return withWorkLock(options.root, () => runWorkOrderUpdateCommandLocked(options));
388
+ }
389
+ function runWorkReceiptNewCommand(options) {
390
+ return withWorkLock(options.root, () => runWorkReceiptNewCommandLocked(options));
391
+ }
392
+ function runWorkReceiptUpdateCommand(options) {
393
+ return withWorkLock(options.root, () => runWorkReceiptUpdateCommandLocked(options));
394
+ }
395
+ function runWorkArtifactAddCommand(options) {
396
+ return withWorkLock(options.root, () => runWorkArtifactAddCommandLocked(options));
397
+ }
@@ -22,7 +22,10 @@ const PACK_EDGE_KEYS = new Set([
22
22
  const NEXT_WORK_STRATEGIES = new Set(["chain_then_priority"]);
23
23
  const WORKSPACE_VISIBILITY_VALUES = new Set(["private", "internal", "public"]);
24
24
  const BUNDLE_PROFILE_VALUES = new Set(["private", "public"]);
25
+ const INDEX_BACKEND_VALUES = new Set(["json", "sqlite"]);
25
26
  const DEFAULT_ARCHIVE_LARGE_CACHE_WARNING_BYTES = 26214400;
27
+ const DEFAULT_SQLITE_COMMIT_WARNING_BYTES = 52428800;
28
+ const DEFAULT_LOCK_TIMEOUT_MS = 10000;
26
29
  function isObject(value) {
27
30
  return typeof value === "object" && value !== null && !Array.isArray(value);
28
31
  }
@@ -231,7 +234,19 @@ function validateConfigSchema(raw) {
231
234
  ? {
232
235
  auto_reindex: requireBoolean(indexRaw.auto_reindex, "index.auto_reindex", errors),
233
236
  tolerant: requireBoolean(indexRaw.tolerant, "index.tolerant", errors),
237
+ backend: indexRaw.backend === undefined
238
+ ? "json"
239
+ : requireStringInSet(indexRaw.backend, "index.backend", INDEX_BACKEND_VALUES, errors),
234
240
  global_index_path: requireContainedPath(indexRaw.global_index_path, "index.global_index_path", errors),
241
+ sqlite_path: indexRaw.sqlite_path === undefined
242
+ ? ".mdkg/index/mdkg.sqlite"
243
+ : requireContainedPath(indexRaw.sqlite_path, "index.sqlite_path", errors),
244
+ sqlite_commit_warning_bytes: indexRaw.sqlite_commit_warning_bytes === undefined
245
+ ? DEFAULT_SQLITE_COMMIT_WARNING_BYTES
246
+ : requireNonNegativeInteger(indexRaw.sqlite_commit_warning_bytes, "index.sqlite_commit_warning_bytes", errors),
247
+ lock_timeout_ms: indexRaw.lock_timeout_ms === undefined
248
+ ? DEFAULT_LOCK_TIMEOUT_MS
249
+ : requirePositiveInteger(indexRaw.lock_timeout_ms, "index.lock_timeout_ms", errors),
235
250
  }
236
251
  : undefined;
237
252
  const capabilities = capabilitiesRaw
@@ -15,6 +15,7 @@ const fs_1 = __importDefault(require("fs"));
15
15
  const path_1 = __importDefault(require("path"));
16
16
  const child_process_1 = require("child_process");
17
17
  const paths_1 = require("../core/paths");
18
+ const atomic_1 = require("../util/atomic");
18
19
  const zip_1 = require("../util/zip");
19
20
  const IMPORTS_CACHE_VERSION = 1;
20
21
  const MANIFEST_ENTRY = "manifest.json";
@@ -298,8 +299,7 @@ function buildBundleImportsIndex(root, config) {
298
299
  };
299
300
  }
300
301
  function writeBundleImportsIndex(indexPath, index) {
301
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
302
- fs_1.default.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf8");
302
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(index, null, 2));
303
303
  }
304
304
  function isBundleImportsIndexStale(root, config) {
305
305
  const indexPath = resolveBundleImportsIndexPath(root);
@@ -9,6 +9,7 @@ exports.loadCapabilitiesIndex = loadCapabilitiesIndex;
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const paths_1 = require("../core/paths");
12
+ const atomic_1 = require("../util/atomic");
12
13
  const workspace_files_1 = require("./workspace_files");
13
14
  const capabilities_indexer_1 = require("./capabilities_indexer");
14
15
  function mtimeMs(filePath) {
@@ -76,8 +77,7 @@ function readCapabilitiesIndex(indexPath) {
76
77
  }
77
78
  }
78
79
  function writeCapabilitiesIndex(indexPath, index) {
79
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
80
- fs_1.default.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf8");
80
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(index, null, 2));
81
81
  }
82
82
  function loadCapabilitiesIndex(options) {
83
83
  const useCache = options.useCache ?? true;
@@ -8,6 +8,7 @@ exports.loadIndex = loadIndex;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const sort_1 = require("../util/sort");
11
+ const atomic_1 = require("../util/atomic");
11
12
  const indexer_1 = require("./indexer");
12
13
  const staleness_1 = require("./staleness");
13
14
  const bundle_imports_1 = require("./bundle_imports");
@@ -23,8 +24,7 @@ function readIndex(indexPath) {
23
24
  }
24
25
  function writeIndex(indexPath, index) {
25
26
  const sortedIndex = { ...index, nodes: (0, sort_1.sortIndexNodes)(index.nodes) };
26
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
27
- fs_1.default.writeFileSync(indexPath, JSON.stringify(sortedIndex, null, 2));
27
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(sortedIndex, null, 2));
28
28
  }
29
29
  function loadIndex(options) {
30
30
  const useCache = options.useCache ?? true;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writeDerivedIndexes = writeDerivedIndexes;
7
+ const path_1 = __importDefault(require("path"));
8
+ const capabilities_indexer_1 = require("./capabilities_indexer");
9
+ const capabilities_index_cache_1 = require("./capabilities_index_cache");
10
+ const bundle_imports_1 = require("./bundle_imports");
11
+ const indexer_1 = require("./indexer");
12
+ const index_cache_1 = require("./index_cache");
13
+ const skills_index_cache_1 = require("./skills_index_cache");
14
+ const skills_indexer_1 = require("./skills_indexer");
15
+ const sqlite_index_1 = require("./sqlite_index");
16
+ function writeDerivedIndexes(root, config, nodeIndex, options) {
17
+ const nextNodeIndex = nodeIndex ?? (0, indexer_1.buildIndex)(root, config, { tolerant: options?.tolerant ?? config.index.tolerant });
18
+ const skillsIndex = (0, skills_indexer_1.buildSkillsIndex)(root, config);
19
+ const capabilitiesIndex = (0, capabilities_indexer_1.buildCapabilitiesIndex)(root, config, nextNodeIndex);
20
+ const importsIndex = (0, bundle_imports_1.buildBundleImportsIndex)(root, config);
21
+ const nodesOutputPath = path_1.default.resolve(root, config.index.global_index_path);
22
+ const skillsOutputPath = (0, skills_indexer_1.resolveSkillsIndexPath)(root);
23
+ const capabilitiesOutputPath = (0, capabilities_indexer_1.resolveCapabilitiesIndexPath)(root, config);
24
+ const importsOutputPath = (0, bundle_imports_1.resolveBundleImportsIndexPath)(root);
25
+ (0, index_cache_1.writeIndex)(nodesOutputPath, nextNodeIndex);
26
+ (0, skills_index_cache_1.writeSkillsIndex)(skillsOutputPath, skillsIndex);
27
+ (0, capabilities_index_cache_1.writeCapabilitiesIndex)(capabilitiesOutputPath, capabilitiesIndex);
28
+ (0, bundle_imports_1.writeBundleImportsIndex)(importsOutputPath, importsIndex.index);
29
+ const paths = {
30
+ nodes: nodesOutputPath,
31
+ skills: skillsOutputPath,
32
+ capabilities: capabilitiesOutputPath,
33
+ imports: importsOutputPath,
34
+ };
35
+ if ((0, sqlite_index_1.isSqliteBackend)(config)) {
36
+ paths.sqlite = (0, sqlite_index_1.writeSqliteIndex)({
37
+ root,
38
+ config,
39
+ nodeIndex: nextNodeIndex,
40
+ skillsIndex,
41
+ capabilitiesIndex,
42
+ importsIndex: importsIndex.index,
43
+ });
44
+ }
45
+ return { nodeIndex: nextNodeIndex, paths };
46
+ }
@@ -8,6 +8,7 @@ exports.loadSkillsIndex = loadSkillsIndex;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const paths_1 = require("../core/paths");
11
+ const atomic_1 = require("../util/atomic");
11
12
  const skills_indexer_1 = require("./skills_indexer");
12
13
  function mtimeMs(filePath) {
13
14
  return fs_1.default.statSync(filePath).mtimeMs;
@@ -67,8 +68,7 @@ function writeSkillsIndex(indexPath, index) {
67
68
  ...index,
68
69
  skills: sortedSkills,
69
70
  };
70
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
71
- fs_1.default.writeFileSync(indexPath, JSON.stringify(sortedIndex, null, 2), "utf8");
71
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(sortedIndex, null, 2));
72
72
  }
73
73
  function loadSkillsIndex(options) {
74
74
  const useCache = options.useCache ?? true;