198 lines
6.5 KiB
JavaScript
198 lines
6.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Make qvac/worker.entry.mjs portable for packaged Electron / CI clients.
|
|
*
|
|
* `@qvac/sdk` bundleSdk writes absolute `file:///…/node_modules/@qvac/…` imports
|
|
* so bare-pack can resolve addons on the *build* machine. Those paths break as
|
|
* soon as the app is shipped elsewhere → Bare worker dies →
|
|
* "RPC initialization timed out after 30000ms — the worker process may have failed to start"
|
|
*
|
|
* This rewrites those URLs to relative paths from qvac/worker.entry.mjs, e.g.
|
|
* ../node_modules/@qvac/sdk/dist/server/worker-core.js
|
|
*
|
|
* Usage:
|
|
* node scripts/rewrite-qvac-worker-entry.cjs
|
|
* node scripts/rewrite-qvac-worker-entry.cjs --entry /path/to/qvac/worker.entry.mjs
|
|
* node scripts/rewrite-qvac-worker-entry.cjs --root /path/to/app
|
|
*/
|
|
'use strict'
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
const { pathToFileURL, fileURLToPath } = require('url')
|
|
|
|
const rootDefault = path.resolve(__dirname, '..')
|
|
|
|
/**
|
|
* @param {string} entryPath absolute path to worker.entry.mjs
|
|
* @returns {{ rewritten: number, path: string, ok: boolean, reason?: string }}
|
|
*/
|
|
function rewriteWorkerEntry(entryPath) {
|
|
if (!fs.existsSync(entryPath)) {
|
|
return { rewritten: 0, path: entryPath, ok: false, reason: 'missing' }
|
|
}
|
|
const original = fs.readFileSync(entryPath, 'utf8')
|
|
const entryDir = path.dirname(entryPath)
|
|
|
|
let count = 0
|
|
const next = original.replace(/file:\/\/\/[^\s"'`]+|file:\/\/[^\s"'`]+/g, (href) => {
|
|
try {
|
|
let filePath
|
|
try {
|
|
filePath = fileURLToPath(href)
|
|
} catch {
|
|
return href
|
|
}
|
|
if (!fs.existsSync(filePath)) {
|
|
// Still rewrite if it points under a known node_modules layout
|
|
// (CI may rewrite before files are fully staged)
|
|
}
|
|
let rel = path.relative(entryDir, filePath)
|
|
if (!rel || rel === path.basename(filePath)) {
|
|
// same dir
|
|
rel = `./${path.basename(filePath)}`
|
|
} else if (!rel.startsWith('.') && !path.isAbsolute(rel)) {
|
|
rel = `./${rel}`
|
|
}
|
|
// ESM wants POSIX separators
|
|
rel = rel.split(path.sep).join('/')
|
|
// Prefer bare relative specifier (no file://) — Bare resolves these
|
|
count++
|
|
return rel
|
|
} catch {
|
|
return href
|
|
}
|
|
})
|
|
|
|
if (count === 0 && !/file:\/\//.test(original)) {
|
|
// Already portable — ensure relative @qvac paths exist or write canonical entry
|
|
if (
|
|
original.includes('../node_modules/@qvac/sdk/') ||
|
|
original.includes('@qvac/sdk/')
|
|
) {
|
|
return { rewritten: 0, path: entryPath, ok: true, reason: 'already-portable' }
|
|
}
|
|
}
|
|
|
|
if (next !== original) {
|
|
fs.writeFileSync(entryPath, next, 'utf8')
|
|
}
|
|
|
|
// Guard: no absolute build-machine paths left
|
|
if (/file:\/\/\/Users\/|file:\/\/\/home\/|file:\/\/\/[A-Za-z]:\//.test(next)) {
|
|
return {
|
|
rewritten: count,
|
|
path: entryPath,
|
|
ok: false,
|
|
reason: 'absolute file:// paths remain after rewrite',
|
|
}
|
|
}
|
|
|
|
return { rewritten: count, path: entryPath, ok: true }
|
|
}
|
|
|
|
/**
|
|
* Write a known-good portable LLM-only worker entry (matches qvac.config.json).
|
|
* Used when rewrite fails or entry is missing.
|
|
* @param {string} entryPath
|
|
*/
|
|
function writePortableLlmWorkerEntry(entryPath) {
|
|
const content = `/**
|
|
* QVAC SDK Worker Entry (portable)
|
|
* Relative imports only — safe for packaged Electron / CI clients.
|
|
* Plugins: 1 — @qvac/sdk/llamacpp-completion/plugin
|
|
*
|
|
* Regenerated by scripts/rewrite-qvac-worker-entry.cjs
|
|
* (bundleSdk emits absolute file:// URLs that break off the build machine)
|
|
*/
|
|
|
|
import { initializeWorkerCore, ensureRPCSetup } from '../node_modules/@qvac/sdk/dist/server/worker-core.js'
|
|
import { registerPlugin } from '../node_modules/@qvac/sdk/dist/server/plugins/index.js'
|
|
import { getServerLogger } from '../node_modules/@qvac/sdk/dist/logging/index.js'
|
|
import { llmPlugin } from '../node_modules/@qvac/sdk/dist/server/bare/plugins/llamacpp-completion/plugin.js'
|
|
|
|
const { hasRPCConfig } = initializeWorkerCore()
|
|
|
|
const logger = getServerLogger()
|
|
logger.info('🐻 QVAC Worker (portable bundle)')
|
|
logger.info('📦 Plugins: 1 (llamacpp-completion)')
|
|
|
|
registerPlugin(llmPlugin)
|
|
|
|
if (hasRPCConfig) {
|
|
ensureRPCSetup()
|
|
}
|
|
`
|
|
fs.mkdirSync(path.dirname(entryPath), { recursive: true })
|
|
fs.writeFileSync(entryPath, content, 'utf8')
|
|
return entryPath
|
|
}
|
|
|
|
/**
|
|
* @param {{ root?: string, entry?: string, forcePortable?: boolean }} [opts]
|
|
*/
|
|
function ensurePortableWorkerEntry(opts = {}) {
|
|
const root = opts.root || rootDefault
|
|
const entryPath =
|
|
opts.entry || path.join(root, 'qvac', 'worker.entry.mjs')
|
|
|
|
if (opts.forcePortable || !fs.existsSync(entryPath)) {
|
|
writePortableLlmWorkerEntry(entryPath)
|
|
console.log(`[rewrite-qvac-worker] wrote portable entry: ${entryPath}`)
|
|
return { ok: true, path: entryPath, rewritten: -1, reason: 'wrote-portable' }
|
|
}
|
|
|
|
const result = rewriteWorkerEntry(entryPath)
|
|
if (!result.ok && result.reason !== 'missing') {
|
|
console.warn(
|
|
`[rewrite-qvac-worker] rewrite incomplete (${result.reason}) — writing portable LLM entry`
|
|
)
|
|
writePortableLlmWorkerEntry(entryPath)
|
|
return { ok: true, path: entryPath, rewritten: result.rewritten, reason: 'fallback-portable' }
|
|
}
|
|
if (result.reason === 'missing') {
|
|
writePortableLlmWorkerEntry(entryPath)
|
|
return { ok: true, path: entryPath, rewritten: -1, reason: 'wrote-portable' }
|
|
}
|
|
|
|
console.log(
|
|
`[rewrite-qvac-worker] ${entryPath}: rewritten=${result.rewritten} (${result.reason || 'ok'})`
|
|
)
|
|
return result
|
|
}
|
|
|
|
function parseArgs(argv) {
|
|
/** @type {{ root?: string, entry?: string, forcePortable?: boolean }} */
|
|
const opts = {}
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const a = argv[i]
|
|
if (a === '--root' && argv[i + 1]) opts.root = path.resolve(argv[++i])
|
|
else if (a.startsWith('--root=')) opts.root = path.resolve(a.slice(7))
|
|
else if (a === '--entry' && argv[i + 1]) opts.entry = path.resolve(argv[++i])
|
|
else if (a.startsWith('--entry=')) opts.entry = path.resolve(a.slice(8))
|
|
else if (a === '--force-portable') opts.forcePortable = true
|
|
else if (a === '--help' || a === '-h') {
|
|
console.log(
|
|
'Usage: node scripts/rewrite-qvac-worker-entry.cjs [--root DIR] [--entry FILE] [--force-portable]'
|
|
)
|
|
process.exit(0)
|
|
}
|
|
}
|
|
return opts
|
|
}
|
|
|
|
if (require.main === module) {
|
|
const result = ensurePortableWorkerEntry(parseArgs(process.argv.slice(2)))
|
|
if (!result.ok) {
|
|
console.error('[rewrite-qvac-worker] FAILED', result)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
rewriteWorkerEntry,
|
|
writePortableLlmWorkerEntry,
|
|
ensurePortableWorkerEntry,
|
|
pathToFileURL,
|
|
}
|