@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.
Files changed (199) hide show
  1. package/.veil/agents/analyst/AGENT.md +21 -0
  2. package/.veil/agents/analyst/agent.json +23 -0
  3. package/.veil/agents/assistant/AGENT.md +15 -0
  4. package/.veil/agents/assistant/agent.json +19 -0
  5. package/.veil/agents/coder/AGENT.md +18 -0
  6. package/.veil/agents/coder/agent.json +19 -0
  7. package/.veil/agents/hello/AGENT.md +5 -0
  8. package/.veil/agents/hello/agent.json +13 -0
  9. package/.veil/agents/writer/AGENT.md +12 -0
  10. package/.veil/agents/writer/agent.json +17 -0
  11. package/.veil/memory/MEMORY.md +343 -0
  12. package/.veil/memory/agents/analyst/MEMORY.md +55 -0
  13. package/.veil/memory/agents/hello/MEMORY.md +12 -0
  14. package/.veil/runtime.pid +1 -0
  15. package/.veil/settings.json +10 -0
  16. package/.veil-studio/studio.db +0 -0
  17. package/.veil-studio/studio.db-shm +0 -0
  18. package/.veil-studio/studio.db-wal +0 -0
  19. package/PLAN/01-vision.md +26 -0
  20. package/PLAN/02-tech-stack.md +94 -0
  21. package/PLAN/03-agents.md +232 -0
  22. package/PLAN/04-runtime.md +171 -0
  23. package/PLAN/05-tools.md +211 -0
  24. package/PLAN/06-communication.md +243 -0
  25. package/PLAN/07-storage.md +218 -0
  26. package/PLAN/08-api-cli.md +153 -0
  27. package/PLAN/09-permissions.md +108 -0
  28. package/PLAN/10-ably.md +105 -0
  29. package/PLAN/11-file-formats.md +442 -0
  30. package/PLAN/12-folder-structure.md +205 -0
  31. package/PLAN/13-operations.md +212 -0
  32. package/PLAN/README.md +23 -0
  33. package/README.md +128 -0
  34. package/REPORT.md +174 -0
  35. package/TODO.md +45 -0
  36. package/ai-tests/FRONTEND_PROMPT.md +220 -0
  37. package/ai-tests/Research & Planning.md +814 -0
  38. package/ai-tests/prompt-001-basic-api.md +230 -0
  39. package/ai-tests/prompt-002-basic-flows.md +230 -0
  40. package/ai-tests/prompt-003-agent-behaviors.md +220 -0
  41. package/api/middleware.js +60 -0
  42. package/api/routes/agents.js +193 -0
  43. package/api/routes/chat.js +93 -0
  44. package/api/routes/completions.js +122 -0
  45. package/api/routes/daemons.js +80 -0
  46. package/api/routes/memory.js +169 -0
  47. package/api/routes/models.js +40 -0
  48. package/api/routes/remote-methods.js +74 -0
  49. package/api/routes/sessions.js +208 -0
  50. package/api/routes/settings.js +108 -0
  51. package/api/routes/system.js +50 -0
  52. package/api/routes/tasks.js +270 -0
  53. package/api/server.js +120 -0
  54. package/cli/formatter.js +70 -0
  55. package/cli/index.js +443 -0
  56. package/cli/parser.js +113 -0
  57. package/config/config.json +10 -0
  58. package/config/models.json +6826 -0
  59. package/core/agent.js +329 -0
  60. package/core/cancel.js +38 -0
  61. package/core/compaction.js +176 -0
  62. package/core/events.js +13 -0
  63. package/core/loop.js +564 -0
  64. package/core/memory.js +51 -0
  65. package/core/prompt.js +185 -0
  66. package/core/queue.js +96 -0
  67. package/core/registry.js +291 -0
  68. package/core/remote-methods.js +124 -0
  69. package/core/router.js +386 -0
  70. package/core/running-sessions.js +18 -0
  71. package/docs/api/01-system.md +84 -0
  72. package/docs/api/02-agents.md +374 -0
  73. package/docs/api/03-chat.md +269 -0
  74. package/docs/api/04-tasks.md +470 -0
  75. package/docs/api/05-sessions.md +444 -0
  76. package/docs/api/06-daemons.md +142 -0
  77. package/docs/api/07-memory.md +186 -0
  78. package/docs/api/08-settings.md +133 -0
  79. package/docs/api/09-models.md +119 -0
  80. package/docs/api/09-websocket.md +350 -0
  81. package/docs/api/10-completions.md +134 -0
  82. package/docs/api/README.md +116 -0
  83. package/docs/guide/01-quickstart.md +220 -0
  84. package/docs/guide/02-folder-structure.md +185 -0
  85. package/docs/guide/03-configuration.md +252 -0
  86. package/docs/guide/04-agents.md +267 -0
  87. package/docs/guide/05-cli.md +290 -0
  88. package/docs/guide/06-tools.md +643 -0
  89. package/docs/guide/07-permissions.md +236 -0
  90. package/docs/guide/08-memory.md +139 -0
  91. package/docs/guide/09-multi-agent.md +271 -0
  92. package/docs/guide/10-daemons.md +226 -0
  93. package/docs/guide/README.md +53 -0
  94. package/docs/index.html +623 -0
  95. package/examples/README.md +151 -0
  96. package/examples/agents/assistant/AGENT.md +31 -0
  97. package/examples/agents/assistant/SOUL.md +9 -0
  98. package/examples/agents/assistant/agent.json +74 -0
  99. package/examples/agents/hello/AGENT.md +15 -0
  100. package/examples/agents/hello/agent.json +14 -0
  101. package/examples/agents/monitor/AGENT.md +51 -0
  102. package/examples/agents/monitor/agent.json +33 -0
  103. package/examples/agents/monitor/heartbeats/monitor.md +24 -0
  104. package/examples/agents/orchestrator/AGENT.md +70 -0
  105. package/examples/agents/orchestrator/agent.json +30 -0
  106. package/examples/agents/researcher/AGENT.md +52 -0
  107. package/examples/agents/researcher/agent.json +49 -0
  108. package/examples/agents/researcher/skills/web-research.md +28 -0
  109. package/examples/skills/code-review.md +72 -0
  110. package/examples/skills/summarise.md +59 -0
  111. package/examples/skills/web-research.md +42 -0
  112. package/examples/tools/word-count/index.js +27 -0
  113. package/examples/tools/word-count/tool.json +18 -0
  114. package/infrastructure/database.js +563 -0
  115. package/infrastructure/scheduler.js +122 -0
  116. package/llm/client.js +206 -0
  117. package/migrations/001-initial.sql +121 -0
  118. package/migrations/002-debuggability.sql +13 -0
  119. package/migrations/003-drop-orphaned-columns.sql +72 -0
  120. package/migrations/004-session-message-token-fields.sql +78 -0
  121. package/migrations/005-session-thinking.sql +5 -0
  122. package/package.json +30 -0
  123. package/schemas/agent.json +143 -0
  124. package/schemas/settings.json +111 -0
  125. package/scripts/fetch-models.js +93 -0
  126. package/session-debug-scenario.md +248 -0
  127. package/settings/fields.js +52 -0
  128. package/system-prompts/base-core.md +7 -0
  129. package/system-prompts/environment.md +13 -0
  130. package/system-prompts/reminders/anti-drift.md +6 -0
  131. package/system-prompts/reminders/stall-recovery.md +10 -0
  132. package/system-prompts/safety-rules.md +25 -0
  133. package/system-prompts/task-heuristics.md +27 -0
  134. package/test/client.js +71 -0
  135. package/test/integration/01-health.test.js +25 -0
  136. package/test/integration/02-agents.test.js +80 -0
  137. package/test/integration/03-chat-hello.test.js +48 -0
  138. package/test/integration/04-chat-multiturn.test.js +61 -0
  139. package/test/integration/05-chat-writer.test.js +48 -0
  140. package/test/integration/06-task-basic.test.js +68 -0
  141. package/test/integration/07-task-tools.test.js +74 -0
  142. package/test/integration/08-task-code-analysis.test.js +69 -0
  143. package/test/integration/09-memory-analyst.test.js +63 -0
  144. package/test/integration/10-task-advanced.test.js +85 -0
  145. package/test/integration/11-sessions-advanced.test.js +84 -0
  146. package/test/integration/12-assistant-chat-tools.test.js +75 -0
  147. package/test/integration/13-edge-cases.test.js +99 -0
  148. package/test/integration/14-cancel.test.js +62 -0
  149. package/test/integration/15-debug.test.js +106 -0
  150. package/test/integration/16-memory-api.test.js +83 -0
  151. package/test/integration/17-settings-api.test.js +41 -0
  152. package/test/integration/18-tool-search-activation.test.js +119 -0
  153. package/test/results/.gitkeep +0 -0
  154. package/test/runner.js +206 -0
  155. package/test/smoke.js +216 -0
  156. package/tools/agent_message.js +85 -0
  157. package/tools/agent_send.js +80 -0
  158. package/tools/agent_spawn.js +44 -0
  159. package/tools/bash.js +49 -0
  160. package/tools/edit_file.js +41 -0
  161. package/tools/glob.js +64 -0
  162. package/tools/grep.js +82 -0
  163. package/tools/list_dir.js +63 -0
  164. package/tools/log_write.js +31 -0
  165. package/tools/memory_read.js +38 -0
  166. package/tools/memory_search.js +65 -0
  167. package/tools/memory_write.js +42 -0
  168. package/tools/read_file.js +48 -0
  169. package/tools/sleep.js +22 -0
  170. package/tools/task_create.js +41 -0
  171. package/tools/task_respond.js +37 -0
  172. package/tools/task_spawn.js +64 -0
  173. package/tools/task_status.js +39 -0
  174. package/tools/task_subscribe.js +37 -0
  175. package/tools/todo_read.js +26 -0
  176. package/tools/todo_write.js +38 -0
  177. package/tools/tool_activate.js +24 -0
  178. package/tools/tool_search.js +24 -0
  179. package/tools/web_fetch.js +50 -0
  180. package/tools/web_search.js +52 -0
  181. package/tools/write_file.js +28 -0
  182. package/ui/api.js +190 -0
  183. package/ui/app.js +281 -0
  184. package/ui/index.html +382 -0
  185. package/ui/views/agents.js +377 -0
  186. package/ui/views/chat.js +610 -0
  187. package/ui/views/connection.js +96 -0
  188. package/ui/views/daemons.js +129 -0
  189. package/ui/views/feed.js +194 -0
  190. package/ui/views/memory.js +263 -0
  191. package/ui/views/models.js +146 -0
  192. package/ui/views/sessions.js +314 -0
  193. package/ui/views/settings.js +142 -0
  194. package/ui/views/tasks.js +415 -0
  195. package/utils/context.js +49 -0
  196. package/utils/id.js +16 -0
  197. package/utils/models.js +88 -0
  198. package/utils/paths.js +213 -0
  199. package/utils/settings.js +172 -0
package/cli/index.js ADDED
@@ -0,0 +1,443 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // ── Node version check (must be first) ───────────────────────────────────────
5
+ const [major] = process.version.slice(1).split('.').map(Number);
6
+ if (major < 18) {
7
+ console.error(`VeilCLI requires Node.js 18+. Current: ${process.version}`);
8
+ process.exit(1);
9
+ }
10
+
11
+ // ── Set CWD and version in context singleton ──────────────────────────────────
12
+ const context = require('../utils/context');
13
+ const pkg = require('../package.json');
14
+ context.setVersion(pkg.version);
15
+
16
+ const { parseCliArgs, USAGE } = require('./parser');
17
+ const fmt = require('./formatter');
18
+ const path = require('path');
19
+ const fs = require('fs');
20
+
21
+ const args = parseCliArgs(process.argv.slice(2));
22
+
23
+ // ── Command dispatch ──────────────────────────────────────────────────────────
24
+ (async () => {
25
+ switch (args.command) {
26
+ case 'help':
27
+ console.log(USAGE);
28
+ process.exit(0);
29
+ break;
30
+
31
+ case 'version':
32
+ console.log(`VeilCLI v${pkg.version}`);
33
+ process.exit(0);
34
+ break;
35
+
36
+ case 'check-for-update':
37
+ await cmdCheckForUpdate();
38
+ break;
39
+
40
+ case 'start':
41
+ await cmdStart(args.args);
42
+ break;
43
+
44
+ case 'run':
45
+ await cmdRun(args.args);
46
+ break;
47
+
48
+ case 'stop':
49
+ await cmdStop(args.args);
50
+ break;
51
+
52
+ case 'status':
53
+ await cmdStatus(args.args);
54
+ break;
55
+
56
+ case 'agents':
57
+ await cmdAgents(args.subcommand, args.args);
58
+ break;
59
+
60
+ case 'login':
61
+ await cmdLogin(args.args);
62
+ break;
63
+
64
+ case 'import':
65
+ await cmdImport(args.args);
66
+ break;
67
+
68
+ case 'dashboard':
69
+ await cmdDashboard(args.args);
70
+ break;
71
+
72
+ default:
73
+ fmt.error(`Unknown command: ${args.command}`);
74
+ console.log('\n' + USAGE);
75
+ process.exit(1);
76
+ }
77
+ })().catch(err => {
78
+ fmt.error(err.message);
79
+ process.exit(1);
80
+ });
81
+
82
+ // ── Command implementations ───────────────────────────────────────────────────
83
+
84
+ async function cmdCheckForUpdate() {
85
+ const https = require('https');
86
+ const current = pkg.version;
87
+
88
+ const latest = await new Promise((resolve, reject) => {
89
+ const req = https.get(
90
+ `https://registry.npmjs.org/${pkg.name}/latest`,
91
+ { headers: { Accept: 'application/json' } },
92
+ (res) => {
93
+ let body = '';
94
+ res.on('data', (chunk) => { body += chunk; });
95
+ res.on('end', () => {
96
+ try { resolve(JSON.parse(body).version); }
97
+ catch { reject(new Error('Failed to parse npm registry response')); }
98
+ });
99
+ }
100
+ );
101
+ req.on('error', reject);
102
+ req.setTimeout(8000, () => { req.destroy(new Error('Request timed out')); });
103
+ });
104
+
105
+ if (latest === current) {
106
+ console.log(`No updates available (current: v${current})`);
107
+ } else {
108
+ console.log(`New update exists: v${latest} (current: v${current})`);
109
+ }
110
+ process.exit(0);
111
+ }
112
+
113
+ async function cmdStart({ port, folder, secret, ably }) {
114
+ const cwd = folder ? path.resolve(folder) : process.cwd();
115
+ context.setCwd(cwd);
116
+
117
+ const { loadSettings } = require('../utils/settings');
118
+ const cliOverrides = {};
119
+ if (port) cliOverrides.port = parseInt(port, 10);
120
+ if (secret) cliOverrides.secret = secret;
121
+ if (ably) cliOverrides.ably = { enabled: true };
122
+
123
+ const settings = loadSettings({ cwd, cliOverrides });
124
+
125
+ // Startup recovery: mark stale processing tasks as failed
126
+ const db = require('../infrastructure/database');
127
+ db.recoverStaleTasks(cwd);
128
+
129
+ // Start scheduler
130
+ const { createScheduler } = require('../infrastructure/scheduler');
131
+ const scheduler = createScheduler();
132
+
133
+ // Auto-start daemons
134
+ const { listAgents, loadAgent } = require('../core/agent');
135
+ const agents = listAgents({ cwd });
136
+ for (const agentSummary of agents) {
137
+ if (agentSummary.modes.includes('daemon')) {
138
+ try {
139
+ const agent = loadAgent({ cwd, name: agentSummary.name });
140
+ if (agent.modes?.daemon?.enabled && agent.modes?.daemon?.cron) {
141
+ scheduler.startDaemon({ agentName: agentSummary.name, agent, cwd, settings });
142
+ fmt.info(`Auto-started daemon: ${agentSummary.name}`);
143
+ }
144
+ } catch (err) {
145
+ fmt.warn(`Failed to auto-start daemon ${agentSummary.name}: ${err.message}`);
146
+ }
147
+ }
148
+ }
149
+
150
+ // Start REST server
151
+ const { startServer } = require('../api/server');
152
+ const listenPort = settings.port || 5050;
153
+
154
+ const server = await startServer({ settings, scheduler, port: listenPort });
155
+
156
+ // Write PID file
157
+ const paths = require('../utils/paths');
158
+ const pidFile = paths.getPidFilePath(cwd);
159
+ fs.mkdirSync(path.dirname(pidFile), { recursive: true });
160
+ fs.writeFileSync(pidFile, String(process.pid), 'utf8');
161
+
162
+ fmt.success(`VeilCLI v${pkg.version} started on port ${listenPort}`);
163
+ fmt.info(`Project: ${cwd}`);
164
+ fmt.info(`Agents: ${agents.length} | PID: ${process.pid}`);
165
+
166
+ // Graceful shutdown
167
+ const shutdown = () => {
168
+ fmt.info('Shutting down...');
169
+ scheduler.stopAll();
170
+ server.close(() => {
171
+ try { fs.unlinkSync(pidFile); } catch {}
172
+ db.closeDb();
173
+ fmt.success('Shutdown complete');
174
+ process.exit(0);
175
+ });
176
+ setTimeout(() => process.exit(1), 5000);
177
+ };
178
+ process.on('SIGINT', shutdown);
179
+ process.on('SIGTERM', shutdown);
180
+ }
181
+
182
+ async function cmdRun({ agent, input, timeout, session, output, 'output-format': outputFormat }) {
183
+ if (!agent) { fmt.error('--agent is required'); process.exit(1); }
184
+ if (!input) { fmt.error('--input is required'); process.exit(1); }
185
+
186
+ const cwd = process.cwd();
187
+ context.setCwd(cwd);
188
+
189
+ const { loadSettings } = require('../utils/settings');
190
+ const settings = loadSettings({ cwd });
191
+ if (timeout) settings.maxDurationSeconds = parseInt(timeout, 10);
192
+
193
+ const db = require('../infrastructure/database');
194
+ const { runTask } = require('../core/router');
195
+
196
+ const taskId = db.createTask({
197
+ agentName: agent,
198
+ input,
199
+ priority: 'normal',
200
+ instanceFolder: cwd,
201
+ sessionId: session || null,
202
+ });
203
+
204
+ fmt.info(`Running task ${taskId} with agent "${agent}"${session ? ` (continuing session ${session})` : ''}...`);
205
+
206
+ try {
207
+ const result = await runTask({ taskId, cwd, settings });
208
+ const data = {
209
+ taskId,
210
+ sessionId: result.sessionId,
211
+ agent,
212
+ status: result.status,
213
+ output: result.output,
214
+ timestamp: new Date().toISOString(),
215
+ };
216
+
217
+ if (result.status === 'finished') {
218
+ writeOutput(data, output, outputFormat || 'json');
219
+ process.exit(0);
220
+ } else {
221
+ data.error = result.error || 'Task did not finish';
222
+ writeOutput(data, output, outputFormat || 'json');
223
+ process.exit(1);
224
+ }
225
+ } catch (err) {
226
+ const data = { error: err.message, timestamp: new Date().toISOString() };
227
+ writeOutput(data, output, outputFormat || 'json');
228
+ process.exit(1);
229
+ }
230
+ }
231
+
232
+ function writeOutput(data, outputFile, format) {
233
+ let content;
234
+ if (format === 'md') {
235
+ const { output, ...meta } = data;
236
+ const yamlLines = Object.entries(meta)
237
+ .filter(([, v]) => v !== undefined && v !== null)
238
+ .map(([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`);
239
+ content = `---\n${yamlLines.join('\n')}\n---\n\n${output || ''}`;
240
+ } else {
241
+ content = JSON.stringify(data, null, 2);
242
+ }
243
+
244
+ if (outputFile) {
245
+ fs.writeFileSync(outputFile, content, 'utf8');
246
+ fmt.success(`Output written to ${outputFile}`);
247
+ } else {
248
+ console.log(content);
249
+ }
250
+ }
251
+
252
+ async function cmdStop() {
253
+ const cwd = process.cwd();
254
+ const paths = require('../utils/paths');
255
+ const pidFile = paths.getPidFilePath(cwd);
256
+
257
+ if (!fs.existsSync(pidFile)) {
258
+ fmt.error('No running server found (no PID file at .veil/runtime.pid)');
259
+ process.exit(1);
260
+ }
261
+
262
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
263
+ try {
264
+ process.kill(pid, 'SIGTERM');
265
+ fmt.success(`Sent SIGTERM to process ${pid}`);
266
+ } catch (err) {
267
+ fmt.error(`Failed to stop server: ${err.message}`);
268
+ process.exit(1);
269
+ }
270
+ }
271
+
272
+ async function cmdStatus() {
273
+ const cwd = process.cwd();
274
+ const paths = require('../utils/paths');
275
+ const pidFile = paths.getPidFilePath(cwd);
276
+
277
+ if (!fs.existsSync(pidFile)) {
278
+ fmt.warn('No running server (no PID file found)');
279
+ return;
280
+ }
281
+
282
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
283
+ try {
284
+ process.kill(pid, 0); // Existence check
285
+ fmt.success(`Server is running (PID: ${pid})`);
286
+ } catch {
287
+ fmt.warn(`PID file exists but process ${pid} is not running (stale PID file)`);
288
+ }
289
+ }
290
+
291
+ async function cmdAgents(subcommand, { name }) {
292
+ const cwd = process.cwd();
293
+ context.setCwd(cwd);
294
+
295
+ const { listAgents, loadAgent } = require('../core/agent');
296
+
297
+ if (subcommand === 'list' || !subcommand) {
298
+ const agents = listAgents({ cwd });
299
+ fmt.header(`Agents (${agents.length})`);
300
+ if (agents.length === 0) {
301
+ fmt.info('No agents found. Add agents to .veil/agents/<name>/');
302
+ } else {
303
+ fmt.table(agents, ['name', 'description', 'model', 'source']);
304
+ }
305
+ return;
306
+ }
307
+
308
+ if (subcommand === 'inspect') {
309
+ if (!name) { fmt.error('Agent name required: veil agents inspect <name>'); process.exit(1); }
310
+ try {
311
+ const agent = loadAgent({ cwd, name });
312
+ fmt.header(`Agent: ${agent.name}`);
313
+ fmt.info(`Description: ${agent.description || '(none)'}`);
314
+ fmt.info(`Model: ${agent.model}`);
315
+ fmt.info(`Folder: ${agent.agentFolder}`);
316
+ fmt.info(`Modes: ${Object.keys(agent.modes || {}).filter(m => agent.modes[m]?.enabled).join(', ') || '(none enabled)'}`);
317
+ if (agent.agentMd) {
318
+ fmt.header('AGENT.md');
319
+ console.log(agent.agentMd.slice(0, 500) + (agent.agentMd.length > 500 ? '\n...(truncated)' : ''));
320
+ }
321
+ } catch (err) {
322
+ fmt.error(err.message);
323
+ process.exit(1);
324
+ }
325
+ return;
326
+ }
327
+
328
+ if (subcommand === 'create') {
329
+ fmt.info('Interactive agent creation is not yet implemented. Create .veil/agents/<name>/agent.json manually.');
330
+ fmt.info('See examples/agents/hello/ for a minimal valid example.');
331
+ return;
332
+ }
333
+
334
+ fmt.error(`Unknown agents subcommand: ${subcommand}`);
335
+ process.exit(1);
336
+ }
337
+
338
+ async function cmdLogin({ key, global: isGlobal }) {
339
+ if (!key) { fmt.error('--key is required'); process.exit(1); }
340
+
341
+ const cwd = process.cwd();
342
+ const paths = require('../utils/paths');
343
+ const targetDir = isGlobal ? paths.getGlobalConfigDir() : paths.getProjectConfigDir(cwd);
344
+ const authPath = path.join(targetDir, 'auth.json');
345
+
346
+ fs.mkdirSync(targetDir, { recursive: true });
347
+
348
+ let existing = {};
349
+ try { existing = JSON.parse(fs.readFileSync(authPath, 'utf8')); } catch {}
350
+
351
+ existing.models = existing.models || {};
352
+ existing.models.main = existing.models.main || {};
353
+ existing.models.main.api_key = key;
354
+
355
+ fs.writeFileSync(authPath, JSON.stringify(existing, null, 2), 'utf8');
356
+ fmt.success(`API key saved to ${authPath}`);
357
+ }
358
+
359
+ async function cmdImport({ type, source }) {
360
+ if (!type || !source) { fmt.error('--type and --source are required'); process.exit(1); }
361
+ fmt.info(`Import adapter for "${type}" is not yet fully implemented.`);
362
+ fmt.info(`Source: ${source}`);
363
+ }
364
+
365
+ async function cmdDashboard({ port, api, secret }) {
366
+ const listenPort = port ? parseInt(port, 10) : 8080;
367
+ if (isNaN(listenPort) || listenPort < 1 || listenPort > 65535) {
368
+ fmt.error(`Invalid port: ${port}`);
369
+ process.exit(1);
370
+ }
371
+
372
+ const http = require('http');
373
+ const uiDir = path.join(__dirname, '..', 'ui');
374
+
375
+ if (!fs.existsSync(uiDir)) {
376
+ fmt.error(`UI folder not found: ${uiDir}`);
377
+ fmt.info('Make sure the ui/ folder exists at the repo root.');
378
+ process.exit(1);
379
+ }
380
+
381
+ const MIME = {
382
+ '.html': 'text/html; charset=utf-8',
383
+ '.js': 'application/javascript; charset=utf-8',
384
+ '.css': 'text/css; charset=utf-8',
385
+ '.json': 'application/json',
386
+ '.png': 'image/png',
387
+ '.ico': 'image/x-icon',
388
+ '.svg': 'image/svg+xml',
389
+ '.woff2':'font/woff2',
390
+ };
391
+
392
+ const server = http.createServer((req, res) => {
393
+ let urlPath = req.url.split('?')[0];
394
+ if (urlPath === '/') urlPath = '/index.html';
395
+
396
+ const safePath = path.normalize(urlPath).replace(/^(\.\.[\/\\])+/, '');
397
+ let filePath = path.join(uiDir, safePath);
398
+ const ext = path.extname(filePath).toLowerCase();
399
+
400
+ if (!ext || !fs.existsSync(filePath)) {
401
+ filePath = path.join(uiDir, 'index.html');
402
+ }
403
+
404
+ try {
405
+ let content = fs.readFileSync(filePath);
406
+ const mime = MIME[path.extname(filePath).toLowerCase()] || 'text/plain';
407
+
408
+ // Inject API pre-connect config into index.html when --api is provided
409
+ if (api && path.extname(filePath).toLowerCase() === '.html') {
410
+ const inject = `<script>window.__VEIL_API__=${JSON.stringify(api)};window.__VEIL_SECRET__=${JSON.stringify(secret || '')};</script>`;
411
+ content = Buffer.from(content.toString().replace('</head>', inject + '</head>'));
412
+ }
413
+
414
+ res.writeHead(200, {
415
+ 'Content-Type': mime,
416
+ 'Cache-Control': 'no-cache',
417
+ });
418
+ res.end(content);
419
+ } catch {
420
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
421
+ res.end('Not found');
422
+ }
423
+ });
424
+
425
+ server.on('error', (err) => {
426
+ if (err.code === 'EADDRINUSE') {
427
+ fmt.error(`Port ${listenPort} is already in use. Use --port to specify a different port.`);
428
+ } else {
429
+ fmt.error(`Server error: ${err.message}`);
430
+ }
431
+ process.exit(1);
432
+ });
433
+
434
+ server.listen(listenPort, '127.0.0.1', () => {
435
+ fmt.success(`Dashboard available at http://localhost:${listenPort}`);
436
+ fmt.info(`Serving: ${uiDir}`);
437
+ if (api) fmt.info(`Pre-connecting to API: ${api}`);
438
+ fmt.info('Press Ctrl+C to stop.');
439
+ });
440
+
441
+ process.on('SIGINT', () => { server.close(); process.exit(0); });
442
+ process.on('SIGTERM', () => { server.close(); process.exit(0); });
443
+ }
package/cli/parser.js ADDED
@@ -0,0 +1,113 @@
1
+ 'use strict';
2
+
3
+ const { parseArgs } = require('util');
4
+
5
+ const USAGE = `
6
+ VeilCLI — Enterprise autonomous agent runtime
7
+
8
+ Usage:
9
+ veil start [--port <n>] [--folder <path>] [--secret <s>] [--ably]
10
+ veil run --agent <name> --input "<text>" [--session <id>] [--timeout <s>] [--output <file>] [--output-format json|md]
11
+ veil stop
12
+ veil status
13
+ veil agents list
14
+ veil agents inspect <name>
15
+ veil agents create
16
+ veil login --key <token> [--global]
17
+ veil import --type <type> --source <path>
18
+ veil dashboard [--port <n>] [--api <url>] [--secret <s>]
19
+
20
+ Commands:
21
+ start Start the REST API server
22
+ run Run a one-shot task and exit
23
+ stop Gracefully stop the running server
24
+ status Show running server info
25
+ agents Manage agents (list, inspect, create)
26
+ login Store API credentials
27
+ import Import agents from Claude Code / other formats
28
+ dashboard Launch the browser-based GUI (default port: 8080)
29
+ Use --api to pre-connect to a specific Veil API URL
30
+
31
+ Options:
32
+ --port Port to listen on (default: 5050 for start, 8080 for dashboard)
33
+ --api Veil API base URL to pre-connect (e.g. http://localhost:5051)
34
+ --folder Project folder (default: current directory)
35
+ --secret API secret for auth header
36
+ --ably Enable Ably remote access
37
+ --agent Agent name (for run command)
38
+ --input Task input text (for run command)
39
+ --session Continue from existing session ID (for run command)
40
+ --timeout Timeout in seconds (for run command)
41
+ --output Write output to file instead of stdout (for run command)
42
+ --output-format Output format: json (default) or md (for run command)
43
+ --key API key (for login command)
44
+ --global Apply globally to ~/.veil/ (for login command)
45
+ --type Import type: claude-code|raw (for import command)
46
+ --source Source path (for import command)
47
+ --help, -h Show this help
48
+ --version Show version
49
+ --check-for-update Check if a newer version is available on npm
50
+ `.trim();
51
+
52
+ /**
53
+ * Parse CLI arguments.
54
+ * @param {string[]} argv - process.argv.slice(2)
55
+ * @returns {{ command: string, subcommand?: string, args: Object }}
56
+ */
57
+ function parseCliArgs(argv) {
58
+ if (!argv || argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
59
+ return { command: 'help', args: {} };
60
+ }
61
+
62
+ if (argv[0] === '--version') {
63
+ return { command: 'version', args: {} };
64
+ }
65
+
66
+ if (argv[0] === '--check-for-update') {
67
+ return { command: 'check-for-update', args: {} };
68
+ }
69
+
70
+ const command = argv[0];
71
+ const rest = argv.slice(1);
72
+
73
+ // Handle subcommands (e.g. `agents list`, `agents inspect <name>`)
74
+ if (command === 'agents') {
75
+ const subcommand = rest[0] || 'list';
76
+ const argName = rest[1]; // e.g. agent name for inspect
77
+ return { command: 'agents', subcommand, args: { name: argName } };
78
+ }
79
+
80
+ // Parse flags for remaining commands
81
+ let parsed;
82
+ try {
83
+ parsed = parseArgs({
84
+ args: rest,
85
+ options: {
86
+ port: { type: 'string' },
87
+ folder: { type: 'string' },
88
+ secret: { type: 'string' },
89
+ api: { type: 'string' },
90
+ ably: { type: 'boolean', default: false },
91
+ agent: { type: 'string' },
92
+ input: { type: 'string' },
93
+ timeout: { type: 'string' },
94
+ session: { type: 'string' },
95
+ output: { type: 'string' },
96
+ 'output-format': { type: 'string' },
97
+ key: { type: 'string' },
98
+ global: { type: 'boolean', default: false },
99
+ type: { type: 'string' },
100
+ source: { type: 'string' },
101
+ help: { type: 'boolean', short: 'h', default: false },
102
+ },
103
+ allowPositionals: true,
104
+ strict: false,
105
+ });
106
+ } catch {
107
+ return { command, args: {} };
108
+ }
109
+
110
+ return { command, args: parsed.values };
111
+ }
112
+
113
+ module.exports = { parseCliArgs, USAGE };
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "VeilCLI",
3
+ "version": "1.0.0",
4
+ "description": "Enterprise autonomous agent runtime",
5
+ "defaultPort": 5050,
6
+ "configDirName": ".veil",
7
+ "dbFileName": "data.db",
8
+ "pidFileName": "runtime.pid",
9
+ "globalConfigDir": "~/.veil"
10
+ }