aisnitch 0.2.1 → 0.2.3
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 +5 -0
- package/dist/cli/index.cjs +246 -2
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +246 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
8
8
|
|
|
9
9
|
// src/package-info.ts
|
|
10
10
|
var AISNITCH_PACKAGE_NAME = "aisnitch";
|
|
11
|
-
var AISNITCH_VERSION = "0.2.
|
|
11
|
+
var AISNITCH_VERSION = "0.2.3";
|
|
12
12
|
var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
|
|
13
13
|
|
|
14
14
|
// src/core/events/schema.ts
|
|
@@ -11748,6 +11748,179 @@ function toConfigArgv(options) {
|
|
|
11748
11748
|
return options.configPath ? ["--config", options.configPath] : [];
|
|
11749
11749
|
}
|
|
11750
11750
|
|
|
11751
|
+
// src/cli/live-logger.ts
|
|
11752
|
+
import { once as once5 } from "events";
|
|
11753
|
+
import WebSocket5 from "ws";
|
|
11754
|
+
function formatLoggerEventBlock(event) {
|
|
11755
|
+
const headerSegments = [
|
|
11756
|
+
colorize(`#${event["aisnitch.seqnum"]}`, EVENT_COLORS[event.type]),
|
|
11757
|
+
colorize(event["aisnitch.tool"], TOOL_COLORS[event["aisnitch.tool"]]),
|
|
11758
|
+
colorize(event.type, EVENT_COLORS[event.type]),
|
|
11759
|
+
colorize(formatSessionLabelFromEvent(event), TUI_THEME.warning),
|
|
11760
|
+
colorize(event.time, TUI_THEME.muted)
|
|
11761
|
+
];
|
|
11762
|
+
const flattenedLines = flattenEventRecord(event);
|
|
11763
|
+
return [
|
|
11764
|
+
`${colorize("\u256D\u2500", TUI_THEME.frame)} ${headerSegments.join(colorize(" ", TUI_THEME.muted))}`,
|
|
11765
|
+
...flattenedLines.map((line) => `${colorize("\u2502", TUI_THEME.frame)} ${line}`),
|
|
11766
|
+
colorize("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", TUI_THEME.frame)
|
|
11767
|
+
].join("\n");
|
|
11768
|
+
}
|
|
11769
|
+
function formatLoggerWelcomeLine(message) {
|
|
11770
|
+
const tools = message.tools.length > 0 ? message.tools.join(", ") : "none configured";
|
|
11771
|
+
return `${colorize("AISnitch logger attached", TUI_THEME.success)} ${colorize(`v${message.version}`, TUI_THEME.warning)} ${colorize(`tools=${tools}`, TUI_THEME.muted)}`;
|
|
11772
|
+
}
|
|
11773
|
+
async function attachWebSocketLogger(url, output, filters = {}) {
|
|
11774
|
+
const socket = new WebSocket5(url);
|
|
11775
|
+
socket.on("message", (data) => {
|
|
11776
|
+
const parsedPayload = parseSocketMessage(data);
|
|
11777
|
+
if (isWelcomeMessage(parsedPayload)) {
|
|
11778
|
+
output.stdout(`${formatLoggerWelcomeLine(parsedPayload)}
|
|
11779
|
+
`);
|
|
11780
|
+
return;
|
|
11781
|
+
}
|
|
11782
|
+
const parsedEvent = AISnitchEventSchema.safeParse(parsedPayload);
|
|
11783
|
+
if (!parsedEvent.success) {
|
|
11784
|
+
output.stderr(
|
|
11785
|
+
`${colorize("logger:", TUI_THEME.danger)} received an unrecognized event payload.
|
|
11786
|
+
`
|
|
11787
|
+
);
|
|
11788
|
+
return;
|
|
11789
|
+
}
|
|
11790
|
+
if (!matchesFilters(parsedEvent.data, filters)) {
|
|
11791
|
+
return;
|
|
11792
|
+
}
|
|
11793
|
+
output.stdout(`${formatLoggerEventBlock(parsedEvent.data)}
|
|
11794
|
+
`);
|
|
11795
|
+
});
|
|
11796
|
+
socket.on("error", (error) => {
|
|
11797
|
+
output.stderr(
|
|
11798
|
+
`${colorize("logger:", TUI_THEME.danger)} ${error instanceof Error ? error.message : "unknown socket error"}
|
|
11799
|
+
`
|
|
11800
|
+
);
|
|
11801
|
+
});
|
|
11802
|
+
await once5(socket, "open");
|
|
11803
|
+
return async () => {
|
|
11804
|
+
if (socket.readyState === WebSocket5.CLOSING || socket.readyState === WebSocket5.CLOSED) {
|
|
11805
|
+
return;
|
|
11806
|
+
}
|
|
11807
|
+
socket.close();
|
|
11808
|
+
await once5(socket, "close");
|
|
11809
|
+
};
|
|
11810
|
+
}
|
|
11811
|
+
function matchesFilters(event, filters) {
|
|
11812
|
+
if (filters.tool && event["aisnitch.tool"] !== filters.tool) {
|
|
11813
|
+
return false;
|
|
11814
|
+
}
|
|
11815
|
+
if (filters.type && event.type !== filters.type) {
|
|
11816
|
+
return false;
|
|
11817
|
+
}
|
|
11818
|
+
return true;
|
|
11819
|
+
}
|
|
11820
|
+
function flattenEventRecord(event) {
|
|
11821
|
+
const lines = [];
|
|
11822
|
+
flattenValue(lines, "", event);
|
|
11823
|
+
return lines;
|
|
11824
|
+
}
|
|
11825
|
+
function flattenValue(lines, currentPath, value) {
|
|
11826
|
+
if (Array.isArray(value)) {
|
|
11827
|
+
if (value.length === 0) {
|
|
11828
|
+
lines.push(formatLoggerField(currentPath, "[]", "empty"));
|
|
11829
|
+
return;
|
|
11830
|
+
}
|
|
11831
|
+
value.forEach((entry, index) => {
|
|
11832
|
+
flattenValue(lines, `${currentPath}[${index}]`, entry);
|
|
11833
|
+
});
|
|
11834
|
+
return;
|
|
11835
|
+
}
|
|
11836
|
+
if (isRecord10(value)) {
|
|
11837
|
+
const entries = Object.entries(value).sort(
|
|
11838
|
+
([left], [right]) => left.localeCompare(right)
|
|
11839
|
+
);
|
|
11840
|
+
if (entries.length === 0) {
|
|
11841
|
+
lines.push(formatLoggerField(currentPath, "{}", "empty"));
|
|
11842
|
+
return;
|
|
11843
|
+
}
|
|
11844
|
+
for (const [key, entry] of entries) {
|
|
11845
|
+
const nextPath = currentPath.length === 0 ? key : `${currentPath}.${key}`;
|
|
11846
|
+
flattenValue(lines, nextPath, entry);
|
|
11847
|
+
}
|
|
11848
|
+
return;
|
|
11849
|
+
}
|
|
11850
|
+
lines.push(formatLoggerField(currentPath, value, inferValueKind(value)));
|
|
11851
|
+
}
|
|
11852
|
+
function formatLoggerField(path, value, kind) {
|
|
11853
|
+
const renderedValue = typeof value === "string" ? JSON.stringify(value) : value === null ? "null" : typeof value === "undefined" ? "undefined" : JSON.stringify(value);
|
|
11854
|
+
return `${colorize(path, TUI_THEME.warning)} ${colorize("=", TUI_THEME.muted)} ${colorize(
|
|
11855
|
+
renderedValue,
|
|
11856
|
+
getValueColor(kind)
|
|
11857
|
+
)}`;
|
|
11858
|
+
}
|
|
11859
|
+
function inferValueKind(value) {
|
|
11860
|
+
if (value === null) {
|
|
11861
|
+
return "null";
|
|
11862
|
+
}
|
|
11863
|
+
switch (typeof value) {
|
|
11864
|
+
case "string":
|
|
11865
|
+
return "string";
|
|
11866
|
+
case "number":
|
|
11867
|
+
return "number";
|
|
11868
|
+
case "boolean":
|
|
11869
|
+
return "boolean";
|
|
11870
|
+
default:
|
|
11871
|
+
return "other";
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
function getValueColor(kind) {
|
|
11875
|
+
switch (kind) {
|
|
11876
|
+
case "string":
|
|
11877
|
+
return TUI_THEME.panelBody;
|
|
11878
|
+
case "number":
|
|
11879
|
+
return TUI_THEME.success;
|
|
11880
|
+
case "boolean":
|
|
11881
|
+
return TUI_THEME.warning;
|
|
11882
|
+
case "null":
|
|
11883
|
+
return TUI_THEME.danger;
|
|
11884
|
+
case "empty":
|
|
11885
|
+
return TUI_THEME.muted;
|
|
11886
|
+
default:
|
|
11887
|
+
return TUI_THEME.panelBody;
|
|
11888
|
+
}
|
|
11889
|
+
}
|
|
11890
|
+
function colorize(value, color) {
|
|
11891
|
+
const [red, green, blue] = hexToRgb(color);
|
|
11892
|
+
return `\x1B[38;2;${red};${green};${blue}m${value}\x1B[39m`;
|
|
11893
|
+
}
|
|
11894
|
+
function hexToRgb(hexColor) {
|
|
11895
|
+
const sanitized = hexColor.slice(1);
|
|
11896
|
+
return [
|
|
11897
|
+
Number.parseInt(sanitized.slice(0, 2), 16),
|
|
11898
|
+
Number.parseInt(sanitized.slice(2, 4), 16),
|
|
11899
|
+
Number.parseInt(sanitized.slice(4, 6), 16)
|
|
11900
|
+
];
|
|
11901
|
+
}
|
|
11902
|
+
function parseSocketMessage(data) {
|
|
11903
|
+
if (typeof data === "string") {
|
|
11904
|
+
return JSON.parse(data);
|
|
11905
|
+
}
|
|
11906
|
+
if (Array.isArray(data)) {
|
|
11907
|
+
return JSON.parse(Buffer.concat(data).toString("utf8"));
|
|
11908
|
+
}
|
|
11909
|
+
if (data instanceof ArrayBuffer) {
|
|
11910
|
+
return JSON.parse(Buffer.from(new Uint8Array(data)).toString("utf8"));
|
|
11911
|
+
}
|
|
11912
|
+
return JSON.parse(Buffer.from(data).toString("utf8"));
|
|
11913
|
+
}
|
|
11914
|
+
function isWelcomeMessage(payload) {
|
|
11915
|
+
if (!isRecord10(payload)) {
|
|
11916
|
+
return false;
|
|
11917
|
+
}
|
|
11918
|
+
return payload.type === "welcome" && typeof payload.version === "string" && Array.isArray(payload.tools);
|
|
11919
|
+
}
|
|
11920
|
+
function isRecord10(value) {
|
|
11921
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11922
|
+
}
|
|
11923
|
+
|
|
11751
11924
|
// src/cli/runtime.ts
|
|
11752
11925
|
var execFile11 = promisify11(execFileCallback11);
|
|
11753
11926
|
var DAEMON_READY_TIMEOUT_MS = 4e3;
|
|
@@ -11946,7 +12119,22 @@ function createCliRuntime(dependencies = {}) {
|
|
|
11946
12119
|
if (!lastLine) {
|
|
11947
12120
|
return null;
|
|
11948
12121
|
}
|
|
11949
|
-
|
|
12122
|
+
if (lastLine.startsWith("AISnitch CLI failed:")) {
|
|
12123
|
+
return lastLine;
|
|
12124
|
+
}
|
|
12125
|
+
const structuredFailure = parseStructuredDaemonFailure(lastLine);
|
|
12126
|
+
return structuredFailure === null ? null : `AISnitch daemon startup failed: ${structuredFailure}`;
|
|
12127
|
+
} catch {
|
|
12128
|
+
return null;
|
|
12129
|
+
}
|
|
12130
|
+
}
|
|
12131
|
+
function parseStructuredDaemonFailure(logLine) {
|
|
12132
|
+
try {
|
|
12133
|
+
const parsedLog = JSON.parse(logLine);
|
|
12134
|
+
if (typeof parsedLog.level !== "number" || parsedLog.level < 50) {
|
|
12135
|
+
return null;
|
|
12136
|
+
}
|
|
12137
|
+
return typeof parsedLog.msg === "string" && parsedLog.msg.length > 0 ? parsedLog.msg : logLine;
|
|
11950
12138
|
} catch {
|
|
11951
12139
|
return null;
|
|
11952
12140
|
}
|
|
@@ -12210,6 +12398,43 @@ function createCliRuntime(dependencies = {}) {
|
|
|
12210
12398
|
}
|
|
12211
12399
|
});
|
|
12212
12400
|
}
|
|
12401
|
+
async function logger2(options) {
|
|
12402
|
+
const snapshot = await getStatusSnapshot(options);
|
|
12403
|
+
if (!snapshot.running) {
|
|
12404
|
+
throw new Error(
|
|
12405
|
+
"AISnitch logger requires a running daemon. Start one with `aisnitch start --daemon` or use `aisnitch start` first."
|
|
12406
|
+
);
|
|
12407
|
+
}
|
|
12408
|
+
const closeLogger = await attachWebSocketLogger(
|
|
12409
|
+
`ws://127.0.0.1:${snapshot.wsPort}`,
|
|
12410
|
+
output,
|
|
12411
|
+
{
|
|
12412
|
+
tool: options.tool,
|
|
12413
|
+
type: options.type
|
|
12414
|
+
}
|
|
12415
|
+
);
|
|
12416
|
+
await new Promise((resolve2) => {
|
|
12417
|
+
let closed = false;
|
|
12418
|
+
const shutdown = async () => {
|
|
12419
|
+
if (closed) {
|
|
12420
|
+
return;
|
|
12421
|
+
}
|
|
12422
|
+
closed = true;
|
|
12423
|
+
process.off("SIGINT", handleSigint);
|
|
12424
|
+
process.off("SIGTERM", handleSigterm);
|
|
12425
|
+
await Promise.resolve(closeLogger());
|
|
12426
|
+
resolve2();
|
|
12427
|
+
};
|
|
12428
|
+
const handleSigint = () => {
|
|
12429
|
+
void shutdown();
|
|
12430
|
+
};
|
|
12431
|
+
const handleSigterm = () => {
|
|
12432
|
+
void shutdown();
|
|
12433
|
+
};
|
|
12434
|
+
process.once("SIGINT", handleSigint);
|
|
12435
|
+
process.once("SIGTERM", handleSigterm);
|
|
12436
|
+
});
|
|
12437
|
+
}
|
|
12213
12438
|
async function mock(selection, options) {
|
|
12214
12439
|
const pathOptions = toPathOptions2(options);
|
|
12215
12440
|
const daemonState = await readDaemonState(pathOptions);
|
|
@@ -12465,6 +12690,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
12465
12690
|
aiderNotify,
|
|
12466
12691
|
attach,
|
|
12467
12692
|
install,
|
|
12693
|
+
logger: logger2,
|
|
12468
12694
|
mock,
|
|
12469
12695
|
runDaemonProcess,
|
|
12470
12696
|
selfUpdateRun,
|
|
@@ -12677,6 +12903,8 @@ Examples:
|
|
|
12677
12903
|
aisnitch status
|
|
12678
12904
|
aisnitch attach
|
|
12679
12905
|
aisnitch attach --view full-data
|
|
12906
|
+
aisnitch logger
|
|
12907
|
+
aisnitch logger --tool claude-code
|
|
12680
12908
|
aisnitch setup claude-code
|
|
12681
12909
|
aisnitch setup aider
|
|
12682
12910
|
aisnitch setup gemini-cli
|
|
@@ -12695,6 +12923,7 @@ Examples:
|
|
|
12695
12923
|
addAdaptersCommand(program, runtime);
|
|
12696
12924
|
addSetupCommand(program, runtime);
|
|
12697
12925
|
addAttachCommand(program, runtime);
|
|
12926
|
+
addLoggerCommand(program, runtime);
|
|
12698
12927
|
addMockCommand(program, runtime);
|
|
12699
12928
|
addWrapCommand(program, runtime);
|
|
12700
12929
|
addInstallCommand(program, runtime);
|
|
@@ -12835,6 +13064,21 @@ function addWrapCommand(program, runtime) {
|
|
|
12835
13064
|
}
|
|
12836
13065
|
);
|
|
12837
13066
|
}
|
|
13067
|
+
function addLoggerCommand(program, runtime) {
|
|
13068
|
+
addCommonOptions(
|
|
13069
|
+
program.command("logger").description("Stream exhaustive live event logs without the TUI").option(
|
|
13070
|
+
"--tool <tool>",
|
|
13071
|
+
"Filter the live logger by tool",
|
|
13072
|
+
wrapOptionParser(parseToolFilterOption)
|
|
13073
|
+
).option(
|
|
13074
|
+
"--type <type>",
|
|
13075
|
+
"Filter the live logger by event type",
|
|
13076
|
+
wrapOptionParser(parseEventTypeFilterOption)
|
|
13077
|
+
)
|
|
13078
|
+
).action(async (options) => {
|
|
13079
|
+
await runtime.logger(options);
|
|
13080
|
+
});
|
|
13081
|
+
}
|
|
12838
13082
|
function addInstallCommand(program, runtime) {
|
|
12839
13083
|
addCommonOptions(
|
|
12840
13084
|
program.command("install").description("Install a macOS LaunchAgent for AISnitch")
|