@sschepis/robodev 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/ai.mjs +8 -0
- package/package.json +48 -0
- package/src/cli/cli-interface.mjs +271 -0
- package/src/config.mjs +64 -0
- package/src/core/ai-assistant.mjs +540 -0
- package/src/core/ai-provider.mjs +579 -0
- package/src/core/history-manager.mjs +330 -0
- package/src/core/system-prompt.mjs +182 -0
- package/src/custom-tools/custom-tools-manager.mjs +310 -0
- package/src/execution/tool-executor.mjs +892 -0
- package/src/lib/README.md +114 -0
- package/src/lib/adapters/console-status-adapter.mjs +48 -0
- package/src/lib/adapters/network-llm-adapter.mjs +37 -0
- package/src/lib/index.mjs +101 -0
- package/src/lib/interfaces.d.ts +98 -0
- package/src/main.mjs +61 -0
- package/src/package/package-manager.mjs +223 -0
- package/src/quality/code-validator.mjs +126 -0
- package/src/quality/quality-evaluator.mjs +248 -0
- package/src/reasoning/reasoning-system.mjs +258 -0
- package/src/structured-dev/flow-manager.mjs +321 -0
- package/src/structured-dev/implementation-planner.mjs +223 -0
- package/src/structured-dev/manifest-manager.mjs +423 -0
- package/src/structured-dev/plan-executor.mjs +113 -0
- package/src/structured-dev/project-bootstrapper.mjs +523 -0
- package/src/tools/desktop-automation-tools.mjs +172 -0
- package/src/tools/file-tools.mjs +141 -0
- package/src/tools/tool-definitions.mjs +872 -0
- package/src/ui/console-styler.mjs +503 -0
- package/src/workspace/workspace-manager.mjs +215 -0
- package/themes.json +66 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# AI Man Library Interface
|
|
2
|
+
|
|
3
|
+
This module exposes the core AI Assistant capabilities as a structured library, allowing integration into other AI agents, tools, or applications.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `AiMan` class provides a high-level interface to the AI system. It supports:
|
|
8
|
+
- **Dependency Injection**: Plug in your own LLM provider and status reporting mechanisms.
|
|
9
|
+
- **Agent Integration**: Easily expose the system as a tool for other AI agents (e.g., LangChain, AutoGPT).
|
|
10
|
+
- **Callback Statusing**: Receive real-time updates on progress, tool execution, and logs.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
### Basic Usage
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { AiMan } from 'ai-assistant/lib';
|
|
18
|
+
|
|
19
|
+
// Initialize with default settings (uses internal LLM and console output)
|
|
20
|
+
const ai = new AiMan();
|
|
21
|
+
|
|
22
|
+
// Execute a complex task
|
|
23
|
+
const result = await ai.execute("Create a new feature for user authentication in the current project");
|
|
24
|
+
|
|
25
|
+
console.log(result);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Advanced Usage with Custom Adapters
|
|
29
|
+
|
|
30
|
+
You can inject custom adapters to control how the AI communicates and reports status.
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { AiMan } from 'ai-assistant/lib';
|
|
34
|
+
|
|
35
|
+
// Custom LLM Adapter (must implement generateContent)
|
|
36
|
+
const myLLMAdapter = {
|
|
37
|
+
async generateContent(request) {
|
|
38
|
+
// request follows OpenAI Chat Completion API format
|
|
39
|
+
// Call your own model service here...
|
|
40
|
+
return {
|
|
41
|
+
choices: [{
|
|
42
|
+
message: { role: 'assistant', content: '...' }
|
|
43
|
+
}]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Custom Status Adapter (must implement log, onProgress, etc.)
|
|
49
|
+
const myStatusAdapter = {
|
|
50
|
+
log(level, message, meta) {
|
|
51
|
+
// Send logs to your dashboard/frontend
|
|
52
|
+
socket.emit('log', { level, message, meta });
|
|
53
|
+
},
|
|
54
|
+
onProgress(percent, status) {
|
|
55
|
+
updateProgressBar(percent, status);
|
|
56
|
+
},
|
|
57
|
+
onToolStart(name, args) {
|
|
58
|
+
console.log(`Tool Started: ${name}`);
|
|
59
|
+
},
|
|
60
|
+
onToolEnd(name, result) {
|
|
61
|
+
console.log(`Tool Ended: ${name}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Initialize with adapters
|
|
66
|
+
const ai = new AiMan({
|
|
67
|
+
workingDir: '/path/to/project',
|
|
68
|
+
llmAdapter: myLLMAdapter,
|
|
69
|
+
statusAdapter: myStatusAdapter
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await ai.execute("Refactor the login module");
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Agent Integration
|
|
76
|
+
|
|
77
|
+
To use AI Man as a tool within another agent (e.g., a "Manager" agent delegating coding tasks), use `getToolDefinition()`:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const aiMan = new AiMan();
|
|
81
|
+
const toolDef = aiMan.getToolDefinition();
|
|
82
|
+
|
|
83
|
+
// toolDef matches standard JSON Schema for function calling:
|
|
84
|
+
// {
|
|
85
|
+
// "name": "execute_software_development_task",
|
|
86
|
+
// "description": "...",
|
|
87
|
+
// "parameters": { ... }
|
|
88
|
+
// }
|
|
89
|
+
|
|
90
|
+
// Register this tool with your primary agent
|
|
91
|
+
myAgent.registerTool(toolDef, async (args) => {
|
|
92
|
+
return await aiMan.execute(args.task);
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API Reference
|
|
97
|
+
|
|
98
|
+
### `AiMan` Class
|
|
99
|
+
|
|
100
|
+
#### `constructor(config)`
|
|
101
|
+
- `config.workingDir`: Path to the project root (default: `process.cwd()`).
|
|
102
|
+
- `config.llmAdapter`: Object implementing `LLMAdapter` interface.
|
|
103
|
+
- `config.statusAdapter`: Object implementing `StatusAdapter` interface.
|
|
104
|
+
- `config.overrides`: Optional configuration overrides (model, etc.).
|
|
105
|
+
|
|
106
|
+
#### `async execute(task)`
|
|
107
|
+
Executes the given natural language task. Returns a Promise resolving to the final output string.
|
|
108
|
+
|
|
109
|
+
#### `getToolDefinition()`
|
|
110
|
+
Returns a JSON Schema object describing the `execute` capability, suitable for LLM function calling.
|
|
111
|
+
|
|
112
|
+
### Interfaces
|
|
113
|
+
|
|
114
|
+
See `src/lib/interfaces.d.ts` for TypeScript definitions of `LLMAdapter` and `StatusAdapter`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { consoleStyler } from '../../ui/console-styler.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Status Adapter that outputs to the console using consoleStyler
|
|
5
|
+
* This is the default behavior if no adapter is provided
|
|
6
|
+
*/
|
|
7
|
+
export class ConsoleStatusAdapter {
|
|
8
|
+
constructor() {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Log a message
|
|
12
|
+
* @param {string} level - Log level/category
|
|
13
|
+
* @param {string} message - Message content
|
|
14
|
+
* @param {Object} [metadata] - Optional metadata
|
|
15
|
+
*/
|
|
16
|
+
log(level, message, metadata = {}) {
|
|
17
|
+
consoleStyler.log(level, message, metadata);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Report progress
|
|
22
|
+
* @param {number} progress - 0-100
|
|
23
|
+
* @param {string} status - Status description
|
|
24
|
+
*/
|
|
25
|
+
onProgress(progress, status) {
|
|
26
|
+
consoleStyler.log('progress', `[${progress}%] ${status}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Called when a tool starts
|
|
31
|
+
* @param {string} toolName
|
|
32
|
+
* @param {Object} args
|
|
33
|
+
*/
|
|
34
|
+
onToolStart(toolName, args) {
|
|
35
|
+
// consoleStyler handles tool logging internally via logs typically,
|
|
36
|
+
// but we can make it explicit here
|
|
37
|
+
// consoleStyler.log('working', `Executing tool: ${toolName}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Called when a tool ends
|
|
42
|
+
* @param {string} toolName
|
|
43
|
+
* @param {any} result
|
|
44
|
+
*/
|
|
45
|
+
onToolEnd(toolName, result) {
|
|
46
|
+
// consoleStyler.log('tools', `✓ Tool completed: ${toolName}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { callProvider, callProviderStream } from '../../core/ai-provider.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default LLM Adapter that uses the built-in network provider (OpenAI/Gemini/Local)
|
|
5
|
+
*/
|
|
6
|
+
export class NetworkLLMAdapter {
|
|
7
|
+
constructor(config = {}) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Call the LLM provider
|
|
13
|
+
* @param {Object} requestBody - OpenAI compatible request
|
|
14
|
+
* @returns {Promise<Object>} OpenAI compatible response
|
|
15
|
+
*/
|
|
16
|
+
async generateContent(requestBody) {
|
|
17
|
+
// Merge config overrides if any
|
|
18
|
+
const finalRequest = {
|
|
19
|
+
...requestBody,
|
|
20
|
+
...this.config
|
|
21
|
+
};
|
|
22
|
+
return await callProvider(finalRequest);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Stream the LLM response
|
|
27
|
+
* @param {Object} requestBody
|
|
28
|
+
* @returns {Promise<Response>} Fetch response or stream
|
|
29
|
+
*/
|
|
30
|
+
async generateContentStream(requestBody) {
|
|
31
|
+
const finalRequest = {
|
|
32
|
+
...requestBody,
|
|
33
|
+
...this.config
|
|
34
|
+
};
|
|
35
|
+
return await callProviderStream(finalRequest);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { MiniAIAssistant } from '../core/ai-assistant.mjs';
|
|
2
|
+
import { ConsoleStatusAdapter } from './adapters/console-status-adapter.mjs';
|
|
3
|
+
import { NetworkLLMAdapter } from './adapters/network-llm-adapter.mjs';
|
|
4
|
+
import { consoleStyler } from '../ui/console-styler.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Main entry point for the AI Man Library.
|
|
8
|
+
* Provides a structured interface for integrating the AI system into other agents or applications.
|
|
9
|
+
*/
|
|
10
|
+
export class AiMan {
|
|
11
|
+
/**
|
|
12
|
+
* Create a new AI Man instance
|
|
13
|
+
* @param {Object} config - Configuration options
|
|
14
|
+
* @param {string} [config.workingDir] - Working directory (defaults to process.cwd())
|
|
15
|
+
* @param {Object} [config.llmAdapter] - Adapter for LLM calls (defaults to NetworkLLMAdapter)
|
|
16
|
+
* @param {Object} [config.statusAdapter] - Adapter for status/logging (defaults to ConsoleStatusAdapter)
|
|
17
|
+
* @param {Object} [config.overrides] - Overrides for internal components (model, temperature, etc.)
|
|
18
|
+
*/
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
this.workingDir = config.workingDir || process.cwd();
|
|
21
|
+
|
|
22
|
+
// Initialize adapters
|
|
23
|
+
this.llmAdapter = config.llmAdapter || new NetworkLLMAdapter(config.overrides || {});
|
|
24
|
+
this.statusAdapter = config.statusAdapter || new ConsoleStatusAdapter();
|
|
25
|
+
|
|
26
|
+
// Configure global logger redirect if a custom status adapter is provided
|
|
27
|
+
// This ensures that internal components using consoleStyler route logs through the adapter
|
|
28
|
+
if (config.statusAdapter) {
|
|
29
|
+
consoleStyler.setListener(this.statusAdapter);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Initialize the core assistant with injected dependencies
|
|
33
|
+
this.assistant = new MiniAIAssistant(this.workingDir, {
|
|
34
|
+
llmAdapter: this.llmAdapter
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Initialize the system (loads tools, manifests, etc.)
|
|
40
|
+
*/
|
|
41
|
+
async initialize() {
|
|
42
|
+
await this.assistant.initializeCustomTools();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Execute a high-level task
|
|
47
|
+
* @param {string} task - The task description
|
|
48
|
+
* @returns {Promise<string>} The result of the task execution
|
|
49
|
+
*/
|
|
50
|
+
async execute(task) {
|
|
51
|
+
// Ensure initialized
|
|
52
|
+
if (!this.assistant.customToolsLoaded) {
|
|
53
|
+
await this.initialize();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.statusAdapter.onToolStart('ai_man_execute', { task });
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const result = await this.assistant.run(task);
|
|
60
|
+
this.statusAdapter.onToolEnd('ai_man_execute', result);
|
|
61
|
+
return result;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const errorMessage = `Execution failed: ${error.message}`;
|
|
64
|
+
this.statusAdapter.log('error', errorMessage);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the tool definition for integrating this library as a tool in another agent
|
|
71
|
+
* @returns {Object} JSON Schema for the tool
|
|
72
|
+
*/
|
|
73
|
+
getToolDefinition() {
|
|
74
|
+
return {
|
|
75
|
+
name: "execute_software_development_task",
|
|
76
|
+
description: "Execute a complex software development task using the AI Man system. Capable of planning, coding, debugging, and verifying software features.",
|
|
77
|
+
parameters: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
task: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "The detailed description of the task to perform. Be specific about requirements."
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
required: ["task"]
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the current status/context of the assistant
|
|
92
|
+
* @returns {Object} Context object
|
|
93
|
+
*/
|
|
94
|
+
getContext() {
|
|
95
|
+
return this.assistant.getContext();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Re-export adapters for convenience
|
|
100
|
+
export { ConsoleStatusAdapter } from './adapters/console-status-adapter.mjs';
|
|
101
|
+
export { NetworkLLMAdapter } from './adapters/network-llm-adapter.mjs';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for the AI Man Library Configuration
|
|
3
|
+
*/
|
|
4
|
+
export interface AiManConfig {
|
|
5
|
+
/** The working directory for the project */
|
|
6
|
+
workingDir: string;
|
|
7
|
+
/** Adapter for the host LLM service */
|
|
8
|
+
llmAdapter?: LLMAdapter;
|
|
9
|
+
/** Adapter for status reporting */
|
|
10
|
+
statusAdapter?: StatusAdapter;
|
|
11
|
+
/** Optional overrides for internal components */
|
|
12
|
+
overrides?: {
|
|
13
|
+
model?: string;
|
|
14
|
+
temperature?: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Adapter interface for providing LLM capabilities
|
|
20
|
+
* Conforms to OpenAI-compatible request/response structure
|
|
21
|
+
*/
|
|
22
|
+
export interface LLMAdapter {
|
|
23
|
+
/**
|
|
24
|
+
* call the LLM provider
|
|
25
|
+
* @param requestBody The standard OpenAI chat completion request body
|
|
26
|
+
* @returns The standard OpenAI chat completion response
|
|
27
|
+
*/
|
|
28
|
+
generateContent(requestBody: any): Promise<any>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* (Optional) Stream the LLM response
|
|
32
|
+
* @param requestBody The standard OpenAI chat completion request body
|
|
33
|
+
* @returns A stream or async iterable of content chunks
|
|
34
|
+
*/
|
|
35
|
+
generateContentStream?(requestBody: any): AsyncIterable<string> | Promise<ReadableStream>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Adapter interface for status reporting and logging
|
|
40
|
+
*/
|
|
41
|
+
export interface StatusAdapter {
|
|
42
|
+
/**
|
|
43
|
+
* Log a message from the system
|
|
44
|
+
* @param level The log level or category (e.g., 'info', 'error', 'step', 'thought')
|
|
45
|
+
* @param message The message content
|
|
46
|
+
* @param metadata Optional metadata
|
|
47
|
+
*/
|
|
48
|
+
log(level: string, message: string, metadata?: any): void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Report progress on a long-running operation
|
|
52
|
+
* @param progress 0-100 completion percentage
|
|
53
|
+
* @param status Current status description
|
|
54
|
+
*/
|
|
55
|
+
onProgress(progress: number, status: string): void;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Called when a tool execution starts
|
|
59
|
+
* @param toolName Name of the tool
|
|
60
|
+
* @param args Tool arguments
|
|
61
|
+
*/
|
|
62
|
+
onToolStart(toolName: string, args: any): void;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Called when a tool execution completes
|
|
66
|
+
* @param toolName Name of the tool
|
|
67
|
+
* @param result Tool result
|
|
68
|
+
*/
|
|
69
|
+
onToolEnd(toolName: string, result: any): void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The main interface for the library
|
|
74
|
+
*/
|
|
75
|
+
export interface AiManInterface {
|
|
76
|
+
/**
|
|
77
|
+
* Initialize the library
|
|
78
|
+
*/
|
|
79
|
+
initialize(): Promise<void>;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Execute a high-level task
|
|
83
|
+
* @param taskDescription The user's request
|
|
84
|
+
* @returns The final result or confirmation
|
|
85
|
+
*/
|
|
86
|
+
executeTask(taskDescription: string): Promise<string>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the current status of the project/workspace
|
|
90
|
+
*/
|
|
91
|
+
getProjectStatus(): Promise<any>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the tool definition for integrating this library into an agent
|
|
95
|
+
* @returns JSON Schema for the tool
|
|
96
|
+
*/
|
|
97
|
+
getToolDefinition(): object;
|
|
98
|
+
}
|
package/src/main.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Main entry point for the AI Assistant
|
|
2
|
+
// Orchestrates CLI interface and AI assistant initialization
|
|
3
|
+
|
|
4
|
+
import { MiniAIAssistant } from './core/ai-assistant.mjs';
|
|
5
|
+
import { CLIInterface } from './cli/cli-interface.mjs';
|
|
6
|
+
|
|
7
|
+
// Main execution function
|
|
8
|
+
async function main() {
|
|
9
|
+
const cli = new CLIInterface();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Set up signal handlers for graceful shutdown
|
|
13
|
+
cli.setupSignalHandlers();
|
|
14
|
+
|
|
15
|
+
// Parse command line arguments
|
|
16
|
+
const { args, workingDir, isInteractive, userInput, resume } = cli.parseArguments();
|
|
17
|
+
|
|
18
|
+
// Display startup information
|
|
19
|
+
cli.displayStartupInfo(workingDir);
|
|
20
|
+
|
|
21
|
+
// Initialize AI assistant
|
|
22
|
+
const assistant = new MiniAIAssistant(workingDir);
|
|
23
|
+
|
|
24
|
+
// Load custom tools before starting
|
|
25
|
+
await assistant.initializeCustomTools();
|
|
26
|
+
|
|
27
|
+
// Resume session if requested
|
|
28
|
+
if (resume) {
|
|
29
|
+
await assistant.loadSession('.ai-session');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isInteractive) {
|
|
33
|
+
// Interactive mode
|
|
34
|
+
await cli.startInteractiveMode(assistant, workingDir);
|
|
35
|
+
} else {
|
|
36
|
+
// Single-shot mode
|
|
37
|
+
await cli.runSingleShot(assistant, userInput, workingDir);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
} catch (error) {
|
|
41
|
+
cli.displayError(error);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
} finally {
|
|
44
|
+
cli.close();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Handle module execution
|
|
49
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
50
|
+
main().catch(err => {
|
|
51
|
+
// Import consoleStyler for error display
|
|
52
|
+
import('./ui/console-styler.mjs').then(({ consoleStyler }) => {
|
|
53
|
+
consoleStyler.log('error', `An unexpected error occurred: ${err.message}`);
|
|
54
|
+
}).catch(() => {
|
|
55
|
+
console.error("\x1b[31mAn unexpected error occurred:\x1b[0m", err);
|
|
56
|
+
});
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { main };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// Package management utilities
|
|
2
|
+
// Handles npm package installation with Node.js compatibility checks
|
|
3
|
+
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import util from 'util';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const execPromise = util.promisify(exec);
|
|
10
|
+
|
|
11
|
+
export class PackageManager {
|
|
12
|
+
constructor() {
|
|
13
|
+
// Node.js v18 compatibility - packages with known issues
|
|
14
|
+
this.problematicPackages = [
|
|
15
|
+
'axios', 'undici', 'node-fetch', 'cheerio', 'jsdom',
|
|
16
|
+
'puppeteer', 'playwright', 'got', 'superagent', 'request'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
this.nodeMajorVersion = parseInt(process.version.slice(1).split('.')[0]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Install a single npm package with compatibility checks
|
|
23
|
+
async installPackage(packageName) {
|
|
24
|
+
// Node.js compatibility check - proactively avoid packages with known issues
|
|
25
|
+
if (this.nodeMajorVersion < 20 && this.problematicPackages.includes(packageName)) {
|
|
26
|
+
consoleStyler.log('system', `Skipping ${packageName} - Node.js v${this.nodeMajorVersion} compatibility issue`);
|
|
27
|
+
consoleStyler.log('system', 'Will use built-in alternatives (fetch for HTTP, regex for HTML parsing)');
|
|
28
|
+
|
|
29
|
+
// Throw a specific error that can be caught and handled
|
|
30
|
+
throw new Error(`COMPATIBILITY_SKIP: ${packageName} incompatible with Node.js v${this.nodeMajorVersion}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if package might have undici dependencies in Node.js v18
|
|
34
|
+
if (this.nodeMajorVersion < 20 && packageName.includes('fetch')) {
|
|
35
|
+
consoleStyler.log('warning', `Warning: ${packageName} may have Node.js v${this.nodeMajorVersion} compatibility issues`);
|
|
36
|
+
throw new Error(`COMPATIBILITY_SKIP: ${packageName} may be incompatible with Node.js v${this.nodeMajorVersion}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Install in the script's directory
|
|
41
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
42
|
+
const scriptDir = path.dirname(path.dirname(path.dirname(__filename))); // Go up two levels from src/package
|
|
43
|
+
const { stdout, stderr } = await execPromise(`cd "${scriptDir}" && npm install ${packageName}`);
|
|
44
|
+
|
|
45
|
+
if (stderr) {
|
|
46
|
+
// Check stderr for undici/File errors
|
|
47
|
+
if (stderr.includes('File is not defined') || stderr.includes('undici')) {
|
|
48
|
+
consoleStyler.log('system', `Node.js compatibility issue - skipping ${packageName}`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Don't log error for compatibility skips
|
|
54
|
+
if (!error.message.startsWith('COMPATIBILITY_SKIP:')) {
|
|
55
|
+
console.error(`\x1b[31m[SYSTEM] Failed to install package '${packageName}': ${error.message}\x1b[0m`);
|
|
56
|
+
|
|
57
|
+
// Enhanced Node.js compatibility detection
|
|
58
|
+
if (error.message.includes('File is not defined') ||
|
|
59
|
+
error.message.includes('undici') ||
|
|
60
|
+
error.message.includes('webidl') ||
|
|
61
|
+
error.stdout?.includes('File is not defined') ||
|
|
62
|
+
error.stderr?.includes('File is not defined')) {
|
|
63
|
+
console.log(`\x1b[33m[SYSTEM] Node.js v${this.nodeMajorVersion} compatibility issue detected with ${packageName}\x1b[0m`);
|
|
64
|
+
console.log(`\x1b[33m[SYSTEM] Skipping ${packageName} - will use built-in alternatives (fetch, fs, etc.)\x1b[0m`);
|
|
65
|
+
throw new Error(`COMPATIBILITY_SKIP: ${packageName} incompatible with Node.js v${this.nodeMajorVersion}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Install multiple packages with error handling
|
|
74
|
+
async installPackages(packages) {
|
|
75
|
+
const skippedPackages = [];
|
|
76
|
+
const installedPackages = [];
|
|
77
|
+
const failedPackages = [];
|
|
78
|
+
|
|
79
|
+
// Helper function to handle single package installation
|
|
80
|
+
const processPackage = async (pkg) => {
|
|
81
|
+
try {
|
|
82
|
+
// First check if package is already available
|
|
83
|
+
await import(pkg);
|
|
84
|
+
installedPackages.push(pkg);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
if (e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
87
|
+
try {
|
|
88
|
+
await this.installPackage(pkg);
|
|
89
|
+
// Verify the package is accessible after installation
|
|
90
|
+
try {
|
|
91
|
+
await import(pkg);
|
|
92
|
+
installedPackages.push(pkg);
|
|
93
|
+
} catch (e2) {
|
|
94
|
+
try {
|
|
95
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
96
|
+
const scriptDir = path.dirname(path.dirname(__filename));
|
|
97
|
+
const modulePath = `file://${path.join(scriptDir, 'node_modules', pkg)}`;
|
|
98
|
+
await import(modulePath);
|
|
99
|
+
installedPackages.push(pkg);
|
|
100
|
+
} catch (e3) {
|
|
101
|
+
// For CommonJS modules, just ensure they're installed
|
|
102
|
+
installedPackages.push(pkg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (installError) {
|
|
106
|
+
if (installError.message.startsWith('COMPATIBILITY_SKIP:')) {
|
|
107
|
+
skippedPackages.push(pkg);
|
|
108
|
+
consoleStyler.log('system', `Skipped ${pkg} - will need to use built-in alternatives`);
|
|
109
|
+
} else {
|
|
110
|
+
failedPackages.push({ package: pkg, error: installError.message });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
failedPackages.push({ package: pkg, error: e.message });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Process packages in parallel batches of 3 to avoid overwhelming system
|
|
120
|
+
const BATCH_SIZE = 3;
|
|
121
|
+
for (let i = 0; i < packages.length; i += BATCH_SIZE) {
|
|
122
|
+
const batch = packages.slice(i, i + BATCH_SIZE);
|
|
123
|
+
await Promise.all(batch.map(pkg => processPackage(pkg)));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
installed: installedPackages,
|
|
128
|
+
skipped: skippedPackages,
|
|
129
|
+
failed: failedPackages
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Attempt to import a package with multiple strategies
|
|
134
|
+
async importPackage(packageName) {
|
|
135
|
+
try {
|
|
136
|
+
// Try direct import first
|
|
137
|
+
return await import(packageName);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
if (e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
140
|
+
try {
|
|
141
|
+
// Try from script directory with file:// protocol
|
|
142
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
143
|
+
const scriptDir = path.dirname(path.dirname(path.dirname(__filename)));
|
|
144
|
+
const modulePath = `file://${path.join(scriptDir, 'node_modules', packageName)}`;
|
|
145
|
+
return await import(modulePath);
|
|
146
|
+
} catch (e2) {
|
|
147
|
+
try {
|
|
148
|
+
// Try with require for CommonJS modules
|
|
149
|
+
const { createRequire } = await import('module');
|
|
150
|
+
const require = createRequire(import.meta.url);
|
|
151
|
+
return { default: require(packageName) };
|
|
152
|
+
} catch (e3) {
|
|
153
|
+
throw e; // Re-throw original error
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
throw e; // Re-throw other import errors
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if a package is problematic for the current Node.js version
|
|
163
|
+
isProblematicPackage(packageName) {
|
|
164
|
+
if (this.nodeMajorVersion < 20 && this.problematicPackages.includes(packageName)) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (this.nodeMajorVersion < 20 && packageName.includes('fetch')) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Get alternative suggestions for problematic packages
|
|
176
|
+
getAlternatives(packageName) {
|
|
177
|
+
const alternatives = {
|
|
178
|
+
'axios': 'Use built-in fetch() for HTTP requests',
|
|
179
|
+
'node-fetch': 'Use built-in fetch() for HTTP requests',
|
|
180
|
+
'cheerio': 'Use regex patterns or built-in string methods for HTML parsing',
|
|
181
|
+
'jsdom': 'Use regex patterns for simple HTML parsing',
|
|
182
|
+
'puppeteer': 'Consider using simpler HTTP requests with fetch()',
|
|
183
|
+
'playwright': 'Consider using simpler HTTP requests with fetch()',
|
|
184
|
+
'got': 'Use built-in fetch() for HTTP requests',
|
|
185
|
+
'superagent': 'Use built-in fetch() for HTTP requests',
|
|
186
|
+
'request': 'Use built-in fetch() for HTTP requests (request is deprecated)'
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return alternatives[packageName] || 'Consider using built-in Node.js modules or simpler alternatives';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Log package installation results
|
|
193
|
+
logInstallationResults(results) {
|
|
194
|
+
if (results.installed.length > 0) {
|
|
195
|
+
consoleStyler.log('packages', `✓ Installed/Available: ${results.installed.join(', ')}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (results.skipped.length > 0) {
|
|
199
|
+
consoleStyler.log('packages', `⚠ Skipped (compatibility): ${results.skipped.join(', ')}`);
|
|
200
|
+
consoleStyler.log('packages', 'Use built-in alternatives instead');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (results.failed.length > 0) {
|
|
204
|
+
consoleStyler.log('packages', '✗ Failed installations:', { box: true });
|
|
205
|
+
results.failed.forEach(({ package: pkg, error }) => {
|
|
206
|
+
consoleStyler.log('packages', ` - ${pkg}: ${error}`, { indent: true });
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Set up require function for CommonJS modules
|
|
212
|
+
async setupCommonJSRequire() {
|
|
213
|
+
const { createRequire } = await import('module');
|
|
214
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
215
|
+
const scriptDir = path.dirname(path.dirname(path.dirname(__filename)));
|
|
216
|
+
const require = createRequire(path.join(scriptDir, 'package.json'));
|
|
217
|
+
|
|
218
|
+
// Make require available globally
|
|
219
|
+
global.require = require;
|
|
220
|
+
|
|
221
|
+
return require;
|
|
222
|
+
}
|
|
223
|
+
}
|