codeburn/app/scripts/after-pack.cjs
iamtoruk 4a100d9d06 feat(app): bundle the CLI inside the packaged app
The packaged app resolved whatever codeburn was on PATH — for real
users the published npm release, which predates every JSON surface the
app calls. The app now ships its own CLI copy under resources/cli
(staged production node_modules tree; tsup output is not
self-contained) and spawns it with Electron's own binary via
ELECTRON_RUN_AS_NODE. No install prerequisite remains.

- resolution order: CODEBURN_BIN, dev repo CLI, bundled, persisted
  path, PATH search
- launch.js shim strips the extra argv element commander mis-slices
  under packaged Electron-as-node (process.versions.electron is set),
  which reproduced the 'too many arguments' class of error
- afterPack hook copies the staged tree (extraResources runs
  node_modules through the production-dep filter and ships it empty);
  lands before signing, signature verified intact
- packaging always restages from src via root build:cli (tsup only,
  no network); ~12MB compressed per artifact

Verified on the built app: Electron-as-node CLI emits current JSON
(providerDetails, currency), and a minimal-PATH GUI launch spawns the
bundled CLI with zero external dependencies. 290/290.
2026-07-16 09:27:46 -07:00

30 lines
1.4 KiB
JavaScript

// Copy the staged self-contained CLI (app/build/cli, produced by stage-cli.mjs)
// into the packaged app's resources/cli directory.
//
// This is done here rather than via `extraResources` because electron-builder
// routes every `node_modules` directory it copies through its production-
// dependency filter, which keeps only the *app's* own deps — so the bundled
// CLI's dependency tree gets stripped out of an `extraResources` copy. afterPack
// runs after packaging but before code signing, so the files we add land inside
// the app's signature. The CLI's deps are pure JS (no native bindings), so the
// same tree is valid for every arch.
const { join } = require('node:path')
const { cpSync, existsSync } = require('node:fs')
exports.default = async function afterPack(context) {
const { appOutDir, electronPlatformName, packager } = context
const src = join(__dirname, '..', 'build', 'cli')
if (!existsSync(join(src, 'node_modules'))) {
throw new Error(`after-pack: ${src}/node_modules is missing — run "npm run stage-cli" first`)
}
const resources =
electronPlatformName === 'darwin'
? join(appOutDir, `${packager.appInfo.productFilename}.app`, 'Contents', 'Resources')
: join(appOutDir, 'resources')
const dest = join(resources, 'cli')
cpSync(src, dest, { recursive: true, dereference: true })
console.log(`after-pack: bundled CLI copied -> ${dest}`)
}