letfixit 0.1.2 → 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/package.json +4 -1
- package/scripts/clean-model.js +27 -0
- package/scripts/setup-model.js +1 -1
- package/scripts/verify-chunk.js +73 -0
- package/server/index.js +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letfixit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "LetFixIt — AI-based realtime performance analysis & suggestions for Flutter, Android & iOS, powered by the local Ultron n0-beta model.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"flutter",
|
|
@@ -59,7 +59,10 @@
|
|
|
59
59
|
"setup:force": "node scripts/setup-model.js --force",
|
|
60
60
|
"encrypt-model": "node scripts/encrypt-model.js",
|
|
61
61
|
"fill-manifest": "node scripts/fill-manifest.js",
|
|
62
|
+
"verify": "node scripts/verify-chunk.js",
|
|
63
|
+
"clean": "node scripts/clean-model.js",
|
|
62
64
|
"postinstall": "node scripts/setup-model.js",
|
|
65
|
+
"preuninstall": "node scripts/clean-model.js",
|
|
63
66
|
"prepublishOnly": "npm run build"
|
|
64
67
|
},
|
|
65
68
|
"dependencies": {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ── Cleanup ───────────────────────────────────────────────────────────────────
|
|
3
|
+
// Removes the downloaded model and any temp/partial files so nothing is left on
|
|
4
|
+
// disk. Runs as a `preuninstall` hook (best-effort — npm's uninstall hooks are
|
|
5
|
+
// unreliable across versions) and is also exposed as `letfixit clean` /
|
|
6
|
+
// `npm run clean` for a guaranteed manual wipe.
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const mc = require('../server/model_config');
|
|
9
|
+
|
|
10
|
+
function clean() {
|
|
11
|
+
let removed = false;
|
|
12
|
+
try {
|
|
13
|
+
if (fs.existsSync(mc.MODEL_DIR)) {
|
|
14
|
+
const gb = mc.sizeGB();
|
|
15
|
+
fs.rmSync(mc.MODEL_DIR, { recursive: true, force: true }); // model + .tmp partials
|
|
16
|
+
console.log(` ✓ Removed ${mc.MODEL_NAME}${gb ? ` (${gb} GB)` : ''} — ${mc.MODEL_DIR}`);
|
|
17
|
+
removed = true;
|
|
18
|
+
}
|
|
19
|
+
} catch (e) {
|
|
20
|
+
// Never fail an uninstall on cleanup trouble.
|
|
21
|
+
console.log(` ⚠ cleanup note: ${e.message}`);
|
|
22
|
+
}
|
|
23
|
+
if (!removed) console.log(' ✓ No model files to remove.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (require.main === module) clean();
|
|
27
|
+
module.exports = { clean };
|
package/scripts/setup-model.js
CHANGED
|
@@ -142,7 +142,7 @@ async function fetchChunk(ch, encPath, baseLabel) {
|
|
|
142
142
|
async function resolveKey() {
|
|
143
143
|
if (process.env.PARITYFIX_MODEL_KEY) return process.env.PARITYFIX_MODEL_KEY.trim();
|
|
144
144
|
if (!process.stdin.isTTY) return null; // can't prompt (postinstall in CI, piped stdin)
|
|
145
|
-
const k = await promptHidden(
|
|
145
|
+
const k = await promptHidden(' 🔑 Enter acceptance key: ');
|
|
146
146
|
return k || null;
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ── Quick verify ──────────────────────────────────────────────────────────────
|
|
3
|
+
// Downloads ONLY the first chunk from the manifest and decrypts it, to confirm
|
|
4
|
+
// the acceptance key + hosted chunks + manifest all line up — without pulling the
|
|
5
|
+
// full ~2.6 GB model.
|
|
6
|
+
//
|
|
7
|
+
// PARITYFIX_MODEL_KEY=<key> npm run verify (or: node scripts/verify-chunk.js)
|
|
8
|
+
// node scripts/verify-chunk.js <key> (pass the key as an argument)
|
|
9
|
+
// If no key is given it prompts (hidden input).
|
|
10
|
+
require('./load-env')();
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const readline = require('readline');
|
|
15
|
+
const mc = require('../server/model_config');
|
|
16
|
+
const { deriveKey } = require('./model_crypto');
|
|
17
|
+
const sm = require('./setup-model'); // fetchChunk, decryptChunk, sha256File
|
|
18
|
+
|
|
19
|
+
const C = { reset: '\x1b[0m', mag: '\x1b[95m', grn: '\x1b[92m', red: '\x1b[91m', dim: '\x1b[2m', cyan: '\x1b[96m' };
|
|
20
|
+
const c = (x, s) => `${x}${s}${C.reset}`;
|
|
21
|
+
|
|
22
|
+
function promptHidden(q) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
process.stdout.write(c(C.mag, q));
|
|
25
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
26
|
+
rl._writeToOutput = () => {};
|
|
27
|
+
rl.question('', (a) => { rl.close(); process.stdout.write('\n'); resolve(a.trim()); });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function run(keyArg) {
|
|
32
|
+
const manifest = mc.loadManifest();
|
|
33
|
+
if (!manifest || !manifest.chunks || !manifest.chunks.length) {
|
|
34
|
+
console.error(c(C.red, ' ❌ No manifest found (config/model.manifest.json).'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let key = process.env.PARITYFIX_MODEL_KEY || keyArg;
|
|
39
|
+
if (!key && process.stdin.isTTY) key = await promptHidden(' 🔑 Enter acceptance key: ');
|
|
40
|
+
if (!key) {
|
|
41
|
+
console.error(c(C.red, ' ❌ No key. Set PARITYFIX_MODEL_KEY, pass it as an argument, or run in a terminal.'));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ch = [...manifest.chunks].sort((a, b) => a.index - b.index)[0];
|
|
46
|
+
const tmp = path.join(os.tmpdir(), 'letfixit-verify');
|
|
47
|
+
fs.mkdirSync(tmp, { recursive: true });
|
|
48
|
+
const enc = path.join(tmp, 'chunk0.enc');
|
|
49
|
+
const dec = path.join(tmp, 'chunk0.dec');
|
|
50
|
+
|
|
51
|
+
console.log(c(C.dim, ` Verifying chunk 0 of ${manifest.chunks.length} — ${ch.url}\n`));
|
|
52
|
+
let ok = false;
|
|
53
|
+
try {
|
|
54
|
+
await sm.fetchChunk(ch, enc, c(C.cyan, 'chunk 0')); // downloads + verifies sha256
|
|
55
|
+
await sm.decryptChunk(enc, dec, deriveKey(key.trim(), manifest.salt), ch.iv, ch.authTag);
|
|
56
|
+
const size = fs.statSync(dec).size;
|
|
57
|
+
ok = size === (ch.bytes || size);
|
|
58
|
+
console.log('\n ' + c(C.grn, '✅ VERIFIED') + c(C.dim, ` — download + checksum + decrypt all passed (${(size / 1048576).toFixed(1)} MB plaintext).`));
|
|
59
|
+
console.log(' ' + c(C.dim, 'Your acceptance key and hosted chunks are correct; the full setup will work.'));
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (/authenticate|unable to authenticate|bad decrypt/i.test(e.message)) {
|
|
62
|
+
console.log('\n ' + c(C.red, '❌ Decryption failed — the acceptance key is incorrect for these chunks.'));
|
|
63
|
+
} else {
|
|
64
|
+
console.log('\n ' + c(C.red, `❌ Verify failed: ${e.message}`));
|
|
65
|
+
}
|
|
66
|
+
} finally {
|
|
67
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
process.exit(ok ? 0 : 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (require.main === module) run(process.argv[2]); // node scripts/verify-chunk.js [key]
|
|
73
|
+
module.exports = { run };
|
package/server/index.js
CHANGED
|
@@ -642,6 +642,8 @@ function printHelp() {
|
|
|
642
642
|
${col(C.cyan, 'letfixit')} Launch the analyzer (first run installs ${model.MODEL_NAME})
|
|
643
643
|
${col(C.cyan, 'letfixit setup')} Download & set up the model (prompts for the decryption key)
|
|
644
644
|
${col(C.cyan, 'letfixit setup --force')} Re-download / reinstall the model
|
|
645
|
+
${col(C.cyan, 'letfixit verify')} Quick test: download + decrypt only chunk 0
|
|
646
|
+
${col(C.cyan, 'letfixit clean')} Remove the downloaded model (free disk / no footprint)
|
|
645
647
|
${col(C.cyan, 'letfixit version')} Print the installed version (or --version / -v)
|
|
646
648
|
${col(C.cyan, 'letfixit --help')} Show this help
|
|
647
649
|
|
|
@@ -665,6 +667,12 @@ if (cmd === '--version' || cmd === '-v') {
|
|
|
665
667
|
require('../scripts/setup-model').setup()
|
|
666
668
|
.then(() => process.exit(0))
|
|
667
669
|
.catch((e) => { console.error(' ❌ setup failed:', e.message); process.exit(1); });
|
|
670
|
+
} else if (cmd === 'verify') {
|
|
671
|
+
// Quick test: download + decrypt only the first chunk to validate the key/host.
|
|
672
|
+
require('../scripts/verify-chunk').run(process.argv[3]);
|
|
673
|
+
} else if (cmd === 'clean') {
|
|
674
|
+
// Remove the downloaded model + temp files (no footprint).
|
|
675
|
+
require('../scripts/clean-model').clean();
|
|
668
676
|
} else {
|
|
669
677
|
main(); // default: launch the analyzer
|
|
670
678
|
}
|