aisnitch 0.2.20 → 0.2.21
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/dist/cli/index.cjs +849 -87
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +825 -63
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +7182 -6485
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +156 -4
- package/dist/index.d.ts +156 -4
- package/dist/index.js +7154 -6464
- 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.21";
|
|
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
|
|
@@ -49,6 +49,8 @@ var TOOL_NAMES = [
|
|
|
49
49
|
"kiro",
|
|
50
50
|
"augment-code",
|
|
51
51
|
"mistral",
|
|
52
|
+
"zed",
|
|
53
|
+
"pi",
|
|
52
54
|
"unknown"
|
|
53
55
|
];
|
|
54
56
|
var ERROR_TYPES = [
|
|
@@ -96,6 +98,11 @@ var ToolInputSchema = z.strictObject({
|
|
|
96
98
|
(value) => value.filePath !== void 0 || value.command !== void 0,
|
|
97
99
|
"toolInput must include filePath or command"
|
|
98
100
|
);
|
|
101
|
+
var ThinkingContentSchema = z.string().max(1e5).describe("Raw thinking/reasoning content from the AI model");
|
|
102
|
+
var ToolCallNameSchema = z.string().min(1).max(100).describe("Name of the tool being invoked (e.g., Edit, Bash, Grep)");
|
|
103
|
+
var FinalMessageSchema = z.string().max(5e4).describe("End-of-run summary or completion message");
|
|
104
|
+
var ToolResultSchema = z.string().max(1e4).describe("Tool execution result or output");
|
|
105
|
+
var MessageContentSchema = z.string().max(1e5).describe("Raw text content from AI messages");
|
|
99
106
|
var ToolNameSchema = z.enum(TOOL_NAMES);
|
|
100
107
|
var AISnitchEventTypeSchema = z.enum(AISNITCH_EVENT_TYPES);
|
|
101
108
|
var ErrorTypeSchema = z.enum(ERROR_TYPES);
|
|
@@ -118,7 +125,13 @@ var EventDataSchema = z.strictObject({
|
|
|
118
125
|
pid: z.number().int().positive().optional(),
|
|
119
126
|
instanceId: z.string().min(1).max(255).optional(),
|
|
120
127
|
instanceIndex: z.number().int().min(1).optional(),
|
|
121
|
-
instanceTotal: z.number().int().min(1).optional()
|
|
128
|
+
instanceTotal: z.number().int().min(1).optional(),
|
|
129
|
+
// New fields for enhanced content capture
|
|
130
|
+
thinkingContent: ThinkingContentSchema.optional(),
|
|
131
|
+
toolCallName: ToolCallNameSchema.optional(),
|
|
132
|
+
finalMessage: FinalMessageSchema.optional(),
|
|
133
|
+
toolResult: ToolResultSchema.optional(),
|
|
134
|
+
messageContent: MessageContentSchema.optional()
|
|
122
135
|
});
|
|
123
136
|
var AISnitchEventSchema = z.strictObject({
|
|
124
137
|
specversion: z.literal("1.0"),
|
|
@@ -2432,11 +2445,11 @@ function toConfigPathOptions(options) {
|
|
|
2432
2445
|
// src/cli/runtime.ts
|
|
2433
2446
|
import { execFile as execFileCallback14, spawn as spawnChildProcess2 } from "child_process";
|
|
2434
2447
|
import { closeSync, openSync } from "fs";
|
|
2435
|
-
import { mkdtemp, readFile as
|
|
2448
|
+
import { mkdtemp, readFile as readFile15, rename, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
2436
2449
|
import { createConnection as createConnection3 } from "net";
|
|
2437
2450
|
import { tmpdir } from "os";
|
|
2438
|
-
import { basename as
|
|
2439
|
-
import { promisify as
|
|
2451
|
+
import { basename as basename12, join as join18 } from "path";
|
|
2452
|
+
import { promisify as promisify15 } from "util";
|
|
2440
2453
|
|
|
2441
2454
|
// src/adapters/generic-pty.ts
|
|
2442
2455
|
import { basename as basename3 } from "path";
|
|
@@ -2507,9 +2520,11 @@ var TOOL_BINARY_MAP = {
|
|
|
2507
2520
|
"openhands": "openhands",
|
|
2508
2521
|
"openclaw": "openclaw",
|
|
2509
2522
|
"opencode": "opencode",
|
|
2523
|
+
"pi": "pi",
|
|
2510
2524
|
"qwen-code": "qwen",
|
|
2511
2525
|
"unknown": "unknown",
|
|
2512
|
-
"windsurf": "windsurf"
|
|
2526
|
+
"windsurf": "windsurf",
|
|
2527
|
+
"zed": "zed"
|
|
2513
2528
|
};
|
|
2514
2529
|
var ContextDetector = class {
|
|
2515
2530
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -4370,7 +4385,7 @@ var UDSServer = class {
|
|
|
4370
4385
|
};
|
|
4371
4386
|
|
|
4372
4387
|
// src/core/engine/pipeline.ts
|
|
4373
|
-
import { join as
|
|
4388
|
+
import { join as join15 } from "path";
|
|
4374
4389
|
import { z as z4 } from "zod";
|
|
4375
4390
|
|
|
4376
4391
|
// src/adapters/aider.ts
|
|
@@ -4477,20 +4492,29 @@ var BaseAdapter = class {
|
|
|
4477
4492
|
});
|
|
4478
4493
|
let published;
|
|
4479
4494
|
try {
|
|
4480
|
-
published = await
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4495
|
+
published = await SHARED_BREAKERS.adapterEmit.execute(async () => {
|
|
4496
|
+
return await this.publishEventImplementation(event, {
|
|
4497
|
+
cwd: context.cwd,
|
|
4498
|
+
env: context.env,
|
|
4499
|
+
hookPayload: context.hookPayload,
|
|
4500
|
+
pid: context.pid,
|
|
4501
|
+
sessionId,
|
|
4502
|
+
source: context.source,
|
|
4503
|
+
transcriptPath: context.transcriptPath
|
|
4504
|
+
});
|
|
4488
4505
|
});
|
|
4489
4506
|
} catch (error) {
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4507
|
+
if (error instanceof Error && error.name === "CircuitOpenError") {
|
|
4508
|
+
logger.warn(
|
|
4509
|
+
{ error, eventType: type, adapter: this.name },
|
|
4510
|
+
"\u{1F4D6} Adapter emit blocked by open circuit \u2014 event dropped"
|
|
4511
|
+
);
|
|
4512
|
+
} else {
|
|
4513
|
+
logger.error(
|
|
4514
|
+
{ error, eventType: type, adapter: this.name, sessionId },
|
|
4515
|
+
"\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
|
|
4516
|
+
);
|
|
4517
|
+
}
|
|
4494
4518
|
published = false;
|
|
4495
4519
|
}
|
|
4496
4520
|
if (published) {
|
|
@@ -5504,7 +5528,11 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
5504
5528
|
return;
|
|
5505
5529
|
}
|
|
5506
5530
|
case "SessionEnd": {
|
|
5507
|
-
|
|
5531
|
+
const finalMessage = extractFinalMessageFromPayload(payload);
|
|
5532
|
+
await this.emitStateChange("session.end", {
|
|
5533
|
+
...sharedData,
|
|
5534
|
+
finalMessage
|
|
5535
|
+
}, context);
|
|
5508
5536
|
return;
|
|
5509
5537
|
}
|
|
5510
5538
|
case "UserPromptSubmit":
|
|
@@ -5521,12 +5549,22 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
5521
5549
|
return;
|
|
5522
5550
|
}
|
|
5523
5551
|
case "PreToolUse": {
|
|
5524
|
-
|
|
5552
|
+
const toolCallName = extractToolNameFromPayload(payload);
|
|
5553
|
+
await this.emitStateChange("agent.tool_call", {
|
|
5554
|
+
...sharedData,
|
|
5555
|
+
toolCallName
|
|
5556
|
+
}, context);
|
|
5525
5557
|
return;
|
|
5526
5558
|
}
|
|
5527
5559
|
case "PostToolUse": {
|
|
5560
|
+
const toolCallName = extractToolNameFromPayload(payload);
|
|
5561
|
+
const toolResult = extractToolResultFromPayload(payload);
|
|
5528
5562
|
const emittedType = isClaudeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
|
|
5529
|
-
await this.emitStateChange(emittedType,
|
|
5563
|
+
await this.emitStateChange(emittedType, {
|
|
5564
|
+
...sharedData,
|
|
5565
|
+
toolCallName,
|
|
5566
|
+
toolResult
|
|
5567
|
+
}, context);
|
|
5530
5568
|
return;
|
|
5531
5569
|
}
|
|
5532
5570
|
case "PostToolUseFailure":
|
|
@@ -5728,21 +5766,50 @@ function extractClaudeTranscriptObservations(payload, transcriptPath) {
|
|
|
5728
5766
|
};
|
|
5729
5767
|
const observations = [];
|
|
5730
5768
|
if (contentParts.some((part) => part.type === "thinking")) {
|
|
5769
|
+
const thinkingParts = contentParts.filter((part) => part.type === "thinking");
|
|
5770
|
+
const thinkingText = thinkingParts.map((part) => {
|
|
5771
|
+
const text = part.text;
|
|
5772
|
+
return typeof text === "string" ? text : void 0;
|
|
5773
|
+
}).filter((text) => text !== void 0).join("\n");
|
|
5731
5774
|
observations.push({
|
|
5732
5775
|
context: sharedContext,
|
|
5733
|
-
data:
|
|
5776
|
+
data: {
|
|
5777
|
+
...sharedData,
|
|
5778
|
+
thinkingContent: thinkingText.length > 0 ? thinkingText : void 0
|
|
5779
|
+
},
|
|
5734
5780
|
type: "agent.thinking"
|
|
5735
5781
|
});
|
|
5736
5782
|
}
|
|
5737
5783
|
if (contentParts.some(
|
|
5738
5784
|
(part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0
|
|
5739
5785
|
)) {
|
|
5786
|
+
const messageTexts = contentParts.filter((part) => part.type === "text").map((part) => part.text).filter((text) => text.trim().length > 0);
|
|
5787
|
+
const messageContent = messageTexts.join("\n");
|
|
5740
5788
|
observations.push({
|
|
5741
5789
|
context: sharedContext,
|
|
5742
|
-
data:
|
|
5790
|
+
data: {
|
|
5791
|
+
...sharedData,
|
|
5792
|
+
messageContent: messageContent.length > 0 ? messageContent : void 0
|
|
5793
|
+
},
|
|
5743
5794
|
type: "agent.streaming"
|
|
5744
5795
|
});
|
|
5745
5796
|
}
|
|
5797
|
+
const toolUseParts = contentParts.filter(
|
|
5798
|
+
(part) => part.type === "tool_use" || part.type === "toolUse"
|
|
5799
|
+
);
|
|
5800
|
+
if (toolUseParts.length > 0) {
|
|
5801
|
+
const toolName = getString(toolUseParts[0], "name") ?? getString(toolUseParts[0], "tool");
|
|
5802
|
+
if (toolName) {
|
|
5803
|
+
observations.push({
|
|
5804
|
+
context: sharedContext,
|
|
5805
|
+
data: {
|
|
5806
|
+
...sharedData,
|
|
5807
|
+
toolCallName: toolName
|
|
5808
|
+
},
|
|
5809
|
+
type: "agent.tool_call"
|
|
5810
|
+
});
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5746
5813
|
return observations;
|
|
5747
5814
|
}
|
|
5748
5815
|
function extractClaudeContentParts(payload) {
|
|
@@ -5865,6 +5932,51 @@ function getString(payload, key) {
|
|
|
5865
5932
|
const value = payload[key];
|
|
5866
5933
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
5867
5934
|
}
|
|
5935
|
+
function extractToolNameFromPayload(payload) {
|
|
5936
|
+
const directToolName = getString(payload, "tool_name") ?? getString(payload, "toolName");
|
|
5937
|
+
if (directToolName) {
|
|
5938
|
+
return directToolName;
|
|
5939
|
+
}
|
|
5940
|
+
const toolUse = getRecord(payload.tool_use) ?? getRecord(payload.toolUse);
|
|
5941
|
+
if (toolUse) {
|
|
5942
|
+
return getString(toolUse, "name") ?? getString(toolUse, "tool");
|
|
5943
|
+
}
|
|
5944
|
+
const toolInput = getRecord(payload.tool_input) ?? getRecord(payload.toolInput);
|
|
5945
|
+
if (toolInput) {
|
|
5946
|
+
return getString(toolInput, "tool_name") ?? getString(toolInput, "type");
|
|
5947
|
+
}
|
|
5948
|
+
return void 0;
|
|
5949
|
+
}
|
|
5950
|
+
function extractToolResultFromPayload(payload) {
|
|
5951
|
+
const directResult = getString(payload, "result") ?? getString(payload, "output");
|
|
5952
|
+
if (directResult) {
|
|
5953
|
+
return directResult;
|
|
5954
|
+
}
|
|
5955
|
+
const toolResult = getRecord(payload.tool_result) ?? getRecord(payload.toolResult);
|
|
5956
|
+
if (toolResult) {
|
|
5957
|
+
return getString(toolResult, "content") ?? getString(toolResult, "output");
|
|
5958
|
+
}
|
|
5959
|
+
const errorField = getString(payload, "error") ?? getString(payload, "error_message");
|
|
5960
|
+
if (errorField) {
|
|
5961
|
+
return errorField;
|
|
5962
|
+
}
|
|
5963
|
+
return void 0;
|
|
5964
|
+
}
|
|
5965
|
+
function extractFinalMessageFromPayload(payload) {
|
|
5966
|
+
const directMessage = getString(payload, "final_message") ?? getString(payload, "finalMessage") ?? getString(payload, "summary") ?? getString(payload, "completion_message");
|
|
5967
|
+
if (directMessage) {
|
|
5968
|
+
return directMessage;
|
|
5969
|
+
}
|
|
5970
|
+
const result = getString(payload, "result") ?? getString(payload, "output") ?? getString(payload, "message");
|
|
5971
|
+
if (result) {
|
|
5972
|
+
return result;
|
|
5973
|
+
}
|
|
5974
|
+
const stats = getRecord(payload.stats);
|
|
5975
|
+
if (stats) {
|
|
5976
|
+
return getString(stats, "summary") ?? getString(stats, "completion_summary");
|
|
5977
|
+
}
|
|
5978
|
+
return void 0;
|
|
5979
|
+
}
|
|
5868
5980
|
|
|
5869
5981
|
// src/adapters/copilot-cli.ts
|
|
5870
5982
|
import { execFile as execFileCallback5 } from "child_process";
|
|
@@ -10838,7 +10950,11 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
10838
10950
|
return;
|
|
10839
10951
|
}
|
|
10840
10952
|
case "session.deleted": {
|
|
10841
|
-
|
|
10953
|
+
const finalMessage = extractOpenCodeFinalMessage(payload);
|
|
10954
|
+
await this.emitStateChange("session.end", {
|
|
10955
|
+
...sharedData,
|
|
10956
|
+
finalMessage
|
|
10957
|
+
}, context);
|
|
10842
10958
|
return;
|
|
10843
10959
|
}
|
|
10844
10960
|
case "session.error": {
|
|
@@ -10863,12 +10979,22 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
10863
10979
|
return;
|
|
10864
10980
|
}
|
|
10865
10981
|
case "tool.execute.before": {
|
|
10866
|
-
|
|
10982
|
+
const toolCallName = extractOpenCodeToolName(payload);
|
|
10983
|
+
await this.emitStateChange("agent.tool_call", {
|
|
10984
|
+
...sharedData,
|
|
10985
|
+
toolCallName
|
|
10986
|
+
}, context);
|
|
10867
10987
|
return;
|
|
10868
10988
|
}
|
|
10869
10989
|
case "tool.execute.after": {
|
|
10990
|
+
const toolCallName = extractOpenCodeToolName(payload);
|
|
10991
|
+
const toolResult = extractOpenCodeToolResult(payload);
|
|
10870
10992
|
const emittedType = isOpenCodeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
|
|
10871
|
-
await this.emitStateChange(emittedType,
|
|
10993
|
+
await this.emitStateChange(emittedType, {
|
|
10994
|
+
...sharedData,
|
|
10995
|
+
toolCallName,
|
|
10996
|
+
toolResult
|
|
10997
|
+
}, context);
|
|
10872
10998
|
return;
|
|
10873
10999
|
}
|
|
10874
11000
|
default: {
|
|
@@ -11035,6 +11161,535 @@ function getString10(payload, key) {
|
|
|
11035
11161
|
const value = payload[key];
|
|
11036
11162
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
11037
11163
|
}
|
|
11164
|
+
function extractOpenCodeFinalMessage(payload) {
|
|
11165
|
+
const directMessage = getString10(payload, "final_message") ?? getString10(payload, "finalMessage") ?? getString10(payload, "summary") ?? getString10(payload, "completion_message");
|
|
11166
|
+
if (directMessage) {
|
|
11167
|
+
return directMessage;
|
|
11168
|
+
}
|
|
11169
|
+
const result = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(getRecord9(payload.properties), "result");
|
|
11170
|
+
if (result) {
|
|
11171
|
+
return result;
|
|
11172
|
+
}
|
|
11173
|
+
return void 0;
|
|
11174
|
+
}
|
|
11175
|
+
function extractOpenCodeToolResult(payload) {
|
|
11176
|
+
const directResult = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(payload, "toolResult");
|
|
11177
|
+
if (directResult) {
|
|
11178
|
+
return directResult;
|
|
11179
|
+
}
|
|
11180
|
+
const toolResult = getRecord9(payload.tool_result) ?? getRecord9(payload.toolResult);
|
|
11181
|
+
if (toolResult) {
|
|
11182
|
+
return getString10(toolResult, "content") ?? getString10(toolResult, "output");
|
|
11183
|
+
}
|
|
11184
|
+
const props = getRecord9(payload.properties);
|
|
11185
|
+
if (props) {
|
|
11186
|
+
const nestedResult = getRecord9(props.tool_result) ?? getRecord9(props.toolResult);
|
|
11187
|
+
if (nestedResult) {
|
|
11188
|
+
return getString10(nestedResult, "content") ?? getString10(nestedResult, "output");
|
|
11189
|
+
}
|
|
11190
|
+
}
|
|
11191
|
+
return void 0;
|
|
11192
|
+
}
|
|
11193
|
+
|
|
11194
|
+
// src/adapters/pi.ts
|
|
11195
|
+
import { execFile as execFile14 } from "child_process";
|
|
11196
|
+
import { join as join13 } from "path";
|
|
11197
|
+
import { promisify as promisify14 } from "util";
|
|
11198
|
+
var execFileAsync = promisify14(execFile14);
|
|
11199
|
+
var PiAdapter = class extends BaseAdapter {
|
|
11200
|
+
displayName = "Pi (MiniMax)";
|
|
11201
|
+
name = "pi";
|
|
11202
|
+
strategies = [
|
|
11203
|
+
"process-detect",
|
|
11204
|
+
"api-client",
|
|
11205
|
+
"log-watch"
|
|
11206
|
+
];
|
|
11207
|
+
apiPort = 7890;
|
|
11208
|
+
logPath;
|
|
11209
|
+
poller = null;
|
|
11210
|
+
activePiSessions = /* @__PURE__ */ new Map();
|
|
11211
|
+
lastCheckedTime = 0;
|
|
11212
|
+
constructor(options) {
|
|
11213
|
+
super(options);
|
|
11214
|
+
this.logPath = join13(
|
|
11215
|
+
options.homeDirectory ?? process.env.HOME ?? "",
|
|
11216
|
+
".pi",
|
|
11217
|
+
"agent.log"
|
|
11218
|
+
);
|
|
11219
|
+
}
|
|
11220
|
+
start() {
|
|
11221
|
+
if (this.getStatus().running) {
|
|
11222
|
+
return Promise.resolve();
|
|
11223
|
+
}
|
|
11224
|
+
this.setRunning(true);
|
|
11225
|
+
this.startPolling();
|
|
11226
|
+
logger.info({ adapter: this.name }, "Pi adapter started");
|
|
11227
|
+
return Promise.resolve();
|
|
11228
|
+
}
|
|
11229
|
+
stop() {
|
|
11230
|
+
if (this.poller !== null) {
|
|
11231
|
+
clearInterval(this.poller);
|
|
11232
|
+
this.poller = null;
|
|
11233
|
+
}
|
|
11234
|
+
this.setRunning(false);
|
|
11235
|
+
logger.info({ adapter: this.name }, "Pi adapter stopped");
|
|
11236
|
+
return Promise.resolve();
|
|
11237
|
+
}
|
|
11238
|
+
async handleHook(payload) {
|
|
11239
|
+
const normalized = this.parseNormalizedHookPayload(payload);
|
|
11240
|
+
if (normalized === null) {
|
|
11241
|
+
return;
|
|
11242
|
+
}
|
|
11243
|
+
const context = {
|
|
11244
|
+
cwd: normalized.cwd,
|
|
11245
|
+
pid: normalized.pid,
|
|
11246
|
+
sessionId: normalized.sessionId,
|
|
11247
|
+
source: "pi-hook"
|
|
11248
|
+
};
|
|
11249
|
+
const eventType = this.mapEventType(normalized.type ?? "");
|
|
11250
|
+
const eventData = this.buildEventData(eventType, normalized);
|
|
11251
|
+
await this.emit(eventType, eventData, context);
|
|
11252
|
+
}
|
|
11253
|
+
startPolling() {
|
|
11254
|
+
this.poller = setInterval(() => {
|
|
11255
|
+
void this.pollPiActivity();
|
|
11256
|
+
}, 2e3);
|
|
11257
|
+
}
|
|
11258
|
+
async pollPiActivity() {
|
|
11259
|
+
const running = await this.detectPiInstance();
|
|
11260
|
+
if (!running) {
|
|
11261
|
+
for (const [sessionId, activity] of this.activePiSessions) {
|
|
11262
|
+
if (activity.state !== "idle") {
|
|
11263
|
+
activity.state = "idle";
|
|
11264
|
+
await this.emitIdle(sessionId);
|
|
11265
|
+
}
|
|
11266
|
+
}
|
|
11267
|
+
return;
|
|
11268
|
+
}
|
|
11269
|
+
try {
|
|
11270
|
+
const response = await fetch(
|
|
11271
|
+
`http://127.0.0.1:${this.apiPort}/api/status`,
|
|
11272
|
+
{
|
|
11273
|
+
signal: AbortSignal.timeout(500)
|
|
11274
|
+
}
|
|
11275
|
+
);
|
|
11276
|
+
if (response.ok) {
|
|
11277
|
+
const data = await response.json();
|
|
11278
|
+
await this.processPiApiResponse(data);
|
|
11279
|
+
}
|
|
11280
|
+
} catch {
|
|
11281
|
+
await this.checkMiniMaxApi();
|
|
11282
|
+
}
|
|
11283
|
+
}
|
|
11284
|
+
async detectPiInstance() {
|
|
11285
|
+
try {
|
|
11286
|
+
const result = await execFileAsync("pgrep", ["-l", "pi|minimax"]);
|
|
11287
|
+
if (result.stdout.includes("pi") || result.stdout.includes("minimax")) {
|
|
11288
|
+
return true;
|
|
11289
|
+
}
|
|
11290
|
+
} catch {
|
|
11291
|
+
}
|
|
11292
|
+
try {
|
|
11293
|
+
const response = await fetch(
|
|
11294
|
+
`http://127.0.0.1:${this.apiPort}/health`,
|
|
11295
|
+
{
|
|
11296
|
+
signal: AbortSignal.timeout(200)
|
|
11297
|
+
}
|
|
11298
|
+
);
|
|
11299
|
+
if (response.ok) {
|
|
11300
|
+
return true;
|
|
11301
|
+
}
|
|
11302
|
+
} catch {
|
|
11303
|
+
}
|
|
11304
|
+
return false;
|
|
11305
|
+
}
|
|
11306
|
+
async checkMiniMaxApi() {
|
|
11307
|
+
try {
|
|
11308
|
+
const response = await fetch("http://127.0.0.1:3000/api/agent/status", {
|
|
11309
|
+
signal: AbortSignal.timeout(500)
|
|
11310
|
+
});
|
|
11311
|
+
if (response.ok) {
|
|
11312
|
+
const data = await response.json();
|
|
11313
|
+
await this.processPiApiResponse(data);
|
|
11314
|
+
}
|
|
11315
|
+
} catch {
|
|
11316
|
+
}
|
|
11317
|
+
}
|
|
11318
|
+
async processPiApiResponse(data) {
|
|
11319
|
+
const rawSession = data.sessionId ?? data.project ?? "default";
|
|
11320
|
+
const sessionId = `pi:${rawSession.replace(/[^a-zA-Z0-9-_]/g, "-")}`;
|
|
11321
|
+
let activity = this.activePiSessions.get(sessionId);
|
|
11322
|
+
if (!activity) {
|
|
11323
|
+
activity = { sessionId, state: "idle" };
|
|
11324
|
+
this.activePiSessions.set(sessionId, activity);
|
|
11325
|
+
await this.emitSessionStart(sessionId, data);
|
|
11326
|
+
}
|
|
11327
|
+
const rawState = data.state ?? "idle";
|
|
11328
|
+
const state = rawState;
|
|
11329
|
+
if (state !== activity.state) {
|
|
11330
|
+
switch (state) {
|
|
11331
|
+
case "thinking": {
|
|
11332
|
+
const rawThinking = data.thinking;
|
|
11333
|
+
if (rawThinking) {
|
|
11334
|
+
await this.emitThinking(sessionId, rawThinking);
|
|
11335
|
+
}
|
|
11336
|
+
break;
|
|
11337
|
+
}
|
|
11338
|
+
case "tool": {
|
|
11339
|
+
const rawFilePath = data.filePath;
|
|
11340
|
+
const rawCommand = data.command;
|
|
11341
|
+
const rawToolName = data.toolName ?? "unknown";
|
|
11342
|
+
await this.emitToolCall(
|
|
11343
|
+
sessionId,
|
|
11344
|
+
{
|
|
11345
|
+
filePath: rawFilePath ?? "",
|
|
11346
|
+
command: rawCommand ?? ""
|
|
11347
|
+
},
|
|
11348
|
+
rawToolName
|
|
11349
|
+
);
|
|
11350
|
+
break;
|
|
11351
|
+
}
|
|
11352
|
+
case "output": {
|
|
11353
|
+
const rawOutput = data.output;
|
|
11354
|
+
if (rawOutput) {
|
|
11355
|
+
await this.emitOutput(sessionId, rawOutput);
|
|
11356
|
+
}
|
|
11357
|
+
break;
|
|
11358
|
+
}
|
|
11359
|
+
case "error": {
|
|
11360
|
+
const rawError = data.error ?? "Unknown error";
|
|
11361
|
+
await this.emitError(sessionId, rawError);
|
|
11362
|
+
break;
|
|
11363
|
+
}
|
|
11364
|
+
case "idle":
|
|
11365
|
+
await this.emitIdle(sessionId);
|
|
11366
|
+
break;
|
|
11367
|
+
}
|
|
11368
|
+
activity.state = state;
|
|
11369
|
+
}
|
|
11370
|
+
}
|
|
11371
|
+
async emitSessionStart(sessionId, data) {
|
|
11372
|
+
const rawProject = data.project ?? "pi-project";
|
|
11373
|
+
const rawModel = data.model ?? "minimax/moonshot";
|
|
11374
|
+
const eventData = {
|
|
11375
|
+
state: "session.start",
|
|
11376
|
+
project: rawProject,
|
|
11377
|
+
model: rawModel,
|
|
11378
|
+
raw: data
|
|
11379
|
+
};
|
|
11380
|
+
await this.emit("session.start", eventData, { sessionId });
|
|
11381
|
+
}
|
|
11382
|
+
async emitThinking(sessionId, content) {
|
|
11383
|
+
if (!content) return;
|
|
11384
|
+
const eventData = {
|
|
11385
|
+
state: "agent.thinking",
|
|
11386
|
+
thinkingContent: content
|
|
11387
|
+
};
|
|
11388
|
+
await this.emit("agent.thinking", eventData, { sessionId });
|
|
11389
|
+
}
|
|
11390
|
+
async emitToolCall(sessionId, toolInput, toolName) {
|
|
11391
|
+
const eventData = {
|
|
11392
|
+
state: "agent.tool_call",
|
|
11393
|
+
toolCallName: toolName,
|
|
11394
|
+
toolInput,
|
|
11395
|
+
activeFile: toolInput.filePath
|
|
11396
|
+
};
|
|
11397
|
+
await this.emit("agent.tool_call", eventData, { sessionId });
|
|
11398
|
+
}
|
|
11399
|
+
async emitOutput(sessionId, content) {
|
|
11400
|
+
if (!content) return;
|
|
11401
|
+
const eventData = {
|
|
11402
|
+
state: "agent.streaming",
|
|
11403
|
+
messageContent: content
|
|
11404
|
+
};
|
|
11405
|
+
await this.emit("agent.streaming", eventData, { sessionId });
|
|
11406
|
+
}
|
|
11407
|
+
async emitError(sessionId, errorMessage) {
|
|
11408
|
+
const eventData = {
|
|
11409
|
+
state: "agent.error",
|
|
11410
|
+
errorMessage,
|
|
11411
|
+
errorType: "api_error"
|
|
11412
|
+
};
|
|
11413
|
+
await this.emit("agent.error", eventData, { sessionId });
|
|
11414
|
+
}
|
|
11415
|
+
async emitIdle(sessionId) {
|
|
11416
|
+
const eventData = {
|
|
11417
|
+
state: "agent.idle"
|
|
11418
|
+
};
|
|
11419
|
+
await this.emit("agent.idle", eventData, { sessionId });
|
|
11420
|
+
}
|
|
11421
|
+
mapEventType(type) {
|
|
11422
|
+
const mapping = {
|
|
11423
|
+
"session.start": "session.start",
|
|
11424
|
+
"session.end": "session.end",
|
|
11425
|
+
"task.start": "task.start",
|
|
11426
|
+
"task.complete": "task.complete",
|
|
11427
|
+
thinking: "agent.thinking",
|
|
11428
|
+
tool: "agent.tool_call",
|
|
11429
|
+
coding: "agent.coding",
|
|
11430
|
+
output: "agent.streaming",
|
|
11431
|
+
message: "agent.streaming",
|
|
11432
|
+
ask: "agent.asking_user",
|
|
11433
|
+
error: "agent.error",
|
|
11434
|
+
idle: "agent.idle",
|
|
11435
|
+
compact: "agent.compact"
|
|
11436
|
+
};
|
|
11437
|
+
return mapping[type] ?? "agent.streaming";
|
|
11438
|
+
}
|
|
11439
|
+
buildEventData(eventType, payload) {
|
|
11440
|
+
const data = payload.data ?? {};
|
|
11441
|
+
return {
|
|
11442
|
+
state: eventType,
|
|
11443
|
+
project: data.project,
|
|
11444
|
+
activeFile: data.activeFile,
|
|
11445
|
+
model: data.model,
|
|
11446
|
+
toolInput: data.toolInput,
|
|
11447
|
+
toolCallName: data.toolCallName,
|
|
11448
|
+
thinkingContent: data.thinkingContent,
|
|
11449
|
+
messageContent: data.messageContent,
|
|
11450
|
+
finalMessage: data.finalMessage,
|
|
11451
|
+
toolResult: data.toolResult,
|
|
11452
|
+
errorMessage: data.errorMessage,
|
|
11453
|
+
errorType: data.errorType,
|
|
11454
|
+
raw: data.raw
|
|
11455
|
+
};
|
|
11456
|
+
}
|
|
11457
|
+
};
|
|
11458
|
+
|
|
11459
|
+
// src/adapters/zed.ts
|
|
11460
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
11461
|
+
import { basename as basename11, join as join14 } from "path";
|
|
11462
|
+
var ZedAdapter = class extends BaseAdapter {
|
|
11463
|
+
displayName = "Zed AI";
|
|
11464
|
+
name = "zed";
|
|
11465
|
+
strategies = [
|
|
11466
|
+
"process-detect",
|
|
11467
|
+
"api-client"
|
|
11468
|
+
];
|
|
11469
|
+
logPaths;
|
|
11470
|
+
apiPort = 9876;
|
|
11471
|
+
pollIntervalMs;
|
|
11472
|
+
poller = null;
|
|
11473
|
+
lastEventTime = 0;
|
|
11474
|
+
activeZedSessions = /* @__PURE__ */ new Map();
|
|
11475
|
+
constructor(options) {
|
|
11476
|
+
super(options);
|
|
11477
|
+
this.pollIntervalMs = 2e3;
|
|
11478
|
+
this.logPaths = [
|
|
11479
|
+
join14(options.homeDirectory ?? process.env.HOME ?? "", ".config", "zed", "logs", "agent.log"),
|
|
11480
|
+
"/tmp/zed-agent.log"
|
|
11481
|
+
];
|
|
11482
|
+
}
|
|
11483
|
+
start() {
|
|
11484
|
+
if (this.getStatus().running) {
|
|
11485
|
+
return Promise.resolve();
|
|
11486
|
+
}
|
|
11487
|
+
this.setRunning(true);
|
|
11488
|
+
this.startPolling();
|
|
11489
|
+
logger.info({ adapter: this.name }, "Zed adapter started");
|
|
11490
|
+
return Promise.resolve();
|
|
11491
|
+
}
|
|
11492
|
+
stop() {
|
|
11493
|
+
if (this.poller !== null) {
|
|
11494
|
+
clearInterval(this.poller);
|
|
11495
|
+
this.poller = null;
|
|
11496
|
+
}
|
|
11497
|
+
this.setRunning(false);
|
|
11498
|
+
logger.info({ adapter: this.name }, "Zed adapter stopped");
|
|
11499
|
+
return Promise.resolve();
|
|
11500
|
+
}
|
|
11501
|
+
async handleHook(payload) {
|
|
11502
|
+
const normalized = this.parseNormalizedHookPayload(payload);
|
|
11503
|
+
if (normalized === null) {
|
|
11504
|
+
return;
|
|
11505
|
+
}
|
|
11506
|
+
const context = {
|
|
11507
|
+
cwd: normalized.cwd,
|
|
11508
|
+
pid: normalized.pid,
|
|
11509
|
+
sessionId: normalized.sessionId,
|
|
11510
|
+
source: "zed-hook"
|
|
11511
|
+
};
|
|
11512
|
+
const eventType = this.mapEventType(normalized.type ?? "");
|
|
11513
|
+
const eventData = this.buildEventData(eventType, normalized);
|
|
11514
|
+
await this.emit(eventType, eventData, context);
|
|
11515
|
+
}
|
|
11516
|
+
startPolling() {
|
|
11517
|
+
this.poller = setInterval(() => {
|
|
11518
|
+
void this.pollZedStatus();
|
|
11519
|
+
}, this.pollIntervalMs);
|
|
11520
|
+
}
|
|
11521
|
+
async pollZedStatus() {
|
|
11522
|
+
try {
|
|
11523
|
+
const response = await fetch(`http://127.0.0.1:${this.apiPort}/api/agent/status`, {
|
|
11524
|
+
signal: AbortSignal.timeout(1e3)
|
|
11525
|
+
});
|
|
11526
|
+
if (response.ok) {
|
|
11527
|
+
const data = await response.json();
|
|
11528
|
+
if (data.sessionId && typeof data.sessionId === "string") {
|
|
11529
|
+
const sessionId = `zed:${data.sessionId}`;
|
|
11530
|
+
if (!this.activeZedSessions.has(sessionId)) {
|
|
11531
|
+
this.activeZedSessions.set(sessionId, sessionId);
|
|
11532
|
+
await this.emitSessionStart(sessionId, data);
|
|
11533
|
+
}
|
|
11534
|
+
if (data.state === "thinking" && this.lastEventTime < Date.now() - 5e3) {
|
|
11535
|
+
const rawThinking = data.thinking;
|
|
11536
|
+
if (rawThinking) {
|
|
11537
|
+
await this.emitThinking(sessionId, rawThinking);
|
|
11538
|
+
}
|
|
11539
|
+
} else if (data.state === "tool" && data.toolName) {
|
|
11540
|
+
const rawFilePath = data.filePath;
|
|
11541
|
+
const rawCommand = data.command;
|
|
11542
|
+
const rawToolName = data.toolName ?? "unknown";
|
|
11543
|
+
await this.emitToolCall(
|
|
11544
|
+
sessionId,
|
|
11545
|
+
{
|
|
11546
|
+
filePath: rawFilePath ?? "",
|
|
11547
|
+
command: rawCommand ?? ""
|
|
11548
|
+
},
|
|
11549
|
+
rawToolName
|
|
11550
|
+
);
|
|
11551
|
+
} else if (data.state === "idle") {
|
|
11552
|
+
await this.emitIdle(sessionId);
|
|
11553
|
+
}
|
|
11554
|
+
}
|
|
11555
|
+
}
|
|
11556
|
+
} catch {
|
|
11557
|
+
await this.checkLogFiles();
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
async checkLogFiles() {
|
|
11561
|
+
for (const logPath of this.logPaths) {
|
|
11562
|
+
try {
|
|
11563
|
+
const content = await readFile12(logPath, "utf8");
|
|
11564
|
+
await this.parseLogContent(content);
|
|
11565
|
+
} catch {
|
|
11566
|
+
}
|
|
11567
|
+
}
|
|
11568
|
+
}
|
|
11569
|
+
async parseLogContent(content) {
|
|
11570
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
11571
|
+
for (const line of lines) {
|
|
11572
|
+
if (this.lastEventTime > 0 && this.lastEventTime >= Date.now() - 2e3) {
|
|
11573
|
+
continue;
|
|
11574
|
+
}
|
|
11575
|
+
const event = this.extractZedEventFromLog(line);
|
|
11576
|
+
if (event) {
|
|
11577
|
+
await this.handleHook(event);
|
|
11578
|
+
this.lastEventTime = Date.now();
|
|
11579
|
+
}
|
|
11580
|
+
}
|
|
11581
|
+
}
|
|
11582
|
+
extractZedEventFromLog(line) {
|
|
11583
|
+
try {
|
|
11584
|
+
const parsed = JSON.parse(line);
|
|
11585
|
+
if (!parsed.type || typeof parsed.type !== "string") {
|
|
11586
|
+
return null;
|
|
11587
|
+
}
|
|
11588
|
+
const rawSessionId = parsed.sessionId;
|
|
11589
|
+
const rawWorkspace = parsed.workspace;
|
|
11590
|
+
const sessionId = rawSessionId ? rawSessionId : rawWorkspace ? `zed:${basename11(rawWorkspace)}` : `zed:${Date.now()}`;
|
|
11591
|
+
const rawCwd = parsed.cwd ?? rawWorkspace;
|
|
11592
|
+
return {
|
|
11593
|
+
type: parsed.type,
|
|
11594
|
+
sessionId,
|
|
11595
|
+
cwd: rawCwd,
|
|
11596
|
+
data: {
|
|
11597
|
+
project: parsed.project ?? rawWorkspace ? basename11(String(rawWorkspace)) : void 0,
|
|
11598
|
+
model: parsed.model,
|
|
11599
|
+
state: parsed.type,
|
|
11600
|
+
thinkingContent: parsed.thinking,
|
|
11601
|
+
toolCallName: parsed.toolName,
|
|
11602
|
+
toolInput: parsed.toolInput,
|
|
11603
|
+
messageContent: parsed.message ?? parsed.output,
|
|
11604
|
+
errorMessage: parsed.error,
|
|
11605
|
+
raw: parsed
|
|
11606
|
+
}
|
|
11607
|
+
};
|
|
11608
|
+
} catch {
|
|
11609
|
+
if (line.includes("[zed:agent]")) {
|
|
11610
|
+
const cleaned = line.replace(/^\[.*?\] \[.*?\] /, "");
|
|
11611
|
+
if (cleaned.includes("Thinking:")) {
|
|
11612
|
+
return { type: "thinking", thinkingContent: cleaned.replace("Thinking:", "").trim() };
|
|
11613
|
+
}
|
|
11614
|
+
if (cleaned.includes("Executing tool:")) {
|
|
11615
|
+
return { type: "tool", toolCallName: cleaned.replace("Executing tool:", "").trim() };
|
|
11616
|
+
}
|
|
11617
|
+
if (cleaned.includes("Error:")) {
|
|
11618
|
+
return { type: "error", errorMessage: cleaned.replace("Error:", "").trim() };
|
|
11619
|
+
}
|
|
11620
|
+
}
|
|
11621
|
+
return null;
|
|
11622
|
+
}
|
|
11623
|
+
}
|
|
11624
|
+
async emitSessionStart(sessionId, _data) {
|
|
11625
|
+
const eventData = {
|
|
11626
|
+
state: "session.start",
|
|
11627
|
+
project: sessionId.split(":")[1] ?? "unknown"
|
|
11628
|
+
};
|
|
11629
|
+
await this.emit("session.start", eventData, { sessionId });
|
|
11630
|
+
}
|
|
11631
|
+
async emitThinking(sessionId, content) {
|
|
11632
|
+
if (!content) return;
|
|
11633
|
+
const eventData = {
|
|
11634
|
+
state: "agent.thinking",
|
|
11635
|
+
thinkingContent: content
|
|
11636
|
+
};
|
|
11637
|
+
await this.emit("agent.thinking", eventData, { sessionId });
|
|
11638
|
+
this.lastEventTime = Date.now();
|
|
11639
|
+
}
|
|
11640
|
+
async emitToolCall(sessionId, toolInput, toolName) {
|
|
11641
|
+
const eventData = {
|
|
11642
|
+
state: "agent.tool_call",
|
|
11643
|
+
toolCallName: toolName,
|
|
11644
|
+
toolInput,
|
|
11645
|
+
activeFile: toolInput.filePath
|
|
11646
|
+
};
|
|
11647
|
+
await this.emit("agent.tool_call", eventData, { sessionId });
|
|
11648
|
+
this.lastEventTime = Date.now();
|
|
11649
|
+
}
|
|
11650
|
+
async emitIdle(sessionId) {
|
|
11651
|
+
const eventData = {
|
|
11652
|
+
state: "agent.idle"
|
|
11653
|
+
};
|
|
11654
|
+
await this.emit("agent.idle", eventData, { sessionId });
|
|
11655
|
+
}
|
|
11656
|
+
mapEventType(type) {
|
|
11657
|
+
const mapping = {
|
|
11658
|
+
"session.start": "session.start",
|
|
11659
|
+
"session.end": "session.end",
|
|
11660
|
+
"task.start": "task.start",
|
|
11661
|
+
"task.complete": "task.complete",
|
|
11662
|
+
thinking: "agent.thinking",
|
|
11663
|
+
tool: "agent.tool_call",
|
|
11664
|
+
coding: "agent.coding",
|
|
11665
|
+
output: "agent.streaming",
|
|
11666
|
+
message: "agent.streaming",
|
|
11667
|
+
ask: "agent.asking_user",
|
|
11668
|
+
error: "agent.error",
|
|
11669
|
+
idle: "agent.idle",
|
|
11670
|
+
compact: "agent.compact"
|
|
11671
|
+
};
|
|
11672
|
+
return mapping[type] ?? "agent.streaming";
|
|
11673
|
+
}
|
|
11674
|
+
buildEventData(eventType, payload) {
|
|
11675
|
+
const data = payload.data ?? {};
|
|
11676
|
+
return {
|
|
11677
|
+
state: eventType,
|
|
11678
|
+
project: data.project,
|
|
11679
|
+
activeFile: data.activeFile,
|
|
11680
|
+
model: data.model,
|
|
11681
|
+
toolInput: data.toolInput,
|
|
11682
|
+
toolCallName: data.toolCallName,
|
|
11683
|
+
thinkingContent: data.thinkingContent,
|
|
11684
|
+
messageContent: data.messageContent,
|
|
11685
|
+
finalMessage: data.finalMessage,
|
|
11686
|
+
toolResult: data.toolResult,
|
|
11687
|
+
errorMessage: data.errorMessage,
|
|
11688
|
+
errorType: data.errorType,
|
|
11689
|
+
raw: data.raw
|
|
11690
|
+
};
|
|
11691
|
+
}
|
|
11692
|
+
};
|
|
11038
11693
|
|
|
11039
11694
|
// src/adapters/registry.ts
|
|
11040
11695
|
var AdapterRegistry = class {
|
|
@@ -11119,7 +11774,9 @@ function createDefaultAdapters(options) {
|
|
|
11119
11774
|
new KiloAdapter(options),
|
|
11120
11775
|
new CodexAdapter(options),
|
|
11121
11776
|
new OpenClawAdapter(options),
|
|
11122
|
-
new OpenCodeAdapter(options)
|
|
11777
|
+
new OpenCodeAdapter(options),
|
|
11778
|
+
new PiAdapter(options),
|
|
11779
|
+
new ZedAdapter(options)
|
|
11123
11780
|
];
|
|
11124
11781
|
}
|
|
11125
11782
|
|
|
@@ -11140,7 +11797,7 @@ function getSocketPath(aisnitchHomePath) {
|
|
|
11140
11797
|
if (process.platform === "win32") {
|
|
11141
11798
|
return "\\\\.\\pipe\\aisnitch.sock";
|
|
11142
11799
|
}
|
|
11143
|
-
return
|
|
11800
|
+
return join15(aisnitchHomePath, "aisnitch.sock");
|
|
11144
11801
|
}
|
|
11145
11802
|
var Pipeline = class {
|
|
11146
11803
|
eventBus = new EventBus();
|
|
@@ -11345,6 +12002,30 @@ var Pipeline = class {
|
|
|
11345
12002
|
getEventBus() {
|
|
11346
12003
|
return this.eventBus;
|
|
11347
12004
|
}
|
|
12005
|
+
/**
|
|
12006
|
+
* Returns the adapter registry for graceful shutdown coordination.
|
|
12007
|
+
*/
|
|
12008
|
+
getAdapterRegistry() {
|
|
12009
|
+
return this.adapterRegistry ?? void 0;
|
|
12010
|
+
}
|
|
12011
|
+
/**
|
|
12012
|
+
* Returns the HTTP receiver for graceful shutdown coordination.
|
|
12013
|
+
*/
|
|
12014
|
+
getHttpReceiver() {
|
|
12015
|
+
return this.httpReceiver;
|
|
12016
|
+
}
|
|
12017
|
+
/**
|
|
12018
|
+
* Returns the UDS server for graceful shutdown coordination.
|
|
12019
|
+
*/
|
|
12020
|
+
getUdsServer() {
|
|
12021
|
+
return this.udsServer;
|
|
12022
|
+
}
|
|
12023
|
+
/**
|
|
12024
|
+
* Returns the WebSocket server for graceful shutdown coordination.
|
|
12025
|
+
*/
|
|
12026
|
+
getWsServer() {
|
|
12027
|
+
return this.wsServer;
|
|
12028
|
+
}
|
|
11348
12029
|
getHealthSnapshot() {
|
|
11349
12030
|
const status = this.getStatus();
|
|
11350
12031
|
return {
|
|
@@ -11469,6 +12150,72 @@ var DEFAULT_TIMEOUTS = Object.freeze({
|
|
|
11469
12150
|
pipelineStartup: 15e3
|
|
11470
12151
|
});
|
|
11471
12152
|
|
|
12153
|
+
// src/core/graceful-shutdown.ts
|
|
12154
|
+
async function withShutdownTimeout(fn, timeoutMs, component) {
|
|
12155
|
+
if (timeoutMs <= 0) {
|
|
12156
|
+
await fn();
|
|
12157
|
+
return;
|
|
12158
|
+
}
|
|
12159
|
+
const timeoutPromise = new Promise((resolve2) => {
|
|
12160
|
+
setTimeout(() => {
|
|
12161
|
+
resolve2("timed_out");
|
|
12162
|
+
}, timeoutMs).unref();
|
|
12163
|
+
});
|
|
12164
|
+
const result = await Promise.race([
|
|
12165
|
+
fn().then(() => "completed"),
|
|
12166
|
+
timeoutPromise
|
|
12167
|
+
]);
|
|
12168
|
+
if (result === "timed_out") {
|
|
12169
|
+
logger.warn(
|
|
12170
|
+
{ component, timeoutMs },
|
|
12171
|
+
`Graceful shutdown exceeded ${timeoutMs}ms timeout \u2014 forcing through`
|
|
12172
|
+
);
|
|
12173
|
+
}
|
|
12174
|
+
}
|
|
12175
|
+
async function shutdownInOrder(components, timeouts, label) {
|
|
12176
|
+
const getTimeout = (key) => {
|
|
12177
|
+
return timeouts[key] ?? DEFAULT_TIMEOUTS.daemonShutdown;
|
|
12178
|
+
};
|
|
12179
|
+
const stopSafely = async (key, fn) => {
|
|
12180
|
+
const timeoutMs = getTimeout(key);
|
|
12181
|
+
try {
|
|
12182
|
+
await withShutdownTimeout(fn, timeoutMs, `${label}.${key}`);
|
|
12183
|
+
} catch (error) {
|
|
12184
|
+
logger.warn(
|
|
12185
|
+
{ error, key, label },
|
|
12186
|
+
`Error during shutdown of ${key} \u2014 continuing with remaining components`
|
|
12187
|
+
);
|
|
12188
|
+
}
|
|
12189
|
+
};
|
|
12190
|
+
if (components.cleanupFns) {
|
|
12191
|
+
for (const cleanupFn of components.cleanupFns) {
|
|
12192
|
+
try {
|
|
12193
|
+
await withShutdownTimeout(
|
|
12194
|
+
async () => {
|
|
12195
|
+
const result = cleanupFn();
|
|
12196
|
+
if (result instanceof Promise) {
|
|
12197
|
+
await result;
|
|
12198
|
+
}
|
|
12199
|
+
},
|
|
12200
|
+
1e3,
|
|
12201
|
+
`${label}.cleanup`
|
|
12202
|
+
);
|
|
12203
|
+
} catch (error) {
|
|
12204
|
+
logger.warn({ error, label }, "Cleanup function failed");
|
|
12205
|
+
}
|
|
12206
|
+
}
|
|
12207
|
+
}
|
|
12208
|
+
if (components.eventBus) {
|
|
12209
|
+
components.eventBus.unsubscribeAll();
|
|
12210
|
+
}
|
|
12211
|
+
await stopSafely("wsServer", () => components.wsServer.stop());
|
|
12212
|
+
await stopSafely("udsServer", () => components.udsServer.stop());
|
|
12213
|
+
await stopSafely("httpReceiver", () => components.httpReceiver.stop());
|
|
12214
|
+
if (components.adapterRegistry) {
|
|
12215
|
+
await stopSafely("adapterRegistry", () => components.adapterRegistry.stopAll());
|
|
12216
|
+
}
|
|
12217
|
+
}
|
|
12218
|
+
|
|
11472
12219
|
// src/tui/index.tsx
|
|
11473
12220
|
import { render } from "ink";
|
|
11474
12221
|
import { withFullScreen } from "fullscreen-ink";
|
|
@@ -11672,9 +12419,11 @@ var TOOL_COLORS = {
|
|
|
11672
12419
|
"openhands": "#facc15",
|
|
11673
12420
|
"openclaw": "#ef4444",
|
|
11674
12421
|
"opencode": "#10b981",
|
|
12422
|
+
"pi": "#1db954",
|
|
11675
12423
|
"qwen-code": "#22c55e",
|
|
11676
12424
|
"unknown": "#94a3b8",
|
|
11677
|
-
"windsurf": "#a855f7"
|
|
12425
|
+
"windsurf": "#a855f7",
|
|
12426
|
+
"zed": "#e85d04"
|
|
11678
12427
|
};
|
|
11679
12428
|
var EVENT_COLORS = {
|
|
11680
12429
|
"agent.asking_user": "#ef4444",
|
|
@@ -13428,10 +14177,10 @@ async function renderManagedTui(options) {
|
|
|
13428
14177
|
|
|
13429
14178
|
// src/cli/pid.ts
|
|
13430
14179
|
import { constants as constants3 } from "fs";
|
|
13431
|
-
import { mkdir as mkdir3, readFile as
|
|
14180
|
+
import { mkdir as mkdir3, readFile as readFile13, rm as rm3, stat as stat10, writeFile as writeFile3, access as access3 } from "fs/promises";
|
|
13432
14181
|
import { createConnection as createConnection2 } from "net";
|
|
13433
14182
|
import { homedir as homedir4 } from "os";
|
|
13434
|
-
import { dirname as dirname7, join as
|
|
14183
|
+
import { dirname as dirname7, join as join16 } from "path";
|
|
13435
14184
|
import { z as z5 } from "zod";
|
|
13436
14185
|
var DaemonStateSchema = z5.strictObject({
|
|
13437
14186
|
pid: z5.number().int().positive(),
|
|
@@ -13446,7 +14195,7 @@ function getDefaultSocketPath(options) {
|
|
|
13446
14195
|
if (process.platform === "win32") {
|
|
13447
14196
|
return "\\\\.\\pipe\\aisnitch.sock";
|
|
13448
14197
|
}
|
|
13449
|
-
return
|
|
14198
|
+
return join16(getAISnitchHomePath(options), "aisnitch.sock");
|
|
13450
14199
|
}
|
|
13451
14200
|
async function cleanupSocketPathIfStale(socketPath) {
|
|
13452
14201
|
if (process.platform === "win32") {
|
|
@@ -13481,16 +14230,16 @@ async function cleanupSocketPathIfStale(socketPath) {
|
|
|
13481
14230
|
return true;
|
|
13482
14231
|
}
|
|
13483
14232
|
function getPidFilePath(options = {}) {
|
|
13484
|
-
return
|
|
14233
|
+
return join16(getAISnitchHomePath(options), "aisnitch.pid");
|
|
13485
14234
|
}
|
|
13486
14235
|
function getDaemonStatePath(options = {}) {
|
|
13487
|
-
return
|
|
14236
|
+
return join16(getAISnitchHomePath(options), "daemon-state.json");
|
|
13488
14237
|
}
|
|
13489
14238
|
function getDaemonLogPath(options = {}) {
|
|
13490
|
-
return
|
|
14239
|
+
return join16(getAISnitchHomePath(options), "daemon.log");
|
|
13491
14240
|
}
|
|
13492
14241
|
function getLaunchAgentPath(options = {}) {
|
|
13493
|
-
return
|
|
14242
|
+
return join16(
|
|
13494
14243
|
options.launchAgentHomeDirectory ?? homedir4(),
|
|
13495
14244
|
"Library",
|
|
13496
14245
|
"LaunchAgents",
|
|
@@ -13506,7 +14255,7 @@ async function writePid(pid, options = {}) {
|
|
|
13506
14255
|
}
|
|
13507
14256
|
async function readPid(options = {}) {
|
|
13508
14257
|
try {
|
|
13509
|
-
const rawPid = await
|
|
14258
|
+
const rawPid = await readFile13(getPidFilePath(options), "utf8");
|
|
13510
14259
|
const parsedPid = Number.parseInt(rawPid.trim(), 10);
|
|
13511
14260
|
if (!Number.isInteger(parsedPid) || parsedPid <= 0) {
|
|
13512
14261
|
throw new Error("Invalid PID file contents.");
|
|
@@ -13536,7 +14285,7 @@ async function writeDaemonState(state, options = {}) {
|
|
|
13536
14285
|
}
|
|
13537
14286
|
async function readDaemonState(options = {}) {
|
|
13538
14287
|
try {
|
|
13539
|
-
const rawJson = await
|
|
14288
|
+
const rawJson = await readFile13(getDaemonStatePath(options), "utf8");
|
|
13540
14289
|
const parsedJson = JSON.parse(rawJson);
|
|
13541
14290
|
return DaemonStateSchema.parse(parsedJson);
|
|
13542
14291
|
} catch (error) {
|
|
@@ -13604,9 +14353,9 @@ function getEffectiveCliConfigPath(options = {}) {
|
|
|
13604
14353
|
|
|
13605
14354
|
// src/cli/auto-update.ts
|
|
13606
14355
|
import { spawn as spawnChildProcess } from "child_process";
|
|
13607
|
-
import { mkdir as mkdir4, readFile as
|
|
14356
|
+
import { mkdir as mkdir4, readFile as readFile14, writeFile as writeFile4 } from "fs/promises";
|
|
13608
14357
|
import { realpath } from "fs/promises";
|
|
13609
|
-
import { join as
|
|
14358
|
+
import { join as join17 } from "path";
|
|
13610
14359
|
var AUTO_UPDATE_STATE_FILE = "auto-update.json";
|
|
13611
14360
|
var AUTO_UPDATE_LOG_FILE = "auto-update.log";
|
|
13612
14361
|
function createAutoUpdateController(dependencies = {}) {
|
|
@@ -13621,7 +14370,7 @@ function createAutoUpdateController(dependencies = {}) {
|
|
|
13621
14370
|
const args = resolveUpdateArgs(options.manager);
|
|
13622
14371
|
const aisnitchHomePath = getAISnitchHomePath(pathOptions);
|
|
13623
14372
|
await mkdir4(aisnitchHomePath, { recursive: true });
|
|
13624
|
-
const logFilePath =
|
|
14373
|
+
const logFilePath = join17(aisnitchHomePath, AUTO_UPDATE_LOG_FILE);
|
|
13625
14374
|
const startedAt = now().toISOString();
|
|
13626
14375
|
await writeAutoUpdateState(
|
|
13627
14376
|
{
|
|
@@ -13809,7 +14558,7 @@ function resolveUpdateArgs(manager) {
|
|
|
13809
14558
|
async function readAutoUpdateState(options) {
|
|
13810
14559
|
const statePath = getAutoUpdateStatePath(options);
|
|
13811
14560
|
try {
|
|
13812
|
-
const rawJson = await
|
|
14561
|
+
const rawJson = await readFile14(statePath, "utf8");
|
|
13813
14562
|
return JSON.parse(rawJson);
|
|
13814
14563
|
} catch (error) {
|
|
13815
14564
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -13829,7 +14578,7 @@ async function writeAutoUpdateState(state, options) {
|
|
|
13829
14578
|
);
|
|
13830
14579
|
}
|
|
13831
14580
|
function getAutoUpdateStatePath(options) {
|
|
13832
|
-
return
|
|
14581
|
+
return join17(getAISnitchHomePath(options), AUTO_UPDATE_STATE_FILE);
|
|
13833
14582
|
}
|
|
13834
14583
|
function toPathOptions(options) {
|
|
13835
14584
|
return {
|
|
@@ -14020,7 +14769,7 @@ function isRecord13(value) {
|
|
|
14020
14769
|
}
|
|
14021
14770
|
|
|
14022
14771
|
// src/cli/runtime.ts
|
|
14023
|
-
var
|
|
14772
|
+
var execFile15 = promisify15(execFileCallback14);
|
|
14024
14773
|
var DAEMON_READY_TIMEOUT_MS = 4e3;
|
|
14025
14774
|
var DAEMON_READY_POLL_INTERVAL_MS = 100;
|
|
14026
14775
|
var DAEMON_STOP_TIMEOUT_MS = 4e3;
|
|
@@ -14036,7 +14785,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14036
14785
|
spawn: spawnImplementation
|
|
14037
14786
|
});
|
|
14038
14787
|
const execFileImplementation = dependencies.execFile ?? (async (file, args) => {
|
|
14039
|
-
return await
|
|
14788
|
+
return await execFile15(file, [...args], {
|
|
14040
14789
|
encoding: "utf8"
|
|
14041
14790
|
});
|
|
14042
14791
|
});
|
|
@@ -14211,7 +14960,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14211
14960
|
}
|
|
14212
14961
|
async function readDaemonStartupFailure(pathOptions) {
|
|
14213
14962
|
try {
|
|
14214
|
-
const daemonLog = await
|
|
14963
|
+
const daemonLog = await readFile15(getDaemonLogPath(pathOptions), "utf8");
|
|
14215
14964
|
const logLines = daemonLog.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
14216
14965
|
const lastLine = logLines.at(-1);
|
|
14217
14966
|
if (!lastLine) {
|
|
@@ -14309,22 +15058,35 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14309
15058
|
return;
|
|
14310
15059
|
}
|
|
14311
15060
|
shuttingDown = true;
|
|
14312
|
-
|
|
14313
|
-
|
|
14314
|
-
|
|
14315
|
-
|
|
14316
|
-
|
|
15061
|
+
const shutdownTimeouts = {
|
|
15062
|
+
adapterRegistry: DEFAULT_TIMEOUTS.adapterShutdown,
|
|
15063
|
+
httpReceiver: DEFAULT_TIMEOUTS.httpRequest,
|
|
15064
|
+
udsServer: DEFAULT_TIMEOUTS.fileOperation,
|
|
15065
|
+
wsServer: DEFAULT_TIMEOUTS.wsConnection,
|
|
15066
|
+
cleanupFns: 1e3
|
|
15067
|
+
};
|
|
15068
|
+
const components = {
|
|
15069
|
+
adapterRegistry: pipeline.getAdapterRegistry(),
|
|
15070
|
+
httpReceiver: pipeline.getHttpReceiver(),
|
|
15071
|
+
udsServer: pipeline.getUdsServer(),
|
|
15072
|
+
wsServer: pipeline.getWsServer(),
|
|
15073
|
+
eventBus: pipeline.getEventBus(),
|
|
15074
|
+
cleanupFns: daemonMode ? [async () => {
|
|
14317
15075
|
await Promise.all([
|
|
14318
|
-
removePid(
|
|
14319
|
-
removeDaemonState(
|
|
15076
|
+
removePid(toPathOptions2(options)),
|
|
15077
|
+
removeDaemonState(toPathOptions2(options))
|
|
14320
15078
|
]);
|
|
14321
|
-
}
|
|
14322
|
-
}
|
|
14323
|
-
|
|
14324
|
-
|
|
15079
|
+
}] : []
|
|
15080
|
+
};
|
|
15081
|
+
try {
|
|
15082
|
+
await shutdownInOrder(components, shutdownTimeouts, "pipeline");
|
|
15083
|
+
} finally {
|
|
15084
|
+
if (!daemonMode) {
|
|
15085
|
+
output.stdout(`AISnitch stopped after ${signal}.
|
|
14325
15086
|
`);
|
|
15087
|
+
}
|
|
15088
|
+
process.exit(exitCode);
|
|
14326
15089
|
}
|
|
14327
|
-
process.exit(exitCode);
|
|
14328
15090
|
};
|
|
14329
15091
|
if (daemonMode) {
|
|
14330
15092
|
process.once("SIGTERM", () => {
|
|
@@ -14581,7 +15343,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14581
15343
|
}
|
|
14582
15344
|
const { config } = await loadEffectiveConfig(options);
|
|
14583
15345
|
setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
|
|
14584
|
-
const ephemeralHomeDirectory = await mkdtemp(
|
|
15346
|
+
const ephemeralHomeDirectory = await mkdtemp(join18(tmpdir(), "aisnitch-mock-"));
|
|
14585
15347
|
const ephemeralPipeline = new Pipeline();
|
|
14586
15348
|
const status2 = await ephemeralPipeline.start({
|
|
14587
15349
|
config: {
|
|
@@ -14665,7 +15427,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14665
15427
|
} else {
|
|
14666
15428
|
const { config } = await loadEffectiveConfig(options);
|
|
14667
15429
|
setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
|
|
14668
|
-
ephemeralHomeDirectory = await mkdtemp(
|
|
15430
|
+
ephemeralHomeDirectory = await mkdtemp(join18(tmpdir(), "aisnitch-wrap-"));
|
|
14669
15431
|
ephemeralPipeline = new Pipeline();
|
|
14670
15432
|
await ephemeralPipeline.start({
|
|
14671
15433
|
config: {
|
|
@@ -14740,7 +15502,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14740
15502
|
},
|
|
14741
15503
|
pid: process.ppid > 1 ? process.ppid : void 0,
|
|
14742
15504
|
source: "aisnitch://adapters/aider/notifications-command",
|
|
14743
|
-
transcriptPath:
|
|
15505
|
+
transcriptPath: join18(process.cwd(), ".aider.chat.history.md"),
|
|
14744
15506
|
type: "agent.idle"
|
|
14745
15507
|
}),
|
|
14746
15508
|
headers: {
|
|
@@ -14941,7 +15703,7 @@ function joinSocketPath(pathOptions) {
|
|
|
14941
15703
|
}
|
|
14942
15704
|
function resolveCliEntryPath() {
|
|
14943
15705
|
const cliEntryPath = process.argv[1];
|
|
14944
|
-
if (!cliEntryPath ||
|
|
15706
|
+
if (!cliEntryPath || basename12(cliEntryPath).length === 0) {
|
|
14945
15707
|
throw new Error("Unable to resolve the AISnitch CLI entry path.");
|
|
14946
15708
|
}
|
|
14947
15709
|
return cliEntryPath;
|