UNPKG

@kya-os/cli

Version:

CLI for MCP-I setup and management

184 lines • 7.88 kB
import chalk from "chalk"; import { existsSync } from "fs"; import { join } from "path"; import { showSuccess, showError, showInfo } from "../utils/prompts.js"; import { formatReceiptTs, isDeprecatedTimestamp } from "../utils/time.js"; /** * List and manage stored receipts */ export async function receipts(options = {}) { const { json = false, path: receiptPath = ".mcpi/receipts", verify = false, } = options; if (!json) { console.log(chalk.cyan("\nšŸ“‹ Receipt Management\n")); } if (!existsSync(receiptPath)) { const message = "No receipts found. Receipts are saved after registration and claim operations."; if (json) { console.log(JSON.stringify({ success: true, receipts: [], message }, null, 2)); } else { showInfo(message); } return; } try { const fs = await import("fs/promises"); const files = await fs.readdir(receiptPath); const receiptFiles = files.filter((f) => f.endsWith(".json")); if (receiptFiles.length === 0) { const message = "No receipt files found in the receipts directory."; if (json) { console.log(JSON.stringify({ success: true, receipts: [], message }, null, 2)); } else { showInfo(message); } return; } const receipts = []; for (const file of receiptFiles) { try { const content = await fs.readFile(join(receiptPath, file), "utf-8"); const receipt = JSON.parse(content); // Basic validation let valid = true; let error; if (!receipt.ref || !receipt.contentHash || !receipt.action) { valid = false; error = "Missing required fields"; } else if (!receipt.contentHash.match(/^sha256:[a-f0-9]{64}$/)) { valid = false; error = "Invalid content hash format"; } else if (!["issue", "revoke"].includes(receipt.action)) { valid = false; error = "Invalid action type"; } receipts.push({ ...receipt, filename: file, valid, error, }); } catch (error) { receipts.push({ filename: file, valid: false, error: "Invalid JSON format", }); } } if (json) { const receiptData = await Promise.all(receipts.map(async (r) => ({ filename: r.filename, ref: r.ref, contentHash: r.contentHash, action: r.action, timestamp: r.ts, logIndex: r.logIndex, valid: r.valid, error: r.error, verified: verify ? await verifyReceipt(r) : undefined, }))); const output = { success: true, receipts: receiptData, total: receipts.length, valid: receipts.filter((r) => r.valid).length, invalid: receipts.filter((r) => !r.valid).length, }; console.log(JSON.stringify(output, null, 2)); } else { console.log(`${chalk.bold("Found receipts:")} ${chalk.gray(`(${receipts.length} total)`)}`); const validReceipts = receipts.filter((r) => r.valid); const invalidReceipts = receipts.filter((r) => !r.valid); if (validReceipts.length > 0) { console.log(`\n${chalk.bold.green("āœ“ Valid Receipts:")}`); for (const receipt of validReceipts) { console.log(`\n${chalk.bold("šŸ“„")} ${chalk.cyan(receipt.filename)}`); console.log(` Reference: ${chalk.gray(receipt.ref)}`); console.log(` Action: ${chalk.green(receipt.action)}`); console.log(` Content Hash: ${chalk.gray(receipt.contentHash)}`); console.log(` Timestamp: ${chalk.gray(formatReceiptTs(receipt.ts))}`); console.log(` Log Index: ${chalk.gray(receipt.logIndex)}`); if (isDeprecatedTimestamp(receipt.ts)) { console.log(` ${chalk.yellow("Warning: Receipt uses deprecated numeric timestamp format.")}`); console.log(` ${chalk.gray("Future versions will require ISO 8601 format.")}`); } if (verify) { const verification = await verifyReceipt(receipt); const verifyColor = verification.valid ? chalk.green : chalk.red; console.log(` Verification: ${verifyColor(verification.valid ? "āœ“ Valid" : "āœ— Invalid")}`); if (!verification.valid && verification.error) { console.log(` Error: ${chalk.red(verification.error)}`); } } } } if (invalidReceipts.length > 0) { console.log(`\n${chalk.bold.red("āœ— Invalid Receipts:")}`); for (const receipt of invalidReceipts) { console.log(`\n${chalk.bold("šŸ“„")} ${chalk.red(receipt.filename)}`); console.log(` Error: ${chalk.red(receipt.error || "Unknown error")}`); } } if (validReceipts.length > 0) { showSuccess(`Found ${validReceipts.length} valid receipt${validReceipts.length === 1 ? "" : "s"}`); } if (invalidReceipts.length > 0) { showError(`Found ${invalidReceipts.length} invalid receipt${invalidReceipts.length === 1 ? "" : "s"}`); } if (!verify && validReceipts.length > 0) { console.log(`\n${chalk.gray("Use --verify to check receipt integrity against KTA log")}`); } } } catch (error) { const errorMessage = `Failed to read receipts: ${error instanceof Error ? error.message : "Unknown error"}`; if (json) { console.log(JSON.stringify({ success: false, error: errorMessage, code: "XMCP_I_ERECEIPTS", }, null, 2)); } else { showError(errorMessage); } } } /** * Verify a receipt against the KTA log */ async function verifyReceipt(receipt) { try { // TODO: Implement actual receipt verification against KTA log root // This is a placeholder implementation // Basic format validation if (!receipt.logRoot || !receipt.inclusionProof || !Array.isArray(receipt.inclusionProof)) { return { valid: false, error: "Missing log root or inclusion proof" }; } // Mock verification - in real implementation, this would: // 1. Fetch current log root from KTA // 2. Verify inclusion proof against the log root // 3. Check that the content hash matches the receipt // For now, just return valid for well-formed receipts const isWellFormed = receipt.logRoot.length > 0 && receipt.inclusionProof.length > 0; return { valid: isWellFormed, error: isWellFormed ? undefined : "Malformed receipt structure", }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : "Verification failed", }; } } //# sourceMappingURL=receipts.js.map