@formigio/fazemos-cli 0.10.64 → 0.10.67

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.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * F54 — Streaming SHA-256 hash for files of any size.
3
+ *
4
+ * AC-F54-13: whole-object SHA-256 computed client-side; broker key matches.
5
+ * Niko F-2: presigned PUT binds x-amz-checksum-sha256 (base64 of raw SHA-256
6
+ * bytes); this module returns both hex and base64 so the caller can pass hex
7
+ * to the API (content_hash, no prefix) and base64 to S3 (checksum header).
8
+ *
9
+ * Files are streamed — no full-file buffering — so large files work within
10
+ * reasonable memory bounds. The resulting hash covers the entire file in one
11
+ * pass; this is the "whole-object SHA-256" required by AC-F54-13.
12
+ */
13
+ export interface FileHashResult {
14
+ /** 64-char lowercase hex — pass to API as content_hash (no sha256: prefix) */
15
+ hex: string;
16
+ /** Base64-encoded SHA-256 bytes — pass as x-amz-checksum-sha256 header to S3 */
17
+ base64: string;
18
+ /** File size in bytes — pass to API as size_bytes and to S3 as content-length */
19
+ sizeBytes: number;
20
+ }
21
+ /**
22
+ * Stream-hash a file with SHA-256 in a single pass.
23
+ * Returns hex, base64 digest, and file size.
24
+ */
25
+ export declare function hashFile(filePath: string): Promise<FileHashResult>;
26
+ /**
27
+ * Hash an in-memory Buffer with SHA-256.
28
+ * Used for verifying downloaded bytes (fazemos asset get).
29
+ */
30
+ export declare function hashBuffer(buf: Buffer): {
31
+ hex: string;
32
+ base64: string;
33
+ };
34
+ /**
35
+ * Convert a 64-char raw hex string to base64.
36
+ * Convenience wrapper for assembling the x-amz-checksum-sha256 value from a
37
+ * hex content_hash that was returned by the API or read from a pointer file.
38
+ */
39
+ export declare function hexToBase64(hex: string): string;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * F54 — Streaming SHA-256 hash for files of any size.
3
+ *
4
+ * AC-F54-13: whole-object SHA-256 computed client-side; broker key matches.
5
+ * Niko F-2: presigned PUT binds x-amz-checksum-sha256 (base64 of raw SHA-256
6
+ * bytes); this module returns both hex and base64 so the caller can pass hex
7
+ * to the API (content_hash, no prefix) and base64 to S3 (checksum header).
8
+ *
9
+ * Files are streamed — no full-file buffering — so large files work within
10
+ * reasonable memory bounds. The resulting hash covers the entire file in one
11
+ * pass; this is the "whole-object SHA-256" required by AC-F54-13.
12
+ */
13
+ import { createHash } from 'crypto';
14
+ import { createReadStream, statSync } from 'fs';
15
+ /**
16
+ * Stream-hash a file with SHA-256 in a single pass.
17
+ * Returns hex, base64 digest, and file size.
18
+ */
19
+ export function hashFile(filePath) {
20
+ const sizeBytes = statSync(filePath).size;
21
+ return new Promise((resolve, reject) => {
22
+ const hash = createHash('sha256');
23
+ const stream = createReadStream(filePath);
24
+ stream.on('data', (chunk) => {
25
+ hash.update(chunk);
26
+ });
27
+ stream.on('end', () => {
28
+ const digest = hash.digest();
29
+ resolve({
30
+ hex: digest.toString('hex'),
31
+ base64: digest.toString('base64'),
32
+ sizeBytes,
33
+ });
34
+ });
35
+ stream.on('error', reject);
36
+ });
37
+ }
38
+ /**
39
+ * Hash an in-memory Buffer with SHA-256.
40
+ * Used for verifying downloaded bytes (fazemos asset get).
41
+ */
42
+ export function hashBuffer(buf) {
43
+ const digest = createHash('sha256').update(buf).digest();
44
+ return {
45
+ hex: digest.toString('hex'),
46
+ base64: digest.toString('base64'),
47
+ };
48
+ }
49
+ /**
50
+ * Convert a 64-char raw hex string to base64.
51
+ * Convenience wrapper for assembling the x-amz-checksum-sha256 value from a
52
+ * hex content_hash that was returned by the API or read from a pointer file.
53
+ */
54
+ export function hexToBase64(hex) {
55
+ return Buffer.from(hex, 'hex').toString('base64');
56
+ }
57
+ //# sourceMappingURL=assetHash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assetHash.js","sourceRoot":"","sources":["../../src/lib/assetHash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAWhD;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IAE1C,OAAO,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAE1C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE;YAC3C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACpB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7B,OAAO,CAAC;gBACN,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC3B,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBACjC,SAAS;aACV,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IACzD,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC3B,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAClC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * F54 — Asset URI parse / validate helpers.
3
+ *
4
+ * URI format: fazemos-asset://<org_slug>/<project_slug>/<sha256_hex>
5
+ *
6
+ * AC-F54-15: resolution walks the URI, not the pointer path — a pointer rename
7
+ * is benign because we always extract the URI and use it as the source of truth.
8
+ */
9
+ export declare const ASSET_URI_PREFIX = "fazemos-asset://";
10
+ /**
11
+ * Regex that matches a valid fazemos-asset URI.
12
+ * Slug segments: 1–63 chars, lowercase alphanum and hyphen, must start/end with
13
+ * alphanum. Hash segment: exactly 64 lowercase hex chars.
14
+ */
15
+ export declare const ASSET_URI_RE: RegExp;
16
+ export interface AssetUriParts {
17
+ orgSlug: string;
18
+ projectSlug: string;
19
+ /** 64-char lowercase hex, no sha256: prefix */
20
+ hash: string;
21
+ }
22
+ /**
23
+ * Parse a fazemos-asset URI string.
24
+ * Throws a descriptive Error on mismatch (used by CLI for pre-flight rejection
25
+ * per rul_asset_store_content_hash_addressed and ec_raw_uri_pattern_mismatch).
26
+ */
27
+ export declare function parseAssetUri(uri: string): AssetUriParts;
28
+ /** Returns true when the string matches the full fazemos-asset URI pattern. */
29
+ export declare function isAssetUri(value: string): boolean;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * F54 — Asset URI parse / validate helpers.
3
+ *
4
+ * URI format: fazemos-asset://<org_slug>/<project_slug>/<sha256_hex>
5
+ *
6
+ * AC-F54-15: resolution walks the URI, not the pointer path — a pointer rename
7
+ * is benign because we always extract the URI and use it as the source of truth.
8
+ */
9
+ export const ASSET_URI_PREFIX = 'fazemos-asset://';
10
+ /**
11
+ * Regex that matches a valid fazemos-asset URI.
12
+ * Slug segments: 1–63 chars, lowercase alphanum and hyphen, must start/end with
13
+ * alphanum. Hash segment: exactly 64 lowercase hex chars.
14
+ */
15
+ export const ASSET_URI_RE = /^fazemos-asset:\/\/([a-z0-9][a-z0-9-]{0,61}[a-z0-9]?)\/([a-z0-9][a-z0-9-]{0,61}[a-z0-9]?)\/([a-f0-9]{64})$/;
16
+ /**
17
+ * Parse a fazemos-asset URI string.
18
+ * Throws a descriptive Error on mismatch (used by CLI for pre-flight rejection
19
+ * per rul_asset_store_content_hash_addressed and ec_raw_uri_pattern_mismatch).
20
+ */
21
+ export function parseAssetUri(uri) {
22
+ const m = ASSET_URI_RE.exec(uri);
23
+ if (!m) {
24
+ throw new Error(`Malformed fazemos-asset URI: "${uri}"\n` +
25
+ `Expected: fazemos-asset://<org>/<project>/<64-char-sha256-hex>\n` +
26
+ `Hint: org/project slugs are lowercase alphanum + hyphen; hash is 64 hex chars.`);
27
+ }
28
+ return { orgSlug: m[1], projectSlug: m[2], hash: m[3] };
29
+ }
30
+ /** Returns true when the string matches the full fazemos-asset URI pattern. */
31
+ export function isAssetUri(value) {
32
+ return ASSET_URI_RE.test(value);
33
+ }
34
+ //# sourceMappingURL=assetUri.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assetUri.js","sourceRoot":"","sources":["../../src/lib/assetUri.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,4GAA4G,CAAC;AAS/G;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,iCAAiC,GAAG,KAAK;YACvC,kEAAkE;YAClE,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * F54 — Extension → MIME type inference.
3
+ *
4
+ * Used by `fazemos asset put` when --content-type is not supplied.
5
+ * Falls back to application/octet-stream for unrecognised extensions.
6
+ *
7
+ * Sage §7 handoff 5 — canonical extension table for agent asset outputs.
8
+ */
9
+ /**
10
+ * Infer the MIME content-type from a filename (using its extension).
11
+ * Handles compound extensions like .tar.gz before falling through to last-dot.
12
+ * Returns 'application/octet-stream' for unknown extensions.
13
+ */
14
+ export declare function inferContentType(filename: string): string;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * F54 — Extension → MIME type inference.
3
+ *
4
+ * Used by `fazemos asset put` when --content-type is not supplied.
5
+ * Falls back to application/octet-stream for unrecognised extensions.
6
+ *
7
+ * Sage §7 handoff 5 — canonical extension table for agent asset outputs.
8
+ */
9
+ const MIME_MAP = {
10
+ // Text / markup
11
+ '.txt': 'text/plain',
12
+ '.md': 'text/markdown',
13
+ '.csv': 'text/csv',
14
+ '.html': 'text/html',
15
+ '.htm': 'text/html',
16
+ '.xml': 'text/xml',
17
+ '.css': 'text/css',
18
+ '.js': 'text/javascript',
19
+ '.mjs': 'text/javascript',
20
+ '.ts': 'text/typescript',
21
+ '.sh': 'text/x-shellscript',
22
+ '.py': 'text/x-python',
23
+ '.ini': 'text/plain',
24
+ // Application / structured data
25
+ '.json': 'application/json',
26
+ '.jsonl': 'application/x-ndjson',
27
+ '.ndjson': 'application/x-ndjson',
28
+ '.yaml': 'application/yaml',
29
+ '.yml': 'application/yaml',
30
+ '.toml': 'application/toml',
31
+ '.pdf': 'application/pdf',
32
+ '.zip': 'application/zip',
33
+ '.gz': 'application/gzip',
34
+ '.tar': 'application/x-tar',
35
+ '.7z': 'application/x-7z-compressed',
36
+ '.rar': 'application/x-rar-compressed',
37
+ '.bz2': 'application/x-bzip2',
38
+ // Images
39
+ '.png': 'image/png',
40
+ '.jpg': 'image/jpeg',
41
+ '.jpeg': 'image/jpeg',
42
+ '.gif': 'image/gif',
43
+ '.svg': 'image/svg+xml',
44
+ '.webp': 'image/webp',
45
+ '.ico': 'image/x-icon',
46
+ '.tiff': 'image/tiff',
47
+ '.tif': 'image/tiff',
48
+ '.avif': 'image/avif',
49
+ // Video
50
+ '.mp4': 'video/mp4',
51
+ '.webm': 'video/webm',
52
+ '.mov': 'video/quicktime',
53
+ // Audio
54
+ '.mp3': 'audio/mpeg',
55
+ '.wav': 'audio/wav',
56
+ '.ogg': 'audio/ogg',
57
+ // Data / analytics
58
+ '.parquet': 'application/vnd.apache.parquet',
59
+ '.avro': 'application/avro',
60
+ '.arrow': 'application/vnd.apache.arrow.file',
61
+ // Fonts
62
+ '.woff': 'font/woff',
63
+ '.woff2': 'font/woff2',
64
+ '.ttf': 'font/ttf',
65
+ '.otf': 'font/otf',
66
+ };
67
+ /**
68
+ * Infer the MIME content-type from a filename (using its extension).
69
+ * Handles compound extensions like .tar.gz before falling through to last-dot.
70
+ * Returns 'application/octet-stream' for unknown extensions.
71
+ */
72
+ export function inferContentType(filename) {
73
+ const lower = filename.toLowerCase();
74
+ // Compound extensions — check before last-dot fallback
75
+ if (lower.endsWith('.tar.gz'))
76
+ return 'application/gzip';
77
+ if (lower.endsWith('.tar.bz2'))
78
+ return 'application/x-bzip2';
79
+ if (lower.endsWith('.tar.xz'))
80
+ return 'application/x-xz';
81
+ const lastDot = lower.lastIndexOf('.');
82
+ if (lastDot < 0)
83
+ return 'application/octet-stream';
84
+ const ext = lower.slice(lastDot);
85
+ return MIME_MAP[ext] ?? 'application/octet-stream';
86
+ }
87
+ //# sourceMappingURL=contentTypeInfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentTypeInfer.js","sourceRoot":"","sources":["../../src/lib/contentTypeInfer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,QAAQ,GAA2B;IACvC,gBAAgB;IAChB,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,eAAe;IACtB,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,iBAAiB;IACxB,MAAM,EAAE,iBAAiB;IACzB,KAAK,EAAE,iBAAiB;IACxB,KAAK,EAAE,oBAAoB;IAC3B,KAAK,EAAE,eAAe;IACtB,MAAM,EAAE,YAAY;IACpB,gCAAgC;IAChC,OAAO,EAAE,kBAAkB;IAC3B,QAAQ,EAAE,sBAAsB;IAChC,SAAS,EAAE,sBAAsB;IACjC,OAAO,EAAE,kBAAkB;IAC3B,MAAM,EAAE,kBAAkB;IAC1B,OAAO,EAAE,kBAAkB;IAC3B,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,KAAK,EAAE,kBAAkB;IACzB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,6BAA6B;IACpC,MAAM,EAAE,8BAA8B;IACtC,MAAM,EAAE,qBAAqB;IAC7B,SAAS;IACT,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,eAAe;IACvB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,QAAQ;IACR,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,iBAAiB;IACzB,QAAQ;IACR,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,mBAAmB;IACnB,UAAU,EAAE,gCAAgC;IAC5C,OAAO,EAAE,kBAAkB;IAC3B,QAAQ,EAAE,mCAAmC;IAC7C,QAAQ;IACR,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;IACtB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,UAAU;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAErC,uDAAuD;IACvD,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,kBAAkB,CAAC;IACzD,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,qBAAqB,CAAC;IAC7D,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,kBAAkB,CAAC;IAEzD,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,0BAA0B,CAAC;IAEnD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AACrD,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * F54 — .asset.yaml pointer file: parse / emit / normalize.
3
+ *
4
+ * BD-1 (ratified Stakeholder Review 2026-08-08): YAML ONLY.
5
+ * The .asset.json escape hatch is permanently removed.
6
+ * One canonical format; extension must be .asset.yaml.
7
+ *
8
+ * Canonical field order (BD-1 / pointer_file_format.schema_fields_ordered):
9
+ * uri, content_hash, size_bytes, content_type, created_at,
10
+ * produced_by.{agent, execution_id, step_id}, consumer_tag, original_filename
11
+ *
12
+ * AC-F54-8: readPointerFile() surfaces field-level errors — missing fields are
13
+ * named explicitly so the operator knows exactly what to fix.
14
+ */
15
+ export interface PointerFileProducedBy {
16
+ agent: string;
17
+ execution_id: string | null;
18
+ step_id: string | null;
19
+ }
20
+ export interface PointerFileData {
21
+ /** fazemos-asset://org/project/sha256 */
22
+ uri: string;
23
+ /** sha256:<64-char-hex> — WITH the sha256: prefix (pointer-file convention) */
24
+ content_hash: string;
25
+ size_bytes: number;
26
+ content_type: string;
27
+ /** ISO-8601 UTC with Z suffix */
28
+ created_at: string;
29
+ produced_by: PointerFileProducedBy;
30
+ consumer_tag: string;
31
+ original_filename: string | null;
32
+ }
33
+ /**
34
+ * Parse and validate a .asset.yaml pointer file.
35
+ *
36
+ * Throws with field-level error messages on validation failure (AC-F54-8).
37
+ * Enforces .asset.yaml extension (rul_asset_store_pointer_format).
38
+ */
39
+ export declare function readPointerFile(filePath: string): PointerFileData;
40
+ /**
41
+ * Return the canonical YAML string for a pointer, including the comment header.
42
+ * Fields are emitted in BD-1 canonical order.
43
+ */
44
+ export declare function buildPointerContent(data: PointerFileData): string;
45
+ /**
46
+ * Write a .asset.yaml pointer file in canonical field order.
47
+ * Creates parent directories as needed.
48
+ * Enforces .asset.yaml extension (rul_asset_store_pointer_format).
49
+ */
50
+ export declare function writePointerFile(filePath: string, data: PointerFileData): void;
51
+ /**
52
+ * Normalize a `pointer` object from the put-url API response into PointerFileData.
53
+ *
54
+ * API's pointer.content_hash is already in `sha256:<hex>` format (per handoff notes).
55
+ * API's pointer.produced_by matches the pointer-file shape.
56
+ */
57
+ export declare function pointerFromApiResponse(pointer: Record<string, unknown>): PointerFileData;
58
+ /**
59
+ * Strip the `sha256:` prefix from a content_hash field (pointer-file convention)
60
+ * to get the raw 64-char hex required by the API.
61
+ */
62
+ export declare function stripHashPrefix(contentHash: string): string;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * F54 — .asset.yaml pointer file: parse / emit / normalize.
3
+ *
4
+ * BD-1 (ratified Stakeholder Review 2026-08-08): YAML ONLY.
5
+ * The .asset.json escape hatch is permanently removed.
6
+ * One canonical format; extension must be .asset.yaml.
7
+ *
8
+ * Canonical field order (BD-1 / pointer_file_format.schema_fields_ordered):
9
+ * uri, content_hash, size_bytes, content_type, created_at,
10
+ * produced_by.{agent, execution_id, step_id}, consumer_tag, original_filename
11
+ *
12
+ * AC-F54-8: readPointerFile() surfaces field-level errors — missing fields are
13
+ * named explicitly so the operator knows exactly what to fix.
14
+ */
15
+ import { readFileSync, writeFileSync, mkdirSync } from 'fs';
16
+ import { dirname } from 'path';
17
+ import yaml from 'js-yaml';
18
+ // ── Constants ─────────────────────────────────────────────────────────────────
19
+ const COMMENT_HEADER = '# fazemos-asset pointer — this file replaces a large binary in the repo.\n' +
20
+ '# The underlying bytes live in the Fazemos asset store; resolve with\n' +
21
+ '# `fazemos asset get <this-file>` or `fazemos asset show <this-file>`.';
22
+ const REQUIRED_FIELDS = [
23
+ 'uri',
24
+ 'content_hash',
25
+ 'size_bytes',
26
+ 'content_type',
27
+ 'created_at',
28
+ 'produced_by',
29
+ 'consumer_tag',
30
+ ];
31
+ const REQUIRED_PRODUCED_BY_FIELDS = [
32
+ 'agent',
33
+ 'execution_id',
34
+ 'step_id',
35
+ ];
36
+ // ── Read ──────────────────────────────────────────────────────────────────────
37
+ /**
38
+ * Parse and validate a .asset.yaml pointer file.
39
+ *
40
+ * Throws with field-level error messages on validation failure (AC-F54-8).
41
+ * Enforces .asset.yaml extension (rul_asset_store_pointer_format).
42
+ */
43
+ export function readPointerFile(filePath) {
44
+ if (!filePath.endsWith('.asset.yaml')) {
45
+ throw new Error(`Pointer files must use the .asset.yaml extension (got: ${filePath})\n` +
46
+ `Hint: pass the raw fazemos-asset:// URI directly, or rename the file.`);
47
+ }
48
+ let raw;
49
+ try {
50
+ raw = readFileSync(filePath, 'utf-8');
51
+ }
52
+ catch (err) {
53
+ throw new Error(`Cannot read pointer file '${filePath}': ${err.message}`);
54
+ }
55
+ let parsed;
56
+ try {
57
+ parsed = yaml.load(raw);
58
+ }
59
+ catch (err) {
60
+ throw new Error(`Pointer file '${filePath}' is not valid YAML: ${err.message}`);
61
+ }
62
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
63
+ throw new Error(`Pointer file '${filePath}' is empty or not a YAML object.`);
64
+ }
65
+ const data = parsed;
66
+ // Field-level validation (AC-F54-8)
67
+ const missing = [];
68
+ for (const field of REQUIRED_FIELDS) {
69
+ if (data[field] === undefined) {
70
+ missing.push(field);
71
+ }
72
+ }
73
+ // Validate produced_by sub-fields
74
+ if (data['produced_by'] !== undefined && data['produced_by'] !== null) {
75
+ if (typeof data['produced_by'] === 'object' && !Array.isArray(data['produced_by'])) {
76
+ const pb = data['produced_by'];
77
+ for (const field of REQUIRED_PRODUCED_BY_FIELDS) {
78
+ if (!(field in pb)) {
79
+ missing.push(`produced_by.${field}`);
80
+ }
81
+ }
82
+ }
83
+ else {
84
+ missing.push('produced_by.agent', 'produced_by.execution_id', 'produced_by.step_id');
85
+ }
86
+ }
87
+ else if (!missing.includes('produced_by')) {
88
+ missing.push('produced_by.agent', 'produced_by.execution_id', 'produced_by.step_id');
89
+ }
90
+ if (missing.length > 0) {
91
+ throw new Error(`Pointer file '${filePath}' is missing required fields: ${missing.join(', ')}\n` +
92
+ `The file may be truncated, hand-edited incorrectly, or from an older format version.`);
93
+ }
94
+ return data;
95
+ }
96
+ // ── Write ─────────────────────────────────────────────────────────────────────
97
+ /**
98
+ * Return the canonical YAML string for a pointer, including the comment header.
99
+ * Fields are emitted in BD-1 canonical order.
100
+ */
101
+ export function buildPointerContent(data) {
102
+ // Construct in canonical field order (BD-1)
103
+ const ordered = {
104
+ uri: data.uri,
105
+ content_hash: data.content_hash,
106
+ size_bytes: data.size_bytes,
107
+ content_type: data.content_type,
108
+ created_at: data.created_at,
109
+ produced_by: {
110
+ agent: data.produced_by.agent,
111
+ execution_id: data.produced_by.execution_id,
112
+ step_id: data.produced_by.step_id,
113
+ },
114
+ consumer_tag: data.consumer_tag,
115
+ original_filename: data.original_filename ?? null,
116
+ };
117
+ const yamlBody = yaml.dump(ordered, {
118
+ lineWidth: -1,
119
+ noRefs: true,
120
+ sortKeys: false,
121
+ indent: 2,
122
+ });
123
+ return `${COMMENT_HEADER}\n${yamlBody}`;
124
+ }
125
+ /**
126
+ * Write a .asset.yaml pointer file in canonical field order.
127
+ * Creates parent directories as needed.
128
+ * Enforces .asset.yaml extension (rul_asset_store_pointer_format).
129
+ */
130
+ export function writePointerFile(filePath, data) {
131
+ if (!filePath.endsWith('.asset.yaml')) {
132
+ throw new Error(`Pointer file path must end in .asset.yaml (got: ${filePath})`);
133
+ }
134
+ const dir = dirname(filePath);
135
+ if (dir && dir !== '.') {
136
+ mkdirSync(dir, { recursive: true });
137
+ }
138
+ writeFileSync(filePath, buildPointerContent(data), 'utf-8');
139
+ }
140
+ // ── API response normalization ────────────────────────────────────────────────
141
+ /**
142
+ * Normalize a `pointer` object from the put-url API response into PointerFileData.
143
+ *
144
+ * API's pointer.content_hash is already in `sha256:<hex>` format (per handoff notes).
145
+ * API's pointer.produced_by matches the pointer-file shape.
146
+ */
147
+ export function pointerFromApiResponse(pointer) {
148
+ const pb = pointer['produced_by'] ?? {};
149
+ return {
150
+ uri: String(pointer['uri'] ?? ''),
151
+ content_hash: String(pointer['content_hash'] ?? ''),
152
+ size_bytes: Number(pointer['size_bytes'] ?? 0),
153
+ content_type: String(pointer['content_type'] ?? ''),
154
+ created_at: String(pointer['created_at'] ?? ''),
155
+ produced_by: {
156
+ agent: String(pb['agent'] ?? 'fazemos-cli'),
157
+ execution_id: pb['execution_id'] != null ? String(pb['execution_id']) : null,
158
+ step_id: pb['step_id'] != null ? String(pb['step_id']) : null,
159
+ },
160
+ consumer_tag: String(pointer['consumer_tag'] ?? ''),
161
+ original_filename: pointer['original_filename'] != null ? String(pointer['original_filename']) : null,
162
+ };
163
+ }
164
+ /**
165
+ * Strip the `sha256:` prefix from a content_hash field (pointer-file convention)
166
+ * to get the raw 64-char hex required by the API.
167
+ */
168
+ export function stripHashPrefix(contentHash) {
169
+ return contentHash.startsWith('sha256:') ? contentHash.slice(7) : contentHash;
170
+ }
171
+ //# sourceMappingURL=pointerFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointerFile.js","sourceRoot":"","sources":["../../src/lib/pointerFile.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formigio/fazemos-cli",
3
- "version": "0.10.64",
3
+ "version": "0.10.67",
4
4
  "description": "CLI for the Fazemos Team Accomplishment Platform",
5
5
  "type": "module",
6
6
  "license": "MIT",