elasticdash-test 0.1.25 → 0.1.26-alpha-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.
@@ -7,6 +7,8 @@
7
7
  > **How to use**: Read the full guide first to understand the project's structure, then execute each step. Use the decision trees section to choose the right pattern for the user's project (framework type, module system, tool architecture).
8
8
  >
9
9
  > **Important**: Do NOT leave `YOUR_TOOL_NAME` / `YOUR_WORKFLOW_NAME` placeholders in generated files. Before creating `ed_tools.ts` and `ed_workflows.ts`, scan the user's codebase to identify their actual tool functions (functions that call external APIs, databases, or services) and workflow functions (async functions that orchestrate LLM calls and tool calls). Create the files with the real function names, imports, and types found in the codebase. If no tools or workflows exist yet, ask the user which functions to instrument rather than generating placeholder files.
10
+ >
11
+ > **Automation level**: Automatically implement all steps unless you are not confident about a specific change (e.g., unclear which function is a tool vs. a utility, or ambiguous workflow boundaries). In those cases, ask the user before proceeding. For straightforward tool/workflow functions, proceed without asking.
10
12
 
11
13
  ---
12
14
 
@@ -39,6 +41,26 @@ Check these before choosing patterns:
39
41
 
40
42
  ---
41
43
 
44
+ ## Step 0: Check environment variables (do this FIRST)
45
+
46
+ Before making any code changes, check the project's `.env` file for the required ElasticDash variables.
47
+
48
+ ### 1. Check for `ELASTICDASH_API_URL`
49
+
50
+ - If present: use the existing value.
51
+ - If missing: add `ELASTICDASH_API_URL=https://server.elasticdash.com` to `.env`.
52
+
53
+ ### 2. Check for `ELASTICDASH_API_KEY`
54
+
55
+ - If present: proceed to Step 1.
56
+ - If missing: **STOP and ask the user**:
57
+
58
+ > "Your project needs an ElasticDash API key. Please complete the onboarding at https://app.elasticdash.com to get your project API key, then add `ELASTICDASH_API_KEY=ed_your_key_here` to your `.env` file. Let me know when it's ready."
59
+
60
+ **Do not continue until `ELASTICDASH_API_KEY` is set.** The SDK cannot connect to the backend without it.
61
+
62
+ ---
63
+
42
64
  ## Step 1: Install
43
65
 
44
66
  ```bash
@@ -56,163 +78,63 @@ Add to `.gitignore`:
56
78
 
57
79
  ## Step 2: Create `ed_tools.ts`
58
80
 
59
- Create `ed_tools.ts` in the project root. This file wraps each tool function with tracing so ElasticDash can record and replay tool calls.
81
+ Create `ed_tools.ts` in the project root. This file wraps each tool function with `wrapTool()` for automatic tracing, mocking, and telemetry.
60
82
 
61
83
  **Before writing this file**: Search the codebase for functions that call external services (APIs, databases, SDKs). Each one becomes a wrapped export in `ed_tools.ts`. Replace all `YOUR_TOOL_*` and `YOUR_SOURCE_PATH_*` placeholders below with the actual function names and import paths found in the project.
62
84
 
63
- ### Choose your pattern
64
-
65
- - **Pattern B (recommended for most projects)**: inline async with mock support. Use when tools are imported individually, no central dispatcher exists, or you want dashboard mock support.
66
- - **Pattern A**: `withTrace` HOF with central dispatcher. Use when tools are pure functions called through a single `dispatchTool(name, args)` dispatcher.
67
-
68
- ### Pattern B template (recommended)
85
+ ### Template
69
86
 
70
87
  ```ts
71
88
  // ed_tools.ts
72
- // Replace imports with your actual tool functions and source paths
73
- import { YOUR_TOOL_1 as YOUR_TOOL_1_impl } from './YOUR_SOURCE_PATH_1'
74
- import { YOUR_TOOL_2 as YOUR_TOOL_2_impl } from './YOUR_SOURCE_PATH_2'
89
+ import { setElasticDashModule } from './ed_workflows'
90
+
91
+ // Import original tool implementations from the actual source files
92
+ import { originalTool1 } from './services/YOUR_SOURCE_1'
93
+ import { originalTool2 } from './utils/YOUR_SOURCE_2'
75
94
 
76
95
  // ---------------------------------------------------------------------------
77
- // Helpers copy as-is, no customization needed
96
+ // Load elasticdash-test and share the module with ed_workflows.ts
78
97
  // ---------------------------------------------------------------------------
79
98
 
80
- function resolveMock(toolName: string): { mocked: true; result: unknown } | { mocked: false } {
81
- const g = globalThis as any
82
- const mocks = g.__ELASTICDASH_TOOL_MOCKS__
83
- if (!mocks) return { mocked: false }
84
-
85
- const entry = mocks[toolName]
86
- if (!entry || entry.mode === 'live') return { mocked: false }
87
-
88
- if (!g.__ELASTICDASH_TOOL_CALL_COUNTERS__) g.__ELASTICDASH_TOOL_CALL_COUNTERS__ = {}
89
- const counters = g.__ELASTICDASH_TOOL_CALL_COUNTERS__
90
- counters[toolName] = (counters[toolName] ?? 0) + 1
91
- const callNumber = counters[toolName]
92
-
93
- if (entry.mode === 'mock-all') {
94
- const data = entry.mockData ?? {}
95
- const result = data[callNumber] !== undefined ? data[callNumber] : data[0]
96
- return { mocked: true, result }
97
- }
98
-
99
- if (entry.mode === 'mock-specific') {
100
- const indices = entry.callIndices ?? []
101
- if (indices.includes(callNumber)) {
102
- return { mocked: true, result: (entry.mockData ?? {})[callNumber] }
103
- }
104
- return { mocked: false }
105
- }
106
-
107
- return { mocked: false }
108
- }
109
-
110
- async function safeRecordToolCall(tool: string, input: any, result: any) {
111
- if (!(globalThis as any).__ELASTICDASH_WORKER__) return
112
- try {
113
- const { recordToolCall } = await import('elasticdash-test')
114
- recordToolCall(tool, input, result)
115
- } catch { /* tracing must never block business logic */ }
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ type WrapToolFn = <T extends (...args: any[]) => any>(name: string, fn: T) => T
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ let wrapTool: WrapToolFn = (_name: string, fn: any) => fn
103
+
104
+ try {
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ const _edModule = (eval('require') as (id: string) => any)('elasticdash-test')
107
+ wrapTool = _edModule.wrapTool ?? wrapTool
108
+ // Share the module instance with ed_workflows.ts so trace hooks use the same context
109
+ setElasticDashModule(_edModule)
110
+ } catch {
111
+ // elasticdash-test not available — all wrappers pass through to original functions
116
112
  }
117
113
 
118
114
  // ---------------------------------------------------------------------------
119
- // Tools — one export per tool, following this pattern
115
+ // Wrapped tools — one export per tool
120
116
  // ---------------------------------------------------------------------------
121
117
 
122
- // TEMPLATE: Copy this block for each tool, replacing YOUR_TOOL_NAME and YOUR_TOOL_IMPL
123
- export const YOUR_TOOL_1 = async (input: any) => {
124
- const mock = resolveMock('YOUR_TOOL_1')
125
- if (mock.mocked) {
126
- await safeRecordToolCall('YOUR_TOOL_1', input, mock.result)
127
- return mock.result
128
- }
129
-
130
- return await YOUR_TOOL_1_impl(input)
131
- .then(async (res: any) => {
132
- await safeRecordToolCall('YOUR_TOOL_1', input, res)
133
- return res
134
- })
135
- .catch(async (err: any) => {
136
- await safeRecordToolCall('YOUR_TOOL_1', input, err)
137
- throw err
138
- })
139
- }
140
-
141
- export const YOUR_TOOL_2 = async (input: any) => {
142
- const mock = resolveMock('YOUR_TOOL_2')
143
- if (mock.mocked) {
144
- await safeRecordToolCall('YOUR_TOOL_2', input, mock.result)
145
- return mock.result
146
- }
147
-
148
- return await YOUR_TOOL_2_impl(input)
149
- .then(async (res: any) => {
150
- await safeRecordToolCall('YOUR_TOOL_2', input, res)
151
- return res
152
- })
153
- .catch(async (err: any) => {
154
- await safeRecordToolCall('YOUR_TOOL_2', input, err)
155
- throw err
156
- })
157
- }
158
- ```
159
-
160
- ### Pattern A template (dispatcher-based)
161
-
162
- ```ts
163
- // ed_tools.ts
164
- // Replace imports with your actual tool functions and source paths
165
- import {
166
- YOUR_TOOL_1 as _YOUR_TOOL_1,
167
- YOUR_TOOL_2 as _YOUR_TOOL_2,
168
- dispatchTool as _dispatchTool,
169
- } from './YOUR_TOOLS_SOURCE'
170
-
171
- async function withTrace<I, O>(
172
- toolName: string,
173
- input: I,
174
- fn: (input: I) => Promise<O>,
175
- ): Promise<O> {
176
- const result = await fn(input)
177
- try {
178
- const { recordToolCall } = await import('elasticdash-test')
179
- recordToolCall(toolName, input, result)
180
- } catch { /* tracing must never block business logic */ }
181
- return result
182
- }
183
-
184
- export function YOUR_TOOL_1(input: Parameters<typeof _YOUR_TOOL_1>[0]) {
185
- return withTrace('YOUR_TOOL_1', input, _YOUR_TOOL_1)
186
- }
187
-
188
- export function YOUR_TOOL_2(input: Parameters<typeof _YOUR_TOOL_2>[0]) {
189
- return withTrace('YOUR_TOOL_2', input, _YOUR_TOOL_2)
190
- }
118
+ export const myTool1 = wrapTool('myTool1', async (input: any) => {
119
+ return await originalTool1(input)
120
+ })
191
121
 
192
- export async function dispatchTool(name: string, args: Record<string, unknown>) {
193
- switch (name) {
194
- case 'YOUR_TOOL_1': return YOUR_TOOL_1(args as Parameters<typeof _YOUR_TOOL_1>[0])
195
- case 'YOUR_TOOL_2': return YOUR_TOOL_2(args as Parameters<typeof _YOUR_TOOL_2>[0])
196
- default: return _dispatchTool(name, args)
197
- }
198
- }
122
+ export const myTool2 = wrapTool('myTool2', async (input: any) => {
123
+ const { someField } = input as { someField: string }
124
+ return await originalTool2(someField)
125
+ })
199
126
  ```
200
127
 
201
- ### Alternative: `wrapTool` shorthand
202
-
203
- For simpler setups where you don't need mock support or dashboard replays:
128
+ ### Key patterns
204
129
 
205
- ```ts
206
- // ed_tools.ts
207
- import { wrapTool } from 'elasticdash-test'
208
- import { YOUR_TOOL_1 as YOUR_TOOL_1_impl } from './YOUR_SOURCE_PATH'
209
-
210
- export const YOUR_TOOL_1 = wrapTool('YOUR_TOOL_1', YOUR_TOOL_1_impl)
211
- ```
130
+ - **`wrapTool(name, fn)`** wraps the function with automatic tracing, mocking, and telemetry. Falls back to a passthrough if `elasticdash-test` is not installed.
131
+ - **`eval('require')`** is used instead of `import()` to share the same module instance across `ed_tools.ts` and `ed_workflows.ts`. This avoids ESM/CJS dual-instance issues.
132
+ - **`setElasticDashModule`** shares the loaded module with `ed_workflows.ts` so `edStartTrace`/`edEndTrace` use the same tracing context as `wrapTool`.
133
+ - The exported name (e.g., `myTool1`) can differ from the original function name (e.g., `originalTool1`). The call sites in existing source files will be updated to use the new name in Step 4.
212
134
 
213
135
  ### Important rules
214
136
 
215
- - The string name passed to `resolveMock()`, `safeRecordToolCall()`, or `wrapTool()` **must match** the exported function name exactly.
137
+ - The string name passed to `wrapTool()` **must match** the exported function name exactly.
216
138
  - Each tool function must accept a single input object and return a plain value (JSON-serializable).
217
139
  - Tool functions must not close over HTTP context, framework state, or database clients — extract pure logic first.
218
140
 
@@ -232,128 +154,173 @@ export default nextConfig
232
154
 
233
155
  ## Step 3: Create `ed_workflows.ts`
234
156
 
235
- Create `ed_workflows.ts` in the project root. This file exports workflow functions for the ElasticDash runner.
157
+ Create `ed_workflows.ts` in the project root. This file serves two purposes:
158
+
159
+ 1. **Trace lifecycle hooks** (`edStartTrace`, `edEndTrace`) — called from route handlers to mark workflow boundaries
160
+ 2. **Workflow exports** — callable functions for the ElasticDash dashboard and test runner
236
161
 
237
162
  **Before writing this file**: Search the codebase for the main AI workflow functions — async functions that orchestrate LLM calls and tool calls (e.g., a chat handler, an agent loop, a pipeline function). Replace all `YOUR_WORKFLOW` and `YOUR_SOURCE_PATH` placeholders below with the actual function names and import paths.
238
163
 
239
- ### Simple case — direct re-export
164
+ ### Trace lifecycle hooks
165
+
166
+ Every `ed_workflows.ts` should export `edStartTrace` and `edEndTrace`. These are called from route handlers (Step 4) to mark when a workflow starts and ends:
240
167
 
241
168
  ```ts
242
- // ed_workflows.ts
243
- // Replace with your actual workflow function and source path
244
- export { YOUR_WORKFLOW } from './YOUR_SOURCE_PATH'
169
+ // ed_workflows.ts — trace hooks (copy as-is)
170
+
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ let _ed: any = null
173
+
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
+ export function setElasticDashModule(mod: any): void {
176
+ _ed = mod
177
+ }
178
+
179
+ export const edStartTrace = async (workflowName: string): Promise<void> => {
180
+ if (!_ed) return
181
+ try {
182
+ await _ed.tryAutoInitHttpContext()
183
+ _ed.startTrace(workflowName)
184
+ } catch (err) {
185
+ console.error('[ed_workflows] edStartTrace error:', err)
186
+ }
187
+ }
188
+
189
+ export const edEndTrace = (): void => {
190
+ if (!_ed) return
191
+ try {
192
+ _ed.endTrace()
193
+ } catch (err) {
194
+ console.error('[ed_workflows] edEndTrace error:', err)
195
+ }
196
+ }
245
197
  ```
246
198
 
247
- ### Framework adapter case (Next.js / Remix)
199
+ ### Workflow exports simple case
248
200
 
249
- When the workflow lives inside a route handler, create a plain-value wrapper:
201
+ For non-framework projects where the workflow can be imported directly:
250
202
 
251
203
  ```ts
252
204
  // ed_workflows.ts
253
- import { YOUR_HANDLER as _YOUR_HANDLER } from './app/api/YOUR_ROUTE/route'
254
-
255
- export async function YOUR_WORKFLOW(input: { message: string; sessionId: string }) {
256
- return _YOUR_HANDLER(input)
257
- }
205
+ export { YOUR_WORKFLOW } from './YOUR_SOURCE_PATH'
258
206
  ```
259
207
 
260
- ### Streaming workflow case (Vercel AI SDK)
208
+ ### Workflow exports HTTP mode (Next.js / Remix / framework projects)
261
209
 
262
- Create a separate handler file that is only imported by `ed_workflows.ts`:
210
+ For framework projects, the workflow calls the running dev server instead of importing the route handler:
263
211
 
264
212
  ```ts
265
- // app/api/chat-stream/chatStreamHandler.ts
266
- import { NextRequest } from 'next/server'
267
- import { readVercelAIStream, recordToolCall } from 'elasticdash-test'
268
- import type { VercelAIStreamResult } from 'elasticdash-test'
269
- import { POST } from './route'
213
+ // ed_workflows.ts
214
+
215
+ const APP_URL = process.env.APP_URL ?? 'http://localhost:3000'
270
216
 
271
- export async function chatStreamHandler(args: {
217
+ export const YOUR_WORKFLOW = async (input: {
272
218
  messages: Array<{ role: string; content: string }>
273
219
  sessionId?: string
274
- }): Promise<VercelAIStreamResult> {
275
- const req = new NextRequest('http://localhost/api/chat-stream', {
220
+ }): Promise<unknown> => {
221
+ const response = await fetch(`${APP_URL}/api/YOUR_ENDPOINT`, {
276
222
  method: 'POST',
277
223
  headers: { 'Content-Type': 'application/json' },
278
- body: JSON.stringify(args),
224
+ body: JSON.stringify(input),
279
225
  })
280
226
 
281
- const response = await POST(req)
282
-
283
- if (response.headers.get('x-vercel-ai-data-stream') !== 'v1') {
284
- const errorMessage = await response.text().catch(() => `HTTP ${response.status}`)
285
- return { message: errorMessage, type: 'error', error: errorMessage }
227
+ if (!response.ok) {
228
+ const text = await response.text()
229
+ throw new Error(`HTTP ${response.status}: ${text}`)
286
230
  }
287
231
 
288
- const result = await readVercelAIStream(response)
289
- recordToolCall('chatStream', args, result)
290
- return result
232
+ return response.json()
291
233
  }
292
234
  ```
293
235
 
294
- Then re-export from `ed_workflows.ts`:
295
-
296
- ```ts
297
- // ed_workflows.ts
298
- export { chatStreamHandler } from './app/api/chat-stream/chatStreamHandler'
299
- ```
300
-
301
236
  ### Requirements for all workflow exports
302
237
 
303
238
  - Accept only JSON-serializable inputs (strings, numbers, arrays, plain objects)
304
239
  - Return only JSON-serializable outputs
305
240
  - Must not depend on framework runtime APIs, HTTP request context, or live service clients
306
- - If a dependency is non-serializable (e.g., database client), instantiate it inside `ed_workflows.ts`, not passed as a parameter
307
241
 
308
242
  ---
309
243
 
310
- ## Step 4: Wire `ed_workflows.ts` to use `ed_tools.ts`
244
+ ## Step 4: Update existing source files to use `ed_tools`
311
245
 
312
- > **Do not modify the user's existing production source files.** Only `ed_workflows.ts` needs to import from `ed_tools.ts`. The production codebase stays untouched.
246
+ > **This step modifies the user's existing source files.** The files that call tool functions must import from `ed_tools` instead of the original source, so that all tool calls go through the ElasticDash tracing layer. Similarly, workflow entry points (route handlers, etc.) must call `edStartTrace` / `edEndTrace` from `ed_workflows`.
313
247
 
314
- The ElasticDash layer sits alongside the production code, not inside it:
248
+ ### Architecture after integration
315
249
 
316
250
  ```
317
- Production code (unchanged):
318
- src/workflows/checkout.ts imports from src/services/payments.ts directly
251
+ ed_tools.ts
252
+ ├── imports original functions from services/utils
253
+ ├── wraps each with wrapTool() for tracing
254
+ └── exports wrapped versions with the SAME or similar names
255
+
256
+ ed_workflows.ts
257
+ ├── exports edStartTrace / edEndTrace for workflow-level tracing
258
+ └── exports workflow functions (for dashboard/test runner)
319
259
 
320
- ElasticDash layer (new files only):
321
- ed_workflows.ts → imports tools from ed_tools.ts → calls the real service functions
260
+ Existing source files (MODIFIED):
261
+ app/api/chat/route.ts
262
+ ├── BEFORE: import { myTool } from '@/services/myService'
263
+ ├── AFTER: import { myTool } from '@/ed_tools'
264
+ ├── ADDED: import { edStartTrace, edEndTrace } from '@/ed_workflows'
265
+ └── ADDED: await edStartTrace('workflowName') at handler entry
266
+ edEndTrace() at handler exit
322
267
  ```
323
268
 
324
269
  ### What to do
325
270
 
326
- In `ed_workflows.ts`, write wrapper functions that call tools through `ed_tools.ts` instead of importing from the original source. The ElasticDash test runner and dashboard call `ed_workflows.ts` — not the production code directly.
271
+ **1. Find every file that calls a tool function and update its imports:**
327
272
 
328
- ### Example
273
+ For each tool exported from `ed_tools.ts`, search the codebase for files that import the original function. Update the import to come from `ed_tools` instead.
329
274
 
330
275
  ```ts
331
- // ed_workflows.ts
332
- // Import tools from ed_tools.ts (instrumented), NOT from original source
333
- import { chargeCard, getOrderDetails } from './ed_tools'
334
-
335
- // Re-create the workflow logic using instrumented tools
336
- export async function checkoutFlow(input: { orderId: string }) {
337
- const order = await getOrderDetails({ orderId: input.orderId })
338
- const payment = await chargeCard({ amount: order.total })
339
- return { orderId: order.orderId, paymentId: payment.id }
340
- }
276
+ // BEFORE — app/api/chat/route.ts
277
+ import { clarifyAndRefineUserInput } from '@/utils/queryRefinement'
278
+ import { dynamicApiRequest } from '@/services/apiService'
279
+
280
+ // AFTER app/api/chat/route.ts
281
+ import { queryRefinement } from '@/ed_tools'
282
+ import { apiService } from '@/ed_tools'
341
283
  ```
342
284
 
343
285
  ```ts
344
- // ed_tools.ts
345
- // Import from the ORIGINAL source
346
- import { chargeCard as chargeCardImpl } from './src/services/payments'
347
- import { getOrderDetails as getOrderDetailsImpl } from './src/services/orders'
286
+ // BEFORE — app/api/chat/executor.ts
287
+ import { dynamicApiRequest } from '@/services/apiService'
348
288
 
349
- // ... wrap with resolveMock + safeRecordToolCall pattern
289
+ // AFTER app/api/chat/executor.ts
290
+ import { apiService } from '@/ed_tools'
350
291
  ```
351
292
 
352
- ### Why this approach
293
+ **2. Add trace hooks to route handlers / workflow entry points:**
353
294
 
354
- - **No production risk**: Existing source files are never modified. Production imports stay as-is.
355
- - **Clean separation**: The `ed_` files are a parallel test/observability layer, not a refactor of the codebase.
356
- - **Easy removal**: Delete `ed_tools.ts` and `ed_workflows.ts` to fully remove ElasticDash — no rollback needed.
295
+ At the top of each route handler or workflow entry function, add `edStartTrace` and `edEndTrace`:
296
+
297
+ ```ts
298
+ // app/api/chat/route.ts
299
+ import { edStartTrace, edEndTrace } from '@/ed_workflows'
300
+
301
+ export async function POST(req: Request) {
302
+ await edStartTrace('chatHandler')
303
+ try {
304
+ // ... existing handler logic, now using tools from ed_tools
305
+ } finally {
306
+ edEndTrace()
307
+ }
308
+ }
309
+ ```
310
+
311
+ **3. Update call sites if the wrapped function has a different name:**
312
+
313
+ If `ed_tools.ts` exports a tool under a different name than the original (e.g., `queryRefinement` wrapping `clarifyAndRefineUserInput`), update the call sites to use the new name.
314
+
315
+ ### What NOT to modify
316
+
317
+ - **Do not modify `ed_tools.ts` imports** — it must still import from the original source files.
318
+ - **Do not modify the original tool implementation files** (e.g., `services/apiService.ts`, `utils/queryRefinement.ts`) — they stay as-is.
319
+ - **Do not modify files that don't call tool functions** — only update files that directly import and call the wrapped tools.
320
+
321
+ ### When to ask the user
322
+
323
+ If you are not confident about a change — e.g., a function might be a utility rather than a tool, or it's unclear whether a file should use the wrapped version — **ask the user** before modifying that file. For clear tool calls (API services, database queries, LLM calls), proceed automatically.
357
324
 
358
325
  ---
359
326
 
@@ -474,7 +441,30 @@ npx ed ed-test --no-upload
474
441
 
475
442
  ---
476
443
 
477
- ## Step 8: Run and verify
444
+ ## Step 8: Validate the integration
445
+
446
+ After all files are created, validate the setup by establishing a socket.io connection to the ElasticDash backend. Run the `observe` command — it connects to the server, authenticates with the API key, and registers the project's tools and workflows:
447
+
448
+ ```bash
449
+ npx elasticdash observe
450
+ ```
451
+
452
+ **Expected output on success:**
453
+
454
+ ```
455
+ [elasticdash] Observability active
456
+ Session ID : <uuid>
457
+ Server : https://server.elasticdash.com
458
+ ```
459
+
460
+ This confirms:
461
+ - `ELASTICDASH_API_URL` and `ELASTICDASH_API_KEY` are valid
462
+ - The socket.io connection to the backend is established
463
+ - The SDK can discover `ed_tools.ts` and `ed_workflows.ts`
464
+
465
+ **If it fails:** Check that `.env` has valid `ELASTICDASH_API_URL` and `ELASTICDASH_API_KEY` values. If the API key is rejected, the user needs to get a new one from https://app.elasticdash.com.
466
+
467
+ After validation, stop the observe process (Ctrl+C) and inform the user that ElasticDash is integrated. Provide these commands for ongoing use:
478
468
 
479
469
  ```bash
480
470
  # Run aiTest tests
@@ -486,6 +476,9 @@ npx ed ed-test --no-upload
486
476
  # Open the dashboard
487
477
  npx elasticdash dashboard
488
478
 
479
+ # Start observability (connect to backend for live tracing)
480
+ npx elasticdash observe
481
+
489
482
  # Record a trace fixture
490
483
  ELASTICDASH_CAPTURE_TRACE=1 tsx your-workflow.ts
491
484
  ```
@@ -591,16 +584,12 @@ After integration, verify these files exist:
591
584
 
592
585
  ```
593
586
  your-project/
594
- ed_tools.ts # Instrumented tool wrappers
595
- ed_workflows.ts # Workflow exports
587
+ .env # ELASTICDASH_API_URL and ELASTICDASH_API_KEY set
588
+ ed_tools.ts # Instrumented tool wrappers (real function names, not placeholders)
589
+ ed_workflows.ts # Workflow exports using tools from ed_tools.ts
596
590
  elasticdash.config.ts # Test runner config
597
591
  package.json # dashboard:ai and test:ai scripts added
598
592
  .gitignore # .temp/ and .ed_traces/ added
599
593
  ```
600
594
 
601
- Verify with:
602
-
603
- ```bash
604
- npx elasticdash test # Should discover and run *.ai.test.ts files
605
- npx elasticdash dashboard # Should open the dashboard UI
606
- ```
595
+ Verify by running `npx elasticdash observe` — a successful socket.io connection confirms the integration is complete.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elasticdash-test",
3
- "version": "0.1.25",
3
+ "version": "0.1.26-alpha-1",
4
4
  "description": "AI-native test runner for ElasticDash workflow testing",
5
5
  "type": "module",
6
6
  "bin": {