@evomap/evolver 1.90.0 → 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/README.md +27 -564
  2. package/bin/evolver-llm-proxy.js +3 -0
  3. package/bin/evolver-mcp.js +2 -0
  4. package/bin/evolver-proxy.js +3 -0
  5. package/bin/evolver.js +4 -0
  6. package/index.js +1 -3670
  7. package/package.json +32 -62
  8. package/CONTRIBUTING.md +0 -19
  9. package/LICENSE +0 -641
  10. package/README.ja-JP.md +0 -521
  11. package/README.ko-KR.md +0 -520
  12. package/README.zh-CN.md +0 -531
  13. package/SKILL.md +0 -365
  14. package/assets/cover.png +0 -0
  15. package/assets/gep/genes.seed.json +0 -496
  16. package/conformance/savings-core/constants.json +0 -30
  17. package/conformance/savings-core/golden-vectors.json +0 -333
  18. package/scripts/a2a_export.js +0 -63
  19. package/scripts/a2a_ingest.js +0 -79
  20. package/scripts/a2a_promote.js +0 -118
  21. package/scripts/analyze_by_skill.js +0 -121
  22. package/scripts/build_binaries.js +0 -479
  23. package/scripts/check-changelog.js +0 -166
  24. package/scripts/extract_log.js +0 -85
  25. package/scripts/generate_history.js +0 -75
  26. package/scripts/gep_append_event.js +0 -96
  27. package/scripts/gep_personality_report.js +0 -234
  28. package/scripts/human_report.js +0 -147
  29. package/scripts/recall-verify-report.js +0 -234
  30. package/scripts/recover_loop.js +0 -61
  31. package/scripts/refresh_stars_badge.js +0 -168
  32. package/scripts/seed-merchants.js +0 -91
  33. package/scripts/skill2recipes.js +0 -118
  34. package/scripts/suggest_version.js +0 -89
  35. package/scripts/validate-modules.js +0 -38
  36. package/scripts/validate-suite.js +0 -78
  37. package/src/adapters/claudeCode.js +0 -194
  38. package/src/adapters/codex.js +0 -216
  39. package/src/adapters/cursor.js +0 -91
  40. package/src/adapters/hookAdapter.js +0 -469
  41. package/src/adapters/kiro.js +0 -195
  42. package/src/adapters/opencode.js +0 -326
  43. package/src/adapters/scripts/_lockPaths.js +0 -74
  44. package/src/adapters/scripts/_memoryFiltering.js +0 -35
  45. package/src/adapters/scripts/_runtimePaths.js +0 -440
  46. package/src/adapters/scripts/evolver-session-end.js +0 -321
  47. package/src/adapters/scripts/evolver-session-start.js +0 -587
  48. package/src/adapters/scripts/evolver-signal-detect.js +0 -98
  49. package/src/adapters/scripts/evolver-task-recall.js +0 -173
  50. package/src/atp/atpExecute.js +0 -283
  51. package/src/atp/atpTaskPickup.js +0 -233
  52. package/src/atp/autoBuyer.js +0 -382
  53. package/src/atp/autoDeliver.js +0 -215
  54. package/src/atp/cli.js +0 -354
  55. package/src/atp/cliAutobuyPrompt.js +0 -154
  56. package/src/atp/consumerAgent.js +0 -157
  57. package/src/atp/defaultHandler.js +0 -69
  58. package/src/atp/heartbeatSignalsHandler.js +0 -254
  59. package/src/atp/hubClient.js +0 -317
  60. package/src/atp/index.js +0 -38
  61. package/src/atp/merchantAgent.js +0 -118
  62. package/src/atp/protocol.js +0 -41
  63. package/src/atp/questionComposer.js +0 -133
  64. package/src/atp/serviceHelper.js +0 -92
  65. package/src/canary.js +0 -13
  66. package/src/config.js +0 -351
  67. package/src/evolve/guards.js +0 -1
  68. package/src/evolve/pipeline/collect.js +0 -1
  69. package/src/evolve/pipeline/dispatch.js +0 -1
  70. package/src/evolve/pipeline/enrich.js +0 -1
  71. package/src/evolve/pipeline/hub.js +0 -1
  72. package/src/evolve/pipeline/select.js +0 -1
  73. package/src/evolve/pipeline/signals.js +0 -1
  74. package/src/evolve/utils.js +0 -1
  75. package/src/evolve.js +0 -1
  76. package/src/experiment/agentRunner.js +0 -229
  77. package/src/experiment/cli.js +0 -159
  78. package/src/experiment/comparison.js +0 -233
  79. package/src/experiment/metrics.js +0 -75
  80. package/src/forceUpdate.js +0 -991
  81. package/src/gep/a2a.js +0 -173
  82. package/src/gep/a2aProtocol.js +0 -1
  83. package/src/gep/analyzer.js +0 -35
  84. package/src/gep/antiAbuseTelemetry.js +0 -1
  85. package/src/gep/assetCallLog.js +0 -197
  86. package/src/gep/assetStore.js +0 -723
  87. package/src/gep/assets.js +0 -36
  88. package/src/gep/autoDistillConv.js +0 -1
  89. package/src/gep/autoDistillLlm.js +0 -1
  90. package/src/gep/bridge.js +0 -138
  91. package/src/gep/candidateEval.js +0 -1
  92. package/src/gep/candidates.js +0 -1
  93. package/src/gep/claimNudge.js +0 -121
  94. package/src/gep/cliContracts.js +0 -1190
  95. package/src/gep/contentHash.js +0 -1
  96. package/src/gep/conversationDistiller.js +0 -1
  97. package/src/gep/conversationSniffer.js +0 -1
  98. package/src/gep/crypto.js +0 -1
  99. package/src/gep/curriculum.js +0 -1
  100. package/src/gep/deviceId.js +0 -1
  101. package/src/gep/directoryClient.js +0 -115
  102. package/src/gep/envFingerprint.js +0 -1
  103. package/src/gep/epigenetics.js +0 -1
  104. package/src/gep/execBridge.js +0 -1
  105. package/src/gep/executionTrace.js +0 -291
  106. package/src/gep/explore.js +0 -1
  107. package/src/gep/featureFlags.js +0 -121
  108. package/src/gep/gitOps.js +0 -265
  109. package/src/gep/hash.js +0 -1
  110. package/src/gep/hostErrorClassifier.js +0 -34
  111. package/src/gep/hubFetch.js +0 -1
  112. package/src/gep/hubReview.js +0 -1
  113. package/src/gep/hubSearch.js +0 -1
  114. package/src/gep/hubVerify.js +0 -1
  115. package/src/gep/idleScheduler.js +0 -400
  116. package/src/gep/issueReporter.js +0 -416
  117. package/src/gep/learningSignals.js +0 -1
  118. package/src/gep/llmReview.js +0 -92
  119. package/src/gep/localStateAwareness.js +0 -243
  120. package/src/gep/mailboxTransport.js +0 -119
  121. package/src/gep/memoryGraph.js +0 -1
  122. package/src/gep/memoryGraphAdapter.js +0 -1
  123. package/src/gep/mutation.js +0 -1
  124. package/src/gep/narrativeMemory.js +0 -1
  125. package/src/gep/oauthLogin.js +0 -181
  126. package/src/gep/openPRRegistry.js +0 -1
  127. package/src/gep/paths.js +0 -522
  128. package/src/gep/personality.js +0 -1
  129. package/src/gep/policyCheck.js +0 -1
  130. package/src/gep/portable.js +0 -103
  131. package/src/gep/privacyClient.js +0 -235
  132. package/src/gep/prompt.js +0 -1
  133. package/src/gep/questionGenerator.js +0 -518
  134. package/src/gep/recallInject.js +0 -1
  135. package/src/gep/recallVerifier.js +0 -1
  136. package/src/gep/reflection.js +0 -1
  137. package/src/gep/sanitize.js +0 -264
  138. package/src/gep/savingsCore.js +0 -1
  139. package/src/gep/schemas/capsule.js +0 -170
  140. package/src/gep/schemas/gene.js +0 -154
  141. package/src/gep/schemas/index.js +0 -8
  142. package/src/gep/schemas/protocol.js +0 -51
  143. package/src/gep/schemas/task.js +0 -74
  144. package/src/gep/selector.js +0 -1
  145. package/src/gep/selfPR.js +0 -469
  146. package/src/gep/signals.js +0 -776
  147. package/src/gep/skill2gep.js +0 -1056
  148. package/src/gep/skill2gepAudit.js +0 -303
  149. package/src/gep/skill2recipes.js +0 -511
  150. package/src/gep/skillDistiller.js +0 -1
  151. package/src/gep/skillPublisher.js +0 -358
  152. package/src/gep/solidify.js +0 -1
  153. package/src/gep/strategy.js +0 -1
  154. package/src/gep/taskReceiver.js +0 -575
  155. package/src/gep/tokenSavings.js +0 -1
  156. package/src/gep/trajectoryExport.js +0 -1
  157. package/src/gep/validationReport.js +0 -55
  158. package/src/gep/validator/index.js +0 -411
  159. package/src/gep/validator/reporter.js +0 -210
  160. package/src/gep/validator/sandboxExecutor.js +0 -480
  161. package/src/gep/validator/stakeBootstrap.js +0 -357
  162. package/src/gep/workspaceKeychain.js +0 -1
  163. package/src/ops/cleanup.js +0 -80
  164. package/src/ops/commentary.js +0 -60
  165. package/src/ops/health_check.js +0 -104
  166. package/src/ops/index.js +0 -11
  167. package/src/ops/innovation.js +0 -67
  168. package/src/ops/lifecycle.js +0 -798
  169. package/src/ops/self_repair.js +0 -76
  170. package/src/ops/skills_monitor.js +0 -147
  171. package/src/ops/trigger.js +0 -33
  172. package/src/proxy/clientSettings.js +0 -405
  173. package/src/proxy/envelope.js +0 -59
  174. package/src/proxy/extensions/dmHandler.js +0 -45
  175. package/src/proxy/extensions/sessionHandler.js +0 -141
  176. package/src/proxy/extensions/skillUpdater.js +0 -64
  177. package/src/proxy/extensions/traceControl.js +0 -1
  178. package/src/proxy/index.js +0 -1395
  179. package/src/proxy/inject.js +0 -1
  180. package/src/proxy/lifecycle/manager.js +0 -1568
  181. package/src/proxy/mailbox/state.js +0 -207
  182. package/src/proxy/mailbox/store.js +0 -602
  183. package/src/proxy/router/cache_passthrough.js +0 -26
  184. package/src/proxy/router/features.js +0 -84
  185. package/src/proxy/router/gemini_route.js +0 -154
  186. package/src/proxy/router/messages_route.js +0 -535
  187. package/src/proxy/router/model_router.js +0 -113
  188. package/src/proxy/router/models_route.js +0 -52
  189. package/src/proxy/router/ollama_route.js +0 -103
  190. package/src/proxy/router/responses_route.js +0 -170
  191. package/src/proxy/router/vertex_route.js +0 -110
  192. package/src/proxy/server/http.js +0 -363
  193. package/src/proxy/server/routes.js +0 -558
  194. package/src/proxy/server/settings.js +0 -115
  195. package/src/proxy/sync/engine.js +0 -179
  196. package/src/proxy/sync/inbound.js +0 -211
  197. package/src/proxy/sync/outbound.js +0 -361
  198. package/src/proxy/task/monitor.js +0 -131
  199. package/src/proxy/trace/extractor.js +0 -1
  200. package/src/proxy/trace/usage.js +0 -1
  201. package/src/solo/breaker.js +0 -25
  202. package/src/solo/gitGuard.js +0 -65
  203. package/src/webui/client/clientJs/assets.js +0 -111
  204. package/src/webui/client/clientJs/bootstrap.js +0 -92
  205. package/src/webui/client/clientJs/common.js +0 -77
  206. package/src/webui/client/clientJs/i18n.js +0 -366
  207. package/src/webui/client/clientJs/index.js +0 -35
  208. package/src/webui/client/clientJs/interactions.js +0 -351
  209. package/src/webui/client/clientJs/overview.js +0 -152
  210. package/src/webui/client/clientJs/personality.js +0 -285
  211. package/src/webui/client/clientJs/pipelines.js +0 -330
  212. package/src/webui/client/indexHtml.js +0 -221
  213. package/src/webui/client/static.js +0 -23
  214. package/src/webui/client/stylesCss.js +0 -639
  215. package/src/webui/client/vendor/README.md +0 -15
  216. package/src/webui/client/vendor/echarts.min.js +0 -45
  217. package/src/webui/index.js +0 -14
  218. package/src/webui/observer/assets.js +0 -146
  219. package/src/webui/observer/index.js +0 -37
  220. package/src/webui/observer/interactions.js +0 -127
  221. package/src/webui/observer/jsonl.js +0 -75
  222. package/src/webui/observer/paths.js +0 -46
  223. package/src/webui/observer/personality.js +0 -43
  224. package/src/webui/observer/pipelineEvents.js +0 -58
  225. package/src/webui/observer/redact.js +0 -63
  226. package/src/webui/observer/runs.js +0 -356
  227. package/src/webui/observer/safety.js +0 -57
  228. package/src/webui/observer/skills.js +0 -70
  229. package/src/webui/observer/status.js +0 -71
  230. package/src/webui/server/http.js +0 -138
  231. package/src/webui/server/routes.js +0 -41
@@ -1,76 +0,0 @@
1
- // Git Self-Repair - Evolver Core Module
2
- // Emergency repair for git sync failures: abort rebase/merge, remove stale locks.
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { execSync } = require('child_process');
7
- // 10 MB — prevents RangeError on large child process output (e.g. git log/diff
8
- // on large repos). See GHSA reports / issue #451.
9
- const MAX_EXEC_BUFFER = 10 * 1024 * 1024;
10
-
11
- const { getWorkspaceRoot } = require('../gep/paths');
12
-
13
- var LOCK_MAX_AGE_MS = require('../config').LOCK_MAX_AGE_MS;
14
-
15
- function repair(gitRoot) {
16
- var root = gitRoot || getWorkspaceRoot();
17
- var repaired = [];
18
-
19
- // 1. Abort pending rebase
20
- try {
21
- execSync('git rebase --abort', { cwd: root, stdio: 'ignore', maxBuffer: MAX_EXEC_BUFFER });
22
- repaired.push('rebase_aborted');
23
- console.log('[SelfRepair] Aborted pending rebase.');
24
- } catch (e) {}
25
-
26
- // 2. Abort pending merge
27
- try {
28
- execSync('git merge --abort', { cwd: root, stdio: 'ignore', maxBuffer: MAX_EXEC_BUFFER });
29
- repaired.push('merge_aborted');
30
- console.log('[SelfRepair] Aborted pending merge.');
31
- } catch (e) {}
32
-
33
- // 3. Remove stale index.lock
34
- var lockFile = path.join(root, '.git', 'index.lock');
35
- if (fs.existsSync(lockFile)) {
36
- try {
37
- var stat = fs.statSync(lockFile);
38
- var age = Date.now() - stat.mtimeMs;
39
- if (age > LOCK_MAX_AGE_MS) {
40
- fs.unlinkSync(lockFile);
41
- repaired.push('stale_lock_removed');
42
- console.log('[SelfRepair] Removed stale index.lock (' + Math.round(age / 60000) + 'min old).');
43
- }
44
- } catch (e) {}
45
- }
46
-
47
- // 4. Reset to remote main if local is corrupt (last resort - guarded by flag)
48
- // Only enabled if explicitly called with --force-reset or EVOLVE_GIT_RESET=true
49
- if (process.env.EVOLVE_GIT_RESET === 'true') {
50
- try {
51
- console.log('[SelfRepair] Resetting local branch to origin/main (HARD reset)...');
52
- execSync('git fetch origin main', { cwd: root, stdio: 'ignore', maxBuffer: MAX_EXEC_BUFFER });
53
- execSync('git reset --hard origin/main', { cwd: root, stdio: 'ignore', maxBuffer: MAX_EXEC_BUFFER });
54
- repaired.push('hard_reset_to_origin');
55
- } catch (e) {
56
- console.warn('[SelfRepair] Hard reset failed: ' + e.message);
57
- }
58
- } else {
59
- // Safe fetch
60
- try {
61
- execSync('git fetch origin', { cwd: root, stdio: 'ignore', timeout: 30000, maxBuffer: MAX_EXEC_BUFFER });
62
- repaired.push('fetch_ok');
63
- } catch (e) {
64
- console.warn('[SelfRepair] git fetch failed: ' + e.message);
65
- }
66
- }
67
-
68
- return repaired;
69
- }
70
-
71
- if (require.main === module) {
72
- var result = repair();
73
- console.log('[SelfRepair] Result:', result.length > 0 ? result.join(', ') : 'nothing to repair');
74
- }
75
-
76
- module.exports = { repair };
@@ -1,147 +0,0 @@
1
- // Skills Monitor (v2.0) - Evolver Core Module
2
- // Checks installed skills for real issues, auto-heals simple problems.
3
- // Zero Feishu dependency.
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const { execSync } = require('child_process');
8
- // 10 MB — prevents RangeError on large child process output (e.g. git log/diff
9
- // on large repos). See GHSA reports / issue #451.
10
- const MAX_EXEC_BUFFER = 10 * 1024 * 1024;
11
-
12
- const { getSkillsDir, getWorkspaceRoot } = require('../gep/paths');
13
-
14
- const IGNORE_LIST = new Set([
15
- 'common',
16
- 'input-validator',
17
- 'proactive-agent',
18
- 'security-audit',
19
- ]);
20
-
21
- // Load user-defined ignore list
22
- try {
23
- const ignoreFile = path.join(getWorkspaceRoot(), '.skill_monitor_ignore');
24
- if (fs.existsSync(ignoreFile)) {
25
- fs.readFileSync(ignoreFile, 'utf8').split('\n').forEach(function(l) {
26
- var t = l.trim();
27
- if (t && !t.startsWith('#')) IGNORE_LIST.add(t);
28
- });
29
- }
30
- } catch (e) { /* ignore */ }
31
-
32
- function checkSkill(skillName) {
33
- var SKILLS_DIR = getSkillsDir();
34
- if (IGNORE_LIST.has(skillName)) return null;
35
- var skillPath = path.join(SKILLS_DIR, skillName);
36
- var issues = [];
37
-
38
- try { if (!fs.statSync(skillPath).isDirectory()) return null; } catch (e) { return null; }
39
-
40
- var mainFile = 'index.js';
41
- var pkgPath = path.join(skillPath, 'package.json');
42
- var hasPkg = false;
43
-
44
- if (fs.existsSync(pkgPath)) {
45
- hasPkg = true;
46
- try {
47
- var pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
48
- if (pkg.main) mainFile = pkg.main;
49
- if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
50
- if (!fs.existsSync(path.join(skillPath, 'node_modules'))) {
51
- issues.push('Missing node_modules (needs npm install)');
52
- } else {
53
- // Optimization: Check for node_modules existence instead of spawning node
54
- // Spawning node for every skill is too slow (perf_bottleneck).
55
- // We assume if node_modules exists, it's likely okay.
56
- // Only spawn check if we really suspect issues (e.g. empty node_modules).
57
- try {
58
- if (fs.readdirSync(path.join(skillPath, 'node_modules')).length === 0) {
59
- issues.push('Empty node_modules (needs npm install)');
60
- }
61
- } catch (e) {
62
- issues.push('Invalid node_modules');
63
- }
64
- }
65
- }
66
- } catch (e) {
67
- issues.push('Invalid package.json');
68
- }
69
- }
70
-
71
- if (mainFile.endsWith('.js')) {
72
- var entryPoint = path.join(skillPath, mainFile);
73
- if (fs.existsSync(entryPoint)) {
74
- // Optimization: Syntax check via node -c is slow.
75
- // We can trust the runtime to catch syntax errors when loading.
76
- // Or we can use a lighter check if absolutely necessary.
77
- // For now, removing the synchronous spawn to fix perf_bottleneck.
78
- }
79
- }
80
-
81
- if (hasPkg && !fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
82
- issues.push('Missing SKILL.md');
83
- }
84
-
85
- return issues.length > 0 ? { name: skillName, issues: issues } : null;
86
- }
87
-
88
- function autoHeal(skillName, issues) {
89
- var SKILLS_DIR = getSkillsDir();
90
- var skillPath = path.join(SKILLS_DIR, skillName);
91
- var healed = [];
92
-
93
- for (var i = 0; i < issues.length; i++) {
94
- if (issues[i] === 'Missing node_modules (needs npm install)' || issues[i] === 'Empty node_modules (needs npm install)') {
95
- try {
96
- // Remove package-lock.json if it exists to prevent conflict errors
97
- try { fs.unlinkSync(path.join(skillPath, 'package-lock.json')); } catch (e) {}
98
-
99
- const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
100
- execSync(npmBin + ' install --production --no-audit --no-fund --ignore-scripts', {
101
- cwd: skillPath, stdio: 'ignore', timeout: 60000 // Increased timeout, maxBuffer: MAX_EXEC_BUFFER
102
- });
103
- healed.push(issues[i]);
104
- console.log('[SkillsMonitor] Auto-healed ' + skillName + ': npm install');
105
- } catch (e) {
106
- console.error('[SkillsMonitor] Failed to heal ' + skillName + ': ' + e.message);
107
- }
108
- } else if (issues[i] === 'Missing SKILL.md') {
109
- try {
110
- var name = skillName.replace(/-/g, ' ');
111
- fs.writeFileSync(path.join(skillPath, 'SKILL.md'), '# ' + skillName + '\n\n' + name + ' skill.\n');
112
- healed.push(issues[i]);
113
- console.log('[SkillsMonitor] Auto-healed ' + skillName + ': created SKILL.md stub');
114
- } catch (e) {}
115
- }
116
- }
117
- return healed;
118
- }
119
-
120
- function run(options) {
121
- var heal = (options && options.autoHeal) !== false;
122
- var SKILLS_DIR = getSkillsDir();
123
- var skills = fs.readdirSync(SKILLS_DIR);
124
- var report = [];
125
-
126
- for (var i = 0; i < skills.length; i++) {
127
- if (skills[i].startsWith('.')) continue;
128
- var result = checkSkill(skills[i]);
129
- if (result) {
130
- if (heal) {
131
- var healed = autoHeal(result.name, result.issues);
132
- result.issues = result.issues.filter(function(issue) { return !healed.includes(issue); });
133
- if (result.issues.length === 0) continue;
134
- }
135
- report.push(result);
136
- }
137
- }
138
- return report;
139
- }
140
-
141
- if (require.main === module) {
142
- var issues = run();
143
- console.log(JSON.stringify(issues, null, 2));
144
- process.exit(issues.length > 0 ? 1 : 0);
145
- }
146
-
147
- module.exports = { run, checkSkill, autoHeal };
@@ -1,33 +0,0 @@
1
- // Evolver Wake Trigger - Evolver Core Module
2
- // Writes a signal file that the wrapper can poll to wake up immediately.
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { getWorkspaceRoot } = require('../gep/paths');
7
-
8
- var WAKE_FILE = path.join(getWorkspaceRoot(), 'memory', 'evolver_wake.signal');
9
-
10
- function send() {
11
- try {
12
- fs.writeFileSync(WAKE_FILE, 'WAKE');
13
- console.log('[Trigger] Wake signal sent to ' + WAKE_FILE);
14
- return true;
15
- } catch (e) {
16
- console.error('[Trigger] Failed: ' + e.message);
17
- return false;
18
- }
19
- }
20
-
21
- function clear() {
22
- try { if (fs.existsSync(WAKE_FILE)) fs.unlinkSync(WAKE_FILE); } catch (e) {}
23
- }
24
-
25
- function isPending() {
26
- return fs.existsSync(WAKE_FILE);
27
- }
28
-
29
- if (require.main === module) {
30
- send();
31
- }
32
-
33
- module.exports = { send, clear, isPending };
@@ -1,405 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const os = require('os');
5
- const path = require('path');
6
-
7
- const MANAGED_BY = 'evomap-proxy';
8
- const REUSABLE_PROXY_TOKEN_RE = /^[a-f0-9]{64}$/i;
9
-
10
- function getHomeDir(env = process.env) {
11
- const home = String(env.HOME || os.homedir() || '').trim();
12
- return home || null;
13
- }
14
-
15
- function normalizeSettingsPath(value, home) {
16
- const raw = String(value || '').trim();
17
- if (!raw) return null;
18
- if (home && raw === '~') return path.resolve(home);
19
- if (home && raw.startsWith('~/')) return path.resolve(home, raw.slice(2));
20
- return path.resolve(raw);
21
- }
22
-
23
- function samePath(a, b) {
24
- if (!a || !b) return false;
25
- const left = path.resolve(a);
26
- const right = path.resolve(b);
27
- return process.platform === 'win32'
28
- ? left.toLowerCase() === right.toLowerCase()
29
- : left === right;
30
- }
31
-
32
- function getEnvClaudeSettingsFile(env = process.env) {
33
- return String(env.CLAUDE_SETTINGS_FILE || env.EVOMAP_CLAUDE_SETTINGS_FILE || '').trim();
34
- }
35
-
36
- function isValidReusableProxyToken(value) {
37
- return typeof value === 'string' && REUSABLE_PROXY_TOKEN_RE.test(value.trim());
38
- }
39
-
40
- function normalizeUrl(value) {
41
- return String(value || '').trim().replace(/\/+$/, '');
42
- }
43
-
44
- function parseUrl(value) {
45
- const raw = normalizeUrl(value);
46
- if (!raw) return null;
47
- try {
48
- return new URL(raw);
49
- } catch {
50
- return null;
51
- }
52
- }
53
-
54
- function isLoopbackProxyUrl(value) {
55
- const parsed = parseUrl(value);
56
- if (!parsed || parsed.protocol !== 'http:') return false;
57
- const host = parsed.hostname.toLowerCase();
58
- return host === '127.0.0.1' || host === 'localhost' || host === '[::1]' || host === '::1';
59
- }
60
-
61
- function safeUpstreamBaseUrl(value, opts = {}) {
62
- const baseUrl = normalizeUrl(value);
63
- if (!baseUrl) return '';
64
- if (baseUrl === normalizeUrl(opts.url)) return '';
65
- return baseUrl;
66
- }
67
-
68
- function safeUpstreamToken(value, proxyToken) {
69
- const token = typeof value === 'string' ? value.trim() : '';
70
- if (!token || token === proxyToken) return '';
71
- return token;
72
- }
73
-
74
- function getClaudeSettingsFile(env = process.env) {
75
- const home = getHomeDir(env);
76
- if (!home) return null;
77
- return path.join(home, '.claude', 'settings.json');
78
- }
79
-
80
- function resolveClaudeSettingsFile(opts = {}, env = process.env) {
81
- if (opts.file) {
82
- return { file: opts.file, source: 'opts' };
83
- }
84
-
85
- const defaultFile = getClaudeSettingsFile(env);
86
- if (!defaultFile) {
87
- return { file: null, reason: 'missing_settings_path' };
88
- }
89
-
90
- const envFile = getEnvClaudeSettingsFile(env);
91
- if (!envFile) {
92
- return { file: defaultFile, source: 'default' };
93
- }
94
-
95
- const home = getHomeDir(env);
96
- const envResolved = normalizeSettingsPath(envFile, home);
97
- const defaultResolved = normalizeSettingsPath(defaultFile, home);
98
- if (samePath(envResolved, defaultResolved)) {
99
- return { file: defaultFile, source: 'env_default' };
100
- }
101
-
102
- return { file: null, reason: 'unsafe_settings_path' };
103
- }
104
-
105
- function readJsonFile(file) {
106
- const result = readJsonFileResult(file);
107
- return result.ok ? result.value : null;
108
- }
109
-
110
- function readJsonFileResult(file) {
111
- try {
112
- if (!file || !fs.existsSync(file)) {
113
- return { exists: false, ok: true, value: null };
114
- }
115
- return { exists: true, ok: true, value: JSON.parse(fs.readFileSync(file, 'utf8')) };
116
- } catch (err) {
117
- return { exists: Boolean(file), ok: false, value: null, error: err };
118
- }
119
- }
120
-
121
- function writePrivateJsonFile(file, data) {
122
- fs.mkdirSync(path.dirname(file), { recursive: true });
123
- fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
124
- try { fs.chmodSync(file, 0o600); } catch { /* best-effort on Windows */ }
125
- }
126
-
127
- function isDisabled(env = process.env) {
128
- const raw = String(env.EVOMAP_PROXY_AUTO_INJECT || '').trim().toLowerCase();
129
- return raw === '0' || raw === 'false' || raw === 'off' || raw === 'none' || raw === 'no';
130
- }
131
-
132
- function hasManagedProxyMarker(settings) {
133
- return settings && settings._evomap_proxy_client_env?.managed_by === MANAGED_BY;
134
- }
135
-
136
- function isManagedProxyBaseUrl(settings, cfg, value, opts = {}) {
137
- const baseUrl = normalizeUrl(value);
138
- if (!isLoopbackProxyUrl(baseUrl)) return false;
139
- if (baseUrl === normalizeUrl(opts.url)) return true;
140
- if (hasManagedProxyMarker(settings)) return true;
141
- if (String(cfg && cfg.EVOMAP_PROXY_AUTO_INJECTED || '') === '1') return true;
142
-
143
- const proxyUrl = normalizeUrl(cfg && cfg.EVOMAP_PROXY_URL);
144
- return Boolean(proxyUrl && isLoopbackProxyUrl(proxyUrl) && proxyUrl === baseUrl);
145
- }
146
-
147
- function isManagedProxyClientSettings(settings, cfg, opts = {}) {
148
- return isManagedProxyBaseUrl(settings, cfg, cfg && cfg.ANTHROPIC_BASE_URL, opts);
149
- }
150
-
151
- function isManagedProxyUpstreamResidual(settings, cfg, baseValue, tokenValue, apiKeyValue) {
152
- const baseUrl = normalizeUrl(baseValue);
153
- const token = typeof tokenValue === 'string' ? tokenValue.trim() : '';
154
- const apiKey = typeof apiKeyValue === 'string' ? apiKeyValue.trim() : '';
155
- if (!isLoopbackProxyUrl(baseUrl)
156
- || (!isValidReusableProxyToken(token) && !isValidReusableProxyToken(apiKey))) {
157
- return false;
158
- }
159
- return hasManagedProxyMarker(settings) || String(cfg && cfg.EVOMAP_PROXY_AUTO_INJECTED || '') === '1';
160
- }
161
-
162
- function safeStoredUpstreamBaseUrl(settings, cfg, value, opts = {}) {
163
- const baseUrl = normalizeUrl(value);
164
- if (!baseUrl) return '';
165
- if (baseUrl === normalizeUrl(opts.url)) return '';
166
- const proxyUrl = normalizeUrl(cfg && cfg.EVOMAP_PROXY_URL);
167
- if (proxyUrl && baseUrl === proxyUrl) return '';
168
- if (baseUrl === normalizeUrl(cfg && cfg.ANTHROPIC_BASE_URL)
169
- && isManagedProxyBaseUrl(settings, cfg, cfg && cfg.ANTHROPIC_BASE_URL, opts)) {
170
- return '';
171
- }
172
- return safeUpstreamBaseUrl(value, opts);
173
- }
174
-
175
- function readReusableClientProxyToken(opts = {}) {
176
- const env = opts.env || process.env;
177
- if (isDisabled(env)) return null;
178
-
179
- const resolved = resolveClaudeSettingsFile(opts, env);
180
- const file = resolved.file;
181
- if (!file) return null;
182
- const settings = readJsonFile(file);
183
- const cfg = settings && settings.env;
184
- if (!cfg || typeof cfg !== 'object') return null;
185
-
186
- const baseUrl = normalizeUrl(cfg.ANTHROPIC_BASE_URL);
187
- const token = typeof cfg.ANTHROPIC_AUTH_TOKEN === 'string'
188
- ? cfg.ANTHROPIC_AUTH_TOKEN.trim()
189
- : '';
190
- if (!isValidReusableProxyToken(token)
191
- || !isManagedProxyClientSettings(settings, cfg, opts)
192
- || !isLoopbackProxyUrl(baseUrl)) {
193
- return null;
194
- }
195
- return token;
196
- }
197
-
198
- function backupExistingFile(file) {
199
- try {
200
- if (!file || !fs.existsSync(file)) return null;
201
- const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
202
- const backupDir = path.join(path.dirname(file), 'backups');
203
- fs.mkdirSync(backupDir, { recursive: true });
204
- for (let i = 0; i < 10; i++) {
205
- const suffix = i === 0 ? '' : `-${i}`;
206
- const backupFile = path.join(backupDir, `settings.json.pre-evomap-proxy-sync-${stamp}${suffix}`);
207
- try {
208
- fs.copyFileSync(file, backupFile, fs.constants.COPYFILE_EXCL);
209
- try { fs.chmodSync(backupFile, 0o600); } catch { /* best-effort */ }
210
- return backupFile;
211
- } catch (err) {
212
- if (!err || err.code !== 'EEXIST') return null;
213
- }
214
- }
215
- return null;
216
- } catch {
217
- return null;
218
- }
219
- }
220
-
221
- function syncClaudeProxySettings(info = {}) {
222
- const env = info.env || process.env;
223
- if (isDisabled(env)) {
224
- return { synced: false, reason: 'disabled' };
225
- }
226
-
227
- const url = normalizeUrl(info.url);
228
- const token = typeof info.token === 'string' ? info.token.trim() : '';
229
- if (!url || !isValidReusableProxyToken(token)) {
230
- return { synced: false, reason: 'missing_proxy_settings' };
231
- }
232
-
233
- const resolved = resolveClaudeSettingsFile(info, env);
234
- const file = resolved.file;
235
- if (!file) {
236
- return { synced: false, changed: false, reason: resolved.reason || 'missing_settings_path' };
237
- }
238
-
239
- const currentResult = readJsonFileResult(file);
240
- if (currentResult.exists && !currentResult.ok) {
241
- const backupFile = info.backup === false ? null : backupExistingFile(file);
242
- return {
243
- synced: false,
244
- changed: false,
245
- reason: 'invalid_settings_json',
246
- file,
247
- backupFile,
248
- };
249
- }
250
- const current = currentResult.value;
251
- const settings = current && typeof current === 'object' && !Array.isArray(current)
252
- ? current
253
- : {};
254
- const cfg = settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env)
255
- ? { ...settings.env }
256
- : {};
257
-
258
- const existingBase = normalizeUrl(cfg.ANTHROPIC_BASE_URL);
259
- const existingToken = typeof cfg.ANTHROPIC_AUTH_TOKEN === 'string'
260
- ? cfg.ANTHROPIC_AUTH_TOKEN.trim()
261
- : '';
262
- const existingApiKey = typeof cfg.ANTHROPIC_API_KEY === 'string'
263
- ? cfg.ANTHROPIC_API_KEY.trim()
264
- : '';
265
- const existingStoredUpstreamBase = normalizeUrl(cfg.EVOMAP_ANTHROPIC_BASE_URL);
266
- const existingStoredUpstreamAuthToken = typeof cfg.EVOMAP_ANTHROPIC_AUTH_TOKEN === 'string'
267
- ? cfg.EVOMAP_ANTHROPIC_AUTH_TOKEN.trim()
268
- : '';
269
- const existingUpstreamApiKey = typeof cfg.EVOMAP_ANTHROPIC_API_KEY === 'string'
270
- ? cfg.EVOMAP_ANTHROPIC_API_KEY.trim()
271
- : '';
272
- const existingBaseIsProxy = isManagedProxyBaseUrl(settings, cfg, existingBase, info);
273
- const runtimeEnv = info.runtimeEnv && typeof info.runtimeEnv === 'object'
274
- ? info.runtimeEnv
275
- : null;
276
-
277
- let changed = false;
278
- let runtimeChanged = false;
279
- let runtimeUpstreamChanged = false;
280
- const setIfChanged = (key, value) => {
281
- if (cfg[key] === value) return;
282
- cfg[key] = value;
283
- changed = true;
284
- };
285
- const deleteIfPresent = (key) => {
286
- if (!Object.prototype.hasOwnProperty.call(cfg, key)) return;
287
- delete cfg[key];
288
- changed = true;
289
- };
290
- const setRuntimeIfMissing = (key, value) => {
291
- if (!runtimeEnv || !value) return false;
292
- if (String(runtimeEnv[key] || '').trim()) return false;
293
- runtimeEnv[key] = value;
294
- runtimeChanged = true;
295
- runtimeUpstreamChanged = true;
296
- return true;
297
- };
298
- const setRuntimeAutoInjected = () => {
299
- if (!runtimeEnv || !runtimeUpstreamChanged || runtimeEnv.EVOMAP_PROXY_AUTO_INJECTED === '1') return;
300
- runtimeEnv.EVOMAP_PROXY_AUTO_INJECTED = '1';
301
- runtimeChanged = true;
302
- };
303
- const setRuntimeUpstreamApiKey = (value) => {
304
- const upstreamApiKey = safeUpstreamToken(value, token);
305
- if (!upstreamApiKey || upstreamApiKey === existingToken) return;
306
- setRuntimeIfMissing('EVOMAP_ANTHROPIC_API_KEY', upstreamApiKey);
307
- };
308
-
309
- if (isManagedProxyUpstreamResidual(
310
- settings,
311
- cfg,
312
- existingStoredUpstreamBase,
313
- existingStoredUpstreamAuthToken,
314
- existingUpstreamApiKey
315
- )) {
316
- deleteIfPresent('EVOMAP_ANTHROPIC_BASE_URL');
317
- deleteIfPresent('EVOMAP_ANTHROPIC_AUTH_TOKEN');
318
- deleteIfPresent('EVOMAP_ANTHROPIC_API_KEY');
319
- }
320
-
321
- const migratedBaseUrl = existingBaseIsProxy ? '' : safeUpstreamBaseUrl(existingBase, info);
322
- if (migratedBaseUrl && !existingBaseIsProxy && !cfg.EVOMAP_ANTHROPIC_BASE_URL) {
323
- setIfChanged('EVOMAP_ANTHROPIC_BASE_URL', migratedBaseUrl);
324
- }
325
- const migratedAuthToken = existingBaseIsProxy ? '' : safeUpstreamToken(existingToken, token);
326
- if (migratedAuthToken && !cfg.EVOMAP_ANTHROPIC_AUTH_TOKEN) {
327
- setIfChanged('EVOMAP_ANTHROPIC_AUTH_TOKEN', migratedAuthToken);
328
- }
329
- if (existingApiKey && existingApiKey !== token && existingApiKey !== existingToken && !cfg.EVOMAP_ANTHROPIC_API_KEY) {
330
- setIfChanged('EVOMAP_ANTHROPIC_API_KEY', existingApiKey);
331
- }
332
- const runtimeHadUpstreamBase = Boolean(runtimeEnv && String(runtimeEnv.EVOMAP_ANTHROPIC_BASE_URL || '').trim());
333
- const runtimeBaseSyncedFromSettings = setRuntimeIfMissing(
334
- 'EVOMAP_ANTHROPIC_BASE_URL',
335
- safeStoredUpstreamBaseUrl(settings, cfg, cfg.EVOMAP_ANTHROPIC_BASE_URL, info)
336
- );
337
- const canSyncSettingsCredentialToRuntime = !runtimeHadUpstreamBase || runtimeBaseSyncedFromSettings;
338
- if (canSyncSettingsCredentialToRuntime) {
339
- const upstreamAuthToken = safeUpstreamToken(cfg.EVOMAP_ANTHROPIC_AUTH_TOKEN, token);
340
- if (!(existingBaseIsProxy && upstreamAuthToken === existingToken)) {
341
- setRuntimeIfMissing('EVOMAP_ANTHROPIC_AUTH_TOKEN', upstreamAuthToken);
342
- }
343
- setRuntimeUpstreamApiKey(cfg.EVOMAP_ANTHROPIC_API_KEY);
344
- }
345
- setRuntimeAutoInjected();
346
-
347
- setIfChanged('ANTHROPIC_BASE_URL', url);
348
- setIfChanged('ANTHROPIC_AUTH_TOKEN', token);
349
- deleteIfPresent('ANTHROPIC_API_KEY');
350
- setIfChanged('CUSTOM_API_KEY', token);
351
- setIfChanged('EVOMAP_PROXY_URL', url);
352
- setIfChanged('EVOMAP_PROXY_AUTO_INJECTED', '1');
353
-
354
- const marker = settings._evomap_proxy_client_env || {};
355
- if (marker.managed_by !== MANAGED_BY) {
356
- settings._evomap_proxy_client_env = { managed_by: MANAGED_BY };
357
- changed = true;
358
- }
359
-
360
- if (!changed) {
361
- return {
362
- synced: true,
363
- changed: false,
364
- runtimeChanged,
365
- file,
366
- vars: [
367
- 'ANTHROPIC_BASE_URL',
368
- 'ANTHROPIC_AUTH_TOKEN',
369
- 'CUSTOM_API_KEY',
370
- 'EVOMAP_PROXY_URL',
371
- ],
372
- };
373
- }
374
-
375
- settings.env = cfg;
376
- settings._evomap_proxy_client_env = {
377
- managed_by: MANAGED_BY,
378
- updated_at: new Date().toISOString(),
379
- };
380
-
381
- const backupFile = info.backup === false ? null : backupExistingFile(file);
382
- writePrivateJsonFile(file, settings);
383
-
384
- return {
385
- synced: true,
386
- changed: true,
387
- runtimeChanged,
388
- file,
389
- backupFile,
390
- vars: [
391
- 'ANTHROPIC_BASE_URL',
392
- 'ANTHROPIC_AUTH_TOKEN',
393
- 'CUSTOM_API_KEY',
394
- 'EVOMAP_PROXY_URL',
395
- ],
396
- };
397
- }
398
-
399
- module.exports = {
400
- getClaudeSettingsFile,
401
- isLoopbackProxyUrl,
402
- isValidReusableProxyToken,
403
- readReusableClientProxyToken,
404
- syncClaudeProxySettings,
405
- };