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,242 @@
1
+
2
+ const publicApiClient = require("../../utils/publicApiClient");
3
+
4
+ class HttpTransport {
5
+
6
+ constructor(config, agentConfig, logger) {
7
+ this.config = config || {};
8
+ this.agentConfig = agentConfig || {};
9
+ this.logger = logger;
10
+
11
+ this.endpoint = this.agentConfig.endpoint;
12
+ this.apiKey = this.agentConfig.apiKey || "";
13
+ this.agentVersion = this.agentConfig.version || "0.1.0";
14
+ this.projectId = this.agentConfig.projectId || null;
15
+
16
+ this.timeout = this.config.timeout || 5000;
17
+ this.retries = this.config.retries || 0;
18
+
19
+ this.parseEndpoint();
20
+ this.validateConfiguration();
21
+ }
22
+
23
+ parseEndpoint() {
24
+ let url = null,
25
+ match = null;
26
+
27
+ try {
28
+ url = new URL(this.endpoint);
29
+ this.apiUrl = `${url.protocol}//${url.host}`;
30
+ this.action = url.pathname;
31
+ } catch {
32
+ match = this.endpoint.match(/^(https?:\/\/[^\/]+)(\/.*)?$/);
33
+
34
+ if (match) {
35
+ this.apiUrl = match[1];
36
+ this.action = match[2] || "/";
37
+ } else {
38
+ this.apiUrl = "";
39
+ this.action = this.endpoint;
40
+ }
41
+ }
42
+ }
43
+
44
+ validateConfiguration() {
45
+ if (!this.endpoint || typeof this.endpoint !== "string" || this.endpoint.trim() === "") {
46
+ throw new Error("HTTP Transport: endpoint is required and must be a non-empty string");
47
+ }
48
+ if (!this.logger) {
49
+ throw new Error("HTTP Transport: logger is required");
50
+ }
51
+ if (typeof this.timeout !== "number" || !isFinite(this.timeout) || this.timeout <= 0) {
52
+ throw new Error("HTTP Transport: timeout must be a number > 0");
53
+ }
54
+ if (typeof this.retries !== "number" || !isFinite(this.retries) || this.retries < 0) {
55
+ throw new Error("HTTP Transport: retries must be a number >= 0");
56
+ }
57
+ }
58
+
59
+ async send(events) {
60
+ let eventCount = 0,
61
+ result = null;
62
+
63
+ if (events && events.length > 0) {
64
+ eventCount = events.length;
65
+
66
+ this.logger.debug(`Sending ${eventCount} event(s) to ${this.endpoint}`);
67
+ this.logger.debug("Transport project_id: " + (this.projectId || "none"));
68
+
69
+ try {
70
+ result = await this.executeRequestWithRetries(events, eventCount, "send");
71
+
72
+ this.logger.info(`Successfully sent ${eventCount} event(s)`);
73
+
74
+ return result;
75
+ } catch (error) {
76
+ if (error.response) {
77
+ this.logger.errorWithStack(
78
+ `HTTP error ${error.response.status}: ${JSON.stringify(error.response.data || error.response.body, null, 2)}`,
79
+ error
80
+ );
81
+ } else {
82
+ this.logger.errorWithStack("Error sending events", error);
83
+ }
84
+
85
+ throw error;
86
+ }
87
+ }
88
+ }
89
+
90
+ async test() {
91
+ const testEvent = {
92
+ type: "test",
93
+ name: "connection-test",
94
+ timestamp: new Date().toISOString(),
95
+ framework: "nodejs",
96
+ language: "nodejs",
97
+ durationMs: 0,
98
+ meta: { test: true }
99
+ };
100
+
101
+ this.logger.info(`Testing connection to ${this.endpoint}`);
102
+
103
+ try {
104
+ await this.executeRequestWithRetries([testEvent], 1, "test");
105
+
106
+ this.logger.info("Connection test successful");
107
+
108
+ return true;
109
+ } catch (error) {
110
+ this.logger.errorWithStack("Connection test failed", error);
111
+
112
+ throw error;
113
+ }
114
+ }
115
+
116
+ async executeRequestWithRetries(events, eventCount, operationType) {
117
+ let lastError = null,
118
+ shouldRetry = null;
119
+
120
+ const maxAttempts = this.retries + 1,
121
+ requestData = {
122
+ events,
123
+ timestamp: new Date().toISOString(),
124
+ agent_version: this.agentVersion,
125
+ project_id: this.projectId
126
+ },
127
+
128
+ apiConfig = {
129
+ apiUrl: this.apiUrl,
130
+ apiKey: this.apiKey,
131
+ timeout: this.timeout
132
+ };
133
+
134
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
135
+ try {
136
+ if (attempt > 0) {
137
+ this.logger.debug(
138
+ `Retry attempt ${attempt}/${this.retries} for ${operationType}`
139
+ );
140
+ }
141
+
142
+ const result = await publicApiClient(
143
+ this.action,
144
+ "POST",
145
+ requestData,
146
+ apiConfig
147
+ );
148
+
149
+ if (result?.error) {
150
+ throw new Error(
151
+ typeof result.error === "string"
152
+ ? result.error
153
+ : JSON.stringify(result.error)
154
+ );
155
+ }
156
+
157
+ this.logger.debug("Request successful");
158
+
159
+ return result;
160
+
161
+ } catch (error) {
162
+ const safeError =
163
+ error instanceof Error
164
+ ? error
165
+ : new Error(JSON.stringify(error));
166
+
167
+ lastError = safeError;
168
+
169
+ shouldRetry = this.shouldRetryError(
170
+ safeError,
171
+ attempt,
172
+ maxAttempts
173
+ );
174
+
175
+ if (shouldRetry) {
176
+ this.logRetryableError(safeError, eventCount, attempt);
177
+ await this.delay(this.calculateBackoff(attempt));
178
+ } else {
179
+ this.logFinalError(safeError, eventCount, operationType);
180
+
181
+ throw safeError;
182
+ }
183
+ }
184
+ }
185
+
186
+ this.logFinalError(lastError, eventCount, operationType);
187
+
188
+ throw lastError;
189
+ }
190
+
191
+ shouldRetryError(error, attempt, maxAttempts) {
192
+ const errorMessage = error.message || "",
193
+ isNetworkError = error.code === "ECONNREFUSED" || error.code === "ENOTFOUND" || errorMessage.includes("Cannot connect"),
194
+ isTimeout = error.code === "ECONNABORTED" || error.code === "ETIMEDOUT" || errorMessage.includes("timeout"),
195
+ isServerError = errorMessage.includes("HTTP 5"),
196
+ isClientError = errorMessage.includes("HTTP 4"),
197
+ hasRetriesLeft = attempt < maxAttempts - 1;
198
+
199
+ if (!hasRetriesLeft || isClientError) {
200
+ return false;
201
+ }
202
+
203
+ return isNetworkError || isTimeout || isServerError;
204
+ }
205
+
206
+ logRetryableError(error, eventCount, attempt) {
207
+ const errorMessage = error.message || "Unknown error";
208
+
209
+ this.logger.warn(`Attempt ${attempt + 1} failed for ${eventCount} event(s): ${errorMessage}`);
210
+ }
211
+
212
+ logFinalError(error, eventCount, operationType) {
213
+ if (!error) {
214
+ this.logger.error(`Failed to ${operationType} ${eventCount} event(s): Unknown error`);
215
+
216
+ return;
217
+ }
218
+
219
+ const errorMessage = error.message || "Unknown error",
220
+ fullError = JSON.stringify(error, Object.getOwnPropertyNames(error), 2);
221
+
222
+ this.logger.error(`Failed to ${operationType} ${eventCount} event(s): ${errorMessage}`);
223
+ this.logger.debug(`Full error details: ${fullError}`);
224
+
225
+ if (error.stack) {
226
+ this.logger.error(error.stack);
227
+ }
228
+ }
229
+
230
+ calculateBackoff(attempt) {
231
+ const baseDelay = 1000;
232
+
233
+ return Math.min(baseDelay * Math.pow(2, attempt), 10000);
234
+ }
235
+
236
+ delay(ms) {
237
+ return new Promise(resolve => setTimeout(resolve, ms));
238
+ }
239
+
240
+ }
241
+
242
+ module.exports = HttpTransport;
@@ -0,0 +1,83 @@
1
+
2
+ let SailsHook = require("./sailsHook");
3
+
4
+ class SailsAdapter {
5
+
6
+ constructor(frameworkConfig, agent) {
7
+ this.frameworkConfig = null;
8
+ this.agent = null;
9
+ this.logger = null;
10
+ this.sailsInstance = null;
11
+ this.sailsHook = null;
12
+ this.initialized = false;
13
+
14
+ this.frameworkConfig = frameworkConfig || {};
15
+ this.agent = agent;
16
+ this.logger = agent.getLogger();
17
+
18
+ this.validateFrameworkConfig();
19
+ }
20
+
21
+ validateFrameworkConfig() {
22
+ if (!this.frameworkConfig.instance) {
23
+ throw new Error("SailsAdapter: framework instance is required");
24
+ } else {
25
+
26
+ }
27
+
28
+ this.sailsInstance = this.frameworkConfig.instance;
29
+ }
30
+
31
+ init() {
32
+ try {
33
+ this.logger.info("Initializing Sails.js adapter");
34
+
35
+ if (!this.sailsInstance) {
36
+ throw new Error("Sails instance is required");
37
+ } else {
38
+
39
+ }
40
+
41
+ this.sailsHook = new SailsHook(this.sailsInstance, this.agent);
42
+
43
+ this.initialized = true;
44
+ this.logger.info("Sails.js adapter initialized successfully");
45
+ } catch (error) {
46
+ this.logger.errorWithStack("Failed to initialize Sails adapter", error);
47
+
48
+ throw error;
49
+ }
50
+ }
51
+
52
+ isInitialized() {
53
+ return this.initialized;
54
+ }
55
+
56
+ getInstrumentationStatus() {
57
+ let status = null;
58
+
59
+ if (this.sailsHook) {
60
+ status = this.sailsHook.getInstrumentationStatus();
61
+ } else {
62
+ status = {
63
+ controllersInstrumented: false,
64
+ helpersInstrumented: false
65
+ };
66
+ }
67
+
68
+ return status;
69
+ }
70
+
71
+ verifyInstrumentation() {
72
+ if (this.sailsHook) {
73
+ return this.sailsHook.verifyInstrumentation();
74
+ } else {
75
+
76
+ }
77
+
78
+ return null;
79
+ }
80
+
81
+ }
82
+
83
+ module.exports = SailsAdapter;
@@ -0,0 +1,248 @@
1
+
2
+ let wrappers = require("../../../instrumentation/nodejs/wrappers"),
3
+ instrumentHelperTree = require("../../../instrumentation/nodejs/instrumentHelperTree");
4
+
5
+ class SailsHook {
6
+
7
+ constructor(sailsInstance, agent) {
8
+ this.sails = null;
9
+ this.agent = null;
10
+ this.logger = null;
11
+ this.buffer = null;
12
+ this.config = null;
13
+ this.controllersInstrumented = false;
14
+ this.helpersInstrumented = false;
15
+
16
+ this.sails = sailsInstance;
17
+ this.agent = agent;
18
+ this.logger = agent.getLogger();
19
+ this.buffer = agent.buffer;
20
+ this.config = agent.getConfig();
21
+
22
+ this.initialize();
23
+ }
24
+
25
+ initialize() {
26
+ let self = null;
27
+
28
+ self = this;
29
+
30
+ try {
31
+ this.logger.debug("Initializing Sails hook");
32
+
33
+ if (!this.sails) {
34
+ throw new Error("Sails instance is required");
35
+ } else {
36
+
37
+ }
38
+
39
+ if (this.sails._exited === false) {
40
+ this.logger.info("Sails is already lifted, instrumenting immediately");
41
+ this.onSailsLifted();
42
+ } else {
43
+ this.logger.debug("Sails not yet lifted, listening for lifted event");
44
+ this.sails.on("lifted", () => {
45
+ self.onSailsLifted();
46
+ });
47
+ }
48
+
49
+ this.logger.debug("Sails hook initialization complete");
50
+ } catch (error) {
51
+ this.logger.errorWithStack("Error initializing Sails hook", error);
52
+
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ onSailsLifted() {
58
+ let self = null;
59
+
60
+ self = this;
61
+
62
+ try {
63
+ this.logger.info("========== SAILS LIFTED - STARTING INSTRUMENTATION ==========");
64
+
65
+ this.instrumentControllers();
66
+ this.instrumentHelpers();
67
+
68
+ this.logger.info("========== INSTRUMENTATION COMPLETE ==========");
69
+
70
+ setTimeout(() => {
71
+ self.verifyInstrumentation();
72
+ }, 5000);
73
+ } catch (error) {
74
+ this.logger.errorWithStack("Error during Sails lifted event", error);
75
+ }
76
+ }
77
+
78
+ instrumentControllers() {
79
+ try {
80
+ const shouldInstrument = this.config.get("instrumentation").controllers;
81
+
82
+ let actions = null,
83
+ actionNames = null,
84
+ instrumentedCount = 0,
85
+ immediateCheck = null,
86
+ immediateSymbols = null,
87
+ immediateWrapped = false;
88
+
89
+ if (!shouldInstrument) {
90
+ this.logger.info("Controller instrumentation is disabled");
91
+
92
+ return;
93
+ }
94
+
95
+ actions = this.sails._actions || {};
96
+ actionNames = Object.keys(actions);
97
+
98
+ this.logger.info(`>>> Found ${actionNames.length} controller actions to instrument`);
99
+
100
+ for (const actionName of actionNames) {
101
+ const action = actions[actionName];
102
+
103
+ let targetFunction = null,
104
+ wrappedFunction = null;
105
+
106
+ if (typeof action === "function") {
107
+ targetFunction = action;
108
+ } else if (typeof action === "object" && action !== null && typeof action.fn === "function") {
109
+ targetFunction = action.fn;
110
+ } else {
111
+ continue;
112
+ }
113
+
114
+ wrappedFunction = wrappers.wrapFunction(
115
+ targetFunction,
116
+ actionName,
117
+ "controller",
118
+ this.buffer,
119
+ this.logger
120
+ );
121
+
122
+ if (typeof action === "function") {
123
+ this.sails._actions[actionName] = wrappedFunction;
124
+ } else {
125
+ action.fn = wrappedFunction;
126
+ }
127
+
128
+ instrumentedCount++;
129
+ }
130
+
131
+ this.controllersInstrumented = true;
132
+ this.logger.info(`>>> Successfully instrumented ${instrumentedCount} controller actions`);
133
+
134
+ immediateCheck = this.sails._actions[actionNames[0]];
135
+
136
+ if (immediateCheck && typeof immediateCheck === "function") {
137
+ immediateSymbols = Object.getOwnPropertySymbols(immediateCheck);
138
+ immediateWrapped = immediateSymbols.some(sym => sym.toString().includes("__codeaura_wrapped__"));
139
+
140
+ this.logger.info(`>>> Immediate verification - first action wrapped: ${immediateWrapped}`);
141
+ } else {
142
+
143
+ }
144
+ } catch (error) {
145
+ this.logger.errorWithStack("Error instrumenting controllers", error);
146
+ }
147
+ }
148
+
149
+ instrumentHelpers() {
150
+ try {
151
+ const shouldInstrument = this.config.get("instrumentation").helpers,
152
+ helpers = this.sails.helpers || {};
153
+
154
+ if (!shouldInstrument) {
155
+ this.logger.info("Helper instrumentation is disabled");
156
+
157
+ return;
158
+ }
159
+
160
+ this.logger.info(`>>> Found ${Object.keys(helpers).length} top-level helpers to instrument`);
161
+
162
+ instrumentHelperTree(helpers, "", this.buffer, this.logger);
163
+
164
+ this.helpersInstrumented = true;
165
+ this.logger.info(">>> All helpers instrumented successfully");
166
+ } catch (error) {
167
+ this.logger.errorWithStack("Error instrumenting helpers", error);
168
+ }
169
+ }
170
+
171
+ getInstrumentationStatus() {
172
+ let status = null;
173
+
174
+ status = {
175
+ controllersInstrumented: this.controllersInstrumented,
176
+ helpersInstrumented: this.helpersInstrumented
177
+ };
178
+
179
+ return status;
180
+ }
181
+
182
+ verifyInstrumentation() {
183
+ let actions = null,
184
+ actionNames = null,
185
+ wrappedCount = 0,
186
+ unwrappedCount = 0,
187
+ actionName = null,
188
+ action = null,
189
+ i = 0,
190
+ symbols = null,
191
+ hasWrappedSymbol = false,
192
+ callStats = null;
193
+
194
+ try {
195
+ actions = this.sails._actions || {};
196
+ actionNames = Object.keys(actions);
197
+
198
+ this.logger.info(`Verifying instrumentation for ${actionNames.length} actions`);
199
+
200
+ for (i = 0; i < actionNames.length; i++) {
201
+ actionName = actionNames[i];
202
+ action = actions[actionName];
203
+
204
+ let functionToCheck = null;
205
+
206
+ if (typeof action === "function") {
207
+ functionToCheck = action;
208
+ } else if (typeof action === "object" && action !== null && typeof action.fn === "function") {
209
+ functionToCheck = action.fn;
210
+ } else {
211
+ this.logger.debug(`Skipping verification for ${actionName} - not a function or object with fn`);
212
+ continue;
213
+ }
214
+
215
+ symbols = Object.getOwnPropertySymbols(functionToCheck);
216
+ hasWrappedSymbol = symbols.some(sym => sym.toString().includes("__codeaura_wrapped__"));
217
+
218
+ if (hasWrappedSymbol) {
219
+ wrappedCount++;
220
+ this.logger.debug(`Action ${actionName} is wrapped (type: ${typeof action})`);
221
+ } else {
222
+ unwrappedCount++;
223
+ this.logger.warn(`Action ${actionName} is NOT wrapped (type: ${typeof action})`);
224
+ }
225
+ }
226
+
227
+ this.logger.info(`Verification complete: ${wrappedCount} wrapped, ${unwrappedCount} unwrapped`);
228
+
229
+ callStats = wrappers.getCallStatistics();
230
+
231
+ this.logger.info(`Call statistics: ${JSON.stringify(callStats, null, 2)}`);
232
+
233
+ return {
234
+ total: actionNames.length,
235
+ wrapped: wrappedCount,
236
+ unwrapped: unwrappedCount,
237
+ callStats: callStats
238
+ };
239
+ } catch (error) {
240
+ this.logger.errorWithStack("Error verifying instrumentation", error);
241
+
242
+ return null;
243
+ }
244
+ }
245
+
246
+ }
247
+
248
+ module.exports = SailsHook;
@@ -0,0 +1,31 @@
1
+
2
+ const { wrapHelper } = require("./wrappers");
3
+
4
+ function instrumentHelperTree(node, path, buffer, logger) {
5
+ let key = null,
6
+ fullPath = null,
7
+ item = null;
8
+
9
+ if (node && typeof node === "object") {
10
+ for (key in node) {
11
+ if (Object.prototype.hasOwnProperty.call(node, key)) {
12
+ fullPath = path ? `${path}/${key}` : key;
13
+ item = node[key];
14
+
15
+ if (typeof item === "function") {
16
+ node[key] = wrapHelper(item, fullPath, buffer, logger);
17
+ } else if (typeof item === "object" && item !== null) {
18
+ instrumentHelperTree(item, fullPath, buffer, logger);
19
+ } else {
20
+
21
+ }
22
+ } else {
23
+
24
+ }
25
+ }
26
+ } else {
27
+
28
+ }
29
+ }
30
+
31
+ module.exports = instrumentHelperTree;