aisnitch 0.2.3 → 0.2.5
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 +312 -669
- package/dist/cli/index.cjs +219 -71
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +219 -71
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +207 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +207 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -482,15 +482,24 @@ var BaseAdapter = class {
|
|
|
482
482
|
cwd: data.cwd ?? context.cwd
|
|
483
483
|
}
|
|
484
484
|
});
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
485
|
+
let published;
|
|
486
|
+
try {
|
|
487
|
+
published = await this.publishEventImplementation(event, {
|
|
488
|
+
cwd: context.cwd,
|
|
489
|
+
env: context.env,
|
|
490
|
+
hookPayload: context.hookPayload,
|
|
491
|
+
pid: context.pid,
|
|
492
|
+
sessionId,
|
|
493
|
+
source: context.source,
|
|
494
|
+
transcriptPath: context.transcriptPath
|
|
495
|
+
});
|
|
496
|
+
} catch (error) {
|
|
497
|
+
logger.error(
|
|
498
|
+
{ error, eventType: type, adapter: this.name, sessionId },
|
|
499
|
+
"\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
|
|
500
|
+
);
|
|
501
|
+
published = false;
|
|
502
|
+
}
|
|
494
503
|
if (published) {
|
|
495
504
|
this.eventsEmitted += 1;
|
|
496
505
|
}
|
|
@@ -1476,6 +1485,9 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
1476
1485
|
});
|
|
1477
1486
|
const context = {
|
|
1478
1487
|
cwd: getString(payload, "cwd"),
|
|
1488
|
+
// 📖 Pass process.env so the context detector can detect the terminal
|
|
1489
|
+
// from TERM_PROGRAM, ITERM_SESSION_ID, etc. — hooks don't carry env vars
|
|
1490
|
+
env: this.env ?? process.env,
|
|
1479
1491
|
hookPayload: payload,
|
|
1480
1492
|
pid: getNumber(payload, "pid"),
|
|
1481
1493
|
sessionId,
|
|
@@ -1709,6 +1721,8 @@ function extractClaudeTranscriptObservations(payload, transcriptPath) {
|
|
|
1709
1721
|
const tokensUsed = extractTokenUsage(payload);
|
|
1710
1722
|
const rawPayload = payload;
|
|
1711
1723
|
const sharedContext = {
|
|
1724
|
+
// 📖 Pass process.env so terminal detection works from transcript path too
|
|
1725
|
+
env: process.env,
|
|
1712
1726
|
hookPayload: rawPayload,
|
|
1713
1727
|
sessionId,
|
|
1714
1728
|
source: "aisnitch://adapters/claude-code/transcript",
|
|
@@ -5365,6 +5379,8 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
5365
5379
|
});
|
|
5366
5380
|
const context = {
|
|
5367
5381
|
cwd: extractOpenCodeCwd(payload),
|
|
5382
|
+
// 📖 Pass process.env so the context detector can detect the terminal
|
|
5383
|
+
env: this.env ?? process.env,
|
|
5368
5384
|
hookPayload: payload,
|
|
5369
5385
|
pid: getNumber5(payload, "pid"),
|
|
5370
5386
|
sessionId,
|
|
@@ -5375,6 +5391,8 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
5375
5391
|
cwd: context.cwd,
|
|
5376
5392
|
errorMessage: extractOpenCodeErrorMessage(payload),
|
|
5377
5393
|
errorType: extractOpenCodeErrorType(payload),
|
|
5394
|
+
// 📖 Extract model from payload — OpenCode may send it as "model" or nested in properties
|
|
5395
|
+
model: getString7(payload, "model") ?? getString7(getRecord6(payload.properties), "model"),
|
|
5378
5396
|
project: extractOpenCodeProject(payload),
|
|
5379
5397
|
raw: payload,
|
|
5380
5398
|
toolInput: extractOpenCodeToolInput(payload),
|
|
@@ -5618,22 +5636,40 @@ var AdapterRegistry = class {
|
|
|
5618
5636
|
}
|
|
5619
5637
|
/**
|
|
5620
5638
|
* Starts every adapter enabled in the current AISnitch config.
|
|
5639
|
+
* 📖 Each adapter is started independently — one failure does not prevent
|
|
5640
|
+
* the others from starting.
|
|
5621
5641
|
*/
|
|
5622
5642
|
async startAll(config) {
|
|
5623
5643
|
for (const adapter of this.list()) {
|
|
5624
5644
|
if (config.adapters[adapter.name]?.enabled !== true) {
|
|
5625
5645
|
continue;
|
|
5626
5646
|
}
|
|
5627
|
-
|
|
5647
|
+
try {
|
|
5648
|
+
await adapter.start();
|
|
5649
|
+
} catch (error) {
|
|
5650
|
+
logger.error(
|
|
5651
|
+
{ error, adapter: adapter.name },
|
|
5652
|
+
`\u{1F4D6} Failed to start adapter "${adapter.name}" \u2014 skipping`
|
|
5653
|
+
);
|
|
5654
|
+
}
|
|
5628
5655
|
}
|
|
5629
5656
|
}
|
|
5630
5657
|
/**
|
|
5631
5658
|
* Stops every adapter in reverse registration order.
|
|
5659
|
+
* 📖 Each adapter is stopped independently — one failure does not prevent
|
|
5660
|
+
* the others from being stopped.
|
|
5632
5661
|
*/
|
|
5633
5662
|
async stopAll() {
|
|
5634
5663
|
const adapters = this.list().reverse();
|
|
5635
5664
|
for (const adapter of adapters) {
|
|
5636
|
-
|
|
5665
|
+
try {
|
|
5666
|
+
await adapter.stop();
|
|
5667
|
+
} catch (error) {
|
|
5668
|
+
logger.warn(
|
|
5669
|
+
{ error, adapter: adapter.name },
|
|
5670
|
+
`\u{1F4D6} Error stopping adapter "${adapter.name}" \u2014 continuing`
|
|
5671
|
+
);
|
|
5672
|
+
}
|
|
5637
5673
|
}
|
|
5638
5674
|
}
|
|
5639
5675
|
};
|
|
@@ -6596,8 +6632,16 @@ var EventBus = class {
|
|
|
6596
6632
|
},
|
|
6597
6633
|
"Published event"
|
|
6598
6634
|
);
|
|
6599
|
-
|
|
6600
|
-
|
|
6635
|
+
try {
|
|
6636
|
+
this.emitter.emit("event", parsedEvent.data);
|
|
6637
|
+
} catch (error) {
|
|
6638
|
+
logger.warn({ error, eventType: parsedEvent.data.type }, "\u{1F4D6} Error in EventBus global subscriber");
|
|
6639
|
+
}
|
|
6640
|
+
try {
|
|
6641
|
+
this.emitter.emit(`event:${parsedEvent.data.type}`, parsedEvent.data);
|
|
6642
|
+
} catch (error) {
|
|
6643
|
+
logger.warn({ error, eventType: parsedEvent.data.type }, "\u{1F4D6} Error in EventBus typed subscriber");
|
|
6644
|
+
}
|
|
6601
6645
|
return true;
|
|
6602
6646
|
}
|
|
6603
6647
|
/**
|
|
@@ -6878,6 +6922,7 @@ var WSServer = class {
|
|
|
6878
6922
|
});
|
|
6879
6923
|
socket.on("error", (error) => {
|
|
6880
6924
|
logger.warn({ error }, "WebSocket consumer error");
|
|
6925
|
+
this.consumers.delete(socket);
|
|
6881
6926
|
});
|
|
6882
6927
|
const welcomeMessage = {
|
|
6883
6928
|
type: "welcome",
|
|
@@ -7032,10 +7077,17 @@ var HTTPReceiver = class {
|
|
|
7032
7077
|
}
|
|
7033
7078
|
async handleRequest(request, response, options) {
|
|
7034
7079
|
this.requestCount += 1;
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7080
|
+
let requestUrl;
|
|
7081
|
+
try {
|
|
7082
|
+
requestUrl = new URL(
|
|
7083
|
+
request.url ?? "/",
|
|
7084
|
+
`http://${this.host}:${this.port ?? options.port}`
|
|
7085
|
+
);
|
|
7086
|
+
} catch {
|
|
7087
|
+
this.invalidRequestCount += 1;
|
|
7088
|
+
this.sendJson(response, 400, { error: "malformed request url" });
|
|
7089
|
+
return;
|
|
7090
|
+
}
|
|
7039
7091
|
if (request.method === "GET" && requestUrl.pathname === "/health") {
|
|
7040
7092
|
this.sendJson(response, 200, options.getHealthSnapshot());
|
|
7041
7093
|
return;
|
|
@@ -7380,26 +7432,32 @@ var Pipeline = class {
|
|
|
7380
7432
|
await adapter.handleHook(payload);
|
|
7381
7433
|
});
|
|
7382
7434
|
}
|
|
7435
|
+
try {
|
|
7436
|
+
this.wsPort = await this.wsServer.start({
|
|
7437
|
+
port: resolvedWsPort,
|
|
7438
|
+
eventBus: this.eventBus,
|
|
7439
|
+
activeTools
|
|
7440
|
+
});
|
|
7441
|
+
this.httpPort = await this.httpReceiver.start({
|
|
7442
|
+
port: resolvedHttpPort,
|
|
7443
|
+
onHook: async (tool, payload) => {
|
|
7444
|
+
await this.handleHook(tool, payload);
|
|
7445
|
+
},
|
|
7446
|
+
getHealthSnapshot: () => this.getHealthSnapshot()
|
|
7447
|
+
});
|
|
7448
|
+
this.socketPath = await this.udsServer.start({
|
|
7449
|
+
socketPath,
|
|
7450
|
+
onEvent: async (event) => {
|
|
7451
|
+
await this.publishEvent(event);
|
|
7452
|
+
}
|
|
7453
|
+
});
|
|
7454
|
+
await this.adapterRegistry.startAll(config);
|
|
7455
|
+
} catch (error) {
|
|
7456
|
+
logger.error({ error }, "\u{1F4D6} Pipeline start failed \u2014 rolling back already-started components");
|
|
7457
|
+
await this.rollbackPartialStart();
|
|
7458
|
+
throw error;
|
|
7459
|
+
}
|
|
7383
7460
|
this.startedAt = Date.now();
|
|
7384
|
-
this.wsPort = await this.wsServer.start({
|
|
7385
|
-
port: resolvedWsPort,
|
|
7386
|
-
eventBus: this.eventBus,
|
|
7387
|
-
activeTools
|
|
7388
|
-
});
|
|
7389
|
-
this.httpPort = await this.httpReceiver.start({
|
|
7390
|
-
port: resolvedHttpPort,
|
|
7391
|
-
onHook: async (tool, payload) => {
|
|
7392
|
-
await this.handleHook(tool, payload);
|
|
7393
|
-
},
|
|
7394
|
-
getHealthSnapshot: () => this.getHealthSnapshot()
|
|
7395
|
-
});
|
|
7396
|
-
this.socketPath = await this.udsServer.start({
|
|
7397
|
-
socketPath,
|
|
7398
|
-
onEvent: async (event) => {
|
|
7399
|
-
await this.publishEvent(event);
|
|
7400
|
-
}
|
|
7401
|
-
});
|
|
7402
|
-
await this.adapterRegistry.startAll(config);
|
|
7403
7461
|
logger.info(this.getStatus(), "Core pipeline started");
|
|
7404
7462
|
return this.getStatus();
|
|
7405
7463
|
}
|
|
@@ -7407,10 +7465,25 @@ var Pipeline = class {
|
|
|
7407
7465
|
* Stops every pipeline component in reverse dependency order.
|
|
7408
7466
|
*/
|
|
7409
7467
|
async stop() {
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7468
|
+
const stopSafely = async (label, fn) => {
|
|
7469
|
+
try {
|
|
7470
|
+
await fn();
|
|
7471
|
+
} catch (error) {
|
|
7472
|
+
logger.warn({ error }, `\u{1F4D6} Error while stopping ${label} \u2014 continuing shutdown`);
|
|
7473
|
+
}
|
|
7474
|
+
};
|
|
7475
|
+
await stopSafely("adapter registry", async () => {
|
|
7476
|
+
await this.adapterRegistry?.stopAll();
|
|
7477
|
+
});
|
|
7478
|
+
await stopSafely("HTTP receiver", async () => {
|
|
7479
|
+
await this.httpReceiver.stop();
|
|
7480
|
+
});
|
|
7481
|
+
await stopSafely("UDS server", async () => {
|
|
7482
|
+
await this.udsServer.stop();
|
|
7483
|
+
});
|
|
7484
|
+
await stopSafely("WebSocket server", async () => {
|
|
7485
|
+
await this.wsServer.stop();
|
|
7486
|
+
});
|
|
7414
7487
|
this.eventBus.unsubscribeAll();
|
|
7415
7488
|
this.adapterRegistry = null;
|
|
7416
7489
|
this.enabledTools.clear();
|
|
@@ -7427,11 +7500,50 @@ var Pipeline = class {
|
|
|
7427
7500
|
registerHookHandler(tool, handler) {
|
|
7428
7501
|
this.hookHandlers.set(tool, handler);
|
|
7429
7502
|
}
|
|
7503
|
+
/**
|
|
7504
|
+
* 📖 Rolls back any components that were successfully started before a
|
|
7505
|
+
* failure occurred, preventing orphaned servers or leaking resources.
|
|
7506
|
+
*/
|
|
7507
|
+
async rollbackPartialStart() {
|
|
7508
|
+
const stopSafe = async (label, fn) => {
|
|
7509
|
+
try {
|
|
7510
|
+
await fn();
|
|
7511
|
+
} catch (error) {
|
|
7512
|
+
logger.warn({ error }, `\u{1F4D6} Error rolling back ${label}`);
|
|
7513
|
+
}
|
|
7514
|
+
};
|
|
7515
|
+
await stopSafe("adapter registry", async () => {
|
|
7516
|
+
await this.adapterRegistry?.stopAll();
|
|
7517
|
+
});
|
|
7518
|
+
await stopSafe("UDS server", async () => {
|
|
7519
|
+
await this.udsServer.stop();
|
|
7520
|
+
});
|
|
7521
|
+
await stopSafe("HTTP receiver", async () => {
|
|
7522
|
+
await this.httpReceiver.stop();
|
|
7523
|
+
});
|
|
7524
|
+
await stopSafe("WebSocket server", async () => {
|
|
7525
|
+
await this.wsServer.stop();
|
|
7526
|
+
});
|
|
7527
|
+
this.adapterRegistry = null;
|
|
7528
|
+
this.enabledTools.clear();
|
|
7529
|
+
this.hookHandlers.clear();
|
|
7530
|
+
}
|
|
7430
7531
|
/**
|
|
7431
7532
|
* Publishes an event after best-effort context enrichment.
|
|
7533
|
+
* 📖 If enrichment fails, the original event is published un-enriched
|
|
7534
|
+
* rather than being dropped entirely.
|
|
7432
7535
|
*/
|
|
7433
7536
|
async publishEvent(event, context = {}) {
|
|
7434
|
-
|
|
7537
|
+
let enrichedEvent;
|
|
7538
|
+
try {
|
|
7539
|
+
enrichedEvent = await this.contextDetector.enrich(event, context);
|
|
7540
|
+
} catch (error) {
|
|
7541
|
+
logger.warn(
|
|
7542
|
+
{ error, eventId: event.id },
|
|
7543
|
+
"\u{1F4D6} Context enrichment failed \u2014 publishing un-enriched event"
|
|
7544
|
+
);
|
|
7545
|
+
enrichedEvent = event;
|
|
7546
|
+
}
|
|
7435
7547
|
return this.eventBus.publish(enrichedEvent);
|
|
7436
7548
|
}
|
|
7437
7549
|
/**
|
|
@@ -7467,6 +7579,16 @@ var Pipeline = class {
|
|
|
7467
7579
|
};
|
|
7468
7580
|
}
|
|
7469
7581
|
async handleHook(tool, payload) {
|
|
7582
|
+
try {
|
|
7583
|
+
await this.handleHookInner(tool, payload);
|
|
7584
|
+
} catch (error) {
|
|
7585
|
+
logger.error(
|
|
7586
|
+
{ error, tool },
|
|
7587
|
+
"\u{1F4D6} Unhandled error in hook handler \u2014 swallowing to prevent daemon crash"
|
|
7588
|
+
);
|
|
7589
|
+
}
|
|
7590
|
+
}
|
|
7591
|
+
async handleHookInner(tool, payload) {
|
|
7470
7592
|
if (!this.enabledTools.has(tool)) {
|
|
7471
7593
|
logger.debug({ tool }, "Ignoring hook for disabled tool");
|
|
7472
7594
|
return;
|
|
@@ -8642,18 +8764,22 @@ function parseSocketPayload(data) {
|
|
|
8642
8764
|
return parsedEvent.success ? parsedEvent.data : null;
|
|
8643
8765
|
}
|
|
8644
8766
|
function parseUnknownPayload(data) {
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8767
|
+
try {
|
|
8768
|
+
if (typeof data === "string") {
|
|
8769
|
+
return JSON.parse(data);
|
|
8770
|
+
}
|
|
8771
|
+
if (Array.isArray(data)) {
|
|
8772
|
+
return JSON.parse(Buffer.concat(data).toString("utf8"));
|
|
8773
|
+
}
|
|
8774
|
+
if (data instanceof ArrayBuffer) {
|
|
8775
|
+
return JSON.parse(
|
|
8776
|
+
Buffer.from(new Uint8Array(data)).toString("utf8")
|
|
8777
|
+
);
|
|
8778
|
+
}
|
|
8779
|
+
return JSON.parse(Buffer.from(data).toString("utf8"));
|
|
8780
|
+
} catch {
|
|
8781
|
+
return null;
|
|
8655
8782
|
}
|
|
8656
|
-
return JSON.parse(Buffer.from(data).toString("utf8"));
|
|
8657
8783
|
}
|
|
8658
8784
|
|
|
8659
8785
|
// src/tui/hooks/useKeyBinds.ts
|
|
@@ -9381,16 +9507,20 @@ function ManagedDaemonApp({
|
|
|
9381
9507
|
}
|
|
9382
9508
|
function parseSocketPayload2(data) {
|
|
9383
9509
|
let parsedPayload;
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9510
|
+
try {
|
|
9511
|
+
if (typeof data === "string") {
|
|
9512
|
+
parsedPayload = JSON.parse(data);
|
|
9513
|
+
} else if (Array.isArray(data)) {
|
|
9514
|
+
parsedPayload = JSON.parse(Buffer.concat(data).toString("utf8"));
|
|
9515
|
+
} else if (data instanceof ArrayBuffer) {
|
|
9516
|
+
parsedPayload = JSON.parse(
|
|
9517
|
+
Buffer.from(new Uint8Array(data)).toString("utf8")
|
|
9518
|
+
);
|
|
9519
|
+
} else {
|
|
9520
|
+
parsedPayload = JSON.parse(Buffer.from(data).toString("utf8"));
|
|
9521
|
+
}
|
|
9522
|
+
} catch {
|
|
9523
|
+
return null;
|
|
9394
9524
|
}
|
|
9395
9525
|
if (typeof parsedPayload === "object" && parsedPayload !== null && "type" in parsedPayload && parsedPayload.type === "welcome") {
|
|
9396
9526
|
return null;
|
|
@@ -9461,18 +9591,22 @@ async function attachWebSocketMonitor(url, output) {
|
|
|
9461
9591
|
};
|
|
9462
9592
|
}
|
|
9463
9593
|
function parseSocketMessage(data) {
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9594
|
+
try {
|
|
9595
|
+
if (typeof data === "string") {
|
|
9596
|
+
return JSON.parse(data);
|
|
9597
|
+
}
|
|
9598
|
+
if (Array.isArray(data)) {
|
|
9599
|
+
return JSON.parse(Buffer.concat(data).toString("utf8"));
|
|
9600
|
+
}
|
|
9601
|
+
if (data instanceof ArrayBuffer) {
|
|
9602
|
+
return JSON.parse(
|
|
9603
|
+
Buffer.from(new Uint8Array(data)).toString("utf8")
|
|
9604
|
+
);
|
|
9605
|
+
}
|
|
9606
|
+
return JSON.parse(Buffer.from(data).toString("utf8"));
|
|
9607
|
+
} catch {
|
|
9608
|
+
return null;
|
|
9474
9609
|
}
|
|
9475
|
-
return JSON.parse(Buffer.from(data).toString("utf8"));
|
|
9476
9610
|
}
|
|
9477
9611
|
function isWelcomeMessage(payload) {
|
|
9478
9612
|
if (typeof payload !== "object" || payload === null) {
|