@kya-os/mcp-i-core 1.3.6 → 1.3.7-canary.clientinfo.20251126041014
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test$colon$coverage.log +2687 -2880
- package/.turbo/turbo-test.log +2014 -2085
- package/coverage/coverage-final.json +56 -56
- package/dist/__tests__/utils/mock-providers.d.ts +103 -0
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -0
- package/dist/__tests__/utils/mock-providers.js +293 -0
- package/dist/__tests__/utils/mock-providers.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +4 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/session-registration.service.d.ts +80 -0
- package/dist/services/session-registration.service.d.ts.map +1 -0
- package/dist/services/session-registration.service.js +228 -0
- package/dist/services/session-registration.service.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +11 -0
- package/src/services/index.ts +9 -0
- package/src/services/session-registration.service.ts +317 -0
- package/.claude/settings.local.json +0 -9
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Registration Service
|
|
4
|
+
*
|
|
5
|
+
* Registers MCP sessions with the AgentShield dashboard, enabling
|
|
6
|
+
* visibility into which MCP clients are connecting to agents.
|
|
7
|
+
*
|
|
8
|
+
* This is a fire-and-forget service - session registration should not
|
|
9
|
+
* block tool execution or affect the user experience.
|
|
10
|
+
*
|
|
11
|
+
* @package @kya-os/mcp-i-core
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.SessionRegistrationService = void 0;
|
|
15
|
+
exports.createSessionRegistrationService = createSessionRegistrationService;
|
|
16
|
+
const agentshield_api_1 = require("@kya-os/contracts/agentshield-api");
|
|
17
|
+
/**
|
|
18
|
+
* Session Registration Service
|
|
19
|
+
*
|
|
20
|
+
* Registers MCP sessions with AgentShield for dashboard visibility.
|
|
21
|
+
* Designed to be non-blocking - failures are logged but don't throw.
|
|
22
|
+
*/
|
|
23
|
+
class SessionRegistrationService {
|
|
24
|
+
config;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.config = {
|
|
27
|
+
baseUrl: config.baseUrl,
|
|
28
|
+
apiKey: config.apiKey,
|
|
29
|
+
fetchProvider: config.fetchProvider,
|
|
30
|
+
logger: config.logger || (() => { }),
|
|
31
|
+
timeoutMs: config.timeoutMs ?? 5000,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Register a session with AgentShield
|
|
36
|
+
*
|
|
37
|
+
* This is a fire-and-forget operation. Failures are logged but don't throw.
|
|
38
|
+
* The method returns quickly and doesn't block the caller.
|
|
39
|
+
*
|
|
40
|
+
* @param request - Session registration request data
|
|
41
|
+
* @returns Result indicating success or failure
|
|
42
|
+
*/
|
|
43
|
+
async registerSession(request) {
|
|
44
|
+
const sessionId = request.session_id;
|
|
45
|
+
try {
|
|
46
|
+
// Validate request
|
|
47
|
+
const validationResult = agentshield_api_1.registerSessionRequestSchema.safeParse(request);
|
|
48
|
+
if (!validationResult.success) {
|
|
49
|
+
const errorMsg = `Invalid session registration request: ${validationResult.error.message}`;
|
|
50
|
+
this.config.logger("[SessionRegistration] Validation failed", {
|
|
51
|
+
sessionId,
|
|
52
|
+
error: errorMsg,
|
|
53
|
+
});
|
|
54
|
+
return { success: false, sessionId, error: errorMsg };
|
|
55
|
+
}
|
|
56
|
+
const url = `${this.config.baseUrl}${agentshield_api_1.AGENTSHIELD_ENDPOINTS.SESSIONS}`;
|
|
57
|
+
this.config.logger("[SessionRegistration] Registering session", {
|
|
58
|
+
sessionId,
|
|
59
|
+
agentDid: request.agent_did,
|
|
60
|
+
clientName: request.client_info.name,
|
|
61
|
+
url,
|
|
62
|
+
});
|
|
63
|
+
// ✅ EMPIRICAL PROOF: Prepare request headers with correct auth format
|
|
64
|
+
const requestHeaders = {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
"X-AgentShield-Key": this.config.apiKey, // Fixed: Use X-AgentShield-Key instead of Authorization: Bearer
|
|
67
|
+
};
|
|
68
|
+
// ✅ EMPIRICAL PROOF: Log exact request details (sanitized for security)
|
|
69
|
+
const sanitizedHeaders = {
|
|
70
|
+
...requestHeaders,
|
|
71
|
+
"X-AgentShield-Key": `${this.config.apiKey.slice(0, 8)}...${this.config.apiKey.slice(-4)}`, // Show first 8 and last 4 chars
|
|
72
|
+
};
|
|
73
|
+
const sanitizedBody = {
|
|
74
|
+
session_id: request.session_id,
|
|
75
|
+
agent_did: request.agent_did,
|
|
76
|
+
project_id: request.project_id,
|
|
77
|
+
created_at: request.created_at,
|
|
78
|
+
client_info: request.client_info,
|
|
79
|
+
client_identity: request.client_identity,
|
|
80
|
+
server_did: request.server_did,
|
|
81
|
+
ttl_minutes: request.ttl_minutes,
|
|
82
|
+
};
|
|
83
|
+
this.config.logger("[SessionRegistration] 🔍 EMPIRICAL DEBUG - Request details", {
|
|
84
|
+
url,
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: sanitizedHeaders,
|
|
87
|
+
headerKeys: Object.keys(requestHeaders),
|
|
88
|
+
authHeaderPresent: !!requestHeaders["X-AgentShield-Key"],
|
|
89
|
+
authHeaderName: "X-AgentShield-Key",
|
|
90
|
+
body: sanitizedBody,
|
|
91
|
+
bodySize: JSON.stringify(request).length,
|
|
92
|
+
});
|
|
93
|
+
// Make the request with timeout
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
96
|
+
try {
|
|
97
|
+
const response = await this.config.fetchProvider.fetch(url, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: requestHeaders,
|
|
100
|
+
body: JSON.stringify(request),
|
|
101
|
+
signal: controller.signal,
|
|
102
|
+
});
|
|
103
|
+
clearTimeout(timeoutId);
|
|
104
|
+
// ✅ EMPIRICAL PROOF: Capture exact response details
|
|
105
|
+
const responseHeaders = {};
|
|
106
|
+
response.headers.forEach((value, key) => {
|
|
107
|
+
responseHeaders[key] = value;
|
|
108
|
+
});
|
|
109
|
+
const responseText = await response
|
|
110
|
+
.text()
|
|
111
|
+
.catch(() => "Failed to read response");
|
|
112
|
+
let responseBody;
|
|
113
|
+
try {
|
|
114
|
+
responseBody = JSON.parse(responseText);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
responseBody = responseText;
|
|
118
|
+
}
|
|
119
|
+
// ✅ EMPIRICAL PROOF: Log exact response details
|
|
120
|
+
this.config.logger("[SessionRegistration] 🔍 EMPIRICAL DEBUG - Response details", {
|
|
121
|
+
status: response.status,
|
|
122
|
+
statusText: response.statusText,
|
|
123
|
+
ok: response.ok,
|
|
124
|
+
headers: responseHeaders,
|
|
125
|
+
body: responseBody,
|
|
126
|
+
bodyLength: responseText.length,
|
|
127
|
+
clientName: request.client_info.name,
|
|
128
|
+
});
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
// Log error but don't throw - this is fire-and-forget
|
|
131
|
+
this.config.logger("[SessionRegistration] Registration failed", {
|
|
132
|
+
sessionId,
|
|
133
|
+
status: response.status,
|
|
134
|
+
error: responseText,
|
|
135
|
+
// ✅ EMPIRICAL PROOF: Include full response details
|
|
136
|
+
responseHeaders,
|
|
137
|
+
responseBody,
|
|
138
|
+
clientName: request.client_info.name,
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
sessionId,
|
|
143
|
+
error: `HTTP ${response.status}: ${responseText}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Parse response (using already-captured responseBody)
|
|
147
|
+
const responseData = responseBody;
|
|
148
|
+
const parseResult = agentshield_api_1.registerSessionResponseSchema.safeParse(responseData.data ||
|
|
149
|
+
responseData);
|
|
150
|
+
if (!parseResult.success) {
|
|
151
|
+
this.config.logger("[SessionRegistration] Invalid response format", {
|
|
152
|
+
sessionId,
|
|
153
|
+
response: responseData,
|
|
154
|
+
});
|
|
155
|
+
// Still consider it a success if we got a 200 OK
|
|
156
|
+
return { success: true, sessionId };
|
|
157
|
+
}
|
|
158
|
+
this.config.logger("[SessionRegistration] Session registered", {
|
|
159
|
+
sessionId,
|
|
160
|
+
registered: parseResult.data.registered,
|
|
161
|
+
});
|
|
162
|
+
return { success: true, sessionId };
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
clearTimeout(timeoutId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
// Handle abort/timeout
|
|
170
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
171
|
+
this.config.logger("[SessionRegistration] Request timed out", {
|
|
172
|
+
sessionId,
|
|
173
|
+
timeoutMs: this.config.timeoutMs,
|
|
174
|
+
});
|
|
175
|
+
return { success: false, sessionId, error: "Request timed out" };
|
|
176
|
+
}
|
|
177
|
+
// Log any other error
|
|
178
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
179
|
+
this.config.logger("[SessionRegistration] Unexpected error", {
|
|
180
|
+
sessionId,
|
|
181
|
+
error: errorMsg,
|
|
182
|
+
});
|
|
183
|
+
return { success: false, sessionId, error: errorMsg };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Fire-and-forget session registration
|
|
188
|
+
*
|
|
189
|
+
* Starts registration in the background without waiting for completion.
|
|
190
|
+
* Useful when you want to register a session but not delay the response.
|
|
191
|
+
*
|
|
192
|
+
* @param request - Session registration request data
|
|
193
|
+
*/
|
|
194
|
+
registerSessionAsync(request) {
|
|
195
|
+
// Start registration in background - don't await
|
|
196
|
+
this.registerSession(request).catch((error) => {
|
|
197
|
+
// This should never happen since registerSession catches all errors,
|
|
198
|
+
// but just in case
|
|
199
|
+
this.config.logger("[SessionRegistration] Background registration failed", {
|
|
200
|
+
sessionId: request.session_id,
|
|
201
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.SessionRegistrationService = SessionRegistrationService;
|
|
207
|
+
/**
|
|
208
|
+
* Create a session registration service from common runtime config
|
|
209
|
+
*
|
|
210
|
+
* Helper function to create the service from typical environment config.
|
|
211
|
+
*/
|
|
212
|
+
function createSessionRegistrationService(options) {
|
|
213
|
+
// Validate required config
|
|
214
|
+
if (!options.apiUrl || !options.apiKey) {
|
|
215
|
+
options.logger?.("[SessionRegistration] Missing required config - session registration disabled", {
|
|
216
|
+
hasApiUrl: !!options.apiUrl,
|
|
217
|
+
hasApiKey: !!options.apiKey,
|
|
218
|
+
});
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return new SessionRegistrationService({
|
|
222
|
+
baseUrl: options.apiUrl,
|
|
223
|
+
apiKey: options.apiKey,
|
|
224
|
+
fetchProvider: options.fetchProvider,
|
|
225
|
+
logger: options.logger,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=session-registration.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-registration.service.js","sourceRoot":"","sources":["../../src/services/session-registration.service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -56,6 +56,17 @@ export type {
|
|
|
56
56
|
AccessControlApiServiceMetrics,
|
|
57
57
|
} from "./services/access-control.service";
|
|
58
58
|
|
|
59
|
+
// Session Registration Service
|
|
60
|
+
export {
|
|
61
|
+
SessionRegistrationService,
|
|
62
|
+
createSessionRegistrationService,
|
|
63
|
+
} from "./services/session-registration.service";
|
|
64
|
+
|
|
65
|
+
export type {
|
|
66
|
+
SessionRegistrationServiceConfig,
|
|
67
|
+
SessionRegistrationResult,
|
|
68
|
+
} from "./services/session-registration.service";
|
|
69
|
+
|
|
59
70
|
// OAuth Config Service (Phase 1)
|
|
60
71
|
export { OAuthConfigService } from "./services/oauth-config.service";
|
|
61
72
|
|
package/src/services/index.ts
CHANGED
|
@@ -7,3 +7,12 @@ export type {
|
|
|
7
7
|
AccessControlApiServiceMetrics,
|
|
8
8
|
} from './access-control.service.js';
|
|
9
9
|
|
|
10
|
+
export {
|
|
11
|
+
SessionRegistrationService,
|
|
12
|
+
createSessionRegistrationService,
|
|
13
|
+
} from './session-registration.service.js';
|
|
14
|
+
export type {
|
|
15
|
+
SessionRegistrationServiceConfig,
|
|
16
|
+
SessionRegistrationResult,
|
|
17
|
+
} from './session-registration.service.js';
|
|
18
|
+
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Registration Service
|
|
3
|
+
*
|
|
4
|
+
* Registers MCP sessions with the AgentShield dashboard, enabling
|
|
5
|
+
* visibility into which MCP clients are connecting to agents.
|
|
6
|
+
*
|
|
7
|
+
* This is a fire-and-forget service - session registration should not
|
|
8
|
+
* block tool execution or affect the user experience.
|
|
9
|
+
*
|
|
10
|
+
* @package @kya-os/mcp-i-core
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
RegisterSessionRequest,
|
|
15
|
+
RegisterSessionResponse,
|
|
16
|
+
} from "@kya-os/contracts/agentshield-api";
|
|
17
|
+
import {
|
|
18
|
+
registerSessionRequestSchema,
|
|
19
|
+
registerSessionResponseSchema,
|
|
20
|
+
AGENTSHIELD_ENDPOINTS,
|
|
21
|
+
} from "@kya-os/contracts/agentshield-api";
|
|
22
|
+
import type { FetchProvider } from "../providers/base.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for the session registration service
|
|
26
|
+
*/
|
|
27
|
+
export interface SessionRegistrationServiceConfig {
|
|
28
|
+
/** Base URL for the AgentShield API (e.g., "https://kya.vouched.id") */
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
|
|
31
|
+
/** API key for authentication */
|
|
32
|
+
apiKey: string;
|
|
33
|
+
|
|
34
|
+
/** Fetch provider for making HTTP requests (platform-agnostic) */
|
|
35
|
+
fetchProvider: FetchProvider;
|
|
36
|
+
|
|
37
|
+
/** Optional logger callback for diagnostics */
|
|
38
|
+
logger?: (message: string, data?: unknown) => void;
|
|
39
|
+
|
|
40
|
+
/** Timeout in milliseconds for the registration request (default: 5000) */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Result of a session registration attempt
|
|
46
|
+
*/
|
|
47
|
+
export interface SessionRegistrationResult {
|
|
48
|
+
/** Whether registration was successful */
|
|
49
|
+
success: boolean;
|
|
50
|
+
/** Session ID that was registered */
|
|
51
|
+
sessionId: string;
|
|
52
|
+
/** Error message if registration failed */
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Session Registration Service
|
|
58
|
+
*
|
|
59
|
+
* Registers MCP sessions with AgentShield for dashboard visibility.
|
|
60
|
+
* Designed to be non-blocking - failures are logged but don't throw.
|
|
61
|
+
*/
|
|
62
|
+
export class SessionRegistrationService {
|
|
63
|
+
private config: Required<
|
|
64
|
+
Omit<SessionRegistrationServiceConfig, "logger" | "timeoutMs">
|
|
65
|
+
> & {
|
|
66
|
+
logger: NonNullable<SessionRegistrationServiceConfig["logger"]>;
|
|
67
|
+
timeoutMs: number;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
constructor(config: SessionRegistrationServiceConfig) {
|
|
71
|
+
this.config = {
|
|
72
|
+
baseUrl: config.baseUrl,
|
|
73
|
+
apiKey: config.apiKey,
|
|
74
|
+
fetchProvider: config.fetchProvider,
|
|
75
|
+
logger: config.logger || (() => {}),
|
|
76
|
+
timeoutMs: config.timeoutMs ?? 5000,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Register a session with AgentShield
|
|
82
|
+
*
|
|
83
|
+
* This is a fire-and-forget operation. Failures are logged but don't throw.
|
|
84
|
+
* The method returns quickly and doesn't block the caller.
|
|
85
|
+
*
|
|
86
|
+
* @param request - Session registration request data
|
|
87
|
+
* @returns Result indicating success or failure
|
|
88
|
+
*/
|
|
89
|
+
async registerSession(
|
|
90
|
+
request: RegisterSessionRequest
|
|
91
|
+
): Promise<SessionRegistrationResult> {
|
|
92
|
+
const sessionId = request.session_id;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// Validate request
|
|
96
|
+
const validationResult = registerSessionRequestSchema.safeParse(request);
|
|
97
|
+
if (!validationResult.success) {
|
|
98
|
+
const errorMsg = `Invalid session registration request: ${validationResult.error.message}`;
|
|
99
|
+
this.config.logger("[SessionRegistration] Validation failed", {
|
|
100
|
+
sessionId,
|
|
101
|
+
error: errorMsg,
|
|
102
|
+
});
|
|
103
|
+
return { success: false, sessionId, error: errorMsg };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const url = `${this.config.baseUrl}${AGENTSHIELD_ENDPOINTS.SESSIONS}`;
|
|
107
|
+
|
|
108
|
+
this.config.logger("[SessionRegistration] Registering session", {
|
|
109
|
+
sessionId,
|
|
110
|
+
agentDid: request.agent_did,
|
|
111
|
+
clientName: request.client_info.name,
|
|
112
|
+
url,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ✅ EMPIRICAL PROOF: Prepare request headers with correct auth format
|
|
116
|
+
const requestHeaders = {
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
"X-AgentShield-Key": this.config.apiKey, // Fixed: Use X-AgentShield-Key instead of Authorization: Bearer
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// ✅ EMPIRICAL PROOF: Log exact request details (sanitized for security)
|
|
122
|
+
const sanitizedHeaders = {
|
|
123
|
+
...requestHeaders,
|
|
124
|
+
"X-AgentShield-Key": `${this.config.apiKey.slice(0, 8)}...${this.config.apiKey.slice(-4)}`, // Show first 8 and last 4 chars
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const sanitizedBody = {
|
|
128
|
+
session_id: request.session_id,
|
|
129
|
+
agent_did: request.agent_did,
|
|
130
|
+
project_id: request.project_id,
|
|
131
|
+
created_at: request.created_at,
|
|
132
|
+
client_info: request.client_info,
|
|
133
|
+
client_identity: request.client_identity,
|
|
134
|
+
server_did: request.server_did,
|
|
135
|
+
ttl_minutes: request.ttl_minutes,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
this.config.logger(
|
|
139
|
+
"[SessionRegistration] 🔍 EMPIRICAL DEBUG - Request details",
|
|
140
|
+
{
|
|
141
|
+
url,
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: sanitizedHeaders,
|
|
144
|
+
headerKeys: Object.keys(requestHeaders),
|
|
145
|
+
authHeaderPresent: !!requestHeaders["X-AgentShield-Key"],
|
|
146
|
+
authHeaderName: "X-AgentShield-Key",
|
|
147
|
+
body: sanitizedBody,
|
|
148
|
+
bodySize: JSON.stringify(request).length,
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Make the request with timeout
|
|
153
|
+
const controller = new AbortController();
|
|
154
|
+
const timeoutId = setTimeout(
|
|
155
|
+
() => controller.abort(),
|
|
156
|
+
this.config.timeoutMs
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const response = await this.config.fetchProvider.fetch(url, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: requestHeaders,
|
|
163
|
+
body: JSON.stringify(request),
|
|
164
|
+
signal: controller.signal,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
clearTimeout(timeoutId);
|
|
168
|
+
|
|
169
|
+
// ✅ EMPIRICAL PROOF: Capture exact response details
|
|
170
|
+
const responseHeaders: Record<string, string> = {};
|
|
171
|
+
response.headers.forEach((value, key) => {
|
|
172
|
+
responseHeaders[key] = value;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const responseText = await response
|
|
176
|
+
.text()
|
|
177
|
+
.catch(() => "Failed to read response");
|
|
178
|
+
let responseBody: unknown;
|
|
179
|
+
try {
|
|
180
|
+
responseBody = JSON.parse(responseText);
|
|
181
|
+
} catch {
|
|
182
|
+
responseBody = responseText;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ✅ EMPIRICAL PROOF: Log exact response details
|
|
186
|
+
this.config.logger(
|
|
187
|
+
"[SessionRegistration] 🔍 EMPIRICAL DEBUG - Response details",
|
|
188
|
+
{
|
|
189
|
+
status: response.status,
|
|
190
|
+
statusText: response.statusText,
|
|
191
|
+
ok: response.ok,
|
|
192
|
+
headers: responseHeaders,
|
|
193
|
+
body: responseBody,
|
|
194
|
+
bodyLength: responseText.length,
|
|
195
|
+
clientName: request.client_info.name,
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
// Log error but don't throw - this is fire-and-forget
|
|
201
|
+
this.config.logger("[SessionRegistration] Registration failed", {
|
|
202
|
+
sessionId,
|
|
203
|
+
status: response.status,
|
|
204
|
+
error: responseText,
|
|
205
|
+
// ✅ EMPIRICAL PROOF: Include full response details
|
|
206
|
+
responseHeaders,
|
|
207
|
+
responseBody,
|
|
208
|
+
clientName: request.client_info.name,
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
success: false,
|
|
212
|
+
sessionId,
|
|
213
|
+
error: `HTTP ${response.status}: ${responseText}`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Parse response (using already-captured responseBody)
|
|
218
|
+
const responseData = responseBody as
|
|
219
|
+
| { data?: RegisterSessionResponse }
|
|
220
|
+
| RegisterSessionResponse;
|
|
221
|
+
const parseResult = registerSessionResponseSchema.safeParse(
|
|
222
|
+
(responseData as { data?: RegisterSessionResponse }).data ||
|
|
223
|
+
responseData
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
if (!parseResult.success) {
|
|
227
|
+
this.config.logger("[SessionRegistration] Invalid response format", {
|
|
228
|
+
sessionId,
|
|
229
|
+
response: responseData,
|
|
230
|
+
});
|
|
231
|
+
// Still consider it a success if we got a 200 OK
|
|
232
|
+
return { success: true, sessionId };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.config.logger("[SessionRegistration] Session registered", {
|
|
236
|
+
sessionId,
|
|
237
|
+
registered: parseResult.data.registered,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return { success: true, sessionId };
|
|
241
|
+
} finally {
|
|
242
|
+
clearTimeout(timeoutId);
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
// Handle abort/timeout
|
|
246
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
247
|
+
this.config.logger("[SessionRegistration] Request timed out", {
|
|
248
|
+
sessionId,
|
|
249
|
+
timeoutMs: this.config.timeoutMs,
|
|
250
|
+
});
|
|
251
|
+
return { success: false, sessionId, error: "Request timed out" };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Log any other error
|
|
255
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
256
|
+
this.config.logger("[SessionRegistration] Unexpected error", {
|
|
257
|
+
sessionId,
|
|
258
|
+
error: errorMsg,
|
|
259
|
+
});
|
|
260
|
+
return { success: false, sessionId, error: errorMsg };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Fire-and-forget session registration
|
|
266
|
+
*
|
|
267
|
+
* Starts registration in the background without waiting for completion.
|
|
268
|
+
* Useful when you want to register a session but not delay the response.
|
|
269
|
+
*
|
|
270
|
+
* @param request - Session registration request data
|
|
271
|
+
*/
|
|
272
|
+
registerSessionAsync(request: RegisterSessionRequest): void {
|
|
273
|
+
// Start registration in background - don't await
|
|
274
|
+
this.registerSession(request).catch((error) => {
|
|
275
|
+
// This should never happen since registerSession catches all errors,
|
|
276
|
+
// but just in case
|
|
277
|
+
this.config.logger(
|
|
278
|
+
"[SessionRegistration] Background registration failed",
|
|
279
|
+
{
|
|
280
|
+
sessionId: request.session_id,
|
|
281
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Create a session registration service from common runtime config
|
|
290
|
+
*
|
|
291
|
+
* Helper function to create the service from typical environment config.
|
|
292
|
+
*/
|
|
293
|
+
export function createSessionRegistrationService(options: {
|
|
294
|
+
apiUrl: string;
|
|
295
|
+
apiKey: string;
|
|
296
|
+
fetchProvider: FetchProvider;
|
|
297
|
+
logger?: (message: string, data?: unknown) => void;
|
|
298
|
+
}): SessionRegistrationService | null {
|
|
299
|
+
// Validate required config
|
|
300
|
+
if (!options.apiUrl || !options.apiKey) {
|
|
301
|
+
options.logger?.(
|
|
302
|
+
"[SessionRegistration] Missing required config - session registration disabled",
|
|
303
|
+
{
|
|
304
|
+
hasApiUrl: !!options.apiUrl,
|
|
305
|
+
hasApiKey: !!options.apiKey,
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return new SessionRegistrationService({
|
|
312
|
+
baseUrl: options.apiUrl,
|
|
313
|
+
apiKey: options.apiKey,
|
|
314
|
+
fetchProvider: options.fetchProvider,
|
|
315
|
+
logger: options.logger,
|
|
316
|
+
});
|
|
317
|
+
}
|