@agentxjs/devtools 1.9.5-dev → 2.0.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/README.md +284 -0
- package/dist/bdd/cli.d.ts +1 -0
- package/dist/bdd/cli.js +117 -0
- package/dist/bdd/cli.js.map +1 -0
- package/dist/bdd/index.d.ts +202 -0
- package/dist/bdd/index.js +381 -0
- package/dist/bdd/index.js.map +1 -0
- package/dist/chunk-6OHXS7LW.js +297 -0
- package/dist/chunk-6OHXS7LW.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-DR45HEV4.js +152 -0
- package/dist/chunk-DR45HEV4.js.map +1 -0
- package/dist/chunk-J6L73HM5.js +301 -0
- package/dist/chunk-J6L73HM5.js.map +1 -0
- package/dist/chunk-S7J75AXG.js +64 -0
- package/dist/chunk-S7J75AXG.js.map +1 -0
- package/dist/fixtures/index.d.ts +49 -0
- package/dist/fixtures/index.js +22 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/index.d.ts +240 -0
- package/dist/index.js +269 -0
- package/dist/index.js.map +1 -0
- package/dist/mock/index.d.ts +115 -0
- package/dist/mock/index.js +11 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/recorder/index.d.ts +120 -0
- package/dist/recorder/index.js +10 -0
- package/dist/recorder/index.js.map +1 -0
- package/dist/types-C6Lf3vz2.d.ts +78 -0
- package/package.json +63 -8
- package/src/Devtools.ts +11 -14
- package/src/bdd/agent-doc-tester.ts +130 -0
- package/src/bdd/agent-ui-tester.ts +88 -0
- package/src/bdd/cli.ts +166 -0
- package/src/bdd/cucumber.config.ts +40 -0
- package/src/bdd/dev-server.ts +82 -0
- package/src/bdd/index.ts +41 -0
- package/src/bdd/paths.ts +140 -0
- package/src/bdd/playwright.ts +110 -0
- package/src/env.ts +97 -0
- package/src/index.ts +6 -1
- package/src/mock/MockDriver.ts +21 -12
- package/src/recorder/RecordingDriver.ts +1 -5
- package/scripts/record-fixture.ts +0 -148
- package/tsconfig.json +0 -10
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { CreateDriver, Driver } from '@agentxjs/core/driver';
|
|
2
|
+
import { F as Fixture } from './types-C6Lf3vz2.js';
|
|
3
|
+
export { a as FixtureEvent, M as MockDriverOptions } from './types-C6Lf3vz2.js';
|
|
4
|
+
export { MockDriver, createMockDriver } from './mock/index.js';
|
|
5
|
+
export { RecordingDriver, RecordingDriverOptions, createRecordingDriver } from './recorder/index.js';
|
|
6
|
+
export { BUILTIN_FIXTURES, EMPTY_RESPONSE, ERROR_RESPONSE, LONG_REPLY, SIMPLE_REPLY, TOOL_CALL, getFixture, listFixtures } from './fixtures/index.js';
|
|
7
|
+
import '@agentxjs/core/agent';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Devtools SDK - VCR-style fixture management
|
|
11
|
+
*
|
|
12
|
+
* Automatically uses existing fixtures or records new ones on-the-fly.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { createDevtools } from "@agentxjs/devtools";
|
|
17
|
+
*
|
|
18
|
+
* import { env } from "@agentxjs/devtools";
|
|
19
|
+
*
|
|
20
|
+
* const devtools = createDevtools({
|
|
21
|
+
* fixturesDir: "./fixtures",
|
|
22
|
+
* apiKey: env.apiKey,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Has fixture → playback (MockDriver)
|
|
26
|
+
* // No fixture → call API, record, save, return MockDriver
|
|
27
|
+
* const driver = await devtools.driver("hello-test", {
|
|
28
|
+
* message: "Hello!",
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* await driver.initialize();
|
|
32
|
+
* for await (const event of driver.receive({ content: "Hello" })) {
|
|
33
|
+
* console.log(event);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Devtools configuration
|
|
40
|
+
*/
|
|
41
|
+
interface DevtoolsConfig {
|
|
42
|
+
/**
|
|
43
|
+
* Directory to store/load fixtures
|
|
44
|
+
*/
|
|
45
|
+
fixturesDir: string;
|
|
46
|
+
/**
|
|
47
|
+
* API key for recording (required if recording)
|
|
48
|
+
*/
|
|
49
|
+
apiKey?: string;
|
|
50
|
+
/**
|
|
51
|
+
* API base URL
|
|
52
|
+
*/
|
|
53
|
+
baseUrl?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Default model
|
|
56
|
+
*/
|
|
57
|
+
model?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Default system prompt
|
|
60
|
+
*/
|
|
61
|
+
systemPrompt?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Working directory for tool execution
|
|
64
|
+
*/
|
|
65
|
+
cwd?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Real driver factory for recording
|
|
68
|
+
* If not provided, will try to use @agentxjs/claude-driver
|
|
69
|
+
*/
|
|
70
|
+
createDriver?: CreateDriver;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Options for getting a driver
|
|
74
|
+
*/
|
|
75
|
+
interface DriverOptions {
|
|
76
|
+
/**
|
|
77
|
+
* Message to send if recording is needed
|
|
78
|
+
*/
|
|
79
|
+
message: string;
|
|
80
|
+
/**
|
|
81
|
+
* Override system prompt
|
|
82
|
+
*/
|
|
83
|
+
systemPrompt?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Override working directory
|
|
86
|
+
*/
|
|
87
|
+
cwd?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Force re-record even if fixture exists
|
|
90
|
+
*/
|
|
91
|
+
forceRecord?: boolean;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Devtools SDK
|
|
95
|
+
*/
|
|
96
|
+
declare class Devtools {
|
|
97
|
+
private config;
|
|
98
|
+
private realCreateDriver;
|
|
99
|
+
constructor(config: DevtoolsConfig);
|
|
100
|
+
/**
|
|
101
|
+
* Get a driver for the given fixture name
|
|
102
|
+
*
|
|
103
|
+
* - If fixture exists → returns MockDriver with playback
|
|
104
|
+
* - If fixture doesn't exist → records, saves, returns MockDriver
|
|
105
|
+
*/
|
|
106
|
+
driver(name: string, options: DriverOptions): Promise<Driver>;
|
|
107
|
+
/**
|
|
108
|
+
* Get a CreateDriver function that uses a pre-loaded fixture
|
|
109
|
+
*
|
|
110
|
+
* NOTE: This loads the fixture synchronously, so the fixture must exist.
|
|
111
|
+
* For async loading/recording, use driver() instead.
|
|
112
|
+
*/
|
|
113
|
+
createDriverForFixture(fixturePath: string): CreateDriver;
|
|
114
|
+
/**
|
|
115
|
+
* Record a fixture
|
|
116
|
+
*/
|
|
117
|
+
record(name: string, options: DriverOptions): Promise<Fixture>;
|
|
118
|
+
/**
|
|
119
|
+
* Load a fixture by name
|
|
120
|
+
*/
|
|
121
|
+
load(name: string): Promise<Fixture>;
|
|
122
|
+
/**
|
|
123
|
+
* Check if a fixture exists
|
|
124
|
+
*/
|
|
125
|
+
exists(name: string): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Delete a fixture
|
|
128
|
+
*/
|
|
129
|
+
delete(name: string): Promise<void>;
|
|
130
|
+
private getFixturePath;
|
|
131
|
+
private loadFixture;
|
|
132
|
+
private saveFixture;
|
|
133
|
+
private getRealCreateDriver;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create a Devtools instance
|
|
137
|
+
*/
|
|
138
|
+
declare function createDevtools(config: DevtoolsConfig): Devtools;
|
|
139
|
+
/**
|
|
140
|
+
* Configuration for VCR-aware CreateDriver
|
|
141
|
+
*/
|
|
142
|
+
interface VcrCreateDriverConfig {
|
|
143
|
+
/**
|
|
144
|
+
* Directory to store/load fixtures
|
|
145
|
+
*/
|
|
146
|
+
fixturesDir: string;
|
|
147
|
+
/**
|
|
148
|
+
* Get current fixture name. Return null to skip VCR (use real driver).
|
|
149
|
+
* Called when driver is created.
|
|
150
|
+
*/
|
|
151
|
+
getFixtureName: () => string | null;
|
|
152
|
+
/**
|
|
153
|
+
* API key for recording
|
|
154
|
+
*/
|
|
155
|
+
apiKey?: string;
|
|
156
|
+
/**
|
|
157
|
+
* API base URL
|
|
158
|
+
*/
|
|
159
|
+
baseUrl?: string;
|
|
160
|
+
/**
|
|
161
|
+
* Default model
|
|
162
|
+
*/
|
|
163
|
+
model?: string;
|
|
164
|
+
/**
|
|
165
|
+
* Real driver factory (optional, defaults to @agentxjs/claude-driver)
|
|
166
|
+
*/
|
|
167
|
+
createRealDriver?: CreateDriver;
|
|
168
|
+
/**
|
|
169
|
+
* Called when playback mode is used
|
|
170
|
+
*/
|
|
171
|
+
onPlayback?: (fixtureName: string) => void;
|
|
172
|
+
/**
|
|
173
|
+
* Called when recording mode is used
|
|
174
|
+
*/
|
|
175
|
+
onRecording?: (fixtureName: string) => void;
|
|
176
|
+
/**
|
|
177
|
+
* Called when fixture is saved
|
|
178
|
+
*/
|
|
179
|
+
onSaved?: (fixtureName: string, eventCount: number) => void;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create a VCR-aware CreateDriver function
|
|
183
|
+
*
|
|
184
|
+
* VCR logic (hardcoded):
|
|
185
|
+
* - Fixture exists → Playback (MockDriver)
|
|
186
|
+
* - Fixture missing → Recording (RecordingDriver) → Auto-save on dispose
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* let currentFixture: string | null = null;
|
|
191
|
+
*
|
|
192
|
+
* const vcrCreateDriver = createVcrCreateDriver({
|
|
193
|
+
* fixturesDir: "./fixtures",
|
|
194
|
+
* getFixtureName: () => currentFixture,
|
|
195
|
+
* apiKey: process.env.API_KEY,
|
|
196
|
+
* });
|
|
197
|
+
*
|
|
198
|
+
* // Before each test:
|
|
199
|
+
* currentFixture = "test-scenario-name";
|
|
200
|
+
*
|
|
201
|
+
* // Use with server:
|
|
202
|
+
* const platform = await createNodePlatform({...});
|
|
203
|
+
* const server = await createServer({
|
|
204
|
+
* platform,
|
|
205
|
+
* createDriver: vcrCreateDriver,
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
declare function createVcrCreateDriver(config: VcrCreateDriverConfig): CreateDriver;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Unified environment configuration for devtools
|
|
213
|
+
*
|
|
214
|
+
* Single source of truth for API credentials and model settings.
|
|
215
|
+
* Automatically loads .env / .env.local from monorepo root on import.
|
|
216
|
+
*
|
|
217
|
+
* All devtools modules (VCR, BDD, Devtools SDK) should use this
|
|
218
|
+
* instead of reading process.env directly.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* import { env } from "@agentxjs/devtools";
|
|
223
|
+
*
|
|
224
|
+
* const driver = createMonoDriver({
|
|
225
|
+
* apiKey: env.apiKey!,
|
|
226
|
+
* baseUrl: env.baseUrl,
|
|
227
|
+
* model: env.model,
|
|
228
|
+
* });
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
declare const env: {
|
|
232
|
+
/** Deepractice API key */
|
|
233
|
+
readonly apiKey: string | undefined;
|
|
234
|
+
/** Deepractice API base URL */
|
|
235
|
+
readonly baseUrl: string | undefined;
|
|
236
|
+
/** Model identifier */
|
|
237
|
+
readonly model: string;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export { Devtools, type DevtoolsConfig, type DriverOptions, Fixture, type VcrCreateDriverConfig, createDevtools, createVcrCreateDriver, env };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MockDriver,
|
|
3
|
+
createMockDriver
|
|
4
|
+
} from "./chunk-J6L73HM5.js";
|
|
5
|
+
import {
|
|
6
|
+
RecordingDriver,
|
|
7
|
+
createRecordingDriver
|
|
8
|
+
} from "./chunk-DR45HEV4.js";
|
|
9
|
+
import {
|
|
10
|
+
BUILTIN_FIXTURES,
|
|
11
|
+
EMPTY_RESPONSE,
|
|
12
|
+
ERROR_RESPONSE,
|
|
13
|
+
LONG_REPLY,
|
|
14
|
+
SIMPLE_REPLY,
|
|
15
|
+
TOOL_CALL,
|
|
16
|
+
getFixture,
|
|
17
|
+
listFixtures
|
|
18
|
+
} from "./chunk-6OHXS7LW.js";
|
|
19
|
+
import {
|
|
20
|
+
env
|
|
21
|
+
} from "./chunk-S7J75AXG.js";
|
|
22
|
+
import "./chunk-DGUM43GV.js";
|
|
23
|
+
|
|
24
|
+
// src/Devtools.ts
|
|
25
|
+
import { createLogger } from "commonxjs/logger";
|
|
26
|
+
import { existsSync, readFileSync } from "fs";
|
|
27
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
28
|
+
import { join, dirname } from "path";
|
|
29
|
+
var logger = createLogger("devtools/Devtools");
|
|
30
|
+
var Devtools = class {
|
|
31
|
+
config;
|
|
32
|
+
realCreateDriver = null;
|
|
33
|
+
constructor(config) {
|
|
34
|
+
this.config = config;
|
|
35
|
+
logger.info("Devtools initialized", { fixturesDir: config.fixturesDir });
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get a driver for the given fixture name
|
|
39
|
+
*
|
|
40
|
+
* - If fixture exists → returns MockDriver with playback
|
|
41
|
+
* - If fixture doesn't exist → records, saves, returns MockDriver
|
|
42
|
+
*/
|
|
43
|
+
async driver(name, options) {
|
|
44
|
+
const fixturePath = this.getFixturePath(name);
|
|
45
|
+
if (!options.forceRecord && existsSync(fixturePath)) {
|
|
46
|
+
logger.info("Loading existing fixture", { name, path: fixturePath });
|
|
47
|
+
const fixture2 = await this.loadFixture(fixturePath);
|
|
48
|
+
return new MockDriver({ fixture: fixture2 });
|
|
49
|
+
}
|
|
50
|
+
logger.info("Recording new fixture", { name, message: options.message });
|
|
51
|
+
const fixture = await this.record(name, options);
|
|
52
|
+
return new MockDriver({ fixture });
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get a CreateDriver function that uses a pre-loaded fixture
|
|
56
|
+
*
|
|
57
|
+
* NOTE: This loads the fixture synchronously, so the fixture must exist.
|
|
58
|
+
* For async loading/recording, use driver() instead.
|
|
59
|
+
*/
|
|
60
|
+
createDriverForFixture(fixturePath) {
|
|
61
|
+
const content = readFileSync(this.getFixturePath(fixturePath), "utf-8");
|
|
62
|
+
const fixture = JSON.parse(content);
|
|
63
|
+
return (_config) => {
|
|
64
|
+
return new MockDriver({ fixture });
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Record a fixture
|
|
69
|
+
*/
|
|
70
|
+
async record(name, options) {
|
|
71
|
+
const createDriver = await this.getRealCreateDriver();
|
|
72
|
+
const agentId = `record-${name}`;
|
|
73
|
+
const driverConfig = {
|
|
74
|
+
apiKey: this.config.apiKey,
|
|
75
|
+
baseUrl: this.config.baseUrl,
|
|
76
|
+
agentId,
|
|
77
|
+
model: this.config.model,
|
|
78
|
+
systemPrompt: options.systemPrompt || this.config.systemPrompt || "You are a helpful assistant.",
|
|
79
|
+
cwd: options.cwd || this.config.cwd || process.cwd()
|
|
80
|
+
};
|
|
81
|
+
const realDriver = createDriver(driverConfig);
|
|
82
|
+
const recorder = new RecordingDriver({
|
|
83
|
+
driver: realDriver,
|
|
84
|
+
name,
|
|
85
|
+
description: `Recording of: "${options.message}"`
|
|
86
|
+
});
|
|
87
|
+
await recorder.initialize();
|
|
88
|
+
try {
|
|
89
|
+
const userMessage = {
|
|
90
|
+
id: `msg_${Date.now()}`,
|
|
91
|
+
role: "user",
|
|
92
|
+
subtype: "user",
|
|
93
|
+
content: options.message,
|
|
94
|
+
timestamp: Date.now()
|
|
95
|
+
};
|
|
96
|
+
for await (const event of recorder.receive(userMessage)) {
|
|
97
|
+
logger.debug("Recording event", { type: event.type });
|
|
98
|
+
if (event.type === "message_stop") {
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (event.type === "error") {
|
|
102
|
+
const errorData = event.data;
|
|
103
|
+
throw new Error(`Recording error: ${errorData.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const fixture = recorder.getFixture();
|
|
107
|
+
await this.saveFixture(name, fixture);
|
|
108
|
+
return fixture;
|
|
109
|
+
} finally {
|
|
110
|
+
await recorder.dispose();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Load a fixture by name
|
|
115
|
+
*/
|
|
116
|
+
async load(name) {
|
|
117
|
+
const fixturePath = this.getFixturePath(name);
|
|
118
|
+
return this.loadFixture(fixturePath);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if a fixture exists
|
|
122
|
+
*/
|
|
123
|
+
exists(name) {
|
|
124
|
+
return existsSync(this.getFixturePath(name));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Delete a fixture
|
|
128
|
+
*/
|
|
129
|
+
async delete(name) {
|
|
130
|
+
const fixturePath = this.getFixturePath(name);
|
|
131
|
+
if (existsSync(fixturePath)) {
|
|
132
|
+
const { unlink } = await import("fs/promises");
|
|
133
|
+
await unlink(fixturePath);
|
|
134
|
+
logger.info("Fixture deleted", { name });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// ==================== Private ====================
|
|
138
|
+
getFixturePath(name) {
|
|
139
|
+
if (name.endsWith(".json")) {
|
|
140
|
+
return name;
|
|
141
|
+
}
|
|
142
|
+
return join(this.config.fixturesDir, `${name}.json`);
|
|
143
|
+
}
|
|
144
|
+
async loadFixture(path) {
|
|
145
|
+
const content = await readFile(path, "utf-8");
|
|
146
|
+
return JSON.parse(content);
|
|
147
|
+
}
|
|
148
|
+
async saveFixture(name, fixture) {
|
|
149
|
+
const path = this.getFixturePath(name);
|
|
150
|
+
await mkdir(dirname(path), { recursive: true });
|
|
151
|
+
await writeFile(path, JSON.stringify(fixture, null, 2), "utf-8");
|
|
152
|
+
logger.info("Fixture saved", { name, path, eventCount: fixture.events.length });
|
|
153
|
+
}
|
|
154
|
+
async getRealCreateDriver() {
|
|
155
|
+
if (this.realCreateDriver) {
|
|
156
|
+
return this.realCreateDriver;
|
|
157
|
+
}
|
|
158
|
+
if (this.config.createDriver) {
|
|
159
|
+
this.realCreateDriver = this.config.createDriver;
|
|
160
|
+
return this.realCreateDriver;
|
|
161
|
+
}
|
|
162
|
+
if (!this.config.apiKey) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"apiKey is required for recording. Set it in DevtoolsConfig or provide a createDriver."
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const { createClaudeDriver } = await import("@agentxjs/claude-driver");
|
|
169
|
+
this.realCreateDriver = createClaudeDriver;
|
|
170
|
+
return this.realCreateDriver;
|
|
171
|
+
} catch {
|
|
172
|
+
throw new Error("@agentxjs/claude-driver not found. Install it or provide a createDriver.");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
function createDevtools(config) {
|
|
177
|
+
return new Devtools(config);
|
|
178
|
+
}
|
|
179
|
+
function createVcrCreateDriver(config) {
|
|
180
|
+
const { fixturesDir, getFixtureName, apiKey, baseUrl, model, onPlayback, onRecording, onSaved } = config;
|
|
181
|
+
const realCreateDriver = config.createRealDriver || null;
|
|
182
|
+
return (driverConfig) => {
|
|
183
|
+
const fixtureName = getFixtureName();
|
|
184
|
+
if (!fixtureName) {
|
|
185
|
+
if (!apiKey) {
|
|
186
|
+
throw new Error("No fixture name and no API key. Cannot create driver.");
|
|
187
|
+
}
|
|
188
|
+
const createDriver2 = realCreateDriver || config.createRealDriver;
|
|
189
|
+
if (!createDriver2) {
|
|
190
|
+
throw new Error("No createRealDriver provided and claude-driver not loaded yet.");
|
|
191
|
+
}
|
|
192
|
+
return createDriver2({
|
|
193
|
+
...driverConfig,
|
|
194
|
+
apiKey,
|
|
195
|
+
baseUrl,
|
|
196
|
+
model
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
const fixturePath = join(fixturesDir, `${fixtureName}.json`);
|
|
200
|
+
if (existsSync(fixturePath)) {
|
|
201
|
+
onPlayback?.(fixtureName);
|
|
202
|
+
logger.info("VCR Playback", { fixtureName });
|
|
203
|
+
const fixture = JSON.parse(readFileSync(fixturePath, "utf-8"));
|
|
204
|
+
return new MockDriver({ fixture });
|
|
205
|
+
}
|
|
206
|
+
if (!apiKey) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`No fixture found for "${fixtureName}" and no API key for recording. Either create the fixture or provide an API key.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
onRecording?.(fixtureName);
|
|
212
|
+
logger.info("VCR Recording", { fixtureName });
|
|
213
|
+
const createDriver = realCreateDriver || config.createRealDriver;
|
|
214
|
+
if (!createDriver) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"createRealDriver not available. For async loading, ensure claude-driver is pre-loaded."
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const realDriver = createDriver({
|
|
220
|
+
...driverConfig,
|
|
221
|
+
apiKey,
|
|
222
|
+
baseUrl,
|
|
223
|
+
model
|
|
224
|
+
});
|
|
225
|
+
const recorder = new RecordingDriver({
|
|
226
|
+
driver: realDriver,
|
|
227
|
+
name: fixtureName,
|
|
228
|
+
description: `VCR recording: ${fixtureName}`
|
|
229
|
+
});
|
|
230
|
+
let fixtureSaved = false;
|
|
231
|
+
const originalDispose = recorder.dispose.bind(recorder);
|
|
232
|
+
recorder.dispose = async () => {
|
|
233
|
+
if (!fixtureSaved && recorder.eventCount > 0) {
|
|
234
|
+
try {
|
|
235
|
+
const fixture = recorder.getFixture();
|
|
236
|
+
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
237
|
+
await mkdir2(dirname(fixturePath), { recursive: true });
|
|
238
|
+
await writeFile2(fixturePath, JSON.stringify(fixture, null, 2), "utf-8");
|
|
239
|
+
fixtureSaved = true;
|
|
240
|
+
onSaved?.(fixtureName, recorder.eventCount);
|
|
241
|
+
logger.info("VCR Saved", { fixtureName, eventCount: recorder.eventCount });
|
|
242
|
+
} catch (e) {
|
|
243
|
+
logger.error("VCR Save failed", { fixtureName, error: e });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return originalDispose();
|
|
247
|
+
};
|
|
248
|
+
return recorder;
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export {
|
|
252
|
+
BUILTIN_FIXTURES,
|
|
253
|
+
Devtools,
|
|
254
|
+
EMPTY_RESPONSE,
|
|
255
|
+
ERROR_RESPONSE,
|
|
256
|
+
LONG_REPLY,
|
|
257
|
+
MockDriver,
|
|
258
|
+
RecordingDriver,
|
|
259
|
+
SIMPLE_REPLY,
|
|
260
|
+
TOOL_CALL,
|
|
261
|
+
createDevtools,
|
|
262
|
+
createMockDriver,
|
|
263
|
+
createRecordingDriver,
|
|
264
|
+
createVcrCreateDriver,
|
|
265
|
+
env,
|
|
266
|
+
getFixture,
|
|
267
|
+
listFixtures
|
|
268
|
+
};
|
|
269
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Devtools.ts"],"sourcesContent":["/**\n * Devtools SDK - VCR-style fixture management\n *\n * Automatically uses existing fixtures or records new ones on-the-fly.\n *\n * Usage:\n * ```typescript\n * import { createDevtools } from \"@agentxjs/devtools\";\n *\n * import { env } from \"@agentxjs/devtools\";\n *\n * const devtools = createDevtools({\n * fixturesDir: \"./fixtures\",\n * apiKey: env.apiKey,\n * });\n *\n * // Has fixture → playback (MockDriver)\n * // No fixture → call API, record, save, return MockDriver\n * const driver = await devtools.driver(\"hello-test\", {\n * message: \"Hello!\",\n * });\n *\n * await driver.initialize();\n * for await (const event of driver.receive({ content: \"Hello\" })) {\n * console.log(event);\n * }\n * ```\n */\n\nimport type { Driver, CreateDriver, DriverConfig } from \"@agentxjs/core/driver\";\nimport type { Fixture } from \"./types\";\nimport { MockDriver } from \"./mock/MockDriver\";\nimport { RecordingDriver } from \"./recorder/RecordingDriver\";\nimport { createLogger } from \"commonxjs/logger\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\n\nconst logger = createLogger(\"devtools/Devtools\");\n\n/**\n * Devtools configuration\n */\nexport interface DevtoolsConfig {\n /**\n * Directory to store/load fixtures\n */\n fixturesDir: string;\n\n /**\n * API key for recording (required if recording)\n */\n apiKey?: string;\n\n /**\n * API base URL\n */\n baseUrl?: string;\n\n /**\n * Default model\n */\n model?: string;\n\n /**\n * Default system prompt\n */\n systemPrompt?: string;\n\n /**\n * Working directory for tool execution\n */\n cwd?: string;\n\n /**\n * Real driver factory for recording\n * If not provided, will try to use @agentxjs/claude-driver\n */\n createDriver?: CreateDriver;\n}\n\n/**\n * Options for getting a driver\n */\nexport interface DriverOptions {\n /**\n * Message to send if recording is needed\n */\n message: string;\n\n /**\n * Override system prompt\n */\n systemPrompt?: string;\n\n /**\n * Override working directory\n */\n cwd?: string;\n\n /**\n * Force re-record even if fixture exists\n */\n forceRecord?: boolean;\n}\n\n/**\n * Devtools SDK\n */\nexport class Devtools {\n private config: DevtoolsConfig;\n private realCreateDriver: CreateDriver | null = null;\n\n constructor(config: DevtoolsConfig) {\n this.config = config;\n logger.info(\"Devtools initialized\", { fixturesDir: config.fixturesDir });\n }\n\n /**\n * Get a driver for the given fixture name\n *\n * - If fixture exists → returns MockDriver with playback\n * - If fixture doesn't exist → records, saves, returns MockDriver\n */\n async driver(name: string, options: DriverOptions): Promise<Driver> {\n const fixturePath = this.getFixturePath(name);\n\n // Check if fixture exists\n if (!options.forceRecord && existsSync(fixturePath)) {\n logger.info(\"Loading existing fixture\", { name, path: fixturePath });\n const fixture = await this.loadFixture(fixturePath);\n return new MockDriver({ fixture });\n }\n\n // Need to record\n logger.info(\"Recording new fixture\", { name, message: options.message });\n const fixture = await this.record(name, options);\n return new MockDriver({ fixture });\n }\n\n /**\n * Get a CreateDriver function that uses a pre-loaded fixture\n *\n * NOTE: This loads the fixture synchronously, so the fixture must exist.\n * For async loading/recording, use driver() instead.\n */\n createDriverForFixture(fixturePath: string): CreateDriver {\n // Load fixture synchronously (requires existing fixture)\n const content = readFileSync(this.getFixturePath(fixturePath), \"utf-8\");\n const fixture = JSON.parse(content) as Fixture;\n\n return (_config: DriverConfig) => {\n return new MockDriver({ fixture });\n };\n }\n\n /**\n * Record a fixture\n */\n async record(name: string, options: DriverOptions): Promise<Fixture> {\n const createDriver = await this.getRealCreateDriver();\n\n const agentId = `record-${name}`;\n\n // Create driver config\n const driverConfig: DriverConfig = {\n apiKey: this.config.apiKey!,\n baseUrl: this.config.baseUrl,\n agentId,\n model: this.config.model,\n systemPrompt:\n options.systemPrompt || this.config.systemPrompt || \"You are a helpful assistant.\",\n cwd: options.cwd || this.config.cwd || process.cwd(),\n };\n\n // Create real driver\n const realDriver = createDriver(driverConfig);\n\n // Wrap with recorder\n const recorder = new RecordingDriver({\n driver: realDriver,\n name,\n description: `Recording of: \"${options.message}\"`,\n });\n\n // Initialize\n await recorder.initialize();\n\n try {\n // Build user message\n const userMessage = {\n id: `msg_${Date.now()}`,\n role: \"user\" as const,\n subtype: \"user\" as const,\n content: options.message,\n timestamp: Date.now(),\n };\n\n // Send message and collect all events\n for await (const event of recorder.receive(userMessage)) {\n logger.debug(\"Recording event\", { type: event.type });\n\n // Check for completion\n if (event.type === \"message_stop\") {\n break;\n }\n\n // Check for error\n if (event.type === \"error\") {\n const errorData = event.data as { message?: string };\n throw new Error(`Recording error: ${errorData.message}`);\n }\n }\n\n // Get fixture\n const fixture = recorder.getFixture();\n\n // Save fixture\n await this.saveFixture(name, fixture);\n\n return fixture;\n } finally {\n // Cleanup\n await recorder.dispose();\n }\n }\n\n /**\n * Load a fixture by name\n */\n async load(name: string): Promise<Fixture> {\n const fixturePath = this.getFixturePath(name);\n return this.loadFixture(fixturePath);\n }\n\n /**\n * Check if a fixture exists\n */\n exists(name: string): boolean {\n return existsSync(this.getFixturePath(name));\n }\n\n /**\n * Delete a fixture\n */\n async delete(name: string): Promise<void> {\n const fixturePath = this.getFixturePath(name);\n if (existsSync(fixturePath)) {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(fixturePath);\n logger.info(\"Fixture deleted\", { name });\n }\n }\n\n // ==================== Private ====================\n\n private getFixturePath(name: string): string {\n // If name is already a path, use it directly\n if (name.endsWith(\".json\")) {\n return name;\n }\n return join(this.config.fixturesDir, `${name}.json`);\n }\n\n private async loadFixture(path: string): Promise<Fixture> {\n const content = await readFile(path, \"utf-8\");\n return JSON.parse(content) as Fixture;\n }\n\n private async saveFixture(name: string, fixture: Fixture): Promise<void> {\n const path = this.getFixturePath(name);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(fixture, null, 2), \"utf-8\");\n logger.info(\"Fixture saved\", { name, path, eventCount: fixture.events.length });\n }\n\n private async getRealCreateDriver(): Promise<CreateDriver> {\n if (this.realCreateDriver) {\n return this.realCreateDriver;\n }\n\n if (this.config.createDriver) {\n this.realCreateDriver = this.config.createDriver;\n return this.realCreateDriver;\n }\n\n // Validate API key\n if (!this.config.apiKey) {\n throw new Error(\n \"apiKey is required for recording. Set it in DevtoolsConfig or provide a createDriver.\"\n );\n }\n\n // Try to import claude-driver\n try {\n const { createClaudeDriver } = await import(\"@agentxjs/claude-driver\");\n this.realCreateDriver = createClaudeDriver;\n return this.realCreateDriver;\n } catch {\n throw new Error(\"@agentxjs/claude-driver not found. Install it or provide a createDriver.\");\n }\n }\n}\n\n/**\n * Create a Devtools instance\n */\nexport function createDevtools(config: DevtoolsConfig): Devtools {\n return new Devtools(config);\n}\n\n// ============================================================================\n// VCR CreateDriver Factory\n// ============================================================================\n\n/**\n * Configuration for VCR-aware CreateDriver\n */\nexport interface VcrCreateDriverConfig {\n /**\n * Directory to store/load fixtures\n */\n fixturesDir: string;\n\n /**\n * Get current fixture name. Return null to skip VCR (use real driver).\n * Called when driver is created.\n */\n getFixtureName: () => string | null;\n\n /**\n * API key for recording\n */\n apiKey?: string;\n\n /**\n * API base URL\n */\n baseUrl?: string;\n\n /**\n * Default model\n */\n model?: string;\n\n /**\n * Real driver factory (optional, defaults to @agentxjs/claude-driver)\n */\n createRealDriver?: CreateDriver;\n\n /**\n * Called when playback mode is used\n */\n onPlayback?: (fixtureName: string) => void;\n\n /**\n * Called when recording mode is used\n */\n onRecording?: (fixtureName: string) => void;\n\n /**\n * Called when fixture is saved\n */\n onSaved?: (fixtureName: string, eventCount: number) => void;\n}\n\n/**\n * Create a VCR-aware CreateDriver function\n *\n * VCR logic (hardcoded):\n * - Fixture exists → Playback (MockDriver)\n * - Fixture missing → Recording (RecordingDriver) → Auto-save on dispose\n *\n * @example\n * ```typescript\n * let currentFixture: string | null = null;\n *\n * const vcrCreateDriver = createVcrCreateDriver({\n * fixturesDir: \"./fixtures\",\n * getFixtureName: () => currentFixture,\n * apiKey: process.env.API_KEY,\n * });\n *\n * // Before each test:\n * currentFixture = \"test-scenario-name\";\n *\n * // Use with server:\n * const platform = await createNodePlatform({...});\n * const server = await createServer({\n * platform,\n * createDriver: vcrCreateDriver,\n * });\n * ```\n */\nexport function createVcrCreateDriver(config: VcrCreateDriverConfig): CreateDriver {\n const { fixturesDir, getFixtureName, apiKey, baseUrl, model, onPlayback, onRecording, onSaved } =\n config;\n\n // Real driver factory (must be provided or pre-loaded)\n const realCreateDriver: CreateDriver | null = config.createRealDriver || null;\n\n return (driverConfig: DriverConfig): Driver => {\n const fixtureName = getFixtureName();\n\n // No fixture name → use real driver without VCR\n if (!fixtureName) {\n if (!apiKey) {\n throw new Error(\"No fixture name and no API key. Cannot create driver.\");\n }\n\n // Sync: we need to return immediately, so we create the driver with merged config\n // Note: This path is for non-VCR scenarios\n const createDriver = realCreateDriver || config.createRealDriver;\n if (!createDriver) {\n throw new Error(\"No createRealDriver provided and claude-driver not loaded yet.\");\n }\n\n return createDriver({\n ...driverConfig,\n apiKey,\n baseUrl,\n model,\n });\n }\n\n const fixturePath = join(fixturesDir, `${fixtureName}.json`);\n\n // Fixture exists → Playback (MockDriver)\n if (existsSync(fixturePath)) {\n onPlayback?.(fixtureName);\n logger.info(\"VCR Playback\", { fixtureName });\n\n const fixture = JSON.parse(readFileSync(fixturePath, \"utf-8\")) as Fixture;\n return new MockDriver({ fixture });\n }\n\n // No fixture → Recording (RecordingDriver)\n if (!apiKey) {\n throw new Error(\n `No fixture found for \"${fixtureName}\" and no API key for recording. ` +\n `Either create the fixture or provide an API key.`\n );\n }\n\n onRecording?.(fixtureName);\n logger.info(\"VCR Recording\", { fixtureName });\n\n // Get real driver factory (sync - must be pre-loaded or provided)\n const createDriver = realCreateDriver || config.createRealDriver;\n if (!createDriver) {\n throw new Error(\n \"createRealDriver not available. For async loading, ensure claude-driver is pre-loaded.\"\n );\n }\n\n // Create real driver with merged config\n const realDriver = createDriver({\n ...driverConfig,\n apiKey,\n baseUrl,\n model,\n });\n\n // Wrap with RecordingDriver\n const recorder = new RecordingDriver({\n driver: realDriver,\n name: fixtureName,\n description: `VCR recording: ${fixtureName}`,\n });\n\n // Auto-save fixture on dispose\n let fixtureSaved = false;\n const originalDispose = recorder.dispose.bind(recorder);\n\n recorder.dispose = async () => {\n if (!fixtureSaved && recorder.eventCount > 0) {\n try {\n const fixture = recorder.getFixture();\n\n // Ensure directory exists\n const { mkdir, writeFile } = await import(\"node:fs/promises\");\n await mkdir(dirname(fixturePath), { recursive: true });\n await writeFile(fixturePath, JSON.stringify(fixture, null, 2), \"utf-8\");\n\n fixtureSaved = true;\n onSaved?.(fixtureName, recorder.eventCount);\n logger.info(\"VCR Saved\", { fixtureName, eventCount: recorder.eventCount });\n } catch (e) {\n logger.error(\"VCR Save failed\", { fixtureName, error: e });\n }\n }\n\n return originalDispose();\n };\n\n return recorder;\n };\n}\n"],"mappings":"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","names":["fixture","createDriver","mkdir","writeFile"]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Driver, DriverConfig, DriverState, DriverStreamEvent } from '@agentxjs/core/driver';
|
|
2
|
+
import { UserMessage } from '@agentxjs/core/agent';
|
|
3
|
+
import { M as MockDriverOptions, F as Fixture } from '../types-C6Lf3vz2.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* MockDriver - Mock Driver for Testing
|
|
7
|
+
*
|
|
8
|
+
* Plays back recorded fixtures when receiving messages.
|
|
9
|
+
* Implements the new Driver interface with receive() returning AsyncIterable.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const driver = new MockDriver({
|
|
14
|
+
* fixture: "simple-reply",
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* await driver.initialize();
|
|
18
|
+
*
|
|
19
|
+
* for await (const event of driver.receive({ content: "Hello" })) {
|
|
20
|
+
* if (event.type === "text_delta") {
|
|
21
|
+
* console.log(event.data.text);
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* await driver.dispose();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* MockDriver - Playback driver for testing
|
|
31
|
+
*
|
|
32
|
+
* Implements the new Driver interface:
|
|
33
|
+
* - receive() returns AsyncIterable<DriverStreamEvent>
|
|
34
|
+
* - Clear input/output boundaries for testing
|
|
35
|
+
*/
|
|
36
|
+
declare class MockDriver implements Driver {
|
|
37
|
+
readonly name = "MockDriver";
|
|
38
|
+
private _sessionId;
|
|
39
|
+
private _state;
|
|
40
|
+
private readonly config;
|
|
41
|
+
private readonly options;
|
|
42
|
+
private readonly fixtures;
|
|
43
|
+
private currentFixture;
|
|
44
|
+
private isInterrupted;
|
|
45
|
+
private eventCursor;
|
|
46
|
+
/**
|
|
47
|
+
* Create a MockDriver
|
|
48
|
+
*
|
|
49
|
+
* @param options - MockDriverOptions or DriverConfig
|
|
50
|
+
* @param mockOptions - MockDriverOptions if first param is DriverConfig
|
|
51
|
+
*/
|
|
52
|
+
constructor(optionsOrConfig: MockDriverOptions | DriverConfig, mockOptions?: MockDriverOptions);
|
|
53
|
+
get sessionId(): string | null;
|
|
54
|
+
get state(): DriverState;
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the Driver
|
|
57
|
+
*/
|
|
58
|
+
initialize(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Dispose and cleanup resources
|
|
61
|
+
*/
|
|
62
|
+
dispose(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Receive a user message and return stream of events
|
|
65
|
+
*
|
|
66
|
+
* Plays back the current fixture as DriverStreamEvent.
|
|
67
|
+
*
|
|
68
|
+
* @param message - User message (ignored for playback)
|
|
69
|
+
* @returns AsyncIterable of stream events
|
|
70
|
+
*/
|
|
71
|
+
receive(_message: UserMessage): AsyncIterable<DriverStreamEvent>;
|
|
72
|
+
/**
|
|
73
|
+
* Interrupt current operation
|
|
74
|
+
*/
|
|
75
|
+
interrupt(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Set the fixture to use for next playback
|
|
78
|
+
*/
|
|
79
|
+
setFixture(fixture: string | Fixture): void;
|
|
80
|
+
/**
|
|
81
|
+
* Add a custom fixture
|
|
82
|
+
*/
|
|
83
|
+
addFixture(fixture: Fixture): void;
|
|
84
|
+
/**
|
|
85
|
+
* Get the current fixture
|
|
86
|
+
*/
|
|
87
|
+
getFixture(): Fixture;
|
|
88
|
+
/**
|
|
89
|
+
* Get available fixture names
|
|
90
|
+
*/
|
|
91
|
+
getFixtureNames(): string[];
|
|
92
|
+
/**
|
|
93
|
+
* Resolve fixture from name or Fixture object
|
|
94
|
+
*/
|
|
95
|
+
private resolveFixture;
|
|
96
|
+
/**
|
|
97
|
+
* Convert FixtureEvent to DriverStreamEvent
|
|
98
|
+
*/
|
|
99
|
+
private convertFixtureEvent;
|
|
100
|
+
/**
|
|
101
|
+
* Sleep for specified milliseconds
|
|
102
|
+
*/
|
|
103
|
+
private sleep;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a MockDriver factory function
|
|
107
|
+
*
|
|
108
|
+
* Returns a CreateDriver-compatible function.
|
|
109
|
+
*
|
|
110
|
+
* @param mockOptions - Options for all created drivers
|
|
111
|
+
* @returns CreateDriver function
|
|
112
|
+
*/
|
|
113
|
+
declare function createMockDriver(mockOptions?: MockDriverOptions): (config: DriverConfig) => Driver;
|
|
114
|
+
|
|
115
|
+
export { MockDriver, createMockDriver };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|