|
| 1 | +#!/usr/bin/env node |
| 2 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 3 | +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 4 | + |
| 5 | +// Cartridge Minter — Package, sign, and version cartridges |
| 6 | + |
| 7 | +import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'; |
| 8 | +import { join, dirname } from 'node:path'; |
| 9 | +import { exec } from 'node:child_process'; |
| 10 | +import { promisify } from 'node:util'; |
| 11 | + |
| 12 | +const execAsync = promisify(exec); |
| 13 | + |
| 14 | +async function readJsonFile(filePath) { |
| 15 | + const content = await readFile(filePath, 'utf8'); |
| 16 | + return JSON.parse(content); |
| 17 | +} |
| 18 | + |
| 19 | +async function writeJsonFile(filePath, data) { |
| 20 | + await writeFile(filePath, JSON.stringify(data, null, 2), 'utf8'); |
| 21 | +} |
| 22 | + |
| 23 | +async function ensureDirectoryExists(directory) { |
| 24 | + await mkdir(directory, { recursive: true }); |
| 25 | +} |
| 26 | + |
| 27 | +async function mintCartridge(cartridgeDir, outputDir, options = {}) { |
| 28 | + const { sign = false, version = '0.1.0' } = options; |
| 29 | + |
| 30 | + // Read cartridge.json |
| 31 | + const cartridgeJsonPath = join(cartridgeDir, 'cartridge.json'); |
| 32 | + const cartridge = await readJsonFile(cartridgeJsonPath); |
| 33 | + |
| 34 | + // Update version |
| 35 | + cartridge.version = version; |
| 36 | + |
| 37 | + // Generate A2ML Manifest |
| 38 | + const a2mlManifest = { |
| 39 | + spdx: 'PMPL-1.0-or-later', |
| 40 | + name: cartridge.name, |
| 41 | + version: cartridge.version, |
| 42 | + description: cartridge.description, |
| 43 | + domain: cartridge.domain, |
| 44 | + tier: cartridge.tier, |
| 45 | + protocols: cartridge.protocols, |
| 46 | + tools: cartridge.tools, |
| 47 | + }; |
| 48 | + |
| 49 | + // Generate panll Descriptor |
| 50 | + const panllDescriptor = { |
| 51 | + name: cartridge.name, |
| 52 | + version: cartridge.version, |
| 53 | + description: cartridge.description, |
| 54 | + domain: cartridge.domain, |
| 55 | + tier: cartridge.tier, |
| 56 | + protocols: cartridge.protocols, |
| 57 | + tools: cartridge.tools, |
| 58 | + }; |
| 59 | + |
| 60 | + // Ensure output directory exists |
| 61 | + await ensureDirectoryExists(outputDir); |
| 62 | + |
| 63 | + // Write A2ML Manifest |
| 64 | + const a2mlManifestPath = join(outputDir, `${cartridge.name}.a2ml.json`); |
| 65 | + await writeJsonFile(a2mlManifestPath, a2mlManifest); |
| 66 | + |
| 67 | + // Write panll Descriptor |
| 68 | + const panllDescriptorPath = join(outputDir, `${cartridge.name}.panll.json`); |
| 69 | + await writeJsonFile(panllDescriptorPath, panllDescriptor); |
| 70 | + |
| 71 | + // Sign the cartridge (placeholder for actual signing logic) |
| 72 | + if (sign) { |
| 73 | + console.log('Signing cartridge (placeholder)...'); |
| 74 | + // In a real implementation, you would use a tool like Sigstore or GPG to sign the cartridge |
| 75 | + } |
| 76 | + |
| 77 | + return { |
| 78 | + cartridge, |
| 79 | + a2mlManifest, |
| 80 | + panllDescriptor, |
| 81 | + a2mlManifestPath, |
| 82 | + panllDescriptorPath, |
| 83 | + }; |
| 84 | +} |
| 85 | + |
| 86 | +async function main() { |
| 87 | + const args = process.argv.slice(2); |
| 88 | + |
| 89 | + if (args.length < 2) { |
| 90 | + console.error('Usage: node minter.js <cartridge-dir> <output-dir> [--sign] [--version <version>]'); |
| 91 | + process.exit(1); |
| 92 | + } |
| 93 | + |
| 94 | + const cartridgeDir = args[0]; |
| 95 | + const outputDir = args[1]; |
| 96 | + const sign = args.includes('--sign'); |
| 97 | + const versionIndex = args.indexOf('--version'); |
| 98 | + const version = versionIndex !== -1 ? args[versionIndex + 1] : '0.1.0'; |
| 99 | + |
| 100 | + try { |
| 101 | + console.log(`Minting cartridge from ${cartridgeDir}...`); |
| 102 | + const result = await mintCartridge(cartridgeDir, outputDir, { sign, version }); |
| 103 | + |
| 104 | + console.log('Cartridge minted successfully:'); |
| 105 | + console.log(` Name: ${result.cartridge.name}`); |
| 106 | + console.log(` Version: ${result.cartridge.version}`); |
| 107 | + console.log(` A2ML Manifest: ${result.a2mlManifestPath}`); |
| 108 | + console.log(` panll Descriptor: ${result.panllDescriptorPath}`); |
| 109 | + |
| 110 | + if (sign) { |
| 111 | + console.log(' Signed: Yes'); |
| 112 | + } |
| 113 | + } catch (error) { |
| 114 | + console.error('Error minting cartridge:', error); |
| 115 | + process.exit(1); |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +if (import.meta.url === `file://${process.argv[1]}`) { |
| 120 | + main(); |
| 121 | +} |
| 122 | + |
| 123 | +export { mintCartridge }; |
0 commit comments