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.
package/dist/cli/index.js CHANGED
@@ -743,7 +743,7 @@ import { Command, InvalidArgumentError } from "commander";
743
743
 
744
744
  // src/package-info.ts
745
745
  var AISNITCH_PACKAGE_NAME = "aisnitch";
746
- var AISNITCH_VERSION = "0.2.24";
746
+ var AISNITCH_VERSION = "0.2.25";
747
747
  var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
748
748
 
749
749
  // src/core/events/schema.ts
@@ -3468,10 +3468,10 @@ function toConfigPathOptions(options) {
3468
3468
  // src/cli/runtime.ts
3469
3469
  import { execFile as execFileCallback14, spawn as spawnChildProcess2 } from "child_process";
3470
3470
  import { closeSync, constants as fsConstants4, openSync } from "fs";
3471
- import { access as access4, mkdtemp, readFile as readFile15, rename, rm as rm4, writeFile as writeFile5 } from "fs/promises";
3471
+ import { access as access4, mkdtemp, readFile as readFile15, realpath as realpath2, rename, rm as rm4, writeFile as writeFile5 } from "fs/promises";
3472
3472
  import { createConnection as createConnection3 } from "net";
3473
3473
  import { tmpdir } from "os";
3474
- import { basename as basename12, join as join18 } from "path";
3474
+ import { basename as basename12, dirname as dirname8, join as join18 } from "path";
3475
3475
  import { promisify as promisify21 } from "util";
3476
3476
 
3477
3477
  // src/adapters/generic-pty.ts
@@ -15934,6 +15934,40 @@ function formatSpawnError(error) {
15934
15934
  }
15935
15935
  return String(error);
15936
15936
  }
15937
+ async function pathExists(path2) {
15938
+ try {
15939
+ await access4(path2, fsConstants4.R_OK);
15940
+ return true;
15941
+ } catch {
15942
+ return false;
15943
+ }
15944
+ }
15945
+ async function resolveDashboardDistPath() {
15946
+ if (process.env.AISNITCH_DASHBOARD_DIST) {
15947
+ const overridePath = process.env.AISNITCH_DASHBOARD_DIST;
15948
+ if (await pathExists(join18(overridePath, "index.html"))) {
15949
+ return overridePath;
15950
+ }
15951
+ throw new Error(
15952
+ `Fullscreen dashboard assets are missing at ${overridePath}. Reinstall AISnitch or run \`pnpm --filter aisnitch-fullscreen-dashboard build\` from the repository checkout.`
15953
+ );
15954
+ }
15955
+ const cliEntryPath = process.argv[1] ? await realpath2(process.argv[1]).catch(() => process.argv[1] ?? "") : "";
15956
+ const moduleDirectory = dirname8(cliEntryPath);
15957
+ const packageRoot = dirname8(dirname8(moduleDirectory));
15958
+ const candidates = [
15959
+ join18(packageRoot, "examples", "fullscreen-dashboard", "dist"),
15960
+ join18(process.cwd(), "examples", "fullscreen-dashboard", "dist")
15961
+ ];
15962
+ for (const candidate of candidates) {
15963
+ if (await pathExists(join18(candidate, "index.html"))) {
15964
+ return candidate;
15965
+ }
15966
+ }
15967
+ throw new Error(
15968
+ "Fullscreen dashboard assets are missing. Reinstall AISnitch or run `pnpm --filter aisnitch-fullscreen-dashboard build` from the repository checkout."
15969
+ );
15970
+ }
15937
15971
  function createCliRuntime(dependencies = {}) {
15938
15972
  const output = dependencies.output ?? createProcessOutput();
15939
15973
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
@@ -16449,34 +16483,77 @@ function createCliRuntime(dependencies = {}) {
16449
16483
  }
16450
16484
  const dashboardPort = options.dashboardPort ?? 5174;
16451
16485
  const dashboardUrl = `http://127.0.0.1:${dashboardPort}`;
16452
- const distPath = join18(process.cwd(), "examples", "fullscreen-dashboard", "dist");
16486
+ const distPath = await resolveDashboardDistPath();
16453
16487
  output.stdout(`Starting dashboard server on port ${dashboardPort}...
16454
16488
  `);
16455
16489
  const nodeExecutable = await resolveNodeExecutable();
16456
16490
  const viteProcess = spawnImplementation(nodeExecutable, [
16457
16491
  "-e",
16458
16492
  `
16459
- import { createServer } from 'vite';
16460
- import path from 'path';
16493
+ import { createReadStream } from 'node:fs';
16494
+ import { stat } from 'node:fs/promises';
16495
+ import { createServer } from 'node:http';
16496
+ import { extname, join, normalize, resolve } from 'node:path';
16461
16497
 
16462
- const distPath = '${distPath}';
16498
+ const distPath = ${JSON.stringify(distPath)};
16463
16499
  const port = ${dashboardPort};
16500
+ const root = resolve(distPath);
16501
+ const contentTypes = new Map([
16502
+ ['.css', 'text/css; charset=utf-8'],
16503
+ ['.html', 'text/html; charset=utf-8'],
16504
+ ['.js', 'text/javascript; charset=utf-8'],
16505
+ ['.json', 'application/json; charset=utf-8'],
16506
+ ['.map', 'application/json; charset=utf-8'],
16507
+ ['.svg', 'image/svg+xml'],
16508
+ ]);
16464
16509
 
16465
- const server = await createServer({
16466
- root: distPath,
16467
- server: {
16468
- port,
16469
- strictPort: true,
16470
- allowedHosts: true,
16471
- },
16472
- preview: {
16473
- port,
16474
- strictPort: true,
16475
- },
16510
+ function safePath(url) {
16511
+ const parsed = new URL(url ?? '/', 'http://127.0.0.1');
16512
+ const pathname = parsed.pathname === '/' ? '/index.html' : parsed.pathname;
16513
+ const decoded = decodeURIComponent(pathname);
16514
+ const normalized = normalize(decoded).replace(/^[/\\]+/, '');
16515
+ const absolute = resolve(join(root, normalized));
16516
+
16517
+ if (absolute !== root && !absolute.startsWith(root + '/')) {
16518
+ return null;
16519
+ }
16520
+
16521
+ return absolute;
16522
+ }
16523
+
16524
+ await stat(join(root, 'index.html'));
16525
+
16526
+ const server = createServer(async (request, response) => {
16527
+ const requestedPath = safePath(request.url);
16528
+
16529
+ if (requestedPath === null) {
16530
+ response.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
16531
+ response.end('Forbidden');
16532
+ return;
16533
+ }
16534
+
16535
+ try {
16536
+ const fileStat = await stat(requestedPath);
16537
+ const filePath = fileStat.isFile() ? requestedPath : join(root, 'index.html');
16538
+ const contentType = contentTypes.get(extname(filePath)) ?? 'application/octet-stream';
16539
+
16540
+ response.writeHead(200, { 'content-type': contentType });
16541
+ createReadStream(filePath).pipe(response);
16542
+ } catch {
16543
+ response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
16544
+ createReadStream(join(root, 'index.html')).pipe(response);
16545
+ }
16476
16546
  });
16477
16547
 
16478
- await server.listen();
16479
- console.log('READY');
16548
+ server.on('error', (error) => {
16549
+ console.error(error instanceof Error ? error.message : String(error));
16550
+ process.exitCode = 1;
16551
+ });
16552
+
16553
+ await new Promise((resolveListen, rejectListen) => {
16554
+ server.once('error', rejectListen);
16555
+ server.listen(port, '127.0.0.1', resolveListen);
16556
+ });
16480
16557
 
16481
16558
  process.stdin.resume();
16482
16559
  `