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
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { remark } from 'remark';
|
|
2
|
+
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { Logger } from '@util/logger';
|
|
4
|
+
|
|
5
|
+
const logger = Logger.createModuleLogger('ExtractCode');
|
|
6
|
+
|
|
7
|
+
/* -------------------------------------------------------------------------- */
|
|
8
|
+
/* Types */
|
|
9
|
+
/* -------------------------------------------------------------------------- */
|
|
10
|
+
|
|
11
|
+
interface ExtractCodeOptions {
|
|
12
|
+
/**
|
|
13
|
+
* If true, throws error when code is not found.
|
|
14
|
+
* If false, returns guidance prompt.
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
throwOnNotFound?: boolean;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Custom message when code is not found
|
|
21
|
+
*/
|
|
22
|
+
customNotFoundMessage?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* -------------------------------------------------------------------------- */
|
|
26
|
+
/* Configuration */
|
|
27
|
+
/* -------------------------------------------------------------------------- */
|
|
28
|
+
|
|
29
|
+
const LANG_ALIASES: Record<string, string[]> = {
|
|
30
|
+
javascript: ['javascript', 'js'],
|
|
31
|
+
typescript: ['typescript', 'ts'],
|
|
32
|
+
python: ['python', 'py'],
|
|
33
|
+
bash: ['bash', 'sh', 'shell'],
|
|
34
|
+
rust: ['rust', 'rs'],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/* -------------------------------------------------------------------------- */
|
|
38
|
+
/* Helpers */
|
|
39
|
+
/* -------------------------------------------------------------------------- */
|
|
40
|
+
|
|
41
|
+
function normalizeLang(lang?: string): string | undefined {
|
|
42
|
+
return lang?.toLowerCase().trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function matchesLanguage(target: string, nodeLang?: string): boolean {
|
|
46
|
+
if (!nodeLang) return false;
|
|
47
|
+
|
|
48
|
+
const normalizedNodeLang = normalizeLang(nodeLang);
|
|
49
|
+
|
|
50
|
+
if (normalizedNodeLang === target) return true;
|
|
51
|
+
|
|
52
|
+
return Object.values(LANG_ALIASES).some(
|
|
53
|
+
(aliases) =>
|
|
54
|
+
aliases.includes(target) && aliases.includes(normalizedNodeLang!)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Heuristic detection of language from a code snippet when no fence language is provided.
|
|
60
|
+
* Returns true when the snippet contains tokens commonly associated with `target`.
|
|
61
|
+
*/
|
|
62
|
+
function detectByContent(target: string, code: string): boolean {
|
|
63
|
+
const c = code || '';
|
|
64
|
+
switch (target) {
|
|
65
|
+
case 'typescript':
|
|
66
|
+
case 'ts':
|
|
67
|
+
case 'javascript':
|
|
68
|
+
case 'js':
|
|
69
|
+
return /\b(import|export|const|let|interface|type|from|require\(|console\.log)\b/.test(c);
|
|
70
|
+
case 'python':
|
|
71
|
+
case 'py':
|
|
72
|
+
return /\b(def |import |from |print\()/.test(c);
|
|
73
|
+
case 'bash':
|
|
74
|
+
case 'sh':
|
|
75
|
+
case 'shell':
|
|
76
|
+
return /(^#!\/bin\/bash)|(^#!\/usr\/bin\/env bash)|\becho\b|\bcd\b|\bmkdir\b/.test(c);
|
|
77
|
+
case 'rust':
|
|
78
|
+
case 'rs':
|
|
79
|
+
return /\b(fn |let |use |extern crate)\b/.test(c);
|
|
80
|
+
default:
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generates guidance prompt when code is not found
|
|
87
|
+
*/
|
|
88
|
+
function generateNotFoundPrompt(lang: string): string {
|
|
89
|
+
return `ERROR: ${lang} code was not found in the response.
|
|
90
|
+
|
|
91
|
+
MANDATORY INSTRUCTIONS:
|
|
92
|
+
1. Generate ONLY the requested ${lang} code
|
|
93
|
+
2. Use the correct code block format: \`\`\`${lang}
|
|
94
|
+
3. DO NOT include explanations, descriptions, or additional text
|
|
95
|
+
4. DO NOT generate multiple code blocks
|
|
96
|
+
5. The code will be executed directly - it will not be read by humans
|
|
97
|
+
6. Do not use markdown for anything other than the code block
|
|
98
|
+
7. Return ONLY a single functional code block
|
|
99
|
+
|
|
100
|
+
Correct format example:
|
|
101
|
+
\`\`\`${lang}
|
|
102
|
+
// your code here
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
Generate the ${lang} code now following these instructions.`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* -------------------------------------------------------------------------- */
|
|
109
|
+
/* Public */
|
|
110
|
+
/* -------------------------------------------------------------------------- */
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Extracts code blocks of a specific language from Markdown.
|
|
114
|
+
*
|
|
115
|
+
* @param md - Markdown string (usually LLM response)
|
|
116
|
+
* @param lang - Code language (javascript, python, etc)
|
|
117
|
+
* @param options - Configuration options
|
|
118
|
+
* @returns Array of found code blocks or error prompt
|
|
119
|
+
*/
|
|
120
|
+
export const extractCode = async (
|
|
121
|
+
md: string,
|
|
122
|
+
lang: string,
|
|
123
|
+
options: ExtractCodeOptions = {}
|
|
124
|
+
): Promise<string[]> => {
|
|
125
|
+
const { throwOnNotFound = false, customNotFoundMessage } = options;
|
|
126
|
+
|
|
127
|
+
if (!md || typeof md !== 'string') {
|
|
128
|
+
const errorMsg = 'Invalid markdown input: expected non-empty string';
|
|
129
|
+
logger.error(errorMsg);
|
|
130
|
+
throw new Error(errorMsg);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!lang || typeof lang !== 'string') {
|
|
134
|
+
const errorMsg = 'Invalid language: expected non-empty string';
|
|
135
|
+
logger.error(errorMsg);
|
|
136
|
+
throw new Error(errorMsg);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const codeBlocks: string[] = [];
|
|
140
|
+
const normalizedLang = normalizeLang(lang)!;
|
|
141
|
+
|
|
142
|
+
const onVisitCode = (node: any) => {
|
|
143
|
+
if (!node?.value) return;
|
|
144
|
+
|
|
145
|
+
// Exact match by explicit fence language
|
|
146
|
+
if (matchesLanguage(normalizedLang, node.lang)) {
|
|
147
|
+
codeBlocks.push(node.value.trim());
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Fallback: if the fence had no language, try to heuristically detect
|
|
152
|
+
// the requested language from the code content itself.
|
|
153
|
+
if (!node.lang && detectByContent(normalizedLang, node.value)) {
|
|
154
|
+
codeBlocks.push(node.value.trim());
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
await remark()
|
|
161
|
+
.use(() => (tree) => {
|
|
162
|
+
visit(tree, 'code', onVisitCode);
|
|
163
|
+
})
|
|
164
|
+
.process(md);
|
|
165
|
+
|
|
166
|
+
if (codeBlocks.length === 0) {
|
|
167
|
+
if (throwOnNotFound) {
|
|
168
|
+
const errorMsg = `No ${lang} code block found in markdown`;
|
|
169
|
+
logger.error(errorMsg);
|
|
170
|
+
throw new Error(errorMsg);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Fallback: try to extract unlabeled fenced blocks or raw code from the
|
|
174
|
+
// entire markdown string when no explicit code nodes were found.
|
|
175
|
+
// 1) unlabeled/any fenced blocks via regex
|
|
176
|
+
const fenceRegex = /```(?:\w+)?\n([\s\S]*?)\n```/g;
|
|
177
|
+
let m: RegExpExecArray | null;
|
|
178
|
+
while ((m = fenceRegex.exec(md)) !== null) {
|
|
179
|
+
if (!m || !m[1]) continue;
|
|
180
|
+
const candidate = m[1].trim();
|
|
181
|
+
if (candidate && (detectByContent(normalizedLang, candidate) || normalizedLang === 'ts' || normalizedLang === 'js')) {
|
|
182
|
+
codeBlocks.push(candidate);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 2) If still nothing, heuristically treat the whole markdown as code
|
|
187
|
+
// when it clearly contains tokens for the requested language.
|
|
188
|
+
if (codeBlocks.length === 0 && detectByContent(normalizedLang, md)) {
|
|
189
|
+
// require at least two lines to avoid grabbing short messages
|
|
190
|
+
if ((md.match(/\n/g) || []).length >= 1) {
|
|
191
|
+
codeBlocks.push(md.trim());
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (codeBlocks.length === 0) {
|
|
196
|
+
if (throwOnNotFound) {
|
|
197
|
+
const errorMsg = `No ${lang} code block found in markdown`;
|
|
198
|
+
logger.error(errorMsg);
|
|
199
|
+
throw new Error(errorMsg);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return [customNotFoundMessage ?? generateNotFoundPrompt(lang)];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return codeBlocks;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const errorMsg = `Failed to parse markdown: ${error}`;
|
|
209
|
+
logger.error(errorMsg);
|
|
210
|
+
|
|
211
|
+
throw new Error(
|
|
212
|
+
`Failed to extract code: ${
|
|
213
|
+
error instanceof Error ? error.message : 'unknown error'
|
|
214
|
+
}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { remark } from 'remark';
|
|
2
|
+
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { Logger } from '@core/util/logger';
|
|
4
|
+
import type { ToolCallParseResult, ToolCallParseError } from '@core/loop/toolCalls';
|
|
5
|
+
import { parseOneToolCall, isParseError } from '@core/loop/toolCalls';
|
|
6
|
+
|
|
7
|
+
const logger = Logger.createModuleLogger('ExtractToolCalls');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extracts json-tool blocks from Markdown using remark (same approach as extractCode)
|
|
11
|
+
*
|
|
12
|
+
* @param md - Markdown string (usually LLM response)
|
|
13
|
+
* @returns ToolCallParseResult with parsed calls and errors
|
|
14
|
+
*/
|
|
15
|
+
export const extractToolCalls = async (md: string): Promise<ToolCallParseResult> => {
|
|
16
|
+
const calls: any[] = [];
|
|
17
|
+
const errors: ToolCallParseError[] = [];
|
|
18
|
+
|
|
19
|
+
if (!md || typeof md !== 'string') {
|
|
20
|
+
const errorMsg = 'Invalid markdown input: expected non-empty string';
|
|
21
|
+
logger.error(errorMsg);
|
|
22
|
+
throw new Error(errorMsg);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
logger.debug(`Processing markdown of length ${md.length}`);
|
|
26
|
+
|
|
27
|
+
const onVisitCode = (node: any) => {
|
|
28
|
+
if (!node?.value) return;
|
|
29
|
+
|
|
30
|
+
// Only process json-tool blocks
|
|
31
|
+
if (node.lang === 'json-tool') {
|
|
32
|
+
logger.debug(`Found json-tool block: ${node.value.substring(0, 100)}...`);
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const jsonContent = node.value.trim();
|
|
36
|
+
const parsed = JSON.parse(jsonContent);
|
|
37
|
+
const toolCall = parseOneToolCall(parsed);
|
|
38
|
+
|
|
39
|
+
if (isParseError(toolCall)) {
|
|
40
|
+
logger.warn(`Invalid tool call: ${toolCall.error}`);
|
|
41
|
+
errors.push({ ...toolCall, fence: jsonContent });
|
|
42
|
+
} else {
|
|
43
|
+
logger.info(`Valid tool call detected: ${toolCall.tool}`);
|
|
44
|
+
calls.push(toolCall);
|
|
45
|
+
}
|
|
46
|
+
} catch (e: any) {
|
|
47
|
+
logger.error(`JSON parse error: ${e?.message ?? String(e)}`);
|
|
48
|
+
errors.push({
|
|
49
|
+
ok: false,
|
|
50
|
+
error: `invalid JSON: ${e?.message ?? String(e)}`,
|
|
51
|
+
fence: node.value
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await remark()
|
|
59
|
+
.use(() => (tree) => {
|
|
60
|
+
visit(tree, 'code', onVisitCode);
|
|
61
|
+
})
|
|
62
|
+
.process(md);
|
|
63
|
+
|
|
64
|
+
logger.info(`Extracted ${calls.length} tool calls, ${errors.length} errors`);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
calls,
|
|
69
|
+
errors
|
|
70
|
+
};
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const errorMsg = `Failed to parse markdown for tool calls: ${error}`;
|
|
73
|
+
logger.error(errorMsg);
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Failed to extract tool calls: ${
|
|
76
|
+
error instanceof Error ? error.message : 'unknown error'
|
|
77
|
+
}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
};
|
package/util/logger.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export enum LogLevel {
|
|
2
|
+
DEBUG = 0,
|
|
3
|
+
INFO = 1,
|
|
4
|
+
WARN = 2,
|
|
5
|
+
ERROR = 3,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface LogEntry {
|
|
9
|
+
level: LogLevel;
|
|
10
|
+
message: string;
|
|
11
|
+
module: string;
|
|
12
|
+
timestamp: Date;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class Logger {
|
|
16
|
+
private static instance: Logger;
|
|
17
|
+
private currentLevel: LogLevel;
|
|
18
|
+
private moduleName: string;
|
|
19
|
+
|
|
20
|
+
// ANSI color codes - simplified for better readability
|
|
21
|
+
private static readonly COLORS = {
|
|
22
|
+
RESET: '\x1b[0m',
|
|
23
|
+
DEBUG: '\x1b[36m', // Cyan
|
|
24
|
+
INFO: '\x1b[32m', // Green
|
|
25
|
+
WARN: '\x1b[33m', // Yellow
|
|
26
|
+
ERROR: '\x1b[31m', // Red
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
private static readonly LEVEL_NAMES = {
|
|
30
|
+
[LogLevel.DEBUG]: 'DEBUG',
|
|
31
|
+
[LogLevel.INFO]: 'INFO',
|
|
32
|
+
[LogLevel.WARN]: 'WARN',
|
|
33
|
+
[LogLevel.ERROR]: 'ERROR',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
constructor(moduleName: string = 'root', level: LogLevel = LogLevel.INFO) {
|
|
37
|
+
this.moduleName = moduleName;
|
|
38
|
+
this.currentLevel = level;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static getInstance(moduleName?: string, level?: LogLevel): Logger {
|
|
42
|
+
if (!Logger.instance) {
|
|
43
|
+
Logger.instance = new Logger(moduleName || 'root', level);
|
|
44
|
+
}
|
|
45
|
+
return Logger.instance;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setLevel(level: LogLevel): void {
|
|
49
|
+
this.currentLevel = level;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getLevel(): LogLevel {
|
|
53
|
+
return this.currentLevel;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private formatMessage(level: LogLevel, message: string): string {
|
|
57
|
+
const levelName = Logger.LEVEL_NAMES[level];
|
|
58
|
+
const color = Logger.COLORS[levelName as keyof typeof Logger.COLORS];
|
|
59
|
+
const reset = Logger.COLORS.RESET;
|
|
60
|
+
|
|
61
|
+
return `${color}[${levelName}(${this.moduleName})]${reset} ${message}\n\n`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private log(level: LogLevel, message: string): void {
|
|
65
|
+
if (level < this.currentLevel) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const formattedMessage = this.formatMessage(level, message);
|
|
70
|
+
process.stdout.write(formattedMessage);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
debug(message: string): void {
|
|
74
|
+
this.log(LogLevel.DEBUG, message);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
info(message: string): void {
|
|
78
|
+
this.log(LogLevel.INFO, message);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
warn(message: string): void {
|
|
82
|
+
this.log(LogLevel.WARN, message);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
error(message: string): void {
|
|
86
|
+
this.log(LogLevel.ERROR, message);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Static methods for quick access
|
|
90
|
+
static debug(message: string, moduleName?: string): void {
|
|
91
|
+
const logger = new Logger(moduleName || 'root');
|
|
92
|
+
logger.debug(message);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static info(message: string, moduleName?: string): void {
|
|
96
|
+
const logger = new Logger(moduleName || 'root');
|
|
97
|
+
logger.info(message);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static warn(message: string, moduleName?: string): void {
|
|
101
|
+
const logger = new Logger(moduleName || 'root');
|
|
102
|
+
logger.warn(message);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static error(message: string, moduleName?: string): void {
|
|
106
|
+
const logger = new Logger(moduleName || 'root');
|
|
107
|
+
logger.error(message);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Method to create a logger instance for a specific module
|
|
111
|
+
static createModuleLogger(moduleName: string, level?: LogLevel): Logger {
|
|
112
|
+
return new Logger(moduleName, level);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Export a default logger instance
|
|
117
|
+
export const logger = Logger.getInstance();
|
|
118
|
+
|
|
119
|
+
// Export convenience functions
|
|
120
|
+
export const log = {
|
|
121
|
+
debug: (message: string, moduleName?: string) => Logger.debug(message, moduleName),
|
|
122
|
+
info: (message: string, moduleName?: string) => Logger.info(message, moduleName),
|
|
123
|
+
warn: (message: string, moduleName?: string) => Logger.warn(message, moduleName),
|
|
124
|
+
error: (message: string, moduleName?: string) => Logger.error(message, moduleName),
|
|
125
|
+
};
|