boxsafe 1.0.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 (118) hide show
  1. package/.directory +2 -0
  2. package/.env.example +3 -0
  3. package/AUDIT_LANG.md +45 -0
  4. package/BOXSAFE_VERSION_NOTES.md +14 -0
  5. package/README.md +4 -0
  6. package/TODO.md +130 -0
  7. package/adapters/index.ts +27 -0
  8. package/adapters/primary/cli-adapter.ts +56 -0
  9. package/adapters/secondary/filesystem/node-filesystem.ts +307 -0
  10. package/adapters/secondary/system/configuration.ts +147 -0
  11. package/ai/caller.ts +42 -0
  12. package/ai/label.ts +33 -0
  13. package/ai/modelConfig.ts +236 -0
  14. package/ai/provider.ts +111 -0
  15. package/boxsafe.config.json +68 -0
  16. package/core/auth/dasktop/cred/CRED.md +112 -0
  17. package/core/auth/dasktop/cred/credLinux.ts +82 -0
  18. package/core/auth/dasktop/cred/credWin.ts +2 -0
  19. package/core/config/defaults/boxsafeDefaults.ts +67 -0
  20. package/core/config/defaults/index.ts +1 -0
  21. package/core/config/loadConfig.ts +133 -0
  22. package/core/loop/about.md +13 -0
  23. package/core/loop/boxConfig.ts +20 -0
  24. package/core/loop/buildExecCommand.ts +76 -0
  25. package/core/loop/cmd/execode.ts +121 -0
  26. package/core/loop/cmd/test.js +3 -0
  27. package/core/loop/execLoop.ts +341 -0
  28. package/core/loop/git/VERSIONING.md +17 -0
  29. package/core/loop/git/commands.ts +11 -0
  30. package/core/loop/git/gitClient.ts +78 -0
  31. package/core/loop/git/index.ts +99 -0
  32. package/core/loop/git/runVersionControlRunner.ts +33 -0
  33. package/core/loop/initNavigator.ts +44 -0
  34. package/core/loop/initTasksManager.ts +35 -0
  35. package/core/loop/runValidation.ts +25 -0
  36. package/core/loop/tasks/AGENT-TASKS.md +36 -0
  37. package/core/loop/tasks/index.ts +96 -0
  38. package/core/loop/toolCalls.ts +168 -0
  39. package/core/loop/toolDispatcher.ts +146 -0
  40. package/core/loop/traceLogger.ts +106 -0
  41. package/core/loop/types.ts +26 -0
  42. package/core/loop/versionControlAdapter.ts +36 -0
  43. package/core/loop/waterfall.ts +404 -0
  44. package/core/loop/writeArtifactAtomically.ts +13 -0
  45. package/core/navigate/NAVIGATE.md +186 -0
  46. package/core/navigate/about.md +128 -0
  47. package/core/navigate/examples.ts +367 -0
  48. package/core/navigate/handler.ts +148 -0
  49. package/core/navigate/index.ts +32 -0
  50. package/core/navigate/navigate.test.ts +372 -0
  51. package/core/navigate/navigator.ts +437 -0
  52. package/core/navigate/types.ts +132 -0
  53. package/core/navigate/utils.ts +146 -0
  54. package/core/paths/paths.ts +33 -0
  55. package/core/ports/index.ts +271 -0
  56. package/core/segments/CONVENTIONS.md +30 -0
  57. package/core/segments/loop/index.ts +18 -0
  58. package/core/segments/map.ts +56 -0
  59. package/core/segments/navigate/index.ts +20 -0
  60. package/core/segments/versionControl/index.ts +18 -0
  61. package/core/util/logger.ts +128 -0
  62. package/docs/AGENT-TASKS.md +36 -0
  63. package/docs/ARQUITETURA_CORRECAO.md +121 -0
  64. package/docs/CONVENTIONS.md +30 -0
  65. package/docs/CRED.md +112 -0
  66. package/docs/L_RAG.md +567 -0
  67. package/docs/NAVIGATE.md +186 -0
  68. package/docs/PRIMARY_ACTORS.md +78 -0
  69. package/docs/SECONDARY_ACTORS.md +174 -0
  70. package/docs/VERSIONING.md +17 -0
  71. package/docs/boxsafe.config.md +472 -0
  72. package/eslint.config.mts +15 -0
  73. package/main.ts +53 -0
  74. package/memo/generated/codelog.md +13 -0
  75. package/memo/state/tasks/state.json +6 -0
  76. package/memo/state/tasks/tasks/task_001.md +2 -0
  77. package/memo/states-logs/logs.txt +7 -0
  78. package/memo/states-logs/trace-mljvrxvi-9g0k4q.jsonl +11 -0
  79. package/memo/states-logs/trace-mljvvc9j-pe9ekj.jsonl +11 -0
  80. package/memo/states-logs/trace-mljvvm1c-wbnqzp.jsonl +11 -0
  81. package/memo/states-logs/trace-mljxecwn-9xh3nw.jsonl +11 -0
  82. package/memo/states-logs/trace-mljxqkfm-ipijik.jsonl +11 -0
  83. package/memo/states-logs/trace-mljxwtrw-3fanky.jsonl +11 -0
  84. package/memo/states-logs/trace-mljxzen3-m8iinh.jsonl +11 -0
  85. package/memo/states-logs/trace-mljyucef-td6odn.jsonl +11 -0
  86. package/memo/states-logs/trace-mljyuprw-b1a6f4.jsonl +11 -0
  87. package/memo/states-logs/trace-mljyvefl-b6yoce.jsonl +11 -0
  88. package/memo/states-logs/trace-mljyxjo4-n7ibj2.jsonl +13 -0
  89. package/memo/states-logs/trace-mljziez5-8drqtn.jsonl +13 -0
  90. package/memo/states-logs/trace-mljziulp-dtd03z.jsonl +13 -0
  91. package/memo/states-logs/trace-mljzjwrq-1p2krb.jsonl +13 -0
  92. package/memo/states-logs/trace-mljzl0i7-b1cqa6.jsonl +13 -0
  93. package/memo/states-logs/trace-mljzmlk6-7kdyls.jsonl +13 -0
  94. package/memo/states-logs/trace-mlk0oj25-xa3dcu.jsonl +13 -0
  95. package/memo/states-logs/trace-mlk1x59q-713huj.jsonl +14 -0
  96. package/memo/states-logs/trace-mlk22dz8-7fd6hq.jsonl +14 -0
  97. package/memo/states-logs/trace-mlk241uy-wmx907.jsonl +14 -0
  98. package/memo/states-logs/trace-mlk2bf5r-yoh1vg.jsonl +15 -0
  99. package/package.json +44 -0
  100. package/pnpm-workspace.yaml +4 -0
  101. package/prompt_improvement_example.md +55 -0
  102. package/remove.txt +1 -0
  103. package/tests/adapters.test.ts +128 -0
  104. package/tests/extractCode.test.ts +26 -0
  105. package/tests/integration.test.ts +83 -0
  106. package/tests/loadConfig.test.ts +25 -0
  107. package/tests/navigatorBoundary.test.ts +17 -0
  108. package/tests/ports.test.ts +84 -0
  109. package/tests/runAllTests.ts +49 -0
  110. package/tests/toolCalls.test.ts +149 -0
  111. package/tests/waterfall.test.ts +52 -0
  112. package/tsconfig.json +32 -0
  113. package/tsup.config.ts +17 -0
  114. package/types.d.ts +96 -0
  115. package/util/ANSI.ts +29 -0
  116. package/util/extractCode.ts +217 -0
  117. package/util/extractToolCalls.ts +80 -0
  118. package/util/logger.ts +125 -0
@@ -0,0 +1,372 @@
1
+ /**
2
+ * @fileoverview
3
+ * Unit tests for the Navigator module.
4
+ * Validates all operations in isolation.
5
+ *
6
+ * Run with: npm test navigate.test.ts
7
+ */
8
+
9
+ import { createNavigator, createNavigatorHandler } from '@core/navigate';
10
+ import type {
11
+ DirectoryListing,
12
+ FileReadResult,
13
+ FileWriteResult,
14
+ DirectoryCreateResult,
15
+ DeleteResult,
16
+ MetadataResult,
17
+ } from '@core/navigate';
18
+ import path from 'node:path';
19
+ import fs from 'node:fs';
20
+ import { Logger } from '@core/util/logger';
21
+
22
+ // Type guards for proper narrowing
23
+ const isDirectory = (result: any): result is DirectoryListing => result.ok && 'entries' in result;
24
+ const isFileRead = (result: any): result is FileReadResult => result.ok && 'content' in result;
25
+ const isFileWrite = (result: any): result is FileWriteResult => result.ok && 'created' in result;
26
+ const isDirectoryCreate = (result: any): result is DirectoryCreateResult => result.ok && !('entries' in result) && !('content' in result) && !('stat' in result);
27
+ const isDelete = (result: any): result is DeleteResult => result.ok && 'type' in result && 'deletedAt' in result;
28
+ const isMetadata = (result: any): result is MetadataResult => result.ok && 'stat' in result;
29
+ const isError = (result: any): result is { ok: false; error: string } => !result.ok;
30
+
31
+ const testLogger = Logger.createModuleLogger('NavigatorTests');
32
+
33
+ /**
34
+ * Test suite for Navigator class
35
+ */
36
+ export const navigatorTests = {
37
+ /**
38
+ * Test: Creating navigator with invalid workspace
39
+ */
40
+ async testInvalidWorkspace() {
41
+ testLogger.info('[TEST] Invalid workspace path');
42
+ try {
43
+ createNavigator({
44
+ workspace: '/nonexistent/path/to/workspace',
45
+ });
46
+ testLogger.info('FAIL: Should throw for nonexistent workspace');
47
+ return false;
48
+ } catch (err) {
49
+ testLogger.info('✓ PASS: Correctly rejects invalid workspace');
50
+ return true;
51
+ }
52
+ },
53
+
54
+ /**
55
+ * Test: List directory
56
+ */
57
+ async testListDirectory() {
58
+ testLogger.info('[TEST] List directory');
59
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
60
+ try {
61
+ // Create test files
62
+ fs.writeFileSync(path.join(tempDir, 'file1.txt'), 'content');
63
+ fs.mkdirSync(path.join(tempDir, 'subdir'));
64
+
65
+ const nav = createNavigator({ workspace: tempDir });
66
+ const result = await nav.listDirectory('.');
67
+
68
+ if (!isDirectory(result)) {
69
+ testLogger.info(`FAIL: ${isError(result) ? result.error : 'unexpected result'}`);
70
+ return false;
71
+ }
72
+
73
+ if (result.total !== 2) {
74
+ testLogger.info(`FAIL: Expected 2 entries, got ${result.total}`);
75
+ return false;
76
+ }
77
+
78
+ const hasFile = result.entries.some((e: any) => e.name === 'file1.txt' && e.type === 'file');
79
+ const hasDir = result.entries.some((e: any) => e.name === 'subdir' && e.type === 'directory');
80
+
81
+ if (!hasFile || !hasDir) {
82
+ testLogger.info('FAIL: Did not find expected entries');
83
+ return false;
84
+ }
85
+
86
+ testLogger.info('✓ PASS: Directory listing works correctly');
87
+ return true;
88
+ } finally {
89
+ fs.rmSync(tempDir, { recursive: true, force: true });
90
+ }
91
+ },
92
+
93
+ /**
94
+ * Test: Read file
95
+ */
96
+ async testReadFile() {
97
+ testLogger.info('[TEST] Read file');
98
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
99
+ try {
100
+ const filePath = path.join(tempDir, 'test.txt');
101
+ const content = 'Hello, World!';
102
+ fs.writeFileSync(filePath, content);
103
+
104
+ const nav = createNavigator({ workspace: tempDir });
105
+ const result = await nav.readFile('test.txt');
106
+
107
+ if (!isFileRead(result)) {
108
+ testLogger.info(`FAIL: ${isError(result) ? result.error : 'unexpected result'}`);
109
+ return false;
110
+ }
111
+
112
+ if (result.content !== content) {
113
+ testLogger.info(`FAIL: Content mismatch`);
114
+ return false;
115
+ }
116
+
117
+ if (result.size !== content.length) {
118
+ testLogger.info(`FAIL: Size mismatch`);
119
+ return false;
120
+ }
121
+
122
+ testLogger.info('✓ PASS: File reading works correctly');
123
+ return true;
124
+ } finally {
125
+ fs.rmSync(tempDir, { recursive: true, force: true });
126
+ }
127
+ },
128
+
129
+ /**
130
+ * Test: Write file (new)
131
+ */
132
+ async testWriteFileNew() {
133
+ testLogger.info('[TEST] Write file (new)');
134
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
135
+ try {
136
+ const nav = createNavigator({ workspace: tempDir });
137
+ const content = 'New file content';
138
+
139
+ const result = await nav.writeFile('newfile.txt', content, { createDirs: true });
140
+
141
+ if (!isFileWrite(result)) {
142
+ testLogger.info(`FAIL: ${isError(result) ? result.error : 'unexpected result'}`);
143
+ return false;
144
+ }
145
+
146
+ if (!result.created) {
147
+ testLogger.info('FAIL: Should mark file as created');
148
+ return false;
149
+ }
150
+
151
+ // Verify file exists
152
+ const filePath = path.join(tempDir, 'newfile.txt');
153
+ const exists = fs.existsSync(filePath);
154
+ if (!exists) {
155
+ testLogger.info('FAIL: File was not created');
156
+ return false;
157
+ }
158
+
159
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
160
+ if (fileContent !== content) {
161
+ testLogger.info('FAIL: File content mismatch');
162
+ return false;
163
+ }
164
+
165
+ testLogger.info('✓ PASS: File writing (new) works correctly');
166
+ return true;
167
+ } finally {
168
+ fs.rmSync(tempDir, { recursive: true, force: true });
169
+ }
170
+ },
171
+
172
+ /**
173
+ * Test: Create directory
174
+ */
175
+ async testCreateDirectory() {
176
+ testLogger.info('[TEST] Create directory');
177
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
178
+ try {
179
+ const nav = createNavigator({ workspace: tempDir });
180
+
181
+ const result = await nav.createDirectory('level1/level2/level3', { recursive: true });
182
+
183
+ if (!isDirectoryCreate(result)) {
184
+ testLogger.info(`FAIL: ${isError(result) ? result.error : 'unexpected result'}`);
185
+ return false;
186
+ }
187
+
188
+ // Verify directory exists
189
+ const dirPath = path.join(tempDir, 'level1/level2/level3');
190
+ if (!fs.existsSync(dirPath)) {
191
+ testLogger.info('FAIL: Directory was not created');
192
+ return false;
193
+ }
194
+
195
+ testLogger.info('✓ PASS: Directory creation works correctly');
196
+ return true;
197
+ } finally {
198
+ fs.rmSync(tempDir, { recursive: true, force: true });
199
+ }
200
+ },
201
+
202
+ /**
203
+ * Test: Delete file
204
+ */
205
+ async testDeleteFile() {
206
+ testLogger.info('[TEST] Delete file');
207
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
208
+ try {
209
+ const filePath = path.join(tempDir, 'todelete.txt');
210
+ fs.writeFileSync(filePath, 'content');
211
+
212
+ const nav = createNavigator({ workspace: tempDir });
213
+ const result = await nav.delete('todelete.txt');
214
+
215
+ if (!isDelete(result)) {
216
+ testLogger.info(`FAIL: ${isError(result) ? result.error : 'unexpected result'}`);
217
+ return false;
218
+ }
219
+
220
+ if (result.type !== 'file') {
221
+ testLogger.info('FAIL: Type should be file');
222
+ return false;
223
+ }
224
+
225
+ // Verify file is gone
226
+ if (fs.existsSync(filePath)) {
227
+ testLogger.info('FAIL: File still exists');
228
+ return false;
229
+ }
230
+
231
+ testLogger.info('✓ PASS: File deletion works correctly');
232
+ return true;
233
+ } finally {
234
+ fs.rmSync(tempDir, { recursive: true, force: true });
235
+ }
236
+ },
237
+
238
+ /**
239
+ * Test: Security - prevent directory traversal
240
+ */
241
+ async testSecurityBoundary() {
242
+ testLogger.info('[TEST] Security boundary (prevent traversal)');
243
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
244
+ try {
245
+ const nav = createNavigator({ workspace: tempDir });
246
+
247
+ // Try to escape workspace
248
+ const result = await nav.readFile('../../../etc/passwd');
249
+
250
+ if (!isError(result)) {
251
+ testLogger.info('FAIL: Should not allow directory traversal');
252
+ return false;
253
+ }
254
+
255
+ if (result.error.includes('outside workspace')) {
256
+ testLogger.info('✓ PASS: Correctly blocks directory traversal');
257
+ return true;
258
+ } else {
259
+ testLogger.info(`FAIL: Wrong error message: ${result.error}`);
260
+ return false;
261
+ }
262
+ } finally {
263
+ fs.rmSync(tempDir, { recursive: true, force: true });
264
+ }
265
+ },
266
+
267
+ /**
268
+ * Test: Get metadata
269
+ */
270
+ async testGetMetadata() {
271
+ testLogger.info('[TEST] Get metadata');
272
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
273
+ try {
274
+ const filePath = path.join(tempDir, 'test.txt');
275
+ const content = 'test content';
276
+ fs.writeFileSync(filePath, content);
277
+
278
+ const nav = createNavigator({ workspace: tempDir });
279
+ const result = await nav.getMetadata('test.txt');
280
+
281
+ if (!isMetadata(result)) {
282
+ testLogger.info(`❌ FAIL: ${isError(result) ? result.error : 'unexpected result'}`);
283
+ return false;
284
+ }
285
+
286
+ if (result.stat.type !== 'file') {
287
+ testLogger.info('❌ FAIL: Type should be file');
288
+ return false;
289
+ }
290
+
291
+ if (result.stat.size !== content.length) {
292
+ testLogger.info('❌ FAIL: Size mismatch');
293
+ return false;
294
+ }
295
+
296
+ if (!result.stat.isReadable) {
297
+ testLogger.info('❌ FAIL: Should be readable');
298
+ return false;
299
+ }
300
+
301
+ testLogger.info('✓ PASS: Metadata retrieval works correctly');
302
+ return true;
303
+ } finally {
304
+ fs.rmSync(tempDir, { recursive: true, force: true });
305
+ }
306
+ },
307
+
308
+ /**
309
+ * Test: Handler integration
310
+ */
311
+ async testHandlerIntegration() {
312
+ testLogger.info('[TEST] Handler integration');
313
+ const tempDir = fs.mkdtempSync(path.join('/tmp', 'nav-test-'));
314
+ try {
315
+ const handler = createNavigatorHandler(tempDir);
316
+
317
+ // Test list operation
318
+ const listResult = await handler.execute({
319
+ op: 'list',
320
+ path: '.',
321
+ });
322
+
323
+ if (!isDirectory(listResult)) {
324
+ testLogger.info(`❌ FAIL: ${isError(listResult) ? listResult.error : 'unexpected result'}`);
325
+ return false;
326
+ }
327
+
328
+ // Test write operation
329
+ const writeResult = await handler.execute({
330
+ op: 'write',
331
+ path: 'test.txt',
332
+ content: 'handler test',
333
+ });
334
+
335
+ if (!isFileWrite(writeResult)) {
336
+ testLogger.info(`❌ FAIL: ${isError(writeResult) ? writeResult.error : 'unexpected result'}`);
337
+ return false;
338
+ }
339
+
340
+ testLogger.info('✓ PASS: Handler integration works correctly');
341
+ return true;
342
+ } finally {
343
+ fs.rmSync(tempDir, { recursive: true, force: true });
344
+ }
345
+ },
346
+ };
347
+
348
+ /**
349
+ * Run all tests
350
+ */
351
+ export async function runAllTests() {
352
+ testLogger.info('🧪 Running Navigator Tests\n');
353
+
354
+ const tests = Object.entries(navigatorTests);
355
+ let passed = 0;
356
+ let failed = 0;
357
+
358
+ for (const [name, test] of tests) {
359
+ try {
360
+ const result = await test();
361
+ if (result) passed++;
362
+ else failed++;
363
+ } catch (err: any) {
364
+ testLogger.info(`❌ EXCEPTION: ${err?.message ?? err}`);
365
+ failed++;
366
+ }
367
+ testLogger.info('');
368
+ }
369
+
370
+ testLogger.info(`\n📊 Results: ${passed} passed, ${failed} failed out of ${tests.length} tests`);
371
+ return failed === 0;
372
+ }