@renxqoo/renx-code 0.0.4 → 0.0.6

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 (210) hide show
  1. package/README.md +82 -51
  2. package/bin/renx.cjs +16 -0
  3. package/package.json +2 -45
  4. package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
  5. package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
  6. package/src/agent/runtime/runtime.test.ts +7 -4
  7. package/src/agent/runtime/runtime.ts +3 -9
  8. package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
  9. package/src/agent/runtime/source-modules.test.ts +16 -35
  10. package/src/agent/runtime/source-modules.ts +17 -0
  11. package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
  12. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
  13. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
  14. package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
  15. package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
  16. package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
  17. package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
  18. package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
  19. package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
  20. package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
  21. package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
  22. package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
  23. package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
  24. package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
  25. package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
  26. package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
  27. package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
  28. package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
  29. package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
  30. package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
  31. package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
  32. package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
  33. package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
  34. package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
  35. package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
  36. package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
  37. package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
  38. package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
  39. package/vendor/agent-root/src/agent/agent/error.ts +198 -0
  40. package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
  41. package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
  42. package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
  43. package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
  44. package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
  45. package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
  46. package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
  47. package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
  48. package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
  49. package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
  50. package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
  51. package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
  52. package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
  53. package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
  54. package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
  55. package/vendor/agent-root/src/agent/app/index.ts +5 -0
  56. package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
  57. package/vendor/agent-root/src/agent/app/ports.ts +72 -0
  58. package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
  59. package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
  60. package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
  61. package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
  62. package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
  63. package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
  64. package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
  65. package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
  66. package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
  67. package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
  68. package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
  69. package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
  70. package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
  71. package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
  72. package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
  73. package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
  74. package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
  75. package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
  76. package/vendor/agent-root/src/agent/error-contract.ts +154 -0
  77. package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
  78. package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
  79. package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
  80. package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
  81. package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
  82. package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
  83. package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
  84. package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
  85. package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
  86. package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
  87. package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
  88. package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
  89. package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
  90. package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
  91. package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
  92. package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
  93. package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
  94. package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
  95. package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
  96. package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
  97. package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
  98. package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
  99. package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
  100. package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
  101. package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
  102. package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
  103. package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
  104. package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
  105. package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
  106. package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
  107. package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
  108. package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
  109. package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
  110. package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
  111. package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
  112. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
  113. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
  114. package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
  115. package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
  116. package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
  117. package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
  118. package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
  119. package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
  120. package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
  121. package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
  122. package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
  123. package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
  124. package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
  125. package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
  126. package/vendor/agent-root/src/agent/tool/error.ts +131 -0
  127. package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
  128. package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
  129. package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
  130. package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
  131. package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
  132. package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
  133. package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
  134. package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
  135. package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
  136. package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
  137. package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
  138. package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
  139. package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
  140. package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
  141. package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
  142. package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
  143. package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
  144. package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
  145. package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
  146. package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
  147. package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
  148. package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
  149. package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
  150. package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
  151. package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
  152. package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
  153. package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
  154. package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
  155. package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
  156. package/vendor/agent-root/src/agent/tool/task.ts +209 -0
  157. package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
  158. package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
  159. package/vendor/agent-root/src/agent/tool/types.ts +116 -0
  160. package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
  161. package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
  162. package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
  163. package/vendor/agent-root/src/agent/types.ts +232 -0
  164. package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
  165. package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
  166. package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
  167. package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
  168. package/vendor/agent-root/src/agent/utils/index.ts +16 -0
  169. package/vendor/agent-root/src/agent/utils/message.ts +171 -0
  170. package/vendor/agent-root/src/agent/utils/token.ts +28 -0
  171. package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
  172. package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
  173. package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
  174. package/vendor/agent-root/src/config/index.ts +54 -0
  175. package/vendor/agent-root/src/config/loader.ts +431 -0
  176. package/vendor/agent-root/src/config/paths.ts +30 -0
  177. package/vendor/agent-root/src/config/runtime.ts +163 -0
  178. package/vendor/agent-root/src/config/types.ts +70 -0
  179. package/vendor/agent-root/src/logger/index.ts +57 -0
  180. package/vendor/agent-root/src/logger/logger.ts +819 -0
  181. package/vendor/agent-root/src/logger/types.ts +150 -0
  182. package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
  183. package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
  184. package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
  185. package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
  186. package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
  187. package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
  188. package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
  189. package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
  190. package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
  191. package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
  192. package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
  193. package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
  194. package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
  195. package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
  196. package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
  197. package/vendor/agent-root/src/providers/http/client.ts +289 -0
  198. package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
  199. package/vendor/agent-root/src/providers/index.ts +76 -0
  200. package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
  201. package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
  202. package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
  203. package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
  204. package/vendor/agent-root/src/providers/registry.ts +135 -0
  205. package/vendor/agent-root/src/providers/types/api.ts +284 -0
  206. package/vendor/agent-root/src/providers/types/config.ts +58 -0
  207. package/vendor/agent-root/src/providers/types/errors.ts +323 -0
  208. package/vendor/agent-root/src/providers/types/index.ts +72 -0
  209. package/vendor/agent-root/src/providers/types/provider.ts +45 -0
  210. package/vendor/agent-root/src/providers/types/registry.ts +88 -0
@@ -0,0 +1,431 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { LogLevel } from '../logger';
4
+ import {
5
+ resolveRenxDatabasePath,
6
+ resolveRenxHome,
7
+ resolveRenxLogsDir,
8
+ resolveRenxStorageRoot,
9
+ } from './paths';
10
+ import type { LoadConfigOptions, LogConfig, RenxConfig, ResolvedConfig } from './types';
11
+
12
+ const PROJECT_DIR_NAME = '.renx';
13
+ const CONFIG_FILENAME = 'config.json';
14
+
15
+ const DEFAULTS: RenxConfig = {
16
+ log: {
17
+ level: 'INFO',
18
+ format: 'pretty',
19
+ console: false,
20
+ file: true,
21
+ },
22
+ storage: {
23
+ fileHistory: {
24
+ enabled: true,
25
+ maxPerFile: 20,
26
+ maxAgeDays: 14,
27
+ maxTotalMb: 500,
28
+ },
29
+ },
30
+ agent: {
31
+ maxSteps: 10000,
32
+ confirmationMode: 'manual',
33
+ defaultModel: 'qwen3.5-plus',
34
+ },
35
+ };
36
+
37
+ function writeConfigFile(configPath: string, config: Partial<RenxConfig>): string {
38
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
39
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
40
+ return configPath;
41
+ }
42
+
43
+ function ensureGlobalConfigFile(globalDir: string): string {
44
+ const globalConfigPath = path.join(globalDir, CONFIG_FILENAME);
45
+ if (!fs.existsSync(globalConfigPath)) {
46
+ writeConfigFile(globalConfigPath, DEFAULTS);
47
+ }
48
+ return globalConfigPath;
49
+ }
50
+
51
+ function readJsonFile<T>(filePath: string): T | null {
52
+ try {
53
+ const content = fs.readFileSync(filePath, 'utf8');
54
+ return JSON.parse(content) as T;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ function parseLogLevelValue(raw: string | undefined): LogLevel | null {
61
+ if (!raw) {
62
+ return null;
63
+ }
64
+
65
+ const normalized = raw.trim().toUpperCase();
66
+ switch (normalized) {
67
+ case 'TRACE':
68
+ return LogLevel.TRACE;
69
+ case 'DEBUG':
70
+ return LogLevel.DEBUG;
71
+ case 'INFO':
72
+ return LogLevel.INFO;
73
+ case 'WARN':
74
+ return LogLevel.WARN;
75
+ case 'ERROR':
76
+ return LogLevel.ERROR;
77
+ case 'FATAL':
78
+ return LogLevel.FATAL;
79
+ default:
80
+ return null;
81
+ }
82
+ }
83
+
84
+ function parseLogLevelString(raw: string | undefined): LogConfig['level'] | null {
85
+ if (!raw) {
86
+ return null;
87
+ }
88
+
89
+ const normalized = raw.trim().toUpperCase();
90
+ if (['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'].includes(normalized)) {
91
+ return normalized as LogConfig['level'];
92
+ }
93
+ return null;
94
+ }
95
+
96
+ function parseBoolean(raw: string | undefined): boolean | null {
97
+ if (raw === undefined) {
98
+ return null;
99
+ }
100
+
101
+ const normalized = raw.trim().toLowerCase();
102
+ if (['1', 'true', 'yes', 'on'].includes(normalized)) {
103
+ return true;
104
+ }
105
+ if (['0', 'false', 'no', 'off'].includes(normalized)) {
106
+ return false;
107
+ }
108
+ return null;
109
+ }
110
+
111
+ function parsePositiveInt(raw: string | undefined): number | null {
112
+ if (!raw) {
113
+ return null;
114
+ }
115
+
116
+ const parsed = Number.parseInt(raw, 10);
117
+ if (!Number.isFinite(parsed) || parsed <= 0) {
118
+ return null;
119
+ }
120
+ return parsed;
121
+ }
122
+
123
+ function parseNonNegativeInt(raw: string | undefined): number | null {
124
+ if (!raw) {
125
+ return null;
126
+ }
127
+
128
+ const parsed = Number.parseInt(raw, 10);
129
+ if (!Number.isFinite(parsed) || parsed < 0) {
130
+ return null;
131
+ }
132
+ return parsed;
133
+ }
134
+
135
+ function parseConfirmationMode(
136
+ raw: string | undefined
137
+ ): ResolvedConfig['agent']['confirmationMode'] | null {
138
+ if (!raw) {
139
+ return null;
140
+ }
141
+
142
+ const normalized = raw.trim().toLowerCase().replace(/_/g, '-');
143
+ if (normalized === 'manual' || normalized === 'auto-approve' || normalized === 'auto-deny') {
144
+ return normalized;
145
+ }
146
+ return null;
147
+ }
148
+
149
+ function deepMerge<T extends object>(base: T, override: Partial<T>): T {
150
+ const result = { ...base };
151
+ for (const key of Object.keys(override) as Array<keyof T>) {
152
+ const value = override[key];
153
+ if (value === undefined || value === null) {
154
+ continue;
155
+ }
156
+
157
+ if (
158
+ typeof value === 'object' &&
159
+ !Array.isArray(value) &&
160
+ typeof base[key] === 'object' &&
161
+ base[key] !== null &&
162
+ !Array.isArray(base[key])
163
+ ) {
164
+ result[key] = deepMerge(
165
+ base[key] as Record<string, unknown>,
166
+ value as Record<string, unknown>
167
+ ) as T[keyof T];
168
+ continue;
169
+ }
170
+
171
+ result[key] = value as T[keyof T];
172
+ }
173
+
174
+ return result;
175
+ }
176
+
177
+ function applyEnvOverrides(config: RenxConfig, env: NodeJS.ProcessEnv): RenxConfig {
178
+ const result: RenxConfig = { ...config };
179
+
180
+ const logLevel = parseLogLevelString(env.AGENT_LOG_LEVEL);
181
+ const logFormat = env.AGENT_LOG_FORMAT;
182
+ const logConsole = parseBoolean(env.AGENT_LOG_CONSOLE);
183
+ const logFile = parseBoolean(env.AGENT_LOG_FILE_ENABLED);
184
+
185
+ if (logLevel || logFormat || logConsole !== null || logFile !== null) {
186
+ result.log = { ...(result.log ?? {}) };
187
+ if (logLevel) {
188
+ result.log.level = logLevel;
189
+ }
190
+ if (logFormat === 'json' || logFormat === 'pretty') {
191
+ result.log.format = logFormat;
192
+ }
193
+ if (logConsole !== null) {
194
+ result.log.console = logConsole;
195
+ }
196
+ if (logFile !== null) {
197
+ result.log.file = logFile;
198
+ }
199
+ }
200
+
201
+ const fileHistoryEnabled = parseBoolean(env.AGENT_FILE_HISTORY_ENABLED);
202
+ const maxPerFile = parseNonNegativeInt(env.AGENT_FILE_HISTORY_MAX_PER_FILE);
203
+ const maxAgeDays = parseNonNegativeInt(env.AGENT_FILE_HISTORY_MAX_AGE_DAYS);
204
+ const maxTotalMb = parseNonNegativeInt(env.AGENT_FILE_HISTORY_MAX_TOTAL_MB);
205
+
206
+ if (
207
+ fileHistoryEnabled !== null ||
208
+ maxPerFile !== null ||
209
+ maxAgeDays !== null ||
210
+ maxTotalMb !== null
211
+ ) {
212
+ result.storage = { ...(result.storage ?? {}) };
213
+ result.storage.fileHistory = { ...(result.storage.fileHistory ?? {}) };
214
+ if (fileHistoryEnabled !== null) {
215
+ result.storage.fileHistory.enabled = fileHistoryEnabled;
216
+ }
217
+ if (maxPerFile !== null) {
218
+ result.storage.fileHistory.maxPerFile = maxPerFile;
219
+ }
220
+ if (maxAgeDays !== null) {
221
+ result.storage.fileHistory.maxAgeDays = maxAgeDays;
222
+ }
223
+ if (maxTotalMb !== null) {
224
+ result.storage.fileHistory.maxTotalMb = maxTotalMb;
225
+ }
226
+ }
227
+
228
+ const confirmationMode = parseConfirmationMode(env.AGENT_TOOL_CONFIRMATION_MODE);
229
+ const defaultModel = env.AGENT_MODEL?.trim();
230
+ const maxSteps = parsePositiveInt(env.AGENT_MAX_STEPS);
231
+
232
+ if (confirmationMode || defaultModel || maxSteps !== null) {
233
+ result.agent = { ...(result.agent ?? {}) };
234
+ if (confirmationMode) {
235
+ result.agent.confirmationMode = confirmationMode;
236
+ }
237
+ if (defaultModel) {
238
+ result.agent.defaultModel = defaultModel;
239
+ }
240
+ if (maxSteps !== null) {
241
+ result.agent.maxSteps = maxSteps;
242
+ }
243
+ }
244
+
245
+ return result;
246
+ }
247
+
248
+ function resolveConfig(
249
+ merged: RenxConfig,
250
+ env: NodeJS.ProcessEnv,
251
+ sources: { global: string | null; project: string | null }
252
+ ): ResolvedConfig {
253
+ const logDir = resolveRenxLogsDir(env);
254
+ const storageRoot = resolveRenxStorageRoot(env);
255
+ const dbPath = resolveRenxDatabasePath(env);
256
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
257
+
258
+ return {
259
+ log: {
260
+ level: parseLogLevelValue(merged.log?.level) ?? LogLevel.INFO,
261
+ format: merged.log?.format ?? 'pretty',
262
+ console: merged.log?.console ?? true,
263
+ file: merged.log?.file ?? false,
264
+ dir: logDir,
265
+ filePath: path.join(logDir, `${timestamp}.log`),
266
+ },
267
+ storage: {
268
+ root: storageRoot,
269
+ fileHistory: {
270
+ enabled: merged.storage?.fileHistory?.enabled ?? true,
271
+ maxPerFile: merged.storage?.fileHistory?.maxPerFile ?? 20,
272
+ maxAgeDays: merged.storage?.fileHistory?.maxAgeDays ?? 14,
273
+ maxTotalMb: merged.storage?.fileHistory?.maxTotalMb ?? 500,
274
+ },
275
+ },
276
+ db: {
277
+ path: dbPath,
278
+ },
279
+ agent: {
280
+ maxSteps: merged.agent?.maxSteps ?? 10000,
281
+ confirmationMode: merged.agent?.confirmationMode ?? 'manual',
282
+ defaultModel: merged.agent?.defaultModel ?? 'qwen3.5-plus',
283
+ },
284
+ sources,
285
+ };
286
+ }
287
+
288
+ export function loadConfig(options: LoadConfigOptions = {}): ResolvedConfig {
289
+ const env = options.env ?? process.env;
290
+ const projectRoot = options.projectRoot ?? process.cwd();
291
+ const globalDir = options.globalDir ?? resolveRenxHome(env);
292
+
293
+ let config: RenxConfig = { ...DEFAULTS };
294
+
295
+ const globalConfigPath = ensureGlobalConfigFile(globalDir);
296
+ const globalConfig = readJsonFile<RenxConfig>(globalConfigPath);
297
+ let globalSource: string | null = null;
298
+ if (globalConfig) {
299
+ config = deepMerge(config, globalConfig);
300
+ globalSource = globalConfigPath;
301
+ }
302
+
303
+ const projectConfigPath = path.join(projectRoot, PROJECT_DIR_NAME, CONFIG_FILENAME);
304
+ const projectConfig = readJsonFile<RenxConfig>(projectConfigPath);
305
+ let projectSource: string | null = null;
306
+ if (projectConfig) {
307
+ config = deepMerge(config, projectConfig);
308
+ projectSource = projectConfigPath;
309
+ }
310
+
311
+ if (options.loadEnv !== false) {
312
+ config = applyEnvOverrides(config, env);
313
+ }
314
+
315
+ return resolveConfig(config, env, {
316
+ global: globalSource,
317
+ project: projectSource,
318
+ });
319
+ }
320
+
321
+ export function loadConfigToEnv(options: LoadConfigOptions = {}): string[] {
322
+ const projectRoot = options.projectRoot ?? process.cwd();
323
+ const globalDir = options.globalDir ?? resolveRenxHome(process.env);
324
+ const loadedFiles: string[] = [];
325
+
326
+ const protectedEnvKeys = new Set(Object.keys(process.env));
327
+
328
+ const globalConfigPath = ensureGlobalConfigFile(globalDir);
329
+ const globalConfig = readJsonFile<RenxConfig>(globalConfigPath);
330
+ if (globalConfig) {
331
+ applyConfigToEnv(globalConfig, protectedEnvKeys);
332
+ loadedFiles.push(globalConfigPath);
333
+ }
334
+
335
+ const projectConfigPath = path.join(projectRoot, PROJECT_DIR_NAME, CONFIG_FILENAME);
336
+ const projectConfig = readJsonFile<RenxConfig>(projectConfigPath);
337
+ if (projectConfig) {
338
+ applyConfigToEnv(projectConfig, protectedEnvKeys);
339
+ loadedFiles.push(projectConfigPath);
340
+ }
341
+
342
+ return loadedFiles;
343
+ }
344
+
345
+ function applyConfigToEnv(config: RenxConfig, protectedEnvKeys: Set<string>): void {
346
+ const setIfUnset = (key: string, value: string | undefined) => {
347
+ if (value !== undefined && !protectedEnvKeys.has(key)) {
348
+ process.env[key] = value;
349
+ }
350
+ };
351
+
352
+ if (config.log) {
353
+ setIfUnset('AGENT_LOG_LEVEL', config.log.level);
354
+ setIfUnset('AGENT_LOG_FORMAT', config.log.format);
355
+ setIfUnset(
356
+ 'AGENT_LOG_CONSOLE',
357
+ config.log.console !== undefined ? String(config.log.console) : undefined
358
+ );
359
+ setIfUnset(
360
+ 'AGENT_LOG_FILE_ENABLED',
361
+ config.log.file !== undefined ? String(config.log.file) : undefined
362
+ );
363
+ }
364
+
365
+ if (config.storage?.fileHistory) {
366
+ setIfUnset(
367
+ 'AGENT_FILE_HISTORY_ENABLED',
368
+ config.storage.fileHistory.enabled !== undefined
369
+ ? String(config.storage.fileHistory.enabled)
370
+ : undefined
371
+ );
372
+ setIfUnset(
373
+ 'AGENT_FILE_HISTORY_MAX_PER_FILE',
374
+ config.storage.fileHistory.maxPerFile !== undefined
375
+ ? String(config.storage.fileHistory.maxPerFile)
376
+ : undefined
377
+ );
378
+ setIfUnset(
379
+ 'AGENT_FILE_HISTORY_MAX_AGE_DAYS',
380
+ config.storage.fileHistory.maxAgeDays !== undefined
381
+ ? String(config.storage.fileHistory.maxAgeDays)
382
+ : undefined
383
+ );
384
+ setIfUnset(
385
+ 'AGENT_FILE_HISTORY_MAX_TOTAL_MB',
386
+ config.storage.fileHistory.maxTotalMb !== undefined
387
+ ? String(config.storage.fileHistory.maxTotalMb)
388
+ : undefined
389
+ );
390
+ }
391
+
392
+ if (config.agent) {
393
+ setIfUnset('AGENT_TOOL_CONFIRMATION_MODE', config.agent.confirmationMode);
394
+ setIfUnset('AGENT_MODEL', config.agent.defaultModel);
395
+ setIfUnset(
396
+ 'AGENT_MAX_STEPS',
397
+ config.agent.maxSteps !== undefined ? String(config.agent.maxSteps) : undefined
398
+ );
399
+ }
400
+ }
401
+
402
+ export function getGlobalConfigDir(): string {
403
+ return resolveRenxHome(process.env);
404
+ }
405
+
406
+ export function getProjectConfigDir(projectRoot?: string): string {
407
+ return path.join(projectRoot ?? process.cwd(), PROJECT_DIR_NAME);
408
+ }
409
+
410
+ export function getGlobalConfigPath(): string {
411
+ return path.join(getGlobalConfigDir(), CONFIG_FILENAME);
412
+ }
413
+
414
+ export function getProjectConfigPath(projectRoot?: string): string {
415
+ return path.join(getProjectConfigDir(projectRoot), CONFIG_FILENAME);
416
+ }
417
+
418
+ export function ensureConfigDirs(projectRoot?: string): void {
419
+ fs.mkdirSync(getGlobalConfigDir(), { recursive: true });
420
+ fs.mkdirSync(getProjectConfigDir(projectRoot), { recursive: true });
421
+ }
422
+
423
+ export function writeProjectConfig(config: Partial<RenxConfig>, projectRoot?: string): string {
424
+ const configPath = getProjectConfigPath(projectRoot);
425
+ return writeConfigFile(configPath, config);
426
+ }
427
+
428
+ export function writeGlobalConfig(config: Partial<RenxConfig>): string {
429
+ const configPath = getGlobalConfigPath();
430
+ return writeConfigFile(configPath, config);
431
+ }
@@ -0,0 +1,30 @@
1
+ import { homedir } from 'node:os';
2
+ import * as path from 'node:path';
3
+
4
+ export const RENX_HOME_ENV = 'RENX_HOME';
5
+ const DEFAULT_RENX_DIRNAME = '.renx';
6
+
7
+ function readEnvPath(env: NodeJS.ProcessEnv, key: string): string | undefined {
8
+ const value = env[key]?.trim();
9
+ return value && value.length > 0 ? path.resolve(value) : undefined;
10
+ }
11
+
12
+ export function resolveRenxHome(env: NodeJS.ProcessEnv = process.env): string {
13
+ return readEnvPath(env, RENX_HOME_ENV) ?? path.join(homedir(), DEFAULT_RENX_DIRNAME);
14
+ }
15
+
16
+ export function resolveRenxLogsDir(env: NodeJS.ProcessEnv = process.env): string {
17
+ return path.join(resolveRenxHome(env), 'logs');
18
+ }
19
+
20
+ export function resolveRenxStorageRoot(env: NodeJS.ProcessEnv = process.env): string {
21
+ return path.join(resolveRenxHome(env), 'storage');
22
+ }
23
+
24
+ export function resolveRenxTaskDir(env: NodeJS.ProcessEnv = process.env): string {
25
+ return path.join(resolveRenxHome(env), 'task');
26
+ }
27
+
28
+ export function resolveRenxDatabasePath(env: NodeJS.ProcessEnv = process.env): string {
29
+ return path.join(resolveRenxHome(env), 'data.db');
30
+ }
@@ -0,0 +1,163 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { createLogger, LogLevel, type Logger } from '../logger';
4
+ import { resolveRenxLogsDir } from './paths';
5
+
6
+ export type LogFormat = 'json' | 'pretty';
7
+
8
+ export interface RuntimeLogConfig {
9
+ level: LogLevel;
10
+ consoleEnabled: boolean;
11
+ fileEnabled: boolean;
12
+ filePath: string;
13
+ format: LogFormat;
14
+ }
15
+
16
+ export interface RuntimeConfig {
17
+ log: RuntimeLogConfig;
18
+ }
19
+
20
+ export interface LoadEnvFilesOptions {
21
+ files?: string[];
22
+ override?: boolean;
23
+ }
24
+
25
+ const TRUE_VALUES = new Set(['1', 'true', 'yes', 'on']);
26
+ const FALSE_VALUES = new Set(['0', 'false', 'no', 'off']);
27
+
28
+ function parseBooleanEnv(raw: string | undefined, fallback: boolean): boolean {
29
+ if (raw === undefined) {
30
+ return fallback;
31
+ }
32
+
33
+ const normalized = raw.trim().toLowerCase();
34
+ if (TRUE_VALUES.has(normalized)) {
35
+ return true;
36
+ }
37
+ if (FALSE_VALUES.has(normalized)) {
38
+ return false;
39
+ }
40
+ throw new Error(`Invalid boolean env value: "${raw}"`);
41
+ }
42
+
43
+ function parseLogLevel(raw: string | undefined): LogLevel {
44
+ if (!raw) {
45
+ return LogLevel.INFO;
46
+ }
47
+
48
+ const normalized = raw.trim().toUpperCase();
49
+ switch (normalized) {
50
+ case 'TRACE':
51
+ return LogLevel.TRACE;
52
+ case 'DEBUG':
53
+ return LogLevel.DEBUG;
54
+ case 'INFO':
55
+ return LogLevel.INFO;
56
+ case 'WARN':
57
+ return LogLevel.WARN;
58
+ case 'ERROR':
59
+ return LogLevel.ERROR;
60
+ case 'FATAL':
61
+ return LogLevel.FATAL;
62
+ default:
63
+ throw new Error(`Invalid AGENT_LOG_LEVEL: "${raw}"`);
64
+ }
65
+ }
66
+
67
+ function parseLogFormat(raw: string | undefined): LogFormat {
68
+ if (!raw) {
69
+ return 'pretty';
70
+ }
71
+
72
+ const normalized = raw.trim().toLowerCase();
73
+ if (normalized === 'json' || normalized === 'pretty') {
74
+ return normalized;
75
+ }
76
+
77
+ throw new Error(`Invalid AGENT_LOG_FORMAT: "${raw}" (expected "json" or "pretty")`);
78
+ }
79
+
80
+ export async function loadEnvFiles(
81
+ cwd = process.cwd(),
82
+ options: LoadEnvFilesOptions = {}
83
+ ): Promise<string[]> {
84
+ const files = options.files ?? ['.env', '.env.development'];
85
+ const override = options.override ?? false;
86
+ const loadedFiles: string[] = [];
87
+
88
+ for (const fileName of files) {
89
+ const filePath = path.resolve(cwd, fileName);
90
+ let content: string;
91
+ try {
92
+ content = await fs.readFile(filePath, 'utf8');
93
+ } catch {
94
+ continue;
95
+ }
96
+
97
+ for (const line of content.split(/\r?\n/)) {
98
+ const trimmed = line.trim();
99
+ if (!trimmed || trimmed.startsWith('#')) {
100
+ continue;
101
+ }
102
+
103
+ const equalIndex = trimmed.indexOf('=');
104
+ if (equalIndex <= 0) {
105
+ continue;
106
+ }
107
+
108
+ const key = trimmed.slice(0, equalIndex).trim();
109
+ let value = trimmed.slice(equalIndex + 1).trim();
110
+ if (
111
+ (value.startsWith('"') && value.endsWith('"')) ||
112
+ (value.startsWith("'") && value.endsWith("'"))
113
+ ) {
114
+ value = value.slice(1, -1);
115
+ }
116
+
117
+ if (override || process.env[key] === undefined) {
118
+ process.env[key] = value;
119
+ }
120
+ }
121
+
122
+ loadedFiles.push(filePath);
123
+ }
124
+
125
+ return loadedFiles;
126
+ }
127
+
128
+ export function loadRuntimeConfigFromEnv(
129
+ env: NodeJS.ProcessEnv = process.env,
130
+ _cwd = process.cwd()
131
+ ): RuntimeConfig {
132
+ const logDir = resolveRenxLogsDir(env);
133
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
134
+
135
+ return {
136
+ log: {
137
+ level: parseLogLevel(env.AGENT_LOG_LEVEL),
138
+ consoleEnabled: parseBooleanEnv(env.AGENT_LOG_CONSOLE, false),
139
+ fileEnabled: parseBooleanEnv(env.AGENT_LOG_FILE_ENABLED, true),
140
+ filePath: path.join(logDir, `${timestamp}.log`),
141
+ format: parseLogFormat(env.AGENT_LOG_FORMAT),
142
+ },
143
+ };
144
+ }
145
+
146
+ export function createLoggerFromRuntimeConfig(config: RuntimeConfig): Logger {
147
+ return createLogger({
148
+ level: config.log.level,
149
+ console: { enabled: config.log.consoleEnabled, format: config.log.format },
150
+ file: {
151
+ enabled: config.log.fileEnabled,
152
+ filepath: config.log.filePath,
153
+ format: config.log.format,
154
+ },
155
+ });
156
+ }
157
+
158
+ export function createLoggerFromEnv(
159
+ env: NodeJS.ProcessEnv = process.env,
160
+ cwd = process.cwd()
161
+ ): Logger {
162
+ return createLoggerFromRuntimeConfig(loadRuntimeConfigFromEnv(env, cwd));
163
+ }
@@ -0,0 +1,70 @@
1
+ import type { LogLevel } from '../logger';
2
+
3
+ export interface LogConfig {
4
+ level?: 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
5
+ format?: 'json' | 'pretty';
6
+ console?: boolean;
7
+ file?: boolean;
8
+ }
9
+
10
+ export interface FileHistoryConfig {
11
+ enabled?: boolean;
12
+ maxPerFile?: number;
13
+ maxAgeDays?: number;
14
+ maxTotalMb?: number;
15
+ }
16
+
17
+ export interface StorageConfig {
18
+ fileHistory?: FileHistoryConfig;
19
+ }
20
+
21
+ export interface AgentConfig {
22
+ maxSteps?: number;
23
+ confirmationMode?: 'manual' | 'auto-approve' | 'auto-deny';
24
+ defaultModel?: string;
25
+ }
26
+
27
+ export interface RenxConfig {
28
+ log?: LogConfig;
29
+ storage?: StorageConfig;
30
+ agent?: AgentConfig;
31
+ }
32
+
33
+ export interface ResolvedConfig {
34
+ log: {
35
+ level: LogLevel;
36
+ format: 'json' | 'pretty';
37
+ console: boolean;
38
+ file: boolean;
39
+ dir: string;
40
+ filePath: string;
41
+ };
42
+ storage: {
43
+ root: string;
44
+ fileHistory: {
45
+ enabled: boolean;
46
+ maxPerFile: number;
47
+ maxAgeDays: number;
48
+ maxTotalMb: number;
49
+ };
50
+ };
51
+ db: {
52
+ path: string;
53
+ };
54
+ agent: {
55
+ maxSteps: number;
56
+ confirmationMode: 'manual' | 'auto-approve' | 'auto-deny';
57
+ defaultModel: string;
58
+ };
59
+ sources: {
60
+ global: string | null;
61
+ project: string | null;
62
+ };
63
+ }
64
+
65
+ export interface LoadConfigOptions {
66
+ projectRoot?: string;
67
+ globalDir?: string;
68
+ loadEnv?: boolean;
69
+ env?: NodeJS.ProcessEnv;
70
+ }