@newsails/veil-cli 1.0.1
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.
- package/.veil/agents/analyst/AGENT.md +21 -0
- package/.veil/agents/analyst/agent.json +23 -0
- package/.veil/agents/assistant/AGENT.md +15 -0
- package/.veil/agents/assistant/agent.json +19 -0
- package/.veil/agents/coder/AGENT.md +18 -0
- package/.veil/agents/coder/agent.json +19 -0
- package/.veil/agents/hello/AGENT.md +5 -0
- package/.veil/agents/hello/agent.json +13 -0
- package/.veil/agents/writer/AGENT.md +12 -0
- package/.veil/agents/writer/agent.json +17 -0
- package/.veil/memory/MEMORY.md +343 -0
- package/.veil/memory/agents/analyst/MEMORY.md +55 -0
- package/.veil/memory/agents/hello/MEMORY.md +12 -0
- package/.veil/runtime.pid +1 -0
- package/.veil/settings.json +10 -0
- package/.veil-studio/studio.db +0 -0
- package/.veil-studio/studio.db-shm +0 -0
- package/.veil-studio/studio.db-wal +0 -0
- package/PLAN/01-vision.md +26 -0
- package/PLAN/02-tech-stack.md +94 -0
- package/PLAN/03-agents.md +232 -0
- package/PLAN/04-runtime.md +171 -0
- package/PLAN/05-tools.md +211 -0
- package/PLAN/06-communication.md +243 -0
- package/PLAN/07-storage.md +218 -0
- package/PLAN/08-api-cli.md +153 -0
- package/PLAN/09-permissions.md +108 -0
- package/PLAN/10-ably.md +105 -0
- package/PLAN/11-file-formats.md +442 -0
- package/PLAN/12-folder-structure.md +205 -0
- package/PLAN/13-operations.md +212 -0
- package/PLAN/README.md +23 -0
- package/README.md +128 -0
- package/REPORT.md +174 -0
- package/TODO.md +45 -0
- package/ai-tests/FRONTEND_PROMPT.md +220 -0
- package/ai-tests/Research & Planning.md +814 -0
- package/ai-tests/prompt-001-basic-api.md +230 -0
- package/ai-tests/prompt-002-basic-flows.md +230 -0
- package/ai-tests/prompt-003-agent-behaviors.md +220 -0
- package/api/middleware.js +60 -0
- package/api/routes/agents.js +193 -0
- package/api/routes/chat.js +93 -0
- package/api/routes/completions.js +122 -0
- package/api/routes/daemons.js +80 -0
- package/api/routes/memory.js +169 -0
- package/api/routes/models.js +40 -0
- package/api/routes/remote-methods.js +74 -0
- package/api/routes/sessions.js +208 -0
- package/api/routes/settings.js +108 -0
- package/api/routes/system.js +50 -0
- package/api/routes/tasks.js +270 -0
- package/api/server.js +120 -0
- package/cli/formatter.js +70 -0
- package/cli/index.js +443 -0
- package/cli/parser.js +113 -0
- package/config/config.json +10 -0
- package/config/models.json +6826 -0
- package/core/agent.js +329 -0
- package/core/cancel.js +38 -0
- package/core/compaction.js +176 -0
- package/core/events.js +13 -0
- package/core/loop.js +564 -0
- package/core/memory.js +51 -0
- package/core/prompt.js +185 -0
- package/core/queue.js +96 -0
- package/core/registry.js +291 -0
- package/core/remote-methods.js +124 -0
- package/core/router.js +386 -0
- package/core/running-sessions.js +18 -0
- package/docs/api/01-system.md +84 -0
- package/docs/api/02-agents.md +374 -0
- package/docs/api/03-chat.md +269 -0
- package/docs/api/04-tasks.md +470 -0
- package/docs/api/05-sessions.md +444 -0
- package/docs/api/06-daemons.md +142 -0
- package/docs/api/07-memory.md +186 -0
- package/docs/api/08-settings.md +133 -0
- package/docs/api/09-models.md +119 -0
- package/docs/api/09-websocket.md +350 -0
- package/docs/api/10-completions.md +134 -0
- package/docs/api/README.md +116 -0
- package/docs/guide/01-quickstart.md +220 -0
- package/docs/guide/02-folder-structure.md +185 -0
- package/docs/guide/03-configuration.md +252 -0
- package/docs/guide/04-agents.md +267 -0
- package/docs/guide/05-cli.md +290 -0
- package/docs/guide/06-tools.md +643 -0
- package/docs/guide/07-permissions.md +236 -0
- package/docs/guide/08-memory.md +139 -0
- package/docs/guide/09-multi-agent.md +271 -0
- package/docs/guide/10-daemons.md +226 -0
- package/docs/guide/README.md +53 -0
- package/docs/index.html +623 -0
- package/examples/README.md +151 -0
- package/examples/agents/assistant/AGENT.md +31 -0
- package/examples/agents/assistant/SOUL.md +9 -0
- package/examples/agents/assistant/agent.json +74 -0
- package/examples/agents/hello/AGENT.md +15 -0
- package/examples/agents/hello/agent.json +14 -0
- package/examples/agents/monitor/AGENT.md +51 -0
- package/examples/agents/monitor/agent.json +33 -0
- package/examples/agents/monitor/heartbeats/monitor.md +24 -0
- package/examples/agents/orchestrator/AGENT.md +70 -0
- package/examples/agents/orchestrator/agent.json +30 -0
- package/examples/agents/researcher/AGENT.md +52 -0
- package/examples/agents/researcher/agent.json +49 -0
- package/examples/agents/researcher/skills/web-research.md +28 -0
- package/examples/skills/code-review.md +72 -0
- package/examples/skills/summarise.md +59 -0
- package/examples/skills/web-research.md +42 -0
- package/examples/tools/word-count/index.js +27 -0
- package/examples/tools/word-count/tool.json +18 -0
- package/infrastructure/database.js +563 -0
- package/infrastructure/scheduler.js +122 -0
- package/llm/client.js +206 -0
- package/migrations/001-initial.sql +121 -0
- package/migrations/002-debuggability.sql +13 -0
- package/migrations/003-drop-orphaned-columns.sql +72 -0
- package/migrations/004-session-message-token-fields.sql +78 -0
- package/migrations/005-session-thinking.sql +5 -0
- package/package.json +30 -0
- package/schemas/agent.json +143 -0
- package/schemas/settings.json +111 -0
- package/scripts/fetch-models.js +93 -0
- package/session-debug-scenario.md +248 -0
- package/settings/fields.js +52 -0
- package/system-prompts/base-core.md +7 -0
- package/system-prompts/environment.md +13 -0
- package/system-prompts/reminders/anti-drift.md +6 -0
- package/system-prompts/reminders/stall-recovery.md +10 -0
- package/system-prompts/safety-rules.md +25 -0
- package/system-prompts/task-heuristics.md +27 -0
- package/test/client.js +71 -0
- package/test/integration/01-health.test.js +25 -0
- package/test/integration/02-agents.test.js +80 -0
- package/test/integration/03-chat-hello.test.js +48 -0
- package/test/integration/04-chat-multiturn.test.js +61 -0
- package/test/integration/05-chat-writer.test.js +48 -0
- package/test/integration/06-task-basic.test.js +68 -0
- package/test/integration/07-task-tools.test.js +74 -0
- package/test/integration/08-task-code-analysis.test.js +69 -0
- package/test/integration/09-memory-analyst.test.js +63 -0
- package/test/integration/10-task-advanced.test.js +85 -0
- package/test/integration/11-sessions-advanced.test.js +84 -0
- package/test/integration/12-assistant-chat-tools.test.js +75 -0
- package/test/integration/13-edge-cases.test.js +99 -0
- package/test/integration/14-cancel.test.js +62 -0
- package/test/integration/15-debug.test.js +106 -0
- package/test/integration/16-memory-api.test.js +83 -0
- package/test/integration/17-settings-api.test.js +41 -0
- package/test/integration/18-tool-search-activation.test.js +119 -0
- package/test/results/.gitkeep +0 -0
- package/test/runner.js +206 -0
- package/test/smoke.js +216 -0
- package/tools/agent_message.js +85 -0
- package/tools/agent_send.js +80 -0
- package/tools/agent_spawn.js +44 -0
- package/tools/bash.js +49 -0
- package/tools/edit_file.js +41 -0
- package/tools/glob.js +64 -0
- package/tools/grep.js +82 -0
- package/tools/list_dir.js +63 -0
- package/tools/log_write.js +31 -0
- package/tools/memory_read.js +38 -0
- package/tools/memory_search.js +65 -0
- package/tools/memory_write.js +42 -0
- package/tools/read_file.js +48 -0
- package/tools/sleep.js +22 -0
- package/tools/task_create.js +41 -0
- package/tools/task_respond.js +37 -0
- package/tools/task_spawn.js +64 -0
- package/tools/task_status.js +39 -0
- package/tools/task_subscribe.js +37 -0
- package/tools/todo_read.js +26 -0
- package/tools/todo_write.js +38 -0
- package/tools/tool_activate.js +24 -0
- package/tools/tool_search.js +24 -0
- package/tools/web_fetch.js +50 -0
- package/tools/web_search.js +52 -0
- package/tools/write_file.js +28 -0
- package/ui/api.js +190 -0
- package/ui/app.js +281 -0
- package/ui/index.html +382 -0
- package/ui/views/agents.js +377 -0
- package/ui/views/chat.js +610 -0
- package/ui/views/connection.js +96 -0
- package/ui/views/daemons.js +129 -0
- package/ui/views/feed.js +194 -0
- package/ui/views/memory.js +263 -0
- package/ui/views/models.js +146 -0
- package/ui/views/sessions.js +314 -0
- package/ui/views/settings.js +142 -0
- package/ui/views/tasks.js +415 -0
- package/utils/context.js +49 -0
- package/utils/id.js +16 -0
- package/utils/models.js +88 -0
- package/utils/paths.js +213 -0
- package/utils/settings.js +172 -0
package/utils/paths.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const CONFIG_DIR_NAME = '.veil';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns the .veil/ config directory for a project.
|
|
10
|
+
* @param {string} cwd - Project root directory
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
function getProjectConfigDir(cwd) {
|
|
14
|
+
return path.join(cwd, CONFIG_DIR_NAME);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the agents directory for a project.
|
|
19
|
+
* @param {string} cwd - Project root directory
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
function getProjectAgentsDir(cwd) {
|
|
23
|
+
return path.join(getProjectConfigDir(cwd), 'agents');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the path to a specific agent's folder in a project.
|
|
28
|
+
* @param {string} cwd - Project root directory
|
|
29
|
+
* @param {string} agentName - Agent name
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
function getAgentDir(cwd, agentName) {
|
|
33
|
+
return path.join(getProjectAgentsDir(cwd), agentName);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns the project-level settings.json path.
|
|
38
|
+
* @param {string} cwd - Project root directory
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
function getProjectSettingsPath(cwd) {
|
|
42
|
+
return path.join(getProjectConfigDir(cwd), 'settings.json');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns the project-level settings.local.json path.
|
|
47
|
+
* @param {string} cwd - Project root directory
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
function getProjectSettingsLocalPath(cwd) {
|
|
51
|
+
return path.join(getProjectConfigDir(cwd), 'settings.local.json');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Returns the project-level auth.json path.
|
|
56
|
+
* @param {string} cwd - Project root directory
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
function getProjectAuthPath(cwd) {
|
|
60
|
+
return path.join(getProjectConfigDir(cwd), 'auth.json');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns the project-level AGENT.md path (global instructions for all agents).
|
|
65
|
+
* @param {string} cwd - Project root directory
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function getProjectAgentMdPath(cwd) {
|
|
69
|
+
return path.join(getProjectConfigDir(cwd), 'AGENT.md');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Returns the project-level memory directory.
|
|
74
|
+
* @param {string} cwd - Project root directory
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function getProjectMemoryDir(cwd) {
|
|
78
|
+
return path.join(getProjectConfigDir(cwd), 'memory');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Returns the memory dir for a specific agent.
|
|
83
|
+
* @param {string} cwd - Project root directory
|
|
84
|
+
* @param {string} agentName - Agent name
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
function getAgentMemoryDir(cwd, agentName) {
|
|
88
|
+
return path.join(getProjectMemoryDir(cwd), 'agents', agentName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns the heartbeats directory.
|
|
93
|
+
* @param {string} cwd - Project root directory
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
function getHeartbeatsDir(cwd) {
|
|
97
|
+
return path.join(getProjectConfigDir(cwd), 'heartbeats');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the project-level tools directory.
|
|
102
|
+
* @param {string} cwd - Project root directory
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
function getProjectToolsDir(cwd) {
|
|
106
|
+
return path.join(getProjectConfigDir(cwd), 'tools');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns the project-level skills directory.
|
|
111
|
+
* @param {string} cwd - Project root directory
|
|
112
|
+
* @returns {string}
|
|
113
|
+
*/
|
|
114
|
+
function getProjectSkillsDir(cwd) {
|
|
115
|
+
return path.join(getProjectConfigDir(cwd), 'skills');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the PID file path.
|
|
120
|
+
* @param {string} cwd - Project root directory
|
|
121
|
+
* @returns {string}
|
|
122
|
+
*/
|
|
123
|
+
function getPidFilePath(cwd) {
|
|
124
|
+
return path.join(getProjectConfigDir(cwd), 'runtime.pid');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Global paths (~/.veil/) ─────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Returns the global ~/.veil/ directory.
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
function getGlobalConfigDir() {
|
|
134
|
+
return path.join(os.homedir(), CONFIG_DIR_NAME);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns the global agents directory.
|
|
139
|
+
* @returns {string}
|
|
140
|
+
*/
|
|
141
|
+
function getGlobalAgentsDir() {
|
|
142
|
+
return path.join(getGlobalConfigDir(), 'agents');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns the global tools directory.
|
|
147
|
+
* @returns {string}
|
|
148
|
+
*/
|
|
149
|
+
function getGlobalToolsDir() {
|
|
150
|
+
return path.join(getGlobalConfigDir(), 'tools');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Returns the global skills directory.
|
|
155
|
+
* @returns {string}
|
|
156
|
+
*/
|
|
157
|
+
function getGlobalSkillsDir() {
|
|
158
|
+
return path.join(getGlobalConfigDir(), 'skills');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Returns the global settings.json path.
|
|
163
|
+
* @returns {string}
|
|
164
|
+
*/
|
|
165
|
+
function getGlobalSettingsPath() {
|
|
166
|
+
return path.join(getGlobalConfigDir(), 'settings.json');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Returns the global auth.json path.
|
|
171
|
+
* @returns {string}
|
|
172
|
+
*/
|
|
173
|
+
function getGlobalAuthPath() {
|
|
174
|
+
return path.join(getGlobalConfigDir(), 'auth.json');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns the global SQLite database path.
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
function getDbPath() {
|
|
182
|
+
return path.join(getGlobalConfigDir(), 'data.db');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Returns the global backups directory.
|
|
187
|
+
* @returns {string}
|
|
188
|
+
*/
|
|
189
|
+
function getBackupsDir() {
|
|
190
|
+
return path.join(getGlobalConfigDir(), 'backups');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
getProjectConfigDir,
|
|
195
|
+
getProjectAgentsDir,
|
|
196
|
+
getAgentDir,
|
|
197
|
+
getProjectSettingsPath,
|
|
198
|
+
getProjectSettingsLocalPath,
|
|
199
|
+
getProjectAuthPath,
|
|
200
|
+
getProjectAgentMdPath,
|
|
201
|
+
getProjectMemoryDir,
|
|
202
|
+
getAgentMemoryDir,
|
|
203
|
+
getHeartbeatsDir,
|
|
204
|
+
getProjectToolsDir,
|
|
205
|
+
getProjectSkillsDir,
|
|
206
|
+
getPidFilePath,
|
|
207
|
+
getGlobalConfigDir,
|
|
208
|
+
getGlobalAgentsDir,
|
|
209
|
+
getGlobalToolsDir,
|
|
210
|
+
getGlobalSettingsPath,
|
|
211
|
+
getGlobalAuthPath,
|
|
212
|
+
getDbPath,
|
|
213
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const paths = require('./paths');
|
|
6
|
+
const F = require('../settings/fields');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Deep merge: target is mutated with source values (source wins).
|
|
10
|
+
* @param {Object} target
|
|
11
|
+
* @param {Object} source
|
|
12
|
+
* @returns {Object}
|
|
13
|
+
*/
|
|
14
|
+
function deepMerge(target, source) {
|
|
15
|
+
for (const key of Object.keys(source)) {
|
|
16
|
+
if (source[key] !== null && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
17
|
+
if (!target[key] || typeof target[key] !== 'object') target[key] = {};
|
|
18
|
+
deepMerge(target[key], source[key]);
|
|
19
|
+
} else {
|
|
20
|
+
target[key] = source[key];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return target;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Safely read + parse a JSON file. Returns null if file doesn't exist.
|
|
28
|
+
* @param {string} filePath
|
|
29
|
+
* @returns {Object|null}
|
|
30
|
+
*/
|
|
31
|
+
function readJsonFile(filePath) {
|
|
32
|
+
try {
|
|
33
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
34
|
+
return JSON.parse(raw);
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build default settings object.
|
|
42
|
+
* @returns {Object}
|
|
43
|
+
*/
|
|
44
|
+
function getDefaults() {
|
|
45
|
+
return {
|
|
46
|
+
port: F.DEFAULT_PORT,
|
|
47
|
+
secret: null,
|
|
48
|
+
models: {
|
|
49
|
+
main: { [F.MODEL_BASE_URL]: 'https://openrouter.ai/api/v1', [F.MODEL_API_KEY]: '', [F.MODEL_NAME]: '' },
|
|
50
|
+
compact: null,
|
|
51
|
+
title: null,
|
|
52
|
+
},
|
|
53
|
+
permissions: {
|
|
54
|
+
[F.PERMISSIONS_ALLOW]: [],
|
|
55
|
+
[F.PERMISSIONS_DENY]: [],
|
|
56
|
+
[F.PERMISSIONS_ASK]: [],
|
|
57
|
+
},
|
|
58
|
+
hooks: {
|
|
59
|
+
[F.HOOKS_PRE_TOOL_USE]: null,
|
|
60
|
+
[F.HOOKS_POST_TOOL_USE]: null,
|
|
61
|
+
},
|
|
62
|
+
compaction: {
|
|
63
|
+
[F.COMPACTION_THRESHOLD]: F.DEFAULT_COMPACTION_THRESHOLD,
|
|
64
|
+
[F.COMPACTION_OBSERVATION_MASKING_TURNS]: F.DEFAULT_OBSERVATION_MASKING_TURNS,
|
|
65
|
+
},
|
|
66
|
+
memory: {
|
|
67
|
+
enabled: true,
|
|
68
|
+
maxLines: F.DEFAULT_MEMORY_MAX_LINES,
|
|
69
|
+
},
|
|
70
|
+
storage: {
|
|
71
|
+
retention: {
|
|
72
|
+
sessions: { maxAgeDays: 90, maxCount: 10000 },
|
|
73
|
+
tasks: { maxAgeDays: 30, maxCount: 5000 },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
maxIterations: F.DEFAULT_MAX_ITERATIONS,
|
|
77
|
+
maxDurationSeconds: F.DEFAULT_MAX_DURATION_SECONDS,
|
|
78
|
+
maxConcurrentTasks: F.DEFAULT_MAX_CONCURRENT_TASKS,
|
|
79
|
+
maxSubAgentDepth: F.DEFAULT_MAX_SUBAGENT_DEPTH,
|
|
80
|
+
mcpServers: {},
|
|
81
|
+
ably: { enabled: false, key: null },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Load and merge settings from all layers:
|
|
87
|
+
* defaults → ~/.veil/settings.json → .veil/settings.json → .veil/settings.local.json → cliOverrides
|
|
88
|
+
*
|
|
89
|
+
* @param {string} cwd - Project root directory
|
|
90
|
+
* @param {Object} [cliOverrides={}] - CLI flag overrides
|
|
91
|
+
* @returns {Object} Merged settings
|
|
92
|
+
*/
|
|
93
|
+
function loadSettings({ cwd, cliOverrides = {} }) {
|
|
94
|
+
const result = getDefaults();
|
|
95
|
+
|
|
96
|
+
const globalSettings = readJsonFile(paths.getGlobalSettingsPath());
|
|
97
|
+
if (globalSettings) deepMerge(result, globalSettings);
|
|
98
|
+
|
|
99
|
+
const globalAuth = readJsonFile(paths.getGlobalAuthPath());
|
|
100
|
+
if (globalAuth) {
|
|
101
|
+
if (globalAuth.models) deepMerge(result.models, globalAuth.models);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const projectSettings = readJsonFile(paths.getProjectSettingsPath(cwd));
|
|
105
|
+
if (projectSettings) deepMerge(result, projectSettings);
|
|
106
|
+
|
|
107
|
+
const projectAuth = readJsonFile(paths.getProjectAuthPath(cwd));
|
|
108
|
+
if (projectAuth) {
|
|
109
|
+
if (projectAuth.models) deepMerge(result.models, projectAuth.models);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const localSettings = readJsonFile(paths.getProjectSettingsLocalPath(cwd));
|
|
113
|
+
if (localSettings) deepMerge(result, localSettings);
|
|
114
|
+
|
|
115
|
+
if (cliOverrides && Object.keys(cliOverrides).length > 0) {
|
|
116
|
+
deepMerge(result, cliOverrides);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the model config for a given role. Falls back to 'main' if role not configured.
|
|
124
|
+
* @param {Object} settings
|
|
125
|
+
* @param {'main'|'compact'|'title'} role
|
|
126
|
+
* @returns {{ base_url: string, api_key: string, model: string }}
|
|
127
|
+
*/
|
|
128
|
+
function getModelConfig(settings, role = F.MODEL_MAIN) {
|
|
129
|
+
const roleConfig = settings.models && settings.models[role];
|
|
130
|
+
if (roleConfig && roleConfig[F.MODEL_NAME]) return roleConfig;
|
|
131
|
+
return settings.models[F.MODEL_MAIN];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const VALID_LEVELS = ['merged', 'project', 'global', 'local'];
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Resolve the settings file path for a given level.
|
|
138
|
+
* @param {'project'|'global'|'local'} level
|
|
139
|
+
* @param {string} cwd
|
|
140
|
+
* @returns {string}
|
|
141
|
+
*/
|
|
142
|
+
function settingsPathForLevel(level, cwd) {
|
|
143
|
+
if (level === 'global') return paths.getGlobalSettingsPath();
|
|
144
|
+
if (level === 'local') return paths.getProjectSettingsLocalPath(cwd);
|
|
145
|
+
return paths.getProjectSettingsPath(cwd);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Read raw settings for a single level without merging.
|
|
150
|
+
* - 'merged' → full merged loadSettings result
|
|
151
|
+
* - 'project' → .veil/settings.json (raw, or {} if absent)
|
|
152
|
+
* - 'global' → ~/.veil/settings.json (raw, or {} if absent)
|
|
153
|
+
* - 'local' → .veil/settings.local.json (raw, or {} if absent)
|
|
154
|
+
*
|
|
155
|
+
* @param {{ cwd: string, level?: string }} opts
|
|
156
|
+
* @returns {{ settings: Object, exists: boolean, path: string|null }}
|
|
157
|
+
*/
|
|
158
|
+
function readSettingsForLevel({ cwd, level = 'merged' }) {
|
|
159
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
160
|
+
throw Object.assign(new Error(`Invalid level "${level}". Must be one of: ${VALID_LEVELS.join(', ')}`), { code: 'INVALID_LEVEL' });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (level === 'merged') {
|
|
164
|
+
return { settings: loadSettings({ cwd }), exists: true, path: null };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const filePath = settingsPathForLevel(level, cwd);
|
|
168
|
+
const raw = readJsonFile(filePath);
|
|
169
|
+
return { settings: raw || {}, exists: raw !== null, path: filePath };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = { loadSettings, getModelConfig, readSettingsForLevel, settingsPathForLevel, VALID_LEVELS };
|