aisnitch 0.2.25 → 0.2.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.
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.25";
746
+ var AISNITCH_VERSION = "0.2.26";
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
@@ -1332,6 +1332,7 @@ var AutoUpdateConfigSchema = z2.strictObject({
1332
1332
  var ConfigSchema = z2.strictObject({
1333
1333
  wsPort: z2.number().int().min(1024).max(65535).default(4820),
1334
1334
  httpPort: z2.number().int().min(1024).max(65535).default(4821),
1335
+ dashboardPort: z2.number().int().min(1024).max(65535).default(5174),
1335
1336
  /**
1336
1337
  * 📖 This is intentionally a partial record because most users will only
1337
1338
  * override a couple of adapters instead of all supported tools at once.
@@ -1350,6 +1351,7 @@ var ConfigSchema = z2.strictObject({
1350
1351
  var DEFAULT_CONFIG = {
1351
1352
  wsPort: 4820,
1352
1353
  httpPort: 4821,
1354
+ dashboardPort: 5174,
1353
1355
  adapters: {},
1354
1356
  autoUpdate: {
1355
1357
  enabled: true,
@@ -14142,6 +14144,10 @@ function Header({
14142
14144
  ] }) }),
14143
14145
  daemon ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
14144
14146
  /* @__PURE__ */ jsx6(Text6, { color: connected ? TUI_THEME.success : TUI_THEME.warning, children: connected ? `\u25CF ${connectionLabel}` : `\u25CB ${connectionLabel}` }),
14147
+ /* @__PURE__ */ jsxs5(Text6, { color: TUI_THEME.success, children: [
14148
+ "Web ",
14149
+ daemon.dashboardUrl
14150
+ ] }),
14145
14151
  /* @__PURE__ */ jsxs5(Text6, { color: TUI_THEME.muted, children: [
14146
14152
  "WS ",
14147
14153
  daemon.wsUrl
@@ -14334,7 +14340,7 @@ function StatusBar({
14334
14340
  }) {
14335
14341
  const streamState = streamFrozen ? `Frozen +${pendingEventCount}` : latestEvent?.type ?? "Live";
14336
14342
  const focusLabel = focusPanel === "events" ? "events" : viewMode === "full-data" ? "inspector" : "sessions";
14337
- const daemonLabel = daemon === void 0 ? null : daemon.busyAction ? `Daemon ${daemon.busyAction}` : daemon.active ? `Daemon active \xB7 ${daemon.wsUrl}` : `Daemon not active \xB7 ${daemon.wsUrl}`;
14343
+ const daemonLabel = daemon === void 0 ? null : daemon.busyAction ? `Daemon ${daemon.busyAction}` : daemon.active ? `Daemon active \xB7 Web ${daemon.dashboardUrl}` : `Daemon not active \xB7 Web ${daemon.dashboardUrl}`;
14338
14344
  return /* @__PURE__ */ jsxs9(
14339
14345
  Box9,
14340
14346
  {
@@ -15919,6 +15925,7 @@ var DAEMON_READY_TIMEOUT_MS = 4e3;
15919
15925
  var DAEMON_READY_POLL_INTERVAL_MS = 100;
15920
15926
  var DAEMON_STOP_TIMEOUT_MS = 4e3;
15921
15927
  var DAEMON_LOG_MAX_BYTES = 5 * 1024 * 1024;
15928
+ var DEFAULT_DASHBOARD_PORT = 5174;
15922
15929
  var LAUNCH_AGENT_LABEL = "com.aisnitch.daemon";
15923
15930
  async function resolveNodeExecutable() {
15924
15931
  try {
@@ -15993,6 +16000,7 @@ function createCliRuntime(dependencies = {}) {
15993
16000
  const config = ConfigSchema.parse({
15994
16001
  ...baseConfig,
15995
16002
  ...isStartCliOptions(options) ? {
16003
+ dashboardPort: options.dashboardPort ?? baseConfig.dashboardPort,
15996
16004
  httpPort: options.httpPort ?? baseConfig.httpPort,
15997
16005
  logLevel: options.logLevel ?? baseConfig.logLevel,
15998
16006
  wsPort: options.wsPort ?? baseConfig.wsPort
@@ -16016,6 +16024,149 @@ function createCliRuntime(dependencies = {}) {
16016
16024
  return null;
16017
16025
  }
16018
16026
  }
16027
+ async function fetchOk(url) {
16028
+ try {
16029
+ const response = await fetchImplementation(url);
16030
+ return response.ok;
16031
+ } catch {
16032
+ return false;
16033
+ }
16034
+ }
16035
+ async function waitForDashboardReady(port) {
16036
+ const dashboardUrl = `http://127.0.0.1:${port}`;
16037
+ for (let i = 0; i < 100; i++) {
16038
+ if (await fetchOk(dashboardUrl)) {
16039
+ return true;
16040
+ }
16041
+ await sleep(100);
16042
+ }
16043
+ return false;
16044
+ }
16045
+ async function startDashboardServerProcess(port) {
16046
+ const dashboardUrl = `http://127.0.0.1:${port}`;
16047
+ if (await fetchOk(dashboardUrl)) {
16048
+ return {
16049
+ kill: () => void 0,
16050
+ pid: void 0
16051
+ };
16052
+ }
16053
+ const distPath = await resolveDashboardDistPath();
16054
+ const nodeExecutable = await resolveNodeExecutable();
16055
+ const serverProcess = spawnImplementation(nodeExecutable, [
16056
+ "-e",
16057
+ buildDashboardServerScript(distPath, port)
16058
+ ], {
16059
+ cwd: distPath,
16060
+ stdio: ["ignore", "pipe", "pipe"]
16061
+ });
16062
+ let serverOutput = "";
16063
+ let serverSpawnError;
16064
+ serverProcess.on("error", (error) => {
16065
+ serverSpawnError = error;
16066
+ serverOutput += `Dashboard server process failed: ${formatSpawnError(error)}
16067
+ `;
16068
+ });
16069
+ serverProcess.stdout?.on("data", (data) => {
16070
+ serverOutput += data.toString();
16071
+ });
16072
+ serverProcess.stderr?.on("data", (data) => {
16073
+ serverOutput += data.toString();
16074
+ });
16075
+ for (let i = 0; i < 100; i++) {
16076
+ if (await fetchOk(dashboardUrl)) {
16077
+ return {
16078
+ kill: () => {
16079
+ if (serverProcess.pid !== void 0) {
16080
+ try {
16081
+ process.kill(serverProcess.pid, "SIGTERM");
16082
+ } catch {
16083
+ }
16084
+ }
16085
+ },
16086
+ pid: serverProcess.pid
16087
+ };
16088
+ }
16089
+ if (serverSpawnError !== void 0) {
16090
+ throw new Error(
16091
+ `Failed to start dashboard server process with ${nodeExecutable}: ${formatSpawnError(serverSpawnError)}`
16092
+ );
16093
+ }
16094
+ await sleep(100);
16095
+ }
16096
+ throw new Error(
16097
+ `Failed to start dashboard server at ${dashboardUrl}. Server output: ${serverOutput}`
16098
+ );
16099
+ }
16100
+ function buildDashboardServerScript(distPath, port) {
16101
+ return `
16102
+ import { createReadStream } from 'node:fs';
16103
+ import { stat } from 'node:fs/promises';
16104
+ import { createServer } from 'node:http';
16105
+ import { extname, join, normalize, resolve } from 'node:path';
16106
+
16107
+ const distPath = ${JSON.stringify(distPath)};
16108
+ const port = ${port};
16109
+ const root = resolve(distPath);
16110
+ const contentTypes = new Map([
16111
+ ['.css', 'text/css; charset=utf-8'],
16112
+ ['.html', 'text/html; charset=utf-8'],
16113
+ ['.js', 'text/javascript; charset=utf-8'],
16114
+ ['.json', 'application/json; charset=utf-8'],
16115
+ ['.map', 'application/json; charset=utf-8'],
16116
+ ['.svg', 'image/svg+xml'],
16117
+ ]);
16118
+
16119
+ function safePath(url) {
16120
+ const parsed = new URL(url ?? '/', 'http://127.0.0.1');
16121
+ const pathname = parsed.pathname === '/' ? '/index.html' : parsed.pathname;
16122
+ const decoded = decodeURIComponent(pathname);
16123
+ const normalized = normalize(decoded).replace(/^[/\\]+/, '');
16124
+ const absolute = resolve(join(root, normalized));
16125
+
16126
+ if (absolute !== root && !absolute.startsWith(root + '/')) {
16127
+ return null;
16128
+ }
16129
+
16130
+ return absolute;
16131
+ }
16132
+
16133
+ await stat(join(root, 'index.html'));
16134
+
16135
+ const server = createServer(async (request, response) => {
16136
+ const requestedPath = safePath(request.url);
16137
+
16138
+ if (requestedPath === null) {
16139
+ response.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
16140
+ response.end('Forbidden');
16141
+ return;
16142
+ }
16143
+
16144
+ try {
16145
+ const fileStat = await stat(requestedPath);
16146
+ const filePath = fileStat.isFile() ? requestedPath : join(root, 'index.html');
16147
+ const contentType = contentTypes.get(extname(filePath)) ?? 'application/octet-stream';
16148
+
16149
+ response.writeHead(200, { 'content-type': contentType });
16150
+ createReadStream(filePath).pipe(response);
16151
+ } catch {
16152
+ response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
16153
+ createReadStream(join(root, 'index.html')).pipe(response);
16154
+ }
16155
+ });
16156
+
16157
+ server.on('error', (error) => {
16158
+ console.error(error instanceof Error ? error.message : String(error));
16159
+ process.exitCode = 1;
16160
+ });
16161
+
16162
+ await new Promise((resolveListen, rejectListen) => {
16163
+ server.once('error', rejectListen);
16164
+ server.listen(port, '127.0.0.1', resolveListen);
16165
+ });
16166
+
16167
+ process.stdin.resume();
16168
+ `;
16169
+ }
16019
16170
  async function ensureDaemonNotRunning(options) {
16020
16171
  const pathOptions = toPathOptions2(options);
16021
16172
  await cleanupStaleDaemonFiles(pathOptions);
@@ -16049,7 +16200,10 @@ function createCliRuntime(dependencies = {}) {
16049
16200
  if (child.pid === void 0) {
16050
16201
  throw new Error("Failed to obtain the AISnitch daemon PID.");
16051
16202
  }
16052
- return await waitForDaemonReady(daemonPathOptions);
16203
+ return await waitForDaemonReady(
16204
+ daemonPathOptions,
16205
+ options.dashboardPort ?? DEFAULT_DASHBOARD_PORT
16206
+ );
16053
16207
  }
16054
16208
  async function stopDetachedDaemon(options) {
16055
16209
  const pathOptions = toPathOptions2(options);
@@ -16080,6 +16234,7 @@ function createCliRuntime(dependencies = {}) {
16080
16234
  consumerCount: snapshot.health?.consumers ?? 0,
16081
16235
  daemon: {
16082
16236
  active: snapshot.running,
16237
+ dashboardUrl: `http://127.0.0.1:${snapshot.dashboardPort}`,
16083
16238
  httpUrl: `http://127.0.0.1:${snapshot.httpPort}/health`,
16084
16239
  pid: snapshot.daemonPid,
16085
16240
  socketPath: snapshot.socketPath,
@@ -16120,7 +16275,7 @@ function createCliRuntime(dependencies = {}) {
16120
16275
  await rm4(backupPath, { force: true });
16121
16276
  await rename(logFilePath, backupPath);
16122
16277
  }
16123
- async function waitForDaemonReady(pathOptions) {
16278
+ async function waitForDaemonReady(pathOptions, dashboardPort = DEFAULT_DASHBOARD_PORT) {
16124
16279
  const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
16125
16280
  while (Date.now() < deadline) {
16126
16281
  const daemonState = await readDaemonState(pathOptions);
@@ -16129,6 +16284,7 @@ function createCliRuntime(dependencies = {}) {
16129
16284
  if (health !== null) {
16130
16285
  return {
16131
16286
  configuredAdapters: [],
16287
+ dashboardPort,
16132
16288
  daemonPid: daemonState.pid,
16133
16289
  health,
16134
16290
  httpPort: daemonState.httpPort,
@@ -16229,6 +16385,7 @@ function createCliRuntime(dependencies = {}) {
16229
16385
  ...pathOptions
16230
16386
  });
16231
16387
  startMockEmitter(pipeline, options);
16388
+ const dashboardServer = daemonMode ? await startDashboardServerProcess(config.dashboardPort) : null;
16232
16389
  if (daemonMode) {
16233
16390
  const daemonPathOptions = toPathOptions2(options);
16234
16391
  await writePid(process.pid, daemonPathOptions);
@@ -16265,6 +16422,7 @@ function createCliRuntime(dependencies = {}) {
16265
16422
  wsServer: pipeline.getWsServer(),
16266
16423
  eventBus: pipeline.getEventBus(),
16267
16424
  cleanupFns: daemonMode ? [async () => {
16425
+ dashboardServer?.kill();
16268
16426
  await Promise.all([
16269
16427
  removePid(toPathOptions2(options)),
16270
16428
  removeDaemonState(toPathOptions2(options))
@@ -16341,16 +16499,16 @@ function createCliRuntime(dependencies = {}) {
16341
16499
  const snapshot = await startDetachedDaemon(options);
16342
16500
  output.stdout(
16343
16501
  `AISnitch daemon started (PID: ${snapshot.daemonPid ?? "unknown"}) on ws://${"127.0.0.1"}:${snapshot.wsPort}
16502
+ Dashboard: http://127.0.0.1:${snapshot.dashboardPort}
16344
16503
  `
16345
16504
  );
16346
16505
  return;
16347
16506
  }
16348
16507
  let initialSnapshot = await getStatusSnapshot(options);
16508
+ if (!initialSnapshot.running) {
16509
+ initialSnapshot = await startDetachedDaemon(options);
16510
+ }
16349
16511
  if (options.mock) {
16350
- if (!initialSnapshot.running) {
16351
- await startDetachedDaemon(options);
16352
- initialSnapshot = await getStatusSnapshot(options);
16353
- }
16354
16512
  output.stdout(
16355
16513
  `Starting mock scenario ${options.mock} (${options.mockSpeed ?? 1}x, ${options.mockDuration ?? 60}s${options.mockLoop ? ", loop" : ""}).
16356
16514
  `
@@ -16410,6 +16568,8 @@ function createCliRuntime(dependencies = {}) {
16410
16568
  output.stdout(`WebSocket port: ${snapshot.wsPort}
16411
16569
  `);
16412
16570
  output.stdout(`HTTP port: ${snapshot.httpPort}
16571
+ `);
16572
+ output.stdout(`Dashboard: http://127.0.0.1:${snapshot.dashboardPort}
16413
16573
  `);
16414
16574
  output.stdout(`Socket path: ${snapshot.socketPath ?? "none"}
16415
16575
  `);
@@ -16466,169 +16626,49 @@ function createCliRuntime(dependencies = {}) {
16466
16626
  });
16467
16627
  }
16468
16628
  async function fullscreen(options) {
16469
- const snapshot = await getStatusSnapshot(options);
16470
- if (!snapshot.running && options.daemon) {
16629
+ let snapshot = await getStatusSnapshot(options);
16630
+ if (!snapshot.running) {
16471
16631
  output.stdout("Starting daemon...\n");
16472
- await startDetachedDaemon(options);
16473
- }
16474
- if (!snapshot.running && !options.daemon) {
16475
- throw new Error(
16476
- "AISnitch daemon is not running. Start one with `aisnitch start --daemon` or use `aisnitch fs --daemon` to start and open the dashboard."
16477
- );
16478
- }
16479
- for (let i = 0; i < 20; i++) {
16480
- const health = await fetchHealth(snapshot.httpPort);
16481
- if (health) break;
16482
- await new Promise((resolve2) => setTimeout(resolve2, 200).unref());
16632
+ snapshot = await startDetachedDaemon({
16633
+ ...options,
16634
+ dashboardPort: options.dashboardPort
16635
+ });
16483
16636
  }
16484
- const dashboardPort = options.dashboardPort ?? 5174;
16637
+ const dashboardPort = options.dashboardPort ?? snapshot.dashboardPort;
16485
16638
  const dashboardUrl = `http://127.0.0.1:${dashboardPort}`;
16486
- const distPath = await resolveDashboardDistPath();
16487
- output.stdout(`Starting dashboard server on port ${dashboardPort}...
16488
- `);
16489
- const nodeExecutable = await resolveNodeExecutable();
16490
- const viteProcess = spawnImplementation(nodeExecutable, [
16491
- "-e",
16492
- `
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';
16497
-
16498
- const distPath = ${JSON.stringify(distPath)};
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
- ]);
16509
-
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
- }
16546
- });
16547
-
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
- });
16557
-
16558
- process.stdin.resume();
16639
+ if (!await waitForDashboardReady(dashboardPort)) {
16640
+ output.stdout(
16641
+ `Dashboard not reachable at ${dashboardUrl}, starting a standalone server...
16559
16642
  `
16560
- ], {
16561
- cwd: distPath,
16562
- stdio: ["pipe", "pipe", "pipe"]
16563
- });
16564
- let serverOutput = "";
16565
- let serverSpawnError;
16566
- viteProcess.on("error", (error) => {
16567
- serverSpawnError = error;
16568
- serverOutput += `Dashboard server process failed: ${formatSpawnError(error)}
16569
- `;
16570
- });
16571
- viteProcess.stdout?.on("data", (data) => {
16572
- serverOutput += data.toString();
16573
- });
16574
- viteProcess.stderr?.on("data", (data) => {
16575
- serverOutput += data.toString();
16576
- });
16577
- let serverReady = false;
16578
- for (let i = 0; i < 100; i++) {
16579
- await new Promise((resolve2) => setTimeout(resolve2, 100).unref());
16643
+ );
16580
16644
  try {
16581
- const response = await fetchImplementation(dashboardUrl);
16582
- if (response.ok) {
16583
- serverReady = true;
16584
- break;
16585
- }
16586
- } catch {
16587
- }
16588
- if (serverSpawnError !== void 0) {
16589
- break;
16645
+ await startDashboardServerProcess(dashboardPort);
16646
+ } catch (error) {
16647
+ throw new Error(
16648
+ `Dashboard did not become reachable at ${dashboardUrl}: ${error instanceof Error ? error.message : "unknown error"}`,
16649
+ { cause: error }
16650
+ );
16590
16651
  }
16591
- if (!viteProcess.pid) break;
16592
- }
16593
- if (!serverReady) {
16594
- output.stdout(`Server output: ${serverOutput}
16595
- `);
16596
- if (serverSpawnError !== void 0) {
16652
+ if (!await waitForDashboardReady(dashboardPort)) {
16597
16653
  throw new Error(
16598
- `Failed to start dashboard server process with ${nodeExecutable}: ${formatSpawnError(serverSpawnError)}`
16654
+ `Dashboard did not become reachable at ${dashboardUrl}.`
16599
16655
  );
16600
16656
  }
16601
- throw new Error("Failed to start dashboard server");
16602
16657
  }
16603
16658
  output.stdout(`Dashboard ready at ${dashboardUrl}
16604
16659
  `);
16605
16660
  if (!options.noBrowser) {
16606
16661
  output.stdout("Opening browser...\n");
16607
- const openModule = await openPromise;
16608
- await openModule.default(dashboardUrl);
16662
+ try {
16663
+ const openModule = await openPromise;
16664
+ await openModule.default(dashboardUrl);
16665
+ } catch (error) {
16666
+ output.stderr(
16667
+ `Failed to open browser automatically (${error instanceof Error ? error.message : "unknown error"}). Open ${dashboardUrl} manually.
16668
+ `
16669
+ );
16670
+ }
16609
16671
  }
16610
- output.stdout("Press Ctrl+C to stop\n");
16611
- await new Promise((resolve2) => {
16612
- const shutdown = () => {
16613
- process.off("SIGINT", handleSigint);
16614
- process.off("SIGTERM", handleSigterm);
16615
- if (viteProcess.pid !== void 0) {
16616
- try {
16617
- process.kill(viteProcess.pid, "SIGTERM");
16618
- } catch {
16619
- }
16620
- }
16621
- resolve2();
16622
- };
16623
- const handleSigint = () => {
16624
- shutdown();
16625
- };
16626
- const handleSigterm = () => {
16627
- shutdown();
16628
- };
16629
- process.once("SIGINT", handleSigint);
16630
- process.once("SIGTERM", handleSigterm);
16631
- });
16632
16672
  }
16633
16673
  async function logger2(options) {
16634
16674
  const snapshot = await getStatusSnapshot(options);
@@ -16895,6 +16935,7 @@ process.stdin.resume();
16895
16935
  const health = running && daemonState !== null ? await fetchHealth(daemonState.httpPort) : null;
16896
16936
  return {
16897
16937
  configuredAdapters: getEnabledAdapters(config),
16938
+ dashboardPort: config.dashboardPort ?? DEFAULT_DASHBOARD_PORT,
16898
16939
  daemonPid,
16899
16940
  health,
16900
16941
  httpPort: daemonState?.httpPort ?? config.httpPort,
@@ -17129,13 +17170,13 @@ function createProgram(dependencies = {}) {
17129
17170
  "after",
17130
17171
  `
17131
17172
  Examples:
17132
- aisnitch start
17173
+ aisnitch (default: starts daemon + dashboard server, opens browser)
17174
+ aisnitch fs --dashboard-port 8080
17175
+ aisnitch fs --no-browser
17176
+ aisnitch start (TUI dashboard)
17133
17177
  aisnitch start --daemon
17134
17178
  aisnitch start --view full-data
17135
17179
  aisnitch start --mock
17136
- aisnitch fs
17137
- aisnitch fs --daemon
17138
- aisnitch fs --dashboard-port 8080
17139
17180
  aisnitch status
17140
17181
  aisnitch attach
17141
17182
  aisnitch attach --view full-data
@@ -17178,7 +17219,7 @@ function addCommonOptions(command) {
17178
17219
  }
17179
17220
  function addStartCommand(program, runtime) {
17180
17221
  addCommonOptions(
17181
- program.command("start", { isDefault: true }).description("Open the AISnitch dashboard and manage the daemon").option("--daemon", "Run AISnitch as a detached daemon").option(
17222
+ program.command("start").description("Open the AISnitch TUI dashboard and manage the daemon").option("--daemon", "Run AISnitch as a detached daemon").option(
17182
17223
  "--mock [tool]",
17183
17224
  'Inject deterministic mock events (defaults to "all" when no tool is specified)',
17184
17225
  wrapOptionParser(parseMockToolSelection)
@@ -17210,6 +17251,10 @@ function addStartCommand(program, runtime) {
17210
17251
  "--http-port <port>",
17211
17252
  "Override the HTTP hook port",
17212
17253
  wrapOptionParser(parsePortOption)
17254
+ ).option(
17255
+ "--dashboard-port <port>",
17256
+ "Override the web dashboard port",
17257
+ wrapOptionParser(parsePortOption)
17213
17258
  ).option(
17214
17259
  "--log-level <level>",
17215
17260
  "Override the runtime log level",
@@ -17294,9 +17339,9 @@ function addAttachCommand(program, runtime) {
17294
17339
  }
17295
17340
  function addFullscreenCommand(program, runtime) {
17296
17341
  addCommonOptions(
17297
- program.command("fs").description("Open the fullscreen web dashboard in browser (starts daemon if needed with --daemon)").alias("fullscreen").option(
17342
+ program.command("fs", { isDefault: true }).description("Start the daemon, the dashboard server, and open the web dashboard in browser").alias("fullscreen").option(
17298
17343
  "--daemon",
17299
- "Start the daemon automatically if not running"
17344
+ "Deprecated: daemon is now started automatically when needed"
17300
17345
  ).option(
17301
17346
  "--dashboard-port <port>",
17302
17347
  "Port for the dashboard server (default: 5174)",