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/README.md +14 -8
- package/dist/cli/index.cjs +208 -163
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +208 -163
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +8 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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.
|
|
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(
|
|
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
|
-
|
|
16470
|
-
if (!snapshot.running
|
|
16629
|
+
let snapshot = await getStatusSnapshot(options);
|
|
16630
|
+
if (!snapshot.running) {
|
|
16471
16631
|
output.stdout("Starting daemon...\n");
|
|
16472
|
-
await startDetachedDaemon(
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
|
|
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 ??
|
|
16637
|
+
const dashboardPort = options.dashboardPort ?? snapshot.dashboardPort;
|
|
16485
16638
|
const dashboardUrl = `http://127.0.0.1:${dashboardPort}`;
|
|
16486
|
-
|
|
16487
|
-
|
|
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
|
-
|
|
16582
|
-
|
|
16583
|
-
|
|
16584
|
-
|
|
16585
|
-
|
|
16586
|
-
|
|
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 (!
|
|
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
|
-
`
|
|
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
|
-
|
|
16608
|
-
|
|
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
|
|
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"
|
|
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("
|
|
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
|
-
"
|
|
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)",
|