mars-ram 0.0.0-stage → 0.1.3

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
@@ -1,3 +1,26 @@
1
- # Temporary Holding Version
1
+ # mars-ram 0.1.3
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The computer side of mars-ram: the command that pairs your phone with this computer, the Pi session bridge and the private-link server. Linux, macOS and Windows; Node.js 24 or newer.
4
+
5
+ ```bash
6
+ pnpm add -g mars-ram # recommended
7
+ npm i -g mars-ram # or
8
+ bun add -g mars-ram # or
9
+ ```
10
+
11
+ Then:
12
+
13
+ ```bash
14
+ mars-ram install
15
+ mars-ram pair
16
+ ```
17
+
18
+ `mars-ram install` copies everything to `~/.mars-ram` in your user account (no administrator rights) and installs the bridge into each Pi you choose. `mars-ram doctor` checks the setup, `mars-ram uninstall` removes it (then `pnpm remove -g mars-ram` removes the package). To update: `pnpm add -g mars-ram`, then `mars-ram install`; if you installed earlier with the curl script and `mars-ram install` still runs the old version, run `mars-ram uninstall` first.
19
+
20
+ The package has no install scripts and no dependencies. It carries the release archive `mars-ram-0.1.3.tar.gz` (SHA-256 `7f0bf65fad1a5c2398c8d8bb7047c7c7de91867d81671ef4771d34f78809836e`, the same file as the GitHub release) and a launcher that checks that SHA-256, unpacks the archive into a private temporary folder and runs it, because Node does not run TypeScript from `node_modules`. After `mars-ram install`, the launcher runs the installed copy directly when it is this same version.
21
+
22
+ Install guide: https://marssall.github.io/mars-ram/install
23
+
24
+ ---
25
+
26
+ **Español.** Instalá con `pnpm add -g mars-ram` (recomendado) o `npm i -g mars-ram`, después ejecutá `mars-ram install` y `mars-ram pair`. Requiere Node.js 24 o más nuevo. Guía: https://marssall.github.io/mars-ram/install
Binary file
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ // mars-ram npm launcher. Node refuses to strip TypeScript types under node_modules, so this package carries the
3
+ // release archive and runs it from a private temporary folder, as install.sh does. Node built-ins only; rendered by
4
+ // host/cli/scripts/build-release.ts, which writes the version, the archive SHA-256 and the CLI fingerprint below.
5
+ import { execFileSync, spawn } from 'node:child_process';
6
+ import { createHash } from 'node:crypto';
7
+ import { lstatSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
8
+ import { constants, homedir, tmpdir } from 'node:os';
9
+ import { join } from 'node:path';
10
+
11
+ const VERSION = '0.1.3';
12
+ const ARCHIVE_SHA256 = '7f0bf65fad1a5c2398c8d8bb7047c7c7de91867d81671ef4771d34f78809836e';
13
+ const CLI_FINGERPRINT = 'f53d63e493b1f2d1a7850694b2b41c112621a7840088ebc73d55fe99a31fd269';
14
+
15
+ // Same locale rule as the CLI (host/cli/src/adapters/terminal.ts); an empty variable counts as unset.
16
+ const spanish = (process.env.LC_ALL || process.env.LC_MESSAGES || process.env.LANG || Intl.DateTimeFormat().resolvedOptions().locale).toLowerCase().startsWith('es');
17
+ const say = (en, es) => spanish ? es : en;
18
+ function fail(message) {
19
+ process.stderr.write(`${message}\n`);
20
+ process.exit(1);
21
+ }
22
+
23
+ const args = process.argv.slice(2);
24
+ const packageRoot = join(import.meta.dirname, '..');
25
+
26
+ // The copy `mars-ram install` wrote runs directly when it is this package's CLI; install and uninstall always run
27
+ // from the package, so they never replace or remove the code that is running them.
28
+ function installedMain() {
29
+ if (args[0] === 'install' || args[0] === 'uninstall') return undefined;
30
+ const cli = join(homedir(), '.mars-ram', 'cli');
31
+ try {
32
+ if (readFileSync(join(cli, 'fingerprint.sha256'), 'utf8').trim() !== CLI_FINGERPRINT) return undefined;
33
+ const main = join(cli, 'src', 'main.ts'), stat = lstatSync(main);
34
+ return stat.isFile() && !stat.isSymbolicLink() ? main : undefined;
35
+ } catch { return undefined; }
36
+ }
37
+
38
+ // Checks the embedded archive against the SHA-256 pinned at build time, then unpacks the checked bytes (not the file
39
+ // again) into a new folder only this user can read.
40
+ function extract() {
41
+ const name = `mars-ram-${VERSION}`;
42
+ const bytes = readFileSync(join(packageRoot, 'archive', `${name}.tar.gz`));
43
+ const actual = createHash('sha256').update(bytes).digest('hex');
44
+ if (actual !== ARCHIVE_SHA256) fail(say(
45
+ `The mars-ram package is damaged: its archive does not match the expected SHA-256.\nExpected: ${ARCHIVE_SHA256}\nActual: ${actual}\nNothing was run. Reinstall it: pnpm add -g mars-ram (or npm i -g mars-ram).`,
46
+ `El paquete mars-ram está dañado: su archivo no coincide con el SHA-256 esperado.\nEsperado: ${ARCHIVE_SHA256}\nObtenido: ${actual}\nNo se ejecutó nada. Instalalo de nuevo: pnpm add -g mars-ram (o npm i -g mars-ram).`));
47
+ const dir = mkdtempSync(join(tmpdir(), 'mars-ram-'));
48
+ try {
49
+ writeFileSync(join(dir, 'archive.tar.gz'), bytes, { mode: 0o600, flag: 'wx' });
50
+ // Windows 10+ ships bsdtar as System32\tar.exe; name it so an earlier tar on PATH is never picked.
51
+ const tar = process.platform === 'win32' ? join(process.env.SystemRoot ?? 'C:\\Windows', 'System32', 'tar.exe') : 'tar';
52
+ // Relative names with cwd: GNU tar would read "C:\..." as a remote host.
53
+ execFileSync(tar, ['-xzf', 'archive.tar.gz'], { cwd: dir, stdio: ['ignore', 'ignore', 'pipe'], shell: false, windowsHide: true });
54
+ rmSync(join(dir, 'archive.tar.gz'));
55
+ } catch (error) {
56
+ rmSync(dir, { recursive: true, force: true });
57
+ fail(say(`mars-ram could not unpack its files: ${error.message}`, `mars-ram no pudo descomprimir sus archivos: ${error.message}`));
58
+ }
59
+ return { main: join(dir, name, 'cli', 'src', 'main.ts'), dir };
60
+ }
61
+
62
+ // Signal handlers come first, before the archive is unpacked: a SIGTERM or SIGHUP that arrives during startup then waits
63
+ // for the synchronous unpacking, reaches the CLI as soon as it starts, and the temporary copy is still removed. Only
64
+ // SIGKILL leaves it behind. Ctrl+C reaches the whole process group, so the CLI gets it directly and decides; this
65
+ // process only waits. Signals sent to this process alone are passed on.
66
+ let child, dir;
67
+ const cleanup = () => { if (dir) rmSync(dir, { recursive: true, force: true }); };
68
+ const forward = signal => {
69
+ if (child) child.kill(signal);
70
+ else { cleanup(); process.exit(128 + (constants.signals[signal] ?? 0)); }
71
+ };
72
+ const handlers = { SIGINT: () => {}, SIGTERM: forward, SIGHUP: forward };
73
+ for (const [signal, handler] of Object.entries(handlers)) process.on(signal, handler);
74
+
75
+ const installed = installedMain();
76
+ let main = installed;
77
+ if (!installed) ({ main, dir } = extract());
78
+
79
+ // MARS_RAM_PACKAGE_BIN tells `mars-ram install` that this package's bin is the command, so it writes no launcher of
80
+ // its own (one earlier on PATH would keep running the old version).
81
+ child = spawn(process.execPath, ['--disable-warning=ExperimentalWarning', main, ...args], { stdio: 'inherit', shell: false, env: { ...process.env, MARS_RAM_PACKAGE_BIN: import.meta.filename } });
82
+ child.on('error', error => {
83
+ cleanup();
84
+ fail(say(`mars-ram could not start Node: ${error.message}`, `mars-ram no pudo iniciar Node: ${error.message}`));
85
+ });
86
+ child.on('exit', (code, signal) => {
87
+ cleanup();
88
+ for (const [name, handler] of Object.entries(handlers)) process.off(name, handler);
89
+ if (signal) {
90
+ // End the same way, so a calling shell sees the signal.
91
+ process.kill(process.pid, signal);
92
+ process.exit(128 + (constants.signals[signal] ?? 0));
93
+ }
94
+ process.exit(code ?? 1);
95
+ });
package/package.json CHANGED
@@ -1,6 +1,23 @@
1
1
  {
2
2
  "name": "mars-ram",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.3",
4
+ "description": "Computer side of mars-ram: pairs your phone with this computer and installs the Pi session bridge and private-link server.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "bin": {
8
+ "mars-ram": "bin/mars-ram.mjs"
9
+ },
10
+ "engines": {
11
+ "node": ">=24"
12
+ },
13
+ "files": [
14
+ "archive",
15
+ "bin",
16
+ "README.md"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/MarsSall/mars-ram.git"
21
+ },
22
+ "homepage": "https://marssall.github.io/mars-ram/install"
23
+ }