mdkg 0.1.0 → 0.1.2
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 +93 -0
- package/README.md +108 -15
- package/dist/cli.js +566 -15
- package/dist/commands/archive.js +474 -0
- package/dist/commands/bundle.js +743 -0
- package/dist/commands/bundle_import.js +243 -0
- package/dist/commands/capability.js +162 -0
- package/dist/commands/doctor.js +233 -2
- package/dist/commands/format.js +38 -9
- package/dist/commands/index.js +11 -0
- package/dist/commands/init.js +188 -63
- package/dist/commands/init_manifest.js +19 -6
- package/dist/commands/list.js +5 -2
- package/dist/commands/new.js +6 -0
- 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 +3 -0
- package/dist/commands/upgrade.js +151 -13
- package/dist/commands/validate.js +19 -2
- package/dist/commands/work.js +365 -0
- package/dist/commands/workspace.js +12 -2
- package/dist/core/config.js +100 -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 +21 -4
- 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/skills_indexer.js +8 -3
- package/dist/graph/template_schema.js +33 -5
- 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 +21 -0
- package/dist/init/CLI_COMMAND_MATRIX.md +58 -3
- package/dist/init/README.md +60 -3
- package/dist/init/config.json +13 -1
- package/dist/init/core/guide.md +6 -2
- package/dist/init/core/rule-3-cli-contract.md +71 -4
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +20 -0
- package/dist/init/core/rule-6-templates-and-schemas.md +10 -1
- package/dist/init/init-manifest.json +19 -14
- 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/templates/builtin.js +38 -0
- package/dist/templates/loader.js +9 -16
- package/dist/util/argparse.js +30 -0
- package/dist/util/refs.js +40 -0
- package/dist/util/zip.js +153 -0
- package/package.json +8 -2
|
@@ -0,0 +1,474 @@
|
|
|
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.runArchiveAddCommand = runArchiveAddCommand;
|
|
7
|
+
exports.runArchiveListCommand = runArchiveListCommand;
|
|
8
|
+
exports.runArchiveShowCommand = runArchiveShowCommand;
|
|
9
|
+
exports.runArchiveVerifyCommand = runArchiveVerifyCommand;
|
|
10
|
+
exports.runArchiveCompressCommand = runArchiveCompressCommand;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const config_1 = require("../core/config");
|
|
14
|
+
const frontmatter_1 = require("../graph/frontmatter");
|
|
15
|
+
const archive_integrity_1 = require("../graph/archive_integrity");
|
|
16
|
+
const indexer_1 = require("../graph/indexer");
|
|
17
|
+
const index_cache_1 = require("../graph/index_cache");
|
|
18
|
+
const visibility_1 = require("../graph/visibility");
|
|
19
|
+
const date_1 = require("../util/date");
|
|
20
|
+
const errors_1 = require("../util/errors");
|
|
21
|
+
const id_1 = require("../util/id");
|
|
22
|
+
const refs_1 = require("../util/refs");
|
|
23
|
+
const zip_1 = require("../util/zip");
|
|
24
|
+
const event_support_1 = require("./event_support");
|
|
25
|
+
const ARCHIVE_KINDS = new Set(["source", "artifact"]);
|
|
26
|
+
const MIME_BY_EXT = {
|
|
27
|
+
".csv": "text/csv",
|
|
28
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
29
|
+
".json": "application/json",
|
|
30
|
+
".md": "text/markdown",
|
|
31
|
+
".pdf": "application/pdf",
|
|
32
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
33
|
+
".txt": "text/plain",
|
|
34
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
35
|
+
".zip": "application/zip",
|
|
36
|
+
};
|
|
37
|
+
function parseCsvList(raw) {
|
|
38
|
+
if (!raw) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return raw
|
|
42
|
+
.split(",")
|
|
43
|
+
.map((value) => value.trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
function toPosixPath(value) {
|
|
47
|
+
return value.split(path_1.default.sep).join("/");
|
|
48
|
+
}
|
|
49
|
+
function slugify(value) {
|
|
50
|
+
const slug = value
|
|
51
|
+
.trim()
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
54
|
+
.replace(/^-+|-+$/g, "")
|
|
55
|
+
.replace(/-+/g, "-");
|
|
56
|
+
return slug || "archive";
|
|
57
|
+
}
|
|
58
|
+
function normalizeWorkspace(value) {
|
|
59
|
+
if (!value) {
|
|
60
|
+
return "root";
|
|
61
|
+
}
|
|
62
|
+
const normalized = value.toLowerCase();
|
|
63
|
+
if (normalized === "all") {
|
|
64
|
+
throw new errors_1.UsageError("--ws all is not valid for archive commands");
|
|
65
|
+
}
|
|
66
|
+
return normalized;
|
|
67
|
+
}
|
|
68
|
+
function normalizeArchiveKind(value) {
|
|
69
|
+
const normalized = (value ?? "source").toLowerCase();
|
|
70
|
+
if (!ARCHIVE_KINDS.has(normalized)) {
|
|
71
|
+
throw new errors_1.UsageError("--kind must be one of source, artifact");
|
|
72
|
+
}
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
function normalizeArchiveId(raw) {
|
|
76
|
+
const normalized = raw.toLowerCase();
|
|
77
|
+
if (!normalized.startsWith("archive.") || !(0, id_1.isPortableId)(normalized)) {
|
|
78
|
+
throw new errors_1.UsageError("--id must be a lowercase portable archive id like archive.example");
|
|
79
|
+
}
|
|
80
|
+
return normalized;
|
|
81
|
+
}
|
|
82
|
+
function archiveIdFromInput(value) {
|
|
83
|
+
return normalizeArchiveId((0, refs_1.archiveIdFromUri)(value) ?? value);
|
|
84
|
+
}
|
|
85
|
+
function inferMime(filePath) {
|
|
86
|
+
return MIME_BY_EXT[path_1.default.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
87
|
+
}
|
|
88
|
+
function sourcePathLabel(root, sourcePath) {
|
|
89
|
+
const relative = path_1.default.relative(root, sourcePath);
|
|
90
|
+
if (!relative.startsWith("..") && !path_1.default.isAbsolute(relative)) {
|
|
91
|
+
return toPosixPath(relative);
|
|
92
|
+
}
|
|
93
|
+
return `external:${path_1.default.basename(sourcePath)}`;
|
|
94
|
+
}
|
|
95
|
+
function nextArchiveId(root, ws, basename, existingIds) {
|
|
96
|
+
const base = `archive.${slugify(basename.replace(/\.[^.]+$/, ""))}`;
|
|
97
|
+
if (!existingIds.has(`${ws}:${base}`)) {
|
|
98
|
+
return base;
|
|
99
|
+
}
|
|
100
|
+
for (let index = 2; index < 1000; index += 1) {
|
|
101
|
+
const candidate = `${base}-${index}`;
|
|
102
|
+
if (!existingIds.has(`${ws}:${candidate}`)) {
|
|
103
|
+
return candidate;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw new errors_1.UsageError(`unable to choose archive id for ${basename} in ${root}`);
|
|
107
|
+
}
|
|
108
|
+
function archiveNodeReceipt(root, node) {
|
|
109
|
+
return {
|
|
110
|
+
workspace: node.ws,
|
|
111
|
+
id: node.id,
|
|
112
|
+
qid: node.qid,
|
|
113
|
+
path: node.path,
|
|
114
|
+
archive_uri: `archive://${node.id}`,
|
|
115
|
+
stored_path: String(node.attributes.stored_path ?? ""),
|
|
116
|
+
compressed_path: String(node.attributes.compressed_path ?? ""),
|
|
117
|
+
sha256: String(node.attributes.sha256 ?? ""),
|
|
118
|
+
compressed_sha256: String(node.attributes.compressed_sha256 ?? ""),
|
|
119
|
+
visibility: (0, visibility_1.normalizeVisibility)(typeof node.attributes.visibility === "string" ? node.attributes.visibility : undefined, "archive visibility"),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function maybeReindex(root) {
|
|
123
|
+
const config = (0, config_1.loadConfig)(root);
|
|
124
|
+
if (!config.index.auto_reindex) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const outputPath = path_1.default.resolve(root, config.index.global_index_path);
|
|
128
|
+
(0, index_cache_1.writeIndex)(outputPath, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
|
|
129
|
+
}
|
|
130
|
+
function resolveArchiveNode(root, id, ws) {
|
|
131
|
+
const config = (0, config_1.loadConfig)(root);
|
|
132
|
+
const { index } = (0, index_cache_1.loadIndex)({ root, config });
|
|
133
|
+
const archiveId = archiveIdFromInput(id);
|
|
134
|
+
const wsHint = normalizeWorkspace(ws);
|
|
135
|
+
const qid = archiveId.includes(":") ? archiveId : `${wsHint}:${archiveId}`;
|
|
136
|
+
const node = index.nodes[qid];
|
|
137
|
+
if (!node || node.type !== "archive") {
|
|
138
|
+
throw new errors_1.NotFoundError(`archive not found: ${id}`);
|
|
139
|
+
}
|
|
140
|
+
return node;
|
|
141
|
+
}
|
|
142
|
+
function archiveNodePaths(root, node) {
|
|
143
|
+
const sidecarPath = path_1.default.resolve(root, node.path);
|
|
144
|
+
const sidecarDir = path_1.default.dirname(sidecarPath);
|
|
145
|
+
return {
|
|
146
|
+
sidecarPath,
|
|
147
|
+
rawPath: path_1.default.resolve(sidecarDir, String(node.attributes.stored_path ?? "")),
|
|
148
|
+
zipPath: path_1.default.resolve(sidecarDir, String(node.attributes.compressed_path ?? "")),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function walkArchiveSidecars(root) {
|
|
152
|
+
if (!fs_1.default.existsSync(root)) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
const entries = fs_1.default.readdirSync(root, { withFileTypes: true });
|
|
156
|
+
const files = [];
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
if (entry.name === "source") {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const fullPath = path_1.default.join(root, entry.name);
|
|
162
|
+
if (entry.isDirectory()) {
|
|
163
|
+
files.push(...walkArchiveSidecars(fullPath));
|
|
164
|
+
}
|
|
165
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
166
|
+
files.push(fullPath);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return files.sort();
|
|
170
|
+
}
|
|
171
|
+
function stringAttribute(value) {
|
|
172
|
+
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
173
|
+
}
|
|
174
|
+
function writeArchiveSidecar(sidecarPath, frontmatter, body) {
|
|
175
|
+
const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter);
|
|
176
|
+
const content = ["---", ...lines, "---", body.trimStart()].join("\n");
|
|
177
|
+
fs_1.default.writeFileSync(sidecarPath, content.endsWith("\n") ? content : `${content}\n`, "utf8");
|
|
178
|
+
}
|
|
179
|
+
function verifyArchiveSidecar(root, ws, sidecarPath) {
|
|
180
|
+
const relativePath = toPosixPath(path_1.default.relative(root, sidecarPath));
|
|
181
|
+
let frontmatter;
|
|
182
|
+
try {
|
|
183
|
+
frontmatter = (0, frontmatter_1.parseFrontmatter)(fs_1.default.readFileSync(sidecarPath, "utf8"), sidecarPath).frontmatter;
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
return {
|
|
187
|
+
qid: `${ws}:${relativePath}`,
|
|
188
|
+
id: relativePath,
|
|
189
|
+
path: relativePath,
|
|
190
|
+
ok: false,
|
|
191
|
+
raw_present: false,
|
|
192
|
+
compressed_present: false,
|
|
193
|
+
errors: [err instanceof Error ? err.message : String(err)],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (frontmatter.type !== "archive") {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
const id = stringAttribute(frontmatter.id) ?? relativePath;
|
|
200
|
+
const result = {
|
|
201
|
+
qid: `${ws}:${id}`,
|
|
202
|
+
id,
|
|
203
|
+
path: relativePath,
|
|
204
|
+
ok: true,
|
|
205
|
+
raw_present: false,
|
|
206
|
+
compressed_present: false,
|
|
207
|
+
errors: [],
|
|
208
|
+
};
|
|
209
|
+
const sidecarDir = path_1.default.dirname(sidecarPath);
|
|
210
|
+
const sourcePath = stringAttribute(frontmatter.source_path);
|
|
211
|
+
const storedPath = stringAttribute(frontmatter.stored_path);
|
|
212
|
+
const compressedPath = stringAttribute(frontmatter.compressed_path);
|
|
213
|
+
const expectedRawHash = stringAttribute(frontmatter.sha256);
|
|
214
|
+
const expectedCompressedHash = stringAttribute(frontmatter.compressed_sha256);
|
|
215
|
+
const expectedByteSize = stringAttribute(frontmatter.byte_size);
|
|
216
|
+
for (const [key, value] of [
|
|
217
|
+
["source_path", sourcePath],
|
|
218
|
+
["stored_path", storedPath],
|
|
219
|
+
["compressed_path", compressedPath],
|
|
220
|
+
["sha256", expectedRawHash],
|
|
221
|
+
["compressed_sha256", expectedCompressedHash],
|
|
222
|
+
["byte_size", expectedByteSize],
|
|
223
|
+
]) {
|
|
224
|
+
if (!value) {
|
|
225
|
+
result.errors.push(`${key} is required`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (result.errors.length > 0) {
|
|
229
|
+
result.ok = false;
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
const checked = (0, archive_integrity_1.checkArchiveIntegrity)({
|
|
233
|
+
root,
|
|
234
|
+
rawPath: path_1.default.resolve(sidecarDir, storedPath),
|
|
235
|
+
zipPath: path_1.default.resolve(sidecarDir, compressedPath),
|
|
236
|
+
expectedRawHash: expectedRawHash,
|
|
237
|
+
expectedCompressedHash: expectedCompressedHash,
|
|
238
|
+
expectedByteSize: expectedByteSize,
|
|
239
|
+
});
|
|
240
|
+
result.raw_present = checked.raw_present;
|
|
241
|
+
result.compressed_present = checked.compressed_present;
|
|
242
|
+
result.errors = checked.errors;
|
|
243
|
+
result.ok = checked.ok;
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
function loadArchiveVerifyResults(options) {
|
|
247
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
248
|
+
const wsFilter = options.ws ? normalizeWorkspace(options.ws) : undefined;
|
|
249
|
+
if (wsFilter && !config.workspaces[wsFilter]) {
|
|
250
|
+
throw new errors_1.NotFoundError(`workspace not found: ${wsFilter}`);
|
|
251
|
+
}
|
|
252
|
+
const idFilter = options.id ? archiveIdFromInput(options.id) : undefined;
|
|
253
|
+
const results = [];
|
|
254
|
+
for (const alias of Object.keys(config.workspaces).sort()) {
|
|
255
|
+
if (wsFilter && alias !== wsFilter) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const workspace = config.workspaces[alias];
|
|
259
|
+
if (!workspace.enabled) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const archiveRoot = path_1.default.resolve(options.root, workspace.path, workspace.mdkg_dir, "archive");
|
|
263
|
+
for (const sidecarPath of walkArchiveSidecars(archiveRoot)) {
|
|
264
|
+
const result = verifyArchiveSidecar(options.root, alias, sidecarPath);
|
|
265
|
+
if (!result) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (idFilter && result.id !== idFilter) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
results.push(result);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (idFilter && results.length === 0) {
|
|
275
|
+
throw new errors_1.NotFoundError(`archive not found: ${options.id}`);
|
|
276
|
+
}
|
|
277
|
+
return results;
|
|
278
|
+
}
|
|
279
|
+
function runArchiveAddCommand(options) {
|
|
280
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
281
|
+
const ws = normalizeWorkspace(options.ws);
|
|
282
|
+
const workspace = config.workspaces[ws];
|
|
283
|
+
if (!workspace) {
|
|
284
|
+
throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
|
|
285
|
+
}
|
|
286
|
+
const sourcePath = path_1.default.resolve(options.root, options.file);
|
|
287
|
+
if (!fs_1.default.existsSync(sourcePath) || !fs_1.default.statSync(sourcePath).isFile()) {
|
|
288
|
+
throw new errors_1.NotFoundError(`archive source file not found: ${options.file}`);
|
|
289
|
+
}
|
|
290
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
291
|
+
const basename = path_1.default.basename(sourcePath);
|
|
292
|
+
const id = options.id
|
|
293
|
+
? normalizeArchiveId(options.id)
|
|
294
|
+
: nextArchiveId(options.root, ws, basename, new Set(Object.keys(index.nodes)));
|
|
295
|
+
const qid = `${ws}:${id}`;
|
|
296
|
+
if (index.nodes[qid]) {
|
|
297
|
+
throw new errors_1.UsageError(`archive already exists: ${qid}`);
|
|
298
|
+
}
|
|
299
|
+
const archiveKind = normalizeArchiveKind(options.kind);
|
|
300
|
+
const visibility = (0, visibility_1.normalizeVisibility)(options.visibility);
|
|
301
|
+
const today = (0, date_1.formatDate)(options.now ?? new Date());
|
|
302
|
+
const archiveDir = path_1.default.resolve(options.root, workspace.path, workspace.mdkg_dir, "archive", id);
|
|
303
|
+
const rawDir = path_1.default.join(archiveDir, "source");
|
|
304
|
+
const rawPath = path_1.default.join(rawDir, basename);
|
|
305
|
+
const zipPath = path_1.default.join(archiveDir, `${basename}.zip`);
|
|
306
|
+
const sidecarPath = path_1.default.join(archiveDir, `${basename}.md`);
|
|
307
|
+
if (fs_1.default.existsSync(sidecarPath)) {
|
|
308
|
+
throw new errors_1.UsageError(`archive sidecar already exists: ${path_1.default.relative(options.root, sidecarPath)}`);
|
|
309
|
+
}
|
|
310
|
+
fs_1.default.mkdirSync(rawDir, { recursive: true });
|
|
311
|
+
fs_1.default.copyFileSync(sourcePath, rawPath);
|
|
312
|
+
const rawData = fs_1.default.readFileSync(rawPath);
|
|
313
|
+
const zipData = (0, zip_1.createDeterministicZip)(basename, rawData);
|
|
314
|
+
fs_1.default.writeFileSync(zipPath, zipData);
|
|
315
|
+
const frontmatter = {
|
|
316
|
+
id,
|
|
317
|
+
type: "archive",
|
|
318
|
+
title: options.title ?? basename,
|
|
319
|
+
archive_kind: archiveKind,
|
|
320
|
+
source_path: sourcePathLabel(options.root, sourcePath),
|
|
321
|
+
stored_path: toPosixPath(path_1.default.relative(archiveDir, rawPath)),
|
|
322
|
+
compressed_path: toPosixPath(path_1.default.relative(archiveDir, zipPath)),
|
|
323
|
+
mime_type: inferMime(sourcePath),
|
|
324
|
+
byte_size: String(rawData.length),
|
|
325
|
+
sha256: (0, archive_integrity_1.hashArchiveBuffer)(rawData),
|
|
326
|
+
compressed_sha256: (0, archive_integrity_1.hashArchiveBuffer)(zipData),
|
|
327
|
+
visibility,
|
|
328
|
+
provenance: "local-copy",
|
|
329
|
+
ingest_status: "compressed",
|
|
330
|
+
tags: [],
|
|
331
|
+
owners: [],
|
|
332
|
+
links: [],
|
|
333
|
+
artifacts: [],
|
|
334
|
+
relates: parseCsvList(options.relates),
|
|
335
|
+
refs: parseCsvList(options.refs),
|
|
336
|
+
aliases: [],
|
|
337
|
+
created: today,
|
|
338
|
+
updated: today,
|
|
339
|
+
};
|
|
340
|
+
writeArchiveSidecar(sidecarPath, frontmatter, ["# Archive Entry", "", `Archived ${archiveKind}: ${basename}`, "", "# Provenance", "", "Copied from local workspace input."].join("\n"));
|
|
341
|
+
maybeReindex(options.root);
|
|
342
|
+
(0, event_support_1.appendAutomaticEvent)({
|
|
343
|
+
root: options.root,
|
|
344
|
+
ws,
|
|
345
|
+
kind: "ARCHIVE_ADDED",
|
|
346
|
+
status: "ok",
|
|
347
|
+
refs: [id],
|
|
348
|
+
artifacts: [`archive://${id}`],
|
|
349
|
+
notes: "archive sidecar created via mdkg archive add",
|
|
350
|
+
now: options.now,
|
|
351
|
+
});
|
|
352
|
+
const receipt = {
|
|
353
|
+
workspace: ws,
|
|
354
|
+
id,
|
|
355
|
+
qid,
|
|
356
|
+
path: toPosixPath(path_1.default.relative(options.root, sidecarPath)),
|
|
357
|
+
archive_uri: `archive://${id}`,
|
|
358
|
+
stored_path: toPosixPath(path_1.default.relative(options.root, rawPath)),
|
|
359
|
+
compressed_path: toPosixPath(path_1.default.relative(options.root, zipPath)),
|
|
360
|
+
sha256: String(frontmatter.sha256),
|
|
361
|
+
compressed_sha256: String(frontmatter.compressed_sha256),
|
|
362
|
+
visibility,
|
|
363
|
+
};
|
|
364
|
+
if (options.json) {
|
|
365
|
+
console.log(JSON.stringify({ action: "created", archive: receipt }, null, 2));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
console.log(`archive created: ${receipt.qid} (${receipt.path})`);
|
|
369
|
+
}
|
|
370
|
+
function runArchiveListCommand(options) {
|
|
371
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
372
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
373
|
+
const ws = options.ws ? normalizeWorkspace(options.ws) : undefined;
|
|
374
|
+
const kind = options.kind ? normalizeArchiveKind(options.kind) : undefined;
|
|
375
|
+
const visibility = options.visibility
|
|
376
|
+
? (0, visibility_1.normalizeVisibility)(options.visibility)
|
|
377
|
+
: undefined;
|
|
378
|
+
const items = Object.values(index.nodes)
|
|
379
|
+
.filter((node) => node.type === "archive")
|
|
380
|
+
.filter((node) => !ws || node.ws === ws)
|
|
381
|
+
.filter((node) => !kind || node.attributes.archive_kind === kind)
|
|
382
|
+
.filter((node) => !visibility || node.attributes.visibility === visibility)
|
|
383
|
+
.sort((a, b) => a.qid.localeCompare(b.qid))
|
|
384
|
+
.map((node) => archiveNodeReceipt(options.root, node));
|
|
385
|
+
if (options.json) {
|
|
386
|
+
console.log(JSON.stringify({ kind: "archive", count: items.length, items }, null, 2));
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
for (const item of items) {
|
|
390
|
+
console.log(`${item.qid} | ${item.archive_uri} | ${item.path}`);
|
|
391
|
+
}
|
|
392
|
+
console.error(`count: ${items.length}`);
|
|
393
|
+
}
|
|
394
|
+
function runArchiveShowCommand(options) {
|
|
395
|
+
const node = resolveArchiveNode(options.root, options.id, options.ws);
|
|
396
|
+
const receipt = archiveNodeReceipt(options.root, node);
|
|
397
|
+
if (options.json) {
|
|
398
|
+
console.log(JSON.stringify({ kind: "archive", item: receipt, attributes: node.attributes }, null, 2));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
console.log(`${node.qid} | archive | ${node.title}`);
|
|
402
|
+
console.log(`path: ${node.path}`);
|
|
403
|
+
console.log(`archive_uri: archive://${node.id}`);
|
|
404
|
+
for (const [key, value] of Object.entries(node.attributes)) {
|
|
405
|
+
console.log(`${key}: ${Array.isArray(value) ? value.join(", ") : value}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function runArchiveVerifyCommand(options) {
|
|
409
|
+
const results = loadArchiveVerifyResults(options);
|
|
410
|
+
const ok = results.every((result) => result.ok);
|
|
411
|
+
if (options.json) {
|
|
412
|
+
console.log(JSON.stringify({ ok, count: results.length, results }, null, 2));
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
for (const result of results) {
|
|
416
|
+
console.log(`${result.ok ? "ok" : "failed"}: ${result.qid}`);
|
|
417
|
+
for (const error of result.errors) {
|
|
418
|
+
console.log(` - ${error}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (!ok) {
|
|
423
|
+
throw new errors_1.ValidationError("archive verification failed");
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function runArchiveCompressCommand(options) {
|
|
427
|
+
if (!options.all && !options.id) {
|
|
428
|
+
throw new errors_1.UsageError("archive compress requires <id-or-archive-uri> or --all");
|
|
429
|
+
}
|
|
430
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
431
|
+
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
432
|
+
const nodes = options.all
|
|
433
|
+
? Object.values(index.nodes).filter((node) => node.type === "archive")
|
|
434
|
+
: [resolveArchiveNode(options.root, String(options.id), options.ws)];
|
|
435
|
+
const updated = [];
|
|
436
|
+
const today = (0, date_1.formatDate)(options.now ?? new Date());
|
|
437
|
+
for (const node of nodes) {
|
|
438
|
+
const { sidecarPath, rawPath, zipPath } = archiveNodePaths(options.root, node);
|
|
439
|
+
if (!fs_1.default.existsSync(rawPath)) {
|
|
440
|
+
throw new errors_1.NotFoundError(`raw archive file missing for ${node.qid}: ${path_1.default.relative(options.root, rawPath)}`);
|
|
441
|
+
}
|
|
442
|
+
const rawData = fs_1.default.readFileSync(rawPath);
|
|
443
|
+
const zipData = (0, zip_1.createDeterministicZip)(path_1.default.basename(rawPath), rawData);
|
|
444
|
+
fs_1.default.writeFileSync(zipPath, zipData);
|
|
445
|
+
const parsed = (0, frontmatter_1.parseFrontmatter)(fs_1.default.readFileSync(sidecarPath, "utf8"), sidecarPath);
|
|
446
|
+
const nextFrontmatter = {
|
|
447
|
+
...parsed.frontmatter,
|
|
448
|
+
byte_size: String(rawData.length),
|
|
449
|
+
sha256: (0, archive_integrity_1.hashArchiveBuffer)(rawData),
|
|
450
|
+
compressed_sha256: (0, archive_integrity_1.hashArchiveBuffer)(zipData),
|
|
451
|
+
ingest_status: "compressed",
|
|
452
|
+
updated: today,
|
|
453
|
+
};
|
|
454
|
+
writeArchiveSidecar(sidecarPath, nextFrontmatter, parsed.body);
|
|
455
|
+
updated.push({
|
|
456
|
+
workspace: node.ws,
|
|
457
|
+
id: node.id,
|
|
458
|
+
qid: node.qid,
|
|
459
|
+
path: node.path,
|
|
460
|
+
archive_uri: `archive://${node.id}`,
|
|
461
|
+
stored_path: String(nextFrontmatter.stored_path ?? ""),
|
|
462
|
+
compressed_path: String(nextFrontmatter.compressed_path ?? ""),
|
|
463
|
+
sha256: String(nextFrontmatter.sha256),
|
|
464
|
+
compressed_sha256: String(nextFrontmatter.compressed_sha256),
|
|
465
|
+
visibility: (0, visibility_1.normalizeVisibility)(typeof nextFrontmatter.visibility === "string" ? nextFrontmatter.visibility : undefined, "archive visibility"),
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
maybeReindex(options.root);
|
|
469
|
+
if (options.json) {
|
|
470
|
+
console.log(JSON.stringify({ action: "compressed", count: updated.length, archives: updated }, null, 2));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
console.log(`archive compressed: ${updated.length}`);
|
|
474
|
+
}
|