@jagilber-org/index-server 1.22.0 → 1.26.1
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/CHANGELOG.md +87 -2
- package/CODE_OF_CONDUCT.md +2 -0
- package/CONTRIBUTING.md +32 -2
- package/README.md +83 -20
- package/SECURITY.md +17 -5
- package/dist/config/dashboardConfig.d.ts +3 -0
- package/dist/config/dashboardConfig.js +3 -0
- package/dist/config/defaultValues.d.ts +1 -1
- package/dist/config/defaultValues.js +1 -1
- package/dist/config/featureConfig.d.ts +2 -0
- package/dist/config/featureConfig.js +6 -1
- package/dist/config/runtimeConfig.d.ts +1 -1
- package/dist/config/runtimeConfig.js +8 -9
- package/dist/dashboard/client/admin.html +173 -54
- package/dist/dashboard/client/css/admin.css +151 -0
- package/dist/dashboard/client/js/admin.auth.js +25 -11
- package/dist/dashboard/client/js/admin.config.js +1 -1
- package/dist/dashboard/client/js/admin.feedback.js +328 -0
- package/dist/dashboard/client/js/admin.graph.js +120 -18
- package/dist/dashboard/client/js/admin.instructions.js +27 -13
- package/dist/dashboard/client/js/admin.logs.js +1 -5
- package/dist/dashboard/client/js/admin.maintenance.js +53 -8
- package/dist/dashboard/client/js/admin.messaging.js +1 -4
- package/dist/dashboard/client/js/admin.overview.js +5 -1
- package/dist/dashboard/client/js/admin.sessions.js +1 -1
- package/dist/dashboard/client/js/admin.utils.js +43 -1
- package/dist/dashboard/client/js/mermaid.min.js +813 -537
- package/dist/dashboard/export/DataExporter.js +2 -1
- package/dist/dashboard/server/AdminPanel.d.ts +3 -0
- package/dist/dashboard/server/AdminPanel.js +132 -35
- package/dist/dashboard/server/ApiRoutes.js +40 -9
- package/dist/dashboard/server/DashboardServer.js +1 -1
- package/dist/dashboard/server/FileMetricsStorage.d.ts +19 -0
- package/dist/dashboard/server/FileMetricsStorage.js +52 -5
- package/dist/dashboard/server/HttpTransport.js +6 -0
- package/dist/dashboard/server/InstanceManager.js +7 -2
- package/dist/dashboard/server/KnowledgeStore.js +7 -2
- package/dist/dashboard/server/MetricsCollector.d.ts +16 -0
- package/dist/dashboard/server/MetricsCollector.js +113 -17
- package/dist/dashboard/server/legacyDashboardHtml.js +7 -2
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.d.ts +1 -1
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.js +8 -3
- package/dist/dashboard/server/routes/admin.feedback.routes.d.ts +15 -0
- package/dist/dashboard/server/routes/admin.feedback.routes.js +188 -0
- package/dist/dashboard/server/routes/admin.routes.js +35 -27
- package/dist/dashboard/server/routes/alerts.routes.js +4 -3
- package/dist/dashboard/server/routes/api.feedback.routes.js +2 -1
- package/dist/dashboard/server/routes/api.usage.routes.js +8 -7
- package/dist/dashboard/server/routes/embeddings.routes.d.ts +2 -1
- package/dist/dashboard/server/routes/embeddings.routes.js +18 -9
- package/dist/dashboard/server/routes/graph.routes.js +10 -13
- package/dist/dashboard/server/routes/index.d.ts +1 -0
- package/dist/dashboard/server/routes/index.js +74 -39
- package/dist/dashboard/server/routes/instances.routes.js +2 -1
- package/dist/dashboard/server/routes/instructions.routes.js +46 -27
- package/dist/dashboard/server/routes/knowledge.routes.js +4 -3
- package/dist/dashboard/server/routes/logs.routes.js +5 -4
- package/dist/dashboard/server/routes/messaging.routes.js +15 -14
- package/dist/dashboard/server/routes/metrics.routes.js +14 -13
- package/dist/dashboard/server/routes/scripts.routes.js +6 -3
- package/dist/dashboard/server/routes/status.routes.js +25 -6
- package/dist/dashboard/server/routes/synthetic.routes.js +3 -2
- package/dist/dashboard/server/routes/usage.routes.js +2 -1
- package/dist/dashboard/server/utils/escapeHtml.d.ts +1 -0
- package/dist/dashboard/server/utils/escapeHtml.js +11 -0
- package/dist/dashboard/server/utils/pathContainment.d.ts +1 -0
- package/dist/dashboard/server/utils/pathContainment.js +15 -0
- package/dist/dashboard/server/wsInit.js +2 -2
- package/dist/lib/mcpStdioLogging.d.ts +165 -0
- package/dist/lib/mcpStdioLogging.js +287 -0
- package/dist/schemas/index.d.ts +37 -2
- package/dist/schemas/index.js +27 -3
- package/dist/server/backgroundServicesStartup.d.ts +7 -1
- package/dist/server/backgroundServicesStartup.js +25 -8
- package/dist/server/certInit.d.ts +97 -0
- package/dist/server/certInit.js +359 -0
- package/dist/server/certInit.types.d.ts +92 -0
- package/dist/server/certInit.types.js +34 -0
- package/dist/server/handshake/fallbackFrames.d.ts +31 -0
- package/dist/server/handshake/fallbackFrames.js +38 -0
- package/dist/server/handshake/initializeDetector.d.ts +31 -0
- package/dist/server/handshake/initializeDetector.js +88 -0
- package/dist/server/handshake/protocol.d.ts +15 -0
- package/dist/server/handshake/protocol.js +37 -0
- package/dist/server/handshake/readyEmitter.d.ts +6 -0
- package/dist/server/handshake/readyEmitter.js +88 -0
- package/dist/server/handshake/safetyFallbacks.d.ts +1 -0
- package/dist/server/handshake/safetyFallbacks.js +134 -0
- package/dist/server/handshake/stdinSniffer.d.ts +1 -0
- package/dist/server/handshake/stdinSniffer.js +260 -0
- package/dist/server/handshake/tracing.d.ts +16 -0
- package/dist/server/handshake/tracing.js +95 -0
- package/dist/server/handshakeManager.d.ts +23 -23
- package/dist/server/handshakeManager.js +36 -466
- package/dist/server/index-server.d.ts +23 -0
- package/dist/server/index-server.js +194 -9
- package/dist/server/mcpReadOnlySurfaces.d.ts +44 -0
- package/dist/server/mcpReadOnlySurfaces.js +297 -0
- package/dist/server/sdkServer.js +69 -7
- package/dist/server/transport.d.ts +5 -6
- package/dist/server/transport.js +46 -64
- package/dist/server/transportFactory.d.ts +3 -9
- package/dist/server/transportFactory.js +18 -380
- package/dist/services/atomicFs.d.ts +3 -0
- package/dist/services/atomicFs.js +171 -13
- package/dist/services/auditLog.d.ts +17 -2
- package/dist/services/auditLog.js +75 -14
- package/dist/services/bootstrapGating.js +1 -1
- package/dist/services/categoryRules.d.ts +10 -0
- package/dist/services/categoryRules.js +17 -0
- package/dist/services/classificationService.js +7 -5
- package/dist/services/embeddingService.d.ts +27 -11
- package/dist/services/embeddingService.js +51 -14
- package/dist/services/feedbackStorage.d.ts +39 -0
- package/dist/services/feedbackStorage.js +88 -0
- package/dist/services/handlers/instructions.add.js +429 -317
- package/dist/services/handlers/instructions.groom.js +128 -31
- package/dist/services/handlers/instructions.import.js +56 -23
- package/dist/services/handlers/instructions.patch.js +43 -32
- package/dist/services/handlers/instructions.query.js +20 -29
- package/dist/services/handlers/instructions.shared.d.ts +54 -0
- package/dist/services/handlers/instructions.shared.js +126 -1
- package/dist/services/handlers.activation.js +83 -81
- package/dist/services/handlers.dashboardConfig.d.ts +2 -2
- package/dist/services/handlers.dashboardConfig.js +1 -2
- package/dist/services/handlers.diagnostics.js +75 -54
- package/dist/services/handlers.feedback.d.ts +4 -11
- package/dist/services/handlers.feedback.js +11 -333
- package/dist/services/handlers.gates.js +69 -37
- package/dist/services/handlers.graph.js +2 -2
- package/dist/services/handlers.help.js +2 -2
- package/dist/services/handlers.instructionSchema.js +4 -2
- package/dist/services/handlers.integrity.js +42 -22
- package/dist/services/handlers.messaging.js +1 -1
- package/dist/services/handlers.metrics.js +51 -6
- package/dist/services/handlers.prompt.js +10 -2
- package/dist/services/handlers.search.js +94 -44
- package/dist/services/handlers.trace.js +1 -1
- package/dist/services/handlers.usage.js +38 -7
- package/dist/services/indexContext.d.ts +21 -1
- package/dist/services/indexContext.js +267 -82
- package/dist/services/indexLoader.d.ts +1 -0
- package/dist/services/indexLoader.js +28 -8
- package/dist/services/instructionRecordValidation.d.ts +39 -0
- package/dist/services/instructionRecordValidation.js +388 -0
- package/dist/services/instructions.dispatcher.js +4 -4
- package/dist/services/loaderSchemaValidator.d.ts +15 -0
- package/dist/services/loaderSchemaValidator.js +69 -0
- package/dist/services/logger.js +11 -2
- package/dist/services/mcpLogBridge.d.ts +49 -0
- package/dist/services/mcpLogBridge.js +83 -0
- package/dist/services/ownershipService.js +18 -8
- package/dist/services/performanceBaseline.js +23 -22
- package/dist/services/promptReviewService.d.ts +3 -1
- package/dist/services/promptReviewService.js +41 -13
- package/dist/services/regexSafety.d.ts +6 -0
- package/dist/services/regexSafety.js +46 -0
- package/dist/services/seedBootstrap.js +4 -4
- package/dist/services/storage/factory.d.ts +14 -1
- package/dist/services/storage/factory.js +61 -1
- package/dist/services/storage/jsonEmbeddingStore.d.ts +15 -0
- package/dist/services/storage/jsonEmbeddingStore.js +83 -0
- package/dist/services/storage/jsonFileStore.d.ts +3 -1
- package/dist/services/storage/jsonFileStore.js +8 -6
- package/dist/services/storage/migrationEngine.d.ts +13 -0
- package/dist/services/storage/migrationEngine.js +31 -0
- package/dist/services/storage/sqliteEmbeddingStore.d.ts +30 -0
- package/dist/services/storage/sqliteEmbeddingStore.js +222 -0
- package/dist/services/storage/sqliteStore.d.ts +3 -1
- package/dist/services/storage/sqliteStore.js +2 -2
- package/dist/services/storage/types.d.ts +48 -1
- package/dist/services/toolRegistry.js +77 -67
- package/dist/services/toolRegistry.zod.js +89 -86
- package/dist/services/tracing.js +5 -4
- package/dist/utils/envUtils.d.ts +4 -0
- package/dist/utils/envUtils.js +7 -0
- package/dist/utils/memoryMonitor.js +11 -10
- package/package.json +11 -4
- package/schemas/instruction.schema.json +38 -1
- package/scripts/copy-dashboard-assets.mjs +1 -1
- package/scripts/dist/README.md +1 -1
- package/scripts/setup-wizard.mjs +781 -0
- package/server.json +1 -0
- package/dist/externalClientLib.d.ts +0 -1
- package/dist/externalClientLib.js +0 -2
- package/dist/portableClientWrapper.d.ts +0 -1
- package/dist/portableClientWrapper.js +0 -2
- package/dist/services/indexingService.d.ts +0 -1
- package/dist/services/indexingService.js +0 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Supported protocol versions, ordered most-preferred-first. */
|
|
2
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly string[];
|
|
3
|
+
/**
|
|
4
|
+
* Negotiate a protocol version with graceful fallback.
|
|
5
|
+
* Returns the requested version when supported, otherwise the most preferred
|
|
6
|
+
* supported version.
|
|
7
|
+
*/
|
|
8
|
+
export declare function negotiateProtocolVersion(requested?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* By default ALL non-primary ready fallbacks (watchdogs, safety timeouts,
|
|
11
|
+
* stdin sniff synthetic initialize, unconditional init fallbacks, etc.) are
|
|
12
|
+
* disabled. Enable via INDEX_SERVER_INIT_FEATURES=handshakeFallbacks to
|
|
13
|
+
* re-enable the safety nets.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isHandshakeFallbacksEnabled(): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SUPPORTED_PROTOCOL_VERSIONS = void 0;
|
|
4
|
+
exports.negotiateProtocolVersion = negotiateProtocolVersion;
|
|
5
|
+
exports.isHandshakeFallbacksEnabled = isHandshakeFallbacksEnabled;
|
|
6
|
+
/**
|
|
7
|
+
* Pure protocol-version negotiation utilities for the MCP handshake.
|
|
8
|
+
* No I/O, no module-level mutable state.
|
|
9
|
+
*/
|
|
10
|
+
const runtimeConfig_1 = require("../../config/runtimeConfig");
|
|
11
|
+
/** Supported protocol versions, ordered most-preferred-first. */
|
|
12
|
+
exports.SUPPORTED_PROTOCOL_VERSIONS = [
|
|
13
|
+
'2025-06-18',
|
|
14
|
+
'2024-11-05',
|
|
15
|
+
'2024-10-07',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* Negotiate a protocol version with graceful fallback.
|
|
19
|
+
* Returns the requested version when supported, otherwise the most preferred
|
|
20
|
+
* supported version.
|
|
21
|
+
*/
|
|
22
|
+
function negotiateProtocolVersion(requested) {
|
|
23
|
+
if (!requested)
|
|
24
|
+
return exports.SUPPORTED_PROTOCOL_VERSIONS[0];
|
|
25
|
+
if (exports.SUPPORTED_PROTOCOL_VERSIONS.includes(requested))
|
|
26
|
+
return requested;
|
|
27
|
+
return exports.SUPPORTED_PROTOCOL_VERSIONS[0];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* By default ALL non-primary ready fallbacks (watchdogs, safety timeouts,
|
|
31
|
+
* stdin sniff synthetic initialize, unconditional init fallbacks, etc.) are
|
|
32
|
+
* disabled. Enable via INDEX_SERVER_INIT_FEATURES=handshakeFallbacks to
|
|
33
|
+
* re-enable the safety nets.
|
|
34
|
+
*/
|
|
35
|
+
function isHandshakeFallbacksEnabled() {
|
|
36
|
+
return (0, runtimeConfig_1.getRuntimeConfig)().initFeatures.has('handshakeFallbacks');
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idempotent emission of `server/ready`. Invoked ONLY after the initialize
|
|
3
|
+
* response has been (or is about to be) flushed unless the caller is one of
|
|
4
|
+
* the explicit fallback paths AND fallback safety nets are enabled.
|
|
5
|
+
*/
|
|
6
|
+
export declare function emitReadyGlobal(server: any, reason: string): void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emitReadyGlobal = emitReadyGlobal;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
+
/**
|
|
6
|
+
* `server/ready` notification dispatcher. Owns the unique ordering and
|
|
7
|
+
* fallback logic for emitting the ready signal exactly once.
|
|
8
|
+
*/
|
|
9
|
+
const mcpLogBridge_1 = require("../../services/mcpLogBridge");
|
|
10
|
+
const protocol_1 = require("./protocol");
|
|
11
|
+
const tracing_1 = require("./tracing");
|
|
12
|
+
const FALLBACK_REASONS_ALLOWED_BEFORE_INIT_RESPONSE = new Set([
|
|
13
|
+
'unconditional-init-fallback',
|
|
14
|
+
'unconditional-init-fallback-direct',
|
|
15
|
+
'forced-init-fallback',
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Idempotent emission of `server/ready`. Invoked ONLY after the initialize
|
|
19
|
+
* response has been (or is about to be) flushed unless the caller is one of
|
|
20
|
+
* the explicit fallback paths AND fallback safety nets are enabled.
|
|
21
|
+
*/
|
|
22
|
+
function emitReadyGlobal(server, reason) {
|
|
23
|
+
try {
|
|
24
|
+
if (!server)
|
|
25
|
+
return;
|
|
26
|
+
if (server.__readyNotified)
|
|
27
|
+
return;
|
|
28
|
+
if (!server.__initResponseSent) {
|
|
29
|
+
if (!(0, protocol_1.isHandshakeFallbacksEnabled)())
|
|
30
|
+
return; // strict mode: never emit early
|
|
31
|
+
if (!FALLBACK_REASONS_ALLOWED_BEFORE_INIT_RESPONSE.has(reason))
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const v = server.__declaredVersion || server.version || '0.0.0';
|
|
35
|
+
server.__readyNotified = true;
|
|
36
|
+
(0, mcpLogBridge_1.activateMcpLogBridge)();
|
|
37
|
+
(0, tracing_1.record)('ready_emitted', { reason, version: v });
|
|
38
|
+
try {
|
|
39
|
+
process.stderr.write(`[ready] emit reason=${reason} version=${v}\n`);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
(0, tracing_1.handshakeError)('emitReadyGlobal:log', err);
|
|
43
|
+
}
|
|
44
|
+
const msg = { jsonrpc: '2.0', method: 'server/ready', params: { version: v } };
|
|
45
|
+
let dispatched = false;
|
|
46
|
+
try {
|
|
47
|
+
const t = server._transport;
|
|
48
|
+
if (t?.send) {
|
|
49
|
+
t.send(msg)?.catch?.((err) => {
|
|
50
|
+
(0, tracing_1.handshakeError)('transport.send(ready)', err);
|
|
51
|
+
});
|
|
52
|
+
dispatched = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
(0, tracing_1.handshakeError)('emitReadyGlobal:transport', err);
|
|
57
|
+
}
|
|
58
|
+
if (!dispatched) {
|
|
59
|
+
try {
|
|
60
|
+
server.sendNotification?.({ method: 'server/ready', params: { version: v } });
|
|
61
|
+
dispatched = true;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
(0, tracing_1.handshakeError)('emitReadyGlobal:sendNotification', err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!dispatched) {
|
|
68
|
+
try {
|
|
69
|
+
process.stdout.write(JSON.stringify(msg) + '\n');
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
(0, tracing_1.handshakeError)('emitReadyGlobal:stdout', err);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
if (typeof server.sendToolListChanged === 'function') {
|
|
77
|
+
server.sendToolListChanged();
|
|
78
|
+
(0, tracing_1.record)('list_changed_after_ready');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
(0, tracing_1.handshakeError)('emitReadyGlobal:sendToolListChanged', err);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
(0, tracing_1.handshakeError)('emitReadyGlobal:outer', err);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupSafetyFallbacks(server: any): void;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setupSafetyFallbacks = setupSafetyFallbacks;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
+
/**
|
|
6
|
+
* Safety fallback timers and `_oninitialize` SDK patch. Provides last-resort
|
|
7
|
+
* ready emission and unconditional initialize-response fabrication when no
|
|
8
|
+
* other path has produced one within tight deadlines.
|
|
9
|
+
*/
|
|
10
|
+
const runtimeConfig_1 = require("../../config/runtimeConfig");
|
|
11
|
+
const fallbackFrames_1 = require("./fallbackFrames");
|
|
12
|
+
const protocol_1 = require("./protocol");
|
|
13
|
+
const readyEmitter_1 = require("./readyEmitter");
|
|
14
|
+
const tracing_1 = require("./tracing");
|
|
15
|
+
const SAFETY_TIMEOUT_MS = 100;
|
|
16
|
+
const UNCONDITIONAL_FALLBACK_MS = 150;
|
|
17
|
+
const FALLBACK_NEGOTIATED_VERSION = '2024-11-05';
|
|
18
|
+
function setupSafetyFallbacks(server) {
|
|
19
|
+
if ((0, protocol_1.isHandshakeFallbacksEnabled)()) {
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
try {
|
|
22
|
+
if (server.__sawInitializeRequest &&
|
|
23
|
+
server.__initResponseSent &&
|
|
24
|
+
!server.__readyNotified) {
|
|
25
|
+
(0, tracing_1.handshakeLog)('safety_timeout_emit_attempt', {
|
|
26
|
+
label: 'safety-timeout-100ms',
|
|
27
|
+
sawInit: true,
|
|
28
|
+
initRespSent: true,
|
|
29
|
+
});
|
|
30
|
+
(0, readyEmitter_1.emitReadyGlobal)(server, 'safety-timeout-100ms');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
(0, tracing_1.handshakeError)('safetyFallback:100ms', err);
|
|
35
|
+
}
|
|
36
|
+
}, SAFETY_TIMEOUT_MS).unref?.();
|
|
37
|
+
}
|
|
38
|
+
if ((0, protocol_1.isHandshakeFallbacksEnabled)()) {
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
try {
|
|
41
|
+
const cfg = (0, runtimeConfig_1.getRuntimeConfig)();
|
|
42
|
+
const INIT_FALLBACK_ENABLED = cfg.initFeatures.has('initFallback');
|
|
43
|
+
if (cfg.trace.has('healthMixed') && !server.__initResponseSent) {
|
|
44
|
+
if (!INIT_FALLBACK_ENABLED) {
|
|
45
|
+
try {
|
|
46
|
+
process.stderr.write(`[diag] ${Date.now()} init_unconditional_fallback_skip gating_off\n`);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
(0, tracing_1.handshakeError)('safetyFallback:skipLog', err);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!server.__sawInitializeRequest) {
|
|
54
|
+
if (process.stderr && !server.__diagForcedInitLogged) {
|
|
55
|
+
server.__diagForcedInitLogged = true;
|
|
56
|
+
try {
|
|
57
|
+
process.stderr.write(`[diag] ${Date.now()} init_unconditional_fallback_emit id=1 reason=no_init_seen_150ms\n`);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
(0, tracing_1.handshakeError)('safetyFallback:noInitLog', err);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
try {
|
|
66
|
+
process.stderr.write(`[diag] ${Date.now()} init_unconditional_fallback_emit id=1 reason=init_seen_no_response_150ms\n`);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
(0, tracing_1.handshakeError)('safetyFallback:initSeenLog', err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const frame = (0, fallbackFrames_1.buildForcedInitResultFrame)(FALLBACK_NEGOTIATED_VERSION, 'unconditional-init-fallback', 1);
|
|
73
|
+
const tr = server._transport || server.__transportRef;
|
|
74
|
+
server.__initResponseSent = true;
|
|
75
|
+
if (tr && typeof tr.send === 'function') {
|
|
76
|
+
Promise.resolve(tr.send(frame))
|
|
77
|
+
.then(() => {
|
|
78
|
+
if (!server.__readyNotified)
|
|
79
|
+
(0, readyEmitter_1.emitReadyGlobal)(server, 'unconditional-init-fallback');
|
|
80
|
+
})
|
|
81
|
+
.catch((err) => {
|
|
82
|
+
(0, tracing_1.handshakeError)('unconditionalFallback:transport.send', err);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
try {
|
|
87
|
+
process.stdout.write(JSON.stringify(frame) + '\n');
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
(0, tracing_1.handshakeError)('unconditionalFallback:stdout', err);
|
|
91
|
+
}
|
|
92
|
+
if (!server.__readyNotified)
|
|
93
|
+
(0, readyEmitter_1.emitReadyGlobal)(server, 'unconditional-init-fallback-direct');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
(0, tracing_1.handshakeError)('safetyFallback:150ms', err);
|
|
99
|
+
}
|
|
100
|
+
}, UNCONDITIONAL_FALLBACK_MS).unref?.();
|
|
101
|
+
}
|
|
102
|
+
// Patch initialize result for instructions (SDK internal property)
|
|
103
|
+
const originalInit = server._oninitialize;
|
|
104
|
+
if (originalInit && !server.__initPatched) {
|
|
105
|
+
server.__initPatched = true;
|
|
106
|
+
server._oninitialize = async function (request) {
|
|
107
|
+
try {
|
|
108
|
+
this.__sawInitializeRequest = true;
|
|
109
|
+
(0, tracing_1.handshakeLog)('oninitialize_enter', {
|
|
110
|
+
sawInit: true,
|
|
111
|
+
ready: !!this.__readyNotified,
|
|
112
|
+
initRespSent: !!server.__initResponseSent,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
(0, tracing_1.handshakeError)('oninitialize:enter', err);
|
|
117
|
+
}
|
|
118
|
+
const result = await originalInit.call(this, request);
|
|
119
|
+
try {
|
|
120
|
+
const negotiated = (0, protocol_1.negotiateProtocolVersion)(request?.params?.protocolVersion);
|
|
121
|
+
result.protocolVersion = negotiated;
|
|
122
|
+
if (result && typeof result === 'object' && !('instructions' in result)) {
|
|
123
|
+
result.instructions =
|
|
124
|
+
'Use initialize -> tools/list -> tools/call { name, arguments }. Health: tools/call health_check. Metrics: tools/call metrics_snapshot. Ping: ping.';
|
|
125
|
+
}
|
|
126
|
+
// Do NOT emit server/ready here; ordering handled strictly by transport send hook.
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
(0, tracing_1.handshakeError)('oninitialize:negotiation', err);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupStdinSniffer(server: any): void;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setupStdinSniffer = setupStdinSniffer;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
+
/**
|
|
6
|
+
* Pre-connect stdin sniffer: scans incoming bytes for an MCP `initialize`
|
|
7
|
+
* request even when fragmented across chunks, then drives a layered
|
|
8
|
+
* fallback chain (synthetic dispatch -> forced result -> ready emission)
|
|
9
|
+
* when the SDK request dispatcher fails to make progress on its own.
|
|
10
|
+
*/
|
|
11
|
+
const runtimeConfig_1 = require("../../config/runtimeConfig");
|
|
12
|
+
const initializeDetector_1 = require("./initializeDetector");
|
|
13
|
+
const fallbackFrames_1 = require("./fallbackFrames");
|
|
14
|
+
const protocol_1 = require("./protocol");
|
|
15
|
+
const readyEmitter_1 = require("./readyEmitter");
|
|
16
|
+
const tracing_1 = require("./tracing");
|
|
17
|
+
const SNIFF_WINDOW_BYTES = 8000;
|
|
18
|
+
const BUFFER_HARD_LIMIT = 10_000;
|
|
19
|
+
const BUFFER_TRIM_TAIL = 2048;
|
|
20
|
+
const SCHEDULE_DETECT_MS = 60;
|
|
21
|
+
const SCHEDULE_SYNTHETIC_MS = 40;
|
|
22
|
+
const SCHEDULE_FORCED_RESULT_MS = 140;
|
|
23
|
+
function setupStdinSniffer(server) {
|
|
24
|
+
try {
|
|
25
|
+
const cfg = (0, runtimeConfig_1.getRuntimeConfig)();
|
|
26
|
+
const disableInitSniff = cfg.initFeatures.has('disableSniff');
|
|
27
|
+
if (!server || disableInitSniff)
|
|
28
|
+
return;
|
|
29
|
+
const INIT_FALLBACK_ENABLED = cfg.initFeatures.has('initFallback');
|
|
30
|
+
let __sniffBuf = '';
|
|
31
|
+
if (cfg.trace.has('healthMixed')) {
|
|
32
|
+
try {
|
|
33
|
+
if (!server.__diagRQMap) {
|
|
34
|
+
server.__diagRQMap = new Map();
|
|
35
|
+
server.__diagQueueDepthSniff = 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
(0, tracing_1.handshakeError)('setupStdinSniffer:diagMapInit', err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
process.stdin.on('data', (chunk) => {
|
|
43
|
+
try {
|
|
44
|
+
const healthMixed = cfg.trace.has('healthMixed');
|
|
45
|
+
if (healthMixed && !server.__diagFirstChunkLogged) {
|
|
46
|
+
server.__diagFirstChunkLogged = true;
|
|
47
|
+
const raw = chunk.toString('utf8');
|
|
48
|
+
const snippet = raw.replace(/\r/g, ' ').replace(/\n/g, '\\n').slice(0, 240);
|
|
49
|
+
process.stderr.write(`[diag] ${Date.now()} stdin_first_chunk size=${chunk.length} snippet="${snippet}"\n`);
|
|
50
|
+
}
|
|
51
|
+
__sniffBuf += chunk.toString('utf8');
|
|
52
|
+
if (!server.__sniffedInit) {
|
|
53
|
+
const bufForScan = __sniffBuf.slice(-SNIFF_WINDOW_BYTES);
|
|
54
|
+
const detect = (0, initializeDetector_1.detectInitializeMethod)(bufForScan, healthMixed);
|
|
55
|
+
if (detect.mode) {
|
|
56
|
+
handleInitializeDetected({
|
|
57
|
+
server,
|
|
58
|
+
bufForScan,
|
|
59
|
+
mode: detect.mode,
|
|
60
|
+
healthMixed,
|
|
61
|
+
initFallbackEnabled: INIT_FALLBACK_ENABLED,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (healthMixed && !server.__dispatcherOverrideActive) {
|
|
66
|
+
__sniffBuf = drainDiagFrames(__sniffBuf, server);
|
|
67
|
+
}
|
|
68
|
+
if (__sniffBuf.length > BUFFER_HARD_LIMIT) {
|
|
69
|
+
__sniffBuf = __sniffBuf.slice(-BUFFER_TRIM_TAIL);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
(0, tracing_1.handshakeError)('stdinSniff:data', err);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
(0, tracing_1.handshakeError)('setupStdinSniffer', err);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function handleInitializeDetected(args) {
|
|
82
|
+
const { server, bufForScan, mode, healthMixed, initFallbackEnabled } = args;
|
|
83
|
+
server.__sniffedInit = true;
|
|
84
|
+
if (healthMixed) {
|
|
85
|
+
try {
|
|
86
|
+
const norm = bufForScan.slice(0, 400).replace(/\r/g, ' ').replace(/\n/g, '\\n');
|
|
87
|
+
process.stderr.write(`[diag] ${Date.now()} sniff_init_${mode}_detect buffer_bytes=${bufForScan.length} preview="${norm}"\n`);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
(0, tracing_1.handshakeError)('stdinSniff:diagPreview', err);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
try {
|
|
95
|
+
if (!server.__sawInitializeRequest) {
|
|
96
|
+
server.__sawInitializeRequest = true;
|
|
97
|
+
if (healthMixed) {
|
|
98
|
+
try {
|
|
99
|
+
process.stderr.write(`[diag] ${Date.now()} sniff_init_mark_sawInit mode=${mode}\n`);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
(0, tracing_1.handshakeError)('stdinSniff:sawInitLog', err);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (initFallbackEnabled && !server.__initResponseSent) {
|
|
107
|
+
scheduleSyntheticInitDispatch(server, bufForScan, healthMixed);
|
|
108
|
+
scheduleForcedInitResult(server, healthMixed);
|
|
109
|
+
}
|
|
110
|
+
else if (healthMixed) {
|
|
111
|
+
try {
|
|
112
|
+
process.stderr.write(`[diag] ${Date.now()} sniff_init_forced_result_skip gating_off\n`);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
(0, tracing_1.handshakeError)('stdinSniff:forcedResultSkipLog', err);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if ((0, protocol_1.isHandshakeFallbacksEnabled)()) {
|
|
119
|
+
if (server.__initResponseSent && !server.__readyNotified) {
|
|
120
|
+
(0, readyEmitter_1.emitReadyGlobal)(server, 'stdin-sniff-fallback');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
(0, tracing_1.handshakeError)('stdinSniff:initSchedule', err);
|
|
126
|
+
}
|
|
127
|
+
}, SCHEDULE_DETECT_MS).unref?.();
|
|
128
|
+
}
|
|
129
|
+
function scheduleSyntheticInitDispatch(server, bufForScan, healthMixed) {
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
try {
|
|
132
|
+
if (server.__initResponseSent || server.__syntheticInitDispatched)
|
|
133
|
+
return;
|
|
134
|
+
const id = (0, initializeDetector_1.extractRequestId)(bufForScan) ?? 1;
|
|
135
|
+
const req = (0, fallbackFrames_1.buildSyntheticInitRequest)(id);
|
|
136
|
+
server.__syntheticInitDispatched = true;
|
|
137
|
+
const dispatch = server._onRequest || server._onrequest;
|
|
138
|
+
if (typeof dispatch === 'function') {
|
|
139
|
+
if (healthMixed) {
|
|
140
|
+
try {
|
|
141
|
+
process.stderr.write(`[diag] ${Date.now()} sniff_init_synthetic_dispatch id=${id}\n`);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
(0, tracing_1.handshakeError)('stdinSniff:syntheticDispatchLog', err);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
dispatch.call(server, req);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
(0, tracing_1.handshakeError)('syntheticInitDispatch', err);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
(0, tracing_1.handshakeError)('syntheticInitOuter', err);
|
|
157
|
+
}
|
|
158
|
+
}, SCHEDULE_SYNTHETIC_MS).unref?.();
|
|
159
|
+
}
|
|
160
|
+
function scheduleForcedInitResult(server, healthMixed) {
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
try {
|
|
163
|
+
if (server.__initResponseSent)
|
|
164
|
+
return;
|
|
165
|
+
const tr = server._transport || server.__transportRef;
|
|
166
|
+
if (tr && typeof tr.send === 'function') {
|
|
167
|
+
let negotiated = '2024-11-05';
|
|
168
|
+
try {
|
|
169
|
+
negotiated = (0, protocol_1.negotiateProtocolVersion)('2024-11-05') || negotiated;
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
(0, tracing_1.handshakeError)('stdinSniff:negotiateVersion', err);
|
|
173
|
+
}
|
|
174
|
+
const frame = (0, fallbackFrames_1.buildForcedInitResultFrame)(negotiated, 'forced-init-fallback', 1);
|
|
175
|
+
server.__initResponseSent = true;
|
|
176
|
+
if (healthMixed) {
|
|
177
|
+
try {
|
|
178
|
+
process.stderr.write(`[diag] ${Date.now()} sniff_init_forced_result_emit id=1 negotiated=${negotiated}\n`);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
(0, tracing_1.handshakeError)('stdinSniff:forcedResultLog', err);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
Promise.resolve(tr.send(frame))
|
|
185
|
+
.then(() => {
|
|
186
|
+
if (!server.__readyNotified)
|
|
187
|
+
(0, readyEmitter_1.emitReadyGlobal)(server, 'forced-init-fallback');
|
|
188
|
+
})
|
|
189
|
+
.catch((err) => {
|
|
190
|
+
(0, tracing_1.handshakeError)('forcedInitFallback:transport.send', err);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
(0, tracing_1.handshakeError)('forcedInitFallback:outer', err);
|
|
196
|
+
}
|
|
197
|
+
}, SCHEDULE_FORCED_RESULT_MS).unref?.();
|
|
198
|
+
}
|
|
199
|
+
function drainDiagFrames(buf, server) {
|
|
200
|
+
try {
|
|
201
|
+
let working = buf;
|
|
202
|
+
let idx;
|
|
203
|
+
while ((idx = working.indexOf('\n')) !== -1) {
|
|
204
|
+
const line = working.slice(0, idx).trim();
|
|
205
|
+
working = working.slice(idx + 1);
|
|
206
|
+
if (!line)
|
|
207
|
+
continue;
|
|
208
|
+
let obj;
|
|
209
|
+
try {
|
|
210
|
+
obj = JSON.parse(line);
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
if (/jsonrpc|method/i.test(line)) {
|
|
214
|
+
const frag = line.replace(/\r/g, ' ').replace(/\n/g, ' ').slice(0, 200);
|
|
215
|
+
process.stderr.write(`[diag] ${Date.now()} malformed_json_line len=${line.length} frag="${frag}" err=${e.message || e}\n`);
|
|
216
|
+
}
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (obj &&
|
|
220
|
+
obj.jsonrpc === '2.0' &&
|
|
221
|
+
obj.method &&
|
|
222
|
+
Object.prototype.hasOwnProperty.call(obj, 'id')) {
|
|
223
|
+
const metaName = obj.method === 'tools/call' ? obj?.params?.name : '';
|
|
224
|
+
const category = (() => {
|
|
225
|
+
if (obj.method === 'initialize')
|
|
226
|
+
return 'init';
|
|
227
|
+
if (obj.method === 'health_check' || metaName === 'health_check')
|
|
228
|
+
return 'health';
|
|
229
|
+
if (obj.method === 'metrics_snapshot' || metaName === 'metrics_snapshot')
|
|
230
|
+
return 'metrics';
|
|
231
|
+
if (metaName === 'meta_tools')
|
|
232
|
+
return 'meta';
|
|
233
|
+
return 'other';
|
|
234
|
+
})();
|
|
235
|
+
if (category === 'health' ||
|
|
236
|
+
category === 'metrics' ||
|
|
237
|
+
category === 'meta' ||
|
|
238
|
+
category === 'init') {
|
|
239
|
+
try {
|
|
240
|
+
server.__diagQueueDepthSniff++;
|
|
241
|
+
server.__diagRQMap.set(obj.id, {
|
|
242
|
+
start: Date.now(),
|
|
243
|
+
cat: category,
|
|
244
|
+
method: obj.method,
|
|
245
|
+
});
|
|
246
|
+
process.stderr.write(`[diag] ${Date.now()} rq_enqueue method=${obj.method} cat=${category} id=${obj.id} qdepth=${server.__diagQueueDepthSniff} src=sniff\n`);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
(0, tracing_1.handshakeError)('stdinSniff:diagEnqueue', err);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return working;
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
(0, tracing_1.handshakeError)('stdinSniff:diagEnqueue', err);
|
|
258
|
+
return buf;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function isHandshakeTraceEnabled(): boolean;
|
|
2
|
+
export interface HandshakeEvent {
|
|
3
|
+
seq: number;
|
|
4
|
+
ts: string;
|
|
5
|
+
stage: string;
|
|
6
|
+
extra?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export declare function getHandshakeEvents(): readonly HandshakeEvent[];
|
|
9
|
+
/** Write a single handshake-error breadcrumb to stderr without throwing. */
|
|
10
|
+
export declare function handshakeError(context: string, err: unknown): void;
|
|
11
|
+
export declare function handshakeLog(stage: string, data?: Record<string, unknown>): void;
|
|
12
|
+
export declare function record(stage: string, extra?: Record<string, unknown>): void;
|
|
13
|
+
export declare function isInitFrameDiagEnabled(): boolean;
|
|
14
|
+
export declare function initFrameLog(stage: string, extra?: Record<string, unknown>): void;
|
|
15
|
+
/** Expose the events ring on globalThis for the diagnostics_handshake tool. */
|
|
16
|
+
export declare function exposeGlobalEventsRef(): void;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isHandshakeTraceEnabled = isHandshakeTraceEnabled;
|
|
4
|
+
exports.getHandshakeEvents = getHandshakeEvents;
|
|
5
|
+
exports.handshakeError = handshakeError;
|
|
6
|
+
exports.handshakeLog = handshakeLog;
|
|
7
|
+
exports.record = record;
|
|
8
|
+
exports.isInitFrameDiagEnabled = isInitFrameDiagEnabled;
|
|
9
|
+
exports.initFrameLog = initFrameLog;
|
|
10
|
+
exports.exposeGlobalEventsRef = exposeGlobalEventsRef;
|
|
11
|
+
/**
|
|
12
|
+
* Handshake tracing primitives — module-scoped sequence counter, ring buffer,
|
|
13
|
+
* stderr-safe loggers, and the `handshakeError` helper used everywhere a
|
|
14
|
+
* handshake-adjacent catch block would otherwise silently swallow an error.
|
|
15
|
+
*/
|
|
16
|
+
const runtimeConfig_1 = require("../../config/runtimeConfig");
|
|
17
|
+
function isHandshakeTraceEnabled() {
|
|
18
|
+
return (0, runtimeConfig_1.getRuntimeConfig)().trace.has('handshake');
|
|
19
|
+
}
|
|
20
|
+
let HANDSHAKE_SEQ = 0;
|
|
21
|
+
const HANDSHAKE_EVENTS = [];
|
|
22
|
+
function getHandshakeEvents() {
|
|
23
|
+
return HANDSHAKE_EVENTS;
|
|
24
|
+
}
|
|
25
|
+
/** Write a single handshake-error breadcrumb to stderr without throwing. */
|
|
26
|
+
function handshakeError(context, err) {
|
|
27
|
+
try {
|
|
28
|
+
process.stderr.write(`[handshake-error] ${context}: ${err.message || String(err)}\n`);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* stderr write failed — truly nothing we can do */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function handshakeLog(stage, data) {
|
|
35
|
+
if (!isHandshakeTraceEnabled())
|
|
36
|
+
return;
|
|
37
|
+
try {
|
|
38
|
+
const payload = {
|
|
39
|
+
handshake: true,
|
|
40
|
+
seq: ++HANDSHAKE_SEQ,
|
|
41
|
+
ts: new Date().toISOString(),
|
|
42
|
+
stage,
|
|
43
|
+
...(data || {}),
|
|
44
|
+
};
|
|
45
|
+
process.stderr.write(`[handshake] ${JSON.stringify(payload)}\n`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
handshakeError('handshakeLog', err);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function record(stage, extra) {
|
|
52
|
+
const evt = {
|
|
53
|
+
seq: ++HANDSHAKE_SEQ,
|
|
54
|
+
ts: new Date().toISOString(),
|
|
55
|
+
stage,
|
|
56
|
+
extra,
|
|
57
|
+
};
|
|
58
|
+
HANDSHAKE_EVENTS.push(evt);
|
|
59
|
+
if (HANDSHAKE_EVENTS.length > 50)
|
|
60
|
+
HANDSHAKE_EVENTS.shift();
|
|
61
|
+
if (isHandshakeTraceEnabled()) {
|
|
62
|
+
try {
|
|
63
|
+
process.stderr.write(`[handshake] ${JSON.stringify(evt)}\n`);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
handshakeError('record:trace', err);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function isInitFrameDiagEnabled() {
|
|
71
|
+
return (0, runtimeConfig_1.getRuntimeConfig)().trace.has('initFrame');
|
|
72
|
+
}
|
|
73
|
+
function initFrameLog(stage, extra) {
|
|
74
|
+
if (!isInitFrameDiagEnabled())
|
|
75
|
+
return;
|
|
76
|
+
try {
|
|
77
|
+
const payload = { stage, t: Date.now(), ...(extra || {}) };
|
|
78
|
+
process.stderr.write(`[init-frame] ${JSON.stringify(payload)}\n`);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
handshakeError('initFrameLog', err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Expose the events ring on globalThis for the diagnostics_handshake tool. */
|
|
85
|
+
function exposeGlobalEventsRef() {
|
|
86
|
+
try {
|
|
87
|
+
global.HANDSHAKE_EVENTS_REF =
|
|
88
|
+
HANDSHAKE_EVENTS;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
handshakeError('globalEventsRef', err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Eager side effect preserved from legacy handshakeManager.ts module-load behavior.
|
|
95
|
+
exposeGlobalEventsRef();
|