| #!/usr/bin/env node |
| |
| 'use strict'; |
| const readline = require('readline'); |
| const path = require('path'); |
| const { spawnSync, spawn } = require('child_process'); |
|
|
| const FORGE = path.join(__dirname, '..'); |
| const ENV = { ...process.env, PYTHONIOENCODING: 'utf-8' }; |
| const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); |
| const pending = [], waiting = []; |
| let closed = false; |
| rl.on('line', l => { const w = waiting.shift(); if (w) w(l); else pending.push(l); }); |
| rl.on('close', () => { closed = true; while (waiting.length) waiting.shift()(null); }); |
| const ask = q => { process.stdout.write(q); return new Promise(r => { const b = pending.shift(); if (b !== undefined) { console.log(b); r(b); } else if (closed) r(null); else waiting.push(r); }); }; |
|
|
| function forge(args) { |
| rl.pause(); |
| spawnSync('node', [path.join(FORGE, 'forge.js'), ...args], { stdio: 'inherit', env: ENV, shell: true }); |
| rl.resume(); |
| } |
|
|
| (async () => { |
| while (true) { |
| console.log('\n=============================================='); |
| console.log(' NPU-FORGE β your models, on your NPU'); |
| console.log('=============================================='); |
| console.log(' 1. doctor β NPU / driver / registry health'); |
| console.log(' 2. list β models + your forge customs'); |
| console.log(' 3. register β activate customs (UAC, one click)'); |
| console.log(' 4. serve <tag> β start an NPU server'); |
| console.log(' t. tune β how to fine-tune your own (guide)'); |
| console.log(' q. quit'); |
| const pick = ((await ask('\nPick: ')) || '').trim().toLowerCase(); |
| if (pick === null || pick === 'q' || pick === '') break; |
| if (pick === '1') forge(['doctor']); |
| else if (pick === '2') forge(['list']); |
| else if (pick === '3') { |
| spawn('cmd', ['/c', 'start', '', path.join(FORGE, 'register-admin.bat')], { detached: true }); |
| console.log('(UAC window launched β click Yes; results land in register-cmd-log.txt)'); |
| } |
| else if (pick.startsWith('4')) { |
| const tag = pick.split(/\s+/)[1] || (await ask('model tag (e.g. llama3.2:1b): ')) || ''; |
| if (tag.trim()) { console.log('(Ctrl+C stops the server and returns here)'); forge(['serve', tag.trim()]); } |
| } |
| else if (pick === 't') { |
| console.log('\nFine-tune your own voice onto the NPU (needs a free modal.com account):'); |
| console.log(' 1. Make chats.jsonl β one line per exchange:'); |
| console.log(' {"messages":[{"role":"user","content":"..."},{"role":"assistant","content":"..."}]}'); |
| console.log(' 2. node forge.js tune chats.jsonl --name myvoice'); |
| console.log(' 3. node forge.js register (one UAC click)'); |
| console.log(' 4. flm run myvoice-forge:1b β your voice, on your NPU.'); |
| console.log(' (~3 minutes and ~$0.10 of cloud time for a 1B; see README.md)'); |
| } |
| } |
| rl.close(); |
| })(); |
|
|