@majikah/majik-signature 0.7.2 → 0.8.1

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/README.md CHANGED
@@ -11,44 +11,132 @@ Beyond signing raw bytes, Majik Signature can **embed signatures directly into a
11
11
 
12
12
  ## Table of Contents
13
13
 
14
- - [Why Majik Signature](#why-majik-signature)
15
- - [Security Architecture](#security-architecture)
16
- - [Overview](#overview)
17
- - [Features](#features)
18
- - [Installation](#installation)
19
- - [Quick Start](#quick-start)
20
- - [Signing Raw Content](#signing-raw-content)
21
- - [File Embedding](#file-embedding)
22
- - [Detached Signing](#detached-signing)
23
- - [Multi-Signature Files & Allowlists](#multi-signature-files--allowlists)
24
- - [Sealing an Envelope](#sealing-an-envelope)
25
- - [Trusted Timestamps (TSA)](#trusted-timestamps-tsa)
26
- - [Batch Signing a Folder](#batch-signing-a-folder)
27
- - [Verifying Signing Order](#verifying-signing-order)
28
- - [Chain Anchoring](#chain-anchoring)
29
- - [API Reference](#api-reference)
30
- - [Content Signing](#content-signing-bytesstrings)
31
- - [File Embedding](#file-embedding-api)
32
- - [Detached Signing & MajikSignatureEnvelope](#detached-signing--majiksignatureenvelope-api)
33
- - [Multi-Signature & Allowlist](#multi-signature--allowlist-api)
34
- - [Sealing](#sealing-api)
35
- - [Trusted Timestamps](#trusted-timestamps-api)
36
- - [Batch Signing & MajikSignatureMap](#batch-signing--majiksignaturemap-api)
37
- - [Signature Order Verification](#signature-order-verification-api)
38
- - [Chain Anchoring API](#chain-anchoring-api)
39
- - [Image Stamping (Experimental)](#image-stamping-experimental)
40
- - [Serialization](#serialization)
41
- - [Supported File Formats](#supported-file-formats)
42
- - [Signature & Envelope Structure](#signature--envelope-structure)
43
- - [Binary Container Formats (MJKSIG / MJKSMAP)](#binary-container-formats-mjksig--mjksmap)
44
- - [Error Handling](#error-handling)
45
- - [Security Considerations](#security-considerations)
46
- - [The Majikah Ecosystem](#the-majikah-ecosystem)
47
- - [Related Projects](#related-projects)
48
- - [Contributing](#contributing)
49
- - [License](#license)
50
- - [Author](#author)
51
- - [Contact](#contact)
14
+ - [Majik Signature](#majik-signature)
15
+ - [Table of Contents](#table-of-contents)
16
+ - [Why Majik Signature](#why-majik-signature)
17
+ - [Security Architecture](#security-architecture)
18
+ - [1. Hybrid Dual-Algorithm Signing](#1-hybrid-dual-algorithm-signing)
19
+ - [2. Canonical Payload Binding](#2-canonical-payload-binding)
20
+ - [3. Content-Agnostic Hashing](#3-content-agnostic-hashing)
21
+ - [4. File Embedding \& Detachment Integrity](#4-file-embedding--detachment-integrity)
22
+ - [5. Multi-Party Signing, Allowlists \& Sealing](#5-multi-party-signing-allowlists--sealing)
23
+ - [6. File Versioning for Visually-Mutating Multi-Sig Workflows](#6-file-versioning-for-visually-mutating-multi-sig-workflows)
24
+ - [7. Trusted Timestamps (TSA)](#7-trusted-timestamps-tsa)
25
+ - [8. Chain Anchoring](#8-chain-anchoring)
26
+ - [Overview](#overview)
27
+ - [What is a Majik Signature?](#what-is-a-majik-signature)
28
+ - [Use Cases](#use-cases)
29
+ - [Features](#features)
30
+ - [Security \& Post-Quantum Readiness](#security--post-quantum-readiness)
31
+ - [Multi-Party Signing](#multi-party-signing)
32
+ - [Detached Signing \& Batch Workflows](#detached-signing--batch-workflows)
33
+ - [Content Format Support](#content-format-support)
34
+ - [Developer Experience](#developer-experience)
35
+ - [Serialization \& Portability](#serialization--portability)
36
+ - [Installation](#installation)
37
+ - [Command-Line Interface](#command-line-interface)
38
+ - [`mjksig` — Sign, Verify, Seal, Inspect](#mjksig--sign-verify-seal-inspect)
39
+ - [Run with `npx`](#run-with-npx)
40
+ - [CLI Documentation](#cli-documentation)
41
+ - [Quick Start](#quick-start)
42
+ - [Signing Raw Content](#signing-raw-content)
43
+ - [File Embedding](#file-embedding)
44
+ - [Detached Signing](#detached-signing)
45
+ - [Multi-Signature Files \& Allowlists](#multi-signature-files--allowlists)
46
+ - [Sealing an Envelope](#sealing-an-envelope)
47
+ - [Trusted Timestamps (TSA)](#trusted-timestamps-tsa)
48
+ - [Batch Signing a Folder](#batch-signing-a-folder)
49
+ - [Verifying Signing Order](#verifying-signing-order)
50
+ - [Chain Anchoring](#chain-anchoring)
51
+ - [File Versioning \& Multi-Sig Revisions](#file-versioning--multi-sig-revisions)
52
+ - [API Reference](#api-reference)
53
+ - [Content Signing (bytes/strings)](#content-signing-bytesstrings)
54
+ - [`MajikSignature.sign(content, key, options?)`](#majiksignaturesigncontent-key-options)
55
+ - [`MajikSignature.verify(content, signature, publicKeys)`](#majiksignatureverifycontent-signature-publickeys)
56
+ - [`MajikSignature.verifyWithKey(content, signature, key)`](#majiksignatureverifywithkeycontent-signature-key)
57
+ - [`MajikSignature.publicKeysFromMajikKey(key)`](#majiksignaturepublickeysfrommajikkeykey)
58
+ - [`signature.extractPublicKeys()` *(instance method)*](#signatureextractpublickeys-instance-method)
59
+ - [`signature.validate()` / `signature.isValid()`](#signaturevalidate--signatureisvalid)
60
+ - [`MajikSignature.fromJSON(json)` / `MajikSignature.deserialize(base64)`](#majiksignaturefromjsonjson--majiksignaturedeserializebase64)
61
+ - [File Embedding API](#file-embedding-api)
62
+ - [`MajikSignature.signFile(file, key, options?)`](#majiksignaturesignfilefile-key-options)
63
+ - [`MajikSignature.verifyFile(file, keyOrPublicKeys, options?)`](#majiksignatureverifyfilefile-keyorpublickeys-options)
64
+ - [`signature.embedIn(file, options?)` *(instance method)*](#signatureembedinfile-options-instance-method)
65
+ - [`MajikSignature.extractFrom(file, options?)`](#majiksignatureextractfromfile-options)
66
+ - [`MajikSignature.stripFrom(file, options?)`](#majiksignaturestripfromfile-options)
67
+ - [`MajikSignature.isSigned(file, options?)`](#majiksignatureissignedfile-options)
68
+ - [Detached Signing \& MajikSignatureEnvelope API](#detached-signing--majiksignatureenvelope-api)
69
+ - [`MajikSignature.signFileDetached(file, key, options?)`](#majiksignaturesignfiledetachedfile-key-options)
70
+ - [`MajikSignature.verifyFileDetached(file, envelope, keyOrPublicKeys, options?)`](#majiksignatureverifyfiledetachedfile-envelope-keyorpublickeys-options)
71
+ - [`MajikSignatureEnvelope`](#majiksignatureenvelope)
72
+ - [Multi-Signature \& Allowlist API](#multi-signature--allowlist-api)
73
+ - [`MajikSignature.expectedSignerFromKey(key)`](#majiksignatureexpectedsignerfromkeykey)
74
+ - [`MajikSignature.getAllowlist(file, options?)`](#majiksignaturegetallowlistfile-options)
75
+ - [`MajikSignature.canSign(file, key, options?)`](#majiksignaturecansignfile-key-options)
76
+ - [`MajikSignature.getSignatories(file, options?, filter?)`](#majiksignaturegetsignatoriesfile-options-filter)
77
+ - [`MajikSignature.getIssuer(file, options?)`](#majiksignaturegetissuerfile-options)
78
+ - [`MajikSignature.getEnvelopeInfo(file, options?)`](#majiksignaturegetenvelopeinfofile-options)
79
+ - [`MajikSignature.isMultiSig(file, options?)`](#majiksignatureismultisigfile-options)
80
+ - [Sealing API](#sealing-api)
81
+ - [`MajikSignature.seal(file, key, options?)`](#majiksignaturesealfile-key-options)
82
+ - [`MajikSignature.verifySeal(file, options?)`](#majiksignatureverifysealfile-options)
83
+ - [`MajikSignature.getSealInfo(file, options?)`](#majiksignaturegetsealinfofile-options)
84
+ - [`MajikSignature.isSealed(file, options?)`](#majiksignatureissealedfile-options)
85
+ - [Trusted Timestamps API](#trusted-timestamps-api)
86
+ - [`signature.buildTSARequestPayload()`](#signaturebuildtsarequestpayload)
87
+ - [`MajikSignature.signTSA(request, key, tsa, options?)`](#majiksignaturesigntsarequest-key-tsa-options)
88
+ - [`signature.addTSA(timestamp)`](#signatureaddtsatimestamp)
89
+ - [`signature.verifyTSA()`](#signatureverifytsa)
90
+ - [`signature.hasTSA`](#signaturehastsa)
91
+ - [Batch Signing \& MajikSignatureMap API](#batch-signing--majiksignaturemap-api)
92
+ - [`MajikSignature.signBatchDetached(files, key, options?)`](#majiksignaturesignbatchdetachedfiles-key-options)
93
+ - [`MajikSignature.verifyFilesFromMjksMap(map, files, publicKeys, options?)`](#majiksignatureverifyfilesfrommjksmapmap-files-publickeys-options)
94
+ - [`MajikSignature.verifyFilesFromMjksMapWithKey(map, files, key, options?)`](#majiksignatureverifyfilesfrommjksmapwithkeymap-files-key-options)
95
+ - [`MajikSignature.summarizeBatchVerification(results)`](#majiksignaturesummarizebatchverificationresults)
96
+ - [`MajikSignatureMap`](#majiksignaturemap)
97
+ - [Signature Order Verification API](#signature-order-verification-api)
98
+ - [`MajikSignature.verifyFileOrder(file, expectedOrder, options?)`](#majiksignatureverifyfileorderfile-expectedorder-options)
99
+ - [`MajikSignature.verifyFileDetachedOrder(file, envelope, expectedOrder, options?)`](#majiksignatureverifyfiledetachedorderfile-envelope-expectedorder-options)
100
+ - [`MajikSignature.normalizeExpectedOrder(expectedOrder)`](#majiksignaturenormalizeexpectedorderexpectedorder)
101
+ - [Chain Anchoring API (Experimental)](#chain-anchoring-api-experimental)
102
+ - [`MajikSignature.canAnchor(file, options?)`](#majiksignaturecananchorfile-options)
103
+ - [`MajikSignature.buildChainAnchorMemo(sealHash)`](#majiksignaturebuildchainanchormemosealhash)
104
+ - [`MajikSignature.registerChainAnchor(file, anchor, options?)`](#majiksignatureregisterchainanchorfile-anchor-options)
105
+ - [`MajikSignature.getChainAnchors(file, options?)`](#majiksignaturegetchainanchorsfile-options)
106
+ - [File Versioning API](#file-versioning-api)
107
+ - [`MajikSignature.signFile(file, key, options?)` — versioning-relevant options](#majiksignaturesignfilefile-key-options--versioning-relevant-options)
108
+ - [`MajikSignature.verifyFileChain(file, options?)`](#majiksignatureverifyfilechainfile-options)
109
+ - [`MajikSignature.verifyFileRevisions(finalFile, revisions, options?)`](#majiksignatureverifyfilerevisionsfinalfile-revisions-options)
110
+ - [`MajikSignature.verifyCommitment(signature, publicKeys, now?)`](#majiksignatureverifycommitmentsignature-publickeys-now)
111
+ - [Image Stamping (Experimental)](#image-stamping-experimental)
112
+ - [Serialization](#serialization)
113
+ - [`signature.toJSON()` / `MajikSignature.fromJSON(json)`](#signaturetojson--majiksignaturefromjsonjson)
114
+ - [`signature.serialize()` / `MajikSignature.deserialize(base64)`](#signatureserialize--majiksignaturedeserializebase64)
115
+ - [`envelope.toJSON()` / `MajikSignatureEnvelope.fromJSON(json)`](#envelopetojson--majiksignatureenvelopefromjsonjson)
116
+ - [`envelope.serialize()` / `MajikSignatureEnvelope.deserialize(base64)`](#envelopeserialize--majiksignatureenvelopedeserializebase64)
117
+ - [`envelope.toMJKSIG()` / `MajikSignatureEnvelope.fromMJKSIG(input)`](#envelopetomjksig--majiksignatureenvelopefrommjksiginput)
118
+ - [Supported File Formats](#supported-file-formats)
119
+ - [Signature \& Envelope Structure](#signature--envelope-structure)
120
+ - [Binary Container Formats (MJKSIG / MJKSMAP)](#binary-container-formats-mjksig--mjksmap)
121
+ - [Error Handling](#error-handling)
122
+ - [Security Considerations](#security-considerations)
123
+ - [What is Guaranteed](#what-is-guaranteed)
124
+ - [What is Your Responsibility](#what-is-your-responsibility)
125
+ - [What NOT to Do](#what-not-to-do)
126
+ - [What TO Do](#what-to-do)
127
+ - [The Majikah Ecosystem](#the-majikah-ecosystem)
128
+ - [Majik Signature — Standalone App](#majik-signature--standalone-app)
129
+ - [🧾 Majik Buwiz](#-majik-buwiz)
130
+ - [💬 Majik Message](#-majik-message)
131
+ - [🪪 Majik Universal ID](#-majik-universal-id)
132
+ - [Related Projects](#related-projects)
133
+ - [Majik Key](#majik-key)
134
+ - [Majik Message](#majik-message)
135
+ - [Contributing](#contributing)
136
+ - [License](#license)
137
+ - [Author](#author)
138
+ - [Contact](#contact)
139
+
52
140
 
53
141
  ---
54
142
 
@@ -260,6 +348,66 @@ npm install @majikah/majik-key
260
348
 
261
349
  No native bindings. Works in Node.js 18+, modern browsers, Deno, Bun, and Tauri.
262
350
 
351
+ ---
352
+
353
+ ## Command-Line Interface
354
+
355
+ ### `mjksig` — Sign, Verify, Seal, Inspect
356
+
357
+ Majik Signature includes a full-featured CLI for working with signed files directly from your terminal.
358
+
359
+ The `mjksig` CLI supports:
360
+
361
+ * Embedded and detached signing
362
+ * MJKSMAP folder signing and verification
363
+ * Multi-signature allowlists and co-signing
364
+ * Envelope sealing
365
+ * Signing-permission checks
366
+ * Signature inspection
367
+ * Batch workflows
368
+ * Machine-readable `--json` output
369
+ * Interactive and non-interactive signing-key passphrases
370
+
371
+ ### Run with `npx`
372
+
373
+ No global installation required:
374
+
375
+ ```bash
376
+ npx @majikah/majik-signature --help
377
+ ```
378
+
379
+ Sign a file:
380
+
381
+ ```bash
382
+ npx @majikah/majik-signature sign report.pdf --key alice.json
383
+ ```
384
+
385
+ Verify a file:
386
+
387
+ ```bash
388
+ npx @majikah/majik-signature verify report.pdf
389
+ ```
390
+
391
+ Or install globally and use the `mjksig` command directly:
392
+
393
+ ```bash
394
+ npm install -g @majikah/majik-signature
395
+ ```
396
+
397
+ ```bash
398
+ mjksig --help
399
+ ```
400
+
401
+ For automation and CI environments, signing-key passphrases can be supplied through `MAJIK_KEY_PASSPHRASE`.
402
+
403
+ ### CLI Documentation
404
+
405
+ For the complete command reference, workflow combinations, options, compatibility rules, examples, exit codes, automation guidance, and `npx` usage, see:
406
+
407
+
408
+ [![CLIREADME](https://img.shields.io/badge/Read_the_CLI_Documentation_Here-EA7F05?style=for-the-badge&logoColor=ea7f05)](./CLI_README.md)
409
+
410
+
263
411
  ---
264
412
 
265
413
  ## Quick Start
@@ -0,0 +1,5 @@
1
+ export type * from "./types.js";
2
+ export * from "./io.js";
3
+ export * from "./key.js";
4
+ export * from "./output.js";
5
+ export * from "./operations.js";
@@ -0,0 +1,4 @@
1
+ export * from "./io.js";
2
+ export * from "./key.js";
3
+ export * from "./output.js";
4
+ export * from "./operations.js";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * src/cli/io.ts
3
+ *
4
+ * Node.js filesystem helpers for the Majik Signature CLI.
5
+ */
6
+ import type { FolderFile } from "./types.js";
7
+ /**
8
+ * Convert a filesystem path into the normalized relative-path convention
9
+ * used by MajikSignatureMap.
10
+ */
11
+ export declare function relativePosix(root: string, target: string): string;
12
+ export declare function mimeTypeForPath(path: string): string;
13
+ export declare function isMajikArtifact(path: string): boolean;
14
+ export declare function pathExists(path: string): Promise<boolean>;
15
+ export declare function ensureDirectory(path: string): Promise<void>;
16
+ export declare function readBytes(path: string): Promise<Uint8Array>;
17
+ export declare function readText(path: string): Promise<string>;
18
+ export declare function readBlob(path: string): Promise<Blob>;
19
+ export declare function readJsonFile<T = unknown>(path: string): Promise<T>;
20
+ export declare function writeBytes(path: string, bytes: Uint8Array, overwrite: boolean): Promise<void>;
21
+ export declare function writeBlob(path: string, blob: Blob, overwrite: boolean): Promise<void>;
22
+ export declare function writeJsonFile(path: string, value: unknown, overwrite: boolean): Promise<void>;
23
+ export declare function writeText(path: string, value: string, overwrite: boolean): Promise<void>;
24
+ /**
25
+ * Read all regular files underneath a folder into memory.
26
+ *
27
+ * This is intentional for MJKSMAP operations because the CLI may need both
28
+ * the file bytes and the generated manifest/bundle during a single operation.
29
+ */
30
+ export declare function readFolderFiles(rootPath: string): Promise<FolderFile[]>;
31
+ export declare function getPathKind(inputPath: string): Promise<"file" | "directory">;
32
+ export declare function resolveCliPath(path: string): string;
33
+ export declare function outputPathForDetachedFile(inputPath: string): string;
34
+ export declare function outputPathForMap(inputDirectory: string): string;
35
+ export declare function outputPathForMapBundle(inputDirectory: string, mapPath: string): string;
36
+ export declare function getPackageVersion(): Promise<string>;
37
+ export declare function summarizeFiles(files: FolderFile[]): {
38
+ count: number;
39
+ bytes: number;
40
+ };
@@ -0,0 +1,227 @@
1
+ /**
2
+ * src/cli/io.ts
3
+ *
4
+ * Node.js filesystem helpers for the Majik Signature CLI.
5
+ */
6
+ import { access, mkdir, readFile, readdir, stat, writeFile, } from "node:fs/promises";
7
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve, } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ const MIME_TYPES = {
10
+ ".txt": "text/plain",
11
+ ".md": "text/markdown",
12
+ ".csv": "text/csv",
13
+ ".json": "application/json",
14
+ ".xml": "application/xml",
15
+ ".html": "text/html",
16
+ ".htm": "text/html",
17
+ ".pdf": "application/pdf",
18
+ ".png": "image/png",
19
+ ".jpg": "image/jpeg",
20
+ ".jpeg": "image/jpeg",
21
+ ".webp": "image/webp",
22
+ ".gif": "image/gif",
23
+ ".mp3": "audio/mpeg",
24
+ ".wav": "audio/wav",
25
+ ".flac": "audio/flac",
26
+ ".mp4": "video/mp4",
27
+ ".mkv": "video/x-matroska",
28
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
29
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
30
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
31
+ ".zip": "application/zip",
32
+ ".exe": "application/vnd.microsoft.portable-executable",
33
+ ".msi": "application/x-msi",
34
+ ".msix": "application/msix",
35
+ ".deb": "application/vnd.debian.binary-package",
36
+ ".mjksig": "application/vnd.majikah.mjksig",
37
+ ".mjksmap": "application/vnd.majikah.mjksmap",
38
+ };
39
+ const MAJIK_ARTIFACT_EXTENSIONS = new Set([".mjksig", ".mjksmap"]);
40
+ function normalizeSlashes(value) {
41
+ return value.replaceAll("\\", "/");
42
+ }
43
+ /**
44
+ * Convert a filesystem path into the normalized relative-path convention
45
+ * used by MajikSignatureMap.
46
+ */
47
+ export function relativePosix(root, target) {
48
+ return normalizeSlashes(relative(root, target)).replace(/^\/+/, "").trim();
49
+ }
50
+ export function mimeTypeForPath(path) {
51
+ return MIME_TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
52
+ }
53
+ export function isMajikArtifact(path) {
54
+ return MAJIK_ARTIFACT_EXTENSIONS.has(extname(path).toLowerCase());
55
+ }
56
+ export async function pathExists(path) {
57
+ try {
58
+ await access(path);
59
+ return true;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ export async function ensureDirectory(path) {
66
+ await mkdir(path, {
67
+ recursive: true,
68
+ });
69
+ }
70
+ export async function readBytes(path) {
71
+ return new Uint8Array(await readFile(path));
72
+ }
73
+ export async function readText(path) {
74
+ return readFile(path, "utf8");
75
+ }
76
+ export async function readBlob(path) {
77
+ const bytes = await readBytes(path);
78
+ return new Blob([bytes], {
79
+ type: mimeTypeForPath(path),
80
+ });
81
+ }
82
+ export async function readJsonFile(path) {
83
+ const text = await readText(path);
84
+ try {
85
+ return JSON.parse(text);
86
+ }
87
+ catch (error) {
88
+ throw new Error(`Invalid JSON in "${path}": ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ }
91
+ export async function writeBytes(path, bytes, overwrite) {
92
+ if (!overwrite && (await pathExists(path))) {
93
+ throw new Error(`Output already exists: "${path}". Use --overwrite to replace it.`);
94
+ }
95
+ await ensureDirectory(dirname(path));
96
+ await writeFile(path, bytes);
97
+ }
98
+ export async function writeBlob(path, blob, overwrite) {
99
+ const bytes = new Uint8Array(await blob.arrayBuffer());
100
+ await writeBytes(path, bytes, overwrite);
101
+ }
102
+ export async function writeJsonFile(path, value, overwrite) {
103
+ const json = JSON.stringify(value, null, 2) + "\n";
104
+ await writeText(path, json, overwrite);
105
+ }
106
+ export async function writeText(path, value, overwrite) {
107
+ if (!overwrite && (await pathExists(path))) {
108
+ throw new Error(`Output already exists: "${path}". Use --overwrite to replace it.`);
109
+ }
110
+ await ensureDirectory(dirname(path));
111
+ await writeFile(path, value, "utf8");
112
+ }
113
+ async function walkDirectory(root, current, output) {
114
+ const entries = await readdir(current, {
115
+ withFileTypes: true,
116
+ });
117
+ for (const entry of entries) {
118
+ const absolutePath = join(current, entry.name);
119
+ /**
120
+ * Do not follow symlinks. This avoids directory cycles and prevents a
121
+ * folder signing operation from unexpectedly escaping its input tree.
122
+ */
123
+ if (entry.isSymbolicLink()) {
124
+ continue;
125
+ }
126
+ if (entry.isDirectory()) {
127
+ await walkDirectory(root, absolutePath, output);
128
+ continue;
129
+ }
130
+ if (!entry.isFile()) {
131
+ continue;
132
+ }
133
+ /**
134
+ * Existing Majik Signature sidecars/manifests are artifacts, not source
135
+ * files for a subsequent folder operation.
136
+ */
137
+ if (isMajikArtifact(absolutePath)) {
138
+ continue;
139
+ }
140
+ const fileStat = await stat(absolutePath);
141
+ output.push({
142
+ absolutePath,
143
+ relativePath: relativePosix(root, absolutePath),
144
+ blob: await readBlob(absolutePath),
145
+ size: fileStat.size,
146
+ mimeType: mimeTypeForPath(absolutePath),
147
+ });
148
+ }
149
+ }
150
+ /**
151
+ * Read all regular files underneath a folder into memory.
152
+ *
153
+ * This is intentional for MJKSMAP operations because the CLI may need both
154
+ * the file bytes and the generated manifest/bundle during a single operation.
155
+ */
156
+ export async function readFolderFiles(rootPath) {
157
+ const root = resolve(rootPath);
158
+ const rootStat = await stat(root);
159
+ if (!rootStat.isDirectory()) {
160
+ throw new Error(`Expected a directory: "${root}"`);
161
+ }
162
+ const result = [];
163
+ await walkDirectory(root, root, result);
164
+ result.sort((a, b) => a.relativePath.localeCompare(b.relativePath, undefined, {
165
+ sensitivity: "base",
166
+ }));
167
+ return result;
168
+ }
169
+ export async function getPathKind(inputPath) {
170
+ const target = resolve(inputPath);
171
+ let fileStat;
172
+ try {
173
+ fileStat = await stat(target);
174
+ }
175
+ catch {
176
+ throw new Error(`Path does not exist: "${target}"`);
177
+ }
178
+ if (fileStat.isDirectory()) {
179
+ return "directory";
180
+ }
181
+ if (fileStat.isFile()) {
182
+ return "file";
183
+ }
184
+ throw new Error(`Unsupported filesystem object: "${target}"`);
185
+ }
186
+ export function resolveCliPath(path) {
187
+ if (isAbsolute(path)) {
188
+ return resolve(path);
189
+ }
190
+ return resolve(process.cwd(), path);
191
+ }
192
+ export function outputPathForDetachedFile(inputPath) {
193
+ return `${inputPath}.mjksig`;
194
+ }
195
+ export function outputPathForMap(inputDirectory) {
196
+ return join(inputDirectory, "signatures.mjksmap");
197
+ }
198
+ export function outputPathForMapBundle(inputDirectory, mapPath) {
199
+ const directoryName = basename(inputDirectory);
200
+ const mapName = basename(mapPath, extname(mapPath));
201
+ return join(dirname(mapPath), `${directoryName || mapName}.mjksbundle.zip`);
202
+ }
203
+ export async function getPackageVersion() {
204
+ const currentFile = fileURLToPath(import.meta.url);
205
+ /**
206
+ * dist/cli/io.js
207
+ * ↑ ../
208
+ * dist/cli
209
+ * ↑ ../
210
+ * dist
211
+ * ↑ package root
212
+ */
213
+ const packageJsonPath = resolve(dirname(currentFile), "../../package.json");
214
+ try {
215
+ const packageJson = await readJsonFile(packageJsonPath);
216
+ return packageJson.version ?? "unknown";
217
+ }
218
+ catch {
219
+ return "unknown";
220
+ }
221
+ }
222
+ export function summarizeFiles(files) {
223
+ return {
224
+ count: files.length,
225
+ bytes: files.reduce((total, file) => total + file.size, 0),
226
+ };
227
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * src/cli/key.ts
3
+ *
4
+ * Majik Key loading/unlocking for the npm CLI.
5
+ *
6
+ * The npm CLI intentionally consumes explicit JSON key backups instead of
7
+ * reaching into the desktop application's account store.
8
+ */
9
+ import { MajikKey } from "@majikah/majik-key";
10
+ export type LoadedMajikKey = MajikKey;
11
+ /**
12
+ * Read a passphrase without echoing it to the terminal.
13
+ *
14
+ * On Windows and Unix terminals we use raw TTY mode rather than relying on
15
+ * readline's normal line editor, which would echo characters.
16
+ */
17
+ export declare function promptForPassphrase(message?: string): Promise<string>;
18
+ /**
19
+ * Load a MajikKey without unlocking it.
20
+ *
21
+ * This is used by verification and allowlist construction because those
22
+ * operations only need public-key material.
23
+ */
24
+ export declare function loadMajikKey(keyPath: string): Promise<LoadedMajikKey>;
25
+ /**
26
+ * Load and unlock a signing-capable MajikKey.
27
+ *
28
+ * Passphrase resolution order:
29
+ *
30
+ * 1. MAJIK_KEY_PASSPHRASE
31
+ * 2. interactive hidden terminal prompt
32
+ */
33
+ export declare function loadSigningKey(keyPath: string): Promise<LoadedMajikKey>;
34
+ /**
35
+ * Explicitly lock a key after the operation.
36
+ */
37
+ export declare function lockKey(key: LoadedMajikKey): void;