drtrace 0.2.0 ā 0.4.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/README.md +74 -4
- package/agents/CONTRIBUTING.md +296 -0
- package/agents/README.md +174 -0
- package/agents/daemon-method-selection.md +370 -0
- package/agents/integration-guides/cpp-best-practices.md +218 -0
- package/agents/integration-guides/cpp-ros-integration.md +88 -0
- package/agents/log-analysis.md +218 -0
- package/agents/log-help.md +226 -0
- package/agents/log-init.md +933 -0
- package/agents/log-it.md +1126 -0
- package/bin/init.js +4 -4
- package/dist/bin/init.js +31 -0
- package/dist/browser.d.ts +28 -0
- package/dist/browser.js +91 -0
- package/dist/config-schema.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/init.d.ts +44 -2
- package/dist/init.js +460 -30
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +30 -4
- package/dist/node.d.ts +13 -0
- package/dist/node.js +67 -0
- package/dist/resources/agents/CONTRIBUTING.md +296 -0
- package/dist/resources/agents/README.md +174 -0
- package/dist/resources/agents/daemon-method-selection.md +370 -0
- package/dist/resources/agents/integration-guides/cpp-best-practices.md +218 -0
- package/dist/resources/agents/integration-guides/cpp-ros-integration.md +88 -0
- package/dist/resources/agents/log-analysis.md +218 -0
- package/dist/resources/agents/log-help.md +226 -0
- package/dist/resources/agents/log-init.md +933 -0
- package/dist/resources/agents/log-it.md +1126 -0
- package/dist/resources/cpp/drtrace_sink.hpp +1249 -0
- package/dist/transport.js +5 -1
- package/dist/types.d.ts +8 -2
- package/package.json +28 -4
- package/.eslintrc.js +0 -20
- package/jest.config.js +0 -11
- package/src/client.ts +0 -68
- package/src/config-schema.ts +0 -115
- package/src/config.ts +0 -326
- package/src/index.ts +0 -3
- package/src/init.ts +0 -451
- package/src/logger.ts +0 -56
- package/src/queue.ts +0 -105
- package/src/transport.ts +0 -60
- package/src/types.ts +0 -20
- package/tests/client.test.ts +0 -66
- package/tests/config-schema.test.ts +0 -198
- package/tests/config.test.ts +0 -456
- package/tests/queue.test.ts +0 -72
- package/tests/transport.test.ts +0 -52
- package/tsconfig.json +0 -18
package/src/init.ts
DELETED
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive project initialization for DrTrace (JavaScript/TypeScript)
|
|
3
|
-
* Mirrors Python init_project.py with interactive prompts via prompts library
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as fs from "fs";
|
|
7
|
-
import * as path from "path";
|
|
8
|
-
import prompts from "prompts";
|
|
9
|
-
import { ConfigSchema, DrTraceConfig } from "./config-schema";
|
|
10
|
-
|
|
11
|
-
export interface InitOptions {
|
|
12
|
-
projectRoot?: string;
|
|
13
|
-
nonInteractive?: boolean;
|
|
14
|
-
config?: Partial<DrTraceConfig>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class ProjectInitializer {
|
|
18
|
-
private projectRoot: string;
|
|
19
|
-
private drtraceDir: string;
|
|
20
|
-
private configPath: string;
|
|
21
|
-
|
|
22
|
-
constructor(projectRoot: string = process.cwd()) {
|
|
23
|
-
this.projectRoot = projectRoot;
|
|
24
|
-
this.drtraceDir = path.join(projectRoot, "_drtrace");
|
|
25
|
-
this.configPath = path.join(this.drtraceDir, "config.json");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Run the interactive initialization workflow
|
|
30
|
-
*/
|
|
31
|
-
async runInteractive(): Promise<boolean> {
|
|
32
|
-
console.log("\nš DrTrace Project Initialization\n");
|
|
33
|
-
console.log("=".repeat(50));
|
|
34
|
-
|
|
35
|
-
// Collect project information
|
|
36
|
-
console.log("\nš Project Information:");
|
|
37
|
-
const projectName = await this.promptText(
|
|
38
|
-
"Project name",
|
|
39
|
-
"my-app"
|
|
40
|
-
);
|
|
41
|
-
const applicationId = await this.promptText(
|
|
42
|
-
"Application ID",
|
|
43
|
-
projectName.toLowerCase().replace(/\s+/g, "-")
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
// Language selection
|
|
47
|
-
console.log("\nš§ Technology Stack:");
|
|
48
|
-
const language = await this.promptChoice(
|
|
49
|
-
"Select language/runtime:",
|
|
50
|
-
["python", "javascript", "both"],
|
|
51
|
-
"javascript"
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
// Daemon configuration
|
|
55
|
-
console.log("\nš” DrTrace Daemon Configuration:");
|
|
56
|
-
const daemonUrl = await this.promptText(
|
|
57
|
-
"Daemon URL",
|
|
58
|
-
"http://localhost:8001"
|
|
59
|
-
);
|
|
60
|
-
const enabled = await this.promptYesNo("Enable DrTrace by default?", true);
|
|
61
|
-
|
|
62
|
-
// Environment selection
|
|
63
|
-
console.log("\nš Environments:");
|
|
64
|
-
const selectedEnvs = await this.promptMultiSelect(
|
|
65
|
-
"Which environments to configure?",
|
|
66
|
-
["development", "staging", "production", "ci"]
|
|
67
|
-
);
|
|
68
|
-
const environments = selectedEnvs.length > 0 ? selectedEnvs : ["development"];
|
|
69
|
-
|
|
70
|
-
// Agent configuration
|
|
71
|
-
console.log("\nš¤ Agent Integration (Optional):");
|
|
72
|
-
const agentEnabled = await this.promptYesNo(
|
|
73
|
-
"Enable agent interface?",
|
|
74
|
-
false
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
let agentFramework = "bmad";
|
|
78
|
-
if (agentEnabled) {
|
|
79
|
-
agentFramework = await this.promptChoice(
|
|
80
|
-
"Select agent framework:",
|
|
81
|
-
["bmad", "langchain", "other"],
|
|
82
|
-
"bmad"
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Check for existing config
|
|
87
|
-
if (!this.handleExistingConfig()) {
|
|
88
|
-
console.log("\nā Initialization cancelled.");
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Create directory structure
|
|
93
|
-
this.createDirectoryStructure();
|
|
94
|
-
|
|
95
|
-
// Generate and save main config
|
|
96
|
-
const config = ConfigSchema.getDefaultConfig({
|
|
97
|
-
project_name: projectName,
|
|
98
|
-
application_id: applicationId,
|
|
99
|
-
language: language as "python" | "javascript" | "both",
|
|
100
|
-
daemon_url: daemonUrl,
|
|
101
|
-
enabled,
|
|
102
|
-
environments,
|
|
103
|
-
agent_enabled: agentEnabled,
|
|
104
|
-
agent_framework: agentFramework as "bmad" | "langchain" | "other",
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
ConfigSchema.save(config, this.configPath);
|
|
108
|
-
console.log(`\nā Main config created: ${this.configPath}`);
|
|
109
|
-
|
|
110
|
-
// Generate environment-specific configs
|
|
111
|
-
this.generateEnvironmentConfigs(config);
|
|
112
|
-
|
|
113
|
-
// Copy default agent spec
|
|
114
|
-
if (agentEnabled) {
|
|
115
|
-
this.copyAgentSpec();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Generate .env.example
|
|
119
|
-
this.generateEnvExample(config);
|
|
120
|
-
|
|
121
|
-
// Generate README
|
|
122
|
-
this.generateReadme();
|
|
123
|
-
|
|
124
|
-
// Summary and next steps
|
|
125
|
-
this.printSummary(config);
|
|
126
|
-
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Prompt for text input
|
|
132
|
-
*/
|
|
133
|
-
private async promptText(
|
|
134
|
-
question: string,
|
|
135
|
-
defaultValue?: string
|
|
136
|
-
): Promise<string> {
|
|
137
|
-
const response = await prompts({
|
|
138
|
-
type: "text",
|
|
139
|
-
name: "value",
|
|
140
|
-
message: question,
|
|
141
|
-
initial: defaultValue,
|
|
142
|
-
});
|
|
143
|
-
return response.value || defaultValue || "";
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Prompt for yes/no
|
|
148
|
-
*/
|
|
149
|
-
private async promptYesNo(
|
|
150
|
-
question: string,
|
|
151
|
-
defaultValue: boolean = true
|
|
152
|
-
): Promise<boolean> {
|
|
153
|
-
const response = await prompts({
|
|
154
|
-
type: "confirm",
|
|
155
|
-
name: "value",
|
|
156
|
-
message: question,
|
|
157
|
-
initial: defaultValue,
|
|
158
|
-
});
|
|
159
|
-
return response.value;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Prompt for single choice
|
|
164
|
-
*/
|
|
165
|
-
private async promptChoice(
|
|
166
|
-
question: string,
|
|
167
|
-
choices: string[],
|
|
168
|
-
defaultValue?: string
|
|
169
|
-
): Promise<string> {
|
|
170
|
-
const response = await prompts({
|
|
171
|
-
type: "select",
|
|
172
|
-
name: "value",
|
|
173
|
-
message: question,
|
|
174
|
-
choices: choices.map((c) => ({ title: c, value: c })),
|
|
175
|
-
initial: defaultValue ? choices.indexOf(defaultValue) : 0,
|
|
176
|
-
});
|
|
177
|
-
return response.value;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Prompt for multiple selections
|
|
182
|
-
*/
|
|
183
|
-
private async promptMultiSelect(
|
|
184
|
-
question: string,
|
|
185
|
-
choices: string[]
|
|
186
|
-
): Promise<string[]> {
|
|
187
|
-
const response = await prompts({
|
|
188
|
-
type: "multiselect",
|
|
189
|
-
name: "value",
|
|
190
|
-
message: question,
|
|
191
|
-
choices: choices.map((c) => ({ title: c, value: c })),
|
|
192
|
-
initial: 0, // Development selected by default
|
|
193
|
-
});
|
|
194
|
-
return response.value || [];
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Handle existing config
|
|
199
|
-
*/
|
|
200
|
-
private handleExistingConfig(): boolean {
|
|
201
|
-
if (!fs.existsSync(this.configPath)) {
|
|
202
|
-
return true;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
console.log(
|
|
206
|
-
`\nā ļø Configuration already exists at ${this.configPath}`
|
|
207
|
-
);
|
|
208
|
-
// In interactive mode, we would prompt here
|
|
209
|
-
// For now, we'll require confirmation via prompts
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Create _drtrace directory structure
|
|
215
|
-
*/
|
|
216
|
-
private createDirectoryStructure(): void {
|
|
217
|
-
const agentsDir = path.join(this.drtraceDir, "agents");
|
|
218
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
219
|
-
console.log(`ā Created directory: ${this.drtraceDir}`);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Generate environment-specific configs
|
|
224
|
-
*/
|
|
225
|
-
private generateEnvironmentConfigs(baseConfig: DrTraceConfig): void {
|
|
226
|
-
const environments = baseConfig.environments || ["development"];
|
|
227
|
-
|
|
228
|
-
for (const env of environments) {
|
|
229
|
-
const envConfig = { ...baseConfig };
|
|
230
|
-
const envConfigPath = path.join(this.drtraceDir, `config.${env}.json`);
|
|
231
|
-
ConfigSchema.save(envConfig, envConfigPath);
|
|
232
|
-
console.log(`ā Generated: ${envConfigPath}`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Copy agent spec to _drtrace/agents/
|
|
238
|
-
*/
|
|
239
|
-
private copyAgentSpec(): void {
|
|
240
|
-
try {
|
|
241
|
-
const agentPath = path.join(
|
|
242
|
-
this.drtraceDir,
|
|
243
|
-
"agents",
|
|
244
|
-
"log-analysis.md"
|
|
245
|
-
);
|
|
246
|
-
const defaultSpec = this.getDefaultAgentSpec();
|
|
247
|
-
fs.writeFileSync(agentPath, defaultSpec);
|
|
248
|
-
console.log(`ā Copied agent spec: ${agentPath}`);
|
|
249
|
-
} catch (error) {
|
|
250
|
-
console.warn(`ā ļø Could not copy agent spec: ${error}`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get default agent spec from shared agents/ or bundled resources
|
|
256
|
-
*
|
|
257
|
-
* Search order:
|
|
258
|
-
* 1. Root repo agents/ directory (development)
|
|
259
|
-
* 2. Bundled default agent from node_modules
|
|
260
|
-
*/
|
|
261
|
-
private getDefaultAgentSpec(): string {
|
|
262
|
-
// Try root agents/ first (development mode)
|
|
263
|
-
const rootAgentPath = path.join(
|
|
264
|
-
this.projectRoot,
|
|
265
|
-
"..",
|
|
266
|
-
"..",
|
|
267
|
-
"agents",
|
|
268
|
-
"log-analysis.default.md"
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
try {
|
|
272
|
-
if (fs.existsSync(rootAgentPath)) {
|
|
273
|
-
return fs.readFileSync(rootAgentPath, "utf-8");
|
|
274
|
-
}
|
|
275
|
-
} catch (error) {
|
|
276
|
-
// Fall through to bundled resource
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Fallback to bundled default agent
|
|
280
|
-
return this.getBundledAgentSpec();
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Get bundled agent spec (fallback when root agents/ not available)
|
|
285
|
-
*/
|
|
286
|
-
private getBundledAgentSpec(): string {
|
|
287
|
-
return `---
|
|
288
|
-
name: "log-analysis"
|
|
289
|
-
description: "Log Analysis Agent"
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
|
|
293
|
-
|
|
294
|
-
\`\`\`xml
|
|
295
|
-
<agent id="log-analysis.agent.yaml" name="drtrace" title="Log Analysis Agent" icon="š">
|
|
296
|
-
<activation critical="MANDATORY">
|
|
297
|
-
<step n="1">Load persona from this current agent file (already in context)</step>
|
|
298
|
-
<step n="2">Remember: You are a Log Analysis Specialist</step>
|
|
299
|
-
<step n="3">Show greeting, then display numbered list of menu items</step>
|
|
300
|
-
<step n="4">STOP and WAIT for user input</step>
|
|
301
|
-
<step n="5">On user input: Process as natural language query</step>
|
|
302
|
-
</activation>
|
|
303
|
-
|
|
304
|
-
<persona>
|
|
305
|
-
<role>Log Analysis Specialist</role>
|
|
306
|
-
<identity>Expert at analyzing application logs and identifying root causes of errors</identity>
|
|
307
|
-
<communication_style>Clear and concise. Provides structured markdown responses.</communication_style>
|
|
308
|
-
</persona>
|
|
309
|
-
\`\`\`
|
|
310
|
-
|
|
311
|
-
## Log Analysis Guide
|
|
312
|
-
|
|
313
|
-
This agent helps you understand what went wrong in your application by analyzing logs.
|
|
314
|
-
|
|
315
|
-
### How to Use
|
|
316
|
-
|
|
317
|
-
1. Describe the error or issue
|
|
318
|
-
2. Provide log entries or time window
|
|
319
|
-
3. Get root cause analysis with suggested fixes
|
|
320
|
-
`;
|
|
321
|
-
}
|
|
322
|
-
private generateEnvExample(config: DrTraceConfig): void {
|
|
323
|
-
const envFile = path.join(this.drtraceDir, ".env.example");
|
|
324
|
-
const content = `# DrTrace Configuration - Copy to .env and customize
|
|
325
|
-
|
|
326
|
-
# Basic Configuration
|
|
327
|
-
DRTRACE_APPLICATION_ID=${config.application_id}
|
|
328
|
-
DRTRACE_DAEMON_URL=${config.daemon_url || "http://localhost:8001"}
|
|
329
|
-
DRTRACE_ENABLED=${config.enabled !== false ? "true" : "false"}
|
|
330
|
-
|
|
331
|
-
# Environment-specific overrides
|
|
332
|
-
# Uncomment and modify for your environment
|
|
333
|
-
# DRTRACE_DAEMON_HOST=localhost
|
|
334
|
-
# DRTRACE_DAEMON_PORT=8001
|
|
335
|
-
# DRTRACE_RETENTION_DAYS=7
|
|
336
|
-
|
|
337
|
-
# Agent configuration
|
|
338
|
-
# DRTRACE_AGENT_ENABLED=false
|
|
339
|
-
# DRTRACE_AGENT_FRAMEWORK=bmad
|
|
340
|
-
`;
|
|
341
|
-
fs.writeFileSync(envFile, content);
|
|
342
|
-
console.log(`ā Generated: ${envFile}`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Generate README.md
|
|
347
|
-
*/
|
|
348
|
-
private generateReadme(): void {
|
|
349
|
-
const readmeFile = path.join(this.drtraceDir, "README.md");
|
|
350
|
-
const content = `# DrTrace Configuration Guide
|
|
351
|
-
|
|
352
|
-
This directory contains configuration files for the DrTrace (Web Workflow Integration) system.
|
|
353
|
-
|
|
354
|
-
## Files
|
|
355
|
-
|
|
356
|
-
- **config.json** - Main project configuration
|
|
357
|
-
- **config.{environment}.json** - Environment-specific overrides
|
|
358
|
-
- **.env.example** - Environment variable template
|
|
359
|
-
- **agents/** - Agent specifications and custom rules
|
|
360
|
-
|
|
361
|
-
## Configuration
|
|
362
|
-
|
|
363
|
-
### Basic Setup
|
|
364
|
-
|
|
365
|
-
1. Review and customize \`config.json\`
|
|
366
|
-
2. For environment-specific settings, edit \`config.{environment}.json\`
|
|
367
|
-
3. Create \`.env\` from \`.env.example\` and set your environment variables
|
|
368
|
-
|
|
369
|
-
### Environment Variables
|
|
370
|
-
|
|
371
|
-
- \`DRTRACE_APPLICATION_ID\` - Unique application identifier
|
|
372
|
-
- \`DRTRACE_DAEMON_URL\` - URL of the DrTrace daemon
|
|
373
|
-
- \`DRTRACE_ENABLED\` - Enable/disable DrTrace globally (true/false)
|
|
374
|
-
- \`DRTRACE_RETENTION_DAYS\` - How long to retain logs (days)
|
|
375
|
-
|
|
376
|
-
### Environments
|
|
377
|
-
|
|
378
|
-
Configure separate settings for:
|
|
379
|
-
- **development** - Local development setup
|
|
380
|
-
- **staging** - Pre-production testing
|
|
381
|
-
- **production** - Live environment
|
|
382
|
-
- **ci** - Continuous integration/testing
|
|
383
|
-
|
|
384
|
-
## Usage
|
|
385
|
-
|
|
386
|
-
Load configuration based on your environment:
|
|
387
|
-
|
|
388
|
-
\`\`\`javascript
|
|
389
|
-
import { ConfigSchema } from 'drtrace';
|
|
390
|
-
const config = ConfigSchema.load('./_drtrace/config.json');
|
|
391
|
-
\`\`\`
|
|
392
|
-
|
|
393
|
-
## Further Reading
|
|
394
|
-
|
|
395
|
-
- See \`docs/\` for complete documentation
|
|
396
|
-
- Check \`agents/\` for agent specifications
|
|
397
|
-
`;
|
|
398
|
-
fs.writeFileSync(readmeFile, content);
|
|
399
|
-
console.log(`ā Generated: ${readmeFile}`);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Print initialization summary
|
|
404
|
-
*/
|
|
405
|
-
private printSummary(config: DrTraceConfig): void {
|
|
406
|
-
console.log("\n" + "=".repeat(50));
|
|
407
|
-
console.log("ā
Project Initialization Complete!\n");
|
|
408
|
-
|
|
409
|
-
console.log(`š Configuration Location: ${this.drtraceDir}\n`);
|
|
410
|
-
|
|
411
|
-
console.log("š Generated Files:");
|
|
412
|
-
console.log(` ⢠${this.configPath}`);
|
|
413
|
-
for (const env of config.environments || []) {
|
|
414
|
-
console.log(` ⢠${path.join(this.drtraceDir, `config.${env}.json`)}`);
|
|
415
|
-
}
|
|
416
|
-
console.log(` ⢠${path.join(this.drtraceDir, ".env.example")}`);
|
|
417
|
-
console.log(` ⢠${path.join(this.drtraceDir, "README.md")}`);
|
|
418
|
-
|
|
419
|
-
if (config.agent?.enabled) {
|
|
420
|
-
console.log(
|
|
421
|
-
` ⢠${path.join(this.drtraceDir, "agents", "log-analysis.md")}`
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
console.log("\nš Next Steps:");
|
|
426
|
-
console.log(` 1. Review ${this.configPath}`);
|
|
427
|
-
console.log(` 2. Create .env: cp ${path.join(this.drtraceDir, ".env.example")} .env`);
|
|
428
|
-
console.log(` 3. Start the daemon: drtrace daemon start`);
|
|
429
|
-
console.log(
|
|
430
|
-
` 4. Read ${path.join(this.drtraceDir, "README.md")} for more details`
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
console.log("\n" + "=".repeat(50) + "\n");
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Entry point for init command
|
|
439
|
-
*/
|
|
440
|
-
export async function runInitProject(
|
|
441
|
-
projectRoot?: string
|
|
442
|
-
): Promise<number> {
|
|
443
|
-
try {
|
|
444
|
-
const initializer = new ProjectInitializer(projectRoot);
|
|
445
|
-
const success = await initializer.runInteractive();
|
|
446
|
-
return success ? 0 : 1;
|
|
447
|
-
} catch (error) {
|
|
448
|
-
console.error(`\nā Error during initialization:`, error);
|
|
449
|
-
return 1;
|
|
450
|
-
}
|
|
451
|
-
}
|
package/src/logger.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { LogEvent, LogLevel } from './types';
|
|
2
|
-
import { LogQueue } from './queue';
|
|
3
|
-
|
|
4
|
-
export class DrTraceLogger {
|
|
5
|
-
private queue: LogQueue;
|
|
6
|
-
private applicationId: string;
|
|
7
|
-
private logLevel: LogLevel;
|
|
8
|
-
private originalConsole?: { log: typeof console.log; error: typeof console.error };
|
|
9
|
-
|
|
10
|
-
constructor(opts: { queue: LogQueue; applicationId: string; logLevel: LogLevel }) {
|
|
11
|
-
this.queue = opts.queue;
|
|
12
|
-
this.applicationId = opts.applicationId;
|
|
13
|
-
this.logLevel = opts.logLevel || 'info';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
attachToConsole(): void {
|
|
17
|
-
if (this.originalConsole) return;
|
|
18
|
-
this.originalConsole = { log: console.log, error: console.error };
|
|
19
|
-
|
|
20
|
-
console.log = (...args: any[]) => {
|
|
21
|
-
try {
|
|
22
|
-
this.log('info', args.map(String).join(' '));
|
|
23
|
-
} catch {
|
|
24
|
-
// Silently ignore logging errors
|
|
25
|
-
}
|
|
26
|
-
this.originalConsole!.log.apply(console, args);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
console.error = (...args: any[]) => {
|
|
30
|
-
try {
|
|
31
|
-
this.log('error', args.map(String).join(' '));
|
|
32
|
-
} catch {
|
|
33
|
-
// Silently ignore logging errors
|
|
34
|
-
}
|
|
35
|
-
this.originalConsole!.error.apply(console, args);
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
detachFromConsole(): void {
|
|
40
|
-
if (!this.originalConsole) return;
|
|
41
|
-
console.log = this.originalConsole.log;
|
|
42
|
-
console.error = this.originalConsole.error;
|
|
43
|
-
this.originalConsole = undefined;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
log(level: LogLevel, message: string, context?: Record<string, unknown>): void {
|
|
47
|
-
const event: LogEvent = {
|
|
48
|
-
timestamp: new Date().toISOString(),
|
|
49
|
-
applicationId: this.applicationId,
|
|
50
|
-
level,
|
|
51
|
-
message,
|
|
52
|
-
context,
|
|
53
|
-
};
|
|
54
|
-
this.queue.push(event);
|
|
55
|
-
}
|
|
56
|
-
}
|
package/src/queue.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { LogEvent } from './types';
|
|
2
|
-
import { Transport } from './transport';
|
|
3
|
-
|
|
4
|
-
export class LogQueue {
|
|
5
|
-
private buffer: LogEvent[] = [];
|
|
6
|
-
private transport: Transport;
|
|
7
|
-
private batchSize: number;
|
|
8
|
-
private flushIntervalMs: number;
|
|
9
|
-
private timer: NodeJS.Timeout | null = null;
|
|
10
|
-
private running = false;
|
|
11
|
-
private maxQueueSize: number;
|
|
12
|
-
private exitHandlerBound = false;
|
|
13
|
-
private exitHandler?: () => Promise<void>;
|
|
14
|
-
|
|
15
|
-
constructor(opts: { transport: Transport; batchSize: number; flushIntervalMs: number; maxQueueSize?: number }) {
|
|
16
|
-
this.transport = opts.transport;
|
|
17
|
-
this.batchSize = Math.max(1, opts.batchSize);
|
|
18
|
-
this.flushIntervalMs = Math.max(100, opts.flushIntervalMs);
|
|
19
|
-
this.maxQueueSize = Math.max(1, opts.maxQueueSize ?? 10000);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
start(): void {
|
|
23
|
-
if (this.running) return;
|
|
24
|
-
this.running = true;
|
|
25
|
-
this.scheduleFlush();
|
|
26
|
-
this.registerExitHandlers();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
stop(): void {
|
|
30
|
-
this.running = false;
|
|
31
|
-
if (this.timer) {
|
|
32
|
-
clearTimeout(this.timer);
|
|
33
|
-
this.timer = null;
|
|
34
|
-
}
|
|
35
|
-
this.unregisterExitHandlers();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
push(event: LogEvent): void {
|
|
39
|
-
this.buffer.push(event);
|
|
40
|
-
if (this.buffer.length > this.maxQueueSize) {
|
|
41
|
-
// Drop oldest to enforce limit
|
|
42
|
-
const dropCount = this.buffer.length - this.maxQueueSize;
|
|
43
|
-
this.buffer.splice(0, dropCount);
|
|
44
|
-
console.warn?.(`DrTrace: maxQueueSize exceeded, dropped ${dropCount} log(s)`);
|
|
45
|
-
}
|
|
46
|
-
if (this.buffer.length >= this.batchSize) {
|
|
47
|
-
this.flush();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async flush(): Promise<void> {
|
|
52
|
-
if (this.buffer.length === 0) {
|
|
53
|
-
this.scheduleFlush();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const toSend = this.buffer.splice(0, this.buffer.length);
|
|
57
|
-
try {
|
|
58
|
-
await this.transport.sendBatch(toSend);
|
|
59
|
-
} finally {
|
|
60
|
-
this.scheduleFlush();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private scheduleFlush(): void {
|
|
65
|
-
if (!this.running) return;
|
|
66
|
-
if (this.timer) {
|
|
67
|
-
clearTimeout(this.timer);
|
|
68
|
-
this.timer = null;
|
|
69
|
-
}
|
|
70
|
-
this.timer = setTimeout(() => {
|
|
71
|
-
this.flush();
|
|
72
|
-
}, this.flushIntervalMs);
|
|
73
|
-
this.timer.unref?.();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private registerExitHandlers(): void {
|
|
77
|
-
if (this.exitHandlerBound) return;
|
|
78
|
-
const handler = async () => {
|
|
79
|
-
try {
|
|
80
|
-
const flushPromise = this.flush();
|
|
81
|
-
const timeout = new Promise((resolve) => {
|
|
82
|
-
const to = setTimeout(resolve, 2000);
|
|
83
|
-
(to as any).unref?.();
|
|
84
|
-
});
|
|
85
|
-
await Promise.race([flushPromise, timeout]);
|
|
86
|
-
} catch {
|
|
87
|
-
// ignore
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
this.exitHandler = handler;
|
|
91
|
-
process.on('SIGINT', handler);
|
|
92
|
-
process.on('SIGTERM', handler);
|
|
93
|
-
this.exitHandlerBound = true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
private unregisterExitHandlers(): void {
|
|
97
|
-
if (!this.exitHandlerBound) return;
|
|
98
|
-
if (this.exitHandler) {
|
|
99
|
-
process.removeListener('SIGINT', this.exitHandler);
|
|
100
|
-
process.removeListener('SIGTERM', this.exitHandler);
|
|
101
|
-
this.exitHandler = undefined;
|
|
102
|
-
}
|
|
103
|
-
this.exitHandlerBound = false;
|
|
104
|
-
}
|
|
105
|
-
}
|
package/src/transport.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { LogEvent } from './types';
|
|
2
|
-
|
|
3
|
-
const DEFAULT_TIMEOUT_MS = 5000;
|
|
4
|
-
const DEFAULT_MAX_RETRIES = 3;
|
|
5
|
-
const BACKOFF_MS = [100, 200, 400];
|
|
6
|
-
const USER_AGENT = 'drtrace-client-js/0.1.0';
|
|
7
|
-
|
|
8
|
-
export class Transport {
|
|
9
|
-
private daemonUrl: string;
|
|
10
|
-
private timeoutMs: number;
|
|
11
|
-
private maxRetries: number;
|
|
12
|
-
|
|
13
|
-
constructor(opts: { daemonUrl: string; timeoutMs?: number; maxRetries?: number }) {
|
|
14
|
-
this.daemonUrl = opts.daemonUrl.replace(/\/$/, '');
|
|
15
|
-
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
16
|
-
this.maxRetries = Math.max(0, opts.maxRetries ?? DEFAULT_MAX_RETRIES);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async sendBatch(events: LogEvent[]): Promise<void> {
|
|
20
|
-
if (events.length === 0) return;
|
|
21
|
-
const url = `${this.daemonUrl}/logs/ingest`;
|
|
22
|
-
const payload = { logs: events };
|
|
23
|
-
|
|
24
|
-
const fetchImpl: typeof fetch | undefined = (globalThis as any).fetch;
|
|
25
|
-
if (!fetchImpl) return; // Non-blocking skip
|
|
26
|
-
|
|
27
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
28
|
-
const controller = new AbortController();
|
|
29
|
-
const to = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
30
|
-
to.unref?.();
|
|
31
|
-
try {
|
|
32
|
-
const res = await fetchImpl(url, {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'application/json',
|
|
36
|
-
'User-Agent': USER_AGENT,
|
|
37
|
-
},
|
|
38
|
-
body: JSON.stringify(payload),
|
|
39
|
-
signal: controller.signal,
|
|
40
|
-
});
|
|
41
|
-
// Consume body to free resources
|
|
42
|
-
res.body && (await res.text().catch(() => ''));
|
|
43
|
-
// Treat non-2xx as retryable
|
|
44
|
-
if (res.ok) return;
|
|
45
|
-
} catch (_err) {
|
|
46
|
-
// swallow and retry
|
|
47
|
-
} finally {
|
|
48
|
-
clearTimeout(to);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (attempt < this.maxRetries) {
|
|
52
|
-
const delay = BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)];
|
|
53
|
-
await new Promise((resolve) => {
|
|
54
|
-
const to = setTimeout(resolve, delay);
|
|
55
|
-
to.unref?.();
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
-
|
|
3
|
-
export interface LogEvent {
|
|
4
|
-
timestamp: string;
|
|
5
|
-
applicationId: string;
|
|
6
|
-
level: LogLevel;
|
|
7
|
-
message: string;
|
|
8
|
-
context?: Record<string, unknown>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ClientOptions {
|
|
12
|
-
applicationId: string;
|
|
13
|
-
daemonUrl: string;
|
|
14
|
-
enabled?: boolean;
|
|
15
|
-
logLevel?: LogLevel;
|
|
16
|
-
batchSize?: number;
|
|
17
|
-
flushIntervalMs?: number;
|
|
18
|
-
maxRetries?: number;
|
|
19
|
-
maxQueueSize?: number;
|
|
20
|
-
}
|