codeaura-embedded-runtime-agent 0.1.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.
@@ -0,0 +1,253 @@
1
+ # CodeAura Embedded Runtime Agent – V1 Implementation Guide
2
+
3
+ This document describes **how to implement Version 1 (V1)** of the CodeAura Embedded Runtime Agent.
4
+
5
+ The goal of V1 is **not completeness**, but to deliver a **working, stable, and understandable foundation** that proves the concept and can evolve safely.
6
+
7
+ ---
8
+
9
+ ## 1. Scope of V1
10
+
11
+ ### What V1 MUST do
12
+
13
+ - Run inside a **Sails.js (Node.js) application**
14
+ - Capture runtime execution of:
15
+ - Controllers
16
+ - Helpers
17
+ - Measure:
18
+ - Execution start / end
19
+ - Duration
20
+ - Errors (if any)
21
+ - Emit structured execution events
22
+ - Send events to the CodeAura API via HTTP
23
+
24
+ ### What V1 MUST NOT do
25
+
26
+ - Support multiple frameworks
27
+ - Support multiple languages
28
+ - Persist data to disk
29
+ - Implement complex retry or resilience logic
30
+ - Capture database internals
31
+ - Behave like a full APM solution
32
+
33
+ ---
34
+
35
+ ## 2. Core Concepts
36
+
37
+ ### 2.1 Runtime Instrumentation
38
+
39
+ The agent **does not analyze source code statically**.
40
+ Instead, it instruments the application **at runtime** by wrapping functions while the application is starting.
41
+
42
+ This ensures:
43
+ - No breakpoints
44
+ - No debugger
45
+ - Real execution paths
46
+
47
+ ---
48
+
49
+ ### 2.2 Function Wrapping (Key Mechanism)
50
+
51
+ The core mechanism of V1 is **function wrapping**:
52
+
53
+ 1. Take an existing function
54
+ 2. Replace it with a wrapper function
55
+ 3. Measure execution
56
+ 4. Capture metadata
57
+ 5. Call the original function
58
+
59
+ This is applied to:
60
+ - Sails controllers
61
+ - Sails helpers
62
+
63
+ ---
64
+
65
+ ## 3. Step-by-Step Implementation Plan
66
+
67
+ ### Step 1 — Agent Entry Point (`Agent.js`)
68
+
69
+ Responsibilities:
70
+ - Load configuration
71
+ - Initialize core services (buffer, transport)
72
+ - Detect runtime environment
73
+ - Initialize framework hooks
74
+
75
+ Pseudo-flow:
76
+
77
+ 1. Agent.init(sails)
78
+ 2. Validate config
79
+ 3. Initialize EventBuffer
80
+ 4. Initialize HttpTransport
81
+ 5. Register Sails hook
82
+
83
+ ---
84
+
85
+ ### Step 2 — Sails Hook (`sailsHook.js`)
86
+
87
+ Responsibilities:
88
+ - Hook into the Sails lifecycle
89
+ - Run once the app is loaded
90
+ - Access controllers and helpers
91
+
92
+ Key lifecycle point:
93
+ - `sails.on('lifted')`
94
+
95
+ At this stage:
96
+ - Controllers and helpers are fully registered
97
+ - Safe to wrap functions
98
+
99
+ ---
100
+
101
+ ### Step 3 — Controller Instrumentation
102
+
103
+ Process:
104
+
105
+ 1. Iterate over `sails.controllers`
106
+ 2. For each controller action:
107
+ - Replace function with wrapped version
108
+ 3. Wrapper logic:
109
+ - Record start time
110
+ - Execute original action
111
+ - Catch errors
112
+ - Record end time
113
+ - Emit event
114
+
115
+ Captured data:
116
+ - controller name
117
+ - action name
118
+ - duration
119
+ - HTTP metadata (method, route, status)
120
+
121
+ ---
122
+
123
+ ### Step 4 — Helper Instrumentation
124
+
125
+ Process:
126
+
127
+ 1. Iterate over `sails.helpers`
128
+ 2. Wrap helper functions
129
+ 3. Track:
130
+ - helper name
131
+ - duration
132
+ - errors
133
+
134
+ Important:
135
+ - Helpers may be async or sync
136
+ - Wrapper must support both
137
+
138
+ ---
139
+
140
+ ### Step 5 — Event Creation (`EventSchema.js`)
141
+
142
+ Each execution produces an event:
143
+
144
+ Required fields:
145
+ - id
146
+ - timestamp
147
+ - type (controller | helper)
148
+ - name
149
+ - durationMs
150
+ - framework
151
+ - language
152
+
153
+ Optional:
154
+ - error
155
+ - HTTP metadata
156
+
157
+ ---
158
+
159
+ ### Step 6 — Event Buffer (`memoryBuffer.js`)
160
+
161
+ Responsibilities:
162
+ - Temporarily store events in memory
163
+ - Prevent sending one HTTP request per event
164
+
165
+ Rules:
166
+ - Simple array-based buffer
167
+ - Flush when:
168
+ - buffer size reaches threshold
169
+ - timer interval expires
170
+
171
+ ---
172
+
173
+ ### Step 7 — Transport (`httpTransport.js`)
174
+
175
+ Responsibilities:
176
+ - Send batched events to CodeAura API
177
+
178
+ Constraints:
179
+ - Best-effort delivery
180
+ - No retries in V1
181
+ - Fail silently (log errors)
182
+
183
+ ---
184
+
185
+ ## 4. Configuration Strategy
186
+
187
+ Minimal configuration:
188
+
189
+ ```js
190
+ {
191
+ enabled: true,
192
+ endpoint: "https://api.codeaura.dev/events",
193
+ apiKey: "YOUR_API_KEY"
194
+ }
195
+ ```
196
+
197
+ Configuration should:
198
+ - Be optional
199
+ - Have safe defaults
200
+
201
+ ---
202
+
203
+ ## 5. Error Handling Philosophy (V1)
204
+
205
+ Rule #1: **The agent must never crash the host application**.
206
+
207
+ Therefore:
208
+ - All agent code must be wrapped in try/catch
209
+ - Errors are logged, not thrown
210
+ - Instrumentation failure should disable itself gracefully
211
+
212
+ ---
213
+
214
+ ## 6. Testing Strategy (V1)
215
+
216
+ ### Manual testing
217
+
218
+ - Create a sample Sails app
219
+ - Install the agent
220
+ - Trigger controller routes
221
+ - Observe event logs
222
+
223
+ ### Success criteria
224
+
225
+ - App runs normally
226
+ - Events are emitted
227
+ - No visible behavior change
228
+
229
+ ---
230
+
231
+ ## 7. What Comes Next (V2+)
232
+
233
+ Once V1 is stable:
234
+
235
+ - Add Express adapter
236
+ - Introduce persistent buffering
237
+ - Add retry strategies
238
+ - Add data masking
239
+ - Add execution context correlation
240
+ - Introduce Python agent
241
+
242
+ ---
243
+
244
+ ## 8. Definition of Done (V1)
245
+
246
+ V1 is complete when:
247
+
248
+ - Agent can be installed via npm
249
+ - Sails controllers & helpers are traced
250
+ - Events reach CodeAura
251
+ - Code is readable and maintainable
252
+ - Architecture is ready for extension
253
+
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ module.exports = require("./src/agent/Agent");
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "codeaura-embedded-runtime-agent",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight runtime execution tracer for JavaScript applications",
5
+ "main": "index.js",
6
+ "keywords": [
7
+ "runtime",
8
+ "tracing",
9
+ "instrumentation",
10
+ "sailsjs",
11
+ "observability",
12
+ "codeaura"
13
+ ],
14
+ "author": "CodeAura",
15
+ "license": "MIT",
16
+ "engines": {
17
+ "node": ">=14.0.0"
18
+ },
19
+ "scripts": {
20
+ "test": "npm run lint && npm run custom-tests && echo 'Done.'",
21
+ "lint": "./node_modules/eslint/bin/eslint.js . --max-warnings=0 --report-unused-disable-directives",
22
+ "lint-windows": "eslint . --max-warnings=0 --report-unused-disable-directives -c .eslintrc --ignore-path .eslintignore",
23
+ "custom-tests": "echo \"(No other custom tests yet.)\" && echo"
24
+ },
25
+ "dependencies": {
26
+ "axios": "^1.6.0"
27
+ },
28
+ "devDependencies": {
29
+ "@babel/core": "^7.23.6",
30
+ "babel-eslint": "^10.1.0",
31
+ "eslint": "^8.55.0"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/codeaura/codeaura-embedded-runtime-agent"
36
+ }
37
+ }
@@ -0,0 +1,224 @@
1
+
2
+ const path = require("path"),
3
+ fs = require("fs"),
4
+ Config = require("../config/config"),
5
+ loggerModule = require("../utils/logger"),
6
+ MemoryBuffer = require("../core/buffer/memoryBuffer"),
7
+ HttpTransport = require("../core/transport/httpTransport");
8
+
9
+ let agentInstance = null;
10
+
11
+ class Agent {
12
+
13
+ constructor() {
14
+ this.config = null;
15
+ this.logger = null;
16
+ this.initialized = false;
17
+ this.buffer = null;
18
+ this.transport = null;
19
+ this.frameworkAdapter = null;
20
+ }
21
+
22
+ readProjectConfig() {
23
+ let configPath = null,
24
+ rawContent = null,
25
+ projectConfig = null;
26
+
27
+ try {
28
+ configPath = path.join(process.cwd(), "codeaura-config.json");
29
+ rawContent = fs.readFileSync(configPath, "utf8");
30
+ projectConfig = JSON.parse(rawContent);
31
+
32
+ return projectConfig;
33
+ } catch (unusedE) {
34
+ console.log("[CodeAura Agent] codeaura-config.json not found or invalid at: " + path.join(process.cwd(), "codeaura-config.json"));
35
+
36
+ return {};
37
+ }
38
+ }
39
+
40
+ init(userConfig = {}) {
41
+ let projectConfig = null;
42
+
43
+ try {
44
+ projectConfig = this.readProjectConfig();
45
+
46
+ if (projectConfig && projectConfig.project) {
47
+ userConfig.projectId = projectConfig.project;
48
+ }
49
+
50
+ this.config = new Config(userConfig);
51
+
52
+ this.logger = loggerModule.getLogger(this.config.get("logging"));
53
+
54
+ if (this.config.isEnabled()) {
55
+ this.logger.info("Initializing CodeAura Runtime Agent...");
56
+ this.logger.info("Project ID: " + (this.config.get("projectId") || "none"));
57
+
58
+ this.initializeCoreServices();
59
+ this.initializeFrameworkAdapter();
60
+
61
+ this.initialized = true;
62
+ this.logger.info("CodeAura Runtime Agent initialized successfully");
63
+ }
64
+ } catch (error) {
65
+ if (this.logger) {
66
+ this.logger.errorWithStack("Failed to initialize agent", error);
67
+ } else {
68
+ console.error("Failed to initialize agent", error);
69
+ }
70
+
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ initializeCoreServices() {
76
+ try {
77
+ const transportConfig = this.config.get("transport"),
78
+ bufferConfig = this.config.get("buffer"),
79
+ agentConfig = this.config.getAll();
80
+
81
+ this.transport = new HttpTransport(transportConfig, agentConfig, this.logger);
82
+ this.buffer = new MemoryBuffer(bufferConfig, this.transport, this.logger, agentConfig);
83
+
84
+ this.logger.debug("Core services initialized successfully");
85
+ } catch (error) {
86
+ this.logger.errorWithStack("Failed to initialize core services", error);
87
+
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ initializeFrameworkAdapter() {
93
+ try {
94
+ const frameworkConfig = this.config.get("framework");
95
+
96
+ let AdapterClass = null;
97
+
98
+ if (!frameworkConfig || typeof frameworkConfig !== "object" || !frameworkConfig.type) {
99
+ this.logger.info("No framework adapter configured, running in standalone mode");
100
+
101
+ return;
102
+ }
103
+
104
+ if (typeof frameworkConfig.type !== "string") {
105
+ throw new Error("Invalid framework configuration: 'type' must be a string");
106
+ }
107
+
108
+ this.logger.debug("Loading framework adapter for: " + frameworkConfig.type);
109
+
110
+ AdapterClass = this.loadFrameworkAdapter(frameworkConfig.type);
111
+
112
+ this.frameworkAdapter = new AdapterClass(frameworkConfig, this);
113
+ if (typeof this.frameworkAdapter.init === "function") {
114
+ this.frameworkAdapter.init();
115
+ }
116
+
117
+ this.logger.debug("Framework adapter initialized successfully");
118
+ } catch (error) {
119
+ this.logger.errorWithStack("Failed to initialize framework adapter", error);
120
+
121
+ throw error;
122
+ }
123
+ }
124
+
125
+ loadFrameworkAdapter(frameworkType) {
126
+ try {
127
+ let adapterPath;
128
+
129
+ switch (frameworkType.toLowerCase()) {
130
+ case "sails":
131
+ adapterPath = path.resolve(__dirname, "../framework/nodejs/sails/SailsAdapter");
132
+ break;
133
+ default:
134
+ throw new Error(`Unsupported framework type: ${frameworkType}`);
135
+ }
136
+
137
+ const AdapterClass = require(adapterPath);
138
+
139
+ return AdapterClass;
140
+ } catch (error) {
141
+ this.logger.errorWithStack(`Failed to load framework adapter: ${frameworkType}`, error);
142
+
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ getConfig() {
148
+ return this.config;
149
+ }
150
+
151
+ getLogger() {
152
+ return this.logger;
153
+ }
154
+
155
+ isInitialized() {
156
+ return this.initialized;
157
+ }
158
+
159
+ shutdown() {
160
+ try {
161
+ if (this.initialized) {
162
+ if (this.buffer) {
163
+ this.buffer.shutdown();
164
+ }
165
+
166
+ if (this.frameworkAdapter && typeof this.frameworkAdapter.shutdown === "function") {
167
+ this.frameworkAdapter.shutdown();
168
+ }
169
+
170
+ this.initialized = false;
171
+ this.logger.info("Agent shutdown complete");
172
+ }
173
+ } catch (error) {
174
+ this.logger.errorWithStack("Error during agent shutdown", error);
175
+ }
176
+ }
177
+ }
178
+
179
+ function init(sailsInstanceOrConfig, userConfig) {
180
+ if (!agentInstance) {
181
+ agentInstance = new Agent();
182
+ }
183
+
184
+ let finalConfig = {};
185
+
186
+ const isObject = (obj) => obj && typeof obj === "object" && !Array.isArray(obj),
187
+
188
+ isSailsInstance = isObject(sailsInstanceOrConfig) &&
189
+ sailsInstanceOrConfig.constructor &&
190
+ typeof sailsInstanceOrConfig.lift === "function" &&
191
+ typeof sailsInstanceOrConfig.hooks === "object";
192
+
193
+ if (isSailsInstance && isObject(userConfig)) {
194
+ finalConfig = userConfig;
195
+ finalConfig.framework = {
196
+ type: "sails",
197
+ instance: sailsInstanceOrConfig
198
+ };
199
+ } else if (!isSailsInstance && isObject(sailsInstanceOrConfig)) {
200
+ finalConfig = sailsInstanceOrConfig;
201
+ } else {
202
+ throw new Error("Invalid arguments: provide either (sailsInstance, config) or (config)");
203
+ }
204
+
205
+ agentInstance.init(finalConfig);
206
+
207
+ return agentInstance;
208
+ }
209
+
210
+ function getInstance() {
211
+ return agentInstance;
212
+ }
213
+
214
+ function shutdown() {
215
+ if (agentInstance) {
216
+ agentInstance.shutdown();
217
+ }
218
+ }
219
+
220
+ module.exports = {
221
+ init,
222
+ getInstance,
223
+ shutdown
224
+ };