@getvision/server 0.4.3 → 0.4.4-44d79d9-develop

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.
Files changed (44) hide show
  1. package/dist/event-bus.d.ts +87 -0
  2. package/dist/event-bus.d.ts.map +1 -0
  3. package/dist/event-bus.js +265 -0
  4. package/dist/event-bus.js.map +10 -0
  5. package/dist/event-registry.d.ts +79 -0
  6. package/dist/event-registry.d.ts.map +1 -0
  7. package/dist/event-registry.js +93 -0
  8. package/dist/event-registry.js.map +10 -0
  9. package/{src/index.ts → dist/index.d.ts} +14 -28
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +33 -0
  12. package/dist/index.js.map +10 -0
  13. package/dist/router.d.ts +16 -0
  14. package/dist/router.d.ts.map +1 -0
  15. package/dist/router.js +117 -0
  16. package/dist/router.js.map +10 -0
  17. package/dist/service.d.ts +151 -0
  18. package/dist/service.d.ts.map +1 -0
  19. package/dist/service.js +341 -0
  20. package/dist/service.js.map +10 -0
  21. package/dist/types.d.ts +71 -0
  22. package/dist/types.d.ts.map +1 -0
  23. package/dist/types.js +2 -0
  24. package/dist/types.js.map +9 -0
  25. package/dist/vision-app.d.ts +166 -0
  26. package/dist/vision-app.d.ts.map +1 -0
  27. package/dist/vision-app.js +611 -0
  28. package/dist/vision-app.js.map +10 -0
  29. package/dist/vision.d.ts +63 -0
  30. package/dist/vision.d.ts.map +1 -0
  31. package/dist/vision.js +223 -0
  32. package/dist/vision.js.map +10 -0
  33. package/package.json +13 -3
  34. package/.env.example +0 -3
  35. package/.eslintrc.cjs +0 -7
  36. package/.turbo/turbo-build.log +0 -1
  37. package/src/event-bus.ts +0 -409
  38. package/src/event-registry.ts +0 -158
  39. package/src/router.ts +0 -118
  40. package/src/service.ts +0 -618
  41. package/src/types.ts +0 -93
  42. package/src/vision-app.ts +0 -880
  43. package/src/vision.ts +0 -319
  44. package/tsconfig.json +0 -9
@@ -0,0 +1,611 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/vision-app.ts
21
+ import { Hono } from "hono";
22
+ import { VisionCore, runInTraceContext } from "@getvision/core";
23
+ import { AsyncLocalStorage } from "async_hooks";
24
+ import { existsSync } from "fs";
25
+ import { spawn, spawnSync } from "child_process";
26
+ import { ServiceBuilder } from "./service";
27
+ import { EventBus } from "./event-bus";
28
+ import { eventRegistry } from "./event-registry";
29
+ var visionContext = new AsyncLocalStorage;
30
+ var GLOBAL_VISION_KEY = "__vision_instance_state";
31
+ if (!globalThis[GLOBAL_VISION_KEY]) {
32
+ globalThis[GLOBAL_VISION_KEY] = {
33
+ instance: null,
34
+ drizzleProcess: null
35
+ };
36
+ }
37
+ function getGlobalState() {
38
+ return globalThis[GLOBAL_VISION_KEY];
39
+ }
40
+ async function cleanupVisionInstance(instance) {
41
+ const existing = instance._cleanupPromise;
42
+ if (existing)
43
+ return existing;
44
+ instance._cleanupPromise = (async () => {
45
+ const server = instance.bunServer;
46
+ const hasBunServer = server && typeof server.stop === "function";
47
+ try {
48
+ if (hasBunServer) {
49
+ server.stop();
50
+ }
51
+ if (globalThis.__vision_bun_server === server) {
52
+ globalThis.__vision_bun_server = undefined;
53
+ }
54
+ } catch {}
55
+ try {
56
+ stopDrizzleStudio({ log: false });
57
+ } catch {}
58
+ try {
59
+ await instance.eventBus?.close();
60
+ } catch {}
61
+ })();
62
+ return instance._cleanupPromise;
63
+ }
64
+ function deepMerge(target, source) {
65
+ const output = { ...target };
66
+ if (source && typeof source === "object") {
67
+ for (const key of Object.keys(source)) {
68
+ const srcVal = source[key];
69
+ const tgtVal = output[key];
70
+ if (srcVal && typeof srcVal === "object" && !Array.isArray(srcVal) && tgtVal && typeof tgtVal === "object" && !Array.isArray(tgtVal)) {
71
+ output[key] = deepMerge(tgtVal, srcVal);
72
+ } else {
73
+ output[key] = srcVal;
74
+ }
75
+ }
76
+ }
77
+ return output;
78
+ }
79
+
80
+ class Vision extends Hono {
81
+ visionCore;
82
+ eventBus;
83
+ config;
84
+ serviceBuilders = [];
85
+ bunServer;
86
+ signalHandler;
87
+ constructor(config) {
88
+ super();
89
+ const defaultConfig = {
90
+ service: {
91
+ name: "Vision SubApp"
92
+ },
93
+ vision: {
94
+ enabled: false,
95
+ port: 9500
96
+ },
97
+ pubsub: {},
98
+ routes: {
99
+ autodiscover: true,
100
+ dirs: ["app/routes"]
101
+ }
102
+ };
103
+ this.config = deepMerge(defaultConfig, config || {});
104
+ const visionEnabled = this.config.vision?.enabled !== false;
105
+ const visionPort = this.config.vision?.port ?? 9500;
106
+ if (visionEnabled) {
107
+ this.visionCore = new VisionCore({
108
+ port: visionPort,
109
+ maxTraces: this.config.vision?.maxTraces ?? 1000,
110
+ maxLogs: this.config.vision?.maxLogs ?? 1e4,
111
+ apiUrl: this.config.vision?.apiUrl
112
+ });
113
+ const drizzleInfo = detectDrizzle();
114
+ let drizzleStudioUrl;
115
+ if (drizzleInfo.detected) {
116
+ console.log(`\uD83D\uDDC4️ Drizzle detected (${drizzleInfo.configPath})`);
117
+ if (this.config.service.drizzle?.autoStart) {
118
+ const drizzlePort = this.config.service.drizzle.port || 4983;
119
+ const started = startDrizzleStudio(drizzlePort);
120
+ if (started) {
121
+ drizzleStudioUrl = "https://local.drizzle.studio";
122
+ }
123
+ } else {
124
+ console.log("\uD83D\uDCA1 Tip: Enable Drizzle Studio auto-start with drizzle: { autoStart: true }");
125
+ drizzleStudioUrl = "https://local.drizzle.studio";
126
+ }
127
+ }
128
+ const cleanIntegrations = Object.fromEntries(Object.entries(this.config.service.integrations || {}).filter(([_, v]) => v !== undefined));
129
+ this.visionCore.setAppStatus({
130
+ name: this.config.service.name,
131
+ version: this.config.service.version ?? "0.0.0",
132
+ description: this.config.service.description,
133
+ running: true,
134
+ pid: process.pid,
135
+ metadata: {
136
+ framework: "vision-server",
137
+ integrations: Object.keys(cleanIntegrations).length > 0 ? cleanIntegrations : undefined,
138
+ drizzle: drizzleInfo.detected ? {
139
+ detected: true,
140
+ configPath: drizzleInfo.configPath,
141
+ studioUrl: drizzleStudioUrl,
142
+ autoStarted: this.config.service.drizzle?.autoStart || false
143
+ } : {
144
+ detected: false,
145
+ configPath: undefined,
146
+ studioUrl: undefined,
147
+ autoStarted: false
148
+ }
149
+ }
150
+ });
151
+ } else {
152
+ this.visionCore = null;
153
+ }
154
+ this.eventBus = this.config.pubsub?.eventBus || new EventBus({
155
+ redis: this.config.pubsub?.redis,
156
+ devMode: this.config.pubsub?.devMode,
157
+ workerConcurrency: this.config.pubsub?.workerConcurrency,
158
+ queue: this.config.pubsub?.queue,
159
+ worker: this.config.pubsub?.worker,
160
+ queueEvents: this.config.pubsub?.queueEvents
161
+ });
162
+ if (visionEnabled) {
163
+ this.registerEventMethods();
164
+ }
165
+ if (visionEnabled) {
166
+ this.installVisionMiddleware();
167
+ }
168
+ }
169
+ registerEventMethods() {
170
+ const server = this.visionCore.getServer();
171
+ server.registerMethod("events/list", async () => {
172
+ const events = eventRegistry.getAllEvents();
173
+ return events.map((event) => ({
174
+ name: event.name,
175
+ description: event.description,
176
+ icon: event.icon,
177
+ tags: event.tags,
178
+ handlers: event.handlers.length,
179
+ lastTriggered: event.lastTriggered,
180
+ totalCount: event.totalCount,
181
+ failedCount: event.failedCount
182
+ }));
183
+ });
184
+ server.registerMethod("cron/list", async () => {
185
+ const crons = eventRegistry.getAllCrons();
186
+ return crons.map((cron) => ({
187
+ name: cron.name,
188
+ schedule: cron.schedule,
189
+ description: cron.description,
190
+ icon: cron.icon,
191
+ tags: cron.tags,
192
+ lastRun: cron.lastRun,
193
+ nextRun: cron.nextRun,
194
+ totalRuns: cron.totalRuns,
195
+ failedRuns: cron.failedRuns
196
+ }));
197
+ });
198
+ }
199
+ installVisionMiddleware() {
200
+ const logging = this.config.vision?.logging !== false;
201
+ this.use("*", async (c, next) => {
202
+ if (c.req.method === "OPTIONS") {
203
+ return next();
204
+ }
205
+ const startTime = Date.now();
206
+ const trace = this.visionCore.createTrace(c.req.method, c.req.path);
207
+ return visionContext.run({
208
+ vision: this.visionCore,
209
+ traceId: trace.id,
210
+ rootSpanId: ""
211
+ }, async () => {
212
+ return runInTraceContext(trace.id, async () => {
213
+ const tracer = this.visionCore.getTracer();
214
+ const rootSpan = tracer.startSpan("http.request", trace.id);
215
+ const ctx = visionContext.getStore();
216
+ if (ctx) {
217
+ ctx.rootSpanId = rootSpan.id;
218
+ }
219
+ if (!c.span) {
220
+ c.addContext = (context) => {
221
+ const current = visionContext.getStore();
222
+ const currentTraceId = current?.traceId || trace.id;
223
+ const visionTrace = this.visionCore.getTraceStore().getTrace(currentTraceId);
224
+ if (visionTrace) {
225
+ visionTrace.metadata = { ...visionTrace.metadata || {}, ...context };
226
+ }
227
+ };
228
+ c.span = (name, attributes = {}, fn) => {
229
+ const current = visionContext.getStore();
230
+ const currentTraceId = current?.traceId || trace.id;
231
+ const currentRootSpanId = current?.rootSpanId || rootSpan.id;
232
+ const s = tracer.startSpan(name, currentTraceId, currentRootSpanId);
233
+ for (const [k, v] of Object.entries(attributes))
234
+ tracer.setAttribute(s.id, k, v);
235
+ try {
236
+ const result = fn ? fn() : undefined;
237
+ const completed = tracer.endSpan(s.id);
238
+ if (completed)
239
+ this.visionCore.getTraceStore().addSpan(currentTraceId, completed);
240
+ return result;
241
+ } catch (err) {
242
+ tracer.setAttribute(s.id, "error", true);
243
+ tracer.setAttribute(s.id, "error.message", err instanceof Error ? err.message : String(err));
244
+ const completed = tracer.endSpan(s.id);
245
+ if (completed)
246
+ this.visionCore.getTraceStore().addSpan(currentTraceId, completed);
247
+ throw err;
248
+ }
249
+ };
250
+ }
251
+ tracer.setAttribute(rootSpan.id, "http.method", c.req.method);
252
+ tracer.setAttribute(rootSpan.id, "http.path", c.req.path);
253
+ tracer.setAttribute(rootSpan.id, "http.url", c.req.url);
254
+ const url = new URL(c.req.url);
255
+ if (url.search) {
256
+ tracer.setAttribute(rootSpan.id, "http.query", url.search);
257
+ }
258
+ try {
259
+ const rawReq = c.req.raw;
260
+ const headers = {};
261
+ rawReq.headers.forEach((v, k) => {
262
+ headers[k] = v;
263
+ });
264
+ const urlObj = new URL(c.req.url);
265
+ const query = {};
266
+ urlObj.searchParams.forEach((v, k) => {
267
+ query[k] = v;
268
+ });
269
+ let body = undefined;
270
+ const ct = headers["content-type"] || headers["Content-Type"];
271
+ if (ct && ct.includes("application/json")) {
272
+ try {
273
+ body = await rawReq.clone().json();
274
+ } catch {}
275
+ }
276
+ const sessionId = headers["x-vision-session"];
277
+ if (sessionId) {
278
+ tracer.setAttribute(rootSpan.id, "session.id", sessionId);
279
+ trace.metadata = { ...trace.metadata || {}, sessionId };
280
+ }
281
+ const requestMeta = {
282
+ method: c.req.method,
283
+ url: urlObj.pathname + (urlObj.search || ""),
284
+ headers,
285
+ query: Object.keys(query).length ? query : undefined,
286
+ body
287
+ };
288
+ tracer.setAttribute(rootSpan.id, "http.request", requestMeta);
289
+ trace.metadata = { ...trace.metadata || {}, request: requestMeta };
290
+ if (logging) {
291
+ const parts = [
292
+ `method=${c.req.method}`,
293
+ `path=${c.req.path}`
294
+ ];
295
+ if (sessionId)
296
+ parts.push(`sessionId=${sessionId}`);
297
+ parts.push(`traceId=${trace.id}`);
298
+ console.info(`INF starting request ${parts.join(" ")}`);
299
+ }
300
+ await next();
301
+ tracer.setAttribute(rootSpan.id, "http.status_code", c.res.status);
302
+ const resHeaders = {};
303
+ c.res.headers?.forEach((v, k) => {
304
+ resHeaders[k] = v;
305
+ });
306
+ let respBody = undefined;
307
+ const resCt = c.res.headers?.get("content-type") || "";
308
+ try {
309
+ const clone = c.res.clone();
310
+ if (resCt.includes("application/json")) {
311
+ const txt = await clone.text();
312
+ if (txt && txt.length <= 65536) {
313
+ try {
314
+ respBody = JSON.parse(txt);
315
+ } catch {
316
+ respBody = txt;
317
+ }
318
+ }
319
+ }
320
+ } catch {}
321
+ const responseMeta = {
322
+ status: c.res.status,
323
+ headers: Object.keys(resHeaders).length ? resHeaders : undefined,
324
+ body: respBody
325
+ };
326
+ tracer.setAttribute(rootSpan.id, "http.response", responseMeta);
327
+ trace.metadata = { ...trace.metadata || {}, response: responseMeta };
328
+ } catch (error) {
329
+ tracer.addEvent(rootSpan.id, "error", {
330
+ message: error instanceof Error ? error.message : "Unknown error",
331
+ stack: error instanceof Error ? error.stack : undefined
332
+ });
333
+ tracer.setAttribute(rootSpan.id, "error", true);
334
+ throw error;
335
+ } finally {
336
+ const completedSpan = tracer.endSpan(rootSpan.id);
337
+ if (completedSpan) {
338
+ this.visionCore.getTraceStore().addSpan(trace.id, completedSpan);
339
+ }
340
+ const duration = Date.now() - startTime;
341
+ this.visionCore.completeTrace(trace.id, c.res.status, duration);
342
+ c.header("X-Vision-Trace-Id", trace.id);
343
+ if (logging) {
344
+ console.info(`INF request completed code=${c.res.status} duration=${duration}ms method=${c.req.method} path=${c.req.path} traceId=${trace.id}`);
345
+ }
346
+ }
347
+ });
348
+ });
349
+ });
350
+ }
351
+ service(name) {
352
+ const builder = new ServiceBuilder(name, this.eventBus, this.visionCore);
353
+ this.serviceBuilders.push(builder);
354
+ return builder;
355
+ }
356
+ getServiceSummaries() {
357
+ const summaries = [];
358
+ for (const builder of this.serviceBuilders) {
359
+ const name = builder.getDisplayName?.() ?? "Service";
360
+ const rawRoutes = builder.getRoutesMetadata?.();
361
+ if (!rawRoutes || !Array.isArray(rawRoutes))
362
+ continue;
363
+ const routes = rawRoutes.map((r) => ({
364
+ method: r.method,
365
+ path: r.path,
366
+ handler: name,
367
+ queryParams: r.queryParams,
368
+ requestBody: r.requestBody,
369
+ responseBody: r.responseBody
370
+ }));
371
+ summaries.push({ name, routes });
372
+ }
373
+ return summaries;
374
+ }
375
+ buildAllServices() {
376
+ const allServices = [];
377
+ for (const builder of this.serviceBuilders) {
378
+ builder.build(this, allServices);
379
+ }
380
+ return allServices;
381
+ }
382
+ getVision() {
383
+ return this.visionCore;
384
+ }
385
+ getEventBus() {
386
+ return this.eventBus;
387
+ }
388
+ async autoloadRoutes() {
389
+ const enabled = this.config.routes?.autodiscover !== false;
390
+ const dirs = this.config.routes?.dirs ?? ["app/routes"];
391
+ if (!enabled)
392
+ return [];
393
+ const existing = [];
394
+ for (const d of dirs) {
395
+ try {
396
+ if (existsSync(d))
397
+ existing.push(d);
398
+ } catch {}
399
+ }
400
+ if (existing.length === 0)
401
+ return [];
402
+ const { loadSubApps } = await import("./router");
403
+ let allSubAppSummaries = [];
404
+ for (const d of existing) {
405
+ try {
406
+ const summaries = await loadSubApps(this, d, this.eventBus);
407
+ allSubAppSummaries = allSubAppSummaries.concat(summaries);
408
+ } catch (e) {
409
+ console.error(`❌ Failed to load sub-apps from ${d}:`, e?.message || e);
410
+ if (e instanceof Error && e.stack) {
411
+ console.error("Stack:", e.stack);
412
+ }
413
+ }
414
+ }
415
+ return allSubAppSummaries;
416
+ }
417
+ async start(port = 3000, options) {
418
+ const { hostname, ...restOptions } = options || {};
419
+ const { fetch: _bf, port: _bp, ...bunRest } = restOptions;
420
+ const { fetch: _nf, port: _np, ...nodeRest } = restOptions;
421
+ const rootSummaries = this.buildAllServices();
422
+ const subAppSummaries = await this.autoloadRoutes();
423
+ const allServices = new Map;
424
+ for (const summary of rootSummaries || []) {
425
+ allServices.set(summary.name, { name: summary.name, routes: [...summary.routes] });
426
+ }
427
+ for (const summary of subAppSummaries || []) {
428
+ if (allServices.has(summary.name)) {
429
+ const existing = allServices.get(summary.name);
430
+ existing.routes.push(...summary.routes);
431
+ } else {
432
+ allServices.set(summary.name, { name: summary.name, routes: [...summary.routes] });
433
+ }
434
+ }
435
+ if (this.visionCore && allServices.size > 0) {
436
+ const servicesToRegister = Array.from(allServices.values());
437
+ this.visionCore.registerServices(servicesToRegister);
438
+ const flatRoutes = servicesToRegister.flatMap((s) => s.routes);
439
+ this.visionCore.registerRoutes(flatRoutes);
440
+ console.log(`✅ Registered ${servicesToRegister.length} total services (${flatRoutes.length} routes)`);
441
+ }
442
+ const state = getGlobalState();
443
+ if (state.instance && state.instance !== this) {
444
+ await cleanupVisionInstance(state.instance);
445
+ }
446
+ state.instance = this;
447
+ console.log(`\uD83D\uDE80 Starting ${this.config.service.name}...`);
448
+ console.log(`\uD83D\uDCE1 API Server: http://localhost:${port}`);
449
+ if (!this.signalHandler) {
450
+ this.signalHandler = async () => {
451
+ const s = getGlobalState();
452
+ if (s.instance) {
453
+ await cleanupVisionInstance(s.instance);
454
+ }
455
+ try {
456
+ process.exit(0);
457
+ } catch {}
458
+ };
459
+ }
460
+ const handleSignal = this.signalHandler;
461
+ process.removeListener("SIGINT", handleSignal);
462
+ process.removeListener("SIGTERM", handleSignal);
463
+ try {
464
+ process.removeListener("SIGQUIT", handleSignal);
465
+ } catch {}
466
+ process.on("SIGINT", handleSignal);
467
+ process.on("SIGTERM", handleSignal);
468
+ try {
469
+ process.on("SIGQUIT", handleSignal);
470
+ } catch {}
471
+ try {
472
+ const hot = import.meta?.hot;
473
+ if (hot && typeof hot.dispose === "function") {
474
+ hot.dispose(async () => {
475
+ console.log("♻️ Hot reload: reloading...");
476
+ process.off("SIGINT", handleSignal);
477
+ process.off("SIGTERM", handleSignal);
478
+ try {
479
+ process.off("SIGQUIT", handleSignal);
480
+ } catch {}
481
+ const s = getGlobalState();
482
+ await cleanupVisionInstance(this);
483
+ if (s.instance === this) {
484
+ s.instance = null;
485
+ }
486
+ });
487
+ }
488
+ } catch {}
489
+ if (typeof process !== "undefined" && process.versions?.bun) {
490
+ const BunServe = globalThis.Bun?.serve;
491
+ if (typeof BunServe === "function") {
492
+ try {
493
+ const existing = globalThis.__vision_bun_server;
494
+ if (existing && typeof existing.stop === "function") {
495
+ try {
496
+ existing.stop();
497
+ } catch {}
498
+ }
499
+ } catch {}
500
+ this.bunServer = BunServe({
501
+ ...bunRest,
502
+ fetch: this.fetch.bind(this),
503
+ port,
504
+ hostname
505
+ });
506
+ try {
507
+ globalThis.__vision_bun_server = this.bunServer;
508
+ } catch {}
509
+ } else {
510
+ console.warn("Bun detected but Bun.serve is unavailable");
511
+ return this;
512
+ }
513
+ } else if (typeof process !== "undefined" && process.versions?.node) {
514
+ const { serve } = await import("@hono/node-server");
515
+ serve({
516
+ ...nodeRest,
517
+ fetch: this.fetch.bind(this),
518
+ port,
519
+ hostname
520
+ });
521
+ } else {
522
+ console.log("⚠️ Use your runtime's serve function");
523
+ return this;
524
+ }
525
+ }
526
+ setEventBus(eventBus) {
527
+ this.eventBus = eventBus;
528
+ }
529
+ }
530
+ function getVisionContext() {
531
+ return visionContext.getStore();
532
+ }
533
+ function detectDrizzle() {
534
+ const possiblePaths = [
535
+ "drizzle.config.ts",
536
+ "drizzle.config.js",
537
+ "drizzle.config.mjs"
538
+ ];
539
+ for (const path of possiblePaths) {
540
+ if (existsSync(path)) {
541
+ return { detected: true, configPath: path };
542
+ }
543
+ }
544
+ return { detected: false };
545
+ }
546
+ function startDrizzleStudio(port) {
547
+ const state = getGlobalState();
548
+ if (state.drizzleProcess) {
549
+ console.log("⚠️ Drizzle Studio is already running");
550
+ return false;
551
+ }
552
+ try {
553
+ if (process.platform === "win32") {
554
+ const res = spawnSync("powershell", ["-NoProfile", "-Command", `netstat -ano | Select-String -Pattern "LISTENING\\s+.*:${port}\\s"`], { encoding: "utf-8" });
555
+ if ((res.stdout || "").trim().length > 0) {
556
+ console.log(`⚠️ Drizzle Studio port ${port} already in use; assuming it is running. Skipping auto-start.`);
557
+ return true;
558
+ }
559
+ } else {
560
+ const res = spawnSync("lsof", ["-i", `tcp:${port}`, "-sTCP:LISTEN"], { encoding: "utf-8" });
561
+ if ((res.stdout || "").trim().length > 0) {
562
+ console.log(`⚠️ Drizzle Studio port ${port} already in use; assuming it is running. Skipping auto-start.`);
563
+ return true;
564
+ }
565
+ }
566
+ } catch {}
567
+ try {
568
+ const proc = spawn("npx", ["drizzle-kit", "studio", "--port", String(port), "--host", "0.0.0.0"], {
569
+ stdio: "inherit",
570
+ detached: false,
571
+ shell: process.platform === "win32"
572
+ });
573
+ state.drizzleProcess = proc;
574
+ proc.on("error", (error) => {
575
+ console.error("❌ Failed to start Drizzle Studio:", error.message);
576
+ });
577
+ proc.on("exit", (code) => {
578
+ if (code !== 0 && code !== null) {
579
+ console.error(`❌ Drizzle Studio exited with code ${code}`);
580
+ }
581
+ const s = getGlobalState();
582
+ if (s.drizzleProcess === proc) {
583
+ s.drizzleProcess = null;
584
+ }
585
+ });
586
+ console.log(`✅ Drizzle Studio: https://local.drizzle.studio`);
587
+ return true;
588
+ } catch (error) {
589
+ console.error("❌ Failed to start Drizzle Studio:", error);
590
+ return false;
591
+ }
592
+ }
593
+ function stopDrizzleStudio(options) {
594
+ const state = getGlobalState();
595
+ if (state.drizzleProcess) {
596
+ state.drizzleProcess.removeAllListeners();
597
+ state.drizzleProcess.kill();
598
+ state.drizzleProcess = null;
599
+ if (options?.log !== false) {
600
+ console.log("\uD83D\uDED1 Drizzle Studio stopped");
601
+ }
602
+ return true;
603
+ }
604
+ return false;
605
+ }
606
+ export {
607
+ getVisionContext,
608
+ Vision
609
+ };
610
+
611
+ //# debugId=030980C43D326B6264756E2164756E21