mdkg 0.1.1 → 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.
- package/CHANGELOG.md +104 -0
- package/README.md +124 -18
- package/dist/cli.js +567 -15
- package/dist/commands/archive.js +486 -0
- package/dist/commands/bundle.js +743 -0
- package/dist/commands/bundle_import.js +255 -0
- package/dist/commands/capability.js +162 -0
- package/dist/commands/checkpoint.js +31 -5
- package/dist/commands/doctor.js +269 -9
- package/dist/commands/format.js +38 -9
- package/dist/commands/index.js +12 -12
- package/dist/commands/init.js +194 -63
- package/dist/commands/init_manifest.js +19 -6
- package/dist/commands/list.js +5 -2
- package/dist/commands/new.js +36 -7
- package/dist/commands/next.js +7 -0
- package/dist/commands/node_card.js +4 -1
- package/dist/commands/pack.js +62 -2
- package/dist/commands/query_output.js +1 -0
- package/dist/commands/search.js +5 -2
- package/dist/commands/show.js +7 -14
- package/dist/commands/skill_mirror.js +22 -0
- package/dist/commands/task.js +23 -6
- package/dist/commands/upgrade.js +24 -1
- package/dist/commands/validate.js +20 -1
- package/dist/commands/work.js +397 -0
- package/dist/commands/workspace.js +12 -2
- package/dist/core/config.js +115 -1
- package/dist/graph/agent_file_types.js +78 -5
- package/dist/graph/archive_file.js +125 -0
- package/dist/graph/archive_integrity.js +66 -0
- package/dist/graph/bundle_imports.js +418 -0
- package/dist/graph/capabilities_index_cache.js +103 -0
- package/dist/graph/capabilities_indexer.js +231 -0
- package/dist/graph/frontmatter.js +19 -0
- package/dist/graph/index_cache.js +23 -6
- package/dist/graph/indexer.js +4 -1
- package/dist/graph/node.js +23 -4
- package/dist/graph/node_body.js +37 -0
- package/dist/graph/reindex.js +46 -0
- package/dist/graph/skills_index_cache.js +2 -2
- package/dist/graph/skills_indexer.js +8 -3
- package/dist/graph/sqlite_index.js +293 -0
- package/dist/graph/validate_graph.js +83 -7
- package/dist/graph/visibility.js +214 -0
- package/dist/graph/workspace_files.js +22 -0
- package/dist/init/AGENT_START.md +24 -0
- package/dist/init/CLI_COMMAND_MATRIX.md +61 -3
- package/dist/init/README.md +70 -4
- package/dist/init/config.json +18 -2
- package/dist/init/core/guide.md +6 -2
- package/dist/init/core/rule-1-mdkg-conventions.md +2 -1
- package/dist/init/core/rule-3-cli-contract.md +72 -4
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +47 -11
- package/dist/init/core/rule-5-release-and-versioning.md +4 -3
- package/dist/init/core/rule-6-templates-and-schemas.md +7 -0
- package/dist/init/init-manifest.json +21 -16
- package/dist/init/skills/default/build-pack-and-execute-task/SKILL.md +2 -1
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +26 -0
- package/dist/init/templates/default/archive.md +33 -0
- package/dist/init/templates/default/receipt.md +15 -1
- package/dist/init/templates/default/work.md +6 -1
- package/dist/init/templates/default/work_order.md +15 -1
- package/dist/pack/export_md.js +3 -0
- package/dist/pack/export_xml.js +3 -0
- package/dist/pack/order.js +1 -0
- package/dist/pack/pack.js +3 -13
- package/dist/util/argparse.js +30 -0
- package/dist/util/atomic.js +44 -0
- package/dist/util/lock.js +72 -0
- package/dist/util/refs.js +40 -0
- package/dist/util/zip.js +153 -0
- package/package.json +14 -5
|
@@ -0,0 +1,397 @@
|
|
|
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.runWorkContractNewCommand = runWorkContractNewCommand;
|
|
7
|
+
exports.runWorkOrderNewCommand = runWorkOrderNewCommand;
|
|
8
|
+
exports.runWorkOrderUpdateCommand = runWorkOrderUpdateCommand;
|
|
9
|
+
exports.runWorkReceiptNewCommand = runWorkReceiptNewCommand;
|
|
10
|
+
exports.runWorkReceiptUpdateCommand = runWorkReceiptUpdateCommand;
|
|
11
|
+
exports.runWorkArtifactAddCommand = runWorkArtifactAddCommand;
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const config_1 = require("../core/config");
|
|
15
|
+
const frontmatter_1 = require("../graph/frontmatter");
|
|
16
|
+
const indexer_1 = require("../graph/indexer");
|
|
17
|
+
const index_cache_1 = require("../graph/index_cache");
|
|
18
|
+
const reindex_1 = require("../graph/reindex");
|
|
19
|
+
const agent_file_types_1 = require("../graph/agent_file_types");
|
|
20
|
+
const loader_1 = require("../templates/loader");
|
|
21
|
+
const date_1 = require("../util/date");
|
|
22
|
+
const errors_1 = require("../util/errors");
|
|
23
|
+
const id_1 = require("../util/id");
|
|
24
|
+
const qid_1 = require("../util/qid");
|
|
25
|
+
const atomic_1 = require("../util/atomic");
|
|
26
|
+
const lock_1 = require("../util/lock");
|
|
27
|
+
const event_support_1 = require("./event_support");
|
|
28
|
+
const archive_1 = require("./archive");
|
|
29
|
+
const PRICING_MODELS = new Set(["free", "included", "quoted", "fixed", "metered", "subscription"]);
|
|
30
|
+
const ORDER_STATUSES = new Set(["submitted", "accepted", "running", "completed", "cancelled", "failed"]);
|
|
31
|
+
const RECEIPT_STATUSES = new Set(["recorded", "verified", "rejected", "superseded"]);
|
|
32
|
+
const OUTCOMES = new Set(["success", "partial", "failure"]);
|
|
33
|
+
function parseCsvList(raw) {
|
|
34
|
+
if (!raw) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
return raw
|
|
38
|
+
.split(",")
|
|
39
|
+
.map((value) => value.trim())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
}
|
|
42
|
+
function normalizeWorkspace(value) {
|
|
43
|
+
if (!value) {
|
|
44
|
+
return "root";
|
|
45
|
+
}
|
|
46
|
+
const normalized = value.toLowerCase();
|
|
47
|
+
if (normalized === "all") {
|
|
48
|
+
throw new errors_1.UsageError("--ws all is not valid for work commands");
|
|
49
|
+
}
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
52
|
+
function normalizePortableId(value, flag) {
|
|
53
|
+
const normalized = value.toLowerCase();
|
|
54
|
+
if (!(0, id_1.isPortableId)(normalized)) {
|
|
55
|
+
throw new errors_1.UsageError(`${flag} must be a lowercase portable id`);
|
|
56
|
+
}
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
function normalizeEnum(value, flag, allowed) {
|
|
60
|
+
const normalized = value.toLowerCase();
|
|
61
|
+
if (!allowed.has(normalized)) {
|
|
62
|
+
throw new errors_1.UsageError(`${flag} must be one of ${Array.from(allowed).join(", ")}`);
|
|
63
|
+
}
|
|
64
|
+
return normalized;
|
|
65
|
+
}
|
|
66
|
+
function slugifyTitle(title) {
|
|
67
|
+
const slug = title
|
|
68
|
+
.trim()
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
71
|
+
.replace(/^-+|-+$/g, "")
|
|
72
|
+
.replace(/-+/g, "-");
|
|
73
|
+
return slug || "work";
|
|
74
|
+
}
|
|
75
|
+
function toPosixPath(value) {
|
|
76
|
+
return value.split(path_1.default.sep).join("/");
|
|
77
|
+
}
|
|
78
|
+
function appendUnique(existing, additions) {
|
|
79
|
+
const next = [...existing];
|
|
80
|
+
const seen = new Set(existing);
|
|
81
|
+
for (const addition of additions) {
|
|
82
|
+
if (!seen.has(addition)) {
|
|
83
|
+
next.push(addition);
|
|
84
|
+
seen.add(addition);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return next;
|
|
88
|
+
}
|
|
89
|
+
function toStringList(value) {
|
|
90
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
91
|
+
}
|
|
92
|
+
function maybeReindex(root, config) {
|
|
93
|
+
if (!config.index.auto_reindex) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
(0, reindex_1.writeDerivedIndexes)(root, config, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
|
|
97
|
+
}
|
|
98
|
+
function findNodeById(index, ws, id, type) {
|
|
99
|
+
const node = index.nodes[`${ws}:${id}`];
|
|
100
|
+
if (!node) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
if (type && node.type !== type) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
return node;
|
|
107
|
+
}
|
|
108
|
+
function requireNodeById(index, ws, id, type, label) {
|
|
109
|
+
const node = findNodeById(index, ws, id, type);
|
|
110
|
+
if (!node) {
|
|
111
|
+
throw new errors_1.NotFoundError(`${label} not found: ${id}`);
|
|
112
|
+
}
|
|
113
|
+
return node;
|
|
114
|
+
}
|
|
115
|
+
function nodeReceipt(root, node) {
|
|
116
|
+
return {
|
|
117
|
+
workspace: node.ws,
|
|
118
|
+
id: node.id,
|
|
119
|
+
qid: node.qid,
|
|
120
|
+
path: node.path,
|
|
121
|
+
type: node.type,
|
|
122
|
+
title: node.title,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function writeFrontmatterFile(filePath, frontmatter, body) {
|
|
126
|
+
const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
|
|
127
|
+
const content = ["---", ...lines, "---", body.trimStart()].join("\n");
|
|
128
|
+
(0, atomic_1.atomicWriteFile)(filePath, content.endsWith("\n") ? content : `${content}\n`);
|
|
129
|
+
}
|
|
130
|
+
function createAgentWorkflowNode(options) {
|
|
131
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
132
|
+
const ws = normalizeWorkspace(options.ws);
|
|
133
|
+
const workspace = config.workspaces[ws];
|
|
134
|
+
if (!workspace) {
|
|
135
|
+
throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
|
|
136
|
+
}
|
|
137
|
+
const id = normalizePortableId(options.id, "--id");
|
|
138
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
139
|
+
if (index.nodes[`${ws}:${id}`]) {
|
|
140
|
+
throw new errors_1.UsageError(`node already exists: ${ws}:${id}`);
|
|
141
|
+
}
|
|
142
|
+
const today = (0, date_1.formatDate)(options.now ?? new Date());
|
|
143
|
+
const slug = slugifyTitle(options.title);
|
|
144
|
+
const filePath = path_1.default.resolve(options.root, workspace.path, workspace.mdkg_dir, "work", `${id}-${slug}`, agent_file_types_1.AGENT_FILE_BASENAMES[options.type]);
|
|
145
|
+
const template = (0, loader_1.loadTemplate)(options.root, config, options.type);
|
|
146
|
+
const content = (0, loader_1.renderTemplate)(template, {
|
|
147
|
+
id,
|
|
148
|
+
type: options.type,
|
|
149
|
+
title: options.title,
|
|
150
|
+
created: today,
|
|
151
|
+
updated: today,
|
|
152
|
+
...options.overrides,
|
|
153
|
+
});
|
|
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
|
+
}
|
|
164
|
+
maybeReindex(options.root, config);
|
|
165
|
+
(0, event_support_1.appendAutomaticEvent)({
|
|
166
|
+
root: options.root,
|
|
167
|
+
ws,
|
|
168
|
+
kind: "WORK_NODE_CREATED",
|
|
169
|
+
status: "ok",
|
|
170
|
+
refs: [id],
|
|
171
|
+
notes: `${options.type} semantic mirror created via mdkg work`,
|
|
172
|
+
now: options.now,
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
workspace: ws,
|
|
176
|
+
id,
|
|
177
|
+
qid: `${ws}:${id}`,
|
|
178
|
+
path: toPosixPath(path_1.default.relative(options.root, filePath)),
|
|
179
|
+
type: options.type,
|
|
180
|
+
title: options.title,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function resolveWorkNode(index, idOrQid, ws, allowedTypes, label) {
|
|
184
|
+
const resolved = (0, qid_1.resolveQid)(index, idOrQid, ws);
|
|
185
|
+
if (resolved.status !== "ok") {
|
|
186
|
+
throw new errors_1.NotFoundError((0, qid_1.formatResolveError)(label, idOrQid, resolved, ws));
|
|
187
|
+
}
|
|
188
|
+
const node = index.nodes[resolved.qid];
|
|
189
|
+
if (!node || !allowedTypes.has(node.type)) {
|
|
190
|
+
throw new errors_1.NotFoundError(`${label} not found: ${idOrQid}`);
|
|
191
|
+
}
|
|
192
|
+
if (node.source?.imported) {
|
|
193
|
+
throw new errors_1.UsageError(`cannot mutate read-only imported node ${node.qid}; update the source workspace for bundle import ${node.source.import_alias}`);
|
|
194
|
+
}
|
|
195
|
+
return node;
|
|
196
|
+
}
|
|
197
|
+
function loadMutableAgentNode(root, idOrQid, wsRaw, type) {
|
|
198
|
+
const config = (0, config_1.loadConfig)(root);
|
|
199
|
+
const ws = normalizeWorkspace(wsRaw);
|
|
200
|
+
const { index } = (0, index_cache_1.loadIndex)({ root, config });
|
|
201
|
+
const node = resolveWorkNode(index, idOrQid, ws, new Set([type]), type);
|
|
202
|
+
const filePath = path_1.default.resolve(root, node.path);
|
|
203
|
+
const parsed = (0, frontmatter_1.parseFrontmatter)(fs_1.default.readFileSync(filePath, "utf8"), filePath);
|
|
204
|
+
return { config, node, filePath, frontmatter: { ...parsed.frontmatter }, body: parsed.body };
|
|
205
|
+
}
|
|
206
|
+
function printReceipt(action, receipt, json) {
|
|
207
|
+
if (json) {
|
|
208
|
+
console.log(JSON.stringify({ action, node: receipt }, null, 2));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
console.log(`work ${action}: ${receipt.qid} (${receipt.path})`);
|
|
212
|
+
}
|
|
213
|
+
function runWorkContractNewCommandLocked(options) {
|
|
214
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
215
|
+
const ws = normalizeWorkspace(options.ws);
|
|
216
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
217
|
+
const agentId = normalizePortableId(options.agentId, "--agent-id");
|
|
218
|
+
const relates = findNodeById(index, ws, agentId, "spec") ? [agentId] : [];
|
|
219
|
+
const receipt = createAgentWorkflowNode({
|
|
220
|
+
root: options.root,
|
|
221
|
+
ws,
|
|
222
|
+
type: "work",
|
|
223
|
+
title: options.title,
|
|
224
|
+
id: options.id,
|
|
225
|
+
now: options.now,
|
|
226
|
+
overrides: {
|
|
227
|
+
agent_id: agentId,
|
|
228
|
+
kind: options.kind.toLowerCase(),
|
|
229
|
+
pricing_model: normalizeEnum(options.pricingModel ?? "quoted", "--pricing-model", PRICING_MODELS),
|
|
230
|
+
required_capabilities: parseCsvList(options.requiredCapabilities),
|
|
231
|
+
inputs: parseCsvList(options.inputs),
|
|
232
|
+
outputs: parseCsvList(options.outputs),
|
|
233
|
+
receipt_required: true,
|
|
234
|
+
relates,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
printReceipt("contract created", receipt, options.json);
|
|
238
|
+
}
|
|
239
|
+
function runWorkOrderNewCommandLocked(options) {
|
|
240
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
241
|
+
const ws = normalizeWorkspace(options.ws);
|
|
242
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
243
|
+
const workId = normalizePortableId(options.workId, "--work-id");
|
|
244
|
+
const workNode = requireNodeById(index, ws, workId, "work", "work contract");
|
|
245
|
+
const workVersion = String(workNode.attributes.version ?? "0.1.0");
|
|
246
|
+
const receipt = createAgentWorkflowNode({
|
|
247
|
+
root: options.root,
|
|
248
|
+
ws,
|
|
249
|
+
type: "work_order",
|
|
250
|
+
title: options.title,
|
|
251
|
+
id: options.id,
|
|
252
|
+
now: options.now,
|
|
253
|
+
overrides: {
|
|
254
|
+
work_id: workId,
|
|
255
|
+
work_version: workVersion,
|
|
256
|
+
requester: options.requester,
|
|
257
|
+
order_status: "submitted",
|
|
258
|
+
request_ref: options.requestRef ?? "request.redacted",
|
|
259
|
+
input_refs: parseCsvList(options.inputRefs),
|
|
260
|
+
requested_outputs: parseCsvList(options.requestedOutputs),
|
|
261
|
+
constraint_refs: parseCsvList(options.constraintRefs),
|
|
262
|
+
artifact_policy: "commit_sidecar_and_zip",
|
|
263
|
+
relates: [workId],
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
printReceipt("order created", receipt, options.json);
|
|
267
|
+
}
|
|
268
|
+
function runWorkOrderUpdateCommandLocked(options) {
|
|
269
|
+
const loaded = loadMutableAgentNode(options.root, options.id, options.ws, "work_order");
|
|
270
|
+
if (options.status) {
|
|
271
|
+
loaded.frontmatter.order_status = normalizeEnum(options.status, "--status", ORDER_STATUSES);
|
|
272
|
+
}
|
|
273
|
+
loaded.frontmatter.input_refs = appendUnique(toStringList(loaded.frontmatter.input_refs), parseCsvList(options.addInputRefs));
|
|
274
|
+
loaded.frontmatter.artifacts = appendUnique(toStringList(loaded.frontmatter.artifacts), parseCsvList(options.addArtifacts));
|
|
275
|
+
loaded.frontmatter.updated = (0, date_1.formatDate)(options.now ?? new Date());
|
|
276
|
+
writeFrontmatterFile(loaded.filePath, loaded.frontmatter, loaded.body);
|
|
277
|
+
maybeReindex(options.root, loaded.config);
|
|
278
|
+
printReceipt("order updated", nodeReceipt(options.root, loaded.node), options.json);
|
|
279
|
+
}
|
|
280
|
+
function runWorkReceiptNewCommandLocked(options) {
|
|
281
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
282
|
+
const ws = normalizeWorkspace(options.ws);
|
|
283
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
284
|
+
const workOrderId = normalizePortableId(options.workOrderId, "--work-order-id");
|
|
285
|
+
requireNodeById(index, ws, workOrderId, "work_order", "work order");
|
|
286
|
+
const receipt = createAgentWorkflowNode({
|
|
287
|
+
root: options.root,
|
|
288
|
+
ws,
|
|
289
|
+
type: "receipt",
|
|
290
|
+
title: options.title,
|
|
291
|
+
id: options.id,
|
|
292
|
+
now: options.now,
|
|
293
|
+
overrides: {
|
|
294
|
+
work_order_id: workOrderId,
|
|
295
|
+
receipt_status: normalizeEnum(options.receiptStatus ?? "recorded", "--receipt-status", RECEIPT_STATUSES),
|
|
296
|
+
outcome: normalizeEnum(options.outcome, "--outcome", OUTCOMES),
|
|
297
|
+
cost_ref: options.costRef ?? "cost.redacted",
|
|
298
|
+
artifacts: parseCsvList(options.artifacts),
|
|
299
|
+
proof_refs: parseCsvList(options.proofRefs),
|
|
300
|
+
attestation_refs: parseCsvList(options.attestationRefs),
|
|
301
|
+
input_hashes: parseCsvList(options.inputHashes),
|
|
302
|
+
output_hashes: parseCsvList(options.outputHashes),
|
|
303
|
+
relates: [workOrderId],
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
printReceipt("receipt created", receipt, options.json);
|
|
307
|
+
}
|
|
308
|
+
function runWorkReceiptUpdateCommandLocked(options) {
|
|
309
|
+
const loaded = loadMutableAgentNode(options.root, options.id, options.ws, "receipt");
|
|
310
|
+
if (options.receiptStatus) {
|
|
311
|
+
loaded.frontmatter.receipt_status = normalizeEnum(options.receiptStatus, "--receipt-status", RECEIPT_STATUSES);
|
|
312
|
+
}
|
|
313
|
+
loaded.frontmatter.artifacts = appendUnique(toStringList(loaded.frontmatter.artifacts), parseCsvList(options.addArtifacts));
|
|
314
|
+
loaded.frontmatter.proof_refs = appendUnique(toStringList(loaded.frontmatter.proof_refs), parseCsvList(options.addProofRefs));
|
|
315
|
+
loaded.frontmatter.attestation_refs = appendUnique(toStringList(loaded.frontmatter.attestation_refs), parseCsvList(options.addAttestationRefs));
|
|
316
|
+
loaded.frontmatter.updated = (0, date_1.formatDate)(options.now ?? new Date());
|
|
317
|
+
writeFrontmatterFile(loaded.filePath, loaded.frontmatter, loaded.body);
|
|
318
|
+
maybeReindex(options.root, loaded.config);
|
|
319
|
+
printReceipt("receipt updated", nodeReceipt(options.root, loaded.node), options.json);
|
|
320
|
+
}
|
|
321
|
+
function runWorkArtifactAddCommandLocked(options) {
|
|
322
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
323
|
+
const ws = normalizeWorkspace(options.ws);
|
|
324
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
325
|
+
const target = resolveWorkNode(index, options.targetId, ws, new Set(["work_order", "receipt"]), "work order or receipt");
|
|
326
|
+
const archiveLogs = [];
|
|
327
|
+
const originalLog = console.log;
|
|
328
|
+
console.log = (...args) => {
|
|
329
|
+
archiveLogs.push(args.map(String).join(" "));
|
|
330
|
+
};
|
|
331
|
+
let archivePayload = {};
|
|
332
|
+
try {
|
|
333
|
+
(0, archive_1.runArchiveAddCommand)({
|
|
334
|
+
root: options.root,
|
|
335
|
+
ws: target.ws,
|
|
336
|
+
file: options.file,
|
|
337
|
+
id: options.id,
|
|
338
|
+
kind: options.kind ?? (target.type === "work_order" ? "source" : "artifact"),
|
|
339
|
+
relates: target.id,
|
|
340
|
+
json: true,
|
|
341
|
+
now: options.now,
|
|
342
|
+
});
|
|
343
|
+
archivePayload = JSON.parse(archiveLogs.join("\n"));
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
console.log = originalLog;
|
|
347
|
+
}
|
|
348
|
+
const archiveUri = archivePayload.archive?.archive_uri;
|
|
349
|
+
if (!archiveUri) {
|
|
350
|
+
throw new Error("archive add did not return an archive URI");
|
|
351
|
+
}
|
|
352
|
+
const loaded = loadMutableAgentNode(options.root, target.qid, target.ws, target.type);
|
|
353
|
+
const archiveKind = (options.kind ?? (target.type === "work_order" ? "source" : "artifact")).toLowerCase();
|
|
354
|
+
if (target.type === "work_order" && archiveKind === "source") {
|
|
355
|
+
loaded.frontmatter.input_refs = appendUnique(toStringList(loaded.frontmatter.input_refs), [archiveUri]);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
loaded.frontmatter.artifacts = appendUnique(toStringList(loaded.frontmatter.artifacts), [archiveUri]);
|
|
359
|
+
}
|
|
360
|
+
if (archivePayload.archive?.id) {
|
|
361
|
+
loaded.frontmatter.relates = appendUnique(toStringList(loaded.frontmatter.relates), [archivePayload.archive.id]);
|
|
362
|
+
}
|
|
363
|
+
loaded.frontmatter.updated = (0, date_1.formatDate)(options.now ?? new Date());
|
|
364
|
+
writeFrontmatterFile(loaded.filePath, loaded.frontmatter, loaded.body);
|
|
365
|
+
maybeReindex(options.root, loaded.config);
|
|
366
|
+
if (options.json) {
|
|
367
|
+
console.log(JSON.stringify({
|
|
368
|
+
action: "artifact_registered",
|
|
369
|
+
target: nodeReceipt(options.root, target),
|
|
370
|
+
archive: archivePayload.archive,
|
|
371
|
+
}, null, 2));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
console.log(`work artifact registered: ${target.qid} -> ${archiveUri}`);
|
|
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
|
+
}
|
|
@@ -21,6 +21,7 @@ function workspaceReceipt(alias, workspace) {
|
|
|
21
21
|
path: workspace.path,
|
|
22
22
|
enabled: workspace.enabled,
|
|
23
23
|
mdkg_dir: workspace.mdkg_dir,
|
|
24
|
+
visibility: workspace.visibility ?? "private",
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
function printWorkspaceMutationReceipt(action, workspace, json) {
|
|
@@ -90,6 +91,7 @@ function runWorkspaceListCommand(options) {
|
|
|
90
91
|
path: ws.path,
|
|
91
92
|
enabled: ws.enabled,
|
|
92
93
|
mdkg_dir: ws.mdkg_dir,
|
|
94
|
+
visibility: ws.visibility,
|
|
93
95
|
};
|
|
94
96
|
}),
|
|
95
97
|
}, null, 2));
|
|
@@ -102,13 +104,21 @@ function runWorkspaceListCommand(options) {
|
|
|
102
104
|
for (const alias of aliases) {
|
|
103
105
|
const ws = config.workspaces[alias];
|
|
104
106
|
const status = ws.enabled ? "enabled" : "disabled";
|
|
105
|
-
console.log(`${alias} | ${status} | ${ws.path} | ${ws.mdkg_dir}`);
|
|
107
|
+
console.log(`${alias} | ${status} | ${ws.visibility} | ${ws.path} | ${ws.mdkg_dir}`);
|
|
106
108
|
}
|
|
107
109
|
}
|
|
110
|
+
function normalizeVisibility(value) {
|
|
111
|
+
const normalized = (value ?? "private").toLowerCase();
|
|
112
|
+
if (normalized === "private" || normalized === "internal" || normalized === "public") {
|
|
113
|
+
return normalized;
|
|
114
|
+
}
|
|
115
|
+
throw new errors_1.UsageError("--visibility must be private, internal, or public");
|
|
116
|
+
}
|
|
108
117
|
function runWorkspaceAddCommand(options) {
|
|
109
118
|
const alias = normalizeAlias(options.alias);
|
|
110
119
|
const workspacePath = normalizeCommandWorkspacePath(options.workspacePath, "workspace path");
|
|
111
120
|
const mdkgDir = normalizeCommandWorkspacePath(options.mdkgDir ?? ".mdkg", "workspace mdkg dir");
|
|
121
|
+
const visibility = normalizeVisibility(options.visibility);
|
|
112
122
|
if ((0, workspace_path_1.isRootWorkspacePath)(workspacePath)) {
|
|
113
123
|
throw new errors_1.UsageError('workspace path must not be "." for non-root workspaces');
|
|
114
124
|
}
|
|
@@ -133,7 +143,7 @@ function runWorkspaceAddCommand(options) {
|
|
|
133
143
|
throw new errors_1.UsageError(`workspace document root already registered by ${existingAlias}`);
|
|
134
144
|
}
|
|
135
145
|
}
|
|
136
|
-
const workspace = { path: workspacePath, enabled: true, mdkg_dir: mdkgDir };
|
|
146
|
+
const workspace = { path: workspacePath, enabled: true, mdkg_dir: mdkgDir, visibility };
|
|
137
147
|
workspaces[alias] = workspace;
|
|
138
148
|
raw.workspaces = workspaces;
|
|
139
149
|
writeRawConfig(configPath, raw);
|
package/dist/core/config.js
CHANGED
|
@@ -20,6 +20,12 @@ const PACK_EDGE_KEYS = new Set([
|
|
|
20
20
|
"next",
|
|
21
21
|
]);
|
|
22
22
|
const NEXT_WORK_STRATEGIES = new Set(["chain_then_priority"]);
|
|
23
|
+
const WORKSPACE_VISIBILITY_VALUES = new Set(["private", "internal", "public"]);
|
|
24
|
+
const BUNDLE_PROFILE_VALUES = new Set(["private", "public"]);
|
|
25
|
+
const INDEX_BACKEND_VALUES = new Set(["json", "sqlite"]);
|
|
26
|
+
const DEFAULT_ARCHIVE_LARGE_CACHE_WARNING_BYTES = 26214400;
|
|
27
|
+
const DEFAULT_SQLITE_COMMIT_WARNING_BYTES = 52428800;
|
|
28
|
+
const DEFAULT_LOCK_TIMEOUT_MS = 10000;
|
|
23
29
|
function isObject(value) {
|
|
24
30
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
31
|
}
|
|
@@ -166,6 +172,18 @@ function validateWorkspaceAlias(alias, errors) {
|
|
|
166
172
|
errors.push(`workspaces.${alias} alias must be lowercase and use [a-z0-9_]`);
|
|
167
173
|
}
|
|
168
174
|
}
|
|
175
|
+
function validateBundleImportAlias(alias, workspaces, errors) {
|
|
176
|
+
if (alias === "all") {
|
|
177
|
+
errors.push("bundle_imports.all alias is reserved");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (alias !== alias.toLowerCase() || !WORKSPACE_ALIAS_RE.test(alias)) {
|
|
181
|
+
errors.push(`bundle_imports.${alias} alias must be lowercase and use [a-z0-9_]`);
|
|
182
|
+
}
|
|
183
|
+
if (workspaces[alias]) {
|
|
184
|
+
errors.push(`bundle_imports.${alias} must not collide with workspaces.${alias}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
169
187
|
function requireContainedPath(value, path, errors) {
|
|
170
188
|
const raw = requireString(value, path, errors);
|
|
171
189
|
if (!raw) {
|
|
@@ -188,16 +206,58 @@ function validateConfigSchema(raw) {
|
|
|
188
206
|
const schema_version = requireNumber(raw.schema_version, "schema_version", errors);
|
|
189
207
|
const tool = requireString(raw.tool, "tool", errors);
|
|
190
208
|
const root_required = requireBoolean(raw.root_required, "root_required", errors);
|
|
209
|
+
const archiveRaw = raw.archive === undefined
|
|
210
|
+
? { large_cache_warning_bytes: DEFAULT_ARCHIVE_LARGE_CACHE_WARNING_BYTES }
|
|
211
|
+
: requireObject(raw.archive, "archive", errors);
|
|
191
212
|
const indexRaw = requireObject(raw.index, "index", errors);
|
|
213
|
+
const capabilitiesRaw = raw.capabilities === undefined
|
|
214
|
+
? { cache_path: ".mdkg/index/capabilities.json" }
|
|
215
|
+
: requireObject(raw.capabilities, "capabilities", errors);
|
|
216
|
+
const bundlesRaw = raw.bundles === undefined
|
|
217
|
+
? { output_dir: ".mdkg/bundles", default_profile: "private" }
|
|
218
|
+
: requireObject(raw.bundles, "bundles", errors);
|
|
219
|
+
const bundleImportsRaw = raw.bundle_imports === undefined
|
|
220
|
+
? {}
|
|
221
|
+
: requireObject(raw.bundle_imports, "bundle_imports", errors);
|
|
192
222
|
const packRaw = requireObject(raw.pack, "pack", errors);
|
|
193
223
|
const templatesRaw = requireObject(raw.templates, "templates", errors);
|
|
194
224
|
const workRaw = requireObject(raw.work, "work", errors);
|
|
195
225
|
const workspacesRaw = requireObject(raw.workspaces, "workspaces", errors);
|
|
226
|
+
const archive = archiveRaw
|
|
227
|
+
? {
|
|
228
|
+
large_cache_warning_bytes: archiveRaw.large_cache_warning_bytes === undefined
|
|
229
|
+
? DEFAULT_ARCHIVE_LARGE_CACHE_WARNING_BYTES
|
|
230
|
+
: requireNonNegativeInteger(archiveRaw.large_cache_warning_bytes, "archive.large_cache_warning_bytes", errors),
|
|
231
|
+
}
|
|
232
|
+
: undefined;
|
|
196
233
|
const index = indexRaw
|
|
197
234
|
? {
|
|
198
235
|
auto_reindex: requireBoolean(indexRaw.auto_reindex, "index.auto_reindex", errors),
|
|
199
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),
|
|
200
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),
|
|
250
|
+
}
|
|
251
|
+
: undefined;
|
|
252
|
+
const capabilities = capabilitiesRaw
|
|
253
|
+
? {
|
|
254
|
+
cache_path: requireContainedPath(capabilitiesRaw.cache_path, "capabilities.cache_path", errors),
|
|
255
|
+
}
|
|
256
|
+
: undefined;
|
|
257
|
+
const bundles = bundlesRaw
|
|
258
|
+
? {
|
|
259
|
+
output_dir: requireContainedPath(bundlesRaw.output_dir, "bundles.output_dir", errors),
|
|
260
|
+
default_profile: requireStringInSet(bundlesRaw.default_profile, "bundles.default_profile", BUNDLE_PROFILE_VALUES, errors),
|
|
201
261
|
}
|
|
202
262
|
: undefined;
|
|
203
263
|
const packLimitsRaw = packRaw ? requireObject(packRaw.limits, "pack.limits", errors) : undefined;
|
|
@@ -268,7 +328,10 @@ function validateConfigSchema(raw) {
|
|
|
268
328
|
const wsPath = requireString(ws.path, `workspaces.${alias}.path`, errors);
|
|
269
329
|
const wsEnabled = requireBoolean(ws.enabled, `workspaces.${alias}.enabled`, errors);
|
|
270
330
|
const wsMdkgDir = requireString(ws.mdkg_dir, `workspaces.${alias}.mdkg_dir`, errors);
|
|
271
|
-
|
|
331
|
+
const wsVisibility = ws.visibility === undefined
|
|
332
|
+
? "private"
|
|
333
|
+
: requireStringInSet(ws.visibility, `workspaces.${alias}.visibility`, WORKSPACE_VISIBILITY_VALUES, errors);
|
|
334
|
+
if (wsPath && wsEnabled !== undefined && wsMdkgDir && wsVisibility) {
|
|
272
335
|
let normalizedPath;
|
|
273
336
|
let normalizedMdkgDir;
|
|
274
337
|
try {
|
|
@@ -303,6 +366,7 @@ function validateConfigSchema(raw) {
|
|
|
303
366
|
path: normalizedPath,
|
|
304
367
|
enabled: wsEnabled,
|
|
305
368
|
mdkg_dir: normalizedMdkgDir,
|
|
369
|
+
visibility: wsVisibility,
|
|
306
370
|
};
|
|
307
371
|
}
|
|
308
372
|
}
|
|
@@ -325,6 +389,52 @@ function validateConfigSchema(raw) {
|
|
|
325
389
|
errors.push('workspaces.root.mdkg_dir must be ".mdkg"');
|
|
326
390
|
}
|
|
327
391
|
}
|
|
392
|
+
const bundle_imports = {};
|
|
393
|
+
if (bundleImportsRaw) {
|
|
394
|
+
for (const [alias, entry] of Object.entries(bundleImportsRaw)) {
|
|
395
|
+
validateBundleImportAlias(alias, workspaces, errors);
|
|
396
|
+
const rawImport = requireObject(entry, `bundle_imports.${alias}`, errors);
|
|
397
|
+
if (!rawImport) {
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const importPath = requireContainedPath(rawImport.path, `bundle_imports.${alias}.path`, errors);
|
|
401
|
+
const enabled = rawImport.enabled === undefined
|
|
402
|
+
? true
|
|
403
|
+
: requireBoolean(rawImport.enabled, `bundle_imports.${alias}.enabled`, errors);
|
|
404
|
+
const visibility = rawImport.visibility === undefined
|
|
405
|
+
? "private"
|
|
406
|
+
: requireStringInSet(rawImport.visibility, `bundle_imports.${alias}.visibility`, WORKSPACE_VISIBILITY_VALUES, errors);
|
|
407
|
+
const expectedProfile = rawImport.expected_profile === undefined
|
|
408
|
+
? "private"
|
|
409
|
+
: requireStringInSet(rawImport.expected_profile, `bundle_imports.${alias}.expected_profile`, BUNDLE_PROFILE_VALUES, errors);
|
|
410
|
+
const sourcePath = rawImport.source_path === undefined
|
|
411
|
+
? undefined
|
|
412
|
+
: requireContainedPath(rawImport.source_path, `bundle_imports.${alias}.source_path`, errors);
|
|
413
|
+
const sourceRepo = rawImport.source_repo === undefined
|
|
414
|
+
? undefined
|
|
415
|
+
: requireString(rawImport.source_repo, `bundle_imports.${alias}.source_repo`, errors);
|
|
416
|
+
const maxStaleSeconds = rawImport.max_stale_seconds === undefined
|
|
417
|
+
? undefined
|
|
418
|
+
: requirePositiveInteger(rawImport.max_stale_seconds, `bundle_imports.${alias}.max_stale_seconds`, errors);
|
|
419
|
+
if (visibility !== undefined &&
|
|
420
|
+
expectedProfile !== undefined &&
|
|
421
|
+
visibility !== "private" &&
|
|
422
|
+
expectedProfile !== "public") {
|
|
423
|
+
errors.push(`bundle_imports.${alias}.expected_profile must be public when visibility is ${visibility}`);
|
|
424
|
+
}
|
|
425
|
+
if (importPath && enabled !== undefined && visibility && expectedProfile) {
|
|
426
|
+
bundle_imports[alias] = {
|
|
427
|
+
path: importPath,
|
|
428
|
+
enabled,
|
|
429
|
+
visibility: visibility,
|
|
430
|
+
expected_profile: expectedProfile,
|
|
431
|
+
...(sourcePath ? { source_path: sourcePath } : {}),
|
|
432
|
+
...(sourceRepo ? { source_repo: sourceRepo } : {}),
|
|
433
|
+
...(maxStaleSeconds !== undefined ? { max_stale_seconds: maxStaleSeconds } : {}),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
328
438
|
if (errors.length > 0) {
|
|
329
439
|
throw new Error(`config validation failed:\n${errors.join("\n")}`);
|
|
330
440
|
}
|
|
@@ -332,7 +442,11 @@ function validateConfigSchema(raw) {
|
|
|
332
442
|
schema_version: schema_version,
|
|
333
443
|
tool: tool,
|
|
334
444
|
root_required: root_required,
|
|
445
|
+
archive: archive,
|
|
335
446
|
index: index,
|
|
447
|
+
capabilities: capabilities,
|
|
448
|
+
bundles: bundles,
|
|
449
|
+
bundle_imports,
|
|
336
450
|
pack: pack,
|
|
337
451
|
templates: templates,
|
|
338
452
|
work: work,
|