mirror of
https://github.com/Ryder-Sun/Meldwork.git
synced 2026-11-07 06:56:03 -10:00
Resolve Hermes dotenv references in provider summaries
Share native dotenv expansion between provider display and credential redaction. Checks: 88 Hermes configuration and ACP tests; desktop pack.
This commit is contained in:
1 parent
953ba18bfa
commit
2cc146a2a4
4 files changed
+33
-17
No files matched your search
@@ -2,6 +2,7 @@ const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const yaml = require('js-yaml')
|
||||
const { nativeIdentityDirectory } = require('../native-config-directory.cjs')
|
||||
const { hermesDotenvAssignments } = require('../hermes-dotenv.cjs')
|
||||
const { configSecrets, nativeSecretEnvKeys } = require('./native-secret-environment.cjs')
|
||||
|
||||
function hermesConfigSecrets({ env, home, platform, declaredKeys }) {
|
||||
@@ -20,19 +21,7 @@ function hermesConfigSecrets({ env, home, platform, declaredKeys }) {
|
||||
const secrets = new Set(configSecrets(configs))
|
||||
const declared = new Set(nativeSecretEnvKeys(declaredKeys))
|
||||
try {
|
||||
const values = { ...env }
|
||||
for (const line of read('.env').split(/\r?\n/)) {
|
||||
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/)
|
||||
if (!match) continue
|
||||
const [, key, raw] = match
|
||||
const quoted = raw.match(/^(['"])(.*?)\1\s*(?:#.*)?$/)
|
||||
if (/^['"]/.test(raw) && !quoted) continue
|
||||
let value = quoted ? quoted[2] : raw.replace(/\s+#.*$/, '').trim()
|
||||
if (quoted?.[1] === '"') value = value.replace(/\\([nr"\\])/g, (_, c) => ({ n: '\n', r: '\r', '"': '"', '\\': '\\' })[c])
|
||||
if (quoted?.[1] === "'") value = value.replace(/\\(['\\])/g, '$1')
|
||||
value = value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g,
|
||||
(_, name, fallback) => Object.hasOwn(values, name) ? values[name] : fallback || '')
|
||||
values[key] = value
|
||||
for (const [key, value] of hermesDotenvAssignments(read('.env'), env)) {
|
||||
if ((declared.has(key) || /(?:API[_-]?KEY|ACCESS[_-]?KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION)/i.test(key))
|
||||
&& value && value.length <= 8192) secrets.add(value)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
const yaml = require('js-yaml')
|
||||
const { redactSecrets } = require('../security/secret-redaction.cjs')
|
||||
const { hermesDotenvAssignments } = require('./hermes-dotenv.cjs')
|
||||
|
||||
function hermesProviderFromConfig(config, env = {}) {
|
||||
const model = config?.model && typeof config.model === 'object' && !Array.isArray(config.model) ? config.model : {}
|
||||
@@ -24,10 +25,8 @@ function hermesProviderEnvironment(root, env = {}) {
|
||||
const stat = fs.statSync(file)
|
||||
if (stat.isFile() && stat.size <= 1024 * 1024) {
|
||||
let provider
|
||||
for (const line of fs.readFileSync(file, 'utf8').split(/\r?\n/)) {
|
||||
if (!/^\s*(?:export\s+)?HERMES_INFERENCE_PROVIDER\s*=/.test(line)) continue
|
||||
const match = line.match(/^\s*(?:export\s+)?HERMES_INFERENCE_PROVIDER\s*=\s*(?:"([A-Za-z0-9._:-]*)"|'([A-Za-z0-9._:-]*)'|([A-Za-z0-9._:-]*))\s*(?:#.*)?$/)
|
||||
provider = match ? match[1] ?? match[2] ?? match[3] : ''
|
||||
for (const [key, value] of hermesDotenvAssignments(fs.readFileSync(file, 'utf8'), env)) {
|
||||
if (key === 'HERMES_INFERENCE_PROVIDER') provider = value ?? ''
|
||||
}
|
||||
if (provider !== undefined) return { ...env, HERMES_INFERENCE_PROVIDER: provider }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
function hermesDotenvAssignments(content, env = {}) {
|
||||
const values = { ...env }
|
||||
const assignments = []
|
||||
for (const line of content.split(/\r?\n/)) {
|
||||
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/)
|
||||
if (!match) continue
|
||||
const [, key, raw] = match
|
||||
const quoted = raw.match(/^(['"])(.*?)\1\s*(?:#.*)?$/)
|
||||
if (/^['"]/.test(raw) && !quoted) {
|
||||
assignments.push([key, null])
|
||||
continue
|
||||
}
|
||||
let value = quoted ? quoted[2] : raw.replace(/\s+#.*$/, '').trim()
|
||||
if (quoted?.[1] === '"') value = value.replace(/\\([nr"\\])/g, (_, c) => ({ n: '\n', r: '\r', '"': '"', '\\': '\\' })[c])
|
||||
if (quoted?.[1] === "'") value = value.replace(/\\(['\\])/g, '$1')
|
||||
value = value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g,
|
||||
(_, name, fallback) => Object.hasOwn(values, name) ? values[name] : fallback || '')
|
||||
values[key] = value
|
||||
assignments.push([key, value])
|
||||
}
|
||||
return assignments
|
||||
}
|
||||
|
||||
module.exports = { hermesDotenvAssignments }
|
||||
@@ -39,6 +39,10 @@ test('Hermes summary reads the selected native environment provider without expo
|
||||
assert.equal(hermesConfiguredProvider({ home, env: { HERMES_HOME: home, HERMES_INFERENCE_PROVIDER: 'custom' } }), '')
|
||||
fs.writeFileSync(path.join(home, '.env'), 'HERMES_INFERENCE_PROVIDER=${OTHER_PROVIDER}\n')
|
||||
assert.equal(hermesConfiguredProvider({ home, env: { HERMES_HOME: home, HERMES_INFERENCE_PROVIDER: 'custom' } }), '')
|
||||
fs.writeFileSync(path.join(home, '.env'), 'SELECTED_PROVIDER=openrouter\nHERMES_INFERENCE_PROVIDER=${SELECTED_PROVIDER}\n')
|
||||
assert.equal(hermesConfiguredProvider({ home, env: { HERMES_HOME: home, SELECTED_PROVIDER: 'stale' } }), 'openrouter')
|
||||
fs.writeFileSync(path.join(home, '.env'), 'HERMES_INFERENCE_PROVIDER=${UNSET_MELDWORK_PROVIDER:-custom:work}\n')
|
||||
assert.equal(hermesConfiguredProvider({ home, env: { HERMES_HOME: home } }), 'custom:work')
|
||||
fs.writeFileSync(path.join(home, 'config.yaml'), 'model:\n provider: anthropic\n')
|
||||
assert.equal(hermesConfiguredProvider({ home, env: { HERMES_HOME: home } }), 'anthropic')
|
||||
})
|
||||
Reference in new issue
Block a user