Files
hermes-agent/apps/desktop/scripts/bundle-env.mjs
T
alt-glitch 303b70f75b chore(release): retire HERMES_GUEST_ONBOARDING from bundles, build tooling and the updater
The Microsoft Store variant forced HERMES_GUEST_ONBOARDING=1 into its bundle env,
and commit/channel builds could bake it with --bundle-env. Nothing reads it now.

- scripts/bundles/desktop_inputs.py: the Store bundle env is just the request's.
- bundle_env.py / bundle-env.mjs: the key is no longer allowed for new builds.
- channel-protocol.ts: the key moves to RETIRED_BUNDLE_ENV_KEYS, so preview
  manifests already published with it still decode (the HERMES_SKIP_INTRO
  precedent).
- BUILDING.md example uses HERMES_PREVIEW_FULL_CONNECTORS.
- Build tests use another allowed key as their sample; both validators reject the
  retired key and the decoder drops it.
- msix-virtualization.test.mjs: every generated MSIX manifest (store, release,
  canary, channel) keeps filesystem and registry write virtualisation disabled,
  and the builder config declares unvirtualizedResources. The machine marker and
  the shared %LOCALAPPDATA%\hermes root depend on that.
2026-10-14 00:32:21 +05:30

51 lines
2.6 KiB
JavaScript

// Defaults must run before bundled modules resolve paths or feature flags.
// An esbuild define would replace reads, not populate the child process env.
// Match scripts/releases/bundle_env.py and the channel request decoder.
const ALLOWED_KEYS = new Set([
'HERMES_HOME', 'HERMES_DATA_DIR_SUFFIX', 'HERMES_DESKTOP_USER_DATA_DIR',
'HERMES_SHARED_AUTH_DIR', 'HERMES_PREVIEW_FULL_CONNECTORS'
])
/** Validate a bundle environment object: plain object of identifiers to
* strings (defaults) or null (clears). Shared by the banner writer and the
* smoke driver's stamp reader.
* @param {unknown} values @returns {Record<string, string|null>} */
export function validateBundleEnvironment(values) {
if (!values || Array.isArray(values) || typeof values !== 'object') {
throw new Error('Bundle environment must be a JSON object')
}
for (const [key, value] of Object.entries(values)) {
if (!ALLOWED_KEYS.has(key) || (value !== null && (typeof value !== 'string' || value.includes('\0')))) {
throw new Error('Bundle environment requires permitted desktop keys and string values without NUL, or null to clear')
}
}
return /** @type {Record<string, string|null>} */ (values)
}
/** Apply bundle environment defaults/clears to a base environment and return a
* new object without mutating it. This is the pure twin of the side-effecting
* loop the bundled banner emits at launch — keep the two in lockstep (pinned
* by bundle-env.test.mjs). Clears (null) set the key to an empty string
* unconditionally so a stale registry or ambient value cannot win; defaults
* fill only an absent key, so an explicit runtime value always wins.
* @param {Record<string, string|undefined>} env
* @param {Record<string, string|null>} values */
export function applyBundleEnvironment(env, values) {
const result = { ...env }
for (const [key, value] of Object.entries(values)) {
if (value === null) result[key] = ''
else if (result[key] == null) result[key] = value
}
return result
}
/** @param {string} raw @returns {string} */
export function environmentDefaultsBanner(raw) {
const values = validateBundleEnvironment(JSON.parse(raw))
// Keep clears present-but-empty so path resolution cannot restore registry
// defaults. The emitted loop is the side-effecting twin of
// applyBundleEnvironment above; it cannot call it (the banner runs before
// the bundle's imports), so the two must stay in lockstep — see the test.
return `\nfor (const [key, value] of ${JSON.stringify(Object.entries(values))}) { if (value === null) process.env[key] = ''; else process.env[key] ??= value; }\n`
}