aisnitch 0.2.2 → 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 +230 -1
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +230 -1
- 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;
|
|
@@ -12256,6 +12429,43 @@ function createCliRuntime(dependencies = {}) {
|
|
|
12256
12429
|
}
|
|
12257
12430
|
});
|
|
12258
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
|
+
}
|
|
12259
12469
|
async function mock(selection, options) {
|
|
12260
12470
|
const pathOptions = toPathOptions2(options);
|
|
12261
12471
|
const daemonState = await readDaemonState(pathOptions);
|
|
@@ -12511,6 +12721,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
12511
12721
|
aiderNotify,
|
|
12512
12722
|
attach,
|
|
12513
12723
|
install,
|
|
12724
|
+
logger: logger2,
|
|
12514
12725
|
mock,
|
|
12515
12726
|
runDaemonProcess,
|
|
12516
12727
|
selfUpdateRun,
|
|
@@ -12723,6 +12934,8 @@ Examples:
|
|
|
12723
12934
|
aisnitch status
|
|
12724
12935
|
aisnitch attach
|
|
12725
12936
|
aisnitch attach --view full-data
|
|
12937
|
+
aisnitch logger
|
|
12938
|
+
aisnitch logger --tool claude-code
|
|
12726
12939
|
aisnitch setup claude-code
|
|
12727
12940
|
aisnitch setup aider
|
|
12728
12941
|
aisnitch setup gemini-cli
|
|
@@ -12741,6 +12954,7 @@ Examples:
|
|
|
12741
12954
|
addAdaptersCommand(program, runtime);
|
|
12742
12955
|
addSetupCommand(program, runtime);
|
|
12743
12956
|
addAttachCommand(program, runtime);
|
|
12957
|
+
addLoggerCommand(program, runtime);
|
|
12744
12958
|
addMockCommand(program, runtime);
|
|
12745
12959
|
addWrapCommand(program, runtime);
|
|
12746
12960
|
addInstallCommand(program, runtime);
|
|
@@ -12881,6 +13095,21 @@ function addWrapCommand(program, runtime) {
|
|
|
12881
13095
|
}
|
|
12882
13096
|
);
|
|
12883
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
|
+
}
|
|
12884
13113
|
function addInstallCommand(program, runtime) {
|
|
12885
13114
|
addCommonOptions(
|
|
12886
13115
|
program.command("install").description("Install a macOS LaunchAgent for AISnitch")
|