@shipvo/cli 0.1.2 → 0.1.4

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/dist/help.js ADDED
@@ -0,0 +1,76 @@
1
+ export const COMMANDS = [
2
+ "init", "sim", "preview", "publish", "promote", "auto-halt", "rollback", "app", "login", "doctor", "store", "status", "keys", "mcp",
3
+ "keygen", "pubkey", "hash", "sign-file", "sign-json", "verify-json", "verify",
4
+ ];
5
+ export const help = `shipvo <command>
6
+
7
+ Start
8
+ init <dir> Create a new app wired to Shipvo
9
+ sim [--port 4545] Build the app and run it in the simulator
10
+ sim --watch [--verbose] Rebuild on file changes and keep serving; --verbose shows full build errors
11
+ preview [--json] Publish a public 24-hour preview link (here.now, no account)
12
+
13
+ Ship
14
+ publish [--bands b0,b1,b2] Build, sign and publish an over-the-air release
15
+ publish --dry-run [--out d] Check a release offline: build and sign, write nothing remote
16
+ promote [--to b1,b2] Widen a staged release to more bands (same release, new signature)
17
+ promote --dry-run Preview a promote, write nothing
18
+ auto-halt [--threshold 0.2 --min-reports 20 | --off] Show, turn on or turn off auto-halt
19
+ rollback --to <release> Republish an earlier release as a new release
20
+ rollback --to <n> --dry-run Preview a rollback, write nothing
21
+ app create | list | delete Register, list or remove apps on the hosted control plane
22
+ login Sign in with GitHub and get a key for the hosted simulator
23
+ store details --dry-run Draft Play Data safety answers and a privacy policy from shipvo.json
24
+ status [--app id] [--json] Show how the newest releases are doing on phones
25
+ doctor [--port <n>] Check setup before first publish
26
+ keys Show the stored key's status and quota
27
+ keys export [--out f] Print or save this app's signing seed, to back it up
28
+ keys import [seed|--file f] Restore the signing seed on a new machine
29
+ mcp Print the MCP config that agents need
30
+
31
+ Advanced
32
+ keygen [--out seed.txt] Create an Ed25519 signing key and print the public key
33
+ pubkey Print the public key for SHIPVO_SIGNING_SEED
34
+ hash <file> Print the SHA-256 and size of a file
35
+ sign-file <file> Print the Ed25519 signature (base64) of a file
36
+ sign-json <file.json> Sign a JSON document and write the signature into it
37
+ verify <pointer> [public-key-b64]
38
+ Check a release pointer (such as one from publish --dry-run)
39
+ verify-json <file.json> <public-key-b64>
40
+ Check the signature of a JSON document
41
+ `;
42
+ export function distance(a, b) {
43
+ let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
44
+ for (let i = 1; i <= a.length; i++) {
45
+ const cur = [i];
46
+ for (let j = 1; j <= b.length; j++) {
47
+ cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
48
+ }
49
+ prev = cur;
50
+ }
51
+ return prev[b.length];
52
+ }
53
+ /** Closest command within edit distance 2, or undefined. */
54
+ export function suggest(input) {
55
+ let best;
56
+ let bestD = 3;
57
+ for (const c of COMMANDS) {
58
+ const d = distance(input.toLowerCase(), c);
59
+ if (d < bestD) {
60
+ best = c;
61
+ bestD = d;
62
+ }
63
+ }
64
+ return best;
65
+ }
66
+ export function unknownCommand(input) {
67
+ const s = suggest(input);
68
+ return `shipvo: '${input}' is not a command.${s ? ` Did you mean '${s}'?` : ""}\nRun 'shipvo --help' to see all commands.\n`;
69
+ }
70
+ /** What to print when a command throws: the message, not a stack trace. Set SHIPVO_DEBUG=1 for the stack. */
71
+ export function failure(e, debug = !!process.env.SHIPVO_DEBUG) {
72
+ if (debug && e instanceof Error && e.stack)
73
+ return e.stack;
74
+ const m = e instanceof Error ? e.message : String(e);
75
+ return `shipvo: ${m}\nRun 'shipvo doctor' to check your setup, or set SHIPVO_DEBUG=1 to see the full error.\n`;
76
+ }
@@ -0,0 +1,11 @@
1
+ /** True if the bytes start with the Hermes bytecode magic number. */
2
+ export declare const isHermesBytecode: (b: Uint8Array) => boolean;
3
+ /** The bytecode version in the header, or null if this is not Hermes bytecode. */
4
+ export declare function hermesBytecodeVersion(b: Uint8Array): number | null;
5
+ /** Parse a declared version list such as "96" or "96,98" (CLI flag) or a number / number[] (shipvo.json). */
6
+ export declare function parseDeclared(v: unknown): number[] | undefined;
7
+ /**
8
+ * Decide what to record in the release. Returns the version (or undefined when nothing is known).
9
+ * Throws when the bundle is not bytecode but the app declares a bytecode version, or when the versions differ.
10
+ */
11
+ export declare function checkBundle(file: string, bytes: Uint8Array, declared?: number[]): number | undefined;
package/dist/hermes.js ADDED
@@ -0,0 +1,41 @@
1
+ // Read the Hermes bytecode version from a bundle header, and check it against what the app's shells declare.
2
+ // A Hermes bytecode file starts with an 8-byte magic number (little-endian 0x1F1903C103BC1FC6),
3
+ // followed by the bytecode format version as a 32-bit little-endian integer.
4
+ // Layout source: the BytecodeFileHeader struct in facebook/hermes, include/hermes/BCGen/HBC/BytecodeFileFormat.h
5
+ // (fields MAGIC, then VERSION).
6
+ const MAGIC = [0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f];
7
+ /** True if the bytes start with the Hermes bytecode magic number. */
8
+ export const isHermesBytecode = (b) => b.length >= 12 && MAGIC.every((m, i) => b[i] === m);
9
+ /** The bytecode version in the header, or null if this is not Hermes bytecode. */
10
+ export function hermesBytecodeVersion(b) {
11
+ if (!isHermesBytecode(b))
12
+ return null;
13
+ return new DataView(b.buffer, b.byteOffset, b.byteLength).getUint32(8, true);
14
+ }
15
+ /** Parse a declared version list such as "96" or "96,98" (CLI flag) or a number / number[] (shipvo.json). */
16
+ export function parseDeclared(v) {
17
+ if (v === undefined || v === null || v === "")
18
+ return undefined;
19
+ const list = Array.isArray(v) ? v : String(v).split(",");
20
+ const nums = list.map((x) => Number(String(x).trim()));
21
+ if (!nums.length || nums.some((n) => !Number.isInteger(n) || n <= 0))
22
+ throw new Error(`hermes bytecode version must be positive integers (got ${JSON.stringify(v)})`);
23
+ return nums;
24
+ }
25
+ /**
26
+ * Decide what to record in the release. Returns the version (or undefined when nothing is known).
27
+ * Throws when the bundle is not bytecode but the app declares a bytecode version, or when the versions differ.
28
+ */
29
+ export function checkBundle(file, bytes, declared) {
30
+ const version = hermesBytecodeVersion(bytes);
31
+ if (version === null) {
32
+ if (file.endsWith(".hbc"))
33
+ throw new Error(`${file} has a .hbc name but no Hermes bytecode header. Was it compiled with hermesc?`);
34
+ if (declared)
35
+ throw new Error(`${file} is plain JavaScript, but the app declares Hermes bytecode version ${declared.join(", ")}. Enable Hermes bytecode in the export, or remove hermesBytecodeVersion.`);
36
+ return undefined;
37
+ }
38
+ if (declared && !declared.includes(version))
39
+ throw new Error(`${file} is Hermes bytecode version ${version}, but the app's shells run ${declared.join(", ")}. Build with the same Hermes as the shell, or update hermesBytecodeVersion if the shell changed.`);
40
+ return version;
41
+ }