Loader and bootstrap workflows
This file explains how execution reaches app.js and what app.js does before entering the main command/runtime flow.
Relevant files:
copilot-cli-pkg/npm-loader.jscopilot-cli-pkg/sea-loader.jscopilot-cli-pkg/index.jscopilot-cli-pkg/app.js
At runtime, sea-loader.js and the embedded copilot.tgz live inside the native copilot binary. The extractor now preserves the recovered sea-loader.js beside the expanded package for analysis; the archive itself is retained only when the extractor is run with --keep-package-tgz.
This page opens the Runtime lifecycle chapter. It explains how execution reaches app.js before command parsing, sessions, context assembly, or tools exist. Continue to Mode dispatch and runtime startup for root command dispatch, then to Sessions, persistence, and remote once a runtime mode owns a session.
Source anchors
app.js is bundled and minified, so the semantic aliases below are documentation names. Loader filenames are stable package anchors, while minified anchors are version-specific lookup aids.
| Semantic alias | Minified anchor | Location | Role |
|---|---|---|---|
| npm launcher | copilot-cli-pkg/npm-loader.js | package bin | Resolves and spawns the installed native platform package, or prints a reinstall error when none is available. |
| SEA loader | copilot-cli-pkg/sea-loader.js | native SEA main | Selects/extracts a cached package version and imports its index.js. |
| JavaScript restart wrapper | copilot-cli-pkg/index.js, COPILOT_RUN_APP, restart code 75 | package loader | Selects active package version, spawns child runtime, forwards signals, and restarts on update handoff. |
| App bootstrap module | copilot-cli-pkg/app.js | bundle entry | Installs restricted module loading, Git safety config, and runtime services before CLI dispatch. |
| Restricted require shim | createRequire, app-path containment check | early app.js bootstrap | Allows Node built-ins and approved vendored native modules while rejecting resolved paths outside the app directory. |
| OOP native require root | __napiOopEntrypoint, napi-oop-runtime | app.js ~15-63 | Resolves the Node transport used when native runtime calls are remoted to a Rust parent. |
| Git hardening | LVe(), safe.bareRepository=explicit, GIT_CONFIG_COUNT | early app.js bootstrap | Adds environment-backed Git safety configuration. |
| Config migration | COPILOT_HOME, XDG .copilot migration helpers | early app.js bootstrap | Resolves state/config roots and compatibility migration behavior. |
Distribution layout
The extracted binary is a Node/V8 single executable application (SEA) that carries a loader and a tarball asset. The tarball expands into the @github/copilot package.
flowchart TD Binary["Native copilot executable"] --> SEA["Node SEA payload"] SEA --> SeaLoader["sea-loader.js"] SEA --> PackageArchive["copilot.tgz asset"] PackageArchive --> Package["expanded @github/copilot package"] Package --> NpmLoader["npm-loader.js"] Package --> Index["index.js"] Package --> App["app.js"]
Package --> Metadata["package.json"] Metadata --> Version["@github/copilot"] Metadata --> Bin["bin: copilot -> npm-loader.js"]npm/native launcher path
When installed as an npm package, the copilot bin points at npm-loader.js. In the current package, that loader resolves and spawns the matching platform-native package. If no platform package is installed, it prints a reinstall error; it does not run app.js directly.
flowchart TD Start["user runs copilot"] --> NpmLoader["npm-loader.js"] NpmLoader --> NativeCheck{"platform package exists?"}
NativeCheck -- yes --> NativePkg["@github/copilot-platform-arch"] NativePkg --> SpawnNative["spawn native binary with same args"] SpawnNative --> NativeRuntime["native SEA runtime"]
NativeCheck -- no --> Fail["print reinstall/platform-package error"] NativeRuntime --> SeaLoader["embedded sea-loader.js"] SeaLoader --> IndexRuntime["selected package index.js"] IndexRuntime --> App["app.js"]index.js update/restart wrapper
The JavaScript loader wrapper does more than import app.js. It chooses the active package version, supports auto-update cache locations, spawns a child process with COPILOT_RUN_APP=1, forwards signals, and handles restart exit code 75.
flowchart TD IndexStart["index.js starts"] --> EnvCheck{"COPILOT_RUN_APP == 1?"}
EnvCheck -- yes --> SelectApp["select preferred app.js"] SelectApp --> ImportApp["dynamic import app.js"]
EnvCheck -- no --> AutoUpdateCheck{"auto-update enabled?"} AutoUpdateCheck -- yes --> FindCached["find cached/copied package versions"] AutoUpdateCheck -- no --> UseBundled["use bundled package"] FindCached --> ChooseVersion["choose preferred version"] UseBundled --> ChooseVersion
ChooseVersion --> SpawnChild["spawn child with COPILOT_RUN_APP=1"] SpawnChild --> ForwardSignals["forward process signals"] SpawnChild --> ExitCode{"child exit code"}
ExitCode -- normal --> ExitSame["exit with child code"] ExitCode -- 75 --> RestartFile["read restart resume JSON"] RestartFile --> SpawnChildEarly app.js bootstrap
The beginning of app.js sets up ESM/CommonJS compatibility and a restricted require shim. It allows built-in Node modules and selected vendored native modules, but rejects resolved module paths outside the application directory.
flowchart TD AppStart["app.js module evaluation"] --> Imports["import node:module/path/fs/url"] Imports --> RootRequire["create __rootRequire"] RootRequire --> AppPath["realpath of application directory"] AppPath --> NativeRoots["create require roots for vendored native modules"] NativeRoots --> RequireShim["define custom require(module)"]
RequireShim --> ModuleType{"requested module"} ModuleType -- "approved vendored native" --> NativeRequire["load via vendored native require"] ModuleType -- "Node builtin" --> BuiltinRequire["load builtin"] ModuleType -- "regular package" --> ResolvePath["resolve module path"] ResolvePath --> InsideCheck{"inside app path?"} InsideCheck -- yes --> AppRequire["load module"] InsideCheck -- no --> SecurityError["throw security error"]Approved vendored-native module families observed in the bootstrap section include:
sharpclipboardfoundry-local-sdk@picovoice/pvrecorder-nodenapi-oop-runtime- scoped native dependencies such as
@img/*and@teddyzhu/*
napi-oop-runtime has its own package-local createRequire root. See Out-of-process native runtime bridge for the mode switch, connect-back protocol, manifest binding, callbacks, and handle lifetime.
Git safety hardening
Early in startup, app.js adds safe.bareRepository=explicit to Git’s environment-backed config list. This constrains how Git treats bare repositories during CLI operations.
flowchart TD GitSetup["LVe() git setup"] --> ReadCount["read GIT_CONFIG_COUNT"] ReadCount --> AlreadySet{"safe.bareRepository already present?"} AlreadySet -- yes --> Done["leave environment unchanged"] AlreadySet -- no --> AppendKey["append GIT_CONFIG_KEY_N=safe.bareRepository"] AppendKey --> AppendValue["append GIT_CONFIG_VALUE_N=explicit"] AppendValue --> Increment["increment GIT_CONFIG_COUNT"]Config and state directory migration
The runtime recognizes COPILOT_HOME and XDG locations. The observed XDG migration helper moves selected state/config files from XDG-based .copilot locations back to the default home location when appropriate, then creates compatibility symlinks.
flowchart TD MigrationStart["startup config migration"] --> EnvVars["check XDG_STATE_HOME and XDG_CONFIG_HOME"] EnvVars --> ExistingXdg{"XDG .copilot exists and differs from ~/.copilot?"} ExistingXdg -- no --> Skip["skip migration"] ExistingXdg -- yes --> EnsureHome["ensure ~/.copilot exists"] EnsureHome --> ForEachItem["for selected state/config items"] ForEachItem --> TargetExists{"target already exists?"} TargetExists -- yes --> WarnSkip["warn and skip item"] TargetExists -- no --> MoveItem["move or copy item"] MoveItem --> LinkBack["create symlink back to XDG path"]Bootstrap summary
Before the user-facing CLI logic runs, app.js has already:
- constrained dynamic module loading;
- installed Git safety config in environment variables;
- checked Node compatibility;
- prepared config/state directory behavior;
- defined shutdown/error/logging infrastructure used later by the top-level action.
Created and maintained by Yingting Huang.