neuro-cli 4.1.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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. package/package.json +83 -0
@@ -0,0 +1,127 @@
1
+ export interface SandboxConfig {
2
+ /** Whether sandbox mode is enabled */
3
+ enabled: boolean;
4
+ /** Root directory - only files under this dir can be modified */
5
+ rootDir: string;
6
+ /** Allowed directories (in addition to rootDir) */
7
+ allowedDirs: string[];
8
+ /** Denied directories (even under rootDir) */
9
+ deniedDirs: string[];
10
+ /** Denied file patterns (glob) */
11
+ deniedPatterns: string[];
12
+ /** Whether to allow command execution */
13
+ allowCommands: boolean;
14
+ /** Allowed commands (whitelist) */
15
+ allowedCommands: string[];
16
+ /** Denied commands (blacklist) */
17
+ deniedCommands: string[];
18
+ /** Whether to create backups before modifications */
19
+ backupOnModify: boolean;
20
+ /** Backup directory */
21
+ backupDir: string;
22
+ /** Maximum file size that can be written (bytes) */
23
+ maxFileSize: number;
24
+ /** Whether to allow network access */
25
+ allowNetwork: boolean;
26
+ /** Whether to allow environment variable access */
27
+ allowEnvAccess: boolean;
28
+ /** Read-only mode (no modifications at all) */
29
+ readOnly: boolean;
30
+ }
31
+ export interface SandboxViolation {
32
+ type: 'file_write' | 'file_delete' | 'file_read' | 'command' | 'network' | 'env';
33
+ path?: string;
34
+ command?: string;
35
+ reason: string;
36
+ timestamp: number;
37
+ }
38
+ export declare const DEFAULT_SANDBOX_CONFIG: SandboxConfig;
39
+ export declare class Sandbox {
40
+ private config;
41
+ private violations;
42
+ private backups;
43
+ private originalContents;
44
+ constructor(config?: Partial<SandboxConfig>);
45
+ /**
46
+ * Check if a file path is allowed for reading
47
+ */
48
+ canRead(filePath: string): boolean;
49
+ /**
50
+ * Check if a file path is allowed for writing
51
+ */
52
+ canWrite(filePath: string, contentSize?: number): boolean;
53
+ /**
54
+ * Check if a file can be deleted
55
+ */
56
+ canDelete(filePath: string): boolean;
57
+ /**
58
+ * Check if a command is allowed
59
+ */
60
+ canRunCommand(command: string): boolean;
61
+ /**
62
+ * Check if network access is allowed
63
+ */
64
+ canAccessNetwork(): boolean;
65
+ /**
66
+ * Check if environment variable access is allowed
67
+ */
68
+ canAccessEnv(): boolean;
69
+ /**
70
+ * Create a backup of a file before modification
71
+ */
72
+ backupFile(filePath: string): boolean;
73
+ /**
74
+ * Undo a file modification by restoring from backup
75
+ */
76
+ undoFile(filePath: string): boolean;
77
+ /**
78
+ * Undo all modifications made in this session
79
+ */
80
+ undoAll(): number;
81
+ /**
82
+ * Get all violations recorded
83
+ */
84
+ getViolations(): SandboxViolation[];
85
+ /**
86
+ * Get the number of violations
87
+ */
88
+ getViolationCount(): number;
89
+ /**
90
+ * Clear all violations
91
+ */
92
+ clearViolations(): void;
93
+ /**
94
+ * Enable sandbox mode
95
+ */
96
+ enable(): void;
97
+ /**
98
+ * Disable sandbox mode
99
+ */
100
+ disable(): void;
101
+ /**
102
+ * Check if sandbox is enabled
103
+ */
104
+ isEnabled(): boolean;
105
+ /**
106
+ * Toggle sandbox mode
107
+ */
108
+ toggle(): boolean;
109
+ /**
110
+ * Get current sandbox configuration
111
+ */
112
+ getConfig(): SandboxConfig;
113
+ /**
114
+ * Update sandbox configuration
115
+ */
116
+ updateConfig(updates: Partial<SandboxConfig>): void;
117
+ /**
118
+ * Print sandbox status
119
+ */
120
+ printStatus(): void;
121
+ private resolvePath;
122
+ private isUnderRootDir;
123
+ private isInDeniedDir;
124
+ private matchesDeniedPattern;
125
+ private recordViolation;
126
+ }
127
+ //# sourceMappingURL=sandbox.d.ts.map
@@ -0,0 +1,369 @@
1
+ // ============================================================
2
+ // NeuroCLI - Sandbox Mode
3
+ // File isolation and safe execution environment
4
+ // Prevents modifications outside the project directory
5
+ // ============================================================
6
+ import { join, resolve, relative, normalize } from 'path';
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
8
+ import chalk from 'chalk';
9
+ export const DEFAULT_SANDBOX_CONFIG = {
10
+ enabled: false,
11
+ rootDir: process.cwd(),
12
+ allowedDirs: [],
13
+ deniedDirs: [
14
+ 'node_modules',
15
+ '.git',
16
+ '__pycache__',
17
+ '.env',
18
+ ],
19
+ deniedPatterns: [
20
+ '**/.env',
21
+ '**/.env.*',
22
+ '**/id_rsa*',
23
+ '**/id_ed25519*',
24
+ '**/*.pem',
25
+ '**/*.key',
26
+ '**/credentials.json',
27
+ '**/secrets.*',
28
+ ],
29
+ allowCommands: true,
30
+ allowedCommands: [],
31
+ deniedCommands: [
32
+ 'rm -rf /',
33
+ 'sudo rm',
34
+ 'mkfs',
35
+ 'dd if=',
36
+ ':(){ :|:& };:', // fork bomb
37
+ 'chmod 777 /',
38
+ 'curl | sh',
39
+ 'wget | sh',
40
+ ],
41
+ backupOnModify: true,
42
+ backupDir: '.neuro/backups',
43
+ maxFileSize: 10 * 1024 * 1024, // 10MB
44
+ allowNetwork: true,
45
+ allowEnvAccess: false,
46
+ readOnly: false,
47
+ };
48
+ export class Sandbox {
49
+ config;
50
+ violations = [];
51
+ backups = new Map(); // original path -> backup path
52
+ originalContents = new Map(); // for undo
53
+ constructor(config) {
54
+ this.config = { ...DEFAULT_SANDBOX_CONFIG, ...config };
55
+ if (!this.config.rootDir)
56
+ this.config.rootDir = process.cwd();
57
+ this.config.rootDir = resolve(this.config.rootDir);
58
+ }
59
+ /**
60
+ * Check if a file path is allowed for reading
61
+ */
62
+ canRead(filePath) {
63
+ if (!this.config.enabled)
64
+ return true;
65
+ const absPath = this.resolvePath(filePath);
66
+ // Check denied dirs
67
+ if (this.isInDeniedDir(absPath)) {
68
+ this.recordViolation('file_read', absPath, 'Access denied: path is in a denied directory');
69
+ return false;
70
+ }
71
+ // Check denied patterns
72
+ if (this.matchesDeniedPattern(absPath)) {
73
+ this.recordViolation('file_read', absPath, 'Access denied: path matches a denied pattern');
74
+ return false;
75
+ }
76
+ return true;
77
+ }
78
+ /**
79
+ * Check if a file path is allowed for writing
80
+ */
81
+ canWrite(filePath, contentSize) {
82
+ if (!this.config.enabled)
83
+ return true;
84
+ if (this.config.readOnly) {
85
+ this.recordViolation('file_write', this.resolvePath(filePath), 'Write denied: sandbox is in read-only mode');
86
+ return false;
87
+ }
88
+ const absPath = this.resolvePath(filePath);
89
+ // Must be under rootDir
90
+ if (!this.isUnderRootDir(absPath)) {
91
+ this.recordViolation('file_write', absPath, `Write denied: path is outside project directory (${this.config.rootDir})`);
92
+ return false;
93
+ }
94
+ // Check denied dirs
95
+ if (this.isInDeniedDir(absPath)) {
96
+ this.recordViolation('file_write', absPath, 'Write denied: path is in a denied directory');
97
+ return false;
98
+ }
99
+ // Check denied patterns
100
+ if (this.matchesDeniedPattern(absPath)) {
101
+ this.recordViolation('file_write', absPath, 'Write denied: path matches a denied pattern');
102
+ return false;
103
+ }
104
+ // Check file size
105
+ if (contentSize !== undefined && contentSize > this.config.maxFileSize) {
106
+ this.recordViolation('file_write', absPath, `Write denied: file size (${contentSize} bytes) exceeds maximum (${this.config.maxFileSize} bytes)`);
107
+ return false;
108
+ }
109
+ return true;
110
+ }
111
+ /**
112
+ * Check if a file can be deleted
113
+ */
114
+ canDelete(filePath) {
115
+ if (!this.config.enabled)
116
+ return true;
117
+ if (this.config.readOnly) {
118
+ this.recordViolation('file_delete', this.resolvePath(filePath), 'Delete denied: sandbox is in read-only mode');
119
+ return false;
120
+ }
121
+ const absPath = this.resolvePath(filePath);
122
+ if (!this.isUnderRootDir(absPath)) {
123
+ this.recordViolation('file_delete', absPath, 'Delete denied: path is outside project directory');
124
+ return false;
125
+ }
126
+ if (this.isInDeniedDir(absPath)) {
127
+ this.recordViolation('file_delete', absPath, 'Delete denied: path is in a denied directory');
128
+ return false;
129
+ }
130
+ return true;
131
+ }
132
+ /**
133
+ * Check if a command is allowed
134
+ */
135
+ canRunCommand(command) {
136
+ if (!this.config.enabled)
137
+ return true;
138
+ if (!this.config.allowCommands) {
139
+ this.recordViolation('command', undefined, `Command denied: command execution is disabled`);
140
+ return false;
141
+ }
142
+ // Check denied commands
143
+ const cmdBase = command.trim().split(/\s+/)[0];
144
+ for (const denied of this.config.deniedCommands) {
145
+ if (command.includes(denied) || cmdBase === denied.split(' ')[0]) {
146
+ this.recordViolation('command', undefined, `Command denied: matches denied pattern "${denied}"`);
147
+ return false;
148
+ }
149
+ }
150
+ // Check allowed commands whitelist (if set)
151
+ if (this.config.allowedCommands.length > 0) {
152
+ const isAllowed = this.config.allowedCommands.some(allowed => cmdBase === allowed || command.startsWith(allowed));
153
+ if (!isAllowed) {
154
+ this.recordViolation('command', undefined, `Command denied: "${cmdBase}" is not in the allowed commands list`);
155
+ return false;
156
+ }
157
+ }
158
+ return true;
159
+ }
160
+ /**
161
+ * Check if network access is allowed
162
+ */
163
+ canAccessNetwork() {
164
+ if (!this.config.enabled)
165
+ return true;
166
+ if (!this.config.allowNetwork) {
167
+ this.recordViolation('network', undefined, 'Network access denied');
168
+ return false;
169
+ }
170
+ return true;
171
+ }
172
+ /**
173
+ * Check if environment variable access is allowed
174
+ */
175
+ canAccessEnv() {
176
+ if (!this.config.enabled)
177
+ return true;
178
+ if (!this.config.allowEnvAccess) {
179
+ this.recordViolation('env', undefined, 'Environment variable access denied');
180
+ return false;
181
+ }
182
+ return true;
183
+ }
184
+ /**
185
+ * Create a backup of a file before modification
186
+ */
187
+ backupFile(filePath) {
188
+ if (!this.config.backupOnModify)
189
+ return true;
190
+ const absPath = this.resolvePath(filePath);
191
+ if (!existsSync(absPath))
192
+ return true;
193
+ try {
194
+ const backupDir = join(this.config.rootDir, this.config.backupDir);
195
+ if (!existsSync(backupDir))
196
+ mkdirSync(backupDir, { recursive: true });
197
+ const relPath = relative(this.config.rootDir, absPath);
198
+ const backupPath = join(backupDir, `${Date.now()}_${relPath.replace(/\//g, '_')}`);
199
+ copyFileSync(absPath, backupPath);
200
+ this.backups.set(absPath, backupPath);
201
+ // Store original content for undo
202
+ const content = readFileSync(absPath, 'utf-8');
203
+ this.originalContents.set(absPath, content);
204
+ return true;
205
+ }
206
+ catch {
207
+ return false;
208
+ }
209
+ }
210
+ /**
211
+ * Undo a file modification by restoring from backup
212
+ */
213
+ undoFile(filePath) {
214
+ const absPath = this.resolvePath(filePath);
215
+ const originalContent = this.originalContents.get(absPath);
216
+ if (originalContent === undefined)
217
+ return false;
218
+ try {
219
+ writeFileSync(absPath, originalContent, 'utf-8');
220
+ this.originalContents.delete(absPath);
221
+ return true;
222
+ }
223
+ catch {
224
+ return false;
225
+ }
226
+ }
227
+ /**
228
+ * Undo all modifications made in this session
229
+ */
230
+ undoAll() {
231
+ let undone = 0;
232
+ for (const [path] of this.originalContents) {
233
+ if (this.undoFile(path))
234
+ undone++;
235
+ }
236
+ return undone;
237
+ }
238
+ /**
239
+ * Get all violations recorded
240
+ */
241
+ getViolations() {
242
+ return [...this.violations];
243
+ }
244
+ /**
245
+ * Get the number of violations
246
+ */
247
+ getViolationCount() {
248
+ return this.violations.length;
249
+ }
250
+ /**
251
+ * Clear all violations
252
+ */
253
+ clearViolations() {
254
+ this.violations = [];
255
+ }
256
+ /**
257
+ * Enable sandbox mode
258
+ */
259
+ enable() {
260
+ this.config.enabled = true;
261
+ }
262
+ /**
263
+ * Disable sandbox mode
264
+ */
265
+ disable() {
266
+ this.config.enabled = false;
267
+ }
268
+ /**
269
+ * Check if sandbox is enabled
270
+ */
271
+ isEnabled() {
272
+ return this.config.enabled;
273
+ }
274
+ /**
275
+ * Toggle sandbox mode
276
+ */
277
+ toggle() {
278
+ this.config.enabled = !this.config.enabled;
279
+ return this.config.enabled;
280
+ }
281
+ /**
282
+ * Get current sandbox configuration
283
+ */
284
+ getConfig() {
285
+ return { ...this.config };
286
+ }
287
+ /**
288
+ * Update sandbox configuration
289
+ */
290
+ updateConfig(updates) {
291
+ Object.assign(this.config, updates);
292
+ }
293
+ /**
294
+ * Print sandbox status
295
+ */
296
+ printStatus() {
297
+ const status = this.config.enabled ? chalk.green('ENABLED') : chalk.gray('DISABLED');
298
+ console.log(chalk.bold(`\n Sandbox: ${status}`));
299
+ if (this.config.enabled) {
300
+ console.log(` Root: ${chalk.cyan(this.config.rootDir)}`);
301
+ console.log(` Read-only: ${this.config.readOnly ? chalk.red('YES') : chalk.green('NO')}`);
302
+ console.log(` Commands: ${this.config.allowCommands ? chalk.green('allowed') : chalk.red('denied')}`);
303
+ console.log(` Network: ${this.config.allowNetwork ? chalk.green('allowed') : chalk.red('denied')}`);
304
+ console.log(` Backups: ${this.config.backupOnModify ? chalk.green('enabled') : chalk.gray('disabled')}`);
305
+ console.log(` Denied dirs: ${this.config.deniedDirs.length > 0 ? chalk.yellow(this.config.deniedDirs.join(', ')) : chalk.gray('none')}`);
306
+ console.log(` Denied patterns: ${this.config.deniedPatterns.length > 0 ? chalk.yellow(this.config.deniedPatterns.length + ' patterns') : chalk.gray('none')}`);
307
+ if (this.violations.length > 0) {
308
+ console.log(` Violations: ${chalk.red(String(this.violations.length))}`);
309
+ }
310
+ }
311
+ console.log();
312
+ }
313
+ // --- Private Helpers ---
314
+ resolvePath(filePath) {
315
+ if (filePath.startsWith('/'))
316
+ return normalize(filePath);
317
+ if (filePath.startsWith('~/'))
318
+ return normalize(join(require('os').homedir(), filePath.slice(2)));
319
+ return normalize(resolve(this.config.rootDir, filePath));
320
+ }
321
+ isUnderRootDir(absPath) {
322
+ const relativePath = relative(this.config.rootDir, absPath);
323
+ return !relativePath.startsWith('..') && !relativePath.startsWith('/');
324
+ }
325
+ isInDeniedDir(absPath) {
326
+ const relativePath = relative(this.config.rootDir, absPath);
327
+ // Check built-in denied dirs
328
+ for (const deniedDir of this.config.deniedDirs) {
329
+ if (relativePath.startsWith(deniedDir + '/') || relativePath === deniedDir) {
330
+ return true;
331
+ }
332
+ }
333
+ // Check custom allowed dirs (everything else is under rootDir)
334
+ // If allowedDirs is set, deny anything not in those dirs
335
+ if (this.config.allowedDirs.length > 0) {
336
+ const inAllowed = this.config.allowedDirs.some(allowedDir => {
337
+ const absAllowed = resolve(this.config.rootDir, allowedDir);
338
+ return absPath.startsWith(absAllowed);
339
+ });
340
+ if (!inAllowed && this.isUnderRootDir(absPath))
341
+ return false; // Under root but not in allowed
342
+ }
343
+ return false;
344
+ }
345
+ matchesDeniedPattern(absPath) {
346
+ const relativePath = relative(this.config.rootDir, absPath);
347
+ for (const pattern of this.config.deniedPatterns) {
348
+ const globPattern = pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*');
349
+ try {
350
+ const regex = new RegExp('^' + globPattern + '$');
351
+ if (regex.test(relativePath) || regex.test(absPath))
352
+ return true;
353
+ }
354
+ catch {
355
+ // Invalid pattern, skip
356
+ }
357
+ }
358
+ return false;
359
+ }
360
+ recordViolation(type, path, reason) {
361
+ this.violations.push({
362
+ type,
363
+ path,
364
+ reason: reason || 'Access denied by sandbox',
365
+ timestamp: Date.now(),
366
+ });
367
+ }
368
+ }
369
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1,151 @@
1
+ export type TaskStatus = 'scheduled' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled';
2
+ export type IntervalUnit = 'minutes' | 'hours' | 'days';
3
+ export interface ScheduledTaskConfig {
4
+ /** Unique identifier (auto-generated if not provided) */
5
+ id: string;
6
+ /** Human-readable name */
7
+ name: string;
8
+ /** The prompt / task description to execute each run */
9
+ prompt: string;
10
+ /** Interval value (used together with intervalUnit) */
11
+ interval: number;
12
+ /** Unit for the interval */
13
+ intervalUnit: IntervalUnit;
14
+ /** Maximum number of runs – 0 = unlimited */
15
+ maxRuns: number;
16
+ /** Optional model override */
17
+ model?: string;
18
+ /** Whether to auto-approve tool calls during execution */
19
+ autoApprove: boolean;
20
+ /** Whether to notify on completion (platform-dependent) */
21
+ notifyOnComplete: boolean;
22
+ /** Whether to save results to a file */
23
+ saveResults: boolean;
24
+ /** Working directory for the task */
25
+ workingDirectory: string;
26
+ }
27
+ export interface TaskExecution {
28
+ /** Unique execution ID */
29
+ id: string;
30
+ /** The task this execution belongs to */
31
+ taskId: string;
32
+ /** ISO timestamp when execution started */
33
+ startedAt: string;
34
+ /** ISO timestamp when execution ended */
35
+ endedAt?: string;
36
+ /** Duration in ms */
37
+ durationMs?: number;
38
+ /** Execution status */
39
+ status: 'success' | 'error' | 'timeout' | 'aborted';
40
+ /** The prompt that was executed (may differ from task if templated) */
41
+ prompt: string;
42
+ /** Result text */
43
+ result?: string;
44
+ /** Error message if status is error */
45
+ error?: string;
46
+ /** Number of iterations used */
47
+ iterations: number;
48
+ /** Cost in USD */
49
+ cost: number;
50
+ /** Files modified during this execution */
51
+ filesChanged: number;
52
+ /** Commands run during this execution */
53
+ commandsRun: number;
54
+ }
55
+ export interface ScheduledTaskState {
56
+ config: ScheduledTaskConfig;
57
+ status: TaskStatus;
58
+ /** Number of completed runs so far */
59
+ runCount: number;
60
+ /** Number of failed runs */
61
+ failCount: number;
62
+ /** ISO timestamp of last successful run */
63
+ lastRunAt?: string;
64
+ /** ISO timestamp of next scheduled run */
65
+ nextRunAt?: string;
66
+ /** ISO timestamp when the task was created */
67
+ createdAt: string;
68
+ /** ISO timestamp when the task was last updated */
69
+ updatedAt: string;
70
+ /** Execution history (most recent first, capped at 100) */
71
+ history: TaskExecution[];
72
+ /** Accumulated cost across all runs */
73
+ totalCost: number;
74
+ /** Accumulated iterations across all runs */
75
+ totalIterations: number;
76
+ }
77
+ export interface ScheduledTaskEngine {
78
+ /** Run a prompt and return structured results */
79
+ runPrompt(prompt: string, model?: string, workingDir?: string): Promise<{
80
+ text: string;
81
+ inputTokens: number;
82
+ outputTokens: number;
83
+ cost: number;
84
+ filesChanged: number;
85
+ commandsRun: number;
86
+ iterations: number;
87
+ error?: string;
88
+ }>;
89
+ }
90
+ export declare class ScheduledTaskManager {
91
+ private tasks;
92
+ private timers;
93
+ private engine;
94
+ private shuttingDown;
95
+ constructor(engine?: ScheduledTaskEngine);
96
+ /** Set or replace the execution engine */
97
+ setEngine(engine: ScheduledTaskEngine): void;
98
+ /**
99
+ * Schedule a new recurring task.
100
+ * Returns the full task state including its generated ID.
101
+ */
102
+ scheduleTask(config: Omit<ScheduledTaskConfig, 'id'> & {
103
+ id?: string;
104
+ }): ScheduledTaskState;
105
+ /** Cancel a scheduled task permanently */
106
+ cancelTask(taskId: string): boolean;
107
+ /** List all tasks, optionally filtered by status */
108
+ listTasks(status?: TaskStatus): ScheduledTaskState[];
109
+ /** Temporarily pause a task */
110
+ pauseTask(taskId: string): boolean;
111
+ /** Resume a paused task */
112
+ resumeTask(taskId: string): boolean;
113
+ /** Get execution status and history for a task */
114
+ getTaskStatus(taskId: string): ScheduledTaskState | undefined;
115
+ /** Update a task's configuration (resets timer) */
116
+ updateTask(taskId: string, updates: Partial<Omit<ScheduledTaskConfig, 'id'>>): boolean;
117
+ /** Delete a task entirely (removes from memory and persistence) */
118
+ deleteTask(taskId: string): boolean;
119
+ /** Manually trigger a task run (outside its schedule) */
120
+ runTaskNow(taskId: string): Promise<TaskExecution | null>;
121
+ /** Pause all scheduled tasks */
122
+ pauseAll(): number;
123
+ /** Resume all paused tasks */
124
+ resumeAll(): number;
125
+ /** Cancel all tasks */
126
+ cancelAll(): number;
127
+ /** Get aggregate stats across all tasks */
128
+ getAggregateStats(): {
129
+ totalTasks: number;
130
+ activeTasks: number;
131
+ pausedTasks: number;
132
+ totalRuns: number;
133
+ totalFailures: number;
134
+ totalCost: number;
135
+ };
136
+ /** Get recent executions across all tasks, sorted by time (most recent first) */
137
+ getRecentExecutions(limit?: number): TaskExecution[];
138
+ /** Gracefully shut down – cancels all timers */
139
+ shutdown(): void;
140
+ private scheduleTimer;
141
+ private clearTimer;
142
+ private onTimerFire;
143
+ private executeTask;
144
+ private intervalToMs;
145
+ private saveResultToFile;
146
+ private notifyCompletion;
147
+ private persistTasks;
148
+ private loadTasks;
149
+ private registerShutdownHooks;
150
+ }
151
+ //# sourceMappingURL=scheduled-tasks.d.ts.map