aisnitch 0.2.24 → 0.2.25

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.
@@ -771,7 +771,7 @@ var import_commander = require("commander");
771
771
 
772
772
  // src/package-info.ts
773
773
  var AISNITCH_PACKAGE_NAME = "aisnitch";
774
- var AISNITCH_VERSION = "0.2.24";
774
+ var AISNITCH_VERSION = "0.2.25";
775
775
  var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
776
776
 
777
777
  // src/core/events/schema.ts
@@ -15960,6 +15960,40 @@ function formatSpawnError(error) {
15960
15960
  }
15961
15961
  return String(error);
15962
15962
  }
15963
+ async function pathExists(path2) {
15964
+ try {
15965
+ await (0, import_promises22.access)(path2, import_node_fs7.constants.R_OK);
15966
+ return true;
15967
+ } catch {
15968
+ return false;
15969
+ }
15970
+ }
15971
+ async function resolveDashboardDistPath() {
15972
+ if (process.env.AISNITCH_DASHBOARD_DIST) {
15973
+ const overridePath = process.env.AISNITCH_DASHBOARD_DIST;
15974
+ if (await pathExists((0, import_node_path22.join)(overridePath, "index.html"))) {
15975
+ return overridePath;
15976
+ }
15977
+ throw new Error(
15978
+ `Fullscreen dashboard assets are missing at ${overridePath}. Reinstall AISnitch or run \`pnpm --filter aisnitch-fullscreen-dashboard build\` from the repository checkout.`
15979
+ );
15980
+ }
15981
+ const cliEntryPath = process.argv[1] ? await (0, import_promises22.realpath)(process.argv[1]).catch(() => process.argv[1] ?? "") : "";
15982
+ const moduleDirectory = (0, import_node_path22.dirname)(cliEntryPath);
15983
+ const packageRoot = (0, import_node_path22.dirname)((0, import_node_path22.dirname)(moduleDirectory));
15984
+ const candidates = [
15985
+ (0, import_node_path22.join)(packageRoot, "examples", "fullscreen-dashboard", "dist"),
15986
+ (0, import_node_path22.join)(process.cwd(), "examples", "fullscreen-dashboard", "dist")
15987
+ ];
15988
+ for (const candidate of candidates) {
15989
+ if (await pathExists((0, import_node_path22.join)(candidate, "index.html"))) {
15990
+ return candidate;
15991
+ }
15992
+ }
15993
+ throw new Error(
15994
+ "Fullscreen dashboard assets are missing. Reinstall AISnitch or run `pnpm --filter aisnitch-fullscreen-dashboard build` from the repository checkout."
15995
+ );
15996
+ }
15963
15997
  function createCliRuntime(dependencies = {}) {
15964
15998
  const output = dependencies.output ?? createProcessOutput();
15965
15999
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
@@ -16475,34 +16509,77 @@ function createCliRuntime(dependencies = {}) {
16475
16509
  }
16476
16510
  const dashboardPort = options.dashboardPort ?? 5174;
16477
16511
  const dashboardUrl = `http://127.0.0.1:${dashboardPort}`;
16478
- const distPath = (0, import_node_path22.join)(process.cwd(), "examples", "fullscreen-dashboard", "dist");
16512
+ const distPath = await resolveDashboardDistPath();
16479
16513
  output.stdout(`Starting dashboard server on port ${dashboardPort}...
16480
16514
  `);
16481
16515
  const nodeExecutable = await resolveNodeExecutable();
16482
16516
  const viteProcess = spawnImplementation(nodeExecutable, [
16483
16517
  "-e",
16484
16518
  `
16485
- import { createServer } from 'vite';
16486
- import path from 'path';
16519
+ import { createReadStream } from 'node:fs';
16520
+ import { stat } from 'node:fs/promises';
16521
+ import { createServer } from 'node:http';
16522
+ import { extname, join, normalize, resolve } from 'node:path';
16487
16523
 
16488
- const distPath = '${distPath}';
16524
+ const distPath = ${JSON.stringify(distPath)};
16489
16525
  const port = ${dashboardPort};
16526
+ const root = resolve(distPath);
16527
+ const contentTypes = new Map([
16528
+ ['.css', 'text/css; charset=utf-8'],
16529
+ ['.html', 'text/html; charset=utf-8'],
16530
+ ['.js', 'text/javascript; charset=utf-8'],
16531
+ ['.json', 'application/json; charset=utf-8'],
16532
+ ['.map', 'application/json; charset=utf-8'],
16533
+ ['.svg', 'image/svg+xml'],
16534
+ ]);
16490
16535
 
16491
- const server = await createServer({
16492
- root: distPath,
16493
- server: {
16494
- port,
16495
- strictPort: true,
16496
- allowedHosts: true,
16497
- },
16498
- preview: {
16499
- port,
16500
- strictPort: true,
16501
- },
16536
+ function safePath(url) {
16537
+ const parsed = new URL(url ?? '/', 'http://127.0.0.1');
16538
+ const pathname = parsed.pathname === '/' ? '/index.html' : parsed.pathname;
16539
+ const decoded = decodeURIComponent(pathname);
16540
+ const normalized = normalize(decoded).replace(/^[/\\]+/, '');
16541
+ const absolute = resolve(join(root, normalized));
16542
+
16543
+ if (absolute !== root && !absolute.startsWith(root + '/')) {
16544
+ return null;
16545
+ }
16546
+
16547
+ return absolute;
16548
+ }
16549
+
16550
+ await stat(join(root, 'index.html'));
16551
+
16552
+ const server = createServer(async (request, response) => {
16553
+ const requestedPath = safePath(request.url);
16554
+
16555
+ if (requestedPath === null) {
16556
+ response.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
16557
+ response.end('Forbidden');
16558
+ return;
16559
+ }
16560
+
16561
+ try {
16562
+ const fileStat = await stat(requestedPath);
16563
+ const filePath = fileStat.isFile() ? requestedPath : join(root, 'index.html');
16564
+ const contentType = contentTypes.get(extname(filePath)) ?? 'application/octet-stream';
16565
+
16566
+ response.writeHead(200, { 'content-type': contentType });
16567
+ createReadStream(filePath).pipe(response);
16568
+ } catch {
16569
+ response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
16570
+ createReadStream(join(root, 'index.html')).pipe(response);
16571
+ }
16502
16572
  });
16503
16573
 
16504
- await server.listen();
16505
- console.log('READY');
16574
+ server.on('error', (error) => {
16575
+ console.error(error instanceof Error ? error.message : String(error));
16576
+ process.exitCode = 1;
16577
+ });
16578
+
16579
+ await new Promise((resolveListen, rejectListen) => {
16580
+ server.once('error', rejectListen);
16581
+ server.listen(port, '127.0.0.1', resolveListen);
16582
+ });
16506
16583
 
16507
16584
  process.stdin.resume();
16508
16585
  `