@evomap/evolver 1.89.20 → 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 -3586
  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/skills/_meta/SKILL.md +0 -41
  38. package/skills/index.json +0 -14
  39. package/src/adapters/claudeCode.js +0 -194
  40. package/src/adapters/codex.js +0 -216
  41. package/src/adapters/cursor.js +0 -91
  42. package/src/adapters/hookAdapter.js +0 -469
  43. package/src/adapters/kiro.js +0 -195
  44. package/src/adapters/opencode.js +0 -326
  45. package/src/adapters/scripts/_lockPaths.js +0 -74
  46. package/src/adapters/scripts/_memoryFiltering.js +0 -35
  47. package/src/adapters/scripts/_runtimePaths.js +0 -440
  48. package/src/adapters/scripts/evolver-session-end.js +0 -321
  49. package/src/adapters/scripts/evolver-session-start.js +0 -587
  50. package/src/adapters/scripts/evolver-signal-detect.js +0 -98
  51. package/src/adapters/scripts/evolver-task-recall.js +0 -173
  52. package/src/atp/atpExecute.js +0 -283
  53. package/src/atp/atpTaskPickup.js +0 -233
  54. package/src/atp/autoBuyer.js +0 -382
  55. package/src/atp/autoDeliver.js +0 -215
  56. package/src/atp/cli.js +0 -354
  57. package/src/atp/cliAutobuyPrompt.js +0 -154
  58. package/src/atp/consumerAgent.js +0 -157
  59. package/src/atp/defaultHandler.js +0 -69
  60. package/src/atp/heartbeatSignalsHandler.js +0 -254
  61. package/src/atp/hubClient.js +0 -317
  62. package/src/atp/index.js +0 -38
  63. package/src/atp/merchantAgent.js +0 -118
  64. package/src/atp/protocol.js +0 -41
  65. package/src/atp/questionComposer.js +0 -133
  66. package/src/atp/serviceHelper.js +0 -92
  67. package/src/canary.js +0 -13
  68. package/src/config.js +0 -351
  69. package/src/evolve/guards.js +0 -1
  70. package/src/evolve/pipeline/collect.js +0 -1
  71. package/src/evolve/pipeline/dispatch.js +0 -1
  72. package/src/evolve/pipeline/enrich.js +0 -1
  73. package/src/evolve/pipeline/hub.js +0 -1
  74. package/src/evolve/pipeline/select.js +0 -1
  75. package/src/evolve/pipeline/signals.js +0 -1
  76. package/src/evolve/utils.js +0 -1
  77. package/src/evolve.js +0 -1
  78. package/src/experiment/agentRunner.js +0 -229
  79. package/src/experiment/cli.js +0 -159
  80. package/src/experiment/comparison.js +0 -233
  81. package/src/experiment/metrics.js +0 -75
  82. package/src/forceUpdate.js +0 -991
  83. package/src/gep/a2a.js +0 -173
  84. package/src/gep/a2aProtocol.js +0 -1
  85. package/src/gep/analyzer.js +0 -35
  86. package/src/gep/antiAbuseTelemetry.js +0 -1
  87. package/src/gep/assetCallLog.js +0 -197
  88. package/src/gep/assetStore.js +0 -723
  89. package/src/gep/assets.js +0 -36
  90. package/src/gep/autoDistillConv.js +0 -1
  91. package/src/gep/autoDistillLlm.js +0 -1
  92. package/src/gep/bridge.js +0 -138
  93. package/src/gep/candidateEval.js +0 -1
  94. package/src/gep/candidates.js +0 -1
  95. package/src/gep/claimNudge.js +0 -121
  96. package/src/gep/cliContracts.js +0 -1190
  97. package/src/gep/contentHash.js +0 -1
  98. package/src/gep/conversationDistiller.js +0 -1
  99. package/src/gep/conversationSniffer.js +0 -1
  100. package/src/gep/crypto.js +0 -1
  101. package/src/gep/curriculum.js +0 -1
  102. package/src/gep/deviceId.js +0 -1
  103. package/src/gep/directoryClient.js +0 -115
  104. package/src/gep/envFingerprint.js +0 -1
  105. package/src/gep/epigenetics.js +0 -1
  106. package/src/gep/execBridge.js +0 -1
  107. package/src/gep/executionTrace.js +0 -291
  108. package/src/gep/explore.js +0 -1
  109. package/src/gep/featureFlags.js +0 -121
  110. package/src/gep/gitOps.js +0 -265
  111. package/src/gep/hash.js +0 -1
  112. package/src/gep/hostErrorClassifier.js +0 -34
  113. package/src/gep/hubFetch.js +0 -1
  114. package/src/gep/hubReview.js +0 -1
  115. package/src/gep/hubSearch.js +0 -1
  116. package/src/gep/hubVerify.js +0 -1
  117. package/src/gep/idleScheduler.js +0 -400
  118. package/src/gep/issueReporter.js +0 -416
  119. package/src/gep/learningSignals.js +0 -1
  120. package/src/gep/llmReview.js +0 -92
  121. package/src/gep/localStateAwareness.js +0 -243
  122. package/src/gep/mailboxTransport.js +0 -119
  123. package/src/gep/memoryGraph.js +0 -1
  124. package/src/gep/memoryGraphAdapter.js +0 -1
  125. package/src/gep/mutation.js +0 -1
  126. package/src/gep/narrativeMemory.js +0 -1
  127. package/src/gep/oauthLogin.js +0 -181
  128. package/src/gep/openPRRegistry.js +0 -1
  129. package/src/gep/paths.js +0 -522
  130. package/src/gep/personality.js +0 -1
  131. package/src/gep/policyCheck.js +0 -1
  132. package/src/gep/portable.js +0 -103
  133. package/src/gep/privacyClient.js +0 -235
  134. package/src/gep/prompt.js +0 -1
  135. package/src/gep/questionGenerator.js +0 -518
  136. package/src/gep/recallInject.js +0 -1
  137. package/src/gep/recallVerifier.js +0 -1
  138. package/src/gep/reflection.js +0 -1
  139. package/src/gep/sanitize.js +0 -264
  140. package/src/gep/savingsCore.js +0 -1
  141. package/src/gep/schemas/capsule.js +0 -170
  142. package/src/gep/schemas/gene.js +0 -154
  143. package/src/gep/schemas/index.js +0 -8
  144. package/src/gep/schemas/protocol.js +0 -51
  145. package/src/gep/schemas/task.js +0 -74
  146. package/src/gep/selector.js +0 -1
  147. package/src/gep/selfPR.js +0 -469
  148. package/src/gep/signals.js +0 -776
  149. package/src/gep/skill2gep.js +0 -1056
  150. package/src/gep/skill2gepAudit.js +0 -303
  151. package/src/gep/skill2recipes.js +0 -511
  152. package/src/gep/skillDistiller.js +0 -1
  153. package/src/gep/skillPublisher.js +0 -358
  154. package/src/gep/solidify.js +0 -1
  155. package/src/gep/strategy.js +0 -1
  156. package/src/gep/taskReceiver.js +0 -575
  157. package/src/gep/tokenSavings.js +0 -1
  158. package/src/gep/trajectoryExport.js +0 -1
  159. package/src/gep/validationReport.js +0 -55
  160. package/src/gep/validator/index.js +0 -411
  161. package/src/gep/validator/reporter.js +0 -210
  162. package/src/gep/validator/sandboxExecutor.js +0 -480
  163. package/src/gep/validator/stakeBootstrap.js +0 -357
  164. package/src/gep/workspaceKeychain.js +0 -1
  165. package/src/ops/cleanup.js +0 -80
  166. package/src/ops/commentary.js +0 -60
  167. package/src/ops/health_check.js +0 -104
  168. package/src/ops/index.js +0 -11
  169. package/src/ops/innovation.js +0 -67
  170. package/src/ops/lifecycle.js +0 -798
  171. package/src/ops/self_repair.js +0 -76
  172. package/src/ops/skills_monitor.js +0 -147
  173. package/src/ops/trigger.js +0 -33
  174. package/src/proxy/clientSettings.js +0 -405
  175. package/src/proxy/envelope.js +0 -59
  176. package/src/proxy/extensions/dmHandler.js +0 -45
  177. package/src/proxy/extensions/sessionHandler.js +0 -141
  178. package/src/proxy/extensions/skillUpdater.js +0 -64
  179. package/src/proxy/extensions/traceControl.js +0 -1
  180. package/src/proxy/index.js +0 -1395
  181. package/src/proxy/inject.js +0 -1
  182. package/src/proxy/lifecycle/manager.js +0 -1568
  183. package/src/proxy/mailbox/state.js +0 -207
  184. package/src/proxy/mailbox/store.js +0 -590
  185. package/src/proxy/router/cache_passthrough.js +0 -26
  186. package/src/proxy/router/features.js +0 -84
  187. package/src/proxy/router/gemini_route.js +0 -154
  188. package/src/proxy/router/messages_route.js +0 -535
  189. package/src/proxy/router/model_router.js +0 -113
  190. package/src/proxy/router/models_route.js +0 -52
  191. package/src/proxy/router/ollama_route.js +0 -103
  192. package/src/proxy/router/responses_route.js +0 -170
  193. package/src/proxy/router/vertex_route.js +0 -110
  194. package/src/proxy/server/http.js +0 -363
  195. package/src/proxy/server/routes.js +0 -558
  196. package/src/proxy/server/settings.js +0 -115
  197. package/src/proxy/sync/engine.js +0 -179
  198. package/src/proxy/sync/inbound.js +0 -211
  199. package/src/proxy/sync/outbound.js +0 -320
  200. package/src/proxy/task/monitor.js +0 -131
  201. package/src/proxy/trace/extractor.js +0 -1
  202. package/src/proxy/trace/usage.js +0 -1
  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
- };