Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Commander migration
Standalone Executable
Dependency ReviewThe following issues were found:
License Issues.github/workflows/sea-build-sign.yml
package.json
OpenSSF Scorecard
Scanned Files
|
|
❌ The last analysis has failed. |
There was a problem hiding this comment.
Pull request overview
This PR adds a cross-platform build/signing workflow for distributing VIP CLI as a Node SEA (single executable), introduces a small SEA runtime filesystem/bootstrap layer for dependencies/assets, and migrates the CLI parser from args to a Commander-backed compatibility wrapper.
Changes:
- Add SEA build tooling (
helpers/build-sea.js), runtime bootstrap/dispatch code, and a GitHub Actions workflow to build/sign/upload SEA artifacts. - Migrate CLI parsing from
argsto a Commander-based compatibility wrapper while preserving existingcommand(opts).option(...).argv(...)call sites. - Update dev-env/Lando integration to support SEA runtime dependency resolution and improve startup/readiness checks.
Reviewed changes
Copilot reviewed 19 out of 23 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/sea-build-sign.yml |
New workflow to build SEA artifacts on macOS/Linux/Windows + WSL and optionally sign them. |
helpers/build-sea.js |
New Node 22 SEA bundling + blob injection script (esbuild + postject) and embeds runtime assets. |
src/lib/dev-environment/lando-loader.ts |
New helper to require() Lando from SEA runtime filesystem when present. |
src/lib/dev-environment/dev-environment-lando.ts |
Switch Lando imports to runtime-loading + add runtime mode info to logging banner. |
src/lib/dev-environment/dev-environment-core.ts |
Load lando/lib/utils via runtime loader; add SEA asset template rendering + readiness polling on start. |
src/lib/dev-environment/dev-environment-cli.ts |
Load Lando formatters through runtime loader to support SEA packaging. |
src/lib/cli/sea-runtime.js |
New SEA bootstrap to extract embedded node_modules archive and adjust module resolution. |
src/lib/cli/sea-dispatch.js |
New argv-to-internal-bin resolver/rewriter for SEA single-binary dispatch. |
src/lib/cli/runtime-mode.ts |
New helper to detect/label SEA vs node-script runtime. |
src/lib/cli/internal-bin-loader.js |
New dynamic loader for internal bins in SEA mode. |
src/lib/cli/exit.ts |
Include runtime mode in debug footer output. |
src/lib/cli/config.ts |
Fallback to bundled publish config when SEA build lacks on-disk config files. |
src/lib/cli/command.js |
Replace args with Commander-backed compatibility wrapper + SEA-aware subcommand dispatch. |
src/commands/dev-env-sync-sql.ts |
Convert Lando import to type-only. |
src/bin/vip.js |
Add SEA target-bin execution path and refactor login flow into a helper. |
src/bin/vip-sea.js |
New SEA entrypoint that prepares runtime filesystem then loads the main CLI. |
src/bin/vip-dev-env-exec.js |
Add bounded readiness polling before treating env as down. |
package.json |
Add build:sea, add commander, esbuild, postject, remove args, fix prettier glob. |
npm-shrinkwrap.json |
Lockfile updates reflecting dependency changes for Commander/SEA tooling. |
docs/SEA-BUILD-SIGNING.md |
New runbook for SEA build/signing across platforms and CI workflow notes. |
docs/COMMANDER-MIGRATION.md |
New migration notes, preserved behaviors, and follow-up work items. |
CLAUDE.md |
Link to SEA build/signing runbook. |
AGENTS.md |
Add SEA packaging notes and pitfalls for future contributors/agents. |
Files not reviewed (1)
- npm-shrinkwrap.json: Language not supported
| if ( sea?.isSea?.() && sea.getAsset ) { | ||
| const template = sea.getAsset( assetKey, 'utf8' ); | ||
| if ( typeof template === 'string' ) { | ||
| return ejs.render( template, templateData ); | ||
| } |
There was a problem hiding this comment.
renderTemplateFile() treats SEA assets as usable only when sea.getAsset() returns a string, but your SeaModule type allows ArrayBuffer. If getAsset() returns an ArrayBuffer here, the code falls back to ejs.renderFile(filePath), which will fail in SEA when templates aren’t on disk. Convert ArrayBuffer/Buffer to a UTF-8 string and render it instead.
| const childScriptPath = extension | ||
| ? `${ baseScriptPath }-${ subcommand }${ extension }` | ||
| : `${ baseScriptPath }-${ subcommand }`; | ||
| const aliasFromRawArgv = argv.slice( 2 ).find( arg => isAlias( arg ) ); |
There was a problem hiding this comment.
executeSubcommand() searches for an env alias with argv.slice(2).find(isAlias) without respecting the -- boundary. This differs from parseEnvAliasFromArgv() (which ignores aliases after --) and can incorrectly treat positional args like -- ... @foo as an alias. Limit the alias search to args before -- when constructing childArgs.
| const aliasFromRawArgv = argv.slice( 2 ).find( arg => isAlias( arg ) ); | |
| const doubleDashIndex = argv.indexOf( '--' ); | |
| const rawArgsBeforeDoubleDash = | |
| doubleDashIndex === -1 ? argv.slice( 2 ) : argv.slice( 2, doubleDashIndex ); | |
| const aliasFromRawArgv = rawArgsBeforeDoubleDash.find( arg => isAlias( arg ) ); |
|
|
||
| let flags = `--${ normalizedLongName }`; | ||
| if ( usesOptionalValue ) { | ||
| flags += ' [value]'; | ||
| } |
There was a problem hiding this comment.
Non-boolean options are defined as --name [value] (optional value). In Commander, passing the flag without a value sets the option to true, which can leak into downstream logic (e.g. --format becomes boolean and isn’t currently validated). Use a required value (<value>) for non-boolean options (matching historical args behavior) and/or add validation for opts.format similar to opts.app/opts.env.
| vip: () => import( '../../bin/vip' ), | ||
| 'vip-app': () => import( '../../bin/vip-app' ), | ||
| 'vip-app-deploy': () => import( '../../bin/vip-app-deploy' ), | ||
| 'vip-app-deploy-validate': () => import( '../../bin/vip-app-deploy-validate' ), | ||
| 'vip-app-list': () => import( '../../bin/vip-app-list' ), | ||
| 'vip-backup': () => import( '../../bin/vip-backup' ), | ||
| 'vip-backup-db': () => import( '../../bin/vip-backup-db' ), | ||
| 'vip-cache': () => import( '../../bin/vip-cache' ), | ||
| 'vip-cache-purge-url': () => import( '../../bin/vip-cache-purge-url' ), | ||
| 'vip-config': () => import( '../../bin/vip-config' ), | ||
| 'vip-config-envvar': () => import( '../../bin/vip-config-envvar' ), | ||
| 'vip-config-envvar-delete': () => import( '../../bin/vip-config-envvar-delete' ), | ||
| 'vip-config-envvar-get': () => import( '../../bin/vip-config-envvar-get' ), | ||
| 'vip-config-envvar-get-all': () => import( '../../bin/vip-config-envvar-get-all' ), | ||
| 'vip-config-envvar-list': () => import( '../../bin/vip-config-envvar-list' ), | ||
| 'vip-config-envvar-set': () => import( '../../bin/vip-config-envvar-set' ), | ||
| 'vip-config-software': () => import( '../../bin/vip-config-software' ), | ||
| 'vip-config-software-get': () => import( '../../bin/vip-config-software-get' ), | ||
| 'vip-config-software-update': () => import( '../../bin/vip-config-software-update' ), | ||
| 'vip-db': () => import( '../../bin/vip-db' ), | ||
| 'vip-db-phpmyadmin': () => import( '../../bin/vip-db-phpmyadmin' ), | ||
| 'vip-dev-env': () => import( '../../bin/vip-dev-env' ), | ||
| 'vip-dev-env-create': () => import( '../../bin/vip-dev-env-create' ), | ||
| 'vip-dev-env-destroy': () => import( '../../bin/vip-dev-env-destroy' ), | ||
| 'vip-dev-env-envvar': () => import( '../../bin/vip-dev-env-envvar' ), | ||
| 'vip-dev-env-envvar-delete': () => import( '../../bin/vip-dev-env-envvar-delete' ), | ||
| 'vip-dev-env-envvar-get': () => import( '../../bin/vip-dev-env-envvar-get' ), | ||
| 'vip-dev-env-envvar-get-all': () => import( '../../bin/vip-dev-env-envvar-get-all' ), | ||
| 'vip-dev-env-envvar-list': () => import( '../../bin/vip-dev-env-envvar-list' ), | ||
| 'vip-dev-env-envvar-set': () => import( '../../bin/vip-dev-env-envvar-set' ), | ||
| 'vip-dev-env-exec': () => import( '../../bin/vip-dev-env-exec' ), | ||
| 'vip-dev-env-import': () => import( '../../bin/vip-dev-env-import' ), | ||
| 'vip-dev-env-import-media': () => import( '../../bin/vip-dev-env-import-media' ), | ||
| 'vip-dev-env-import-sql': () => import( '../../bin/vip-dev-env-import-sql' ), | ||
| 'vip-dev-env-info': () => import( '../../bin/vip-dev-env-info' ), | ||
| 'vip-dev-env-list': () => import( '../../bin/vip-dev-env-list' ), | ||
| 'vip-dev-env-logs': () => import( '../../bin/vip-dev-env-logs' ), | ||
| 'vip-dev-env-purge': () => import( '../../bin/vip-dev-env-purge' ), | ||
| 'vip-dev-env-shell': () => import( '../../bin/vip-dev-env-shell' ), | ||
| 'vip-dev-env-start': () => import( '../../bin/vip-dev-env-start' ), | ||
| 'vip-dev-env-stop': () => import( '../../bin/vip-dev-env-stop' ), | ||
| 'vip-dev-env-sync': () => import( '../../bin/vip-dev-env-sync' ), | ||
| 'vip-dev-env-sync-sql': () => import( '../../bin/vip-dev-env-sync-sql' ), | ||
| 'vip-dev-env-update': () => import( '../../bin/vip-dev-env-update' ), | ||
| 'vip-export': () => import( '../../bin/vip-export' ), | ||
| 'vip-export-sql': () => import( '../../bin/vip-export-sql' ), | ||
| 'vip-import': () => import( '../../bin/vip-import' ), | ||
| 'vip-import-media': () => import( '../../bin/vip-import-media' ), | ||
| 'vip-import-media-abort': () => import( '../../bin/vip-import-media-abort' ), | ||
| 'vip-import-media-status': () => import( '../../bin/vip-import-media-status' ), | ||
| 'vip-import-sql': () => import( '../../bin/vip-import-sql' ), | ||
| 'vip-import-sql-status': () => import( '../../bin/vip-import-sql-status' ), | ||
| 'vip-import-validate-files': () => import( '../../bin/vip-import-validate-files' ), | ||
| 'vip-import-validate-sql': () => import( '../../bin/vip-import-validate-sql' ), | ||
| 'vip-logout': () => import( '../../bin/vip-logout' ), | ||
| 'vip-logs': () => import( '../../bin/vip-logs' ), | ||
| 'vip-search-replace': () => import( '../../bin/vip-search-replace' ), | ||
| 'vip-slowlogs': () => import( '../../bin/vip-slowlogs' ), | ||
| 'vip-sync': () => import( '../../bin/vip-sync' ), | ||
| 'vip-whoami': () => import( '../../bin/vip-whoami' ), | ||
| 'vip-wp': () => import( '../../bin/vip-wp' ), |
There was a problem hiding this comment.
internal-bin-loader uses relative dynamic imports without file extensions (e.g. import('../../bin/vip')). Node’s import() uses ESM-style resolution for relative specifiers, which typically requires explicit extensions; this can break if this codepath is ever executed outside the esbuild-bundled SEA context. Consider adding explicit .js extensions (or using new URL(..., import.meta.url)) to make the loader robust in plain Node execution too.
| vip: () => import( '../../bin/vip' ), | |
| 'vip-app': () => import( '../../bin/vip-app' ), | |
| 'vip-app-deploy': () => import( '../../bin/vip-app-deploy' ), | |
| 'vip-app-deploy-validate': () => import( '../../bin/vip-app-deploy-validate' ), | |
| 'vip-app-list': () => import( '../../bin/vip-app-list' ), | |
| 'vip-backup': () => import( '../../bin/vip-backup' ), | |
| 'vip-backup-db': () => import( '../../bin/vip-backup-db' ), | |
| 'vip-cache': () => import( '../../bin/vip-cache' ), | |
| 'vip-cache-purge-url': () => import( '../../bin/vip-cache-purge-url' ), | |
| 'vip-config': () => import( '../../bin/vip-config' ), | |
| 'vip-config-envvar': () => import( '../../bin/vip-config-envvar' ), | |
| 'vip-config-envvar-delete': () => import( '../../bin/vip-config-envvar-delete' ), | |
| 'vip-config-envvar-get': () => import( '../../bin/vip-config-envvar-get' ), | |
| 'vip-config-envvar-get-all': () => import( '../../bin/vip-config-envvar-get-all' ), | |
| 'vip-config-envvar-list': () => import( '../../bin/vip-config-envvar-list' ), | |
| 'vip-config-envvar-set': () => import( '../../bin/vip-config-envvar-set' ), | |
| 'vip-config-software': () => import( '../../bin/vip-config-software' ), | |
| 'vip-config-software-get': () => import( '../../bin/vip-config-software-get' ), | |
| 'vip-config-software-update': () => import( '../../bin/vip-config-software-update' ), | |
| 'vip-db': () => import( '../../bin/vip-db' ), | |
| 'vip-db-phpmyadmin': () => import( '../../bin/vip-db-phpmyadmin' ), | |
| 'vip-dev-env': () => import( '../../bin/vip-dev-env' ), | |
| 'vip-dev-env-create': () => import( '../../bin/vip-dev-env-create' ), | |
| 'vip-dev-env-destroy': () => import( '../../bin/vip-dev-env-destroy' ), | |
| 'vip-dev-env-envvar': () => import( '../../bin/vip-dev-env-envvar' ), | |
| 'vip-dev-env-envvar-delete': () => import( '../../bin/vip-dev-env-envvar-delete' ), | |
| 'vip-dev-env-envvar-get': () => import( '../../bin/vip-dev-env-envvar-get' ), | |
| 'vip-dev-env-envvar-get-all': () => import( '../../bin/vip-dev-env-envvar-get-all' ), | |
| 'vip-dev-env-envvar-list': () => import( '../../bin/vip-dev-env-envvar-list' ), | |
| 'vip-dev-env-envvar-set': () => import( '../../bin/vip-dev-env-envvar-set' ), | |
| 'vip-dev-env-exec': () => import( '../../bin/vip-dev-env-exec' ), | |
| 'vip-dev-env-import': () => import( '../../bin/vip-dev-env-import' ), | |
| 'vip-dev-env-import-media': () => import( '../../bin/vip-dev-env-import-media' ), | |
| 'vip-dev-env-import-sql': () => import( '../../bin/vip-dev-env-import-sql' ), | |
| 'vip-dev-env-info': () => import( '../../bin/vip-dev-env-info' ), | |
| 'vip-dev-env-list': () => import( '../../bin/vip-dev-env-list' ), | |
| 'vip-dev-env-logs': () => import( '../../bin/vip-dev-env-logs' ), | |
| 'vip-dev-env-purge': () => import( '../../bin/vip-dev-env-purge' ), | |
| 'vip-dev-env-shell': () => import( '../../bin/vip-dev-env-shell' ), | |
| 'vip-dev-env-start': () => import( '../../bin/vip-dev-env-start' ), | |
| 'vip-dev-env-stop': () => import( '../../bin/vip-dev-env-stop' ), | |
| 'vip-dev-env-sync': () => import( '../../bin/vip-dev-env-sync' ), | |
| 'vip-dev-env-sync-sql': () => import( '../../bin/vip-dev-env-sync-sql' ), | |
| 'vip-dev-env-update': () => import( '../../bin/vip-dev-env-update' ), | |
| 'vip-export': () => import( '../../bin/vip-export' ), | |
| 'vip-export-sql': () => import( '../../bin/vip-export-sql' ), | |
| 'vip-import': () => import( '../../bin/vip-import' ), | |
| 'vip-import-media': () => import( '../../bin/vip-import-media' ), | |
| 'vip-import-media-abort': () => import( '../../bin/vip-import-media-abort' ), | |
| 'vip-import-media-status': () => import( '../../bin/vip-import-media-status' ), | |
| 'vip-import-sql': () => import( '../../bin/vip-import-sql' ), | |
| 'vip-import-sql-status': () => import( '../../bin/vip-import-sql-status' ), | |
| 'vip-import-validate-files': () => import( '../../bin/vip-import-validate-files' ), | |
| 'vip-import-validate-sql': () => import( '../../bin/vip-import-validate-sql' ), | |
| 'vip-logout': () => import( '../../bin/vip-logout' ), | |
| 'vip-logs': () => import( '../../bin/vip-logs' ), | |
| 'vip-search-replace': () => import( '../../bin/vip-search-replace' ), | |
| 'vip-slowlogs': () => import( '../../bin/vip-slowlogs' ), | |
| 'vip-sync': () => import( '../../bin/vip-sync' ), | |
| 'vip-whoami': () => import( '../../bin/vip-whoami' ), | |
| 'vip-wp': () => import( '../../bin/vip-wp' ), | |
| vip: () => import( '../../bin/vip.js' ), | |
| 'vip-app': () => import( '../../bin/vip-app.js' ), | |
| 'vip-app-deploy': () => import( '../../bin/vip-app-deploy.js' ), | |
| 'vip-app-deploy-validate': () => import( '../../bin/vip-app-deploy-validate.js' ), | |
| 'vip-app-list': () => import( '../../bin/vip-app-list.js' ), | |
| 'vip-backup': () => import( '../../bin/vip-backup.js' ), | |
| 'vip-backup-db': () => import( '../../bin/vip-backup-db.js' ), | |
| 'vip-cache': () => import( '../../bin/vip-cache.js' ), | |
| 'vip-cache-purge-url': () => import( '../../bin/vip-cache-purge-url.js' ), | |
| 'vip-config': () => import( '../../bin/vip-config.js' ), | |
| 'vip-config-envvar': () => import( '../../bin/vip-config-envvar.js' ), | |
| 'vip-config-envvar-delete': () => import( '../../bin/vip-config-envvar-delete.js' ), | |
| 'vip-config-envvar-get': () => import( '../../bin/vip-config-envvar-get.js' ), | |
| 'vip-config-envvar-get-all': () => import( '../../bin/vip-config-envvar-get-all.js' ), | |
| 'vip-config-envvar-list': () => import( '../../bin/vip-config-envvar-list.js' ), | |
| 'vip-config-envvar-set': () => import( '../../bin/vip-config-envvar-set.js' ), | |
| 'vip-config-software': () => import( '../../bin/vip-config-software.js' ), | |
| 'vip-config-software-get': () => import( '../../bin/vip-config-software-get.js' ), | |
| 'vip-config-software-update': () => import( '../../bin/vip-config-software-update.js' ), | |
| 'vip-db': () => import( '../../bin/vip-db.js' ), | |
| 'vip-db-phpmyadmin': () => import( '../../bin/vip-db-phpmyadmin.js' ), | |
| 'vip-dev-env': () => import( '../../bin/vip-dev-env.js' ), | |
| 'vip-dev-env-create': () => import( '../../bin/vip-dev-env-create.js' ), | |
| 'vip-dev-env-destroy': () => import( '../../bin/vip-dev-env-destroy.js' ), | |
| 'vip-dev-env-envvar': () => import( '../../bin/vip-dev-env-envvar.js' ), | |
| 'vip-dev-env-envvar-delete': () => import( '../../bin/vip-dev-env-envvar-delete.js' ), | |
| 'vip-dev-env-envvar-get': () => import( '../../bin/vip-dev-env-envvar-get.js' ), | |
| 'vip-dev-env-envvar-get-all': () => import( '../../bin/vip-dev-env-envvar-get-all.js' ), | |
| 'vip-dev-env-envvar-list': () => import( '../../bin/vip-dev-env-envvar-list.js' ), | |
| 'vip-dev-env-envvar-set': () => import( '../../bin/vip-dev-env-envvar-set.js' ), | |
| 'vip-dev-env-exec': () => import( '../../bin/vip-dev-env-exec.js' ), | |
| 'vip-dev-env-import': () => import( '../../bin/vip-dev-env-import.js' ), | |
| 'vip-dev-env-import-media': () => import( '../../bin/vip-dev-env-import-media.js' ), | |
| 'vip-dev-env-import-sql': () => import( '../../bin/vip-dev-env-import-sql.js' ), | |
| 'vip-dev-env-info': () => import( '../../bin/vip-dev-env-info.js' ), | |
| 'vip-dev-env-list': () => import( '../../bin/vip-dev-env-list.js' ), | |
| 'vip-dev-env-logs': () => import( '../../bin/vip-dev-env-logs.js' ), | |
| 'vip-dev-env-purge': () => import( '../../bin/vip-dev-env-purge.js' ), | |
| 'vip-dev-env-shell': () => import( '../../bin/vip-dev-env-shell.js' ), | |
| 'vip-dev-env-start': () => import( '../../bin/vip-dev-env-start.js' ), | |
| 'vip-dev-env-stop': () => import( '../../bin/vip-dev-env-stop.js' ), | |
| 'vip-dev-env-sync': () => import( '../../bin/vip-dev-env-sync.js' ), | |
| 'vip-dev-env-sync-sql': () => import( '../../bin/vip-dev-env-sync-sql.js' ), | |
| 'vip-dev-env-update': () => import( '../../bin/vip-dev-env-update.js' ), | |
| 'vip-export': () => import( '../../bin/vip-export.js' ), | |
| 'vip-export-sql': () => import( '../../bin/vip-export-sql.js' ), | |
| 'vip-import': () => import( '../../bin/vip-import.js' ), | |
| 'vip-import-media': () => import( '../../bin/vip-import-media.js' ), | |
| 'vip-import-media-abort': () => import( '../../bin/vip-import-media-abort.js' ), | |
| 'vip-import-media-status': () => import( '../../bin/vip-import-media-status.js' ), | |
| 'vip-import-sql': () => import( '../../bin/vip-import-sql.js' ), | |
| 'vip-import-sql-status': () => import( '../../bin/vip-import-sql-status.js' ), | |
| 'vip-import-validate-files': () => import( '../../bin/vip-import-validate-files.js' ), | |
| 'vip-import-validate-sql': () => import( '../../bin/vip-import-validate-sql.js' ), | |
| 'vip-logout': () => import( '../../bin/vip-logout.js' ), | |
| 'vip-logs': () => import( '../../bin/vip-logs.js' ), | |
| 'vip-search-replace': () => import( '../../bin/vip-search-replace.js' ), | |
| 'vip-slowlogs': () => import( '../../bin/vip-slowlogs.js' ), | |
| 'vip-sync': () => import( '../../bin/vip-sync.js' ), | |
| 'vip-whoami': () => import( '../../bin/vip-whoami.js' ), | |
| 'vip-wp': () => import( '../../bin/vip-wp.js' ), |
| if ( ! existsSync( runtimeReadyPath ) || ! existsSync( runtimeNodeModulesPath ) ) { | ||
| const archiveAsset = sea.getAsset( RUNTIME_ARCHIVE_KEY ); | ||
| const archiveBuffer = Buffer.isBuffer( archiveAsset ) | ||
| ? archiveAsset | ||
| : Buffer.from( archiveAsset ); |
There was a problem hiding this comment.
SEA runtime extraction wires a user-writable node_modules directory into module resolution (NODE_PATH) based only on existence checks. A tampered runtime directory could cause the SEA executable to load attacker-controlled JS. Consider validating .ready contents and verifying an embedded checksum/signature for the archive (and/or using atomic extraction + restrictive permissions) before trusting the extracted directory.
| class CommanderArgsCompat { | ||
| constructor( opts ) { | ||
| this.details = { | ||
| commands: [], | ||
| }; |
There was a problem hiding this comment.
There are existing unit tests for src/lib/cli/command.js, but the new Commander compatibility layer isn’t covered. Add tests around option parsing, alias handling with --, and subcommand dispatch to prevent regressions in the args -> commander migration.
Description
This pull request introduces a comprehensive workflow and tooling for building, signing, and distributing a standalone SEA (Single Executable Application) version of the VIP CLI across macOS, Linux, Windows, and WSL environments. It adds automation via GitHub Actions, a dedicated build script, supporting documentation, and updates the codebase to support and document the new packaging approach. Additionally, it removes the legacy
argsCLI parser in favor ofcommander, documents the migration, and updates dependencies accordingly.The most important changes are:
SEA Build & Signing Automation
.github/workflows/sea-build-sign.ymlworkflow to automate building, optional signing, and artifact upload for SEA executables on all major platforms, including WSL-mediated Windows builds.helpers/build-sea.js, a script that bundles the CLI, packages dependencies, and produces a self-contained executable using Node 22 SEA, with platform-specific post-processing (codesign, blob injection, etc.).build:seanpm script and new dependencies (esbuild,postject,commander) inpackage.jsonto support SEA builds and CLI migration. [1] [2] [3]Documentation & Developer Guidance
docs/SEA-BUILD-SIGNING.md, a detailed runbook for building and signing SEA executables on each platform, including prerequisites, step-by-step instructions, and release checklists.AGENTS.mdandCLAUDE.mdto reference the new SEA build/signing runbook and clarify SEA packaging requirements and pitfalls. [1] [2]CLI Parser Migration
argsCLI parser and migrated tocommander, updating all relevant imports and documentation. Addeddocs/COMMANDER-MIGRATION.mdto describe the migration, compatibility, and next steps. [1] [2] [3]Codebase Cleanups and Type Improvements
Landoto type-only imports insrc/commands/dev-env-sync-sql.tsto support SEA bundling and improve type safety. [1] [2]These changes collectively enable robust, repeatable, and secure distribution of the VIP CLI as a single-file executable, while modernizing the CLI parsing infrastructure and providing clear guidance for future development and releases.
Changelog Description
Changed
Pull request checklist
New release checklist
Steps to Test
Outline the steps to test and verify the PR here.
Example:
npm run build./dist/bin/vip-cookies.js nom