aisnitch 0.2.24 → 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 +242 -120
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +244 -122
- 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/examples/fullscreen-dashboard/README.md +72 -0
- package/examples/fullscreen-dashboard/dist/assets/index-8iYVmJ7n.js +65 -0
- package/examples/fullscreen-dashboard/dist/index.html +16 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -82,10 +82,16 @@ brew install aisnitch
|
|
|
82
82
|
### 2. Run
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
aisnitch
|
|
85
|
+
aisnitch
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
That's it!
|
|
88
|
+
That's it! `aisnitch` (no argument) starts the daemon, launches the dashboard server in the background, and opens the web dashboard in your browser.
|
|
89
|
+
|
|
90
|
+
Want the in-terminal TUI instead?
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
aisnitch start
|
|
94
|
+
```
|
|
89
95
|
|
|
90
96
|
**No AI tools yet?** Try the demo mode:
|
|
91
97
|
|
|
@@ -222,8 +228,8 @@ brew upgrade aisnitch
|
|
|
222
228
|
Open a beautiful real-time dashboard in your browser:
|
|
223
229
|
|
|
224
230
|
```bash
|
|
225
|
-
aisnitch
|
|
226
|
-
aisnitch fs
|
|
231
|
+
aisnitch # Default: start daemon + dashboard + open browser
|
|
232
|
+
aisnitch fs # Same as `aisnitch`
|
|
227
233
|
aisnitch fs --dashboard-port 8080 # Custom port
|
|
228
234
|
aisnitch fs --no-browser # Just start the server
|
|
229
235
|
```
|
|
@@ -336,12 +342,12 @@ aisnitch start --type agent.coding # Filter by event type
|
|
|
336
342
|
aisnitch start --view full-data # Show full JSON
|
|
337
343
|
```
|
|
338
344
|
|
|
339
|
-
### Web Dashboard
|
|
345
|
+
### Web Dashboard (Default)
|
|
340
346
|
|
|
341
347
|
```bash
|
|
342
|
-
aisnitch
|
|
343
|
-
aisnitch fs
|
|
344
|
-
aisnitch fs --dashboard-port 8080
|
|
348
|
+
aisnitch # Default: daemon + dashboard + browser
|
|
349
|
+
aisnitch fs # Explicit form (same effect)
|
|
350
|
+
aisnitch fs --dashboard-port 8080 # Custom port
|
|
345
351
|
aisnitch fs --no-browser # Server only
|
|
346
352
|
```
|
|
347
353
|
|
package/dist/cli/index.cjs
CHANGED
|
@@ -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.
|
|
774
|
+
var AISNITCH_VERSION = "0.2.26";
|
|
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
|
|
@@ -1360,6 +1360,7 @@ var AutoUpdateConfigSchema = import_zod2.z.strictObject({
|
|
|
1360
1360
|
var ConfigSchema = import_zod2.z.strictObject({
|
|
1361
1361
|
wsPort: import_zod2.z.number().int().min(1024).max(65535).default(4820),
|
|
1362
1362
|
httpPort: import_zod2.z.number().int().min(1024).max(65535).default(4821),
|
|
1363
|
+
dashboardPort: import_zod2.z.number().int().min(1024).max(65535).default(5174),
|
|
1363
1364
|
/**
|
|
1364
1365
|
* 📖 This is intentionally a partial record because most users will only
|
|
1365
1366
|
* override a couple of adapters instead of all supported tools at once.
|
|
@@ -1378,6 +1379,7 @@ var ConfigSchema = import_zod2.z.strictObject({
|
|
|
1378
1379
|
var DEFAULT_CONFIG = {
|
|
1379
1380
|
wsPort: 4820,
|
|
1380
1381
|
httpPort: 4821,
|
|
1382
|
+
dashboardPort: 5174,
|
|
1381
1383
|
adapters: {},
|
|
1382
1384
|
autoUpdate: {
|
|
1383
1385
|
enabled: true,
|
|
@@ -14168,6 +14170,10 @@ function Header({
|
|
|
14168
14170
|
] }) }),
|
|
14169
14171
|
daemon ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
14170
14172
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink6.Text, { color: connected ? TUI_THEME.success : TUI_THEME.warning, children: connected ? `\u25CF ${connectionLabel}` : `\u25CB ${connectionLabel}` }),
|
|
14173
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink6.Text, { color: TUI_THEME.success, children: [
|
|
14174
|
+
"Web ",
|
|
14175
|
+
daemon.dashboardUrl
|
|
14176
|
+
] }),
|
|
14171
14177
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink6.Text, { color: TUI_THEME.muted, children: [
|
|
14172
14178
|
"WS ",
|
|
14173
14179
|
daemon.wsUrl
|
|
@@ -14360,7 +14366,7 @@ function StatusBar({
|
|
|
14360
14366
|
}) {
|
|
14361
14367
|
const streamState = streamFrozen ? `Frozen +${pendingEventCount}` : latestEvent?.type ?? "Live";
|
|
14362
14368
|
const focusLabel = focusPanel === "events" ? "events" : viewMode === "full-data" ? "inspector" : "sessions";
|
|
14363
|
-
const daemonLabel = daemon === void 0 ? null : daemon.busyAction ? `Daemon ${daemon.busyAction}` : daemon.active ? `Daemon active \xB7 ${daemon.
|
|
14369
|
+
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}`;
|
|
14364
14370
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
14365
14371
|
import_ink10.Box,
|
|
14366
14372
|
{
|
|
@@ -15945,6 +15951,7 @@ var DAEMON_READY_TIMEOUT_MS = 4e3;
|
|
|
15945
15951
|
var DAEMON_READY_POLL_INTERVAL_MS = 100;
|
|
15946
15952
|
var DAEMON_STOP_TIMEOUT_MS = 4e3;
|
|
15947
15953
|
var DAEMON_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
15954
|
+
var DEFAULT_DASHBOARD_PORT = 5174;
|
|
15948
15955
|
var LAUNCH_AGENT_LABEL = "com.aisnitch.daemon";
|
|
15949
15956
|
async function resolveNodeExecutable() {
|
|
15950
15957
|
try {
|
|
@@ -15960,6 +15967,40 @@ function formatSpawnError(error) {
|
|
|
15960
15967
|
}
|
|
15961
15968
|
return String(error);
|
|
15962
15969
|
}
|
|
15970
|
+
async function pathExists(path2) {
|
|
15971
|
+
try {
|
|
15972
|
+
await (0, import_promises22.access)(path2, import_node_fs7.constants.R_OK);
|
|
15973
|
+
return true;
|
|
15974
|
+
} catch {
|
|
15975
|
+
return false;
|
|
15976
|
+
}
|
|
15977
|
+
}
|
|
15978
|
+
async function resolveDashboardDistPath() {
|
|
15979
|
+
if (process.env.AISNITCH_DASHBOARD_DIST) {
|
|
15980
|
+
const overridePath = process.env.AISNITCH_DASHBOARD_DIST;
|
|
15981
|
+
if (await pathExists((0, import_node_path22.join)(overridePath, "index.html"))) {
|
|
15982
|
+
return overridePath;
|
|
15983
|
+
}
|
|
15984
|
+
throw new Error(
|
|
15985
|
+
`Fullscreen dashboard assets are missing at ${overridePath}. Reinstall AISnitch or run \`pnpm --filter aisnitch-fullscreen-dashboard build\` from the repository checkout.`
|
|
15986
|
+
);
|
|
15987
|
+
}
|
|
15988
|
+
const cliEntryPath = process.argv[1] ? await (0, import_promises22.realpath)(process.argv[1]).catch(() => process.argv[1] ?? "") : "";
|
|
15989
|
+
const moduleDirectory = (0, import_node_path22.dirname)(cliEntryPath);
|
|
15990
|
+
const packageRoot = (0, import_node_path22.dirname)((0, import_node_path22.dirname)(moduleDirectory));
|
|
15991
|
+
const candidates = [
|
|
15992
|
+
(0, import_node_path22.join)(packageRoot, "examples", "fullscreen-dashboard", "dist"),
|
|
15993
|
+
(0, import_node_path22.join)(process.cwd(), "examples", "fullscreen-dashboard", "dist")
|
|
15994
|
+
];
|
|
15995
|
+
for (const candidate of candidates) {
|
|
15996
|
+
if (await pathExists((0, import_node_path22.join)(candidate, "index.html"))) {
|
|
15997
|
+
return candidate;
|
|
15998
|
+
}
|
|
15999
|
+
}
|
|
16000
|
+
throw new Error(
|
|
16001
|
+
"Fullscreen dashboard assets are missing. Reinstall AISnitch or run `pnpm --filter aisnitch-fullscreen-dashboard build` from the repository checkout."
|
|
16002
|
+
);
|
|
16003
|
+
}
|
|
15963
16004
|
function createCliRuntime(dependencies = {}) {
|
|
15964
16005
|
const output = dependencies.output ?? createProcessOutput();
|
|
15965
16006
|
const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
|
|
@@ -15985,6 +16026,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
15985
16026
|
const config = ConfigSchema.parse({
|
|
15986
16027
|
...baseConfig,
|
|
15987
16028
|
...isStartCliOptions(options) ? {
|
|
16029
|
+
dashboardPort: options.dashboardPort ?? baseConfig.dashboardPort,
|
|
15988
16030
|
httpPort: options.httpPort ?? baseConfig.httpPort,
|
|
15989
16031
|
logLevel: options.logLevel ?? baseConfig.logLevel,
|
|
15990
16032
|
wsPort: options.wsPort ?? baseConfig.wsPort
|
|
@@ -16008,6 +16050,149 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16008
16050
|
return null;
|
|
16009
16051
|
}
|
|
16010
16052
|
}
|
|
16053
|
+
async function fetchOk(url) {
|
|
16054
|
+
try {
|
|
16055
|
+
const response = await fetchImplementation(url);
|
|
16056
|
+
return response.ok;
|
|
16057
|
+
} catch {
|
|
16058
|
+
return false;
|
|
16059
|
+
}
|
|
16060
|
+
}
|
|
16061
|
+
async function waitForDashboardReady(port) {
|
|
16062
|
+
const dashboardUrl = `http://127.0.0.1:${port}`;
|
|
16063
|
+
for (let i = 0; i < 100; i++) {
|
|
16064
|
+
if (await fetchOk(dashboardUrl)) {
|
|
16065
|
+
return true;
|
|
16066
|
+
}
|
|
16067
|
+
await sleep(100);
|
|
16068
|
+
}
|
|
16069
|
+
return false;
|
|
16070
|
+
}
|
|
16071
|
+
async function startDashboardServerProcess(port) {
|
|
16072
|
+
const dashboardUrl = `http://127.0.0.1:${port}`;
|
|
16073
|
+
if (await fetchOk(dashboardUrl)) {
|
|
16074
|
+
return {
|
|
16075
|
+
kill: () => void 0,
|
|
16076
|
+
pid: void 0
|
|
16077
|
+
};
|
|
16078
|
+
}
|
|
16079
|
+
const distPath = await resolveDashboardDistPath();
|
|
16080
|
+
const nodeExecutable = await resolveNodeExecutable();
|
|
16081
|
+
const serverProcess = spawnImplementation(nodeExecutable, [
|
|
16082
|
+
"-e",
|
|
16083
|
+
buildDashboardServerScript(distPath, port)
|
|
16084
|
+
], {
|
|
16085
|
+
cwd: distPath,
|
|
16086
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16087
|
+
});
|
|
16088
|
+
let serverOutput = "";
|
|
16089
|
+
let serverSpawnError;
|
|
16090
|
+
serverProcess.on("error", (error) => {
|
|
16091
|
+
serverSpawnError = error;
|
|
16092
|
+
serverOutput += `Dashboard server process failed: ${formatSpawnError(error)}
|
|
16093
|
+
`;
|
|
16094
|
+
});
|
|
16095
|
+
serverProcess.stdout?.on("data", (data) => {
|
|
16096
|
+
serverOutput += data.toString();
|
|
16097
|
+
});
|
|
16098
|
+
serverProcess.stderr?.on("data", (data) => {
|
|
16099
|
+
serverOutput += data.toString();
|
|
16100
|
+
});
|
|
16101
|
+
for (let i = 0; i < 100; i++) {
|
|
16102
|
+
if (await fetchOk(dashboardUrl)) {
|
|
16103
|
+
return {
|
|
16104
|
+
kill: () => {
|
|
16105
|
+
if (serverProcess.pid !== void 0) {
|
|
16106
|
+
try {
|
|
16107
|
+
process.kill(serverProcess.pid, "SIGTERM");
|
|
16108
|
+
} catch {
|
|
16109
|
+
}
|
|
16110
|
+
}
|
|
16111
|
+
},
|
|
16112
|
+
pid: serverProcess.pid
|
|
16113
|
+
};
|
|
16114
|
+
}
|
|
16115
|
+
if (serverSpawnError !== void 0) {
|
|
16116
|
+
throw new Error(
|
|
16117
|
+
`Failed to start dashboard server process with ${nodeExecutable}: ${formatSpawnError(serverSpawnError)}`
|
|
16118
|
+
);
|
|
16119
|
+
}
|
|
16120
|
+
await sleep(100);
|
|
16121
|
+
}
|
|
16122
|
+
throw new Error(
|
|
16123
|
+
`Failed to start dashboard server at ${dashboardUrl}. Server output: ${serverOutput}`
|
|
16124
|
+
);
|
|
16125
|
+
}
|
|
16126
|
+
function buildDashboardServerScript(distPath, port) {
|
|
16127
|
+
return `
|
|
16128
|
+
import { createReadStream } from 'node:fs';
|
|
16129
|
+
import { stat } from 'node:fs/promises';
|
|
16130
|
+
import { createServer } from 'node:http';
|
|
16131
|
+
import { extname, join, normalize, resolve } from 'node:path';
|
|
16132
|
+
|
|
16133
|
+
const distPath = ${JSON.stringify(distPath)};
|
|
16134
|
+
const port = ${port};
|
|
16135
|
+
const root = resolve(distPath);
|
|
16136
|
+
const contentTypes = new Map([
|
|
16137
|
+
['.css', 'text/css; charset=utf-8'],
|
|
16138
|
+
['.html', 'text/html; charset=utf-8'],
|
|
16139
|
+
['.js', 'text/javascript; charset=utf-8'],
|
|
16140
|
+
['.json', 'application/json; charset=utf-8'],
|
|
16141
|
+
['.map', 'application/json; charset=utf-8'],
|
|
16142
|
+
['.svg', 'image/svg+xml'],
|
|
16143
|
+
]);
|
|
16144
|
+
|
|
16145
|
+
function safePath(url) {
|
|
16146
|
+
const parsed = new URL(url ?? '/', 'http://127.0.0.1');
|
|
16147
|
+
const pathname = parsed.pathname === '/' ? '/index.html' : parsed.pathname;
|
|
16148
|
+
const decoded = decodeURIComponent(pathname);
|
|
16149
|
+
const normalized = normalize(decoded).replace(/^[/\\]+/, '');
|
|
16150
|
+
const absolute = resolve(join(root, normalized));
|
|
16151
|
+
|
|
16152
|
+
if (absolute !== root && !absolute.startsWith(root + '/')) {
|
|
16153
|
+
return null;
|
|
16154
|
+
}
|
|
16155
|
+
|
|
16156
|
+
return absolute;
|
|
16157
|
+
}
|
|
16158
|
+
|
|
16159
|
+
await stat(join(root, 'index.html'));
|
|
16160
|
+
|
|
16161
|
+
const server = createServer(async (request, response) => {
|
|
16162
|
+
const requestedPath = safePath(request.url);
|
|
16163
|
+
|
|
16164
|
+
if (requestedPath === null) {
|
|
16165
|
+
response.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
|
|
16166
|
+
response.end('Forbidden');
|
|
16167
|
+
return;
|
|
16168
|
+
}
|
|
16169
|
+
|
|
16170
|
+
try {
|
|
16171
|
+
const fileStat = await stat(requestedPath);
|
|
16172
|
+
const filePath = fileStat.isFile() ? requestedPath : join(root, 'index.html');
|
|
16173
|
+
const contentType = contentTypes.get(extname(filePath)) ?? 'application/octet-stream';
|
|
16174
|
+
|
|
16175
|
+
response.writeHead(200, { 'content-type': contentType });
|
|
16176
|
+
createReadStream(filePath).pipe(response);
|
|
16177
|
+
} catch {
|
|
16178
|
+
response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
16179
|
+
createReadStream(join(root, 'index.html')).pipe(response);
|
|
16180
|
+
}
|
|
16181
|
+
});
|
|
16182
|
+
|
|
16183
|
+
server.on('error', (error) => {
|
|
16184
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16185
|
+
process.exitCode = 1;
|
|
16186
|
+
});
|
|
16187
|
+
|
|
16188
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
16189
|
+
server.once('error', rejectListen);
|
|
16190
|
+
server.listen(port, '127.0.0.1', resolveListen);
|
|
16191
|
+
});
|
|
16192
|
+
|
|
16193
|
+
process.stdin.resume();
|
|
16194
|
+
`;
|
|
16195
|
+
}
|
|
16011
16196
|
async function ensureDaemonNotRunning(options) {
|
|
16012
16197
|
const pathOptions = toPathOptions2(options);
|
|
16013
16198
|
await cleanupStaleDaemonFiles(pathOptions);
|
|
@@ -16041,7 +16226,10 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16041
16226
|
if (child.pid === void 0) {
|
|
16042
16227
|
throw new Error("Failed to obtain the AISnitch daemon PID.");
|
|
16043
16228
|
}
|
|
16044
|
-
return await waitForDaemonReady(
|
|
16229
|
+
return await waitForDaemonReady(
|
|
16230
|
+
daemonPathOptions,
|
|
16231
|
+
options.dashboardPort ?? DEFAULT_DASHBOARD_PORT
|
|
16232
|
+
);
|
|
16045
16233
|
}
|
|
16046
16234
|
async function stopDetachedDaemon(options) {
|
|
16047
16235
|
const pathOptions = toPathOptions2(options);
|
|
@@ -16072,6 +16260,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16072
16260
|
consumerCount: snapshot.health?.consumers ?? 0,
|
|
16073
16261
|
daemon: {
|
|
16074
16262
|
active: snapshot.running,
|
|
16263
|
+
dashboardUrl: `http://127.0.0.1:${snapshot.dashboardPort}`,
|
|
16075
16264
|
httpUrl: `http://127.0.0.1:${snapshot.httpPort}/health`,
|
|
16076
16265
|
pid: snapshot.daemonPid,
|
|
16077
16266
|
socketPath: snapshot.socketPath,
|
|
@@ -16112,7 +16301,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16112
16301
|
await (0, import_promises22.rm)(backupPath, { force: true });
|
|
16113
16302
|
await (0, import_promises22.rename)(logFilePath, backupPath);
|
|
16114
16303
|
}
|
|
16115
|
-
async function waitForDaemonReady(pathOptions) {
|
|
16304
|
+
async function waitForDaemonReady(pathOptions, dashboardPort = DEFAULT_DASHBOARD_PORT) {
|
|
16116
16305
|
const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
|
|
16117
16306
|
while (Date.now() < deadline) {
|
|
16118
16307
|
const daemonState = await readDaemonState(pathOptions);
|
|
@@ -16121,6 +16310,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16121
16310
|
if (health !== null) {
|
|
16122
16311
|
return {
|
|
16123
16312
|
configuredAdapters: [],
|
|
16313
|
+
dashboardPort,
|
|
16124
16314
|
daemonPid: daemonState.pid,
|
|
16125
16315
|
health,
|
|
16126
16316
|
httpPort: daemonState.httpPort,
|
|
@@ -16221,6 +16411,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16221
16411
|
...pathOptions
|
|
16222
16412
|
});
|
|
16223
16413
|
startMockEmitter(pipeline, options);
|
|
16414
|
+
const dashboardServer = daemonMode ? await startDashboardServerProcess(config.dashboardPort) : null;
|
|
16224
16415
|
if (daemonMode) {
|
|
16225
16416
|
const daemonPathOptions = toPathOptions2(options);
|
|
16226
16417
|
await writePid(process.pid, daemonPathOptions);
|
|
@@ -16257,6 +16448,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16257
16448
|
wsServer: pipeline.getWsServer(),
|
|
16258
16449
|
eventBus: pipeline.getEventBus(),
|
|
16259
16450
|
cleanupFns: daemonMode ? [async () => {
|
|
16451
|
+
dashboardServer?.kill();
|
|
16260
16452
|
await Promise.all([
|
|
16261
16453
|
removePid(toPathOptions2(options)),
|
|
16262
16454
|
removeDaemonState(toPathOptions2(options))
|
|
@@ -16333,16 +16525,16 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16333
16525
|
const snapshot = await startDetachedDaemon(options);
|
|
16334
16526
|
output.stdout(
|
|
16335
16527
|
`AISnitch daemon started (PID: ${snapshot.daemonPid ?? "unknown"}) on ws://${"127.0.0.1"}:${snapshot.wsPort}
|
|
16528
|
+
Dashboard: http://127.0.0.1:${snapshot.dashboardPort}
|
|
16336
16529
|
`
|
|
16337
16530
|
);
|
|
16338
16531
|
return;
|
|
16339
16532
|
}
|
|
16340
16533
|
let initialSnapshot = await getStatusSnapshot(options);
|
|
16534
|
+
if (!initialSnapshot.running) {
|
|
16535
|
+
initialSnapshot = await startDetachedDaemon(options);
|
|
16536
|
+
}
|
|
16341
16537
|
if (options.mock) {
|
|
16342
|
-
if (!initialSnapshot.running) {
|
|
16343
|
-
await startDetachedDaemon(options);
|
|
16344
|
-
initialSnapshot = await getStatusSnapshot(options);
|
|
16345
|
-
}
|
|
16346
16538
|
output.stdout(
|
|
16347
16539
|
`Starting mock scenario ${options.mock} (${options.mockSpeed ?? 1}x, ${options.mockDuration ?? 60}s${options.mockLoop ? ", loop" : ""}).
|
|
16348
16540
|
`
|
|
@@ -16402,6 +16594,8 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16402
16594
|
output.stdout(`WebSocket port: ${snapshot.wsPort}
|
|
16403
16595
|
`);
|
|
16404
16596
|
output.stdout(`HTTP port: ${snapshot.httpPort}
|
|
16597
|
+
`);
|
|
16598
|
+
output.stdout(`Dashboard: http://127.0.0.1:${snapshot.dashboardPort}
|
|
16405
16599
|
`);
|
|
16406
16600
|
output.stdout(`Socket path: ${snapshot.socketPath ?? "none"}
|
|
16407
16601
|
`);
|
|
@@ -16458,126 +16652,49 @@ function createCliRuntime(dependencies = {}) {
|
|
|
16458
16652
|
});
|
|
16459
16653
|
}
|
|
16460
16654
|
async function fullscreen(options) {
|
|
16461
|
-
|
|
16462
|
-
if (!snapshot.running
|
|
16655
|
+
let snapshot = await getStatusSnapshot(options);
|
|
16656
|
+
if (!snapshot.running) {
|
|
16463
16657
|
output.stdout("Starting daemon...\n");
|
|
16464
|
-
await startDetachedDaemon(
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
"AISnitch daemon is not running. Start one with `aisnitch start --daemon` or use `aisnitch fs --daemon` to start and open the dashboard."
|
|
16469
|
-
);
|
|
16470
|
-
}
|
|
16471
|
-
for (let i = 0; i < 20; i++) {
|
|
16472
|
-
const health = await fetchHealth(snapshot.httpPort);
|
|
16473
|
-
if (health) break;
|
|
16474
|
-
await new Promise((resolve2) => setTimeout(resolve2, 200).unref());
|
|
16658
|
+
snapshot = await startDetachedDaemon({
|
|
16659
|
+
...options,
|
|
16660
|
+
dashboardPort: options.dashboardPort
|
|
16661
|
+
});
|
|
16475
16662
|
}
|
|
16476
|
-
const dashboardPort = options.dashboardPort ??
|
|
16663
|
+
const dashboardPort = options.dashboardPort ?? snapshot.dashboardPort;
|
|
16477
16664
|
const dashboardUrl = `http://127.0.0.1:${dashboardPort}`;
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
`
|
|
16481
|
-
const nodeExecutable = await resolveNodeExecutable();
|
|
16482
|
-
const viteProcess = spawnImplementation(nodeExecutable, [
|
|
16483
|
-
"-e",
|
|
16484
|
-
`
|
|
16485
|
-
import { createServer } from 'vite';
|
|
16486
|
-
import path from 'path';
|
|
16487
|
-
|
|
16488
|
-
const distPath = '${distPath}';
|
|
16489
|
-
const port = ${dashboardPort};
|
|
16490
|
-
|
|
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
|
-
},
|
|
16502
|
-
});
|
|
16503
|
-
|
|
16504
|
-
await server.listen();
|
|
16505
|
-
console.log('READY');
|
|
16506
|
-
|
|
16507
|
-
process.stdin.resume();
|
|
16665
|
+
if (!await waitForDashboardReady(dashboardPort)) {
|
|
16666
|
+
output.stdout(
|
|
16667
|
+
`Dashboard not reachable at ${dashboardUrl}, starting a standalone server...
|
|
16508
16668
|
`
|
|
16509
|
-
|
|
16510
|
-
cwd: distPath,
|
|
16511
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
16512
|
-
});
|
|
16513
|
-
let serverOutput = "";
|
|
16514
|
-
let serverSpawnError;
|
|
16515
|
-
viteProcess.on("error", (error) => {
|
|
16516
|
-
serverSpawnError = error;
|
|
16517
|
-
serverOutput += `Dashboard server process failed: ${formatSpawnError(error)}
|
|
16518
|
-
`;
|
|
16519
|
-
});
|
|
16520
|
-
viteProcess.stdout?.on("data", (data) => {
|
|
16521
|
-
serverOutput += data.toString();
|
|
16522
|
-
});
|
|
16523
|
-
viteProcess.stderr?.on("data", (data) => {
|
|
16524
|
-
serverOutput += data.toString();
|
|
16525
|
-
});
|
|
16526
|
-
let serverReady = false;
|
|
16527
|
-
for (let i = 0; i < 100; i++) {
|
|
16528
|
-
await new Promise((resolve2) => setTimeout(resolve2, 100).unref());
|
|
16669
|
+
);
|
|
16529
16670
|
try {
|
|
16530
|
-
|
|
16531
|
-
|
|
16532
|
-
|
|
16533
|
-
|
|
16534
|
-
|
|
16535
|
-
|
|
16536
|
-
}
|
|
16537
|
-
if (serverSpawnError !== void 0) {
|
|
16538
|
-
break;
|
|
16671
|
+
await startDashboardServerProcess(dashboardPort);
|
|
16672
|
+
} catch (error) {
|
|
16673
|
+
throw new Error(
|
|
16674
|
+
`Dashboard did not become reachable at ${dashboardUrl}: ${error instanceof Error ? error.message : "unknown error"}`,
|
|
16675
|
+
{ cause: error }
|
|
16676
|
+
);
|
|
16539
16677
|
}
|
|
16540
|
-
if (!
|
|
16541
|
-
}
|
|
16542
|
-
if (!serverReady) {
|
|
16543
|
-
output.stdout(`Server output: ${serverOutput}
|
|
16544
|
-
`);
|
|
16545
|
-
if (serverSpawnError !== void 0) {
|
|
16678
|
+
if (!await waitForDashboardReady(dashboardPort)) {
|
|
16546
16679
|
throw new Error(
|
|
16547
|
-
`
|
|
16680
|
+
`Dashboard did not become reachable at ${dashboardUrl}.`
|
|
16548
16681
|
);
|
|
16549
16682
|
}
|
|
16550
|
-
throw new Error("Failed to start dashboard server");
|
|
16551
16683
|
}
|
|
16552
16684
|
output.stdout(`Dashboard ready at ${dashboardUrl}
|
|
16553
16685
|
`);
|
|
16554
16686
|
if (!options.noBrowser) {
|
|
16555
16687
|
output.stdout("Opening browser...\n");
|
|
16556
|
-
|
|
16557
|
-
|
|
16688
|
+
try {
|
|
16689
|
+
const openModule = await openPromise;
|
|
16690
|
+
await openModule.default(dashboardUrl);
|
|
16691
|
+
} catch (error) {
|
|
16692
|
+
output.stderr(
|
|
16693
|
+
`Failed to open browser automatically (${error instanceof Error ? error.message : "unknown error"}). Open ${dashboardUrl} manually.
|
|
16694
|
+
`
|
|
16695
|
+
);
|
|
16696
|
+
}
|
|
16558
16697
|
}
|
|
16559
|
-
output.stdout("Press Ctrl+C to stop\n");
|
|
16560
|
-
await new Promise((resolve2) => {
|
|
16561
|
-
const shutdown = () => {
|
|
16562
|
-
process.off("SIGINT", handleSigint);
|
|
16563
|
-
process.off("SIGTERM", handleSigterm);
|
|
16564
|
-
if (viteProcess.pid !== void 0) {
|
|
16565
|
-
try {
|
|
16566
|
-
process.kill(viteProcess.pid, "SIGTERM");
|
|
16567
|
-
} catch {
|
|
16568
|
-
}
|
|
16569
|
-
}
|
|
16570
|
-
resolve2();
|
|
16571
|
-
};
|
|
16572
|
-
const handleSigint = () => {
|
|
16573
|
-
shutdown();
|
|
16574
|
-
};
|
|
16575
|
-
const handleSigterm = () => {
|
|
16576
|
-
shutdown();
|
|
16577
|
-
};
|
|
16578
|
-
process.once("SIGINT", handleSigint);
|
|
16579
|
-
process.once("SIGTERM", handleSigterm);
|
|
16580
|
-
});
|
|
16581
16698
|
}
|
|
16582
16699
|
async function logger2(options) {
|
|
16583
16700
|
const snapshot = await getStatusSnapshot(options);
|
|
@@ -16844,6 +16961,7 @@ process.stdin.resume();
|
|
|
16844
16961
|
const health = running && daemonState !== null ? await fetchHealth(daemonState.httpPort) : null;
|
|
16845
16962
|
return {
|
|
16846
16963
|
configuredAdapters: getEnabledAdapters(config),
|
|
16964
|
+
dashboardPort: config.dashboardPort ?? DEFAULT_DASHBOARD_PORT,
|
|
16847
16965
|
daemonPid,
|
|
16848
16966
|
health,
|
|
16849
16967
|
httpPort: daemonState?.httpPort ?? config.httpPort,
|
|
@@ -17078,13 +17196,13 @@ function createProgram(dependencies = {}) {
|
|
|
17078
17196
|
"after",
|
|
17079
17197
|
`
|
|
17080
17198
|
Examples:
|
|
17081
|
-
aisnitch
|
|
17199
|
+
aisnitch (default: starts daemon + dashboard server, opens browser)
|
|
17200
|
+
aisnitch fs --dashboard-port 8080
|
|
17201
|
+
aisnitch fs --no-browser
|
|
17202
|
+
aisnitch start (TUI dashboard)
|
|
17082
17203
|
aisnitch start --daemon
|
|
17083
17204
|
aisnitch start --view full-data
|
|
17084
17205
|
aisnitch start --mock
|
|
17085
|
-
aisnitch fs
|
|
17086
|
-
aisnitch fs --daemon
|
|
17087
|
-
aisnitch fs --dashboard-port 8080
|
|
17088
17206
|
aisnitch status
|
|
17089
17207
|
aisnitch attach
|
|
17090
17208
|
aisnitch attach --view full-data
|
|
@@ -17127,7 +17245,7 @@ function addCommonOptions(command) {
|
|
|
17127
17245
|
}
|
|
17128
17246
|
function addStartCommand(program, runtime) {
|
|
17129
17247
|
addCommonOptions(
|
|
17130
|
-
program.command("start"
|
|
17248
|
+
program.command("start").description("Open the AISnitch TUI dashboard and manage the daemon").option("--daemon", "Run AISnitch as a detached daemon").option(
|
|
17131
17249
|
"--mock [tool]",
|
|
17132
17250
|
'Inject deterministic mock events (defaults to "all" when no tool is specified)',
|
|
17133
17251
|
wrapOptionParser(parseMockToolSelection)
|
|
@@ -17159,6 +17277,10 @@ function addStartCommand(program, runtime) {
|
|
|
17159
17277
|
"--http-port <port>",
|
|
17160
17278
|
"Override the HTTP hook port",
|
|
17161
17279
|
wrapOptionParser(parsePortOption)
|
|
17280
|
+
).option(
|
|
17281
|
+
"--dashboard-port <port>",
|
|
17282
|
+
"Override the web dashboard port",
|
|
17283
|
+
wrapOptionParser(parsePortOption)
|
|
17162
17284
|
).option(
|
|
17163
17285
|
"--log-level <level>",
|
|
17164
17286
|
"Override the runtime log level",
|
|
@@ -17243,9 +17365,9 @@ function addAttachCommand(program, runtime) {
|
|
|
17243
17365
|
}
|
|
17244
17366
|
function addFullscreenCommand(program, runtime) {
|
|
17245
17367
|
addCommonOptions(
|
|
17246
|
-
program.command("fs").description("
|
|
17368
|
+
program.command("fs", { isDefault: true }).description("Start the daemon, the dashboard server, and open the web dashboard in browser").alias("fullscreen").option(
|
|
17247
17369
|
"--daemon",
|
|
17248
|
-
"
|
|
17370
|
+
"Deprecated: daemon is now started automatically when needed"
|
|
17249
17371
|
).option(
|
|
17250
17372
|
"--dashboard-port <port>",
|
|
17251
17373
|
"Port for the dashboard server (default: 5174)",
|