@fatalmistake02/rian 0.0.0-stage → 0.2.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +155 -2
  3. package/THIRD_PARTY_NOTICES.txt +673 -0
  4. package/bin/rian.cjs +12 -0
  5. package/cmd/rian/main.go +17 -0
  6. package/dist/binaries/darwin-arm64/rian +0 -0
  7. package/dist/binaries/darwin-x64/rian +0 -0
  8. package/dist/binaries/linux-arm64/rian +0 -0
  9. package/dist/binaries/linux-x64/rian +0 -0
  10. package/dist/binaries/manifest.json +29 -0
  11. package/dist/binaries/win32-arm64/rian.exe +0 -0
  12. package/dist/binaries/win32-x64/rian.exe +0 -0
  13. package/go.mod +28 -0
  14. package/go.sum +44 -0
  15. package/internal/app/app.go +298 -0
  16. package/internal/app/app_test.go +85 -0
  17. package/internal/config/config.go +189 -0
  18. package/internal/config/config_test.go +101 -0
  19. package/internal/detect/detect.go +252 -0
  20. package/internal/detect/detect_test.go +86 -0
  21. package/internal/git/git.go +166 -0
  22. package/internal/git/git_test.go +73 -0
  23. package/internal/git/window_unix.go +7 -0
  24. package/internal/git/window_windows.go +8 -0
  25. package/internal/process/platform_unix.go +19 -0
  26. package/internal/process/platform_windows.go +25 -0
  27. package/internal/process/process.go +172 -0
  28. package/internal/process/process_test.go +137 -0
  29. package/internal/project/project.go +180 -0
  30. package/internal/project/project_test.go +71 -0
  31. package/internal/tools/lookup.go +41 -0
  32. package/internal/tools/platform_unix.go +34 -0
  33. package/internal/tools/platform_windows.go +52 -0
  34. package/internal/tools/platform_windows_test.go +29 -0
  35. package/internal/tools/tools.go +152 -0
  36. package/internal/tools/tools_test.go +56 -0
  37. package/internal/ui/actions.go +259 -0
  38. package/internal/ui/forms.go +156 -0
  39. package/internal/ui/model.go +220 -0
  40. package/internal/ui/styles.go +85 -0
  41. package/internal/ui/ui_test.go +170 -0
  42. package/internal/ui/update.go +470 -0
  43. package/internal/ui/view.go +459 -0
  44. package/lib/launcher.cjs +46 -0
  45. package/lib/platforms.cjs +20 -0
  46. package/package.json +39 -4
  47. package/scripts/build-binaries.cjs +60 -0
  48. package/scripts/check-package.cjs +49 -0
  49. package/scripts/ensure-binary.cjs +11 -0
  50. package/test/npm/launcher.test.cjs +45 -0
  51. package/test/npm/package.test.cjs +26 -0
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const crypto = require('node:crypto');
6
+ const { spawnSync } = require('node:child_process');
7
+ const { targets, targetFor } = require('../lib/platforms.cjs');
8
+ const { binaryPath } = require('../lib/launcher.cjs');
9
+
10
+ const root = path.resolve(__dirname, '..');
11
+ const pkg = require('../package.json');
12
+
13
+ function checkPackage(packageRoot = root) {
14
+ const assetRoot = path.join(packageRoot, 'dist', 'binaries');
15
+ const manifest = JSON.parse(fs.readFileSync(path.join(assetRoot, 'manifest.json'), 'utf8'));
16
+ if (manifest.version !== pkg.version) throw new Error('Binary version is stale; run npm run build');
17
+ if (Object.keys(manifest.targets).length !== targets.length) throw new Error('The binary manifest must contain all six platforms');
18
+ for (const item of targets) {
19
+ const target = targetFor(item.platform, item.arch);
20
+ const expected = `${target.key}/${target.filename}`;
21
+ const info = manifest.targets[target.key];
22
+ if (!info || info.file !== expected) throw new Error(`Missing ${target.key} executable`);
23
+ const data = fs.readFileSync(path.join(assetRoot, expected));
24
+ if (data.length < 1024) throw new Error(`${target.key} is not a built executable`);
25
+ const actual = crypto.createHash('sha256').update(data).digest('hex');
26
+ if (actual !== info.sha256) throw new Error(`${target.key} checksum does not match`);
27
+ if (target.platform === 'darwin') {
28
+ if (data.readUInt32LE(0) !== 0xfeedfacf || data.readUInt32LE(4) !== (target.arch === 'arm64' ? 0x100000c : 0x1000007)) throw new Error(`Wrong Mach-O architecture for ${target.key}`);
29
+ } else if (target.platform === 'linux') {
30
+ if (data.subarray(0, 4).toString('hex') !== '7f454c46' || data[4] !== 2 || data.readUInt16LE(18) !== (target.arch === 'arm64' ? 183 : 62)) throw new Error(`Wrong ELF architecture for ${target.key}`);
31
+ } else {
32
+ const header = data.readUInt32LE(0x3c);
33
+ if (data.subarray(0, 2).toString() !== 'MZ' || data.readUInt32LE(header) !== 0x4550 || data.readUInt16LE(header + 4) !== (target.arch === 'arm64' ? 0xaa64 : 0x8664)) throw new Error(`Wrong PE architecture for ${target.key}`);
34
+ }
35
+ }
36
+ for (const file of ['LICENSE', 'THIRD_PARTY_NOTICES.txt', 'bin/rian.cjs', 'scripts/ensure-binary.cjs']) {
37
+ if (!fs.statSync(path.join(packageRoot, file)).isFile()) throw new Error(`Missing ${file}`);
38
+ }
39
+ const result = spawnSync(binaryPath(packageRoot), ['--version'], { encoding: 'utf8', windowsHide: true });
40
+ if (result.error || result.status !== 0 || result.stdout.trim() !== `rian ${pkg.version}`) throw new Error('The current-platform executable failed the version check');
41
+ return manifest;
42
+ }
43
+
44
+ if (require.main === module) {
45
+ try { checkPackage(); console.log(`Rian ${pkg.version}: all six binaries, checksums, architectures, and package files verified.`); }
46
+ catch (err) { console.error(`rian package: ${err.message}`); process.exitCode = 1; }
47
+ }
48
+
49
+ module.exports = { checkPackage };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ // This only fixes executable permissions. No downloads or builds happen on
4
+ // the user's machine. The launcher also repairs permissions if scripts are off.
5
+ const path = require('node:path');
6
+ const { binaryPath } = require('../lib/launcher.cjs');
7
+
8
+ try { binaryPath(path.resolve(__dirname, '..')); } catch (err) {
9
+ console.error(`rian: ${err.message}`);
10
+ process.exitCode = 1;
11
+ }
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const test = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+ const fs = require('node:fs');
6
+ const path = require('node:path');
7
+ const os = require('node:os');
8
+ const { EventEmitter } = require('node:events');
9
+ const { targets, targetFor } = require('../../lib/platforms.cjs');
10
+ const { binaryPath, launch } = require('../../lib/launcher.cjs');
11
+
12
+ test('dispatches all six platform/architecture combinations', t => {
13
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'rian-launcher-'));
14
+ t.after(() => fs.rmSync(root, { recursive: true, force: true }));
15
+ for (const item of targets) {
16
+ const target = targetFor(item.platform, item.arch);
17
+ const file = path.join(root, 'dist', 'binaries', target.key, target.filename);
18
+ fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, 'fixture');
19
+ assert.equal(binaryPath(root, item.platform, item.arch), file);
20
+ }
21
+ assert.throws(() => binaryPath(root, 'freebsd', 'x64'), /does not support/);
22
+ assert.throws(() => binaryPath(root, 'darwin', 'ia32'), /does not support/);
23
+ });
24
+
25
+ test('missing binaries give an actionable install error', () => {
26
+ assert.throws(() => binaryPath(path.join(os.tmpdir(), 'rian-missing-install')), /npm install -g @fatalmistake02\/rian/);
27
+ });
28
+
29
+ test('preserves arguments, terminal stdio, working directory, and child exit status', () => {
30
+ const root = path.resolve(__dirname, '../..');
31
+ const child = new EventEmitter(); child.exitCode = null; child.signalCode = null;
32
+ const oldCode = process.exitCode;
33
+ const oldHandlerCount = process.listenerCount('SIGTERM');
34
+ let invocation;
35
+ const args = ['add', '/directory with spaces', '--yes'];
36
+ launch(root, args, (file, passed, options) => { invocation = { file, passed, options }; return child; });
37
+ assert.deepEqual(invocation.passed, args);
38
+ assert.equal(invocation.options.stdio, 'inherit');
39
+ assert.equal(invocation.options.shell, false);
40
+ assert.equal(invocation.options.cwd, undefined);
41
+ child.emit('exit', 7, null);
42
+ assert.equal(process.exitCode, 7);
43
+ assert.equal(process.listenerCount('SIGTERM'), oldHandlerCount);
44
+ process.exitCode = oldCode;
45
+ });
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const test = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+ const path = require('node:path');
6
+ const { spawnSync } = require('node:child_process');
7
+ const { checkPackage } = require('../../scripts/check-package.cjs');
8
+ const pkg = require('../../package.json');
9
+
10
+ test('built package includes verified native executables for every supported platform', () => {
11
+ const manifest = checkPackage();
12
+ assert.equal(manifest.version, pkg.version);
13
+ assert.equal(Object.keys(manifest.targets).length, 6);
14
+ assert.equal(pkg.bin.rian, 'bin/rian.cjs');
15
+ });
16
+
17
+ test('npm launcher runs the native executable and returns errors cleanly', () => {
18
+ const root = path.resolve(__dirname, '../..');
19
+ const launcher = path.join(root, 'bin', 'rian.cjs');
20
+ const version = spawnSync(process.execPath, [launcher, '--version'], { encoding: 'utf8', cwd: path.dirname(root), windowsHide: true });
21
+ assert.equal(version.status, 0, version.stderr);
22
+ assert.equal(version.stdout.trim(), `rian ${pkg.version}`);
23
+ const invalid = spawnSync(process.execPath, [launcher, 'invalid-subcommand'], { encoding: 'utf8', windowsHide: true });
24
+ assert.equal(invalid.status, 1);
25
+ assert.match(invalid.stderr, /unknown command/);
26
+ });