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.
- package/.directory +2 -0
- package/.env.example +3 -0
- package/AUDIT_LANG.md +45 -0
- package/BOXSAFE_VERSION_NOTES.md +14 -0
- package/README.md +4 -0
- package/TODO.md +130 -0
- package/adapters/index.ts +27 -0
- package/adapters/primary/cli-adapter.ts +56 -0
- package/adapters/secondary/filesystem/node-filesystem.ts +307 -0
- package/adapters/secondary/system/configuration.ts +147 -0
- package/ai/caller.ts +42 -0
- package/ai/label.ts +33 -0
- package/ai/modelConfig.ts +236 -0
- package/ai/provider.ts +111 -0
- package/boxsafe.config.json +68 -0
- package/core/auth/dasktop/cred/CRED.md +112 -0
- package/core/auth/dasktop/cred/credLinux.ts +82 -0
- package/core/auth/dasktop/cred/credWin.ts +2 -0
- package/core/config/defaults/boxsafeDefaults.ts +67 -0
- package/core/config/defaults/index.ts +1 -0
- package/core/config/loadConfig.ts +133 -0
- package/core/loop/about.md +13 -0
- package/core/loop/boxConfig.ts +20 -0
- package/core/loop/buildExecCommand.ts +76 -0
- package/core/loop/cmd/execode.ts +121 -0
- package/core/loop/cmd/test.js +3 -0
- package/core/loop/execLoop.ts +341 -0
- package/core/loop/git/VERSIONING.md +17 -0
- package/core/loop/git/commands.ts +11 -0
- package/core/loop/git/gitClient.ts +78 -0
- package/core/loop/git/index.ts +99 -0
- package/core/loop/git/runVersionControlRunner.ts +33 -0
- package/core/loop/initNavigator.ts +44 -0
- package/core/loop/initTasksManager.ts +35 -0
- package/core/loop/runValidation.ts +25 -0
- package/core/loop/tasks/AGENT-TASKS.md +36 -0
- package/core/loop/tasks/index.ts +96 -0
- package/core/loop/toolCalls.ts +168 -0
- package/core/loop/toolDispatcher.ts +146 -0
- package/core/loop/traceLogger.ts +106 -0
- package/core/loop/types.ts +26 -0
- package/core/loop/versionControlAdapter.ts +36 -0
- package/core/loop/waterfall.ts +404 -0
- package/core/loop/writeArtifactAtomically.ts +13 -0
- package/core/navigate/NAVIGATE.md +186 -0
- package/core/navigate/about.md +128 -0
- package/core/navigate/examples.ts +367 -0
- package/core/navigate/handler.ts +148 -0
- package/core/navigate/index.ts +32 -0
- package/core/navigate/navigate.test.ts +372 -0
- package/core/navigate/navigator.ts +437 -0
- package/core/navigate/types.ts +132 -0
- package/core/navigate/utils.ts +146 -0
- package/core/paths/paths.ts +33 -0
- package/core/ports/index.ts +271 -0
- package/core/segments/CONVENTIONS.md +30 -0
- package/core/segments/loop/index.ts +18 -0
- package/core/segments/map.ts +56 -0
- package/core/segments/navigate/index.ts +20 -0
- package/core/segments/versionControl/index.ts +18 -0
- package/core/util/logger.ts +128 -0
- package/docs/AGENT-TASKS.md +36 -0
- package/docs/ARQUITETURA_CORRECAO.md +121 -0
- package/docs/CONVENTIONS.md +30 -0
- package/docs/CRED.md +112 -0
- package/docs/L_RAG.md +567 -0
- package/docs/NAVIGATE.md +186 -0
- package/docs/PRIMARY_ACTORS.md +78 -0
- package/docs/SECONDARY_ACTORS.md +174 -0
- package/docs/VERSIONING.md +17 -0
- package/docs/boxsafe.config.md +472 -0
- package/eslint.config.mts +15 -0
- package/main.ts +53 -0
- package/memo/generated/codelog.md +13 -0
- package/memo/state/tasks/state.json +6 -0
- package/memo/state/tasks/tasks/task_001.md +2 -0
- package/memo/states-logs/logs.txt +7 -0
- package/memo/states-logs/trace-mljvrxvi-9g0k4q.jsonl +11 -0
- package/memo/states-logs/trace-mljvvc9j-pe9ekj.jsonl +11 -0
- package/memo/states-logs/trace-mljvvm1c-wbnqzp.jsonl +11 -0
- package/memo/states-logs/trace-mljxecwn-9xh3nw.jsonl +11 -0
- package/memo/states-logs/trace-mljxqkfm-ipijik.jsonl +11 -0
- package/memo/states-logs/trace-mljxwtrw-3fanky.jsonl +11 -0
- package/memo/states-logs/trace-mljxzen3-m8iinh.jsonl +11 -0
- package/memo/states-logs/trace-mljyucef-td6odn.jsonl +11 -0
- package/memo/states-logs/trace-mljyuprw-b1a6f4.jsonl +11 -0
- package/memo/states-logs/trace-mljyvefl-b6yoce.jsonl +11 -0
- package/memo/states-logs/trace-mljyxjo4-n7ibj2.jsonl +13 -0
- package/memo/states-logs/trace-mljziez5-8drqtn.jsonl +13 -0
- package/memo/states-logs/trace-mljziulp-dtd03z.jsonl +13 -0
- package/memo/states-logs/trace-mljzjwrq-1p2krb.jsonl +13 -0
- package/memo/states-logs/trace-mljzl0i7-b1cqa6.jsonl +13 -0
- package/memo/states-logs/trace-mljzmlk6-7kdyls.jsonl +13 -0
- package/memo/states-logs/trace-mlk0oj25-xa3dcu.jsonl +13 -0
- package/memo/states-logs/trace-mlk1x59q-713huj.jsonl +14 -0
- package/memo/states-logs/trace-mlk22dz8-7fd6hq.jsonl +14 -0
- package/memo/states-logs/trace-mlk241uy-wmx907.jsonl +14 -0
- package/memo/states-logs/trace-mlk2bf5r-yoh1vg.jsonl +15 -0
- package/package.json +44 -0
- package/pnpm-workspace.yaml +4 -0
- package/prompt_improvement_example.md +55 -0
- package/remove.txt +1 -0
- package/tests/adapters.test.ts +128 -0
- package/tests/extractCode.test.ts +26 -0
- package/tests/integration.test.ts +83 -0
- package/tests/loadConfig.test.ts +25 -0
- package/tests/navigatorBoundary.test.ts +17 -0
- package/tests/ports.test.ts +84 -0
- package/tests/runAllTests.ts +49 -0
- package/tests/toolCalls.test.ts +149 -0
- package/tests/waterfall.test.ts +52 -0
- package/tsconfig.json +32 -0
- package/tsup.config.ts +17 -0
- package/types.d.ts +96 -0
- package/util/ANSI.ts +29 -0
- package/util/extractCode.ts +217 -0
- package/util/extractToolCalls.ts +80 -0
- package/util/logger.ts +125 -0
package/.directory
ADDED
package/.env.example
ADDED
package/AUDIT_LANG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Language Audit - BoxSafe Project
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
Ensure all code files use English for comments and documentation to maintain consistency across the project.
|
|
5
|
+
|
|
6
|
+
## Files Checked & Fixed
|
|
7
|
+
|
|
8
|
+
### ✅ Fixed
|
|
9
|
+
- [x] `/home/inky/Development/boxsafe/ai/modelConfig.ts` - Portuguese comments → English
|
|
10
|
+
- [x] `/home/inky/Development/boxsafe/prompt_improvement_example.md` - Portuguese content → English
|
|
11
|
+
- [x] `/home/inky/Development/boxsafe/util/logger.ts` - Portuguese comments → English
|
|
12
|
+
- [x] `/home/inky/Development/boxsafe/core/segments/map.ts` - Portuguese comments → English
|
|
13
|
+
- [x] `/home/inky/Development/boxsafe/core/auth/dasktop/cred/credWin.ts` - Portuguese comments → English
|
|
14
|
+
- [x] `/home/inky/Development/boxsafe/core/ports/index.ts` - Portuguese comments → English (COMPLETED)
|
|
15
|
+
- [x] `/home/inky/Development/boxsafe/core/config/loadConfig.ts` - Portuguese comments → English
|
|
16
|
+
- [x] `/home/inky/Development/boxsafe/core/config/defaults/boxsafeDefaults.ts` - Portuguese comments → English
|
|
17
|
+
- [x] `/home/inky/Development/boxsafe/core/navigate/handler.ts` - Portuguese comments → English
|
|
18
|
+
- [x] `/home/inky/Development/boxsafe/core/navigate/navigator.ts` - Portuguese comments → English (COMPLETED)
|
|
19
|
+
|
|
20
|
+
### 🔄 Already in English
|
|
21
|
+
- [x] `/home/inky/Development/boxsafe/core/loop/toolCalls.ts` - Comments already in English
|
|
22
|
+
- [x] `/home/inky/Development/boxsafe/core/loop/execLoop.ts` - Comments already in English
|
|
23
|
+
- [x] `/home/inky/Development/boxsafe/core/loop/waterfall.ts` - Comments already in English
|
|
24
|
+
- [x] `/home/inky/Development/boxsafe/tests/integration.test.ts` - Comments already in English
|
|
25
|
+
- [x] `/home/inky/Development/boxsafe/tests/waterfall.test.ts` - Comments already in English
|
|
26
|
+
- [x] `/home/inky/Development/boxsafe/tests/ports.test.ts` - Comments already in English
|
|
27
|
+
- [x] `/home/inky/Development/boxsafe/tests/adapters.test.ts` - Comments already in English
|
|
28
|
+
|
|
29
|
+
### 📝 Notes
|
|
30
|
+
- Documentation files in `/docs/` can remain in Portuguese as they are user-facing
|
|
31
|
+
- `/util/` and `/root/` directories are excluded from this audit
|
|
32
|
+
- Focus on core functionality files only
|
|
33
|
+
|
|
34
|
+
## Standards
|
|
35
|
+
- All code comments: English
|
|
36
|
+
- All variable/function names: English (already standard)
|
|
37
|
+
- All documentation in code: English
|
|
38
|
+
- User-facing docs: Can be multilingual
|
|
39
|
+
|
|
40
|
+
## 🎯 AUDIT COMPLETE
|
|
41
|
+
**Status: 100% COMPLETE**
|
|
42
|
+
- All core code files have been audited
|
|
43
|
+
- All Portuguese comments have been converted to English
|
|
44
|
+
- All files now follow the language standards
|
|
45
|
+
- Project consistency achieved
|
package/README.md
ADDED
package/TODO.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# BoxSafe TODO - Sistema Inteligente de Prompts
|
|
2
|
+
|
|
3
|
+
## Status Atual
|
|
4
|
+
- ✅ Sistema de tools 100% funcional
|
|
5
|
+
- ✅ Parsing de json-tool perfeito
|
|
6
|
+
- ✅ Logging consistente implementado
|
|
7
|
+
- ❌ Prompt atual incompleto (falta parâmetro `content`)
|
|
8
|
+
- ❌ Sistema de prompts hardcoded (gambiarra funcional)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🎯 Objetivo Principal
|
|
13
|
+
Implementar um sistema inteligente de gerenciamento de prompts que:
|
|
14
|
+
- Categorize modelos por capacidade (LOW/MEDIUM/HIGH/EXCELLENT)
|
|
15
|
+
- Otimize frequência de lembretes baseada no modelo
|
|
16
|
+
- Reduza custos com providers
|
|
17
|
+
- Melhore a experiência do usuário
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📋 Tasks Organizadas
|
|
22
|
+
|
|
23
|
+
### Phase 1: Fundação (Crítica)
|
|
24
|
+
**1. Criar Sistema de Configuração de Modelos**
|
|
25
|
+
- [ ] Criar arquivo `ai/modelConfig.ts` com perfis de modelos
|
|
26
|
+
- [ ] Definir categorias: LOW (<8k), MEDIUM (8k-32k), HIGH (32k-128k), EXCELLENT (>128k)
|
|
27
|
+
- [ ] Configurar frequências de lembrete por categoria
|
|
28
|
+
- [ ] Adicionar metadados de custo por token
|
|
29
|
+
|
|
30
|
+
**2. Implementar Estratégias de Prompt**
|
|
31
|
+
- [ ] Criar prompts específicos por capacidade do modelo
|
|
32
|
+
- [ ] Incluir exemplos completos com parâmetro `content`
|
|
33
|
+
- [ ] Sistema de lembretes inteligentes
|
|
34
|
+
- [ ] Validação de prompts
|
|
35
|
+
|
|
36
|
+
**3. Integrar com Loop Principal**
|
|
37
|
+
- [ ] Substituir prompt hardcoded pelo PromptManager
|
|
38
|
+
- [ ] Detectar modelo automaticamente da configuração
|
|
39
|
+
- [ ] Implementar sistema de contexto/usage tracking
|
|
40
|
+
- [ ] Testar integração completa
|
|
41
|
+
|
|
42
|
+
### Phase 2: Inteligência (Otimização)
|
|
43
|
+
**4. Sistema de Context Awareness**
|
|
44
|
+
- [ ] Monitorar uso de contexto em tempo real
|
|
45
|
+
- [ ] Trigger de lembretes baseado em threshold
|
|
46
|
+
- [ ] Otimização de tokens por interação
|
|
47
|
+
- [ ] Sistema de recuperação de erros
|
|
48
|
+
|
|
49
|
+
**5. Otimização de Custos**
|
|
50
|
+
- [ ] Calcular custo por interação
|
|
51
|
+
- [ ] Estratégias para reduzir tokens desnecessários
|
|
52
|
+
- [ ] Balance entre qualidade e custo
|
|
53
|
+
- [ ] Relatórios de uso
|
|
54
|
+
|
|
55
|
+
**6. Sistema de Aprendizado**
|
|
56
|
+
- [ ] Detectar padrões de erros do modelo
|
|
57
|
+
- [ ] Adaptar prompts baseado no histórico
|
|
58
|
+
- [ ] Sistema de feedback automático
|
|
59
|
+
- [ ] Melhoria contínua
|
|
60
|
+
|
|
61
|
+
### Phase 3: Avançado (Futuro)
|
|
62
|
+
**7. Interface de Configuração**
|
|
63
|
+
- [ ] CLI para gerenciar modelos
|
|
64
|
+
- [ ] Configuração via arquivo YAML/JSON
|
|
65
|
+
- [ ] Validação de configurações
|
|
66
|
+
- [ ] Documentação interativa
|
|
67
|
+
|
|
68
|
+
**8. Monitoramento e Analytics**
|
|
69
|
+
- [ ] Dashboard de uso de prompts
|
|
70
|
+
- [ ] Métricas de performance
|
|
71
|
+
- [ ] Alertas de anomalias
|
|
72
|
+
- [ ] Sistema de logging avançado
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 🔧 Detalhes Técnicos
|
|
77
|
+
|
|
78
|
+
### Arquivos a Criar:
|
|
79
|
+
- `ai/modelConfig.ts` - Configurações dos modelos
|
|
80
|
+
- `ai/promptManager.ts` - Sistema inteligente de prompts
|
|
81
|
+
- `ai/promptStrategies.ts` - Estratégias por capacidade
|
|
82
|
+
- `ai/contextTracker.ts` - Monitoramento de contexto
|
|
83
|
+
- `ai/costOptimizer.ts` - Otimização de custos
|
|
84
|
+
|
|
85
|
+
### Arquivos a Modificar:
|
|
86
|
+
- `core/loop/execLoop.ts` - Integrar PromptManager
|
|
87
|
+
- `boxsafe.config.json` - Adicionar configurações de modelo
|
|
88
|
+
- `ai/prompts.ts` - Migrar para sistema estruturado
|
|
89
|
+
|
|
90
|
+
### Critérios de Sucesso:
|
|
91
|
+
- [ ] Prompt completo com parâmetro `content`
|
|
92
|
+
- [ ] Sistema adaptável a diferentes modelos
|
|
93
|
+
- [ ] Redução de 30% nos custos de API
|
|
94
|
+
- [ ] Zero erros de json-tool incompleto
|
|
95
|
+
- [ ] Documentação completa e testada
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 🚀 Próximos Passos Imediatos
|
|
100
|
+
|
|
101
|
+
1. **Discussão**: Validar arquitetura proposta
|
|
102
|
+
2. **Prioridade**: Phase 1 (fundação crítica)
|
|
103
|
+
3. **Execução**: Implementar task por task com validação
|
|
104
|
+
4. **Testes**: Garantir funcionamento em todos os modelos
|
|
105
|
+
5. **Deploy**: Substituir sistema atual
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 📝 Notas
|
|
110
|
+
- Manter compatibilidade com sistema atual
|
|
111
|
+
- Implementar fallbacks para modelos desconhecidos
|
|
112
|
+
- Considerar modelos locais vs API
|
|
113
|
+
- Performance crítica para não impactar o loop
|
|
114
|
+
|
|
115
|
+
Dynamic Tool Loading: Só injete as tools relevantes ao contexto atual
|
|
116
|
+
Schema Compression: Para modelos fracos, use schemas simplificados
|
|
117
|
+
1. Início: Ensina TUDO (full system prompt + todas as tools)
|
|
118
|
+
2. Durante: Relembra periodicamente baseado em:
|
|
119
|
+
• Qualidade do modelo (tier)
|
|
120
|
+
• Frequência de uso das tools
|
|
121
|
+
3. Otimização: Prompts compactos para modelos fracos
|
|
122
|
+
2. uso eficiente ded sistema de caching dos provaders integrar todas os plugs para economizar tokes oferecido pelo provider
|
|
123
|
+
|
|
124
|
+
3. criação de (RAG)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
16. tool para busca inteligente, o modelo não sabe aonde um metodo ou função em expecifica ou qualquer coisa dentro do de um codigo esta mas ele consegue de forma inteligente so por um trecho do codigo achar
|
|
128
|
+
sem indicação externa
|
|
129
|
+
|
|
130
|
+
17. usar script sheel para comandos fixo do projeto para ter menos codigo com comandos misturados
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview
|
|
3
|
+
* Hexagonal architecture adapters - Concrete implementations of ports
|
|
4
|
+
*
|
|
5
|
+
* Each adapter connects BoxSafe core with a specific Secondary Actor.
|
|
6
|
+
*
|
|
7
|
+
* @module adapters/index
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Secondary Adapters
|
|
11
|
+
export { SystemConfigurationAdapter } from '@adapters/secondary/system/configuration';
|
|
12
|
+
export { FileSystemAdapter } from '@adapters/secondary/filesystem/node-filesystem';
|
|
13
|
+
|
|
14
|
+
// Primary Adapters
|
|
15
|
+
export { CLIAdapter } from '@adapters/primary/cli-adapter';
|
|
16
|
+
|
|
17
|
+
// Factory functions
|
|
18
|
+
export { createSystemConfigurationAdapter } from '@adapters/secondary/system/configuration';
|
|
19
|
+
export { createFileSystemAdapter } from '@adapters/secondary/filesystem/node-filesystem';
|
|
20
|
+
export { createCLIAdapter } from '@adapters/primary/cli-adapter';
|
|
21
|
+
|
|
22
|
+
// Re-export ports for convenience
|
|
23
|
+
export type {
|
|
24
|
+
ISystemConfigurationPort,
|
|
25
|
+
IFileSystemPort,
|
|
26
|
+
ISystemExecutionPort
|
|
27
|
+
} from '../core/ports';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview
|
|
3
|
+
* Primary Adapters - Implement ports for primary actors
|
|
4
|
+
*
|
|
5
|
+
* Connect Primary Actors (CLI, Web, IDE) with system core
|
|
6
|
+
*
|
|
7
|
+
* @module adapters/primary/index
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ISystemExecutionPort } from '@core/ports';
|
|
11
|
+
import { initSegments } from '@core/segments/map';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* CLI Adapter - implements ISystemExecutionPort for command line interface
|
|
15
|
+
*/
|
|
16
|
+
export class CLIAdapter implements ISystemExecutionPort {
|
|
17
|
+
private segments: any;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.init();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private async init() {
|
|
24
|
+
const { runSegment, BSConfig } = await initSegments();
|
|
25
|
+
this.segments = { runSegment, BSConfig };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Execute a specific system segment
|
|
30
|
+
*/
|
|
31
|
+
async executeSegment(segmentName: string, args?: any): Promise<any> {
|
|
32
|
+
if (!this.segments?.runSegment) {
|
|
33
|
+
throw new Error('Segments not initialized');
|
|
34
|
+
}
|
|
35
|
+
return this.segments.runSegment(segmentName, args);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* List all available segments
|
|
40
|
+
*/
|
|
41
|
+
listSegments(): Record<string, any> {
|
|
42
|
+
// TODO: Implement segment listing
|
|
43
|
+
return {
|
|
44
|
+
loop: { description: 'Iterative LLM -> code -> exec loop', implemented: true },
|
|
45
|
+
navigate: { description: 'File system navigation with workspace boundary', implemented: true },
|
|
46
|
+
versionControl: { description: 'Git version control operations', implemented: true }
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Factory function to create CLI adapter
|
|
53
|
+
*/
|
|
54
|
+
export function createCLIAdapter(): CLIAdapter {
|
|
55
|
+
return new CLIAdapter();
|
|
56
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview
|
|
3
|
+
* Adapter for IFileSystemPort
|
|
4
|
+
* Implementation based on existing navigate module
|
|
5
|
+
*
|
|
6
|
+
* @module adapters/secondary/filesystem/node-filesystem
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs/promises';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import type {
|
|
12
|
+
IFileSystemPort,
|
|
13
|
+
NavigatorResult,
|
|
14
|
+
DirectoryListing,
|
|
15
|
+
FileReadResult,
|
|
16
|
+
FileWriteResult,
|
|
17
|
+
DirectoryCreateResult,
|
|
18
|
+
DeleteResult,
|
|
19
|
+
MetadataResult,
|
|
20
|
+
OperationError
|
|
21
|
+
} from '@core/ports';
|
|
22
|
+
import type {
|
|
23
|
+
FileSystemEntry,
|
|
24
|
+
NavigatorConfig
|
|
25
|
+
} from '@core/navigate/types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* File system adapter using Node.js fs module
|
|
29
|
+
*/
|
|
30
|
+
export class FileSystemAdapter implements IFileSystemPort {
|
|
31
|
+
private workspace: string;
|
|
32
|
+
private followSymlinks: boolean;
|
|
33
|
+
private maxFileSize: number;
|
|
34
|
+
|
|
35
|
+
constructor(config: NavigatorConfig) {
|
|
36
|
+
this.workspace = config.workspace;
|
|
37
|
+
this.followSymlinks = config.followSymlinks ?? false;
|
|
38
|
+
this.maxFileSize = config.maxFileSize ?? 10 * 1024 * 1024; // 10MB
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Lista conteúdo de um diretório
|
|
43
|
+
*/
|
|
44
|
+
async listDirectory(dirPath: string): Promise<NavigatorResult> {
|
|
45
|
+
try {
|
|
46
|
+
const resolvedPath = this.resolvePath(dirPath);
|
|
47
|
+
const entries = await fs.readdir(resolvedPath, { withFileTypes: true });
|
|
48
|
+
|
|
49
|
+
const fileSystemEntries: FileSystemEntry[] = [];
|
|
50
|
+
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const fullPath = path.join(resolvedPath, entry.name);
|
|
53
|
+
const relativePath = path.relative(this.workspace, fullPath);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const stats = await fs.stat(fullPath);
|
|
57
|
+
|
|
58
|
+
fileSystemEntries.push({
|
|
59
|
+
path: relativePath,
|
|
60
|
+
name: entry.name,
|
|
61
|
+
type: entry.isDirectory() ? 'directory' : 'file',
|
|
62
|
+
size: entry.isFile() ? stats.size : undefined,
|
|
63
|
+
mtime: stats.mtime.getTime(),
|
|
64
|
+
readable: await this.isReadable(fullPath),
|
|
65
|
+
writable: await this.isWritable(fullPath)
|
|
66
|
+
} as FileSystemEntry);
|
|
67
|
+
} catch {
|
|
68
|
+
// If stat fails, add basic entry
|
|
69
|
+
fileSystemEntries.push({
|
|
70
|
+
path: relativePath,
|
|
71
|
+
name: entry.name,
|
|
72
|
+
type: entry.isDirectory() ? 'directory' : 'file',
|
|
73
|
+
readable: false,
|
|
74
|
+
writable: false
|
|
75
|
+
} as FileSystemEntry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Sort: directories first, then files, both alphabetically
|
|
80
|
+
fileSystemEntries.sort((a, b) => {
|
|
81
|
+
if (a.type !== b.type) {
|
|
82
|
+
return a.type === 'directory' ? -1 : 1;
|
|
83
|
+
}
|
|
84
|
+
return a.name.localeCompare(b.name);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const result: DirectoryListing = {
|
|
88
|
+
ok: true,
|
|
89
|
+
path: dirPath,
|
|
90
|
+
entries: fileSystemEntries,
|
|
91
|
+
total: fileSystemEntries.length
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return this.createOperationError('listDirectory', error as Error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Lê conteúdo de um arquivo
|
|
102
|
+
*/
|
|
103
|
+
async readFile(filePath: string): Promise<NavigatorResult> {
|
|
104
|
+
try {
|
|
105
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
106
|
+
|
|
107
|
+
// Verificar tamanho do arquivo
|
|
108
|
+
const stats = await fs.stat(resolvedPath);
|
|
109
|
+
if (stats.size > this.maxFileSize) {
|
|
110
|
+
throw new Error(`File too large: ${stats.size} bytes (max: ${this.maxFileSize})`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
114
|
+
|
|
115
|
+
const result: FileReadResult = {
|
|
116
|
+
ok: true,
|
|
117
|
+
path: filePath,
|
|
118
|
+
content,
|
|
119
|
+
size: stats.size,
|
|
120
|
+
encoding: 'utf-8'
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return result;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return this.createOperationError('readFile', error as Error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Write content to a file
|
|
131
|
+
*/
|
|
132
|
+
async writeFile(filePath: string, content: string, options?: { append?: boolean; createDirs?: boolean }): Promise<NavigatorResult> {
|
|
133
|
+
try {
|
|
134
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
135
|
+
const dir = path.dirname(resolvedPath);
|
|
136
|
+
|
|
137
|
+
// Create directories if necessary
|
|
138
|
+
if (options?.createDirs) {
|
|
139
|
+
await fs.mkdir(dir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check if directory exists
|
|
143
|
+
try {
|
|
144
|
+
await fs.access(dir);
|
|
145
|
+
} catch {
|
|
146
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Write file
|
|
150
|
+
if (options?.append) {
|
|
151
|
+
await fs.appendFile(resolvedPath, content, 'utf-8');
|
|
152
|
+
} else {
|
|
153
|
+
await fs.writeFile(resolvedPath, content, 'utf-8');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const stats = await fs.stat(resolvedPath);
|
|
157
|
+
const created = !options?.append;
|
|
158
|
+
|
|
159
|
+
const result: FileWriteResult = {
|
|
160
|
+
ok: true,
|
|
161
|
+
path: filePath,
|
|
162
|
+
size: stats.size,
|
|
163
|
+
created
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return this.createOperationError('writeFile', error as Error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Create a directory
|
|
174
|
+
*/
|
|
175
|
+
async createDirectory(dirPath: string, options?: { recursive?: boolean }): Promise<NavigatorResult> {
|
|
176
|
+
try {
|
|
177
|
+
const resolvedPath = this.resolvePath(dirPath);
|
|
178
|
+
|
|
179
|
+
await fs.mkdir(resolvedPath, { recursive: options?.recursive ?? false });
|
|
180
|
+
|
|
181
|
+
const stats = await fs.stat(resolvedPath);
|
|
182
|
+
const created = true; // mkdir always creates or throws error
|
|
183
|
+
|
|
184
|
+
const result: DirectoryCreateResult = {
|
|
185
|
+
ok: true,
|
|
186
|
+
path: dirPath,
|
|
187
|
+
created
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return result;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return this.createOperationError('createDirectory', error as Error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Remove arquivo ou diretório
|
|
198
|
+
*/
|
|
199
|
+
async delete(targetPath: string, options?: { recursive?: boolean }): Promise<NavigatorResult> {
|
|
200
|
+
try {
|
|
201
|
+
const resolvedPath = this.resolvePath(targetPath);
|
|
202
|
+
const stats = await fs.stat(resolvedPath);
|
|
203
|
+
const type = stats.isDirectory() ? 'directory' : 'file';
|
|
204
|
+
|
|
205
|
+
if (type === 'directory' && options?.recursive) {
|
|
206
|
+
await fs.rm(resolvedPath, { recursive: true, force: true });
|
|
207
|
+
} else if (type === 'directory') {
|
|
208
|
+
await fs.rmdir(resolvedPath);
|
|
209
|
+
} else {
|
|
210
|
+
await fs.unlink(resolvedPath);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const result: DeleteResult = {
|
|
214
|
+
ok: true,
|
|
215
|
+
path: targetPath,
|
|
216
|
+
type,
|
|
217
|
+
deletedAt: Date.now()
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
return result;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return this.createOperationError('delete', error as Error);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Obtém metadados de um arquivo/diretório
|
|
228
|
+
*/
|
|
229
|
+
async getMetadata(targetPath: string): Promise<NavigatorResult> {
|
|
230
|
+
try {
|
|
231
|
+
const resolvedPath = this.resolvePath(targetPath);
|
|
232
|
+
const stats = await fs.stat(resolvedPath);
|
|
233
|
+
|
|
234
|
+
const result: MetadataResult = {
|
|
235
|
+
ok: true,
|
|
236
|
+
path: targetPath,
|
|
237
|
+
stat: {
|
|
238
|
+
type: stats.isDirectory() ? 'directory' : 'file',
|
|
239
|
+
size: stats.size,
|
|
240
|
+
mtime: stats.mtime.getTime(),
|
|
241
|
+
isReadable: await this.isReadable(resolvedPath),
|
|
242
|
+
isWritable: await this.isWritable(resolvedPath)
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return this.createOperationError('getMetadata', error as Error);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Helper to resolve path within workspace
|
|
254
|
+
*/
|
|
255
|
+
private resolvePath(targetPath: string): string {
|
|
256
|
+
const resolved = path.resolve(this.workspace, targetPath);
|
|
257
|
+
|
|
258
|
+
// Check if inside workspace (security)
|
|
259
|
+
if (!resolved.startsWith(path.resolve(this.workspace))) {
|
|
260
|
+
throw new Error('Path outside workspace is not allowed');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return resolved;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Helper to check if path is readable
|
|
268
|
+
*/
|
|
269
|
+
private async isReadable(filePath: string): Promise<boolean> {
|
|
270
|
+
try {
|
|
271
|
+
await fs.access(filePath, fs.constants.R_OK);
|
|
272
|
+
return true;
|
|
273
|
+
} catch {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Helper to check if path is writable
|
|
280
|
+
*/
|
|
281
|
+
private async isWritable(filePath: string): Promise<boolean> {
|
|
282
|
+
try {
|
|
283
|
+
await fs.access(filePath, fs.constants.W_OK);
|
|
284
|
+
return true;
|
|
285
|
+
} catch {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Helper to create OperationError
|
|
292
|
+
*/
|
|
293
|
+
private createOperationError(operation: string, error: Error): OperationError {
|
|
294
|
+
return {
|
|
295
|
+
ok: false,
|
|
296
|
+
operation,
|
|
297
|
+
error: error.message
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Factory function para criar o adapter
|
|
304
|
+
*/
|
|
305
|
+
export function createFileSystemAdapter(config: NavigatorConfig): FileSystemAdapter {
|
|
306
|
+
return new FileSystemAdapter(config);
|
|
307
|
+
}
|