elasticdash-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +775 -0
- package/dist/browser-ui.d.ts +43 -0
- package/dist/browser-ui.d.ts.map +1 -0
- package/dist/browser-ui.js +246 -0
- package/dist/browser-ui.js.map +1 -0
- package/dist/capture/event.d.ts +33 -0
- package/dist/capture/event.d.ts.map +1 -0
- package/dist/capture/event.js +2 -0
- package/dist/capture/event.js.map +1 -0
- package/dist/capture/index.d.ts +4 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +4 -0
- package/dist/capture/index.js.map +1 -0
- package/dist/capture/recorder.d.ts +24 -0
- package/dist/capture/recorder.d.ts.map +1 -0
- package/dist/capture/recorder.js +46 -0
- package/dist/capture/recorder.js.map +1 -0
- package/dist/capture/replay.d.ts +20 -0
- package/dist/capture/replay.d.ts.map +1 -0
- package/dist/capture/replay.js +47 -0
- package/dist/capture/replay.js.map +1 -0
- package/dist/ci/api-client.d.ts +38 -0
- package/dist/ci/api-client.d.ts.map +1 -0
- package/dist/ci/api-client.js +96 -0
- package/dist/ci/api-client.js.map +1 -0
- package/dist/ci/benchmark.d.ts +33 -0
- package/dist/ci/benchmark.d.ts.map +1 -0
- package/dist/ci/benchmark.js +213 -0
- package/dist/ci/benchmark.js.map +1 -0
- package/dist/ci/ed-runner.d.ts +48 -0
- package/dist/ci/ed-runner.d.ts.map +1 -0
- package/dist/ci/ed-runner.js +260 -0
- package/dist/ci/ed-runner.js.map +1 -0
- package/dist/ci/executor.d.ts +13 -0
- package/dist/ci/executor.d.ts.map +1 -0
- package/dist/ci/executor.js +542 -0
- package/dist/ci/executor.js.map +1 -0
- package/dist/ci/git-info.d.ts +17 -0
- package/dist/ci/git-info.d.ts.map +1 -0
- package/dist/ci/git-info.js +102 -0
- package/dist/ci/git-info.js.map +1 -0
- package/dist/ci/index.d.ts +6 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +4 -0
- package/dist/ci/index.js.map +1 -0
- package/dist/ci/measurement.d.ts +9 -0
- package/dist/ci/measurement.d.ts.map +1 -0
- package/dist/ci/measurement.js +15 -0
- package/dist/ci/measurement.js.map +1 -0
- package/dist/ci/replay.d.ts +31 -0
- package/dist/ci/replay.d.ts.map +1 -0
- package/dist/ci/replay.js +96 -0
- package/dist/ci/replay.js.map +1 -0
- package/dist/ci/reporters/default.d.ts +8 -0
- package/dist/ci/reporters/default.d.ts.map +1 -0
- package/dist/ci/reporters/default.js +46 -0
- package/dist/ci/reporters/default.js.map +1 -0
- package/dist/ci/reporters/index.d.ts +8 -0
- package/dist/ci/reporters/index.d.ts.map +1 -0
- package/dist/ci/reporters/index.js +14 -0
- package/dist/ci/reporters/index.js.map +1 -0
- package/dist/ci/reporters/json.d.ts +8 -0
- package/dist/ci/reporters/json.d.ts.map +1 -0
- package/dist/ci/reporters/json.js +14 -0
- package/dist/ci/reporters/json.js.map +1 -0
- package/dist/ci/reporters/junit.d.ts +8 -0
- package/dist/ci/reporters/junit.d.ts.map +1 -0
- package/dist/ci/reporters/junit.js +48 -0
- package/dist/ci/reporters/junit.js.map +1 -0
- package/dist/ci/runner.d.ts +3 -0
- package/dist/ci/runner.d.ts.map +1 -0
- package/dist/ci/runner.js +187 -0
- package/dist/ci/runner.js.map +1 -0
- package/dist/ci/test-discovery.d.ts +5 -0
- package/dist/ci/test-discovery.d.ts.map +1 -0
- package/dist/ci/test-discovery.js +11 -0
- package/dist/ci/test-discovery.js.map +1 -0
- package/dist/ci/test-loader.d.ts +19 -0
- package/dist/ci/test-loader.d.ts.map +1 -0
- package/dist/ci/test-loader.js +149 -0
- package/dist/ci/test-loader.js.map +1 -0
- package/dist/ci/test-registry.d.ts +42 -0
- package/dist/ci/test-registry.d.ts.map +1 -0
- package/dist/ci/test-registry.js +18 -0
- package/dist/ci/test-registry.js.map +1 -0
- package/dist/ci/trace-schema.d.ts +30 -0
- package/dist/ci/trace-schema.d.ts.map +1 -0
- package/dist/ci/trace-schema.js +66 -0
- package/dist/ci/trace-schema.js.map +1 -0
- package/dist/ci/trace-writer.d.ts +16 -0
- package/dist/ci/trace-writer.d.ts.map +1 -0
- package/dist/ci/trace-writer.js +108 -0
- package/dist/ci/trace-writer.js.map +1 -0
- package/dist/ci/types.d.ts +108 -0
- package/dist/ci/types.d.ts.map +1 -0
- package/dist/ci/types.js +3 -0
- package/dist/ci/types.js.map +1 -0
- package/dist/ci/upload-client.d.ts +74 -0
- package/dist/ci/upload-client.d.ts.map +1 -0
- package/dist/ci/upload-client.js +195 -0
- package/dist/ci/upload-client.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +716 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/agent-state.d.ts +47 -0
- package/dist/core/agent-state.d.ts.map +1 -0
- package/dist/core/agent-state.js +137 -0
- package/dist/core/agent-state.js.map +1 -0
- package/dist/core/judge-utils.d.ts +22 -0
- package/dist/core/judge-utils.d.ts.map +1 -0
- package/dist/core/judge-utils.js +211 -0
- package/dist/core/judge-utils.js.map +1 -0
- package/dist/core/registry.d.ts +28 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +52 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/dashboard-server.d.ts +65 -0
- package/dist/dashboard-server.d.ts.map +1 -0
- package/dist/dashboard-server.js +3940 -0
- package/dist/dashboard-server.js.map +1 -0
- package/dist/execution/tool-runner.d.ts +26 -0
- package/dist/execution/tool-runner.d.ts.map +1 -0
- package/dist/execution/tool-runner.js +316 -0
- package/dist/execution/tool-runner.js.map +1 -0
- package/dist/html/dashboard.html +2218 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +13 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +8102 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/ai-interceptor.d.ts +26 -0
- package/dist/interceptors/ai-interceptor.d.ts.map +1 -0
- package/dist/interceptors/ai-interceptor.js +756 -0
- package/dist/interceptors/ai-interceptor.js.map +1 -0
- package/dist/interceptors/db-auto.d.ts +8 -0
- package/dist/interceptors/db-auto.d.ts.map +1 -0
- package/dist/interceptors/db-auto.js +217 -0
- package/dist/interceptors/db-auto.js.map +1 -0
- package/dist/interceptors/db.d.ts +23 -0
- package/dist/interceptors/db.d.ts.map +1 -0
- package/dist/interceptors/db.js +137 -0
- package/dist/interceptors/db.js.map +1 -0
- package/dist/interceptors/http.d.ts +28 -0
- package/dist/interceptors/http.d.ts.map +1 -0
- package/dist/interceptors/http.js +356 -0
- package/dist/interceptors/http.js.map +1 -0
- package/dist/interceptors/side-effects.d.ts +7 -0
- package/dist/interceptors/side-effects.d.ts.map +1 -0
- package/dist/interceptors/side-effects.js +72 -0
- package/dist/interceptors/side-effects.js.map +1 -0
- package/dist/interceptors/telemetry-push.d.ts +142 -0
- package/dist/interceptors/telemetry-push.d.ts.map +1 -0
- package/dist/interceptors/telemetry-push.js +463 -0
- package/dist/interceptors/telemetry-push.js.map +1 -0
- package/dist/interceptors/tool.d.ts +2 -0
- package/dist/interceptors/tool.d.ts.map +1 -0
- package/dist/interceptors/tool.js +274 -0
- package/dist/interceptors/tool.js.map +1 -0
- package/dist/interceptors/workflow-ai.d.ts +5 -0
- package/dist/interceptors/workflow-ai.d.ts.map +1 -0
- package/dist/interceptors/workflow-ai.js +382 -0
- package/dist/interceptors/workflow-ai.js.map +1 -0
- package/dist/internals/conditional-recorder.d.ts +21 -0
- package/dist/internals/conditional-recorder.d.ts.map +1 -0
- package/dist/internals/conditional-recorder.js +54 -0
- package/dist/internals/conditional-recorder.js.map +1 -0
- package/dist/internals/mock-resolver.d.ts +146 -0
- package/dist/internals/mock-resolver.d.ts.map +1 -0
- package/dist/internals/mock-resolver.js +427 -0
- package/dist/internals/mock-resolver.js.map +1 -0
- package/dist/matchers/index.d.ts +96 -0
- package/dist/matchers/index.d.ts.map +1 -0
- package/dist/matchers/index.js +668 -0
- package/dist/matchers/index.js.map +1 -0
- package/dist/observability.d.ts +82 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +471 -0
- package/dist/observability.js.map +1 -0
- package/dist/portal-executor.d.ts +30 -0
- package/dist/portal-executor.d.ts.map +1 -0
- package/dist/portal-executor.js +324 -0
- package/dist/portal-executor.js.map +1 -0
- package/dist/portal-server.d.ts +3 -0
- package/dist/portal-server.d.ts.map +1 -0
- package/dist/portal-server.js +279 -0
- package/dist/portal-server.js.map +1 -0
- package/dist/proxy/llm-capture.d.ts +14 -0
- package/dist/proxy/llm-capture.d.ts.map +1 -0
- package/dist/proxy/llm-capture.js +264 -0
- package/dist/proxy/llm-capture.js.map +1 -0
- package/dist/reporter.d.ts +3 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +72 -0
- package/dist/reporter.js.map +1 -0
- package/dist/runWorkflowSubprocess.d.ts +14 -0
- package/dist/runWorkflowSubprocess.d.ts.map +1 -0
- package/dist/runWorkflowSubprocess.js +66 -0
- package/dist/runWorkflowSubprocess.js.map +1 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +138 -0
- package/dist/runner.js.map +1 -0
- package/dist/socket-connector.d.ts +22 -0
- package/dist/socket-connector.d.ts.map +1 -0
- package/dist/socket-connector.js +104 -0
- package/dist/socket-connector.js.map +1 -0
- package/dist/telemetry-batcher.d.ts +56 -0
- package/dist/telemetry-batcher.d.ts.map +1 -0
- package/dist/telemetry-batcher.js +143 -0
- package/dist/telemetry-batcher.js.map +1 -0
- package/dist/test-setup.d.ts +12 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +13 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/tool-registry.d.ts +31 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +73 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/tool-runner-worker.d.ts +2 -0
- package/dist/tool-runner-worker.d.ts.map +1 -0
- package/dist/tool-runner-worker.js +215 -0
- package/dist/tool-runner-worker.js.map +1 -0
- package/dist/trace-adapter/context.d.ts +72 -0
- package/dist/trace-adapter/context.d.ts.map +1 -0
- package/dist/trace-adapter/context.js +80 -0
- package/dist/trace-adapter/context.js.map +1 -0
- package/dist/tracing.d.ts +2 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +59 -0
- package/dist/tracing.js.map +1 -0
- package/dist/trigger-executor.d.ts +12 -0
- package/dist/trigger-executor.d.ts.map +1 -0
- package/dist/trigger-executor.js +130 -0
- package/dist/trigger-executor.js.map +1 -0
- package/dist/types/portal.d.ts +76 -0
- package/dist/types/portal.d.ts.map +1 -0
- package/dist/types/portal.js +2 -0
- package/dist/types/portal.js.map +1 -0
- package/dist/utils/debug.d.ts +3 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +8 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/license-error.d.ts +23 -0
- package/dist/utils/license-error.d.ts.map +1 -0
- package/dist/utils/license-error.js +42 -0
- package/dist/utils/license-error.js.map +1 -0
- package/dist/utils/redact.d.ts +7 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +26 -0
- package/dist/utils/redact.js.map +1 -0
- package/dist/workflow-runner-worker.d.ts +2 -0
- package/dist/workflow-runner-worker.d.ts.map +1 -0
- package/dist/workflow-runner-worker.js +329 -0
- package/dist/workflow-runner-worker.js.map +1 -0
- package/dist/workflow-runner.d.ts +14 -0
- package/dist/workflow-runner.d.ts.map +1 -0
- package/dist/workflow-runner.js +34 -0
- package/dist/workflow-runner.js.map +1 -0
- package/docs/agent-coding-instructions.md +138 -0
- package/docs/agent-integration-guide.md +564 -0
- package/docs/agents.md +140 -0
- package/docs/dashboard.md +394 -0
- package/docs/deno.md +69 -0
- package/docs/instrumentation.md +424 -0
- package/docs/langfuse-trace-structure.md +145 -0
- package/docs/matchers.md +173 -0
- package/docs/observability_contract.md +192 -0
- package/docs/observability_mode.md +195 -0
- package/docs/quickstart.md +621 -0
- package/docs/security-compliance.md +566 -0
- package/docs/test-writing-guidelines.md +444 -0
- package/docs/tools.md +165 -0
- package/docs/workflow-modes.md +253 -0
- package/package.json +76 -0
- package/src/browser-ui.ts +281 -0
- package/src/capture/event.ts +30 -0
- package/src/capture/index.ts +3 -0
- package/src/capture/recorder.ts +62 -0
- package/src/capture/replay.ts +55 -0
- package/src/ci/api-client.ts +136 -0
- package/src/ci/benchmark.ts +257 -0
- package/src/ci/ed-runner.ts +351 -0
- package/src/ci/executor.ts +671 -0
- package/src/ci/git-info.ts +127 -0
- package/src/ci/index.ts +5 -0
- package/src/ci/measurement.ts +25 -0
- package/src/ci/replay.ts +127 -0
- package/src/ci/reporters/default.ts +50 -0
- package/src/ci/reporters/index.ts +21 -0
- package/src/ci/reporters/json.ts +18 -0
- package/src/ci/reporters/junit.ts +61 -0
- package/src/ci/runner.ts +208 -0
- package/src/ci/test-discovery.ts +16 -0
- package/src/ci/test-loader.ts +187 -0
- package/src/ci/test-registry.ts +62 -0
- package/src/ci/trace-schema.ts +96 -0
- package/src/ci/trace-writer.ts +107 -0
- package/src/ci/types.ts +115 -0
- package/src/ci/upload-client.ts +300 -0
- package/src/cli.ts +811 -0
- package/src/core/agent-state.ts +162 -0
- package/src/core/judge-utils.ts +232 -0
- package/src/core/registry.ts +92 -0
- package/src/dashboard-server.ts +2047 -0
- package/src/execution/tool-runner.ts +352 -0
- package/src/html/dashboard.html +2218 -0
- package/src/http.ts +13 -0
- package/src/index.ts +138 -0
- package/src/interceptors/ai-interceptor.ts +798 -0
- package/src/interceptors/db-auto.ts +243 -0
- package/src/interceptors/db.ts +156 -0
- package/src/interceptors/http.ts +393 -0
- package/src/interceptors/side-effects.ts +83 -0
- package/src/interceptors/telemetry-push.ts +537 -0
- package/src/interceptors/tool.ts +287 -0
- package/src/interceptors/workflow-ai.ts +419 -0
- package/src/internals/conditional-recorder.ts +63 -0
- package/src/internals/mock-resolver.ts +492 -0
- package/src/matchers/index.ts +824 -0
- package/src/observability.ts +501 -0
- package/src/portal-executor.ts +355 -0
- package/src/portal-server.ts +304 -0
- package/src/proxy/llm-capture.ts +301 -0
- package/src/reporter.ts +81 -0
- package/src/runWorkflowSubprocess.ts +74 -0
- package/src/runner.ts +178 -0
- package/src/socket-connector.ts +117 -0
- package/src/telemetry-batcher.ts +191 -0
- package/src/test-setup.ts +16 -0
- package/src/tool-registry.ts +94 -0
- package/src/tool-runner-worker.ts +244 -0
- package/src/trace-adapter/context.ts +156 -0
- package/src/tracing.ts +62 -0
- package/src/trigger-executor.ts +171 -0
- package/src/types/agent.d.ts +63 -0
- package/src/types/expect.d.ts +81 -0
- package/src/types/modules.d.ts +2 -0
- package/src/types/portal.ts +69 -0
- package/src/utils/debug.ts +8 -0
- package/src/utils/license-error.ts +43 -0
- package/src/utils/redact.ts +25 -0
- package/src/workflow-runner-worker.ts +386 -0
- package/src/workflow-runner.ts +58 -0
package/dist/runner.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { clearRegistry, getRegistry } from './core/registry.js';
|
|
2
|
+
import { startTraceSession, setCurrentTrace } from './trace-adapter/context.js';
|
|
3
|
+
import { startLLMProxy, fetchCapturedTrace } from './proxy/llm-capture.js';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
export async function runFiles(files, options = {}) {
|
|
8
|
+
// Optional local LLM capture proxy (opt-in via env). Default behavior stays unchanged when disabled.
|
|
9
|
+
const proxyOptIn = process.env.ELASTICDASH_LLM_PROXY === '1' || Boolean(process.env.ELASTICDASH_LLM_PROXY_URL);
|
|
10
|
+
const proxyPort = Number.parseInt(process.env.ELASTICDASH_LLM_PROXY_PORT || '8787', 10);
|
|
11
|
+
let proxyUrl = process.env.ELASTICDASH_LLM_PROXY_URL;
|
|
12
|
+
const proxyHandle = proxyOptIn && !proxyUrl ? await startLLMProxy({ port: proxyPort }) : null;
|
|
13
|
+
if (proxyHandle) {
|
|
14
|
+
proxyUrl = proxyHandle.url;
|
|
15
|
+
}
|
|
16
|
+
const fileResults = [];
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const result = await runFile(file, options, { proxyOptIn, proxyUrl });
|
|
19
|
+
fileResults.push(result);
|
|
20
|
+
}
|
|
21
|
+
if (proxyHandle) {
|
|
22
|
+
await proxyHandle.stop();
|
|
23
|
+
}
|
|
24
|
+
return fileResults;
|
|
25
|
+
}
|
|
26
|
+
async function runFile(file, options, proxyCtx) {
|
|
27
|
+
const { hooks = {} } = options;
|
|
28
|
+
// 1. Clear the global registry before loading the file
|
|
29
|
+
clearRegistry();
|
|
30
|
+
// 2. Dynamically import the test file (triggers aiTest() registrations)
|
|
31
|
+
const resolvedPath = file.startsWith('file://')
|
|
32
|
+
? file
|
|
33
|
+
: pathToFileURL(path.resolve(file)).href;
|
|
34
|
+
if (resolvedPath.endsWith('.ts') && typeof globalThis.Deno === 'undefined') {
|
|
35
|
+
await import('tsx/esm');
|
|
36
|
+
await import('tsx/cjs');
|
|
37
|
+
}
|
|
38
|
+
await import(resolvedPath);
|
|
39
|
+
const registry = getRegistry();
|
|
40
|
+
const results = [];
|
|
41
|
+
// Shared unhandled error trap for this file's test run
|
|
42
|
+
let currentTestName = null;
|
|
43
|
+
let pendingUnhandled;
|
|
44
|
+
const onUnhandled = (reason) => {
|
|
45
|
+
if (!pendingUnhandled)
|
|
46
|
+
pendingUnhandled = reason instanceof Error ? reason : new Error(String(reason));
|
|
47
|
+
};
|
|
48
|
+
process.on('unhandledRejection', onUnhandled);
|
|
49
|
+
process.on('uncaughtException', onUnhandled);
|
|
50
|
+
// 3. Run beforeAll hooks
|
|
51
|
+
for (const hook of registry.beforeAllHooks) {
|
|
52
|
+
await hook();
|
|
53
|
+
}
|
|
54
|
+
// 4. Execute each test sequentially
|
|
55
|
+
for (const entry of registry.tests) {
|
|
56
|
+
const { context, finalise } = startTraceSession();
|
|
57
|
+
const traceId = proxyCtx.proxyOptIn ? randomUUID() : null;
|
|
58
|
+
setCurrentTrace(context.trace);
|
|
59
|
+
if (traceId) {
|
|
60
|
+
process.env.ELASTICDASH_TRACE_ID = traceId;
|
|
61
|
+
}
|
|
62
|
+
if (hooks.onTestStart) {
|
|
63
|
+
await hooks.onTestStart(entry.name);
|
|
64
|
+
}
|
|
65
|
+
const startTime = Date.now();
|
|
66
|
+
let passed = false;
|
|
67
|
+
let error;
|
|
68
|
+
// Reset per-test unhandled capture and mark current test name
|
|
69
|
+
pendingUnhandled = undefined;
|
|
70
|
+
currentTestName = entry.name;
|
|
71
|
+
setCurrentTrace(context.trace);
|
|
72
|
+
try {
|
|
73
|
+
for (const hook of registry.beforeEachHooks) {
|
|
74
|
+
await hook();
|
|
75
|
+
}
|
|
76
|
+
await entry.fn(context);
|
|
77
|
+
passed = true;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
try {
|
|
84
|
+
for (const hook of registry.afterEachHooks) {
|
|
85
|
+
await hook();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (afterErr) {
|
|
89
|
+
if (!error) {
|
|
90
|
+
error = afterErr instanceof Error ? afterErr : new Error(String(afterErr));
|
|
91
|
+
passed = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
setCurrentTrace(undefined);
|
|
95
|
+
if (!error && pendingUnhandled) {
|
|
96
|
+
error = pendingUnhandled;
|
|
97
|
+
passed = false;
|
|
98
|
+
}
|
|
99
|
+
currentTestName = null;
|
|
100
|
+
}
|
|
101
|
+
const durationMs = Date.now() - startTime;
|
|
102
|
+
if (hooks.onTestFinish) {
|
|
103
|
+
await hooks.onTestFinish(entry.name, passed, durationMs, error);
|
|
104
|
+
}
|
|
105
|
+
if (hooks.onTraceComplete) {
|
|
106
|
+
await hooks.onTraceComplete(entry.name, context.trace);
|
|
107
|
+
}
|
|
108
|
+
// If proxy mode is enabled, pull captured LLM steps and fold into the trace
|
|
109
|
+
if (traceId && proxyCtx.proxyUrl) {
|
|
110
|
+
try {
|
|
111
|
+
const captured = await fetchCapturedTrace(proxyCtx.proxyUrl, traceId);
|
|
112
|
+
for (const step of captured) {
|
|
113
|
+
context.trace.recordLLMStep(step);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (proxyErr) {
|
|
117
|
+
// Non-fatal: keep test result as-is
|
|
118
|
+
// eslint-disable-next-line no-console
|
|
119
|
+
console.warn('[elasticdash] Failed to fetch proxy-captured steps:', proxyErr);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
finalise();
|
|
123
|
+
setCurrentTrace(undefined);
|
|
124
|
+
if (traceId) {
|
|
125
|
+
delete process.env.ELASTICDASH_TRACE_ID;
|
|
126
|
+
}
|
|
127
|
+
results.push({ name: entry.name, passed, durationMs, error });
|
|
128
|
+
}
|
|
129
|
+
// 5. Run afterAll hooks
|
|
130
|
+
for (const hook of registry.afterAllHooks) {
|
|
131
|
+
await hook();
|
|
132
|
+
}
|
|
133
|
+
// Cleanup shared handlers
|
|
134
|
+
process.off('unhandledRejection', onUnhandled);
|
|
135
|
+
process.off('uncaughtException', onUnhandled);
|
|
136
|
+
return { file, results };
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Socket } from 'socket.io-client';
|
|
2
|
+
export interface SocketConnectorOptions {
|
|
3
|
+
serverUrl: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Establish a persistent socket.io connection to the backend.
|
|
9
|
+
*
|
|
10
|
+
* The SDK registers its sessionId and available tools/workflows
|
|
11
|
+
* on connect. The backend can then push portal tasks and trigger signals
|
|
12
|
+
* to this SDK instance for execution.
|
|
13
|
+
*
|
|
14
|
+
* Auto-reconnects on disconnect with exponential backoff (1s–30s).
|
|
15
|
+
* Safe to call multiple times — subsequent calls return the existing socket.
|
|
16
|
+
*/
|
|
17
|
+
export declare function connectToBackend(options: SocketConnectorOptions): Socket;
|
|
18
|
+
/**
|
|
19
|
+
* Disconnect from the backend and clean up the socket.
|
|
20
|
+
*/
|
|
21
|
+
export declare function disconnectFromBackend(): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=socket-connector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-connector.d.ts","sourceRoot":"","sources":["../src/socket-connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAQ7C,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAID;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAgFxE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3D"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { io } from 'socket.io-client';
|
|
2
|
+
import { executePortalTask } from './portal-executor.js';
|
|
3
|
+
import { executeTrigger } from './trigger-executor.js';
|
|
4
|
+
import { scanTools, scanWorkflows } from './execution/tool-runner.js';
|
|
5
|
+
import { debugLog } from './utils/debug.js';
|
|
6
|
+
let socket = null;
|
|
7
|
+
/**
|
|
8
|
+
* Establish a persistent socket.io connection to the backend.
|
|
9
|
+
*
|
|
10
|
+
* The SDK registers its sessionId and available tools/workflows
|
|
11
|
+
* on connect. The backend can then push portal tasks and trigger signals
|
|
12
|
+
* to this SDK instance for execution.
|
|
13
|
+
*
|
|
14
|
+
* Auto-reconnects on disconnect with exponential backoff (1s–30s).
|
|
15
|
+
* Safe to call multiple times — subsequent calls return the existing socket.
|
|
16
|
+
*/
|
|
17
|
+
export function connectToBackend(options) {
|
|
18
|
+
if (socket?.connected)
|
|
19
|
+
return socket;
|
|
20
|
+
// If a socket exists but is disconnected/reconnecting, reuse it
|
|
21
|
+
if (socket)
|
|
22
|
+
return socket;
|
|
23
|
+
const { serverUrl, apiKey, sessionId } = options;
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
socket = io(serverUrl, {
|
|
26
|
+
auth: {
|
|
27
|
+
...(apiKey ? { apiKey } : {}),
|
|
28
|
+
sessionId,
|
|
29
|
+
},
|
|
30
|
+
transports: ['websocket', 'polling'],
|
|
31
|
+
reconnection: true,
|
|
32
|
+
reconnectionDelay: 1000,
|
|
33
|
+
reconnectionDelayMax: 30_000,
|
|
34
|
+
});
|
|
35
|
+
socket.on('connect', () => {
|
|
36
|
+
debugLog(`[elasticdash] Socket connected: ${socket.id}`);
|
|
37
|
+
const tools = scanTools(cwd);
|
|
38
|
+
const workflows = scanWorkflows(cwd);
|
|
39
|
+
socket.emit('register', {
|
|
40
|
+
sessionId,
|
|
41
|
+
tools: tools.map(t => t.name),
|
|
42
|
+
workflows: workflows.map(w => w.name),
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
socket.on('auth:ok', (data) => {
|
|
46
|
+
debugLog(`[elasticdash] Authenticated for project ${data.projectId}`);
|
|
47
|
+
socket.emit('join', `observability:project:${data.projectId}`);
|
|
48
|
+
});
|
|
49
|
+
socket.on('portal:task', async (task, ack) => {
|
|
50
|
+
debugLog(`[elasticdash] Socket received portal task: ${task.taskId} type=${task.type} name=${task.name}`);
|
|
51
|
+
try {
|
|
52
|
+
const tools = scanTools(cwd);
|
|
53
|
+
const result = await executePortalTask(task, cwd, tools);
|
|
54
|
+
if (typeof ack === 'function') {
|
|
55
|
+
ack(result);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
socket.emit('portal:result', result);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
const errorResult = {
|
|
63
|
+
taskId: task.taskId,
|
|
64
|
+
ok: false,
|
|
65
|
+
output: null,
|
|
66
|
+
error: e instanceof Error ? e.message : String(e),
|
|
67
|
+
durationMs: 0,
|
|
68
|
+
metadata: task.metadata,
|
|
69
|
+
};
|
|
70
|
+
if (typeof ack === 'function') {
|
|
71
|
+
ack(errorResult);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
socket.emit('portal:result', errorResult);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
socket.on('trigger', async (trigger) => {
|
|
79
|
+
debugLog(`[elasticdash] Socket received trigger: ${trigger.triggerId} steps=${trigger.steps.length} runs=${trigger.runCount}`);
|
|
80
|
+
try {
|
|
81
|
+
await executeTrigger(serverUrl, apiKey, trigger);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
debugLog(`[elasticdash] Socket trigger execution failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
socket.on('disconnect', (reason) => {
|
|
88
|
+
debugLog(`[elasticdash] Socket disconnected: ${reason}`);
|
|
89
|
+
});
|
|
90
|
+
socket.on('connect_error', (err) => {
|
|
91
|
+
debugLog(`[elasticdash] Socket connection error: ${err.message}`);
|
|
92
|
+
});
|
|
93
|
+
return socket;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Disconnect from the backend and clean up the socket.
|
|
97
|
+
*/
|
|
98
|
+
export async function disconnectFromBackend() {
|
|
99
|
+
if (socket) {
|
|
100
|
+
socket.disconnect();
|
|
101
|
+
socket = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=socket-connector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-connector.js","sourceRoot":"","sources":["../src/socket-connector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { WorkflowEvent } from './capture/event.js';
|
|
2
|
+
export interface TriggerStep {
|
|
3
|
+
eventId: number;
|
|
4
|
+
eventType: 'ai' | 'tool' | 'http' | 'db';
|
|
5
|
+
eventName: string;
|
|
6
|
+
originalEventDbId: number;
|
|
7
|
+
input: unknown;
|
|
8
|
+
model?: string;
|
|
9
|
+
provider?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FrozenEvent {
|
|
12
|
+
id: number;
|
|
13
|
+
type: string;
|
|
14
|
+
name: string;
|
|
15
|
+
input: unknown;
|
|
16
|
+
output: unknown;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
durationMs: number | null;
|
|
19
|
+
streamed?: boolean;
|
|
20
|
+
streamRaw?: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface TriggerSignal {
|
|
23
|
+
triggerId: number;
|
|
24
|
+
runCount: number;
|
|
25
|
+
steps: TriggerStep[];
|
|
26
|
+
/** HTTP/DB events from the same trace to freeze (mock) during step reruns */
|
|
27
|
+
frozenEvents?: FrozenEvent[];
|
|
28
|
+
}
|
|
29
|
+
export interface TelemetryBatcherOptions {
|
|
30
|
+
serverUrl: string;
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
sessionId: string;
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
batchIntervalMs?: number;
|
|
35
|
+
maxBatchSize?: number;
|
|
36
|
+
redactKeys?: string[];
|
|
37
|
+
onTrigger?: (trigger: TriggerSignal) => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export declare class TelemetryBatcher {
|
|
40
|
+
private buffer;
|
|
41
|
+
private timer;
|
|
42
|
+
private readonly serverUrl;
|
|
43
|
+
private readonly apiKey;
|
|
44
|
+
private readonly sessionId;
|
|
45
|
+
private readonly metadata;
|
|
46
|
+
private readonly maxBatchSize;
|
|
47
|
+
private readonly redactKeys;
|
|
48
|
+
private readonly onTrigger;
|
|
49
|
+
private shuttingDown;
|
|
50
|
+
constructor(opts: TelemetryBatcherOptions);
|
|
51
|
+
enqueue(event: WorkflowEvent): void;
|
|
52
|
+
flush(): Promise<void>;
|
|
53
|
+
private send;
|
|
54
|
+
shutdown(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=telemetry-batcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-batcher.d.ts","sourceRoot":"","sources":["../src/telemetry-batcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAMvD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,WAAW,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyD;IACnF,OAAO,CAAC,YAAY,CAAQ;gBAEhB,IAAI,EAAE,uBAAuB;IAiBzC,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAW7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAMd,IAAI;IAuEZ,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAShC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { getOriginalFetch } from './interceptors/http.js';
|
|
4
|
+
import { debugLog } from './utils/debug.js';
|
|
5
|
+
import { notifyLicenseError } from './utils/license-error.js';
|
|
6
|
+
import { redactPayload } from './utils/redact.js';
|
|
7
|
+
export class TelemetryBatcher {
|
|
8
|
+
buffer = [];
|
|
9
|
+
timer = null;
|
|
10
|
+
serverUrl;
|
|
11
|
+
apiKey;
|
|
12
|
+
sessionId;
|
|
13
|
+
metadata;
|
|
14
|
+
maxBatchSize;
|
|
15
|
+
redactKeys;
|
|
16
|
+
onTrigger;
|
|
17
|
+
shuttingDown = false;
|
|
18
|
+
constructor(opts) {
|
|
19
|
+
this.serverUrl = opts.serverUrl.replace(/\/$/, '');
|
|
20
|
+
this.apiKey = opts.apiKey;
|
|
21
|
+
this.sessionId = opts.sessionId;
|
|
22
|
+
this.metadata = opts.metadata;
|
|
23
|
+
this.maxBatchSize = opts.maxBatchSize ?? 50;
|
|
24
|
+
this.redactKeys = opts.redactKeys ?? [];
|
|
25
|
+
this.onTrigger = opts.onTrigger;
|
|
26
|
+
const intervalMs = opts.batchIntervalMs ?? 2000;
|
|
27
|
+
this.timer = setInterval(() => { this.flush().catch(() => { }); }, intervalMs);
|
|
28
|
+
// Allow the process to exit even if the timer is still scheduled
|
|
29
|
+
if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {
|
|
30
|
+
this.timer.unref();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
enqueue(event) {
|
|
34
|
+
if (this.shuttingDown)
|
|
35
|
+
return;
|
|
36
|
+
const redacted = this.redactKeys.length > 0
|
|
37
|
+
? { ...event, input: redactPayload(event.input, this.redactKeys), output: redactPayload(event.output, this.redactKeys) }
|
|
38
|
+
: event;
|
|
39
|
+
this.buffer.push(redacted);
|
|
40
|
+
if (this.buffer.length >= this.maxBatchSize) {
|
|
41
|
+
this.flush().catch(() => { });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async flush() {
|
|
45
|
+
if (this.buffer.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
const batch = this.buffer.splice(0, this.buffer.length);
|
|
48
|
+
await this.send(batch, 0);
|
|
49
|
+
}
|
|
50
|
+
async send(batch, attempt) {
|
|
51
|
+
if (!this.serverUrl) {
|
|
52
|
+
debugLog(`[elasticdash] Dropping ${batch.length} events: serverUrl is empty`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const maxRetries = 3;
|
|
56
|
+
const url = `${this.serverUrl}/api/observability/events`;
|
|
57
|
+
const headers = {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
'X-Correlation-ID': randomUUID(),
|
|
60
|
+
};
|
|
61
|
+
if (this.apiKey)
|
|
62
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
63
|
+
try {
|
|
64
|
+
// Stream the JSON body to avoid buffering large payloads (e.g. events
|
|
65
|
+
// with streamRaw can exceed 100 MB). Node.js fetch (undici) supports
|
|
66
|
+
// Readable as body and uses chunked Transfer-Encoding automatically.
|
|
67
|
+
const body = Readable.from(jsonStreamEvents(this.sessionId, batch, this.metadata));
|
|
68
|
+
const res = await getOriginalFetch()(url, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers,
|
|
71
|
+
body,
|
|
72
|
+
duplex: 'half',
|
|
73
|
+
});
|
|
74
|
+
if (res.status === 402) {
|
|
75
|
+
notifyLicenseError(res.status, 'telemetry');
|
|
76
|
+
debugLog(`[elasticdash] Dropping ${batch.length} events: no available license (402)`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (res.status === 429 || res.status >= 500) {
|
|
80
|
+
if (attempt < maxRetries) {
|
|
81
|
+
const delayMs = Math.pow(2, attempt) * 1000;
|
|
82
|
+
debugLog(`[elasticdash] Telemetry flush failed (${res.status}), retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
83
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
84
|
+
return this.send(batch, attempt + 1);
|
|
85
|
+
}
|
|
86
|
+
debugLog(`[elasticdash] Dropping ${batch.length} events after ${maxRetries} retries (last status: ${res.status})`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
debugLog(`[elasticdash] Flushed ${batch.length} events (status ${res.status})`);
|
|
90
|
+
// Parse response for trigger signal
|
|
91
|
+
if (res.ok && this.onTrigger) {
|
|
92
|
+
try {
|
|
93
|
+
const body = await res.json();
|
|
94
|
+
if (body.trigger && typeof body.trigger.triggerId === 'number' && Array.isArray(body.trigger.steps)) {
|
|
95
|
+
debugLog(`[elasticdash] Trigger received: id=${body.trigger.triggerId} steps=${body.trigger.steps.length} runCount=${body.trigger.runCount}`);
|
|
96
|
+
// Fire-and-forget — don't block the flush pipeline
|
|
97
|
+
this.onTrigger(body.trigger).catch((err) => {
|
|
98
|
+
debugLog(`[elasticdash] Trigger execution failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Response parsing failed — not critical, ignore
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
if (attempt < maxRetries) {
|
|
109
|
+
const delayMs = Math.pow(2, attempt) * 1000;
|
|
110
|
+
debugLog(`[elasticdash] Telemetry flush error, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
111
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
112
|
+
return this.send(batch, attempt + 1);
|
|
113
|
+
}
|
|
114
|
+
debugLog(`[elasticdash] Dropping ${batch.length} events after ${maxRetries} retries: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async shutdown() {
|
|
118
|
+
if (this.shuttingDown)
|
|
119
|
+
return;
|
|
120
|
+
this.shuttingDown = true;
|
|
121
|
+
if (this.timer) {
|
|
122
|
+
clearInterval(this.timer);
|
|
123
|
+
this.timer = null;
|
|
124
|
+
}
|
|
125
|
+
await this.flush();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Yields JSON fragments for the events payload without building a single
|
|
130
|
+
* giant string. Each event is serialized individually so the process never
|
|
131
|
+
* holds the full serialized body in memory at once.
|
|
132
|
+
*/
|
|
133
|
+
function* jsonStreamEvents(sessionId, events, metadata) {
|
|
134
|
+
const metaPart = metadata ? `,"metadata":${JSON.stringify(metadata)}` : '';
|
|
135
|
+
yield `{"sessionId":${JSON.stringify(sessionId)}${metaPart},"events":[`;
|
|
136
|
+
for (let i = 0; i < events.length; i++) {
|
|
137
|
+
if (i > 0)
|
|
138
|
+
yield ',';
|
|
139
|
+
yield JSON.stringify(events[i]);
|
|
140
|
+
}
|
|
141
|
+
yield ']}';
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=telemetry-batcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-batcher.js","sourceRoot":"","sources":["../src/telemetry-batcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import this file in your AI test files to get:
|
|
3
|
+
* - aiTest / beforeAll / afterAll / beforeEach / afterEach available as globals (TypeScript types + runtime)
|
|
4
|
+
* - Custom matcher types (toHaveLLMStep, toCallTool, toMatchSemanticOutput)
|
|
5
|
+
*
|
|
6
|
+
* The CLI registers matchers at startup, so this import is for TypeScript
|
|
7
|
+
* type awareness only — no double-registration occurs at runtime.
|
|
8
|
+
*/
|
|
9
|
+
import './core/registry.js';
|
|
10
|
+
import './matchers/index.js';
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=test-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,oBAAoB,CAAA;AAG3B,OAAO,qBAAqB,CAAA;AAE5B,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import this file in your AI test files to get:
|
|
3
|
+
* - aiTest / beforeAll / afterAll / beforeEach / afterEach available as globals (TypeScript types + runtime)
|
|
4
|
+
* - Custom matcher types (toHaveLLMStep, toCallTool, toMatchSemanticOutput)
|
|
5
|
+
*
|
|
6
|
+
* The CLI registers matchers at startup, so this import is for TypeScript
|
|
7
|
+
* type awareness only — no double-registration occurs at runtime.
|
|
8
|
+
*/
|
|
9
|
+
// Side-effect: populates globalThis.aiTest + brings declare global into scope
|
|
10
|
+
import './core/registry.js';
|
|
11
|
+
// Side-effect: brings declare module 'expect' augmentation into scope
|
|
12
|
+
import './matchers/index.js';
|
|
13
|
+
//# sourceMappingURL=test-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-setup.js","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,8EAA8E;AAC9E,OAAO,oBAAoB,CAAA;AAE3B,sEAAsE;AACtE,OAAO,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global registry for tools defined via edTool().
|
|
3
|
+
*
|
|
4
|
+
* Tools registered here are discoverable for reruns regardless of which
|
|
5
|
+
* module they live in. The helper also applies wrapTool() so telemetry
|
|
6
|
+
* fires automatically — symmetric with the Python @ed_tool decorator.
|
|
7
|
+
*/
|
|
8
|
+
export interface RegisteredTool {
|
|
9
|
+
name: string;
|
|
10
|
+
fn: (...args: unknown[]) => unknown | Promise<unknown>;
|
|
11
|
+
wrapped: (...args: unknown[]) => Promise<unknown>;
|
|
12
|
+
isAsync: boolean;
|
|
13
|
+
signature: string;
|
|
14
|
+
sourceFile: string | null;
|
|
15
|
+
lineNumber: number | null;
|
|
16
|
+
}
|
|
17
|
+
export declare function getRegisteredTools(): RegisteredTool[];
|
|
18
|
+
export declare function getRegisteredTool(name: string): RegisteredTool | undefined;
|
|
19
|
+
export declare function clearToolRegistry(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Register a function as a rerunnable tool.
|
|
22
|
+
*
|
|
23
|
+
* export const myTool = edTool('my_tool', async (query: string) => { ... })
|
|
24
|
+
*
|
|
25
|
+
* The returned function is the telemetry-wrapped version, so calling it from
|
|
26
|
+
* normal code paths still produces traces. The CLI `run-tool` command and the
|
|
27
|
+
* ElasticDash MCP `run_tool` tool will resolve the original by name.
|
|
28
|
+
*/
|
|
29
|
+
export declare function edTool<Args extends unknown[], R>(name: string, fn: (...args: Args) => R | Promise<R>): (...args: Args) => Promise<R>;
|
|
30
|
+
export declare const defineTool: typeof edTool;
|
|
31
|
+
//# sourceMappingURL=tool-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../src/tool-registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACjD,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAID,wBAAgB,kBAAkB,IAAI,cAAc,EAAE,CAErD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE1E;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC,EAC9C,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACpC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAkB/B;AAED,eAAO,MAAM,UAAU,eAAS,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global registry for tools defined via edTool().
|
|
3
|
+
*
|
|
4
|
+
* Tools registered here are discoverable for reruns regardless of which
|
|
5
|
+
* module they live in. The helper also applies wrapTool() so telemetry
|
|
6
|
+
* fires automatically — symmetric with the Python @ed_tool decorator.
|
|
7
|
+
*/
|
|
8
|
+
import { wrapTool } from './interceptors/tool.js';
|
|
9
|
+
const _registry = new Map();
|
|
10
|
+
export function getRegisteredTools() {
|
|
11
|
+
return Array.from(_registry.values());
|
|
12
|
+
}
|
|
13
|
+
export function getRegisteredTool(name) {
|
|
14
|
+
return _registry.get(name);
|
|
15
|
+
}
|
|
16
|
+
export function clearToolRegistry() {
|
|
17
|
+
_registry.clear();
|
|
18
|
+
}
|
|
19
|
+
function inferCallerLocation() {
|
|
20
|
+
const stack = new Error().stack;
|
|
21
|
+
if (!stack)
|
|
22
|
+
return { file: null, line: null };
|
|
23
|
+
const lines = stack.split('\n');
|
|
24
|
+
for (let i = 2; i < lines.length; i++) {
|
|
25
|
+
const m = lines[i].match(/\((.*?):(\d+):\d+\)|at\s+(.*?):(\d+):\d+/);
|
|
26
|
+
if (!m)
|
|
27
|
+
continue;
|
|
28
|
+
const file = m[1] ?? m[3];
|
|
29
|
+
const line = parseInt(m[2] ?? m[4], 10);
|
|
30
|
+
if (file && !file.includes('tool-registry')) {
|
|
31
|
+
return { file: file.replace(/^file:\/\//, ''), line: isNaN(line) ? null : line };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { file: null, line: null };
|
|
35
|
+
}
|
|
36
|
+
function inferSignature(fn) {
|
|
37
|
+
const src = fn.toString();
|
|
38
|
+
const m = src.match(/^[^(]*\(([^)]*)\)/);
|
|
39
|
+
if (!m)
|
|
40
|
+
return '()';
|
|
41
|
+
const params = m[1]
|
|
42
|
+
.split(',')
|
|
43
|
+
.map(p => p.trim().split(/[\s=:]/)[0])
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
return `(${params.join(', ')})`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Register a function as a rerunnable tool.
|
|
49
|
+
*
|
|
50
|
+
* export const myTool = edTool('my_tool', async (query: string) => { ... })
|
|
51
|
+
*
|
|
52
|
+
* The returned function is the telemetry-wrapped version, so calling it from
|
|
53
|
+
* normal code paths still produces traces. The CLI `run-tool` command and the
|
|
54
|
+
* ElasticDash MCP `run_tool` tool will resolve the original by name.
|
|
55
|
+
*/
|
|
56
|
+
export function edTool(name, fn) {
|
|
57
|
+
const isAsync = fn.constructor.name === 'AsyncFunction';
|
|
58
|
+
const signature = inferSignature(fn);
|
|
59
|
+
const { file, line } = inferCallerLocation();
|
|
60
|
+
const wrapped = wrapTool(name, fn);
|
|
61
|
+
_registry.set(name, {
|
|
62
|
+
name,
|
|
63
|
+
fn: fn,
|
|
64
|
+
wrapped: wrapped,
|
|
65
|
+
isAsync,
|
|
66
|
+
signature,
|
|
67
|
+
sourceFile: file,
|
|
68
|
+
lineNumber: line,
|
|
69
|
+
});
|
|
70
|
+
return wrapped;
|
|
71
|
+
}
|
|
72
|
+
export const defineTool = edTool;
|
|
73
|
+
//# sourceMappingURL=tool-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registry.js","sourceRoot":"","sources":["../src/tool-registry.ts"],"names":[],"mappings":"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"}
|