@turtle170/rift 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/bin/rift.js +35 -0
- package/package.json +31 -0
- package/scripts/postinstall.js +127 -0
package/bin/rift.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin shim: finds rift.exe and forwards all args to it.
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
|
|
10
|
+
const EXE_NAME = 'rift.exe';
|
|
11
|
+
|
|
12
|
+
function findRiftExe() {
|
|
13
|
+
// First: check next to this script (for dev / local installs)
|
|
14
|
+
const localPath = path.join(__dirname, '..', EXE_NAME);
|
|
15
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
16
|
+
|
|
17
|
+
// Second: check %LOCALAPPDATA%\rift\bin\rift.exe (postinstall destination)
|
|
18
|
+
const appLocal = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
19
|
+
const installedPath = path.join(appLocal, 'rift', 'bin', EXE_NAME);
|
|
20
|
+
if (fs.existsSync(installedPath)) return installedPath;
|
|
21
|
+
|
|
22
|
+
console.error(
|
|
23
|
+
'\n\x1b[31m[rift]\x1b[0m Cannot find rift.exe.\n' +
|
|
24
|
+
'Try reinstalling: \x1b[36mnpm install -g rift\x1b[0m\n'
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const exe = findRiftExe();
|
|
30
|
+
const result = spawnSync(exe, process.argv.slice(2), {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
windowsHide: false,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
process.exit(result.status ?? 1);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@turtle170/rift",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Rift - your desktop pet code reviewer powered by local AI",
|
|
5
|
+
"keywords": ["code-review", "ai", "desktop-pet", "llm", "tree-sitter", "rust"],
|
|
6
|
+
"homepage": "https://github.com/turtle170/Rift",
|
|
7
|
+
"bugs": { "url": "https://github.com/turtle170/Rift/issues" },
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/turtle170/Rift.git"
|
|
11
|
+
},
|
|
12
|
+
"license": "Apache-2.0",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"rift": "bin/rift.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"postinstall": "node scripts/postinstall.js"
|
|
21
|
+
},
|
|
22
|
+
"os": ["win32"],
|
|
23
|
+
"cpu": ["x64"],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"scripts/"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Postinstall: downloads rift.exe from GitHub Releases into %LOCALAPPDATA%\rift\bin\
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const http = require('http');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
const REPO = 'turtle170/Rift';
|
|
14
|
+
const EXE_ASSET = 'rift.exe';
|
|
15
|
+
const SHA_ASSET = 'rift.exe.sha256';
|
|
16
|
+
|
|
17
|
+
function log(msg) { process.stdout.write(`\x1b[36m[rift]\x1b[0m ${msg}\n`); }
|
|
18
|
+
function warn(msg) { process.stdout.write(`\x1b[33m[rift]\x1b[0m ${msg}\n`); }
|
|
19
|
+
function err(msg) { process.stderr.write(`\x1b[31m[rift]\x1b[0m ${msg}\n`); }
|
|
20
|
+
|
|
21
|
+
function getDestDir() {
|
|
22
|
+
const appLocal = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
23
|
+
return path.join(appLocal, 'rift', 'bin');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function fetchJson(url) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const opts = new URL(url);
|
|
29
|
+
const req = https.request({
|
|
30
|
+
hostname: opts.hostname,
|
|
31
|
+
path: opts.pathname + opts.search,
|
|
32
|
+
headers: {
|
|
33
|
+
'User-Agent': 'rift-npm-installer/0.1.0',
|
|
34
|
+
'Accept': 'application/vnd.github+json',
|
|
35
|
+
},
|
|
36
|
+
}, res => {
|
|
37
|
+
let data = '';
|
|
38
|
+
res.on('data', c => data += c);
|
|
39
|
+
res.on('end', () => {
|
|
40
|
+
try { resolve(JSON.parse(data)); }
|
|
41
|
+
catch (e) { reject(new Error(`JSON parse error: ${e.message}`)); }
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
req.on('error', reject);
|
|
45
|
+
req.end();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function downloadFile(url, destPath) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const file = fs.createWriteStream(destPath);
|
|
52
|
+
function doGet(u) {
|
|
53
|
+
const proto = u.startsWith('https') ? https : http;
|
|
54
|
+
proto.get(u, { headers: { 'User-Agent': 'rift-npm-installer/0.1.0' } }, res => {
|
|
55
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
56
|
+
return doGet(res.headers.location);
|
|
57
|
+
}
|
|
58
|
+
if (res.statusCode !== 200) {
|
|
59
|
+
return reject(new Error(`HTTP ${res.statusCode} from ${u}`));
|
|
60
|
+
}
|
|
61
|
+
res.pipe(file);
|
|
62
|
+
file.on('finish', () => file.close(resolve));
|
|
63
|
+
}).on('error', reject);
|
|
64
|
+
}
|
|
65
|
+
doGet(url);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function main() {
|
|
70
|
+
if (process.platform !== 'win32') {
|
|
71
|
+
warn('Rift is Windows-only. Skipping install on ' + process.platform);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
log('Fetching latest release info from GitHub...');
|
|
76
|
+
let release;
|
|
77
|
+
try {
|
|
78
|
+
release = await fetchJson(`https://api.github.com/repos/${REPO}/releases/latest`);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
err(`Failed to fetch release info: ${e.message}`);
|
|
81
|
+
err('You can manually download rift.exe from: https://github.com/' + REPO + '/releases/latest');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const assets = release.assets || [];
|
|
86
|
+
const exeAsset = assets.find(a => a.name === EXE_ASSET);
|
|
87
|
+
const shaAsset = assets.find(a => a.name === SHA_ASSET);
|
|
88
|
+
|
|
89
|
+
if (!exeAsset) {
|
|
90
|
+
err(`No ${EXE_ASSET} found in release ${release.tag_name}. Please install manually.`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const destDir = getDestDir();
|
|
95
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
96
|
+
const destExe = path.join(destDir, EXE_ASSET);
|
|
97
|
+
|
|
98
|
+
log(`Downloading ${EXE_ASSET} v${release.tag_name}...`);
|
|
99
|
+
await downloadFile(exeAsset.browser_download_url, destExe);
|
|
100
|
+
|
|
101
|
+
// Verify SHA-256 if available
|
|
102
|
+
if (shaAsset) {
|
|
103
|
+
try {
|
|
104
|
+
const tmpSha = path.join(os.tmpdir(), SHA_ASSET);
|
|
105
|
+
await downloadFile(shaAsset.browser_download_url, tmpSha);
|
|
106
|
+
const expected = fs.readFileSync(tmpSha, 'utf8').trim().split(/\s+/)[0].toLowerCase();
|
|
107
|
+
const actual = crypto.createHash('sha256').update(fs.readFileSync(destExe)).digest('hex');
|
|
108
|
+
if (expected !== actual) {
|
|
109
|
+
err(`SHA-256 mismatch! Expected ${expected}, got ${actual}. Deleting download.`);
|
|
110
|
+
fs.unlinkSync(destExe);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
log('SHA-256 verified ✓');
|
|
114
|
+
fs.unlinkSync(tmpSha);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
warn(`Could not verify checksum: ${e.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
log(`Installed to: ${destExe}`);
|
|
121
|
+
log('Run \x1b[32mrift hatch\x1b[0m to meet your companion!');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
main().catch(e => {
|
|
125
|
+
err(`Unexpected error: ${e.message}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
});
|