@ai-sdk/devtools 1.0.24 → 1.0.26

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.
@@ -1,335 +1,302 @@
1
- // src/viewer/server.ts
2
1
  import { serve } from "@hono/node-server";
3
2
  import { serveStatic } from "@hono/node-server/serve-static";
4
3
  import { Hono } from "hono";
5
4
  import { streamSSE } from "hono/streaming";
6
- import path2 from "path";
7
- import fs2 from "fs";
8
- import { fileURLToPath } from "url";
9
-
10
- // src/db.ts
11
5
  import path from "path";
12
6
  import fs from "fs";
13
- var DEVTOOLS_DB_DIR = ".devtools";
14
- var DEVTOOLS_DB_FILE = "generations.json";
15
- var MAX_DB_BYTES = 100 * 1024 * 1024;
16
- var DB_DIR = path.join(process.cwd(), DEVTOOLS_DB_DIR);
17
- var DB_PATH = path.join(DB_DIR, DEVTOOLS_DB_FILE);
18
- var DEVTOOLS_PORT = process.env.AI_SDK_DEVTOOLS_PORT ? parseInt(process.env.AI_SDK_DEVTOOLS_PORT) : 4983;
19
- var notifyServer = (event) => {
20
- notifyServerAsync(event);
7
+ import { fileURLToPath } from "url";
8
+ import path$1 from "node:path";
9
+ import fs$1 from "node:fs";
10
+ //#region src/db.ts
11
+ const DEVTOOLS_DB_DIR = ".devtools";
12
+ const DEVTOOLS_DB_FILE = "generations.json";
13
+ const MAX_DB_BYTES = 104857600;
14
+ const DB_DIR = path$1.join(process.cwd(), DEVTOOLS_DB_DIR);
15
+ const DB_PATH = path$1.join(DB_DIR, DEVTOOLS_DB_FILE);
16
+ const DEVTOOLS_PORT = process.env.AI_SDK_DEVTOOLS_PORT ? parseInt(process.env.AI_SDK_DEVTOOLS_PORT) : 4983;
17
+ /**
18
+ * Notify the devtools server that data has changed.
19
+ * Fire-and-forget: doesn't block, ignores errors if server isn't running.
20
+ */
21
+ const notifyServer = (event) => {
22
+ notifyServerAsync(event);
21
23
  };
22
- var notifyServerAsync = async (event) => {
23
- try {
24
- await fetch(`http://localhost:${DEVTOOLS_PORT}/api/notify`, {
25
- method: "POST",
26
- headers: { "Content-Type": "application/json" },
27
- body: JSON.stringify({ event, timestamp: Date.now(), dbPath: DB_PATH })
28
- });
29
- } catch {
30
- }
24
+ /**
25
+ * Notify the devtools server and wait for the request to complete.
26
+ * Used during process cleanup to ensure notifications are sent before exit.
27
+ */
28
+ const notifyServerAsync = async (event) => {
29
+ try {
30
+ await fetch(`http://localhost:${DEVTOOLS_PORT}/api/notify`, {
31
+ method: "POST",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify({
34
+ event,
35
+ timestamp: Date.now(),
36
+ dbPath: DB_PATH
37
+ })
38
+ });
39
+ } catch {}
31
40
  };
32
- var ensureGitignore = () => {
33
- const gitignorePath = path.join(process.cwd(), ".gitignore");
34
- if (!fs.existsSync(gitignorePath)) {
35
- return;
36
- }
37
- const content = fs.readFileSync(gitignorePath, "utf-8");
38
- const lines = content.split("\n");
39
- const alreadyIgnored = lines.some(
40
- (line) => line.trim() === ".devtools" || line.trim() === ".devtools/"
41
- );
42
- if (!alreadyIgnored) {
43
- const newContent = content.endsWith("\n") ? `${content}.devtools
44
- ` : `${content}
45
- .devtools
46
- `;
47
- fs.writeFileSync(gitignorePath, newContent);
48
- }
41
+ /**
42
+ * Ensure .devtools is in .gitignore.
43
+ * Only writes if .gitignore exists and doesn't already contain .devtools.
44
+ */
45
+ const ensureGitignore = () => {
46
+ const gitignorePath = path$1.join(process.cwd(), ".gitignore");
47
+ if (!fs$1.existsSync(gitignorePath)) return;
48
+ const content = fs$1.readFileSync(gitignorePath, "utf-8");
49
+ if (!content.split("\n").some((line) => line.trim() === ".devtools" || line.trim() === ".devtools/")) {
50
+ const newContent = content.endsWith("\n") ? `${content}.devtools\n` : `${content}\n.devtools\n`;
51
+ fs$1.writeFileSync(gitignorePath, newContent);
52
+ }
49
53
  };
50
- var readDb = () => {
51
- try {
52
- if (fs.existsSync(DB_PATH)) {
53
- const content = fs.readFileSync(DB_PATH, "utf-8");
54
- return JSON.parse(content);
55
- }
56
- } catch {
57
- }
58
- return { runs: [], steps: [] };
54
+ const readDb = () => {
55
+ try {
56
+ if (fs$1.existsSync(DB_PATH)) {
57
+ const content = fs$1.readFileSync(DB_PATH, "utf-8");
58
+ return JSON.parse(content);
59
+ }
60
+ } catch {}
61
+ return {
62
+ runs: [],
63
+ steps: []
64
+ };
59
65
  };
60
- var writeDb = (db) => {
61
- const isFirstRun = !fs.existsSync(DB_DIR);
62
- if (isFirstRun) {
63
- fs.mkdirSync(DB_DIR, { recursive: true });
64
- ensureGitignore();
65
- }
66
- fs.writeFileSync(DB_PATH, JSON.stringify(db, null, 2));
66
+ const writeDb = (db) => {
67
+ if (!fs$1.existsSync(DB_DIR)) {
68
+ fs$1.mkdirSync(DB_DIR, { recursive: true });
69
+ ensureGitignore();
70
+ }
71
+ fs$1.writeFileSync(DB_PATH, JSON.stringify(db, null, 2));
67
72
  };
68
- var dbCache = null;
69
- var getDb = () => {
70
- if (!dbCache) {
71
- dbCache = readDb();
72
- }
73
- return dbCache;
73
+ let dbCache = null;
74
+ const getDb = () => {
75
+ if (!dbCache) dbCache = readDb();
76
+ return dbCache;
74
77
  };
75
- var saveDb = (db) => {
76
- dbCache = db;
77
- writeDb(db);
78
+ const saveDb = (db) => {
79
+ dbCache = db;
80
+ writeDb(db);
78
81
  };
79
- var normalizeDevtoolsDbPath = (dbPath) => {
80
- const resolvedPath = path.resolve(dbPath);
81
- const dbDir = path.dirname(resolvedPath);
82
- if (path.basename(resolvedPath) !== DEVTOOLS_DB_FILE || path.basename(dbDir) !== DEVTOOLS_DB_DIR) {
83
- return void 0;
84
- }
85
- return resolvedPath;
82
+ const normalizeDevtoolsDbPath = (dbPath) => {
83
+ const resolvedPath = path$1.resolve(dbPath);
84
+ const dbDir = path$1.dirname(resolvedPath);
85
+ if (path$1.basename(resolvedPath) !== DEVTOOLS_DB_FILE || path$1.basename(dbDir) !== DEVTOOLS_DB_DIR) return;
86
+ return resolvedPath;
86
87
  };
87
- var validateRemoteDbPath = (dbPath) => {
88
- if (typeof dbPath !== "string") {
89
- return void 0;
90
- }
91
- const normalizedPath = normalizeDevtoolsDbPath(dbPath);
92
- if (!normalizedPath) {
93
- return void 0;
94
- }
95
- try {
96
- const stats = fs.statSync(normalizedPath);
97
- if (!stats.isFile() || stats.size > MAX_DB_BYTES) {
98
- return void 0;
99
- }
100
- const realPath = fs.realpathSync(normalizedPath);
101
- return normalizeDevtoolsDbPath(realPath);
102
- } catch {
103
- return void 0;
104
- }
88
+ const validateRemoteDbPath = (dbPath) => {
89
+ if (typeof dbPath !== "string") return;
90
+ const normalizedPath = normalizeDevtoolsDbPath(dbPath);
91
+ if (!normalizedPath) return;
92
+ try {
93
+ const stats = fs$1.statSync(normalizedPath);
94
+ if (!stats.isFile() || stats.size > MAX_DB_BYTES) return;
95
+ const realPath = fs$1.realpathSync(normalizedPath);
96
+ return normalizeDevtoolsDbPath(realPath);
97
+ } catch {
98
+ return;
99
+ }
105
100
  };
106
- var reloadDb = async (remoteDbPath2) => {
107
- const validatedRemoteDbPath = validateRemoteDbPath(remoteDbPath2);
108
- if (validatedRemoteDbPath) {
109
- try {
110
- const content = fs.readFileSync(validatedRemoteDbPath, "utf-8");
111
- dbCache = JSON.parse(content);
112
- return;
113
- } catch {
114
- }
115
- }
116
- dbCache = readDb();
101
+ /**
102
+ * Reload the database from disk.
103
+ * Used by the viewer server to pick up changes made by the middleware/integration.
104
+ * When a valid remote dbPath is provided (from a notify POST), reads from that
105
+ * path instead of the local CWD-based path, so the viewer works regardless of
106
+ * where it was started.
107
+ */
108
+ const reloadDb = async (remoteDbPath) => {
109
+ const validatedRemoteDbPath = validateRemoteDbPath(remoteDbPath);
110
+ if (validatedRemoteDbPath) try {
111
+ const content = fs$1.readFileSync(validatedRemoteDbPath, "utf-8");
112
+ dbCache = JSON.parse(content);
113
+ return;
114
+ } catch {}
115
+ dbCache = readDb();
117
116
  };
118
- var getRuns = async () => {
119
- const db = getDb();
120
- return [...db.runs].sort(
121
- (a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime()
122
- );
117
+ const getRuns = async () => {
118
+ return [...getDb().runs].sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime());
123
119
  };
124
- var getStepsForRun = async (runId) => {
125
- const db = getDb();
126
- return db.steps.filter((s) => s.run_id === runId).sort((a, b) => a.step_number - b.step_number);
120
+ const getStepsForRun = async (runId) => {
121
+ return getDb().steps.filter((s) => s.run_id === runId).sort((a, b) => a.step_number - b.step_number);
127
122
  };
128
- var getRunWithSteps = async (runId) => {
129
- const db = getDb();
130
- const run = db.runs.find((r) => r.id === runId);
131
- if (!run) return null;
132
- const steps = await getStepsForRun(runId);
133
- return { run, steps };
123
+ const getRunWithSteps = async (runId) => {
124
+ const run = getDb().runs.find((r) => r.id === runId);
125
+ if (!run) return null;
126
+ return {
127
+ run,
128
+ steps: await getStepsForRun(runId)
129
+ };
134
130
  };
135
- var clearDatabase = async () => {
136
- const db = { runs: [], steps: [] };
137
- saveDb(db);
138
- notifyServer("clear");
131
+ const clearDatabase = async () => {
132
+ saveDb({
133
+ runs: [],
134
+ steps: []
135
+ });
136
+ notifyServer("clear");
139
137
  };
140
-
141
- // src/viewer/server.ts
142
- var sseClients = /* @__PURE__ */ new Set();
143
- var broadcastToClients = (event, data) => {
144
- const message = `event: ${event}
145
- data: ${JSON.stringify(data)}
146
-
147
- `;
148
- for (const client of sseClients) {
149
- try {
150
- client.controller.enqueue(message);
151
- } catch {
152
- sseClients.delete(client);
153
- }
154
- }
138
+ //#endregion
139
+ //#region src/viewer/server.ts
140
+ const sseClients = /* @__PURE__ */ new Set();
141
+ const broadcastToClients = (event, data) => {
142
+ const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
143
+ for (const client of sseClients) try {
144
+ client.controller.enqueue(message);
145
+ } catch {
146
+ sseClients.delete(client);
147
+ }
155
148
  };
156
- var __dirname = path2.dirname(fileURLToPath(import.meta.url));
157
- var devEnv = process.env.AI_SDK_DEVTOOLS_DEV;
158
- var isDevMode = devEnv !== void 0 && devEnv !== "false" && devEnv !== "0";
159
- var projectRoot = path2.resolve(__dirname, "../..");
160
- var clientDir = path2.join(projectRoot, "dist/client");
161
- var remoteDbPath;
162
- var viewerPort = 4983;
163
- var app = new Hono();
164
- var getAllowedHosts = () => /* @__PURE__ */ new Set([
165
- `localhost:${viewerPort}`,
166
- `127.0.0.1:${viewerPort}`,
167
- `[::1]:${viewerPort}`
149
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
150
+ const devEnv = process.env.AI_SDK_DEVTOOLS_DEV;
151
+ const isDevMode = devEnv !== void 0 && devEnv !== "false" && devEnv !== "0";
152
+ const projectRoot = path.resolve(__dirname, "../..");
153
+ const clientDir = path.join(projectRoot, "dist/client");
154
+ let remoteDbPath;
155
+ let viewerPort = 4983;
156
+ const app = new Hono();
157
+ const getAllowedHosts = () => /* @__PURE__ */ new Set([
158
+ `localhost:${viewerPort}`,
159
+ `127.0.0.1:${viewerPort}`,
160
+ `[::1]:${viewerPort}`
168
161
  ]);
169
- var getAllowedOrigins = () => /* @__PURE__ */ new Set([
170
- `http://localhost:${viewerPort}`,
171
- `http://127.0.0.1:${viewerPort}`,
172
- `http://[::1]:${viewerPort}`,
173
- "http://localhost:5173",
174
- "http://127.0.0.1:5173",
175
- "http://[::1]:5173"
162
+ const getAllowedOrigins = () => /* @__PURE__ */ new Set([
163
+ `http://localhost:${viewerPort}`,
164
+ `http://127.0.0.1:${viewerPort}`,
165
+ `http://[::1]:${viewerPort}`,
166
+ "http://localhost:5173",
167
+ "http://127.0.0.1:5173",
168
+ "http://[::1]:5173"
176
169
  ]);
177
170
  app.use("/api/*", async (c, next) => {
178
- const host = c.req.header("host");
179
- if (!host || !getAllowedHosts().has(host)) {
180
- return c.text("Forbidden", 403);
181
- }
182
- const origin = c.req.header("origin");
183
- if (origin && !getAllowedOrigins().has(origin)) {
184
- return c.text("Forbidden", 403);
185
- }
186
- await next();
171
+ const host = c.req.header("host");
172
+ if (!host || !getAllowedHosts().has(host)) return c.text("Forbidden", 403);
173
+ const origin = c.req.header("origin");
174
+ if (origin && !getAllowedOrigins().has(origin)) return c.text("Forbidden", 403);
175
+ await next();
187
176
  });
188
177
  app.get("/api/runs", async (c) => {
189
- await reloadDb(remoteDbPath);
190
- const runs = await getRuns();
191
- const rootRuns = runs.filter((r) => !r.parent_run_id);
192
- const runsWithMeta = await Promise.all(
193
- rootRuns.map(async (run) => {
194
- const steps = await getStepsForRun(run.id);
195
- let firstMessage = "No user message";
196
- let hasError = false;
197
- let isInProgress = false;
198
- const firstStep = steps[0];
199
- if (firstStep) {
200
- try {
201
- const input = JSON.parse(firstStep.input);
202
- const userMsg = input?.prompt?.findLast(
203
- (m) => m.role === "user"
204
- );
205
- if (userMsg) {
206
- const content = typeof userMsg.content === "string" ? userMsg.content : userMsg.content?.[0]?.text || "";
207
- firstMessage = content.slice(0, 60) + (content.length > 60 ? "..." : "");
208
- }
209
- } catch {
210
- }
211
- hasError = steps.some((s) => s.error);
212
- isInProgress = steps.some((s) => s.duration_ms === null && !s.error);
213
- }
214
- return {
215
- ...run,
216
- stepCount: steps.length,
217
- firstMessage,
218
- hasError,
219
- isInProgress,
220
- type: firstStep?.type
221
- };
222
- })
223
- );
224
- return c.json(runsWithMeta);
178
+ await reloadDb(remoteDbPath);
179
+ const rootRuns = (await getRuns()).filter((r) => !r.parent_run_id);
180
+ const runsWithMeta = await Promise.all(rootRuns.map(async (run) => {
181
+ const steps = await getStepsForRun(run.id);
182
+ let firstMessage = "No user message";
183
+ let hasError = false;
184
+ let isInProgress = false;
185
+ const firstStep = steps[0];
186
+ if (firstStep) {
187
+ try {
188
+ const userMsg = JSON.parse(firstStep.input)?.prompt?.findLast((m) => m.role === "user");
189
+ if (userMsg) {
190
+ const content = typeof userMsg.content === "string" ? userMsg.content : userMsg.content?.[0]?.text || "";
191
+ firstMessage = content.slice(0, 60) + (content.length > 60 ? "..." : "");
192
+ }
193
+ } catch {}
194
+ hasError = steps.some((s) => s.error);
195
+ isInProgress = steps.some((s) => s.duration_ms === null && !s.error);
196
+ }
197
+ return {
198
+ ...run,
199
+ stepCount: steps.length,
200
+ firstMessage,
201
+ hasError,
202
+ isInProgress,
203
+ type: firstStep?.type
204
+ };
205
+ }));
206
+ return c.json(runsWithMeta);
225
207
  });
226
208
  app.get("/api/runs/:id", async (c) => {
227
- await reloadDb(remoteDbPath);
228
- const data = await getRunWithSteps(c.req.param("id"));
229
- if (!data) {
230
- return c.json({ error: "Run not found" }, 404);
231
- }
232
- const isInProgress = data.steps.some((s) => s.duration_ms === null && !s.error);
233
- const allRuns = await getRuns();
234
- async function getDescendantRuns(parentRunId) {
235
- const children = allRuns.filter((r) => r.parent_run_id === parentRunId);
236
- return Promise.all(
237
- children.map(async (childRun) => {
238
- const childSteps = await getStepsForRun(childRun.id);
239
- const childIsInProgress = childSteps.some(
240
- (s) => s.duration_ms === null && !s.error
241
- );
242
- const grandchildren = await getDescendantRuns(childRun.id);
243
- return {
244
- run: { ...childRun, isInProgress: childIsInProgress },
245
- steps: childSteps,
246
- childRuns: grandchildren
247
- };
248
- })
249
- );
250
- }
251
- const childRuns = await getDescendantRuns(data.run.id);
252
- return c.json({
253
- run: { ...data.run, isInProgress },
254
- steps: data.steps,
255
- childRuns
256
- });
209
+ await reloadDb(remoteDbPath);
210
+ const data = await getRunWithSteps(c.req.param("id"));
211
+ if (!data) return c.json({ error: "Run not found" }, 404);
212
+ const isInProgress = data.steps.some((s) => s.duration_ms === null && !s.error);
213
+ const allRuns = await getRuns();
214
+ async function getDescendantRuns(parentRunId) {
215
+ const children = allRuns.filter((r) => r.parent_run_id === parentRunId);
216
+ return Promise.all(children.map(async (childRun) => {
217
+ const childSteps = await getStepsForRun(childRun.id);
218
+ const childIsInProgress = childSteps.some((s) => s.duration_ms === null && !s.error);
219
+ const grandchildren = await getDescendantRuns(childRun.id);
220
+ return {
221
+ run: {
222
+ ...childRun,
223
+ isInProgress: childIsInProgress
224
+ },
225
+ steps: childSteps,
226
+ childRuns: grandchildren
227
+ };
228
+ }));
229
+ }
230
+ const childRuns = await getDescendantRuns(data.run.id);
231
+ return c.json({
232
+ run: {
233
+ ...data.run,
234
+ isInProgress
235
+ },
236
+ steps: data.steps,
237
+ childRuns
238
+ });
257
239
  });
258
240
  app.post("/api/clear", async (c) => {
259
- await clearDatabase();
260
- return c.json({ success: true });
241
+ await clearDatabase();
242
+ return c.json({ success: true });
261
243
  });
262
244
  app.get("/api/events", (c) => {
263
- return streamSSE(c, async (stream) => {
264
- const clientId = crypto.randomUUID();
265
- const client = {
266
- id: clientId,
267
- controller: null
268
- };
269
- await stream.writeSSE({
270
- event: "connected",
271
- data: JSON.stringify({ clientId })
272
- });
273
- const originalWrite = stream.writeSSE.bind(stream);
274
- client.controller = {
275
- enqueue: (message) => {
276
- const lines = message.split("\n");
277
- let event = "message";
278
- let data = "";
279
- for (const line of lines) {
280
- if (line.startsWith("event: ")) {
281
- event = line.slice(7);
282
- } else if (line.startsWith("data: ")) {
283
- data = line.slice(6);
284
- }
285
- }
286
- originalWrite({ event, data }).catch(() => {
287
- });
288
- }
289
- };
290
- sseClients.add(client);
291
- const heartbeat = setInterval(async () => {
292
- try {
293
- await stream.writeSSE({
294
- event: "heartbeat",
295
- data: JSON.stringify({ time: Date.now() })
296
- });
297
- } catch {
298
- clearInterval(heartbeat);
299
- }
300
- }, 3e4);
301
- try {
302
- while (true) {
303
- await stream.sleep(1e3);
304
- }
305
- } finally {
306
- clearInterval(heartbeat);
307
- sseClients.delete(client);
308
- }
309
- });
245
+ return streamSSE(c, async (stream) => {
246
+ const clientId = crypto.randomUUID();
247
+ const client = {
248
+ id: clientId,
249
+ controller: null
250
+ };
251
+ await stream.writeSSE({
252
+ event: "connected",
253
+ data: JSON.stringify({ clientId })
254
+ });
255
+ const originalWrite = stream.writeSSE.bind(stream);
256
+ client.controller = { enqueue: (message) => {
257
+ const lines = message.split("\n");
258
+ let event = "message";
259
+ let data = "";
260
+ for (const line of lines) if (line.startsWith("event: ")) event = line.slice(7);
261
+ else if (line.startsWith("data: ")) data = line.slice(6);
262
+ originalWrite({
263
+ event,
264
+ data
265
+ }).catch(() => {});
266
+ } };
267
+ sseClients.add(client);
268
+ const heartbeat = setInterval(async () => {
269
+ try {
270
+ await stream.writeSSE({
271
+ event: "heartbeat",
272
+ data: JSON.stringify({ time: Date.now() })
273
+ });
274
+ } catch {
275
+ clearInterval(heartbeat);
276
+ }
277
+ }, 3e4);
278
+ try {
279
+ while (true) await stream.sleep(1e3);
280
+ } finally {
281
+ clearInterval(heartbeat);
282
+ sseClients.delete(client);
283
+ }
284
+ });
310
285
  });
311
286
  app.post("/api/notify", async (c) => {
312
- const body = await c.req.json();
313
- if (body.dbPath !== void 0) {
314
- const validatedDbPath = validateRemoteDbPath(body.dbPath);
315
- if (!validatedDbPath) {
316
- return c.text("Invalid dbPath", 400);
317
- }
318
- remoteDbPath = validatedDbPath;
319
- }
320
- await reloadDb(remoteDbPath);
321
- broadcastToClients("update", body);
322
- return c.json({ success: true });
287
+ const body = await c.req.json();
288
+ if (body.dbPath !== void 0) {
289
+ const validatedDbPath = validateRemoteDbPath(body.dbPath);
290
+ if (!validatedDbPath) return c.text("Invalid dbPath", 400);
291
+ remoteDbPath = validatedDbPath;
292
+ }
293
+ await reloadDb(remoteDbPath);
294
+ broadcastToClients("update", body);
295
+ return c.json({ success: true });
323
296
  });
324
- app.use(
325
- "/assets/*",
326
- serveStatic({
327
- root: clientDir.replace(/\/+$/, "")
328
- })
329
- );
297
+ app.use("/assets/*", serveStatic({ root: clientDir.replace(/\/+$/, "") }));
330
298
  app.get("*", async (c) => {
331
- if (isDevMode) {
332
- return c.html(`
299
+ if (isDevMode) return c.html(`
333
300
  <!DOCTYPE html>
334
301
  <html>
335
302
  <head>
@@ -346,66 +313,44 @@ app.get("*", async (c) => {
346
313
  <body>
347
314
  <div class="container">
348
315
  <h2>Development Mode</h2>
349
- <a href="http://localhost:5173">Open DevTools UI \u2192</a>
316
+ <a href="http://localhost:5173">Open DevTools UI →</a>
350
317
  <p>This port (4983) only serves the API in dev mode.</p>
351
318
  </div>
352
319
  </body>
353
320
  </html>
354
321
  `);
355
- }
356
- const indexPath = path2.join(clientDir, "index.html");
357
- try {
358
- const html = fs2.readFileSync(indexPath, "utf-8");
359
- return c.html(html);
360
- } catch {
361
- return c.text("DevTools client not built. Run `pnpm build` first.", 500);
362
- }
322
+ const indexPath = path.join(clientDir, "index.html");
323
+ try {
324
+ const html = fs.readFileSync(indexPath, "utf-8");
325
+ return c.html(html);
326
+ } catch {
327
+ return c.text("DevTools client not built. Run `pnpm build` first.", 500);
328
+ }
363
329
  });
364
- var startViewer = (port = 4983) => {
365
- viewerPort = port;
366
- const server = serve(
367
- {
368
- fetch: app.fetch,
369
- port,
370
- hostname: "localhost"
371
- },
372
- () => {
373
- if (isDevMode) {
374
- console.log(`\u{1F50D} AI SDK DevTools API running on port ${port}`);
375
- console.log(` Open http://localhost:5173 for the dev UI`);
376
- } else {
377
- console.log(`\u{1F50D} AI SDK DevTools running at http://localhost:${port}`);
378
- }
379
- }
380
- );
381
- server.on("error", (err) => {
382
- if (err.code === "EADDRINUSE") {
383
- console.error(`
384
- \u274C Port ${port} is already in use.`);
385
- console.error(
386
- `
387
- This likely means AI SDK DevTools is already running.`
388
- );
389
- console.error(` Open http://localhost:${port} in your browser.
390
- `);
391
- console.error(` To use a different port, set AI_SDK_DEVTOOLS_PORT:
392
- `);
393
- console.error(
394
- ` AI_SDK_DEVTOOLS_PORT=4984 npx @ai-sdk/devtools@latest
395
- `
396
- );
397
- process.exit(1);
398
- }
399
- throw err;
400
- });
401
- };
402
- var currentFile = fileURLToPath(import.meta.url);
403
- var isDirectRun = process.argv[1] === currentFile || process.argv[1]?.endsWith("/server.ts") || process.argv[1]?.endsWith("/server.js");
404
- if (isDirectRun) {
405
- const port = process.env.AI_SDK_DEVTOOLS_PORT ? parseInt(process.env.AI_SDK_DEVTOOLS_PORT) : 4983;
406
- startViewer(port);
407
- }
408
- export {
409
- app,
410
- startViewer
330
+ const startViewer = (port = 4983) => {
331
+ viewerPort = port;
332
+ serve({
333
+ fetch: app.fetch,
334
+ port,
335
+ hostname: "localhost"
336
+ }, () => {
337
+ if (isDevMode) {
338
+ console.log(`šŸ” AI SDK DevTools API running on port ${port}`);
339
+ console.log(` Open http://localhost:5173 for the dev UI`);
340
+ } else console.log(`šŸ” AI SDK DevTools running at http://localhost:${port}`);
341
+ }).on("error", (err) => {
342
+ if (err.code === "EADDRINUSE") {
343
+ console.error(`\nāŒ Port ${port} is already in use.`);
344
+ console.error(`\n This likely means AI SDK DevTools is already running.`);
345
+ console.error(` Open http://localhost:${port} in your browser.\n`);
346
+ console.error(` To use a different port, set AI_SDK_DEVTOOLS_PORT:\n`);
347
+ console.error(` AI_SDK_DEVTOOLS_PORT=4984 npx @ai-sdk/devtools@latest\n`);
348
+ process.exit(1);
349
+ }
350
+ throw err;
351
+ });
411
352
  };
353
+ const currentFile = fileURLToPath(import.meta.url);
354
+ if (process.argv[1] === currentFile || process.argv[1]?.endsWith("/server.ts") || process.argv[1]?.endsWith("/server.js")) startViewer(process.env.AI_SDK_DEVTOOLS_PORT ? parseInt(process.env.AI_SDK_DEVTOOLS_PORT) : 4983);
355
+ //#endregion
356
+ export { app, startViewer };