diff --git a/package-lock.json b/package-lock.json index f09839eea..d60271467 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14555,6 +14555,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/default-shell": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/default-shell/-/default-shell-2.2.0.tgz", + "integrity": "sha512-sPpMZcVhRQ0nEMDtuMJ+RtCxt7iHPAMBU+I4tAlo5dU1sjRpNax0crj6nR3qKpvVnckaQ9U38enXcwW9nZJeCw==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -15993,7 +16005,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -16017,7 +16028,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -18290,7 +18300,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=10.17.0" @@ -21463,7 +21472,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -22055,7 +22063,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -26683,6 +26690,50 @@ "node": ">=8" } }, + "node_modules/shell-env": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/shell-env/-/shell-env-4.0.2.tgz", + "integrity": "sha512-8VJLnsyY//uoDJYl7hBcPdX54x0LaKbbfo5htiv8v/jrR4MD7uRUEom6Cb+S54ugMM9GkBbQJSwlLNCI3VXAHQ==", + "license": "MIT", + "dependencies": { + "default-shell": "^2.0.0", + "execa": "^5.1.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shell-env/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/shell-env/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/shell-quote": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", @@ -26781,7 +26832,6 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "devOptional": true, "license": "ISC" }, "node_modules/simple-concat": { @@ -27389,7 +27439,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -35325,6 +35374,7 @@ "http-proxy-agent": "~7.0.2", "https-proxy-agent": "~7.0.6", "is-ip": "^5.0.1", + "shell-env": "^4.0.1", "socks-proxy-agent": "~8.0.5", "system-ca": "^2.0.1", "tough-cookie": "^6.0.0", diff --git a/packages/bruno-cli/src/index.js b/packages/bruno-cli/src/index.js index f92f9ce0a..f1c206fa9 100644 --- a/packages/bruno-cli/src/index.js +++ b/packages/bruno-cli/src/index.js @@ -1,5 +1,6 @@ const yargs = require('yargs'); const chalk = require('chalk'); +const { initializeShellEnv } = require('@usebruno/requests'); const { CLI_EPILOGUE, CLI_VERSION } = require('./constants'); @@ -8,6 +9,9 @@ const printBanner = () => { }; const run = async () => { + // Fetch shell environment (useful when CLI is run as subprocess from GUI app or cron) + await initializeShellEnv(); + const argLength = process.argv.length; const commandsToPrintBanner = ['--help', '-h']; diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index ee4d12a5c..cfd0985a0 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -3,6 +3,10 @@ const path = require('path'); const { execSync } = require('node:child_process'); const isDev = require('electron-is-dev'); const os = require('os'); +const { initializeShellEnv } = require('@usebruno/requests'); + +// Fetch shell environment early (before app ready) +const shellEnvPromise = initializeShellEnv(); if (isDev) { if (!fs.existsSync(path.join(__dirname, '../../bruno-js/src/sandbox/bundle-browser-rollup.js'))) { @@ -155,6 +159,9 @@ if (useSingleInstance && !gotTheLock) { // Prepare the renderer once the app is ready app.on('ready', async () => { + // Ensure shell environment is loaded before any operations that need it + await shellEnvPromise; + if (isDev) { const { installExtension, REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS } = require('electron-devtools-installer'); try { diff --git a/packages/bruno-requests/package.json b/packages/bruno-requests/package.json index e91f79a08..aec9f823c 100644 --- a/packages/bruno-requests/package.json +++ b/packages/bruno-requests/package.json @@ -34,7 +34,8 @@ "socks-proxy-agent": "~8.0.5", "system-ca": "^2.0.1", "tough-cookie": "^6.0.0", - "ws": "^8.18.3" + "ws": "^8.18.3", + "shell-env": "^4.0.1" }, "devDependencies": { "@babel/preset-env": "^7.22.0", diff --git a/packages/bruno-requests/rollup.config.js b/packages/bruno-requests/rollup.config.js index 04ad20448..598b96a74 100644 --- a/packages/bruno-requests/rollup.config.js +++ b/packages/bruno-requests/rollup.config.js @@ -39,6 +39,6 @@ module.exports = [ typescript({ tsconfig: './tsconfig.json' }), terser() ], - external: (id) => isBuiltin(id) || ['axios', 'qs', 'ws', 'debug'].includes(id) + external: (id) => isBuiltin(id) || ['axios', 'qs', 'ws', 'debug', 'shell-env'].includes(id) } ]; diff --git a/packages/bruno-requests/src/index.ts b/packages/bruno-requests/src/index.ts index 51f201545..dd728899c 100644 --- a/packages/bruno-requests/src/index.ts +++ b/packages/bruno-requests/src/index.ts @@ -8,6 +8,7 @@ export { transformProxyConfig } from './utils/proxy-util'; export { default as createVaultClient, VaultError } from './utils/node-vault'; export type { VaultClient, VaultConfig, VaultRequestOptions } from './utils/node-vault'; export { getHttpHttpsAgents } from './utils/http-https-agents'; +export { initializeShellEnv } from './utils/shell-env'; export * as scripting from './scripting'; diff --git a/packages/bruno-requests/src/utils/shell-env.ts b/packages/bruno-requests/src/utils/shell-env.ts new file mode 100644 index 000000000..7f3d44830 --- /dev/null +++ b/packages/bruno-requests/src/utils/shell-env.ts @@ -0,0 +1,33 @@ +/** + * Shell Environment Utility + * + * Fetches environment variables from the user's shell configuration files (e.g., .zshenv, .bashrc) + */ + +const fetchShellEnv = async (): Promise> => { + // Windows handles environment variables differently - skip + if (process.platform === 'win32') { + return {}; + } + + try { + // shell-env is ESM-only, so we use dynamic import + const { shellEnv } = await import('shell-env'); + const env = await shellEnv(); + return env; + } catch (error) { + return {}; + } +}; + +/** + * Initializes process.env with shell environment variables. + * Should be called early in the app startup. + * + * @returns The fetched shell environment variables + */ +export const initializeShellEnv = async (): Promise> => { + const shellEnvVars = await fetchShellEnv(); + Object.assign(process.env, shellEnvVars); + return shellEnvVars; +};