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/README.md
CHANGED
|
@@ -51,12 +51,17 @@ node dist/cli/index.js start
|
|
|
51
51
|
|
|
52
52
|
# Launch with simulated events (no real AI tool needed)
|
|
53
53
|
node dist/cli/index.js start --mock all
|
|
54
|
+
|
|
55
|
+
# Exhaustive live logger, no TUI
|
|
56
|
+
node dist/cli/index.js logger
|
|
54
57
|
```
|
|
55
58
|
|
|
56
59
|
`start` now always opens the TUI dashboard. If the daemon is offline you still land in the UI, see `Daemon not active`, and can start or stop it from inside the TUI with `d`.
|
|
57
60
|
|
|
58
61
|
`start --mock all` boots the dashboard, ensures the daemon is active, then streams realistic fake events from Claude Code, OpenCode, and Gemini CLI so you can see the product immediately.
|
|
59
62
|
|
|
63
|
+
If you want the full live payload stream without Ink truncation, use `aisnitch logger`. It attaches to the running daemon and prints one structured field per line, including nested `data.raw.*` paths.
|
|
64
|
+
|
|
60
65
|
To consume the stream from another terminal:
|
|
61
66
|
|
|
62
67
|
```bash
|
package/dist/cli/index.cjs
CHANGED
|
@@ -41,7 +41,7 @@ var import_commander = require("commander");
|
|
|
41
41
|
|
|
42
42
|
// src/package-info.ts
|
|
43
43
|
var AISNITCH_PACKAGE_NAME = "aisnitch";
|
|
44
|
-
var AISNITCH_VERSION = "0.2.
|
|
44
|
+
var AISNITCH_VERSION = "0.2.3";
|
|
45
45
|
var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
|
|
46
46
|
|
|
47
47
|
// src/core/events/schema.ts
|
|
@@ -11779,6 +11779,179 @@ function toConfigArgv(options) {
|
|
|
11779
11779
|
return options.configPath ? ["--config", options.configPath] : [];
|
|
11780
11780
|
}
|
|
11781
11781
|
|
|
11782
|
+
// src/cli/live-logger.ts
|
|
11783
|
+
var import_node_events5 = require("events");
|
|
11784
|
+
var import_ws5 = __toESM(require("ws"), 1);
|
|
11785
|
+
function formatLoggerEventBlock(event) {
|
|
11786
|
+
const headerSegments = [
|
|
11787
|
+
colorize(`#${event["aisnitch.seqnum"]}`, EVENT_COLORS[event.type]),
|
|
11788
|
+
colorize(event["aisnitch.tool"], TOOL_COLORS[event["aisnitch.tool"]]),
|
|
11789
|
+
colorize(event.type, EVENT_COLORS[event.type]),
|
|
11790
|
+
colorize(formatSessionLabelFromEvent(event), TUI_THEME.warning),
|
|
11791
|
+
colorize(event.time, TUI_THEME.muted)
|
|
11792
|
+
];
|
|
11793
|
+
const flattenedLines = flattenEventRecord(event);
|
|
11794
|
+
return [
|
|
11795
|
+
`${colorize("\u256D\u2500", TUI_THEME.frame)} ${headerSegments.join(colorize(" ", TUI_THEME.muted))}`,
|
|
11796
|
+
...flattenedLines.map((line) => `${colorize("\u2502", TUI_THEME.frame)} ${line}`),
|
|
11797
|
+
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)
|
|
11798
|
+
].join("\n");
|
|
11799
|
+
}
|
|
11800
|
+
function formatLoggerWelcomeLine(message) {
|
|
11801
|
+
const tools = message.tools.length > 0 ? message.tools.join(", ") : "none configured";
|
|
11802
|
+
return `${colorize("AISnitch logger attached", TUI_THEME.success)} ${colorize(`v${message.version}`, TUI_THEME.warning)} ${colorize(`tools=${tools}`, TUI_THEME.muted)}`;
|
|
11803
|
+
}
|
|
11804
|
+
async function attachWebSocketLogger(url, output, filters = {}) {
|
|
11805
|
+
const socket = new import_ws5.default(url);
|
|
11806
|
+
socket.on("message", (data) => {
|
|
11807
|
+
const parsedPayload = parseSocketMessage(data);
|
|
11808
|
+
if (isWelcomeMessage(parsedPayload)) {
|
|
11809
|
+
output.stdout(`${formatLoggerWelcomeLine(parsedPayload)}
|
|
11810
|
+
`);
|
|
11811
|
+
return;
|
|
11812
|
+
}
|
|
11813
|
+
const parsedEvent = AISnitchEventSchema.safeParse(parsedPayload);
|
|
11814
|
+
if (!parsedEvent.success) {
|
|
11815
|
+
output.stderr(
|
|
11816
|
+
`${colorize("logger:", TUI_THEME.danger)} received an unrecognized event payload.
|
|
11817
|
+
`
|
|
11818
|
+
);
|
|
11819
|
+
return;
|
|
11820
|
+
}
|
|
11821
|
+
if (!matchesFilters(parsedEvent.data, filters)) {
|
|
11822
|
+
return;
|
|
11823
|
+
}
|
|
11824
|
+
output.stdout(`${formatLoggerEventBlock(parsedEvent.data)}
|
|
11825
|
+
`);
|
|
11826
|
+
});
|
|
11827
|
+
socket.on("error", (error) => {
|
|
11828
|
+
output.stderr(
|
|
11829
|
+
`${colorize("logger:", TUI_THEME.danger)} ${error instanceof Error ? error.message : "unknown socket error"}
|
|
11830
|
+
`
|
|
11831
|
+
);
|
|
11832
|
+
});
|
|
11833
|
+
await (0, import_node_events5.once)(socket, "open");
|
|
11834
|
+
return async () => {
|
|
11835
|
+
if (socket.readyState === import_ws5.default.CLOSING || socket.readyState === import_ws5.default.CLOSED) {
|
|
11836
|
+
return;
|
|
11837
|
+
}
|
|
11838
|
+
socket.close();
|
|
11839
|
+
await (0, import_node_events5.once)(socket, "close");
|
|
11840
|
+
};
|
|
11841
|
+
}
|
|
11842
|
+
function matchesFilters(event, filters) {
|
|
11843
|
+
if (filters.tool && event["aisnitch.tool"] !== filters.tool) {
|
|
11844
|
+
return false;
|
|
11845
|
+
}
|
|
11846
|
+
if (filters.type && event.type !== filters.type) {
|
|
11847
|
+
return false;
|
|
11848
|
+
}
|
|
11849
|
+
return true;
|
|
11850
|
+
}
|
|
11851
|
+
function flattenEventRecord(event) {
|
|
11852
|
+
const lines = [];
|
|
11853
|
+
flattenValue(lines, "", event);
|
|
11854
|
+
return lines;
|
|
11855
|
+
}
|
|
11856
|
+
function flattenValue(lines, currentPath, value) {
|
|
11857
|
+
if (Array.isArray(value)) {
|
|
11858
|
+
if (value.length === 0) {
|
|
11859
|
+
lines.push(formatLoggerField(currentPath, "[]", "empty"));
|
|
11860
|
+
return;
|
|
11861
|
+
}
|
|
11862
|
+
value.forEach((entry, index) => {
|
|
11863
|
+
flattenValue(lines, `${currentPath}[${index}]`, entry);
|
|
11864
|
+
});
|
|
11865
|
+
return;
|
|
11866
|
+
}
|
|
11867
|
+
if (isRecord10(value)) {
|
|
11868
|
+
const entries = Object.entries(value).sort(
|
|
11869
|
+
([left], [right]) => left.localeCompare(right)
|
|
11870
|
+
);
|
|
11871
|
+
if (entries.length === 0) {
|
|
11872
|
+
lines.push(formatLoggerField(currentPath, "{}", "empty"));
|
|
11873
|
+
return;
|
|
11874
|
+
}
|
|
11875
|
+
for (const [key, entry] of entries) {
|
|
11876
|
+
const nextPath = currentPath.length === 0 ? key : `${currentPath}.${key}`;
|
|
11877
|
+
flattenValue(lines, nextPath, entry);
|
|
11878
|
+
}
|
|
11879
|
+
return;
|
|
11880
|
+
}
|
|
11881
|
+
lines.push(formatLoggerField(currentPath, value, inferValueKind(value)));
|
|
11882
|
+
}
|
|
11883
|
+
function formatLoggerField(path, value, kind) {
|
|
11884
|
+
const renderedValue = typeof value === "string" ? JSON.stringify(value) : value === null ? "null" : typeof value === "undefined" ? "undefined" : JSON.stringify(value);
|
|
11885
|
+
return `${colorize(path, TUI_THEME.warning)} ${colorize("=", TUI_THEME.muted)} ${colorize(
|
|
11886
|
+
renderedValue,
|
|
11887
|
+
getValueColor(kind)
|
|
11888
|
+
)}`;
|
|
11889
|
+
}
|
|
11890
|
+
function inferValueKind(value) {
|
|
11891
|
+
if (value === null) {
|
|
11892
|
+
return "null";
|
|
11893
|
+
}
|
|
11894
|
+
switch (typeof value) {
|
|
11895
|
+
case "string":
|
|
11896
|
+
return "string";
|
|
11897
|
+
case "number":
|
|
11898
|
+
return "number";
|
|
11899
|
+
case "boolean":
|
|
11900
|
+
return "boolean";
|
|
11901
|
+
default:
|
|
11902
|
+
return "other";
|
|
11903
|
+
}
|
|
11904
|
+
}
|
|
11905
|
+
function getValueColor(kind) {
|
|
11906
|
+
switch (kind) {
|
|
11907
|
+
case "string":
|
|
11908
|
+
return TUI_THEME.panelBody;
|
|
11909
|
+
case "number":
|
|
11910
|
+
return TUI_THEME.success;
|
|
11911
|
+
case "boolean":
|
|
11912
|
+
return TUI_THEME.warning;
|
|
11913
|
+
case "null":
|
|
11914
|
+
return TUI_THEME.danger;
|
|
11915
|
+
case "empty":
|
|
11916
|
+
return TUI_THEME.muted;
|
|
11917
|
+
default:
|
|
11918
|
+
return TUI_THEME.panelBody;
|
|
11919
|
+
}
|
|
11920
|
+
}
|
|
11921
|
+
function colorize(value, color) {
|
|
11922
|
+
const [red, green, blue] = hexToRgb(color);
|
|
11923
|
+
return `\x1B[38;2;${red};${green};${blue}m${value}\x1B[39m`;
|
|
11924
|
+
}
|
|
11925
|
+
function hexToRgb(hexColor) {
|
|
11926
|
+
const sanitized = hexColor.slice(1);
|
|
11927
|
+
return [
|
|
11928
|
+
Number.parseInt(sanitized.slice(0, 2), 16),
|
|
11929
|
+
Number.parseInt(sanitized.slice(2, 4), 16),
|
|
11930
|
+
Number.parseInt(sanitized.slice(4, 6), 16)
|
|
11931
|
+
];
|
|
11932
|
+
}
|
|
11933
|
+
function parseSocketMessage(data) {
|
|
11934
|
+
if (typeof data === "string") {
|
|
11935
|
+
return JSON.parse(data);
|
|
11936
|
+
}
|
|
11937
|
+
if (Array.isArray(data)) {
|
|
11938
|
+
return JSON.parse(Buffer.concat(data).toString("utf8"));
|
|
11939
|
+
}
|
|
11940
|
+
if (data instanceof ArrayBuffer) {
|
|
11941
|
+
return JSON.parse(Buffer.from(new Uint8Array(data)).toString("utf8"));
|
|
11942
|
+
}
|
|
11943
|
+
return JSON.parse(Buffer.from(data).toString("utf8"));
|
|
11944
|
+
}
|
|
11945
|
+
function isWelcomeMessage(payload) {
|
|
11946
|
+
if (!isRecord10(payload)) {
|
|
11947
|
+
return false;
|
|
11948
|
+
}
|
|
11949
|
+
return payload.type === "welcome" && typeof payload.version === "string" && Array.isArray(payload.tools);
|
|
11950
|
+
}
|
|
11951
|
+
function isRecord10(value) {
|
|
11952
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11953
|
+
}
|
|
11954
|
+
|
|
11782
11955
|
// src/cli/runtime.ts
|
|
11783
11956
|
var execFile11 = (0, import_node_util11.promisify)(import_node_child_process12.execFile);
|
|
11784
11957
|
var DAEMON_READY_TIMEOUT_MS = 4e3;
|
|
@@ -11977,7 +12150,22 @@ function createCliRuntime(dependencies = {}) {
|
|
|
11977
12150
|
if (!lastLine) {
|
|
11978
12151
|
return null;
|
|
11979
12152
|
}
|
|
11980
|
-
|
|
12153
|
+
if (lastLine.startsWith("AISnitch CLI failed:")) {
|
|
12154
|
+
return lastLine;
|
|
12155
|
+
}
|
|
12156
|
+
const structuredFailure = parseStructuredDaemonFailure(lastLine);
|
|
12157
|
+
return structuredFailure === null ? null : `AISnitch daemon startup failed: ${structuredFailure}`;
|
|
12158
|
+
} catch {
|
|
12159
|
+
return null;
|
|
12160
|
+
}
|
|
12161
|
+
}
|
|
12162
|
+
function parseStructuredDaemonFailure(logLine) {
|
|
12163
|
+
try {
|
|
12164
|
+
const parsedLog = JSON.parse(logLine);
|
|
12165
|
+
if (typeof parsedLog.level !== "number" || parsedLog.level < 50) {
|
|
12166
|
+
return null;
|
|
12167
|
+
}
|
|
12168
|
+
return typeof parsedLog.msg === "string" && parsedLog.msg.length > 0 ? parsedLog.msg : logLine;
|
|
11981
12169
|
} catch {
|
|
11982
12170
|
return null;
|
|
11983
12171
|
}
|
|
@@ -12241,6 +12429,43 @@ function createCliRuntime(dependencies = {}) {
|
|
|
12241
12429
|
}
|
|
12242
12430
|
});
|
|
12243
12431
|
}
|
|
12432
|
+
async function logger2(options) {
|
|
12433
|
+
const snapshot = await getStatusSnapshot(options);
|
|
12434
|
+
if (!snapshot.running) {
|
|
12435
|
+
throw new Error(
|
|
12436
|
+
"AISnitch logger requires a running daemon. Start one with `aisnitch start --daemon` or use `aisnitch start` first."
|
|
12437
|
+
);
|
|
12438
|
+
}
|
|
12439
|
+
const closeLogger = await attachWebSocketLogger(
|
|
12440
|
+
`ws://127.0.0.1:${snapshot.wsPort}`,
|
|
12441
|
+
output,
|
|
12442
|
+
{
|
|
12443
|
+
tool: options.tool,
|
|
12444
|
+
type: options.type
|
|
12445
|
+
}
|
|
12446
|
+
);
|
|
12447
|
+
await new Promise((resolve2) => {
|
|
12448
|
+
let closed = false;
|
|
12449
|
+
const shutdown = async () => {
|
|
12450
|
+
if (closed) {
|
|
12451
|
+
return;
|
|
12452
|
+
}
|
|
12453
|
+
closed = true;
|
|
12454
|
+
process.off("SIGINT", handleSigint);
|
|
12455
|
+
process.off("SIGTERM", handleSigterm);
|
|
12456
|
+
await Promise.resolve(closeLogger());
|
|
12457
|
+
resolve2();
|
|
12458
|
+
};
|
|
12459
|
+
const handleSigint = () => {
|
|
12460
|
+
void shutdown();
|
|
12461
|
+
};
|
|
12462
|
+
const handleSigterm = () => {
|
|
12463
|
+
void shutdown();
|
|
12464
|
+
};
|
|
12465
|
+
process.once("SIGINT", handleSigint);
|
|
12466
|
+
process.once("SIGTERM", handleSigterm);
|
|
12467
|
+
});
|
|
12468
|
+
}
|
|
12244
12469
|
async function mock(selection, options) {
|
|
12245
12470
|
const pathOptions = toPathOptions2(options);
|
|
12246
12471
|
const daemonState = await readDaemonState(pathOptions);
|
|
@@ -12496,6 +12721,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
12496
12721
|
aiderNotify,
|
|
12497
12722
|
attach,
|
|
12498
12723
|
install,
|
|
12724
|
+
logger: logger2,
|
|
12499
12725
|
mock,
|
|
12500
12726
|
runDaemonProcess,
|
|
12501
12727
|
selfUpdateRun,
|
|
@@ -12708,6 +12934,8 @@ Examples:
|
|
|
12708
12934
|
aisnitch status
|
|
12709
12935
|
aisnitch attach
|
|
12710
12936
|
aisnitch attach --view full-data
|
|
12937
|
+
aisnitch logger
|
|
12938
|
+
aisnitch logger --tool claude-code
|
|
12711
12939
|
aisnitch setup claude-code
|
|
12712
12940
|
aisnitch setup aider
|
|
12713
12941
|
aisnitch setup gemini-cli
|
|
@@ -12726,6 +12954,7 @@ Examples:
|
|
|
12726
12954
|
addAdaptersCommand(program, runtime);
|
|
12727
12955
|
addSetupCommand(program, runtime);
|
|
12728
12956
|
addAttachCommand(program, runtime);
|
|
12957
|
+
addLoggerCommand(program, runtime);
|
|
12729
12958
|
addMockCommand(program, runtime);
|
|
12730
12959
|
addWrapCommand(program, runtime);
|
|
12731
12960
|
addInstallCommand(program, runtime);
|
|
@@ -12866,6 +13095,21 @@ function addWrapCommand(program, runtime) {
|
|
|
12866
13095
|
}
|
|
12867
13096
|
);
|
|
12868
13097
|
}
|
|
13098
|
+
function addLoggerCommand(program, runtime) {
|
|
13099
|
+
addCommonOptions(
|
|
13100
|
+
program.command("logger").description("Stream exhaustive live event logs without the TUI").option(
|
|
13101
|
+
"--tool <tool>",
|
|
13102
|
+
"Filter the live logger by tool",
|
|
13103
|
+
wrapOptionParser(parseToolFilterOption)
|
|
13104
|
+
).option(
|
|
13105
|
+
"--type <type>",
|
|
13106
|
+
"Filter the live logger by event type",
|
|
13107
|
+
wrapOptionParser(parseEventTypeFilterOption)
|
|
13108
|
+
)
|
|
13109
|
+
).action(async (options) => {
|
|
13110
|
+
await runtime.logger(options);
|
|
13111
|
+
});
|
|
13112
|
+
}
|
|
12869
13113
|
function addInstallCommand(program, runtime) {
|
|
12870
13114
|
addCommonOptions(
|
|
12871
13115
|
program.command("install").description("Install a macOS LaunchAgent for AISnitch")
|