aisnitch 0.2.19 → 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 +1246 -100
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1222 -76
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1808 -133
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1491 -4
- package/dist/index.d.ts +1491 -4
- package/dist/index.js +1736 -122
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.21";
|
|
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
|
|
@@ -82,6 +82,8 @@ var TOOL_NAMES = [
|
|
|
82
82
|
"kiro",
|
|
83
83
|
"augment-code",
|
|
84
84
|
"mistral",
|
|
85
|
+
"zed",
|
|
86
|
+
"pi",
|
|
85
87
|
"unknown"
|
|
86
88
|
];
|
|
87
89
|
var ERROR_TYPES = [
|
|
@@ -123,47 +125,58 @@ function createUuidV7() {
|
|
|
123
125
|
return (0, import_uuid.v7)();
|
|
124
126
|
}
|
|
125
127
|
var ToolInputSchema = import_zod.z.strictObject({
|
|
126
|
-
filePath: import_zod.z.string().min(1).optional(),
|
|
127
|
-
command: import_zod.z.string().min(1).optional()
|
|
128
|
+
filePath: import_zod.z.string().min(1).max(4096).optional(),
|
|
129
|
+
command: import_zod.z.string().min(1).max(1e4).optional()
|
|
128
130
|
}).refine(
|
|
129
131
|
(value) => value.filePath !== void 0 || value.command !== void 0,
|
|
130
132
|
"toolInput must include filePath or command"
|
|
131
133
|
);
|
|
134
|
+
var ThinkingContentSchema = import_zod.z.string().max(1e5).describe("Raw thinking/reasoning content from the AI model");
|
|
135
|
+
var ToolCallNameSchema = import_zod.z.string().min(1).max(100).describe("Name of the tool being invoked (e.g., Edit, Bash, Grep)");
|
|
136
|
+
var FinalMessageSchema = import_zod.z.string().max(5e4).describe("End-of-run summary or completion message");
|
|
137
|
+
var ToolResultSchema = import_zod.z.string().max(1e4).describe("Tool execution result or output");
|
|
138
|
+
var MessageContentSchema = import_zod.z.string().max(1e5).describe("Raw text content from AI messages");
|
|
132
139
|
var ToolNameSchema = import_zod.z.enum(TOOL_NAMES);
|
|
133
140
|
var AISnitchEventTypeSchema = import_zod.z.enum(AISNITCH_EVENT_TYPES);
|
|
134
141
|
var ErrorTypeSchema = import_zod.z.enum(ERROR_TYPES);
|
|
135
142
|
var CESPCategorySchema = import_zod.z.enum(CESP_CATEGORIES);
|
|
136
143
|
var EventDataSchema = import_zod.z.strictObject({
|
|
137
144
|
state: AISnitchEventTypeSchema,
|
|
138
|
-
project: import_zod.z.string().min(1).optional(),
|
|
139
|
-
projectPath: import_zod.z.string().min(1).optional(),
|
|
145
|
+
project: import_zod.z.string().min(1).max(255).optional(),
|
|
146
|
+
projectPath: import_zod.z.string().min(1).max(4096).optional(),
|
|
140
147
|
duration: import_zod.z.number().int().min(0).optional(),
|
|
141
|
-
toolName: import_zod.z.string().min(1).optional(),
|
|
148
|
+
toolName: import_zod.z.string().min(1).max(100).optional(),
|
|
142
149
|
toolInput: ToolInputSchema.optional(),
|
|
143
|
-
activeFile: import_zod.z.string().min(1).optional(),
|
|
144
|
-
model: import_zod.z.string().min(1).optional(),
|
|
150
|
+
activeFile: import_zod.z.string().min(1).max(4096).optional(),
|
|
151
|
+
model: import_zod.z.string().min(1).max(200).optional(),
|
|
145
152
|
tokensUsed: import_zod.z.number().int().min(0).optional(),
|
|
146
|
-
errorMessage: import_zod.z.string().min(1).optional(),
|
|
153
|
+
errorMessage: import_zod.z.string().min(1).max(1e4).optional(),
|
|
147
154
|
errorType: ErrorTypeSchema.optional(),
|
|
148
155
|
raw: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
|
|
149
|
-
terminal: import_zod.z.string().min(1).optional(),
|
|
150
|
-
cwd: import_zod.z.string().min(1).optional(),
|
|
156
|
+
terminal: import_zod.z.string().min(1).max(100).optional(),
|
|
157
|
+
cwd: import_zod.z.string().min(1).max(4096).optional(),
|
|
151
158
|
pid: import_zod.z.number().int().positive().optional(),
|
|
152
|
-
instanceId: import_zod.z.string().min(1).optional(),
|
|
159
|
+
instanceId: import_zod.z.string().min(1).max(255).optional(),
|
|
153
160
|
instanceIndex: import_zod.z.number().int().min(1).optional(),
|
|
154
|
-
instanceTotal: import_zod.z.number().int().min(1).optional()
|
|
161
|
+
instanceTotal: import_zod.z.number().int().min(1).optional(),
|
|
162
|
+
// New fields for enhanced content capture
|
|
163
|
+
thinkingContent: ThinkingContentSchema.optional(),
|
|
164
|
+
toolCallName: ToolCallNameSchema.optional(),
|
|
165
|
+
finalMessage: FinalMessageSchema.optional(),
|
|
166
|
+
toolResult: ToolResultSchema.optional(),
|
|
167
|
+
messageContent: MessageContentSchema.optional()
|
|
155
168
|
});
|
|
156
169
|
var AISnitchEventSchema = import_zod.z.strictObject({
|
|
157
170
|
specversion: import_zod.z.literal("1.0"),
|
|
158
171
|
id: import_zod.z.string().refine(isUuidV7, "id must be a valid UUIDv7 string"),
|
|
159
|
-
source: import_zod.z.string().refine(
|
|
172
|
+
source: import_zod.z.string().max(2e3).refine(
|
|
160
173
|
isValidUriReference,
|
|
161
174
|
"source must be a valid non-empty CloudEvents URI-reference"
|
|
162
175
|
),
|
|
163
176
|
type: AISnitchEventTypeSchema,
|
|
164
177
|
time: ISO_TIMESTAMP_SCHEMA,
|
|
165
178
|
"aisnitch.tool": ToolNameSchema,
|
|
166
|
-
"aisnitch.sessionid": import_zod.z.string().min(1),
|
|
179
|
+
"aisnitch.sessionid": import_zod.z.string().min(1).max(500),
|
|
167
180
|
"aisnitch.seqnum": import_zod.z.number().int().min(1),
|
|
168
181
|
data: EventDataSchema
|
|
169
182
|
});
|
|
@@ -2463,13 +2476,13 @@ function toConfigPathOptions(options) {
|
|
|
2463
2476
|
}
|
|
2464
2477
|
|
|
2465
2478
|
// src/cli/runtime.ts
|
|
2466
|
-
var
|
|
2479
|
+
var import_node_child_process16 = require("child_process");
|
|
2467
2480
|
var import_node_fs4 = require("fs");
|
|
2468
|
-
var
|
|
2481
|
+
var import_promises19 = require("fs/promises");
|
|
2469
2482
|
var import_node_net4 = require("net");
|
|
2470
2483
|
var import_node_os5 = require("os");
|
|
2471
|
-
var
|
|
2472
|
-
var
|
|
2484
|
+
var import_node_path21 = require("path");
|
|
2485
|
+
var import_node_util15 = require("util");
|
|
2473
2486
|
|
|
2474
2487
|
// src/adapters/generic-pty.ts
|
|
2475
2488
|
var import_node_path5 = require("path");
|
|
@@ -2540,9 +2553,11 @@ var TOOL_BINARY_MAP = {
|
|
|
2540
2553
|
"openhands": "openhands",
|
|
2541
2554
|
"openclaw": "openclaw",
|
|
2542
2555
|
"opencode": "opencode",
|
|
2556
|
+
"pi": "pi",
|
|
2543
2557
|
"qwen-code": "qwen",
|
|
2544
2558
|
"unknown": "unknown",
|
|
2545
|
-
"windsurf": "windsurf"
|
|
2559
|
+
"windsurf": "windsurf",
|
|
2560
|
+
"zed": "zed"
|
|
2546
2561
|
};
|
|
2547
2562
|
var ContextDetector = class {
|
|
2548
2563
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -3351,6 +3366,346 @@ function isPidRunning(pid) {
|
|
|
3351
3366
|
}
|
|
3352
3367
|
}
|
|
3353
3368
|
|
|
3369
|
+
// src/core/errors.ts
|
|
3370
|
+
var AISnitchError = class _AISnitchError extends Error {
|
|
3371
|
+
/**
|
|
3372
|
+
* Machine-readable error code for programmatic handling.
|
|
3373
|
+
* Format: `SUBCATEGORY_SPECIFIC_DETAIL` (uppercase with underscores).
|
|
3374
|
+
*/
|
|
3375
|
+
code;
|
|
3376
|
+
/**
|
|
3377
|
+
* Arbitrary context bag forwarded to the logger for structured debugging.
|
|
3378
|
+
*/
|
|
3379
|
+
context;
|
|
3380
|
+
constructor(message, code, context) {
|
|
3381
|
+
super(message);
|
|
3382
|
+
this.name = "AISnitchError";
|
|
3383
|
+
this.code = code;
|
|
3384
|
+
this.context = context;
|
|
3385
|
+
if (Error.captureStackTrace) {
|
|
3386
|
+
Error.captureStackTrace(this, _AISnitchError);
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
/**
|
|
3390
|
+
* Full error chain for logging: `[name] code — message`.
|
|
3391
|
+
*/
|
|
3392
|
+
toString() {
|
|
3393
|
+
return `${this.name} [${this.code}] \u2014 ${this.message}`;
|
|
3394
|
+
}
|
|
3395
|
+
/**
|
|
3396
|
+
* JSON serialization friendly to pino serializers.
|
|
3397
|
+
*/
|
|
3398
|
+
toJSON() {
|
|
3399
|
+
return {
|
|
3400
|
+
name: this.name,
|
|
3401
|
+
code: this.code,
|
|
3402
|
+
message: this.message,
|
|
3403
|
+
context: this.context,
|
|
3404
|
+
stack: this.stack
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
};
|
|
3408
|
+
function isAISnitchError(error) {
|
|
3409
|
+
return error instanceof AISnitchError;
|
|
3410
|
+
}
|
|
3411
|
+
function isRetryableError(error) {
|
|
3412
|
+
if (!isAISnitchError(error)) {
|
|
3413
|
+
if (error instanceof Error) {
|
|
3414
|
+
const code = error.code;
|
|
3415
|
+
const retryableCodes = /* @__PURE__ */ new Set([
|
|
3416
|
+
"ECONNREFUSED",
|
|
3417
|
+
"ECONNRESET",
|
|
3418
|
+
"ETIMEDOUT",
|
|
3419
|
+
"ENOTFOUND",
|
|
3420
|
+
"EHOSTUNREACH",
|
|
3421
|
+
"EPIPE",
|
|
3422
|
+
"EPERM"
|
|
3423
|
+
// sometimes transient on macOS file locks
|
|
3424
|
+
]);
|
|
3425
|
+
if (typeof code === "string" && retryableCodes.has(code)) {
|
|
3426
|
+
return true;
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
return false;
|
|
3430
|
+
}
|
|
3431
|
+
const retryableCategories = /* @__PURE__ */ new Set(["TIMEOUT", "NETWORK"]);
|
|
3432
|
+
for (const category of retryableCategories) {
|
|
3433
|
+
if (error.code.startsWith(category)) {
|
|
3434
|
+
return true;
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
const retryablePatterns = [
|
|
3438
|
+
/^ADAPTER_.*_(FILE_IO|NETWORK|PROCESS_DETECT)_ERROR$/,
|
|
3439
|
+
/^PIPELINE_.*_(RETRY|RECONNECT)_ERROR$/
|
|
3440
|
+
];
|
|
3441
|
+
for (const pattern of retryablePatterns) {
|
|
3442
|
+
if (pattern.test(error.code)) {
|
|
3443
|
+
return true;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
return false;
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
// src/core/circuit-breaker.ts
|
|
3450
|
+
var CircuitOpenError = class _CircuitOpenError extends AISnitchError {
|
|
3451
|
+
constructor(circuitId, state) {
|
|
3452
|
+
super(
|
|
3453
|
+
`Circuit "${circuitId}" is OPEN \u2014 operation rejected`,
|
|
3454
|
+
"CIRCUIT_OPEN",
|
|
3455
|
+
{ circuitId, failures: state.failures, lastFailureAt: state.lastFailureAt }
|
|
3456
|
+
);
|
|
3457
|
+
this.circuitId = circuitId;
|
|
3458
|
+
this.state = state;
|
|
3459
|
+
this.name = "CircuitOpenError";
|
|
3460
|
+
if (Error.captureStackTrace) {
|
|
3461
|
+
Error.captureStackTrace(this, _CircuitOpenError);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
toString() {
|
|
3465
|
+
return `${this.name} [${this.code}] "${this.circuitId}" \u2014 failures=${this.state.failures}`;
|
|
3466
|
+
}
|
|
3467
|
+
};
|
|
3468
|
+
var DEFAULT_OPTIONS = {
|
|
3469
|
+
failureThreshold: 5,
|
|
3470
|
+
halfOpenAfterMs: 3e4,
|
|
3471
|
+
id: "unnamed",
|
|
3472
|
+
resetOnSuccess: true,
|
|
3473
|
+
shouldCountAsFailure: isRetryableError,
|
|
3474
|
+
windowMs: 6e4
|
|
3475
|
+
};
|
|
3476
|
+
var CircuitBreaker = class {
|
|
3477
|
+
failures = 0;
|
|
3478
|
+
lastFailureAt = null;
|
|
3479
|
+
state = "closed";
|
|
3480
|
+
halfOpenTestStartedAt = null;
|
|
3481
|
+
options;
|
|
3482
|
+
constructor(options = {}) {
|
|
3483
|
+
this.options = {
|
|
3484
|
+
...DEFAULT_OPTIONS,
|
|
3485
|
+
...options,
|
|
3486
|
+
// Re-spread to ensure all fields have defaults
|
|
3487
|
+
failureThreshold: options.failureThreshold ?? DEFAULT_OPTIONS.failureThreshold,
|
|
3488
|
+
halfOpenAfterMs: options.halfOpenAfterMs ?? DEFAULT_OPTIONS.halfOpenAfterMs,
|
|
3489
|
+
id: options.id ?? DEFAULT_OPTIONS.id,
|
|
3490
|
+
resetOnSuccess: options.resetOnSuccess ?? DEFAULT_OPTIONS.resetOnSuccess,
|
|
3491
|
+
shouldCountAsFailure: options.shouldCountAsFailure ?? DEFAULT_OPTIONS.shouldCountAsFailure,
|
|
3492
|
+
windowMs: options.windowMs ?? DEFAULT_OPTIONS.windowMs
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Executes an async operation through the circuit breaker.
|
|
3497
|
+
*
|
|
3498
|
+
* - If the circuit is CLOSED → runs `fn` and updates state based on result
|
|
3499
|
+
* - If the circuit is HALF-OPEN → runs `fn` once to test recovery
|
|
3500
|
+
* - If the circuit is OPEN → throws `CircuitOpenError` immediately (no call)
|
|
3501
|
+
*
|
|
3502
|
+
* @param fn - The async operation to protect
|
|
3503
|
+
* @returns The result of `fn` if successful
|
|
3504
|
+
* @throws CircuitOpenError if the circuit is OPEN
|
|
3505
|
+
* @throws The error from `fn` if it throws (and `shouldCountAsFailure` returns true)
|
|
3506
|
+
*/
|
|
3507
|
+
async execute(fn) {
|
|
3508
|
+
switch (this.state) {
|
|
3509
|
+
case "closed":
|
|
3510
|
+
return this.executeClosed(fn);
|
|
3511
|
+
case "half-open":
|
|
3512
|
+
return this.executeHalfOpen(fn);
|
|
3513
|
+
case "open":
|
|
3514
|
+
if (this.shouldTransitionToHalfOpen()) {
|
|
3515
|
+
this.transitionToHalfOpen();
|
|
3516
|
+
return this.executeHalfOpen(fn);
|
|
3517
|
+
}
|
|
3518
|
+
throw new CircuitOpenError(this.options.id, this.getState());
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
/**
|
|
3522
|
+
* Returns the current observable circuit state.
|
|
3523
|
+
*/
|
|
3524
|
+
getState() {
|
|
3525
|
+
return {
|
|
3526
|
+
failures: this.failures,
|
|
3527
|
+
lastFailureAt: this.lastFailureAt,
|
|
3528
|
+
state: this.state
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
/**
|
|
3532
|
+
* Forces the circuit to CLOSED (resets failure count and state).
|
|
3533
|
+
* Useful for manual recovery after a known-fix or after a maintenance window.
|
|
3534
|
+
*/
|
|
3535
|
+
reset() {
|
|
3536
|
+
this.failures = 0;
|
|
3537
|
+
this.lastFailureAt = null;
|
|
3538
|
+
this.state = "closed";
|
|
3539
|
+
this.halfOpenTestStartedAt = null;
|
|
3540
|
+
logger.debug({ circuitId: this.options.id }, "Circuit breaker manually reset");
|
|
3541
|
+
}
|
|
3542
|
+
/**
|
|
3543
|
+
* Pre-warms the circuit by performing one test call in HALF-OPEN state.
|
|
3544
|
+
* If the circuit is already HALF-OPEN, this does nothing.
|
|
3545
|
+
* If the circuit is CLOSED, this does nothing.
|
|
3546
|
+
*/
|
|
3547
|
+
async preWarm(fn) {
|
|
3548
|
+
if (this.state !== "open") {
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
this.transitionToHalfOpen();
|
|
3552
|
+
try {
|
|
3553
|
+
await fn();
|
|
3554
|
+
this.transitionToClosed();
|
|
3555
|
+
} catch {
|
|
3556
|
+
this.transitionToOpen();
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3560
|
+
// Private methods
|
|
3561
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3562
|
+
async executeClosed(fn) {
|
|
3563
|
+
try {
|
|
3564
|
+
const result = await fn();
|
|
3565
|
+
this.onSuccess();
|
|
3566
|
+
return result;
|
|
3567
|
+
} catch (error) {
|
|
3568
|
+
this.onFailure(error);
|
|
3569
|
+
throw error;
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
async executeHalfOpen(fn) {
|
|
3573
|
+
this.halfOpenTestStartedAt = Date.now();
|
|
3574
|
+
try {
|
|
3575
|
+
const result = await fn();
|
|
3576
|
+
this.transitionToClosed();
|
|
3577
|
+
return result;
|
|
3578
|
+
} catch (error) {
|
|
3579
|
+
this.transitionToOpen();
|
|
3580
|
+
throw error;
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
onSuccess() {
|
|
3584
|
+
if (this.options.resetOnSuccess) {
|
|
3585
|
+
this.failures = 0;
|
|
3586
|
+
this.lastFailureAt = null;
|
|
3587
|
+
} else {
|
|
3588
|
+
this.failures = Math.max(0, this.failures - 1);
|
|
3589
|
+
if (this.failures === 0) {
|
|
3590
|
+
this.lastFailureAt = null;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
logger.debug(
|
|
3594
|
+
{
|
|
3595
|
+
circuitId: this.options.id,
|
|
3596
|
+
failures: this.failures
|
|
3597
|
+
},
|
|
3598
|
+
"Circuit breaker operation succeeded"
|
|
3599
|
+
);
|
|
3600
|
+
}
|
|
3601
|
+
onFailure(error) {
|
|
3602
|
+
if (!this.options.shouldCountAsFailure(error)) {
|
|
3603
|
+
logger.debug(
|
|
3604
|
+
{ circuitId: this.options.id, error },
|
|
3605
|
+
"Circuit breaker operation failed but error is not counted as failure"
|
|
3606
|
+
);
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3609
|
+
this.failures += 1;
|
|
3610
|
+
this.lastFailureAt = Date.now();
|
|
3611
|
+
if (this.failures >= this.options.failureThreshold) {
|
|
3612
|
+
this.transitionToOpen();
|
|
3613
|
+
} else {
|
|
3614
|
+
logger.debug(
|
|
3615
|
+
{
|
|
3616
|
+
circuitId: this.options.id,
|
|
3617
|
+
failures: this.failures,
|
|
3618
|
+
threshold: this.options.failureThreshold
|
|
3619
|
+
},
|
|
3620
|
+
"Circuit breaker recorded failure"
|
|
3621
|
+
);
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
transitionToOpen() {
|
|
3625
|
+
if (this.state === "open") {
|
|
3626
|
+
return;
|
|
3627
|
+
}
|
|
3628
|
+
this.state = "open";
|
|
3629
|
+
this.halfOpenTestStartedAt = null;
|
|
3630
|
+
logger.warn(
|
|
3631
|
+
{
|
|
3632
|
+
circuitId: this.options.id,
|
|
3633
|
+
failures: this.failures,
|
|
3634
|
+
windowMs: this.options.windowMs
|
|
3635
|
+
},
|
|
3636
|
+
"\u{1F534} Circuit breaker OPEN \u2014 blocking operations"
|
|
3637
|
+
);
|
|
3638
|
+
}
|
|
3639
|
+
transitionToHalfOpen() {
|
|
3640
|
+
this.state = "half-open";
|
|
3641
|
+
this.halfOpenTestStartedAt = Date.now();
|
|
3642
|
+
logger.info(
|
|
3643
|
+
{ circuitId: this.options.id },
|
|
3644
|
+
"\u{1F7E1} Circuit breaker HALF-OPEN \u2014 testing recovery"
|
|
3645
|
+
);
|
|
3646
|
+
}
|
|
3647
|
+
transitionToClosed() {
|
|
3648
|
+
this.state = "closed";
|
|
3649
|
+
this.failures = 0;
|
|
3650
|
+
this.lastFailureAt = null;
|
|
3651
|
+
this.halfOpenTestStartedAt = null;
|
|
3652
|
+
logger.info(
|
|
3653
|
+
{ circuitId: this.options.id },
|
|
3654
|
+
"\u{1F7E2} Circuit breaker CLOSED \u2014 recovery successful"
|
|
3655
|
+
);
|
|
3656
|
+
}
|
|
3657
|
+
shouldTransitionToHalfOpen() {
|
|
3658
|
+
if (this.lastFailureAt === null) {
|
|
3659
|
+
return true;
|
|
3660
|
+
}
|
|
3661
|
+
const elapsed = Date.now() - this.lastFailureAt;
|
|
3662
|
+
return elapsed >= this.options.halfOpenAfterMs;
|
|
3663
|
+
}
|
|
3664
|
+
};
|
|
3665
|
+
var SHARED_BREAKERS = Object.freeze({
|
|
3666
|
+
/**
|
|
3667
|
+
* Breaker for adapter event emission.
|
|
3668
|
+
* Threshold: 5 failures in 60s → open for 30s → half-open test.
|
|
3669
|
+
*/
|
|
3670
|
+
adapterEmit: new CircuitBreaker({
|
|
3671
|
+
id: "adapter.emit",
|
|
3672
|
+
failureThreshold: 5,
|
|
3673
|
+
halfOpenAfterMs: 3e4,
|
|
3674
|
+
shouldCountAsFailure: isRetryableError,
|
|
3675
|
+
windowMs: 6e4
|
|
3676
|
+
}),
|
|
3677
|
+
/**
|
|
3678
|
+
* Breaker for file system operations (transcript reading, config loading).
|
|
3679
|
+
* More tolerant: 10 failures in 60s → open for 30s.
|
|
3680
|
+
*/
|
|
3681
|
+
fileSystem: new CircuitBreaker({
|
|
3682
|
+
id: "filesystem",
|
|
3683
|
+
failureThreshold: 10,
|
|
3684
|
+
halfOpenAfterMs: 3e4,
|
|
3685
|
+
windowMs: 6e4
|
|
3686
|
+
}),
|
|
3687
|
+
/**
|
|
3688
|
+
* Breaker for HTTP/HTTPS requests.
|
|
3689
|
+
* Stricter: 3 failures in 30s → open for 15s.
|
|
3690
|
+
*/
|
|
3691
|
+
httpRequest: new CircuitBreaker({
|
|
3692
|
+
id: "http-request",
|
|
3693
|
+
failureThreshold: 3,
|
|
3694
|
+
halfOpenAfterMs: 15e3,
|
|
3695
|
+
windowMs: 3e4
|
|
3696
|
+
}),
|
|
3697
|
+
/**
|
|
3698
|
+
* Breaker for process detection operations.
|
|
3699
|
+
* Most tolerant: 20 failures in 60s → open for 10s.
|
|
3700
|
+
*/
|
|
3701
|
+
processDetection: new CircuitBreaker({
|
|
3702
|
+
id: "process-detection",
|
|
3703
|
+
failureThreshold: 20,
|
|
3704
|
+
halfOpenAfterMs: 1e4,
|
|
3705
|
+
windowMs: 6e4
|
|
3706
|
+
})
|
|
3707
|
+
});
|
|
3708
|
+
|
|
3354
3709
|
// src/core/engine/event-bus.ts
|
|
3355
3710
|
var import_eventemitter3 = require("eventemitter3");
|
|
3356
3711
|
var EventBus = class {
|
|
@@ -4061,7 +4416,7 @@ var UDSServer = class {
|
|
|
4061
4416
|
};
|
|
4062
4417
|
|
|
4063
4418
|
// src/core/engine/pipeline.ts
|
|
4064
|
-
var
|
|
4419
|
+
var import_node_path18 = require("path");
|
|
4065
4420
|
var import_zod4 = require("zod");
|
|
4066
4421
|
|
|
4067
4422
|
// src/adapters/aider.ts
|
|
@@ -4168,20 +4523,29 @@ var BaseAdapter = class {
|
|
|
4168
4523
|
});
|
|
4169
4524
|
let published;
|
|
4170
4525
|
try {
|
|
4171
|
-
published = await
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4526
|
+
published = await SHARED_BREAKERS.adapterEmit.execute(async () => {
|
|
4527
|
+
return await this.publishEventImplementation(event, {
|
|
4528
|
+
cwd: context.cwd,
|
|
4529
|
+
env: context.env,
|
|
4530
|
+
hookPayload: context.hookPayload,
|
|
4531
|
+
pid: context.pid,
|
|
4532
|
+
sessionId,
|
|
4533
|
+
source: context.source,
|
|
4534
|
+
transcriptPath: context.transcriptPath
|
|
4535
|
+
});
|
|
4179
4536
|
});
|
|
4180
4537
|
} catch (error) {
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4538
|
+
if (error instanceof Error && error.name === "CircuitOpenError") {
|
|
4539
|
+
logger.warn(
|
|
4540
|
+
{ error, eventType: type, adapter: this.name },
|
|
4541
|
+
"\u{1F4D6} Adapter emit blocked by open circuit \u2014 event dropped"
|
|
4542
|
+
);
|
|
4543
|
+
} else {
|
|
4544
|
+
logger.error(
|
|
4545
|
+
{ error, eventType: type, adapter: this.name, sessionId },
|
|
4546
|
+
"\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
|
|
4547
|
+
);
|
|
4548
|
+
}
|
|
4185
4549
|
published = false;
|
|
4186
4550
|
}
|
|
4187
4551
|
if (published) {
|
|
@@ -5195,7 +5559,11 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
5195
5559
|
return;
|
|
5196
5560
|
}
|
|
5197
5561
|
case "SessionEnd": {
|
|
5198
|
-
|
|
5562
|
+
const finalMessage = extractFinalMessageFromPayload(payload);
|
|
5563
|
+
await this.emitStateChange("session.end", {
|
|
5564
|
+
...sharedData,
|
|
5565
|
+
finalMessage
|
|
5566
|
+
}, context);
|
|
5199
5567
|
return;
|
|
5200
5568
|
}
|
|
5201
5569
|
case "UserPromptSubmit":
|
|
@@ -5212,12 +5580,22 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
5212
5580
|
return;
|
|
5213
5581
|
}
|
|
5214
5582
|
case "PreToolUse": {
|
|
5215
|
-
|
|
5583
|
+
const toolCallName = extractToolNameFromPayload(payload);
|
|
5584
|
+
await this.emitStateChange("agent.tool_call", {
|
|
5585
|
+
...sharedData,
|
|
5586
|
+
toolCallName
|
|
5587
|
+
}, context);
|
|
5216
5588
|
return;
|
|
5217
5589
|
}
|
|
5218
5590
|
case "PostToolUse": {
|
|
5591
|
+
const toolCallName = extractToolNameFromPayload(payload);
|
|
5592
|
+
const toolResult = extractToolResultFromPayload(payload);
|
|
5219
5593
|
const emittedType = isClaudeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
|
|
5220
|
-
await this.emitStateChange(emittedType,
|
|
5594
|
+
await this.emitStateChange(emittedType, {
|
|
5595
|
+
...sharedData,
|
|
5596
|
+
toolCallName,
|
|
5597
|
+
toolResult
|
|
5598
|
+
}, context);
|
|
5221
5599
|
return;
|
|
5222
5600
|
}
|
|
5223
5601
|
case "PostToolUseFailure":
|
|
@@ -5419,21 +5797,50 @@ function extractClaudeTranscriptObservations(payload, transcriptPath) {
|
|
|
5419
5797
|
};
|
|
5420
5798
|
const observations = [];
|
|
5421
5799
|
if (contentParts.some((part) => part.type === "thinking")) {
|
|
5800
|
+
const thinkingParts = contentParts.filter((part) => part.type === "thinking");
|
|
5801
|
+
const thinkingText = thinkingParts.map((part) => {
|
|
5802
|
+
const text = part.text;
|
|
5803
|
+
return typeof text === "string" ? text : void 0;
|
|
5804
|
+
}).filter((text) => text !== void 0).join("\n");
|
|
5422
5805
|
observations.push({
|
|
5423
5806
|
context: sharedContext,
|
|
5424
|
-
data:
|
|
5807
|
+
data: {
|
|
5808
|
+
...sharedData,
|
|
5809
|
+
thinkingContent: thinkingText.length > 0 ? thinkingText : void 0
|
|
5810
|
+
},
|
|
5425
5811
|
type: "agent.thinking"
|
|
5426
5812
|
});
|
|
5427
5813
|
}
|
|
5428
5814
|
if (contentParts.some(
|
|
5429
5815
|
(part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0
|
|
5430
5816
|
)) {
|
|
5817
|
+
const messageTexts = contentParts.filter((part) => part.type === "text").map((part) => part.text).filter((text) => text.trim().length > 0);
|
|
5818
|
+
const messageContent = messageTexts.join("\n");
|
|
5431
5819
|
observations.push({
|
|
5432
5820
|
context: sharedContext,
|
|
5433
|
-
data:
|
|
5821
|
+
data: {
|
|
5822
|
+
...sharedData,
|
|
5823
|
+
messageContent: messageContent.length > 0 ? messageContent : void 0
|
|
5824
|
+
},
|
|
5434
5825
|
type: "agent.streaming"
|
|
5435
5826
|
});
|
|
5436
5827
|
}
|
|
5828
|
+
const toolUseParts = contentParts.filter(
|
|
5829
|
+
(part) => part.type === "tool_use" || part.type === "toolUse"
|
|
5830
|
+
);
|
|
5831
|
+
if (toolUseParts.length > 0) {
|
|
5832
|
+
const toolName = getString(toolUseParts[0], "name") ?? getString(toolUseParts[0], "tool");
|
|
5833
|
+
if (toolName) {
|
|
5834
|
+
observations.push({
|
|
5835
|
+
context: sharedContext,
|
|
5836
|
+
data: {
|
|
5837
|
+
...sharedData,
|
|
5838
|
+
toolCallName: toolName
|
|
5839
|
+
},
|
|
5840
|
+
type: "agent.tool_call"
|
|
5841
|
+
});
|
|
5842
|
+
}
|
|
5843
|
+
}
|
|
5437
5844
|
return observations;
|
|
5438
5845
|
}
|
|
5439
5846
|
function extractClaudeContentParts(payload) {
|
|
@@ -5556,6 +5963,51 @@ function getString(payload, key) {
|
|
|
5556
5963
|
const value = payload[key];
|
|
5557
5964
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
5558
5965
|
}
|
|
5966
|
+
function extractToolNameFromPayload(payload) {
|
|
5967
|
+
const directToolName = getString(payload, "tool_name") ?? getString(payload, "toolName");
|
|
5968
|
+
if (directToolName) {
|
|
5969
|
+
return directToolName;
|
|
5970
|
+
}
|
|
5971
|
+
const toolUse = getRecord(payload.tool_use) ?? getRecord(payload.toolUse);
|
|
5972
|
+
if (toolUse) {
|
|
5973
|
+
return getString(toolUse, "name") ?? getString(toolUse, "tool");
|
|
5974
|
+
}
|
|
5975
|
+
const toolInput = getRecord(payload.tool_input) ?? getRecord(payload.toolInput);
|
|
5976
|
+
if (toolInput) {
|
|
5977
|
+
return getString(toolInput, "tool_name") ?? getString(toolInput, "type");
|
|
5978
|
+
}
|
|
5979
|
+
return void 0;
|
|
5980
|
+
}
|
|
5981
|
+
function extractToolResultFromPayload(payload) {
|
|
5982
|
+
const directResult = getString(payload, "result") ?? getString(payload, "output");
|
|
5983
|
+
if (directResult) {
|
|
5984
|
+
return directResult;
|
|
5985
|
+
}
|
|
5986
|
+
const toolResult = getRecord(payload.tool_result) ?? getRecord(payload.toolResult);
|
|
5987
|
+
if (toolResult) {
|
|
5988
|
+
return getString(toolResult, "content") ?? getString(toolResult, "output");
|
|
5989
|
+
}
|
|
5990
|
+
const errorField = getString(payload, "error") ?? getString(payload, "error_message");
|
|
5991
|
+
if (errorField) {
|
|
5992
|
+
return errorField;
|
|
5993
|
+
}
|
|
5994
|
+
return void 0;
|
|
5995
|
+
}
|
|
5996
|
+
function extractFinalMessageFromPayload(payload) {
|
|
5997
|
+
const directMessage = getString(payload, "final_message") ?? getString(payload, "finalMessage") ?? getString(payload, "summary") ?? getString(payload, "completion_message");
|
|
5998
|
+
if (directMessage) {
|
|
5999
|
+
return directMessage;
|
|
6000
|
+
}
|
|
6001
|
+
const result = getString(payload, "result") ?? getString(payload, "output") ?? getString(payload, "message");
|
|
6002
|
+
if (result) {
|
|
6003
|
+
return result;
|
|
6004
|
+
}
|
|
6005
|
+
const stats = getRecord(payload.stats);
|
|
6006
|
+
if (stats) {
|
|
6007
|
+
return getString(stats, "summary") ?? getString(stats, "completion_summary");
|
|
6008
|
+
}
|
|
6009
|
+
return void 0;
|
|
6010
|
+
}
|
|
5559
6011
|
|
|
5560
6012
|
// src/adapters/copilot-cli.ts
|
|
5561
6013
|
var import_node_child_process5 = require("child_process");
|
|
@@ -10529,7 +10981,11 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
10529
10981
|
return;
|
|
10530
10982
|
}
|
|
10531
10983
|
case "session.deleted": {
|
|
10532
|
-
|
|
10984
|
+
const finalMessage = extractOpenCodeFinalMessage(payload);
|
|
10985
|
+
await this.emitStateChange("session.end", {
|
|
10986
|
+
...sharedData,
|
|
10987
|
+
finalMessage
|
|
10988
|
+
}, context);
|
|
10533
10989
|
return;
|
|
10534
10990
|
}
|
|
10535
10991
|
case "session.error": {
|
|
@@ -10554,12 +11010,22 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
10554
11010
|
return;
|
|
10555
11011
|
}
|
|
10556
11012
|
case "tool.execute.before": {
|
|
10557
|
-
|
|
11013
|
+
const toolCallName = extractOpenCodeToolName(payload);
|
|
11014
|
+
await this.emitStateChange("agent.tool_call", {
|
|
11015
|
+
...sharedData,
|
|
11016
|
+
toolCallName
|
|
11017
|
+
}, context);
|
|
10558
11018
|
return;
|
|
10559
11019
|
}
|
|
10560
11020
|
case "tool.execute.after": {
|
|
11021
|
+
const toolCallName = extractOpenCodeToolName(payload);
|
|
11022
|
+
const toolResult = extractOpenCodeToolResult(payload);
|
|
10561
11023
|
const emittedType = isOpenCodeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
|
|
10562
|
-
await this.emitStateChange(emittedType,
|
|
11024
|
+
await this.emitStateChange(emittedType, {
|
|
11025
|
+
...sharedData,
|
|
11026
|
+
toolCallName,
|
|
11027
|
+
toolResult
|
|
11028
|
+
}, context);
|
|
10563
11029
|
return;
|
|
10564
11030
|
}
|
|
10565
11031
|
default: {
|
|
@@ -10726,6 +11192,535 @@ function getString10(payload, key) {
|
|
|
10726
11192
|
const value = payload[key];
|
|
10727
11193
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
10728
11194
|
}
|
|
11195
|
+
function extractOpenCodeFinalMessage(payload) {
|
|
11196
|
+
const directMessage = getString10(payload, "final_message") ?? getString10(payload, "finalMessage") ?? getString10(payload, "summary") ?? getString10(payload, "completion_message");
|
|
11197
|
+
if (directMessage) {
|
|
11198
|
+
return directMessage;
|
|
11199
|
+
}
|
|
11200
|
+
const result = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(getRecord9(payload.properties), "result");
|
|
11201
|
+
if (result) {
|
|
11202
|
+
return result;
|
|
11203
|
+
}
|
|
11204
|
+
return void 0;
|
|
11205
|
+
}
|
|
11206
|
+
function extractOpenCodeToolResult(payload) {
|
|
11207
|
+
const directResult = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(payload, "toolResult");
|
|
11208
|
+
if (directResult) {
|
|
11209
|
+
return directResult;
|
|
11210
|
+
}
|
|
11211
|
+
const toolResult = getRecord9(payload.tool_result) ?? getRecord9(payload.toolResult);
|
|
11212
|
+
if (toolResult) {
|
|
11213
|
+
return getString10(toolResult, "content") ?? getString10(toolResult, "output");
|
|
11214
|
+
}
|
|
11215
|
+
const props = getRecord9(payload.properties);
|
|
11216
|
+
if (props) {
|
|
11217
|
+
const nestedResult = getRecord9(props.tool_result) ?? getRecord9(props.toolResult);
|
|
11218
|
+
if (nestedResult) {
|
|
11219
|
+
return getString10(nestedResult, "content") ?? getString10(nestedResult, "output");
|
|
11220
|
+
}
|
|
11221
|
+
}
|
|
11222
|
+
return void 0;
|
|
11223
|
+
}
|
|
11224
|
+
|
|
11225
|
+
// src/adapters/pi.ts
|
|
11226
|
+
var import_node_child_process14 = require("child_process");
|
|
11227
|
+
var import_node_path16 = require("path");
|
|
11228
|
+
var import_node_util14 = require("util");
|
|
11229
|
+
var execFileAsync = (0, import_node_util14.promisify)(import_node_child_process14.execFile);
|
|
11230
|
+
var PiAdapter = class extends BaseAdapter {
|
|
11231
|
+
displayName = "Pi (MiniMax)";
|
|
11232
|
+
name = "pi";
|
|
11233
|
+
strategies = [
|
|
11234
|
+
"process-detect",
|
|
11235
|
+
"api-client",
|
|
11236
|
+
"log-watch"
|
|
11237
|
+
];
|
|
11238
|
+
apiPort = 7890;
|
|
11239
|
+
logPath;
|
|
11240
|
+
poller = null;
|
|
11241
|
+
activePiSessions = /* @__PURE__ */ new Map();
|
|
11242
|
+
lastCheckedTime = 0;
|
|
11243
|
+
constructor(options) {
|
|
11244
|
+
super(options);
|
|
11245
|
+
this.logPath = (0, import_node_path16.join)(
|
|
11246
|
+
options.homeDirectory ?? process.env.HOME ?? "",
|
|
11247
|
+
".pi",
|
|
11248
|
+
"agent.log"
|
|
11249
|
+
);
|
|
11250
|
+
}
|
|
11251
|
+
start() {
|
|
11252
|
+
if (this.getStatus().running) {
|
|
11253
|
+
return Promise.resolve();
|
|
11254
|
+
}
|
|
11255
|
+
this.setRunning(true);
|
|
11256
|
+
this.startPolling();
|
|
11257
|
+
logger.info({ adapter: this.name }, "Pi adapter started");
|
|
11258
|
+
return Promise.resolve();
|
|
11259
|
+
}
|
|
11260
|
+
stop() {
|
|
11261
|
+
if (this.poller !== null) {
|
|
11262
|
+
clearInterval(this.poller);
|
|
11263
|
+
this.poller = null;
|
|
11264
|
+
}
|
|
11265
|
+
this.setRunning(false);
|
|
11266
|
+
logger.info({ adapter: this.name }, "Pi adapter stopped");
|
|
11267
|
+
return Promise.resolve();
|
|
11268
|
+
}
|
|
11269
|
+
async handleHook(payload) {
|
|
11270
|
+
const normalized = this.parseNormalizedHookPayload(payload);
|
|
11271
|
+
if (normalized === null) {
|
|
11272
|
+
return;
|
|
11273
|
+
}
|
|
11274
|
+
const context = {
|
|
11275
|
+
cwd: normalized.cwd,
|
|
11276
|
+
pid: normalized.pid,
|
|
11277
|
+
sessionId: normalized.sessionId,
|
|
11278
|
+
source: "pi-hook"
|
|
11279
|
+
};
|
|
11280
|
+
const eventType = this.mapEventType(normalized.type ?? "");
|
|
11281
|
+
const eventData = this.buildEventData(eventType, normalized);
|
|
11282
|
+
await this.emit(eventType, eventData, context);
|
|
11283
|
+
}
|
|
11284
|
+
startPolling() {
|
|
11285
|
+
this.poller = setInterval(() => {
|
|
11286
|
+
void this.pollPiActivity();
|
|
11287
|
+
}, 2e3);
|
|
11288
|
+
}
|
|
11289
|
+
async pollPiActivity() {
|
|
11290
|
+
const running = await this.detectPiInstance();
|
|
11291
|
+
if (!running) {
|
|
11292
|
+
for (const [sessionId, activity] of this.activePiSessions) {
|
|
11293
|
+
if (activity.state !== "idle") {
|
|
11294
|
+
activity.state = "idle";
|
|
11295
|
+
await this.emitIdle(sessionId);
|
|
11296
|
+
}
|
|
11297
|
+
}
|
|
11298
|
+
return;
|
|
11299
|
+
}
|
|
11300
|
+
try {
|
|
11301
|
+
const response = await fetch(
|
|
11302
|
+
`http://127.0.0.1:${this.apiPort}/api/status`,
|
|
11303
|
+
{
|
|
11304
|
+
signal: AbortSignal.timeout(500)
|
|
11305
|
+
}
|
|
11306
|
+
);
|
|
11307
|
+
if (response.ok) {
|
|
11308
|
+
const data = await response.json();
|
|
11309
|
+
await this.processPiApiResponse(data);
|
|
11310
|
+
}
|
|
11311
|
+
} catch {
|
|
11312
|
+
await this.checkMiniMaxApi();
|
|
11313
|
+
}
|
|
11314
|
+
}
|
|
11315
|
+
async detectPiInstance() {
|
|
11316
|
+
try {
|
|
11317
|
+
const result = await execFileAsync("pgrep", ["-l", "pi|minimax"]);
|
|
11318
|
+
if (result.stdout.includes("pi") || result.stdout.includes("minimax")) {
|
|
11319
|
+
return true;
|
|
11320
|
+
}
|
|
11321
|
+
} catch {
|
|
11322
|
+
}
|
|
11323
|
+
try {
|
|
11324
|
+
const response = await fetch(
|
|
11325
|
+
`http://127.0.0.1:${this.apiPort}/health`,
|
|
11326
|
+
{
|
|
11327
|
+
signal: AbortSignal.timeout(200)
|
|
11328
|
+
}
|
|
11329
|
+
);
|
|
11330
|
+
if (response.ok) {
|
|
11331
|
+
return true;
|
|
11332
|
+
}
|
|
11333
|
+
} catch {
|
|
11334
|
+
}
|
|
11335
|
+
return false;
|
|
11336
|
+
}
|
|
11337
|
+
async checkMiniMaxApi() {
|
|
11338
|
+
try {
|
|
11339
|
+
const response = await fetch("http://127.0.0.1:3000/api/agent/status", {
|
|
11340
|
+
signal: AbortSignal.timeout(500)
|
|
11341
|
+
});
|
|
11342
|
+
if (response.ok) {
|
|
11343
|
+
const data = await response.json();
|
|
11344
|
+
await this.processPiApiResponse(data);
|
|
11345
|
+
}
|
|
11346
|
+
} catch {
|
|
11347
|
+
}
|
|
11348
|
+
}
|
|
11349
|
+
async processPiApiResponse(data) {
|
|
11350
|
+
const rawSession = data.sessionId ?? data.project ?? "default";
|
|
11351
|
+
const sessionId = `pi:${rawSession.replace(/[^a-zA-Z0-9-_]/g, "-")}`;
|
|
11352
|
+
let activity = this.activePiSessions.get(sessionId);
|
|
11353
|
+
if (!activity) {
|
|
11354
|
+
activity = { sessionId, state: "idle" };
|
|
11355
|
+
this.activePiSessions.set(sessionId, activity);
|
|
11356
|
+
await this.emitSessionStart(sessionId, data);
|
|
11357
|
+
}
|
|
11358
|
+
const rawState = data.state ?? "idle";
|
|
11359
|
+
const state = rawState;
|
|
11360
|
+
if (state !== activity.state) {
|
|
11361
|
+
switch (state) {
|
|
11362
|
+
case "thinking": {
|
|
11363
|
+
const rawThinking = data.thinking;
|
|
11364
|
+
if (rawThinking) {
|
|
11365
|
+
await this.emitThinking(sessionId, rawThinking);
|
|
11366
|
+
}
|
|
11367
|
+
break;
|
|
11368
|
+
}
|
|
11369
|
+
case "tool": {
|
|
11370
|
+
const rawFilePath = data.filePath;
|
|
11371
|
+
const rawCommand = data.command;
|
|
11372
|
+
const rawToolName = data.toolName ?? "unknown";
|
|
11373
|
+
await this.emitToolCall(
|
|
11374
|
+
sessionId,
|
|
11375
|
+
{
|
|
11376
|
+
filePath: rawFilePath ?? "",
|
|
11377
|
+
command: rawCommand ?? ""
|
|
11378
|
+
},
|
|
11379
|
+
rawToolName
|
|
11380
|
+
);
|
|
11381
|
+
break;
|
|
11382
|
+
}
|
|
11383
|
+
case "output": {
|
|
11384
|
+
const rawOutput = data.output;
|
|
11385
|
+
if (rawOutput) {
|
|
11386
|
+
await this.emitOutput(sessionId, rawOutput);
|
|
11387
|
+
}
|
|
11388
|
+
break;
|
|
11389
|
+
}
|
|
11390
|
+
case "error": {
|
|
11391
|
+
const rawError = data.error ?? "Unknown error";
|
|
11392
|
+
await this.emitError(sessionId, rawError);
|
|
11393
|
+
break;
|
|
11394
|
+
}
|
|
11395
|
+
case "idle":
|
|
11396
|
+
await this.emitIdle(sessionId);
|
|
11397
|
+
break;
|
|
11398
|
+
}
|
|
11399
|
+
activity.state = state;
|
|
11400
|
+
}
|
|
11401
|
+
}
|
|
11402
|
+
async emitSessionStart(sessionId, data) {
|
|
11403
|
+
const rawProject = data.project ?? "pi-project";
|
|
11404
|
+
const rawModel = data.model ?? "minimax/moonshot";
|
|
11405
|
+
const eventData = {
|
|
11406
|
+
state: "session.start",
|
|
11407
|
+
project: rawProject,
|
|
11408
|
+
model: rawModel,
|
|
11409
|
+
raw: data
|
|
11410
|
+
};
|
|
11411
|
+
await this.emit("session.start", eventData, { sessionId });
|
|
11412
|
+
}
|
|
11413
|
+
async emitThinking(sessionId, content) {
|
|
11414
|
+
if (!content) return;
|
|
11415
|
+
const eventData = {
|
|
11416
|
+
state: "agent.thinking",
|
|
11417
|
+
thinkingContent: content
|
|
11418
|
+
};
|
|
11419
|
+
await this.emit("agent.thinking", eventData, { sessionId });
|
|
11420
|
+
}
|
|
11421
|
+
async emitToolCall(sessionId, toolInput, toolName) {
|
|
11422
|
+
const eventData = {
|
|
11423
|
+
state: "agent.tool_call",
|
|
11424
|
+
toolCallName: toolName,
|
|
11425
|
+
toolInput,
|
|
11426
|
+
activeFile: toolInput.filePath
|
|
11427
|
+
};
|
|
11428
|
+
await this.emit("agent.tool_call", eventData, { sessionId });
|
|
11429
|
+
}
|
|
11430
|
+
async emitOutput(sessionId, content) {
|
|
11431
|
+
if (!content) return;
|
|
11432
|
+
const eventData = {
|
|
11433
|
+
state: "agent.streaming",
|
|
11434
|
+
messageContent: content
|
|
11435
|
+
};
|
|
11436
|
+
await this.emit("agent.streaming", eventData, { sessionId });
|
|
11437
|
+
}
|
|
11438
|
+
async emitError(sessionId, errorMessage) {
|
|
11439
|
+
const eventData = {
|
|
11440
|
+
state: "agent.error",
|
|
11441
|
+
errorMessage,
|
|
11442
|
+
errorType: "api_error"
|
|
11443
|
+
};
|
|
11444
|
+
await this.emit("agent.error", eventData, { sessionId });
|
|
11445
|
+
}
|
|
11446
|
+
async emitIdle(sessionId) {
|
|
11447
|
+
const eventData = {
|
|
11448
|
+
state: "agent.idle"
|
|
11449
|
+
};
|
|
11450
|
+
await this.emit("agent.idle", eventData, { sessionId });
|
|
11451
|
+
}
|
|
11452
|
+
mapEventType(type) {
|
|
11453
|
+
const mapping = {
|
|
11454
|
+
"session.start": "session.start",
|
|
11455
|
+
"session.end": "session.end",
|
|
11456
|
+
"task.start": "task.start",
|
|
11457
|
+
"task.complete": "task.complete",
|
|
11458
|
+
thinking: "agent.thinking",
|
|
11459
|
+
tool: "agent.tool_call",
|
|
11460
|
+
coding: "agent.coding",
|
|
11461
|
+
output: "agent.streaming",
|
|
11462
|
+
message: "agent.streaming",
|
|
11463
|
+
ask: "agent.asking_user",
|
|
11464
|
+
error: "agent.error",
|
|
11465
|
+
idle: "agent.idle",
|
|
11466
|
+
compact: "agent.compact"
|
|
11467
|
+
};
|
|
11468
|
+
return mapping[type] ?? "agent.streaming";
|
|
11469
|
+
}
|
|
11470
|
+
buildEventData(eventType, payload) {
|
|
11471
|
+
const data = payload.data ?? {};
|
|
11472
|
+
return {
|
|
11473
|
+
state: eventType,
|
|
11474
|
+
project: data.project,
|
|
11475
|
+
activeFile: data.activeFile,
|
|
11476
|
+
model: data.model,
|
|
11477
|
+
toolInput: data.toolInput,
|
|
11478
|
+
toolCallName: data.toolCallName,
|
|
11479
|
+
thinkingContent: data.thinkingContent,
|
|
11480
|
+
messageContent: data.messageContent,
|
|
11481
|
+
finalMessage: data.finalMessage,
|
|
11482
|
+
toolResult: data.toolResult,
|
|
11483
|
+
errorMessage: data.errorMessage,
|
|
11484
|
+
errorType: data.errorType,
|
|
11485
|
+
raw: data.raw
|
|
11486
|
+
};
|
|
11487
|
+
}
|
|
11488
|
+
};
|
|
11489
|
+
|
|
11490
|
+
// src/adapters/zed.ts
|
|
11491
|
+
var import_promises15 = require("fs/promises");
|
|
11492
|
+
var import_node_path17 = require("path");
|
|
11493
|
+
var ZedAdapter = class extends BaseAdapter {
|
|
11494
|
+
displayName = "Zed AI";
|
|
11495
|
+
name = "zed";
|
|
11496
|
+
strategies = [
|
|
11497
|
+
"process-detect",
|
|
11498
|
+
"api-client"
|
|
11499
|
+
];
|
|
11500
|
+
logPaths;
|
|
11501
|
+
apiPort = 9876;
|
|
11502
|
+
pollIntervalMs;
|
|
11503
|
+
poller = null;
|
|
11504
|
+
lastEventTime = 0;
|
|
11505
|
+
activeZedSessions = /* @__PURE__ */ new Map();
|
|
11506
|
+
constructor(options) {
|
|
11507
|
+
super(options);
|
|
11508
|
+
this.pollIntervalMs = 2e3;
|
|
11509
|
+
this.logPaths = [
|
|
11510
|
+
(0, import_node_path17.join)(options.homeDirectory ?? process.env.HOME ?? "", ".config", "zed", "logs", "agent.log"),
|
|
11511
|
+
"/tmp/zed-agent.log"
|
|
11512
|
+
];
|
|
11513
|
+
}
|
|
11514
|
+
start() {
|
|
11515
|
+
if (this.getStatus().running) {
|
|
11516
|
+
return Promise.resolve();
|
|
11517
|
+
}
|
|
11518
|
+
this.setRunning(true);
|
|
11519
|
+
this.startPolling();
|
|
11520
|
+
logger.info({ adapter: this.name }, "Zed adapter started");
|
|
11521
|
+
return Promise.resolve();
|
|
11522
|
+
}
|
|
11523
|
+
stop() {
|
|
11524
|
+
if (this.poller !== null) {
|
|
11525
|
+
clearInterval(this.poller);
|
|
11526
|
+
this.poller = null;
|
|
11527
|
+
}
|
|
11528
|
+
this.setRunning(false);
|
|
11529
|
+
logger.info({ adapter: this.name }, "Zed adapter stopped");
|
|
11530
|
+
return Promise.resolve();
|
|
11531
|
+
}
|
|
11532
|
+
async handleHook(payload) {
|
|
11533
|
+
const normalized = this.parseNormalizedHookPayload(payload);
|
|
11534
|
+
if (normalized === null) {
|
|
11535
|
+
return;
|
|
11536
|
+
}
|
|
11537
|
+
const context = {
|
|
11538
|
+
cwd: normalized.cwd,
|
|
11539
|
+
pid: normalized.pid,
|
|
11540
|
+
sessionId: normalized.sessionId,
|
|
11541
|
+
source: "zed-hook"
|
|
11542
|
+
};
|
|
11543
|
+
const eventType = this.mapEventType(normalized.type ?? "");
|
|
11544
|
+
const eventData = this.buildEventData(eventType, normalized);
|
|
11545
|
+
await this.emit(eventType, eventData, context);
|
|
11546
|
+
}
|
|
11547
|
+
startPolling() {
|
|
11548
|
+
this.poller = setInterval(() => {
|
|
11549
|
+
void this.pollZedStatus();
|
|
11550
|
+
}, this.pollIntervalMs);
|
|
11551
|
+
}
|
|
11552
|
+
async pollZedStatus() {
|
|
11553
|
+
try {
|
|
11554
|
+
const response = await fetch(`http://127.0.0.1:${this.apiPort}/api/agent/status`, {
|
|
11555
|
+
signal: AbortSignal.timeout(1e3)
|
|
11556
|
+
});
|
|
11557
|
+
if (response.ok) {
|
|
11558
|
+
const data = await response.json();
|
|
11559
|
+
if (data.sessionId && typeof data.sessionId === "string") {
|
|
11560
|
+
const sessionId = `zed:${data.sessionId}`;
|
|
11561
|
+
if (!this.activeZedSessions.has(sessionId)) {
|
|
11562
|
+
this.activeZedSessions.set(sessionId, sessionId);
|
|
11563
|
+
await this.emitSessionStart(sessionId, data);
|
|
11564
|
+
}
|
|
11565
|
+
if (data.state === "thinking" && this.lastEventTime < Date.now() - 5e3) {
|
|
11566
|
+
const rawThinking = data.thinking;
|
|
11567
|
+
if (rawThinking) {
|
|
11568
|
+
await this.emitThinking(sessionId, rawThinking);
|
|
11569
|
+
}
|
|
11570
|
+
} else if (data.state === "tool" && data.toolName) {
|
|
11571
|
+
const rawFilePath = data.filePath;
|
|
11572
|
+
const rawCommand = data.command;
|
|
11573
|
+
const rawToolName = data.toolName ?? "unknown";
|
|
11574
|
+
await this.emitToolCall(
|
|
11575
|
+
sessionId,
|
|
11576
|
+
{
|
|
11577
|
+
filePath: rawFilePath ?? "",
|
|
11578
|
+
command: rawCommand ?? ""
|
|
11579
|
+
},
|
|
11580
|
+
rawToolName
|
|
11581
|
+
);
|
|
11582
|
+
} else if (data.state === "idle") {
|
|
11583
|
+
await this.emitIdle(sessionId);
|
|
11584
|
+
}
|
|
11585
|
+
}
|
|
11586
|
+
}
|
|
11587
|
+
} catch {
|
|
11588
|
+
await this.checkLogFiles();
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
async checkLogFiles() {
|
|
11592
|
+
for (const logPath of this.logPaths) {
|
|
11593
|
+
try {
|
|
11594
|
+
const content = await (0, import_promises15.readFile)(logPath, "utf8");
|
|
11595
|
+
await this.parseLogContent(content);
|
|
11596
|
+
} catch {
|
|
11597
|
+
}
|
|
11598
|
+
}
|
|
11599
|
+
}
|
|
11600
|
+
async parseLogContent(content) {
|
|
11601
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
11602
|
+
for (const line of lines) {
|
|
11603
|
+
if (this.lastEventTime > 0 && this.lastEventTime >= Date.now() - 2e3) {
|
|
11604
|
+
continue;
|
|
11605
|
+
}
|
|
11606
|
+
const event = this.extractZedEventFromLog(line);
|
|
11607
|
+
if (event) {
|
|
11608
|
+
await this.handleHook(event);
|
|
11609
|
+
this.lastEventTime = Date.now();
|
|
11610
|
+
}
|
|
11611
|
+
}
|
|
11612
|
+
}
|
|
11613
|
+
extractZedEventFromLog(line) {
|
|
11614
|
+
try {
|
|
11615
|
+
const parsed = JSON.parse(line);
|
|
11616
|
+
if (!parsed.type || typeof parsed.type !== "string") {
|
|
11617
|
+
return null;
|
|
11618
|
+
}
|
|
11619
|
+
const rawSessionId = parsed.sessionId;
|
|
11620
|
+
const rawWorkspace = parsed.workspace;
|
|
11621
|
+
const sessionId = rawSessionId ? rawSessionId : rawWorkspace ? `zed:${(0, import_node_path17.basename)(rawWorkspace)}` : `zed:${Date.now()}`;
|
|
11622
|
+
const rawCwd = parsed.cwd ?? rawWorkspace;
|
|
11623
|
+
return {
|
|
11624
|
+
type: parsed.type,
|
|
11625
|
+
sessionId,
|
|
11626
|
+
cwd: rawCwd,
|
|
11627
|
+
data: {
|
|
11628
|
+
project: parsed.project ?? rawWorkspace ? (0, import_node_path17.basename)(String(rawWorkspace)) : void 0,
|
|
11629
|
+
model: parsed.model,
|
|
11630
|
+
state: parsed.type,
|
|
11631
|
+
thinkingContent: parsed.thinking,
|
|
11632
|
+
toolCallName: parsed.toolName,
|
|
11633
|
+
toolInput: parsed.toolInput,
|
|
11634
|
+
messageContent: parsed.message ?? parsed.output,
|
|
11635
|
+
errorMessage: parsed.error,
|
|
11636
|
+
raw: parsed
|
|
11637
|
+
}
|
|
11638
|
+
};
|
|
11639
|
+
} catch {
|
|
11640
|
+
if (line.includes("[zed:agent]")) {
|
|
11641
|
+
const cleaned = line.replace(/^\[.*?\] \[.*?\] /, "");
|
|
11642
|
+
if (cleaned.includes("Thinking:")) {
|
|
11643
|
+
return { type: "thinking", thinkingContent: cleaned.replace("Thinking:", "").trim() };
|
|
11644
|
+
}
|
|
11645
|
+
if (cleaned.includes("Executing tool:")) {
|
|
11646
|
+
return { type: "tool", toolCallName: cleaned.replace("Executing tool:", "").trim() };
|
|
11647
|
+
}
|
|
11648
|
+
if (cleaned.includes("Error:")) {
|
|
11649
|
+
return { type: "error", errorMessage: cleaned.replace("Error:", "").trim() };
|
|
11650
|
+
}
|
|
11651
|
+
}
|
|
11652
|
+
return null;
|
|
11653
|
+
}
|
|
11654
|
+
}
|
|
11655
|
+
async emitSessionStart(sessionId, _data) {
|
|
11656
|
+
const eventData = {
|
|
11657
|
+
state: "session.start",
|
|
11658
|
+
project: sessionId.split(":")[1] ?? "unknown"
|
|
11659
|
+
};
|
|
11660
|
+
await this.emit("session.start", eventData, { sessionId });
|
|
11661
|
+
}
|
|
11662
|
+
async emitThinking(sessionId, content) {
|
|
11663
|
+
if (!content) return;
|
|
11664
|
+
const eventData = {
|
|
11665
|
+
state: "agent.thinking",
|
|
11666
|
+
thinkingContent: content
|
|
11667
|
+
};
|
|
11668
|
+
await this.emit("agent.thinking", eventData, { sessionId });
|
|
11669
|
+
this.lastEventTime = Date.now();
|
|
11670
|
+
}
|
|
11671
|
+
async emitToolCall(sessionId, toolInput, toolName) {
|
|
11672
|
+
const eventData = {
|
|
11673
|
+
state: "agent.tool_call",
|
|
11674
|
+
toolCallName: toolName,
|
|
11675
|
+
toolInput,
|
|
11676
|
+
activeFile: toolInput.filePath
|
|
11677
|
+
};
|
|
11678
|
+
await this.emit("agent.tool_call", eventData, { sessionId });
|
|
11679
|
+
this.lastEventTime = Date.now();
|
|
11680
|
+
}
|
|
11681
|
+
async emitIdle(sessionId) {
|
|
11682
|
+
const eventData = {
|
|
11683
|
+
state: "agent.idle"
|
|
11684
|
+
};
|
|
11685
|
+
await this.emit("agent.idle", eventData, { sessionId });
|
|
11686
|
+
}
|
|
11687
|
+
mapEventType(type) {
|
|
11688
|
+
const mapping = {
|
|
11689
|
+
"session.start": "session.start",
|
|
11690
|
+
"session.end": "session.end",
|
|
11691
|
+
"task.start": "task.start",
|
|
11692
|
+
"task.complete": "task.complete",
|
|
11693
|
+
thinking: "agent.thinking",
|
|
11694
|
+
tool: "agent.tool_call",
|
|
11695
|
+
coding: "agent.coding",
|
|
11696
|
+
output: "agent.streaming",
|
|
11697
|
+
message: "agent.streaming",
|
|
11698
|
+
ask: "agent.asking_user",
|
|
11699
|
+
error: "agent.error",
|
|
11700
|
+
idle: "agent.idle",
|
|
11701
|
+
compact: "agent.compact"
|
|
11702
|
+
};
|
|
11703
|
+
return mapping[type] ?? "agent.streaming";
|
|
11704
|
+
}
|
|
11705
|
+
buildEventData(eventType, payload) {
|
|
11706
|
+
const data = payload.data ?? {};
|
|
11707
|
+
return {
|
|
11708
|
+
state: eventType,
|
|
11709
|
+
project: data.project,
|
|
11710
|
+
activeFile: data.activeFile,
|
|
11711
|
+
model: data.model,
|
|
11712
|
+
toolInput: data.toolInput,
|
|
11713
|
+
toolCallName: data.toolCallName,
|
|
11714
|
+
thinkingContent: data.thinkingContent,
|
|
11715
|
+
messageContent: data.messageContent,
|
|
11716
|
+
finalMessage: data.finalMessage,
|
|
11717
|
+
toolResult: data.toolResult,
|
|
11718
|
+
errorMessage: data.errorMessage,
|
|
11719
|
+
errorType: data.errorType,
|
|
11720
|
+
raw: data.raw
|
|
11721
|
+
};
|
|
11722
|
+
}
|
|
11723
|
+
};
|
|
10729
11724
|
|
|
10730
11725
|
// src/adapters/registry.ts
|
|
10731
11726
|
var AdapterRegistry = class {
|
|
@@ -10810,7 +11805,9 @@ function createDefaultAdapters(options) {
|
|
|
10810
11805
|
new KiloAdapter(options),
|
|
10811
11806
|
new CodexAdapter(options),
|
|
10812
11807
|
new OpenClawAdapter(options),
|
|
10813
|
-
new OpenCodeAdapter(options)
|
|
11808
|
+
new OpenCodeAdapter(options),
|
|
11809
|
+
new PiAdapter(options),
|
|
11810
|
+
new ZedAdapter(options)
|
|
10814
11811
|
];
|
|
10815
11812
|
}
|
|
10816
11813
|
|
|
@@ -10831,7 +11828,7 @@ function getSocketPath(aisnitchHomePath) {
|
|
|
10831
11828
|
if (process.platform === "win32") {
|
|
10832
11829
|
return "\\\\.\\pipe\\aisnitch.sock";
|
|
10833
11830
|
}
|
|
10834
|
-
return (0,
|
|
11831
|
+
return (0, import_node_path18.join)(aisnitchHomePath, "aisnitch.sock");
|
|
10835
11832
|
}
|
|
10836
11833
|
var Pipeline = class {
|
|
10837
11834
|
eventBus = new EventBus();
|
|
@@ -11036,6 +12033,30 @@ var Pipeline = class {
|
|
|
11036
12033
|
getEventBus() {
|
|
11037
12034
|
return this.eventBus;
|
|
11038
12035
|
}
|
|
12036
|
+
/**
|
|
12037
|
+
* Returns the adapter registry for graceful shutdown coordination.
|
|
12038
|
+
*/
|
|
12039
|
+
getAdapterRegistry() {
|
|
12040
|
+
return this.adapterRegistry ?? void 0;
|
|
12041
|
+
}
|
|
12042
|
+
/**
|
|
12043
|
+
* Returns the HTTP receiver for graceful shutdown coordination.
|
|
12044
|
+
*/
|
|
12045
|
+
getHttpReceiver() {
|
|
12046
|
+
return this.httpReceiver;
|
|
12047
|
+
}
|
|
12048
|
+
/**
|
|
12049
|
+
* Returns the UDS server for graceful shutdown coordination.
|
|
12050
|
+
*/
|
|
12051
|
+
getUdsServer() {
|
|
12052
|
+
return this.udsServer;
|
|
12053
|
+
}
|
|
12054
|
+
/**
|
|
12055
|
+
* Returns the WebSocket server for graceful shutdown coordination.
|
|
12056
|
+
*/
|
|
12057
|
+
getWsServer() {
|
|
12058
|
+
return this.wsServer;
|
|
12059
|
+
}
|
|
11039
12060
|
getHealthSnapshot() {
|
|
11040
12061
|
const status = this.getStatus();
|
|
11041
12062
|
return {
|
|
@@ -11116,6 +12137,116 @@ var Pipeline = class {
|
|
|
11116
12137
|
}
|
|
11117
12138
|
};
|
|
11118
12139
|
|
|
12140
|
+
// src/core/timeout.ts
|
|
12141
|
+
var DEFAULT_TIMEOUTS = Object.freeze({
|
|
12142
|
+
/**
|
|
12143
|
+
* File read/write operations (JSONL transcripts, config files).
|
|
12144
|
+
* Default: 5 seconds
|
|
12145
|
+
*/
|
|
12146
|
+
fileOperation: 5e3,
|
|
12147
|
+
/**
|
|
12148
|
+
* HTTP requests to health endpoint or external APIs.
|
|
12149
|
+
* Default: 30 seconds
|
|
12150
|
+
*/
|
|
12151
|
+
httpRequest: 3e4,
|
|
12152
|
+
/**
|
|
12153
|
+
* Process detection commands (`pgrep`, `ps aux`).
|
|
12154
|
+
* Default: 3 seconds
|
|
12155
|
+
*/
|
|
12156
|
+
processDetection: 3e3,
|
|
12157
|
+
/**
|
|
12158
|
+
* Adapter startup (file watchers, hook bridges, pollers).
|
|
12159
|
+
* Default: 10 seconds
|
|
12160
|
+
*/
|
|
12161
|
+
adapterStartup: 1e4,
|
|
12162
|
+
/**
|
|
12163
|
+
* Adapter shutdown (graceful cleanup, watcher close).
|
|
12164
|
+
* Default: 5 seconds — after this, resources are force-closed
|
|
12165
|
+
*/
|
|
12166
|
+
adapterShutdown: 5e3,
|
|
12167
|
+
/**
|
|
12168
|
+
* Daemon graceful shutdown (stop all components in order).
|
|
12169
|
+
* Default: 30 seconds
|
|
12170
|
+
*/
|
|
12171
|
+
daemonShutdown: 3e4,
|
|
12172
|
+
/**
|
|
12173
|
+
* WebSocket connection establishment.
|
|
12174
|
+
* Default: 10 seconds
|
|
12175
|
+
*/
|
|
12176
|
+
wsConnection: 1e4,
|
|
12177
|
+
/**
|
|
12178
|
+
* Overall pipeline start (all components).
|
|
12179
|
+
* Default: 15 seconds
|
|
12180
|
+
*/
|
|
12181
|
+
pipelineStartup: 15e3
|
|
12182
|
+
});
|
|
12183
|
+
|
|
12184
|
+
// src/core/graceful-shutdown.ts
|
|
12185
|
+
async function withShutdownTimeout(fn, timeoutMs, component) {
|
|
12186
|
+
if (timeoutMs <= 0) {
|
|
12187
|
+
await fn();
|
|
12188
|
+
return;
|
|
12189
|
+
}
|
|
12190
|
+
const timeoutPromise = new Promise((resolve2) => {
|
|
12191
|
+
setTimeout(() => {
|
|
12192
|
+
resolve2("timed_out");
|
|
12193
|
+
}, timeoutMs).unref();
|
|
12194
|
+
});
|
|
12195
|
+
const result = await Promise.race([
|
|
12196
|
+
fn().then(() => "completed"),
|
|
12197
|
+
timeoutPromise
|
|
12198
|
+
]);
|
|
12199
|
+
if (result === "timed_out") {
|
|
12200
|
+
logger.warn(
|
|
12201
|
+
{ component, timeoutMs },
|
|
12202
|
+
`Graceful shutdown exceeded ${timeoutMs}ms timeout \u2014 forcing through`
|
|
12203
|
+
);
|
|
12204
|
+
}
|
|
12205
|
+
}
|
|
12206
|
+
async function shutdownInOrder(components, timeouts, label) {
|
|
12207
|
+
const getTimeout = (key) => {
|
|
12208
|
+
return timeouts[key] ?? DEFAULT_TIMEOUTS.daemonShutdown;
|
|
12209
|
+
};
|
|
12210
|
+
const stopSafely = async (key, fn) => {
|
|
12211
|
+
const timeoutMs = getTimeout(key);
|
|
12212
|
+
try {
|
|
12213
|
+
await withShutdownTimeout(fn, timeoutMs, `${label}.${key}`);
|
|
12214
|
+
} catch (error) {
|
|
12215
|
+
logger.warn(
|
|
12216
|
+
{ error, key, label },
|
|
12217
|
+
`Error during shutdown of ${key} \u2014 continuing with remaining components`
|
|
12218
|
+
);
|
|
12219
|
+
}
|
|
12220
|
+
};
|
|
12221
|
+
if (components.cleanupFns) {
|
|
12222
|
+
for (const cleanupFn of components.cleanupFns) {
|
|
12223
|
+
try {
|
|
12224
|
+
await withShutdownTimeout(
|
|
12225
|
+
async () => {
|
|
12226
|
+
const result = cleanupFn();
|
|
12227
|
+
if (result instanceof Promise) {
|
|
12228
|
+
await result;
|
|
12229
|
+
}
|
|
12230
|
+
},
|
|
12231
|
+
1e3,
|
|
12232
|
+
`${label}.cleanup`
|
|
12233
|
+
);
|
|
12234
|
+
} catch (error) {
|
|
12235
|
+
logger.warn({ error, label }, "Cleanup function failed");
|
|
12236
|
+
}
|
|
12237
|
+
}
|
|
12238
|
+
}
|
|
12239
|
+
if (components.eventBus) {
|
|
12240
|
+
components.eventBus.unsubscribeAll();
|
|
12241
|
+
}
|
|
12242
|
+
await stopSafely("wsServer", () => components.wsServer.stop());
|
|
12243
|
+
await stopSafely("udsServer", () => components.udsServer.stop());
|
|
12244
|
+
await stopSafely("httpReceiver", () => components.httpReceiver.stop());
|
|
12245
|
+
if (components.adapterRegistry) {
|
|
12246
|
+
await stopSafely("adapterRegistry", () => components.adapterRegistry.stopAll());
|
|
12247
|
+
}
|
|
12248
|
+
}
|
|
12249
|
+
|
|
11119
12250
|
// src/tui/index.tsx
|
|
11120
12251
|
var import_ink13 = require("ink");
|
|
11121
12252
|
var import_fullscreen_ink = require("fullscreen-ink");
|
|
@@ -11319,9 +12450,11 @@ var TOOL_COLORS = {
|
|
|
11319
12450
|
"openhands": "#facc15",
|
|
11320
12451
|
"openclaw": "#ef4444",
|
|
11321
12452
|
"opencode": "#10b981",
|
|
12453
|
+
"pi": "#1db954",
|
|
11322
12454
|
"qwen-code": "#22c55e",
|
|
11323
12455
|
"unknown": "#94a3b8",
|
|
11324
|
-
"windsurf": "#a855f7"
|
|
12456
|
+
"windsurf": "#a855f7",
|
|
12457
|
+
"zed": "#e85d04"
|
|
11325
12458
|
};
|
|
11326
12459
|
var EVENT_COLORS = {
|
|
11327
12460
|
"agent.asking_user": "#ef4444",
|
|
@@ -13075,10 +14208,10 @@ async function renderManagedTui(options) {
|
|
|
13075
14208
|
|
|
13076
14209
|
// src/cli/pid.ts
|
|
13077
14210
|
var import_node_fs3 = require("fs");
|
|
13078
|
-
var
|
|
14211
|
+
var import_promises16 = require("fs/promises");
|
|
13079
14212
|
var import_node_net3 = require("net");
|
|
13080
14213
|
var import_node_os4 = require("os");
|
|
13081
|
-
var
|
|
14214
|
+
var import_node_path19 = require("path");
|
|
13082
14215
|
var import_zod5 = require("zod");
|
|
13083
14216
|
var DaemonStateSchema = import_zod5.z.strictObject({
|
|
13084
14217
|
pid: import_zod5.z.number().int().positive(),
|
|
@@ -13093,14 +14226,14 @@ function getDefaultSocketPath(options) {
|
|
|
13093
14226
|
if (process.platform === "win32") {
|
|
13094
14227
|
return "\\\\.\\pipe\\aisnitch.sock";
|
|
13095
14228
|
}
|
|
13096
|
-
return (0,
|
|
14229
|
+
return (0, import_node_path19.join)(getAISnitchHomePath(options), "aisnitch.sock");
|
|
13097
14230
|
}
|
|
13098
14231
|
async function cleanupSocketPathIfStale(socketPath) {
|
|
13099
14232
|
if (process.platform === "win32") {
|
|
13100
14233
|
return false;
|
|
13101
14234
|
}
|
|
13102
14235
|
try {
|
|
13103
|
-
await (0,
|
|
14236
|
+
await (0, import_promises16.access)(socketPath, import_node_fs3.constants.F_OK);
|
|
13104
14237
|
} catch (error) {
|
|
13105
14238
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
13106
14239
|
return false;
|
|
@@ -13124,20 +14257,20 @@ async function cleanupSocketPathIfStale(socketPath) {
|
|
|
13124
14257
|
if (!staleSocket) {
|
|
13125
14258
|
return false;
|
|
13126
14259
|
}
|
|
13127
|
-
await (0,
|
|
14260
|
+
await (0, import_promises16.rm)(socketPath, { force: true });
|
|
13128
14261
|
return true;
|
|
13129
14262
|
}
|
|
13130
14263
|
function getPidFilePath(options = {}) {
|
|
13131
|
-
return (0,
|
|
14264
|
+
return (0, import_node_path19.join)(getAISnitchHomePath(options), "aisnitch.pid");
|
|
13132
14265
|
}
|
|
13133
14266
|
function getDaemonStatePath(options = {}) {
|
|
13134
|
-
return (0,
|
|
14267
|
+
return (0, import_node_path19.join)(getAISnitchHomePath(options), "daemon-state.json");
|
|
13135
14268
|
}
|
|
13136
14269
|
function getDaemonLogPath(options = {}) {
|
|
13137
|
-
return (0,
|
|
14270
|
+
return (0, import_node_path19.join)(getAISnitchHomePath(options), "daemon.log");
|
|
13138
14271
|
}
|
|
13139
14272
|
function getLaunchAgentPath(options = {}) {
|
|
13140
|
-
return (0,
|
|
14273
|
+
return (0, import_node_path19.join)(
|
|
13141
14274
|
options.launchAgentHomeDirectory ?? (0, import_node_os4.homedir)(),
|
|
13142
14275
|
"Library",
|
|
13143
14276
|
"LaunchAgents",
|
|
@@ -13147,13 +14280,13 @@ function getLaunchAgentPath(options = {}) {
|
|
|
13147
14280
|
async function writePid(pid, options = {}) {
|
|
13148
14281
|
await ensureConfigDir(options);
|
|
13149
14282
|
const pidFilePath = getPidFilePath(options);
|
|
13150
|
-
await (0,
|
|
14283
|
+
await (0, import_promises16.writeFile)(pidFilePath, `${pid}
|
|
13151
14284
|
`, "utf8");
|
|
13152
14285
|
return pidFilePath;
|
|
13153
14286
|
}
|
|
13154
14287
|
async function readPid(options = {}) {
|
|
13155
14288
|
try {
|
|
13156
|
-
const rawPid = await (0,
|
|
14289
|
+
const rawPid = await (0, import_promises16.readFile)(getPidFilePath(options), "utf8");
|
|
13157
14290
|
const parsedPid = Number.parseInt(rawPid.trim(), 10);
|
|
13158
14291
|
if (!Number.isInteger(parsedPid) || parsedPid <= 0) {
|
|
13159
14292
|
throw new Error("Invalid PID file contents.");
|
|
@@ -13167,13 +14300,13 @@ async function readPid(options = {}) {
|
|
|
13167
14300
|
}
|
|
13168
14301
|
}
|
|
13169
14302
|
async function removePid(options = {}) {
|
|
13170
|
-
await (0,
|
|
14303
|
+
await (0, import_promises16.rm)(getPidFilePath(options), { force: true });
|
|
13171
14304
|
}
|
|
13172
14305
|
async function writeDaemonState(state, options = {}) {
|
|
13173
14306
|
await ensureConfigDir(options);
|
|
13174
14307
|
const daemonStatePath = getDaemonStatePath(options);
|
|
13175
14308
|
const validatedState = DaemonStateSchema.parse(state);
|
|
13176
|
-
await (0,
|
|
14309
|
+
await (0, import_promises16.writeFile)(
|
|
13177
14310
|
daemonStatePath,
|
|
13178
14311
|
`${JSON.stringify(validatedState, null, 2)}
|
|
13179
14312
|
`,
|
|
@@ -13183,7 +14316,7 @@ async function writeDaemonState(state, options = {}) {
|
|
|
13183
14316
|
}
|
|
13184
14317
|
async function readDaemonState(options = {}) {
|
|
13185
14318
|
try {
|
|
13186
|
-
const rawJson = await (0,
|
|
14319
|
+
const rawJson = await (0, import_promises16.readFile)(getDaemonStatePath(options), "utf8");
|
|
13187
14320
|
const parsedJson = JSON.parse(rawJson);
|
|
13188
14321
|
return DaemonStateSchema.parse(parsedJson);
|
|
13189
14322
|
} catch (error) {
|
|
@@ -13194,7 +14327,7 @@ async function readDaemonState(options = {}) {
|
|
|
13194
14327
|
}
|
|
13195
14328
|
}
|
|
13196
14329
|
async function removeDaemonState(options = {}) {
|
|
13197
|
-
await (0,
|
|
14330
|
+
await (0, import_promises16.rm)(getDaemonStatePath(options), { force: true });
|
|
13198
14331
|
}
|
|
13199
14332
|
function isProcessRunning(pid) {
|
|
13200
14333
|
try {
|
|
@@ -13230,13 +14363,13 @@ async function cleanupStaleDaemonFiles(options = {}) {
|
|
|
13230
14363
|
}
|
|
13231
14364
|
async function ensureLaunchAgentDir(options = {}) {
|
|
13232
14365
|
const launchAgentPath = getLaunchAgentPath(options);
|
|
13233
|
-
const directoryPath = (0,
|
|
13234
|
-
await (0,
|
|
14366
|
+
const directoryPath = (0, import_node_path19.dirname)(launchAgentPath);
|
|
14367
|
+
await (0, import_promises16.mkdir)(directoryPath, { recursive: true });
|
|
13235
14368
|
return directoryPath;
|
|
13236
14369
|
}
|
|
13237
14370
|
async function getDaemonLogSize(options = {}) {
|
|
13238
14371
|
try {
|
|
13239
|
-
const logStats = await (0,
|
|
14372
|
+
const logStats = await (0, import_promises16.stat)(getDaemonLogPath(options));
|
|
13240
14373
|
return logStats.size;
|
|
13241
14374
|
} catch (error) {
|
|
13242
14375
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -13250,16 +14383,16 @@ function getEffectiveCliConfigPath(options = {}) {
|
|
|
13250
14383
|
}
|
|
13251
14384
|
|
|
13252
14385
|
// src/cli/auto-update.ts
|
|
13253
|
-
var
|
|
13254
|
-
var import_promises16 = require("fs/promises");
|
|
14386
|
+
var import_node_child_process15 = require("child_process");
|
|
13255
14387
|
var import_promises17 = require("fs/promises");
|
|
13256
|
-
var
|
|
14388
|
+
var import_promises18 = require("fs/promises");
|
|
14389
|
+
var import_node_path20 = require("path");
|
|
13257
14390
|
var AUTO_UPDATE_STATE_FILE = "auto-update.json";
|
|
13258
14391
|
var AUTO_UPDATE_LOG_FILE = "auto-update.log";
|
|
13259
14392
|
function createAutoUpdateController(dependencies = {}) {
|
|
13260
14393
|
const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
|
|
13261
14394
|
const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
13262
|
-
const spawnImplementation = dependencies.spawn ??
|
|
14395
|
+
const spawnImplementation = dependencies.spawn ?? import_node_child_process15.spawn;
|
|
13263
14396
|
return {
|
|
13264
14397
|
runDetachedUpdate: async (options) => {
|
|
13265
14398
|
const pathOptions = toPathOptions(options);
|
|
@@ -13267,8 +14400,8 @@ function createAutoUpdateController(dependencies = {}) {
|
|
|
13267
14400
|
const command = resolveUpdateCommand(options.manager);
|
|
13268
14401
|
const args = resolveUpdateArgs(options.manager);
|
|
13269
14402
|
const aisnitchHomePath = getAISnitchHomePath(pathOptions);
|
|
13270
|
-
await (0,
|
|
13271
|
-
const logFilePath = (0,
|
|
14403
|
+
await (0, import_promises17.mkdir)(aisnitchHomePath, { recursive: true });
|
|
14404
|
+
const logFilePath = (0, import_node_path20.join)(aisnitchHomePath, AUTO_UPDATE_LOG_FILE);
|
|
13272
14405
|
const startedAt = now().toISOString();
|
|
13273
14406
|
await writeAutoUpdateState(
|
|
13274
14407
|
{
|
|
@@ -13301,7 +14434,7 @@ function createAutoUpdateController(dependencies = {}) {
|
|
|
13301
14434
|
});
|
|
13302
14435
|
combinedLog += `[${now().toISOString()}] finished with code ${exitCode}
|
|
13303
14436
|
`;
|
|
13304
|
-
await (0,
|
|
14437
|
+
await (0, import_promises17.writeFile)(logFilePath, combinedLog, "utf8");
|
|
13305
14438
|
await writeAutoUpdateState(
|
|
13306
14439
|
{
|
|
13307
14440
|
attemptedVersion: options.latestVersion,
|
|
@@ -13392,7 +14525,7 @@ async function detectInstallManager(options) {
|
|
|
13392
14525
|
}
|
|
13393
14526
|
let resolvedCliPath = options.cliEntryPath;
|
|
13394
14527
|
try {
|
|
13395
|
-
resolvedCliPath = await (0,
|
|
14528
|
+
resolvedCliPath = await (0, import_promises18.realpath)(options.cliEntryPath);
|
|
13396
14529
|
} catch {
|
|
13397
14530
|
}
|
|
13398
14531
|
if (resolvedCliPath.includes("/Cellar/aisnitch/")) {
|
|
@@ -13456,7 +14589,7 @@ function resolveUpdateArgs(manager) {
|
|
|
13456
14589
|
async function readAutoUpdateState(options) {
|
|
13457
14590
|
const statePath = getAutoUpdateStatePath(options);
|
|
13458
14591
|
try {
|
|
13459
|
-
const rawJson = await (0,
|
|
14592
|
+
const rawJson = await (0, import_promises17.readFile)(statePath, "utf8");
|
|
13460
14593
|
return JSON.parse(rawJson);
|
|
13461
14594
|
} catch (error) {
|
|
13462
14595
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -13467,8 +14600,8 @@ async function readAutoUpdateState(options) {
|
|
|
13467
14600
|
}
|
|
13468
14601
|
async function writeAutoUpdateState(state, options) {
|
|
13469
14602
|
const aisnitchHomePath = getAISnitchHomePath(options);
|
|
13470
|
-
await (0,
|
|
13471
|
-
await (0,
|
|
14603
|
+
await (0, import_promises17.mkdir)(aisnitchHomePath, { recursive: true });
|
|
14604
|
+
await (0, import_promises17.writeFile)(
|
|
13472
14605
|
getAutoUpdateStatePath(options),
|
|
13473
14606
|
`${JSON.stringify(state, null, 2)}
|
|
13474
14607
|
`,
|
|
@@ -13476,7 +14609,7 @@ async function writeAutoUpdateState(state, options) {
|
|
|
13476
14609
|
);
|
|
13477
14610
|
}
|
|
13478
14611
|
function getAutoUpdateStatePath(options) {
|
|
13479
|
-
return (0,
|
|
14612
|
+
return (0, import_node_path20.join)(getAISnitchHomePath(options), AUTO_UPDATE_STATE_FILE);
|
|
13480
14613
|
}
|
|
13481
14614
|
function toPathOptions(options) {
|
|
13482
14615
|
return {
|
|
@@ -13667,7 +14800,7 @@ function isRecord13(value) {
|
|
|
13667
14800
|
}
|
|
13668
14801
|
|
|
13669
14802
|
// src/cli/runtime.ts
|
|
13670
|
-
var
|
|
14803
|
+
var execFile15 = (0, import_node_util15.promisify)(import_node_child_process16.execFile);
|
|
13671
14804
|
var DAEMON_READY_TIMEOUT_MS = 4e3;
|
|
13672
14805
|
var DAEMON_READY_POLL_INTERVAL_MS = 100;
|
|
13673
14806
|
var DAEMON_STOP_TIMEOUT_MS = 4e3;
|
|
@@ -13677,13 +14810,13 @@ function createCliRuntime(dependencies = {}) {
|
|
|
13677
14810
|
const output = dependencies.output ?? createProcessOutput();
|
|
13678
14811
|
const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
|
|
13679
14812
|
const renderManagedTuiImplementation = dependencies.renderManagedTui ?? renderManagedTui;
|
|
13680
|
-
const spawnImplementation = dependencies.spawn ??
|
|
14813
|
+
const spawnImplementation = dependencies.spawn ?? import_node_child_process16.spawn;
|
|
13681
14814
|
const autoUpdateController = createAutoUpdateController({
|
|
13682
14815
|
fetch: fetchImplementation,
|
|
13683
14816
|
spawn: spawnImplementation
|
|
13684
14817
|
});
|
|
13685
14818
|
const execFileImplementation = dependencies.execFile ?? (async (file, args) => {
|
|
13686
|
-
return await
|
|
14819
|
+
return await execFile15(file, [...args], {
|
|
13687
14820
|
encoding: "utf8"
|
|
13688
14821
|
});
|
|
13689
14822
|
});
|
|
@@ -13822,8 +14955,8 @@ function createCliRuntime(dependencies = {}) {
|
|
|
13822
14955
|
if (logSize < DAEMON_LOG_MAX_BYTES) {
|
|
13823
14956
|
return;
|
|
13824
14957
|
}
|
|
13825
|
-
await (0,
|
|
13826
|
-
await (0,
|
|
14958
|
+
await (0, import_promises19.rm)(backupPath, { force: true });
|
|
14959
|
+
await (0, import_promises19.rename)(logFilePath, backupPath);
|
|
13827
14960
|
}
|
|
13828
14961
|
async function waitForDaemonReady(pathOptions) {
|
|
13829
14962
|
const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
|
|
@@ -13858,7 +14991,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
13858
14991
|
}
|
|
13859
14992
|
async function readDaemonStartupFailure(pathOptions) {
|
|
13860
14993
|
try {
|
|
13861
|
-
const daemonLog = await (0,
|
|
14994
|
+
const daemonLog = await (0, import_promises19.readFile)(getDaemonLogPath(pathOptions), "utf8");
|
|
13862
14995
|
const logLines = daemonLog.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
13863
14996
|
const lastLine = logLines.at(-1);
|
|
13864
14997
|
if (!lastLine) {
|
|
@@ -13956,22 +15089,35 @@ function createCliRuntime(dependencies = {}) {
|
|
|
13956
15089
|
return;
|
|
13957
15090
|
}
|
|
13958
15091
|
shuttingDown = true;
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
15092
|
+
const shutdownTimeouts = {
|
|
15093
|
+
adapterRegistry: DEFAULT_TIMEOUTS.adapterShutdown,
|
|
15094
|
+
httpReceiver: DEFAULT_TIMEOUTS.httpRequest,
|
|
15095
|
+
udsServer: DEFAULT_TIMEOUTS.fileOperation,
|
|
15096
|
+
wsServer: DEFAULT_TIMEOUTS.wsConnection,
|
|
15097
|
+
cleanupFns: 1e3
|
|
15098
|
+
};
|
|
15099
|
+
const components = {
|
|
15100
|
+
adapterRegistry: pipeline.getAdapterRegistry(),
|
|
15101
|
+
httpReceiver: pipeline.getHttpReceiver(),
|
|
15102
|
+
udsServer: pipeline.getUdsServer(),
|
|
15103
|
+
wsServer: pipeline.getWsServer(),
|
|
15104
|
+
eventBus: pipeline.getEventBus(),
|
|
15105
|
+
cleanupFns: daemonMode ? [async () => {
|
|
13964
15106
|
await Promise.all([
|
|
13965
|
-
removePid(
|
|
13966
|
-
removeDaemonState(
|
|
15107
|
+
removePid(toPathOptions2(options)),
|
|
15108
|
+
removeDaemonState(toPathOptions2(options))
|
|
13967
15109
|
]);
|
|
13968
|
-
}
|
|
13969
|
-
}
|
|
13970
|
-
|
|
13971
|
-
|
|
15110
|
+
}] : []
|
|
15111
|
+
};
|
|
15112
|
+
try {
|
|
15113
|
+
await shutdownInOrder(components, shutdownTimeouts, "pipeline");
|
|
15114
|
+
} finally {
|
|
15115
|
+
if (!daemonMode) {
|
|
15116
|
+
output.stdout(`AISnitch stopped after ${signal}.
|
|
13972
15117
|
`);
|
|
15118
|
+
}
|
|
15119
|
+
process.exit(exitCode);
|
|
13973
15120
|
}
|
|
13974
|
-
process.exit(exitCode);
|
|
13975
15121
|
};
|
|
13976
15122
|
if (daemonMode) {
|
|
13977
15123
|
process.once("SIGTERM", () => {
|
|
@@ -14228,7 +15374,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14228
15374
|
}
|
|
14229
15375
|
const { config } = await loadEffectiveConfig(options);
|
|
14230
15376
|
setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
|
|
14231
|
-
const ephemeralHomeDirectory = await (0,
|
|
15377
|
+
const ephemeralHomeDirectory = await (0, import_promises19.mkdtemp)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), "aisnitch-mock-"));
|
|
14232
15378
|
const ephemeralPipeline = new Pipeline();
|
|
14233
15379
|
const status2 = await ephemeralPipeline.start({
|
|
14234
15380
|
config: {
|
|
@@ -14267,7 +15413,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14267
15413
|
} finally {
|
|
14268
15414
|
await Promise.resolve(monitorClose());
|
|
14269
15415
|
await ephemeralPipeline.stop();
|
|
14270
|
-
await (0,
|
|
15416
|
+
await (0, import_promises19.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
|
|
14271
15417
|
}
|
|
14272
15418
|
}
|
|
14273
15419
|
async function install(options) {
|
|
@@ -14289,7 +15435,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14289
15435
|
await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
|
|
14290
15436
|
} catch {
|
|
14291
15437
|
}
|
|
14292
|
-
await (0,
|
|
15438
|
+
await (0, import_promises19.writeFile)(launchAgentPath, plistContents, "utf8");
|
|
14293
15439
|
await execFileImplementation("launchctl", ["bootstrap", domainTarget, launchAgentPath]);
|
|
14294
15440
|
output.stdout(`AISnitch LaunchAgent installed at ${launchAgentPath}
|
|
14295
15441
|
`);
|
|
@@ -14312,7 +15458,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14312
15458
|
} else {
|
|
14313
15459
|
const { config } = await loadEffectiveConfig(options);
|
|
14314
15460
|
setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
|
|
14315
|
-
ephemeralHomeDirectory = await (0,
|
|
15461
|
+
ephemeralHomeDirectory = await (0, import_promises19.mkdtemp)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), "aisnitch-wrap-"));
|
|
14316
15462
|
ephemeralPipeline = new Pipeline();
|
|
14317
15463
|
await ephemeralPipeline.start({
|
|
14318
15464
|
config: {
|
|
@@ -14367,7 +15513,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14367
15513
|
await ephemeralPipeline.stop();
|
|
14368
15514
|
}
|
|
14369
15515
|
if (ephemeralHomeDirectory !== null) {
|
|
14370
|
-
await (0,
|
|
15516
|
+
await (0, import_promises19.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
|
|
14371
15517
|
}
|
|
14372
15518
|
}
|
|
14373
15519
|
process.exit(wrappedExitCode);
|
|
@@ -14387,7 +15533,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14387
15533
|
},
|
|
14388
15534
|
pid: process.ppid > 1 ? process.ppid : void 0,
|
|
14389
15535
|
source: "aisnitch://adapters/aider/notifications-command",
|
|
14390
|
-
transcriptPath: (0,
|
|
15536
|
+
transcriptPath: (0, import_node_path21.join)(process.cwd(), ".aider.chat.history.md"),
|
|
14391
15537
|
type: "agent.idle"
|
|
14392
15538
|
}),
|
|
14393
15539
|
headers: {
|
|
@@ -14409,7 +15555,7 @@ function createCliRuntime(dependencies = {}) {
|
|
|
14409
15555
|
await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
|
|
14410
15556
|
} catch {
|
|
14411
15557
|
}
|
|
14412
|
-
await (0,
|
|
15558
|
+
await (0, import_promises19.rm)(launchAgentPath, { force: true });
|
|
14413
15559
|
output.stdout(`AISnitch LaunchAgent removed from ${launchAgentPath}
|
|
14414
15560
|
`);
|
|
14415
15561
|
}
|
|
@@ -14588,7 +15734,7 @@ function joinSocketPath(pathOptions) {
|
|
|
14588
15734
|
}
|
|
14589
15735
|
function resolveCliEntryPath() {
|
|
14590
15736
|
const cliEntryPath = process.argv[1];
|
|
14591
|
-
if (!cliEntryPath || (0,
|
|
15737
|
+
if (!cliEntryPath || (0, import_node_path21.basename)(cliEntryPath).length === 0) {
|
|
14592
15738
|
throw new Error("Unable to resolve the AISnitch CLI entry path.");
|
|
14593
15739
|
}
|
|
14594
15740
|
return cliEntryPath;
|